📄 acd.c
字号:
if (n != 1) { key_usage(""); return; } if (phase != INIT) { inappropriate(); return; } if (talk()) prin2n(p); argscan(); if (p->car->name[0] == 'c') compile(); nextpc= next; } else { /* A UNIX command. */ t= evaluate(inc(pc->line), IMPLODE); unix_exec(t); dec(t); }}void execute(exec_t how, unsigned indent)/* Execute (or skip) all lines with at least the given indent. */{ int work= 0; /* Need to execute at least one line. */ unsigned firstline; unsigned nice_indent= 0; /* 0 = Don't know what's nice yet. */ if (pc == nil) return; /* End of program. */ firstline= pc->lineno; if (how == DONT) { /* Skipping a body, but is there another guard? */ pc= pc->next; if (pc != nil && pc->indent < indent && pc->line != nil) { /* There is one! Bail out, then it get's executed. */ return; } } else { /* Skip lines with a lesser indentation, they are guards for * the same substatements. Don't go past empty lines. */ while (pc != nil && pc->indent < indent && pc->line != nil) pc= pc->next; } /* Execute all lines with an indentation of at least "indent". */ while (pc != nil && pc->indent >= indent) { if (pc->indent != nice_indent && how == DOIT) { if (nice_indent != 0) { fprintf(stderr, "\"%s\", line %u: (warning) sudden indentation shift\n", descr, pc->lineno); } nice_indent= pc->indent; } nextpc= pc->next; if (how == DOIT) exec_one(); pc= nextpc; work= 1; } if (indent > 0 && !work) { fprintf(stderr, "\"%s\", line %u: empty body, no statements\n", descr, firstline); action= 0; }}int argmatch(int shift, cell_t *match, cell_t *match1, char *arg1)/* Try to match an arg rule to the input file list L_args. Execute the arg * body (pc is set to it) on success. */{ cell_t *oldval, *v; int m, oldflags; size_t i, len; int minus= 0; if (shift) { /* An argument has been accepted and may be shifted to $*. */ cell_t **oldpstar= pV_star; *pV_star= L_args; L_args= *(pV_star= &L_args->cdr); *pV_star= nil; if (argmatch(0, match->cdr, nil, nil)) return 1; /* Undo the damage. */ *pV_star= L_args; L_args= *(pV_star= oldpstar); *pV_star= nil; return 0; } if (match == nil) { /* A full match, execute the arg body. */ /* Enable $>. */ V_out->flags= W_SET|W_LOCAL; if (verbose >= 3) { prin2(pc->line); printf(" =\b=\b= "); prin2n(V_star->value); } execute(DOIT, pc->indent+1); /* Append $> to the file list. */ if (V_out->value != nil) { *pL_files= cons(CELL, V_out->value); pL_files= &(*pL_files)->cdr; } /* Disable $>. */ V_out->value= nil; V_out->flags= W_SET|W_LOCAL|W_RDONLY; return 1; } if (L_args == nil) return 0; /* Out of arguments to match. */ /* Match is a list of words, substs and strings containing letters and * substs. Match1 is the current element of the first element of match. * Arg1 is the current character of the first element of L_args. */ if (match1 == nil) { /* match1 is at the end of a string, then arg1 must also. */ if (arg1 != nil) { if (*arg1 != 0) return 0; return argmatch(1, match, nil, nil); } /* If both are nil: Initialize. */ match1= match->car; arg1= L_args->car->name; /* A subst may not match a leading '-'. */ if (arg1[0] == '-') minus= 1; } if (match1->type == WORD && strcmp(match1->name, arg1) == 0) { /* A simple match of an argument. */ return argmatch(1, match, nil, nil); } if (match1->type == SUBST && !minus) { /* A simple match of a subst. */ /* The variable gets the first of the arguments as its value. */ v= match1->subst; if (v->flags & W_RDONLY) return 0; /* ouch */ oldflags= v->flags; v->flags= W_SET|W_LOCAL|W_RDONLY; oldval= v->value; v->value= inc(L_args->car); m= argmatch(1, match, nil, nil); /* Recover the value of the variable. */ dec(v->value); v->flags= oldflags; v->value= oldval; return m; } if (match1->type != STRING) return 0; /* Match the first item in the string. */ if (match1->car == nil) return 0; if (match1->car->type == LETTER && match1->car->letter == (unsigned char) *arg1) { /* A letter matches, try the rest of the string. */ return argmatch(0, match, match1->cdr, arg1+1); } /* It can only be a subst in a string now. */ len= strlen(arg1); if (match1->car->type != SUBST || minus || len == 0) return 0; /* The variable can match from 1 character to all of the argument. * Matching as few characters as possible happens to be the Right Thing. */ v= match1->car->subst; if (v->flags & W_RDONLY) return 0; /* ouch */ oldflags= v->flags; v->flags= W_SET|W_LOCAL|W_RDONLY; oldval= v->value; m= 0; for (i= match1->cdr == nil ? len : 1; !m && i <= len; i++) { v->value= findnword(arg1, i); m= argmatch(0, match, match1->cdr, arg1+i); dec(v->value); } /* Recover the value of the variable. */ v->flags= oldflags; v->value= oldval; return m;}void argscan(void)/* Match all the arguments to the arg rules, those that don't match are * used as files for transformation. */{ rule_t *rule; int m; phase= SCAN; /* Process all the arguments. */ while (L_args != nil) { pV_star= &V_star->value; /* Try all the arg rules. */ m= 0; for (rule= rules; !m && rule != nil; rule= rule->next) { if (rule->type != ARG) continue; pc= rule->prog; m= argmatch(0, rule->match, nil, nil); } dec(V_star->value); V_star->value= nil; /* On failure, add the first argument to the list of files. */ if (!m) { *pL_files= L_args; L_args= *(pL_files= &L_args->cdr); *pL_files= nil; } } phase= INIT;}int member(cell_t *p, cell_t *l)/* True if p is a member of list l. */{ while (l != nil && l->type == CELL) { if (p == l->car) return 1; l= l->cdr; } return p == l;}long basefind(cell_t *f, cell_t *l)/* See if f has a suffix in list l + set the base name of f. * -1 if not found, preference number for a short basename otherwise. */{ cell_t *suff; size_t blen, slen; char *base; /* Determine base name of f, with suffix. */ if ((base= strrchr(f->name, '/')) == nil) base= f->name; else base++; blen= strlen(base); /* Try suffixes. */ while (l != nil) { if (l->type == CELL) { suff= l->car; l= l->cdr; } else { suff= l; l= nil; } if (f->flags & W_SUFF) { /* F has a suffix imposed on it. */ if (f->suffix == suff) return 0; continue; } slen= strlen(suff->name); if (slen < blen && strcmp(base+blen-slen, suff->name) == 0) { /* Got it! */ dec(f->base); f->base= findnword(base, blen-slen); return 10000L * (blen - slen); } } return -1;}#define NO_PATH 2000000000 /* No path found yet. */long shortest; /* Length of the shortest path as yet. */rule_t *findpath(long depth, int seek, cell_t *file, rule_t *start)/* Find the path of the shortest transformation to the stop suffix. */{ rule_t *rule; if (action == 0) return nil; if (start == nil) { /* No starting point defined, find one using "file". */ for (rule= rules; rule != nil; rule= rule->next) { if (rule->type < TRANSFORM) continue; if ((depth= basefind(file, rule->from)) >= 0) { if (findpath(depth, seek, nil, rule) != nil) return rule; } } return nil; } /* Cycle? */ if (start->path != nil) { /* We can't have cycles through combines. */ if (start->type == COMBINE) { fprintf(stderr, "\"%s\": contains a combine-combine cycle\n", descr); action= 0; } return nil; } /* Preferred transformations are cheap. */ if (start->flags & R_PREFER) depth-= 100; /* Try to go from start closer to the stop suffix. */ for (rule= rules; rule != nil; rule= rule->next) { if (rule->type < TRANSFORM) continue; if (member(start->to, rule->from)) { start->path= rule; rule->npaths++; if (findpath(depth+1, seek, nil, rule) != nil) return start; start->path= nil; rule->npaths--; } } if (V_stop == nil) { fprintf(stderr, "\"%s\": no stop suffix has been defined\n", descr); action= 0; return nil; } /* End of the line? */ if (start->to == V_stop) { /* Got it. */ if (seek) { /* Second hunt, do we find the shortest? */ if (depth == shortest) return start; } else { /* Is this path shorter than the last one? */ if (depth < shortest) shortest= depth; } } return nil; /* Fail. */}void transform(rule_t *rule)/* Transform the file(s) connected to the rule according to the rule. */{ cell_t *file, *in, *out; char *base; /* Let $* be the list of input files. */ while (rule->wait != nil) { file= rule->wait; rule->wait= file->cdr; file->cdr= V_star->value; V_star->value= file; } /* Set $< to the basename of the first input file. */ file= file->car; V_in->value= in= inc(file->flags & W_SUFF ? file : file->base); file->flags&= ~W_SUFF; /* Set $> to the output file name of the transformation. */ out= newcell(); out->type= WORD; base= rule->path == nil ? in->name : maketemp(); out->name= allocate(nil, (strlen(base)+strlen(rule->to->name)+1) * sizeof(*out->name)); strcpy(out->name, base); if (rule->path == nil || strchr(rule->to->name, '/') == nil) strcat(out->name, rule->to->name); out= inc(out); if (rule->path != nil) out->flags|= W_TEMP; V_out->value= out; V_out->flags= W_SET|W_LOCAL; /* Do a transformation. (Finally) */ if (verbose >= 3) { printf("%s ", rule->type==TRANSFORM ? "transform" : "combine"); prin2(V_star->value); printf(" %s\n", out->name); } pc= rule->prog; execute(DOIT, pc->indent+1); /* Hand $> over to the next rule, it must be a single word. */ out= evaluate(V_out->value, IMPLODE); if (wordlist(&out, 1) != 1) { fprintf(stderr, "\"%s\", line %u: $> should be returned as a single word\n", descr, rule->prog->lineno); action= 0; } if ((rule= rule->path) != nil) { /* There is a next rule. */ dec(out->base); out->base= in; /* Basename of input file. */ file= inc(newcell()); file->car= out; file->cdr= rule->wait; rule->wait= file; } else { dec(in); dec(out); } /* Undo the damage to $*, $<, and $>. */ dec(V_star->value); V_star->value= nil; V_in->value= nil; V_out->value= nil; V_out->flags= W_SET|W_LOCAL|W_RDONLY;}void compile(void){ rule_t *rule; cell_t *file, *t; phase= COMPILE; /* Implode the files list. */ L_files= evaluate(L_files, IMPLODE); if (wordlist(&L_files, 0) < 0) { fprintf(stderr, "\"%s\": An assignment to $> contained junk\n", descr); action= 0; } while (action != 0 && L_files != nil) { file= L_files->car; /* Initialize. */ shortest= NO_PATH; for (rule= rules; rule != nil; rule= rule->next) rule->path= nil; /* Try all possible transformation paths. */ (void) findpath(0L, 0, file, nil); if (shortest == NO_PATH) { /* Can't match the file. */ fprintf(stderr, "%s: %s: can't compile, no transformation applies\n", program, file->name); action= 0; return; } /* Find the first short path. */ if ((rule= findpath(0L, 1, file, nil)) == nil) return; /* Transform the file until you hit a combine. */ t= inc(newcell()); t->car= inc(file); L_files= go(L_files, L_files->cdr); t->cdr= rule->wait; rule->wait= t; while (action != 0 && rule != nil && rule->type != COMBINE) { transform(rule); rule= rule->path; } } /* All input files have been transformed to combine rule(s). Now * we need to find the combine rule with the least number of paths * running through it (this combine may be followed by another) and * transform from there. */ while (action != 0) { int least; rule_t *comb= nil; for (rule= rules; rule != nil; rule= rule->next) { rule->path= nil; if (rule->type != COMBINE || rule->wait == nil) continue; if (comb == nil || rule->npaths < least) { least= rule->npaths; comb= rule; } } /* No combine? Then we're done. */ if (comb == nil) break; /* Initialize. */ shortest= NO_PATH; /* Try all possible transformation paths. */ (void) findpath(0L, 0, nil, comb); if (shortest == NO_PATH) break; /* Find the first short path. */ if ((rule= findpath(0L, 1, nil, comb)) == nil) return; /* Transform until you hit another combine. */ do { transform(rule); rule= rule->path; } while (action != 0 && rule != nil && rule->type != COMBINE); } phase= INIT;}cell_t *predef(char *var, char *val)/* A predefined variable var with value val, or a special variable. */{ cell_t *p, *t; p= findword(var); if (val != nil) { /* Predefined. */ t= findword(val); dec(p->value); p->value= t; p->flags|= W_SET; if (verbose >= 3) { prin1(p); printf(" =\b=\b= "); prin2n(t); } } else { /* Special: $* and such. */ p->flags= W_SET|W_LOCAL|W_RDONLY; } t= inc(newcell()); t->car= p; t->cdr= L_predef; L_predef= t; return p;}void usage(void){ fprintf(stderr, "Usage: %s -v<n> -vn<n> -name <name> -descr <descr> -T <dir> ...\n", program); exit(-1);}int main(int argc, char **argv){ program_t *prog; cell_t **pa; int i; /* Call name of the program, decides which description to use. */ if ((program= strrchr(argv[0], '/')) == nil) program= argv[0]; else program++; /* Directory for temporary files. */ if ((template= getenv("TMPDIR")) == nil || *template == 0) template= "/tmp"; /* Transform arguments to a list, processing the few ACD options. */ pa= &L_args; for (i= 1; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] == 'v') { char *a= argv[i]+2; if (*a == 'n') { a++; action= 1; } verbose= 2; if (*a != 0) { verbose= strtoul(a, &a, 10); if (*a != 0) usage(); } } else if (strcmp(argv[i], "-name") == 0) { if (++i == argc) usage(); program= argv[i]; } else if (strcmp(argv[i], "-descr") == 0) { if (++i == argc) usage(); descr= argv[i]; } else if (argv[i][0] == '-' && argv[i][1] == 'T') { if (argv[i][2] == 0) { if (++i == argc) usage(); template= argv[i]; } else template= argv[i]+2; } else { /* Any other argument must be processed. */ *pa= cons(CELL, findword(argv[i])); pa= &(*pa)->cdr; } }#ifndef DESCR /* Default description file is based on the program name. */ if (descr == nil) descr= program;#else /* Default description file is predefined. */ if (descr == nil) descr= DESCR;#endif template= copystr(template); /* Catch user signals. */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, interrupt); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, interrupt); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, interrupt); /* Predefined or special variables. */ predef("PROGRAM", program); predef("VERSION", version);#ifdef ARCH predef("ARCH", ARCH); /* Cross-compilers like this. */#endif V_star= predef("*", nil); V_in= predef("<", nil); V_out= predef(">", nil); /* Read the description file. */ if (verbose >= 3) printf("include %s\n", descr); prog= get_prog(); phase= INIT; pc= prog; execute(DOIT, 0); argscan(); compile(); /* Delete all allocated data to test inc/dec balance. */ while (prog != nil) { program_t *junk= prog; prog= junk->next; dec(junk->file); dec(junk->line); deallocate(junk); } while (rules != nil) { rule_t *junk= rules; rules= junk->next; dec(junk->from); dec(junk->to); dec(junk->wait); deallocate(junk); } deallocate(template); dec(V_stop); dec(L_args); dec(L_files); dec(L_predef); quit(action == 0 ? 1 : 0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -