| 287 | } |
| 288 | |
| 289 | butil::Status |
| 290 | RedisCommandNoFormat(butil::IOBuf* outbuf, const butil::StringPiece& cmd) { |
| 291 | if (outbuf == NULL || cmd == NULL) { |
| 292 | return butil::Status(EINVAL, "Param[outbuf] or [cmd] is NULL"); |
| 293 | } |
| 294 | const size_t cmd_len = cmd.size(); |
| 295 | std::string nocount_buf; |
| 296 | nocount_buf.reserve(cmd_len * 3 / 2 + 16); |
| 297 | std::string compbuf; // A component |
| 298 | compbuf.reserve(cmd_len + 16); |
| 299 | int ncomponent = 0; |
| 300 | char quote_char = 0; |
| 301 | const char* quote_pos = cmd.data(); |
| 302 | for (const char* c = cmd.data(); c != cmd.data() + cmd.size(); ++c) { |
| 303 | if (*c == ' ') { |
| 304 | if (quote_char) { |
| 305 | compbuf.push_back(*c); |
| 306 | } else if (!compbuf.empty()) { |
| 307 | FlushComponent(&nocount_buf, &compbuf, &ncomponent); |
| 308 | } |
| 309 | } else if (*c == '"' || *c == '\'') { // Check quotation. |
| 310 | if (!quote_char) { // begin quote |
| 311 | quote_char = *c; |
| 312 | quote_pos = c; |
| 313 | if (!compbuf.empty()) { |
| 314 | FlushComponent(&nocount_buf, &compbuf, &ncomponent); |
| 315 | } |
| 316 | } else if (quote_char == *c) { |
| 317 | const char last_char = (compbuf.empty() ? 0 : compbuf.back()); |
| 318 | if (last_char == '\\') { |
| 319 | // Even if the preceding chars are two consecutive backslashes |
| 320 | // (\\), still do the escaping, which is the behavior of |
| 321 | // official redis-cli. |
| 322 | compbuf.pop_back(); |
| 323 | compbuf.push_back(*c); |
| 324 | } else { // end quote |
| 325 | quote_char = 0; |
| 326 | FlushComponent(&nocount_buf, &compbuf, &ncomponent); |
| 327 | } |
| 328 | } else { |
| 329 | compbuf.push_back(*c); |
| 330 | } |
| 331 | } else { |
| 332 | compbuf.push_back(*c); |
| 333 | } |
| 334 | } |
| 335 | if (quote_char) { |
| 336 | const char* ctx_begin = |
| 337 | quote_pos - std::min((size_t)(quote_pos - cmd.data()), CTX_WIDTH); |
| 338 | size_t ctx_size = |
| 339 | std::min((size_t)(cmd.data() + cmd.size() - ctx_begin), CTX_WIDTH * 2 + 1); |
| 340 | return butil::Status(EINVAL, "Unmatched quote: ...%.*s... (offset=%lu)", |
| 341 | (int)ctx_size, ctx_begin, quote_pos - cmd.data()); |
| 342 | } |
| 343 | |
| 344 | if (!compbuf.empty()) { |
| 345 | FlushComponent(&nocount_buf, &compbuf, &ncomponent); |
| 346 | } |