1*0134f510Santon /* $OpenBSD: file.c,v 1.24 2017/06/17 18:14:47 anton Exp $ */ 226b220adSmillert /* $NetBSD: file.c,v 1.11 1996/11/08 19:34:37 christos Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /*- 5df930be7Sderaadt * Copyright (c) 1980, 1991, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 9df930be7Sderaadt * modification, are permitted provided that the following conditions 10df930be7Sderaadt * are met: 11df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 12df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 13df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 14df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 15df930be7Sderaadt * documentation and/or other materials provided with the distribution. 1629295d1cSmillert * 3. Neither the name of the University nor the names of its contributors 17df930be7Sderaadt * may be used to endorse or promote products derived from this software 18df930be7Sderaadt * without specific prior written permission. 19df930be7Sderaadt * 20df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30df930be7Sderaadt * SUCH DAMAGE. 31df930be7Sderaadt */ 32df930be7Sderaadt 33df930be7Sderaadt 34b9fc9a72Sderaadt #include <sys/types.h> 35df930be7Sderaadt #include <sys/ioctl.h> 36df930be7Sderaadt #include <sys/stat.h> 37df930be7Sderaadt #include <termios.h> 38df930be7Sderaadt #include <dirent.h> 39df930be7Sderaadt #include <pwd.h> 40df930be7Sderaadt #include <stdlib.h> 41df930be7Sderaadt #include <unistd.h> 42b9fc9a72Sderaadt #include <limits.h> 43df930be7Sderaadt #include <stdarg.h> 44df930be7Sderaadt 45df930be7Sderaadt #include "csh.h" 46df930be7Sderaadt #include "extern.h" 47df930be7Sderaadt 48df930be7Sderaadt /* 49df930be7Sderaadt * Tenex style file name recognition, .. and more. 50df930be7Sderaadt * History: 51df930be7Sderaadt * Author: Ken Greer, Sept. 1975, CMU. 52df930be7Sderaadt * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. 53df930be7Sderaadt */ 54df930be7Sderaadt 55df930be7Sderaadt #define ON 1 56df930be7Sderaadt #define OFF 0 57df930be7Sderaadt #ifndef TRUE 58df930be7Sderaadt #define TRUE 1 59df930be7Sderaadt #endif 60df930be7Sderaadt #ifndef FALSE 61df930be7Sderaadt #define FALSE 0 62df930be7Sderaadt #endif 63df930be7Sderaadt 64df930be7Sderaadt #define ESC '\033' 65df930be7Sderaadt 66df930be7Sderaadt typedef enum { 67df930be7Sderaadt LIST, RECOGNIZE 68df930be7Sderaadt } COMMAND; 69df930be7Sderaadt 70c72b5b24Smillert static void setup_tty(int); 71c72b5b24Smillert static void back_to_col_1(void); 72c72b5b24Smillert static void pushback(Char *); 73c72b5b24Smillert static void catn(Char *, Char *, int); 74c72b5b24Smillert static void copyn(Char *, Char *, int); 75c72b5b24Smillert static Char filetype(Char *, Char *); 76c72b5b24Smillert static void print_by_column(Char *, Char *[], int); 77c72b5b24Smillert static Char *tilde(Char *, Char *); 78c72b5b24Smillert static void retype(void); 79c72b5b24Smillert static void beep(void); 80c72b5b24Smillert static void print_recognized_stuff(Char *); 81c72b5b24Smillert static void extract_dir_and_name(Char *, Char *, Char *); 82c72b5b24Smillert static Char *getentry(DIR *, int); 83b1f6b197Smillert static void free_items(Char **, int); 84c72b5b24Smillert static int tsearch(Char *, COMMAND, int); 85c72b5b24Smillert static int recognize(Char *, Char *, int, int); 86c72b5b24Smillert static int is_prefix(Char *, Char *); 87c72b5b24Smillert static int is_suffix(Char *, Char *); 88c72b5b24Smillert static int ignored(Char *); 89df930be7Sderaadt 90df930be7Sderaadt /* 91df930be7Sderaadt * Put this here so the binary can be patched with adb to enable file 92df930be7Sderaadt * completion by default. Filec controls completion, nobeep controls 93df930be7Sderaadt * ringing the terminal bell on incomplete expansions. 94df930be7Sderaadt */ 95df930be7Sderaadt bool filec = 0; 96df930be7Sderaadt 97df930be7Sderaadt static void 98e757c91eSderaadt setup_tty(int on) 99df930be7Sderaadt { 100df930be7Sderaadt struct termios tchars; 101df930be7Sderaadt 102df930be7Sderaadt (void) tcgetattr(SHIN, &tchars); 103df930be7Sderaadt 104df930be7Sderaadt if (on) { 105df930be7Sderaadt tchars.c_cc[VEOL] = ESC; 106df930be7Sderaadt if (tchars.c_lflag & ICANON) 107df930be7Sderaadt on = TCSADRAIN; 108df930be7Sderaadt else { 109df930be7Sderaadt tchars.c_lflag |= ICANON; 110df930be7Sderaadt on = TCSAFLUSH; 111df930be7Sderaadt } 112df930be7Sderaadt } 113df930be7Sderaadt else { 114df930be7Sderaadt tchars.c_cc[VEOL] = _POSIX_VDISABLE; 115df930be7Sderaadt on = TCSADRAIN; 116df930be7Sderaadt } 117df930be7Sderaadt 118df930be7Sderaadt (void) tcsetattr(SHIN, on, &tchars); 119df930be7Sderaadt } 120df930be7Sderaadt 121df930be7Sderaadt /* 122df930be7Sderaadt * Move back to beginning of current line 123df930be7Sderaadt */ 124df930be7Sderaadt static void 125e757c91eSderaadt back_to_col_1(void) 126df930be7Sderaadt { 127df930be7Sderaadt struct termios tty, tty_normal; 128df930be7Sderaadt sigset_t sigset, osigset; 129df930be7Sderaadt 130df930be7Sderaadt sigemptyset(&sigset); 131df930be7Sderaadt sigaddset(&sigset, SIGINT); 132df930be7Sderaadt sigprocmask(SIG_BLOCK, &sigset, &osigset); 133df930be7Sderaadt (void) tcgetattr(SHOUT, &tty); 134df930be7Sderaadt tty_normal = tty; 135df930be7Sderaadt tty.c_iflag &= ~INLCR; 136df930be7Sderaadt tty.c_oflag &= ~ONLCR; 13726b220adSmillert (void) tcsetattr(SHOUT, TCSADRAIN, &tty); 138df930be7Sderaadt (void) write(SHOUT, "\r", 1); 13926b220adSmillert (void) tcsetattr(SHOUT, TCSADRAIN, &tty_normal); 140df930be7Sderaadt sigprocmask(SIG_SETMASK, &osigset, NULL); 141df930be7Sderaadt } 142df930be7Sderaadt 143df930be7Sderaadt /* 144df930be7Sderaadt * Push string contents back into tty queue 145df930be7Sderaadt */ 146df930be7Sderaadt static void 147e757c91eSderaadt pushback(Char *string) 148df930be7Sderaadt { 149e757c91eSderaadt Char *p; 150df930be7Sderaadt struct termios tty, tty_normal; 151df930be7Sderaadt sigset_t sigset, osigset; 152df930be7Sderaadt char c; 153df930be7Sderaadt 154df930be7Sderaadt sigemptyset(&sigset); 155df930be7Sderaadt sigaddset(&sigset, SIGINT); 156df930be7Sderaadt sigprocmask(SIG_BLOCK, &sigset, &osigset); 157df930be7Sderaadt (void) tcgetattr(SHOUT, &tty); 158df930be7Sderaadt tty_normal = tty; 159df930be7Sderaadt tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL); 16026b220adSmillert (void) tcsetattr(SHOUT, TCSADRAIN, &tty); 161df930be7Sderaadt 162df930be7Sderaadt for (p = string; (c = *p) != '\0'; p++) 163df930be7Sderaadt (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 16426b220adSmillert (void) tcsetattr(SHOUT, TCSADRAIN, &tty_normal); 165df930be7Sderaadt sigprocmask(SIG_SETMASK, &osigset, NULL); 166df930be7Sderaadt } 167df930be7Sderaadt 168df930be7Sderaadt /* 169df930be7Sderaadt * Concatenate src onto tail of des. 170df930be7Sderaadt * Des is a string whose maximum length is count. 171df930be7Sderaadt * Always null terminate. 172df930be7Sderaadt */ 173df930be7Sderaadt static void 174e757c91eSderaadt catn(Char *des, Char *src, int count) 175df930be7Sderaadt { 176df930be7Sderaadt while (--count >= 0 && *des) 177df930be7Sderaadt des++; 178df930be7Sderaadt while (--count >= 0) 179df930be7Sderaadt if ((*des++ = *src++) == 0) 180df930be7Sderaadt return; 181df930be7Sderaadt *des = '\0'; 182df930be7Sderaadt } 183df930be7Sderaadt 184df930be7Sderaadt /* 185aead66f0Sderaadt * Places Char's like strlcpy, but no special return value. 186df930be7Sderaadt */ 187df930be7Sderaadt static void 188e757c91eSderaadt copyn(Char *des, Char *src, int count) 189df930be7Sderaadt { 190df930be7Sderaadt while (--count >= 0) 191df930be7Sderaadt if ((*des++ = *src++) == 0) 192df930be7Sderaadt return; 193df930be7Sderaadt *des = '\0'; 194df930be7Sderaadt } 195df930be7Sderaadt 196df930be7Sderaadt static Char 197e757c91eSderaadt filetype(Char *dir, Char *file) 198df930be7Sderaadt { 199b9fc9a72Sderaadt Char path[PATH_MAX]; 200df930be7Sderaadt struct stat statb; 201df930be7Sderaadt 2026a01f4acSderaadt Strlcpy(path, dir, sizeof path/sizeof(Char)); 2036a01f4acSderaadt catn(path, file, sizeof(path) / sizeof(Char)); 204df930be7Sderaadt if (lstat(short2str(path), &statb) == 0) { 205df930be7Sderaadt switch (statb.st_mode & S_IFMT) { 206df930be7Sderaadt case S_IFDIR: 207df930be7Sderaadt return ('/'); 208df930be7Sderaadt 209df930be7Sderaadt case S_IFLNK: 210df930be7Sderaadt if (stat(short2str(path), &statb) == 0 && /* follow it out */ 211df930be7Sderaadt S_ISDIR(statb.st_mode)) 212df930be7Sderaadt return ('>'); 213df930be7Sderaadt else 214df930be7Sderaadt return ('@'); 215df930be7Sderaadt 216df930be7Sderaadt case S_IFSOCK: 217df930be7Sderaadt return ('='); 218df930be7Sderaadt 219df930be7Sderaadt default: 220df930be7Sderaadt if (statb.st_mode & 0111) 221df930be7Sderaadt return ('*'); 222df930be7Sderaadt } 223df930be7Sderaadt } 224df930be7Sderaadt return (' '); 225df930be7Sderaadt } 226df930be7Sderaadt 227df930be7Sderaadt /* 228df930be7Sderaadt * Print sorted down columns 229df930be7Sderaadt */ 230df930be7Sderaadt static void 231e757c91eSderaadt print_by_column(Char *dir, Char *items[], int count) 232df930be7Sderaadt { 233*0134f510Santon struct winsize win; 234e757c91eSderaadt int i, rows, r, c, maxwidth = 0, columns; 235df930be7Sderaadt 236df930be7Sderaadt if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 237df930be7Sderaadt win.ws_col = 80; 238df930be7Sderaadt for (i = 0; i < count; i++) 239df930be7Sderaadt maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 240df930be7Sderaadt maxwidth += 2; /* for the file tag and space */ 241df930be7Sderaadt columns = win.ws_col / maxwidth; 242df930be7Sderaadt if (columns == 0) 243df930be7Sderaadt columns = 1; 244df930be7Sderaadt rows = (count + (columns - 1)) / columns; 245df930be7Sderaadt for (r = 0; r < rows; r++) { 246df930be7Sderaadt for (c = 0; c < columns; c++) { 247df930be7Sderaadt i = c * rows + r; 248df930be7Sderaadt if (i < count) { 249e757c91eSderaadt int w; 250df930be7Sderaadt 251df930be7Sderaadt (void) fprintf(cshout, "%s", vis_str(items[i])); 252df930be7Sderaadt (void) fputc(dir ? filetype(dir, items[i]) : ' ', cshout); 253df930be7Sderaadt if (c < columns - 1) { /* last column? */ 254df930be7Sderaadt w = Strlen(items[i]) + 1; 255df930be7Sderaadt for (; w < maxwidth; w++) 256df930be7Sderaadt (void) fputc(' ', cshout); 257df930be7Sderaadt } 258df930be7Sderaadt } 259df930be7Sderaadt } 260df930be7Sderaadt (void) fputc('\r', cshout); 261df930be7Sderaadt (void) fputc('\n', cshout); 262df930be7Sderaadt } 263df930be7Sderaadt } 264df930be7Sderaadt 265df930be7Sderaadt /* 266df930be7Sderaadt * Expand file name with possible tilde usage 267df930be7Sderaadt * ~person/mumble 268df930be7Sderaadt * expands to 269df930be7Sderaadt * home_directory_of_person/mumble 270df930be7Sderaadt */ 271df930be7Sderaadt static Char * 272e757c91eSderaadt tilde(Char *new, Char *old) 273df930be7Sderaadt { 274e757c91eSderaadt Char *o, *p; 275e757c91eSderaadt struct passwd *pw; 276df930be7Sderaadt static Char person[40]; 277df930be7Sderaadt 2786a01f4acSderaadt if (old[0] != '~') { 279b9fc9a72Sderaadt Strlcpy(new, old, PATH_MAX); 2806a01f4acSderaadt return new; 2816a01f4acSderaadt } 282df930be7Sderaadt 283df930be7Sderaadt for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++) 284df930be7Sderaadt continue; 285df930be7Sderaadt *p = '\0'; 286df930be7Sderaadt if (person[0] == '\0') 287b9fc9a72Sderaadt (void) Strlcpy(new, value(STRhome), PATH_MAX); 288df930be7Sderaadt else { 289df930be7Sderaadt pw = getpwnam(short2str(person)); 290df930be7Sderaadt if (pw == NULL) 291df930be7Sderaadt return (NULL); 292b9fc9a72Sderaadt (void) Strlcpy(new, str2short(pw->pw_dir), PATH_MAX); 293df930be7Sderaadt } 294b9fc9a72Sderaadt (void) Strlcat(new, o, PATH_MAX); 295df930be7Sderaadt return (new); 296df930be7Sderaadt } 297df930be7Sderaadt 298df930be7Sderaadt /* 299df930be7Sderaadt * Cause pending line to be printed 300df930be7Sderaadt */ 301df930be7Sderaadt static void 302e757c91eSderaadt retype(void) 303df930be7Sderaadt { 304df930be7Sderaadt struct termios tty; 305df930be7Sderaadt 306df930be7Sderaadt (void) tcgetattr(SHOUT, &tty); 307df930be7Sderaadt tty.c_lflag |= PENDIN; 30826b220adSmillert (void) tcsetattr(SHOUT, TCSADRAIN, &tty); 309df930be7Sderaadt } 310df930be7Sderaadt 311df930be7Sderaadt static void 312e757c91eSderaadt beep(void) 313df930be7Sderaadt { 314df930be7Sderaadt if (adrof(STRnobeep) == 0) 315df930be7Sderaadt (void) write(SHOUT, "\007", 1); 316df930be7Sderaadt } 317df930be7Sderaadt 318df930be7Sderaadt /* 319df930be7Sderaadt * Erase that silly ^[ and 320df930be7Sderaadt * print the recognized part of the string 321df930be7Sderaadt */ 322df930be7Sderaadt static void 323e757c91eSderaadt print_recognized_stuff(Char *recognized_part) 324df930be7Sderaadt { 325df930be7Sderaadt /* An optimized erasing of that silly ^[ */ 326df930be7Sderaadt (void) fputc('\b', cshout); 327df930be7Sderaadt (void) fputc('\b', cshout); 328df930be7Sderaadt switch (Strlen(recognized_part)) { 329df930be7Sderaadt 330df930be7Sderaadt case 0: /* erase two Characters: ^[ */ 331df930be7Sderaadt (void) fputc(' ', cshout); 332df930be7Sderaadt (void) fputc(' ', cshout); 333df930be7Sderaadt (void) fputc('\b', cshout); 334df930be7Sderaadt (void) fputc('\b', cshout); 335df930be7Sderaadt break; 336df930be7Sderaadt 337df930be7Sderaadt case 1: /* overstrike the ^, erase the [ */ 338df930be7Sderaadt (void) fprintf(cshout, "%s", vis_str(recognized_part)); 339df930be7Sderaadt (void) fputc(' ', cshout); 340df930be7Sderaadt (void) fputc('\b', cshout); 341df930be7Sderaadt break; 342df930be7Sderaadt 343df930be7Sderaadt default: /* overstrike both Characters ^[ */ 344df930be7Sderaadt (void) fprintf(cshout, "%s", vis_str(recognized_part)); 345df930be7Sderaadt break; 346df930be7Sderaadt } 347df930be7Sderaadt (void) fflush(cshout); 348df930be7Sderaadt } 349df930be7Sderaadt 350df930be7Sderaadt /* 351df930be7Sderaadt * Parse full path in file into 2 parts: directory and file names 352df930be7Sderaadt * Should leave final slash (/) at end of dir. 353df930be7Sderaadt */ 354df930be7Sderaadt static void 355e757c91eSderaadt extract_dir_and_name(Char *path, Char *dir, Char *name) 356df930be7Sderaadt { 357e757c91eSderaadt Char *p; 358df930be7Sderaadt 359df930be7Sderaadt p = Strrchr(path, '/'); 360df930be7Sderaadt if (p == NULL) { 361df930be7Sderaadt copyn(name, path, MAXNAMLEN); 362df930be7Sderaadt dir[0] = '\0'; 363df930be7Sderaadt } 364df930be7Sderaadt else { 365df930be7Sderaadt copyn(name, ++p, MAXNAMLEN); 366df930be7Sderaadt copyn(dir, path, p - path); 367df930be7Sderaadt } 368df930be7Sderaadt } 369df930be7Sderaadt 370df930be7Sderaadt static Char * 371e757c91eSderaadt getentry(DIR *dir_fd, int looking_for_lognames) 372df930be7Sderaadt { 373e757c91eSderaadt struct passwd *pw; 374e757c91eSderaadt struct dirent *dirp; 375df930be7Sderaadt 376df930be7Sderaadt if (looking_for_lognames) { 377df930be7Sderaadt if ((pw = getpwent()) == NULL) 378df930be7Sderaadt return (NULL); 379df930be7Sderaadt return (str2short(pw->pw_name)); 380df930be7Sderaadt } 381df930be7Sderaadt if ((dirp = readdir(dir_fd)) != NULL) 382df930be7Sderaadt return (str2short(dirp->d_name)); 383df930be7Sderaadt return (NULL); 384df930be7Sderaadt } 385df930be7Sderaadt 386df930be7Sderaadt static void 387e757c91eSderaadt free_items(Char **items, int numitems) 388df930be7Sderaadt { 389b1f6b197Smillert int i; 390df930be7Sderaadt 391b1f6b197Smillert for (i = 0; i < numitems; i++) 392acdb3202Smestre free(items[i]); 393acdb3202Smestre free(items); 394df930be7Sderaadt } 395df930be7Sderaadt 396df930be7Sderaadt #define FREE_ITEMS(items) { \ 397df930be7Sderaadt sigset_t sigset, osigset;\ 398df930be7Sderaadt \ 399df930be7Sderaadt sigemptyset(&sigset);\ 400df930be7Sderaadt sigaddset(&sigset, SIGINT);\ 401df930be7Sderaadt sigprocmask(SIG_BLOCK, &sigset, &osigset);\ 402b1f6b197Smillert free_items(items, numitems);\ 403df930be7Sderaadt sigprocmask(SIG_SETMASK, &osigset, NULL);\ 404df930be7Sderaadt } 405df930be7Sderaadt 406df930be7Sderaadt /* 407df930be7Sderaadt * Perform a RECOGNIZE or LIST command on string "word". 408df930be7Sderaadt */ 409df930be7Sderaadt static int 410e757c91eSderaadt tsearch(Char *word, COMMAND command, int max_word_length) 411df930be7Sderaadt { 412e757c91eSderaadt DIR *dir_fd; 413e757c91eSderaadt int numitems = 0, ignoring = TRUE, nignored = 0; 414e757c91eSderaadt int name_length, looking_for_lognames; 415b9fc9a72Sderaadt Char tilded_dir[PATH_MAX], dir[PATH_MAX]; 416df930be7Sderaadt Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; 417df930be7Sderaadt Char *entry; 4189a5c8861Smillert Char **items = NULL; 4199a5c8861Smillert size_t maxitems = 0; 420df930be7Sderaadt 421df930be7Sderaadt looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 422df930be7Sderaadt if (looking_for_lognames) { 423df930be7Sderaadt (void) setpwent(); 424df930be7Sderaadt copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ 425df930be7Sderaadt dir_fd = NULL; 426df930be7Sderaadt } 427df930be7Sderaadt else { 428df930be7Sderaadt extract_dir_and_name(word, dir, name); 429df930be7Sderaadt if (tilde(tilded_dir, dir) == 0) 430df930be7Sderaadt return (0); 431df930be7Sderaadt dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 432df930be7Sderaadt if (dir_fd == NULL) 433df930be7Sderaadt return (0); 434df930be7Sderaadt } 435df930be7Sderaadt 436df930be7Sderaadt again: /* search for matches */ 437df930be7Sderaadt name_length = Strlen(name); 438df930be7Sderaadt for (numitems = 0; (entry = getentry(dir_fd, looking_for_lognames)) != NULL;) { 439df930be7Sderaadt if (!is_prefix(name, entry)) 440df930be7Sderaadt continue; 441df930be7Sderaadt /* Don't match . files on null prefix match */ 442df930be7Sderaadt if (name_length == 0 && entry[0] == '.' && 443df930be7Sderaadt !looking_for_lognames) 444df930be7Sderaadt continue; 445df930be7Sderaadt if (command == LIST) { 4469a5c8861Smillert if (numitems >= maxitems) { 4479a5c8861Smillert maxitems += 1024; 448a8627d2cSderaadt items = xreallocarray(items, maxitems, sizeof(*items)); 4499a5c8861Smillert } 450a8627d2cSderaadt items[numitems] = xreallocarray(NULL, (Strlen(entry) + 1), sizeof(Char)); 451df930be7Sderaadt copyn(items[numitems], entry, MAXNAMLEN); 452df930be7Sderaadt numitems++; 453df930be7Sderaadt } 454df930be7Sderaadt else { /* RECOGNIZE command */ 455df930be7Sderaadt if (ignoring && ignored(entry)) 456df930be7Sderaadt nignored++; 457df930be7Sderaadt else if (recognize(extended_name, 458df930be7Sderaadt entry, name_length, ++numitems)) 459df930be7Sderaadt break; 460df930be7Sderaadt } 461df930be7Sderaadt } 462df930be7Sderaadt if (ignoring && numitems == 0 && nignored > 0) { 463df930be7Sderaadt ignoring = FALSE; 464df930be7Sderaadt nignored = 0; 465df930be7Sderaadt if (looking_for_lognames) 466df930be7Sderaadt (void) setpwent(); 467df930be7Sderaadt else 468df930be7Sderaadt rewinddir(dir_fd); 469df930be7Sderaadt goto again; 470df930be7Sderaadt } 471df930be7Sderaadt 472df930be7Sderaadt if (looking_for_lognames) 473df930be7Sderaadt (void) endpwent(); 474df930be7Sderaadt else 475df930be7Sderaadt (void) closedir(dir_fd); 476df930be7Sderaadt if (numitems == 0) 477df930be7Sderaadt return (0); 478df930be7Sderaadt if (command == RECOGNIZE) { 479df930be7Sderaadt if (looking_for_lognames) 480df930be7Sderaadt copyn(word, STRtilde, 1); 481df930be7Sderaadt else 482df930be7Sderaadt /* put back dir part */ 483df930be7Sderaadt copyn(word, dir, max_word_length); 484df930be7Sderaadt /* add extended name */ 485df930be7Sderaadt catn(word, extended_name, max_word_length); 486df930be7Sderaadt return (numitems); 487df930be7Sderaadt } 488df930be7Sderaadt else { /* LIST */ 489f4d574c6Stedu qsort(items, numitems, sizeof(*items), 490c72b5b24Smillert (int (*)(const void *, const void *)) sortscmp); 491df930be7Sderaadt print_by_column(looking_for_lognames ? NULL : tilded_dir, 492df930be7Sderaadt items, numitems); 493df930be7Sderaadt if (items != NULL) 494df930be7Sderaadt FREE_ITEMS(items); 495df930be7Sderaadt } 496df930be7Sderaadt return (0); 497df930be7Sderaadt } 498df930be7Sderaadt 499df930be7Sderaadt /* 500df930be7Sderaadt * Object: extend what user typed up to an ambiguity. 501df930be7Sderaadt * Algorithm: 502df930be7Sderaadt * On first match, copy full entry (assume it'll be the only match) 503df930be7Sderaadt * On subsequent matches, shorten extended_name to the first 504df930be7Sderaadt * Character mismatch between extended_name and entry. 505df930be7Sderaadt * If we shorten it back to the prefix length, stop searching. 506df930be7Sderaadt */ 507df930be7Sderaadt static int 508e757c91eSderaadt recognize(Char *extended_name, Char *entry, int name_length, int numitems) 509df930be7Sderaadt { 510df930be7Sderaadt if (numitems == 1) /* 1st match */ 511df930be7Sderaadt copyn(extended_name, entry, MAXNAMLEN); 512df930be7Sderaadt else { /* 2nd & subsequent matches */ 513e757c91eSderaadt Char *x, *ent; 514e757c91eSderaadt int len = 0; 515df930be7Sderaadt 516df930be7Sderaadt x = extended_name; 517df930be7Sderaadt for (ent = entry; *x && *x == *ent++; x++, len++) 518df930be7Sderaadt continue; 519df930be7Sderaadt *x = '\0'; /* Shorten at 1st Char diff */ 520df930be7Sderaadt if (len == name_length) /* Ambiguous to prefix? */ 521df930be7Sderaadt return (-1); /* So stop now and save time */ 522df930be7Sderaadt } 523df930be7Sderaadt return (0); 524df930be7Sderaadt } 525df930be7Sderaadt 526df930be7Sderaadt /* 527df930be7Sderaadt * Return true if check matches initial Chars in template. 528df930be7Sderaadt * This differs from PWB imatch in that if check is null 529df930be7Sderaadt * it matches anything. 530df930be7Sderaadt */ 531df930be7Sderaadt static int 532e757c91eSderaadt is_prefix(Char *check, Char *template) 533df930be7Sderaadt { 534df930be7Sderaadt do 535df930be7Sderaadt if (*check == 0) 536df930be7Sderaadt return (TRUE); 537df930be7Sderaadt while (*check++ == *template++); 538df930be7Sderaadt return (FALSE); 539df930be7Sderaadt } 540df930be7Sderaadt 541df930be7Sderaadt /* 542df930be7Sderaadt * Return true if the Chars in template appear at the 543df930be7Sderaadt * end of check, I.e., are it's suffix. 544df930be7Sderaadt */ 545df930be7Sderaadt static int 546e757c91eSderaadt is_suffix(Char *check, Char *template) 547df930be7Sderaadt { 548e757c91eSderaadt Char *c, *t; 549df930be7Sderaadt 550df930be7Sderaadt for (c = check; *c++;) 551df930be7Sderaadt continue; 552df930be7Sderaadt for (t = template; *t++;) 553df930be7Sderaadt continue; 554df930be7Sderaadt for (;;) { 555df930be7Sderaadt if (t == template) 556df930be7Sderaadt return 1; 557df930be7Sderaadt if (c == check || *--t != *--c) 558df930be7Sderaadt return 0; 559df930be7Sderaadt } 560df930be7Sderaadt } 561df930be7Sderaadt 562df930be7Sderaadt int 563e757c91eSderaadt tenex(Char *inputline, int inputline_size) 564df930be7Sderaadt { 565e757c91eSderaadt int numitems, num_read; 566df930be7Sderaadt char tinputline[BUFSIZ]; 567df930be7Sderaadt 568df930be7Sderaadt setup_tty(ON); 569df930be7Sderaadt 570df930be7Sderaadt while ((num_read = read(SHIN, tinputline, BUFSIZ)) > 0) { 571df930be7Sderaadt int i; 572df930be7Sderaadt static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', 573df930be7Sderaadt '>', '(', ')', '|', '^', '%', '\0'}; 574e757c91eSderaadt Char *str_end, *word_start, last_Char, should_retype; 575e757c91eSderaadt int space_left; 576df930be7Sderaadt COMMAND command; 577df930be7Sderaadt 578df930be7Sderaadt for (i = 0; i < num_read; i++) 579df930be7Sderaadt inputline[i] = (unsigned char) tinputline[i]; 580df930be7Sderaadt last_Char = inputline[num_read - 1] & ASCII; 581df930be7Sderaadt 582df930be7Sderaadt if (last_Char == '\n' || num_read == inputline_size) 583df930be7Sderaadt break; 584df930be7Sderaadt command = (last_Char == ESC) ? RECOGNIZE : LIST; 585df930be7Sderaadt if (command == LIST) 586df930be7Sderaadt (void) fputc('\n', cshout); 587df930be7Sderaadt str_end = &inputline[num_read]; 588df930be7Sderaadt if (last_Char == ESC) 589df930be7Sderaadt --str_end; /* wipeout trailing cmd Char */ 590df930be7Sderaadt *str_end = '\0'; 591df930be7Sderaadt /* 5929ab19ecaStodd * Find LAST occurrence of a delimiter in the inputline. The word start 593df930be7Sderaadt * is one Character past it. 594df930be7Sderaadt */ 595df930be7Sderaadt for (word_start = str_end; word_start > inputline; --word_start) 596df930be7Sderaadt if (Strchr(delims, word_start[-1])) 597df930be7Sderaadt break; 598df930be7Sderaadt space_left = inputline_size - (word_start - inputline) - 1; 599df930be7Sderaadt numitems = tsearch(word_start, command, space_left); 600df930be7Sderaadt 601df930be7Sderaadt if (command == RECOGNIZE) { 602df930be7Sderaadt /* print from str_end on */ 603df930be7Sderaadt print_recognized_stuff(str_end); 604df930be7Sderaadt if (numitems != 1) /* Beep = No match/ambiguous */ 605df930be7Sderaadt beep(); 606df930be7Sderaadt } 607df930be7Sderaadt 608df930be7Sderaadt /* 609df930be7Sderaadt * Tabs in the input line cause trouble after a pushback. tty driver 610df930be7Sderaadt * won't backspace over them because column positions are now 611df930be7Sderaadt * incorrect. This is solved by retyping over current line. 612df930be7Sderaadt */ 613df930be7Sderaadt should_retype = FALSE; 614df930be7Sderaadt if (Strchr(inputline, '\t')) { /* tab Char in input line? */ 615df930be7Sderaadt back_to_col_1(); 616df930be7Sderaadt should_retype = TRUE; 617df930be7Sderaadt } 618df930be7Sderaadt if (command == LIST) /* Always retype after a LIST */ 619df930be7Sderaadt should_retype = TRUE; 620df930be7Sderaadt if (should_retype) 621df930be7Sderaadt printprompt(); 622df930be7Sderaadt pushback(inputline); 623df930be7Sderaadt if (should_retype) 624df930be7Sderaadt retype(); 625df930be7Sderaadt } 626df930be7Sderaadt setup_tty(OFF); 627df930be7Sderaadt return (num_read); 628df930be7Sderaadt } 629df930be7Sderaadt 630df930be7Sderaadt static int 631e757c91eSderaadt ignored(Char *entry) 632df930be7Sderaadt { 633df930be7Sderaadt struct varent *vp; 634e757c91eSderaadt Char **cp; 635df930be7Sderaadt 636df930be7Sderaadt if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 637df930be7Sderaadt return (FALSE); 638df930be7Sderaadt for (; *cp != NULL; cp++) 639df930be7Sderaadt if (is_suffix(entry, *cp)) 640df930be7Sderaadt return (TRUE); 641df930be7Sderaadt return (FALSE); 642df930be7Sderaadt } 643