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. */
35struct 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
55static 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
73static void
74identification_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 (n: 1, s: 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
95void
96identification_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 name: from->copy_name[LC_IDENTIFICATION],
118 repertoire_name: 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 (_("\
131No definition for %s category found"), "LC_IDENTIFICATION");
132 identification_startup (NULL, locale, ignore_content: 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 (s1: identification->category[num], s2: standards[i]) == 0)
193 matched = true;
194
195 if (matched != true)
196 record_error (status: 0, errnum: 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
206void
207identification_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: &file, _NL_ITEM_INDEX (_NL_NUM_LC_IDENTIFICATION));
217 add_locale_string (file: &file, string: identification->title);
218 add_locale_string (file: &file, string: identification->source);
219 add_locale_string (file: &file, string: identification->address);
220 add_locale_string (file: &file, string: identification->contact);
221 add_locale_string (file: &file, string: identification->email);
222 add_locale_string (file: &file, string: identification->tel);
223 add_locale_string (file: &file, string: identification->fax);
224 add_locale_string (file: &file, string: identification->language);
225 add_locale_string (file: &file, string: identification->territory);
226 add_locale_string (file: &file, string: identification->audience);
227 add_locale_string (file: &file, string: identification->application);
228 add_locale_string (file: &file, string: identification->abbreviation);
229 add_locale_string (file: &file, string: identification->revision);
230 add_locale_string (file: &file, string: identification->date);
231 start_locale_structure (file: &file);
232 for (num = 0; num < __LC_LAST; ++num)
233 if (num != LC_ALL)
234 add_locale_string (file: &file, string: identification->category[num]);
235 end_locale_structure (file: &file);
236 add_locale_string (file: &file, string: charmap->code_set_name);
237 write_locale_data (output_path, LC_IDENTIFICATION, category: "LC_IDENTIFICATION",
238 file: &file);
239}
240
241
242/* The parser for the LC_IDENTIFICATION section of the locale definition. */
243void
244identification_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 (lr: ldfile, verbose: 1);
257
258 do
259 {
260 now = lr_token (lr: ldfile, charmap, locale: 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 token: tok_lc_identification, LC_IDENTIFICATION,
270 locale_name: "LC_IDENTIFICATION", ignore_content);
271 return;
272 }
273
274 /* Prepare the data structures. */
275 identification_startup (lr: ldfile, locale: 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 (lr: ldfile, charmap, locale: 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 (lr: ldfile, verbose: 0);
341 break;
342 }
343
344 /* We expect two operands. */
345 arg = lr_token (lr: ldfile, charmap, locale: 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 (lr: ldfile, charmap, locale: result, NULL, verbose);
350 if (cattok->tok != tok_semicolon)
351 goto err_label;
352 /* Now a LC_xxx identifier. */
353 cattok = lr_token (lr: ldfile, charmap, locale: 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 (lr: ldfile, _("\
380%s: duplicate category version definition"), "LC_IDENTIFICATION");
381 free (ptr: 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 (lr: ldfile, charmap, locale: result, NULL, verbose);
390 if (arg->tok == tok_eof)
391 break;
392 if (arg->tok == tok_eol)
393 lr_error (lr: ldfile, _("%s: incomplete `END' line"),
394 "LC_IDENTIFICATION");
395 else if (arg->tok != tok_lc_identification)
396 lr_error (lr: ldfile, _("\
397%1$s: definition does not end with `END %1$s'"), "LC_IDENTIFICATION");
398 lr_ignore_rest (lr: ldfile, verbose: 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 (lr: ldfile, charmap, locale: result, NULL, verbose);
408 nowtok = now->tok;
409 }
410
411 /* When we come here we reached the end of the file. */
412 lr_error (lr: ldfile, _("%s: premature end of file"), "LC_IDENTIFICATION");
413}
414

source code of glibc/locale/programs/ld-identification.c