From 152002616846a839f351a95d438f95409b43db5e Mon Sep 17 00:00:00 2001 From: Blair Bonnett Date: Sat, 18 Aug 2012 16:38:14 +1200 Subject: [PATCH] User commands: handle nested substitution blocks when escaping message. The UCMD specification doesn't mention nested blocks, and LinuxDC++ (and therefore presumably any other client based off the DC++ core) doesn't support them. But for future-proofing / clients that do support them, make sure the ucmd_escape_msg() function can handle nested blocks properly. --- src/core/pluginucmd.c | 150 +++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 61 deletions(-) diff --git a/src/core/pluginucmd.c b/src/core/pluginucmd.c index ea0e78e..0b81e93 100644 --- a/src/core/pluginucmd.c +++ b/src/core/pluginucmd.c @@ -32,7 +32,7 @@ * terminator. Since unused bytes are set to zero, there should always be a * terminator. */ -int ucmd_expand_tt(struct plugin_ucmd* ucmd, size_t size) +static int ucmd_expand_tt(struct plugin_ucmd* ucmd, size_t size) { if(size < ucmd->capacity) return 1; @@ -55,31 +55,44 @@ int ucmd_expand_tt(struct plugin_ucmd* ucmd, size_t size) } /* Calculate the number of characters needed to store the escaped message. */ -size_t ucmd_msg_escape_length(const char* message) +static size_t ucmd_msg_escape_length(const char* message) { - size_t extra = 0; size_t i; - int insub = 0; + size_t extra = 0; + + /* Keep track of substitution blocks. */ + int substart = 0; /* Possible start of a block. */ + int sublevel = 0; /* Number of nested blocks we are in. */ + for(i = 0; message[i]; i++) { - /* In a substitution block, no escaping needed. */ - if(insub == 2) + switch(message[i]) { - if(message[i] == ']') insub = 0; - } + /* Character to escape. */ + case ' ': + case '\n': + case '\\': + if(!sublevel) extra++; + substart = 0; + break; - /* Not in a substitution block. */ - else{ - /* A character that needs escaping. */ - if(message[i] == ' ' || message[i] == '\n' || message[i] == '\\'){ - extra++; - insub = 0; - } + /* Heading into a substitution block. */ + case '%': + substart = 1; + break; + case '[': + if(substart) sublevel++; + substart = 0; + break; - /* See if we're heading into a substitution block. */ - else if(message[i] == '%') insub = 1; - else if(message[i] == '[' && insub == 1) insub = 2; - else insub = 0; + /* Coming out of a substitution block. */ + case ']': + substart = 0; + if(sublevel) sublevel --; + break; + + default: + break; } } @@ -90,63 +103,78 @@ size_t ucmd_msg_escape_length(const char* message) /* Escape a message that is being put into a user command. We cannot use * adc_msg_escape for this as keyword substitutions should not be escaped while * general text should be. */ -char* ucmd_msg_escape(const char* message) +static char* ucmd_msg_escape(const char* message) { /* Allocate the memory we need. */ size_t esclen = ucmd_msg_escape_length(message); char *escaped = hub_malloc(esclen + 1); + if(escaped == NULL) return NULL; + + /* Keep track of substitution blocks. */ + int substart = 0; /* Possible start of a block. */ + int sublevel = 0; /* Number of nested blocks we are in. */ - int insub = 0; size_t i; size_t n = 0; - for(i = 0; message[i]; i++) { - /* In a substitution block, no escaping needed. */ - if(insub == 2) + switch(message[i]) { - if(message[i] == ']') insub = 0; - escaped[n++] = message[i]; - } - - /* Not in a substitution block. */ - else - { - switch(message[i]) - { - /* Deal with characters that need escaping. */ - case '\\': - escaped[n++] = '\\'; - escaped[n++] = '\\'; - insub = 0; - break; - case '\n': - escaped[n++] = '\\'; - escaped[n++] = 'n'; - insub = 0; - break; - case ' ': + /* Characters to escape provided we are *outside* + * any substitution blocks. */ + case ' ': + if(sublevel) + { + escaped[n++] = ' '; + } + else + { escaped[n++] = '\\'; escaped[n++] = 's'; - insub = 0; + } + substart = 0; + break; + case '\n': + if(sublevel) + { + escaped[n++] = '\n'; + } + else + { + escaped[n++] = '\\'; + escaped[n++] = 'n'; + substart = 0; break; + } + case '\\': + escaped[n++] = '\\'; + if(!sublevel) escaped[n++] = '\\'; + substart = 0; + break; - /* Characters that start a substitution block. */ - case '%': - escaped[n++] = message[i]; - insub = 1; - break; - case '[': - escaped[n++] = message[i]; - if(insub == 1) insub = 2; - break; + /* Heading into a substitution block. */ + case '%': + escaped[n++] = '%'; + substart = 1; + break; + case '[': + escaped[n++] = '['; + if(substart) sublevel++; + substart = 0; + break; - /* Standard character. */ - default: - escaped[n++] = message[i]; - insub = 0; - break; - } + /* Coming out of a substitution block. */ + case ']': + escaped[n++] = ']'; + if(sublevel) sublevel--; + substart = 0; + break; + + /* Normal character. */ + default: + escaped[n++] = message[i]; + substart = 0; + break; } }