summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Noordhuis <pcnoordhuis@gmail.com>2011-07-10 17:28:51 +0200
committerPieter Noordhuis <pcnoordhuis@gmail.com>2011-07-10 17:28:51 +0200
commite40daec29aa738f18e86a93c087e1d1b584e4f5d (patch)
treebe9d6c5c7fcc7190da0f6ed32a812e0a0a9f811d
parent5def86434e1ff4554885af3436cb2d83a8349ef3 (diff)
parentdad240f0a364829a9969d864de5121f33d7332c2 (diff)
Merge branch 'printf-width'
-rw-r--r--hiredis.c88
-rw-r--r--test.c58
2 files changed, 103 insertions, 43 deletions
diff --git a/hiredis.c b/hiredis.c
index 9ea998a..dfee705 100644
--- a/hiredis.c
+++ b/hiredis.c
@@ -770,33 +770,79 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
while (*_p != '\0' && isdigit(*_p)) _p++;
}
- /* Modifiers */
- if (*_p != '\0') {
- if (*_p == 'h' || *_p == 'l') {
- /* Allow a single repetition for these modifiers */
- if (_p[0] == _p[1]) _p++;
- _p++;
+ /* Copy va_list before consuming with va_arg */
+ va_copy(_cpy,ap);
+
+ /* Integer conversion (without modifiers) */
+ if (strchr("diouxX",*_p) != NULL) {
+ va_arg(ap,int);
+ goto fmt_valid;
+ }
+
+ /* Double conversion (without modifiers) */
+ if (strchr("eEfFgGaA",*_p) != NULL) {
+ va_arg(ap,double);
+ goto fmt_valid;
+ }
+
+ /* Size: char */
+ if (_p[0] == 'h' && _p[1] == 'h') {
+ _p += 2;
+ if (*_p != '\0' && strchr("diouxX",*_p) != NULL) {
+ va_arg(ap,int); /* char gets promoted to int */
+ goto fmt_valid;
+ }
+ goto fmt_invalid;
+ }
+
+ /* Size: short */
+ if (_p[0] == 'h') {
+ _p += 1;
+ if (*_p != '\0' && strchr("diouxX",*_p) != NULL) {
+ va_arg(ap,int); /* short gets promoted to int */
+ goto fmt_valid;
}
+ goto fmt_invalid;
}
- /* Conversion specifier */
- if (*_p != '\0' && strchr("diouxXeEfFgGaA",*_p) != NULL) {
- _l = (_p+1)-c;
- if (_l < sizeof(_format)-2) {
- memcpy(_format,c,_l);
- _format[_l] = '\0';
- va_copy(_cpy,ap);
- newarg = sdscatvprintf(curarg,_format,_cpy);
- va_end(_cpy);
-
- /* Update current position (note: outer blocks
- * increment c twice so compensate here) */
- c = _p-1;
+ /* Size: long long */
+ if (_p[0] == 'l' && _p[1] == 'l') {
+ _p += 2;
+ if (*_p != '\0' && strchr("diouxX",*_p) != NULL) {
+ va_arg(ap,long long);
+ goto fmt_valid;
}
+ goto fmt_invalid;
+ }
+
+ /* Size: long */
+ if (_p[0] == 'l') {
+ _p += 1;
+ if (*_p != '\0' && strchr("diouxX",*_p) != NULL) {
+ va_arg(ap,long);
+ goto fmt_valid;
+ }
+ goto fmt_invalid;
+ }
+
+ fmt_invalid:
+ va_end(_cpy);
+ goto err;
+
+ fmt_valid:
+ _l = (_p+1)-c;
+ if (_l < sizeof(_format)-2) {
+ memcpy(_format,c,_l);
+ _format[_l] = '\0';
+ newarg = sdscatvprintf(curarg,_format,_cpy);
+
+ /* Update current position (note: outer blocks
+ * increment c twice so compensate here) */
+ c = _p-1;
}
- /* Consume and discard vararg */
- va_arg(ap,void);
+ va_end(_cpy);
+ break;
}
}
diff --git a/test.c b/test.c
index bac10a7..83d265e 100644
--- a/test.c
+++ b/test.c
@@ -142,29 +142,43 @@ static void test_format_commands(void) {
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
free(cmd);
- test("Format command with printf-delegation (long long): ");
- len = redisFormatCommand(&cmd,"key:%08lld",1234ll);
- test_cond(strncmp(cmd,"*1\r\n$12\r\nkey:00001234\r\n",len) == 0 &&
- len == 4+5+(12+2));
- free(cmd);
-
- test("Format command with printf-delegation (float): ");
- len = redisFormatCommand(&cmd,"v:%06.1f",12.34f);
- test_cond(strncmp(cmd,"*1\r\n$8\r\nv:0012.3\r\n",len) == 0 &&
- len == 4+4+(8+2));
- free(cmd);
-
- test("Format command with printf-delegation and extra interpolation: ");
- len = redisFormatCommand(&cmd,"key:%d %b",1234,"foo",3);
- test_cond(strncmp(cmd,"*2\r\n$8\r\nkey:1234\r\n$3\r\nfoo\r\n",len) == 0 &&
- len == 4+4+(8+2)+4+(3+2));
- free(cmd);
-
- test("Format command with wrong printf format and extra interpolation: ");
+ /* Vararg width depends on the type. These tests make sure that the
+ * width is correctly determined using the format and subsequent varargs
+ * can correctly be interpolated. */
+#define INTEGER_WIDTH_TEST(fmt, type) do { \
+ type value = 123; \
+ test("Format command with printf-delegation (" #type "): "); \
+ len = redisFormatCommand(&cmd,"key:%08" fmt " str:%s", value, "hello"); \
+ test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:00000123\r\n$9\r\nstr:hello\r\n",len) == 0 && \
+ len == 4+5+(12+2)+4+(9+2)); \
+ free(cmd); \
+} while(0)
+
+#define FLOAT_WIDTH_TEST(type) do { \
+ type value = 123.0; \
+ test("Format command with printf-delegation (" #type "): "); \
+ len = redisFormatCommand(&cmd,"key:%08.3f str:%s", value, "hello"); \
+ test_cond(strncmp(cmd,"*2\r\n$12\r\nkey:0123.000\r\n$9\r\nstr:hello\r\n",len) == 0 && \
+ len == 4+5+(12+2)+4+(9+2)); \
+ free(cmd); \
+} while(0)
+
+ INTEGER_WIDTH_TEST("d", int);
+ INTEGER_WIDTH_TEST("hhd", char);
+ INTEGER_WIDTH_TEST("hd", short);
+ INTEGER_WIDTH_TEST("ld", long);
+ INTEGER_WIDTH_TEST("lld", long long);
+ INTEGER_WIDTH_TEST("u", unsigned int);
+ INTEGER_WIDTH_TEST("hhu", unsigned char);
+ INTEGER_WIDTH_TEST("hu", unsigned short);
+ INTEGER_WIDTH_TEST("lu", unsigned long);
+ INTEGER_WIDTH_TEST("llu", unsigned long long);
+ FLOAT_WIDTH_TEST(float);
+ FLOAT_WIDTH_TEST(double);
+
+ test("Format command with invalid printf format: ");
len = redisFormatCommand(&cmd,"key:%08p %b",1234,"foo",3);
- test_cond(strncmp(cmd,"*2\r\n$6\r\nkey:8p\r\n$3\r\nfoo\r\n",len) == 0 &&
- len == 4+4+(6+2)+4+(3+2));
- free(cmd);
+ test_cond(len == -1);
const char *argv[3];
argv[0] = "SET";