sh.file.c revision 232633
1232633Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.file.c,v 3.37 2010/02/09 20:21:49 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 36232633SmpRCSID("$tcsh: sh.file.c,v 3.37 2010/02/09 20:21:49 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; 237195609Smp tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | 238195609Smp#ifndef __QNXNTO__ 239195609Smp ECHOPRT | 240195609Smp#endif 241195609Smp ECHOCTL); 24259243Sobrien# ifdef POSIX 243167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty); 24459243Sobrien# else 24559243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 24659243Sobrien# endif /* POSIX */ 24759243Sobrien 248145479Smp for (p = string; *p != '\0'; p++) { 249145479Smp char buf[MB_LEN_MAX]; 250145479Smp size_t i, len; 251145479Smp 252145479Smp len = one_wctomb(buf, *p & CHAR); 253145479Smp for (i = 0; i < len; i++) 254145479Smp (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) &buf[i]); 255145479Smp } 25659243Sobrien# ifdef POSIX 257167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty_normal); 25859243Sobrien# else 25959243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 26059243Sobrien# endif /* POSIX */ 26159243Sobrien#else 26259243Sobrien (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty); 26359243Sobrien tty_normal = tty; 26459243Sobrien tty.sg_flags &= ~ECHO; 26559243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty); 26659243Sobrien 26759243Sobrien for (p = string; c = *p; p++) 26859243Sobrien (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 26959243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal); 27059243Sobrien#endif /* TERMIO */ 27159243Sobrien 272167465Smp cleanup_until(&pintr_disabled); 27359243Sobrien} 27459243Sobrien 275145479Smpstatic int 276167465Smpfiletype(const Char *dir, const Char *file) 27759243Sobrien{ 278167465Smp Char *path; 279167465Smp char *spath; 28059243Sobrien struct stat statb; 28159243Sobrien 282167465Smp path = Strspl(dir, file); 283167465Smp spath = short2str(path); 284167465Smp xfree(path); 285167465Smp if (lstat(spath, &statb) == 0) { 28659243Sobrien switch (statb.st_mode & S_IFMT) { 28759243Sobrien case S_IFDIR: 28859243Sobrien return ('/'); 28959243Sobrien 29059243Sobrien case S_IFLNK: 291167465Smp if (stat(spath, &statb) == 0 && /* follow it out */ 29259243Sobrien S_ISDIR(statb.st_mode)) 29359243Sobrien return ('>'); 29459243Sobrien else 29559243Sobrien return ('@'); 29659243Sobrien 29759243Sobrien case S_IFSOCK: 29859243Sobrien return ('='); 29959243Sobrien 30059243Sobrien default: 30159243Sobrien if (statb.st_mode & 0111) 30259243Sobrien return ('*'); 30359243Sobrien } 30459243Sobrien } 30559243Sobrien return (' '); 30659243Sobrien} 30759243Sobrien 30859243Sobrien/* 30959243Sobrien * Print sorted down columns 31059243Sobrien */ 31159243Sobrienstatic void 312167465Smpprint_by_column(const Char *dir, Char *items[], size_t count) 31359243Sobrien{ 314167465Smp struct winsize win; 315145479Smp size_t i; 316145479Smp int rows, r, c, maxwidth = 0, columns; 31759243Sobrien 31859243Sobrien if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 31959243Sobrien win.ws_col = 80; 32059243Sobrien for (i = 0; i < count; i++) 32159243Sobrien maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 32259243Sobrien maxwidth += 2; /* for the file tag and space */ 32359243Sobrien columns = win.ws_col / maxwidth; 32459243Sobrien if (columns == 0) 32559243Sobrien columns = 1; 32659243Sobrien rows = (count + (columns - 1)) / columns; 32759243Sobrien for (r = 0; r < rows; r++) { 32859243Sobrien for (c = 0; c < columns; c++) { 32959243Sobrien i = c * rows + r; 33059243Sobrien if (i < count) { 331100616Smp int w; 33259243Sobrien 33359243Sobrien xprintf("%S", items[i]); 33459243Sobrien xputchar(dir ? filetype(dir, items[i]) : ' '); 33559243Sobrien if (c < columns - 1) { /* last column? */ 33659243Sobrien w = Strlen(items[i]) + 1; 33759243Sobrien for (; w < maxwidth; w++) 33859243Sobrien xputchar(' '); 33959243Sobrien } 34059243Sobrien } 34159243Sobrien } 34259243Sobrien xputchar('\r'); 34359243Sobrien xputchar('\n'); 34459243Sobrien } 34559243Sobrien} 34659243Sobrien 34759243Sobrien/* 34859243Sobrien * Expand file name with possible tilde usage 34959243Sobrien * ~person/mumble 35059243Sobrien * expands to 35159243Sobrien * home_directory_of_person/mumble 35259243Sobrien */ 35359243Sobrienstatic Char * 354167465Smptilde(const Char *old) 35559243Sobrien{ 356167465Smp const Char *o, *home; 357100616Smp struct passwd *pw; 35859243Sobrien 35959243Sobrien if (old[0] != '~') 360167465Smp return (Strsave(old)); 361167465Smp old++; 36259243Sobrien 363167465Smp for (o = old; *o != '\0' && *o != '/'; o++) 364167465Smp ; 365167465Smp if (o == old) 366167465Smp home = varval(STRhome); 36759243Sobrien else { 368167465Smp Char *person; 369167465Smp 370167465Smp person = Strnsave(old, o - old); 371167465Smp pw = xgetpwnam(short2str(person)); 372167465Smp xfree(person); 37359243Sobrien if (pw == NULL) 37459243Sobrien return (NULL); 375167465Smp home = str2short(pw->pw_dir); 37659243Sobrien } 377167465Smp return Strspl(home, o); 37859243Sobrien} 37959243Sobrien 38059243Sobrien/* 38159243Sobrien * Cause pending line to be printed 38259243Sobrien */ 38359243Sobrienstatic void 384167465Smpretype(void) 38559243Sobrien{ 38659243Sobrien#ifdef TERMIO 38759243Sobrien# ifdef POSIX 38859243Sobrien struct termios tty; 38959243Sobrien 39059243Sobrien (void) tcgetattr(SHOUT, &tty); 39159243Sobrien# else 39259243Sobrien struct termio tty; 39359243Sobrien 39459243Sobrien (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 39559243Sobrien# endif /* POSIX */ 39659243Sobrien 397195609Smp#ifndef __QNXNTO__ 39859243Sobrien tty.c_lflag |= PENDIN; 399195609Smp#endif 40059243Sobrien 40159243Sobrien# ifdef POSIX 402167465Smp (void) xtcsetattr(SHOUT, TCSANOW, &tty); 40359243Sobrien# else 40459243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 40559243Sobrien# endif /* POSIX */ 40659243Sobrien#else 40759243Sobrien int pending_input = LPENDIN; 40859243Sobrien 40959243Sobrien (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input); 41059243Sobrien#endif /* TERMIO */ 41159243Sobrien} 41259243Sobrien 41359243Sobrienstatic void 414167465Smpbeep(void) 41559243Sobrien{ 41659243Sobrien if (adrof(STRnobeep) == 0) 41769408Sache#ifdef IS_ASCII 418167465Smp (void) xwrite(SHOUT, "\007", 1); 41969408Sache#else 42059243Sobrien { 42159243Sobrien unsigned char beep_ch = CTL_ESC('\007'); 422167465Smp (void) xwrite(SHOUT, &beep_ch, 1); 42359243Sobrien } 42469408Sache#endif 42559243Sobrien} 42659243Sobrien 42759243Sobrien/* 42859243Sobrien * Erase that silly ^[ and 42959243Sobrien * print the recognized part of the string 43059243Sobrien */ 43159243Sobrienstatic void 432167465Smpprint_recognized_stuff(const Char *recognized_part) 43359243Sobrien{ 43459243Sobrien /* An optimized erasing of that silly ^[ */ 43559243Sobrien (void) putraw('\b'); 43659243Sobrien (void) putraw('\b'); 43759243Sobrien switch (Strlen(recognized_part)) { 43859243Sobrien 43959243Sobrien case 0: /* erase two Characters: ^[ */ 44059243Sobrien (void) putraw(' '); 44159243Sobrien (void) putraw(' '); 44259243Sobrien (void) putraw('\b'); 44359243Sobrien (void) putraw('\b'); 44459243Sobrien break; 44559243Sobrien 44659243Sobrien case 1: /* overstrike the ^, erase the [ */ 44759243Sobrien xprintf("%S", recognized_part); 44859243Sobrien (void) putraw(' '); 44959243Sobrien (void) putraw('\b'); 45059243Sobrien break; 45159243Sobrien 45259243Sobrien default: /* overstrike both Characters ^[ */ 45359243Sobrien xprintf("%S", recognized_part); 45459243Sobrien break; 45559243Sobrien } 45659243Sobrien flush(); 45759243Sobrien} 45859243Sobrien 45959243Sobrien/* 46059243Sobrien * Parse full path in file into 2 parts: directory and file names 46159243Sobrien * Should leave final slash (/) at end of dir. 46259243Sobrien */ 46359243Sobrienstatic void 464167465Smpextract_dir_and_name(const Char *path, Char **dir, const Char **name) 46559243Sobrien{ 466167465Smp const Char *p; 46759243Sobrien 46859243Sobrien p = Strrchr(path, '/'); 469167465Smp if (p == NULL) 470167465Smp p = path; 471167465Smp else 472167465Smp p++; 473167465Smp *name = p; 474167465Smp *dir = Strnsave(path, p - path); 47559243Sobrien} 476145479Smp 47759243Sobrienstatic Char * 478167465Smpgetitem(DIR *dir_fd, int looking_for_lognames) 47959243Sobrien{ 480100616Smp struct passwd *pw; 481100616Smp struct dirent *dirp; 48259243Sobrien 48359243Sobrien if (looking_for_lognames) { 484145479Smp#ifndef HAVE_GETPWENT 48559243Sobrien return (NULL); 48659243Sobrien#else 48759243Sobrien if ((pw = getpwent()) == NULL) 48859243Sobrien return (NULL); 48959243Sobrien return (str2short(pw->pw_name)); 49059243Sobrien#endif /* atp vmsposix */ 49159243Sobrien } 492100616Smp if ((dirp = readdir(dir_fd)) != NULL) 49359243Sobrien return (str2short(dirp->d_name)); 49459243Sobrien return (NULL); 49559243Sobrien} 49659243Sobrien 49759243Sobrien/* 49859243Sobrien * Perform a RECOGNIZE or LIST command on string "word". 49959243Sobrien */ 500167465Smpstatic size_t 501167465Smptsearch(Char *word, COMMAND command, size_t max_word_length) 50259243Sobrien{ 503100616Smp DIR *dir_fd; 504145479Smp int ignoring = TRUE, nignored = 0; 505167465Smp int looking_for_lognames; 506167465Smp Char *tilded_dir = NULL, *dir = NULL; 507167465Smp Char *extended_name = NULL; 508167465Smp const Char *name; 509167465Smp Char *item; 510167465Smp struct blk_buf items = BLK_BUF_INIT; 511167465Smp size_t name_length; 51259243Sobrien 51359243Sobrien looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 51459243Sobrien if (looking_for_lognames) { 515145479Smp#ifdef HAVE_GETPWENT 51659243Sobrien (void) setpwent(); 517145479Smp#endif 518167465Smp name = word + 1; /* name sans ~ */ 51959243Sobrien dir_fd = NULL; 520167465Smp cleanup_push(dir, xfree); 52159243Sobrien } 52259243Sobrien else { 523167465Smp extract_dir_and_name(word, &dir, &name); 524167465Smp cleanup_push(dir, xfree); 525167465Smp tilded_dir = tilde(dir); 526167465Smp if (tilded_dir == NULL) 527167465Smp goto end; 528167465Smp cleanup_push(tilded_dir, xfree); 52959243Sobrien dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 53059243Sobrien if (dir_fd == NULL) 531167465Smp goto end; 53259243Sobrien } 53359243Sobrien 534167465Smp name_length = Strlen(name); 535167465Smp cleanup_push(&extended_name, xfree_indirect); 536167465Smp cleanup_push(&items, bb_cleanup); 53759243Sobrienagain: /* search for matches */ 538167465Smp while ((item = getitem(dir_fd, looking_for_lognames)) != NULL) { 53959243Sobrien if (!is_prefix(name, item)) 54059243Sobrien continue; 54159243Sobrien /* Don't match . files on null prefix match */ 54259243Sobrien if (name_length == 0 && item[0] == '.' && 54359243Sobrien !looking_for_lognames) 54459243Sobrien continue; 545167465Smp if (command == LIST) 546167465Smp bb_append(&items, Strsave(item)); 54759243Sobrien else { /* RECOGNIZE command */ 54859243Sobrien if (ignoring && ignored(item)) 54959243Sobrien nignored++; 550167465Smp else if (recognize(&extended_name, item, name_length, ++items.len)) 55159243Sobrien break; 55259243Sobrien } 55359243Sobrien } 554167465Smp if (ignoring && items.len == 0 && nignored > 0) { 55559243Sobrien ignoring = FALSE; 55659243Sobrien nignored = 0; 557145479Smp if (looking_for_lognames) { 558145479Smp#ifdef HAVE_GETPWENT 55959243Sobrien (void) setpwent(); 56059243Sobrien#endif /* atp vmsposix */ 561145479Smp } else 56259243Sobrien rewinddir(dir_fd); 56359243Sobrien goto again; 56459243Sobrien } 56559243Sobrien 566145479Smp if (looking_for_lognames) { 567145479Smp#ifndef HAVE_GETPWENT 56859243Sobrien (void) endpwent(); 569145479Smp#endif 570145479Smp } else 571167465Smp xclosedir(dir_fd); 572167465Smp if (items.len != 0) { 573167465Smp if (command == RECOGNIZE) { 574167465Smp if (looking_for_lognames) 575167465Smp copyn(word, STRtilde, 2);/*FIXBUF, sort of */ 576167465Smp else 577167465Smp /* put back dir part */ 578167465Smp copyn(word, dir, max_word_length);/*FIXBUF*/ 579167465Smp /* add extended name */ 580167465Smp catn(word, extended_name, max_word_length);/*FIXBUF*/ 581167465Smp } 582167465Smp else { /* LIST */ 583167465Smp qsort(items.vec, items.len, sizeof(items.vec[0]), compare); 584167465Smp print_by_column(looking_for_lognames ? NULL : tilded_dir, 585167465Smp items.vec, items.len); 586167465Smp } 58759243Sobrien } 588167465Smp end: 589167465Smp cleanup_until(dir); 590167465Smp return items.len; 59159243Sobrien} 59259243Sobrien 593100616Smp 594100616Smpstatic int 595167465Smpcompare(const void *p, const void *q) 596100616Smp{ 597232633Smp#if defined (WIDE_STRINGS) && !defined (UTF16_STRING) 598145479Smp errno = 0; 599145479Smp 600167465Smp return (wcscoll(*(Char *const *) p, *(Char *const *) q)); 601145479Smp#else 602145479Smp char *p1, *q1; 603145479Smp int res; 604145479Smp 605167465Smp p1 = strsave(short2str(*(Char *const *) p)); 606167465Smp q1 = strsave(short2str(*(Char *const *) q)); 607167465Smp# if defined(NLS) && defined(HAVE_STRCOLL) 608145479Smp res = strcoll(p1, q1); 609145479Smp# else 610145479Smp res = strcmp(p1, q1); 611167465Smp# endif /* NLS && HAVE_STRCOLL */ 612145479Smp xfree (p1); 613145479Smp xfree (q1); 614145479Smp return res; 615145479Smp#endif /* not WIDE_STRINGS */ 616100616Smp} 617100616Smp 61859243Sobrien/* 61959243Sobrien * Object: extend what user typed up to an ambiguity. 62059243Sobrien * Algorithm: 62159243Sobrien * On first match, copy full item (assume it'll be the only match) 62259243Sobrien * On subsequent matches, shorten extended_name to the first 62359243Sobrien * Character mismatch between extended_name and item. 62459243Sobrien * If we shorten it back to the prefix length, stop searching. 62559243Sobrien */ 62659243Sobrienstatic int 627167465Smprecognize(Char **extended_name, Char *item, size_t name_length, 628167465Smp size_t numitems) 62959243Sobrien{ 63059243Sobrien if (numitems == 1) /* 1st match */ 631167465Smp *extended_name = Strsave(item); 63259243Sobrien else { /* 2nd & subsequent matches */ 633100616Smp Char *x, *ent; 634167465Smp size_t len = 0; 63559243Sobrien 636167465Smp x = *extended_name; 63759243Sobrien for (ent = item; *x && *x == *ent++; x++, len++); 63859243Sobrien *x = '\0'; /* Shorten at 1st Char diff */ 63959243Sobrien if (len == name_length) /* Ambiguous to prefix? */ 64059243Sobrien return (-1); /* So stop now and save time */ 64159243Sobrien } 64259243Sobrien return (0); 64359243Sobrien} 64459243Sobrien 64559243Sobrien/* 64659243Sobrien * Return true if check matches initial Chars in template. 64759243Sobrien * This differs from PWB imatch in that if check is null 64859243Sobrien * it matches anything. 64959243Sobrien */ 65059243Sobrienstatic int 651167465Smpis_prefix(const Char *check, const Char *template) 65259243Sobrien{ 65359243Sobrien do 65459243Sobrien if (*check == 0) 65559243Sobrien return (TRUE); 65659243Sobrien while (*check++ == *template++); 65759243Sobrien return (FALSE); 65859243Sobrien} 65959243Sobrien 66059243Sobrien/* 66159243Sobrien * Return true if the Chars in template appear at the 66259243Sobrien * end of check, I.e., are it's suffix. 66359243Sobrien */ 66459243Sobrienstatic int 665167465Smpis_suffix(const Char *check, const Char *template) 66659243Sobrien{ 667167465Smp const Char *c, *t; 66859243Sobrien 66959243Sobrien for (c = check; *c++;); 67059243Sobrien for (t = template; *t++;); 67159243Sobrien for (;;) { 67259243Sobrien if (t == template) 67359243Sobrien return 1; 67459243Sobrien if (c == check || *--t != *--c) 67559243Sobrien return 0; 67659243Sobrien } 67759243Sobrien} 67859243Sobrien 679167465Smpstatic void 680167465Smpsetup_tty_cleanup(void *dummy) 68159243Sobrien{ 682167465Smp USE(dummy); 683167465Smp setup_tty(OFF); 684167465Smp} 68559243Sobrien 686167465Smpsize_t 687167465Smptenex(Char *inputline, size_t inputline_size) 688167465Smp{ 689167465Smp size_t numitems; 690167465Smp ssize_t num_read; 691167465Smp char tinputline[BUFSIZE + 1];/*FIXBUF*/ 69259243Sobrien 69359243Sobrien setup_tty(ON); 694167465Smp cleanup_push(&num_read, setup_tty_cleanup); /* num_read is only a marker */ 69559243Sobrien 696167465Smp while ((num_read = xread(SHIN, tinputline, BUFSIZE)) > 0) {/*FIXBUF*/ 697167465Smp static const Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', 69859243Sobrien '>', '(', ')', '|', '^', '%', '\0'}; 699100616Smp Char *str_end, *word_start, last_Char, should_retype; 700167465Smp size_t space_left; 70159243Sobrien COMMAND command; 70259243Sobrien 703145479Smp tinputline[num_read] = 0; 704167465Smp Strcpy(inputline, str2short(tinputline));/*FIXBUF*/ 705145479Smp num_read = Strlen(inputline); 706167465Smp last_Char = CTL_ESC(ASC(inputline[num_read - 1]) & ASCII); 70759243Sobrien 708167465Smp if (last_Char == '\n' || (size_t)num_read == inputline_size) 70959243Sobrien break; 71059243Sobrien command = (last_Char == ESC) ? RECOGNIZE : LIST; 71159243Sobrien if (command == LIST) 71259243Sobrien xputchar('\n'); 71359243Sobrien str_end = &inputline[num_read]; 71459243Sobrien if (last_Char == ESC) 71559243Sobrien --str_end; /* wipeout trailing cmd Char */ 71659243Sobrien *str_end = '\0'; 71759243Sobrien /* 71859243Sobrien * Find LAST occurence of a delimiter in the inputline. The word start 71959243Sobrien * is one Character past it. 72059243Sobrien */ 72159243Sobrien for (word_start = str_end; word_start > inputline; --word_start) 72259243Sobrien if (Strchr(delims, word_start[-1])) 72359243Sobrien break; 72459243Sobrien space_left = inputline_size - (word_start - inputline) - 1; 72559243Sobrien numitems = tsearch(word_start, command, space_left); 72659243Sobrien 72759243Sobrien if (command == RECOGNIZE) { 72859243Sobrien /* print from str_end on */ 72959243Sobrien print_recognized_stuff(str_end); 73059243Sobrien if (numitems != 1) /* Beep = No match/ambiguous */ 73159243Sobrien beep(); 73259243Sobrien } 73359243Sobrien 73459243Sobrien /* 73559243Sobrien * Tabs in the input line cause trouble after a pushback. tty driver 73659243Sobrien * won't backspace over them because column positions are now 73759243Sobrien * incorrect. This is solved by retyping over current line. 73859243Sobrien */ 73959243Sobrien should_retype = FALSE; 74059243Sobrien if (Strchr(inputline, '\t')) { /* tab Char in input line? */ 74159243Sobrien back_to_col_1(); 74259243Sobrien should_retype = TRUE; 74359243Sobrien } 74459243Sobrien if (command == LIST) /* Always retype after a LIST */ 74559243Sobrien should_retype = TRUE; 74659243Sobrien if (should_retype) 74759243Sobrien printprompt(0, NULL); 74859243Sobrien pushback(inputline); 74959243Sobrien if (should_retype) 75059243Sobrien retype(); 75159243Sobrien } 752167465Smp cleanup_until(&num_read); 75359243Sobrien return (num_read); 75459243Sobrien} 75559243Sobrien 75659243Sobrienstatic int 757167465Smpignored(const Char *item) 75859243Sobrien{ 75959243Sobrien struct varent *vp; 760100616Smp Char **cp; 76159243Sobrien 76259243Sobrien if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 76359243Sobrien return (FALSE); 76459243Sobrien for (; *cp != NULL; cp++) 76559243Sobrien if (is_suffix(item, *cp)) 76659243Sobrien return (TRUE); 76759243Sobrien return (FALSE); 76859243Sobrien} 769100616Smp#endif /* FILEC && TIOCSTI */ 770