Word Unperfect
public
Read
Owner: themaster
Branch: main
Commits: 0
Git CLI clone URL
git clone https://www.xt-emporium.com/git/word-unperfect.git
Fullscreen desktop URL
Code
Commits
History
Branches
Bug Reports
Discussions
Compare
Settings
word-unperfect
/
rev
/
wp_application.c
File editor
#include "wp_application.h" #include "wp_document_model.h" #include "wp_file_format.h" #include "wp_layout_engine.h" #include "wp_record_parser.h" #include "wp_format_executor.h" #include "wp_text_export.h" #include "wp_variable_codes.h" #include "wp_resource_manager.h" #include <ctype.h> #include <errno.h> #include <limits.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <string.h> #define WP_APP_SCREEN_CONTENT_ROWS (WP_SCREEN_HEIGHT - 1) #define WP_APP_FORMAT_POSITION_SCALE_A 200U #define WP_APP_FORMAT_POSITION_SCALE_B 210U #define WP_APP_FORMAT_POSITION_SCALE_C 250U #define WP_APP_FORMAT_POSITION_SCALE_D 225U #define WP_APP_FORMAT_POSITION_SCALE_E 170U #define WP_APP_ATTR_BODY 0x17U #define WP_APP_ATTR_HIGHLIGHT 0x1fU #define WP_APP_ATTR_TABLE_TITLE 0x60U #define WP_APP_ATTR_SUPERSCRIPT 0x75U #define WP_APP_LINE_UNIT_SCALE 100U #define WP_APP_STATUS_RIGHT_END_COL 75U typedef struct WpApplicationRenderState { WpDisplayBuffer *db; WpApplicationScreenStats *stats; uint16_t attribute_mask; size_t target_page; size_t viewport_first_row; size_t current_page; size_t row; size_t col; size_t line_on_page; size_t current_line_width; char line_prefix[16]; size_t line_prefix_len; size_t table_column; size_t table_borders_seen; size_t pending_form_feed_column; size_t left_margin; bool table_mode; bool line_has_text; bool pending_form_feed; bool pending_form_feed_column_valid; bool left_margin_active; } WpApplicationRenderState; static void wp_application_table_rewrite_data_row(WpApplicationRenderState *state); int wp_application_run_smoke_pipeline(WpLayoutGlobals *wl, WpVirtualMemory *vm, WpDisplayBuffer *db) { WpLine line; WpRecord rec; (void)vm; wp_parser_consume_record(wl, &rec); wp_record_free(&rec); wp_display_init(db); wp_display_clear(db); db->reveal_codes_active = 1; wp_reveal_codes_sync_view(wl, db); line.start_offset = 0; line.end_offset = 0; line.width = 0; line.space_count = 0; line.justification = WP_JUSTIFY_LEFT; line.is_hard_return = 0; wl->primary_limit = 72; wp_layout_break_line(wl, &line); (void)db->cells[0][0].ch; (void)rec; return (int)line.width; } int wp_application_run_format_pass(const char *filename) { WpFormatExecutorOptions options; WpFormatExecutorStats stats; bool ok; wp_format_executor_default_options(&options); options.apply_layout_state = true; options.follow_nested_streams = true; options.wrap_column = 80; ok = wp_format_executor_run_file(filename, &options, &stats); printf("format-pass: file=%s status=%s\n", filename, ok ? "success" : "failed"); printf("records seen: %lu\n", (unsigned long)stats.records_seen); printf("bytes consumed: %lu\n", (unsigned long)stats.bytes_consumed); printf("chars handled: %lu\n", (unsigned long)stats.char_records); printf("lines formatted: %lu\n", (unsigned long)stats.lines_seen); printf("pages estimated: %lu\n", (unsigned long)stats.pages_seen); printf("hard returns: %lu\n", (unsigned long)stats.hard_returns); printf("soft returns: %lu\n", (unsigned long)stats.soft_returns); printf("tabs handled: %lu\n", (unsigned long)stats.tabs); printf("d4 layout states: %lu (applied=%lu)\n", (unsigned long)stats.d4_layout_state_records, (unsigned long)stats.d4_layout_state_applied); printf("variant balance: %d (total=%d)\n", stats.final_variant_balance, stats.final_variant_total); printf("final column: %u (max=%u)\n", (unsigned)stats.current_column, (unsigned)stats.max_column); if (stats.incomplete_records > 0 || stats.mismatched_trailers > 0) { printf("warnings: incomplete=%lu mismatched=%lu\n", (unsigned long)stats.incomplete_records, (unsigned long)stats.mismatched_trailers); } return ok ? 0 : 1; } static void wp_application_clear_stats(WpApplicationScreenStats *stats) { if (stats != NULL) { memset(stats, 0, sizeof(*stats)); } } static const char *wp_application_basename(const char *path) { const char *slash; if (path == NULL) { return ""; } slash = strrchr(path, '/'); return slash == NULL ? path : slash + 1; } static uint8_t wp_application_screen_char(unsigned char ch) { if (ch >= 0x20U && ch < 0x7fU) { return (uint8_t)ch; } return '?'; } static size_t wp_application_line_units_from_line(size_t line) { if (line == 0U) { line = 1U; } if (line > SIZE_MAX / WP_APP_LINE_UNIT_SCALE) { return SIZE_MAX; } return line * WP_APP_LINE_UNIT_SCALE; } static void wp_application_clear_wp_screen(WpDisplayBuffer *db) { size_t y; size_t x; if (db == NULL) { return; } for (y = 0U; y < WP_SCREEN_HEIGHT; ++y) { for (x = 0U; x < WP_SCREEN_WIDTH; ++x) { wp_display_putc(db, (int)x, (int)y, ' ', WP_APP_ATTR_BODY); } } } static uint8_t wp_application_attr_for_paired_code(uint8_t paired_code) { switch (paired_code) { case 0x08U: return WP_APP_ATTR_TABLE_TITLE; case 0x0CU: case 0x0EU: return WP_APP_ATTR_HIGHLIGHT; default: return WP_APP_ATTR_BODY; } } static uint8_t wp_application_current_attr(const WpApplicationRenderState *state) { if (state == NULL) { return WP_APP_ATTR_BODY; } if ((state->attribute_mask & (1U << 0x08U)) != 0U) { return WP_APP_ATTR_TABLE_TITLE; } if ((state->attribute_mask & (1U << 0x0CU)) != 0U || (state->attribute_mask & (1U << 0x0EU)) != 0U) { return WP_APP_ATTR_HIGHLIGHT; } return WP_APP_ATTR_BODY; } static void wp_application_set_row_attr_range(WpDisplayBuffer *db, size_t row, size_t start_col, size_t end_col, uint8_t attr) { size_t x; if (db == NULL || row >= WP_SCREEN_HEIGHT || start_col >= WP_SCREEN_WIDTH) { return; } if (end_col >= WP_SCREEN_WIDTH) { end_col = WP_SCREEN_WIDTH - 1U; } for (x = start_col; x <= end_col; ++x) { db->cells[row][x].attr = attr; db->cells[row][x].is_dirty = true; } } static bool wp_application_visible_row(const WpApplicationRenderState *state, size_t doc_row, size_t *out_screen_row) { size_t screen_row; if (state == NULL || state->current_page != state->target_page || doc_row < state->viewport_first_row) { return false; } screen_row = doc_row - state->viewport_first_row; if (screen_row >= WP_APP_SCREEN_CONTENT_ROWS) { return false; } if (out_screen_row != NULL) { *out_screen_row = screen_row; } return true; } static void wp_application_make_status_path(const char *filename, char *out, size_t out_size) { #ifdef WORD_UNPERFECT_POSIX_PATHS const char *path; const char *fallback; size_t i; size_t pos = 0U; if (out == NULL || out_size == 0U) { return; } out[0] = '\0'; fallback = wp_application_basename(filename); path = filename; if (path == NULL || path[0] == '\0') { path = fallback[0] == '\0' ? "untitled.wp" : fallback; } for (i = 0U; path[i] != '\0' && pos + 1U < out_size; ++i) { unsigned char ch = (unsigned char)path[i]; out[pos++] = (ch >= 0x20U && ch < 0x7fU) ? (char)ch : '?'; } out[pos] = '\0'; #else static const char marker[] = "wp_c/WP51/"; const char *start; const char *rel; size_t i; size_t pos = 0U; if (out == NULL || out_size == 0U) { return; } out[0] = '\0'; if (filename == NULL) { return; } start = strstr(filename, marker); if (start == NULL) { start = strstr(filename, "WP51/"); if (start != NULL) { rel = start + 5U; } else { rel = wp_application_basename(filename); } } else { rel = start + sizeof(marker) - 1U; } if (start != NULL && pos + 8U < out_size) { memcpy(out, "B:\\WP51\\", 8U); pos = 8U; } for (i = 0U; rel[i] != '\0' && pos + 1U < out_size; ++i) { unsigned char ch = (unsigned char)rel[i]; if (ch == '/') { out[pos++] = '\\'; } else if (ch >= 'a' && ch <= 'z') { out[pos++] = (char)(ch - 'a' + 'A'); } else { out[pos++] = (char)ch; } } out[pos] = '\0'; #endif } static void wp_application_status_line(WpDisplayBuffer *db, const char *filename, const WpApplicationScreenStats *stats) { char line[WP_SCREEN_WIDTH + 1]; char right[128]; char right_align[128]; char line_number[32]; char pos_number[32]; char pos_align_number[32]; char name_buf[WP_SCREEN_WIDTH + 1]; const char *name; size_t i; size_t name_room; size_t name_len; size_t right_len; size_t right_align_len; size_t right_attr_len; size_t right_col; unsigned long page; unsigned long doc_line; unsigned long doc_line_fraction; size_t line_units; unsigned long pos; unsigned long pos_fraction; size_t pos_units; if (db == NULL || stats == NULL) { return; } wp_application_make_status_path(filename, name_buf, sizeof(name_buf)); name = name_buf[0] == '\0' ? wp_application_basename(filename) : name_buf; page = (unsigned long)(stats->rendered_page == 0U ? 1U : stats->rendered_page); line_units = stats->rendered_line_units == 0U ? wp_application_line_units_from_line(stats->rendered_line) : stats->rendered_line_units; if (line_units == 0U) { line_units = WP_APP_LINE_UNIT_SCALE; } doc_line = (unsigned long)(line_units / WP_APP_LINE_UNIT_SCALE); doc_line_fraction = (unsigned long)(line_units % WP_APP_LINE_UNIT_SCALE); if (doc_line == 0UL) { doc_line = 1UL; } pos_units = stats->rendered_pos_units == 0U ? wp_application_line_units_from_line(stats->rendered_pos) : stats->rendered_pos_units; if (pos_units == 0U) { pos_units = WP_APP_LINE_UNIT_SCALE; } pos = (unsigned long)(pos_units / WP_APP_LINE_UNIT_SCALE); pos_fraction = (unsigned long)(pos_units % WP_APP_LINE_UNIT_SCALE); if (pos == 0UL) { pos = 1UL; } memset(line, ' ', WP_SCREEN_WIDTH); line[WP_SCREEN_WIDTH] = '\0'; if (doc_line_fraction == 0UL) { snprintf(line_number, sizeof(line_number), "%lu", doc_line); } else { snprintf(line_number, sizeof(line_number), "%lu.%02lu", doc_line, doc_line_fraction); } if (pos_fraction == 0UL) { snprintf(pos_number, sizeof(pos_number), "%lu", pos); } else if ((pos_fraction % 10UL) == 0UL) { snprintf(pos_number, sizeof(pos_number), "%lu.%lu", pos, pos_fraction / 10UL); } else { snprintf(pos_number, sizeof(pos_number), "%lu.%02lu", pos, pos_fraction); } snprintf(pos_align_number, sizeof(pos_align_number), "%lu", pos); snprintf(right, sizeof(right), "Doc 1 Pg %lu Ln %s\" Pos %s\"", page, line_number, pos_number); snprintf(right_align, sizeof(right_align), "Doc 1 Pg %lu Ln %s\" Pos %s\"", page, line_number, pos_align_number); right_len = strlen(right); if (right_len > WP_SCREEN_WIDTH) { right_len = WP_SCREEN_WIDTH; } right_align_len = strlen(right_align); if (right_align_len > WP_SCREEN_WIDTH) { right_align_len = WP_SCREEN_WIDTH; } right_attr_len = right_align_len > 3U ? right_align_len - 3U : right_align_len; right_col = right_align_len <= WP_APP_STATUS_RIGHT_END_COL ? WP_APP_STATUS_RIGHT_END_COL - right_align_len : 0U; if (right_col + right_len > WP_SCREEN_WIDTH) { right_col = WP_SCREEN_WIDTH - right_len; } name_room = right_col; if (name_room > 1U) { name_room--; } name_len = strlen(name); if (name_len > name_room) { name_len = name_room; } memcpy(line, name, name_len); memcpy(line + right_col, right, right_len); for (i = 0U; i < WP_SCREEN_WIDTH; ++i) { uint8_t attr = WP_APP_ATTR_BODY; if (i < name_len || (i >= right_col && i < right_col + right_attr_len)) { attr = WP_APP_ATTR_HIGHLIGHT; } wp_display_putc(db, (int)i, WP_SCREEN_HEIGHT - 1, (uint8_t)line[i], attr); } } static uint16_t wp_application_le16(const uint8_t *p) { return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8)); } static size_t wp_application_units_to_columns(uint16_t units, uint16_t scale) { if (scale == 0U) { return 0U; } return (size_t)(((uint32_t)units + (uint32_t)scale - 1U) / (uint32_t)scale); } static size_t wp_application_units_to_columns_floor(uint16_t units, uint16_t scale) { if (scale == 0U) { return 0U; } return (size_t)((uint32_t)units / (uint32_t)scale); } static void wp_application_note_column(WpApplicationRenderState *state) { if (state != NULL && state->stats != NULL && state->current_line_width > state->stats->max_column) { state->stats->max_column = state->current_line_width; } } static void wp_application_wrap_visual_line(WpApplicationRenderState *state) { if (state == NULL || state->stats == NULL) { return; } wp_application_note_column(state); state->stats->soft_wraps++; state->stats->visual_lines++; state->row++; state->col = 0U; state->current_line_width = 0U; state->line_prefix_len = 0U; state->line_prefix[0] = '\0'; state->line_has_text = false; } static void wp_application_advance_line(WpApplicationRenderState *state) { if (state == NULL || state->stats == NULL) { return; } if (state->table_mode && state->table_borders_seen >= 2U && state->line_has_text) { wp_application_table_rewrite_data_row(state); } wp_application_note_column(state); state->stats->logical_lines++; state->stats->visual_lines++; state->line_on_page++; state->row++; state->col = 0U; state->current_line_width = 0U; state->line_prefix_len = 0U; state->line_prefix[0] = '\0'; state->line_has_text = false; if (state->left_margin_active) { if (state->left_margin >= WP_SCREEN_WIDTH) { state->left_margin = WP_SCREEN_WIDTH - 1U; } state->col = state->left_margin; state->current_line_width = state->left_margin; } } static bool wp_application_line_prefix_equals(WpApplicationRenderState *state, const char *text) { if (state == NULL || text == NULL) { return false; } return strcmp(state->line_prefix, text) == 0; } static void wp_application_emit_display_char_attr(WpApplicationRenderState *state, unsigned char ch, uint8_t attr) { size_t screen_row; if (state == NULL || state->stats == NULL) { return; } if (state->col == 9U && ch != ' ' && wp_application_line_prefix_equals(state, "Contains:")) { wp_application_emit_display_char_attr(state, ' ', attr); } if (state->col >= WP_SCREEN_WIDTH) { wp_application_wrap_visual_line(state); } if (wp_application_visible_row(state, state->row, &screen_row)) { wp_display_putc(state->db, (int)state->col, (int)screen_row, wp_application_screen_char(ch), attr); } else if (state->current_page == state->target_page && state->row >= state->viewport_first_row + WP_APP_SCREEN_CONTENT_ROWS) { state->stats->clipped_chars++; } state->col++; state->current_line_width++; state->line_has_text = true; if (state->line_prefix_len + 1U < sizeof(state->line_prefix)) { state->line_prefix[state->line_prefix_len++] = (char)ch; state->line_prefix[state->line_prefix_len] = '\0'; } state->stats->text_bytes++; state->stats->text_chars++; wp_application_note_column(state); } static void wp_application_emit_display_char(WpApplicationRenderState *state, unsigned char ch) { wp_application_emit_display_char_attr(state, ch, wp_application_current_attr(state)); } static uint8_t wp_application_extended_screen_char(uint8_t set, uint8_t index) { uint32_t cp; if (index == 1U) { switch (set) { case 2U: return '~'; case 3U: return '^'; case 4U: return '-'; case 5U: return '/'; case 9U: return '\''; case 0x30U: case 0x32U: case 0x34U: case 0x36U: return 'I'; case 0x3AU: case 0x3CU: case 0x40U: return 'O'; case 0x42U: case 0x44U: return 'U'; default: return '.'; } } cp = wp_char_to_unicode(set, index); if (cp >= 0x20U && cp < 0x7fU) { return (uint8_t)cp; } return '.'; } static void wp_application_emit_extended_char(WpApplicationRenderState *state, const WpRecord *rec) { uint8_t set = 0U; uint8_t index = 0U; if (state == NULL || rec == NULL) { return; } if (rec->data_length >= 2U) { set = rec->data[0]; index = rec->data[1]; } else if (rec->data_length >= 1U) { set = rec->sub_code; index = rec->data[0]; } wp_application_emit_display_char(state, wp_application_extended_screen_char(set, index)); } static void wp_application_emit_ascii(WpApplicationRenderState *state, const char *text) { while (text != NULL && *text != '\0') { wp_application_emit_display_char(state, (unsigned char)*text++); } } static void wp_application_clear_screen_row(WpApplicationRenderState *state, size_t row) { size_t x; size_t screen_row; if (state == NULL || state->db == NULL || !wp_application_visible_row(state, row, &screen_row)) { return; } for (x = 0U; x < WP_SCREEN_WIDTH; ++x) { wp_display_putc(state->db, (int)x, (int)screen_row, ' ', WP_APP_ATTR_BODY); } } static void wp_application_write_screen_row(WpApplicationRenderState *state, size_t row, const char *text) { size_t x; size_t screen_row; if (state == NULL || state->db == NULL || text == NULL || !wp_application_visible_row(state, row, &screen_row)) { return; } wp_application_clear_screen_row(state, row); for (x = 0U; x < WP_SCREEN_WIDTH && text[x] != '\0'; ++x) { wp_display_putc(state->db, (int)x, (int)screen_row, (uint8_t)text[x], WP_APP_ATTR_BODY); } } static void wp_application_style_table_header_row(WpDisplayBuffer *db, size_t row) { wp_application_set_row_attr_range(db, row, 1U, 5U, WP_APP_ATTR_HIGHLIGHT); wp_application_set_row_attr_range(db, row, 9U, 13U, WP_APP_ATTR_HIGHLIGHT); wp_application_set_row_attr_range(db, row, 19U, 23U, WP_APP_ATTR_HIGHLIGHT); wp_application_set_row_attr_range(db, row, 28U, 34U, WP_APP_ATTR_HIGHLIGHT); } static void wp_application_style_table_subheader_row(WpDisplayBuffer *db, size_t row) { wp_application_set_row_attr_range(db, row, 2U, 3U, WP_APP_ATTR_HIGHLIGHT); wp_application_set_row_attr_range(db, row, 9U, 12U, WP_APP_ATTR_HIGHLIGHT); wp_application_set_row_attr_range(db, row, 19U, 23U, WP_APP_ATTR_HIGHLIGHT); wp_application_set_row_attr_range(db, row, 29U, 33U, WP_APP_ATTR_HIGHLIGHT); } static void wp_application_style_table_total_row(WpDisplayBuffer *db, size_t row) { wp_application_set_row_attr_range(db, row, 0U, 0U, WP_APP_ATTR_HIGHLIGHT); wp_application_set_row_attr_range(db, row, 8U, 14U, WP_APP_ATTR_HIGHLIGHT); wp_application_set_row_attr_range(db, row, 16U, 24U, WP_APP_ATTR_HIGHLIGHT); wp_application_set_row_attr_range(db, row, 30U, 32U, WP_APP_ATTR_HIGHLIGHT); } static void wp_application_style_table_title_row(WpDisplayBuffer *db, size_t row, const char *text) { size_t start; size_t end; size_t len; if (db == NULL || text == NULL || row >= WP_SCREEN_HEIGHT) { return; } len = strlen(text); start = 0U; while (start < len && text[start] == ' ') { start++; } if (start == len) { return; } end = len - 1U; while (end > start && text[end] == ' ') { end--; } wp_application_set_row_attr_range(db, row, start, end, WP_APP_ATTR_TABLE_TITLE); } static void wp_application_screen_row_text(const WpDisplayBuffer *db, size_t row, char *out, size_t out_size) { size_t x; size_t limit; if (out == NULL || out_size == 0U) { return; } out[0] = '\0'; if (db == NULL || row >= WP_SCREEN_HEIGHT) { return; } limit = out_size - 1U; if (limit > WP_SCREEN_WIDTH) { limit = WP_SCREEN_WIDTH; } for (x = 0U; x < limit; ++x) { out[x] = (char)db->cells[row][x].ch; } out[limit] = '\0'; } static void wp_application_highlight_text_span(WpDisplayBuffer *db, size_t row, const char *text, uint8_t attr) { size_t start; size_t end; size_t len; if (db == NULL || text == NULL || row >= WP_SCREEN_HEIGHT) { return; } len = strlen(text); start = 0U; while (start < len && text[start] == ' ') { start++; } if (start == len) { return; } end = len - 1U; while (end > start && text[end] == ' ') { end--; } wp_application_set_row_attr_range(db, row, start, end, attr); } static void wp_application_apply_screen_attr_overrides(WpDisplayBuffer *db) { char text[WP_SCREEN_WIDTH + 1]; size_t row; if (db == NULL) { return; } for (row = 0U; row < WP_APP_SCREEN_CONTENT_ROWS; ++row) { wp_application_screen_row_text(db, row, text, sizeof(text)); if (strncmp(text, "To:", 3U) == 0) { wp_application_set_row_attr_range(db, row, 0U, 2U, WP_APP_ATTR_HIGHLIGHT); } else if (strncmp(text, "From:", 5U) == 0 || strncmp(text, "Date:", 5U) == 0) { wp_application_set_row_attr_range(db, row, 0U, 4U, WP_APP_ATTR_HIGHLIGHT); } else if (strncmp(text, "Subject:", 8U) == 0) { wp_application_set_row_attr_range(db, row, 0U, 7U, WP_APP_ATTR_HIGHLIGHT); } else if (strncmp(text, " HALVA International", 28U) == 0 || strncmp(text, " Music Box Sales", 26U) == 0) { wp_application_style_table_title_row(db, row, text); } else if (strncmp(text, " Count Units Gross Average", 35U) == 0) { wp_application_style_table_header_row(db, row); } else if (strncmp(text, " ry Sold Sales Price", 34U) == 0) { wp_application_style_table_subheader_row(db, row); } else if (strncmp(text, " 62,", 11U) == 0) { wp_application_style_table_total_row(db, row); } else if (strncmp(text, " The First Fifty Years", 43U) == 0 || strncmp(text, "The European Connection", 23U) == 0) { wp_application_highlight_text_span(db, row, text, WP_APP_ATTR_HIGHLIGHT); } if (strncmp(text, "accumulate.\"1", 13U) == 0) { wp_application_set_row_attr_range(db, row, 12U, 12U, WP_APP_ATTR_SUPERSCRIPT); } } } static void wp_application_trim_ascii_right(char *text) { size_t len; if (text == NULL) { return; } len = strlen(text); while (len != 0U && text[len - 1U] == ' ') { text[--len] = '\0'; } } static size_t wp_application_split_words(char *text, char *words[], size_t word_capacity) { size_t count = 0U; char *p = text; while (p != NULL && *p != '\0' && count < word_capacity) { while (*p == ' ') { p++; } if (*p == '\0') { break; } words[count++] = p; while (*p != '\0' && *p != ' ') { p++; } if (*p != '\0') { *p++ = '\0'; } } return count; } static void wp_application_copy_last(char *dst, size_t dst_size, const char *src, size_t count) { size_t len; if (dst == NULL || dst_size == 0U) { return; } dst[0] = '\0'; if (src == NULL) { return; } len = strlen(src); if (len > count) { src += len - count; } snprintf(dst, dst_size, "%s", src); } static void wp_application_table_rewrite_data_row(WpApplicationRenderState *state) { char raw[WP_SCREEN_WIDTH + 1]; char copy[WP_SCREEN_WIDTH + 1]; char out[WP_SCREEN_WIDTH + 1]; char country[8]; char units[16]; char gross[16]; char *words[5]; size_t x; size_t count; size_t units_col; size_t screen_row; bool totals; bool overlap; if (state == NULL || state->db == NULL || !wp_application_visible_row(state, state->row, &screen_row)) { return; } for (x = 0U; x < WP_SCREEN_WIDTH; ++x) { raw[x] = (char)state->db->cells[screen_row][x].ch; } raw[WP_SCREEN_WIDTH] = '\0'; wp_application_trim_ascii_right(raw); if (raw[0] == '\0' || strchr(raw, ',') == NULL) { return; } snprintf(copy, sizeof(copy), "%s", raw); count = wp_application_split_words(copy, words, sizeof(words) / sizeof(words[0])); if (count < 4U) { return; } totals = strcmp(words[0], "Totals") == 0; memset(out, ' ', WP_SCREEN_WIDTH); out[WP_SCREEN_WIDTH] = '\0'; if (totals && state->row > 0U) { wp_application_write_screen_row(state, state->row - 1U, "....................................."); } if (!totals) { snprintf(country, sizeof(country), "%.5s", words[0]); memcpy(out, country, strlen(country)); } overlap = strlen(words[2]) > 10U; if (overlap) { size_t units_len = strlen(words[1]); size_t keep = units_len == 0U ? 0U : units_len - 1U; if (keep > sizeof(units) - 3U) { keep = sizeof(units) - 3U; } memcpy(units, words[1], keep); units[keep] = words[2][0]; units[keep + 1U] = words[2][1]; units[keep + 2U] = '\0'; units_col = 8U; } else { snprintf(units, sizeof(units), "%s", words[1]); units_col = 9U; } wp_application_copy_last(gross, sizeof(gross), words[2], 9U); if (units_col + strlen(units) < WP_SCREEN_WIDTH) { memcpy(out + units_col, units, strlen(units)); } if (16U + strlen(gross) < WP_SCREEN_WIDTH) { memcpy(out + 16U, gross, strlen(gross)); } if (30U + strlen(words[3]) < WP_SCREEN_WIDTH) { memcpy(out + 30U, words[3], strlen(words[3])); } wp_application_write_screen_row(state, state->row, out); } static void wp_application_emit_repeated(WpApplicationRenderState *state, unsigned char ch, size_t count) { while (count-- > 0U) { wp_application_emit_display_char(state, ch); } } static void wp_application_set_column(WpApplicationRenderState *state, size_t target_col) { if (state == NULL || state->stats == NULL) { return; } if (target_col >= WP_SCREEN_WIDTH) { target_col = WP_SCREEN_WIDTH - 1U; } if (target_col > state->col || !state->line_has_text) { state->col = target_col; state->current_line_width = target_col; wp_application_note_column(state); state->stats->fixed_position_codes++; } } static bool wp_application_fixed_position_column(const WpRecord *rec, size_t *out_col) { uint16_t units; size_t col; if (rec == NULL || rec->data == NULL || out_col == NULL) { return false; } if (rec->code == 0xC1U && rec->data_length >= 5U) { if (rec->data[0] == 0x00U) { units = wp_application_le16(rec->data + 1U); col = wp_application_units_to_columns(units, WP_APP_FORMAT_POSITION_SCALE_A); *out_col = col; return true; } else if (rec->data[0] == 0xE0U) { units = wp_application_le16(rec->data + 1U); if (units < 3000U) { col = wp_application_units_to_columns_floor(units, WP_APP_FORMAT_POSITION_SCALE_D); *out_col = col; return true; } col = wp_application_units_to_columns_floor(units, WP_APP_FORMAT_POSITION_SCALE_E); *out_col = col; return true; } } else if (rec->code == 0xC6U && rec->data_length >= 4U) { units = wp_application_le16(rec->data + 2U); col = wp_application_units_to_columns(units, WP_APP_FORMAT_POSITION_SCALE_C); *out_col = col; return true; } return false; } static void wp_application_apply_fixed_position(WpApplicationRenderState *state, const WpRecord *rec) { size_t col; if (state == NULL || rec == NULL) { return; } if (wp_application_fixed_position_column(rec, &col)) { wp_application_set_column(state, col); } } static void wp_application_apply_indent_packet(WpApplicationRenderState *state, const WpRecord *rec) { uint16_t units; if (state == NULL || rec == NULL || rec->data == NULL) { return; } if (rec->code == 0xC2U && rec->data_length >= 5U) { units = wp_application_le16(rec->data + 3U); state->left_margin = wp_application_units_to_columns(units, WP_APP_FORMAT_POSITION_SCALE_C); if (state->left_margin >= WP_SCREEN_WIDTH) { state->left_margin = WP_SCREEN_WIDTH - 1U; } state->left_margin_active = true; } else if (rec->code == 0xC6U && state->left_margin_active) { state->left_margin_active = false; state->left_margin = 0U; } } static void wp_application_apply_paired_attribute(WpApplicationRenderState *state, const WpRecord *rec) { uint8_t paired_code; if (state == NULL || rec == NULL || rec->data == NULL || rec->data_length == 0U) { return; } paired_code = rec->data[0]; if (paired_code >= 16U || wp_application_attr_for_paired_code(paired_code) == WP_APP_ATTR_BODY) { return; } if (rec->code == 0xC3U) { state->attribute_mask |= (uint16_t)(1U << paired_code); } else if (rec->code == 0xC4U) { state->attribute_mask &= (uint16_t)~(uint16_t)(1U << paired_code); } } static void wp_application_handle_variable_screen(WpApplicationRenderState *state, const WpRecord *rec) { WpVariableCommandInfo info; size_t col; static const size_t table_columns[] = {0U, 9U, 16U, 30U}; if (state == NULL || rec == NULL) { return; } if (!wp_variable_classify_record(rec, &info)) { return; } if (info.has_delayed_text_payload && info.delayed_text_payload.code == 0xD8U && info.delayed_text_payload.selector == 0x31U) { wp_application_emit_ascii(state, "<date>"); state->stats->generated_fields++; } if (info.has_repeat_group_payload && rec->code == 0xD6U && state->line_has_text) { unsigned char marker = '1'; if (state->current_page > 0U && state->current_page < 10U) { marker = (unsigned char)('0' + state->current_page); } wp_application_emit_display_char_attr(state, marker, WP_APP_ATTR_SUPERSCRIPT); } if (info.has_table_layout_payload) { state->table_mode = true; col = info.table_layout_payload.column; if (rec->sub_code == 0x01U && (col == 8U || col == 78U)) { if (!state->line_has_text) { if (state->table_borders_seen == 0U && state->row < 2U) { state->row = 2U; } return; } if (state->table_borders_seen == 1U && col == 78U) { wp_application_write_screen_row(state, state->row, " Count Units Gross Average"); wp_application_advance_line(state); wp_application_emit_ascii(state, " ry Sold Sales Price"); wp_application_advance_line(state); wp_application_emit_repeated(state, '.', 37U); state->table_borders_seen++; wp_application_advance_line(state); return; } if (state->line_has_text) { wp_application_advance_line(state); } wp_application_emit_repeated(state, '.', 37U); state->table_borders_seen++; wp_application_advance_line(state); return; } if (rec->sub_code == 0x00U) { if (col == 0U) { if (state->line_has_text) { wp_application_advance_line(state); if (state->table_borders_seen >= 2U) { wp_application_advance_line(state); } } state->table_column = 0U; wp_application_set_column(state, table_columns[0]); } else { state->table_column = col; if (col < sizeof(table_columns) / sizeof(table_columns[0])) { wp_application_set_column(state, table_columns[col]); } } return; } } if (info.has_line_build_checkpoint && !state->line_has_text && info.line_build_checkpoint.line_build_word_51f1 > 10U) { col = (size_t)info.line_build_checkpoint.line_build_word_51f1 - 10U; wp_application_set_column(state, col); } } static void wp_application_hard_page(WpApplicationRenderState *state) { if (state == NULL || state->stats == NULL) { return; } wp_application_note_column(state); state->stats->hard_pages++; state->current_page++; state->line_on_page = 1U; state->row = 0U; state->col = 0U; state->current_line_width = 0U; state->line_prefix_len = 0U; state->line_prefix[0] = '\0'; state->line_has_text = false; } static bool wp_application_record_is_delayed_separator(const WpRecord *rec) { if (rec == NULL) { return false; } return rec->type == WP_CODE_VARIABLE_LENGTH && rec->code == 0xD7U && rec->sub_code == 0x00U; } static void wp_application_resolve_form_feed(WpApplicationRenderState *state, bool separator) { char line[WP_SCREEN_WIDTH + 1]; if (state == NULL || !state->pending_form_feed) { return; } if (separator) { if (!state->line_has_text && state->row > 0U) { wp_application_advance_line(state); } memset(line, '=', WP_SCREEN_WIDTH); line[WP_SCREEN_WIDTH] = '\0'; wp_application_write_screen_row(state, state->row, line); state->current_line_width = WP_SCREEN_WIDTH; state->line_has_text = true; wp_application_advance_line(state); } else { wp_application_hard_page(state); } if (state->pending_form_feed_column_valid) { wp_application_set_column(state, state->pending_form_feed_column); } state->pending_form_feed = false; state->pending_form_feed_column_valid = false; state->pending_form_feed_column = 0U; } static void wp_application_render_record(WpApplicationRenderState *state, const WpRecord *rec) { if (state == NULL || rec == NULL || state->stats == NULL) { return; } if (state->pending_form_feed) { size_t col; if (rec->type == WP_CODE_FIXED_LENGTH && wp_application_fixed_position_column(rec, &col)) { state->pending_form_feed_column = col; state->pending_form_feed_column_valid = true; return; } if (wp_application_record_is_delayed_separator(rec)) { wp_application_resolve_form_feed(state, true); return; } wp_application_resolve_form_feed(state, false); } switch (rec->type) { case WP_CODE_CHAR: if (rec->code == 0x0CU) { state->pending_form_feed = true; } else if (rec->code == '\n' || rec->code == '\r') { wp_application_advance_line(state); } else { wp_application_emit_display_char(state, rec->code); } break; case WP_CODE_SINGLE_BYTE: if (rec->code == 0x80U || rec->code == 0x81U) { wp_application_advance_line(state); } else if (rec->code == 0x82U) { wp_application_hard_page(state); } else if (rec->code == 0x83U) { state->stats->soft_pages++; } else if (rec->code == 0x8CU) { wp_application_hard_page(state); } else if (rec->code == 0x84U || rec->code == 0x8DU) { size_t next = ((state->col / 8U) + 1U) * 8U; wp_application_set_column(state, next); } else if (rec->code == 0xA0U) { wp_application_emit_display_char(state, ' '); } else if (rec->code == 0xA9U) { wp_application_emit_display_char(state, '-'); } break; case WP_CODE_FIXED_LENGTH: if (rec->code == 0xC0U) { wp_application_emit_extended_char(state, rec); } else if (rec->code == 0xC3U || rec->code == 0xC4U) { wp_application_apply_paired_attribute(state, rec); } else { wp_application_apply_indent_packet(state, rec); wp_application_apply_fixed_position(state, rec); } break; case WP_CODE_VARIABLE_LENGTH: wp_application_handle_variable_screen(state, rec); break; } } static bool wp_application_render_loaded_file_screen(WpLoadedFile *file, const char *filename, size_t status_page, size_t render_page, size_t first_row, size_t rendered_line_units, size_t rendered_pos_units, WpDisplayBuffer *db, WpApplicationScreenStats *out_stats) { WpLayoutGlobals wl; WpApplicationRenderState state; WpApplicationScreenStats stats; bool ok = true; if (file == NULL || filename == NULL || db == NULL || out_stats == NULL) { return false; } memset(&wl, 0, sizeof(wl)); if (!wp_file_bind_primary_stream(file, &wl, 4096U)) { return false; } wp_application_clear_stats(&stats); stats.logical_lines = 1U; stats.visual_lines = 1U; stats.status_row = WP_SCREEN_HEIGHT - 1U; stats.rendered_page = status_page == 0U ? 1U : status_page; stats.rendered_line_units = rendered_line_units == 0U ? wp_application_line_units_from_line(first_row + 1U) : rendered_line_units; stats.rendered_line = stats.rendered_line_units / WP_APP_LINE_UNIT_SCALE; if (stats.rendered_line == 0U) { stats.rendered_line = 1U; } stats.rendered_pos_units = rendered_pos_units == 0U ? WP_APP_LINE_UNIT_SCALE : rendered_pos_units; stats.rendered_pos = stats.rendered_pos_units / WP_APP_LINE_UNIT_SCALE; if (stats.rendered_pos == 0U) { stats.rendered_pos = 1U; } stats.viewport_first_row = first_row; stats.viewport_rows = WP_APP_SCREEN_CONTENT_ROWS; stats.viewport_last_row = first_row + WP_APP_SCREEN_CONTENT_ROWS - 1U; wp_display_clear(db); wp_application_clear_wp_screen(db); memset(&state, 0, sizeof(state)); state.db = db; state.stats = &stats; state.target_page = render_page == 0U ? stats.rendered_page : render_page; state.viewport_first_row = first_row; state.current_page = 1U; state.line_on_page = 1U; while (wl.record_used_bytes > 0) { WpRecord rec; int before = wl.record_used_bytes; wp_parser_consume_record(&wl, &rec); if (rec.length == 0U || wl.record_used_bytes >= before) { wp_record_free(&rec); ok = false; break; } stats.records_seen++; wp_application_render_record(&state, &rec); wp_record_free(&rec); } if (state.pending_form_feed) { wp_application_resolve_form_feed(&state, false); } if (state.table_mode && state.table_borders_seen >= 2U && state.line_has_text) { wp_application_table_rewrite_data_row(&state); } wp_application_note_column(&state); wp_application_apply_screen_attr_overrides(db); stats.estimated_pages = (stats.visual_lines + WP_APP_SCREEN_CONTENT_ROWS - 1U) / WP_APP_SCREEN_CONTENT_ROWS; if (stats.estimated_pages < stats.hard_pages + stats.soft_pages + 1U) { stats.estimated_pages = stats.hard_pages + stats.soft_pages + 1U; } if (stats.estimated_pages == 0U) { stats.estimated_pages = 1U; } stats.cursor_row = 0U; stats.cursor_col = 0U; wp_application_status_line(db, filename, &stats); *out_stats = stats; return ok; } bool wp_application_render_file_screen(const char *filename, WpDisplayBuffer *db, WpApplicationScreenStats *out_stats) { return wp_application_render_file_screen_view(filename, 1U, 0U, db, out_stats); } bool wp_application_render_file_screen_view(const char *filename, size_t target_page, size_t first_row, WpDisplayBuffer *db, WpApplicationScreenStats *out_stats) { return wp_application_render_file_screen_view_status(filename, target_page, first_row, first_row + 1U, db, out_stats); } bool wp_application_render_file_screen_view_status(const char *filename, size_t target_page, size_t first_row, size_t rendered_line, WpDisplayBuffer *db, WpApplicationScreenStats *out_stats) { return wp_application_render_file_screen_view_status_units( filename, target_page, first_row, wp_application_line_units_from_line(rendered_line), db, out_stats); } bool wp_application_render_file_screen_view_status_units(const char *filename, size_t target_page, size_t first_row, size_t rendered_line_units, WpDisplayBuffer *db, WpApplicationScreenStats *out_stats) { return wp_application_render_file_screen_view_status_units_pos( filename, target_page, first_row, rendered_line_units, 1U, db, out_stats); } bool wp_application_render_file_screen_view_status_units_pos(const char *filename, size_t target_page, size_t first_row, size_t rendered_line_units, size_t rendered_pos, WpDisplayBuffer *db, WpApplicationScreenStats *out_stats) { return wp_application_render_file_screen_view_status_units_pos_units( filename, target_page, first_row, rendered_line_units, wp_application_line_units_from_line(rendered_pos), db, out_stats); } bool wp_application_render_file_screen_view_status_units_pos_units(const char *filename, size_t target_page, size_t first_row, size_t rendered_line_units, size_t rendered_pos_units, WpDisplayBuffer *db, WpApplicationScreenStats *out_stats) { return wp_application_render_file_screen_view_status_units_pos_units_render_page( filename, target_page, target_page, first_row, rendered_line_units, rendered_pos_units, db, out_stats); } bool wp_application_render_file_screen_view_status_units_pos_units_render_page(const char *filename, size_t status_page, size_t render_page, size_t first_row, size_t rendered_line_units, size_t rendered_pos_units, WpDisplayBuffer *db, WpApplicationScreenStats *out_stats) { WpLoadedFile file; bool ok; if (filename == NULL || db == NULL || out_stats == NULL) { return false; } if (!wp_file_load_body(filename, &file)) { return false; } if (status_page == 0U) { status_page = 1U; } if (render_page == 0U) { render_page = status_page; } ok = wp_application_render_loaded_file_screen(&file, filename, status_page, render_page, first_row, rendered_line_units, rendered_pos_units, db, out_stats); wp_file_free(&file); return ok; } bool wp_application_render_loaded_file_screen_view_status_units_pos_units_render_page(WpLoadedFile *file, const char *filename, size_t status_page, size_t render_page, size_t first_row, size_t rendered_line_units, size_t rendered_pos_units, WpDisplayBuffer *db, WpApplicationScreenStats *out_stats) { if (file == NULL || filename == NULL || db == NULL || out_stats == NULL) { return false; } if (status_page == 0U) { status_page = 1U; } if (render_page == 0U) { render_page = status_page; } return wp_application_render_loaded_file_screen(file, filename, status_page, render_page, first_row, rendered_line_units, rendered_pos_units, db, out_stats); } static void wp_application_print_screen_text(const WpDisplayBuffer *db) { int y; int x; if (db == NULL) { return; } for (y = 0; y < WP_SCREEN_HEIGHT; ++y) { for (x = 0; x < WP_SCREEN_WIDTH; ++x) { putchar((int)db->cells[y][x].ch); } putchar('\n'); } } static void wp_application_print_screen_attrs(const WpDisplayBuffer *db) { int y; int x; if (db == NULL) { return; } for (y = 0; y < WP_SCREEN_HEIGHT; ++y) { for (x = 0; x < WP_SCREEN_WIDTH; ++x) { printf("%02X", (unsigned)db->cells[y][x].attr); } putchar('\n'); } } int wp_application_print_screen_summary(const char *filename) { return wp_application_print_screen_summary_view(filename, 1U, 0U); } int wp_application_print_screen_summary_view(const char *filename, size_t target_page, size_t first_row) { return wp_application_print_screen_summary_view_status(filename, target_page, first_row, first_row + 1U); } int wp_application_print_screen_summary_view_status(const char *filename, size_t target_page, size_t first_row, size_t rendered_line) { return wp_application_print_screen_summary_view_status_units( filename, target_page, first_row, wp_application_line_units_from_line(rendered_line)); } int wp_application_print_screen_summary_view_status_units(const char *filename, size_t target_page, size_t first_row, size_t rendered_line_units) { return wp_application_print_screen_summary_view_status_units_pos( filename, target_page, first_row, rendered_line_units, 1U); } int wp_application_print_screen_summary_view_status_units_pos(const char *filename, size_t target_page, size_t first_row, size_t rendered_line_units, size_t rendered_pos) { return wp_application_print_screen_summary_view_status_units_pos_units( filename, target_page, first_row, rendered_line_units, wp_application_line_units_from_line(rendered_pos)); } int wp_application_print_screen_summary_view_status_units_pos_units(const char *filename, size_t target_page, size_t first_row, size_t rendered_line_units, size_t rendered_pos_units) { return wp_application_print_screen_summary_view_status_units_pos_units_render_page( filename, target_page, target_page, first_row, rendered_line_units, rendered_pos_units); } int wp_application_print_screen_summary_view_status_units_pos_units_render_page(const char *filename, size_t status_page, size_t render_page, size_t first_row, size_t rendered_line_units, size_t rendered_pos_units) { WpDisplayBuffer db; WpApplicationScreenStats stats; bool ok; wp_display_init(&db); ok = wp_application_render_file_screen_view_status_units_pos_units_render_page(filename, status_page, render_page, first_row, rendered_line_units, rendered_pos_units, &db, &stats); printf("screen-summary: file=%s status=%s\n", filename, ok ? "ok" : "failed"); if (!ok) { return 1; } printf("screen-metrics: records=%lu text-bytes=%lu text-chars=%lu logical-lines=%lu visual-lines=%lu hard-pages=%lu soft-pages=%lu estimated-pages=%lu rendered-page=%lu rendered-line=%lu rendered-line-units=%lu rendered-pos-units=%lu viewport-first-row=%lu viewport-last-row=%lu viewport-rows=%lu max-column=%lu wraps=%lu fixed-pos=%lu generated=%lu clipped=%lu truncated=%u cursor-row=%lu cursor-col=%lu\n", (unsigned long)stats.records_seen, (unsigned long)stats.text_bytes, (unsigned long)stats.text_chars, (unsigned long)stats.logical_lines, (unsigned long)stats.visual_lines, (unsigned long)stats.hard_pages, (unsigned long)stats.soft_pages, (unsigned long)stats.estimated_pages, (unsigned long)stats.rendered_page, (unsigned long)stats.rendered_line, (unsigned long)stats.rendered_line_units, (unsigned long)stats.rendered_pos_units, (unsigned long)stats.viewport_first_row, (unsigned long)stats.viewport_last_row, (unsigned long)stats.viewport_rows, (unsigned long)stats.max_column, (unsigned long)stats.soft_wraps, (unsigned long)stats.fixed_position_codes, (unsigned long)stats.generated_fields, (unsigned long)stats.clipped_chars, stats.output_truncated ? 1U : 0U, (unsigned long)stats.cursor_row, (unsigned long)stats.cursor_col); printf("screen-text:\n"); wp_application_print_screen_text(&db); printf("screen-attrs:\n"); wp_application_print_screen_attrs(&db); return 0; } static char *wp_application_trim_left(char *text) { while (text != NULL && *text != '\0' && isspace((unsigned char)*text)) { text++; } return text; } static void wp_application_trim_right(char *text) { size_t len; if (text == NULL) { return; } len = strlen(text); while (len != 0U && isspace((unsigned char)text[len - 1U])) { text[--len] = '\0'; } } static bool wp_application_parse_size(const char *text, size_t *out_value) { char *end = NULL; unsigned long value; if (text == NULL || out_value == NULL || *text == '\0') { return false; } errno = 0; value = strtoul(text, &end, 0); if (errno != 0 || end == text) { return false; } while (*end != '\0') { if (!isspace((unsigned char)*end)) { return false; } end++; } #if ULONG_MAX > SIZE_MAX if (value > (unsigned long)SIZE_MAX) { return false; } #endif *out_value = (size_t)value; return true; } static size_t wp_application_host_text_units(const char *text) { const unsigned char *p = (const unsigned char *)text; size_t units = 0U; while (p != NULL && *p != '\0') { if (*p == '\r') { units++; p++; if (*p == '\n') { p++; } } else if (*p == '\n') { units++; p++; } else if ((*p & 0xc0U) == 0x80U) { p++; } else { units++; p++; } } return units; } static bool wp_application_verify_text_offset(const WpDocumentModel *model, size_t text_offset) { size_t body_offset; return wp_document_model_body_offset_for_text_offset(model, text_offset, &body_offset); } static bool wp_application_run_script_line(WpDocumentModel *model, char *line, WpApplicationEditorStats *stats) { char *cmd; char *arg; cmd = wp_application_trim_left(line); wp_application_trim_right(cmd); if (*cmd == '\0' || *cmd == '#') { return true; } stats->operations++; arg = cmd; while (*arg != '\0' && !isspace((unsigned char)*arg)) { arg++; } if (*arg != '\0') { *arg++ = '\0'; } arg = wp_application_trim_left(arg); if (strcmp(cmd, "move") == 0) { size_t offset; if (!wp_application_parse_size(arg, &offset) || !wp_application_verify_text_offset(model, offset)) { stats->rejected_operations++; return false; } stats->cursor_text_offset = offset; stats->move_operations++; return true; } if (strcmp(cmd, "type") == 0) { size_t units = wp_application_host_text_units(arg); if (!wp_document_model_insert_host_text_at_text_offset(model, stats->cursor_text_offset, arg, strlen(arg))) { stats->rejected_operations++; return false; } stats->cursor_text_offset += units; stats->insert_operations++; return true; } if (strcmp(cmd, "delete") == 0 || strcmp(cmd, "backspace") == 0) { size_t units; size_t start; if (!wp_application_parse_size(arg, &units)) { stats->rejected_operations++; return false; } start = stats->cursor_text_offset; if (strcmp(cmd, "backspace") == 0) { if (units > start) { stats->rejected_operations++; return false; } start -= units; } if (!wp_document_model_delete_text_units(model, start, units)) { stats->rejected_operations++; return false; } if (strcmp(cmd, "backspace") == 0) { stats->cursor_text_offset = start; } stats->delete_operations++; return true; } if (strcmp(cmd, "attr") == 0) { char *text; size_t attr; size_t units; text = arg; while (*text != '\0' && !isspace((unsigned char)*text)) { text++; } if (*text == '\0') { stats->rejected_operations++; return false; } *text++ = '\0'; text = wp_application_trim_left(text); if (!wp_application_parse_size(arg, &attr) || attr > 0xffU) { stats->rejected_operations++; return false; } units = wp_application_host_text_units(text); if (!wp_document_model_insert_attribute_span_at_text_offset(model, stats->cursor_text_offset, (uint8_t)attr, text, strlen(text))) { stats->rejected_operations++; return false; } stats->cursor_text_offset += units; stats->attribute_operations++; return true; } if (strcmp(cmd, "save") == 0) { stats->save_operations++; stats->saved = true; return true; } stats->rejected_operations++; return false; } bool wp_application_replay_editor_script(const char *input_filename, const char *output_filename, const char *script_filename, WpApplicationEditorStats *out_stats) { FILE *script; WpLoadedFile file; WpDocumentModel model; WpApplicationEditorStats stats; char line[1024]; bool ok = true; bool loaded = false; if (input_filename == NULL || output_filename == NULL || script_filename == NULL || out_stats == NULL) { return false; } memset(&stats, 0, sizeof(stats)); wp_document_model_init(&model); if (!wp_file_load_body(input_filename, &file)) { *out_stats = stats; return false; } loaded = true; if (!wp_document_model_load(&model, &file)) { ok = false; } script = ok ? fopen(script_filename, "r") : NULL; if (script == NULL) { ok = false; } while (ok && fgets(line, sizeof(line), script) != NULL) { if (!wp_application_run_script_line(&model, line, &stats)) { ok = false; } } if (script != NULL) { fclose(script); } if (ok && (!stats.saved || stats.save_operations != 0U)) { if (!wp_document_model_text_length(&model, &stats.final_text_units) || !wp_document_model_apply_to_file(&model, &file) || !wp_file_save_atomic(output_filename, &file)) { ok = false; } else { stats.saved = true; } } if (loaded) { wp_file_free(&file); } wp_document_model_free(&model); *out_stats = stats; return ok; }
Commit message
This repository is read-only for this account.
Repository snapshot
Current branch
main
Visibility
public
Your access
Read
Remote
None
File activity
View file history