sh.file.c revision 131962
1/* $Header: /src/pub/tcsh/sh.file.c,v 3.23 2003/02/08 20:03:26 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
36RCSID("$Id: sh.file.c,v 3.23 2003/02/08 20:03:26 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
58typedef enum {
59    LIST, RECOGNIZE
60}       COMMAND;
61
62static	void	 setup_tty		__P((int));
63static	void	 back_to_col_1		__P((void));
64static	void	 pushback		__P((Char *));
65static	void	 catn			__P((Char *, Char *, int));
66static	void	 copyn			__P((Char *, Char *, int));
67static	Char	 filetype		__P((Char *, Char *));
68static	void	 print_by_column	__P((Char *, Char *[], int));
69static	Char 	*tilde			__P((Char *, Char *));
70static	void	 retype			__P((void));
71static	void	 beep			__P((void));
72static	void 	 print_recognized_stuff	__P((Char *));
73static	void	 extract_dir_and_name	__P((Char *, Char *, Char *));
74static	Char	*getitem		__P((DIR *, int));
75static	void	 free_items		__P((Char **, size_t));
76static	int	 tsearch		__P((Char *, COMMAND, int));
77static	int	 compare		__P((const ptr_t, const ptr_t));
78static	int	 recognize		__P((Char *, Char *, int, int));
79static	int	 is_prefix		__P((Char *, Char *));
80static	int	 is_suffix		__P((Char *, Char *));
81static	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 */
89bool    filec = 0;
90
91static void
92setup_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 */
166static void
167back_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 */
224static void
225pushback(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 */
290static void
291catn(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 */
307static void
308copyn(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
318static  Char
319filetype(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
349static struct winsize win;
350
351/*
352 * Print sorted down columns
353 */
354static void
355print_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 */
396static Char *
397tilde(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 */
424static void
425retype()
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
452static void
453beep()
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 */
470static void
471print_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 */
503static void
504extract_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 */
524static Char *
525getitem(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
546static void
547free_items(items, numitems)
548    Char **items;
549    size_t numitems;
550{
551    size_t i;
552
553    for (i = 0; i < numitems; i++)
554	xfree((ptr_t) items[i]);
555    xfree((ptr_t) items);
556}
557
558#ifdef BSDSIGS
559# define FREE_ITEMS(items, numitems) { \
560	sigmask_t omask;\
561\
562	omask = sigblock(sigmask(SIGINT));\
563	free_items(items, numitems);\
564	(void) sigsetmask(omask);\
565}
566#else
567# define FREE_ITEMS(items, numitems) { \
568	(void) sighold(SIGINT);\
569	free_items(items, numitems);\
570	(void) sigrelse(SIGINT);\
571}
572#endif /* BSDSIGS */
573
574/*
575 * Perform a RECOGNIZE or LIST command on string "word".
576 */
577static int
578tsearch(word, command, max_word_length)
579    Char   *word;
580    int     max_word_length;
581    COMMAND command;
582{
583    DIR *dir_fd;
584    int numitems = 0, ignoring = TRUE, nignored = 0;
585    int name_length, looking_for_lognames;
586    Char    tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
587    Char    name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
588    Char   *item;
589    Char **items = NULL;
590    size_t maxitems = 0;
591
592    looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
593    if (looking_for_lognames) {
594#ifndef _VMS_POSIX
595	(void) setpwent();
596#endif /*atp vmsposix */
597	copyn(name, &word[1], MAXNAMLEN);	/* name sans ~ */
598	dir_fd = NULL;
599    }
600    else {
601	extract_dir_and_name(word, dir, name);
602	if (tilde(tilded_dir, dir) == 0)
603	    return (0);
604	dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
605	if (dir_fd == NULL)
606	    return (0);
607    }
608
609again:				/* search for matches */
610    name_length = Strlen(name);
611    for (numitems = 0;
612	(item = getitem(dir_fd, looking_for_lognames)) != NULL;) {
613	if (!is_prefix(name, item))
614	    continue;
615	/* Don't match . files on null prefix match */
616	if (name_length == 0 && item[0] == '.' &&
617	    !looking_for_lognames)
618	    continue;
619	if (command == LIST) {
620	    if (numitems >= maxitems) {
621		maxitems += 1024;
622		if (items == NULL)
623			items = (Char **) xmalloc(sizeof(*items) * maxitems);
624		else
625			items = (Char **) xrealloc((ptr_t) items,
626			    sizeof(*items) * maxitems);
627	    }
628	    items[numitems] = (Char *) xmalloc((size_t) (Strlen(item) + 1) *
629					       sizeof(Char));
630	    copyn(items[numitems], item, MAXNAMLEN);
631	    numitems++;
632	}
633	else {			/* RECOGNIZE command */
634	    if (ignoring && ignored(item))
635		nignored++;
636	    else if (recognize(extended_name,
637			       item, name_length, ++numitems))
638		break;
639	}
640    }
641    if (ignoring && numitems == 0 && nignored > 0) {
642	ignoring = FALSE;
643	nignored = 0;
644	if (looking_for_lognames)
645#ifndef _VMS_POSIX
646	    (void) setpwent();
647#endif /* atp vmsposix */
648	else
649	    rewinddir(dir_fd);
650	goto again;
651    }
652
653    if (looking_for_lognames)
654#ifndef _VMS_POSIX
655	(void) endpwent();
656#endif /*atp vmsposix */
657    else
658	(void) closedir(dir_fd);
659    if (numitems == 0)
660	return (0);
661    if (command == RECOGNIZE) {
662	if (looking_for_lognames)
663	    copyn(word, STRtilde, 1);
664	else
665	    /* put back dir part */
666	    copyn(word, dir, max_word_length);
667	/* add extended name */
668	catn(word, extended_name, max_word_length);
669	return (numitems);
670    }
671    else {			/* LIST */
672	qsort((ptr_t) items, (size_t) numitems, sizeof(items[0]),
673	    (int (*) __P((const void *, const void *))) compare);
674	print_by_column(looking_for_lognames ? NULL : tilded_dir,
675			items, numitems);
676	if (items != NULL)
677	    FREE_ITEMS(items, numitems);
678    }
679    return (0);
680}
681
682
683static int
684compare(p, q)
685    const ptr_t  p, q;
686{
687#if defined(NLS) && !defined(NOSTRCOLL)
688    errno = 0;  /* strcoll sets errno, another brain-damage */
689
690    return (strcoll(*(char **) p, *(char **) q));
691#else
692    return (strcmp(*(char **) p, *(char **) q));
693#endif /* NLS && !NOSTRCOLL */
694}
695
696/*
697 * Object: extend what user typed up to an ambiguity.
698 * Algorithm:
699 * On first match, copy full item (assume it'll be the only match)
700 * On subsequent matches, shorten extended_name to the first
701 * Character mismatch between extended_name and item.
702 * If we shorten it back to the prefix length, stop searching.
703 */
704static int
705recognize(extended_name, item, name_length, numitems)
706    Char   *extended_name, *item;
707    int     name_length, numitems;
708{
709    if (numitems == 1)		/* 1st match */
710	copyn(extended_name, item, MAXNAMLEN);
711    else {			/* 2nd & subsequent matches */
712	Char *x, *ent;
713	int len = 0;
714
715	x = extended_name;
716	for (ent = item; *x && *x == *ent++; x++, len++);
717	*x = '\0';		/* Shorten at 1st Char diff */
718	if (len == name_length)	/* Ambiguous to prefix? */
719	    return (-1);	/* So stop now and save time */
720    }
721    return (0);
722}
723
724/*
725 * Return true if check matches initial Chars in template.
726 * This differs from PWB imatch in that if check is null
727 * it matches anything.
728 */
729static int
730is_prefix(check, template)
731    Char *check, *template;
732{
733    do
734	if (*check == 0)
735	    return (TRUE);
736    while (*check++ == *template++);
737    return (FALSE);
738}
739
740/*
741 *  Return true if the Chars in template appear at the
742 *  end of check, I.e., are it's suffix.
743 */
744static int
745is_suffix(check, template)
746    Char   *check, *template;
747{
748    Char *c, *t;
749
750    for (c = check; *c++;);
751    for (t = template; *t++;);
752    for (;;) {
753	if (t == template)
754	    return 1;
755	if (c == check || *--t != *--c)
756	    return 0;
757    }
758}
759
760int
761tenex(inputline, inputline_size)
762    Char   *inputline;
763    int     inputline_size;
764{
765    int numitems, num_read;
766    char    tinputline[BUFSIZE];
767
768
769    setup_tty(ON);
770
771    while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) {
772	int     i;
773	static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
774	'>', '(', ')', '|', '^', '%', '\0'};
775	Char *str_end, *word_start, last_Char, should_retype;
776	int space_left;
777	COMMAND command;
778
779	for (i = 0; i < num_read; i++)
780	    inputline[i] = (unsigned char) tinputline[i];
781	last_Char = inputline[num_read - 1] & ASCII;
782
783	if (last_Char == '\n' || num_read == inputline_size)
784	    break;
785	command = (last_Char == ESC) ? RECOGNIZE : LIST;
786	if (command == LIST)
787	    xputchar('\n');
788	str_end = &inputline[num_read];
789	if (last_Char == ESC)
790	    --str_end;		/* wipeout trailing cmd Char */
791	*str_end = '\0';
792	/*
793	 * Find LAST occurence of a delimiter in the inputline. The word start
794	 * is one Character past it.
795	 */
796	for (word_start = str_end; word_start > inputline; --word_start)
797	    if (Strchr(delims, word_start[-1]))
798		break;
799	space_left = inputline_size - (word_start - inputline) - 1;
800	numitems = tsearch(word_start, command, space_left);
801
802	if (command == RECOGNIZE) {
803	    /* print from str_end on */
804	    print_recognized_stuff(str_end);
805	    if (numitems != 1)	/* Beep = No match/ambiguous */
806		beep();
807	}
808
809	/*
810	 * Tabs in the input line cause trouble after a pushback. tty driver
811	 * won't backspace over them because column positions are now
812	 * incorrect. This is solved by retyping over current line.
813	 */
814	should_retype = FALSE;
815	if (Strchr(inputline, '\t')) {	/* tab Char in input line? */
816	    back_to_col_1();
817	    should_retype = TRUE;
818	}
819	if (command == LIST)	/* Always retype after a LIST */
820	    should_retype = TRUE;
821	if (should_retype)
822	    printprompt(0, NULL);
823	pushback(inputline);
824	if (should_retype)
825	    retype();
826    }
827    setup_tty(OFF);
828    return (num_read);
829}
830
831static int
832ignored(item)
833    Char *item;
834{
835    struct varent *vp;
836    Char **cp;
837
838    if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
839	return (FALSE);
840    for (; *cp != NULL; cp++)
841	if (is_suffix(item, *cp))
842	    return (TRUE);
843    return (FALSE);
844}
845#endif	/* FILEC && TIOCSTI */
846