The following patch series implements custom quotation marks in quotearg. My main motivation is to be able to handle single-quoted strings. For example, you can invoke this:
quotearg_custom ("'", "'", "joel's\tstring"); to produce this: "'joel\\'s\\tstring'" However, I figured it was probably better to generalize. For example, you can also invoke this: quotearg_custom ("(", ")", "(joel's)\tstring"); to produce this: "((joel's\\)\\tstring)" As a somewhat separate issue, the last patch in this series documents a limitation of the quote_these_too array that I discovered while working on the other patches: quote_these_too should not contain a digit or a letter that's special after a backslash. It looks straightforward to overcome this limitation, but I'm not sure anyone would benefit, so I didn't bother. I just documented it. >From f81fc0cf92afa6a6b3f7a87bc898b344fb73c6a8 Mon Sep 17 00:00:00 2001 From: Joel E. Denny <jde...@clemson.edu> Date: Sat, 22 Aug 2009 17:55:15 -0400 Subject: [PATCH] quotearg-tests: test escaping of embedded locale quotes * tests/test-quotearg.c (struct result_strings): Add member for new input. (LQ_ENC, RQ_ENC, RQ_ESC): New macros. (inputs): Add new input. (results_g): Add expected results. (flag_results): Likewise. (locale_results): Likewise. (compare_strings): Check those. --- ChangeLog | 12 +++++ tests/test-quotearg.c | 115 ++++++++++++++++++++++++++++++------------------- 2 files changed, 83 insertions(+), 44 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4e33717..361914b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2009-08-22 Joel E. Denny <jde...@clemson.edu> + + quotearg-tests: test escaping of embedded locale quotes + * tests/test-quotearg.c (struct result_strings): Add member for + new input. + (LQ_ENC, RQ_ENC, RQ_ESC): New macros. + (inputs): Add new input. + (results_g): Add expected results. + (flag_results): Likewise. + (locale_results): Likewise. + (compare_strings): Check those. + 2009-08-22 Bruno Haible <br...@clisp.org> * modules/binary-io (License): Relicense under LGPL. diff --git a/tests/test-quotearg.c b/tests/test-quotearg.c index 73bb1f9..ef91c80 100644 --- a/tests/test-quotearg.c +++ b/tests/test-quotearg.c @@ -52,6 +52,7 @@ struct result_strings { char const *str4; /* Translation of " \t\n'\"\033?""?/\\". */ char const *str5; /* Translation of "a:b". */ char const *str6; /* Translation of "a\\b". */ + char const *str7; /* Translation of LQ RQ. */ }; struct result_groups { @@ -60,118 +61,140 @@ struct result_groups { struct result_strings group3; /* Via quotearg_colon{,_mem}. */ }; +/* These quotes are borrowed from a pt_PT.utf8 translation. */ +# define LQ "\302\253" +# define RQ "\302\273" +# define LQ_ENC "\\302\\253" +# define RQ_ENC "\\302\\273" +# define RQ_ESC "\\\302\273" + static struct result_strings inputs = { - "", "\0001\0", 3, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b" + "", "\0001\0", 3, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b", + LQ RQ }; static struct result_groups results_g[] = { /* literal_quoting_style */ - { { "", "\0""1\0", 3, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b" }, - { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b" }, - { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b" } }, + { { "", "\0""1\0", 3, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b", + LQ RQ }, + { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b", + LQ RQ }, + { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b", + LQ RQ } }, /* shell_quoting_style */ { { "''", "\0""1\0", 3, "simple", "' \t\n'\\''\"\033?""?/\\'", "a:b", - "'a\\b'" }, + "'a\\b'", LQ RQ }, { "''", "1", 1, "simple", "' \t\n'\\''\"\033?""?/\\'", "a:b", - "'a\\b'" }, + "'a\\b'", LQ RQ }, { "''", "1", 1, "simple", "' \t\n'\\''\"\033?""?/\\'", "'a:b'", - "'a\\b'" } }, + "'a\\b'", LQ RQ } }, /* shell_always_quoting_style */ { { "''", "'\0""1\0'", 5, "'simple'", "' \t\n'\\''\"\033?""?/\\'", "'a:b'", - "'a\\b'" }, + "'a\\b'", "'" LQ RQ "'" }, { "''", "'1'", 3, "'simple'", "' \t\n'\\''\"\033?""?/\\'", "'a:b'", - "'a\\b'" }, + "'a\\b'", "'" LQ RQ "'" }, { "''", "'1'", 3, "'simple'", "' \t\n'\\''\"\033?""?/\\'", "'a:b'", - "'a\\b'" } }, + "'a\\b'", "'" LQ RQ "'" } }, /* c_quoting_style */ { { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"", - "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"", "\"a\\\\b\"" }, + "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"", "\"a\\\\b\"", + "\"" LQ_ENC RQ_ENC "\"" }, { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"", - "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"", "\"a\\\\b\"" }, + "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"", "\"a\\\\b\"", + "\"" LQ_ENC RQ_ENC "\"" }, { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"", - "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a\\:b\"", "\"a\\\\b\"" } }, + "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a\\:b\"", "\"a\\\\b\"", + "\"" LQ_ENC RQ_ENC "\"" } }, /* c_maybe_quoting_style */ { { "", "\"\\0001\\0\"", 9, "simple", "\" \\t\\n'\\\"\\033?""?/\\\\\"", - "a:b", "a\\b" }, + "a:b", "a\\b", "\"" LQ_ENC RQ_ENC "\"" }, { "", "\"\\0001\\0\"", 9, "simple", "\" \\t\\n'\\\"\\033?""?/\\\\\"", - "a:b", "a\\b" }, + "a:b", "a\\b", "\"" LQ_ENC RQ_ENC "\"" }, { "", "\"\\0001\\0\"", 9, "simple", "\" \\t\\n'\\\"\\033?""?/\\\\\"", - "\"a:b\"", "a\\b" } }, + "\"a:b\"", "a\\b", "\"" LQ_ENC RQ_ENC "\"" } }, /* escape_quoting_style */ { { "", "\\0001\\0", 7, "simple", " \\t\\n'\"\\033?""?/\\\\", "a:b", - "a\\\\b" }, + "a\\\\b", LQ_ENC RQ_ENC }, { "", "\\0001\\0", 7, "simple", " \\t\\n'\"\\033?""?/\\\\", "a:b", - "a\\\\b" }, + "a\\\\b", LQ_ENC RQ_ENC }, { "", "\\0001\\0", 7, "simple", " \\t\\n'\"\\033?""?/\\\\", "a\\:b", - "a\\\\b" } }, + "a\\\\b", LQ_ENC RQ_ENC } }, /* locale_quoting_style */ { { "`'", "`\\0001\\0'", 9, "`simple'", "` \\t\\n\\'\"\\033?""?/\\\\'", - "`a:b'", "`a\\\\b'" }, + "`a:b'", "`a\\\\b'", "`" LQ_ENC RQ_ENC "'" }, { "`'", "`\\0001\\0'", 9, "`simple'", "` \\t\\n\\'\"\\033?""?/\\\\'", - "`a:b'", "`a\\\\b'" }, + "`a:b'", "`a\\\\b'", "`" LQ_ENC RQ_ENC "'" }, { "`'", "`\\0001\\0'", 9, "`simple'", "` \\t\\n\\'\"\\033?""?/\\\\'", - "`a\\:b'", "`a\\\\b'" } }, + "`a\\:b'", "`a\\\\b'", "`" LQ_ENC RQ_ENC "'" } }, /* clocale_quoting_style */ { { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"", - "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"", "\"a\\\\b\"" }, + "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"", "\"a\\\\b\"", + "\"" LQ_ENC RQ_ENC "\"" }, { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"", - "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"", "\"a\\\\b\"" }, + "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a:b\"", "\"a\\\\b\"", + "\"" LQ_ENC RQ_ENC "\"" }, { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"", - "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a\\:b\"", "\"a\\\\b\"" } } + "\" \\t\\n'\\\"\\033?""?/\\\\\"", "\"a\\:b\"", "\"a\\\\b\"", + "\"" LQ_ENC RQ_ENC "\"" } } }; static struct result_groups flag_results[] = { /* literal_quoting_style and QA_ELIDE_NULL_BYTES */ - { { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b" }, - { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b" }, - { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b" } }, + { { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b", LQ RQ }, + { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b", LQ RQ }, + { "", "1", 1, "simple", " \t\n'\"\033?""?/\\", "a:b", "a\\b", LQ RQ } }, /* c_quoting_style and QA_ELIDE_OUTER_QUOTES */ { { "", "\"\\0001\\0\"", 9, "simple", "\" \\t\\n'\\\"\\033?""?/\\\\\"", - "a:b", "a\\b" }, + "a:b", "a\\b", "\"" LQ_ENC RQ_ENC "\"" }, { "", "\"\\0001\\0\"", 9, "simple", "\" \\t\\n'\\\"\\033?""?/\\\\\"", - "a:b", "a\\b" }, + "a:b", "a\\b", "\"" LQ_ENC RQ_ENC "\"" }, { "", "\"\\0001\\0\"", 9, "simple", "\" \\t\\n'\\\"\\033?""?/\\\\\"", - "\"a:b\"", "a\\b" } }, + "\"a:b\"", "a\\b", "\"" LQ_ENC RQ_ENC "\"" } }, /* c_quoting_style and QA_SPLIT_TRIGRAPHS */ { { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"", - "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a:b\"", "\"a\\\\b\"" }, + "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a:b\"", "\"a\\\\b\"", + "\"" LQ_ENC RQ_ENC "\"" }, { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"", - "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a:b\"", "\"a\\\\b\"" }, + "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a:b\"", "\"a\\\\b\"", + "\"" LQ_ENC RQ_ENC "\"" }, { "\"\"", "\"\\0001\\0\"", 9, "\"simple\"", - "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a\\:b\"", "\"a\\\\b\"" } } + "\" \\t\\n'\\\"\\033?\"\"?/\\\\\"", "\"a\\:b\"", "\"a\\\\b\"", + "\"" LQ_ENC RQ_ENC "\"" } } }; #if ENABLE_NLS -/* These quotes are borrowed from a pt_PT.utf8 translation. */ -# define LQ "\302\253" -# define RQ "\302\273" - static struct result_groups locale_results[] = { /* locale_quoting_style */ { { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ, - LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ, LQ "a\\\\b" RQ }, + LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ, LQ "a\\\\b" RQ, + LQ LQ RQ_ESC RQ }, { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ, - LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ, LQ "a\\\\b" RQ }, + LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ, LQ "a\\\\b" RQ, + LQ LQ RQ_ESC RQ }, { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ, - LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a\\:b" RQ, LQ "a\\\\b" RQ } }, + LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a\\:b" RQ, LQ "a\\\\b" RQ, + LQ LQ RQ_ESC RQ } }, /* clocale_quoting_style */ { { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ, - LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ, LQ "a\\\\b" RQ }, + LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ, LQ "a\\\\b" RQ, + LQ LQ RQ_ESC RQ }, { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ, - LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ, LQ "a\\\\b" RQ }, + LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a:b" RQ, LQ "a\\\\b" RQ, + LQ LQ RQ_ESC RQ }, { LQ RQ, LQ "\\0001\\0" RQ, 11, LQ "simple" RQ, - LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a\\:b" RQ, LQ "a\\\\b" RQ } } + LQ " \\t\\n'\"\\033?""?/\\\\" RQ, LQ "a\\:b" RQ, LQ "a\\\\b" RQ, + LQ LQ RQ_ESC RQ } } }; #endif /* ENABLE_NLS */ @@ -214,6 +237,10 @@ compare_strings (char *(func) (char const *, size_t *), len = strlen (inputs.str6); p = func (inputs.str6, &len); compare (results->str6, strlen (results->str6), p, len); + + len = strlen (inputs.str7); + p = func (inputs.str7, &len); + compare (results->str7, strlen (results->str7), p, len); } static char * -- 1.5.4.3 >From 47bd1531d990bddf3ba09419ccc6309b9fe3ed4e Mon Sep 17 00:00:00 2001 From: Joel E. Denny <jde...@clemson.edu> Date: Sat, 22 Aug 2009 20:26:46 -0400 Subject: [PATCH] quotearg: fix right quote escaping when it's in quote_these_too * lib/quotearg.c (quotearg_buffer_restyled): Upon seeing a right quote, be sure to prepend only one backslash. * tests/test-quotearg.c (use_quote_double_quotes): New function. (main): Test it. --- ChangeLog | 8 ++++++++ lib/quotearg.c | 11 +++++++++-- tests/test-quotearg.c | 13 +++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 361914b..5f1ec94 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2009-08-22 Joel E. Denny <jde...@clemson.edu> + quotearg: fix right quote escaping when it's in quote_these_too + * lib/quotearg.c (quotearg_buffer_restyled): Upon seeing a right + quote, be sure to prepend only one backslash. + * tests/test-quotearg.c (use_quote_double_quotes): New function. + (main): Test it. + +2009-08-22 Joel E. Denny <jde...@clemson.edu> + quotearg-tests: test escaping of embedded locale quotes * tests/test-quotearg.c (struct result_strings): Add member for new input. diff --git a/lib/quotearg.c b/lib/quotearg.c index 339bf34..3f9e628 100644 --- a/lib/quotearg.c +++ b/lib/quotearg.c @@ -280,6 +280,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, { unsigned char c; unsigned char esc; + bool is_right_quote = false; if (backslash_escapes && quote_string_len @@ -288,7 +289,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, { if (elide_outer_quotes) goto force_outer_quoting_style; - STORE ('\\'); + is_right_quote = true; } c = arg[i]; @@ -521,6 +522,11 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, STORE ('0' + ((c >> 3) & 7)); c = '0' + (c & 7); } + else if (is_right_quote) + { + STORE ('\\'); + is_right_quote = false; + } if (ilim <= i + 1) break; STORE (c); @@ -534,7 +540,8 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, if (! ((backslash_escapes || elide_outer_quotes) && quote_these_too - && quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS)))) + && quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS))) + && !is_right_quote) goto store_c; store_escape: diff --git a/tests/test-quotearg.c b/tests/test-quotearg.c index ef91c80..2e2c56b 100644 --- a/tests/test-quotearg.c +++ b/tests/test-quotearg.c @@ -264,6 +264,15 @@ use_quotearg (const char *str, size_t *len) } static char * +use_quote_double_quotes (const char *str, size_t *len) +{ + char *p = *len == SIZE_MAX ? quotearg_char (str, '"') + : quotearg_char_mem (str, *len, '"'); + *len = strlen (p); + return p; +} + +static char * use_quotearg_colon (const char *str, size_t *len) { char *p = (*len == SIZE_MAX ? quotearg_colon (str) @@ -287,6 +296,8 @@ main (int argc, char *argv[]) set_quoting_style (NULL, i); compare_strings (use_quotearg_buffer, &results_g[i].group1); compare_strings (use_quotearg, &results_g[i].group2); + if (i == c_quoting_style) + compare_strings (use_quote_double_quotes, &results_g[i].group2); compare_strings (use_quotearg_colon, &results_g[i].group3); } @@ -301,12 +312,14 @@ main (int argc, char *argv[]) == QA_ELIDE_NULL_BYTES); compare_strings (use_quotearg_buffer, &flag_results[1].group1); compare_strings (use_quotearg, &flag_results[1].group2); + compare_strings (use_quote_double_quotes, &flag_results[1].group2); compare_strings (use_quotearg_colon, &flag_results[1].group3); ASSERT (set_quoting_flags (NULL, QA_SPLIT_TRIGRAPHS) == QA_ELIDE_OUTER_QUOTES); compare_strings (use_quotearg_buffer, &flag_results[2].group1); compare_strings (use_quotearg, &flag_results[2].group2); + compare_strings (use_quote_double_quotes, &flag_results[2].group2); compare_strings (use_quotearg_colon, &flag_results[2].group3); ASSERT (set_quoting_flags (NULL, 0) == QA_SPLIT_TRIGRAPHS); -- 1.5.4.3 >From 814f12a49687047a2e9a5b03f8373a3986ca2c3b Mon Sep 17 00:00:00 2001 From: Joel E. Denny <jde...@clemson.edu> Date: Sat, 22 Aug 2009 04:21:49 -0400 Subject: [PATCH] quotearg: implement custom_quoting_style * lib/quotearg.c: (struct quoting_options): Add left and right fields to store quotes. (set_custom_quoting): New public function. (quotearg_buffer_restyled): Add left and right arguments, handle them very much like locale quoting, and update all uses. (quotearg_n_custom): New public function. (quotearg_n_custom_mem): New public function. (quotearg_custom): New public function. (quotearg_custom_mem): New public function. * lib/quotearg.h: Prototype and document new public functions. (enum quoting_style): Add custom_quoting_style member and document. * tests/test-quotearg.c (custom_quotes): New array. (custom_results): New array. (main): Extend to test custom quoting. --- ChangeLog | 19 ++++++++++ lib/quotearg.c | 89 +++++++++++++++++++++++++++++++++++++++++++----- lib/quotearg.h | 61 +++++++++++++++++++++++++++++++++- tests/test-quotearg.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5f1ec94..255e17e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,24 @@ 2009-08-22 Joel E. Denny <jde...@clemson.edu> + quotearg: implement custom_quoting_style + * lib/quotearg.c: (struct quoting_options): Add left and right + fields to store quotes. + (set_custom_quoting): New public function. + (quotearg_buffer_restyled): Add left and right arguments, + handle them very much like locale quoting, and update all uses. + (quotearg_n_custom): New public function. + (quotearg_n_custom_mem): New public function. + (quotearg_custom): New public function. + (quotearg_custom_mem): New public function. + * lib/quotearg.h: Prototype and document new public functions. + (enum quoting_style): Add custom_quoting_style member and + document. + * tests/test-quotearg.c (custom_quotes): New array. + (custom_results): New array. + (main): Extend to test custom quoting. + +2009-08-22 Joel E. Denny <jde...@clemson.edu> + quotearg: fix right quote escaping when it's in quote_these_too * lib/quotearg.c (quotearg_buffer_restyled): Upon seeing a right quote, be sure to prepend only one backslash. diff --git a/lib/quotearg.c b/lib/quotearg.c index 3f9e628..9e5f038 100644 --- a/lib/quotearg.c +++ b/lib/quotearg.c @@ -54,6 +54,12 @@ struct quoting_options /* Quote the characters indicated by this bit vector even if the quoting style would not normally require them to be quoted. */ unsigned int quote_these_too[(UCHAR_MAX / INT_BITS) + 1]; + + /* The left quote for custom_quoting_style. */ + char const *left; + + /* The right quote for custom_quoting_style. */ + char const *right; }; /* Names of quoting styles. */ @@ -146,6 +152,19 @@ set_quoting_flags (struct quoting_options *o, int i) return r; } +void +set_custom_quoting (struct quoting_options *o, + char const *l, char const *r) +{ + if (!o) + o = &default_quoting_options; + o->style = custom_quoting_style; + if (!l || !r) + abort (); + o->left = l; + o->right = r; +} + /* Return quoting options for STYLE, with no extra quoting. */ static struct quoting_options quoting_options_from_style (enum quoting_style style) @@ -185,7 +204,8 @@ static size_t quotearg_buffer_restyled (char *buffer, size_t buffersize, char const *arg, size_t argsize, enum quoting_style quoting_style, int flags, - unsigned int const *quote_these_too) + unsigned int const *quote_these_too, + char const *left, char const *right) { size_t i; size_t len = 0; @@ -225,6 +245,7 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, case locale_quoting_style: case clocale_quoting_style: + case custom_quoting_style: { /* TRANSLATORS: Get translations for open and closing quotation marks. @@ -246,8 +267,11 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, <http://en.wikipedia.org/wiki/Quotation_mark#Glyphs> and use glyphs suitable for your language. */ - char const *left = gettext_quote (N_("`"), quoting_style); - char const *right = gettext_quote (N_("'"), quoting_style); + if (quoting_style != custom_quoting_style) + { + left = gettext_quote (N_("`"), quoting_style); + right = gettext_quote (N_("'"), quoting_style); + } if (!elide_outer_quotes) for (quote_string = left; *quote_string; quote_string++) STORE (*quote_string); @@ -301,6 +325,11 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, if (elide_outer_quotes) goto force_outer_quoting_style; STORE ('\\'); + /* If quote_string were to begin with digits, we'd need to + test for the end of the arg as well. However, it's + hard to imagine any locale that would use digits in + quotes, and set_custom_quoting is documented not to + accept them. */ if (i + 1 < argsize && '0' <= arg[i + 1] && arg[i + 1] <= '9') { STORE ('0'); @@ -426,7 +455,13 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': /* These characters don't cause problems, no matter what the - quoting style is. They cannot start multibyte sequences. */ + quoting style is. They cannot start multibyte sequences. + A digit or a special letter would cause trouble if it + appeared at the beginning of quote_string because we'd then + escape by prepending a backslash. However, it's hard to + imagine any locale that would use digits or letters as + quotes, and set_custom_quoting is documented not to accept + them. */ break; default: @@ -570,7 +605,8 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, sufficiently quotes the specified characters. */ return quotearg_buffer_restyled (buffer, buffersize, arg, argsize, quoting_style, - flags & ~QA_ELIDE_OUTER_QUOTES, NULL); + flags & ~QA_ELIDE_OUTER_QUOTES, NULL, + left, right); } /* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of @@ -590,7 +626,8 @@ quotearg_buffer (char *buffer, size_t buffersize, struct quoting_options const *p = o ? o : &default_quoting_options; int e = errno; size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize, - p->style, p->flags, p->quote_these_too); + p->style, p->flags, p->quote_these_too, + p->left, p->right); errno = e; return r; } @@ -618,10 +655,11 @@ quotearg_alloc_mem (char const *arg, size_t argsize, size_t *size, /* Elide embedded null bytes if we can't return a size. */ int flags = p->flags | (size ? 0 : QA_ELIDE_NULL_BYTES); size_t bufsize = quotearg_buffer_restyled (0, 0, arg, argsize, p->style, - flags, p->quote_these_too) + 1; + flags, p->quote_these_too, + p->left, p->right) + 1; char *buf = xcharalloc (bufsize); quotearg_buffer_restyled (buf, bufsize, arg, argsize, p->style, flags, - p->quote_these_too); + p->quote_these_too, p->left, p->right); errno = e; if (size) *size = bufsize - 1; @@ -710,7 +748,9 @@ quotearg_n_options (int n, char const *arg, size_t argsize, int flags = options->flags | QA_ELIDE_NULL_BYTES; size_t qsize = quotearg_buffer_restyled (val, size, arg, argsize, options->style, flags, - options->quote_these_too); + options->quote_these_too, + options->left, + options->right); if (size <= qsize) { @@ -719,7 +759,8 @@ quotearg_n_options (int n, char const *arg, size_t argsize, free (val); sv[n].val = val = xcharalloc (size); quotearg_buffer_restyled (val, size, arg, argsize, options->style, - flags, options->quote_these_too); + flags, options->quote_these_too, + options->left, options->right); } errno = e; @@ -804,3 +845,31 @@ quotearg_colon_mem (char const *arg, size_t argsize) { return quotearg_char_mem (arg, argsize, ':'); } + +char * +quotearg_n_custom (int n, char const *l, char const *r, char const *arg) +{ + return quotearg_n_custom_mem (n, l, r, arg, SIZE_MAX); +} + +char * +quotearg_n_custom_mem (int n, char const *l, char const *r, + char const *arg, size_t argsize) +{ + struct quoting_options o = default_quoting_options; + set_custom_quoting (&o, l, r); + return quotearg_n_options (n, arg, argsize, &o); +} + +char * +quotearg_custom (char const *l, char const *r, char const *arg) +{ + return quotearg_n_custom (0, l, r, arg); +} + +char * +quotearg_custom_mem (char const *l, char const *r, char const *arg, + size_t argsize) +{ + return quotearg_n_custom_mem (0, l, r, arg, argsize); +} diff --git a/lib/quotearg.h b/lib/quotearg.h index 7700107..6704b32 100644 --- a/lib/quotearg.h +++ b/lib/quotearg.h @@ -157,7 +157,37 @@ enum quoting_style "\302\253simple\302\273", "\302\253\\0 \\t\\n'\"\\033??/\\\\\302\253", "\302\253a\\:b\302\273" */ - clocale_quoting_style + clocale_quoting_style, + + /* Like c_quoting_style except use the custom quotation marks set by + set_custom_quoting. If custom quotation marks are not set, the + behavior is undefined. + + left = right = "'" + quotearg_buffer: + "'simple'", "'\\0 \\t\\n\\'\"\\033??/\\\\'", "'a:b'" + quotearg: + "'simple'", "'\\0 \\t\\n\\'\"\\033??/\\\\'", "'a:b'" + quotearg_colon: + "'simple'", "'\\0 \\t\\n\\'\"\\033??/\\\\'", "'a\\:b'" + + left = "(" and right = ")" + quotearg_buffer: + "(simple)", "(\\0 \\t\\n'\"\\033??/\\\\)", "(a:b)" + quotearg: + "(simple)", "(\\0 \\t\\n'\"\\033??/\\\\)", "(a:b)" + quotearg_colon: + "(simple)", "(\\0 \\t\\n'\"\\033??/\\\\)", "(a\\:b)" + + left = ":" and right = " " + quotearg_buffer: + ":simple ", ":\\0\\ \\t\\n'\"\\033??/\\\\ ", ":a:b " + quotearg: + ":simple ", ":\\0\\ \\t\\n'\"\\033??/\\\\ ", ":a:b " + quotearg_colon: + ":simple ", ":\\0\\ \\t\\n'\"\\033??/\\\\ ", ":a\\:b " + */ + custom_quoting_style }; /* Flags for use in set_quoting_flags. */ @@ -219,6 +249,17 @@ int set_char_quoting (struct quoting_options *o, char c, int i); behavior. Return the old value. */ int set_quoting_flags (struct quoting_options *o, int i); +/* In O (or in the default if O is null), + set the value of the quoting style to custom_quoting_style, + set the left quote to L, and set the right quote to R. + Each of L and R must be null-terminated and can be the empty + string. Because backslashes are used for escaping, it does not make + sense for R to contain a backslash. R must not begin with a digit or + a letter that has special meaning after a backslash (for example, + "\t" for tab). */ +void set_custom_quoting (struct quoting_options *o, + char const *l, char const *r); + /* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of argument ARG (of size ARGSIZE), using O to control quoting. If O is null, use the default. @@ -299,6 +340,24 @@ char *quotearg_colon (char const *arg); /* Like quotearg_colon (ARG), except it can quote null bytes. */ char *quotearg_colon_mem (char const *arg, size_t argsize); +/* Like quotearg_n_style (N, S, ARG) but with S as custom_quoting_style + with left quote as L and right quote as R. See set_custom_quoting + for a description of acceptable L and R values. */ +char *quotearg_n_custom (int n, char const *l, char const *r, + char const *arg); + +/* Like quotearg_n_custom (N, L, R, ARG) except it can quote null + bytes. */ +char *quotearg_n_custom_mem (int n, char const *l, char const *r, + char const *arg, size_t argsize); + +/* Equivalent to quotearg_n_custom (0, L, R, ARG). */ +char *quotearg_custom (char const *l, char const *r, char const *arg); + +/* Equivalent to quotearg_n_custom_mem (0, L, R, ARG, ARGSIZE). */ +char *quotearg_custom_mem (char const *l, char const *r, + char const *arg, size_t argsize); + /* Free any dynamically allocated memory. */ void quotearg_free (void); diff --git a/tests/test-quotearg.c b/tests/test-quotearg.c index 2e2c56b..5a680d9 100644 --- a/tests/test-quotearg.c +++ b/tests/test-quotearg.c @@ -199,6 +199,83 @@ static struct result_groups locale_results[] = { #endif /* ENABLE_NLS */ +static char const *custom_quotes[][2] = { + { "", "" }, + { "'", "'" }, + { "(", ")" }, + { ":", " " }, + { " ", ":" }, + { "# ", "\n\n" } +}; + +static struct result_groups custom_results[] = { + /* left = right = "" */ + { { "", "\\0001\\0", 7, "simple", + " \\t\\n'\"\\033?""?/\\\\", "a:b", "a\\\\b", + LQ_ENC RQ_ENC }, + { "", "\\0001\\0", 7, "simple", + " \\t\\n'\"\\033?""?/\\\\", "a:b", "a\\\\b", + LQ_ENC RQ_ENC }, + { "", "\\0001\\0", 7, "simple", + " \\t\\n'\"\\033?""?/\\\\", "a\\:b", "a\\\\b", + LQ_ENC RQ_ENC } }, + + /* left = right = "'" */ + { { "''", "'\\0001\\0'", 9, "'simple'", + "' \\t\\n\\'\"\\033?""?/\\\\'", "'a:b'", "'a\\\\b'", + "'" LQ_ENC RQ_ENC "'" }, + { "''", "'\\0001\\0'", 9, "'simple'", + "' \\t\\n\\'\"\\033?""?/\\\\'", "'a:b'", "'a\\\\b'", + "'" LQ_ENC RQ_ENC "'" }, + { "''", "'\\0001\\0'", 9, "'simple'", + "' \\t\\n\\'\"\\033?""?/\\\\'", "'a\\:b'", "'a\\\\b'", + "'" LQ_ENC RQ_ENC "'" } }, + + /* left = "(" and right = ")" */ + { { "()", "(\\0001\\0)", 9, "(simple)", + "( \\t\\n'\"\\033?""?/\\\\)", "(a:b)", "(a\\\\b)", + "(" LQ_ENC RQ_ENC ")" }, + { "()", "(\\0001\\0)", 9, "(simple)", + "( \\t\\n'\"\\033?""?/\\\\)", "(a:b)", "(a\\\\b)", + "(" LQ_ENC RQ_ENC ")" }, + { "()", "(\\0001\\0)", 9, "(simple)", + "( \\t\\n'\"\\033?""?/\\\\)", "(a\\:b)", "(a\\\\b)", + "(" LQ_ENC RQ_ENC ")" } }, + + /* left = ":" and right = " " */ + { { ": ", ":\\0001\\0 ", 9, ":simple ", + ":\\ \\t\\n'\"\\033?""?/\\\\ ", ":a:b ", ":a\\\\b ", + ":" LQ_ENC RQ_ENC " " }, + { ": ", ":\\0001\\0 ", 9, ":simple ", + ":\\ \\t\\n'\"\\033?""?/\\\\ ", ":a:b ", ":a\\\\b ", + ":" LQ_ENC RQ_ENC " " }, + { ": ", ":\\0001\\0 ", 9, ":simple ", + ":\\ \\t\\n'\"\\033?""?/\\\\ ", ":a\\:b ", ":a\\\\b ", + ":" LQ_ENC RQ_ENC " " } }, + + /* left = " " and right = ":" */ + { { " :", " \\0001\\0:", 9, " simple:", + " \\t\\n'\"\\033?""?/\\\\:", " a\\:b:", " a\\\\b:", + " " LQ_ENC RQ_ENC ":" }, + { " :", " \\0001\\0:", 9, " simple:", + " \\t\\n'\"\\033?""?/\\\\:", " a\\:b:", " a\\\\b:", + " " LQ_ENC RQ_ENC ":" }, + { " :", " \\0001\\0:", 9, " simple:", + " \\t\\n'\"\\033?""?/\\\\:", " a\\:b:", " a\\\\b:", + " " LQ_ENC RQ_ENC ":" } }, + + /* left = "# " and right = "\n\n" */ + { { "# \n\n", "# \\0001\\0\n\n", 11, "# simple\n\n", + "# \\t\\n'\"\\033?""?/\\\\\n\n", "# a:b\n\n", "# a\\\\b\n\n", + "# " LQ_ENC RQ_ENC "\n\n" }, + { "# \n\n", "# \\0001\\0\n\n", 11, "# simple\n\n", + "# \\t\\n'\"\\033?""?/\\\\\n\n", "# a:b\n\n", "# a\\\\b\n\n", + "# " LQ_ENC RQ_ENC "\n\n" }, + { "# \n\n", "# \\0001\\0\n\n", 11, "# simple\n\n", + "# \\t\\n'\"\\033?""?/\\\\\n\n", "# a\\:b\n\n", "# a\\\\b\n\n", + "# " LQ_ENC RQ_ENC "\n\n" } } +}; + static void compare (char const *a, size_t la, char const *b, size_t lb) { @@ -324,6 +401,15 @@ main (int argc, char *argv[]) ASSERT (set_quoting_flags (NULL, 0) == QA_SPLIT_TRIGRAPHS); + for (i = 0; i < sizeof custom_quotes / sizeof *custom_quotes; ++i) + { + set_custom_quoting (NULL, + custom_quotes[i][0], custom_quotes[i][1]); + compare_strings (use_quotearg_buffer, &custom_results[i].group1); + compare_strings (use_quotearg, &custom_results[i].group2); + compare_strings (use_quotearg_colon, &custom_results[i].group3); + } + #if ENABLE_NLS /* Clean up environment. */ unsetenv ("LANGUAGE"); -- 1.5.4.3 >From f4414fcbb869bda06d8c66d95451228cb016e034 Mon Sep 17 00:00:00 2001 From: Joel E. Denny <jde...@clemson.edu> Date: Sat, 22 Aug 2009 20:55:17 -0400 Subject: [PATCH] quotearg: document limitations of quote_these_too * lib/quotearg.c (quotearg_buffer_restyled): Add comments where those limitations are created. * lib/quotearg.h (set_char_quoting): Document that digits and letters that are special after backslash are not permitted. (quotearg_char): Cross-reference set_char_quoting documentation. --- ChangeLog | 9 +++++++++ lib/quotearg.c | 8 +++++++- lib/quotearg.h | 7 +++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 255e17e..42b96f4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,14 @@ 2009-08-22 Joel E. Denny <jde...@clemson.edu> + quotearg: document limitations of quote_these_too + * lib/quotearg.c (quotearg_buffer_restyled): Add comments where + those limitations are created. + * lib/quotearg.h (set_char_quoting): Document that digits and + letters that are special after backslash are not permitted. + (quotearg_char): Cross-reference set_char_quoting documentation. + +2009-08-22 Joel E. Denny <jde...@clemson.edu> + quotearg: implement custom_quoting_style * lib/quotearg.c: (struct quoting_options): Add left and right fields to store quotes. diff --git a/lib/quotearg.c b/lib/quotearg.c index 9e5f038..0c11a62 100644 --- a/lib/quotearg.c +++ b/lib/quotearg.c @@ -336,6 +336,10 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, STORE ('0'); } c = '0'; + /* We don't have to worry that this last '0' will be + backslash-escaped because, again, quote_string should + not start with it and because quote_these_too is + documented as not accepting it. */ } else if (flags & QA_ELIDE_NULL_BYTES) continue; @@ -461,7 +465,9 @@ quotearg_buffer_restyled (char *buffer, size_t buffersize, escape by prepending a backslash. However, it's hard to imagine any locale that would use digits or letters as quotes, and set_custom_quoting is documented not to accept - them. */ + them. Also, a digit or a special letter would cause + trouble if it appeared in quote_these_too, but that's also + documented as not accepting them. */ break; default: diff --git a/lib/quotearg.h b/lib/quotearg.h index 6704b32..0701d73 100644 --- a/lib/quotearg.h +++ b/lib/quotearg.h @@ -240,7 +240,9 @@ void set_quoting_style (struct quoting_options *o, enum quoting_style s); set the value of the quoting options for character C to I. Return the old value. Currently, the only values defined for I are 0 (the default) and 1 (which means to quote the character even if - it would not otherwise be quoted). */ + it would not otherwise be quoted). C must never be a digit or a + letter that has special meaning after a backslash (for example, "\t" + for tab). */ int set_char_quoting (struct quoting_options *o, char c, int i); /* In O (or in the default if O is null), @@ -328,7 +330,8 @@ char *quotearg_style (enum quoting_style s, char const *arg); char *quotearg_style_mem (enum quoting_style s, char const *arg, size_t argsize); -/* Like quotearg (ARG), except also quote any instances of CH. */ +/* Like quotearg (ARG), except also quote any instances of CH. + See set_char_quoting for a description of acceptable CH values. */ char *quotearg_char (char const *arg, char ch); /* Like quotearg_char (ARG, CH), except it can quote null bytes. */ -- 1.5.4.3