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
/
unix-word-unperfect
/
main.c
File editor
#include "wp_document_analyzer.h" #include "wp_document_model.h" #include "wp_file_format.h" #include "wp_msdos_frontend.h" #include "wp_text_export.h" #include <errno.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define WORD_UNPERFECT_UNIX_NAME "unix-word-unperfect" #define UNIX_WP_INITIAL_EXPORT_CAPACITY 4096U #define UNIX_WP_LINE_CAPACITY 4096U #define UNIX_WP_DEFAULT_LPT "/dev/lp0" #define UNIX_WP_LPT_ENV "WORD_UNPERFECT_LPT" static void usage(FILE *out) { fprintf(out, "usage:\n" " %s [IN] start WordPerfect-style Linux TUI\n" " %s --tui [IN] start WordPerfect-style Linux TUI\n" " %s --stdio start libc stdio fallback editor\n" " %s --export IN [OUT] export WP document text\n" " %s --print IN [LPT] print exported text to Linux LPT\n" " %s --analyze IN print parser/analyzer stats\n" " %s --roundtrip IN OUT load and save byte-preserved WP file\n" " %s --new OUT [TEXT] write a new WP document\n" " %s --append IN OUT TEXT append text and save\n" " %s --help show this help\n", WORD_UNPERFECT_UNIX_NAME, WORD_UNPERFECT_UNIX_NAME, WORD_UNPERFECT_UNIX_NAME, WORD_UNPERFECT_UNIX_NAME, WORD_UNPERFECT_UNIX_NAME, WORD_UNPERFECT_UNIX_NAME, WORD_UNPERFECT_UNIX_NAME, WORD_UNPERFECT_UNIX_NAME, WORD_UNPERFECT_UNIX_NAME, WORD_UNPERFECT_UNIX_NAME); } static int write_all(FILE *out, const char *bytes, size_t len) { return out != NULL && (len == 0U || fwrite(bytes, 1U, len, out) == len); } static const char * default_lpt_path(void) { const char *path = getenv(UNIX_WP_LPT_ENV); if (path != NULL && path[0] != '\0') { return path; } return UNIX_WP_DEFAULT_LPT; } static int open_output(const char *path, FILE **out_file) { FILE *f; if (out_file == NULL) { return 0; } *out_file = NULL; if (path == NULL) { *out_file = stdout; return 1; } f = fopen(path, "wb"); if (f == NULL) { fprintf(stderr, "%s: could not open '%s' for writing: %s\n", WORD_UNPERFECT_UNIX_NAME, path, strerror(errno)); return 0; } *out_file = f; return 1; } static int close_output(FILE *f) { if (f == NULL || f == stdout) { return 1; } if (fclose(f) != 0) { fprintf(stderr, "%s: close failed: %s\n", WORD_UNPERFECT_UNIX_NAME, strerror(errno)); return 0; } return 1; } static int copy_text(char *dst, size_t dst_size, const char *src) { size_t len; if (dst == NULL || dst_size == 0U || src == NULL) { return 0; } len = strlen(src); if (len >= dst_size) { return 0; } memcpy(dst, src, len + 1U); return 1; } static int init_blank_file(WpLoadedFile *file) { uint8_t *prefix; if (file == NULL) { return 0; } memset(file, 0, sizeof(*file)); prefix = (uint8_t *)calloc(WP_FILE_PREFIX_SIZE, 1U); if (prefix == NULL) { return 0; } prefix[0] = 0xffU; prefix[1] = 'W'; prefix[2] = 'P'; prefix[3] = 'C'; prefix[4] = (uint8_t)(WP_FILE_PREFIX_SIZE & 0xffU); prefix[5] = 0x00U; prefix[6] = 0x00U; prefix[7] = 0x00U; prefix[8] = 0x01U; prefix[9] = 0x0aU; prefix[10] = 0x00U; prefix[11] = 0x01U; file->header.signature[0] = prefix[0]; file->header.signature[1] = prefix[1]; file->header.signature[2] = prefix[2]; file->header.signature[3] = prefix[3]; file->header.data_offset = WP_FILE_PREFIX_SIZE; file->header.product_type = 0x01U; file->header.file_type = 0x0aU; file->header.major_version = 0x00U; file->header.minor_version = 0x01U; file->header.file_size = WP_FILE_PREFIX_SIZE; file->prefix_bytes = prefix; file->prefix_size = WP_FILE_PREFIX_SIZE; return 1; } static int load_model_from_file(const char *path, WpLoadedFile *file, WpDocumentModel *model) { if (path == NULL || file == NULL || model == NULL) { return 0; } memset(file, 0, sizeof(*file)); wp_document_model_init(model); if (!wp_file_load_body(path, file)) { fprintf(stderr, "%s: could not load '%s'\n", WORD_UNPERFECT_UNIX_NAME, path); return 0; } if (!wp_document_model_load(model, file)) { fprintf(stderr, "%s: could not model '%s'\n", WORD_UNPERFECT_UNIX_NAME, path); wp_file_free(file); return 0; } return 1; } static int load_blank_model(WpLoadedFile *file, WpDocumentModel *model) { if (file == NULL || model == NULL) { return 0; } wp_document_model_init(model); if (!init_blank_file(file) || !wp_document_model_load(model, file)) { wp_document_model_free(model); wp_file_free(file); return 0; } return 1; } static int save_model(const char *path, WpLoadedFile *file, WpDocumentModel *model) { if (path == NULL || file == NULL || model == NULL) { return 0; } if (!wp_document_model_apply_to_file(model, file) || !wp_file_save(path, file)) { fprintf(stderr, "%s: could not save '%s'\n", WORD_UNPERFECT_UNIX_NAME, path); return 0; } return 1; } static int append_text(WpDocumentModel *model, const char *text) { size_t len; size_t text_units; if (model == NULL || text == NULL) { return 0; } len = strlen(text); if (!wp_document_model_text_length(model, &text_units)) { return 0; } return wp_document_model_insert_host_text_at_text_offset(model, text_units, text, len); } static int export_loaded_file_to_path(WpLoadedFile *file, const char *output_path, int final_form_feed) { WpTextExportOptions options; WpTextExportStats stats; FILE *out = NULL; char *buffer = NULL; size_t capacity = UNIX_WP_INITIAL_EXPORT_CAPACITY; size_t out_len = 0U; int ok = 0; if (file == NULL) { return 0; } wp_text_export_default_options(&options); for (;;) { free(buffer); buffer = (char *)malloc(capacity); if (buffer == NULL) { fprintf(stderr, "%s: out of memory\n", WORD_UNPERFECT_UNIX_NAME); goto done; } if (wp_text_export_loaded_file(file, buffer, capacity, &out_len, &options, &stats)) { break; } if (!stats.output_truncated || capacity > ((size_t)-1) / 2U) { fprintf(stderr, "%s: export failed\n", WORD_UNPERFECT_UNIX_NAME); goto done; } capacity *= 2U; } if (!open_output(output_path, &out)) { goto done; } ok = write_all(out, buffer, out_len); if (ok && final_form_feed) { ok = write_all(out, "\f", 1U); } ok = ok && close_output(out); out = NULL; done: if (out != NULL) { close_output(out); } free(buffer); return ok; } static int export_file(const char *input_path, const char *output_path) { WpLoadedFile file; int ok; memset(&file, 0, sizeof(file)); if (!wp_file_load_body(input_path, &file)) { fprintf(stderr, "%s: could not load '%s'\n", WORD_UNPERFECT_UNIX_NAME, input_path); return 1; } ok = export_loaded_file_to_path(&file, output_path, 0); wp_file_free(&file); return ok ? 0 : 1; } static int print_file(const char *input_path, const char *lpt_path) { WpLoadedFile file; const char *target = lpt_path; int ok; if (target == NULL || target[0] == '\0') { target = default_lpt_path(); } memset(&file, 0, sizeof(file)); if (!wp_file_load_body(input_path, &file)) { fprintf(stderr, "%s: could not load '%s'\n", WORD_UNPERFECT_UNIX_NAME, input_path); return 1; } ok = export_loaded_file_to_path(&file, target, 1); wp_file_free(&file); if (!ok) { fprintf(stderr, "%s: could not print '%s' to '%s'\n", WORD_UNPERFECT_UNIX_NAME, input_path, target); } return ok ? 0 : 1; } static int export_model_text(const char *output_path, WpLoadedFile *file, WpDocumentModel *model) { if (output_path == NULL || file == NULL || model == NULL || !wp_document_model_apply_to_file(model, file)) { return 0; } return export_loaded_file_to_path(file, output_path, 0); } static int print_model_text(const char *lpt_path, WpLoadedFile *file, WpDocumentModel *model) { const char *target = lpt_path; if (file == NULL || model == NULL || !wp_document_model_apply_to_file(model, file)) { return 0; } if (target == NULL || target[0] == '\0') { target = default_lpt_path(); } return export_loaded_file_to_path(file, target, 1); } static int analyze_file(const char *path) { WpLoadedFile file; WpDocumentStats stats; int ok; memset(&file, 0, sizeof(file)); if (!wp_file_load_body(path, &file)) { fprintf(stderr, "%s: could not load '%s'\n", WORD_UNPERFECT_UNIX_NAME, path); return 1; } wp_document_stats_clear(&stats); ok = wp_document_analyze_loaded_file(&file, &stats); if (!ok) { fprintf(stderr, "%s: analyzer failed for '%s'\n", WORD_UNPERFECT_UNIX_NAME, path); wp_file_free(&file); return 1; } printf("file: %s\n", path); printf("type: 0x%02X (%s)\n", (unsigned int)file.header.file_type, wp_file_type_name(file.header.file_type)); printf("data-offset: %lu\n", (unsigned long)file.header.data_offset); printf("body-bytes: %lu\n", (unsigned long)file.logical_size); printf("records: %lu\n", (unsigned long)stats.records_seen); printf("chars: %lu\n", (unsigned long)stats.char_records); printf("single-byte-codes: %lu\n", (unsigned long)stats.single_byte_codes); printf("fixed-packets: %lu\n", (unsigned long)stats.fixed_length_codes); printf("variable-packets: %lu\n", (unsigned long)stats.variable_length_codes); printf("incomplete-records: %lu\n", (unsigned long)stats.incomplete_records); printf("mismatched-trailers: %lu\n", (unsigned long)stats.mismatched_trailers); wp_file_free(&file); return 0; } static int roundtrip_file(const char *input_path, const char *output_path) { WpLoadedFile file; int ok; memset(&file, 0, sizeof(file)); if (!wp_file_load_body(input_path, &file)) { fprintf(stderr, "%s: could not load '%s'\n", WORD_UNPERFECT_UNIX_NAME, input_path); return 1; } ok = wp_file_save(output_path, &file); wp_file_free(&file); if (!ok) { fprintf(stderr, "%s: could not write '%s'\n", WORD_UNPERFECT_UNIX_NAME, output_path); return 1; } return 0; } static int new_file(const char *output_path, const char *text) { WpLoadedFile file; WpDocumentModel model; int ok; memset(&file, 0, sizeof(file)); if (!load_blank_model(&file, &model)) { fprintf(stderr, "%s: could not create blank document\n", WORD_UNPERFECT_UNIX_NAME); return 1; } if (text != NULL && !append_text(&model, text)) { fprintf(stderr, "%s: could not insert initial text\n", WORD_UNPERFECT_UNIX_NAME); wp_document_model_free(&model); wp_file_free(&file); return 1; } ok = save_model(output_path, &file, &model); wp_document_model_free(&model); wp_file_free(&file); return ok ? 0 : 1; } static int append_file(const char *input_path, const char *output_path, const char *text) { WpLoadedFile file; WpDocumentModel model; int ok; if (!load_model_from_file(input_path, &file, &model)) { return 1; } if (!append_text(&model, text)) { fprintf(stderr, "%s: could not append text\n", WORD_UNPERFECT_UNIX_NAME); wp_document_model_free(&model); wp_file_free(&file); return 1; } ok = save_model(output_path, &file, &model); wp_document_model_free(&model); wp_file_free(&file); return ok ? 0 : 1; } static void trim_newline(char *line) { size_t len; if (line == NULL) { return; } len = strlen(line); while (len != 0U && (line[len - 1U] == '\n' || line[len - 1U] == '\r')) { line[--len] = '\0'; } } static int stdio_editor(void) { WpLoadedFile file; WpDocumentModel model; char line[UNIX_WP_LINE_CAPACITY]; char save_path[UNIX_WP_LINE_CAPACITY]; int dirty = 0; int have_path = 0; memset(&file, 0, sizeof(file)); save_path[0] = '\0'; if (!load_blank_model(&file, &model)) { fprintf(stderr, "%s: could not start editor\n", WORD_UNPERFECT_UNIX_NAME); return 1; } puts("word unperfect unix libc editor"); puts("Commands: .save FILE, .open FILE, .export FILE, .print [LPT], .quit"); puts("Type ordinary lines to append text."); for (;;) { fputs(dirty ? "untitled* > " : "untitled > ", stdout); fflush(stdout); if (fgets(line, sizeof(line), stdin) == NULL) { break; } trim_newline(line); if (strncmp(line, ".save ", 6U) == 0) { if (save_model(line + 6U, &file, &model)) { have_path = copy_text(save_path, sizeof(save_path), line + 6U); dirty = 0; puts("saved"); } } else if (strcmp(line, ".save") == 0 && have_path) { if (save_model(save_path, &file, &model)) { dirty = 0; puts("saved"); } } else if (strncmp(line, ".open ", 6U) == 0) { WpLoadedFile next_file; WpDocumentModel next_model; if (load_model_from_file(line + 6U, &next_file, &next_model)) { wp_document_model_free(&model); wp_file_free(&file); file = next_file; model = next_model; have_path = copy_text(save_path, sizeof(save_path), line + 6U); dirty = 0; puts("opened"); } } else if (strncmp(line, ".export ", 8U) == 0) { if (export_model_text(line + 8U, &file, &model)) { puts("exported text"); } } else if (strcmp(line, ".print") == 0) { if (print_model_text(NULL, &file, &model)) { puts("printed"); } } else if (strncmp(line, ".print ", 7U) == 0) { if (print_model_text(line + 7U, &file, &model)) { puts("printed"); } } else if (strcmp(line, ".quit") == 0) { break; } else if (line[0] == '.') { puts("unknown command"); } else { char with_return[UNIX_WP_LINE_CAPACITY + 2U]; size_t len = strlen(line); if (len > UNIX_WP_LINE_CAPACITY - 2U) { puts("line too long"); continue; } memcpy(with_return, line, len); with_return[len++] = '\n'; with_return[len] = '\0'; if (append_text(&model, with_return)) { dirty = 1; } else { puts("edit rejected"); } } } wp_document_model_free(&model); wp_file_free(&file); return 0; } int main(int argc, char **argv) { if (argc == 1) { return wp_msdos_frontend_run(NULL); } if (argc == 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)) { usage(stdout); return 0; } if (argc == 2 && strcmp(argv[1], "--stdio") == 0) { return stdio_editor(); } if (argc == 2 && strcmp(argv[1], "--tui") == 0) { return wp_msdos_frontend_run(NULL); } if (argc == 3 && strcmp(argv[1], "--tui") == 0) { return wp_msdos_frontend_run(argv[2]); } if (argc == 3 && strcmp(argv[1], "--export") == 0) { return export_file(argv[2], NULL); } if (argc == 4 && strcmp(argv[1], "--export") == 0) { return export_file(argv[2], argv[3]); } if (argc == 3 && strcmp(argv[1], "--print") == 0) { return print_file(argv[2], NULL); } if (argc == 4 && strcmp(argv[1], "--print") == 0) { return print_file(argv[2], argv[3]); } if (argc == 3 && strcmp(argv[1], "--analyze") == 0) { return analyze_file(argv[2]); } if (argc == 4 && strcmp(argv[1], "--roundtrip") == 0) { return roundtrip_file(argv[2], argv[3]); } if (argc == 3 && strcmp(argv[1], "--new") == 0) { return new_file(argv[2], NULL); } if (argc == 4 && strcmp(argv[1], "--new") == 0) { return new_file(argv[2], argv[3]); } if (argc == 5 && strcmp(argv[1], "--append") == 0) { return append_file(argv[2], argv[3], argv[4]); } if (argc == 2 && argv[1][0] != '-') { return wp_msdos_frontend_run(argv[1]); } usage(stderr); return 1; }
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