sh.file.c revision 167465
1167465Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.file.c,v 3.35 2006/08/23 15:03:14 christos Exp $ */ 259243Sobrien/* 359243Sobrien * sh.file.c: File completion for csh. This file is not used in tcsh. 459243Sobrien */ 559243Sobrien/*- 659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 759243Sobrien * All rights reserved. 859243Sobrien * 959243Sobrien * Redistribution and use in source and binary forms, with or without 1059243Sobrien * modification, are permitted provided that the following conditions 1159243Sobrien * are met: 1259243Sobrien * 1. Redistributions of source code must retain the above copyright 1359243Sobrien * notice, this list of conditions and the following disclaimer. 1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1559243Sobrien * notice, this list of conditions and the following disclaimer in the 1659243Sobrien * documentation and/or other materials provided with the distribution. 17100616Smp * 3. Neither the name of the University nor the names of its contributors 1859243Sobrien * may be used to endorse or promote products derived from this software 1959243Sobrien * without specific prior written permission. 2059243Sobrien * 2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2459243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3159243Sobrien * SUCH DAMAGE. 3259243Sobrien */ 3359243Sobrien#include "sh.h" 34100616Smp#include "ed.h" 3559243Sobrien 36167465SmpRCSID("$tcsh: sh.file.c,v 3.35 2006/08/23 15:03:14 christos Exp $") 3759243Sobrien 38100616Smp#if defined(FILEC) && defined(TIOCSTI) 3959243Sobrien 4059243Sobrien/* 4159243Sobrien * Tenex style file name recognition, .. and more. 4259243Sobrien * History: 4359243Sobrien * Author: Ken Greer, Sept. 1975, CMU. 4459243Sobrien * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. 4559243Sobrien */ 4659243Sobrien 4759243Sobrien#define ON 1 4859243Sobrien#define OFF 0 4959243Sobrien#ifndef TRUE 5059243Sobrien#define TRUE 1 5159243Sobrien#endif 5259243Sobrien#ifndef FALSE 5359243Sobrien#define FALSE 0 5459243Sobrien#endif 5559243Sobrien 5659243Sobrien#define ESC CTL_ESC('\033') 5759243Sobrien 5859243Sobrientypedef enum { 5959243Sobrien LIST, RECOGNIZE 6059243Sobrien} COMMAND; 6159243Sobrien 62167465Smpstatic void setup_tty (int); 63167465Smpstatic void back_to_col_1 (void); 64167465Smpstatic void pushback (const Char *); 65167465Smpstatic int filetype (const Char *, const Char *); 66167465Smpstatic void print_by_column (const Char *, Char *[], size_t); 67167465Smpstatic Char *tilde (const Char *); 68167465Smpstatic void retype (void); 69167465Smpstatic void beep (void); 70167465Smpstatic void print_recognized_stuff (const Char *); 71167465Smpstatic void extract_dir_and_name (const Char *, Char **, const Char **); 72167465Smpstatic Char *getitem (DIR *, int); 73167465Smpstatic size_t tsearch (Char *, COMMAND, size_t); 74167465Smpstatic int compare (const void *, const void *); 75167465Smpstatic int recognize (Char **, Char *, size_t, size_t); 76167465Smpstatic int is_prefix (const Char *, const Char *); 77167465Smpstatic int is_suffix (const Char *, const Char *); 78167465Smpstatic int ignored (const Char *); 7959243Sobrien 8059243Sobrien 8159243Sobrien/* 8259243Sobrien * Put this here so the binary can be patched with adb to enable file 8359243Sobrien * completion by default. Filec controls completion, nobeep controls 8459243Sobrien * ringing the terminal bell on incomplete expansions. 8559243Sobrien */ 86145479Smpint filec = 0; 8759243Sobrien 8859243Sobrienstatic void 89167465Smpsetup_tty(int on) 9059243Sobrien{ 9159243Sobrien#ifdef TERMIO 9259243Sobrien# ifdef POSIX 9359243Sobrien struct termios tchars; 9459243Sobrien# else 9559243Sobrien struct termio tchars; 9659243Sobrien# endif /* POSIX */ 9759243Sobrien 9859243Sobrien# ifdef POSIX 9959243Sobrien (void) tcgetattr(SHIN, &tchars); 10059243Sobrien# else 10159243Sobrien (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars); 10259243Sobrien# endif /* POSIX */ 10359243Sobrien if (on) { 10459243Sobrien tchars.c_cc[VEOL] = ESC; 10559243Sobrien if (tchars.c_lflag & ICANON) 10659243Sobrien# ifdef POSIX 10759243Sobrien on = TCSADRAIN; 10859243Sobrien# else 10959243Sobrien on = TCSETA; 11059243Sobrien# endif /* POSIX */ 11159243Sobrien else { 11259243Sobrien# ifdef POSIX 11359243Sobrien on = TCSAFLUSH; 11459243Sobrien# else 11559243Sobrien on = TCSETAF; 11659243Sobrien# endif /* POSIX */ 11759243Sobrien tchars.c_lflag |= ICANON; 11859243Sobrien 11959243Sobrien } 12059243Sobrien } 12159243Sobrien else { 12259243Sobrien tchars.c_cc[VEOL] = _POSIX_VDISABLE; 12359243Sobrien# ifdef POSIX 12459243Sobrien on = TCSADRAIN; 12559243Sobrien# else 12659243Sobrien on = TCSETA; 12759243Sobrien# endif /* POSIX */ 12859243Sobrien } 12959243Sobrien# ifdef POSIX 130167465Smp (void) xtcsetattr(SHIN, on, &tchars); 13159243Sobrien# else 13259243Sobrien (void) ioctl(SHIN, on, (ioctl_t) &tchars); 13359243Sobrien# endif /* POSIX */ 13459243Sobrien#else 13559243Sobrien struct sgttyb sgtty; 13659243Sobrien static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */ 13759243Sobrien 13859243Sobrien if (on) { 13959243Sobrien (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars); 14059243Sobrien tchars.t_brkc = ESC; 14159243Sobrien (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 14259243Sobrien /* 14359243Sobrien * This must be done after every command: if the tty gets into raw or 14459243Sobrien * cbreak mode the user can't even type 'reset'. 14559243Sobrien */ 14659243Sobrien (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty); 14759243Sobrien if (sgtty.sg_flags & (RAW | CBREAK)) { 14859243Sobrien sgtty.sg_flags &= ~(RAW | CBREAK); 14959243Sobrien (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty); 15059243Sobrien } 15159243Sobrien } 15259243Sobrien else { 15359243Sobrien tchars.t_brkc = -1; 15459243Sobrien (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 15559243Sobrien } 15659243Sobrien#endif /* TERMIO */ 15759243Sobrien} 15859243Sobrien 15959243Sobrien/* 16059243Sobrien * Move back to beginning of current line 16159243Sobrien */ 16259243Sobrienstatic void 163167465Smpback_to_col_1(void) 16459243Sobrien{ 16559243Sobrien#ifdef TERMIO 16659243Sobrien# ifdef POSIX 16759243Sobrien struct termios tty, tty_normal; 16859243Sobrien# else 16959243Sobrien struct termio tty, tty_normal; 17059243Sobrien# endif /* POSIX */ 17159243Sobrien#else 17259243Sobrien struct sgttyb tty, tty_normal; 17359243Sobrien#endif /* TERMIO */ 17459243Sobrien 175167465Smp pintr_disabled++; 176167465Smp cleanup_push(&pintr_disabled, disabled_cleanup); 17759243Sobrien 17859243Sobrien#ifdef TERMIO 17959243Sobrien# ifdef POSIX 18059243Sobrien (void) tcgetattr(SHOUT, &tty); 18159243Sobrien# else 18259243Sobrien (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal); 18359243Sobrien# endif /* POSIX */ 18459243Sobrien tty_normal = tty; 18559243Sobrien tty.c_iflag &= ~INLCR; 18659243Sobrien tty.c_oflag &= ~ONLCR; 18759243Sobrien# ifdef POSIX 188167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty); 18959243Sobrien# else 19059243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 19159243Sobrien# endif /* POSIX */ 192167465Smp (void) xwrite(SHOUT, "\r", 1); 19359243Sobrien# ifdef POSIX 194167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal); 19559243Sobrien# else 19659243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 19759243Sobrien# endif /* POSIX */ 19859243Sobrien#else 19959243Sobrien (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty); 20059243Sobrien tty_normal = tty; 20159243Sobrien tty.sg_flags &= ~CRMOD; 20259243Sobrien (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty); 203167465Smp (void) xwrite(SHOUT, "\r", 1); 20459243Sobrien (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal); 20559243Sobrien#endif /* TERMIO */ 20659243Sobrien 207167465Smp cleanup_until(&pintr_disabled); 20859243Sobrien} 20959243Sobrien 21059243Sobrien/* 21159243Sobrien * Push string contents back into tty queue 21259243Sobrien */ 21359243Sobrienstatic void 214167465Smppushback(const Char *string) 21559243Sobrien{ 216167465Smp const Char *p; 21759243Sobrien#ifdef TERMIO 21859243Sobrien# ifdef POSIX 21959243Sobrien struct termios tty, tty_normal; 22059243Sobrien# else 22159243Sobrien struct termio tty, tty_normal; 22259243Sobrien# endif /* POSIX */ 22359243Sobrien#else 22459243Sobrien struct sgttyb tty, tty_normal; 22559243Sobrien#endif /* TERMIO */ 22659243Sobrien 227167465Smp pintr_disabled++; 228167465Smp cleanup_push(&pintr_disabled, disabled_cleanup); 22959243Sobrien 23059243Sobrien#ifdef TERMIO 23159243Sobrien# ifdef POSIX 23259243Sobrien (void) tcgetattr(SHOUT, &tty); 23359243Sobrien# else 23459243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 23559243Sobrien# endif /* POSIX */ 23659243Sobrien tty_normal = tty; 23759243Sobrien tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL); 23859243Sobrien# ifdef POSIX 239167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty); 24059243Sobrien# else 24159243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 24259243Sobrien# endif /* POSIX */ 24359243Sobrien 244145479Smp for (p = string; *p != '\0'; p++) { 245145479Smp char buf[MB_LEN_MAX]; 246145479Smp size_t i, len; 247145479Smp 248145479Smp len = one_wctomb(buf, *p & CHAR); 249145479Smp for (i = 0; i < len; i++) 250145479Smp (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) &buf[i]); 251145479Smp } 25259243Sobrien# ifdef POSIX 253167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal); 25459243Sobrien# else 25559243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 25659243Sobrien# endif /* POSIX */ 25759243Sobrien#else 25859243Sobrien (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty); 25959243Sobrien tty_normal = tty; 26059243Sobrien tty.sg_flags &= ~ECHO; 26159243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty); 26259243Sobrien 26359243Sobrien for (p = string; c = *p; p++) 26459243Sobrien (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 26559243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal); 26659243Sobrien#endif /* TERMIO */ 26759243Sobrien 268167465Smp cleanup_until(&pintr_disabled); 26959243Sobrien} 27059243Sobrien 271145479Smpstatic int 272167465Smpfiletype(const Char *dir, const Char *file) 27359243Sobrien{ 274167465Smp Char *path; 275167465Smp char *spath; 27659243Sobrien struct stat statb; 27759243Sobrien 278167465Smp path = Strspl(dir, file); 279167465Smp spath = short2str(path); 280167465Smp xfree(path); 281167465Smp if (lstat(spath, &statb) == 0) { 28259243Sobrien switch (statb.st_mode & S_IFMT) { 28359243Sobrien case S_IFDIR: 28459243Sobrien return ('/'); 28559243Sobrien 28659243Sobrien case S_IFLNK: 287167465Smp if (stat(spath, &statb) == 0 && /* follow it out */ 28859243Sobrien S_ISDIR(statb.st_mode)) 28959243Sobrien return ('>'); 29059243Sobrien else 29159243Sobrien return ('@'); 29259243Sobrien 29359243Sobrien case S_IFSOCK: 29459243Sobrien return ('='); 29559243Sobrien 29659243Sobrien default: 29759243Sobrien if (statb.st_mode & 0111) 29859243Sobrien return ('*'); 29959243Sobrien } 30059243Sobrien } 30159243Sobrien return (' '); 30259243Sobrien} 30359243Sobrien 30459243Sobrien/* 30559243Sobrien * Print sorted down columns 30659243Sobrien */ 30759243Sobrienstatic void 308167465Smpprint_by_column(const Char *dir, Char *items[], size_t count) 30959243Sobrien{ 310167465Smp struct winsize win; 311145479Smp size_t i; 312145479Smp int rows, r, c, maxwidth = 0, columns; 31359243Sobrien 31459243Sobrien if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 31559243Sobrien win.ws_col = 80; 31659243Sobrien for (i = 0; i < count; i++) 31759243Sobrien maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 31859243Sobrien maxwidth += 2; /* for the file tag and space */ 31959243Sobrien columns = win.ws_col / maxwidth; 32059243Sobrien if (columns == 0) 32159243Sobrien columns = 1; 32259243Sobrien rows = (count + (columns - 1)) / columns; 32359243Sobrien for (r = 0; r < rows; r++) { 32459243Sobrien for (c = 0; c < columns; c++) { 32559243Sobrien i = c * rows + r; 32659243Sobrien if (i < count) { 327100616Smp int w; 32859243Sobrien 32959243Sobrien xprintf("%S", items[i]); 33059243Sobrien xputchar(dir ? filetype(dir, items[i]) : ' '); 33159243Sobrien if (c < columns - 1) { /* last column? */ 33259243Sobrien w = Strlen(items[i]) + 1; 33359243Sobrien for (; w < maxwidth; w++) 33459243Sobrien xputchar(' '); 33559243Sobrien } 33659243Sobrien } 33759243Sobrien } 33859243Sobrien xputchar('\r'); 33959243Sobrien xputchar('\n'); 34059243Sobrien } 34159243Sobrien} 34259243Sobrien 34359243Sobrien/* 34459243Sobrien * Expand file name with possible tilde usage 34559243Sobrien * ~person/mumble 34659243Sobrien * expands to 34759243Sobrien * home_directory_of_person/mumble 34859243Sobrien */ 34959243Sobrienstatic Char * 350167465Smptilde(const Char *old) 35159243Sobrien{ 352167465Smp const Char *o, *home; 353100616Smp struct passwd *pw; 35459243Sobrien 35559243Sobrien if (old[0] != '~') 356167465Smp return (Strsave(old)); 357167465Smp old++; 35859243Sobrien 359167465Smp for (o = old; *o != '\0' && *o != '/'; o++) 360167465Smp ; 361167465Smp if (o == old) 362167465Smp home = varval(STRhome); 36359243Sobrien else { 364167465Smp Char *person; 365167465Smp 366167465Smp person = Strnsave(old, o - old); 367167465Smp pw = xgetpwnam(short2str(person)); 368167465Smp xfree(person); 36959243Sobrien if (pw == NULL) 37059243Sobrien return (NULL); 371167465Smp home = str2short(pw->pw_dir); 37259243Sobrien } 373167465Smp return Strspl(home, o); 37459243Sobrien} 37559243Sobrien 37659243Sobrien/* 37759243Sobrien * Cause pending line to be printed 37859243Sobrien */ 37959243Sobrienstatic void 380167465Smpretype(void) 38159243Sobrien{ 38259243Sobrien#ifdef TERMIO 38359243Sobrien# ifdef POSIX 38459243Sobrien struct termios tty; 38559243Sobrien 38659243Sobrien (void) tcgetattr(SHOUT, &tty); 38759243Sobrien# else 38859243Sobrien struct termio tty; 38959243Sobrien 39059243Sobrien (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 39159243Sobrien# endif /* POSIX */ 39259243Sobrien 39359243Sobrien tty.c_lflag |= PENDIN; 39459243Sobrien 39559243Sobrien# ifdef POSIX 396167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty); 39759243Sobrien# else 39859243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 39959243Sobrien# endif /* POSIX */ 40059243Sobrien#else 40159243Sobrien int pending_input = LPENDIN; 40259243Sobrien 40359243Sobrien (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input); 40459243Sobrien#endif /* TERMIO */ 40559243Sobrien} 40659243Sobrien 40759243Sobrienstatic void 408167465Smpbeep(void) 40959243Sobrien{ 41059243Sobrien if (adrof(STRnobeep) == 0) 41169408Sache#ifdef IS_ASCII 412167465Smp (void) xwrite(SHOUT, "\007", 1); 41369408Sache#else 41459243Sobrien { 41559243Sobrien unsigned char beep_ch = CTL_ESC('\007'); 416167465Smp (void) xwrite(SHOUT, &beep_ch, 1); 41759243Sobrien } 41869408Sache#endif 41959243Sobrien} 42059243Sobrien 42159243Sobrien/* 42259243Sobrien * Erase that silly ^[ and 42359243Sobrien * print the recognized part of the string 42459243Sobrien */ 42559243Sobrienstatic void 426167465Smpprint_recognized_stuff(const Char *recognized_part) 42759243Sobrien{ 42859243Sobrien /* An optimized erasing of that silly ^[ */ 42959243Sobrien (void) putraw('\b'); 43059243Sobrien (void) putraw('\b'); 43159243Sobrien switch (Strlen(recognized_part)) { 43259243Sobrien 43359243Sobrien case 0: /* erase two Characters: ^[ */ 43459243Sobrien (void) putraw(' '); 43559243Sobrien (void) putraw(' '); 43659243Sobrien (void) putraw('\b'); 43759243Sobrien (void) putraw('\b'); 43859243Sobrien break; 43959243Sobrien 44059243Sobrien case 1: /* overstrike the ^, erase the [ */ 44159243Sobrien xprintf("%S", recognized_part); 44259243Sobrien (void) putraw(' '); 44359243Sobrien (void) putraw('\b'); 44459243Sobrien break; 44559243Sobrien 44659243Sobrien default: /* overstrike both Characters ^[ */ 44759243Sobrien xprintf("%S", recognized_part); 44859243Sobrien break; 44959243Sobrien } 45059243Sobrien flush(); 45159243Sobrien} 45259243Sobrien 45359243Sobrien/* 45459243Sobrien * Parse full path in file into 2 parts: directory and file names 45559243Sobrien * Should leave final slash (/) at end of dir. 45659243Sobrien */ 45759243Sobrienstatic void 458167465Smpextract_dir_and_name(const Char *path, Char **dir, const Char **name) 45959243Sobrien{ 460167465Smp const Char *p; 46159243Sobrien 46259243Sobrien p = Strrchr(path, '/'); 463167465Smp if (p == NULL) 464167465Smp p = path; 465167465Smp else 466167465Smp p++; 467167465Smp *name = p; 468167465Smp *dir = Strnsave(path, p - path); 46959243Sobrien} 470145479Smp 47159243Sobrienstatic Char * 472167465Smpgetitem(DIR *dir_fd, int looking_for_lognames) 47359243Sobrien{ 474100616Smp struct passwd *pw; 475100616Smp struct dirent *dirp; 47659243Sobrien 47759243Sobrien if (looking_for_lognames) { 478145479Smp#ifndef HAVE_GETPWENT 47959243Sobrien return (NULL); 48059243Sobrien#else 48159243Sobrien if ((pw = getpwent()) == NULL) 48259243Sobrien return (NULL); 48359243Sobrien return (str2short(pw->pw_name)); 48459243Sobrien#endif /* atp vmsposix */ 48559243Sobrien } 486100616Smp if ((dirp = readdir(dir_fd)) != NULL) 48759243Sobrien return (str2short(dirp->d_name)); 48859243Sobrien return (NULL); 48959243Sobrien} 49059243Sobrien 49159243Sobrien/* 49259243Sobrien * Perform a RECOGNIZE or LIST command on string "word". 49359243Sobrien */ 494167465Smpstatic size_t 495167465Smptsearch(Char *word, COMMAND command, size_t max_word_length) 49659243Sobrien{ 497100616Smp DIR *dir_fd; 498145479Smp int ignoring = TRUE, nignored = 0; 499167465Smp int looking_for_lognames; 500167465Smp Char *tilded_dir = NULL, *dir = NULL; 501167465Smp Char *extended_name = NULL; 502167465Smp const Char *name; 503167465Smp Char *item; 504167465Smp struct blk_buf items = BLK_BUF_INIT; 505167465Smp size_t name_length; 50659243Sobrien 50759243Sobrien looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 50859243Sobrien if (looking_for_lognames) { 509145479Smp#ifdef HAVE_GETPWENT 51059243Sobrien (void) setpwent(); 511145479Smp#endif 512167465Smp name = word + 1; /* name sans ~ */ 51359243Sobrien dir_fd = NULL; 514167465Smp cleanup_push(dir, xfree); 51559243Sobrien } 51659243Sobrien else { 517167465Smp extract_dir_and_name(word, &dir, &name); 518167465Smp cleanup_push(dir, xfree); 519167465Smp tilded_dir = tilde(dir); 520167465Smp if (tilded_dir == NULL) 521167465Smp goto end; 522167465Smp cleanup_push(tilded_dir, xfree); 52359243Sobrien dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 52459243Sobrien if (dir_fd == NULL) 525167465Smp goto end; 52659243Sobrien } 52759243Sobrien 528167465Smp name_length = Strlen(name); 529167465Smp cleanup_push(&extended_name, xfree_indirect); 530167465Smp cleanup_push(&items, bb_cleanup); 53159243Sobrienagain: /* search for matches */ 532167465Smp while ((item = getitem(dir_fd, looking_for_lognames)) != NULL) { 53359243Sobrien if (!is_prefix(name, item)) 53459243Sobrien continue; 53559243Sobrien /* Don't match . files on null prefix match */ 53659243Sobrien if (name_length == 0 && item[0] == '.' && 53759243Sobrien !looking_for_lognames) 53859243Sobrien continue; 539167465Smp if (command == LIST) 540167465Smp bb_append(&items, Strsave(item)); 54159243Sobrien else { /* RECOGNIZE command */ 54259243Sobrien if (ignoring && ignored(item)) 54359243Sobrien nignored++; 544167465Smp else if (recognize(&extended_name, item, name_length, ++items.len)) 54559243Sobrien break; 54659243Sobrien } 54759243Sobrien } 548167465Smp if (ignoring && items.len == 0 && nignored > 0) { 54959243Sobrien ignoring = FALSE; 55059243Sobrien nignored = 0; 551145479Smp if (looking_for_lognames) { 552145479Smp#ifdef HAVE_GETPWENT 55359243Sobrien (void) setpwent(); 55459243Sobrien#endif /* atp vmsposix */ 555145479Smp } else 55659243Sobrien rewinddir(dir_fd); 55759243Sobrien goto again; 55859243Sobrien } 55959243Sobrien 560145479Smp if (looking_for_lognames) { 561145479Smp#ifndef HAVE_GETPWENT 56259243Sobrien (void) endpwent(); 563145479Smp#endif 564145479Smp } else 565167465Smp xclosedir(dir_fd); 566167465Smp if (items.len != 0) { 567167465Smp if (command == RECOGNIZE) { 568167465Smp if (looking_for_lognames) 569167465Smp copyn(word, STRtilde, 2);/*FIXBUF, sort of */ 570167465Smp else 571167465Smp /* put back dir part */ 572167465Smp copyn(word, dir, max_word_length);/*FIXBUF*/ 573167465Smp /* add extended name */ 574167465Smp catn(word, extended_name, max_word_length);/*FIXBUF*/ 575167465Smp } 576167465Smp else { /* LIST */ 577167465Smp qsort(items.vec, items.len, sizeof(items.vec[0]), compare); 578167465Smp print_by_column(looking_for_lognames ? NULL : tilded_dir, 579167465Smp items.vec, items.len); 580167465Smp } 58159243Sobrien } 582167465Smp end: 583167465Smp cleanup_until(dir); 584167465Smp return items.len; 58559243Sobrien} 58659243Sobrien 587100616Smp 588100616Smpstatic int 589167465Smpcompare(const void *p, const void *q) 590100616Smp{ 591145479Smp#ifdef WIDE_STRINGS 592145479Smp errno = 0; 593145479Smp 594167465Smp return (wcscoll(*(Char *const *) p, *(Char *const *) q)); 595145479Smp#else 596145479Smp char *p1, *q1; 597145479Smp int res; 598145479Smp 599167465Smp p1 = strsave(short2str(*(Char *const *) p)); 600167465Smp q1 = strsave(short2str(*(Char *const *) q)); 601167465Smp# if defined(NLS) && defined(HAVE_STRCOLL) 602145479Smp res = strcoll(p1, q1); 603145479Smp# else 604145479Smp res = strcmp(p1, q1); 605167465Smp# endif /* NLS && HAVE_STRCOLL */ 606145479Smp xfree (p1); 607145479Smp xfree (q1); 608145479Smp return res; 609145479Smp#endif /* not WIDE_STRINGS */ 610100616Smp} 611100616Smp 61259243Sobrien/* 61359243Sobrien * Object: extend what user typed up to an ambiguity. 61459243Sobrien * Algorithm: 61559243Sobrien * On first match, copy full item (assume it'll be the only match) 61659243Sobrien * On subsequent matches, shorten extended_name to the first 61759243Sobrien * Character mismatch between extended_name and item. 61859243Sobrien * If we shorten it back to the prefix length, stop searching. 61959243Sobrien */ 62059243Sobrienstatic int 621167465Smprecognize(Char **extended_name, Char *item, size_t name_length, 622167465Smp size_t numitems) 62359243Sobrien{ 62459243Sobrien if (numitems == 1) /* 1st match */ 625167465Smp *extended_name = Strsave(item); 62659243Sobrien else { /* 2nd & subsequent matches */ 627100616Smp Char *x, *ent; 628167465Smp size_t len = 0; 62959243Sobrien 630167465Smp x = *extended_name; 63159243Sobrien for (ent = item; *x && *x == *ent++; x++, len++); 63259243Sobrien *x = '\0'; /* Shorten at 1st Char diff */ 63359243Sobrien if (len == name_length) /* Ambiguous to prefix? */ 63459243Sobrien return (-1); /* So stop now and save time */ 63559243Sobrien } 63659243Sobrien return (0); 63759243Sobrien} 63859243Sobrien 63959243Sobrien/* 64059243Sobrien * Return true if check matches initial Chars in template. 64159243Sobrien * This differs from PWB imatch in that if check is null 64259243Sobrien * it matches anything. 64359243Sobrien */ 64459243Sobrienstatic int 645167465Smpis_prefix(const Char *check, const Char *template) 64659243Sobrien{ 64759243Sobrien do 64859243Sobrien if (*check == 0) 64959243Sobrien return (TRUE); 65059243Sobrien while (*check++ == *template++); 65159243Sobrien return (FALSE); 65259243Sobrien} 65359243Sobrien 65459243Sobrien/* 65559243Sobrien * Return true if the Chars in template appear at the 65659243Sobrien * end of check, I.e., are it's suffix. 65759243Sobrien */ 65859243Sobrienstatic int 659167465Smpis_suffix(const Char *check, const Char *template) 66059243Sobrien{ 661167465Smp const Char *c, *t; 66259243Sobrien 66359243Sobrien for (c = check; *c++;); 66459243Sobrien for (t = template; *t++;); 66559243Sobrien for (;;) { 66659243Sobrien if (t == template) 66759243Sobrien return 1; 66859243Sobrien if (c == check || *--t != *--c) 66959243Sobrien return 0; 67059243Sobrien } 67159243Sobrien} 67259243Sobrien 673167465Smpstatic void 674167465Smpsetup_tty_cleanup(void *dummy) 67559243Sobrien{ 676167465Smp USE(dummy); 677167465Smp setup_tty(OFF); 678167465Smp} 67959243Sobrien 680167465Smpsize_t 681167465Smptenex(Char *inputline, size_t inputline_size) 682167465Smp{ 683167465Smp size_t numitems; 684167465Smp ssize_t num_read; 685167465Smp char tinputline[BUFSIZE + 1];/*FIXBUF*/ 68659243Sobrien 68759243Sobrien setup_tty(ON); 688167465Smp cleanup_push(&num_read, setup_tty_cleanup); /* num_read is only a marker */ 68959243Sobrien 690167465Smp while ((num_read = xread(SHIN, tinputline, BUFSIZE)) > 0) {/*FIXBUF*/ 691167465Smp static const Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', 69259243Sobrien '>', '(', ')', '|', '^', '%', '\0'}; 693100616Smp Char *str_end, *word_start, last_Char, should_retype; 694167465Smp size_t space_left; 69559243Sobrien COMMAND command; 69659243Sobrien 697145479Smp tinputline[num_read] = 0; 698167465Smp Strcpy(inputline, str2short(tinputline));/*FIXBUF*/ 699145479Smp num_read = Strlen(inputline); 700167465Smp last_Char = CTL_ESC(ASC(inputline[num_read - 1]) & ASCII); 70159243Sobrien 702167465Smp if (last_Char == '\n' || (size_t)num_read == inputline_size) 70359243Sobrien break; 70459243Sobrien command = (last_Char == ESC) ? RECOGNIZE : LIST; 70559243Sobrien if (command == LIST) 70659243Sobrien xputchar('\n'); 70759243Sobrien str_end = &inputline[num_read]; 70859243Sobrien if (last_Char == ESC) 70959243Sobrien --str_end; /* wipeout trailing cmd Char */ 71059243Sobrien *str_end = '\0'; 71159243Sobrien /* 71259243Sobrien * Find LAST occurence of a delimiter in the inputline. The word start 71359243Sobrien * is one Character past it. 71459243Sobrien */ 71559243Sobrien for (word_start = str_end; word_start > inputline; --word_start) 71659243Sobrien if (Strchr(delims, word_start[-1])) 71759243Sobrien break; 71859243Sobrien space_left = inputline_size - (word_start - inputline) - 1; 71959243Sobrien numitems = tsearch(word_start, command, space_left); 72059243Sobrien 72159243Sobrien if (command == RECOGNIZE) { 72259243Sobrien /* print from str_end on */ 72359243Sobrien print_recognized_stuff(str_end); 72459243Sobrien if (numitems != 1) /* Beep = No match/ambiguous */ 72559243Sobrien beep(); 72659243Sobrien } 72759243Sobrien 72859243Sobrien /* 72959243Sobrien * Tabs in the input line cause trouble after a pushback. tty driver 73059243Sobrien * won't backspace over them because column positions are now 73159243Sobrien * incorrect. This is solved by retyping over current line. 73259243Sobrien */ 73359243Sobrien should_retype = FALSE; 73459243Sobrien if (Strchr(inputline, '\t')) { /* tab Char in input line? */ 73559243Sobrien back_to_col_1(); 73659243Sobrien should_retype = TRUE; 73759243Sobrien } 73859243Sobrien if (command == LIST) /* Always retype after a LIST */ 73959243Sobrien should_retype = TRUE; 74059243Sobrien if (should_retype) 74159243Sobrien printprompt(0, NULL); 74259243Sobrien pushback(inputline); 74359243Sobrien if (should_retype) 74459243Sobrien retype(); 74559243Sobrien } 746167465Smp cleanup_until(&num_read); 74759243Sobrien return (num_read); 74859243Sobrien} 74959243Sobrien 75059243Sobrienstatic int 751167465Smpignored(const Char *item) 75259243Sobrien{ 75359243Sobrien struct varent *vp; 754100616Smp Char **cp; 75559243Sobrien 75659243Sobrien if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 75759243Sobrien return (FALSE); 75859243Sobrien for (; *cp != NULL; cp++) 75959243Sobrien if (is_suffix(item, *cp)) 76059243Sobrien return (TRUE); 76159243Sobrien return (FALSE); 76259243Sobrien} 763100616Smp#endif /* FILEC && TIOCSTI */ 764