--- /dev/null
+%{
+/*
+ * cstrings.c -- quick-and-dirty internationalization helper
+ *
+ * by Eric S. Raymond <esr@snark.thyrsus.com>, 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 <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+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 */