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