source: MondoRescue/branches/stable/mindi-busybox/libbb/lineedit.c

Last change on this file was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

  • Property svn:eol-style set to native
File size: 41.3 KB
RevLine 
[1765]1/* vi: set sw=4 ts=4: */
2/*
3 * Termios command line History and Editing.
4 *
5 * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
6 * Written by: Vladimir Oleynik <dzo@simtreas.ru>
7 *
8 * Used ideas:
9 * Adam Rogoyski <rogoyski@cs.utexas.edu>
10 * Dave Cinege <dcinege@psychosis.com>
11 * Jakub Jelinek (c) 1995
12 * Erik Andersen <andersen@codepoet.org> (Majorly adjusted for busybox)
13 *
14 * This code is 'as is' with no warranty.
15 */
16
17/*
18 Usage and known bugs:
19 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.
23 Ctrl-E also works as End.
24
25 Small bugs (simple effect):
26 - not true viewing if terminal size (x*y symbols) less
27 size (prompt + editor's line + 2 symbols)
28 - not true viewing if length prompt less terminal width
29 */
30
31#include "libbb.h"
32
33
34/* FIXME: obsolete CONFIG item? */
35#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
36
37
38#ifdef TEST
39
40#define ENABLE_FEATURE_EDITING 0
41#define ENABLE_FEATURE_TAB_COMPLETION 0
42#define ENABLE_FEATURE_USERNAME_COMPLETION 0
43#define ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT 0
44#define ENABLE_FEATURE_CLEAN_UP 0
45
46#endif /* TEST */
47
48
49/* Entire file (except TESTing part) sits inside this #if */
50#if ENABLE_FEATURE_EDITING
51
52#if ENABLE_LOCALE_SUPPORT
53#define Isprint(c) isprint(c)
54#else
55#define Isprint(c) ((c) >= ' ' && (c) != ((unsigned char)'\233'))
56#endif
57
58#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \
59(ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
60
61enum { MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN };
62
63static line_input_t *state;
64
65static struct termios initial_settings, new_settings;
66
67static volatile unsigned cmdedit_termw = 80; /* actual terminal width */
68
69static int cmdedit_x; /* real x terminal position */
70static int cmdedit_y; /* pseudoreal y terminal position */
71static int cmdedit_prmt_len; /* length of prompt (without colors etc) */
72
73static unsigned cursor;
74static unsigned command_len;
75static char *command_ps;
76static const char *cmdedit_prompt;
77
78#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
79static char *hostname_buf;
80static int num_ok_lines = 1;
81#endif
82
83#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
84static const char null_str[] = "";
85static char *user_buf;
86static char *home_pwd_buf = (char*)null_str;
87#endif
88
89/* Put 'command_ps[cursor]', cursor++.
90 * Advance cursor on screen. If we reached right margin, scroll text up
91 * and remove terminal margin effect by printing 'next_char' */
92static void cmdedit_set_out_char(int next_char)
93{
94 int c = (unsigned char)command_ps[cursor];
95
96 if (c == '\0') {
97 /* erase character after end of input string */
98 c = ' ';
99 }
100#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
101 /* Display non-printable characters in reverse */
102 if (!Isprint(c)) {
103 if (c >= 128)
104 c -= 128;
105 if (c < ' ')
106 c += '@';
107 if (c == 127)
108 c = '?';
109 printf("\033[7m%c\033[0m", c);
110 } else
111#endif
112 {
113 if (initial_settings.c_lflag & ECHO)
114 putchar(c);
115 }
116 if (++cmdedit_x >= cmdedit_termw) {
117 /* terminal is scrolled down */
118 cmdedit_y++;
119 cmdedit_x = 0;
120 /* destroy "(auto)margin" */
121 putchar(next_char);
122 putchar('\b');
123 }
124// Huh? What if command_ps[cursor] == '\0' (we are at the end already?)
125 cursor++;
126}
127
128/* Move to end of line (by printing all chars till the end) */
129static void input_end(void)
130{
131 while (cursor < command_len)
132 cmdedit_set_out_char(' ');
133}
134
135/* Go to the next line */
136static void goto_new_line(void)
137{
138 input_end();
139 if (cmdedit_x)
140 putchar('\n');
141}
142
143
144static void out1str(const char *s)
145{
146 if (s)
147 fputs(s, stdout);
148}
149
150static void beep(void)
151{
152 putchar('\007');
153}
154
155/* Move back one character */
156/* (optimized for slow terminals) */
157static void input_backward(unsigned num)
158{
159 int count_y;
160
161 if (num > cursor)
162 num = cursor;
163 if (!num)
164 return;
165 cursor -= num;
166
167 if (cmdedit_x >= num) {
168 cmdedit_x -= num;
169 if (num <= 4) {
170 printf("\b\b\b\b" + (4-num));
171 return;
172 }
173 printf("\033[%uD", num);
174 return;
175 }
176
177 /* Need to go one or more lines up */
178 num -= cmdedit_x;
179 count_y = 1 + (num / cmdedit_termw);
180 cmdedit_y -= count_y;
181 cmdedit_x = cmdedit_termw * count_y - num;
182 /* go to 1st column; go up; go to correct column */
183 printf("\r" "\033[%dA" "\033[%dC", count_y, cmdedit_x);
184}
185
186static void put_prompt(void)
187{
188 out1str(cmdedit_prompt);
189 cmdedit_x = cmdedit_prmt_len;
190 cursor = 0;
191// Huh? what if cmdedit_prmt_len >= width?
192 cmdedit_y = 0; /* new quasireal y */
193}
194
195/* draw prompt, editor line, and clear tail */
196static void redraw(int y, int back_cursor)
197{
198 if (y > 0) /* up to start y */
199 printf("\033[%dA", y);
200 putchar('\r');
201 put_prompt();
202 input_end(); /* rewrite */
203 printf("\033[J"); /* erase after cursor */
204 input_backward(back_cursor);
205}
206
207#if ENABLE_FEATURE_EDITING_VI
208#define DELBUFSIZ 128
209static char *delbuf; /* a (malloced) place to store deleted characters */
210static char *delp;
211static char newdelflag; /* whether delbuf should be reused yet */
212#endif
213
214/* Delete the char in front of the cursor, optionally saving it
215 * for later putback */
216static void input_delete(int save)
217{
218 int j = cursor;
219
220 if (j == command_len)
221 return;
222
223#if ENABLE_FEATURE_EDITING_VI
224 if (save) {
225 if (newdelflag) {
226 if (!delbuf)
227 delbuf = malloc(DELBUFSIZ);
228 /* safe if malloc fails */
229 delp = delbuf;
230 newdelflag = 0;
231 }
232 if (delbuf && (delp - delbuf < DELBUFSIZ))
233 *delp++ = command_ps[j];
234 }
235#endif
236
237 strcpy(command_ps + j, command_ps + j + 1);
238 command_len--;
239 input_end(); /* rewrite new line */
240 cmdedit_set_out_char(' '); /* erase char */
241 input_backward(cursor - j); /* back to old pos cursor */
242}
243
244#if ENABLE_FEATURE_EDITING_VI
245static void put(void)
246{
247 int ocursor;
248 int j = delp - delbuf;
249
250 if (j == 0)
251 return;
252 ocursor = cursor;
253 /* open hole and then fill it */
254 memmove(command_ps + cursor + j, command_ps + cursor, command_len - cursor + 1);
255 strncpy(command_ps + cursor, delbuf, j);
256 command_len += j;
257 input_end(); /* rewrite new line */
258 input_backward(cursor - ocursor - j + 1); /* at end of new text */
259}
260#endif
261
262/* Delete the char in back of the cursor */
263static void input_backspace(void)
264{
265 if (cursor > 0) {
266 input_backward(1);
267 input_delete(0);
268 }
269}
270
271/* Move forward one character */
272static void input_forward(void)
273{
274 if (cursor < command_len)
275 cmdedit_set_out_char(command_ps[cursor + 1]);
276}
277
278
279#if ENABLE_FEATURE_TAB_COMPLETION
280
281static char **matches;
282static unsigned num_matches;
283
284static void free_tab_completion_data(void)
285{
286 if (matches) {
287 while (num_matches)
288 free(matches[--num_matches]);
289 free(matches);
290 matches = NULL;
291 }
292}
293
294static void add_match(char *matched)
295{
296 int nm = num_matches;
297 int nm1 = nm + 1;
298
299 matches = xrealloc(matches, nm1 * sizeof(char *));
300 matches[nm] = matched;
301 num_matches++;
302}
303
304#if ENABLE_FEATURE_USERNAME_COMPLETION
305static void username_tab_completion(char *ud, char *with_shash_flg)
306{
307 struct passwd *entry;
308 int userlen;
309
310 ud++; /* ~user/... to user/... */
311 userlen = strlen(ud);
312
313 if (with_shash_flg) { /* "~/..." or "~user/..." */
314 char *sav_ud = ud - 1;
315 char *home = NULL;
316 char *temp;
317
318 if (*ud == '/') { /* "~/..." */
319 home = home_pwd_buf;
320 } else {
321 /* "~user/..." */
322 temp = strchr(ud, '/');
323 *temp = 0; /* ~user\0 */
324 entry = getpwnam(ud);
325 *temp = '/'; /* restore ~user/... */
326 ud = temp;
327 if (entry)
328 home = entry->pw_dir;
329 }
330 if (home) {
331 if ((userlen + strlen(home) + 1) < MAX_LINELEN) {
332 char temp2[MAX_LINELEN]; /* argument size */
333
334 /* /home/user/... */
335 sprintf(temp2, "%s%s", home, ud);
336 strcpy(sav_ud, temp2);
337 }
338 }
339 } else {
340 /* "~[^/]*" */
341 /* Using _r function to avoid pulling in static buffers */
342 char line_buff[256];
343 struct passwd pwd;
344 struct passwd *result;
345
346 setpwent();
347 while (!getpwent_r(&pwd, line_buff, sizeof(line_buff), &result)) {
348 /* Null usernames should result in all users as possible completions. */
349 if (/*!userlen || */ strncmp(ud, pwd.pw_name, userlen) == 0) {
350 add_match(xasprintf("~%s/", pwd.pw_name));
351 }
352 }
353 endpwent();
354 }
355}
356#endif /* FEATURE_COMMAND_USERNAME_COMPLETION */
357
358enum {
359 FIND_EXE_ONLY = 0,
360 FIND_DIR_ONLY = 1,
361 FIND_FILE_ONLY = 2,
362};
363
364static int path_parse(char ***p, int flags)
365{
366 int npth;
367 const char *pth;
368 char *tmp;
369 char **res;
370
371 /* if not setenv PATH variable, to search cur dir "." */
372 if (flags != FIND_EXE_ONLY)
373 return 1;
374
375 if (state->flags & WITH_PATH_LOOKUP)
376 pth = state->path_lookup;
377 else
378 pth = getenv("PATH");
379 /* PATH=<empty> or PATH=:<empty> */
380 if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
381 return 1;
382
383 tmp = (char*)pth;
384 npth = 1; /* path component count */
385 while (1) {
386 tmp = strchr(tmp, ':');
387 if (!tmp)
388 break;
389 if (*++tmp == '\0')
390 break; /* :<empty> */
391 npth++;
392 }
393
394 res = xmalloc(npth * sizeof(char*));
395 res[0] = tmp = xstrdup(pth);
396 npth = 1;
397 while (1) {
398 tmp = strchr(tmp, ':');
399 if (!tmp)
400 break;
401 *tmp++ = '\0'; /* ':' -> '\0' */
402 if (*tmp == '\0')
403 break; /* :<empty> */
404 res[npth++] = tmp;
405 }
406 *p = res;
407 return npth;
408}
409
410static void exe_n_cwd_tab_completion(char *command, int type)
411{
412 DIR *dir;
413 struct dirent *next;
414 char dirbuf[MAX_LINELEN];
415 struct stat st;
416 char *path1[1];
417 char **paths = path1;
418 int npaths;
419 int i;
420 char *found;
421 char *pfind = strrchr(command, '/');
422
423 npaths = 1;
424 path1[0] = (char*)".";
425
426 if (pfind == NULL) {
427 /* no dir, if flags==EXE_ONLY - get paths, else "." */
428 npaths = path_parse(&paths, type);
429 pfind = command;
430 } else {
431 /* dirbuf = ".../.../.../" */
432 safe_strncpy(dirbuf, command, (pfind - command) + 2);
433#if ENABLE_FEATURE_USERNAME_COMPLETION
434 if (dirbuf[0] == '~') /* ~/... or ~user/... */
435 username_tab_completion(dirbuf, dirbuf);
436#endif
437 paths[0] = dirbuf;
438 /* point to 'l' in "..../last_component" */
439 pfind++;
440 }
441
442 for (i = 0; i < npaths; i++) {
443 dir = opendir(paths[i]);
444 if (!dir) /* Don't print an error */
445 continue;
446
447 while ((next = readdir(dir)) != NULL) {
448 int len1;
449 const char *str_found = next->d_name;
450
451 /* matched? */
452 if (strncmp(str_found, pfind, strlen(pfind)))
453 continue;
454 /* not see .name without .match */
455 if (*str_found == '.' && *pfind == 0) {
456 if (NOT_LONE_CHAR(paths[i], '/') || str_found[1])
457 continue;
458 str_found = ""; /* only "/" */
459 }
460 found = concat_path_file(paths[i], str_found);
461 /* hmm, remover in progress? */
462 if (stat(found, &st) < 0)
463 goto cont;
464 /* find with dirs? */
465 if (paths[i] != dirbuf)
466 strcpy(found, next->d_name); /* only name */
467
468 len1 = strlen(found);
469 found = xrealloc(found, len1 + 2);
470 found[len1] = '\0';
471 found[len1+1] = '\0';
472
473 if (S_ISDIR(st.st_mode)) {
474 /* name is directory */
475 if (found[len1-1] != '/') {
476 found[len1] = '/';
477 }
478 } else {
479 /* not put found file if search only dirs for cd */
480 if (type == FIND_DIR_ONLY)
481 goto cont;
482 }
483 /* Add it to the list */
484 add_match(found);
485 continue;
486 cont:
487 free(found);
488 }
489 closedir(dir);
490 }
491 if (paths != path1) {
492 free(paths[0]); /* allocated memory only in first member */
493 free(paths);
494 }
495}
496
497#define QUOT (UCHAR_MAX+1)
498
499#define collapse_pos(is, in) { \
500 memmove(int_buf+(is), int_buf+(in), (MAX_LINELEN+1-(is)-(in))*sizeof(int)); \
501 memmove(pos_buf+(is), pos_buf+(in), (MAX_LINELEN+1-(is)-(in))*sizeof(int)); }
502
503static int find_match(char *matchBuf, int *len_with_quotes)
504{
505 int i, j;
506 int command_mode;
507 int c, c2;
508 int int_buf[MAX_LINELEN + 1];
509 int pos_buf[MAX_LINELEN + 1];
510
511 /* set to integer dimension characters and own positions */
512 for (i = 0;; i++) {
513 int_buf[i] = (unsigned char)matchBuf[i];
514 if (int_buf[i] == 0) {
515 pos_buf[i] = -1; /* indicator end line */
516 break;
517 }
518 pos_buf[i] = i;
519 }
520
521 /* mask \+symbol and convert '\t' to ' ' */
522 for (i = j = 0; matchBuf[i]; i++, j++)
523 if (matchBuf[i] == '\\') {
524 collapse_pos(j, j + 1);
525 int_buf[j] |= QUOT;
526 i++;
527#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
528 if (matchBuf[i] == '\t') /* algorithm equivalent */
529 int_buf[j] = ' ' | QUOT;
530#endif
531 }
532#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
533 else if (matchBuf[i] == '\t')
534 int_buf[j] = ' ';
535#endif
536
537 /* mask "symbols" or 'symbols' */
538 c2 = 0;
539 for (i = 0; int_buf[i]; i++) {
540 c = int_buf[i];
541 if (c == '\'' || c == '"') {
542 if (c2 == 0)
543 c2 = c;
544 else {
545 if (c == c2)
546 c2 = 0;
547 else
548 int_buf[i] |= QUOT;
549 }
550 } else if (c2 != 0 && c != '$')
551 int_buf[i] |= QUOT;
552 }
553
554 /* skip commands with arguments if line has commands delimiters */
555 /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
556 for (i = 0; int_buf[i]; i++) {
557 c = int_buf[i];
558 c2 = int_buf[i + 1];
559 j = i ? int_buf[i - 1] : -1;
560 command_mode = 0;
561 if (c == ';' || c == '&' || c == '|') {
562 command_mode = 1 + (c == c2);
563 if (c == '&') {
564 if (j == '>' || j == '<')
565 command_mode = 0;
566 } else if (c == '|' && j == '>')
567 command_mode = 0;
568 }
569 if (command_mode) {
570 collapse_pos(0, i + command_mode);
571 i = -1; /* hack incremet */
572 }
573 }
574 /* collapse `command...` */
575 for (i = 0; int_buf[i]; i++)
576 if (int_buf[i] == '`') {
577 for (j = i + 1; int_buf[j]; j++)
578 if (int_buf[j] == '`') {
579 collapse_pos(i, j + 1);
580 j = 0;
581 break;
582 }
583 if (j) {
584 /* not found close ` - command mode, collapse all previous */
585 collapse_pos(0, i + 1);
586 break;
587 } else
588 i--; /* hack incremet */
589 }
590
591 /* collapse (command...(command...)...) or {command...{command...}...} */
592 c = 0; /* "recursive" level */
593 c2 = 0;
594 for (i = 0; int_buf[i]; i++)
595 if (int_buf[i] == '(' || int_buf[i] == '{') {
596 if (int_buf[i] == '(')
597 c++;
598 else
599 c2++;
600 collapse_pos(0, i + 1);
601 i = -1; /* hack incremet */
602 }
603 for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
604 if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
605 if (int_buf[i] == ')')
606 c--;
607 else
608 c2--;
609 collapse_pos(0, i + 1);
610 i = -1; /* hack incremet */
611 }
612
613 /* skip first not quote space */
614 for (i = 0; int_buf[i]; i++)
615 if (int_buf[i] != ' ')
616 break;
617 if (i)
618 collapse_pos(0, i);
619
620 /* set find mode for completion */
621 command_mode = FIND_EXE_ONLY;
622 for (i = 0; int_buf[i]; i++)
623 if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
624 if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
625 && matchBuf[pos_buf[0]]=='c'
626 && matchBuf[pos_buf[1]]=='d'
627 ) {
628 command_mode = FIND_DIR_ONLY;
629 } else {
630 command_mode = FIND_FILE_ONLY;
631 break;
632 }
633 }
634 for (i = 0; int_buf[i]; i++)
635 /* "strlen" */;
636 /* find last word */
637 for (--i; i >= 0; i--) {
638 c = int_buf[i];
639 if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
640 collapse_pos(0, i + 1);
641 break;
642 }
643 }
644 /* skip first not quoted '\'' or '"' */
645 for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++)
646 /*skip*/;
647 /* collapse quote or unquote // or /~ */
648 while ((int_buf[i] & ~QUOT) == '/'
649 && ((int_buf[i+1] & ~QUOT) == '/' || (int_buf[i+1] & ~QUOT) == '~')
650 ) {
651 i++;
652 }
653
654 /* set only match and destroy quotes */
655 j = 0;
656 for (c = 0; pos_buf[i] >= 0; i++) {
657 matchBuf[c++] = matchBuf[pos_buf[i]];
658 j = pos_buf[i] + 1;
659 }
660 matchBuf[c] = 0;
661 /* old lenght matchBuf with quotes symbols */
662 *len_with_quotes = j ? j - pos_buf[0] : 0;
663
664 return command_mode;
665}
666
667/*
668 * display by column (original idea from ls applet,
669 * very optimized by me :)
670 */
671static void showfiles(void)
672{
673 int ncols, row;
674 int column_width = 0;
675 int nfiles = num_matches;
676 int nrows = nfiles;
677 int l;
678
679 /* find the longest file name- use that as the column width */
680 for (row = 0; row < nrows; row++) {
681 l = strlen(matches[row]);
682 if (column_width < l)
683 column_width = l;
684 }
685 column_width += 2; /* min space for columns */
686 ncols = cmdedit_termw / column_width;
687
688 if (ncols > 1) {
689 nrows /= ncols;
690 if (nfiles % ncols)
691 nrows++; /* round up fractionals */
692 } else {
693 ncols = 1;
694 }
695 for (row = 0; row < nrows; row++) {
696 int n = row;
697 int nc;
698
699 for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
700 printf("%s%-*s", matches[n],
701 (int)(column_width - strlen(matches[n])), "");
702 }
703 printf("%s\n", matches[n]);
704 }
705}
706
707static char *add_quote_for_spec_chars(char *found)
708{
709 int l = 0;
710 char *s = xmalloc((strlen(found) + 1) * 2);
711
712 while (*found) {
713 if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
714 s[l++] = '\\';
715 s[l++] = *found++;
716 }
717 s[l] = 0;
718 return s;
719}
720
721static int match_compare(const void *a, const void *b)
722{
723 return strcmp(*(char**)a, *(char**)b);
724}
725
726/* Do TAB completion */
727static void input_tab(int *lastWasTab)
728{
729 if (!(state->flags & TAB_COMPLETION))
730 return;
731
732 if (!*lastWasTab) {
733 char *tmp, *tmp1;
734 int len_found;
735 char matchBuf[MAX_LINELEN];
736 int find_type;
737 int recalc_pos;
738
739 *lastWasTab = TRUE; /* flop trigger */
740
741 /* Make a local copy of the string -- up
742 * to the position of the cursor */
743 tmp = strncpy(matchBuf, command_ps, cursor);
744 tmp[cursor] = '\0';
745
746 find_type = find_match(matchBuf, &recalc_pos);
747
748 /* Free up any memory already allocated */
749 free_tab_completion_data();
750
751#if ENABLE_FEATURE_USERNAME_COMPLETION
752 /* If the word starts with `~' and there is no slash in the word,
753 * then try completing this word as a username. */
754 if (state->flags & USERNAME_COMPLETION)
755 if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
756 username_tab_completion(matchBuf, NULL);
757#endif
758 /* Try to match any executable in our path and everything
759 * in the current working directory */
760 if (!matches)
761 exe_n_cwd_tab_completion(matchBuf, find_type);
762 /* Sort, then remove any duplicates found */
763 if (matches) {
764 int i, n = 0;
765 qsort(matches, num_matches, sizeof(char*), match_compare);
766 for (i = 0; i < num_matches - 1; ++i) {
767 if (matches[i] && matches[i+1]) { /* paranoia */
768 if (strcmp(matches[i], matches[i+1]) == 0) {
769 free(matches[i]);
770 matches[i] = NULL; /* paranoia */
771 } else {
772 matches[n++] = matches[i];
773 }
774 }
775 }
776 matches[n] = matches[i];
777 num_matches = n + 1;
778 }
779 /* Did we find exactly one match? */
780 if (!matches || num_matches > 1) {
781 beep();
782 if (!matches)
783 return; /* not found */
784 /* find minimal match */
785 // ash: yet another failure in trying to achieve "we don't die on OOM"
786 tmp1 = xstrdup(matches[0]);
787 for (tmp = tmp1; *tmp; tmp++)
788 for (len_found = 1; len_found < num_matches; len_found++)
789 if (matches[len_found][(tmp - tmp1)] != *tmp) {
790 *tmp = '\0';
791 break;
792 }
793 if (*tmp1 == '\0') { /* have unique */
794 free(tmp1);
795 return;
796 }
797 tmp = add_quote_for_spec_chars(tmp1);
798 free(tmp1);
799 } else { /* one match */
800 tmp = add_quote_for_spec_chars(matches[0]);
801 /* for next completion current found */
802 *lastWasTab = FALSE;
803
804 len_found = strlen(tmp);
805 if (tmp[len_found-1] != '/') {
806 tmp[len_found] = ' ';
807 tmp[len_found+1] = '\0';
808 }
809 }
810 len_found = strlen(tmp);
811 /* have space to placed match? */
812 if ((len_found - strlen(matchBuf) + command_len) < MAX_LINELEN) {
813 /* before word for match */
814 command_ps[cursor - recalc_pos] = 0;
815 /* save tail line */
816 strcpy(matchBuf, command_ps + cursor);
817 /* add match */
818 strcat(command_ps, tmp);
819 /* add tail */
820 strcat(command_ps, matchBuf);
821 /* back to begin word for match */
822 input_backward(recalc_pos);
823 /* new pos */
824 recalc_pos = cursor + len_found;
825 /* new len */
826 command_len = strlen(command_ps);
827 /* write out the matched command */
828 redraw(cmdedit_y, command_len - recalc_pos);
829 }
830 free(tmp);
831 } else {
832 /* Ok -- the last char was a TAB. Since they
833 * just hit TAB again, print a list of all the
834 * available choices... */
835 if (matches && num_matches > 0) {
836 int sav_cursor = cursor; /* change goto_new_line() */
837
838 /* Go to the next line */
839 goto_new_line();
840 showfiles();
841 redraw(0, command_len - sav_cursor);
842 }
843 }
844}
845
846#else
847#define input_tab(a) ((void)0)
848#endif /* FEATURE_COMMAND_TAB_COMPLETION */
849
850
851#if MAX_HISTORY > 0
852
853/* state->flags is already checked to be nonzero */
854static void get_previous_history(void)
855{
856 if (command_ps[0] != '\0' || state->history[state->cur_history] == NULL) {
857 free(state->history[state->cur_history]);
858 state->history[state->cur_history] = xstrdup(command_ps);
859 }
860 state->cur_history--;
861}
862
863static int get_next_history(void)
864{
865 if (state->flags & DO_HISTORY) {
866 int ch = state->cur_history;
867 if (ch < state->cnt_history) {
868 get_previous_history(); /* save the current history line */
869 state->cur_history = ch + 1;
870 return state->cur_history;
871 }
872 }
873 beep();
874 return 0;
875}
876
877#if ENABLE_FEATURE_EDITING_SAVEHISTORY
878/* state->flags is already checked to be nonzero */
879static void load_history(const char *fromfile)
880{
881 FILE *fp;
882 int hi;
883
884 /* cleanup old */
885 for (hi = state->cnt_history; hi > 0;) {
886 hi--;
887 free(state->history[hi]);
888 }
889
890 fp = fopen(fromfile, "r");
891 if (fp) {
892 for (hi = 0; hi < MAX_HISTORY;) {
893 char *hl = xmalloc_getline(fp);
894 int l;
895
896 if (!hl)
897 break;
898 l = strlen(hl);
899 if (l >= MAX_LINELEN)
900 hl[MAX_LINELEN-1] = '\0';
901 if (l == 0 || hl[0] == ' ') {
902 free(hl);
903 continue;
904 }
905 state->history[hi++] = hl;
906 }
907 fclose(fp);
908 }
909 state->cur_history = state->cnt_history = hi;
910}
911
912/* state->flags is already checked to be nonzero */
913static void save_history(const char *tofile)
914{
915 FILE *fp;
916
917 fp = fopen(tofile, "w");
918 if (fp) {
919 int i;
920
921 for (i = 0; i < state->cnt_history; i++) {
922 fprintf(fp, "%s\n", state->history[i]);
923 }
924 fclose(fp);
925 }
926}
927#else
928#define load_history(a) ((void)0)
929#define save_history(a) ((void)0)
930#endif /* FEATURE_COMMAND_SAVEHISTORY */
931
932static void remember_in_history(const char *str)
933{
934 int i;
935
936 if (!(state->flags & DO_HISTORY))
937 return;
938
939 i = state->cnt_history;
940 free(state->history[MAX_HISTORY]);
941 state->history[MAX_HISTORY] = NULL;
942 /* After max history, remove the oldest command */
943 if (i >= MAX_HISTORY) {
944 free(state->history[0]);
945 for (i = 0; i < MAX_HISTORY-1; i++)
946 state->history[i] = state->history[i+1];
947 }
948// Maybe "if (!i || strcmp(history[i-1], command) != 0) ..."
949// (i.e. do not save dups?)
950 state->history[i++] = xstrdup(str);
951 state->cur_history = i;
952 state->cnt_history = i;
953#if ENABLE_FEATURE_EDITING_SAVEHISTORY
954 if ((state->flags & SAVE_HISTORY) && state->hist_file)
955 save_history(state->hist_file);
956#endif
957 USE_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
958}
959
960#else /* MAX_HISTORY == 0 */
961#define remember_in_history(a) ((void)0)
962#endif /* MAX_HISTORY */
963
964
965/*
966 * This function is used to grab a character buffer
967 * from the input file descriptor and allows you to
968 * a string with full command editing (sort of like
969 * a mini readline).
970 *
971 * The following standard commands are not implemented:
972 * ESC-b -- Move back one word
973 * ESC-f -- Move forward one word
974 * ESC-d -- Delete back one word
975 * ESC-h -- Delete forward one word
976 * CTL-t -- Transpose two characters
977 *
978 * Minimalist vi-style command line editing available if configured.
979 * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
980 */
981
982#if ENABLE_FEATURE_EDITING_VI
983static void
984vi_Word_motion(char *command, int eat)
985{
986 while (cursor < command_len && !isspace(command[cursor]))
987 input_forward();
988 if (eat) while (cursor < command_len && isspace(command[cursor]))
989 input_forward();
990}
991
992static void
993vi_word_motion(char *command, int eat)
994{
995 if (isalnum(command[cursor]) || command[cursor] == '_') {
996 while (cursor < command_len
997 && (isalnum(command[cursor+1]) || command[cursor+1] == '_'))
998 input_forward();
999 } else if (ispunct(command[cursor])) {
1000 while (cursor < command_len && ispunct(command[cursor+1]))
1001 input_forward();
1002 }
1003
1004 if (cursor < command_len)
1005 input_forward();
1006
1007 if (eat && cursor < command_len && isspace(command[cursor]))
1008 while (cursor < command_len && isspace(command[cursor]))
1009 input_forward();
1010}
1011
1012static void
1013vi_End_motion(char *command)
1014{
1015 input_forward();
1016 while (cursor < command_len && isspace(command[cursor]))
1017 input_forward();
1018 while (cursor < command_len-1 && !isspace(command[cursor+1]))
1019 input_forward();
1020}
1021
1022static void
1023vi_end_motion(char *command)
1024{
1025 if (cursor >= command_len-1)
1026 return;
1027 input_forward();
1028 while (cursor < command_len-1 && isspace(command[cursor]))
1029 input_forward();
1030 if (cursor >= command_len-1)
1031 return;
1032 if (isalnum(command[cursor]) || command[cursor] == '_') {
1033 while (cursor < command_len-1
1034 && (isalnum(command[cursor+1]) || command[cursor+1] == '_')
1035 ) {
1036 input_forward();
1037 }
1038 } else if (ispunct(command[cursor])) {
1039 while (cursor < command_len-1 && ispunct(command[cursor+1]))
1040 input_forward();
1041 }
1042}
1043
1044static void
1045vi_Back_motion(char *command)
1046{
1047 while (cursor > 0 && isspace(command[cursor-1]))
1048 input_backward(1);
1049 while (cursor > 0 && !isspace(command[cursor-1]))
1050 input_backward(1);
1051}
1052
1053static void
1054vi_back_motion(char *command)
1055{
1056 if (cursor <= 0)
1057 return;
1058 input_backward(1);
1059 while (cursor > 0 && isspace(command[cursor]))
1060 input_backward(1);
1061 if (cursor <= 0)
1062 return;
1063 if (isalnum(command[cursor]) || command[cursor] == '_') {
1064 while (cursor > 0
1065 && (isalnum(command[cursor-1]) || command[cursor-1] == '_')
1066 ) {
1067 input_backward(1);
1068 }
1069 } else if (ispunct(command[cursor])) {
1070 while (cursor > 0 && ispunct(command[cursor-1]))
1071 input_backward(1);
1072 }
1073}
1074#endif
1075
1076
1077/*
1078 * read_line_input and its helpers
1079 */
1080
1081#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
1082static void parse_prompt(const char *prmt_ptr)
1083{
1084 cmdedit_prompt = prmt_ptr;
1085 cmdedit_prmt_len = strlen(prmt_ptr);
1086 put_prompt();
1087}
1088#else
1089static void parse_prompt(const char *prmt_ptr)
1090{
1091 int prmt_len = 0;
1092 size_t cur_prmt_len = 0;
1093 char flg_not_length = '[';
1094 char *prmt_mem_ptr = xzalloc(1);
1095 char *pwd_buf = xrealloc_getcwd_or_warn(NULL);
1096 char buf2[PATH_MAX + 1];
1097 char buf[2];
1098 char c;
1099 char *pbuf;
1100
1101 cmdedit_prmt_len = 0;
1102
1103 if (!pwd_buf) {
1104 pwd_buf = (char *)bb_msg_unknown;
1105 }
1106
1107 while (*prmt_ptr) {
1108 pbuf = buf;
1109 pbuf[1] = 0;
1110 c = *prmt_ptr++;
1111 if (c == '\\') {
1112 const char *cp = prmt_ptr;
1113 int l;
1114
1115 c = bb_process_escape_sequence(&prmt_ptr);
1116 if (prmt_ptr == cp) {
1117 if (*cp == 0)
1118 break;
1119 c = *prmt_ptr++;
1120 switch (c) {
1121#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
1122 case 'u':
1123 pbuf = user_buf ? user_buf : (char*)"";
1124 break;
1125#endif
1126 case 'h':
1127 pbuf = hostname_buf;
1128 if (!pbuf) {
1129 pbuf = xzalloc(256);
1130 if (gethostname(pbuf, 255) < 0) {
1131 strcpy(pbuf, "?");
1132 } else {
1133 char *s = strchr(pbuf, '.');
1134 if (s)
1135 *s = '\0';
1136 }
1137 hostname_buf = pbuf;
1138 }
1139 break;
1140 case '$':
1141 c = (geteuid() == 0 ? '#' : '$');
1142 break;
1143#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
1144 case 'w':
1145 pbuf = pwd_buf;
1146 l = strlen(home_pwd_buf);
1147 if (l != 0
1148 && strncmp(home_pwd_buf, pbuf, l) == 0
1149 && (pbuf[l]=='/' || pbuf[l]=='\0')
1150 && strlen(pwd_buf+l)<PATH_MAX
1151 ) {
1152 pbuf = buf2;
1153 *pbuf = '~';
1154 strcpy(pbuf+1, pwd_buf+l);
1155 }
1156 break;
1157#endif
1158 case 'W':
1159 pbuf = pwd_buf;
1160 cp = strrchr(pbuf, '/');
1161 if (cp != NULL && cp != pbuf)
1162 pbuf += (cp-pbuf) + 1;
1163 break;
1164 case '!':
1165 pbuf = buf2;
1166 snprintf(buf2, sizeof(buf2), "%d", num_ok_lines);
1167 break;
1168 case 'e': case 'E': /* \e \E = \033 */
1169 c = '\033';
1170 break;
1171 case 'x': case 'X':
1172 for (l = 0; l < 3;) {
1173 int h;
1174 buf2[l++] = *prmt_ptr;
1175 buf2[l] = 0;
1176 h = strtol(buf2, &pbuf, 16);
1177 if (h > UCHAR_MAX || (pbuf - buf2) < l) {
1178 l--;
1179 break;
1180 }
1181 prmt_ptr++;
1182 }
1183 buf2[l] = 0;
1184 c = (char)strtol(buf2, NULL, 16);
1185 if (c == 0)
1186 c = '?';
1187 pbuf = buf;
1188 break;
1189 case '[': case ']':
1190 if (c == flg_not_length) {
1191 flg_not_length = flg_not_length == '[' ? ']' : '[';
1192 continue;
1193 }
1194 break;
1195 }
1196 }
1197 }
1198 if (pbuf == buf)
1199 *pbuf = c;
1200 cur_prmt_len = strlen(pbuf);
1201 prmt_len += cur_prmt_len;
1202 if (flg_not_length != ']')
1203 cmdedit_prmt_len += cur_prmt_len;
1204 prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
1205 }
1206 if (pwd_buf != (char *)bb_msg_unknown)
1207 free(pwd_buf);
1208 cmdedit_prompt = prmt_mem_ptr;
1209 put_prompt();
1210}
1211#endif
1212
1213#define setTermSettings(fd, argp) tcsetattr(fd, TCSANOW, argp)
1214#define getTermSettings(fd, argp) tcgetattr(fd, argp);
1215
1216static sighandler_t previous_SIGWINCH_handler;
1217
1218static void cmdedit_setwidth(unsigned w, int redraw_flg)
1219{
1220 cmdedit_termw = w;
1221 if (redraw_flg) {
1222 /* new y for current cursor */
1223 int new_y = (cursor + cmdedit_prmt_len) / w;
1224 /* redraw */
1225 redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
1226 fflush(stdout);
1227 }
1228}
1229
1230static void win_changed(int nsig)
1231{
1232 int width;
1233 get_terminal_width_height(0, &width, NULL);
1234 cmdedit_setwidth(width, nsig /* - just a yes/no flag */);
1235 if (nsig == SIGWINCH)
1236 signal(SIGWINCH, win_changed); /* rearm ourself */
1237}
1238
1239/*
1240 * The emacs and vi modes share much of the code in the big
1241 * command loop. Commands entered when in vi's command mode (aka
1242 * "escape mode") get an extra bit added to distinguish them --
1243 * this keeps them from being self-inserted. This clutters the
1244 * big switch a bit, but keeps all the code in one place.
1245 */
1246
1247#define vbit 0x100
1248
1249/* leave out the "vi-mode"-only case labels if vi editing isn't
1250 * configured. */
1251#define vi_case(caselabel) USE_FEATURE_EDITING(case caselabel)
1252
1253/* convert uppercase ascii to equivalent control char, for readability */
1254#undef CTRL
1255#define CTRL(a) ((a) & ~0x40)
1256
1257/* Returns:
1258 * -1 on read errors or EOF, or on bare Ctrl-D.
1259 * 0 on ctrl-C,
1260 * >0 length of input string, including terminating '\n'
1261 */
1262int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *st)
1263{
1264 int lastWasTab = FALSE;
1265 unsigned int ic;
1266 unsigned char c;
1267 smallint break_out = 0;
1268#if ENABLE_FEATURE_EDITING_VI
1269 smallint vi_cmdmode = 0;
1270 smalluint prevc;
1271#endif
1272
1273// FIXME: audit & improve this
1274 if (maxsize > MAX_LINELEN)
1275 maxsize = MAX_LINELEN;
1276
1277 /* With null flags, no other fields are ever used */
1278 state = st ? st : (line_input_t*) &const_int_0;
1279#if ENABLE_FEATURE_EDITING_SAVEHISTORY
1280 if ((state->flags & SAVE_HISTORY) && state->hist_file)
1281 load_history(state->hist_file);
1282#endif
1283
1284 /* prepare before init handlers */
1285 cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */
1286 command_len = 0;
1287 command_ps = command;
1288 command[0] = '\0';
1289
1290 getTermSettings(0, (void *) &initial_settings);
1291 memcpy(&new_settings, &initial_settings, sizeof(new_settings));
1292 new_settings.c_lflag &= ~ICANON; /* unbuffered input */
1293 /* Turn off echoing and CTRL-C, so we can trap it */
1294 new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
1295 /* Hmm, in linux c_cc[] is not parsed if ICANON is off */
1296 new_settings.c_cc[VMIN] = 1;
1297 new_settings.c_cc[VTIME] = 0;
1298 /* Turn off CTRL-C, so we can trap it */
1299#ifndef _POSIX_VDISABLE
1300#define _POSIX_VDISABLE '\0'
1301#endif
1302 new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
1303 setTermSettings(0, (void *) &new_settings);
1304
1305 /* Now initialize things */
1306 previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
1307 win_changed(0); /* do initial resizing */
1308#if ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR
1309 {
1310 struct passwd *entry;
1311
1312 entry = getpwuid(geteuid());
1313 if (entry) {
1314 /* If we enter read_line_input for the Nth time,
1315 * they may be already allocated! Need to free. */
1316 free(user_buf);
1317 if (home_pwd_buf != null_str)
1318 free(home_pwd_buf);
1319 user_buf = xstrdup(entry->pw_name);
1320 home_pwd_buf = xstrdup(entry->pw_dir);
1321 /* They are not freed on exit (too small to bother) */
1322 }
1323 }
1324#endif
1325 /* Print out the command prompt */
1326 parse_prompt(prompt);
1327
1328 while (1) {
1329 fflush(stdout);
1330
1331 if (safe_read(0, &c, 1) < 1) {
1332 /* if we can't read input then exit */
1333 goto prepare_to_die;
1334 }
1335
1336 ic = c;
1337
1338#if ENABLE_FEATURE_EDITING_VI
1339 newdelflag = 1;
1340 if (vi_cmdmode)
1341 ic |= vbit;
1342#endif
1343 switch (ic) {
1344 case '\n':
1345 case '\r':
1346 vi_case('\n'|vbit:)
1347 vi_case('\r'|vbit:)
1348 /* Enter */
1349 goto_new_line();
1350 break_out = 1;
1351 break;
1352#if ENABLE_FEATURE_EDITING_FANCY_KEYS
1353 case CTRL('A'):
1354 vi_case('0'|vbit:)
1355 /* Control-a -- Beginning of line */
1356 input_backward(cursor);
1357 break;
1358 case CTRL('B'):
1359 vi_case('h'|vbit:)
1360 vi_case('\b'|vbit:)
1361 vi_case('\x7f'|vbit:) /* DEL */
1362 /* Control-b -- Move back one character */
1363 input_backward(1);
1364 break;
1365#endif
1366 case CTRL('C'):
1367 vi_case(CTRL('C')|vbit:)
1368 /* Control-c -- stop gathering input */
1369 goto_new_line();
1370 command_len = 0;
1371 break_out = -1; /* "do not append '\n'" */
1372 break;
1373 case CTRL('D'):
1374 /* Control-d -- Delete one character, or exit
1375 * if the len=0 and no chars to delete */
1376 if (command_len == 0) {
1377 errno = 0;
1378 prepare_to_die:
1379 /* to control stopped jobs */
1380 break_out = command_len = -1;
1381 break;
1382 }
1383 input_delete(0);
1384 break;
1385
1386#if ENABLE_FEATURE_EDITING_FANCY_KEYS
1387 case CTRL('E'):
1388 vi_case('$'|vbit:)
1389 /* Control-e -- End of line */
1390 input_end();
1391 break;
1392 case CTRL('F'):
1393 vi_case('l'|vbit:)
1394 vi_case(' '|vbit:)
1395 /* Control-f -- Move forward one character */
1396 input_forward();
1397 break;
1398#endif
1399
1400 case '\b':
1401 case '\x7f': /* DEL */
1402 /* Control-h and DEL */
1403 input_backspace();
1404 break;
1405
1406 case '\t':
1407 input_tab(&lastWasTab);
1408 break;
1409
1410#if ENABLE_FEATURE_EDITING_FANCY_KEYS
1411 case CTRL('K'):
1412 /* Control-k -- clear to end of line */
1413 command[cursor] = 0;
1414 command_len = cursor;
1415 printf("\033[J");
1416 break;
1417 case CTRL('L'):
1418 vi_case(CTRL('L')|vbit:)
1419 /* Control-l -- clear screen */
1420 printf("\033[H");
1421 redraw(0, command_len - cursor);
1422 break;
1423#endif
1424
1425#if MAX_HISTORY > 0
1426 case CTRL('N'):
1427 vi_case(CTRL('N')|vbit:)
1428 vi_case('j'|vbit:)
1429 /* Control-n -- Get next command in history */
1430 if (get_next_history())
1431 goto rewrite_line;
1432 break;
1433 case CTRL('P'):
1434 vi_case(CTRL('P')|vbit:)
1435 vi_case('k'|vbit:)
1436 /* Control-p -- Get previous command from history */
1437 if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
1438 get_previous_history();
1439 goto rewrite_line;
1440 }
1441 beep();
1442 break;
1443#endif
1444
1445#if ENABLE_FEATURE_EDITING_FANCY_KEYS
1446 case CTRL('U'):
1447 vi_case(CTRL('U')|vbit:)
1448 /* Control-U -- Clear line before cursor */
1449 if (cursor) {
1450 strcpy(command, command + cursor);
1451 command_len -= cursor;
1452 redraw(cmdedit_y, command_len);
1453 }
1454 break;
1455#endif
1456 case CTRL('W'):
1457 vi_case(CTRL('W')|vbit:)
1458 /* Control-W -- Remove the last word */
1459 while (cursor > 0 && isspace(command[cursor-1]))
1460 input_backspace();
1461 while (cursor > 0 && !isspace(command[cursor-1]))
1462 input_backspace();
1463 break;
1464
1465#if ENABLE_FEATURE_EDITING_VI
1466 case 'i'|vbit:
1467 vi_cmdmode = 0;
1468 break;
1469 case 'I'|vbit:
1470 input_backward(cursor);
1471 vi_cmdmode = 0;
1472 break;
1473 case 'a'|vbit:
1474 input_forward();
1475 vi_cmdmode = 0;
1476 break;
1477 case 'A'|vbit:
1478 input_end();
1479 vi_cmdmode = 0;
1480 break;
1481 case 'x'|vbit:
1482 input_delete(1);
1483 break;
1484 case 'X'|vbit:
1485 if (cursor > 0) {
1486 input_backward(1);
1487 input_delete(1);
1488 }
1489 break;
1490 case 'W'|vbit:
1491 vi_Word_motion(command, 1);
1492 break;
1493 case 'w'|vbit:
1494 vi_word_motion(command, 1);
1495 break;
1496 case 'E'|vbit:
1497 vi_End_motion(command);
1498 break;
1499 case 'e'|vbit:
1500 vi_end_motion(command);
1501 break;
1502 case 'B'|vbit:
1503 vi_Back_motion(command);
1504 break;
1505 case 'b'|vbit:
1506 vi_back_motion(command);
1507 break;
1508 case 'C'|vbit:
1509 vi_cmdmode = 0;
1510 /* fall through */
1511 case 'D'|vbit:
1512 goto clear_to_eol;
1513
1514 case 'c'|vbit:
1515 vi_cmdmode = 0;
1516 /* fall through */
1517 case 'd'|vbit: {
1518 int nc, sc;
1519 sc = cursor;
1520 prevc = ic;
1521 if (safe_read(0, &c, 1) < 1)
1522 goto prepare_to_die;
1523 if (c == (prevc & 0xff)) {
1524 /* "cc", "dd" */
1525 input_backward(cursor);
1526 goto clear_to_eol;
1527 break;
1528 }
1529 switch (c) {
1530 case 'w':
1531 case 'W':
1532 case 'e':
1533 case 'E':
1534 switch (c) {
1535 case 'w': /* "dw", "cw" */
1536 vi_word_motion(command, vi_cmdmode);
1537 break;
1538 case 'W': /* 'dW', 'cW' */
1539 vi_Word_motion(command, vi_cmdmode);
1540 break;
1541 case 'e': /* 'de', 'ce' */
1542 vi_end_motion(command);
1543 input_forward();
1544 break;
1545 case 'E': /* 'dE', 'cE' */
1546 vi_End_motion(command);
1547 input_forward();
1548 break;
1549 }
1550 nc = cursor;
1551 input_backward(cursor - sc);
1552 while (nc-- > cursor)
1553 input_delete(1);
1554 break;
1555 case 'b': /* "db", "cb" */
1556 case 'B': /* implemented as B */
1557 if (c == 'b')
1558 vi_back_motion(command);
1559 else
1560 vi_Back_motion(command);
1561 while (sc-- > cursor)
1562 input_delete(1);
1563 break;
1564 case ' ': /* "d ", "c " */
1565 input_delete(1);
1566 break;
1567 case '$': /* "d$", "c$" */
1568 clear_to_eol:
1569 while (cursor < command_len)
1570 input_delete(1);
1571 break;
1572 }
1573 break;
1574 }
1575 case 'p'|vbit:
1576 input_forward();
1577 /* fallthrough */
1578 case 'P'|vbit:
1579 put();
1580 break;
1581 case 'r'|vbit:
1582 if (safe_read(0, &c, 1) < 1)
1583 goto prepare_to_die;
1584 if (c == 0)
1585 beep();
1586 else {
1587 *(command + cursor) = c;
1588 putchar(c);
1589 putchar('\b');
1590 }
1591 break;
1592#endif /* FEATURE_COMMAND_EDITING_VI */
1593
1594 case '\x1b': /* ESC */
1595
1596#if ENABLE_FEATURE_EDITING_VI
1597 if (state->flags & VI_MODE) {
1598 /* ESC: insert mode --> command mode */
1599 vi_cmdmode = 1;
1600 input_backward(1);
1601 break;
1602 }
1603#endif
1604 /* escape sequence follows */
1605 if (safe_read(0, &c, 1) < 1)
1606 goto prepare_to_die;
1607 /* different vt100 emulations */
1608 if (c == '[' || c == 'O') {
1609 vi_case('['|vbit:)
1610 vi_case('O'|vbit:)
1611 if (safe_read(0, &c, 1) < 1)
1612 goto prepare_to_die;
1613 }
1614 if (c >= '1' && c <= '9') {
1615 unsigned char dummy;
1616
1617 if (safe_read(0, &dummy, 1) < 1)
1618 goto prepare_to_die;
1619 if (dummy != '~')
1620 c = '\0';
1621 }
1622
1623 switch (c) {
1624#if ENABLE_FEATURE_TAB_COMPLETION
1625 case '\t': /* Alt-Tab */
1626 input_tab(&lastWasTab);
1627 break;
1628#endif
1629#if MAX_HISTORY > 0
1630 case 'A':
1631 /* Up Arrow -- Get previous command from history */
1632 if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
1633 get_previous_history();
1634 goto rewrite_line;
1635 }
1636 beep();
1637 break;
1638 case 'B':
1639 /* Down Arrow -- Get next command in history */
1640 if (!get_next_history())
1641 break;
1642 rewrite_line:
1643 /* Rewrite the line with the selected history item */
1644 /* change command */
1645 command_len = strlen(strcpy(command, state->history[state->cur_history]));
1646 /* redraw and go to eol (bol, in vi */
1647 redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
1648 break;
1649#endif
1650 case 'C':
1651 /* Right Arrow -- Move forward one character */
1652 input_forward();
1653 break;
1654 case 'D':
1655 /* Left Arrow -- Move back one character */
1656 input_backward(1);
1657 break;
1658 case '3':
1659 /* Delete */
1660 input_delete(0);
1661 break;
1662 case '1': // vt100? linux vt? or what?
1663 case '7': // vt100? linux vt? or what?
1664 case 'H': /* xterm's <Home> */
1665 input_backward(cursor);
1666 break;
1667 case '4': // vt100? linux vt? or what?
1668 case '8': // vt100? linux vt? or what?
1669 case 'F': /* xterm's <End> */
1670 input_end();
1671 break;
1672 default:
1673 c = '\0';
1674 beep();
1675 }
1676 break;
1677
1678 default: /* If it's regular input, do the normal thing */
1679#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
1680 /* Control-V -- Add non-printable symbol */
1681 if (c == CTRL('V')) {
1682 if (safe_read(0, &c, 1) < 1)
1683 goto prepare_to_die;
1684 if (c == 0) {
1685 beep();
1686 break;
1687 }
1688 } else
1689#endif
1690
1691#if ENABLE_FEATURE_EDITING_VI
1692 if (vi_cmdmode) /* Don't self-insert */
1693 break;
1694#endif
1695 if (!Isprint(c)) /* Skip non-printable characters */
1696 break;
1697
1698 if (command_len >= (maxsize - 2)) /* Need to leave space for enter */
1699 break;
1700
1701 command_len++;
1702 if (cursor == (command_len - 1)) { /* Append if at the end of the line */
1703 command[cursor] = c;
1704 command[cursor+1] = '\0';
1705 cmdedit_set_out_char(' ');
1706 } else { /* Insert otherwise */
1707 int sc = cursor;
1708
1709 memmove(command + sc + 1, command + sc, command_len - sc);
1710 command[sc] = c;
1711 sc++;
1712 /* rewrite from cursor */
1713 input_end();
1714 /* to prev x pos + 1 */
1715 input_backward(cursor - sc);
1716 }
1717 break;
1718 }
1719 if (break_out) /* Enter is the command terminator, no more input. */
1720 break;
1721
1722 if (c != '\t')
1723 lastWasTab = FALSE;
1724 }
1725
1726 if (command_len > 0)
1727 remember_in_history(command);
1728
1729 if (break_out > 0) {
1730 command[command_len++] = '\n';
1731 command[command_len] = '\0';
1732 }
1733
1734#if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_TAB_COMPLETION
1735 free_tab_completion_data();
1736#endif
1737
1738#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
1739 free((char*)cmdedit_prompt);
1740#endif
1741 /* restore initial_settings */
1742 setTermSettings(STDIN_FILENO, (void *) &initial_settings);
1743 /* restore SIGWINCH handler */
1744 signal(SIGWINCH, previous_SIGWINCH_handler);
1745 fflush(stdout);
1746 return command_len;
1747}
1748
1749line_input_t *new_line_input_t(int flags)
1750{
1751 line_input_t *n = xzalloc(sizeof(*n));
1752 n->flags = flags;
1753 return n;
1754}
1755
1756#else
1757
1758#undef read_line_input
1759int read_line_input(const char* prompt, char* command, int maxsize)
1760{
1761 fputs(prompt, stdout);
1762 fflush(stdout);
1763 fgets(command, maxsize, stdin);
1764 return strlen(command);
1765}
1766
1767#endif /* FEATURE_COMMAND_EDITING */
1768
1769
1770/*
1771 * Testing
1772 */
1773
1774#ifdef TEST
1775
1776#include <locale.h>
1777
1778const char *applet_name = "debug stuff usage";
1779
1780int main(int argc, char **argv)
1781{
1782 char buff[MAX_LINELEN];
1783 char *prompt =
1784#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
1785 "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:"
1786 "\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] "
1787 "\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
1788#else
1789 "% ";
1790#endif
1791
1792#if ENABLE_FEATURE_NONPRINTABLE_INVERSE_PUT
1793 setlocale(LC_ALL, "");
1794#endif
1795 while (1) {
1796 int l;
1797 l = read_line_input(prompt, buff);
1798 if (l <= 0 || buff[l-1] != '\n')
1799 break;
1800 buff[l-1] = 0;
1801 printf("*** read_line_input() returned line =%s=\n", buff);
1802 }
1803 printf("*** read_line_input() detect ^D\n");
1804 return 0;
1805}
1806
1807#endif /* TEST */
Note: See TracBrowser for help on using the repository browser.