escaping a string for use in CR needs to only replace quotes and backslashes.

This commit is contained in:
Enno Rehling 2018-05-21 13:27:02 +02:00
parent 226fa2ba08
commit 60a86e7b2f
9 changed files with 266 additions and 22 deletions

View file

@ -517,7 +517,7 @@ static void report_crtypes(FILE * F, const struct locale *lang)
assert(hash > 0); assert(hash > 0);
fprintf(F, "MESSAGETYPE %d\n", hash); fprintf(F, "MESSAGETYPE %d\n", hash);
fputc('\"', F); fputc('\"', F);
fputs(str_escape(nrt_string(kmt->mtype, lang), buffer, sizeof(buffer)), F); fputs(crescape(nrt_string(kmt->mtype, lang), buffer, sizeof(buffer)), F);
fputs("\";text\n", F); fputs("\";text\n", F);
fprintf(F, "\"%s\";section\n", kmt->mtype->section); fprintf(F, "\"%s\";section\n", kmt->mtype->section);
} }

View file

@ -110,6 +110,15 @@ char* get_command(const order *ord, const struct locale *lang, char *sbuffer, si
return sbuffer; return sbuffer;
} }
const char *crescape(const char *str, char *buffer, size_t size) {
const char *replace = "\"\\";
const char * pos = strpbrk(str, replace);
if (!pos) {
return str;
}
return str_escape_ex(str, buffer, size, replace);
}
int stream_order(struct stream *out, const struct order *ord, const struct locale *lang, bool escape) int stream_order(struct stream *out, const struct order *ord, const struct locale *lang, bool escape)
{ {
const char *text; const char *text;
@ -151,7 +160,7 @@ int stream_order(struct stream *out, const struct order *ord, const struct local
char obuf[1024]; char obuf[1024];
swrite(" ", 1, 1, out); swrite(" ", 1, 1, out);
if (escape) { if (escape) {
text = str_escape(text, obuf, sizeof(obuf)); text = crescape(text, obuf, sizeof(obuf));
} }
swrite(text, 1, strlen(text), out); swrite(text, 1, strlen(text), out);
} }

View file

@ -67,6 +67,7 @@ extern "C" {
bool is_exclusive(const order * ord); bool is_exclusive(const order * ord);
bool is_repeated(keyword_t kwd); bool is_repeated(keyword_t kwd);
bool is_long(keyword_t kwd); bool is_long(keyword_t kwd);
const char *crescape(const char *str, char *buffer, size_t size);
char *write_order(const order * ord, const struct locale *lang, char *write_order(const order * ord, const struct locale *lang,
char *buffer, size_t size); char *buffer, size_t size);

View file

@ -14,6 +14,7 @@
#include <tests.h> #include <tests.h>
#include <CuTest.h> #include <CuTest.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
static void test_create_order(CuTest *tc) { static void test_create_order(CuTest *tc) {
char cmd[32]; char cmd[32];
@ -484,10 +485,62 @@ static void test_study_order_unknown_quoted(CuTest *tc) {
test_teardown(); test_teardown();
} }
static void test_create_order_long(CuTest *tc) {
char buffer[2048];
order *ord;
size_t len;
struct locale *lang;
stream out;
const char * longstr = "// BESCHREIBEN EINHEIT \"In weiÃ&#131; &#131; &#131; &#131; &#131; &#131; &#131; &#"
"131;&#131;&#131;&#131;&#131;&#131;&#131;?e GewÃ&#131;&#131;&#131;&#131;&#13"
"1;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;Ã&#131;&#131;&#131;"
"&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;Ã&#131;&#131;&#131;&#"
"131;&#131;&#131;&#131;&#131;&#131;&#131;&#130;Ã&#131;&#131;&#131;&#131;&#13"
"1;&#131;&#131;&#131;&#131;&#130;Ã&#131;&#131;&#131;&#131;&#131;&#131;&#131;"
"&#131;&#130;Ã&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#130;Ã&#131;&#131;&"
"#131;&#131;&#131;&#131;&#130;Ã&#131;&#131;&#131;&#131;&#131;&#130;Ã&#131;&#"
"131;&#131;&#131;&#130;Ã&#131;&#131;&#131;&#130;Ã&#131;&#131;&#130;Ã&#131;&#"
"130;Ã&#130;¢&#130;Ã&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&"
"#131;&#131;&#130;Ã&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#1"
"31;&#130;Ã&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#130;Ã&#13"
"1;&#131;&#131;&#131;&#131;&#131;&#131;&#131;&#130;Ã&#131;&#131;&#131;&#131;"
"&#131;&#131;&#131;&#130[...]hB&#65533;&#65533;2&#65533;xa&#65533;Hv$P&#65533;xa&#65533;&#65533;A&#65533;&#65533;&#65533;A&#65533;&#65533;";
test_setup();
lang = test_create_locale();
ord = parse_order(longstr, lang);
len = strlen(longstr);
CuAssertIntEquals(tc, 0, ord->command);
mstream_init(&out);
stream_order(&out, ord, lang, true);
out.api->rewind(out.handle);
out.api->readln(out.handle, buffer, sizeof(buffer));
mstream_done(&out);
free_order(ord);
test_teardown();
}
static void test_crescape(CuTest *tc) {
char buffer[16];
const char *input = "12345678901234567890";
CuAssertStrEquals(tc, "1234", crescape("1234", buffer, 16));
CuAssertPtrEquals(tc, (void *)input, (void *)crescape(input, buffer, 16));
CuAssertStrEquals(tc, "\\\"1234\\\"", crescape("\"1234\"", buffer, 16));
CuAssertStrEquals(tc, "\\\"1234\\\"", buffer);
CuAssertStrEquals(tc, "\\\"1234", crescape("\"1234\"", buffer, 8));
/* unlike in C strings, only " and \ are escaped: */
CuAssertStrEquals(tc, "\\\"\\\\\n\r\'", crescape("\"\\\n\r\'", buffer, 16));
}
CuSuite *get_order_suite(void) CuSuite *get_order_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_crescape);
SUITE_ADD_TEST(suite, test_create_order); SUITE_ADD_TEST(suite, test_create_order);
SUITE_ADD_TEST(suite, test_create_order_long);
SUITE_ADD_TEST(suite, test_study_orders); SUITE_ADD_TEST(suite, test_study_orders);
SUITE_ADD_TEST(suite, test_study_order); SUITE_ADD_TEST(suite, test_study_order);
SUITE_ADD_TEST(suite, test_study_order_unknown); SUITE_ADD_TEST(suite, test_study_order_unknown);

View file

@ -46,7 +46,7 @@
static int read_permissions(variant *var, void *owner, struct gamedata *data) static int read_permissions(variant *var, void *owner, struct gamedata *data)
{ {
attrib *a; attrib *a = NULL;
UNUSED_ARG(var); UNUSED_ARG(var);
read_attribs(data, &a, owner); read_attribs(data, &a, owner);
a_remove(&a, a); a_remove(&a, a);

View file

@ -172,8 +172,8 @@ static order *monster_attack(unit * u, const unit * target)
if (monster_is_waiting(u)) if (monster_is_waiting(u))
return NULL; return NULL;
if (u->region->land) { if (u->region->land && (u->region->flags & RF_GUARDED) == 0) {
assert(u->region->flags & RF_GUARDED); return NULL;
} }
return create_order(K_ATTACK, u->faction->locale, "%i", target->no); return create_order(K_ATTACK, u->faction->locale, "%i", target->no);
} }

View file

@ -20,9 +20,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <platform.h> #include <platform.h>
#endif #endif
#include "strings.h" #include "strings.h"
#include "assert.h"
/* libc includes */ /* libc includes */
#include <ctype.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
@ -155,27 +155,28 @@ int str_hash(const char *s)
return key & 0x7FFFFFFF; return key & 0x7FFFFFFF;
} }
const char *str_escape(const char *str, char *buffer, size_t len) const char *str_escape_wrong(const char *str, char *buffer, size_t len)
{ {
const char *handle_start = strchr(str, '\"'); const char *handle_start = strchr(str, '\"');
if (!handle_start) handle_start = strchr(str, '\\'); if (!handle_start) handle_start = strchr(str, '\\');
assert(buffer); assert(buffer);
if (handle_start) { if (handle_start) {
const char *p; const char *p = str;
char *o; char *o = buffer;
size_t skip = handle_start - str; size_t skip = handle_start - str;
if (skip > len) { if (skip > len) {
skip = len; skip = len;
} }
if (skip > 0) {
memcpy(buffer, str, skip); memcpy(buffer, str, skip);
o = buffer + skip; o += skip;
p = str + skip; p += skip;
len -= skip; len -= skip;
}
do { do {
if (*p == '\"' || *p == '\\') { if (*p == '\"' || *p == '\\') {
if (len < 2) { if (len < 2) {
*o = '\0';
break; break;
} }
(*o++) = '\\'; (*o++) = '\\';
@ -183,13 +184,15 @@ const char *str_escape(const char *str, char *buffer, size_t len)
} }
else { else {
if (len < 1) { if (len < 1) {
*o = '\0';
break; break;
} }
--len; --len;
} }
if (len > 0) {
(*o++) = (*p); (*o++) = (*p);
} while (*p++); }
} while (len > 0 && *p++);
*o = '\0';
return buffer; return buffer;
} }
return str; return str;
@ -306,3 +309,166 @@ char *str_unescape(char *str) {
} }
return str; return str;
} }
const char *str_escape_ex(const char *str, char *buffer, size_t size, const char *chars)
{
const char *read = str;
char *write = buffer;
if (size < 1) return NULL;
while (size > 1 && *read) {
const char *pos = strpbrk(read, chars);
size_t len = size;
if (pos) {
len = pos - read;
}
if (len < size) {
unsigned char ch = *(const unsigned char *)pos;
if (len > 0) {
memmove(write, read, len);
write += len;
read += len;
size -= len;
}
switch (ch) {
case '\t':
if (size > 2) {
*write++ = '\\';
*write++ = 't';
size -= 2;
}
else size = 1;
break;
case '\n':
if (size > 2) {
*write++ = '\\';
*write++ = 'n';
size -= 2;
}
else size = 1;
break;
case '\r':
if (size > 2) {
*write++ = '\\';
*write++ = 'r';
size -= 2;
}
else size = 1;
break;
case '\"':
case '\'':
case '\\':
if (size > 2) {
*write++ = '\\';
*write++ = ch;
size -= 2;
}
break;
default:
if (size > 5) {
int n = sprintf(write, "\\%03o", ch);
if (n > 0) {
assert(n == 5);
write += n;
size -= n;
}
else size = 1;
}
else size = 1;
}
++read;
} else {
/* end of buffer space */
len = size - 1;
if (len > 0) {
memmove(write, read, len);
write += len;
size -= len;
break;
}
}
}
*write = '\0';
return buffer;
}
const char *str_escape(const char *str, char *buffer, size_t size) {
return str_escape_ex(str, buffer, size, "\n\t\r\'\"\\");
}
const char *str_escape_slow(const char *str, char *buffer, size_t size) {
const char *read = str;
char *write = buffer;
if (size < 1) return NULL;
while (size > 1 && *read) {
size_t len;
const char *pos = read;
while (pos + 1 < read + size && *pos) {
unsigned char ch = *(unsigned char *)pos;
if (iscntrl(ch) || ch == '\"' || ch == '\\' || ch == '\'' || ch == '\n' || ch == '\r' || ch == '\t') {
len = pos - read;
memmove(write, read, len);
write += len;
size -= len;
switch (ch) {
case '\t':
if (size > 2) {
*write++ = '\\';
*write++ = 't';
size -= 2;
}
else size = 1;
break;
case '\n':
if (size > 2) {
*write++ = '\\';
*write++ = 'n';
size -= 2;
}
else size = 1;
break;
case '\r':
if (size > 2) {
*write++ = '\\';
*write++ = 'r';
size -= 2;
}
else size = 1;
break;
case '\"':
case '\'':
case '\\':
if (size > 2) {
*write++ = '\\';
*write++ = ch;
size -= 2;
}
break;
default:
if (size > 5) {
int n = sprintf(write, "\\%03o", ch);
if (n > 0) {
assert(n == 5);
write += n;
size -= n;
}
else size = 1;
}
else size = 1;
}
assert(size > 0);
read = pos + 1;
break;
}
++pos;
}
if (read < pos) {
len = pos - read;
memmove(write, read, len);
read = pos;
write += len;
size -= len;
}
}
*write = '\0';
return buffer;
}

View file

@ -32,7 +32,8 @@ extern "C" {
size_t str_strlcat(char *dst, const char *src, size_t len); size_t str_strlcat(char *dst, const char *src, size_t len);
char *str_strdup(const char *s); char *str_strdup(const char *s);
const char *str_escape(const char *str, char *buffer, size_t len); const char *str_escape(const char *str, char *buffer, size_t size);
const char *str_escape_ex(const char *str, char *buffer, size_t size, const char *chars);
char *str_unescape(char *str); char *str_unescape(char *str);
unsigned int jenkins_hash(unsigned int a); unsigned int jenkins_hash(unsigned int a);

View file

@ -24,10 +24,24 @@ static void test_str_unescape(CuTest * tc)
static void test_str_escape(CuTest * tc) static void test_str_escape(CuTest * tc)
{ {
char scratch[64]; char scratch[16];
CuAssertStrEquals(tc, "12345678901234567890", str_escape("12345678901234567890", scratch, 16));
CuAssertStrEquals(tc, "123456789\\\"12345", str_escape("123456789\"1234567890", scratch, 16)); CuAssertPtrEquals(tc, NULL, (void *)str_escape("1234", scratch, 0));
CuAssertStrEquals(tc, "1234567890123456", str_escape("1234567890123456\"890", scratch, 16)); CuAssertStrEquals(tc, "", str_escape("1234", scratch, 1));
CuAssertStrEquals(tc, "1", str_escape("1234", scratch, 2));
CuAssertStrEquals(tc, "\\n", str_escape("\n234", scratch, 3));
CuAssertStrEquals(tc, "\\n\\r\\t\\\\\\\"\\\'", str_escape("\n\r\t\\\"\'", scratch, 16));
CuAssertStrEquals(tc, "12345678", str_escape("12345678", scratch, 16));
CuAssertStrEquals(tc, "123456789012345", str_escape("123456789012345", scratch, 16));
CuAssertStrEquals(tc, "12345678901234", str_escape("12345678901234\n", scratch, 15));
CuAssertStrEquals(tc, "123456789012345", str_escape("12345678901234567890", scratch, 16));
CuAssertStrEquals(tc, "\\\\\\\"234", str_escape("\\\"234567890", scratch, 8));
CuAssertStrEquals(tc, "\\\"\\\\234", str_escape("\"\\234567890", scratch, 8));
CuAssertStrEquals(tc, "123456789012345", str_escape("12345678901234567890", scratch, 16));
CuAssertStrEquals(tc, "123456789\\\"1234", str_escape("123456789\"1234567890", scratch, 16));
CuAssertStrEquals(tc, "123456789012345", str_escape("1234567890123456\"890", scratch, 16));
CuAssertStrEquals(tc, "hello world", str_escape("hello world", scratch, sizeof(scratch))); CuAssertStrEquals(tc, "hello world", str_escape("hello world", scratch, sizeof(scratch)));
CuAssertStrEquals(tc, "hello \\\"world\\\"", str_escape("hello \"world\"", scratch, sizeof(scratch))); CuAssertStrEquals(tc, "hello \\\"world\\\"", str_escape("hello \"world\"", scratch, sizeof(scratch)));
CuAssertStrEquals(tc, "\\\"\\\\", str_escape("\"\\", scratch, sizeof(scratch))); CuAssertStrEquals(tc, "\\\"\\\\", str_escape("\"\\", scratch, sizeof(scratch)));