blob: 8409bb267ad3eae816b7faf974d4c803f49c6668 [file] [log] [blame]
Erik Andersenc7c634b2000-03-19 05:28:55 +00001/* vi: set sw=4 ts=4: */
Erik Andersen13456d12000-03-16 08:09:57 +00002/*
Eric Andersenaff114c2004-04-14 17:51:38 +00003 * Termios command line History and Editing.
Erik Andersen13456d12000-03-16 08:09:57 +00004 *
Eric Andersen81fe1232003-07-29 06:38:40 +00005 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
Eric Andersen7467c8d2001-07-12 20:26:32 +00006 * Written by: Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersen5f2c79d2001-02-16 18:36:04 +00007 *
8 * Used ideas:
9 * Adam Rogoyski <rogoyski@cs.utexas.edu>
10 * Dave Cinege <dcinege@psychosis.com>
11 * Jakub Jelinek (c) 1995
Eric Andersen81fe1232003-07-29 06:38:40 +000012 * Erik Andersen <andersen@codepoet.org> (Majorly adjusted for busybox)
Eric Andersen5f2c79d2001-02-16 18:36:04 +000013 *
Erik Andersen13456d12000-03-16 08:09:57 +000014 * This code is 'as is' with no warranty.
Erik Andersen13456d12000-03-16 08:09:57 +000015 */
16
17/*
Denis Vlasenko7f1dc212006-12-19 01:10:25 +000018 Usage and known bugs:
Erik Andersen13456d12000-03-16 08:09:57 +000019 Terminal key codes are not extensive, and more will probably
20 need to be added. This version was created on Debian GNU/Linux 2.x.
21 Delete, Backspace, Home, End, and the arrow keys were tested
22 to work in an Xterm and console. Ctrl-A also works as Home.
Mark Whitley4e338752001-01-26 20:42:23 +000023 Ctrl-E also works as End.
Erik Andersen13456d12000-03-16 08:09:57 +000024
Eric Andersen5f2c79d2001-02-16 18:36:04 +000025 Small bugs (simple effect):
26 - not true viewing if terminal size (x*y symbols) less
27 size (prompt + editor`s line + 2 symbols)
Eric Andersenb3d6e2d2001-03-13 22:57:56 +000028 - not true viewing if length prompt less terminal width
Erik Andersen13456d12000-03-16 08:09:57 +000029 */
30
Bernhard Reutner-Fischere15d7572006-06-02 20:56:16 +000031#include "busybox.h"
Eric Andersencbe31da2001-02-20 06:14:08 +000032#include <sys/ioctl.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000033
"Robert P. J. Day"4eddb422006-07-03 00:46:47 +000034#include "cmdedit.h"
Glenn L McGrath67285962004-01-14 09:34:51 +000035
Glenn L McGrath475820c2004-01-22 12:42:23 +000036
Denis Vlasenko7f1dc212006-12-19 01:10:25 +000037#if ENABLE_LOCALE_SUPPORT
38#define Isprint(c) isprint(c)
Eric Andersene5dfced2001-04-09 22:48:12 +000039#else
Denis Vlasenko7f1dc212006-12-19 01:10:25 +000040#define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233'))
Eric Andersene5dfced2001-04-09 22:48:12 +000041#endif
42
Denis Vlasenko7f1dc212006-12-19 01:10:25 +000043
44/* FIXME: obsolete CONFIG item? */
45#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
46
47
Glenn L McGrath3b251852004-01-03 12:07:32 +000048#ifdef TEST
Eric Andersen5f2c79d2001-02-16 18:36:04 +000049
Denis Vlasenko7f1dc212006-12-19 01:10:25 +000050#define ENABLE_FEATURE_COMMAND_EDITING 0
51#define ENABLE_FEATURE_COMMAND_TAB_COMPLETION 0
52#define ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION 0
53#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
54#define ENABLE_FEATURE_CLEAN_UP 0
Eric Andersen5f2c79d2001-02-16 18:36:04 +000055
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +000056#endif /* TEST */
Eric Andersen5f2c79d2001-02-16 18:36:04 +000057
Eric Andersen5165fbe2001-02-20 06:42:29 +000058
Denis Vlasenko7f1dc212006-12-19 01:10:25 +000059#if ENABLE_FEATURE_COMMAND_EDITING
Erik Andersen13456d12000-03-16 08:09:57 +000060
Denis Vlasenko7f1dc212006-12-19 01:10:25 +000061#if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION || ENABLE_FEATURE_SH_FANCY_PROMPT
62#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR 1
Eric Andersen5f2c79d2001-02-16 18:36:04 +000063#endif
64
Eric Andersen5f2c79d2001-02-16 18:36:04 +000065/* Maximum length of the linked list for the command line history */
Denis Vlasenko7f1dc212006-12-19 01:10:25 +000066#if !ENABLE_FEATURE_COMMAND_HISTORY
Robert Griebl350d26b2002-12-03 22:45:46 +000067#define MAX_HISTORY 15
68#else
Denis Vlasenko9d4533e2006-11-02 22:09:37 +000069#define MAX_HISTORY (CONFIG_FEATURE_COMMAND_HISTORY + 0)
Robert Griebl350d26b2002-12-03 22:45:46 +000070#endif
71
Denis Vlasenko9d4533e2006-11-02 22:09:37 +000072#if MAX_HISTORY > 0
Glenn L McGrath062c74f2002-11-27 09:29:49 +000073static char *history[MAX_HISTORY+1]; /* history + current */
74/* saved history lines */
75static int n_history;
76/* current pointer to history line */
77static int cur_history;
78#endif
Erik Andersen1d1d9502000-04-21 01:26:49 +000079
Denis Vlasenko7f1dc212006-12-19 01:10:25 +000080//#include <termios.h>
Glenn L McGrath78b0e372001-06-26 02:06:08 +000081#define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
82#define getTermSettings(fd,argp) tcgetattr(fd, argp);
Erik Andersen1d1d9502000-04-21 01:26:49 +000083
84/* Current termio and the previous termio before starting sh */
Eric Andersen63a86222000-11-07 06:52:13 +000085static struct termios initial_settings, new_settings;
Erik Andersen8ea7d8c2000-05-20 00:40:08 +000086
87
Mark Whitley4e338752001-01-26 20:42:23 +000088static
Eric Andersenc470f442003-07-28 09:56:35 +000089volatile int cmdedit_termw = 80; /* actual terminal width */
Mark Whitley4e338752001-01-26 20:42:23 +000090static
Eric Andersenc470f442003-07-28 09:56:35 +000091volatile int handlers_sets = 0; /* Set next bites: */
Eric Andersen5f2c79d2001-02-16 18:36:04 +000092
Mark Whitley4e338752001-01-26 20:42:23 +000093enum {
Eric Andersenc470f442003-07-28 09:56:35 +000094 SET_ATEXIT = 1, /* when atexit() has been called
Eric Andersenb3d6e2d2001-03-13 22:57:56 +000095 and get euid,uid,gid to fast compare */
Eric Andersen7467c8d2001-07-12 20:26:32 +000096 SET_WCHG_HANDLERS = 2, /* winchg signal handler */
97 SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */
Mark Whitley4e338752001-01-26 20:42:23 +000098};
Erik Andersen13456d12000-03-16 08:09:57 +000099
Mark Whitley4e338752001-01-26 20:42:23 +0000100
Eric Andersenc470f442003-07-28 09:56:35 +0000101static int cmdedit_x; /* real x terminal position */
102static int cmdedit_y; /* pseudoreal y terminal position */
103static int cmdedit_prmt_len; /* lenght prompt without colores string */
Eric Andersen86349772000-12-18 20:25:50 +0000104
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000105static int cursor; /* required globals for signal handler */
106static int len; /* --- "" - - "" -- -"- --""-- --""--- */
107static char *command_ps; /* --- "" - - "" -- -"- --""-- --""--- */
108static SKIP_FEATURE_SH_FANCY_PROMPT(const) char *cmdedit_prompt; /* -- */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000109
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000110#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000111static char *user_buf = "";
112static char *home_pwd_buf = "";
113static int my_euid;
114#endif
115
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000116#if ENABLE_FEATURE_SH_FANCY_PROMPT
Glenn L McGrath062c74f2002-11-27 09:29:49 +0000117static char *hostname_buf;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000118static int num_ok_lines = 1;
119#endif
120
121
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000122#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000123
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000124#if !ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000125static int my_euid;
126#endif
127
128static int my_uid;
129static int my_gid;
130
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000131#endif /* FEATURE_COMMAND_TAB_COMPLETION */
Eric Andersenc470f442003-07-28 09:56:35 +0000132
Mark Whitley4e338752001-01-26 20:42:23 +0000133static void cmdedit_setwidth(int w, int redraw_flg);
Erik Andersen13456d12000-03-16 08:09:57 +0000134
Mark Whitley4e338752001-01-26 20:42:23 +0000135static void win_changed(int nsig)
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000136{
Eric Andersenc470f442003-07-28 09:56:35 +0000137 static sighandler_t previous_SIGWINCH_handler; /* for reset */
Erik Andersen61677fe2000-04-13 01:18:56 +0000138
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000139 /* emulate || signal call */
140 if (nsig == -SIGWINCH || nsig == SIGWINCH) {
Eric Andersen8efe9672003-09-15 08:33:45 +0000141 int width = 0;
142 get_terminal_width_height(0, &width, NULL);
143 cmdedit_setwidth(width, nsig == SIGWINCH);
Mark Whitley4e338752001-01-26 20:42:23 +0000144 }
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000145 /* Unix not all standard in recall signal */
Mark Whitley4e338752001-01-26 20:42:23 +0000146
Eric Andersenc470f442003-07-28 09:56:35 +0000147 if (nsig == -SIGWINCH) /* save previous handler */
Mark Whitley4e338752001-01-26 20:42:23 +0000148 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
Eric Andersenc470f442003-07-28 09:56:35 +0000149 else if (nsig == SIGWINCH) /* signaled called handler */
150 signal(SIGWINCH, win_changed); /* set for next call */
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000151 else /* nsig == 0 */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000152 /* set previous handler */
Eric Andersenc470f442003-07-28 09:56:35 +0000153 signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
Mark Whitley4e338752001-01-26 20:42:23 +0000154}
Eric Andersenb3dc3b82001-01-04 11:08:45 +0000155
156static void cmdedit_reset_term(void)
Erik Andersen13456d12000-03-16 08:09:57 +0000157{
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000158 if ((handlers_sets & SET_RESET_TERM) != 0) {
Eric Andersene5dfced2001-04-09 22:48:12 +0000159/* sparc and other have broken termios support: use old termio handling. */
Eric Andersen70060d22004-03-27 10:02:48 +0000160 setTermSettings(STDIN_FILENO, (void *) &initial_settings);
Mark Whitley4e338752001-01-26 20:42:23 +0000161 handlers_sets &= ~SET_RESET_TERM;
162 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000163 if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
Mark Whitley4e338752001-01-26 20:42:23 +0000164 /* reset SIGWINCH handler to previous (default) */
165 win_changed(0);
166 handlers_sets &= ~SET_WCHG_HANDLERS;
167 }
168 fflush(stdout);
Erik Andersen13456d12000-03-16 08:09:57 +0000169}
170
Mark Whitley4e338752001-01-26 20:42:23 +0000171
Mark Whitley4e338752001-01-26 20:42:23 +0000172/* special for recount position for scroll and remove terminal margin effect */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000173static void cmdedit_set_out_char(int next_char)
174{
175
Eric Andersenf9ff8a72001-03-15 20:51:09 +0000176 int c = (int)((unsigned char) command_ps[cursor]);
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000177
178 if (c == 0)
Eric Andersenc470f442003-07-28 09:56:35 +0000179 c = ' '; /* destroy end char? */
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000180#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
Eric Andersenc470f442003-07-28 09:56:35 +0000181 if (!Isprint(c)) { /* Inverse put non-printable characters */
Eric Andersenf9ff8a72001-03-15 20:51:09 +0000182 if (c >= 128)
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000183 c -= 128;
Eric Andersenf9ff8a72001-03-15 20:51:09 +0000184 if (c < ' ')
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000185 c += '@';
186 if (c == 127)
187 c = '?';
188 printf("\033[7m%c\033[0m", c);
189 } else
190#endif
Rob Landleyd921b2e2006-08-03 15:41:12 +0000191 if (initial_settings.c_lflag & ECHO) putchar(c);
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000192 if (++cmdedit_x >= cmdedit_termw) {
Mark Whitley4e338752001-01-26 20:42:23 +0000193 /* terminal is scrolled down */
194 cmdedit_y++;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000195 cmdedit_x = 0;
Mark Whitley4e338752001-01-26 20:42:23 +0000196
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000197 if (!next_char)
Mark Whitley4e338752001-01-26 20:42:23 +0000198 next_char = ' ';
199 /* destroy "(auto)margin" */
200 putchar(next_char);
201 putchar('\b');
202 }
203 cursor++;
Erik Andersen13456d12000-03-16 08:09:57 +0000204}
205
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000206/* Move to end line. Bonus: rewrite line from cursor */
207static void input_end(void)
208{
209 while (cursor < len)
210 cmdedit_set_out_char(0);
Mark Whitley4e338752001-01-26 20:42:23 +0000211}
212
213/* Go to the next line */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000214static void goto_new_line(void)
215{
Mark Whitley4e338752001-01-26 20:42:23 +0000216 input_end();
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000217 if (cmdedit_x)
218 putchar('\n');
Mark Whitley4e338752001-01-26 20:42:23 +0000219}
220
221
Rob Landley88621d72006-08-29 19:41:06 +0000222static void out1str(const char *s)
Erik Andersenf0657d32000-04-12 17:49:52 +0000223{
Robert Grieblb2301592002-07-30 23:13:51 +0000224 if ( s )
225 fputs(s, stdout);
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000226}
Eric Andersen81fe1232003-07-29 06:38:40 +0000227
Rob Landley88621d72006-08-29 19:41:06 +0000228static void beep(void)
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000229{
230 putchar('\007');
Erik Andersen13456d12000-03-16 08:09:57 +0000231}
232
Eric Andersenaff114c2004-04-14 17:51:38 +0000233/* Move back one character */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000234/* special for slow terminal */
235static void input_backward(int num)
236{
237 if (num > cursor)
238 num = cursor;
Eric Andersenc470f442003-07-28 09:56:35 +0000239 cursor -= num; /* new cursor (in command, not terminal) */
Erik Andersen13456d12000-03-16 08:09:57 +0000240
Eric Andersenc470f442003-07-28 09:56:35 +0000241 if (cmdedit_x >= num) { /* no to up line */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000242 cmdedit_x -= num;
243 if (num < 4)
244 while (num-- > 0)
245 putchar('\b');
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000246 else
247 printf("\033[%dD", num);
248 } else {
249 int count_y;
250
251 if (cmdedit_x) {
Eric Andersenc470f442003-07-28 09:56:35 +0000252 putchar('\r'); /* back to first terminal pos. */
253 num -= cmdedit_x; /* set previous backward */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000254 }
255 count_y = 1 + num / cmdedit_termw;
256 printf("\033[%dA", count_y);
257 cmdedit_y -= count_y;
258 /* require forward after uping */
259 cmdedit_x = cmdedit_termw * count_y - num;
Eric Andersenc470f442003-07-28 09:56:35 +0000260 printf("\033[%dC", cmdedit_x); /* set term cursor */
Erik Andersen13456d12000-03-16 08:09:57 +0000261 }
Erik Andersen13456d12000-03-16 08:09:57 +0000262}
263
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000264static void put_prompt(void)
265{
266 out1str(cmdedit_prompt);
Eric Andersenc470f442003-07-28 09:56:35 +0000267 cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000268 cursor = 0;
Eric Andersen7467c8d2001-07-12 20:26:32 +0000269 cmdedit_y = 0; /* new quasireal y */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000270}
271
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000272#if !ENABLE_FEATURE_SH_FANCY_PROMPT
Eric Andersenb3d6e2d2001-03-13 22:57:56 +0000273static void parse_prompt(const char *prmt_ptr)
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000274{
Eric Andersenb3d6e2d2001-03-13 22:57:56 +0000275 cmdedit_prompt = prmt_ptr;
276 cmdedit_prmt_len = strlen(prmt_ptr);
277 put_prompt();
278}
279#else
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000280static void parse_prompt(const char *prmt_ptr)
281{
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000282 int prmt_len = 0;
"Vladimir N. Oleynik"f0874802005-09-05 15:46:26 +0000283 size_t cur_prmt_len = 0;
Eric Andersene5dfced2001-04-09 22:48:12 +0000284 char flg_not_length = '[';
Rob Landley081e3842006-08-03 20:07:35 +0000285 char *prmt_mem_ptr = xzalloc(1);
Eric Andersene5dfced2001-04-09 22:48:12 +0000286 char *pwd_buf = xgetcwd(0);
287 char buf2[PATH_MAX + 1];
288 char buf[2];
289 char c;
290 char *pbuf;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000291
Eric Andersen5f265b72001-05-11 16:58:46 +0000292 if (!pwd_buf) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000293 pwd_buf=(char *)bb_msg_unknown;
Eric Andersen5f265b72001-05-11 16:58:46 +0000294 }
295
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000296 while (*prmt_ptr) {
Eric Andersene5dfced2001-04-09 22:48:12 +0000297 pbuf = buf;
298 pbuf[1] = 0;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000299 c = *prmt_ptr++;
300 if (c == '\\') {
Eric Andersene5dfced2001-04-09 22:48:12 +0000301 const char *cp = prmt_ptr;
302 int l;
Eric Andersenc470f442003-07-28 09:56:35 +0000303
Manuel Novoa III cad53642003-03-19 09:13:01 +0000304 c = bb_process_escape_sequence(&prmt_ptr);
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000305 if (prmt_ptr==cp) {
Eric Andersene5dfced2001-04-09 22:48:12 +0000306 if (*cp == 0)
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000307 break;
Eric Andersene5dfced2001-04-09 22:48:12 +0000308 c = *prmt_ptr++;
309 switch (c) {
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000310#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
Eric Andersene5dfced2001-04-09 22:48:12 +0000311 case 'u':
312 pbuf = user_buf;
313 break;
Eric Andersenc470f442003-07-28 09:56:35 +0000314#endif
Eric Andersene5dfced2001-04-09 22:48:12 +0000315 case 'h':
316 pbuf = hostname_buf;
Glenn L McGrath062c74f2002-11-27 09:29:49 +0000317 if (pbuf == 0) {
Rob Landley081e3842006-08-03 20:07:35 +0000318 pbuf = xzalloc(256);
Eric Andersene5dfced2001-04-09 22:48:12 +0000319 if (gethostname(pbuf, 255) < 0) {
320 strcpy(pbuf, "?");
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000321 } else {
Eric Andersene5dfced2001-04-09 22:48:12 +0000322 char *s = strchr(pbuf, '.');
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000323
324 if (s)
325 *s = 0;
326 }
Eric Andersene5dfced2001-04-09 22:48:12 +0000327 hostname_buf = pbuf;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000328 }
Eric Andersene5dfced2001-04-09 22:48:12 +0000329 break;
330 case '$':
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000331 c = my_euid == 0 ? '#' : '$';
332 break;
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000333#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
Eric Andersene5dfced2001-04-09 22:48:12 +0000334 case 'w':
335 pbuf = pwd_buf;
336 l = strlen(home_pwd_buf);
337 if (home_pwd_buf[0] != 0 &&
338 strncmp(home_pwd_buf, pbuf, l) == 0 &&
339 (pbuf[l]=='/' || pbuf[l]=='\0') &&
340 strlen(pwd_buf+l)<PATH_MAX) {
341 pbuf = buf2;
342 *pbuf = '~';
343 strcpy(pbuf+1, pwd_buf+l);
Denis Vlasenko92758142006-10-03 19:56:34 +0000344 }
Eric Andersene5dfced2001-04-09 22:48:12 +0000345 break;
Eric Andersenc470f442003-07-28 09:56:35 +0000346#endif
Eric Andersene5dfced2001-04-09 22:48:12 +0000347 case 'W':
348 pbuf = pwd_buf;
349 cp = strrchr(pbuf,'/');
350 if ( (cp != NULL) && (cp != pbuf) )
351 pbuf += (cp-pbuf)+1;
352 break;
353 case '!':
354 snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
355 break;
356 case 'e': case 'E': /* \e \E = \033 */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000357 c = '\033';
358 break;
Eric Andersenc470f442003-07-28 09:56:35 +0000359 case 'x': case 'X':
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000360 for (l = 0; l < 3;) {
Eric Andersene5dfced2001-04-09 22:48:12 +0000361 int h;
362 buf2[l++] = *prmt_ptr;
363 buf2[l] = 0;
364 h = strtol(buf2, &pbuf, 16);
365 if (h > UCHAR_MAX || (pbuf - buf2) < l) {
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000366 l--;
367 break;
368 }
369 prmt_ptr++;
370 }
Eric Andersene5dfced2001-04-09 22:48:12 +0000371 buf2[l] = 0;
372 c = (char)strtol(buf2, 0, 16);
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000373 if (c==0)
Eric Andersene5dfced2001-04-09 22:48:12 +0000374 c = '?';
375 pbuf = buf;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000376 break;
Eric Andersene5dfced2001-04-09 22:48:12 +0000377 case '[': case ']':
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000378 if (c == flg_not_length) {
379 flg_not_length = flg_not_length == '[' ? ']' : '[';
380 continue;
381 }
382 break;
Eric Andersene5dfced2001-04-09 22:48:12 +0000383 }
Eric Andersenc470f442003-07-28 09:56:35 +0000384 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000385 }
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000386 if (pbuf == buf)
Eric Andersene5dfced2001-04-09 22:48:12 +0000387 *pbuf = c;
"Vladimir N. Oleynik"f0874802005-09-05 15:46:26 +0000388 cur_prmt_len = strlen(pbuf);
389 prmt_len += cur_prmt_len;
390 if (flg_not_length != ']')
391 cmdedit_prmt_len += cur_prmt_len;
Eric Andersene5dfced2001-04-09 22:48:12 +0000392 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000393 }
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000394 if (pwd_buf!=(char *)bb_msg_unknown)
Eric Andersen8f697842001-07-02 15:36:57 +0000395 free(pwd_buf);
Eric Andersenb3d6e2d2001-03-13 22:57:56 +0000396 cmdedit_prompt = prmt_mem_ptr;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000397 put_prompt();
398}
Eric Andersenb3d6e2d2001-03-13 22:57:56 +0000399#endif
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000400
401
Eric Andersenaff114c2004-04-14 17:51:38 +0000402/* draw prompt, editor line, and clear tail */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000403static void redraw(int y, int back_cursor)
404{
Eric Andersenc470f442003-07-28 09:56:35 +0000405 if (y > 0) /* up to start y */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000406 printf("\033[%dA", y);
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000407 putchar('\r');
408 put_prompt();
Eric Andersenc470f442003-07-28 09:56:35 +0000409 input_end(); /* rewrite */
410 printf("\033[J"); /* destroy tail after cursor */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000411 input_backward(back_cursor);
412}
413
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000414#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox0f2dd9f2006-03-07 20:26:11 +0000415#define DELBUFSIZ 128
416static char *delbuf; /* a (malloced) place to store deleted characters */
417static char *delp;
418static char newdelflag; /* whether delbuf should be reused yet */
Paul Fox3f11b1b2005-08-04 19:04:46 +0000419#endif
420
421/* Delete the char in front of the cursor, optionally saving it
422 * for later putback */
423static void input_delete(int save)
Erik Andersenf0657d32000-04-12 17:49:52 +0000424{
Mark Whitley4e338752001-01-26 20:42:23 +0000425 int j = cursor;
Erik Andersena2685732000-04-09 18:27:46 +0000426
Mark Whitley4e338752001-01-26 20:42:23 +0000427 if (j == len)
Erik Andersenf0657d32000-04-12 17:49:52 +0000428 return;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000429
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000430#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox3f11b1b2005-08-04 19:04:46 +0000431 if (save) {
432 if (newdelflag) {
Paul Fox0f2dd9f2006-03-07 20:26:11 +0000433 if (!delbuf)
434 delbuf = malloc(DELBUFSIZ);
435 /* safe if malloc fails */
Paul Fox3f11b1b2005-08-04 19:04:46 +0000436 delp = delbuf;
437 newdelflag = 0;
438 }
Paul Fox0f2dd9f2006-03-07 20:26:11 +0000439 if (delbuf && (delp - delbuf < DELBUFSIZ))
Paul Fox3f11b1b2005-08-04 19:04:46 +0000440 *delp++ = command_ps[j];
441 }
442#endif
443
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000444 strcpy(command_ps + j, command_ps + j + 1);
Mark Whitley4e338752001-01-26 20:42:23 +0000445 len--;
Paul Fox3f11b1b2005-08-04 19:04:46 +0000446 input_end(); /* rewrite new line */
Eric Andersenc470f442003-07-28 09:56:35 +0000447 cmdedit_set_out_char(0); /* destroy end char */
448 input_backward(cursor - j); /* back to old pos cursor */
Erik Andersenf0657d32000-04-12 17:49:52 +0000449}
450
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000451#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox3f11b1b2005-08-04 19:04:46 +0000452static void put(void)
453{
454 int ocursor, j = delp - delbuf;
455 if (j == 0)
456 return;
457 ocursor = cursor;
458 /* open hole and then fill it */
459 memmove(command_ps + cursor + j, command_ps + cursor, len - cursor + 1);
460 strncpy(command_ps + cursor, delbuf, j);
461 len += j;
462 input_end(); /* rewrite new line */
463 input_backward(cursor-ocursor-j+1); /* at end of new text */
464}
465#endif
466
Mark Whitley4e338752001-01-26 20:42:23 +0000467/* Delete the char in back of the cursor */
468static void input_backspace(void)
469{
470 if (cursor > 0) {
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000471 input_backward(1);
Paul Fox3f11b1b2005-08-04 19:04:46 +0000472 input_delete(0);
Mark Whitley4e338752001-01-26 20:42:23 +0000473 }
474}
475
476
Eric Andersenaff114c2004-04-14 17:51:38 +0000477/* Move forward one character */
Mark Whitley4e338752001-01-26 20:42:23 +0000478static void input_forward(void)
Erik Andersenf0657d32000-04-12 17:49:52 +0000479{
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000480 if (cursor < len)
481 cmdedit_set_out_char(command_ps[cursor + 1]);
Erik Andersenf0657d32000-04-12 17:49:52 +0000482}
483
Mark Whitley4e338752001-01-26 20:42:23 +0000484static void cmdedit_setwidth(int w, int redraw_flg)
485{
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000486 cmdedit_termw = cmdedit_prmt_len + 2;
Eric Andersen6faae7d2001-02-16 20:09:17 +0000487 if (w <= cmdedit_termw) {
488 cmdedit_termw = cmdedit_termw % w;
489 }
Mark Whitley4e338752001-01-26 20:42:23 +0000490 if (w > cmdedit_termw) {
Mark Whitley4e338752001-01-26 20:42:23 +0000491 cmdedit_termw = w;
492
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000493 if (redraw_flg) {
494 /* new y for current cursor */
495 int new_y = (cursor + cmdedit_prmt_len) / w;
Mark Whitley4e338752001-01-26 20:42:23 +0000496
497 /* redraw */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000498 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
499 fflush(stdout);
Mark Whitley4e338752001-01-26 20:42:23 +0000500 }
Eric Andersenc470f442003-07-28 09:56:35 +0000501 }
Mark Whitley4e338752001-01-26 20:42:23 +0000502}
503
Eric Andersen7467c8d2001-07-12 20:26:32 +0000504static void cmdedit_init(void)
Mark Whitley4e338752001-01-26 20:42:23 +0000505{
Eric Andersen61173a52001-03-19 17:48:55 +0000506 cmdedit_prmt_len = 0;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000507 if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
508 /* emulate usage handler to set handler and call yours work */
Mark Whitley4e338752001-01-26 20:42:23 +0000509 win_changed(-SIGWINCH);
510 handlers_sets |= SET_WCHG_HANDLERS;
511 }
512
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000513 if ((handlers_sets & SET_ATEXIT) == 0) {
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000514#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000515 struct passwd *entry;
516
517 my_euid = geteuid();
518 entry = getpwuid(my_euid);
519 if (entry) {
Rob Landleyd921b2e2006-08-03 15:41:12 +0000520 user_buf = xstrdup(entry->pw_name);
521 home_pwd_buf = xstrdup(entry->pw_dir);
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000522 }
523#endif
524
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000525#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000526
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000527#if !ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000528 my_euid = geteuid();
529#endif
530 my_uid = getuid();
531 my_gid = getgid();
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000532#endif /* FEATURE_COMMAND_TAB_COMPLETION */
Mark Whitley4e338752001-01-26 20:42:23 +0000533 handlers_sets |= SET_ATEXIT;
Eric Andersenc470f442003-07-28 09:56:35 +0000534 atexit(cmdedit_reset_term); /* be sure to do this only once */
Mark Whitley4e338752001-01-26 20:42:23 +0000535 }
Mark Whitley4e338752001-01-26 20:42:23 +0000536}
Erik Andersenf0657d32000-04-12 17:49:52 +0000537
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000538#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
Mark Whitley4e338752001-01-26 20:42:23 +0000539
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000540static char **matches;
541static int num_matches;
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000542
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000543static void add_match(char *matched)
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000544{
545 int nm = num_matches;
546 int nm1 = nm + 1;
547
548 matches = xrealloc(matches, nm1 * sizeof(char *));
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000549 matches[nm] = matched;
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000550 num_matches++;
551}
552
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000553/*
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000554static int is_execute(const struct stat *st)
Erik Andersen6273f652000-03-17 01:12:41 +0000555{
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000556 if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
557 (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
558 (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
559 (st->st_mode & S_IXOTH)) return TRUE;
560 return FALSE;
Erik Andersen6273f652000-03-17 01:12:41 +0000561}
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000562*/
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000563
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000564#if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000565
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000566static void username_tab_completion(char *ud, char *with_shash_flg)
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000567{
568 struct passwd *entry;
569 int userlen;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000570
Eric Andersenc470f442003-07-28 09:56:35 +0000571 ud++; /* ~user/... to user/... */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000572 userlen = strlen(ud);
573
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000574 if (with_shash_flg) { /* "~/..." or "~user/..." */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000575 char *sav_ud = ud - 1;
576 char *home = 0;
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000577 char *temp;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000578
Eric Andersenc470f442003-07-28 09:56:35 +0000579 if (*ud == '/') { /* "~/..." */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000580 home = home_pwd_buf;
581 } else {
582 /* "~user/..." */
583 temp = strchr(ud, '/');
Eric Andersenc470f442003-07-28 09:56:35 +0000584 *temp = 0; /* ~user\0 */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000585 entry = getpwnam(ud);
Eric Andersenc470f442003-07-28 09:56:35 +0000586 *temp = '/'; /* restore ~user/... */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000587 ud = temp;
588 if (entry)
589 home = entry->pw_dir;
590 }
591 if (home) {
592 if ((userlen + strlen(home) + 1) < BUFSIZ) {
Eric Andersenc470f442003-07-28 09:56:35 +0000593 char temp2[BUFSIZ]; /* argument size */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000594
595 /* /home/user/... */
596 sprintf(temp2, "%s%s", home, ud);
597 strcpy(sav_ud, temp2);
598 }
599 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000600 } else {
601 /* "~[^/]*" */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000602 setpwent();
603
604 while ((entry = getpwent()) != NULL) {
605 /* Null usernames should result in all users as possible completions. */
606 if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000607 add_match(xasprintf("~%s/", entry->pw_name));
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000608 }
609 }
610
611 endpwent();
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000612 }
613}
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000614#endif /* FEATURE_COMMAND_USERNAME_COMPLETION */
Mark Whitley4e338752001-01-26 20:42:23 +0000615
616enum {
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000617 FIND_EXE_ONLY = 0,
618 FIND_DIR_ONLY = 1,
Mark Whitley4e338752001-01-26 20:42:23 +0000619 FIND_FILE_ONLY = 2,
620};
Erik Andersen1dbe3402000-03-19 10:46:06 +0000621
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000622#if ENABLE_ASH
Glenn L McGrath67285962004-01-14 09:34:51 +0000623const char *cmdedit_path_lookup;
624#else
625#define cmdedit_path_lookup getenv("PATH")
626#endif
627
Mark Whitley4e338752001-01-26 20:42:23 +0000628static int path_parse(char ***p, int flags)
629{
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000630 int npth;
Glenn L McGrath67285962004-01-14 09:34:51 +0000631 const char *tmp;
632 const char *pth;
Mark Whitley4e338752001-01-26 20:42:23 +0000633
Mark Whitley4e338752001-01-26 20:42:23 +0000634 /* if not setenv PATH variable, to search cur dir "." */
Glenn L McGrath67285962004-01-14 09:34:51 +0000635 if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000636 /* PATH=<empty> or PATH=:<empty> */
637 *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
Mark Whitley4e338752001-01-26 20:42:23 +0000638 return 1;
639 }
640
641 tmp = pth;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000642 npth = 0;
Mark Whitley4e338752001-01-26 20:42:23 +0000643
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000644 for (;;) {
Eric Andersenc470f442003-07-28 09:56:35 +0000645 npth++; /* count words is + 1 count ':' */
Mark Whitley4e338752001-01-26 20:42:23 +0000646 tmp = strchr(tmp, ':');
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000647 if (tmp) {
648 if (*++tmp == 0)
Eric Andersenc470f442003-07-28 09:56:35 +0000649 break; /* :<empty> */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000650 } else
Mark Whitley4e338752001-01-26 20:42:23 +0000651 break;
652 }
653
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000654 *p = xmalloc(npth * sizeof(char *));
Mark Whitley4e338752001-01-26 20:42:23 +0000655
656 tmp = pth;
Rob Landleyd921b2e2006-08-03 15:41:12 +0000657 (*p)[0] = xstrdup(tmp);
Eric Andersenc470f442003-07-28 09:56:35 +0000658 npth = 1; /* count words is + 1 count ':' */
Mark Whitley4e338752001-01-26 20:42:23 +0000659
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000660 for (;;) {
Mark Whitley4e338752001-01-26 20:42:23 +0000661 tmp = strchr(tmp, ':');
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000662 if (tmp) {
Eric Andersenc470f442003-07-28 09:56:35 +0000663 (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000664 if (*++tmp == 0)
Eric Andersenc470f442003-07-28 09:56:35 +0000665 break; /* :<empty> */
Mark Whitley4e338752001-01-26 20:42:23 +0000666 } else
667 break;
Eric Andersenc470f442003-07-28 09:56:35 +0000668 (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
Mark Whitley4e338752001-01-26 20:42:23 +0000669 }
670
671 return npth;
672}
673
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000674static char *add_quote_for_spec_chars(char *found)
Erik Andersen6273f652000-03-17 01:12:41 +0000675{
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000676 int l = 0;
677 char *s = xmalloc((strlen(found) + 1) * 2);
678
679 while (*found) {
680 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
681 s[l++] = '\\';
682 s[l++] = *found++;
683 }
684 s[l] = 0;
685 return s;
686}
687
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000688static void exe_n_cwd_tab_completion(char *command, int type)
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000689{
Erik Andersen1dbe3402000-03-19 10:46:06 +0000690 DIR *dir;
691 struct dirent *next;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000692 char dirbuf[BUFSIZ];
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000693 struct stat st;
694 char *path1[1];
695 char **paths = path1;
696 int npaths;
697 int i;
Eric Andersene5dfced2001-04-09 22:48:12 +0000698 char *found;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000699 char *pfind = strrchr(command, '/');
Mark Whitley4e338752001-01-26 20:42:23 +0000700
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000701 path1[0] = ".";
Mark Whitley4e338752001-01-26 20:42:23 +0000702
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000703 if (pfind == NULL) {
Mark Whitley4e338752001-01-26 20:42:23 +0000704 /* no dir, if flags==EXE_ONLY - get paths, else "." */
705 npaths = path_parse(&paths, type);
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000706 pfind = command;
Mark Whitley4e338752001-01-26 20:42:23 +0000707 } else {
708 /* with dir */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000709 /* save for change */
710 strcpy(dirbuf, command);
711 /* set dir only */
712 dirbuf[(pfind - command) + 1] = 0;
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000713#if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION
Eric Andersenc470f442003-07-28 09:56:35 +0000714 if (dirbuf[0] == '~') /* ~/... or ~user/... */
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000715 username_tab_completion(dirbuf, dirbuf);
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000716#endif
717 /* "strip" dirname in command */
718 pfind++;
Mark Whitley4e338752001-01-26 20:42:23 +0000719
Mark Whitley4e338752001-01-26 20:42:23 +0000720 paths[0] = dirbuf;
Eric Andersenc470f442003-07-28 09:56:35 +0000721 npaths = 1; /* only 1 dir */
Mark Whitley4e338752001-01-26 20:42:23 +0000722 }
Erik Andersenc7c634b2000-03-19 05:28:55 +0000723
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000724 for (i = 0; i < npaths; i++) {
Erik Andersenc7c634b2000-03-19 05:28:55 +0000725
Mark Whitley4e338752001-01-26 20:42:23 +0000726 dir = opendir(paths[i]);
Eric Andersenc470f442003-07-28 09:56:35 +0000727 if (!dir) /* Don't print an error */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000728 continue;
729
730 while ((next = readdir(dir)) != NULL) {
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000731 char *str_found = next->d_name;
732
Mark Whitley4e338752001-01-26 20:42:23 +0000733 /* matched ? */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000734 if (strncmp(str_found, pfind, strlen(pfind)))
Mark Whitley4e338752001-01-26 20:42:23 +0000735 continue;
736 /* not see .name without .match */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000737 if (*str_found == '.' && *pfind == 0) {
738 if (*paths[i] == '/' && paths[i][1] == 0
Eric Andersenc470f442003-07-28 09:56:35 +0000739 && str_found[1] == 0) str_found = ""; /* only "/" */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000740 else
Mark Whitley4e338752001-01-26 20:42:23 +0000741 continue;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000742 }
Eric Andersene5dfced2001-04-09 22:48:12 +0000743 found = concat_path_file(paths[i], str_found);
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000744 /* hmm, remover in progress? */
Eric Andersenc470f442003-07-28 09:56:35 +0000745 if (stat(found, &st) < 0)
Eric Andersene5dfced2001-04-09 22:48:12 +0000746 goto cont;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000747 /* find with dirs ? */
748 if (paths[i] != dirbuf)
Eric Andersenc470f442003-07-28 09:56:35 +0000749 strcpy(found, next->d_name); /* only name */
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000750
751 int len1 = strlen(found);
752 found = xrealloc(found, len1+2);
753 found[len1] = '\0';
754 found[len1+1] = '\0';
755
Mark Whitley4e338752001-01-26 20:42:23 +0000756 if (S_ISDIR(st.st_mode)) {
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000757 /* name is directory */
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000758 if (found[len1-1] != '/') {
759 found[len1] = '/';
760 }
Mark Whitley4e338752001-01-26 20:42:23 +0000761 } else {
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000762 /* not put found file if search only dirs for cd */
Eric Andersenc470f442003-07-28 09:56:35 +0000763 if (type == FIND_DIR_ONLY)
Eric Andersene5dfced2001-04-09 22:48:12 +0000764 goto cont;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000765 }
Mark Whitley4e338752001-01-26 20:42:23 +0000766 /* Add it to the list */
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000767 add_match(found);
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000768 continue;
Eric Andersene5dfced2001-04-09 22:48:12 +0000769cont:
770 free(found);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000771 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000772 closedir(dir);
Erik Andersen1dbe3402000-03-19 10:46:06 +0000773 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000774 if (paths != path1) {
Eric Andersenc470f442003-07-28 09:56:35 +0000775 free(paths[0]); /* allocated memory only in first member */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000776 free(paths);
777 }
Erik Andersen6273f652000-03-17 01:12:41 +0000778}
Erik Andersenf0657d32000-04-12 17:49:52 +0000779
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000780
781#define QUOT (UCHAR_MAX+1)
782
783#define collapse_pos(is, in) { \
Paul Fox574fee42005-07-19 20:41:06 +0000784 memmove(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
785 memmove(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000786
787static int find_match(char *matchBuf, int *len_with_quotes)
788{
789 int i, j;
790 int command_mode;
791 int c, c2;
792 int int_buf[BUFSIZ + 1];
793 int pos_buf[BUFSIZ + 1];
794
795 /* set to integer dimension characters and own positions */
796 for (i = 0;; i++) {
797 int_buf[i] = (int) ((unsigned char) matchBuf[i]);
798 if (int_buf[i] == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +0000799 pos_buf[i] = -1; /* indicator end line */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000800 break;
801 } else
802 pos_buf[i] = i;
803 }
804
805 /* mask \+symbol and convert '\t' to ' ' */
806 for (i = j = 0; matchBuf[i]; i++, j++)
807 if (matchBuf[i] == '\\') {
808 collapse_pos(j, j + 1);
809 int_buf[j] |= QUOT;
810 i++;
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000811#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
Eric Andersenc470f442003-07-28 09:56:35 +0000812 if (matchBuf[i] == '\t') /* algorithm equivalent */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000813 int_buf[j] = ' ' | QUOT;
814#endif
815 }
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000816#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000817 else if (matchBuf[i] == '\t')
818 int_buf[j] = ' ';
819#endif
820
821 /* mask "symbols" or 'symbols' */
822 c2 = 0;
823 for (i = 0; int_buf[i]; i++) {
824 c = int_buf[i];
825 if (c == '\'' || c == '"') {
826 if (c2 == 0)
827 c2 = c;
828 else {
829 if (c == c2)
830 c2 = 0;
831 else
832 int_buf[i] |= QUOT;
833 }
834 } else if (c2 != 0 && c != '$')
835 int_buf[i] |= QUOT;
836 }
837
838 /* skip commands with arguments if line have commands delimiters */
839 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
840 for (i = 0; int_buf[i]; i++) {
841 c = int_buf[i];
842 c2 = int_buf[i + 1];
843 j = i ? int_buf[i - 1] : -1;
844 command_mode = 0;
845 if (c == ';' || c == '&' || c == '|') {
846 command_mode = 1 + (c == c2);
847 if (c == '&') {
848 if (j == '>' || j == '<')
849 command_mode = 0;
850 } else if (c == '|' && j == '>')
851 command_mode = 0;
852 }
853 if (command_mode) {
854 collapse_pos(0, i + command_mode);
Eric Andersenc470f442003-07-28 09:56:35 +0000855 i = -1; /* hack incremet */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000856 }
857 }
858 /* collapse `command...` */
859 for (i = 0; int_buf[i]; i++)
860 if (int_buf[i] == '`') {
861 for (j = i + 1; int_buf[j]; j++)
862 if (int_buf[j] == '`') {
863 collapse_pos(i, j + 1);
864 j = 0;
865 break;
866 }
867 if (j) {
868 /* not found close ` - command mode, collapse all previous */
869 collapse_pos(0, i + 1);
870 break;
871 } else
Eric Andersenc470f442003-07-28 09:56:35 +0000872 i--; /* hack incremet */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000873 }
874
875 /* collapse (command...(command...)...) or {command...{command...}...} */
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000876 c = 0; /* "recursive" level */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000877 c2 = 0;
878 for (i = 0; int_buf[i]; i++)
879 if (int_buf[i] == '(' || int_buf[i] == '{') {
880 if (int_buf[i] == '(')
881 c++;
882 else
883 c2++;
884 collapse_pos(0, i + 1);
Eric Andersenc470f442003-07-28 09:56:35 +0000885 i = -1; /* hack incremet */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000886 }
887 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
888 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
889 if (int_buf[i] == ')')
890 c--;
891 else
892 c2--;
893 collapse_pos(0, i + 1);
Eric Andersenc470f442003-07-28 09:56:35 +0000894 i = -1; /* hack incremet */
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000895 }
896
897 /* skip first not quote space */
898 for (i = 0; int_buf[i]; i++)
899 if (int_buf[i] != ' ')
900 break;
901 if (i)
902 collapse_pos(0, i);
903
904 /* set find mode for completion */
905 command_mode = FIND_EXE_ONLY;
906 for (i = 0; int_buf[i]; i++)
907 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
908 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
Eric Andersenb3d6e2d2001-03-13 22:57:56 +0000909 && matchBuf[pos_buf[0]]=='c'
910 && matchBuf[pos_buf[1]]=='d' )
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000911 command_mode = FIND_DIR_ONLY;
912 else {
913 command_mode = FIND_FILE_ONLY;
914 break;
915 }
916 }
917 /* "strlen" */
918 for (i = 0; int_buf[i]; i++);
919 /* find last word */
920 for (--i; i >= 0; i--) {
921 c = int_buf[i];
922 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
923 collapse_pos(0, i + 1);
924 break;
925 }
926 }
927 /* skip first not quoted '\'' or '"' */
928 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
929 /* collapse quote or unquote // or /~ */
Eric Andersenc470f442003-07-28 09:56:35 +0000930 while ((int_buf[i] & ~QUOT) == '/' &&
Mark Whitley7e5291f2001-03-08 19:31:12 +0000931 ((int_buf[i + 1] & ~QUOT) == '/'
932 || (int_buf[i + 1] & ~QUOT) == '~')) {
933 i++;
934 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000935
936 /* set only match and destroy quotes */
937 j = 0;
Eric Andersen4f990532001-05-31 17:15:57 +0000938 for (c = 0; pos_buf[i] >= 0; i++) {
939 matchBuf[c++] = matchBuf[pos_buf[i]];
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000940 j = pos_buf[i] + 1;
941 }
Eric Andersen4f990532001-05-31 17:15:57 +0000942 matchBuf[c] = 0;
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000943 /* old lenght matchBuf with quotes symbols */
944 *len_with_quotes = j ? j - pos_buf[0] : 0;
945
946 return command_mode;
947}
948
Glenn L McGrath4d001292003-01-06 01:11:50 +0000949/*
950 display by column original ideas from ls applet,
951 very optimize by my :)
952*/
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000953static void showfiles(void)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000954{
955 int ncols, row;
956 int column_width = 0;
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000957 int nfiles = num_matches;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000958 int nrows = nfiles;
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000959 int l;
Glenn L McGrath4d001292003-01-06 01:11:50 +0000960
961 /* find the longest file name- use that as the column width */
962 for (row = 0; row < nrows; row++) {
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000963 l = strlen(matches[row]);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000964 if (column_width < l)
965 column_width = l;
966 }
967 column_width += 2; /* min space for columns */
968 ncols = cmdedit_termw / column_width;
969
970 if (ncols > 1) {
971 nrows /= ncols;
Denis Vlasenko7f1dc212006-12-19 01:10:25 +0000972 if (nfiles % ncols)
Glenn L McGrath4d001292003-01-06 01:11:50 +0000973 nrows++; /* round up fractionals */
Glenn L McGrath4d001292003-01-06 01:11:50 +0000974 } else {
975 ncols = 1;
976 }
977 for (row = 0; row < nrows; row++) {
978 int n = row;
979 int nc;
980
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000981 for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000982 printf("%s%-*s", matches[n],
983 column_width - strlen(matches[n]), "");
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +0000984 }
Denis Vlasenkod56b47f2006-12-21 22:24:46 +0000985 printf("%s\n", matches[n]);
Glenn L McGrath4d001292003-01-06 01:11:50 +0000986 }
987}
988
Denis Vlasenkof58906b2006-12-19 19:30:37 +0000989static int match_compare(const void *a, const void *b)
990{
991 return strcmp(*(char**)a, *(char**)b);
992}
993
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000994static void input_tab(int *lastWasTab)
Erik Andersenf0657d32000-04-12 17:49:52 +0000995{
996 /* Do TAB completion */
Eric Andersenc470f442003-07-28 09:56:35 +0000997 if (lastWasTab == 0) { /* free all memory */
Erik Andersenf0657d32000-04-12 17:49:52 +0000998 if (matches) {
Eric Andersen5f2c79d2001-02-16 18:36:04 +0000999 while (num_matches > 0)
Mark Whitley4e338752001-01-26 20:42:23 +00001000 free(matches[--num_matches]);
Erik Andersenf0657d32000-04-12 17:49:52 +00001001 free(matches);
1002 matches = (char **) NULL;
1003 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001004 return;
1005 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00001006 if (! *lastWasTab) {
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001007 char *tmp, *tmp1;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001008 int len_found;
1009 char matchBuf[BUFSIZ];
1010 int find_type;
1011 int recalc_pos;
1012
Eric Andersenc470f442003-07-28 09:56:35 +00001013 *lastWasTab = TRUE; /* flop trigger */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001014
1015 /* Make a local copy of the string -- up
1016 * to the position of the cursor */
1017 tmp = strncpy(matchBuf, command_ps, cursor);
1018 tmp[cursor] = 0;
1019
1020 find_type = find_match(matchBuf, &recalc_pos);
1021
1022 /* Free up any memory already allocated */
1023 input_tab(0);
Erik Andersenf0657d32000-04-12 17:49:52 +00001024
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001025#if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001026 /* If the word starts with `~' and there is no slash in the word,
Erik Andersenf0657d32000-04-12 17:49:52 +00001027 * then try completing this word as a username. */
1028
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001029 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001030 username_tab_completion(matchBuf, NULL);
1031 if (!matches)
Mark Whitley4e338752001-01-26 20:42:23 +00001032#endif
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001033 /* Try to match any executable in our path and everything
Erik Andersenf0657d32000-04-12 17:49:52 +00001034 * in the current working directory that matches. */
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001035 exe_n_cwd_tab_completion(matchBuf, find_type);
Denis Vlasenkof58906b2006-12-19 19:30:37 +00001036 /* Sort, then remove any duplicates found */
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001037 if (matches) {
Denis Vlasenkof58906b2006-12-19 19:30:37 +00001038 int i, n = 0;
1039 qsort(matches, num_matches, sizeof(char*), match_compare);
1040 for (i = 0; i < num_matches - 1; ++i) {
1041 if (matches[i] && matches[i+1]) {
1042 if (strcmp(matches[i], matches[i+1]) == 0) {
1043 free(matches[i]);
1044 matches[i] = 0;
1045 } else {
Denis Vlasenkof58906b2006-12-19 19:30:37 +00001046 matches[n++] = matches[i];
Denis Vlasenko92758142006-10-03 19:56:34 +00001047 }
Glenn L McGrath78b0e372001-06-26 02:06:08 +00001048 }
Denis Vlasenko92758142006-10-03 19:56:34 +00001049 }
Denis Vlasenkof58906b2006-12-19 19:30:37 +00001050 matches[n++] = matches[num_matches-1];
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001051 num_matches = n;
Glenn L McGrath78b0e372001-06-26 02:06:08 +00001052 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001053 /* Did we find exactly one match? */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001054 if (!matches || num_matches > 1) {
Mark Whitley4e338752001-01-26 20:42:23 +00001055 beep();
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001056 if (!matches)
Eric Andersenc470f442003-07-28 09:56:35 +00001057 return; /* not found */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001058 /* find minimal match */
Rob Landleyd921b2e2006-08-03 15:41:12 +00001059 tmp1 = xstrdup(matches[0]);
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001060 for (tmp = tmp1; *tmp; tmp++)
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001061 for (len_found = 1; len_found < num_matches; len_found++)
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001062 if (matches[len_found][(tmp - tmp1)] != *tmp) {
1063 *tmp = 0;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001064 break;
1065 }
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001066 if (*tmp1 == 0) { /* have unique */
1067 free(tmp1);
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001068 return;
1069 }
Denis Vlasenkod56b47f2006-12-21 22:24:46 +00001070 tmp = add_quote_for_spec_chars(tmp1);
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001071 free(tmp1);
Eric Andersenc470f442003-07-28 09:56:35 +00001072 } else { /* one match */
Denis Vlasenkod56b47f2006-12-21 22:24:46 +00001073 tmp = add_quote_for_spec_chars(matches[0]);
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001074 /* for next completion current found */
1075 *lastWasTab = FALSE;
Denis Vlasenkod56b47f2006-12-21 22:24:46 +00001076
1077 len_found = strlen(tmp);
1078 if (tmp[len_found-1] != '/') {
1079 tmp[len_found] = ' ';
1080 tmp[len_found+1] = '\0';
1081 }
Mark Whitley4e338752001-01-26 20:42:23 +00001082 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001083 len_found = strlen(tmp);
Mark Whitley4e338752001-01-26 20:42:23 +00001084 /* have space to placed match? */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001085 if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
Mark Whitley4e338752001-01-26 20:42:23 +00001086
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001087 /* before word for match */
1088 command_ps[cursor - recalc_pos] = 0;
1089 /* save tail line */
1090 strcpy(matchBuf, command_ps + cursor);
1091 /* add match */
1092 strcat(command_ps, tmp);
1093 /* add tail */
Mark Whitley4e338752001-01-26 20:42:23 +00001094 strcat(command_ps, matchBuf);
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001095 /* back to begin word for match */
1096 input_backward(recalc_pos);
1097 /* new pos */
1098 recalc_pos = cursor + len_found;
1099 /* new len */
1100 len = strlen(command_ps);
1101 /* write out the matched command */
Eric Andersen4f990532001-05-31 17:15:57 +00001102 redraw(cmdedit_y, len - recalc_pos);
Erik Andersenf0657d32000-04-12 17:49:52 +00001103 }
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001104 free(tmp);
Erik Andersenf0657d32000-04-12 17:49:52 +00001105 } else {
1106 /* Ok -- the last char was a TAB. Since they
1107 * just hit TAB again, print a list of all the
1108 * available choices... */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001109 if (matches && num_matches > 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00001110 int sav_cursor = cursor; /* change goto_new_line() */
Erik Andersenf0657d32000-04-12 17:49:52 +00001111
1112 /* Go to the next line */
Mark Whitley4e338752001-01-26 20:42:23 +00001113 goto_new_line();
"Vladimir N. Oleynik"fdb871c2006-01-25 11:53:47 +00001114 showfiles();
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001115 redraw(0, len - sav_cursor);
Erik Andersenf0657d32000-04-12 17:49:52 +00001116 }
1117 }
1118}
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001119#endif /* FEATURE_COMMAND_TAB_COMPLETION */
Erik Andersenf0657d32000-04-12 17:49:52 +00001120
Denis Vlasenko9d4533e2006-11-02 22:09:37 +00001121#if MAX_HISTORY > 0
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001122static void get_previous_history(void)
Erik Andersenf0657d32000-04-12 17:49:52 +00001123{
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001124 if (command_ps[0] != 0 || history[cur_history] == 0) {
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001125 free(history[cur_history]);
Rob Landleyd921b2e2006-08-03 15:41:12 +00001126 history[cur_history] = xstrdup(command_ps);
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001127 }
1128 cur_history--;
Erik Andersenf0657d32000-04-12 17:49:52 +00001129}
1130
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001131static int get_next_history(void)
Erik Andersenf0657d32000-04-12 17:49:52 +00001132{
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001133 int ch = cur_history;
1134
1135 if (ch < n_history) {
1136 get_previous_history(); /* save the current history line */
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001137 cur_history = ch + 1;
1138 return cur_history;
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001139 } else {
1140 beep();
1141 return 0;
1142 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001143}
Robert Griebl350d26b2002-12-03 22:45:46 +00001144
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001145#if ENABLE_FEATURE_COMMAND_SAVEHISTORY
Rob Landleydfba7412006-03-06 20:47:33 +00001146void load_history ( const char *fromfile )
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001147{
Robert Griebl350d26b2002-12-03 22:45:46 +00001148 FILE *fp;
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001149 int hi;
Robert Griebl350d26b2002-12-03 22:45:46 +00001150
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001151 /* cleanup old */
1152
1153 for(hi = n_history; hi > 0; ) {
1154 hi--;
1155 free ( history [hi] );
Robert Griebl350d26b2002-12-03 22:45:46 +00001156 }
1157
1158 if (( fp = fopen ( fromfile, "r" ))) {
Eric Andersenc470f442003-07-28 09:56:35 +00001159
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001160 for ( hi = 0; hi < MAX_HISTORY; ) {
Denis Vlasenko2d5ca602006-10-12 22:43:20 +00001161 char * hl = xmalloc_getline(fp);
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001162 int l;
1163
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001164 if (!hl)
Robert Griebl350d26b2002-12-03 22:45:46 +00001165 break;
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001166 l = strlen(hl);
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001167 if (l >= BUFSIZ)
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001168 hl[BUFSIZ-1] = 0;
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001169 if (l == 0 || hl[0] == ' ') {
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001170 free(hl);
1171 continue;
1172 }
1173 history [hi++] = hl;
Robert Griebl350d26b2002-12-03 22:45:46 +00001174 }
1175 fclose ( fp );
1176 }
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001177 cur_history = n_history = hi;
Robert Griebl350d26b2002-12-03 22:45:46 +00001178}
1179
Rob Landleydfba7412006-03-06 20:47:33 +00001180void save_history ( const char *tofile )
Robert Griebl350d26b2002-12-03 22:45:46 +00001181{
Robert Griebl350d26b2002-12-03 22:45:46 +00001182 FILE *fp = fopen ( tofile, "w" );
Eric Andersenc470f442003-07-28 09:56:35 +00001183
Robert Griebl350d26b2002-12-03 22:45:46 +00001184 if ( fp ) {
1185 int i;
Eric Andersenc470f442003-07-28 09:56:35 +00001186
Robert Griebl350d26b2002-12-03 22:45:46 +00001187 for ( i = 0; i < n_history; i++ ) {
Eric Andersen27bb7902003-12-23 20:24:51 +00001188 fprintf(fp, "%s\n", history [i]);
Robert Griebl350d26b2002-12-03 22:45:46 +00001189 }
1190 fclose ( fp );
1191 }
Robert Griebl350d26b2002-12-03 22:45:46 +00001192}
Glenn L McGrathfdbbb042002-12-09 11:10:40 +00001193#endif
Robert Griebl350d26b2002-12-03 22:45:46 +00001194
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001195#endif
Erik Andersenf0657d32000-04-12 17:49:52 +00001196
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001197enum {
1198 ESC = 27,
1199 DEL = 127,
1200};
1201
1202
Erik Andersen6273f652000-03-17 01:12:41 +00001203/*
1204 * This function is used to grab a character buffer
1205 * from the input file descriptor and allows you to
Eric Andersen9b3ce772004-04-12 15:03:51 +00001206 * a string with full command editing (sort of like
Erik Andersen6273f652000-03-17 01:12:41 +00001207 * a mini readline).
1208 *
1209 * The following standard commands are not implemented:
1210 * ESC-b -- Move back one word
1211 * ESC-f -- Move forward one word
1212 * ESC-d -- Delete back one word
1213 * ESC-h -- Delete forward one word
1214 * CTL-t -- Transpose two characters
1215 *
Paul Fox3f11b1b2005-08-04 19:04:46 +00001216 * Minimalist vi-style command line editing available if configured.
1217 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
Erik Andersen6273f652000-03-17 01:12:41 +00001218 *
Erik Andersen6273f652000-03-17 01:12:41 +00001219 */
Eric Andersenc470f442003-07-28 09:56:35 +00001220
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001221#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox3f11b1b2005-08-04 19:04:46 +00001222static int vi_mode;
1223
1224void setvimode ( int viflag )
1225{
1226 vi_mode = viflag;
1227}
1228
"Vladimir N. Oleynik"e4baaa22005-09-22 12:59:26 +00001229static void
Paul Fox3f11b1b2005-08-04 19:04:46 +00001230vi_Word_motion(char *command, int eat)
1231{
1232 while (cursor < len && !isspace(command[cursor]))
1233 input_forward();
1234 if (eat) while (cursor < len && isspace(command[cursor]))
1235 input_forward();
1236}
1237
"Vladimir N. Oleynik"e4baaa22005-09-22 12:59:26 +00001238static void
Paul Fox3f11b1b2005-08-04 19:04:46 +00001239vi_word_motion(char *command, int eat)
1240{
1241 if (isalnum(command[cursor]) || command[cursor] == '_') {
1242 while (cursor < len &&
1243 (isalnum(command[cursor+1]) ||
1244 command[cursor+1] == '_'))
1245 input_forward();
1246 } else if (ispunct(command[cursor])) {
1247 while (cursor < len &&
1248 (ispunct(command[cursor+1])))
1249 input_forward();
1250 }
1251
1252 if (cursor < len)
1253 input_forward();
1254
1255 if (eat && cursor < len && isspace(command[cursor]))
1256 while (cursor < len && isspace(command[cursor]))
1257 input_forward();
1258}
1259
"Vladimir N. Oleynik"e4baaa22005-09-22 12:59:26 +00001260static void
Paul Fox3f11b1b2005-08-04 19:04:46 +00001261vi_End_motion(char *command)
1262{
1263 input_forward();
1264 while (cursor < len && isspace(command[cursor]))
1265 input_forward();
1266 while (cursor < len-1 && !isspace(command[cursor+1]))
1267 input_forward();
1268}
1269
"Vladimir N. Oleynik"e4baaa22005-09-22 12:59:26 +00001270static void
Paul Fox3f11b1b2005-08-04 19:04:46 +00001271vi_end_motion(char *command)
1272{
1273 if (cursor >= len-1)
1274 return;
1275 input_forward();
1276 while (cursor < len-1 && isspace(command[cursor]))
1277 input_forward();
1278 if (cursor >= len-1)
1279 return;
1280 if (isalnum(command[cursor]) || command[cursor] == '_') {
1281 while (cursor < len-1 &&
1282 (isalnum(command[cursor+1]) ||
1283 command[cursor+1] == '_'))
1284 input_forward();
1285 } else if (ispunct(command[cursor])) {
1286 while (cursor < len-1 &&
1287 (ispunct(command[cursor+1])))
1288 input_forward();
1289 }
1290}
1291
"Vladimir N. Oleynik"e4baaa22005-09-22 12:59:26 +00001292static void
Paul Fox3f11b1b2005-08-04 19:04:46 +00001293vi_Back_motion(char *command)
1294{
1295 while (cursor > 0 && isspace(command[cursor-1]))
1296 input_backward(1);
1297 while (cursor > 0 && !isspace(command[cursor-1]))
1298 input_backward(1);
1299}
1300
"Vladimir N. Oleynik"e4baaa22005-09-22 12:59:26 +00001301static void
Paul Fox3f11b1b2005-08-04 19:04:46 +00001302vi_back_motion(char *command)
1303{
1304 if (cursor <= 0)
1305 return;
1306 input_backward(1);
1307 while (cursor > 0 && isspace(command[cursor]))
1308 input_backward(1);
1309 if (cursor <= 0)
1310 return;
1311 if (isalnum(command[cursor]) || command[cursor] == '_') {
1312 while (cursor > 0 &&
1313 (isalnum(command[cursor-1]) ||
1314 command[cursor-1] == '_'))
1315 input_backward(1);
1316 } else if (ispunct(command[cursor])) {
1317 while (cursor > 0 &&
1318 (ispunct(command[cursor-1])))
1319 input_backward(1);
1320 }
1321}
1322#endif
1323
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001324/*
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001325 * the emacs and vi modes share much of the code in the big
1326 * command loop. commands entered when in vi's command mode (aka
1327 * "escape mode") get an extra bit added to distinguish them --
1328 * this keeps them from being self-inserted. this clutters the
1329 * big switch a bit, but keeps all the code in one place.
Paul Fox3f11b1b2005-08-04 19:04:46 +00001330 */
1331
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001332#define vbit 0x100
1333
1334/* leave out the "vi-mode"-only case labels if vi editing isn't
1335 * configured. */
1336#define vi_case(caselabel) USE_FEATURE_COMMAND_EDITING(caselabel)
Paul Fox3f11b1b2005-08-04 19:04:46 +00001337
1338/* convert uppercase ascii to equivalent control char, for readability */
1339#define CNTRL(uc_char) ((uc_char) - 0x40)
1340
Eric Andersen044228d2001-07-17 01:12:36 +00001341
1342int cmdedit_read_input(char *prompt, char command[BUFSIZ])
Erik Andersen13456d12000-03-16 08:09:57 +00001343{
1344
Erik Andersenc7c634b2000-03-19 05:28:55 +00001345 int break_out = 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +00001346 int lastWasTab = FALSE;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001347 unsigned char c;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001348 unsigned int ic;
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001349#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001350 unsigned int prevc;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001351 int vi_cmdmode = 0;
1352#endif
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001353 /* prepare before init handlers */
Eric Andersenc470f442003-07-28 09:56:35 +00001354 cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
Mark Whitley4e338752001-01-26 20:42:23 +00001355 len = 0;
Mark Whitley4e338752001-01-26 20:42:23 +00001356 command_ps = command;
1357
Eric Andersen34506362001-08-02 05:02:46 +00001358 getTermSettings(0, (void *) &initial_settings);
1359 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
1360 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
1361 /* Turn off echoing and CTRL-C, so we can trap it */
1362 new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
Eric Andersen34506362001-08-02 05:02:46 +00001363 /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
1364 new_settings.c_cc[VMIN] = 1;
1365 new_settings.c_cc[VTIME] = 0;
1366 /* Turn off CTRL-C, so we can trap it */
Glenn L McGrath78b0e372001-06-26 02:06:08 +00001367# ifndef _POSIX_VDISABLE
1368# define _POSIX_VDISABLE '\0'
1369# endif
Eric Andersenc470f442003-07-28 09:56:35 +00001370 new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001371 command[0] = 0;
1372
Glenn L McGrath78b0e372001-06-26 02:06:08 +00001373 setTermSettings(0, (void *) &new_settings);
Mark Whitley4e338752001-01-26 20:42:23 +00001374 handlers_sets |= SET_RESET_TERM;
Erik Andersen13456d12000-03-16 08:09:57 +00001375
Eric Andersen6faae7d2001-02-16 20:09:17 +00001376 /* Now initialize things */
1377 cmdedit_init();
Eric Andersenf9ff8a72001-03-15 20:51:09 +00001378 /* Print out the command prompt */
1379 parse_prompt(prompt);
Eric Andersenb3dc3b82001-01-04 11:08:45 +00001380
Erik Andersenc7c634b2000-03-19 05:28:55 +00001381 while (1) {
Erik Andersen6273f652000-03-17 01:12:41 +00001382
Eric Andersenc470f442003-07-28 09:56:35 +00001383 fflush(stdout); /* buffered out to fast */
Mark Whitley4e338752001-01-26 20:42:23 +00001384
Eric Andersen7467c8d2001-07-12 20:26:32 +00001385 if (safe_read(0, &c, 1) < 1)
Eric Andersened424db2001-04-23 15:28:28 +00001386 /* if we can't read input then exit */
1387 goto prepare_to_die;
Erik Andersenf3b3d172000-04-09 18:24:05 +00001388
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001389 ic = c;
1390
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001391#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox3f11b1b2005-08-04 19:04:46 +00001392 newdelflag = 1;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001393 if (vi_cmdmode)
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001394 ic |= vbit;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001395#endif
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001396 switch (ic)
Paul Fox3f11b1b2005-08-04 19:04:46 +00001397 {
Erik Andersenf0657d32000-04-12 17:49:52 +00001398 case '\n':
1399 case '\r':
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001400 vi_case( case '\n'|vbit: )
1401 vi_case( case '\r'|vbit: )
Erik Andersenf0657d32000-04-12 17:49:52 +00001402 /* Enter */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001403 goto_new_line();
Erik Andersenf0657d32000-04-12 17:49:52 +00001404 break_out = 1;
1405 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001406 case CNTRL('A'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001407 vi_case( case '0'|vbit: )
Erik Andersenc7c634b2000-03-19 05:28:55 +00001408 /* Control-a -- Beginning of line */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001409 input_backward(cursor);
Mark Whitley4e338752001-01-26 20:42:23 +00001410 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001411 case CNTRL('B'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001412 vi_case( case 'h'|vbit: )
1413 vi_case( case '\b'|vbit: )
1414 vi_case( case DEL|vbit: )
Erik Andersenf0657d32000-04-12 17:49:52 +00001415 /* Control-b -- Move back one character */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001416 input_backward(1);
Erik Andersenf0657d32000-04-12 17:49:52 +00001417 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001418 case CNTRL('C'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001419 vi_case( case CNTRL('C')|vbit: )
Eric Andersen86349772000-12-18 20:25:50 +00001420 /* Control-c -- stop gathering input */
Mark Whitley4e338752001-01-26 20:42:23 +00001421 goto_new_line();
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001422#if !ENABLE_ASH
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001423 command[0] = 0;
Eric Andersen7467c8d2001-07-12 20:26:32 +00001424 len = 0;
1425 lastWasTab = FALSE;
1426 put_prompt();
Glenn L McGrath16e45d72004-02-04 08:24:39 +00001427#else
1428 len = 0;
Glenn L McGrath7fc504c2004-02-22 11:13:28 +00001429 break_out = -1; /* to control traps */
Glenn L McGrath16e45d72004-02-04 08:24:39 +00001430#endif
Eric Andersen7467c8d2001-07-12 20:26:32 +00001431 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001432 case CNTRL('D'):
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001433 /* Control-d -- Delete one character, or exit
Erik Andersenf0657d32000-04-12 17:49:52 +00001434 * if the len=0 and no chars to delete */
1435 if (len == 0) {
Eric Andersencb01bb12004-08-19 18:22:13 +00001436 errno = 0;
Eric Andersened424db2001-04-23 15:28:28 +00001437prepare_to_die:
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001438#if !ENABLE_ASH
Mark Whitley4e338752001-01-26 20:42:23 +00001439 printf("exit");
Eric Andersen7467c8d2001-07-12 20:26:32 +00001440 goto_new_line();
1441 /* cmdedit_reset_term() called in atexit */
1442 exit(EXIT_SUCCESS);
Eric Andersen044228d2001-07-17 01:12:36 +00001443#else
Glenn L McGrath7fc504c2004-02-22 11:13:28 +00001444 /* to control stopped jobs */
1445 len = break_out = -1;
Eric Andersen044228d2001-07-17 01:12:36 +00001446 break;
1447#endif
Erik Andersenf0657d32000-04-12 17:49:52 +00001448 } else {
Paul Fox3f11b1b2005-08-04 19:04:46 +00001449 input_delete(0);
Erik Andersenf0657d32000-04-12 17:49:52 +00001450 }
1451 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001452 case CNTRL('E'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001453 vi_case( case '$'|vbit: )
Erik Andersenc7c634b2000-03-19 05:28:55 +00001454 /* Control-e -- End of line */
Mark Whitley4e338752001-01-26 20:42:23 +00001455 input_end();
Erik Andersenc7c634b2000-03-19 05:28:55 +00001456 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001457 case CNTRL('F'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001458 vi_case( case 'l'|vbit: )
1459 vi_case( case ' '|vbit: )
Erik Andersenc7c634b2000-03-19 05:28:55 +00001460 /* Control-f -- Move forward one character */
Mark Whitley4e338752001-01-26 20:42:23 +00001461 input_forward();
Erik Andersenc7c634b2000-03-19 05:28:55 +00001462 break;
Erik Andersenf0657d32000-04-12 17:49:52 +00001463 case '\b':
1464 case DEL:
Erik Andersen1d1d9502000-04-21 01:26:49 +00001465 /* Control-h and DEL */
Mark Whitley4e338752001-01-26 20:42:23 +00001466 input_backspace();
Erik Andersenc7c634b2000-03-19 05:28:55 +00001467 break;
1468 case '\t':
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001469#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001470 input_tab(&lastWasTab);
Erik Andersena2685732000-04-09 18:27:46 +00001471#endif
Erik Andersenc7c634b2000-03-19 05:28:55 +00001472 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001473 case CNTRL('K'):
Eric Andersenc470f442003-07-28 09:56:35 +00001474 /* Control-k -- clear to end of line */
Eric Andersen65a07302002-04-13 13:26:49 +00001475 *(command + cursor) = 0;
1476 len = cursor;
Eric Andersenae103612002-04-24 23:08:23 +00001477 printf("\033[J");
Eric Andersen65a07302002-04-13 13:26:49 +00001478 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001479 case CNTRL('L'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001480 vi_case( case CNTRL('L')|vbit: )
Eric Andersen27bb7902003-12-23 20:24:51 +00001481 /* Control-l -- clear screen */
Eric Andersenc470f442003-07-28 09:56:35 +00001482 printf("\033[H");
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001483 redraw(0, len-cursor);
Eric Andersenf1f2bd02001-12-21 11:20:15 +00001484 break;
Denis Vlasenko9d4533e2006-11-02 22:09:37 +00001485#if MAX_HISTORY > 0
Paul Fox3f11b1b2005-08-04 19:04:46 +00001486 case CNTRL('N'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001487 vi_case( case CNTRL('N')|vbit: )
1488 vi_case( case 'j'|vbit: )
Erik Andersenf0657d32000-04-12 17:49:52 +00001489 /* Control-n -- Get next command in history */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001490 if (get_next_history())
Erik Andersenf0657d32000-04-12 17:49:52 +00001491 goto rewrite_line;
Erik Andersenf0657d32000-04-12 17:49:52 +00001492 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001493 case CNTRL('P'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001494 vi_case( case CNTRL('P')|vbit: )
1495 vi_case( case 'k'|vbit: )
Erik Andersenf0657d32000-04-12 17:49:52 +00001496 /* Control-p -- Get previous command from history */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001497 if (cur_history > 0) {
1498 get_previous_history();
Erik Andersenf0657d32000-04-12 17:49:52 +00001499 goto rewrite_line;
1500 } else {
Mark Whitley4e338752001-01-26 20:42:23 +00001501 beep();
Erik Andersenf0657d32000-04-12 17:49:52 +00001502 }
Erik Andersenc7c634b2000-03-19 05:28:55 +00001503 break;
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001504#endif
Paul Fox3f11b1b2005-08-04 19:04:46 +00001505 case CNTRL('U'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001506 vi_case( case CNTRL('U')|vbit: )
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001507 /* Control-U -- Clear line before cursor */
1508 if (cursor) {
1509 strcpy(command, command + cursor);
1510 redraw(cmdedit_y, len -= cursor);
Erik Andersenc7c634b2000-03-19 05:28:55 +00001511 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001512 break;
Paul Fox3f11b1b2005-08-04 19:04:46 +00001513 case CNTRL('W'):
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001514 vi_case( case CNTRL('W')|vbit: )
Eric Andersen27bb7902003-12-23 20:24:51 +00001515 /* Control-W -- Remove the last word */
1516 while (cursor > 0 && isspace(command[cursor-1]))
1517 input_backspace();
1518 while (cursor > 0 &&!isspace(command[cursor-1]))
1519 input_backspace();
1520 break;
Denis Vlasenko966ec7c2006-11-01 09:13:26 +00001521#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001522 case 'i'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001523 vi_cmdmode = 0;
1524 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001525 case 'I'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001526 input_backward(cursor);
1527 vi_cmdmode = 0;
1528 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001529 case 'a'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001530 input_forward();
1531 vi_cmdmode = 0;
1532 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001533 case 'A'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001534 input_end();
1535 vi_cmdmode = 0;
1536 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001537 case 'x'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001538 input_delete(1);
1539 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001540 case 'X'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001541 if (cursor > 0) {
1542 input_backward(1);
1543 input_delete(1);
1544 }
1545 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001546 case 'W'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001547 vi_Word_motion(command, 1);
1548 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001549 case 'w'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001550 vi_word_motion(command, 1);
1551 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001552 case 'E'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001553 vi_End_motion(command);
1554 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001555 case 'e'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001556 vi_end_motion(command);
1557 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001558 case 'B'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001559 vi_Back_motion(command);
1560 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001561 case 'b'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001562 vi_back_motion(command);
1563 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001564 case 'C'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001565 vi_cmdmode = 0;
1566 /* fall through */
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001567 case 'D'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001568 goto clear_to_eol;
1569
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001570 case 'c'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001571 vi_cmdmode = 0;
1572 /* fall through */
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001573 case 'd'|vbit:
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001574 {
Denis Vlasenko92758142006-10-03 19:56:34 +00001575 int nc, sc;
1576 sc = cursor;
1577 prevc = ic;
1578 if (safe_read(0, &c, 1) < 1)
1579 goto prepare_to_die;
1580 if (c == (prevc & 0xff)) {
1581 /* "cc", "dd" */
1582 input_backward(cursor);
1583 goto clear_to_eol;
1584 break;
1585 }
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001586 switch (c) {
Denis Vlasenko92758142006-10-03 19:56:34 +00001587 case 'w':
1588 case 'W':
1589 case 'e':
1590 case 'E':
1591 switch (c) {
1592 case 'w': /* "dw", "cw" */
1593 vi_word_motion(command, vi_cmdmode);
1594 break;
1595 case 'W': /* 'dW', 'cW' */
1596 vi_Word_motion(command, vi_cmdmode);
1597 break;
1598 case 'e': /* 'de', 'ce' */
1599 vi_end_motion(command);
1600 input_forward();
1601 break;
1602 case 'E': /* 'dE', 'cE' */
1603 vi_End_motion(command);
1604 input_forward();
1605 break;
1606 }
1607 nc = cursor;
1608 input_backward(cursor - sc);
1609 while (nc-- > cursor)
1610 input_delete(1);
1611 break;
1612 case 'b': /* "db", "cb" */
1613 case 'B': /* implemented as B */
1614 if (c == 'b')
1615 vi_back_motion(command);
1616 else
1617 vi_Back_motion(command);
1618 while (sc-- > cursor)
1619 input_delete(1);
1620 break;
1621 case ' ': /* "d ", "c " */
1622 input_delete(1);
1623 break;
1624 case '$': /* "d$", "c$" */
1625 clear_to_eol:
1626 while (cursor < len)
1627 input_delete(1);
1628 break;
1629 }
Paul Fox3f11b1b2005-08-04 19:04:46 +00001630 }
1631 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001632 case 'p'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001633 input_forward();
1634 /* fallthrough */
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001635 case 'P'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001636 put();
1637 break;
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001638 case 'r'|vbit:
Paul Fox3f11b1b2005-08-04 19:04:46 +00001639 if (safe_read(0, &c, 1) < 1)
1640 goto prepare_to_die;
1641 if (c == 0)
1642 beep();
1643 else {
1644 *(command + cursor) = c;
1645 putchar(c);
1646 putchar('\b');
1647 }
1648 break;
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001649#endif /* FEATURE_COMMAND_EDITING_VI */
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001650
1651 case ESC:
1652
Denis Vlasenko966ec7c2006-11-01 09:13:26 +00001653#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox3f11b1b2005-08-04 19:04:46 +00001654 if (vi_mode) {
1655 /* ESC: insert mode --> command mode */
1656 vi_cmdmode = 1;
1657 input_backward(1);
1658 break;
1659 }
1660#endif
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001661 /* escape sequence follows */
Eric Andersen7467c8d2001-07-12 20:26:32 +00001662 if (safe_read(0, &c, 1) < 1)
1663 goto prepare_to_die;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001664 /* different vt100 emulations */
1665 if (c == '[' || c == 'O') {
Paul Fox0f2dd9f2006-03-07 20:26:11 +00001666 vi_case( case '['|vbit: )
1667 vi_case( case 'O'|vbit: )
Eric Andersen7467c8d2001-07-12 20:26:32 +00001668 if (safe_read(0, &c, 1) < 1)
1669 goto prepare_to_die;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001670 }
Glenn L McGrath475820c2004-01-22 12:42:23 +00001671 if (c >= '1' && c <= '9') {
1672 unsigned char dummy;
1673
1674 if (safe_read(0, &dummy, 1) < 1)
1675 goto prepare_to_die;
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001676 if (dummy != '~')
Glenn L McGrath475820c2004-01-22 12:42:23 +00001677 c = 0;
1678 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001679 switch (c) {
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001680#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
Eric Andersenc470f442003-07-28 09:56:35 +00001681 case '\t': /* Alt-Tab */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001682
1683 input_tab(&lastWasTab);
1684 break;
1685#endif
Denis Vlasenko9d4533e2006-11-02 22:09:37 +00001686#if MAX_HISTORY > 0
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001687 case 'A':
1688 /* Up Arrow -- Get previous command from history */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001689 if (cur_history > 0) {
1690 get_previous_history();
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001691 goto rewrite_line;
1692 } else {
1693 beep();
1694 }
1695 break;
1696 case 'B':
1697 /* Down Arrow -- Get next command in history */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001698 if (!get_next_history())
Paul Fox3f11b1b2005-08-04 19:04:46 +00001699 break;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001700 /* Rewrite the line with the selected history item */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001701rewrite_line:
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001702 /* change command */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001703 len = strlen(strcpy(command, history[cur_history]));
Paul Fox3f11b1b2005-08-04 19:04:46 +00001704 /* redraw and go to eol (bol, in vi */
Denis Vlasenko966ec7c2006-11-01 09:13:26 +00001705#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox3f11b1b2005-08-04 19:04:46 +00001706 redraw(cmdedit_y, vi_mode ? 9999:0);
1707#else
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001708 redraw(cmdedit_y, 0);
Paul Fox3f11b1b2005-08-04 19:04:46 +00001709#endif
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001710 break;
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001711#endif
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001712 case 'C':
1713 /* Right Arrow -- Move forward one character */
1714 input_forward();
1715 break;
1716 case 'D':
1717 /* Left Arrow -- Move back one character */
1718 input_backward(1);
1719 break;
1720 case '3':
1721 /* Delete */
Paul Fox3f11b1b2005-08-04 19:04:46 +00001722 input_delete(0);
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001723 break;
1724 case '1':
1725 case 'H':
Glenn L McGrath7fc504c2004-02-22 11:13:28 +00001726 /* <Home> */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001727 input_backward(cursor);
1728 break;
1729 case '4':
1730 case 'F':
Glenn L McGrath7fc504c2004-02-22 11:13:28 +00001731 /* <End> */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001732 input_end();
1733 break;
1734 default:
Glenn L McGrath475820c2004-01-22 12:42:23 +00001735 c = 0;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001736 beep();
1737 }
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001738 break;
Erik Andersenc7c634b2000-03-19 05:28:55 +00001739
Eric Andersenc470f442003-07-28 09:56:35 +00001740 default: /* If it's regular input, do the normal thing */
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001741#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001742 /* Control-V -- Add non-printable symbol */
Paul Fox3f11b1b2005-08-04 19:04:46 +00001743 if (c == CNTRL('V')) {
Eric Andersen7467c8d2001-07-12 20:26:32 +00001744 if (safe_read(0, &c, 1) < 1)
1745 goto prepare_to_die;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001746 if (c == 0) {
1747 beep();
1748 break;
1749 }
1750 } else
1751#endif
Paul Fox3f11b1b2005-08-04 19:04:46 +00001752 {
Denis Vlasenko966ec7c2006-11-01 09:13:26 +00001753#if ENABLE_FEATURE_COMMAND_EDITING_VI
Paul Fox3f11b1b2005-08-04 19:04:46 +00001754 if (vi_cmdmode) /* don't self-insert */
1755 break;
1756#endif
1757 if (!Isprint(c)) /* Skip non-printable characters */
1758 break;
1759 }
Erik Andersenc7c634b2000-03-19 05:28:55 +00001760
Eric Andersenc470f442003-07-28 09:56:35 +00001761 if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
Erik Andersenc7c634b2000-03-19 05:28:55 +00001762 break;
1763
1764 len++;
1765
Eric Andersenc470f442003-07-28 09:56:35 +00001766 if (cursor == (len - 1)) { /* Append if at the end of the line */
Erik Andersenf0657d32000-04-12 17:49:52 +00001767 *(command + cursor) = c;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001768 *(command + cursor + 1) = 0;
1769 cmdedit_set_out_char(0);
Eric Andersenc470f442003-07-28 09:56:35 +00001770 } else { /* Insert otherwise */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001771 int sc = cursor;
Erik Andersenc7c634b2000-03-19 05:28:55 +00001772
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001773 memmove(command + sc + 1, command + sc, len - sc);
1774 *(command + sc) = c;
1775 sc++;
Mark Whitley4e338752001-01-26 20:42:23 +00001776 /* rewrite from cursor */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001777 input_end();
Mark Whitley4e338752001-01-26 20:42:23 +00001778 /* to prev x pos + 1 */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001779 input_backward(cursor - sc);
Erik Andersenc7c634b2000-03-19 05:28:55 +00001780 }
1781
Erik Andersenc7c634b2000-03-19 05:28:55 +00001782 break;
Erik Andersen13456d12000-03-16 08:09:57 +00001783 }
Eric Andersenc470f442003-07-28 09:56:35 +00001784 if (break_out) /* Enter is the command terminator, no more input. */
Erik Andersenc7c634b2000-03-19 05:28:55 +00001785 break;
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001786
1787 if (c != '\t')
1788 lastWasTab = FALSE;
Erik Andersenc7c634b2000-03-19 05:28:55 +00001789 }
1790
Glenn L McGrath78b0e372001-06-26 02:06:08 +00001791 setTermSettings(0, (void *) &initial_settings);
Mark Whitley4e338752001-01-26 20:42:23 +00001792 handlers_sets &= ~SET_RESET_TERM;
Erik Andersenc7c634b2000-03-19 05:28:55 +00001793
Denis Vlasenko9d4533e2006-11-02 22:09:37 +00001794#if MAX_HISTORY > 0
Erik Andersenc7c634b2000-03-19 05:28:55 +00001795 /* Handle command history log */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001796 /* cleanup may be saved current command line */
Glenn L McGrath16e45d72004-02-04 08:24:39 +00001797 if (len> 0) { /* no put empty line */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001798 int i = n_history;
Glenn L McGrath7fc504c2004-02-22 11:13:28 +00001799
1800 free(history[MAX_HISTORY]);
1801 history[MAX_HISTORY] = 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +00001802 /* After max history, remove the oldest command */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001803 if (i >= MAX_HISTORY) {
1804 free(history[0]);
1805 for(i = 0; i < (MAX_HISTORY-1); i++)
1806 history[i] = history[i+1];
Erik Andersen13456d12000-03-16 08:09:57 +00001807 }
Rob Landleyd921b2e2006-08-03 15:41:12 +00001808 history[i++] = xstrdup(command);
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001809 cur_history = i;
1810 n_history = i;
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001811#if ENABLE_FEATURE_SH_FANCY_PROMPT
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001812 num_ok_lines++;
1813#endif
Erik Andersen6273f652000-03-17 01:12:41 +00001814 }
Denis Vlasenko9d4533e2006-11-02 22:09:37 +00001815#else /* MAX_HISTORY == 0 */
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001816#if ENABLE_FEATURE_SH_FANCY_PROMPT
Glenn L McGrath16e45d72004-02-04 08:24:39 +00001817 if (len > 0) { /* no put empty line */
Glenn L McGrath062c74f2002-11-27 09:29:49 +00001818 num_ok_lines++;
1819 }
1820#endif
Denis Vlasenko9d4533e2006-11-02 22:09:37 +00001821#endif /* MAX_HISTORY > 0 */
Eric Andersen27bb7902003-12-23 20:24:51 +00001822 if (break_out > 0) {
Eric Andersenc470f442003-07-28 09:56:35 +00001823 command[len++] = '\n'; /* set '\n' */
1824 command[len] = 0;
Eric Andersen044228d2001-07-17 01:12:36 +00001825 }
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001826#if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_COMMAND_TAB_COMPLETION
Eric Andersenc470f442003-07-28 09:56:35 +00001827 input_tab(0); /* strong free */
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001828#endif
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001829#if ENABLE_FEATURE_SH_FANCY_PROMPT
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001830 free(cmdedit_prompt);
1831#endif
Eric Andersen501c88b2000-07-28 15:14:45 +00001832 cmdedit_reset_term();
Eric Andersen044228d2001-07-17 01:12:36 +00001833 return len;
Eric Andersen501c88b2000-07-28 15:14:45 +00001834}
1835
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001836#endif /* FEATURE_COMMAND_EDITING */
Eric Andersen501c88b2000-07-28 15:14:45 +00001837
1838
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001839#ifdef TEST
1840
Denis Vlasenko8f8f2682006-10-03 21:00:43 +00001841const char *applet_name = "debug stuff usage";
Eric Andersene5dfced2001-04-09 22:48:12 +00001842
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001843#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
Eric Andersenf9ff8a72001-03-15 20:51:09 +00001844#include <locale.h>
1845#endif
1846
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001847int main(int argc, char **argv)
1848{
1849 char buff[BUFSIZ];
1850 char *prompt =
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001851#if ENABLE_FEATURE_SH_FANCY_PROMPT
Eric Andersenb3d6e2d2001-03-13 22:57:56 +00001852 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001853\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
Eric Andersenb3d6e2d2001-03-13 22:57:56 +00001854\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001855#else
1856 "% ";
1857#endif
1858
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001859#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
Eric Andersenf9ff8a72001-03-15 20:51:09 +00001860 setlocale(LC_ALL, "");
1861#endif
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001862 while (1) {
Eric Andersenb3d6e2d2001-03-13 22:57:56 +00001863 int l;
Eric Andersen27bb7902003-12-23 20:24:51 +00001864 l = cmdedit_read_input(prompt, buff);
Denis Vlasenko7f1dc212006-12-19 01:10:25 +00001865 if (l > 0 && buff[l-1] == '\n') {
Eric Andersenb3d6e2d2001-03-13 22:57:56 +00001866 buff[l-1] = 0;
Eric Andersen27bb7902003-12-23 20:24:51 +00001867 printf("*** cmdedit_read_input() returned line =%s=\n", buff);
1868 } else {
1869 break;
1870 }
Eric Andersen7467c8d2001-07-12 20:26:32 +00001871 }
Eric Andersen27bb7902003-12-23 20:24:51 +00001872 printf("*** cmdedit_read_input() detect ^D\n");
Eric Andersen5f2c79d2001-02-16 18:36:04 +00001873 return 0;
1874}
1875
Eric Andersenc470f442003-07-28 09:56:35 +00001876#endif /* TEST */