tw.parse.c revision 59243
159243Sobrien/* $Header: /src/pub/tcsh/tw.parse.c,v 3.86 1998/10/25 15:10:52 christos Exp $ */ 259243Sobrien/* 359243Sobrien * tw.parse.c: Everyone has taken a shot in this futile effort to 459243Sobrien * lexically analyze a csh line... Well we cannot good 559243Sobrien * a job as good as sh.lex.c; but we try. Amazing that 659243Sobrien * it works considering how many hands have touched this code 759243Sobrien */ 859243Sobrien/*- 959243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 1059243Sobrien * All rights reserved. 1159243Sobrien * 1259243Sobrien * Redistribution and use in source and binary forms, with or without 1359243Sobrien * modification, are permitted provided that the following conditions 1459243Sobrien * are met: 1559243Sobrien * 1. Redistributions of source code must retain the above copyright 1659243Sobrien * notice, this list of conditions and the following disclaimer. 1759243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1859243Sobrien * notice, this list of conditions and the following disclaimer in the 1959243Sobrien * documentation and/or other materials provided with the distribution. 2059243Sobrien * 3. All advertising materials mentioning features or use of this software 2159243Sobrien * must display the following acknowledgement: 2259243Sobrien * This product includes software developed by the University of 2359243Sobrien * California, Berkeley and its contributors. 2459243Sobrien * 4. Neither the name of the University nor the names of its contributors 2559243Sobrien * may be used to endorse or promote products derived from this software 2659243Sobrien * without specific prior written permission. 2759243Sobrien * 2859243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2959243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3059243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3159243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3259243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3359243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3459243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3559243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3659243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3759243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3859243Sobrien * SUCH DAMAGE. 3959243Sobrien */ 4059243Sobrien#include "sh.h" 4159243Sobrien 4259243SobrienRCSID("$Id: tw.parse.c,v 3.86 1998/10/25 15:10:52 christos Exp $") 4359243Sobrien 4459243Sobrien#include "tw.h" 4559243Sobrien#include "ed.h" 4659243Sobrien#include "tc.h" 4759243Sobrien 4859243Sobrien#ifdef WINNT 4959243Sobrien#include "nt.const.h" 5059243Sobrien#endif /* WINNT */ 5159243Sobrien#define EVEN(x) (((x) & 1) != 1) 5259243Sobrien 5359243Sobrien#define DOT_NONE 0 /* Don't display dot files */ 5459243Sobrien#define DOT_NOT 1 /* Don't display dot or dot-dot */ 5559243Sobrien#define DOT_ALL 2 /* Display all dot files */ 5659243Sobrien 5759243Sobrien/* TW_NONE, TW_COMMAND, TW_VARIABLE, TW_LOGNAME, */ 5859243Sobrien/* TW_FILE, TW_DIRECTORY, TW_VARLIST, TW_USER, */ 5959243Sobrien/* TW_COMPLETION, TW_ALIAS, TW_SHELLVAR, TW_ENVVAR, */ 6059243Sobrien/* TW_BINDING, TW_WORDLIST, TW_LIMIT, TW_SIGNAL */ 6159243Sobrien/* TW_JOB, TW_EXPLAIN, TW_TEXT, TW_GRPNAME */ 6259243Sobrienstatic void (*tw_start_entry[]) __P((DIR *, Char *)) = { 6359243Sobrien tw_file_start, tw_cmd_start, tw_var_start, tw_logname_start, 6459243Sobrien tw_file_start, tw_file_start, tw_vl_start, tw_logname_start, 6559243Sobrien tw_complete_start, tw_alias_start, tw_var_start, tw_var_start, 6659243Sobrien tw_bind_start, tw_wl_start, tw_limit_start, tw_sig_start, 6759243Sobrien tw_job_start, tw_file_start, tw_file_start, tw_grpname_start 6859243Sobrien}; 6959243Sobrien 7059243Sobrienstatic Char * (*tw_next_entry[]) __P((Char *, int *)) = { 7159243Sobrien tw_file_next, tw_cmd_next, tw_var_next, tw_logname_next, 7259243Sobrien tw_file_next, tw_file_next, tw_var_next, tw_logname_next, 7359243Sobrien tw_var_next, tw_var_next, tw_shvar_next, tw_envvar_next, 7459243Sobrien tw_bind_next, tw_wl_next, tw_limit_next, tw_sig_next, 7559243Sobrien tw_job_next, tw_file_next, tw_file_next, tw_grpname_next 7659243Sobrien}; 7759243Sobrien 7859243Sobrienstatic void (*tw_end_entry[]) __P((void)) = { 7959243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_logname_end, 8059243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_logname_end, 8159243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_dir_end, 8259243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_dir_end, 8359243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_grpname_end 8459243Sobrien}; 8559243Sobrien 8659243Sobrien/* #define TDEBUG */ 8759243Sobrien 8859243Sobrien/* Set to TRUE if recexact is set and an exact match is found 8959243Sobrien * along with other, longer, matches. 9059243Sobrien */ 9159243Sobrien 9259243Sobrienint curchoice = -1; 9359243Sobrien 9459243Sobrienint match_unique_match = FALSE; 9559243Sobrienint non_unique_match = FALSE; 9659243Sobrienstatic bool SearchNoDirErr = 0; /* t_search returns -2 if dir is unreadable */ 9759243Sobrien 9859243Sobrien/* state so if a completion is interrupted, the input line doesn't get 9959243Sobrien nuked */ 10059243Sobrienint InsideCompletion = 0; 10159243Sobrien 10259243Sobrien/* do the expand or list on the command line -- SHOULD BE REPLACED */ 10359243Sobrien 10459243Sobrienextern Char NeedsRedraw; /* from ed.h */ 10559243Sobrienextern int Tty_raw_mode; 10659243Sobrienextern int TermH; /* from the editor routines */ 10759243Sobrienextern int lbuffed; /* from sh.print.c */ 10859243Sobrien 10959243Sobrienstatic void extract_dir_and_name __P((Char *, Char *, Char *)); 11059243Sobrienstatic int insert_meta __P((Char *, Char *, Char *, bool)); 11159243Sobrienstatic Char *tilde __P((Char *, Char *)); 11259243Sobrienstatic int expand_dir __P((Char *, Char *, DIR **, COMMAND)); 11359243Sobrienstatic bool nostat __P((Char *)); 11459243Sobrienstatic Char filetype __P((Char *, Char *)); 11559243Sobrienstatic int t_glob __P((Char ***, int)); 11659243Sobrienstatic int c_glob __P((Char ***)); 11759243Sobrienstatic int is_prefix __P((Char *, Char *)); 11859243Sobrienstatic int is_prefixmatch __P((Char *, Char *, int)); 11959243Sobrienstatic int is_suffix __P((Char *, Char *)); 12059243Sobrienstatic int recognize __P((Char *, Char *, int, int, int)); 12159243Sobrienstatic int ignored __P((Char *)); 12259243Sobrienstatic int isadirectory __P((Char *, Char *)); 12359243Sobrienstatic int tw_collect_items __P((COMMAND, int, Char *, Char *, 12459243Sobrien Char *, Char *, int)); 12559243Sobrienstatic int tw_collect __P((COMMAND, int, Char *, Char *, 12659243Sobrien Char **, Char *, int, DIR *)); 12759243Sobrienstatic Char tw_suffix __P((int, Char *, Char *, Char *, 12859243Sobrien Char *)); 12959243Sobrienstatic void tw_fixword __P((int, Char *, Char *, Char *, int)); 13059243Sobrienstatic void tw_list_items __P((int, int, int)); 13159243Sobrienstatic void add_scroll_tab __P((Char *)); 13259243Sobrienstatic void choose_scroll_tab __P((Char **, int)); 13359243Sobrienstatic void free_scroll_tab __P((void)); 13459243Sobrienstatic int find_rows __P((Char *[], int, int)); 13559243Sobrien 13659243Sobrien#ifdef notdef 13759243Sobrien/* 13859243Sobrien * If we find a set command, then we break a=b to a= and word becomes 13959243Sobrien * b else, we don't break a=b. [don't use that; splits words badly and 14059243Sobrien * messes up tw_complete()] 14159243Sobrien */ 14259243Sobrien#define isaset(c, w) ((w)[-1] == '=' && \ 14359243Sobrien ((c)[0] == 's' && (c)[1] == 'e' && (c)[2] == 't' && \ 14459243Sobrien ((c[3] == ' ' || (c)[3] == '\t')))) 14559243Sobrien#endif 14659243Sobrien 14759243Sobrien#define QLINESIZE (INBUFSIZE + 1) 14859243Sobrien 14959243Sobrien/* TRUE if character must be quoted */ 15059243Sobrien#define tricky(w) (cmap(w, _META | _DOL | _QF | _QB | _ESC | _GLOB) && w != '#') 15159243Sobrien/* TRUE if double quotes don't protect character */ 15259243Sobrien#define tricky_dq(w) (cmap(w, _DOL | _QB)) 15359243Sobrien 15459243Sobrien/* tenematch(): 15559243Sobrien * Return: 15659243Sobrien * > 1: No. of items found 15759243Sobrien * = 1: Exactly one match / spelling corrected 15859243Sobrien * = 0: No match / spelling was correct 15959243Sobrien * < 0: Error (incl spelling correction impossible) 16059243Sobrien */ 16159243Sobrienint 16259243Sobrientenematch(inputline, num_read, command) 16359243Sobrien Char *inputline; /* match string prefix */ 16459243Sobrien int num_read; /* # actually in inputline */ 16559243Sobrien COMMAND command; /* LIST or RECOGNIZE or PRINT_HELP */ 16659243Sobrien 16759243Sobrien{ 16859243Sobrien Char qline[QLINESIZE]; 16959243Sobrien Char qu = 0, *pat = STRNULL; 17059243Sobrien Char *str_end, *cp, *wp, *wordp; 17159243Sobrien Char *cmd_start, *word_start, *word; 17259243Sobrien Char *ocmd_start = NULL, *oword_start = NULL, *oword = NULL; 17359243Sobrien int suf = 0; 17459243Sobrien int space_left; 17559243Sobrien int looking; /* what we are looking for */ 17659243Sobrien int search_ret; /* what search returned for debugging */ 17759243Sobrien int backq = 0; 17859243Sobrien 17959243Sobrien if (num_read > QLINESIZE - 1) 18059243Sobrien return -1; 18159243Sobrien str_end = &inputline[num_read]; 18259243Sobrien 18359243Sobrien word_start = inputline; 18459243Sobrien word = cmd_start = wp = qline; 18559243Sobrien for (cp = inputline; cp < str_end; cp++) { 18659243Sobrien if (!cmap(qu, _ESC)) { 18759243Sobrien if (cmap(*cp, _QF|_ESC)) { 18859243Sobrien if (qu == 0 || qu == *cp) { 18959243Sobrien qu ^= *cp; 19059243Sobrien continue; 19159243Sobrien } 19259243Sobrien } 19359243Sobrien if (qu != '\'' && cmap(*cp, _QB)) { 19459243Sobrien if ((backq ^= 1) != 0) { 19559243Sobrien ocmd_start = cmd_start; 19659243Sobrien oword_start = word_start; 19759243Sobrien oword = word; 19859243Sobrien word_start = cp + 1; 19959243Sobrien word = cmd_start = wp + 1; 20059243Sobrien } 20159243Sobrien else { 20259243Sobrien cmd_start = ocmd_start; 20359243Sobrien word_start = oword_start; 20459243Sobrien word = oword; 20559243Sobrien } 20659243Sobrien *wp++ = *cp; 20759243Sobrien continue; 20859243Sobrien } 20959243Sobrien } 21059243Sobrien if (iscmdmeta(*cp)) 21159243Sobrien cmd_start = wp + 1; 21259243Sobrien 21359243Sobrien /* Don't quote '/' to make the recognize stuff work easily */ 21459243Sobrien /* Don't quote '$' in double quotes */ 21559243Sobrien 21659243Sobrien if (cmap(*cp, _ESC) && cp < str_end - 1 && cp[1] == HIST) 21759243Sobrien *wp = *++cp | QUOTE; 21859243Sobrien else if (qu && (tricky(*cp) || *cp == '~') && !(qu == '\"' && tricky_dq(*cp))) 21959243Sobrien *wp = *cp | QUOTE; 22059243Sobrien else 22159243Sobrien *wp = *cp; 22259243Sobrien if (ismetahash(*wp) /* || isaset(cmd_start, wp + 1) */) 22359243Sobrien word = wp + 1, word_start = cp + 1; 22459243Sobrien wp++; 22559243Sobrien if (cmap(qu, _ESC)) 22659243Sobrien qu = 0; 22759243Sobrien } 22859243Sobrien *wp = 0; 22959243Sobrien 23059243Sobrien#ifdef masscomp 23159243Sobrien /* 23259243Sobrien * Avoid a nasty message from the RTU 4.1A & RTU 5.0 compiler concerning 23359243Sobrien * the "overuse of registers". According to the compiler release notes, 23459243Sobrien * incorrect code may be produced unless the offending expression is 23559243Sobrien * rewritten. Therefore, we can't just ignore it, DAS DEC-90. 23659243Sobrien */ 23759243Sobrien space_left = QLINESIZE - 1; 23859243Sobrien space_left -= word - qline; 23959243Sobrien#else 24059243Sobrien space_left = QLINESIZE - 1 - (int) (word - qline); 24159243Sobrien#endif 24259243Sobrien 24359243Sobrien /* 24459243Sobrien * SPECIAL HARDCODED COMPLETIONS: 24559243Sobrien * first word of command -> TW_COMMAND 24659243Sobrien * everything else -> TW_ZERO 24759243Sobrien * 24859243Sobrien */ 24959243Sobrien looking = starting_a_command(word - 1, qline) ? 25059243Sobrien TW_COMMAND : TW_ZERO; 25159243Sobrien 25259243Sobrien wordp = word; 25359243Sobrien 25459243Sobrien#ifdef TDEBUG 25559243Sobrien xprintf(CGETS(30, 1, "starting_a_command %d\n"), looking); 25659243Sobrien xprintf("\ncmd_start:%S:\n", cmd_start); 25759243Sobrien xprintf("qline:%S:\n", qline); 25859243Sobrien xprintf("qline:"); 25959243Sobrien for (wp = qline; *wp; wp++) 26059243Sobrien xprintf("%c", *wp & QUOTE ? '-' : ' '); 26159243Sobrien xprintf(":\n"); 26259243Sobrien xprintf("word:%S:\n", word); 26359243Sobrien xprintf("word:"); 26459243Sobrien /* Must be last, so wp is still pointing to the end of word */ 26559243Sobrien for (wp = word; *wp; wp++) 26659243Sobrien xprintf("%c", *wp & QUOTE ? '-' : ' '); 26759243Sobrien xprintf(":\n"); 26859243Sobrien#endif 26959243Sobrien 27059243Sobrien if ((looking == TW_COMMAND || looking == TW_ZERO) && 27159243Sobrien (command == RECOGNIZE || command == LIST || command == SPELL || 27259243Sobrien command == RECOGNIZE_SCROLL)) { 27359243Sobrien#ifdef TDEBUG 27459243Sobrien xprintf(CGETS(30, 2, "complete %d "), looking); 27559243Sobrien#endif 27659243Sobrien looking = tw_complete(cmd_start, &wordp, &pat, looking, &suf); 27759243Sobrien#ifdef TDEBUG 27859243Sobrien xprintf(CGETS(30, 3, "complete %d %S\n"), looking, pat); 27959243Sobrien#endif 28059243Sobrien } 28159243Sobrien 28259243Sobrien switch (command) { 28359243Sobrien Char buffer[FILSIZ + 1], *bptr; 28459243Sobrien Char *slshp; 28559243Sobrien Char *items[2], **ptr; 28659243Sobrien int i, count; 28759243Sobrien 28859243Sobrien case RECOGNIZE: 28959243Sobrien case RECOGNIZE_SCROLL: 29059243Sobrien case RECOGNIZE_ALL: 29159243Sobrien if (adrof(STRautocorrect)) { 29259243Sobrien if ((slshp = Strrchr(wordp, '/')) != NULL && slshp[1] != '\0') { 29359243Sobrien SearchNoDirErr = 1; 29459243Sobrien for (bptr = wordp; bptr < slshp; bptr++) { 29559243Sobrien /* 29659243Sobrien * do not try to correct spelling of words containing 29759243Sobrien * globbing characters 29859243Sobrien */ 29959243Sobrien if (isglob(*bptr)) { 30059243Sobrien SearchNoDirErr = 0; 30159243Sobrien break; 30259243Sobrien } 30359243Sobrien } 30459243Sobrien } 30559243Sobrien } 30659243Sobrien else 30759243Sobrien slshp = STRNULL; 30859243Sobrien search_ret = t_search(wordp, wp, command, space_left, looking, 1, 30959243Sobrien pat, suf); 31059243Sobrien SearchNoDirErr = 0; 31159243Sobrien 31259243Sobrien if (search_ret == -2) { 31359243Sobrien Char rword[FILSIZ + 1]; 31459243Sobrien 31559243Sobrien (void) Strcpy(rword, slshp); 31659243Sobrien if (slshp != STRNULL) 31759243Sobrien *slshp = '\0'; 31859243Sobrien search_ret = spell_me(wordp, QLINESIZE - (wordp - qline), looking, 31959243Sobrien pat, suf); 32059243Sobrien if (search_ret == 1) { 32159243Sobrien (void) Strcat(wordp, rword); 32259243Sobrien wp = wordp + (int) Strlen(wordp); 32359243Sobrien search_ret = t_search(wordp, wp, command, space_left, 32459243Sobrien looking, 1, pat, suf); 32559243Sobrien } 32659243Sobrien } 32759243Sobrien if (*wp && insert_meta(word_start, str_end, word, !qu) < 0) 32859243Sobrien return -1; /* error inserting */ 32959243Sobrien return search_ret; 33059243Sobrien 33159243Sobrien case SPELL: 33259243Sobrien for (bptr = word_start; bptr < str_end; bptr++) { 33359243Sobrien /* 33459243Sobrien * do not try to correct spelling of words containing globbing 33559243Sobrien * characters 33659243Sobrien */ 33759243Sobrien if (isglob(*bptr)) 33859243Sobrien return 0; 33959243Sobrien } 34059243Sobrien search_ret = spell_me(wordp, QLINESIZE - (wordp - qline), looking, 34159243Sobrien pat, suf); 34259243Sobrien if (search_ret == 1) { 34359243Sobrien if (insert_meta(word_start, str_end, word, !qu) < 0) 34459243Sobrien return -1; /* error inserting */ 34559243Sobrien } 34659243Sobrien return search_ret; 34759243Sobrien 34859243Sobrien case PRINT_HELP: 34959243Sobrien do_help(cmd_start); 35059243Sobrien return 1; 35159243Sobrien 35259243Sobrien case GLOB: 35359243Sobrien case GLOB_EXPAND: 35459243Sobrien (void) Strncpy(buffer, wordp, FILSIZ + 1); 35559243Sobrien items[0] = buffer; 35659243Sobrien items[1] = NULL; 35759243Sobrien ptr = items; 35859243Sobrien count = (looking == TW_COMMAND && Strchr(wordp, '/') == 0) ? 35959243Sobrien c_glob(&ptr) : 36059243Sobrien t_glob(&ptr, looking == TW_COMMAND); 36159243Sobrien if (count > 0) { 36259243Sobrien if (command == GLOB) 36359243Sobrien print_by_column(STRNULL, ptr, count, 0); 36459243Sobrien else { 36559243Sobrien DeleteBack(str_end - word_start);/* get rid of old word */ 36659243Sobrien for (i = 0; i < count; i++) 36759243Sobrien if (ptr[i] && *ptr[i]) { 36859243Sobrien (void) quote(ptr[i]); 36959243Sobrien if (insert_meta(0, 0, ptr[i], 0) < 0 || 37059243Sobrien InsertStr(STRspace) < 0) { 37159243Sobrien blkfree(ptr); 37259243Sobrien return -1; /* error inserting */ 37359243Sobrien } 37459243Sobrien } 37559243Sobrien } 37659243Sobrien blkfree(ptr); 37759243Sobrien } 37859243Sobrien return count; 37959243Sobrien 38059243Sobrien case VARS_EXPAND: 38159243Sobrien if (dollar(buffer, word)) { 38259243Sobrien if (insert_meta(word_start, str_end, buffer, !qu) < 0) 38359243Sobrien return -1; /* error inserting */ 38459243Sobrien return 1; 38559243Sobrien } 38659243Sobrien return 0; 38759243Sobrien 38859243Sobrien case PATH_NORMALIZE: 38959243Sobrien if ((bptr = dnormalize(wordp, symlinks == SYM_IGNORE || 39059243Sobrien symlinks == SYM_EXPAND)) != NULL) { 39159243Sobrien (void) Strcpy(buffer, bptr); 39259243Sobrien xfree((ptr_t) bptr); 39359243Sobrien if (insert_meta(word_start, str_end, buffer, !qu) < 0) 39459243Sobrien return -1; /* error inserting */ 39559243Sobrien return 1; 39659243Sobrien } 39759243Sobrien return 0; 39859243Sobrien 39959243Sobrien case COMMAND_NORMALIZE: 40059243Sobrien if (!cmd_expand(wordp, buffer)) 40159243Sobrien return 0; 40259243Sobrien if (insert_meta(word_start, str_end, buffer, !qu) < 0) 40359243Sobrien return -1; /* error inserting */ 40459243Sobrien return 1; 40559243Sobrien 40659243Sobrien case LIST: 40759243Sobrien case LIST_ALL: 40859243Sobrien search_ret = t_search(wordp, wp, LIST, space_left, looking, 1, 40959243Sobrien pat, suf); 41059243Sobrien return search_ret; 41159243Sobrien 41259243Sobrien default: 41359243Sobrien xprintf(CGETS(30, 4, "%s: Internal match error.\n"), progname); 41459243Sobrien return 1; 41559243Sobrien 41659243Sobrien } 41759243Sobrien} /* end tenematch */ 41859243Sobrien 41959243Sobrien 42059243Sobrien/* t_glob(): 42159243Sobrien * Return a list of files that match the pattern 42259243Sobrien */ 42359243Sobrienstatic int 42459243Sobrient_glob(v, cmd) 42559243Sobrien register Char ***v; 42659243Sobrien int cmd; 42759243Sobrien{ 42859243Sobrien jmp_buf_t osetexit; 42959243Sobrien 43059243Sobrien if (**v == 0) 43159243Sobrien return (0); 43259243Sobrien gflag = 0, tglob(*v); 43359243Sobrien if (gflag) { 43459243Sobrien getexit(osetexit); /* make sure to come back here */ 43559243Sobrien if (setexit() == 0) 43659243Sobrien *v = globall(*v); 43759243Sobrien resexit(osetexit); 43859243Sobrien gargv = 0; 43959243Sobrien if (haderr) { 44059243Sobrien haderr = 0; 44159243Sobrien NeedsRedraw = 1; 44259243Sobrien return (-1); 44359243Sobrien } 44459243Sobrien if (*v == 0) 44559243Sobrien return (0); 44659243Sobrien } 44759243Sobrien else 44859243Sobrien return (0); 44959243Sobrien 45059243Sobrien if (cmd) { 45159243Sobrien Char **av = *v, *p; 45259243Sobrien int fwd, i, ac = gargc; 45359243Sobrien 45459243Sobrien for (i = 0, fwd = 0; i < ac; i++) 45559243Sobrien if (!executable(NULL, av[i], 0)) { 45659243Sobrien fwd++; 45759243Sobrien p = av[i]; 45859243Sobrien av[i] = NULL; 45959243Sobrien xfree((ptr_t) p); 46059243Sobrien } 46159243Sobrien else if (fwd) 46259243Sobrien av[i - fwd] = av[i]; 46359243Sobrien 46459243Sobrien if (fwd) 46559243Sobrien av[i - fwd] = av[i]; 46659243Sobrien gargc -= fwd; 46759243Sobrien av[gargc] = NULL; 46859243Sobrien } 46959243Sobrien 47059243Sobrien return (gargc); 47159243Sobrien} /* end t_glob */ 47259243Sobrien 47359243Sobrien 47459243Sobrien/* c_glob(): 47559243Sobrien * Return a list of commands that match the pattern 47659243Sobrien */ 47759243Sobrienstatic int 47859243Sobrienc_glob(v) 47959243Sobrien register Char ***v; 48059243Sobrien{ 48159243Sobrien Char *pat = **v, *cmd, **av; 48259243Sobrien Char dir[MAXPATHLEN+1]; 48359243Sobrien int flag, at, ac; 48459243Sobrien 48559243Sobrien if (pat == NULL) 48659243Sobrien return (0); 48759243Sobrien 48859243Sobrien ac = 0; 48959243Sobrien at = 10; 49059243Sobrien av = (Char **) xmalloc((size_t) (at * sizeof(Char *))); 49159243Sobrien av[ac] = NULL; 49259243Sobrien 49359243Sobrien tw_cmd_start(NULL, NULL); 49459243Sobrien while ((cmd = tw_cmd_next(dir, &flag)) != NULL) 49559243Sobrien if (Gmatch(cmd, pat)) { 49659243Sobrien if (ac + 1 >= at) { 49759243Sobrien at += 10; 49859243Sobrien av = (Char **) xrealloc((ptr_t) av, 49959243Sobrien (size_t) (at * sizeof(Char *))); 50059243Sobrien } 50159243Sobrien av[ac++] = Strsave(cmd); 50259243Sobrien av[ac] = NULL; 50359243Sobrien } 50459243Sobrien tw_dir_end(); 50559243Sobrien *v = av; 50659243Sobrien 50759243Sobrien return (ac); 50859243Sobrien} /* end c_glob */ 50959243Sobrien 51059243Sobrien 51159243Sobrien/* insert_meta(): 51259243Sobrien * change the word before the cursor. 51359243Sobrien * cp must point to the start of the unquoted word. 51459243Sobrien * cpend to the end of it. 51559243Sobrien * word is the text that has to be substituted. 51659243Sobrien * strategy: 51759243Sobrien * try to keep all the quote characters of the user's input. 51859243Sobrien * change quote type only if necessary. 51959243Sobrien */ 52059243Sobrienstatic int 52159243Sobrieninsert_meta(cp, cpend, word, closequotes) 52259243Sobrien Char *cp; 52359243Sobrien Char *cpend; 52459243Sobrien Char *word; 52559243Sobrien bool closequotes; 52659243Sobrien{ 52759243Sobrien Char buffer[2 * FILSIZ + 1], *bptr, *wptr; 52859243Sobrien int in_sync = (cp != NULL); 52959243Sobrien int qu = 0; 53059243Sobrien int ndel = (int) (cp ? cpend - cp : 0); 53159243Sobrien Char w, wq; 53259243Sobrien 53359243Sobrien for (bptr = buffer, wptr = word;;) { 53459243Sobrien if (bptr > buffer + 2 * FILSIZ - 5) 53559243Sobrien break; 53659243Sobrien 53759243Sobrien if (cp >= cpend) 53859243Sobrien in_sync = 0; 53959243Sobrien if (in_sync && !cmap(qu, _ESC) && cmap(*cp, _QF|_ESC)) 54059243Sobrien if (qu == 0 || qu == *cp) { 54159243Sobrien qu ^= *cp; 54259243Sobrien *bptr++ = *cp++; 54359243Sobrien continue; 54459243Sobrien } 54559243Sobrien w = *wptr; 54659243Sobrien if (w == 0) 54759243Sobrien break; 54859243Sobrien 54959243Sobrien wq = w & QUOTE; 55059243Sobrien w &= ~QUOTE; 55159243Sobrien 55259243Sobrien if (cmap(w, _ESC | _QF)) 55359243Sobrien wq = QUOTE; /* quotes are always quoted */ 55459243Sobrien 55559243Sobrien if (!wq && qu && tricky(w) && !(qu == '\"' && tricky_dq(w))) { 55659243Sobrien /* We have to unquote the character */ 55759243Sobrien in_sync = 0; 55859243Sobrien if (cmap(qu, _ESC)) 55959243Sobrien bptr[-1] = w; 56059243Sobrien else { 56159243Sobrien *bptr++ = (Char) qu; 56259243Sobrien *bptr++ = w; 56359243Sobrien if (wptr[1] == 0) 56459243Sobrien qu = 0; 56559243Sobrien else 56659243Sobrien *bptr++ = (Char) qu; 56759243Sobrien } 56859243Sobrien } else if (qu && w == qu) { 56959243Sobrien in_sync = 0; 57059243Sobrien if (bptr > buffer && bptr[-1] == qu) { 57159243Sobrien /* User misunderstanding :) */ 57259243Sobrien bptr[-1] = '\\'; 57359243Sobrien *bptr++ = w; 57459243Sobrien qu = 0; 57559243Sobrien } else { 57659243Sobrien *bptr++ = (Char) qu; 57759243Sobrien *bptr++ = '\\'; 57859243Sobrien *bptr++ = w; 57959243Sobrien *bptr++ = (Char) qu; 58059243Sobrien } 58159243Sobrien } 58259243Sobrien else if (wq && qu == '\"' && tricky_dq(w)) { 58359243Sobrien in_sync = 0; 58459243Sobrien *bptr++ = (Char) qu; 58559243Sobrien *bptr++ = '\\'; 58659243Sobrien *bptr++ = w; 58759243Sobrien *bptr++ = (Char) qu; 58859243Sobrien } else if (wq && ((!qu && (tricky(w) || (w == HISTSUB && bptr == buffer))) || (!cmap(qu, _ESC) && w == HIST))) { 58959243Sobrien in_sync = 0; 59059243Sobrien *bptr++ = '\\'; 59159243Sobrien *bptr++ = w; 59259243Sobrien } else { 59359243Sobrien if (in_sync && *cp++ != w) 59459243Sobrien in_sync = 0; 59559243Sobrien *bptr++ = w; 59659243Sobrien } 59759243Sobrien wptr++; 59859243Sobrien if (cmap(qu, _ESC)) 59959243Sobrien qu = 0; 60059243Sobrien } 60159243Sobrien if (closequotes && qu && !cmap(qu, _ESC)) 60259243Sobrien *bptr++ = (Char) qu; 60359243Sobrien *bptr = '\0'; 60459243Sobrien if (ndel) 60559243Sobrien DeleteBack(ndel); 60659243Sobrien return InsertStr(buffer); 60759243Sobrien} /* end insert_meta */ 60859243Sobrien 60959243Sobrien 61059243Sobrien 61159243Sobrien/* is_prefix(): 61259243Sobrien * return true if check matches initial chars in template 61359243Sobrien * This differs from PWB imatch in that if check is null 61459243Sobrien * it matches anything 61559243Sobrien */ 61659243Sobrienstatic int 61759243Sobrienis_prefix(check, template) 61859243Sobrien register Char *check, *template; 61959243Sobrien{ 62059243Sobrien for (; *check; check++, template++) 62159243Sobrien if ((*check & TRIM) != (*template & TRIM)) 62259243Sobrien return (FALSE); 62359243Sobrien return (TRUE); 62459243Sobrien} /* end is_prefix */ 62559243Sobrien 62659243Sobrien 62759243Sobrien/* is_prefixmatch(): 62859243Sobrien * return true if check matches initial chars in template 62959243Sobrien * This differs from PWB imatch in that if check is null 63059243Sobrien * it matches anything 63159243Sobrien * and matches on shortening of commands 63259243Sobrien */ 63359243Sobrienstatic int 63459243Sobrienis_prefixmatch(check, template, igncase) 63559243Sobrien Char *check, *template; 63659243Sobrien int igncase; 63759243Sobrien{ 63859243Sobrien Char MCH1, MCH2; 63959243Sobrien 64059243Sobrien for (; *check; check++, template++) { 64159243Sobrien if ((*check & TRIM) != (*template & TRIM)) { 64259243Sobrien MCH1 = (*check & TRIM); 64359243Sobrien MCH2 = (*template & TRIM); 64459243Sobrien MCH1 = Isupper(MCH1) ? Tolower(MCH1) : MCH1; 64559243Sobrien MCH2 = Isupper(MCH2) ? Tolower(MCH2) : MCH2; 64659243Sobrien if (MCH1 != MCH2) { 64759243Sobrien if (!igncase && ((*check & TRIM) == '-' || 64859243Sobrien (*check & TRIM) == '.' || 64959243Sobrien (*check & TRIM) == '_')) { 65059243Sobrien MCH1 = MCH2 = (*check & TRIM); 65159243Sobrien if (MCH1 == '_') { 65259243Sobrien MCH2 = '-'; 65359243Sobrien } else if (MCH1 == '-') { 65459243Sobrien MCH2 = '_'; 65559243Sobrien } 65659243Sobrien for (;*template && (*template & TRIM) != MCH1 && 65759243Sobrien (*template & TRIM) != MCH2; template++) 65859243Sobrien continue; 65959243Sobrien if (!*template) { 66059243Sobrien return (FALSE); 66159243Sobrien } 66259243Sobrien } else { 66359243Sobrien return (FALSE); 66459243Sobrien } 66559243Sobrien } 66659243Sobrien } 66759243Sobrien } 66859243Sobrien return (TRUE); 66959243Sobrien} /* end is_prefixmatch */ 67059243Sobrien 67159243Sobrien 67259243Sobrien/* is_suffix(): 67359243Sobrien * Return true if the chars in template appear at the 67459243Sobrien * end of check, I.e., are it's suffix. 67559243Sobrien */ 67659243Sobrienstatic int 67759243Sobrienis_suffix(check, template) 67859243Sobrien register Char *check, *template; 67959243Sobrien{ 68059243Sobrien register Char *t, *c; 68159243Sobrien 68259243Sobrien for (t = template; *t++;) 68359243Sobrien continue; 68459243Sobrien for (c = check; *c++;) 68559243Sobrien continue; 68659243Sobrien for (;;) { 68759243Sobrien if (t == template) 68859243Sobrien return 1; 68959243Sobrien if (c == check || (*--t & TRIM) != (*--c & TRIM)) 69059243Sobrien return 0; 69159243Sobrien } 69259243Sobrien} /* end is_suffix */ 69359243Sobrien 69459243Sobrien 69559243Sobrien/* ignored(): 69659243Sobrien * Return true if this is an ignored item 69759243Sobrien */ 69859243Sobrienstatic int 69959243Sobrienignored(item) 70059243Sobrien register Char *item; 70159243Sobrien{ 70259243Sobrien struct varent *vp; 70359243Sobrien register Char **cp; 70459243Sobrien 70559243Sobrien if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 70659243Sobrien return (FALSE); 70759243Sobrien for (; *cp != NULL; cp++) 70859243Sobrien if (is_suffix(item, *cp)) 70959243Sobrien return (TRUE); 71059243Sobrien return (FALSE); 71159243Sobrien} /* end ignored */ 71259243Sobrien 71359243Sobrien 71459243Sobrien 71559243Sobrien/* starting_a_command(): 71659243Sobrien * return true if the command starting at wordstart is a command 71759243Sobrien */ 71859243Sobrienint 71959243Sobrienstarting_a_command(wordstart, inputline) 72059243Sobrien register Char *wordstart, *inputline; 72159243Sobrien{ 72259243Sobrien register Char *ptr, *ncmdstart; 72359243Sobrien int count; 72459243Sobrien static Char 72559243Sobrien cmdstart[] = {'`', ';', '&', '(', '|', '\0'}, 72659243Sobrien cmdalive[] = {' ', '\t', '\'', '"', '<', '>', '\0'}; 72759243Sobrien 72859243Sobrien /* 72959243Sobrien * Find if the number of backquotes is odd or even. 73059243Sobrien */ 73159243Sobrien for (ptr = wordstart, count = 0; 73259243Sobrien ptr >= inputline; 73359243Sobrien count += (*ptr-- == '`')) 73459243Sobrien continue; 73559243Sobrien /* 73659243Sobrien * if the number of backquotes is even don't include the backquote char in 73759243Sobrien * the list of command starting delimiters [if it is zero, then it does not 73859243Sobrien * matter] 73959243Sobrien */ 74059243Sobrien ncmdstart = cmdstart + EVEN(count); 74159243Sobrien 74259243Sobrien /* 74359243Sobrien * look for the characters previous to this word if we find a command 74459243Sobrien * starting delimiter we break. if we find whitespace and another previous 74559243Sobrien * word then we are not a command 74659243Sobrien * 74759243Sobrien * count is our state machine: 0 looking for anything 1 found white-space 74859243Sobrien * looking for non-ws 74959243Sobrien */ 75059243Sobrien for (count = 0; wordstart >= inputline; wordstart--) { 75159243Sobrien if (*wordstart == '\0') 75259243Sobrien continue; 75359243Sobrien if (Strchr(ncmdstart, *wordstart)) 75459243Sobrien break; 75559243Sobrien /* 75659243Sobrien * found white space 75759243Sobrien */ 75859243Sobrien if ((ptr = Strchr(cmdalive, *wordstart)) != NULL) 75959243Sobrien count = 1; 76059243Sobrien if (count == 1 && !ptr) 76159243Sobrien return (FALSE); 76259243Sobrien } 76359243Sobrien 76459243Sobrien if (wordstart > inputline) 76559243Sobrien switch (*wordstart) { 76659243Sobrien case '&': /* Look for >& */ 76759243Sobrien while (wordstart > inputline && 76859243Sobrien (*--wordstart == ' ' || *wordstart == '\t')) 76959243Sobrien continue; 77059243Sobrien if (*wordstart == '>') 77159243Sobrien return (FALSE); 77259243Sobrien break; 77359243Sobrien case '(': /* check for foreach, if etc. */ 77459243Sobrien while (wordstart > inputline && 77559243Sobrien (*--wordstart == ' ' || *wordstart == '\t')) 77659243Sobrien continue; 77759243Sobrien if (!iscmdmeta(*wordstart) && 77859243Sobrien (*wordstart != ' ' && *wordstart != '\t')) 77959243Sobrien return (FALSE); 78059243Sobrien break; 78159243Sobrien default: 78259243Sobrien break; 78359243Sobrien } 78459243Sobrien return (TRUE); 78559243Sobrien} /* end starting_a_command */ 78659243Sobrien 78759243Sobrien 78859243Sobrien/* recognize(): 78959243Sobrien * Object: extend what user typed up to an ambiguity. 79059243Sobrien * Algorithm: 79159243Sobrien * On first match, copy full item (assume it'll be the only match) 79259243Sobrien * On subsequent matches, shorten exp_name to the first 79359243Sobrien * character mismatch between exp_name and item. 79459243Sobrien * If we shorten it back to the prefix length, stop searching. 79559243Sobrien */ 79659243Sobrienstatic int 79759243Sobrienrecognize(exp_name, item, name_length, numitems, enhanced) 79859243Sobrien Char *exp_name, *item; 79959243Sobrien int name_length, numitems, enhanced; 80059243Sobrien{ 80159243Sobrien Char MCH1, MCH2; 80259243Sobrien register Char *x, *ent; 80359243Sobrien register int len = 0; 80459243Sobrien#ifdef WINNT 80559243Sobrien struct varent *vp; 80659243Sobrien int igncase; 80759243Sobrien igncase = (vp = adrof(STRcomplete)) != NULL && 80859243Sobrien Strcmp(*(vp->vec), STRigncase) == 0; 80959243Sobrien#endif /* WINNT */ 81059243Sobrien 81159243Sobrien if (numitems == 1) { /* 1st match */ 81259243Sobrien copyn(exp_name, item, MAXNAMLEN); 81359243Sobrien return (0); 81459243Sobrien } 81559243Sobrien if (!enhanced 81659243Sobrien#ifdef WINNT 81759243Sobrien && !igncase 81859243Sobrien#endif /* WINNT */ 81959243Sobrien ) { 82059243Sobrien for (x = exp_name, ent = item; *x && (*x & TRIM) == (*ent & TRIM); x++, ent++) 82159243Sobrien len++; 82259243Sobrien } else { 82359243Sobrien for (x = exp_name, ent = item; *x; x++, ent++) { 82459243Sobrien MCH1 = *x & TRIM; 82559243Sobrien MCH2 = *ent & TRIM; 82659243Sobrien MCH1 = Isupper(MCH1) ? Tolower(MCH1) : MCH1; 82759243Sobrien MCH2 = Isupper(MCH2) ? Tolower(MCH2) : MCH2; 82859243Sobrien if (MCH1 != MCH2) 82959243Sobrien break; 83059243Sobrien len++; 83159243Sobrien } 83259243Sobrien if (*x || !*ent) /* Shorter or exact match */ 83359243Sobrien copyn(exp_name, item, MAXNAMLEN); 83459243Sobrien } 83559243Sobrien *x = '\0'; /* Shorten at 1st char diff */ 83659243Sobrien if (!(match_unique_match || is_set(STRrecexact) || (enhanced && *ent)) && len == name_length) /* Ambiguous to prefix? */ 83759243Sobrien return (-1); /* So stop now and save time */ 83859243Sobrien return (0); 83959243Sobrien} /* end recognize */ 84059243Sobrien 84159243Sobrien 84259243Sobrien/* tw_collect_items(): 84359243Sobrien * Collect items that match target. 84459243Sobrien * SPELL command: 84559243Sobrien * Returns the spelling distance of the closest match. 84659243Sobrien * else 84759243Sobrien * Returns the number of items found. 84859243Sobrien * If none found, but some ignored items were found, 84959243Sobrien * It returns the -number of ignored items. 85059243Sobrien */ 85159243Sobrienstatic int 85259243Sobrientw_collect_items(command, looking, exp_dir, exp_name, target, pat, flags) 85359243Sobrien COMMAND command; 85459243Sobrien int looking; 85559243Sobrien Char *exp_dir, *exp_name, *target, *pat; 85659243Sobrien int flags; 85759243Sobrien 85859243Sobrien{ 85959243Sobrien int done = FALSE; /* Search is done */ 86059243Sobrien int showdots; /* Style to show dot files */ 86159243Sobrien int nignored = 0; /* Number of fignored items */ 86259243Sobrien int numitems = 0; /* Number of matched items */ 86359243Sobrien int name_length = (int) Strlen(target); /* Length of prefix (file name) */ 86459243Sobrien int exec_check = flags & TW_EXEC_CHK;/* need to check executability */ 86559243Sobrien int dir_check = flags & TW_DIR_CHK; /* Need to check for directories */ 86659243Sobrien int text_check = flags & TW_TEXT_CHK;/* Need to check for non-directories */ 86759243Sobrien int dir_ok = flags & TW_DIR_OK; /* Ignore directories? */ 86859243Sobrien int gpat = flags & TW_PAT_OK; /* Match against a pattern */ 86959243Sobrien int ignoring = flags & TW_IGN_OK; /* Use fignore? */ 87059243Sobrien int d = 4, nd; /* Spelling distance */ 87159243Sobrien Char *item, *ptr; 87259243Sobrien Char buf[MAXPATHLEN+1]; 87359243Sobrien struct varent *vp; 87459243Sobrien int len, enhanced; 87559243Sobrien int cnt = 0; 87659243Sobrien int igncase = 0; 87759243Sobrien 87859243Sobrien 87959243Sobrien flags = 0; 88059243Sobrien 88159243Sobrien showdots = DOT_NONE; 88259243Sobrien if ((ptr = varval(STRlistflags)) != STRNULL) 88359243Sobrien while (*ptr) 88459243Sobrien switch (*ptr++) { 88559243Sobrien case 'a': 88659243Sobrien showdots = DOT_ALL; 88759243Sobrien break; 88859243Sobrien case 'A': 88959243Sobrien showdots = DOT_NOT; 89059243Sobrien break; 89159243Sobrien default: 89259243Sobrien break; 89359243Sobrien } 89459243Sobrien 89559243Sobrien while (!done && (item = (*tw_next_entry[looking])(exp_dir, &flags))) { 89659243Sobrien#ifdef TDEBUG 89759243Sobrien xprintf("item = %S\n", item); 89859243Sobrien#endif 89959243Sobrien switch (looking) { 90059243Sobrien case TW_FILE: 90159243Sobrien case TW_DIRECTORY: 90259243Sobrien case TW_TEXT: 90359243Sobrien /* 90459243Sobrien * Don't match . files on null prefix match 90559243Sobrien */ 90659243Sobrien if (showdots == DOT_NOT && (ISDOT(item) || ISDOTDOT(item))) 90759243Sobrien done = TRUE; 90859243Sobrien if (name_length == 0 && item[0] == '.' && showdots == DOT_NONE) 90959243Sobrien done = TRUE; 91059243Sobrien break; 91159243Sobrien 91259243Sobrien case TW_COMMAND: 91359243Sobrien exec_check = flags & TW_EXEC_CHK; 91459243Sobrien dir_ok = flags & TW_DIR_OK; 91559243Sobrien break; 91659243Sobrien 91759243Sobrien default: 91859243Sobrien break; 91959243Sobrien } 92059243Sobrien 92159243Sobrien if (done) { 92259243Sobrien done = FALSE; 92359243Sobrien continue; 92459243Sobrien } 92559243Sobrien 92659243Sobrien switch (command) { 92759243Sobrien 92859243Sobrien case SPELL: /* correct the spelling of the last bit */ 92959243Sobrien if (name_length == 0) {/* zero-length word can't be misspelled */ 93059243Sobrien exp_name[0] = '\0';/* (not trying is important for ~) */ 93159243Sobrien d = 0; 93259243Sobrien done = TRUE; 93359243Sobrien break; 93459243Sobrien } 93559243Sobrien if (gpat && !Gmatch(item, pat)) 93659243Sobrien break; 93759243Sobrien /* 93859243Sobrien * Swapped the order of the spdist() arguments as suggested 93959243Sobrien * by eeide@asylum.cs.utah.edu (Eric Eide) 94059243Sobrien */ 94159243Sobrien nd = spdist(target, item); /* test the item against original */ 94259243Sobrien if (nd <= d && nd != 4) { 94359243Sobrien if (!(exec_check && !executable(exp_dir, item, dir_ok))) { 94459243Sobrien (void) Strcpy(exp_name, item); 94559243Sobrien d = nd; 94659243Sobrien if (d == 0) /* if found it exactly */ 94759243Sobrien done = TRUE; 94859243Sobrien } 94959243Sobrien } 95059243Sobrien else if (nd == 4) { 95159243Sobrien if (spdir(exp_name, exp_dir, item, target)) { 95259243Sobrien if (exec_check && !executable(exp_dir, exp_name, dir_ok)) 95359243Sobrien break; 95459243Sobrien#ifdef notdef 95559243Sobrien /* 95659243Sobrien * We don't want to stop immediately, because 95759243Sobrien * we might find an exact/better match later. 95859243Sobrien */ 95959243Sobrien d = 0; 96059243Sobrien done = TRUE; 96159243Sobrien#endif 96259243Sobrien d = 3; 96359243Sobrien } 96459243Sobrien } 96559243Sobrien break; 96659243Sobrien 96759243Sobrien case LIST: 96859243Sobrien case RECOGNIZE: 96959243Sobrien case RECOGNIZE_ALL: 97059243Sobrien case RECOGNIZE_SCROLL: 97159243Sobrien 97259243Sobrien#ifdef WINNT 97359243Sobrien igncase = (vp = adrof(STRcomplete)) != NULL && 97459243Sobrien Strcmp(*(vp->vec), STRigncase) == 0; 97559243Sobrien#endif /* WINNT */ 97659243Sobrien enhanced = (vp = adrof(STRcomplete)) != NULL && !Strcmp(*(vp->vec),STRenhance); 97759243Sobrien if (enhanced || igncase) { 97859243Sobrien if (!is_prefixmatch(target, item, igncase)) 97959243Sobrien break; 98059243Sobrien } else { 98159243Sobrien if (!is_prefix(target, item)) 98259243Sobrien break; 98359243Sobrien } 98459243Sobrien 98559243Sobrien if (exec_check && !executable(exp_dir, item, dir_ok)) 98659243Sobrien break; 98759243Sobrien 98859243Sobrien if (dir_check && !isadirectory(exp_dir, item)) 98959243Sobrien break; 99059243Sobrien 99159243Sobrien if (text_check && isadirectory(exp_dir, item)) 99259243Sobrien break; 99359243Sobrien 99459243Sobrien /* 99559243Sobrien * Only pattern match directories if we're checking 99659243Sobrien * for directories. 99759243Sobrien */ 99859243Sobrien if (gpat && !Gmatch(item, pat) && 99959243Sobrien (dir_check || !isadirectory(exp_dir, item))) 100059243Sobrien break; 100159243Sobrien 100259243Sobrien /* 100359243Sobrien * Remove duplicates in command listing and completion 100459243Sobrien * AFEB added code for TW_LOGNAME and TW_USER cases 100559243Sobrien */ 100659243Sobrien if (looking == TW_COMMAND || looking == TW_LOGNAME 100759243Sobrien || looking == TW_USER || command == LIST) { 100859243Sobrien copyn(buf, item, MAXPATHLEN); 100959243Sobrien len = (int) Strlen(buf); 101059243Sobrien switch (looking) { 101159243Sobrien case TW_COMMAND: 101259243Sobrien if (!(dir_ok && exec_check)) 101359243Sobrien break; 101459243Sobrien if (filetype(exp_dir, item) == '/') { 101559243Sobrien buf[len++] = '/'; 101659243Sobrien buf[len] = '\0'; 101759243Sobrien } 101859243Sobrien break; 101959243Sobrien 102059243Sobrien case TW_FILE: 102159243Sobrien case TW_DIRECTORY: 102259243Sobrien buf[len++] = filetype(exp_dir, item); 102359243Sobrien buf[len] = '\0'; 102459243Sobrien break; 102559243Sobrien 102659243Sobrien default: 102759243Sobrien break; 102859243Sobrien } 102959243Sobrien if ((looking == TW_COMMAND || looking == TW_USER 103059243Sobrien || looking == TW_LOGNAME) && tw_item_find(buf)) 103159243Sobrien break; 103259243Sobrien else { 103359243Sobrien /* maximum length 1 (NULL) + 1 (~ or $) + 1 (filetype) */ 103459243Sobrien ptr = tw_item_add(len + 3); 103559243Sobrien copyn(ptr, buf, MAXPATHLEN); 103659243Sobrien if (command == LIST) 103759243Sobrien numitems++; 103859243Sobrien } 103959243Sobrien } 104059243Sobrien 104159243Sobrien if (command == RECOGNIZE || command == RECOGNIZE_ALL || 104259243Sobrien command == RECOGNIZE_SCROLL) { 104359243Sobrien if (ignoring && ignored(item)) { 104459243Sobrien nignored++; 104559243Sobrien break; 104659243Sobrien } 104759243Sobrien else if (command == RECOGNIZE_SCROLL) { 104859243Sobrien add_scroll_tab(item); 104959243Sobrien cnt++; 105059243Sobrien } 105159243Sobrien 105259243Sobrien if (match_unique_match || is_set(STRrecexact)) { 105359243Sobrien if (StrQcmp(target, item) == 0) { /* EXACT match */ 105459243Sobrien copyn(exp_name, item, MAXNAMLEN); 105559243Sobrien numitems = 1; /* fake into expanding */ 105659243Sobrien non_unique_match = TRUE; 105759243Sobrien done = TRUE; 105859243Sobrien break; 105959243Sobrien } 106059243Sobrien } 106159243Sobrien if (recognize(exp_name, item, name_length, ++numitems, enhanced)) 106259243Sobrien if (command != RECOGNIZE_SCROLL) 106359243Sobrien done = TRUE; 106459243Sobrien if (enhanced && (int)Strlen(exp_name) < name_length) 106559243Sobrien copyn(exp_name, target, MAXNAMLEN); 106659243Sobrien } 106759243Sobrien break; 106859243Sobrien 106959243Sobrien default: 107059243Sobrien break; 107159243Sobrien } 107259243Sobrien#ifdef TDEBUG 107359243Sobrien xprintf("done item = %S\n", item); 107459243Sobrien#endif 107559243Sobrien } 107659243Sobrien 107759243Sobrien 107859243Sobrien if (command == RECOGNIZE_SCROLL) { 107959243Sobrien if ((cnt <= curchoice) || (curchoice == -1)) { 108059243Sobrien curchoice = -1; 108159243Sobrien nignored = 0; 108259243Sobrien numitems = 0; 108359243Sobrien } else if (numitems > 1) { 108459243Sobrien if (curchoice < -1) 108559243Sobrien curchoice = cnt - 1; 108659243Sobrien choose_scroll_tab(&exp_name, cnt); 108759243Sobrien numitems = 1; 108859243Sobrien } 108959243Sobrien } 109059243Sobrien free_scroll_tab(); 109159243Sobrien 109259243Sobrien if (command == SPELL) 109359243Sobrien return d; 109459243Sobrien else { 109559243Sobrien if (ignoring && numitems == 0 && nignored > 0) 109659243Sobrien return -nignored; 109759243Sobrien else 109859243Sobrien return numitems; 109959243Sobrien } 110059243Sobrien} 110159243Sobrien 110259243Sobrien 110359243Sobrien/* tw_suffix(): 110459243Sobrien * Find and return the appropriate suffix character 110559243Sobrien */ 110659243Sobrien/*ARGSUSED*/ 110759243Sobrienstatic Char 110859243Sobrientw_suffix(looking, exp_dir, exp_name, target, name) 110959243Sobrien int looking; 111059243Sobrien Char *exp_dir, *exp_name, *target, *name; 111159243Sobrien{ 111259243Sobrien Char *ptr; 111359243Sobrien struct varent *vp; 111459243Sobrien 111559243Sobrien USE(name); 111659243Sobrien (void) strip(exp_name); 111759243Sobrien 111859243Sobrien switch (looking) { 111959243Sobrien 112059243Sobrien case TW_LOGNAME: 112159243Sobrien return '/'; 112259243Sobrien 112359243Sobrien case TW_VARIABLE: 112459243Sobrien /* 112559243Sobrien * Don't consider array variables or empty variables 112659243Sobrien */ 112759243Sobrien if ((vp = adrof(exp_name)) != NULL) { 112859243Sobrien if ((ptr = vp->vec[0]) == NULL || *ptr == '\0' || 112959243Sobrien vp->vec[1] != NULL) 113059243Sobrien return ' '; 113159243Sobrien } 113259243Sobrien else if ((ptr = tgetenv(exp_name)) == NULL || *ptr == '\0') 113359243Sobrien return ' '; 113459243Sobrien 113559243Sobrien *--target = '\0'; 113659243Sobrien 113759243Sobrien return isadirectory(exp_dir, ptr) ? '/' : ' '; 113859243Sobrien 113959243Sobrien 114059243Sobrien case TW_DIRECTORY: 114159243Sobrien return '/'; 114259243Sobrien 114359243Sobrien case TW_COMMAND: 114459243Sobrien case TW_FILE: 114559243Sobrien return isadirectory(exp_dir, exp_name) ? '/' : ' '; 114659243Sobrien 114759243Sobrien case TW_ALIAS: 114859243Sobrien case TW_VARLIST: 114959243Sobrien case TW_WORDLIST: 115059243Sobrien case TW_SHELLVAR: 115159243Sobrien case TW_ENVVAR: 115259243Sobrien case TW_USER: 115359243Sobrien case TW_BINDING: 115459243Sobrien case TW_LIMIT: 115559243Sobrien case TW_SIGNAL: 115659243Sobrien case TW_JOB: 115759243Sobrien case TW_COMPLETION: 115859243Sobrien case TW_TEXT: 115959243Sobrien case TW_GRPNAME: 116059243Sobrien return ' '; 116159243Sobrien 116259243Sobrien default: 116359243Sobrien return '\0'; 116459243Sobrien } 116559243Sobrien} /* end tw_suffix */ 116659243Sobrien 116759243Sobrien 116859243Sobrien/* tw_fixword(): 116959243Sobrien * Repair a word after a spalling or a recognizwe 117059243Sobrien */ 117159243Sobrienstatic void 117259243Sobrientw_fixword(looking, word, dir, exp_name, max_word_length) 117359243Sobrien int looking; 117459243Sobrien Char *word, *dir, *exp_name; 117559243Sobrien int max_word_length; 117659243Sobrien{ 117759243Sobrien Char *ptr; 117859243Sobrien 117959243Sobrien switch (looking) { 118059243Sobrien case TW_LOGNAME: 118159243Sobrien copyn(word, STRtilde, 1); 118259243Sobrien break; 118359243Sobrien 118459243Sobrien case TW_VARIABLE: 118559243Sobrien if ((ptr = Strrchr(word, '$')) != NULL) 118659243Sobrien *++ptr = '\0'; /* Delete after the dollar */ 118759243Sobrien else 118859243Sobrien word[0] = '\0'; 118959243Sobrien break; 119059243Sobrien 119159243Sobrien case TW_DIRECTORY: 119259243Sobrien case TW_FILE: 119359243Sobrien case TW_TEXT: 119459243Sobrien copyn(word, dir, max_word_length); /* put back dir part */ 119559243Sobrien break; 119659243Sobrien 119759243Sobrien default: 119859243Sobrien word[0] = '\0'; 119959243Sobrien break; 120059243Sobrien } 120159243Sobrien 120259243Sobrien (void) quote(exp_name); 120359243Sobrien catn(word, exp_name, max_word_length); /* add extended name */ 120459243Sobrien} /* end tw_fixword */ 120559243Sobrien 120659243Sobrien 120759243Sobrien/* tw_collect(): 120859243Sobrien * Collect items. Return -1 in case we were interrupted or 120959243Sobrien * the return value of tw_collect 121059243Sobrien * This is really a wrapper for tw_collect_items, serving two 121159243Sobrien * purposes: 121259243Sobrien * 1. Handles interrupt cleanups. 121359243Sobrien * 2. Retries if we had no matches, but there were ignored matches 121459243Sobrien */ 121559243Sobrienstatic int 121659243Sobrientw_collect(command, looking, exp_dir, exp_name, target, pat, flags, dir_fd) 121759243Sobrien COMMAND command; 121859243Sobrien int looking; 121959243Sobrien Char *exp_dir, *exp_name, **target, *pat; 122059243Sobrien int flags; 122159243Sobrien DIR *dir_fd; 122259243Sobrien{ 122359243Sobrien static int ni; /* static so we don't get clobbered */ 122459243Sobrien jmp_buf_t osetexit; 122559243Sobrien 122659243Sobrien#ifdef TDEBUG 122759243Sobrien xprintf("target = %S\n", *target); 122859243Sobrien#endif 122959243Sobrien ni = 0; 123059243Sobrien getexit(osetexit); 123159243Sobrien for (;;) { 123259243Sobrien (*tw_start_entry[looking])(dir_fd, pat); 123359243Sobrien InsideCompletion = 1; 123459243Sobrien if (setexit()) { 123559243Sobrien /* interrupted, clean up */ 123659243Sobrien resexit(osetexit); 123759243Sobrien InsideCompletion = 0; 123859243Sobrien haderr = 0; 123959243Sobrien 124059243Sobrien#if defined(SOLARIS2) && defined(i386) && !defined(__GNUC__) 124159243Sobrien /* Compiler bug? (from PWP) */ 124259243Sobrien if ((looking == TW_LOGNAME) || (looking == TW_USER)) 124359243Sobrien tw_logname_end(); 124459243Sobrien else 124559243Sobrien if (looking == TW_GRPNAME) 124659243Sobrien tw_grpname_end(); 124759243Sobrien else 124859243Sobrien tw_dir_end(); 124959243Sobrien#else /* !(SOLARIS2 && i386 && !__GNUC__) */ 125059243Sobrien (*tw_end_entry[looking])(); 125159243Sobrien#endif /* !(SOLARIS2 && i386 && !__GNUC__) */ 125259243Sobrien 125359243Sobrien /* flag error */ 125459243Sobrien return(-1); 125559243Sobrien } 125659243Sobrien if ((ni = tw_collect_items(command, looking, exp_dir, exp_name, 125759243Sobrien *target, pat, 125859243Sobrien ni >= 0 ? flags : 125959243Sobrien flags & ~TW_IGN_OK)) >= 0) { 126059243Sobrien resexit(osetexit); 126159243Sobrien InsideCompletion = 0; 126259243Sobrien 126359243Sobrien#if defined(SOLARIS2) && defined(i386) && !defined(__GNUC__) 126459243Sobrien /* Compiler bug? (from PWP) */ 126559243Sobrien if ((looking == TW_LOGNAME) || (looking == TW_USER)) 126659243Sobrien tw_logname_end(); 126759243Sobrien else 126859243Sobrien if (looking == TW_GRPNAME) 126959243Sobrien tw_grpname_end(); 127059243Sobrien else 127159243Sobrien tw_dir_end(); 127259243Sobrien#else /* !(SOLARIS2 && i386 && !__GNUC__) */ 127359243Sobrien (*tw_end_entry[looking])(); 127459243Sobrien#endif /* !(SOLARIS2 && i386 && !__GNUC__) */ 127559243Sobrien 127659243Sobrien return(ni); 127759243Sobrien } 127859243Sobrien } 127959243Sobrien} /* end tw_collect */ 128059243Sobrien 128159243Sobrien 128259243Sobrien/* tw_list_items(): 128359243Sobrien * List the items that were found 128459243Sobrien * 128559243Sobrien * NOTE instead of looking at numerical vars listmax and listmaxrows 128659243Sobrien * we can look at numerical var listmax, and have a string value 128759243Sobrien * listmaxtype (or similar) than can have values 'items' and 'rows' 128859243Sobrien * (by default interpreted as 'items', for backwards compatibility) 128959243Sobrien */ 129059243Sobrienstatic void 129159243Sobrientw_list_items(looking, numitems, list_max) 129259243Sobrien int looking, numitems, list_max; 129359243Sobrien{ 129459243Sobrien Char *ptr; 129559243Sobrien int max_items = 0; 129659243Sobrien int max_rows = 0; 129759243Sobrien 129859243Sobrien if ((ptr = varval(STRlistmax)) != STRNULL) { 129959243Sobrien while (*ptr) { 130059243Sobrien if (!Isdigit(*ptr)) { 130159243Sobrien max_items = 0; 130259243Sobrien break; 130359243Sobrien } 130459243Sobrien max_items = max_items * 10 + *ptr++ - '0'; 130559243Sobrien } 130659243Sobrien if ((max_items > 0) && (numitems > max_items) && list_max) 130759243Sobrien max_items = numitems; 130859243Sobrien else 130959243Sobrien max_items = 0; 131059243Sobrien } 131159243Sobrien 131259243Sobrien if (max_items == 0 && (ptr = varval(STRlistmaxrows)) != STRNULL) { 131359243Sobrien int rows; 131459243Sobrien 131559243Sobrien while (*ptr) { 131659243Sobrien if (!Isdigit(*ptr)) { 131759243Sobrien max_rows = 0; 131859243Sobrien break; 131959243Sobrien } 132059243Sobrien max_rows = max_rows * 10 + *ptr++ - '0'; 132159243Sobrien } 132259243Sobrien if (max_rows != 0 && looking != TW_JOB) 132359243Sobrien rows = find_rows(tw_item_get(), numitems, TRUE); 132459243Sobrien else 132559243Sobrien rows = numitems; /* underestimate for lines wider than the termH */ 132659243Sobrien if ((max_rows > 0) && (rows > max_rows) && list_max) 132759243Sobrien max_rows = rows; 132859243Sobrien else 132959243Sobrien max_rows = 0; 133059243Sobrien } 133159243Sobrien 133259243Sobrien 133359243Sobrien if (max_items || max_rows) { 133459243Sobrien char tc; 133559243Sobrien const char *name; 133659243Sobrien int maxs; 133759243Sobrien 133859243Sobrien if (max_items) { 133959243Sobrien name = CGETS(30, 5, "items"); 134059243Sobrien maxs = max_items; 134159243Sobrien } 134259243Sobrien else { 134359243Sobrien name = CGETS(30, 6, "rows"); 134459243Sobrien maxs = max_rows; 134559243Sobrien } 134659243Sobrien 134759243Sobrien xprintf(CGETS(30, 7, "There are %d %s, list them anyway? [n/y] "), 134859243Sobrien maxs, name); 134959243Sobrien flush(); 135059243Sobrien /* We should be in Rawmode here, so no \n to catch */ 135159243Sobrien (void) read(SHIN, &tc, 1); 135259243Sobrien xprintf("%c\r\n", tc); /* echo the char, do a newline */ 135359243Sobrien /* 135459243Sobrien * Perhaps we should use the yesexpr from the 135559243Sobrien * actual locale 135659243Sobrien */ 135759243Sobrien if (strchr(CGETS(30, 13, "Yy"), tc) == NULL) 135859243Sobrien return; 135959243Sobrien } 136059243Sobrien 136159243Sobrien if (looking != TW_SIGNAL) 136259243Sobrien qsort((ptr_t) tw_item_get(), (size_t) numitems, sizeof(Char *), 136359243Sobrien (int (*) __P((const void *, const void *))) fcompare); 136459243Sobrien if (looking != TW_JOB) 136559243Sobrien print_by_column(STRNULL, tw_item_get(), numitems, TRUE); 136659243Sobrien else { 136759243Sobrien /* 136859243Sobrien * print one item on every line because jobs can have spaces 136959243Sobrien * and it is confusing. 137059243Sobrien */ 137159243Sobrien int i; 137259243Sobrien Char **w = tw_item_get(); 137359243Sobrien 137459243Sobrien for (i = 0; i < numitems; i++) { 137559243Sobrien xprintf("%S", w[i]); 137659243Sobrien if (Tty_raw_mode) 137759243Sobrien xputchar('\r'); 137859243Sobrien xputchar('\n'); 137959243Sobrien } 138059243Sobrien } 138159243Sobrien} /* end tw_list_items */ 138259243Sobrien 138359243Sobrien 138459243Sobrien/* t_search(): 138559243Sobrien * Perform a RECOGNIZE, LIST or SPELL command on string "word". 138659243Sobrien * 138759243Sobrien * Return value: 138859243Sobrien * >= 0: SPELL command: "distance" (see spdist()) 138959243Sobrien * other: No. of items found 139059243Sobrien * < 0: Error (message or beep is output) 139159243Sobrien */ 139259243Sobrien/*ARGSUSED*/ 139359243Sobrienint 139459243Sobrient_search(word, wp, command, max_word_length, looking, list_max, pat, suf) 139559243Sobrien Char *word, *wp; /* original end-of-word */ 139659243Sobrien COMMAND command; 139759243Sobrien int max_word_length, looking, list_max; 139859243Sobrien Char *pat; 139959243Sobrien int suf; 140059243Sobrien{ 140159243Sobrien int numitems, /* Number of items matched */ 140259243Sobrien flags = 0, /* search flags */ 140359243Sobrien gpat = pat[0] != '\0', /* Glob pattern search */ 140459243Sobrien nd; /* Normalized directory return */ 140559243Sobrien Char exp_dir[FILSIZ + 1], /* dir after ~ expansion */ 140659243Sobrien dir[FILSIZ + 1], /* /x/y/z/ part in /x/y/z/f */ 140759243Sobrien exp_name[MAXNAMLEN + 1], /* the recognized (extended) */ 140859243Sobrien name[MAXNAMLEN + 1], /* f part in /d/d/d/f name */ 140959243Sobrien *target; /* Target to expand/correct/list */ 141059243Sobrien DIR *dir_fd = NULL; 141159243Sobrien 141259243Sobrien USE(wp); 141359243Sobrien 141459243Sobrien /* 141559243Sobrien * bugfix by Marty Grossman (grossman@CC5.BBN.COM): directory listing can 141659243Sobrien * dump core when interrupted 141759243Sobrien */ 141859243Sobrien tw_item_free(); 141959243Sobrien 142059243Sobrien non_unique_match = FALSE; /* See the recexact code below */ 142159243Sobrien 142259243Sobrien extract_dir_and_name(word, dir, name); 142359243Sobrien 142459243Sobrien /* 142559243Sobrien * SPECIAL HARDCODED COMPLETIONS: 142659243Sobrien * foo$variable -> TW_VARIABLE 142759243Sobrien * ~user -> TW_LOGNAME 142859243Sobrien * 142959243Sobrien */ 143059243Sobrien if ((*word == '~') && (Strchr(word, '/') == NULL)) { 143159243Sobrien looking = TW_LOGNAME; 143259243Sobrien target = name; 143359243Sobrien gpat = 0; /* Override pattern mechanism */ 143459243Sobrien } 143559243Sobrien else if ((target = Strrchr(name, '$')) != 0 && 143659243Sobrien (Strchr(name, '/') == NULL)) { 143759243Sobrien target++; 143859243Sobrien looking = TW_VARIABLE; 143959243Sobrien gpat = 0; /* Override pattern mechanism */ 144059243Sobrien } 144159243Sobrien else 144259243Sobrien target = name; 144359243Sobrien 144459243Sobrien /* 144559243Sobrien * Try to figure out what we should be looking for 144659243Sobrien */ 144759243Sobrien if (looking & TW_PATH) { 144859243Sobrien gpat = 0; /* pattern holds the pathname to be used */ 144959243Sobrien copyn(exp_dir, pat, MAXNAMLEN); 145059243Sobrien if (exp_dir[Strlen(exp_dir) - 1] != '/') 145159243Sobrien catn(exp_dir, STRslash, MAXNAMLEN); 145259243Sobrien catn(exp_dir, dir, MAXNAMLEN); 145359243Sobrien } 145459243Sobrien else 145559243Sobrien exp_dir[0] = '\0'; 145659243Sobrien 145759243Sobrien switch (looking & ~TW_PATH) { 145859243Sobrien case TW_NONE: 145959243Sobrien return -1; 146059243Sobrien 146159243Sobrien case TW_ZERO: 146259243Sobrien looking = TW_FILE; 146359243Sobrien break; 146459243Sobrien 146559243Sobrien case TW_COMMAND: 146659243Sobrien if (Strchr(word, '/') || (looking & TW_PATH)) { 146759243Sobrien looking = TW_FILE; 146859243Sobrien flags |= TW_EXEC_CHK; 146959243Sobrien flags |= TW_DIR_OK; 147059243Sobrien } 147159243Sobrien#ifdef notdef 147259243Sobrien /* PWP: don't even bother when doing ALL of the commands */ 147359243Sobrien if (looking == TW_COMMAND && (*word == '\0')) 147459243Sobrien return (-1); 147559243Sobrien#endif 147659243Sobrien break; 147759243Sobrien 147859243Sobrien 147959243Sobrien case TW_VARLIST: 148059243Sobrien case TW_WORDLIST: 148159243Sobrien gpat = 0; /* pattern holds the name of the variable */ 148259243Sobrien break; 148359243Sobrien 148459243Sobrien case TW_EXPLAIN: 148559243Sobrien if (command == LIST && pat != NULL) { 148659243Sobrien xprintf("%S", pat); 148759243Sobrien if (Tty_raw_mode) 148859243Sobrien xputchar('\r'); 148959243Sobrien xputchar('\n'); 149059243Sobrien } 149159243Sobrien return 2; 149259243Sobrien 149359243Sobrien default: 149459243Sobrien break; 149559243Sobrien } 149659243Sobrien 149759243Sobrien /* 149859243Sobrien * let fignore work only when we are not using a pattern 149959243Sobrien */ 150059243Sobrien flags |= (gpat == 0) ? TW_IGN_OK : TW_PAT_OK; 150159243Sobrien 150259243Sobrien#ifdef TDEBUG 150359243Sobrien xprintf(CGETS(30, 8, "looking = %d\n"), looking); 150459243Sobrien#endif 150559243Sobrien 150659243Sobrien switch (looking) { 150759243Sobrien case TW_ALIAS: 150859243Sobrien case TW_SHELLVAR: 150959243Sobrien case TW_ENVVAR: 151059243Sobrien case TW_BINDING: 151159243Sobrien case TW_LIMIT: 151259243Sobrien case TW_SIGNAL: 151359243Sobrien case TW_JOB: 151459243Sobrien case TW_COMPLETION: 151559243Sobrien case TW_GRPNAME: 151659243Sobrien break; 151759243Sobrien 151859243Sobrien 151959243Sobrien case TW_VARIABLE: 152059243Sobrien if ((nd = expand_dir(dir, exp_dir, &dir_fd, command)) != 0) 152159243Sobrien return nd; 152259243Sobrien break; 152359243Sobrien 152459243Sobrien case TW_DIRECTORY: 152559243Sobrien flags |= TW_DIR_CHK; 152659243Sobrien 152759243Sobrien#ifdef notyet 152859243Sobrien /* 152959243Sobrien * This is supposed to expand the directory stack. 153059243Sobrien * Problems: 153159243Sobrien * 1. Slow 153259243Sobrien * 2. directories with the same name 153359243Sobrien */ 153459243Sobrien flags |= TW_DIR_OK; 153559243Sobrien#endif 153659243Sobrien#ifdef notyet 153759243Sobrien /* 153859243Sobrien * Supposed to do delayed expansion, but it is inconsistent 153959243Sobrien * from a user-interface point of view, since it does not 154059243Sobrien * immediately obey addsuffix 154159243Sobrien */ 154259243Sobrien if ((nd = expand_dir(dir, exp_dir, &dir_fd, command)) != 0) 154359243Sobrien return nd; 154459243Sobrien if (isadirectory(exp_dir, name)) { 154559243Sobrien if (exp_dir[0] != '\0' || name[0] != '\0') { 154659243Sobrien catn(dir, name, MAXNAMLEN); 154759243Sobrien if (dir[Strlen(dir) - 1] != '/') 154859243Sobrien catn(dir, STRslash, MAXNAMLEN); 154959243Sobrien if ((nd = expand_dir(dir, exp_dir, &dir_fd, command)) != 0) 155059243Sobrien return nd; 155159243Sobrien if (word[Strlen(word) - 1] != '/') 155259243Sobrien catn(word, STRslash, MAXNAMLEN); 155359243Sobrien name[0] = '\0'; 155459243Sobrien } 155559243Sobrien } 155659243Sobrien#endif 155759243Sobrien if ((nd = expand_dir(dir, exp_dir, &dir_fd, command)) != 0) 155859243Sobrien return nd; 155959243Sobrien break; 156059243Sobrien 156159243Sobrien case TW_TEXT: 156259243Sobrien flags |= TW_TEXT_CHK; 156359243Sobrien /*FALLTHROUGH*/ 156459243Sobrien case TW_FILE: 156559243Sobrien if ((nd = expand_dir(dir, exp_dir, &dir_fd, command)) != 0) 156659243Sobrien return nd; 156759243Sobrien break; 156859243Sobrien 156959243Sobrien case TW_PATH | TW_TEXT: 157059243Sobrien case TW_PATH | TW_FILE: 157159243Sobrien case TW_PATH | TW_DIRECTORY: 157259243Sobrien case TW_PATH | TW_COMMAND: 157359243Sobrien if ((dir_fd = opendir(short2str(exp_dir))) == NULL) { 157459243Sobrien xprintf("%S: %s\n", exp_dir, strerror(errno)); 157559243Sobrien return -1; 157659243Sobrien } 157759243Sobrien if (exp_dir[Strlen(exp_dir) - 1] != '/') 157859243Sobrien catn(exp_dir, STRslash, MAXNAMLEN); 157959243Sobrien 158059243Sobrien looking &= ~TW_PATH; 158159243Sobrien 158259243Sobrien switch (looking) { 158359243Sobrien case TW_TEXT: 158459243Sobrien flags |= TW_TEXT_CHK; 158559243Sobrien break; 158659243Sobrien 158759243Sobrien case TW_FILE: 158859243Sobrien break; 158959243Sobrien 159059243Sobrien case TW_DIRECTORY: 159159243Sobrien flags |= TW_DIR_CHK; 159259243Sobrien break; 159359243Sobrien 159459243Sobrien case TW_COMMAND: 159559243Sobrien copyn(target, word, MAXNAMLEN); /* so it can match things */ 159659243Sobrien break; 159759243Sobrien 159859243Sobrien default: 159959243Sobrien abort(); /* Cannot happen */ 160059243Sobrien break; 160159243Sobrien } 160259243Sobrien break; 160359243Sobrien 160459243Sobrien case TW_LOGNAME: 160559243Sobrien word++; 160659243Sobrien /*FALLTHROUGH*/ 160759243Sobrien case TW_USER: 160859243Sobrien /* 160959243Sobrien * Check if the spelling was already correct 161059243Sobrien * From: Rob McMahon <cudcv@cu.warwick.ac.uk> 161159243Sobrien */ 161259243Sobrien if (command == SPELL && getpwnam(short2str(word)) != NULL) { 161359243Sobrien#ifdef YPBUGS 161459243Sobrien fix_yp_bugs(); 161559243Sobrien#endif /* YPBUGS */ 161659243Sobrien return (0); 161759243Sobrien } 161859243Sobrien copyn(name, word, MAXNAMLEN); /* name sans ~ */ 161959243Sobrien if (looking == TW_LOGNAME) 162059243Sobrien word--; 162159243Sobrien break; 162259243Sobrien 162359243Sobrien case TW_COMMAND: 162459243Sobrien case TW_VARLIST: 162559243Sobrien case TW_WORDLIST: 162659243Sobrien copyn(target, word, MAXNAMLEN); /* so it can match things */ 162759243Sobrien break; 162859243Sobrien 162959243Sobrien default: 163059243Sobrien xprintf(CGETS(30, 9, 163159243Sobrien "\n%s internal error: I don't know what I'm looking for!\n"), 163259243Sobrien progname); 163359243Sobrien NeedsRedraw = 1; 163459243Sobrien return (-1); 163559243Sobrien } 163659243Sobrien 163759243Sobrien numitems = tw_collect(command, looking, exp_dir, exp_name, 163859243Sobrien &target, pat, flags, dir_fd); 163959243Sobrien if (numitems == -1) 164059243Sobrien return -1; 164159243Sobrien 164259243Sobrien switch (command) { 164359243Sobrien case RECOGNIZE: 164459243Sobrien case RECOGNIZE_ALL: 164559243Sobrien case RECOGNIZE_SCROLL: 164659243Sobrien if (numitems <= 0) 164759243Sobrien return (numitems); 164859243Sobrien 164959243Sobrien tw_fixword(looking, word, dir, exp_name, max_word_length); 165059243Sobrien 165159243Sobrien if (!match_unique_match && is_set(STRaddsuffix) && numitems == 1) { 165259243Sobrien Char suffix[2]; 165359243Sobrien 165459243Sobrien suffix[1] = '\0'; 165559243Sobrien switch (suf) { 165659243Sobrien case 0: /* Automatic suffix */ 165759243Sobrien suffix[0] = tw_suffix(looking, exp_dir, exp_name, target, name); 165859243Sobrien break; 165959243Sobrien 166059243Sobrien case -1: /* No suffix */ 166159243Sobrien return numitems; 166259243Sobrien 166359243Sobrien default: /* completion specified suffix */ 166459243Sobrien suffix[0] = (Char) suf; 166559243Sobrien break; 166659243Sobrien } 166759243Sobrien catn(word, suffix, max_word_length); 166859243Sobrien } 166959243Sobrien return numitems; 167059243Sobrien 167159243Sobrien case LIST: 167259243Sobrien tw_list_items(looking, numitems, list_max); 167359243Sobrien tw_item_free(); 167459243Sobrien return (numitems); 167559243Sobrien 167659243Sobrien case SPELL: 167759243Sobrien tw_fixword(looking, word, dir, exp_name, max_word_length); 167859243Sobrien return (numitems); 167959243Sobrien 168059243Sobrien default: 168159243Sobrien xprintf("Bad tw_command\n"); 168259243Sobrien return (0); 168359243Sobrien } 168459243Sobrien} /* end t_search */ 168559243Sobrien 168659243Sobrien 168759243Sobrien/* extract_dir_and_name(): 168859243Sobrien * parse full path in file into 2 parts: directory and file names 168959243Sobrien * Should leave final slash (/) at end of dir. 169059243Sobrien */ 169159243Sobrienstatic void 169259243Sobrienextract_dir_and_name(path, dir, name) 169359243Sobrien Char *path, *dir, *name; 169459243Sobrien{ 169559243Sobrien register Char *p; 169659243Sobrien 169759243Sobrien p = Strrchr(path, '/'); 169859243Sobrien#ifdef WINNT 169959243Sobrien if (p == NULL) 170059243Sobrien p = Strrchr(path, ':'); 170159243Sobrien#endif /* WINNT */ 170259243Sobrien if (p == NULL) { 170359243Sobrien copyn(name, path, MAXNAMLEN); 170459243Sobrien dir[0] = '\0'; 170559243Sobrien } 170659243Sobrien else { 170759243Sobrien p++; 170859243Sobrien copyn(name, p, MAXNAMLEN); 170959243Sobrien copyn(dir, path, p - path); 171059243Sobrien } 171159243Sobrien} /* end extract_dir_and_name */ 171259243Sobrien 171359243Sobrien 171459243Sobrien/* dollar(): 171559243Sobrien * expand "/$old1/$old2/old3/" 171659243Sobrien * to "/value_of_old1/value_of_old2/old3/" 171759243Sobrien */ 171859243SobrienChar * 171959243Sobriendollar(new, old) 172059243Sobrien Char *new; 172159243Sobrien const Char *old; 172259243Sobrien{ 172359243Sobrien Char *p; 172459243Sobrien size_t space; 172559243Sobrien 172659243Sobrien for (space = FILSIZ, p = new; *old && space > 0;) 172759243Sobrien if (*old != '$') { 172859243Sobrien *p++ = *old++; 172959243Sobrien space--; 173059243Sobrien } 173159243Sobrien else { 173259243Sobrien if (expdollar(&p, &old, &space, QUOTE) == NULL) 173359243Sobrien return NULL; 173459243Sobrien } 173559243Sobrien *p = '\0'; 173659243Sobrien return (new); 173759243Sobrien} /* end dollar */ 173859243Sobrien 173959243Sobrien 174059243Sobrien/* tilde(): 174159243Sobrien * expand ~person/foo to home_directory_of_person/foo 174259243Sobrien * or =<stack-entry> to <dir in stack entry> 174359243Sobrien */ 174459243Sobrienstatic Char * 174559243Sobrientilde(new, old) 174659243Sobrien Char *new, *old; 174759243Sobrien{ 174859243Sobrien register Char *o, *p; 174959243Sobrien 175059243Sobrien switch (old[0]) { 175159243Sobrien case '~': 175259243Sobrien for (p = new, o = &old[1]; *o && *o != '/'; *p++ = *o++) 175359243Sobrien continue; 175459243Sobrien *p = '\0'; 175559243Sobrien if (gethdir(new)) { 175659243Sobrien new[0] = '\0'; 175759243Sobrien return NULL; 175859243Sobrien } 175959243Sobrien (void) Strcat(new, o); 176059243Sobrien return new; 176159243Sobrien 176259243Sobrien case '=': 176359243Sobrien if ((p = globequal(new, old)) == NULL) { 176459243Sobrien *new = '\0'; 176559243Sobrien return NULL; 176659243Sobrien } 176759243Sobrien if (p == new) 176859243Sobrien return new; 176959243Sobrien /*FALLTHROUGH*/ 177059243Sobrien 177159243Sobrien default: 177259243Sobrien (void) Strcpy(new, old); 177359243Sobrien return new; 177459243Sobrien } 177559243Sobrien} /* end tilde */ 177659243Sobrien 177759243Sobrien 177859243Sobrien/* expand_dir(): 177959243Sobrien * Open the directory given, expanding ~user and $var 178059243Sobrien * Optionally normalize the path given 178159243Sobrien */ 178259243Sobrienstatic int 178359243Sobrienexpand_dir(dir, edir, dfd, cmd) 178459243Sobrien Char *dir, *edir; 178559243Sobrien DIR **dfd; 178659243Sobrien COMMAND cmd; 178759243Sobrien{ 178859243Sobrien Char *nd = NULL; 178959243Sobrien Char tdir[MAXPATHLEN + 1]; 179059243Sobrien 179159243Sobrien if ((dollar(tdir, dir) == 0) || 179259243Sobrien (tilde(edir, tdir) == 0) || 179359243Sobrien !(nd = dnormalize(*edir ? edir : STRdot, symlinks == SYM_IGNORE || 179459243Sobrien symlinks == SYM_EXPAND)) || 179559243Sobrien ((*dfd = opendir(short2str(nd))) == NULL)) { 179659243Sobrien xfree((ptr_t) nd); 179759243Sobrien if (cmd == SPELL || SearchNoDirErr) 179859243Sobrien return (-2); 179959243Sobrien /* 180059243Sobrien * From: Amos Shapira <amoss@cs.huji.ac.il> 180159243Sobrien * Print a better message when completion fails 180259243Sobrien */ 180359243Sobrien xprintf("\n%S %s\n", 180459243Sobrien *edir ? edir : 180559243Sobrien (*tdir ? tdir : dir), 180659243Sobrien (errno == ENOTDIR ? CGETS(30, 10, "not a directory") : 180759243Sobrien (errno == ENOENT ? CGETS(30, 11, "not found") : 180859243Sobrien CGETS(30, 12, "unreadable")))); 180959243Sobrien NeedsRedraw = 1; 181059243Sobrien return (-1); 181159243Sobrien } 181259243Sobrien if (nd) { 181359243Sobrien if (*dir != '\0') { 181459243Sobrien Char *s, *d, *p; 181559243Sobrien 181659243Sobrien /* 181759243Sobrien * Copy and append a / if there was one 181859243Sobrien */ 181959243Sobrien for (p = edir; *p; p++) 182059243Sobrien continue; 182159243Sobrien if (*--p == '/') { 182259243Sobrien for (p = nd; *p; p++) 182359243Sobrien continue; 182459243Sobrien if (*--p != '/') 182559243Sobrien p = NULL; 182659243Sobrien } 182759243Sobrien for (d = edir, s = nd; (*d++ = *s++) != '\0';) 182859243Sobrien continue; 182959243Sobrien if (!p) { 183059243Sobrien *d-- = '\0'; 183159243Sobrien *d = '/'; 183259243Sobrien } 183359243Sobrien } 183459243Sobrien xfree((ptr_t) nd); 183559243Sobrien } 183659243Sobrien return 0; 183759243Sobrien} /* end expand_dir */ 183859243Sobrien 183959243Sobrien 184059243Sobrien/* nostat(): 184159243Sobrien * Returns true if the directory should not be stat'd, 184259243Sobrien * false otherwise. 184359243Sobrien * This way, things won't grind to a halt when you complete in /afs 184459243Sobrien * or very large directories. 184559243Sobrien */ 184659243Sobrienstatic bool 184759243Sobriennostat(dir) 184859243Sobrien Char *dir; 184959243Sobrien{ 185059243Sobrien struct varent *vp; 185159243Sobrien register Char **cp; 185259243Sobrien 185359243Sobrien if ((vp = adrof(STRnostat)) == NULL || (cp = vp->vec) == NULL) 185459243Sobrien return FALSE; 185559243Sobrien for (; *cp != NULL; cp++) { 185659243Sobrien if (Strcmp(*cp, STRstar) == 0) 185759243Sobrien return TRUE; 185859243Sobrien if (Gmatch(dir, *cp)) 185959243Sobrien return TRUE; 186059243Sobrien } 186159243Sobrien return FALSE; 186259243Sobrien} /* end nostat */ 186359243Sobrien 186459243Sobrien 186559243Sobrien/* filetype(): 186659243Sobrien * Return a character that signifies a filetype 186759243Sobrien * symbology from 4.3 ls command. 186859243Sobrien */ 186959243Sobrienstatic Char 187059243Sobrienfiletype(dir, file) 187159243Sobrien Char *dir, *file; 187259243Sobrien{ 187359243Sobrien if (dir) { 187459243Sobrien Char path[512]; 187559243Sobrien char *ptr; 187659243Sobrien struct stat statb; 187759243Sobrien#ifdef S_ISCDF 187859243Sobrien /* 187959243Sobrien * From: veals@crchh84d.bnr.ca (Percy Veals) 188059243Sobrien * An extra stat is required for HPUX CDF files. 188159243Sobrien */ 188259243Sobrien struct stat hpstatb; 188359243Sobrien#endif /* S_ISCDF */ 188459243Sobrien 188559243Sobrien if (nostat(dir)) return(' '); 188659243Sobrien 188759243Sobrien (void) Strcpy(path, dir); 188859243Sobrien catn(path, file, (int) (sizeof(path) / sizeof(Char))); 188959243Sobrien 189059243Sobrien if (lstat(ptr = short2str(path), &statb) != -1) 189159243Sobrien /* see above #define of lstat */ 189259243Sobrien { 189359243Sobrien#ifdef S_ISLNK 189459243Sobrien if (S_ISLNK(statb.st_mode)) { /* Symbolic link */ 189559243Sobrien if (adrof(STRlistlinks)) { 189659243Sobrien if (stat(ptr, &statb) == -1) 189759243Sobrien return ('&'); 189859243Sobrien else if (S_ISDIR(statb.st_mode)) 189959243Sobrien return ('>'); 190059243Sobrien else 190159243Sobrien return ('@'); 190259243Sobrien } 190359243Sobrien else 190459243Sobrien return ('@'); 190559243Sobrien } 190659243Sobrien#endif 190759243Sobrien#ifdef S_ISSOCK 190859243Sobrien if (S_ISSOCK(statb.st_mode)) /* Socket */ 190959243Sobrien return ('='); 191059243Sobrien#endif 191159243Sobrien#ifdef S_ISFIFO 191259243Sobrien if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ 191359243Sobrien return ('|'); 191459243Sobrien#endif 191559243Sobrien#ifdef S_ISHIDDEN 191659243Sobrien if (S_ISHIDDEN(statb.st_mode)) /* Hidden Directory [aix] */ 191759243Sobrien return ('+'); 191859243Sobrien#endif 191959243Sobrien#ifdef S_ISCDF 192059243Sobrien (void) strcat(ptr, "+"); /* Must append a '+' and re-stat(). */ 192159243Sobrien if ((stat(ptr, &hpstatb) != -1) && S_ISCDF(hpstatb.st_mode)) 192259243Sobrien return ('+'); /* Context Dependent Files [hpux] */ 192359243Sobrien#endif 192459243Sobrien#ifdef S_ISNWK 192559243Sobrien if (S_ISNWK(statb.st_mode)) /* Network Special [hpux] */ 192659243Sobrien return (':'); 192759243Sobrien#endif 192859243Sobrien#ifdef S_ISCHR 192959243Sobrien if (S_ISCHR(statb.st_mode)) /* char device */ 193059243Sobrien return ('%'); 193159243Sobrien#endif 193259243Sobrien#ifdef S_ISBLK 193359243Sobrien if (S_ISBLK(statb.st_mode)) /* block device */ 193459243Sobrien return ('#'); 193559243Sobrien#endif 193659243Sobrien#ifdef S_ISDIR 193759243Sobrien if (S_ISDIR(statb.st_mode)) /* normal Directory */ 193859243Sobrien return ('/'); 193959243Sobrien#endif 194059243Sobrien if (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) 194159243Sobrien return ('*'); 194259243Sobrien } 194359243Sobrien } 194459243Sobrien return (' '); 194559243Sobrien} /* end filetype */ 194659243Sobrien 194759243Sobrien 194859243Sobrien/* isadirectory(): 194959243Sobrien * Return trus if the file is a directory 195059243Sobrien */ 195159243Sobrienstatic int 195259243Sobrienisadirectory(dir, file) /* return 1 if dir/file is a directory */ 195359243Sobrien Char *dir, *file; /* uses stat rather than lstat to get dest. */ 195459243Sobrien{ 195559243Sobrien if (dir) { 195659243Sobrien Char path[MAXPATHLEN]; 195759243Sobrien struct stat statb; 195859243Sobrien 195959243Sobrien (void) Strcpy(path, dir); 196059243Sobrien catn(path, file, (int) (sizeof(path) / sizeof(Char))); 196159243Sobrien if (stat(short2str(path), &statb) >= 0) { /* resolve through 196259243Sobrien * symlink */ 196359243Sobrien#ifdef S_ISSOCK 196459243Sobrien if (S_ISSOCK(statb.st_mode)) /* Socket */ 196559243Sobrien return 0; 196659243Sobrien#endif 196759243Sobrien#ifdef S_ISFIFO 196859243Sobrien if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ 196959243Sobrien return 0; 197059243Sobrien#endif 197159243Sobrien if (S_ISDIR(statb.st_mode)) /* normal Directory */ 197259243Sobrien return 1; 197359243Sobrien } 197459243Sobrien } 197559243Sobrien return 0; 197659243Sobrien} /* end isadirectory */ 197759243Sobrien 197859243Sobrien 197959243Sobrien 198059243Sobrien/* find_rows(): 198159243Sobrien * Return how many rows needed to print sorted down columns 198259243Sobrien */ 198359243Sobrienstatic int 198459243Sobrienfind_rows(items, count, no_file_suffix) 198559243Sobrien Char *items[]; 198659243Sobrien int count, no_file_suffix; 198759243Sobrien{ 198859243Sobrien register int i, columns, rows; 198959243Sobrien unsigned int maxwidth = 0; 199059243Sobrien 199159243Sobrien for (i = 0; i < count; i++) /* find widest string */ 199259243Sobrien maxwidth = max(maxwidth, (unsigned int) Strlen(items[i])); 199359243Sobrien 199459243Sobrien maxwidth += no_file_suffix ? 1 : 2; /* for the file tag and space */ 199559243Sobrien columns = (TermH + 1) / maxwidth; /* PWP: terminal size change */ 199659243Sobrien if (!columns) 199759243Sobrien columns = 1; 199859243Sobrien rows = (count + (columns - 1)) / columns; 199959243Sobrien 200059243Sobrien return rows; 200159243Sobrien} /* end rows_needed_by_print_by_column */ 200259243Sobrien 200359243Sobrien 200459243Sobrien/* print_by_column(): 200559243Sobrien * Print sorted down columns or across columns when the first 200659243Sobrien * word of $listflags shell variable contains 'x'. 200759243Sobrien * 200859243Sobrien */ 200959243Sobrienvoid 201059243Sobrienprint_by_column(dir, items, count, no_file_suffix) 201159243Sobrien register Char *dir, *items[]; 201259243Sobrien int count, no_file_suffix; 201359243Sobrien{ 201459243Sobrien register int i, r, c, columns, rows; 201559243Sobrien unsigned int w, maxwidth = 0; 201659243Sobrien Char *val; 201759243Sobrien bool across; 201859243Sobrien 201959243Sobrien lbuffed = 0; /* turn off line buffering */ 202059243Sobrien 202159243Sobrien 202259243Sobrien across = ((val = varval(STRlistflags)) != STRNULL) && 202359243Sobrien (Strchr(val, 'x') != NULL); 202459243Sobrien 202559243Sobrien for (i = 0; i < count; i++) /* find widest string */ 202659243Sobrien maxwidth = max(maxwidth, (unsigned int) Strlen(items[i])); 202759243Sobrien 202859243Sobrien maxwidth += no_file_suffix ? 1 : 2; /* for the file tag and space */ 202959243Sobrien columns = TermH / maxwidth; /* PWP: terminal size change */ 203059243Sobrien if (!columns || !isatty(didfds ? 1 : SHOUT)) 203159243Sobrien columns = 1; 203259243Sobrien rows = (count + (columns - 1)) / columns; 203359243Sobrien 203459243Sobrien i = -1; 203559243Sobrien for (r = 0; r < rows; r++) { 203659243Sobrien for (c = 0; c < columns; c++) { 203759243Sobrien i = across ? (i + 1) : (c * rows + r); 203859243Sobrien 203959243Sobrien if (i < count) { 204059243Sobrien w = (unsigned int) Strlen(items[i]); 204159243Sobrien 204259243Sobrien#ifdef COLOR_LS_F 204359243Sobrien if (no_file_suffix) { 204459243Sobrien /* Print the command name */ 204559243Sobrien Char f = items[i][w - 1]; 204659243Sobrien items[i][w - 1] = 0; 204759243Sobrien print_with_color(items[i], w - 1, f); 204859243Sobrien } 204959243Sobrien else { 205059243Sobrien /* Print filename followed by '/' or '*' or ' ' */ 205159243Sobrien print_with_color(items[i], w, filetype(dir, items[i])); 205259243Sobrien w++; 205359243Sobrien } 205459243Sobrien#else /* ifndef COLOR_LS_F */ 205559243Sobrien if (no_file_suffix) { 205659243Sobrien /* Print the command name */ 205759243Sobrien xprintf("%S", items[i]); 205859243Sobrien } 205959243Sobrien else { 206059243Sobrien /* Print filename followed by '/' or '*' or ' ' */ 206159243Sobrien xprintf("%S%c", items[i], 206259243Sobrien filetype(dir, items[i])); 206359243Sobrien w++; 206459243Sobrien } 206559243Sobrien#endif /* COLOR_LS_F */ 206659243Sobrien 206759243Sobrien if (c < (columns - 1)) /* Not last column? */ 206859243Sobrien for (; w < maxwidth; w++) 206959243Sobrien xputchar(' '); 207059243Sobrien } 207159243Sobrien else if (across) 207259243Sobrien break; 207359243Sobrien } 207459243Sobrien if (Tty_raw_mode) 207559243Sobrien xputchar('\r'); 207659243Sobrien xputchar('\n'); 207759243Sobrien } 207859243Sobrien 207959243Sobrien lbuffed = 1; /* turn back on line buffering */ 208059243Sobrien flush(); 208159243Sobrien} /* end print_by_column */ 208259243Sobrien 208359243Sobrien 208459243Sobrien/* StrQcmp(): 208559243Sobrien * Compare strings ignoring the quoting chars 208659243Sobrien */ 208759243Sobrienint 208859243SobrienStrQcmp(str1, str2) 208959243Sobrien register Char *str1, *str2; 209059243Sobrien{ 209159243Sobrien for (; *str1 && samecase(*str1 & TRIM) == samecase(*str2 & TRIM); 209259243Sobrien str1++, str2++) 209359243Sobrien continue; 209459243Sobrien /* 209559243Sobrien * The following case analysis is necessary so that characters which look 209659243Sobrien * negative collate low against normal characters but high against the 209759243Sobrien * end-of-string NUL. 209859243Sobrien */ 209959243Sobrien if (*str1 == '\0' && *str2 == '\0') 210059243Sobrien return (0); 210159243Sobrien else if (*str1 == '\0') 210259243Sobrien return (-1); 210359243Sobrien else if (*str2 == '\0') 210459243Sobrien return (1); 210559243Sobrien else 210659243Sobrien return ((*str1 & TRIM) - (*str2 & TRIM)); 210759243Sobrien} /* end StrQcmp */ 210859243Sobrien 210959243Sobrien 211059243Sobrien/* fcompare(): 211159243Sobrien * Comparison routine for qsort 211259243Sobrien */ 211359243Sobrienint 211459243Sobrienfcompare(file1, file2) 211559243Sobrien Char **file1, **file2; 211659243Sobrien{ 211759243Sobrien return (int) collate(*file1, *file2); 211859243Sobrien} /* end fcompare */ 211959243Sobrien 212059243Sobrien 212159243Sobrien/* catn(): 212259243Sobrien * Concatenate src onto tail of des. 212359243Sobrien * Des is a string whose maximum length is count. 212459243Sobrien * Always null terminate. 212559243Sobrien */ 212659243Sobrienvoid 212759243Sobriencatn(des, src, count) 212859243Sobrien register Char *des, *src; 212959243Sobrien int count; 213059243Sobrien{ 213159243Sobrien while (--count >= 0 && *des) 213259243Sobrien des++; 213359243Sobrien while (--count >= 0) 213459243Sobrien if ((*des++ = *src++) == 0) 213559243Sobrien return; 213659243Sobrien *des = '\0'; 213759243Sobrien} /* end catn */ 213859243Sobrien 213959243Sobrien 214059243Sobrien/* copyn(): 214159243Sobrien * like strncpy but always leave room for trailing \0 214259243Sobrien * and always null terminate. 214359243Sobrien */ 214459243Sobrienvoid 214559243Sobriencopyn(des, src, count) 214659243Sobrien register Char *des, *src; 214759243Sobrien int count; 214859243Sobrien{ 214959243Sobrien while (--count >= 0) 215059243Sobrien if ((*des++ = *src++) == 0) 215159243Sobrien return; 215259243Sobrien *des = '\0'; 215359243Sobrien} /* end copyn */ 215459243Sobrien 215559243Sobrien 215659243Sobrien/* tgetenv(): 215759243Sobrien * like it's normal string counter-part 215859243Sobrien * [apollo uses that in tc.os.c, so it cannot be static] 215959243Sobrien */ 216059243SobrienChar * 216159243Sobrientgetenv(str) 216259243Sobrien Char *str; 216359243Sobrien{ 216459243Sobrien Char **var; 216559243Sobrien int len, res; 216659243Sobrien 216759243Sobrien len = (int) Strlen(str); 216859243Sobrien /* Search the STR_environ for the entry matching str. */ 216959243Sobrien for (var = STR_environ; var != NULL && *var != NULL; var++) 217059243Sobrien if (Strlen(*var) >= len && (*var)[len] == '=') { 217159243Sobrien /* Temporarily terminate the string so we can copy the variable 217259243Sobrien name. */ 217359243Sobrien (*var)[len] = '\0'; 217459243Sobrien res = StrQcmp(*var, str); 217559243Sobrien /* Restore the '=' and return a pointer to the value of the 217659243Sobrien environment variable. */ 217759243Sobrien (*var)[len] = '='; 217859243Sobrien if (res == 0) 217959243Sobrien return (&((*var)[len + 1])); 218059243Sobrien } 218159243Sobrien return (NULL); 218259243Sobrien} /* end tgetenv */ 218359243Sobrien 218459243Sobrien 218559243Sobrienstruct scroll_tab_list *scroll_tab = 0; 218659243Sobrien 218759243Sobrienstatic void 218859243Sobrienadd_scroll_tab(item) 218959243Sobrien Char *item; 219059243Sobrien{ 219159243Sobrien struct scroll_tab_list *new_scroll; 219259243Sobrien 219359243Sobrien new_scroll = (struct scroll_tab_list *) xmalloc((size_t) 219459243Sobrien sizeof(struct scroll_tab_list)); 219559243Sobrien new_scroll->element = Strsave(item); 219659243Sobrien new_scroll->next = scroll_tab; 219759243Sobrien scroll_tab = new_scroll; 219859243Sobrien} 219959243Sobrien 220059243Sobrienstatic void 220159243Sobrienchoose_scroll_tab(exp_name, cnt) 220259243Sobrien Char **exp_name; 220359243Sobrien int cnt; 220459243Sobrien{ 220559243Sobrien struct scroll_tab_list *loop; 220659243Sobrien int tmp = cnt; 220759243Sobrien Char **ptr; 220859243Sobrien 220959243Sobrien ptr = (Char **) xmalloc((size_t) sizeof(Char *) * cnt); 221059243Sobrien 221159243Sobrien for(loop = scroll_tab; loop && (tmp >= 0); loop = loop->next) 221259243Sobrien ptr[--tmp] = loop->element; 221359243Sobrien 221459243Sobrien qsort((ptr_t) ptr, (size_t) cnt, sizeof(Char *), 221559243Sobrien (int (*) __P((const void *, const void *))) fcompare); 221659243Sobrien 221759243Sobrien copyn(*exp_name, ptr[curchoice], (int) Strlen(ptr[curchoice])); 221859243Sobrien xfree((ptr_t) ptr); 221959243Sobrien} 222059243Sobrien 222159243Sobrienstatic void 222259243Sobrienfree_scroll_tab() 222359243Sobrien{ 222459243Sobrien struct scroll_tab_list *loop; 222559243Sobrien 222659243Sobrien while(scroll_tab) { 222759243Sobrien loop = scroll_tab; 222859243Sobrien scroll_tab = scroll_tab->next; 222959243Sobrien xfree((ptr_t) loop->element); 223059243Sobrien xfree((ptr_t) loop); 223159243Sobrien } 223259243Sobrien} 2233