]> git.ipfire.org Git - thirdparty/glibc.git/blob - locale/programs/charmap.c
6b4ebb4a6003d34e6df8f05b4adc6ef21a998ab3
[thirdparty/glibc.git] / locale / programs / charmap.c
1 /* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1996.
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 not,
17 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 <obstack.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "error.h"
33 #include "linereader.h"
34 #include "charset.h"
35
36
37 /* Uncomment following line for production version. */
38 /* define NDEBUG 1 */
39 #include <assert.h>
40
41
42 /* Define the lookup function. */
43 #include "charmap-kw.h"
44
45
46 void *xmalloc (size_t __n);
47
48 /* Prototypes for local functions. */
49 static struct charset_t *parse_charmap (const char *filename);
50 static void new_width (struct linereader *cmfile, struct charset_t *result,
51 const char *from, const char *to,
52 unsigned long int width);
53
54
55 struct charset_t *
56 charmap_read (const char *filename)
57 {
58 const char *pathnfile;
59 struct charset_t *result = NULL;
60
61 if (filename != NULL)
62 {
63 if (euidaccess (filename, R_OK) >= 0)
64 pathnfile = filename;
65 else
66 {
67 char *cp = xmalloc (strlen (filename) + sizeof CHARMAP_PATH + 1);
68 stpcpy (stpcpy (stpcpy (cp, CHARMAP_PATH), "/"), filename);
69
70 pathnfile = (const char *) cp;
71 }
72
73 result = parse_charmap (pathnfile);
74
75 if (result == NULL)
76 error (0, errno, _("character map file `%s' not found"), filename);
77 }
78
79 if (result == NULL)
80 {
81 pathnfile = CHARMAP_PATH "/" DEFAULT_CHARMAP;
82
83 result = parse_charmap (pathnfile);
84
85 if (result == NULL)
86 error (4, errno, _("default character map file `%s' not found"),
87 DEFAULT_CHARMAP);
88 }
89
90 return result;
91 }
92
93
94 static struct charset_t *
95 parse_charmap (const char *filename)
96 {
97 struct linereader *cmfile;
98 struct charset_t *result;
99 int state;
100 enum token_t expected_tok = tok_error;
101 const char *expected_str = NULL;
102 char *from_name = NULL;
103 char *to_name = NULL;
104
105 /* Determine path. */
106 cmfile = lr_open (filename, charmap_hash);
107 if (cmfile == NULL)
108 {
109 if (strchr (filename, '/') == NULL)
110 {
111 /* Look in the systems charmap directory. */
112 char *buf = xmalloc (strlen (filename) + 1 + sizeof (CHARMAP_PATH));
113
114 stpcpy (stpcpy (stpcpy (buf, CHARMAP_PATH), "/"), filename);
115 cmfile = lr_open (buf, charmap_hash);
116
117 if (cmfile == NULL)
118 free (buf);
119 }
120
121 if (cmfile == NULL)
122 return NULL;
123 }
124
125 /* Allocate room for result. */
126 result = (struct charset_t *) xmalloc (sizeof (struct charset_t));
127 memset (result, '\0', sizeof (struct charset_t));
128 /* The default DEFAULT_WIDTH is 1. */
129 result->width_default = 1;
130
131 #define obstack_chunk_alloc malloc
132 #define obstack_chunk_free free
133 obstack_init (&result->mem_pool);
134
135 if (init_hash (&result->char_table, 256))
136 {
137 free (result);
138 return NULL;
139 }
140
141 /* We use a state machine to describe the charmap description file
142 format. */
143 state = 1;
144 while (1)
145 {
146 /* What's on? */
147 struct token *now = lr_token (cmfile, NULL);
148 enum token_t nowtok = now->tok;
149 struct token *arg;
150
151 if (nowtok == tok_eof)
152 break;
153
154 switch (state)
155 {
156 case 1:
157 /* The beginning. We expect the special declarations, EOL or
158 `CHARMAP'. */
159 if (nowtok == tok_eol)
160 /* Ignore empty lines. */
161 continue;
162
163 if (nowtok == tok_charmap)
164 {
165 from_name = NULL;
166 to_name = NULL;
167
168 /* We have to set up the real work. Fill in some
169 default values. */
170 if (result->mb_cur_max == 0)
171 result->mb_cur_max = 1;
172 if (result->mb_cur_min == 0)
173 result->mb_cur_min = result->mb_cur_max;
174 if (result->mb_cur_min > result->mb_cur_max)
175 {
176 error (0, 0, _("\
177 %s: <mb_cur_max> must be greater than <mb_cur_min>\n"),
178 cmfile->fname);
179
180 result->mb_cur_min = result->mb_cur_max;
181 }
182
183 lr_ignore_rest (cmfile, 1);
184
185 state = 2;
186 continue;
187 }
188
189 if (nowtok != tok_code_set_name && nowtok != tok_mb_cur_max
190 && nowtok != tok_mb_cur_min && nowtok != tok_escape_char
191 && nowtok != tok_comment_char && nowtok != tok_g0esc
192 && nowtok != tok_g1esc && nowtok != tok_g2esc
193 && nowtok != tok_g3esc)
194 {
195 lr_error (cmfile, _("syntax error in prolog: %s"),
196 _("illegal definition"));
197
198 lr_ignore_rest (cmfile, 0);
199 continue;
200 }
201
202 /* We know that we need an argument. */
203 arg = lr_token (cmfile, NULL);
204
205 switch (nowtok)
206 {
207 case tok_code_set_name:
208 if (arg->tok != tok_ident)
209 {
210 badarg:
211 lr_error (cmfile, _("syntax error in prolog: %s"),
212 _("bad argument"));
213
214 lr_ignore_rest (cmfile, 0);
215 continue;
216 }
217
218 result->code_set_name = obstack_copy0 (&result->mem_pool,
219 arg->val.str.start,
220 arg->val.str.len);
221
222 lr_ignore_rest (cmfile, 1);
223 continue;
224
225 case tok_mb_cur_max:
226 case tok_mb_cur_min:
227 if (arg->tok != tok_number)
228 goto badarg;
229
230 if (arg->val.num < 1 || arg->val.num > 4)
231 {
232 lr_error (cmfile,
233 _("value for <%s> must lie between 1 and 4"),
234 nowtok == tok_mb_cur_min ? "mb_cur_min"
235 : "mb_cur_max");
236
237 lr_ignore_rest (cmfile, 0);
238 continue;
239 }
240 if ((nowtok == tok_mb_cur_max && result->mb_cur_min != 0
241 && (int) arg->val.num < result->mb_cur_min)
242 || (nowtok == tok_mb_cur_min && result->mb_cur_max != 0
243 && (int) arg->val.num > result->mb_cur_max))
244 {
245 lr_error (cmfile, _("\
246 value of <mb_cur_max> must be greater than the value of <mb_cur_min>"));
247
248 lr_ignore_rest (cmfile, 0);
249 continue;
250 }
251
252 if (nowtok == tok_mb_cur_max)
253 result->mb_cur_max = arg->val.num;
254 else
255 result->mb_cur_min = arg->val.num;
256
257 lr_ignore_rest (cmfile, 1);
258 continue;
259
260 case tok_escape_char:
261 case tok_comment_char:
262 if (arg->tok != tok_ident)
263 goto badarg;
264
265 if (arg->val.str.len != 1)
266 {
267 lr_error (cmfile, _("\
268 argument to <%s> must be a single character"),
269 nowtok == tok_escape_char ? "escape_char"
270 : "comment_char");
271
272 lr_ignore_rest (cmfile, 0);
273 continue;
274 }
275
276 if (nowtok == tok_escape_char)
277 cmfile->escape_char = *arg->val.str.start;
278 else
279 cmfile->comment_char = *arg->val.str.start;
280
281 lr_ignore_rest (cmfile, 1);
282 continue;
283
284 case tok_g0esc:
285 case tok_g1esc:
286 case tok_g2esc:
287 case tok_g3esc:
288 lr_ignore_rest (cmfile, 0); /* XXX */
289 continue;
290
291 default:
292 /* Cannot happen. */
293 assert (! "Should not happen");
294 }
295 break;
296
297 case 2:
298 /* We have seen `CHARMAP' and now are in the body. Each line
299 must have the format "%s %s %s\n" or "%s...%s %s %s\n". */
300 if (nowtok == tok_eol)
301 /* Ignore empty lines. */
302 continue;
303
304 if (nowtok == tok_end)
305 {
306 expected_tok = tok_charmap;
307 expected_str = "CHARMAP";
308 state = 90;
309 continue;
310 }
311
312 if (nowtok != tok_bsymbol)
313 {
314 lr_error (cmfile, _("syntax error in %s definition: %s"),
315 "CHARMAP", _("no symbolic name given"));
316
317 lr_ignore_rest (cmfile, 0);
318 continue;
319 }
320
321 /* If the previous line was not completely correct free the
322 used memory. */
323 if (from_name != NULL)
324 obstack_free (&result->mem_pool, from_name);
325
326 from_name = (char *) obstack_copy0 (&result->mem_pool,
327 now->val.str.start,
328 now->val.str.len);
329 to_name = NULL;
330
331 state = 3;
332 continue;
333
334 case 3:
335 /* We have two possibilities: We can see an ellipsis or an
336 encoding value. */
337 if (nowtok == tok_ellipsis)
338 {
339 state = 4;
340 continue;
341 }
342 /* FALLTHROUGH */
343
344 case 5:
345 if (nowtok != tok_charcode && nowtok != tok_ucs2
346 && nowtok != tok_ucs4)
347 {
348 lr_error (cmfile, _("syntax error in %s definition: %s"),
349 "CHARMAP", _("illegal encoding given"));
350
351 lr_ignore_rest (cmfile, 0);
352
353 state = 2;
354 continue;
355 }
356
357 if (nowtok == tok_charcode)
358 /* Write char value in table. */
359 charset_new_char (cmfile, result, now->val.charcode.nbytes,
360 now->val.charcode.val, from_name, to_name);
361 else
362 /* Determine ISO 10646 value and write into table. */
363 charset_new_unicode (cmfile, result, now->val.charcode.nbytes,
364 now->val.charcode.val, from_name, to_name);
365
366 /* Ignore trailing comment silently. */
367 lr_ignore_rest (cmfile, 0);
368
369 from_name = NULL;
370 to_name = NULL;
371
372 state = 2;
373 continue;
374
375 case 4:
376 if (nowtok != tok_bsymbol)
377 {
378 lr_error (cmfile, _("syntax error in %s definition: %s"),
379 "CHARMAP",
380 _("no symbolic name given for end of range"));
381
382 lr_ignore_rest (cmfile, 0);
383 continue;
384 }
385
386 /* If the previous line was not completely correct free the
387 used memory. */
388 to_name = (char *) obstack_copy0 (&result->mem_pool,
389 cmfile->token.val.str.start,
390 cmfile->token.val.str.len);
391
392 state = 3;
393 continue;
394
395 case 90:
396 if (nowtok != expected_tok)
397 lr_error (cmfile, _("\
398 `%1$s' definition does not end with `END %1$s'"), expected_str);
399
400 lr_ignore_rest (cmfile, nowtok == expected_tok);
401 state = 91;
402 continue;
403
404 case 91:
405 /* Waiting for WIDTH... */
406 if (nowtok == tok_eol)
407 /* Ignore empty lines. */
408 continue;
409
410 if (nowtok == tok_width_default)
411 {
412 state = 92;
413 continue;
414 }
415
416 if (nowtok == tok_width)
417 {
418 lr_ignore_rest (cmfile, 1);
419 state = 93;
420 continue;
421 }
422
423 if (nowtok == tok_width_variable)
424 {
425 lr_ignore_rest (cmfile, 1);
426 state = 98;
427 continue;
428 }
429
430 lr_error (cmfile, _("\
431 only WIDTH definitions are allowed to follow the CHARMAP definition"));
432
433 lr_ignore_rest (cmfile, 0);
434 continue;
435
436 case 92:
437 if (nowtok != tok_number)
438 lr_error (cmfile, _("value for %s must be an integer"),
439 "WIDTH_DEFAULT");
440 else
441 result->width_default = now->val.num;
442
443 lr_ignore_rest (cmfile, nowtok == tok_number);
444
445 state = 91;
446 continue;
447
448 case 93:
449 /* We now expect `END WIDTH' or lines of the format "%s %d\n" or
450 "%s...%s %d\n". */
451 if (nowtok == tok_eol)
452 /* ignore empty lines. */
453 continue;
454
455 if (nowtok == tok_end)
456 {
457 expected_tok = tok_width;
458 expected_str = "WIDTH";
459 state = 90;
460 continue;
461 }
462
463 if (nowtok != tok_bsymbol)
464 {
465 lr_error (cmfile, _("syntax error in %s definition: %s"),
466 "WIDTH", _("no symbolic name given"));
467
468 lr_ignore_rest (cmfile, 0);
469 continue;
470 }
471
472 if (from_name != NULL)
473 obstack_free (&result->mem_pool, from_name);
474
475 from_name = (char *) obstack_copy0 (&result->mem_pool,
476 now->val.str.start,
477 now->val.str.len);
478 to_name = NULL;
479
480 state = 94;
481 continue;
482
483 case 94:
484 if (nowtok == tok_ellipsis)
485 {
486 state = 95;
487 continue;
488 }
489
490 case 96:
491 if (nowtok != tok_number)
492 lr_error (cmfile, _("value for %s must be an integer"),
493 "WIDTH");
494 else
495 {
496 /* Store width for chars. */
497 new_width (cmfile, result, from_name, to_name, now->val.num);
498
499 from_name = NULL;
500 to_name = NULL;
501 }
502
503 lr_ignore_rest (cmfile, nowtok == tok_number);
504
505 state = 93;
506 continue;
507
508 case 95:
509 if (nowtok != tok_bsymbol)
510 {
511 lr_error (cmfile, _("syntax error in %s definition: %s"),
512 "WIDTH", _("no symbolic name given for end of range"));
513
514 lr_ignore_rest (cmfile, 0);
515
516 state = 93;
517 continue;
518 }
519
520 to_name = (char *) obstack_copy0 (&result->mem_pool,
521 now->val.str.start,
522 now->val.str.len);
523
524 state = 96;
525 continue;
526
527 case 98:
528 /* We now expect `END WIDTH_VARIABLE' or lines of the format
529 "%s\n" or "%s...%s\n". */
530 if (nowtok == tok_eol)
531 /* ignore empty lines. */
532 continue;
533
534 if (nowtok == tok_end)
535 {
536 expected_tok = tok_width_variable;
537 expected_str = "WIDTH_VARIABLE";
538 state = 90;
539 continue;
540 }
541
542 if (nowtok != tok_bsymbol)
543 {
544 lr_error (cmfile, _("syntax error in %s definition: %s"),
545 "WIDTH_VARIABLE", _("no symbolic name given"));
546
547 lr_ignore_rest (cmfile, 0);
548
549 continue;
550 }
551
552 if (from_name != NULL)
553 obstack_free (&result->mem_pool, from_name);
554
555 from_name = (char *) obstack_copy0 (&result->mem_pool,
556 now->val.str.start,
557 now->val.str.len);
558 to_name = NULL;
559
560 state = 99;
561 continue;
562
563 case 99:
564 if (nowtok == tok_ellipsis)
565 state = 100;
566
567 /* Store info. */
568 from_name = NULL;
569
570 /* Warn */
571 state = 98;
572 continue;
573
574 case 100:
575 if (nowtok != tok_bsymbol)
576 lr_error (cmfile, _("syntax error in %s definition: %s"),
577 "WIDTH_VARIABLE",
578 _("no symbolic name given for end of range"));
579 else
580 {
581 to_name = (char *) obstack_copy0 (&result->mem_pool,
582 now->val.str.start,
583 now->val.str.len);
584 /* XXX Enter value into table. */
585 }
586
587 lr_ignore_rest (cmfile, nowtok == tok_bsymbol);
588
589 state = 98;
590 continue;
591
592 default:
593 error (5, 0, _("%s: error in state machine"), __FILE__);
594 /* NOTREACHED */
595 }
596 break;
597 }
598
599 if (state != 91)
600 error (0, 0, _("%s: premature end of file"), cmfile->fname);
601
602 lr_close (cmfile);
603
604 return result;
605 }
606
607
608 static void
609 new_width (struct linereader *cmfile, struct charset_t *result,
610 const char *from, const char *to, unsigned long int width)
611 {
612 unsigned int from_val, to_val;
613
614 from_val = charset_find_value (result, from, strlen (from));
615 if ((wchar_t) from_val == ILLEGAL_CHAR_VALUE)
616 {
617 lr_error (cmfile, _("unknown character `%s'"), from);
618 return;
619 }
620
621 if (to == NULL)
622 to_val = from_val;
623 else
624 {
625 to_val = charset_find_value (result, to, strlen (to));
626 if ((wchar_t) to_val == ILLEGAL_CHAR_VALUE)
627 {
628 lr_error (cmfile, _("unknown character `%s'"), to);
629 return;
630 }
631 }
632
633 if (result->nwidth_rules >= result->nwidth_rules_max)
634 {
635 size_t new_size = result->nwidth_rules + 32;
636 struct width_rule *new_rules =
637 (struct width_rule *) obstack_alloc (&result->mem_pool,
638 (new_size
639 * sizeof (struct width_rule)));
640
641 memcpy (new_rules, result->width_rules,
642 result->nwidth_rules_max * sizeof (struct width_rule));
643
644 result->width_rules = new_rules;
645 result->nwidth_rules_max = new_size;
646 }
647
648 result->width_rules[result->nwidth_rules].from = from_val;
649 result->width_rules[result->nwidth_rules].to = to_val;
650 result->width_rules[result->nwidth_rules].width = (unsigned int) width;
651 ++result->nwidth_rules;
652 }