159243Sobrien/* 259243Sobrien * tw.parse.c: Everyone has taken a shot in this futile effort to 359243Sobrien * lexically analyze a csh line... Well we cannot good 459243Sobrien * a job as good as sh.lex.c; but we try. Amazing that 559243Sobrien * it works considering how many hands have touched this code 659243Sobrien */ 759243Sobrien/*- 859243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 959243Sobrien * All rights reserved. 1059243Sobrien * 1159243Sobrien * Redistribution and use in source and binary forms, with or without 1259243Sobrien * modification, are permitted provided that the following conditions 1359243Sobrien * are met: 1459243Sobrien * 1. Redistributions of source code must retain the above copyright 1559243Sobrien * notice, this list of conditions and the following disclaimer. 1659243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1759243Sobrien * notice, this list of conditions and the following disclaimer in the 1859243Sobrien * documentation and/or other materials provided with the distribution. 19100616Smp * 3. Neither the name of the University nor the names of its contributors 2059243Sobrien * may be used to endorse or promote products derived from this software 2159243Sobrien * without specific prior written permission. 2259243Sobrien * 2359243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2459243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2559243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2659243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2759243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2859243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2959243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3059243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3159243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3259243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3359243Sobrien * SUCH DAMAGE. 3459243Sobrien */ 3559243Sobrien#include "sh.h" 3659243Sobrien#include "tw.h" 3759243Sobrien#include "ed.h" 3859243Sobrien#include "tc.h" 3959243Sobrien 40167465Smp#include <assert.h> 41167465Smp 4269408Sache#ifdef WINNT_NATIVE 4359243Sobrien#include "nt.const.h" 4469408Sache#endif /* WINNT_NATIVE */ 4559243Sobrien#define EVEN(x) (((x) & 1) != 1) 4659243Sobrien 4759243Sobrien#define DOT_NONE 0 /* Don't display dot files */ 4859243Sobrien#define DOT_NOT 1 /* Don't display dot or dot-dot */ 4959243Sobrien#define DOT_ALL 2 /* Display all dot files */ 5059243Sobrien 5159243Sobrien/* TW_NONE, TW_COMMAND, TW_VARIABLE, TW_LOGNAME, */ 5259243Sobrien/* TW_FILE, TW_DIRECTORY, TW_VARLIST, TW_USER, */ 5359243Sobrien/* TW_COMPLETION, TW_ALIAS, TW_SHELLVAR, TW_ENVVAR, */ 5459243Sobrien/* TW_BINDING, TW_WORDLIST, TW_LIMIT, TW_SIGNAL */ 5559243Sobrien/* TW_JOB, TW_EXPLAIN, TW_TEXT, TW_GRPNAME */ 56167465Smpstatic void (*const tw_start_entry[]) (DIR *, const Char *) = { 5759243Sobrien tw_file_start, tw_cmd_start, tw_var_start, tw_logname_start, 5859243Sobrien tw_file_start, tw_file_start, tw_vl_start, tw_logname_start, 5959243Sobrien tw_complete_start, tw_alias_start, tw_var_start, tw_var_start, 6059243Sobrien tw_bind_start, tw_wl_start, tw_limit_start, tw_sig_start, 6159243Sobrien tw_job_start, tw_file_start, tw_file_start, tw_grpname_start 6259243Sobrien}; 6359243Sobrien 64167465Smpstatic int (*const tw_next_entry[]) (struct Strbuf *, struct Strbuf *, 65167465Smp int *) = { 6659243Sobrien tw_file_next, tw_cmd_next, tw_var_next, tw_logname_next, 6759243Sobrien tw_file_next, tw_file_next, tw_var_next, tw_logname_next, 6859243Sobrien tw_var_next, tw_var_next, tw_shvar_next, tw_envvar_next, 6959243Sobrien tw_bind_next, tw_wl_next, tw_limit_next, tw_sig_next, 7059243Sobrien tw_job_next, tw_file_next, tw_file_next, tw_grpname_next 7159243Sobrien}; 7259243Sobrien 73167465Smpstatic void (*const tw_end_entry[]) (void) = { 7459243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_logname_end, 7559243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_logname_end, 7659243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_dir_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_grpname_end 7959243Sobrien}; 8059243Sobrien 8159243Sobrien/* #define TDEBUG */ 8259243Sobrien 8359243Sobrien/* Set to TRUE if recexact is set and an exact match is found 8459243Sobrien * along with other, longer, matches. 8559243Sobrien */ 8659243Sobrien 8759243Sobrienint curchoice = -1; 8859243Sobrien 8959243Sobrienint match_unique_match = FALSE; 9059243Sobrienint non_unique_match = FALSE; 91145479Smpstatic int SearchNoDirErr = 0; /* t_search returns -2 if dir is unreadable */ 9259243Sobrien 9359243Sobrien/* state so if a completion is interrupted, the input line doesn't get 9459243Sobrien nuked */ 9559243Sobrienint InsideCompletion = 0; 9659243Sobrien 9759243Sobrien/* do the expand or list on the command line -- SHOULD BE REPLACED */ 9859243Sobrien 99167465Smpstatic void extract_dir_and_name (const Char *, struct Strbuf *, 100167465Smp Char **); 101167465Smpstatic int insert_meta (const Char *, const Char *, 102167465Smp const Char *, int); 103167465Smpstatic int tilde (struct Strbuf *, Char *); 104167465Smpstatic int expand_dir (const Char *, struct Strbuf *, DIR **, 105167465Smp COMMAND); 106167465Smpstatic int nostat (Char *); 107167465Smpstatic Char filetype (Char *, Char *); 108167465Smpstatic int t_glob (Char ***, int); 109167465Smpstatic int c_glob (Char ***); 110167465Smpstatic int is_prefix (Char *, Char *); 111167465Smpstatic int is_prefixmatch (Char *, Char *, int); 112167465Smpstatic int is_suffix (Char *, Char *); 113167465Smpstatic int recognize (struct Strbuf *, const Char *, size_t, 114167465Smp int, int, int); 115167465Smpstatic int ignored (Char *); 116167465Smpstatic int isadirectory (const Char *, const Char *); 117167465Smpstatic int tw_collect_items (COMMAND, int, struct Strbuf *, 118167465Smp struct Strbuf *, Char *, const Char *, 119167465Smp int); 120167465Smpstatic int tw_collect (COMMAND, int, struct Strbuf *, 121167465Smp struct Strbuf *, Char *, Char *, int, 122167465Smp DIR *); 123231990Smpstatic Char tw_suffix (int, struct Strbuf *,const Char *, 124231990Smp Char *); 125167465Smpstatic void tw_fixword (int, struct Strbuf *, Char *, Char *); 126167465Smpstatic void tw_list_items (int, int, int); 127167465Smpstatic void add_scroll_tab (Char *); 128167465Smpstatic void choose_scroll_tab (struct Strbuf *, int); 129167465Smpstatic void free_scroll_tab (void); 130167465Smpstatic int find_rows (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/* TRUE if character must be quoted */ 14459243Sobrien#define tricky(w) (cmap(w, _META | _DOL | _QF | _QB | _ESC | _GLOB) && w != '#') 14559243Sobrien/* TRUE if double quotes don't protect character */ 14659243Sobrien#define tricky_dq(w) (cmap(w, _DOL | _QB)) 14759243Sobrien 14859243Sobrien/* tenematch(): 14959243Sobrien * Return: 15059243Sobrien * > 1: No. of items found 15159243Sobrien * = 1: Exactly one match / spelling corrected 15259243Sobrien * = 0: No match / spelling was correct 15359243Sobrien * < 0: Error (incl spelling correction impossible) 15459243Sobrien */ 15559243Sobrienint 156167465Smptenematch(Char *inputline, int num_read, COMMAND command) 15759243Sobrien{ 158167465Smp struct Strbuf qline = Strbuf_INIT; 15959243Sobrien Char qu = 0, *pat = STRNULL; 160167465Smp size_t wp, word, wordp, cmd_start, oword = 0, ocmd_start = 0; 161167465Smp Char *str_end, *cp; 162167465Smp Char *word_start; 163167465Smp Char *oword_start = NULL; 164167465Smp eChar suf = 0; 16559243Sobrien int looking; /* what we are looking for */ 16659243Sobrien int search_ret; /* what search returned for debugging */ 16759243Sobrien int backq = 0; 16859243Sobrien 16959243Sobrien str_end = &inputline[num_read]; 170167465Smp cleanup_push(&qline, Strbuf_cleanup); 17159243Sobrien 17259243Sobrien word_start = inputline; 173167465Smp word = cmd_start = 0; 17459243Sobrien for (cp = inputline; cp < str_end; cp++) { 17559243Sobrien if (!cmap(qu, _ESC)) { 17659243Sobrien if (cmap(*cp, _QF|_ESC)) { 17759243Sobrien if (qu == 0 || qu == *cp) { 17859243Sobrien qu ^= *cp; 17959243Sobrien continue; 18059243Sobrien } 18159243Sobrien } 18259243Sobrien if (qu != '\'' && cmap(*cp, _QB)) { 18359243Sobrien if ((backq ^= 1) != 0) { 18459243Sobrien ocmd_start = cmd_start; 18559243Sobrien oword_start = word_start; 18659243Sobrien oword = word; 18759243Sobrien word_start = cp + 1; 188167465Smp word = cmd_start = qline.len + 1; 18959243Sobrien } 19059243Sobrien else { 19159243Sobrien cmd_start = ocmd_start; 19259243Sobrien word_start = oword_start; 19359243Sobrien word = oword; 19459243Sobrien } 195167465Smp Strbuf_append1(&qline, *cp); 19659243Sobrien continue; 19759243Sobrien } 19859243Sobrien } 19959243Sobrien if (iscmdmeta(*cp)) 200167465Smp cmd_start = qline.len + 1; 20159243Sobrien 20259243Sobrien /* Don't quote '/' to make the recognize stuff work easily */ 20359243Sobrien /* Don't quote '$' in double quotes */ 20459243Sobrien 205231990Smp if (cmap(*cp, _ESC) && cp < str_end - 1 && cp[1] == HIST && 206231990Smp HIST != '\0') 207167465Smp Strbuf_append1(&qline, *++cp | QUOTE); 208231990Smp else if (qu && (tricky(*cp) || *cp == '~') && 209231990Smp !(qu == '\"' && tricky_dq(*cp))) 210167465Smp Strbuf_append1(&qline, *cp | QUOTE); 21159243Sobrien else 212167465Smp Strbuf_append1(&qline, *cp); 213167465Smp if (ismetahash(qline.s[qline.len - 1]) 214167465Smp /* || isaset(qline.s + cmd_start, qline.s + qline.len) */) 215167465Smp word = qline.len, word_start = cp + 1; 21659243Sobrien if (cmap(qu, _ESC)) 21759243Sobrien qu = 0; 21859243Sobrien } 219167465Smp Strbuf_terminate(&qline); 220167465Smp wp = qline.len; 22159243Sobrien 22259243Sobrien /* 22359243Sobrien * SPECIAL HARDCODED COMPLETIONS: 22459243Sobrien * first word of command -> TW_COMMAND 22559243Sobrien * everything else -> TW_ZERO 22659243Sobrien * 22759243Sobrien */ 228167465Smp looking = starting_a_command(qline.s + word - 1, qline.s) ? 22959243Sobrien TW_COMMAND : TW_ZERO; 23059243Sobrien 23159243Sobrien wordp = word; 23259243Sobrien 23359243Sobrien#ifdef TDEBUG 234167465Smp { 235167465Smp const Char *p; 236167465Smp 237167465Smp xprintf(CGETS(30, 1, "starting_a_command %d\n"), looking); 238167465Smp xprintf("\ncmd_start:%S:\n", qline.s + cmd_start); 239167465Smp xprintf("qline:%S:\n", qline.s); 240167465Smp xprintf("qline:"); 241167465Smp for (p = qline.s; *p; p++) 242167465Smp xprintf("%c", *p & QUOTE ? '-' : ' '); 243167465Smp xprintf(":\n"); 244167465Smp xprintf("word:%S:\n", qline.s + word); 245167465Smp xprintf("word:"); 246167465Smp for (p = qline.s + word; *p; p++) 247167465Smp xprintf("%c", *p & QUOTE ? '-' : ' '); 248167465Smp xprintf(":\n"); 249167465Smp } 25059243Sobrien#endif 25159243Sobrien 25259243Sobrien if ((looking == TW_COMMAND || looking == TW_ZERO) && 25359243Sobrien (command == RECOGNIZE || command == LIST || command == SPELL || 25459243Sobrien command == RECOGNIZE_SCROLL)) { 255167465Smp Char *p; 256167465Smp 25759243Sobrien#ifdef TDEBUG 25859243Sobrien xprintf(CGETS(30, 2, "complete %d "), looking); 25959243Sobrien#endif 260167465Smp p = qline.s + wordp; 261167465Smp looking = tw_complete(qline.s + cmd_start, &p, &pat, looking, &suf); 262167465Smp wordp = p - qline.s; 26359243Sobrien#ifdef TDEBUG 26459243Sobrien xprintf(CGETS(30, 3, "complete %d %S\n"), looking, pat); 26559243Sobrien#endif 26659243Sobrien } 26759243Sobrien 26859243Sobrien switch (command) { 269167465Smp Char *bptr; 27059243Sobrien Char *items[2], **ptr; 27159243Sobrien int i, count; 27259243Sobrien 27359243Sobrien case RECOGNIZE: 27459243Sobrien case RECOGNIZE_SCROLL: 275167465Smp case RECOGNIZE_ALL: { 276167465Smp struct Strbuf wordbuf = Strbuf_INIT; 277167465Smp Char *slshp; 278167465Smp 27959243Sobrien if (adrof(STRautocorrect)) { 280167465Smp if ((slshp = Strrchr(qline.s + wordp, '/')) != NULL && 281167465Smp slshp[1] != '\0') { 28259243Sobrien SearchNoDirErr = 1; 283167465Smp for (bptr = qline.s + wordp; bptr < slshp; bptr++) { 28459243Sobrien /* 28559243Sobrien * do not try to correct spelling of words containing 28659243Sobrien * globbing characters 28759243Sobrien */ 28859243Sobrien if (isglob(*bptr)) { 28959243Sobrien SearchNoDirErr = 0; 29059243Sobrien break; 29159243Sobrien } 29259243Sobrien } 29359243Sobrien } 29459243Sobrien } 29559243Sobrien else 29659243Sobrien slshp = STRNULL; 297167465Smp Strbuf_append(&wordbuf, qline.s + wordp); 298167465Smp Strbuf_terminate(&wordbuf); 299167465Smp cleanup_push(&wordbuf, Strbuf_cleanup); 300167465Smp search_ret = t_search(&wordbuf, command, looking, 1, pat, suf); 301167465Smp qline.len = wordp; 302167465Smp Strbuf_append(&qline, wordbuf.s); 303167465Smp Strbuf_terminate(&qline); 304167465Smp cleanup_until(&wordbuf); 30559243Sobrien SearchNoDirErr = 0; 30659243Sobrien 30759243Sobrien if (search_ret == -2) { 308167465Smp Char *rword; 30959243Sobrien 310167465Smp rword = Strsave(slshp); 311167465Smp cleanup_push(rword, xfree); 31259243Sobrien if (slshp != STRNULL) 31359243Sobrien *slshp = '\0'; 314167465Smp wordbuf = Strbuf_init; 315167465Smp Strbuf_append(&wordbuf, qline.s + wordp); 316167465Smp Strbuf_terminate(&wordbuf); 317167465Smp cleanup_push(&wordbuf, Strbuf_cleanup); 318167465Smp search_ret = spell_me(&wordbuf, looking, pat, suf); 31959243Sobrien if (search_ret == 1) { 320167465Smp Strbuf_append(&wordbuf, rword); 321167465Smp Strbuf_terminate(&wordbuf); 322167465Smp wp = wordp + wordbuf.len; 323167465Smp search_ret = t_search(&wordbuf, command, looking, 1, pat, suf); 32459243Sobrien } 325167465Smp qline.len = wordp; 326167465Smp Strbuf_append(&qline, wordbuf.s); 327167465Smp Strbuf_terminate(&qline); 328167465Smp cleanup_until(rword); 32959243Sobrien } 330167465Smp if (qline.s[wp] != '\0' && 331167465Smp insert_meta(word_start, str_end, qline.s + word, !qu) < 0) 332167465Smp goto err; /* error inserting */ 333167465Smp break; 334167465Smp } 33559243Sobrien 336167465Smp case SPELL: { 337167465Smp struct Strbuf wordbuf = Strbuf_INIT; 338167465Smp 33959243Sobrien for (bptr = word_start; bptr < str_end; bptr++) { 34059243Sobrien /* 34159243Sobrien * do not try to correct spelling of words containing globbing 34259243Sobrien * characters 34359243Sobrien */ 344167465Smp if (isglob(*bptr)) { 345167465Smp search_ret = 0; 346167465Smp goto end; 347167465Smp } 34859243Sobrien } 349167465Smp Strbuf_append(&wordbuf, qline.s + wordp); 350167465Smp Strbuf_terminate(&wordbuf); 351167465Smp cleanup_push(&wordbuf, Strbuf_cleanup); 352231990Smp 353231990Smp /* 354231990Smp * Don't try to spell things that we know they are correct. 355231990Smp * Trying to spell can hang when we have NFS mounted hung 356231990Smp * volumes. 357231990Smp */ 358231990Smp if ((looking == TW_COMMAND) && Strchr(wordbuf.s, '/') != NULL) { 359231990Smp if (executable(NULL, wordbuf.s, 0)) { 360231990Smp cleanup_until(&wordbuf); 361231990Smp search_ret = 0; 362231990Smp goto end; 363231990Smp } 364231990Smp } 365231990Smp 366167465Smp search_ret = spell_me(&wordbuf, looking, pat, suf); 367167465Smp qline.len = wordp; 368167465Smp Strbuf_append(&qline, wordbuf.s); 369167465Smp Strbuf_terminate(&qline); 370167465Smp cleanup_until(&wordbuf); 37159243Sobrien if (search_ret == 1) { 372167465Smp if (insert_meta(word_start, str_end, qline.s + word, !qu) < 0) 373167465Smp goto err; /* error inserting */ 37459243Sobrien } 375167465Smp break; 376167465Smp } 37759243Sobrien 37859243Sobrien case PRINT_HELP: 379167465Smp do_help(qline.s + cmd_start); 380167465Smp search_ret = 1; 381167465Smp break; 38259243Sobrien 38359243Sobrien case GLOB: 38459243Sobrien case GLOB_EXPAND: 385167465Smp items[0] = Strsave(qline.s + wordp); 38659243Sobrien items[1] = NULL; 387167465Smp cleanup_push(items[0], xfree); 38859243Sobrien ptr = items; 389167465Smp count = (looking == TW_COMMAND && Strchr(qline.s + wordp, '/') == 0) ? 390167465Smp c_glob(&ptr) : 39159243Sobrien t_glob(&ptr, looking == TW_COMMAND); 392167465Smp cleanup_until(items[0]); 393167465Smp if (ptr != items) 394167465Smp cleanup_push(ptr, blk_cleanup); 39559243Sobrien if (count > 0) { 39659243Sobrien if (command == GLOB) 39759243Sobrien print_by_column(STRNULL, ptr, count, 0); 39859243Sobrien else { 39959243Sobrien DeleteBack(str_end - word_start);/* get rid of old word */ 40059243Sobrien for (i = 0; i < count; i++) 40159243Sobrien if (ptr[i] && *ptr[i]) { 40259243Sobrien (void) quote(ptr[i]); 40359243Sobrien if (insert_meta(0, 0, ptr[i], 0) < 0 || 40459243Sobrien InsertStr(STRspace) < 0) { 405167465Smp if (ptr != items) 406167465Smp cleanup_until(ptr); 407167465Smp goto err; /* error inserting */ 40859243Sobrien } 40959243Sobrien } 41059243Sobrien } 41159243Sobrien } 412167465Smp if (ptr != items) 413167465Smp cleanup_until(ptr); 414167465Smp search_ret = count; 415167465Smp break; 41659243Sobrien 41759243Sobrien case VARS_EXPAND: 418167465Smp bptr = dollar(qline.s + word); 419167465Smp if (bptr != NULL) { 420167465Smp if (insert_meta(word_start, str_end, bptr, !qu) < 0) { 421167465Smp xfree(bptr); 422167465Smp goto err; /* error inserting */ 423167465Smp } 424167465Smp xfree(bptr); 425167465Smp search_ret = 1; 426167465Smp break; 42759243Sobrien } 428167465Smp search_ret = 0; 429167465Smp break; 43059243Sobrien 43159243Sobrien case PATH_NORMALIZE: 432167465Smp if ((bptr = dnormalize(qline.s + wordp, symlinks == SYM_IGNORE || 433167465Smp symlinks == SYM_EXPAND)) != NULL) { 434167465Smp if (insert_meta(word_start, str_end, bptr, !qu) < 0) { 435167465Smp xfree(bptr); 436167465Smp goto err; /* error inserting */ 437167465Smp } 438167465Smp xfree(bptr); 439167465Smp search_ret = 1; 440167465Smp break; 44159243Sobrien } 442167465Smp search_ret = 0; 443167465Smp break; 44459243Sobrien 445167465Smp case COMMAND_NORMALIZE: { 446167465Smp Char *p; 447167465Smp int found; 44859243Sobrien 449195609Smp found = cmd_expand(qline.s + wordp, &p); 450167465Smp 451167465Smp if (!found) { 452167465Smp xfree(p); 453167465Smp search_ret = 0; 454167465Smp break; 455167465Smp } 456167465Smp if (insert_meta(word_start, str_end, p, !qu) < 0) { 457167465Smp xfree(p); 458167465Smp goto err; /* error inserting */ 459167465Smp } 460167465Smp xfree(p); 461167465Smp search_ret = 1; 462167465Smp break; 463167465Smp } 464167465Smp 46559243Sobrien case LIST: 466167465Smp case LIST_ALL: { 467167465Smp struct Strbuf wordbuf = Strbuf_INIT; 46859243Sobrien 469167465Smp Strbuf_append(&wordbuf, qline.s + wordp); 470167465Smp Strbuf_terminate(&wordbuf); 471167465Smp cleanup_push(&wordbuf, Strbuf_cleanup); 472167465Smp search_ret = t_search(&wordbuf, LIST, looking, 1, pat, suf); 473167465Smp qline.len = wordp; 474167465Smp Strbuf_append(&qline, wordbuf.s); 475167465Smp Strbuf_terminate(&qline); 476167465Smp cleanup_until(&wordbuf); 477167465Smp break; 478167465Smp } 479167465Smp 48059243Sobrien default: 48159243Sobrien xprintf(CGETS(30, 4, "%s: Internal match error.\n"), progname); 482167465Smp search_ret = 1; 483167465Smp } 484167465Smp end: 485167465Smp cleanup_until(&qline); 486167465Smp return search_ret; 48759243Sobrien 488167465Smp err: 489167465Smp cleanup_until(&qline); 490167465Smp return -1; 49159243Sobrien} /* end tenematch */ 49259243Sobrien 49359243Sobrien 49459243Sobrien/* t_glob(): 49559243Sobrien * Return a list of files that match the pattern 49659243Sobrien */ 49759243Sobrienstatic int 498167465Smpt_glob(Char ***v, int cmd) 49959243Sobrien{ 50059243Sobrien jmp_buf_t osetexit; 501167465Smp int gflag; 50259243Sobrien 50359243Sobrien if (**v == 0) 50459243Sobrien return (0); 505167465Smp gflag = tglob(*v); 50659243Sobrien if (gflag) { 507167465Smp size_t omark; 508167465Smp 50959243Sobrien getexit(osetexit); /* make sure to come back here */ 510167465Smp omark = cleanup_push_mark(); 51159243Sobrien if (setexit() == 0) 512167465Smp *v = globall(*v, gflag); 513167465Smp cleanup_pop_mark(omark); 51459243Sobrien resexit(osetexit); 51559243Sobrien if (haderr) { 51659243Sobrien haderr = 0; 51759243Sobrien NeedsRedraw = 1; 51859243Sobrien return (-1); 51959243Sobrien } 52059243Sobrien if (*v == 0) 52159243Sobrien return (0); 52259243Sobrien } 52359243Sobrien else 52459243Sobrien return (0); 52559243Sobrien 52659243Sobrien if (cmd) { 52759243Sobrien Char **av = *v, *p; 528167465Smp int fwd, i; 52959243Sobrien 530167465Smp for (i = 0, fwd = 0; av[i] != NULL; i++) 53159243Sobrien if (!executable(NULL, av[i], 0)) { 53259243Sobrien fwd++; 53359243Sobrien p = av[i]; 53459243Sobrien av[i] = NULL; 535167465Smp xfree(p); 53659243Sobrien } 53759243Sobrien else if (fwd) 53859243Sobrien av[i - fwd] = av[i]; 53959243Sobrien 54059243Sobrien if (fwd) 54159243Sobrien av[i - fwd] = av[i]; 54259243Sobrien } 54359243Sobrien 544167465Smp return blklen(*v); 54559243Sobrien} /* end t_glob */ 54659243Sobrien 54759243Sobrien 54859243Sobrien/* c_glob(): 54959243Sobrien * Return a list of commands that match the pattern 55059243Sobrien */ 55159243Sobrienstatic int 552167465Smpc_glob(Char ***v) 55359243Sobrien{ 554167465Smp struct blk_buf av = BLK_BUF_INIT; 555167465Smp struct Strbuf cmd = Strbuf_INIT, dir = Strbuf_INIT; 556167465Smp Char *pat = **v; 557167465Smp int flag; 55859243Sobrien 55959243Sobrien if (pat == NULL) 56059243Sobrien return (0); 56159243Sobrien 562167465Smp cleanup_push(&av, bb_cleanup); 563167465Smp cleanup_push(&cmd, Strbuf_cleanup); 564167465Smp cleanup_push(&dir, Strbuf_cleanup); 56559243Sobrien 56659243Sobrien tw_cmd_start(NULL, NULL); 567167465Smp while (cmd.len = 0, tw_cmd_next(&cmd, &dir, &flag) != 0) { 568167465Smp Strbuf_terminate(&cmd); 569167465Smp if (Gmatch(cmd.s, pat)) 570167465Smp bb_append(&av, Strsave(cmd.s)); 571167465Smp } 57259243Sobrien tw_dir_end(); 573167465Smp *v = bb_finish(&av); 574167465Smp cleanup_ignore(&av); 575167465Smp cleanup_until(&av); 57659243Sobrien 577167465Smp return av.len; 57859243Sobrien} /* end c_glob */ 57959243Sobrien 58059243Sobrien 58159243Sobrien/* insert_meta(): 58259243Sobrien * change the word before the cursor. 58359243Sobrien * cp must point to the start of the unquoted word. 58459243Sobrien * cpend to the end of it. 58559243Sobrien * word is the text that has to be substituted. 58659243Sobrien * strategy: 58759243Sobrien * try to keep all the quote characters of the user's input. 58859243Sobrien * change quote type only if necessary. 58959243Sobrien */ 59059243Sobrienstatic int 591167465Smpinsert_meta(const Char *cp, const Char *cpend, const Char *word, 592167465Smp int closequotes) 59359243Sobrien{ 594167465Smp struct Strbuf buffer = Strbuf_INIT; 595167465Smp Char *bptr; 596167465Smp const Char *wptr; 59759243Sobrien int in_sync = (cp != NULL); 598145479Smp Char qu = 0; 59959243Sobrien int ndel = (int) (cp ? cpend - cp : 0); 60059243Sobrien Char w, wq; 601167465Smp int res; 60259243Sobrien 603167465Smp for (wptr = word;;) { 60459243Sobrien if (cp >= cpend) 60559243Sobrien in_sync = 0; 60659243Sobrien if (in_sync && !cmap(qu, _ESC) && cmap(*cp, _QF|_ESC)) 60759243Sobrien if (qu == 0 || qu == *cp) { 60859243Sobrien qu ^= *cp; 609167465Smp Strbuf_append1(&buffer, *cp++); 61059243Sobrien continue; 61159243Sobrien } 61259243Sobrien w = *wptr; 61359243Sobrien if (w == 0) 61459243Sobrien break; 61559243Sobrien 61659243Sobrien wq = w & QUOTE; 617316957Sdchagin#if INVALID_BYTE != 0 618316957Sdchagin /* add checking INVALID_BYTE for FIX UTF32 */ 619316957Sdchagin if ((w & INVALID_BYTE) != INVALID_BYTE) /* w < INVALID_BYTE */ 620316957Sdchagin#endif 621316957Sdchagin w &= ~QUOTE; 62259243Sobrien 62359243Sobrien if (cmap(w, _ESC | _QF)) 62459243Sobrien wq = QUOTE; /* quotes are always quoted */ 62559243Sobrien 62659243Sobrien if (!wq && qu && tricky(w) && !(qu == '\"' && tricky_dq(w))) { 62759243Sobrien /* We have to unquote the character */ 62859243Sobrien in_sync = 0; 62959243Sobrien if (cmap(qu, _ESC)) 630167465Smp buffer.s[buffer.len - 1] = w; 63159243Sobrien else { 632167465Smp Strbuf_append1(&buffer, qu); 633167465Smp Strbuf_append1(&buffer, w); 63459243Sobrien if (wptr[1] == 0) 63559243Sobrien qu = 0; 63659243Sobrien else 637167465Smp Strbuf_append1(&buffer, qu); 63859243Sobrien } 63959243Sobrien } else if (qu && w == qu) { 64059243Sobrien in_sync = 0; 641167465Smp if (buffer.len != 0 && buffer.s[buffer.len - 1] == qu) { 64259243Sobrien /* User misunderstanding :) */ 643167465Smp buffer.s[buffer.len - 1] = '\\'; 644167465Smp Strbuf_append1(&buffer, w); 64559243Sobrien qu = 0; 64659243Sobrien } else { 647167465Smp Strbuf_append1(&buffer, qu); 648167465Smp Strbuf_append1(&buffer, '\\'); 649167465Smp Strbuf_append1(&buffer, w); 650167465Smp Strbuf_append1(&buffer, qu); 65159243Sobrien } 65259243Sobrien } 65359243Sobrien else if (wq && qu == '\"' && tricky_dq(w)) { 65459243Sobrien in_sync = 0; 655167465Smp Strbuf_append1(&buffer, qu); 656167465Smp Strbuf_append1(&buffer, '\\'); 657167465Smp Strbuf_append1(&buffer, w); 658167465Smp Strbuf_append1(&buffer, qu); 659167465Smp } else if (wq && 660231990Smp ((!qu && (tricky(w) || (w == HISTSUB && HISTSUB != '\0' 661231990Smp && buffer.len == 0))) || 662231990Smp (!cmap(qu, _ESC) && w == HIST && HIST != '\0'))) { 66359243Sobrien in_sync = 0; 664167465Smp Strbuf_append1(&buffer, '\\'); 665167465Smp Strbuf_append1(&buffer, w); 66659243Sobrien } else { 66759243Sobrien if (in_sync && *cp++ != w) 66859243Sobrien in_sync = 0; 669167465Smp Strbuf_append1(&buffer, w); 67059243Sobrien } 67159243Sobrien wptr++; 67259243Sobrien if (cmap(qu, _ESC)) 67359243Sobrien qu = 0; 67459243Sobrien } 67559243Sobrien if (closequotes && qu && !cmap(qu, _ESC)) 676167465Smp Strbuf_append1(&buffer, w); 677167465Smp bptr = Strbuf_finish(&buffer); 67859243Sobrien if (ndel) 67959243Sobrien DeleteBack(ndel); 680167465Smp res = InsertStr(bptr); 681167465Smp xfree(bptr); 682167465Smp return res; 68359243Sobrien} /* end insert_meta */ 68459243Sobrien 68559243Sobrien 68659243Sobrien 68759243Sobrien/* is_prefix(): 68859243Sobrien * return true if check matches initial chars in template 68959243Sobrien * This differs from PWB imatch in that if check is null 69059243Sobrien * it matches anything 69159243Sobrien */ 69259243Sobrienstatic int 693167465Smpis_prefix(Char *check, Char *template) 69459243Sobrien{ 69559243Sobrien for (; *check; check++, template++) 69659243Sobrien if ((*check & TRIM) != (*template & TRIM)) 69759243Sobrien return (FALSE); 69859243Sobrien return (TRUE); 69959243Sobrien} /* end is_prefix */ 70059243Sobrien 70159243Sobrien 70259243Sobrien/* is_prefixmatch(): 70359243Sobrien * return true if check matches initial chars in template 70459243Sobrien * This differs from PWB imatch in that if check is null 70559243Sobrien * it matches anything 70659243Sobrien * and matches on shortening of commands 70759243Sobrien */ 70859243Sobrienstatic int 709231990Smpis_prefixmatch(Char *check, Char *template, int enhanced) 71059243Sobrien{ 711231990Smp Char MCH1, MCH2, LCH1, LCH2; 71259243Sobrien 71359243Sobrien for (; *check; check++, template++) { 71459243Sobrien if ((*check & TRIM) != (*template & TRIM)) { 715231990Smp MCH1 = (*check & TRIM); 716231990Smp MCH2 = (*template & TRIM); 717231990Smp LCH1 = Isupper(MCH1) ? Tolower(MCH1) : 718231990Smp enhanced == 2 && MCH1 == '_' ? '-' : MCH1; 719231990Smp LCH2 = Isupper(MCH2) ? Tolower(MCH2) : 720231990Smp enhanced == 2 && MCH2 == '_' ? '-' : MCH2; 721231990Smp if (MCH1 != MCH2 && MCH1 != LCH2 && 722231990Smp (LCH1 != MCH2 || enhanced == 2)) { 723231990Smp if (enhanced && ((*check & TRIM) == '-' || 72459243Sobrien (*check & TRIM) == '.' || 72559243Sobrien (*check & TRIM) == '_')) { 726231990Smp MCH1 = MCH2 = (*check & TRIM); 727231990Smp if (MCH1 == '_' && enhanced != 2) { 728231990Smp MCH2 = '-'; 729231990Smp } else if (MCH1 == '-') { 730231990Smp MCH2 = '_'; 731231990Smp } 732231990Smp for (; *template && (*template & TRIM) != MCH1 && 733231990Smp (*template & TRIM) != MCH2; template++) 73459243Sobrien continue; 735231990Smp if (!*template) { 73659243Sobrien return (FALSE); 737231990Smp } 738231990Smp } else { 739231990Smp return (FALSE); 740231990Smp } 741231990Smp } 742231990Smp } 74359243Sobrien } 74459243Sobrien return (TRUE); 74559243Sobrien} /* end is_prefixmatch */ 74659243Sobrien 74759243Sobrien 74859243Sobrien/* is_suffix(): 74959243Sobrien * Return true if the chars in template appear at the 75059243Sobrien * end of check, I.e., are it's suffix. 75159243Sobrien */ 75259243Sobrienstatic int 753167465Smpis_suffix(Char *check, Char *template) 75459243Sobrien{ 755145479Smp Char *t, *c; 75659243Sobrien 757167465Smp t = Strend(template); 758167465Smp c = Strend(check); 75959243Sobrien for (;;) { 76059243Sobrien if (t == template) 76159243Sobrien return 1; 76259243Sobrien if (c == check || (*--t & TRIM) != (*--c & TRIM)) 76359243Sobrien return 0; 76459243Sobrien } 76559243Sobrien} /* end is_suffix */ 76659243Sobrien 76759243Sobrien 76859243Sobrien/* ignored(): 76959243Sobrien * Return true if this is an ignored item 77059243Sobrien */ 77159243Sobrienstatic int 772167465Smpignored(Char *item) 77359243Sobrien{ 77459243Sobrien struct varent *vp; 775145479Smp Char **cp; 77659243Sobrien 77759243Sobrien if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 77859243Sobrien return (FALSE); 77959243Sobrien for (; *cp != NULL; cp++) 78059243Sobrien if (is_suffix(item, *cp)) 78159243Sobrien return (TRUE); 78259243Sobrien return (FALSE); 78359243Sobrien} /* end ignored */ 78459243Sobrien 78559243Sobrien 78659243Sobrien 78759243Sobrien/* starting_a_command(): 78859243Sobrien * return true if the command starting at wordstart is a command 78959243Sobrien */ 79059243Sobrienint 791167465Smpstarting_a_command(Char *wordstart, Char *inputline) 79259243Sobrien{ 793145479Smp Char *ptr, *ncmdstart; 794145479Smp int count, bsl; 79559243Sobrien static Char 79659243Sobrien cmdstart[] = {'`', ';', '&', '(', '|', '\0'}, 79759243Sobrien cmdalive[] = {' ', '\t', '\'', '"', '<', '>', '\0'}; 79859243Sobrien 79959243Sobrien /* 80059243Sobrien * Find if the number of backquotes is odd or even. 80159243Sobrien */ 80259243Sobrien for (ptr = wordstart, count = 0; 80359243Sobrien ptr >= inputline; 80459243Sobrien count += (*ptr-- == '`')) 80559243Sobrien continue; 80659243Sobrien /* 80759243Sobrien * if the number of backquotes is even don't include the backquote char in 80859243Sobrien * the list of command starting delimiters [if it is zero, then it does not 80959243Sobrien * matter] 81059243Sobrien */ 81159243Sobrien ncmdstart = cmdstart + EVEN(count); 81259243Sobrien 81359243Sobrien /* 81459243Sobrien * look for the characters previous to this word if we find a command 81559243Sobrien * starting delimiter we break. if we find whitespace and another previous 81659243Sobrien * word then we are not a command 81759243Sobrien * 81859243Sobrien * count is our state machine: 0 looking for anything 1 found white-space 81959243Sobrien * looking for non-ws 82059243Sobrien */ 82159243Sobrien for (count = 0; wordstart >= inputline; wordstart--) { 82259243Sobrien if (*wordstart == '\0') 82359243Sobrien continue; 824145479Smp if (Strchr(ncmdstart, *wordstart)) { 825145479Smp for (ptr = wordstart, bsl = 0; *(--ptr) == '\\'; bsl++); 826145479Smp if (bsl & 1) { 827145479Smp wordstart--; 828145479Smp continue; 829145479Smp } else 830145479Smp break; 831145479Smp } 83259243Sobrien /* 83359243Sobrien * found white space 83459243Sobrien */ 83559243Sobrien if ((ptr = Strchr(cmdalive, *wordstart)) != NULL) 83659243Sobrien count = 1; 83759243Sobrien if (count == 1 && !ptr) 83859243Sobrien return (FALSE); 83959243Sobrien } 84059243Sobrien 84159243Sobrien if (wordstart > inputline) 84259243Sobrien switch (*wordstart) { 84359243Sobrien case '&': /* Look for >& */ 84459243Sobrien while (wordstart > inputline && 84559243Sobrien (*--wordstart == ' ' || *wordstart == '\t')) 84659243Sobrien continue; 84759243Sobrien if (*wordstart == '>') 84859243Sobrien return (FALSE); 84959243Sobrien break; 85059243Sobrien case '(': /* check for foreach, if etc. */ 85159243Sobrien while (wordstart > inputline && 85259243Sobrien (*--wordstart == ' ' || *wordstart == '\t')) 85359243Sobrien continue; 85459243Sobrien if (!iscmdmeta(*wordstart) && 85559243Sobrien (*wordstart != ' ' && *wordstart != '\t')) 85659243Sobrien return (FALSE); 85759243Sobrien break; 85859243Sobrien default: 85959243Sobrien break; 86059243Sobrien } 86159243Sobrien return (TRUE); 86259243Sobrien} /* end starting_a_command */ 86359243Sobrien 86459243Sobrien 86559243Sobrien/* recognize(): 86659243Sobrien * Object: extend what user typed up to an ambiguity. 86759243Sobrien * Algorithm: 86859243Sobrien * On first match, copy full item (assume it'll be the only match) 86959243Sobrien * On subsequent matches, shorten exp_name to the first 87059243Sobrien * character mismatch between exp_name and item. 87159243Sobrien * If we shorten it back to the prefix length, stop searching. 87259243Sobrien */ 87359243Sobrienstatic int 874167465Smprecognize(struct Strbuf *exp_name, const Char *item, size_t name_length, 875167465Smp int numitems, int enhanced, int igncase) 87659243Sobrien{ 877231990Smp Char MCH1, MCH2, LCH1, LCH2; 878167465Smp Char *x; 879167465Smp const Char *ent; 880167465Smp size_t len = 0; 88159243Sobrien 88259243Sobrien if (numitems == 1) { /* 1st match */ 883167465Smp exp_name->len = 0; 884167465Smp Strbuf_append(exp_name, item); 885167465Smp Strbuf_terminate(exp_name); 88659243Sobrien return (0); 88759243Sobrien } 888131962Smp if (!enhanced && !igncase) { 889167465Smp for (x = exp_name->s, ent = item; *x && (*x & TRIM) == (*ent & TRIM); 890167465Smp x++, ent++) 89159243Sobrien len++; 89259243Sobrien } else { 893167465Smp for (x = exp_name->s, ent = item; *x; x++, ent++) { 89459243Sobrien MCH1 = *x & TRIM; 89559243Sobrien MCH2 = *ent & TRIM; 896231990Smp LCH1 = Isupper(MCH1) ? Tolower(MCH1) : MCH1; 897231990Smp LCH2 = Isupper(MCH2) ? Tolower(MCH2) : MCH2; 898231990Smp if (MCH1 != MCH2) { 899231990Smp if (LCH1 == MCH2 || (MCH1 == '_' && MCH2 == '-')) 900231990Smp *x = *ent; 901231990Smp else if (LCH1 != LCH2) 902231990Smp break; 903231990Smp } 90459243Sobrien len++; 90559243Sobrien } 90659243Sobrien } 90759243Sobrien *x = '\0'; /* Shorten at 1st char diff */ 908167465Smp exp_name->len = x - exp_name->s; 909231990Smp if (!(match_unique_match || is_set(STRrecexact) || (enhanced && *ent)) && 910231990Smp len == name_length) /* Ambiguous to prefix? */ 91159243Sobrien return (-1); /* So stop now and save time */ 91259243Sobrien return (0); 91359243Sobrien} /* end recognize */ 91459243Sobrien 91559243Sobrien 91659243Sobrien/* tw_collect_items(): 91759243Sobrien * Collect items that match target. 91859243Sobrien * SPELL command: 91959243Sobrien * Returns the spelling distance of the closest match. 92059243Sobrien * else 92159243Sobrien * Returns the number of items found. 92259243Sobrien * If none found, but some ignored items were found, 92359243Sobrien * It returns the -number of ignored items. 92459243Sobrien */ 92559243Sobrienstatic int 926167465Smptw_collect_items(COMMAND command, int looking, struct Strbuf *exp_dir, 927167465Smp struct Strbuf *exp_name, Char *target, const Char *pat, 928167465Smp int flags) 92959243Sobrien{ 93059243Sobrien int done = FALSE; /* Search is done */ 93159243Sobrien int showdots; /* Style to show dot files */ 93259243Sobrien int nignored = 0; /* Number of fignored items */ 93359243Sobrien int numitems = 0; /* Number of matched items */ 934167465Smp size_t name_length = Strlen(target); /* Length of prefix (file name) */ 93559243Sobrien int exec_check = flags & TW_EXEC_CHK;/* need to check executability */ 93659243Sobrien int dir_check = flags & TW_DIR_CHK; /* Need to check for directories */ 93759243Sobrien int text_check = flags & TW_TEXT_CHK;/* Need to check for non-directories */ 93859243Sobrien int dir_ok = flags & TW_DIR_OK; /* Ignore directories? */ 93959243Sobrien int gpat = flags & TW_PAT_OK; /* Match against a pattern */ 94059243Sobrien int ignoring = flags & TW_IGN_OK; /* Use fignore? */ 94159243Sobrien int d = 4, nd; /* Spelling distance */ 942231990Smp Char **cp; 943167465Smp Char *ptr; 94459243Sobrien struct varent *vp; 945167465Smp struct Strbuf buf = Strbuf_INIT, item = Strbuf_INIT; 946167465Smp int enhanced = 0; 94759243Sobrien int cnt = 0; 94859243Sobrien int igncase = 0; 94959243Sobrien 95059243Sobrien 95159243Sobrien flags = 0; 95259243Sobrien 95359243Sobrien showdots = DOT_NONE; 95459243Sobrien if ((ptr = varval(STRlistflags)) != STRNULL) 95559243Sobrien while (*ptr) 95659243Sobrien switch (*ptr++) { 95759243Sobrien case 'a': 95859243Sobrien showdots = DOT_ALL; 95959243Sobrien break; 96059243Sobrien case 'A': 96159243Sobrien showdots = DOT_NOT; 96259243Sobrien break; 96359243Sobrien default: 96459243Sobrien break; 96559243Sobrien } 96659243Sobrien 967231990Smp if (looking == TW_COMMAND 968231990Smp && (vp = adrof(STRautorehash)) != NULL && vp->vec != NULL) 969231990Smp for (cp = vp->vec; *cp; cp++) 970231990Smp if (Strcmp(*cp, STRalways) == 0 971231990Smp || (Strcmp(*cp, STRcorrect) == 0 && command == SPELL) 972231990Smp || (Strcmp(*cp, STRcomplete) == 0 && command != SPELL)) { 973231990Smp tw_cmd_free(); 974231990Smp tw_cmd_start(NULL, NULL); 975231990Smp break; 976231990Smp } 977231990Smp 978167465Smp cleanup_push(&item, Strbuf_cleanup); 979167465Smp cleanup_push(&buf, Strbuf_cleanup); 980167465Smp while (!done && 981167465Smp (item.len = 0, 982167465Smp tw_next_entry[looking](&item, exp_dir, &flags) != 0)) { 983167465Smp Strbuf_terminate(&item); 98459243Sobrien#ifdef TDEBUG 985167465Smp xprintf("item = %S\n", item.s); 98659243Sobrien#endif 98759243Sobrien switch (looking) { 98859243Sobrien case TW_FILE: 98959243Sobrien case TW_DIRECTORY: 99059243Sobrien case TW_TEXT: 99159243Sobrien /* 99259243Sobrien * Don't match . files on null prefix match 99359243Sobrien */ 994167465Smp if (showdots == DOT_NOT && (ISDOT(item.s) || ISDOTDOT(item.s))) 99559243Sobrien done = TRUE; 996167465Smp if (name_length == 0 && item.s[0] == '.' && showdots == DOT_NONE) 99759243Sobrien done = TRUE; 99859243Sobrien break; 99959243Sobrien 100059243Sobrien case TW_COMMAND: 1001131962Smp#if defined(_UWIN) || defined(__CYGWIN__) 1002167465Smp /* 1003167465Smp * Turn foo.{exe,com,bat,cmd} into foo since UWIN's readdir returns 1004167465Smp * the file with the .exe, .com, .bat, .cmd extension 1005231990Smp * 1006231990Smp * Same for Cygwin, but only for .exe and .com extension. 1007131962Smp */ 1008131962Smp { 1009231990Smp#ifdef __CYGWIN__ 1010231990Smp static const char *rext[] = { ".exe", ".com" }; 1011231990Smp#else 1012167465Smp static const char *rext[] = { ".exe", ".bat", ".com", ".cmd" }; 1013231990Smp#endif 1014167465Smp size_t exti = Strlen(item.s); 1015167465Smp 1016167465Smp if (exti > 4) { 1017167465Smp char *ext = short2str(&item.s[exti -= 4]); 1018167465Smp size_t i; 1019167465Smp 1020167465Smp for (i = 0; i < sizeof(rext) / sizeof(rext[0]); i++) 1021167465Smp if (strcasecmp(ext, rext[i]) == 0) { 1022167465Smp item.len = exti; 1023167465Smp Strbuf_terminate(&item); 1024167465Smp break; 1025167465Smp } 1026167465Smp } 1027131962Smp } 1028131962Smp#endif /* _UWIN || __CYGWIN__ */ 102959243Sobrien exec_check = flags & TW_EXEC_CHK; 103059243Sobrien dir_ok = flags & TW_DIR_OK; 103159243Sobrien break; 103259243Sobrien 103359243Sobrien default: 103459243Sobrien break; 103559243Sobrien } 103659243Sobrien 103759243Sobrien if (done) { 103859243Sobrien done = FALSE; 103959243Sobrien continue; 104059243Sobrien } 104159243Sobrien 104259243Sobrien switch (command) { 104359243Sobrien 104459243Sobrien case SPELL: /* correct the spelling of the last bit */ 104559243Sobrien if (name_length == 0) {/* zero-length word can't be misspelled */ 1046167465Smp exp_name->len = 0; /* (not trying is important for ~) */ 1047167465Smp Strbuf_terminate(exp_name); 104859243Sobrien d = 0; 104959243Sobrien done = TRUE; 105059243Sobrien break; 105159243Sobrien } 1052167465Smp if (gpat && !Gmatch(item.s, pat)) 105359243Sobrien break; 105459243Sobrien /* 105559243Sobrien * Swapped the order of the spdist() arguments as suggested 105659243Sobrien * by eeide@asylum.cs.utah.edu (Eric Eide) 105759243Sobrien */ 1058167465Smp nd = spdist(target, item.s); /* test the item against original */ 105959243Sobrien if (nd <= d && nd != 4) { 1060167465Smp if (!(exec_check && !executable(exp_dir->s, item.s, dir_ok))) { 1061167465Smp exp_name->len = 0; 1062167465Smp Strbuf_append(exp_name, item.s); 1063167465Smp Strbuf_terminate(exp_name); 106459243Sobrien d = nd; 106559243Sobrien if (d == 0) /* if found it exactly */ 106659243Sobrien done = TRUE; 106759243Sobrien } 106859243Sobrien } 106959243Sobrien else if (nd == 4) { 1070167465Smp if (spdir(exp_name, exp_dir->s, item.s, target)) { 1071167465Smp if (exec_check && 1072167465Smp !executable(exp_dir->s, exp_name->s, dir_ok)) 107359243Sobrien break; 107459243Sobrien#ifdef notdef 107559243Sobrien /* 107659243Sobrien * We don't want to stop immediately, because 107759243Sobrien * we might find an exact/better match later. 107859243Sobrien */ 107959243Sobrien d = 0; 108059243Sobrien done = TRUE; 108159243Sobrien#endif 108259243Sobrien d = 3; 108359243Sobrien } 108459243Sobrien } 108559243Sobrien break; 108659243Sobrien 108759243Sobrien case LIST: 108859243Sobrien case RECOGNIZE: 108959243Sobrien case RECOGNIZE_ALL: 109059243Sobrien case RECOGNIZE_SCROLL: 109159243Sobrien 1092231990Smp if ((vp = adrof(STRcomplete)) != NULL && vp->vec != NULL) 1093131962Smp for (cp = vp->vec; *cp; cp++) { 1094231990Smp if (Strcmp(*cp, STREnhance) == 0) 1095231990Smp enhanced = 2; 1096231990Smp else if (Strcmp(*cp, STRigncase) == 0) 1097131962Smp igncase = 1; 1098231990Smp else if (Strcmp(*cp, STRenhance) == 0) 1099131962Smp enhanced = 1; 1100131962Smp } 1101131962Smp 110259243Sobrien if (enhanced || igncase) { 1103231990Smp if (!is_prefixmatch(target, item.s, enhanced)) 110459243Sobrien break; 110559243Sobrien } else { 1106167465Smp if (!is_prefix(target, item.s)) 110759243Sobrien break; 110859243Sobrien } 110959243Sobrien 1110167465Smp if (exec_check && !executable(exp_dir->s, item.s, dir_ok)) 111159243Sobrien break; 111259243Sobrien 1113167465Smp if (dir_check && !isadirectory(exp_dir->s, item.s)) 111459243Sobrien break; 111559243Sobrien 1116167465Smp if (text_check && isadirectory(exp_dir->s, item.s)) 111759243Sobrien break; 111859243Sobrien 111959243Sobrien /* 112059243Sobrien * Only pattern match directories if we're checking 112159243Sobrien * for directories. 112259243Sobrien */ 1123167465Smp if (gpat && !Gmatch(item.s, pat) && 1124167465Smp (dir_check || !isadirectory(exp_dir->s, item.s))) 112559243Sobrien break; 112659243Sobrien 112759243Sobrien /* 112859243Sobrien * Remove duplicates in command listing and completion 112959243Sobrien * AFEB added code for TW_LOGNAME and TW_USER cases 113059243Sobrien */ 113159243Sobrien if (looking == TW_COMMAND || looking == TW_LOGNAME 113259243Sobrien || looking == TW_USER || command == LIST) { 1133167465Smp buf.len = 0; 1134167465Smp Strbuf_append(&buf, item.s); 113559243Sobrien switch (looking) { 113659243Sobrien case TW_COMMAND: 113759243Sobrien if (!(dir_ok && exec_check)) 113859243Sobrien break; 1139167465Smp if (filetype(exp_dir->s, item.s) == '/') 1140167465Smp Strbuf_append1(&buf, '/'); 114159243Sobrien break; 114259243Sobrien 114359243Sobrien case TW_FILE: 114459243Sobrien case TW_DIRECTORY: 1145167465Smp Strbuf_append1(&buf, filetype(exp_dir->s, item.s)); 114659243Sobrien break; 114759243Sobrien 114859243Sobrien default: 114959243Sobrien break; 115059243Sobrien } 1151167465Smp Strbuf_terminate(&buf); 115259243Sobrien if ((looking == TW_COMMAND || looking == TW_USER 1153167465Smp || looking == TW_LOGNAME) && tw_item_find(buf.s)) 115459243Sobrien break; 115559243Sobrien else { 115659243Sobrien /* maximum length 1 (NULL) + 1 (~ or $) + 1 (filetype) */ 1157167465Smp tw_item_add(&buf); 115859243Sobrien if (command == LIST) 115959243Sobrien numitems++; 116059243Sobrien } 116159243Sobrien } 116259243Sobrien 116359243Sobrien if (command == RECOGNIZE || command == RECOGNIZE_ALL || 116459243Sobrien command == RECOGNIZE_SCROLL) { 1165167465Smp if (ignoring && ignored(item.s)) { 116659243Sobrien nignored++; 116759243Sobrien break; 1168167465Smp } 116959243Sobrien else if (command == RECOGNIZE_SCROLL) { 1170167465Smp add_scroll_tab(item.s); 117159243Sobrien cnt++; 117259243Sobrien } 1173167465Smp 117459243Sobrien if (match_unique_match || is_set(STRrecexact)) { 1175167465Smp if (StrQcmp(target, item.s) == 0) { /* EXACT match */ 1176167465Smp exp_name->len = 0; 1177167465Smp Strbuf_append(exp_name, item.s); 1178167465Smp Strbuf_terminate(exp_name); 117959243Sobrien numitems = 1; /* fake into expanding */ 118059243Sobrien non_unique_match = TRUE; 118159243Sobrien done = TRUE; 118259243Sobrien break; 118359243Sobrien } 118459243Sobrien } 1185167465Smp if (recognize(exp_name, item.s, name_length, ++numitems, 1186167465Smp enhanced, igncase)) 118759243Sobrien if (command != RECOGNIZE_SCROLL) 118859243Sobrien done = TRUE; 1189167465Smp if (enhanced && exp_name->len < name_length) { 1190167465Smp exp_name->len = 0; 1191167465Smp Strbuf_append(exp_name, target); 1192167465Smp Strbuf_terminate(exp_name); 1193167465Smp } 119459243Sobrien } 119559243Sobrien break; 119659243Sobrien 119759243Sobrien default: 119859243Sobrien break; 119959243Sobrien } 120059243Sobrien#ifdef TDEBUG 1201167465Smp xprintf("done item = %S\n", item.s); 120259243Sobrien#endif 120359243Sobrien } 1204167465Smp cleanup_until(&item); 120559243Sobrien 120659243Sobrien if (command == RECOGNIZE_SCROLL) { 120759243Sobrien if ((cnt <= curchoice) || (curchoice == -1)) { 120859243Sobrien curchoice = -1; 120959243Sobrien nignored = 0; 121059243Sobrien numitems = 0; 121159243Sobrien } else if (numitems > 1) { 121259243Sobrien if (curchoice < -1) 121359243Sobrien curchoice = cnt - 1; 1214167465Smp choose_scroll_tab(exp_name, cnt); 121559243Sobrien numitems = 1; 121659243Sobrien } 121759243Sobrien } 121859243Sobrien free_scroll_tab(); 121959243Sobrien 122059243Sobrien if (command == SPELL) 122159243Sobrien return d; 122259243Sobrien else { 122359243Sobrien if (ignoring && numitems == 0 && nignored > 0) 122459243Sobrien return -nignored; 122559243Sobrien else 122659243Sobrien return numitems; 122759243Sobrien } 122859243Sobrien} 122959243Sobrien 123059243Sobrien 123159243Sobrien/* tw_suffix(): 123259243Sobrien * Find and return the appropriate suffix character 123359243Sobrien */ 123459243Sobrien/*ARGSUSED*/ 1235167465Smpstatic Char 1236231990Smptw_suffix(int looking, struct Strbuf *word, const Char *exp_dir, Char *exp_name) 1237167465Smp{ 123859243Sobrien Char *ptr; 1239316957Sdchagin Char *dol; 124059243Sobrien struct varent *vp; 124159243Sobrien 124259243Sobrien (void) strip(exp_name); 124359243Sobrien 124459243Sobrien switch (looking) { 124559243Sobrien 124659243Sobrien case TW_LOGNAME: 124759243Sobrien return '/'; 124859243Sobrien 124959243Sobrien case TW_VARIABLE: 125059243Sobrien /* 125159243Sobrien * Don't consider array variables or empty variables 125259243Sobrien */ 1253100616Smp if ((vp = adrof(exp_name)) != NULL && vp->vec != NULL) { 125459243Sobrien if ((ptr = vp->vec[0]) == NULL || *ptr == '\0' || 125559243Sobrien vp->vec[1] != NULL) 125659243Sobrien return ' '; 125759243Sobrien } 125859243Sobrien else if ((ptr = tgetenv(exp_name)) == NULL || *ptr == '\0') 125959243Sobrien return ' '; 126059243Sobrien 1261316957Sdchagin if ((dol = Strrchr(word->s, '$')) != 0 && 1262316957Sdchagin dol[1] == '{' && Strchr(dol, '}') == NULL) 1263231990Smp return '}'; 1264231990Smp 126559243Sobrien return isadirectory(exp_dir, ptr) ? '/' : ' '; 126659243Sobrien 126759243Sobrien 126859243Sobrien case TW_DIRECTORY: 126959243Sobrien return '/'; 127059243Sobrien 127159243Sobrien case TW_COMMAND: 127259243Sobrien case TW_FILE: 127359243Sobrien return isadirectory(exp_dir, exp_name) ? '/' : ' '; 127459243Sobrien 127559243Sobrien case TW_ALIAS: 127659243Sobrien case TW_VARLIST: 127759243Sobrien case TW_WORDLIST: 127859243Sobrien case TW_SHELLVAR: 127959243Sobrien case TW_ENVVAR: 128059243Sobrien case TW_USER: 128159243Sobrien case TW_BINDING: 128259243Sobrien case TW_LIMIT: 128359243Sobrien case TW_SIGNAL: 128459243Sobrien case TW_JOB: 128559243Sobrien case TW_COMPLETION: 128659243Sobrien case TW_TEXT: 128759243Sobrien case TW_GRPNAME: 128859243Sobrien return ' '; 128959243Sobrien 129059243Sobrien default: 129159243Sobrien return '\0'; 129259243Sobrien } 129359243Sobrien} /* end tw_suffix */ 129459243Sobrien 129559243Sobrien 129659243Sobrien/* tw_fixword(): 129759243Sobrien * Repair a word after a spalling or a recognizwe 129859243Sobrien */ 129959243Sobrienstatic void 1300167465Smptw_fixword(int looking, struct Strbuf *word, Char *dir, Char *exp_name) 130159243Sobrien{ 130259243Sobrien Char *ptr; 130359243Sobrien 130459243Sobrien switch (looking) { 130559243Sobrien case TW_LOGNAME: 1306167465Smp word->len = 0; 1307167465Smp Strbuf_append1(word, '~'); 130859243Sobrien break; 1309167465Smp 131059243Sobrien case TW_VARIABLE: 1311167465Smp if ((ptr = Strrchr(word->s, '$')) != NULL) { 1312231990Smp if (ptr[1] == '{') ptr++; 1313167465Smp word->len = ptr + 1 - word->s; /* Delete after the dollar */ 1314167465Smp } else 1315167465Smp word->len = 0; 131659243Sobrien break; 131759243Sobrien 131859243Sobrien case TW_DIRECTORY: 131959243Sobrien case TW_FILE: 132059243Sobrien case TW_TEXT: 1321167465Smp word->len = 0; 1322167465Smp Strbuf_append(word, dir); /* put back dir part */ 132359243Sobrien break; 132459243Sobrien 132559243Sobrien default: 1326167465Smp word->len = 0; 132759243Sobrien break; 132859243Sobrien } 132959243Sobrien 133059243Sobrien (void) quote(exp_name); 1331167465Smp Strbuf_append(word, exp_name); /* add extended name */ 1332167465Smp Strbuf_terminate(word); 133359243Sobrien} /* end tw_fixword */ 133459243Sobrien 133559243Sobrien 133659243Sobrien/* tw_collect(): 133759243Sobrien * Collect items. Return -1 in case we were interrupted or 133859243Sobrien * the return value of tw_collect 133959243Sobrien * This is really a wrapper for tw_collect_items, serving two 134059243Sobrien * purposes: 134159243Sobrien * 1. Handles interrupt cleanups. 134259243Sobrien * 2. Retries if we had no matches, but there were ignored matches 134359243Sobrien */ 134459243Sobrienstatic int 1345167465Smptw_collect(COMMAND command, int looking, struct Strbuf *exp_dir, 1346167465Smp struct Strbuf *exp_name, Char *target, Char *pat, int flags, 1347167465Smp DIR *dir_fd) 134859243Sobrien{ 1349167465Smp volatile int ni; 135059243Sobrien jmp_buf_t osetexit; 135159243Sobrien 135259243Sobrien#ifdef TDEBUG 1353167465Smp xprintf("target = %S\n", target); 135459243Sobrien#endif 135559243Sobrien ni = 0; 135659243Sobrien getexit(osetexit); 135759243Sobrien for (;;) { 1358167465Smp volatile size_t omark; 1359167465Smp 136059243Sobrien (*tw_start_entry[looking])(dir_fd, pat); 136159243Sobrien InsideCompletion = 1; 136259243Sobrien if (setexit()) { 1363167465Smp cleanup_pop_mark(omark); 1364167465Smp resexit(osetexit); 136559243Sobrien /* interrupted, clean up */ 136659243Sobrien haderr = 0; 1367167465Smp ni = -1; /* flag error */ 1368167465Smp break; 136959243Sobrien } 1370167465Smp omark = cleanup_push_mark(); 1371167465Smp ni = tw_collect_items(command, looking, exp_dir, exp_name, target, pat, 1372167465Smp ni >= 0 ? flags : flags & ~TW_IGN_OK); 1373167465Smp cleanup_pop_mark(omark); 1374167465Smp resexit(osetexit); 1375167465Smp if (ni >= 0) 1376167465Smp break; 1377167465Smp } 1378167465Smp InsideCompletion = 0; 137959243Sobrien#if defined(SOLARIS2) && defined(i386) && !defined(__GNUC__) 1380167465Smp /* Compiler bug? (from PWP) */ 1381167465Smp if ((looking == TW_LOGNAME) || (looking == TW_USER)) 1382167465Smp tw_logname_end(); 1383167465Smp else if (looking == TW_GRPNAME) 1384167465Smp tw_grpname_end(); 1385167465Smp else 1386167465Smp tw_dir_end(); 138759243Sobrien#else /* !(SOLARIS2 && i386 && !__GNUC__) */ 1388167465Smp (*tw_end_entry[looking])(); 138959243Sobrien#endif /* !(SOLARIS2 && i386 && !__GNUC__) */ 1390167465Smp return(ni); 139159243Sobrien} /* end tw_collect */ 139259243Sobrien 139359243Sobrien 139459243Sobrien/* tw_list_items(): 139559243Sobrien * List the items that were found 139659243Sobrien * 139759243Sobrien * NOTE instead of looking at numerical vars listmax and listmaxrows 139859243Sobrien * we can look at numerical var listmax, and have a string value 139959243Sobrien * listmaxtype (or similar) than can have values 'items' and 'rows' 140059243Sobrien * (by default interpreted as 'items', for backwards compatibility) 140159243Sobrien */ 140259243Sobrienstatic void 1403167465Smptw_list_items(int looking, int numitems, int list_max) 140459243Sobrien{ 140559243Sobrien Char *ptr; 140659243Sobrien int max_items = 0; 140759243Sobrien int max_rows = 0; 140859243Sobrien 140983098Smp if (numitems == 0) 141083098Smp return; 141183098Smp 141259243Sobrien if ((ptr = varval(STRlistmax)) != STRNULL) { 141359243Sobrien while (*ptr) { 141459243Sobrien if (!Isdigit(*ptr)) { 141559243Sobrien max_items = 0; 141659243Sobrien break; 141759243Sobrien } 141859243Sobrien max_items = max_items * 10 + *ptr++ - '0'; 141959243Sobrien } 142059243Sobrien if ((max_items > 0) && (numitems > max_items) && list_max) 142159243Sobrien max_items = numitems; 142259243Sobrien else 142359243Sobrien max_items = 0; 142459243Sobrien } 142559243Sobrien 142659243Sobrien if (max_items == 0 && (ptr = varval(STRlistmaxrows)) != STRNULL) { 142759243Sobrien int rows; 142859243Sobrien 142959243Sobrien while (*ptr) { 143059243Sobrien if (!Isdigit(*ptr)) { 143159243Sobrien max_rows = 0; 143259243Sobrien break; 143359243Sobrien } 143459243Sobrien max_rows = max_rows * 10 + *ptr++ - '0'; 143559243Sobrien } 143659243Sobrien if (max_rows != 0 && looking != TW_JOB) 143759243Sobrien rows = find_rows(tw_item_get(), numitems, TRUE); 143859243Sobrien else 143959243Sobrien rows = numitems; /* underestimate for lines wider than the termH */ 144059243Sobrien if ((max_rows > 0) && (rows > max_rows) && list_max) 144159243Sobrien max_rows = rows; 144259243Sobrien else 144359243Sobrien max_rows = 0; 144459243Sobrien } 144559243Sobrien 144659243Sobrien 144759243Sobrien if (max_items || max_rows) { 1448145479Smp char tc, *sname; 144959243Sobrien const char *name; 145059243Sobrien int maxs; 145159243Sobrien 145259243Sobrien if (max_items) { 145359243Sobrien name = CGETS(30, 5, "items"); 145459243Sobrien maxs = max_items; 145559243Sobrien } 145659243Sobrien else { 145759243Sobrien name = CGETS(30, 6, "rows"); 145859243Sobrien maxs = max_rows; 145959243Sobrien } 146059243Sobrien 1461145479Smp sname = strsave(name); 1462167465Smp cleanup_push(sname, xfree); 146359243Sobrien xprintf(CGETS(30, 7, "There are %d %s, list them anyway? [n/y] "), 1464145479Smp maxs, sname); 1465167465Smp cleanup_until(sname); 146659243Sobrien flush(); 146759243Sobrien /* We should be in Rawmode here, so no \n to catch */ 1468167465Smp (void) xread(SHIN, &tc, 1); 146959243Sobrien xprintf("%c\r\n", tc); /* echo the char, do a newline */ 1470167465Smp /* 147159243Sobrien * Perhaps we should use the yesexpr from the 147259243Sobrien * actual locale 147359243Sobrien */ 147459243Sobrien if (strchr(CGETS(30, 13, "Yy"), tc) == NULL) 147559243Sobrien return; 147659243Sobrien } 147759243Sobrien 147859243Sobrien if (looking != TW_SIGNAL) 1479167465Smp qsort(tw_item_get(), numitems, sizeof(Char *), fcompare); 148059243Sobrien if (looking != TW_JOB) 148159243Sobrien print_by_column(STRNULL, tw_item_get(), numitems, TRUE); 148259243Sobrien else { 148359243Sobrien /* 148459243Sobrien * print one item on every line because jobs can have spaces 148559243Sobrien * and it is confusing. 148659243Sobrien */ 148759243Sobrien int i; 148859243Sobrien Char **w = tw_item_get(); 148959243Sobrien 149059243Sobrien for (i = 0; i < numitems; i++) { 149159243Sobrien xprintf("%S", w[i]); 149259243Sobrien if (Tty_raw_mode) 149359243Sobrien xputchar('\r'); 149459243Sobrien xputchar('\n'); 149559243Sobrien } 149659243Sobrien } 149759243Sobrien} /* end tw_list_items */ 149859243Sobrien 149959243Sobrien 150059243Sobrien/* t_search(): 150159243Sobrien * Perform a RECOGNIZE, LIST or SPELL command on string "word". 150259243Sobrien * 150359243Sobrien * Return value: 150459243Sobrien * >= 0: SPELL command: "distance" (see spdist()) 150559243Sobrien * other: No. of items found 150659243Sobrien * < 0: Error (message or beep is output) 150759243Sobrien */ 150859243Sobrien/*ARGSUSED*/ 150959243Sobrienint 1510167465Smpt_search(struct Strbuf *word, COMMAND command, int looking, int list_max, 1511167465Smp Char *pat, eChar suf) 151259243Sobrien{ 151359243Sobrien int numitems, /* Number of items matched */ 151459243Sobrien flags = 0, /* search flags */ 151559243Sobrien gpat = pat[0] != '\0', /* Glob pattern search */ 1516167465Smp res; /* Return value */ 1517167465Smp struct Strbuf exp_dir = Strbuf_INIT;/* dir after ~ expansion */ 1518167465Smp struct Strbuf dir = Strbuf_INIT; /* /x/y/z/ part in /x/y/z/f */ 1519167465Smp struct Strbuf exp_name = Strbuf_INIT;/* the recognized (extended) */ 1520167465Smp Char *name, /* f part in /d/d/d/f name */ 152159243Sobrien *target; /* Target to expand/correct/list */ 1522167465Smp DIR *dir_fd = NULL; 152359243Sobrien 152459243Sobrien /* 152559243Sobrien * bugfix by Marty Grossman (grossman@CC5.BBN.COM): directory listing can 152659243Sobrien * dump core when interrupted 152759243Sobrien */ 152859243Sobrien tw_item_free(); 152959243Sobrien 153059243Sobrien non_unique_match = FALSE; /* See the recexact code below */ 153159243Sobrien 1532167465Smp extract_dir_and_name(word->s, &dir, &name); 1533167465Smp cleanup_push(&dir, Strbuf_cleanup); 1534167465Smp cleanup_push(&name, xfree_indirect); 153559243Sobrien 153659243Sobrien /* 153759243Sobrien * SPECIAL HARDCODED COMPLETIONS: 153859243Sobrien * foo$variable -> TW_VARIABLE 153959243Sobrien * ~user -> TW_LOGNAME 154059243Sobrien * 154159243Sobrien */ 1542167465Smp if ((*word->s == '~') && (Strchr(word->s, '/') == NULL)) { 154359243Sobrien looking = TW_LOGNAME; 154459243Sobrien target = name; 154559243Sobrien gpat = 0; /* Override pattern mechanism */ 154659243Sobrien } 154759243Sobrien else if ((target = Strrchr(name, '$')) != 0 && 1548231990Smp (target[1] != '{' || Strchr(target, '}') == NULL) && 154959243Sobrien (Strchr(name, '/') == NULL)) { 155059243Sobrien target++; 1551231990Smp if (target[0] == '{') target++; 155259243Sobrien looking = TW_VARIABLE; 155359243Sobrien gpat = 0; /* Override pattern mechanism */ 155459243Sobrien } 155559243Sobrien else 155659243Sobrien target = name; 155759243Sobrien 155859243Sobrien /* 155959243Sobrien * Try to figure out what we should be looking for 156059243Sobrien */ 156159243Sobrien if (looking & TW_PATH) { 156259243Sobrien gpat = 0; /* pattern holds the pathname to be used */ 1563167465Smp Strbuf_append(&exp_dir, pat); 1564167465Smp if (exp_dir.len != 0 && exp_dir.s[exp_dir.len - 1] != '/') 1565167465Smp Strbuf_append1(&exp_dir, '/'); 1566167465Smp Strbuf_append(&exp_dir, dir.s); 156759243Sobrien } 1568167465Smp Strbuf_terminate(&exp_dir); 1569167465Smp cleanup_push(&exp_dir, Strbuf_cleanup); 157059243Sobrien 157159243Sobrien switch (looking & ~TW_PATH) { 157259243Sobrien case TW_NONE: 1573167465Smp res = -1; 1574167465Smp goto err_dir; 157559243Sobrien 157659243Sobrien case TW_ZERO: 157759243Sobrien looking = TW_FILE; 157859243Sobrien break; 157959243Sobrien 158059243Sobrien case TW_COMMAND: 1581167465Smp if (Strchr(word->s, '/') || (looking & TW_PATH)) { 158259243Sobrien looking = TW_FILE; 158359243Sobrien flags |= TW_EXEC_CHK; 158459243Sobrien flags |= TW_DIR_OK; 158559243Sobrien } 158659243Sobrien#ifdef notdef 158759243Sobrien /* PWP: don't even bother when doing ALL of the commands */ 1588167465Smp if (looking == TW_COMMAND && word->len == 0) { 1589167465Smp res = -1; 1590167465Smp goto err_dir; 1591167465Smp } 159259243Sobrien#endif 159359243Sobrien break; 159459243Sobrien 159559243Sobrien 159659243Sobrien case TW_VARLIST: 159759243Sobrien case TW_WORDLIST: 159859243Sobrien gpat = 0; /* pattern holds the name of the variable */ 159959243Sobrien break; 160059243Sobrien 160159243Sobrien case TW_EXPLAIN: 160259243Sobrien if (command == LIST && pat != NULL) { 160359243Sobrien xprintf("%S", pat); 160459243Sobrien if (Tty_raw_mode) 160559243Sobrien xputchar('\r'); 160659243Sobrien xputchar('\n'); 160759243Sobrien } 1608167465Smp res = 2; 1609167465Smp goto err_dir; 161059243Sobrien 161159243Sobrien default: 161259243Sobrien break; 161359243Sobrien } 161459243Sobrien 161559243Sobrien /* 161659243Sobrien * let fignore work only when we are not using a pattern 161759243Sobrien */ 161859243Sobrien flags |= (gpat == 0) ? TW_IGN_OK : TW_PAT_OK; 161959243Sobrien 162059243Sobrien#ifdef TDEBUG 162159243Sobrien xprintf(CGETS(30, 8, "looking = %d\n"), looking); 162259243Sobrien#endif 162359243Sobrien 162459243Sobrien switch (looking) { 1625167465Smp Char *user_name; 1626167465Smp 162759243Sobrien case TW_ALIAS: 162859243Sobrien case TW_SHELLVAR: 162959243Sobrien case TW_ENVVAR: 163059243Sobrien case TW_BINDING: 163159243Sobrien case TW_LIMIT: 163259243Sobrien case TW_SIGNAL: 163359243Sobrien case TW_JOB: 163459243Sobrien case TW_COMPLETION: 163559243Sobrien case TW_GRPNAME: 163659243Sobrien break; 163759243Sobrien 163859243Sobrien 163959243Sobrien case TW_VARIABLE: 1640167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1641167465Smp goto err_dir; 164259243Sobrien break; 164359243Sobrien 164459243Sobrien case TW_DIRECTORY: 164559243Sobrien flags |= TW_DIR_CHK; 164659243Sobrien 164759243Sobrien#ifdef notyet 164859243Sobrien /* 164959243Sobrien * This is supposed to expand the directory stack. 165059243Sobrien * Problems: 165159243Sobrien * 1. Slow 165259243Sobrien * 2. directories with the same name 165359243Sobrien */ 165459243Sobrien flags |= TW_DIR_OK; 165559243Sobrien#endif 165659243Sobrien#ifdef notyet 165759243Sobrien /* 165859243Sobrien * Supposed to do delayed expansion, but it is inconsistent 165959243Sobrien * from a user-interface point of view, since it does not 166059243Sobrien * immediately obey addsuffix 166159243Sobrien */ 1662167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1663167465Smp goto err_dir; 1664167465Smp if (isadirectory(exp_dir.s, name)) { 1665167465Smp if (exp_dir.len != 0 || name[0] != '\0') { 1666167465Smp Strbuf_append(&dir, name); 1667167465Smp if (dir.s[dir.len - 1] != '/') 1668167465Smp Strbuf_append1(&dir, '/'); 1669167465Smp Strbuf_terminate(&dir); 1670167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1671167465Smp goto err_dir; 1672167465Smp if (word->len != 0 && word->s[word->len - 1] != '/') { 1673167465Smp Strbuf_append1(word, '/'); 1674167465Smp Strbuf_terminate(word); 1675167465Smp } 167659243Sobrien name[0] = '\0'; 167759243Sobrien } 167859243Sobrien } 167959243Sobrien#endif 1680167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1681167465Smp goto err_dir; 168259243Sobrien break; 168359243Sobrien 168459243Sobrien case TW_TEXT: 168559243Sobrien flags |= TW_TEXT_CHK; 168659243Sobrien /*FALLTHROUGH*/ 168759243Sobrien case TW_FILE: 1688167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1689167465Smp goto err_dir; 169059243Sobrien break; 169159243Sobrien 169259243Sobrien case TW_PATH | TW_TEXT: 169359243Sobrien case TW_PATH | TW_FILE: 169459243Sobrien case TW_PATH | TW_DIRECTORY: 169559243Sobrien case TW_PATH | TW_COMMAND: 1696167465Smp if ((dir_fd = opendir(short2str(exp_dir.s))) == NULL) { 169783098Smp if (command == RECOGNIZE) 169883098Smp xprintf("\n"); 1699167465Smp xprintf("%S: %s", exp_dir.s, strerror(errno)); 170083098Smp if (command != RECOGNIZE) 170183098Smp xprintf("\n"); 170283098Smp NeedsRedraw = 1; 1703167465Smp res = -1; 1704167465Smp goto err_dir; 170559243Sobrien } 1706167465Smp if (exp_dir.len != 0 && exp_dir.s[exp_dir.len - 1] != '/') { 1707167465Smp Strbuf_append1(&exp_dir, '/'); 1708167465Smp Strbuf_terminate(&exp_dir); 1709167465Smp } 171059243Sobrien 171159243Sobrien looking &= ~TW_PATH; 171259243Sobrien 171359243Sobrien switch (looking) { 171459243Sobrien case TW_TEXT: 171559243Sobrien flags |= TW_TEXT_CHK; 171659243Sobrien break; 171759243Sobrien 171859243Sobrien case TW_FILE: 171959243Sobrien break; 172059243Sobrien 172159243Sobrien case TW_DIRECTORY: 172259243Sobrien flags |= TW_DIR_CHK; 172359243Sobrien break; 172459243Sobrien 172559243Sobrien case TW_COMMAND: 1726167465Smp xfree(name); 1727167465Smp target = name = Strsave(word->s); /* so it can match things */ 172859243Sobrien break; 172959243Sobrien 173059243Sobrien default: 173159243Sobrien abort(); /* Cannot happen */ 173259243Sobrien break; 173359243Sobrien } 173459243Sobrien break; 173559243Sobrien 173659243Sobrien case TW_LOGNAME: 1737167465Smp user_name = word->s + 1; 1738167465Smp goto do_user; 1739167465Smp 174059243Sobrien /*FALLTHROUGH*/ 174159243Sobrien case TW_USER: 1742167465Smp user_name = word->s; 1743167465Smp do_user: 174459243Sobrien /* 174559243Sobrien * Check if the spelling was already correct 174659243Sobrien * From: Rob McMahon <cudcv@cu.warwick.ac.uk> 174759243Sobrien */ 1748167465Smp if (command == SPELL && xgetpwnam(short2str(user_name)) != NULL) { 174959243Sobrien#ifdef YPBUGS 175059243Sobrien fix_yp_bugs(); 175159243Sobrien#endif /* YPBUGS */ 1752167465Smp res = 0; 1753167465Smp goto err_dir; 175459243Sobrien } 1755167465Smp xfree(name); 1756167465Smp target = name = Strsave(user_name); 175759243Sobrien break; 175859243Sobrien 175959243Sobrien case TW_COMMAND: 176059243Sobrien case TW_VARLIST: 176159243Sobrien case TW_WORDLIST: 1762167465Smp target = name = Strsave(word->s); /* so it can match things */ 176359243Sobrien break; 176459243Sobrien 176559243Sobrien default: 176659243Sobrien xprintf(CGETS(30, 9, 176759243Sobrien "\n%s internal error: I don't know what I'm looking for!\n"), 176859243Sobrien progname); 176959243Sobrien NeedsRedraw = 1; 1770167465Smp res = -1; 1771167465Smp goto err_dir; 177259243Sobrien } 177359243Sobrien 1774167465Smp cleanup_push(&exp_name, Strbuf_cleanup); 1775167465Smp numitems = tw_collect(command, looking, &exp_dir, &exp_name, target, pat, 1776167465Smp flags, dir_fd); 177759243Sobrien if (numitems == -1) 1778167465Smp goto end; 177959243Sobrien 178059243Sobrien switch (command) { 178159243Sobrien case RECOGNIZE: 178259243Sobrien case RECOGNIZE_ALL: 178359243Sobrien case RECOGNIZE_SCROLL: 178459243Sobrien if (numitems <= 0) 1785167465Smp break; 178659243Sobrien 1787167465Smp Strbuf_terminate(&exp_name); 1788167465Smp tw_fixword(looking, word, dir.s, exp_name.s); 178959243Sobrien 179059243Sobrien if (!match_unique_match && is_set(STRaddsuffix) && numitems == 1) { 179159243Sobrien switch (suf) { 179259243Sobrien case 0: /* Automatic suffix */ 1793167465Smp Strbuf_append1(word, 1794231990Smp tw_suffix(looking, word, exp_dir.s, exp_name.s)); 179559243Sobrien break; 179659243Sobrien 1797167465Smp case CHAR_ERR: /* No suffix */ 1798167465Smp break; 179959243Sobrien 180059243Sobrien default: /* completion specified suffix */ 1801167465Smp Strbuf_append1(word, suf); 180259243Sobrien break; 180359243Sobrien } 1804167465Smp Strbuf_terminate(word); 180559243Sobrien } 1806167465Smp break; 180759243Sobrien 180859243Sobrien case LIST: 180959243Sobrien tw_list_items(looking, numitems, list_max); 181059243Sobrien tw_item_free(); 1811167465Smp break; 181259243Sobrien 181359243Sobrien case SPELL: 1814167465Smp Strbuf_terminate(&exp_name); 1815167465Smp tw_fixword(looking, word, dir.s, exp_name.s); 1816167465Smp break; 181759243Sobrien 181859243Sobrien default: 181959243Sobrien xprintf("Bad tw_command\n"); 1820167465Smp numitems = 0; 182159243Sobrien } 1822167465Smp end: 1823167465Smp res = numitems; 1824167465Smp err_dir: 1825167465Smp cleanup_until(&dir); 1826167465Smp return res; 182759243Sobrien} /* end t_search */ 182859243Sobrien 182959243Sobrien 183059243Sobrien/* extract_dir_and_name(): 183159243Sobrien * parse full path in file into 2 parts: directory and file names 183259243Sobrien * Should leave final slash (/) at end of dir. 183359243Sobrien */ 183459243Sobrienstatic void 1835167465Smpextract_dir_and_name(const Char *path, struct Strbuf *dir, Char **name) 183659243Sobrien{ 1837145479Smp Char *p; 183859243Sobrien 183959243Sobrien p = Strrchr(path, '/'); 184069408Sache#ifdef WINNT_NATIVE 184159243Sobrien if (p == NULL) 184259243Sobrien p = Strrchr(path, ':'); 184369408Sache#endif /* WINNT_NATIVE */ 1844167465Smp if (p == NULL) 1845167465Smp *name = Strsave(path); 184659243Sobrien else { 184759243Sobrien p++; 1848167465Smp *name = Strsave(p); 1849167465Smp Strbuf_appendn(dir, path, p - path); 185059243Sobrien } 1851167465Smp Strbuf_terminate(dir); 185259243Sobrien} /* end extract_dir_and_name */ 185359243Sobrien 185459243Sobrien 185559243Sobrien/* dollar(): 185659243Sobrien * expand "/$old1/$old2/old3/" 185759243Sobrien * to "/value_of_old1/value_of_old2/old3/" 185859243Sobrien */ 185959243SobrienChar * 1860167465Smpdollar(const Char *old) 186159243Sobrien{ 1862167465Smp struct Strbuf buf = Strbuf_INIT; 186359243Sobrien 1864167465Smp while (*old) { 1865167465Smp if (*old != '$') 1866167465Smp Strbuf_append1(&buf, *old++); 186759243Sobrien else { 1868167465Smp if (expdollar(&buf, &old, QUOTE) == 0) { 1869167465Smp xfree(buf.s); 187059243Sobrien return NULL; 1871167465Smp } 187259243Sobrien } 1873167465Smp } 1874167465Smp return Strbuf_finish(&buf); 187559243Sobrien} /* end dollar */ 187659243Sobrien 187759243Sobrien 187859243Sobrien/* tilde(): 187959243Sobrien * expand ~person/foo to home_directory_of_person/foo 188059243Sobrien * or =<stack-entry> to <dir in stack entry> 188159243Sobrien */ 1882167465Smpstatic int 1883167465Smptilde(struct Strbuf *new, Char *old) 188459243Sobrien{ 1885145479Smp Char *o, *p; 188659243Sobrien 1887167465Smp new->len = 0; 188859243Sobrien switch (old[0]) { 1889167465Smp case '~': { 1890167465Smp Char *name, *home; 1891167465Smp 1892167465Smp old++; 1893167465Smp for (o = old; *o && *o != '/'; o++) 189459243Sobrien continue; 1895167465Smp name = Strnsave(old, o - old); 1896167465Smp home = gethdir(name); 1897167465Smp xfree(name); 1898167465Smp if (home == NULL) 1899167465Smp goto err; 1900167465Smp Strbuf_append(new, home); 1901167465Smp xfree(home); 1902167465Smp /* If the home directory expands to "/", we do 190383098Smp * not want to create "//" by appending a slash from o. 190483098Smp */ 1905167465Smp if (new->s[0] == '/' && new->len == 1 && *o == '/') 190683098Smp ++o; 1907167465Smp Strbuf_append(new, o); 1908167465Smp break; 1909167465Smp } 191059243Sobrien 191159243Sobrien case '=': 1912167465Smp if ((p = globequal(old)) == NULL) 1913167465Smp goto err; 1914167465Smp if (p != old) { 1915167465Smp Strbuf_append(new, p); 1916167465Smp xfree(p); 1917167465Smp break; 191859243Sobrien } 191959243Sobrien /*FALLTHROUGH*/ 192059243Sobrien 192159243Sobrien default: 1922167465Smp Strbuf_append(new, old); 1923167465Smp break; 192459243Sobrien } 1925167465Smp Strbuf_terminate(new); 1926167465Smp return 0; 1927167465Smp 1928167465Smp err: 1929167465Smp Strbuf_terminate(new); 1930167465Smp return -1; 193159243Sobrien} /* end tilde */ 193259243Sobrien 193359243Sobrien 193459243Sobrien/* expand_dir(): 193559243Sobrien * Open the directory given, expanding ~user and $var 193659243Sobrien * Optionally normalize the path given 193759243Sobrien */ 193859243Sobrienstatic int 1939167465Smpexpand_dir(const Char *dir, struct Strbuf *edir, DIR **dfd, COMMAND cmd) 194059243Sobrien{ 194159243Sobrien Char *nd = NULL; 1942167465Smp Char *tdir; 194359243Sobrien 1944167465Smp tdir = dollar(dir); 1945167465Smp cleanup_push(tdir, xfree); 1946167465Smp if (tdir == NULL || 1947167465Smp (tilde(edir, tdir) != 0) || 1948167465Smp !(nd = dnormalize(edir->len ? edir->s : STRdot, 1949167465Smp symlinks == SYM_IGNORE || symlinks == SYM_EXPAND)) || 195059243Sobrien ((*dfd = opendir(short2str(nd))) == NULL)) { 1951167465Smp xfree(nd); 1952167465Smp if (cmd == SPELL || SearchNoDirErr) { 1953167465Smp cleanup_until(tdir); 195459243Sobrien return (-2); 1955167465Smp } 195659243Sobrien /* 195759243Sobrien * From: Amos Shapira <amoss@cs.huji.ac.il> 195859243Sobrien * Print a better message when completion fails 195959243Sobrien */ 1960167465Smp xprintf("\n%S %s\n", edir->len ? edir->s : (tdir ? tdir : dir), 196159243Sobrien (errno == ENOTDIR ? CGETS(30, 10, "not a directory") : 196259243Sobrien (errno == ENOENT ? CGETS(30, 11, "not found") : 196359243Sobrien CGETS(30, 12, "unreadable")))); 196459243Sobrien NeedsRedraw = 1; 1965167465Smp cleanup_until(tdir); 196659243Sobrien return (-1); 196759243Sobrien } 1968167465Smp cleanup_until(tdir); 196959243Sobrien if (nd) { 197059243Sobrien if (*dir != '\0') { 1971167465Smp int slash; 197259243Sobrien 197359243Sobrien /* 197459243Sobrien * Copy and append a / if there was one 197559243Sobrien */ 1976167465Smp slash = edir->len != 0 && edir->s[edir->len - 1] == '/'; 1977167465Smp edir->len = 0; 1978167465Smp Strbuf_append(edir, nd); 1979167465Smp if (slash != 0 && edir->s[edir->len - 1] != '/') 1980167465Smp Strbuf_append1(edir, '/'); 1981167465Smp Strbuf_terminate(edir); 198259243Sobrien } 1983167465Smp xfree(nd); 198459243Sobrien } 198559243Sobrien return 0; 198659243Sobrien} /* end expand_dir */ 198759243Sobrien 198859243Sobrien 198959243Sobrien/* nostat(): 199059243Sobrien * Returns true if the directory should not be stat'd, 199159243Sobrien * false otherwise. 199259243Sobrien * This way, things won't grind to a halt when you complete in /afs 199359243Sobrien * or very large directories. 199459243Sobrien */ 1995145479Smpstatic int 1996167465Smpnostat(Char *dir) 199759243Sobrien{ 199859243Sobrien struct varent *vp; 1999145479Smp Char **cp; 200059243Sobrien 200159243Sobrien if ((vp = adrof(STRnostat)) == NULL || (cp = vp->vec) == NULL) 200259243Sobrien return FALSE; 200359243Sobrien for (; *cp != NULL; cp++) { 200459243Sobrien if (Strcmp(*cp, STRstar) == 0) 200559243Sobrien return TRUE; 200659243Sobrien if (Gmatch(dir, *cp)) 200759243Sobrien return TRUE; 200859243Sobrien } 200959243Sobrien return FALSE; 201059243Sobrien} /* end nostat */ 201159243Sobrien 201259243Sobrien 201359243Sobrien/* filetype(): 201459243Sobrien * Return a character that signifies a filetype 201559243Sobrien * symbology from 4.3 ls command. 201659243Sobrien */ 201759243Sobrienstatic Char 2018167465Smpfiletype(Char *dir, Char *file) 201959243Sobrien{ 202059243Sobrien if (dir) { 2021167465Smp Char *path; 202259243Sobrien char *ptr; 202359243Sobrien struct stat statb; 202459243Sobrien 202559243Sobrien if (nostat(dir)) return(' '); 202659243Sobrien 2027167465Smp path = Strspl(dir, file); 2028167465Smp ptr = short2str(path); 2029167465Smp xfree(path); 203059243Sobrien 2031167465Smp if (lstat(ptr, &statb) != -1) { 203259243Sobrien#ifdef S_ISLNK 203359243Sobrien if (S_ISLNK(statb.st_mode)) { /* Symbolic link */ 203459243Sobrien if (adrof(STRlistlinks)) { 203559243Sobrien if (stat(ptr, &statb) == -1) 203659243Sobrien return ('&'); 203759243Sobrien else if (S_ISDIR(statb.st_mode)) 203859243Sobrien return ('>'); 203959243Sobrien else 204059243Sobrien return ('@'); 204159243Sobrien } 204259243Sobrien else 204359243Sobrien return ('@'); 204459243Sobrien } 204559243Sobrien#endif 204659243Sobrien#ifdef S_ISSOCK 204759243Sobrien if (S_ISSOCK(statb.st_mode)) /* Socket */ 204859243Sobrien return ('='); 204959243Sobrien#endif 205059243Sobrien#ifdef S_ISFIFO 205159243Sobrien if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ 205259243Sobrien return ('|'); 205359243Sobrien#endif 205459243Sobrien#ifdef S_ISHIDDEN 205559243Sobrien if (S_ISHIDDEN(statb.st_mode)) /* Hidden Directory [aix] */ 205659243Sobrien return ('+'); 205759243Sobrien#endif 2058167465Smp#ifdef S_ISCDF 2059167465Smp { 2060167465Smp struct stat hpstatb; 2061167465Smp char *p2; 2062167465Smp 2063167465Smp p2 = strspl(ptr, "+"); /* Must append a '+' and re-stat(). */ 2064167465Smp if ((stat(p2, &hpstatb) != -1) && S_ISCDF(hpstatb.st_mode)) { 2065167465Smp xfree(p2); 2066167465Smp return ('+'); /* Context Dependent Files [hpux] */ 2067167465Smp } 2068167465Smp xfree(p2); 2069167465Smp } 2070167465Smp#endif 207159243Sobrien#ifdef S_ISNWK 207259243Sobrien if (S_ISNWK(statb.st_mode)) /* Network Special [hpux] */ 207359243Sobrien return (':'); 207459243Sobrien#endif 207559243Sobrien#ifdef S_ISCHR 207659243Sobrien if (S_ISCHR(statb.st_mode)) /* char device */ 207759243Sobrien return ('%'); 207859243Sobrien#endif 207959243Sobrien#ifdef S_ISBLK 208059243Sobrien if (S_ISBLK(statb.st_mode)) /* block device */ 208159243Sobrien return ('#'); 208259243Sobrien#endif 208359243Sobrien#ifdef S_ISDIR 208459243Sobrien if (S_ISDIR(statb.st_mode)) /* normal Directory */ 208559243Sobrien return ('/'); 208659243Sobrien#endif 208759243Sobrien if (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) 208859243Sobrien return ('*'); 208959243Sobrien } 209059243Sobrien } 209159243Sobrien return (' '); 209259243Sobrien} /* end filetype */ 209359243Sobrien 209459243Sobrien 209559243Sobrien/* isadirectory(): 209659243Sobrien * Return trus if the file is a directory 209759243Sobrien */ 209859243Sobrienstatic int 2099167465Smpisadirectory(const Char *dir, const Char *file) 2100167465Smp /* return 1 if dir/file is a directory */ 2101167465Smp /* uses stat rather than lstat to get dest. */ 210259243Sobrien{ 210359243Sobrien if (dir) { 2104167465Smp Char *path; 2105167465Smp char *cpath; 210659243Sobrien struct stat statb; 210759243Sobrien 2108167465Smp path = Strspl(dir, file); 2109167465Smp cpath = short2str(path); 2110167465Smp xfree(path); 2111167465Smp if (stat(cpath, &statb) >= 0) { /* resolve through symlink */ 211259243Sobrien#ifdef S_ISSOCK 211359243Sobrien if (S_ISSOCK(statb.st_mode)) /* Socket */ 211459243Sobrien return 0; 211559243Sobrien#endif 211659243Sobrien#ifdef S_ISFIFO 211759243Sobrien if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ 211859243Sobrien return 0; 211959243Sobrien#endif 212059243Sobrien if (S_ISDIR(statb.st_mode)) /* normal Directory */ 212159243Sobrien return 1; 212259243Sobrien } 212359243Sobrien } 212459243Sobrien return 0; 212559243Sobrien} /* end isadirectory */ 212659243Sobrien 212759243Sobrien 212859243Sobrien 212959243Sobrien/* find_rows(): 213059243Sobrien * Return how many rows needed to print sorted down columns 213159243Sobrien */ 213259243Sobrienstatic int 2133167465Smpfind_rows(Char *items[], int count, int no_file_suffix) 213459243Sobrien{ 2135145479Smp int i, columns, rows; 213659243Sobrien unsigned int maxwidth = 0; 213759243Sobrien 213859243Sobrien for (i = 0; i < count; i++) /* find widest string */ 213959243Sobrien maxwidth = max(maxwidth, (unsigned int) Strlen(items[i])); 214059243Sobrien 214159243Sobrien maxwidth += no_file_suffix ? 1 : 2; /* for the file tag and space */ 214259243Sobrien columns = (TermH + 1) / maxwidth; /* PWP: terminal size change */ 214359243Sobrien if (!columns) 214459243Sobrien columns = 1; 214559243Sobrien rows = (count + (columns - 1)) / columns; 214659243Sobrien 214759243Sobrien return rows; 214859243Sobrien} /* end rows_needed_by_print_by_column */ 214959243Sobrien 215059243Sobrien 215159243Sobrien/* print_by_column(): 215259243Sobrien * Print sorted down columns or across columns when the first 215359243Sobrien * word of $listflags shell variable contains 'x'. 215459243Sobrien * 215559243Sobrien */ 215659243Sobrienvoid 2157167465Smpprint_by_column(Char *dir, Char *items[], int count, int no_file_suffix) 215859243Sobrien{ 2159145479Smp int i, r, c, columns, rows; 2160167465Smp size_t w; 2161167465Smp unsigned int wx, maxwidth = 0; 216259243Sobrien Char *val; 2163145479Smp int across; 216459243Sobrien 216559243Sobrien lbuffed = 0; /* turn off line buffering */ 216659243Sobrien 216759243Sobrien 216859243Sobrien across = ((val = varval(STRlistflags)) != STRNULL) && 216959243Sobrien (Strchr(val, 'x') != NULL); 217059243Sobrien 2171145479Smp for (i = 0; i < count; i++) { /* find widest string */ 2172145479Smp maxwidth = max(maxwidth, (unsigned int) NLSStringWidth(items[i])); 2173145479Smp } 217459243Sobrien 217559243Sobrien maxwidth += no_file_suffix ? 1 : 2; /* for the file tag and space */ 217659243Sobrien columns = TermH / maxwidth; /* PWP: terminal size change */ 217759243Sobrien if (!columns || !isatty(didfds ? 1 : SHOUT)) 217859243Sobrien columns = 1; 217959243Sobrien rows = (count + (columns - 1)) / columns; 218059243Sobrien 218159243Sobrien i = -1; 218259243Sobrien for (r = 0; r < rows; r++) { 218359243Sobrien for (c = 0; c < columns; c++) { 218459243Sobrien i = across ? (i + 1) : (c * rows + r); 218559243Sobrien 218659243Sobrien if (i < count) { 2187145479Smp wx = 0; 2188167465Smp w = Strlen(items[i]); 218959243Sobrien 219059243Sobrien#ifdef COLOR_LS_F 219159243Sobrien if (no_file_suffix) { 219259243Sobrien /* Print the command name */ 219359243Sobrien Char f = items[i][w - 1]; 219459243Sobrien items[i][w - 1] = 0; 219559243Sobrien print_with_color(items[i], w - 1, f); 2196167465Smp items[i][w - 1] = f; 219759243Sobrien } 219859243Sobrien else { 219959243Sobrien /* Print filename followed by '/' or '*' or ' ' */ 220059243Sobrien print_with_color(items[i], w, filetype(dir, items[i])); 2201145479Smp wx++; 220259243Sobrien } 220359243Sobrien#else /* ifndef COLOR_LS_F */ 220459243Sobrien if (no_file_suffix) { 220559243Sobrien /* Print the command name */ 220659243Sobrien xprintf("%S", items[i]); 220759243Sobrien } 220859243Sobrien else { 220959243Sobrien /* Print filename followed by '/' or '*' or ' ' */ 2210167465Smp xprintf("%-S%c", items[i], filetype(dir, items[i])); 2211145479Smp wx++; 221259243Sobrien } 221359243Sobrien#endif /* COLOR_LS_F */ 221459243Sobrien 2215145479Smp if (c < (columns - 1)) { /* Not last column? */ 2216167465Smp w = NLSStringWidth(items[i]) + wx; 221759243Sobrien for (; w < maxwidth; w++) 221859243Sobrien xputchar(' '); 2219145479Smp } 222059243Sobrien } 222159243Sobrien else if (across) 222259243Sobrien break; 222359243Sobrien } 222459243Sobrien if (Tty_raw_mode) 222559243Sobrien xputchar('\r'); 222659243Sobrien xputchar('\n'); 222759243Sobrien } 222859243Sobrien 222959243Sobrien lbuffed = 1; /* turn back on line buffering */ 223059243Sobrien flush(); 223159243Sobrien} /* end print_by_column */ 223259243Sobrien 223359243Sobrien 223459243Sobrien/* StrQcmp(): 223559243Sobrien * Compare strings ignoring the quoting chars 223659243Sobrien */ 223759243Sobrienint 2238167465SmpStrQcmp(const Char *str1, const Char *str2) 223959243Sobrien{ 224059243Sobrien for (; *str1 && samecase(*str1 & TRIM) == samecase(*str2 & TRIM); 224159243Sobrien str1++, str2++) 224259243Sobrien continue; 224359243Sobrien /* 224459243Sobrien * The following case analysis is necessary so that characters which look 224559243Sobrien * negative collate low against normal characters but high against the 224659243Sobrien * end-of-string NUL. 224759243Sobrien */ 224859243Sobrien if (*str1 == '\0' && *str2 == '\0') 224959243Sobrien return (0); 225059243Sobrien else if (*str1 == '\0') 225159243Sobrien return (-1); 225259243Sobrien else if (*str2 == '\0') 225359243Sobrien return (1); 225459243Sobrien else 225559243Sobrien return ((*str1 & TRIM) - (*str2 & TRIM)); 225659243Sobrien} /* end StrQcmp */ 225759243Sobrien 225859243Sobrien 225959243Sobrien/* fcompare(): 2260167465Smp * Comparison routine for qsort, (Char **, Char **) 226159243Sobrien */ 226259243Sobrienint 2263167465Smpfcompare(const void *xfile1, const void *xfile2) 226459243Sobrien{ 2265167465Smp const Char *const *file1 = xfile1, *const *file2 = xfile2; 2266167465Smp 2267167465Smp return collate(*file1, *file2); 226859243Sobrien} /* end fcompare */ 226959243Sobrien 227059243Sobrien 227159243Sobrien/* catn(): 227259243Sobrien * Concatenate src onto tail of des. 227359243Sobrien * Des is a string whose maximum length is count. 227459243Sobrien * Always null terminate. 227559243Sobrien */ 227659243Sobrienvoid 2277167465Smpcatn(Char *des, const Char *src, int count) 227859243Sobrien{ 2279167465Smp while (*des && --count > 0) 228059243Sobrien des++; 2281167465Smp while (--count > 0) 228259243Sobrien if ((*des++ = *src++) == 0) 228359243Sobrien return; 228459243Sobrien *des = '\0'; 228559243Sobrien} /* end catn */ 228659243Sobrien 228759243Sobrien 228859243Sobrien/* copyn(): 228959243Sobrien * like strncpy but always leave room for trailing \0 229059243Sobrien * and always null terminate. 229159243Sobrien */ 229259243Sobrienvoid 2293167465Smpcopyn(Char *des, const Char *src, size_t count) 229459243Sobrien{ 2295167465Smp while (--count != 0) 229659243Sobrien if ((*des++ = *src++) == 0) 229759243Sobrien return; 229859243Sobrien *des = '\0'; 229959243Sobrien} /* end copyn */ 230059243Sobrien 230159243Sobrien 230259243Sobrien/* tgetenv(): 230359243Sobrien * like it's normal string counter-part 230459243Sobrien */ 230559243SobrienChar * 2306167465Smptgetenv(Char *str) 230759243Sobrien{ 230859243Sobrien Char **var; 2309145479Smp size_t len; 2310145479Smp int res; 231159243Sobrien 2312145479Smp len = Strlen(str); 231359243Sobrien /* Search the STR_environ for the entry matching str. */ 231459243Sobrien for (var = STR_environ; var != NULL && *var != NULL; var++) 231559243Sobrien if (Strlen(*var) >= len && (*var)[len] == '=') { 231659243Sobrien /* Temporarily terminate the string so we can copy the variable 231759243Sobrien name. */ 231859243Sobrien (*var)[len] = '\0'; 231959243Sobrien res = StrQcmp(*var, str); 232059243Sobrien /* Restore the '=' and return a pointer to the value of the 232159243Sobrien environment variable. */ 232259243Sobrien (*var)[len] = '='; 232359243Sobrien if (res == 0) 232459243Sobrien return (&((*var)[len + 1])); 232559243Sobrien } 232659243Sobrien return (NULL); 232759243Sobrien} /* end tgetenv */ 232859243Sobrien 232959243Sobrien 233059243Sobrienstruct scroll_tab_list *scroll_tab = 0; 233159243Sobrien 233259243Sobrienstatic void 2333167465Smpadd_scroll_tab(Char *item) 233459243Sobrien{ 233559243Sobrien struct scroll_tab_list *new_scroll; 233659243Sobrien 2337167465Smp new_scroll = xmalloc(sizeof(struct scroll_tab_list)); 233859243Sobrien new_scroll->element = Strsave(item); 233959243Sobrien new_scroll->next = scroll_tab; 234059243Sobrien scroll_tab = new_scroll; 234159243Sobrien} 234259243Sobrien 234359243Sobrienstatic void 2344167465Smpchoose_scroll_tab(struct Strbuf *exp_name, int cnt) 234559243Sobrien{ 234659243Sobrien struct scroll_tab_list *loop; 234759243Sobrien int tmp = cnt; 234859243Sobrien Char **ptr; 234959243Sobrien 2350167465Smp ptr = xmalloc(sizeof(Char *) * cnt); 2351167465Smp cleanup_push(ptr, xfree); 235259243Sobrien 235359243Sobrien for(loop = scroll_tab; loop && (tmp >= 0); loop = loop->next) 235459243Sobrien ptr[--tmp] = loop->element; 235559243Sobrien 2356167465Smp qsort(ptr, cnt, sizeof(Char *), fcompare); 2357167465Smp 2358167465Smp exp_name->len = 0; 2359167465Smp Strbuf_append(exp_name, ptr[curchoice]); 2360167465Smp Strbuf_terminate(exp_name); 2361167465Smp cleanup_until(ptr); 236259243Sobrien} 236359243Sobrien 236459243Sobrienstatic void 2365167465Smpfree_scroll_tab(void) 236659243Sobrien{ 236759243Sobrien struct scroll_tab_list *loop; 236859243Sobrien 236959243Sobrien while(scroll_tab) { 237059243Sobrien loop = scroll_tab; 237159243Sobrien scroll_tab = scroll_tab->next; 2372167465Smp xfree(loop->element); 2373167465Smp xfree(loop); 237459243Sobrien } 237559243Sobrien} 2376