sh.file.c revision 100616
1100616Smp/* $Header: /src/pub/tcsh/sh.file.c,v 3.22 2002/07/01 20:53:00 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 36100616SmpRCSID("$Id: sh.file.c,v 3.22 2002/07/01 20:53:00 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 6259243Sobrienstatic void setup_tty __P((int)); 6359243Sobrienstatic void back_to_col_1 __P((void)); 6459243Sobrienstatic void pushback __P((Char *)); 6559243Sobrienstatic void catn __P((Char *, Char *, int)); 6659243Sobrienstatic void copyn __P((Char *, Char *, int)); 6759243Sobrienstatic Char filetype __P((Char *, Char *)); 6859243Sobrienstatic void print_by_column __P((Char *, Char *[], int)); 6959243Sobrienstatic Char *tilde __P((Char *, Char *)); 7059243Sobrienstatic void retype __P((void)); 7159243Sobrienstatic void beep __P((void)); 7259243Sobrienstatic void print_recognized_stuff __P((Char *)); 7359243Sobrienstatic void extract_dir_and_name __P((Char *, Char *, Char *)); 7459243Sobrienstatic Char *getitem __P((DIR *, int)); 7559243Sobrienstatic void free_items __P((Char **)); 7659243Sobrienstatic int tsearch __P((Char *, COMMAND, int)); 77100616Smpstatic int compare __P((const ptr_t, const ptr_t)); 7859243Sobrienstatic int recognize __P((Char *, Char *, int, int)); 7959243Sobrienstatic int is_prefix __P((Char *, Char *)); 8059243Sobrienstatic int is_suffix __P((Char *, Char *)); 8159243Sobrienstatic int ignored __P((Char *)); 8259243Sobrien 8359243Sobrien 8459243Sobrien/* 8559243Sobrien * Put this here so the binary can be patched with adb to enable file 8659243Sobrien * completion by default. Filec controls completion, nobeep controls 8759243Sobrien * ringing the terminal bell on incomplete expansions. 8859243Sobrien */ 8959243Sobrienbool filec = 0; 9059243Sobrien 9159243Sobrienstatic void 9259243Sobriensetup_tty(on) 9359243Sobrien int on; 9459243Sobrien{ 9559243Sobrien#ifdef TERMIO 9659243Sobrien# ifdef POSIX 9759243Sobrien struct termios tchars; 9859243Sobrien# else 9959243Sobrien struct termio tchars; 10059243Sobrien# endif /* POSIX */ 10159243Sobrien 10259243Sobrien# ifdef POSIX 10359243Sobrien (void) tcgetattr(SHIN, &tchars); 10459243Sobrien# else 10559243Sobrien (void) ioctl(SHIN, TCGETA, (ioctl_t) &tchars); 10659243Sobrien# endif /* POSIX */ 10759243Sobrien if (on) { 10859243Sobrien tchars.c_cc[VEOL] = ESC; 10959243Sobrien if (tchars.c_lflag & ICANON) 11059243Sobrien# ifdef POSIX 11159243Sobrien on = TCSADRAIN; 11259243Sobrien# else 11359243Sobrien on = TCSETA; 11459243Sobrien# endif /* POSIX */ 11559243Sobrien else { 11659243Sobrien# ifdef POSIX 11759243Sobrien on = TCSAFLUSH; 11859243Sobrien# else 11959243Sobrien on = TCSETAF; 12059243Sobrien# endif /* POSIX */ 12159243Sobrien tchars.c_lflag |= ICANON; 12259243Sobrien 12359243Sobrien } 12459243Sobrien } 12559243Sobrien else { 12659243Sobrien tchars.c_cc[VEOL] = _POSIX_VDISABLE; 12759243Sobrien# ifdef POSIX 12859243Sobrien on = TCSADRAIN; 12959243Sobrien# else 13059243Sobrien on = TCSETA; 13159243Sobrien# endif /* POSIX */ 13259243Sobrien } 13359243Sobrien# ifdef POSIX 13459243Sobrien (void) tcsetattr(SHIN, on, &tchars); 13559243Sobrien# else 13659243Sobrien (void) ioctl(SHIN, on, (ioctl_t) &tchars); 13759243Sobrien# endif /* POSIX */ 13859243Sobrien#else 13959243Sobrien struct sgttyb sgtty; 14059243Sobrien static struct tchars tchars;/* INT, QUIT, XON, XOFF, EOF, BRK */ 14159243Sobrien 14259243Sobrien if (on) { 14359243Sobrien (void) ioctl(SHIN, TIOCGETC, (ioctl_t) & tchars); 14459243Sobrien tchars.t_brkc = ESC; 14559243Sobrien (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 14659243Sobrien /* 14759243Sobrien * This must be done after every command: if the tty gets into raw or 14859243Sobrien * cbreak mode the user can't even type 'reset'. 14959243Sobrien */ 15059243Sobrien (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & sgtty); 15159243Sobrien if (sgtty.sg_flags & (RAW | CBREAK)) { 15259243Sobrien sgtty.sg_flags &= ~(RAW | CBREAK); 15359243Sobrien (void) ioctl(SHIN, TIOCSETP, (ioctl_t) & sgtty); 15459243Sobrien } 15559243Sobrien } 15659243Sobrien else { 15759243Sobrien tchars.t_brkc = -1; 15859243Sobrien (void) ioctl(SHIN, TIOCSETC, (ioctl_t) & tchars); 15959243Sobrien } 16059243Sobrien#endif /* TERMIO */ 16159243Sobrien} 16259243Sobrien 16359243Sobrien/* 16459243Sobrien * Move back to beginning of current line 16559243Sobrien */ 16659243Sobrienstatic void 16759243Sobrienback_to_col_1() 16859243Sobrien{ 16959243Sobrien#ifdef TERMIO 17059243Sobrien# ifdef POSIX 17159243Sobrien struct termios tty, tty_normal; 17259243Sobrien# else 17359243Sobrien struct termio tty, tty_normal; 17459243Sobrien# endif /* POSIX */ 17559243Sobrien#else 17659243Sobrien struct sgttyb tty, tty_normal; 17759243Sobrien#endif /* TERMIO */ 17859243Sobrien 17959243Sobrien# ifdef BSDSIGS 18059243Sobrien sigmask_t omask = sigblock(sigmask(SIGINT)); 18159243Sobrien# else 18259243Sobrien (void) sighold(SIGINT); 18359243Sobrien# endif /* BSDSIGS */ 18459243Sobrien 18559243Sobrien#ifdef TERMIO 18659243Sobrien# ifdef POSIX 18759243Sobrien (void) tcgetattr(SHOUT, &tty); 18859243Sobrien# else 18959243Sobrien (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty_normal); 19059243Sobrien# endif /* POSIX */ 19159243Sobrien tty_normal = tty; 19259243Sobrien tty.c_iflag &= ~INLCR; 19359243Sobrien tty.c_oflag &= ~ONLCR; 19459243Sobrien# ifdef POSIX 19559243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty); 19659243Sobrien# else 19759243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 19859243Sobrien# endif /* POSIX */ 19959243Sobrien (void) write(SHOUT, "\r", 1); 20059243Sobrien# ifdef POSIX 20159243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); 20259243Sobrien# else 20359243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 20459243Sobrien# endif /* POSIX */ 20559243Sobrien#else 20659243Sobrien (void) ioctl(SHIN, TIOCGETP, (ioctl_t) & tty); 20759243Sobrien tty_normal = tty; 20859243Sobrien tty.sg_flags &= ~CRMOD; 20959243Sobrien (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty); 21059243Sobrien (void) write(SHOUT, "\r", 1); 21159243Sobrien (void) ioctl(SHIN, TIOCSETN, (ioctl_t) & tty_normal); 21259243Sobrien#endif /* TERMIO */ 21359243Sobrien 21459243Sobrien# ifdef BSDSIGS 21559243Sobrien (void) sigsetmask(omask); 21659243Sobrien# else 21759243Sobrien (void) sigrelse(SIGINT); 21859243Sobrien# endif /* BSDISGS */ 21959243Sobrien} 22059243Sobrien 22159243Sobrien/* 22259243Sobrien * Push string contents back into tty queue 22359243Sobrien */ 22459243Sobrienstatic void 22559243Sobrienpushback(string) 22659243Sobrien Char *string; 22759243Sobrien{ 228100616Smp Char *p; 22959243Sobrien char c; 23059243Sobrien#ifdef TERMIO 23159243Sobrien# ifdef POSIX 23259243Sobrien struct termios tty, tty_normal; 23359243Sobrien# else 23459243Sobrien struct termio tty, tty_normal; 23559243Sobrien# endif /* POSIX */ 23659243Sobrien#else 23759243Sobrien struct sgttyb tty, tty_normal; 23859243Sobrien#endif /* TERMIO */ 23959243Sobrien 24059243Sobrien#ifdef BSDSIGS 24159243Sobrien sigmask_t omask = sigblock(sigmask(SIGINT)); 24259243Sobrien#else 24359243Sobrien (void) sighold(SIGINT); 24459243Sobrien#endif /* BSDSIGS */ 24559243Sobrien 24659243Sobrien#ifdef TERMIO 24759243Sobrien# ifdef POSIX 24859243Sobrien (void) tcgetattr(SHOUT, &tty); 24959243Sobrien# else 25059243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 25159243Sobrien# endif /* POSIX */ 25259243Sobrien tty_normal = tty; 25359243Sobrien tty.c_lflag &= ~(ECHOKE | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOCTL); 25459243Sobrien# ifdef POSIX 25559243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty); 25659243Sobrien# else 25759243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 25859243Sobrien# endif /* POSIX */ 25959243Sobrien 260100616Smp for (p = string; (c = *p) != '\0'; p++) 26159243Sobrien (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 26259243Sobrien# ifdef POSIX 26359243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty_normal); 26459243Sobrien# else 26559243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty_normal); 26659243Sobrien# endif /* POSIX */ 26759243Sobrien#else 26859243Sobrien (void) ioctl(SHOUT, TIOCGETP, (ioctl_t) & tty); 26959243Sobrien tty_normal = tty; 27059243Sobrien tty.sg_flags &= ~ECHO; 27159243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty); 27259243Sobrien 27359243Sobrien for (p = string; c = *p; p++) 27459243Sobrien (void) ioctl(SHOUT, TIOCSTI, (ioctl_t) & c); 27559243Sobrien (void) ioctl(SHOUT, TIOCSETN, (ioctl_t) & tty_normal); 27659243Sobrien#endif /* TERMIO */ 27759243Sobrien 27859243Sobrien# ifdef BSDSIGS 27959243Sobrien (void) sigsetmask(omask); 28059243Sobrien# else 28159243Sobrien (void) sigrelse(SIGINT); 28259243Sobrien# endif /* BSDISGS */ 28359243Sobrien} 28459243Sobrien 28559243Sobrien/* 28659243Sobrien * Concatenate src onto tail of des. 28759243Sobrien * Des is a string whose maximum length is count. 28859243Sobrien * Always null terminate. 28959243Sobrien */ 29059243Sobrienstatic void 29159243Sobriencatn(des, src, count) 292100616Smp Char *des, *src; 293100616Smp int count; 29459243Sobrien{ 29559243Sobrien while (--count >= 0 && *des) 29659243Sobrien des++; 29759243Sobrien while (--count >= 0) 29859243Sobrien if ((*des++ = *src++) == 0) 29959243Sobrien return; 30059243Sobrien *des = '\0'; 30159243Sobrien} 30259243Sobrien 30359243Sobrien/* 30459243Sobrien * Like strncpy but always leave room for trailing \0 30559243Sobrien * and always null terminate. 30659243Sobrien */ 30759243Sobrienstatic void 30859243Sobriencopyn(des, src, count) 309100616Smp Char *des, *src; 310100616Smp int count; 31159243Sobrien{ 31259243Sobrien while (--count >= 0) 31359243Sobrien if ((*des++ = *src++) == 0) 31459243Sobrien return; 31559243Sobrien *des = '\0'; 31659243Sobrien} 31759243Sobrien 31859243Sobrienstatic Char 31959243Sobrienfiletype(dir, file) 32059243Sobrien Char *dir, *file; 32159243Sobrien{ 32259243Sobrien Char path[MAXPATHLEN]; 32359243Sobrien struct stat statb; 32459243Sobrien 32559243Sobrien catn(Strcpy(path, dir), file, sizeof(path) / sizeof(Char)); 32659243Sobrien if (lstat(short2str(path), &statb) == 0) { 32759243Sobrien switch (statb.st_mode & S_IFMT) { 32859243Sobrien case S_IFDIR: 32959243Sobrien return ('/'); 33059243Sobrien 33159243Sobrien case S_IFLNK: 33259243Sobrien if (stat(short2str(path), &statb) == 0 && /* follow it out */ 33359243Sobrien S_ISDIR(statb.st_mode)) 33459243Sobrien return ('>'); 33559243Sobrien else 33659243Sobrien return ('@'); 33759243Sobrien 33859243Sobrien case S_IFSOCK: 33959243Sobrien return ('='); 34059243Sobrien 34159243Sobrien default: 34259243Sobrien if (statb.st_mode & 0111) 34359243Sobrien return ('*'); 34459243Sobrien } 34559243Sobrien } 34659243Sobrien return (' '); 34759243Sobrien} 34859243Sobrien 34959243Sobrienstatic struct winsize win; 35059243Sobrien 35159243Sobrien/* 35259243Sobrien * Print sorted down columns 35359243Sobrien */ 35459243Sobrienstatic void 35559243Sobrienprint_by_column(dir, items, count) 35659243Sobrien Char *dir, *items[]; 35759243Sobrien int count; 35859243Sobrien{ 359100616Smp int i, rows, r, c, maxwidth = 0, columns; 36059243Sobrien 36159243Sobrien if (ioctl(SHOUT, TIOCGWINSZ, (ioctl_t) & win) < 0 || win.ws_col == 0) 36259243Sobrien win.ws_col = 80; 36359243Sobrien for (i = 0; i < count; i++) 36459243Sobrien maxwidth = maxwidth > (r = Strlen(items[i])) ? maxwidth : r; 36559243Sobrien maxwidth += 2; /* for the file tag and space */ 36659243Sobrien columns = win.ws_col / maxwidth; 36759243Sobrien if (columns == 0) 36859243Sobrien columns = 1; 36959243Sobrien rows = (count + (columns - 1)) / columns; 37059243Sobrien for (r = 0; r < rows; r++) { 37159243Sobrien for (c = 0; c < columns; c++) { 37259243Sobrien i = c * rows + r; 37359243Sobrien if (i < count) { 374100616Smp int w; 37559243Sobrien 37659243Sobrien xprintf("%S", items[i]); 37759243Sobrien xputchar(dir ? filetype(dir, items[i]) : ' '); 37859243Sobrien if (c < columns - 1) { /* last column? */ 37959243Sobrien w = Strlen(items[i]) + 1; 38059243Sobrien for (; w < maxwidth; w++) 38159243Sobrien xputchar(' '); 38259243Sobrien } 38359243Sobrien } 38459243Sobrien } 38559243Sobrien xputchar('\r'); 38659243Sobrien xputchar('\n'); 38759243Sobrien } 38859243Sobrien} 38959243Sobrien 39059243Sobrien/* 39159243Sobrien * Expand file name with possible tilde usage 39259243Sobrien * ~person/mumble 39359243Sobrien * expands to 39459243Sobrien * home_directory_of_person/mumble 39559243Sobrien */ 39659243Sobrienstatic Char * 39759243Sobrientilde(new, old) 39859243Sobrien Char *new, *old; 39959243Sobrien{ 400100616Smp Char *o, *p; 401100616Smp struct passwd *pw; 40259243Sobrien static Char person[40]; 40359243Sobrien 40459243Sobrien if (old[0] != '~') 40559243Sobrien return (Strcpy(new, old)); 40659243Sobrien 40759243Sobrien for (p = person, o = &old[1]; *o && *o != '/'; *p++ = *o++); 40859243Sobrien *p = '\0'; 40959243Sobrien if (person[0] == '\0') 41059243Sobrien (void) Strcpy(new, varval(STRhome)); 41159243Sobrien else { 41259243Sobrien pw = getpwnam(short2str(person)); 41359243Sobrien if (pw == NULL) 41459243Sobrien return (NULL); 41559243Sobrien (void) Strcpy(new, str2short(pw->pw_dir)); 41659243Sobrien } 41759243Sobrien (void) Strcat(new, o); 41859243Sobrien return (new); 41959243Sobrien} 42059243Sobrien 42159243Sobrien/* 42259243Sobrien * Cause pending line to be printed 42359243Sobrien */ 42459243Sobrienstatic void 42559243Sobrienretype() 42659243Sobrien{ 42759243Sobrien#ifdef TERMIO 42859243Sobrien# ifdef POSIX 42959243Sobrien struct termios tty; 43059243Sobrien 43159243Sobrien (void) tcgetattr(SHOUT, &tty); 43259243Sobrien# else 43359243Sobrien struct termio tty; 43459243Sobrien 43559243Sobrien (void) ioctl(SHOUT, TCGETA, (ioctl_t) &tty); 43659243Sobrien# endif /* POSIX */ 43759243Sobrien 43859243Sobrien tty.c_lflag |= PENDIN; 43959243Sobrien 44059243Sobrien# ifdef POSIX 44159243Sobrien (void) tcsetattr(SHOUT, TCSANOW, &tty); 44259243Sobrien# else 44359243Sobrien (void) ioctl(SHOUT, TCSETAW, (ioctl_t) &tty); 44459243Sobrien# endif /* POSIX */ 44559243Sobrien#else 44659243Sobrien int pending_input = LPENDIN; 44759243Sobrien 44859243Sobrien (void) ioctl(SHOUT, TIOCLBIS, (ioctl_t) & pending_input); 44959243Sobrien#endif /* TERMIO */ 45059243Sobrien} 45159243Sobrien 45259243Sobrienstatic void 45359243Sobrienbeep() 45459243Sobrien{ 45559243Sobrien if (adrof(STRnobeep) == 0) 45669408Sache#ifdef IS_ASCII 45759243Sobrien (void) write(SHOUT, "\007", 1); 45869408Sache#else 45959243Sobrien { 46059243Sobrien unsigned char beep_ch = CTL_ESC('\007'); 46159243Sobrien (void) write(SHOUT, &beep_ch, 1); 46259243Sobrien } 46369408Sache#endif 46459243Sobrien} 46559243Sobrien 46659243Sobrien/* 46759243Sobrien * Erase that silly ^[ and 46859243Sobrien * print the recognized part of the string 46959243Sobrien */ 47059243Sobrienstatic void 47159243Sobrienprint_recognized_stuff(recognized_part) 47259243Sobrien Char *recognized_part; 47359243Sobrien{ 47459243Sobrien /* An optimized erasing of that silly ^[ */ 47559243Sobrien (void) putraw('\b'); 47659243Sobrien (void) putraw('\b'); 47759243Sobrien switch (Strlen(recognized_part)) { 47859243Sobrien 47959243Sobrien case 0: /* erase two Characters: ^[ */ 48059243Sobrien (void) putraw(' '); 48159243Sobrien (void) putraw(' '); 48259243Sobrien (void) putraw('\b'); 48359243Sobrien (void) putraw('\b'); 48459243Sobrien break; 48559243Sobrien 48659243Sobrien case 1: /* overstrike the ^, erase the [ */ 48759243Sobrien xprintf("%S", recognized_part); 48859243Sobrien (void) putraw(' '); 48959243Sobrien (void) putraw('\b'); 49059243Sobrien break; 49159243Sobrien 49259243Sobrien default: /* overstrike both Characters ^[ */ 49359243Sobrien xprintf("%S", recognized_part); 49459243Sobrien break; 49559243Sobrien } 49659243Sobrien flush(); 49759243Sobrien} 49859243Sobrien 49959243Sobrien/* 50059243Sobrien * Parse full path in file into 2 parts: directory and file names 50159243Sobrien * Should leave final slash (/) at end of dir. 50259243Sobrien */ 50359243Sobrienstatic void 50459243Sobrienextract_dir_and_name(path, dir, name) 50559243Sobrien Char *path, *dir, *name; 50659243Sobrien{ 507100616Smp Char *p; 50859243Sobrien 50959243Sobrien p = Strrchr(path, '/'); 51059243Sobrien if (p == NULL) { 51159243Sobrien copyn(name, path, MAXNAMLEN); 51259243Sobrien dir[0] = '\0'; 51359243Sobrien } 51459243Sobrien else { 51559243Sobrien copyn(name, ++p, MAXNAMLEN); 51659243Sobrien copyn(dir, path, p - path); 51759243Sobrien } 51859243Sobrien} 51959243Sobrien/* atp vmsposix - I need to remove all the setpwent 52059243Sobrien * getpwent endpwent stuff. VMS_POSIX has getpwnam getpwuid 52159243Sobrien * and getlogin. This needs fixing. (There is no access to 52259243Sobrien * pw->passwd in VMS - a secure system benefit :-| ) 52359243Sobrien */ 52459243Sobrienstatic Char * 52559243Sobriengetitem(dir_fd, looking_for_lognames) 52659243Sobrien DIR *dir_fd; 52759243Sobrien int looking_for_lognames; 52859243Sobrien{ 529100616Smp struct passwd *pw; 530100616Smp struct dirent *dirp; 53159243Sobrien 53259243Sobrien if (looking_for_lognames) { 53359243Sobrien#ifdef _VMS_POSIX 53459243Sobrien return (NULL); 53559243Sobrien#else 53659243Sobrien if ((pw = getpwent()) == NULL) 53759243Sobrien return (NULL); 53859243Sobrien return (str2short(pw->pw_name)); 53959243Sobrien#endif /* atp vmsposix */ 54059243Sobrien } 541100616Smp if ((dirp = readdir(dir_fd)) != NULL) 54259243Sobrien return (str2short(dirp->d_name)); 54359243Sobrien return (NULL); 54459243Sobrien} 54559243Sobrien 54659243Sobrienstatic void 54759243Sobrienfree_items(items) 548100616Smp Char **items; 54959243Sobrien{ 550100616Smp int i; 55159243Sobrien 55259243Sobrien for (i = 0; items[i]; i++) 55359243Sobrien xfree((ptr_t) items[i]); 55459243Sobrien xfree((ptr_t) items); 55559243Sobrien} 55659243Sobrien 55759243Sobrien#ifdef BSDSIGS 55859243Sobrien# define FREE_ITEMS(items) { \ 55959243Sobrien sigmask_t omask;\ 56059243Sobrien\ 56159243Sobrien omask = sigblock(sigmask(SIGINT));\ 56259243Sobrien free_items(items);\ 56359243Sobrien items = NULL;\ 56459243Sobrien (void) sigsetmask(omask);\ 56559243Sobrien} 56659243Sobrien#else 56759243Sobrien# define FREE_ITEMS(items) { \ 56859243Sobrien (void) sighold(SIGINT);\ 56959243Sobrien free_items(items);\ 57059243Sobrien items = NULL;\ 57159243Sobrien (void) sigrelse(SIGINT);\ 57259243Sobrien} 57359243Sobrien#endif /* BSDSIGS */ 57459243Sobrien 57559243Sobrien/* 57659243Sobrien * Perform a RECOGNIZE or LIST command on string "word". 57759243Sobrien */ 57859243Sobrienstatic int 57959243Sobrientsearch(word, command, max_word_length) 58059243Sobrien Char *word; 58159243Sobrien int max_word_length; 58259243Sobrien COMMAND command; 58359243Sobrien{ 58459243Sobrien static Char **items = NULL; 585100616Smp DIR *dir_fd; 586100616Smp int numitems = 0, ignoring = TRUE, nignored = 0; 587100616Smp int name_length, looking_for_lognames; 58859243Sobrien Char tilded_dir[MAXPATHLEN + 1], dir[MAXPATHLEN + 1]; 58959243Sobrien Char name[MAXNAMLEN + 1], extended_name[MAXNAMLEN + 1]; 59059243Sobrien Char *item; 59159243Sobrien 59259243Sobrien#define MAXITEMS 1024 59359243Sobrien 59459243Sobrien if (items != NULL) 59559243Sobrien FREE_ITEMS(items); 59659243Sobrien 59759243Sobrien looking_for_lognames = (*word == '~') && (Strchr(word, '/') == NULL); 59859243Sobrien if (looking_for_lognames) { 59959243Sobrien#ifndef _VMS_POSIX 60059243Sobrien (void) setpwent(); 60159243Sobrien#endif /*atp vmsposix */ 60259243Sobrien copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ 60359243Sobrien dir_fd = NULL; 60459243Sobrien } 60559243Sobrien else { 60659243Sobrien extract_dir_and_name(word, dir, name); 60759243Sobrien if (tilde(tilded_dir, dir) == 0) 60859243Sobrien return (0); 60959243Sobrien dir_fd = opendir(*tilded_dir ? short2str(tilded_dir) : "."); 61059243Sobrien if (dir_fd == NULL) 61159243Sobrien return (0); 61259243Sobrien } 61359243Sobrien 61459243Sobrienagain: /* search for matches */ 61559243Sobrien name_length = Strlen(name); 616100616Smp for (numitems = 0; 617100616Smp (item = getitem(dir_fd, looking_for_lognames)) != NULL;) { 61859243Sobrien if (!is_prefix(name, item)) 61959243Sobrien continue; 62059243Sobrien /* Don't match . files on null prefix match */ 62159243Sobrien if (name_length == 0 && item[0] == '.' && 62259243Sobrien !looking_for_lognames) 62359243Sobrien continue; 62459243Sobrien if (command == LIST) { 62559243Sobrien if (numitems >= MAXITEMS) { 62659243Sobrien xprintf(CGETS(14, 1, "\nYikes!! Too many %s!!\n"), 62759243Sobrien looking_for_lognames ? 62859243Sobrien CGETS(14, 2, "names in password file") : 629100616Smp CGETS(14, 3, "files")); 63059243Sobrien break; 63159243Sobrien } 63259243Sobrien /* 63359243Sobrien * From Beto Appleton (beto@aixwiz.austin.ibm.com) 63459243Sobrien * typing "./control-d" will cause the csh to core-dump. 63559243Sobrien * the problem can be reproduce as following: 63659243Sobrien * 1. set ignoreeof 63759243Sobrien * 2. set filec 63859243Sobrien * 3. create a directory with 1050 files 63959243Sobrien * 4. typing "./control-d" will cause the csh to core-dump 64059243Sobrien * Solution: Add + 1 to MAXITEMS 64159243Sobrien */ 64259243Sobrien if (items == NULL) 64359243Sobrien items = (Char **) xcalloc(sizeof(items[0]), MAXITEMS + 1); 64459243Sobrien items[numitems] = (Char *) xmalloc((size_t) (Strlen(item) + 1) * 64559243Sobrien sizeof(Char)); 64659243Sobrien copyn(items[numitems], item, MAXNAMLEN); 64759243Sobrien numitems++; 64859243Sobrien } 64959243Sobrien else { /* RECOGNIZE command */ 65059243Sobrien if (ignoring && ignored(item)) 65159243Sobrien nignored++; 65259243Sobrien else if (recognize(extended_name, 65359243Sobrien item, name_length, ++numitems)) 65459243Sobrien break; 65559243Sobrien } 65659243Sobrien } 65759243Sobrien if (ignoring && numitems == 0 && nignored > 0) { 65859243Sobrien ignoring = FALSE; 65959243Sobrien nignored = 0; 66059243Sobrien if (looking_for_lognames) 66159243Sobrien#ifndef _VMS_POSIX 66259243Sobrien (void) setpwent(); 66359243Sobrien#endif /* atp vmsposix */ 66459243Sobrien else 66559243Sobrien rewinddir(dir_fd); 66659243Sobrien goto again; 66759243Sobrien } 66859243Sobrien 66959243Sobrien if (looking_for_lognames) 67059243Sobrien#ifndef _VMS_POSIX 67159243Sobrien (void) endpwent(); 67259243Sobrien#endif /*atp vmsposix */ 67359243Sobrien else 67459243Sobrien (void) closedir(dir_fd); 67559243Sobrien if (numitems == 0) 67659243Sobrien return (0); 67759243Sobrien if (command == RECOGNIZE) { 67859243Sobrien if (looking_for_lognames) 67959243Sobrien copyn(word, STRtilde, 1); 68059243Sobrien else 68159243Sobrien /* put back dir part */ 68259243Sobrien copyn(word, dir, max_word_length); 68359243Sobrien /* add extended name */ 68459243Sobrien catn(word, extended_name, max_word_length); 68559243Sobrien return (numitems); 68659243Sobrien } 68759243Sobrien else { /* LIST */ 688100616Smp qsort((ptr_t) items, (size_t) numitems, sizeof(items[0]), 689100616Smp (int (*) __P((const void *, const void *))) compare); 69059243Sobrien print_by_column(looking_for_lognames ? NULL : tilded_dir, 69159243Sobrien items, numitems); 69259243Sobrien if (items != NULL) 69359243Sobrien FREE_ITEMS(items); 69459243Sobrien } 69559243Sobrien return (0); 69659243Sobrien} 69759243Sobrien 698100616Smp 699100616Smpstatic int 700100616Smpcompare(p, q) 701100616Smp const ptr_t p, q; 702100616Smp{ 703100616Smp#if defined(NLS) && !defined(NOSTRCOLL) 704100616Smp errno = 0; /* strcoll sets errno, another brain-damage */ 705100616Smp 706100616Smp return (strcoll(*(char **) p, *(char **) q)); 707100616Smp#else 708100616Smp return (strcmp(*(char **) p, *(char **) q)); 709100616Smp#endif /* NLS && !NOSTRCOLL */ 710100616Smp} 711100616Smp 71259243Sobrien/* 71359243Sobrien * Object: extend what user typed up to an ambiguity. 71459243Sobrien * Algorithm: 71559243Sobrien * On first match, copy full item (assume it'll be the only match) 71659243Sobrien * On subsequent matches, shorten extended_name to the first 71759243Sobrien * Character mismatch between extended_name and item. 71859243Sobrien * If we shorten it back to the prefix length, stop searching. 71959243Sobrien */ 72059243Sobrienstatic int 72159243Sobrienrecognize(extended_name, item, name_length, numitems) 72259243Sobrien Char *extended_name, *item; 72359243Sobrien int name_length, numitems; 72459243Sobrien{ 72559243Sobrien if (numitems == 1) /* 1st match */ 72659243Sobrien copyn(extended_name, item, MAXNAMLEN); 72759243Sobrien else { /* 2nd & subsequent matches */ 728100616Smp Char *x, *ent; 729100616Smp int len = 0; 73059243Sobrien 73159243Sobrien x = extended_name; 73259243Sobrien for (ent = item; *x && *x == *ent++; x++, len++); 73359243Sobrien *x = '\0'; /* Shorten at 1st Char diff */ 73459243Sobrien if (len == name_length) /* Ambiguous to prefix? */ 73559243Sobrien return (-1); /* So stop now and save time */ 73659243Sobrien } 73759243Sobrien return (0); 73859243Sobrien} 73959243Sobrien 74059243Sobrien/* 74159243Sobrien * Return true if check matches initial Chars in template. 74259243Sobrien * This differs from PWB imatch in that if check is null 74359243Sobrien * it matches anything. 74459243Sobrien */ 74559243Sobrienstatic int 74659243Sobrienis_prefix(check, template) 747100616Smp Char *check, *template; 74859243Sobrien{ 74959243Sobrien do 75059243Sobrien if (*check == 0) 75159243Sobrien return (TRUE); 75259243Sobrien while (*check++ == *template++); 75359243Sobrien return (FALSE); 75459243Sobrien} 75559243Sobrien 75659243Sobrien/* 75759243Sobrien * Return true if the Chars in template appear at the 75859243Sobrien * end of check, I.e., are it's suffix. 75959243Sobrien */ 76059243Sobrienstatic int 76159243Sobrienis_suffix(check, template) 76259243Sobrien Char *check, *template; 76359243Sobrien{ 764100616Smp Char *c, *t; 76559243Sobrien 76659243Sobrien for (c = check; *c++;); 76759243Sobrien for (t = template; *t++;); 76859243Sobrien for (;;) { 76959243Sobrien if (t == template) 77059243Sobrien return 1; 77159243Sobrien if (c == check || *--t != *--c) 77259243Sobrien return 0; 77359243Sobrien } 77459243Sobrien} 77559243Sobrien 77659243Sobrienint 77759243Sobrientenex(inputline, inputline_size) 77859243Sobrien Char *inputline; 77959243Sobrien int inputline_size; 78059243Sobrien{ 781100616Smp int numitems, num_read; 78259243Sobrien char tinputline[BUFSIZE]; 78359243Sobrien 78459243Sobrien 78559243Sobrien setup_tty(ON); 78659243Sobrien 78759243Sobrien while ((num_read = read(SHIN, tinputline, BUFSIZE)) > 0) { 78859243Sobrien int i; 78959243Sobrien static Char delims[] = {' ', '\'', '"', '\t', ';', '&', '<', 79059243Sobrien '>', '(', ')', '|', '^', '%', '\0'}; 791100616Smp Char *str_end, *word_start, last_Char, should_retype; 792100616Smp int space_left; 79359243Sobrien COMMAND command; 79459243Sobrien 79559243Sobrien for (i = 0; i < num_read; i++) 79659243Sobrien inputline[i] = (unsigned char) tinputline[i]; 79759243Sobrien last_Char = inputline[num_read - 1] & ASCII; 79859243Sobrien 79959243Sobrien if (last_Char == '\n' || num_read == inputline_size) 80059243Sobrien break; 80159243Sobrien command = (last_Char == ESC) ? RECOGNIZE : LIST; 80259243Sobrien if (command == LIST) 80359243Sobrien xputchar('\n'); 80459243Sobrien str_end = &inputline[num_read]; 80559243Sobrien if (last_Char == ESC) 80659243Sobrien --str_end; /* wipeout trailing cmd Char */ 80759243Sobrien *str_end = '\0'; 80859243Sobrien /* 80959243Sobrien * Find LAST occurence of a delimiter in the inputline. The word start 81059243Sobrien * is one Character past it. 81159243Sobrien */ 81259243Sobrien for (word_start = str_end; word_start > inputline; --word_start) 81359243Sobrien if (Strchr(delims, word_start[-1])) 81459243Sobrien break; 81559243Sobrien space_left = inputline_size - (word_start - inputline) - 1; 81659243Sobrien numitems = tsearch(word_start, command, space_left); 81759243Sobrien 81859243Sobrien if (command == RECOGNIZE) { 81959243Sobrien /* print from str_end on */ 82059243Sobrien print_recognized_stuff(str_end); 82159243Sobrien if (numitems != 1) /* Beep = No match/ambiguous */ 82259243Sobrien beep(); 82359243Sobrien } 82459243Sobrien 82559243Sobrien /* 82659243Sobrien * Tabs in the input line cause trouble after a pushback. tty driver 82759243Sobrien * won't backspace over them because column positions are now 82859243Sobrien * incorrect. This is solved by retyping over current line. 82959243Sobrien */ 83059243Sobrien should_retype = FALSE; 83159243Sobrien if (Strchr(inputline, '\t')) { /* tab Char in input line? */ 83259243Sobrien back_to_col_1(); 83359243Sobrien should_retype = TRUE; 83459243Sobrien } 83559243Sobrien if (command == LIST) /* Always retype after a LIST */ 83659243Sobrien should_retype = TRUE; 83759243Sobrien if (should_retype) 83859243Sobrien printprompt(0, NULL); 83959243Sobrien pushback(inputline); 84059243Sobrien if (should_retype) 84159243Sobrien retype(); 84259243Sobrien } 84359243Sobrien setup_tty(OFF); 84459243Sobrien return (num_read); 84559243Sobrien} 84659243Sobrien 84759243Sobrienstatic int 84859243Sobrienignored(item) 849100616Smp Char *item; 85059243Sobrien{ 85159243Sobrien struct varent *vp; 852100616Smp Char **cp; 85359243Sobrien 85459243Sobrien if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 85559243Sobrien return (FALSE); 85659243Sobrien for (; *cp != NULL; cp++) 85759243Sobrien if (is_suffix(item, *cp)) 85859243Sobrien return (TRUE); 85959243Sobrien return (FALSE); 86059243Sobrien} 861100616Smp#endif /* FILEC && TIOCSTI */ 862