poe

Уређивач .po фајлова
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување 896305171f8464e8e1f9f02f7c23fa84cf73b3e1
родитељ 1b488738b7455a16a0ac5707dabc7d3eed7b1850
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Sun,  4 Jul 2021 21:53:37 +0200

Added .po loaded structures saving

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
MTODO | 3++-
Mdraw.c | 40+++++++++++++++++++++++++++++++++++++---
Mdraw.h | 2++
Mpo.c | 356+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mpo.h | 29++++++++++++++++++-----------
Mpoe.1.in | 9+++++++++
Mpoe.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mutil.h | 5+++--
измењених датотека: 8, додавања: 485(+), брисања: 58(-)

diff --git a/TODO b/TODO @@ -8,7 +8,8 @@ [x] Reading #, comments [x] Reading #: comments - [ ] .po file save + [~] .po file save + [x] Save all read .po structure [ ] Edit first msgid's msgstr [ ] Yank/paste (entire current msgstr) diff --git a/draw.c b/draw.c @@ -31,7 +31,8 @@ const char* errors[] = { [ERR_DLG_OPEN] = "Press Esc to close dialog or q/C-Q to quit", [ERR_DLG_OPEN_QUIT] = "Press Esc to close dialog", [ERR_EMPTY_FILE] = "No msgids to edit", - [ERR_CANT_ALLOC] = "Memory allocation failed - exit advised" + [ERR_CANT_ALLOC] = "Memory allocation failed - exit advised", + [ERR_CANT_SAVE] = "File is not writeable" }; const char* flag_strings[] = { [FL_C_FORMAT] = "c-format", @@ -67,6 +68,39 @@ free_bufferline(struct BufferLine* bl) bl->bg = 0; } +struct BufferLine* +split(const uint32_t* s) +{ + if (!s) + return NULL; + + size_t num_lines = u32_lines_in_string(s); + struct BufferLine* newbuf = NULL; + struct BufferLine* pnewbuf = NULL; + const uint32_t* ps = s; + + newbuf = calloc(num_lines, sizeof(struct BufferLine)); + if (!newbuf) + return NULL; + + pnewbuf = newbuf; + for (size_t i = 0; i < num_lines; i++) + { + init_bufferline(pnewbuf); + uint32_t* newtext = calloc(MAXBUFLINE, sizeof(uint32_t)); + if (!newtext) + return NULL; + pnewbuf->text = newtext; + size_t len = u32_line_length(ps); + pnewbuf->length = u32_strncpy(pnewbuf->text, ps, + MIN(len+1, MAXBUFLINE)); + ps += len + (ps[len] ? 2 : 0); + pnewbuf++; + } + + return newbuf; +} + void draw_rect(const int startx, const int endx, const int starty, const int endy, const uint16_t fg, const uint16_t bg) @@ -275,8 +309,8 @@ draw_editbox(const struct DrawState* state) u32_draw_string(endx-2, starty+1, fg, bg, (uint32_t*)L"\u2191", 1, 1, 0, 0, LEFT); if (state->info_first_shown_row + (h-3)/2 + 2 <= state->info_rows_count) - u32_draw_string(endx-2, h/2+1, fg, bg, (uint32_t*)L"\u2193", - 1, 1, 0, 0, LEFT); + u32_draw_string(endx-2, starty+(h-3)/2+1, fg, bg, + (uint32_t*)L"\u2193", 1, 1, 0, 0, LEFT); /* *u32_draw_string(startx+1, starty+1, EDIT_MSGID_FG, EDIT_MSGID_BG, * state->entries[state->msgid_number-1].msgid, w-2, diff --git a/draw.h b/draw.h @@ -51,6 +51,7 @@ struct DrawState { void (*prompt_callback)(struct DrawState*, struct tb_event*); int running; int dirty; + int dirty_before_edit; int in_prompt; int show_help; int show_edit; @@ -76,6 +77,7 @@ extern struct PoEntry entries[]; void init_bufferline(struct BufferLine* bl); void free_bufferline(struct BufferLine* bl); +struct BufferLine* split(const uint32_t* s); void draw_rect(const int startx, const int endx, const int starty, const int endy, const uint16_t fg, const uint16_t bg); void draw_box(const int startx, const int endx, const int starty, diff --git a/po.c b/po.c @@ -1,7 +1,9 @@ #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "po.h" +#include "draw.h" #include "util.h" extern const char* flag_strings[]; @@ -19,16 +21,18 @@ format_flags(char* buffer, const size_t max, const struct PoEntry* entry) void init_po_entry(struct PoEntry* entry) { - entry->flags = FL_NONE; - entry->msgid = NULL; - entry->msgid_plural = NULL; - entry->plural_forms = NULL; - entry->msgstr = NULL; - entry->msgstr_count = 0; - entry->comments = NULL; - entry->comment_lines = 0; - entry->ref_comments = NULL; - entry->ref_comment_lines = 0; + entry->flags = FL_NONE; + entry->msgid = NULL; + entry->msgid_plural = NULL; + entry->plural_forms = NULL; + entry->msgstr = NULL; + entry->msgstr_count = 0; + entry->comments = NULL; + entry->comment_lines = 0; + entry->trans_comments = NULL; + entry->trans_comment_lines = 0; + entry->ref_comments = NULL; + entry->ref_comment_lines = 0; } void @@ -56,6 +60,15 @@ free_po_entry(struct PoEntry* entry) free(entry->comments); } + if (entry->trans_comments) + { + uint32_t** trans_comment = entry->trans_comments; + while (trans_comment < entry->trans_comments + + entry->trans_comment_lines) + free(*trans_comment++); + free(entry->trans_comments); + } + if (entry->ref_comments) { char** comment = entry->ref_comments; @@ -208,7 +221,7 @@ struct PoEntry* u32_add_comment(struct PoEntry* entry, const uint32_t* comment) { uint32_t** comments = NULL; - uint32_t* new_line = NULL; + uint32_t* new_line = NULL; if (!entry) return NULL; @@ -232,6 +245,34 @@ u32_add_comment(struct PoEntry* entry, const uint32_t* comment) } struct PoEntry* +u32_add_trans_comment(struct PoEntry* entry, const uint32_t* trans_comment) +{ + uint32_t** trans_comments = NULL; + uint32_t* new_line = NULL; + + if (!entry) + return NULL; + + entry->trans_comment_lines++; + if (!entry->trans_comments) + trans_comments = calloc(entry->trans_comment_lines, + sizeof(uint32_t*)); + else + trans_comments = realloc(entry->trans_comments, + entry->trans_comment_lines * sizeof(uint32_t*)); + if (!trans_comments) + return NULL; + entry->trans_comments = trans_comments; + + new_line = calloc(MAXCOMMENTLINE, sizeof(uint32_t)); + if (!new_line) + return NULL; + entry->trans_comments[entry->trans_comment_lines-1] = new_line; + u32_strncpy(new_line, trans_comment, MAXCOMMENTLINE); + return entry; +} + +struct PoEntry* add_ref_comment(struct PoEntry* entry, const char* comment) { char** ref_comments = NULL; @@ -316,6 +357,8 @@ load_file(struct PoEntry** entries, size_t* entries_size, (*lineno)++; } fclose(input); + if (*entries_count > 1) + (*entries_count)++; /* file has < PO_DETECTION_LINES */ if (msgid_counter > 0) @@ -325,8 +368,277 @@ load_file(struct PoEntry** entries, size_t* entries_size, } int -save_file(const struct PoEntry* entries, const char* filename) +save_file(const struct PoEntry* entries, const size_t msgid_count, + const char* filename) { + const struct PoEntry* current = entries; + size_t len = strlen(filename); + FILE* output = NULL; + char* backup_filename = calloc(len+2, 1); + char* tmp_filename = "/tmp/poe.tmp"; + + if (!(output = fopen(tmp_filename, "wt"))) + return SAVE_ERR_CANT_OPEN_FILE; + strncpy(backup_filename, filename, len+1); + backup_filename[len] = '~'; + backup_filename[len+1] = 0; + + while (current < entries + msgid_count) + { + if (current->comment_lines > 0) + { + char u8_comment[MAXCOMMENTLINE*6]; + for (size_t c = 0; c < current->comment_lines; c++) + { + unicode_string_to_u8(u8_comment, + *(current->comments + c)); + fprintf(output, "#%s\n", u8_comment); + } + } + if (current->trans_comment_lines > 0) + { + char u8_trans_comment[MAXCOMMENTLINE*6]; + for (size_t tc = 0; tc < current->trans_comment_lines; + tc++) + { + unicode_string_to_u8(u8_trans_comment, + *(current->trans_comments + tc)); + fprintf(output, "#.%s%s\n", + u8_trans_comment[0] == 0 ? "" : + " ", + u8_trans_comment); + } + } + if (current->ref_comment_lines > 0) + { + for (size_t rc = 0; rc < current->ref_comment_lines; + rc++) + fprintf(output, "#: %s\n", + *(current->ref_comments + rc)); + } + Flags flags = current->flags; + if (current->flags != FL_NONE) + fprintf(output, "#, "); + while (flags != FL_NONE) + { + if (flags & FL_C_FORMAT) + { + flags &= ~FL_C_FORMAT; + fprintf(output, "%s", + flag_strings[FL_C_FORMAT]); + } + else if (flags & FL_FUZZY) + { + flags &= ~FL_FUZZY; + fprintf(output, "%s", + flag_strings[FL_FUZZY]); + } + if (flags != FL_NONE) + fprintf(output, ", "); + } + if (current->flags != FL_NONE) + fprintf(output, "\n"); + + if (current->msgid) + { + char msgid[MAXMSG]; + unicode_string_to_u8(msgid, current->msgid); + size_t num_lines = u32_lines_in_string(current->msgid); + if (num_lines > 1) + { + struct BufferLine* msgid_split = split( + current->msgid); + struct BufferLine* pmsgid_split = NULL; + + if (!msgid_split) + return SAVE_ERR_CANT_ALLOC; + + fprintf(output, "msgid \"\"\n"); + for (size_t i = 0; i < num_lines; i++) + { + pmsgid_split = msgid_split + i; + int last = i == num_lines - 1; + char u8_msgid[MAXMSG*6]; + size_t len = u32_strlen( + pmsgid_split->text); + unicode_string_to_u8(u8_msgid, + pmsgid_split->text); + if (!(last && len == 0)) + fprintf(output, "\"%s%s\"\n", + u8_msgid, last ? "" : + "\\n"); + } + for (size_t i = 0; i < num_lines; i++) + { + pmsgid_split = msgid_split + i; + free_bufferline(pmsgid_split); + } + free(msgid_split); + } + else + { + char u8_msgid[MAXMSG*6]; + unicode_string_to_u8(u8_msgid, current->msgid); + fprintf(output, "msgid \"%s\"\n", u8_msgid); + } + } + else + fprintf(output, "msgid \"\"\n"); + + if (current->msgid_plural) + { + char msgid_plural[MAXMSG]; + unicode_string_to_u8(msgid_plural, + current->msgid_plural); + size_t num_lines = u32_lines_in_string( + current->msgid_plural); + if (num_lines > 1) + { + struct BufferLine* msgid_split = split( + current->msgid_plural); + struct BufferLine* pmsgid_split = NULL; + + if (!msgid_split) + return SAVE_ERR_CANT_ALLOC; + + fprintf(output, "msgid_plural \"\"\n"); + for (size_t i = 0; i < num_lines; i++) + { + pmsgid_split = msgid_split + i; + int last = i == num_lines - 1; + char u8_msgid[MAXMSG*6]; + size_t len = u32_strlen( + pmsgid_split->text); + unicode_string_to_u8(u8_msgid, + pmsgid_split->text); + if (!(last && len == 0)) + fprintf(output, "\"%s%s\"\n", + u8_msgid, last ? "" : + "\\n"); + } + for (size_t i = 0; i < num_lines; i++) + { + pmsgid_split = msgid_split + i; + free_bufferline(pmsgid_split); + } + free(msgid_split); + } + else + { + char u8_msgid[MAXMSG*6]; + unicode_string_to_u8(u8_msgid, + current->msgid_plural); + fprintf(output, "msgid_plural \"%s\"\n", + u8_msgid); + } + } + + if (current->msgstr_count == 1) + { + size_t num_lines = u32_lines_in_string( + *current->msgstr); + if (num_lines > 1) + { + struct BufferLine* msgstr_split = + split(*current->msgstr); + struct BufferLine* pmsgstr_split = NULL; + + if (!msgstr_split) + return SAVE_ERR_CANT_ALLOC; + + fprintf(output, "msgstr \"\"\n"); + for (size_t i = 0; i < num_lines; i++) + { + pmsgstr_split = msgstr_split + i; + int last = i == num_lines - 1; + char u8_msgstr[MAXMSG*6]; + size_t len = u32_strlen( + pmsgstr_split->text); + unicode_string_to_u8(u8_msgstr, + pmsgstr_split->text); + if (!(last && len == 0)) + fprintf(output, "\"%s%s\"\n", + u8_msgstr, + last ? "" : "\\n"); + } + for (size_t i = 0; i < num_lines; i++) + { + pmsgstr_split = msgstr_split + i; + free_bufferline(pmsgstr_split); + } + free(msgstr_split); + } + else + { + char u8_msgstr[MAXMSG*6]; + unicode_string_to_u8(u8_msgstr, *current->msgstr); + fprintf(output, "msgstr \"%s\"\n", u8_msgstr); + } + } + else if (current->msgstr_count > 0) + { + for (size_t m = 0; m < current->msgstr_count; m++) + { + size_t num_lines = u32_lines_in_string( + *current->msgstr); + if (num_lines > 1) + { + struct BufferLine* msgstr_split = + split(*(current->msgstr + m)); + struct BufferLine* pmsgstr_split = NULL; + + if (!msgstr_split) + return SAVE_ERR_CANT_ALLOC; + + fprintf(output, "msgstr[%ld] \"\"\n", + m); + for (size_t i = 0; i < num_lines; i++) + { + pmsgstr_split = + msgstr_split + i; + int last = i == num_lines - 1; + char u8_msgstr[MAXMSG*6]; + size_t len = u32_strlen( + pmsgstr_split->text); + unicode_string_to_u8(u8_msgstr, + pmsgstr_split->text); + if (!(last && len == 0)) + fprintf(output, + "\"%s%s\"\n", + u8_msgstr, + last ? "" : + "\\n"); + } + for (size_t i = 0; i < num_lines; i++) + { + pmsgstr_split = + msgstr_split + i; + free_bufferline(pmsgstr_split); + } + free(msgstr_split); + } + else + { + char u8_msgstr[MAXMSG*6]; + unicode_string_to_u8(u8_msgstr, + *(current->msgstr + m)); + fprintf(output, "msgstr[%ld] \"%s\"\n", + m, u8_msgstr); + } + } + } + else + fprintf(output, "msgstr \"\"\n"); + + fprintf(output, "\n"); + current++; + } + fclose(output); + + rename(filename, backup_filename); + rename(tmp_filename, filename); + free(backup_filename); + return SAVE_ERR_NONE; } @@ -424,7 +736,7 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, if (*col == 2 && *state & PS_COMMENT) { *state &= ~PS_COMMENT; - *state |= PS_COMMENT_DESC; + *state |= PS_COMMENT_TRANS; pline++; (*col)++; if (*pline == ' ') @@ -485,7 +797,9 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, break; case '"': /* skip beginning and end quote */ - if (ptoken == token || *(pline+1) == 0) + if ((ptoken == token || *(pline+1) == 0) + && !(*state & (PS_COMMENT | + PS_COMMENT_TRANS))) { pline++; (*col)++; @@ -538,12 +852,18 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, } else if (*state & PS_MSGSTR_ARRAY) set_msgstr(*current_entry, token, *msgstr_index, 1); - else if (*state & PS_COMMENT_DESC) + else if (*state & PS_COMMENT) { uint32_t u32_token[MAXCOMMENTLINE]; u8_string_to_unicode(u32_token, token); u32_add_comment(*current_entry, u32_token); } + else if (*state & PS_COMMENT_TRANS) + { + uint32_t u32_token[MAXCOMMENTLINE]; + u8_string_to_unicode(u32_token, token); + u32_add_trans_comment(*current_entry, u32_token); + } else if (*state & PS_COMMENT_FLAG) { ptoken = token; @@ -574,9 +894,13 @@ parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, add_ref_comment(*current_entry, token); *token = 0; } + else if (*state & PS_COMMENT) + u32_add_comment(*current_entry, (uint32_t*)L""); + else if (*state & PS_COMMENT_TRANS) + u32_add_trans_comment(*current_entry, (uint32_t*)L""); /* one-line states */ - *state &= ~(PS_COMMENT | PS_COMMENT_DESC | PS_COMMENT_FLAG | + *state &= ~(PS_COMMENT | PS_COMMENT_TRANS | PS_COMMENT_FLAG | PS_COMMENT_REF); return PARSE_ERR_NONE; diff --git a/po.h b/po.h @@ -29,19 +29,21 @@ enum { }; enum { - SAVE_ERR_NONE = 0 + SAVE_ERR_NONE = 0, + SAVE_ERR_CANT_ALLOC = 1, + SAVE_ERR_CANT_OPEN_FILE = 2 }; typedef enum { - PS_NONE = 0, - PS_COMMENT = 1, - PS_COMMENT_DESC = 1 << 1, - PS_COMMENT_FLAG = 1 << 2, - PS_COMMENT_REF = 1 << 3, - PS_MSGID = 1 << 4, - PS_MSGID_PLURAL = 1 << 5, - PS_MSGSTR = 1 << 6, - PS_MSGSTR_ARRAY = 1 << 7 + PS_NONE = 0, + PS_COMMENT = 1, + PS_COMMENT_TRANS = 1 << 1, + PS_COMMENT_FLAG = 1 << 2, + PS_COMMENT_REF = 1 << 3, + PS_MSGID = 1 << 4, + PS_MSGID_PLURAL = 1 << 5, + PS_MSGSTR = 1 << 6, + PS_MSGSTR_ARRAY = 1 << 7 } ParseState; typedef enum { @@ -59,6 +61,8 @@ struct PoEntry { int msgstr_count; uint32_t** comments; size_t comment_lines; + uint32_t** trans_comments; + size_t trans_comment_lines; char** ref_comments; size_t ref_comment_lines; }; @@ -84,11 +88,14 @@ struct PoEntry* u32_set_plural_forms(struct PoEntry* entry, struct PoEntry* u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr, int msgstr_index, int append); struct PoEntry* u32_add_comment(struct PoEntry* entry, const uint32_t* comment); +struct PoEntry* u32_add_trans_comment(struct PoEntry* entry, + const uint32_t* trans_comment); struct PoEntry* add_ref_comment(struct PoEntry* entry, const char* comment); int load_file(struct PoEntry** entries, size_t* entries_size, size_t* entries_count, const char* filename, long* lineno, long* col); -int save_file(const struct PoEntry* entries, const char* filename); +int save_file(const struct PoEntry* entries, const size_t msgid_count, + const char* filename); int parse_po_line(const char* line, struct PoEntry** entries, size_t* entries_size, size_t* entries_count, struct PoEntry** current_entry, ParseState* state, long* col, diff --git a/poe.1.in b/poe.1.in @@ -108,6 +108,10 @@ _ . .sp 1 . +.LP +When the info box is focused, arrow keys, C-B/C-F (PgUp/PgDn) and C-A/C-E +(Home/End) keys scroll the info box. +. .SH "KNOWN LIMITATIONS" . .IP \(bu 4 @@ -128,6 +132,11 @@ for more details. Only one file can be edited at a time, and no additional files can be loaded at runtime. This is a design decision. . +.IP \(bu +The directory \fC/tmp\fP must be writeable by the user in order to write the +changes to the file. This is due to creating backups in order to avoid the +potential data loss under specific circumstances (loss of power during write). +. .SH BUGS . .LP diff --git a/poe.c b/poe.c @@ -84,7 +84,24 @@ quit_callback(struct DrawState* state, struct tb_event* ev) { if (state) { - // if (ev->ch == 'y')... + if (ev->ch == 'y') + { + int result = save_file(state->entries, + state->msgid_count, state->filename); + switch (result) + { + case SAVE_ERR_CANT_ALLOC: + strcpy(state->error, + errors[ERR_CANT_ALLOC]); + break; + case SAVE_ERR_CANT_OPEN_FILE: + strcpy(state->error, + errors[ERR_CANT_SAVE]); + break; + default: + state->error[0] = 0; + } + } state->dirty = 0; state->prompt_callback = NULL; state->running = 0; @@ -141,6 +158,8 @@ load_info(struct DrawState* state) } if (current->comments) num_lines += current->comment_lines + 1; + if (current->trans_comments) + num_lines += current->trans_comment_lines + 1; if (current->ref_comments) num_lines += current->ref_comment_lines + 1; newbuf = calloc(num_lines, sizeof(struct BufferLine)); @@ -187,11 +206,6 @@ load_info(struct DrawState* state) pumsgid += len+(pumsgid[len] ? 2 : 0); pnewbuf++; } - /* - *pnewbuf->text[0] = 0; - *pnewbuf->length = 0; - *pnewbuf++; - */ } if (current->msgid_plural) { @@ -217,11 +231,6 @@ load_info(struct DrawState* state) pumsgid += len+(pumsgid[len] ? 2 : 0); pnewbuf++; } - /* - *pnewbuf->text[0] = 0; - *pnewbuf->length = 0; - *pnewbuf++; - */ if (first->plural_forms) { newlen = u32_u8_strncpy(pnewbuf->text, "Plural forms:", @@ -238,11 +247,6 @@ load_info(struct DrawState* state) if (newlen > state->info_rows_maxlen) state->info_rows_maxlen = newlen; pnewbuf++; - /* - *pnewbuf->text[0] = 0; - *pnewbuf->length = 0; - *pnewbuf++; - */ } } if (current->comments) @@ -266,11 +270,28 @@ load_info(struct DrawState* state) state->info_rows_maxlen = newlen; pnewbuf++; } - /* - *pnewbuf->text[0] = 0; - *pnewbuf->length = 0; - *pnewbuf++; - */ + } + if (current->trans_comments) + { + newlen = u32_u8_strncpy(pnewbuf->text, "Translation comments:", + MAXBUFLINE); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf->fg = EDIT_MSGID_HEADING_FG; + pnewbuf->bg = EDIT_MSGID_HEADING_BG; + pnewbuf++; + for (size_t i = 0; i < current->trans_comment_lines; i++) + { + uint32_t* trans_comment = current->trans_comments[i]; + size_t len = u32_strlen(trans_comment); + newlen = u32_strncpy(pnewbuf->text, trans_comment, + MIN(len+1, MAXBUFLINE)); + pnewbuf->length = newlen; + if (newlen > state->info_rows_maxlen) + state->info_rows_maxlen = newlen; + pnewbuf++; + } } if (current->ref_comments) { @@ -387,7 +408,7 @@ save_msgstr(struct DrawState* state) u32_encode_tabs(to_save, MAXBUFLINE); u32_set_msgstr(&state->entries[state->msgid_number-1], to_save, state->msgstr_index, 0); - state->dirty = 1; + //state->dirty = 1; } void @@ -403,6 +424,7 @@ cancel_msgstr(struct DrawState* state) free_bufferline(state->info_buffer+i); free(state->info_buffer); state->info_buffer = NULL; + state->dirty = state->dirty_before_edit; } void @@ -743,6 +765,7 @@ insert_char(struct DrawState* state, uint32_t ch) > EDIT_WIDTH-5) state->input_first_shown_column = state->input_display_column - EDIT_WIDTH+5; + state->dirty = 1; } void @@ -763,6 +786,7 @@ erase_to_start(struct DrawState* state) buffer_line->text[len-state->input_column] = 0; buffer_line->length = len-state->input_column; move_start(state); + state->dirty = 1; } void @@ -774,6 +798,7 @@ erase_to_end(struct DrawState* state) buffer_line->text[state->input_column] = 0; buffer_line->length = state->input_column; move_end(state); + state->dirty = 1; } struct DrawState* @@ -816,6 +841,7 @@ insert_line(struct DrawState* state) state->input_first_shown_row = state->input_row - (EDIT_HEIGHT-3)/2; move_start(state); + state->dirty = 1; return state; } @@ -851,6 +877,7 @@ join_lines(struct DrawState* state, size_t first_line) if (!newbuf) return NULL; state->input_buffer = newbuf; + state->dirty = 1; return state; } @@ -941,6 +968,7 @@ handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) } buffer_line->text[i] = 0; buffer_line->length--; + state->dirty = 1; } else if (state->input_row < state->input_rows_count-1) if (!join_lines(state, state->input_row)) @@ -981,6 +1009,7 @@ handle_editbox_key_event(struct tb_event* ev, struct DrawState* state) else { save_msgstr(state); + state->dirty_before_edit = state->dirty; move_list_down(state); state->msgstr_index = 0; load_msgstr(state); @@ -1083,6 +1112,23 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) move_list_page_up(state); else if (ev->key == TB_KEY_CTRL_F || ev->key == TB_KEY_PGDN) move_list_page_down(state); + else if (ev->key == TB_KEY_CTRL_S || ev->ch == 'w') + { + int result = save_file(state->entries, state->msgid_count, + state->filename); + switch (result) + { + case SAVE_ERR_CANT_ALLOC: + strcpy(state->error, errors[ERR_CANT_ALLOC]); + break; + case SAVE_ERR_CANT_OPEN_FILE: + strcpy(state->error, errors[ERR_CANT_SAVE]); + break; + default: + state->error[0] = 0; + } + state->dirty = 0; + } else if (ev->key == TB_KEY_CTRL_Z || (ev->ch == 'f' && !state->show_edit)) toggle_fuzzy(state); @@ -1098,6 +1144,7 @@ handle_key_event(struct tb_event* ev, struct DrawState* state) state->msgstr_index = 0; if (load_msgstr(state) && load_info(state)) { + state->dirty_before_edit = state->dirty; state->show_edit = 1; state->input_row = 0; state->input_first_shown_row = 0; @@ -1160,6 +1207,7 @@ main(int argc, char** argv) if (!(input = fopen(filename, "rt"))) return print_error(ENOENT, "No such file `%s'", filename); + fclose(input); long lineno = 1; long col = 1; @@ -1180,8 +1228,8 @@ main(int argc, char** argv) case LOAD_ERR_P_INVAL: return print_error(result, "Invalid value"); case LOAD_ERR_P_SYNTAX: - return print_error(result, "%s:%ld: Syntax error", - filename, lineno); + return print_error(result, "%s:%ld:%ld: Syntax error", + filename, lineno, col); case LOAD_ERR_NONE: break; default: @@ -1268,8 +1316,9 @@ main(int argc, char** argv) nanosleep(&sleep_spec, NULL); } } - fclose(input); tb_shutdown(); + if (state.error && state.error[0]) + return print_error(1, state.error); return 0; } diff --git a/util.h b/util.h @@ -10,10 +10,11 @@ enum { ERR_DLG_OPEN, ERR_DLG_OPEN_QUIT, ERR_EMPTY_FILE, - ERR_CANT_ALLOC + ERR_CANT_ALLOC, + ERR_CANT_SAVE }; -#define MAXBUFLINE 1024 +#define MAXBUFLINE 4096 #define MAXMSG 1024 #define MAXPATH 1024 #define MAXFLAGSBUF 3+1