Add (nearly) full printf support by delegating to vsprintf
This commit is contained in:
parent
ff50dff70d
commit
7684d556bd
54
hiredis.c
54
hiredis.c
@ -32,6 +32,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "hiredis.h"
|
#include "hiredis.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
@ -639,6 +640,59 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) {
|
|||||||
case '%':
|
case '%':
|
||||||
current = sdscat(current,"%");
|
current = sdscat(current,"%");
|
||||||
break;
|
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++;
|
c++;
|
||||||
}
|
}
|
||||||
|
24
test.c
24
test.c
@ -71,6 +71,30 @@ static void test_format_commands() {
|
|||||||
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
|
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
|
||||||
free(cmd);
|
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];
|
const char *argv[3];
|
||||||
argv[0] = "SET";
|
argv[0] = "SET";
|
||||||
argv[1] = "foo\0xxx";
|
argv[1] = "foo\0xxx";
|
||||||
|
Loading…
Reference in New Issue
Block a user