tw.parse.c revision 167465
1167465Smp/* $Header: /p/tcsh/cvsroot/tcsh/tw.parse.c,v 3.123 2007/03/01 21:21:42 corinna 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 38167465SmpRCSID("$tcsh: tw.parse.c,v 3.123 2007/03/01 21:21:42 corinna Exp $") 3959243Sobrien 4059243Sobrien#include "tw.h" 4159243Sobrien#include "ed.h" 4259243Sobrien#include "tc.h" 4359243Sobrien 44167465Smp#include <assert.h> 45167465Smp 4669408Sache#ifdef WINNT_NATIVE 4759243Sobrien#include "nt.const.h" 4869408Sache#endif /* WINNT_NATIVE */ 4959243Sobrien#define EVEN(x) (((x) & 1) != 1) 5059243Sobrien 5159243Sobrien#define DOT_NONE 0 /* Don't display dot files */ 5259243Sobrien#define DOT_NOT 1 /* Don't display dot or dot-dot */ 5359243Sobrien#define DOT_ALL 2 /* Display all dot files */ 5459243Sobrien 5559243Sobrien/* TW_NONE, TW_COMMAND, TW_VARIABLE, TW_LOGNAME, */ 5659243Sobrien/* TW_FILE, TW_DIRECTORY, TW_VARLIST, TW_USER, */ 5759243Sobrien/* TW_COMPLETION, TW_ALIAS, TW_SHELLVAR, TW_ENVVAR, */ 5859243Sobrien/* TW_BINDING, TW_WORDLIST, TW_LIMIT, TW_SIGNAL */ 5959243Sobrien/* TW_JOB, TW_EXPLAIN, TW_TEXT, TW_GRPNAME */ 60167465Smpstatic void (*const tw_start_entry[]) (DIR *, const Char *) = { 6159243Sobrien tw_file_start, tw_cmd_start, tw_var_start, tw_logname_start, 6259243Sobrien tw_file_start, tw_file_start, tw_vl_start, tw_logname_start, 6359243Sobrien tw_complete_start, tw_alias_start, tw_var_start, tw_var_start, 6459243Sobrien tw_bind_start, tw_wl_start, tw_limit_start, tw_sig_start, 6559243Sobrien tw_job_start, tw_file_start, tw_file_start, tw_grpname_start 6659243Sobrien}; 6759243Sobrien 68167465Smpstatic int (*const tw_next_entry[]) (struct Strbuf *, struct Strbuf *, 69167465Smp int *) = { 7059243Sobrien tw_file_next, tw_cmd_next, tw_var_next, tw_logname_next, 7159243Sobrien tw_file_next, tw_file_next, tw_var_next, tw_logname_next, 7259243Sobrien tw_var_next, tw_var_next, tw_shvar_next, tw_envvar_next, 7359243Sobrien tw_bind_next, tw_wl_next, tw_limit_next, tw_sig_next, 7459243Sobrien tw_job_next, tw_file_next, tw_file_next, tw_grpname_next 7559243Sobrien}; 7659243Sobrien 77167465Smpstatic void (*const tw_end_entry[]) (void) = { 7859243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_logname_end, 7959243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_logname_end, 8059243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_dir_end, 8159243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_dir_end, 8259243Sobrien tw_dir_end, tw_dir_end, tw_dir_end, tw_grpname_end 8359243Sobrien}; 8459243Sobrien 8559243Sobrien/* #define TDEBUG */ 8659243Sobrien 8759243Sobrien/* Set to TRUE if recexact is set and an exact match is found 8859243Sobrien * along with other, longer, matches. 8959243Sobrien */ 9059243Sobrien 9159243Sobrienint curchoice = -1; 9259243Sobrien 9359243Sobrienint match_unique_match = FALSE; 9459243Sobrienint non_unique_match = FALSE; 95145479Smpstatic int SearchNoDirErr = 0; /* t_search returns -2 if dir is unreadable */ 9659243Sobrien 9759243Sobrien/* state so if a completion is interrupted, the input line doesn't get 9859243Sobrien nuked */ 9959243Sobrienint InsideCompletion = 0; 10059243Sobrien 10159243Sobrien/* do the expand or list on the command line -- SHOULD BE REPLACED */ 10259243Sobrien 103167465Smpstatic void extract_dir_and_name (const Char *, struct Strbuf *, 104167465Smp Char **); 105167465Smpstatic int insert_meta (const Char *, const Char *, 106167465Smp const Char *, int); 107167465Smpstatic int tilde (struct Strbuf *, Char *); 108167465Smpstatic int expand_dir (const Char *, struct Strbuf *, DIR **, 109167465Smp COMMAND); 110167465Smpstatic int nostat (Char *); 111167465Smpstatic Char filetype (Char *, Char *); 112167465Smpstatic int t_glob (Char ***, int); 113167465Smpstatic int c_glob (Char ***); 114167465Smpstatic int is_prefix (Char *, Char *); 115167465Smpstatic int is_prefixmatch (Char *, Char *, int); 116167465Smpstatic int is_suffix (Char *, Char *); 117167465Smpstatic int recognize (struct Strbuf *, const Char *, size_t, 118167465Smp int, int, int); 119167465Smpstatic int ignored (Char *); 120167465Smpstatic int isadirectory (const Char *, const Char *); 121167465Smpstatic int tw_collect_items (COMMAND, int, struct Strbuf *, 122167465Smp struct Strbuf *, Char *, const Char *, 123167465Smp int); 124167465Smpstatic int tw_collect (COMMAND, int, struct Strbuf *, 125167465Smp struct Strbuf *, Char *, Char *, int, 126167465Smp DIR *); 127167465Smpstatic Char tw_suffix (int, const Char *, Char *); 128167465Smpstatic void tw_fixword (int, struct Strbuf *, Char *, Char *); 129167465Smpstatic void tw_list_items (int, int, int); 130167465Smpstatic void add_scroll_tab (Char *); 131167465Smpstatic void choose_scroll_tab (struct Strbuf *, int); 132167465Smpstatic void free_scroll_tab (void); 133167465Smpstatic int find_rows (Char *[], int, int); 13459243Sobrien 13559243Sobrien#ifdef notdef 13659243Sobrien/* 13759243Sobrien * If we find a set command, then we break a=b to a= and word becomes 13859243Sobrien * b else, we don't break a=b. [don't use that; splits words badly and 13959243Sobrien * messes up tw_complete()] 14059243Sobrien */ 14159243Sobrien#define isaset(c, w) ((w)[-1] == '=' && \ 14259243Sobrien ((c)[0] == 's' && (c)[1] == 'e' && (c)[2] == 't' && \ 14359243Sobrien ((c[3] == ' ' || (c)[3] == '\t')))) 14459243Sobrien#endif 14559243Sobrien 14659243Sobrien/* TRUE if character must be quoted */ 14759243Sobrien#define tricky(w) (cmap(w, _META | _DOL | _QF | _QB | _ESC | _GLOB) && w != '#') 14859243Sobrien/* TRUE if double quotes don't protect character */ 14959243Sobrien#define tricky_dq(w) (cmap(w, _DOL | _QB)) 15059243Sobrien 15159243Sobrien/* tenematch(): 15259243Sobrien * Return: 15359243Sobrien * > 1: No. of items found 15459243Sobrien * = 1: Exactly one match / spelling corrected 15559243Sobrien * = 0: No match / spelling was correct 15659243Sobrien * < 0: Error (incl spelling correction impossible) 15759243Sobrien */ 15859243Sobrienint 159167465Smptenematch(Char *inputline, int num_read, COMMAND command) 16059243Sobrien{ 161167465Smp struct Strbuf qline = Strbuf_INIT; 16259243Sobrien Char qu = 0, *pat = STRNULL; 163167465Smp size_t wp, word, wordp, cmd_start, oword = 0, ocmd_start = 0; 164167465Smp Char *str_end, *cp; 165167465Smp Char *word_start; 166167465Smp Char *oword_start = NULL; 167167465Smp eChar suf = 0; 16859243Sobrien int looking; /* what we are looking for */ 16959243Sobrien int search_ret; /* what search returned for debugging */ 17059243Sobrien int backq = 0; 17159243Sobrien 17259243Sobrien str_end = &inputline[num_read]; 173167465Smp cleanup_push(&qline, Strbuf_cleanup); 17459243Sobrien 17559243Sobrien word_start = inputline; 176167465Smp word = cmd_start = 0; 17759243Sobrien for (cp = inputline; cp < str_end; cp++) { 17859243Sobrien if (!cmap(qu, _ESC)) { 17959243Sobrien if (cmap(*cp, _QF|_ESC)) { 18059243Sobrien if (qu == 0 || qu == *cp) { 18159243Sobrien qu ^= *cp; 18259243Sobrien continue; 18359243Sobrien } 18459243Sobrien } 18559243Sobrien if (qu != '\'' && cmap(*cp, _QB)) { 18659243Sobrien if ((backq ^= 1) != 0) { 18759243Sobrien ocmd_start = cmd_start; 18859243Sobrien oword_start = word_start; 18959243Sobrien oword = word; 19059243Sobrien word_start = cp + 1; 191167465Smp word = cmd_start = qline.len + 1; 19259243Sobrien } 19359243Sobrien else { 19459243Sobrien cmd_start = ocmd_start; 19559243Sobrien word_start = oword_start; 19659243Sobrien word = oword; 19759243Sobrien } 198167465Smp Strbuf_append1(&qline, *cp); 19959243Sobrien continue; 20059243Sobrien } 20159243Sobrien } 20259243Sobrien if (iscmdmeta(*cp)) 203167465Smp cmd_start = qline.len + 1; 20459243Sobrien 20559243Sobrien /* Don't quote '/' to make the recognize stuff work easily */ 20659243Sobrien /* Don't quote '$' in double quotes */ 20759243Sobrien 20859243Sobrien if (cmap(*cp, _ESC) && cp < str_end - 1 && cp[1] == HIST) 209167465Smp Strbuf_append1(&qline, *++cp | QUOTE); 21059243Sobrien else if (qu && (tricky(*cp) || *cp == '~') && !(qu == '\"' && tricky_dq(*cp))) 211167465Smp Strbuf_append1(&qline, *cp | QUOTE); 21259243Sobrien else 213167465Smp Strbuf_append1(&qline, *cp); 214167465Smp if (ismetahash(qline.s[qline.len - 1]) 215167465Smp /* || isaset(qline.s + cmd_start, qline.s + qline.len) */) 216167465Smp word = qline.len, word_start = cp + 1; 21759243Sobrien if (cmap(qu, _ESC)) 21859243Sobrien qu = 0; 21959243Sobrien } 220167465Smp Strbuf_terminate(&qline); 221167465Smp wp = qline.len; 22259243Sobrien 22359243Sobrien /* 22459243Sobrien * SPECIAL HARDCODED COMPLETIONS: 22559243Sobrien * first word of command -> TW_COMMAND 22659243Sobrien * everything else -> TW_ZERO 22759243Sobrien * 22859243Sobrien */ 229167465Smp looking = starting_a_command(qline.s + word - 1, qline.s) ? 23059243Sobrien TW_COMMAND : TW_ZERO; 23159243Sobrien 23259243Sobrien wordp = word; 23359243Sobrien 23459243Sobrien#ifdef TDEBUG 235167465Smp { 236167465Smp const Char *p; 237167465Smp 238167465Smp xprintf(CGETS(30, 1, "starting_a_command %d\n"), looking); 239167465Smp xprintf("\ncmd_start:%S:\n", qline.s + cmd_start); 240167465Smp xprintf("qline:%S:\n", qline.s); 241167465Smp xprintf("qline:"); 242167465Smp for (p = qline.s; *p; p++) 243167465Smp xprintf("%c", *p & QUOTE ? '-' : ' '); 244167465Smp xprintf(":\n"); 245167465Smp xprintf("word:%S:\n", qline.s + word); 246167465Smp xprintf("word:"); 247167465Smp for (p = qline.s + word; *p; p++) 248167465Smp xprintf("%c", *p & QUOTE ? '-' : ' '); 249167465Smp xprintf(":\n"); 250167465Smp } 25159243Sobrien#endif 25259243Sobrien 25359243Sobrien if ((looking == TW_COMMAND || looking == TW_ZERO) && 25459243Sobrien (command == RECOGNIZE || command == LIST || command == SPELL || 25559243Sobrien command == RECOGNIZE_SCROLL)) { 256167465Smp Char *p; 257167465Smp 25859243Sobrien#ifdef TDEBUG 25959243Sobrien xprintf(CGETS(30, 2, "complete %d "), looking); 26059243Sobrien#endif 261167465Smp p = qline.s + wordp; 262167465Smp looking = tw_complete(qline.s + cmd_start, &p, &pat, looking, &suf); 263167465Smp wordp = p - qline.s; 26459243Sobrien#ifdef TDEBUG 26559243Sobrien xprintf(CGETS(30, 3, "complete %d %S\n"), looking, pat); 26659243Sobrien#endif 26759243Sobrien } 26859243Sobrien 26959243Sobrien switch (command) { 270167465Smp Char *bptr; 27159243Sobrien Char *items[2], **ptr; 27259243Sobrien int i, count; 27359243Sobrien 27459243Sobrien case RECOGNIZE: 27559243Sobrien case RECOGNIZE_SCROLL: 276167465Smp case RECOGNIZE_ALL: { 277167465Smp struct Strbuf wordbuf = Strbuf_INIT; 278167465Smp Char *slshp; 279167465Smp 28059243Sobrien if (adrof(STRautocorrect)) { 281167465Smp if ((slshp = Strrchr(qline.s + wordp, '/')) != NULL && 282167465Smp slshp[1] != '\0') { 28359243Sobrien SearchNoDirErr = 1; 284167465Smp for (bptr = qline.s + wordp; bptr < slshp; bptr++) { 28559243Sobrien /* 28659243Sobrien * do not try to correct spelling of words containing 28759243Sobrien * globbing characters 28859243Sobrien */ 28959243Sobrien if (isglob(*bptr)) { 29059243Sobrien SearchNoDirErr = 0; 29159243Sobrien break; 29259243Sobrien } 29359243Sobrien } 29459243Sobrien } 29559243Sobrien } 29659243Sobrien else 29759243Sobrien slshp = STRNULL; 298167465Smp Strbuf_append(&wordbuf, qline.s + wordp); 299167465Smp Strbuf_terminate(&wordbuf); 300167465Smp cleanup_push(&wordbuf, Strbuf_cleanup); 301167465Smp search_ret = t_search(&wordbuf, command, looking, 1, pat, suf); 302167465Smp qline.len = wordp; 303167465Smp Strbuf_append(&qline, wordbuf.s); 304167465Smp Strbuf_terminate(&qline); 305167465Smp cleanup_until(&wordbuf); 30659243Sobrien SearchNoDirErr = 0; 30759243Sobrien 30859243Sobrien if (search_ret == -2) { 309167465Smp Char *rword; 31059243Sobrien 311167465Smp rword = Strsave(slshp); 312167465Smp cleanup_push(rword, xfree); 31359243Sobrien if (slshp != STRNULL) 31459243Sobrien *slshp = '\0'; 315167465Smp wordbuf = Strbuf_init; 316167465Smp Strbuf_append(&wordbuf, qline.s + wordp); 317167465Smp Strbuf_terminate(&wordbuf); 318167465Smp cleanup_push(&wordbuf, Strbuf_cleanup); 319167465Smp search_ret = spell_me(&wordbuf, looking, pat, suf); 32059243Sobrien if (search_ret == 1) { 321167465Smp Strbuf_append(&wordbuf, rword); 322167465Smp Strbuf_terminate(&wordbuf); 323167465Smp wp = wordp + wordbuf.len; 324167465Smp search_ret = t_search(&wordbuf, command, looking, 1, pat, suf); 32559243Sobrien } 326167465Smp qline.len = wordp; 327167465Smp Strbuf_append(&qline, wordbuf.s); 328167465Smp Strbuf_terminate(&qline); 329167465Smp cleanup_until(rword); 33059243Sobrien } 331167465Smp if (qline.s[wp] != '\0' && 332167465Smp insert_meta(word_start, str_end, qline.s + word, !qu) < 0) 333167465Smp goto err; /* error inserting */ 334167465Smp break; 335167465Smp } 33659243Sobrien 337167465Smp case SPELL: { 338167465Smp struct Strbuf wordbuf = Strbuf_INIT; 339167465Smp 34059243Sobrien for (bptr = word_start; bptr < str_end; bptr++) { 34159243Sobrien /* 34259243Sobrien * do not try to correct spelling of words containing globbing 34359243Sobrien * characters 34459243Sobrien */ 345167465Smp if (isglob(*bptr)) { 346167465Smp search_ret = 0; 347167465Smp goto end; 348167465Smp } 34959243Sobrien } 350167465Smp Strbuf_append(&wordbuf, qline.s + wordp); 351167465Smp Strbuf_terminate(&wordbuf); 352167465Smp cleanup_push(&wordbuf, Strbuf_cleanup); 353167465Smp search_ret = spell_me(&wordbuf, looking, pat, suf); 354167465Smp qline.len = wordp; 355167465Smp Strbuf_append(&qline, wordbuf.s); 356167465Smp Strbuf_terminate(&qline); 357167465Smp cleanup_until(&wordbuf); 35859243Sobrien if (search_ret == 1) { 359167465Smp if (insert_meta(word_start, str_end, qline.s + word, !qu) < 0) 360167465Smp goto err; /* error inserting */ 36159243Sobrien } 362167465Smp break; 363167465Smp } 36459243Sobrien 36559243Sobrien case PRINT_HELP: 366167465Smp do_help(qline.s + cmd_start); 367167465Smp search_ret = 1; 368167465Smp break; 36959243Sobrien 37059243Sobrien case GLOB: 37159243Sobrien case GLOB_EXPAND: 372167465Smp items[0] = Strsave(qline.s + wordp); 37359243Sobrien items[1] = NULL; 374167465Smp cleanup_push(items[0], xfree); 37559243Sobrien ptr = items; 376167465Smp count = (looking == TW_COMMAND && Strchr(qline.s + wordp, '/') == 0) ? 377167465Smp c_glob(&ptr) : 37859243Sobrien t_glob(&ptr, looking == TW_COMMAND); 379167465Smp cleanup_until(items[0]); 380167465Smp if (ptr != items) 381167465Smp cleanup_push(ptr, blk_cleanup); 38259243Sobrien if (count > 0) { 38359243Sobrien if (command == GLOB) 38459243Sobrien print_by_column(STRNULL, ptr, count, 0); 38559243Sobrien else { 38659243Sobrien DeleteBack(str_end - word_start);/* get rid of old word */ 38759243Sobrien for (i = 0; i < count; i++) 38859243Sobrien if (ptr[i] && *ptr[i]) { 38959243Sobrien (void) quote(ptr[i]); 39059243Sobrien if (insert_meta(0, 0, ptr[i], 0) < 0 || 39159243Sobrien InsertStr(STRspace) < 0) { 392167465Smp if (ptr != items) 393167465Smp cleanup_until(ptr); 394167465Smp goto err; /* error inserting */ 39559243Sobrien } 39659243Sobrien } 39759243Sobrien } 39859243Sobrien } 399167465Smp if (ptr != items) 400167465Smp cleanup_until(ptr); 401167465Smp search_ret = count; 402167465Smp break; 40359243Sobrien 40459243Sobrien case VARS_EXPAND: 405167465Smp bptr = dollar(qline.s + word); 406167465Smp if (bptr != NULL) { 407167465Smp if (insert_meta(word_start, str_end, bptr, !qu) < 0) { 408167465Smp xfree(bptr); 409167465Smp goto err; /* error inserting */ 410167465Smp } 411167465Smp xfree(bptr); 412167465Smp search_ret = 1; 413167465Smp break; 41459243Sobrien } 415167465Smp search_ret = 0; 416167465Smp break; 41759243Sobrien 41859243Sobrien case PATH_NORMALIZE: 419167465Smp if ((bptr = dnormalize(qline.s + wordp, symlinks == SYM_IGNORE || 420167465Smp symlinks == SYM_EXPAND)) != NULL) { 421167465Smp if (insert_meta(word_start, str_end, bptr, !qu) < 0) { 422167465Smp xfree(bptr); 423167465Smp goto err; /* error inserting */ 424167465Smp } 425167465Smp xfree(bptr); 426167465Smp search_ret = 1; 427167465Smp break; 42859243Sobrien } 429167465Smp search_ret = 0; 430167465Smp break; 43159243Sobrien 432167465Smp case COMMAND_NORMALIZE: { 433167465Smp Char *p; 434167465Smp int found; 43559243Sobrien 436167465Smp found = !cmd_expand(qline.s + wordp, &p); 437167465Smp 438167465Smp if (!found) { 439167465Smp xfree(p); 440167465Smp search_ret = 0; 441167465Smp break; 442167465Smp } 443167465Smp if (insert_meta(word_start, str_end, p, !qu) < 0) { 444167465Smp xfree(p); 445167465Smp goto err; /* error inserting */ 446167465Smp } 447167465Smp xfree(p); 448167465Smp search_ret = 1; 449167465Smp break; 450167465Smp } 451167465Smp 45259243Sobrien case LIST: 453167465Smp case LIST_ALL: { 454167465Smp struct Strbuf wordbuf = Strbuf_INIT; 45559243Sobrien 456167465Smp Strbuf_append(&wordbuf, qline.s + wordp); 457167465Smp Strbuf_terminate(&wordbuf); 458167465Smp cleanup_push(&wordbuf, Strbuf_cleanup); 459167465Smp search_ret = t_search(&wordbuf, LIST, looking, 1, pat, suf); 460167465Smp qline.len = wordp; 461167465Smp Strbuf_append(&qline, wordbuf.s); 462167465Smp Strbuf_terminate(&qline); 463167465Smp cleanup_until(&wordbuf); 464167465Smp break; 465167465Smp } 466167465Smp 46759243Sobrien default: 46859243Sobrien xprintf(CGETS(30, 4, "%s: Internal match error.\n"), progname); 469167465Smp search_ret = 1; 470167465Smp } 471167465Smp end: 472167465Smp cleanup_until(&qline); 473167465Smp return search_ret; 47459243Sobrien 475167465Smp err: 476167465Smp cleanup_until(&qline); 477167465Smp return -1; 47859243Sobrien} /* end tenematch */ 47959243Sobrien 48059243Sobrien 48159243Sobrien/* t_glob(): 48259243Sobrien * Return a list of files that match the pattern 48359243Sobrien */ 48459243Sobrienstatic int 485167465Smpt_glob(Char ***v, int cmd) 48659243Sobrien{ 48759243Sobrien jmp_buf_t osetexit; 488167465Smp int gflag; 48959243Sobrien 49059243Sobrien if (**v == 0) 49159243Sobrien return (0); 492167465Smp gflag = tglob(*v); 49359243Sobrien if (gflag) { 494167465Smp size_t omark; 495167465Smp 49659243Sobrien getexit(osetexit); /* make sure to come back here */ 497167465Smp omark = cleanup_push_mark(); 49859243Sobrien if (setexit() == 0) 499167465Smp *v = globall(*v, gflag); 500167465Smp cleanup_pop_mark(omark); 50159243Sobrien resexit(osetexit); 50259243Sobrien if (haderr) { 50359243Sobrien haderr = 0; 50459243Sobrien NeedsRedraw = 1; 50559243Sobrien return (-1); 50659243Sobrien } 50759243Sobrien if (*v == 0) 50859243Sobrien return (0); 50959243Sobrien } 51059243Sobrien else 51159243Sobrien return (0); 51259243Sobrien 51359243Sobrien if (cmd) { 51459243Sobrien Char **av = *v, *p; 515167465Smp int fwd, i; 51659243Sobrien 517167465Smp for (i = 0, fwd = 0; av[i] != NULL; i++) 51859243Sobrien if (!executable(NULL, av[i], 0)) { 51959243Sobrien fwd++; 52059243Sobrien p = av[i]; 52159243Sobrien av[i] = NULL; 522167465Smp xfree(p); 52359243Sobrien } 52459243Sobrien else if (fwd) 52559243Sobrien av[i - fwd] = av[i]; 52659243Sobrien 52759243Sobrien if (fwd) 52859243Sobrien av[i - fwd] = av[i]; 52959243Sobrien } 53059243Sobrien 531167465Smp return blklen(*v); 53259243Sobrien} /* end t_glob */ 53359243Sobrien 53459243Sobrien 53559243Sobrien/* c_glob(): 53659243Sobrien * Return a list of commands that match the pattern 53759243Sobrien */ 53859243Sobrienstatic int 539167465Smpc_glob(Char ***v) 54059243Sobrien{ 541167465Smp struct blk_buf av = BLK_BUF_INIT; 542167465Smp struct Strbuf cmd = Strbuf_INIT, dir = Strbuf_INIT; 543167465Smp Char *pat = **v; 544167465Smp int flag; 54559243Sobrien 54659243Sobrien if (pat == NULL) 54759243Sobrien return (0); 54859243Sobrien 549167465Smp cleanup_push(&av, bb_cleanup); 550167465Smp cleanup_push(&cmd, Strbuf_cleanup); 551167465Smp cleanup_push(&dir, Strbuf_cleanup); 55259243Sobrien 55359243Sobrien tw_cmd_start(NULL, NULL); 554167465Smp while (cmd.len = 0, tw_cmd_next(&cmd, &dir, &flag) != 0) { 555167465Smp Strbuf_terminate(&cmd); 556167465Smp if (Gmatch(cmd.s, pat)) 557167465Smp bb_append(&av, Strsave(cmd.s)); 558167465Smp } 55959243Sobrien tw_dir_end(); 560167465Smp *v = bb_finish(&av); 561167465Smp cleanup_ignore(&av); 562167465Smp cleanup_until(&av); 56359243Sobrien 564167465Smp return av.len; 56559243Sobrien} /* end c_glob */ 56659243Sobrien 56759243Sobrien 56859243Sobrien/* insert_meta(): 56959243Sobrien * change the word before the cursor. 57059243Sobrien * cp must point to the start of the unquoted word. 57159243Sobrien * cpend to the end of it. 57259243Sobrien * word is the text that has to be substituted. 57359243Sobrien * strategy: 57459243Sobrien * try to keep all the quote characters of the user's input. 57559243Sobrien * change quote type only if necessary. 57659243Sobrien */ 57759243Sobrienstatic int 578167465Smpinsert_meta(const Char *cp, const Char *cpend, const Char *word, 579167465Smp int closequotes) 58059243Sobrien{ 581167465Smp struct Strbuf buffer = Strbuf_INIT; 582167465Smp Char *bptr; 583167465Smp const Char *wptr; 58459243Sobrien int in_sync = (cp != NULL); 585145479Smp Char qu = 0; 58659243Sobrien int ndel = (int) (cp ? cpend - cp : 0); 58759243Sobrien Char w, wq; 588167465Smp int res; 58959243Sobrien 590167465Smp for (wptr = word;;) { 59159243Sobrien if (cp >= cpend) 59259243Sobrien in_sync = 0; 59359243Sobrien if (in_sync && !cmap(qu, _ESC) && cmap(*cp, _QF|_ESC)) 59459243Sobrien if (qu == 0 || qu == *cp) { 59559243Sobrien qu ^= *cp; 596167465Smp Strbuf_append1(&buffer, *cp++); 59759243Sobrien continue; 59859243Sobrien } 59959243Sobrien w = *wptr; 60059243Sobrien if (w == 0) 60159243Sobrien break; 60259243Sobrien 60359243Sobrien wq = w & QUOTE; 60459243Sobrien w &= ~QUOTE; 60559243Sobrien 60659243Sobrien if (cmap(w, _ESC | _QF)) 60759243Sobrien wq = QUOTE; /* quotes are always quoted */ 60859243Sobrien 60959243Sobrien if (!wq && qu && tricky(w) && !(qu == '\"' && tricky_dq(w))) { 61059243Sobrien /* We have to unquote the character */ 61159243Sobrien in_sync = 0; 61259243Sobrien if (cmap(qu, _ESC)) 613167465Smp buffer.s[buffer.len - 1] = w; 61459243Sobrien else { 615167465Smp Strbuf_append1(&buffer, qu); 616167465Smp Strbuf_append1(&buffer, w); 61759243Sobrien if (wptr[1] == 0) 61859243Sobrien qu = 0; 61959243Sobrien else 620167465Smp Strbuf_append1(&buffer, qu); 62159243Sobrien } 62259243Sobrien } else if (qu && w == qu) { 62359243Sobrien in_sync = 0; 624167465Smp if (buffer.len != 0 && buffer.s[buffer.len - 1] == qu) { 62559243Sobrien /* User misunderstanding :) */ 626167465Smp buffer.s[buffer.len - 1] = '\\'; 627167465Smp Strbuf_append1(&buffer, w); 62859243Sobrien qu = 0; 62959243Sobrien } else { 630167465Smp Strbuf_append1(&buffer, qu); 631167465Smp Strbuf_append1(&buffer, '\\'); 632167465Smp Strbuf_append1(&buffer, w); 633167465Smp Strbuf_append1(&buffer, qu); 63459243Sobrien } 63559243Sobrien } 63659243Sobrien else if (wq && qu == '\"' && tricky_dq(w)) { 63759243Sobrien in_sync = 0; 638167465Smp Strbuf_append1(&buffer, qu); 639167465Smp Strbuf_append1(&buffer, '\\'); 640167465Smp Strbuf_append1(&buffer, w); 641167465Smp Strbuf_append1(&buffer, qu); 642167465Smp } else if (wq && 643167465Smp ((!qu && (tricky(w) || (w == HISTSUB && buffer.len == 0))) || 644167465Smp (!cmap(qu, _ESC) && w == HIST))) { 64559243Sobrien in_sync = 0; 646167465Smp Strbuf_append1(&buffer, '\\'); 647167465Smp Strbuf_append1(&buffer, w); 64859243Sobrien } else { 64959243Sobrien if (in_sync && *cp++ != w) 65059243Sobrien in_sync = 0; 651167465Smp Strbuf_append1(&buffer, w); 65259243Sobrien } 65359243Sobrien wptr++; 65459243Sobrien if (cmap(qu, _ESC)) 65559243Sobrien qu = 0; 65659243Sobrien } 65759243Sobrien if (closequotes && qu && !cmap(qu, _ESC)) 658167465Smp Strbuf_append1(&buffer, w); 659167465Smp bptr = Strbuf_finish(&buffer); 66059243Sobrien if (ndel) 66159243Sobrien DeleteBack(ndel); 662167465Smp res = InsertStr(bptr); 663167465Smp xfree(bptr); 664167465Smp return res; 66559243Sobrien} /* end insert_meta */ 66659243Sobrien 66759243Sobrien 66859243Sobrien 66959243Sobrien/* is_prefix(): 67059243Sobrien * return true if check matches initial chars in template 67159243Sobrien * This differs from PWB imatch in that if check is null 67259243Sobrien * it matches anything 67359243Sobrien */ 67459243Sobrienstatic int 675167465Smpis_prefix(Char *check, Char *template) 67659243Sobrien{ 67759243Sobrien for (; *check; check++, template++) 67859243Sobrien if ((*check & TRIM) != (*template & TRIM)) 67959243Sobrien return (FALSE); 68059243Sobrien return (TRUE); 68159243Sobrien} /* end is_prefix */ 68259243Sobrien 68359243Sobrien 68459243Sobrien/* is_prefixmatch(): 68559243Sobrien * return true if check matches initial chars in template 68659243Sobrien * This differs from PWB imatch in that if check is null 68759243Sobrien * it matches anything 68859243Sobrien * and matches on shortening of commands 68959243Sobrien */ 69059243Sobrienstatic int 691167465Smpis_prefixmatch(Char *check, Char *template, int igncase) 69259243Sobrien{ 69359243Sobrien Char MCH1, MCH2; 69459243Sobrien 69559243Sobrien for (; *check; check++, template++) { 69659243Sobrien if ((*check & TRIM) != (*template & TRIM)) { 69759243Sobrien MCH1 = (*check & TRIM); 69859243Sobrien MCH2 = (*template & TRIM); 69959243Sobrien MCH1 = Isupper(MCH1) ? Tolower(MCH1) : MCH1; 70059243Sobrien MCH2 = Isupper(MCH2) ? Tolower(MCH2) : MCH2; 70159243Sobrien if (MCH1 != MCH2) { 70259243Sobrien if (!igncase && ((*check & TRIM) == '-' || 70359243Sobrien (*check & TRIM) == '.' || 70459243Sobrien (*check & TRIM) == '_')) { 70559243Sobrien MCH1 = MCH2 = (*check & TRIM); 70659243Sobrien if (MCH1 == '_') { 70759243Sobrien MCH2 = '-'; 70859243Sobrien } else if (MCH1 == '-') { 70959243Sobrien MCH2 = '_'; 71059243Sobrien } 71159243Sobrien for (;*template && (*template & TRIM) != MCH1 && 71259243Sobrien (*template & TRIM) != MCH2; template++) 71359243Sobrien continue; 71459243Sobrien if (!*template) { 71559243Sobrien return (FALSE); 71659243Sobrien } 71759243Sobrien } else { 71859243Sobrien return (FALSE); 71959243Sobrien } 72059243Sobrien } 72159243Sobrien } 72259243Sobrien } 72359243Sobrien return (TRUE); 72459243Sobrien} /* end is_prefixmatch */ 72559243Sobrien 72659243Sobrien 72759243Sobrien/* is_suffix(): 72859243Sobrien * Return true if the chars in template appear at the 72959243Sobrien * end of check, I.e., are it's suffix. 73059243Sobrien */ 73159243Sobrienstatic int 732167465Smpis_suffix(Char *check, Char *template) 73359243Sobrien{ 734145479Smp Char *t, *c; 73559243Sobrien 736167465Smp t = Strend(template); 737167465Smp c = Strend(check); 73859243Sobrien for (;;) { 73959243Sobrien if (t == template) 74059243Sobrien return 1; 74159243Sobrien if (c == check || (*--t & TRIM) != (*--c & TRIM)) 74259243Sobrien return 0; 74359243Sobrien } 74459243Sobrien} /* end is_suffix */ 74559243Sobrien 74659243Sobrien 74759243Sobrien/* ignored(): 74859243Sobrien * Return true if this is an ignored item 74959243Sobrien */ 75059243Sobrienstatic int 751167465Smpignored(Char *item) 75259243Sobrien{ 75359243Sobrien struct varent *vp; 754145479Smp Char **cp; 75559243Sobrien 75659243Sobrien if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) 75759243Sobrien return (FALSE); 75859243Sobrien for (; *cp != NULL; cp++) 75959243Sobrien if (is_suffix(item, *cp)) 76059243Sobrien return (TRUE); 76159243Sobrien return (FALSE); 76259243Sobrien} /* end ignored */ 76359243Sobrien 76459243Sobrien 76559243Sobrien 76659243Sobrien/* starting_a_command(): 76759243Sobrien * return true if the command starting at wordstart is a command 76859243Sobrien */ 76959243Sobrienint 770167465Smpstarting_a_command(Char *wordstart, Char *inputline) 77159243Sobrien{ 772145479Smp Char *ptr, *ncmdstart; 773145479Smp int count, bsl; 77459243Sobrien static Char 77559243Sobrien cmdstart[] = {'`', ';', '&', '(', '|', '\0'}, 77659243Sobrien cmdalive[] = {' ', '\t', '\'', '"', '<', '>', '\0'}; 77759243Sobrien 77859243Sobrien /* 77959243Sobrien * Find if the number of backquotes is odd or even. 78059243Sobrien */ 78159243Sobrien for (ptr = wordstart, count = 0; 78259243Sobrien ptr >= inputline; 78359243Sobrien count += (*ptr-- == '`')) 78459243Sobrien continue; 78559243Sobrien /* 78659243Sobrien * if the number of backquotes is even don't include the backquote char in 78759243Sobrien * the list of command starting delimiters [if it is zero, then it does not 78859243Sobrien * matter] 78959243Sobrien */ 79059243Sobrien ncmdstart = cmdstart + EVEN(count); 79159243Sobrien 79259243Sobrien /* 79359243Sobrien * look for the characters previous to this word if we find a command 79459243Sobrien * starting delimiter we break. if we find whitespace and another previous 79559243Sobrien * word then we are not a command 79659243Sobrien * 79759243Sobrien * count is our state machine: 0 looking for anything 1 found white-space 79859243Sobrien * looking for non-ws 79959243Sobrien */ 80059243Sobrien for (count = 0; wordstart >= inputline; wordstart--) { 80159243Sobrien if (*wordstart == '\0') 80259243Sobrien continue; 803145479Smp if (Strchr(ncmdstart, *wordstart)) { 804145479Smp for (ptr = wordstart, bsl = 0; *(--ptr) == '\\'; bsl++); 805145479Smp if (bsl & 1) { 806145479Smp wordstart--; 807145479Smp continue; 808145479Smp } else 809145479Smp break; 810145479Smp } 81159243Sobrien /* 81259243Sobrien * found white space 81359243Sobrien */ 81459243Sobrien if ((ptr = Strchr(cmdalive, *wordstart)) != NULL) 81559243Sobrien count = 1; 81659243Sobrien if (count == 1 && !ptr) 81759243Sobrien return (FALSE); 81859243Sobrien } 81959243Sobrien 82059243Sobrien if (wordstart > inputline) 82159243Sobrien switch (*wordstart) { 82259243Sobrien case '&': /* Look for >& */ 82359243Sobrien while (wordstart > inputline && 82459243Sobrien (*--wordstart == ' ' || *wordstart == '\t')) 82559243Sobrien continue; 82659243Sobrien if (*wordstart == '>') 82759243Sobrien return (FALSE); 82859243Sobrien break; 82959243Sobrien case '(': /* check for foreach, if etc. */ 83059243Sobrien while (wordstart > inputline && 83159243Sobrien (*--wordstart == ' ' || *wordstart == '\t')) 83259243Sobrien continue; 83359243Sobrien if (!iscmdmeta(*wordstart) && 83459243Sobrien (*wordstart != ' ' && *wordstart != '\t')) 83559243Sobrien return (FALSE); 83659243Sobrien break; 83759243Sobrien default: 83859243Sobrien break; 83959243Sobrien } 84059243Sobrien return (TRUE); 84159243Sobrien} /* end starting_a_command */ 84259243Sobrien 84359243Sobrien 84459243Sobrien/* recognize(): 84559243Sobrien * Object: extend what user typed up to an ambiguity. 84659243Sobrien * Algorithm: 84759243Sobrien * On first match, copy full item (assume it'll be the only match) 84859243Sobrien * On subsequent matches, shorten exp_name to the first 84959243Sobrien * character mismatch between exp_name and item. 85059243Sobrien * If we shorten it back to the prefix length, stop searching. 85159243Sobrien */ 85259243Sobrienstatic int 853167465Smprecognize(struct Strbuf *exp_name, const Char *item, size_t name_length, 854167465Smp int numitems, int enhanced, int igncase) 85559243Sobrien{ 85659243Sobrien Char MCH1, MCH2; 857167465Smp Char *x; 858167465Smp const Char *ent; 859167465Smp size_t len = 0; 86059243Sobrien 86159243Sobrien if (numitems == 1) { /* 1st match */ 862167465Smp exp_name->len = 0; 863167465Smp Strbuf_append(exp_name, item); 864167465Smp Strbuf_terminate(exp_name); 86559243Sobrien return (0); 86659243Sobrien } 867131962Smp if (!enhanced && !igncase) { 868167465Smp for (x = exp_name->s, ent = item; *x && (*x & TRIM) == (*ent & TRIM); 869167465Smp x++, ent++) 87059243Sobrien len++; 87159243Sobrien } else { 872167465Smp for (x = exp_name->s, ent = item; *x; x++, ent++) { 87359243Sobrien MCH1 = *x & TRIM; 87459243Sobrien MCH2 = *ent & TRIM; 87559243Sobrien MCH1 = Isupper(MCH1) ? Tolower(MCH1) : MCH1; 87659243Sobrien MCH2 = Isupper(MCH2) ? Tolower(MCH2) : MCH2; 87759243Sobrien if (MCH1 != MCH2) 87859243Sobrien break; 87959243Sobrien len++; 88059243Sobrien } 88159243Sobrien if (*x || !*ent) /* Shorter or exact match */ 882167465Smp memcpy(exp_name->s, item, len * sizeof(*exp_name->s)); 88359243Sobrien } 88459243Sobrien *x = '\0'; /* Shorten at 1st char diff */ 885167465Smp exp_name->len = x - exp_name->s; 88659243Sobrien if (!(match_unique_match || is_set(STRrecexact) || (enhanced && *ent)) && len == name_length) /* Ambiguous to prefix? */ 88759243Sobrien return (-1); /* So stop now and save time */ 88859243Sobrien return (0); 88959243Sobrien} /* end recognize */ 89059243Sobrien 89159243Sobrien 89259243Sobrien/* tw_collect_items(): 89359243Sobrien * Collect items that match target. 89459243Sobrien * SPELL command: 89559243Sobrien * Returns the spelling distance of the closest match. 89659243Sobrien * else 89759243Sobrien * Returns the number of items found. 89859243Sobrien * If none found, but some ignored items were found, 89959243Sobrien * It returns the -number of ignored items. 90059243Sobrien */ 90159243Sobrienstatic int 902167465Smptw_collect_items(COMMAND command, int looking, struct Strbuf *exp_dir, 903167465Smp struct Strbuf *exp_name, Char *target, const Char *pat, 904167465Smp int flags) 90559243Sobrien{ 90659243Sobrien int done = FALSE; /* Search is done */ 90759243Sobrien int showdots; /* Style to show dot files */ 90859243Sobrien int nignored = 0; /* Number of fignored items */ 90959243Sobrien int numitems = 0; /* Number of matched items */ 910167465Smp size_t name_length = Strlen(target); /* Length of prefix (file name) */ 91159243Sobrien int exec_check = flags & TW_EXEC_CHK;/* need to check executability */ 91259243Sobrien int dir_check = flags & TW_DIR_CHK; /* Need to check for directories */ 91359243Sobrien int text_check = flags & TW_TEXT_CHK;/* Need to check for non-directories */ 91459243Sobrien int dir_ok = flags & TW_DIR_OK; /* Ignore directories? */ 91559243Sobrien int gpat = flags & TW_PAT_OK; /* Match against a pattern */ 91659243Sobrien int ignoring = flags & TW_IGN_OK; /* Use fignore? */ 91759243Sobrien int d = 4, nd; /* Spelling distance */ 918167465Smp Char *ptr; 91959243Sobrien struct varent *vp; 920167465Smp struct Strbuf buf = Strbuf_INIT, item = Strbuf_INIT; 921167465Smp int enhanced = 0; 92259243Sobrien int cnt = 0; 92359243Sobrien int igncase = 0; 92459243Sobrien 92559243Sobrien 92659243Sobrien flags = 0; 92759243Sobrien 92859243Sobrien showdots = DOT_NONE; 92959243Sobrien if ((ptr = varval(STRlistflags)) != STRNULL) 93059243Sobrien while (*ptr) 93159243Sobrien switch (*ptr++) { 93259243Sobrien case 'a': 93359243Sobrien showdots = DOT_ALL; 93459243Sobrien break; 93559243Sobrien case 'A': 93659243Sobrien showdots = DOT_NOT; 93759243Sobrien break; 93859243Sobrien default: 93959243Sobrien break; 94059243Sobrien } 94159243Sobrien 942167465Smp cleanup_push(&item, Strbuf_cleanup); 943167465Smp cleanup_push(&buf, Strbuf_cleanup); 944167465Smp while (!done && 945167465Smp (item.len = 0, 946167465Smp tw_next_entry[looking](&item, exp_dir, &flags) != 0)) { 947167465Smp Strbuf_terminate(&item); 94859243Sobrien#ifdef TDEBUG 949167465Smp xprintf("item = %S\n", item.s); 95059243Sobrien#endif 95159243Sobrien switch (looking) { 95259243Sobrien case TW_FILE: 95359243Sobrien case TW_DIRECTORY: 95459243Sobrien case TW_TEXT: 95559243Sobrien /* 95659243Sobrien * Don't match . files on null prefix match 95759243Sobrien */ 958167465Smp if (showdots == DOT_NOT && (ISDOT(item.s) || ISDOTDOT(item.s))) 95959243Sobrien done = TRUE; 960167465Smp if (name_length == 0 && item.s[0] == '.' && showdots == DOT_NONE) 96159243Sobrien done = TRUE; 96259243Sobrien break; 96359243Sobrien 96459243Sobrien case TW_COMMAND: 965131962Smp#if defined(_UWIN) || defined(__CYGWIN__) 966167465Smp /* 967167465Smp * Turn foo.{exe,com,bat,cmd} into foo since UWIN's readdir returns 968167465Smp * the file with the .exe, .com, .bat, .cmd extension 969131962Smp */ 970131962Smp { 971167465Smp static const char *rext[] = { ".exe", ".bat", ".com", ".cmd" }; 972167465Smp size_t exti = Strlen(item.s); 973167465Smp 974167465Smp if (exti > 4) { 975167465Smp char *ext = short2str(&item.s[exti -= 4]); 976167465Smp size_t i; 977167465Smp 978167465Smp for (i = 0; i < sizeof(rext) / sizeof(rext[0]); i++) 979167465Smp if (strcasecmp(ext, rext[i]) == 0) { 980167465Smp item.len = exti; 981167465Smp Strbuf_terminate(&item); 982167465Smp break; 983167465Smp } 984167465Smp } 985131962Smp } 986131962Smp#endif /* _UWIN || __CYGWIN__ */ 98759243Sobrien exec_check = flags & TW_EXEC_CHK; 98859243Sobrien dir_ok = flags & TW_DIR_OK; 98959243Sobrien break; 99059243Sobrien 99159243Sobrien default: 99259243Sobrien break; 99359243Sobrien } 99459243Sobrien 99559243Sobrien if (done) { 99659243Sobrien done = FALSE; 99759243Sobrien continue; 99859243Sobrien } 99959243Sobrien 100059243Sobrien switch (command) { 100159243Sobrien 100259243Sobrien case SPELL: /* correct the spelling of the last bit */ 100359243Sobrien if (name_length == 0) {/* zero-length word can't be misspelled */ 1004167465Smp exp_name->len = 0; /* (not trying is important for ~) */ 1005167465Smp Strbuf_terminate(exp_name); 100659243Sobrien d = 0; 100759243Sobrien done = TRUE; 100859243Sobrien break; 100959243Sobrien } 1010167465Smp if (gpat && !Gmatch(item.s, pat)) 101159243Sobrien break; 101259243Sobrien /* 101359243Sobrien * Swapped the order of the spdist() arguments as suggested 101459243Sobrien * by eeide@asylum.cs.utah.edu (Eric Eide) 101559243Sobrien */ 1016167465Smp nd = spdist(target, item.s); /* test the item against original */ 101759243Sobrien if (nd <= d && nd != 4) { 1018167465Smp if (!(exec_check && !executable(exp_dir->s, item.s, dir_ok))) { 1019167465Smp exp_name->len = 0; 1020167465Smp Strbuf_append(exp_name, item.s); 1021167465Smp Strbuf_terminate(exp_name); 102259243Sobrien d = nd; 102359243Sobrien if (d == 0) /* if found it exactly */ 102459243Sobrien done = TRUE; 102559243Sobrien } 102659243Sobrien } 102759243Sobrien else if (nd == 4) { 1028167465Smp if (spdir(exp_name, exp_dir->s, item.s, target)) { 1029167465Smp if (exec_check && 1030167465Smp !executable(exp_dir->s, exp_name->s, dir_ok)) 103159243Sobrien break; 103259243Sobrien#ifdef notdef 103359243Sobrien /* 103459243Sobrien * We don't want to stop immediately, because 103559243Sobrien * we might find an exact/better match later. 103659243Sobrien */ 103759243Sobrien d = 0; 103859243Sobrien done = TRUE; 103959243Sobrien#endif 104059243Sobrien d = 3; 104159243Sobrien } 104259243Sobrien } 104359243Sobrien break; 104459243Sobrien 104559243Sobrien case LIST: 104659243Sobrien case RECOGNIZE: 104759243Sobrien case RECOGNIZE_ALL: 104859243Sobrien case RECOGNIZE_SCROLL: 104959243Sobrien 1050131962Smp if ((vp = adrof(STRcomplete)) != NULL && vp->vec != NULL) { 1051131962Smp Char **cp; 1052131962Smp for (cp = vp->vec; *cp; cp++) { 1053131962Smp if (Strcmp(*cp, STRigncase) == 0) 1054131962Smp igncase = 1; 1055131962Smp if (Strcmp(*cp, STRenhance) == 0) 1056131962Smp enhanced = 1; 1057131962Smp } 1058131962Smp } 1059131962Smp 106059243Sobrien if (enhanced || igncase) { 1061167465Smp if (!is_prefixmatch(target, item.s, igncase)) 106259243Sobrien break; 106359243Sobrien } else { 1064167465Smp if (!is_prefix(target, item.s)) 106559243Sobrien break; 106659243Sobrien } 106759243Sobrien 1068167465Smp if (exec_check && !executable(exp_dir->s, item.s, dir_ok)) 106959243Sobrien break; 107059243Sobrien 1071167465Smp if (dir_check && !isadirectory(exp_dir->s, item.s)) 107259243Sobrien break; 107359243Sobrien 1074167465Smp if (text_check && isadirectory(exp_dir->s, item.s)) 107559243Sobrien break; 107659243Sobrien 107759243Sobrien /* 107859243Sobrien * Only pattern match directories if we're checking 107959243Sobrien * for directories. 108059243Sobrien */ 1081167465Smp if (gpat && !Gmatch(item.s, pat) && 1082167465Smp (dir_check || !isadirectory(exp_dir->s, item.s))) 108359243Sobrien break; 108459243Sobrien 108559243Sobrien /* 108659243Sobrien * Remove duplicates in command listing and completion 108759243Sobrien * AFEB added code for TW_LOGNAME and TW_USER cases 108859243Sobrien */ 108959243Sobrien if (looking == TW_COMMAND || looking == TW_LOGNAME 109059243Sobrien || looking == TW_USER || command == LIST) { 1091167465Smp buf.len = 0; 1092167465Smp Strbuf_append(&buf, item.s); 109359243Sobrien switch (looking) { 109459243Sobrien case TW_COMMAND: 109559243Sobrien if (!(dir_ok && exec_check)) 109659243Sobrien break; 1097167465Smp if (filetype(exp_dir->s, item.s) == '/') 1098167465Smp Strbuf_append1(&buf, '/'); 109959243Sobrien break; 110059243Sobrien 110159243Sobrien case TW_FILE: 110259243Sobrien case TW_DIRECTORY: 1103167465Smp Strbuf_append1(&buf, filetype(exp_dir->s, item.s)); 110459243Sobrien break; 110559243Sobrien 110659243Sobrien default: 110759243Sobrien break; 110859243Sobrien } 1109167465Smp Strbuf_terminate(&buf); 111059243Sobrien if ((looking == TW_COMMAND || looking == TW_USER 1111167465Smp || looking == TW_LOGNAME) && tw_item_find(buf.s)) 111259243Sobrien break; 111359243Sobrien else { 111459243Sobrien /* maximum length 1 (NULL) + 1 (~ or $) + 1 (filetype) */ 1115167465Smp tw_item_add(&buf); 111659243Sobrien if (command == LIST) 111759243Sobrien numitems++; 111859243Sobrien } 111959243Sobrien } 112059243Sobrien 112159243Sobrien if (command == RECOGNIZE || command == RECOGNIZE_ALL || 112259243Sobrien command == RECOGNIZE_SCROLL) { 1123167465Smp if (ignoring && ignored(item.s)) { 112459243Sobrien nignored++; 112559243Sobrien break; 1126167465Smp } 112759243Sobrien else if (command == RECOGNIZE_SCROLL) { 1128167465Smp add_scroll_tab(item.s); 112959243Sobrien cnt++; 113059243Sobrien } 1131167465Smp 113259243Sobrien if (match_unique_match || is_set(STRrecexact)) { 1133167465Smp if (StrQcmp(target, item.s) == 0) { /* EXACT match */ 1134167465Smp exp_name->len = 0; 1135167465Smp Strbuf_append(exp_name, item.s); 1136167465Smp Strbuf_terminate(exp_name); 113759243Sobrien numitems = 1; /* fake into expanding */ 113859243Sobrien non_unique_match = TRUE; 113959243Sobrien done = TRUE; 114059243Sobrien break; 114159243Sobrien } 114259243Sobrien } 1143167465Smp if (recognize(exp_name, item.s, name_length, ++numitems, 1144167465Smp enhanced, igncase)) 114559243Sobrien if (command != RECOGNIZE_SCROLL) 114659243Sobrien done = TRUE; 1147167465Smp if (enhanced && exp_name->len < name_length) { 1148167465Smp exp_name->len = 0; 1149167465Smp Strbuf_append(exp_name, target); 1150167465Smp Strbuf_terminate(exp_name); 1151167465Smp } 115259243Sobrien } 115359243Sobrien break; 115459243Sobrien 115559243Sobrien default: 115659243Sobrien break; 115759243Sobrien } 115859243Sobrien#ifdef TDEBUG 1159167465Smp xprintf("done item = %S\n", item.s); 116059243Sobrien#endif 116159243Sobrien } 1162167465Smp cleanup_until(&item); 116359243Sobrien 116459243Sobrien if (command == RECOGNIZE_SCROLL) { 116559243Sobrien if ((cnt <= curchoice) || (curchoice == -1)) { 116659243Sobrien curchoice = -1; 116759243Sobrien nignored = 0; 116859243Sobrien numitems = 0; 116959243Sobrien } else if (numitems > 1) { 117059243Sobrien if (curchoice < -1) 117159243Sobrien curchoice = cnt - 1; 1172167465Smp choose_scroll_tab(exp_name, cnt); 117359243Sobrien numitems = 1; 117459243Sobrien } 117559243Sobrien } 117659243Sobrien free_scroll_tab(); 117759243Sobrien 117859243Sobrien if (command == SPELL) 117959243Sobrien return d; 118059243Sobrien else { 118159243Sobrien if (ignoring && numitems == 0 && nignored > 0) 118259243Sobrien return -nignored; 118359243Sobrien else 118459243Sobrien return numitems; 118559243Sobrien } 118659243Sobrien} 118759243Sobrien 118859243Sobrien 118959243Sobrien/* tw_suffix(): 119059243Sobrien * Find and return the appropriate suffix character 119159243Sobrien */ 119259243Sobrien/*ARGSUSED*/ 1193167465Smpstatic Char 1194167465Smptw_suffix(int looking, const Char *exp_dir, Char *exp_name) 1195167465Smp{ 119659243Sobrien Char *ptr; 119759243Sobrien struct varent *vp; 119859243Sobrien 119959243Sobrien (void) strip(exp_name); 120059243Sobrien 120159243Sobrien switch (looking) { 120259243Sobrien 120359243Sobrien case TW_LOGNAME: 120459243Sobrien return '/'; 120559243Sobrien 120659243Sobrien case TW_VARIABLE: 120759243Sobrien /* 120859243Sobrien * Don't consider array variables or empty variables 120959243Sobrien */ 1210100616Smp if ((vp = adrof(exp_name)) != NULL && vp->vec != NULL) { 121159243Sobrien if ((ptr = vp->vec[0]) == NULL || *ptr == '\0' || 121259243Sobrien vp->vec[1] != NULL) 121359243Sobrien return ' '; 121459243Sobrien } 121559243Sobrien else if ((ptr = tgetenv(exp_name)) == NULL || *ptr == '\0') 121659243Sobrien return ' '; 121759243Sobrien 121859243Sobrien return isadirectory(exp_dir, ptr) ? '/' : ' '; 121959243Sobrien 122059243Sobrien 122159243Sobrien case TW_DIRECTORY: 122259243Sobrien return '/'; 122359243Sobrien 122459243Sobrien case TW_COMMAND: 122559243Sobrien case TW_FILE: 122659243Sobrien return isadirectory(exp_dir, exp_name) ? '/' : ' '; 122759243Sobrien 122859243Sobrien case TW_ALIAS: 122959243Sobrien case TW_VARLIST: 123059243Sobrien case TW_WORDLIST: 123159243Sobrien case TW_SHELLVAR: 123259243Sobrien case TW_ENVVAR: 123359243Sobrien case TW_USER: 123459243Sobrien case TW_BINDING: 123559243Sobrien case TW_LIMIT: 123659243Sobrien case TW_SIGNAL: 123759243Sobrien case TW_JOB: 123859243Sobrien case TW_COMPLETION: 123959243Sobrien case TW_TEXT: 124059243Sobrien case TW_GRPNAME: 124159243Sobrien return ' '; 124259243Sobrien 124359243Sobrien default: 124459243Sobrien return '\0'; 124559243Sobrien } 124659243Sobrien} /* end tw_suffix */ 124759243Sobrien 124859243Sobrien 124959243Sobrien/* tw_fixword(): 125059243Sobrien * Repair a word after a spalling or a recognizwe 125159243Sobrien */ 125259243Sobrienstatic void 1253167465Smptw_fixword(int looking, struct Strbuf *word, Char *dir, Char *exp_name) 125459243Sobrien{ 125559243Sobrien Char *ptr; 125659243Sobrien 125759243Sobrien switch (looking) { 125859243Sobrien case TW_LOGNAME: 1259167465Smp word->len = 0; 1260167465Smp Strbuf_append1(word, '~'); 126159243Sobrien break; 1262167465Smp 126359243Sobrien case TW_VARIABLE: 1264167465Smp if ((ptr = Strrchr(word->s, '$')) != NULL) { 1265167465Smp word->len = ptr + 1 - word->s; /* Delete after the dollar */ 1266167465Smp } else 1267167465Smp word->len = 0; 126859243Sobrien break; 126959243Sobrien 127059243Sobrien case TW_DIRECTORY: 127159243Sobrien case TW_FILE: 127259243Sobrien case TW_TEXT: 1273167465Smp word->len = 0; 1274167465Smp Strbuf_append(word, dir); /* put back dir part */ 127559243Sobrien break; 127659243Sobrien 127759243Sobrien default: 1278167465Smp word->len = 0; 127959243Sobrien break; 128059243Sobrien } 128159243Sobrien 128259243Sobrien (void) quote(exp_name); 1283167465Smp Strbuf_append(word, exp_name); /* add extended name */ 1284167465Smp Strbuf_terminate(word); 128559243Sobrien} /* end tw_fixword */ 128659243Sobrien 128759243Sobrien 128859243Sobrien/* tw_collect(): 128959243Sobrien * Collect items. Return -1 in case we were interrupted or 129059243Sobrien * the return value of tw_collect 129159243Sobrien * This is really a wrapper for tw_collect_items, serving two 129259243Sobrien * purposes: 129359243Sobrien * 1. Handles interrupt cleanups. 129459243Sobrien * 2. Retries if we had no matches, but there were ignored matches 129559243Sobrien */ 129659243Sobrienstatic int 1297167465Smptw_collect(COMMAND command, int looking, struct Strbuf *exp_dir, 1298167465Smp struct Strbuf *exp_name, Char *target, Char *pat, int flags, 1299167465Smp DIR *dir_fd) 130059243Sobrien{ 1301167465Smp volatile int ni; 130259243Sobrien jmp_buf_t osetexit; 130359243Sobrien 130459243Sobrien#ifdef TDEBUG 1305167465Smp xprintf("target = %S\n", target); 130659243Sobrien#endif 130759243Sobrien ni = 0; 130859243Sobrien getexit(osetexit); 130959243Sobrien for (;;) { 1310167465Smp volatile size_t omark; 1311167465Smp 131259243Sobrien (*tw_start_entry[looking])(dir_fd, pat); 131359243Sobrien InsideCompletion = 1; 131459243Sobrien if (setexit()) { 1315167465Smp cleanup_pop_mark(omark); 1316167465Smp resexit(osetexit); 131759243Sobrien /* interrupted, clean up */ 131859243Sobrien haderr = 0; 1319167465Smp ni = -1; /* flag error */ 1320167465Smp break; 132159243Sobrien } 1322167465Smp omark = cleanup_push_mark(); 1323167465Smp ni = tw_collect_items(command, looking, exp_dir, exp_name, target, pat, 1324167465Smp ni >= 0 ? flags : flags & ~TW_IGN_OK); 1325167465Smp cleanup_pop_mark(omark); 1326167465Smp resexit(osetexit); 1327167465Smp if (ni >= 0) 1328167465Smp break; 1329167465Smp } 1330167465Smp InsideCompletion = 0; 133159243Sobrien#if defined(SOLARIS2) && defined(i386) && !defined(__GNUC__) 1332167465Smp /* Compiler bug? (from PWP) */ 1333167465Smp if ((looking == TW_LOGNAME) || (looking == TW_USER)) 1334167465Smp tw_logname_end(); 1335167465Smp else if (looking == TW_GRPNAME) 1336167465Smp tw_grpname_end(); 1337167465Smp else 1338167465Smp tw_dir_end(); 133959243Sobrien#else /* !(SOLARIS2 && i386 && !__GNUC__) */ 1340167465Smp (*tw_end_entry[looking])(); 134159243Sobrien#endif /* !(SOLARIS2 && i386 && !__GNUC__) */ 1342167465Smp return(ni); 134359243Sobrien} /* end tw_collect */ 134459243Sobrien 134559243Sobrien 134659243Sobrien/* tw_list_items(): 134759243Sobrien * List the items that were found 134859243Sobrien * 134959243Sobrien * NOTE instead of looking at numerical vars listmax and listmaxrows 135059243Sobrien * we can look at numerical var listmax, and have a string value 135159243Sobrien * listmaxtype (or similar) than can have values 'items' and 'rows' 135259243Sobrien * (by default interpreted as 'items', for backwards compatibility) 135359243Sobrien */ 135459243Sobrienstatic void 1355167465Smptw_list_items(int looking, int numitems, int list_max) 135659243Sobrien{ 135759243Sobrien Char *ptr; 135859243Sobrien int max_items = 0; 135959243Sobrien int max_rows = 0; 136059243Sobrien 136183098Smp if (numitems == 0) 136283098Smp return; 136383098Smp 136459243Sobrien if ((ptr = varval(STRlistmax)) != STRNULL) { 136559243Sobrien while (*ptr) { 136659243Sobrien if (!Isdigit(*ptr)) { 136759243Sobrien max_items = 0; 136859243Sobrien break; 136959243Sobrien } 137059243Sobrien max_items = max_items * 10 + *ptr++ - '0'; 137159243Sobrien } 137259243Sobrien if ((max_items > 0) && (numitems > max_items) && list_max) 137359243Sobrien max_items = numitems; 137459243Sobrien else 137559243Sobrien max_items = 0; 137659243Sobrien } 137759243Sobrien 137859243Sobrien if (max_items == 0 && (ptr = varval(STRlistmaxrows)) != STRNULL) { 137959243Sobrien int rows; 138059243Sobrien 138159243Sobrien while (*ptr) { 138259243Sobrien if (!Isdigit(*ptr)) { 138359243Sobrien max_rows = 0; 138459243Sobrien break; 138559243Sobrien } 138659243Sobrien max_rows = max_rows * 10 + *ptr++ - '0'; 138759243Sobrien } 138859243Sobrien if (max_rows != 0 && looking != TW_JOB) 138959243Sobrien rows = find_rows(tw_item_get(), numitems, TRUE); 139059243Sobrien else 139159243Sobrien rows = numitems; /* underestimate for lines wider than the termH */ 139259243Sobrien if ((max_rows > 0) && (rows > max_rows) && list_max) 139359243Sobrien max_rows = rows; 139459243Sobrien else 139559243Sobrien max_rows = 0; 139659243Sobrien } 139759243Sobrien 139859243Sobrien 139959243Sobrien if (max_items || max_rows) { 1400145479Smp char tc, *sname; 140159243Sobrien const char *name; 140259243Sobrien int maxs; 140359243Sobrien 140459243Sobrien if (max_items) { 140559243Sobrien name = CGETS(30, 5, "items"); 140659243Sobrien maxs = max_items; 140759243Sobrien } 140859243Sobrien else { 140959243Sobrien name = CGETS(30, 6, "rows"); 141059243Sobrien maxs = max_rows; 141159243Sobrien } 141259243Sobrien 1413145479Smp sname = strsave(name); 1414167465Smp cleanup_push(sname, xfree); 141559243Sobrien xprintf(CGETS(30, 7, "There are %d %s, list them anyway? [n/y] "), 1416145479Smp maxs, sname); 1417167465Smp cleanup_until(sname); 141859243Sobrien flush(); 141959243Sobrien /* We should be in Rawmode here, so no \n to catch */ 1420167465Smp (void) xread(SHIN, &tc, 1); 142159243Sobrien xprintf("%c\r\n", tc); /* echo the char, do a newline */ 1422167465Smp /* 142359243Sobrien * Perhaps we should use the yesexpr from the 142459243Sobrien * actual locale 142559243Sobrien */ 142659243Sobrien if (strchr(CGETS(30, 13, "Yy"), tc) == NULL) 142759243Sobrien return; 142859243Sobrien } 142959243Sobrien 143059243Sobrien if (looking != TW_SIGNAL) 1431167465Smp qsort(tw_item_get(), numitems, sizeof(Char *), fcompare); 143259243Sobrien if (looking != TW_JOB) 143359243Sobrien print_by_column(STRNULL, tw_item_get(), numitems, TRUE); 143459243Sobrien else { 143559243Sobrien /* 143659243Sobrien * print one item on every line because jobs can have spaces 143759243Sobrien * and it is confusing. 143859243Sobrien */ 143959243Sobrien int i; 144059243Sobrien Char **w = tw_item_get(); 144159243Sobrien 144259243Sobrien for (i = 0; i < numitems; i++) { 144359243Sobrien xprintf("%S", w[i]); 144459243Sobrien if (Tty_raw_mode) 144559243Sobrien xputchar('\r'); 144659243Sobrien xputchar('\n'); 144759243Sobrien } 144859243Sobrien } 144959243Sobrien} /* end tw_list_items */ 145059243Sobrien 145159243Sobrien 145259243Sobrien/* t_search(): 145359243Sobrien * Perform a RECOGNIZE, LIST or SPELL command on string "word". 145459243Sobrien * 145559243Sobrien * Return value: 145659243Sobrien * >= 0: SPELL command: "distance" (see spdist()) 145759243Sobrien * other: No. of items found 145859243Sobrien * < 0: Error (message or beep is output) 145959243Sobrien */ 146059243Sobrien/*ARGSUSED*/ 146159243Sobrienint 1462167465Smpt_search(struct Strbuf *word, COMMAND command, int looking, int list_max, 1463167465Smp Char *pat, eChar suf) 146459243Sobrien{ 146559243Sobrien int numitems, /* Number of items matched */ 146659243Sobrien flags = 0, /* search flags */ 146759243Sobrien gpat = pat[0] != '\0', /* Glob pattern search */ 1468167465Smp res; /* Return value */ 1469167465Smp struct Strbuf exp_dir = Strbuf_INIT;/* dir after ~ expansion */ 1470167465Smp struct Strbuf dir = Strbuf_INIT; /* /x/y/z/ part in /x/y/z/f */ 1471167465Smp struct Strbuf exp_name = Strbuf_INIT;/* the recognized (extended) */ 1472167465Smp Char *name, /* f part in /d/d/d/f name */ 147359243Sobrien *target; /* Target to expand/correct/list */ 1474167465Smp DIR *dir_fd = NULL; 147559243Sobrien 147659243Sobrien /* 147759243Sobrien * bugfix by Marty Grossman (grossman@CC5.BBN.COM): directory listing can 147859243Sobrien * dump core when interrupted 147959243Sobrien */ 148059243Sobrien tw_item_free(); 148159243Sobrien 148259243Sobrien non_unique_match = FALSE; /* See the recexact code below */ 148359243Sobrien 1484167465Smp extract_dir_and_name(word->s, &dir, &name); 1485167465Smp cleanup_push(&dir, Strbuf_cleanup); 1486167465Smp cleanup_push(&name, xfree_indirect); 148759243Sobrien 148859243Sobrien /* 148959243Sobrien * SPECIAL HARDCODED COMPLETIONS: 149059243Sobrien * foo$variable -> TW_VARIABLE 149159243Sobrien * ~user -> TW_LOGNAME 149259243Sobrien * 149359243Sobrien */ 1494167465Smp if ((*word->s == '~') && (Strchr(word->s, '/') == NULL)) { 149559243Sobrien looking = TW_LOGNAME; 149659243Sobrien target = name; 149759243Sobrien gpat = 0; /* Override pattern mechanism */ 149859243Sobrien } 149959243Sobrien else if ((target = Strrchr(name, '$')) != 0 && 150059243Sobrien (Strchr(name, '/') == NULL)) { 150159243Sobrien target++; 150259243Sobrien looking = TW_VARIABLE; 150359243Sobrien gpat = 0; /* Override pattern mechanism */ 150459243Sobrien } 150559243Sobrien else 150659243Sobrien target = name; 150759243Sobrien 150859243Sobrien /* 150959243Sobrien * Try to figure out what we should be looking for 151059243Sobrien */ 151159243Sobrien if (looking & TW_PATH) { 151259243Sobrien gpat = 0; /* pattern holds the pathname to be used */ 1513167465Smp Strbuf_append(&exp_dir, pat); 1514167465Smp if (exp_dir.len != 0 && exp_dir.s[exp_dir.len - 1] != '/') 1515167465Smp Strbuf_append1(&exp_dir, '/'); 1516167465Smp Strbuf_append(&exp_dir, dir.s); 151759243Sobrien } 1518167465Smp Strbuf_terminate(&exp_dir); 1519167465Smp cleanup_push(&exp_dir, Strbuf_cleanup); 152059243Sobrien 152159243Sobrien switch (looking & ~TW_PATH) { 152259243Sobrien case TW_NONE: 1523167465Smp res = -1; 1524167465Smp goto err_dir; 152559243Sobrien 152659243Sobrien case TW_ZERO: 152759243Sobrien looking = TW_FILE; 152859243Sobrien break; 152959243Sobrien 153059243Sobrien case TW_COMMAND: 1531167465Smp if (Strchr(word->s, '/') || (looking & TW_PATH)) { 153259243Sobrien looking = TW_FILE; 153359243Sobrien flags |= TW_EXEC_CHK; 153459243Sobrien flags |= TW_DIR_OK; 153559243Sobrien } 153659243Sobrien#ifdef notdef 153759243Sobrien /* PWP: don't even bother when doing ALL of the commands */ 1538167465Smp if (looking == TW_COMMAND && word->len == 0) { 1539167465Smp res = -1; 1540167465Smp goto err_dir; 1541167465Smp } 154259243Sobrien#endif 154359243Sobrien break; 154459243Sobrien 154559243Sobrien 154659243Sobrien case TW_VARLIST: 154759243Sobrien case TW_WORDLIST: 154859243Sobrien gpat = 0; /* pattern holds the name of the variable */ 154959243Sobrien break; 155059243Sobrien 155159243Sobrien case TW_EXPLAIN: 155259243Sobrien if (command == LIST && pat != NULL) { 155359243Sobrien xprintf("%S", pat); 155459243Sobrien if (Tty_raw_mode) 155559243Sobrien xputchar('\r'); 155659243Sobrien xputchar('\n'); 155759243Sobrien } 1558167465Smp res = 2; 1559167465Smp goto err_dir; 156059243Sobrien 156159243Sobrien default: 156259243Sobrien break; 156359243Sobrien } 156459243Sobrien 156559243Sobrien /* 156659243Sobrien * let fignore work only when we are not using a pattern 156759243Sobrien */ 156859243Sobrien flags |= (gpat == 0) ? TW_IGN_OK : TW_PAT_OK; 156959243Sobrien 157059243Sobrien#ifdef TDEBUG 157159243Sobrien xprintf(CGETS(30, 8, "looking = %d\n"), looking); 157259243Sobrien#endif 157359243Sobrien 157459243Sobrien switch (looking) { 1575167465Smp Char *user_name; 1576167465Smp 157759243Sobrien case TW_ALIAS: 157859243Sobrien case TW_SHELLVAR: 157959243Sobrien case TW_ENVVAR: 158059243Sobrien case TW_BINDING: 158159243Sobrien case TW_LIMIT: 158259243Sobrien case TW_SIGNAL: 158359243Sobrien case TW_JOB: 158459243Sobrien case TW_COMPLETION: 158559243Sobrien case TW_GRPNAME: 158659243Sobrien break; 158759243Sobrien 158859243Sobrien 158959243Sobrien case TW_VARIABLE: 1590167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1591167465Smp goto err_dir; 159259243Sobrien break; 159359243Sobrien 159459243Sobrien case TW_DIRECTORY: 159559243Sobrien flags |= TW_DIR_CHK; 159659243Sobrien 159759243Sobrien#ifdef notyet 159859243Sobrien /* 159959243Sobrien * This is supposed to expand the directory stack. 160059243Sobrien * Problems: 160159243Sobrien * 1. Slow 160259243Sobrien * 2. directories with the same name 160359243Sobrien */ 160459243Sobrien flags |= TW_DIR_OK; 160559243Sobrien#endif 160659243Sobrien#ifdef notyet 160759243Sobrien /* 160859243Sobrien * Supposed to do delayed expansion, but it is inconsistent 160959243Sobrien * from a user-interface point of view, since it does not 161059243Sobrien * immediately obey addsuffix 161159243Sobrien */ 1612167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1613167465Smp goto err_dir; 1614167465Smp if (isadirectory(exp_dir.s, name)) { 1615167465Smp if (exp_dir.len != 0 || name[0] != '\0') { 1616167465Smp Strbuf_append(&dir, name); 1617167465Smp if (dir.s[dir.len - 1] != '/') 1618167465Smp Strbuf_append1(&dir, '/'); 1619167465Smp Strbuf_terminate(&dir); 1620167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1621167465Smp goto err_dir; 1622167465Smp if (word->len != 0 && word->s[word->len - 1] != '/') { 1623167465Smp Strbuf_append1(word, '/'); 1624167465Smp Strbuf_terminate(word); 1625167465Smp } 162659243Sobrien name[0] = '\0'; 162759243Sobrien } 162859243Sobrien } 162959243Sobrien#endif 1630167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1631167465Smp goto err_dir; 163259243Sobrien break; 163359243Sobrien 163459243Sobrien case TW_TEXT: 163559243Sobrien flags |= TW_TEXT_CHK; 163659243Sobrien /*FALLTHROUGH*/ 163759243Sobrien case TW_FILE: 1638167465Smp if ((res = expand_dir(dir.s, &exp_dir, &dir_fd, command)) != 0) 1639167465Smp goto err_dir; 164059243Sobrien break; 164159243Sobrien 164259243Sobrien case TW_PATH | TW_TEXT: 164359243Sobrien case TW_PATH | TW_FILE: 164459243Sobrien case TW_PATH | TW_DIRECTORY: 164559243Sobrien case TW_PATH | TW_COMMAND: 1646167465Smp if ((dir_fd = opendir(short2str(exp_dir.s))) == NULL) { 164783098Smp if (command == RECOGNIZE) 164883098Smp xprintf("\n"); 1649167465Smp xprintf("%S: %s", exp_dir.s, strerror(errno)); 165083098Smp if (command != RECOGNIZE) 165183098Smp xprintf("\n"); 165283098Smp NeedsRedraw = 1; 1653167465Smp res = -1; 1654167465Smp goto err_dir; 165559243Sobrien } 1656167465Smp if (exp_dir.len != 0 && exp_dir.s[exp_dir.len - 1] != '/') { 1657167465Smp Strbuf_append1(&exp_dir, '/'); 1658167465Smp Strbuf_terminate(&exp_dir); 1659167465Smp } 166059243Sobrien 166159243Sobrien looking &= ~TW_PATH; 166259243Sobrien 166359243Sobrien switch (looking) { 166459243Sobrien case TW_TEXT: 166559243Sobrien flags |= TW_TEXT_CHK; 166659243Sobrien break; 166759243Sobrien 166859243Sobrien case TW_FILE: 166959243Sobrien break; 167059243Sobrien 167159243Sobrien case TW_DIRECTORY: 167259243Sobrien flags |= TW_DIR_CHK; 167359243Sobrien break; 167459243Sobrien 167559243Sobrien case TW_COMMAND: 1676167465Smp xfree(name); 1677167465Smp target = name = Strsave(word->s); /* so it can match things */ 167859243Sobrien break; 167959243Sobrien 168059243Sobrien default: 168159243Sobrien abort(); /* Cannot happen */ 168259243Sobrien break; 168359243Sobrien } 168459243Sobrien break; 168559243Sobrien 168659243Sobrien case TW_LOGNAME: 1687167465Smp user_name = word->s + 1; 1688167465Smp goto do_user; 1689167465Smp 169059243Sobrien /*FALLTHROUGH*/ 169159243Sobrien case TW_USER: 1692167465Smp user_name = word->s; 1693167465Smp do_user: 169459243Sobrien /* 169559243Sobrien * Check if the spelling was already correct 169659243Sobrien * From: Rob McMahon <cudcv@cu.warwick.ac.uk> 169759243Sobrien */ 1698167465Smp if (command == SPELL && xgetpwnam(short2str(user_name)) != NULL) { 169959243Sobrien#ifdef YPBUGS 170059243Sobrien fix_yp_bugs(); 170159243Sobrien#endif /* YPBUGS */ 1702167465Smp res = 0; 1703167465Smp goto err_dir; 170459243Sobrien } 1705167465Smp xfree(name); 1706167465Smp target = name = Strsave(user_name); 170759243Sobrien break; 170859243Sobrien 170959243Sobrien case TW_COMMAND: 171059243Sobrien case TW_VARLIST: 171159243Sobrien case TW_WORDLIST: 1712167465Smp target = name = Strsave(word->s); /* so it can match things */ 171359243Sobrien break; 171459243Sobrien 171559243Sobrien default: 171659243Sobrien xprintf(CGETS(30, 9, 171759243Sobrien "\n%s internal error: I don't know what I'm looking for!\n"), 171859243Sobrien progname); 171959243Sobrien NeedsRedraw = 1; 1720167465Smp res = -1; 1721167465Smp goto err_dir; 172259243Sobrien } 172359243Sobrien 1724167465Smp cleanup_push(&exp_name, Strbuf_cleanup); 1725167465Smp numitems = tw_collect(command, looking, &exp_dir, &exp_name, target, pat, 1726167465Smp flags, dir_fd); 172759243Sobrien if (numitems == -1) 1728167465Smp goto end; 172959243Sobrien 173059243Sobrien switch (command) { 173159243Sobrien case RECOGNIZE: 173259243Sobrien case RECOGNIZE_ALL: 173359243Sobrien case RECOGNIZE_SCROLL: 173459243Sobrien if (numitems <= 0) 1735167465Smp break; 173659243Sobrien 1737167465Smp Strbuf_terminate(&exp_name); 1738167465Smp tw_fixword(looking, word, dir.s, exp_name.s); 173959243Sobrien 174059243Sobrien if (!match_unique_match && is_set(STRaddsuffix) && numitems == 1) { 174159243Sobrien switch (suf) { 174259243Sobrien case 0: /* Automatic suffix */ 1743167465Smp Strbuf_append1(word, 1744167465Smp tw_suffix(looking, exp_dir.s, exp_name.s)); 174559243Sobrien break; 174659243Sobrien 1747167465Smp case CHAR_ERR: /* No suffix */ 1748167465Smp break; 174959243Sobrien 175059243Sobrien default: /* completion specified suffix */ 1751167465Smp Strbuf_append1(word, suf); 175259243Sobrien break; 175359243Sobrien } 1754167465Smp Strbuf_terminate(word); 175559243Sobrien } 1756167465Smp break; 175759243Sobrien 175859243Sobrien case LIST: 175959243Sobrien tw_list_items(looking, numitems, list_max); 176059243Sobrien tw_item_free(); 1761167465Smp break; 176259243Sobrien 176359243Sobrien case SPELL: 1764167465Smp Strbuf_terminate(&exp_name); 1765167465Smp tw_fixword(looking, word, dir.s, exp_name.s); 1766167465Smp break; 176759243Sobrien 176859243Sobrien default: 176959243Sobrien xprintf("Bad tw_command\n"); 1770167465Smp numitems = 0; 177159243Sobrien } 1772167465Smp end: 1773167465Smp res = numitems; 1774167465Smp err_dir: 1775167465Smp cleanup_until(&dir); 1776167465Smp return res; 177759243Sobrien} /* end t_search */ 177859243Sobrien 177959243Sobrien 178059243Sobrien/* extract_dir_and_name(): 178159243Sobrien * parse full path in file into 2 parts: directory and file names 178259243Sobrien * Should leave final slash (/) at end of dir. 178359243Sobrien */ 178459243Sobrienstatic void 1785167465Smpextract_dir_and_name(const Char *path, struct Strbuf *dir, Char **name) 178659243Sobrien{ 1787145479Smp Char *p; 178859243Sobrien 178959243Sobrien p = Strrchr(path, '/'); 179069408Sache#ifdef WINNT_NATIVE 179159243Sobrien if (p == NULL) 179259243Sobrien p = Strrchr(path, ':'); 179369408Sache#endif /* WINNT_NATIVE */ 1794167465Smp if (p == NULL) 1795167465Smp *name = Strsave(path); 179659243Sobrien else { 179759243Sobrien p++; 1798167465Smp *name = Strsave(p); 1799167465Smp Strbuf_appendn(dir, path, p - path); 180059243Sobrien } 1801167465Smp Strbuf_terminate(dir); 180259243Sobrien} /* end extract_dir_and_name */ 180359243Sobrien 180459243Sobrien 180559243Sobrien/* dollar(): 180659243Sobrien * expand "/$old1/$old2/old3/" 180759243Sobrien * to "/value_of_old1/value_of_old2/old3/" 180859243Sobrien */ 180959243SobrienChar * 1810167465Smpdollar(const Char *old) 181159243Sobrien{ 1812167465Smp struct Strbuf buf = Strbuf_INIT; 181359243Sobrien 1814167465Smp while (*old) { 1815167465Smp if (*old != '$') 1816167465Smp Strbuf_append1(&buf, *old++); 181759243Sobrien else { 1818167465Smp if (expdollar(&buf, &old, QUOTE) == 0) { 1819167465Smp xfree(buf.s); 182059243Sobrien return NULL; 1821167465Smp } 182259243Sobrien } 1823167465Smp } 1824167465Smp return Strbuf_finish(&buf); 182559243Sobrien} /* end dollar */ 182659243Sobrien 182759243Sobrien 182859243Sobrien/* tilde(): 182959243Sobrien * expand ~person/foo to home_directory_of_person/foo 183059243Sobrien * or =<stack-entry> to <dir in stack entry> 183159243Sobrien */ 1832167465Smpstatic int 1833167465Smptilde(struct Strbuf *new, Char *old) 183459243Sobrien{ 1835145479Smp Char *o, *p; 183659243Sobrien 1837167465Smp new->len = 0; 183859243Sobrien switch (old[0]) { 1839167465Smp case '~': { 1840167465Smp Char *name, *home; 1841167465Smp 1842167465Smp old++; 1843167465Smp for (o = old; *o && *o != '/'; o++) 184459243Sobrien continue; 1845167465Smp name = Strnsave(old, o - old); 1846167465Smp home = gethdir(name); 1847167465Smp xfree(name); 1848167465Smp if (home == NULL) 1849167465Smp goto err; 1850167465Smp Strbuf_append(new, home); 1851167465Smp xfree(home); 1852167465Smp /* If the home directory expands to "/", we do 185383098Smp * not want to create "//" by appending a slash from o. 185483098Smp */ 1855167465Smp if (new->s[0] == '/' && new->len == 1 && *o == '/') 185683098Smp ++o; 1857167465Smp Strbuf_append(new, o); 1858167465Smp break; 1859167465Smp } 186059243Sobrien 186159243Sobrien case '=': 1862167465Smp if ((p = globequal(old)) == NULL) 1863167465Smp goto err; 1864167465Smp if (p != old) { 1865167465Smp Strbuf_append(new, p); 1866167465Smp xfree(p); 1867167465Smp break; 186859243Sobrien } 186959243Sobrien /*FALLTHROUGH*/ 187059243Sobrien 187159243Sobrien default: 1872167465Smp Strbuf_append(new, old); 1873167465Smp break; 187459243Sobrien } 1875167465Smp Strbuf_terminate(new); 1876167465Smp return 0; 1877167465Smp 1878167465Smp err: 1879167465Smp Strbuf_terminate(new); 1880167465Smp return -1; 188159243Sobrien} /* end tilde */ 188259243Sobrien 188359243Sobrien 188459243Sobrien/* expand_dir(): 188559243Sobrien * Open the directory given, expanding ~user and $var 188659243Sobrien * Optionally normalize the path given 188759243Sobrien */ 188859243Sobrienstatic int 1889167465Smpexpand_dir(const Char *dir, struct Strbuf *edir, DIR **dfd, COMMAND cmd) 189059243Sobrien{ 189159243Sobrien Char *nd = NULL; 1892167465Smp Char *tdir; 189359243Sobrien 1894167465Smp tdir = dollar(dir); 1895167465Smp cleanup_push(tdir, xfree); 1896167465Smp if (tdir == NULL || 1897167465Smp (tilde(edir, tdir) != 0) || 1898167465Smp !(nd = dnormalize(edir->len ? edir->s : STRdot, 1899167465Smp symlinks == SYM_IGNORE || symlinks == SYM_EXPAND)) || 190059243Sobrien ((*dfd = opendir(short2str(nd))) == NULL)) { 1901167465Smp xfree(nd); 1902167465Smp if (cmd == SPELL || SearchNoDirErr) { 1903167465Smp cleanup_until(tdir); 190459243Sobrien return (-2); 1905167465Smp } 190659243Sobrien /* 190759243Sobrien * From: Amos Shapira <amoss@cs.huji.ac.il> 190859243Sobrien * Print a better message when completion fails 190959243Sobrien */ 1910167465Smp xprintf("\n%S %s\n", edir->len ? edir->s : (tdir ? tdir : dir), 191159243Sobrien (errno == ENOTDIR ? CGETS(30, 10, "not a directory") : 191259243Sobrien (errno == ENOENT ? CGETS(30, 11, "not found") : 191359243Sobrien CGETS(30, 12, "unreadable")))); 191459243Sobrien NeedsRedraw = 1; 1915167465Smp cleanup_until(tdir); 191659243Sobrien return (-1); 191759243Sobrien } 1918167465Smp cleanup_until(tdir); 191959243Sobrien if (nd) { 192059243Sobrien if (*dir != '\0') { 1921167465Smp int slash; 192259243Sobrien 192359243Sobrien /* 192459243Sobrien * Copy and append a / if there was one 192559243Sobrien */ 1926167465Smp slash = edir->len != 0 && edir->s[edir->len - 1] == '/'; 1927167465Smp edir->len = 0; 1928167465Smp Strbuf_append(edir, nd); 1929167465Smp if (slash != 0 && edir->s[edir->len - 1] != '/') 1930167465Smp Strbuf_append1(edir, '/'); 1931167465Smp Strbuf_terminate(edir); 193259243Sobrien } 1933167465Smp xfree(nd); 193459243Sobrien } 193559243Sobrien return 0; 193659243Sobrien} /* end expand_dir */ 193759243Sobrien 193859243Sobrien 193959243Sobrien/* nostat(): 194059243Sobrien * Returns true if the directory should not be stat'd, 194159243Sobrien * false otherwise. 194259243Sobrien * This way, things won't grind to a halt when you complete in /afs 194359243Sobrien * or very large directories. 194459243Sobrien */ 1945145479Smpstatic int 1946167465Smpnostat(Char *dir) 194759243Sobrien{ 194859243Sobrien struct varent *vp; 1949145479Smp Char **cp; 195059243Sobrien 195159243Sobrien if ((vp = adrof(STRnostat)) == NULL || (cp = vp->vec) == NULL) 195259243Sobrien return FALSE; 195359243Sobrien for (; *cp != NULL; cp++) { 195459243Sobrien if (Strcmp(*cp, STRstar) == 0) 195559243Sobrien return TRUE; 195659243Sobrien if (Gmatch(dir, *cp)) 195759243Sobrien return TRUE; 195859243Sobrien } 195959243Sobrien return FALSE; 196059243Sobrien} /* end nostat */ 196159243Sobrien 196259243Sobrien 196359243Sobrien/* filetype(): 196459243Sobrien * Return a character that signifies a filetype 196559243Sobrien * symbology from 4.3 ls command. 196659243Sobrien */ 196759243Sobrienstatic Char 1968167465Smpfiletype(Char *dir, Char *file) 196959243Sobrien{ 197059243Sobrien if (dir) { 1971167465Smp Char *path; 197259243Sobrien char *ptr; 197359243Sobrien struct stat statb; 197459243Sobrien 197559243Sobrien if (nostat(dir)) return(' '); 197659243Sobrien 1977167465Smp path = Strspl(dir, file); 1978167465Smp ptr = short2str(path); 1979167465Smp xfree(path); 198059243Sobrien 1981167465Smp if (lstat(ptr, &statb) != -1) { 198259243Sobrien#ifdef S_ISLNK 198359243Sobrien if (S_ISLNK(statb.st_mode)) { /* Symbolic link */ 198459243Sobrien if (adrof(STRlistlinks)) { 198559243Sobrien if (stat(ptr, &statb) == -1) 198659243Sobrien return ('&'); 198759243Sobrien else if (S_ISDIR(statb.st_mode)) 198859243Sobrien return ('>'); 198959243Sobrien else 199059243Sobrien return ('@'); 199159243Sobrien } 199259243Sobrien else 199359243Sobrien return ('@'); 199459243Sobrien } 199559243Sobrien#endif 199659243Sobrien#ifdef S_ISSOCK 199759243Sobrien if (S_ISSOCK(statb.st_mode)) /* Socket */ 199859243Sobrien return ('='); 199959243Sobrien#endif 200059243Sobrien#ifdef S_ISFIFO 200159243Sobrien if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ 200259243Sobrien return ('|'); 200359243Sobrien#endif 200459243Sobrien#ifdef S_ISHIDDEN 200559243Sobrien if (S_ISHIDDEN(statb.st_mode)) /* Hidden Directory [aix] */ 200659243Sobrien return ('+'); 200759243Sobrien#endif 2008167465Smp#ifdef S_ISCDF 2009167465Smp { 2010167465Smp struct stat hpstatb; 2011167465Smp char *p2; 2012167465Smp 2013167465Smp p2 = strspl(ptr, "+"); /* Must append a '+' and re-stat(). */ 2014167465Smp if ((stat(p2, &hpstatb) != -1) && S_ISCDF(hpstatb.st_mode)) { 2015167465Smp xfree(p2); 2016167465Smp return ('+'); /* Context Dependent Files [hpux] */ 2017167465Smp } 2018167465Smp xfree(p2); 2019167465Smp } 2020167465Smp#endif 202159243Sobrien#ifdef S_ISNWK 202259243Sobrien if (S_ISNWK(statb.st_mode)) /* Network Special [hpux] */ 202359243Sobrien return (':'); 202459243Sobrien#endif 202559243Sobrien#ifdef S_ISCHR 202659243Sobrien if (S_ISCHR(statb.st_mode)) /* char device */ 202759243Sobrien return ('%'); 202859243Sobrien#endif 202959243Sobrien#ifdef S_ISBLK 203059243Sobrien if (S_ISBLK(statb.st_mode)) /* block device */ 203159243Sobrien return ('#'); 203259243Sobrien#endif 203359243Sobrien#ifdef S_ISDIR 203459243Sobrien if (S_ISDIR(statb.st_mode)) /* normal Directory */ 203559243Sobrien return ('/'); 203659243Sobrien#endif 203759243Sobrien if (statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) 203859243Sobrien return ('*'); 203959243Sobrien } 204059243Sobrien } 204159243Sobrien return (' '); 204259243Sobrien} /* end filetype */ 204359243Sobrien 204459243Sobrien 204559243Sobrien/* isadirectory(): 204659243Sobrien * Return trus if the file is a directory 204759243Sobrien */ 204859243Sobrienstatic int 2049167465Smpisadirectory(const Char *dir, const Char *file) 2050167465Smp /* return 1 if dir/file is a directory */ 2051167465Smp /* uses stat rather than lstat to get dest. */ 205259243Sobrien{ 205359243Sobrien if (dir) { 2054167465Smp Char *path; 2055167465Smp char *cpath; 205659243Sobrien struct stat statb; 205759243Sobrien 2058167465Smp path = Strspl(dir, file); 2059167465Smp cpath = short2str(path); 2060167465Smp xfree(path); 2061167465Smp if (stat(cpath, &statb) >= 0) { /* resolve through symlink */ 206259243Sobrien#ifdef S_ISSOCK 206359243Sobrien if (S_ISSOCK(statb.st_mode)) /* Socket */ 206459243Sobrien return 0; 206559243Sobrien#endif 206659243Sobrien#ifdef S_ISFIFO 206759243Sobrien if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ 206859243Sobrien return 0; 206959243Sobrien#endif 207059243Sobrien if (S_ISDIR(statb.st_mode)) /* normal Directory */ 207159243Sobrien return 1; 207259243Sobrien } 207359243Sobrien } 207459243Sobrien return 0; 207559243Sobrien} /* end isadirectory */ 207659243Sobrien 207759243Sobrien 207859243Sobrien 207959243Sobrien/* find_rows(): 208059243Sobrien * Return how many rows needed to print sorted down columns 208159243Sobrien */ 208259243Sobrienstatic int 2083167465Smpfind_rows(Char *items[], int count, int no_file_suffix) 208459243Sobrien{ 2085145479Smp int i, columns, rows; 208659243Sobrien unsigned int maxwidth = 0; 208759243Sobrien 208859243Sobrien for (i = 0; i < count; i++) /* find widest string */ 208959243Sobrien maxwidth = max(maxwidth, (unsigned int) Strlen(items[i])); 209059243Sobrien 209159243Sobrien maxwidth += no_file_suffix ? 1 : 2; /* for the file tag and space */ 209259243Sobrien columns = (TermH + 1) / maxwidth; /* PWP: terminal size change */ 209359243Sobrien if (!columns) 209459243Sobrien columns = 1; 209559243Sobrien rows = (count + (columns - 1)) / columns; 209659243Sobrien 209759243Sobrien return rows; 209859243Sobrien} /* end rows_needed_by_print_by_column */ 209959243Sobrien 210059243Sobrien 210159243Sobrien/* print_by_column(): 210259243Sobrien * Print sorted down columns or across columns when the first 210359243Sobrien * word of $listflags shell variable contains 'x'. 210459243Sobrien * 210559243Sobrien */ 210659243Sobrienvoid 2107167465Smpprint_by_column(Char *dir, Char *items[], int count, int no_file_suffix) 210859243Sobrien{ 2109145479Smp int i, r, c, columns, rows; 2110167465Smp size_t w; 2111167465Smp unsigned int wx, maxwidth = 0; 211259243Sobrien Char *val; 2113145479Smp int across; 211459243Sobrien 211559243Sobrien lbuffed = 0; /* turn off line buffering */ 211659243Sobrien 211759243Sobrien 211859243Sobrien across = ((val = varval(STRlistflags)) != STRNULL) && 211959243Sobrien (Strchr(val, 'x') != NULL); 212059243Sobrien 2121145479Smp for (i = 0; i < count; i++) { /* find widest string */ 2122145479Smp maxwidth = max(maxwidth, (unsigned int) NLSStringWidth(items[i])); 2123145479Smp } 212459243Sobrien 212559243Sobrien maxwidth += no_file_suffix ? 1 : 2; /* for the file tag and space */ 212659243Sobrien columns = TermH / maxwidth; /* PWP: terminal size change */ 212759243Sobrien if (!columns || !isatty(didfds ? 1 : SHOUT)) 212859243Sobrien columns = 1; 212959243Sobrien rows = (count + (columns - 1)) / columns; 213059243Sobrien 213159243Sobrien i = -1; 213259243Sobrien for (r = 0; r < rows; r++) { 213359243Sobrien for (c = 0; c < columns; c++) { 213459243Sobrien i = across ? (i + 1) : (c * rows + r); 213559243Sobrien 213659243Sobrien if (i < count) { 2137145479Smp wx = 0; 2138167465Smp w = Strlen(items[i]); 213959243Sobrien 214059243Sobrien#ifdef COLOR_LS_F 214159243Sobrien if (no_file_suffix) { 214259243Sobrien /* Print the command name */ 214359243Sobrien Char f = items[i][w - 1]; 214459243Sobrien items[i][w - 1] = 0; 214559243Sobrien print_with_color(items[i], w - 1, f); 2146167465Smp items[i][w - 1] = f; 214759243Sobrien } 214859243Sobrien else { 214959243Sobrien /* Print filename followed by '/' or '*' or ' ' */ 215059243Sobrien print_with_color(items[i], w, filetype(dir, items[i])); 2151145479Smp wx++; 215259243Sobrien } 215359243Sobrien#else /* ifndef COLOR_LS_F */ 215459243Sobrien if (no_file_suffix) { 215559243Sobrien /* Print the command name */ 215659243Sobrien xprintf("%S", items[i]); 215759243Sobrien } 215859243Sobrien else { 215959243Sobrien /* Print filename followed by '/' or '*' or ' ' */ 2160167465Smp xprintf("%-S%c", items[i], filetype(dir, items[i])); 2161145479Smp wx++; 216259243Sobrien } 216359243Sobrien#endif /* COLOR_LS_F */ 216459243Sobrien 2165145479Smp if (c < (columns - 1)) { /* Not last column? */ 2166167465Smp w = NLSStringWidth(items[i]) + wx; 216759243Sobrien for (; w < maxwidth; w++) 216859243Sobrien xputchar(' '); 2169145479Smp } 217059243Sobrien } 217159243Sobrien else if (across) 217259243Sobrien break; 217359243Sobrien } 217459243Sobrien if (Tty_raw_mode) 217559243Sobrien xputchar('\r'); 217659243Sobrien xputchar('\n'); 217759243Sobrien } 217859243Sobrien 217959243Sobrien lbuffed = 1; /* turn back on line buffering */ 218059243Sobrien flush(); 218159243Sobrien} /* end print_by_column */ 218259243Sobrien 218359243Sobrien 218459243Sobrien/* StrQcmp(): 218559243Sobrien * Compare strings ignoring the quoting chars 218659243Sobrien */ 218759243Sobrienint 2188167465SmpStrQcmp(const Char *str1, const Char *str2) 218959243Sobrien{ 219059243Sobrien for (; *str1 && samecase(*str1 & TRIM) == samecase(*str2 & TRIM); 219159243Sobrien str1++, str2++) 219259243Sobrien continue; 219359243Sobrien /* 219459243Sobrien * The following case analysis is necessary so that characters which look 219559243Sobrien * negative collate low against normal characters but high against the 219659243Sobrien * end-of-string NUL. 219759243Sobrien */ 219859243Sobrien if (*str1 == '\0' && *str2 == '\0') 219959243Sobrien return (0); 220059243Sobrien else if (*str1 == '\0') 220159243Sobrien return (-1); 220259243Sobrien else if (*str2 == '\0') 220359243Sobrien return (1); 220459243Sobrien else 220559243Sobrien return ((*str1 & TRIM) - (*str2 & TRIM)); 220659243Sobrien} /* end StrQcmp */ 220759243Sobrien 220859243Sobrien 220959243Sobrien/* fcompare(): 2210167465Smp * Comparison routine for qsort, (Char **, Char **) 221159243Sobrien */ 221259243Sobrienint 2213167465Smpfcompare(const void *xfile1, const void *xfile2) 221459243Sobrien{ 2215167465Smp const Char *const *file1 = xfile1, *const *file2 = xfile2; 2216167465Smp 2217167465Smp return collate(*file1, *file2); 221859243Sobrien} /* end fcompare */ 221959243Sobrien 222059243Sobrien 222159243Sobrien/* catn(): 222259243Sobrien * Concatenate src onto tail of des. 222359243Sobrien * Des is a string whose maximum length is count. 222459243Sobrien * Always null terminate. 222559243Sobrien */ 222659243Sobrienvoid 2227167465Smpcatn(Char *des, const Char *src, int count) 222859243Sobrien{ 2229167465Smp while (*des && --count > 0) 223059243Sobrien des++; 2231167465Smp while (--count > 0) 223259243Sobrien if ((*des++ = *src++) == 0) 223359243Sobrien return; 223459243Sobrien *des = '\0'; 223559243Sobrien} /* end catn */ 223659243Sobrien 223759243Sobrien 223859243Sobrien/* copyn(): 223959243Sobrien * like strncpy but always leave room for trailing \0 224059243Sobrien * and always null terminate. 224159243Sobrien */ 224259243Sobrienvoid 2243167465Smpcopyn(Char *des, const Char *src, size_t count) 224459243Sobrien{ 2245167465Smp while (--count != 0) 224659243Sobrien if ((*des++ = *src++) == 0) 224759243Sobrien return; 224859243Sobrien *des = '\0'; 224959243Sobrien} /* end copyn */ 225059243Sobrien 225159243Sobrien 225259243Sobrien/* tgetenv(): 225359243Sobrien * like it's normal string counter-part 225459243Sobrien */ 225559243SobrienChar * 2256167465Smptgetenv(Char *str) 225759243Sobrien{ 225859243Sobrien Char **var; 2259145479Smp size_t len; 2260145479Smp int res; 226159243Sobrien 2262145479Smp len = Strlen(str); 226359243Sobrien /* Search the STR_environ for the entry matching str. */ 226459243Sobrien for (var = STR_environ; var != NULL && *var != NULL; var++) 226559243Sobrien if (Strlen(*var) >= len && (*var)[len] == '=') { 226659243Sobrien /* Temporarily terminate the string so we can copy the variable 226759243Sobrien name. */ 226859243Sobrien (*var)[len] = '\0'; 226959243Sobrien res = StrQcmp(*var, str); 227059243Sobrien /* Restore the '=' and return a pointer to the value of the 227159243Sobrien environment variable. */ 227259243Sobrien (*var)[len] = '='; 227359243Sobrien if (res == 0) 227459243Sobrien return (&((*var)[len + 1])); 227559243Sobrien } 227659243Sobrien return (NULL); 227759243Sobrien} /* end tgetenv */ 227859243Sobrien 227959243Sobrien 228059243Sobrienstruct scroll_tab_list *scroll_tab = 0; 228159243Sobrien 228259243Sobrienstatic void 2283167465Smpadd_scroll_tab(Char *item) 228459243Sobrien{ 228559243Sobrien struct scroll_tab_list *new_scroll; 228659243Sobrien 2287167465Smp new_scroll = xmalloc(sizeof(struct scroll_tab_list)); 228859243Sobrien new_scroll->element = Strsave(item); 228959243Sobrien new_scroll->next = scroll_tab; 229059243Sobrien scroll_tab = new_scroll; 229159243Sobrien} 229259243Sobrien 229359243Sobrienstatic void 2294167465Smpchoose_scroll_tab(struct Strbuf *exp_name, int cnt) 229559243Sobrien{ 229659243Sobrien struct scroll_tab_list *loop; 229759243Sobrien int tmp = cnt; 229859243Sobrien Char **ptr; 229959243Sobrien 2300167465Smp ptr = xmalloc(sizeof(Char *) * cnt); 2301167465Smp cleanup_push(ptr, xfree); 230259243Sobrien 230359243Sobrien for(loop = scroll_tab; loop && (tmp >= 0); loop = loop->next) 230459243Sobrien ptr[--tmp] = loop->element; 230559243Sobrien 2306167465Smp qsort(ptr, cnt, sizeof(Char *), fcompare); 2307167465Smp 2308167465Smp exp_name->len = 0; 2309167465Smp Strbuf_append(exp_name, ptr[curchoice]); 2310167465Smp Strbuf_terminate(exp_name); 2311167465Smp cleanup_until(ptr); 231259243Sobrien} 231359243Sobrien 231459243Sobrienstatic void 2315167465Smpfree_scroll_tab(void) 231659243Sobrien{ 231759243Sobrien struct scroll_tab_list *loop; 231859243Sobrien 231959243Sobrien while(scroll_tab) { 232059243Sobrien loop = scroll_tab; 232159243Sobrien scroll_tab = scroll_tab->next; 2322167465Smp xfree(loop->element); 2323167465Smp xfree(loop); 232459243Sobrien } 232559243Sobrien} 2326