poe.c (59311B)
1 /* This program is licensed under the terms of GNU GPL v3 or (at your option) 2 * any later version. Copyright (C) 2021-2024 Страхиња Радић. 3 * See the file LICENSE for exact copyright and license details. */ 4 5 #include <errno.h> 6 #include <fcntl.h> 7 #include <stdarg.h> 8 #include <stdint.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <sys/stat.h> 13 #include <unistd.h> 14 15 #define TB_OPT_V1_COMPAT 16 #define TB_IMPL 17 18 #define COPYRIGHT \ 19 (" This program is licensed under the terms of GNU GPL v3" \ 20 " or (at your option)\n" \ 21 " any later version. Copyright (C) 2021-2024 Strahinya Radich.\n" \ 22 " See the file LICENSE for exact copyright and license " \ 23 "details.\n" \ 24 " File termbox.h is based on termbox2 TUI library, (c) 2021" \ 25 " termbox developers,\n" \ 26 " which is released under the terms of Expat license, see the file" \ 27 " LICENSE.termbox.\n") 28 #define POE_C_GLOBALS 29 30 #include "version.h" 31 #include "util.h" 32 #include "po.h" 33 #include "draw.h" 34 #include "config.h" 35 36 extern const char* backup_suffix; 37 extern const char* errors[]; 38 extern const char* prompt_dirty; 39 extern const char* prompt_cancel_dirty; 40 extern const char* prompt_overwrite; 41 42 void cancel_callback(struct DrawState* state, struct tb_event* ev); 43 char* dirname(char* path); 44 void goto_msgid(struct DrawState* state, const size_t msgid_number); 45 void handle_key_event(struct tb_event* ev, struct DrawState* state); 46 int insert_char(struct DrawState* state, uint32_t ch); 47 void insert_search_char(struct DrawState* state, uint32_t ch); 48 struct DrawState* join_lines(struct DrawState* state, size_t first_line, 49 int forward); 50 struct BufferLine* load_info(struct DrawState* state); 51 struct BufferLine* load_msgstr(struct DrawState* state); 52 size_t next_msgid(struct DrawState* state, const int delta); 53 void overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev); 54 void overwrite_paste_callback(struct DrawState* state, struct tb_event* ev); 55 size_t prev_msgid(struct DrawState* state, const int delta); 56 int print_error(const int code, const char* msg, ...); 57 void quit_callback(struct DrawState* state, struct tb_event* ev); 58 size_t real_msgid_to_display_msgid(const struct DrawState* state); 59 60 void 61 cancel_callback(struct DrawState* state, struct tb_event* ev) 62 { 63 if (ev->ch == 'y') 64 save_msgstr(state); 65 else 66 cancel_msgstr(state); 67 state->prompt_callback = NULL; 68 } 69 70 char* 71 dirname(char* path) 72 { 73 static char* result = NULL; 74 static const char* dot = "."; 75 char* p = NULL; 76 char* slash = NULL; 77 78 if (!path || !*path) 79 return NULL; 80 81 free(result); 82 result = malloc(MAXPATH); 83 memset(result, 0, MAXPATH); 84 85 STRLCPY(path, result, MAXPATH); 86 p = result + strlen(result) - 1; 87 if (*p == '/') 88 *p = 0; 89 90 slash = strrchr(result, '/'); 91 if (!slash) 92 return (char*)dot; 93 94 *slash = 0; 95 96 return result; 97 } 98 99 void 100 goto_msgid(struct DrawState* state, const size_t msgid_number) 101 { 102 size_t last_non_obsolete 103 = state->real_msgid_count - state->obsolete_at_end_count; 104 105 if (msgid_number > state->real_msgid_count) 106 return; 107 108 state->msgid_number = msgid_number; 109 110 if (state->msgid_number < state->first_shown_msgid) 111 state->first_shown_msgid = state->msgid_number; 112 else if (state->msgid_number 113 > state->first_shown_msgid + state->maxy - 2) 114 state->first_shown_msgid 115 = state->msgid_number - (state->maxy - 2); 116 else if (last_non_obsolete < state->msgid_number + state->maxy - 2) 117 state->first_shown_msgid = (last_non_obsolete > state->maxy - 2 118 ? last_non_obsolete - (state->maxy - 2) 119 : 1); 120 else 121 ; 122 } 123 124 void 125 handle_key_event(struct tb_event* ev, struct DrawState* state) 126 { 127 const struct Key* current = NULL; 128 129 *state->error = 0; 130 131 if (state->in_prompt) 132 { 133 if (ev->ch == 'y' || ev->ch == 'n') 134 { 135 if (state->prompt_callback) 136 state->prompt_callback(state, ev); 137 state->in_prompt = 0; 138 *state->prompt = 0; 139 } 140 else if (ev->key == TB_KEY_ESC) 141 cancel_close(state); 142 return; 143 } 144 145 /* Remember to also edit the following condition if you reassign motion 146 * keys */ 147 if (ev->key != TB_KEY_ARROW_DOWN && ev->key != TB_KEY_ARROW_UP 148 && state->show_edit) 149 state->saved_column = -1; 150 151 for (int key = 0; key < LEN(top_level_keys); key++) 152 { 153 current = top_level_keys + key; 154 if (((current->ev.key == ev->key) && !ev->ch) 155 || (current->ev.ch && (current->ev.ch == ev->ch))) 156 if (current->callback && !current->callback(state)) 157 return; 158 } 159 160 if (state->show_help) 161 { 162 STRLCPY(errors[ERR_DLG_OPEN_QUIT], state->error, MAXBUFLINE); 163 return; 164 } 165 else if (state->show_edit) 166 { 167 for (int key = 0; key < LEN(edit_keys); key++) 168 { 169 current = edit_keys + key; 170 if (((current->ev.key == ev->key) && !ev->ch) 171 || (current->ev.ch 172 && (current->ev.ch == ev->ch))) 173 if (current->callback 174 && !current->callback(state)) 175 return; 176 } 177 if (!state->edit_info_focused && ev->ch) 178 { 179 insert_char(state, ev->ch); 180 return; 181 } 182 } 183 else if (state->show_search) 184 { 185 for (int key = 0; key < LEN(search_keys); key++) 186 { 187 current = search_keys + key; 188 if (((current->ev.key == ev->key) && !ev->ch) 189 || (current->ev.ch 190 && (current->ev.ch == ev->ch))) 191 if (current->callback 192 && !current->callback(state)) 193 return; 194 } 195 if (ev->ch) 196 { 197 insert_search_char(state, ev->ch); 198 return; 199 } 200 } 201 else 202 { 203 for (int key = 0; key < LEN(main_keys); key++) 204 { 205 current = main_keys + key; 206 if (((current->ev.key == ev->key) && !ev->ch) 207 || (current->ev.ch 208 && (current->ev.ch == ev->ch))) 209 if (current->callback) 210 { 211 if (!current->callback(state)) 212 return; 213 else 214 break; 215 } 216 } 217 } 218 219 if (state->edit_info_focused) 220 STRLCPY(errors[ERR_DLG_FOCUS_EDIT], state->error, MAXBUFLINE); 221 else if (state->show_edit || state->show_search) 222 STRLCPY(errors[ERR_UNKNOWN_KEY], state->error, MAXBUFLINE); 223 else 224 STRLCPY(errors[ERR_UNKNOWN_KEY_HELP], state->error, MAXBUFLINE); 225 } 226 227 int 228 insert_char(struct DrawState* state, uint32_t ch) 229 { 230 if (state->edit_info_focused) 231 return 1; 232 struct BufferLine* buffer_line = &state->input_buffer[state->input_row]; 233 size_t len = buffer_line->length; 234 if (state->input_column < MAXBUFLINE - 1) 235 { 236 size_t i = len + 1; 237 int add_null = buffer_line->text[state->input_column] == 0; 238 while (i < MAXBUFLINE - 2 && i > state->input_column) 239 { 240 buffer_line->text[i] = buffer_line->text[i - 1]; 241 i--; 242 } 243 buffer_line->text[state->input_column++] = ch; 244 buffer_line->length++; 245 if (state->input_column == MAXBUFLINE) 246 state->input_column = MAXBUFLINE - 1; 247 state->input_display_column = display_length(state, 248 state->input_buffer[state->input_row].text, 249 state->input_column); 250 if (state->input_display_column - state->input_first_shown_column 251 > state->edit_width - 4) 252 { 253 state->input_first_shown_column 254 = state->input_display_column 255 - state->edit_width + 4; 256 state->input_display_column = display_length(state, 257 state->input_buffer[state->input_row].text, 258 state->input_column); 259 } 260 if (add_null) 261 buffer_line->text[state->input_column] = 0; 262 } 263 state->dirty = 1; 264 return 0; 265 } 266 267 void 268 insert_search_char(struct DrawState* state, uint32_t ch) 269 { 270 size_t len = u32_strlen(state->search); 271 if (state->search_column < MAXSEARCH - 1) 272 { 273 size_t i = len + 1; 274 int add_null = state->search[state->search_column] == 0; 275 while (i < MAXSEARCH - 2 && i > state->search_column) 276 { 277 state->search[i] = state->search[i - 1]; 278 i--; 279 } 280 state->search[state->search_column++] = ch; 281 if (state->search_column == MAXSEARCH) 282 state->search_column = MAXSEARCH - 1; 283 state->search_display_column = display_length(state, 284 state->search, state->search_column); 285 if (state->search_display_column 286 - state->search_first_shown_column 287 > state->search_width - 4) 288 { 289 state->search_first_shown_column 290 = state->search_display_column 291 - state->search_width + 4; 292 state->search_display_column = display_length(state, 293 state->search, state->search_column); 294 } 295 if (add_null) 296 state->search[state->search_column] = 0; 297 298 next_match(state); 299 } 300 } 301 302 struct DrawState* 303 join_lines(struct DrawState* state, size_t first_line, int forward) 304 { 305 state->input_column = state->input_buffer[first_line].length; 306 state->input_display_column = display_length(state, 307 state->input_buffer[state->input_row].text, 308 state->input_column); 309 u32_strncat(state->input_buffer[first_line].text, 310 state->input_buffer[first_line + 1].text, MAXBUFLINE); 311 state->input_buffer[first_line].length 312 = u32_strlen(state->input_buffer[first_line].text); 313 if (!forward) 314 { 315 state->input_row--; 316 if (state->input_row < 0) 317 state->input_row = 0; 318 } 319 for (size_t i = first_line + 1; i < state->input_rows_count - 1; i++) 320 { 321 u32_strncpy(state->input_buffer[i].text, 322 state->input_buffer[i + 1].text, MAXBUFLINE); 323 state->input_buffer[i].length 324 = state->input_buffer[i + 1].length; 325 } 326 free_bufferline(state->input_buffer + state->input_rows_count - 1); 327 state->input_rows_count--; 328 struct BufferLine* newbuf = realloc(state->input_buffer, 329 state->input_rows_count * sizeof(struct BufferLine)); 330 if (!newbuf) 331 return NULL; 332 state->input_buffer = newbuf; 333 state->dirty = 1; 334 335 return state; 336 } 337 338 struct BufferLine* 339 load_info(struct DrawState* state) 340 { 341 struct PoEntry* first = &state->entries[0]; 342 struct PoEntry* current = &state->entries[state->msgid_number - 1]; 343 size_t msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); 344 size_t msgid_plural_num_lines 345 = u32_lines_in_string(current->msgid_plural, 0, 0); 346 size_t num_lines = 0; 347 struct BufferLine* newbuf = NULL; 348 struct BufferLine* pnewbuf = NULL; 349 350 for (size_t i = 0; i < state->info_rows_count; i++) 351 free_bufferline(state->info_buffer + i); 352 free(state->info_buffer); 353 354 state->info_rows_maxlen = 0; 355 356 if (current->msgid) 357 num_lines += msgid_num_lines + 1; 358 if (current->msgid_plural) 359 { 360 num_lines += msgid_plural_num_lines + 1; 361 if (first->plural_forms) 362 num_lines += 3; 363 } 364 if (current->comments) 365 num_lines += current->comment_lines + 1; 366 if (current->trans_comments) 367 num_lines += current->trans_comment_lines + 1; 368 if (current->ref_comments) 369 num_lines += current->ref_comment_lines + 1; 370 newbuf = calloc(num_lines, sizeof(struct BufferLine)); 371 if (!newbuf) 372 return NULL; 373 374 pnewbuf = newbuf; 375 for (size_t i = 0; i < num_lines; i++) 376 { 377 init_bufferline(pnewbuf); 378 uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); 379 if (!newtext) 380 return NULL; 381 pnewbuf->text = newtext; 382 pnewbuf++; 383 } 384 385 state->info_rows_count = num_lines; 386 387 pnewbuf = newbuf; 388 size_t newlen = 0; 389 const uint32_t* pumsgid = NULL; 390 if (current->msgid) 391 { 392 pumsgid = current->msgid; 393 newlen = u32_u8_strncpy(pnewbuf->text, "msgid:", MAXBUFLINE); 394 pnewbuf->length = newlen; 395 if (newlen > state->info_rows_maxlen) 396 state->info_rows_maxlen = newlen; 397 pnewbuf->fg = EDIT_MSGID_HEADING_FG; 398 pnewbuf->bg = EDIT_MSGID_HEADING_BG; 399 pnewbuf++; 400 while (*pumsgid) 401 { 402 size_t len = 0; 403 const uint32_t* old_pumsgid = pumsgid; 404 405 len = u32_next_line(current->msgid, &pumsgid, 0, 0); 406 u32_strncpy(pnewbuf->text, old_pumsgid, 407 MIN(len + 1, MAXBUFLINE)); 408 newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE); 409 pnewbuf->length = newlen; 410 if (newlen > state->info_rows_maxlen) 411 state->info_rows_maxlen = newlen; 412 pnewbuf++; 413 } 414 } 415 if (current->msgid_plural) 416 { 417 newlen = u32_u8_strncpy(pnewbuf->text, 418 "msgid_plural:", MAXBUFLINE); 419 pnewbuf->length = newlen; 420 if (newlen > state->info_rows_maxlen) 421 state->info_rows_maxlen = newlen; 422 pnewbuf->fg = EDIT_MSGID_HEADING_FG; 423 pnewbuf->bg = EDIT_MSGID_HEADING_BG; 424 pnewbuf++; 425 pumsgid = current->msgid_plural; 426 while (*pumsgid) 427 { 428 size_t len = 0; 429 const uint32_t* old_pumsgid = pumsgid; 430 431 len = u32_next_line(current->msgid_plural, &pumsgid, 0, 432 0); 433 u32_strncpy(pnewbuf->text, old_pumsgid, 434 MIN(len + 1, MAXBUFLINE)); 435 newlen = u32_decode_tabs(pnewbuf->text, MAXBUFLINE); 436 pnewbuf->length = newlen; 437 if (newlen > state->info_rows_maxlen) 438 state->info_rows_maxlen = newlen; 439 pnewbuf++; 440 } 441 if (first->plural_forms) 442 { 443 newlen = u32_u8_strncpy(pnewbuf->text, 444 "Plural forms:", MAXBUFLINE); 445 pnewbuf->length = newlen; 446 if (newlen > state->info_rows_maxlen) 447 state->info_rows_maxlen = newlen; 448 pnewbuf->fg = EDIT_MSGID_HEADING_FG; 449 pnewbuf->bg = EDIT_MSGID_HEADING_BG; 450 pnewbuf++; 451 newlen = u32_strncpy(pnewbuf->text, first->plural_forms, 452 MAXBUFLINE); 453 pnewbuf->length = newlen; 454 if (newlen > state->info_rows_maxlen) 455 state->info_rows_maxlen = newlen; 456 pnewbuf++; 457 } 458 } 459 if (current->comments) 460 { 461 newlen = u32_u8_strncpy(pnewbuf->text, "Comments:", MAXBUFLINE); 462 pnewbuf->length = newlen; 463 if (newlen > state->info_rows_maxlen) 464 state->info_rows_maxlen = newlen; 465 pnewbuf->fg = EDIT_MSGID_HEADING_FG; 466 pnewbuf->bg = EDIT_MSGID_HEADING_BG; 467 pnewbuf++; 468 for (size_t i = 0; i < current->comment_lines; i++) 469 { 470 uint32_t* comment = current->comments[i]; 471 size_t len = u32_strlen(comment); 472 newlen = u32_strncpy(pnewbuf->text, comment, 473 MIN(len + 1, MAXBUFLINE)); 474 pnewbuf->length = newlen; 475 if (newlen > state->info_rows_maxlen) 476 state->info_rows_maxlen = newlen; 477 pnewbuf++; 478 } 479 } 480 if (current->trans_comments) 481 { 482 newlen = u32_u8_strncpy(pnewbuf->text, 483 "Translation comments:", MAXBUFLINE); 484 pnewbuf->length = newlen; 485 if (newlen > state->info_rows_maxlen) 486 state->info_rows_maxlen = newlen; 487 pnewbuf->fg = EDIT_MSGID_HEADING_FG; 488 pnewbuf->bg = EDIT_MSGID_HEADING_BG; 489 pnewbuf++; 490 for (size_t i = 0; i < current->trans_comment_lines; i++) 491 { 492 uint32_t* trans_comment = current->trans_comments[i]; 493 size_t len = u32_strlen(trans_comment); 494 newlen = u32_strncpy(pnewbuf->text, trans_comment, 495 MIN(len + 1, MAXBUFLINE)); 496 pnewbuf->length = newlen; 497 if (newlen > state->info_rows_maxlen) 498 state->info_rows_maxlen = newlen; 499 pnewbuf++; 500 } 501 } 502 if (current->ref_comments) 503 { 504 newlen = u32_u8_strncpy(pnewbuf->text, 505 "References:", MAXBUFLINE); 506 pnewbuf->length = newlen; 507 if (newlen > state->info_rows_maxlen) 508 state->info_rows_maxlen = newlen; 509 pnewbuf->fg = EDIT_MSGID_HEADING_FG; 510 pnewbuf->bg = EDIT_MSGID_HEADING_BG; 511 pnewbuf++; 512 for (size_t i = 0; i < current->ref_comment_lines; i++) 513 { 514 char* ref_comment = current->ref_comments[i]; 515 size_t len = strlen(ref_comment); 516 newlen = u32_u8_strncpy(pnewbuf->text, ref_comment, 517 MIN(len + 1, MAXBUFLINE)); 518 pnewbuf->length = newlen; 519 if (newlen > state->info_rows_maxlen) 520 state->info_rows_maxlen = newlen; 521 pnewbuf++; 522 } 523 } 524 525 state->info_buffer = newbuf; 526 527 return state->info_buffer; 528 } 529 530 struct BufferLine* 531 load_msgstr(struct DrawState* state) 532 { 533 int msgstr_index = state->msgstr_index; 534 struct PoEntry* current = &state->entries[state->msgid_number - 1]; 535 size_t num_lines = current->msgstr_count == 0 536 ? 1 537 : u32_lines_in_string(*(current->msgstr + msgstr_index), 0, 0); 538 struct BufferLine* newbuf = NULL; 539 struct BufferLine* pnewbuf = NULL; 540 541 for (size_t i = 0; i < state->input_rows_count; i++) 542 free_bufferline(state->input_buffer + i); 543 free(state->input_buffer); 544 545 newbuf = calloc(num_lines, sizeof(struct BufferLine)); 546 if (!newbuf) 547 return NULL; 548 549 pnewbuf = newbuf; 550 for (size_t i = 0; i < num_lines; i++) 551 { 552 init_bufferline(pnewbuf); 553 uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); 554 if (!newtext) 555 return NULL; 556 pnewbuf->text = newtext; 557 pnewbuf++; 558 } 559 560 state->input_rows_count = num_lines; 561 562 if (current->msgstr_count > 0) 563 { 564 const uint32_t* pumsgstr = *(current->msgstr + msgstr_index); 565 if (pumsgstr) 566 { 567 pnewbuf = newbuf; 568 while (*pumsgstr) 569 { 570 size_t len = 0; 571 size_t newlen = 0; 572 const uint32_t* old_pumsgstr = pumsgstr; 573 len = u32_next_line(*(current->msgstr 574 + msgstr_index), 575 &pumsgstr, 0, 0); 576 u32_strncpy(pnewbuf->text, old_pumsgstr, 577 MIN(len + 1, MAXBUFLINE)); 578 newlen = u32_decode_tabs(pnewbuf->text, 579 MAXBUFLINE); 580 pnewbuf->length = newlen; 581 pnewbuf++; 582 } 583 } 584 else 585 newbuf[0].length = 0; 586 } 587 else 588 newbuf[0].length = 0; 589 590 state->input_buffer = newbuf; 591 return state->input_buffer; 592 } 593 594 size_t 595 next_msgid(struct DrawState* state, const int delta) 596 { 597 size_t ret = state->msgid_number; 598 size_t start = ret; 599 size_t last_non_obsolete 600 = state->real_msgid_count - state->obsolete_at_end_count; 601 602 while (ret < last_non_obsolete + 1) 603 { 604 ret++; 605 if (!state->entries[ret - 1].obsolete && ret >= start + delta) 606 break; 607 } 608 609 return ret == last_non_obsolete + 1 ? last_non_obsolete : ret; 610 } 611 612 void 613 overwrite_copy_msgid_callback(struct DrawState* state, struct tb_event* ev) 614 { 615 if (ev->ch == 'y') 616 copy_msgid_to_input(state); 617 state->prompt_callback = NULL; 618 } 619 620 void 621 overwrite_paste_callback(struct DrawState* state, struct tb_event* ev) 622 { 623 if (ev->ch == 'y') 624 paste_paste_buffer_to_input(state); 625 state->prompt_callback = NULL; 626 } 627 628 size_t 629 prev_msgid(struct DrawState* state, const int delta) 630 { 631 size_t ret = state->msgid_number; 632 size_t start = ret; 633 634 if (start < delta) 635 return state->real_msgid_count ? 1 : 0; 636 637 while (ret > 1) 638 { 639 ret--; 640 if (!state->entries[ret - 1].obsolete && ret <= start - delta) 641 break; 642 } 643 644 return ret; 645 } 646 647 int 648 print_error(const int code, const char* msg, ...) 649 { 650 char buf[MAXBUFLINE]; 651 va_list args; 652 va_start(args, msg); 653 vsnprintf(buf, sizeof(buf), msg, args); 654 va_end(args); 655 fprintf(stderr, "%s: %s\n", PROGRAMNAME, buf); 656 return code; 657 } 658 659 void 660 quit_callback(struct DrawState* state, struct tb_event* ev) 661 { 662 if (ev->ch == 'y') 663 write_file(state); 664 state->prompt_callback = NULL; 665 state->running = 0; 666 } 667 668 size_t 669 real_msgid_to_display_msgid(const struct DrawState* state) 670 { 671 struct PoEntry* current = state->entries; 672 size_t ret = 1; 673 674 if (!state->msgid_count) 675 return 0; 676 677 while (current != state->entries + state->msgid_number - 1) 678 { 679 if (current != state->entries + state->msgid_number - 1 680 && !current->obsolete) 681 ret++; 682 current++; 683 } 684 685 return ret; 686 } 687 688 int 689 cancel_close(struct DrawState* state) 690 { 691 *state->error = 0; 692 if (state->in_prompt) 693 { 694 state->in_prompt = 0; 695 *state->prompt = 0; 696 } 697 else if (state->show_help) 698 state->show_help = 0; 699 else if (state->show_edit) 700 cancel_msgstr(state); 701 else if (state->show_search) 702 cancel_search(state); 703 else 704 STRLCPY(errors[ERR_EXIT_KEY], state->error, MAXBUFLINE); 705 706 return 0; 707 } 708 709 int 710 cancel_msgstr(struct DrawState* state) 711 { 712 if (!state->in_prompt && state->dirty) 713 { 714 state->in_prompt = 1; 715 STRLCPY(prompt_cancel_dirty, state->prompt, MAXBUFLINE); 716 state->prompt_callback = cancel_callback; 717 return 0; 718 } 719 720 state->show_edit = 0; 721 state->edit_info_focused = 0; 722 723 for (size_t i = 0; i < state->input_rows_count; i++) 724 free_bufferline(state->input_buffer + i); 725 free(state->input_buffer); 726 state->input_buffer = NULL; 727 state->input_rows_count = 0; 728 if (state->info_buffer) 729 for (size_t i = 0; i < state->info_rows_count; i++) 730 free_bufferline(state->info_buffer + i); 731 free(state->info_buffer); 732 state->info_buffer = NULL; 733 state->info_rows_count = 0; 734 state->dirty = state->dirty_before_edit; 735 state->fuzzy_count = state->fuzzy_before_edit; 736 switch (state->input_changed_fuzzy) 737 { 738 case SET: 739 state->entries[state->msgid_number - 1].flags &= ~FL_FUZZY; 740 break; 741 case CLEARED: 742 state->entries[state->msgid_number - 1].flags |= FL_FUZZY; 743 break; 744 default:; 745 } 746 state->input_changed_fuzzy = UNCHANGED; 747 tb_hide_cursor(); 748 return 0; 749 } 750 751 int 752 cancel_search(struct DrawState* state) 753 { 754 state->show_search = 0; 755 *state->search = 0; 756 tb_hide_cursor(); 757 return 0; 758 } 759 760 int 761 copy_msgid_to_input(struct DrawState* state) 762 { 763 if (state->edit_info_focused) 764 return 0; 765 766 if (!state->in_prompt && *state->input_buffer->text) 767 { 768 state->in_prompt = 1; 769 STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE); 770 state->prompt_callback = overwrite_copy_msgid_callback; 771 return 0; 772 } 773 774 if (state->msgid_number == 1) 775 { 776 STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE); 777 return 0; 778 } 779 780 struct PoEntry* current = &state->entries[state->msgid_number - 1]; 781 size_t msgid_num_lines = u32_lines_in_string(current->msgid, 0, 0); 782 size_t lines_to_init = state->input_rows_count < msgid_num_lines 783 ? msgid_num_lines - state->input_rows_count 784 : 0; 785 size_t lines_to_free = state->input_rows_count > msgid_num_lines 786 ? state->input_rows_count - msgid_num_lines 787 : 0; 788 for (size_t i = 0; i < lines_to_free; i++) 789 free_bufferline( 790 state->input_buffer + state->input_rows_count - i - 1); 791 struct BufferLine* newbuf = realloc(state->input_buffer, 792 msgid_num_lines * sizeof(struct BufferLine)); 793 const uint32_t* pmsgid = current->msgid; 794 795 if (!newbuf) 796 return 0; 797 798 struct BufferLine* pbuffer = NULL; 799 for (size_t i = 0; i < lines_to_init; i++) 800 { 801 pbuffer = newbuf + msgid_num_lines - 1 - i; 802 init_bufferline(pbuffer); 803 uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); 804 if (!newtext) 805 return 0; 806 pbuffer->text = newtext; 807 } 808 809 pbuffer = newbuf; 810 while (pmsgid && *pmsgid) 811 { 812 size_t len = 0; 813 size_t newlen = 0; 814 const uint32_t* old_pmsgid = pmsgid; 815 len = u32_next_line(current->msgid, &pmsgid, 0, 0); 816 u32_strncpy(pbuffer->text, old_pmsgid, MIN(len + 1, MAXBUFLINE)); 817 newlen = u32_decode_tabs(pbuffer->text, MAXBUFLINE); 818 pbuffer->length = newlen; 819 pbuffer++; 820 } 821 822 state->input_rows_count = msgid_num_lines; 823 state->input_buffer = newbuf; 824 state->dirty = 1; 825 826 return 0; 827 } 828 829 int 830 edit_page_down(struct DrawState* state) 831 { 832 if (state->edit_info_focused) 833 { 834 if (state->info_first_shown_row + (EDIT_HEIGHT - 3) / 2 835 < state->info_rows_count - 1) 836 state->info_first_shown_row += (EDIT_HEIGHT - 3) / 2; 837 return 0; 838 } 839 840 if (state->dirty) 841 save_msgstr(state); 842 else /* No need to call cancel_msgstr */ 843 state->dirty = state->dirty_before_edit; 844 move_list_down(state); 845 state->msgstr_index = 0; 846 return show_edit(state); 847 } 848 849 int 850 edit_page_up(struct DrawState* state) 851 { 852 if (state->edit_info_focused) 853 { 854 if (state->info_first_shown_row > (EDIT_HEIGHT - 3) / 2) 855 state->info_first_shown_row -= (EDIT_HEIGHT - 3) / 2; 856 else 857 move_start(state); 858 return 0; 859 } 860 861 if (state->dirty) 862 save_msgstr(state); 863 else /* No need to call cancel_msgstr */ 864 state->dirty = state->dirty_before_edit; 865 move_list_up(state); 866 state->msgstr_index = 0; 867 return show_edit(state); 868 } 869 870 int 871 erase_backwards(struct DrawState* state) 872 { 873 struct BufferLine* buffer_line = &state->input_buffer[state->input_row]; 874 if (state->edit_info_focused) 875 return 1; 876 877 if (state->input_column > 0) 878 { 879 size_t i = state->input_column; 880 do 881 { 882 buffer_line->text[i - 1] = buffer_line->text[i]; 883 i++; 884 } while (buffer_line->text[i]); 885 buffer_line->text[i - 1] = 0; 886 buffer_line->length--; 887 state->input_column--; 888 if (state->input_first_shown_column > 0) 889 { 890 const struct BufferLine* current_line 891 = &state->input_buffer[state->input_row]; 892 if (display_length(state, current_line->text, 893 current_line->length) 894 >= state->edit_width - 4) 895 state->input_first_shown_column--; 896 } 897 state->input_display_column = display_length(state, 898 state->input_buffer[state->input_row].text, 899 state->input_column); 900 if (state->input_display_column 901 < state->input_first_shown_column) 902 state->input_first_shown_column 903 = state->input_display_column; 904 state->dirty = 1; 905 } 906 else if (state->input_row > 0) 907 { 908 move_up(state); 909 move_end(state); 910 erase_forward(state); 911 } 912 return 0; 913 } 914 915 int 916 erase_forward(struct DrawState* state) 917 { 918 struct BufferLine* buffer_line = &state->input_buffer[state->input_row]; 919 if (state->edit_info_focused) 920 return 1; 921 922 if (buffer_line->text[state->input_column]) 923 { 924 size_t i = state->input_column; 925 while (i < MAXBUFLINE - 2 && buffer_line->text[i + 1]) 926 { 927 buffer_line->text[i] = buffer_line->text[i + 1]; 928 i++; 929 } 930 buffer_line->text[i] = 0; 931 buffer_line->length--; 932 state->dirty = 1; 933 } 934 else if (state->input_row < state->input_rows_count - 1) 935 { 936 if (!join_lines(state, state->input_row, 1)) 937 { 938 STRLCPY(errors[ERR_CANT_ALLOC], state->error, 939 MAXBUFLINE); 940 state->running = 0; 941 } 942 } 943 return 0; 944 } 945 946 int 947 erase_prev_word(struct DrawState* state) 948 { 949 if (state->edit_info_focused) 950 return 1; 951 952 int i = state->input_column; 953 size_t len = state->input_buffer[state->input_row].length; 954 955 uint32_t* pline = state->input_buffer[state->input_row].text; 956 957 if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) 958 i--; 959 while (i > 0 && u32_is_word_boundary(pline[i], 0)) 960 i--; 961 while (i > 0 && !u32_is_word_boundary(pline[i], 0) 962 && !u32_is_word_boundary(pline[i - 1], 0)) 963 i--; 964 965 int d = state->input_column - i; 966 967 for (int j = i; j < len - d; j++) 968 pline[j] = pline[j + d]; 969 970 len -= d; 971 state->input_buffer[state->input_row].length = len; 972 pline[len] = 0; 973 state->input_column = i; 974 state->input_display_column = display_length(state, 975 state->input_buffer[state->input_row].text, 976 state->input_column); 977 if (state->input_display_column < state->input_first_shown_column) 978 state->input_first_shown_column = state->input_display_column; 979 state->dirty = 1; 980 return 0; 981 } 982 983 int 984 erase_search_backwards(struct DrawState* state) 985 { 986 if (state->search_column > 0) 987 { 988 size_t i = state->search_column; 989 do 990 { 991 state->search[i - 1] = state->search[i]; 992 i++; 993 } while (state->search[i]); 994 state->search[i - 1] = 0; 995 state->search_column--; 996 if (state->search_column < state->search_first_shown_column) 997 { 998 if (state->search_column > state->search_width - 4) 999 state->search_first_shown_column 1000 = state->search_column 1001 - state->search_width + 4; 1002 else 1003 state->search_first_shown_column = 0; 1004 } 1005 state->search_display_column = display_length(state, 1006 state->search, state->search_column); 1007 1008 return next_match(state); 1009 } 1010 return 0; 1011 } 1012 1013 int 1014 erase_search_forward(struct DrawState* state) 1015 { 1016 if (state->search[state->search_column]) 1017 { 1018 size_t i = state->search_column; 1019 while (i < MAXSEARCH - 2 && state->search[i + 1]) 1020 { 1021 state->search[i] = state->search[i + 1]; 1022 i++; 1023 } 1024 state->search[i] = 0; 1025 1026 return next_match(state); 1027 } 1028 return 0; 1029 } 1030 1031 int 1032 erase_search_prev_word(struct DrawState* state) 1033 { 1034 int i = state->search_column; 1035 size_t len = u32_strlen(state->search); 1036 1037 uint32_t* pline = state->search; 1038 1039 if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) 1040 i--; 1041 while (i > 0 && u32_is_word_boundary(pline[i], 0)) 1042 i--; 1043 while (i > 0 && !u32_is_word_boundary(pline[i], 0) 1044 && !u32_is_word_boundary(pline[i - 1], 0)) 1045 i--; 1046 1047 int d = state->search_column - i; 1048 1049 for (int j = i; j < len - d; j++) 1050 pline[j] = pline[j + d]; 1051 1052 len -= state->search_column - i; 1053 pline[len] = 0; 1054 state->search_column = i; 1055 state->search_display_column 1056 = display_length(state, state->search, state->search_column); 1057 1058 if (state->search_column < state->search_first_shown_column) 1059 { 1060 if (state->search_column > state->search_width - 4) 1061 state->search_first_shown_column = state->search_column 1062 - state->search_width + 4; 1063 else 1064 state->search_first_shown_column = 0; 1065 } 1066 state->search_display_column 1067 = display_length(state, state->search, state->search_column); 1068 1069 return next_match(state); 1070 } 1071 1072 int 1073 erase_search_to_end(struct DrawState* state) 1074 { 1075 state->search[state->search_column] = 0; 1076 move_search_end(state); 1077 return next_match(state); 1078 } 1079 1080 int 1081 erase_search_to_start(struct DrawState* state) 1082 { 1083 size_t i = state->search_column; 1084 size_t len = u32_strlen(state->search); 1085 do 1086 { 1087 state->search[i - state->search_column] = state->search[i]; 1088 i++; 1089 } while (state->search[i]); 1090 state->search[len - state->search_column] = 0; 1091 move_search_start(state); 1092 return next_match(state); 1093 } 1094 1095 int 1096 erase_to_end(struct DrawState* state) 1097 { 1098 struct BufferLine* buffer_line = NULL; 1099 1100 if (state->edit_info_focused) 1101 return 1; 1102 1103 buffer_line = &state->input_buffer[state->input_row]; 1104 buffer_line->text[state->input_column] = 0; 1105 buffer_line->length = state->input_column; 1106 move_end(state); 1107 state->dirty = 1; 1108 return 0; 1109 } 1110 1111 int 1112 erase_to_start(struct DrawState* state) 1113 { 1114 size_t i = state->input_column; 1115 struct BufferLine* buffer_line = &state->input_buffer[state->input_row]; 1116 size_t len = buffer_line->length; 1117 1118 if (state->edit_info_focused) 1119 return 1; 1120 1121 do 1122 { 1123 buffer_line->text[i - state->input_column] 1124 = buffer_line->text[i]; 1125 i++; 1126 } while (buffer_line->text[i]); 1127 buffer_line->text[len - state->input_column] = 0; 1128 buffer_line->length = len - state->input_column; 1129 move_start(state); 1130 state->dirty = 1; 1131 return 0; 1132 } 1133 1134 int 1135 exit_program(struct DrawState* state) 1136 { 1137 if (state->show_edit) 1138 { 1139 STRLCPY(errors[ERR_DLG_OPEN], state->error, MAXBUFLINE); 1140 return 0; 1141 } 1142 if (state->dirty) 1143 { 1144 state->in_prompt = 1; 1145 STRLCPY(prompt_dirty, state->prompt, MAXBUFLINE); 1146 state->prompt_callback = quit_callback; 1147 } 1148 else 1149 state->running = 0; 1150 *state->error = 0; 1151 return 0; 1152 } 1153 1154 int 1155 hide_search(struct DrawState* state) 1156 { 1157 state->show_search = 0; 1158 tb_hide_cursor(); 1159 return 0; 1160 } 1161 1162 int 1163 insert_line(struct DrawState* state) 1164 { 1165 struct BufferLine* newbuf = NULL; 1166 1167 if (state->edit_info_focused) 1168 return 1; 1169 1170 state->input_rows_count++; 1171 newbuf = realloc(state->input_buffer, 1172 state->input_rows_count * sizeof(struct BufferLine)); 1173 if (!newbuf) 1174 { 1175 STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE); 1176 state->running = 0; 1177 return 0; 1178 } 1179 1180 state->input_buffer = newbuf; 1181 init_bufferline(&state->input_buffer[state->input_rows_count - 1]); 1182 uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); 1183 if (!newtext) 1184 { 1185 STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE); 1186 state->running = 0; 1187 return 0; 1188 } 1189 state->input_buffer[state->input_rows_count - 1].text = newtext; 1190 for (size_t i = state->input_rows_count - 1; i > state->input_row + 1; 1191 i--) 1192 { 1193 u32_strncpy(state->input_buffer[i].text, 1194 state->input_buffer[i - 1].text, MAXBUFLINE); 1195 state->input_buffer[i].length 1196 = state->input_buffer[i - 1].length; 1197 } 1198 u32_strncpy(state->input_buffer[state->input_row + 1].text, 1199 state->input_buffer[state->input_row].text + state->input_column, 1200 MAXBUFLINE); 1201 state->input_buffer[state->input_row + 1].length 1202 = u32_strlen(state->input_buffer[state->input_row + 1].text); 1203 state->input_buffer[state->input_row].text[state->input_column] = 0; 1204 state->input_buffer[state->input_row].length 1205 = u32_strlen(state->input_buffer[state->input_row].text); 1206 1207 state->input_row++; 1208 if (state->input_row 1209 > state->input_first_shown_row + (EDIT_HEIGHT - 3) / 2) 1210 state->input_first_shown_row 1211 = state->input_row - (EDIT_HEIGHT - 3) / 2; 1212 move_start(state); 1213 state->dirty = 1; 1214 1215 return 0; 1216 } 1217 1218 int 1219 insert_search_space(struct DrawState* state) 1220 { 1221 insert_search_char(state, (uint32_t)L' '); 1222 return 0; 1223 } 1224 1225 int 1226 insert_search_tab(struct DrawState* state) 1227 { 1228 insert_search_char(state, (uint32_t)L'\t'); 1229 return 0; 1230 } 1231 1232 int 1233 insert_space(struct DrawState* state) 1234 { 1235 insert_char(state, (uint32_t)L' '); 1236 return 0; 1237 } 1238 1239 int 1240 insert_tab(struct DrawState* state) 1241 { 1242 insert_char(state, (uint32_t)L'\t'); 1243 return 0; 1244 } 1245 1246 int 1247 move_end(struct DrawState* state) 1248 { 1249 if (state->edit_info_focused) 1250 { 1251 if (state->info_rows_count > (EDIT_HEIGHT - 3) / 2) 1252 state->info_first_shown_row = state->info_rows_count 1253 - (EDIT_HEIGHT - 3) / 2; 1254 state->info_first_shown_column = 0; 1255 return 0; 1256 } 1257 1258 size_t len = state->input_buffer[state->input_row].length; 1259 state->input_column = len; 1260 size_t dlen = display_length(state, 1261 state->input_buffer[state->input_row].text, 1262 state->input_column); 1263 state->input_display_column = dlen; 1264 if (dlen > state->edit_width - 4) 1265 state->input_first_shown_column = dlen - state->edit_width + 4; 1266 else 1267 state->input_first_shown_column = 0; 1268 return 0; 1269 } 1270 1271 int 1272 move_down(struct DrawState* state) 1273 { 1274 if (state->edit_info_focused) 1275 { 1276 if (state->info_first_shown_row + (EDIT_HEIGHT - 3) / 2 1277 < state->info_rows_count) 1278 state->info_first_shown_row++; 1279 return 0; 1280 } 1281 1282 if (state->input_row < state->input_rows_count - 1) 1283 state->input_row++; 1284 if (state->input_first_shown_row + (EDIT_HEIGHT - 3) / 2 1285 < state->input_row) 1286 state->input_first_shown_row 1287 = state->input_row - (EDIT_HEIGHT - 3) / 2; 1288 if (state->saved_column == -1) 1289 state->saved_column = state->input_display_column; 1290 size_t dlen = display_length(state, 1291 state->input_buffer[state->input_row].text, 1292 state->input_buffer[state->input_row].length); 1293 if (dlen < state->saved_column) 1294 state->input_display_column = dlen; 1295 else 1296 state->input_display_column = state->saved_column; 1297 state->input_column = input_length(state, 1298 state->input_buffer[state->input_row].text, 1299 state->input_display_column); 1300 if (state->input_first_shown_column > state->input_display_column) 1301 state->input_first_shown_column = state->input_display_column; 1302 if (state->input_first_shown_column + state->edit_width - 4 1303 < state->input_display_column) 1304 state->input_first_shown_column 1305 = state->input_display_column - state->edit_width + 4; 1306 state->input_display_column = display_length(state, 1307 state->input_buffer[state->input_row].text, 1308 state->input_column); 1309 return 0; 1310 } 1311 1312 int 1313 move_left(struct DrawState* state) 1314 { 1315 if (state->edit_info_focused) 1316 { 1317 if (state->info_first_shown_column > 0) 1318 state->info_first_shown_column--; 1319 return 0; 1320 } 1321 1322 if (state->input_column == 0) 1323 return 0; 1324 1325 if (state->input_buffer[state->input_row].text[state->input_column - 1] 1326 == '\t') 1327 state->input_display_column = display_length(state, 1328 state->input_buffer[state->input_row].text, 1329 state->input_column - 1); 1330 else 1331 state->input_display_column--; 1332 state->input_column--; 1333 1334 if (state->input_display_column < state->input_first_shown_column) 1335 state->input_first_shown_column = state->input_display_column; 1336 return 0; 1337 } 1338 1339 int 1340 move_list_down(struct DrawState* state) 1341 { 1342 goto_msgid(state, next_msgid(state, 1)); 1343 return 0; 1344 } 1345 1346 int 1347 move_list_end(struct DrawState* state) 1348 { 1349 goto_msgid(state, 1350 next_msgid(state, 1351 state->real_msgid_count - state->msgid_number)); 1352 return 0; 1353 } 1354 1355 int 1356 move_list_page_down(struct DrawState* state) 1357 { 1358 *state->error = 0; 1359 goto_msgid(state, next_msgid(state, state->maxy - 2)); 1360 return 0; 1361 } 1362 1363 int 1364 move_list_page_up(struct DrawState* state) 1365 { 1366 *state->error = 0; 1367 goto_msgid(state, prev_msgid(state, state->maxy - 2)); 1368 return 0; 1369 } 1370 1371 int 1372 move_list_start(struct DrawState* state) 1373 { 1374 goto_msgid(state, 1); 1375 return 0; 1376 } 1377 1378 int 1379 move_list_up(struct DrawState* state) 1380 { 1381 goto_msgid(state, prev_msgid(state, 1)); 1382 return 0; 1383 } 1384 1385 int 1386 move_next_word(struct DrawState* state) 1387 { 1388 if (state->edit_info_focused) 1389 return 1; 1390 1391 int i = state->input_column; 1392 uint32_t* pline = state->input_buffer[state->input_row].text; 1393 size_t len = state->input_buffer[state->input_row].length; 1394 1395 while (i < len && !u32_is_word_boundary(pline[i], 0)) 1396 i++; 1397 while (i < len && u32_is_word_boundary(pline[i], 0)) 1398 i++; 1399 state->input_column = i; 1400 state->input_display_column = display_length(state, 1401 state->input_buffer[state->input_row].text, 1402 state->input_column); 1403 if (state->input_display_column - state->input_first_shown_column 1404 > state->edit_width - 4) 1405 state->input_first_shown_column 1406 = state->input_display_column - state->edit_width + 4; 1407 return 0; 1408 } 1409 1410 int 1411 move_prev_word(struct DrawState* state) 1412 { 1413 if (state->edit_info_focused) 1414 return 1; 1415 1416 int i = state->input_column; 1417 uint32_t* pline = state->input_buffer[state->input_row].text; 1418 1419 if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) 1420 i--; 1421 while (i > 0 && u32_is_word_boundary(pline[i], 0)) 1422 i--; 1423 while (i > 0 && !u32_is_word_boundary(pline[i], 0) 1424 && !u32_is_word_boundary(pline[i - 1], 0)) 1425 i--; 1426 1427 state->input_column = i; 1428 state->input_display_column = display_length(state, 1429 state->input_buffer[state->input_row].text, 1430 state->input_column); 1431 if (state->input_display_column < state->input_first_shown_column) 1432 state->input_first_shown_column = state->input_display_column; 1433 return 0; 1434 } 1435 1436 int 1437 move_right(struct DrawState* state) 1438 { 1439 if (state->edit_info_focused) 1440 { 1441 if (state->info_first_shown_column + state->edit_width - 3 1442 < state->info_rows_maxlen + 1) 1443 state->info_first_shown_column++; 1444 return 0; 1445 } 1446 1447 size_t len = state->input_buffer[state->input_row].length; 1448 if (state->input_column == len) 1449 return 0; 1450 1451 if (state->input_buffer[state->input_row].text[state->input_column] 1452 == '\t') 1453 state->input_display_column = display_length(state, 1454 state->input_buffer[state->input_row].text, 1455 state->input_column + 1); 1456 else 1457 state->input_display_column++; 1458 state->input_column++; 1459 1460 if (state->input_display_column - state->input_first_shown_column 1461 > state->edit_width - 4) 1462 state->input_first_shown_column 1463 = state->input_display_column - state->edit_width + 4; 1464 return 0; 1465 } 1466 1467 int 1468 move_search_end(struct DrawState* state) 1469 { 1470 size_t len = u32_strlen(state->search); 1471 state->search_column = len; 1472 size_t dlen 1473 = display_length(state, state->search, state->search_column); 1474 state->search_display_column = dlen; 1475 if (dlen > state->search_width - 4) 1476 state->search_first_shown_column 1477 = dlen - state->search_width + 4; 1478 else 1479 state->search_first_shown_column = 0; 1480 return 0; 1481 } 1482 1483 int 1484 move_search_left(struct DrawState* state) 1485 { 1486 if (state->search_column == 0) 1487 return 0; 1488 1489 if (state->search[state->search_column - 1] == '\t') 1490 state->search_display_column = display_length(state, 1491 state->search, state->search_column - 1); 1492 else 1493 state->search_display_column--; 1494 state->search_column--; 1495 1496 if (state->search_display_column < state->search_first_shown_column) 1497 state->search_first_shown_column = state->search_display_column; 1498 return 0; 1499 } 1500 1501 int 1502 move_search_next_word(struct DrawState* state) 1503 { 1504 int i = state->search_column; 1505 uint32_t* pline = state->search; 1506 size_t len = u32_strlen(state->search); 1507 1508 while (i < len && !u32_is_word_boundary(pline[i], 0)) 1509 i++; 1510 while (i < len && u32_is_word_boundary(pline[i], 0)) 1511 i++; 1512 state->search_column = i; 1513 state->search_display_column 1514 = display_length(state, state->search, state->search_column); 1515 1516 if (state->search_display_column - state->search_first_shown_column 1517 > state->search_width - 4) 1518 { 1519 state->search_first_shown_column = state->search_display_column 1520 - state->search_width + 4; 1521 state->search_display_column = display_length(state, 1522 state->search, state->search_column); 1523 } 1524 return 0; 1525 } 1526 1527 int 1528 move_search_prev_word(struct DrawState* state) 1529 { 1530 int i = state->search_column; 1531 uint32_t* pline = state->search; 1532 1533 if (i > 0 && u32_is_word_boundary(pline[i - 1], 0)) 1534 i--; 1535 while (i > 0 && u32_is_word_boundary(pline[i], 0)) 1536 i--; 1537 while (i > 0 && !u32_is_word_boundary(pline[i], 0) 1538 && !u32_is_word_boundary(pline[i - 1], 0)) 1539 i--; 1540 1541 state->search_column = i; 1542 state->search_display_column 1543 = display_length(state, state->search, state->search_column); 1544 1545 if (state->search_column < state->search_first_shown_column) 1546 { 1547 if (state->search_column > state->search_width - 4) 1548 state->search_first_shown_column = state->search_column 1549 - state->search_width + 4; 1550 else 1551 state->search_first_shown_column = 0; 1552 } 1553 state->search_display_column 1554 = display_length(state, state->search, state->search_column); 1555 return 0; 1556 } 1557 1558 int 1559 move_search_right(struct DrawState* state) 1560 { 1561 size_t len = u32_strlen(state->search); 1562 if (state->search_column == len) 1563 return 0; 1564 1565 if (state->search[state->search_column] == '\t') 1566 state->search_display_column = display_length(state, 1567 state->search, state->search_column + 1); 1568 else 1569 state->search_display_column++; 1570 state->search_column++; 1571 1572 if (state->search_display_column - state->search_first_shown_column 1573 > state->search_width - 4) 1574 state->search_first_shown_column = state->search_display_column 1575 - state->search_width + 4; 1576 return 0; 1577 } 1578 1579 int 1580 move_search_start(struct DrawState* state) 1581 { 1582 state->search_column = 0; 1583 state->search_display_column = 0; 1584 state->search_first_shown_column = 0; 1585 return 0; 1586 } 1587 1588 int 1589 move_start(struct DrawState* state) 1590 { 1591 if (state->edit_info_focused) 1592 { 1593 state->info_first_shown_row = 0; 1594 state->info_first_shown_column = 0; 1595 return 0; 1596 } 1597 1598 state->input_column = 0; 1599 state->input_display_column = 0; 1600 state->input_first_shown_column = 0; 1601 return 0; 1602 } 1603 1604 int 1605 move_up(struct DrawState* state) 1606 { 1607 if (state->edit_info_focused) 1608 { 1609 if (state->info_first_shown_row > 0) 1610 state->info_first_shown_row--; 1611 return 0; 1612 } 1613 1614 if (state->input_row > 0) 1615 state->input_row--; 1616 if (state->input_first_shown_row > state->input_row) 1617 state->input_first_shown_row = state->input_row; 1618 if (state->saved_column == -1) 1619 state->saved_column = state->input_display_column; 1620 size_t dlen = display_length(state, 1621 state->input_buffer[state->input_row].text, 1622 state->input_buffer[state->input_row].length); 1623 if (dlen < state->saved_column) 1624 state->input_display_column = dlen; 1625 else 1626 state->input_display_column = state->saved_column; 1627 state->input_column = input_length(state, 1628 state->input_buffer[state->input_row].text, 1629 state->input_display_column); 1630 if (state->input_first_shown_column > state->input_display_column) 1631 state->input_first_shown_column = state->input_display_column; 1632 if (state->input_first_shown_column + state->edit_width - 4 1633 < state->input_display_column) 1634 state->input_first_shown_column 1635 = state->input_display_column - state->edit_width + 4; 1636 state->input_display_column = display_length(state, 1637 state->input_buffer[state->input_row].text, 1638 state->input_column); 1639 return 0; 1640 } 1641 1642 int 1643 next_fuzzy(struct DrawState* state) 1644 { 1645 if (state->msgid_count == 0) 1646 return 0; 1647 1648 *state->error = 0; 1649 1650 struct PoEntry* current = NULL; 1651 for (size_t e = state->msgid_number; e < state->msgid_count; e++) 1652 { 1653 current = state->entries + e; 1654 if (current->flags & FL_FUZZY) 1655 { 1656 int saved_show_edit = state->show_edit; 1657 if (saved_show_edit) 1658 { 1659 if (state->dirty) 1660 save_msgstr(state); 1661 else /* No need to call cancel_msgstr */ 1662 state->dirty = state->dirty_before_edit; 1663 } 1664 1665 goto_msgid(state, e + 1); 1666 1667 if (saved_show_edit) 1668 { 1669 state->msgstr_index = 0; 1670 show_edit(state); 1671 } 1672 1673 return 0; 1674 } 1675 } 1676 return 0; 1677 } 1678 1679 int 1680 next_match(struct DrawState* state) 1681 { 1682 if (state->msgid_count == 0 || !state->search || !*state->search) 1683 return 0; 1684 1685 *state->error = 0; 1686 struct PoEntry* current = NULL; 1687 for (size_t e = state->msgid_number; e < state->real_msgid_count; e++) 1688 { 1689 current = state->entries + e; 1690 if (current->obsolete) 1691 continue; 1692 if ((current->msgid && *current->msgid 1693 && u32_strstr(current->msgid, state->search)) 1694 || (current->msgstr && *current->msgstr 1695 && **current->msgstr 1696 && u32_strstr(*current->msgstr, state->search))) 1697 { 1698 goto_msgid(state, e + 1); 1699 return 0; 1700 } 1701 } 1702 return 0; 1703 } 1704 1705 int 1706 next_plural(struct DrawState* state) 1707 { 1708 struct PoEntry* current = NULL; 1709 if (state->edit_info_focused) 1710 return 1; 1711 current = &state->entries[state->msgid_number - 1]; 1712 save_msgstr(state); 1713 if (state->msgstr_index + 1 < current->msgstr_count) 1714 state->msgstr_index++; 1715 show_edit(state); 1716 return 0; 1717 } 1718 1719 int 1720 next_untranslated(struct DrawState* state) 1721 { 1722 if (state->msgid_count == 0) 1723 return 0; 1724 1725 *state->error = 0; 1726 1727 struct PoEntry* current = NULL; 1728 for (size_t e = state->msgid_number; e < state->msgid_count; e++) 1729 { 1730 current = state->entries + e; 1731 if (!current->msgstr || !*current->msgstr 1732 || u32_strlen(*current->msgstr) == 0) 1733 { 1734 int saved_show_edit = state->show_edit; 1735 if (saved_show_edit) 1736 { 1737 if (state->dirty) 1738 save_msgstr(state); 1739 else /* No need to call cancel_msgstr */ 1740 state->dirty = state->dirty_before_edit; 1741 } 1742 1743 goto_msgid(state, e + 1); 1744 1745 if (saved_show_edit) 1746 { 1747 state->msgstr_index = 0; 1748 show_edit(state); 1749 } 1750 1751 return 0; 1752 } 1753 } 1754 return 0; 1755 } 1756 1757 int 1758 paste_paste_buffer_to_input(struct DrawState* state) 1759 { 1760 if (state->edit_info_focused) 1761 return 1; 1762 1763 if (!state->in_prompt && *state->input_buffer->text) 1764 { 1765 state->in_prompt = 1; 1766 STRLCPY(prompt_overwrite, state->prompt, MAXBUFLINE); 1767 state->prompt_callback = overwrite_paste_callback; 1768 return 0; 1769 } 1770 1771 if (state->msgid_number == 1) 1772 { 1773 STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE); 1774 return 0; 1775 } 1776 1777 size_t lines_to_init = 0; 1778 size_t lines_to_free = 0; 1779 struct BufferLine* pinput = NULL; 1780 struct BufferLine* newbuf = NULL; 1781 struct BufferLine* ppaste = NULL; 1782 1783 if (!state->paste_buffer || !(*state->paste_buffer).text 1784 || !*(*state->paste_buffer).text) 1785 { 1786 /* leave one row */ 1787 for (size_t i = 1; i < state->input_rows_count; i++) 1788 free_bufferline(state->input_buffer + i); 1789 state->input_buffer = realloc(state->input_buffer, 1790 sizeof(struct BufferLine)); 1791 *(state->input_buffer->text) = 0; 1792 state->input_column = 0; 1793 state->input_display_column = 0; 1794 state->input_first_shown_column = 0; 1795 state->input_first_shown_row = 0; 1796 state->input_row = 0; 1797 state->input_rows_count = 1; 1798 state->dirty = 1; 1799 return 0; 1800 } 1801 1802 lines_to_init = state->input_rows_count < state->paste_rows_count 1803 ? state->paste_rows_count - state->input_rows_count 1804 : 0; 1805 lines_to_free = state->input_rows_count > state->paste_rows_count 1806 ? state->input_rows_count - state->paste_rows_count 1807 : 0; 1808 1809 for (size_t i = 0; i < lines_to_free; i++) 1810 free_bufferline( 1811 state->input_buffer + state->input_rows_count - i - 1); 1812 newbuf = realloc(state->input_buffer, 1813 state->paste_rows_count * sizeof(struct BufferLine)); 1814 ppaste = state->paste_buffer; 1815 1816 if (!newbuf) 1817 return 0; 1818 1819 for (size_t i = 0; i < lines_to_init; i++) 1820 { 1821 pinput = newbuf + state->paste_rows_count - 1 - i; 1822 init_bufferline(pinput); 1823 uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); 1824 if (!newtext) 1825 return 0; 1826 pinput->text = newtext; 1827 } 1828 1829 if (ppaste) 1830 for (size_t i = 0; i < state->paste_rows_count; i++) 1831 { 1832 pinput = newbuf + i; 1833 u32_strncpy(pinput->text, ppaste->text, 1834 MIN(ppaste->length + 1, MAXBUFLINE)); 1835 pinput->length = ppaste->length; 1836 ppaste++; 1837 } 1838 1839 state->input_rows_count = state->paste_rows_count; 1840 state->input_buffer = newbuf; 1841 state->dirty = 1; 1842 1843 return 0; 1844 } 1845 1846 int 1847 prev_match(struct DrawState* state) 1848 { 1849 if (state->msgid_count == 0 || !state->search || !*state->search) 1850 return 0; 1851 1852 *state->error = 0; 1853 struct PoEntry* current = NULL; 1854 for (size_t e = state->msgid_number - 1; e > 0; e--) 1855 { 1856 current = state->entries + e - 1; 1857 if ((current->msgid && *current->msgid 1858 && u32_strstr(current->msgid, state->search)) 1859 || (current->msgstr && *current->msgstr 1860 && **current->msgstr 1861 && u32_strstr(*current->msgstr, state->search))) 1862 { 1863 goto_msgid(state, e); 1864 return 0; 1865 } 1866 } 1867 return 0; 1868 } 1869 1870 int 1871 prev_plural(struct DrawState* state) 1872 { 1873 if (state->edit_info_focused) 1874 return 1; 1875 save_msgstr(state); 1876 if (state->msgstr_index > 0) 1877 state->msgstr_index--; 1878 show_edit(state); 1879 return 0; 1880 } 1881 1882 int 1883 save_msgstr(struct DrawState* state) 1884 { 1885 uint32_t* to_save = NULL; 1886 size_t to_save_size = 0; 1887 struct PoEntry* current = NULL; 1888 1889 state->show_edit = 0; 1890 state->edit_info_focused = 0; 1891 1892 if (state->input_buffer) 1893 { 1894 to_save_size = state->input_rows_count * MAXMSGLINE; 1895 to_save = calloc(to_save_size, sizeof(uint32_t)); 1896 if (!to_save) 1897 { 1898 STRLCPY(errors[ERR_CANT_ALLOC], state->error, 1899 MAXBUFLINE); 1900 return 0; 1901 } 1902 struct BufferLine* pinput_buffer = NULL; 1903 for (size_t i = 0; i < state->input_rows_count; i++) 1904 { 1905 pinput_buffer = state->input_buffer + i; 1906 if (i == 0) 1907 u32_strncpy(to_save, pinput_buffer->text, 1908 to_save_size * sizeof(uint32_t)); 1909 else 1910 { 1911 u32_strncat(to_save, (uint32_t*)L"\\n", 1912 to_save_size * sizeof(uint32_t)); 1913 u32_strncat(to_save, pinput_buffer->text, 1914 to_save_size * sizeof(uint32_t)); 1915 } 1916 free_bufferline(pinput_buffer); 1917 } 1918 } 1919 free(state->input_buffer); 1920 state->input_buffer = NULL; 1921 state->input_rows_count = 0; 1922 for (size_t i = 0; i < state->info_rows_count; i++) 1923 free_bufferline(state->info_buffer + i); 1924 free(state->info_buffer); 1925 state->dirty = (state->dirty_before_edit || state->dirty); 1926 state->info_rows_count = 0; 1927 state->info_buffer = NULL; 1928 u32_encode_tabs(to_save, to_save_size * sizeof(uint32_t)); 1929 current = &state->entries[state->msgid_number - 1]; 1930 u32_match_msgid_ending(current->msgid, to_save, 1931 to_save_size * sizeof(uint32_t)); 1932 u32_set_msgstr(current, to_save, state->msgstr_index, 0, state); 1933 update_warning(current->msgid, to_save, ¤t->warning); 1934 state->input_changed_fuzzy = UNCHANGED; 1935 1936 tb_hide_cursor(); 1937 free(to_save); 1938 return 0; 1939 } 1940 1941 int 1942 show_edit(struct DrawState* state) 1943 { 1944 if (!load_msgstr(state) || !load_info(state)) 1945 { 1946 STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE); 1947 state->running = 0; 1948 return 0; 1949 } 1950 1951 state->dirty_before_edit = state->dirty; 1952 state->dirty = 0; 1953 state->fuzzy_before_edit = state->fuzzy_count; 1954 state->input_changed_fuzzy = UNCHANGED; 1955 state->show_edit = 1; 1956 state->input_row = 0; 1957 state->input_first_shown_row = 0; 1958 state->input_first_shown_column = 0; 1959 state->info_first_shown_row = 0; 1960 state->info_first_shown_column = 0; 1961 move_start(state); 1962 *state->error = 0; 1963 return 0; 1964 } 1965 1966 int 1967 show_help(struct DrawState* state) 1968 { 1969 state->show_help = 1; 1970 *state->error = 0; 1971 return 0; 1972 } 1973 1974 int 1975 show_search(struct DrawState* state) 1976 { 1977 state->show_search = 1; 1978 state->search_column = 0; 1979 state->search_display_column = 0; 1980 state->search_first_shown_column = 0; 1981 if (!state->search) 1982 state->search = calloc(MAXSEARCH, sizeof(uint32_t)); 1983 *state->search = 0; 1984 tb_set_cursor((state->maxx - state->search_width + 2) / 2, 1985 state->maxy - 1 - SEARCH_HEIGHT + 1); 1986 return 0; 1987 } 1988 1989 int 1990 toggle_fuzzy(struct DrawState* state) 1991 { 1992 if (state->msgid_number == 1) 1993 { 1994 STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE); 1995 return 0; 1996 } 1997 if (state->msgid_count > 0) 1998 { 1999 state->entries[state->msgid_number - 1].flags ^= FL_FUZZY; 2000 if (state->entries[state->msgid_number - 1].flags & FL_FUZZY) 2001 { 2002 state->fuzzy_count++; 2003 if (state->input_changed_fuzzy == CLEARED) 2004 state->input_changed_fuzzy = UNCHANGED; 2005 else 2006 state->input_changed_fuzzy = SET; 2007 } 2008 else 2009 { 2010 state->fuzzy_count--; 2011 if (state->input_changed_fuzzy == SET) 2012 state->input_changed_fuzzy = UNCHANGED; 2013 else 2014 state->input_changed_fuzzy = CLEARED; 2015 } 2016 state->dirty = 1; 2017 } 2018 return 0; 2019 } 2020 2021 int 2022 toggle_info_focused(struct DrawState* state) 2023 { 2024 state->edit_info_focused = !state->edit_info_focused; 2025 return 0; 2026 } 2027 2028 int 2029 yank_input_to_paste_buffer(struct DrawState* state) 2030 { 2031 if (state->edit_info_focused) 2032 return 1; 2033 2034 if (state->msgid_number == 1) 2035 { 2036 STRLCPY(errors[ERR_ILLEGAL_ON_FIRST], state->error, MAXBUFLINE); 2037 return 0; 2038 } 2039 2040 size_t lines_to_init = 0; 2041 size_t lines_to_free = 0; 2042 struct BufferLine* ppaste = NULL; 2043 struct BufferLine* newbuf = NULL; 2044 struct BufferLine* pinput = NULL; 2045 2046 if (!state->input_buffer || !(*state->input_buffer).text 2047 || !*(*state->input_buffer).text) 2048 { 2049 for (size_t i = 0; i < state->paste_rows_count; i++) 2050 free_bufferline(state->paste_buffer + i); 2051 free(state->paste_buffer); 2052 state->paste_buffer = NULL; 2053 state->paste_rows_count = 0; 2054 return 0; 2055 } 2056 2057 lines_to_init = state->paste_rows_count < state->input_rows_count 2058 ? state->input_rows_count - state->paste_rows_count 2059 : 0; 2060 lines_to_free = state->paste_rows_count > state->input_rows_count 2061 ? state->paste_rows_count - state->input_rows_count 2062 : 0; 2063 2064 for (size_t i = 0; i < lines_to_free; i++) 2065 free_bufferline( 2066 state->paste_buffer + state->paste_rows_count - i - 1); 2067 if (!state->paste_buffer) 2068 newbuf = calloc(state->input_rows_count, 2069 sizeof(struct BufferLine)); 2070 else 2071 newbuf = realloc(state->paste_buffer, 2072 state->input_rows_count * sizeof(struct BufferLine)); 2073 pinput = state->input_buffer; 2074 2075 if (!newbuf) 2076 return 0; 2077 2078 for (size_t i = 0; i < lines_to_init; i++) 2079 { 2080 ppaste = newbuf + state->input_rows_count - 1 - i; 2081 init_bufferline(ppaste); 2082 uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); 2083 if (!newtext) 2084 return 0; 2085 ppaste->text = newtext; 2086 } 2087 2088 if (pinput) 2089 for (size_t i = 0; i < state->input_rows_count; i++) 2090 { 2091 ppaste = newbuf + i; 2092 u32_strncpy(ppaste->text, pinput->text, 2093 MIN(pinput->length + 1, MAXBUFLINE)); 2094 ppaste->length = pinput->length; 2095 pinput++; 2096 } 2097 2098 state->paste_rows_count = state->input_rows_count; 2099 state->paste_buffer = newbuf; 2100 2101 return 0; 2102 } 2103 2104 int 2105 write_file(struct DrawState* state) 2106 { 2107 int result = save_file(state->entries, state->real_msgid_count, 2108 state->filename, state->nplurals); 2109 switch (result) 2110 { 2111 case SAVE_ERR_CANT_ALLOC: 2112 STRLCPY(errors[ERR_CANT_ALLOC], state->error, MAXBUFLINE); 2113 state->running = 0; 2114 break; 2115 case SAVE_ERR_CANT_OPEN_FILE: 2116 STRLCPY(errors[ERR_CANT_SAVE], state->error, MAXBUFLINE); 2117 state->running = 0; 2118 break; 2119 case SAVE_ERR_CANT_MOVE_FILE: 2120 STRLCPY(strerror(errno), state->error, MAXBUFLINE); 2121 state->running = 0; 2122 break; 2123 default: 2124 *state->error = 0; 2125 state->dirty = 0; 2126 } 2127 return 0; 2128 } 2129 2130 int 2131 main(int argc, char** argv) 2132 { 2133 struct tb_event ev; 2134 char error[MAXBUFLINE]; 2135 char prompt[MAXBUFLINE]; 2136 struct DrawState state; 2137 char filename[MAXPATH]; 2138 int recalculate_size = 1; 2139 struct stat st; 2140 long lineno; 2141 long col; 2142 int result; 2143 int dir_access; 2144 int dir_access_errno; 2145 char* dir = NULL; 2146 2147 *error = 0; 2148 *prompt = 0; 2149 *filename = 0; 2150 2151 #ifdef __OpenBSD__ 2152 if (pledge("stdio tty unveil rpath wpath cpath fattr", NULL) < 0) 2153 { 2154 perror(PROGRAMNAME ": pledge"); 2155 exit(1); 2156 } 2157 #endif 2158 if (argc == 1) 2159 return print_error(1, "No filename given"); 2160 2161 if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) 2162 { 2163 printf("Usage:\t%s -h | --help | -V | --full-version | -v |" 2164 " --version\n" 2165 "\t%s filename.po\n", 2166 PROGRAMNAME, PROGRAMNAME); 2167 return 0; 2168 } 2169 else if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) 2170 { 2171 printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE); 2172 return 0; 2173 } 2174 else if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--full-version")) 2175 { 2176 printf("%s %s, committed on %s\n", PROGRAMNAME, VERSION, DATE); 2177 puts(COPYRIGHT); 2178 printf("Features set in config.h:\n"); 2179 #ifdef CREATE_BACKUPS 2180 printf(" CREATE_BACKUPS:\tset\n"); 2181 #else 2182 printf(" CREATE_BACKUPS:\tunset\n"); 2183 #endif 2184 #ifdef MATCH_MSGSTR_ENDING 2185 printf(" MATCH_MSGSTR_ENDING:\tset\n"); 2186 #else 2187 printf(" MATCH_MSGSTR_ENDING:\tunset\n"); 2188 #endif 2189 #ifdef PLURAL_STRING 2190 printf(" PLURAL_STRING:\t\"%s\"\n", PLURAL_STRING); 2191 #else 2192 printf(" PLURAL_STRING:\tunset\n"); 2193 #endif 2194 printf(" SAVE_WRAP_WIDTH:\t%d\n", SAVE_WRAP_WIDTH); 2195 printf(" WRAP_FIRST_MSGSTR:\t%d\n", WRAP_FIRST_MSGSTR); 2196 printf(" WARN_COUNT_DOTS:\t%d\n", WARN_COUNT_DOTS); 2197 return 0; 2198 } 2199 else 2200 { 2201 strncpy(filename, argv[1], MAXPATH - 1); 2202 filename[MAXPATH - 1] = 0; 2203 if (!*filename) 2204 print_error(EINVAL, "No filename given"); 2205 } 2206 2207 init_drawstate(&state, error, filename, prompt); 2208 2209 if (stat(filename, &st) == -1 || access(filename, R_OK | W_OK) == -1) 2210 goto file_error; 2211 2212 if (!S_ISREG(st.st_mode)) 2213 { 2214 result = print_error(EINVAL, "Is not a regular file"); 2215 goto general_error; 2216 } 2217 2218 #ifdef __OpenBSD__ 2219 if (unveil(state.filename, "rwc") < 0) 2220 { 2221 perror(PROGRAMNAME ": unveil"); 2222 exit(1); 2223 } 2224 #endif 2225 2226 dir = dirname(state.filename); 2227 #ifdef __OpenBSD__ 2228 if (unveil(dir, "rwc") < 0) 2229 { 2230 perror(PROGRAMNAME ": unveil"); 2231 exit(1); 2232 } 2233 #endif 2234 #ifdef CREATE_BACKUPS 2235 errno = 0; 2236 if (lstat(state.filename, &st) != -1) 2237 { 2238 if (S_ISLNK(st.st_mode)) 2239 { 2240 if (!(state.real_filename = calloc(MAXPATH, 1))) 2241 return print_error(1, 2242 "Allocation failed (out of memory?)"); 2243 state.rfn_len = readlink(state.filename, 2244 state.real_filename, MAXPATH); 2245 if (state.rfn_len == -1) 2246 goto file_error; 2247 state.real_filename[state.rfn_len] = 0; 2248 #ifdef __OpenBSD__ 2249 if (unveil(state.real_filename, "rwc") < 0) 2250 { 2251 perror(PROGRAMNAME ": unveil"); 2252 exit(1); 2253 } 2254 #endif 2255 if (stat(state.real_filename, &st) == -1) 2256 goto file_error; 2257 } 2258 if (!(state.backup_filename = calloc(MAXPATH, 1))) 2259 { 2260 free(state.real_filename); 2261 return print_error(1, 2262 "Allocation failed (out of memory?)"); 2263 } 2264 2265 /* Backup filename is the target file + suffix if the file to 2266 * write is a symlink, otherwise file + suffix */ 2267 STRLCPY(state.rfn_len > 0 ? state.real_filename : state.filename, 2268 state.backup_filename, MAXPATH); 2269 STRLCAT(backup_suffix, state.backup_filename, MAXPATH); 2270 #ifdef __OpenBSD__ 2271 if (unveil(state.backup_filename, "rwc") < 0) 2272 { 2273 perror(PROGRAMNAME ": unveil"); 2274 exit(1); 2275 } 2276 #endif 2277 } 2278 else 2279 goto file_error; 2280 #endif /* CREATE_BACKUPS */ 2281 2282 if (state.rfn_len > 0) 2283 { 2284 dir = dirname(state.real_filename); 2285 #ifdef __OpenBSD__ 2286 if (unveil(dir, "rwc") < 0) 2287 { 2288 perror(PROGRAMNAME ": unveil"); 2289 exit(1); 2290 } 2291 #endif 2292 } 2293 2294 dir_access = access(dir, W_OK | X_OK); 2295 dir_access_errno = errno; 2296 #ifdef BACKUP_FAIL_OK 2297 state.backup_possible = dir_access != -1; 2298 #else /* !BACKUP_FAIL_OK */ 2299 if (dir_access == -1) 2300 { 2301 errno = dir_access_errno; 2302 goto file_error; 2303 } 2304 #endif /* BACKUP_FAIL_OK */ 2305 if (stat(state.filename, &st) == -1) 2306 { 2307 errno = dir_access_errno; 2308 goto file_error; 2309 } 2310 2311 if (!S_ISREG(st.st_mode)) 2312 { 2313 result = print_error(EINVAL, "Is not a regular file"); 2314 goto general_error; 2315 } 2316 2317 #ifdef __OpenBSD__ 2318 if (unveil(NULL, NULL) < 0) 2319 { 2320 perror(PROGRAMNAME ": unveil"); 2321 exit(1); 2322 } 2323 #endif 2324 2325 if (access(state.filename, F_OK | R_OK | W_OK) == -1) 2326 goto file_error; 2327 2328 if (state.rfn_len > 0 2329 && access(state.real_filename, F_OK | R_OK | W_OK) == -1) 2330 goto file_error; 2331 2332 lineno = 1; 2333 col = 1; 2334 result = load_file(&state, &lineno, &col); 2335 switch (result) 2336 { 2337 case LOAD_ERR_CANT_ALLOC: 2338 case LOAD_ERR_P_ALLOC: 2339 (void)print_error(result, "Allocation failed (out of memory?)"); 2340 goto general_error; 2341 case LOAD_ERR_CANT_OPEN_FILE: 2342 (void)print_error(result, "Cannot open file `%s'", filename); 2343 goto general_error; 2344 case LOAD_ERR_NOT_PO_FILE: 2345 (void)print_error(result, "Not a .po file `%s'", filename); 2346 goto general_error; 2347 case LOAD_ERR_P_INVAL: 2348 (void)print_error(result, "Invalid value"); 2349 goto general_error; 2350 case LOAD_ERR_P_SYNTAX: 2351 (void)print_error(result, "%s:%ld:%ld: Syntax error", filename, 2352 lineno, col); 2353 goto general_error; 2354 case LOAD_ERR_NONE: 2355 if (*error) 2356 { 2357 result = print_error(1, error); 2358 goto general_error; 2359 } 2360 break; 2361 default: 2362 result = print_error(1, "Unknown error (%d)", result); 2363 goto general_error; 2364 } 2365 state.msgid_number = state.msgid_count > 0 ? 1 : 0; 2366 state.first_shown_msgid = state.msgid_number; 2367 goto init_termbox; 2368 2369 file_error: 2370 result = errno; 2371 perror(PROGRAMNAME); 2372 /* FALLTHROUGH */ 2373 2374 general_error: 2375 free(state.real_filename); 2376 free(state.backup_filename); 2377 return result; 2378 2379 init_termbox: 2380 tb_init(); 2381 *error = 0; 2382 state.maxx = 0; 2383 2384 while (state.running) 2385 { 2386 tb_clear(); 2387 2388 if (recalculate_size) 2389 { 2390 state.maxx = tb_width(); 2391 state.help_width = calculate_help_width(&state); 2392 state.edit_width = calculate_edit_width(&state); 2393 state.search_width = calculate_search_width(&state); 2394 recalculate_size = 0; 2395 } 2396 state.maxy = tb_height(); 2397 2398 if (!draw(&state)) 2399 STRLCPY(errors[ERR_CANT_ALLOC], state.error, MAXBUFLINE); 2400 tb_present(); 2401 result = tb_poll_event(&ev); 2402 if (result == TB_OK && ev.type == TB_EVENT_RESIZE) 2403 recalculate_size = 1; 2404 else if (result == TB_OK && ev.type == TB_EVENT_KEY) 2405 handle_key_event(&ev, &state); 2406 } 2407 tb_shutdown(); 2408 if (state.error && *state.error) 2409 return print_error(1, state.error); 2410 2411 free_drawstate(&state); 2412 return 0; 2413 }