Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / src / po-lex.c
1 /* GNU gettext - internationalization aids
2    Copyright (C) 1995-2009, 2011, 2015 Free Software Foundation, Inc.
3
4    This file was written by Peter Miller <millerp@canb.auug.org.au>.
5    Multibyte character handling by Bruno Haible <haible@clisp.cons.org>.
6
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 /* Specification.  */
26 #include "po-lex.h"
27
28 #include <errno.h>
29 #include <limits.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdarg.h>
34
35 #if HAVE_ICONV
36 # include <iconv.h>
37 #endif
38
39 #include "c-ctype.h"
40 #include "uniwidth.h"
41 #include "gettext.h"
42 #include "po-charset.h"
43 #include "xalloc.h"
44 #include "error.h"
45 #include "error-progname.h"
46 #include "xvasprintf.h"
47 #include "po-error.h"
48 #include "po-xerror.h"
49 #include "pos.h"
50 #include "message.h"
51 #include "str-list.h"
52 #include "po-gram-gen2.h"
53
54 #define _(str) gettext(str)
55
56 #if HAVE_ICONV
57 # include "unistr.h"
58 #endif
59
60 #if HAVE_DECL_GETC_UNLOCKED
61 # undef getc
62 # define getc getc_unlocked
63 #endif
64
65
66 /* Current position within the PO file.  */
67 lex_pos_ty gram_pos;
68 int gram_pos_column;
69
70
71 /* Error handling during the parsing of a PO file.
72    These functions can access gram_pos and gram_pos_column.  */
73
74 /* VARARGS1 */
75 void
76 po_gram_error (const char *fmt, ...)
77 {
78   va_list ap;
79   char *buffer;
80
81   va_start (ap, fmt);
82   if (vasprintf (&buffer, fmt, ap) < 0)
83     error (EXIT_FAILURE, 0, _("memory exhausted"));
84   va_end (ap);
85   po_xerror (PO_SEVERITY_ERROR, NULL, gram_pos.file_name, gram_pos.line_number,
86              gram_pos_column + 1, false, buffer);
87   free (buffer);
88
89   if (error_message_count >= gram_max_allowed_errors)
90     po_error (EXIT_FAILURE, 0, _("too many errors, aborting"));
91 }
92
93 /* VARARGS2 */
94 void
95 po_gram_error_at_line (const lex_pos_ty *pp, const char *fmt, ...)
96 {
97   va_list ap;
98   char *buffer;
99
100   va_start (ap, fmt);
101   if (vasprintf (&buffer, fmt, ap) < 0)
102     error (EXIT_FAILURE, 0, _("memory exhausted"));
103   va_end (ap);
104   po_xerror (PO_SEVERITY_ERROR, NULL, pp->file_name, pp->line_number,
105              (size_t)(-1), false, buffer);
106   free (buffer);
107
108   if (error_message_count >= gram_max_allowed_errors)
109     po_error (EXIT_FAILURE, 0, _("too many errors, aborting"));
110 }
111
112
113 /* The lowest level of PO file parsing converts bytes to multibyte characters.
114    This is needed
115    1. for C compatibility: ISO C 99 section 5.1.1.2 says that the first
116       translation phase maps bytes to characters.
117    2. to keep track of the current column, for the sake of precise error
118       location. Emacs compile.el interprets the column in error messages
119       by default as a screen column number, not as character number.
120    3. to avoid skipping backslash-newline in the midst of a multibyte
121       character. If XY is a multibyte character,  X \ newline Y  is invalid.
122  */
123
124 /* Multibyte character data type.  */
125 /* Note this depends on po_lex_charset and po_lex_iconv, which get set
126    while the file is being parsed.  */
127
128 #define MBCHAR_BUF_SIZE 24
129
130 struct mbchar
131 {
132   size_t bytes;         /* number of bytes of current character, > 0 */
133 #if HAVE_ICONV
134   bool uc_valid;        /* true if uc is a valid Unicode character */
135   ucs4_t uc;            /* if uc_valid: the current character */
136 #endif
137   char buf[MBCHAR_BUF_SIZE]; /* room for the bytes */
138 };
139
140 /* We want to pass multibyte characters by reference automatically,
141    therefore we use an array type.  */
142 typedef struct mbchar mbchar_t[1];
143
144 /* A version of memcpy optimized for the case n <= 1.  */
145 static inline void
146 memcpy_small (void *dst, const void *src, size_t n)
147 {
148   if (n > 0)
149     {
150       char *q = (char *) dst;
151       const char *p = (const char *) src;
152
153       *q = *p;
154       if (--n > 0)
155         do *++q = *++p; while (--n > 0);
156     }
157 }
158
159 /* EOF (not a real character) is represented with bytes = 0 and
160    uc_valid = false.  */
161 static inline bool
162 mb_iseof (const mbchar_t mbc)
163 {
164   return (mbc->bytes == 0);
165 }
166
167 /* Access the current character.  */
168 static inline const char *
169 mb_ptr (const mbchar_t mbc)
170 {
171   return mbc->buf;
172 }
173 static inline size_t
174 mb_len (const mbchar_t mbc)
175 {
176   return mbc->bytes;
177 }
178
179 /* Comparison of characters.  */
180
181 static inline bool
182 mb_iseq (const mbchar_t mbc, char sc)
183 {
184   /* Note: It is wrong to compare only mbc->uc, because when the encoding is
185      SHIFT_JIS, mbc->buf[0] == '\\' corresponds to mbc->uc == 0x00A5, but we
186      want to treat it as an escape character, although it looks like a Yen
187      sign.  */
188 #if HAVE_ICONV && 0
189   if (mbc->uc_valid)
190     return (mbc->uc == sc); /* wrong! */
191   else
192 #endif
193     return (mbc->bytes == 1 && mbc->buf[0] == sc);
194 }
195
196 static inline bool
197 mb_isnul (const mbchar_t mbc)
198 {
199 #if HAVE_ICONV
200   if (mbc->uc_valid)
201     return (mbc->uc == 0);
202   else
203 #endif
204     return (mbc->bytes == 1 && mbc->buf[0] == 0);
205 }
206
207 static inline int
208 mb_cmp (const mbchar_t mbc1, const mbchar_t mbc2)
209 {
210 #if HAVE_ICONV
211   if (mbc1->uc_valid && mbc2->uc_valid)
212     return (int) mbc1->uc - (int) mbc2->uc;
213   else
214 #endif
215     return (mbc1->bytes == mbc2->bytes
216             ? memcmp (mbc1->buf, mbc2->buf, mbc1->bytes)
217             : mbc1->bytes < mbc2->bytes
218               ? (memcmp (mbc1->buf, mbc2->buf, mbc1->bytes) > 0 ? 1 : -1)
219               : (memcmp (mbc1->buf, mbc2->buf, mbc2->bytes) >= 0 ? 1 : -1));
220 }
221
222 static inline bool
223 mb_equal (const mbchar_t mbc1, const mbchar_t mbc2)
224 {
225 #if HAVE_ICONV
226   if (mbc1->uc_valid && mbc2->uc_valid)
227     return mbc1->uc == mbc2->uc;
228   else
229 #endif
230     return (mbc1->bytes == mbc2->bytes
231             && memcmp (mbc1->buf, mbc2->buf, mbc1->bytes) == 0);
232 }
233
234 /* <ctype.h>, <wctype.h> classification.  */
235
236 static inline bool
237 mb_isascii (const mbchar_t mbc)
238 {
239 #if HAVE_ICONV
240   if (mbc->uc_valid)
241     return (mbc->uc >= 0x0000 && mbc->uc <= 0x007F);
242   else
243 #endif
244     return (mbc->bytes == 1
245 #if CHAR_MIN < 0x00 /* to avoid gcc warning */
246             && mbc->buf[0] >= 0x00
247 #endif
248 #if CHAR_MAX > 0x7F /* to avoid gcc warning */
249             && mbc->buf[0] <= 0x7F
250 #endif
251            );
252 }
253
254 /* Extra <wchar.h> function.  */
255
256 /* Unprintable characters appear as a small box of width 1.  */
257 #define MB_UNPRINTABLE_WIDTH 1
258
259 static int
260 mb_width (const mbchar_t mbc)
261 {
262 #if HAVE_ICONV
263   if (mbc->uc_valid)
264     {
265       ucs4_t uc = mbc->uc;
266       const char *encoding =
267         (po_lex_iconv != (iconv_t)(-1) ? po_lex_charset : "");
268       int w = uc_width (uc, encoding);
269       /* For unprintable characters, arbitrarily return 0 for control
270          characters (except tab) and MB_UNPRINTABLE_WIDTH otherwise.  */
271       if (w >= 0)
272         return w;
273       if (uc >= 0x0000 && uc <= 0x001F)
274         {
275           if (uc == 0x0009)
276             return 8 - (gram_pos_column & 7);
277           return 0;
278         }
279       if ((uc >= 0x007F && uc <= 0x009F) || (uc >= 0x2028 && uc <= 0x2029))
280         return 0;
281       return MB_UNPRINTABLE_WIDTH;
282     }
283   else
284 #endif
285     {
286       if (mbc->bytes == 1)
287         {
288           if (
289 #if CHAR_MIN < 0x00 /* to avoid gcc warning */
290               mbc->buf[0] >= 0x00 &&
291 #endif
292               mbc->buf[0] <= 0x1F)
293             {
294               if (mbc->buf[0] == 0x09)
295                 return 8 - (gram_pos_column & 7);
296               return 0;
297             }
298           if (mbc->buf[0] == 0x7F)
299             return 0;
300         }
301       return MB_UNPRINTABLE_WIDTH;
302     }
303 }
304
305 /* Output.  */
306 static inline void
307 mb_putc (const mbchar_t mbc, FILE *stream)
308 {
309   fwrite (mbc->buf, 1, mbc->bytes, stream);
310 }
311
312 /* Assignment.  */
313 static inline void
314 mb_setascii (mbchar_t mbc, char sc)
315 {
316   mbc->bytes = 1;
317 #if HAVE_ICONV
318   mbc->uc_valid = 1;
319   mbc->uc = sc;
320 #endif
321   mbc->buf[0] = sc;
322 }
323
324 /* Copying a character.  */
325 static inline void
326 mb_copy (mbchar_t new_mbc, const mbchar_t old_mbc)
327 {
328   memcpy_small (&new_mbc->buf[0], &old_mbc->buf[0], old_mbc->bytes);
329   new_mbc->bytes = old_mbc->bytes;
330 #if HAVE_ICONV
331   if ((new_mbc->uc_valid = old_mbc->uc_valid))
332     new_mbc->uc = old_mbc->uc;
333 #endif
334 }
335
336
337 /* Multibyte character input.  */
338
339 /* Number of characters that can be pushed back.
340    We need 1 for lex_getc, plus 1 for lex_ungetc.  */
341 #define NPUSHBACK 2
342
343 /* Data type of a multibyte character input stream.  */
344 struct mbfile
345 {
346   FILE *fp;
347   bool eof_seen;
348   int have_pushback;
349   unsigned int bufcount;
350   char buf[MBCHAR_BUF_SIZE];
351   struct mbchar pushback[NPUSHBACK];
352 };
353
354 /* We want to pass multibyte streams by reference automatically,
355    therefore we use an array type.  */
356 typedef struct mbfile mbfile_t[1];
357
358 /* Whether invalid multibyte sequences in the input shall be signalled
359    or silently tolerated.  */
360 static bool signal_eilseq;
361
362 static inline void
363 mbfile_init (mbfile_t mbf, FILE *stream)
364 {
365   mbf->fp = stream;
366   mbf->eof_seen = false;
367   mbf->have_pushback = 0;
368   mbf->bufcount = 0;
369 }
370
371 /* Read the next multibyte character from mbf and put it into mbc.
372    If a read error occurs, errno is set and ferror (mbf->fp) becomes true.  */
373 static void
374 mbfile_getc (mbchar_t mbc, mbfile_t mbf)
375 {
376   size_t bytes;
377
378   /* If EOF has already been seen, don't use getc.  This matters if
379      mbf->fp is connected to an interactive tty.  */
380   if (mbf->eof_seen)
381     goto eof;
382
383   /* Return character pushed back, if there is one.  */
384   if (mbf->have_pushback > 0)
385     {
386       mbf->have_pushback--;
387       mb_copy (mbc, &mbf->pushback[mbf->have_pushback]);
388       return;
389     }
390
391   /* Before using iconv, we need at least one byte.  */
392   if (mbf->bufcount == 0)
393     {
394       int c = getc (mbf->fp);
395       if (c == EOF)
396         {
397           mbf->eof_seen = true;
398           goto eof;
399         }
400       mbf->buf[0] = (unsigned char) c;
401       mbf->bufcount++;
402     }
403
404 #if HAVE_ICONV
405   if (po_lex_iconv != (iconv_t)(-1))
406     {
407       /* Use iconv on an increasing number of bytes.  Read only as many
408          bytes from mbf->fp as needed.  This is needed to give reasonable
409          interactive behaviour when mbf->fp is connected to an interactive
410          tty.  */
411       for (;;)
412         {
413           unsigned char scratchbuf[64];
414           const char *inptr = &mbf->buf[0];
415           size_t insize = mbf->bufcount;
416           char *outptr = (char *) &scratchbuf[0];
417           size_t outsize = sizeof (scratchbuf);
418
419           size_t res = iconv (po_lex_iconv,
420                               (ICONV_CONST char **) &inptr, &insize,
421                               &outptr, &outsize);
422           /* We expect that a character has been produced if and only if
423              some input bytes have been consumed.  */
424           if ((insize < mbf->bufcount) != (outsize < sizeof (scratchbuf)))
425             abort ();
426           if (outsize == sizeof (scratchbuf))
427             {
428               /* No character has been produced.  Must be an error.  */
429               if (res != (size_t)(-1))
430                 abort ();
431
432               if (errno == EILSEQ)
433                 {
434                   /* An invalid multibyte sequence was encountered.  */
435                   /* Return a single byte.  */
436                   if (signal_eilseq)
437                     po_gram_error (_("invalid multibyte sequence"));
438                   bytes = 1;
439                   mbc->uc_valid = false;
440                   break;
441                 }
442               else if (errno == EINVAL)
443                 {
444                   /* An incomplete multibyte character.  */
445                   int c;
446
447                   if (mbf->bufcount == MBCHAR_BUF_SIZE)
448                     {
449                       /* An overlong incomplete multibyte sequence was
450                          encountered.  */
451                       /* Return a single byte.  */
452                       bytes = 1;
453                       mbc->uc_valid = false;
454                       break;
455                     }
456
457                   /* Read one more byte and retry iconv.  */
458                   c = getc (mbf->fp);
459                   if (c == EOF)
460                     {
461                       mbf->eof_seen = true;
462                       if (ferror (mbf->fp))
463                         goto eof;
464                       if (signal_eilseq)
465                         po_gram_error (_("\
466 incomplete multibyte sequence at end of file"));
467                       bytes = mbf->bufcount;
468                       mbc->uc_valid = false;
469                       break;
470                     }
471                   mbf->buf[mbf->bufcount++] = (unsigned char) c;
472                   if (c == '\n')
473                     {
474                       if (signal_eilseq)
475                         po_gram_error (_("\
476 incomplete multibyte sequence at end of line"));
477                       bytes = mbf->bufcount - 1;
478                       mbc->uc_valid = false;
479                       break;
480                     }
481                 }
482               else
483                 {
484                   const char *errno_description = strerror (errno);
485                   po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
486                              xasprintf ("%s: %s",
487                                         _("iconv failure"),
488                                         errno_description));
489                 }
490             }
491           else
492             {
493               size_t outbytes = sizeof (scratchbuf) - outsize;
494               bytes = mbf->bufcount - insize;
495
496               /* We expect that one character has been produced.  */
497               if (bytes == 0)
498                 abort ();
499               if (outbytes == 0)
500                 abort ();
501               /* Convert it from UTF-8 to UCS-4.  */
502               if (u8_mbtoucr (&mbc->uc, scratchbuf, outbytes) < (int) outbytes)
503                 {
504                   /* scratchbuf contains an out-of-range Unicode character
505                      (> 0x10ffff).  */
506                   if (signal_eilseq)
507                     po_gram_error (_("invalid multibyte sequence"));
508                   mbc->uc_valid = false;
509                   break;
510                 }
511               mbc->uc_valid = true;
512               break;
513             }
514         }
515     }
516   else
517 #endif
518     {
519       if (po_lex_weird_cjk
520           /* Special handling of encodings with CJK structure.  */
521           && (unsigned char) mbf->buf[0] >= 0x80)
522         {
523           if (mbf->bufcount == 1)
524             {
525               /* Read one more byte.  */
526               int c = getc (mbf->fp);
527               if (c == EOF)
528                 {
529                   if (ferror (mbf->fp))
530                     {
531                       mbf->eof_seen = true;
532                       goto eof;
533                     }
534                 }
535               else
536                 {
537                   mbf->buf[1] = (unsigned char) c;
538                   mbf->bufcount++;
539                 }
540             }
541           if (mbf->bufcount >= 2 && (unsigned char) mbf->buf[1] >= 0x30)
542             /* Return a double byte.  */
543             bytes = 2;
544           else
545             /* Return a single byte.  */
546             bytes = 1;
547         }
548       else
549         {
550           /* Return a single byte.  */
551           bytes = 1;
552         }
553 #if HAVE_ICONV
554       mbc->uc_valid = false;
555 #endif
556     }
557
558   /* Return the multibyte sequence mbf->buf[0..bytes-1].  */
559   memcpy_small (&mbc->buf[0], &mbf->buf[0], bytes);
560   mbc->bytes = bytes;
561
562   mbf->bufcount -= bytes;
563   if (mbf->bufcount > 0)
564     {
565       /* It's not worth calling memmove() for so few bytes.  */
566       unsigned int count = mbf->bufcount;
567       char *p = &mbf->buf[0];
568
569       do
570         {
571           *p = *(p + bytes);
572           p++;
573         }
574       while (--count > 0);
575     }
576   return;
577
578 eof:
579   /* An mbchar_t with bytes == 0 is used to indicate EOF.  */
580   mbc->bytes = 0;
581 #if HAVE_ICONV
582   mbc->uc_valid = false;
583 #endif
584   return;
585 }
586
587 static void
588 mbfile_ungetc (const mbchar_t mbc, mbfile_t mbf)
589 {
590   if (mbf->have_pushback >= NPUSHBACK)
591     abort ();
592   mb_copy (&mbf->pushback[mbf->have_pushback], mbc);
593   mbf->have_pushback++;
594 }
595
596
597 /* Lexer variables.  */
598
599 static mbfile_t mbf;
600 unsigned int gram_max_allowed_errors = 20;
601 static bool po_lex_obsolete;
602 static bool po_lex_previous;
603 static bool pass_comments = false;
604 bool pass_obsolete_entries = false;
605
606
607 /* Prepare lexical analysis.  */
608 void
609 lex_start (FILE *fp, const char *real_filename, const char *logical_filename)
610 {
611   /* Ignore the logical_filename, because PO file entries already have
612      their file names attached.  But use real_filename for error messages.  */
613   gram_pos.file_name = xstrdup (real_filename);
614
615   mbfile_init (mbf, fp);
616
617   gram_pos.line_number = 1;
618   gram_pos_column = 0;
619   signal_eilseq = true;
620   po_lex_obsolete = false;
621   po_lex_previous = false;
622   po_lex_charset_init ();
623 }
624
625 /* Terminate lexical analysis.  */
626 void
627 lex_end ()
628 {
629   mbf->fp = NULL;
630   gram_pos.file_name = NULL;
631   gram_pos.line_number = 0;
632   gram_pos_column = 0;
633   signal_eilseq = false;
634   po_lex_obsolete = false;
635   po_lex_previous = false;
636   po_lex_charset_close ();
637 }
638
639
640 /* Read a single character, dealing with backslash-newline.
641    Also keep track of the current line number and column number.  */
642 static void
643 lex_getc (mbchar_t mbc)
644 {
645   for (;;)
646     {
647       mbfile_getc (mbc, mbf);
648
649       if (mb_iseof (mbc))
650         {
651           if (ferror (mbf->fp))
652            bomb:
653             {
654               const char *errno_description = strerror (errno);
655               po_xerror (PO_SEVERITY_FATAL_ERROR, NULL, NULL, 0, 0, false,
656                          xasprintf ("%s: %s",
657                                     xasprintf (_("error while reading \"%s\""),
658                                                gram_pos.file_name),
659                                     errno_description));
660             }
661           break;
662         }
663
664       if (mb_iseq (mbc, '\n'))
665         {
666           gram_pos.line_number++;
667           gram_pos_column = 0;
668           break;
669         }
670
671       gram_pos_column += mb_width (mbc);
672
673       if (mb_iseq (mbc, '\\'))
674         {
675           mbchar_t mbc2;
676
677           mbfile_getc (mbc2, mbf);
678
679           if (mb_iseof (mbc2))
680             {
681               if (ferror (mbf->fp))
682                 goto bomb;
683               break;
684             }
685
686           if (!mb_iseq (mbc2, '\n'))
687             {
688               mbfile_ungetc (mbc2, mbf);
689               break;
690             }
691
692           gram_pos.line_number++;
693           gram_pos_column = 0;
694         }
695       else
696         break;
697     }
698 }
699
700
701 static void
702 lex_ungetc (const mbchar_t mbc)
703 {
704   if (!mb_iseof (mbc))
705     {
706       if (mb_iseq (mbc, '\n'))
707         /* Decrement the line number, but don't care about the column.  */
708         gram_pos.line_number--;
709       else
710         /* Decrement the column number.  Also works well enough for tabs.  */
711         gram_pos_column -= mb_width (mbc);
712
713       mbfile_ungetc (mbc, mbf);
714     }
715 }
716
717
718 static int
719 keyword_p (const char *s)
720 {
721   if (!po_lex_previous)
722     {
723       if (!strcmp (s, "domain"))
724         return DOMAIN;
725       if (!strcmp (s, "msgid"))
726         return MSGID;
727       if (!strcmp (s, "msgid_plural"))
728         return MSGID_PLURAL;
729       if (!strcmp (s, "msgstr"))
730         return MSGSTR;
731       if (!strcmp (s, "msgctxt"))
732         return MSGCTXT;
733     }
734   else
735     {
736       /* Inside a "#|" context, the keywords have a different meaning.  */
737       if (!strcmp (s, "msgid"))
738         return PREV_MSGID;
739       if (!strcmp (s, "msgid_plural"))
740         return PREV_MSGID_PLURAL;
741       if (!strcmp (s, "msgctxt"))
742         return PREV_MSGCTXT;
743     }
744   po_gram_error_at_line (&gram_pos, _("keyword \"%s\" unknown"), s);
745   return NAME;
746 }
747
748
749 static int
750 control_sequence ()
751 {
752   mbchar_t mbc;
753   int val;
754   int max;
755
756   lex_getc (mbc);
757   if (mb_len (mbc) == 1)
758     switch (mb_ptr (mbc) [0])
759       {
760       case 'n':
761         return '\n';
762
763       case 't':
764         return '\t';
765
766       case 'b':
767         return '\b';
768
769       case 'r':
770         return '\r';
771
772       case 'f':
773         return '\f';
774
775       case 'v':
776         return '\v';
777
778       case 'a':
779         return '\a';
780
781       case '\\':
782       case '"':
783         return mb_ptr (mbc) [0];
784
785       case '0': case '1': case '2': case '3':
786       case '4': case '5': case '6': case '7':
787         val = 0;
788         max = 0;
789         for (;;)
790           {
791             char c = mb_ptr (mbc) [0];
792             /* Warning: not portable, can't depend on '0'..'7' ordering.  */
793             val = val * 8 + (c - '0');
794             if (++max == 3)
795               break;
796             lex_getc (mbc);
797             if (mb_len (mbc) == 1)
798               switch (mb_ptr (mbc) [0])
799                 {
800                 case '0': case '1': case '2': case '3':
801                 case '4': case '5': case '6': case '7':
802                   continue;
803
804                 default:
805                   break;
806                 }
807             lex_ungetc (mbc);
808             break;
809           }
810         return val;
811
812       case 'x':
813         lex_getc (mbc);
814         if (mb_iseof (mbc) || mb_len (mbc) != 1
815             || !c_isxdigit (mb_ptr (mbc) [0]))
816           break;
817
818         val = 0;
819         for (;;)
820           {
821             char c = mb_ptr (mbc) [0];
822             val *= 16;
823             if (c_isdigit (c))
824               /* Warning: not portable, can't depend on '0'..'9' ordering */
825               val += c - '0';
826             else if (c_isupper (c))
827               /* Warning: not portable, can't depend on 'A'..'F' ordering */
828               val += c - 'A' + 10;
829             else
830               /* Warning: not portable, can't depend on 'a'..'f' ordering */
831               val += c - 'a' + 10;
832
833             lex_getc (mbc);
834             if (mb_len (mbc) == 1)
835               switch (mb_ptr (mbc) [0])
836                 {
837                 case '0': case '1': case '2': case '3': case '4':
838                 case '5': case '6': case '7': case '8': case '9':
839                 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
840                 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
841                   continue;
842
843                 default:
844                   break;
845                 }
846             lex_ungetc (mbc);
847             break;
848           }
849         return val;
850
851       /* FIXME: \u and \U are not handled.  */
852       }
853   lex_ungetc (mbc);
854   po_gram_error (_("invalid control sequence"));
855   return ' ';
856 }
857
858
859 /* Return the next token in the PO file.  The return codes are defined
860    in "po-gram-gen2.h".  Associated data is put in 'po_gram_lval'.  */
861 int
862 po_gram_lex ()
863 {
864   static char *buf;
865   static size_t bufmax;
866   mbchar_t mbc;
867   size_t bufpos;
868
869   for (;;)
870     {
871       lex_getc (mbc);
872
873       if (mb_iseof (mbc))
874         /* Yacc want this for end of file.  */
875         return 0;
876
877       if (mb_len (mbc) == 1)
878         switch (mb_ptr (mbc) [0])
879           {
880           case '\n':
881             po_lex_obsolete = false;
882             po_lex_previous = false;
883             /* Ignore whitespace, not relevant for the grammar.  */
884             break;
885
886           case ' ':
887           case '\t':
888           case '\r':
889           case '\f':
890           case '\v':
891             /* Ignore whitespace, not relevant for the grammar.  */
892             break;
893
894           case '#':
895             lex_getc (mbc);
896             if (mb_iseq (mbc, '~'))
897               /* A pseudo-comment beginning with #~ is found.  This is
898                  not a comment.  It is the format for obsolete entries.
899                  We simply discard the "#~" prefix.  The following
900                  characters are expected to be well formed.  */
901               {
902                 po_lex_obsolete = true;
903                 /* A pseudo-comment beginning with #~| denotes a previous
904                    untranslated string in an obsolete entry.  This does not
905                    make much sense semantically, and is implemented here
906                    for completeness only.  */
907                 lex_getc (mbc);
908                 if (mb_iseq (mbc, '|'))
909                   po_lex_previous = true;
910                 else
911                   lex_ungetc (mbc);
912                 break;
913               }
914             if (mb_iseq (mbc, '|'))
915               /* A pseudo-comment beginning with #| is found.  This is
916                  the previous untranslated string.  We discard the "#|"
917                  prefix, but change the keywords and string returns
918                  accordingly.  */
919               {
920                 po_lex_previous = true;
921                 break;
922               }
923
924             /* Accumulate comments into a buffer.  If we have been asked
925                to pass comments, generate a COMMENT token, otherwise
926                discard it.  */
927             signal_eilseq = false;
928             if (pass_comments)
929               {
930                 bufpos = 0;
931                 for (;;)
932                   {
933                     while (bufpos + mb_len (mbc) >= bufmax)
934                       {
935                         bufmax += 100;
936                         buf = xrealloc (buf, bufmax);
937                       }
938                     if (mb_iseof (mbc) || mb_iseq (mbc, '\n'))
939                       break;
940
941                     memcpy_small (&buf[bufpos], mb_ptr (mbc), mb_len (mbc));
942                     bufpos += mb_len (mbc);
943
944                     lex_getc (mbc);
945                   }
946                 buf[bufpos] = '\0';
947
948                 po_gram_lval.string.string = buf;
949                 po_gram_lval.string.pos = gram_pos;
950                 po_gram_lval.string.obsolete = po_lex_obsolete;
951                 po_lex_obsolete = false;
952                 signal_eilseq = true;
953                 return COMMENT;
954               }
955             else
956               {
957                 /* We do this in separate loop because collecting large
958                    comments while they get not passed to the upper layers
959                    is not very efficient.  */
960                 while (!mb_iseof (mbc) && !mb_iseq (mbc, '\n'))
961                   lex_getc (mbc);
962                 po_lex_obsolete = false;
963                 signal_eilseq = true;
964               }
965             break;
966
967           case '"':
968             /* Accumulate a string.  */
969             bufpos = 0;
970             for (;;)
971               {
972                 lex_getc (mbc);
973                 while (bufpos + mb_len (mbc) >= bufmax)
974                   {
975                     bufmax += 100;
976                     buf = xrealloc (buf, bufmax);
977                   }
978                 if (mb_iseof (mbc))
979                   {
980                     po_gram_error_at_line (&gram_pos,
981                                            _("end-of-file within string"));
982                     break;
983                   }
984                 if (mb_iseq (mbc, '\n'))
985                   {
986                     po_gram_error_at_line (&gram_pos,
987                                            _("end-of-line within string"));
988                     break;
989                   }
990                 if (mb_iseq (mbc, '"'))
991                   break;
992                 if (mb_iseq (mbc, '\\'))
993                   {
994                     buf[bufpos++] = control_sequence ();
995                     continue;
996                   }
997
998                 /* Add mbc to the accumulator.  */
999                 memcpy_small (&buf[bufpos], mb_ptr (mbc), mb_len (mbc));
1000                 bufpos += mb_len (mbc);
1001               }
1002             buf[bufpos] = '\0';
1003
1004             /* Strings cannot contain the msgctxt separator, because it cannot
1005                be faithfully represented in the msgid of a .mo file.  */
1006             if (strchr (buf, MSGCTXT_SEPARATOR) != NULL)
1007               po_gram_error_at_line (&gram_pos,
1008                                      _("context separator <EOT> within string"));
1009
1010             /* FIXME: Treatment of embedded \000 chars is incorrect.  */
1011             po_gram_lval.string.string = xstrdup (buf);
1012             po_gram_lval.string.pos = gram_pos;
1013             po_gram_lval.string.obsolete = po_lex_obsolete;
1014             return (po_lex_previous ? PREV_STRING : STRING);
1015
1016           case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
1017           case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
1018           case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
1019           case 's': case 't': case 'u': case 'v': case 'w': case 'x':
1020           case 'y': case 'z':
1021           case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
1022           case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
1023           case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
1024           case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
1025           case 'Y': case 'Z':
1026           case '_': case '$':
1027             bufpos = 0;
1028             for (;;)
1029               {
1030                 char c = mb_ptr (mbc) [0];
1031                 if (bufpos + 1 >= bufmax)
1032                   {
1033                     bufmax += 100;
1034                     buf = xrealloc (buf, bufmax);
1035                   }
1036                 buf[bufpos++] = c;
1037                 lex_getc (mbc);
1038                 if (mb_len (mbc) == 1)
1039                   switch (mb_ptr (mbc) [0])
1040                     {
1041                     default:
1042                       break;
1043                     case 'a': case 'b': case 'c': case 'd': case 'e':
1044                     case 'f': case 'g': case 'h': case 'i': case 'j':
1045                     case 'k': case 'l': case 'm': case 'n': case 'o':
1046                     case 'p': case 'q': case 'r': case 's': case 't':
1047                     case 'u': case 'v': case 'w': case 'x': case 'y':
1048                     case 'z':
1049                     case 'A': case 'B': case 'C': case 'D': case 'E':
1050                     case 'F': case 'G': case 'H': case 'I': case 'J':
1051                     case 'K': case 'L': case 'M': case 'N': case 'O':
1052                     case 'P': case 'Q': case 'R': case 'S': case 'T':
1053                     case 'U': case 'V': case 'W': case 'X': case 'Y':
1054                     case 'Z':
1055                     case '_': case '$':
1056                     case '0': case '1': case '2': case '3': case '4':
1057                     case '5': case '6': case '7': case '8': case '9':
1058                       continue;
1059                     }
1060                 break;
1061               }
1062             lex_ungetc (mbc);
1063
1064             buf[bufpos] = '\0';
1065
1066             {
1067               int k = keyword_p (buf);
1068               if (k == NAME)
1069                 {
1070                   po_gram_lval.string.string = xstrdup (buf);
1071                   po_gram_lval.string.pos = gram_pos;
1072                   po_gram_lval.string.obsolete = po_lex_obsolete;
1073                 }
1074               else
1075                 {
1076                   po_gram_lval.pos.pos = gram_pos;
1077                   po_gram_lval.pos.obsolete = po_lex_obsolete;
1078                 }
1079               return k;
1080             }
1081
1082           case '0': case '1': case '2': case '3': case '4':
1083           case '5': case '6': case '7': case '8': case '9':
1084             bufpos = 0;
1085             for (;;)
1086               {
1087                 char c = mb_ptr (mbc) [0];
1088                 if (bufpos + 1 >= bufmax)
1089                   {
1090                     bufmax += 100;
1091                     buf = xrealloc (buf, bufmax + 1);
1092                   }
1093                 buf[bufpos++] = c;
1094                 lex_getc (mbc);
1095                 if (mb_len (mbc) == 1)
1096                   switch (mb_ptr (mbc) [0])
1097                     {
1098                     default:
1099                       break;
1100
1101                     case '0': case '1': case '2': case '3': case '4':
1102                     case '5': case '6': case '7': case '8': case '9':
1103                       continue;
1104                     }
1105                 break;
1106               }
1107             lex_ungetc (mbc);
1108
1109             buf[bufpos] = '\0';
1110
1111             po_gram_lval.number.number = atol (buf);
1112             po_gram_lval.number.pos = gram_pos;
1113             po_gram_lval.number.obsolete = po_lex_obsolete;
1114             return NUMBER;
1115
1116           case '[':
1117             po_gram_lval.pos.pos = gram_pos;
1118             po_gram_lval.pos.obsolete = po_lex_obsolete;
1119             return '[';
1120
1121           case ']':
1122             po_gram_lval.pos.pos = gram_pos;
1123             po_gram_lval.pos.obsolete = po_lex_obsolete;
1124             return ']';
1125
1126           default:
1127             /* This will cause a syntax error.  */
1128             return JUNK;
1129           }
1130       else
1131         /* This will cause a syntax error.  */
1132         return JUNK;
1133     }
1134 }
1135
1136
1137 /* po_gram_lex() can return comments as COMMENT.  Switch this on or off.  */
1138 void
1139 po_lex_pass_comments (bool flag)
1140 {
1141   pass_comments = flag;
1142 }
1143
1144
1145 /* po_gram_lex() can return obsolete entries as if they were normal entries.
1146    Switch this on or off.  */
1147 void
1148 po_lex_pass_obsolete_entries (bool flag)
1149 {
1150   pass_obsolete_entries = flag;
1151 }