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 #include <sys/endian.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include <string.h>
43 #include <libgen.h>
44 #include <stddef.h>
45 #include <unistd.h>
46 #include <limits.h>
47 #include <locale.h>
48 #include <dirent.h>
49 #include "collate.h"
50 #include "localedef.h"
51 #include "parser.h"
52
53 #ifndef TEXT_DOMAIN
54 #define TEXT_DOMAIN "SYS_TEST"
55 #endif
56
57 static int bsd = 0;
58 static int byteorder = 0;
59 int verbose = 0;
60 int undefok = 0;
61 int warnok = 0;
62 static char *locname = NULL;
63 static char locpath[PATH_MAX];
64 char *version = NULL;
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\n", 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\n",
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 (void) fprintf(stderr, " -V version : version string for locale\n");
257 exit(4);
258 }
259
260 int
main(int argc,char ** argv)261 main(int argc, char **argv)
262 {
263 int c;
264 char *lfname = NULL;
265 char *cfname = NULL;
266 char *wfname = NULL;
267 DIR *dir;
268
269 init_charmap();
270 init_collate();
271 init_ctype();
272 init_messages();
273 init_monetary();
274 init_numeric();
275 init_time();
276
277 #if YYDEBUG
278 yydebug = 0;
279 #endif
280
281 (void) setlocale(LC_ALL, "");
282
283 while ((c = getopt(argc, argv, "blw:i:cf:u:vUDV:")) != -1) {
284 switch (c) {
285 case 'D':
286 bsd = 1;
287 break;
288 case 'b':
289 case 'l':
290 if (byteorder != 0)
291 usage();
292 byteorder = c == 'b' ? 4321 : 1234;
293 break;
294 case 'v':
295 verbose++;
296 break;
297 case 'i':
298 lfname = optarg;
299 break;
300 case 'u':
301 set_wide_encoding(optarg);
302 break;
303 case 'f':
304 cfname = optarg;
305 break;
306 case 'U':
307 undefok++;
308 break;
309 case 'c':
310 warnok++;
311 break;
312 case 'w':
313 wfname = optarg;
314 break;
315 case '?':
316 usage();
317 break;
318 case 'V':
319 version = optarg;
320 break;
321 }
322 }
323
324 if ((argc - 1) != (optind)) {
325 usage();
326 }
327 locname = argv[argc - 1];
328 if (verbose) {
329 (void) printf("Processing locale %s.\n", locname);
330 }
331
332 if (version && strlen(version) >= XLOCALE_DEF_VERSION_LEN) {
333 (void) fprintf(stderr, "Version string too long.\n");
334 exit(1);
335 }
336
337 if (cfname) {
338 if (verbose)
339 (void) printf("Loading charmap %s.\n", cfname);
340 reset_scanner(cfname);
341 (void) yyparse();
342 }
343
344 if (wfname) {
345 if (verbose)
346 (void) printf("Loading widths %s.\n", wfname);
347 reset_scanner(wfname);
348 (void) yyparse();
349 }
350
351 if (verbose) {
352 (void) printf("Loading POSIX portable characters.\n");
353 }
354 add_charmap_posix();
355
356 if (lfname) {
357 reset_scanner(lfname);
358 } else {
359 reset_scanner(NULL);
360 }
361
362 /* make the directory for the locale if not already present */
363 if (!bsd) {
364 while ((dir = opendir(locname)) == NULL) {
365 if ((errno != ENOENT) ||
366 (mkdir(locname, 0755) < 0)) {
367 errf("%s", strerror(errno));
368 }
369 }
370 (void) closedir(dir);
371 (void) mkdir(dirname(category_file()), 0755);
372 }
373
374 (void) yyparse();
375 if (verbose) {
376 (void) printf("All done.\n");
377 }
378 return (warnings ? 1 : 0);
379 }
380