/* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/tw.parse.c,v 3.0 1991/07/04 21:49:28 christos Exp $ */ /* * tw.parse.c: Everyone has taken a shot in this futile effort to * lexically analyze a csh line... Well we cannot good * a job as good as sh.lex.c; but we try. Amazing that * it works considering how many hands have touched this code */ /*- * Copyright (c) 1980, 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #if !defined(lint) && !defined(pdp11) static char *rcsid() { return "$Id: tw.parse.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; } #endif #include "sh.h" #include "tw.h" #include "ed.h" /* #define TENEDEBUG */ /* true if the path has relative elements */ static bool relatives_in_path; static int maxitems = 0; Char **com_list = (Char **) NULL; /* the pre-digested list of commands * for speed and general usefullness */ int numcommands = 0; int have_sorted = 0; /* Set to TRUE if recexact is set and an exact match is found * along with other, longer, matches. */ int non_unique_match = FALSE; #ifdef notdef int dirctr = 0; /* -1 0 1 2 ... */ #endif Char dirflag[5]; /* ' nn\0' - dir #s - . 1 2 ... */ static bool SearchNoDirErr = 0; /* t_search returns -2 if dir is unreadable */ /* do the expand or list on the command line -- SHOULD BE REPLACED */ extern Char NeedsRedraw; /* from ed.h */ extern int TermH; /* from the editor routines */ extern int lbuffed; /* from sh.print.c */ extern bool relatives_in_path; /* set true if PATH has relative elements */ static void free_items __P((Char **, int)); static void extract_dir_and_name __P((Char *, Char *, Char *)); static Char *quote_meta __P((Char *, bool)); static Char *getentry __P((DIR *, int)); static Char *dollar __P((Char *, Char *)); static Char *tilde __P((Char *, Char *)); static Char filetype __P((Char *, Char *)); static int t_glob __P((Char ***)); static int is_prefix __P((Char *, Char *)); static int is_suffix __P((Char *, Char *)); static int recognize __P((Char *, Char *, int, int)); static int ignored __P((Char *)); static void tw_get_comm_list __P((void)); static int isadirectory __P((Char *, Char *)); /* * If we find a set command, then we break a=b to a= and word becomes * b else, we don't break a=b. */ #define isaset(c, w) ((w)[-1] == '=' && \ ((c)[0] == 's' && (c)[1] == 'e' && (c)[2] == 't' && \ ((c[3] == ' ' || (c)[3] == '\t')))) /* * Return value for tenematch(): * > 1: No. of items found * = 1: Exactly one match / spelling corrected * = 0: No match / spelling was correct * < 0: Error (incl spelling correction impossible) */ int tenematch(in_line, in_li_size, num_read, command) Char *in_line; /* match string prefix */ int in_li_size; /* max size of string */ int num_read; /* # actually in in_line */ COMMAND command; /* LIST or RECOGNIZE or PRINT_HELP */ { Char word[FILSIZ + 1]; register Char *str_end, *word_start, *cmd_start, *wp; Char *cmd_st; int space_left; int is_a_cmd; /* UNIX command rather than filename */ int search_ret; /* what search returned for debugging */ int in_single, in_double; /* In single or in_double quotes */ str_end = &in_line[num_read]; /* * space backward looking for the beginning of this command */ for (cmd_st = str_end; cmd_st > in_line; --cmd_st) if (iscmdmeta(cmd_st[-1]) && ((cmd_st - 1 == in_line) || (cmd_st[-2] != '\\'))) break; /* step forward over leading spaces */ while (*cmd_st != '\0' && (*cmd_st == ' ' || *cmd_st == '\t')) cmd_st++; /* * Find LAST occurence of a delimiter in the in_line. The word start is * one character past it. */ for (word_start = str_end; word_start > in_line; --word_start) { if ((ismeta(word_start[-1]) || isaset(cmd_st, word_start)) && (word_start[-1] != '#') && (word_start[-1] != '$') && ((word_start - 1 == in_line) || (word_start[-2] != '\\'))) break; } #ifdef masscomp /* * Avoid a nasty message from the RTU 4.1A & RTU 5.0 comp_r concerning * the "overuse of registers". According to the comp_r release notes, * incorrect code may be produced unless the offending expression is * rewritten. Therefore, we can't just ignore it, DAS DEC-90. */ space_left = in_li_size; space_left -= word_start - in_line + 1; #else space_left = in_li_size - (word_start - in_line) - 1; #endif is_a_cmd = starting_a_command(word_start, in_line); #ifdef TENEDEBUG xprintf("starting_a_command %d\n", is_a_cmd); #endif /* * Quote args */ in_double = 0; in_single = 0; for (cmd_start = word_start, wp = word; cmd_start < str_end && wp <= word + FILSIZ; cmd_start++) switch (*cmd_start) { case '\'': if (!in_double) { if (in_single) in_single = 0; else in_single = QUOTE; /* * Move the word_start further, cause the quotes so far have no * effect. */ if (cmd_start == word_start) word_start++; } else *wp++ = *cmd_start | QUOTE; break; case '"': if (!in_single) { if (in_double) in_double = 0; else in_double = QUOTE; /* * Move the word_start further, cause the quotes so far have no * effect. */ if (cmd_start == word_start) word_start++; } else *wp++ = *cmd_start | QUOTE; break; case '/': /* * This is so that the recognize stuff works easily */ *wp++ = *cmd_start; break; case '\\': if (in_single || in_double) *wp++ = *cmd_start | QUOTE; else *wp++ = *++cmd_start | QUOTE; break; default: *wp++ = *cmd_start | in_single; break; } if (wp > word + FILSIZ) return (-1); *wp = '\0'; #ifdef TENEDEBUG xprintf("\ncmd_st:%s:\n", short2str(cmd_st)); xprintf("word:%s:\n", short2str(word)); xprintf("word:"); for (wp = word; *wp; wp++) xprintf("%c", *wp & QUOTE ? '-' : ' '); xprintf(":\n"); #endif switch ((int) command) { Char buffer[FILSIZ + 1], *bptr; Char *slshp; Char *items[2], **ptr; int i, count; case RECOGNIZE: if (adrof(STRa_correct)) { if ((slshp = Strrchr(word, '/')) != NULL && slshp[1] != NULL) { SearchNoDirErr = 1; for (bptr = word; bptr < slshp; bptr++) { /* * do not try to correct spelling of words containing * globbing characters */ if (isglob(*bptr)) { SearchNoDirErr = 0; break; } } } } else slshp = STRNULL; search_ret = t_search(word, wp, command, space_left, is_a_cmd, 1); SearchNoDirErr = 0; if (search_ret == -2) { Char rword[FILSIZ + 1]; (void) Strcpy(rword, slshp); if (slshp != STRNULL) *slshp = NULL; if ((search_ret = spell_me(word, sizeof(word), is_a_cmd)) == 1) { DeleteBack(str_end - word_start);/* get rid of old word */ (void) Strcat(word, rword); if (InsertStr(word) < 0) /* insert newly spelled word */ return -1; /* error inserting */ wp = word + Strlen(word); search_ret = t_search(word, wp, command, space_left, is_a_cmd, 1); } } /* * Change by Christos Zoulas: if the name has metachars in it, quote * the metachars, but only if we are outside quotes. */ if (*wp && InsertStr((in_single || in_double) ? wp : quote_meta(wp, (bool) is_set(STRaddsuffix))) < 0) /* put it in the input buffer */ return -1; /* error inserting */ return search_ret; case SPELL: for (bptr = word_start; bptr < str_end; bptr++) { /* * do not try to correct spelling of words containing globbing * characters */ if (isglob(*bptr)) return 0; } if ((search_ret = spell_me(word, sizeof(word), is_a_cmd)) == 1) { DeleteBack(str_end - word_start); /* get rid of old word */ if (InsertStr(word) < 0) /* insert newly spelled word */ return -1; /* error inserting */ } return search_ret; case PRINT_HELP: do_help(cmd_st); return 1; case GLOB: case GLOB_EXPAND: (void) Strncpy(buffer, word, FILSIZ + 1); items[0] = buffer; items[1] = NULL; ptr = items; if (is_a_cmd) { xprintf("Sorry no globbing for commands yet..\n"); return 0; } if ((count = t_glob(&ptr)) > 0) { if (command == GLOB) print_by_column(STRNULL, ptr, count, is_a_cmd); else { DeleteBack(str_end - word_start);/* get rid of old word */ for (i = 0; i < count; i++) if (ptr[i] && *ptr[i]) { if (InsertStr((in_single || in_double) ? ptr[i] : quote_meta(ptr[i], 0)) < 0 || InsertStr(STRspace) < 0) { blkfree(ptr); return (-1); } } } blkfree(ptr); } return count; case VARS_EXPAND: if (dollar(buffer, word)) { DeleteBack(str_end - word_start); if (InsertStr((in_single || in_double) ? buffer : quote_meta(buffer, 0)) < 0) return (-1); return (1); } return (0); case LIST: search_ret = t_search(word, wp, command, space_left, is_a_cmd, 1); return search_ret; default: xprintf("tcsh: Internal match error.\n"); return 1; } } static int t_glob(v) register Char ***v; { jmp_buf osetexit; if (**v == 0) return (0); gflag = 0, tglob(*v); if (gflag) { getexit(osetexit); /* make sure to come back here */ if (setexit() == 0) *v = globall(*v); resexit(osetexit); gargv = 0; if (haderr) { haderr = 0; NeedsRedraw = 1; return (-1); } if (*v == 0) return (0); } else return (0); return ((int)gargc); } /* * quote (\) the meta-characters in a word * except trailing space if trail_space is set * return pointer to quoted word in static storage */ static Char * quote_meta(word, trail_space) Char *word; bool trail_space; { static Char buffer[2 * FILSIZ + 1], *bptr, *wptr; for (bptr = buffer, wptr = word; *wptr != '\0';) { if ((cmap(*wptr, _META | _DOL | _Q | _ESC | _GLOB) || *wptr == HIST || *wptr == HISTSUB) && (*wptr != ' ' || !trail_space || *(wptr + 1) != '\0')) *bptr++ = '\\'; *bptr++ = *wptr++; } *bptr = '\0'; return (buffer); } /* * return true if check items initial chars in template * This differs from PWB imatch in that if check is null * it items anything */ static int is_prefix(check, template) register Char *check, *template; { for (; *check; check++, template++) if ((*check & TRIM) != (*template & TRIM)) return (FALSE); return (TRUE); } /* * Return true if the chars in template appear at the * end of check, I.e., are it's suffix. */ static int is_suffix(check, template) register Char *check, *template; { register Char *t, *c; for (t = template; *t++;); for (c = check; *c++;); for (;;) { if (t == template) return 1; --t; --c; if (c == check || (*t & TRIM) != (*c & TRIM)) return 0; } } static int ignored(entry) register Char *entry; { struct varent *vp; register Char **cp; if ((vp = adrof(STRfignore)) == NULL || (cp = vp->vec) == NULL) return (FALSE); for (; *cp != NULL; cp++) if (is_suffix(entry, *cp)) return (TRUE); return (FALSE); } /* return true if the command starting at wordstart is a command */ #define EVEN(x) (((x) & 1) != 1) int starting_a_command(wordstart, in_line) register Char *wordstart, *in_line; { register Char *ptr, *ncmdstart; int count; static Char cmdstart[] = {'`', ';', '&', '(', '|', '\0'}, cmdalive[] = {' ', '\t', '\'', '"', '<', '>', '\0'}; /* * Find if the number of backquotes is odd or even. */ for (ptr = wordstart, count = 0; ptr >= in_line; count += (*ptr-- == '`')); /* * if the number of backquotes is even don't include the backquote char in * the list of command starting delimiters [if it is zero, then it does not * matter] */ ncmdstart = cmdstart + EVEN(count); /* * look for the characters previous to this word if we find a command * starting delimiter we break. if we find whitespace and another previous * word then we are not a command * * count is our state machine: 0 looking for anything 1 found white-space * looking for non-ws */ for (count = 0; wordstart >= in_line; wordstart--) { if (*wordstart == '\0') continue; if (Strchr(ncmdstart, *wordstart)) break; /* * found white space */ if (ptr = Strchr(cmdalive, *wordstart)) count = 1; if (count == 1 && !ptr) return (FALSE); } if (wordstart > in_line) switch (*wordstart) { case '&': /* Look for >& */ while (wordstart > in_line && (*--wordstart == ' ' || *wordstart == '\t')); if (*wordstart == '>') return (FALSE); break; case '(': /* check for foreach, if etc. */ while (wordstart > in_line && (*--wordstart == ' ' || *wordstart == '\t')); if (!iscmdmeta(*wordstart) && (*wordstart != ' ' && *wordstart != '\t')) return (FALSE); break; default: break; } return (TRUE); } /* * Object: extend what user typed up to an ambiguity. * Algorithm: * On first match, copy full entry (assume it'll be the only match) * On subsequent matches, shorten extended_name to the first * character mismatch between extended_name and entry. * If we shorten it back to the prefix length, stop searching. */ static int recognize(extended_name, entry, name_length, numitems) Char *extended_name, *entry; int name_length, numitems; { if (numitems == 1) /* 1st match */ copyn(extended_name, entry, MAXNAMLEN); else { /* 2nd and subsequent matches */ register Char *x, *ent; register int len = 0; for (x = extended_name, ent = entry; *x && (*x & TRIM) == (*ent & TRIM); x++, len++, ent++); *x = '\0'; /* Shorten at 1st char diff */ if (len == name_length) /* Ambiguous to prefix? */ return (-1); /* So stop now and save time */ } return (0); } /* * Perform a RECOGNIZE or LIST command on string "word". * * Return value: * >= 0: SPELL command: "distance" (see spdist()) * other: No. of items found * < 0: Error (message or beep is output) */ /*ARGSUSED*/ int t_search(word, wp, command, max_word_length, look_command, list_max) Char *word, *wp; /* original end-of-word */ COMMAND command; int max_word_length, look_command, list_max; { register ignoring = 1, nignored = 0; register name_length, /* Length of prefix (file name) */ look_lognames; /* True if looking for login names */ int showpathn; /* True if we want path number */ Char tilded_dir[FILSIZ + 1], /* dir after ~ expansion */ dollar_dir[FILSIZ + 1], /* dir after $ expansion */ dir[FILSIZ + 1], /* /x/y/z/ part in /x/y/z/f */ name[MAXNAMLEN + 1],/* f part in /d/d/d/f */ extended_name[MAXNAMLEN + 1], /* the recognized (extended) * name */ *entry = NULL, /* single directory entry or logname */ *target; /* Target to expand/correct/list */ int next_command = 0; /* the next command to take out of */ /* the list of commands */ int look_shellvar, /* true if looking for $foo */ look_file; /* true if looking for a file name */ Char **pathv; /* pointer to PATH elements */ struct varent *v_ptr = NULL;/* current shell variable position */ Char **env_ptr = NULL; /* current env. variable position */ int d = 4, nd; /* distance and new distance to command for * SPELL */ int exec_check = 0, dir_ok = 0; /* need to check * executability/directory */ static Char /* For unset path */ * pv[2] = {STRNULL, NULL}; static DIR * dir_fd = NULL; static Char ** items = NULL; /* file names when doing a LIST */ /* * bugfix by Marty Grossman (grossman@CC5.BBN.COM): directory listing can * dump core when interrupted */ static int numitems; pathv = (v_ptr = adrof(STRPATH)) == NULL ? pv : v_ptr->vec; if (items != NULL) FREE_ITEMS(items, numitems); numitems = 0; if (dir_fd != NULL) FREE_DIR(dir_fd); non_unique_match = FALSE; /* See the recexact code below */ extract_dir_and_name(word, dir, name); look_lognames = (*word == '~') && (Strchr(word, '/') == NULL); look_shellvar = (target = Strrchr(name, '$')) && (Strchr(name, '/') == NULL); look_file = (!look_command && !look_lognames && !look_shellvar) || Strchr(word, '/'); /* PWP: don't even bother when doing ALL of the commands */ if (look_command && (*word == '\0')) { Beep(); return (-1); } tilded_dir[0] = '\0'; dollar_dir[0] = '\0'; if (look_shellvar) { /* Looking for a shell var? */ v_ptr = tw_shell_list_start(); env_ptr = tw_env_list_start(); target++; } else target = name; if (look_shellvar || look_file) { Char *nd = NULL; /* Open the directory */ /* expand ~user/... and variables stuff */ if ((dollar(dollar_dir, dir) == 0) || (tilde(tilded_dir, dollar_dir) == 0) || !(nd = dnormalize(*tilded_dir ? tilded_dir : STRdot)) || ((dir_fd = opendir(short2str(nd))) == NULL)) { xfree((ptr_t) nd); if (SearchNoDirErr) return (-2); xprintf("\n%s unreadable\n", *tilded_dir ? short2str(tilded_dir) : (*dollar_dir ? short2str(dollar_dir) : short2str(dir))); NeedsRedraw = 1; return (-1); } if (nd) { if (*tilded_dir != '\0') { Char *s, *d, *p; /* * Copy and append a / if there was one */ for (p = tilded_dir; *p; p++); if (*--p == '/') { for (p = nd; *p; p++); if (*--p != '/') p = NULL; } for (d = tilded_dir, s = nd; *d++ = *s++;); if (!p) { *d-- = '\0'; *d = '/'; } } xfree((ptr_t) nd); } } else if (look_lognames) { /* Looking for login names? */ (void) setpwent(); /* Open passwd file */ copyn(name, &word[1], MAXNAMLEN); /* name sans ~ */ } else if (look_command) { if (!numcommands) /* if we have no list of commands */ tw_get_comm_list(); if (!have_sorted) { /* if we haven't sorted them yet */ tw_builtins_add(); tw_aliases_add(); tw_sort_comms(); /* re-build the command path for twenex.c */ } copyn(target, word, MAXNAMLEN); /* so it can match things */ } else { xprintf("\ntcsh internal error: I don't know what I'm looking for!\n"); NeedsRedraw = 1; return (-1); } again: name_length = Strlen(target); showpathn = look_command && is_set(STRl_pathnum); while (1) { if (look_shellvar) { if ((entry = tw_n_shell_var(&v_ptr)) == NULL) if ((entry = tw_n_env_var(&env_ptr)) == NULL) break; } else if (look_file || look_lognames) { if ((entry = getentry(dir_fd, look_lognames)) == NULL) { break; } /* * Don't match . files on null prefix match */ if (name_length == 0 && entry[0] == '.' && !look_lognames && !is_set(STRshowdots)) continue; if (look_command && !look_lognames) { exec_check = 1; dir_ok = 1; } } else if (look_command) { #ifdef NOTDEF /* Not possible */ if (numcommands == 0) { dohash(); } #endif /* searching . added by Andreas Luik */ if ((next_command < numcommands) && (entry = com_list[next_command]) == NULL) next_command = numcommands; if (next_command >= numcommands) { /* search relative elems */ if (!relatives_in_path) break; /* we don't need to do it */ while ((dir_fd == NULL || (entry = getentry(dir_fd, FALSE)) == NULL) && *pathv) { if (dir_fd != NULL) FREE_DIR(dir_fd); entry = NULL; while (*pathv && pathv[0][0] == '/') pathv++; if (*pathv) { if (pathv[0][0] == '\0' || (pathv[0][0] == '.' && pathv[0][1] == '\0')) { *tilded_dir = '\0'; dir_fd = opendir("."); } else { copyn(tilded_dir, *pathv, FILSIZ); catn(tilded_dir, STRslash, FILSIZ); dir_fd = opendir(short2str(*pathv)); } pathv++; } } if (entry == NULL) break; /* end of PATH */ /* * executability check for other than "." should perhaps be * conditional on recognize_only_executables? */ exec_check = 1; dir_ok = 0; } else next_command++; } if (command == SPELL) { /* correct the spelling of the last bit */ if (name_length == 0) {/* zero-length word can't be misspelled */ extended_name[0] = '\0';/* (not trying is important for ~) */ d = 0; break; } nd = spdist(entry, target); /* test the entry against original */ if (nd <= d && nd != 4) { if (exec_check && !executable(tilded_dir, entry, dir_ok)) continue; (void) Strcpy(extended_name, entry); d = nd; if (d == 0) /* if found it exactly */ break; } else if (nd == 4) { if (spdir(extended_name, tilded_dir, entry, target)) { if (exec_check && !executable(tilded_dir, extended_name, dir_ok)) continue; d = 0; break; } } } else if (command == LIST) { /* LIST command */ register int length; register long i; register Char **ni, **p2; if (!is_prefix(target, entry)) continue; if (exec_check && !executable(tilded_dir, entry, dir_ok)) continue; if (items == NULL || maxitems == 0) { items = (Char **) xmalloc((size_t) (sizeof(items[0]) * (ITEMS_START + 1))); maxitems = ITEMS_START; for (i = 0, p2 = items; i < maxitems; i++) *p2++ = NULL; } else if (numitems >= maxitems) { ni = (Char **) xrealloc((ptr_t) items, (size_t) (sizeof(items[0])) * (maxitems + ITEMS_INCR)); items = ni; maxitems += ITEMS_INCR; } length = Strlen(entry) + 1; if (showpathn) length += Strlen(dirflag); if (!look_lognames && !look_shellvar) length++; /* safety check */ items[numitems] = (Char *) xmalloc((size_t) (length * sizeof(Char))); copyn(items[numitems], entry, MAXNAMLEN); if (!look_lognames && !look_shellvar && !(look_command && !look_file)) { Char typestr[2]; typestr[0] = filetype(tilded_dir, entry); typestr[1] = '\0'; catn(items[numitems], typestr, MAXNAMLEN); } if (showpathn) catn(items[numitems], dirflag, MAXNAMLEN); numitems++; } else { /* RECOGNIZE command */ if (!is_prefix(target, entry)) continue; if (exec_check && !executable(tilded_dir, entry, dir_ok)) continue; if (ignoring && ignored(entry)) { nignored++; continue; } if (is_set(STRrecexact)) { if (StrQcmp(target, entry) == 0) { /* EXACT match */ copyn(extended_name, entry, MAXNAMLEN); numitems = 1; /* fake into expanding */ non_unique_match = TRUE; break; } } if (recognize(extended_name, entry, name_length, ++numitems)) break; } } if (ignoring && numitems == 0 && nignored > 0) { ignoring = 0; nignored = 0; if (look_lognames) (void) setpwent(); else rewinddir(dir_fd); goto again; } if (look_lognames) { #ifdef YPBUGS fix_yp_bugs(); #endif /* YPBUGS */ (void) endpwent(); } else if (look_file || look_shellvar || (look_command && relatives_in_path)) { if (dir_fd != NULL) FREE_DIR(dir_fd); } if (command == RECOGNIZE) { if (numitems > 0) { if (look_lognames) copyn(word, STRtilde, 1); else if (look_shellvar) { Char *ptr = Strrchr(word, '$'); *++ptr = '\0'; /* Delete after the dollar */ } else if (look_file) copyn(word, dir, max_word_length); /* put back dir part */ else word[0] = '\0'; catn(word, extended_name, max_word_length); /* add extended name */ if (is_set(STRaddsuffix)) { if (numitems == 1) { if (look_lognames) { /* add / */ catn(word, STRslash, max_word_length); } else if (look_shellvar) { struct varent *vp = adrof(extended_name); Char *stp; /* * Don't consider array variables or empty variables */ if (vp) { if (!(stp = vp->vec[0]) || vp->vec[0][0] == '\0' || vp->vec[1]) { catn(word, STRspace, max_word_length); stp = NULL; } else stp = vp->vec[0]; } else if ((stp = Getenv(extended_name)) == NULL) catn(word, STRspace, max_word_length); if (stp != NULL) { *--target = '\0'; (void) Strcat(tilded_dir, name); if (isadirectory(tilded_dir, stp)) catn(word, STRslash, max_word_length); else catn(word, STRspace, max_word_length); } } else if (look_file) { if (isadirectory(tilded_dir, extended_name)) { catn(word, STRslash, max_word_length); } else { catn(word, STRspace, max_word_length); } } else { /* prob. looking for a command */ catn(word, STRspace, max_word_length); } } } } return (numitems); /* at the end */ } else if (command == LIST) { register int max_items = 0; register Char *cp; if (cp = value(STRl_max)) { while (*cp) { if (!Isdigit(*cp)) { max_items = 0; break; } max_items = max_items * 10 + *cp++ - '0'; } } if ((max_items > 0) && (numitems > max_items) && list_max) { char tc; xprintf("There are %d items, list them anyway? [n/y] ", numitems); flush(); /* We should be in Rawmode here, so no \n to catch */ (void) read(SHIN, &tc, 1); xprintf("%c\r\n", tc); /* echo the char, do a newline */ if ((tc != 'y') && (tc != 'Y')) goto done_list; } qsort((ptr_t) items, (size_t) numitems, sizeof(items[1]), (int (*) __P((const void *, const void *))) fcompare); print_by_column(STRNULL, items, numitems, TRUE); done_list: if (items != NULL) FREE_ITEMS(items, numitems); return (numitems); } else if (command == SPELL) { if (look_lognames) copyn(word, STRtilde, 1); else if (look_shellvar) { Char *ptr = Strrchr(word, '$'); *++ptr = '\0'; /* Delete after the dollar */ } else if (look_file) copyn(word, dir, max_word_length); /* put back dir part */ else word[0] = '\0'; catn(word, extended_name, max_word_length); /* add extended name */ return (d); } else { xprintf("Bad tw_command\n"); return (0); } } /* stuff for general command line hacking */ /* * Strip next directory from path; return ptr to next unstripped directory. */ #ifdef notdef Char * extct_dir_from_path(path, dir) Char *path, dir[]; { register Char *d = dir; while (*path && (*path == ' ' || *path == ':')) path++; while (*path && (*path != ' ' && *path != ':')) *(d++) = *(path++); while (*path && (*path == ' ' || *path == ':')) path++; ++dirctr; if (*dir == '.') (void) Strcpy(dirflag, STRdotsp); else { dirflag[0] = ' '; if (dirctr <= 9) { dirflag[1] = '0' + dirctr; dirflag[2] = '\0'; } else { dirflag[1] = '0' + dirctr / 10; dirflag[2] = '0' + dirctr % 10; dirflag[3] = '\0'; } } *(d++) = '/'; *d = 0; return path; } #endif static void free_items(items, numitems) register Char **items; register int numitems; { register int i; /* for (i = 0; items[i] != (Char *)NULL; i++) */ for (i = 0; i < numitems; i++) xfree((ptr_t) items[i]); xfree((ptr_t) items); maxitems = 0; } /* * parse full path in file into 2 parts: directory and file names * Should leave final slash (/) at end of dir. */ static void extract_dir_and_name(path, dir, name) Char *path, *dir, *name; { register Char *p; p = Strrchr(path, '/'); if (p == NULL) { copyn(name, path, MAXNAMLEN); dir[0] = '\0'; } else { p++; copyn(name, p, MAXNAMLEN); copyn(dir, path, p - path); } } static Char * getentry(dir_fd, look_lognames) DIR *dir_fd; int look_lognames; { register struct passwd *pw; static Char retname[MAXPATHLEN]; register struct dirent *dirp; if (look_lognames) { /* Is it login names we want? */ pw = getpwent(); if (pw == NULL) { #ifdef YPBUGS fix_yp_bugs(); #endif return (NULL); } (void) Strcpy(retname, str2short(pw->pw_name)); return (retname); } else { /* It's a dir entry we want */ if (dirp = readdir(dir_fd)) { (void) Strcpy(retname, str2short(dirp->d_name)); return (retname); } return (NULL); } } /* * expand "/$old1/$old2/old3/" * to "/value_of_old1/value_of_old2/old3/" */ static Char * dollar(new, old) Char *new, *old; { Char *var, *val, *p, save; int space; for (space = FILSIZ, p = new; *old && space > 0;) if (*old != '$') { *p++ = *old++; space--; } else { struct varent *vp; /* found a variable, expand it */ for (var = ++old; alnum(*old); old++); save = *old; *old = '\0'; vp = adrof(var); val = (!vp) ? Getenv(var) : NULL; *old = save; /* * Don't expand array variables */ if (vp) { if (!vp->vec[0] || vp->vec[1]) { *new = '\0'; return (NULL); } else val = vp->vec[0]; } else if (!val) { *new = '\0'; return (NULL); } for (; space > 0 && *val; space--) *p++ = *val++; } *p = '\0'; return (new); } /* * expand "old" file name with possible tilde usage * ~person/mumble * expands to * home_directory_of_person/mumble * into string "new". */ static Char * tilde(new, old) Char *new, *old; { register Char *o, *p; if ((old[0] != '~') && (old[0] != '=')) { (void) Strcpy(new, old); return (new); } new[0] = '\0'; for (p = new, o = &old[1]; *o && *o != '/'; *p++ = *o++); *p = '\0'; if (old[0] == '~') { if (gethdir(new)) return (NULL); } else { /* '=' stack expansion */ if (!Isdigit(old[1]) && old[1] != '-') return (NULL); if (!getstakd(new, (old[1] == '-') ? -1 : old[1] - '0')) return (NULL); } (void) Strcat(new, o); return (new); } static Char filetype(dir, file) /* symbology from 4.3 ls command */ Char *dir, *file; { if (dir) { Char path[512]; char *ptr; struct stat statb; (void) Strcpy(path, dir); catn(path, file, sizeof(path) / sizeof(Char)); if (lstat(ptr = short2str(path), &statb) != -1) /* see above #define of lstat */ { #ifdef S_ISLNK if (S_ISLNK(statb.st_mode)) { /* Symbolic link */ if (adrof(STRl_links)) { if (stat(ptr, &statb) == -1) return ('&'); else if (S_ISDIR(statb.st_mode)) return ('>'); else return ('@'); } else return ('@'); } #endif #ifdef S_ISSOCK if (S_ISSOCK(statb.st_mode)) /* Socket */ return ('='); #endif #ifdef S_ISFIFO if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ return ('|'); #endif #ifdef S_ISHIDDEN if (S_ISHIDDEN(statb.st_mode)) /* Hidden Directory [aix] */ return ('+'); #endif #ifdef S_ISCDF if (S_ISCDF(statb.st_mode)) /* Context Dependent Files [hpux] */ return ('+'); #endif #ifdef S_ISNWK if (S_ISNWK(statb.st_mode)) /* Network Special [hpux] */ return (':'); #endif if (S_ISCHR(statb.st_mode)) /* char device */ return ('%'); if (S_ISBLK(statb.st_mode)) /* block device */ return ('#'); if (S_ISDIR(statb.st_mode)) /* normal Directory */ return ('/'); if (statb.st_mode & 0111) return ('*'); } } return (' '); } static int isadirectory(dir, file) /* return 1 if dir/file is a directory */ Char *dir, *file; /* uses stat rather than lstat to get dest. */ { if (dir) { Char path[MAXPATHLEN]; struct stat statb; (void) Strcpy(path, dir); catn(path, file, sizeof(path) / sizeof(Char)); if (stat(short2str(path), &statb) >= 0) { /* resolve through * symlink */ #ifdef S_ISSOCK if (S_ISSOCK(statb.st_mode)) /* Socket */ return 0; #endif #ifdef S_ISFIFO if (S_ISFIFO(statb.st_mode)) /* Named Pipe */ return 0; #endif if (S_ISDIR(statb.st_mode)) /* normal Directory */ return 1; } } return 0; } /* * Print sorted down columns */ void print_by_column(dir, items, count, no_file_suffix) register Char *dir, *items[]; int count, no_file_suffix; { register int i, r, c, w, maxwidth = 0, columns, rows; extern int Tty_raw_mode; lbuffed = 0; /* turn off line buffering */ for (i = 0; i < count; i++) /* find widest string */ maxwidth = max(maxwidth, Strlen(items[i])); maxwidth += no_file_suffix ? 1 : 2; /* for the file tag and space */ columns = (TermH + 1) / maxwidth; /* PWP: terminal size change */ if (!columns) columns = 1; rows = (count + (columns - 1)) / columns; for (r = 0; r < rows; r++) { for (c = 0; c < columns; c++) { i = c * rows + r; if (i < count) { w = Strlen(items[i]); if (no_file_suffix) { /* Print the command name */ xprintf("%s", short2str(items[i])); } else { /* Print filename followed by '/' or '*' or ' ' */ xprintf("%s%c", short2str(items[i]), filetype(dir, items[i])); w++; } if (c < (columns - 1)) /* Not last column? */ for (; w < maxwidth; w++) xputchar(' '); } } if (Tty_raw_mode) xputchar('\r'); xputchar('\n'); } lbuffed = 1; /* turn back on line buffering */ flush(); } int StrQcmp(str1, str2) register Char *str1, *str2; { for (; *str1 && (*str1 & TRIM) == (*str2 & TRIM); str1++, str2++); /* * The following case analysis is necessary so that characters which look * negative collate low against normal characters but high against the * end-of-string NUL. */ if (*str1 == '\0' && *str2 == '\0') return (0); else if (*str1 == '\0') return (-1); else if (*str2 == '\0') return (1); else return ((*str1 & TRIM) - (*str2 & TRIM)); } /* * For qsort() */ int fcompare(file1, file2) Char **file1, **file2; { #if defined(NLS) && !defined(NOSTRCOLL) char buf[2048]; (void) strcpy(buf, short2str(*file1)); return ((int) strcoll(buf, short2str(*file2))); #else return (StrQcmp(*file1, *file2)); #endif } /* * Concatenate src onto tail of des. * Des is a string whose maximum length is count. * Always null terminate. */ void catn(des, src, count) register Char *des, *src; register count; { while (--count >= 0 && *des) des++; while (--count >= 0) if ((*des++ = *src++) == 0) return; *des = '\0'; } /* * like strncpy but always leave room for trailing \0 * and always null terminate. */ void copyn(des, src, count) register Char *des, *src; register count; { while (--count >= 0) if ((*des++ = *src++) == 0) return; *des = '\0'; } static void tw_get_comm_list() { /* stolen from sh.exec.c dohash() */ register DIR *dirp; register struct dirent *dp; register Char *dir; register Char **pv; struct varent *v = adrof(STRpath); relatives_in_path = 0; /* set to false until we know better */ tw_clear_comm_list(); if (v == 0) /* if no path */ return; if (adrof(STRrecognize_only_executables)) { for (pv = v->vec; *pv; pv++) { if (pv[0][0] != '/') { relatives_in_path = 1; continue; } dirp = opendir(short2str(*pv)); if (dirp == NULL) continue; dir = Strspl(*pv, STRslash); while ((dp = readdir(dirp)) != NULL) { /* the call to executable() may make this a bit slow */ if (dp->d_ino != 0 && executable(dir, str2short(dp->d_name), 0)) tw_comm_name_add(str2short(dp->d_name)); } (void) closedir(dirp); xfree((ptr_t) dir); } } else { for (pv = v->vec; *pv; pv++) { if (pv[0][0] != '/') { relatives_in_path = 1; continue; } dirp = opendir(short2str(*pv)); if (dirp == NULL) continue; while ((dp = readdir(dirp)) != NULL) { if (dp->d_ino != 0) tw_comm_name_add(str2short(dp->d_name)); } (void) closedir(dirp); } } }