diff options
| -rw-r--r-- | hiredis.c | 54 | ||||
| -rw-r--r-- | test.c | 24 | 
2 files changed, 78 insertions, 0 deletions
@@ -32,6 +32,7 @@  #include <unistd.h>  #include <assert.h>  #include <errno.h> +#include <ctype.h>  #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++;          } @@ -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";  | 
