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"
notification_email: troglobit@gmail.com
build_command_prepend: "./autogen.sh && ./configure --enable-sigstop --enable-terminal-bell"
build_command: "make clean all"
build_command: "make -j5"
branch_pattern: dev
# We don't store generated files (configure and Makefile) in GIT,
@ -31,4 +31,4 @@ addons:
script:
- ./autogen.sh
- ./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.
[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
-----------------------
@ -249,10 +203,7 @@ Adaptations to Debian editline package.
- First version, forked from Minix current 2008-06-06
[UNRELEASED]: https://github.com/troglobit/finit/compare/1.17.1...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
[UNRELEASED]: https://github.com/troglobit/finit/compare/1.16.0...HEAD
[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.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
[Coverity Scan]: https://scan.coverity.com/projects/2947
[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
doc_DATA = README.md LICENSE
EXTRA_DIST = README.md LICENSE ChangeLog.md INSTALL.md
SUBDIRS = src include man
SUBDIRS = src include man examples
if ENABLE_EXAMPLES
SUBDIRS += examples
endif
## Generate .deb package
package build-deb:
@dpkg-buildpackage -uc -us
## Generate MD5 checksum file
MD5 = md5sum
@ -39,7 +39,7 @@ package:
dpkg-buildpackage -uc -us -B
## Target to run when building a release
release: release-hook distcheck md5-dist package
release: distcheck release-hook md5-dist package
@echo
@echo "Resulting release files:"
@echo "========================================================================="

View File

@ -33,17 +33,13 @@ to use the library is available in the `examples/` directory.
Editline is maintained collaboratively at [GitHub][].
> **Note:** Windows is not a supported target for editline.
Example
-------
Below 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
example is Jush, <https://github.com/troglobit/jush/>, a small and very
simplistic UNIX shell. The Editline sources also include an `examples/`
sub-directory.
Here is a very brief example to illustrate how one can use Editline to
create a simple CLI, use Ctrl-D to exit the program. More examples are
availble in the source tree.
1. Build and install the library, preferably using a [release tarball][]
The configure script defaults to a `/usr/local` prefix.
@ -58,7 +54,6 @@ sub-directory.
e.g. `~/src/example.c`
```C
#include <stdio.h>
#include <stdlib.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
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>
2. Build the library and examples: <kbd>make all</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
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
that [Jess Thrysøe][jess] disitributes to the world outside *BSD. The
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/
[Rich Salz]: https://github.com/richsalz/editline/
[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/
[BSD license]: http://en.wikipedia.org/wiki/BSD_licenses
[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_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz])
AC_INIT(editline, 1.16.0, https://github.com/troglobit/editline/issues)
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
AM_SILENT_RULES([yes])
AC_CONFIG_MACRO_DIR([m4])
@ -61,35 +60,29 @@ AC_ARG_ENABLE(terminal-bell,
AC_ARG_ENABLE(termcap,
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
#
AS_IF([test "x$enable_unique_history" != "xno"], [
AC_DEFINE(CONFIG_UNIQUE_HISTORY, 1, [Define to skip duplicate lines in the scrollback history.])])
AS_IF([test "x$enable_unique_history" != "xno"],
AC_DEFINE(CONFIG_UNIQUE_HISTORY, 1, [Define to skip duplicate lines in the scrollback history.]))
AS_IF([test "x$enable_terminal_bell" != "xno"], [
AC_DEFINE(CONFIG_ANSI_ARROWS, 1, [Define to include ANSI arrow keys support.])])
AS_IF([test "x$enable_terminal_bell" != "xno"],
AC_DEFINE(CONFIG_ANSI_ARROWS, 1, [Define to include ANSI arrow keys support.]))
AS_IF([test "x$enable_eof" != "xno"], [
AC_DEFINE(CONFIG_EOF, 1, [Define to enable EOF (Ctrl-D) key.])])
AS_IF([test "x$enable_terminal_bell" != "xno"],
AC_DEFINE(CONFIG_EOF, 1, [Define to enable EOF (Ctrl-C) key.]))
AS_IF([test "x$enable_sigint" != "xno"], [
AC_DEFINE(CONFIG_SIGINT, 1, [Define to enable SIGINT (Ctrl-C) key.])])
AS_IF([test "x$enable_sigint" != "xno"],
AC_DEFINE(CONFIG_SIGINT, 1, [Define to enable SIGINT (Ctrl-C) key.]))
AS_IF([test "x$enable_sigstop" = "xyes"], [
AC_DEFINE(CONFIG_SIGSTOP, 1, [Define to enable SIGSTOP (Ctrl-Z) key.])])
AS_IF([test "x$enable_sigstop" = "xyes"],
AC_DEFINE(CONFIG_SIGSTOP, 1, [Define to enable SIGSTOP (Ctrl-Z) key.]))
AS_IF([test "x$enable_terminal_bell" = "xyes"], [
AC_DEFINE(CONFIG_TERMINAL_BELL, 1, [Define to enable terminal bell on completion.])])
AM_CONDITIONAL([ENABLE_EXAMPLES], [test "$enable_examples" = yes])
AS_IF([test "x$enable_terminal_bell" = "xyes"],
AC_DEFINE(CONFIG_TERMINAL_BELL, 1, [Define to enable terminal bell on completion.]))
# 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_CHECK_LIB(terminfo, tgetent, , [
AC_CHECK_LIB(termcap, tgetent, , [
@ -100,7 +93,7 @@ AS_IF([test "x$enable_termcap" = "xyes"], [
])
])
])
])])
]))
# Generate all files
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
* 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
Section: devel
Priority: optional
Build-Depends: debhelper (>= 10), libtool
Maintainer: Joachim Wiberg <troglobit@gmail.com>
Standards-Version: 4.3.0
Build-Depends: debhelper (>= 5.0), libtool
Maintainer: Joachim Nilsson <troglobit@gmail.com>
Standards-Version: 3.8.3
Package: libeditline-dev
Architecture: any

25
debian/copyright vendored
View File

@ -1,23 +1,17 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: editline
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>
This package was debianized by Jim Studt <jim@federated.com> on
Fri, 5 May 2000 13:25:51 -0500.
Files: *
Copyright: 1992,1993 Simmule Turner and Rich Salz.
License: C-News
It was received from Rich Salz rsalz@shore.net
Files: debian/*
Copyright: 2010-2020 Joachim Wiberg <troglobit@gmail.com>
License: BSD-2-clause
Upstream Author: Rich Salz rsalz@shore.net
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
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
any computer system, and to alter it and redistribute it freely, subject
to the following restrictions:
@ -30,3 +24,4 @@ License: C-News
misrepresented as being the original software. Since few users
ever read sources, credits must appear in the documentation.
4. This notice may not be removed or altered.

View File

@ -1,5 +1,3 @@
usr/include/*.h
usr/lib/*/libeditline*.*a
usr/lib/*/libeditline.so
usr/lib/*/pkgconfig/*
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
# Uncomment this to turn on verbose mode.
#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)))
include /usr/share/dpkg/default.mk # provides DEB_VERSION
%:
dh $@ --with autoreconf
override_dh_installchangelogs:
dh_installchangelogs ChangeLog.md

View File

@ -64,10 +64,7 @@ static int my_rl_list_possib(char *token, char ***av)
for (num = 0; list[num]; num++)
;
if (!num)
return 0;
copy = malloc(num * sizeof(char *));
copy = (char **) malloc (num * sizeof(char *));
for (i = 0; i < num; i++) {
if (!strncmp (list[i], token, strlen (token))) {
copy[total] = strdup (list[i]);
@ -101,68 +98,39 @@ el_status_t list_possible(void)
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)
{
puts("Abort!");
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)
{
char *line;
char *prompt = "cli> ";
signal(SIGINT, breakit);
/* Setup callbacks */
rl_set_complete_func(&my_rl_complete);
rl_set_list_possib_func(&my_rl_list_possib);
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);
read_history(HISTORY);
while ((line = readline(prompt))) {
if (!strncmp(line, "unlock", 6) && unlock("secret")) {
free(line);
fprintf(stderr, "\nSecurity breach, user logged out!\n");
break;
}
if (*line != '\0')
while ((line = readline(prompt)) != NULL) {
printf("\t\t\t|%s|\n", line);
free(line);
}

View File

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

View File

@ -7,7 +7,6 @@
*/
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
@ -22,11 +21,14 @@
#include "editline.h"
void * xmalloc (size_t size);
void too_dangerous (char *caller);
void initialize_readline(const char *prompt);
void initialize_readline ();
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. */
int com_list (char *);
int com_view (char *);
@ -39,13 +41,16 @@ int com_help(char *);
int com_cd(char *);
int com_quit(char *);
struct cmd {
char *name; /* User printable name of the function. */
int (*func)(char *); /* Function to call to do the job. */
char *doc; /* Documentation for this function. */
};
/* A structure which contains information on the commands this program
can understand. */
struct cmd commands[] = {
typedef struct {
char *name; /* User printable name of the function. */
rl_icpfunc_t *func; /* Function to call to do the job. */
char *doc; /* Documentation for this function. */
} COMMAND;
COMMAND commands[] = {
{ "cd", com_cd, "Change to directory DIR" },
{ "delete", com_delete, "Delete FILE" },
{ "help", com_help, "Display this text" },
@ -58,33 +63,44 @@ struct cmd commands[] = {
{ "stat", com_stat, "Print out statistics on FILE" },
{ "view", com_view, "View the contents of FILE" },
{ "history", com_history, "List editline history" },
{ NULL, NULL, NULL },
{ (char *)NULL, (rl_icpfunc_t *)NULL, (char *)NULL }
};
/* Forward declarations. */
char *stripwhite(char *string);
struct cmd *find_command(char *name);
char *stripwhite ();
COMMAND *find_command ();
/* ~/.fileman_history */
char *fileman_history;
/* Prompt base and current */
const char *prompt_init;
char *prompt_curr;
/* The name of this program, as taken from argv[0]. */
char *progname;
/* When non-zero, this means the user is done using this program. */
int done;
int main(int argc, char **argv)
char *
dupstr (char* s)
{
char *r;
r = xmalloc (strlen (s) + 1);
strcpy (r, s);
return (r);
}
int
main (int argc __attribute__((__unused__)), char **argv)
{
char *line, *s;
progname = argv[0];
setlocale(LC_CTYPE, "");
initialize_readline("(FileMan)");
initialize_readline(); /* Bind our completer. */
/* Loop reading and executing lines until the user quits. */
for (; done == 0;) {
line = readline(NULL);
for ( ; done == 0; )
{
line = readline ("FileMan: ");
if (!line)
break;
@ -113,19 +129,17 @@ int main(int argc, char **argv)
#endif
free(line);
}
puts("");
write_history(fileman_history);
free(fileman_history);
exit (0);
return 0;
}
/* Execute a command line. */
int execute_line(char *line)
int
execute_line (char *line)
{
int i;
struct cmd *command;
register int i;
COMMAND *command;
char *word;
/* Isolate the command word. */
@ -142,9 +156,10 @@ int execute_line(char *line)
command = find_command (word);
if (!command) {
if (!command)
{
fprintf (stderr, "%s: No such command for FileMan.\n", word);
return -1;
return (-1);
}
/* Get argument to command, if any. */
@ -154,34 +169,35 @@ int execute_line(char *line)
word = line + i;
/* 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
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++)
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
* into STRING.
*/
char *stripwhite(char *string)
/* Strip whitespace from the start and end of STRING. Return a pointer
into 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)
return s;
return (s);
t = s + strlen (s) - 1;
while (t > s && isspace (*t))
@ -199,70 +215,31 @@ char *stripwhite(char *string)
char *command_generator(const char *, 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
* complete on command names if this is the first word in the line, or
* on filenames if not.
*/
void initialize_readline(const char *prompt)
/* 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
on filenames if not. */
void
initialize_readline ()
{
const char *home;
size_t len;
/* Allow conditional parsing of the ~/.inputrc file. */
rl_readline_name = "FileMan";
/* 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();
}
/*
* Update prompt when changing directory. Use an allocated string to
* show off the rl_set_prompt() API for issue #51.
*/
void fileman_prompt(void)
/* 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 __attribute__((__unused__)))
{
char cwd[1024];
size_t len;
char **matches;
if (prompt_curr)
free(prompt_curr);
assert(getcwd(cwd, sizeof(cwd)));
len = strlen(prompt_init) + strlen(cwd) + 10;
prompt_curr = malloc(len);
assert(prompt_curr);
snprintf(prompt_curr, len, "%s %s/> ", prompt_init, cwd);
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;
matches = (char **)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
@ -270,13 +247,16 @@ char **fileman_completion(const char *text, int start, int end)
if (start == 0)
matches = rl_completion_matches (text, command_generator);
return matches;
return (matches);
}
/* Generator function for command completion. STATE lets us
know whether to start from scratch; without any state
(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;
char *name;
@ -284,22 +264,24 @@ char *command_generator(const char *text, int state)
/* If this is a new word to complete, initialize now. This
includes saving the length of TEXT for efficiency, and
initializing the index variable to 0. */
if (!state) {
if (!state)
{
list_index = 0;
len = strlen (text);
}
/* Return the next name which partially matches from the
command list. */
while ((name = commands[list_index].name)) {
while ((name = commands[list_index].name))
{
list_index++;
if (strncmp (name, text, len) == 0)
return strdup(name);
return (dupstr(name));
}
/* If no names matched, then return NULL. */
return NULL;
return ((char *)NULL);
}
/* **************************************************************** */
@ -313,32 +295,34 @@ char *command_generator(const char *text, int state)
static char syscom[1024];
/* List the file(s) named in arg. */
int com_list(char *arg)
int
com_list (char *arg)
{
if (!arg)
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))
return 1;
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;
/* rewind history */
while (el_prev_hist()) ;
while (el_prev_hist())
;
for (he = el_next_hist(); he != NULL; he = el_next_hist())
printf("%s\n", he);
@ -346,22 +330,25 @@ int com_history(char *arg)
return 0;
}
int com_rename(char *arg)
int
com_rename (char *arg __attribute__((__unused__)))
{
too_dangerous ("rename");
return 1;
return (1);
}
int com_stat(char *arg)
int
com_stat (char *arg)
{
struct stat finfo;
if (!valid_argument ("stat", arg))
return 1;
return (1);
if (stat(arg, &finfo) == -1) {
if (stat (arg, &finfo) == -1)
{
perror (arg);
return 1;
return (1);
}
printf ("Statistics for `%s':\n", arg);
@ -369,41 +356,47 @@ int com_stat(char *arg)
printf ("%s has %ld link%s, and is %lld byte%s in length.\n", arg,
(long) finfo.st_nlink,
(finfo.st_nlink == 1) ? "" : "s",
(long long)finfo.st_size, (finfo.st_size == 1) ? "" : "s");
(long long) finfo.st_size,
(finfo.st_size == 1) ? "" : "s");
printf ("Inode Last Change at: %s", ctime (&finfo.st_ctime));
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");
return 1;
return (1);
}
/* Print out help for ARG, or for all of the commands if ARG is
not present. */
int com_help(char *arg)
int
com_help (char *arg)
{
int i;
register int i;
int printed = 0;
for (i = 0; commands[i].name; i++) {
if (!*arg || (strcmp(arg, commands[i].name) == 0)) {
printf("%s\t\t%s.\n", commands[i].name,
commands[i].doc);
for (i = 0; commands[i].name; i++)
{
if (!*arg || (strcmp (arg, commands[i].name) == 0))
{
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);
for (i = 0; commands[i].name; i++) {
for (i = 0; commands[i].name; i++)
{
/* Print in six columns. */
if (printed == 6) {
if (printed == 6)
{
printed = 0;
printf ("\n");
}
@ -415,30 +408,32 @@ int com_help(char *arg)
if (printed)
printf ("\n");
}
return 0;
return (0);
}
/* 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;
}
//com_pwd("");
fileman_prompt();
return 0;
com_pwd ("");
return (0);
}
/* Print out the current working directory. */
int com_pwd(char *ignore)
int
com_pwd (char* ignore __attribute__((__unused__)))
{
char dir[1024], *s;
s = getcwd(dir, sizeof(dir) - 1);
if (!s) {
s = (char*)getcwd(dir, sizeof(dir) - 1);
if (s == 0)
{
printf ("Error getting pwd: %s\n", dir);
return 1;
}
@ -449,34 +444,44 @@ int com_pwd(char *ignore)
/* The user wishes to quit using this program. Just set DONE
non-zero. */
int com_quit(char *arg)
int
com_quit (char *arg __attribute__((__unused__)))
{
done = 1;
return 0;
return (0);
}
/* 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,
"%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,
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;
return (0);
}
return 1;
return (1);
}
/**
* Local Variables:
* c-file-style: "k&r"
* c-basic-offset: 4
* End:
*/
void *
xmalloc (size_t size)
{
register void *value = (void*)malloc(size);
if (value == 0)
fprintf(stderr, "virtual memory exhausted");
return value;
}

View File

@ -21,8 +21,6 @@
#ifndef EDITLINE_H_
#define EDITLINE_H_
#include <stdio.h>
/* Handy macros when binding keys. */
#define CTL(x) ((x) & 0x1F)
#define ISCTL(x) ((x) && (x) < ' ')
@ -84,7 +82,6 @@ extern int rl_point;
extern int rl_mark;
extern int rl_end;
extern int rl_inhibit_complete;
extern int rl_attempted_completion_over;
extern char *rl_line_buffer;
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! */
@ -117,8 +114,6 @@ extern void add_history (const char *line);
extern int read_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_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);

View File

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

View File

@ -91,7 +91,6 @@ static const char *el_input = NILSTR;
static char *Yanked;
static char *Screen;
static char NEWLINE[]= CRLF;
static char CLEAR[]= "\ec";
static const char *el_term = "dumb";
static int Repeat;
static int old_point;
@ -113,9 +112,6 @@ static int Searching = 0;
static const char *(*search_move)(void);
static const char *old_prompt = NULL;
static rl_vcpfunc_t *line_handler = NULL;
static char *line_up = "\x1b[A";
static char *line_down = "\x1b[B";
static int prompt_len = 0;
int el_no_echo = 0; /* e.g., under Emacs */
int el_no_hist = 0;
@ -125,7 +121,6 @@ int rl_end;
int rl_meta_chars = 0; /* Display 8-bit chars as the actual char(0) or as `M-x'(1)? */
int rl_inhibit_complete = 0;
char *rl_line_buffer = NULL;
static const char *rl_saved_prompt = NULL;
const char *rl_prompt = NULL;
const char *rl_readline_name = NULL; /* Set by calling program, for conditional parsing of ~/.inputrc - Not supported yet! */
FILE *rl_instream = NULL; /* The stdio stream from which input is read. Defaults to stdin if NULL */
@ -138,6 +133,7 @@ extern char *tgetstr(const char *, char **);
extern int tgetent(char *, const char *);
extern int tgetnum(const char *);
#endif
/*
** Misc. local helper functions.
@ -179,12 +175,8 @@ static void tty_put(const char c)
Screen[ScreenCount] = c;
if (++ScreenCount >= ScreenSize) {
char *ptr;
ScreenSize += SCREEN_INC;
ptr = realloc(Screen, sizeof(char) * ScreenSize);
if (ptr)
Screen = ptr;
Screen = realloc(Screen, sizeof(char) * ScreenSize);
}
}
@ -211,17 +203,10 @@ static void tty_show(unsigned char c)
}
}
static void tty_string(const char *p)
static void tty_string(char *p)
{
int i = rl_point + prompt_len + 1;
while (*p) {
while (*p)
tty_show(*p++);
if ((i++) % tty_cols == 0) {
tty_put(' ');
tty_put('\b');
}
}
}
static void tty_push(int c)
@ -265,18 +250,11 @@ static void tty_backn(int n)
tty_back();
}
static void tty_forwardn(int n)
{
char buf[12];
snprintf(buf, sizeof(buf), "\x1b[%dC", n);
tty_puts(buf);
}
static void tty_info(void)
{
rl_reset_terminal(NULL);
}
/*
** Glue routines to rl_ttyset()
@ -305,18 +283,13 @@ void el_print_columns(int ac, char **av)
int skip;
int longest;
int cols;
int colwidth;
/* Find longest name, determine column count from that. */
for (longest = 0, i = 0; i < ac; i++) {
if ((j = strlen((char *)av[i])) > longest)
longest = j;
}
colwidth = longest + 3;
if (colwidth > tty_cols)
colwidth = tty_cols;
cols = tty_cols / colwidth;
cols = tty_cols / (longest + 3);
tty_puts(NEWLINE);
for (skip = ac / cols + 1, i = 0; i < skip; i++) {
@ -325,7 +298,7 @@ void el_print_columns(int ac, char **av)
tty_put(*p);
if (j + skip < ac) {
while (++len < colwidth)
while (++len < longest + 3)
tty_put(' ');
}
}
@ -334,64 +307,20 @@ void el_print_columns(int ac, char **av)
}
}
static void reposition(int key)
static void reposition(void)
{
int len_with_prompt = prompt_len + rl_end;
int n = len_with_prompt / tty_cols; /* determine the number of lines */
int i = 0;
int i;
tty_put('\r');
if (n > 0) {
int line;
/* determine num of current line */
if (key == CTL('A') || key == CTL('E') || key == rl_kill)
line = (prompt_len + old_point) / tty_cols;
else
line = len_with_prompt / tty_cols;
/* move to end of line(s) */
if (key == CTL('E')) {
int k;
for (k = line; k < n; k++)
tty_puts(line_down);
/* determine reminder of last line and redraw only it */
i = rl_point - (len_with_prompt % tty_cols);
} else {
int k;
/* CTRL-A, CTRL-U, insert (end, middle), remove (end, middle) */
for (k = line; k > 0; k--)
tty_puts(line_up); /* redraw characters until changed data */
tty_puts(rl_prompt);
}
} else if (n == 0) {
tty_puts(rl_prompt);
}
for (; i < rl_point; i++) {
for (i = 0; i < rl_point; i++)
tty_show(rl_line_buffer[i]);
/* move to the next line */
if ((i + prompt_len + 1) % tty_cols == 0)
tty_put('\n');
}
}
static void left(el_status_t Change)
{
if (rl_point) {
if ((rl_point + prompt_len) % tty_cols == 0) {
tty_puts(line_up);
tty_forwardn(tty_cols);
} else {
tty_back();
}
if (ISMETA(rl_line_buffer[rl_point - 1])) {
if (rl_meta_chars) {
tty_back();
@ -408,9 +337,6 @@ static void left(el_status_t Change)
static void right(el_status_t Change)
{
if ((rl_point + prompt_len + 1) % tty_cols == 0)
tty_put('\n');
else
tty_show(rl_line_buffer[rl_point]);
if (Change == CSmove)
@ -464,6 +390,12 @@ static el_status_t do_forward(el_status_t move)
right(CSstay);
}
/* Skip to next word, or skip leading white space if outside a word. */
for ( ; rl_point < rl_end && (p[0] == ' ' || !is_alpha_num(p[0])); rl_point++, p++) {
if (move == CSmove)
right(CSstay);
}
if (rl_point == rl_end)
break;
} while (++i < Repeat);
@ -489,10 +421,10 @@ static el_status_t do_case(el_case_t type)
for (i = rl_point, p = &rl_line_buffer[i]; rl_point < end; p++) {
if ((type == TOupper) || (type == TOcapitalize && rl_point == i)) {
if (islower((unsigned char)(*p)))
*p = toupper((unsigned char)(*p));
} else if (isupper((unsigned char)(*p))) {
*p = tolower((unsigned char)(*p));
if (islower(*p))
*p = toupper(*p);
} else if (isupper(*p)) {
*p = tolower(*p);
}
right(CSmove);
}
@ -525,14 +457,10 @@ static void ceol(void)
while (rl_point < 0) {
tty_put(' ');
rl_point++;
extras++;
}
for (i = rl_point, p = &rl_line_buffer[i]; i <= rl_end; i++, p++) {
if ((i + prompt_len + 1) % tty_cols == 0){
tty_put(' ');
tty_put('\n');
}
else
tty_put(' ');
if (ISMETA(*p)) {
if (rl_meta_chars) {
@ -546,32 +474,15 @@ static void ceol(void)
}
}
for (i += extras; i > rl_point; i--) {
if ((i + prompt_len) % tty_cols == 0) {
tty_puts(line_up);
tty_forwardn(tty_cols);
} else {
for (i += extras; i > rl_point; i--)
tty_back();
}
}
}
static void clear_line(void)
{
int n = (rl_point + prompt_len) / tty_cols;
rl_point = -(int)strlen(rl_prompt);
if (n > 0) {
for(int k = 0; k < n; k++)
tty_puts(line_up);
tty_put('\r');
}
else {
tty_put('\r');
}
ceol();
rl_point = 0;
rl_end = 0;
rl_line_buffer[0] = '\0';
@ -621,36 +532,28 @@ int rl_insert_text(const char *text)
return rl_point - mark;
}
static el_status_t redisplay(int cls)
static el_status_t redisplay(void)
{
if (cls)
tty_puts(CLEAR);
else
tty_puts("\r\e[K");
/* XXX: Use "\r\e[K" to get really neat effect on ANSI capable terminals. */
tty_puts(NEWLINE);
tty_puts(rl_prompt);
rl_point = 0;
tty_string(rl_line_buffer);
rl_point = rl_end;
return CSmove;
}
static el_status_t refresh(void)
{
return redisplay(1);
return CSmove;
}
int rl_refresh_line(int ignore1 __attribute__((unused)), int ignore2 __attribute__((unused)))
{
redisplay(0);
redisplay();
return 0;
}
static el_status_t toggle_meta_mode(void)
{
rl_meta_chars = ! rl_meta_chars;
return redisplay(0);
return redisplay();
}
const char *el_next_hist(void)
{
@ -670,7 +573,7 @@ static el_status_t do_insert_hist(const char *p)
clear_line();
rl_point = 0;
reposition(EOF);
reposition();
rl_end = 0;
return insert_string(p);
@ -773,20 +676,19 @@ static const char *search_hist(const char *search, const char *(*move)(void))
static el_status_t h_search_end(const char *p)
{
rl_set_prompt(old_prompt);
rl_prompt = old_prompt;
Searching = 0;
if (el_intr_pending > 0) {
if (p == NULL && el_intr_pending > 0) {
el_intr_pending = 0;
clear_line();
return redisplay(0);
return redisplay();
}
p = search_hist(p, search_move);
if (p == NULL) {
el_ring_bell();
clear_line();
return redisplay(0);
return redisplay();
}
return do_insert_hist(p);
@ -800,8 +702,8 @@ static el_status_t h_search(void)
clear_line();
old_prompt = rl_prompt;
rl_set_prompt("Search: ");
reposition(EOF);
rl_prompt = "Search: ";
tty_puts(rl_prompt);
search_move = Repeat == NO_ARG ? el_prev_hist : el_next_hist;
if (line_handler) {
@ -877,7 +779,6 @@ static el_status_t delete_string(int count)
for (p = &rl_line_buffer[rl_point], i = rl_end - (rl_point + count) + 1; --i >= 0; p++)
p[0] = p[count];
ceol();
rl_end -= count;
tty_string(&rl_line_buffer[rl_point]);
@ -918,7 +819,7 @@ static el_status_t kill_line(void)
if (Repeat < rl_point) {
i = rl_point;
rl_point = Repeat;
reposition(EOF);
reposition();
delete_string(i - rl_point);
} else if (Repeat > rl_point) {
right(CSmove);
@ -987,7 +888,7 @@ static el_status_t end_line(void)
static el_status_t del_char(void)
{
return delete_string(Repeat == NO_ARG ? CSeof : Repeat);
return delete_string(Repeat == NO_ARG ? 1 : Repeat);
}
el_status_t el_del_char(void)
@ -1029,30 +930,6 @@ static el_status_t meta(void)
return CSeof;
#ifdef CONFIG_ANSI_ARROWS
/* See: https://en.wikipedia.org/wiki/ANSI_escape_code */
/* Recognize ANSI escapes for `Meta+Left` and `Meta+Right`. */
if (c == '\e') {
switch (tty_get()) {
case '[':
{
switch (tty_get()) {
/* \e\e[C = Meta+Left */
case 'C': return fd_word();
/* \e\e[D = Meta+Right */
case 'D': return bk_word();
default:
break;
}
return el_ring_bell();
}
default:
break;
}
return el_ring_bell();
}
/* Also include VT-100 arrows. */
if (c == '[' || c == 'O') {
switch (tty_get()) {
@ -1060,31 +937,21 @@ static el_status_t meta(void)
case '1':
{
char seq[4] = { 0 };
seq[0] = tty_get();
/* \e[1~ */
if (seq[0] == '~')
return beg_line(); /* Home */
for (c = 1; c < 3; c++)
for (c = 0; c < 3; c++)
seq[c] = tty_get();
if (!strncmp(seq, ";5C", 3)
|| !strncmp(seq, ";3C", 3))
return fd_word(); /* \e[1;5C = Ctrl+Right */
if (!strncmp(seq, ";5D", 3)
|| !strncmp(seq, ";3D", 3))
return bk_word(); /* \e[1;5D = Ctrl+Left */
if (!strncmp(seq, ";5C", 3))
return fd_word(); /* Ctrl+Right */
if (!strncmp(seq, ";5D", 3))
return bk_word(); /* Ctrl+Left */
break;
}
case '2': tty_get(); return CSstay; /* Insert */
case '3': tty_get(); return del_char(); /* Delete */
case '4': tty_get(); return end_line(); /* End */
case '5': tty_get(); return CSstay; /* PgUp */
case '6': tty_get(); return CSstay; /* PgDn */
case '7': tty_get(); return beg_line(); /* Home (urxvt) */
case '8': tty_get(); return end_line(); /* End (urxvt) */
case 'A': return h_prev(); /* Up */
case 'B': return h_next(); /* Down */
case 'C': return fd_char(); /* Left */
@ -1154,8 +1021,6 @@ static el_status_t emacs(int c)
static el_status_t tty_special(int c)
{
el_status_t rc;
#ifdef CONFIG_SIGINT
if (c == rl_intr) {
el_intr_pending = SIGINT;
@ -1180,10 +1045,13 @@ static el_status_t tty_special(int c)
return bk_del_char();
if (c == rl_kill) {
Repeat = rl_point;
rc = bk_del_char();
if (rl_point != 0) {
rl_point = 0;
reposition();
}
Repeat = NO_ARG;
return rc;
return kill_line();
}
#ifdef CONFIG_EOF
@ -1214,7 +1082,7 @@ static char *editinput(int complete)
return (char *)"";
case CSmove:
reposition(c);
reposition();
break;
case CSdispatch:
@ -1229,7 +1097,7 @@ static char *editinput(int complete)
return (char *)"";
case CSmove:
reposition(c);
reposition();
break;
case CSdispatch:
@ -1249,7 +1117,7 @@ static char *editinput(int complete)
static void hist_alloc(void)
{
if (!H.Lines)
H.Lines = calloc(1 + el_hist_size, sizeof(char *));
H.Lines = calloc(el_hist_size, sizeof(char *));
}
static void hist_add(const char *p)
@ -1258,7 +1126,7 @@ static void hist_add(const char *p)
char *s;
#ifdef CONFIG_UNIQUE_HISTORY
if (H.Size && strcmp(p, H.Lines[H.Size - 1]) == 0)
if (H.Pos && strcmp(p, H.Lines[H.Pos - 1]) == 0)
return;
#endif
@ -1266,11 +1134,11 @@ static void hist_add(const char *p)
if (s == NULL)
return;
if (H.Size <= el_hist_size) {
if (H.Size < el_hist_size) {
H.Lines[H.Size++] = s;
} else {
free(H.Lines[0]);
for (i = 0; i < el_hist_size; i++)
for (i = 0; i < el_hist_size - 1; i++)
H.Lines[i] = H.Lines[i + 1];
H.Lines[i] = s;
}
@ -1282,7 +1150,7 @@ static char *read_redirected(void)
int size = MEM_INC;
char *p;
char *line;
const char *end;
char *end;
p = line = malloc(sizeof(char) * size);
if (!p)
@ -1294,12 +1162,9 @@ static char *read_redirected(void)
int oldpos = end - line;
size += MEM_INC;
p = realloc(line, sizeof(char) * size);
if (!p) {
free(line);
p = line = realloc(line, sizeof(char) * size);
if (!p)
return NULL;
}
line = p;
end = p + size;
p += oldpos; /* Continue where we left off... */
@ -1364,28 +1229,10 @@ void rl_reset_terminal(const char *terminal_name)
}
}
void rl_set_prompt(const char *prompt)
{
if (prompt)
rl_prompt = prompt;
prompt_len = strlen(rl_prompt);
}
void rl_save_prompt(void)
{
rl_saved_prompt = rl_prompt;
}
void rl_restore_prompt(void)
{
if (rl_saved_prompt)
rl_set_prompt(rl_saved_prompt);
}
void rl_initialize(void)
{
if (!rl_prompt)
rl_set_prompt("? ");
rl_prompt = "? ";
hist_alloc();
@ -1404,7 +1251,7 @@ void rl_uninitialize(void)
/* Uninitialize the history */
if (H.Lines) {
for (i = 0; i <= el_hist_size; i++) {
for (i = 0; i < el_hist_size; i++) {
if (H.Lines[i])
free(H.Lines[i]);
H.Lines[i] = NULL;
@ -1426,14 +1273,31 @@ void rl_uninitialize(void)
Length = 0;
}
static const char *rl_saved_prompt = NULL;
void rl_save_prompt(void)
{
rl_saved_prompt = rl_prompt;
}
void rl_restore_prompt(void)
{
if (rl_saved_prompt)
rl_prompt = rl_saved_prompt;
}
void rl_set_prompt(const char *prompt)
{
rl_prompt = prompt;
}
void rl_clear_message(void)
{
/* Nothing to do atm. */
}
void rl_forced_update_display(void)
void rl_forced_update_display()
{
redisplay(0);
redisplay();
tty_flush();
}
@ -1456,8 +1320,7 @@ static int el_prep(const char *prompt)
if (!Screen)
return -1;
rl_set_prompt(prompt);
rl_prompt = prompt ? prompt : NILSTR;
if (el_no_echo) {
int old = el_no_echo;
@ -1619,11 +1482,8 @@ int read_history(const char *filename)
char buf[SCREEN_INC];
hist_alloc();
fp = fopen(filename, "r");
if (!fp)
return EOF;
if (fp) {
H.Size = 0;
while (H.Size < el_hist_size) {
if (!fgets(buf, SCREEN_INC, fp))
@ -1636,16 +1496,17 @@ int read_history(const char *filename)
return fclose(fp);
}
return errno;
}
int write_history(const char *filename)
{
FILE *fp;
int i = 0;
hist_alloc();
fp = fopen(filename, "w");
if (!fp)
return EOF;
if (fp) {
int i = 0;
while (i < H.Size)
fprintf(fp, "%s\n", H.Lines[i++]);
@ -1653,6 +1514,10 @@ int write_history(const char *filename)
return fclose(fp);
}
return errno;
}
/*
** Move back to the beginning of the current word and return an
** allocated copy of it.
@ -1773,6 +1638,14 @@ static el_status_t accept_line(void)
return CSdone;
}
#ifdef SYSTEM_IS_WIN32
static el_status_t end_of_input(void)
{
rl_line_buffer[rl_end] = '\0';
return CSeof;
}
#endif
static el_status_t transpose(void)
{
char c;
@ -1895,14 +1768,14 @@ static int argify(char *line, char ***avp)
if (!p)
return 0;
for (c = line; isspace((unsigned char)(*c)); c++)
for (c = line; isspace(*c); c++)
continue;
if (*c == '\n' || *c == '\0')
return 0;
for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
if (!isspace((unsigned char)(*c))) {
if (!isspace(*c)) {
c++;
continue;
}
@ -1970,7 +1843,7 @@ static el_keymap_t Map[64] = {
{ CTL('I'), c_complete },
{ CTL('J'), accept_line },
{ CTL('K'), kill_line },
{ CTL('L'), refresh },
{ CTL('L'), redisplay },
{ CTL('M'), accept_line },
{ CTL('N'), h_next },
{ CTL('O'), el_ring_bell },
@ -1984,7 +1857,11 @@ static el_keymap_t Map[64] = {
{ CTL('W'), bk_kill_word },
{ CTL('X'), exchange },
{ CTL('Y'), yank },
#ifdef SYSTEM_IS_WIN32
{ CTL('Z'), end_of_input },
#else
{ CTL('Z'), el_ring_bell },
#endif
{ CTL('['), meta },
{ CTL(']'), move_to_char },
{ CTL('^'), el_ring_bell },
@ -2016,7 +1893,7 @@ static size_t find_key_in_map(int key, el_keymap_t map[], size_t mapsz)
{
size_t i;
for (i = 0; i < mapsz && map[i].Function; i++) {
for (i = 0; map[i].Function && i < mapsz; i++) {
if (map[i].Key == key)
return i;
}
@ -2056,19 +1933,12 @@ static el_status_t el_bind_key_in_map(int key, el_keymap_func_t function, el_key
el_status_t el_bind_key(int key, el_keymap_func_t function)
{
return el_bind_key_in_map(key, function, Map, NELEMS(Map));
return el_bind_key_in_map(key, function, Map, ARRAY_ELEMENTS(Map));
}
el_status_t el_bind_key_in_metamap(int key, el_keymap_func_t function)
{
return el_bind_key_in_map(key, function, MetaMap, NELEMS(MetaMap));
}
rl_getc_func_t *rl_set_getc_func(rl_getc_func_t *func)
{
rl_getc_func_t *old = rl_getc_function;
rl_getc_function = func;
return old;
return el_bind_key_in_map(key, function, MetaMap, ARRAY_ELEMENTS(MetaMap));
}
/**

View File

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