1/* Copyright (C) 1996-2024 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published
6 by the Free Software Foundation; version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, see <https://www.gnu.org/licenses/>. */
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20
21#include <assert.h>
22#include <ctype.h>
23#include <errno.h>
24#include <libintl.h>
25#include <stdarg.h>
26#include <stdlib.h>
27#include <string.h>
28#include <stdint.h>
29
30#include "localedef.h"
31#include "charmap.h"
32#include "error.h"
33#include "linereader.h"
34#include "locfile.h"
35
36/* Prototypes for local functions. */
37static struct token *get_toplvl_escape (struct linereader *lr);
38static struct token *get_symname (struct linereader *lr);
39static struct token *get_ident (struct linereader *lr);
40static struct token *get_string (struct linereader *lr,
41 const struct charmap_t *charmap,
42 struct localedef_t *locale,
43 const struct repertoire_t *repertoire,
44 int verbose);
45static bool utf8_decode (struct linereader *lr, uint8_t ch1, uint32_t *wch);
46
47
48struct linereader *
49lr_open (const char *fname, kw_hash_fct_t hf)
50{
51 FILE *fp;
52
53 if (fname == NULL || strcmp (s1: fname, s2: "-") == 0
54 || strcmp (s1: fname, s2: "/dev/stdin") == 0)
55 return lr_create (stdin, fname: "<stdin>", hf);
56 else
57 {
58 fp = fopen (filename: fname, modes: "rm");
59 if (fp == NULL)
60 return NULL;
61 return lr_create (fp, fname, hf);
62 }
63}
64
65struct linereader *
66lr_create (FILE *fp, const char *fname, kw_hash_fct_t hf)
67{
68 struct linereader *result;
69 int n;
70
71 result = (struct linereader *) xmalloc (n: sizeof (*result));
72
73 result->fp = fp;
74 result->fname = xstrdup (fname);
75 result->buf = NULL;
76 result->bufsize = 0;
77 result->lineno = 1;
78 result->idx = 0;
79 result->comment_char = '#';
80 result->escape_char = '\\';
81 result->translate_strings = 1;
82 result->return_widestr = 0;
83
84 n = getdelim (lineptr: &result->buf, n: &result->bufsize, delimiter: '\n', stream: result->fp);
85 if (n < 0)
86 {
87 int save = errno;
88 fclose (stream: result->fp);
89 free (ptr: (char *) result->fname);
90 free (ptr: result);
91 errno = save;
92 return NULL;
93 }
94
95 if (n > 1 && result->buf[n - 2] == '\\' && result->buf[n - 1] == '\n')
96 n -= 2;
97
98 result->buf[n] = '\0';
99 result->bufact = n;
100 result->hash_fct = hf;
101
102 return result;
103}
104
105
106int
107lr_eof (struct linereader *lr)
108{
109 return lr->bufact = 0;
110}
111
112
113void
114lr_ignore_rest (struct linereader *lr, int verbose)
115{
116 if (verbose)
117 {
118 while (isspace (lr->buf[lr->idx]) && lr->buf[lr->idx] != '\n'
119 && lr->buf[lr->idx] != lr->comment_char)
120 if (lr->buf[lr->idx] == '\0')
121 {
122 if (lr_next (lr) < 0)
123 return;
124 }
125 else
126 ++lr->idx;
127
128 if (lr->buf[lr->idx] != '\n' && ! feof (stream: lr->fp)
129 && lr->buf[lr->idx] != lr->comment_char)
130 lr_error (lr, _("trailing garbage at end of line"));
131 }
132
133 /* Ignore continued line. */
134 while (lr->bufact > 0 && lr->buf[lr->bufact - 1] != '\n')
135 if (lr_next (lr) < 0)
136 break;
137
138 lr->idx = lr->bufact;
139}
140
141
142void
143lr_close (struct linereader *lr)
144{
145 fclose (stream: lr->fp);
146 free (ptr: lr->buf);
147 free (ptr: lr);
148}
149
150
151int
152lr_next (struct linereader *lr)
153{
154 int n;
155
156 n = getdelim (lineptr: &lr->buf, n: &lr->bufsize, delimiter: '\n', stream: lr->fp);
157 if (n < 0)
158 return -1;
159
160 ++lr->lineno;
161
162 if (n > 1 && lr->buf[n - 2] == lr->escape_char && lr->buf[n - 1] == '\n')
163 {
164#if 0
165 /* XXX Is this correct? */
166 /* An escaped newline character is substituted with a single <SP>. */
167 --n;
168 lr->buf[n - 1] = ' ';
169#else
170 n -= 2;
171#endif
172 }
173
174 lr->buf[n] = '\0';
175 lr->bufact = n;
176 lr->idx = 0;
177
178 return 0;
179}
180
181
182/* Defined in error.c. */
183/* This variable is incremented each time `error' is called. */
184extern unsigned int error_message_count;
185
186/* The calling program should define program_name and set it to the
187 name of the executing program. */
188extern char *program_name;
189
190
191struct token *
192lr_token (struct linereader *lr, const struct charmap_t *charmap,
193 struct localedef_t *locale, const struct repertoire_t *repertoire,
194 int verbose)
195{
196 int ch;
197
198 while (1)
199 {
200 do
201 {
202 ch = lr_getc (lr);
203
204 if (ch == EOF)
205 {
206 lr->token.tok = tok_eof;
207 return &lr->token;
208 };
209
210 if (ch == '\n')
211 {
212 lr->token.tok = tok_eol;
213 return &lr->token;
214 }
215 }
216 while (isspace (ch));
217
218 if (ch != lr->comment_char)
219 break;
220
221 /* Is there an newline at the end of the buffer? */
222 if (lr->buf[lr->bufact - 1] != '\n')
223 {
224 /* No. Some people want this to mean that only the line in
225 the file not the logical, concatenated line is ignored.
226 Let's try this. */
227 lr->idx = lr->bufact;
228 continue;
229 }
230
231 /* Ignore rest of line. */
232 lr_ignore_rest (lr, verbose: 0);
233 lr->token.tok = tok_eol;
234 return &lr->token;
235 }
236
237 /* Match escape sequences. */
238 if (ch == lr->escape_char)
239 return get_toplvl_escape (lr);
240
241 /* Match ellipsis. */
242 if (ch == '.')
243 {
244 if (strncmp (s1: &lr->buf[lr->idx], s2: "...(2)....", n: 10) == 0)
245 {
246 int cnt;
247 for (cnt = 0; cnt < 10; ++cnt)
248 lr_getc (lr);
249 lr->token.tok = tok_ellipsis4_2;
250 return &lr->token;
251 }
252 if (strncmp (s1: &lr->buf[lr->idx], s2: "...", n: 3) == 0)
253 {
254 lr_getc (lr);
255 lr_getc (lr);
256 lr_getc (lr);
257 lr->token.tok = tok_ellipsis4;
258 return &lr->token;
259 }
260 if (strncmp (s1: &lr->buf[lr->idx], s2: "..", n: 2) == 0)
261 {
262 lr_getc (lr);
263 lr_getc (lr);
264 lr->token.tok = tok_ellipsis3;
265 return &lr->token;
266 }
267 if (strncmp (s1: &lr->buf[lr->idx], s2: ".(2)..", n: 6) == 0)
268 {
269 int cnt;
270 for (cnt = 0; cnt < 6; ++cnt)
271 lr_getc (lr);
272 lr->token.tok = tok_ellipsis2_2;
273 return &lr->token;
274 }
275 if (lr->buf[lr->idx] == '.')
276 {
277 lr_getc (lr);
278 lr->token.tok = tok_ellipsis2;
279 return &lr->token;
280 }
281 }
282
283 switch (ch)
284 {
285 case '<':
286 return get_symname (lr);
287
288 case '0' ... '9':
289 lr->token.tok = tok_number;
290 lr->token.val.num = ch - '0';
291
292 while (isdigit (ch = lr_getc (lr)))
293 {
294 lr->token.val.num *= 10;
295 lr->token.val.num += ch - '0';
296 }
297 if (isalpha (ch))
298 lr_error (lr, _("garbage at end of number"));
299 lr_ungetn (lr, n: 1);
300
301 return &lr->token;
302
303 case ';':
304 lr->token.tok = tok_semicolon;
305 return &lr->token;
306
307 case ',':
308 lr->token.tok = tok_comma;
309 return &lr->token;
310
311 case '(':
312 lr->token.tok = tok_open_brace;
313 return &lr->token;
314
315 case ')':
316 lr->token.tok = tok_close_brace;
317 return &lr->token;
318
319 case '"':
320 return get_string (lr, charmap, locale, repertoire, verbose);
321
322 case '-':
323 ch = lr_getc (lr);
324 if (ch == '1')
325 {
326 lr->token.tok = tok_minus1;
327 return &lr->token;
328 }
329 lr_ungetn (lr, n: 2);
330 break;
331
332 case 0x80 ... 0xff: /* UTF-8 sequence. */
333 {
334 uint32_t wch;
335 if (!utf8_decode (lr, ch1: ch, wch: &wch))
336 {
337 lr->token.tok = tok_error;
338 return &lr->token;
339 }
340 lr->token.tok = tok_ucs4;
341 lr->token.val.ucs4 = wch;
342 return &lr->token;
343 }
344 }
345
346 return get_ident (lr);
347}
348
349
350static struct token *
351get_toplvl_escape (struct linereader *lr)
352{
353 /* This is supposed to be a numeric value. We return the
354 numerical value and the number of bytes. */
355 size_t start_idx = lr->idx - 1;
356 unsigned char *bytes = lr->token.val.charcode.bytes;
357 size_t nbytes = 0;
358 int ch;
359
360 do
361 {
362 unsigned int byte = 0;
363 unsigned int base = 8;
364
365 ch = lr_getc (lr);
366
367 if (ch == 'd')
368 {
369 base = 10;
370 ch = lr_getc (lr);
371 }
372 else if (ch == 'x')
373 {
374 base = 16;
375 ch = lr_getc (lr);
376 }
377
378 if ((base == 16 && !isxdigit (ch))
379 || (base != 16 && (ch < '0' || ch >= (int) ('0' + base))))
380 {
381 esc_error:
382 lr->token.val.str.startmb = &lr->buf[start_idx];
383
384 while (ch != EOF && !isspace (ch))
385 ch = lr_getc (lr);
386 lr->token.val.str.lenmb = lr->idx - start_idx;
387
388 lr->token.tok = tok_error;
389 return &lr->token;
390 }
391
392 if (isdigit (ch))
393 byte = ch - '0';
394 else
395 byte = tolower (ch) - 'a' + 10;
396
397 ch = lr_getc (lr);
398 if ((base == 16 && !isxdigit (ch))
399 || (base != 16 && (ch < '0' || ch >= (int) ('0' + base))))
400 goto esc_error;
401
402 byte *= base;
403 if (isdigit (ch))
404 byte += ch - '0';
405 else
406 byte += tolower (ch) - 'a' + 10;
407
408 ch = lr_getc (lr);
409 if (base != 16 && isdigit (ch))
410 {
411 byte *= base;
412 byte += ch - '0';
413
414 ch = lr_getc (lr);
415 }
416
417 bytes[nbytes++] = byte;
418 }
419 while (ch == lr->escape_char
420 && nbytes < (int) sizeof (lr->token.val.charcode.bytes));
421
422 if (!isspace (ch))
423 lr_error (lr, _("garbage at end of character code specification"));
424
425 lr_ungetn (lr, n: 1);
426
427 lr->token.tok = tok_charcode;
428 lr->token.val.charcode.nbytes = nbytes;
429
430 return &lr->token;
431}
432
433/* Multibyte string buffer. */
434struct lr_buffer
435{
436 size_t act;
437 size_t max;
438 char *buf;
439};
440
441/* Initialize *LRB with a default-sized buffer. */
442static void
443lr_buffer_init (struct lr_buffer *lrb)
444{
445 lrb->act = 0;
446 lrb->max = 56;
447 lrb->buf = xmalloc (n: lrb->max);
448}
449
450/* Transfers the buffer string from *LRB to LR->token.mbstr. */
451static void
452lr_buffer_to_token (struct lr_buffer *lrb, struct linereader *lr)
453{
454 lr->token.val.str.startmb = xrealloc (o: lrb->buf, n: lrb->act + 1);
455 lr->token.val.str.startmb[lrb->act] = '\0';
456 lr->token.val.str.lenmb = lrb->act;
457}
458
459/* Adds CH to *LRB. */
460static void
461addc (struct lr_buffer *lrb, char ch)
462{
463 if (lrb->act == lrb->max)
464 {
465 lrb->max *= 2;
466 lrb->buf = xrealloc (o: lrb->buf, n: lrb->max);
467 }
468 lrb->buf[lrb->act++] = ch;
469}
470
471/* Adds L bytes at S to *LRB. */
472static void
473adds (struct lr_buffer *lrb, const unsigned char *s, size_t l)
474{
475 if (lrb->max - lrb->act < l)
476 {
477 size_t required_size = lrb->act + l;
478 size_t new_max = 2 * lrb->max;
479 if (new_max < required_size)
480 new_max = required_size;
481 lrb->buf = xrealloc (o: lrb->buf, n: new_max);
482 lrb->max = new_max;
483 }
484 memcpy (dest: lrb->buf + lrb->act, src: s, n: l);
485 lrb->act += l;
486}
487
488#define ADDWC(ch) \
489 do \
490 { \
491 if (buf2act == buf2max) \
492 { \
493 buf2max *= 2; \
494 buf2 = xrealloc (buf2, buf2max * 4); \
495 } \
496 buf2[buf2act++] = (ch); \
497 } \
498 while (0)
499
500
501static struct token *
502get_symname (struct linereader *lr)
503{
504 /* Symbol in brackets. We must distinguish three kinds:
505 1. reserved words
506 2. ISO 10646 position values
507 3. all other. */
508 const struct keyword_t *kw;
509 int ch;
510 struct lr_buffer lrb;
511
512 lr_buffer_init (lrb: &lrb);
513
514 do
515 {
516 ch = lr_getc (lr);
517 if (ch == lr->escape_char)
518 {
519 int c2 = lr_getc (lr);
520 addc (lrb: &lrb, ch: c2);
521
522 if (c2 == '\n')
523 ch = '\n';
524 }
525 else
526 addc (lrb: &lrb, ch);
527 }
528 while (ch != '>' && ch != '\n');
529
530 if (ch == '\n')
531 lr_error (lr, _("unterminated symbolic name"));
532
533 /* Test for ISO 10646 position value. */
534 if (lrb.buf[0] == 'U' && (lrb.act == 6 || lrb.act == 10))
535 {
536 char *cp = lrb.buf + 1;
537 while (cp < &lrb.buf[lrb.act - 1] && isxdigit (*cp))
538 ++cp;
539
540 if (cp == &lrb.buf[lrb.act - 1])
541 {
542 /* Yes, it is. */
543 lr->token.tok = tok_ucs4;
544 lr->token.val.ucs4 = strtoul (lrb.buf + 1, NULL, 16);
545
546 return &lr->token;
547 }
548 }
549
550 /* It is a symbolic name. Test for reserved words. */
551 kw = lr->hash_fct (lrb.buf, lrb.act - 1);
552
553 if (kw != NULL && kw->symname_or_ident == 1)
554 {
555 lr->token.tok = kw->token;
556 free (ptr: lrb.buf);
557 }
558 else
559 {
560 lr->token.tok = tok_bsymbol;
561 lr_buffer_to_token (lrb: &lrb, lr);
562 --lr->token.val.str.lenmb; /* Hide the training '>'. */
563 }
564
565 return &lr->token;
566}
567
568
569static struct token *
570get_ident (struct linereader *lr)
571{
572 const struct keyword_t *kw;
573 int ch;
574 struct lr_buffer lrb;
575
576 lr_buffer_init (lrb: &lrb);
577
578 addc (lrb: &lrb, ch: lr->buf[lr->idx - 1]);
579
580 while (!isspace ((ch = lr_getc (lr))) && ch != '"' && ch != ';'
581 && ch != '<' && ch != ',' && ch != EOF)
582 {
583 if (ch == lr->escape_char)
584 {
585 ch = lr_getc (lr);
586 if (ch == '\n' || ch == EOF)
587 {
588 lr_error (lr, _("invalid escape sequence"));
589 break;
590 }
591 }
592 addc (lrb: &lrb, ch);
593 }
594
595 lr_ungetc (lr, ch);
596
597 kw = lr->hash_fct (lrb.buf, lrb.act);
598
599 if (kw != NULL && kw->symname_or_ident == 0)
600 {
601 lr->token.tok = kw->token;
602 free (ptr: lrb.buf);
603 }
604 else
605 {
606 lr->token.tok = tok_ident;
607 lr_buffer_to_token (lrb: &lrb, lr);
608 }
609
610 return &lr->token;
611}
612
613/* Process a decoded Unicode codepoint WCH in a string, placing the
614 multibyte sequence into LRB. Return false if the character is not
615 found in CHARMAP/REPERTOIRE. */
616static bool
617translate_unicode_codepoint (struct localedef_t *locale,
618 const struct charmap_t *charmap,
619 const struct repertoire_t *repertoire,
620 uint32_t wch, struct lr_buffer *lrb)
621{
622 /* See whether the charmap contains the Uxxxxxxxx names. */
623 char utmp[10];
624 snprintf (s: utmp, maxlen: sizeof (utmp), format: "U%08X", wch);
625 struct charseq *seq = charmap_find_value (charmap, name: utmp, len: 9);
626
627 if (seq == NULL)
628 {
629 /* No, this isn't the case. Now determine from
630 the repertoire the name of the character and
631 find it in the charmap. */
632 if (repertoire != NULL)
633 {
634 const char *symbol = repertoire_find_symbol (repertoire, ucs: wch);
635 if (symbol != NULL)
636 seq = charmap_find_value (charmap, name: symbol, len: strlen (s: symbol));
637 }
638
639 if (seq == NULL)
640 {
641#ifndef NO_TRANSLITERATION
642 /* Transliterate if possible. */
643 if (locale != NULL)
644 {
645 if ((locale->avail & CTYPE_LOCALE) == 0)
646 {
647 /* Load the CTYPE data now. */
648 int old_needed = locale->needed;
649
650 locale->needed = 0;
651 locale = load_locale (LC_CTYPE, locale->name,
652 locale->repertoire_name,
653 charmap, locale);
654 locale->needed = old_needed;
655 }
656
657 uint32_t *translit;
658 if ((locale->avail & CTYPE_LOCALE) != 0
659 && ((translit = find_translit (locale, charmap, wch))
660 != NULL))
661 /* The CTYPE data contains a matching
662 transliteration. */
663 {
664 for (int i = 0; translit[i] != 0; ++i)
665 {
666 snprintf (utmp, sizeof (utmp), "U%08X", translit[i]);
667 seq = charmap_find_value (charmap, utmp, 9);
668 assert (seq != NULL);
669 adds (lrb, seq->bytes, seq->nbytes);
670 }
671 return true;
672 }
673 }
674#endif /* NO_TRANSLITERATION */
675
676 /* Not a known name. */
677 return false;
678 }
679 }
680
681 if (seq != NULL)
682 {
683 adds (lrb, s: seq->bytes, l: seq->nbytes);
684 return true;
685 }
686 else
687 return false;
688}
689
690/* Returns true if ch is not EOF (that is, non-negative) and a valid
691 UTF-8 trailing byte. */
692static bool
693utf8_valid_trailing (int ch)
694{
695 return ch >= 0 && (ch & 0xc0) == 0x80;
696}
697
698/* Reports an error for a broken UTF-8 sequence. CH2 to CH4 may be
699 EOF. Always returns false. */
700static bool
701utf8_sequence_error (struct linereader *lr, uint8_t ch1, int ch2, int ch3,
702 int ch4)
703{
704 char buf[38];
705
706 if (ch2 < 0)
707 snprintf (s: buf, maxlen: sizeof (buf), format: "0x%02x", ch1);
708 else if (ch3 < 0)
709 snprintf (s: buf, maxlen: sizeof (buf), format: "0x%02x 0x%02x", ch1, ch2);
710 else if (ch4 < 0)
711 snprintf (s: buf, maxlen: sizeof (buf), format: "0x%02x 0x%02x 0x%02x", ch1, ch2, ch3);
712 else
713 snprintf (s: buf, maxlen: sizeof (buf), format: "0x%02x 0x%02x 0x%02x 0x%02x",
714 ch1, ch2, ch3, ch4);
715
716 lr_error (lr, _("invalid UTF-8 sequence %s"), buf);
717 return false;
718}
719
720/* Reads a UTF-8 sequence from LR, with the leading byte CH1, and
721 stores the decoded codepoint in *WCH. Returns false on failure and
722 reports an error. */
723static bool
724utf8_decode (struct linereader *lr, uint8_t ch1, uint32_t *wch)
725{
726 /* See RFC 3629 section 4 and __gconv_transform_utf8_internal. */
727 if (ch1 < 0xc2)
728 return utf8_sequence_error (lr, ch1, ch2: -1, ch3: -1, ch4: -1);
729
730 int ch2 = lr_getc (lr);
731 if (!utf8_valid_trailing (ch: ch2))
732 return utf8_sequence_error (lr, ch1, ch2, ch3: -1, ch4: -1);
733
734 if (ch1 <= 0xdf)
735 {
736 uint32_t result = ((ch1 & 0x1f) << 6) | (ch2 & 0x3f);
737 if (result < 0x80)
738 return utf8_sequence_error (lr, ch1, ch2, ch3: -1, ch4: -1);
739 *wch = result;
740 return true;
741 }
742
743 int ch3 = lr_getc (lr);
744 if (!utf8_valid_trailing (ch: ch3) || ch1 < 0xe0)
745 return utf8_sequence_error (lr, ch1, ch2, ch3, ch4: -1);
746
747 if (ch1 <= 0xef)
748 {
749 uint32_t result = (((ch1 & 0x0f) << 12)
750 | ((ch2 & 0x3f) << 6)
751 | (ch3 & 0x3f));
752 if (result < 0x800)
753 return utf8_sequence_error (lr, ch1, ch2, ch3, ch4: -1);
754 *wch = result;
755 return true;
756 }
757
758 int ch4 = lr_getc (lr);
759 if (!utf8_valid_trailing (ch: ch4) || ch1 < 0xf0 || ch1 > 0xf4)
760 return utf8_sequence_error (lr, ch1, ch2, ch3, ch4);
761
762 uint32_t result = (((ch1 & 0x07) << 18)
763 | ((ch2 & 0x3f) << 12)
764 | ((ch3 & 0x3f) << 6)
765 | (ch4 & 0x3f));
766 if (result < 0x10000)
767 return utf8_sequence_error (lr, ch1, ch2, ch3, ch4);
768 *wch = result;
769 return true;
770}
771
772static struct token *
773get_string (struct linereader *lr, const struct charmap_t *charmap,
774 struct localedef_t *locale, const struct repertoire_t *repertoire,
775 int verbose)
776{
777 int return_widestr = lr->return_widestr;
778 struct lr_buffer lrb;
779 wchar_t *buf2 = NULL;
780
781 lr_buffer_init (lrb: &lrb);
782
783 /* We know it'll be a string. */
784 lr->token.tok = tok_string;
785
786 /* If we need not translate the strings (i.e., expand <...> parts)
787 we can run a simple loop. */
788 if (!lr->translate_strings)
789 {
790 int ch;
791
792 buf2 = NULL;
793 while ((ch = lr_getc (lr)) != '"' && ch != '\n' && ch != EOF)
794 {
795 if (ch >= 0x80)
796 lr_error (lr, _("illegal 8-bit character in untranslated string"));
797 addc (lrb: &lrb, ch);
798 }
799
800 /* Catch errors with trailing escape character. */
801 if (lrb.act > 0 && lrb.buf[lrb.act - 1] == lr->escape_char
802 && (lrb.act == 1 || lrb.buf[lrb.act - 2] != lr->escape_char))
803 {
804 lr_error (lr, _("illegal escape sequence at end of string"));
805 --lrb.act;
806 }
807 else if (ch == '\n' || ch == EOF)
808 lr_error (lr, _("unterminated string"));
809
810 addc (lrb: &lrb, ch: '\0');
811 }
812 else
813 {
814 bool illegal_string = false;
815 size_t buf2act = 0;
816 size_t buf2max = 56 * sizeof (uint32_t);
817 int ch;
818
819 /* We have to provide the wide character result as well. */
820 if (return_widestr)
821 buf2 = xmalloc (n: buf2max);
822
823 /* Read until the end of the string (or end of the line or file). */
824 while ((ch = lr_getc (lr)) != '"' && ch != '\n' && ch != EOF)
825 {
826 size_t startidx;
827 uint32_t wch;
828 struct charseq *seq;
829
830 if (ch != '<')
831 {
832 /* The standards leave it up to the implementation to
833 decide what to do with characters which stand for
834 themselves. This implementation treats the input
835 file as encoded in UTF-8. */
836 if (ch == lr->escape_char)
837 {
838 ch = lr_getc (lr);
839 if (ch >= 0x80)
840 {
841 lr_error (lr, _("illegal 8-bit escape sequence"));
842 illegal_string = true;
843 break;
844 }
845 if (ch == '\n' || ch == EOF)
846 break;
847 addc (lrb: &lrb, ch);
848 wch = ch;
849 }
850 else if (ch < 0x80)
851 {
852 wch = ch;
853 addc (lrb: &lrb, ch);
854 }
855 else /* UTF-8 sequence. */
856 {
857 if (!utf8_decode (lr, ch1: ch, wch: &wch))
858 {
859 illegal_string = true;
860 break;
861 }
862 if (!translate_unicode_codepoint (locale, charmap,
863 repertoire, wch, lrb: &lrb))
864 {
865 /* Ignore the rest of the string. Callers may
866 skip this string because it cannot be encoded
867 in the output character set. */
868 illegal_string = true;
869 continue;
870 }
871 }
872
873 if (return_widestr)
874 ADDWC (wch);
875
876 continue;
877 }
878
879 /* Now we have to search for the end of the symbolic name, i.e.,
880 the closing '>'. */
881 startidx = lrb.act;
882 while ((ch = lr_getc (lr)) != '>' && ch != '\n' && ch != EOF)
883 {
884 if (ch == lr->escape_char)
885 {
886 ch = lr_getc (lr);
887 if (ch == '\n' || ch == EOF)
888 break;
889 }
890 addc (lrb: &lrb, ch);
891 }
892 if (ch == '\n' || ch == EOF)
893 /* Not a correct string. */
894 break;
895 if (lrb.act == startidx)
896 {
897 /* <> is no correct name. Ignore it and also signal an
898 error. */
899 illegal_string = true;
900 continue;
901 }
902
903 /* It might be a Uxxxx symbol. */
904 if (lrb.buf[startidx] == 'U'
905 && (lrb.act - startidx == 5 || lrb.act - startidx == 9))
906 {
907 char *cp = lrb.buf + startidx + 1;
908 while (cp < &lrb.buf[lrb.act] && isxdigit (*cp))
909 ++cp;
910
911 if (cp == &lrb.buf[lrb.act])
912 {
913 /* Yes, it is. */
914 addc (lrb: &lrb, ch: '\0');
915 wch = strtoul (lrb.buf + startidx + 1, NULL, 16);
916
917 /* Now forget about the name we just added. */
918 lrb.act = startidx;
919
920 if (return_widestr)
921 ADDWC (wch);
922
923 if (!translate_unicode_codepoint (locale, charmap,
924 repertoire, wch, lrb: &lrb))
925 illegal_string = true;
926 continue;
927 }
928 }
929
930 /* We now have the symbolic name in lrb.buf[startidx] to
931 lrb.buf[lrb.act-1]. Now find out the value for this character
932 in the charmap as well as in the repertoire map (in this
933 order). */
934 seq = charmap_find_value (charmap, name: &lrb.buf[startidx],
935 len: lrb.act - startidx);
936
937 if (seq == NULL)
938 {
939 /* This name is not in the charmap. */
940 lr_error (lr, _("symbol `%.*s' not in charmap"),
941 (int) (lrb.act - startidx), &lrb.buf[startidx]);
942 illegal_string = true;
943 }
944
945 if (return_widestr)
946 {
947 /* Now the same for the multibyte representation. */
948 if (seq != NULL && seq->ucs4 != UNINITIALIZED_CHAR_VALUE)
949 wch = seq->ucs4;
950 else
951 {
952 wch = repertoire_find_value (repertoire, name: &lrb.buf[startidx],
953 len: lrb.act - startidx);
954 if (seq != NULL)
955 seq->ucs4 = wch;
956 }
957
958 if (wch == ILLEGAL_CHAR_VALUE)
959 {
960 /* This name is not in the repertoire map. */
961 lr_error (lr, _("symbol `%.*s' not in repertoire map"),
962 (int) (lrb.act - startidx), &lrb.buf[startidx]);
963 illegal_string = true;
964 }
965 else
966 ADDWC (wch);
967 }
968
969 /* Now forget about the name we just added. */
970 lrb.act = startidx;
971
972 /* And copy the bytes. */
973 if (seq != NULL)
974 adds (lrb: &lrb, s: seq->bytes, l: seq->nbytes);
975 }
976
977 if (ch == '\n' || ch == EOF)
978 {
979 lr_error (lr, _("unterminated string"));
980 illegal_string = true;
981 }
982
983 if (illegal_string)
984 {
985 free (ptr: lrb.buf);
986 free (ptr: buf2);
987 lr->token.val.str.startmb = NULL;
988 lr->token.val.str.lenmb = 0;
989 lr->token.val.str.startwc = NULL;
990 lr->token.val.str.lenwc = 0;
991
992 return &lr->token;
993 }
994
995 addc (lrb: &lrb, ch: '\0');
996
997 if (return_widestr)
998 {
999 ADDWC (0);
1000 lr->token.val.str.startwc = xrealloc (o: buf2,
1001 n: buf2act * sizeof (uint32_t));
1002 lr->token.val.str.lenwc = buf2act;
1003 }
1004 }
1005
1006 lr_buffer_to_token (lrb: &lrb, lr);
1007
1008 return &lr->token;
1009}
1010

source code of glibc/locale/programs/linereader.c