poe.c (71633B)
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 <errno.h> 7 #include <fcntl.h> 8 #include <locale.h> 9 #include <stdarg.h> 10 #include <stdint.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/stat.h> 15 #include <unistd.h> 16 17 #define COPYRIGHT \ 18 (" This program is licensed under the terms of GNU GPL v3" \ 19 " or (at your option)\n" \ 20 " any later version. Copyright (C) 2021-2026 Strahinya Radich.\n" \ 21 " See the file LICENSE for exact copyright and license " \ 22 "details.\n" \ 23 " File termbox.h is based on termbox2 TUI library, (c) 2021" \ 24 " termbox developers,\n" \ 25 " which is released under the terms of Expat license, see the file" \ 26 " LICENSE.termbox.\n") 27 #define POE_C_GLOBALS 28 #define TB_IMPL 29 30 /* Order is important! */ 31 #include "draw.h" 32 #include "poe.h" 33 #include "config.h" 34 #include "limit.h" 35 #include "po.h" 36 #include "strtonum.h" 37 #include "util.h" 38 #include "version.h" 39 40 #define MAXCOPYBUF 8192 41 42 extern const char* errors[]; 43 extern const char* actions[]; 44 extern const char* prompt_dirty; 45 extern const char* prompt_edit_dirty; 46 extern const char* prompt_overwrite; 47 48 static int 49 cancel_callback(struct DrawState* state, struct tb_event* ev) 50 { 51 assert((state != NULL) && (ev != NULL)); 52 if (strchr("Yy", ev->ch)) 53 { 54 state->reshow_edit = 0; 55 save_msgstr(state); 56 } 57 else /* not saving */ 58 { 59 state->edit_dirty = 0; 60 cancel_msgstr(state); 61 } 62 return 0; 63 } 64 65 static int 66 cancel_close(struct DrawState* state) 67 { 68 assert(state != NULL); 69 if (!state->error && state->in_prompt) 70 { 71 state->in_prompt = 0; 72 *state->prompt = 0; 73 } 74 else if (state->show_help) 75 state->show_help = 0; 76 else if (state->show_edit) 77 cancel_msgstr(state); 78 else if (state->show_search) 79 cancel_search(state); 80 else 81 state->error = ERR_EXIT_KEY; 82 return 0; 83 } 84 85 static int 86 cancel_msgstr(struct DrawState* state) 87 { 88 char* temp = NULL; 89 ssize_t i; 90 91 assert(state != NULL); 92 if (state->edit_dirty) 93 { 94 state->in_prompt = 1; 95 MEMCCPY(state->prompt, prompt_edit_dirty, LINE_DEFAULT, temp); 96 state->prompt_callback = cancel_callback; 97 return 0; 98 } 99 100 state->show_edit = 0; 101 state->reshow_edit = 0; 102 state->edit_info_focused = 0; 103 104 for (i = 0; i < state->input_rows_count; i++) 105 free_bufferline(state->input_buffer + i); 106 free(state->input_buffer); 107 state->input_buffer = NULL; 108 state->input_rows_count = 0; 109 if (state->info_buffer) 110 for (i = 0; i < state->info_rows_count; i++) 111 free_bufferline(&state->info_buffer[i]); 112 free(state->info_buffer); 113 state->info_buffer = NULL; 114 state->info_rows_count = 0; 115 state->fuzzy_count = state->fuzzy_before_edit; 116 switch (state->input_changed_fuzzy) 117 { 118 case SET: 119 state->entries[state->msgid_number - 1].flags &= ~FL_FUZZY; 120 break; 121 case CLEARED: 122 state->entries[state->msgid_number - 1].flags |= FL_FUZZY; 123 break; 124 default:; 125 } 126 state->input_changed_fuzzy = UNCHANGED; 127 tb_hide_cursor(); 128 return 0; 129 } 130 131 static int 132 cancel_search(struct DrawState* state) 133 { 134 assert(state != NULL); 135 state->show_search = 0; 136 *state->search = 0; 137 tb_hide_cursor(); 138 return 0; 139 } 140 141 static int 142 copy_msgid_to_input(struct DrawState* state) 143 { 144 struct PoEntry* current; 145 struct BufferLine* newbuf; 146 struct BufferLine* pbuffer; 147 const uint32_t* pmsgid; 148 const uint32_t* old_pmsgid; 149 uint32_t* newtext; 150 char* temp = NULL; 151 ssize_t msgid_num_lines, lines_to_init, lines_to_free, i, len, newlen; 152 153 assert(state != NULL); 154 155 if (state->edit_info_focused) 156 { 157 state->error = ERR_DLG_FOCUS_EDIT; 158 return 0; 159 } 160 161 if (!state->in_prompt && *state->input_buffer->text) 162 { 163 state->in_prompt = 1; 164 MEMCCPY(state->prompt, prompt_overwrite, LINE_DEFAULT, temp); 165 state->prompt_callback = overwrite_copy_msgid_callback; 166 return 0; 167 } 168 169 if (state->msgid_number == 1) 170 { 171 state->error = ERR_ILLEGAL_ON_FIRST; 172 return 0; 173 } 174 175 assert(state->entries != NULL); 176 current = &state->entries[state->msgid_number - 1]; 177 msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); 178 lines_to_init = state->input_rows_count < msgid_num_lines 179 ? msgid_num_lines - state->input_rows_count 180 : 0; 181 lines_to_free = state->input_rows_count > msgid_num_lines 182 ? state->input_rows_count - msgid_num_lines 183 : 0; 184 for (i = 0; i < lines_to_free; i++) 185 free_bufferline( 186 state->input_buffer + state->input_rows_count - i - 1); 187 newbuf = realloc(state->input_buffer, 188 msgid_num_lines * sizeof(struct BufferLine)); 189 pmsgid = current->msgid; 190 191 if (!newbuf) 192 return 0; 193 194 pbuffer = NULL; 195 for (i = 0; i < lines_to_init; i++) 196 { 197 pbuffer = newbuf + msgid_num_lines - 1 - i; 198 init_bufferline(pbuffer); 199 newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); 200 if (!newtext) 201 return 0; 202 pbuffer->text = newtext; 203 } 204 205 pbuffer = newbuf; 206 while (pmsgid && *pmsgid) 207 { 208 old_pmsgid = pmsgid; 209 len = u32_next_line(current->msgid, &pmsgid, 0, 0); 210 u32_strncpy(pbuffer->text, old_pmsgid, 211 MIN(len + 1, LINE_DEFAULT)); 212 newlen = u32_decode_tabs(state, pbuffer->text, LINE_DEFAULT); 213 pbuffer->length = newlen; 214 pbuffer++; 215 } 216 217 state->input_rows_count = msgid_num_lines; 218 state->input_buffer = newbuf; 219 state->edit_dirty = 1; 220 221 return 0; 222 } 223 224 int 225 cp(const char* from, const char* to) 226 { 227 struct stat st; 228 struct timespec times[2]; 229 char buf[MAXCOPYBUF]; 230 int fromfd, tofd; 231 int nread = 0; 232 int nwritten = 0; 233 int result; 234 int saved_errno; 235 236 assert((from != NULL) && (to != NULL)); 237 238 tofd = 0; 239 fromfd = open(from, O_RDONLY); 240 if (fromfd < 0) 241 return 1; 242 if (fstat(fromfd, &st) < 0) 243 { 244 result = 1; 245 goto cp_cleanup; 246 } 247 248 tofd = open(to, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode); 249 if (tofd < 0) 250 return 1; 251 if (fchmod(tofd, st.st_mode) < 0) 252 { 253 result = 1; 254 goto cp_cleanup; 255 } 256 times[0] = st.st_atim; 257 times[1] = st.st_mtim; 258 259 result = 0; 260 while ((nread = read(fromfd, buf, MAXCOPYBUF)) > 0) 261 { 262 nwritten = write(tofd, buf, nread); 263 if (nwritten < nread) 264 { 265 result = 1; 266 goto cp_cleanup; 267 } 268 } 269 270 if (nread < 0) 271 { 272 result = 1; 273 goto cp_cleanup; 274 } 275 276 errno = 0; 277 if (futimens(tofd, times) < 0) 278 result = 1; 279 280 cp_cleanup: 281 saved_errno = errno; 282 if (tofd) 283 close(tofd); 284 close(fromfd); 285 errno = saved_errno; 286 return result; 287 } 288 289 static char* 290 dirname(char* dir, char* path, const ssize_t dir_size) 291 { 292 char* p = NULL; 293 294 assert((dir != NULL) && (path != NULL) && (dir_size > 0)); 295 if (!*path) 296 return NULL; 297 298 MEMCCPY(dir, path, dir_size, p); 299 300 if (p > dir + 2 && *(p - 2) == '/') 301 *p = 0; 302 if (p > dir + 1) 303 p--; 304 305 while (p != dir && *p != '/') 306 { 307 p--; 308 if (p != dir && *p == '/') 309 { 310 *p = 0; 311 return dir; 312 } 313 else if (p == dir && *p == '/') 314 return dir; 315 } 316 dir[0] = '.'; 317 dir[1] = 0; 318 return dir; 319 } 320 321 static int 322 edit_page_down(struct DrawState* state) 323 { 324 char* temp = NULL; 325 assert(state != NULL); 326 if (state->edit_info_focused) 327 { 328 if (state->info_first_shown.row + (EDIT_HEIGHT - 3) / 2 329 < state->info_rows_count - 1) 330 state->info_first_shown.row += (EDIT_HEIGHT - 3) / 2; 331 return 0; 332 } 333 if (state->edit_dirty) 334 { 335 state->in_prompt = 1; 336 MEMCCPY(state->prompt, prompt_edit_dirty, LINE_DEFAULT, temp); 337 state->prompt_callback = edit_page_down_callback; 338 return 0; 339 } 340 move_list_down(state); 341 state->msgstr_index = 0; 342 return show_edit(state); 343 } 344 345 static int 346 edit_page_down_callback(struct DrawState* state, struct tb_event* ev) 347 { 348 assert((state != NULL) && (ev != NULL)); 349 if (strchr("Yy", ev->ch)) 350 { 351 state->reshow_edit = 1; 352 save_msgstr(state); 353 } 354 else 355 state->edit_dirty = 0; 356 return edit_page_down(state); 357 } 358 359 static int 360 edit_page_up(struct DrawState* state) 361 { 362 char* temp = NULL; 363 assert(state != NULL); 364 if (state->edit_info_focused) 365 { 366 if (state->info_first_shown.row > (EDIT_HEIGHT - 3) / 2) 367 state->info_first_shown.row -= (EDIT_HEIGHT - 3) / 2; 368 else 369 move_start(state); 370 return 0; 371 } 372 if (state->edit_dirty) 373 { 374 state->in_prompt = 1; 375 MEMCCPY(state->prompt, prompt_edit_dirty, LINE_DEFAULT, temp); 376 state->prompt_callback = edit_page_up_callback; 377 return 0; 378 } 379 move_list_up(state); 380 state->msgstr_index = 0; 381 return show_edit(state); 382 } 383 384 static int 385 edit_page_up_callback(struct DrawState* state, struct tb_event* ev) 386 { 387 assert((state != NULL) && (ev != NULL)); 388 if (strchr("Yy", ev->ch)) 389 { 390 state->reshow_edit = 1; 391 save_msgstr(state); 392 } 393 else 394 state->edit_dirty = 0; 395 return edit_page_up(state); 396 } 397 398 static int 399 erase_backwards(struct DrawState* state) 400 { 401 struct BufferLine* buffer_line; 402 403 assert((state != NULL) && (state->input_buffer != NULL)); 404 buffer_line = &state->input_buffer[state->coord.row]; 405 if (state->edit_info_focused) 406 { 407 state->error = ERR_DLG_FOCUS_EDIT; 408 return 1; 409 } 410 411 if (state->coord.col > 0) 412 { 413 ssize_t i = state->coord.col; 414 assert(buffer_line->text != NULL); 415 while (i <= buffer_line->length) 416 { 417 buffer_line->text[i - 1] = buffer_line->text[i]; 418 i++; 419 } 420 buffer_line->text[i - 1] = 0; 421 buffer_line->length--; 422 position_to(state, state->coord.row, state->coord.col - 1); 423 state->edit_dirty = 1; 424 } 425 else if (state->coord.row > 0) 426 { 427 move_up(state); 428 move_end(state); 429 erase_forward(state); 430 } 431 return 0; 432 } 433 434 static int 435 erase_forward(struct DrawState* state) 436 { 437 struct BufferLine* buffer_line; 438 size_t i; 439 440 assert((state != NULL) && (state->input_buffer != NULL)); 441 buffer_line = &state->input_buffer[state->coord.row]; 442 if (state->edit_info_focused) 443 { 444 state->error = ERR_DLG_FOCUS_EDIT; 445 return 1; 446 } 447 448 assert(buffer_line->text != NULL); 449 if (buffer_line->text[state->coord.col]) 450 { 451 i = state->coord.col; 452 while (i < LINE_DEFAULT - 2 && buffer_line->text[i + 1]) 453 { 454 buffer_line->text[i] = buffer_line->text[i + 1]; 455 i++; 456 } 457 buffer_line->text[i] = 0; 458 buffer_line->length--; 459 state->edit_dirty = 1; 460 } 461 else if (state->coord.row < state->input_rows_count - 1) 462 { 463 if (join_lines(state)) 464 { 465 state->error = ERR_CANT_ALLOC; 466 state->running = 0; 467 return 1; 468 } 469 } 470 return 0; 471 } 472 473 static int 474 erase_prev_word(struct DrawState* state) 475 { 476 uint32_t* pline; 477 ssize_t len; 478 int i, j, d; 479 480 assert(state != NULL); 481 if (state->edit_info_focused) 482 { 483 state->error = ERR_DLG_FOCUS_EDIT; 484 return 1; 485 } 486 i = state->coord.col; 487 len = state->input_buffer[state->coord.row].length; 488 pline = state->input_buffer[state->coord.row].text; 489 490 if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) 491 i--; 492 while (i > 0 && u32_is_word_boundary(pline[i], 0)) 493 i--; 494 while (i > 0 && !u32_is_word_boundary(pline[i], 0) 495 && !u32_is_word_boundary(pline[i - 1], 0)) 496 i--; 497 498 d = state->coord.col - i; 499 500 for (j = i; j < len - d; j++) 501 pline[j] = pline[j + d]; 502 503 len -= d; 504 state->input_buffer[state->coord.row].length = len; 505 pline[len] = 0; 506 state->coord.col = i; 507 state->input_display_column = display_length(pline, state->coord.col); 508 if (state->input_display_column < state->input_first_shown.col) 509 state->input_first_shown.col = state->input_display_column; 510 state->edit_dirty = 1; 511 return 0; 512 } 513 514 static int 515 erase_search_backwards(struct DrawState* state) 516 { 517 size_t i; 518 assert(state != NULL); 519 if (state->search_column > 0) 520 { 521 i = state->search_column; 522 do 523 { 524 state->search[i - 1] = state->search[i]; 525 i++; 526 } while (state->search[i]); 527 state->search[i - 1] = 0; 528 search_position_to(state, state->search_column - 1); 529 return next_match(state); 530 } 531 return 0; 532 } 533 534 static int 535 erase_search_forward(struct DrawState* state) 536 { 537 size_t i; 538 assert(state != NULL); 539 if (state->search[state->search_column]) 540 { 541 i = state->search_column; 542 while (i < MAXSEARCH - 2 && state->search[i + 1]) 543 { 544 state->search[i] = state->search[i + 1]; 545 i++; 546 } 547 state->search[i] = 0; 548 549 return next_match(state); 550 } 551 return 0; 552 } 553 554 static int 555 erase_search_prev_word(struct DrawState* state) 556 { 557 uint32_t* pline; 558 ssize_t len; 559 int i, j, d; 560 561 assert(state != NULL); 562 pline = state->search; 563 len = u32_strlen(state->search); 564 i = state->search_column; 565 if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) 566 i--; 567 while (i > 0 && u32_is_word_boundary(pline[i], 0)) 568 i--; 569 while (i > 0 && !u32_is_word_boundary(pline[i], 0) 570 && !u32_is_word_boundary(pline[i - 1], 0)) 571 i--; 572 573 d = state->search_column - i; 574 575 for (j = i; j < len - d; j++) 576 pline[j] = pline[j + d]; 577 578 len -= d; 579 pline[len] = 0; 580 search_position_to(state, i); 581 return next_match(state); 582 } 583 584 static int 585 erase_search_to_end(struct DrawState* state) 586 { 587 assert(state != NULL); 588 state->search[state->search_column] = 0; 589 move_search_end(state); 590 return next_match(state); 591 } 592 593 static int 594 erase_search_to_start(struct DrawState* state) 595 { 596 size_t i, len; 597 598 assert((state != NULL) && (state->search != NULL)); 599 i = state->search_column; 600 len = u32_strlen(state->search); 601 do 602 { 603 assert(i < MAXSEARCH); 604 state->search[i - state->search_column] = state->search[i]; 605 i++; 606 } while (state->search[i]); 607 state->search[len - state->search_column] = 0; 608 move_search_start(state); 609 return next_match(state); 610 } 611 612 static int 613 erase_to_end(struct DrawState* state) 614 { 615 struct BufferLine* buffer_line = NULL; 616 617 assert((state != NULL) && (state->input_buffer != NULL)); 618 if (state->edit_info_focused) 619 { 620 state->error = ERR_DLG_FOCUS_EDIT; 621 return 1; 622 } 623 624 buffer_line = &state->input_buffer[state->coord.row]; 625 assert(buffer_line->text != NULL); 626 buffer_line->text[state->coord.col] = 0; 627 buffer_line->length = state->coord.col; 628 move_end(state); 629 state->edit_dirty = 1; 630 return 0; 631 } 632 633 static int 634 erase_to_start(struct DrawState* state) 635 { 636 struct BufferLine* buffer_line; 637 ssize_t i; 638 ssize_t len; 639 640 assert((state != NULL) && (state->input_buffer != NULL)); 641 i = state->coord.col; 642 buffer_line = &state->input_buffer[state->coord.row]; 643 len = buffer_line->length; 644 645 if (state->edit_info_focused) 646 { 647 state->error = ERR_DLG_FOCUS_EDIT; 648 return 1; 649 } 650 651 do 652 { 653 assert(i <= len); 654 buffer_line->text[i - state->coord.col] = buffer_line->text[i]; 655 i++; 656 } while (buffer_line->text[i]); 657 buffer_line->text[len - state->coord.col] = 0; 658 buffer_line->length = len - state->coord.col; 659 move_start(state); 660 state->edit_dirty = 1; 661 return 0; 662 } 663 664 static int 665 exit_program(struct DrawState* state) 666 { 667 char* temp = NULL; 668 669 assert(state != NULL); 670 if (state->edit_dirty) 671 { 672 state->in_prompt = 1; 673 MEMCCPY(state->prompt, prompt_edit_dirty, LINE_DEFAULT, temp); 674 state->prompt_callback = quit_callback; 675 } 676 else if (state->dirty) 677 { 678 state->in_prompt = 1; 679 MEMCCPY(state->prompt, prompt_dirty, LINE_DEFAULT, temp); 680 state->prompt_callback = quit_callback; 681 } 682 else 683 state->running = 0; 684 state->error = ERR_NONE; 685 return 0; 686 } 687 688 static void 689 goto_msgid(struct DrawState* state, const ssize_t msgid_number) 690 { 691 ssize_t last_non_obsolete; 692 693 assert(state != NULL); 694 last_non_obsolete 695 = state->real_msgid_count - state->obsolete_at_end_count; 696 if (msgid_number > state->real_msgid_count) 697 return; 698 699 state->msgid_number = msgid_number; 700 701 if (state->msgid_number < state->first_shown_msgid) 702 state->first_shown_msgid = state->msgid_number; 703 else if (state->msgid_number 704 > state->first_shown_msgid + state->maxy - 2) 705 state->first_shown_msgid 706 = state->msgid_number - (state->maxy - 2); 707 else if (last_non_obsolete < state->msgid_number + state->maxy - 2) 708 state->first_shown_msgid = (last_non_obsolete > state->maxy - 2 709 ? last_non_obsolete - (state->maxy - 2) 710 : 1); 711 } 712 713 static void 714 handle_key_event(struct tb_event* ev, struct DrawState* state) 715 { 716 const struct Key* current = NULL; 717 size_t key; 718 719 assert((state != NULL) && (ev != NULL)); 720 state->error = ERR_NONE; 721 errno = 0; 722 723 if (state->in_prompt) 724 { 725 if (ev->ch && strchr("YyNn", ev->ch)) 726 { 727 if (state->prompt_callback) 728 { 729 state->prompt_callback(state, ev); 730 state->prompt_callback = NULL; 731 } 732 state->in_prompt = 0; 733 *state->prompt = 0; 734 } 735 else if (ev->key == TB_KEY_ESC) 736 cancel_close(state); 737 return; 738 } 739 740 /* Remember to also edit the following condition if you 741 * reassign motion keys */ 742 if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP 743 && state->show_edit) 744 state->saved_column = -1; 745 746 for (key = 0; key < LEN(top_level_keys); key++) 747 { 748 current = top_level_keys + key; 749 if (((current->ev.mod == ev->mod) 750 && (current->ev.key == ev->key) && !ev->ch) 751 || (current->ev.ch && (current->ev.ch == ev->ch))) 752 if (current->callback) 753 { 754 current->callback(state); 755 return; 756 } 757 } 758 759 if (state->show_edit) 760 { 761 for (key = 0; key < LEN(edit_keys); key++) 762 { 763 current = &edit_keys[key]; 764 if (((current->ev.mod == ev->mod) 765 && (current->ev.key == ev->key) && !ev->ch) 766 || (current->ev.ch 767 && (current->ev.ch == ev->ch))) 768 if (current->callback) 769 { 770 current->callback(state); 771 return; 772 } 773 } 774 if (state->edit_info_focused) 775 { 776 state->error = ERR_DLG_FOCUS_EDIT; 777 return; 778 } 779 if (ev->ch) 780 { 781 insert_char(state, ev->ch); 782 return; 783 } 784 } 785 786 if (state->show_search) 787 { 788 for (key = 0; key < LEN(search_keys); key++) 789 { 790 current = &search_keys[key]; 791 if (((current->ev.mod == ev->mod) 792 && (current->ev.key == ev->key) && !ev->ch) 793 || (current->ev.ch 794 && (current->ev.ch == ev->ch))) 795 if (current->callback) 796 { 797 current->callback(state); 798 return; 799 } 800 } 801 if (ev->ch) 802 { 803 insert_search_char(state, ev->ch); 804 return; 805 } 806 } 807 808 if (state->show_help) 809 { 810 for (key = 0; key < LEN(help_keys); key++) 811 { 812 current = help_keys + key; 813 if (((current->ev.mod == ev->mod) 814 && (current->ev.key == ev->key) && !ev->ch) 815 || (current->ev.ch 816 && (current->ev.ch == ev->ch))) 817 if (current->callback) 818 { 819 current->callback(state); 820 return; 821 } 822 } 823 824 state->error = ERR_DLG_OPEN; 825 return; 826 } 827 828 if (state->show_search || state->show_edit) 829 { 830 state->error = ERR_DLG_OPEN_QUIT; 831 return; 832 } 833 834 for (key = 0; key < LEN(main_keys); key++) 835 { 836 current = &main_keys[key]; 837 if (((current->ev.mod == ev->mod) 838 && (current->ev.key == ev->key) && !ev->ch) 839 || (current->ev.ch && (current->ev.ch == ev->ch))) 840 if (current->callback) 841 { 842 current->callback(state); 843 return; 844 } 845 } 846 847 if (state->edit_info_focused) 848 state->error = ERR_DLG_FOCUS_EDIT; 849 else if (state->show_edit || state->show_search) 850 state->error = ERR_UNKNOWN_KEY; 851 else 852 state->error = ERR_UNKNOWN_KEY_HELP; 853 } 854 855 static int 856 hide_search(struct DrawState* state) 857 { 858 assert(state != NULL); 859 state->show_search = 0; 860 tb_hide_cursor(); 861 return 0; 862 } 863 864 static int 865 insert_char(struct DrawState* state, uint32_t ch) 866 { 867 struct BufferLine* buffer_line; 868 uint32_t* temp = NULL; 869 ssize_t i, len, size; 870 871 assert((state != NULL) && (state->input_buffer != NULL)); 872 if (state->edit_info_focused) 873 return 1; 874 875 ENSURE_SIZE(state->input_buffer[state->coord.row].text, temp, 876 state->input_buffer[state->coord.row].size, 877 state->input_buffer[state->coord.row].length + 2, 878 state->input_buffer[state->coord.row].size + LINE_ALLOC_DELTA, 879 insert_char_alloc_error, uint32_t); 880 881 buffer_line = &state->input_buffer[state->coord.row]; 882 len = buffer_line->length; 883 size = buffer_line->size; 884 i = len; 885 886 assert(buffer_line->text != NULL); 887 while (i + 1 < size && i > state->coord.col) 888 { 889 buffer_line->text[i] = buffer_line->text[i - 1]; 890 i--; 891 } 892 buffer_line->text[state->coord.col] = ch; 893 buffer_line->length++; 894 position_to(state, state->coord.row, state->coord.col + 1); 895 buffer_line->text[buffer_line->length] = 0; 896 897 state->edit_dirty = 1; 898 return 0; 899 900 insert_char_alloc_error: 901 state->error = ERR_CANT_ALLOC; 902 return 1; 903 } 904 905 static int 906 insert_line(struct DrawState* state) 907 { 908 struct BufferLine* newbuf = NULL; 909 struct BufferLine* line = NULL; 910 uint32_t* newtext = NULL; 911 uint32_t* ptext = NULL; 912 ssize_t i; 913 914 if (state->edit_info_focused) 915 { 916 state->error = ERR_DLG_FOCUS_EDIT; 917 return 1; 918 } 919 920 assert(state != NULL); 921 newbuf = realloc(state->input_buffer, 922 (state->input_rows_count + 1) * sizeof(struct BufferLine)); 923 if (!newbuf) 924 goto insert_line_alloc_error; 925 state->input_buffer = newbuf; 926 line = &state->input_buffer[state->input_rows_count]; 927 init_bufferline(line); 928 newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); 929 if (!newtext) 930 goto insert_line_alloc_error; 931 line->text = newtext; 932 line->size = LINE_DEFAULT; 933 state->input_rows_count++; 934 for (i = state->input_rows_count - 1; i > state->coord.row + 1; i--) 935 { 936 U32_SAFE_COPY(state->input_buffer[i].text, ptext, 937 state->input_buffer[i].size, 938 state->input_buffer[i - 1].text, 939 state->input_buffer[i].size, insert_line_alloc_error); 940 state->input_buffer[i].length 941 = state->input_buffer[i - 1].length; 942 } 943 ENSURE_SIZE(state->input_buffer[state->coord.row + 1].text, ptext, 944 state->input_buffer[state->coord.row + 1].size, LINE_DEFAULT, 945 LINE_DEFAULT, insert_line_alloc_error, uint32_t); 946 *state->input_buffer[state->coord.row + 1].text = 0; 947 if (state->coord.col < state->input_buffer[state->coord.row].length) 948 U32_SAFE_COPY(state->input_buffer[state->coord.row + 1].text, 949 ptext, state->input_buffer[state->coord.row + 1].size, 950 state->input_buffer[state->coord.row].text 951 + state->coord.col, 952 state->input_buffer[state->coord.row].size, 953 insert_line_alloc_error); 954 state->input_buffer[state->coord.row + 1].length 955 = state->input_buffer[state->coord.row].length 956 - state->coord.col; 957 state->input_buffer[state->coord.row].text[state->coord.col] = 0; 958 state->input_buffer[state->coord.row].length = state->coord.col; 959 960 state->saved_column = -1; 961 state->coord.row++; 962 if (state->coord.row 963 > state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2) 964 state->input_first_shown.row 965 = state->coord.row - (EDIT_HEIGHT - 3) / 2; 966 move_start(state); 967 state->edit_dirty = 1; 968 return 0; 969 970 insert_line_alloc_error: 971 state->error = ERR_CANT_ALLOC; 972 return 1; 973 } 974 975 static int 976 insert_search_char(struct DrawState* state, uint32_t ch) 977 { 978 ssize_t len, i; 979 int add_null; 980 assert(state != NULL); 981 len = u32_strlen(state->search); 982 if (state->search_column + 1 < MAXSEARCH) 983 { 984 i = len + 1; 985 add_null = state->search[state->search_column] == 0; 986 while (i + 2 < MAXSEARCH && i > state->search_column) 987 { 988 state->search[i] = state->search[i - 1]; 989 i--; 990 } 991 state->search[state->search_column++] = ch; 992 if (state->search_column == MAXSEARCH) 993 state->search_column = MAXSEARCH - 1; 994 state->search_display_column 995 = display_length(state->search, state->search_column); 996 if (state->search_display_column 997 - state->search_first_shown_column 998 > state->search_width - 4) 999 { 1000 state->search_first_shown_column 1001 = state->search_display_column 1002 - state->search_width + 4; 1003 state->search_display_column 1004 = display_length(state->search, 1005 state->search_column); 1006 } 1007 if (add_null) 1008 state->search[state->search_column] = 0; 1009 1010 next_match(state); 1011 } 1012 return 0; 1013 } 1014 1015 static int 1016 insert_search_space(struct DrawState* state) 1017 { 1018 return insert_search_char(state, SPACE); 1019 } 1020 1021 static int 1022 insert_search_tab(struct DrawState* state) 1023 { 1024 return insert_search_char(state, TAB); 1025 } 1026 1027 static int 1028 insert_space(struct DrawState* state) 1029 { 1030 return insert_char(state, SPACE); 1031 } 1032 1033 static int 1034 insert_tab(struct DrawState* state) 1035 { 1036 return insert_char(state, TAB); 1037 } 1038 1039 static int 1040 join_lines(struct DrawState* state) 1041 { 1042 struct BufferLine* first_line = NULL; 1043 struct BufferLine* next_line = NULL; 1044 uint32_t* ptext = NULL; 1045 ssize_t i; 1046 1047 assert((state != NULL) && (state->input_buffer != NULL)); 1048 if (state->coord.row + 1 == state->input_rows_count) 1049 return 1; 1050 1051 first_line = &state->input_buffer[state->coord.row]; 1052 next_line = &state->input_buffer[state->coord.row + 1]; 1053 ENSURE_SIZE(first_line->text, ptext, first_line->size, 1054 first_line->length + next_line->length + 1, 1055 first_line->length + next_line->length + LINE_ALLOC_DELTA, 1056 join_lines_alloc_error, uint32_t); 1057 U32_MEMCCPY(first_line->text + first_line->length, next_line->text, 1058 next_line->length + 1, ptext); 1059 first_line->length = u32_strlen(first_line->text); 1060 for (i = state->coord.row + 1; i + 1 < state->input_rows_count; i++) 1061 { 1062 U32_SAFE_COPY(state->input_buffer[i].text, ptext, 1063 state->input_buffer[i].size, 1064 state->input_buffer[i + 1].text, 1065 state->input_buffer[i + 1].size, 1066 join_lines_alloc_error); 1067 state->input_buffer[i].length 1068 = state->input_buffer[i + 1].length; 1069 } 1070 free_bufferline(&state->input_buffer[state->input_rows_count - 1]); 1071 state->input_rows_count--; 1072 state->edit_dirty = 1; 1073 return 0; 1074 1075 join_lines_alloc_error: 1076 state->error = ERR_CANT_ALLOC; 1077 return 1; 1078 } 1079 1080 static int 1081 load_info(struct DrawState* state) 1082 { 1083 struct PoEntry* first = NULL; 1084 struct PoEntry* current = NULL; 1085 struct BufferLine* newbuf = NULL; 1086 struct BufferLine* pnewbuf = NULL; 1087 uint32_t* newtext = NULL; 1088 ssize_t msgid_num_lines, msgid_plural_num_lines, num_lines, i; 1089 1090 assert((state != NULL) && (state->entries != NULL)); 1091 first = &state->entries[0]; 1092 current = &state->entries[state->msgid_number - 1]; 1093 msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); 1094 msgid_plural_num_lines 1095 = u32_lines_in_string(current->msgid_plural, 0, 0); 1096 num_lines = 0; 1097 1098 for (i = 0; i < state->info_rows_count; i++) 1099 free_bufferline(&state->info_buffer[i]); 1100 free(state->info_buffer); 1101 1102 state->info_maxlen = 0; 1103 1104 if (current->msgid) 1105 num_lines += msgid_num_lines + 1; 1106 if (current->msgid_plural) 1107 { 1108 num_lines += msgid_plural_num_lines + 1; 1109 if (first->plural_forms) 1110 num_lines += 2; /* title + plural_forms */ 1111 } 1112 if (current->comments) 1113 num_lines += current->comment_lines + 1; 1114 if (current->extr_comments) 1115 num_lines += current->extr_comment_lines + 1; 1116 if (current->ref_comments) 1117 num_lines += current->ref_comment_lines + 1; 1118 newbuf = calloc(num_lines, sizeof(struct BufferLine)); 1119 if (!newbuf) 1120 goto load_info_alloc_error; 1121 1122 pnewbuf = newbuf; 1123 for (i = 0; i < num_lines; i++) 1124 { 1125 init_bufferline(pnewbuf); 1126 newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); 1127 if (!newtext) 1128 goto load_info_alloc_error; 1129 pnewbuf->text = newtext; 1130 pnewbuf->size = LINE_DEFAULT; 1131 pnewbuf->size = 0; 1132 pnewbuf++; 1133 } 1134 1135 state->info_rows_count = num_lines; 1136 1137 pnewbuf = newbuf; 1138 1139 if (current->msgid) 1140 if (load_info_msgid(state, &pnewbuf)) 1141 return 1; 1142 1143 if (current->msgid_plural) 1144 if (load_info_msgid_plural(state, &pnewbuf)) 1145 return 1; 1146 1147 if (current->comments) 1148 if (load_info_comments(state, &pnewbuf)) 1149 return 1; 1150 1151 if (current->extr_comments) 1152 if (load_info_extr_comments(state, &pnewbuf)) 1153 return 1; 1154 1155 if (current->ref_comments) 1156 if (load_info_ref_comments(state, &pnewbuf)) 1157 return 1; 1158 1159 state->info_buffer = newbuf; 1160 return 0; 1161 1162 load_info_alloc_error: 1163 state->error = ERR_CANT_ALLOC; 1164 return 1; 1165 } 1166 1167 static int 1168 load_info_comments(struct DrawState* state, struct BufferLine** lines) 1169 { 1170 struct PoEntry* current = NULL; 1171 uint32_t* comment = NULL; 1172 uint32_t* ptext = NULL; 1173 ssize_t len, llen, size, i; 1174 1175 current = &state->entries[state->msgid_number - 1]; 1176 ptext = u32_u8_memccpy((*lines)->text, "Comments:", 0, LINE_DEFAULT); 1177 (*lines)->length = llen 1178 = ptext ? ptext - (*lines)->text : LINE_DEFAULT - 1; 1179 (*lines)->size = LINE_DEFAULT; 1180 if (llen > state->info_maxlen) 1181 state->info_maxlen = llen; 1182 (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; 1183 (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; 1184 (*lines)++; 1185 for (i = 0; i < current->comment_lines; i++) 1186 { 1187 comment = current->comments[i]; 1188 len = u32_strlen(comment); 1189 size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ 1190 ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, 1191 load_info_comments_alloc_error, uint32_t); 1192 U32_MEMCCPY((*lines)->text, comment, len + 1, ptext); 1193 llen = ptext ? ptext - (*lines)->text : (*lines)->size - 1; 1194 (*lines)->length = llen; 1195 if (llen > state->info_maxlen) 1196 state->info_maxlen = llen; 1197 (*lines)++; 1198 } 1199 return 0; 1200 1201 load_info_comments_alloc_error: 1202 state->error = ERR_CANT_ALLOC; 1203 return 1; 1204 } 1205 1206 static int 1207 load_info_extr_comments(struct DrawState* state, struct BufferLine** lines) 1208 { 1209 struct PoEntry* current = NULL; 1210 uint32_t* extr_comment = NULL; 1211 uint32_t* ptext = NULL; 1212 ssize_t len, llen, size, i; 1213 1214 current = &state->entries[state->msgid_number - 1]; 1215 ptext = u32_u8_memccpy((*lines)->text, "Translation comments:", 0, 1216 LINE_DEFAULT); 1217 (*lines)->length = llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; 1218 (*lines)->size = MAX(llen + 1, LINE_DEFAULT); 1219 if (llen > state->info_maxlen) 1220 state->info_maxlen = llen; 1221 (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; 1222 (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; 1223 (*lines)++; 1224 for (i = 0; i < current->extr_comment_lines; i++) 1225 { 1226 extr_comment = current->extr_comments[i]; 1227 len = u32_strlen(extr_comment); 1228 size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ 1229 ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, 1230 load_info_extr_comments_alloc_error, uint32_t); 1231 U32_MEMCCPY((*lines)->text, extr_comment, len + 1, ptext); 1232 /*len = ptext ? ptext - (*lines)->text : (*lines)->size - 1;*/ 1233 (*lines)->length = llen; 1234 if (llen > state->info_maxlen) 1235 state->info_maxlen = llen; 1236 (*lines)++; 1237 } 1238 return 0; 1239 1240 load_info_extr_comments_alloc_error: 1241 state->error = ERR_CANT_ALLOC; 1242 return 1; 1243 } 1244 1245 static int 1246 load_info_msgid(struct DrawState* state, struct BufferLine** lines) 1247 { 1248 struct PoEntry* current = NULL; 1249 const uint32_t* pumsgid = NULL; 1250 const uint32_t* old_pumsgid = NULL; 1251 uint32_t* ptext = NULL; 1252 ssize_t len, llen, size; 1253 1254 current = &state->entries[state->msgid_number - 1]; 1255 ptext = u32_u8_memccpy((*lines)->text, "msgid:", 0, LINE_DEFAULT); 1256 (*lines)->length = llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; 1257 if (llen > state->info_maxlen) 1258 state->info_maxlen = llen; 1259 (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; 1260 (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; 1261 (*lines)++; 1262 pumsgid = current->msgid; 1263 while (*pumsgid) 1264 { 1265 old_pumsgid = pumsgid; 1266 len = u32_next_line(current->msgid, &pumsgid, 0, 0); 1267 size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ 1268 ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, 1269 load_info_msgid_alloc_error, uint32_t); 1270 U32_MEMCCPY((*lines)->text, old_pumsgid, len + 1, ptext); 1271 llen = u32_decode_tabs(state, (*lines)->text, len + 1); 1272 (*lines)->length = llen; 1273 if (llen > state->info_maxlen) 1274 state->info_maxlen = llen; 1275 (*lines)++; 1276 } 1277 return 0; 1278 1279 load_info_msgid_alloc_error: 1280 state->error = ERR_CANT_ALLOC; 1281 return 1; 1282 } 1283 1284 static int 1285 load_info_msgid_plural(struct DrawState* state, struct BufferLine** lines) 1286 { 1287 struct PoEntry* current = NULL; 1288 struct PoEntry* first = NULL; 1289 const uint32_t* old_pumsgid = NULL; 1290 const uint32_t* pumsgid = NULL; 1291 uint32_t* ptext = NULL; 1292 ssize_t len, llen, size; 1293 1294 first = &state->entries[0]; 1295 current = &state->entries[state->msgid_number - 1]; 1296 ptext = u32_u8_memccpy((*lines)->text, "msgid_plural:", 0, LINE_DEFAULT); 1297 (*lines)->length = llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; 1298 if (llen > state->info_maxlen) 1299 state->info_maxlen = llen; 1300 (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; 1301 (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; 1302 (*lines)++; 1303 pumsgid = current->msgid_plural; 1304 while (*pumsgid) 1305 { 1306 old_pumsgid = pumsgid; 1307 len = u32_next_line(current->msgid_plural, &pumsgid, 0, 0); 1308 size = MAX(len + 2, LINE_DEFAULT); 1309 ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, 1310 load_info_msgid_plural_alloc_error, uint32_t); 1311 U32_MEMCCPY((*lines)->text, old_pumsgid, len + 1, ptext); 1312 llen = u32_decode_tabs(state, (*lines)->text, size); 1313 (*lines)->length = llen; 1314 if (llen > state->info_maxlen) 1315 state->info_maxlen = llen; 1316 (*lines)++; 1317 } 1318 if (first->plural_forms) 1319 { 1320 ptext = u32_u8_memccpy((*lines)->text, "Plural forms:", 0, 1321 LINE_DEFAULT); 1322 (*lines)->length = llen 1323 = ptext ? ptext - (*lines)->text : LINE_DEFAULT - 1; 1324 (*lines)->size = LINE_DEFAULT; 1325 if (llen > state->info_maxlen) 1326 state->info_maxlen = llen; 1327 (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; 1328 (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; 1329 (*lines)++; 1330 U32_SAFE_COPY((*lines)->text, ptext, (*lines)->size, 1331 first->plural_forms, first->plural_forms_size, 1332 load_info_msgid_plural_alloc_error); 1333 (*lines)->length = llen 1334 = ptext ? ptext - (*lines)->text : (*lines)->size - 1; 1335 //(*lines)->size = MAX(llen + 1, LINE_DEFAULT); 1336 if (llen > state->info_maxlen) 1337 state->info_maxlen = llen; 1338 (*lines)++; 1339 } 1340 return 0; 1341 1342 load_info_msgid_plural_alloc_error: 1343 state->error = ERR_CANT_ALLOC; 1344 return 1; 1345 } 1346 1347 static int 1348 load_info_ref_comments(struct DrawState* state, struct BufferLine** lines) 1349 { 1350 struct PoEntry* current = NULL; 1351 uint32_t* ptext = NULL; 1352 char* ref_comment = NULL; 1353 ssize_t len, llen, size, i; 1354 1355 current = &state->entries[state->msgid_number - 1]; 1356 ptext = u32_u8_memccpy((*lines)->text, "References:", 0, LINE_DEFAULT); 1357 (*lines)->length = llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; 1358 (*lines)->size = MAX(llen + 1, LINE_DEFAULT); 1359 if (llen > state->info_maxlen) 1360 state->info_maxlen = llen; 1361 (*lines)->fg = colors[C_EDIT_MSGID_HEADING].fg; 1362 (*lines)->bg = colors[C_EDIT_MSGID_HEADING].bg; 1363 (*lines)++; 1364 for (i = 0; i < current->ref_comment_lines; i++) 1365 { 1366 ref_comment = current->ref_comments[i]; 1367 len = strlen(ref_comment); 1368 size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ 1369 ENSURE_SIZE((*lines)->text, ptext, (*lines)->size, size, size, 1370 load_info_ref_comments_alloc_error, uint32_t); 1371 U32_U8_MEMCCPY((*lines)->text, ref_comment, len + 1, ptext); 1372 llen = ptext ? ptext - (*lines)->text : LINE_DEFAULT; 1373 (*lines)->length = llen; 1374 if (llen > state->info_maxlen) 1375 state->info_maxlen = llen; 1376 (*lines)++; 1377 } 1378 return 0; 1379 1380 load_info_ref_comments_alloc_error: 1381 state->error = ERR_CANT_ALLOC; 1382 return 1; 1383 } 1384 1385 static int 1386 load_msgstr(struct DrawState* state) 1387 { 1388 struct PoEntry* current; 1389 struct BufferLine* newbuf; 1390 struct BufferLine* pnewbuf; 1391 const uint32_t* old_pumsgstr = NULL; 1392 const uint32_t* pumsgstr = NULL; 1393 uint32_t* newtext = NULL; 1394 uint32_t* temp = NULL; 1395 ssize_t num_lines, i, len, newlen, size, row; 1396 int msgstr_index; 1397 1398 assert((state != NULL) && (state->entries != NULL)); 1399 current = &state->entries[state->msgid_number - 1]; 1400 newbuf = NULL; 1401 pnewbuf = NULL; 1402 msgstr_index = state->msgstr_index; 1403 num_lines = current->msgstr_count == 0 1404 ? 1 1405 : u32_lines_in_string(current->msgstr[msgstr_index], 0, 0); 1406 1407 for (row = 0; row < state->input_rows_count; row++) 1408 free_bufferline(&state->input_buffer[row]); 1409 free(state->input_buffer); 1410 1411 newbuf = calloc(num_lines, sizeof(struct BufferLine)); 1412 if (!newbuf) 1413 goto load_msgstr_alloc_error; 1414 1415 pnewbuf = newbuf; 1416 for (i = 0; i < num_lines; i++) 1417 { 1418 init_bufferline(pnewbuf); 1419 assert(current->msgstr_size != NULL); 1420 size = MAX(current->msgstr_size[msgstr_index], LINE_DEFAULT); 1421 newtext = calloc(size, sizeof(uint32_t)); 1422 if (!newtext) 1423 goto load_msgstr_alloc_error; 1424 pnewbuf->text = newtext; 1425 pnewbuf->size = size; 1426 pnewbuf->length = 0; 1427 pnewbuf++; 1428 } 1429 state->input_rows_count = num_lines; 1430 1431 if (current->msgstr_count > 0) 1432 { 1433 pumsgstr = current->msgstr[msgstr_index]; 1434 if (!pumsgstr) 1435 { 1436 newbuf[0].length = 0; 1437 goto load_msgstr_done; 1438 } 1439 pnewbuf = newbuf; 1440 while (*pumsgstr) 1441 { 1442 old_pumsgstr = pumsgstr; 1443 len = u32_next_line(current->msgstr[msgstr_index], 1444 &pumsgstr, 0, 0); 1445 size = MAX(len + 2, LINE_DEFAULT); /* + NUL */ 1446 ENSURE_SIZE(pnewbuf->text, temp, pnewbuf->size, size, 1447 size, load_msgstr_alloc_error, uint32_t); 1448 U32_MEMCCPY(pnewbuf->text, old_pumsgstr, len + 1, temp); 1449 newlen = u32_decode_tabs(state, pnewbuf->text, len + 1); 1450 pnewbuf->length = newlen; 1451 pnewbuf++; 1452 } 1453 } 1454 else 1455 newbuf[0].length = 0; 1456 1457 load_msgstr_done: 1458 state->input_buffer = newbuf; 1459 return 0; 1460 1461 load_msgstr_alloc_error: 1462 state->error = ERR_CANT_ALLOC; 1463 return 1; 1464 } 1465 1466 static int 1467 move_down(struct DrawState* state) 1468 { 1469 assert(state != NULL); 1470 if (state->edit_info_focused) 1471 { 1472 if (state->info_first_shown.row + (EDIT_HEIGHT - 3) / 2 1473 < state->info_rows_count) 1474 state->info_first_shown.row++; 1475 return 0; 1476 } 1477 position_to(state, state->coord.row + 1, state->coord.col); 1478 return 0; 1479 } 1480 1481 static int 1482 move_end(struct DrawState* state) 1483 { 1484 assert((state != NULL) && (state->input_buffer != NULL)); 1485 if (state->edit_info_focused) 1486 { 1487 if (state->info_maxlen < state->edit_width - 3) 1488 state->info_first_shown.col = 0; 1489 else 1490 state->info_first_shown.col 1491 = state->info_maxlen - state->edit_width + 3; 1492 return 0; 1493 } 1494 position_to(state, state->coord.row, 1495 state->input_buffer[state->coord.row].length); 1496 return 0; 1497 } 1498 1499 static int 1500 move_left(struct DrawState* state) 1501 { 1502 assert((state != NULL) && (state->input_buffer != NULL)); 1503 if (state->edit_info_focused) 1504 { 1505 if (state->info_first_shown.col > 0) 1506 state->info_first_shown.col--; 1507 return 0; 1508 } 1509 position_to(state, state->coord.row, state->coord.col - 1); 1510 return 0; 1511 } 1512 1513 static int 1514 move_list_down(struct DrawState* state) 1515 { 1516 assert(state != NULL); 1517 goto_msgid(state, next_msgid(state, 1)); 1518 return 0; 1519 } 1520 1521 static int 1522 move_list_end(struct DrawState* state) 1523 { 1524 assert(state != NULL); 1525 goto_msgid(state, 1526 next_msgid(state, 1527 state->real_msgid_count - state->msgid_number)); 1528 return 0; 1529 } 1530 1531 static int 1532 move_list_page_down(struct DrawState* state) 1533 { 1534 assert(state != NULL); 1535 state->error = ERR_NONE; 1536 goto_msgid(state, next_msgid(state, state->maxy - 2)); 1537 return 0; 1538 } 1539 1540 static int 1541 move_list_page_up(struct DrawState* state) 1542 { 1543 assert(state != NULL); 1544 state->error = ERR_NONE; 1545 goto_msgid(state, prev_msgid(state, state->maxy - 2)); 1546 return 0; 1547 } 1548 1549 static int 1550 move_list_start(struct DrawState* state) 1551 { 1552 assert(state != NULL); 1553 goto_msgid(state, 1); 1554 return 0; 1555 } 1556 1557 static int 1558 move_list_up(struct DrawState* state) 1559 { 1560 assert(state != NULL); 1561 goto_msgid(state, prev_msgid(state, 1)); 1562 return 0; 1563 } 1564 1565 static int 1566 move_next_word(struct DrawState* state) 1567 { 1568 uint32_t* pline; 1569 ssize_t len; 1570 int i; 1571 1572 assert(state != NULL); 1573 if (state->edit_info_focused) 1574 { 1575 state->error = ERR_DLG_FOCUS_EDIT; 1576 return 1; 1577 } 1578 1579 i = state->coord.col; 1580 pline = state->input_buffer[state->coord.row].text; 1581 len = state->input_buffer[state->coord.row].length; 1582 1583 while (i < len && !u32_is_word_boundary(pline[i], 0)) 1584 i++; 1585 while (i < len && u32_is_word_boundary(pline[i], 0)) 1586 i++; 1587 1588 position_to(state, state->coord.row, i); 1589 return 0; 1590 } 1591 1592 static int 1593 move_prev_word(struct DrawState* state) 1594 { 1595 uint32_t* pline; 1596 int i; 1597 1598 assert((state != NULL) && (state->input_buffer != NULL)); 1599 if (state->edit_info_focused) 1600 { 1601 state->error = ERR_DLG_FOCUS_EDIT; 1602 return 1; 1603 } 1604 1605 i = state->coord.col; 1606 pline = state->input_buffer[state->coord.row].text; 1607 1608 if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) 1609 i--; 1610 while (i > 0 && u32_is_word_boundary(pline[i], 0)) 1611 i--; 1612 while (i > 0 && !u32_is_word_boundary(pline[i], 0) 1613 && !u32_is_word_boundary(pline[i - 1], 0)) 1614 i--; 1615 1616 position_to(state, state->coord.row, i); 1617 return 0; 1618 } 1619 1620 static int 1621 move_right(struct DrawState* state) 1622 { 1623 assert(state != NULL); 1624 if (state->edit_info_focused) 1625 { 1626 if (state->info_first_shown.col + state->edit_width - 3 1627 < state->info_maxlen + 1) 1628 state->info_first_shown.col++; 1629 return 0; 1630 } 1631 position_to(state, state->coord.row, state->coord.col + 1); 1632 return 0; 1633 } 1634 1635 static int 1636 move_search_end(struct DrawState* state) 1637 { 1638 assert(state != NULL); 1639 search_position_to(state, u32_strlen(state->search)); 1640 return 0; 1641 } 1642 1643 static int 1644 move_search_left(struct DrawState* state) 1645 { 1646 assert((state != NULL) && (state->search != NULL)); 1647 if (state->search_column == 0) 1648 return 1; 1649 search_position_to(state, state->search_column - 1); 1650 return 0; 1651 } 1652 1653 static int 1654 move_search_next_word(struct DrawState* state) 1655 { 1656 uint32_t* pline = NULL; 1657 ssize_t len; 1658 int i; 1659 1660 assert((state != NULL) && (state->search != NULL)); 1661 i = state->search_column; 1662 pline = state->search; 1663 len = u32_strlen(state->search); 1664 while (i < len && !u32_is_word_boundary(pline[i], 0)) 1665 i++; 1666 while (i < len && u32_is_word_boundary(pline[i], 0)) 1667 i++; 1668 search_position_to(state, i); 1669 return 0; 1670 } 1671 1672 static int 1673 move_search_prev_word(struct DrawState* state) 1674 { 1675 uint32_t* pline = NULL; 1676 int i; 1677 1678 assert((state != NULL) && (state->search != NULL)); 1679 i = state->search_column; 1680 pline = state->search; 1681 if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) 1682 i--; 1683 while (i > 0 && u32_is_word_boundary(pline[i], 0)) 1684 i--; 1685 while (i > 0 && !u32_is_word_boundary(pline[i], 0) 1686 && !u32_is_word_boundary(pline[i - 1], 0)) 1687 i--; 1688 search_position_to(state, i); 1689 return 0; 1690 } 1691 1692 static int 1693 move_search_right(struct DrawState* state) 1694 { 1695 assert(state != NULL); 1696 search_position_to(state, state->search_display_column + 1); 1697 return 0; 1698 } 1699 1700 static int 1701 move_search_start(struct DrawState* state) 1702 { 1703 assert(state != NULL); 1704 search_position_to(state, 0); 1705 return 0; 1706 } 1707 1708 static int 1709 move_start(struct DrawState* state) 1710 { 1711 assert(state != NULL); 1712 if (state->edit_info_focused) 1713 { 1714 state->info_first_shown.row = 0; 1715 state->info_first_shown.col = 0; 1716 return 0; 1717 } 1718 position_to(state, state->coord.row, 0); 1719 return 0; 1720 } 1721 1722 static int 1723 move_up(struct DrawState* state) 1724 { 1725 assert(state != NULL); 1726 if (state->edit_info_focused) 1727 { 1728 if (state->info_first_shown.row > 0) 1729 state->info_first_shown.row--; 1730 return 0; 1731 } 1732 position_to(state, state->coord.row > 1 ? state->coord.row - 1 : 0, 1733 state->coord.col); 1734 return 0; 1735 } 1736 1737 static int 1738 next_fuzzy(struct DrawState* state) 1739 { 1740 struct PoEntry* current = NULL; 1741 char* temp = NULL; 1742 ssize_t e; 1743 1744 assert(state != NULL); 1745 if (state->msgid_count == 0) 1746 return 1; 1747 1748 if (state->edit_dirty) 1749 { 1750 state->in_prompt = 1; 1751 MEMCCPY(state->prompt, prompt_edit_dirty, LINE_DEFAULT, temp); 1752 state->prompt_callback = next_fuzzy_callback; 1753 return 0; 1754 } 1755 1756 for (e = state->msgid_number; e < state->msgid_count; e++) 1757 { 1758 current = &state->entries[e]; 1759 if (current->flags & FL_FUZZY) 1760 { 1761 goto_msgid(state, e + 1); 1762 if (state->show_edit) 1763 { 1764 state->msgstr_index = 0; 1765 show_edit(state); 1766 } 1767 return 0; 1768 } 1769 } 1770 return 0; 1771 } 1772 1773 static int 1774 next_fuzzy_callback(struct DrawState* state, struct tb_event* ev) 1775 { 1776 assert((state != NULL) && (ev != NULL)); 1777 if (strchr("Yy", ev->ch)) 1778 { 1779 state->reshow_edit = 1; 1780 save_msgstr(state); 1781 } 1782 else 1783 state->edit_dirty = 0; 1784 return next_fuzzy(state); 1785 } 1786 1787 static int 1788 next_match(struct DrawState* state) 1789 { 1790 struct PoEntry* current = NULL; 1791 ssize_t e; 1792 1793 assert(state != NULL); 1794 if (state->msgid_count == 0 || !state->search || !*state->search) 1795 return 1; 1796 1797 for (e = state->msgid_number; e < state->real_msgid_count; e++) 1798 { 1799 current = state->entries + e; 1800 if (current->obsolete) 1801 continue; 1802 if ((current->msgid && *current->msgid 1803 && u32_strstr(current->msgid, state->search)) 1804 || (current->msgstr && *current->msgstr 1805 && **current->msgstr 1806 && u32_strstr(*current->msgstr, state->search))) 1807 { 1808 goto_msgid(state, e + 1); 1809 return 0; 1810 } 1811 } 1812 return 0; 1813 } 1814 1815 static int 1816 next_plural(struct DrawState* state) 1817 { 1818 struct PoEntry* current = NULL; 1819 char* temp = NULL; 1820 assert((state != NULL) && (state->entries != NULL)); 1821 if (state->edit_info_focused) 1822 { 1823 state->error = ERR_DLG_FOCUS_EDIT; 1824 return 1; 1825 } 1826 if (state->edit_dirty) 1827 { 1828 state->in_prompt = 1; 1829 MEMCCPY(state->prompt, prompt_edit_dirty, LINE_DEFAULT, temp); 1830 state->prompt_callback = next_plural_callback; 1831 return 0; 1832 } 1833 current = &state->entries[state->msgid_number - 1]; 1834 if (state->msgstr_index + 1 < current->msgstr_count) 1835 state->msgstr_index++; 1836 show_edit(state); 1837 return 0; 1838 } 1839 1840 static int 1841 next_plural_callback(struct DrawState* state, struct tb_event* ev) 1842 { 1843 assert((state != NULL) && (ev != NULL)); 1844 if (strchr("Yy", ev->ch)) 1845 { 1846 state->reshow_edit = 1; 1847 save_msgstr(state); 1848 } 1849 else 1850 state->edit_dirty = 0; 1851 return next_plural(state); 1852 } 1853 1854 static int 1855 next_untranslated(struct DrawState* state) 1856 { 1857 struct PoEntry* current = NULL; 1858 char* temp = NULL; 1859 ssize_t e; 1860 1861 assert((state != NULL) && (state->entries != NULL)); 1862 if (state->msgid_count == 0) 1863 return 1; 1864 1865 if (state->edit_dirty) 1866 { 1867 state->in_prompt = 1; 1868 MEMCCPY(state->prompt, prompt_edit_dirty, LINE_DEFAULT, temp); 1869 state->prompt_callback = next_untranslated_callback; 1870 return 0; 1871 } 1872 1873 for (e = state->msgid_number; e < state->msgid_count; e++) 1874 { 1875 current = state->entries + e; 1876 if (!current->msgstr || !*current->msgstr 1877 || u32_strlen(*current->msgstr) == 0) 1878 { 1879 goto_msgid(state, e + 1); 1880 if (state->show_edit) 1881 { 1882 state->msgstr_index = 0; 1883 show_edit(state); 1884 } 1885 return 0; 1886 } 1887 } 1888 return 0; 1889 } 1890 1891 static int 1892 next_untranslated_callback(struct DrawState* state, struct tb_event* ev) 1893 { 1894 assert((state != NULL) && (ev != NULL)); 1895 if (strchr("Yy", ev->ch)) 1896 { 1897 state->reshow_edit = 1; 1898 save_msgstr(state); 1899 } 1900 else 1901 state->edit_dirty = 0; 1902 return next_untranslated(state); 1903 } 1904 1905 static size_t 1906 next_msgid(struct DrawState* state, const int delta) 1907 { 1908 size_t ret, start, last_non_obsolete; 1909 assert((state != NULL) && (state->entries != NULL)); 1910 ret = state->msgid_number; 1911 start = ret; 1912 last_non_obsolete 1913 = state->real_msgid_count - state->obsolete_at_end_count; 1914 while (ret < last_non_obsolete + 1) 1915 { 1916 ret++; 1917 if (!state->entries[ret - 1].obsolete && ret >= start + delta) 1918 break; 1919 } 1920 return ret == last_non_obsolete + 1 ? last_non_obsolete : ret; 1921 } 1922 1923 static int 1924 overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev) 1925 { 1926 assert((state != NULL) && (ev != NULL)); 1927 if (ev->ch == 'y') 1928 copy_msgid_to_input(state); 1929 state->prompt_callback = NULL; 1930 return 0; 1931 } 1932 1933 static int 1934 overwrite_paste_callback(struct DrawState* state, struct tb_event* ev) 1935 { 1936 assert((state != NULL) && (ev != NULL)); 1937 if (ev->ch == 'y') 1938 paste_paste_buffer_to_input(state); 1939 state->prompt_callback = NULL; 1940 return 0; 1941 } 1942 1943 static int 1944 paste_paste_buffer_to_input(struct DrawState* state) 1945 { 1946 struct BufferLine* pinput = NULL; 1947 struct BufferLine* newbuf = NULL; 1948 struct BufferLine* ppaste = NULL; 1949 uint32_t* newtext = NULL; 1950 uint32_t* utemp = NULL; 1951 char* temp = NULL; 1952 ssize_t lines_to_init = 0; 1953 ssize_t lines_to_free = 0; 1954 ssize_t i; 1955 1956 assert(state != NULL); 1957 if (state->edit_info_focused) 1958 { 1959 state->error = ERR_DLG_FOCUS_EDIT; 1960 return 1; 1961 } 1962 1963 assert(state->input_buffer != NULL); 1964 if (!state->in_prompt && *state->input_buffer->text) 1965 { 1966 state->in_prompt = 1; 1967 MEMCCPY(state->prompt, prompt_overwrite, LINE_DEFAULT, temp); 1968 state->prompt_callback = overwrite_paste_callback; 1969 return 0; 1970 } 1971 1972 if (state->msgid_number == 1) 1973 { 1974 state->error = ERR_ILLEGAL_ON_FIRST; 1975 return 1; 1976 } 1977 1978 if (!state->paste || !(*state->paste).text || !*(*state->paste).text) 1979 { 1980 /* leave one row */ 1981 for (i = 1; i < state->input_rows_count; i++) 1982 free_bufferline(state->input_buffer + i); 1983 state->input_buffer = realloc(state->input_buffer, 1984 sizeof(struct BufferLine)); 1985 if (!state->input_buffer) 1986 goto paste_paste_buffer_alloc_error; 1987 *(state->input_buffer->text) = 0; 1988 init_coord(&state->coord, 0, 0); 1989 state->input_display_column = 0; 1990 init_coord(&state->input_first_shown, 0, 0); 1991 state->input_rows_count = 1; 1992 state->edit_dirty = 1; 1993 return 0; 1994 } 1995 1996 lines_to_init = state->input_rows_count < state->paste_count 1997 ? state->paste_count - state->input_rows_count 1998 : 0; 1999 lines_to_free = state->input_rows_count > state->paste_count 2000 ? state->input_rows_count - state->paste_count 2001 : 0; 2002 2003 for (i = 0; i < lines_to_free; i++) 2004 free_bufferline( 2005 state->input_buffer + state->input_rows_count - i - 1); 2006 newbuf = realloc(state->input_buffer, 2007 state->paste_count * sizeof(struct BufferLine)); 2008 ppaste = state->paste; 2009 2010 if (!newbuf) 2011 goto paste_paste_buffer_alloc_error; 2012 2013 for (i = 0; i < lines_to_init; i++) 2014 { 2015 pinput = newbuf + state->paste_count - 1 - i; 2016 init_bufferline(pinput); 2017 newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); 2018 if (!newtext) 2019 goto paste_paste_buffer_alloc_error; 2020 pinput->text = newtext; 2021 } 2022 2023 if (ppaste) 2024 for (i = 0; i < state->paste_count; i++) 2025 { 2026 pinput = newbuf + i; 2027 U32_MEMCCPY(pinput->text, ppaste->text, 2028 MIN(ppaste->length + 1, LINE_DEFAULT), utemp); 2029 pinput->length = ppaste->length; 2030 ppaste++; 2031 } 2032 2033 state->input_rows_count = state->paste_count; 2034 state->input_buffer = newbuf; 2035 state->edit_dirty = 1; 2036 return 0; 2037 2038 paste_paste_buffer_alloc_error: 2039 state->error = ERR_CANT_ALLOC; 2040 return 1; 2041 } 2042 2043 /* 2044 * state->saved_column - display coordinate 2045 * row, column - buffer coordinates (not display) 2046 */ 2047 static void 2048 position_to(struct DrawState* state, const int row, const int column) 2049 { 2050 ssize_t dlen; 2051 2052 assert((state != NULL) && (state->input_buffer != NULL)); 2053 if (row != state->coord.row) 2054 { 2055 state->coord.row = row; 2056 if ((state->coord.row + 1) > state->input_rows_count) 2057 state->coord.row = state->input_rows_count - 1; 2058 if (state->coord.row < state->input_first_shown.row) 2059 state->input_first_shown.row = state->coord.row; 2060 if (state->input_first_shown.row + (EDIT_HEIGHT - 3) / 2 2061 < state->coord.row) 2062 state->input_first_shown.row 2063 = state->coord.row - (EDIT_HEIGHT - 3) / 2; 2064 if (state->saved_column == -1) 2065 { 2066 state->saved_column = state->input_display_column; 2067 state->coord.col = column; 2068 } 2069 else 2070 state->coord.col = buffer_length( 2071 state->input_buffer[state->coord.row].text, 2072 state->saved_column); 2073 if (state->coord.col < 0) 2074 state->coord.col = 0; 2075 else if (state->coord.col 2076 > (int)state->input_buffer[state->coord.row].length) 2077 state->coord.col 2078 = state->input_buffer[state->coord.row].length; 2079 dlen = display_length(state->input_buffer[state->coord.row].text, 2080 state->coord.col); 2081 if (dlen < state->saved_column) 2082 state->input_display_column = dlen; 2083 else 2084 state->input_display_column = state->saved_column; 2085 } 2086 else 2087 { 2088 state->coord.col = column; 2089 if (state->coord.col < 0) 2090 { 2091 if (state->coord.row > 0) 2092 { 2093 state->coord.row--; 2094 if (state->input_first_shown.row > 0 2095 && state->input_first_shown.row 2096 > state->coord.row) 2097 state->input_first_shown.row--; 2098 state->coord.col 2099 = state->input_buffer[state->coord.row] 2100 .length; 2101 } 2102 else 2103 state->coord.col = 0; 2104 } 2105 else if (state->coord.col 2106 > state->input_buffer[state->coord.row].length) 2107 { 2108 if ((state->coord.row + 1) < state->input_rows_count) 2109 { 2110 state->coord.row++; 2111 if (state->input_first_shown.row 2112 + (state->maxy - 2) 2113 < state->coord.row 2114 && state->input_first_shown.row + 1 2115 < state->input_rows_count) 2116 state->input_first_shown.row++; 2117 state->coord.col = 0; 2118 } 2119 else 2120 state->coord.col 2121 = state->input_buffer[state->coord.row] 2122 .length; 2123 } 2124 state->input_display_column 2125 = display_length(state->input_buffer[state->coord.row] 2126 .text, 2127 state->coord.col); 2128 state->saved_column = state->input_display_column; 2129 } 2130 if (state->input_first_shown.col > state->input_display_column) 2131 state->input_first_shown.col = state->input_display_column; 2132 if (state->input_first_shown.col + state->edit_width - 4 2133 < state->input_display_column) 2134 { 2135 int index_first; 2136 int fsc = state->input_first_shown.col; 2137 index_first 2138 = buffer_length(state->input_buffer[state->coord.row] 2139 .text, 2140 fsc); 2141 while (fsc + state->edit_width < state->input_display_column + 4) 2142 { 2143 index_first++; 2144 fsc = display_length(state->input_buffer[state->coord.row] 2145 .text, 2146 index_first); 2147 } 2148 state->input_first_shown.col = fsc; 2149 } 2150 } 2151 2152 static int 2153 prev_match(struct DrawState* state) 2154 { 2155 struct PoEntry* current = NULL; 2156 ssize_t e; 2157 2158 assert(state != NULL); 2159 if (state->msgid_count == 0 || !state->search || !*state->search) 2160 return 1; 2161 for (e = state->msgid_number - 1; e > 0; e--) 2162 { 2163 current = state->entries + e - 1; 2164 if ((current->msgid && *current->msgid 2165 && u32_strstr(current->msgid, state->search)) 2166 || (current->msgstr && *current->msgstr 2167 && **current->msgstr 2168 && u32_strstr(*current->msgstr, state->search))) 2169 { 2170 goto_msgid(state, e); 2171 return 0; 2172 } 2173 } 2174 return 0; 2175 } 2176 2177 static size_t 2178 prev_msgid(struct DrawState* state, const int delta) 2179 { 2180 ssize_t ret = state->msgid_number; 2181 ssize_t start = ret; 2182 2183 assert((state != NULL) && (state->entries != NULL)); 2184 if (start < delta) 2185 return state->real_msgid_count ? 1 : 0; 2186 while (ret > 1) 2187 { 2188 ret--; 2189 if (!state->entries[ret - 1].obsolete && ret <= start - delta) 2190 break; 2191 } 2192 return ret; 2193 } 2194 2195 static int 2196 prev_plural(struct DrawState* state) 2197 { 2198 char* temp = NULL; 2199 assert(state != NULL); 2200 if (state->edit_info_focused) 2201 { 2202 state->error = ERR_DLG_FOCUS_EDIT; 2203 return 1; 2204 } 2205 if (state->edit_dirty) 2206 { 2207 state->in_prompt = 1; 2208 MEMCCPY(state->prompt, prompt_edit_dirty, LINE_DEFAULT, temp); 2209 state->prompt_callback = prev_plural_callback; 2210 return 0; 2211 } 2212 if (state->msgstr_index > 0) 2213 state->msgstr_index--; 2214 show_edit(state); 2215 return 0; 2216 } 2217 2218 static int 2219 prev_plural_callback(struct DrawState* state, struct tb_event* ev) 2220 { 2221 assert((state != NULL) && (ev != NULL)); 2222 if (strchr("Yy", ev->ch)) 2223 { 2224 state->reshow_edit = 1; 2225 save_msgstr(state); 2226 } 2227 else 2228 state->edit_dirty = 0; 2229 return prev_plural(state); 2230 } 2231 2232 int 2233 print_error(const int code, const char* msg, ...) 2234 { 2235 char* buf = NULL; 2236 va_list args; 2237 long bufsize; 2238 assert((msg != NULL) && (limits != NULL)); 2239 bufsize = limits[L_LINE_MAX] ? limits[L_LINE_MAX] 2240 : PRINT_BUF_MAX_FALLBACK; 2241 buf = malloc(bufsize); 2242 if (!buf) 2243 return ERR_CANT_ALLOC; 2244 va_start(args, msg); 2245 if (vsnprintf(buf, bufsize, msg, args) >= bufsize) 2246 buf[bufsize - 1] = 0; 2247 va_end(args); 2248 fprintf(stderr, "%s: %s\n", PROGRAM_NAME, buf); 2249 free(buf); 2250 return code; 2251 } 2252 2253 static int 2254 quit_callback(struct DrawState* state, struct tb_event* ev) 2255 { 2256 int result = 0; 2257 assert((state != NULL) && (ev != NULL)); 2258 if (strchr("Yy", ev->ch)) 2259 { 2260 state->reshow_edit = 0; 2261 if (state->edit_dirty) 2262 save_msgstr(state); 2263 result = write_file(state); 2264 } 2265 state->prompt_callback = NULL; 2266 state->running = 0; 2267 return result; 2268 } 2269 2270 int 2271 redraw_status(struct DrawState* state) 2272 { 2273 int result = 0; 2274 assert(state != NULL); 2275 if (state->recalculate_size) 2276 { 2277 recalculate_size(state); 2278 state->recalculate_size = 0; 2279 } 2280 result = draw_status(state); 2281 tb_present(); 2282 return result; 2283 } 2284 2285 static int 2286 save_msgstr(struct DrawState* state) 2287 { 2288 struct BufferLine* pinput_buffer; 2289 struct PoEntry* current = NULL; 2290 uint32_t* to_save = NULL; 2291 uint32_t* temp = NULL; 2292 uint32_t* temp2 = NULL; 2293 size_t to_save_size = 0; 2294 ssize_t i; 2295 2296 assert(state != NULL); 2297 if (state->reshow_edit) 2298 state->reshow_edit = 0; 2299 else 2300 state->show_edit = 0; 2301 state->edit_info_focused = 0; 2302 2303 if (state->input_buffer) 2304 { 2305 to_save_size = state->input_rows_count * MAXMSGLINE; 2306 to_save = calloc(to_save_size, sizeof(uint32_t)); 2307 if (!to_save) 2308 { 2309 state->error = ERR_CANT_ALLOC; 2310 return 1; 2311 } 2312 pinput_buffer = NULL; 2313 for (i = 0; i < state->input_rows_count; i++) 2314 { 2315 pinput_buffer = &state->input_buffer[i]; 2316 if (i == 0) 2317 U32_MEMCCPY(to_save, pinput_buffer->text, 2318 to_save_size * sizeof(uint32_t), temp); 2319 else if (temp) 2320 { 2321 U32_U8_MEMCCPY((temp - 1), "\\n", 2322 to_save_size * sizeof(uint32_t), temp2); 2323 if (temp2) 2324 U32_MEMCCPY((temp2 - 1), 2325 pinput_buffer->text, 2326 to_save_size * sizeof(uint32_t), 2327 temp); 2328 } 2329 free_bufferline(pinput_buffer); 2330 } 2331 } 2332 free(state->input_buffer); 2333 state->input_buffer = NULL; 2334 state->input_rows_count = 0; 2335 assert(state->info_buffer != NULL); 2336 for (i = 0; i < state->info_rows_count; i++) 2337 free_bufferline(&state->info_buffer[i]); 2338 free(state->info_buffer); 2339 state->dirty = 1; 2340 state->edit_dirty = 0; 2341 state->info_rows_count = 0; 2342 state->info_buffer = NULL; 2343 if (u32_encode_tabs(state, to_save, to_save_size * sizeof(uint32_t)) < 0) 2344 goto save_msgstr_error; 2345 current = &state->entries[state->msgid_number - 1]; 2346 if (u32_match_msgid_ending(state, current->msgid, to_save, 2347 to_save_size * sizeof(uint32_t)) 2348 < 0) 2349 goto save_msgstr_error; 2350 if (*current->msgstr[state->msgstr_index] && (!to_save || !*to_save)) 2351 state->untranslated_count++; 2352 else if (!*current->msgstr[state->msgstr_index] && to_save && *to_save) 2353 state->untranslated_count--; 2354 2355 u32_set_msgstr(state, current, to_save, state->msgstr_index, 0); 2356 update_warning(current->msgid, to_save, ¤t->warning); 2357 state->input_changed_fuzzy = UNCHANGED; 2358 2359 save_msgstr_error: 2360 if (state->error == ERR_CANT_ALLOC) 2361 goto save_msgstr_alloc_error; 2362 2363 tb_hide_cursor(); 2364 free(to_save); 2365 return !!state->error; 2366 2367 save_msgstr_alloc_error: 2368 return 1; 2369 } 2370 2371 static int 2372 search_position_to(struct DrawState* state, const int column) 2373 { 2374 ssize_t len; 2375 2376 assert((state != NULL) && (state->search != NULL)); 2377 len = u32_strlen(state->search); 2378 state->search_column = column; 2379 2380 if (state->search_column > len) 2381 state->search_column = len; 2382 state->search_display_column 2383 = display_length(state->search, state->search_column); 2384 if (state->search_first_shown_column > state->search_display_column) 2385 state->search_first_shown_column = state->search_display_column; 2386 if (state->search_first_shown_column + (state->search_width - 4) 2387 < state->search_column) 2388 state->search_first_shown_column = state->search_display_column 2389 - (state->search_width - 4); 2390 return 0; 2391 } 2392 2393 static int 2394 show_edit(struct DrawState* state) 2395 { 2396 assert((state != NULL) && (state->entries != NULL)); 2397 if (state->msgstr_index + 1 2398 > state->entries[state->msgid_number - 1].msgstr_count) 2399 state->msgstr_index = 0; 2400 2401 if (load_msgstr(state) || load_info(state)) 2402 { 2403 state->running = 0; 2404 return 1; 2405 } 2406 2407 state->fuzzy_before_edit = state->fuzzy_count; 2408 state->input_changed_fuzzy = UNCHANGED; 2409 state->show_edit = 1; 2410 state->coord.row = 0; 2411 init_coord(&state->input_first_shown, 0, 0); 2412 init_coord(&state->info_first_shown, 0, 0); 2413 move_start(state); 2414 state->error = ERR_NONE; 2415 return 0; 2416 } 2417 2418 static int 2419 show_help(struct DrawState* state) 2420 { 2421 assert(state != NULL); 2422 state->show_help = 1; 2423 state->error = ERR_NONE; 2424 return 0; 2425 } 2426 2427 static int 2428 show_search(struct DrawState* state) 2429 { 2430 assert(state != NULL); 2431 state->show_search = 1; 2432 state->search_column = 0; 2433 state->search_display_column = 0; 2434 state->search_first_shown_column = 0; 2435 if (!state->search) 2436 state->search = calloc(MAXSEARCH, sizeof(uint32_t)); 2437 *state->search = 0; 2438 tb_set_cursor((state->maxx - state->search_width + 2) / 2, 2439 state->maxy - 1 - SEARCH_HEIGHT + 1); 2440 return 0; 2441 } 2442 2443 static int 2444 toggle_fuzzy(struct DrawState* state) 2445 { 2446 assert((state != NULL) && (state->entries != NULL)); 2447 if (state->msgid_number == 1) 2448 { 2449 state->error = ERR_ILLEGAL_ON_FIRST; 2450 return 0; 2451 } 2452 if (state->msgid_count > 0) 2453 { 2454 state->entries[state->msgid_number - 1].flags ^= FL_FUZZY; 2455 if (state->entries[state->msgid_number - 1].flags & FL_FUZZY) 2456 { 2457 state->fuzzy_count++; 2458 if (state->input_changed_fuzzy == CLEARED) 2459 state->input_changed_fuzzy = UNCHANGED; 2460 else 2461 state->input_changed_fuzzy = SET; 2462 } 2463 else 2464 { 2465 state->fuzzy_count--; 2466 if (state->input_changed_fuzzy == SET) 2467 state->input_changed_fuzzy = UNCHANGED; 2468 else 2469 state->input_changed_fuzzy = CLEARED; 2470 } 2471 state->edit_dirty = 1; 2472 } 2473 return 0; 2474 } 2475 2476 static int 2477 toggle_info_focused(struct DrawState* state) 2478 { 2479 assert(state != NULL); 2480 state->edit_info_focused = !state->edit_info_focused; 2481 return 0; 2482 } 2483 2484 static int 2485 yank_input_to_paste_buffer(struct DrawState* state) 2486 { 2487 struct BufferLine* ppaste = NULL; 2488 struct BufferLine* newbuf = NULL; 2489 struct BufferLine* pinput = NULL; 2490 uint32_t* newtext = NULL; 2491 uint32_t* temp = NULL; 2492 ssize_t lines_to_init = 0; 2493 ssize_t lines_to_free = 0; 2494 ssize_t i; 2495 2496 assert(state != NULL); 2497 if (state->edit_info_focused) 2498 return 1; 2499 2500 if (state->msgid_number == 1) 2501 { 2502 state->error = ERR_ILLEGAL_ON_FIRST; 2503 return 0; 2504 } 2505 2506 if (!state->input_buffer || !(*state->input_buffer).text 2507 || !*(*state->input_buffer).text) 2508 { 2509 for (i = 0; i < state->paste_count; i++) 2510 free_bufferline(state->paste + i); 2511 free(state->paste); 2512 state->paste = NULL; 2513 state->paste_count = 0; 2514 return 0; 2515 } 2516 2517 lines_to_init = state->paste_count < state->input_rows_count 2518 ? state->input_rows_count - state->paste_count 2519 : 0; 2520 lines_to_free = state->paste_count > state->input_rows_count 2521 ? state->paste_count - state->input_rows_count 2522 : 0; 2523 2524 for (i = 0; i < lines_to_free; i++) 2525 free_bufferline(state->paste + state->paste_count - i - 1); 2526 if (!state->paste) 2527 newbuf = calloc(state->input_rows_count, 2528 sizeof(struct BufferLine)); 2529 else 2530 newbuf = realloc(state->paste, 2531 state->input_rows_count * sizeof(struct BufferLine)); 2532 if (!newbuf) 2533 goto yank_input_alloc_error; 2534 2535 pinput = state->input_buffer; 2536 for (i = 0; i < lines_to_init; i++) 2537 { 2538 ppaste = newbuf + state->input_rows_count - 1 - i; 2539 init_bufferline(ppaste); 2540 newtext = calloc(LINE_DEFAULT, sizeof(uint32_t)); 2541 if (!newtext) 2542 goto yank_input_alloc_error; 2543 ppaste->text = newtext; 2544 ppaste->size = LINE_DEFAULT; 2545 } 2546 2547 if (pinput) 2548 for (i = 0; i < state->input_rows_count; i++) 2549 { 2550 ppaste = &newbuf[i]; 2551 U32_SAFE_COPY(ppaste->text, temp, ppaste->size, 2552 pinput->text, pinput->size, 2553 yank_input_alloc_error); 2554 ppaste->length = pinput->length; 2555 pinput++; 2556 } 2557 2558 state->paste_count = state->input_rows_count; 2559 state->paste = newbuf; 2560 return 0; 2561 2562 yank_input_alloc_error: 2563 state->error = ERR_CANT_ALLOC; 2564 return 1; 2565 } 2566 2567 static int 2568 write_file(struct DrawState* state) 2569 { 2570 int result; 2571 assert(state != NULL); 2572 result = save_file(state); 2573 if (result) 2574 { 2575 state->running = 0; 2576 return result; 2577 } 2578 state->error = ERR_NONE; 2579 state->edit_dirty = 0; 2580 state->dirty = 0; 2581 return 0; 2582 } 2583 2584 int 2585 main(int argc, char** argv) 2586 { 2587 struct DrawState state; 2588 struct tb_event ev; 2589 struct stat st; 2590 char prompt[LINE_DEFAULT]; 2591 char* dir = NULL; 2592 char* filename = NULL; 2593 char* pdir = NULL; 2594 char* rfn = NULL; 2595 const char* saved_tb_error = NULL; 2596 char* temp = NULL; 2597 char* wd = NULL; 2598 ssize_t filename_size = 0; 2599 long col = 1; 2600 long lineno = 1; 2601 int dir_access; 2602 int dir_access_errno; 2603 int errornum; 2604 int optind = 1; 2605 int result; 2606 int saved_errno = 0; 2607 int tbires; 2608 2609 *prompt = 0; 2610 2611 #ifdef __OpenBSD__ 2612 if (pledge("stdio tty unveil rpath wpath cpath fattr", NULL) < 0) 2613 { 2614 perror(PROGRAM_NAME ": pledge"); 2615 exit(errno); 2616 } 2617 #endif 2618 2619 /* Necessary in order for termbox to function properly 2620 * See: https://github.com/termbox/termbox2/issues/73 2621 */ 2622 setlocale(LC_ALL, ""); 2623 2624 main_options_loop: 2625 if (optind >= argc) 2626 return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG, 2627 errors[ERR_NO_CL_ARG]); 2628 2629 if (!strcmp(argv[optind], "-h") || !strcmp(argv[optind], "--help")) 2630 { 2631 printf("Usage:\t%s -h | --help | -V | --full-version | -v |" 2632 " --version\n" 2633 "\t%s [-B | --no-backup] [-b | --backup]" 2634 " [-L | --dont-break-literals]" 2635 " [-W | --no-wrap] [-w <cols> | --wrap-width=<cols>]" 2636 " filename.po\n", 2637 PROGRAM_NAME, PROGRAM_NAME); 2638 return 0; 2639 } 2640 else if (!strcmp(argv[optind], "-v") 2641 || !strcmp(argv[optind], "--version")) 2642 { 2643 printf("%s %s, committed on %s\n", PROGRAM_NAME, VERSION, DATE); 2644 return 0; 2645 } 2646 else if (!strcmp(argv[optind], "-V") 2647 || !strcmp(argv[optind], "--full-version")) 2648 { 2649 printf("%s %s, committed on %s\n", PROGRAM_NAME, VERSION, DATE); 2650 puts(COPYRIGHT); 2651 printf("Features set in config.h:\n"); 2652 printf(" backup_fail:\t\t\t%s\n", 2653 backup_fail ? "set" : "unset"); 2654 if (backup_suffix) 2655 printf(" backup_suffix:\t\t\"%s\"\n", backup_suffix); 2656 else 2657 printf(" backup_suffix:\t\tunset\n"); 2658 printf(" break_literals:\t\t%s\n", 2659 break_literals ? "set" : "unset"); 2660 printf(" create_backups:\t\t%s\n", 2661 create_backups ? "set" : "unset"); 2662 printf(" literal_break_threshold:\t%d\n", 2663 literal_break_threshold); 2664 printf(" match_msgstr_ending:\t\t%s\n", 2665 match_msgstr_ending ? "set" : "unset"); 2666 if (plural_string) 2667 printf(" plural_string:\t\t\"%s\"\n", plural_string); 2668 else 2669 printf(" plural_string:\t\tunset\n"); 2670 printf(" warn_count_dots:\t\t%s\n", 2671 warn_count_dots ? "set" : "unset"); 2672 printf(" wrap_width:\t\t\t%d\n", wrap_width); 2673 return 0; 2674 } 2675 else if (!strcmp(argv[optind], "-B") 2676 || !strcmp(argv[optind], "--no-backup")) 2677 { 2678 create_backups = 0; 2679 optind++; 2680 goto main_options_loop; 2681 } 2682 else if (!strcmp(argv[optind], "-b") 2683 || !strcmp(argv[optind], "--backup")) 2684 { 2685 create_backups = 1; 2686 optind++; 2687 goto main_options_loop; 2688 } 2689 else if (!strcmp(argv[optind], "-L") 2690 || !strcmp(argv[optind], "--dont-break-literals")) 2691 { 2692 break_literals = 0; 2693 optind++; 2694 goto main_options_loop; 2695 } 2696 else if (!strcmp(argv[optind], "-W") 2697 || !strcmp(argv[optind], "--no-wrap")) 2698 { 2699 wrap_width = 0; 2700 optind++; 2701 goto main_options_loop; 2702 } 2703 else if (!strcmp(argv[optind], "-w") 2704 || !strcmp(argv[optind], "--wrap-width=")) 2705 { 2706 optind++; 2707 wrap_width = strtonumber(argv[optind], 0, INT_MAX, &errornum); 2708 if (errornum != STN_NONE) 2709 { 2710 if (*argv[optind] == '-') 2711 state.error = ERR_NO_CL_ARG_PARAM; 2712 else 2713 state.error = ERR_BAD_CL_ARG; 2714 goto main_done; 2715 } 2716 if (wrap_width < 0) 2717 wrap_width = 0; 2718 optind++; 2719 goto main_options_loop; 2720 } 2721 else if (starts_with(argv[optind], "--") 2722 || starts_with(argv[optind], "-")) 2723 { 2724 state.error = ERR_BAD_CL_ARG; 2725 goto main_done; 2726 } 2727 2728 /* 2729 * Chicken and egg problem: to get fpathconf(dirfd, _PC_PATH_MAX) 2730 * we need a dir, which needs to be allocated, and to allocate it we 2731 * need to know the value returned by fpathconf(dirfd, _PC_PATH_MAX)... 2732 */ 2733 /* argv[optind] is the filename; BTW, "filename" is a misnomer */ 2734 filename_size = strlen(argv[optind]) + 1; 2735 if (!(dir = malloc(filename_size))) 2736 { 2737 perror(PROGRAM_NAME ": malloc"); 2738 exit(errno); 2739 } 2740 2741 (void)dirname(dir, argv[optind], filename_size); 2742 if (init_limits(dir) < 0) 2743 { 2744 perror(PROGRAM_NAME ": init_limits"); 2745 free(dir); 2746 exit(errno); 2747 } 2748 2749 filename = calloc(limits[L_PATH_MAX], 1); 2750 if (!filename) 2751 { 2752 perror(PROGRAM_NAME ": calloc"); 2753 exit(errno); 2754 } 2755 2756 *filename = 0; 2757 MEMCCPY(filename, argv[optind], limits[L_PATH_MAX], temp); 2758 if (!*filename) 2759 { 2760 free(filename); 2761 free(dir); 2762 return print_error(CUSTOM_ERROR_START + ERR_NO_CL_ARG, 2763 errors[ERR_NO_CL_ARG]); 2764 } 2765 2766 init_drawstate(&state, filename, prompt); 2767 2768 #ifdef __OpenBSD__ 2769 if (unveil(state.filename, "rwc") < 0) 2770 { 2771 perror(PROGRAM_NAME ": unveil"); 2772 goto main_done; 2773 } 2774 if (unveil(dir, "rwc") < 0) 2775 { 2776 perror(PROGRAM_NAME ": unveil"); 2777 goto main_done; 2778 } 2779 #endif 2780 2781 if (!create_backups) 2782 goto main_skip_backups; 2783 2784 if (!(wd = malloc(limits[L_PATH_MAX]))) 2785 { 2786 perror(PROGRAM_NAME ": malloc"); 2787 goto main_done; 2788 } 2789 if (!getcwd(wd, limits[L_PATH_MAX])) 2790 { 2791 perror(PROGRAM_NAME ": getcwd"); 2792 goto main_done; 2793 } 2794 #ifdef __OpenBSD__ 2795 if (unveil(dir, "rwc") < 0) 2796 { 2797 perror(PROGRAM_NAME ": unveil"); 2798 goto main_done; 2799 } 2800 #endif 2801 2802 errno = 0; 2803 if (lstat(state.filename, &st) != -1) 2804 { 2805 if (S_ISLNK(st.st_mode)) 2806 { 2807 if (!(rfn = malloc(limits[L_PATH_MAX]))) 2808 { 2809 perror(PROGRAM_NAME ": malloc"); 2810 goto main_done; 2811 } 2812 2813 if (!(state.real_filename = malloc(limits[L_PATH_MAX]))) 2814 { 2815 perror(PROGRAM_NAME ": malloc"); 2816 goto main_done; 2817 } 2818 2819 state.rfn_len = readlink(state.filename, rfn, 2820 limits[L_PATH_MAX]); 2821 if (state.rfn_len == -1) 2822 goto main_done; 2823 2824 rfn[state.rfn_len] = 0; 2825 2826 if (*rfn == '/') /* rfn is absolute */ 2827 { 2828 MEMCCPY(state.real_filename, rfn, 2829 limits[L_PATH_MAX], temp); 2830 state.rfn_len = strlen(rfn); 2831 } 2832 else /* rfn is relative */ 2833 { 2834 size_t rfn_dir_len; 2835 2836 state.orig_real_filename 2837 = strndup(rfn, state.rfn_len); 2838 if (!state.orig_real_filename) 2839 { 2840 perror(PROGRAM_NAME ": strndup"); 2841 goto main_done; 2842 } 2843 2844 MEMCCPY(state.real_filename, dir, 2845 limits[L_PATH_MAX], temp); 2846 if (!temp) 2847 { 2848 errno = ENAMETOOLONG; 2849 goto main_done; 2850 } 2851 *(temp - 1) = '/'; /* overwrite NUL */ 2852 rfn_dir_len = temp - state.real_filename - 1; 2853 MEMCCPY_EXT(temp, state.real_filename, rfn, 2854 limits[L_PATH_MAX] - rfn_dir_len, temp); 2855 if (temp) 2856 state.rfn_len = temp 2857 - state.real_filename - 1; 2858 } 2859 free(rfn); 2860 rfn = NULL; 2861 #ifdef __OpenBSD__ 2862 if (unveil(state.real_filename, "rwc") < 0) 2863 { 2864 perror(PROGRAM_NAME ": unveil"); 2865 goto main_done; 2866 } 2867 #endif 2868 if (stat(state.real_filename, &st) == -1) 2869 goto main_done; 2870 } 2871 } 2872 else 2873 goto main_done; 2874 2875 if (!(state.backup_filename = malloc(limits[L_PATH_MAX]))) 2876 { 2877 perror(PROGRAM_NAME ": malloc"); 2878 goto main_done; 2879 } 2880 2881 /* Backup filename is the target file + suffix if the file to 2882 * write is a symlink, otherwise file + suffix */ 2883 MEMCCPY(state.backup_filename, 2884 state.rfn_len > 0 ? state.real_filename : state.filename, 2885 limits[L_PATH_MAX], temp); 2886 if (!temp) /* filename is full, turning off backups */ 2887 state.backup_possible = 0; 2888 else 2889 MEMCCPY_EXT((temp - 1), state.backup_filename, backup_suffix, 2890 limits[L_PATH_MAX] + temp - state.backup_filename, 2891 temp); 2892 #ifdef __OpenBSD__ 2893 if (unveil(state.backup_filename, "rwc") < 0) 2894 { 2895 perror(PROGRAM_NAME ": unveil"); 2896 goto main_done; 2897 } 2898 #endif 2899 2900 main_skip_backups: 2901 if (state.rfn_len > 0) 2902 { 2903 if (!(pdir = realloc(dir, limits[L_PATH_MAX]))) 2904 { 2905 perror(PROGRAM_NAME ": realloc"); 2906 goto main_done; 2907 } 2908 dir = pdir; 2909 *dir = 0; 2910 (void)dirname(dir, state.real_filename, limits[L_PATH_MAX]); 2911 #ifdef __OpenBSD__ 2912 if (unveil(dir, "rwc") < 0) 2913 { 2914 perror(PROGRAM_NAME ": unveil"); 2915 goto main_done; 2916 } 2917 #endif 2918 } 2919 2920 #ifdef __OpenBSD__ 2921 if (unveil_terminfo_path() < 0) 2922 { 2923 perror(PROGRAM_NAME ": unveil"); 2924 goto main_done; 2925 } 2926 /* Have to explicitly unveil /dev/tty before tb_init despite pledge(tty) 2927 * in OpenBSD 7.9+ 2928 */ 2929 if (unveil("/dev/tty", "rw")) 2930 { 2931 perror(PROGRAM_NAME ": unveil"); 2932 goto main_done; 2933 } 2934 if (unveil(NULL, NULL) < 0) 2935 { 2936 perror(PROGRAM_NAME ": unveil"); 2937 goto main_done; 2938 } 2939 #endif 2940 2941 dir_access = access(dir, W_OK | X_OK); 2942 dir_access_errno = errno; 2943 2944 if (!backup_fail && state.backup_possible) 2945 state.backup_possible = dir_access != -1; 2946 2947 result = stat(state.filename, &st); 2948 if (result == -1) 2949 { 2950 if (dir_access == -1) 2951 { 2952 errno = dir_access_errno; 2953 goto main_done; 2954 } 2955 goto init_termbox; 2956 } 2957 2958 errno = 0; 2959 2960 if (!S_ISREG(st.st_mode)) 2961 { 2962 state.error = ERR_NOTREG_FILE; 2963 goto main_done; 2964 } 2965 2966 if (access(state.filename, F_OK | R_OK | W_OK) == -1) 2967 goto main_done; 2968 2969 if (state.rfn_len > 0 2970 && access(state.real_filename, F_OK | R_OK | W_OK) == -1) 2971 goto main_done; 2972 2973 if (load_file(&state, &lineno, &col)) 2974 goto main_done; 2975 2976 state.msgid_number = state.msgid_count > 0 ? 1 : 0; 2977 state.first_shown_msgid = state.msgid_number; 2978 2979 init_termbox: 2980 if ((tbires = tb_init())) 2981 { 2982 saved_tb_error = tb_strerror(tbires); 2983 errno = ERR_TB_ERROR; 2984 goto main_done; 2985 } 2986 2987 errno = 0; 2988 state.maxx = 0; 2989 state.recalculate_size = 1; 2990 2991 while (state.running) 2992 { 2993 tb_set_clear_attrs(colors[C_CLEAR].fg, colors[C_CLEAR].bg); 2994 tb_clear(); 2995 2996 if (state.recalculate_size) 2997 { 2998 recalculate_size(&state); 2999 state.recalculate_size = 0; 3000 } 3001 if (draw(&state)) 3002 { 3003 state.running = 0; 3004 goto main_done; 3005 } 3006 tb_present(); 3007 result = tb_poll_event(&ev); 3008 if (result == TB_OK && ev.type == TB_EVENT_RESIZE) 3009 state.recalculate_size = 1; 3010 else if (result == TB_OK && ev.type == TB_EVENT_KEY) 3011 handle_key_event(&ev, &state); 3012 } 3013 3014 main_done: 3015 saved_errno = errno; 3016 3017 if (saved_tb_error) 3018 { 3019 tb_shutdown(1); 3020 print_error(1, saved_tb_error); 3021 exit(CUSTOM_ERROR_START + saved_errno); 3022 } 3023 3024 tb_shutdown(saved_errno != ENOMEM); 3025 3026 if (saved_errno) 3027 perror(PROGRAM_NAME); 3028 3029 switch (state.error) 3030 { 3031 case ERR_NOTREG_FILE: 3032 print_error(1, errors[state.error], state.filename); 3033 break; 3034 case ERR_NO_CL_ARG_PARAM: 3035 print_error(1, errors[state.error], argv[optind - 1]); 3036 break; 3037 case ERR_BAD_CL_ARG: 3038 print_error(1, errors[state.error], argv[optind]); 3039 break; 3040 case ERR_SYNTAX: 3041 print_error(1, errors[state.error], state.filename, lineno, col); 3042 break; 3043 case ERR_NONE: 3044 break; 3045 default: 3046 print_error(1, errors[state.error]); 3047 } 3048 3049 return saved_errno 3050 ? saved_errno 3051 : (state.error ? CUSTOM_ERROR_START + state.error : 0); 3052 }