diff options
author | Neal H. Walfield <[email protected]> | 2015-11-23 21:20:28 +0000 |
---|---|---|
committer | Neal H. Walfield <[email protected]> | 2015-11-23 21:23:38 +0000 |
commit | 19362a8dd7ee986c082a5afc5a446f939991ec0f (patch) | |
tree | a5f4b3d346583ada7518bda71fcc50153815506a | |
parent | common: Extend utf8_charcount to include the string's length. (diff) | |
download | gnupg-19362a8dd7ee986c082a5afc5a446f939991ec0f.tar.gz gnupg-19362a8dd7ee986c082a5afc5a446f939991ec0f.zip |
gpg: Reflow long texts.
* common/stringhelp.c (format_text): New function.
* common/t-stringhelp.c (stresc): New function.
(test_format_text): New function. Test format_text.
* g10/tofu.c (get_trust): Use format_text to reflow long texts.
(show_statistics): Likewise.
--
Signed-off-by: Neal H. Walfield <[email protected]>
-rw-r--r-- | common/stringhelp.c | 129 | ||||
-rw-r--r-- | common/stringhelp.h | 5 | ||||
-rw-r--r-- | common/t-stringhelp.c | 137 | ||||
-rw-r--r-- | g10/tofu.c | 16 |
4 files changed, 283 insertions, 4 deletions
diff --git a/common/stringhelp.c b/common/stringhelp.c index d0b55615d..6748d1eab 100644 --- a/common/stringhelp.c +++ b/common/stringhelp.c @@ -1327,3 +1327,132 @@ strtokenize (const char *string, const char *delim) return result; } + +char * +format_text (char *text, int in_place, int target_cols, int max_cols) +{ + const int do_debug = 0; + + /* The character under consideration. */ + char *p; + /* The start of the current line. */ + char *line; + /* The last space that we saw. */ + char *last_space = NULL; + int last_space_cols = 0; + int copied_last_space = 0; + + if (! in_place) + text = xstrdup (text); + + p = line = text; + while (1) + { + /* The number of columns including any trailing space. */ + int cols; + + p = p + strcspn (p, "\n "); + if (! p) + /* P now points to the NUL character. */ + p = &text[strlen (text)]; + + if (*p == '\n') + /* Pass through any newlines. */ + { + p ++; + line = p; + last_space = NULL; + last_space_cols = 0; + copied_last_space = 1; + continue; + } + + /* Have a space or a NUL. Note: we don't count the trailing + space. */ + cols = utf8_charcount (line, (uintptr_t) p - (uintptr_t) line); + if (cols < target_cols) + { + if (! *p) + /* Nothing left to break. */ + break; + + last_space = p; + last_space_cols = cols; + p ++; + /* Skip any immediately following spaces. If we break: + "... foo bar ..." between "foo" and "bar" then we want: + "... foo\nbar ...", which means that the left space has + to be the first space after foo, not the last space + before bar. */ + while (*p == ' ') + p ++; + } + else + { + int cols_with_left_space; + int cols_with_right_space; + int left_penalty; + int right_penalty; + + cols_with_left_space = last_space_cols; + cols_with_right_space = cols; + + if (do_debug) + log_debug ("Breaking: '%.*s'\n", + (int) ((uintptr_t) p - (uintptr_t) line), line); + + /* The number of columns away from TARGET_COLS. We prefer + to underflow than to overflow. */ + left_penalty = target_cols - cols_with_left_space; + right_penalty = 2 * (cols_with_right_space - target_cols); + + if (cols_with_right_space > max_cols) + /* Add a large penalty for each column that exceeds + max_cols. */ + right_penalty += 4 * (cols_with_right_space - max_cols); + + if (do_debug) + log_debug ("Left space => %d cols (penalty: %d); right space => %d cols (penalty: %d)\n", + cols_with_left_space, left_penalty, + cols_with_right_space, right_penalty); + if (last_space_cols && left_penalty <= right_penalty) + /* Prefer the left space. */ + { + if (do_debug) + log_debug ("Breaking at left space.\n"); + p = last_space; + } + else + { + if (do_debug) + log_debug ("Breaking at right space.\n"); + } + + if (! *p) + break; + + *p = '\n'; + p ++; + if (*p == ' ') + { + int spaces; + for (spaces = 1; p[spaces] == ' '; spaces ++) + ; + memmove (p, &p[spaces], strlen (&p[spaces]) + 1); + } + line = p; + last_space = NULL; + last_space_cols = 0; + copied_last_space = 0; + } + } + + /* Chop off any trailing space. */ + while (text[strlen (text) - 1] == ' ') + text[strlen (text) - 1] = '\0'; + /* If we inserted the trailing newline, then remove it. */ + if (! copied_last_space && text[strlen (text) - 1] == '\n') + text[strlen (text) - 1] = '\0'; + + return text; +} diff --git a/common/stringhelp.h b/common/stringhelp.h index b34d28b89..9ff062bd9 100644 --- a/common/stringhelp.h +++ b/common/stringhelp.h @@ -148,6 +148,11 @@ char **strsplit (char *string, char delim, char replacement, int *count); /* Tokenize STRING using the set of delimiters in DELIM. */ char **strtokenize (const char *string, const char *delim); +/* Format a string so that it fits within about TARGET_COLS columns. + If IN_PLACE is 0, then TEXT is copied to a new buffer, which is + returned. Otherwise, TEXT is modified in place and returned. + Normally, target_cols will be 72 and max_cols is 80. */ +char *format_text (char *text, int in_place, int target_cols, int max_cols); /*-- mapstrings.c --*/ const char *map_static_macro_string (const char *string); diff --git a/common/t-stringhelp.c b/common/t-stringhelp.c index 13f3afa74..9e5410bbe 100644 --- a/common/t-stringhelp.c +++ b/common/t-stringhelp.c @@ -677,6 +677,142 @@ test_strtokenize (void) } } +static char * +stresc (char *s) +{ + char *p; + int l = strlen (s) + 1; + + for (p = s; *p; p ++) + if (*p == '\n') + l ++; + + p = xmalloc (l); + for (l = 0; *s; s ++, l ++) + { + if (*s == ' ') + p[l] = '_'; + else if (*p == '\n') + { + p[l ++] = '\\'; + p[l ++] = 'n'; + p[l] = '\n'; + } + else + p[l] = *s; + } + p[l] = *s; + + return p; +} + +static void +test_format_text (void) +{ + struct test + { + int target_cols, max_cols; + char *input; + char *expected; + }; + + struct test tests[] = { + { + 10, 12, + "", + "", + }, + { + 10, 12, + " ", + "", + }, + { + 10, 12, + " ", + "", + }, + { + 10, 12, + " \n ", + " \n", + }, + { + 10, 12, + " \n \n ", + " \n \n", + }, + { + 10, 12, + "0123456789 0123456789 0", + "0123456789\n0123456789\n0", + }, + { + 10, 12, + " 0123456789 0123456789 0 ", + " 0123456789\n0123456789\n0", + }, + { + 10, 12, + "01 34 67 90 23 56 89 12 45 67 89 1", + "01 34 67\n90 23 56\n89 12 45\n67 89 1" + }, + { + 10, 12, + "01 34 67 90 23 56 89 12 45 67 89 1", + "01 34 67\n90 23 56\n89 12 45\n67 89 1" + }, + { + 72, 80, + "Warning: if you think you've seen more than 10 messages " + "signed by this key, then this key might be a forgery! " + "Carefully examine the email address for small variations " + "(e.g., additional white space). If the key is suspect, " + "then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as being bad.\n", + "Warning: if you think you've seen more than 10 messages signed by this\n" + "key, then this key might be a forgery! Carefully examine the email\n" + "address for small variations (e.g., additional white space). If the key\n" + "is suspect, then use 'gpg --tofu-policy bad \"FINGERPRINT\"' to mark it as\n" + "being bad.\n" + + }, + { + 72, 80, + "Normally, there is only a single key associated with an email " + "address. However, people sometimes generate a new key if " + "their key is too old or they think it might be compromised. " + "Alternatively, a new key may indicate a man-in-the-middle " + "attack! Before accepting this key, you should talk to or " + "call the person to make sure this new key is legitimate.", + "Normally, there is only a single key associated with an email " + "address.\nHowever, people sometimes generate a new key if " + "their key is too old or\nthey think it might be compromised. " + "Alternatively, a new key may indicate\na man-in-the-middle " + "attack! Before accepting this key, you should talk\nto or " + "call the person to make sure this new key is legitimate.", + } + }; + + int i; + int failed = 0; + + for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i ++) + { + struct test *test = &tests[i]; + char *result = + format_text (test->input, 0, test->target_cols, test->max_cols); + if (strcmp (result, test->expected) != 0) + { + printf ("%s: Test #%d failed.\nExpected: '%s'\nResult: '%s'\n", + __func__, i + 1, stresc (test->expected), stresc (result)); + failed ++; + } + xfree (result); + } + + if (failed) + fail(0); +} int main (int argc, char **argv) @@ -692,6 +828,7 @@ main (int argc, char **argv) test_make_absfilename_try (); test_strsplit (); test_strtokenize (); + test_format_text (); xfree (home_buffer); return 0; diff --git a/g10/tofu.c b/g10/tofu.c index 5e38d211b..d340bfeb1 100644 --- a/g10/tofu.c +++ b/g10/tofu.c @@ -2038,7 +2038,9 @@ get_trust (struct dbs *dbs, const char *fingerprint, const char *email, "Alternatively, a new key may indicate a man-in-the-middle " "attack! Before accepting this key, you should talk to or " "call the person to make sure this new key is legitimate."; + text = format_text (text, 0, 72, 80); es_fprintf (fp, "\n%s\n", text); + xfree (text); } es_fputc ('\n', fp); @@ -2440,7 +2442,8 @@ show_statistics (struct dbs *dbs, const char *fingerprint, if (policy == TOFU_POLICY_AUTO && messages < 10) { char *set_policy_command; - const char *text; + char *text; + char *tmp; if (messages == 0) log_info (_("Warning: we've have yet to see" @@ -2462,9 +2465,14 @@ show_statistics (struct dbs *dbs, const char *fingerprint, "Carefully examine the email address for small variations " "(e.g., additional white space). If the key is suspect, " "then use '%s' to mark it as being bad.\n"; - log_info (text, - messages, messages == 1 ? _("message") : _("message"), - set_policy_command); + tmp = xasprintf + (text, + messages, messages == 1 ? _("message") : _("message"), + set_policy_command); + text = format_text (tmp, 0, 72, 80); + xfree (tmp); + log_info ("%s", text); + xfree (text); free (set_policy_command); } } |