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