/contrib/tcsh/sh.file.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 769 lines · 604 code · 68 blank · 97 comment · 110 complexity · 1a39e15abc1cd465ab763e2936ddca76 MD5 · raw file

  1. /* $Header: /p/tcsh/cvsroot/tcsh/sh.file.c,v 3.37 2010/02/09 20:21:49 christos Exp $ */
  2. /*
  3. * sh.file.c: File completion for csh. This file is not used in tcsh.
  4. */
  5. /*-
  6. * Copyright (c) 1980, 1991 The Regents of the University of California.
  7. * All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. * 3. Neither the name of the University nor the names of its contributors
  18. * may be used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. */
  33. #include "sh.h"
  34. #include "ed.h"
  35. RCSID("$tcsh: sh.file.c,v 3.37 2010/02/09 20:21:49 christos Exp $")
  36. #if defined(FILEC) && defined(TIOCSTI)
  37. /*
  38. * Tenex style file name recognition, .. and more.
  39. * History:
  40. * Author: Ken Greer, Sept. 1975, CMU.
  41. * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
  42. */
  43. #define ON 1
  44. #define OFF 0
  45. #ifndef TRUE
  46. #define TRUE 1
  47. #endif
  48. #ifndef FALSE
  49. #define FALSE 0
  50. #endif
  51. #define ESC CTL_ESC('\033')
  52. typedef enum {
  53. LIST, RECOGNIZE
  54. } COMMAND;
  55. static void setup_tty (int);
  56. static void back_to_col_1 (void);
  57. static void pushback (const Char *);
  58. static int filetype (const Char *, const Char *);
  59. static void print_by_column (const Char *, Char *[], size_t);
  60. static Char *tilde (const Char *);
  61. static void retype (void);
  62. static void beep (void);
  63. static void print_recognized_stuff (const Char *);
  64. static void extract_dir_and_name (const Char *, Char **, const Char **);
  65. static Char *getitem (DIR *, int);
  66. static size_t tsearch (Char *, COMMAND, size_t);
  67. static int compare (const void *, const void *);
  68. static int recognize (Char **, Char *, size_t, size_t);
  69. static int is_prefix (const Char *, const Char *);
  70. static int is_suffix (const Char *, const Char *);
  71. static int ignored (const Char *);
  72. /*
  73. * Put this here so the binary can be patched with adb to enable file
  74. * completion by default. Filec controls completion, nobeep controls
  75. * ringing the terminal bell on incomplete expansions.
  76. */
  77. int filec = 0;
  78. static void
  79. setup_tty(int on)
  80. {
  81. #ifdef TERMIO
  82. # ifdef POSIX
  83. struct termios tchars;
  84. # else
  85. struct termio tchars;
  86. # endif /* POSIX */
  87. # ifdef POSIX
  88. (void) tcgetattr(SHIN, &tchars);
  89. # else
  90. (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars);
  91. # endif /* POSIX */
  92. if (on) {
  93. tchars.c_cc[VEOL] = ESC;
  94. if (tchars.c_lflag & ICANON)
  95. # ifdef POSIX
  96. on = TCSADRAIN;
  97. # else
  98. on = TCSETA;
  99. # endif /* POSIX */
  100. else {
  101. # ifdef POSIX
  102. on = TCSAFLUSH;
  103. # else
  104. on = TCSETAF;
  105. # endif /* POSIX */
  106. tchars.c_lflag |= ICANON;
  107. }
  108. }
  109. else {
  110. tchars.c_cc[VEOL] = _POSIX_VDISABLE;
  111. # ifdef POSIX
  112. on = TCSADRAIN;
  113. # else
  114. on = TCSETA;
  115. # endif /* POSIX */
  116. }
  117. # ifdef POSIX
  118. (void) xtcsetattr(SHIN, on, &tchars);
  119. # else
  120. (void) ioctl(SHIN, on, (ioctl_t) &tchars);
  121. # endif /* POSIX */
  122. #else
  123. struct sgttyb sgtty;
  124. static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */
  125. if (on) {
  126. (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars);
  127. tchars.t_brkc = ESC;
  128. (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
  129. /*
  130. * This must be done after every command: if the tty gets into raw or
  131. * cbreak mode the user can't even type 'reset'.
  132. */
  133. (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty);
  134. if (sgtty.sg_flags & (RAW | CBREAK)) {
  135. sgtty.sg_flags &= ~(RAW | CBREAK);
  136. (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty);
  137. }
  138. }
  139. else {
  140. tchars.t_brkc = -1;
  141. (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
  142. }
  143. #endif /* TERMIO */
  144. }
  145. /*
  146. * Move back to beginning of current line
  147. */
  148. static void
  149. back_to_col_1(void)
  150. {
  151. #ifdef TERMIO
  152. # ifdef POSIX
  153. struct termios tty, tty_normal;
  154. # else
  155. struct termio tty, tty_normal;
  156. # endif /* POSIX */
  157. #else
  158. struct sgttyb tty, tty_normal;
  159. #endif /* TERMIO */
  160. pintr_disabled++;
  161. cleanup_push(&pintr_disabled, disabled_cleanup);
  162. #ifdef TERMIO
  163. # ifdef POSIX
  164. (void) tcgetattr(SHOUT, &tty);
  165. # else
  166. (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal);
  167. # endif /* POSIX */
  168. tty_normal = tty;
  169. tty.c_iflag &= ~INLCR;
  170. tty.c_oflag &= ~ONLCR;
  171. # ifdef POSIX
  172. (void) xtcsetattr(SHOUT, TCSANOW, &tty);
  173. # else
  174. (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  175. # endif /* POSIX */
  176. (void) xwrite(SHOUT, "\r", 1);
  177. # ifdef POSIX
  178. (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal);
  179. # else
  180. (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
  181. # endif /* POSIX */
  182. #else
  183. (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty);
  184. tty_normal = tty;
  185. tty.sg_flags &= ~CRMOD;
  186. (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty);
  187. (void) xwrite(SHOUT, "\r", 1);
  188. (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal);
  189. #endif /* TERMIO */
  190. cleanup_until(&pintr_disabled);
  191. }
  192. /*
  193. * Push string contents back into tty queue
  194. */
  195. static void
  196. pushback(const Char *string)
  197. {
  198. const Char *p;
  199. #ifdef TERMIO
  200. # ifdef POSIX
  201. struct termios tty, tty_normal;
  202. # else
  203. struct termio tty, tty_normal;
  204. # endif /* POSIX */
  205. #else
  206. struct sgttyb tty, tty_normal;
  207. #endif /* TERMIO */
  208. pintr_disabled++;
  209. cleanup_push(&pintr_disabled, disabled_cleanup);
  210. #ifdef TERMIO
  211. # ifdef POSIX
  212. (void) tcgetattr(SHOUT, &tty);
  213. # else
  214. (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  215. # endif /* POSIX */
  216. tty_normal = tty;
  217. tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL |
  218. #ifndef __QNXNTO__
  219. ECHOPRT |
  220. #endif
  221. ECHOCTL);
  222. # ifdef POSIX
  223. (void) xtcsetattr(SHOUT, TCSANOW, &tty);
  224. # else
  225. (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  226. # endif /* POSIX */
  227. for (p = string; *p != '\0'; p++) {
  228. char buf[MB_LEN_MAX];
  229. size_t i, len;
  230. len = one_wctomb(buf, *p & CHAR);
  231. for (i = 0; i < len; i++)
  232. (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) &buf[i]);
  233. }
  234. # ifdef POSIX
  235. (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal);
  236. # else
  237. (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
  238. # endif /* POSIX */
  239. #else
  240. (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty);
  241. tty_normal = tty;
  242. tty.sg_flags &= ~ECHO;
  243. (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty);
  244. for (p = string; c = *p; p++)
  245. (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
  246. (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal);
  247. #endif /* TERMIO */
  248. cleanup_until(&pintr_disabled);
  249. }
  250. static int
  251. filetype(const Char *dir, const Char *file)
  252. {
  253. Char *path;
  254. char *spath;
  255. struct stat statb;
  256. path = Strspl(dir, file);
  257. spath = short2str(path);
  258. xfree(path);
  259. if (lstat(spath, &statb) == 0) {
  260. switch (statb.st_mode & S_IFMT) {
  261. case S_IFDIR:
  262. return ('/');
  263. case S_IFLNK:
  264. if (stat(spath, &statb) == 0 && /* follow it out */
  265. S_ISDIR(statb.st_mode))
  266. return ('>');
  267. else
  268. return ('@');
  269. case S_IFSOCK:
  270. return ('=');
  271. default:
  272. if (statb.st_mode & 0111)
  273. return ('*');
  274. }
  275. }
  276. return (' ');
  277. }
  278. /*
  279. * Print sorted down columns
  280. */
  281. static void
  282. print_by_column(const Char *dir, Char *items[], size_t count)
  283. {
  284. struct winsize win;
  285. size_t i;
  286. int rows, r, c, maxwidth = 0, columns;
  287. if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
  288. win.ws_col = 80;
  289. for (i = 0; i < count; i++)
  290. maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
  291. maxwidth += 2; /* for the file tag and space */
  292. columns = win.ws_col / maxwidth;
  293. if (columns == 0)
  294. columns = 1;
  295. rows = (count + (columns - 1)) / columns;
  296. for (r = 0; r < rows; r++) {
  297. for (c = 0; c < columns; c++) {
  298. i = c * rows + r;
  299. if (i < count) {
  300. int w;
  301. xprintf("%S", items[i]);
  302. xputchar(dir ? filetype(dir, items[i]) : ' ');
  303. if (c < columns - 1) { /* last column? */
  304. w = Strlen(items[i]) + 1;
  305. for (; w < maxwidth; w++)
  306. xputchar(' ');
  307. }
  308. }
  309. }
  310. xputchar('\r');
  311. xputchar('\n');
  312. }
  313. }
  314. /*
  315. * Expand file name with possible tilde usage
  316. * ~person/mumble
  317. * expands to
  318. * home_directory_of_person/mumble
  319. */
  320. static Char *
  321. tilde(const Char *old)
  322. {
  323. const Char *o, *home;
  324. struct passwd *pw;
  325. if (old[0] != '~')
  326. return (Strsave(old));
  327. old++;
  328. for (o = old; *o != '\0' && *o != '/'; o++)
  329. ;
  330. if (o == old)
  331. home = varval(STRhome);
  332. else {
  333. Char *person;
  334. person = Strnsave(old, o - old);
  335. pw = xgetpwnam(short2str(person));
  336. xfree(person);
  337. if (pw == NULL)
  338. return (NULL);
  339. home = str2short(pw->pw_dir);
  340. }
  341. return Strspl(home, o);
  342. }
  343. /*
  344. * Cause pending line to be printed
  345. */
  346. static void
  347. retype(void)
  348. {
  349. #ifdef TERMIO
  350. # ifdef POSIX
  351. struct termios tty;
  352. (void) tcgetattr(SHOUT, &tty);
  353. # else
  354. struct termio tty;
  355. (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty);
  356. # endif /* POSIX */
  357. #ifndef __QNXNTO__
  358. tty.c_lflag |= PENDIN;
  359. #endif
  360. # ifdef POSIX
  361. (void) xtcsetattr(SHOUT, TCSANOW, &tty);
  362. # else
  363. (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
  364. # endif /* POSIX */
  365. #else
  366. int pending_input = LPENDIN;
  367. (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input);
  368. #endif /* TERMIO */
  369. }
  370. static void
  371. beep(void)
  372. {
  373. if (adrof(STRnobeep) == 0)
  374. #ifdef IS_ASCII
  375. (void) xwrite(SHOUT, "\007", 1);
  376. #else
  377. {
  378. unsigned char beep_ch = CTL_ESC('\007');
  379. (void) xwrite(SHOUT, &beep_ch, 1);
  380. }
  381. #endif
  382. }
  383. /*
  384. * Erase that silly ^[ and
  385. * print the recognized part of the string
  386. */
  387. static void
  388. print_recognized_stuff(const Char *recognized_part)
  389. {
  390. /* An optimized erasing of that silly ^[ */
  391. (void) putraw('\b');
  392. (void) putraw('\b');
  393. switch (Strlen(recognized_part)) {
  394. case 0: /* erase two Characters: ^[ */
  395. (void) putraw(' ');
  396. (void) putraw(' ');
  397. (void) putraw('\b');
  398. (void) putraw('\b');
  399. break;
  400. case 1: /* overstrike the ^, erase the [ */
  401. xprintf("%S", recognized_part);
  402. (void) putraw(' ');
  403. (void) putraw('\b');
  404. break;
  405. default: /* overstrike both Characters ^[ */
  406. xprintf("%S", recognized_part);
  407. break;
  408. }
  409. flush();
  410. }
  411. /*
  412. * Parse full path in file into 2 parts: directory and file names
  413. * Should leave final slash (/) at end of dir.
  414. */
  415. static void
  416. extract_dir_and_name(const Char *path, Char **dir, const Char **name)
  417. {
  418. const Char *p;
  419. p = Strrchr(path, '/');
  420. if (p == NULL)
  421. p = path;
  422. else
  423. p++;
  424. *name = p;
  425. *dir = Strnsave(path, p - path);
  426. }
  427. static Char *
  428. getitem(DIR *dir_fd, int looking_for_lognames)
  429. {
  430. struct passwd *pw;
  431. struct dirent *dirp;
  432. if (looking_for_lognames) {
  433. #ifndef HAVE_GETPWENT
  434. return (NULL);
  435. #else
  436. if ((pw = getpwent()) == NULL)
  437. return (NULL);
  438. return (str2short(pw->pw_name));
  439. #endif /* atp vmsposix */
  440. }
  441. if ((dirp = readdir(dir_fd)) != NULL)
  442. return (str2short(dirp->d_name));
  443. return (NULL);
  444. }
  445. /*
  446. * Perform a RECOGNIZE or LIST command on string "word".
  447. */
  448. static size_t
  449. tsearch(Char *word, COMMAND command, size_t max_word_length)
  450. {
  451. DIR *dir_fd;
  452. int ignoring = TRUE, nignored = 0;
  453. int looking_for_lognames;
  454. Char *tilded_dir = NULL, *dir = NULL;
  455. Char *extended_name = NULL;
  456. const Char *name;
  457. Char *item;
  458. struct blk_buf items = BLK_BUF_INIT;
  459. size_t name_length;
  460. looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
  461. if (looking_for_lognames) {
  462. #ifdef HAVE_GETPWENT
  463. (void) setpwent();
  464. #endif
  465. name = word + 1; /* name sans ~ */
  466. dir_fd = NULL;
  467. cleanup_push(dir, xfree);
  468. }
  469. else {
  470. extract_dir_and_name(word, &dir, &name);
  471. cleanup_push(dir, xfree);
  472. tilded_dir = tilde(dir);
  473. if (tilded_dir == NULL)
  474. goto end;
  475. cleanup_push(tilded_dir, xfree);
  476. dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
  477. if (dir_fd == NULL)
  478. goto end;
  479. }
  480. name_length = Strlen(name);
  481. cleanup_push(&extended_name, xfree_indirect);
  482. cleanup_push(&items, bb_cleanup);
  483. again: /* search for matches */
  484. while ((item = getitem(dir_fd, looking_for_lognames)) != NULL) {
  485. if (!is_prefix(name, item))
  486. continue;
  487. /* Don't match . files on null prefix match */
  488. if (name_length == 0 && item[0] == '.' &&
  489. !looking_for_lognames)
  490. continue;
  491. if (command == LIST)
  492. bb_append(&items, Strsave(item));
  493. else { /* RECOGNIZE command */
  494. if (ignoring && ignored(item))
  495. nignored++;
  496. else if (recognize(&extended_name, item, name_length, ++items.len))
  497. break;
  498. }
  499. }
  500. if (ignoring && items.len == 0 && nignored > 0) {
  501. ignoring = FALSE;
  502. nignored = 0;
  503. if (looking_for_lognames) {
  504. #ifdef HAVE_GETPWENT
  505. (void) setpwent();
  506. #endif /* atp vmsposix */
  507. } else
  508. rewinddir(dir_fd);
  509. goto again;
  510. }
  511. if (looking_for_lognames) {
  512. #ifndef HAVE_GETPWENT
  513. (void) endpwent();
  514. #endif
  515. } else
  516. xclosedir(dir_fd);
  517. if (items.len != 0) {
  518. if (command == RECOGNIZE) {
  519. if (looking_for_lognames)
  520. copyn(word, STRtilde, 2);/*FIXBUF, sort of */
  521. else
  522. /* put back dir part */
  523. copyn(word, dir, max_word_length);/*FIXBUF*/
  524. /* add extended name */
  525. catn(word, extended_name, max_word_length);/*FIXBUF*/
  526. }
  527. else { /* LIST */
  528. qsort(items.vec, items.len, sizeof(items.vec[0]), compare);
  529. print_by_column(looking_for_lognames ? NULL : tilded_dir,
  530. items.vec, items.len);
  531. }
  532. }
  533. end:
  534. cleanup_until(dir);
  535. return items.len;
  536. }
  537. static int
  538. compare(const void *p, const void *q)
  539. {
  540. #if defined (WIDE_STRINGS) && !defined (UTF16_STRING)
  541. errno = 0;
  542. return (wcscoll(*(Char *const *) p, *(Char *const *) q));
  543. #else
  544. char *p1, *q1;
  545. int res;
  546. p1 = strsave(short2str(*(Char *const *) p));
  547. q1 = strsave(short2str(*(Char *const *) q));
  548. # if defined(NLS) && defined(HAVE_STRCOLL)
  549. res = strcoll(p1, q1);
  550. # else
  551. res = strcmp(p1, q1);
  552. # endif /* NLS && HAVE_STRCOLL */
  553. xfree (p1);
  554. xfree (q1);
  555. return res;
  556. #endif /* not WIDE_STRINGS */
  557. }
  558. /*
  559. * Object: extend what user typed up to an ambiguity.
  560. * Algorithm:
  561. * On first match, copy full item (assume it'll be the only match)
  562. * On subsequent matches, shorten extended_name to the first
  563. * Character mismatch between extended_name and item.
  564. * If we shorten it back to the prefix length, stop searching.
  565. */
  566. static int
  567. recognize(Char **extended_name, Char *item, size_t name_length,
  568. size_t numitems)
  569. {
  570. if (numitems == 1) /* 1st match */
  571. *extended_name = Strsave(item);
  572. else { /* 2nd & subsequent matches */
  573. Char *x, *ent;
  574. size_t len = 0;
  575. x = *extended_name;
  576. for (ent = item; *x && *x == *ent++; x++, len++);
  577. *x = '\0'; /* Shorten at 1st Char diff */
  578. if (len == name_length) /* Ambiguous to prefix? */
  579. return (-1); /* So stop now and save time */
  580. }
  581. return (0);
  582. }
  583. /*
  584. * Return true if check matches initial Chars in template.
  585. * This differs from PWB imatch in that if check is null
  586. * it matches anything.
  587. */
  588. static int
  589. is_prefix(const Char *check, const Char *template)
  590. {
  591. do
  592. if (*check == 0)
  593. return (TRUE);
  594. while (*check++ == *template++);
  595. return (FALSE);
  596. }
  597. /*
  598. * Return true if the Chars in template appear at the
  599. * end of check, I.e., are it's suffix.
  600. */
  601. static int
  602. is_suffix(const Char *check, const Char *template)
  603. {
  604. const Char *c, *t;
  605. for (c = check; *c++;);
  606. for (t = template; *t++;);
  607. for (;;) {
  608. if (t == template)
  609. return 1;
  610. if (c == check || *--t != *--c)
  611. return 0;
  612. }
  613. }
  614. static void
  615. setup_tty_cleanup(void *dummy)
  616. {
  617. USE(dummy);
  618. setup_tty(OFF);
  619. }
  620. size_t
  621. tenex(Char *inputline, size_t inputline_size)
  622. {
  623. size_t numitems;
  624. ssize_t num_read;
  625. char tinputline[BUFSIZE + 1];/*FIXBUF*/
  626. setup_tty(ON);
  627. cleanup_push(&num_read, setup_tty_cleanup); /* num_read is only a marker */
  628. while ((num_read = xread(SHIN, tinputline, BUFSIZE)) > 0) {/*FIXBUF*/
  629. static const Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
  630. '>', '(', ')', '|', '^', '%', '\0'};
  631. Char *str_end, *word_start, last_Char, should_retype;
  632. size_t space_left;
  633. COMMAND command;
  634. tinputline[num_read] = 0;
  635. Strcpy(inputline, str2short(tinputline));/*FIXBUF*/
  636. num_read = Strlen(inputline);
  637. last_Char = CTL_ESC(ASC(inputline[num_read - 1]) & ASCII);
  638. if (last_Char == '\n' || (size_t)num_read == inputline_size)
  639. break;
  640. command = (last_Char == ESC) ? RECOGNIZE : LIST;
  641. if (command == LIST)
  642. xputchar('\n');
  643. str_end = &inputline[num_read];
  644. if (last_Char == ESC)
  645. --str_end; /* wipeout trailing cmd Char */
  646. *str_end = '\0';
  647. /*
  648. * Find LAST occurence of a delimiter in the inputline. The word start
  649. * is one Character past it.
  650. */
  651. for (word_start = str_end; word_start > inputline; --word_start)
  652. if (Strchr(delims, word_start[-1]))
  653. break;
  654. space_left = inputline_size - (word_start - inputline) - 1;
  655. numitems = tsearch(word_start, command, space_left);
  656. if (command == RECOGNIZE) {
  657. /* print from str_end on */
  658. print_recognized_stuff(str_end);
  659. if (numitems != 1) /* Beep = No match/ambiguous */
  660. beep();
  661. }
  662. /*
  663. * Tabs in the input line cause trouble after a pushback. tty driver
  664. * won't backspace over them because column positions are now
  665. * incorrect. This is solved by retyping over current line.
  666. */
  667. should_retype = FALSE;
  668. if (Strchr(inputline, '\t')) { /* tab Char in input line? */
  669. back_to_col_1();
  670. should_retype = TRUE;
  671. }
  672. if (command == LIST) /* Always retype after a LIST */
  673. should_retype = TRUE;
  674. if (should_retype)
  675. printprompt(0, NULL);
  676. pushback(inputline);
  677. if (should_retype)
  678. retype();
  679. }
  680. cleanup_until(&num_read);
  681. return (num_read);
  682. }
  683. static int
  684. ignored(const Char *item)
  685. {
  686. struct varent *vp;
  687. Char **cp;
  688. if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
  689. return (FALSE);
  690. for (; *cp != NULL; cp++)
  691. if (is_suffix(item, *cp))
  692. return (TRUE);
  693. return (FALSE);
  694. }
  695. #endif /* FILEC && TIOCSTI */