3
0

cmdedit.c 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582
  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. /*
  19. Usage and Known bugs:
  20. Terminal key codes are not extensive, and more will probably
  21. need to be added. This version was created on Debian GNU/Linux 2.x.
  22. Delete, Backspace, Home, End, and the arrow keys were tested
  23. to work in an Xterm and console. Ctrl-A also works as Home.
  24. Ctrl-E also works as End.
  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. #include <stdio.h>
  31. #include <errno.h>
  32. #include <unistd.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <sys/ioctl.h>
  36. #include <ctype.h>
  37. #include <signal.h>
  38. #include <limits.h>
  39. #include "busybox.h"
  40. #include "../shell/cmdedit.h"
  41. #ifdef CONFIG_LOCALE_SUPPORT
  42. #define Isprint(c) isprint((c))
  43. #else
  44. #define Isprint(c) ( (c) >= ' ' && (c) != ((unsigned char)'\233') )
  45. #endif
  46. #ifdef TEST
  47. /* pretect redefined for test */
  48. #undef CONFIG_FEATURE_COMMAND_EDITING
  49. #undef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
  50. #undef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
  51. #undef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
  52. #undef CONFIG_FEATURE_CLEAN_UP
  53. #define CONFIG_FEATURE_COMMAND_EDITING
  54. #define CONFIG_FEATURE_COMMAND_TAB_COMPLETION
  55. #define CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
  56. #define CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
  57. #define CONFIG_FEATURE_CLEAN_UP
  58. #endif /* TEST */
  59. #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
  60. #include <dirent.h>
  61. #include <sys/stat.h>
  62. #endif
  63. #ifdef CONFIG_FEATURE_COMMAND_EDITING
  64. #if defined(CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION) || defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
  65. #define CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
  66. #endif
  67. #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
  68. #include "pwd_.h"
  69. #endif /* advanced FEATURES */
  70. /* Maximum length of the linked list for the command line history */
  71. #ifndef CONFIG_FEATURE_COMMAND_HISTORY
  72. #define MAX_HISTORY 15
  73. #else
  74. #define MAX_HISTORY CONFIG_FEATURE_COMMAND_HISTORY
  75. #endif
  76. #if MAX_HISTORY < 1
  77. #warning cmdedit: You set MAX_HISTORY < 1. The history algorithm switched off.
  78. #else
  79. static char *history[MAX_HISTORY+1]; /* history + current */
  80. /* saved history lines */
  81. static int n_history;
  82. /* current pointer to history line */
  83. static int cur_history;
  84. #endif
  85. #include <termios.h>
  86. #define setTermSettings(fd,argp) tcsetattr(fd,TCSANOW,argp)
  87. #define getTermSettings(fd,argp) tcgetattr(fd, argp);
  88. /* Current termio and the previous termio before starting sh */
  89. static struct termios initial_settings, new_settings;
  90. static
  91. volatile int cmdedit_termw = 80; /* actual terminal width */
  92. static
  93. volatile int handlers_sets = 0; /* Set next bites: */
  94. enum {
  95. SET_ATEXIT = 1, /* when atexit() has been called
  96. and get euid,uid,gid to fast compare */
  97. SET_WCHG_HANDLERS = 2, /* winchg signal handler */
  98. SET_RESET_TERM = 4, /* if the terminal needs to be reset upon exit */
  99. };
  100. static int cmdedit_x; /* real x terminal position */
  101. static int cmdedit_y; /* pseudoreal y terminal position */
  102. static int cmdedit_prmt_len; /* lenght prompt without colores string */
  103. static int cursor; /* required global for signal handler */
  104. static int len; /* --- "" - - "" - -"- --""-- --""--- */
  105. static char *command_ps; /* --- "" - - "" - -"- --""-- --""--- */
  106. static
  107. #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
  108. const
  109. #endif
  110. char *cmdedit_prompt; /* --- "" - - "" - -"- --""-- --""--- */
  111. #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
  112. static char *user_buf = "";
  113. static char *home_pwd_buf = "";
  114. static int my_euid;
  115. #endif
  116. #ifdef CONFIG_FEATURE_SH_FANCY_PROMPT
  117. static char *hostname_buf;
  118. static int num_ok_lines = 1;
  119. #endif
  120. #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
  121. #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
  122. static int my_euid;
  123. #endif
  124. static int my_uid;
  125. static int my_gid;
  126. #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
  127. static void cmdedit_setwidth(int w, int redraw_flg);
  128. static void win_changed(int nsig)
  129. {
  130. static sighandler_t previous_SIGWINCH_handler; /* for reset */
  131. /* emulate || signal call */
  132. if (nsig == -SIGWINCH || nsig == SIGWINCH) {
  133. int width = 0;
  134. get_terminal_width_height(0, &width, NULL);
  135. cmdedit_setwidth(width, nsig == SIGWINCH);
  136. }
  137. /* Unix not all standart in recall signal */
  138. if (nsig == -SIGWINCH) /* save previous handler */
  139. previous_SIGWINCH_handler = signal(SIGWINCH, win_changed);
  140. else if (nsig == SIGWINCH) /* signaled called handler */
  141. signal(SIGWINCH, win_changed); /* set for next call */
  142. else /* nsig == 0 */
  143. /* set previous handler */
  144. signal(SIGWINCH, previous_SIGWINCH_handler); /* reset */
  145. }
  146. static void cmdedit_reset_term(void)
  147. {
  148. if ((handlers_sets & SET_RESET_TERM) != 0) {
  149. /* sparc and other have broken termios support: use old termio handling. */
  150. setTermSettings(STDIN_FILENO, (void *) &initial_settings);
  151. handlers_sets &= ~SET_RESET_TERM;
  152. }
  153. if ((handlers_sets & SET_WCHG_HANDLERS) != 0) {
  154. /* reset SIGWINCH handler to previous (default) */
  155. win_changed(0);
  156. handlers_sets &= ~SET_WCHG_HANDLERS;
  157. }
  158. fflush(stdout);
  159. }
  160. /* special for recount position for scroll and remove terminal margin effect */
  161. static void cmdedit_set_out_char(int next_char)
  162. {
  163. int c = (int)((unsigned char) command_ps[cursor]);
  164. if (c == 0)
  165. c = ' '; /* destroy end char? */
  166. #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
  167. if (!Isprint(c)) { /* Inverse put non-printable characters */
  168. if (c >= 128)
  169. c -= 128;
  170. if (c < ' ')
  171. c += '@';
  172. if (c == 127)
  173. c = '?';
  174. printf("\033[7m%c\033[0m", c);
  175. } else
  176. #endif
  177. putchar(c);
  178. if (++cmdedit_x >= cmdedit_termw) {
  179. /* terminal is scrolled down */
  180. cmdedit_y++;
  181. cmdedit_x = 0;
  182. if (!next_char)
  183. next_char = ' ';
  184. /* destroy "(auto)margin" */
  185. putchar(next_char);
  186. putchar('\b');
  187. }
  188. cursor++;
  189. }
  190. /* Move to end line. Bonus: rewrite line from cursor */
  191. static void input_end(void)
  192. {
  193. while (cursor < len)
  194. cmdedit_set_out_char(0);
  195. }
  196. /* Go to the next line */
  197. static void goto_new_line(void)
  198. {
  199. input_end();
  200. if (cmdedit_x)
  201. putchar('\n');
  202. }
  203. static inline void out1str(const char *s)
  204. {
  205. if ( s )
  206. fputs(s, stdout);
  207. }
  208. static inline void beep(void)
  209. {
  210. putchar('\007');
  211. }
  212. /* Move back one character */
  213. /* special for slow terminal */
  214. static void input_backward(int num)
  215. {
  216. if (num > cursor)
  217. num = cursor;
  218. cursor -= num; /* new cursor (in command, not terminal) */
  219. if (cmdedit_x >= num) { /* no to up line */
  220. cmdedit_x -= num;
  221. if (num < 4)
  222. while (num-- > 0)
  223. putchar('\b');
  224. else
  225. printf("\033[%dD", num);
  226. } else {
  227. int count_y;
  228. if (cmdedit_x) {
  229. putchar('\r'); /* back to first terminal pos. */
  230. num -= cmdedit_x; /* set previous backward */
  231. }
  232. count_y = 1 + num / cmdedit_termw;
  233. printf("\033[%dA", count_y);
  234. cmdedit_y -= count_y;
  235. /* require forward after uping */
  236. cmdedit_x = cmdedit_termw * count_y - num;
  237. printf("\033[%dC", cmdedit_x); /* set term cursor */
  238. }
  239. }
  240. static void put_prompt(void)
  241. {
  242. out1str(cmdedit_prompt);
  243. cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
  244. cursor = 0;
  245. cmdedit_y = 0; /* new quasireal y */
  246. }
  247. #ifndef CONFIG_FEATURE_SH_FANCY_PROMPT
  248. static void parse_prompt(const char *prmt_ptr)
  249. {
  250. cmdedit_prompt = prmt_ptr;
  251. cmdedit_prmt_len = strlen(prmt_ptr);
  252. put_prompt();
  253. }
  254. #else
  255. static void parse_prompt(const char *prmt_ptr)
  256. {
  257. int prmt_len = 0;
  258. int sub_len = 0;
  259. char flg_not_length = '[';
  260. char *prmt_mem_ptr = xcalloc(1, 1);
  261. char *pwd_buf = xgetcwd(0);
  262. char buf2[PATH_MAX + 1];
  263. char buf[2];
  264. char c;
  265. char *pbuf;
  266. if (!pwd_buf) {
  267. pwd_buf=(char *)bb_msg_unknown;
  268. }
  269. while (*prmt_ptr) {
  270. pbuf = buf;
  271. pbuf[1] = 0;
  272. c = *prmt_ptr++;
  273. if (c == '\\') {
  274. const char *cp = prmt_ptr;
  275. int l;
  276. c = bb_process_escape_sequence(&prmt_ptr);
  277. if(prmt_ptr==cp) {
  278. if (*cp == 0)
  279. break;
  280. c = *prmt_ptr++;
  281. switch (c) {
  282. #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
  283. case 'u':
  284. pbuf = user_buf;
  285. break;
  286. #endif
  287. case 'h':
  288. pbuf = hostname_buf;
  289. if (pbuf == 0) {
  290. pbuf = xcalloc(256, 1);
  291. if (gethostname(pbuf, 255) < 0) {
  292. strcpy(pbuf, "?");
  293. } else {
  294. char *s = strchr(pbuf, '.');
  295. if (s)
  296. *s = 0;
  297. }
  298. hostname_buf = pbuf;
  299. }
  300. break;
  301. case '$':
  302. c = my_euid == 0 ? '#' : '$';
  303. break;
  304. #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
  305. case 'w':
  306. pbuf = pwd_buf;
  307. l = strlen(home_pwd_buf);
  308. if (home_pwd_buf[0] != 0 &&
  309. strncmp(home_pwd_buf, pbuf, l) == 0 &&
  310. (pbuf[l]=='/' || pbuf[l]=='\0') &&
  311. strlen(pwd_buf+l)<PATH_MAX) {
  312. pbuf = buf2;
  313. *pbuf = '~';
  314. strcpy(pbuf+1, pwd_buf+l);
  315. }
  316. break;
  317. #endif
  318. case 'W':
  319. pbuf = pwd_buf;
  320. cp = strrchr(pbuf,'/');
  321. if ( (cp != NULL) && (cp != pbuf) )
  322. pbuf += (cp-pbuf)+1;
  323. break;
  324. case '!':
  325. snprintf(pbuf = buf2, sizeof(buf2), "%d", num_ok_lines);
  326. break;
  327. case 'e': case 'E': /* \e \E = \033 */
  328. c = '\033';
  329. break;
  330. case 'x': case 'X':
  331. for (l = 0; l < 3;) {
  332. int h;
  333. buf2[l++] = *prmt_ptr;
  334. buf2[l] = 0;
  335. h = strtol(buf2, &pbuf, 16);
  336. if (h > UCHAR_MAX || (pbuf - buf2) < l) {
  337. l--;
  338. break;
  339. }
  340. prmt_ptr++;
  341. }
  342. buf2[l] = 0;
  343. c = (char)strtol(buf2, 0, 16);
  344. if(c==0)
  345. c = '?';
  346. pbuf = buf;
  347. break;
  348. case '[': case ']':
  349. if (c == flg_not_length) {
  350. flg_not_length = flg_not_length == '[' ? ']' : '[';
  351. continue;
  352. }
  353. break;
  354. }
  355. }
  356. }
  357. if(pbuf == buf)
  358. *pbuf = c;
  359. prmt_len += strlen(pbuf);
  360. prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
  361. if (flg_not_length == ']')
  362. sub_len++;
  363. }
  364. if(pwd_buf!=(char *)bb_msg_unknown)
  365. free(pwd_buf);
  366. cmdedit_prompt = prmt_mem_ptr;
  367. cmdedit_prmt_len = prmt_len - sub_len;
  368. put_prompt();
  369. }
  370. #endif
  371. /* draw prompt, editor line, and clear tail */
  372. static void redraw(int y, int back_cursor)
  373. {
  374. if (y > 0) /* up to start y */
  375. printf("\033[%dA", y);
  376. putchar('\r');
  377. put_prompt();
  378. input_end(); /* rewrite */
  379. printf("\033[J"); /* destroy tail after cursor */
  380. input_backward(back_cursor);
  381. }
  382. /* Delete the char in front of the cursor */
  383. static void input_delete(void)
  384. {
  385. int j = cursor;
  386. if (j == len)
  387. return;
  388. strcpy(command_ps + j, command_ps + j + 1);
  389. len--;
  390. input_end(); /* rewtite new line */
  391. cmdedit_set_out_char(0); /* destroy end char */
  392. input_backward(cursor - j); /* back to old pos cursor */
  393. }
  394. /* Delete the char in back of the cursor */
  395. static void input_backspace(void)
  396. {
  397. if (cursor > 0) {
  398. input_backward(1);
  399. input_delete();
  400. }
  401. }
  402. /* Move forward one character */
  403. static void input_forward(void)
  404. {
  405. if (cursor < len)
  406. cmdedit_set_out_char(command_ps[cursor + 1]);
  407. }
  408. static void cmdedit_setwidth(int w, int redraw_flg)
  409. {
  410. cmdedit_termw = cmdedit_prmt_len + 2;
  411. if (w <= cmdedit_termw) {
  412. cmdedit_termw = cmdedit_termw % w;
  413. }
  414. if (w > cmdedit_termw) {
  415. cmdedit_termw = w;
  416. if (redraw_flg) {
  417. /* new y for current cursor */
  418. int new_y = (cursor + cmdedit_prmt_len) / w;
  419. /* redraw */
  420. redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
  421. fflush(stdout);
  422. }
  423. }
  424. }
  425. static void cmdedit_init(void)
  426. {
  427. cmdedit_prmt_len = 0;
  428. if ((handlers_sets & SET_WCHG_HANDLERS) == 0) {
  429. /* emulate usage handler to set handler and call yours work */
  430. win_changed(-SIGWINCH);
  431. handlers_sets |= SET_WCHG_HANDLERS;
  432. }
  433. if ((handlers_sets & SET_ATEXIT) == 0) {
  434. #ifdef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
  435. struct passwd *entry;
  436. my_euid = geteuid();
  437. entry = getpwuid(my_euid);
  438. if (entry) {
  439. user_buf = bb_xstrdup(entry->pw_name);
  440. home_pwd_buf = bb_xstrdup(entry->pw_dir);
  441. }
  442. #endif
  443. #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
  444. #ifndef CONFIG_FEATURE_GETUSERNAME_AND_HOMEDIR
  445. my_euid = geteuid();
  446. #endif
  447. my_uid = getuid();
  448. my_gid = getgid();
  449. #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
  450. handlers_sets |= SET_ATEXIT;
  451. atexit(cmdedit_reset_term); /* be sure to do this only once */
  452. }
  453. }
  454. #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
  455. static int is_execute(const struct stat *st)
  456. {
  457. if ((!my_euid && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
  458. (my_uid == st->st_uid && (st->st_mode & S_IXUSR)) ||
  459. (my_gid == st->st_gid && (st->st_mode & S_IXGRP)) ||
  460. (st->st_mode & S_IXOTH)) return TRUE;
  461. return FALSE;
  462. }
  463. #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
  464. static char **username_tab_completion(char *ud, int *num_matches)
  465. {
  466. struct passwd *entry;
  467. int userlen;
  468. char *temp;
  469. ud++; /* ~user/... to user/... */
  470. userlen = strlen(ud);
  471. if (num_matches == 0) { /* "~/..." or "~user/..." */
  472. char *sav_ud = ud - 1;
  473. char *home = 0;
  474. if (*ud == '/') { /* "~/..." */
  475. home = home_pwd_buf;
  476. } else {
  477. /* "~user/..." */
  478. temp = strchr(ud, '/');
  479. *temp = 0; /* ~user\0 */
  480. entry = getpwnam(ud);
  481. *temp = '/'; /* restore ~user/... */
  482. ud = temp;
  483. if (entry)
  484. home = entry->pw_dir;
  485. }
  486. if (home) {
  487. if ((userlen + strlen(home) + 1) < BUFSIZ) {
  488. char temp2[BUFSIZ]; /* argument size */
  489. /* /home/user/... */
  490. sprintf(temp2, "%s%s", home, ud);
  491. strcpy(sav_ud, temp2);
  492. }
  493. }
  494. return 0; /* void, result save to argument :-) */
  495. } else {
  496. /* "~[^/]*" */
  497. char **matches = (char **) NULL;
  498. int nm = 0;
  499. setpwent();
  500. while ((entry = getpwent()) != NULL) {
  501. /* Null usernames should result in all users as possible completions. */
  502. if ( /*!userlen || */ !strncmp(ud, entry->pw_name, userlen)) {
  503. bb_xasprintf(&temp, "~%s/", entry->pw_name);
  504. matches = xrealloc(matches, (nm + 1) * sizeof(char *));
  505. matches[nm++] = temp;
  506. }
  507. }
  508. endpwent();
  509. (*num_matches) = nm;
  510. return (matches);
  511. }
  512. }
  513. #endif /* CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION */
  514. enum {
  515. FIND_EXE_ONLY = 0,
  516. FIND_DIR_ONLY = 1,
  517. FIND_FILE_ONLY = 2,
  518. };
  519. #ifdef CONFIG_ASH
  520. const char *cmdedit_path_lookup;
  521. #else
  522. #define cmdedit_path_lookup getenv("PATH")
  523. #endif
  524. static int path_parse(char ***p, int flags)
  525. {
  526. int npth;
  527. const char *tmp;
  528. const char *pth;
  529. /* if not setenv PATH variable, to search cur dir "." */
  530. if (flags != FIND_EXE_ONLY || (pth = cmdedit_path_lookup) == 0 ||
  531. /* PATH=<empty> or PATH=:<empty> */
  532. *pth == 0 || (*pth == ':' && *(pth + 1) == 0)) {
  533. return 1;
  534. }
  535. tmp = pth;
  536. npth = 0;
  537. for (;;) {
  538. npth++; /* count words is + 1 count ':' */
  539. tmp = strchr(tmp, ':');
  540. if (tmp) {
  541. if (*++tmp == 0)
  542. break; /* :<empty> */
  543. } else
  544. break;
  545. }
  546. *p = xmalloc(npth * sizeof(char *));
  547. tmp = pth;
  548. (*p)[0] = bb_xstrdup(tmp);
  549. npth = 1; /* count words is + 1 count ':' */
  550. for (;;) {
  551. tmp = strchr(tmp, ':');
  552. if (tmp) {
  553. (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
  554. if (*++tmp == 0)
  555. break; /* :<empty> */
  556. } else
  557. break;
  558. (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
  559. }
  560. return npth;
  561. }
  562. static char *add_quote_for_spec_chars(char *found)
  563. {
  564. int l = 0;
  565. char *s = xmalloc((strlen(found) + 1) * 2);
  566. while (*found) {
  567. if (strchr(" `\"#$%^&*()=+{}[]:;\'|\\<>", *found))
  568. s[l++] = '\\';
  569. s[l++] = *found++;
  570. }
  571. s[l] = 0;
  572. return s;
  573. }
  574. static char **exe_n_cwd_tab_completion(char *command, int *num_matches,
  575. int type)
  576. {
  577. char **matches = 0;
  578. DIR *dir;
  579. struct dirent *next;
  580. char dirbuf[BUFSIZ];
  581. int nm = *num_matches;
  582. struct stat st;
  583. char *path1[1];
  584. char **paths = path1;
  585. int npaths;
  586. int i;
  587. char *found;
  588. char *pfind = strrchr(command, '/');
  589. path1[0] = ".";
  590. if (pfind == NULL) {
  591. /* no dir, if flags==EXE_ONLY - get paths, else "." */
  592. npaths = path_parse(&paths, type);
  593. pfind = command;
  594. } else {
  595. /* with dir */
  596. /* save for change */
  597. strcpy(dirbuf, command);
  598. /* set dir only */
  599. dirbuf[(pfind - command) + 1] = 0;
  600. #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
  601. if (dirbuf[0] == '~') /* ~/... or ~user/... */
  602. username_tab_completion(dirbuf, 0);
  603. #endif
  604. /* "strip" dirname in command */
  605. pfind++;
  606. paths[0] = dirbuf;
  607. npaths = 1; /* only 1 dir */
  608. }
  609. for (i = 0; i < npaths; i++) {
  610. dir = opendir(paths[i]);
  611. if (!dir) /* Don't print an error */
  612. continue;
  613. while ((next = readdir(dir)) != NULL) {
  614. char *str_found = next->d_name;
  615. /* matched ? */
  616. if (strncmp(str_found, pfind, strlen(pfind)))
  617. continue;
  618. /* not see .name without .match */
  619. if (*str_found == '.' && *pfind == 0) {
  620. if (*paths[i] == '/' && paths[i][1] == 0
  621. && str_found[1] == 0) str_found = ""; /* only "/" */
  622. else
  623. continue;
  624. }
  625. found = concat_path_file(paths[i], str_found);
  626. /* hmm, remover in progress? */
  627. if (stat(found, &st) < 0)
  628. goto cont;
  629. /* find with dirs ? */
  630. if (paths[i] != dirbuf)
  631. strcpy(found, next->d_name); /* only name */
  632. if (S_ISDIR(st.st_mode)) {
  633. /* name is directory */
  634. str_found = found;
  635. found = concat_path_file(found, "");
  636. free(str_found);
  637. str_found = add_quote_for_spec_chars(found);
  638. } else {
  639. /* not put found file if search only dirs for cd */
  640. if (type == FIND_DIR_ONLY)
  641. goto cont;
  642. str_found = add_quote_for_spec_chars(found);
  643. if (type == FIND_FILE_ONLY ||
  644. (type == FIND_EXE_ONLY && is_execute(&st)))
  645. strcat(str_found, " ");
  646. }
  647. /* Add it to the list */
  648. matches = xrealloc(matches, (nm + 1) * sizeof(char *));
  649. matches[nm++] = str_found;
  650. cont:
  651. free(found);
  652. }
  653. closedir(dir);
  654. }
  655. if (paths != path1) {
  656. free(paths[0]); /* allocated memory only in first member */
  657. free(paths);
  658. }
  659. *num_matches = nm;
  660. return (matches);
  661. }
  662. static int match_compare(const void *a, const void *b)
  663. {
  664. return strcmp(*(char **) a, *(char **) b);
  665. }
  666. #define QUOT (UCHAR_MAX+1)
  667. #define collapse_pos(is, in) { \
  668. memcpy(int_buf+(is), int_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); \
  669. memcpy(pos_buf+(is), pos_buf+(in), (BUFSIZ+1-(is)-(in))*sizeof(int)); }
  670. static int find_match(char *matchBuf, int *len_with_quotes)
  671. {
  672. int i, j;
  673. int command_mode;
  674. int c, c2;
  675. int int_buf[BUFSIZ + 1];
  676. int pos_buf[BUFSIZ + 1];
  677. /* set to integer dimension characters and own positions */
  678. for (i = 0;; i++) {
  679. int_buf[i] = (int) ((unsigned char) matchBuf[i]);
  680. if (int_buf[i] == 0) {
  681. pos_buf[i] = -1; /* indicator end line */
  682. break;
  683. } else
  684. pos_buf[i] = i;
  685. }
  686. /* mask \+symbol and convert '\t' to ' ' */
  687. for (i = j = 0; matchBuf[i]; i++, j++)
  688. if (matchBuf[i] == '\\') {
  689. collapse_pos(j, j + 1);
  690. int_buf[j] |= QUOT;
  691. i++;
  692. #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
  693. if (matchBuf[i] == '\t') /* algorithm equivalent */
  694. int_buf[j] = ' ' | QUOT;
  695. #endif
  696. }
  697. #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
  698. else if (matchBuf[i] == '\t')
  699. int_buf[j] = ' ';
  700. #endif
  701. /* mask "symbols" or 'symbols' */
  702. c2 = 0;
  703. for (i = 0; int_buf[i]; i++) {
  704. c = int_buf[i];
  705. if (c == '\'' || c == '"') {
  706. if (c2 == 0)
  707. c2 = c;
  708. else {
  709. if (c == c2)
  710. c2 = 0;
  711. else
  712. int_buf[i] |= QUOT;
  713. }
  714. } else if (c2 != 0 && c != '$')
  715. int_buf[i] |= QUOT;
  716. }
  717. /* skip commands with arguments if line have commands delimiters */
  718. /* ';' ';;' '&' '|' '&&' '||' but `>&' `<&' `>|' */
  719. for (i = 0; int_buf[i]; i++) {
  720. c = int_buf[i];
  721. c2 = int_buf[i + 1];
  722. j = i ? int_buf[i - 1] : -1;
  723. command_mode = 0;
  724. if (c == ';' || c == '&' || c == '|') {
  725. command_mode = 1 + (c == c2);
  726. if (c == '&') {
  727. if (j == '>' || j == '<')
  728. command_mode = 0;
  729. } else if (c == '|' && j == '>')
  730. command_mode = 0;
  731. }
  732. if (command_mode) {
  733. collapse_pos(0, i + command_mode);
  734. i = -1; /* hack incremet */
  735. }
  736. }
  737. /* collapse `command...` */
  738. for (i = 0; int_buf[i]; i++)
  739. if (int_buf[i] == '`') {
  740. for (j = i + 1; int_buf[j]; j++)
  741. if (int_buf[j] == '`') {
  742. collapse_pos(i, j + 1);
  743. j = 0;
  744. break;
  745. }
  746. if (j) {
  747. /* not found close ` - command mode, collapse all previous */
  748. collapse_pos(0, i + 1);
  749. break;
  750. } else
  751. i--; /* hack incremet */
  752. }
  753. /* collapse (command...(command...)...) or {command...{command...}...} */
  754. c = 0; /* "recursive" level */
  755. c2 = 0;
  756. for (i = 0; int_buf[i]; i++)
  757. if (int_buf[i] == '(' || int_buf[i] == '{') {
  758. if (int_buf[i] == '(')
  759. c++;
  760. else
  761. c2++;
  762. collapse_pos(0, i + 1);
  763. i = -1; /* hack incremet */
  764. }
  765. for (i = 0; pos_buf[i] >= 0 && (c > 0 || c2 > 0); i++)
  766. if ((int_buf[i] == ')' && c > 0) || (int_buf[i] == '}' && c2 > 0)) {
  767. if (int_buf[i] == ')')
  768. c--;
  769. else
  770. c2--;
  771. collapse_pos(0, i + 1);
  772. i = -1; /* hack incremet */
  773. }
  774. /* skip first not quote space */
  775. for (i = 0; int_buf[i]; i++)
  776. if (int_buf[i] != ' ')
  777. break;
  778. if (i)
  779. collapse_pos(0, i);
  780. /* set find mode for completion */
  781. command_mode = FIND_EXE_ONLY;
  782. for (i = 0; int_buf[i]; i++)
  783. if (int_buf[i] == ' ' || int_buf[i] == '<' || int_buf[i] == '>') {
  784. if (int_buf[i] == ' ' && command_mode == FIND_EXE_ONLY
  785. && matchBuf[pos_buf[0]]=='c'
  786. && matchBuf[pos_buf[1]]=='d' )
  787. command_mode = FIND_DIR_ONLY;
  788. else {
  789. command_mode = FIND_FILE_ONLY;
  790. break;
  791. }
  792. }
  793. /* "strlen" */
  794. for (i = 0; int_buf[i]; i++);
  795. /* find last word */
  796. for (--i; i >= 0; i--) {
  797. c = int_buf[i];
  798. if (c == ' ' || c == '<' || c == '>' || c == '|' || c == '&') {
  799. collapse_pos(0, i + 1);
  800. break;
  801. }
  802. }
  803. /* skip first not quoted '\'' or '"' */
  804. for (i = 0; int_buf[i] == '\'' || int_buf[i] == '"'; i++);
  805. /* collapse quote or unquote // or /~ */
  806. while ((int_buf[i] & ~QUOT) == '/' &&
  807. ((int_buf[i + 1] & ~QUOT) == '/'
  808. || (int_buf[i + 1] & ~QUOT) == '~')) {
  809. i++;
  810. }
  811. /* set only match and destroy quotes */
  812. j = 0;
  813. for (c = 0; pos_buf[i] >= 0; i++) {
  814. matchBuf[c++] = matchBuf[pos_buf[i]];
  815. j = pos_buf[i] + 1;
  816. }
  817. matchBuf[c] = 0;
  818. /* old lenght matchBuf with quotes symbols */
  819. *len_with_quotes = j ? j - pos_buf[0] : 0;
  820. return command_mode;
  821. }
  822. /*
  823. display by column original ideas from ls applet,
  824. very optimize by my :)
  825. */
  826. static void showfiles(char **matches, int nfiles)
  827. {
  828. int ncols, row;
  829. int column_width = 0;
  830. int nrows = nfiles;
  831. /* find the longest file name- use that as the column width */
  832. for (row = 0; row < nrows; row++) {
  833. int l = strlen(matches[row]);
  834. if (column_width < l)
  835. column_width = l;
  836. }
  837. column_width += 2; /* min space for columns */
  838. ncols = cmdedit_termw / column_width;
  839. if (ncols > 1) {
  840. nrows /= ncols;
  841. if(nfiles % ncols)
  842. nrows++; /* round up fractionals */
  843. column_width = -column_width; /* for printf("%-Ns", ...); */
  844. } else {
  845. ncols = 1;
  846. }
  847. for (row = 0; row < nrows; row++) {
  848. int n = row;
  849. int nc;
  850. for(nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++)
  851. printf("%*s", column_width, matches[n]);
  852. printf("%s\n", matches[n]);
  853. }
  854. }
  855. static void input_tab(int *lastWasTab)
  856. {
  857. /* Do TAB completion */
  858. static int num_matches;
  859. static char **matches;
  860. if (lastWasTab == 0) { /* free all memory */
  861. if (matches) {
  862. while (num_matches > 0)
  863. free(matches[--num_matches]);
  864. free(matches);
  865. matches = (char **) NULL;
  866. }
  867. return;
  868. }
  869. if (! *lastWasTab) {
  870. char *tmp;
  871. int len_found;
  872. char matchBuf[BUFSIZ];
  873. int find_type;
  874. int recalc_pos;
  875. *lastWasTab = TRUE; /* flop trigger */
  876. /* Make a local copy of the string -- up
  877. * to the position of the cursor */
  878. tmp = strncpy(matchBuf, command_ps, cursor);
  879. tmp[cursor] = 0;
  880. find_type = find_match(matchBuf, &recalc_pos);
  881. /* Free up any memory already allocated */
  882. input_tab(0);
  883. #ifdef CONFIG_FEATURE_COMMAND_USERNAME_COMPLETION
  884. /* If the word starts with `~' and there is no slash in the word,
  885. * then try completing this word as a username. */
  886. if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
  887. matches = username_tab_completion(matchBuf, &num_matches);
  888. #endif
  889. /* Try to match any executable in our path and everything
  890. * in the current working directory that matches. */
  891. if (!matches)
  892. matches =
  893. exe_n_cwd_tab_completion(matchBuf,
  894. &num_matches, find_type);
  895. /* Remove duplicate found */
  896. if(matches) {
  897. int i, j;
  898. /* bubble */
  899. for(i=0; i<(num_matches-1); i++)
  900. for(j=i+1; j<num_matches; j++)
  901. if(matches[i]!=0 && matches[j]!=0 &&
  902. strcmp(matches[i], matches[j])==0) {
  903. free(matches[j]);
  904. matches[j]=0;
  905. }
  906. j=num_matches;
  907. num_matches = 0;
  908. for(i=0; i<j; i++)
  909. if(matches[i]) {
  910. if(!strcmp(matches[i], "./"))
  911. matches[i][1]=0;
  912. else if(!strcmp(matches[i], "../"))
  913. matches[i][2]=0;
  914. matches[num_matches++]=matches[i];
  915. }
  916. }
  917. /* Did we find exactly one match? */
  918. if (!matches || num_matches > 1) {
  919. char *tmp1;
  920. beep();
  921. if (!matches)
  922. return; /* not found */
  923. /* sort */
  924. qsort(matches, num_matches, sizeof(char *), match_compare);
  925. /* find minimal match */
  926. tmp = bb_xstrdup(matches[0]);
  927. for (tmp1 = tmp; *tmp1; tmp1++)
  928. for (len_found = 1; len_found < num_matches; len_found++)
  929. if (matches[len_found][(tmp1 - tmp)] != *tmp1) {
  930. *tmp1 = 0;
  931. break;
  932. }
  933. if (*tmp == 0) { /* have unique */
  934. free(tmp);
  935. return;
  936. }
  937. } else { /* one match */
  938. tmp = matches[0];
  939. /* for next completion current found */
  940. *lastWasTab = FALSE;
  941. }
  942. len_found = strlen(tmp);
  943. /* have space to placed match? */
  944. if ((len_found - strlen(matchBuf) + len) < BUFSIZ) {
  945. /* before word for match */
  946. command_ps[cursor - recalc_pos] = 0;
  947. /* save tail line */
  948. strcpy(matchBuf, command_ps + cursor);
  949. /* add match */
  950. strcat(command_ps, tmp);
  951. /* add tail */
  952. strcat(command_ps, matchBuf);
  953. /* back to begin word for match */
  954. input_backward(recalc_pos);
  955. /* new pos */
  956. recalc_pos = cursor + len_found;
  957. /* new len */
  958. len = strlen(command_ps);
  959. /* write out the matched command */
  960. redraw(cmdedit_y, len - recalc_pos);
  961. }
  962. if (tmp != matches[0])
  963. free(tmp);
  964. } else {
  965. /* Ok -- the last char was a TAB. Since they
  966. * just hit TAB again, print a list of all the
  967. * available choices... */
  968. if (matches && num_matches > 0) {
  969. int sav_cursor = cursor; /* change goto_new_line() */
  970. /* Go to the next line */
  971. goto_new_line();
  972. showfiles(matches, num_matches);
  973. redraw(0, len - sav_cursor);
  974. }
  975. }
  976. }
  977. #endif /* CONFIG_FEATURE_COMMAND_TAB_COMPLETION */
  978. #if MAX_HISTORY >= 1
  979. static void get_previous_history(void)
  980. {
  981. if(command_ps[0] != 0 || history[cur_history] == 0) {
  982. free(history[cur_history]);
  983. history[cur_history] = bb_xstrdup(command_ps);
  984. }
  985. cur_history--;
  986. }
  987. static int get_next_history(void)
  988. {
  989. int ch = cur_history;
  990. if (ch < n_history) {
  991. get_previous_history(); /* save the current history line */
  992. return (cur_history = ch+1);
  993. } else {
  994. beep();
  995. return 0;
  996. }
  997. }
  998. #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
  999. extern void load_history ( const char *fromfile )
  1000. {
  1001. FILE *fp;
  1002. int hi;
  1003. /* cleanup old */
  1004. for(hi = n_history; hi > 0; ) {
  1005. hi--;
  1006. free ( history [hi] );
  1007. }
  1008. if (( fp = fopen ( fromfile, "r" ))) {
  1009. for ( hi = 0; hi < MAX_HISTORY; ) {
  1010. char * hl = bb_get_chomped_line_from_file(fp);
  1011. int l;
  1012. if(!hl)
  1013. break;
  1014. l = strlen(hl);
  1015. if(l >= BUFSIZ)
  1016. hl[BUFSIZ-1] = 0;
  1017. if(l == 0 || hl[0] == ' ') {
  1018. free(hl);
  1019. continue;
  1020. }
  1021. history [hi++] = hl;
  1022. }
  1023. fclose ( fp );
  1024. }
  1025. cur_history = n_history = hi;
  1026. }
  1027. extern void save_history ( const char *tofile )
  1028. {
  1029. FILE *fp = fopen ( tofile, "w" );
  1030. if ( fp ) {
  1031. int i;
  1032. for ( i = 0; i < n_history; i++ ) {
  1033. fprintf(fp, "%s\n", history [i]);
  1034. }
  1035. fclose ( fp );
  1036. }
  1037. }
  1038. #endif
  1039. #endif
  1040. enum {
  1041. ESC = 27,
  1042. DEL = 127,
  1043. };
  1044. /*
  1045. * This function is used to grab a character buffer
  1046. * from the input file descriptor and allows you to
  1047. * a string with full command editing (sort of like
  1048. * a mini readline).
  1049. *
  1050. * The following standard commands are not implemented:
  1051. * ESC-b -- Move back one word
  1052. * ESC-f -- Move forward one word
  1053. * ESC-d -- Delete back one word
  1054. * ESC-h -- Delete forward one word
  1055. * CTL-t -- Transpose two characters
  1056. *
  1057. * Furthermore, the "vi" command editing keys are not implemented.
  1058. *
  1059. */
  1060. int cmdedit_read_input(char *prompt, char command[BUFSIZ])
  1061. {
  1062. int break_out = 0;
  1063. int lastWasTab = FALSE;
  1064. unsigned char c = 0;
  1065. /* prepare before init handlers */
  1066. cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
  1067. len = 0;
  1068. command_ps = command;
  1069. getTermSettings(0, (void *) &initial_settings);
  1070. memcpy(&new_settings, &initial_settings, sizeof(struct termios));
  1071. new_settings.c_lflag &= ~ICANON; /* unbuffered input */
  1072. /* Turn off echoing and CTRL-C, so we can trap it */
  1073. new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
  1074. /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
  1075. new_settings.c_cc[VMIN] = 1;
  1076. new_settings.c_cc[VTIME] = 0;
  1077. /* Turn off CTRL-C, so we can trap it */
  1078. # ifndef _POSIX_VDISABLE
  1079. # define _POSIX_VDISABLE '\0'
  1080. # endif
  1081. new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
  1082. command[0] = 0;
  1083. setTermSettings(0, (void *) &new_settings);
  1084. handlers_sets |= SET_RESET_TERM;
  1085. /* Now initialize things */
  1086. cmdedit_init();
  1087. /* Print out the command prompt */
  1088. parse_prompt(prompt);
  1089. while (1) {
  1090. fflush(stdout); /* buffered out to fast */
  1091. if (safe_read(0, &c, 1) < 1)
  1092. /* if we can't read input then exit */
  1093. goto prepare_to_die;
  1094. switch (c) {
  1095. case '\n':
  1096. case '\r':
  1097. /* Enter */
  1098. goto_new_line();
  1099. break_out = 1;
  1100. break;
  1101. case 1:
  1102. /* Control-a -- Beginning of line */
  1103. input_backward(cursor);
  1104. break;
  1105. case 2:
  1106. /* Control-b -- Move back one character */
  1107. input_backward(1);
  1108. break;
  1109. case 3:
  1110. /* Control-c -- stop gathering input */
  1111. goto_new_line();
  1112. #ifndef CONFIG_ASH
  1113. command[0] = 0;
  1114. len = 0;
  1115. lastWasTab = FALSE;
  1116. put_prompt();
  1117. #else
  1118. len = 0;
  1119. break_out = -1; /* to control traps */
  1120. #endif
  1121. break;
  1122. case 4:
  1123. /* Control-d -- Delete one character, or exit
  1124. * if the len=0 and no chars to delete */
  1125. if (len == 0) {
  1126. errno = 0;
  1127. prepare_to_die:
  1128. #if !defined(CONFIG_ASH)
  1129. printf("exit");
  1130. goto_new_line();
  1131. /* cmdedit_reset_term() called in atexit */
  1132. exit(EXIT_SUCCESS);
  1133. #else
  1134. /* to control stopped jobs */
  1135. len = break_out = -1;
  1136. break;
  1137. #endif
  1138. } else {
  1139. input_delete();
  1140. }
  1141. break;
  1142. case 5:
  1143. /* Control-e -- End of line */
  1144. input_end();
  1145. break;
  1146. case 6:
  1147. /* Control-f -- Move forward one character */
  1148. input_forward();
  1149. break;
  1150. case '\b':
  1151. case DEL:
  1152. /* Control-h and DEL */
  1153. input_backspace();
  1154. break;
  1155. case '\t':
  1156. #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
  1157. input_tab(&lastWasTab);
  1158. #endif
  1159. break;
  1160. case 11:
  1161. /* Control-k -- clear to end of line */
  1162. *(command + cursor) = 0;
  1163. len = cursor;
  1164. printf("\033[J");
  1165. break;
  1166. case 12:
  1167. /* Control-l -- clear screen */
  1168. printf("\033[H");
  1169. redraw(0, len-cursor);
  1170. break;
  1171. #if MAX_HISTORY >= 1
  1172. case 14:
  1173. /* Control-n -- Get next command in history */
  1174. if (get_next_history())
  1175. goto rewrite_line;
  1176. break;
  1177. case 16:
  1178. /* Control-p -- Get previous command from history */
  1179. if (cur_history > 0) {
  1180. get_previous_history();
  1181. goto rewrite_line;
  1182. } else {
  1183. beep();
  1184. }
  1185. break;
  1186. #endif
  1187. case 21:
  1188. /* Control-U -- Clear line before cursor */
  1189. if (cursor) {
  1190. strcpy(command, command + cursor);
  1191. redraw(cmdedit_y, len -= cursor);
  1192. }
  1193. break;
  1194. case 23:
  1195. /* Control-W -- Remove the last word */
  1196. while (cursor > 0 && isspace(command[cursor-1]))
  1197. input_backspace();
  1198. while (cursor > 0 &&!isspace(command[cursor-1]))
  1199. input_backspace();
  1200. break;
  1201. case ESC:{
  1202. /* escape sequence follows */
  1203. if (safe_read(0, &c, 1) < 1)
  1204. goto prepare_to_die;
  1205. /* different vt100 emulations */
  1206. if (c == '[' || c == 'O') {
  1207. if (safe_read(0, &c, 1) < 1)
  1208. goto prepare_to_die;
  1209. }
  1210. if (c >= '1' && c <= '9') {
  1211. unsigned char dummy;
  1212. if (safe_read(0, &dummy, 1) < 1)
  1213. goto prepare_to_die;
  1214. if(dummy != '~')
  1215. c = 0;
  1216. }
  1217. switch (c) {
  1218. #ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
  1219. case '\t': /* Alt-Tab */
  1220. input_tab(&lastWasTab);
  1221. break;
  1222. #endif
  1223. #if MAX_HISTORY >= 1
  1224. case 'A':
  1225. /* Up Arrow -- Get previous command from history */
  1226. if (cur_history > 0) {
  1227. get_previous_history();
  1228. goto rewrite_line;
  1229. } else {
  1230. beep();
  1231. }
  1232. break;
  1233. case 'B':
  1234. /* Down Arrow -- Get next command in history */
  1235. if (!get_next_history())
  1236. break;
  1237. /* Rewrite the line with the selected history item */
  1238. rewrite_line:
  1239. /* change command */
  1240. len = strlen(strcpy(command, history[cur_history]));
  1241. /* redraw and go to end line */
  1242. redraw(cmdedit_y, 0);
  1243. break;
  1244. #endif
  1245. case 'C':
  1246. /* Right Arrow -- Move forward one character */
  1247. input_forward();
  1248. break;
  1249. case 'D':
  1250. /* Left Arrow -- Move back one character */
  1251. input_backward(1);
  1252. break;
  1253. case '3':
  1254. /* Delete */
  1255. input_delete();
  1256. break;
  1257. case '1':
  1258. case 'H':
  1259. /* <Home> */
  1260. input_backward(cursor);
  1261. break;
  1262. case '4':
  1263. case 'F':
  1264. /* <End> */
  1265. input_end();
  1266. break;
  1267. default:
  1268. c = 0;
  1269. beep();
  1270. }
  1271. break;
  1272. }
  1273. default: /* If it's regular input, do the normal thing */
  1274. #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
  1275. /* Control-V -- Add non-printable symbol */
  1276. if (c == 22) {
  1277. if (safe_read(0, &c, 1) < 1)
  1278. goto prepare_to_die;
  1279. if (c == 0) {
  1280. beep();
  1281. break;
  1282. }
  1283. } else
  1284. #endif
  1285. if (!Isprint(c)) /* Skip non-printable characters */
  1286. break;
  1287. if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
  1288. break;
  1289. len++;
  1290. if (cursor == (len - 1)) { /* Append if at the end of the line */
  1291. *(command + cursor) = c;
  1292. *(command + cursor + 1) = 0;
  1293. cmdedit_set_out_char(0);
  1294. } else { /* Insert otherwise */
  1295. int sc = cursor;
  1296. memmove(command + sc + 1, command + sc, len - sc);
  1297. *(command + sc) = c;
  1298. sc++;
  1299. /* rewrite from cursor */
  1300. input_end();
  1301. /* to prev x pos + 1 */
  1302. input_backward(cursor - sc);
  1303. }
  1304. break;
  1305. }
  1306. if (break_out) /* Enter is the command terminator, no more input. */
  1307. break;
  1308. if (c != '\t')
  1309. lastWasTab = FALSE;
  1310. }
  1311. setTermSettings(0, (void *) &initial_settings);
  1312. handlers_sets &= ~SET_RESET_TERM;
  1313. #if MAX_HISTORY >= 1
  1314. /* Handle command history log */
  1315. /* cleanup may be saved current command line */
  1316. if (len> 0) { /* no put empty line */
  1317. int i = n_history;
  1318. free(history[MAX_HISTORY]);
  1319. history[MAX_HISTORY] = 0;
  1320. /* After max history, remove the oldest command */
  1321. if (i >= MAX_HISTORY) {
  1322. free(history[0]);
  1323. for(i = 0; i < (MAX_HISTORY-1); i++)
  1324. history[i] = history[i+1];
  1325. }
  1326. history[i++] = bb_xstrdup(command);
  1327. cur_history = i;
  1328. n_history = i;
  1329. #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
  1330. num_ok_lines++;
  1331. #endif
  1332. }
  1333. #else /* MAX_HISTORY < 1 */
  1334. #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
  1335. if (len > 0) { /* no put empty line */
  1336. num_ok_lines++;
  1337. }
  1338. #endif
  1339. #endif /* MAX_HISTORY >= 1 */
  1340. if (break_out > 0) {
  1341. command[len++] = '\n'; /* set '\n' */
  1342. command[len] = 0;
  1343. }
  1344. #if defined(CONFIG_FEATURE_CLEAN_UP) && defined(CONFIG_FEATURE_COMMAND_TAB_COMPLETION)
  1345. input_tab(0); /* strong free */
  1346. #endif
  1347. #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
  1348. free(cmdedit_prompt);
  1349. #endif
  1350. cmdedit_reset_term();
  1351. return len;
  1352. }
  1353. #endif /* CONFIG_FEATURE_COMMAND_EDITING */
  1354. #ifdef TEST
  1355. const char *bb_applet_name = "debug stuff usage";
  1356. #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
  1357. #include <locale.h>
  1358. #endif
  1359. int main(int argc, char **argv)
  1360. {
  1361. char buff[BUFSIZ];
  1362. char *prompt =
  1363. #if defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
  1364. "\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:\
  1365. \\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
  1366. \\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
  1367. #else
  1368. "% ";
  1369. #endif
  1370. #ifdef CONFIG_FEATURE_NONPRINTABLE_INVERSE_PUT
  1371. setlocale(LC_ALL, "");
  1372. #endif
  1373. while(1) {
  1374. int l;
  1375. l = cmdedit_read_input(prompt, buff);
  1376. if(l > 0 && buff[l-1] == '\n') {
  1377. buff[l-1] = 0;
  1378. printf("*** cmdedit_read_input() returned line =%s=\n", buff);
  1379. } else {
  1380. break;
  1381. }
  1382. }
  1383. printf("*** cmdedit_read_input() detect ^D\n");
  1384. return 0;
  1385. }
  1386. #endif /* TEST */