1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include "gnu_msgfmt.h"
29
30 static int next_entry_is_fuzzy = 0;
31 static int next_entry_is_c_format = 0;
32 static struct catalog *cur_catalog = NULL;
33 static char *cur_mo = NULL;
34
35 FILE *fp;
36 iconv_t cd = (iconv_t)-1;
37 struct catalog *catalog_head = NULL;
38 int cur_po_index = 0;
39
40 static size_t
search_alias(char ** paddr,size_t size,const char * variant)41 search_alias(char **paddr, size_t size, const char *variant)
42 {
43 char *addr = *paddr;
44 char *p, *sp, *q;
45 size_t var_len, can_len;
46
47 var_len = strlen(variant);
48 p = addr;
49 q = addr + size;
50 while (q > p) {
51 if (*p == '#') {
52 /*
53 * Line beginning with '#' is a comment
54 */
55 p++;
56 while ((q > p) && (*p++ != '\n'))
57 ;
58 continue;
59 }
60 /* skip leading spaces */
61 while ((q > p) &&
62 ((*p == ' ') || (*p == '\t')))
63 p++;
64 if (q <= p)
65 break;
66 sp = p;
67 while ((q > p) && (*p != ' ') &&
68 (*p != '\t') && (*p != '\n'))
69 p++;
70 if (q <= p) {
71 /* invalid entry */
72 break;
73 }
74 if (*p == '\n') {
75 /* invalid entry */
76 p++;
77 continue;
78 }
79
80 if (((p - sp) != var_len) ||
81 ((strncmp(sp, variant, var_len) != 0) &&
82 (strncasecmp(sp, variant, var_len) != 0))) {
83 /*
84 * didn't match
85 */
86
87 /* skip remaining chars in this line */
88 p++;
89 while ((q > p) && (*p++ != '\n'))
90 ;
91 continue;
92 }
93
94 /* matching entry found */
95
96 /* skip spaces */
97 while ((q > p) &&
98 ((*p == ' ') || (*p == '\t')))
99 p++;
100 if (q <= p)
101 break;
102 sp = p;
103 while ((q > p) && (*p != ' ') &&
104 (*p != '\t') && (*p != '\n'))
105 p++;
106 can_len = p - sp;
107 if (can_len == 0) {
108 while ((q > p) && (*p++ != '\n'))
109 ;
110 continue;
111 }
112 *paddr = sp;
113 return (can_len);
114 }
115 return (0);
116 }
117
118 /*
119 * Checks if the specified charset is equivalent to UTF-8.
120 * If it's equivalent to UTF-8, returns 1; Otherwise, returns 0.
121 */
122 static int
check_utf8(const char * charset)123 check_utf8(const char *charset)
124 {
125 int fd;
126 struct stat64 statbuf;
127 caddr_t addr;
128 size_t buflen, charset_len, utf8_len;
129 char *c_charset, *c_utf8, *p;
130
131 if (strcmp(charset, DEST_CHARSET) == 0)
132 return (1);
133
134 fd = open(_ENCODING_ALIAS_PATH, O_RDONLY);
135 if (fd == -1) {
136 /* no alias file found */
137 return (0);
138 }
139 if (fstat64(fd, &statbuf) == -1) {
140 (void) close(fd);
141 return (0);
142 }
143 buflen = (size_t)statbuf.st_size;
144 addr = mmap(NULL, buflen, PROT_READ, MAP_SHARED, fd, 0);
145 (void) close(fd);
146 if (addr == MAP_FAILED) {
147 warning("mmap() for %s failed.", _ENCODING_ALIAS_PATH);
148 return (0);
149 }
150 p = (char *)addr;
151 charset_len = search_alias(&p, buflen, charset);
152 if (charset_len) {
153 c_charset = alloca(charset_len + 1);
154 (void) memcpy(c_charset, p, charset_len);
155 c_charset[charset_len] = '\0';
156 } else {
157 c_charset = (char *)charset;
158 }
159 p = (char *)addr;
160 utf8_len = search_alias(&p, buflen, DEST_CHARSET);
161 if (utf8_len) {
162 c_utf8 = alloca(utf8_len + 1);
163 (void) memcpy(c_utf8, p, utf8_len);
164 c_utf8[utf8_len] = '\0';
165 } else {
166 c_utf8 = DEST_CHARSET;
167 }
168 (void) munmap(addr, buflen);
169 if (charset_len == 0 && utf8_len == 0) {
170 /*
171 * Entry for neither charset nor utf8 found
172 */
173 return (0);
174 }
175
176 if (strcmp(c_charset, c_utf8) == 0)
177 return (1);
178 else
179 return (0);
180 }
181
182 static void
conv_init(const char * charset)183 conv_init(const char *charset)
184 {
185 if (charset == NULL) {
186 /*
187 * No conversion
188 */
189 cd = (iconv_t)-1;
190 return;
191 }
192 if (check_utf8(charset)) {
193 /*
194 * Charset is UTF-8.
195 * No conversion is required.
196 */
197 cd = (iconv_t)-1;
198 return;
199 }
200 cd = iconv_open(DEST_CHARSET, charset);
201 if (cd == (iconv_t)-1) {
202 /*
203 * No such a conversion
204 */
205 warning(gettext(WARN_NOCONV),
206 cur_line, cur_po, charset, DEST_CHARSET);
207 return;
208 }
209 }
210
211 void
clear_state(void)212 clear_state(void)
213 {
214 next_entry_is_fuzzy = 0;
215 next_entry_is_c_format = 0;
216 }
217
218 void
handle_domain(char * domainname)219 handle_domain(char *domainname)
220 {
221 if (outfile) {
222 /*
223 * outfile has been specified by -o option
224 * ignore all domain directives
225 */
226 if (verbose_flag) {
227 diag(gettext(DIAG_IGNORE_DOMAIN),
228 cur_line, cur_po, domainname);
229 }
230 free(domainname);
231 return;
232 }
233
234 if (strict_flag) {
235 /*
236 * add ".mo" to the domain
237 */
238 char *tmp;
239 tmp = Xrealloc(domainname, strlen(domainname) + 3 + 1);
240 (void) strcat(tmp, ".mo");
241 domainname = tmp;
242 }
243 catalog_init(domainname);
244 free(domainname);
245 }
246
247 void
catalog_init(const char * filename)248 catalog_init(const char *filename)
249 {
250 struct catalog *p;
251
252 if (!catalog_head) {
253 p = Xcalloc(1, sizeof (struct catalog));
254 p->fname = Xstrdup(filename);
255 p->msg_size = DEF_MSG_NUM;
256 p->nmsg = 0;
257 p->msg = Xcalloc(p->msg_size, sizeof (struct messages));
258 p->thash_size = find_prime(DEF_MSG_NUM);
259 p->thash = Xcalloc(p->thash_size, sizeof (unsigned int));
260 catalog_head = p;
261 } else {
262 p = catalog_head;
263 for (; ; ) {
264 struct catalog *tmp;
265 if (strcmp(p->fname, filename) == 0) {
266 /* already registered */
267 break;
268 }
269 if (p->next) {
270 p = p->next;
271 continue;
272 }
273 /*
274 * this domain hasn't been registered
275 */
276 tmp = Xcalloc(1, sizeof (struct catalog));
277 tmp->fname = Xstrdup(filename);
278 tmp->msg_size = DEF_MSG_NUM;
279 tmp->nmsg = 0;
280 tmp->msg = Xcalloc(tmp->msg_size,
281 sizeof (struct messages));
282 tmp->thash_size = find_prime(DEF_MSG_NUM);
283 tmp->thash = Xcalloc(tmp->thash_size,
284 sizeof (unsigned int));
285 p->next = tmp;
286 p = tmp;
287 break;
288 }
289 }
290 cur_catalog = p;
291 cur_mo = p->fname;
292 }
293
294
295 void
handle_comment(char * comment)296 handle_comment(char *comment)
297 {
298 char *p;
299
300 p = comment;
301
302 if (*p != ',') {
303 /*
304 * This comment is just informative only.
305 */
306 free(comment);
307 return;
308 }
309 /*
310 * Checks "fuzzy", "c-format", and "no-c-format"
311 */
312 p++;
313 if (strstr(p, "fuzzy") != NULL) {
314 next_entry_is_fuzzy = 1;
315 }
316 if (strstr(p, "no-c-format") != NULL) {
317 next_entry_is_c_format = 0;
318 } else if (strstr(p, "c-format") != NULL) {
319 next_entry_is_c_format = 1;
320 }
321
322 free(comment);
323 }
324
325 void
handle_message(struct entry * id,struct entry * str)326 handle_message(struct entry *id, struct entry *str)
327 {
328 char *charset, *nplurals, *tmp, *p;
329 struct messages *msg, *dupmsg;
330 size_t len;
331 unsigned int hash_val;
332 unsigned int nmsg, n, thash_idx;
333
334 if (cur_mo == NULL) {
335 /*
336 * output file hasn't been specified, nor
337 * no domain directive found
338 */
339 char *default_domain;
340
341 default_domain = strict_flag ? DEFAULT_DOMAIN_MO :
342 DEFAULT_DOMAIN;
343 catalog_init(default_domain);
344 }
345
346 /*
347 * cur_catalog should be valid, at this point
348 */
349
350 hash_val = hashpjw(id->str);
351 dupmsg = search_msg(cur_catalog, id->str, hash_val);
352
353 if (dupmsg) {
354 if ((dupmsg->str_len == str->len) &&
355 (memcmp(dupmsg->str, str->str, str->len) == 0)) {
356 /* totally same entry */
357 if (verbose_flag) {
358 warning(gettext(WARN_DUP_ENTRIES),
359 dupmsg->num, po_names[dupmsg->po],
360 id->num, cur_po);
361 }
362 free(id->str);
363 if (id->pos)
364 free(id->pos);
365 free(str->str);
366 if (str->pos)
367 free(str->pos);
368 return;
369 }
370 /* duplicate msgid */
371 if (verbose_flag) {
372 diag(gettext(ERR_DUP_ENTRIES),
373 dupmsg->num, po_names[dupmsg->po],
374 id->num, cur_po);
375 po_error++;
376 }
377 /* ignore this etnry */
378 free(id->str);
379 if (id->pos)
380 free(id->pos);
381 free(str->str);
382 if (str->pos)
383 free(str->pos);
384 return;
385 }
386
387 if (next_entry_is_fuzzy) {
388 /* fuzzy entry */
389 cur_catalog->fnum++;
390 if (!fuzzy_flag) {
391 /* ignore this entry */
392 free(id->str);
393 if (id->pos)
394 free(id->pos);
395 free(str->str);
396 if (str->pos)
397 free(str->pos);
398 return;
399 }
400 }
401
402 if (str->len == str->no) {
403 /* this entry is not translated */
404 cur_catalog->unum++;
405 free(id->str);
406 if (id->pos)
407 free(id->pos);
408 free(str->str);
409 if (str->pos)
410 free(str->pos);
411 return;
412 }
413
414 /* Checks if this is the header entry */
415 if ((id->no == 1) && (id->len == 1)) {
416 /*
417 * Header entry
418 */
419 cur_catalog->header++;
420
421 /*
422 * Need to extract the charset information
423 */
424 charset = strstr(str->str, CHARSET_STR);
425 if (charset == NULL) {
426 /* no charset information */
427 warning(gettext(WARN_NOCHARSET),
428 id->num, cur_po, str->num);
429 conv_init(NULL);
430 } else {
431 charset += CHARSET_LEN;
432 p = strpbrk(charset, " \t\n");
433 if (p != NULL) {
434 /* p points to a space, tab or new line char */
435 len = p - charset;
436 } else {
437 /* not found */
438 len = strlen(charset);
439 }
440 tmp = Xmalloc(len + 1);
441 (void) memcpy(tmp, charset, len);
442 *(tmp + len) = '\0';
443 charset = tmp;
444 conv_init(charset);
445 free(charset);
446 }
447 nplurals = strstr(str->str, NPLURALS_STR);
448 if (nplurals == NULL) {
449 cur_catalog->nplurals = 0;
450 } else {
451 unsigned int num;
452 nplurals += NPLURALS_LEN;
453 p = nplurals;
454 num = 0;
455 while (isdigit((unsigned char)*p)) {
456 num = num * 10 + *p++ - '0';
457 }
458 cur_catalog->nplurals = num;
459 }
460 }
461
462 if (verbose_flag)
463 check_format(id, str, next_entry_is_c_format);
464
465 if (id->pos)
466 free(id->pos);
467 if (str->pos)
468 free(str->pos);
469
470 msg = cur_catalog->msg;
471 nmsg = cur_catalog->nmsg;
472
473 msg[nmsg].po = cur_po_index;
474 msg[nmsg].num = id->num;
475 msg[nmsg].id = id->str;
476 msg[nmsg].id_len = id->len;
477 msg[nmsg].str = str->str;
478 msg[nmsg].str_len = str->len;
479 msg[nmsg].hash = hash_val;
480
481 thash_idx = get_hash_index(cur_catalog->thash,
482 hash_val, cur_catalog->thash_size);
483 cur_catalog->thash[thash_idx] = nmsg + 1;
484 cur_catalog->nmsg++;
485
486 if (cur_catalog->nmsg >= cur_catalog->msg_size) {
487 /* no vacancy in message array */
488 cur_catalog->msg_size += DEF_MSG_NUM;
489 cur_catalog->msg = Xrealloc(cur_catalog->msg,
490 cur_catalog->msg_size * sizeof (struct messages));
491
492 cur_catalog->thash_size =
493 find_prime(cur_catalog->msg_size);
494 free(cur_catalog->thash);
495 cur_catalog->thash = Xcalloc(cur_catalog->thash_size,
496 sizeof (unsigned int));
497
498 for (n = 0; n < cur_catalog->nmsg; n++) {
499 thash_idx = get_hash_index(cur_catalog->thash,
500 cur_catalog->msg[n].hash,
501 cur_catalog->thash_size);
502 cur_catalog->thash[thash_idx] = n + 1;
503 }
504 }
505 }
506
507 void
po_init(const char * file)508 po_init(const char *file)
509 {
510 char *filename;
511
512 if (!inputdir) {
513 filename = Xstrdup(file);
514 } else {
515 size_t dirlen, filelen, len;
516
517 dirlen = strlen(inputdir);
518 filelen = strlen(file);
519 len = dirlen + 1 + filelen + 1;
520 filename = Xmalloc(len);
521 (void) memcpy(filename, inputdir, dirlen);
522 *(filename + dirlen) = '/';
523 (void) memcpy(filename + dirlen + 1, file, filelen);
524 *(filename + dirlen + 1 + filelen) = '\0';
525 }
526
527 fp = fopen(filename, "r");
528 if (fp == NULL) {
529 error(gettext(ERR_OPEN_FAILED), filename);
530 /* NOTREACHED */
531 }
532
533 po_names[cur_po_index] = filename;
534 cur_line = 1;
535 cd = (iconv_t)-1;
536 if (!outfile)
537 cur_mo = NULL;
538 }
539
540 void
po_fini(void)541 po_fini(void)
542 {
543 cur_po_index++;
544 (void) fclose(fp);
545 if (cd != (iconv_t)-1)
546 (void) iconv_close(cd);
547 }
548