encap_getarg() was not properly loading the pointer argument associated
[dragonfly.git] / contrib / tcsh / sh.file.c
1 /* $Header: /src/pub/tcsh/sh.file.c,v 3.22 2002/07/01 20:53:00 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
36 RCSID("$Id: sh.file.c,v 3.22 2002/07/01 20:53:00 christos Exp $")
37
38 #if defined(FILEC) && defined(TIOCSTI)
39
40 /*
41  * Tenex style file name recognition, .. and more.
42  * History:
43  *      Author: Ken Greer, Sept. 1975, CMU.
44  *      Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
45  */
46
47 #define ON      1
48 #define OFF     0
49 #ifndef TRUE
50 #define TRUE 1
51 #endif
52 #ifndef FALSE
53 #define FALSE 0
54 #endif
55
56 #define ESC     CTL_ESC('\033')
57
58 typedef enum {
59     LIST, RECOGNIZE
60 }       COMMAND;
61
62 static  void     setup_tty              __P((int));
63 static  void     back_to_col_1          __P((void));
64 static  void     pushback               __P((Char *));
65 static  void     catn                   __P((Char *, Char *, int));
66 static  void     copyn                  __P((Char *, Char *, int));
67 static  Char     filetype               __P((Char *, Char *));
68 static  void     print_by_column        __P((Char *, Char *[], int));
69 static  Char    *tilde                  __P((Char *, Char *));
70 static  void     retype                 __P((void));
71 static  void     beep                   __P((void));
72 static  void     print_recognized_stuff __P((Char *));
73 static  void     extract_dir_and_name   __P((Char *, Char *, Char *));
74 static  Char    *getitem                __P((DIR *, int));
75 static  void     free_items             __P((Char **));
76 static  int      tsearch                __P((Char *, COMMAND, int));
77 static  int      compare                __P((const ptr_t, const ptr_t));
78 static  int      recognize              __P((Char *, Char *, int, int));
79 static  int      is_prefix              __P((Char *, Char *));
80 static  int      is_suffix              __P((Char *, Char *));
81 static  int      ignored                __P((Char *));
82
83
84 /*
85  * Put this here so the binary can be patched with adb to enable file
86  * completion by default.  Filec controls completion, nobeep controls
87  * ringing the terminal bell on incomplete expansions.
88  */
89 bool    filec = 0;
90
91 static void
92 setup_tty(on)
93     int     on;
94 {
95 #ifdef TERMIO
96 # ifdef POSIX
97     struct termios tchars;
98 # else
99     struct termio tchars;
100 # endif /* POSIX */
101
102 # ifdef POSIX
103     (void) tcgetattr(SHIN, &tchars);
104 # else
105     (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars);
106 # endif /* POSIX */
107     if (on) {
108         tchars.c_cc[VEOL] = ESC;
109         if (tchars.c_lflag & ICANON)
110 # ifdef POSIX
111             on = TCSADRAIN;
112 # else
113             on = TCSETA;
114 # endif /* POSIX */
115         else {
116 # ifdef POSIX
117             on = TCSAFLUSH;
118 # else
119             on = TCSETAF;
120 # endif /* POSIX */
121             tchars.c_lflag |= ICANON;
122     
123         }
124     }
125     else {
126         tchars.c_cc[VEOL] = _POSIX_VDISABLE;
127 # ifdef POSIX
128         on = TCSADRAIN;
129 # else
130         on = TCSETA;
131 # endif /* POSIX */
132     }
133 # ifdef POSIX
134     (void) tcsetattr(SHIN, on, &tchars);
135 # else
136     (void) ioctl(SHIN, on, (ioctl_t) &tchars);
137 # endif /* POSIX */
138 #else
139     struct sgttyb sgtty;
140     static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */
141
142     if (on) {
143         (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars);
144         tchars.t_brkc = ESC;
145         (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
146         /*
147          * This must be done after every command: if the tty gets into raw or
148          * cbreak mode the user can't even type 'reset'.
149          */
150         (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty);
151         if (sgtty.sg_flags & (RAW | CBREAK)) {
152             sgtty.sg_flags &= ~(RAW | CBREAK);
153             (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty);
154         }
155     }
156     else {
157         tchars.t_brkc = -1;
158         (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars);
159     }
160 #endif /* TERMIO */
161 }
162
163 /*
164  * Move back to beginning of current line
165  */
166 static void
167 back_to_col_1()
168 {
169 #ifdef TERMIO
170 # ifdef POSIX
171     struct termios tty, tty_normal;
172 # else
173     struct termio tty, tty_normal;
174 # endif /* POSIX */
175 #else
176     struct sgttyb tty, tty_normal;
177 #endif /* TERMIO */
178
179 # ifdef BSDSIGS
180     sigmask_t omask = sigblock(sigmask(SIGINT));
181 # else
182     (void) sighold(SIGINT);
183 # endif /* BSDSIGS */
184
185 #ifdef TERMIO
186 # ifdef POSIX
187     (void) tcgetattr(SHOUT, &tty);
188 # else
189     (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal);
190 # endif /* POSIX */
191     tty_normal = tty;
192     tty.c_iflag &= ~INLCR;
193     tty.c_oflag &= ~ONLCR;
194 # ifdef POSIX
195     (void) tcsetattr(SHOUT, TCSANOW, &tty);
196 # else
197     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
198 # endif /* POSIX */
199     (void) write(SHOUT, "\r", 1);
200 # ifdef POSIX
201     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
202 # else
203     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
204 # endif /* POSIX */
205 #else
206     (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty);
207     tty_normal = tty;
208     tty.sg_flags &= ~CRMOD;
209     (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty);
210     (void) write(SHOUT, "\r", 1);
211     (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal);
212 #endif /* TERMIO */
213
214 # ifdef BSDSIGS
215     (void) sigsetmask(omask);
216 # else
217     (void) sigrelse(SIGINT);
218 # endif /* BSDISGS */
219 }
220
221 /*
222  * Push string contents back into tty queue
223  */
224 static void
225 pushback(string)
226     Char   *string;
227 {
228     Char *p;
229     char    c;
230 #ifdef TERMIO
231 # ifdef POSIX
232     struct termios tty, tty_normal;
233 # else
234     struct termio tty, tty_normal;
235 # endif /* POSIX */
236 #else
237     struct sgttyb tty, tty_normal;
238 #endif /* TERMIO */
239
240 #ifdef BSDSIGS
241     sigmask_t omask = sigblock(sigmask(SIGINT));
242 #else
243     (void) sighold(SIGINT);
244 #endif /* BSDSIGS */
245
246 #ifdef TERMIO
247 # ifdef POSIX
248     (void) tcgetattr(SHOUT, &tty);
249 # else
250     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
251 # endif /* POSIX */
252     tty_normal = tty;
253     tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
254 # ifdef POSIX
255     (void) tcsetattr(SHOUT, TCSANOW, &tty);
256 # else
257     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
258 # endif /* POSIX */
259
260     for (p = string; (c = *p) != '\0'; p++)
261         (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
262 # ifdef POSIX
263     (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
264 # else
265     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal);
266 # endif /* POSIX */
267 #else
268     (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty);
269     tty_normal = tty;
270     tty.sg_flags &= ~ECHO;
271     (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty);
272
273     for (p = string; c = *p; p++)
274         (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
275     (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal);
276 #endif /* TERMIO */
277
278 # ifdef BSDSIGS
279     (void) sigsetmask(omask);
280 # else
281     (void) sigrelse(SIGINT);
282 # endif /* BSDISGS */
283 }
284
285 /*
286  * Concatenate src onto tail of des.
287  * Des is a string whose maximum length is count.
288  * Always null terminate.
289  */
290 static void
291 catn(des, src, count)
292     Char *des, *src;
293     int count;
294 {
295     while (--count >= 0 && *des)
296         des++;
297     while (--count >= 0)
298         if ((*des++ = *src++) == 0)
299             return;
300     *des = '\0';
301 }
302
303 /*
304  * Like strncpy but always leave room for trailing \0
305  * and always null terminate.
306  */
307 static void
308 copyn(des, src, count)
309     Char *des, *src;
310     int count;
311 {
312     while (--count >= 0)
313         if ((*des++ = *src++) == 0)
314             return;
315     *des = '\0';
316 }
317
318 static  Char
319 filetype(dir, file)
320     Char   *dir, *file;
321 {
322     Char    path[MAXPATHLEN];
323     struct stat statb;
324
325     catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
326     if (lstat(short2str(path), &statb) == 0) {
327         switch (statb.st_mode & S_IFMT) {
328         case S_IFDIR:
329             return ('/');
330
331         case S_IFLNK:
332             if (stat(short2str(path), &statb) == 0 &&   /* follow it out */
333                 S_ISDIR(statb.st_mode))
334                 return ('>');
335             else
336                 return ('@');
337
338         case S_IFSOCK:
339             return ('=');
340
341         default:
342             if (statb.st_mode & 0111)
343                 return ('*');
344         }
345     }
346     return (' ');
347 }
348
349 static struct winsize win;
350
351 /*
352  * Print sorted down columns
353  */
354 static void
355 print_by_column(dir, items, count)
356     Char   *dir, *items[];
357     int     count;
358 {
359     int i, rows, r, c, maxwidth = 0, columns;
360
361     if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
362         win.ws_col = 80;
363     for (i = 0; i < count; i++)
364         maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
365     maxwidth += 2;              /* for the file tag and space */
366     columns = win.ws_col / maxwidth;
367     if (columns == 0)
368         columns = 1;
369     rows = (count + (columns - 1)) / columns;
370     for (r = 0; r < rows; r++) {
371         for (c = 0; c < columns; c++) {
372             i = c * rows + r;
373             if (i < count) {
374                 int w;
375
376                 xprintf("%S", items[i]);
377                 xputchar(dir ? filetype(dir, items[i]) : ' ');
378                 if (c < columns - 1) {  /* last column? */
379                     w = Strlen(items[i]) + 1;
380                     for (; w < maxwidth; w++)
381                         xputchar(' ');
382                 }
383             }
384         }
385         xputchar('\r');
386         xputchar('\n');
387     }
388 }
389
390 /*
391  * Expand file name with possible tilde usage
392  *      ~person/mumble
393  * expands to
394  *      home_directory_of_person/mumble
395  */
396 static Char *
397 tilde(new, old)
398     Char   *new, *old;
399 {
400     Char *o, *p;
401     struct passwd *pw;
402     static Char person[40];
403
404     if (old[0] != '~')
405         return (Strcpy(new, old));
406
407     for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++);
408     *p = '\0';
409     if (person[0] == '\0')
410         (void) Strcpy(new, varval(STRhome));
411     else {
412         pw = getpwnam(short2str(person));
413         if (pw == NULL)
414             return (NULL);
415         (void) Strcpy(new, str2short(pw->pw_dir));
416     }
417     (void) Strcat(new, o);
418     return (new);
419 }
420
421 /*
422  * Cause pending line to be printed
423  */
424 static void
425 retype()
426 {
427 #ifdef TERMIO
428 # ifdef POSIX
429     struct termios tty;
430
431     (void) tcgetattr(SHOUT, &tty);
432 # else
433     struct termio tty;
434
435     (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty);
436 # endif /* POSIX */
437
438     tty.c_lflag |= PENDIN;
439
440 # ifdef POSIX
441     (void) tcsetattr(SHOUT, TCSANOW, &tty);
442 # else
443     (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty);
444 # endif /* POSIX */
445 #else
446     int     pending_input = LPENDIN;
447
448     (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input);
449 #endif /* TERMIO */
450 }
451
452 static void
453 beep()
454 {
455     if (adrof(STRnobeep) == 0)
456 #ifdef IS_ASCII
457         (void) write(SHOUT, "\007", 1);
458 #else
459     {
460         unsigned char beep_ch = CTL_ESC('\007');
461         (void) write(SHOUT, &beep_ch, 1);
462     }
463 #endif
464 }
465
466 /*
467  * Erase that silly ^[ and
468  * print the recognized part of the string
469  */
470 static void
471 print_recognized_stuff(recognized_part)
472     Char   *recognized_part;
473 {
474     /* An optimized erasing of that silly ^[ */
475     (void) putraw('\b');
476     (void) putraw('\b');
477     switch (Strlen(recognized_part)) {
478
479     case 0:                     /* erase two Characters: ^[ */
480         (void) putraw(' ');
481         (void) putraw(' ');
482         (void) putraw('\b');
483         (void) putraw('\b');
484         break;
485
486     case 1:                     /* overstrike the ^, erase the [ */
487         xprintf("%S", recognized_part);
488         (void) putraw(' ');
489         (void) putraw('\b');
490         break;
491
492     default:                    /* overstrike both Characters ^[ */
493         xprintf("%S", recognized_part);
494         break;
495     }
496     flush();
497 }
498
499 /*
500  * Parse full path in file into 2 parts: directory and file names
501  * Should leave final slash (/) at end of dir.
502  */
503 static void
504 extract_dir_and_name(path, dir, name)
505     Char   *path, *dir, *name;
506 {
507     Char *p;
508
509     p = Strrchr(path, '/');
510     if (p == NULL) {
511         copyn(name, path, MAXNAMLEN);
512         dir[0] = '\0';
513     }
514     else {
515         copyn(name, ++p, MAXNAMLEN);
516         copyn(dir, path, p - path);
517     }
518 }
519 /* atp vmsposix - I need to remove all the setpwent 
520  *                getpwent endpwent stuff. VMS_POSIX has getpwnam getpwuid
521  *                and getlogin. This needs fixing. (There is no access to 
522  *                pw->passwd in VMS - a secure system benefit :-| )
523  */
524 static Char *
525 getitem(dir_fd, looking_for_lognames)
526     DIR    *dir_fd;
527     int     looking_for_lognames;
528 {
529     struct passwd *pw;
530     struct dirent *dirp;
531
532     if (looking_for_lognames) {
533 #ifdef _VMS_POSIX
534             return (NULL);
535 #else
536         if ((pw = getpwent()) == NULL)
537             return (NULL);
538         return (str2short(pw->pw_name));
539 #endif /* atp vmsposix */
540     }
541     if ((dirp = readdir(dir_fd)) != NULL)
542         return (str2short(dirp->d_name));
543     return (NULL);
544 }
545
546 static void
547 free_items(items)
548     Char **items;
549 {
550     int i;
551
552     for (i = 0; items[i]; i++)
553         xfree((ptr_t) items[i]);
554     xfree((ptr_t) items);
555 }
556
557 #ifdef BSDSIGS
558 # define FREE_ITEMS(items) { \
559         sigmask_t omask;\
560 \
561         omask = sigblock(sigmask(SIGINT));\
562         free_items(items);\
563         items = NULL;\
564         (void) sigsetmask(omask);\
565 }
566 #else
567 # define FREE_ITEMS(items) { \
568         (void) sighold(SIGINT);\
569         free_items(items);\
570         items = NULL;\
571         (void) sigrelse(SIGINT);\
572 }
573 #endif /* BSDSIGS */
574
575 /*
576  * Perform a RECOGNIZE or LIST command on string "word".
577  */
578 static int
579 tsearch(word, command, max_word_length)
580     Char   *word;
581     int     max_word_length;
582     COMMAND command;
583 {
584     static Char **items = NULL;
585     DIR *dir_fd;
586     int numitems = 0, ignoring = TRUE, nignored = 0;
587     int name_length, looking_for_lognames;
588     Char    tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
589     Char    name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
590     Char   *item;
591
592 #define MAXITEMS 1024
593
594     if (items != NULL)
595         FREE_ITEMS(items);
596
597     looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
598     if (looking_for_lognames) {
599 #ifndef _VMS_POSIX
600         (void) setpwent();
601 #endif /*atp vmsposix */
602         copyn(name, &word[1], MAXNAMLEN);       /* name sans ~ */
603         dir_fd = NULL;
604     }
605     else {
606         extract_dir_and_name(word, dir, name);
607         if (tilde(tilded_dir, dir) == 0)
608             return (0);
609         dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
610         if (dir_fd == NULL)
611             return (0);
612     }
613
614 again:                          /* search for matches */
615     name_length = Strlen(name);
616     for (numitems = 0;
617         (item = getitem(dir_fd, looking_for_lognames)) != NULL;) {
618         if (!is_prefix(name, item))
619             continue;
620         /* Don't match . files on null prefix match */
621         if (name_length == 0 && item[0] == '.' &&
622             !looking_for_lognames)
623             continue;
624         if (command == LIST) {
625             if (numitems >= MAXITEMS) {
626                 xprintf(CGETS(14, 1, "\nYikes!! Too many %s!!\n"),
627                         looking_for_lognames ?
628                         CGETS(14, 2, "names in password file") :
629                         CGETS(14, 3, "files"));
630                 break;
631             }
632             /*
633              * From Beto Appleton (beto@aixwiz.austin.ibm.com)
634              *  typing "./control-d" will cause the csh to core-dump.
635              *  the problem can be reproduce as following:
636              *   1. set ignoreeof
637              *   2. set filec
638              *   3. create a directory with 1050 files
639              *   4. typing "./control-d" will cause the csh to core-dump
640              * Solution: Add + 1 to MAXITEMS
641              */
642             if (items == NULL)
643                 items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS + 1);
644             items[numitems] = (Char *) xmalloc((size_t) (Strlen(item) + 1) *
645                                                sizeof(Char));
646             copyn(items[numitems], item, MAXNAMLEN);
647             numitems++;
648         }
649         else {                  /* RECOGNIZE command */
650             if (ignoring && ignored(item))
651                 nignored++;
652             else if (recognize(extended_name,
653                                item, name_length, ++numitems))
654                 break;
655         }
656     }
657     if (ignoring && numitems == 0 && nignored > 0) {
658         ignoring = FALSE;
659         nignored = 0;
660         if (looking_for_lognames)
661 #ifndef _VMS_POSIX
662             (void) setpwent();
663 #endif /* atp vmsposix */
664         else
665             rewinddir(dir_fd);
666         goto again;
667     }
668
669     if (looking_for_lognames)
670 #ifndef _VMS_POSIX
671         (void) endpwent();
672 #endif /*atp vmsposix */
673     else
674         (void) closedir(dir_fd);
675     if (numitems == 0)
676         return (0);
677     if (command == RECOGNIZE) {
678         if (looking_for_lognames)
679             copyn(word, STRtilde, 1);
680         else
681             /* put back dir part */
682             copyn(word, dir, max_word_length);
683         /* add extended name */
684         catn(word, extended_name, max_word_length);
685         return (numitems);
686     }
687     else {                      /* LIST */
688         qsort((ptr_t) items, (size_t) numitems, sizeof(items[0]), 
689             (int (*) __P((const void *, const void *))) compare);
690         print_by_column(looking_for_lognames ? NULL : tilded_dir,
691                         items, numitems);
692         if (items != NULL)
693             FREE_ITEMS(items);
694     }
695     return (0);
696 }
697
698
699 static int
700 compare(p, q)
701     const ptr_t  p, q;
702 {
703 #if defined(NLS) && !defined(NOSTRCOLL)
704     errno = 0;  /* strcoll sets errno, another brain-damage */
705  
706     return (strcoll(*(char **) p, *(char **) q));
707 #else
708     return (strcmp(*(char **) p, *(char **) q));
709 #endif /* NLS && !NOSTRCOLL */
710 }
711
712 /*
713  * Object: extend what user typed up to an ambiguity.
714  * Algorithm:
715  * On first match, copy full item (assume it'll be the only match)
716  * On subsequent matches, shorten extended_name to the first
717  * Character mismatch between extended_name and item.
718  * If we shorten it back to the prefix length, stop searching.
719  */
720 static int
721 recognize(extended_name, item, name_length, numitems)
722     Char   *extended_name, *item;
723     int     name_length, numitems;
724 {
725     if (numitems == 1)          /* 1st match */
726         copyn(extended_name, item, MAXNAMLEN);
727     else {                      /* 2nd & subsequent matches */
728         Char *x, *ent;
729         int len = 0;
730
731         x = extended_name;
732         for (ent = item; *x && *x == *ent++; x++, len++);
733         *x = '\0';              /* Shorten at 1st Char diff */
734         if (len == name_length) /* Ambiguous to prefix? */
735             return (-1);        /* So stop now and save time */
736     }
737     return (0);
738 }
739
740 /*
741  * Return true if check matches initial Chars in template.
742  * This differs from PWB imatch in that if check is null
743  * it matches anything.
744  */
745 static int
746 is_prefix(check, template)
747     Char *check, *template;
748 {
749     do
750         if (*check == 0)
751             return (TRUE);
752     while (*check++ == *template++);
753     return (FALSE);
754 }
755
756 /*
757  *  Return true if the Chars in template appear at the
758  *  end of check, I.e., are it's suffix.
759  */
760 static int
761 is_suffix(check, template)
762     Char   *check, *template;
763 {
764     Char *c, *t;
765
766     for (c = check; *c++;);
767     for (t = template; *t++;);
768     for (;;) {
769         if (t == template)
770             return 1;
771         if (c == check || *--t != *--c)
772             return 0;
773     }
774 }
775
776 int
777 tenex(inputline, inputline_size)
778     Char   *inputline;
779     int     inputline_size;
780 {
781     int numitems, num_read;
782     char    tinputline[BUFSIZE];
783
784
785     setup_tty(ON);
786
787     while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) {
788         int     i;
789         static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
790         '>', '(', ')', '|', '^', '%', '\0'};
791         Char *str_end, *word_start, last_Char, should_retype;
792         int space_left;
793         COMMAND command;
794
795         for (i = 0; i < num_read; i++)
796             inputline[i] = (unsigned char) tinputline[i];
797         last_Char = inputline[num_read - 1] & ASCII;
798
799         if (last_Char == '\n' || num_read == inputline_size)
800             break;
801         command = (last_Char == ESC) ? RECOGNIZE : LIST;
802         if (command == LIST)
803             xputchar('\n');
804         str_end = &inputline[num_read];
805         if (last_Char == ESC)
806             --str_end;          /* wipeout trailing cmd Char */
807         *str_end = '\0';
808         /*
809          * Find LAST occurence of a delimiter in the inputline. The word start
810          * is one Character past it.
811          */
812         for (word_start = str_end; word_start > inputline; --word_start)
813             if (Strchr(delims, word_start[-1]))
814                 break;
815         space_left = inputline_size - (word_start - inputline) - 1;
816         numitems = tsearch(word_start, command, space_left);
817
818         if (command == RECOGNIZE) {
819             /* print from str_end on */
820             print_recognized_stuff(str_end);
821             if (numitems != 1)  /* Beep = No match/ambiguous */
822                 beep();
823         }
824
825         /*
826          * Tabs in the input line cause trouble after a pushback. tty driver
827          * won't backspace over them because column positions are now
828          * incorrect. This is solved by retyping over current line.
829          */
830         should_retype = FALSE;
831         if (Strchr(inputline, '\t')) {  /* tab Char in input line? */
832             back_to_col_1();
833             should_retype = TRUE;
834         }
835         if (command == LIST)    /* Always retype after a LIST */
836             should_retype = TRUE;
837         if (should_retype)
838             printprompt(0, NULL);
839         pushback(inputline);
840         if (should_retype)
841             retype();
842     }
843     setup_tty(OFF);
844     return (num_read);
845 }
846
847 static int
848 ignored(item)
849     Char *item;
850 {
851     struct varent *vp;
852     Char **cp;
853
854     if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
855         return (FALSE);
856     for (; *cp != NULL; cp++)
857         if (is_suffix(item, *cp))
858             return (TRUE);
859     return (FALSE);
860 }
861 #endif  /* FILEC && TIOCSTI */