1 /* Copyright (C) 1998-2022 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 <langinfo.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdint.h>
25 #include <sys/uio.h>
26
27 #include <assert.h>
28
29 #include "localedef.h"
30 #include "localeinfo.h"
31 #include "locfile.h"
32
33
34 /* The real definition of the struct for the LC_IDENTIFICATION locale. */
35 struct locale_identification_t
36 {
37 const char *title;
38 const char *source;
39 const char *address;
40 const char *contact;
41 const char *email;
42 const char *tel;
43 const char *fax;
44 const char *language;
45 const char *territory;
46 const char *audience;
47 const char *application;
48 const char *abbreviation;
49 const char *revision;
50 const char *date;
51 const char *category[__LC_LAST];
52 };
53
54
55 static const char *category_name[__LC_LAST] =
56 {
57 [LC_CTYPE] = "LC_CTYPE",
58 [LC_NUMERIC] = "LC_NUMERIC",
59 [LC_TIME] = "LC_TIME",
60 [LC_COLLATE] = "LC_COLLATE",
61 [LC_MONETARY] = "LC_MONETARY",
62 [LC_MESSAGES] = "LC_MESSAGES",
63 [LC_ALL] = "LC_ALL",
64 [LC_PAPER] = "LC_PAPER",
65 [LC_NAME] = "LC_NAME",
66 [LC_ADDRESS] = "LC_ADDRESS",
67 [LC_TELEPHONE] = "LC_TELEPHONE",
68 [LC_MEASUREMENT] = "LC_MEASUREMENT",
69 [LC_IDENTIFICATION] = "LC_IDENTIFICATION"
70 };
71
72
73 static void
identification_startup(struct linereader * lr,struct localedef_t * locale,int ignore_content)74 identification_startup (struct linereader *lr, struct localedef_t *locale,
75 int ignore_content)
76 {
77 if (!ignore_content)
78 {
79 locale->categories[LC_IDENTIFICATION].identification =
80 (struct locale_identification_t *)
81 xcalloc (1, sizeof (struct locale_identification_t));
82
83 locale->categories[LC_IDENTIFICATION].identification->category[LC_ALL] =
84 "";
85 }
86
87 if (lr != NULL)
88 {
89 lr->translate_strings = 1;
90 lr->return_widestr = 0;
91 }
92 }
93
94
95 void
identification_finish(struct localedef_t * locale,const struct charmap_t * charmap)96 identification_finish (struct localedef_t *locale,
97 const struct charmap_t *charmap)
98 {
99 struct locale_identification_t *identification
100 = locale->categories[LC_IDENTIFICATION].identification;
101 int nothing = 0;
102 size_t num;
103
104 /* Now resolve copying and also handle completely missing definitions. */
105 if (identification == NULL)
106 {
107 /* First see whether we were supposed to copy. If yes, find the
108 actual definition. */
109 if (locale->copy_name[LC_IDENTIFICATION] != NULL)
110 {
111 /* Find the copying locale. This has to happen transitively since
112 the locale we are copying from might also copying another one. */
113 struct localedef_t *from = locale;
114
115 do
116 from = find_locale (LC_IDENTIFICATION,
117 from->copy_name[LC_IDENTIFICATION],
118 from->repertoire_name, charmap);
119 while (from->categories[LC_IDENTIFICATION].identification == NULL
120 && from->copy_name[LC_IDENTIFICATION] != NULL);
121
122 identification = locale->categories[LC_IDENTIFICATION].identification
123 = from->categories[LC_IDENTIFICATION].identification;
124 }
125
126 /* If there is still no definition issue an warning and create an
127 empty one. */
128 if (identification == NULL)
129 {
130 record_warning (_("\
131 No definition for %s category found"), "LC_IDENTIFICATION");
132 identification_startup (NULL, locale, 0);
133 identification
134 = locale->categories[LC_IDENTIFICATION].identification;
135 nothing = 1;
136 }
137 }
138
139 #define TEST_ELEM(cat) \
140 if (identification->cat == NULL) \
141 { \
142 if (verbose && ! nothing) \
143 record_warning (_("%s: field `%s' not defined"), "LC_IDENTIFICATION", \
144 #cat); \
145 identification->cat = ""; \
146 }
147
148 TEST_ELEM (title);
149 TEST_ELEM (source);
150 TEST_ELEM (address);
151 TEST_ELEM (contact);
152 TEST_ELEM (email);
153 TEST_ELEM (tel);
154 TEST_ELEM (fax);
155 TEST_ELEM (language);
156 TEST_ELEM (territory);
157 TEST_ELEM (audience);
158 TEST_ELEM (application);
159 TEST_ELEM (abbreviation);
160 TEST_ELEM (revision);
161 TEST_ELEM (date);
162
163 for (num = 0; num < __LC_LAST; ++num)
164 {
165 /* We don't accept/parse this category, so skip it early. */
166 if (num == LC_ALL)
167 continue;
168
169 if (identification->category[num] == NULL)
170 {
171 if (verbose && ! nothing)
172 record_warning (_("\
173 %s: no identification for category `%s'"), "LC_IDENTIFICATION",
174 category_name[num]);
175 identification->category[num] = "";
176 }
177 else
178 {
179 /* Only list the standards we care about. This is based on the
180 ISO 30112 WD10 [2014] standard which supersedes all previous
181 revisions of the ISO 14652 standard. */
182 static const char * const standards[] =
183 {
184 "posix:1993",
185 "i18n:2004",
186 "i18n:2012",
187 };
188 size_t i;
189 bool matched = false;
190
191 for (i = 0; i < sizeof (standards) / sizeof (standards[0]); ++i)
192 if (strcmp (identification->category[num], standards[i]) == 0)
193 matched = true;
194
195 if (matched != true)
196 record_error (0, 0, _("\
197 %s: unknown standard `%s' for category `%s'"),
198 "LC_IDENTIFICATION",
199 identification->category[num],
200 category_name[num]);
201 }
202 }
203 }
204
205
206 void
identification_output(struct localedef_t * locale,const struct charmap_t * charmap,const char * output_path)207 identification_output (struct localedef_t *locale,
208 const struct charmap_t *charmap,
209 const char *output_path)
210 {
211 struct locale_identification_t *identification
212 = locale->categories[LC_IDENTIFICATION].identification;
213 struct locale_file file;
214 size_t num;
215
216 init_locale_data (&file, _NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION));
217 add_locale_string (&file, identification->title);
218 add_locale_string (&file, identification->source);
219 add_locale_string (&file, identification->address);
220 add_locale_string (&file, identification->contact);
221 add_locale_string (&file, identification->email);
222 add_locale_string (&file, identification->tel);
223 add_locale_string (&file, identification->fax);
224 add_locale_string (&file, identification->language);
225 add_locale_string (&file, identification->territory);
226 add_locale_string (&file, identification->audience);
227 add_locale_string (&file, identification->application);
228 add_locale_string (&file, identification->abbreviation);
229 add_locale_string (&file, identification->revision);
230 add_locale_string (&file, identification->date);
231 start_locale_structure (&file);
232 for (num = 0; num < __LC_LAST; ++num)
233 if (num != LC_ALL)
234 add_locale_string (&file, identification->category[num]);
235 end_locale_structure (&file);
236 add_locale_string (&file, charmap->code_set_name);
237 write_locale_data (output_path, LC_IDENTIFICATION, "LC_IDENTIFICATION",
238 &file);
239 }
240
241
242 /* The parser for the LC_IDENTIFICATION section of the locale definition. */
243 void
identification_read(struct linereader * ldfile,struct localedef_t * result,const struct charmap_t * charmap,const char * repertoire_name,int ignore_content)244 identification_read (struct linereader *ldfile, struct localedef_t *result,
245 const struct charmap_t *charmap, const char *repertoire_name,
246 int ignore_content)
247 {
248 struct locale_identification_t *identification;
249 struct token *now;
250 struct token *arg;
251 struct token *cattok;
252 int category;
253 enum token_t nowtok;
254
255 /* The rest of the line containing `LC_IDENTIFICATION' must be free. */
256 lr_ignore_rest (ldfile, 1);
257
258 do
259 {
260 now = lr_token (ldfile, charmap, result, NULL, verbose);
261 nowtok = now->tok;
262 }
263 while (nowtok == tok_eol);
264
265 /* If we see `copy' now we are almost done. */
266 if (nowtok == tok_copy)
267 {
268 handle_copy (ldfile, charmap, repertoire_name, result,
269 tok_lc_identification, LC_IDENTIFICATION,
270 "LC_IDENTIFICATION", ignore_content);
271 return;
272 }
273
274 /* Prepare the data structures. */
275 identification_startup (ldfile, result, ignore_content);
276 identification = result->categories[LC_IDENTIFICATION].identification;
277
278 while (1)
279 {
280 /* Of course we don't proceed beyond the end of file. */
281 if (nowtok == tok_eof)
282 break;
283
284 /* Ignore empty lines. */
285 if (nowtok == tok_eol)
286 {
287 now = lr_token (ldfile, charmap, result, NULL, verbose);
288 nowtok = now->tok;
289 continue;
290 }
291
292 switch (nowtok)
293 {
294 #define STR_ELEM(cat) \
295 case tok_##cat: \
296 /* Ignore the rest of the line if we don't need the input of \
297 this line. */ \
298 if (ignore_content) \
299 { \
300 lr_ignore_rest (ldfile, 0); \
301 break; \
302 } \
303 \
304 arg = lr_token (ldfile, charmap, result, NULL, verbose); \
305 if (arg->tok != tok_string) \
306 goto err_label; \
307 if (identification->cat != NULL) \
308 lr_error (ldfile, _("\
309 %s: field `%s' declared more than once"), "LC_IDENTIFICATION", #cat); \
310 else if (!ignore_content && arg->val.str.startmb == NULL) \
311 { \
312 lr_error (ldfile, _("\
313 %s: unknown character in field `%s'"), "LC_IDENTIFICATION", #cat); \
314 identification->cat = ""; \
315 } \
316 else if (!ignore_content) \
317 identification->cat = arg->val.str.startmb; \
318 break
319
320 STR_ELEM (title);
321 STR_ELEM (source);
322 STR_ELEM (address);
323 STR_ELEM (contact);
324 STR_ELEM (email);
325 STR_ELEM (tel);
326 STR_ELEM (fax);
327 STR_ELEM (language);
328 STR_ELEM (territory);
329 STR_ELEM (audience);
330 STR_ELEM (application);
331 STR_ELEM (abbreviation);
332 STR_ELEM (revision);
333 STR_ELEM (date);
334
335 case tok_category:
336 /* Ignore the rest of the line if we don't need the input of
337 this line. */
338 if (ignore_content)
339 {
340 lr_ignore_rest (ldfile, 0);
341 break;
342 }
343
344 /* We expect two operands. */
345 arg = lr_token (ldfile, charmap, result, NULL, verbose);
346 if (arg->tok != tok_string && arg->tok != tok_ident)
347 goto err_label;
348 /* Next is a semicolon. */
349 cattok = lr_token (ldfile, charmap, result, NULL, verbose);
350 if (cattok->tok != tok_semicolon)
351 goto err_label;
352 /* Now a LC_xxx identifier. */
353 cattok = lr_token (ldfile, charmap, result, NULL, verbose);
354 switch (cattok->tok)
355 {
356 #define CATEGORY(lname, uname) \
357 case tok_lc_##lname: \
358 category = LC_##uname; \
359 break
360
361 CATEGORY (identification, IDENTIFICATION);
362 CATEGORY (ctype, CTYPE);
363 CATEGORY (collate, COLLATE);
364 CATEGORY (time, TIME);
365 CATEGORY (numeric, NUMERIC);
366 CATEGORY (monetary, MONETARY);
367 CATEGORY (messages, MESSAGES);
368 CATEGORY (paper, PAPER);
369 CATEGORY (name, NAME);
370 CATEGORY (address, ADDRESS);
371 CATEGORY (telephone, TELEPHONE);
372 CATEGORY (measurement, MEASUREMENT);
373
374 default:
375 goto err_label;
376 }
377 if (identification->category[category] != NULL)
378 {
379 lr_error (ldfile, _("\
380 %s: duplicate category version definition"), "LC_IDENTIFICATION");
381 free (arg->val.str.startmb);
382 }
383 else
384 identification->category[category] = arg->val.str.startmb;
385 break;
386
387 case tok_end:
388 /* Next we assume `LC_IDENTIFICATION'. */
389 arg = lr_token (ldfile, charmap, result, NULL, verbose);
390 if (arg->tok == tok_eof)
391 break;
392 if (arg->tok == tok_eol)
393 lr_error (ldfile, _("%s: incomplete `END' line"),
394 "LC_IDENTIFICATION");
395 else if (arg->tok != tok_lc_identification)
396 lr_error (ldfile, _("\
397 %1$s: definition does not end with `END %1$s'"), "LC_IDENTIFICATION");
398 lr_ignore_rest (ldfile, arg->tok == tok_lc_identification);
399 return;
400
401 default:
402 err_label:
403 SYNTAX_ERROR (_("%s: syntax error"), "LC_IDENTIFICATION");
404 }
405
406 /* Prepare for the next round. */
407 now = lr_token (ldfile, charmap, result, NULL, verbose);
408 nowtok = now->tok;
409 }
410
411 /* When we come here we reached the end of the file. */
412 lr_error (ldfile, _("%s: premature end of file"), "LC_IDENTIFICATION");
413 }
414