Sat Mar 23 17:52:49 1996 Ulrich Drepper <drepper@gnu.ai.mit.edu>
[kopensolaris-gnu/glibc.git] / locale / programs / linereader.c
1 /* Copyright (C) 1996 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper, <drepper@gnu.ai.mit.edu>.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB.  If
17 not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 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 <libintl.h>
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "error.h"
32 #include "linereader.h"
33 #include "charset.h"
34 #include "stringtrans.h"
35
36
37 void *xmalloc (size_t __n);
38 void *xrealloc (void *__p, size_t __n);
39 char *xstrdup (const char *__str);
40
41
42 static struct token *get_toplvl_escape (struct linereader *lr);
43 static struct token *get_symname (struct linereader *lr);
44 static struct token *get_ident (struct linereader *lr);
45 static struct token *get_string (struct linereader *lr,
46                                  const struct charset_t *charset);
47
48
49 struct linereader *
50 lr_open (const char *fname, kw_hash_fct_t hf)
51 {
52   FILE *fp;
53   struct linereader *result;
54   int n;
55
56   if (fname == NULL || strcmp (fname, "-") == 0
57       || strcmp (fname, "/dev/stdin") == 0)
58     fp = stdin;
59   else
60     {
61       fp = fopen (fname, "r");
62       if (fp == NULL)
63         return NULL;
64     }
65
66   result = (struct linereader *) xmalloc (sizeof (*result));
67
68   result->fp = fp;
69   result->fname = xstrdup (fname);
70   result->buf = NULL;
71   result->bufsize = 0;
72   result->lineno = 1;
73   result->idx = 0;
74   result->comment_char = '#';
75   result->escape_char = '\\';
76   result->translate_strings = 1;
77
78   n = getdelim (&result->buf, &result->bufsize, '\n', result->fp);
79   if (n < 0)
80     {
81       int save = errno;
82       fclose (result->fp);
83       free (result);
84       errno = save;
85       return NULL;
86     }
87
88   if (n > 1 && result->buf[n - 2] == '\\' && result->buf[n - 1] == '\n')
89     n -= 2;
90
91   result->buf[n] = '\0';
92   result->bufact = n;
93   result->hash_fct = hf;
94
95   return result;
96 }
97
98
99 int
100 lr_eof (struct linereader *lr)
101 {
102   return lr->bufact = 0;
103 }
104
105
106 void
107 lr_close (struct linereader *lr)
108 {
109   fclose (lr->fp);
110   free (lr->buf);
111   free (lr);
112 }
113
114
115 int
116 lr_next (struct linereader *lr)
117 {
118   int n;
119
120   n = getdelim (&lr->buf, &lr->bufsize, '\n', lr->fp);
121   if (n < 0)
122     return -1;
123
124   ++lr->lineno;
125
126   if (n > 1 && lr->buf[n - 2] == lr->escape_char && lr->buf[n - 1] == '\n')
127     {
128       /* An escaped newline character is substituted with a single <SP>.  */
129       --n;
130       lr->buf[n - 1] = ' ';
131     }
132
133   lr->buf[n] = '\0';
134   lr->bufact = n;
135   lr->idx = 0;
136
137   return 0;
138 }
139
140
141 /* Defined in error.c.  */
142 /* This variable is incremented each time `error' is called.  */
143 extern unsigned int error_message_count;
144
145 /* The calling program should define program_name and set it to the
146    name of the executing program.  */
147 extern char *program_name;
148
149
150 struct token *
151 lr_token (struct linereader *lr, const struct charset_t *charset)
152 {
153   int ch;
154
155   while (1)
156     {
157       do
158         {
159           ch = lr_getc (lr);
160
161           if (ch == '\n')
162             {
163               lr->token.tok = tok_eol;
164               return &lr->token;
165             }
166         }
167       while (isspace (ch));
168
169       if (ch == EOF)
170         {
171           lr->token.tok = tok_eof;
172           return &lr->token;
173         };
174
175       if (ch != lr->comment_char)
176         break;
177
178       /* Ignore rest of line.  */
179       lr_ignore_rest (lr, 0);
180       lr->token.tok = tok_eol;
181       return &lr->token;
182     }
183
184   /* Match escape sequences.  */
185   if (ch == lr->escape_char)
186     return get_toplvl_escape (lr);
187
188   /* Match ellipsis.  */
189   if (ch == '.' && strncmp (&lr->buf[lr->idx], "..", 2) == 0)
190     {
191       lr_getc (lr);
192       lr_getc (lr);
193       lr->token.tok = tok_ellipsis;
194       return &lr->token;
195     }
196
197   switch (ch)
198     {
199     case '<':
200       return get_symname (lr);
201
202     case '0' ... '9':
203       lr->token.tok = tok_number;
204       lr->token.val.num = ch - '0';
205
206       while (isdigit (ch = lr_getc (lr)))
207         {
208           lr->token.val.num *= 10;
209           lr->token.val.num += ch - '0';
210         }
211       if (isalpha (ch))
212         lr_error (lr, _("garbage at end of digit"));
213       lr_ungetn (lr, 1);
214
215       return &lr->token;
216
217     case ';':
218       lr->token.tok = tok_semicolon;
219       return &lr->token;
220
221     case ',':
222       lr->token.tok = tok_comma;
223       return &lr->token;
224
225     case '(':
226       lr->token.tok = tok_open_brace;
227       return &lr->token;
228
229     case ')':
230       lr->token.tok = tok_close_brace;
231       return &lr->token;
232
233     case '"':
234       return get_string (lr, charset);
235
236     case '-':
237       ch = lr_getc (lr);
238       if (ch == '1')
239         {
240           lr->token.tok = tok_minus1;
241           return &lr->token;
242         }
243       lr_ungetn (lr, 2);
244       break;
245     }
246
247   return get_ident (lr);
248 }
249
250
251 static struct token *
252 get_toplvl_escape (struct linereader *lr)
253 {
254   /* This is supposed to be a numeric value.  We return the
255      numerical value and the number of bytes.  */
256   size_t start_idx = lr->idx - 1;
257   unsigned int value = 0;
258   int nbytes = 0;
259   int ch;
260
261   do
262     {
263       unsigned int byte = 0;
264       unsigned int base = 8;
265
266       ch = lr_getc (lr);
267
268       if (ch == 'd')
269         {
270           base = 10;
271           ch = lr_getc (lr);
272         }
273       else if (ch == 'x')
274         {
275           base = 16;
276           ch = lr_getc (lr);
277         }
278
279       if ((base == 16 && !isxdigit (ch))
280           || (base != 16 && (ch < '0' || ch >= '0' + base)))
281         {
282         esc_error:
283           lr->token.val.str.start = &lr->buf[start_idx];
284
285           while (ch != EOF || !isspace (ch))
286             ch = lr_getc (lr);
287           lr->token.val.str.len = lr->idx - start_idx;
288
289           lr->token.tok = tok_error;
290           return &lr->token;
291         }
292
293       if (isdigit (ch))
294         byte = ch - '0';
295       else
296         byte = tolower (ch) - 'a' + 10;
297
298       ch = lr_getc (lr);
299       if ((base == 16 && !isxdigit (ch))
300           || (base != 16 && (ch < '0' || ch >= '0' + base)))
301         goto esc_error;
302
303       byte *= base;
304       if (isdigit (ch))
305         byte += ch - '0';
306       else
307         byte += tolower (ch) - 'a' + 10;
308
309       ch = lr_getc (lr);
310       if (base != 16 && isdigit (ch))
311         {
312           byte *= base;
313           base += ch - '0';
314
315           ch = lr_getc (lr);
316         }
317
318       value *= 256;
319       value += byte;
320
321       ++nbytes;
322     }
323   while (ch == lr->escape_char && nbytes < 4);
324
325   if (!isspace (ch))
326     lr_error (lr, _("garbage at end of character code specification"));
327
328   lr_ungetn (lr, 1);
329
330   lr->token.tok = tok_charcode;
331   lr->token.val.charcode.val = value;
332   lr->token.val.charcode.nbytes = nbytes;
333
334   return &lr->token;
335 }
336
337
338 #define ADDC(ch)                                                            \
339   do                                                                        \
340     {                                                                       \
341       if (bufact == bufmax)                                                 \
342         {                                                                   \
343           bufmax *= 2;                                                      \
344           buf = xrealloc (buf, bufmax);                                     \
345         }                                                                   \
346       buf[bufact++] = (ch);                                                 \
347     }                                                                       \
348   while (0)
349
350
351 static struct token *
352 get_symname (struct linereader *lr)
353 {
354   /* Symbol in brackets.  We must distinguish three kinds:
355      1. reserved words
356      2. ISO 10646 position values
357      3. all other.  */
358   char *buf;
359   size_t bufact = 0;
360   size_t bufmax = 56;
361   const struct keyword_t *kw;
362   int ch;
363
364   buf = (char *) xmalloc (bufmax);
365
366   do
367     {
368       ch = lr_getc (lr);
369       if (ch == lr->escape_char)
370         {
371           int c2 = lr_getc (lr);
372           ADDC (c2);
373
374           if (c2 == '\n')
375             ch = '\n';
376         }
377       else
378         ADDC (ch);
379     }
380   while (ch != '>' && ch != '\n');
381
382   if (ch == '\n')
383     lr_error (lr, _("unterminated symbolic name"));
384
385   /* Test for ISO 10646 position value.  */
386   if (buf[0] == 'U' && (bufact == 6 || bufact == 10))
387     {
388       char *cp = buf + 1;
389       while (cp < &buf[bufact - 1] && isxdigit (*cp))
390         ++cp;
391
392       if (cp == &buf[bufact - 1])
393         {
394           /* Yes, it is.  */
395           lr->token.tok = bufact == 6 ? tok_ucs2 : tok_ucs4;
396           lr->token.val.charcode.val = strtoul (buf, NULL, 16);
397           lr->token.val.charcode.nbytes = lr->token.tok == tok_ucs2 ? 2 : 4;
398
399           return &lr->token;
400         }
401     }
402
403   /* It is a symbolic name.  Test for reserved words.  */
404   kw = lr->hash_fct (buf, bufact - 1);
405
406   if (kw != NULL && kw->symname_or_ident == 1)
407     {
408       lr->token.tok = kw->token;
409       free (buf);
410     }
411   else
412     {
413       lr->token.tok = tok_bsymbol;
414
415       buf[bufact] = '\0';
416       buf = xrealloc (buf, bufact + 1);
417
418       lr->token.val.str.start = buf;
419       lr->token.val.str.len = bufact - 1;
420     }
421
422   return &lr->token;
423 }
424
425
426 static struct token *
427 get_ident (struct linereader *lr)
428 {
429   char *buf;
430   size_t bufact;
431   size_t bufmax = 56;
432   const struct keyword_t *kw;
433   int ch;
434
435   buf = xmalloc (bufmax);
436   bufact = 0;
437
438   ADDC (lr->buf[lr->idx - 1]);
439
440   while (!isspace ((ch = lr_getc (lr))) && ch != '"' && ch != ';'
441          && ch != '<' && ch != ',')
442     /* XXX Handle escape sequences?  */
443     ADDC (ch);
444
445   lr_ungetn (lr, 1);
446
447   kw = lr->hash_fct (buf, bufact);
448
449   if (kw != NULL && kw->symname_or_ident == 0)
450     {
451       lr->token.tok = kw->token;
452       free (buf);
453     }
454   else
455     {
456       lr->token.tok = tok_ident;
457
458       buf[bufact] = '\0';
459       buf = xrealloc (buf, bufact + 1);
460
461       lr->token.val.str.start = buf;
462       lr->token.val.str.len = bufact;
463     }
464
465   return &lr->token;
466 }
467
468
469 static struct token *
470 get_string (struct linereader *lr, const struct charset_t *charset)
471 {
472   int illegal_string = 0;
473   char *buf, *cp;
474   size_t bufact;
475   size_t bufmax = 56;
476   int ch;
477
478   buf = xmalloc (bufmax);
479   bufact = 0;
480
481   while ((ch = lr_getc (lr)) != '"' && ch != '\n' && ch != EOF)
482     if (ch != '<' || charset == NULL)
483       {
484         if (ch == lr->escape_char)
485           {
486             ch = lr_getc (lr);
487             if (ch == '\n' || ch == EOF)
488               break;
489           }
490         ADDC (ch);
491       }
492     else
493       {
494         /* We have to get the value of the symbol.  */
495         unsigned int value;
496         size_t startidx = bufact;
497
498         if (!lr->translate_strings)
499           ADDC ('<');
500
501         while ((ch = lr_getc (lr)) != '>' && ch != '\n' && ch != EOF)
502           {
503             if (ch == lr->escape_char)
504               {
505                 ch = lr_getc (lr);
506                 if (ch == '\n' || ch == EOF)
507                   break;
508               }
509             ADDC (ch);
510           }
511
512         if (ch == '\n' || ch == EOF)
513           lr_error (lr, _("unterminated string"));
514         else
515           if (!lr->translate_strings)
516             ADDC ('>');
517
518         if (lr->translate_strings)
519           {
520             value = charset_find_value (charset, &buf[startidx],
521                                         bufact - startidx);
522             if (value == ILLEGAL_CHAR_VALUE)
523               illegal_string = 1;
524             bufact = startidx;
525
526             if (bufmax - bufact < 8)
527               {
528                 bufmax *= 2;
529                 buf = (char *) xrealloc (buf, bufmax);
530               }
531
532             cp = &buf[bufact];
533             if (encode_char (value, &cp))
534               illegal_string = 1;
535
536             bufact = cp - buf;
537           }
538       }
539
540   /* Catch errors with trailing escape character.  */
541   if (bufact > 0 && buf[bufact - 1] == lr->escape_char
542       && (bufact == 1 || buf[bufact - 2] != lr->escape_char))
543     {
544       lr_error (lr, _("illegal escape sequence at end of string"));
545       --bufact;
546     }
547   else if (ch == '\n' || ch == EOF)
548     lr_error (lr, _("unterminated string"));
549
550   /* Terminate string if necessary.  */
551   if (lr->translate_strings)
552     {
553       cp = &buf[bufact];
554       if (encode_char (0, &cp))
555         illegal_string = 1;
556
557       bufact = cp - buf;
558     }
559   else
560     ADDC ('\0');
561
562   lr->token.tok = tok_string;
563
564   if (illegal_string)
565     {
566       free (buf);
567       lr->token.val.str.start = NULL;
568       lr->token.val.str.len = 0;
569     }
570   else
571     {
572       buf = xrealloc (buf, bufact + 1);
573
574       lr->token.val.str.start = buf;
575       lr->token.val.str.len = bufact;
576     }
577
578   return &lr->token;
579 }