2 * Copyright (c) 1980, 1991, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. 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.
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
36 static char sccsid[] = "@(#)file.c 8.2 (Berkeley) 3/19/94";
38 static const char rcsid[] =
45 #include <sys/param.h>
62 * Tenex style file name recognition, .. and more.
64 * Author: Ken Greer, Sept. 1975, CMU.
65 * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981.
83 static void setup_tty __P((int));
84 static void back_to_col_1 __P((void));
85 static void pushback __P((Char *));
86 static void catn __P((Char *, Char *, int));
87 static void copyn __P((Char *, Char *, int));
88 static Char filetype __P((Char *, Char *));
89 static void print_by_column __P((Char *, Char *[], int));
90 static Char *tilde __P((Char *, Char *));
91 static void retype __P((void));
92 static void beep __P((void));
93 static void print_recognized_stuff __P((Char *));
94 static void extract_dir_and_name __P((Char *, Char *, Char *));
95 static Char *getentry __P((DIR *, int));
96 static void free_items __P((Char **));
97 static int tsearch __P((Char *, COMMAND, int));
98 static int recognize __P((Char *, Char *, int, int));
99 static int is_prefix __P((Char *, Char *));
100 static int is_suffix __P((Char *, Char *));
101 static int ignored __P((Char *));
104 * Put this here so the binary can be patched with adb to enable file
105 * completion by default. Filec controls completion, nobeep controls
106 * ringing the terminal bell on incomplete expansions.
114 struct termios tchars;
116 (void) tcgetattr(SHIN, &tchars);
119 tchars.c_cc[VEOL] = ESC;
120 if (tchars.c_lflag & ICANON)
124 tchars.c_lflag |= ICANON;
128 tchars.c_cc[VEOL] = _POSIX_VDISABLE;
132 (void) tcsetattr(SHIN, on, &tchars);
136 * Move back to beginning of current line
141 struct termios tty, tty_normal;
144 omask = sigblock(sigmask(SIGINT));
145 (void) tcgetattr(SHOUT, &tty);
147 tty.c_iflag &= ~INLCR;
148 tty.c_oflag &= ~ONLCR;
149 (void) tcsetattr(SHOUT, TCSANOW, &tty);
150 (void) write(SHOUT, "\r", 1);
151 (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
152 (void) sigsetmask(omask);
156 * Push string contents back into tty queue
163 struct termios tty, tty_normal;
167 omask = sigblock(sigmask(SIGINT));
168 (void) tcgetattr(SHOUT, &tty);
170 tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL);
171 (void) tcsetattr(SHOUT, TCSANOW, &tty);
173 for (p = string; (c = *p) != '\0'; p++)
174 (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c);
175 (void) tcsetattr(SHOUT, TCSANOW, &tty_normal);
176 (void) sigsetmask(omask);
180 * Concatenate src onto tail of des.
181 * Des is a string whose maximum length is count.
182 * Always null terminate.
185 catn(des, src, count)
189 while (--count >= 0 && *des)
192 if ((*des++ = *src++) == 0)
198 * Like strncpy but always leave room for trailing \0
199 * and always null terminate.
202 copyn(des, src, count)
207 if ((*des++ = *src++) == 0)
216 Char path[MAXPATHLEN];
219 catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char));
220 if (lstat(short2str(path), &statb) == 0) {
221 switch (statb.st_mode & S_IFMT) {
226 if (stat(short2str(path), &statb) == 0 && /* follow it out */
227 S_ISDIR(statb.st_mode))
236 if (statb.st_mode & 0111)
243 static struct winsize win;
246 * Print sorted down columns
249 print_by_column(dir, items, count)
253 int i, rows, r, c, maxwidth = 0, columns;
255 if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0)
257 for (i = 0; i < count; i++)
258 maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r;
259 maxwidth += 2; /* for the file tag and space */
260 columns = win.ws_col / maxwidth;
263 rows = (count + (columns - 1)) / columns;
264 for (r = 0; r < rows; r++) {
265 for (c = 0; c < columns; c++) {
270 (void) fprintf(cshout, "%s", vis_str(items[i]));
271 (void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout);
272 if (c < columns - 1) { /* last column? */
273 w = Strlen(items[i]) + 1;
274 for (; w < maxwidth; w++)
275 (void) fputc(' ', cshout);
279 (void) fputc('\r', cshout);
280 (void) fputc('\n', cshout);
285 * Expand file name with possible tilde usage
288 * home_directory_of_person/mumble
296 static Char person[40];
299 return (Strcpy(new, old));
301 for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++)
304 if (person[0] == '\0')
305 (void) Strcpy(new, value(STRhome));
307 pw = getpwnam(short2str(person));
310 (void) Strcpy(new, str2short(pw->pw_dir));
312 (void) Strcat(new, o);
317 * Cause pending line to be printed
324 (void) tcgetattr(SHOUT, &tty);
325 tty.c_lflag |= PENDIN;
326 (void) tcsetattr(SHOUT, TCSANOW, &tty);
332 if (adrof(STRnobeep) == 0)
333 (void) write(SHOUT, "\007", 1);
337 * Erase that silly ^[ and
338 * print the recognized part of the string
341 print_recognized_stuff(recognized_part)
342 Char *recognized_part;
344 /* An optimized erasing of that silly ^[ */
345 (void) fputc('\b', cshout);
346 (void) fputc('\b', cshout);
347 switch (Strlen(recognized_part)) {
349 case 0: /* erase two Characters: ^[ */
350 (void) fputc(' ', cshout);
351 (void) fputc(' ', cshout);
352 (void) fputc('\b', cshout);
353 (void) fputc('\b', cshout);
356 case 1: /* overstrike the ^, erase the [ */
357 (void) fprintf(cshout, "%s", vis_str(recognized_part));
358 (void) fputc(' ', cshout);
359 (void) fputc('\b', cshout);
362 default: /* overstrike both Characters ^[ */
363 (void) fprintf(cshout, "%s", vis_str(recognized_part));
366 (void) fflush(cshout);
370 * Parse full path in file into 2 parts: directory and file names
371 * Should leave final slash (/) at end of dir.
374 extract_dir_and_name(path, dir, name)
375 Char *path, *dir, *name;
379 p = Strrchr(path, '/');
381 copyn(name, path, MAXNAMLEN);
385 copyn(name, ++p, MAXNAMLEN);
386 copyn(dir, path, p - path);
391 getentry(dir_fd, looking_for_lognames)
393 int looking_for_lognames;
398 if (looking_for_lognames) {
399 if ((pw = getpwent()) == NULL)
401 return (str2short(pw->pw_name));
403 if ((dirp = readdir(dir_fd)) != NULL)
404 return (str2short(dirp->d_name));
414 for (i = 0; items[i]; i++)
415 xfree((ptr_t) items[i]);
416 xfree((ptr_t) items);
419 #define FREE_ITEMS(items) { \
422 omask = sigblock(sigmask(SIGINT));\
425 (void) sigsetmask(omask);\
429 * Perform a RECOGNIZE or LIST command on string "word".
432 tsearch(word, command, max_word_length)
437 static Char **items = NULL;
439 int numitems = 0, ignoring = TRUE, nignored = 0;
440 int name_length, looking_for_lognames;
441 Char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1];
442 Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1];
445 #define MAXITEMS 1024
450 looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL);
451 if (looking_for_lognames) {
453 copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */
457 extract_dir_and_name(word, dir, name);
458 if (tilde(tilded_dir, dir) == 0)
460 dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : ".");
465 again: /* search for matches */
466 name_length = Strlen(name);
467 for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) {
468 if (!is_prefix(name, entry))
470 /* Don't match . files on null prefix match */
471 if (name_length == 0 && entry[0] == '.' &&
472 !looking_for_lognames)
474 if (command == LIST) {
475 if (numitems >= MAXITEMS) {
476 (void) fprintf(csherr, "\nYikes!! Too many %s!!\n",
477 looking_for_lognames ?
478 "names in password file" : "files");
482 items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS);
483 items[numitems] = (Char *) xmalloc((size_t) (Strlen(entry) + 1) *
485 copyn(items[numitems], entry, MAXNAMLEN);
488 else { /* RECOGNIZE command */
489 if (ignoring && ignored(entry))
491 else if (recognize(extended_name,
492 entry, name_length, ++numitems))
496 if (ignoring && numitems == 0 && nignored > 0) {
499 if (looking_for_lognames)
506 if (looking_for_lognames)
509 (void) closedir(dir_fd);
512 if (command == RECOGNIZE) {
513 if (looking_for_lognames)
514 copyn(word, STRtilde, 1);
516 /* put back dir part */
517 copyn(word, dir, max_word_length);
518 /* add extended name */
519 catn(word, extended_name, max_word_length);
523 qsort((ptr_t) items, numitems, sizeof(items[0]),
524 (int (*) __P((const void *, const void *))) sortscmp);
525 print_by_column(looking_for_lognames ? NULL : tilded_dir,
534 * Object: extend what user typed up to an ambiguity.
536 * On first match, copy full entry (assume it'll be the only match)
537 * On subsequent matches, shorten extended_name to the first
538 * Character mismatch between extended_name and entry.
539 * If we shorten it back to the prefix length, stop searching.
542 recognize(extended_name, entry, name_length, numitems)
543 Char *extended_name, *entry;
544 int name_length, numitems;
546 if (numitems == 1) /* 1st match */
547 copyn(extended_name, entry, MAXNAMLEN);
548 else { /* 2nd & subsequent matches */
553 for (ent = entry; *x && *x == *ent++; x++, len++)
555 *x = '\0'; /* Shorten at 1st Char diff */
556 if (len == name_length) /* Ambiguous to prefix? */
557 return (-1); /* So stop now and save time */
563 * Return true if check matches initial Chars in template.
564 * This differs from PWB imatch in that if check is null
565 * it matches anything.
568 is_prefix(check, template)
569 Char *check, *template;
574 while (*check++ == *template++);
579 * Return true if the Chars in template appear at the
580 * end of check, I.e., are it's suffix.
583 is_suffix(check, template)
584 Char *check, *template;
588 for (c = check; *c++;)
590 for (t = template; *t++;)
595 if (c == check || *--t != *--c)
601 tenex(inputline, inputline_size)
605 int numitems, num_read;
606 char tinputline[BUFSIZ];
611 while ((num_read = read(SHIN, tinputline, BUFSIZ)) > 0) {
613 static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<',
614 '>', '(', ')', '|', '^', '%', '\0'};
615 Char *str_end, *word_start, last_Char, should_retype;
619 for (i = 0; i < num_read; i++)
620 inputline[i] = (unsigned char) tinputline[i];
621 last_Char = inputline[num_read - 1] & ASCII;
623 if (last_Char == '\n' || num_read == inputline_size)
625 command = (last_Char == ESC) ? RECOGNIZE : LIST;
627 (void) fputc('\n', cshout);
628 str_end = &inputline[num_read];
629 if (last_Char == ESC)
630 --str_end; /* wipeout trailing cmd Char */
633 * Find LAST occurence of a delimiter in the inputline. The word start
634 * is one Character past it.
636 for (word_start = str_end; word_start > inputline; --word_start)
637 if (Strchr(delims, word_start[-1]))
639 space_left = inputline_size - (word_start - inputline) - 1;
640 numitems = tsearch(word_start, command, space_left);
642 if (command == RECOGNIZE) {
643 /* print from str_end on */
644 print_recognized_stuff(str_end);
645 if (numitems != 1) /* Beep = No match/ambiguous */
650 * Tabs in the input line cause trouble after a pushback. tty driver
651 * won't backspace over them because column positions are now
652 * incorrect. This is solved by retyping over current line.
654 should_retype = FALSE;
655 if (Strchr(inputline, '\t')) { /* tab Char in input line? */
657 should_retype = TRUE;
659 if (command == LIST) /* Always retype after a LIST */
660 should_retype = TRUE;
678 if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL)
680 for (; *cp != NULL; cp++)
681 if (is_suffix(entry, *cp))