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