📄 expand.c
字号:
struct strlist *sp; char *start; register char *p; char *q; char *ifs; start = string; if (ifslastp != NULL) { ifsp = &ifsfirst; do { p = string + ifsp->begoff; ifs = ifsp->nulonly? nullstr : ifsval(); while (p < string + ifsp->endoff) { q = p; if (*p == CTLESC) p++; if (strchr(ifs, *p++)) { if (q > start || *ifs != ' ') { *q = '\0'; sp = (struct strlist *)stalloc(sizeof *sp); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; } if (*ifs == ' ') { for (;;) { if (p >= string + ifsp->endoff) break; q = p; if (*p == CTLESC) p++; if (strchr(ifs, *p++) == NULL) { p = q; break; } } } start = p; } } } while ((ifsp = ifsp->next) != NULL); if (*start || (*ifs != ' ' && start > string)) { sp = (struct strlist *)stalloc(sizeof *sp); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; } } else { sp = (struct strlist *)stalloc(sizeof *sp); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; }}/* * Expand shell metacharacters. At this point, the only control characters * should be escapes. The results are stored in the list exparg. */char *expdir;STATIC voidexpandmeta(str) struct strlist *str; { char *p; struct strlist **savelastp; struct strlist *sp; char c; while (str) { if (fflag) goto nometa; p = str->text;#if UDIR if (p[0] == '/' && p[1] == 'u' && p[2] == '/') str->text = p = expudir(p);#endif#if TILDE if (p[0] == '~') str->text = p = expudir(p);#endif for (;;) { /* fast check for meta chars */ if ((c = *p++) == '\0') goto nometa; if (c == '*' || c == '?' || c == '[' || c == '!') break; } savelastp = exparg.lastp; INTOFF; if (expdir == NULL) expdir = ckmalloc(1024); /* I hope this is big enough */ expmeta(expdir, str->text); ckfree(expdir); expdir = NULL; INTON; if (exparg.lastp == savelastp) { if (! zflag) {nometa: *exparg.lastp = str; rmescapes(str->text); exparg.lastp = &str->next; } } else { *exparg.lastp = NULL; *savelastp = sp = expsort(*savelastp); while (sp->next != NULL) sp = sp->next; exparg.lastp = &sp->next; } str = str->next; }}#if UDIR || TILDE/* * UDIR: Expand /u/username into the home directory for the specified user. * TILDE: Expand ~username into the home directory for the specified user. * We hope not to use the getpw stuff here, because then we would have to load * in stdio and who knows what else. With networked password files there is * no choice alas. */#define MAXLOGNAME 32#define MAXPWLINE 128char *pfgets();STATIC char *expudir(path) char *path; { register char *p, *q, *r; char name[MAXLOGNAME]; char line[MAXPWLINE]; int i;#if USEGETPW struct passwd *pw;#endif r = path; /* result on failure */ p = r + (*r == '~' ? 1 : 3); /* the 1 skips "~", 3 skips "/u/" */ q = name; while (*p && *p != '/') { if (q >= name + MAXLOGNAME - 1) return r; /* fail, name too long */ *q++ = *p++; } *q = '\0';#if TILDE if (*name == 0 && *r == '~') { /* null name, use $HOME */ if ((q = lookupvar("HOME")) == NULL) return r; /* fail, home not set */ i = strlen(q); r = stalloc(i + strlen(p) + 1); scopy(q, r); scopy(p, r + i); TRACE(("expudir converts %s to %s\n", path, r)); didudir = 1; path = r; return r; }#endif#if !USEGETPW /* can do without the bloat */ setinputfile("/etc/passwd", 1); q = line + strlen(name); while (pfgets(line, MAXPWLINE) != NULL) { if (line[0] == name[0] && prefix(name, line) && *q == ':') { /* skip to start of home directory */ i = 4; do { while (*++q && *q != ':'); } while (--i > 0); if (*q == '\0') break; /* fail, corrupted /etc/passwd */ q++; for (r = q ; *r && *r != '\n' && *r != ':' ; r++); *r = '\0'; /* nul terminate home directory */ i = r - q; /* i = strlen(q) */ r = stalloc(i + strlen(p) + 1); scopy(q, r); scopy(p, r + i); TRACE(("expudir converts %s to %s\n", path, r)); didudir = 1; path = r; /* succeed */ break; } } popfile();#else if ((pw = getpwnam(name)) != NULL) { /* user exists */ q = pw->pw_dir; i = strlen(q); r = stalloc(i + strlen(p) + 1); scopy(q, r); scopy(p, r + i); TRACE(("expudir converts %s to %s\n", path, r)); didudir = 1; path = r; } endpwent();#endif /* USEGETPW */ return r;}#endif/* * Do metacharacter (i.e. *, ?, [...]) expansion. */STATIC voidexpmeta(enddir, name) char *enddir; char *name; { register char *p; char *q; char *start; char *endname; int metaflag; struct stat statb; DIR *dirp; struct dirent *dp; int atend; int matchdot; metaflag = 0; start = name; for (p = name ; ; p++) { if (*p == '*' || *p == '?') metaflag = 1; else if (*p == '[') { q = p + 1; if (*q == '!') q++; for (;;) { if (*q == CTLESC) q++; if (*q == '/' || *q == '\0') break; if (*++q == ']') { metaflag = 1; break; } } } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { metaflag = 1; } else if (*p == '\0') break; else if (*p == CTLESC) p++; if (*p == '/') { if (metaflag) break; start = p + 1; } } if (metaflag == 0) { /* we've reached the end of the file name */ if (enddir != expdir) metaflag++; for (p = name ; ; p++) { if (*p == CTLESC) p++; *enddir++ = *p; if (*p == '\0') break; } if (metaflag == 0 || stat(expdir, &statb) >= 0) addfname(expdir); return; } endname = p; if (start != name) { p = name; while (p < start) { if (*p == CTLESC) p++; *enddir++ = *p++; } } if (enddir == expdir) { p = "."; } else if (enddir == expdir + 1 && *expdir == '/') { p = "/"; } else { p = expdir; enddir[-1] = '\0'; } if ((dirp = opendir(p)) == NULL) return; if (enddir != expdir) enddir[-1] = '/'; if (*endname == 0) { atend = 1; } else { atend = 0; *endname++ = '\0'; } matchdot = 0; if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') matchdot++; while (! int_pending() && (dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.' && ! matchdot) continue; if (patmatch(start, dp->d_name)) { if (atend) { scopy(dp->d_name, enddir); addfname(expdir); } else { char *q; for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); p[-1] = '/'; expmeta(p, endname); } } } closedir(dirp); if (! atend) endname[-1] = '/';}/* * Add a file name to the list. */STATIC voidaddfname(name) char *name; { char *p; struct strlist *sp; p = stalloc(strlen(name) + 1); scopy(name, p); sp = (struct strlist *)stalloc(sizeof *sp); sp->text = p; *exparg.lastp = sp; exparg.lastp = &sp->next;}/* * Sort the results of file name expansion. It calculates the number of * strings to sort and then calls msort (short for merge sort) to do the * work. */STATIC struct strlist *expsort(str) struct strlist *str; { int len; struct strlist *sp; len = 0; for (sp = str ; sp ; sp = sp->next) len++; return msort(str, len);}STATIC struct strlist *msort(list, len) struct strlist *list; { struct strlist *p, *q; struct strlist **lpp; int half; int n; if (len <= 1) return list; half = len >> 1; p = list; for (n = half ; --n >= 0 ; ) { q = p; p = p->next; } q->next = NULL; /* terminate first half of list */ q = msort(list, half); /* sort first half of list */ p = msort(p, len - half); /* sort second half */ lpp = &list; for (;;) { if (strcmp(p->text, q->text) < 0) { *lpp = p; lpp = &p->next; if ((p = *lpp) == NULL) { *lpp = q; break; } } else { *lpp = q; lpp = &q->next; if ((q = *lpp) == NULL) { *lpp = p; break; } } } return list;}/* * Returns true if the pattern matches the string. */intpatmatch(pattern, string) char *pattern; char *string; { if (pattern[0] == '!' && pattern[1] == '!') return 1 - pmatch(pattern + 2, string); else return pmatch(pattern, string);}STATIC intpmatch(pattern, string) char *pattern; char *string; { register char *p, *q; register char c; p = pattern; q = string; for (;;) { switch (c = *p++) { case '\0': goto breakloop; case CTLESC: if (*q++ != *p++) return 0; break; case '?': if (*q++ == '\0') return 0; break; case '*': c = *p; if (c != CTLESC && c != '?' && c != '*' && c != '[') { while (*q != c) { if (*q == '\0') return 0; q++; } } do { if (pmatch(p, q)) return 1; } while (*q++ != '\0'); return 0; case '[': { char *endp; int invert, found; char chr; endp = p; if (*endp == '!') endp++; for (;;) { if (*endp == '\0') goto dft; /* no matching ] */ if (*endp == CTLESC) endp++; if (*++endp == ']') break; } invert = 0; if (*p == '!') { invert++; p++; } found = 0; chr = *q++; c = *p++; do { if (c == CTLESC) c = *p++; if (*p == '-' && p[1] != ']') { p++; if (*p == CTLESC) p++; if (chr >= c && chr <= *p) found = 1; p++; } else { if (chr == c) found = 1; } } while ((c = *p++) != ']'); if (found == invert) return 0; break; }dft: default: if (*q++ != c) return 0; break; } }breakloop: if (*q != '\0') return 0; return 1;}/* * Remove any CTLESC characters from a string. */voidrmescapes(str) char *str; { register char *p, *q; p = str; while (*p != CTLESC) { if (*p++ == '\0') return; } q = p; while (*p) { if (*p == CTLESC) p++; *q++ = *p++; } *q = '\0';}/* * See if a pattern matches in a case statement. */intcasematch(pattern, val) union node *pattern; char *val; { struct stackmark smark; int result; char *p; setstackmark(&smark); argbackq = pattern->narg.backquote; STARTSTACKSTR(expdest); ifslastp = NULL; /* Preserve any CTLESC characters inserted previously, so that we won't expand reg exps which are inside strings. */ argstr(pattern->narg.text, 1); STPUTC('\0', expdest); p = grabstackstr(expdest); result = patmatch(p, val); popstackmark(&smark); return result;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -