📄 acd.c
字号:
name= allocate(name, (n*= 2) * sizeof(char)); getdesc(); } while (extalnum(dch)); } else { /* $* */ name[i++]= dch; getdesc(); } name[i++]= 0; name= allocate(name, i * sizeof(char)); tok->type= SUBST; tok->subst= newcell(); tok->subst->type= WORD; tok->subst->name= name; tok->subst= inc(tok->subst); return inc(tok);}typedef enum how { SUPERFICIAL, PARTIAL, FULL, EXPLODE, IMPLODE } how_t;cell_t *explode(cell_t *p, how_t how);cell_t *get_string(cell_t **pp)/* Get a string: A series of letters and substs. Special tokens '=', '+', '-' * and '*' are also recognized if on their own. A finished string is "exploded" * to a word if it consists of letters only. */{ cell_t *p= *pp, *s= nil, **ps= &s; int quoted= 0; while (p != nil) { switch (p->type) { case STRING: quoted= 1; dec(p); break; case EQUALS: case PLUS: case MINUS: case STAR: case SUBST: case LETTER: *ps= cons(STRING, p); ps= &(*ps)->cdr; break; default: goto got_string; } p= get_token(); } got_string: *pp= p; /* A single special token must be folded up. */ if (!quoted && s != nil && s->cdr == nil) { switch (s->car->type) { case EQUALS: case PLUS: case MINUS: case STAR: case SUBST: return go(s, s->car); } } /* Go over the string changing '=', '+', '-', '*' to letters. */ for (p= s; p != nil; p= p->cdr) { int c= 0; switch (p->car->type) { case EQUALS: c= '='; break; case PLUS: c= '+'; break; case MINUS: c= '-'; break; case STAR: c= '*'; break; } if (c != 0) { dec(p->car); p->car= newcell(); p->car->type= LETTER; p->car->letter= c; p->car= inc(p->car); } } return explode(s, SUPERFICIAL);}cell_t *get_list(cell_t **pp, type_t stop)/* Read a series of tokens upto a token of type "stop". */{ cell_t *p= *pp, *l= nil, **pl= &l; while (p != nil && p->type != stop && !(stop == EOLN && p->type == SEMI)) { switch (p->type) { case WHITE: case COMMENT: case SEMI: case EOLN: dec(p); p= get_token(); break; case OPEN: /* '(' words ')'. */ dec(p); p= get_token(); *pl= cons(CELL, get_list(&p, CLOSE)); pl= &(*pl)->cdr; dec(p); p= get_token(); break; case CLOSE: /* Unexpected closing parenthesis. (*/ fprintf(stderr, "\"%s\", line %u: unmatched ')'\n", descr, lineno); action= 0; dec(p); p= get_token(); break; case INPUT: case OUTPUT: *pl= cons(CELL, p); pl= &(*pl)->cdr; p= get_token(); break; case STRING: case EQUALS: case PLUS: case MINUS: case STAR: case LETTER: case SUBST: *pl= cons(CELL, get_string(&p)); pl= &(*pl)->cdr; break; default: assert(0); } } if (p == nil && stop == CLOSE) { /* Couldn't get the closing parenthesis. */ fprintf(stderr, "\"%s\", lines %u-%u: unmatched '('\n", /*)*/ descr, pc->lineno, lineno); action= 0; } *pp= p; return l;}program_t *get_line(cell_t *file){ program_t *l; cell_t *p; static keep_indent= 0; static unsigned old_indent= 0; /* Skip leading whitespace to determine the indentation level. */ indent= 0; while ((p= get_token()) != nil && p->type == WHITE) dec(p); if (p == nil) return nil; /* EOF */ if (p->type == EOLN) indent= old_indent; /* Empty line. */ /* Make a program line. */ pc= l= allocate(nil, sizeof(*l)); l->next= nil; l->file= inc(file); l->indent= keep_indent ? old_indent : indent; l->lineno= lineno; l->line= get_list(&p, EOLN); /* If the line ended in a semicolon then keep the indentation level. */ keep_indent= (p != nil && p->type == SEMI); old_indent= l->indent; dec(p); if (verbose >= 4) { if (l->line == nil) fputc('\n', stdout); else { printf("%*s", (int) l->indent, ""); prin2n(l->line); } } return l;}program_t *get_prog(void)/* Read the description file into core. */{ cell_t *file; program_t *prog, **ppg= &prog; descr= copystr(descr); if (descr[0] == '-' && descr[1] == 0) { /* -descr -: Read from standard input. */ deallocate(descr); descr= copystr("stdin"); dfp= stdin; } else { char *d= descr; if (*d == '.' && *++d == '.') d++; if (*d != '/') { /* -descr name: Read /usr/lib/<name>/descr. */ d= allocate(nil, sizeof(LIB) + (strlen(descr) + 7) * sizeof(*d)); sprintf(d, "%s/%s/descr", LIB, descr); deallocate(descr); descr= d; } if ((dfp= fopen(descr, "r")) == nil) fatal(descr); } file= findword(descr); deallocate(descr); descr= file->name; /* Preread the first character. */ dch= 0; lineno= 1; indent= 0; getdesc(); while ((*ppg= get_line(file)) != nil) ppg= &(*ppg)->next; if (dfp != stdin) (void) fclose(dfp); dec(file); return prog;}void makenames(cell_t ***ppr, cell_t *s, char **name, size_t i, size_t *n)/* Turn a string of letters and lists into words. A list denotes a choice * between several paths, like a search on $PATH. */{ cell_t *p, *q; size_t len; /* Simply add letters, skip empty lists. */ while (s != nil && (s->car == nil || s->car->type == LETTER)) { if (s->car != nil) { if (i == *n) *name= allocate(*name, (*n *= 2) * sizeof(**name)); (*name)[i++]= s->car->letter; } s= s->cdr; } /* If the end is reached then make a word out of the result. */ if (s == nil) { **ppr= cons(CELL, findnword(*name, i)); *ppr= &(**ppr)->cdr; return; } /* Elements of a list must be tried one by one. */ p= s->car; s= s->cdr; while (p != nil) { if (p->type == WORD) { q= p; p= nil; } else { assert(p->type == CELL); q= p->car; p= p->cdr; assert(q != nil); assert(q->type == WORD); } len= strlen(q->name); if (i + len > *n) *name= allocate(*name, (*n += i + len) * sizeof(**name)); memcpy(*name + i, q->name, len); makenames(ppr, s, name, i+len, n); }}int constant(cell_t *p)/* See if a string has been partially evaluated to a constant so that it * can be imploded to a word. */{ while (p != nil) { switch (p->type) { case CELL: case STRING: if (!constant(p->car)) return 0; p= p->cdr; break; case SUBST: return 0; default: return 1; } } return 1;}cell_t *evaluate(cell_t *p, how_t how);cell_t *explode(cell_t *s, how_t how)/* Explode a string with several choices to just one list of choices. */{ cell_t *t, *r= nil, **pr= &r; size_t i, n; char *name; struct stat st; if (how >= PARTIAL) { /* Evaluate the string, expanding substitutions. */ while (s != nil) { assert(s->type == STRING); t= inc(s->car); s= go(s, s->cdr); t= evaluate(t, how == IMPLODE ? EXPLODE : how); /* A list of one element becomes that element. */ if (t != nil && t->type == CELL && t->cdr == nil) t= go(t, t->car); /* Append the result, trying to flatten it. */ *pr= t; /* Find the end of what has just been added. */ while ((*pr) != nil) { *pr= append(STRING, *pr); pr= &(*pr)->cdr; } } s= r; } /* Is the result a simple string of constants? */ if (how <= PARTIAL && !constant(s)) return s; /* Explode the string to all possible choices, by now the string is * a series of characters, words and lists of words. */ r= nil; pr= &r; name= allocate(nil, (n= 16) * sizeof(char)); i= 0; makenames(&pr, s, &name, i, &n); deallocate(name); assert(r != nil); dec(s); s= r; /* "How" may specify that a choice must be made. */ if (how == IMPLODE) { if (s->cdr != nil) { /* More than one choice, find the file. */ do { assert(s->car->type == WORD); if (stat(s->car->name, &st) >= 0) return go(r, s->car); /* Found. */ } while ((s= s->cdr) != nil); } /* The first name is the default if nothing is found. */ return go(r, r->car); } /* If the result is a list of one word then return that word, otherwise * turn it into a string again unless this explode has been called * by another explode. (Exploding a string inside a string, the joys * of recursion.) */ if (s->cdr == nil) return go(s, s->car); return how >= EXPLODE ? s : cons(STRING, s);}void modify(cell_t **pp, cell_t *p, type_t mode)/* Add or remove the element p from the list *pp. */{ while (*pp != nil) { *pp= append(CELL, *pp); if ((*pp)->car == p) { /* Found it, if adding then exit, else remove. */ if (mode == PLUS) break; *pp= go(*pp, (*pp)->cdr); } else pp= &(*pp)->cdr; } if (*pp == nil && mode == PLUS) { /* Not found, add it. */ *pp= cons(CELL, p); } else dec(p);}int tainted(cell_t *p)/* A variable is tainted (must be substituted) if either it is marked as a * local variable, or some subst in its value is. */{ if (p == nil) return 0; switch (p->type) { case CELL: case STRING: return tainted(p->car) || tainted(p->cdr); case SUBST: return p->subst->flags & W_LOCAL || tainted(p->subst->value); default: return 0; }}cell_t *evaluate(cell_t *p, how_t how)/* Evaluate an expression, usually the right hand side of an assignment. */{ cell_t *q, *t, *r= nil, **pr= &r; type_t mode; if (p == nil) return nil; switch (p->type) { case CELL: break; /* see below */ case STRING: return explode(p, how); case SUBST: if (how >= FULL || tainted(p)) p= evaluate(go(p, p->subst->value), how); return p; case EQUALS: fprintf(stderr, "\"%s\", line %u: Can't do nested assignments\n", descr, pc->lineno); action= 0; dec(p); return nil; case LETTER: case WORD: case INPUT: case OUTPUT: case PLUS: case MINUS: return p; default: assert(0); } /* It's a list, see if there is a '*' there forcing a full expansion, * or a '+' or '-' forcing an implosive expansion. (Yeah, right.) * Otherwise evaluate each element. */ q = inc(p); while (p != nil) { if ((t= p->car) != nil) { if (t->type == STAR) { if (how < FULL) how= FULL; dec(q); *pr= evaluate(go(p, p->cdr), how); return r; } if (how>=FULL && (t->type == PLUS || t->type == MINUS)) break; } t= evaluate(inc(t), how); assert(p->type == CELL); p= go(p, p->cdr); if (how >= FULL) { /* Flatten the list. */ *pr= t; } else { /* Keep the nested list structure. */ *pr= cons(CELL, t); } /* Find the end of what has just been added. */ while ((*pr) != nil) { *pr= append(CELL, *pr); pr= &(*pr)->cdr; } } if (p == nil) { /* No PLUS or MINUS: done. */ dec(q); return r; } /* A PLUS or MINUS, reevaluate the original list implosively. */ if (how < IMPLODE) { dec(r); dec(p); return evaluate(q, IMPLODE); } dec(q); /* Execute the PLUSes and MINUSes. */ while (p != nil) { t= inc(p->car); p= go(p, p->cdr); if (t != nil && (t->type == PLUS || t->type == MINUS)) { /* Change the add/subtract mode. */ mode= t->type; dec(t); continue; } t= evaluate(t, IMPLODE); /* Add or remove all elements of t to/from r. */ while (t != nil) { if (t->type == CELL) { modify(&r, inc(t->car), mode); } else { modify(&r, t, mode); break; } t= go(t, t->cdr); } } return r;}/* An ACD program can be in three phases: Initialization (the first run * of the program), argument scanning, and compilation. */typedef enum phase { INIT, SCAN, COMPILE } phase_t;phase_t phase;typedef struct rule { /* Transformation rule. */ struct rule *next; char type; /* arg, transform, combine */ char flags; unsigned short npaths; /* Number of paths running through. */# define match from /* Arg matching strings. */ cell_t *from; /* Transformation source suffixe(s) */ cell_t *to; /* Destination suffix. */ cell_t *wait; /* Files waiting to be transformed. */ program_t *prog; /* Program to execute. */ struct rule *path; /* Transformation path. */} rule_t;typedef enum ruletype { ARG, PREFER, TRANSFORM, COMBINE } ruletype_t;#define R_PREFER 0x01 /* A preferred transformation. */rule_t *rules= nil;void newrule(ruletype_t type, cell_t *from, cell_t *to)/* Make a new rule cell. */{ rule_t *r= nil, **pr= &rules; /* See if there is a rule with the same suffixes, probably a matching * transform and prefer, or a re-execution of the same arg command. */ while ((r= *pr) != nil) { if (r->from == from && r->to == to) break; pr= &r->next; } if (*pr == nil) { /* Add a new rule. */ *pr= r= allocate(nil, sizeof(*r)); r->next= nil; r->type= type; r->flags= 0; r->from= r->to= r->wait= nil; r->path= nil; } if (type == TRANSFORM) r->type= TRANSFORM; if (type == PREFER) r->flags|= R_PREFER; if (type != PREFER) r->prog= pc; dec(r->from); r->from= from; dec(r->to); r->to= to;}int talk(void)/* True if verbose and if so indent what is to come. */{ if (verbose < 3) return 0; printf("%*s", (int) pc->indent, ""); return 1;}void unix_exec(cell_t *c)/* Execute the list of words p as a UNIX command. */{ cell_t *v, *a; int fd[2]; int *pf; char **argv; int i, n; int r, pid, status; if (action == 0) return; /* Error mode. */ if (talk() || verbose >= 2) prin2n(c); fd[0]= fd[1]= -1; argv= allocate(nil, (n= 16) * sizeof(*argv)); i= 0; /* Gather argv[] and scan for I/O redirection. */ for (v= c; v != nil; v= v->cdr) { a= v->car; pf= nil; if (a->type == INPUT) pf= &fd[0]; if (a->type == OUTPUT) pf= &fd[1]; if (pf == nil) { /* An argument. */ argv[i++]= a->name; if (i==n) argv= allocate(argv, (n*= 2) * sizeof(*argv)); continue; } /* I/O redirection. */ if ((v= v->cdr) == nil || (a= v->car)->type != WORD) { fprintf(stderr, "\"%s\", line %u: I/O redirection without a file\n", descr, pc->lineno); action= 0; if (v == nil) break; } if (*pf >= 0) close(*pf); if (action >= 2 && (*pf= open(a->name, pf == &fd[0] ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0 ) { report(a->name); action= 0; } } argv[i]= nil; if (i >= 0 && action > 0 && verbose == 1) { char *name= strrchr(argv[0], '/'); if (name == nil) name= argv[0]; else name++; printf("%s\n", name); } if (i >= 0 && action >= 2) { /* Really execute the command. */ fflush(stdout); switch (pid= fork()) { case -1: fatal("fork()"); case 0: if (fd[0] >= 0) { dup2(fd[0], 0); close(fd[0]); } if (fd[1] >= 0) { dup2(fd[1], 1); close(fd[1]); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -