From: Eric S. Raymond Date: Mon, 15 Nov 1993 18:04:00 +0000 (+0000) Subject: Initial revision X-Git-Tag: 2.0~13 X-Git-Url: http://thyrsus.com/gitweb/?a=commitdiff_plain;h=5482b475a84134f278dd8d1ed7e7e6248f6e5ee9;p=cstrings.git Initial revision --- 5482b475a84134f278dd8d1ed7e7e6248f6e5ee9 diff --git a/cstrings.l b/cstrings.l new file mode 100644 index 0000000..f40c388 --- /dev/null +++ b/cstrings.l @@ -0,0 +1,319 @@ +%{ +/* + * cstrings.c -- quick-and-dirty internationalization helper + * + * by Eric S. Raymond , July 10 1990 + * + * Note: this code will not run unaltered on Berkeley UNIXes that don't have + * a System V compatibility suite. If this bothers you, get a real UNIX. + */ +#include +#include +#include +#include + +typedef int bool; +#define TRUE 1 +#define FALSE 0 + +static bool skipline = FALSE, checkmode = FALSE, incomment = FALSE; +static char *ranges, *template = "G%05d"; +static int minlength = 3; +static FILE *listfp; + +/* this bizarreness avoids dangling-else problems with the macroexpansion */ +#undef output +#define output(c) (checkmode || putc(c,yyout)) +#undef ECHO +#define ECHO (checkmode || fputs(yytext, yyout)) + +/* + * The VIS class is intended to include all printables except the string quote. + * EBCDIC sites can eat flaming death, thank you. Have a nice day. + */ +%} +VIS [!#-~ ] +QUOTE \" +%% +^#.*\n {ECHO;} +\/\* {ECHO; incomment = TRUE;} +\*\/ {ECHO; incomment = FALSE;} +\\{QUOTE} {ECHO;} +{QUOTE}{VIS}*{QUOTE} { + if (!inrange(yylineno,ranges) || reject(yytext) || incomment) + ECHO; + else + { + if (checkmode) + (void) printf("%d: %s\n", yylineno,yytext); + else + { + char label[BUFSIZ]; + static int lnum = 1; + + (void) sprintf(label, template, lnum); + (void) fputs(label, yyout); + (void) fprintf(listfp, + "#define %s\t%s\n", + label, yytext); + lnum++; + } + } + } +. {ECHO;} +%% +yywrap() {} + +int inrange(num, cp) +int num; +char *cp; +{ +#define S_COMMA 0 /* low end of a range has been gotten */ +#define S_NUM 1 /* comma after a number has been seen */ +#define S_DASH 2 /* dash has been seen */ +#define S_EOL 3 /* end-of-line seen */ +#define S_SOL 4 /* start-of-line state */ + + static int action[4][5] + = {{0,1,0,0,0}, {0,0,2,0,0}, {0,3,0,0,4}, {0,5,6,0,0}}; + + int toktype, state; + int lo = 0; + int val = 0; + int i; + bool dflt, bang, in; + + if (cp == (char *)NULL) + return(TRUE); + + dflt = (cp[0] != '!'); + bang = FALSE; + in = FALSE; + + for (state = S_SOL; state != S_EOL; state = toktype) + { + /* + * Here's the lexical analyzer + */ + while (*cp && isspace(*cp)) /* skip leading whitespace */ + cp++; + + /* look for a token */ + if (*cp == '\0') + { + toktype = S_EOL; + } + else if (*cp == '!') /* a complement mark */ + { + bang = dflt; + cp++; + } + else if (*cp == ',') /* a comma (ends current range) */ + { + toktype = S_COMMA; + cp++; + } + else if (*cp == '-') /* a dash (flags a real range) */ + { + toktype = S_DASH; + cp++; + } + else if (isdigit(*cp)) /* or a digit sequence */ + { + val = atoi(cp); + toktype = S_NUM; + while (isdigit(*cp)) + cp++; + } + else /* anything else is an illegal char */ + return(FALSE); + + /* + * Here's the state machine that does the grammar processing + */ + switch(action[toktype][state]) + { + case 1: /* number followed by comma */ + if (num == val) + in = !bang; + bang = FALSE; + break; + + case 2: /* dash followed by number */ + if (num >= lo && num <= val) + in = !bang; + break; + + case 3: /* number followed by dash */ + lo = val; + break; + + case 4: /* start-of-field followed by dash */ + lo = 1; + break; + + case 5: /* digit followed by end-of-field */ + if (num == val) + in = !bang; + break; + + case 6: /* dash followed by end-of-field */ + if (num >= lo) + in = !bang; + break; + } + } + return(in == dflt); +} + +int reject(str) +char *str; +{ + /* + * Whether to add more filtering options here + * is an interesting question in design + * philosophy... + */ + return(minlength && (strlen(str) - 2 < minlength)); +} + +void cstrfilt(ifp, ofp) +/* filter yyin to stdout, doing the conversion */ +FILE *ifp, *ofp; +{ + char buf[BUFSIZ]; + + yyin = ifp; + if (!checkmode) + { + if ((listfp = tmpfile()) == (FILE *)NULL) + { + perror("cstrings, opening list tempfile"); + exit(1); + } + + if ((yyout = tmpfile()) == (FILE *)NULL) + { + perror("cstrings, opening source tempfile"); + exit(1); + } + } + + yylex(); + + if (!checkmode) + { + /* dump the generated #define list */ + (void) fseek(listfp, 0L, 0); + while (fgets(buf, BUFSIZ, listfp) != (char *)NULL) + (void) fputs(buf, ofp); + (void) fclose(listfp); + + /* now dump the filtered C text */ + (void) fseek(yyout, 0L, 0); + while (fgets(buf, BUFSIZ, yyout) != (char *)NULL) + (void) fputs(buf, ofp); + (void) fclose(yyout); + } +} + +main(argc, argv) +int argc; +char *argv[]; +{ + extern char *optarg; /* set by getopt */ + extern int optind; /* set by getopt */ + extern int yydebug; + + extern FILE *tmpfile(); + int status; + + while ((status = getopt(argc, argv, "cm:r:t:v")) != EOF) + { + switch (status) + { + case 'c': + checkmode = TRUE; + break; + + case 'm': + minlength = atoi(optarg); + break; + + case 'r': + ranges = optarg; + break; + + case 't': + template = optarg; + break; + + default: + (void) fprintf(stderr, + "cstrings [-c] [-r ranges] [-t template] [-m minlength]\n"); + exit(0); + } + } + + if (optind >= argc) + cstrfilt(stdin, stdout); + else + for (; optind < argc; optind++) + { + char buf[BUFSIZ], *cp, *tf; + FILE *ifp, *ofp; + + if ((ifp = fopen(argv[optind], "r")) == (FILE *)NULL) + { + perror("cstrings, opening input file"); + exit(1); + } + + if (checkmode) + (void) printf("%s:\n", argv[optind]); + else + { + if (argv[optind][0] != '/') + (void) getcwd(buf, BUFSIZ); + else + buf[0] = '\0'; + (void) strcat(buf, argv[optind]); + if (cp = strrchr(buf, '/')) + *cp = '\0'; + if ((tf = tempnam(buf, "cstr")) == (char *)NULL) + { + perror("cstrings, making tempfile"); + exit(1); + } + + if ((ofp = fopen(tf, "w")) == (FILE *)NULL) + { + perror("cstrings, making output file"); + exit(1); + } + } + + cstrfilt(ifp, ofp); + + (void) fclose(ifp); + + if (!checkmode && access(argv[optind], W_OK) == 0) + { + (void) fclose(ofp); + (void) unlink(argv[optind]); + if (link(tf, argv[optind]) < 0) /* that magic moment... */ + { + perror("cstrings, linking converted form to original"); + exit(1); + } + } + } +} + +/* + The following sets edit modes for GNU EMACS + Local Variables: + mode:c + End: +*/ +/* cstrings.l ends here */