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