1 /*-
2 * Copyright 2018 Nexenta Systems, Inc.
3 * Copyright 2015 John Marino <draco@marino.st>
4 *
5 * This source code is derived from the illumos localedef command, and
6 * provided under BSD-style license terms by Nexenta Systems, Inc.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 /*
32 * POSIX localedef.
33 */
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD: stable/12/usr.bin/localedef/localedef.c 363535 2020-07-25 23:06:47Z truckman $");
36
37 #include <sys/endian.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <errno.h>
44 #include <string.h>
45 #include <libgen.h>
46 #include <stddef.h>
47 #include <unistd.h>
48 #include <limits.h>
49 #include <locale.h>
50 #include <dirent.h>
51 #include "localedef.h"
52 #include "parser.h"
53
54 #ifndef TEXT_DOMAIN
55 #define TEXT_DOMAIN "SYS_TEST"
56 #endif
57
58 static int bsd = 0;
59 static int byteorder = 0;
60 int verbose = 0;
61 int undefok = 0;
62 int warnok = 0;
63 static char *locname = NULL;
64 static char locpath[PATH_MAX];
65
66 const char *
category_name(void)67 category_name(void)
68 {
69 switch (get_category()) {
70 case T_CHARMAP:
71 return ("CHARMAP");
72 case T_WIDTH:
73 return ("WIDTH");
74 case T_COLLATE:
75 return ("LC_COLLATE");
76 case T_CTYPE:
77 return ("LC_CTYPE");
78 case T_MESSAGES:
79 return ("LC_MESSAGES");
80 case T_MONETARY:
81 return ("LC_MONETARY");
82 case T_NUMERIC:
83 return ("LC_NUMERIC");
84 case T_TIME:
85 return ("LC_TIME");
86 default:
87 INTERR;
88 return (NULL);
89 }
90 }
91
92 static char *
category_file(void)93 category_file(void)
94 {
95 if (bsd)
96 (void) snprintf(locpath, sizeof (locpath), "%s.%s",
97 locname, category_name());
98 else
99 (void) snprintf(locpath, sizeof (locpath), "%s/%s",
100 locname, category_name());
101 return (locpath);
102 }
103
104 FILE *
open_category(void)105 open_category(void)
106 {
107 FILE *file;
108
109 if (verbose) {
110 (void) printf("Writing category %s: ", category_name());
111 (void) fflush(stdout);
112 }
113
114 /* make the parent directory */
115 if (!bsd)
116 (void) mkdir(dirname(category_file()), 0755);
117
118 /*
119 * note that we have to regenerate the file name, as dirname
120 * clobbered it.
121 */
122 file = fopen(category_file(), "w");
123 if (file == NULL) {
124 errf("%s", strerror(errno));
125 return (NULL);
126 }
127 return (file);
128 }
129
130 void
close_category(FILE * f)131 close_category(FILE *f)
132 {
133 if (fchmod(fileno(f), 0644) < 0) {
134 (void) fclose(f);
135 (void) unlink(category_file());
136 errf("%s", strerror(errno));
137 }
138 if (fclose(f) < 0) {
139 (void) unlink(category_file());
140 errf("%s", strerror(errno));
141 }
142 if (verbose) {
143 (void) fprintf(stdout, "done.\n");
144 (void) fflush(stdout);
145 }
146 }
147
148 /*
149 * This function is used when copying the category from another
150 * locale. Note that the copy is actually performed using a hard
151 * link for efficiency.
152 */
153 void
copy_category(char * src)154 copy_category(char *src)
155 {
156 char srcpath[PATH_MAX];
157 int rv;
158
159 (void) snprintf(srcpath, sizeof (srcpath), "%s/%s",
160 src, category_name());
161 rv = access(srcpath, R_OK);
162 if ((rv != 0) && (strchr(srcpath, '/') == NULL)) {
163 /* Maybe we should try the system locale */
164 (void) snprintf(srcpath, sizeof (srcpath),
165 "/usr/lib/locale/%s/%s", src, category_name());
166 rv = access(srcpath, R_OK);
167 }
168
169 if (rv != 0) {
170 fprintf(stderr,"source locale data unavailable: %s", src);
171 return;
172 }
173
174 if (verbose > 1) {
175 (void) printf("Copying category %s from %s: ",
176 category_name(), src);
177 (void) fflush(stdout);
178 }
179
180 /* make the parent directory */
181 if (!bsd)
182 (void) mkdir(dirname(category_file()), 0755);
183
184 if (link(srcpath, category_file()) != 0) {
185 fprintf(stderr,"unable to copy locale data: %s",
186 strerror(errno));
187 return;
188 }
189 if (verbose > 1) {
190 (void) printf("done.\n");
191 }
192 }
193
194 int
putl_category(const char * s,FILE * f)195 putl_category(const char *s, FILE *f)
196 {
197 if (s && fputs(s, f) == EOF) {
198 (void) fclose(f);
199 (void) unlink(category_file());
200 errf("%s", strerror(errno));
201 return (EOF);
202 }
203 if (fputc('\n', f) == EOF) {
204 (void) fclose(f);
205 (void) unlink(category_file());
206 errf("%s", strerror(errno));
207 return (EOF);
208 }
209 return (0);
210 }
211
212 int
wr_category(void * buf,size_t sz,FILE * f)213 wr_category(void *buf, size_t sz, FILE *f)
214 {
215 if (!sz) {
216 return (0);
217 }
218 if (fwrite(buf, sz, 1, f) < 1) {
219 (void) fclose(f);
220 (void) unlink(category_file());
221 errf("%s", strerror(errno));
222 return (EOF);
223 }
224 return (0);
225 }
226
227 uint32_t
htote(uint32_t arg)228 htote(uint32_t arg)
229 {
230
231 if (byteorder == 4321)
232 return (htobe32(arg));
233 else if (byteorder == 1234)
234 return (htole32(arg));
235 else
236 return (arg);
237 }
238
239 int yyparse(void);
240
241 static void
usage(void)242 usage(void)
243 {
244 (void) fprintf(stderr, "Usage: localedef [options] localename\n");
245 (void) fprintf(stderr, "[options] are:\n");
246 (void) fprintf(stderr, " -D : BSD-style output\n");
247 (void) fprintf(stderr, " -b : big-endian output\n");
248 (void) fprintf(stderr, " -c : ignore warnings\n");
249 (void) fprintf(stderr, " -l : little-endian output\n");
250 (void) fprintf(stderr, " -v : verbose output\n");
251 (void) fprintf(stderr, " -U : ignore undefined symbols\n");
252 (void) fprintf(stderr, " -f charmap : use given charmap file\n");
253 (void) fprintf(stderr, " -u encoding : assume encoding\n");
254 (void) fprintf(stderr, " -w widths : use screen widths file\n");
255 (void) fprintf(stderr, " -i locsrc : source file for locale\n");
256 exit(4);
257 }
258
259 int
main(int argc,char ** argv)260 main(int argc, char **argv)
261 {
262 int c;
263 char *lfname = NULL;
264 char *cfname = NULL;
265 char *wfname = NULL;
266 DIR *dir;
267
268 init_charmap();
269 init_collate();
270 init_ctype();
271 init_messages();
272 init_monetary();
273 init_numeric();
274 init_time();
275
276 #if YYDEBUG
277 yydebug = 0;
278 #endif
279
280 (void) setlocale(LC_ALL, "");
281
282 while ((c = getopt(argc, argv, "blw:i:cf:u:vUD")) != -1) {
283 switch (c) {
284 case 'D':
285 bsd = 1;
286 break;
287 case 'b':
288 case 'l':
289 if (byteorder != 0)
290 usage();
291 byteorder = c == 'b' ? 4321 : 1234;
292 break;
293 case 'v':
294 verbose++;
295 break;
296 case 'i':
297 lfname = optarg;
298 break;
299 case 'u':
300 set_wide_encoding(optarg);
301 break;
302 case 'f':
303 cfname = optarg;
304 break;
305 case 'U':
306 undefok++;
307 break;
308 case 'c':
309 warnok++;
310 break;
311 case 'w':
312 wfname = optarg;
313 break;
314 case '?':
315 usage();
316 break;
317 }
318 }
319
320 if ((argc - 1) != (optind)) {
321 usage();
322 }
323 locname = argv[argc - 1];
324 if (verbose) {
325 (void) printf("Processing locale %s.\n", locname);
326 }
327
328 if (cfname) {
329 if (verbose)
330 (void) printf("Loading charmap %s.\n", cfname);
331 reset_scanner(cfname);
332 (void) yyparse();
333 }
334
335 if (wfname) {
336 if (verbose)
337 (void) printf("Loading widths %s.\n", wfname);
338 reset_scanner(wfname);
339 (void) yyparse();
340 }
341
342 if (verbose) {
343 (void) printf("Loading POSIX portable characters.\n");
344 }
345 add_charmap_posix();
346
347 if (lfname) {
348 reset_scanner(lfname);
349 } else {
350 reset_scanner(NULL);
351 }
352
353 /* make the directory for the locale if not already present */
354 if (!bsd) {
355 while ((dir = opendir(locname)) == NULL) {
356 if ((errno != ENOENT) ||
357 (mkdir(locname, 0755) < 0)) {
358 errf("%s", strerror(errno));
359 }
360 }
361 (void) closedir(dir);
362 (void) mkdir(dirname(category_file()), 0755);
363 }
364
365 (void) yyparse();
366 if (verbose) {
367 (void) printf("All done.\n");
368 }
369 return (warnings ? 1 : 0);
370 }
371