po.c (40954B)
1 /* This program is licensed under the terms of GNU GPL v3 or (at your option) 2 * any later version. Copyright (C) 2021-2026 Страхиња Радић. 3 * See the file LICENSE for exact copyright and license details. */ 4 5 #include <assert.h> 6 #include <libgen.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <sys/stat.h> 11 #include <time.h> 12 13 #include "draw.h" 14 #include "config.h" 15 #include "limit.h" 16 #include "po.h" 17 #include "strtonum.h" 18 #include "util.h" 19 #include "version.h" 20 21 #define INC_COL() \ 22 do \ 23 { \ 24 if (!dstate->error) \ 25 (*col)++; \ 26 } while (0) 27 28 extern const char* errors[]; 29 extern const char* flag_strings[]; 30 31 extern int cp(const char* from, const char* to); 32 extern int print_error(const int code, const char* msg, ...); 33 extern int redraw_status(struct DrawState* state); 34 35 const uint32_t empty_string[] = {0}; 36 37 int 38 add_ref_comment(struct PoEntry* entry, const char* comment) 39 { 40 char** ref_comments = NULL; 41 char* new_line = NULL; 42 size_t len = 0; 43 44 assert((entry != NULL) && (comment != NULL)); 45 46 entry->ref_comment_lines++; 47 48 if (!entry->ref_comments) 49 ref_comments = calloc(entry->ref_comment_lines, sizeof(char*)); 50 else 51 ref_comments = realloc(entry->ref_comments, 52 entry->ref_comment_lines * sizeof(char*)); 53 if (!ref_comments) 54 return 1; 55 entry->ref_comments = ref_comments; 56 57 len = strlen(comment) + 1; 58 new_line = calloc(len, 1); 59 if (!new_line) 60 return 1; 61 entry->ref_comments[entry->ref_comment_lines - 1] = new_line; 62 strncpy(new_line, comment, len); 63 return 0; 64 } 65 66 char* 67 format_flags(char* buffer, const size_t max, const struct PoEntry* entry, 68 const int draw_box) 69 { 70 snprintf(buffer, max, "%s%c%c%c%c%s", draw_box ? "[ " : "", 71 entry->flags & FL_C_FORMAT ? 'C' : ' ', 72 entry->flags & FL_FUZZY ? 'F' : ' ', 73 entry->msgstr_count > 1 ? 'P' : ' ', 74 entry->flags & FL_NO_C_FORMAT ? 'N' : ' ', 75 draw_box ? " ]" : ""); 76 return buffer; 77 } 78 79 void 80 free_po_entry(struct PoEntry* entry) 81 { 82 if (!entry) 83 return; 84 85 free(entry->msgid); 86 if (entry->msgstr) 87 { 88 uint32_t** msgstr = entry->msgstr; 89 while (msgstr < entry->msgstr + entry->msgstr_count) 90 free(*msgstr++); 91 free(entry->msgstr); 92 } 93 free(entry->msgstr_size); 94 free(entry->msgstr_len); 95 free(entry->msgid_plural); 96 free(entry->plural_forms); 97 98 if (entry->comments) 99 { 100 uint32_t** comment = entry->comments; 101 while (comment < entry->comments + entry->comment_lines) 102 free(*comment++); 103 free(entry->comments); 104 } 105 106 if (entry->extr_comments) 107 { 108 uint32_t** extr_comment = entry->extr_comments; 109 while (extr_comment 110 < entry->extr_comments + entry->extr_comment_lines) 111 free(*extr_comment++); 112 free(entry->extr_comments); 113 } 114 115 if (entry->ref_comments) 116 { 117 char** comment = entry->ref_comments; 118 while (comment < entry->ref_comments + entry->ref_comment_lines) 119 free(*comment++); 120 free(entry->ref_comments); 121 } 122 } 123 124 void 125 init_po_entry(struct PoEntry* entry) 126 { 127 entry->flags = FL_NONE; 128 entry->obsolete = 0; 129 entry->warning = 0; 130 entry->msgid = NULL; 131 entry->msgid_size = 0; 132 entry->msgid_len = 0; 133 entry->msgid_plural = NULL; 134 entry->msgid_plural_size = 0; 135 entry->msgid_plural_len = 0; 136 entry->plural_forms = NULL; 137 entry->plural_forms_size = 0; 138 entry->plural_forms_len = 0; 139 entry->msgstr = NULL; 140 entry->msgstr_count = 0; 141 entry->msgstr_size = NULL; 142 entry->msgstr_len = NULL; 143 entry->comments = NULL; 144 entry->comment_lines = 0; 145 entry->extr_comments = NULL; 146 entry->extr_comment_lines = 0; 147 entry->ref_comments = NULL; 148 entry->ref_comment_lines = 0; 149 } 150 151 int 152 load_file(struct DrawState* dstate, long* lineno, long* col) 153 { 154 struct PoEntry* newchunk = NULL; 155 struct PoEntry* entry = NULL; 156 struct PoEntry* current = NULL; 157 FILE* input = NULL; 158 char* input_line = NULL; 159 ParseState pstate = PS_NONE; 160 int has_plural_msgid = 0; 161 ssize_t input_size; 162 ssize_t non_obsolete_sofar = 0; 163 /* lines to tolerate the lack of msgid */ 164 int msgid_counter = PO_DETECTION_LINES; 165 int msgstr_index = -1; 166 int possible_nulbyte; 167 168 assert((dstate != NULL) && (limits != NULL)); 169 input_size = limits[L_LINE_MAX]; 170 input_line = malloc(input_size); 171 if (!input_line) 172 goto load_file_alloc_error; 173 dstate->msgid_size = 0; 174 newchunk = calloc(ALLOC_DELTA, sizeof(struct PoEntry)); 175 if (!newchunk) 176 goto load_file_alloc_error; 177 178 dstate->msgid_count = 0; 179 dstate->msgid_size = ALLOC_DELTA; 180 dstate->entries = newchunk; 181 entry = dstate->entries; 182 dstate->untranslated_count++; 183 184 if (!(input = fopen(dstate->filename, "rt"))) 185 return 1; 186 187 dstate->action = ACT_READING; 188 if (redraw_status(dstate)) 189 return 1; 190 191 possible_nulbyte = 0; 192 *lineno = 1; 193 while (!feof(input)) 194 { 195 char* eol = NULL; 196 char* nulbyte = NULL; 197 198 if (!fgets(input_line, input_size, input)) 199 { 200 if (ferror(input)) 201 { 202 fclose(input); 203 return 1; 204 } 205 else 206 continue; 207 } 208 209 if (possible_nulbyte) 210 { 211 /* Definitely not EOF, NUL byte confirmed */ 212 dstate->error = ERR_NOTPO_FILE; 213 fclose(input); 214 free(input_line); 215 return 1; 216 } 217 eol = strchr(input_line, '\n'); 218 if (eol) 219 *eol = 0; 220 nulbyte = strchr(input_line, 0); 221 222 /* We don't know if the NUL byte not being equal to 223 * (possibly NULL) eol is the result of EOF or not 224 */ 225 if (nulbyte && (nulbyte != eol)) 226 possible_nulbyte = 1; 227 228 if (parse_po_line(input_line, dstate, &entry, &pstate, col, 229 &msgstr_index)) 230 { 231 if (possible_nulbyte && dstate->error == ERR_SYNTAX) 232 dstate->error = ERR_NOTPO_FILE; 233 goto load_file_cleanup; 234 } 235 236 if (entry && entry->msgid_plural) 237 has_plural_msgid = 1; 238 239 switch (dstate->error) 240 { 241 case ERR_NONE: 242 if (pstate & PS_MSGID) 243 msgid_counter = -1; 244 else if (msgid_counter != -1) 245 msgid_counter--; 246 if (msgid_counter == 0) 247 { 248 dstate->error = ERR_NOTPO_FILE; 249 return 1; 250 } 251 break; 252 case ERR_SYNTAX: 253 /* Allow for ERR_NOTPO_FILE to override ERR_SYNTAX */ 254 break; 255 default: 256 goto load_file_alloc_error; 257 } 258 if (!dstate->error) 259 (*lineno)++; 260 } 261 262 /* when the .po file is not a standard text file (no LF at end) */ 263 if (dstate->msgid_count > 1) 264 { 265 if (entry) 266 { 267 if (entry->msgid) 268 /* regular entry */ 269 dstate->msgid_count++; 270 else if (entry->comment_lines > 0) 271 /* obsolete entry */ 272 entry->obsolete = 1; 273 } 274 if (has_plural_msgid && !dstate->entries->plural_forms) 275 { 276 dstate->error = ERR_NO_PLURAL_FORMS; 277 goto load_file_cleanup; 278 } 279 } 280 281 /* file has < PO_DETECTION_LINES */ 282 if (msgid_counter > 0) 283 { 284 dstate->error = ERR_NOTPO_FILE; 285 goto load_file_cleanup; 286 } 287 288 dstate->first_shown_msgid = dstate->msgid_count > 0 ? 1 : 0; 289 update_statistics(dstate->entries, dstate, entry); 290 dstate->real_msgid_count = dstate->msgid_count + dstate->obsolete_count; 291 292 current = dstate->entries; 293 dstate->obsolete_at_end_count = 0; 294 non_obsolete_sofar = 0; 295 while (current != dstate->entries + dstate->real_msgid_count) 296 { 297 if (!current->obsolete) 298 non_obsolete_sofar++; 299 else if (non_obsolete_sofar == dstate->msgid_count) 300 dstate->obsolete_at_end_count++; 301 if (!current->obsolete && current->msgid && current->msgstr) 302 update_warning(current->msgid, *current->msgstr, 303 ¤t->warning); 304 current++; 305 } 306 307 load_file_cleanup: 308 if (dstate->error == ERR_CANT_ALLOC) 309 goto load_file_alloc_error; 310 fclose(input); 311 dstate->action = ACT_IDLE; 312 free(input_line); 313 return dstate->error != ERR_NONE; 314 315 load_file_alloc_error: 316 return 1; 317 } 318 319 int 320 parse_po_line(const char* line, struct DrawState* dstate, 321 struct PoEntry** current_entry, ParseState* pstate, long* col, 322 int* msgstr_index) 323 { 324 char* ptoken = NULL; 325 const char* pline = line; 326 char* token = NULL; 327 size_t i = 0; 328 size_t len; 329 330 assert((line != NULL) && (limits != NULL)); 331 *col = 1; 332 token = malloc(limits[L_LINE_MAX]); 333 if (!token) 334 goto parse_po_line_alloc_error; 335 *token = 0; 336 ptoken = token; 337 338 if (starts_with(line, "msgid_plural ")) 339 { 340 len = strlen("msgid_plural "); 341 *pstate &= ~PS_MSGID; 342 *pstate |= PS_MSGID_PLURAL; 343 pline += len; 344 *col += len; 345 } 346 else if (starts_with(line, "msgid ")) 347 { 348 len = strlen("msgid "); 349 *pstate |= PS_MSGID; 350 pline += len; 351 *col += len; 352 } 353 else if (starts_with(line, "msgstr[")) 354 { 355 len = strlen("msgstr["); 356 *pstate &= ~PS_MSGID; 357 *pstate &= ~PS_MSGID_PLURAL; 358 *pstate |= PS_MSGSTR_ARRAY; 359 (*msgstr_index)++; 360 pline += len; 361 *col += len; 362 363 while (*pline >= '0' && *pline <= '9') 364 { 365 pline++; 366 (*col)++; 367 } 368 369 if (starts_with(pline, "] ")) 370 { 371 pline += 2; 372 (*col) += 2; 373 } 374 else 375 { 376 dstate->error = ERR_SYNTAX; 377 goto parse_po_cleanup; 378 } 379 } 380 else if (starts_with(line, "msgstr ")) 381 { 382 len = strlen("msgstr "); 383 *pstate &= ~PS_MSGID; 384 *pstate &= ~PS_MSGID_PLURAL; 385 *pstate |= PS_MSGSTR; 386 pline += len; 387 *col += len; 388 } 389 else if (*line == 0) 390 { 391 *pstate &= ~PS_MSGID; 392 *pstate &= ~PS_MSGID_PLURAL; 393 *pstate &= ~PS_MSGSTR; 394 *pstate &= ~PS_MSGSTR_ARRAY; 395 } 396 else if (*line != ' ' && *line != '\t' && *line != '"' && *line != '#') 397 { 398 dstate->error = ERR_SYNTAX; 399 goto parse_po_cleanup; 400 } 401 402 while (*pline && i < strlen(line)) 403 { 404 switch (*pline) 405 { 406 case ' ': 407 case '\t': 408 if (*pstate & PS_COMMENT) 409 { 410 *ptoken++ = *pline++; 411 INC_COL(); 412 } 413 else if (*pstate & PS_STRING) 414 { 415 *ptoken++ = *pline++; 416 INC_COL(); 417 } 418 else 419 { 420 pline++; 421 INC_COL(); 422 } 423 break; 424 case '#': 425 if (pline - line + 1 == 1) 426 { 427 *pstate |= PS_COMMENT_TRAN; 428 pline++; 429 INC_COL(); 430 } 431 else if (*pstate & (PS_STRING | PS_COMMENT)) 432 { 433 *ptoken++ = *pline++; 434 INC_COL(); 435 } 436 else 437 { 438 dstate->error = ERR_SYNTAX; 439 goto parse_po_cleanup; 440 } 441 break; 442 case '.': 443 if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN) 444 { 445 *pstate &= ~PS_COMMENT_TRAN; 446 *pstate |= PS_COMMENT_EXTR; 447 pline++; 448 INC_COL(); 449 while (*pline == ' ' || *pline == '\t') 450 { 451 pline++; 452 INC_COL(); 453 } 454 } 455 else 456 { 457 *ptoken++ = *pline++; 458 INC_COL(); 459 } 460 break; 461 case ',': 462 if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN) 463 { 464 *pstate &= ~PS_COMMENT_TRAN; 465 *pstate |= PS_COMMENT_FLAG; 466 pline++; 467 INC_COL(); 468 while (*pline == ' ' || *pline == '\t') 469 { 470 pline++; 471 INC_COL(); 472 } 473 } 474 else 475 { 476 *ptoken++ = *pline++; 477 INC_COL(); 478 } 479 break; 480 case ':': 481 if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN) 482 { 483 *pstate &= ~PS_COMMENT_TRAN; 484 *pstate |= PS_COMMENT_REFN; 485 pline++; 486 INC_COL(); 487 while (*pline == ' ' || *pline == '\t') 488 { 489 pline++; 490 INC_COL(); 491 } 492 } 493 else 494 { 495 *ptoken++ = *pline++; 496 INC_COL(); 497 } 498 break; 499 case '~': 500 if (pline - line + 1 == 2 && (*pstate & PS_COMMENT_TRAN)) 501 { 502 *pstate &= ~PS_COMMENT_TRAN; 503 *pstate |= PS_COMMENT_OBSO; 504 pline++; 505 INC_COL(); 506 while (*pline == ' ' || *pline == '\t') 507 { 508 pline++; 509 INC_COL(); 510 } 511 } 512 else 513 { 514 *ptoken++ = *pline++; 515 INC_COL(); 516 } 517 break; 518 case '\\': 519 if ((*pstate & PS_STRING) && *(pline + 1) == '"') 520 { 521 *ptoken++ = *pline++; 522 INC_COL(); 523 /* Skip parsing '"' */ 524 *ptoken++ = *pline++; 525 INC_COL(); 526 } 527 else 528 { 529 *ptoken++ = *pline++; 530 INC_COL(); 531 } 532 break; 533 case '"': 534 if (*pstate & PS_COMMENT) 535 { 536 *ptoken++ = *pline++; 537 INC_COL(); 538 } 539 else if (*pstate & PS_STRING) 540 { 541 *pstate &= ~PS_STRING; 542 pline++; 543 INC_COL(); 544 } 545 else 546 { 547 *pstate |= PS_STRING; 548 pline++; 549 INC_COL(); 550 } 551 break; 552 default: 553 if (*pstate & PS_COMMENT) 554 { 555 *ptoken++ = *pline++; 556 INC_COL(); 557 } 558 else if (*pstate & PS_STRING) 559 { 560 *ptoken++ = *pline++; 561 INC_COL(); 562 } 563 else 564 { 565 dstate->error = ERR_SYNTAX; 566 goto parse_po_cleanup; 567 } 568 } 569 } 570 *ptoken = 0; 571 if (*pstate == PS_NONE) 572 { 573 struct PoEntry* newchunk = NULL; 574 575 if (!(*current_entry)->obsolete) 576 dstate->msgid_count++; 577 578 (*current_entry)++; 579 if (*current_entry == dstate->entries + dstate->msgid_size) 580 { 581 dstate->msgid_size += ALLOC_DELTA; 582 newchunk = realloc(dstate->entries, 583 dstate->msgid_size * sizeof(struct PoEntry)); 584 if (!newchunk) 585 goto parse_po_line_alloc_error; 586 dstate->entries = newchunk; 587 *current_entry = dstate->entries + dstate->msgid_size 588 - ALLOC_DELTA; 589 } 590 init_po_entry(*current_entry); 591 *msgstr_index = -1; 592 } 593 594 if (!*token) 595 goto parse_po_empty_token; 596 597 if (*pstate & PS_MSGID) 598 { 599 if (set_msgid(dstate, *current_entry, token, 1)) 600 goto parse_po_line_alloc_error; 601 } 602 else if (*pstate & PS_MSGID_PLURAL) 603 { 604 if (set_msgid_plural(dstate, *current_entry, token, 1)) 605 goto parse_po_line_alloc_error; 606 if (*current_entry != dstate->entries) 607 for (int i = 0; i < dstate->nplurals; i++) 608 if (set_msgstr(dstate, *current_entry, NULL, i, 609 0)) 610 goto parse_po_line_alloc_error; 611 } 612 else if (*pstate & PS_MSGSTR) 613 { 614 /* first entry */ 615 if (*current_entry == dstate->entries) 616 { 617 if (*pstate & PS_PLURAL_FORMS) 618 { 619 if (set_plural_forms(dstate, *current_entry, 620 token, 1)) 621 goto parse_po_line_alloc_error; 622 if (strstr(token, "\\n")) 623 { 624 *pstate &= ~PS_PLURAL_FORMS; 625 if (set_nplurals(dstate, *current_entry)) 626 goto parse_po_cleanup; 627 } 628 } 629 else if (starts_with(token, "Plural-Forms: ")) 630 { 631 if (!strstr(token, "\\n")) 632 *pstate |= PS_PLURAL_FORMS; 633 if (set_plural_forms(dstate, *current_entry, 634 token + strlen("Plural-Forms: "), 0)) 635 goto parse_po_line_alloc_error; 636 } 637 } 638 639 if (set_msgstr(dstate, *current_entry, token, 0, 1)) 640 goto parse_po_line_alloc_error; 641 } 642 else if (*pstate & PS_MSGSTR_ARRAY) 643 { 644 if (set_msgstr(dstate, *current_entry, token, *msgstr_index, 1)) 645 goto parse_po_line_alloc_error; 646 } 647 else if (*pstate & PS_COMMENT_TRAN) 648 { 649 uint32_t* u32_token = NULL; 650 size_t len = strlen(token) + 1; 651 u32_token = calloc(len, sizeof(uint32_t)); 652 if (!u32_token) 653 goto parse_po_line_alloc_error; 654 u8_string_to_unicode(u32_token, token, len); 655 if (u32_add_comment(*current_entry, u32_token)) 656 goto parse_po_line_alloc_error; 657 free(u32_token); 658 } 659 else if (*pstate & PS_COMMENT_EXTR) 660 { 661 uint32_t* u32_token = NULL; 662 size_t len = strlen(token) + 1; 663 u32_token = calloc(len, sizeof(uint32_t)); 664 if (!u32_token) 665 goto parse_po_line_alloc_error; 666 u8_string_to_unicode(u32_token, token, len); 667 if (u32_add_extr_comment(*current_entry, u32_token)) 668 goto parse_po_line_alloc_error; 669 free(u32_token); 670 } 671 else if (*pstate & PS_COMMENT_FLAG) 672 { 673 ptoken = token; 674 while (*ptoken) 675 { 676 int first = *current_entry == dstate->entries; 677 if (starts_with(ptoken, flag_strings[FL_C_FORMAT])) 678 { 679 (*current_entry)->flags |= FL_C_FORMAT; 680 ptoken += strlen(flag_strings[FL_C_FORMAT]); 681 } 682 else if (starts_with(ptoken, 683 flag_strings[FL_NO_C_FORMAT])) 684 { 685 (*current_entry)->flags |= FL_NO_C_FORMAT; 686 ptoken += strlen(flag_strings[FL_NO_C_FORMAT]); 687 } 688 else if (starts_with(ptoken, flag_strings[FL_FUZZY]) 689 && !first) 690 { 691 (*current_entry)->flags |= FL_FUZZY; 692 dstate->fuzzy_count++; 693 ptoken += strlen(flag_strings[FL_FUZZY]); 694 } 695 else if (*ptoken == ',' && *(ptoken + 1) == ' ') 696 ptoken += 2; 697 else 698 { 699 /* Let this slip, ignore 700 * everything instead; see typedef Flags in po.h 701 * for rationale */ 702 while (*ptoken) 703 ptoken++; 704 } 705 } 706 } 707 else if (*pstate & PS_COMMENT_REFN) 708 { 709 if (add_ref_comment(*current_entry, token)) 710 goto parse_po_line_alloc_error; 711 } 712 else if (*pstate & PS_COMMENT_OBSO) 713 { 714 uint32_t* u32_token = NULL; 715 size_t len = strlen(token) + 1; 716 u32_token = calloc(len, sizeof(uint32_t)); 717 if (!u32_token) 718 goto parse_po_line_alloc_error; 719 u8_string_to_unicode(u32_token, token, len); 720 if (u32_add_comment(*current_entry, u32_token)) 721 goto parse_po_line_alloc_error; 722 else 723 (*current_entry)->obsolete = 1; 724 free(u32_token); 725 } 726 *token = 0; 727 goto parse_po_cleanup; 728 729 parse_po_empty_token: 730 if (*pstate & PS_MSGSTR) 731 { 732 if (u32_set_msgstr(dstate, *current_entry, empty_string, 0, 1)) 733 goto parse_po_line_alloc_error; 734 } 735 else if (*pstate & PS_MSGSTR_ARRAY) 736 { 737 if (u32_set_msgstr(dstate, *current_entry, empty_string, 738 *msgstr_index, 1)) 739 goto parse_po_line_alloc_error; 740 } 741 else if (*pstate & PS_COMMENT_TRAN) 742 { 743 if (u32_add_comment(*current_entry, empty_string)) 744 goto parse_po_line_alloc_error; 745 } 746 else if (*pstate & PS_COMMENT_EXTR) 747 { 748 if (u32_add_extr_comment(*current_entry, empty_string)) 749 goto parse_po_line_alloc_error; 750 } 751 752 parse_po_cleanup: 753 /* one-line states */ 754 *pstate &= ~PS_COMMENT; 755 return !!dstate->error; 756 757 parse_po_line_alloc_error: 758 dstate->error = ERR_CANT_ALLOC; 759 return 1; 760 } 761 762 int 763 save_comment(FILE* output, struct DrawState* state, 764 const struct PoEntry* current) 765 { 766 char* temp = NULL; 767 char* u8_comment = NULL; 768 ssize_t c, len, size; 769 770 assert((output != NULL) && (state != NULL) && (current != NULL)); 771 for (c = 0; c < current->comment_lines; c++) 772 { 773 len = u32_strlen(current->comments[c]); 774 size = len * UTF8REPMAX + 1; 775 temp = realloc(u8_comment, size); 776 if (!temp) 777 { 778 state->error = ERR_CANT_ALLOC; 779 return 1; 780 } 781 u8_comment = temp; 782 unicode_string_to_u8(u8_comment, current->comments[c], len); 783 fprintf(output, "#%s%s\n", current->obsolete ? "~ " : "", 784 u8_comment); 785 } 786 free(u8_comment); 787 return 0; 788 } 789 790 int 791 save_extr_comment(FILE* output, struct DrawState* state, 792 const struct PoEntry* current) 793 { 794 char* temp = NULL; 795 char* u8_extr_comment = NULL; 796 ssize_t tc, len, size; 797 798 assert((output != NULL) && (state != NULL) && (current != NULL)); 799 for (tc = 0; tc < current->extr_comment_lines; tc++) 800 { 801 len = u32_strlen(current->extr_comments[tc]); 802 size = len * UTF8REPMAX + 1; 803 804 temp = realloc(u8_extr_comment, size); 805 if (!temp) 806 { 807 state->error = ERR_CANT_ALLOC; 808 return 1; 809 } 810 u8_extr_comment = temp; 811 unicode_string_to_u8(u8_extr_comment, 812 current->extr_comments[tc], len); 813 fprintf(output, "#.%s%s\n", u8_extr_comment[0] == 0 ? "" : " ", 814 u8_extr_comment); 815 } 816 free(u8_extr_comment); 817 return 0; 818 } 819 820 int 821 save_file(struct DrawState* state) 822 { 823 FILE* output = NULL; 824 const struct PoEntry* current = NULL; 825 struct stat st; 826 int result = 0; 827 int saved_errno; 828 829 assert(state != NULL); 830 state->action = ACT_WRITING; 831 if (redraw_status(state)) 832 return 1; 833 errno = 0; 834 835 current = state->entries; 836 if (!create_backups) 837 goto save_skip_backup_copy; 838 if (!backup_fail && !state->backup_possible) 839 goto save_skip_backup_copy; 840 841 if ((unlink(state->backup_filename) < 0) && (errno != ENOENT)) 842 { 843 state->error = ERR_ERRNO_SET; 844 result = 1; 845 goto save_file_cleanup; 846 } 847 848 if (cp(state->rfn_len > 0 ? state->real_filename : state->filename, 849 state->backup_filename)) 850 { 851 if (backup_fail) 852 { 853 state->error = ERR_ERRNO_SET; 854 result = 1; 855 goto save_file_cleanup; 856 } 857 else 858 errno = 0; /* ignore errors */ 859 } 860 861 save_skip_backup_copy: 862 if (!(output = fopen(state->rfn_len > 0 ? state->real_filename 863 : state->filename, 864 "wt"))) 865 { 866 state->error = ERR_ERRNO_SET; 867 result = 1; 868 goto save_file_cleanup; 869 } 870 871 if (!create_backups) 872 goto save_skip_backup_symlink; 873 874 if (lstat(state->rfn_len > 0 ? state->real_filename : state->filename, 875 &st) 876 < 0) 877 { 878 state->error = ERR_ERRNO_SET; 879 result = 1; 880 goto save_file_cleanup; 881 } 882 883 if (!backup_fail && !state->backup_possible) 884 goto save_skip_backup_symlink; 885 886 if (state->rfn_len > 0) 887 { 888 if (unlink(state->filename) == -1) 889 { 890 state->error = ERR_ERRNO_SET; 891 result = 1; 892 goto save_file_cleanup; 893 } 894 if (symlink(state->orig_real_filename, state->filename) == -1) 895 { 896 state->error = ERR_ERRNO_SET; 897 result = 1; 898 goto save_file_cleanup; 899 } 900 } 901 chmod(state->rfn_len > 0 ? state->real_filename : state->filename, 902 st.st_mode); 903 904 save_skip_backup_symlink: 905 save_loop_entries_start: 906 if (current >= state->entries + state->real_msgid_count) 907 goto save_loop_entries_done; 908 909 if (!current->obsolete && current->comment_lines > 0) 910 if (save_comment(output, state, current)) 911 goto save_file_error_cleanup; 912 if (current->extr_comment_lines > 0) 913 if (save_extr_comment(output, state, current)) 914 goto save_file_error_cleanup; 915 if (current->ref_comment_lines > 0) 916 if (save_ref_comment(output, current)) 917 goto save_file_error_cleanup; 918 if (save_flags(output, current)) 919 goto save_file_error_cleanup; 920 if (current->obsolete && current->comment_lines > 0) 921 if (save_comment(output, state, current)) 922 goto save_file_error_cleanup; 923 924 if (current->obsolete) 925 { 926 if (current < state->entries + state->real_msgid_count - 1) 927 fprintf(output, "\n"); 928 current++; 929 goto save_loop_entries_start; 930 } 931 932 if (save_msgid(output, state, current)) 933 goto save_file_error_cleanup; 934 if (current->msgid_plural) 935 if (save_msgid_plural(output, state, current)) 936 goto save_file_error_cleanup; 937 938 if (save_msgstrs(output, state, current)) 939 goto save_file_error_cleanup; 940 941 if (current < state->entries + state->real_msgid_count - 1) 942 fprintf(output, "\n"); 943 current++; 944 goto save_loop_entries_start; 945 946 save_file_error_cleanup: 947 result = 1; 948 949 save_loop_entries_done: 950 save_file_cleanup: 951 saved_errno = errno; 952 if (output) 953 fclose(output); 954 state->action = ACT_IDLE; 955 errno = saved_errno; 956 return result; 957 } 958 959 int 960 save_flags(FILE* output, const struct PoEntry* current) 961 { 962 Flags flags; 963 964 assert((output != NULL) && (current != NULL)); 965 flags = current->flags; 966 if (flags != FL_NONE) 967 fprintf(output, "#, "); 968 while (flags != FL_NONE) 969 { 970 if (flags & FL_C_FORMAT) 971 { 972 flags &= ~FL_C_FORMAT; 973 fprintf(output, "%s", flag_strings[FL_C_FORMAT]); 974 } 975 else if (flags & FL_NO_C_FORMAT) 976 { 977 flags &= ~FL_NO_C_FORMAT; 978 fprintf(output, "%s", flag_strings[FL_NO_C_FORMAT]); 979 } 980 else if (flags & FL_FUZZY) 981 { 982 flags &= ~FL_FUZZY; 983 fprintf(output, "%s", flag_strings[FL_FUZZY]); 984 } 985 else 986 flags = FL_NONE; /* see po.h, typedef Flags */ 987 988 if (flags != FL_NONE) 989 fprintf(output, ", "); 990 } 991 if (current->flags != FL_NONE) 992 fprintf(output, "\n"); 993 return 0; 994 } 995 996 int 997 save_msgid(FILE* output, struct DrawState* state, const struct PoEntry* current) 998 { 999 const uint32_t* old_pumsgid = NULL; 1000 const uint32_t* pumsgid = NULL; 1001 char* u8_msgid = NULL; 1002 size_t len = 0; 1003 size_t num_lines; 1004 size_t size; 1005 1006 assert((output != NULL) && (state != NULL) && (current != NULL)); 1007 if (!current->msgid) 1008 { 1009 fprintf(output, "msgid \"\"\n"); 1010 return 0; 1011 } 1012 num_lines = u32_lines_in_string(current->msgid, 1, wrap_width); 1013 if ((num_lines > 1) 1014 || u32_strlen(current->msgid) + strlen("msgid \"") + 1 1015 > (size_t)literal_break_threshold) 1016 { 1017 pumsgid = current->msgid; 1018 fprintf(output, "msgid %s", break_literals ? "\"\"\n" : ""); 1019 while (*pumsgid) 1020 { 1021 u8_msgid = NULL; 1022 old_pumsgid = pumsgid; 1023 1024 len = u32_next_line(current->msgid, &pumsgid, 1, 1025 wrap_width); 1026 if (len == 0) 1027 continue; 1028 1029 size = len * UTF8REPMAX + 1; 1030 u8_msgid = malloc(size); 1031 if (!u8_msgid) 1032 { 1033 state->error = ERR_CANT_ALLOC; 1034 return 1; 1035 } 1036 memset(u8_msgid, 0, size); 1037 1038 unicode_string_to_u8(u8_msgid, old_pumsgid, len); 1039 fprintf(output, "\"%s\"\n", u8_msgid); 1040 free(u8_msgid); 1041 } 1042 } 1043 else 1044 { 1045 size = current->msgid_size * UTF8REPMAX + 1; 1046 u8_msgid = malloc(size); 1047 if (!u8_msgid) 1048 { 1049 state->error = ERR_CANT_ALLOC; 1050 return 1; 1051 } 1052 1053 memset(u8_msgid, 0, size); 1054 1055 unicode_string_to_u8(u8_msgid, current->msgid, 1056 current->msgid_size); 1057 fprintf(output, "msgid \"%s\"\n", u8_msgid); 1058 free(u8_msgid); 1059 } 1060 return 0; 1061 } 1062 1063 int 1064 save_msgid_plural(FILE* output, struct DrawState* state, 1065 const struct PoEntry* current) 1066 { 1067 const uint32_t* old_pumsgid = NULL; 1068 const uint32_t* pumsgid = NULL; 1069 char* u8_msgid = NULL; 1070 size_t len = 0; 1071 size_t msgid_plural_len, num_lines, size; 1072 1073 assert((output != NULL) && (state != NULL) && (current != NULL)); 1074 num_lines = u32_lines_in_string(current->msgid_plural, 1, wrap_width); 1075 if ((num_lines > 1) 1076 || u32_strlen(current->msgid_plural) + strlen("msgid_plural \"") 1077 + 1 1078 > (size_t)literal_break_threshold) 1079 { 1080 pumsgid = current->msgid_plural; 1081 fprintf(output, "msgid_plural %s", 1082 break_literals ? "\"\"\n" : ""); 1083 while (*pumsgid) 1084 { 1085 u8_msgid = NULL; 1086 old_pumsgid = pumsgid; 1087 len = u32_next_line(current->msgid_plural, &pumsgid, 1, 1088 wrap_width); 1089 if (len == 0) 1090 continue; 1091 1092 size = len * UTF8REPMAX + 1; 1093 u8_msgid = malloc(size); 1094 if (!u8_msgid) 1095 { 1096 state->error = ERR_CANT_ALLOC; 1097 return 1; 1098 } 1099 1100 memset(u8_msgid, 0, size); 1101 1102 unicode_string_to_u8(u8_msgid, old_pumsgid, len); 1103 fprintf(output, "\"%s\"\n", u8_msgid); 1104 free(u8_msgid); 1105 } 1106 } 1107 else 1108 { 1109 size = current->msgid_plural_size * UTF8REPMAX + 1; 1110 u8_msgid = malloc(size); 1111 if (!u8_msgid) 1112 { 1113 state->error = ERR_CANT_ALLOC; 1114 return 1; 1115 } 1116 1117 memset(u8_msgid, 9, size); 1118 1119 msgid_plural_len = u32_strlen(current->msgid_plural); 1120 unicode_string_to_u8(u8_msgid, current->msgid_plural, 1121 msgid_plural_len); 1122 fprintf(output, "msgid_plural \"%s\"\n", u8_msgid); 1123 free(u8_msgid); 1124 } 1125 return 0; 1126 } 1127 1128 int 1129 save_msgstrs(FILE* output, struct DrawState* state, 1130 const struct PoEntry* current) 1131 { 1132 struct tm* now_tm = NULL; 1133 const uint32_t* old_pumsgstr = NULL; 1134 const uint32_t* pumsgstr = NULL; 1135 char now[MAXDATEBUF]; 1136 char* u8_msgstr = NULL; 1137 time_t t; 1138 ssize_t m, num_lines; 1139 size_t len, size, msgstr_len, llen; 1140 int i; 1141 int seen_generator = 0; 1142 int seen_plural_forms = 0; 1143 int first_entry; 1144 1145 assert((output != NULL) && (state != NULL) && (current != NULL)); 1146 first_entry = (current == state->entries) && !current->msgid; 1147 if (!current->msgstr_count) 1148 { 1149 if (current->msgid_plural) 1150 for (i = 0; i < state->nplurals; i++) 1151 fprintf(output, "msgstr[%d] \"\"\n", i); 1152 else 1153 fprintf(output, "msgstr \"\"\n"); 1154 return 0; 1155 } 1156 if (current->msgstr_count == 1) 1157 { 1158 t = time(NULL); 1159 now_tm = localtime(&t); 1160 strftime(now, MAXDATEBUF, "%F %H:%M%z", now_tm); 1161 } 1162 for (m = 0; m < current->msgstr_count; m++) 1163 { 1164 assert(current->msgstr); 1165 num_lines = u32_lines_in_string(current->msgstr[m], 1, 1166 wrap_width); 1167 1168 llen = (current->msgstr_count == 1 ? strlen("msgstr \"") 1169 : strlen("msgstr[ ] \"")) 1170 + u32_strlen(current->msgstr[m]) + 1; 1171 if ((num_lines > 1) || llen > (size_t)literal_break_threshold) 1172 { 1173 pumsgstr = current->msgstr[m]; 1174 if (current->msgstr_count == 1) 1175 fprintf(output, "msgstr %s", 1176 break_literals ? "\"\"\n" : ""); 1177 else 1178 fprintf(output, "msgstr[%ld] %s", m, 1179 break_literals ? "\"\"\n" : ""); 1180 1181 save_msgstrs_pumsgstr_loop: 1182 old_pumsgstr = pumsgstr; 1183 len = u32_next_line(current->msgstr[m], &pumsgstr, 1, 1184 wrap_width); 1185 if (len == 0) 1186 goto save_msgstrs_pumsgstr_done; 1187 size = len * UTF8REPMAX + 1; 1188 u8_msgstr = malloc(size); 1189 if (!u8_msgstr) 1190 { 1191 state->error = ERR_CANT_ALLOC; 1192 return 1; 1193 } 1194 memset(u8_msgstr, 0, size); 1195 1196 unicode_string_to_u8(u8_msgstr, old_pumsgstr, len); 1197 if (first_entry 1198 && starts_with(u8_msgstr, "X-Generator: ")) 1199 { 1200 seen_generator = 1; 1201 fprintf(output, "\"X-Generator: poe %s\\n\"\n", 1202 VERSION); 1203 } 1204 else if (first_entry && plural_string 1205 && starts_with(u8_msgstr, "Plural-Forms: ")) 1206 { 1207 seen_plural_forms = 1; 1208 fprintf(output, "\"%s\"\n", u8_msgstr); 1209 } 1210 else 1211 fprintf(output, "\"%s\"\n", u8_msgstr); 1212 free(u8_msgstr); 1213 if (*pumsgstr) 1214 goto save_msgstrs_pumsgstr_loop; 1215 1216 save_msgstrs_pumsgstr_done: 1217 if (first_entry && !seen_generator) 1218 fprintf(output, "\"X-Generator: poe %s\\n\"\n", 1219 VERSION); 1220 if (first_entry && plural_string && !seen_plural_forms) 1221 fprintf(output, "\"Plural-Forms: %s\\n\"\n", 1222 plural_string); 1223 } 1224 else 1225 { 1226 msgstr_len = u32_strlen(current->msgstr[m]); 1227 size = (msgstr_len + 1) * UTF8REPMAX; 1228 u8_msgstr = malloc(size); 1229 if (!u8_msgstr) 1230 { 1231 state->error = ERR_CANT_ALLOC; 1232 return 1; 1233 } 1234 unicode_string_to_u8(u8_msgstr, current->msgstr[m], 1235 msgstr_len); 1236 if (current->msgstr_count == 1) 1237 fprintf(output, "msgstr \"%s\"\n", u8_msgstr); 1238 else 1239 fprintf(output, "msgstr[%ld] \"%s\"\n", m, 1240 u8_msgstr); 1241 free(u8_msgstr); 1242 } 1243 } 1244 return 0; 1245 } 1246 1247 int 1248 save_ref_comment(FILE* output, const struct PoEntry* current) 1249 { 1250 ssize_t rc; 1251 assert((output != NULL) && (current != NULL)); 1252 for (rc = 0; rc < current->ref_comment_lines; rc++) 1253 fprintf(output, "#: %s\n", current->ref_comments[rc]); 1254 return 0; 1255 } 1256 1257 int 1258 set_flags(struct PoEntry* entry, const Flags flags) 1259 { 1260 assert(entry != NULL); 1261 entry->flags = flags; 1262 return 0; 1263 } 1264 1265 int 1266 set_msgid(struct DrawState* state, struct PoEntry* entry, const char* msgid, 1267 int append) 1268 { 1269 uint32_t* umsgid = NULL; 1270 size_t msgid_len = 0; 1271 int result = 0; 1272 1273 assert(entry != NULL); 1274 if (msgid) 1275 msgid_len = strlen(msgid); 1276 umsgid = calloc(msgid_len + 1, sizeof(uint32_t)); 1277 if (!umsgid) 1278 goto set_msgid_alloc_error; 1279 if (msgid) 1280 (void)u8_string_to_unicode(umsgid, msgid, msgid_len); 1281 result = u32_set_msgid(state, entry, umsgid, msgid_len, append); 1282 free(umsgid); 1283 return result; 1284 1285 set_msgid_alloc_error: 1286 state->error = ERR_CANT_ALLOC; 1287 return 1; 1288 } 1289 1290 int 1291 set_msgid_plural(struct DrawState* state, struct PoEntry* entry, 1292 const char* msgid_plural, int append) 1293 { 1294 uint32_t* umsgid_plural = NULL; 1295 size_t msgid_plural_len = 0; 1296 int result = 0; 1297 1298 assert(entry != NULL); 1299 if (msgid_plural) 1300 msgid_plural_len = strlen(msgid_plural); 1301 umsgid_plural = calloc(msgid_plural_len + 1, sizeof(uint32_t)); 1302 if (!umsgid_plural) 1303 goto set_msgid_plural_alloc_error; 1304 if (msgid_plural) 1305 (void)u8_string_to_unicode(umsgid_plural, msgid_plural, 1306 msgid_plural_len); 1307 result = u32_set_msgid_plural(state, entry, umsgid_plural, 1308 msgid_plural_len, append); 1309 free(umsgid_plural); 1310 return result; 1311 1312 set_msgid_plural_alloc_error: 1313 state->error = ERR_CANT_ALLOC; 1314 return 1; 1315 } 1316 1317 int 1318 set_msgstr(struct DrawState* state, struct PoEntry* entry, const char* msgstr, 1319 int msgstr_index, int append) 1320 { 1321 uint32_t* umsgstr = NULL; 1322 size_t msgstr_len = 0; 1323 int result = 0; 1324 1325 assert(entry != NULL); 1326 if (msgstr) 1327 msgstr_len = strlen(msgstr); 1328 umsgstr = calloc(msgstr_len + 1, sizeof(uint32_t)); 1329 if (!umsgstr) 1330 goto set_msgstr_alloc_error; 1331 if (msgstr) 1332 (void)u8_string_to_unicode(umsgstr, msgstr, msgstr_len); 1333 result = u32_set_msgstr(state, entry, umsgstr, msgstr_index, append); 1334 free(umsgstr); 1335 return result; 1336 1337 set_msgstr_alloc_error: 1338 state->error = ERR_CANT_ALLOC; 1339 return 1; 1340 } 1341 1342 int 1343 set_nplurals(struct DrawState* state, struct PoEntry* entry) 1344 { 1345 char token[LINE_DEFAULT]; 1346 static uint32_t* unplurals = NULL; 1347 uint32_t* ppf; 1348 uint32_t* temp; 1349 char* ptoken = token; 1350 int errornum; 1351 int read_number = 0; 1352 int nplurals_len = 0; 1353 1354 assert(state != NULL); 1355 if (!entry || !entry->plural_forms) 1356 return 1; 1357 1358 nplurals_len = strlen("nplurals="); 1359 ppf = entry->plural_forms; 1360 *ptoken = 0; 1361 while (*ppf) 1362 { 1363 if (!unplurals) 1364 { 1365 unplurals = calloc(nplurals_len + 1, sizeof(uint32_t)); 1366 if (!unplurals) 1367 goto set_nplurals_alloc_error; 1368 U32_U8_MEMCCPY(unplurals, "nplurals=", nplurals_len, 1369 temp); 1370 } 1371 if (u32_starts_with(ppf, unplurals)) 1372 { 1373 ppf += nplurals_len; 1374 read_number = 1; 1375 } 1376 else if (read_number && *ppf >= ZERO && *ppf <= NINE) 1377 { 1378 *ptoken++ = *ppf++; 1379 *ptoken = 0; 1380 } 1381 else if (read_number && *ppf == SEMICOL) 1382 { 1383 *ptoken = 0; 1384 ptoken = token; 1385 break; 1386 } 1387 else 1388 ppf++; 1389 } 1390 if (*ptoken) 1391 { 1392 state->nplurals = strtonumber(ptoken, 0, INT_MAX, &errornum); 1393 if (errornum != STN_NONE) 1394 { 1395 state->error = ERR_BAD_NPLURALS; 1396 return 1; 1397 } 1398 } 1399 else 1400 { 1401 state->error = ERR_BAD_NPLURALS; 1402 return 1; 1403 } 1404 1405 return 0; 1406 1407 set_nplurals_alloc_error: 1408 state->error = ERR_CANT_ALLOC; 1409 return 1; 1410 } 1411 1412 int 1413 set_plural_forms(struct DrawState* state, struct PoEntry* entry, 1414 const char* plural_forms, int append) 1415 { 1416 uint32_t* uplural_forms = NULL; 1417 size_t plural_forms_len = 0; 1418 int result = 0; 1419 1420 assert(entry != NULL); 1421 if (plural_forms) 1422 plural_forms_len = strlen(plural_forms); 1423 uplural_forms = calloc(plural_forms_len + 1, sizeof(uint32_t)); 1424 if (!uplural_forms) 1425 goto set_plural_forms_alloc_error; 1426 if (plural_forms) 1427 (void)u8_string_to_unicode(uplural_forms, plural_forms, 1428 plural_forms_len); 1429 result = u32_set_plural_forms(state, entry, uplural_forms, 1430 plural_forms_len, append); 1431 free(uplural_forms); 1432 return result; 1433 1434 set_plural_forms_alloc_error: 1435 state->error = ERR_CANT_ALLOC; 1436 return 1; 1437 } 1438 1439 int 1440 u32_add_comment(struct PoEntry* entry, const uint32_t* comment) 1441 { 1442 uint32_t** comments = NULL; 1443 uint32_t* new_line = NULL; 1444 size_t len = 0; 1445 size_t size; 1446 1447 assert((entry != NULL) && (comment != NULL)); 1448 entry->comment_lines++; 1449 if (!entry->comments) 1450 comments = calloc(entry->comment_lines, sizeof(uint32_t*)); 1451 else 1452 comments = realloc(entry->comments, 1453 entry->comment_lines * sizeof(uint32_t*)); 1454 if (!comments) 1455 return 1; 1456 entry->comments = comments; 1457 1458 len = u32_strlen(comment); 1459 size = len + 1; 1460 new_line = calloc(size, sizeof(uint32_t)); 1461 if (!new_line) 1462 return 1; 1463 entry->comments[entry->comment_lines - 1] = new_line; 1464 u32_strncpy(new_line, comment, size); 1465 return 0; 1466 } 1467 1468 int 1469 u32_add_extr_comment(struct PoEntry* entry, const uint32_t* extr_comment) 1470 { 1471 uint32_t** extr_comments = NULL; 1472 uint32_t* new_line = NULL; 1473 size_t len = 0; 1474 size_t size; 1475 1476 assert((entry != NULL) && (extr_comment != NULL)); 1477 entry->extr_comment_lines++; 1478 if (!entry->extr_comments) 1479 extr_comments 1480 = calloc(entry->extr_comment_lines, sizeof(uint32_t*)); 1481 else 1482 extr_comments = realloc(entry->extr_comments, 1483 entry->extr_comment_lines * sizeof(uint32_t*)); 1484 if (!extr_comments) 1485 return 1; 1486 entry->extr_comments = extr_comments; 1487 1488 len = u32_strlen(extr_comment); 1489 size = len + 1; 1490 new_line = calloc(size, sizeof(uint32_t)); 1491 if (!new_line) 1492 return 1; 1493 entry->extr_comments[entry->extr_comment_lines - 1] = new_line; 1494 u32_strncpy(new_line, extr_comment, size); 1495 return 0; 1496 } 1497 1498 int 1499 u32_set_msgid(struct DrawState* state, struct PoEntry* entry, 1500 const uint32_t* msgid, ssize_t msgid_len, int append) 1501 { 1502 uint32_t* temp = NULL; 1503 1504 assert((state != NULL) && (entry != NULL)); 1505 if (!entry->msgid) 1506 { 1507 entry->msgid_size = MSGDELTA; 1508 entry->msgid_len = 0; 1509 entry->msgid = calloc(entry->msgid_size, sizeof(uint32_t)); 1510 if (!entry->msgid) 1511 goto u32_set_msgid_alloc_error; 1512 } 1513 else if (entry->msgid_len + msgid_len >= entry->msgid_size) 1514 { 1515 entry->msgid_size += MSGDELTA; 1516 temp = realloc(entry->msgid, 1517 entry->msgid_size * sizeof(uint32_t)); 1518 if (!temp) 1519 goto u32_set_msgid_alloc_error; 1520 entry->msgid = temp; 1521 } 1522 entry->msgid_len += msgid_len; 1523 if (append) 1524 U32_MEMCCPY(entry->msgid + entry->msgid_len - msgid_len, msgid, 1525 entry->msgid_size - msgid_len, temp); 1526 else 1527 U32_MEMCCPY(entry->msgid, msgid, entry->msgid_size, temp); 1528 return 0; 1529 1530 u32_set_msgid_alloc_error: 1531 state->error = ERR_CANT_ALLOC; 1532 return 1; 1533 } 1534 1535 int 1536 u32_set_msgid_plural(struct DrawState* state, struct PoEntry* entry, 1537 const uint32_t* msgid_plural, ssize_t msgid_plural_len, int append) 1538 { 1539 uint32_t* temp = NULL; 1540 1541 assert((state != NULL) && (entry != NULL)); 1542 if (msgid_plural) 1543 msgid_plural_len = u32_strlen(msgid_plural); 1544 if (!entry->msgid_plural) 1545 { 1546 entry->msgid_plural_size = MSGDELTA; 1547 entry->msgid_plural_len = 0; 1548 entry->msgid_plural 1549 = calloc(entry->msgid_plural_size, sizeof(uint32_t)); 1550 if (!entry->msgid_plural) 1551 goto u32_set_msgid_plural_alloc_error; 1552 } 1553 else if (entry->msgid_plural_size + msgid_plural_len 1554 >= entry->msgid_plural_size) 1555 { 1556 entry->msgid_plural_size += MSGDELTA; 1557 temp = realloc(entry->msgid_plural, 1558 entry->msgid_plural_size * sizeof(uint32_t)); 1559 if (!temp) 1560 goto u32_set_msgid_plural_alloc_error; 1561 entry->msgid_plural = temp; 1562 } 1563 entry->msgid_plural_len += msgid_plural_len; 1564 if (append) 1565 U32_MEMCCPY(entry->msgid_plural + entry->msgid_plural_len 1566 - msgid_plural_len, 1567 msgid_plural, 1568 entry->msgid_plural_size - msgid_plural_len, temp); 1569 else 1570 U32_MEMCCPY(entry->msgid_plural, msgid_plural, 1571 entry->msgid_plural_size, temp); 1572 return 0; 1573 1574 u32_set_msgid_plural_alloc_error: 1575 state->error = ERR_CANT_ALLOC; 1576 return 1; 1577 } 1578 1579 int 1580 u32_set_msgstr(struct DrawState* state, struct PoEntry* entry, 1581 const uint32_t* msgstr, int msgstr_index, int append) 1582 { 1583 uint32_t** newmsgstr = NULL; 1584 uint32_t* temp = NULL; 1585 ssize_t* newmsgstr_len = NULL; 1586 ssize_t* newmsgstr_size = NULL; 1587 ssize_t msgstr_len = 0; 1588 1589 assert((state != NULL) && (entry != NULL)); 1590 if (msgstr) 1591 msgstr_len = u32_strlen(msgstr); 1592 if (entry->msgstr_count == 0 || msgstr_index + 1 > entry->msgstr_count) 1593 { 1594 newmsgstr = NULL; 1595 newmsgstr_size = NULL; 1596 newmsgstr_len = NULL; 1597 entry->msgstr_count = msgstr_index + 1; 1598 if (!entry->msgstr) 1599 { 1600 newmsgstr = calloc(entry->msgstr_count, 1601 sizeof(uint32_t*)); 1602 if (!newmsgstr) 1603 goto u32_set_msgstr_alloc_error; 1604 newmsgstr_size 1605 = calloc(entry->msgstr_count, sizeof(ssize_t)); 1606 if (!newmsgstr_size) 1607 goto u32_set_msgstr_alloc_error; 1608 newmsgstr_len 1609 = calloc(entry->msgstr_count, sizeof(ssize_t)); 1610 if (!newmsgstr_len) 1611 goto u32_set_msgstr_alloc_error; 1612 /* when the .po file is not a standard text file (no LF 1613 * at end) */ 1614 if (state->untranslated_count > 0) 1615 state->untranslated_count--; 1616 } 1617 else 1618 { 1619 newmsgstr = realloc(entry->msgstr, 1620 entry->msgstr_count * sizeof(uint32_t*)); 1621 if (!newmsgstr) 1622 goto u32_set_msgstr_alloc_error; 1623 newmsgstr_size = realloc(entry->msgstr_size, 1624 entry->msgstr_count * sizeof(ssize_t)); 1625 if (!newmsgstr_size) 1626 goto u32_set_msgstr_alloc_error; 1627 newmsgstr_len = realloc(entry->msgstr_len, 1628 entry->msgstr_count * sizeof(ssize_t)); 1629 if (!newmsgstr_len) 1630 goto u32_set_msgstr_alloc_error; 1631 } 1632 newmsgstr[msgstr_index] = NULL; 1633 entry->msgstr = newmsgstr; 1634 entry->msgstr_size = newmsgstr_size; 1635 entry->msgstr_len = newmsgstr_len; 1636 } 1637 if (!entry->msgstr[msgstr_index]) 1638 { 1639 entry->msgstr_size[msgstr_index] = MSGDELTA; 1640 entry->msgstr_len[msgstr_index] = 0; 1641 entry->msgstr[msgstr_index] 1642 = calloc(entry->msgstr_size[msgstr_index], 1643 sizeof(uint32_t)); 1644 if (!entry->msgstr[msgstr_index]) 1645 goto u32_set_msgstr_alloc_error; 1646 } 1647 else if (entry->msgstr_len[msgstr_index] + msgstr_len 1648 >= entry->msgstr_size[msgstr_index]) 1649 { 1650 entry->msgstr_size[msgstr_index] += MSGDELTA; 1651 entry->msgstr[msgstr_index] 1652 = realloc(entry->msgstr[msgstr_index], 1653 entry->msgstr_size[msgstr_index] 1654 * sizeof(uint32_t)); 1655 if (!entry->msgstr[msgstr_index]) 1656 goto u32_set_msgstr_alloc_error; 1657 } 1658 entry->msgstr_len[msgstr_index] += msgstr_len; 1659 if (append) 1660 U32_MEMCCPY(entry->msgstr[msgstr_index] 1661 + entry->msgstr_len[msgstr_index] - msgstr_len, 1662 msgstr, entry->msgstr_size[msgstr_index] - msgstr_len, 1663 temp); 1664 else 1665 U32_MEMCCPY(entry->msgstr[msgstr_index], msgstr, 1666 entry->msgstr_size[msgstr_index], temp); 1667 return 0; 1668 1669 u32_set_msgstr_alloc_error: 1670 state->error = ERR_CANT_ALLOC; 1671 return 1; 1672 } 1673 1674 int 1675 u32_set_plural_forms(struct DrawState* state, struct PoEntry* entry, 1676 const uint32_t* plural_forms, const ssize_t plural_forms_len, 1677 int append) 1678 { 1679 uint32_t* temp = NULL; 1680 1681 assert((state != NULL) && (entry != NULL)); 1682 if (plural_forms_len >= MSGDELTA) 1683 return 1; 1684 if (!entry->plural_forms) 1685 { 1686 entry->plural_forms_size = MSGDELTA; 1687 entry->plural_forms_len = 0; 1688 entry->plural_forms = calloc(MAXMSGLINE, sizeof(uint32_t)); 1689 if (!entry->plural_forms) 1690 goto u32_set_plural_forms_alloc_error; 1691 } 1692 else if (entry->plural_forms_len + plural_forms_len 1693 >= entry->plural_forms_size) 1694 { 1695 entry->plural_forms_size += MSGDELTA; 1696 temp = realloc(entry->plural_forms, 1697 entry->plural_forms_size * sizeof(uint32_t)); 1698 if (!temp) 1699 goto u32_set_plural_forms_alloc_error; 1700 entry->plural_forms = temp; 1701 } 1702 entry->plural_forms_len += plural_forms_len; 1703 if (append) 1704 U32_MEMCCPY(entry->plural_forms + entry->plural_forms_size 1705 - plural_forms_len, 1706 plural_forms, 1707 entry->plural_forms_size - plural_forms_len, temp); 1708 else 1709 U32_MEMCCPY(entry->plural_forms, plural_forms, 1710 entry->plural_forms_size, temp); 1711 return 0; 1712 1713 u32_set_plural_forms_alloc_error: 1714 state->error = ERR_CANT_ALLOC; 1715 return 1; 1716 } 1717 1718 void 1719 update_statistics(const struct PoEntry* entries, struct DrawState* state, 1720 struct PoEntry* entry) 1721 { 1722 const struct PoEntry* current = NULL; 1723 int advance = 1; 1724 1725 state->fuzzy_count = 0; 1726 state->obsolete_count = 0; 1727 state->untranslated_count = 0; 1728 1729 current = entries; 1730 while (current && advance) 1731 { 1732 if (current->obsolete) 1733 state->obsolete_count++; 1734 else 1735 { 1736 if (current->msgid 1737 && (!current->msgstr || !*current->msgstr 1738 || !**current->msgstr)) 1739 state->untranslated_count++; 1740 if (current->flags & FL_FUZZY) 1741 state->fuzzy_count++; 1742 } 1743 if (current == entry) 1744 advance = 0; 1745 else 1746 current++; 1747 } 1748 } 1749 1750 void 1751 update_warning(const uint32_t* msgid, const uint32_t* msgstr, int* warning) 1752 { 1753 if (!msgid || !msgstr || !warning) 1754 return; 1755 if (u32_count_strings(msgid, bslash_en) 1756 != u32_count_strings(msgstr, bslash_en) 1757 || (warn_count_dots 1758 && u32_count_chars(msgid, DOT) 1759 != u32_count_chars(msgstr, DOT))) 1760 *warning = 1; 1761 else 1762 *warning = 0; 1763 }