]> thyrsus.com Git - cstrings.git/commitdiff
Initial revision
authorEric S. Raymond <esr@thyrsus.com>
Mon, 15 Nov 1993 18:04:00 +0000 (18:04 +0000)
committerEric S. Raymond <esr@thyrsus.com>
Mon, 15 Nov 1993 18:04:00 +0000 (18:04 +0000)
cstrings.l [new file with mode: 0644]

diff --git a/cstrings.l b/cstrings.l
new file mode 100644 (file)
index 0000000..f40c388
--- /dev/null
@@ -0,0 +1,319 @@
+%{
+/*
+ * 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 */