From 7684d556bd7250786eb6fd38c656368530903677 Mon Sep 17 00:00:00 2001 From: Pieter Noordhuis Date: Thu, 2 Dec 2010 16:18:30 +0100 Subject: [PATCH] Add (nearly) full printf support by delegating to vsprintf --- hiredis.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ test.c | 24 ++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/hiredis.c b/hiredis.c index 7ae962b..c3b2815 100644 --- a/hiredis.c +++ b/hiredis.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "hiredis.h" #include "net.h" @@ -639,6 +640,59 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { case '%': current = sdscat(current,"%"); break; + default: + /* Try to detect printf format */ + { + char _format[16]; + const char *_p = c+1; + size_t _l = 0; + va_list _cpy; + + /* Flags */ + if (*_p != '\0' && *_p == '#') _p++; + if (*_p != '\0' && *_p == '0') _p++; + if (*_p != '\0' && *_p == '-') _p++; + if (*_p != '\0' && *_p == ' ') _p++; + if (*_p != '\0' && *_p == '+') _p++; + + /* Field width */ + while (*_p != '\0' && isdigit(*_p)) _p++; + + /* Precision */ + if (*_p == '.') { + _p++; + 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++; + } + } + + /* 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); + current = sdscatvprintf(current,_format,_cpy); + interpolated = 1; + va_end(_cpy); + + /* Update current position (note: outer blocks + * increment c twice so compensate here) */ + c = _p-1; + } + } + + /* Consume and discard vararg */ + va_arg(ap,void); + } } c++; } diff --git a/test.c b/test.c index a6527f6..ed355a7 100644 --- a/test.c +++ b/test.c @@ -71,6 +71,30 @@ static void test_format_commands() { 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: "); + 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); + const char *argv[3]; argv[0] = "SET"; argv[1] = "foo\0xxx";