Compare commits

..

No commits in common. "master" and "1.16.0" have entirely different histories.

23 changed files with 890 additions and 1324 deletions

View File

@ -23,7 +23,7 @@ addons:
description: "A small line editing library" description: "A small line editing library"
notification_email: troglobit@gmail.com notification_email: troglobit@gmail.com
build_command_prepend: "./autogen.sh && ./configure --enable-sigstop --enable-terminal-bell" build_command_prepend: "./autogen.sh && ./configure --enable-sigstop --enable-terminal-bell"
build_command: "make clean all" build_command: "make -j5"
branch_pattern: dev branch_pattern: dev
# We don't store generated files (configure and Makefile) in GIT, # We don't store generated files (configure and Makefile) in GIT,
@ -31,4 +31,4 @@ addons:
script: script:
- ./autogen.sh - ./autogen.sh
- ./configure --enable-sigstop --enable-terminal-bell - ./configure --enable-sigstop --enable-terminal-bell
- make clean all - make -j5

View File

@ -4,52 +4,6 @@ Change Log
All notable changes to the project are documented in this file. All notable changes to the project are documented in this file.
[1.17.1][] - 2020-02-23
-----------------------
### Fixes
- Fix #38: Fix for multiline representing as one line
- Fix packaging, missing files in libeditline1, regression from 1.16.0
- Fix packaging, update to latest std version
- Fix formatting of function names in man page
- Restore tar.gz distribution, for usability on systems that do not
have xz in their default install
[1.17.0][] - 2020-01-05
-----------------------
### Changes
- Simple multi-line support by Dima Volynets, @dvolynets
### Fixes
- Fix return value from `read_history()` and `write_history()`, could
return `errno` instead of `EOF` to indicate error. Now both functions
have uniform return values on error
- Handle internal `realloc()` errors better. Now memory is not leaked
if `realloc()` fails
- Fix possible NULL pointer dereference in key binding lookup function
[1.16.1][] - 2019-06-07
-----------------------
### Changes
- Major updates to the `editline.3` man page
- Cleanup of examples `cli.c` and `fileman.c`
- Add example of hidden input prompt to `cli.c`
### Fixes
- Fix #20: `configure --disable-eof` does not bite
- Fix #23: Make Ctrl-L clear the screan instead of starting a new line
Like Ctrl-D, which exits, Ctrl-L only clears the screen when the line
is empty and the cursor is at the start of the line, otherwise Ctrl-L
will redraw/refresh the current line.
- Fix #24: Fix behavior when TTY is narrower than column width, by Will Dietz
- Fix #25: Avoid continuously duplicate commands in history
- Fix #31: Aborting i-search with Ctrl-C should not generate signal
[1.16.0][] - 2018-09-16 [1.16.0][] - 2018-09-16
----------------------- -----------------------
@ -249,10 +203,7 @@ Adaptations to Debian editline package.
- First version, forked from Minix current 2008-06-06 - First version, forked from Minix current 2008-06-06
[UNRELEASED]: https://github.com/troglobit/finit/compare/1.17.1...HEAD [UNRELEASED]: https://github.com/troglobit/finit/compare/1.16.0...HEAD
[1.17.1]: https://github.com/troglobit/finit/compare/1.17.0...1.17.1
[1.17.0]: https://github.com/troglobit/finit/compare/1.16.1...1.17.0
[1.16.1]: https://github.com/troglobit/finit/compare/1.16.0...1.16.1
[1.16.0]: https://github.com/troglobit/finit/compare/1.15.3...1.16.0 [1.16.0]: https://github.com/troglobit/finit/compare/1.15.3...1.16.0
[1.15.3]: https://github.com/troglobit/finit/compare/1.15.2...1.15.3 [1.15.3]: https://github.com/troglobit/finit/compare/1.15.2...1.15.3
[1.15.2]: https://github.com/troglobit/finit/compare/1.15.1...1.15.2 [1.15.2]: https://github.com/troglobit/finit/compare/1.15.1...1.15.2
@ -269,3 +220,9 @@ Adaptations to Debian editline package.
[Travis-CI]: https://travis-ci.org/troglobit/uftpd [Travis-CI]: https://travis-ci.org/troglobit/uftpd
[Coverity Scan]: https://scan.coverity.com/projects/2947 [Coverity Scan]: https://scan.coverity.com/projects/2947
[README.md]: https://github.com/troglobit/editline/blob/master/README.md [README.md]: https://github.com/troglobit/editline/blob/master/README.md
<!--
-- Local Variables:
-- mode: markdown
-- End:
-->

View File

@ -2,11 +2,11 @@ pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libeditline.pc pkgconfig_DATA = libeditline.pc
doc_DATA = README.md LICENSE doc_DATA = README.md LICENSE
EXTRA_DIST = README.md LICENSE ChangeLog.md INSTALL.md EXTRA_DIST = README.md LICENSE ChangeLog.md INSTALL.md
SUBDIRS = src include man SUBDIRS = src include man examples
if ENABLE_EXAMPLES ## Generate .deb package
SUBDIRS += examples package build-deb:
endif @dpkg-buildpackage -uc -us
## Generate MD5 checksum file ## Generate MD5 checksum file
MD5 = md5sum MD5 = md5sum
@ -39,7 +39,7 @@ package:
dpkg-buildpackage -uc -us -B dpkg-buildpackage -uc -us -B
## Target to run when building a release ## Target to run when building a release
release: release-hook distcheck md5-dist package release: distcheck release-hook md5-dist package
@echo @echo
@echo "Resulting release files:" @echo "Resulting release files:"
@echo "=========================================================================" @echo "========================================================================="

View File

@ -33,17 +33,13 @@ to use the library is available in the `examples/` directory.
Editline is maintained collaboratively at [GitHub][]. Editline is maintained collaboratively at [GitHub][].
> **Note:** Windows is not a supported target for editline.
Example Example
------- -------
Below is a very brief example to illustrate how one can use Editline to Here is a very brief example to illustrate how one can use Editline to
create a simple CLI, Ctrl-D exits the program. A slightly more advanced create a simple CLI, use Ctrl-D to exit the program. More examples are
example is Jush, <https://github.com/troglobit/jush/>, a small and very availble in the source tree.
simplistic UNIX shell. The Editline sources also include an `examples/`
sub-directory.
1. Build and install the library, preferably using a [release tarball][] 1. Build and install the library, preferably using a [release tarball][]
The configure script defaults to a `/usr/local` prefix. The configure script defaults to a `/usr/local` prefix.
@ -58,7 +54,6 @@ sub-directory.
e.g. `~/src/example.c` e.g. `~/src/example.c`
```C ```C
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <editline.h> #include <editline.h>
@ -198,7 +193,6 @@ current maintainer works exclusively on GNU/Linux systems, so it may use
GCC and GNU Make specific extensions here and there. This is not on GCC and GNU Make specific extensions here and there. This is not on
purpose and patches or pull requests to correct this are most welcome! purpose and patches or pull requests to correct this are most welcome!
0. Call <kbd>./autogen.sh</kbd> if you build from git
1. Configure editline with default features: <kbd>./configure</kbd> 1. Configure editline with default features: <kbd>./configure</kbd>
2. Build the library and examples: <kbd>make all</kbd> 2. Build the library and examples: <kbd>make all</kbd>
3. Install using <kbd>make install</kbd> 3. Install using <kbd>make install</kbd>
@ -227,7 +221,7 @@ similar to the [BSD license][]. Rich's current version is however under
the Apache license. For details on the licensing terms of this version the Apache license. For details on the licensing terms of this version
of the software, see [License][]. of the software, see [License][].
This version of the editline library was forked from the [Minix 2][] This version of the editline library was forked from the [Minix 3][]
source tree and is *not* related to the similarily named NetBSD version source tree and is *not* related to the similarily named NetBSD version
that [Jess Thrysøe][jess] disitributes to the world outside *BSD. The that [Jess Thrysøe][jess] disitributes to the world outside *BSD. The
libraries have much in common, but the latter is heavily refactored and libraries have much in common, but the latter is heavily refactored and
@ -261,7 +255,7 @@ Outstanding issues are listed in the [TODO.md][] file.
[FSF readline]: http://www.gnu.org/software/readline/ [FSF readline]: http://www.gnu.org/software/readline/
[Rich Salz]: https://github.com/richsalz/editline/ [Rich Salz]: https://github.com/richsalz/editline/
[comp.sources.unix]: http://ftp.cs.toronto.edu/pub/white/pub/rc/editline.shar [comp.sources.unix]: http://ftp.cs.toronto.edu/pub/white/pub/rc/editline.shar
[Minix 2]: http://www.cise.ufl.edu/~cop4600/cgi-bin/lxr/http/source.cgi/lib/editline/ [Minix 3]: http://www.cise.ufl.edu/~cop4600/cgi-bin/lxr/http/source.cgi/lib/editline/
[jess]: http://thrysoee.dk/editline/ [jess]: http://thrysoee.dk/editline/
[BSD license]: http://en.wikipedia.org/wiki/BSD_licenses [BSD license]: http://en.wikipedia.org/wiki/BSD_licenses
[libeditline]: http://packages.qa.debian.org/e/editline.html [libeditline]: http://packages.qa.debian.org/e/editline.html

View File

@ -1,6 +1,5 @@
AC_INIT(editline, 1.17.1, https://github.com/troglobit/editline/issues) AC_INIT(editline, 1.16.0, https://github.com/troglobit/editline/issues)
AC_CONFIG_AUX_DIR(aux) AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
AM_INIT_AUTOMAKE([1.11 foreign dist-xz])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
@ -61,35 +60,29 @@ AC_ARG_ENABLE(terminal-bell,
AC_ARG_ENABLE(termcap, AC_ARG_ENABLE(termcap,
AS_HELP_STRING([--enable-termcap], [Use termcap library to query terminal size.])) AS_HELP_STRING([--enable-termcap], [Use termcap library to query terminal size.]))
AC_ARG_ENABLE([examples],
[AC_HELP_STRING([--enable-examples], [Build examples/ directory])],
[], [enable_examples=no])
# #
# Check what features have been enabled # Check what features have been enabled
# #
AS_IF([test "x$enable_unique_history" != "xno"], [ AS_IF([test "x$enable_unique_history" != "xno"],
AC_DEFINE(CONFIG_UNIQUE_HISTORY, 1, [Define to skip duplicate lines in the scrollback history.])]) AC_DEFINE(CONFIG_UNIQUE_HISTORY, 1, [Define to skip duplicate lines in the scrollback history.]))
AS_IF([test "x$enable_terminal_bell" != "xno"], [ AS_IF([test "x$enable_terminal_bell" != "xno"],
AC_DEFINE(CONFIG_ANSI_ARROWS, 1, [Define to include ANSI arrow keys support.])]) AC_DEFINE(CONFIG_ANSI_ARROWS, 1, [Define to include ANSI arrow keys support.]))
AS_IF([test "x$enable_eof" != "xno"], [ AS_IF([test "x$enable_terminal_bell" != "xno"],
AC_DEFINE(CONFIG_EOF, 1, [Define to enable EOF (Ctrl-D) key.])]) AC_DEFINE(CONFIG_EOF, 1, [Define to enable EOF (Ctrl-C) key.]))
AS_IF([test "x$enable_sigint" != "xno"], [ AS_IF([test "x$enable_sigint" != "xno"],
AC_DEFINE(CONFIG_SIGINT, 1, [Define to enable SIGINT (Ctrl-C) key.])]) AC_DEFINE(CONFIG_SIGINT, 1, [Define to enable SIGINT (Ctrl-C) key.]))
AS_IF([test "x$enable_sigstop" = "xyes"], [ AS_IF([test "x$enable_sigstop" = "xyes"],
AC_DEFINE(CONFIG_SIGSTOP, 1, [Define to enable SIGSTOP (Ctrl-Z) key.])]) AC_DEFINE(CONFIG_SIGSTOP, 1, [Define to enable SIGSTOP (Ctrl-Z) key.]))
AS_IF([test "x$enable_terminal_bell" = "xyes"], [ AS_IF([test "x$enable_terminal_bell" = "xyes"],
AC_DEFINE(CONFIG_TERMINAL_BELL, 1, [Define to enable terminal bell on completion.])]) AC_DEFINE(CONFIG_TERMINAL_BELL, 1, [Define to enable terminal bell on completion.]))
AM_CONDITIONAL([ENABLE_EXAMPLES], [test "$enable_examples" = yes])
# Check for a termcap compatible library if enabled # Check for a termcap compatible library if enabled
AS_IF([test "x$enable_termcap" = "xyes"], [ AS_IF([test "x$enable_termcap" = "xyes"],
AC_DEFINE(CONFIG_USE_TERMCAP, 1, [Define to use the termcap library for terminal size.]) AC_DEFINE(CONFIG_USE_TERMCAP, 1, [Define to use the termcap library for terminal size.])
AC_CHECK_LIB(terminfo, tgetent, , [ AC_CHECK_LIB(terminfo, tgetent, , [
AC_CHECK_LIB(termcap, tgetent, , [ AC_CHECK_LIB(termcap, tgetent, , [
@ -100,7 +93,7 @@ AS_IF([test "x$enable_termcap" = "xyes"], [
]) ])
]) ])
]) ])
])]) ]))
# Generate all files # Generate all files
AC_OUTPUT AC_OUTPUT

24
debian/changelog vendored
View File

@ -1,27 +1,3 @@
editline (1.17.1) stable; urgency=medium
* Fix multiline representing as one line
* Fix missing content in libedtline1, introduced in 1.16.0
* Update packaging to latest std version
-- Joachim Nilsson <troglobit@gmail.com> Sun, 23 Feb 2020 18:46:41 +0100
editline (1.17.0) unstable; urgency=medium
* Simple multi-line support
* Handle internal realloc() errors better
* Fix return value from read_history() and write_history()
* Fix potential NULL pointer dereference in key binging lookup
-- Joachim Nilsson <troglobit@gmail.com> Sun, 05 Jan 2020 09:47:34 +0100
editline (1.16.1) unstable; urgency=medium
* Minor bug fix and documentation update release.
* Add missing pkg-config .pc file to -dev package
-- Joachim Nilsson <troglobit@gmail.com> Fri, 07 Jun 2019 11:45:50 +0200
editline (1.16.0) unstable; urgency=medium editline (1.16.0) unstable; urgency=medium
* New upstream release, v1.60.0 * New upstream release, v1.60.0

2
debian/compat vendored
View File

@ -1 +1 @@
10 9

6
debian/control vendored
View File

@ -1,9 +1,9 @@
Source: editline Source: editline
Section: devel Section: devel
Priority: optional Priority: optional
Build-Depends: debhelper (>= 10), libtool Build-Depends: debhelper (>= 5.0), libtool
Maintainer: Joachim Wiberg <troglobit@gmail.com> Maintainer: Joachim Nilsson <troglobit@gmail.com>
Standards-Version: 4.3.0 Standards-Version: 3.8.3
Package: libeditline-dev Package: libeditline-dev
Architecture: any Architecture: any

25
debian/copyright vendored
View File

@ -1,23 +1,17 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ This package was debianized by Jim Studt <jim@federated.com> on
Upstream-Name: editline Fri, 5 May 2000 13:25:51 -0500.
Upstream-Contact: Joachim Wiberg <troglobit@gmail.com>
Source: http://github.com/troglobit/editline
Comment: This package was originally debianized by Jim Studt <jim@federated.com>
on Fri, 5 May 2000 13:25:51 -0500. It was received from, then upstream
author, Rich Salz <rsalz@shore.net>
Files: * It was received from Rich Salz rsalz@shore.net
Copyright: 1992,1993 Simmule Turner and Rich Salz.
License: C-News
Files: debian/* Upstream Author: Rich Salz rsalz@shore.net
Copyright: 2010-2020 Joachim Wiberg <troglobit@gmail.com>
License: BSD-2-clause Copyright:
Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved.
License: C-News
This software is not subject to any license of the American Telephone This software is not subject to any license of the American Telephone
and Telegraph Company or of the Regents of the University of California. and Telegraph Company or of the Regents of the University of California.
.
Permission is granted to anyone to use this software for any purpose on Permission is granted to anyone to use this software for any purpose on
any computer system, and to alter it and redistribute it freely, subject any computer system, and to alter it and redistribute it freely, subject
to the following restrictions: to the following restrictions:
@ -30,3 +24,4 @@ License: C-News
misrepresented as being the original software. Since few users misrepresented as being the original software. Since few users
ever read sources, credits must appear in the documentation. ever read sources, credits must appear in the documentation.
4. This notice may not be removed or altered. 4. This notice may not be removed or altered.

View File

@ -1,5 +1,3 @@
usr/include/*.h usr/include/*.h
usr/lib/*/libeditline*.*a usr/lib/*/libeditline*.*a
usr/lib/*/libeditline.so
usr/lib/*/pkgconfig/*
usr/share/man/man3/* usr/share/man/man3/*

1
debian/libeditline0.install vendored Normal file
View File

@ -0,0 +1 @@
usr/lib/*/libeditline*.so*

View File

@ -1 +0,0 @@
usr/lib/*/libeditline.so.*

View File

@ -1,67 +0,0 @@
libeditline.so.1 libeditline1 #MINVER#
* Build-Depends-Package: libeditline-dev
add_history@Base 1.17.1
el_bind_key@Base 1.17.1
el_bind_key_in_metamap@Base 1.17.1
el_del_char@Base 1.17.1
el_filename_complete@Base 1.17.1
el_filename_list_possib@Base 1.17.1
el_find_word@Base 1.17.1
el_hist_size@Base 1.17.1
el_next_hist@Base 1.17.1
el_no_echo@Base 1.17.1
el_no_hist@Base 1.17.1
el_prev_hist@Base 1.17.1
el_print_columns@Base 1.17.1
el_ring_bell@Base 1.17.1
prompt_len@Base 1.17.1
read_history@Base 1.17.1
readline@Base 1.17.1
rl_add_slash@Base 1.17.1
rl_attempted_completion_function@Base 1.17.1
rl_attempted_completion_over@Base 1.17.1
rl_callback_handler_install@Base 1.17.1
rl_callback_handler_remove@Base 1.17.1
rl_callback_read_char@Base 1.17.1
rl_clear_message@Base 1.17.1
rl_complete@Base 1.17.1
rl_completion_entry_function@Base 1.17.1
rl_completion_matches@Base 1.17.1
rl_deprep_term_function@Base 1.17.1
rl_deprep_terminal@Base 1.17.1
rl_end@Base 1.17.1
rl_eof@Base 1.17.1
rl_erase@Base 1.17.1
rl_event_hook@Base 1.17.1
rl_filename_completion_function@Base 1.17.1
rl_forced_update_display@Base 1.17.1
rl_getc@Base 1.17.1
rl_getc_function@Base 1.17.1
rl_inhibit_complete@Base 1.17.1
rl_initialize@Base 1.17.1
rl_insert_text@Base 1.17.1
rl_instream@Base 1.17.1
rl_intr@Base 1.17.1
rl_kill@Base 1.17.1
rl_line_buffer@Base 1.17.1
rl_list_possib@Base 1.17.1
rl_mark@Base 1.17.1
rl_meta_chars@Base 1.17.1
rl_outstream@Base 1.17.1
rl_point@Base 1.17.1
rl_prep_term_function@Base 1.17.1
rl_prep_terminal@Base 1.17.1
rl_prompt@Base 1.17.1
rl_quit@Base 1.17.1
rl_readline_name@Base 1.17.1
rl_refresh_line@Base 1.17.1
rl_reset_terminal@Base 1.17.1
rl_restore_prompt@Base 1.17.1
rl_save_prompt@Base 1.17.1
rl_set_complete_func@Base 1.17.1
rl_set_getc_func@Base 1.17.1
rl_set_list_possib_func@Base 1.17.1
rl_set_prompt@Base 1.17.1
rl_ttyset@Base 1.17.1
rl_uninitialize@Base 1.17.1
write_history@Base 1.17.1

12
debian/rules vendored
View File

@ -1,16 +1,8 @@
#!/usr/bin/make -f #!/usr/bin/make -f
# export DH_VERBOSE=1
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export DEB_CFLAGS_MAINT_APPEND = -W -Wall -Wextra -O3
export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS))) # Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
include /usr/share/dpkg/default.mk # provides DEB_VERSION
%: %:
dh $@ --with autoreconf dh $@ --with autoreconf
override_dh_installchangelogs:
dh_installchangelogs ChangeLog.md

View File

@ -38,9 +38,9 @@ static char *my_rl_complete(char *token, int *match)
int count = 0; int count = 0;
for (i = 0; list[i]; i++) { for (i = 0; list[i]; i++) {
int partlen = strlen(token); /* Part of token */ int partlen = strlen (token); /* Part of token */
if (!strncmp(list[i], token, partlen)) { if (!strncmp (list[i], token, partlen)) {
index = i; index = i;
matchlen = partlen; matchlen = partlen;
count ++; count ++;
@ -49,7 +49,7 @@ static char *my_rl_complete(char *token, int *match)
if (count == 1) { if (count == 1) {
*match = 1; *match = 1;
return strdup(list[index] + matchlen); return strdup (list[index] + matchlen);
} }
return NULL; return NULL;
@ -60,18 +60,15 @@ static int my_rl_list_possib(char *token, char ***av)
{ {
int i, num, total = 0; int i, num, total = 0;
char **copy; char **copy;
for (num = 0; list[num]; num++) for (num = 0; list[num]; num++)
; ;
if (!num) copy = (char **) malloc (num * sizeof(char *));
return 0;
copy = malloc(num * sizeof(char *));
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
if (!strncmp(list[i], token, strlen (token))) { if (!strncmp (list[i], token, strlen (token))) {
copy[total] = strdup(list[i]); copy[total] = strdup (list[i]);
total++; total ++;
} }
} }
*av = copy; *av = copy;
@ -101,70 +98,41 @@ el_status_t list_possible(void)
return el_ring_bell(); return el_ring_bell();
} }
el_status_t do_break(void)
{
puts("Breakout!");
return CSeof;
}
el_status_t do_exit(void)
{
puts("Bye bye!");
return CSeof;
}
el_status_t do_suspend(void) el_status_t do_suspend(void)
{ {
puts("Abort!"); puts("Abort!");
return CSstay; return CSstay;
} }
static void breakit(int signo)
{
(void)signo;
puts("Got SIGINT");
}
/* Use el_no_echo when reading passwords and similar */
static int unlock(const char *passwd)
{
char *prompt = "Enter password: ";
char *line;
int rc = 1;
el_no_echo = 1;
while ((line = readline(prompt))) {
rc = strncmp(line, passwd, strlen(passwd));
free(line);
if (rc) {
printf("\nWrong password, please try again, it's secret.\n");
continue;
}
printf("\nAchievement unlocked!\n");
break;
}
el_no_echo = 0;
return rc;
}
int main(void) int main(void)
{ {
char *line; char *line;
char *prompt = "cli> "; char *prompt = "cli> ";
signal(SIGINT, breakit);
/* Setup callbacks */ /* Setup callbacks */
rl_set_complete_func(&my_rl_complete); rl_set_complete_func(&my_rl_complete);
rl_set_list_possib_func(&my_rl_list_possib); rl_set_list_possib_func(&my_rl_list_possib);
el_bind_key('?', list_possible); el_bind_key('?', list_possible);
el_bind_key(CTL('C'), do_break);
el_bind_key(CTL('D'), do_exit);
el_bind_key(CTL('Z'), do_suspend); el_bind_key(CTL('Z'), do_suspend);
read_history(HISTORY); read_history(HISTORY);
while ((line = readline(prompt))) { while ((line = readline(prompt)) != NULL) {
if (!strncmp(line, "unlock", 6) && unlock("secret")) { printf("\t\t\t|%s|\n", line);
free(line); free(line);
fprintf(stderr, "\nSecurity breach, user logged out!\n");
break;
}
if (*line != '\0')
printf("\t\t\t|%s|\n", line);
free(line);
} }
write_history(HISTORY); write_history(HISTORY);

View File

@ -39,7 +39,6 @@ Jeff
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/select.h>
#ifdef HAVE_STDLIB_H #ifdef HAVE_STDLIB_H
#include <stdlib.h> #include <stdlib.h>

View File

@ -5,9 +5,8 @@
NOTE: this was taken from the GNU Readline documentation and ported NOTE: this was taken from the GNU Readline documentation and ported
to libedit. A command to output the history list was added. to libedit. A command to output the history list was added.
*/ */
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/file.h> #include <sys/file.h>
@ -22,15 +21,18 @@
#include "editline.h" #include "editline.h"
void too_dangerous(char *caller); void * xmalloc (size_t size);
void initialize_readline(const char *prompt); void too_dangerous (char *caller);
int execute_line(char *line); void initialize_readline ();
int valid_argument(char *caller, char *arg); int execute_line (char *line);
int valid_argument (char *caller, char *arg);
typedef int rl_icpfunc_t (char *);
/* The names of functions that actually do the manipulation. */ /* The names of functions that actually do the manipulation. */
int com_list(char *); int com_list (char *);
int com_view(char *); int com_view (char *);
int com_history(char *); int com_history (char *);
int com_rename(char *); int com_rename(char *);
int com_stat(char *); int com_stat(char *);
int com_pwd(char *); int com_pwd(char *);
@ -39,156 +41,170 @@ int com_help(char *);
int com_cd(char *); int com_cd(char *);
int com_quit(char *); int com_quit(char *);
struct cmd { /* A structure which contains information on the commands this program
char *name; /* User printable name of the function. */ can understand. */
int (*func)(char *); /* Function to call to do the job. */
char *doc; /* Documentation for this function. */
};
struct cmd commands[] = { typedef struct {
{ "cd", com_cd, "Change to directory DIR"}, char *name; /* User printable name of the function. */
{ "delete", com_delete, "Delete FILE"}, rl_icpfunc_t *func; /* Function to call to do the job. */
{ "help", com_help, "Display this text"}, char *doc; /* Documentation for this function. */
{ "?", com_help, "Synonym for `help'"}, } COMMAND;
{ "list", com_list, "List files in DIR"},
{ "ls", com_list, "Synonym for `list'"}, COMMAND commands[] = {
{ "pwd", com_pwd, "Print the current working directory"}, { "cd", com_cd, "Change to directory DIR" },
{ "quit", com_quit, "Quit using Fileman"}, { "delete", com_delete, "Delete FILE" },
{ "rename", com_rename, "Rename FILE to NEWNAME"}, { "help", com_help, "Display this text" },
{ "stat", com_stat, "Print out statistics on FILE"}, { "?", com_help, "Synonym for `help'" },
{ "view", com_view, "View the contents of FILE"}, { "list", com_list, "List files in DIR" },
{ "history", com_history, "List editline history"}, { "ls", com_list, "Synonym for `list'" },
{ NULL, NULL, NULL }, { "pwd", com_pwd, "Print the current working directory" },
{ "quit", com_quit, "Quit using Fileman" },
{ "rename", com_rename, "Rename FILE to NEWNAME" },
{ "stat", com_stat, "Print out statistics on FILE" },
{ "view", com_view, "View the contents of FILE" },
{ "history", com_history, "List editline history" },
{ (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL }
}; };
/* Forward declarations. */ /* Forward declarations. */
char *stripwhite(char *string); char *stripwhite ();
struct cmd *find_command(char *name); COMMAND *find_command ();
/* ~/.fileman_history */ /* The name of this program, as taken from argv[0]. */
char *fileman_history; char *progname;
/* Prompt base and current */
const char *prompt_init;
char *prompt_curr;
/* When non-zero, this means the user is done using this program. */ /* When non-zero, this means the user is done using this program. */
int done; int done;
int main(int argc, char **argv) char *
dupstr (char* s)
{ {
char *line, *s; char *r;
setlocale(LC_CTYPE, ""); r = xmalloc (strlen (s) + 1);
initialize_readline("(FileMan)"); strcpy (r, s);
return (r);
}
/* Loop reading and executing lines until the user quits. */ int
for (; done == 0;) { main (int argc __attribute__((__unused__)), char **argv)
line = readline(NULL); {
char *line, *s;
if (!line) progname = argv[0];
break;
/* Remove leading and trailing whitespace from the line. setlocale(LC_CTYPE, "");
Then, if there is anything left, add it to the history list
and execute it. */ initialize_readline(); /* Bind our completer. */
s = stripwhite(line);
/* Loop reading and executing lines until the user quits. */
for ( ; done == 0; )
{
line = readline ("FileMan: ");
if (!line)
break;
/* Remove leading and trailing whitespace from the line.
Then, if there is anything left, add it to the history list
and execute it. */
s = stripwhite(line);
#if 0 #if 0
if (*s) { if (*s) {
char *expansion; char* expansion;
int result; int result;
result = history_expand(s, &expansion); result = history_expand(s, &expansion);
if (result < 0 || result == 2) { if (result < 0 || result == 2) {
fprintf(stderr, "%s\n", expansion); fprintf(stderr, "%s\n", expansion);
} else { } else {
add_history(expansion); add_history(expansion);
execute_line(expansion); execute_line(expansion);
} }
free(expansion); free(expansion);
} }
#else #else
execute_line(s); execute_line(s);
#endif #endif
free(line); free(line);
} }
exit (0);
puts(""); return 0;
write_history(fileman_history);
free(fileman_history);
return 0;
} }
/* Execute a command line. */ /* Execute a command line. */
int execute_line(char *line) int
execute_line (char *line)
{ {
int i; register int i;
struct cmd *command; COMMAND *command;
char *word; char *word;
/* Isolate the command word. */ /* Isolate the command word. */
i = 0; i = 0;
while (line[i] && isspace(line[i])) while (line[i] && isspace (line[i]))
i++; i++;
word = line + i; word = line + i;
while (line[i] && !isspace(line[i])) while (line[i] && !isspace (line[i]))
i++; i++;
if (line[i]) if (line[i])
line[i++] = '\0'; line[i++] = '\0';
command = find_command(word); command = find_command (word);
if (!command) { if (!command)
fprintf(stderr, "%s: No such command for FileMan.\n", word); {
return -1; fprintf (stderr, "%s: No such command for FileMan.\n", word);
} return (-1);
}
/* Get argument to command, if any. */ /* Get argument to command, if any. */
while (isspace(line[i])) while (isspace (line[i]))
i++; i++;
word = line + i; word = line + i;
/* Call the function. */ /* Call the function. */
return command->func(word); return ((*(command->func)) (word));
} }
/* Look up NAME as the name of a command, and return a pointer to that /* Look up NAME as the name of a command, and return a pointer to that
command. Return a NULL pointer if NAME isn't a command name. */ command. Return a NULL pointer if NAME isn't a command name. */
struct cmd *find_command(char *name) COMMAND *
find_command (char *name)
{ {
int i; register int i;
for (i = 0; commands[i].name; i++) for (i = 0; commands[i].name; i++)
if (strcmp(name, commands[i].name) == 0) if (strcmp (name, commands[i].name) == 0)
return &commands[i]; return (&commands[i]);
return NULL; return ((COMMAND *)NULL);
} }
/* /* Strip whitespace from the start and end of STRING. Return a pointer
* Strip whitespace from the start and end of STRING. Return a pointer into STRING. */
* into STRING. char *
*/ stripwhite (char *string)
char *stripwhite(char *string)
{ {
char *s, *t; register char *s, *t;
for (s = string; isspace(*s); s++) ; for (s = string; isspace (*s); s++)
;
if (*s == 0) if (*s == 0)
return s; return (s);
t = s + strlen(s) - 1; t = s + strlen (s) - 1;
while (t > s && isspace(*t)) while (t > s && isspace (*t))
t--; t--;
*++t = '\0'; *++t = '\0';
return s; return s;
} }
/* **************************************************************** */ /* **************************************************************** */
@ -199,107 +215,73 @@ char *stripwhite(char *string)
char *command_generator(const char *, int); char *command_generator(const char *, int);
char **fileman_completion(const char *, int, int); char **fileman_completion(const char *, int, int);
void fileman_prompt(void);
/* /* Tell the GNU Readline library how to complete. We want to try to
* Tell the GNU Readline library how to complete. We want to try to complete on command names if this is the first word in the line, or
* complete on command names if this is the first word in the line, or on filenames if not. */
* on filenames if not. void
*/ initialize_readline ()
void initialize_readline(const char *prompt)
{ {
const char *home; /* Allow conditional parsing of the ~/.inputrc file. */
size_t len; rl_readline_name = "FileMan";
/* Allow conditional parsing of the ~/.inputrc file. */ /* Tell the completer that we want a crack first. */
rl_readline_name = "FileMan"; rl_attempted_completion_function = fileman_completion;
/* Tell the completer that we want a crack first. */
rl_attempted_completion_function = fileman_completion;
/* Restore command history */
home = getenv("HOME");
len = (home ? strlen(home) : 0) + 14;
fileman_history = malloc(len);
assert(fileman_history);
snprintf(fileman_history, len, "%s/.fileman_history", home ? home : ".");
read_history(fileman_history);
/* Prompt is updated when moving around in the tree */
prompt_init = prompt;
fileman_prompt();
} }
/* /* Attempt to complete on the contents of TEXT. START and END
* Update prompt when changing directory. Use an allocated string to bound the region of rl_line_buffer that contains the word to
* show off the rl_set_prompt() API for issue #51. complete. TEXT is the word to complete. We can use the entire
*/ contents of rl_line_buffer in case we want to do some simple
void fileman_prompt(void) parsing. Returnthe array of matches, or NULL if there aren't any. */
char **
fileman_completion (const char* text, int start, int end __attribute__((__unused__)))
{ {
char cwd[1024]; char **matches;
size_t len;
if (prompt_curr) matches = (char **)NULL;
free(prompt_curr);
assert(getcwd(cwd, sizeof(cwd))); /* If this word is at the start of the line, then it is a command
len = strlen(prompt_init) + strlen(cwd) + 10; to complete. Otherwise it is the name of a file in the current
prompt_curr = malloc(len); directory. */
assert(prompt_curr); if (start == 0)
matches = rl_completion_matches (text, command_generator);
snprintf(prompt_curr, len, "%s %s/> ", prompt_init, cwd); return (matches);
rl_set_prompt(prompt_curr);
}
/*
* Attempt to complete on the contents of TEXT. START and END
* bound the region of rl_line_buffer that contains the word to
* complete. TEXT is the word to complete. We can use the entire
* contents of rl_line_buffer in case we want to do some simple
* parsing. Returnthe array of matches, or NULL if there aren't any.
*/
char **fileman_completion(const char *text, int start, int end)
{
char **matches = NULL;
/* If this word is at the start of the line, then it is a command
to complete. Otherwise it is the name of a file in the current
directory. */
if (start == 0)
matches = rl_completion_matches(text, command_generator);
return matches;
} }
/* Generator function for command completion. STATE lets us /* Generator function for command completion. STATE lets us
know whether to start from scratch; without any state know whether to start from scratch; without any state
(i.e. STATE == 0), then we start at the top of the list. */ (i.e. STATE == 0), then we start at the top of the list. */
char *command_generator(const char *text, int state) char *
command_generator (text, state)
const char *text;
int state;
{ {
static int list_index, len; static int list_index, len;
char *name; char *name;
/* If this is a new word to complete, initialize now. This /* If this is a new word to complete, initialize now. This
includes saving the length of TEXT for efficiency, and includes saving the length of TEXT for efficiency, and
initializing the index variable to 0. */ initializing the index variable to 0. */
if (!state) { if (!state)
list_index = 0; {
len = strlen(text); list_index = 0;
} len = strlen (text);
}
/* Return the next name which partially matches from the /* Return the next name which partially matches from the
command list. */ command list. */
while ((name = commands[list_index].name)) { while ((name = commands[list_index].name))
list_index++; {
list_index++;
if (strncmp(name, text, len) == 0) if (strncmp (name, text, len) == 0)
return strdup(name); return (dupstr(name));
} }
/* If no names matched, then return NULL. */ /* If no names matched, then return NULL. */
return NULL; return ((char *)NULL);
} }
/* **************************************************************** */ /* **************************************************************** */
@ -313,170 +295,193 @@ char *command_generator(const char *text, int state)
static char syscom[1024]; static char syscom[1024];
/* List the file(s) named in arg. */ /* List the file(s) named in arg. */
int com_list(char *arg) int
com_list (char *arg)
{ {
if (!arg) if (!arg)
arg = ""; arg = "";
sprintf(syscom, "ls -FClg %s", arg); sprintf (syscom, "ls -FClg %s", arg);
return (system (syscom));
return system(syscom);
} }
int com_view(char *arg) int
com_view (char *arg)
{ {
if (!valid_argument("view", arg)) if (!valid_argument ("view", arg))
return 1; return 1;
sprintf(syscom, "more %s", arg); sprintf (syscom, "more %s", arg);
return (system (syscom));
return system(syscom);
} }
int com_history(char *arg) int
com_history(char* arg __attribute__((__unused__)))
{ {
const char *he; const char *he;
/* rewind history */ /* rewind history */
while (el_prev_hist()) ; while (el_prev_hist())
;
for (he = el_next_hist(); he != NULL; he = el_next_hist()) for (he = el_next_hist(); he != NULL; he = el_next_hist())
printf("%s\n", he); printf("%s\n", he);
return 0; return 0;
} }
int com_rename(char *arg) int
com_rename (char *arg __attribute__((__unused__)))
{ {
too_dangerous("rename"); too_dangerous ("rename");
return 1; return (1);
} }
int com_stat(char *arg) int
com_stat (char *arg)
{ {
struct stat finfo; struct stat finfo;
if (!valid_argument("stat", arg)) if (!valid_argument ("stat", arg))
return 1; return (1);
if (stat(arg, &finfo) == -1) { if (stat (arg, &finfo) == -1)
perror(arg); {
return 1; perror (arg);
} return (1);
}
printf("Statistics for `%s':\n", arg); printf ("Statistics for `%s':\n", arg);
printf("%s has %ld link%s, and is %lld byte%s in length.\n", arg, printf ("%s has %ld link%s, and is %lld byte%s in length.\n", arg,
(long)finfo.st_nlink, (long) finfo.st_nlink,
(finfo.st_nlink == 1) ? "" : "s", (finfo.st_nlink == 1) ? "" : "s",
(long long)finfo.st_size, (finfo.st_size == 1) ? "" : "s"); (long long) finfo.st_size,
printf("Inode Last Change at: %s", ctime(&finfo.st_ctime)); (finfo.st_size == 1) ? "" : "s");
printf(" Last access at: %s", ctime(&finfo.st_atime)); printf ("Inode Last Change at: %s", ctime (&finfo.st_ctime));
printf(" Last modified at: %s", ctime(&finfo.st_mtime)); printf (" Last access at: %s", ctime (&finfo.st_atime));
printf (" Last modified at: %s", ctime (&finfo.st_mtime));
return 0; return (0);
} }
int com_delete(char *arg) int
com_delete (char *arg __attribute__((__unused__)))
{ {
too_dangerous("delete"); too_dangerous ("delete");
return 1; return (1);
} }
/* Print out help for ARG, or for all of the commands if ARG is /* Print out help for ARG, or for all of the commands if ARG is
not present. */ not present. */
int com_help(char *arg) int
com_help (char *arg)
{ {
int i; register int i;
int printed = 0; int printed = 0;
for (i = 0; commands[i].name; i++) { for (i = 0; commands[i].name; i++)
if (!*arg || (strcmp(arg, commands[i].name) == 0)) { {
printf("%s\t\t%s.\n", commands[i].name, if (!*arg || (strcmp (arg, commands[i].name) == 0))
commands[i].doc); {
printed++; printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc);
} printed++;
} }
}
if (!printed) { if (!printed)
printf("No commands match `%s'. Possibilties are:\n", arg); {
printf ("No commands match `%s'. Possibilties are:\n", arg);
for (i = 0; commands[i].name; i++) { for (i = 0; commands[i].name; i++)
/* Print in six columns. */ {
if (printed == 6) { /* Print in six columns. */
printed = 0; if (printed == 6)
printf("\n"); {
} printed = 0;
printf ("\n");
}
printf("%s\t", commands[i].name); printf ("%s\t", commands[i].name);
printed++; printed++;
} }
if (printed) if (printed)
printf("\n"); printf ("\n");
} }
return (0);
return 0;
} }
/* Change to the directory ARG. */ /* Change to the directory ARG. */
int com_cd(char *arg) int
com_cd (char *arg)
{ {
if (chdir(arg) == -1) { if (chdir (arg) == -1)
perror(arg); {
return 1; perror (arg);
} return 1;
}
//com_pwd(""); com_pwd ("");
fileman_prompt(); return (0);
return 0;
} }
/* Print out the current working directory. */ /* Print out the current working directory. */
int com_pwd(char *ignore) int
com_pwd (char* ignore __attribute__((__unused__)))
{ {
char dir[1024], *s; char dir[1024], *s;
s = getcwd(dir, sizeof(dir) - 1); s = (char*)getcwd(dir, sizeof(dir) - 1);
if (!s) { if (s == 0)
printf("Error getting pwd: %s\n", dir); {
return 1; printf ("Error getting pwd: %s\n", dir);
} return 1;
}
printf("Current directory is %s\n", dir); printf ("Current directory is %s\n", dir);
return 0; return 0;
} }
/* The user wishes to quit using this program. Just set DONE /* The user wishes to quit using this program. Just set DONE
non-zero. */ non-zero. */
int com_quit(char *arg) int
com_quit (char *arg __attribute__((__unused__)))
{ {
done = 1; done = 1;
return 0; return (0);
} }
/* Function which tells you that you can't do this. */ /* Function which tells you that you can't do this. */
void too_dangerous(char *caller) void
too_dangerous (char *caller)
{ {
fprintf(stderr, "%s: Too dangerous for me to distribute.\n", caller); fprintf (stderr,
fprintf(stderr, "Write it yourself.\n"); "%s: Too dangerous for me to distribute.\n",
caller);
fprintf (stderr, "Write it yourself.\n");
} }
/* Return non-zero if ARG is a valid argument for CALLER, /* Return non-zero if ARG is a valid argument for CALLER,
else print an error message and return zero. */ else print an error message and return zero. */
int valid_argument(char *caller, char *arg) int
valid_argument (char *caller, char *arg)
{ {
if (!arg || !*arg) { if (!arg || !*arg)
fprintf(stderr, "%s: Argument required.\n", caller); {
return 0; fprintf (stderr, "%s: Argument required.\n", caller);
} return (0);
}
return 1; return (1);
} }
/** void *
* Local Variables: xmalloc (size_t size)
* c-file-style: "k&r" {
* c-basic-offset: 4 register void *value = (void*)malloc(size);
* End: if (value == 0)
*/ fprintf(stderr, "virtual memory exhausted");
return value;
}

View File

@ -21,8 +21,6 @@
#ifndef EDITLINE_H_ #ifndef EDITLINE_H_
#define EDITLINE_H_ #define EDITLINE_H_
#include <stdio.h>
/* Handy macros when binding keys. */ /* Handy macros when binding keys. */
#define CTL(x) ((x) & 0x1F) #define CTL(x) ((x) & 0x1F)
#define ISCTL(x) ((x) && (x) < ' ') #define ISCTL(x) ((x) && (x) < ' ')
@ -84,7 +82,6 @@ extern int rl_point;
extern int rl_mark; extern int rl_mark;
extern int rl_end; extern int rl_end;
extern int rl_inhibit_complete; extern int rl_inhibit_complete;
extern int rl_attempted_completion_over;
extern char *rl_line_buffer; extern char *rl_line_buffer;
extern const char *rl_readline_name; extern const char *rl_readline_name;
extern FILE *rl_instream; /* The stdio stream from which input is read. Defaults to stdin if NULL - Not supported yet! */ extern FILE *rl_instream; /* The stdio stream from which input is read. Defaults to stdin if NULL - Not supported yet! */
@ -117,8 +114,6 @@ extern void add_history (const char *line);
extern int read_history (const char *filename); extern int read_history (const char *filename);
extern int write_history (const char *filename); extern int write_history (const char *filename);
extern rl_getc_func_t *rl_set_getc_func(rl_getc_func_t *func);
extern rl_completion_func_t *rl_attempted_completion_function; extern rl_completion_func_t *rl_attempted_completion_function;
extern rl_complete_func_t *rl_set_complete_func (rl_complete_func_t *func); extern rl_complete_func_t *rl_set_complete_func (rl_complete_func_t *func);
extern rl_list_possib_func_t *rl_set_list_possib_func (rl_list_possib_func_t *func); extern rl_list_possib_func_t *rl_set_list_possib_func (rl_list_possib_func_t *func);

View File

@ -1,287 +1,200 @@
.Dd February 23, 2020 .TH EDITLINE 3
.Dt EDITLINE 3 .SH NAME
.Os editline \- command-line editing library with history
.Sh NAME .SH SYNOPSIS
.Nm editline .nf
.Nd command-line editing library with history .B "char *readline(char *prompt);"
.Sh LIBRARY .fi
.Lb libeditline .SH DESCRIPTION
.Sh SYNOPSIS .I Editline
.In editline.h is a library that provides an line-editing interface with history.
.Ft char * It is intended to be functionally equivalent with the
.Fo readline .I readline
.Fa const char *prompt library provided by the Free Software Foundation, but much smaller.
.Fc The bulk of this manual page describes the user interface.
.Ft void .PP
.Fo add_history
.Fa const char *line
.Fc
.Ft int
.Fo read_history
.Fa const char *filename
.Fc
.Ft int
.Fo write_history
.Fa const char *filename
.Fc
.Sh DESCRIPTION
.Nm
is a library that provides n line-editing interface with history. It
is intended to be functionally equivalent with the
.Nm readline
library provided by the Free Software Foundation, but much smaller. The
bulk of this manual page describes the basic user interface. More APIs,
both native and for
.Nm readline
compatibility ,
are also available. See the
.Cm editline.h
header file for details.
.Pp
The The
.Fn readline .I readline()
function displays the given function displays the given
.Fa prompt .I prompt
on stdout, waits for user input on stdin and then returns a line of text on stdout, waits for user input on stdin and then
with the trailing newline removed. The data is returned in a buffer returns a line of text with the trailing newline removed. The data is returned in a
allocated with buffer allocated with
.Xr malloc 3 , .IR malloc (3),
so the space should be released with so the space should be released with
.Xr free 3 .IR free (3)
when the calling program is done with it. when the calling program is done with it.
.Pp .PP
Each line returned is automatically saved in the internal history list, Each line returned is copied to the internal history list, unless it happens
unless it happens to be equal to the previous line. This is to be equal to the previous line. This is configurable if you are building editline
configurable if you are building editline from source, i.e. if you would from source.
rather like to call .SS User Interface
.Fn add_history A program that uses this library provides a simple emacs-like editing interface to
manually. its users. A line may be edited before it is sent to the calling program by typing
.Pp either control characters or escape sequences. A control character, shown as a caret
The followed by a letter, is typed by holding down the ``control'' key while the letter
.Fn read_history is typed. For example, ``^A'' is a control-A. An escape sequence is entered by
and typing the ``escape'' key followed by one or more characters. The escape key is
.Fn write_history abbreviated as ``ESC''. Note that unlike control keys, case matters in escape
functions can be used to load and store the history of your application. sequences; ``ESC\ F'' is not the same as ``ESC\ f''.
.Em Note: .PP
these APIs do not do any tilde or environment variable expansion of the An editing command may be typed anywhere on the line, not just at the beginning. In
given filename. addition, a return may also be typed anywhere on the line, not just at the end.
.Ss User Interface .PP
A program that uses this library provides a simple emacs-like editing
interface to its users. A line may be edited before it is sent to the
calling program by typing either control characters or escape sequences.
A control character, shown as a caret followed by a letter, is typed by
holding down the control key while the letter is typed. For example,
.Cm ^A
is a control-A. An escape sequence is entered by typing the escape key
followed by one or more characters. The escape key is abbreviated as
.Cm ESC .
Note that unlike control keys, case matters in escape sequences;
.Cm ESC F
is not the same as
.Cm ESC f .
.Pp
An editing command may be typed anywhere on the line, not just at the
beginning. In addition, a return may also be typed anywhere on the
line, not just at the end.
.Pp
Most editing commands may be given a repeat count, Most editing commands may be given a repeat count,
.Ar n , .IR n ,
where where
.Ar n .I n
is a number. To enter a repeat count, type the escape key, the number, is a number.
and then the command to execute. For example, To enter a repeat count, type the escape key, the number, and then
.Cm ESC 4 ^f the command to execute.
moves forward four characters. If a command may be given a repeat count For example, ``ESC\ 4\ ^f'' moves forward four characters.
then the text If a command may be given a repeat count then the text ``[n]'' is given at the
.Cm [n] end of its description.
is given at the end of its description. .PP
.Pp
The following control characters are accepted: The following control characters are accepted:
.Pp .RS
.Bl -tag -width "ESC DEL " -compact .nf
.It ^A .ta \w'ESC DEL 'u
Move to the beginning of the line ^A Move to the beginning of the line
.It ^B ^B Move left (backwards) [n]
Move left (backwards) [n] ^D Delete character [n]
.It ^D ^E Move to end of line
Delete character [n] ^F Move right (forwards) [n]
.It ^E ^G Ring the bell
Move to end of line ^H Delete character before cursor (backspace key) [n]
.It ^F ^I Complete filename (tab key); see below
Move right (forwards) [n] ^J Done with line (return key)
.It ^G ^K Kill to end of line (or column [n])
Ring the bell ^L Redisplay line
.It ^H ^M Done with line (alternate return key)
Delete character before cursor (backspace key) [n] ^N Get next line from history [n]
.It ^I ^P Get previous line from history [n]
Complete filename (tab key); see below ^R Search backward (forward if [n]) through history for
.It ^J \& text; must start line if text begins with an uparrow
Done with line (return key) ^T Transpose characters
.It ^K ^V Insert next character, even if it is an edit command
Kill to end of line (or column [n]) ^W Wipe to the mark
.It ^L ^X^X Exchange current location and mark
Redisplay line ^Y Yank back last killed text
.It ^M ^[ Start an escape sequence (escape key)
Done with line (alternate return key) ^]c Move forward to next character ``c''
.It ^N ^? Delete character before cursor (delete key) [n]
Get next line from history [n] .fi
.It ^P .RE
Get previous line from history [n] .PP
.It ^R The following escape sequences are provided.
Search backward (forward if [n]) through history for text; prefixing the .RS
string with a caret (^) forces it to match only at the beginning of a .nf
history line .ta \w'ESC DEL 'u
.It ^T ESC\ ^H Delete previous word (backspace key) [n]
Transpose characters ESC\ DEL Delete previous word (delete key) [n]
.It ^V ESC\ SP Set the mark (space key); see ^X^X and ^Y above
Insert next character, even if it is an edit command ESC\ \. Get the last (or [n]'th) word from previous line
.It ^W ESC\ ? Show possible completions; see below
Wipe to the mark ESC\ < Move to start of history
.It ^X^X ESC\ > Move to end of history
Exchange current location and mark ESC\ b Move backward a word [n]
.It ^Y ESC\ d Delete word under cursor [n]
Yank back last killed text ESC\ f Move forward a word [n]
.It ^[ ESC\ l Make word lowercase [n]
Start an escape sequence (escape key) ESC\ m Toggle if 8bit chars display normally or with an
.It ^]c \& ``M\-'' prefix
Move forward to next character ESC\ u Make word uppercase [n]
.Cm c ESC\ y Yank back last killed text
.It ^? ESC\ v Show library version
Delete character before cursor (delete key) [n] ESC\ w Make area up to mark yankable
.El ESC\ nn Set repeat count to the number nn
.Pp ESC\ C Read from environment variable ``_C_'', where C is
The following escape sequences are provided: \& an uppercase letter
.Pp .fi
.Bl -tag -width "ESC DEL " -compact .RE
.It ESC ^H .PP
Delete previous word (backspace key) [n]
.It ESC DEL
Delete previous word (delete key) [n]
.It ESC SP
Set the mark (space key); see ^X^X and ^Y above
.It ESC\ .
Get the last (or [n]'th) word from previous line
.It ESC\ ?
Show possible completions; see below
.It ESC <
Move to start of history
.It ESC >
Move to end of history
.It ESC b
Move backward a word [n]
.It ESC d
Delete word under cursor [n]
.It ESC f
Move forward a word [n]
.It ESC l
Make word lowercase [n]
.It ESC m
Toggle if 8bit chars display normally or with an
.Ar M-
prefix
.It ESC u
Make word uppercase [n]
.It ESC y
Yank back last killed text
.It ESC v
Show library version
.It ESC w
Make area up to mark yankable
.It ESC nn
Set repeat count to the number nn
.It ESC C
Read from environment variable
.Ar $C ,
where
.Ar C
is an uppercase letter
.El
.Pp
The The
.Nm .I editline
library has a small macro facility. If you type the escape key followed library has a small macro facility.
by an uppercase letter, If you type the escape key followed by an uppercase letter,
.Ar C , .IR C ,
then the contents of the environment variable then the contents of the environment variable
.Ar $C .I _C_
are read in as if you had typed them at the keyboard. For example, if are read in as if you had typed them at the keyboard.
the variable For example, if the variable
.Ar $L .I _L_
contains the following: contains the following:
.Pp .PP
.Dl ^A^Kecho '^V^[[H^V^[[2J'^M .RS
.Pp ^A^Kecho '^V^[[H^V^[[2J'^M
Then typing .RE
.Cm ESC L .PP
will move to the beginning of the line, kill the entire line, enter the Then typing ``ESC L'' will move to the beginning of the line, kill the
echo command needed to clear the terminal (if your terminal is like a entire line, enter the echo command needed to clear the terminal (if your
VT-100), and send the line back to the shell. terminal is like a VT-100), and send the line back to the shell.
.Pp .PP
The The
.Nm .I editline
library also does filename completion. Suppose the root directory has library also does filename completion.
the following files in it: Suppose the root directory has the following files in it:
.Pp .PP
.Dl bin vmunix .RS
.Dl core vmunix.old .nf
.Pp .ta \w'core 'u
If you type bin vmunix
.Cm rm /v core vmunix.old
and then the tab key, .fi
.Nm .RE
will then finish off as much of the name as possible by adding .PP
.Ar munix . If you type ``rm\ /v'' and then the tab key.
Because the name is not unique, it will then beep. If you type the .I Editline
escape key and a question mark, it will display the two choices. If you will then finish off as much of the name as possible by adding ``munix''.
then type a period and a tab, the library will finish off the filename Because the name is not unique, it will then beep.
If you type the escape key and a question mark, it will display the
two choices.
If you then type a period and a tab, the library will finish off the filename
for you: for you:
.Pp .PP
.Bd -ragged -offset indent .RS
rm /v[TAB] .nf
.Em munix .RI "rm /v[TAB]" munix ".[TAB]" old
\&.[TAB] .fi
.Em old .RE
.Ed .PP
.Pp The tab key is shown by ``[TAB]'' and the automatically-entered text
The tab key is shown by [TAB] and the automatically-entered text is shown in italics.
is shown in italics, or underline. .SH USAGE
.Sh USAGE
To include To include
.Nm .I readline()
in your program, call it as you do any other function and link your in your program, simply call it as you do any other function. Just make sure to link
program with your program with libeditline.
.Ar -leditline . .SS Example
.Ss Example
The following brief example lets you enter a line and edit it, then displays it. The following brief example lets you enter a line and edit it, then displays it.
.Pp .nf
.Bd -literal -offset indent .B ""
#include <stdio.h> .B #include <stdlib.h>
#include <stdlib.h> .B ""
#include <editline.h> .B extern char *readline(char *prompt);
.B ""
int main(void) .B int main(void)
{ .B {
char *p; .RS
.B char *p;
while ((p = readline("CLI> "))) { .B ""
puts(p); .B while ((p = readline("CLI>"))) {
free(p); .RS
} .B puts(p);
.B free(p);
return 0; .RE
} .B }
.El .B ""
.Sh AUTHORS .B return 0;
The original editline library was posted to comp.sources.unix newsgroup .RE
by created by Simmule R. Turner and Rich Salz in 1992. It now exists in .B }
several forks: Debian, Minix, Heimdal, Festival speech tools, Mozilla, .fi
Google Gadgets for Linux, and many others. The original manual page was .SH AUTHORS
made by David W. Sanderson. The original editline library was created by Simmule R. Turner and Rich
.Pp Salz. It now exists in several forks: Heimdal, Festival speech tools,
This version stems from the Minix 2 sources, but has since evolved to Mozilla, Google Gadgets for Linux, and many other places. The original
include patches from all relevant forks. It is currently maintained by manual page was made by David W. Sanderson. This version is maintained
.An Joachim Wiberg by Joachim Nilsson at GitHub,
at .Aq http://github.com/troglobit/editline
.Lk https://github.com/troglobit/editline "GitHub" . .SH BUGS
.Sh BUGS
Does not handle multiple lines or unicode characters well. Does not handle multiple lines or unicode characters well.

View File

@ -3,4 +3,4 @@ libeditline_la_SOURCES = editline.c editline.h complete.c sysunix.c unix.h
libeditline_la_CFLAGS = -std=gnu99 libeditline_la_CFLAGS = -std=gnu99
libeditline_la_CFLAGS += -W -Wall -Wextra -Wundef -Wunused -Wstrict-prototypes libeditline_la_CFLAGS += -W -Wall -Wextra -Wundef -Wunused -Wstrict-prototypes
libeditline_la_CFLAGS += -Werror-implicit-function-declaration -Wshadow -Wcast-qual libeditline_la_CFLAGS += -Werror-implicit-function-declaration -Wshadow -Wcast-qual
libeditline_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:2:0 libeditline_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:0:0

View File

@ -40,7 +40,7 @@ static int compare(const void *p1, const void *p2)
/* Fill in *avp with an array of names that match file, up to its length. /* Fill in *avp with an array of names that match file, up to its length.
* Ignore . and .. . */ * Ignore . and .. . */
static int FindMatches(const char *dir, const char *file, char ***avp) static int FindMatches(char *dir, char *file, char ***avp)
{ {
char **av; char **av;
char **word; char **word;
@ -128,7 +128,7 @@ static int FindMatches(const char *dir, const char *file, char ***avp)
/* Split a pathname into allocated directory and trailing filename parts. */ /* Split a pathname into allocated directory and trailing filename parts. */
static int SplitPath(const char *path, char **dirpart, char **filepart) static int SplitPath(const char *path, char **dirpart, char **filepart)
{ {
static const char DOT[] = "."; static char DOT[] = ".";
char *dpart; char *dpart;
char *fpart; char *fpart;
@ -197,14 +197,14 @@ char *el_filename_complete(char *pathname, int *match)
if (ac == 1) { if (ac == 1) {
/* Exactly one match -- finish it off. */ /* Exactly one match -- finish it off. */
*match = 1; *match = 1;
j = strlen(av[0]) - len + 1; j = strlen(av[0]) - len + 2;
p = malloc(sizeof(char) * (j + 1)); p = malloc(sizeof(char) * (j + 1));
if (p) { if (p) {
memcpy(p, av[0] + len, j); memcpy(p, av[0] + len, j);
len = strlen(dir) + strlen(av[0]) + 2; len = strlen(dir) + strlen(av[0]) + 2;
path = malloc(sizeof(char) * len); path = malloc(sizeof(char) * len);
if (path) { if (path) {
snprintf(path, len, "%s/%s", dir, av[0]); snprintf(path, len, "%s/%s", dir, av[0]);
rl_add_slash(path, p); rl_add_slash(path, p);
free(path); free(path);
} }
@ -243,7 +243,9 @@ char *el_filename_complete(char *pathname, int *match)
char *rl_filename_completion_function(const char *text, int state) char *rl_filename_completion_function(const char *text, int state)
{ {
static char **av, *dir, *file; char *dir;
char *file;
static char **av;
static size_t i, ac; static size_t i, ac;
if (!state) { if (!state) {
@ -251,47 +253,32 @@ char *rl_filename_completion_function(const char *text, int state)
return NULL; return NULL;
ac = FindMatches(dir, file, &av); ac = FindMatches(dir, file, &av);
if (!ac) { free(dir);
free(dir); free(file);
free(file); if (!ac)
return NULL; return NULL;
}
i = 0; i = 0;
} }
if (i < ac) { if (i < ac)
size_t len = (dir ? strlen(dir) : 0) + strlen(av[i]) + 3; return av[i++];
char *ptr = malloc(len);
if (ptr) {
snprintf(ptr, len, "%s%s", dir, av[i++]);
if (ac == 1)
rl_add_slash(ptr, ptr);
return ptr;
}
}
do { do {
free(av[--i]); free(av[--i]);
} while (i > 0); } while (i > 0);
free(av);
free(dir);
free(file);
return NULL; return NULL;
} }
/* Similar to el_find_word(), but used by GNU Readline API */ /* Similar to el_find_word(), but used by GNU Readline API */
static char *rl_find_token(size_t *len) static char *rl_find_token(size_t *len)
{ {
const char *ptr; char *ptr;
int pos; int pos;
for (pos = rl_point; pos < rl_end; pos++) { for (pos = rl_point; pos < rl_end; pos++) {
if (isspace((unsigned char) rl_line_buffer[pos])) { if (isspace(rl_line_buffer[pos])) {
if (pos > 0) if (pos > 0)
pos--; pos--;
break; break;
@ -299,7 +286,7 @@ static char *rl_find_token(size_t *len)
} }
ptr = &rl_line_buffer[pos]; ptr = &rl_line_buffer[pos];
while (pos >= 0 && !isspace((unsigned char) rl_line_buffer[pos])) { while (pos >= 0 && !isspace(rl_line_buffer[pos])) {
if (pos == 0) if (pos == 0)
break; break;
@ -380,12 +367,8 @@ static char *complete(char *token, int *match)
free(word); free(word);
word = NULL; word = NULL;
if (words[0])
/* Exactly one match -- finish it off. */
if (words[0] && !words[1]) {
*match = 1;
word = strdup(words[0] + len); word = strdup(words[0] + len);
}
while (words[i]) while (words[i])
free(words[i++]); free(words[i++]);
@ -395,9 +378,6 @@ static char *complete(char *token, int *match)
return word; return word;
} }
if (word)
free(word);
fallback: fallback:
return el_filename_complete(token, match); return el_filename_complete(token, match);
} }

File diff suppressed because it is too large Load Diff

View File

@ -57,10 +57,8 @@
#define MEM_INC 64 #define MEM_INC 64
#define SCREEN_INC 256 #define SCREEN_INC 256
/* From The Practice of Programming, by Kernighan and Pike */ /* http://stackoverflow.com/questions/1598773/is-there-a-standard-function-in-c-that-would-return-the-length-of-an-array/1598827#1598827 */
#ifndef NELEMS #define ARRAY_ELEMENTS(arr) ((sizeof(arr)/sizeof(0[arr])) / ((size_t)(!(sizeof(arr) % sizeof(0[arr])))))
#define NELEMS(array) (sizeof(array) / sizeof(array[0]))
#endif
/* /*
** Variables and routines internal to this package. ** Variables and routines internal to this package.