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_text_export.c
File editor
#include "wp_text_export.h" #include "wp_control_codes.h" #include "wp_document_analyzer.h" #include "wp_fixed_codes.h" #include "wp_layout_engine.h" #include "wp_record_parser.h" #include "wp_resource_manager.h" #include "wp_variable_codes.h" #include <stdio.h> #include <string.h> typedef struct WpTextWriter { char *out; size_t cap; size_t len; size_t chars; size_t column; bool truncated; } WpTextWriter; static void wp_text_writer_init(WpTextWriter *wr, char *out, size_t cap) { memset(wr, 0, sizeof(*wr)); wr->out = out; wr->cap = cap; if (wr->out != NULL && wr->cap > 0) { wr->out[0] = '\0'; } } static void wp_text_append_byte(WpTextWriter *wr, char ch) { if (wr->out == NULL || wr->cap == 0) { wr->truncated = true; return; } if (wr->len + 1U >= wr->cap) { wr->truncated = true; wr->out[wr->cap - 1U] = '\0'; return; } wr->out[wr->len++] = ch; wr->out[wr->len] = '\0'; wr->chars++; if (ch == '\n' || ch == '\r' || ch == '\f') { wr->column = 0; } else if (ch == '\t') { wr->column = (wr->column + 8U) & ~7U; } else { wr->column++; } } static void wp_text_append_ascii(WpTextWriter *wr, const char *text) { while (text != NULL && *text != '\0') { wp_text_append_byte(wr, *text++); } } static void wp_text_append_utf8(WpTextWriter *wr, uint32_t cp) { char tmp[4]; size_t n = 0; if (cp <= 0x7FU) { wp_text_append_byte(wr, (char)cp); return; } if (cp <= 0x7FFU) { tmp[n++] = (char)(0xC0U | (cp >> 6)); tmp[n++] = (char)(0x80U | (cp & 0x3FU)); } else if (cp <= 0xFFFFU) { tmp[n++] = (char)(0xE0U | (cp >> 12)); tmp[n++] = (char)(0x80U | ((cp >> 6) & 0x3FU)); tmp[n++] = (char)(0x80U | (cp & 0x3FU)); } else if (cp <= 0x10FFFFU) { tmp[n++] = (char)(0xF0U | (cp >> 18)); tmp[n++] = (char)(0x80U | ((cp >> 12) & 0x3FU)); tmp[n++] = (char)(0x80U | ((cp >> 6) & 0x3FU)); tmp[n++] = (char)(0x80U | (cp & 0x3FU)); } else { wp_text_append_byte(wr, '?'); return; } if (wr->out == NULL || wr->cap == 0 || wr->len + n >= wr->cap) { wr->truncated = true; if (wr->out != NULL && wr->cap > 0) { wr->out[wr->cap - 1U] = '\0'; } return; } memcpy(wr->out + wr->len, tmp, n); wr->len += n; wr->out[wr->len] = '\0'; wr->chars++; wr->column++; } static void wp_text_append_tab(WpTextWriter *wr, bool preserve_tabs) { if (preserve_tabs) { wp_text_append_byte(wr, '\t'); } else { size_t spaces = 8U - (wr->column & 7U); if (spaces == 0) { spaces = 8U; } while (spaces-- > 0) { wp_text_append_byte(wr, ' '); } } } static const char *wp_text_code_label(uint8_t code, uint8_t sub_code) { const char *marker; (void)sub_code; if (code < 0xC0U) { marker = wp_control_code_marker(code); if (marker != NULL) { return marker; } } switch (code) { case 0xC0: return "[ExtChar]"; case 0xC4: return "[Bold]"; case 0xC5: return "[Undln]"; default: return NULL; } } static bool wp_text_emit_extended_char(const WpRecord *rec, WpTextWriter *wr) { uint8_t set = 0; uint8_t index = 0; uint32_t cp; if (rec == NULL || wr == NULL || rec->code != 0xC0) { return false; } 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]; } else { return false; } cp = wp_char_to_unicode(set, index); if (cp == 0) { return false; } wp_text_append_utf8(wr, cp); return true; } static void wp_text_append_record_marker(WpTextWriter *wr, const WpRecord *rec, WpTextExportStats *stats) { char marker[512]; char detail[256]; const char *label; size_t used; if (wr == NULL || rec == NULL) { return; } detail[0] = '\0'; if (rec->type == WP_CODE_SINGLE_BYTE) { label = wp_text_code_label(rec->code, rec->sub_code); if (label != NULL) { snprintf(marker, sizeof(marker), "%s", label); } else { snprintf(marker, sizeof(marker), "[%02X]", (unsigned)rec->code); } } else if (rec->type == WP_CODE_FIXED_LENGTH) { label = wp_fixed_code_name(rec->code); if (!wp_fixed_code_describe_payload(rec->code, rec->data, rec->data_length, detail, sizeof(detail))) { detail[0] = '\0'; } snprintf(marker, sizeof(marker), "[%02X %s%s%s%s]", (unsigned)rec->code, label, detail[0] != '\0' ? " " : "", detail, rec->is_complete ? "" : " incomplete"); } else if (rec->type == WP_CODE_VARIABLE_LENGTH) { label = wp_variable_code_name(rec->code, rec->sub_code); if (!wp_variable_describe_payload(rec, detail, sizeof(detail))) { detail[0] = '\0'; } used = (size_t)snprintf(marker, sizeof(marker), "[%02X:%02X %s%s%s", (unsigned)rec->code, (unsigned)rec->sub_code, label, detail[0] != '\0' ? " " : "", detail); if (used >= sizeof(marker)) { used = sizeof(marker) - 1U; } if (!rec->is_complete) { snprintf(marker + used, sizeof(marker) - used, " incomplete"); used = strlen(marker); } else if (rec->trailer_present && !rec->trailer_matches) { snprintf(marker + used, sizeof(marker) - used, " bad-trailer"); used = strlen(marker); } snprintf(marker + used, sizeof(marker) - used, "]"); } else { label = wp_document_code_label(rec->code, rec->sub_code); snprintf(marker, sizeof(marker), "[%02X %s]", (unsigned)rec->code, label); } wp_text_append_ascii(wr, marker); if (stats != NULL) { stats->code_markers_emitted++; } } void wp_text_export_default_options(WpTextExportOptions *options) { if (options == NULL) { return; } options->preserve_tabs = true; options->emit_form_feed = true; options->emit_code_markers = false; } void wp_text_export_clear_stats(WpTextExportStats *stats) { if (stats != NULL) { memset(stats, 0, sizeof(*stats)); } } bool wp_text_export_stream(WpLayoutGlobals *wl, char *out, size_t out_capacity, size_t *out_len, const WpTextExportOptions *options, WpTextExportStats *stats) { WpTextExportOptions local_options; WpTextExportStats local_stats; WpTextWriter wr; WpLayoutGlobals cursor; if (out_len != NULL) { *out_len = 0; } if (wl == NULL || out == NULL || out_capacity == 0) { return false; } if (options == NULL) { wp_text_export_default_options(&local_options); options = &local_options; } wp_text_export_clear_stats(&local_stats); wp_text_writer_init(&wr, out, out_capacity); cursor = *wl; while (cursor.record_used_bytes > 0) { WpRecord rec; const char *label; wp_parser_consume_record(&cursor, &rec); if (rec.length == 0) { wp_record_free(&rec); break; } local_stats.records_seen++; local_stats.bytes_consumed += rec.length; if (rec.type == WP_CODE_CHAR) { if (rec.code == '\r' || rec.code == '\n') { wp_text_append_byte(&wr, '\n'); } else if (rec.code >= 0x20 && rec.code < 0x7F) { wp_text_append_byte(&wr, (char)rec.code); } else if (options->emit_code_markers) { char marker[12]; snprintf(marker, sizeof(marker), "[%02X]", (unsigned)rec.code); wp_text_append_ascii(&wr, marker); } } else if (rec.type == WP_CODE_SINGLE_BYTE) { local_stats.format_codes_seen++; switch (rec.code) { case 0x80: /* Hard return. */ case 0x81: /* Soft return. */ wp_text_append_byte(&wr, '\n'); break; case 0x82: /* Hard page. */ case 0x83: /* Soft page. */ wp_text_append_byte(&wr, options->emit_form_feed ? '\f' : '\n'); break; case 0x84: /* Tab. */ case 0x8D: /* Indent. */ wp_text_append_tab(&wr, options->preserve_tabs); break; default: if (options->emit_code_markers) { wp_text_append_record_marker(&wr, &rec, &local_stats); } break; } } else { local_stats.format_codes_seen++; if (rec.type == WP_CODE_FIXED_LENGTH) { local_stats.fixed_packets_seen++; } else if (rec.type == WP_CODE_VARIABLE_LENGTH) { local_stats.variable_packets_seen++; } if (!rec.is_complete && !rec.trailer_present) { local_stats.incomplete_records++; } if (rec.trailer_present && !rec.trailer_matches) { local_stats.mismatched_trailers++; } if (wp_text_emit_extended_char(&rec, &wr)) { local_stats.extended_chars_emitted++; } else if (options->emit_code_markers) { wp_text_append_record_marker(&wr, &rec, &local_stats); } } wp_record_free(&rec); } local_stats.text_bytes_written = wr.len; local_stats.text_chars_emitted = wr.chars; local_stats.output_truncated = wr.truncated; if (out_len != NULL) { *out_len = wr.len; } if (stats != NULL) { *stats = local_stats; } return !wr.truncated; } bool wp_text_export_loaded_file(WpLoadedFile *file, char *out, size_t out_capacity, size_t *out_len, const WpTextExportOptions *options, WpTextExportStats *stats) { WpLayoutGlobals wl; if (file == NULL) { return false; } memset(&wl, 0, sizeof(wl)); if (!wp_file_bind_primary_stream(file, &wl, 4096U)) { return false; } return wp_text_export_stream(&wl, out, out_capacity, out_len, options, stats); } bool wp_text_export_file_to_buffer(const char *filename, char *out, size_t out_capacity, size_t *out_len, const WpTextExportOptions *options, WpTextExportStats *stats) { WpLoadedFile file; bool ok; if (filename == NULL) { return false; } if (!wp_file_load_body(filename, &file)) { return false; } ok = wp_text_export_loaded_file(&file, out, out_capacity, out_len, options, stats); wp_file_free(&file); 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