%{ /* xid.l - extract identifiers from c source Steve Simon 2005 */ #include #undef YYLMAX #define YYLMAX (1024 * 16) #define FUNC_CALL 1 #define FUNC_DEF 2 struct { /* last function name found */ int line; int fnum; int brace; char *name; } func; int nolvars = 0; /* don't extract info from variables */ int nocomments = 0; /* don't extract info from comments */ Biobuf *symb; /* symbols index */ Biobuf *file; /* filenames index */ int fnum = 0; /* file "number" */ int incomment = 0; /* inside a comment */ int line = 0; /* current line number */ int brace = 0; /* nesting level of braces */ int gotid = 0; /* have a buffered identifier */ char *infile = "stdin"; /* current file name */ char *prefix = NULL; /* prefix to remove from filenames */ int yywrap(void); void vardef(void); void varuse(void); void charconst(void); void keepfun(void); void outfun(int); void outdef(void); void getstring(void); void getcomment(void); void xid(Biobuf *); %} %p 3000 letter [A-Za-z_] digit [0-9] ws [ \t] %% const | volatile | void | int | char | float | double | struct | union | long | short | unsigned | auto | extern | register | typedef | static | goto | return{ws}*\( | return | sizeof | sizeof{ws}*\( | break | continue | if{ws}*\( | else | for{ws}*\( | do | while{ws}*\( | switch{ws}*\( | case | default | entry | enum | define | undef | ifdef | ifndef | include | defined{ws}*\( | endif { ; } "\"" { getstring(); } "\n" { line++; } {letter}({letter}|{digit})*{ws}*\( { keepfun(); } /* function */ ";" { outfun(';'); } "{" { outfun('{'); brace++; } "}" { brace--; } "/*" { getcomment(); } ^#{ws}*line.*$ ; /* #line */ ^#{ws}*include.*$ ; /* #include */ ^#{ws}*define{ws}+{letter}({letter}|{digit})* { outdef(); } /* #define */ {letter}({letter}|{digit})*{ws}+{letter}({letter}|{digit})*{ws}*[;,] { vardef(); } /* variable definition */ {letter}({letter}|{digit})* { varuse(); } /* variable use */ '[^\\']' | '\\{digit}{1,3}' | '\\[\\bfrnlt"']' { charconst(); }; /* character constant */ . ; /* delete everything else */ %% int yywrap(void) { return(1); } void vardef(void) /* variable definition */ { if (brace == 0) Bprint(symb, "g %d %d %s\n", fnum, line, yytext); else Bprint(symb, "l %d %d %s\n", fnum, line, yytext); } void varuse(void) /* variable use */ { if (!nolvars) Bprint(symb, "v %d %d %s\n", fnum, line, yytext); } void charconst(void) /* character constant */ { Bprint(symb, "c %d %d %s\n", fnum, line, yytext); } void keepfun(void) /* rembember function, definition or call */ { int i; if ((i = strlen(yytext)) > 1) /* trim bracket */ yytext[i -1] = 0; func.fnum = fnum; func.line = line; func.brace = brace; if (func.name) free(func.name); func.name = strdup(yytext); if (func.name == nil) sysfatal("no memory\n"); } void outfun(int c) /* function call definition, or prototype */ { if (func.line == 0) return; if (brace) Bprint(symb, "f %d %d %s\n", func.fnum, func.line, func.name); else if (c == '{') Bprint(symb, "F %d %d %s\n", func.fnum, func.line, func.name); else Bprint(symb, "P %d %d %s\n", func.fnum, func.line, func.name); func.line = 0; } void outdef(void) /* #define */ { int i; char *p; /* trim op to last first whitespace */ if ((i = strlen(yytext)) > 1) { for (p = &yytext[i -1]; *p != ' ' && *p != '\t' && *p; p--) continue; p++; Bprint(symb, "d %d %d %s\n", fnum, line, p); } } void getstring(void) { char c; int p = 0; Bprint(symb, "s %d %d ", fnum, line); while (1) { switch (c = input()) { case '\n': line++; case '\r': case '\b': case '\f': case '\t': case ' ' : if (p == 0) continue; c = ' '; break; case 0: fprint(2, "%s:%d EOF in string\n", infile, line); goto fini; case '"': if (p != '\\') goto fini; break; default: break; } if (p != ' ' || c != ' ') Bputc(symb, c); if (p == '\\' && c == '\\') /* kludge around the string \\" */ p = '#'; else p = c; } fini: Bputc(symb, '\n'); } void getcomment(void) { char c, p = 0; Bprint(symb, "x %d %d ", fnum, line); while (1) { switch (c = input()) { case '\n': line++; case '\r': case '\b': case '\f': case '\t': case ' ' : if (p == 0) continue; c = ' '; break; case 0: fprint(2, "%s:%d EOF in comment\n", infile, line); goto fini; case '*': p = c; continue; case '/': if (p == '*') goto fini; break; default: break; } if (p == '*') /* catchup delayed '*' */ Bputc(symb, p); if (!(p == ' ' && c == ' ')) /* colapse multiple spaces into one */ Bputc(symb, c); p = c; } fini: Bputc(symb, '\n'); } void xid(Biobuf *bi) { int l; char *p, *buf; l = (prefix)? strlen(prefix): 0; while ((buf = Brdline(bi, '\n')) != NULL) { buf[Blinelen(bi)-1] = 0; if ((p = strchr(buf, '\n')) != NULL) *p = 0; if ((infd = open(buf, OREAD)) == NULL) { fprint(2, "%s can't open\n", buf); continue; } if (prefix && strncmp(buf, prefix, l) == 0) Bprint(file, "%d %s\n", ++fnum, buf+l); else Bprint(file, "%d %s\n", ++fnum, buf); infile = buf; line = 1; incomment = 0; brace = 0; yylex(); close(infd); } } void usage(void) { fprint(2, "usage: %s [-vc] [-p path] [index-base]\n", argv0); exits("usage"); } void main(int argc, char *argv[]) { Biobuf bin; char *fi, *sy; ARGBEGIN { case 'c': nocomments = 1; break; case 'v': nolvars = 1; break; case 'p': prefix = EARGF(usage()); break; default: usage(); }ARGEND; if (argc > 1) usage(); if (argc == 0) { fi = "files.idx"; sy = "symbs.idx"; } else { fi = smprint("%s/files.idx", argv[0]); sy = smprint("%s/symbs.idx", argv[0]); } if (fi == nil || sy == nil) sysfatal("no memory"); if ((file = Bopen(fi, OWRITE)) == NULL) sysfatal("%s can't open for writing", fi); if ((symb = Bopen(sy, OWRITE)) == NULL) sysfatal("%s can't open for writing", sy); Binit(&bin, 0, OREAD); xid(&bin); Bterm(symb); Bterm(file); exits(0); }