]> git.saurik.com Git - redis.git/blame - src/redis-cli.c
Move code for pushing on a (blocking) RPOPLPUSH
[redis.git] / src / redis-cli.c
CommitLineData
ed9b544e 1/* Redis CLI (command line interface)
2 *
12d090d2 3 * Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
ed9b544e 4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
23d4709d 31#include "fmacros.h"
185cabda 32#include "version.h"
23d4709d 33
ed9b544e 34#include <stdio.h>
35#include <string.h>
36#include <stdlib.h>
37#include <unistd.h>
a88a2af6 38#include <ctype.h>
c0b3d423 39#include <errno.h>
b4b62c34 40#include <sys/stat.h>
3ce014c7 41#include <sys/time.h>
41945ba6 42#include <assert.h>
ed9b544e 43
7fc4ce13 44#include "hiredis.h"
ed9b544e 45#include "sds.h"
ed9b544e 46#include "zmalloc.h"
cf87ebf2 47#include "linenoise.h"
5397f2b5 48#include "help.h"
ed9b544e 49
ed9b544e 50#define REDIS_NOTUSED(V) ((void) V)
51
7fc4ce13 52static redisContext *context;
ed9b544e 53static struct config {
54 char *hostip;
55 int hostport;
7e91f971 56 char *hostsocket;
5762b7f0 57 long repeat;
62e920df 58 int dbnum;
5d15b520 59 int interactive;
36e5db6d 60 int shutdown;
249c3a7d 61 int monitor_mode;
62 int pubsub_mode;
f2dd4769 63 int raw_output; /* output mode per command */
123a10f7 64 int tty; /* flag for default output format */
bc63407b 65 int stdinarg; /* get last arg from stdin. (-x option) */
3a51bff0 66 char mb_sep;
288799e0 67 char *auth;
99628c1a 68 char *historyfile;
ed9b544e 69} config;
70
a9158272 71static void usage();
11fd0c42 72char *redisGitSHA1(void);
c937aa89 73
3ce014c7 74/*------------------------------------------------------------------------------
75 * Utility functions
76 *--------------------------------------------------------------------------- */
77
78static long long mstime(void) {
79 struct timeval tv;
80 long long mst;
81
82 gettimeofday(&tv, NULL);
83 mst = ((long)tv.tv_sec)*1000;
84 mst += tv.tv_usec/1000;
85 return mst;
86}
87
a2a69d58
PN
88/*------------------------------------------------------------------------------
89 * Help functions
90 *--------------------------------------------------------------------------- */
91
b2cc45bf
PN
92#define CLI_HELP_COMMAND 1
93#define CLI_HELP_GROUP 2
94
95typedef struct {
96 int type;
97 int argc;
98 sds *argv;
99 sds full;
100
101 /* Only used for help on commands */
102 struct commandHelp *org;
103} helpEntry;
104
105static helpEntry *helpEntries;
106static int helpEntriesLen;
107
108static void cliInitHelp() {
109 int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
110 int groupslen = sizeof(commandGroups)/sizeof(char*);
111 int i, len, pos = 0;
112 helpEntry tmp;
113
114 helpEntriesLen = len = commandslen+groupslen;
115 helpEntries = malloc(sizeof(helpEntry)*len);
116
117 for (i = 0; i < groupslen; i++) {
118 tmp.argc = 1;
119 tmp.argv = malloc(sizeof(sds));
120 tmp.argv[0] = sdscatprintf(sdsempty(),"@%s",commandGroups[i]);
121 tmp.full = tmp.argv[0];
122 tmp.type = CLI_HELP_GROUP;
123 tmp.org = NULL;
124 helpEntries[pos++] = tmp;
125 }
126
127 for (i = 0; i < commandslen; i++) {
128 tmp.argv = sdssplitargs(commandHelp[i].name,&tmp.argc);
129 tmp.full = sdsnew(commandHelp[i].name);
130 tmp.type = CLI_HELP_COMMAND;
131 tmp.org = &commandHelp[i];
132 helpEntries[pos++] = tmp;
133 }
134}
135
a2a69d58 136/* Output command help to stdout. */
41945ba6
PN
137static void cliOutputCommandHelp(struct commandHelp *help, int group) {
138 printf("\r\n \x1b[1m%s\x1b[0m \x1b[90m%s\x1b[0m\r\n", help->name, help->params);
139 printf(" \x1b[33msummary:\x1b[0m %s\r\n", help->summary);
140 printf(" \x1b[33msince:\x1b[0m %s\r\n", help->since);
141 if (group) {
142 printf(" \x1b[33mgroup:\x1b[0m %s\r\n", commandGroups[help->group]);
143 }
a2a69d58
PN
144}
145
41945ba6
PN
146/* Print generic help. */
147static void cliOutputGenericHelp() {
148 printf(
149 "redis-cli %s\r\n"
150 "Type: \"help @<group>\" to get a list of commands in <group>\r\n"
151 " \"help <command>\" for help on <command>\r\n"
152 " \"help <tab>\" to get a list of possible help topics\r\n"
153 " \"quit\" to exit\r\n",
154 REDIS_VERSION
155 );
a2a69d58
PN
156}
157
158/* Output all command help, filtering by group or command name. */
41945ba6 159static void cliOutputHelp(int argc, char **argv) {
b2cc45bf 160 int i, j, len;
41945ba6 161 int group = -1;
b2cc45bf
PN
162 helpEntry *entry;
163 struct commandHelp *help;
a2a69d58 164
41945ba6
PN
165 if (argc == 0) {
166 cliOutputGenericHelp();
a2a69d58 167 return;
41945ba6
PN
168 } else if (argc > 0 && argv[0][0] == '@') {
169 len = sizeof(commandGroups)/sizeof(char*);
170 for (i = 0; i < len; i++) {
171 if (strcasecmp(argv[0]+1,commandGroups[i]) == 0) {
172 group = i;
173 break;
174 }
175 }
a2a69d58
PN
176 }
177
41945ba6 178 assert(argc > 0);
b2cc45bf
PN
179 for (i = 0; i < helpEntriesLen; i++) {
180 entry = &helpEntries[i];
181 if (entry->type != CLI_HELP_COMMAND) continue;
182
183 help = entry->org;
a2a69d58 184 if (group == -1) {
b2cc45bf
PN
185 /* Compare all arguments */
186 if (argc == entry->argc) {
187 for (j = 0; j < argc; j++) {
188 if (strcasecmp(argv[j],entry->argv[j]) != 0) break;
189 }
190 if (j == argc) {
191 cliOutputCommandHelp(help,1);
192 }
a2a69d58
PN
193 }
194 } else {
195 if (group == help->group) {
41945ba6 196 cliOutputCommandHelp(help,0);
a2a69d58
PN
197 }
198 }
199 }
41945ba6
PN
200 printf("\r\n");
201}
202
41945ba6
PN
203static void completionCallback(const char *buf, linenoiseCompletions *lc) {
204 size_t startpos = 0;
205 int mask;
206 int i;
207 size_t matchlen;
b2cc45bf 208 sds tmp;
41945ba6
PN
209
210 if (strncasecmp(buf,"help ",5) == 0) {
211 startpos = 5;
212 while (isspace(buf[startpos])) startpos++;
b2cc45bf 213 mask = CLI_HELP_COMMAND | CLI_HELP_GROUP;
41945ba6 214 } else {
b2cc45bf 215 mask = CLI_HELP_COMMAND;
41945ba6
PN
216 }
217
b2cc45bf
PN
218 for (i = 0; i < helpEntriesLen; i++) {
219 if (!(helpEntries[i].type & mask)) continue;
41945ba6
PN
220
221 matchlen = strlen(buf+startpos);
b2cc45bf
PN
222 if (strncasecmp(buf+startpos,helpEntries[i].full,matchlen) == 0) {
223 tmp = sdsnewlen(buf,startpos);
224 tmp = sdscat(tmp,helpEntries[i].full);
41945ba6 225 linenoiseAddCompletion(lc,tmp);
b2cc45bf 226 sdsfree(tmp);
41945ba6
PN
227 }
228 }
a2a69d58
PN
229}
230
3ce014c7 231/*------------------------------------------------------------------------------
232 * Networking / parsing
233 *--------------------------------------------------------------------------- */
234
7fc4ce13
PN
235/* Send AUTH command to the server */
236static int cliAuth() {
237 redisReply *reply;
238 if (config.auth == NULL) return REDIS_OK;
239
240 reply = redisCommand(context,"AUTH %s",config.auth);
241 if (reply != NULL) {
242 freeReplyObject(reply);
243 return REDIS_OK;
244 }
245 return REDIS_ERR;
246}
247
248/* Send SELECT dbnum to the server */
249static int cliSelect() {
250 redisReply *reply;
251 char dbnum[16];
252 if (config.dbnum == 0) return REDIS_OK;
253
254 snprintf(dbnum,sizeof(dbnum),"%d",config.dbnum);
255 reply = redisCommand(context,"SELECT %s",dbnum);
256 if (reply != NULL) {
257 freeReplyObject(reply);
258 return REDIS_OK;
259 }
260 return REDIS_ERR;
261}
262
c0b3d423 263/* Connect to the client. If force is not zero the connection is performed
264 * even if there is already a connected socket. */
265static int cliConnect(int force) {
7fc4ce13
PN
266 if (context == NULL || force) {
267 if (context != NULL)
268 redisFree(context);
ed9b544e 269
7e91f971 270 if (config.hostsocket == NULL) {
7fc4ce13 271 context = redisConnect(config.hostip,config.hostport);
7e91f971 272 } else {
7fc4ce13 273 context = redisConnectUnix(config.hostsocket);
7e91f971 274 }
7fc4ce13
PN
275
276 if (context->err) {
7e91f971
PN
277 fprintf(stderr,"Could not connect to Redis at ");
278 if (config.hostsocket == NULL)
7fc4ce13 279 fprintf(stderr,"%s:%d: %s\n",config.hostip,config.hostport,context->errstr);
7e91f971 280 else
7fc4ce13
PN
281 fprintf(stderr,"%s: %s\n",config.hostsocket,context->errstr);
282 redisFree(context);
283 context = NULL;
284 return REDIS_ERR;
6fa24622 285 }
ed9b544e 286
7fc4ce13
PN
287 /* Do AUTH and select the right DB. */
288 if (cliAuth() != REDIS_OK)
289 return REDIS_ERR;
290 if (cliSelect() != REDIS_OK)
291 return REDIS_ERR;
ed9b544e 292 }
7fc4ce13 293 return REDIS_OK;
ed9b544e 294}
295
7fc4ce13
PN
296static void cliPrintContextErrorAndExit() {
297 if (context == NULL) return;
298 fprintf(stderr,"Error: %s\n",context->errstr);
299 exit(1);
ed9b544e 300}
301
7fc4ce13
PN
302static sds cliFormatReply(redisReply *r, char *prefix) {
303 sds out = sdsempty();
304 switch (r->type) {
305 case REDIS_REPLY_ERROR:
7fc4ce13
PN
306 if (config.tty) out = sdscat(out,"(error) ");
307 out = sdscatprintf(out,"%s\n", r->str);
308 break;
309 case REDIS_REPLY_STATUS:
7fc4ce13
PN
310 out = sdscat(out,r->str);
311 out = sdscat(out,"\n");
312 break;
313 case REDIS_REPLY_INTEGER:
7fc4ce13
PN
314 if (config.tty) out = sdscat(out,"(integer) ");
315 out = sdscatprintf(out,"%lld\n",r->integer);
316 break;
317 case REDIS_REPLY_STRING:
7fc4ce13
PN
318 if (config.raw_output || !config.tty) {
319 out = sdscatlen(out,r->str,r->len);
320 } else {
321 /* If you are producing output for the standard output we want
322 * a more interesting output with quoted characters and so forth */
323 out = sdscatrepr(out,r->str,r->len);
324 out = sdscat(out,"\n");
21cdc9f0 325 }
7fc4ce13
PN
326 break;
327 case REDIS_REPLY_NIL:
7fc4ce13
PN
328 out = sdscat(out,"(nil)\n");
329 break;
330 case REDIS_REPLY_ARRAY:
331 if (r->elements == 0) {
7fc4ce13 332 out = sdscat(out,"(empty list or set)\n");
c0b3d423 333 } else {
cfcd5d6d
PN
334 unsigned int i, idxlen = 0;
335 char _prefixlen[16];
336 char _prefixfmt[16];
337 sds _prefix;
7fc4ce13
PN
338 sds tmp;
339
cfcd5d6d
PN
340 /* Calculate chars needed to represent the largest index */
341 i = r->elements;
342 do {
343 idxlen++;
344 i /= 10;
345 } while(i);
346
347 /* Prefix for nested multi bulks should grow with idxlen+2 spaces */
348 memset(_prefixlen,' ',idxlen+2);
349 _prefixlen[idxlen+2] = '\0';
350 _prefix = sdscat(sdsnew(prefix),_prefixlen);
351
352 /* Setup prefix format for every entry */
353 snprintf(_prefixfmt,sizeof(_prefixfmt),"%%s%%%dd) ",idxlen);
354
7fc4ce13 355 for (i = 0; i < r->elements; i++) {
cfcd5d6d
PN
356 /* Don't use the prefix for the first element, as the parent
357 * caller already prepended the index number. */
358 out = sdscatprintf(out,_prefixfmt,i == 0 ? "" : prefix,i+1);
359
360 /* Format the multi bulk entry */
361 tmp = cliFormatReply(r->element[i],_prefix);
7fc4ce13
PN
362 out = sdscatlen(out,tmp,sdslen(tmp));
363 sdsfree(tmp);
364 }
cfcd5d6d 365 sdsfree(_prefix);
c0b3d423 366 }
7fc4ce13 367 break;
c937aa89 368 default:
7fc4ce13
PN
369 fprintf(stderr,"Unknown reply type: %d\n", r->type);
370 exit(1);
c937aa89 371 }
7fc4ce13 372 return out;
c937aa89 373}
374
7fc4ce13
PN
375static int cliReadReply() {
376 redisReply *reply;
377 sds out;
378
379 if (redisGetReply(context,(void**)&reply) != REDIS_OK) {
380 if (config.shutdown)
381 return REDIS_OK;
382 if (config.interactive) {
383 /* Filter cases where we should reconnect */
384 if (context->err == REDIS_ERR_IO && errno == ECONNRESET)
385 return REDIS_ERR;
386 if (context->err == REDIS_ERR_EOF)
387 return REDIS_ERR;
388 }
389 cliPrintContextErrorAndExit();
390 return REDIS_ERR; /* avoid compiler warning */
62e920df 391 }
7fc4ce13
PN
392
393 out = cliFormatReply(reply,"");
394 freeReplyObject(reply);
395 fwrite(out,sdslen(out),1,stdout);
396 sdsfree(out);
397 return REDIS_OK;
62e920df 398}
399
aab055ae 400static int cliSendCommand(int argc, char **argv, int repeat) {
37dc9e5a 401 char *command = argv[0];
7fc4ce13
PN
402 size_t *argvlen;
403 int j;
ed9b544e 404
efcf948c 405 if (context == NULL) {
406 printf("Not connected, please use: connect <host> <port>\n");
407 return REDIS_OK;
408 }
409
37dc9e5a 410 config.raw_output = !strcasecmp(command,"info");
41945ba6
PN
411 if (!strcasecmp(command,"help") || !strcasecmp(command,"?")) {
412 cliOutputHelp(--argc, ++argv);
7fc4ce13 413 return REDIS_OK;
8079656a 414 }
37dc9e5a
PN
415 if (!strcasecmp(command,"shutdown")) config.shutdown = 1;
416 if (!strcasecmp(command,"monitor")) config.monitor_mode = 1;
417 if (!strcasecmp(command,"subscribe") ||
418 !strcasecmp(command,"psubscribe")) config.pubsub_mode = 1;
ed9b544e 419
7fc4ce13
PN
420 /* Setup argument length */
421 argvlen = malloc(argc*sizeof(size_t));
422 for (j = 0; j < argc; j++)
423 argvlen[j] = sdslen(argv[j]);
a2f4f871 424
aab055ae 425 while(repeat--) {
7fc4ce13 426 redisAppendCommandArgv(context,argc,(const char**)argv,argvlen);
249c3a7d 427 while (config.monitor_mode) {
7fc4ce13 428 if (cliReadReply() != REDIS_OK) exit(1);
d9d8ccab 429 fflush(stdout);
621d5c19 430 }
431
249c3a7d 432 if (config.pubsub_mode) {
7fc4ce13 433 printf("Reading messages... (press Ctrl-C to quit)\n");
249c3a7d 434 while (1) {
7fc4ce13 435 if (cliReadReply() != REDIS_OK) exit(1);
249c3a7d 436 }
437 }
438
7fc4ce13
PN
439 if (cliReadReply() != REDIS_OK)
440 return REDIS_ERR;
ed9b544e 441 }
7fc4ce13 442 return REDIS_OK;
ed9b544e 443}
444
3ce014c7 445/*------------------------------------------------------------------------------
446 * User interface
447 *--------------------------------------------------------------------------- */
448
ed9b544e 449static int parseOptions(int argc, char **argv) {
450 int i;
451
452 for (i = 1; i < argc; i++) {
453 int lastarg = i==argc-1;
6cf5882c 454
ed9b544e 455 if (!strcmp(argv[i],"-h") && !lastarg) {
efcf948c 456 sdsfree(config.hostip);
457 config.hostip = sdsnew(argv[i+1]);
ed9b544e 458 i++;
a9158272 459 } else if (!strcmp(argv[i],"-h") && lastarg) {
460 usage();
bc63407b 461 } else if (!strcmp(argv[i],"-x")) {
462 config.stdinarg = 1;
ed9b544e 463 } else if (!strcmp(argv[i],"-p") && !lastarg) {
464 config.hostport = atoi(argv[i+1]);
465 i++;
7e91f971
PN
466 } else if (!strcmp(argv[i],"-s") && !lastarg) {
467 config.hostsocket = argv[i+1];
468 i++;
5762b7f0 469 } else if (!strcmp(argv[i],"-r") && !lastarg) {
470 config.repeat = strtoll(argv[i+1],NULL,10);
471 i++;
62e920df 472 } else if (!strcmp(argv[i],"-n") && !lastarg) {
473 config.dbnum = atoi(argv[i+1]);
474 i++;
fdfdae0f 475 } else if (!strcmp(argv[i],"-a") && !lastarg) {
288799e0 476 config.auth = argv[i+1];
fdfdae0f 477 i++;
6cf5882c 478 } else if (!strcmp(argv[i],"-i")) {
abb731e5
PN
479 fprintf(stderr,
480"Starting interactive mode using -i is deprecated. Interactive mode is started\n"
481"by default when redis-cli is executed without a command to execute.\n"
482 );
37dc9e5a 483 } else if (!strcmp(argv[i],"-c")) {
b4b62c34
PN
484 fprintf(stderr,
485"Reading last argument from standard input using -c is deprecated.\n"
486"When standard input is connected to a pipe or regular file, it is\n"
487"automatically used as last argument.\n"
488 );
185cabda 489 } else if (!strcmp(argv[i],"-v")) {
11fd0c42 490 printf("redis-cli shipped with Redis version %s (%s)\n", REDIS_VERSION, redisGitSHA1());
185cabda 491 exit(0);
ed9b544e 492 } else {
493 break;
494 }
495 }
496 return i;
497}
498
499static sds readArgFromStdin(void) {
500 char buf[1024];
501 sds arg = sdsempty();
502
503 while(1) {
504 int nread = read(fileno(stdin),buf,1024);
505
506 if (nread == 0) break;
507 else if (nread == -1) {
508 perror("Reading from standard input");
509 exit(1);
510 }
511 arg = sdscatlen(arg,buf,nread);
512 }
513 return arg;
514}
515
a9158272 516static void usage() {
7e91f971 517 fprintf(stderr, "usage: redis-cli [-iv] [-h host] [-p port] [-s /path/to/socket] [-a authpw] [-r repeat_times] [-n db_num] cmd arg1 arg2 arg3 ... argN\n");
bc63407b 518 fprintf(stderr, "usage: echo \"argN\" | redis-cli -x [options] cmd arg1 arg2 ... arg(N-1)\n\n");
519 fprintf(stderr, "example: cat /etc/passwd | redis-cli -x set my_passwd\n");
a9158272 520 fprintf(stderr, "example: redis-cli get my_passwd\n");
521 fprintf(stderr, "example: redis-cli -r 100 lpush mylist x\n");
d239ec59 522 fprintf(stderr, "\nRun in interactive mode: redis-cli -i or just don't pass any command\n");
a9158272 523 exit(1);
524}
525
6cf5882c
MMDJ
526/* Turn the plain C strings into Sds strings */
527static char **convertToSds(int count, char** args) {
528 int j;
37dc9e5a 529 char **sds = zmalloc(sizeof(char*)*count);
6cf5882c
MMDJ
530
531 for(j = 0; j < count; j++)
532 sds[j] = sdsnew(args[j]);
533
534 return sds;
535}
536
a88a2af6 537#define LINE_BUFLEN 4096
6cf5882c 538static void repl() {
a88a2af6 539 int argc, j;
cbce5171 540 char *line;
541 sds *argv;
6cf5882c 542
5d15b520 543 config.interactive = 1;
41945ba6 544 linenoiseSetCompletionCallback(completionCallback);
ce260f73 545
d8d528e9 546 while((line = linenoise(context ? "redis> " : "not connected> ")) != NULL) {
cf87ebf2 547 if (line[0] != '\0') {
cbce5171 548 argv = sdssplitargs(line,&argc);
a88a2af6 549 linenoiseHistoryAdd(line);
99628c1a 550 if (config.historyfile) linenoiseHistorySave(config.historyfile);
0439d792
PN
551 if (argv == NULL) {
552 printf("Invalid argument(s)\n");
553 continue;
554 } else if (argc > 0) {
a88a2af6 555 if (strcasecmp(argv[0],"quit") == 0 ||
556 strcasecmp(argv[0],"exit") == 0)
c0b3d423 557 {
558 exit(0);
efcf948c 559 } else if (argc == 3 && !strcasecmp(argv[0],"connect")) {
560 sdsfree(config.hostip);
561 config.hostip = sdsnew(argv[1]);
562 config.hostport = atoi(argv[2]);
563 cliConnect(1);
bbac56c2 564 } else if (argc == 1 && !strcasecmp(argv[0],"clear")) {
565 linenoiseClearScreen();
c0b3d423 566 } else {
3ce014c7 567 long long start_time = mstime(), elapsed;
c0b3d423 568
7fc4ce13 569 if (cliSendCommand(argc,argv,1) != REDIS_OK) {
efcf948c 570 cliConnect(1);
7fc4ce13
PN
571
572 /* If we still cannot send the command,
573 * print error and abort. */
574 if (cliSendCommand(argc,argv,1) != REDIS_OK)
575 cliPrintContextErrorAndExit();
c0b3d423 576 }
3ce014c7 577 elapsed = mstime()-start_time;
339b9dc2
PN
578 if (elapsed >= 500) {
579 printf("(%.2fs)\n",(double)elapsed/1000);
580 }
c0b3d423 581 }
a88a2af6 582 }
583 /* Free the argument vector */
584 for (j = 0; j < argc; j++)
585 sdsfree(argv[j]);
8ff6a48b 586 zfree(argv);
6cf5882c 587 }
a88a2af6 588 /* linenoise() returns malloc-ed lines like readline() */
cf87ebf2 589 free(line);
6cf5882c 590 }
6cf5882c
MMDJ
591 exit(0);
592}
593
b4b62c34
PN
594static int noninteractive(int argc, char **argv) {
595 int retval = 0;
bc63407b 596 if (config.stdinarg) {
b4b62c34
PN
597 argv = zrealloc(argv, (argc+1)*sizeof(char*));
598 argv[argc] = readArgFromStdin();
599 retval = cliSendCommand(argc+1, argv, config.repeat);
600 } else {
601 /* stdin is probably a tty, can be tested with S_ISCHR(s.st_mode) */
602 retval = cliSendCommand(argc, argv, config.repeat);
603 }
604 return retval;
605}
606
ed9b544e 607int main(int argc, char **argv) {
6cf5882c 608 int firstarg;
ed9b544e 609
efcf948c 610 config.hostip = sdsnew("127.0.0.1");
ed9b544e 611 config.hostport = 6379;
7e91f971 612 config.hostsocket = NULL;
5762b7f0 613 config.repeat = 1;
62e920df 614 config.dbnum = 0;
5d15b520 615 config.interactive = 0;
36e5db6d 616 config.shutdown = 0;
249c3a7d 617 config.monitor_mode = 0;
618 config.pubsub_mode = 0;
f40b035d 619 config.raw_output = 0;
bc63407b 620 config.stdinarg = 0;
288799e0 621 config.auth = NULL;
99628c1a 622 config.historyfile = NULL;
cf0c6b78 623 config.tty = isatty(fileno(stdout)) || (getenv("FAKETTY") != NULL);
3a51bff0 624 config.mb_sep = '\n';
41945ba6 625 cliInitHelp();
99628c1a 626
627 if (getenv("HOME") != NULL) {
628 config.historyfile = malloc(256);
629 snprintf(config.historyfile,256,"%s/.rediscli_history",getenv("HOME"));
630 linenoiseHistoryLoad(config.historyfile);
631 }
ed9b544e 632
633 firstarg = parseOptions(argc,argv);
634 argc -= firstarg;
635 argv += firstarg;
ed9b544e 636
7fc4ce13
PN
637 /* Try to connect */
638 if (cliConnect(0) != REDIS_OK) exit(1);
aab055ae 639
abb731e5
PN
640 /* Start interactive mode when no command is provided */
641 if (argc == 0) repl();
b4b62c34
PN
642 /* Otherwise, we have some arguments to execute */
643 return noninteractive(argc,convertToSds(argc,argv));
ed9b544e 644}