From: Ben Pfaff Date: Sat, 3 Jun 2023 17:02:28 +0000 (-0700) Subject: spreadsheet-reader: Merge duplicate code with libpspp/str.h. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2ba8dc2955f8a4bcad4a265fd3354f4fd8f74564;p=pspp spreadsheet-reader: Merge duplicate code with libpspp/str.h. The tree had two functions for 26-adic spreadsheet number formatting. This eliminates the duplication and moves the corresponding parsing function into str.h as well. --- diff --git a/src/data/dictionary.c b/src/data/dictionary.c index 10768ce921..653ed941f1 100644 --- a/src/data/dictionary.c +++ b/src/data/dictionary.c @@ -1168,13 +1168,11 @@ make_hinted_name (const struct dictionary *dict, const char *hint) for (i = 0; i < ULONG_MAX; i++) { char suffix[INT_BUFSIZE_BOUND (i) + 1]; - char *name; suffix[0] = '_'; - if (!str_format_26adic (i + 1, true, &suffix[1], sizeof suffix - 1)) - NOT_REACHED (); + str_format_26adic (i + 1, true, &suffix[1], sizeof suffix - 1); - name = utf8_encoding_concat (root, suffix, dict->encoding, 64); + char *name = utf8_encoding_concat (root, suffix, dict->encoding, 64); if (var_name_is_insertable (dict, name)) { free (root); diff --git a/src/data/short-names.c b/src/data/short-names.c index ff6b4ff773..78fc0f7325 100644 --- a/src/data/short-names.c +++ b/src/data/short-names.c @@ -62,7 +62,7 @@ assign_short_name (struct variable *v, size_t i, else { suffix[0] = '_'; - str_format_26adic (trial, true, &suffix[1], sizeof suffix - 1); + str_format_26adic__ (trial, true, &suffix[1], sizeof suffix - 1); } /* Set name. */ diff --git a/src/data/spreadsheet-reader.c b/src/data/spreadsheet-reader.c index a16b430a52..32021fa46c 100644 --- a/src/data/spreadsheet-reader.c +++ b/src/data/spreadsheet-reader.c @@ -18,17 +18,18 @@ #include "spreadsheet-reader.h" -#include -#include "gnumeric-reader.h" -#include "ods-reader.h" - -#include #include #include -#include -#include #include +#include "data/gnumeric-reader.h" +#include "data/ods-reader.h" +#include "libpspp/assertion.h" +#include "libpspp/str.h" + +#include "gl/xalloc.h" +#include "gl/c-xvasprintf.h" + struct spreadsheet * spreadsheet_ref (struct spreadsheet *s) { @@ -89,115 +90,18 @@ spreadsheet_get_cell (struct spreadsheet *s, int n, int row, int column) } -#define RADIX 26 - -static void -reverse (char *s, int len) -{ - int i; - for (i = 0; i < len / 2; ++i) - { - char tmp = s[len - i - 1]; - s[len - i -1] = s[i]; - s[i] = tmp; - } -} - - -/* Convert a string, which is an integer encoded in base26 - IE, A=0, B=1, ... Z=25 to the integer it represents. - ... except that in this scheme, digits with an exponent - greater than 1 are implicitly incremented by 1, so - AA = 0 + 1*26, AB = 1 + 1*26, - ABC = 2 + 2*26 + 1*26^2 .... - On error, this function returns -1 -*/ -int -ps26_to_int (const char *str) -{ - int i; - int multiplier = 1; - int result = 0; - int len = strlen (str); - - for (i = len - 1 ; i >= 0; --i) - { - char c = str[i]; - if (c < 'A' || c > 'Z') - return -1; - int mantissa = (c - 'A'); - - assert (mantissa >= 0); - assert (mantissa < RADIX); - - if (i != len - 1) - mantissa++; - - result += mantissa * multiplier; - multiplier *= RADIX; - } - - return result; -} - -/* Convert an integer, which must be non-negative, - to pseudo base 26. - The caller must free the return value when no longer required. */ -char * -int_to_ps26 (int i) -{ - char *ret = NULL; - - int lower = 0; - long long int base = RADIX; - int exp = 1; - - if (i < 0) - return NULL; - - while (i > lower + base - 1) - { - lower += base; - base *= RADIX; - assert (base > 0); - exp++; - } - - i -= lower; - i += base; - - ret = xmalloc (exp + 1); - - exp = 0; - do - { - ret[exp++] = (i % RADIX) + 'A'; - i /= RADIX; - } - while (i > 1); - - ret[exp]='\0'; - - reverse (ret, exp); - return ret; -} - - char * create_cell_ref (int col0, int row0) { - char *cs0 ; - char *s ; - - if (col0 < 0) return NULL; - if (row0 < 0) return NULL; - - cs0 = int_to_ps26 (col0); - s = c_xasprintf ("%s%d", cs0, row0 + 1); + if (col0 < 0 || row0 < 0) + return NULL; - free (cs0); + char s[F26ADIC_STRLEN_MAX + INT_STRLEN_BOUND (row0) + 1]; + str_format_26adic (col0 + 1, true, s, sizeof s); + size_t len = strlen (s); + snprintf (s + len, sizeof s - len, "%d", row0 + 1); - return s; + return xstrdup (s); } char * @@ -236,10 +140,8 @@ convert_cell_ref (const char *ref, if (n != 4) return false; - str_uppercase (startcol); - *col0 = ps26_to_int (startcol); - str_uppercase (stopcol); - *coli = ps26_to_int (stopcol); + *col0 = str_parse_26adic (startcol); + *coli = str_parse_26adic (stopcol); *row0 = startrow - 1; *rowi = stoprow - 1 ; diff --git a/src/data/spreadsheet-reader.h b/src/data/spreadsheet-reader.h index d7783bab7f..8e13d03b13 100644 --- a/src/data/spreadsheet-reader.h +++ b/src/data/spreadsheet-reader.h @@ -39,8 +39,6 @@ struct spreadsheet_read_options int asw ; /* The width of string variables in the created dictionary */ }; -int ps26_to_int (const char *str); -char * int_to_ps26 (int); bool convert_cell_ref (const char *ref, int *col0, int *row0, diff --git a/src/libpspp/str.c b/src/libpspp/str.c index f85844076b..a85d1fb9ef 100644 --- a/src/libpspp/str.c +++ b/src/libpspp/str.c @@ -25,6 +25,7 @@ #include #include +#include "libpspp/assertion.h" #include "libpspp/cast.h" #include "libpspp/i18n.h" #include "libpspp/message.h" @@ -262,10 +263,14 @@ str_lowercase (char *s) which has room for SIZE bytes. Uses uppercase if UPPERCASE is true, otherwise lowercase, Returns true if successful, false if NUMBER, plus a trailing null, is too large to fit in the - available space. + available space. If SIZE is at least F26ADIC_STRLEN_MAX + 1, + the number is guaranteed to fit. Even if the number doesn't + fit, if SIZE > 0, the characters that do fit, if any, will be + null-terminated. 26-adic notation is "spreadsheet column numbering": 1 = A, 2 = - B, 3 = C, ... 26 = Z, 27 = AA, 28 = AB, 29 = AC, ... + B, 3 = C, ... 26 = Z, 27 = AA, 28 = AB, 29 = AC, ... Zero is + the empty string. 26-adic notation is the special case of a k-adic numeration system (aka bijective base-k numeration) with k=26. In k-adic @@ -274,8 +279,8 @@ str_lowercase (char *s) For more information, see http://en.wikipedia.org/wiki/Bijective_numeration. */ bool -str_format_26adic (unsigned long int number, bool uppercase, - char buffer[], size_t size) +str_format_26adic__ (unsigned long int number, bool uppercase, + char buffer[], size_t size) { const char *alphabet = uppercase ? "ABCDEFGHIJKLMNOPQRSTUVWXYZ" : "abcdefghijklmnopqrstuvwxyz"; @@ -302,6 +307,50 @@ overflow: return false; } +/* Like str_format_26adic__(), but SIZE must be big enough that it cannot + fail. */ +void +str_format_26adic (unsigned long int number, bool uppercase, + char buffer[], size_t size) +{ + assert (size >= F26ADIC_STRLEN_MAX + 1); + if (!str_format_26adic__ (number, uppercase, buffer, size)) + NOT_REACHED (); +} + +/* Returns the value of 26-adic string STR. See str_format_26adic() for a + definition of 26-adic. + + On error, this function returns -1. */ +int +str_parse_26adic (const char *str) +{ + enum { RADIX = 26 }; + + int multiplier = 1; + int result = 0; + + size_t len = strlen (str); + for (size_t i = 0; i < len; i++) + { + if (result >= INT_MAX / RADIX) + return -1; + + char c = str[len - i - 1]; + int digit = (c >= 'A' && c <= 'Z' ? c - 'A' + : c >= 'a' && c <= 'z' ? c - 'a' + : -1); + if (digit < 0) + return -1; + assert (digit >= 0 && digit < RADIX); + + result += (digit + (i > 0)) * multiplier; + multiplier *= RADIX; + } + + return result; +} + /* Copies IN to buffer OUT with size OUT_SIZE, appending a null terminator. If IN is too long for OUT, or if IN contains a new-line, replaces the tail with "...". diff --git a/src/libpspp/str.h b/src/libpspp/str.h index 77ba625dec..2fbbf510b3 100644 --- a/src/libpspp/str.h +++ b/src/libpspp/str.h @@ -18,6 +18,7 @@ #define str_h 1 #include +#include #include #include #include @@ -30,6 +31,7 @@ #include "xstrndup.h" #include "xvasprintf.h" +#include "gl/verify.h" #include "gl/xalloc.h" /* Miscellaneous. */ @@ -51,8 +53,15 @@ void str_copy_buf_trunc (char *, size_t, const char *, size_t); void str_uppercase (char *); void str_lowercase (char *); -bool str_format_26adic (unsigned long int number, bool uppercase, - char buffer[], size_t); +/* Maximum number of digits needed to express ULONG_MAX in 26-adic notation. */ +#define F26ADIC_STRLEN_MAX 14 +verify (ULONG_MAX <= UINT64_MAX); + +bool str_format_26adic__ (unsigned long int number, bool uppercase, + char buffer[], size_t); +void str_format_26adic (unsigned long int number, bool uppercase, + char buffer[], size_t); +int str_parse_26adic (const char *str); void str_ellipsize (struct substring in, char *out, size_t out_size); diff --git a/src/output/pivot-table.c b/src/output/pivot-table.c index 9251621676..a8f70ca623 100644 --- a/src/output/pivot-table.c +++ b/src/output/pivot-table.c @@ -1547,7 +1547,7 @@ pivot_footnote_format_marker (const struct pivot_footnote *f, ds_put_format (s, "%zu", f->idx + 1); else { - char text[INT_BUFSIZE_BOUND (size_t)]; + char text[F26ADIC_STRLEN_MAX + 1]; str_format_26adic (f->idx + 1, false, text, sizeof text); ds_put_cstr (s, text); } diff --git a/src/ui/gui/psppire-import-spreadsheet.c b/src/ui/gui/psppire-import-spreadsheet.c index 6e8bd155c3..9423e30ddf 100644 --- a/src/ui/gui/psppire-import-spreadsheet.c +++ b/src/ui/gui/psppire-import-spreadsheet.c @@ -21,6 +21,7 @@ #include "psppire-import-spreadsheet.h" #include "builder-wrapper.h" +#include "libpspp/assertion.h" #include "libpspp/misc.h" #include "psppire-spreadsheet-model.h" #include "psppire-spreadsheet-data-model.h" @@ -33,9 +34,9 @@ static void set_column_header_label (GtkWidget *button, int i, gpointer user_data) { - gchar *x = int_to_ps26 (i); + char x[F26ADIC_STRLEN_MAX + 1]; + str_format_26adic (i + 1, true, x, sizeof x); gtk_button_set_label (GTK_BUTTON (button), x); - g_free (x); } static void do_selection_update (PsppireImportAssistant *ia); @@ -146,19 +147,19 @@ static gboolean column_output (GtkSpinButton *sb, gpointer unused) { gint value = gtk_spin_button_get_value_as_int (sb); - char *text = int_to_ps26 (value); - if (text == NULL) + if (value < 0) return FALSE; + char text[F26ADIC_STRLEN_MAX + 1]; + str_format_26adic (value + 1, true, text, sizeof text); gtk_entry_set_text (GTK_ENTRY (sb), text); - free (text); return TRUE; } /* Interprets the SBs text as 1 based instead of zero based. */ static gint -row_input (GtkSpinButton *sb, gpointer new_value, gpointer unused) +row_input (GtkSpinButton *sb, gdouble *new_value, gpointer unused) { const char *text = gtk_entry_get_text (GTK_ENTRY (sb)); gdouble value = g_strtod (text, NULL) - 1; @@ -166,7 +167,7 @@ row_input (GtkSpinButton *sb, gpointer new_value, gpointer unused) if (value < 0) return FALSE; - memcpy (new_value, &value, sizeof (value)); + *new_value = value; return TRUE; } @@ -175,15 +176,15 @@ row_input (GtkSpinButton *sb, gpointer new_value, gpointer unused) /* Interprets the SBs text of the form A, B, C etc and sets NEW_VALUE as a double. */ static gint -column_input (GtkSpinButton *sb, gpointer new_value, gpointer unused) +column_input (GtkSpinButton *sb, gdouble *new_value, gpointer unused) { const char *text = gtk_entry_get_text (GTK_ENTRY (sb)); - double value = ps26_to_int (text); + double value = str_parse_26adic (text); if (value < 0) return FALSE; - memcpy (new_value, &value, sizeof (value)); + *new_value = value; return TRUE; } diff --git a/tests/libpspp/str-test.c b/tests/libpspp/str-test.c index f5897d7202..16caa5ca1e 100644 --- a/tests/libpspp/str-test.c +++ b/tests/libpspp/str-test.c @@ -32,7 +32,7 @@ check_die (void) static void check_26adic (unsigned long int number, const char *expected_string) { - char string[8]; + char string[F26ADIC_STRLEN_MAX + 1]; str_format_26adic (number, true, string, sizeof string); if (strcmp (string, expected_string)) { diff --git a/tests/libpspp/stringi-map-test.c b/tests/libpspp/stringi-map-test.c index 499e8b33b6..47d6384b84 100644 --- a/tests/libpspp/stringi-map-test.c +++ b/tests/libpspp/stringi-map-test.c @@ -92,8 +92,9 @@ get_string (int idx) s = &string_table[idx]; if (*s == NULL) { - *s = xmalloc (16); - str_format_26adic (idx + 1, true, *s, 16); + size_t size = F26ADIC_STRLEN_MAX + 1; + *s = xmalloc (size); + str_format_26adic (idx + 1, true, *s, size); } return *s; } diff --git a/tests/libpspp/stringi-set-test.c b/tests/libpspp/stringi-set-test.c index fd2645b51f..2346ccaef5 100644 --- a/tests/libpspp/stringi-set-test.c +++ b/tests/libpspp/stringi-set-test.c @@ -79,8 +79,9 @@ make_string (int value) s = &string_table[value]; if (*s == NULL) { - *s = xmalloc (16); - str_format_26adic (value + 1, true, *s, 16); + size_t size = F26ADIC_STRLEN_MAX + 1; + *s = xmalloc (size); + str_format_26adic (value + 1, true, *s, size); } return *s; }