Commit e3222d80 authored by Eric Wong's avatar Eric Wong

Revert buffer2array() behavior back to tried and true 0.11.x version

Warren's fix in r4872 made phpMp work again, but also broke the unit tests completely (they work in this version). The version in 0.12.0 is far too buggy (it was from mpd-ke, what do you expect?). This one passes all the unit tests that the mpd-ke one passed, and should also work with phpMp when used with PHP magic quotes. This also means we can search on 100 (or more) tags at once, so no more arbitrary limits other than system memory. To run the unit tests, just do this: gcc -o t -DUNIT_TEST=1 src/buffer2array.c && ./t && echo OK git-svn-id: https://svn.musicpd.org/mpd/trunk@4874 09075e82-0dd4-0310-85a5-a0d7c8717e4f
parent 1a51bfb8
...@@ -17,54 +17,111 @@ ...@@ -17,54 +17,111 @@
*/ */
#include "buffer2array.h" #include "buffer2array.h"
#ifdef UNIT_TEST
# define xstrdup(x) strdup(x)
# define xmalloc(x) malloc(x)
#else
# include "utils.h"
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
int buffer2array(char *origBuffer, char ***array)
inline static
int
isWhiteSpace(char c)
{ {
return (c == ' ' || c == '\t'); int quotes = 0;
} int count = 0;
int i;
int curr;
int *beginArray;
char *buffer = xstrdup(origBuffer);
int bufferLength = strlen(buffer);
char *markArray = xmalloc(sizeof(char) * (bufferLength + 1));
for (curr = 0; curr < bufferLength; curr++) {
if (!quotes && (buffer[curr] == ' ' || buffer[curr] == '\t')) {
markArray[curr] = '0';
} else if (buffer[curr] == '\"') {
if (curr > 0 && buffer[curr - 1] != '\\') {
quotes = quotes ? 0 : 1;
markArray[curr] = '0';
} else {
markArray[curr] = '1';
}
} else {
markArray[curr] = '1';
}
if (markArray[curr] == '1') {
if (curr > 0) {
if (markArray[curr - 1] == '0') {
count++;
}
} else {
count++;
}
}
}
markArray[bufferLength] = '\0';
int buffer2array(char *buffer, char *array[], const int max) if (!count) {
{ free(buffer);
int i = 0; free(markArray);
char *c = buffer; return count;
}
while (*c != '\0' && i < max) {
if (*c == '\"') { beginArray = xmalloc(sizeof(int) * count);
array[i++] = ++c; (*array) = xmalloc(sizeof(char *) * count);
while (*c != '\0') {
if (*c == '\"') { count = 0;
*(c++) = '\0';
break; for (curr = 0; curr < bufferLength; curr++) {
} if (markArray[curr] == '1') {
else if (*(c++) == '\\') { if (curr > 0) {
memmove(c - 1, c, strlen(c) + 1); if (markArray[curr - 1] == '0') {
++c; beginArray[count++] = curr;
} }
} else {
beginArray[count++] = curr;
} }
} else { } else {
while (isWhiteSpace(*c)) buffer[curr] = '\0';
++c;
array[i++] = c++;
if (*c == '\0')
return i;
while (!isWhiteSpace(*c) && *c != '\0')
++c;
} }
if (*c == '\0')
return i;
*(c++) = '\0';
while (isWhiteSpace(*c))
++c;
} }
return i;
for (i = 0; i < count; i++) {
int len = strlen(buffer + beginArray[i]) + 1;
int arrayCurr = 0;
(*array)[i] = xmalloc(sizeof(char) * len);
for (curr = beginArray[i]; buffer[curr] != '\0'; curr++) {
if (buffer[curr] == '\\') {
if (buffer[curr + 1] != '\0') {
curr++;
}
}
(*array)[i][arrayCurr++] = buffer[curr];
}
(*array)[i][arrayCurr] = '\0';
}
free(markArray);
free(beginArray);
free(buffer);
return count;
}
void freeArgArray(char **array, int argArrayLength)
{
int i;
if (argArrayLength == 0)
return;
for (i = 0; i < argArrayLength; i++) {
free(array[i]);
}
free(array);
} }
#ifdef UNIT_TEST #ifdef UNIT_TEST
...@@ -75,42 +132,42 @@ int buffer2array(char *buffer, char *array[], const int max) ...@@ -75,42 +132,42 @@ int buffer2array(char *buffer, char *array[], const int max)
int main() int main()
{ {
char *a[4] = { NULL }; char **a;
char *b; char *b;
int i, max; int i, max;
b = xstrdup("lsinfo \"/some/dir/name \\\"test\\\"\""); b = xstrdup("lsinfo \"/some/dir/name \\\"test\\\"\"");
max = buffer2array(b, a, 4); max = buffer2array(b, &a);
assert( !strcmp("lsinfo", a[0]) ); assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir/name \"test\"", a[1]) ); assert( !strcmp("/some/dir/name \"test\"", a[1]) );
assert( !a[2] ); assert( !a[2] );
b = xstrdup("lsinfo \"/some/dir/name \\\"test\\\" something else\""); b = xstrdup("lsinfo \"/some/dir/name \\\"test\\\" something else\"");
max = buffer2array(b, a, 4); max = buffer2array(b, &a);
assert( !strcmp("lsinfo", a[0]) ); assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir/name \"test\" something else", a[1]) ); assert( !strcmp("/some/dir/name \"test\" something else", a[1]) );
assert( !a[2] ); assert( !a[2] );
b = xstrdup("lsinfo \"/some/dir\\\\name\""); b = xstrdup("lsinfo \"/some/dir\\\\name\"");
max = buffer2array(b, a, 4); max = buffer2array(b, &a);
assert( !strcmp("lsinfo", a[0]) ); assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir\\name", a[1]) ); assert( !strcmp("/some/dir\\name", a[1]) );
assert( !a[2] ); assert( !a[2] );
b = xstrdup("lsinfo \"/some/dir name\""); b = xstrdup("lsinfo \"/some/dir name\"");
max = buffer2array(b, a, 4); max = buffer2array(b, &a);
assert( !strcmp("lsinfo", a[0]) ); assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("/some/dir name", a[1]) ); assert( !strcmp("/some/dir name", a[1]) );
assert( !a[2] ); assert( !a[2] );
b = xstrdup("lsinfo \"\\\"/some/dir\\\"\""); b = xstrdup("lsinfo \"\\\"/some/dir\\\"\"");
max = buffer2array(b, a, 4); max = buffer2array(b, &a);
assert( !strcmp("lsinfo", a[0]) ); assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("\"/some/dir\"", a[1]) ); assert( !strcmp("\"/some/dir\"", a[1]) );
assert( !a[2] ); assert( !a[2] );
b = xstrdup("lsinfo \"\\\"/some/dir\\\" x\""); b = xstrdup("lsinfo \"\\\"/some/dir\\\" x\"");
max = buffer2array(b, a, 4); max = buffer2array(b, &a);
assert( !strcmp("lsinfo", a[0]) ); assert( !strcmp("lsinfo", a[0]) );
assert( !strcmp("\"/some/dir\" x", a[1]) ); assert( !strcmp("\"/some/dir\" x", a[1]) );
assert( !a[2] ); assert( !a[2] );
......
...@@ -21,12 +21,8 @@ ...@@ -21,12 +21,8 @@
#include "../config.h" #include "../config.h"
/* tokenizes up to max elements in buffer (a null-terminated string) and int buffer2array(char *buffer, char ***array);
* stores the result in array (which must be capable of holding up to
* max elements). Tokenization is based on C string quoting rules. void freeArgArray(char **array, int argArrayLength);
* The arguments buffer and array are modified.
* Returns the number of elements tokenized.
*/
int buffer2array(char *buffer, char *array[], const int max);
#endif #endif
...@@ -108,13 +108,6 @@ ...@@ -108,13 +108,6 @@
#define COMMAND_STATUS_AUDIO "audio" #define COMMAND_STATUS_AUDIO "audio"
#define COMMAND_STATUS_UPDATING_DB "updating_db" #define COMMAND_STATUS_UPDATING_DB "updating_db"
/*
* The most we ever use is for search/find, and that limits it to the
* number of tags we can have. Add one for the command, and one extra
* to catch errors clients may send us
*/
#define COMMAND_ARGV_MAX (2+(TAG_NUM_OF_ITEM_TYPES*2))
typedef struct _CommandEntry CommandEntry; typedef struct _CommandEntry CommandEntry;
typedef int (*CommandHandlerFunction) (int, int *, int, char **); typedef int (*CommandHandlerFunction) (int, int *, int, char **);
...@@ -1059,28 +1052,27 @@ static CommandEntry *getCommandEntryAndCheckArgcAndPermission(int fd, ...@@ -1059,28 +1052,27 @@ static CommandEntry *getCommandEntryAndCheckArgcAndPermission(int fd,
static CommandEntry *getCommandEntryFromString(char *string, int *permission) static CommandEntry *getCommandEntryFromString(char *string, int *permission)
{ {
CommandEntry *cmd = NULL; CommandEntry *cmd = NULL;
char *argv[COMMAND_ARGV_MAX] = { NULL }; char **argv;
int argc = buffer2array(string, argv, COMMAND_ARGV_MAX); int argc = buffer2array(string, &argv);
if (0 == argc) if (0 == argc)
return NULL; return NULL;
cmd = getCommandEntryAndCheckArgcAndPermission(0, permission, cmd = getCommandEntryAndCheckArgcAndPermission(0, permission,
argc, argv); argc, argv);
freeArgArray(argv, argc);
return cmd; return cmd;
} }
static int processCommandInternal(int fd, int *permission, static int processCommandInternal(int fd, int *permission,
char *commandString, struct strnode *cmdnode) char *string, struct strnode *cmdnode)
{ {
int argc; char **argv;
char *argv[COMMAND_ARGV_MAX] = { NULL }; int argc = buffer2array(string, &argv);
CommandEntry *cmd; CommandEntry *cmd;
int ret = -1; int ret = -1;
argc = buffer2array(commandString, argv, COMMAND_ARGV_MAX);
if (argc == 0) if (argc == 0)
return 0; return 0;
...@@ -1094,6 +1086,8 @@ static int processCommandInternal(int fd, int *permission, ...@@ -1094,6 +1086,8 @@ static int processCommandInternal(int fd, int *permission,
} }
} }
freeArgArray(argv, argc);
current_command = NULL; current_command = NULL;
return ret; return ret;
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
#define CONF_REPEATABLE_MASK 0x01 #define CONF_REPEATABLE_MASK 0x01
#define CONF_BLOCK_MASK 0x02 #define CONF_BLOCK_MASK 0x02
#define CONF_LINE_TOKEN_MAX 3
typedef struct _configEntry { typedef struct _configEntry {
unsigned char mask; unsigned char mask;
...@@ -194,16 +193,15 @@ static ConfigParam *readConfigBlock(FILE * fp, int *count, char *string) ...@@ -194,16 +193,15 @@ static ConfigParam *readConfigBlock(FILE * fp, int *count, char *string)
{ {
ConfigParam *ret = newConfigParam(NULL, *count); ConfigParam *ret = newConfigParam(NULL, *count);
char **array;
int i; int i;
int numberOfArgs; int numberOfArgs;
int argsMinusComment; int argsMinusComment;
while (myFgets(string, MAX_STRING_SIZE, fp)) { while (myFgets(string, MAX_STRING_SIZE, fp)) {
char *array[CONF_LINE_TOKEN_MAX] = { NULL };
(*count)++; (*count)++;
numberOfArgs = buffer2array(string, array, CONF_LINE_TOKEN_MAX); numberOfArgs = buffer2array(string, &array);
for (i = 0; i < numberOfArgs; i++) { for (i = 0; i < numberOfArgs; i++) {
if (array[i][0] == CONF_COMMENT) if (array[i][0] == CONF_COMMENT)
...@@ -213,11 +211,13 @@ static ConfigParam *readConfigBlock(FILE * fp, int *count, char *string) ...@@ -213,11 +211,13 @@ static ConfigParam *readConfigBlock(FILE * fp, int *count, char *string)
argsMinusComment = i; argsMinusComment = i;
if (0 == argsMinusComment) { if (0 == argsMinusComment) {
freeArgArray(array, numberOfArgs);
continue; continue;
} }
if (1 == argsMinusComment && if (1 == argsMinusComment &&
0 == strcmp(array[0], CONF_BLOCK_END)) { 0 == strcmp(array[0], CONF_BLOCK_END)) {
freeArgArray(array, numberOfArgs);
break; break;
} }
...@@ -238,6 +238,8 @@ static ConfigParam *readConfigBlock(FILE * fp, int *count, char *string) ...@@ -238,6 +238,8 @@ static ConfigParam *readConfigBlock(FILE * fp, int *count, char *string)
} }
addBlockParam(ret, array[0], array[1], *count); addBlockParam(ret, array[0], array[1], *count);
freeArgArray(array, numberOfArgs);
} }
return ret; return ret;
...@@ -254,6 +256,7 @@ void readConf(char *file) ...@@ -254,6 +256,7 @@ void readConf(char *file)
ConfigEntry *entry; ConfigEntry *entry;
void *voidPtr; void *voidPtr;
ConfigParam *param; ConfigParam *param;
char **array;
if (!(fp = fopen(file, "r"))) { if (!(fp = fopen(file, "r"))) {
ERROR("problems opening file %s for reading: %s\n", file, ERROR("problems opening file %s for reading: %s\n", file,
...@@ -262,10 +265,9 @@ void readConf(char *file) ...@@ -262,10 +265,9 @@ void readConf(char *file)
} }
while (myFgets(string, MAX_STRING_SIZE, fp)) { while (myFgets(string, MAX_STRING_SIZE, fp)) {
char *array[CONF_LINE_TOKEN_MAX] = { NULL };
count++; count++;
numberOfArgs = buffer2array(string, array, CONF_LINE_TOKEN_MAX); numberOfArgs = buffer2array(string, &array);
for (i = 0; i < numberOfArgs; i++) { for (i = 0; i < numberOfArgs; i++) {
if (array[i][0] == CONF_COMMENT) if (array[i][0] == CONF_COMMENT)
...@@ -275,6 +277,7 @@ void readConf(char *file) ...@@ -275,6 +277,7 @@ void readConf(char *file)
argsMinusComment = i; argsMinusComment = i;
if (0 == argsMinusComment) { if (0 == argsMinusComment) {
freeArgArray(array, numberOfArgs);
continue; continue;
} }
...@@ -313,6 +316,8 @@ void readConf(char *file) ...@@ -313,6 +316,8 @@ void readConf(char *file)
param = newConfigParam(array[1], count); param = newConfigParam(array[1], count);
insertInListWithoutKey(entry->configParamList, param); insertInListWithoutKey(entry->configParamList, param);
freeArgArray(array, numberOfArgs);
} }
fclose(fp); fclose(fp);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment