Support hiredis-style format, namely everything is same with printf except that %b corresponds to binary-data + length. Notice that we can't use %.*s (printf built-in) which ends scaning at \0 and is not binary-safe. Some code is copied or modified from redisvFormatCommand() in https://github.com/redis/hiredis/blob/master/hiredis.c to keep close compatibility with hiredis.
| 82 | // https://github.com/redis/hiredis/blob/master/hiredis.c to keep close |
| 83 | // compatibility with hiredis. |
| 84 | butil::Status |
| 85 | RedisCommandFormatV(butil::IOBuf* outbuf, const char* fmt, va_list ap) { |
| 86 | if (outbuf == NULL || fmt == NULL) { |
| 87 | return butil::Status(EINVAL, "Param[outbuf] or [fmt] is NULL"); |
| 88 | } |
| 89 | const size_t fmt_len = strlen(fmt); |
| 90 | std::string nocount_buf; |
| 91 | nocount_buf.reserve(fmt_len * 3 / 2 + 16); |
| 92 | std::string compbuf; // A component |
| 93 | compbuf.reserve(fmt_len + 16); |
| 94 | const char* c = fmt; |
| 95 | int ncomponent = 0; |
| 96 | char quote_char = 0; |
| 97 | const char* quote_pos = fmt; |
| 98 | int nargs = 0; |
| 99 | for (; *c; ++c) { |
| 100 | if (*c != '%' || c[1] == '\0') { |
| 101 | if (*c == ' ') { |
| 102 | if (quote_char) { |
| 103 | compbuf.push_back(*c); |
| 104 | } else if (!compbuf.empty()) { |
| 105 | FlushComponent(&nocount_buf, &compbuf, &ncomponent); |
| 106 | } |
| 107 | } else if (*c == '"' || *c == '\'') { // Check quotation. |
| 108 | if (!quote_char) { // begin quote |
| 109 | quote_char = *c; |
| 110 | quote_pos = c; |
| 111 | if (!compbuf.empty()) { |
| 112 | FlushComponent(&nocount_buf, &compbuf, &ncomponent); |
| 113 | } |
| 114 | } else if (quote_char == *c) { |
| 115 | const char last_char = (compbuf.empty() ? 0 : compbuf.back()); |
| 116 | if (last_char == '\\') { |
| 117 | // Even if the preceding chars are two consecutive backslashes |
| 118 | // (\\), still do the escaping, which is the behavior of |
| 119 | // official redis-cli. |
| 120 | compbuf.pop_back(); |
| 121 | compbuf.push_back(*c); |
| 122 | } else { // end quote |
| 123 | quote_char = 0; |
| 124 | FlushComponent(&nocount_buf, &compbuf, &ncomponent); |
| 125 | } |
| 126 | } else { |
| 127 | compbuf.push_back(*c); |
| 128 | } |
| 129 | } else { |
| 130 | compbuf.push_back(*c); |
| 131 | } |
| 132 | } else { |
| 133 | char *arg; |
| 134 | size_t size; |
| 135 | |
| 136 | switch(c[1]) { |
| 137 | case 's': |
| 138 | arg = va_arg(ap, char*); |
| 139 | size = strlen(arg); |
| 140 | if (size > 0) { |
| 141 | compbuf.append(arg, size); |
no test coverage detected