sh.file.c revision 69408
169408Sache/* $Header: /src/pub/tcsh/sh.file.c,v 3.16 2000/06/11 02:14:14 kim 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. 1759243Sobrien * 3. All advertising materials mentioning features or use of this software 1859243Sobrien * must display the following acknowledgement: 1959243Sobrien * This product includes software developed by the University of 2059243Sobrien * California, Berkeley and its contributors. 2159243Sobrien * 4. Neither the name of the University nor the names of its contributors 2259243Sobrien * may be used to endorse or promote products derived from this software 2359243Sobrien * without specific prior written permission. 2459243Sobrien * 2559243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2659243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2759243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2859243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2959243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3059243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3159243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3259243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3359243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3459243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3559243Sobrien * SUCH DAMAGE. 3659243Sobrien */ 3759243Sobrien#include "sh.h" 3859243Sobrien 3969408SacheRCSID("$Id: sh.file.c,v 3.16 2000/06/11 02:14:14 kim Exp $") 4059243Sobrien 4159243Sobrien#ifdef FILEC 4259243Sobrien 4359243Sobrien/* 4459243Sobrien * Tenex style file name recognition, .. and more. 4559243Sobrien * History: 4659243Sobrien * Author: Ken Greer, Sept. 1975, CMU. 4759243Sobrien * Finally got around to adding to the Cshell., Ken Greer, Dec. 1981. 4859243Sobrien */ 4959243Sobrien 5059243Sobrien#define ON 1 5159243Sobrien#define OFF 0 5259243Sobrien#ifndef TRUE 5359243Sobrien#define TRUE 1 5459243Sobrien#endif 5559243Sobrien#ifndef FALSE 5659243Sobrien#define FALSE 0 5759243Sobrien#endif 5859243Sobrien 5959243Sobrien#define ESC CTL_ESC('\033') 6059243Sobrien 6159243Sobrientypedef enum { 6259243Sobrien LIST, RECOGNIZE 6359243Sobrien} COMMAND; 6459243Sobrien 6559243Sobrienstatic void setup_tty __P((int)); 6659243Sobrienstatic void back_to_col_1 __P((void)); 6759243Sobrienstatic void pushback __P((Char *)); 6859243Sobrienstatic void catn __P((Char *, Char *, int)); 6959243Sobrienstatic void copyn __P((Char *, Char *, int)); 7059243Sobrienstatic Char filetype __P((Char *, Char *)); 7159243Sobrienstatic void print_by_column __P((Char *, Char *[], int)); 7259243Sobrienstatic Char *tilde __P((Char *, Char *)); 7359243Sobrienstatic void retype __P((void)); 7459243Sobrienstatic void beep __P((void)); 7559243Sobrienstatic void print_recognized_stuff __P((Char *)); 7659243Sobrienstatic void extract_dir_and_name __P((Char *, Char *, Char *)); 7759243Sobrienstatic Char *getitem __P((DIR *, int)); 7859243Sobrienstatic void free_items __P((Char **)); 7959243Sobrienstatic int tsearch __P((Char *, COMMAND, int)); 8059243Sobrienstatic int recognize __P((Char *, Char *, int, int)); 8159243Sobrienstatic int is_prefix __P((Char *, Char *)); 8259243Sobrienstatic int is_suffix __P((Char *, Char *)); 8359243Sobrienstatic int ignored __P((Char *)); 8459243Sobrien 8559243Sobrien 8659243Sobrien/* 8759243Sobrien * Put this here so the binary can be patched with adb to enable file 8859243Sobrien * completion by default. Filec controls completion, nobeep controls 8959243Sobrien * ringing the terminal bell on incomplete expansions. 9059243Sobrien */ 9159243Sobrienbool filec = 0; 9259243Sobrien 9359243Sobrienstatic void 9459243Sobriensetup_tty(on) 9559243Sobrien int on; 9659243Sobrien{ 9759243Sobrien#ifdef TERMIO 9859243Sobrien# ifdef POSIX 9959243Sobrien struct termios tchars; 10059243Sobrien# else 10159243Sobrien struct termio tchars; 10259243Sobrien# endif /* POSIX */ 10359243Sobrien 10459243Sobrien# ifdef POSIX 10559243Sobrien (void) tcgetattr(SHIN, &tchars); 10659243Sobrien# else 10759243Sobrien (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars); 10859243Sobrien# endif /* POSIX */ 10959243Sobrien if (on) { 11059243Sobrien tchars.c_cc[VEOL] = ESC; 11159243Sobrien if (tchars.c_lflag & ICANON) 11259243Sobrien# ifdef POSIX 11359243Sobrien on = TCSADRAIN; 11459243Sobrien# else 11559243Sobrien on = TCSETA; 11659243Sobrien# endif /* POSIX */ 11759243Sobrien else { 11859243Sobrien# ifdef POSIX 11959243Sobrien on = TCSAFLUSH; 12059243Sobrien# else 12159243Sobrien on = TCSETAF; 12259243Sobrien# endif /* POSIX */ 12359243Sobrien tchars.c_lflag |= ICANON; 12459243Sobrien 12559243Sobrien } 12659243Sobrien } 12759243Sobrien else { 12859243Sobrien tchars.c_cc[VEOL] = _POSIX_VDISABLE; 12959243Sobrien# ifdef POSIX 13059243Sobrien on = TCSADRAIN; 13159243Sobrien# else 13259243Sobrien on = TCSETA; 13359243Sobrien# endif /* POSIX */ 13459243Sobrien } 13559243Sobrien# ifdef POSIX 13659243Sobrien (void) tcsetattr(SHIN, on, &tchars); 13759243Sobrien# else 13859243Sobrien (void) ioctl(SHIN, on, (ioctl_t) &tchars); 13959243Sobrien# endif /* POSIX */ 14059243Sobrien#else 14159243Sobrien struct sgttyb sgtty; 14259243Sobrien static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */ 14359243Sobrien 14459243Sobrien if (on) { 14559243Sobrien (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars); 14659243Sobrien tchars.t_brkc = ESC; 14759243Sobrien (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 14859243Sobrien /* 14959243Sobrien * This must be done after every command: if the tty gets into raw or 15059243Sobrien * cbreak mode the user can't even type 'reset'. 15159243Sobrien */ 15259243Sobrien (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty); 15359243Sobrien if (sgtty.sg_flags & (RAW | CBREAK)) { 15459243Sobrien sgtty.sg_flags &= ~(RAW | CBREAK); 15559243Sobrien (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty); 15659243Sobrien } 15759243Sobrien } 15859243Sobrien else { 15959243Sobrien tchars.t_brkc = -1; 16059243Sobrien (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 16159243Sobrien } 16259243Sobrien#endif /* TERMIO */ 16359243Sobrien} 16459243Sobrien 16559243Sobrien/* 16659243Sobrien * Move back to beginning of current line 16759243Sobrien */ 16859243Sobrienstatic void 16959243Sobrienback_to_col_1() 17059243Sobrien{ 17159243Sobrien#ifdef TERMIO 17259243Sobrien# ifdef POSIX 17359243Sobrien struct termios tty, tty_normal; 17459243Sobrien# else 17559243Sobrien struct termio tty, tty_normal; 17659243Sobrien# endif /* POSIX */ 17759243Sobrien#else 17859243Sobrien struct sgttyb tty, tty_normal; 17959243Sobrien#endif /* TERMIO */ 18059243Sobrien 18159243Sobrien# ifdef BSDSIGS 18259243Sobrien sigmask_t omask = sigblock(sigmask(SIGINT)); 18359243Sobrien# else 18459243Sobrien (void) sighold(SIGINT); 18559243Sobrien# endif /* BSDSIGS */ 18659243Sobrien 18759243Sobrien#ifdef TERMIO 18859243Sobrien# ifdef POSIX 18959243Sobrien (void) tcgetattr(SHOUT, &tty); 19059243Sobrien# else 19159243Sobrien (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal); 19259243Sobrien# endif /* POSIX */ 19359243Sobrien tty_normal = tty; 19459243Sobrien tty.c_iflag &= ~INLCR; 19559243Sobrien tty.c_oflag &= ~ONLCR; 19659243Sobrien# ifdef POSIX 19759243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty); 19859243Sobrien# else 19959243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 20059243Sobrien# endif /* POSIX */ 20159243Sobrien (void) write(SHOUT, "\r", 1); 20259243Sobrien# ifdef POSIX 20359243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); 20459243Sobrien# else 20559243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 20659243Sobrien# endif /* POSIX */ 20759243Sobrien#else 20859243Sobrien (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty); 20959243Sobrien tty_normal = tty; 21059243Sobrien tty.sg_flags &= ~CRMOD; 21159243Sobrien (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty); 21259243Sobrien (void) write(SHOUT, "\r", 1); 21359243Sobrien (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal); 21459243Sobrien#endif /* TERMIO */ 21559243Sobrien 21659243Sobrien# ifdef BSDSIGS 21759243Sobrien (void) sigsetmask(omask); 21859243Sobrien# else 21959243Sobrien (void) sigrelse(SIGINT); 22059243Sobrien# endif /* BSDISGS */ 22159243Sobrien} 22259243Sobrien 22359243Sobrien/* 22459243Sobrien * Push string contents back into tty queue 22559243Sobrien */ 22659243Sobrienstatic void 22759243Sobrienpushback(string) 22859243Sobrien Char *string; 22959243Sobrien{ 23059243Sobrien register Char *p; 23159243Sobrien char c; 23259243Sobrien#ifdef TERMIO 23359243Sobrien# ifdef POSIX 23459243Sobrien struct termios tty, tty_normal; 23559243Sobrien# else 23659243Sobrien struct termio tty, tty_normal; 23759243Sobrien# endif /* POSIX */ 23859243Sobrien#else 23959243Sobrien struct sgttyb tty, tty_normal; 24059243Sobrien#endif /* TERMIO */ 24159243Sobrien 24259243Sobrien#ifdef BSDSIGS 24359243Sobrien sigmask_t omask = sigblock(sigmask(SIGINT)); 24459243Sobrien#else 24559243Sobrien (void) sighold(SIGINT); 24659243Sobrien#endif /* BSDSIGS */ 24759243Sobrien 24859243Sobrien#ifdef TERMIO 24959243Sobrien# ifdef POSIX 25059243Sobrien (void) tcgetattr(SHOUT, &tty); 25159243Sobrien# else 25259243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 25359243Sobrien# endif /* POSIX */ 25459243Sobrien tty_normal = tty; 25559243Sobrien tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL); 25659243Sobrien# ifdef POSIX 25759243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty); 25859243Sobrien# else 25959243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 26059243Sobrien# endif /* POSIX */ 26159243Sobrien 26259243Sobrien for (p = string; c = *p; p++) 26359243Sobrien (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 26459243Sobrien# ifdef POSIX 26559243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); 26659243Sobrien# else 26759243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 26859243Sobrien# endif /* POSIX */ 26959243Sobrien (void) sigsetmask(omask); 27059243Sobrien#else 27159243Sobrien (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty); 27259243Sobrien tty_normal = tty; 27359243Sobrien tty.sg_flags &= ~ECHO; 27459243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty); 27559243Sobrien 27659243Sobrien for (p = string; c = *p; p++) 27759243Sobrien (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 27859243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal); 27959243Sobrien#endif /* TERMIO */ 28059243Sobrien 28159243Sobrien# ifdef BSDSIGS 28259243Sobrien (void) sigsetmask(omask); 28359243Sobrien# else 28459243Sobrien (void) sigrelse(SIGINT); 28559243Sobrien# endif /* BSDISGS */ 28659243Sobrien} 28759243Sobrien 28859243Sobrien/* 28959243Sobrien * Concatenate src onto tail of des. 29059243Sobrien * Des is a string whose maximum length is count. 29159243Sobrien * Always null terminate. 29259243Sobrien */ 29359243Sobrienstatic void 29459243Sobriencatn(des, src, count) 29559243Sobrien register Char *des, *src; 29659243Sobrien register count; 29759243Sobrien{ 29859243Sobrien while (--count >= 0 && *des) 29959243Sobrien des++; 30059243Sobrien while (--count >= 0) 30159243Sobrien if ((*des++ = *src++) == 0) 30259243Sobrien return; 30359243Sobrien *des = '\0'; 30459243Sobrien} 30559243Sobrien 30659243Sobrien/* 30759243Sobrien * Like strncpy but always leave room for trailing \0 30859243Sobrien * and always null terminate. 30959243Sobrien */ 31059243Sobrienstatic void 31159243Sobriencopyn(des, src, count) 31259243Sobrien register Char *des, *src; 31359243Sobrien register count; 31459243Sobrien{ 31559243Sobrien while (--count >= 0) 31659243Sobrien if ((*des++ = *src++) == 0) 31759243Sobrien return; 31859243Sobrien *des = '\0'; 31959243Sobrien} 32059243Sobrien 32159243Sobrienstatic Char 32259243Sobrienfiletype(dir, file) 32359243Sobrien Char *dir, *file; 32459243Sobrien{ 32559243Sobrien Char path[MAXPATHLEN]; 32659243Sobrien struct stat statb; 32759243Sobrien 32859243Sobrien catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char)); 32959243Sobrien if (lstat(short2str(path), &statb) == 0) { 33059243Sobrien switch (statb.st_mode & S_IFMT) { 33159243Sobrien case S_IFDIR: 33259243Sobrien return ('/'); 33359243Sobrien 33459243Sobrien case S_IFLNK: 33559243Sobrien if (stat(short2str(path), &statb) == 0 && /* follow it out */ 33659243Sobrien S_ISDIR(statb.st_mode)) 33759243Sobrien return ('>'); 33859243Sobrien else 33959243Sobrien return ('@'); 34059243Sobrien 34159243Sobrien case S_IFSOCK: 34259243Sobrien return ('='); 34359243Sobrien 34459243Sobrien default: 34559243Sobrien if (statb.st_mode & 0111) 34659243Sobrien return ('*'); 34759243Sobrien } 34859243Sobrien } 34959243Sobrien return (' '); 35059243Sobrien} 35159243Sobrien 35259243Sobrienstatic struct winsize win; 35359243Sobrien 35459243Sobrien/* 35559243Sobrien * Print sorted down columns 35659243Sobrien */ 35759243Sobrienstatic void 35859243Sobrienprint_by_column(dir, items, count) 35959243Sobrien Char *dir, *items[]; 36059243Sobrien int count; 36159243Sobrien{ 36259243Sobrien register int i, rows, r, c, maxwidth = 0, columns; 36359243Sobrien 36459243Sobrien if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 36559243Sobrien win.ws_col = 80; 36659243Sobrien for (i = 0; i < count; i++) 36759243Sobrien maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 36859243Sobrien maxwidth += 2; /* for the file tag and space */ 36959243Sobrien columns = win.ws_col / maxwidth; 37059243Sobrien if (columns == 0) 37159243Sobrien columns = 1; 37259243Sobrien rows = (count + (columns - 1)) / columns; 37359243Sobrien for (r = 0; r < rows; r++) { 37459243Sobrien for (c = 0; c < columns; c++) { 37559243Sobrien i = c * rows + r; 37659243Sobrien if (i < count) { 37759243Sobrien register int w; 37859243Sobrien 37959243Sobrien xprintf("%S", items[i]); 38059243Sobrien xputchar(dir ? filetype(dir, items[i]) : ' '); 38159243Sobrien if (c < columns - 1) { /* last column? */ 38259243Sobrien w = Strlen(items[i]) + 1; 38359243Sobrien for (; w < maxwidth; w++) 38459243Sobrien xputchar(' '); 38559243Sobrien } 38659243Sobrien } 38759243Sobrien } 38859243Sobrien xputchar('\r'); 38959243Sobrien xputchar('\n'); 39059243Sobrien } 39159243Sobrien} 39259243Sobrien 39359243Sobrien/* 39459243Sobrien * Expand file name with possible tilde usage 39559243Sobrien * ~person/mumble 39659243Sobrien * expands to 39759243Sobrien * home_directory_of_person/mumble 39859243Sobrien */ 39959243Sobrienstatic Char * 40059243Sobrientilde(new, old) 40159243Sobrien Char *new, *old; 40259243Sobrien{ 40359243Sobrien register Char *o, *p; 40459243Sobrien register struct passwd *pw; 40559243Sobrien static Char person[40]; 40659243Sobrien 40759243Sobrien if (old[0] != '~') 40859243Sobrien return (Strcpy(new, old)); 40959243Sobrien 41059243Sobrien for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++); 41159243Sobrien *p = '\0'; 41259243Sobrien if (person[0] == '\0') 41359243Sobrien (void) Strcpy(new, varval(STRhome)); 41459243Sobrien else { 41559243Sobrien pw = getpwnam(short2str(person)); 41659243Sobrien if (pw == NULL) 41759243Sobrien return (NULL); 41859243Sobrien (void) Strcpy(new, str2short(pw->pw_dir)); 41959243Sobrien } 42059243Sobrien (void) Strcat(new, o); 42159243Sobrien return (new); 42259243Sobrien} 42359243Sobrien 42459243Sobrien/* 42559243Sobrien * Cause pending line to be printed 42659243Sobrien */ 42759243Sobrienstatic void 42859243Sobrienretype() 42959243Sobrien{ 43059243Sobrien#ifdef TERMIO 43159243Sobrien# ifdef POSIX 43259243Sobrien struct termios tty; 43359243Sobrien 43459243Sobrien (void) tcgetattr(SHOUT, &tty); 43559243Sobrien# else 43659243Sobrien struct termio tty; 43759243Sobrien 43859243Sobrien (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 43959243Sobrien# endif /* POSIX */ 44059243Sobrien 44159243Sobrien tty.c_lflag |= PENDIN; 44259243Sobrien 44359243Sobrien# ifdef POSIX 44459243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty); 44559243Sobrien# else 44659243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 44759243Sobrien# endif /* POSIX */ 44859243Sobrien#else 44959243Sobrien int pending_input = LPENDIN; 45059243Sobrien 45159243Sobrien (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input); 45259243Sobrien#endif /* TERMIO */ 45359243Sobrien} 45459243Sobrien 45559243Sobrienstatic void 45659243Sobrienbeep() 45759243Sobrien{ 45859243Sobrien if (adrof(STRnobeep) == 0) 45969408Sache#ifdef IS_ASCII 46059243Sobrien (void) write(SHOUT, "\007", 1); 46169408Sache#else 46259243Sobrien { 46359243Sobrien unsigned char beep_ch = CTL_ESC('\007'); 46459243Sobrien (void) write(SHOUT, &beep_ch, 1); 46559243Sobrien } 46669408Sache#endif 46759243Sobrien} 46859243Sobrien 46959243Sobrien/* 47059243Sobrien * Erase that silly ^[ and 47159243Sobrien * print the recognized part of the string 47259243Sobrien */ 47359243Sobrienstatic void 47459243Sobrienprint_recognized_stuff(recognized_part) 47559243Sobrien Char *recognized_part; 47659243Sobrien{ 47759243Sobrien /* An optimized erasing of that silly ^[ */ 47859243Sobrien (void) putraw('\b'); 47959243Sobrien (void) putraw('\b'); 48059243Sobrien switch (Strlen(recognized_part)) { 48159243Sobrien 48259243Sobrien case 0: /* erase two Characters: ^[ */ 48359243Sobrien (void) putraw(' '); 48459243Sobrien (void) putraw(' '); 48559243Sobrien (void) putraw('\b'); 48659243Sobrien (void) putraw('\b'); 48759243Sobrien break; 48859243Sobrien 48959243Sobrien case 1: /* overstrike the ^, erase the [ */ 49059243Sobrien xprintf("%S", recognized_part); 49159243Sobrien (void) putraw(' '); 49259243Sobrien (void) putraw('\b'); 49359243Sobrien break; 49459243Sobrien 49559243Sobrien default: /* overstrike both Characters ^[ */ 49659243Sobrien xprintf("%S", recognized_part); 49759243Sobrien break; 49859243Sobrien } 49959243Sobrien flush(); 50059243Sobrien} 50159243Sobrien 50259243Sobrien/* 50359243Sobrien * Parse full path in file into 2 parts: directory and file names 50459243Sobrien * Should leave final slash (/) at end of dir. 50559243Sobrien */ 50659243Sobrienstatic void 50759243Sobrienextract_dir_and_name(path, dir, name) 50859243Sobrien Char *path, *dir, *name; 50959243Sobrien{ 51059243Sobrien register Char *p; 51159243Sobrien 51259243Sobrien p = Strrchr(path, '/'); 51359243Sobrien if (p == NULL) { 51459243Sobrien copyn(name, path, MAXNAMLEN); 51559243Sobrien dir[0] = '\0'; 51659243Sobrien } 51759243Sobrien else { 51859243Sobrien copyn(name, ++p, MAXNAMLEN); 51959243Sobrien copyn(dir, path, p - path); 52059243Sobrien } 52159243Sobrien} 52259243Sobrien/* atp vmsposix - I need to remove all the setpwent 52359243Sobrien * getpwent endpwent stuff. VMS_POSIX has getpwnam getpwuid 52459243Sobrien * and getlogin. This needs fixing. (There is no access to 52559243Sobrien * pw->passwd in VMS - a secure system benefit :-| ) 52659243Sobrien */ 52759243Sobrienstatic Char * 52859243Sobriengetitem(dir_fd, looking_for_lognames) 52959243Sobrien DIR *dir_fd; 53059243Sobrien int looking_for_lognames; 53159243Sobrien{ 53259243Sobrien register struct passwd *pw; 53359243Sobrien register struct dirent *dirp; 53459243Sobrien 53559243Sobrien if (looking_for_lognames) { 53659243Sobrien#ifdef _VMS_POSIX 53759243Sobrien return (NULL); 53859243Sobrien#else 53959243Sobrien if ((pw = getpwent()) == NULL) 54059243Sobrien return (NULL); 54159243Sobrien return (str2short(pw->pw_name)); 54259243Sobrien#endif /* atp vmsposix */ 54359243Sobrien } 54459243Sobrien if (dirp = readdir(dir_fd)) 54559243Sobrien return (str2short(dirp->d_name)); 54659243Sobrien return (NULL); 54759243Sobrien} 54859243Sobrien 54959243Sobrienstatic void 55059243Sobrienfree_items(items) 55159243Sobrien register Char **items; 55259243Sobrien{ 55359243Sobrien register int i; 55459243Sobrien 55559243Sobrien for (i = 0; items[i]; i++) 55659243Sobrien xfree((ptr_t) items[i]); 55759243Sobrien xfree((ptr_t) items); 55859243Sobrien} 55959243Sobrien 56059243Sobrien#ifdef BSDSIGS 56159243Sobrien# define FREE_ITEMS(items) { \ 56259243Sobrien sigmask_t omask;\ 56359243Sobrien\ 56459243Sobrien omask = sigblock(sigmask(SIGINT));\ 56559243Sobrien free_items(items);\ 56659243Sobrien items = NULL;\ 56759243Sobrien (void) sigsetmask(omask);\ 56859243Sobrien} 56959243Sobrien#else 57059243Sobrien# define FREE_ITEMS(items) { \ 57159243Sobrien (void) sighold(SIGINT);\ 57259243Sobrien free_items(items);\ 57359243Sobrien items = NULL;\ 57459243Sobrien (void) sigrelse(SIGINT);\ 57559243Sobrien} 57659243Sobrien#endif /* BSDSIGS */ 57759243Sobrien 57859243Sobrien/* 57959243Sobrien * Perform a RECOGNIZE or LIST command on string "word". 58059243Sobrien */ 58159243Sobrienstatic int 58259243Sobrientsearch(word, command, max_word_length) 58359243Sobrien Char *word; 58459243Sobrien int max_word_length; 58559243Sobrien COMMAND command; 58659243Sobrien{ 58759243Sobrien static Char **items = NULL; 58859243Sobrien register DIR *dir_fd; 58959243Sobrien register numitems = 0, ignoring = TRUE, nignored = 0; 59059243Sobrien register name_length, looking_for_lognames; 59159243Sobrien Char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1]; 59259243Sobrien Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; 59359243Sobrien Char *item; 59459243Sobrien 59559243Sobrien#define MAXITEMS 1024 59659243Sobrien 59759243Sobrien if (items != NULL) 59859243Sobrien FREE_ITEMS(items); 59959243Sobrien 60059243Sobrien looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 60159243Sobrien if (looking_for_lognames) { 60259243Sobrien#ifndef _VMS_POSIX 60359243Sobrien (void) setpwent(); 60459243Sobrien#endif /*atp vmsposix */ 60559243Sobrien copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ 60659243Sobrien dir_fd = NULL; 60759243Sobrien } 60859243Sobrien else { 60959243Sobrien extract_dir_and_name(word, dir, name); 61059243Sobrien if (tilde(tilded_dir, dir) == 0) 61159243Sobrien return (0); 61259243Sobrien dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 61359243Sobrien if (dir_fd == NULL) 61459243Sobrien return (0); 61559243Sobrien } 61659243Sobrien 61759243Sobrienagain: /* search for matches */ 61859243Sobrien name_length = Strlen(name); 61959243Sobrien for (numitems = 0; item = getitem(dir_fd, looking_for_lognames);) { 62059243Sobrien if (!is_prefix(name, item)) 62159243Sobrien continue; 62259243Sobrien /* Don't match . files on null prefix match */ 62359243Sobrien if (name_length == 0 && item[0] == '.' && 62459243Sobrien !looking_for_lognames) 62559243Sobrien continue; 62659243Sobrien if (command == LIST) { 62759243Sobrien if (numitems >= MAXITEMS) { 62859243Sobrien xprintf(CGETS(14, 1, "\nYikes!! Too many %s!!\n"), 62959243Sobrien looking_for_lognames ? 63059243Sobrien CGETS(14, 2, "names in password file") : 63159243Sobrien CGETS(14, 3, "files"); 63259243Sobrien break; 63359243Sobrien } 63459243Sobrien /* 63559243Sobrien * From Beto Appleton (beto@aixwiz.austin.ibm.com) 63659243Sobrien * typing "./control-d" will cause the csh to core-dump. 63759243Sobrien * the problem can be reproduce as following: 63859243Sobrien * 1. set ignoreeof 63959243Sobrien * 2. set filec 64059243Sobrien * 3. create a directory with 1050 files 64159243Sobrien * 4. typing "./control-d" will cause the csh to core-dump 64259243Sobrien * Solution: Add + 1 to MAXITEMS 64359243Sobrien */ 64459243Sobrien if (items == NULL) 64559243Sobrien items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS + 1); 64659243Sobrien items[numitems] = (Char *) xmalloc((size_t) (Strlen(item) + 1) * 64759243Sobrien sizeof(Char)); 64859243Sobrien copyn(items[numitems], item, MAXNAMLEN); 64959243Sobrien numitems++; 65059243Sobrien } 65159243Sobrien else { /* RECOGNIZE command */ 65259243Sobrien if (ignoring && ignored(item)) 65359243Sobrien nignored++; 65459243Sobrien else if (recognize(extended_name, 65559243Sobrien item, name_length, ++numitems)) 65659243Sobrien break; 65759243Sobrien } 65859243Sobrien } 65959243Sobrien if (ignoring && numitems == 0 && nignored > 0) { 66059243Sobrien ignoring = FALSE; 66159243Sobrien nignored = 0; 66259243Sobrien if (looking_for_lognames) 66359243Sobrien#ifndef _VMS_POSIX 66459243Sobrien (void) setpwent(); 66559243Sobrien#endif /* atp vmsposix */ 66659243Sobrien else 66759243Sobrien rewinddir(dir_fd); 66859243Sobrien goto again; 66959243Sobrien } 67059243Sobrien 67159243Sobrien if (looking_for_lognames) 67259243Sobrien#ifndef _VMS_POSIX 67359243Sobrien (void) endpwent(); 67459243Sobrien#endif /*atp vmsposix */ 67559243Sobrien else 67659243Sobrien (void) closedir(dir_fd); 67759243Sobrien if (numitems == 0) 67859243Sobrien return (0); 67959243Sobrien if (command == RECOGNIZE) { 68059243Sobrien if (looking_for_lognames) 68159243Sobrien copyn(word, STRtilde, 1); 68259243Sobrien else 68359243Sobrien /* put back dir part */ 68459243Sobrien copyn(word, dir, max_word_length); 68559243Sobrien /* add extended name */ 68659243Sobrien catn(word, extended_name, max_word_length); 68759243Sobrien return (numitems); 68859243Sobrien } 68959243Sobrien else { /* LIST */ 69059243Sobrien qsort((ptr_t) items, (size_t) numitems, sizeof(items[0]), sortscmp); 69159243Sobrien print_by_column(looking_for_lognames ? NULL : tilded_dir, 69259243Sobrien items, numitems); 69359243Sobrien if (items != NULL) 69459243Sobrien FREE_ITEMS(items); 69559243Sobrien } 69659243Sobrien return (0); 69759243Sobrien} 69859243Sobrien 69959243Sobrien/* 70059243Sobrien * Object: extend what user typed up to an ambiguity. 70159243Sobrien * Algorithm: 70259243Sobrien * On first match, copy full item (assume it'll be the only match) 70359243Sobrien * On subsequent matches, shorten extended_name to the first 70459243Sobrien * Character mismatch between extended_name and item. 70559243Sobrien * If we shorten it back to the prefix length, stop searching. 70659243Sobrien */ 70759243Sobrienstatic int 70859243Sobrienrecognize(extended_name, item, name_length, numitems) 70959243Sobrien Char *extended_name, *item; 71059243Sobrien int name_length, numitems; 71159243Sobrien{ 71259243Sobrien if (numitems == 1) /* 1st match */ 71359243Sobrien copyn(extended_name, item, MAXNAMLEN); 71459243Sobrien else { /* 2nd & subsequent matches */ 71559243Sobrien register Char *x, *ent; 71659243Sobrien register int len = 0; 71759243Sobrien 71859243Sobrien x = extended_name; 71959243Sobrien for (ent = item; *x && *x == *ent++; x++, len++); 72059243Sobrien *x = '\0'; /* Shorten at 1st Char diff */ 72159243Sobrien if (len == name_length) /* Ambiguous to prefix? */ 72259243Sobrien return (-1); /* So stop now and save time */ 72359243Sobrien } 72459243Sobrien return (0); 72559243Sobrien} 72659243Sobrien 72759243Sobrien/* 72859243Sobrien * Return true if check matches initial Chars in template. 72959243Sobrien * This differs from PWB imatch in that if check is null 73059243Sobrien * it matches anything. 73159243Sobrien */ 73259243Sobrienstatic int 73359243Sobrienis_prefix(check, template) 73459243Sobrien register Char *check, *template; 73559243Sobrien{ 73659243Sobrien do 73759243Sobrien if (*check == 0) 73859243Sobrien return (TRUE); 73959243Sobrien while (*check++ == *template++); 74059243Sobrien return (FALSE); 74159243Sobrien} 74259243Sobrien 74359243Sobrien/* 74459243Sobrien * Return true if the Chars in template appear at the 74559243Sobrien * end of check, I.e., are it's suffix. 74659243Sobrien */ 74759243Sobrienstatic int 74859243Sobrienis_suffix(check, template) 74959243Sobrien Char *check, *template; 75059243Sobrien{ 75159243Sobrien register Char *c, *t; 75259243Sobrien 75359243Sobrien for (c = check; *c++;); 75459243Sobrien for (t = template; *t++;); 75559243Sobrien for (;;) { 75659243Sobrien if (t == template) 75759243Sobrien return 1; 75859243Sobrien if (c == check || *--t != *--c) 75959243Sobrien return 0; 76059243Sobrien } 76159243Sobrien} 76259243Sobrien 76359243Sobrienint 76459243Sobrientenex(inputline, inputline_size) 76559243Sobrien Char *inputline; 76659243Sobrien int inputline_size; 76759243Sobrien{ 76859243Sobrien register int numitems, num_read; 76959243Sobrien char tinputline[BUFSIZE]; 77059243Sobrien 77159243Sobrien 77259243Sobrien setup_tty(ON); 77359243Sobrien 77459243Sobrien while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) { 77559243Sobrien int i; 77659243Sobrien static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', 77759243Sobrien '>', '(', ')', '|', '^', '%', '\0'}; 77859243Sobrien register Char *str_end, *word_start, last_Char, should_retype; 77959243Sobrien register int space_left; 78059243Sobrien COMMAND command; 78159243Sobrien 78259243Sobrien for (i = 0; i < num_read; i++) 78359243Sobrien inputline[i] = (unsigned char) tinputline[i]; 78459243Sobrien last_Char = inputline[num_read - 1] & ASCII; 78559243Sobrien 78659243Sobrien if (last_Char == '\n' || num_read == inputline_size) 78759243Sobrien break; 78859243Sobrien command = (last_Char == ESC) ? RECOGNIZE : LIST; 78959243Sobrien if (command == LIST) 79059243Sobrien xputchar('\n'); 79159243Sobrien str_end = &inputline[num_read]; 79259243Sobrien if (last_Char == ESC) 79359243Sobrien --str_end; /* wipeout trailing cmd Char */ 79459243Sobrien *str_end = '\0'; 79559243Sobrien /* 79659243Sobrien * Find LAST occurence of a delimiter in the inputline. The word start 79759243Sobrien * is one Character past it. 79859243Sobrien */ 79959243Sobrien for (word_start = str_end; word_start > inputline; --word_start) 80059243Sobrien if (Strchr(delims, word_start[-1])) 80159243Sobrien break; 80259243Sobrien space_left = inputline_size - (word_start - inputline) - 1; 80359243Sobrien numitems = tsearch(word_start, command, space_left); 80459243Sobrien 80559243Sobrien if (command == RECOGNIZE) { 80659243Sobrien /* print from str_end on */ 80759243Sobrien print_recognized_stuff(str_end); 80859243Sobrien if (numitems != 1) /* Beep = No match/ambiguous */ 80959243Sobrien beep(); 81059243Sobrien } 81159243Sobrien 81259243Sobrien /* 81359243Sobrien * Tabs in the input line cause trouble after a pushback. tty driver 81459243Sobrien * won't backspace over them because column positions are now 81559243Sobrien * incorrect. This is solved by retyping over current line. 81659243Sobrien */ 81759243Sobrien should_retype = FALSE; 81859243Sobrien if (Strchr(inputline, '\t')) { /* tab Char in input line? */ 81959243Sobrien back_to_col_1(); 82059243Sobrien should_retype = TRUE; 82159243Sobrien } 82259243Sobrien if (command == LIST) /* Always retype after a LIST */ 82359243Sobrien should_retype = TRUE; 82459243Sobrien if (should_retype) 82559243Sobrien printprompt(0, NULL); 82659243Sobrien pushback(inputline); 82759243Sobrien if (should_retype) 82859243Sobrien retype(); 82959243Sobrien } 83059243Sobrien setup_tty(OFF); 83159243Sobrien return (num_read); 83259243Sobrien} 83359243Sobrien 83459243Sobrienstatic int 83559243Sobrienignored(item) 83659243Sobrien register Char *item; 83759243Sobrien{ 83859243Sobrien struct varent *vp; 83959243Sobrien register Char **cp; 84059243Sobrien 84159243Sobrien if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 84259243Sobrien return (FALSE); 84359243Sobrien for (; *cp != NULL; cp++) 84459243Sobrien if (is_suffix(item, *cp)) 84559243Sobrien return (TRUE); 84659243Sobrien return (FALSE); 84759243Sobrien} 84859243Sobrien#endif /* FILEC */ 849