├── .github └── workflows │ ├── build.yml │ ├── coverity.yml │ └── release.yml ├── .gitignore ├── ChangeLog.md ├── INSTALL.md ├── LICENSE ├── Make.os9 ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── debian ├── .gitignore ├── README.Debian ├── changelog ├── compat ├── control ├── copyright ├── dirs ├── docs ├── libeditline-dev.install ├── libeditline1.install ├── libeditline1.symbols ├── rules └── source │ └── format ├── docs ├── HACKING.md ├── README └── TODO.md ├── examples ├── .gitignore ├── Makefile.am ├── cli.c ├── excallback.c ├── fileman.c └── testit.c ├── include ├── Makefile.am └── editline.h ├── libeditline.pc.in ├── m4 └── .gitignore ├── man ├── Makefile.am └── editline.3 └── src ├── .gitignore ├── Makefile.am ├── complete.c ├── editline.c ├── editline.h ├── os9.h ├── sysos9.c ├── sysunix.c └── unix.h /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Bob the Builder 2 | 3 | # Run on all branches, including all pull requests, except the 'dev' 4 | # branch which we use for Coverity Scan (limited tokens/day) 5 | on: 6 | push: 7 | branches: 8 | - '**' 9 | - '!dev' 10 | pull_request: 11 | branches: 12 | - '**' 13 | 14 | jobs: 15 | build: 16 | name: ${{ matrix.compiler }} 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | compiler: [gcc, clang] 21 | fail-fast: false 22 | env: 23 | MAKEFLAGS: -j3 24 | CC: ${{ matrix.compiler }} 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Configure 28 | run: | 29 | ./autogen.sh 30 | ./configure --prefix= --disable-silent-rules \ 31 | --enable-sigstop --enable-terminal-bell 32 | - name: Build 33 | run: | 34 | make 35 | - name: Install to ~/tmp and Inspect 36 | run: | 37 | DESTDIR=~/tmp make install-strip 38 | ls -lR ~/tmp 39 | -------------------------------------------------------------------------------- /.github/workflows/coverity.yml: -------------------------------------------------------------------------------- 1 | name: Coverity Scan 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'dev' 7 | 8 | env: 9 | PROJECT_NAME: editline 10 | CONTACT_EMAIL: troglobit@gmail.com 11 | COVERITY_NAME: troglobit-editline 12 | COVERITY_PROJ: troglobit%2Feditline 13 | 14 | jobs: 15 | coverity: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Fetch latest Coverity Scan MD5 20 | id: var 21 | env: 22 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 23 | run: | 24 | wget -q https://scan.coverity.com/download/cxx/linux64 \ 25 | --post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \ 26 | -O coverity-latest.tar.gz.md5 27 | echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT 28 | - uses: actions/cache@v4 29 | id: cache 30 | with: 31 | path: coverity-latest.tar.gz 32 | key: ${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }} 33 | restore-keys: | 34 | ${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }} 35 | ${{ runner.os }}-coverity- 36 | ${{ runner.os }}-coverity 37 | - name: Download Coverity Scan 38 | env: 39 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 40 | run: | 41 | if [ ! -f coverity-latest.tar.gz ]; then 42 | wget -q https://scan.coverity.com/download/cxx/linux64 \ 43 | --post-data "token=$TOKEN&project=${COVERITY_PROJ}" \ 44 | -O coverity-latest.tar.gz 45 | else 46 | echo "Latest Coverity Scan available from cache :-)" 47 | md5sum coverity-latest.tar.gz 48 | fi 49 | mkdir coverity 50 | tar xzf coverity-latest.tar.gz --strip 1 -C coverity 51 | - name: Configure 52 | run: | 53 | ./autogen.sh 54 | ./configure --prefix= --enable-sigstop --enable-terminal-bell --enable-examples 55 | - name: Build 56 | run: | 57 | export PATH=`pwd`/coverity/bin:$PATH 58 | cov-build --dir cov-int make 59 | - name: Submit results to Coverity Scan 60 | env: 61 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 62 | run: | 63 | tar czvf ${PROJECT_NAME}.tgz cov-int 64 | curl \ 65 | --form project=${COVERITY_NAME} \ 66 | --form token=$TOKEN \ 67 | --form email=${CONTACT_EMAIL} \ 68 | --form file=@${PROJECT_NAME}.tgz \ 69 | --form version=trunk \ 70 | --form description="${PROJECT_NAME} $(git rev-parse HEAD)" \ 71 | https://scan.coverity.com/builds?project=${COVERITY_PROJ} 72 | - name: Upload build.log 73 | uses: actions/upload-artifact@v4 74 | with: 75 | name: coverity-build.log 76 | path: cov-int/build-log.txt 77 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release General 2 | 3 | on: 4 | push: 5 | tags: 6 | - '[0-9]+.[0-9]+*' 7 | 8 | jobs: 9 | release: 10 | name: Build and upload release tarball 11 | if: startsWith(github.ref, 'refs/tags/') 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Creating Makefiles ... 16 | run: | 17 | ./autogen.sh 18 | ./configure --prefix= 19 | - name: Build release ... 20 | run: | 21 | make release 22 | mkdir -p artifacts/ 23 | mv ../*.tar.* artifacts/ 24 | - name: Extract ChangeLog entry ... 25 | run: | 26 | awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \ 27 | |head -n -1 > release.md 28 | cat release.md 29 | - uses: ncipollo/release-action@v1 30 | with: 31 | name: Editline v${{ github.ref_name }} 32 | bodyFile: "release.md" 33 | artifacts: "artifacts/*" 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | */.libs/ 3 | *.lo 4 | *.o 5 | *.pc 6 | .deps 7 | .testit_history 8 | GPATH 9 | GRTAGS 10 | GTAGS 11 | Makefile 12 | Makefile.in 13 | aclocal.m4 14 | archive 15 | autom4te.cache 16 | compile 17 | config.guess 18 | config.h 19 | config.h.in 20 | config.log 21 | config.status 22 | config.sub 23 | configure 24 | depcomp 25 | install-sh 26 | libtool 27 | ltmain.sh 28 | missing 29 | stamp-h1 30 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | All notable changes to the project are documented in this file. 5 | 6 | 7 | [1.17.1][] - 2020-02-23 8 | ----------------------- 9 | 10 | ### Fixes 11 | - Fix #38: Fix for multiline representing as one line 12 | - Fix packaging, missing files in libeditline1, regression from 1.16.0 13 | - Fix packaging, update to latest std version 14 | - Fix formatting of function names in man page 15 | - Restore tar.gz distribution, for usability on systems that do not 16 | have xz in their default install 17 | 18 | 19 | [1.17.0][] - 2020-01-05 20 | ----------------------- 21 | 22 | ### Changes 23 | - Simple multi-line support by Dima Volynets, @dvolynets 24 | 25 | ### Fixes 26 | - Fix return value from `read_history()` and `write_history()`, could 27 | return `errno` instead of `EOF` to indicate error. Now both functions 28 | have uniform return values on error 29 | - Handle internal `realloc()` errors better. Now memory is not leaked 30 | if `realloc()` fails 31 | - Fix possible NULL pointer dereference in key binding lookup function 32 | 33 | 34 | [1.16.1][] - 2019-06-07 35 | ----------------------- 36 | 37 | ### Changes 38 | - Major updates to the `editline.3` man page 39 | - Cleanup of examples `cli.c` and `fileman.c` 40 | - Add example of hidden input prompt to `cli.c` 41 | 42 | ### Fixes 43 | - Fix #20: `configure --disable-eof` does not bite 44 | - Fix #23: Make Ctrl-L clear the screan instead of starting a new line 45 | Like Ctrl-D, which exits, Ctrl-L only clears the screen when the line 46 | is empty and the cursor is at the start of the line, otherwise Ctrl-L 47 | will redraw/refresh the current line. 48 | - Fix #24: Fix behavior when TTY is narrower than column width, by Will Dietz 49 | - Fix #25: Avoid continuously duplicate commands in history 50 | - Fix #31: Aborting i-search with Ctrl-C should not generate signal 51 | 52 | 53 | [1.16.0][] - 2018-09-16 54 | ----------------------- 55 | 56 | Event loop callback support. 57 | 58 | ### Changes 59 | - `rl_unintialize()`, new function to free all memory, by Claus Fischer 60 | - `rl_insert_text()`, new GNU Readline compat function 61 | - `rl_refresh_line()`, new GNU Readline compat function 62 | - `rl_callback_*()`, alternate interface to plain `readline()` for event 63 | loops. Modeled after the GNU Readline API 64 | - `rl_completion_entry_function`, and `rl_attempted_completion_function` 65 | are two new GNU Readline compat user hooks for the completion framework 66 | - `rl_completion_matches()` and `rl_filename_completion_function()` 67 | are two new GNU Readline compat functions 68 | - Add new example: `fileman.c` from GNU Readline to demonstrate the 69 | level of compatibility of the revamped completion framework 70 | - Add support for Ctrl-Right and Ctrl-Left, forward/backward word 71 | - Add .deb package to official release target 72 | 73 | ### Fixes 74 | - Fixed header guards, avoid using leading `__` 75 | - Spell check fixes 76 | - Remove duplicate code in history check 77 | - Use `NULL` instead of `0`, and `-1` instead of `NULL`, where applicable 78 | - Misc. minor Coverity Scan fixes 79 | - Misc. minor fixes to `testit.c` example code 80 | - Add `-Wextra` to std `CFLAGS` 81 | - Check `fclose()` return value in in `write_history()` and `read_history()` 82 | - Initialize global variables and reset to `NULL` on `free()` 83 | - Fix off-by-one in forward kill word, avoid deleting too much 84 | - Skip (or kill) leading whitespace when skipping (or killing) forwards 85 | 86 | 87 | [1.15.3][] - 2017-09-07 88 | ----------------------- 89 | 90 | Bug fix release. 91 | 92 | ### Changes 93 | - Refactor all enable/disable configure options, same problem as in #7 94 | 95 | ### Fixes 96 | - Fix #7: `--enable-termcap` configure option does not work. The script 97 | enabled termcap by default rather than the other way around. 98 | 99 | Also, check for terminfo as well, when `--enable-termcap` is selected. 100 | 101 | 102 | [1.15.2][] - 2016-06-06 103 | ----------------------- 104 | 105 | Bug fixes and minor feature creep in `pkg-config` support. 106 | 107 | ### Changes 108 | - Prevent mangling of symbols when linking with C++. Patch courtesy of 109 | Jakub Pawlowski 110 | - Add `libeditline.pc` for `pkg-config` 111 | 112 | ### Fixes 113 | - Do not assume a termcap library exists, check for `tgetent()` in 114 | curses, ncurses, tinfo and termcap libraries 115 | - Call `tty_flush()` when user calls `rl_forced_update_display()` 116 | to prevent screen becoming garbled. Patch by Jakub Pawlowski 117 | 118 | 119 | [1.15.1][] - 2015-11-16 120 | ----------------------- 121 | 122 | Bug fixes only. 123 | 124 | ### Changes 125 | - Update README with origin of this version of editline 126 | 127 | ### Fixes 128 | - Fix build system, don't force automake v1.11, require at least v1.11 129 | - Fix build problem with examples using `--enable-termcap` 130 | 131 | 132 | [1.15.0][] - 2015-09-10 133 | ----------------------- 134 | 135 | ### Changes 136 | - Add support for `--disable-eof` and `--disable-sigint` to disable 137 | default Ctrl-D and Ctrl-C behavior 138 | - Add support for `el_no_hist` to disable access to and auto-save of history 139 | - GNU readline compat functions for prompt handling and redisplay 140 | - Refactor: replace variables named 'new' with non-reserved word 141 | - Add support for [Travis-CI][], continuous integration with GitHub 142 | - Add support for [Coverity Scan][], the best static code analyzer, 143 | integrated with [Travis-CI][] -- scan runs for each push to master 144 | - Rename NEWS.md --> ChangeLog.md, with symlinks for make install 145 | - Attempt to align with http://keepachangelog.com/ for this file 146 | - Cleanup and improve Markdown syntax in [README.md][] 147 | - Add API and example to [README.md][], inspired by [libuEv][] 148 | - Removed generated files from version control. Use `./autogen.sh` 149 | to generate the `configure` script when working from GIT. This 150 | does not affect distributed tarballs 151 | 152 | ### Fixes 153 | - Fix issue #2, regression in Ctrl-D (EOF) behavior. Regression 154 | introduced in [1.14.1][]. Fixed by @TobyGoodwin 155 | - Fix memory leak in completion handler. Found by [Coverity Scan][]. 156 | - Fix suspicious use of `sizeof(char **)`, same as `sizeof(char *)` but 157 | non-portable. Found by [Coverity Scan][] 158 | - Fix out-of-bounds access in user key binding routines 159 | Found by [Coverity Scan][]. 160 | - Fix invisible example code in man page 161 | 162 | 163 | [1.14.2][] - 2014-09-14 164 | ----------------------- 165 | 166 | Bug fixes only. 167 | 168 | ### Fixes 169 | - Fix `el_no_echo` bug causing secrets to leak when disabling no-echo 170 | - Handle `EINTR` in syscalls better 171 | 172 | 173 | [1.14.1][] - 2014-09-14 174 | ----------------------- 175 | 176 | Minor fixes and additions. 177 | 178 | ### Changes 179 | - Don't print status message on `stderr` in key binding funcions 180 | - Export `el_del_char()` 181 | - Check for and return pending signals when detected 182 | - Allow custom key bindings ... 183 | 184 | ### Fixes 185 | - Bug fixes ... 186 | 187 | 188 | [1.14.0][] - 2010-08-10 189 | ----------------------- 190 | 191 | Major cleanups and further merges with Debian editline package. 192 | 193 | ### Changes 194 | - Merge in changes to `debian/` from `editline_1.12-6.debian.tar.gz` 195 | - Migrate to use libtool 196 | - Make `UNIQUE_HISTORY` configurable 197 | - Make scrollback history (`HIST_SIZE`) configurable 198 | - Configure options for toggling terminal bell and `SIGSTOP` (Ctrl-Z) 199 | - Configure option for using termcap to read/control terminal size 200 | - Rename Signal to `el_intr_pending`, from Festival speech-tools 201 | - Merge support for capitalizing words (`M-c`) from Festival 202 | speech-tools by Alan W Black 203 | - Fallback backspace handling, in case `tgetstr("le")` fails 204 | 205 | ### Fixes 206 | - Cleanups and fixes thanks to the Sparse static code analysis tool 207 | - Merge `el_no_echo` patch from Festival speech-tools 208 | - Merge fixes from Heimdal project 209 | - Completely refactor `rl_complete()` and `rl_list_possib()` with 210 | fixes from the Heimdal project. Use `rl_set_complete_func()` and 211 | `rl_set_list_possib_func()`. Default completion callbacks are now 212 | available as a configure option `--enable-default-complete` 213 | - Memory leak fixes 214 | - Actually fix 8-bit handling by reverting old Debian patch 215 | - Merge patch to improve compatibility with GNU readline, thanks to 216 | Steve Tell from way back in 1997 and 1998 217 | 218 | 219 | [1.13.0][] - 2010-03-09 220 | ----------------------- 221 | 222 | Adaptations to Debian editline package. 223 | 224 | ### Changes 225 | - Major version number bump, adapt to Jim Studt's v1.12 226 | - Import `debian/` directory and adapt it to configure et al. 227 | - Change library name to libeditline to distinguish it from BSD libedit 228 | 229 | 230 | [0.3.0][] - 2009-02-08 231 | ---------------------- 232 | 233 | ### Changes 234 | - Support for ANSI arrow keys using configure --enable-arrow-keys 235 | 236 | 237 | [0.2.3][] - 2008-12-02 238 | ---------------------- 239 | 240 | ### Changes 241 | - Patches from Debian package merged 242 | - Support for custom command completion 243 | 244 | 245 | [0.1.0][] - 2008-06-07 246 | ---------------------- 247 | 248 | ### Changes 249 | - First version, forked from Minix current 2008-06-06 250 | 251 | 252 | [UNRELEASED]: https://github.com/troglobit/finit/compare/1.17.1...HEAD 253 | [1.17.1]: https://github.com/troglobit/finit/compare/1.17.0...1.17.1 254 | [1.17.0]: https://github.com/troglobit/finit/compare/1.16.1...1.17.0 255 | [1.16.1]: https://github.com/troglobit/finit/compare/1.16.0...1.16.1 256 | [1.16.0]: https://github.com/troglobit/finit/compare/1.15.3...1.16.0 257 | [1.15.3]: https://github.com/troglobit/finit/compare/1.15.2...1.15.3 258 | [1.15.2]: https://github.com/troglobit/finit/compare/1.15.1...1.15.2 259 | [1.15.1]: https://github.com/troglobit/finit/compare/1.15.0...1.15.1 260 | [1.15.0]: https://github.com/troglobit/finit/compare/1.14.2...1.15.0 261 | [1.14.2]: https://github.com/troglobit/finit/compare/1.14.1...1.14.2 262 | [1.14.1]: https://github.com/troglobit/finit/compare/1.14.0...1.14.1 263 | [1.14.0]: https://github.com/troglobit/finit/compare/1.13.0...1.14.0 264 | [1.13.0]: https://github.com/troglobit/finit/compare/0.3.0...1.13.0 265 | [0.3.0]: https://github.com/troglobit/finit/compare/0.2.3...0.3.0 266 | [0.2.3]: https://github.com/troglobit/finit/compare/0.1.0...0.2.3 267 | [0.1.0]: https://github.com/troglobit/finit/compare/0.0.0...0.1.0 268 | [libuEv]: http://github.com/troglobit/libuev 269 | [Travis-CI]: https://travis-ci.org/troglobit/uftpd 270 | [Coverity Scan]: https://scan.coverity.com/projects/2947 271 | [README.md]: https://github.com/troglobit/editline/blob/master/README.md 272 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | HowTo Build Minix Editline 2 | ========================== 3 | 4 | Minix editline use the GNU configure tools, which includes autoconf, 5 | automake and libtool. This enables high levels of portability and ease 6 | of use. In most cases all you need to do is unpack the tarball, enter 7 | the directory and type: 8 | 9 | ./configure 10 | 11 | There are are, however, more options available. For instance, sometimes 12 | it is useful to build editline as a static library, type: 13 | 14 | ./configure --disable-shared 15 | 16 | By default editline employs a default handler for the TAB key, pressing 17 | it once completes to the nearest matching filename in the current 18 | working directory, or it can display a listing of possible completions. 19 | For some uses this default is not desirable at all, type: 20 | 21 | ./configure --disable-default-complete 22 | 23 | An even more common desire is to change the default install location. 24 | By default all configure scripts setup /usr/local as the install 25 | "prefix", to change this type: 26 | 27 | ./configure --prefix=/home/troglobit/tmp 28 | 29 | Advanced users are encouraged to read up on --libdir, --mandir, etc. in 30 | the GNU Configure and Build System. 31 | 32 | For more available options, type: 33 | 34 | ./configure --help 35 | 36 | To build and install, simply type: 37 | 38 | make 39 | 40 | followed by 41 | 42 | sudo make install 43 | 44 | Good Luck! 45 | //Joachim 46 | 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 1992,1993 Simmule Turner and Rich Salz 2 | All rights reserved. 3 | 4 | This software is not subject to any license of the American Telephone 5 | and Telegraph Company or of the Regents of the University of California. 6 | 7 | Permission is granted to anyone to use this software for any purpose on 8 | any computer system, and to alter it and redistribute it freely, subject 9 | to the following restrictions: 10 | 1. The authors are not responsible for the consequences of use of this 11 | software, no matter how awful, even if they arise from flaws in it. 12 | 2. The origin of this software must not be misrepresented, either by 13 | explicit claim or by omission. Since few users ever read sources, 14 | credits must appear in the documentation. 15 | 3. Altered versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. Since few users 17 | ever read sources, credits must appear in the documentation. 18 | 4. This notice may not be removed or altered. 19 | -------------------------------------------------------------------------------- /Make.os9: -------------------------------------------------------------------------------- 1 | ## $Revision: 1.2 $ 2 | ## 3 | ## OS-9 makefile for editline library. 4 | ## 5 | 6 | .SUFFIXES: 7 | 8 | RFILES = editline.r complete.r sysos9.r 9 | 10 | %.r: %.c 11 | cc68 -r -Dstrchr=index -Dstrrchr=rindex -DNEED_STRDUP -DSYS_OS9 $*.c 12 | 13 | testit: testit.r editline.lib 14 | cc68 -f=testit testit.r -l=editline.lib 15 | 16 | $(RFILES): $(RFILES:%.r=%.c) 17 | 18 | editline.lib: $(RFILES) 19 | cat $(RFILES) >$@ 20 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | pkgconfigdir = $(libdir)/pkgconfig 2 | pkgconfig_DATA = libeditline.pc 3 | doc_DATA = README.md LICENSE 4 | EXTRA_DIST = README.md LICENSE ChangeLog.md INSTALL.md 5 | SUBDIRS = src include man 6 | 7 | if ENABLE_EXAMPLES 8 | SUBDIRS += examples 9 | endif 10 | 11 | ## Check if tagged in git 12 | release-hook: 13 | if [ ! `git tag | grep $(PACKAGE_VERSION) | grep $(PACKAGE_VERSION)` ]; then \ 14 | echo; \ 15 | printf "\e[1m\e[41mCannot find release tag $(PACKAGE_VERSION)\e[0m\n"; \ 16 | printf "\e[1m\e[5mDo release anyway?\e[0m "; read yorn; \ 17 | if [ "$$yorn" != "y" -a "$$yorn" != "Y" ]; then \ 18 | printf "OK, aborting release.\n"; \ 19 | exit 1; \ 20 | fi; \ 21 | echo; \ 22 | else \ 23 | echo; \ 24 | printf "\e[1m\e[42mFound GIT release tag $(PACKAGE_VERSION)\e[0m\n"; \ 25 | printf "\e[1m\e[44m>>Remember to push tags!\e[0m\n"; \ 26 | echo; \ 27 | fi 28 | 29 | # lintian --profile debian -i -I --show-overrides ../$PKG.changes 30 | package: 31 | dpkg-buildpackage -uc -us -B 32 | 33 | ## Target to run when building a release 34 | release: release-hook distcheck 35 | @for file in $(DIST_ARCHIVES); do \ 36 | md5sum $$file > ../$$file.md5; \ 37 | sha256sum $$file > ../$$file.sha256; \ 38 | done 39 | @mv $(DIST_ARCHIVES) ../ 40 | @echo 41 | @echo "Resulting release files:" 42 | @echo "=========================================================================" 43 | @for file in $(DIST_ARCHIVES); do \ 44 | printf "%-40s Distribution tarball\n" $$file; \ 45 | printf "%-40s " $$file.md5; cat ../$$file.md5 | cut -f1 -d' '; \ 46 | printf "%-40s " $$file.sha256; cat ../$$file.sha256 | cut -f1 -d' '; \ 47 | done 48 | @for file in `cd ..; ls *$(PACKAGE)*_$(subst _,.,$(VERSION))*`; do \ 49 | printf "%-40s Debian/Ubuntu package file\n" $$file; \ 50 | done 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Editline 2 | ======== 3 | [![License Badge][]][License] [![GitHub Status][]][GitHub] [![Coverity Status]][Coverity Scan] 4 | 5 | 6 | Table of Contents 7 | ----------------- 8 | 9 | * [Introduction](#introduction) 10 | * [API](#api) 11 | * [Example](#example) 12 | * [Build & Install](#build--install) 13 | * [Origin & References](#origin--references) 14 | 15 | 16 | Introduction 17 | ------------ 18 | 19 | This is a small [line editing][] library. It can be linked into almost 20 | any program to provide command line editing and history functions. It 21 | is call compatible with the [FSF readline][] library, but at a fraction 22 | of the size, and as a result fewer features. It is also distributed 23 | under a much more liberal [License][]. 24 | 25 | The small size (<30k), lack of dependencies (ncurses not needed!), and 26 | the free license should make this library interesting to many embedded 27 | developers. 28 | 29 | Editline has several optional build-time features that can be enabled by 30 | supplying different options to the GNU configure script. See the output 31 | from configure --help for details. Some useful hints on how 32 | to use the library is available in the `examples/` directory. 33 | 34 | Editline is maintained collaboratively at [GitHub][1]. 35 | 36 | > **Note:** Windows is not a supported target for editline. 37 | 38 | 39 | Example 40 | ------- 41 | 42 | Below is a very brief example to illustrate how one can use Editline to 43 | create a simple CLI, Ctrl-D exits the program. A slightly more advanced 44 | example is Jush, , a small and very 45 | simplistic UNIX shell. The Editline sources also include an `examples/` 46 | sub-directory. 47 | 48 | 1. Build and install the library, preferably using a [release tarball][] 49 | The configure script defaults to a `/usr/local` prefix. 50 | 51 | tar xf editline-1.15.3.tar.xz 52 | cd editline-1.15.3/ 53 | ./configure --prefix=/usr 54 | make all 55 | sudo make install 56 | 57 | 2. Place the below source code in a separate project directory, 58 | e.g. `~/src/example.c` 59 | 60 | ```C 61 | #include 62 | #include 63 | #include 64 | 65 | int main(void) 66 | { 67 | char *p; 68 | 69 | while ((p = readline("CLI> ")) != NULL) { 70 | puts(p); 71 | free(p); 72 | } 73 | 74 | return 0; 75 | } 76 | ``` 77 | 78 | 3. Compile the example: 79 | 80 | cd ~/src/ 81 | make LDLIBS=-leditline example 82 | 83 | Here I use `make` and rely on its implicit (built-in) rules to handle 84 | all the compiler magic, but you may want to create your own Makefile for 85 | the project. In particular if you don't change the default prefix 86 | (above), because then you need to specify the search path for the 87 | include file(s) and the library manually. 88 | 89 | A simple `~/src/Makefile` could look like this: 90 | 91 | CFLAGS = -I/usr/local/include 92 | LDFLAGS = -L/usr/local/lib 93 | LDLIBS = -leditline 94 | EXEC = example 95 | OBJS = example.o 96 | 97 | all: $(EXEC) 98 | 99 | $(EXEC): $(OBJS) 100 | 101 | clean: 102 | $(RM) $(OBJS) $(EXEC) 103 | 104 | distclean: clean 105 | $(RM) *.o *~ *.bak 106 | 107 | Then simply type `make` from your `~/src/` directory. You can also use 108 | `pkg-config` for your `~/src/Makefile`, replace the following lines: 109 | 110 | CFLAGS = $(shell pkg-config --cflags libeditline) 111 | LDFLAGS = $(shell pkg-config --libs-only-L libeditline) 112 | LDLIBS = $(shell pkg-config --libs-only-l libeditline) 113 | 114 | Then simply type make, like above. 115 | 116 | However, most `.rpm` based distributions `pkg-config` doesn't search in 117 | `/usr/local` anymore, so you need to call make like this: 118 | 119 | PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig make 120 | 121 | Debian/Ubuntu based systems do not have this problem. 122 | 123 | 124 | API 125 | --- 126 | 127 | Here is the libeditline interfaces. It has a small compatibility layer 128 | to [FSF readline][], which may not be entirely up-to-date. 129 | 130 | ```C 131 | /* Editline specific global variables. */ 132 | int el_no_echo; /* Do not echo input characters */ 133 | int el_no_hist; /* Disable auto-save of and access to history, 134 | * e.g. for password prompts or wizards */ 135 | int el_hist_size; /* Size of history scrollback buffer, default: 15 */ 136 | 137 | /* Editline specific functions. */ 138 | char * el_find_word (void); 139 | void el_print_columns (int ac, char **av); 140 | el_status_t el_ring_bell (void); 141 | el_status_t el_del_char (void); 142 | 143 | /* Callback function for key binding */ 144 | typedef el_status_t el_keymap_func_t(void); 145 | 146 | /* Bind key to a callback, use CTL('f') to change Ctrl-F, for example */ 147 | el_status_t el_bind_key (int key, el_keymap_func_t function); 148 | el_status_t el_bind_key_in_metamap (int key, el_keymap_func_t function); 149 | 150 | /* For compatibility with FSF readline. */ 151 | int rl_point; 152 | int rl_mark; 153 | int rl_end; 154 | int rl_inhibit_complete; 155 | char *rl_line_buffer; 156 | const char *rl_readline_name; 157 | 158 | void (*rl_deprep_term_function)(void); 159 | void rl_deprep_terminal (void); 160 | void rl_reset_terminal (const char *terminal_name); 161 | 162 | void rl_initialize (void); 163 | void rl_uninitialize (void); /* Free all internal memory */ 164 | 165 | void rl_save_prompt (void); 166 | void rl_restore_prompt (void); 167 | void rl_set_prompt (const char *prompt); 168 | 169 | void rl_clear_message (void); 170 | void rl_forced_update_display (void); 171 | 172 | /* Main function to use, saves history by default */ 173 | char *readline (const char *prompt); 174 | 175 | /* Use to save a read line to history, when el_no_hist is set */ 176 | void add_history (const char *line); 177 | 178 | /* Load and save editline history from/to a file. */ 179 | int read_history (const char *filename); 180 | int write_history (const char *filename); 181 | 182 | /* Magic completion API, see examples/cli.c for more info */ 183 | rl_complete_func_t *rl_set_complete_func (rl_complete_func_t *func); 184 | rl_list_possib_func_t *rl_set_list_possib_func (rl_list_possib_func_t *func); 185 | 186 | /* Alternate interface to plain readline(), for event loops */ 187 | void rl_callback_handler_install (const char *prompt, rl_vcpfunc_t *lhandler); 188 | void rl_callback_read_char (void); 189 | void rl_callback_handler_remove (void); 190 | ``` 191 | 192 | 193 | Build & Install 194 | --------------- 195 | 196 | Editline was originally designed for older UNIX systems and Plan 9. The 197 | current maintainer works exclusively on GNU/Linux systems, so it may use 198 | GCC and GNU Make specific extensions here and there. This is not on 199 | purpose and patches or pull requests to correct this are most welcome! 200 | 201 | 0. Call ./autogen.sh if you build from git 202 | 1. Configure editline with default features: ./configure 203 | 2. Build the library and examples: make all 204 | 3. Install using make install 205 | 206 | The `$DESTDIR` environment variable is honored at install. For more 207 | options, see ./configure --help 208 | 209 | Remember to run `ldconfig` after install to update the linker cache. If 210 | you've installed to a non-standard location (`--prefix`) you may also 211 | have to update your `/etc/ld.so.conf`, or use `pkg-confg` to build your 212 | application (above). 213 | 214 | **NOTE:** RedHat/Fedora/CentOS and other `.rpm`-based distributions do 215 | not consider `/usr/local` as standard path anymore. So make sure to 216 | `./configure --prefix=/usr`, otherwise the build system use the GNU 217 | default, which is `/usr/local`. The Debian based distributions, like 218 | Ubuntu, do not have this problem. 219 | 220 | 221 | Origin & References 222 | -------------------- 223 | 224 | This [line editing][] library was created by [Rich Salz][] and Simmule 225 | Turner and in 1992. It is distributed with a “[C News][]-like” license, 226 | similar to the [BSD license][]. Rich's current version is however under 227 | the Apache license. For details on the licensing terms of this version 228 | of the software, see [License][]. 229 | 230 | This version of the editline library was forked from the [Minix 2][] 231 | source tree and is *not* related to the similarily named NetBSD version 232 | that [Jess Thrysøe][jess] disitributes to the world outside *BSD. The 233 | libraries have much in common, but the latter is heavily refactored and 234 | also relies on libtermcap (usually supplied by ncurses), whereas this 235 | library only uses termios from the standard C library. 236 | 237 | Patches and bug fixes from the following forks, based on the original 238 | [comp.sources.unix][] posting, have been merged: 239 | 240 | * Debian [libeditline][] 241 | * [Heimdal][] 242 | * [Festival][] speech-tools 243 | * [Steve Tell][]'s editline patches 244 | 245 | The version numbering scheme today follows that of the Debian version, 246 | details available in the [ChangeLog.md][]. The current [maintainer][] 247 | was unaware of the Debian version for quite some time, so a different 248 | name and versioning scheme was used. In June 2009 this was changed to 249 | line up alongside Debian, with the intent is to eventually merge the 250 | efforts. 251 | 252 | Outstanding issues are listed in the [TODO.md][] file. 253 | 254 | [1]: https://github.com/troglobit/editline 255 | [line editing]: https://github.com/troglobit/editline/blob/master/docs/README 256 | [release tarball]: https://github.com/troglobit/editline/releases 257 | [maintainer]: http://troglobit.com 258 | [C News]: https://en.wikipedia.org/wiki/C_News 259 | [TODO.md]: https://github.com/troglobit/editline/blob/master/docs/TODO.md 260 | [ChangeLog.md]: https://github.com/troglobit/editline/blob/master/ChangeLog.md 261 | [FSF readline]: http://www.gnu.org/software/readline/ 262 | [Rich Salz]: https://github.com/richsalz/editline/ 263 | [comp.sources.unix]: http://ftp.cs.toronto.edu/pub/white/pub/rc/editline.shar 264 | [Minix 2]: http://www.cise.ufl.edu/~cop4600/cgi-bin/lxr/http/source.cgi/lib/editline/ 265 | [jess]: http://thrysoee.dk/editline/ 266 | [BSD license]: http://en.wikipedia.org/wiki/BSD_licenses 267 | [libeditline]: http://packages.qa.debian.org/e/editline.html 268 | [Heimdal]: http://www.h5l.org 269 | [Festival]: http://festvox.org/festival/ 270 | [Steve Tell]: http://www.cs.unc.edu/~tell/dist.html 271 | [License]: https://github.com/troglobit/editline/blob/master/LICENSE 272 | [License Badge]: https://img.shields.io/badge/License-C%20News-orange.svg 273 | [GitHub]: https://github.com/troglobit/editline/actions/workflows/build.yml/ 274 | [GitHub Status]: https://github.com/troglobit/editline/actions/workflows/build.yml/badge.svg 275 | [Coverity Scan]: https://scan.coverity.com/projects/2982 276 | [Coverity Status]: https://scan.coverity.com/projects/2982/badge.svg 277 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -W portability -visfm 4 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(editline, 1.17.2-pre, https://github.com/troglobit/editline/issues) 2 | AC_CONFIG_AUX_DIR(aux) 3 | AM_INIT_AUTOMAKE([1.11 foreign dist-xz]) 4 | AM_SILENT_RULES([yes]) 5 | 6 | AC_CONFIG_MACRO_DIR([m4]) 7 | AC_CONFIG_SRCDIR([src/editline.c]) 8 | AC_CONFIG_HEADERS([config.h]) 9 | AC_CONFIG_FILES([Makefile libeditline.pc src/Makefile include/Makefile man/Makefile examples/Makefile]) 10 | 11 | # Checks for programs. 12 | AC_PROG_CC 13 | AC_PROG_INSTALL 14 | 15 | # Checks for libraries. 16 | LT_INIT 17 | 18 | # Checks for header files. 19 | AC_HEADER_DIRENT 20 | AC_HEADER_STAT 21 | 22 | # Check for malloc.h instead of AC_FUNC_MALLOC/REALLOC AIX and others 23 | # mess up the traditional malloc check. 24 | AC_CHECK_HEADERS([malloc.h signal.h stdlib.h string.h termcap.h termio.h termios.h sgtty.h unistd.h]) 25 | 26 | # In termios.h or in sys/ioctl.g? 27 | AC_HEADER_TIOCGWINSZ 28 | 29 | # Overrides and types, should be a check. 30 | AC_DEFINE([SYS_UNIX], [1], [Default to UNIX backend, should be detected.]) 31 | AC_TYPE_SIZE_T 32 | 33 | # Checks for library functions. 34 | AC_FUNC_CLOSEDIR_VOID 35 | AC_PROG_GCC_TRADITIONAL 36 | AC_FUNC_STAT 37 | AC_CHECK_FUNCS([strchr strdup strrchr tcgetattr perror]) 38 | 39 | # 40 | # Available features 41 | # 42 | AC_ARG_ENABLE(unique-history, 43 | [AS_HELP_STRING([--disable-unique-history], 44 | [Disable uniqify of scrollback. Default: duplicate entries are ignored. Use this to save dupes.])]) 45 | 46 | AC_ARG_ENABLE(arrow-keys, 47 | [AS_HELP_STRING([--disable-arrow-keys], [Disable ANSI arrow keys.])]) 48 | 49 | AC_ARG_ENABLE(eof, 50 | [AS_HELP_STRING([--disable-eof], [Disable default EOF (Ctrl-D) behavior.])]) 51 | 52 | AC_ARG_ENABLE(sigint, 53 | [AS_HELP_STRING([--disable-sigint], [Disable default SIGINT (Ctrl-C) behavior.])]) 54 | 55 | AC_ARG_ENABLE(sigstop, 56 | [AS_HELP_STRING([--enable-sigstop], [Enable SIGSTOP (Ctrl-Z) behavior.])]) 57 | 58 | AC_ARG_ENABLE(terminal-bell, 59 | [AS_HELP_STRING([--enable-terminal-bell], [Enable terminal bell on completion.])]) 60 | 61 | AC_ARG_ENABLE(termcap, 62 | AS_HELP_STRING([--enable-termcap], [Use termcap library to query terminal size.])) 63 | 64 | AC_ARG_ENABLE([examples], 65 | [AS_HELP_STRING([--enable-examples], [Build examples/ directory])], 66 | [], [enable_examples=no]) 67 | 68 | # 69 | # Check what features have been enabled 70 | # 71 | AS_IF([test "x$enable_unique_history" != "xno"], [ 72 | AC_DEFINE(CONFIG_UNIQUE_HISTORY, 1, [Define to skip duplicate lines in the scrollback history.])]) 73 | 74 | AS_IF([test "x$enable_terminal_bell" != "xno"], [ 75 | AC_DEFINE(CONFIG_ANSI_ARROWS, 1, [Define to include ANSI arrow keys support.])]) 76 | 77 | AS_IF([test "x$enable_eof" != "xno"], [ 78 | AC_DEFINE(CONFIG_EOF, 1, [Define to enable EOF (Ctrl-D) key.])]) 79 | 80 | AS_IF([test "x$enable_sigint" != "xno"], [ 81 | AC_DEFINE(CONFIG_SIGINT, 1, [Define to enable SIGINT (Ctrl-C) key.])]) 82 | 83 | AS_IF([test "x$enable_sigstop" = "xyes"], [ 84 | AC_DEFINE(CONFIG_SIGSTOP, 1, [Define to enable SIGSTOP (Ctrl-Z) key.])]) 85 | 86 | AS_IF([test "x$enable_terminal_bell" = "xyes"], [ 87 | AC_DEFINE(CONFIG_TERMINAL_BELL, 1, [Define to enable terminal bell on completion.])]) 88 | 89 | AM_CONDITIONAL([ENABLE_EXAMPLES], [test "$enable_examples" = yes]) 90 | 91 | # Check for a termcap compatible library if enabled 92 | AS_IF([test "x$enable_termcap" = "xyes"], [ 93 | AC_DEFINE(CONFIG_USE_TERMCAP, 1, [Define to use the termcap library for terminal size.]) 94 | AC_CHECK_LIB(terminfo, tgetent, , [ 95 | AC_CHECK_LIB(termcap, tgetent, , [ 96 | AC_CHECK_LIB(tinfo, tgetent, , [ 97 | AC_CHECK_LIB(curses, tgetent, , [ 98 | AC_CHECK_LIB(ncurses, tgetent, , [ 99 | AC_MSG_ERROR([Cannot find a termcap capable library, try installing Ncurses.])]) 100 | ]) 101 | ]) 102 | ]) 103 | ]) 104 | ]) 105 | 106 | # Generate all files 107 | AC_OUTPUT 108 | -------------------------------------------------------------------------------- /debian/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.debhelper 3 | *.substvars 4 | autoreconf.* 5 | debhelper-build-stamp 6 | files 7 | tmp/* 8 | libeditline-dev/* 9 | libeditline0/* 10 | libeditline1/* 11 | -------------------------------------------------------------------------------- /debian/README.Debian: -------------------------------------------------------------------------------- 1 | editline for Debian 2 | ---------------------- 3 | 4 | This is packaged in typical Debian form with a shared library version and 5 | a static library version. It will be a rare package that wishes to 6 | use the shared library form, the static one is only adds about 12.5kb 7 | to your program. 8 | 9 | -- Jim Studt , Fri, 5 May 2000 13:25:51 -0500 10 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | editline (1.17.1) stable; urgency=medium 2 | 3 | * Fix multiline representing as one line 4 | * Fix missing content in libedtline1, introduced in 1.16.0 5 | * Update packaging to latest std version 6 | 7 | -- Joachim Nilsson Sun, 23 Feb 2020 18:46:41 +0100 8 | 9 | editline (1.17.0) unstable; urgency=medium 10 | 11 | * Simple multi-line support 12 | * Handle internal realloc() errors better 13 | * Fix return value from read_history() and write_history() 14 | * Fix potential NULL pointer dereference in key binging lookup 15 | 16 | -- Joachim Nilsson Sun, 05 Jan 2020 09:47:34 +0100 17 | 18 | editline (1.16.1) unstable; urgency=medium 19 | 20 | * Minor bug fix and documentation update release. 21 | * Add missing pkg-config .pc file to -dev package 22 | 23 | -- Joachim Nilsson Fri, 07 Jun 2019 11:45:50 +0200 24 | 25 | editline (1.16.0) unstable; urgency=medium 26 | 27 | * New upstream release, v1.60.0 28 | + Event loop support 29 | + New GNU Readline compat functions and callbacks 30 | + Minor compat fixes for movement and deletion 31 | * Bump .so/ABI version => libeditline1 32 | * New maintainer, upstream author 33 | 34 | -- Joachim Nilsson Sun, 16 Sep 2018 09:45:53 +0200 35 | 36 | editline (1.15.3-1) unstable; urgency=medium 37 | 38 | * New upstream bug fix release, v1.15.3 39 | 40 | -- Joachim Nilsson Thu, 07 Sep 2017 01:24:19 +0200 41 | 42 | editline (1.15.2-1) unstable; urgency=medium 43 | 44 | * New upstream bug fix release, v1.15.2 45 | 46 | -- Joachim Nilsson Wed, 06 Jun 2016 20:04:35 +0200 47 | 48 | editline (1.15.1-1) unstable; urgency=medium 49 | 50 | * New upstream bug fix release, v1.15.1 51 | 52 | -- Joachim Nilsson Wed, 16 Nov 2015 21:17:17 +0200 53 | 54 | editline (1.15.0-1) unstable; urgency=medium 55 | 56 | * New upstream release, v1.15.0 57 | 58 | -- Joachim Nilsson Wed, 10 Sep 2015 13:26:03 +0200 59 | 60 | editline (1.14.2-1) unstable; urgency=low 61 | 62 | * Minor bugfix release: 63 | + Fix `el_no_echo` bug causing secrets to leak when disabling no-echo 64 | + Handle `EINTR` in syscalls better 65 | 66 | -- Joachim Nilsson Sun, 14 Sep 2014 04:27:25 +0200 67 | 68 | editline (1.14.1-1) unstable; urgency=low 69 | 70 | * New release. 71 | + Minor fixes to key binding. 72 | + Check signals and custom keys before acting on them in readline(). 73 | + Update maintainer email address. 74 | 75 | -- Joachim Nilsson Mon, 8 Jul 2013 17:04:00 +0100 76 | 77 | editline (1.14.0-1) unstable; urgency=low 78 | 79 | * Update to new configure based build. 80 | 81 | -- Joachim Nilsson Wed, 11 Aug 2010 13:28:00 +0100 82 | 83 | editline (1.12-6) unstable; urgency=low 84 | 85 | * Switch package format to 3.0 (quilt). 86 | * 200_fix-truncation-at-64-char.diff: fix invalid 64-char truncation in 87 | batch mode, courtesy of Mark O'Donohue. Thanks to Damyan Ivanov for 88 | forwarding the patch from Ubuntu (Closes: #508640). 89 | 90 | -- Sam Hocevar Sun, 03 Jan 2010 15:45:34 +0100 91 | 92 | editline (1.12-5) unstable; urgency=low 93 | 94 | * New maintainer (Closes: #229962). 95 | * debian/changelog: 96 | + Removed emacs helper variables. 97 | * debian/control: 98 | + Set policy to 3.6.1.0. 99 | + Set debhelper build dependency to (>= 4.0). 100 | + Use the default dh_shlibs information, since the API is rock stable 101 | (Closes: #131139). 102 | + Removed the libc6-dev dependency in the -dev package. 103 | + Rephrased the short and long descriptions. 104 | * debian/copyright: 105 | + Replaced "Author(s)" with "Author". 106 | * debian/rules: 107 | + Removed obsolete call to dh_suidregister. 108 | + Use debian/compat instead of DH_COMPAT. 109 | * include_editline.h: 110 | + Added a minimalist /usr/include/editline.h (Closes: #129544). 111 | * Makefile: 112 | + Call libtool with the proper --mode flags. 113 | 114 | -- Sam Hocevar (Debian packages) Sat, 31 Jan 2004 22:32:35 +0100 115 | 116 | editline (1.12-4) unstable; urgency=low 117 | 118 | * Add a Build-Depends for debhelper and libtool. Thought those 119 | were baseline. closes: #117780 120 | 121 | -- Jim Studt Thu, 15 Nov 2001 19:00:00 -0600 122 | 123 | editline (1.12-3) unstable; urgency=low 124 | 125 | * Make man pages not be in the shared library package. 126 | 127 | -- Jim Studt Sat, 13 Jan 2001 00:03:53 -0600 128 | 129 | editline (1.12-2) unstable; urgency=low 130 | 131 | * Patch to not declare read() and write(), it fails on alphas. 132 | 133 | -- Jim Studt Tue, 12 Sep 2000 16:39:34 -0500 134 | 135 | editline (1.12-1) unstable; urgency=low 136 | 137 | * Initial Release. 138 | 139 | -- Jim Studt Fri, 5 May 2000 13:25:51 -0500 140 | 141 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: editline 2 | Section: devel 3 | Priority: optional 4 | Build-Depends: debhelper (>= 10), libtool 5 | Maintainer: Joachim Wiberg 6 | Standards-Version: 4.3.0 7 | 8 | Package: libeditline-dev 9 | Architecture: any 10 | Section: libdevel 11 | Depends: libeditline1 (= ${binary:Version}), ${misc:Depends} 12 | Description: development files for libeditline 13 | This is a line-editing library. It can be linked into almost any program 14 | to provide command-line editing and recall. It is call-compatible with a 15 | subset of the FSF readline library, but it is a fraction of the size (and 16 | offers fewer features). 17 | . 18 | This package contains the developer files: static libraries, headers, 19 | manpages. 20 | 21 | Package: libeditline1 22 | Architecture: any 23 | Section: libs 24 | Depends: ${shlibs:Depends}, ${misc:Depends} 25 | Description: line editing library similar to readline 26 | This is a line-editing library. It can be linked into almost any program 27 | to provide command-line editing and recall. It is call-compatible with a 28 | subset of the FSF readline library, but it is a fraction of the size (and 29 | offers fewer features). 30 | . 31 | This package contains the runtime library only. 32 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: editline 3 | Upstream-Contact: Joachim Wiberg 4 | Source: http://github.com/troglobit/editline 5 | Comment: This package was originally debianized by Jim Studt 6 | on Fri, 5 May 2000 13:25:51 -0500. It was received from, then upstream 7 | author, Rich Salz 8 | 9 | Files: * 10 | Copyright: 1992,1993 Simmule Turner and Rich Salz. 11 | License: C-News 12 | 13 | Files: debian/* 14 | Copyright: 2010-2020 Joachim Wiberg 15 | License: BSD-2-clause 16 | 17 | License: C-News 18 | This software is not subject to any license of the American Telephone 19 | and Telegraph Company or of the Regents of the University of California. 20 | . 21 | Permission is granted to anyone to use this software for any purpose on 22 | any computer system, and to alter it and redistribute it freely, subject 23 | to the following restrictions: 24 | 1. The authors are not responsible for the consequences of use of this 25 | software, no matter how awful, even if they arise from flaws in it. 26 | 2. The origin of this software must not be misrepresented, either by 27 | explicit claim or by omission. Since few users ever read sources, 28 | credits must appear in the documentation. 29 | 3. Altered versions must be plainly marked as such, and must not be 30 | misrepresented as being the original software. Since few users 31 | ever read sources, credits must appear in the documentation. 32 | 4. This notice may not be removed or altered. 33 | -------------------------------------------------------------------------------- /debian/dirs: -------------------------------------------------------------------------------- 1 | usr/lib 2 | usr/include 3 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/libeditline-dev.install: -------------------------------------------------------------------------------- 1 | usr/include/*.h 2 | usr/lib/*/libeditline*.*a 3 | usr/lib/*/libeditline.so 4 | usr/lib/*/pkgconfig/* 5 | usr/share/man/man3/* 6 | -------------------------------------------------------------------------------- /debian/libeditline1.install: -------------------------------------------------------------------------------- 1 | usr/lib/*/libeditline.so.* 2 | -------------------------------------------------------------------------------- /debian/libeditline1.symbols: -------------------------------------------------------------------------------- 1 | libeditline.so.1 libeditline1 #MINVER# 2 | * Build-Depends-Package: libeditline-dev 3 | add_history@Base 1.17.1 4 | el_bind_key@Base 1.17.1 5 | el_bind_key_in_metamap@Base 1.17.1 6 | el_del_char@Base 1.17.1 7 | el_filename_complete@Base 1.17.1 8 | el_filename_list_possib@Base 1.17.1 9 | el_find_word@Base 1.17.1 10 | el_hist_size@Base 1.17.1 11 | el_next_hist@Base 1.17.1 12 | el_no_echo@Base 1.17.1 13 | el_no_hist@Base 1.17.1 14 | el_prev_hist@Base 1.17.1 15 | el_print_columns@Base 1.17.1 16 | el_ring_bell@Base 1.17.1 17 | prompt_len@Base 1.17.1 18 | read_history@Base 1.17.1 19 | readline@Base 1.17.1 20 | rl_add_slash@Base 1.17.1 21 | rl_attempted_completion_function@Base 1.17.1 22 | rl_attempted_completion_over@Base 1.17.1 23 | rl_callback_handler_install@Base 1.17.1 24 | rl_callback_handler_remove@Base 1.17.1 25 | rl_callback_read_char@Base 1.17.1 26 | rl_clear_message@Base 1.17.1 27 | rl_complete@Base 1.17.1 28 | rl_completion_entry_function@Base 1.17.1 29 | rl_completion_matches@Base 1.17.1 30 | rl_deprep_term_function@Base 1.17.1 31 | rl_deprep_terminal@Base 1.17.1 32 | rl_end@Base 1.17.1 33 | rl_eof@Base 1.17.1 34 | rl_erase@Base 1.17.1 35 | rl_event_hook@Base 1.17.1 36 | rl_filename_completion_function@Base 1.17.1 37 | rl_forced_update_display@Base 1.17.1 38 | rl_getc@Base 1.17.1 39 | rl_getc_function@Base 1.17.1 40 | rl_inhibit_complete@Base 1.17.1 41 | rl_initialize@Base 1.17.1 42 | rl_insert_text@Base 1.17.1 43 | rl_instream@Base 1.17.1 44 | rl_intr@Base 1.17.1 45 | rl_kill@Base 1.17.1 46 | rl_line_buffer@Base 1.17.1 47 | rl_list_possib@Base 1.17.1 48 | rl_mark@Base 1.17.1 49 | rl_meta_chars@Base 1.17.1 50 | rl_outstream@Base 1.17.1 51 | rl_point@Base 1.17.1 52 | rl_prep_term_function@Base 1.17.1 53 | rl_prep_terminal@Base 1.17.1 54 | rl_prompt@Base 1.17.1 55 | rl_quit@Base 1.17.1 56 | rl_readline_name@Base 1.17.1 57 | rl_refresh_line@Base 1.17.1 58 | rl_reset_terminal@Base 1.17.1 59 | rl_restore_prompt@Base 1.17.1 60 | rl_save_prompt@Base 1.17.1 61 | rl_set_complete_func@Base 1.17.1 62 | rl_set_getc_func@Base 1.17.1 63 | rl_set_list_possib_func@Base 1.17.1 64 | rl_set_prompt@Base 1.17.1 65 | rl_ttyset@Base 1.17.1 66 | rl_uninitialize@Base 1.17.1 67 | write_history@Base 1.17.1 68 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # export DH_VERBOSE=1 3 | export DEB_BUILD_MAINT_OPTIONS = hardening=+all 4 | export DEB_CFLAGS_MAINT_APPEND = -W -Wall -Wextra -O3 5 | export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed 6 | 7 | NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) 8 | 9 | include /usr/share/dpkg/default.mk # provides DEB_VERSION 10 | 11 | %: 12 | dh $@ --with autoreconf 13 | 14 | override_dh_installchangelogs: 15 | dh_installchangelogs ChangeLog.md 16 | 17 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /docs/HACKING.md: -------------------------------------------------------------------------------- 1 | Maintenance and Release Checklist 2 | ================================= 3 | 4 | Maintenance 5 | ----------- 6 | 7 | * Encourage contributors to write tests, in particular for new features 8 | * Run tests regularly, use Travis-CI to do this automatically 9 | * Leverage GitHub issues for milestone planning 10 | * Reference issues from GitHub pull requests to alert issue subscribers 11 | * Bump library ABI version just before release! 12 | 13 | 14 | Release Checklist 15 | ----------------- 16 | 17 | * Update ChangeLog, follow http://keepachangelog.com/ loosely 18 | - Inform users in a plain language of changes and bug fixes 19 | - Do *not* copy-paste GIT commit logs! 20 | - Order entries according to importance, most relevant first 21 | * Run unit tests: `make check` 22 | * Make at least one `-rcN` release and test it in an actual real project 23 | * **REMEMBER:** bump ABI version according to below rules 24 | * Tag 25 | * Push last commit(s) *and* tags to GitHub 26 | * Make release 27 | 28 | make distclean 29 | ./autogen.sh 30 | ./configure 31 | make release 32 | 33 | * Create new release in GitHub releases page 34 | * Copy and paste ChangeLog entry, check any stale links! 35 | * Upload release tarball and MD5 files 36 | 37 | 38 | Library Versioning 39 | ------------------ 40 | 41 | Editline relies on GNU Libtool for building the library. For a user of 42 | the library it is important to maintain a clear ABI versioning scheme. 43 | This is not the same as the Editline version, but rather the library 44 | "compatibility level". 45 | 46 | The Editline ABI version is specified in `src/Makefile.am` and looks 47 | like this: 48 | 49 | libeditline_la_LDFLAGS = -version-info 0:0:0 50 | \ \ `-- age 51 | \ `--- revision 52 | `---- current 53 | 54 | It must be updated according to the [GNU Libtool recommendations][1]: 55 | 56 | 1. Start with version information of `0:0:0` for each libtool library. 57 | 2. Update the version information only immediately before a public 58 | release of your software. More frequent updates are unnecessary, and 59 | only guarantee that the current interface number gets larger faster. 60 | 3. If the library *source code has changed at all* since the last update, 61 | then increment revision (`c:r:a` becomes `c:r+1:a`). 62 | 4. If any *interfaces have been added, removed, or changed* since the 63 | last update, increment current, and set revision to 0. 64 | 5. If any *interfaces have been added* since the last public release, 65 | then increment age. 66 | 6. If any *interfaces have been removed or changed* since the last 67 | public release, then set age to 0. 68 | 69 | The libtool ABI versioning logic is very confusing but works if you just 70 | disable your brain and follow the rules, one by one. 71 | 72 | **Example #1:** a new function has been added, none of the existing ones 73 | have changed. The initial version is 1:0:0, we follow the rules above to 74 | the letter: increase revision, increase current and set revision to zero, 75 | and finally increase age. This, rather confusingly, gives us 2:0:1 which 76 | libtool then translates to `libeditline.so.1.1.0`. 77 | 78 | **Example #2:** some existing functions are changed, they now return an 79 | `int` instead of `void`. The initial version is 0:0:0, and we follow the 80 | rules again: increment revision, increment current and set revision to 81 | zero, set age to zero. This gives us 1:0:0, which is then translated to 82 | `libeditline.so.1.0.0`. 83 | 84 | ### Note 85 | 86 | Usually, non-developers have no interest in running development versions 87 | (releases are frequent enough), and developers are expected to know how 88 | to juggle versions. In such an ideal world, it is good enough to bump 89 | the library version just prior to a release, point 2. 90 | 91 | However, if releases are few and far between, distributors may start to 92 | use snapshots. When a distributor uses a snapshot, the distributor has 93 | to handle the library version manually. Things can get ugly when the 94 | distributor has released an intermediate version with a bumped library 95 | version, and when the official release is bumped to that version, the 96 | distributor will then have to bump the library version for the official 97 | release, and it can be confusing if someone reports bugs on versions 98 | that you didn't even know existed. 99 | 100 | The problem with bumping the version with every change is that if your 101 | interface is not finished, the version number might run away, and it 102 | looks pretty bad if a library is at version 262. It kind of tells the 103 | user that the library interface is volatile, which is not good for 104 | business. 105 | 106 | [1]: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html 107 | -------------------------------------------------------------------------------- /docs/README: -------------------------------------------------------------------------------- 1 | Original Minix README 2 | ===================== 3 | 4 | Below is the original Minix editline README file. It has been 5 | split into a modified README and LICENSE file. 6 | 7 | -- Joachim Nilsson, June 7th 2008 8 | 9 | ----------------------------------------------------------------- 10 | $Revision: 5 $ 11 | 12 | This is a line-editing library. It can be linked into almost any 13 | program to provide command-line editing and recall. 14 | 15 | It is call-compatible with the FSF readline library, but it is a 16 | fraction of the size (and offers fewer features). It does not use 17 | standard I/O. It is distributed under a "C News-like" copyright. 18 | 19 | Configuration is done in the Makefile. Type "make testit" to get 20 | a small slow shell for testing. 21 | 22 | An earlier version was distributed with Byron's rc. Principal 23 | changes over that version include: 24 | Faster. 25 | Is eight-bit clean (thanks to brendan()cs!widener!edu) 26 | Written in K&R C, but ANSI compliant (gcc all warnings) 27 | Propagates EOF properly; rc trip test now passes 28 | Doesn't need or use or provide memmove. 29 | More robust 30 | Calling sequence changed to be compatible with readline. 31 | Test program, new manpage, better configuration 32 | More system-independant; includes Unix and OS-9 support. 33 | 34 | This contains some changes since the posting to comp.sources.misc: 35 | Bugfix for completion on absolute pathnames. 36 | Better handling of M-n versus showing raw 8bit chars. 37 | Better signal handling. 38 | Now supports termios/termio/sgttyb ioctl's. 39 | Add M-m command to toggle how 8bit data is displayed. 40 | 41 | There is one known bug: 42 | History-searching redraws the line wrong if the text 43 | retrieved is shorter then the prompt. 44 | 45 | Enjoy, 46 | Rich $alz 47 | 48 | 49 | Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved. 50 | 51 | This software is not subject to any license of the American Telephone 52 | and Telegraph Company or of the Regents of the University of California. 53 | 54 | Permission is granted to anyone to use this software for any purpose on 55 | any computer system, and to alter it and redistribute it freely, subject 56 | to the following restrictions: 57 | 1. The authors are not responsible for the consequences of use of this 58 | software, no matter how awful, even if they arise from flaws in it. 59 | 2. The origin of this software must not be misrepresented, either by 60 | explicit claim or by omission. Since few users ever read sources, 61 | credits must appear in the documentation. 62 | 3. Altered versions must be plainly marked as such, and must not be 63 | misrepresented as being the original software. Since few users 64 | ever read sources, credits must appear in the documentation. 65 | 4. This notice may not be removed or altered. 66 | 67 | -- 68 | $PchId: README,v 1.3 1996/02/22 21:18:51 philip Exp $ 69 | -------------------------------------------------------------------------------- /docs/TODO.md: -------------------------------------------------------------------------------- 1 | TODO 2 | ==== 3 | 4 | Issues in need of work. Mostly compatibility with GNU readline, 5 | BSD [libedit][], and usability improvements. 6 | 7 | Remember, the general idea is to keep this library small with no 8 | external dependencies, except for a generic C library. 9 | 10 | 11 | Check what's needed to run the fileman example 12 | ---------------------------------------------- 13 | 14 | The BSD libedit library has imported the GNU readline "fileman" example 15 | into its tree to demonstrate the abilities of that library. This would 16 | also be quite useful for this library! 17 | 18 | The first task is to investigate the depependencies and form TODO list 19 | items detailing what is missing and, if possible, proposals how to 20 | implement including any optional configure flags. 21 | 22 | 23 | Other minor TODO's 24 | ------------------ 25 | 26 | - Instead of supporting multiline input, try the Emacs approach, line 27 | scrolling. 28 | - Add support for `rl_bind_key()`, currently only en editline specific 29 | `el_bind_key()` exists. 30 | - Make `char *rl_prompt;` globally visible. 31 | - Add support for `rl_set_prompt()` 32 | - Add support for `--enable-utf8` to configure script 33 | - Use `strcmp(nl_langinfo(CODESET), "UTF-8")` to look for utf8 capable 34 | terminal 35 | - Implement simple UTF-8 parser according to 36 | http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 37 | 38 | 39 | [gnu]: http://www.delorie.com/gnu/docs/readline/rlman_41.html#IDX288 40 | [libuEv]: https://github.com/troglobit/libuev/ 41 | [libedit]: http://www.thrysoee.dk/editline/ 42 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | cli 3 | testit 4 | excallback 5 | fileman 6 | -------------------------------------------------------------------------------- /examples/Makefile.am: -------------------------------------------------------------------------------- 1 | noinst_PROGRAMS = testit cli excallback fileman 2 | LDADD = $(top_builddir)/src/libeditline.la 3 | AM_CPPFLAGS = -DEDITLINE_LIBRARY 4 | AM_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/include 5 | AM_LDFLAGS = -static 6 | -------------------------------------------------------------------------------- /examples/cli.c: -------------------------------------------------------------------------------- 1 | /* Custom CLI command completion. 2 | * 3 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz. All rights reserved. 4 | * 5 | * This software is not subject to any license of the American Telephone 6 | * and Telegraph Company or of the Regents of the University of California. 7 | * 8 | * Permission is granted to anyone to use this software for any purpose on 9 | * any computer system, and to alter it and redistribute it freely, subject 10 | * to the following restrictions: 11 | * 1. The authors are not responsible for the consequences of use of this 12 | * software, no matter how awful, even if they arise from flaws in it. 13 | * 2. The origin of this software must not be misrepresented, either by 14 | * explicit claim or by omission. Since few users ever read sources, 15 | * credits must appear in the documentation. 16 | * 3. Altered versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. Since few users 18 | * ever read sources, credits must appear in the documentation. 19 | * 4. This notice may not be removed or altered. 20 | */ 21 | 22 | #include "editline.h" 23 | #include 24 | 25 | #define HISTORY "/tmp/.cli-history" 26 | 27 | static char *list[] = { 28 | "foo ", "bar ", "bsd ", "cli ", "ls ", "cd ", "malloc ", "tee ", NULL 29 | }; 30 | 31 | /* Attempt to complete the pathname, returning an allocated copy. 32 | * Fill in *unique if we completed it, or set it to 0 if ambiguous. */ 33 | static char *my_rl_complete(char *token, int *match) 34 | { 35 | int i; 36 | int index = -1; 37 | int matchlen = 0; 38 | int count = 0; 39 | 40 | for (i = 0; list[i]; i++) { 41 | int partlen = strlen(token); /* Part of token */ 42 | 43 | if (!strncmp(list[i], token, partlen)) { 44 | index = i; 45 | matchlen = partlen; 46 | count ++; 47 | } 48 | } 49 | 50 | if (count == 1) { 51 | *match = 1; 52 | return strdup(list[index] + matchlen); 53 | } 54 | 55 | return NULL; 56 | } 57 | 58 | /* Return all possible completions. */ 59 | static int my_rl_list_possib(char *token, char ***av) 60 | { 61 | int i, num, total = 0; 62 | char **copy; 63 | 64 | for (num = 0; list[num]; num++) 65 | ; 66 | 67 | if (!num) 68 | return 0; 69 | 70 | copy = malloc(num * sizeof(char *)); 71 | for (i = 0; i < num; i++) { 72 | if (!strncmp(list[i], token, strlen (token))) { 73 | copy[total] = strdup(list[i]); 74 | total++; 75 | } 76 | } 77 | *av = copy; 78 | 79 | return total; 80 | } 81 | 82 | el_status_t list_possible(void) 83 | { 84 | char **av; 85 | char *word; 86 | int ac; 87 | 88 | word = el_find_word(); 89 | ac = rl_list_possib(word, &av); 90 | if (word) 91 | free(word); 92 | if (ac) { 93 | el_print_columns(ac, av); 94 | while (--ac >= 0) 95 | free(av[ac]); 96 | free(av); 97 | 98 | return CSmove; 99 | } 100 | 101 | return el_ring_bell(); 102 | } 103 | 104 | el_status_t do_suspend(void) 105 | { 106 | puts("Abort!"); 107 | return CSstay; 108 | } 109 | 110 | static void breakit(int signo) 111 | { 112 | (void)signo; 113 | puts("Got SIGINT"); 114 | } 115 | 116 | /* Use el_no_echo when reading passwords and similar */ 117 | static int unlock(const char *passwd) 118 | { 119 | char *prompt = "Enter password: "; 120 | char *line; 121 | int rc = 1; 122 | 123 | el_no_echo = 1; 124 | 125 | while ((line = readline(prompt))) { 126 | rc = strncmp(line, passwd, strlen(passwd)); 127 | free(line); 128 | 129 | if (rc) { 130 | printf("\nWrong password, please try again, it's secret.\n"); 131 | continue; 132 | } 133 | 134 | printf("\nAchievement unlocked!\n"); 135 | break; 136 | } 137 | 138 | el_no_echo = 0; 139 | 140 | return rc; 141 | } 142 | 143 | int main(void) 144 | { 145 | char *line; 146 | char *prompt = "cli> "; 147 | 148 | signal(SIGINT, breakit); 149 | 150 | /* Setup callbacks */ 151 | rl_set_complete_func(&my_rl_complete); 152 | rl_set_list_possib_func(&my_rl_list_possib); 153 | 154 | el_bind_key('?', list_possible); 155 | el_bind_key(CTL('Z'), do_suspend); 156 | read_history(HISTORY); 157 | 158 | while ((line = readline(prompt))) { 159 | if (!strncmp(line, "unlock", 6) && unlock("secret")) { 160 | free(line); 161 | fprintf(stderr, "\nSecurity breach, user logged out!\n"); 162 | break; 163 | } 164 | 165 | if (*line != '\0') 166 | printf("\t\t\t|%s|\n", line); 167 | free(line); 168 | } 169 | 170 | write_history(HISTORY); 171 | 172 | return 0; 173 | } 174 | 175 | /** 176 | * Local Variables: 177 | * c-file-style: "k&r" 178 | * c-basic-offset: 4 179 | * End: 180 | */ 181 | -------------------------------------------------------------------------------- /examples/excallback.c: -------------------------------------------------------------------------------- 1 | /* 2 | From: Jeff Solomon 3 | Date: Fri, 9 Apr 1999 10:13:27 -0700 (PDT) 4 | To: chet@po.cwru.edu 5 | Subject: new readline example 6 | Message-ID: <14094.12094.527305.199695@mrclean.Stanford.EDU> 7 | 8 | Chet, 9 | 10 | I've been using readline 4.0. Specifically, I've been using the perl 11 | version Term::ReadLine::Gnu. It works great. 12 | 13 | Anyway, I've been playing around the alternate interface and I wanted 14 | to contribute a little C program, callback.c, to you that you could 15 | use as an example of the alternate interface in the /examples 16 | directory of the readline distribution. 17 | 18 | My example shows how, using the alternate interface, you can 19 | interactively change the prompt (which is very nice imo). Also, I 20 | point out that you must roll your own terminal setting when using the 21 | alternate interface because readline depreps (using your parlance) the 22 | terminal while in the user callback. I try to demostrate what I mean 23 | with an example. I've included the program below. 24 | 25 | To compile, I just put the program in the examples directory and made 26 | the appropriate changes to the EXECUTABLES and OBJECTS line and added 27 | an additional target 'callback'. 28 | 29 | I compiled on my Sun Solaris2.6 box using Sun's cc. 30 | 31 | Let me know what you think. 32 | 33 | Jeff 34 | */ 35 | 36 | #if defined (HAVE_CONFIG_H) 37 | #include 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | #ifdef HAVE_STDLIB_H 45 | #include 46 | #endif 47 | 48 | #ifdef HAVE_UNISTD_H 49 | #include 50 | #endif 51 | 52 | #include /* xxx - should make this more general */ 53 | 54 | #ifdef EDITLINE_LIBRARY 55 | # include "editline.h" 56 | #else 57 | # include 58 | #endif 59 | 60 | /* This little examples demonstrates the alternate interface to using readline. 61 | * In the alternate interface, the user maintains control over program flow and 62 | * only calls readline when STDIN is readable. Using the alternate interface, 63 | * you can do anything else while still using readline (like talking to a 64 | * network or another program) without blocking. 65 | * 66 | * Specifically, this program highlights two importants features of the 67 | * alternate interface. The first is the ability to interactively change the 68 | * prompt, which can't be done using the regular interface since rl_prompt is 69 | * read-only. 70 | * 71 | * The second feature really highlights a subtle point when using the alternate 72 | * interface. That is, readline will not alter the terminal when inside your 73 | * callback handler. So let's so, your callback executes a user command that 74 | * takes a non-trivial amount of time to complete (seconds). While your 75 | * executing the command, the user continues to type keystrokes and expects them 76 | * to be re-echoed on the new prompt when it returns. Unfortunately, the default 77 | * terminal configuration doesn't do this. After the prompt returns, the user 78 | * must hit one additional keystroke and then will see all of his previous 79 | * keystrokes. To illustrate this, compile and run this program. Type "sleep" at 80 | * the prompt and then type "bar" before the prompt returns (you have 3 81 | * seconds). Notice how "bar" is re-echoed on the prompt after the prompt 82 | * returns? This is what you expect to happen. Now comment out the 4 lines below 83 | * the line that says COMMENT LINE BELOW. Recompile and rerun the program and do 84 | * the same thing. When the prompt returns, you should not see "bar". Now type 85 | * "f", see how "barf" magically appears? This behavior is un-expected and not 86 | * desired. 87 | */ 88 | 89 | void process_line(char *line); 90 | int change_prompt(void); 91 | char *get_prompt(void); 92 | 93 | int prompt = 1; 94 | char prompt_buf[40], line_buf[256]; 95 | tcflag_t old_lflag; 96 | cc_t old_vtime; 97 | struct termios term; 98 | 99 | int 100 | main() 101 | { 102 | fd_set fds; 103 | 104 | /* Adjust the terminal slightly before the handler is installed. Disable 105 | * canonical mode processing and set the input character time flag to be 106 | * non-blocking. 107 | */ 108 | if( tcgetattr(STDIN_FILENO, &term) < 0 ) { 109 | perror("tcgetattr"); 110 | exit(1); 111 | } 112 | old_lflag = term.c_lflag; 113 | old_vtime = term.c_cc[VTIME]; 114 | term.c_lflag &= ~ICANON; 115 | term.c_cc[VTIME] = 1; 116 | /* COMMENT LINE BELOW - see above */ 117 | if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) { 118 | perror("tcsetattr"); 119 | exit(1); 120 | } 121 | 122 | // rl_add_defun("change-prompt", change_prompt, CTRL('t')); 123 | rl_callback_handler_install(get_prompt(), process_line); 124 | 125 | while(1) { 126 | FD_ZERO(&fds); 127 | FD_SET(fileno(stdin), &fds); 128 | 129 | if( select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) { 130 | perror("select"); 131 | exit(1); 132 | } 133 | 134 | if( FD_ISSET(fileno(stdin), &fds) ) { 135 | rl_callback_read_char(); 136 | } 137 | } 138 | 139 | return 0; 140 | } 141 | 142 | void 143 | process_line(char *line) 144 | { 145 | if( line == NULL ) { 146 | fprintf(stderr, "\n"); 147 | 148 | /* reset the old terminal setting before exiting */ 149 | term.c_lflag = old_lflag; 150 | term.c_cc[VTIME] = old_vtime; 151 | if( tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) { 152 | perror("tcsetattr"); 153 | exit(1); 154 | } 155 | exit(0); 156 | } 157 | 158 | if( strcmp(line, "sleep") == 0 ) { 159 | sleep(3); 160 | } else { 161 | fprintf(stderr, "|%s|\n", line); 162 | } 163 | 164 | free (line); 165 | } 166 | 167 | int 168 | change_prompt(void) 169 | { 170 | /* toggle the prompt variable */ 171 | prompt = !prompt; 172 | 173 | /* save away the current contents of the line */ 174 | strncpy(line_buf, rl_line_buffer, sizeof(line_buf)); 175 | line_buf[sizeof(line_buf) - 1] = 0; 176 | 177 | /* install a new handler which will change the prompt and erase the current line */ 178 | rl_callback_handler_install(get_prompt(), process_line); 179 | 180 | /* insert the old text on the new line */ 181 | rl_insert_text(line_buf); 182 | 183 | /* redraw the current line - this is an undocumented function. It invokes the 184 | * redraw-current-line command. 185 | */ 186 | return rl_refresh_line(0, 0); 187 | } 188 | 189 | char * 190 | get_prompt(void) 191 | { 192 | /* The prompts can even be different lengths! */ 193 | sprintf(prompt_buf, "%s", 194 | prompt ? "Hit ctrl-t to toggle prompt> " : "Pretty cool huh?> "); 195 | return prompt_buf; 196 | } 197 | -------------------------------------------------------------------------------- /examples/fileman.c: -------------------------------------------------------------------------------- 1 | /* fileman.c -- A tiny application which demonstrates how to use the 2 | GNU Readline library. This application interactively allows users 3 | to manipulate files and their modes. 4 | 5 | NOTE: this was taken from the GNU Readline documentation and ported 6 | to libedit. A command to output the history list was added. 7 | 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "editline.h" 24 | 25 | void too_dangerous(char *caller); 26 | void initialize_readline(const char *prompt); 27 | int execute_line(char *line); 28 | int valid_argument(char *caller, char *arg); 29 | 30 | /* The names of functions that actually do the manipulation. */ 31 | int com_list(char *); 32 | int com_view(char *); 33 | int com_history(char *); 34 | int com_rename(char *); 35 | int com_stat(char *); 36 | int com_pwd(char *); 37 | int com_delete(char *); 38 | int com_help(char *); 39 | int com_cd(char *); 40 | int com_quit(char *); 41 | 42 | struct cmd { 43 | char *name; /* User printable name of the function. */ 44 | int (*func)(char *); /* Function to call to do the job. */ 45 | char *doc; /* Documentation for this function. */ 46 | }; 47 | 48 | struct cmd commands[] = { 49 | { "cd", com_cd, "Change to directory DIR"}, 50 | { "delete", com_delete, "Delete FILE"}, 51 | { "help", com_help, "Display this text"}, 52 | { "?", com_help, "Synonym for `help'"}, 53 | { "list", com_list, "List files in DIR"}, 54 | { "ls", com_list, "Synonym for `list'"}, 55 | { "pwd", com_pwd, "Print the current working directory"}, 56 | { "quit", com_quit, "Quit using Fileman"}, 57 | { "rename", com_rename, "Rename FILE to NEWNAME"}, 58 | { "stat", com_stat, "Print out statistics on FILE"}, 59 | { "view", com_view, "View the contents of FILE"}, 60 | { "history", com_history, "List editline history"}, 61 | { NULL, NULL, NULL }, 62 | }; 63 | 64 | /* Forward declarations. */ 65 | char *stripwhite(char *string); 66 | struct cmd *find_command(char *name); 67 | 68 | /* ~/.fileman_history */ 69 | char *fileman_history; 70 | 71 | /* Prompt base and current */ 72 | const char *prompt_init; 73 | char *prompt_curr; 74 | 75 | /* When non-zero, this means the user is done using this program. */ 76 | int done; 77 | 78 | int main(int argc, char **argv) 79 | { 80 | char *line, *s; 81 | 82 | setlocale(LC_CTYPE, ""); 83 | initialize_readline("(FileMan)"); 84 | 85 | /* Loop reading and executing lines until the user quits. */ 86 | for (; done == 0;) { 87 | line = readline(NULL); 88 | 89 | if (!line) 90 | break; 91 | 92 | /* Remove leading and trailing whitespace from the line. 93 | Then, if there is anything left, add it to the history list 94 | and execute it. */ 95 | s = stripwhite(line); 96 | #if 0 97 | if (*s) { 98 | 99 | char *expansion; 100 | int result; 101 | 102 | result = history_expand(s, &expansion); 103 | if (result < 0 || result == 2) { 104 | fprintf(stderr, "%s\n", expansion); 105 | } else { 106 | add_history(expansion); 107 | execute_line(expansion); 108 | } 109 | free(expansion); 110 | } 111 | #else 112 | execute_line(s); 113 | #endif 114 | free(line); 115 | } 116 | 117 | puts(""); 118 | write_history(fileman_history); 119 | free(fileman_history); 120 | 121 | return 0; 122 | } 123 | 124 | /* Execute a command line. */ 125 | int execute_line(char *line) 126 | { 127 | int i; 128 | struct cmd *command; 129 | char *word; 130 | 131 | /* Isolate the command word. */ 132 | i = 0; 133 | while (line[i] && isspace(line[i])) 134 | i++; 135 | word = line + i; 136 | 137 | while (line[i] && !isspace(line[i])) 138 | i++; 139 | 140 | if (line[i]) 141 | line[i++] = '\0'; 142 | 143 | command = find_command(word); 144 | 145 | if (!command) { 146 | fprintf(stderr, "%s: No such command for FileMan.\n", word); 147 | return -1; 148 | } 149 | 150 | /* Get argument to command, if any. */ 151 | while (isspace(line[i])) 152 | i++; 153 | 154 | word = line + i; 155 | 156 | /* Call the function. */ 157 | return command->func(word); 158 | } 159 | 160 | /* Look up NAME as the name of a command, and return a pointer to that 161 | command. Return a NULL pointer if NAME isn't a command name. */ 162 | struct cmd *find_command(char *name) 163 | { 164 | int i; 165 | 166 | for (i = 0; commands[i].name; i++) 167 | if (strcmp(name, commands[i].name) == 0) 168 | return &commands[i]; 169 | 170 | return NULL; 171 | } 172 | 173 | /* 174 | * Strip whitespace from the start and end of STRING. Return a pointer 175 | * into STRING. 176 | */ 177 | char *stripwhite(char *string) 178 | { 179 | char *s, *t; 180 | 181 | for (s = string; isspace(*s); s++) ; 182 | 183 | if (*s == 0) 184 | return s; 185 | 186 | t = s + strlen(s) - 1; 187 | while (t > s && isspace(*t)) 188 | t--; 189 | *++t = '\0'; 190 | 191 | return s; 192 | } 193 | 194 | /* **************************************************************** */ 195 | /* */ 196 | /* Interface to Readline Completion */ 197 | /* */ 198 | /* **************************************************************** */ 199 | 200 | char *command_generator(const char *, int); 201 | char **fileman_completion(const char *, int, int); 202 | void fileman_prompt(void); 203 | 204 | /* 205 | * Tell the GNU Readline library how to complete. We want to try to 206 | * complete on command names if this is the first word in the line, or 207 | * on filenames if not. 208 | */ 209 | void initialize_readline(const char *prompt) 210 | { 211 | const char *home; 212 | size_t len; 213 | 214 | /* Allow conditional parsing of the ~/.inputrc file. */ 215 | rl_readline_name = "FileMan"; 216 | 217 | /* Tell the completer that we want a crack first. */ 218 | rl_attempted_completion_function = fileman_completion; 219 | 220 | /* Restore command history */ 221 | home = getenv("HOME"); 222 | len = (home ? strlen(home) : 0) + 14; 223 | fileman_history = malloc(len); 224 | assert(fileman_history); 225 | snprintf(fileman_history, len, "%s/.fileman_history", home ? home : "."); 226 | 227 | read_history(fileman_history); 228 | 229 | /* Prompt is updated when moving around in the tree */ 230 | prompt_init = prompt; 231 | fileman_prompt(); 232 | } 233 | 234 | /* 235 | * Update prompt when changing directory. Use an allocated string to 236 | * show off the rl_set_prompt() API for issue #51. 237 | */ 238 | void fileman_prompt(void) 239 | { 240 | char cwd[1024]; 241 | size_t len; 242 | 243 | if (prompt_curr) 244 | free(prompt_curr); 245 | 246 | assert(getcwd(cwd, sizeof(cwd))); 247 | len = strlen(prompt_init) + strlen(cwd) + 10; 248 | prompt_curr = malloc(len); 249 | assert(prompt_curr); 250 | 251 | snprintf(prompt_curr, len, "%s %s/> ", prompt_init, cwd); 252 | 253 | rl_set_prompt(prompt_curr); 254 | } 255 | 256 | /* 257 | * Attempt to complete on the contents of TEXT. START and END 258 | * bound the region of rl_line_buffer that contains the word to 259 | * complete. TEXT is the word to complete. We can use the entire 260 | * contents of rl_line_buffer in case we want to do some simple 261 | * parsing. Returnthe array of matches, or NULL if there aren't any. 262 | */ 263 | char **fileman_completion(const char *text, int start, int end) 264 | { 265 | char **matches = NULL; 266 | 267 | /* If this word is at the start of the line, then it is a command 268 | to complete. Otherwise it is the name of a file in the current 269 | directory. */ 270 | if (start == 0) 271 | matches = rl_completion_matches(text, command_generator); 272 | 273 | return matches; 274 | } 275 | 276 | /* Generator function for command completion. STATE lets us 277 | know whether to start from scratch; without any state 278 | (i.e. STATE == 0), then we start at the top of the list. */ 279 | char *command_generator(const char *text, int state) 280 | { 281 | static int list_index, len; 282 | char *name; 283 | 284 | /* If this is a new word to complete, initialize now. This 285 | includes saving the length of TEXT for efficiency, and 286 | initializing the index variable to 0. */ 287 | if (!state) { 288 | list_index = 0; 289 | len = strlen(text); 290 | } 291 | 292 | /* Return the next name which partially matches from the 293 | command list. */ 294 | while ((name = commands[list_index].name)) { 295 | list_index++; 296 | 297 | if (strncmp(name, text, len) == 0) 298 | return strdup(name); 299 | } 300 | 301 | /* If no names matched, then return NULL. */ 302 | return NULL; 303 | } 304 | 305 | /* **************************************************************** */ 306 | /* */ 307 | /* FileMan Commands */ 308 | /* */ 309 | /* **************************************************************** */ 310 | 311 | /* String to pass to system (). This is for the LIST, VIEW and RENAME 312 | commands. */ 313 | static char syscom[1024]; 314 | 315 | /* List the file(s) named in arg. */ 316 | int com_list(char *arg) 317 | { 318 | if (!arg) 319 | arg = ""; 320 | 321 | sprintf(syscom, "ls -FClg %s", arg); 322 | 323 | return system(syscom); 324 | } 325 | 326 | int com_view(char *arg) 327 | { 328 | if (!valid_argument("view", arg)) 329 | return 1; 330 | 331 | sprintf(syscom, "more %s", arg); 332 | 333 | return system(syscom); 334 | } 335 | 336 | int com_history(char *arg) 337 | { 338 | const char *he; 339 | 340 | /* rewind history */ 341 | while (el_prev_hist()) ; 342 | 343 | for (he = el_next_hist(); he != NULL; he = el_next_hist()) 344 | printf("%s\n", he); 345 | 346 | return 0; 347 | } 348 | 349 | int com_rename(char *arg) 350 | { 351 | too_dangerous("rename"); 352 | return 1; 353 | } 354 | 355 | int com_stat(char *arg) 356 | { 357 | struct stat finfo; 358 | 359 | if (!valid_argument("stat", arg)) 360 | return 1; 361 | 362 | if (stat(arg, &finfo) == -1) { 363 | perror(arg); 364 | return 1; 365 | } 366 | 367 | printf("Statistics for `%s':\n", arg); 368 | 369 | printf("%s has %ld link%s, and is %lld byte%s in length.\n", arg, 370 | (long)finfo.st_nlink, 371 | (finfo.st_nlink == 1) ? "" : "s", 372 | (long long)finfo.st_size, (finfo.st_size == 1) ? "" : "s"); 373 | printf("Inode Last Change at: %s", ctime(&finfo.st_ctime)); 374 | printf(" Last access at: %s", ctime(&finfo.st_atime)); 375 | printf(" Last modified at: %s", ctime(&finfo.st_mtime)); 376 | 377 | return 0; 378 | } 379 | 380 | int com_delete(char *arg) 381 | { 382 | too_dangerous("delete"); 383 | return 1; 384 | } 385 | 386 | /* Print out help for ARG, or for all of the commands if ARG is 387 | not present. */ 388 | int com_help(char *arg) 389 | { 390 | int i; 391 | int printed = 0; 392 | 393 | for (i = 0; commands[i].name; i++) { 394 | if (!*arg || (strcmp(arg, commands[i].name) == 0)) { 395 | printf("%s\t\t%s.\n", commands[i].name, 396 | commands[i].doc); 397 | printed++; 398 | } 399 | } 400 | 401 | if (!printed) { 402 | printf("No commands match `%s'. Possibilties are:\n", arg); 403 | 404 | for (i = 0; commands[i].name; i++) { 405 | /* Print in six columns. */ 406 | if (printed == 6) { 407 | printed = 0; 408 | printf("\n"); 409 | } 410 | 411 | printf("%s\t", commands[i].name); 412 | printed++; 413 | } 414 | 415 | if (printed) 416 | printf("\n"); 417 | } 418 | 419 | return 0; 420 | } 421 | 422 | /* Change to the directory ARG. */ 423 | int com_cd(char *arg) 424 | { 425 | if (chdir(arg) == -1) { 426 | perror(arg); 427 | return 1; 428 | } 429 | 430 | //com_pwd(""); 431 | fileman_prompt(); 432 | return 0; 433 | } 434 | 435 | /* Print out the current working directory. */ 436 | int com_pwd(char *ignore) 437 | { 438 | char dir[1024], *s; 439 | 440 | s = getcwd(dir, sizeof(dir) - 1); 441 | if (!s) { 442 | printf("Error getting pwd: %s\n", dir); 443 | return 1; 444 | } 445 | 446 | printf("Current directory is %s\n", dir); 447 | return 0; 448 | } 449 | 450 | /* The user wishes to quit using this program. Just set DONE 451 | non-zero. */ 452 | int com_quit(char *arg) 453 | { 454 | done = 1; 455 | return 0; 456 | } 457 | 458 | /* Function which tells you that you can't do this. */ 459 | void too_dangerous(char *caller) 460 | { 461 | fprintf(stderr, "%s: Too dangerous for me to distribute.\n", caller); 462 | fprintf(stderr, "Write it yourself.\n"); 463 | } 464 | 465 | /* Return non-zero if ARG is a valid argument for CALLER, 466 | else print an error message and return zero. */ 467 | int valid_argument(char *caller, char *arg) 468 | { 469 | if (!arg || !*arg) { 470 | fprintf(stderr, "%s: Argument required.\n", caller); 471 | return 0; 472 | } 473 | 474 | return 1; 475 | } 476 | 477 | /** 478 | * Local Variables: 479 | * c-file-style: "k&r" 480 | * c-basic-offset: 4 481 | * End: 482 | */ 483 | -------------------------------------------------------------------------------- /examples/testit.c: -------------------------------------------------------------------------------- 1 | /* A "micro-shell" to test editline library. 2 | * If given any arguments, commands aren't executed. 3 | * 4 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz. All rights reserved. 5 | * 6 | * This software is not subject to any license of the American Telephone 7 | * and Telegraph Company or of the Regents of the University of California. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose on 10 | * any computer system, and to alter it and redistribute it freely, subject 11 | * to the following restrictions: 12 | * 1. The authors are not responsible for the consequences of use of this 13 | * software, no matter how awful, even if they arise from flaws in it. 14 | * 2. The origin of this software must not be misrepresented, either by 15 | * explicit claim or by omission. Since few users ever read sources, 16 | * credits must appear in the documentation. 17 | * 3. Altered versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. Since few users 19 | * ever read sources, credits must appear in the documentation. 20 | * 4. This notice may not be removed or altered. 21 | */ 22 | #include 23 | #include 24 | #ifdef HAVE_STDLIB_H 25 | #include 26 | #endif 27 | #ifdef HAVE_STRING_H 28 | #include 29 | #endif 30 | #ifdef HAVE_UNISTD_H 31 | #include 32 | #endif 33 | #include "editline.h" 34 | 35 | #ifndef HAVE_PERROR 36 | extern int errno; 37 | void perror(char *s) 38 | { 39 | fprintf(stderr, "%s: error %d\n", s, errno); 40 | } 41 | #endif /* !HAVE_PERROR */ 42 | 43 | int main(int argc, char *argv[] __attribute__ ((unused))) 44 | { 45 | int doit; 46 | char *prompt, *p; 47 | 48 | read_history(".testit_history"); 49 | 50 | doit = argc == 1; 51 | if ((prompt = getenv("TESTPROMPT")) == NULL) 52 | prompt = "testit> "; 53 | 54 | while ((p = readline(prompt)) != NULL) { 55 | printf("\t\t\t|%s|\n", p); 56 | if (doit) { 57 | if (strncmp(p, "cd ", 3) == 0) { 58 | if (chdir(&p[3]) < 0) 59 | perror(&p[3]); 60 | } else if (system(p) != 0) { 61 | perror(p); 62 | } 63 | } 64 | free(p); 65 | } 66 | 67 | write_history(".testit_history"); 68 | rl_uninitialize(); 69 | 70 | return 0; 71 | } 72 | 73 | /** 74 | * Local Variables: 75 | * c-file-style: "k&r" 76 | * c-basic-offset: 4 77 | * End: 78 | */ 79 | -------------------------------------------------------------------------------- /include/Makefile.am: -------------------------------------------------------------------------------- 1 | library_includedir = $(includedir) 2 | library_include_HEADERS = editline.h 3 | -------------------------------------------------------------------------------- /include/editline.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz 3 | * All rights reserved. 4 | * 5 | * This software is not subject to any license of the American Telephone 6 | * and Telegraph Company or of the Regents of the University of California. 7 | * 8 | * Permission is granted to anyone to use this software for any purpose on 9 | * any computer system, and to alter it and redistribute it freely, subject 10 | * to the following restrictions: 11 | * 1. The authors are not responsible for the consequences of use of this 12 | * software, no matter how awful, even if they arise from flaws in it. 13 | * 2. The origin of this software must not be misrepresented, either by 14 | * explicit claim or by omission. Since few users ever read sources, 15 | * credits must appear in the documentation. 16 | * 3. Altered versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. Since few users 18 | * ever read sources, credits must appear in the documentation. 19 | * 4. This notice may not be removed or altered. 20 | */ 21 | #ifndef EDITLINE_H_ 22 | #define EDITLINE_H_ 23 | 24 | #include 25 | 26 | /* Handy macros when binding keys. */ 27 | #define CTL(x) ((x) & 0x1F) 28 | #define ISCTL(x) ((x) && (x) < ' ') 29 | #define UNCTL(x) ((x) + 64) 30 | #define META(x) ((x) | 0x80) 31 | #define ISMETA(x) ((x) & 0x80) 32 | #define UNMETA(x) ((x) & 0x7F) 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | /* Command status codes. */ 39 | typedef enum { 40 | CSdone = 0, /* OK */ 41 | CSeof, /* Error, or EOF */ 42 | CSmove, 43 | CSdispatch, 44 | CSstay, 45 | CSsignal 46 | } el_status_t; 47 | 48 | /* Editline specific types, despite rl_ prefix. From Heimdal project. */ 49 | typedef int rl_list_possib_func_t(char*, char***); 50 | typedef el_status_t el_keymap_func_t(void); 51 | typedef int rl_hook_func_t(void); 52 | typedef int rl_getc_func_t(void); 53 | typedef void rl_voidfunc_t(void); 54 | typedef void rl_vintfunc_t(int); 55 | typedef void rl_vcpfunc_t(char *); 56 | 57 | /* FSF Readline compat tupes */ 58 | typedef char *rl_complete_func_t (char *, int*); 59 | typedef char *rl_compentry_func_t (const char *, int); 60 | typedef char **rl_completion_func_t (const char *, int, int); 61 | 62 | /* Display 8-bit chars "as-is" or as `M-x'? Toggle with M-m. (Default:0 - "as-is") */ 63 | extern int rl_meta_chars; 64 | 65 | /* Editline specific functions. */ 66 | extern char * el_find_word(void); 67 | extern void el_print_columns(int ac, char **av); 68 | extern el_status_t el_ring_bell(void); 69 | extern el_status_t el_del_char(void); 70 | 71 | extern el_status_t el_bind_key(int key, el_keymap_func_t function); 72 | extern el_status_t el_bind_key_in_metamap(int key, el_keymap_func_t function); 73 | 74 | extern const char *el_next_hist(void); 75 | extern const char *el_prev_hist(void); 76 | 77 | extern char *rl_complete(char *token, int *match); 78 | extern int rl_list_possib(char *token, char ***av); 79 | extern char **rl_completion_matches(const char *token, rl_compentry_func_t *generator); 80 | extern char *rl_filename_completion_function(const char *text, int state); 81 | 82 | /* For compatibility with FSF readline. */ 83 | extern int rl_point; 84 | extern int rl_mark; 85 | extern int rl_end; 86 | extern int rl_inhibit_complete; 87 | extern int rl_attempted_completion_over; 88 | extern char *rl_line_buffer; 89 | extern const char *rl_readline_name; 90 | extern FILE *rl_instream; /* The stdio stream from which input is read. Defaults to stdin if NULL - Not supported yet! */ 91 | extern FILE *rl_outstream; /* The stdio stream to which output is flushed. Defaults to stdout if NULL - Not supported yet! */ 92 | extern int el_no_echo; /* E.g under emacs, don't echo except prompt */ 93 | extern int el_no_hist; /* Disable auto-save of and access to history -- e.g. for password prompts or wizards */ 94 | extern int el_hist_size; /* size of history scrollback buffer, default: 15 */ 95 | 96 | extern void rl_initialize (void); 97 | extern void rl_reset_terminal (const char *terminal_name); 98 | extern void rl_uninitialize (void); 99 | 100 | extern void rl_save_prompt (void); 101 | extern void rl_restore_prompt (void); 102 | extern void rl_set_prompt (const char *prompt); 103 | 104 | extern void rl_clear_message (void); 105 | extern void rl_forced_update_display(void); 106 | 107 | extern void rl_prep_terminal (int meta_flag); 108 | extern void rl_deprep_terminal (void); 109 | 110 | extern int rl_getc(void); 111 | extern int rl_insert_text (const char *text); 112 | extern int rl_refresh_line (int ignore1, int ignore2); 113 | 114 | extern char *readline (const char *prompt); 115 | 116 | extern void add_history (const char *line); 117 | extern int read_history (const char *filename); 118 | extern int write_history (const char *filename); 119 | 120 | extern rl_getc_func_t *rl_set_getc_func(rl_getc_func_t *func); 121 | 122 | extern rl_completion_func_t *rl_attempted_completion_function; 123 | extern rl_complete_func_t *rl_set_complete_func (rl_complete_func_t *func); 124 | extern rl_list_possib_func_t *rl_set_list_possib_func (rl_list_possib_func_t *func); 125 | 126 | /* Alternate interface to plain readline(), for event loops */ 127 | extern void rl_callback_handler_install (const char *prompt, rl_vcpfunc_t *lhandler); 128 | extern void rl_callback_read_char (void); 129 | extern void rl_callback_handler_remove (void); 130 | 131 | #ifdef __cplusplus 132 | } 133 | #endif 134 | 135 | #endif /* EDITLINE_H_ */ 136 | -------------------------------------------------------------------------------- /libeditline.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: @PACKAGE@ 7 | Description: A small line editing library without termcap/curses 8 | Version: @VERSION@ 9 | Requires: 10 | Libs: -L${libdir} -leditline 11 | Cflags: -I${includedir} 12 | 13 | -------------------------------------------------------------------------------- /m4/.gitignore: -------------------------------------------------------------------------------- 1 | *.m4 2 | -------------------------------------------------------------------------------- /man/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = foreign 2 | 3 | dist_man_MANS = editline.3 4 | -------------------------------------------------------------------------------- /man/editline.3: -------------------------------------------------------------------------------- 1 | .Dd February 23, 2020 2 | .Dt EDITLINE 3 3 | .Os 4 | .Sh NAME 5 | .Nm editline 6 | .Nd command-line editing library with history 7 | .Sh LIBRARY 8 | .Lb libeditline 9 | .Sh SYNOPSIS 10 | .In editline.h 11 | .Ft char * 12 | .Fo readline 13 | .Fa const char *prompt 14 | .Fc 15 | .Ft void 16 | .Fo add_history 17 | .Fa const char *line 18 | .Fc 19 | .Ft int 20 | .Fo read_history 21 | .Fa const char *filename 22 | .Fc 23 | .Ft int 24 | .Fo write_history 25 | .Fa const char *filename 26 | .Fc 27 | .Sh DESCRIPTION 28 | .Nm 29 | is a library that provides n line-editing interface with history. It 30 | is intended to be functionally equivalent with the 31 | .Nm readline 32 | library provided by the Free Software Foundation, but much smaller. The 33 | bulk of this manual page describes the basic user interface. More APIs, 34 | both native and for 35 | .Nm readline 36 | compatibility , 37 | are also available. See the 38 | .Cm editline.h 39 | header file for details. 40 | .Pp 41 | The 42 | .Fn readline 43 | function displays the given 44 | .Fa prompt 45 | on stdout, waits for user input on stdin and then returns a line of text 46 | with the trailing newline removed. The data is returned in a buffer 47 | allocated with 48 | .Xr malloc 3 , 49 | so the space should be released with 50 | .Xr free 3 51 | when the calling program is done with it. 52 | .Pp 53 | Each line returned is automatically saved in the internal history list, 54 | unless it happens to be equal to the previous line. This is 55 | configurable if you are building editline from source, i.e. if you would 56 | rather like to call 57 | .Fn add_history 58 | manually. 59 | .Pp 60 | The 61 | .Fn read_history 62 | and 63 | .Fn write_history 64 | functions can be used to load and store the history of your application. 65 | .Em Note: 66 | these APIs do not do any tilde or environment variable expansion of the 67 | given filename. 68 | .Ss User Interface 69 | A program that uses this library provides a simple emacs-like editing 70 | interface to its users. A line may be edited before it is sent to the 71 | calling program by typing either control characters or escape sequences. 72 | A control character, shown as a caret followed by a letter, is typed by 73 | holding down the control key while the letter is typed. For example, 74 | .Cm ^A 75 | is a control-A. An escape sequence is entered by typing the escape key 76 | followed by one or more characters. The escape key is abbreviated as 77 | .Cm ESC . 78 | Note that unlike control keys, case matters in escape sequences; 79 | .Cm ESC F 80 | is not the same as 81 | .Cm ESC f . 82 | .Pp 83 | An editing command may be typed anywhere on the line, not just at the 84 | beginning. In addition, a return may also be typed anywhere on the 85 | line, not just at the end. 86 | .Pp 87 | Most editing commands may be given a repeat count, 88 | .Ar n , 89 | where 90 | .Ar n 91 | is a number. To enter a repeat count, type the escape key, the number, 92 | and then the command to execute. For example, 93 | .Cm ESC 4 ^f 94 | moves forward four characters. If a command may be given a repeat count 95 | then the text 96 | .Cm [n] 97 | is given at the end of its description. 98 | .Pp 99 | The following control characters are accepted: 100 | .Pp 101 | .Bl -tag -width "ESC DEL " -compact 102 | .It ^A 103 | Move to the beginning of the line 104 | .It ^B 105 | Move left (backwards) [n] 106 | .It ^D 107 | Delete character [n] 108 | .It ^E 109 | Move to end of line 110 | .It ^F 111 | Move right (forwards) [n] 112 | .It ^G 113 | Ring the bell 114 | .It ^H 115 | Delete character before cursor (backspace key) [n] 116 | .It ^I 117 | Complete filename (tab key); see below 118 | .It ^J 119 | Done with line (return key) 120 | .It ^K 121 | Kill to end of line (or column [n]) 122 | .It ^L 123 | Redisplay line 124 | .It ^M 125 | Done with line (alternate return key) 126 | .It ^N 127 | Get next line from history [n] 128 | .It ^P 129 | Get previous line from history [n] 130 | .It ^R 131 | Search backward (forward if [n]) through history for text; prefixing the 132 | string with a caret (^) forces it to match only at the beginning of a 133 | history line 134 | .It ^T 135 | Transpose characters 136 | .It ^V 137 | Insert next character, even if it is an edit command 138 | .It ^W 139 | Wipe to the mark 140 | .It ^X^X 141 | Exchange current location and mark 142 | .It ^Y 143 | Yank back last killed text 144 | .It ^[ 145 | Start an escape sequence (escape key) 146 | .It ^]c 147 | Move forward to next character 148 | .Cm c 149 | .It ^? 150 | Delete character before cursor (delete key) [n] 151 | .El 152 | .Pp 153 | The following escape sequences are provided: 154 | .Pp 155 | .Bl -tag -width "ESC DEL " -compact 156 | .It ESC ^H 157 | Delete previous word (backspace key) [n] 158 | .It ESC DEL 159 | Delete previous word (delete key) [n] 160 | .It ESC SP 161 | Set the mark (space key); see ^X^X and ^Y above 162 | .It ESC\ . 163 | Get the last (or [n]'th) word from previous line 164 | .It ESC\ ? 165 | Show possible completions; see below 166 | .It ESC < 167 | Move to start of history 168 | .It ESC > 169 | Move to end of history 170 | .It ESC b 171 | Move backward a word [n] 172 | .It ESC d 173 | Delete word under cursor [n] 174 | .It ESC f 175 | Move forward a word [n] 176 | .It ESC l 177 | Make word lowercase [n] 178 | .It ESC m 179 | Toggle if 8bit chars display normally or with an 180 | .Ar M- 181 | prefix 182 | .It ESC u 183 | Make word uppercase [n] 184 | .It ESC y 185 | Yank back last killed text 186 | .It ESC v 187 | Show library version 188 | .It ESC w 189 | Make area up to mark yankable 190 | .It ESC nn 191 | Set repeat count to the number nn 192 | .It ESC C 193 | Read from environment variable 194 | .Ar $C , 195 | where 196 | .Ar C 197 | is an uppercase letter 198 | .El 199 | .Pp 200 | The 201 | .Nm 202 | library has a small macro facility. If you type the escape key followed 203 | by an uppercase letter, 204 | .Ar C , 205 | then the contents of the environment variable 206 | .Ar $C 207 | are read in as if you had typed them at the keyboard. For example, if 208 | the variable 209 | .Ar $L 210 | contains the following: 211 | .Pp 212 | .Dl ^A^Kecho '^V^[[H^V^[[2J'^M 213 | .Pp 214 | Then typing 215 | .Cm ESC L 216 | will move to the beginning of the line, kill the entire line, enter the 217 | echo command needed to clear the terminal (if your terminal is like a 218 | VT-100), and send the line back to the shell. 219 | .Pp 220 | The 221 | .Nm 222 | library also does filename completion. Suppose the root directory has 223 | the following files in it: 224 | .Pp 225 | .Dl bin vmunix 226 | .Dl core vmunix.old 227 | .Pp 228 | If you type 229 | .Cm rm /v 230 | and then the tab key, 231 | .Nm 232 | will then finish off as much of the name as possible by adding 233 | .Ar munix . 234 | Because the name is not unique, it will then beep. If you type the 235 | escape key and a question mark, it will display the two choices. If you 236 | then type a period and a tab, the library will finish off the filename 237 | for you: 238 | .Pp 239 | .Bd -ragged -offset indent 240 | rm /v[TAB] 241 | .Em munix 242 | \&.[TAB] 243 | .Em old 244 | .Ed 245 | .Pp 246 | The tab key is shown by [TAB] and the automatically-entered text 247 | is shown in italics, or underline. 248 | .Sh USAGE 249 | To include 250 | .Nm 251 | in your program, call it as you do any other function and link your 252 | program with 253 | .Ar -leditline . 254 | .Ss Example 255 | The following brief example lets you enter a line and edit it, then displays it. 256 | .Pp 257 | .Bd -literal -offset indent 258 | #include 259 | #include 260 | #include 261 | 262 | int main(void) 263 | { 264 | char *p; 265 | 266 | while ((p = readline("CLI> "))) { 267 | puts(p); 268 | free(p); 269 | } 270 | 271 | return 0; 272 | } 273 | .El 274 | .Sh AUTHORS 275 | The original editline library was posted to comp.sources.unix newsgroup 276 | by created by Simmule R. Turner and Rich Salz in 1992. It now exists in 277 | several forks: Debian, Minix, Heimdal, Festival speech tools, Mozilla, 278 | Google Gadgets for Linux, and many others. The original manual page was 279 | made by David W. Sanderson. 280 | .Pp 281 | This version stems from the Minix 2 sources, but has since evolved to 282 | include patches from all relevant forks. It is currently maintained by 283 | .An Joachim Wiberg 284 | at 285 | .Lk https://github.com/troglobit/editline "GitHub" . 286 | .Sh BUGS 287 | Does not handle multiple lines or unicode characters well. 288 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | libeditline.a 2 | libeditline.la 3 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | lib_LTLIBRARIES = libeditline.la 2 | libeditline_la_SOURCES = editline.c editline.h complete.c sysunix.c unix.h 3 | libeditline_la_CFLAGS = -std=gnu99 4 | libeditline_la_CFLAGS += -W -Wall -Wextra -Wundef -Wunused -Wstrict-prototypes 5 | libeditline_la_CFLAGS += -Werror-implicit-function-declaration -Wshadow -Wcast-qual 6 | libeditline_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:2:0 7 | -------------------------------------------------------------------------------- /src/complete.c: -------------------------------------------------------------------------------- 1 | /* History and file completion functions for editline library. 2 | * 3 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz 4 | * All rights reserved. 5 | * 6 | * This software is not subject to any license of the American Telephone 7 | * and Telegraph Company or of the Regents of the University of California. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose on 10 | * any computer system, and to alter it and redistribute it freely, subject 11 | * to the following restrictions: 12 | * 1. The authors are not responsible for the consequences of use of this 13 | * software, no matter how awful, even if they arise from flaws in it. 14 | * 2. The origin of this software must not be misrepresented, either by 15 | * explicit claim or by omission. Since few users ever read sources, 16 | * credits must appear in the documentation. 17 | * 3. Altered versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. Since few users 19 | * ever read sources, credits must appear in the documentation. 20 | * 4. This notice may not be removed or altered. 21 | */ 22 | 23 | #include 24 | #include "editline.h" 25 | 26 | #define MAX_TOTAL_MATCHES (256 << sizeof(char *)) 27 | 28 | int rl_attempted_completion_over = 0; 29 | rl_completion_func_t *rl_attempted_completion_function = NULL; 30 | rl_compentry_func_t *rl_completion_entry_function = NULL; 31 | 32 | /* Wrap strcmp() for qsort() -- weird construct to pass -Wcast-qual */ 33 | static int compare(const void *p1, const void *p2) 34 | { 35 | char *const *v1 = (char *const *)p1; 36 | char *const *v2 = (char *const *)p2; 37 | 38 | return strcmp(*v1, *v2); 39 | } 40 | 41 | /* Fill in *avp with an array of names that match file, up to its length. 42 | * Ignore . and .. . */ 43 | static int FindMatches(const char *dir, const char *file, char ***avp) 44 | { 45 | char **av; 46 | char **word; 47 | char *p; 48 | DIR *dp; 49 | DIRENTRY *ep; 50 | size_t ac; 51 | size_t len; 52 | size_t choices; 53 | size_t total; 54 | 55 | if ((dp = opendir(dir)) == NULL) 56 | return 0; 57 | 58 | av = NULL; 59 | ac = 0; 60 | len = strlen(file); 61 | choices = 0; 62 | total = 0; 63 | while ((ep = readdir(dp)) != NULL) { 64 | p = ep->d_name; 65 | if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))) 66 | continue; 67 | if (len && strncmp(p, file, len) != 0) 68 | continue; 69 | 70 | choices++; 71 | if ((total += strlen(p)) > MAX_TOTAL_MATCHES) { 72 | /* This is a bit too much. */ 73 | while (ac > 0) free(av[--ac]); 74 | continue; 75 | } 76 | 77 | if ((ac % MEM_INC) == 0) { 78 | word = malloc(sizeof(char *) * (ac + MEM_INC)); 79 | if (!word) { 80 | total = 0; 81 | break; 82 | } 83 | 84 | if (ac) { 85 | memcpy(word, av, ac * sizeof(char *)); 86 | free(av); 87 | } 88 | *avp = av = word; 89 | } 90 | 91 | if ((av[ac] = strdup(p)) == NULL) { 92 | if (ac == 0) 93 | free(av); 94 | total = 0; 95 | break; 96 | } 97 | ac++; 98 | } 99 | 100 | /* Clean up and return. */ 101 | closedir(dp); 102 | if (total > MAX_TOTAL_MATCHES) { 103 | char many[sizeof(total) * 3]; 104 | 105 | p = many + sizeof(many); 106 | *--p = '\0'; 107 | while (choices > 0) { 108 | *--p = '0' + choices % 10; 109 | choices /= 10; 110 | } 111 | 112 | while (p > many + sizeof(many) - 8) 113 | *--p = ' '; 114 | 115 | if ((p = strdup(p)) != NULL) 116 | av[ac++] = p; 117 | 118 | if ((p = strdup("choices")) != NULL) 119 | av[ac++] = p; 120 | } else { 121 | if (ac) 122 | qsort(av, ac, sizeof(char *), compare); 123 | } 124 | 125 | return ac; 126 | } 127 | 128 | /* Split a pathname into allocated directory and trailing filename parts. */ 129 | static int SplitPath(const char *path, char **dirpart, char **filepart) 130 | { 131 | static const char DOT[] = "."; 132 | char *dpart; 133 | char *fpart; 134 | 135 | if ((fpart = strrchr(path, '/')) == NULL) { 136 | if ((dpart = strdup(DOT)) == NULL) 137 | return -1; 138 | 139 | if ((fpart = strdup(path)) == NULL) { 140 | free(dpart); 141 | return -1; 142 | } 143 | } else { 144 | if ((dpart = strdup(path)) == NULL) 145 | return -1; 146 | 147 | dpart[fpart - path + 1] = '\0'; 148 | if ((fpart = strdup(fpart + 1)) == NULL) { 149 | free(dpart); 150 | return -1; 151 | } 152 | } 153 | *dirpart = dpart; 154 | *filepart = fpart; 155 | 156 | return 0; 157 | } 158 | 159 | static rl_complete_func_t *el_complete_func = NULL; 160 | 161 | /* For compatibility with the Heimdal project. */ 162 | rl_complete_func_t *rl_set_complete_func(rl_complete_func_t *func) 163 | { 164 | rl_complete_func_t *old = el_complete_func; 165 | el_complete_func = func; 166 | 167 | return old; 168 | } 169 | 170 | /* Attempt to complete the pathname, returning an allocated copy. 171 | * Fill in *match if we completed it, or set it to 0 if ambiguous. */ 172 | char *el_filename_complete(char *pathname, int *match) 173 | { 174 | char **av; 175 | char *dir; 176 | char *file; 177 | char *path; 178 | char *p; 179 | size_t ac; 180 | size_t end; 181 | size_t i; 182 | size_t j; 183 | size_t len; 184 | 185 | if (SplitPath((const char *)pathname, &dir, &file) < 0) 186 | return NULL; 187 | 188 | if ((ac = FindMatches(dir, file, &av)) == 0) { 189 | free(dir); 190 | free(file); 191 | 192 | return NULL; 193 | } 194 | 195 | p = NULL; 196 | len = strlen(file); 197 | if (ac == 1) { 198 | /* Exactly one match -- finish it off. */ 199 | *match = 1; 200 | j = strlen(av[0]) - len + 1; 201 | p = malloc(sizeof(char) * (j + 1)); 202 | if (p) { 203 | memcpy(p, av[0] + len, j); 204 | len = strlen(dir) + strlen(av[0]) + 2; 205 | path = malloc(sizeof(char) * len); 206 | if (path) { 207 | snprintf(path, len, "%s/%s", dir, av[0]); 208 | rl_add_slash(path, p); 209 | free(path); 210 | } 211 | } 212 | } else { 213 | *match = 0; 214 | if (len) { 215 | /* Find largest matching substring. */ 216 | for (i = len, end = strlen(av[0]); i < end; i++) { 217 | for (j = 1; j < ac; j++) { 218 | if (av[0][i] != av[j][i]) 219 | goto breakout; 220 | } 221 | } 222 | breakout: 223 | if (i > len) { 224 | j = i - len + 1; 225 | p = malloc(sizeof(char) * j); 226 | if (p) { 227 | memcpy(p, av[0] + len, j); 228 | p[j - 1] = '\0'; 229 | } 230 | } 231 | } 232 | } 233 | 234 | /* Clean up and return. */ 235 | free(dir); 236 | free(file); 237 | for (i = 0; i < ac; i++) 238 | free(av[i]); 239 | free(av); 240 | 241 | return p; 242 | } 243 | 244 | char *rl_filename_completion_function(const char *text, int state) 245 | { 246 | static char **av, *dir, *file; 247 | static size_t i, ac; 248 | 249 | if (!state) { 250 | if (SplitPath(text, &dir, &file) < 0) 251 | return NULL; 252 | 253 | ac = FindMatches(dir, file, &av); 254 | if (!ac) { 255 | free(dir); 256 | free(file); 257 | return NULL; 258 | } 259 | 260 | i = 0; 261 | } 262 | 263 | if (i < ac) { 264 | size_t len = (dir ? strlen(dir) : 0) + strlen(av[i]) + 3; 265 | char *ptr = malloc(len); 266 | 267 | if (ptr) { 268 | snprintf(ptr, len, "%s%s", dir, av[i++]); 269 | if (ac == 1) 270 | rl_add_slash(ptr, ptr); 271 | 272 | return ptr; 273 | } 274 | } 275 | 276 | while (i > 0) 277 | free(av[--i]); 278 | 279 | if (av) { 280 | free(av); 281 | av = NULL; 282 | } 283 | if (dir) { 284 | free(dir); 285 | dir = NULL; 286 | } 287 | if (file) { 288 | free(file); 289 | file = NULL; 290 | } 291 | 292 | return NULL; 293 | } 294 | 295 | /* Similar to el_find_word(), but used by GNU Readline API */ 296 | static char *rl_find_token(size_t *len) 297 | { 298 | const char *ptr; 299 | int pos; 300 | 301 | for (pos = rl_point; pos < rl_end; pos++) { 302 | if (isspace((unsigned char) rl_line_buffer[pos])) { 303 | if (pos > 0) 304 | pos--; 305 | break; 306 | } 307 | } 308 | 309 | ptr = &rl_line_buffer[pos]; 310 | while (pos >= 0 && !isspace((unsigned char) rl_line_buffer[pos])) { 311 | if (pos == 0) 312 | break; 313 | 314 | pos--; 315 | } 316 | 317 | if (ptr != &rl_line_buffer[pos]) { 318 | *len = (size_t)(ptr - &rl_line_buffer[pos]); 319 | return &rl_line_buffer[pos]; 320 | } 321 | 322 | return NULL; 323 | } 324 | 325 | /* 326 | * "uses an application-supplied generator function to generate the list 327 | * of possible matches, and then returns the array of these matches. The 328 | * caller should place the address of its generator function in 329 | * rl_completion_entry_function" 330 | */ 331 | char **rl_completion_matches(const char *token, rl_compentry_func_t *generator) 332 | { 333 | int state = 0, num = 0; 334 | char **array, *entry; 335 | 336 | if (!generator) { 337 | generator = rl_completion_entry_function; 338 | if (!generator) 339 | generator = rl_filename_completion_function; 340 | } 341 | 342 | if (!generator) 343 | return NULL; 344 | 345 | array = malloc(512 * sizeof(char *)); 346 | if (!array) 347 | return NULL; 348 | 349 | while (num < 511 && (entry = generator(token, state))) { 350 | state = 1; 351 | array[num++] = entry; 352 | } 353 | array[num] = NULL; 354 | 355 | if (!num) { 356 | free(array); 357 | return NULL; 358 | } 359 | 360 | return array; 361 | } 362 | 363 | static char *complete(char *token, int *match) 364 | { 365 | size_t len = 0; 366 | char *word, **words = NULL; 367 | int start, end; 368 | 369 | word = rl_find_token(&len); 370 | if (!word) 371 | goto fallback; 372 | 373 | start = word - rl_line_buffer; 374 | end = start + len; 375 | 376 | word = strndup(word, len); 377 | if (!word) 378 | goto fallback; 379 | 380 | rl_attempted_completion_over = 0; 381 | words = rl_attempted_completion_function(word, start, end); 382 | 383 | if (!rl_attempted_completion_over && !words) 384 | words = rl_completion_matches(word, NULL); 385 | 386 | if (words) { 387 | int i = 0; 388 | 389 | free(word); 390 | word = NULL; 391 | 392 | /* Exactly one match -- finish it off. */ 393 | if (words[0] && !words[1]) { 394 | *match = 1; 395 | word = strdup(words[0] + len); 396 | } 397 | 398 | while (words[i]) 399 | free(words[i++]); 400 | free(words); 401 | 402 | if (word) 403 | return word; 404 | } 405 | 406 | if (word) 407 | free(word); 408 | 409 | fallback: 410 | return el_filename_complete(token, match); 411 | } 412 | 413 | /* 414 | * First check for editline specific custom completion function, then 415 | * for any GNU Readline compat, then fallback to filename completion. 416 | */ 417 | char *rl_complete(char *token, int *match) 418 | { 419 | if (el_complete_func) 420 | return el_complete_func(token, match); 421 | 422 | if (rl_attempted_completion_function) 423 | return complete(token, match); 424 | 425 | return el_filename_complete(token, match); 426 | } 427 | 428 | static rl_list_possib_func_t *el_list_possib_func = NULL; 429 | 430 | /* For compatibility with the Heimdal project. */ 431 | rl_list_possib_func_t *rl_set_list_possib_func(rl_list_possib_func_t *func) 432 | { 433 | rl_list_possib_func_t *old = el_list_possib_func; 434 | el_list_possib_func = func; 435 | return old; 436 | } 437 | 438 | /* Default possible completions. */ 439 | int el_filename_list_possib(char *pathname, char ***av) 440 | { 441 | char *dir; 442 | char *file; 443 | int ac; 444 | 445 | if (SplitPath(pathname, &dir, &file) < 0) 446 | return 0; 447 | 448 | ac = FindMatches(dir, file, av); 449 | free(dir); 450 | free(file); 451 | 452 | return ac; 453 | } 454 | 455 | /* Return all possible completions. */ 456 | int rl_list_possib(char *token, char ***av) 457 | { 458 | if (el_list_possib_func) 459 | return el_list_possib_func(token, av); 460 | 461 | return el_filename_list_possib(token, av); 462 | } 463 | 464 | 465 | /** 466 | * Local Variables: 467 | * c-file-style: "k&r" 468 | * c-basic-offset: 4 469 | * End: 470 | */ 471 | -------------------------------------------------------------------------------- /src/editline.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz 3 | * All rights reserved. 4 | * 5 | * This software is not subject to any license of the American Telephone 6 | * and Telegraph Company or of the Regents of the University of California. 7 | * 8 | * Permission is granted to anyone to use this software for any purpose on 9 | * any computer system, and to alter it and redistribute it freely, subject 10 | * to the following restrictions: 11 | * 1. The authors are not responsible for the consequences of use of this 12 | * software, no matter how awful, even if they arise from flaws in it. 13 | * 2. The origin of this software must not be misrepresented, either by 14 | * explicit claim or by omission. Since few users ever read sources, 15 | * credits must appear in the documentation. 16 | * 3. Altered versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. Since few users 18 | * ever read sources, credits must appear in the documentation. 19 | * 4. This notice may not be removed or altered. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "editline.h" 28 | 29 | /* 30 | ** Manifest constants. 31 | */ 32 | #define SCREEN_COLS 80 33 | #define SCREEN_ROWS 24 34 | #define EL_STDIN 0 35 | #define EL_STDOUT 1 36 | #define NO_ARG (-1) 37 | #define DEL 127 38 | #define SEPS "\"#$&'()*:;<=>?[\\]^`{|}~\n\t " 39 | 40 | /* 41 | ** The type of case-changing to perform. 42 | */ 43 | typedef enum { 44 | TOupper, TOlower, TOcapitalize 45 | } el_case_t; 46 | 47 | /* 48 | ** Key to command mapping. 49 | */ 50 | typedef struct { 51 | int Key; 52 | el_status_t (*Function)(void); 53 | } el_keymap_t; 54 | 55 | /* 56 | ** Command history structure. 57 | */ 58 | typedef struct { 59 | int Size; 60 | int Pos; 61 | char **Lines; 62 | } el_hist_t; 63 | 64 | /* User definable callbacks. */ 65 | rl_getc_func_t *rl_getc_function = rl_getc; 66 | rl_hook_func_t *rl_event_hook; 67 | rl_vintfunc_t *rl_prep_term_function = rl_prep_terminal; 68 | rl_voidfunc_t *rl_deprep_term_function = rl_deprep_terminal; 69 | 70 | /* 71 | ** Globals. 72 | */ 73 | int rl_eof; 74 | int rl_erase; 75 | int rl_intr; 76 | int rl_kill; 77 | int rl_quit; 78 | #ifdef CONFIG_SIGSTOP 79 | int rl_susp; 80 | #endif 81 | 82 | int el_hist_size = 15; 83 | static el_hist_t H = { 84 | .Size = 0, 85 | .Pos = 0, 86 | .Lines = NULL, 87 | }; 88 | 89 | static char NILSTR[] = ""; 90 | static const char *el_input = NILSTR; 91 | static char *Yanked; 92 | static char *Screen; 93 | static char NEWLINE[]= CRLF; 94 | static char CLEAR[]= "\ec"; 95 | static const char *el_term = "dumb"; 96 | static int Repeat; 97 | static int old_point; 98 | static int el_push_back; 99 | static int el_pushed; 100 | static int el_intr_pending; 101 | static int el_infd = EL_STDIN; 102 | static int el_outfd = EL_STDOUT; 103 | static el_keymap_t Map[]; 104 | static el_keymap_t MetaMap[]; 105 | static size_t Length = 0; 106 | static size_t ScreenCount; 107 | static size_t ScreenSize; 108 | static char *backspace = "\b"; 109 | static char *old_search = NULL; 110 | static int tty_cols = SCREEN_COLS; 111 | static int tty_rows = SCREEN_ROWS; 112 | static int Searching = 0; 113 | static const char *(*search_move)(void); 114 | static const char *old_prompt = NULL; 115 | static rl_vcpfunc_t *line_handler = NULL; 116 | static char *line_up = "\x1b[A"; 117 | static char *line_down = "\x1b[B"; 118 | static int prompt_len = 0; 119 | 120 | int el_no_echo = 0; /* e.g., under Emacs */ 121 | int el_no_hist = 0; 122 | int rl_point; 123 | int rl_mark; 124 | int rl_end; 125 | int rl_meta_chars = 0; /* Display 8-bit chars as the actual char(0) or as `M-x'(1)? */ 126 | int rl_inhibit_complete = 0; 127 | char *rl_line_buffer = NULL; 128 | static const char *rl_saved_prompt = NULL; 129 | const char *rl_prompt = NULL; 130 | const char *rl_readline_name = NULL; /* Set by calling program, for conditional parsing of ~/.inputrc - Not supported yet! */ 131 | FILE *rl_instream = NULL; /* The stdio stream from which input is read. Defaults to stdin if NULL */ 132 | FILE *rl_outstream = NULL; /* The stdio stream to which output is flushed. Defaults to stdout if NULL */ 133 | 134 | /* Declarations. */ 135 | static char *editinput(int complete); 136 | #ifdef CONFIG_USE_TERMCAP 137 | extern char *tgetstr(const char *, char **); 138 | extern int tgetent(char *, const char *); 139 | extern int tgetnum(const char *); 140 | #endif 141 | 142 | /* 143 | ** Misc. local helper functions. 144 | */ 145 | static int is_alpha_num(unsigned char c) 146 | { 147 | if (isalnum(c)) 148 | return 1; 149 | if (ISMETA(c)) 150 | return 1; 151 | if (ISCTL(c)) 152 | return 1; 153 | 154 | return 0; 155 | } 156 | 157 | /* 158 | ** TTY input/output functions. 159 | */ 160 | 161 | static void tty_flush(void) 162 | { 163 | ssize_t res; 164 | 165 | if (!ScreenCount) 166 | return; 167 | 168 | if (!el_no_echo) { 169 | res = write(el_outfd, Screen, ScreenCount); 170 | if (res > 0) 171 | ScreenCount = 0; 172 | } 173 | } 174 | 175 | static void tty_put(const char c) 176 | { 177 | if (el_no_echo) 178 | return; 179 | 180 | Screen[ScreenCount] = c; 181 | if (++ScreenCount >= ScreenSize) { 182 | char *ptr; 183 | 184 | ScreenSize += SCREEN_INC; 185 | ptr = realloc(Screen, sizeof(char) * ScreenSize); 186 | if (ptr) 187 | Screen = ptr; 188 | } 189 | } 190 | 191 | static void tty_puts(const char *p) 192 | { 193 | while (*p) 194 | tty_put(*p++); 195 | } 196 | 197 | static void tty_show(unsigned char c) 198 | { 199 | if (c == DEL) { 200 | tty_put('^'); 201 | tty_put('?'); 202 | } else if (ISCTL(c)) { 203 | tty_put('^'); 204 | tty_put(UNCTL(c)); 205 | } else if (rl_meta_chars && ISMETA(c)) { 206 | tty_put('M'); 207 | tty_put('-'); 208 | tty_put(UNMETA(c)); 209 | } else { 210 | tty_put(c); 211 | } 212 | } 213 | 214 | static void tty_string(const char *p) 215 | { 216 | int i = rl_point + prompt_len + 1; 217 | 218 | while (*p) { 219 | tty_show(*p++); 220 | if ((i++) % tty_cols == 0) { 221 | tty_put(' '); 222 | tty_put('\b'); 223 | } 224 | } 225 | } 226 | 227 | static void tty_push(int c) 228 | { 229 | el_pushed = 1; 230 | el_push_back = c; 231 | } 232 | 233 | int rl_getc(void) 234 | { 235 | int r; 236 | char c; 237 | 238 | do { 239 | r = read(el_infd, &c, 1); 240 | } while (r == -1 && errno == EINTR); 241 | 242 | return r == 1 ? c : EOF; 243 | } 244 | 245 | static int tty_get(void) 246 | { 247 | tty_flush(); 248 | 249 | if (el_pushed) { 250 | el_pushed = 0; 251 | return el_push_back; 252 | } 253 | 254 | if (*el_input) 255 | return *el_input++; 256 | 257 | return rl_getc_function(); 258 | } 259 | 260 | #define tty_back() tty_puts(backspace) 261 | 262 | static void tty_backn(int n) 263 | { 264 | while (--n >= 0) 265 | tty_back(); 266 | } 267 | 268 | static void tty_forwardn(int n) 269 | { 270 | char buf[12]; 271 | 272 | snprintf(buf, sizeof(buf), "\x1b[%dC", n); 273 | tty_puts(buf); 274 | } 275 | 276 | static void tty_info(void) 277 | { 278 | rl_reset_terminal(NULL); 279 | } 280 | 281 | /* 282 | ** Glue routines to rl_ttyset() 283 | */ 284 | void rl_prep_terminal(int meta_flag) 285 | { 286 | rl_meta_chars = !meta_flag; 287 | rl_ttyset(0); 288 | } 289 | 290 | void rl_deprep_terminal(void) 291 | { 292 | rl_ttyset(1); 293 | } 294 | 295 | /* 296 | ** Print an array of words in columns. 297 | */ 298 | void el_print_columns(int ac, char **av) 299 | { 300 | char *p; 301 | int i; 302 | int j; 303 | int k; 304 | int len; 305 | int skip; 306 | int longest; 307 | int cols; 308 | int colwidth; 309 | 310 | /* Find longest name, determine column count from that. */ 311 | for (longest = 0, i = 0; i < ac; i++) { 312 | if ((j = strlen((char *)av[i])) > longest) 313 | longest = j; 314 | } 315 | 316 | colwidth = longest + 3; 317 | if (colwidth > tty_cols) 318 | colwidth = tty_cols; 319 | cols = tty_cols / colwidth; 320 | 321 | tty_puts(NEWLINE); 322 | for (skip = ac / cols + 1, i = 0; i < skip; i++) { 323 | for (j = i; j < ac; j += skip) { 324 | for (p = av[j], len = strlen((char *)p), k = len; --k >= 0; p++) 325 | tty_put(*p); 326 | 327 | if (j + skip < ac) { 328 | while (++len < colwidth) 329 | tty_put(' '); 330 | } 331 | } 332 | 333 | tty_puts(NEWLINE); 334 | } 335 | } 336 | 337 | static void reposition(int key) 338 | { 339 | int len_with_prompt = prompt_len + rl_end; 340 | int n = len_with_prompt / tty_cols; /* determine the number of lines */ 341 | int i = 0; 342 | 343 | tty_put('\r'); 344 | 345 | if (n > 0) { 346 | int line; 347 | 348 | /* determine num of current line */ 349 | if (key == CTL('A') || key == CTL('E') || key == rl_kill) 350 | line = (prompt_len + old_point) / tty_cols; 351 | else 352 | line = len_with_prompt / tty_cols; 353 | 354 | /* move to end of line(s) */ 355 | if (key == CTL('E')) { 356 | int k; 357 | 358 | for (k = line; k < n; k++) 359 | tty_puts(line_down); 360 | 361 | /* determine reminder of last line and redraw only it */ 362 | i = rl_point - (len_with_prompt % tty_cols); 363 | } else { 364 | int k; 365 | 366 | /* CTRL-A, CTRL-U, insert (end, middle), remove (end, middle) */ 367 | for (k = line; k > 0; k--) 368 | tty_puts(line_up); /* redraw characters until changed data */ 369 | 370 | tty_puts(rl_prompt); 371 | } 372 | } else if (n == 0) { 373 | tty_puts(rl_prompt); 374 | } 375 | 376 | for (; i < rl_point; i++) { 377 | tty_show(rl_line_buffer[i]); 378 | 379 | /* move to the next line */ 380 | if ((i + prompt_len + 1) % tty_cols == 0) 381 | tty_put('\n'); 382 | } 383 | } 384 | 385 | static void left(el_status_t Change) 386 | { 387 | if (rl_point) { 388 | if ((rl_point + prompt_len) % tty_cols == 0) { 389 | tty_puts(line_up); 390 | tty_forwardn(tty_cols); 391 | } else { 392 | tty_back(); 393 | } 394 | 395 | if (ISMETA(rl_line_buffer[rl_point - 1])) { 396 | if (rl_meta_chars) { 397 | tty_back(); 398 | tty_back(); 399 | } 400 | } else if (ISCTL(rl_line_buffer[rl_point - 1])) { 401 | tty_back(); 402 | } 403 | } 404 | 405 | if (Change == CSmove) 406 | rl_point--; 407 | } 408 | 409 | static void right(el_status_t Change) 410 | { 411 | if ((rl_point + prompt_len + 1) % tty_cols == 0) 412 | tty_put('\n'); 413 | else 414 | tty_show(rl_line_buffer[rl_point]); 415 | 416 | if (Change == CSmove) 417 | rl_point++; 418 | } 419 | 420 | el_status_t el_ring_bell(void) 421 | { 422 | tty_put('\07'); 423 | tty_flush(); 424 | 425 | return CSstay; 426 | } 427 | 428 | static el_status_t do_macro(int c) 429 | { 430 | char name[4]; 431 | 432 | name[0] = '_'; 433 | name[1] = c; 434 | name[2] = '_'; 435 | name[3] = '\0'; 436 | 437 | if ((el_input = (char *)getenv((char *)name)) == NULL) { 438 | el_input = NILSTR; 439 | return el_ring_bell(); 440 | } 441 | 442 | return CSstay; 443 | } 444 | 445 | /* Skip forward to start of next word. If @move is set we also move the cursor. */ 446 | static el_status_t do_forward(el_status_t move) 447 | { 448 | int i; 449 | char *p; 450 | 451 | i = 0; 452 | do { 453 | p = &rl_line_buffer[rl_point]; 454 | 455 | /* Skip leading whitespace, like FSF Readline */ 456 | for ( ; rl_point < rl_end && (p[0] == ' ' || !is_alpha_num(p[0])); rl_point++, p++) { 457 | if (move == CSmove) 458 | right(CSstay); 459 | } 460 | 461 | /* Skip to end of word, if inside a word. */ 462 | for (; rl_point < rl_end && is_alpha_num(p[0]); rl_point++, p++) { 463 | if (move == CSmove) 464 | right(CSstay); 465 | } 466 | 467 | if (rl_point == rl_end) 468 | break; 469 | } while (++i < Repeat); 470 | 471 | return CSstay; 472 | } 473 | 474 | static el_status_t do_case(el_case_t type) 475 | { 476 | int i; 477 | int end; 478 | int count; 479 | char *p; 480 | 481 | do_forward(CSstay); 482 | if (old_point != rl_point) { 483 | if ((count = rl_point - old_point) < 0) 484 | count = -count; 485 | 486 | rl_point = old_point; 487 | if ((end = rl_point + count) > rl_end) 488 | end = rl_end; 489 | 490 | for (i = rl_point, p = &rl_line_buffer[i]; rl_point < end; p++) { 491 | if ((type == TOupper) || (type == TOcapitalize && rl_point == i)) { 492 | if (islower((unsigned char)(*p))) 493 | *p = toupper((unsigned char)(*p)); 494 | } else if (isupper((unsigned char)(*p))) { 495 | *p = tolower((unsigned char)(*p)); 496 | } 497 | right(CSmove); 498 | } 499 | } 500 | 501 | return CSstay; 502 | } 503 | 504 | static el_status_t case_down_word(void) 505 | { 506 | return do_case(TOlower); 507 | } 508 | 509 | static el_status_t case_up_word(void) 510 | { 511 | return do_case(TOupper); 512 | } 513 | 514 | static el_status_t case_cap_word(void) 515 | { 516 | return do_case(TOcapitalize); 517 | } 518 | 519 | static void ceol(void) 520 | { 521 | int extras = 0; 522 | int i; 523 | char *p; 524 | 525 | while (rl_point < 0) { 526 | tty_put(' '); 527 | rl_point++; 528 | } 529 | 530 | for (i = rl_point, p = &rl_line_buffer[i]; i <= rl_end; i++, p++) { 531 | if ((i + prompt_len + 1) % tty_cols == 0){ 532 | tty_put(' '); 533 | tty_put('\n'); 534 | } 535 | else 536 | tty_put(' '); 537 | if (ISMETA(*p)) { 538 | if (rl_meta_chars) { 539 | tty_put(' '); 540 | tty_put(' '); 541 | extras += 2; 542 | } 543 | } else if (ISCTL(*p)) { 544 | tty_put(' '); 545 | extras++; 546 | } 547 | } 548 | 549 | for (i += extras; i > rl_point; i--) { 550 | if ((i + prompt_len) % tty_cols == 0) { 551 | tty_puts(line_up); 552 | tty_forwardn(tty_cols); 553 | } else { 554 | tty_back(); 555 | } 556 | } 557 | } 558 | 559 | static void clear_line(void) 560 | { 561 | int n = (rl_point + prompt_len) / tty_cols; 562 | rl_point = -(int)strlen(rl_prompt); 563 | 564 | if (n > 0) { 565 | for(int k = 0; k < n; k++) 566 | tty_puts(line_up); 567 | tty_put('\r'); 568 | } 569 | else { 570 | tty_put('\r'); 571 | } 572 | 573 | ceol(); 574 | 575 | rl_point = 0; 576 | rl_end = 0; 577 | rl_line_buffer[0] = '\0'; 578 | } 579 | 580 | static el_status_t insert_string(const char *p) 581 | { 582 | size_t len; 583 | int i; 584 | char *line; 585 | char *q; 586 | 587 | len = strlen(p); 588 | if (rl_end + len >= Length) { 589 | line = malloc(sizeof(char) * (Length + len + MEM_INC)); 590 | if (!line) 591 | return CSstay; 592 | 593 | if (Length) { 594 | memcpy(line, rl_line_buffer, Length); 595 | free(rl_line_buffer); 596 | } 597 | 598 | rl_line_buffer = line; 599 | Length += len + MEM_INC; 600 | } 601 | 602 | for (q = &rl_line_buffer[rl_point], i = rl_end - rl_point; --i >= 0; ) 603 | q[len + i] = q[i]; 604 | 605 | memcpy(&rl_line_buffer[rl_point], p, len); 606 | rl_end += len; 607 | rl_line_buffer[rl_end] = '\0'; 608 | tty_string(&rl_line_buffer[rl_point]); 609 | rl_point += len; 610 | 611 | return rl_point == rl_end ? CSstay : CSmove; 612 | } 613 | 614 | int rl_insert_text(const char *text) 615 | { 616 | int mark = rl_point; 617 | 618 | insert_string(text); 619 | ceol(); 620 | 621 | return rl_point - mark; 622 | } 623 | 624 | static el_status_t redisplay(int cls) 625 | { 626 | if (cls) 627 | tty_puts(CLEAR); 628 | else 629 | tty_puts("\r\e[K"); 630 | 631 | tty_puts(rl_prompt); 632 | rl_point = 0; 633 | tty_string(rl_line_buffer); 634 | rl_point = rl_end; 635 | return CSmove; 636 | } 637 | 638 | static el_status_t refresh(void) 639 | { 640 | return redisplay(1); 641 | } 642 | 643 | int rl_refresh_line(int ignore1 __attribute__((unused)), int ignore2 __attribute__((unused))) 644 | { 645 | redisplay(0); 646 | return 0; 647 | } 648 | 649 | static el_status_t toggle_meta_mode(void) 650 | { 651 | rl_meta_chars = ! rl_meta_chars; 652 | return redisplay(0); 653 | } 654 | 655 | const char *el_next_hist(void) 656 | { 657 | return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos]; 658 | } 659 | 660 | const char *el_prev_hist(void) 661 | { 662 | return H.Pos == 0 ? NULL : H.Lines[--H.Pos]; 663 | } 664 | 665 | static el_status_t do_insert_hist(const char *p) 666 | { 667 | if (p == NULL) 668 | return el_ring_bell(); 669 | 670 | clear_line(); 671 | 672 | rl_point = 0; 673 | reposition(EOF); 674 | rl_end = 0; 675 | 676 | return insert_string(p); 677 | } 678 | 679 | static el_status_t do_hist(const char *(*move)(void)) 680 | { 681 | const char *p; 682 | int i = 0; 683 | 684 | do { 685 | if ((p = move()) == NULL) 686 | return el_ring_bell(); 687 | } while (++i < Repeat); 688 | 689 | return do_insert_hist(p); 690 | } 691 | 692 | static el_status_t h_next(void) 693 | { 694 | if (el_no_hist) 695 | return CSstay; 696 | 697 | return do_hist(el_next_hist); 698 | } 699 | 700 | static el_status_t h_prev(void) 701 | { 702 | if (el_no_hist) 703 | return CSstay; 704 | 705 | return do_hist(el_prev_hist); 706 | } 707 | 708 | static el_status_t h_first(void) 709 | { 710 | return do_insert_hist(H.Lines[H.Pos = 0]); 711 | } 712 | 713 | static el_status_t h_last(void) 714 | { 715 | return do_insert_hist(H.Lines[H.Pos = H.Size - 1]); 716 | } 717 | 718 | /* 719 | ** Return zero if pat appears as a substring in text. 720 | */ 721 | static int substrcmp(const char *text, const char *pat, size_t len) 722 | { 723 | char c; 724 | 725 | if ((c = *pat) == '\0') 726 | return *text == '\0'; 727 | 728 | for ( ; *text; text++) { 729 | if (*text == c && strncmp(text, pat, len) == 0) 730 | return 0; 731 | } 732 | 733 | return 1; 734 | } 735 | 736 | static const char *search_hist(const char *search, const char *(*move)(void)) 737 | { 738 | int len; 739 | int pos; 740 | int (*match)(const char *s1, const char *s2, size_t n); 741 | const char *pat; 742 | 743 | /* Save or get remembered search pattern. */ 744 | if (search && *search) { 745 | if (old_search) 746 | free(old_search); 747 | old_search = strdup(search); 748 | } else { 749 | if (old_search == NULL || *old_search == '\0') 750 | return NULL; 751 | search = old_search; 752 | } 753 | 754 | /* Set up pattern-finder. */ 755 | if (*search == '^') { 756 | match = strncmp; 757 | pat = search + 1; 758 | } else { 759 | match = substrcmp; 760 | pat = search; 761 | } 762 | len = strlen(pat); 763 | 764 | pos = H.Pos; /* Save H.Pos */ 765 | while (move()) { 766 | if (match(H.Lines[H.Pos], pat, len) == 0) 767 | return H.Lines[H.Pos]; 768 | } 769 | H.Pos = pos; /* Restore H.Pos */ 770 | 771 | return NULL; 772 | } 773 | 774 | static el_status_t h_search_end(const char *p) 775 | { 776 | rl_set_prompt(old_prompt); 777 | Searching = 0; 778 | 779 | if (el_intr_pending > 0) { 780 | el_intr_pending = 0; 781 | clear_line(); 782 | return redisplay(0); 783 | } 784 | 785 | p = search_hist(p, search_move); 786 | if (p == NULL) { 787 | el_ring_bell(); 788 | clear_line(); 789 | return redisplay(0); 790 | } 791 | 792 | return do_insert_hist(p); 793 | } 794 | 795 | static el_status_t h_search(void) 796 | { 797 | if (Searching) 798 | return el_ring_bell(); 799 | Searching = 1; 800 | 801 | clear_line(); 802 | old_prompt = rl_prompt; 803 | rl_set_prompt("Search: "); 804 | reposition(EOF); 805 | 806 | search_move = Repeat == NO_ARG ? el_prev_hist : el_next_hist; 807 | if (line_handler) { 808 | editinput(0); 809 | return CSstay; 810 | } 811 | 812 | return h_search_end(editinput(1)); 813 | } 814 | 815 | static el_status_t fd_char(void) 816 | { 817 | int i = 0; 818 | 819 | do { 820 | if (rl_point >= rl_end) 821 | break; 822 | right(CSmove); 823 | } while (++i < Repeat); 824 | return CSstay; 825 | } 826 | 827 | static void save_yank(int begin, int i) 828 | { 829 | if (Yanked) { 830 | free(Yanked); 831 | Yanked = NULL; 832 | } 833 | 834 | if (i < 1) 835 | return; 836 | 837 | Yanked = malloc(sizeof(char) * (i + 1)); 838 | if (Yanked) { 839 | memcpy(Yanked, &rl_line_buffer[begin], i); 840 | Yanked[i] = '\0'; 841 | } 842 | } 843 | 844 | static el_status_t delete_string(int count) 845 | { 846 | int i; 847 | char *p; 848 | 849 | if (count <= 0 || rl_end == rl_point) 850 | return el_ring_bell(); 851 | 852 | if (count == 1 && rl_point == rl_end - 1) { 853 | /* Optimize common case of delete at end of line. */ 854 | rl_end--; 855 | p = &rl_line_buffer[rl_point]; 856 | i = 1; 857 | tty_put(' '); 858 | if (ISCTL(*p)) { 859 | i = 2; 860 | tty_put(' '); 861 | } else if (rl_meta_chars && ISMETA(*p)) { 862 | i = 3; 863 | tty_put(' '); 864 | tty_put(' '); 865 | } 866 | tty_backn(i); 867 | *p = '\0'; 868 | return CSmove; 869 | } 870 | 871 | if (rl_point + count > rl_end && (count = rl_end - rl_point) <= 0) 872 | return CSstay; 873 | 874 | if (count > 1) 875 | save_yank(rl_point, count); 876 | 877 | for (p = &rl_line_buffer[rl_point], i = rl_end - (rl_point + count) + 1; --i >= 0; p++) 878 | p[0] = p[count]; 879 | ceol(); 880 | 881 | rl_end -= count; 882 | tty_string(&rl_line_buffer[rl_point]); 883 | 884 | return CSmove; 885 | } 886 | 887 | static el_status_t bk_char(void) 888 | { 889 | int i = 0; 890 | 891 | do { 892 | if (rl_point == 0) 893 | break; 894 | left(CSmove); 895 | } while (++i < Repeat); 896 | 897 | return CSstay; 898 | } 899 | 900 | static el_status_t bk_del_char(void) 901 | { 902 | int i = 0; 903 | 904 | do { 905 | if (rl_point == 0) 906 | break; 907 | left(CSmove); 908 | } while (++i < Repeat); 909 | 910 | return delete_string(i); 911 | } 912 | 913 | static el_status_t kill_line(void) 914 | { 915 | int i; 916 | 917 | if (Repeat != NO_ARG) { 918 | if (Repeat < rl_point) { 919 | i = rl_point; 920 | rl_point = Repeat; 921 | reposition(EOF); 922 | delete_string(i - rl_point); 923 | } else if (Repeat > rl_point) { 924 | right(CSmove); 925 | delete_string(Repeat - rl_point - 1); 926 | } 927 | 928 | return CSmove; 929 | } 930 | 931 | save_yank(rl_point, rl_end - rl_point); 932 | rl_line_buffer[rl_point] = '\0'; 933 | ceol(); 934 | rl_end = rl_point; 935 | 936 | return CSstay; 937 | } 938 | 939 | static el_status_t insert_char(int c) 940 | { 941 | el_status_t s; 942 | char buff[2]; 943 | char *p; 944 | char *q; 945 | int i; 946 | 947 | if (Repeat == NO_ARG || Repeat < 2) { 948 | buff[0] = c; 949 | buff[1] = '\0'; 950 | 951 | return insert_string(buff); 952 | } 953 | 954 | p = malloc(sizeof(char) * (Repeat + 1)); 955 | if (!p) 956 | return CSstay; 957 | 958 | for (i = Repeat, q = p; --i >= 0; ) 959 | *q++ = c; 960 | *q = '\0'; 961 | Repeat = 0; 962 | s = insert_string(p); 963 | free(p); 964 | 965 | return s; 966 | } 967 | 968 | static el_status_t beg_line(void) 969 | { 970 | if (rl_point) { 971 | rl_point = 0; 972 | return CSmove; 973 | } 974 | 975 | return CSstay; 976 | } 977 | 978 | static el_status_t end_line(void) 979 | { 980 | if (rl_point != rl_end) { 981 | rl_point = rl_end; 982 | return CSmove; 983 | } 984 | 985 | return CSstay; 986 | } 987 | 988 | static el_status_t del_char(void) 989 | { 990 | return delete_string(Repeat == NO_ARG ? CSeof : Repeat); 991 | } 992 | 993 | el_status_t el_del_char(void) 994 | { 995 | return del_char(); 996 | } 997 | 998 | static el_status_t fd_word(void) 999 | { 1000 | return do_forward(CSmove); 1001 | } 1002 | 1003 | static el_status_t bk_word(void) 1004 | { 1005 | int i; 1006 | char *p; 1007 | 1008 | i = 0; 1009 | do { 1010 | for (p = &rl_line_buffer[rl_point]; p > rl_line_buffer && !is_alpha_num(p[-1]); p--) 1011 | left(CSmove); 1012 | 1013 | for (; p > rl_line_buffer && !isblank(p[-1]) && is_alpha_num(p[-1]); p--) 1014 | left(CSmove); 1015 | 1016 | if (rl_point == 0) 1017 | break; 1018 | } while (++i < Repeat); 1019 | 1020 | return CSstay; 1021 | } 1022 | 1023 | static el_status_t meta(void) 1024 | { 1025 | int c; 1026 | el_keymap_t *kp; 1027 | 1028 | if ((c = tty_get()) == EOF) 1029 | return CSeof; 1030 | 1031 | #ifdef CONFIG_ANSI_ARROWS 1032 | /* See: https://en.wikipedia.org/wiki/ANSI_escape_code */ 1033 | /* Recognize ANSI escapes for `Meta+Left` and `Meta+Right`. */ 1034 | if (c == '\e') { 1035 | switch (tty_get()) { 1036 | case '[': 1037 | { 1038 | switch (tty_get()) { 1039 | /* \e\e[C = Meta+Left */ 1040 | case 'C': return fd_word(); 1041 | /* \e\e[D = Meta+Right */ 1042 | case 'D': return bk_word(); 1043 | default: 1044 | break; 1045 | } 1046 | 1047 | return el_ring_bell(); 1048 | } 1049 | default: 1050 | break; 1051 | } 1052 | 1053 | return el_ring_bell(); 1054 | } 1055 | 1056 | /* Also include VT-100 arrows. */ 1057 | if (c == '[' || c == 'O') { 1058 | switch (tty_get()) { 1059 | case EOF: return CSeof; 1060 | case '1': 1061 | { 1062 | char seq[4] = { 0 }; 1063 | seq[0] = tty_get(); 1064 | 1065 | /* \e[1~ */ 1066 | if (seq[0] == '~') 1067 | return beg_line(); /* Home */ 1068 | 1069 | for (c = 1; c < 3; c++) 1070 | seq[c] = tty_get(); 1071 | 1072 | if (!strncmp(seq, ";5C", 3) 1073 | || !strncmp(seq, ";3C", 3)) 1074 | return fd_word(); /* \e[1;5C = Ctrl+Right */ 1075 | if (!strncmp(seq, ";5D", 3) 1076 | || !strncmp(seq, ";3D", 3)) 1077 | return bk_word(); /* \e[1;5D = Ctrl+Left */ 1078 | 1079 | break; 1080 | } 1081 | case '2': tty_get(); return CSstay; /* Insert */ 1082 | case '3': tty_get(); return del_char(); /* Delete */ 1083 | case '4': tty_get(); return end_line(); /* End */ 1084 | case '5': tty_get(); return CSstay; /* PgUp */ 1085 | case '6': tty_get(); return CSstay; /* PgDn */ 1086 | case '7': tty_get(); return beg_line(); /* Home (urxvt) */ 1087 | case '8': tty_get(); return end_line(); /* End (urxvt) */ 1088 | case 'A': return h_prev(); /* Up */ 1089 | case 'B': return h_next(); /* Down */ 1090 | case 'C': return fd_char(); /* Left */ 1091 | case 'D': return bk_char(); /* Right */ 1092 | case 'F': return end_line(); /* End */ 1093 | case 'H': return beg_line(); /* Home */ 1094 | default: /* Fall through */ 1095 | break; 1096 | } 1097 | 1098 | return el_ring_bell(); 1099 | } 1100 | #endif /* CONFIG_ANSI_ARROWS */ 1101 | 1102 | if (isdigit(c)) { 1103 | for (Repeat = c - '0'; (c = tty_get()) != EOF && isdigit(c); ) 1104 | Repeat = Repeat * 10 + c - '0'; 1105 | tty_push(c); 1106 | 1107 | return CSstay; 1108 | } 1109 | 1110 | if (isupper(c)) 1111 | return do_macro(c); 1112 | 1113 | for (kp = MetaMap; kp->Function; kp++) { 1114 | if (kp->Key == c) 1115 | return kp->Function(); 1116 | } 1117 | 1118 | return el_ring_bell(); 1119 | } 1120 | 1121 | static el_status_t emacs(int c) 1122 | { 1123 | el_status_t s; 1124 | el_keymap_t *kp; 1125 | 1126 | /* Save point before interpreting input character 'c'. */ 1127 | old_point = rl_point; 1128 | 1129 | if (rl_meta_chars && ISMETA(c)) { 1130 | tty_push(UNMETA(c)); 1131 | return meta(); 1132 | } 1133 | 1134 | for (kp = Map; kp->Function; kp++) { 1135 | if (kp->Key == c) 1136 | break; 1137 | } 1138 | 1139 | if (kp->Function) { 1140 | s = kp->Function(); 1141 | if (s == CSdispatch) /* If Function is inhibited. */ 1142 | s = insert_char(c); 1143 | } else { 1144 | s = insert_char(c); 1145 | } 1146 | 1147 | if (!el_pushed) { 1148 | /* No pushback means no repeat count; hacky, but true. */ 1149 | Repeat = NO_ARG; 1150 | } 1151 | 1152 | return s; 1153 | } 1154 | 1155 | static el_status_t tty_special(int c) 1156 | { 1157 | el_status_t rc; 1158 | 1159 | #ifdef CONFIG_SIGINT 1160 | if (c == rl_intr) { 1161 | el_intr_pending = SIGINT; 1162 | return CSsignal; 1163 | } 1164 | #endif 1165 | if (c == rl_quit) { 1166 | el_intr_pending = SIGQUIT; 1167 | return CSeof; 1168 | } 1169 | #ifdef CONFIG_SIGSTOP 1170 | if (c == rl_susp) { 1171 | el_intr_pending = SIGTSTP; 1172 | return CSsignal; 1173 | } 1174 | #endif 1175 | 1176 | if (rl_meta_chars && ISMETA(c)) 1177 | return CSdispatch; 1178 | 1179 | if (c == rl_erase || c == DEL) 1180 | return bk_del_char(); 1181 | 1182 | if (c == rl_kill) { 1183 | Repeat = rl_point; 1184 | rc = bk_del_char(); 1185 | Repeat = NO_ARG; 1186 | return rc; 1187 | } 1188 | 1189 | #ifdef CONFIG_EOF 1190 | if (c == rl_eof && rl_point == 0 && rl_end == 0) 1191 | return CSeof; 1192 | #endif 1193 | 1194 | return CSdispatch; 1195 | } 1196 | 1197 | static char *editinput(int complete) 1198 | { 1199 | int c; 1200 | 1201 | do { 1202 | c = tty_get(); 1203 | if (c == EOF) 1204 | break; 1205 | 1206 | switch (tty_special(c)) { 1207 | case CSdone: 1208 | return rl_line_buffer; 1209 | 1210 | case CSeof: 1211 | return NULL; 1212 | 1213 | case CSsignal: 1214 | return (char *)""; 1215 | 1216 | case CSmove: 1217 | reposition(c); 1218 | break; 1219 | 1220 | case CSdispatch: 1221 | switch (emacs(c)) { 1222 | case CSdone: 1223 | return rl_line_buffer; 1224 | 1225 | case CSeof: 1226 | return NULL; 1227 | 1228 | case CSsignal: 1229 | return (char *)""; 1230 | 1231 | case CSmove: 1232 | reposition(c); 1233 | break; 1234 | 1235 | case CSdispatch: 1236 | case CSstay: 1237 | break; 1238 | } 1239 | break; 1240 | 1241 | case CSstay: 1242 | break; 1243 | } 1244 | } while (complete); 1245 | 1246 | return NULL; 1247 | } 1248 | 1249 | static void hist_alloc(void) 1250 | { 1251 | if (!H.Lines) 1252 | H.Lines = calloc(1 + el_hist_size, sizeof(char *)); 1253 | } 1254 | 1255 | static void hist_add(const char *p) 1256 | { 1257 | int i; 1258 | char *s; 1259 | 1260 | #ifdef CONFIG_UNIQUE_HISTORY 1261 | if (H.Size && strcmp(p, H.Lines[H.Size - 1]) == 0) 1262 | return; 1263 | #endif 1264 | 1265 | s = strdup(p); 1266 | if (s == NULL) 1267 | return; 1268 | 1269 | if (H.Size <= el_hist_size) { 1270 | H.Lines[H.Size++] = s; 1271 | } else { 1272 | free(H.Lines[0]); 1273 | for (i = 0; i < el_hist_size; i++) 1274 | H.Lines[i] = H.Lines[i + 1]; 1275 | H.Lines[i] = s; 1276 | } 1277 | H.Pos = H.Size - 1; 1278 | } 1279 | 1280 | static char *read_redirected(void) 1281 | { 1282 | int size = MEM_INC; 1283 | char *p; 1284 | char *line; 1285 | const char *end; 1286 | 1287 | p = line = malloc(sizeof(char) * size); 1288 | if (!p) 1289 | return NULL; 1290 | 1291 | end = p + size; 1292 | while (1) { 1293 | if (p == end) { 1294 | int oldpos = end - line; 1295 | 1296 | size += MEM_INC; 1297 | p = realloc(line, sizeof(char) * size); 1298 | if (!p) { 1299 | free(line); 1300 | return NULL; 1301 | } 1302 | line = p; 1303 | end = p + size; 1304 | 1305 | p += oldpos; /* Continue where we left off... */ 1306 | } 1307 | 1308 | if (read(el_infd, p, 1) <= 0) { 1309 | /* Ignore "incomplete" lines at EOF, just like we do for a tty. */ 1310 | free(line); 1311 | return NULL; 1312 | } 1313 | 1314 | if (*p == '\n') 1315 | break; 1316 | p++; 1317 | } 1318 | *p = '\0'; 1319 | 1320 | return line; 1321 | } 1322 | 1323 | /* For compatibility with FSF readline. */ 1324 | void rl_reset_terminal(const char *terminal_name) 1325 | { 1326 | #ifdef CONFIG_USE_TERMCAP 1327 | char buf[1024]; 1328 | char *bp; 1329 | #endif 1330 | #ifdef TIOCGWINSZ 1331 | struct winsize W; 1332 | #endif 1333 | 1334 | if (terminal_name) { 1335 | el_term = terminal_name; 1336 | } else if ((el_term = getenv("TERM")) == NULL) { 1337 | el_term = "dumb"; 1338 | } 1339 | 1340 | /* Initialize to faulty values to trigger fallback if nothing else works. */ 1341 | tty_cols = tty_rows = -1; 1342 | 1343 | #ifdef CONFIG_USE_TERMCAP 1344 | bp = buf; 1345 | if (-1 != tgetent(buf, el_term)) { 1346 | if ((backspace = tgetstr("le", &bp)) != NULL) 1347 | backspace = strdup(backspace); 1348 | tty_cols = tgetnum("co"); 1349 | tty_rows = tgetnum("li"); 1350 | } 1351 | /* Make sure to check width & rows and fallback to TIOCGWINSZ if available. */ 1352 | #endif 1353 | 1354 | if (tty_cols <= 0 || tty_rows <= 0) { 1355 | #ifdef TIOCGWINSZ 1356 | if (ioctl(el_outfd, TIOCGWINSZ, &W) >= 0 && W.ws_col > 0 && W.ws_row > 0) { 1357 | tty_cols = (int)W.ws_col; 1358 | tty_rows = (int)W.ws_row; 1359 | return; 1360 | } 1361 | #endif 1362 | tty_cols = SCREEN_COLS; 1363 | tty_rows = SCREEN_ROWS; 1364 | } 1365 | } 1366 | 1367 | void rl_set_prompt(const char *prompt) 1368 | { 1369 | if (prompt) 1370 | rl_prompt = prompt; 1371 | prompt_len = strlen(rl_prompt); 1372 | } 1373 | 1374 | void rl_save_prompt(void) 1375 | { 1376 | rl_saved_prompt = rl_prompt; 1377 | } 1378 | 1379 | void rl_restore_prompt(void) 1380 | { 1381 | if (rl_saved_prompt) 1382 | rl_set_prompt(rl_saved_prompt); 1383 | } 1384 | 1385 | void rl_initialize(void) 1386 | { 1387 | if (!rl_prompt) 1388 | rl_set_prompt("? "); 1389 | 1390 | hist_alloc(); 1391 | 1392 | /* Setup I/O descriptors */ 1393 | if (!rl_instream) el_infd = EL_STDIN; 1394 | else el_infd = fileno(rl_instream); 1395 | if (el_infd < 0) el_infd = EL_STDIN; 1396 | if (!rl_outstream) el_outfd = EL_STDOUT; 1397 | else el_outfd = fileno(rl_outstream); 1398 | if (el_outfd < 0) el_outfd = EL_STDOUT; 1399 | } 1400 | 1401 | void rl_uninitialize(void) 1402 | { 1403 | int i; 1404 | 1405 | /* Uninitialize the history */ 1406 | if (H.Lines) { 1407 | for (i = 0; i <= el_hist_size; i++) { 1408 | if (H.Lines[i]) 1409 | free(H.Lines[i]); 1410 | H.Lines[i] = NULL; 1411 | } 1412 | free(H.Lines); 1413 | H.Lines = NULL; 1414 | } 1415 | H.Size = 0; 1416 | H.Pos = 0; 1417 | 1418 | if (old_search) 1419 | free(old_search); 1420 | old_search = NULL; 1421 | 1422 | /* Uninitialize the line buffer */ 1423 | if (rl_line_buffer) 1424 | free(rl_line_buffer); 1425 | rl_line_buffer = NULL; 1426 | Length = 0; 1427 | } 1428 | 1429 | void rl_clear_message(void) 1430 | { 1431 | /* Nothing to do atm. */ 1432 | } 1433 | 1434 | void rl_forced_update_display(void) 1435 | { 1436 | redisplay(0); 1437 | tty_flush(); 1438 | } 1439 | 1440 | static int el_prep(const char *prompt) 1441 | { 1442 | rl_initialize(); 1443 | 1444 | if (!rl_line_buffer) { 1445 | Length = MEM_INC; 1446 | rl_line_buffer = malloc(sizeof(char) * Length); 1447 | if (!rl_line_buffer) 1448 | return -1; 1449 | } 1450 | 1451 | tty_info(); 1452 | rl_prep_term_function(!rl_meta_chars); 1453 | hist_add(NILSTR); 1454 | ScreenSize = SCREEN_INC; 1455 | Screen = malloc(sizeof(char) * ScreenSize); 1456 | if (!Screen) 1457 | return -1; 1458 | 1459 | rl_set_prompt(prompt); 1460 | 1461 | if (el_no_echo) { 1462 | int old = el_no_echo; 1463 | 1464 | el_no_echo = 0; 1465 | tty_puts(rl_prompt); 1466 | tty_flush(); 1467 | el_no_echo = old; 1468 | } else { 1469 | tty_puts(rl_prompt); 1470 | } 1471 | 1472 | Repeat = NO_ARG; 1473 | old_point = rl_point = rl_mark = rl_end = 0; 1474 | rl_line_buffer[0] = '\0'; 1475 | el_intr_pending = -1; 1476 | 1477 | return 0; 1478 | } 1479 | 1480 | static char *el_deprep(char *line) 1481 | { 1482 | if (line) { 1483 | line = strdup(line); 1484 | tty_puts(NEWLINE); 1485 | tty_flush(); 1486 | } 1487 | 1488 | rl_deprep_term_function(); 1489 | if (Screen) { 1490 | free(Screen); 1491 | Screen = NULL; 1492 | } 1493 | 1494 | free(H.Lines[--H.Size]); 1495 | H.Lines[H.Size] = NULL; 1496 | 1497 | /* Add to history, unless no-echo or no-history mode ... */ 1498 | if (!el_no_echo && !el_no_hist) { 1499 | if (line != NULL && *line != '\0') 1500 | hist_add(line); 1501 | } 1502 | 1503 | if (el_intr_pending > 0) { 1504 | int signo = el_intr_pending; 1505 | 1506 | el_intr_pending = 0; 1507 | kill(getpid(), signo); 1508 | } 1509 | 1510 | return line; 1511 | } 1512 | 1513 | void rl_callback_handler_install(const char *prompt, rl_vcpfunc_t *lhandler) 1514 | { 1515 | if (!lhandler) 1516 | return; 1517 | line_handler = lhandler; 1518 | 1519 | /* 1520 | * Any error from el_prep() is handled by the lhandler callbck as 1521 | * soon as the user calls rl_callback_read_char(). 1522 | */ 1523 | el_prep(prompt); 1524 | tty_flush(); 1525 | } 1526 | 1527 | /* 1528 | * Reads one character at a time, when a complete line has been received 1529 | * the lhandler from rl_callback_handler_install() is called with the 1530 | * line as argument. 1531 | * 1532 | * If the callback returns the terminal is prepped for reading a new 1533 | * line. 1534 | * 1535 | * If any error occurs, either in the _install() phase, or while reading 1536 | * one character, this function restores the terminal and calls lhandler 1537 | * with a NULL argument. 1538 | */ 1539 | void rl_callback_read_char(void) 1540 | { 1541 | char *line; 1542 | 1543 | if (!line_handler) { 1544 | errno = EINVAL; 1545 | return; 1546 | } 1547 | 1548 | /* 1549 | * Check if rl_callback_handler_install() failed 1550 | * This is the only point where we can tell user 1551 | */ 1552 | if (!Screen || !rl_line_buffer) { 1553 | errno = ENOMEM; 1554 | line_handler(el_deprep(NULL)); 1555 | return; 1556 | } 1557 | 1558 | line = editinput(0); 1559 | if (line) { 1560 | char *l; 1561 | 1562 | if (Searching) { 1563 | h_search_end(line); 1564 | tty_flush(); 1565 | return; 1566 | } 1567 | 1568 | l = el_deprep(line); 1569 | line_handler(l); 1570 | 1571 | if (el_prep(rl_prompt)) 1572 | line_handler(NULL); 1573 | } 1574 | tty_flush(); 1575 | } 1576 | 1577 | void rl_callback_handler_remove(void) 1578 | { 1579 | if (!line_handler) 1580 | return; 1581 | 1582 | el_deprep(NULL); 1583 | line_handler = NULL; 1584 | } 1585 | 1586 | char *readline(const char *prompt) 1587 | { 1588 | /* Unless called by the user already. */ 1589 | rl_initialize(); 1590 | 1591 | if (!isatty(el_infd)) { 1592 | tty_flush(); 1593 | 1594 | return read_redirected(); 1595 | } 1596 | 1597 | if (el_prep(prompt)) 1598 | return NULL; 1599 | 1600 | return el_deprep(editinput(1)); 1601 | } 1602 | 1603 | /* 1604 | * Even though readline() itself adds history automatically, the user 1605 | * can also add lines. This is for compatibility with GNU Readline. 1606 | */ 1607 | void add_history(const char *p) 1608 | { 1609 | if (p == NULL || *p == '\0') 1610 | return; 1611 | 1612 | hist_add(p); 1613 | } 1614 | 1615 | 1616 | int read_history(const char *filename) 1617 | { 1618 | FILE *fp; 1619 | char buf[SCREEN_INC]; 1620 | 1621 | hist_alloc(); 1622 | 1623 | fp = fopen(filename, "r"); 1624 | if (!fp) 1625 | return EOF; 1626 | 1627 | H.Size = 0; 1628 | while (H.Size < el_hist_size) { 1629 | if (!fgets(buf, SCREEN_INC, fp)) 1630 | break; 1631 | 1632 | buf[strlen(buf) - 1] = 0; /* Remove '\n' */ 1633 | add_history(buf); 1634 | } 1635 | 1636 | return fclose(fp); 1637 | } 1638 | 1639 | int write_history(const char *filename) 1640 | { 1641 | FILE *fp; 1642 | int i = 0; 1643 | 1644 | hist_alloc(); 1645 | 1646 | fp = fopen(filename, "w"); 1647 | if (!fp) 1648 | return EOF; 1649 | 1650 | while (i < H.Size) 1651 | fprintf(fp, "%s\n", H.Lines[i++]); 1652 | 1653 | return fclose(fp); 1654 | } 1655 | 1656 | /* 1657 | ** Move back to the beginning of the current word and return an 1658 | ** allocated copy of it. 1659 | */ 1660 | char *el_find_word(void) 1661 | { 1662 | char *p, *q; 1663 | char *word; 1664 | size_t len; 1665 | 1666 | p = &rl_line_buffer[rl_point]; 1667 | while (p > rl_line_buffer) { 1668 | p--; 1669 | if (p > rl_line_buffer && p[-1] == '\\') { 1670 | p--; 1671 | } else { 1672 | if (strchr(SEPS, (char) *p) != NULL) { 1673 | p++; 1674 | break; 1675 | } 1676 | } 1677 | } 1678 | 1679 | len = rl_point - (p - rl_line_buffer) + 1; 1680 | word = malloc(sizeof(char) * len); 1681 | if (!word) 1682 | return NULL; 1683 | 1684 | q = word; 1685 | while (p < &rl_line_buffer[rl_point]) { 1686 | if (*p == '\\') { 1687 | if (++p == &rl_line_buffer[rl_point]) 1688 | break; 1689 | } 1690 | *q++ = *p++; 1691 | } 1692 | *q = '\0'; 1693 | 1694 | return word; 1695 | } 1696 | 1697 | static el_status_t c_possible(void) 1698 | { 1699 | char **av; 1700 | char *word; 1701 | int ac; 1702 | 1703 | word = el_find_word(); 1704 | ac = rl_list_possib(word, &av); 1705 | if (word) 1706 | free(word); 1707 | if (ac) { 1708 | el_print_columns(ac, av); 1709 | while (--ac >= 0) 1710 | free(av[ac]); 1711 | free(av); 1712 | 1713 | return CSmove; 1714 | } 1715 | 1716 | return el_ring_bell(); 1717 | } 1718 | 1719 | static el_status_t c_complete(void) 1720 | { 1721 | char *p, *q; 1722 | char *word, *string; 1723 | size_t len; 1724 | int unique; 1725 | el_status_t s = CSdone; 1726 | 1727 | if (rl_inhibit_complete) 1728 | return CSdispatch; 1729 | 1730 | word = el_find_word(); 1731 | p = rl_complete(word, &unique); 1732 | if (word) 1733 | free(word); 1734 | if (p) { 1735 | len = strlen(p); 1736 | word = p; 1737 | 1738 | string = q = malloc(sizeof(char) * (2 * len + 1)); 1739 | if (!string) { 1740 | free(word); 1741 | return CSstay; 1742 | } 1743 | 1744 | while (*p) { 1745 | if ((*p < ' ' || strchr(SEPS, *p) != NULL) 1746 | && (!unique || p[1] != 0)) { 1747 | *q++ = '\\'; 1748 | } 1749 | *q++ = *p++; 1750 | } 1751 | *q = '\0'; 1752 | free(word); 1753 | 1754 | if (len > 0) { 1755 | s = insert_string(string); 1756 | #ifdef CONFIG_TERMINAL_BELL 1757 | if (!unique) 1758 | el_ring_bell(); 1759 | #endif 1760 | } 1761 | free(string); 1762 | 1763 | if (len > 0) 1764 | return s; 1765 | } 1766 | 1767 | return c_possible(); 1768 | } 1769 | 1770 | static el_status_t accept_line(void) 1771 | { 1772 | rl_line_buffer[rl_end] = '\0'; 1773 | return CSdone; 1774 | } 1775 | 1776 | static el_status_t transpose(void) 1777 | { 1778 | char c; 1779 | 1780 | if (rl_point) { 1781 | if (rl_point == rl_end) 1782 | left(CSmove); 1783 | c = rl_line_buffer[rl_point - 1]; 1784 | left(CSstay); 1785 | rl_line_buffer[rl_point - 1] = rl_line_buffer[rl_point]; 1786 | tty_show(rl_line_buffer[rl_point - 1]); 1787 | rl_line_buffer[rl_point++] = c; 1788 | tty_show(c); 1789 | } 1790 | 1791 | return CSstay; 1792 | } 1793 | 1794 | static el_status_t quote(void) 1795 | { 1796 | int c; 1797 | 1798 | return (c = tty_get()) == EOF ? CSeof : insert_char((int)c); 1799 | } 1800 | 1801 | static el_status_t mk_set(void) 1802 | { 1803 | rl_mark = rl_point; 1804 | return CSstay; 1805 | } 1806 | 1807 | static el_status_t exchange(void) 1808 | { 1809 | int c; 1810 | 1811 | if ((c = tty_get()) != CTL('X')) 1812 | return c == EOF ? CSeof : el_ring_bell(); 1813 | 1814 | if ((c = rl_mark) <= rl_end) { 1815 | rl_mark = rl_point; 1816 | rl_point = c; 1817 | return CSmove; 1818 | } 1819 | 1820 | return CSstay; 1821 | } 1822 | 1823 | static el_status_t yank(void) 1824 | { 1825 | if (Yanked && *Yanked) 1826 | return insert_string(Yanked); 1827 | 1828 | return CSstay; 1829 | } 1830 | 1831 | static el_status_t copy_region(void) 1832 | { 1833 | if (rl_mark > rl_end) 1834 | return el_ring_bell(); 1835 | 1836 | if (rl_point > rl_mark) 1837 | save_yank(rl_mark, rl_point - rl_mark); 1838 | else 1839 | save_yank(rl_point, rl_mark - rl_point); 1840 | 1841 | return CSstay; 1842 | } 1843 | 1844 | static el_status_t move_to_char(void) 1845 | { 1846 | int i, c; 1847 | char *p; 1848 | 1849 | if ((c = tty_get()) == EOF) 1850 | return CSeof; 1851 | 1852 | for (i = rl_point + 1, p = &rl_line_buffer[i]; i < rl_end; i++, p++) { 1853 | if (*p == c) { 1854 | rl_point = i; 1855 | return CSmove; 1856 | } 1857 | } 1858 | 1859 | return CSstay; 1860 | } 1861 | 1862 | static el_status_t fd_kill_word(void) 1863 | { 1864 | int i; 1865 | 1866 | do_forward(CSstay); 1867 | if (old_point != rl_point) { 1868 | i = rl_point - old_point - 1; 1869 | rl_point = old_point; 1870 | return delete_string(i); 1871 | } 1872 | 1873 | return CSstay; 1874 | } 1875 | 1876 | static el_status_t bk_kill_word(void) 1877 | { 1878 | bk_word(); 1879 | if (old_point != rl_point) 1880 | return delete_string(old_point - rl_point); 1881 | 1882 | return CSstay; 1883 | } 1884 | 1885 | static int argify(char *line, char ***avp) 1886 | { 1887 | char *c; 1888 | char **p; 1889 | char **arg; 1890 | int ac; 1891 | int i; 1892 | 1893 | i = MEM_INC; 1894 | *avp = p = malloc(sizeof(char *) * i); 1895 | if (!p) 1896 | return 0; 1897 | 1898 | for (c = line; isspace((unsigned char)(*c)); c++) 1899 | continue; 1900 | 1901 | if (*c == '\n' || *c == '\0') 1902 | return 0; 1903 | 1904 | for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) { 1905 | if (!isspace((unsigned char)(*c))) { 1906 | c++; 1907 | continue; 1908 | } 1909 | 1910 | *c++ = '\0'; 1911 | if (*c && *c != '\n') { 1912 | if (ac + 1 == i) { 1913 | arg = malloc(sizeof(char *) * (i + MEM_INC)); 1914 | if (!arg) { 1915 | p[ac] = NULL; 1916 | return ac; 1917 | } 1918 | 1919 | memcpy(arg, p, i * sizeof(char *)); 1920 | i += MEM_INC; 1921 | free(p); 1922 | *avp = p = arg; 1923 | } 1924 | p[ac++] = c; 1925 | } 1926 | } 1927 | 1928 | *c = '\0'; 1929 | p[ac] = NULL; 1930 | 1931 | return ac; 1932 | } 1933 | 1934 | static el_status_t last_argument(void) 1935 | { 1936 | char **av = NULL; 1937 | char *p; 1938 | el_status_t s; 1939 | int ac; 1940 | 1941 | if (H.Size == 1 || (p = (char *)H.Lines[H.Size - 2]) == NULL) 1942 | return el_ring_bell(); 1943 | 1944 | p = strdup(p); 1945 | if (!p) 1946 | return CSstay; 1947 | 1948 | ac = argify(p, &av); 1949 | if (Repeat != NO_ARG) 1950 | s = Repeat < ac ? insert_string(av[Repeat]) : el_ring_bell(); 1951 | else 1952 | s = ac ? insert_string(av[ac - 1]) : CSstay; 1953 | 1954 | if (av) 1955 | free(av); 1956 | free(p); 1957 | 1958 | return s; 1959 | } 1960 | 1961 | static el_keymap_t Map[64] = { 1962 | { CTL('@'), mk_set }, 1963 | { CTL('A'), beg_line }, 1964 | { CTL('B'), bk_char }, 1965 | { CTL('D'), del_char }, 1966 | { CTL('E'), end_line }, 1967 | { CTL('F'), fd_char }, 1968 | { CTL('G'), el_ring_bell }, 1969 | { CTL('H'), bk_del_char }, 1970 | { CTL('I'), c_complete }, 1971 | { CTL('J'), accept_line }, 1972 | { CTL('K'), kill_line }, 1973 | { CTL('L'), refresh }, 1974 | { CTL('M'), accept_line }, 1975 | { CTL('N'), h_next }, 1976 | { CTL('O'), el_ring_bell }, 1977 | { CTL('P'), h_prev }, 1978 | { CTL('Q'), el_ring_bell }, 1979 | { CTL('R'), h_search }, 1980 | { CTL('S'), el_ring_bell }, 1981 | { CTL('T'), transpose }, 1982 | { CTL('U'), el_ring_bell }, 1983 | { CTL('V'), quote }, 1984 | { CTL('W'), bk_kill_word }, 1985 | { CTL('X'), exchange }, 1986 | { CTL('Y'), yank }, 1987 | { CTL('Z'), el_ring_bell }, 1988 | { CTL('['), meta }, 1989 | { CTL(']'), move_to_char }, 1990 | { CTL('^'), el_ring_bell }, 1991 | { CTL('_'), el_ring_bell }, 1992 | { 0, NULL } 1993 | }; 1994 | 1995 | static el_keymap_t MetaMap[64]= { 1996 | { CTL('H'), bk_kill_word }, 1997 | { DEL, bk_kill_word }, 1998 | { ' ', mk_set }, 1999 | { '.', last_argument }, 2000 | { '<', h_first }, 2001 | { '>', h_last }, 2002 | { '?', c_possible }, 2003 | { 'b', bk_word }, 2004 | { 'c', case_cap_word }, 2005 | { 'd', fd_kill_word }, 2006 | { 'f', fd_word }, 2007 | { 'l', case_down_word }, 2008 | { 'm', toggle_meta_mode }, 2009 | { 'u', case_up_word }, 2010 | { 'y', yank }, 2011 | { 'w', copy_region }, 2012 | { 0, NULL } 2013 | }; 2014 | 2015 | static size_t find_key_in_map(int key, el_keymap_t map[], size_t mapsz) 2016 | { 2017 | size_t i; 2018 | 2019 | for (i = 0; i < mapsz && map[i].Function; i++) { 2020 | if (map[i].Key == key) 2021 | return i; 2022 | } 2023 | 2024 | if (i < mapsz) 2025 | return i; 2026 | 2027 | return mapsz; 2028 | } 2029 | 2030 | static el_status_t el_bind_key_in_map(int key, el_keymap_func_t function, el_keymap_t map[], size_t mapsz) 2031 | { 2032 | size_t creat, pos = find_key_in_map(key, map, mapsz); 2033 | 2034 | /* Must check that pos is not the next to last array position, 2035 | * otherwise we will write out-of-bounds to terminate the list. */ 2036 | if (pos + 1 >= mapsz) { 2037 | errno = ENOMEM; 2038 | return CSeof; 2039 | } 2040 | 2041 | /* Add at end, create new? */ 2042 | creat = map[pos].Function == NULL; 2043 | 2044 | /* A new key so have to add it to end */ 2045 | map[pos].Key = key; 2046 | map[pos].Function = function; 2047 | 2048 | /* Terminate list */ 2049 | if (creat) { 2050 | map[pos + 1].Key = 0; 2051 | map[pos + 1].Function = NULL; 2052 | } 2053 | 2054 | return CSdone; 2055 | } 2056 | 2057 | el_status_t el_bind_key(int key, el_keymap_func_t function) 2058 | { 2059 | return el_bind_key_in_map(key, function, Map, NELEMS(Map)); 2060 | } 2061 | 2062 | el_status_t el_bind_key_in_metamap(int key, el_keymap_func_t function) 2063 | { 2064 | return el_bind_key_in_map(key, function, MetaMap, NELEMS(MetaMap)); 2065 | } 2066 | 2067 | rl_getc_func_t *rl_set_getc_func(rl_getc_func_t *func) 2068 | { 2069 | rl_getc_func_t *old = rl_getc_function; 2070 | rl_getc_function = func; 2071 | return old; 2072 | } 2073 | 2074 | /** 2075 | * Local Variables: 2076 | * c-file-style: "k&r" 2077 | * c-basic-offset: 4 2078 | * End: 2079 | */ 2080 | -------------------------------------------------------------------------------- /src/editline.h: -------------------------------------------------------------------------------- 1 | /* Internal header file for editline library. 2 | * 3 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz 4 | * All rights reserved. 5 | * 6 | * This software is not subject to any license of the American Telephone 7 | * and Telegraph Company or of the Regents of the University of California. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose on 10 | * any computer system, and to alter it and redistribute it freely, subject 11 | * to the following restrictions: 12 | * 1. The authors are not responsible for the consequences of use of this 13 | * software, no matter how awful, even if they arise from flaws in it. 14 | * 2. The origin of this software must not be misrepresented, either by 15 | * explicit claim or by omission. Since few users ever read sources, 16 | * credits must appear in the documentation. 17 | * 3. Altered versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. Since few users 19 | * ever read sources, credits must appear in the documentation. 20 | * 4. This notice may not be removed or altered. 21 | */ 22 | 23 | #ifndef EDITLINE_PRIVATE_H_ 24 | #define EDITLINE_PRIVATE_H_ 25 | 26 | #include 27 | #include 28 | #ifdef HAVE_MALLOC_H 29 | #include 30 | #endif 31 | #ifdef HAVE_STDLIB_H 32 | #include 33 | #endif 34 | #ifdef HAVE_STRING_H 35 | #include 36 | #endif 37 | #ifdef HAVE_DIRENT_H 38 | #include 39 | #endif 40 | #ifdef HAVE_SIGNAL_H 41 | #include 42 | #endif 43 | #ifdef SYS_UNIX 44 | #include "unix.h" 45 | #endif 46 | #ifdef SYS_OS9 47 | #include "os9.h" 48 | #endif 49 | /* The following two are for TIOCGWINSZ */ 50 | #ifdef HAVE_TERMIOS_H 51 | # include 52 | #endif 53 | #ifdef GWINSZ_IN_SYS_IOCTL 54 | # include 55 | #endif 56 | 57 | #define MEM_INC 64 58 | #define SCREEN_INC 256 59 | 60 | /* From The Practice of Programming, by Kernighan and Pike */ 61 | #ifndef NELEMS 62 | #define NELEMS(array) (sizeof(array) / sizeof(array[0])) 63 | #endif 64 | 65 | /* 66 | ** Variables and routines internal to this package. 67 | */ 68 | extern int rl_eof; 69 | extern int rl_erase; 70 | extern int rl_intr; 71 | extern int rl_kill; 72 | extern int rl_quit; 73 | #ifdef CONFIG_SIGSTOP 74 | extern int rl_susp; 75 | #endif 76 | void rl_ttyset(int Reset); 77 | void rl_add_slash(char *path, char *p); 78 | char *rl_complete(char *token, int *match); 79 | int rl_list_possib(char *token, char ***av); 80 | 81 | #ifndef HAVE_STDLIB_H 82 | extern char *getenv(const char *name); 83 | extern char *malloc(size_t size); 84 | extern char *realloc(void *ptr, size_t size); 85 | extern char *memcpy(void *dest, const void *src, size_t n); 86 | extern char *strcat(char *dest, const char *src); 87 | extern char *strchr(const char *s, int c); 88 | extern char *strrchr(const char *s, int c); 89 | extern char *strcpy(char *dest, const char *src); 90 | extern char *strdup(const char *s); 91 | extern int strcmp(const char *s1, const char *s2); 92 | extern int strlen(const char *s); 93 | extern int strncmp(const char *s1, const char *s2, size_t n); 94 | #endif/* !HAVE_STDLIB_H */ 95 | 96 | #ifndef HAVE_STRDUP 97 | extern char *strdup(const char *s); 98 | #endif 99 | 100 | #include "../include/editline.h" 101 | #endif /* EDITLINE_PRIVATE_H_ */ 102 | -------------------------------------------------------------------------------- /src/os9.h: -------------------------------------------------------------------------------- 1 | /* Editline system header file for OS-9 (on 68k). 2 | * 3 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz 4 | * All rights reserved. 5 | * 6 | * This software is not subject to any license of the American Telephone 7 | * and Telegraph Company or of the Regents of the University of California. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose on 10 | * any computer system, and to alter it and redistribute it freely, subject 11 | * to the following restrictions: 12 | * 1. The authors are not responsible for the consequences of use of this 13 | * software, no matter how awful, even if they arise from flaws in it. 14 | * 2. The origin of this software must not be misrepresented, either by 15 | * explicit claim or by omission. Since few users ever read sources, 16 | * credits must appear in the documentation. 17 | * 3. Altered versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. Since few users 19 | * ever read sources, credits must appear in the documentation. 20 | * 4. This notice may not be removed or altered. 21 | */ 22 | 23 | #define CRLF "\r\l" 24 | 25 | #include 26 | typedef struct direct DIRENTRY; 27 | -------------------------------------------------------------------------------- /src/sysos9.c: -------------------------------------------------------------------------------- 1 | /* OS-9 (on 68k) system-dependant routines for editline library. 2 | * 3 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz 4 | * All rights reserved. 5 | * 6 | * This software is not subject to any license of the American Telephone 7 | * and Telegraph Company or of the Regents of the University of California. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose on 10 | * any computer system, and to alter it and redistribute it freely, subject 11 | * to the following restrictions: 12 | * 1. The authors are not responsible for the consequences of use of this 13 | * software, no matter how awful, even if they arise from flaws in it. 14 | * 2. The origin of this software must not be misrepresented, either by 15 | * explicit claim or by omission. Since few users ever read sources, 16 | * credits must appear in the documentation. 17 | * 3. Altered versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. Since few users 19 | * ever read sources, credits must appear in the documentation. 20 | * 4. This notice may not be removed or altered. 21 | */ 22 | #include "editline.h" 23 | #include 24 | #include 25 | 26 | void rl_ttyset(int Reset) 27 | { 28 | static struct sgbuf old; 29 | struct sgbuf new; 30 | 31 | if (Reset == 0) { 32 | _gs_opt(0, &old); 33 | _gs_opt(0, &new); 34 | new.sg_backsp = 0; new.sg_delete = 0; new.sg_echo = 0; 35 | new.sg_alf = 0; new.sg_nulls = 0; new.sg_pause = 0; 36 | new.sg_page = 0; new.sg_bspch = 0; new.sg_dlnch = 0; 37 | new.sg_eorch = 0; new.sg_eofch = 0; new.sg_rlnch = 0; 38 | new.sg_dulnch = 0; new.sg_psch = 0; new.sg_kbich = 0; 39 | new.sg_kbach = 0; new.sg_bsech = 0; new.sg_bellch = 0; 40 | new.sg_xon = 0; new.sg_xoff = 0; new.sg_tabcr = 0; 41 | new.sg_tabsiz = 0; 42 | _ss_opt(0, &new); 43 | rl_erase = old.sg_bspch; 44 | rl_kill = old.sg_dlnch; 45 | rl_eof = old.sg_eofch; 46 | rl_intr = old.sg_kbich; 47 | rl_quit = -1; 48 | } 49 | else { 50 | _ss_opt(0, &old); 51 | } 52 | } 53 | 54 | void rl_add_slash(char *path, char *p) 55 | { 56 | strcat(p, access(path, S_IREAD | S_IFDIR) ? " " : "/"); 57 | } 58 | 59 | /** 60 | * Local Variables: 61 | * c-file-style: "k&r" 62 | * c-basic-offset: 4 63 | * End: 64 | */ 65 | -------------------------------------------------------------------------------- /src/sysunix.c: -------------------------------------------------------------------------------- 1 | /* Unix system-dependant routines for editline library. 2 | * 3 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz 4 | * All rights reserved. 5 | * 6 | * This software is not subject to any license of the American Telephone 7 | * and Telegraph Company or of the Regents of the University of California. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose on 10 | * any computer system, and to alter it and redistribute it freely, subject 11 | * to the following restrictions: 12 | * 1. The authors are not responsible for the consequences of use of this 13 | * software, no matter how awful, even if they arise from flaws in it. 14 | * 2. The origin of this software must not be misrepresented, either by 15 | * explicit claim or by omission. Since few users ever read sources, 16 | * credits must appear in the documentation. 17 | * 3. Altered versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. Since few users 19 | * ever read sources, credits must appear in the documentation. 20 | * 4. This notice may not be removed or altered. 21 | */ 22 | 23 | #include 24 | #include "editline.h" 25 | 26 | #ifndef HAVE_TCGETATTR 27 | /* Wrapper for ioctl syscalls to restart on signal */ 28 | static int ioctl_wrap(int fd, int req, void *arg) 29 | { 30 | int result, retries = 3; 31 | 32 | while (-1 == (result = ioctl(fd, req, arg)) && retries > 0) { 33 | retries--; 34 | 35 | if (EINTR == errno) 36 | continue; 37 | 38 | break; 39 | } 40 | 41 | return result; 42 | } 43 | #endif 44 | 45 | /* Prefer termios over the others since it is likely the most portable. */ 46 | #if defined(HAVE_TCGETATTR) 47 | #include 48 | 49 | /* Wrapper for tcgetattr */ 50 | static int getattr(int fd, struct termios *arg) 51 | { 52 | int result, retries = 3; 53 | 54 | while (-1 == (result = tcgetattr(fd, arg)) && retries > 0) { 55 | retries--; 56 | 57 | if (EINTR == errno) 58 | continue; 59 | 60 | break; 61 | } 62 | 63 | return result; 64 | } 65 | 66 | /* Wrapper for tcgetattr */ 67 | static int setattr(int fd, int opt, const struct termios *arg) 68 | { 69 | int result, retries = 3; 70 | 71 | while (-1 == (result = tcsetattr(fd, opt, arg)) && retries > 0) { 72 | retries--; 73 | 74 | if (EINTR == errno) 75 | continue; 76 | 77 | break; 78 | } 79 | 80 | return result; 81 | } 82 | 83 | void rl_ttyset(int Reset) 84 | { 85 | static struct termios old; 86 | struct termios new; 87 | 88 | if (!Reset) { 89 | if (-1 == getattr(0, &old)) 90 | perror("Failed tcgetattr()"); 91 | 92 | rl_erase = old.c_cc[VERASE]; 93 | rl_kill = old.c_cc[VKILL]; 94 | rl_eof = old.c_cc[VEOF]; 95 | rl_intr = old.c_cc[VINTR]; 96 | rl_quit = old.c_cc[VQUIT]; 97 | #ifdef CONFIG_SIGSTOP 98 | rl_susp = old.c_cc[VSUSP]; 99 | #endif 100 | 101 | new = old; 102 | new.c_lflag &= ~(ECHO | ICANON | ISIG); 103 | new.c_iflag &= ~INPCK; 104 | if (rl_meta_chars) 105 | new.c_iflag |= ISTRIP; 106 | else 107 | new.c_iflag &= ~ISTRIP; 108 | new.c_cc[VMIN] = 1; 109 | new.c_cc[VTIME] = 0; 110 | if (-1 == setattr(0, TCSADRAIN, &new)) 111 | perror("Failed tcsetattr(TCSADRAIN)"); 112 | } else { 113 | if (-1 == setattr(0, TCSADRAIN, &old)) 114 | perror("Failed tcsetattr(TCSADRAIN)"); 115 | } 116 | } 117 | 118 | #elif defined(HAVE_TERMIO_H) 119 | #include 120 | 121 | void rl_ttyset(int Reset) 122 | { 123 | static struct termio old; 124 | struct termio new; 125 | 126 | if (!Reset) { 127 | if (-1 == ioctl_wrap(0, TCGETA, &old)) 128 | perror("Failed ioctl(TCGETA)"); 129 | 130 | rl_erase = old.c_cc[VERASE]; 131 | rl_kill = old.c_cc[VKILL]; 132 | rl_eof = old.c_cc[VEOF]; 133 | rl_intr = old.c_cc[VINTR]; 134 | rl_quit = old.c_cc[VQUIT]; 135 | #ifdef CONFIG_SIGSTOP 136 | rl_susp = old.c_cc[VSUSP]; 137 | #endif 138 | 139 | new = old; 140 | new.c_lflag &= ~(ECHO | ICANON | ISIG); 141 | new.c_iflag &= ~INPCK; 142 | if (rl_meta_chars) 143 | new.c_iflag |= ISTRIP; 144 | else 145 | new.c_iflag &= ~ISTRIP; 146 | 147 | new.c_cc[VMIN] = 1; 148 | new.c_cc[VTIME] = 0; 149 | if (-1 == ioctl_wrap(0, TCSETAW, &new)) 150 | perror("Failed ioctl(TCSETAW)"); 151 | } else { 152 | if (-1 == ioctl_wrap(0, TCSETAW, &old)) 153 | perror("Failed ioctl(TCSETAW)"); 154 | } 155 | } 156 | 157 | #elif defined(HAVE_SGTTY_H) 158 | #include 159 | 160 | void rl_ttyset(int Reset) 161 | { 162 | static struct sgttyb old_sgttyb; 163 | static struct tchars old_tchars; 164 | struct sgttyb new_sgttyb; 165 | struct tchars new_tchars; 166 | #ifdef CONFIG_SIGSTOP 167 | struct ltchars old_ltchars; 168 | #endif 169 | 170 | if (!Reset) { 171 | if (-1 == ioctl_wrap(0, TIOCGETP, &old_sgttyb)) 172 | perror("Failed TIOCGETP"); 173 | 174 | rl_erase = old_sgttyb.sg_erase; 175 | rl_kill = old_sgttyb.sg_kill; 176 | 177 | if (-1 == ioctl_wrap(0, TIOCGETC, &old_tchars)) 178 | perror("Failed TIOCGETC"); 179 | 180 | rl_eof = old_tchars.t_eofc; 181 | rl_intr = old_tchars.t_intrc; 182 | rl_quit = old_tchars.t_quitc; 183 | 184 | #ifdef CONFIG_SIGSTOP 185 | if (-1 == ioctl_wrap(0, TIOCGLTC, &old_ltchars)) 186 | perror("Failed TIOCGLTC"); 187 | 188 | rl_susp = old_ltchars.t_suspc; 189 | #endif 190 | 191 | new_sgttyb = old_sgttyb; 192 | new_sgttyb.sg_flags &= ~ECHO; 193 | new_sgttyb.sg_flags |= RAW; 194 | if (rl_meta_chars) 195 | new_sgttyb.sg_flags &= ~PASS8; 196 | else 197 | new_sgttyb.sg_flags |= PASS8; 198 | 199 | if (-1 == ioctl_wrap(0, TIOCSETP, &new_sgttyb)) 200 | perror("Failed TIOCSETP"); 201 | 202 | new_tchars = old_tchars; 203 | new_tchars.t_intrc = -1; 204 | new_tchars.t_quitc = -1; 205 | if (-1 == ioctl_wrap(0, TIOCSETC, &new_tchars)) 206 | perror("Failed TIOCSETC"); 207 | } else { 208 | if (-1 == ioctl_wrap(0, TIOCSETP, &old_sgttyb)) 209 | perror("Failed TIOCSETP"); 210 | 211 | if (-1 == ioctl_wrap(0, TIOCSETC, &old_tchars)) 212 | perror("Failed TIOCSETC"); 213 | } 214 | } 215 | #else /* Neither HAVE_SGTTY_H, HAVE_TERMIO_H or HAVE_TCGETATTR */ 216 | #error Unsupported platform, missing tcgetattr(), termio.h and sgtty.h 217 | #endif /* Neither HAVE_SGTTY_H, HAVE_TERMIO_H or HAVE_TCGETATTR */ 218 | 219 | #ifndef HAVE_STRDUP 220 | /* Return an allocated copy of a string. */ 221 | char *strdup(const char *s) 222 | { 223 | size_t len; 224 | char *ptr; 225 | 226 | if (!s) 227 | return NULL; 228 | 229 | len = strlen(s) + 1; 230 | ptr = malloc(len); 231 | if (ptr) 232 | return memcpy(ptr, s, len); 233 | 234 | return NULL; 235 | } 236 | #endif 237 | 238 | void rl_add_slash(char *path, char *p) 239 | { 240 | struct stat Sb; 241 | 242 | if (stat(path, &Sb) >= 0) 243 | strcat(p, S_ISDIR(Sb.st_mode) ? "/" : " "); 244 | } 245 | 246 | /** 247 | * Local Variables: 248 | * c-file-style: "k&r" 249 | * c-basic-offset: 4 250 | * End: 251 | */ 252 | -------------------------------------------------------------------------------- /src/unix.h: -------------------------------------------------------------------------------- 1 | /* Editline system header file for Unix. 2 | * 3 | * Copyright (c) 1992, 1993 Simmule Turner and Rich Salz 4 | * All rights reserved. 5 | * 6 | * This software is not subject to any license of the American Telephone 7 | * and Telegraph Company or of the Regents of the University of California. 8 | * 9 | * Permission is granted to anyone to use this software for any purpose on 10 | * any computer system, and to alter it and redistribute it freely, subject 11 | * to the following restrictions: 12 | * 1. The authors are not responsible for the consequences of use of this 13 | * software, no matter how awful, even if they arise from flaws in it. 14 | * 2. The origin of this software must not be misrepresented, either by 15 | * explicit claim or by omission. Since few users ever read sources, 16 | * credits must appear in the documentation. 17 | * 3. Altered versions must be plainly marked as such, and must not be 18 | * misrepresented as being the original software. Since few users 19 | * ever read sources, credits must appear in the documentation. 20 | * 4. This notice may not be removed or altered. 21 | */ 22 | 23 | #ifndef EDITLINE_UNIX_H_ 24 | #define EDITLINE_UNIX_H_ 25 | 26 | #define CRLF "\r\n" 27 | #define FORWARD STATIC 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | typedef struct dirent DIRENTRY; 35 | 36 | #endif /* EDITLINE_UNIX_H_ */ 37 | --------------------------------------------------------------------------------