1*509d26c5Snia /* $NetBSD: file.c,v 1.34 2024/04/24 15:49:03 nia Exp $ */
249f0ad86Scgd
361f28255Scgd /*-
4cee2bad8Smycroft * Copyright (c) 1980, 1991, 1993
5cee2bad8Smycroft * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * Redistribution and use in source and binary forms, with or without
861f28255Scgd * modification, are permitted provided that the following conditions
961f28255Scgd * are met:
1061f28255Scgd * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd * notice, this list of conditions and the following disclaimer.
1261f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd * notice, this list of conditions and the following disclaimer in the
1461f28255Scgd * documentation and/or other materials provided with the distribution.
15b5b29542Sagc * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd * may be used to endorse or promote products derived from this software
1761f28255Scgd * without specific prior written permission.
1861f28255Scgd *
1961f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd * SUCH DAMAGE.
3061f28255Scgd */
3161f28255Scgd
328ea378c6Schristos #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3449f0ad86Scgd #if 0
3549f0ad86Scgd static char sccsid[] = "@(#)file.c 8.2 (Berkeley) 3/19/94";
3649f0ad86Scgd #else
37*509d26c5Snia __RCSID("$NetBSD: file.c,v 1.34 2024/04/24 15:49:03 nia Exp $");
3849f0ad86Scgd #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd
4161f28255Scgd #ifdef FILEC
4261f28255Scgd
4361f28255Scgd #include <sys/ioctl.h>
44b771e65bSwiz #include <sys/param.h>
4561f28255Scgd #include <sys/stat.h>
46f1a39637Sitohy #include <sys/tty.h>
47b771e65bSwiz
4861f28255Scgd #include <dirent.h>
4961f28255Scgd #include <pwd.h>
50b771e65bSwiz #include <termios.h>
5118158540Swiz #include <stdarg.h>
5261f28255Scgd #include <stdlib.h>
5361f28255Scgd #include <unistd.h>
54b771e65bSwiz
55cee2bad8Smycroft #ifndef SHORT_STRINGS
56cee2bad8Smycroft #include <string.h>
57cee2bad8Smycroft #endif /* SHORT_STRINGS */
58b771e65bSwiz
5961f28255Scgd #include "csh.h"
6061f28255Scgd #include "extern.h"
6161f28255Scgd
6261f28255Scgd /*
6361f28255Scgd * Tenex style file name recognition, .. and more.
6461f28255Scgd * History:
6561f28255Scgd * Author: Ken Greer, Sept. 1975, CMU.
6661f28255Scgd * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
6761f28255Scgd */
6861f28255Scgd
6961f28255Scgd #define ON 1
7061f28255Scgd #define OFF 0
7161f28255Scgd #ifndef TRUE
7261f28255Scgd #define TRUE 1
7361f28255Scgd #endif
7461f28255Scgd #ifndef FALSE
7561f28255Scgd #define FALSE 0
7661f28255Scgd #endif
7761f28255Scgd
7861f28255Scgd #define ESC '\033'
7961f28255Scgd
8061f28255Scgd typedef enum {
8161f28255Scgd LIST, RECOGNIZE
8261f28255Scgd } COMMAND;
8361f28255Scgd
84b771e65bSwiz static void setup_tty(int);
85b771e65bSwiz static void back_to_col_1(void);
86b771e65bSwiz static int pushback(Char *);
8737e39248Schristos static void catn(Char *, Char *, size_t);
8837e39248Schristos static void copyn(Char *, Char *, size_t);
89b771e65bSwiz static Char filetype(Char *, Char *);
9037e39248Schristos static void print_by_column(Char *, Char *[], size_t);
91b771e65bSwiz static Char *tilde(Char *, Char *);
92b771e65bSwiz static void retype(void);
93b771e65bSwiz static void beep(void);
94b771e65bSwiz static void print_recognized_stuff(Char *);
95b771e65bSwiz static void extract_dir_and_name(Char *, Char *, Char *);
96b771e65bSwiz static Char *getentry(DIR *, int);
97a7c23d87Schristos static void free_items(Char **, size_t);
9837e39248Schristos static size_t tsearch(Char *, COMMAND, size_t);
9937e39248Schristos static int recognize(Char *, Char *, size_t, size_t);
100b771e65bSwiz static int is_prefix(Char *, Char *);
101b771e65bSwiz static int is_suffix(Char *, Char *);
102b771e65bSwiz static int ignored(Char *);
10361f28255Scgd
10461f28255Scgd /*
10561f28255Scgd * Put this here so the binary can be patched with adb to enable file
10661f28255Scgd * completion by default. Filec controls completion, nobeep controls
10761f28255Scgd * ringing the terminal bell on incomplete expansions.
10861f28255Scgd */
109b79c2ef2Schristos int filec = 0;
11061f28255Scgd
11161f28255Scgd static void
setup_tty(int on)112b771e65bSwiz setup_tty(int on)
11361f28255Scgd {
114bec9fc50Scgd struct termios tchars;
11561f28255Scgd
11661f28255Scgd (void)tcgetattr(SHIN, &tchars);
117cee2bad8Smycroft
118cee2bad8Smycroft if (on) {
11961f28255Scgd tchars.c_cc[VEOL] = ESC;
12061f28255Scgd if (tchars.c_lflag & ICANON)
121af75045fSpk on = TCSADRAIN;
12261f28255Scgd else {
12361f28255Scgd tchars.c_lflag |= ICANON;
124cee2bad8Smycroft on = TCSAFLUSH;
12561f28255Scgd }
12661f28255Scgd }
12761f28255Scgd else {
12861f28255Scgd tchars.c_cc[VEOL] = _POSIX_VDISABLE;
129cee2bad8Smycroft on = TCSADRAIN;
13061f28255Scgd }
131cee2bad8Smycroft
132cee2bad8Smycroft (void)tcsetattr(SHIN, on, &tchars);
13361f28255Scgd }
13461f28255Scgd
13561f28255Scgd /*
13661f28255Scgd * Move back to beginning of current line
13761f28255Scgd */
13861f28255Scgd static void
back_to_col_1(void)139b771e65bSwiz back_to_col_1(void)
14061f28255Scgd {
14161f28255Scgd struct termios tty, tty_normal;
142b3df6303Skleink sigset_t nsigset, osigset;
14361f28255Scgd
144b3df6303Skleink sigemptyset(&nsigset);
145b3df6303Skleink (void)sigaddset(&nsigset, SIGINT);
146b3df6303Skleink (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
14761f28255Scgd (void)tcgetattr(SHOUT, &tty);
14861f28255Scgd tty_normal = tty;
14961f28255Scgd tty.c_iflag &= ~INLCR;
15061f28255Scgd tty.c_oflag &= ~ONLCR;
15108051348Schristos (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
15261f28255Scgd (void)write(SHOUT, "\r", 1);
15308051348Schristos (void)tcsetattr(SHOUT, TCSADRAIN, &tty_normal);
1545924694dSmycroft (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
15561f28255Scgd }
15661f28255Scgd
15761f28255Scgd /*
15861f28255Scgd * Push string contents back into tty queue
15961f28255Scgd */
160e50ba7eaSitohy static int
pushback(Char * string)161b771e65bSwiz pushback(Char *string)
16261f28255Scgd {
16361f28255Scgd struct termios tty, tty_normal;
16403561a04Schristos char buf[64], svchars[sizeof(buf)];
165b3df6303Skleink sigset_t nsigset, osigset;
166b771e65bSwiz Char *p;
16703561a04Schristos size_t bufidx, i, len_str, nbuf, nsv, onsv, retrycnt;
16861f28255Scgd char c;
16961f28255Scgd
170b771e65bSwiz nsv = 0;
171b3df6303Skleink sigemptyset(&nsigset);
172b3df6303Skleink (void)sigaddset(&nsigset, SIGINT);
173b3df6303Skleink (void)sigprocmask(SIG_BLOCK, &nsigset, &osigset);
17461f28255Scgd (void)tcgetattr(SHOUT, &tty);
17561f28255Scgd tty_normal = tty;
17661f28255Scgd tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
177e50ba7eaSitohy /* FIONREAD works only in noncanonical mode. */
178e50ba7eaSitohy tty.c_lflag &= ~ICANON;
179e50ba7eaSitohy tty.c_cc[VMIN] = 0;
18008051348Schristos (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
18161f28255Scgd
182e50ba7eaSitohy for (retrycnt = 5; ; retrycnt--) {
183e50ba7eaSitohy /*
184e50ba7eaSitohy * Push back characters.
185e50ba7eaSitohy */
18637e39248Schristos for (p = string; (c = (char)*p) != '\0'; p++)
18761f28255Scgd (void)ioctl(SHOUT, TIOCSTI, (ioctl_t) &c);
188e50ba7eaSitohy for (i = 0; i < nsv; i++)
189e50ba7eaSitohy (void)ioctl(SHOUT, TIOCSTI, (ioctl_t) &svchars[i]);
190e50ba7eaSitohy
191e50ba7eaSitohy if (retrycnt == 0)
192e50ba7eaSitohy break; /* give up salvaging characters */
193e50ba7eaSitohy
19437e39248Schristos len_str = (size_t)(p - string);
195e50ba7eaSitohy
196e50ba7eaSitohy if (ioctl(SHOUT, FIONREAD, (ioctl_t) &nbuf) ||
197e50ba7eaSitohy nbuf <= len_str + nsv || /* The string fit. */
19803561a04Schristos nbuf > sizeof(buf)) /* For future binary compatibility
199e50ba7eaSitohy (and safety). */
200e50ba7eaSitohy break;
201e50ba7eaSitohy
202e50ba7eaSitohy /*
203e50ba7eaSitohy * User has typed characters before the pushback finished.
204e50ba7eaSitohy * Salvage the characters.
205e50ba7eaSitohy */
206e50ba7eaSitohy
207e50ba7eaSitohy /* This read() should be in noncanonical mode. */
20803561a04Schristos if (read(SHOUT, &buf, nbuf) != (ssize_t)nbuf)
209e50ba7eaSitohy continue; /* hangup? */
210e50ba7eaSitohy
211e50ba7eaSitohy onsv = nsv;
212e50ba7eaSitohy for (bufidx = 0, i = 0; bufidx < nbuf; bufidx++, i++) {
213e50ba7eaSitohy c = buf[bufidx];
214e50ba7eaSitohy if ((i < len_str) ? c != (char)string[i] :
215e50ba7eaSitohy (i < len_str + onsv) ? c != svchars[i - len_str] : 1) {
216e50ba7eaSitohy /* Salvage a character. */
217e50ba7eaSitohy if (nsv < (int)(sizeof svchars / sizeof svchars[0])) {
218e50ba7eaSitohy svchars[nsv++] = c;
219e50ba7eaSitohy i--; /* try this comparison with the next char */
220e50ba7eaSitohy } else
221f1a39637Sitohy break; /* too many */
222e50ba7eaSitohy }
223e50ba7eaSitohy }
224e50ba7eaSitohy }
225e50ba7eaSitohy
226e50ba7eaSitohy #if 1
227e50ba7eaSitohy /*
228e50ba7eaSitohy * XXX Is this a bug or a feature of kernel tty driver?
229e50ba7eaSitohy *
230e50ba7eaSitohy * FIONREAD in canonical mode does not return correct byte count
231e50ba7eaSitohy * in tty input queue, but this is required to avoid unwanted echo.
232e50ba7eaSitohy */
233e50ba7eaSitohy tty.c_lflag |= ICANON;
234e50ba7eaSitohy (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
235e50ba7eaSitohy (void)ioctl(SHOUT, FIONREAD, (ioctl_t) &i);
236e50ba7eaSitohy #endif
23708051348Schristos (void)tcsetattr(SHOUT, TCSADRAIN, &tty_normal);
2385924694dSmycroft (void)sigprocmask(SIG_SETMASK, &osigset, NULL);
239e50ba7eaSitohy
24037e39248Schristos return (int)nsv;
24161f28255Scgd }
24261f28255Scgd
24361f28255Scgd /*
24461f28255Scgd * Concatenate src onto tail of des.
24561f28255Scgd * Des is a string whose maximum length is count.
24661f28255Scgd * Always null terminate.
24761f28255Scgd */
24861f28255Scgd static void
catn(Char * des,Char * src,size_t count)24937e39248Schristos catn(Char *des, Char *src, size_t count)
25061f28255Scgd {
25137e39248Schristos while (count-- > 0 && *des)
25261f28255Scgd des++;
25337e39248Schristos while (count-- > 0)
25461f28255Scgd if ((*des++ = *src++) == 0)
25561f28255Scgd return;
25661f28255Scgd *des = '\0';
25761f28255Scgd }
25861f28255Scgd
25961f28255Scgd /*
26061f28255Scgd * Like strncpy but always leave room for trailing \0
26161f28255Scgd * and always null terminate.
26261f28255Scgd */
26361f28255Scgd static void
copyn(Char * des,Char * src,size_t count)26437e39248Schristos copyn(Char *des, Char *src, size_t count)
26561f28255Scgd {
26637e39248Schristos while (count-- > 0)
26761f28255Scgd if ((*des++ = *src++) == 0)
26861f28255Scgd return;
26961f28255Scgd *des = '\0';
27061f28255Scgd }
27161f28255Scgd
27261f28255Scgd static Char
filetype(Char * dir,Char * file)273b771e65bSwiz filetype(Char *dir, Char *file)
27461f28255Scgd {
27561f28255Scgd struct stat statb;
276b771e65bSwiz Char path[MAXPATHLEN];
27761f28255Scgd
27861f28255Scgd catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
27961f28255Scgd if (lstat(short2str(path), &statb) == 0) {
28061f28255Scgd switch (statb.st_mode & S_IFMT) {
28161f28255Scgd case S_IFDIR:
28261f28255Scgd return ('/');
28361f28255Scgd case S_IFLNK:
28461f28255Scgd if (stat(short2str(path), &statb) == 0 && /* follow it out */
28561f28255Scgd S_ISDIR(statb.st_mode))
28661f28255Scgd return ('>');
28761f28255Scgd else
28861f28255Scgd return ('@');
28961f28255Scgd case S_IFSOCK:
29061f28255Scgd return ('=');
29161f28255Scgd default:
29261f28255Scgd if (statb.st_mode & 0111)
29361f28255Scgd return ('*');
29461f28255Scgd }
29561f28255Scgd }
29661f28255Scgd return (' ');
29761f28255Scgd }
29861f28255Scgd
29961f28255Scgd static struct winsize win;
30061f28255Scgd
30161f28255Scgd /*
30261f28255Scgd * Print sorted down columns
30361f28255Scgd */
30461f28255Scgd static void
print_by_column(Char * dir,Char * items[],size_t count)30537e39248Schristos print_by_column(Char *dir, Char *items[], size_t count)
30661f28255Scgd {
30737e39248Schristos size_t c, columns, i, maxwidth, r, rows;
308b771e65bSwiz
309b771e65bSwiz maxwidth = 0;
31061f28255Scgd
31161f28255Scgd if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
31261f28255Scgd win.ws_col = 80;
31361f28255Scgd for (i = 0; i < count; i++)
31461f28255Scgd maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
31561f28255Scgd maxwidth += 2; /* for the file tag and space */
31661f28255Scgd columns = win.ws_col / maxwidth;
31761f28255Scgd if (columns == 0)
31861f28255Scgd columns = 1;
31961f28255Scgd rows = (count + (columns - 1)) / columns;
32061f28255Scgd for (r = 0; r < rows; r++) {
32161f28255Scgd for (c = 0; c < columns; c++) {
32261f28255Scgd i = c * rows + r;
32361f28255Scgd if (i < count) {
32437e39248Schristos size_t w;
32561f28255Scgd
326cee2bad8Smycroft (void)fprintf(cshout, "%s", vis_str(items[i]));
327cee2bad8Smycroft (void)fputc(dir ? filetype(dir, items[i]) : ' ', cshout);
32861f28255Scgd if (c < columns - 1) { /* last column? */
32961f28255Scgd w = Strlen(items[i]) + 1;
33061f28255Scgd for (; w < maxwidth; w++)
331cee2bad8Smycroft (void) fputc(' ', cshout);
33261f28255Scgd }
33361f28255Scgd }
33461f28255Scgd }
335cee2bad8Smycroft (void)fputc('\r', cshout);
336cee2bad8Smycroft (void)fputc('\n', cshout);
33761f28255Scgd }
33861f28255Scgd }
33961f28255Scgd
34061f28255Scgd /*
34161f28255Scgd * Expand file name with possible tilde usage
34261f28255Scgd * ~person/mumble
34361f28255Scgd * expands to
34461f28255Scgd * home_directory_of_person/mumble
34561f28255Scgd */
34661f28255Scgd static Char *
tilde(Char * new,Char * old)347b771e65bSwiz tilde(Char *new, Char *old)
34861f28255Scgd {
34961f28255Scgd static Char person[40];
350b771e65bSwiz struct passwd *pw;
351b771e65bSwiz Char *o, *p;
35261f28255Scgd
35361f28255Scgd if (old[0] != '~')
35461f28255Scgd return (Strcpy(new, old));
35561f28255Scgd
356cee2bad8Smycroft for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
357cee2bad8Smycroft continue;
35861f28255Scgd *p = '\0';
35961f28255Scgd if (person[0] == '\0')
36061f28255Scgd (void)Strcpy(new, value(STRhome));
36161f28255Scgd else {
36261f28255Scgd pw = getpwnam(short2str(person));
36361f28255Scgd if (pw == NULL)
36461f28255Scgd return (NULL);
36561f28255Scgd (void)Strcpy(new, str2short(pw->pw_dir));
36661f28255Scgd }
36761f28255Scgd (void)Strcat(new, o);
36861f28255Scgd return (new);
36961f28255Scgd }
37061f28255Scgd
37161f28255Scgd /*
37261f28255Scgd * Cause pending line to be printed
37361f28255Scgd */
37461f28255Scgd static void
retype(void)375b771e65bSwiz retype(void)
37661f28255Scgd {
37761f28255Scgd struct termios tty;
37861f28255Scgd
37961f28255Scgd (void)tcgetattr(SHOUT, &tty);
38061f28255Scgd tty.c_lflag |= PENDIN;
38108051348Schristos (void)tcsetattr(SHOUT, TCSADRAIN, &tty);
38261f28255Scgd }
38361f28255Scgd
38461f28255Scgd static void
beep(void)385b771e65bSwiz beep(void)
38661f28255Scgd {
38761f28255Scgd if (adrof(STRnobeep) == 0)
38861f28255Scgd (void)write(SHOUT, "\007", 1);
38961f28255Scgd }
39061f28255Scgd
39161f28255Scgd /*
39261f28255Scgd * Erase that silly ^[ and
39361f28255Scgd * print the recognized part of the string
39461f28255Scgd */
39561f28255Scgd static void
print_recognized_stuff(Char * recognized_part)396b771e65bSwiz print_recognized_stuff(Char *recognized_part)
39761f28255Scgd {
39861f28255Scgd /* An optimized erasing of that silly ^[ */
399cee2bad8Smycroft (void)fputc('\b', cshout);
400cee2bad8Smycroft (void)fputc('\b', cshout);
40161f28255Scgd switch (Strlen(recognized_part)) {
40261f28255Scgd case 0: /* erase two Characters: ^[ */
403cee2bad8Smycroft (void)fputc(' ', cshout);
404cee2bad8Smycroft (void)fputc(' ', cshout);
405cee2bad8Smycroft (void)fputc('\b', cshout);
406cee2bad8Smycroft (void)fputc('\b', cshout);
40761f28255Scgd break;
40861f28255Scgd case 1: /* overstrike the ^, erase the [ */
409cee2bad8Smycroft (void)fprintf(cshout, "%s", vis_str(recognized_part));
410cee2bad8Smycroft (void)fputc(' ', cshout);
411cee2bad8Smycroft (void)fputc('\b', cshout);
41261f28255Scgd break;
41361f28255Scgd default: /* overstrike both Characters ^[ */
414cee2bad8Smycroft (void)fprintf(cshout, "%s", vis_str(recognized_part));
41561f28255Scgd break;
41661f28255Scgd }
417cee2bad8Smycroft (void)fflush(cshout);
41861f28255Scgd }
41961f28255Scgd
42061f28255Scgd /*
42161f28255Scgd * Parse full path in file into 2 parts: directory and file names
42261f28255Scgd * Should leave final slash (/) at end of dir.
42361f28255Scgd */
42461f28255Scgd static void
extract_dir_and_name(Char * path,Char * dir,Char * name)425b771e65bSwiz extract_dir_and_name(Char *path, Char *dir, Char *name)
42661f28255Scgd {
42776adbe2bStls Char *p;
42861f28255Scgd
42961f28255Scgd p = Strrchr(path, '/');
43061f28255Scgd if (p == NULL) {
43161f28255Scgd copyn(name, path, MAXNAMLEN);
43261f28255Scgd dir[0] = '\0';
43361f28255Scgd }
43461f28255Scgd else {
43561f28255Scgd copyn(name, ++p, MAXNAMLEN);
43637e39248Schristos copyn(dir, path, (size_t)(p - path));
43761f28255Scgd }
43861f28255Scgd }
43961f28255Scgd
44061f28255Scgd static Char *
getentry(DIR * dir_fd,int looking_for_lognames)441b771e65bSwiz getentry(DIR *dir_fd, int looking_for_lognames)
44261f28255Scgd {
44376adbe2bStls struct dirent *dirp;
444b771e65bSwiz struct passwd *pw;
44561f28255Scgd
44661f28255Scgd if (looking_for_lognames) {
44761f28255Scgd if ((pw = getpwent()) == NULL)
44861f28255Scgd return (NULL);
44961f28255Scgd return (str2short(pw->pw_name));
45061f28255Scgd }
451cee2bad8Smycroft if ((dirp = readdir(dir_fd)) != NULL)
45261f28255Scgd return (str2short(dirp->d_name));
45361f28255Scgd return (NULL);
45461f28255Scgd }
45561f28255Scgd
45661f28255Scgd static void
free_items(Char ** items,size_t numitems)457a7c23d87Schristos free_items(Char **items, size_t numitems)
45861f28255Scgd {
459a7c23d87Schristos size_t i;
46061f28255Scgd
461a7c23d87Schristos for (i = 0; i < numitems; i++)
4621767ce60Schristos free(items[i]);
4631767ce60Schristos free(items);
46461f28255Scgd }
46561f28255Scgd
466a7c23d87Schristos #define FREE_ITEMS(items, numitems) { \
467b3df6303Skleink sigset_t nsigset, osigset;\
46861f28255Scgd \
469b3df6303Skleink sigemptyset(&nsigset);\
470b3df6303Skleink (void) sigaddset(&nsigset, SIGINT);\
471b3df6303Skleink (void) sigprocmask(SIG_BLOCK, &nsigset, &osigset);\
472a7c23d87Schristos free_items(items, numitems);\
4735924694dSmycroft (void) sigprocmask(SIG_SETMASK, &osigset, NULL);\
47461f28255Scgd }
47561f28255Scgd
47661f28255Scgd /*
47761f28255Scgd * Perform a RECOGNIZE or LIST command on string "word".
47861f28255Scgd */
47937e39248Schristos static size_t
tsearch(Char * word,COMMAND command,size_t max_word_length)48037e39248Schristos tsearch(Char *word, COMMAND command, size_t max_word_length)
48161f28255Scgd {
482b771e65bSwiz Char dir[MAXPATHLEN + 1], extended_name[MAXNAMLEN + 1];
483b771e65bSwiz Char name[MAXNAMLEN + 1], tilded_dir[MAXPATHLEN + 1];
48476adbe2bStls DIR *dir_fd;
48561f28255Scgd Char *entry;
48637e39248Schristos int ignoring, looking_for_lognames;
48737e39248Schristos size_t name_length, nignored, numitems;
488a7c23d87Schristos Char **items = NULL;
489a7c23d87Schristos size_t maxitems = 0;
490b771e65bSwiz
491b771e65bSwiz numitems = 0;
492b771e65bSwiz ignoring = TRUE;
493b771e65bSwiz nignored = 0;
49461f28255Scgd
49561f28255Scgd looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
49661f28255Scgd if (looking_for_lognames) {
49761f28255Scgd (void)setpwent();
49861f28255Scgd copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */
49961f28255Scgd dir_fd = NULL;
50061f28255Scgd }
50161f28255Scgd else {
50261f28255Scgd extract_dir_and_name(word, dir, name);
50361f28255Scgd if (tilde(tilded_dir, dir) == 0)
50461f28255Scgd return (0);
50561f28255Scgd dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
50661f28255Scgd if (dir_fd == NULL)
50761f28255Scgd return (0);
50861f28255Scgd }
50961f28255Scgd
51061f28255Scgd again: /* search for matches */
51161f28255Scgd name_length = Strlen(name);
512cee2bad8Smycroft for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) {
51361f28255Scgd if (!is_prefix(name, entry))
51461f28255Scgd continue;
51561f28255Scgd /* Don't match . files on null prefix match */
51661f28255Scgd if (name_length == 0 && entry[0] == '.' &&
51761f28255Scgd !looking_for_lognames)
51861f28255Scgd continue;
51961f28255Scgd if (command == LIST) {
5209050ab5cSlukem if ((size_t)numitems >= maxitems) {
521a7c23d87Schristos maxitems += 1024;
522*509d26c5Snia items = xreallocarray(items, sizeof(*items), maxitems);
523a7c23d87Schristos }
524*509d26c5Snia items[numitems] = xreallocarray(NULL,
525*509d26c5Snia (size_t) (Strlen(entry) + 1), sizeof(Char));
52661f28255Scgd copyn(items[numitems], entry, MAXNAMLEN);
52761f28255Scgd numitems++;
52861f28255Scgd }
52961f28255Scgd else { /* RECOGNIZE command */
53061f28255Scgd if (ignoring && ignored(entry))
53161f28255Scgd nignored++;
53261f28255Scgd else if (recognize(extended_name,
53361f28255Scgd entry, name_length, ++numitems))
53461f28255Scgd break;
53561f28255Scgd }
53661f28255Scgd }
53761f28255Scgd if (ignoring && numitems == 0 && nignored > 0) {
53861f28255Scgd ignoring = FALSE;
53961f28255Scgd nignored = 0;
54061f28255Scgd if (looking_for_lognames)
54161f28255Scgd (void)setpwent();
54261f28255Scgd else
54361f28255Scgd rewinddir(dir_fd);
54461f28255Scgd goto again;
54561f28255Scgd }
54661f28255Scgd
54761f28255Scgd if (looking_for_lognames)
54861f28255Scgd (void)endpwent();
54961f28255Scgd else
55061f28255Scgd (void)closedir(dir_fd);
55161f28255Scgd if (numitems == 0)
55261f28255Scgd return (0);
55361f28255Scgd if (command == RECOGNIZE) {
55461f28255Scgd if (looking_for_lognames)
55561f28255Scgd copyn(word, STRtilde, 1);
55661f28255Scgd else
55761f28255Scgd /* put back dir part */
55861f28255Scgd copyn(word, dir, max_word_length);
55961f28255Scgd /* add extended name */
56061f28255Scgd catn(word, extended_name, max_word_length);
56161f28255Scgd return (numitems);
56261f28255Scgd }
56361f28255Scgd else { /* LIST */
5641767ce60Schristos qsort(items, numitems, sizeof(items[0]),
565b771e65bSwiz (int (*) (const void *, const void *)) sortscmp);
56661f28255Scgd print_by_column(looking_for_lognames ? NULL : tilded_dir,
56761f28255Scgd items, numitems);
56861f28255Scgd if (items != NULL)
569a7c23d87Schristos FREE_ITEMS(items, numitems);
57061f28255Scgd }
57161f28255Scgd return (0);
57261f28255Scgd }
57361f28255Scgd
57461f28255Scgd /*
57561f28255Scgd * Object: extend what user typed up to an ambiguity.
57661f28255Scgd * Algorithm:
57761f28255Scgd * On first match, copy full entry (assume it'll be the only match)
57861f28255Scgd * On subsequent matches, shorten extended_name to the first
57961f28255Scgd * Character mismatch between extended_name and entry.
58061f28255Scgd * If we shorten it back to the prefix length, stop searching.
58161f28255Scgd */
58261f28255Scgd static int
recognize(Char * extended_name,Char * entry,size_t name_length,size_t numitems)58337e39248Schristos recognize(Char *extended_name, Char *entry, size_t name_length, size_t numitems)
58461f28255Scgd {
58561f28255Scgd if (numitems == 1) /* 1st match */
58661f28255Scgd copyn(extended_name, entry, MAXNAMLEN);
58761f28255Scgd else { /* 2nd & subsequent matches */
588b771e65bSwiz Char *ent, *x;
58937e39248Schristos size_t len = 0;
59061f28255Scgd
59161f28255Scgd x = extended_name;
592cee2bad8Smycroft for (ent = entry; *x && *x == *ent++; x++, len++)
593cee2bad8Smycroft continue;
59461f28255Scgd *x = '\0'; /* Shorten at 1st Char diff */
59561f28255Scgd if (len == name_length) /* Ambiguous to prefix? */
59661f28255Scgd return (-1); /* So stop now and save time */
59761f28255Scgd }
59861f28255Scgd return (0);
59961f28255Scgd }
60061f28255Scgd
60161f28255Scgd /*
60261f28255Scgd * Return true if check matches initial Chars in template.
60361f28255Scgd * This differs from PWB imatch in that if check is null
60461f28255Scgd * it matches anything.
60561f28255Scgd */
60661f28255Scgd static int
is_prefix(Char * check,Char * template)607b771e65bSwiz is_prefix(Char *check, Char *template)
60861f28255Scgd {
60961f28255Scgd do
61061f28255Scgd if (*check == 0)
61161f28255Scgd return (TRUE);
61261f28255Scgd while (*check++ == *template++);
61361f28255Scgd return (FALSE);
61461f28255Scgd }
61561f28255Scgd
61661f28255Scgd /*
61761f28255Scgd * Return true if the Chars in template appear at the
618a640fe8cSsnj * end of check, I.e., are its suffix.
61961f28255Scgd */
62061f28255Scgd static int
is_suffix(Char * check,Char * template)621b771e65bSwiz is_suffix(Char *check, Char *template)
62261f28255Scgd {
62376adbe2bStls Char *c, *t;
62461f28255Scgd
625cee2bad8Smycroft for (c = check; *c++;)
626cee2bad8Smycroft continue;
627cee2bad8Smycroft for (t = template; *t++;)
628cee2bad8Smycroft continue;
62961f28255Scgd for (;;) {
63061f28255Scgd if (t == template)
63161f28255Scgd return 1;
63261f28255Scgd if (c == check || *--t != *--c)
63361f28255Scgd return 0;
63461f28255Scgd }
63561f28255Scgd }
63661f28255Scgd
63737e39248Schristos ssize_t
tenex(Char * inputline,size_t inputline_size)63837e39248Schristos tenex(Char *inputline, size_t inputline_size)
63961f28255Scgd {
6400bf6fd0cSchristos char tinputline[BUFSIZE];
64137e39248Schristos ssize_t num_read;
64237e39248Schristos size_t numitems;
64361f28255Scgd
64461f28255Scgd setup_tty(ON);
64561f28255Scgd
6460bf6fd0cSchristos while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) {
64737e39248Schristos size_t i, nr = (size_t) num_read;
64837e39248Schristos
649b771e65bSwiz
65061f28255Scgd static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
65161f28255Scgd '>', '(', ')', '|', '^', '%', '\0'};
65276adbe2bStls Char *str_end, *word_start, last_Char, should_retype;
65337e39248Schristos size_t space_left;
65461f28255Scgd COMMAND command;
65561f28255Scgd
65637e39248Schristos for (i = 0; i < nr; i++)
65761f28255Scgd inputline[i] = (unsigned char) tinputline[i];
65837e39248Schristos last_Char = inputline[nr - 1] & ASCII;
65961f28255Scgd
66037e39248Schristos if (last_Char == '\n' || nr == inputline_size)
66161f28255Scgd break;
66261f28255Scgd command = (last_Char == ESC) ? RECOGNIZE : LIST;
66361f28255Scgd if (command == LIST)
664cee2bad8Smycroft (void)fputc('\n', cshout);
66537e39248Schristos str_end = &inputline[nr];
66661f28255Scgd if (last_Char == ESC)
66761f28255Scgd --str_end; /* wipeout trailing cmd Char */
66861f28255Scgd *str_end = '\0';
66961f28255Scgd /*
67057920690Smsaitoh * Find LAST occurrence of a delimiter in the inputline. The word start
67161f28255Scgd * is one Character past it.
67261f28255Scgd */
67361f28255Scgd for (word_start = str_end; word_start > inputline; --word_start)
67461f28255Scgd if (Strchr(delims, word_start[-1]))
67561f28255Scgd break;
67637e39248Schristos space_left = inputline_size - (size_t)(word_start - inputline) - 1;
67761f28255Scgd numitems = tsearch(word_start, command, space_left);
67861f28255Scgd
67961f28255Scgd if (command == RECOGNIZE) {
68061f28255Scgd /* print from str_end on */
68161f28255Scgd print_recognized_stuff(str_end);
68261f28255Scgd if (numitems != 1) /* Beep = No match/ambiguous */
68361f28255Scgd beep();
68461f28255Scgd }
68561f28255Scgd
68661f28255Scgd /*
68761f28255Scgd * Tabs in the input line cause trouble after a pushback. tty driver
68861f28255Scgd * won't backspace over them because column positions are now
68961f28255Scgd * incorrect. This is solved by retyping over current line.
69061f28255Scgd */
69161f28255Scgd should_retype = FALSE;
69261f28255Scgd if (Strchr(inputline, '\t')) { /* tab Char in input line? */
69361f28255Scgd back_to_col_1();
69461f28255Scgd should_retype = TRUE;
69561f28255Scgd }
69661f28255Scgd if (command == LIST) /* Always retype after a LIST */
69761f28255Scgd should_retype = TRUE;
698e50ba7eaSitohy if (pushback(inputline))
699e50ba7eaSitohy should_retype = TRUE;
700e50ba7eaSitohy if (should_retype) {
701e50ba7eaSitohy if (command == RECOGNIZE)
702e50ba7eaSitohy (void) fputc('\n', cshout);
70361f28255Scgd printprompt();
704e50ba7eaSitohy }
70561f28255Scgd if (should_retype)
70661f28255Scgd retype();
70761f28255Scgd }
70861f28255Scgd setup_tty(OFF);
70937e39248Schristos return num_read;
71061f28255Scgd }
71161f28255Scgd
71261f28255Scgd static int
ignored(Char * entry)713b771e65bSwiz ignored(Char *entry)
71461f28255Scgd {
71561f28255Scgd struct varent *vp;
71676adbe2bStls Char **cp;
71761f28255Scgd
71861f28255Scgd if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
71961f28255Scgd return (FALSE);
72061f28255Scgd for (; *cp != NULL; cp++)
72161f28255Scgd if (is_suffix(entry, *cp))
72261f28255Scgd return (TRUE);
72361f28255Scgd return (FALSE);
72461f28255Scgd }
72561f28255Scgd #endif /* FILEC */
726