159243Sobrien/* 259243Sobrien * sh.file.c: File completion for csh. This file is not used in tcsh. 359243Sobrien */ 459243Sobrien/*- 559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 659243Sobrien * All rights reserved. 759243Sobrien * 859243Sobrien * Redistribution and use in source and binary forms, with or without 959243Sobrien * modification, are permitted provided that the following conditions 1059243Sobrien * are met: 1159243Sobrien * 1. Redistributions of source code must retain the above copyright 1259243Sobrien * notice, this list of conditions and the following disclaimer. 1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1459243Sobrien * notice, this list of conditions and the following disclaimer in the 1559243Sobrien * documentation and/or other materials provided with the distribution. 16100616Smp * 3. Neither the name of the University nor the names of its contributors 1759243Sobrien * may be used to endorse or promote products derived from this software 1859243Sobrien * without specific prior written permission. 1959243Sobrien * 2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2359243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3059243Sobrien * SUCH DAMAGE. 3159243Sobrien */ 3259243Sobrien#include "sh.h" 33100616Smp#include "ed.h" 3459243Sobrien 35100616Smp#if defined(FILEC) && defined(TIOCSTI) 3659243Sobrien 3759243Sobrien/* 3859243Sobrien * Tenex style file name recognition, .. and more. 3959243Sobrien * History: 4059243Sobrien * Author: Ken Greer, Sept. 1975, CMU. 4159243Sobrien * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. 4259243Sobrien */ 4359243Sobrien 4459243Sobrien#define ON 1 4559243Sobrien#define OFF 0 4659243Sobrien#ifndef TRUE 4759243Sobrien#define TRUE 1 4859243Sobrien#endif 4959243Sobrien#ifndef FALSE 5059243Sobrien#define FALSE 0 5159243Sobrien#endif 5259243Sobrien 5359243Sobrien#define ESC CTL_ESC('\033') 5459243Sobrien 5559243Sobrientypedef enum { 5659243Sobrien LIST, RECOGNIZE 5759243Sobrien} COMMAND; 5859243Sobrien 59167465Smpstatic void setup_tty (int); 60167465Smpstatic void back_to_col_1 (void); 61167465Smpstatic void pushback (const Char *); 62167465Smpstatic int filetype (const Char *, const Char *); 63167465Smpstatic void print_by_column (const Char *, Char *[], size_t); 64167465Smpstatic Char *tilde (const Char *); 65167465Smpstatic void retype (void); 66167465Smpstatic void beep (void); 67167465Smpstatic void print_recognized_stuff (const Char *); 68167465Smpstatic void extract_dir_and_name (const Char *, Char **, const Char **); 69167465Smpstatic Char *getitem (DIR *, int); 70167465Smpstatic size_t tsearch (Char *, COMMAND, size_t); 71167465Smpstatic int compare (const void *, const void *); 72167465Smpstatic int recognize (Char **, Char *, size_t, size_t); 73167465Smpstatic int is_prefix (const Char *, const Char *); 74167465Smpstatic int is_suffix (const Char *, const Char *); 75167465Smpstatic int ignored (const Char *); 7659243Sobrien 7759243Sobrien 7859243Sobrien/* 7959243Sobrien * Put this here so the binary can be patched with adb to enable file 8059243Sobrien * completion by default. Filec controls completion, nobeep controls 8159243Sobrien * ringing the terminal bell on incomplete expansions. 8259243Sobrien */ 83145479Smpint filec = 0; 8459243Sobrien 8559243Sobrienstatic void 86167465Smpsetup_tty(int on) 8759243Sobrien{ 8859243Sobrien#ifdef TERMIO 8959243Sobrien# ifdef POSIX 9059243Sobrien struct termios tchars; 9159243Sobrien# else 9259243Sobrien struct termio tchars; 9359243Sobrien# endif /* POSIX */ 9459243Sobrien 9559243Sobrien# ifdef POSIX 9659243Sobrien (void) tcgetattr(SHIN, &tchars); 9759243Sobrien# else 9859243Sobrien (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars); 9959243Sobrien# endif /* POSIX */ 10059243Sobrien if (on) { 10159243Sobrien tchars.c_cc[VEOL] = ESC; 10259243Sobrien if (tchars.c_lflag & ICANON) 10359243Sobrien# ifdef POSIX 10459243Sobrien on = TCSADRAIN; 10559243Sobrien# else 10659243Sobrien on = TCSETA; 10759243Sobrien# endif /* POSIX */ 10859243Sobrien else { 10959243Sobrien# ifdef POSIX 11059243Sobrien on = TCSAFLUSH; 11159243Sobrien# else 11259243Sobrien on = TCSETAF; 11359243Sobrien# endif /* POSIX */ 11459243Sobrien tchars.c_lflag |= ICANON; 11559243Sobrien 11659243Sobrien } 11759243Sobrien } 11859243Sobrien else { 11959243Sobrien tchars.c_cc[VEOL] = _POSIX_VDISABLE; 12059243Sobrien# ifdef POSIX 12159243Sobrien on = TCSADRAIN; 12259243Sobrien# else 12359243Sobrien on = TCSETA; 12459243Sobrien# endif /* POSIX */ 12559243Sobrien } 12659243Sobrien# ifdef POSIX 127167465Smp (void) xtcsetattr(SHIN, on, &tchars); 12859243Sobrien# else 12959243Sobrien (void) ioctl(SHIN, on, (ioctl_t) &tchars); 13059243Sobrien# endif /* POSIX */ 13159243Sobrien#else 13259243Sobrien struct sgttyb sgtty; 13359243Sobrien static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */ 13459243Sobrien 13559243Sobrien if (on) { 13659243Sobrien (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars); 13759243Sobrien tchars.t_brkc = ESC; 13859243Sobrien (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 13959243Sobrien /* 14059243Sobrien * This must be done after every command: if the tty gets into raw or 14159243Sobrien * cbreak mode the user can't even type 'reset'. 14259243Sobrien */ 14359243Sobrien (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty); 14459243Sobrien if (sgtty.sg_flags & (RAW | CBREAK)) { 14559243Sobrien sgtty.sg_flags &= ~(RAW | CBREAK); 14659243Sobrien (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty); 14759243Sobrien } 14859243Sobrien } 14959243Sobrien else { 15059243Sobrien tchars.t_brkc = -1; 15159243Sobrien (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 15259243Sobrien } 15359243Sobrien#endif /* TERMIO */ 15459243Sobrien} 15559243Sobrien 15659243Sobrien/* 15759243Sobrien * Move back to beginning of current line 15859243Sobrien */ 15959243Sobrienstatic void 160167465Smpback_to_col_1(void) 16159243Sobrien{ 16259243Sobrien#ifdef TERMIO 16359243Sobrien# ifdef POSIX 16459243Sobrien struct termios tty, tty_normal; 16559243Sobrien# else 16659243Sobrien struct termio tty, tty_normal; 16759243Sobrien# endif /* POSIX */ 16859243Sobrien#else 16959243Sobrien struct sgttyb tty, tty_normal; 17059243Sobrien#endif /* TERMIO */ 17159243Sobrien 172167465Smp pintr_disabled++; 173167465Smp cleanup_push(&pintr_disabled, disabled_cleanup); 17459243Sobrien 17559243Sobrien#ifdef TERMIO 17659243Sobrien# ifdef POSIX 17759243Sobrien (void) tcgetattr(SHOUT, &tty); 17859243Sobrien# else 17959243Sobrien (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal); 18059243Sobrien# endif /* POSIX */ 18159243Sobrien tty_normal = tty; 18259243Sobrien tty.c_iflag &= ~INLCR; 18359243Sobrien tty.c_oflag &= ~ONLCR; 18459243Sobrien# ifdef POSIX 185167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty); 18659243Sobrien# else 18759243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 18859243Sobrien# endif /* POSIX */ 189167465Smp (void) xwrite(SHOUT, "\r", 1); 19059243Sobrien# ifdef POSIX 191167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal); 19259243Sobrien# else 19359243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 19459243Sobrien# endif /* POSIX */ 19559243Sobrien#else 19659243Sobrien (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty); 19759243Sobrien tty_normal = tty; 19859243Sobrien tty.sg_flags &= ~CRMOD; 19959243Sobrien (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty); 200167465Smp (void) xwrite(SHOUT, "\r", 1); 20159243Sobrien (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal); 20259243Sobrien#endif /* TERMIO */ 20359243Sobrien 204167465Smp cleanup_until(&pintr_disabled); 20559243Sobrien} 20659243Sobrien 20759243Sobrien/* 20859243Sobrien * Push string contents back into tty queue 20959243Sobrien */ 21059243Sobrienstatic void 211167465Smppushback(const Char *string) 21259243Sobrien{ 213167465Smp const Char *p; 21459243Sobrien#ifdef TERMIO 21559243Sobrien# ifdef POSIX 21659243Sobrien struct termios tty, tty_normal; 21759243Sobrien# else 21859243Sobrien struct termio tty, tty_normal; 21959243Sobrien# endif /* POSIX */ 22059243Sobrien#else 22159243Sobrien struct sgttyb tty, tty_normal; 22259243Sobrien#endif /* TERMIO */ 22359243Sobrien 224167465Smp pintr_disabled++; 225167465Smp cleanup_push(&pintr_disabled, disabled_cleanup); 22659243Sobrien 22759243Sobrien#ifdef TERMIO 22859243Sobrien# ifdef POSIX 22959243Sobrien (void) tcgetattr(SHOUT, &tty); 23059243Sobrien# else 231316957Sdchagin (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 23259243Sobrien# endif /* POSIX */ 23359243Sobrien tty_normal = tty; 234195609Smp tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | 235195609Smp#ifndef __QNXNTO__ 236195609Smp ECHOPRT | 237195609Smp#endif 238195609Smp ECHOCTL); 23959243Sobrien# ifdef POSIX 240167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty); 24159243Sobrien# else 24259243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 24359243Sobrien# endif /* POSIX */ 24459243Sobrien 245145479Smp for (p = string; *p != '\0'; p++) { 246145479Smp char buf[MB_LEN_MAX]; 247145479Smp size_t i, len; 248145479Smp 249316957Sdchagin len = one_wctomb(buf, *p); 250145479Smp for (i = 0; i < len; i++) 251145479Smp (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) &buf[i]); 252145479Smp } 25359243Sobrien# ifdef POSIX 254167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal); 25559243Sobrien# else 25659243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 25759243Sobrien# endif /* POSIX */ 25859243Sobrien#else 25959243Sobrien (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty); 26059243Sobrien tty_normal = tty; 26159243Sobrien tty.sg_flags &= ~ECHO; 26259243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty); 26359243Sobrien 26459243Sobrien for (p = string; c = *p; p++) 26559243Sobrien (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 26659243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal); 26759243Sobrien#endif /* TERMIO */ 26859243Sobrien 269167465Smp cleanup_until(&pintr_disabled); 27059243Sobrien} 27159243Sobrien 272145479Smpstatic int 273167465Smpfiletype(const Char *dir, const Char *file) 27459243Sobrien{ 275167465Smp Char *path; 276167465Smp char *spath; 27759243Sobrien struct stat statb; 27859243Sobrien 279167465Smp path = Strspl(dir, file); 280167465Smp spath = short2str(path); 281167465Smp xfree(path); 282167465Smp if (lstat(spath, &statb) == 0) { 28359243Sobrien switch (statb.st_mode & S_IFMT) { 28459243Sobrien case S_IFDIR: 28559243Sobrien return ('/'); 28659243Sobrien 28759243Sobrien case S_IFLNK: 288167465Smp if (stat(spath, &statb) == 0 && /* follow it out */ 28959243Sobrien S_ISDIR(statb.st_mode)) 29059243Sobrien return ('>'); 29159243Sobrien else 29259243Sobrien return ('@'); 29359243Sobrien 29459243Sobrien case S_IFSOCK: 29559243Sobrien return ('='); 29659243Sobrien 29759243Sobrien default: 29859243Sobrien if (statb.st_mode & 0111) 29959243Sobrien return ('*'); 30059243Sobrien } 30159243Sobrien } 30259243Sobrien return (' '); 30359243Sobrien} 30459243Sobrien 30559243Sobrien/* 30659243Sobrien * Print sorted down columns 30759243Sobrien */ 30859243Sobrienstatic void 309167465Smpprint_by_column(const Char *dir, Char *items[], size_t count) 31059243Sobrien{ 311167465Smp struct winsize win; 312145479Smp size_t i; 313145479Smp int rows, r, c, maxwidth = 0, columns; 31459243Sobrien 31559243Sobrien if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 31659243Sobrien win.ws_col = 80; 31759243Sobrien for (i = 0; i < count; i++) 31859243Sobrien maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 31959243Sobrien maxwidth += 2; /* for the file tag and space */ 32059243Sobrien columns = win.ws_col / maxwidth; 32159243Sobrien if (columns == 0) 32259243Sobrien columns = 1; 32359243Sobrien rows = (count + (columns - 1)) / columns; 32459243Sobrien for (r = 0; r < rows; r++) { 32559243Sobrien for (c = 0; c < columns; c++) { 32659243Sobrien i = c * rows + r; 32759243Sobrien if (i < count) { 328100616Smp int w; 32959243Sobrien 33059243Sobrien xprintf("%S", items[i]); 33159243Sobrien xputchar(dir ? filetype(dir, items[i]) : ' '); 33259243Sobrien if (c < columns - 1) { /* last column? */ 33359243Sobrien w = Strlen(items[i]) + 1; 33459243Sobrien for (; w < maxwidth; w++) 33559243Sobrien xputchar(' '); 33659243Sobrien } 33759243Sobrien } 33859243Sobrien } 33959243Sobrien xputchar('\r'); 34059243Sobrien xputchar('\n'); 34159243Sobrien } 34259243Sobrien} 34359243Sobrien 34459243Sobrien/* 34559243Sobrien * Expand file name with possible tilde usage 34659243Sobrien * ~person/mumble 34759243Sobrien * expands to 34859243Sobrien * home_directory_of_person/mumble 34959243Sobrien */ 35059243Sobrienstatic Char * 351167465Smptilde(const Char *old) 35259243Sobrien{ 353167465Smp const Char *o, *home; 354100616Smp struct passwd *pw; 35559243Sobrien 35659243Sobrien if (old[0] != '~') 357167465Smp return (Strsave(old)); 358167465Smp old++; 35959243Sobrien 360167465Smp for (o = old; *o != '\0' && *o != '/'; o++) 361167465Smp ; 362167465Smp if (o == old) 363167465Smp home = varval(STRhome); 36459243Sobrien else { 365167465Smp Char *person; 366167465Smp 367167465Smp person = Strnsave(old, o - old); 368167465Smp pw = xgetpwnam(short2str(person)); 369167465Smp xfree(person); 37059243Sobrien if (pw == NULL) 37159243Sobrien return (NULL); 372167465Smp home = str2short(pw->pw_dir); 37359243Sobrien } 374167465Smp return Strspl(home, o); 37559243Sobrien} 37659243Sobrien 37759243Sobrien/* 37859243Sobrien * Cause pending line to be printed 37959243Sobrien */ 38059243Sobrienstatic void 381167465Smpretype(void) 38259243Sobrien{ 38359243Sobrien#ifdef TERMIO 38459243Sobrien# ifdef POSIX 38559243Sobrien struct termios tty; 38659243Sobrien 38759243Sobrien (void) tcgetattr(SHOUT, &tty); 38859243Sobrien# else 38959243Sobrien struct termio tty; 39059243Sobrien 39159243Sobrien (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 39259243Sobrien# endif /* POSIX */ 39359243Sobrien 394195609Smp#ifndef __QNXNTO__ 39559243Sobrien tty.c_lflag |= PENDIN; 396195609Smp#endif 39759243Sobrien 39859243Sobrien# ifdef POSIX 399167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty); 40059243Sobrien# else 40159243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 40259243Sobrien# endif /* POSIX */ 40359243Sobrien#else 40459243Sobrien int pending_input = LPENDIN; 40559243Sobrien 40659243Sobrien (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input); 40759243Sobrien#endif /* TERMIO */ 40859243Sobrien} 40959243Sobrien 41059243Sobrienstatic void 411167465Smpbeep(void) 41259243Sobrien{ 41359243Sobrien if (adrof(STRnobeep) == 0) 41469408Sache#ifdef IS_ASCII 415167465Smp (void) xwrite(SHOUT, "\007", 1); 41669408Sache#else 41759243Sobrien { 41859243Sobrien unsigned char beep_ch = CTL_ESC('\007'); 419167465Smp (void) xwrite(SHOUT, &beep_ch, 1); 42059243Sobrien } 42169408Sache#endif 42259243Sobrien} 42359243Sobrien 42459243Sobrien/* 42559243Sobrien * Erase that silly ^[ and 42659243Sobrien * print the recognized part of the string 42759243Sobrien */ 42859243Sobrienstatic void 429167465Smpprint_recognized_stuff(const Char *recognized_part) 43059243Sobrien{ 43159243Sobrien /* An optimized erasing of that silly ^[ */ 43259243Sobrien (void) putraw('\b'); 43359243Sobrien (void) putraw('\b'); 43459243Sobrien switch (Strlen(recognized_part)) { 43559243Sobrien 43659243Sobrien case 0: /* erase two Characters: ^[ */ 43759243Sobrien (void) putraw(' '); 43859243Sobrien (void) putraw(' '); 43959243Sobrien (void) putraw('\b'); 44059243Sobrien (void) putraw('\b'); 44159243Sobrien break; 44259243Sobrien 44359243Sobrien case 1: /* overstrike the ^, erase the [ */ 44459243Sobrien xprintf("%S", recognized_part); 44559243Sobrien (void) putraw(' '); 44659243Sobrien (void) putraw('\b'); 44759243Sobrien break; 44859243Sobrien 44959243Sobrien default: /* overstrike both Characters ^[ */ 45059243Sobrien xprintf("%S", recognized_part); 45159243Sobrien break; 45259243Sobrien } 45359243Sobrien flush(); 45459243Sobrien} 45559243Sobrien 45659243Sobrien/* 45759243Sobrien * Parse full path in file into 2 parts: directory and file names 45859243Sobrien * Should leave final slash (/) at end of dir. 45959243Sobrien */ 46059243Sobrienstatic void 461167465Smpextract_dir_and_name(const Char *path, Char **dir, const Char **name) 46259243Sobrien{ 463167465Smp const Char *p; 46459243Sobrien 46559243Sobrien p = Strrchr(path, '/'); 466167465Smp if (p == NULL) 467167465Smp p = path; 468167465Smp else 469167465Smp p++; 470167465Smp *name = p; 471167465Smp *dir = Strnsave(path, p - path); 47259243Sobrien} 473145479Smp 47459243Sobrienstatic Char * 475167465Smpgetitem(DIR *dir_fd, int looking_for_lognames) 47659243Sobrien{ 477100616Smp struct passwd *pw; 478100616Smp struct dirent *dirp; 47959243Sobrien 48059243Sobrien if (looking_for_lognames) { 481145479Smp#ifndef HAVE_GETPWENT 48259243Sobrien return (NULL); 48359243Sobrien#else 48459243Sobrien if ((pw = getpwent()) == NULL) 48559243Sobrien return (NULL); 48659243Sobrien return (str2short(pw->pw_name)); 48759243Sobrien#endif /* atp vmsposix */ 48859243Sobrien } 489100616Smp if ((dirp = readdir(dir_fd)) != NULL) 49059243Sobrien return (str2short(dirp->d_name)); 49159243Sobrien return (NULL); 49259243Sobrien} 49359243Sobrien 49459243Sobrien/* 49559243Sobrien * Perform a RECOGNIZE or LIST command on string "word". 49659243Sobrien */ 497167465Smpstatic size_t 498167465Smptsearch(Char *word, COMMAND command, size_t max_word_length) 49959243Sobrien{ 500100616Smp DIR *dir_fd; 501145479Smp int ignoring = TRUE, nignored = 0; 502167465Smp int looking_for_lognames; 503167465Smp Char *tilded_dir = NULL, *dir = NULL; 504167465Smp Char *extended_name = NULL; 505167465Smp const Char *name; 506167465Smp Char *item; 507167465Smp struct blk_buf items = BLK_BUF_INIT; 508167465Smp size_t name_length; 50959243Sobrien 51059243Sobrien looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 51159243Sobrien if (looking_for_lognames) { 512145479Smp#ifdef HAVE_GETPWENT 51359243Sobrien (void) setpwent(); 514145479Smp#endif 515167465Smp name = word + 1; /* name sans ~ */ 51659243Sobrien dir_fd = NULL; 517167465Smp cleanup_push(dir, xfree); 51859243Sobrien } 51959243Sobrien else { 520167465Smp extract_dir_and_name(word, &dir, &name); 521167465Smp cleanup_push(dir, xfree); 522167465Smp tilded_dir = tilde(dir); 523167465Smp if (tilded_dir == NULL) 524167465Smp goto end; 525167465Smp cleanup_push(tilded_dir, xfree); 52659243Sobrien dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 52759243Sobrien if (dir_fd == NULL) 528167465Smp goto end; 52959243Sobrien } 53059243Sobrien 531167465Smp name_length = Strlen(name); 532167465Smp cleanup_push(&extended_name, xfree_indirect); 533167465Smp cleanup_push(&items, bb_cleanup); 53459243Sobrienagain: /* search for matches */ 535167465Smp while ((item = getitem(dir_fd, looking_for_lognames)) != NULL) { 53659243Sobrien if (!is_prefix(name, item)) 53759243Sobrien continue; 53859243Sobrien /* Don't match . files on null prefix match */ 53959243Sobrien if (name_length == 0 && item[0] == '.' && 54059243Sobrien !looking_for_lognames) 54159243Sobrien continue; 542167465Smp if (command == LIST) 543167465Smp bb_append(&items, Strsave(item)); 54459243Sobrien else { /* RECOGNIZE command */ 54559243Sobrien if (ignoring && ignored(item)) 54659243Sobrien nignored++; 547167465Smp else if (recognize(&extended_name, item, name_length, ++items.len)) 54859243Sobrien break; 54959243Sobrien } 55059243Sobrien } 551167465Smp if (ignoring && items.len == 0 && nignored > 0) { 55259243Sobrien ignoring = FALSE; 55359243Sobrien nignored = 0; 554145479Smp if (looking_for_lognames) { 555145479Smp#ifdef HAVE_GETPWENT 55659243Sobrien (void) setpwent(); 55759243Sobrien#endif /* atp vmsposix */ 558145479Smp } else 55959243Sobrien rewinddir(dir_fd); 56059243Sobrien goto again; 56159243Sobrien } 56259243Sobrien 563145479Smp if (looking_for_lognames) { 564316957Sdchagin#ifdef HAVE_GETPWENT 56559243Sobrien (void) endpwent(); 566145479Smp#endif 567145479Smp } else 568167465Smp xclosedir(dir_fd); 569167465Smp if (items.len != 0) { 570167465Smp if (command == RECOGNIZE) { 571167465Smp if (looking_for_lognames) 572167465Smp copyn(word, STRtilde, 2);/*FIXBUF, sort of */ 573167465Smp else 574167465Smp /* put back dir part */ 575167465Smp copyn(word, dir, max_word_length);/*FIXBUF*/ 576167465Smp /* add extended name */ 577167465Smp catn(word, extended_name, max_word_length);/*FIXBUF*/ 578167465Smp } 579167465Smp else { /* LIST */ 580167465Smp qsort(items.vec, items.len, sizeof(items.vec[0]), compare); 581167465Smp print_by_column(looking_for_lognames ? NULL : tilded_dir, 582167465Smp items.vec, items.len); 583167465Smp } 58459243Sobrien } 585167465Smp end: 586167465Smp cleanup_until(dir); 587167465Smp return items.len; 58859243Sobrien} 58959243Sobrien 590100616Smp 591100616Smpstatic int 592167465Smpcompare(const void *p, const void *q) 593100616Smp{ 594231990Smp#if defined (WIDE_STRINGS) && !defined (UTF16_STRING) 595145479Smp errno = 0; 596145479Smp 597167465Smp return (wcscoll(*(Char *const *) p, *(Char *const *) q)); 598145479Smp#else 599145479Smp char *p1, *q1; 600145479Smp int res; 601145479Smp 602167465Smp p1 = strsave(short2str(*(Char *const *) p)); 603167465Smp q1 = strsave(short2str(*(Char *const *) q)); 604167465Smp# if defined(NLS) && defined(HAVE_STRCOLL) 605145479Smp res = strcoll(p1, q1); 606145479Smp# else 607145479Smp res = strcmp(p1, q1); 608167465Smp# endif /* NLS && HAVE_STRCOLL */ 609145479Smp xfree (p1); 610145479Smp xfree (q1); 611145479Smp return res; 612145479Smp#endif /* not WIDE_STRINGS */ 613100616Smp} 614100616Smp 61559243Sobrien/* 61659243Sobrien * Object: extend what user typed up to an ambiguity. 61759243Sobrien * Algorithm: 61859243Sobrien * On first match, copy full item (assume it'll be the only match) 61959243Sobrien * On subsequent matches, shorten extended_name to the first 62059243Sobrien * Character mismatch between extended_name and item. 62159243Sobrien * If we shorten it back to the prefix length, stop searching. 62259243Sobrien */ 62359243Sobrienstatic int 624167465Smprecognize(Char **extended_name, Char *item, size_t name_length, 625167465Smp size_t numitems) 62659243Sobrien{ 62759243Sobrien if (numitems == 1) /* 1st match */ 628167465Smp *extended_name = Strsave(item); 62959243Sobrien else { /* 2nd & subsequent matches */ 630100616Smp Char *x, *ent; 631167465Smp size_t len = 0; 63259243Sobrien 633167465Smp x = *extended_name; 63459243Sobrien for (ent = item; *x && *x == *ent++; x++, len++); 63559243Sobrien *x = '\0'; /* Shorten at 1st Char diff */ 63659243Sobrien if (len == name_length) /* Ambiguous to prefix? */ 63759243Sobrien return (-1); /* So stop now and save time */ 63859243Sobrien } 63959243Sobrien return (0); 64059243Sobrien} 64159243Sobrien 64259243Sobrien/* 64359243Sobrien * Return true if check matches initial Chars in template. 64459243Sobrien * This differs from PWB imatch in that if check is null 64559243Sobrien * it matches anything. 64659243Sobrien */ 64759243Sobrienstatic int 648167465Smpis_prefix(const Char *check, const Char *template) 64959243Sobrien{ 65059243Sobrien do 65159243Sobrien if (*check == 0) 65259243Sobrien return (TRUE); 65359243Sobrien while (*check++ == *template++); 65459243Sobrien return (FALSE); 65559243Sobrien} 65659243Sobrien 65759243Sobrien/* 65859243Sobrien * Return true if the Chars in template appear at the 65959243Sobrien * end of check, I.e., are it's suffix. 66059243Sobrien */ 66159243Sobrienstatic int 662167465Smpis_suffix(const Char *check, const Char *template) 66359243Sobrien{ 664167465Smp const Char *c, *t; 66559243Sobrien 66659243Sobrien for (c = check; *c++;); 66759243Sobrien for (t = template; *t++;); 66859243Sobrien for (;;) { 66959243Sobrien if (t == template) 67059243Sobrien return 1; 67159243Sobrien if (c == check || *--t != *--c) 67259243Sobrien return 0; 67359243Sobrien } 67459243Sobrien} 67559243Sobrien 676167465Smpstatic void 677167465Smpsetup_tty_cleanup(void *dummy) 67859243Sobrien{ 679167465Smp USE(dummy); 680167465Smp setup_tty(OFF); 681167465Smp} 68259243Sobrien 683167465Smpsize_t 684167465Smptenex(Char *inputline, size_t inputline_size) 685167465Smp{ 686167465Smp size_t numitems; 687167465Smp ssize_t num_read; 688167465Smp char tinputline[BUFSIZE + 1];/*FIXBUF*/ 68959243Sobrien 69059243Sobrien setup_tty(ON); 691167465Smp cleanup_push(&num_read, setup_tty_cleanup); /* num_read is only a marker */ 69259243Sobrien 693167465Smp while ((num_read = xread(SHIN, tinputline, BUFSIZE)) > 0) {/*FIXBUF*/ 694167465Smp static const Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', 69559243Sobrien '>', '(', ')', '|', '^', '%', '\0'}; 696100616Smp Char *str_end, *word_start, last_Char, should_retype; 697167465Smp size_t space_left; 69859243Sobrien COMMAND command; 69959243Sobrien 700145479Smp tinputline[num_read] = 0; 701167465Smp Strcpy(inputline, str2short(tinputline));/*FIXBUF*/ 702145479Smp num_read = Strlen(inputline); 703167465Smp last_Char = CTL_ESC(ASC(inputline[num_read - 1]) & ASCII); 70459243Sobrien 705167465Smp if (last_Char == '\n' || (size_t)num_read == inputline_size) 70659243Sobrien break; 70759243Sobrien command = (last_Char == ESC) ? RECOGNIZE : LIST; 70859243Sobrien if (command == LIST) 70959243Sobrien xputchar('\n'); 71059243Sobrien str_end = &inputline[num_read]; 71159243Sobrien if (last_Char == ESC) 71259243Sobrien --str_end; /* wipeout trailing cmd Char */ 71359243Sobrien *str_end = '\0'; 71459243Sobrien /* 71559243Sobrien * Find LAST occurence of a delimiter in the inputline. The word start 71659243Sobrien * is one Character past it. 71759243Sobrien */ 71859243Sobrien for (word_start = str_end; word_start > inputline; --word_start) 71959243Sobrien if (Strchr(delims, word_start[-1])) 72059243Sobrien break; 72159243Sobrien space_left = inputline_size - (word_start - inputline) - 1; 72259243Sobrien numitems = tsearch(word_start, command, space_left); 72359243Sobrien 72459243Sobrien if (command == RECOGNIZE) { 72559243Sobrien /* print from str_end on */ 72659243Sobrien print_recognized_stuff(str_end); 72759243Sobrien if (numitems != 1) /* Beep = No match/ambiguous */ 72859243Sobrien beep(); 72959243Sobrien } 73059243Sobrien 73159243Sobrien /* 73259243Sobrien * Tabs in the input line cause trouble after a pushback. tty driver 73359243Sobrien * won't backspace over them because column positions are now 73459243Sobrien * incorrect. This is solved by retyping over current line. 73559243Sobrien */ 73659243Sobrien should_retype = FALSE; 73759243Sobrien if (Strchr(inputline, '\t')) { /* tab Char in input line? */ 73859243Sobrien back_to_col_1(); 73959243Sobrien should_retype = TRUE; 74059243Sobrien } 74159243Sobrien if (command == LIST) /* Always retype after a LIST */ 74259243Sobrien should_retype = TRUE; 74359243Sobrien if (should_retype) 74459243Sobrien printprompt(0, NULL); 74559243Sobrien pushback(inputline); 74659243Sobrien if (should_retype) 74759243Sobrien retype(); 74859243Sobrien } 749167465Smp cleanup_until(&num_read); 75059243Sobrien return (num_read); 75159243Sobrien} 75259243Sobrien 75359243Sobrienstatic int 754167465Smpignored(const Char *item) 75559243Sobrien{ 75659243Sobrien struct varent *vp; 757100616Smp Char **cp; 75859243Sobrien 75959243Sobrien if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 76059243Sobrien return (FALSE); 76159243Sobrien for (; *cp != NULL; cp++) 76259243Sobrien if (is_suffix(item, *cp)) 76359243Sobrien return (TRUE); 76459243Sobrien return (FALSE); 76559243Sobrien} 766100616Smp#endif /* FILEC && TIOCSTI */ 767