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.
This commit is contained in:
Blair Bonnett 2012-08-18 16:38:14 +12:00
parent 4cb80427fe
commit 1520026168

View File

@ -32,7 +32,7 @@
* terminator. Since unused bytes are set to zero, there should always be a * terminator. Since unused bytes are set to zero, there should always be a
* terminator. * 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; 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. */ /* 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; 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++) for(i = 0; message[i]; i++)
{ {
/* In a substitution block, no escaping needed. */ switch(message[i])
if(insub == 2)
{ {
if(message[i] == ']') insub = 0; /* Character to escape. */
} case ' ':
case '\n':
case '\\':
if(!sublevel) extra++;
substart = 0;
break;
/* Not in a substitution block. */ /* Heading into a substitution block. */
else{ case '%':
/* A character that needs escaping. */ substart = 1;
if(message[i] == ' ' || message[i] == '\n' || message[i] == '\\'){ break;
extra++; case '[':
insub = 0; if(substart) sublevel++;
} substart = 0;
break;
/* See if we're heading into a substitution block. */ /* Coming out of a substitution block. */
else if(message[i] == '%') insub = 1; case ']':
else if(message[i] == '[' && insub == 1) insub = 2; substart = 0;
else insub = 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 /* 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 * adc_msg_escape for this as keyword substitutions should not be escaped while
* general text should be. */ * general text should be. */
char* ucmd_msg_escape(const char* message) static char* ucmd_msg_escape(const char* message)
{ {
/* Allocate the memory we need. */ /* Allocate the memory we need. */
size_t esclen = ucmd_msg_escape_length(message); size_t esclen = ucmd_msg_escape_length(message);
char *escaped = hub_malloc(esclen + 1); 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 i;
size_t n = 0; size_t n = 0;
for(i = 0; message[i]; i++) for(i = 0; message[i]; i++)
{
/* In a substitution block, no escaping needed. */
if(insub == 2)
{
if(message[i] == ']') insub = 0;
escaped[n++] = message[i];
}
/* Not in a substitution block. */
else
{ {
switch(message[i]) switch(message[i])
{ {
/* Deal with characters that need escaping. */ /* Characters to escape provided we are *outside*
case '\\': * any substitution blocks. */
escaped[n++] = '\\';
escaped[n++] = '\\';
insub = 0;
break;
case '\n':
escaped[n++] = '\\';
escaped[n++] = 'n';
insub = 0;
break;
case ' ': case ' ':
if(sublevel)
{
escaped[n++] = ' ';
}
else
{
escaped[n++] = '\\'; escaped[n++] = '\\';
escaped[n++] = 's'; escaped[n++] = 's';
insub = 0; }
substart = 0;
break; break;
case '\n':
/* Characters that start a substitution block. */ if(sublevel)
case '%': {
escaped[n++] = message[i]; escaped[n++] = '\n';
insub = 1; }
break; else
case '[': {
escaped[n++] = message[i]; escaped[n++] = '\\';
if(insub == 1) insub = 2; escaped[n++] = 'n';
break; substart = 0;
/* Standard character. */
default:
escaped[n++] = message[i];
insub = 0;
break; break;
} }
case '\\':
escaped[n++] = '\\';
if(!sublevel) escaped[n++] = '\\';
substart = 0;
break;
/* Heading into a substitution block. */
case '%':
escaped[n++] = '%';
substart = 1;
break;
case '[':
escaped[n++] = '[';
if(substart) sublevel++;
substart = 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;
} }
} }