From b184852a50f31d90fae3d67a822ffa843fd30d76 Mon Sep 17 00:00:00 2001 From: Dino Date: Mon, 12 Jan 2026 19:32:27 -0500 Subject: [PATCH] ruledit/ruleup: improve format of output RM #1903 --- utility/registry_ini.c | 245 ++++++++++++++++++++++++++++++++++++++++- utility/support.c | 35 ------ utility/support.h | 1 - 3 files changed, 240 insertions(+), 41 deletions(-) diff --git a/utility/registry_ini.c b/utility/registry_ini.c index 0685521c41..9a5f21591b 100644 --- a/utility/registry_ini.c +++ b/utility/registry_ini.c @@ -15,7 +15,7 @@ the idea with this file is to create something similar to the ms-windows .ini files functions. however the interface is nice. ie: - secfile_lookup_str(file, "player%d.unit%d.name", plrno, unitno); + secfile_lookup_str(file, "player%d.unit%d.name", plrno, unitno); ***************************************************************************/ /************************************************************************** @@ -152,6 +152,7 @@ #include #endif +#include #include #include #include @@ -170,6 +171,9 @@ #include "shared.h" #include "support.h" +/* common */ +#include "fc_types.h" + #include "registry_ini.h" #define MAX_LEN_SECPATH 1024 @@ -622,6 +626,166 @@ static bool is_legal_table_entry_name(char c, bool num) return (num ? fc_isalnum(c) : fc_isalpha(c)) || c == '_'; } +/**********************************************************************//** + Utility routine used in word_wrap_helptext() + + returns whether it's OK to insert a line break before the passed in char, + most non-alphanumeric are OK, test high bit to rule out UTF-8. +**************************************************************************/ +static bool is_delim(char ch) { + if (isalnum(ch) || ch & 0x80) { + return FALSE; + } else { + return (ch != '%' && ch != '.' && ch != ',' && ch != '?' + && ch != '!' && ch != ';' && ch != ':' && ch != ')'); + } +} + +/**********************************************************************//** + Utility routine used in word_wrap_helptext() + + starting at a location in a string, find the offsets of the next 2 + delims, and return those offset values. Return value -1 means not found. +**************************************************************************/ +static void find_next_delims(char *start_ptr, + int *delim1_offset, int *delim2_offset) { + char *p = start_ptr; + + *delim1_offset = -1; + *delim2_offset = -1; + while (*p && !is_delim(*p)) { + p++; + } + if (*p) { + *delim1_offset = p - start_ptr; + } else { + return; + } + p++; + while (*p && !is_delim(*p)) { + p++; + } + if (*p) { + *delim2_offset = p - start_ptr; + } +} + +/**********************************************************************//** + Utility routine used in secfile_save() when writing out helptext strings. + + When a helptext string has "\n\" followed by a newline, that gets lost + when it's read in. This re-inserts the "\n\" so the output will look + the same. + + Also tries to keep output lines to be shorter than LINE_BREAK chars, by + inserting new lines as needed. + + Assumes the struct entry is type ENTRY_STR. +**************************************************************************/ +static void word_wrap_helptext(struct entry *pentry, int indent) { + static char new_buf[8192]; + char *new_buf_ptr = new_buf; + char *buf_ptr = pentry->string.value; + char *end_new_buf_ptr; + int width = LINE_BREAK - indent; + size_t str_length; + bool is_finished = FALSE; + char *break_ptr, *p; + int num_undone; + int delim1_offset, delim2_offset; + int looked_at; + char *old_val; + + fc_assert(pentry->type == ENTRY_STR); + + /* look for newline chars */ + while (*buf_ptr) { + if (*buf_ptr == '\n') { + /* found a newline char, insert "\n\" */ + *new_buf_ptr++ = '\\'; + *new_buf_ptr++ = 'n'; + *new_buf_ptr++ = '\\'; + *new_buf_ptr++ = *buf_ptr++; + } + *new_buf_ptr++ = *buf_ptr++; + } + *new_buf_ptr = 0; + buf_ptr = new_buf; + + /* do word wrapping */ + str_length = strlen(new_buf); + end_new_buf_ptr = new_buf + str_length; + p = new_buf; + num_undone = str_length; + looked_at = 0; + while (! is_finished) { + if (num_undone > width) { + find_next_delims(p, &delim1_offset, &delim2_offset); + if (delim1_offset == -1) { + is_finished = TRUE; + } else { + if (looked_at + delim1_offset < width + && (looked_at + delim2_offset >= width || delim2_offset == -1)) { + break_ptr = p + delim1_offset; + /* found where we want to word wrap */ + /* insert '\' and newline char into new_buf at break_ptr */ + /* first, move to make room */ + p = end_new_buf_ptr; + while (p >= break_ptr) { + *(p + 2) = *p; + p--; + } + *break_ptr++ = '\\'; + *break_ptr++ = '\n'; + end_new_buf_ptr += 2; + /* update for next time thru loop */ + num_undone = end_new_buf_ptr - break_ptr; + looked_at = 0; + p = break_ptr + 1; + width = LINE_BREAK; + } else { + /* still looking, update for next time thru loop */ + p += delim1_offset + 1; + looked_at += delim1_offset + 1; + } + /* if now pointing at a new line, update for next time thru loop */ + if (*p == '\n') { + looked_at = 0; + width = LINE_BREAK; + } + } + } else { + is_finished = TRUE; + } + } + + /* We free() old value only after we've placed the new one, to + * support secfile_replace_str_vec() calls that want to keep some of + * the entries from the old vector in the new one. We don't want + * to lose the entry in between. */ + old_val = pentry->string.value; + pentry->string.value = fc_strdup(buf_ptr); + free(old_val); +} + +/**********************************************************************//** + Utility routine used in secfile_save() when writing out strings. + + Assumes the struct entry is type ENTRY_STR. +**************************************************************************/ +static size_t entry_str_length(struct entry *pentry) { + size_t len; + + fc_assert(pentry->type == ENTRY_STR); + + /* + 2 for the enclosing " chars */ + len = strlen(pentry->string.value) + 2; + if (pentry->string.gt_marking) { + len +=3; /* + 3 for the initial "_(" and terminating ")" */ + } + return len; +} + /**********************************************************************//** Save the previously filled in section_file to disk. @@ -850,9 +1014,31 @@ bool secfile_save(const struct section_file *secfile, const char *filename, if (pentry->type == ENTRY_LONG_COMMENT) { entry_to_file(pentry, fs); } else { + /* these vars are only used for ENTRY_STR's */ + bool need_new_line = FALSE; + int col, indent, next_len; + /* Classic entry. */ col_entry_name = entry_name(pentry); - fz_fprintf(fs, "%s=", col_entry_name); + indent = fz_fprintf(fs, "%s=", col_entry_name); + col = indent; + + /* for ENTRY_STR's, wrap long lines */ + if (pentry->type == ENTRY_STR) { + if (!strcmp(col_entry_name, "helptext") + || !strcmp(col_entry_name, "summary")) { + /* helptext - do word wrap, always put next on new line */ + word_wrap_helptext(pentry, indent); + need_new_line = TRUE; + col = indent; + } else { + /* not helptext - track the col position */ + /* + 1 for the comma */ + next_len = entry_str_length(pentry) + 1; + col += next_len; + } + } + entry_to_file(pentry, fs); /* Check for vector. */ @@ -867,8 +1053,36 @@ bool secfile_save(const struct section_file *secfile, const char *filename, if (0 != strcmp(pentry_name, entry_name(col_pentry))) { break; } + fz_fprintf(fs, ","); + col++; + + /* for ENTRY_STR's, wrap long lines */ + if (pentry->type == ENTRY_STR) { + if (!strcmp(col_entry_name, "helptext")) { + /* helptext - do word wrap, always put next on new line */ + word_wrap_helptext(col_pentry, indent); + need_new_line = TRUE; + } else { + /* not helptext - track the col position and + check if we will need a new line */ + /* + 1 for the comma */ + next_len = entry_str_length(col_pentry) + 1; + if (col + next_len > LINE_BREAK) { + need_new_line = TRUE; + } + } + if (need_new_line) { + fz_fprintf(fs, "\n%*s", indent, " "); + need_new_line = FALSE; + col = indent + next_len; + } else { + col += next_len; + } + } + entry_to_file(col_pentry, fs); + ent_iter = col_iter; } @@ -884,7 +1098,7 @@ bool secfile_save(const struct section_file *secfile, const char *filename, } section_list_iterate_end; if (0 != fz_ferror(fs)) { - SECFILE_LOG(secfile, NULL, "Error before closing %s: %s", + SECFILE_LOG(secfile, NULL, "Error before closing %s: %s", real_filename, fz_strerror(fs)); fz_fclose(fs); return FALSE; @@ -1033,7 +1247,7 @@ struct entry *secfile_insert_bool_full(struct section_file *secfile, } /**********************************************************************//** - Insert 'dim' boolean entries at 'path,0', 'path,1' etc. Returns + Insert 'dim' boolean entries at 'path,0', 'path,1' etc. Returns the number of entries inserted or replaced. **************************************************************************/ size_t secfile_insert_bool_vec_full(struct section_file *secfile, @@ -3622,6 +3836,27 @@ bool entry_str_set_gt_marking(struct entry *pentry, bool gt_marking) return TRUE; } +/************************************************************************//** + Copies a string and convert the following characters: + - '\"' to "\\\"". +****************************************************************************/ +static void make_escapes_quotes(const char *str, char *buf, size_t buf_len) +{ + char *dest = buf; + /* Sometimes we insert a character, so keep + * place for '\0' and an extra character. */ + const char *max = buf + buf_len - 1; + + while (*str != '\0' && dest < max) { + if (*str == '\"') { + *dest++ = '\\'; + max--; + } + *dest++ = *str++; + } + *dest = 0; +} + /**********************************************************************//** Push an entry into a file stream. **************************************************************************/ @@ -3656,7 +3891,7 @@ static void entry_to_file(const struct entry *pentry, fz_FILE *fs) break; case ENTRY_STR: if (pentry->string.escaped) { - make_escapes(pentry->string.value, buf, sizeof(buf)); + make_escapes_quotes(pentry->string.value, buf, sizeof(buf)); if (pentry->string.gt_marking) { fz_fprintf(fs, "_(\"%s\")", buf); } else { diff --git a/utility/support.c b/utility/support.c index 042568b4c2..6384c51152 100644 --- a/utility/support.c +++ b/utility/support.c @@ -287,40 +287,6 @@ int fc_strncasecmp(const char *str0, const char *str1, size_t n) return ret; } -/************************************************************************//** - Copies a string and convert the following characters: - - '\n' to "\\n". - - '\\' to "\\\\". - - '\"' to "\\\"". - See also remove_escapes(). -****************************************************************************/ -void make_escapes(const char *str, char *buf, size_t buf_len) -{ - char *dest = buf; - /* Sometimes we insert 2 characters at once ('\n' -> "\\n"), so keep - * place for '\0' and an extra character. */ - const char *const max = buf + buf_len - 2; - - while (*str != '\0' && dest < max) { - switch (*str) { - case '\n': - *dest++ = '\\'; - *dest++ = 'n'; - str++; - break; - case '\\': - case '\"': - *dest++ = '\\'; - - fc__fallthrough; - default: - *dest++ = *str++; - break; - } - } - *dest = 0; -} - /************************************************************************//** Copies a string. Backslash followed by a genuine newline always removes the newline. @@ -328,7 +294,6 @@ void make_escapes(const char *str, char *buf, size_t buf_len) - '\n' -> newline translation. - Other '\?' sequences (any character '?') are just passed through with the '\' removed (eg, includes '\\', '\"'). - See also make_escapes(). ****************************************************************************/ void remove_escapes(const char *str, bool full_escapes, char *buf, size_t buf_len) diff --git a/utility/support.h b/utility/support.h index 16c2be1737..708e97fb0c 100644 --- a/utility/support.h +++ b/utility/support.h @@ -241,7 +241,6 @@ static inline bool is_bigendian(void) #endif /* WORDS_BIGENDIAN */ } -void make_escapes(const char *str, char *buf, size_t buf_len); void remove_escapes(const char *str, bool full_escapes, char *buf, size_t buf_len); -- 2.31.0