00001
00002
00003
00004
00005 char version[] = "1.9";
00006
00007 #define nil 0
00008 #define _POSIX_SOURCE 1
00009 #include <sys/types.h>
00010 #include <stdio.h>
00011 #include <stddef.h>
00012 #include <stdlib.h>
00013 #include <unistd.h>
00014 #include <fcntl.h>
00015 #include <string.h>
00016 #include <signal.h>
00017 #include <errno.h>
00018 #include <ctype.h>
00019 #include <assert.h>
00020 #include <sys/stat.h>
00021 #include <sys/wait.h>
00022
00023 #ifndef LIB
00024 #define LIB "/usr/lib"
00025 #endif
00026
00027 #define arraysize(a) (sizeof(a) / sizeof((a)[0]))
00028 #define arraylimit(a) ((a) + arraysize(a))
00029
00030 char *program;
00031
00032 int verbose= 0;
00033
00034
00035
00036
00037
00038
00039 int action= 2;
00040
00041
00042
00043
00044 void report(char *label)
00045 {
00046 if (label == nil || label[0] == 0) {
00047 fprintf(stderr, "%s: %s\n", program, strerror(errno));
00048 } else {
00049 fprintf(stderr, "%s: %s: %s\n",
00050 program, label, strerror(errno));
00051 }
00052 action= 0;
00053 }
00054
00055 void quit(int exit_code);
00056
00057 void fatal(char *label)
00058 {
00059 report(label);
00060 quit(-1);
00061 }
00062
00063 size_t heap_chunks= 0;
00064
00065 void *allocate(void *mem, size_t size)
00066
00067
00068
00069
00070 {
00071 assert(size > 0);
00072
00073 if (mem != nil) {
00074 mem= realloc(mem, size);
00075 } else {
00076 mem= malloc(size);
00077 heap_chunks++;
00078 }
00079 if (mem == nil) fatal(nil);
00080 return mem;
00081 }
00082
00083 void deallocate(void *mem)
00084 {
00085 if (mem != nil) {
00086 free(mem);
00087 heap_chunks--;
00088 }
00089 }
00090
00091 char *copystr(const char *s)
00092 {
00093 char *c;
00094 c= allocate(nil, (strlen(s)+1) * sizeof(*c));
00095 strcpy(c, s);
00096 return c;
00097 }
00098
00099
00100 typedef struct cell {
00101 unsigned short refc;
00102 char type;
00103 unsigned char letter;
00104 char *name;
00105 struct cell *hash;
00106 struct cell *car, *cdr;
00107
00108
00109 # define value car
00110 # define base cdr
00111 # define suffix cdr
00112 # define flags letter
00113
00114
00115 # define subst car
00116
00117 } cell_t;
00118
00119 typedef enum type {
00120 CELL,
00121 STRING,
00122 SUBST,
00123
00124 LETTER,
00125 WORD,
00126 EQUALS,
00127 OPEN,
00128 CLOSE,
00129 PLUS,
00130 MINUS,
00131 STAR,
00132 INPUT,
00133 OUTPUT,
00134 WHITE,
00135 COMMENT,
00136 SEMI,
00137 EOLN,
00138 N_TYPES
00139 } type_t;
00140
00141 #define is_unique(type) ((type) >= LETTER)
00142
00143
00144 #define W_SET 0x01
00145 #define W_RDONLY 0x02
00146 #define W_LOCAL 0x04
00147 #define W_TEMP 0x08
00148 #define W_SUFF 0x10
00149
00150 void princhar(int c)
00151
00152 {
00153 if (strchr("\\'\"<>();~$^&*|{}[]?", c) != nil) fputc('\\', stdout);
00154 putchar(c);
00155 }
00156
00157 void prinstr(char *s)
00158
00159 {
00160 int q= 0;
00161 char *s2= s;
00162
00163 while (*s2 != 0)
00164 if (strchr("~`$^&*()=\\|[]{};'\"<>?", *s2++) != nil) q= 1;
00165
00166 if (q) fputc('"', stdout);
00167 while (*s != 0) princhar(*s++);
00168 if (q) fputc('"', stdout);
00169 }
00170
00171 void prin2(cell_t *p);
00172
00173 void prin1(cell_t *p)
00174
00175 {
00176 if (p == nil) {
00177 printf("(\b(\b()\b)\b)");
00178 return;
00179 }
00180
00181 switch (p->type) {
00182 case CELL:
00183 printf("(\b(\b(");
00184 prin2(p);
00185 printf(")\b)\b)");
00186 break;
00187 case STRING:
00188 printf("\"\b\"\b\"");
00189 prin2(p);
00190 printf("\"\b\"\b\"");
00191 break;
00192 case SUBST:
00193 printf("$\b$\b${%s}", p->subst->name);
00194 break;
00195 case LETTER:
00196 princhar(p->letter);
00197 break;
00198 case WORD:
00199 prinstr(p->name);
00200 break;
00201 case EQUALS:
00202 printf("=\b=\b=");
00203 break;
00204 case PLUS:
00205 printf("+\b+\b+");
00206 break;
00207 case MINUS:
00208 printf("-\b-\b-");
00209 break;
00210 case STAR:
00211 printf("*\b*\b*");
00212 break;
00213 case INPUT:
00214 printf(verbose >= 3 ? "<\b<\b<" : "<");
00215 break;
00216 case OUTPUT:
00217 printf(verbose >= 3 ? ">\b>\b>" : ">");
00218 break;
00219 default:
00220 assert(0);
00221 }
00222 }
00223
00224 void prin2(cell_t *p)
00225
00226 {
00227 while (p != nil && p->type <= STRING) {
00228 prin1(p->car);
00229
00230 if (p->type == CELL && p->cdr != nil) fputc(' ', stdout);
00231
00232 p= p->cdr;
00233 }
00234 if (p != nil) prin1(p);
00235 }
00236
00237 void prin1n(cell_t *p) { prin1(p); fputc('\n', stdout); }
00238
00239 void prin2n(cell_t *p) { prin2(p); fputc('\n', stdout); }
00240
00241
00242 typedef struct program {
00243 struct program *next;
00244 cell_t *file;
00245 unsigned indent;
00246 unsigned lineno;
00247 cell_t *line;
00248 } program_t;
00249
00250 program_t *pc;
00251 program_t *nextpc;
00252
00253 cell_t *oldcells;
00254
00255 cell_t *newcell(void)
00256
00257 {
00258 cell_t *p;
00259
00260 if (oldcells != nil) {
00261 p= oldcells;
00262 oldcells= p->cdr;
00263 heap_chunks++;
00264 } else {
00265 p= allocate(nil, sizeof(*p));
00266 }
00267
00268 p->refc= 0;
00269 p->type= CELL;
00270 p->letter= 0;
00271 p->name= nil;
00272 p->car= nil;
00273 p->cdr= nil;
00274 return p;
00275 }
00276
00277 #define N_CHARS (1 + (unsigned char) -1)
00278 #define HASHDENSE 0x400
00279
00280 cell_t *oblist[HASHDENSE + N_CHARS + N_TYPES];
00281
00282 unsigned hashfun(cell_t *p)
00283
00284 {
00285 unsigned h;
00286 char *name;
00287
00288 switch (p->type) {
00289 case WORD:
00290 h= 0;
00291 name= p->name;
00292 while (*name != 0) h= (h * 0x1111) + *name++;
00293 return h % HASHDENSE;
00294 case LETTER:
00295 return HASHDENSE + p->letter;
00296 default:
00297 return HASHDENSE + N_CHARS + p->type;
00298 }
00299 }
00300
00301 cell_t *search(cell_t *p, cell_t ***hook)
00302
00303
00304
00305 {
00306 cell_t *sp;
00307
00308 sp= *(*hook= &oblist[hashfun(p)]);
00309
00310 if (p->type == WORD) {
00311
00312 int cmp= 0;
00313
00314 while (sp != nil && (cmp= strcmp(p->name, sp->name)) > 0)
00315 sp= *(*hook= &sp->hash);
00316
00317 if (cmp != 0) sp= nil;
00318 }
00319 return sp;
00320 }
00321
00322 void dec(cell_t *p)
00323
00324 {
00325 if (p == nil || --p->refc > 0) return;
00326
00327 if (is_unique(p->type)) {
00328
00329 cell_t *o, **hook;
00330
00331 o= search(p, &hook);
00332
00333 if (o == p) {
00334
00335 *hook= p->hash;
00336 p->hash= nil;
00337 }
00338
00339 if (p->type == WORD && (p->flags & W_TEMP)) {
00340
00341 if (verbose >= 2) {
00342 printf("rm -f ");
00343 prinstr(p->name);
00344 fputc('\n', stdout);
00345 }
00346 if (unlink(p->name) < 0 && errno != ENOENT)
00347 report(p->name);
00348 }
00349 }
00350 deallocate(p->name);
00351 dec(p->car);
00352 dec(p->cdr);
00353 p->cdr= oldcells;
00354 oldcells= p;
00355 heap_chunks--;
00356 }
00357
00358 cell_t *inc(cell_t *p)
00359
00360 {
00361 cell_t *o, **hook;
00362
00363 if (p == nil) return nil;
00364
00365 if (++p->refc > 1 || !is_unique(p->type)) return p;
00366
00367
00368 o= search(p, &hook);
00369
00370 if (o == nil) {
00371
00372 p->hash= *hook;
00373 *hook= p;
00374 } else {
00375
00376 o->refc++;
00377 dec(p);
00378 p= o;
00379 }
00380 return p;
00381 }
00382
00383 cell_t *go(cell_t *p, cell_t *field)
00384
00385 {
00386 field= inc(field);
00387 dec(p);
00388 return field;
00389 }
00390
00391 cell_t *cons(type_t type, cell_t *p)
00392
00393 {
00394 cell_t *l= newcell();
00395 l->type= type;
00396 l->refc++;
00397 l->car= p;
00398 return l;
00399 }
00400
00401 cell_t *append(type_t type, cell_t *p)
00402
00403 {
00404 return p == nil || p->type == type ? p : cons(type, p);
00405 }
00406
00407 cell_t *findnword(char *name, size_t n)
00408
00409 {
00410 cell_t *w= newcell();
00411 w->type= WORD;
00412 w->name= allocate(nil, (n+1) * sizeof(*w->name));
00413 memcpy(w->name, name, n);
00414 w->name[n]= 0;
00415 return inc(w);
00416 }
00417
00418 cell_t *findword(char *name)
00419
00420 {
00421 return findnword(name, strlen(name));
00422 }
00423
00424 void quit(int exstat)
00425
00426 {
00427 cell_t **op, *p, *v, *b;
00428 size_t chunks;
00429
00430
00431 for (op= oblist; op < oblist + HASHDENSE; op++) {
00432 p= *op;
00433 while (p != nil) {
00434 if (p->value != nil || p->base != nil) {
00435 v= p->value;
00436 b= p->base;
00437 p->value= nil;
00438 p->base= nil;
00439 p= *op;
00440 dec(v);
00441 dec(b);
00442 } else {
00443 p= p->hash;
00444 }
00445 }
00446 }
00447 chunks= heap_chunks;
00448
00449
00450 for (op= oblist; op < oblist + HASHDENSE; op++) {
00451
00452 while (*op != nil) { (*op)->refc= 1; dec(*op); }
00453 }
00454
00455 if (exstat != -1 && chunks > 0) {
00456 fprintf(stderr,
00457 "%s: internal fault: %d chunks still on the heap\n",
00458 program, chunks);
00459 }
00460 exit(exstat);
00461 }
00462
00463 void interrupt(int sig)
00464 {
00465 signal(sig, interrupt);
00466 if (verbose >= 2) write(1, "# interrupt\n", 12);
00467 action= 0;
00468 }
00469
00470 int extalnum(int c)
00471
00472 {
00473 return isalnum(c) || c == '_' || c >= 0200;
00474 }
00475
00476 char *descr;
00477 FILE *dfp;
00478 int dch;
00479 unsigned lineno;
00480 unsigned indent;
00481
00482 void getdesc(void)
00483 {
00484 if (dch == EOF) return;
00485
00486 if (dch == '\n') { lineno++; indent= 0; }
00487
00488 if ((dch = getc(dfp)) == EOF && ferror(dfp)) fatal(descr);
00489
00490 if (dch == 0) {
00491 fprintf(stderr, "%s: %s is a binary file.\n", program, descr);
00492 quit(-1);
00493 }
00494 }
00495
00496 #define E_BASH 0x01
00497 #define E_QUOTE 0x02
00498 #define E_SIMPLE 0x04
00499
00500 cell_t *get_token(void)
00501
00502 {
00503 int whitetype= 0;
00504 static int escape= 0;
00505 cell_t *tok;
00506 char *name;
00507 int n, i;
00508
00509 if (escape & E_SIMPLE) {
00510
00511 if (isalnum(dch)) {
00512 tok= newcell();
00513 tok->type= LETTER;
00514 tok->letter= dch;
00515 getdesc();
00516 return inc(tok);
00517 }
00518 escape&= ~E_SIMPLE;
00519 }
00520
00521
00522 for (;;) {
00523 if (dch == '\\' && whitetype == 0) {
00524 getdesc();
00525 if (isspace(dch)) {
00526
00527 do {
00528 getdesc();
00529 if (dch == '#' && !(escape & E_QUOTE)) {
00530
00531 do
00532 getdesc();
00533 while (dch != '\n'
00534 && dch != EOF);
00535 }
00536 } while (isspace(dch));
00537 continue;
00538 }
00539 escape|= E_BASH;
00540 }
00541
00542 if (escape != 0) break;
00543
00544 if (dch == '#' && (indent == 0 || whitetype != 0)) {
00545
00546 do getdesc(); while (dch != '\n' && dch != EOF);
00547 whitetype= COMMENT;
00548 break;
00549 }
00550
00551 if (!isspace(dch) || dch == '\n' || dch == EOF) break;
00552
00553 whitetype= WHITE;
00554
00555 indent++;
00556 if (dch == '\t') indent= (indent + 7) & ~7;
00557
00558 getdesc();
00559 }
00560
00561 if (dch == EOF) return nil;
00562
00563
00564 tok= newcell();
00565
00566 if (whitetype != 0) {
00567 tok->type= whitetype;
00568 return inc(tok);
00569 }
00570
00571 if (!(escape & E_BASH) && dch == '"') {
00572 getdesc();
00573 if (!(escape & E_QUOTE)) {
00574
00575 escape|= E_QUOTE;
00576 tok->type= STRING;
00577 return inc(tok);
00578 } else {
00579
00580 escape&= ~E_QUOTE;
00581 deallocate(tok);
00582 return get_token();
00583 }
00584 }
00585
00586 if (escape & E_BASH
00587 || strchr(escape & E_QUOTE ? "$" : "$=()+-*<>;\n", dch) == nil
00588 ) {
00589 if (dch == '\n') {
00590 fprintf(stderr,
00591 "\"%s\", line %u: missing closing quote\n",
00592 descr, lineno);
00593 escape&= ~E_QUOTE;
00594 action= 0;
00595 }
00596 if (escape & E_BASH && dch == 'n') dch= '\n';
00597 escape&= ~E_BASH;
00598
00599
00600 tok->type= LETTER;
00601 tok->letter= dch;
00602 getdesc();
00603 escape|= E_SIMPLE;
00604 return inc(tok);
00605 }
00606
00607 if (dch != '$') {
00608
00609 switch (dch) {
00610 case '=': tok->type= EQUALS; break;
00611 case '(': tok->type= OPEN; break;
00612 case ')': tok->type= CLOSE; break;
00613 case '+': tok->type= PLUS; break;
00614 case '-': tok->type= MINUS; break;
00615 case '*': tok->type= STAR; break;
00616 case '<': tok->type= INPUT; break;
00617 case '>': tok->type= OUTPUT; break;
00618 case ';': tok->type= SEMI; break;
00619 case '\n': tok->type= EOLN; break;
00620 }
00621 getdesc();
00622 return inc(tok);
00623 }
00624
00625
00626 getdesc();
00627 if (dch == EOF || isspace(dch)) {
00628 fprintf(stderr, "\"%s\", line %u: Word expected after '$'\n",
00629 descr, lineno);
00630 action= 0;
00631 deallocate(tok);
00632 return get_token();
00633 }
00634
00635 name= allocate(nil, (n= 16) * sizeof(*name));
00636 i= 0;
00637
00638 if (dch == '{' || dch == '(' ) {
00639
00640 int lpar= dch;
00641 int rpar= lpar == '{' ? '}' : ')';
00642
00643 for (;;) {
00644 getdesc();
00645 if (dch == rpar) { getdesc(); break; }
00646 if (isspace(dch) || dch == EOF) {
00647 fprintf(stderr,
00648 "\"%s\", line %u: $%c unmatched, no '%c'\n",
00649 descr, lineno, lpar, rpar);
00650 action= 0;
00651 break;
00652 }
00653 name[i++]= dch;
00654 if (i == n)
00655 name= allocate(name, (n*= 2) * sizeof(char));
00656 }
00657 } else
00658 if (extalnum(dch)) {
00659
00660 do {
00661 name[i++]= dch;
00662 if (i == n)
00663 name= allocate(name, (n*= 2) * sizeof(char));
00664 getdesc();
00665 } while (extalnum(dch));
00666 } else {
00667
00668 name[i++]= dch;
00669 getdesc();
00670 }
00671 name[i++]= 0;
00672 name= allocate(name, i * sizeof(char));
00673 tok->type= SUBST;
00674 tok->subst= newcell();
00675 tok->subst->type= WORD;
00676 tok->subst->name= name;
00677 tok->subst= inc(tok->subst);
00678 return inc(tok);
00679 }
00680
00681 typedef enum how { SUPERFICIAL, PARTIAL, FULL, EXPLODE, IMPLODE } how_t;
00682
00683 cell_t *explode(cell_t *p, how_t how);
00684
00685 cell_t *get_string(cell_t **pp)
00686
00687
00688
00689
00690 {
00691 cell_t *p= *pp, *s= nil, **ps= &s;
00692 int quoted= 0;
00693
00694 while (p != nil) {
00695 switch (p->type) {
00696 case STRING:
00697 quoted= 1;
00698 dec(p);
00699 break;
00700 case EQUALS:
00701 case PLUS:
00702 case MINUS:
00703 case STAR:
00704 case SUBST:
00705 case LETTER:
00706 *ps= cons(STRING, p);
00707 ps= &(*ps)->cdr;
00708 break;
00709 default:
00710 goto got_string;
00711 }
00712 p= get_token();
00713 }
00714 got_string:
00715 *pp= p;
00716
00717
00718 if (!quoted && s != nil && s->cdr == nil) {
00719 switch (s->car->type) {
00720 case EQUALS:
00721 case PLUS:
00722 case MINUS:
00723 case STAR:
00724 case SUBST:
00725 return go(s, s->car);
00726 }
00727 }
00728
00729
00730 for (p= s; p != nil; p= p->cdr) {
00731 int c= 0;
00732
00733 switch (p->car->type) {
00734 case EQUALS:
00735 c= '='; break;
00736 case PLUS:
00737 c= '+'; break;
00738 case MINUS:
00739 c= '-'; break;
00740 case STAR:
00741 c= '*'; break;
00742 }
00743 if (c != 0) {
00744 dec(p->car);
00745 p->car= newcell();
00746 p->car->type= LETTER;
00747 p->car->letter= c;
00748 p->car= inc(p->car);
00749 }
00750 }
00751 return explode(s, SUPERFICIAL);
00752 }
00753
00754 cell_t *get_list(cell_t **pp, type_t stop)
00755
00756 {
00757 cell_t *p= *pp, *l= nil, **pl= &l;
00758
00759 while (p != nil && p->type != stop
00760 && !(stop == EOLN && p->type == SEMI)) {
00761 switch (p->type) {
00762 case WHITE:
00763 case COMMENT:
00764 case SEMI:
00765 case EOLN:
00766 dec(p);
00767 p= get_token();
00768 break;
00769 case OPEN:
00770
00771 dec(p);
00772 p= get_token();
00773 *pl= cons(CELL, get_list(&p, CLOSE));
00774 pl= &(*pl)->cdr;
00775 dec(p);
00776 p= get_token();
00777 break;
00778 case CLOSE:
00779
00780 fprintf(stderr, "\"%s\", line %u: unmatched ')'\n",
00781 descr, lineno);
00782 action= 0;
00783 dec(p);
00784 p= get_token();
00785 break;
00786 case INPUT:
00787 case OUTPUT:
00788 *pl= cons(CELL, p);
00789 pl= &(*pl)->cdr;
00790 p= get_token();
00791 break;
00792 case STRING:
00793 case EQUALS:
00794 case PLUS:
00795 case MINUS:
00796 case STAR:
00797 case LETTER:
00798 case SUBST:
00799 *pl= cons(CELL, get_string(&p));
00800 pl= &(*pl)->cdr;
00801 break;
00802 default:
00803 assert(0);
00804 }
00805 }
00806
00807 if (p == nil && stop == CLOSE) {
00808
00809 fprintf(stderr, "\"%s\", lines %u-%u: unmatched '('\n",
00810 descr, pc->lineno, lineno);
00811 action= 0;
00812 }
00813 *pp= p;
00814 return l;
00815 }
00816
00817 program_t *get_line(cell_t *file)
00818 {
00819 program_t *l;
00820 cell_t *p;
00821 static keep_indent= 0;
00822 static unsigned old_indent= 0;
00823
00824
00825 indent= 0;
00826 while ((p= get_token()) != nil && p->type == WHITE) dec(p);
00827
00828 if (p == nil) return nil;
00829
00830 if (p->type == EOLN) indent= old_indent;
00831
00832
00833 pc= l= allocate(nil, sizeof(*l));
00834
00835 l->next= nil;
00836 l->file= inc(file);
00837 l->indent= keep_indent ? old_indent : indent;
00838 l->lineno= lineno;
00839
00840 l->line= get_list(&p, EOLN);
00841
00842
00843 keep_indent= (p != nil && p->type == SEMI);
00844 old_indent= l->indent;
00845
00846 dec(p);
00847
00848 if (verbose >= 4) {
00849 if (l->line == nil)
00850 fputc('\n', stdout);
00851 else {
00852 printf("%*s", (int) l->indent, "");
00853 prin2n(l->line);
00854 }
00855 }
00856 return l;
00857 }
00858
00859 program_t *get_prog(void)
00860
00861 {
00862 cell_t *file;
00863 program_t *prog, **ppg= &prog;
00864
00865 descr= copystr(descr);
00866
00867 if (descr[0] == '-' && descr[1] == 0) {
00868
00869 deallocate(descr);
00870 descr= copystr("stdin");
00871 dfp= stdin;
00872 } else {
00873 char *d= descr;
00874
00875 if (*d == '.' && *++d == '.') d++;
00876 if (*d != '/') {
00877
00878
00879 d= allocate(nil, sizeof(LIB) +
00880 (strlen(descr) + 7) * sizeof(*d));
00881 sprintf(d, "%s/%s/descr", LIB, descr);
00882 deallocate(descr);
00883 descr= d;
00884 }
00885 if ((dfp= fopen(descr, "r")) == nil) fatal(descr);
00886 }
00887 file= findword(descr);
00888 deallocate(descr);
00889 descr= file->name;
00890
00891
00892 dch= 0;
00893 lineno= 1;
00894 indent= 0;
00895 getdesc();
00896
00897 while ((*ppg= get_line(file)) != nil) ppg= &(*ppg)->next;
00898
00899 if (dfp != stdin) (void) fclose(dfp);
00900 dec(file);
00901
00902 return prog;
00903 }
00904
00905 void makenames(cell_t ***ppr, cell_t *s, char **name, size_t i, size_t *n)
00906
00907
00908
00909 {
00910 cell_t *p, *q;
00911 size_t len;
00912
00913
00914 while (s != nil && (s->car == nil || s->car->type == LETTER)) {
00915 if (s->car != nil) {
00916 if (i == *n) *name= allocate(*name,
00917 (*n *= 2) * sizeof(**name));
00918 (*name)[i++]= s->car->letter;
00919 }
00920 s= s->cdr;
00921 }
00922
00923
00924 if (s == nil) {
00925 **ppr= cons(CELL, findnword(*name, i));
00926 *ppr= &(**ppr)->cdr;
00927 return;
00928 }
00929
00930
00931 p= s->car;
00932 s= s->cdr;
00933
00934 while (p != nil) {
00935 if (p->type == WORD) {
00936 q= p; p= nil;
00937 } else {
00938 assert(p->type == CELL);
00939 q= p->car; p= p->cdr;
00940 assert(q != nil);
00941 assert(q->type == WORD);
00942 }
00943 len= strlen(q->name);
00944 if (i + len > *n) *name= allocate(*name,
00945 (*n += i + len) * sizeof(**name));
00946 memcpy(*name + i, q->name, len);
00947
00948 makenames(ppr, s, name, i+len, n);
00949 }
00950 }
00951
00952 int constant(cell_t *p)
00953
00954
00955
00956 {
00957 while (p != nil) {
00958 switch (p->type) {
00959 case CELL:
00960 case STRING:
00961 if (!constant(p->car)) return 0;
00962 p= p->cdr;
00963 break;
00964 case SUBST:
00965 return 0;
00966 default:
00967 return 1;
00968 }
00969 }
00970 return 1;
00971 }
00972
00973 cell_t *evaluate(cell_t *p, how_t how);
00974
00975 cell_t *explode(cell_t *s, how_t how)
00976
00977 {
00978 cell_t *t, *r= nil, **pr= &r;
00979 size_t i, n;
00980 char *name;
00981 struct stat st;
00982
00983 if (how >= PARTIAL) {
00984
00985 while (s != nil) {
00986 assert(s->type == STRING);
00987 t= inc(s->car);
00988 s= go(s, s->cdr);
00989
00990 t= evaluate(t, how == IMPLODE ? EXPLODE : how);
00991
00992
00993 if (t != nil && t->type == CELL && t->cdr == nil)
00994 t= go(t, t->car);
00995
00996
00997 *pr= t;
00998
00999
01000 while ((*pr) != nil) {
01001 *pr= append(STRING, *pr);
01002 pr= &(*pr)->cdr;
01003 }
01004 }
01005 s= r;
01006 }
01007
01008
01009 if (how <= PARTIAL && !constant(s)) return s;
01010
01011
01012
01013
01014 r= nil; pr= &r;
01015 name= allocate(nil, (n= 16) * sizeof(char));
01016 i= 0;
01017
01018 makenames(&pr, s, &name, i, &n);
01019 deallocate(name);
01020 assert(r != nil);
01021 dec(s);
01022 s= r;
01023
01024
01025 if (how == IMPLODE) {
01026 if (s->cdr != nil) {
01027
01028 do {
01029 assert(s->car->type == WORD);
01030 if (stat(s->car->name, &st) >= 0)
01031 return go(r, s->car);
01032 } while ((s= s->cdr) != nil);
01033 }
01034
01035 return go(r, r->car);
01036 }
01037
01038
01039
01040
01041
01042
01043 if (s->cdr == nil) return go(s, s->car);
01044
01045 return how >= EXPLODE ? s : cons(STRING, s);
01046 }
01047
01048 void modify(cell_t **pp, cell_t *p, type_t mode)
01049
01050 {
01051 while (*pp != nil) {
01052 *pp= append(CELL, *pp);
01053
01054 if ((*pp)->car == p) {
01055
01056 if (mode == PLUS) break;
01057 *pp= go(*pp, (*pp)->cdr);
01058 } else
01059 pp= &(*pp)->cdr;
01060 }
01061
01062 if (*pp == nil && mode == PLUS) {
01063
01064 *pp= cons(CELL, p);
01065 } else
01066 dec(p);
01067 }
01068
01069 int tainted(cell_t *p)
01070
01071
01072
01073 {
01074 if (p == nil) return 0;
01075
01076 switch (p->type) {
01077 case CELL:
01078 case STRING:
01079 return tainted(p->car) || tainted(p->cdr);
01080 case SUBST:
01081 return p->subst->flags & W_LOCAL || tainted(p->subst->value);
01082 default:
01083 return 0;
01084 }
01085 }
01086
01087 cell_t *evaluate(cell_t *p, how_t how)
01088
01089 {
01090 cell_t *q, *t, *r= nil, **pr= &r;
01091 type_t mode;
01092
01093 if (p == nil) return nil;
01094
01095 switch (p->type) {
01096 case CELL:
01097 break;
01098 case STRING:
01099 return explode(p, how);
01100 case SUBST:
01101 if (how >= FULL || tainted(p))
01102 p= evaluate(go(p, p->subst->value), how);
01103 return p;
01104 case EQUALS:
01105 fprintf(stderr,
01106 "\"%s\", line %u: Can't do nested assignments\n",
01107 descr, pc->lineno);
01108 action= 0;
01109 dec(p);
01110 return nil;
01111 case LETTER:
01112 case WORD:
01113 case INPUT:
01114 case OUTPUT:
01115 case PLUS:
01116 case MINUS:
01117 return p;
01118 default:
01119 assert(0);
01120 }
01121
01122
01123
01124
01125
01126 q = inc(p);
01127 while (p != nil) {
01128 if ((t= p->car) != nil) {
01129 if (t->type == STAR) {
01130 if (how < FULL) how= FULL;
01131 dec(q);
01132 *pr= evaluate(go(p, p->cdr), how);
01133 return r;
01134 }
01135 if (how>=FULL && (t->type == PLUS || t->type == MINUS))
01136 break;
01137 }
01138
01139 t= evaluate(inc(t), how);
01140 assert(p->type == CELL);
01141 p= go(p, p->cdr);
01142
01143 if (how >= FULL) {
01144
01145 *pr= t;
01146 } else {
01147
01148 *pr= cons(CELL, t);
01149 }
01150
01151
01152 while ((*pr) != nil) {
01153 *pr= append(CELL, *pr);
01154 pr= &(*pr)->cdr;
01155 }
01156 }
01157
01158 if (p == nil) {
01159
01160 dec(q);
01161 return r;
01162 }
01163
01164
01165 if (how < IMPLODE) {
01166 dec(r);
01167 dec(p);
01168 return evaluate(q, IMPLODE);
01169 }
01170 dec(q);
01171
01172
01173 while (p != nil) {
01174 t= inc(p->car);
01175 p= go(p, p->cdr);
01176
01177 if (t != nil && (t->type == PLUS || t->type == MINUS)) {
01178
01179 mode= t->type;
01180 dec(t);
01181 continue;
01182 }
01183
01184 t= evaluate(t, IMPLODE);
01185
01186
01187 while (t != nil) {
01188 if (t->type == CELL) {
01189 modify(&r, inc(t->car), mode);
01190 } else {
01191 modify(&r, t, mode);
01192 break;
01193 }
01194 t= go(t, t->cdr);
01195 }
01196 }
01197 return r;
01198 }
01199
01200
01201
01202
01203 typedef enum phase { INIT, SCAN, COMPILE } phase_t;
01204
01205 phase_t phase;
01206
01207 typedef struct rule {
01208 struct rule *next;
01209 char type;
01210 char flags;
01211 unsigned short npaths;
01212 # define match from
01213 cell_t *from;
01214 cell_t *to;
01215 cell_t *wait;
01216 program_t *prog;
01217 struct rule *path;
01218 } rule_t;
01219
01220 typedef enum ruletype { ARG, PREFER, TRANSFORM, COMBINE } ruletype_t;
01221
01222 #define R_PREFER 0x01
01223
01224 rule_t *rules= nil;
01225
01226 void newrule(ruletype_t type, cell_t *from, cell_t *to)
01227
01228 {
01229 rule_t *r= nil, **pr= &rules;
01230
01231
01232
01233
01234 while ((r= *pr) != nil) {
01235 if (r->from == from && r->to == to) break;
01236 pr= &r->next;
01237 }
01238
01239 if (*pr == nil) {
01240
01241 *pr= r= allocate(nil, sizeof(*r));
01242
01243 r->next= nil;
01244 r->type= type;
01245 r->flags= 0;
01246 r->from= r->to= r->wait= nil;
01247 r->path= nil;
01248 }
01249 if (type == TRANSFORM) r->type= TRANSFORM;
01250 if (type == PREFER) r->flags|= R_PREFER;
01251 if (type != PREFER) r->prog= pc;
01252 dec(r->from); r->from= from;
01253 dec(r->to); r->to= to;
01254 }
01255
01256 int talk(void)
01257
01258 {
01259 if (verbose < 3) return 0;
01260 printf("%*s", (int) pc->indent, "");
01261 return 1;
01262 }
01263
01264 void unix_exec(cell_t *c)
01265
01266 {
01267 cell_t *v, *a;
01268 int fd[2];
01269 int *pf;
01270 char **argv;
01271 int i, n;
01272 int r, pid, status;
01273
01274 if (action == 0) return;
01275
01276 if (talk() || verbose >= 2) prin2n(c);
01277
01278 fd[0]= fd[1]= -1;
01279
01280 argv= allocate(nil, (n= 16) * sizeof(*argv));
01281 i= 0;
01282
01283
01284 for (v= c; v != nil; v= v->cdr) {
01285 a= v->car;
01286 pf= nil;
01287 if (a->type == INPUT) pf= &fd[0];
01288 if (a->type == OUTPUT) pf= &fd[1];
01289
01290 if (pf == nil) {
01291
01292 argv[i++]= a->name;
01293 if (i==n) argv= allocate(argv, (n*= 2) * sizeof(*argv));
01294 continue;
01295 }
01296
01297 if ((v= v->cdr) == nil || (a= v->car)->type != WORD) {
01298 fprintf(stderr,
01299 "\"%s\", line %u: I/O redirection without a file\n",
01300 descr, pc->lineno);
01301 action= 0;
01302 if (v == nil) break;
01303 }
01304 if (*pf >= 0) close(*pf);
01305
01306 if (action >= 2
01307 && (*pf= open(a->name, pf == &fd[0] ? O_RDONLY
01308 : O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0
01309 ) {
01310 report(a->name);
01311 action= 0;
01312 }
01313 }
01314 argv[i]= nil;
01315
01316 if (i >= 0 && action > 0 && verbose == 1) {
01317 char *name= strrchr(argv[0], '/');
01318
01319 if (name == nil) name= argv[0]; else name++;
01320
01321 printf("%s\n", name);
01322 }
01323 if (i >= 0 && action >= 2) {
01324
01325 fflush(stdout);
01326 switch (pid= fork()) {
01327 case -1:
01328 fatal("fork()");
01329 case 0:
01330 if (fd[0] >= 0) { dup2(fd[0], 0); close(fd[0]); }
01331 if (fd[1] >= 0) { dup2(fd[1], 1); close(fd[1]); }
01332 execvp(argv[0], argv);
01333 report(argv[0]);
01334 exit(-1);
01335 }
01336 }
01337 if (fd[0] >= 0) close(fd[0]);
01338 if (fd[1] >= 0) close(fd[1]);
01339
01340 if (i >= 0 && action >= 2) {
01341
01342 while ((r= wait(&status)) != pid && (r >= 0 || errno == EINTR));
01343
01344 if (status != 0) {
01345 int sig= WTERMSIG(status);
01346
01347 if (!WIFEXITED(status)
01348 && sig != SIGINT && sig != SIGPIPE) {
01349 fprintf(stderr, "%s: %s: Signal %d%s\n",
01350 program, argv[0], sig,
01351 status & 0x80 ? " - core dumped" : "");
01352 }
01353 action= 0;
01354 }
01355 }
01356 deallocate(argv);
01357 }
01358
01359
01360 cell_t *V_star, **pV_star;
01361 cell_t *L_files, **pL_files= &L_files;
01362 cell_t *V_in, *V_out, *V_stop, *L_args, *L_predef;
01363
01364 typedef enum exec { DOIT, DONT } exec_t;
01365
01366 void execute(exec_t how, unsigned indent);
01367
01368 int equal(cell_t *p, cell_t *q)
01369
01370 {
01371 cell_t *t, *m1, *m2;
01372
01373 t= inc(newcell());
01374 t->cdr= inc(newcell());
01375 t->cdr->cdr= inc(newcell());
01376 t->cdr->car= newcell();
01377 t->cdr->car->type= MINUS;
01378 t->cdr->car= inc(t->cdr->car);
01379
01380
01381 t->car= inc(p);
01382 t->cdr->cdr->car= inc(q);
01383 m1= evaluate(inc(t), IMPLODE);
01384 dec(m1);
01385
01386
01387 t->car= q;
01388 t->cdr->cdr->car= p;
01389 m2= evaluate(t, IMPLODE);
01390 dec(m2);
01391
01392
01393 return m1 == nil && m2 == nil;
01394 }
01395
01396 int wordlist(cell_t **pw, int atom)
01397
01398
01399
01400
01401 {
01402 int n= 0;
01403 cell_t *p, **pp= pw;
01404
01405 while (*pp != nil) {
01406 *pp= append(CELL, *pp);
01407 p= (*pp)->car;
01408 n= n >= 0 && p != nil && p->type == WORD ? n+1 : -1;
01409 pp= &(*pp)->cdr;
01410 }
01411 if (atom && n == 1) *pw= go(*pw, (*pw)->car);
01412 return n;
01413 }
01414
01415 char *template;
01416 static char *tp;
01417
01418 char *maketemp(void)
01419
01420 {
01421 int i= 0;
01422
01423 if (tp == nil) {
01424 size_t len= strlen(template);
01425
01426 template= allocate(template, (len+20) * sizeof(*template));
01427 sprintf(template+len, "/acd%d", getpid());
01428 tp= template + strlen(template);
01429 }
01430
01431 for (;;) {
01432 switch (tp[i]) {
01433 case 0: tp[i]= 'a';
01434 tp[i+1]= 0; return template;
01435 case 'z': tp[i++]= 'a'; break;
01436 default: tp[i]++; return template;
01437 }
01438 }
01439 }
01440
01441 void inittemp(char *tmpdir)
01442
01443 {
01444 template= allocate(nil, (strlen(tmpdir)+20) * sizeof(*template));
01445 sprintf(template, "%s/acd%d", tmpdir, getpid());
01446 tp= template + strlen(template);
01447
01448
01449 while (action != 1 && mkdir(template, 0700) < 0) {
01450 if (errno == EEXIST) {
01451 (void) maketemp();
01452 } else {
01453 report(template);
01454 action= 0;
01455 }
01456 }
01457 if (verbose >= 2) printf("mkdir %s\n", template);
01458 while (*tp != 0) tp++;
01459 *tp++= '/';
01460 *tp= 0;
01461 }
01462
01463 void deltemp(void)
01464
01465 {
01466 while (*--tp != '/') {}
01467 *tp = 0;
01468 if (rmdir(template) < 0 && errno != ENOENT) report(template);
01469 if (verbose >= 2) printf("rmdir %s\n", template);
01470 deallocate(template);
01471 }
01472
01473 cell_t *splitenv(char *env)
01474
01475
01476
01477 {
01478 cell_t *r= nil, **pr= &r;
01479 char *p;
01480
01481 do {
01482 while (*env != 0 && isspace(*env)) env++;
01483
01484 if (*env == 0) break;
01485
01486 p= env;
01487 while (*p != 0 && !isspace(*p) && *p != ':') p++;
01488
01489 *pr= cons(CELL,
01490 p == env ? findword(".") : findnword(env, p-env));
01491 pr= &(*pr)->cdr;
01492 env= p;
01493 } while (*env++ != 0);
01494 return r;
01495 }
01496
01497 void key_usage(char *how)
01498 {
01499 fprintf(stderr, "\"%s\", line %u: Usage: %s %s\n",
01500 descr, pc->lineno, pc->line->car->name, how);
01501 action= 0;
01502 }
01503
01504 void inappropriate(void)
01505 {
01506 fprintf(stderr, "\"%s\", line %u: wrong execution phase for '%s'\n",
01507 descr, pc->lineno, pc->line->car->name);
01508 action= 0;
01509 }
01510
01511 int readonly(cell_t *v)
01512 {
01513 if (v->flags & W_RDONLY) {
01514 fprintf(stderr, "\"%s\", line %u: %s is read-only\n",
01515 descr, pc->lineno, v->name);
01516 action= 0;
01517 return 1;
01518 }
01519 return 0;
01520 }
01521
01522 void complain(cell_t *err)
01523
01524 {
01525 cell_t *w;
01526
01527 fprintf(stderr, "%s:", program);
01528
01529 while (err != nil) {
01530 if (err->type == CELL) {
01531 w= err->car; err= err->cdr;
01532 } else {
01533 w= err; err= nil;
01534 }
01535 fprintf(stderr, " %s", w->name);
01536 }
01537 action= 0;
01538 }
01539
01540 int keyword(char *name)
01541
01542 {
01543 cell_t *t;
01544
01545 return (t= pc->line) != nil && t->type == CELL
01546 && (t= t->car) != nil && t->type == WORD
01547 && strcmp(t->name, name) == 0;
01548 }
01549
01550 cell_t *getvar(cell_t *v)
01551
01552 {
01553 if (v == nil) return nil;
01554 if (v->type == WORD) return v;
01555 if (v->type == SUBST) return v->subst;
01556 return nil;
01557 }
01558
01559 void argscan(void), compile(void);
01560 void transform(rule_t *);
01561
01562 void exec_one(void)
01563
01564 {
01565 cell_t *v, *p, *q, *r, *t;
01566 unsigned n= 0;
01567 static int last_if= 1;
01568
01569
01570 descr= pc->file->name;
01571
01572 for (p= pc->line; p != nil; p= p->cdr) n++;
01573
01574 if (n == 0) return;
01575
01576 p= pc->line;
01577 q= p->cdr;
01578 r= q == nil ? nil : q->cdr;
01579
01580
01581
01582 if (n >= 2 && q->car != nil && q->car->type == EQUALS) {
01583
01584 int flags;
01585
01586 if ((v= getvar(p->car)) == nil) {
01587 fprintf(stderr,
01588 "\"%s\", line %u: Usage: <var> = expr ...\n",
01589 descr, pc->lineno);
01590 action= 0;
01591 return;
01592 }
01593
01594 if (readonly(v)) return;
01595
01596 flags= v->flags;
01597 v->flags|= W_LOCAL|W_RDONLY;
01598 t= evaluate(inc(r), PARTIAL);
01599 dec(v->value);
01600 v->value= t;
01601 v->flags= flags | W_SET;
01602 if (talk()) {
01603 printf("%s =\b=\b= ", v->name);
01604 prin2n(t);
01605 }
01606 } else
01607 if (keyword("unset")) {
01608
01609
01610 if (n != 2 || (v= getvar(q->car)) == nil) {
01611 key_usage("<var>");
01612 return;
01613 }
01614 if (readonly(v)) return;
01615
01616 if (talk()) prin2n(p);
01617
01618 dec(v->value);
01619 v->value= nil;
01620 v->flags&= ~W_SET;
01621 } else
01622 if (keyword("import")) {
01623
01624 char *env;
01625
01626 if (n != 2 || (v= getvar(q->car)) == nil) {
01627 key_usage("<var>");
01628 return;
01629 }
01630 if (readonly(v)) return;
01631
01632 if ((env= getenv(v->name)) == nil) return;
01633
01634 if (talk()) printf("import %s=%s\n", v->name, env);
01635
01636 t= splitenv(env);
01637 dec(v->value);
01638 v->value= t;
01639 v->flags|= W_SET;
01640 } else
01641 if (keyword("mktemp")) {
01642
01643 char *tmp, *suff;
01644
01645 r= evaluate(inc(r), IMPLODE);
01646 if (n == 3 && wordlist(&r, 1) != 1) n= 0;
01647
01648 if ((n != 2 && n != 3) || (v= getvar(q->car)) == nil) {
01649 dec(r);
01650 key_usage("<var> [<suffix>]");
01651 return;
01652 }
01653 if (readonly(v)) { dec(r); return; }
01654
01655 tmp= maketemp();
01656 suff= r == nil ? "" : r->name;
01657
01658 t= newcell();
01659 t->type= WORD;
01660 t->name= allocate(nil,
01661 (strlen(tmp) + strlen(suff) + 1) * sizeof(*t->name));
01662 strcpy(t->name, tmp);
01663 strcat(t->name, suff);
01664 t= inc(t);
01665 dec(r);
01666 dec(v->value);
01667 v->value= t;
01668 v->flags|= W_SET;
01669 t->flags|= W_TEMP;
01670 if (talk()) printf("mktemp %s=%s\n", v->name, t->name);
01671 } else
01672 if (keyword("temporary")) {
01673
01674 cell_t *tmp;
01675
01676 tmp= evaluate(inc(q), IMPLODE);
01677
01678 if (wordlist(&tmp, 1) < 0) {
01679 dec(tmp);
01680 key_usage("<word>");
01681 return;
01682 }
01683 if (talk()) printf("temporary %s\n", tmp->name);
01684
01685 tmp->flags|= W_TEMP;
01686 dec(tmp);
01687 } else
01688 if (keyword("stop")) {
01689
01690 cell_t *suff;
01691
01692 if (phase > SCAN) { inappropriate(); return; }
01693
01694 suff= evaluate(inc(q), IMPLODE);
01695
01696 if (wordlist(&suff, 1) != 1) {
01697 dec(suff);
01698 key_usage("<suffix>");
01699 return;
01700 }
01701 dec(V_stop);
01702 V_stop= suff;
01703 if (talk()) printf("stop %s\n", suff->name);
01704 } else
01705 if (keyword("numeric")) {
01706
01707 cell_t *num;
01708 char *pn;
01709
01710 num= evaluate(inc(q), IMPLODE);
01711
01712 if (wordlist(&num, 1) != 1) {
01713 dec(num);
01714 key_usage("<arg>");
01715 return;
01716 }
01717 if (talk()) printf("numeric %s\n", num->name);
01718
01719 (void) strtoul(num->name, &pn, 10);
01720 if (*pn != 0) {
01721 complain(phase == SCAN ? V_star->value : nil);
01722 if (phase == SCAN) fputc(':', stderr);
01723 fprintf(stderr, " '%s' is not a number\n", num->name);
01724 }
01725 dec(num);
01726 } else
01727 if (keyword("error")) {
01728
01729 cell_t *err;
01730
01731 err= evaluate(inc(q), IMPLODE);
01732
01733 if (wordlist(&err, 0) < 1) {
01734 dec(err);
01735 key_usage("expr ...");
01736 return;
01737 }
01738
01739 if (talk()) { printf("error "); prin2n(err); }
01740
01741 complain(err);
01742 fputc('\n', stderr);
01743 dec(err);
01744 } else
01745 if (keyword("if")) {
01746
01747 int eq;
01748
01749 if (n != 4 || r->car == nil || r->car->type != EQUALS) {
01750 key_usage("<expr> = <expr>");
01751 execute(DONT, pc->indent+1);
01752 last_if= 1;
01753 return;
01754 }
01755 q= q->car;
01756 r= r->cdr->car;
01757 if (talk()) {
01758 printf("if ");
01759 prin1(t= evaluate(inc(q), IMPLODE));
01760 dec(t);
01761 printf(" = ");
01762 prin1n(t= evaluate(inc(r), IMPLODE));
01763 dec(t);
01764 }
01765 eq= equal(q, r);
01766 execute(eq ? DOIT : DONT, pc->indent+1);
01767 last_if= eq;
01768 } else
01769 if (keyword("ifdef") || keyword("ifndef")) {
01770
01771 int doit;
01772
01773 if (n != 2 || (v= getvar(q->car)) == nil) {
01774 key_usage("<var>");
01775 execute(DONT, pc->indent+1);
01776 last_if= 1;
01777 return;
01778 }
01779 if (talk()) prin2n(p);
01780
01781 doit= ((v->flags & W_SET) != 0) ^ (p->car->name[2] == 'n');
01782 execute(doit ? DOIT : DONT, pc->indent+1);
01783 last_if= doit;
01784 } else
01785 if (keyword("iftemp") || keyword("ifhash")) {
01786
01787
01788 cell_t *file;
01789 int doit= 0;
01790
01791 file= evaluate(inc(q), IMPLODE);
01792
01793 if (wordlist(&file, 1) != 1) {
01794 dec(file);
01795 key_usage("<arg>");
01796 return;
01797 }
01798 if (talk()) printf("%s %s\n", p->car->name, file->name);
01799
01800 if (p->car->name[2] == 't') {
01801
01802 if (file->flags & W_TEMP) doit= 1;
01803 } else {
01804
01805 int fd;
01806 char hash;
01807
01808 if ((fd= open(file->name, O_RDONLY)) >= 0) {
01809 if (read(fd, &hash, 1) == 1 && hash == '#')
01810 doit= 1;
01811 close(fd);
01812 }
01813 }
01814 dec(file);
01815
01816 execute(doit ? DOIT : DONT, pc->indent+1);
01817 last_if= doit;
01818 } else
01819 if (keyword("else")) {
01820
01821 if (n != 1) {
01822 key_usage("");
01823 execute(DONT, pc->indent+1);
01824 return;
01825 }
01826 if (talk()) prin2n(p);
01827
01828 execute(!last_if ? DOIT : DONT, pc->indent+1);
01829 } else
01830 if (keyword("treat")) {
01831
01832
01833 if (phase > SCAN) { inappropriate(); return; }
01834
01835 if (n == 3) {
01836 q= evaluate(inc(q->car), IMPLODE);
01837 r= evaluate(inc(r->car), IMPLODE);
01838 }
01839 if (n != 3 || wordlist(&q, 1) != 1 || wordlist(&r, 1) != 1) {
01840 if (n == 3) { dec(q); dec(r); }
01841 key_usage("<file> <suffix>");
01842 return;
01843 }
01844 if (talk()) printf("treat %s %s\n", q->name, r->name);
01845
01846 dec(q->suffix);
01847 q->suffix= r;
01848 q->flags|= W_SUFF;
01849 dec(q);
01850 } else
01851 if (keyword("apply")) {
01852
01853 rule_t *rule, *sav_path;
01854 cell_t *sav_wait, *sav_in, *sav_out;
01855 program_t *sav_next;
01856
01857 if (phase != COMPILE) { inappropriate(); return; }
01858
01859 if (V_star->value->cdr != nil) {
01860 fprintf(stderr, "\"%s\", line %u: $* is not one file\n",
01861 descr, pc->lineno);
01862 action= 0;
01863 return;
01864 }
01865 if (n == 3) {
01866 q= evaluate(inc(q->car), IMPLODE);
01867 r= evaluate(inc(r->car), IMPLODE);
01868 }
01869 if (n != 3 || wordlist(&q, 1) != 1 || wordlist(&r, 1) != 1) {
01870 if (n == 3) { dec(q); dec(r); }
01871 key_usage("<file> <suffix>");
01872 return;
01873 }
01874 if (talk()) printf("apply %s %s\n", q->name, r->name);
01875
01876
01877 for (rule= rules; rule != nil; rule= rule->next) {
01878 if (rule->type == TRANSFORM
01879 && rule->from == q && rule->to == r) break;
01880 }
01881 if (rule == nil) {
01882 fprintf(stderr,
01883 "\"%s\", line %u: no %s %s transformation\n",
01884 descr, pc->lineno, q->name, r->name);
01885 action= 0;
01886 }
01887 dec(q);
01888 dec(r);
01889 if (rule == nil) return;
01890
01891
01892 sav_path= rule->path;
01893 sav_wait= rule->wait;
01894 sav_in= V_in->value;
01895 sav_out= V_out->value;
01896 sav_next= nextpc;
01897
01898
01899 rule->path= rule;
01900 rule->wait= V_star->value;
01901 V_star->value= nil;
01902 V_in->value= nil;
01903 V_out->value= nil;
01904
01905 transform(rule);
01906
01907
01908 V_star->value= rule->wait;
01909 rule->path= sav_path;
01910 rule->wait= sav_wait;
01911 V_in->value= sav_in;
01912 V_out->value= sav_out;
01913 V_out->flags= W_SET|W_LOCAL;
01914 nextpc= sav_next;
01915 } else
01916 if (keyword("include")) {
01917
01918 cell_t *file;
01919 program_t *incl, *prog, **ppg= &prog;
01920
01921 file= evaluate(inc(q), IMPLODE);
01922
01923 if (wordlist(&file, 1) != 1) {
01924 dec(file);
01925 key_usage("<file>");
01926 return;
01927 }
01928 if (talk()) printf("include %s\n", file->name);
01929 descr= file->name;
01930 incl= pc;
01931 prog= get_prog();
01932 dec(file);
01933
01934
01935 while (*ppg != nil) {
01936 (*ppg)->indent += incl->indent;
01937 ppg= &(*ppg)->next;
01938 }
01939
01940
01941 dec(incl->line);
01942 incl->line= nil;
01943 *ppg= incl->next;
01944 incl->next= prog;
01945 pc= incl;
01946 nextpc= prog;
01947 } else
01948 if (keyword("arg")) {
01949
01950
01951 if (phase > SCAN) { inappropriate(); return; }
01952
01953 if (n < 2) {
01954 key_usage("<string> ...");
01955 execute(DONT, pc->indent+1);
01956 return;
01957 }
01958 if (talk()) prin2n(p);
01959
01960 newrule(ARG, inc(q), nil);
01961
01962
01963 execute(DONT, pc->indent+1);
01964 } else
01965 if (keyword("transform")) {
01966
01967
01968 if (phase > SCAN) { inappropriate(); return; }
01969
01970 if (n == 3) {
01971 q= evaluate(inc(q->car), IMPLODE);
01972 r= evaluate(inc(r->car), IMPLODE);
01973 }
01974 if (n != 3 || wordlist(&q, 1) != 1 || wordlist(&r, 1) != 1) {
01975 if (n == 3) { dec(q); dec(r); }
01976 key_usage("<suffix1> <suffix2>");
01977 execute(DONT, pc->indent+1);
01978 return;
01979 }
01980 if (talk()) printf("transform %s %s\n", q->name, r->name);
01981
01982 newrule(TRANSFORM, q, r);
01983
01984
01985 execute(DONT, pc->indent+1);
01986 } else
01987 if (keyword("prefer")) {
01988
01989
01990 if (phase > SCAN) { inappropriate(); return; }
01991
01992 if (n == 3) {
01993 q= evaluate(inc(q->car), IMPLODE);
01994 r= evaluate(inc(r->car), IMPLODE);
01995 }
01996 if (n != 3 || wordlist(&q, 1) != 1 || wordlist(&r, 1) != 1) {
01997 if (n == 3) { dec(q); dec(r); }
01998 key_usage("<suffix1> <suffix2>");
01999 return;
02000 }
02001 if (talk()) printf("prefer %s %s\n", q->name, r->name);
02002
02003 newrule(PREFER, q, r);
02004 } else
02005 if (keyword("combine")) {
02006
02007
02008 if (phase > SCAN) { inappropriate(); return; }
02009
02010 if (n == 3) {
02011 q= evaluate(inc(q->car), IMPLODE);
02012 r= evaluate(inc(r->car), IMPLODE);
02013 }
02014 if (n != 3 || wordlist(&q, 0) < 1 || wordlist(&r, 1) != 1) {
02015 if (n == 3) { dec(q); dec(r); }
02016 key_usage("<suffix-list> <suffix>");
02017 execute(DONT, pc->indent+1);
02018 return;
02019 }
02020 if (talk()) {
02021 printf("combine ");
02022 prin1(q);
02023 printf(" %s\n", r->name);
02024 }
02025
02026 newrule(COMBINE, q, r);
02027
02028
02029 execute(DONT, pc->indent+1);
02030 } else
02031 if (keyword("scan") || keyword("compile")) {
02032 program_t *next= nextpc;
02033
02034 if (n != 1) { key_usage(""); return; }
02035 if (phase != INIT) { inappropriate(); return; }
02036
02037 if (talk()) prin2n(p);
02038
02039 argscan();
02040 if (p->car->name[0] == 'c') compile();
02041 nextpc= next;
02042 } else {
02043
02044 t= evaluate(inc(pc->line), IMPLODE);
02045 unix_exec(t);
02046 dec(t);
02047 }
02048 }
02049
02050 void execute(exec_t how, unsigned indent)
02051
02052 {
02053 int work= 0;
02054 unsigned firstline;
02055 unsigned nice_indent= 0;
02056
02057 if (pc == nil) return;
02058
02059 firstline= pc->lineno;
02060
02061 if (how == DONT) {
02062
02063 pc= pc->next;
02064 if (pc != nil && pc->indent < indent && pc->line != nil) {
02065
02066 return;
02067 }
02068 } else {
02069
02070
02071
02072 while (pc != nil && pc->indent < indent && pc->line != nil)
02073 pc= pc->next;
02074 }
02075
02076
02077 while (pc != nil && pc->indent >= indent) {
02078 if (pc->indent != nice_indent && how == DOIT) {
02079 if (nice_indent != 0) {
02080 fprintf(stderr,
02081 "\"%s\", line %u: (warning) sudden indentation shift\n",
02082 descr, pc->lineno);
02083 }
02084 nice_indent= pc->indent;
02085 }
02086 nextpc= pc->next;
02087 if (how == DOIT) exec_one();
02088 pc= nextpc;
02089 work= 1;
02090 }
02091
02092 if (indent > 0 && !work) {
02093 fprintf(stderr, "\"%s\", line %u: empty body, no statements\n",
02094 descr, firstline);
02095 action= 0;
02096 }
02097 }
02098
02099 int argmatch(int shift, cell_t *match, cell_t *match1, char *arg1)
02100
02101
02102
02103 {
02104 cell_t *oldval, *v;
02105 int m, oldflags;
02106 size_t i, len;
02107 int minus= 0;
02108
02109 if (shift) {
02110
02111 cell_t **oldpstar= pV_star;
02112 *pV_star= L_args;
02113 L_args= *(pV_star= &L_args->cdr);
02114 *pV_star= nil;
02115
02116 if (argmatch(0, match->cdr, nil, nil)) return 1;
02117
02118
02119 *pV_star= L_args;
02120 L_args= *(pV_star= oldpstar);
02121 *pV_star= nil;
02122 return 0;
02123 }
02124
02125 if (match == nil) {
02126
02127
02128
02129 V_out->flags= W_SET|W_LOCAL;
02130
02131 if (verbose >= 3) {
02132 prin2(pc->line);
02133 printf(" =\b=\b= ");
02134 prin2n(V_star->value);
02135 }
02136 execute(DOIT, pc->indent+1);
02137
02138
02139 if (V_out->value != nil) {
02140 *pL_files= cons(CELL, V_out->value);
02141 pL_files= &(*pL_files)->cdr;
02142 }
02143
02144
02145 V_out->value= nil;
02146 V_out->flags= W_SET|W_LOCAL|W_RDONLY;
02147
02148 return 1;
02149 }
02150
02151 if (L_args == nil) return 0;
02152
02153
02154
02155
02156
02157 if (match1 == nil) {
02158
02159 if (arg1 != nil) {
02160 if (*arg1 != 0) return 0;
02161 return argmatch(1, match, nil, nil);
02162 }
02163
02164 match1= match->car;
02165 arg1= L_args->car->name;
02166
02167
02168 if (arg1[0] == '-') minus= 1;
02169 }
02170
02171 if (match1->type == WORD && strcmp(match1->name, arg1) == 0) {
02172
02173
02174 return argmatch(1, match, nil, nil);
02175 }
02176
02177 if (match1->type == SUBST && !minus) {
02178
02179
02180
02181 v= match1->subst;
02182 if (v->flags & W_RDONLY) return 0;
02183 oldflags= v->flags;
02184 v->flags= W_SET|W_LOCAL|W_RDONLY;
02185 oldval= v->value;
02186 v->value= inc(L_args->car);
02187
02188 m= argmatch(1, match, nil, nil);
02189
02190
02191 dec(v->value);
02192 v->flags= oldflags;
02193 v->value= oldval;
02194 return m;
02195 }
02196 if (match1->type != STRING) return 0;
02197
02198
02199 if (match1->car == nil) return 0;
02200
02201 if (match1->car->type == LETTER
02202 && match1->car->letter == (unsigned char) *arg1) {
02203
02204
02205 return argmatch(0, match, match1->cdr, arg1+1);
02206 }
02207
02208
02209 len= strlen(arg1);
02210 if (match1->car->type != SUBST || minus || len == 0) return 0;
02211
02212
02213
02214
02215 v= match1->car->subst;
02216 if (v->flags & W_RDONLY) return 0;
02217 oldflags= v->flags;
02218 v->flags= W_SET|W_LOCAL|W_RDONLY;
02219 oldval= v->value;
02220
02221 m= 0;
02222 for (i= match1->cdr == nil ? len : 1; !m && i <= len; i++) {
02223 v->value= findnword(arg1, i);
02224
02225 m= argmatch(0, match, match1->cdr, arg1+i);
02226
02227 dec(v->value);
02228 }
02229
02230 v->flags= oldflags;
02231 v->value= oldval;
02232 return m;
02233 }
02234
02235 void argscan(void)
02236
02237
02238
02239 {
02240 rule_t *rule;
02241 int m;
02242
02243 phase= SCAN;
02244
02245
02246 while (L_args != nil) {
02247 pV_star= &V_star->value;
02248
02249
02250 m= 0;
02251 for (rule= rules; !m && rule != nil; rule= rule->next) {
02252 if (rule->type != ARG) continue;
02253
02254 pc= rule->prog;
02255
02256 m= argmatch(0, rule->match, nil, nil);
02257 }
02258 dec(V_star->value);
02259 V_star->value= nil;
02260
02261
02262 if (!m) {
02263 *pL_files= L_args;
02264 L_args= *(pL_files= &L_args->cdr);
02265 *pL_files= nil;
02266 }
02267 }
02268 phase= INIT;
02269 }
02270
02271 int member(cell_t *p, cell_t *l)
02272
02273 {
02274 while (l != nil && l->type == CELL) {
02275 if (p == l->car) return 1;
02276 l= l->cdr;
02277 }
02278 return p == l;
02279 }
02280
02281 long basefind(cell_t *f, cell_t *l)
02282
02283
02284 {
02285 cell_t *suff;
02286 size_t blen, slen;
02287 char *base;
02288
02289
02290 if ((base= strrchr(f->name, '/')) == nil) base= f->name; else base++;
02291 blen= strlen(base);
02292
02293
02294 while (l != nil) {
02295 if (l->type == CELL) {
02296 suff= l->car; l= l->cdr;
02297 } else {
02298 suff= l; l= nil;
02299 }
02300 if (f->flags & W_SUFF) {
02301
02302 if (f->suffix == suff) return 0;
02303 continue;
02304 }
02305 slen= strlen(suff->name);
02306 if (slen < blen && strcmp(base+blen-slen, suff->name) == 0) {
02307
02308 dec(f->base);
02309 f->base= findnword(base, blen-slen);
02310 return 10000L * (blen - slen);
02311 }
02312 }
02313 return -1;
02314 }
02315
02316 #define NO_PATH 2000000000
02317
02318 long shortest;
02319
02320 rule_t *findpath(long depth, int seek, cell_t *file, rule_t *start)
02321
02322 {
02323 rule_t *rule;
02324
02325 if (action == 0) return nil;
02326
02327 if (start == nil) {
02328
02329
02330 for (rule= rules; rule != nil; rule= rule->next) {
02331 if (rule->type < TRANSFORM) continue;
02332
02333 if ((depth= basefind(file, rule->from)) >= 0) {
02334 if (findpath(depth, seek, nil, rule) != nil)
02335 return rule;
02336 }
02337 }
02338 return nil;
02339 }
02340
02341
02342 if (start->path != nil) {
02343
02344 if (start->type == COMBINE) {
02345 fprintf(stderr,
02346 "\"%s\": contains a combine-combine cycle\n",
02347 descr);
02348 action= 0;
02349 }
02350 return nil;
02351 }
02352
02353
02354 if (start->flags & R_PREFER) depth-= 100;
02355
02356
02357 for (rule= rules; rule != nil; rule= rule->next) {
02358 if (rule->type < TRANSFORM) continue;
02359
02360 if (member(start->to, rule->from)) {
02361 start->path= rule;
02362 rule->npaths++;
02363 if (findpath(depth+1, seek, nil, rule) != nil)
02364 return start;
02365 start->path= nil;
02366 rule->npaths--;
02367 }
02368 }
02369
02370 if (V_stop == nil) {
02371 fprintf(stderr, "\"%s\": no stop suffix has been defined\n",
02372 descr);
02373 action= 0;
02374 return nil;
02375 }
02376
02377
02378 if (start->to == V_stop) {
02379
02380 if (seek) {
02381
02382 if (depth == shortest) return start;
02383 } else {
02384
02385 if (depth < shortest) shortest= depth;
02386 }
02387 }
02388 return nil;
02389 }
02390
02391 void transform(rule_t *rule)
02392
02393 {
02394 cell_t *file, *in, *out;
02395 char *base;
02396
02397
02398 while (rule->wait != nil) {
02399 file= rule->wait;
02400 rule->wait= file->cdr;
02401 file->cdr= V_star->value;
02402 V_star->value= file;
02403 }
02404
02405
02406 file= file->car;
02407 V_in->value= in= inc(file->flags & W_SUFF ? file : file->base);
02408 file->flags&= ~W_SUFF;
02409
02410
02411 out= newcell();
02412 out->type= WORD;
02413 base= rule->path == nil ? in->name : maketemp();
02414 out->name= allocate(nil,
02415 (strlen(base)+strlen(rule->to->name)+1) * sizeof(*out->name));
02416 strcpy(out->name, base);
02417 if (rule->path == nil || strchr(rule->to->name, '/') == nil)
02418 strcat(out->name, rule->to->name);
02419 out= inc(out);
02420 if (rule->path != nil) out->flags|= W_TEMP;
02421
02422 V_out->value= out;
02423 V_out->flags= W_SET|W_LOCAL;
02424
02425
02426 if (verbose >= 3) {
02427 printf("%s ", rule->type==TRANSFORM ? "transform" : "combine");
02428 prin2(V_star->value);
02429 printf(" %s\n", out->name);
02430 }
02431 pc= rule->prog;
02432 execute(DOIT, pc->indent+1);
02433
02434
02435 out= evaluate(V_out->value, IMPLODE);
02436 if (wordlist(&out, 1) != 1) {
02437 fprintf(stderr,
02438 "\"%s\", line %u: $> should be returned as a single word\n",
02439 descr, rule->prog->lineno);
02440 action= 0;
02441 }
02442
02443 if ((rule= rule->path) != nil) {
02444
02445 dec(out->base);
02446 out->base= in;
02447 file= inc(newcell());
02448 file->car= out;
02449 file->cdr= rule->wait;
02450 rule->wait= file;
02451 } else {
02452 dec(in);
02453 dec(out);
02454 }
02455
02456
02457 dec(V_star->value);
02458 V_star->value= nil;
02459 V_in->value= nil;
02460 V_out->value= nil;
02461 V_out->flags= W_SET|W_LOCAL|W_RDONLY;
02462 }
02463
02464 void compile(void)
02465 {
02466 rule_t *rule;
02467 cell_t *file, *t;
02468
02469 phase= COMPILE;
02470
02471
02472 L_files= evaluate(L_files, IMPLODE);
02473 if (wordlist(&L_files, 0) < 0) {
02474 fprintf(stderr, "\"%s\": An assignment to $> contained junk\n",
02475 descr);
02476 action= 0;
02477 }
02478
02479 while (action != 0 && L_files != nil) {
02480 file= L_files->car;
02481
02482
02483 shortest= NO_PATH;
02484 for (rule= rules; rule != nil; rule= rule->next)
02485 rule->path= nil;
02486
02487
02488 (void) findpath(0L, 0, file, nil);
02489
02490 if (shortest == NO_PATH) {
02491 fprintf(stderr,
02492 "%s: %s: can't compile, no transformation applies\n",
02493 program, file->name);
02494 action= 0;
02495 return;
02496 }
02497
02498
02499 if ((rule= findpath(0L, 1, file, nil)) == nil) return;
02500
02501
02502 t= inc(newcell());
02503 t->car= inc(file);
02504 L_files= go(L_files, L_files->cdr);
02505 t->cdr= rule->wait;
02506 rule->wait= t;
02507 while (action != 0 && rule != nil && rule->type != COMBINE) {
02508 transform(rule);
02509 rule= rule->path;
02510 }
02511 }
02512
02513
02514
02515
02516
02517
02518 while (action != 0) {
02519 int least;
02520 rule_t *comb= nil;
02521
02522 for (rule= rules; rule != nil; rule= rule->next) {
02523 rule->path= nil;
02524
02525 if (rule->type != COMBINE || rule->wait == nil)
02526 continue;
02527
02528 if (comb == nil || rule->npaths < least) {
02529 least= rule->npaths;
02530 comb= rule;
02531 }
02532 }
02533
02534
02535 if (comb == nil) break;
02536
02537
02538 shortest= NO_PATH;
02539
02540
02541 (void) findpath(0L, 0, nil, comb);
02542
02543 if (shortest == NO_PATH) break;
02544
02545
02546 if ((rule= findpath(0L, 1, nil, comb)) == nil) return;
02547
02548
02549 do {
02550 transform(rule);
02551 rule= rule->path;
02552 } while (action != 0 && rule != nil && rule->type != COMBINE);
02553 }
02554 phase= INIT;
02555 }
02556
02557 cell_t *predef(char *var, char *val)
02558
02559 {
02560 cell_t *p, *t;
02561
02562 p= findword(var);
02563 if (val != nil) {
02564 t= findword(val);
02565 dec(p->value);
02566 p->value= t;
02567 p->flags|= W_SET;
02568 if (verbose >= 3) {
02569 prin1(p);
02570 printf(" =\b=\b= ");
02571 prin2n(t);
02572 }
02573 } else {
02574 p->flags= W_SET|W_LOCAL|W_RDONLY;
02575 }
02576 t= inc(newcell());
02577 t->car= p;
02578 t->cdr= L_predef;
02579 L_predef= t;
02580 return p;
02581 }
02582
02583 void usage(void)
02584 {
02585 fprintf(stderr,
02586 "Usage: %s -v<n> -vn<n> -name <name> -descr <descr> -T <dir> ...\n",
02587 program);
02588 exit(-1);
02589 }
02590
02591 int main(int argc, char **argv)
02592 {
02593 char *tmpdir;
02594 program_t *prog;
02595 cell_t **pa;
02596 int i;
02597
02598
02599 if ((program= strrchr(argv[0], '/')) == nil)
02600 program= argv[0];
02601 else
02602 program++;
02603
02604
02605 if ((tmpdir= getenv("TMPDIR")) == nil || *tmpdir == 0)
02606 tmpdir= "/tmp";
02607
02608
02609 pa= &L_args;
02610 for (i= 1; i < argc; i++) {
02611 if (argv[i][0] == '-' && argv[i][1] == 'v') {
02612 char *a= argv[i]+2;
02613
02614 if (*a == 'n') { a++; action= 1; }
02615 verbose= 2;
02616
02617 if (*a != 0) {
02618 verbose= strtoul(a, &a, 10);
02619 if (*a != 0) usage();
02620 }
02621 } else
02622 if (strcmp(argv[i], "-name") == 0) {
02623 if (++i == argc) usage();
02624 program= argv[i];
02625 } else
02626 if (strcmp(argv[i], "-descr") == 0) {
02627 if (++i == argc) usage();
02628 descr= argv[i];
02629 } else
02630 if (argv[i][0] == '-' && argv[i][1] == 'T') {
02631 if (argv[i][2] == 0) {
02632 if (++i == argc) usage();
02633 tmpdir= argv[i];
02634 } else
02635 tmpdir= argv[i]+2;
02636 } else {
02637
02638 *pa= cons(CELL, findword(argv[i]));
02639 pa= &(*pa)->cdr;
02640 }
02641 }
02642 #ifndef DESCR
02643
02644 if (descr == nil) descr= program;
02645 #else
02646
02647 if (descr == nil) descr= DESCR;
02648 #endif
02649
02650 inittemp(tmpdir);
02651
02652
02653 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, interrupt);
02654 if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, interrupt);
02655 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, interrupt);
02656
02657
02658 predef("PROGRAM", program);
02659 predef("VERSION", version);
02660 #ifdef ARCH
02661 predef("ARCH", ARCH);
02662 #endif
02663 V_star= predef("*", nil);
02664 V_in= predef("<", nil);
02665 V_out= predef(">", nil);
02666
02667
02668 if (verbose >= 3) printf("include %s\n", descr);
02669 prog= get_prog();
02670
02671 phase= INIT;
02672 pc= prog;
02673 execute(DOIT, 0);
02674
02675 argscan();
02676 compile();
02677
02678
02679 while (prog != nil) {
02680 program_t *junk= prog;
02681 prog= junk->next;
02682 dec(junk->file);
02683 dec(junk->line);
02684 deallocate(junk);
02685 }
02686 while (rules != nil) {
02687 rule_t *junk= rules;
02688 rules= junk->next;
02689 dec(junk->from);
02690 dec(junk->to);
02691 dec(junk->wait);
02692 deallocate(junk);
02693 }
02694 deltemp();
02695 dec(V_stop);
02696 dec(L_args);
02697 dec(L_files);
02698 dec(L_predef);
02699
02700 quit(action == 0 ? 1 : 0);
02701 }