📄 complete.c
字号:
/**********Copyright 1990 Regents of the University of California. All rights reserved.Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group**********//* * Command completion code. We keep a data structure with information on each * command, to make lookups fast. We also keep NCLASSES (which is sort of * hardwired as 32) sets of keywords. Each command has an array of NARGS * bitmasks (also hardwired as 4), stating whether the command takes that * particular class of keywords in that position. Class 0 always means * filename completion. */#include "spice.h"#include "util.h"#include "cpdefs.h"#ifdef HAS_BSDDIRS#include <sys/types.h>#include <sys/dir.h>#else#ifdef HAS_SYSVDIRS#include <sys/types.h>#include <dirent.h>#ifndef direct#define direct dirent#endif#endif#endif#ifdef HAS_GETPW#include <pwd.h>#endif/* Be sure the ioctls get included in the following */#ifdef HAS_BSDTTY#include <sgtty.h>#else#ifdef HAS_SYSVTTY#include <termio.h>#else#ifdef HAS_POSIXTTY#include <termios.h>#endif#endif#endif#include "suffix.h"#define CNTRL_D '\004'#define ESCAPE '\033'#define NCLASSES 32#define NARGS 4static wordlist *ccfilec();static wordlist *ccmatch();static wordlist *cctowl();static void cdelete();static struct ccom *clookup();static struct ccom *getccom();static void printem();static void throwaway();bool cp_nocc; /* Don't do command completion. *//* The data structure for the commands is as follows: every node has a pointer * to its leftmost child, where the children of a node are those of which * the node is a prefix. This means that for a word like "ducks", there * must be nodes "d", "du", "duc", etc (which are all marked "invalid", * of course). This data structure is called a "trie". */struct ccom { char *cc_name; /* Command or keyword name. */ long cc_kwords[NARGS]; /* What this command takes. */ char cc_invalid; /* This node has been deleted. */ struct ccom *cc_child; /* Left-most child. */ struct ccom *cc_sibling;/* Right (alph. greater) sibling. */ struct ccom *cc_ysibling;/* Left (alph. less) sibling. */ struct ccom *cc_parent; /* Parent node. */} ;static struct ccom *commands = NULL; /* The available commands. */static struct ccom *keywords[NCLASSES]; /* Keywords. */#ifdef TIOCSTIvoidcp_ccom(wlist, buf, esc) wordlist *wlist; char *buf; bool esc;{ struct ccom *cc; wordlist *a, *pmatches = NULL; char wbuf[BSIZE_SP], *s; int i, j, arg; buf = cp_unquote(copy(buf)); cp_wstrip(buf); if (wlist->wl_next) { /* Not the first word. */ cc = getccom(wlist->wl_word); if (cc && cc->cc_invalid) cc = NULL; arg = wl_length(wlist) - 2; if (arg > 3) arg = 3; /* First filenames. */ if (cc && (cc->cc_kwords[arg] & 1)) { pmatches = ccfilec(buf); s = rindex(buf, '/'); i = strlen(s ? s + 1 : buf); if ((*buf == '~') && !index(buf, '/')) i--; } /* The keywords. */ for (j = 1; j < NCLASSES; j++) { if (cc && (cc->cc_kwords[arg] & (1 << j))) { /* Find all the matching keywords. */ a = ccmatch(buf, &keywords[j]); i = strlen(buf); if (pmatches) pmatches = wl_append(pmatches, a); else pmatches = a; } } wl_sort(pmatches); } else { pmatches = ccmatch(buf, &commands); i = strlen(buf); } if (!esc) { printem(pmatches); wl_free(pmatches); return; } if (pmatches == NULL) { (void) putchar('\07'); (void) fflush(cp_out); return; } if (pmatches->wl_next == NULL) { (void) strcpy(wbuf, &pmatches->wl_word[i]); goto found; } /* Now we know which words might work. Extend the command as much * as possible, then TIOCSTI the characters out. */ for (j = 0;; j++, i++) { wbuf[j] = pmatches->wl_word[i]; for (a = pmatches->wl_next; a; a = a->wl_next) if (a->wl_word[i] != wbuf[j]) { (void) putchar('\07'); (void) fflush(cp_out); wbuf[j] = '\0'; goto found; } if (wbuf[j] == '\0') goto found; }found: for (i = 0; wbuf[i]; i++) (void) ioctl(fileno(cp_in), TIOCSTI, &wbuf[i]); wl_free(pmatches); return;}/* Figure out what the command is, given the name. Returns NULL if there * is no such command in the command list. This is tricky, because we have * to do a preliminary history and alias parse. (Or at least we should.) */static struct ccom *getccom(first) char *first;{ struct alias *al; int ntries = 21; /* First look for aliases. Just interested in the first word... * Don't bother doing history yet -- that might get complicated. */ while (ntries-- > 0) { for (al = cp_aliases; al; al = al->al_next) if (eq(first, al->al_name)) { first = al->al_text->wl_word; break; } if (al == NULL) break; } if (ntries == 0) { fprintf(cp_err, "\nError: alias loop.\n"); return (NULL); } return (clookup(first, &commands, false, false));}/* Figure out what files match the prefix. */static wordlist *ccfilec(buf) char *buf;{ DIR *wdir; char *lcomp, *dir; struct direct *de; wordlist *wl = NULL, *t; struct passwd *pw; buf = copy(buf); /* Don't mangle anything... */ lcomp = rindex(buf, '/'); if (lcomp == NULL) { dir = "."; lcomp = buf; if (*buf == cp_til) { /* User name completion... */ buf++; while (pw = getpwent()) { if (prefix(buf, pw->pw_name)) { if (wl == NULL) { wl = alloc(struct wordlist); wl->wl_next = NULL; wl->wl_prev = NULL; } else { t = wl; wl = alloc(struct wordlist); wl->wl_prev = NULL; wl->wl_next = t; t->wl_prev = wl; } wl->wl_word = copy(pw->pw_name); } } (void) endpwent(); return (wl); } } else { dir = buf; *lcomp = '\0'; lcomp++; if (*dir == cp_til) { dir = cp_tildexpand(dir); if (dir == NULL) return (NULL); } } if (!(wdir = opendir(dir))) return (NULL); while (de = readdir(wdir)) if ((prefix(lcomp, de->d_name)) && (*lcomp || (*de->d_name != '.'))) { if (wl == NULL) { wl = alloc(struct wordlist); wl->wl_next = NULL; wl->wl_prev = NULL; } else { t = wl; wl = alloc(struct wordlist); wl->wl_next = t; t->wl_prev = wl; wl->wl_prev = NULL; } wl->wl_word = copy(de->d_name); } (void) closedir(wdir); wl_sort(wl); return (wl);}/* See what keywords or commands match the prefix. Check extra also for * matches, if it is non-NULL. Return a wordlist which is in alphabetical * order. Note that we have to call this once for each class. */static wordlist *ccmatch(word, dbase) char *word; struct ccom **dbase;{ wordlist *wl; register struct ccom *cc; cc = clookup(word, dbase, true, false); if (cc) { if (*word) /* This is a big drag. */ wl = cctowl(cc, false); else wl = cctowl(cc, true); } else wl = NULL; return (wl);}/* Print the words in the wordlist in columns. They are already sorted... * This is a hard thing to do with wordlists... */static voidprintem(wl) wordlist *wl;{ wordlist *ww; int maxl = 0, num, i, j, k, width = 79, ncols, nlines; (void) putchar('\n'); if (wl == NULL) { return; } num = wl_length(wl); for (ww = wl; ww; ww = ww->wl_next) { j = strlen(ww->wl_word); if (j > maxl) maxl = j; } if (++maxl % 8) maxl += 8 - (maxl % 8); ncols = width / maxl; if (ncols == 0) ncols = 1; nlines = num / ncols + (num % ncols ? 1 : 0); for (k = 0; k < nlines; k++) { for (i = 0; i < ncols; i++) { j = i * nlines + k; if (j < num) { fprintf(cp_out, "%-*s", maxl, wl_nthelem(j, wl)->wl_word); } else break; } (void) putchar('\n'); } return;}#else /* if not TIOCSTI */voidcp_ccom(wlist, buf, esc) wordlist *wlist; char *buf; bool esc;{ return;}#endifstatic wordlist *cctowl(cc, sib) struct ccom *cc; bool sib;{ wordlist *wl, *end; if (!cc) return (NULL); if (!cc->cc_invalid) { wl = alloc(struct wordlist); wl->wl_word = copy(cc->cc_name); wl->wl_prev = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -