%{ /* Copyright(C) 1990, Marcus J. Ranum, All Rights Reserved. This software may be freely used, modified, and redistributed, as long as this copyright message is left intact, and this software is not used to develop any commercial product, or used in any product that is provided on a pay-for-use basis. */ #include <stdio.h> #include <ctype.h> /* turn this on to run files through your C-preprocessor */ #define USECPP #define DEFAULTCPP "/lib/cpp -P" int infunct = 0; int yylineno = 1; int yyerrcnt = 0; int yywarcnt = 0; char lexbuf[BUFSIZ]; char *fname; FILE *yyin = stdin; %} %token NUM STR IDENT %token OUTOFSPACE BLTIN %token IF IN FOR FORARG ELSE FUNC RETURN VNULL %right ASGN %left REF %left OR %left AND %left GT GTE LT LTE EQ NE %left ADD SUB %left MUL DIV %right UNARY %left NOT %% program: /* nothing */ | program statement | program funcdef | program error ; asgn: '$' IDENT ASGN expr | MUL element ASGN expr %prec UNARY | element ASGN expr ; funcdef: funcheader '@' '.' IDENT statement { infunct = 0; } | funcheader '#' NUM '.' IDENT statement { infunct = 0; } | funcheader '#' NUM '.' error { yyerror("invalid name field in function declaration"); infunct = 0; } | funcheader '#' NUM error { yyerror("function declaration missing '.' specifier"); infunct = 0; } | funcheader '#' error { yyerror("function declaration missing object number"); infunct = 0; } | funcheader '@' error { yyerror("invalid name specifier in system function declaration"); infunct = 0; } | funcheader error { yyerror("function declaration name incorrecly specified"); infunct = 0; } ; funcheader: FUNC { infunct = 1; } ; statement: expr ';' | RETURN ';' | RETURN expr ';' | ';' | IF condition statement | IF condition statement ELSE statement | FOR '$' IDENT IN condition statement | FORARG '$' IDENT statement | '{' statements '}' ; statements: /* nothing */ | statements statement ; condition: '(' expr ')' ; refnum: '@' | '#' NUM | '#' IDENT | '#' error { yyerror("illegal object number specifier after \"#\""); } ; element_head: refnum | '$' IDENT | '$' NUM { if(!infunct) yyerror("cannot use $ NUM outside of a function"); } ; element: element_head '.' IDENT | element_head '.' '(' expr ')' | element '.' IDENT | element '.' '(' expr ')' | element '.' error { yyerror("illegal name specifier for element"); } ; evaluated_element: element | '$' IDENT | '$' NUM { if(!infunct) yyerror("cannot use $ NUM outside of a function"); } | '$' error { yyerror("illegal use of \"$\" to form a variable"); } ; expr: NUM | STR | VNULL | evaluated_element | refnum | '$' '#' { if(!infunct) { yyerror("cannot use $# outside of a function"); } } | asgn | BLTIN '(' arglist ')' | BLTIN error { yyerror("malformed call to built-in function"); } | evaluated_element '(' arglist ')' | '(' expr ')' | expr ADD expr | expr SUB expr | expr MUL expr | expr DIV expr | SUB expr %prec UNARY | REF element %prec UNARY | REF error { yyerror("malformed reference"); } | expr EQ expr | expr NE expr | expr AND expr | expr OR expr | expr LT expr | expr LTE expr | expr GT expr | expr GTE expr | NOT expr %prec UNARY ; arglist: /* nothing */ | expr | arglist ',' expr | arglist error { yyerror("malformed function parameter list"); } ; %% int lookup(s) char *s; { int start = 0; int ret; static struct kwordz { char *kw; int rval; } keyz[] = { "NULL", VNULL, "atoi", BLTIN, "atoobj", BLTIN, "catfile", BLTIN, "chmod", BLTIN, "chown", BLTIN, "echo", BLTIN, "echoto", BLTIN, "else", ELSE, "errno", BLTIN, "error", BLTIN, "foreach", FOR, "foreacharg", FORARG, "func", FUNC, "geteuid", BLTIN, "getuid", BLTIN, "if", IF, "in", IN, "islist", BLTIN, "isnum", BLTIN, "isobj", BLTIN, "isstr", BLTIN, "listadd", BLTIN, "listappend", BLTIN, "listcount", BLTIN, "listdrop", BLTIN, "listelem", BLTIN, "listmerge", BLTIN, "listnew", BLTIN, "listprepend", BLTIN, "listsearch", BLTIN, "listsetelem", BLTIN, "log", BLTIN, "match", BLTIN, "objectdestroy", BLTIN, "objectelements", BLTIN, "objectnew", BLTIN, "objectowner", BLTIN, "random", BLTIN, "return", RETURN, "setuid", BLTIN, "str", BLTIN, "strlen", BLTIN, "strtime", BLTIN, "time", BLTIN, 0,0 }; int end = (sizeof(keyz)/sizeof(struct kwordz)) - 2; int p = end/2; while(start <= end) { ret = strcmp(s,keyz[p].kw); if(ret == 0) return(keyz[p].rval); if(ret > 0) start = p + 1; else end = p - 1; p = start + ((end - start)/2); } return(-1); } /* THIS IS NOT THE SAME LEXICAL ANALYSER AS IN THE U-COMPILER! Mix them and you will DIE! */ yylex() { char *p = lexbuf; int c; *p = '\0'; /* handle whitespace */ while(isspace(c = fgetc(yyin))) if(c == '\n') yylineno++; /* handle EOF */ if(c == EOF) { return(0); } /* save current char - it is valuable */ *p++ = c; *p = '\0'; /* handle NUM */ if(isdigit(c)) { int num; num = c - '0'; while(isdigit(c = fgetc(yyin))) num = (num * 10) + (c - '0'); (void)ungetc(c,yyin); if(c == '\n') yylineno--; yylval = num; return(NUM); } /* handle keywords or idents/builtins */ if(isalpha(c) || c == '_') { int cnt = 0; int rv; int bltin; while((c = fgetc(yyin)) != EOF && (isalnum(c) || c == '_')) { if(++cnt + 1 >= 20) yyerror("identifier too long"); *p++ = c; } (void)ungetc(c,yyin); *p = '\0'; if((rv = lookup(lexbuf)) != -1) return(rv); return(IDENT); } /* handle quoted strings */ if(c == '"' || c == '\'') { int cnt = 0; int quot = c; /* strip start quote by resetting ptr */ p = lexbuf; *p = '\0'; /* match quoted strings */ while((c = fgetc(yyin)) != EOF && c != quot) { if(!isascii(c)) continue; if(++cnt + 1 >= sizeof(lexbuf)) yyerror("string too long"); /* we have to guard the line count */ if(c == '\n') yylineno++; if(c == '\\') { int b; switch(b = fgetc(yyin)) { case EOF: yyerror("EOF in quoted string"); break; case 't': case ';': case '\'': case '"': case 'n': break; default: yywarning("unrecognized escape"); } } else { *p++ = c; } } if(c == EOF) yyerror("EOF in quoted string"); *p = '\0'; return(STR); } switch(c) { case '=': { int t; t = fgetc(yyin); if(t != '=') { (void)ungetc(t,yyin); return(ASGN); } *p++ = t; *p++ = '\0'; } return(EQ); case '>': { int t; t = fgetc(yyin); if(t != '=') { (void)ungetc(t,yyin); return(GT); } *p++ = t; *p++ = '\0'; } return(GTE); case '<': { int t; t = fgetc(yyin); if(t != '=') { (void)ungetc(t,yyin); return(LT); } *p++ = t; *p++ = '\0'; } return(LTE); case '!': { int t; t = fgetc(yyin); if(t != '=') { (void)ungetc(t,yyin); return(NOT); } *p++ = t; *p++ = '\0'; } return(NE); case '&': { int t; t = fgetc(yyin); if(t != '&') { (void)ungetc(t,yyin); return(REF); } *p++ = t; *p++ = '\0'; } return(AND); case '|': { int t; t = fgetc(yyin); if(t != '|') { (void)ungetc(t,yyin); return(t); } *p++ = t; *p++ = '\0'; } return(OR); case '/': return(DIV); case '*': return(MUL); case '-': return(SUB); case '+': return(ADD); } /* punt */ if(c == '\n') yylineno++; *p = '\0'; return(c); } yyerror(s) char *s; { fprintf(stderr,"%s:[line %d] Error: %s",fname,yylineno,s); if(lexbuf[0] != '\0'); fprintf(stderr," near \"%s\"",lexbuf); fprintf(stderr,"\n"); yyerrcnt++; } yywarning(s) char *s; { fprintf(stderr,"%s:[line %d] Warning: %s",fname,yylineno,s); if(lexbuf[0] != '\0'); fprintf(stderr," near \"%s\"",lexbuf); fprintf(stderr,"\n"); yywarcnt++; } #ifdef USECPP /* run a file through the pre-processor and return 0 or the resulting file name */ char * preprocess_file(f) char *f; { extern char *mktemp(); static char *tmpf = (char *)0; int sval; char cbuf[600]; if(tmpf == (char *)0) { if((tmpf = mktemp("/tmp/usynXXXXXX")) == NULL) return(NULL); } (void)strcpy(cbuf,DEFAULTCPP); (void)strcat(cbuf," "); (void)strcat(cbuf,f); (void)strcat(cbuf," "); (void)strcat(cbuf,tmpf); sval = system(cbuf); if(sval == 127) { fprintf(stderr,"cannot invoke c-preprocessor %s!\n",DEFAULTCPP); return((char *)0); } if(sval != 0) return((char *)0); return(tmpf); } #endif crunch_file(name,ifd) char *name; FILE *ifd; { if(ifd == NULL) return(0); yyin = ifd; fname = name; yylineno = 1; yyerrcnt = yywarcnt = 0; while(yyparse()) ; fprintf(stderr,"%s:%d errors, %d warnings\n",fname,yyerrcnt,yywarcnt); return(yyerrcnt); } main(ac,av) int ac; char *av[]; { int acx; int rv = 0; if(ac == 1) exit(crunch_file("standard input",stdin)); for(acx = 1; acx < ac; acx++) { #ifdef USECPP char *tmp; if((tmp = preprocess_file(av[acx])) == (char *)0) { perror(av[acx]); rv++; continue; } if((yyin = fopen(tmp,"r")) == NULL) { perror(tmp); (void)unlink(tmp); rv++; continue; } rv += crunch_file(av[acx],yyin); (void)unlink(tmp); #else if((yyin = fopen(av[acx],"r")) == NULL) { perror(av[acx]); rv++; continue; } rv += crunch_file(av[acx],yyin); #endif } exit(rv); }