home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / gettext-0.10.24-src.tgz / tar.out / fsf / gettext / src / xget-lex.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  28KB  |  1,282 lines

  1. /* GNU gettext - internationalization aids
  2.    Copyright (C) 1995, 1996 Free Software Foundation, Inc.
  3.  
  4.    This file was written by Peter Miller <pmiller@agso.gov.au>
  5.  
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  19.  
  20. #ifdef HAVE_CONFIG_H
  21. # include "config.h"
  22. #endif
  23.  
  24. #include <ctype.h>
  25. #include <errno.h>
  26. #include <stdio.h>
  27.  
  28. #ifdef STDC_HEADERS
  29. # include <stdlib.h>
  30. #endif
  31.  
  32. #include "dir-list.h"
  33. #include "error.h"
  34. #include "system.h"
  35. #include "libgettext.h"
  36. #include "str-list.h"
  37. #include "xget-lex.h"
  38.  
  39. #ifndef errno
  40. extern int errno;
  41. #endif
  42.  
  43. #define _(s) gettext(s)
  44.  
  45.  
  46. /* The ANSI C standard defines several phases of translation:
  47.  
  48.    1. Terminate line by \n, regardless of the external representation
  49.       of a text line.  Stdio does this for us.
  50.  
  51.    2. Convert trigraphs to their single character equivalents.
  52.  
  53.    3. Concatenate each line ending in backslash (\) with the following
  54.       line.
  55.  
  56.    4. Replace each comment with a space character.
  57.  
  58.    5. Parse each resulting logical line as preprocessing tokens a
  59.       white space.
  60.  
  61.    6. Recognise and carry out directives (it also expands macros on
  62.       non-directive lines, which we do not do here).
  63.  
  64.    7. Replaces escape sequences within character strings with their
  65.       single character equivanents (we do this in step 5, because we
  66.       dont have to worry about the #include argument).
  67.  
  68.    8. Concatenates adjacent string literals to form single string
  69.       literals (because we don't expand macros, there are a few things
  70.       we will miss).
  71.  
  72.    9. Converts the remaining preprocessing tokens to C tokens and
  73.       discards any white space from the translation unit.
  74.  
  75.    This lexer implements the above, and presents the scanner (in
  76.    xgettext.c) with a stream of C tokens.  The comments are
  77.    accumulated in a buffer, and given to xgettext when asked for.  */
  78.  
  79. enum token_type_ty
  80. {
  81.   token_type_character_constant,
  82.   token_type_eof,
  83.   token_type_eoln,
  84.   token_type_hash,
  85.   token_type_lp,
  86.   token_type_comma,
  87.   token_type_name,
  88.   token_type_number,
  89.   token_type_string_literal,
  90.   token_type_symbol,
  91.   token_type_white_space
  92. };
  93. typedef enum token_type_ty token_type_ty;
  94.  
  95. typedef struct token_ty token_ty;
  96. struct token_ty
  97. {
  98.   token_type_ty type;
  99.   char *string;
  100.   long number;
  101.   int line_number;
  102. };
  103.  
  104.  
  105. static const char *file_name;
  106. static char *logical_file_name;
  107. static int line_number;
  108. static FILE *fp;
  109. static int trigraphs;
  110. static int cplusplus_comments;
  111. static string_list_ty *comment;
  112. static string_list_ty *keywords;
  113. static int default_keywords = 1;
  114.  
  115. /* These are for tracking whether comments count as immediately before
  116.    keyword.  */
  117. static int last_comment_line = -1;
  118. static int last_non_comment_line = -1;
  119. static int newline_count = 0;
  120.  
  121.  
  122. /* Prototypes for lcoal functions.  */
  123. static int phase1_getc PARAMS ((void));
  124. static void phase1_ungetc PARAMS ((int __c));
  125. static int phase2_getc PARAMS ((void));
  126. static void phase2_ungetc PARAMS ((int __c));
  127. static int phase3_getc PARAMS ((void));
  128. static void phase3_ungetc PARAMS ((int __c));
  129. static int phase4_getc PARAMS ((void));
  130. static void phase4_ungetc PARAMS ((int __c));
  131. static int phase7_getc PARAMS ((void));
  132. static void phase7_ungetc PARAMS ((int __c));
  133. static void phase5_get PARAMS ((token_ty *__tp));
  134. static void phase5_unget PARAMS ((token_ty *__tp));
  135. static void phaseX_get PARAMS ((token_ty *__tp));
  136. static void phase6_get PARAMS ((token_ty *__tp));
  137. static void phase6_unget PARAMS ((token_ty *__tp));
  138. static void phase8_get PARAMS ((token_ty *__tp));
  139.  
  140.  
  141.  
  142. void
  143. xgettext_lex_open (fn)
  144.      const char *fn;
  145. {
  146.   char *new_name;
  147.  
  148.   if (strcmp (fn, "-") == 0)
  149.     {
  150.       new_name = xstrdup (_("standard input"));
  151.       logical_file_name = xstrdup (new_name);
  152.       fp = stdin;
  153.     }
  154.   else if (*fn == '/')
  155.     {
  156.       new_name = xstrdup (fn);
  157.       fp = fopen (fn, "r");
  158.       if (fp == NULL)
  159.     error (EXIT_FAILURE, errno, _("\
  160. error while opening \"%s\" for reading"), fn);
  161.       logical_file_name = xstrdup (new_name);
  162.     }
  163.   else
  164.     {
  165.       size_t len1, len2;
  166.       int j;
  167.       const char *dir;
  168.  
  169.       len2 = strlen (fn);
  170.       for (j = 0; ; ++j)
  171.     {
  172.       dir = dir_list_nth (j);
  173.       if (dir == NULL)
  174.         error (EXIT_FAILURE, ENOENT, _("\
  175. error while opening \"%s\" for reading"), fn);
  176.  
  177.       if (dir[0] =='.' && dir[1] == '\0')
  178.         new_name = xstrdup (fn);
  179.       else
  180.         {
  181.           len1 = strlen (dir);
  182.           new_name = xmalloc (len1 + len2 + 2);
  183.           stpcpy (stpcpy (stpcpy (new_name, dir), "/"), fn);
  184.         }
  185.  
  186.       fp = fopen (new_name, "r");
  187.       if (fp != NULL)
  188.         break;
  189.  
  190.       if (errno != ENOENT)
  191.         error (EXIT_FAILURE, errno, _("\
  192. error while opening \"%s\" for reading"), new_name);
  193.       free (new_name);
  194.     }
  195.  
  196.       /* Note that the NEW_NAME variable contains the actual file name
  197.      and the logical file name is what is reported by xgettext.  In
  198.      this case NEW_NAME is set to the file which was found along the
  199.      directory search path, and LOGICAL_FILE_NAME is is set to the
  200.      file name which was searched for.  */
  201.       logical_file_name = xstrdup (fn);
  202.     }
  203.  
  204.   file_name = new_name;
  205.   line_number = 1;
  206. }
  207.  
  208.  
  209. void
  210. xgettext_lex_close ()
  211. {
  212.   if (fp != stdin)
  213.     fclose (fp);
  214.   free ((char *) file_name);
  215.   free (logical_file_name);
  216.   fp = NULL;
  217.   file_name = NULL;
  218.   logical_file_name = NULL;
  219.   line_number = 0;
  220. }
  221.  
  222.  
  223. /* 1. Terminate line by \n, regardless of the external representation of
  224.    a text line.  Stdio does this for us, we just need to check that
  225.    there are no I/O errors, and cope with potentially 2 chacarters of
  226.    pushback, not just the one that ungetc can cope with.  */
  227.  
  228. /* Maximum used guaranteed to be < 4.  */
  229. static unsigned char phase1_pushback[4];
  230. static int phase1_pushback_length;
  231.  
  232.  
  233. static int
  234. phase1_getc ()
  235. {
  236.   int c;
  237.  
  238.   if (phase1_pushback_length)
  239.     {
  240.       c = phase1_pushback[--phase1_pushback_length];
  241.       if (c == '\n')
  242.     ++line_number;
  243.       return c;
  244.     }
  245.   while (1)
  246.     {
  247.       c = getc (fp);
  248.       switch (c)
  249.     {
  250.     case EOF:
  251.       if (ferror (fp))
  252.         {
  253.         bomb:
  254.           error (EXIT_FAILURE, errno, _("\
  255. error while reading \"%s\""), file_name);
  256.         }
  257.       return EOF;
  258.  
  259.     case '\n':
  260.       ++line_number;
  261.       return '\n';
  262.  
  263.     case '\\':
  264.       c = getc (fp);
  265.       if (c == EOF)
  266.         {
  267.           if (ferror (fp))
  268.         goto bomb;
  269.           return '\\';
  270.         }
  271.       if (c != '\n')
  272.         {
  273.           ungetc (c, fp);
  274.           return '\\';
  275.         }
  276.       ++line_number;
  277.       break;
  278.  
  279.     default:
  280.       return c;
  281.     }
  282.     }
  283. }
  284.  
  285.  
  286. static void
  287. phase1_ungetc (c)
  288.      int c;
  289. {
  290.   switch (c)
  291.     {
  292.     case EOF:
  293.       break;
  294.  
  295.     case '\n':
  296.       --line_number;
  297.       /* FALLTHROUGH */
  298.  
  299.     default:
  300.       phase1_pushback[phase1_pushback_length++] = c;
  301.       break;
  302.     }
  303. }
  304.  
  305.  
  306. /* 2. Convert trigraphs to their single character equivalents.  Most
  307.    sane human beings vomit copiously at the mention of trigraphs, which
  308.    is why they are on option.  */
  309.  
  310. /* Maximum used guaranteed to be < 4.  */
  311. static unsigned char phase2_pushback[4];
  312. static int phase2_pushback_length;
  313.  
  314.  
  315. static int
  316. phase2_getc ()
  317. {
  318.   int c;
  319.  
  320.   if (phase2_pushback_length)
  321.     return phase2_pushback[--phase2_pushback_length];
  322.   if (!trigraphs)
  323.     return phase1_getc ();
  324.  
  325.   c = phase1_getc ();
  326.   if (c != '?')
  327.     return c;
  328.   c = phase1_getc ();
  329.   if (c != '?')
  330.     {
  331.       phase1_ungetc (c);
  332.       return '?';
  333.     }
  334.   c = phase1_getc ();
  335.   switch (c)
  336.     {
  337.     case '(':
  338.       return '[';
  339.     case '/':
  340.       return '\\';
  341.     case ')':
  342.       return ']';
  343.     case '\'':
  344.       return '^';
  345.     case '<':
  346.       return '{';
  347.     case '!':
  348.       return '|';
  349.     case '>':
  350.       return '}';
  351.     case '-':
  352.       return '~';
  353.     case '#':
  354.       return '=';
  355.     }
  356.   phase1_ungetc (c);
  357.   phase1_ungetc ('?');
  358.   return '?';
  359. }
  360.  
  361.  
  362. static void
  363. phase2_ungetc (c)
  364.      int c;
  365. {
  366.   if (c != EOF)
  367.     phase2_pushback[phase2_pushback_length++] = c;
  368. }
  369.  
  370.  
  371. /* 3. Concatenate each line ending in backslash (\) with the following
  372.    line.  Basically, all you need to do is elide "\\\n" sequences from
  373.    the input.  */
  374.  
  375. /* Maximum used guaranteed to be < 4.  */
  376. static unsigned char phase3_pushback[4];
  377. static int phase3_pushback_length;
  378.  
  379.  
  380. static int
  381. phase3_getc ()
  382. {
  383.   if (phase3_pushback_length)
  384.     return phase3_pushback[--phase3_pushback_length];
  385.   for (;;)
  386.     {
  387.       int c = phase2_getc ();
  388.       if (c != '\\')
  389.     return c;
  390.       c = phase2_getc ();
  391.       if (c != '\n')
  392.     {
  393.       phase2_ungetc (c);
  394.       return '\\';
  395.     }
  396.     }
  397. }
  398.  
  399.  
  400. static void
  401. phase3_ungetc (c)
  402.      int c;
  403. {
  404.   if (c != EOF)
  405.     phase3_pushback[phase3_pushback_length++] = c;
  406. }
  407.  
  408.  
  409. /* 4. Replace each comment that is not inside a character constant or
  410.    string literal with a space character.  We need to remember the
  411.    comment for later, because it may be attached to a keyword string.
  412.    We also optionally understand C++ comments.  */
  413.  
  414. static int
  415. phase4_getc ()
  416. {
  417.   static char *buffer;
  418.   static size_t bufmax;
  419.   size_t buflen;
  420.   int c;
  421.   int state;
  422.  
  423.   c = phase3_getc ();
  424.   if (c != '/')
  425.     return c;
  426.   c = phase3_getc ();
  427.   switch (c)
  428.     {
  429.     default:
  430.       phase3_ungetc (c);
  431.       return '/';
  432.  
  433.     case '*':
  434.       /* C comment.  */
  435.       buflen = 0;
  436.       state = 0;
  437.       if (comment == NULL)
  438.     comment = string_list_alloc ();
  439.       while (1)
  440.     {
  441.       c = phase3_getc ();
  442.       if (c == EOF)
  443.         break;
  444.       /* We skip all leading white space, but not EOLs.  */
  445.       if (buflen == 0 && isspace (c) && c != '\n')
  446.         continue;
  447.       if (buflen >= bufmax)
  448.         {
  449.           bufmax += 100;
  450.           buffer = xrealloc (buffer, bufmax);
  451.         }
  452.       buffer[buflen++] = c;
  453.       switch (c)
  454.         {
  455.         case '\n':
  456.           --buflen;
  457.           while (buflen >= 1 && (buffer[buflen - 1] == ' '
  458.                      || buffer[buflen - 1] == '\t'))
  459.         --buflen;
  460.           buffer[buflen] = 0;
  461.           string_list_append (comment, buffer);
  462.           buflen = 0;
  463.           state = 0;
  464.           continue;
  465.  
  466.         case '*':
  467.           state = 1;
  468.           continue;
  469.  
  470.         case '/':
  471.           if (state == 1)
  472.         {
  473.           buflen -= 2;
  474.           while (buflen >= 1 && (buffer[buflen - 1] == ' '
  475.                      || buffer[buflen - 1] == '\t'))
  476.             --buflen;
  477.           buffer[buflen] = 0;
  478.           string_list_append (comment, buffer);
  479.           break;
  480.         }
  481.           /* FALLTHROUGH */
  482.  
  483.         default:
  484.           state = 0;
  485.           continue;
  486.         }
  487.       break;
  488.     }
  489.       last_comment_line = newline_count;
  490.       return ' ';
  491.  
  492.     case '/':
  493.       /* C++ comment.  */
  494.       if (!cplusplus_comments)
  495.     {
  496.       phase3_ungetc ('/');
  497.       return '/';
  498.     }
  499.       buflen = 0;
  500.       while (1)
  501.     {
  502.       c = phase3_getc ();
  503.       if (c == '\n' || c == EOF)
  504.         break;
  505.       if (buflen >= bufmax)
  506.         {
  507.           bufmax += 100;
  508.           buffer = xrealloc (buffer, bufmax);
  509.         }
  510.       buffer[buflen++] = c;
  511.     }
  512.       if (buflen >= bufmax)
  513.     {
  514.       bufmax += 100;
  515.       buffer = xrealloc (buffer, bufmax);
  516.     }
  517.       buffer[buflen] = 0;
  518.       if (comment == NULL)
  519.     comment = string_list_alloc ();
  520.       string_list_append (comment, buffer);
  521.       last_comment_line = newline_count;
  522.       return '\n';
  523.     }
  524. }
  525.  
  526.  
  527. static void
  528. phase4_ungetc (c)
  529.      int c;
  530. {
  531.   phase3_ungetc (c);
  532. }
  533.  
  534.  
  535. /* 7. Replace escape sequences within character strings with their
  536.    single character equivanents.  This is called from phase 5, because
  537.    we don't have to worry about the #include argument.  There are
  538.    pathological cases which could bite us (like the DOS directory
  539.    separator), but just pretend it can't happen.  */
  540.  
  541. #define P7_QUOTES (1000 + '"')
  542. #define P7_QUOTE (1000 + '\'')
  543. #define P7_NEWLINE (1000 + '\n')
  544.  
  545. static int
  546. phase7_getc ()
  547. {
  548.   int c, n, j;
  549.  
  550.   /* Use phase 3, because phase 4 elides comments.  */
  551.   c = phase3_getc ();
  552.   if (c == '\n')
  553.     return P7_NEWLINE;
  554.   if (c == '"')
  555.     return P7_QUOTES;
  556.   if (c == '\'')
  557.     return P7_QUOTE;
  558.   if (c != '\\')
  559.     return c;
  560.   c = phase3_getc ();
  561.   switch (c)
  562.     {
  563.     default:
  564.       /* Unknown escape sequences really should be an error, but just
  565.      ignore them, and let the real compiler complain.  */
  566.       phase3_ungetc (c);
  567.       return '\\';
  568.  
  569.     case '"':
  570.     case '\'':
  571.     case '?':
  572.     case '\\':
  573.       return c;
  574.  
  575.       /* The \a and \v escapes were added by the ANSI C Standard.
  576.      Prior to the Standard, most compilers did not have them.
  577.      Because we need the same program on all platforms we don't
  578.      provide support for them here.  */
  579.  
  580.     case 'b':
  581.       return '\b';
  582.     case 'f':
  583.       return '\f';
  584.     case 'n':
  585.       return '\n';
  586.     case 'r':
  587.       return '\r';
  588.     case 't':
  589.       return '\t';
  590.  
  591.     case 'x':
  592.       c = phase3_getc ();
  593.       switch (c)
  594.     {
  595.     default:
  596.       phase3_ungetc (c);
  597.       phase3_ungetc ('x');
  598.       return '\\';
  599.  
  600.     case '0': case '1': case '2': case '3': case '4':
  601.     case '5': case '6': case '7': case '8': case '9':
  602.     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  603.     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  604.       break;
  605.     }
  606.       n = 0;
  607.       for (;;)
  608.     {
  609.       switch (c)
  610.         {
  611.         default:
  612.           phase3_ungetc (c);
  613.           return n;
  614.           break;
  615.  
  616.         case '0': case '1': case '2': case '3': case '4':
  617.         case '5': case '6': case '7': case '8': case '9':
  618.           n = n * 16 + c - '0';
  619.           break;;
  620.  
  621.         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  622.           n = n * 16 + 10 + c - 'A';
  623.           break;
  624.  
  625.         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  626.           n = n * 16 + 10 + c - 'a';
  627.           break;
  628.         }
  629.       c = phase3_getc ();
  630.     }
  631.       return n;
  632.  
  633.     case '0': case '1': case '2': case '3':
  634.     case '4': case '5': case '6': case '7':
  635.       n = 0;
  636.       for (j = 0; j < 3; ++j)
  637.     {
  638.       n = n * 8 + c - '0';
  639.       c = phase3_getc ();
  640.       switch (c)
  641.         {
  642.         default:
  643.           break;
  644.  
  645.         case '0': case '1': case '2': case '3':
  646.         case '4': case '5': case '6': case '7':
  647.           continue;
  648.         }
  649.       break;
  650.     }
  651.       phase3_ungetc (c);
  652.       return n;
  653.     }
  654. }
  655.  
  656.  
  657. static void
  658. phase7_ungetc (c)
  659.      int c;
  660. {
  661.   phase3_ungetc (c);
  662. }
  663.  
  664.  
  665. /* 5. Parse each resulting logical line as preprocessing tokens and
  666.    white space.  Preprocessing tokens and C tokens don't always match.  */
  667.  
  668. /* Maximum used guaranteed to be < 4.  */
  669. static token_ty phase5_pushback[4];
  670. static int phase5_pushback_length;
  671.  
  672.  
  673. static void
  674. phase5_get (tp)
  675.      token_ty *tp;
  676. {
  677.   static char *buffer;
  678.   static int bufmax;
  679.   int bufpos;
  680.   int c;
  681.  
  682.   if (phase5_pushback_length)
  683.     {
  684.       *tp = phase5_pushback[--phase5_pushback_length];
  685.       return;
  686.     }
  687.   tp->string = 0;
  688.   tp->number = 0;
  689.   tp->line_number = line_number;
  690.   c = phase4_getc ();
  691.   switch (c)
  692.     {
  693.     case EOF:
  694.       tp->type = token_type_eof;
  695.       return;
  696.  
  697.     case '\n':
  698.       tp->type = token_type_eoln;
  699.       return;
  700.  
  701.     case ' ':
  702.     case '\f':
  703.     case '\t':
  704.       for (;;)
  705.     {
  706.       c = phase4_getc ();
  707.       switch (c)
  708.         {
  709.         case ' ':
  710.         case '\f':
  711.         case '\t':
  712.           continue;
  713.  
  714.         default:
  715.           phase4_ungetc (c);
  716.           break;
  717.         }
  718.       break;
  719.     }
  720.       tp->type = token_type_white_space;
  721.       return;
  722.  
  723.     case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
  724.     case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
  725.     case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
  726.     case 'V': case 'W': case 'X': case 'Y': case 'Z':
  727.     case '_':
  728.     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
  729.     case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
  730.     case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
  731.     case 'v': case 'w': case 'x': case 'y': case 'z':
  732.       bufpos = 0;
  733.       for (;;)
  734.     {
  735.       if (bufpos >= bufmax)
  736.         {
  737.           bufmax += 100;
  738.           buffer = xrealloc (buffer, bufmax);
  739.         }
  740.       buffer[bufpos++] = c;
  741.       c = phase4_getc ();
  742.       switch (c)
  743.         {
  744.         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
  745.         case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
  746.         case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
  747.         case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
  748.         case 'Y': case 'Z':
  749.         case '_':
  750.         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  751.         case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
  752.         case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
  753.         case 's': case 't': case 'u': case 'v': case 'w': case 'x':
  754.         case 'y': case 'z':
  755.         case '0': case '1': case '2': case '3': case '4':
  756.         case '5': case '6': case '7': case '8': case '9':
  757.           continue;
  758.  
  759.         default:
  760.           phase4_ungetc (c);
  761.           break;
  762.         }
  763.       break;
  764.     }
  765.       if (bufpos >= bufmax)
  766.     {
  767.       bufmax += 100;
  768.       buffer = xrealloc (buffer, bufmax);
  769.     }
  770.       buffer[bufpos] = 0;
  771.       tp->string = xstrdup (buffer);
  772.       tp->type = token_type_name;
  773.       return;
  774.  
  775.     case '.':
  776.       c = phase4_getc ();
  777.       phase4_ungetc (c);
  778.       switch (c)
  779.     {
  780.     default:
  781.       tp->type = token_type_symbol;
  782.       return;
  783.  
  784.     case '0': case '1': case '2': case '3': case '4':
  785.     case '5': case '6': case '7': case '8': case '9':
  786.       c = '.';
  787.       break;
  788.     }
  789.       /* FALLTHROUGH */
  790.  
  791.     case '0': case '1': case '2': case '3': case '4':
  792.     case '5': case '6': case '7': case '8': case '9':
  793.       /* The preprecessing number token is more "generous" than the C
  794.      number tokens.  This is mostly due to token pasting (another
  795.      thing we can ignore here).  */
  796.       bufpos = 0;
  797.       while (1)
  798.     {
  799.       if (bufpos >= bufmax)
  800.         {
  801.           bufmax += 100;
  802.           buffer = xrealloc (buffer, bufmax);
  803.         }
  804.       buffer[bufpos++] = c;
  805.       c = phase4_getc ();
  806.       switch (c)
  807.         {
  808.         case 'e':
  809.         case 'E':
  810.           if (bufpos >= bufmax)
  811.         {
  812.           bufmax += 100;
  813.           buffer = xrealloc (buffer, bufmax);
  814.         }
  815.           buffer[bufpos++] = c;
  816.           c = phase4_getc ();
  817.           if (c != '+' || c != '-')
  818.         {
  819.           phase4_ungetc (c);
  820.           break;
  821.         }
  822.           continue;
  823.  
  824.         case 'A': case 'B': case 'C': case 'D':           case 'F':
  825.         case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
  826.         case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
  827.         case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
  828.         case 'Y': case 'Z':
  829.         case 'a': case 'b': case 'c': case 'd':           case 'f':
  830.         case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
  831.         case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
  832.         case 's': case 't': case 'u': case 'v': case 'w': case 'x':
  833.         case 'y': case 'z':
  834.         case '0': case '1': case '2': case '3': case '4':
  835.         case '5': case '6': case '7': case '8': case '9':
  836.         case '.':
  837.           continue;
  838.  
  839.         default:
  840.           phase4_ungetc (c);
  841.           break;
  842.         }
  843.       break;
  844.     }
  845.       if (bufpos >= bufmax)
  846.     {
  847.       bufmax += 100;
  848.       buffer = xrealloc (buffer, bufmax);
  849.     }
  850.       buffer[bufpos] = 0;
  851.       tp->type = token_type_number;
  852.       tp->number = atol (buffer);
  853.       return;
  854.  
  855.     case '\'':
  856.       /* We could worry about the 'L' before wide character constants,
  857.      but ignoring it has no effect unless one of the keywords is
  858.      "L".  Just pretend it won't happen.  Also, we don't need to
  859.      remember the character constant.  */
  860.       while (1)
  861.     {
  862.       c = phase7_getc ();
  863.       if (c == P7_NEWLINE)
  864.         {
  865.           phase7_ungetc ('\n');
  866.           break;
  867.         }
  868.       if (c == EOF || c == P7_QUOTE)
  869.         break;
  870.     }
  871.       tp->type = token_type_character_constant;
  872.       return;
  873.  
  874.     case '"':
  875.       /* We could worry about the 'L' before wide string constants,
  876.      but since gettext's argument is not a wide character string,
  877.      let the compiler complain about the argument not matching the
  878.      prototype.  Just pretend it won't happen.  */
  879.       bufpos = 0;
  880.       while (1)
  881.     {
  882.       c = phase7_getc ();
  883.       if (c == P7_NEWLINE)
  884.         {
  885.           phase7_ungetc ('\n');
  886.           break;
  887.         }
  888.       if (c == EOF || c == P7_QUOTES)
  889.         break;
  890.       if (c == P7_QUOTE)
  891.         c = '\'';
  892.       if (bufpos >= bufmax)
  893.         {
  894.           bufmax += 100;
  895.           buffer = xrealloc (buffer, bufmax);
  896.         }
  897.       buffer[bufpos++] = c;
  898.     }
  899.       if (bufpos >= bufmax)
  900.     {
  901.       bufmax += 100;
  902.       buffer = xrealloc (buffer, bufmax);
  903.     }
  904.       buffer[bufpos] = 0;
  905.       tp->type = token_type_string_literal;
  906.       tp->string = xstrdup (buffer);
  907.       return;
  908.  
  909.     case '(':
  910.       tp->type = token_type_lp;
  911.       return;
  912.  
  913.     case ',':
  914.       tp->type = token_type_comma;
  915.       return;
  916.  
  917.     case '#':
  918.       tp->type = token_type_hash;
  919.       return;
  920.  
  921.     default:
  922.       /* We could carefully recognize each of the 2 and 3 character
  923.         operators, but it is not necessary, as we only need to recognize
  924.         gettext invokations.  Don't bother.  */
  925.       tp->type = token_type_symbol;
  926.       return;
  927.     }
  928. }
  929.  
  930.  
  931. static void
  932. phase5_unget (tp)
  933.      token_ty *tp;
  934. {
  935.   if (tp->type != token_type_eof)
  936.     phase5_pushback[phase5_pushback_length++] = *tp;
  937. }
  938.  
  939.  
  940. /* X. Recognize a leading # symbol.  Leave leading hash as a hash, but
  941.    turn hash in the middle of a line into a plain symbol token.  This
  942.    makes the phase 6 easier.  */
  943.  
  944. static void
  945. phaseX_get (tp)
  946.      token_ty *tp;
  947. {
  948.   static int middle;
  949.   token_ty tmp;
  950.  
  951.   phase5_get (tp);
  952.   if (middle)
  953.     {
  954.       switch (tp->type)
  955.     {
  956.     case token_type_eoln:
  957.     case token_type_eof:
  958.       middle = 0;
  959.       break;
  960.  
  961.     case token_type_hash:
  962.       tp->type = token_type_symbol;
  963.       break;
  964.  
  965.     default:
  966.       break;
  967.     }
  968.     }
  969.   else
  970.     {
  971.       switch (tp->type)
  972.     {
  973.     case token_type_eoln:
  974.     case token_type_eof:
  975.       break;
  976.  
  977.     case token_type_white_space:
  978.       tmp = *tp;
  979.       phase5_get (tp);
  980.       if (tp->type != token_type_hash)
  981.         {
  982.           phase5_unget (tp);
  983.           *tp = tmp;
  984.           middle = 1;
  985.           return;
  986.         }
  987.  
  988.       /* Discard the leading white space token, the hash is all
  989.          phase 6 is intersted in.  */
  990.       if (tp->type != token_type_eof && tp->type != token_type_eoln)
  991.         middle = 1;
  992.       break;
  993.  
  994.     default:
  995.       middle = 1;
  996.       break;
  997.     }
  998.     }
  999. }
  1000.  
  1001.  
  1002. /* 6. Recognise and carry out directives (it also expands macros on
  1003.    non-directive lines, which we do not do here).  The only directive
  1004.    we care about is the #line directive.  We throw all the others
  1005.    away.  */
  1006.  
  1007. /* Maximum used guaranteed to be < 4.  */
  1008. static token_ty phase6_pushback[4];
  1009. static int phase6_pushback_length;
  1010.  
  1011.  
  1012. static void
  1013. phase6_get (tp)
  1014.      token_ty *tp;
  1015. {
  1016.   static token_ty *buf;
  1017.   static int bufmax;
  1018.   int bufpos;
  1019.   int j;
  1020.  
  1021.   if (phase6_pushback_length)
  1022.     {
  1023.       *tp = phase6_pushback[--phase6_pushback_length];
  1024.       return;
  1025.     }
  1026.   while (1)
  1027.     {
  1028.       /* Get the next token.  If it is not a '#' at the beginning of a
  1029.      line, return immediately.  Be careful of white space.  */
  1030.       phaseX_get (tp);
  1031.       if (tp->type != token_type_hash)
  1032.     return;
  1033.  
  1034.       /* Accumulate the rest of the directive in a buffer.  Work out
  1035.      what it is later.  */
  1036.       bufpos = 0;
  1037.       while (1)
  1038.     {
  1039.       phaseX_get (tp);
  1040.       if (tp->type == token_type_eoln || tp->type == token_type_eof)
  1041.         break;
  1042.  
  1043.       /* White space would be important in the directive, if we
  1044.          were interested in the #define directive.  But we are
  1045.          going to ignore the #define directive, so just throw
  1046.          white space away.  */
  1047.       if (tp->type == token_type_white_space)
  1048.         continue;
  1049.  
  1050.       if (bufpos >= bufmax)
  1051.         {
  1052.           bufmax += 100;
  1053.           buf = xrealloc (buf, bufmax * sizeof (buf[0]));
  1054.         }
  1055.       buf[bufpos++] = *tp;
  1056.     }
  1057.  
  1058.       /* If it is a #line directive, with no macros to expand, act on
  1059.      it.  Ignore all other directives.  */
  1060.       if (bufpos >= 3 && buf[0].type == token_type_name
  1061.       && strcmp (buf[0].string, "line") == 0
  1062.       && buf[1].type == token_type_number
  1063.       && buf[2].type == token_type_string_literal)
  1064.     {
  1065.       free (logical_file_name);
  1066.       logical_file_name = xstrdup (buf[2].string);
  1067.       line_number = buf[1].number;
  1068.     }
  1069.       if (bufpos >= 2 && buf[0].type == token_type_number
  1070.       && buf[1].type == token_type_string_literal)
  1071.     {
  1072.       free (logical_file_name);
  1073.       logical_file_name = xstrdup (buf[1].string);
  1074.       line_number = buf[0].number;
  1075.     }
  1076.  
  1077.       /* Release the storage held by the directive.  */
  1078.       for (j = 0; j < bufpos; ++j)
  1079.     {
  1080.       switch (buf[j].type)
  1081.         {
  1082.         case token_type_name:
  1083.         case token_type_string_literal:
  1084.           free (buf[j].string);
  1085.           break;
  1086.  
  1087.         default:
  1088.           break;
  1089.         }
  1090.     }
  1091.  
  1092.       /* We must reset the selected comments.  */
  1093.       xgettext_lex_comment_reset ();
  1094.     }
  1095. }
  1096.  
  1097.  
  1098. static void
  1099. phase6_unget (tp)
  1100.      token_ty *tp;
  1101. {
  1102.   if (tp->type != token_type_eof)
  1103.     phase6_pushback[phase6_pushback_length++] = *tp;
  1104. }
  1105.  
  1106.  
  1107. /* 8. Concatenate adjacent string literals to form single string
  1108.    literals (because we don't expand macros, there are a few things we
  1109.    will miss).  */
  1110.  
  1111. static void
  1112. phase8_get (tp)
  1113.      token_ty *tp;
  1114. {
  1115.   phase6_get (tp);
  1116.   if (tp->type != token_type_string_literal)
  1117.     return;
  1118.   while (1)
  1119.     {
  1120.       token_ty tmp;
  1121.       size_t len;
  1122.  
  1123.       phase6_get (&tmp);
  1124.       if (tmp.type == token_type_white_space)
  1125.     continue;
  1126.       if (tmp.type == token_type_eoln)
  1127.     continue;
  1128.       if (tmp.type != token_type_string_literal)
  1129.     {
  1130.       phase6_unget (&tmp);
  1131.       return;
  1132.     }
  1133.       len = strlen (tp->string);
  1134.       tp->string = xrealloc (tp->string, len + strlen (tmp.string) + 1);
  1135.       strcpy (tp->string + len, tmp.string);
  1136.       free (tmp.string);
  1137.     }
  1138. }
  1139.  
  1140.  
  1141. /* 9. Convert the remaining preprocessing tokens to C tokens and
  1142.    discards any white space from the translation unit.  */
  1143.  
  1144. void
  1145. xgettext_lex (tp)
  1146.      xgettext_token_ty *tp;
  1147. {
  1148.   while (1)
  1149.     {
  1150.       token_ty token;
  1151.  
  1152.       phase8_get (&token);
  1153.       switch (token.type)
  1154.     {
  1155.     case token_type_eof:
  1156.       newline_count = 0;
  1157.       last_comment_line = -1;
  1158.       last_non_comment_line = -1;
  1159.       tp->type = xgettext_token_type_eof;
  1160.       return;
  1161.  
  1162.     case token_type_white_space:
  1163.       break;
  1164.  
  1165.     case token_type_eoln:
  1166.       /* We have to track the last occurrence of a string.  One
  1167.          mode of xgettext allows to group an extracted message
  1168.          with a comment for documentation.  The rule which states
  1169.          which comment is assumed to be grouped with the message
  1170.          says it should immediately precede it.  Our
  1171.          interpretation: between the last line of the comment and
  1172.          the line in which the keyword is found must be no line
  1173.          with non-white space tokens.  */
  1174.       ++newline_count;
  1175.       if (last_non_comment_line > last_comment_line)
  1176.         xgettext_lex_comment_reset ();
  1177.       break;
  1178.  
  1179.     case token_type_name:
  1180.       last_non_comment_line = newline_count;
  1181.  
  1182.       if (default_keywords)
  1183.         {
  1184.           xgettext_lex_keyword ("gettext");
  1185.           xgettext_lex_keyword ("dgettext");
  1186.           xgettext_lex_keyword ("dcgettext");
  1187.           xgettext_lex_keyword ("gettext_noop");
  1188.           default_keywords = 0;
  1189.         }
  1190.  
  1191.       if (string_list_member (keywords, token.string))
  1192.         {
  1193.           tp->type = (strcmp (token.string, "dgettext") == 0
  1194.               || strcmp (token.string, "dcgettext") == 0)
  1195.         ? xgettext_token_type_keyword2 : xgettext_token_type_keyword1;
  1196.         }
  1197.       else
  1198.         tp->type = xgettext_token_type_symbol;
  1199.       free (token.string);
  1200.       return;
  1201.  
  1202.     case token_type_lp:
  1203.       last_non_comment_line = newline_count;
  1204.  
  1205.       tp->type = xgettext_token_type_lp;
  1206.       return;
  1207.  
  1208.     case token_type_comma:
  1209.       last_non_comment_line = newline_count;
  1210.  
  1211.       tp->type = xgettext_token_type_comma;
  1212.       return;
  1213.  
  1214.     case token_type_string_literal:
  1215.       last_non_comment_line = newline_count;
  1216.  
  1217.       tp->type = xgettext_token_type_string_literal;
  1218.       tp->string = token.string;
  1219.       tp->line_number = token.line_number;
  1220.       tp->file_name = logical_file_name;
  1221.       return;
  1222.  
  1223.     default:
  1224.       last_non_comment_line = newline_count;
  1225.  
  1226.       tp->type = xgettext_token_type_symbol;
  1227.       return;
  1228.     }
  1229.     }
  1230. }
  1231.  
  1232.  
  1233. void
  1234. xgettext_lex_keyword (name)
  1235.      char *name;
  1236. {
  1237.   if (name == NULL)
  1238.     default_keywords = 0;
  1239.   else
  1240.     {
  1241.       if (keywords == NULL)
  1242.     keywords = string_list_alloc ();
  1243.  
  1244.       string_list_append_unique (keywords, name);
  1245.     }
  1246. }
  1247.  
  1248.  
  1249. const char *
  1250. xgettext_lex_comment (n)
  1251.      size_t n;
  1252. {
  1253.   if (comment == NULL || n >= comment->nitems)
  1254.     return NULL;
  1255.   return comment->item[n];
  1256. }
  1257.  
  1258.  
  1259. void
  1260. xgettext_lex_comment_reset ()
  1261. {
  1262.   if (comment != NULL)
  1263.     {
  1264.       string_list_free (comment);
  1265.       comment = NULL;
  1266.     }
  1267. }
  1268.  
  1269.  
  1270. void
  1271. xgettext_lex_cplusplus ()
  1272. {
  1273.   cplusplus_comments = 1;
  1274. }
  1275.  
  1276.  
  1277. void
  1278. xgettext_lex_trigraphs ()
  1279. {
  1280.   trigraphs = 1;
  1281. }
  1282.