increase argv in batch mode to silence a false gcc 4.4 warning
[project/uci.git] / cli.c
1 /*
2  * cli - Command Line Interface for the Unified Configuration Interface
3  * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14 #include <strings.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include "uci.h"
19
20 #define MAX_ARGS        4 /* max command line arguments for batch mode */
21
22 static const char *delimiter = " ";
23 static const char *appname;
24 static enum {
25         CLI_FLAG_MERGE =    (1 << 0),
26         CLI_FLAG_QUIET =    (1 << 1),
27         CLI_FLAG_NOCOMMIT = (1 << 2),
28         CLI_FLAG_BATCH =    (1 << 3),
29         CLI_FLAG_SHOW_EXT = (1 << 4),
30 } flags;
31
32 static FILE *input;
33
34 static struct uci_context *ctx;
35 enum {
36         /* section cmds */
37         CMD_GET,
38         CMD_SET,
39         CMD_ADD_LIST,
40         CMD_DEL,
41         CMD_RENAME,
42         CMD_REVERT,
43         /* package cmds */
44         CMD_SHOW,
45         CMD_CHANGES,
46         CMD_EXPORT,
47         CMD_COMMIT,
48         /* other cmds */
49         CMD_ADD,
50         CMD_IMPORT,
51         CMD_HELP,
52 };
53
54 struct uci_type_list {
55         unsigned int idx;
56         const char *name;
57         struct uci_type_list *next;
58 };
59
60 static struct uci_type_list *type_list = NULL;
61 static char *typestr = NULL;
62 static const char *cur_section_ref = NULL;
63
64 static int uci_cmd(int argc, char **argv);
65
66 static void
67 uci_reset_typelist(void)
68 {
69         struct uci_type_list *type;
70         while (type_list != NULL) {
71                         type = type_list;
72                         type_list = type_list->next;
73                         free(type);
74         }
75         if (typestr) {
76                 free(typestr);
77                 typestr = NULL;
78         }
79         cur_section_ref = NULL;
80 }
81
82 static char *
83 uci_lookup_section_ref(struct uci_section *s)
84 {
85         struct uci_type_list *ti = type_list;
86         int maxlen;
87
88         if (!s->anonymous || !(flags & CLI_FLAG_SHOW_EXT))
89                 return s->e.name;
90
91         /* look up in section type list */
92         while (ti) {
93                 if (strcmp(ti->name, s->type) == 0)
94                         break;
95                 ti = ti->next;
96         }
97         if (!ti) {
98                 ti = malloc(sizeof(struct uci_type_list));
99                 memset(ti, 0, sizeof(struct uci_type_list));
100                 ti->next = type_list;
101                 type_list = ti;
102                 ti->name = s->type;
103         }
104
105         maxlen = strlen(s->type) + 1 + 2 + 10;
106         if (!typestr) {
107                 typestr = malloc(maxlen);
108         } else {
109                 typestr = realloc(typestr, maxlen);
110         }
111         sprintf(typestr, "@%s[%d]", ti->name, ti->idx);
112         ti->idx++;
113         return typestr;
114 }
115
116 static void uci_usage(void)
117 {
118         fprintf(stderr,
119                 "Usage: %s [<options>] <command> [<arguments>]\n\n"
120                 "Commands:\n"
121                 "\tbatch\n"
122                 "\texport     [<config>]\n"
123                 "\timport     [<config>]\n"
124                 "\tchanges    [<config>]\n"
125                 "\tcommit     [<config>]\n"
126                 "\tadd        <config> <section-type>\n"
127                 "\tadd_list   <config>.<section>.<option>=<string>\n"
128                 "\tshow       [<config>[.<section>[.<option>]]]\n"
129                 "\tget        <config>.<section>[.<option>]\n"
130                 "\tset        <config>.<section>[.<option>]=<value>\n"
131                 "\tdelete     <config>[.<section[.<option>]]\n"
132                 "\trename     <config>.<section>[.<option>]=<name>\n"
133                 "\trevert     <config>[.<section>[.<option>]]\n"
134                 "\n"
135                 "Options:\n"
136                 "\t-c <path>  set the search path for config files (default: /etc/config)\n"
137                 "\t-d <str>   set the delimiter for list values in uci show\n"
138                 "\t-f <file>  use <file> as input instead of stdin\n"
139                 "\t-m         when importing, merge data into an existing package\n"
140                 "\t-n         name unnamed sections on export (default)\n"
141                 "\t-N         don't name unnamed sections\n"
142                 "\t-p <path>  add a search path for config change files\n"
143                 "\t-P <path>  add a search path for config change files and use as default\n"
144                 "\t-q         quiet mode (don't print error messages)\n"
145                 "\t-s         force strict mode (stop on parser errors, default)\n"
146                 "\t-S         disable strict mode\n"
147                 "\t-X         do not use extended syntax on 'show'\n"
148                 "\n",
149                 appname
150         );
151 }
152
153 static void cli_perror(void)
154 {
155         if (flags & CLI_FLAG_QUIET)
156                 return;
157
158         uci_perror(ctx, appname);
159 }
160
161 static void uci_show_value(struct uci_option *o)
162 {
163         struct uci_element *e;
164         bool sep = false;
165
166         switch(o->type) {
167         case UCI_TYPE_STRING:
168                 printf("%s\n", o->v.string);
169                 break;
170         case UCI_TYPE_LIST:
171                 uci_foreach_element(&o->v.list, e) {
172                         printf("%s%s", (sep ? delimiter : ""), e->name);
173                         sep = true;
174                 }
175                 printf("\n");
176                 break;
177         default:
178                 printf("<unknown>\n");
179                 break;
180         }
181 }
182
183 static void uci_show_option(struct uci_option *o)
184 {
185         printf("%s.%s.%s=",
186                 o->section->package->e.name,
187                 (cur_section_ref ? cur_section_ref : o->section->e.name),
188                 o->e.name);
189         uci_show_value(o);
190 }
191
192 static void uci_show_section(struct uci_section *s)
193 {
194         struct uci_element *e;
195         const char *cname;
196         const char *sname;
197
198         cname = s->package->e.name;
199         sname = (cur_section_ref ? cur_section_ref : s->e.name);
200         printf("%s.%s=%s\n", cname, sname, s->type);
201         uci_foreach_element(&s->options, e) {
202                 uci_show_option(uci_to_option(e));
203         }
204 }
205
206 static void uci_show_package(struct uci_package *p)
207 {
208         struct uci_element *e;
209
210         uci_reset_typelist();
211         uci_foreach_element( &p->sections, e) {
212                 struct uci_section *s = uci_to_section(e);
213                 cur_section_ref = uci_lookup_section_ref(s);
214                 uci_show_section(s);
215         }
216         uci_reset_typelist();
217 }
218
219 static void uci_show_changes(struct uci_package *p)
220 {
221         struct uci_element *e;
222
223         uci_foreach_element(&p->saved_history, e) {
224                 struct uci_history *h = uci_to_history(e);
225                 char *prefix = "";
226                 char *op = "=";
227
228                 switch(h->cmd) {
229                 case UCI_CMD_REMOVE:
230                         prefix = "-";
231                         break;
232                 case UCI_CMD_LIST_ADD:
233                         op = "+=";
234                         break;
235                 default:
236                         break;
237                 }
238                 printf("%s%s.%s", prefix, p->e.name, h->section);
239                 if (e->name)
240                         printf(".%s", e->name);
241                 if (h->cmd != UCI_CMD_REMOVE)
242                         printf("%s%s", op, h->value);
243                 printf("\n");
244         }
245 }
246
247 static int package_cmd(int cmd, char *tuple)
248 {
249         struct uci_element *e = NULL;
250         struct uci_ptr ptr;
251
252         if (uci_lookup_ptr(ctx, &ptr, tuple, true) != UCI_OK) {
253                 cli_perror();
254                 return 1;
255         }
256
257         e = ptr.last;
258         switch(cmd) {
259         case CMD_CHANGES:
260                 uci_show_changes(ptr.p);
261                 break;
262         case CMD_COMMIT:
263                 if (flags & CLI_FLAG_NOCOMMIT)
264                         return 0;
265                 if (uci_commit(ctx, &ptr.p, false) != UCI_OK)
266                         cli_perror();
267                 break;
268         case CMD_EXPORT:
269                 uci_export(ctx, stdout, ptr.p, true);
270                 break;
271         case CMD_SHOW:
272                 if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) {
273                         ctx->err = UCI_ERR_NOTFOUND;
274                         cli_perror();
275                         return 1;
276                 }
277                 switch(e->type) {
278                         case UCI_TYPE_PACKAGE:
279                                 uci_show_package(ptr.p);
280                                 break;
281                         case UCI_TYPE_SECTION:
282                                 uci_show_section(ptr.s);
283                                 break;
284                         case UCI_TYPE_OPTION:
285                                 uci_show_option(ptr.o);
286                                 break;
287                         default:
288                                 /* should not happen */
289                                 return 1;
290                 }
291                 break;
292         }
293
294         uci_unload(ctx, ptr.p);
295         return 0;
296 }
297
298 static int uci_do_import(int argc, char **argv)
299 {
300         struct uci_package *package = NULL;
301         char *name = NULL;
302         int ret = UCI_OK;
303         bool merge = false;
304
305         if (argc > 2)
306                 return 255;
307
308         if (argc == 2)
309                 name = argv[1];
310         else if (flags & CLI_FLAG_MERGE)
311                 /* need a package to merge */
312                 return 255;
313
314         if (flags & CLI_FLAG_MERGE) {
315                 if (uci_load(ctx, name, &package) != UCI_OK)
316                         package = NULL;
317                 else
318                         merge = true;
319         }
320         ret = uci_import(ctx, input, name, &package, (name != NULL));
321         if (ret == UCI_OK) {
322                 if (merge) {
323                         ret = uci_save(ctx, package);
324                 } else {
325                         struct uci_element *e;
326                         /* loop through all config sections and overwrite existing data */
327                         uci_foreach_element(&ctx->root, e) {
328                                 struct uci_package *p = uci_to_package(e);
329                                 ret = uci_commit(ctx, &p, true);
330                         }
331                 }
332         }
333
334         if (ret != UCI_OK) {
335                 cli_perror();
336                 return 1;
337         }
338
339         return 0;
340 }
341
342 static int uci_do_package_cmd(int cmd, int argc, char **argv)
343 {
344         char **configs = NULL;
345         char **p;
346
347         if (argc > 2)
348                 return 255;
349
350         if (argc == 2)
351                 return package_cmd(cmd, argv[1]);
352
353         if ((uci_list_configs(ctx, &configs) != UCI_OK) || !configs) {
354                 cli_perror();
355                 return 1;
356         }
357
358         for (p = configs; *p; p++) {
359                 package_cmd(cmd, *p);
360         }
361
362         return 0;
363 }
364
365 static int uci_do_add(int argc, char **argv)
366 {
367         struct uci_package *p = NULL;
368         struct uci_section *s = NULL;
369         int ret;
370
371         if (argc != 3)
372                 return 255;
373
374         ret = uci_load(ctx, argv[1], &p);
375         if (ret != UCI_OK)
376                 goto done;
377
378         ret = uci_add_section(ctx, p, argv[2], &s);
379         if (ret != UCI_OK)
380                 goto done;
381
382         ret = uci_save(ctx, p);
383
384 done:
385         if (ret != UCI_OK)
386                 cli_perror();
387         else if (s)
388                 fprintf(stdout, "%s\n", s->e.name);
389
390         return ret;
391 }
392
393 static int uci_do_section_cmd(int cmd, int argc, char **argv)
394 {
395         struct uci_element *e;
396         struct uci_ptr ptr;
397         int ret = UCI_OK;
398
399         if (argc != 2)
400                 return 255;
401
402         if (uci_lookup_ptr(ctx, &ptr, argv[1], true) != UCI_OK) {
403                 cli_perror();
404                 return 1;
405         }
406
407         if (ptr.value && (cmd != CMD_SET) && (cmd != CMD_ADD_LIST) && (cmd != CMD_RENAME))
408                 return 1;
409
410         e = ptr.last;
411         switch(cmd) {
412         case CMD_GET:
413                 if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) {
414                         ctx->err = UCI_ERR_NOTFOUND;
415                         cli_perror();
416                         return 1;
417                 }
418                 switch(e->type) {
419                 case UCI_TYPE_SECTION:
420                         printf("%s\n", ptr.s->type);
421                         break;
422                 case UCI_TYPE_OPTION:
423                         uci_show_value(ptr.o);
424                         break;
425                 default:
426                         break;
427                 }
428                 /* throw the value to stdout */
429                 break;
430         case CMD_RENAME:
431                 ret = uci_rename(ctx, &ptr);
432                 break;
433         case CMD_REVERT:
434                 ret = uci_revert(ctx, &ptr);
435                 break;
436         case CMD_SET:
437                 ret = uci_set(ctx, &ptr);
438                 break;
439         case CMD_ADD_LIST:
440                 ret = uci_add_list(ctx, &ptr);
441                 break;
442         case CMD_DEL:
443                 ret = uci_delete(ctx, &ptr);
444                 break;
445         }
446
447         /* no save necessary for get */
448         if ((cmd == CMD_GET) || (cmd == CMD_REVERT))
449                 return 0;
450
451         /* save changes, but don't commit them yet */
452         if (ret == UCI_OK)
453                 ret = uci_save(ctx, ptr.p);
454
455         if (ret != UCI_OK) {
456                 cli_perror();
457                 return 1;
458         }
459
460         return 0;
461 }
462
463 static int uci_batch_cmd(void)
464 {
465         char *argv[MAX_ARGS + 2];
466         char *str = NULL;
467         int ret = 0;
468         int i, j;
469
470         for(i = 0; i <= MAX_ARGS; i++) {
471                 if (i == MAX_ARGS) {
472                         fprintf(stderr, "Too many arguments\n");
473                         return 1;
474                 }
475                 argv[i] = NULL;
476                 if ((ret = uci_parse_argument(ctx, input, &str, &argv[i])) != UCI_OK) {
477                         cli_perror();
478                         i = 0;
479                         break;
480                 }
481                 if (!argv[i][0])
482                         break;
483                 argv[i] = strdup(argv[i]);
484                 if (!argv[i]) {
485                         perror("uci");
486                         return 1;
487                 }
488         }
489         argv[i] = NULL;
490
491         if (i > 0) {
492                 if (!strcasecmp(argv[0], "exit"))
493                         return 254;
494                 ret = uci_cmd(i, argv);
495         } else
496                 return 0;
497
498         for (j = 0; j < i; j++) {
499                 if (argv[j])
500                         free(argv[j]);
501         }
502
503         return ret;
504 }
505
506 static int uci_batch(void)
507 {
508         int ret = 0;
509
510         flags |= CLI_FLAG_BATCH;
511         while (!feof(input)) {
512                 struct uci_element *e, *tmp;
513
514                 ret = uci_batch_cmd();
515                 if (ret == 254)
516                         return 0;
517                 else if (ret == 255)
518                         fprintf(stderr, "Unknown command\n");
519
520                 /* clean up */
521                 uci_foreach_element_safe(&ctx->root, tmp, e) {
522                         uci_unload(ctx, uci_to_package(e));
523                 }
524         }
525         flags &= ~CLI_FLAG_BATCH;
526
527         return 0;
528 }
529
530 static int uci_cmd(int argc, char **argv)
531 {
532         int cmd = 0;
533
534         if (!strcasecmp(argv[0], "batch") && !(flags & CLI_FLAG_BATCH))
535                 return uci_batch();
536         else if (!strcasecmp(argv[0], "show"))
537                 cmd = CMD_SHOW;
538         else if (!strcasecmp(argv[0], "changes"))
539                 cmd = CMD_CHANGES;
540         else if (!strcasecmp(argv[0], "export"))
541                 cmd = CMD_EXPORT;
542         else if (!strcasecmp(argv[0], "commit"))
543                 cmd = CMD_COMMIT;
544         else if (!strcasecmp(argv[0], "get"))
545                 cmd = CMD_GET;
546         else if (!strcasecmp(argv[0], "set"))
547                 cmd = CMD_SET;
548         else if (!strcasecmp(argv[0], "ren") ||
549                  !strcasecmp(argv[0], "rename"))
550                 cmd = CMD_RENAME;
551         else if (!strcasecmp(argv[0], "revert"))
552                 cmd = CMD_REVERT;
553         else if (!strcasecmp(argv[0], "del") ||
554                  !strcasecmp(argv[0], "delete"))
555                 cmd = CMD_DEL;
556         else if (!strcasecmp(argv[0], "import"))
557                 cmd = CMD_IMPORT;
558         else if (!strcasecmp(argv[0], "help"))
559                 cmd = CMD_HELP;
560         else if (!strcasecmp(argv[0], "add"))
561                 cmd = CMD_ADD;
562         else if (!strcasecmp(argv[0], "add_list"))
563                 cmd = CMD_ADD_LIST;
564         else
565                 cmd = -1;
566
567         switch(cmd) {
568                 case CMD_ADD_LIST:
569                 case CMD_GET:
570                 case CMD_SET:
571                 case CMD_DEL:
572                 case CMD_RENAME:
573                 case CMD_REVERT:
574                         return uci_do_section_cmd(cmd, argc, argv);
575                 case CMD_SHOW:
576                 case CMD_EXPORT:
577                 case CMD_COMMIT:
578                 case CMD_CHANGES:
579                         return uci_do_package_cmd(cmd, argc, argv);
580                 case CMD_IMPORT:
581                         return uci_do_import(argc, argv);
582                 case CMD_ADD:
583                         return uci_do_add(argc, argv);
584                 case CMD_HELP:
585                         uci_usage();
586                         return 0;
587                 default:
588                         return 255;
589         }
590 }
591
592 int main(int argc, char **argv)
593 {
594         int ret;
595         int c;
596
597         flags = CLI_FLAG_SHOW_EXT;
598         appname = argv[0];
599         input = stdin;
600         ctx = uci_alloc_context();
601         if (!ctx) {
602                 fprintf(stderr, "Out of memory\n");
603                 return 1;
604         }
605
606         while((c = getopt(argc, argv, "c:d:f:mnNp:P:sSqX")) != -1) {
607                 switch(c) {
608                         case 'c':
609                                 uci_set_confdir(ctx, optarg);
610                                 break;
611                         case 'd':
612                                 delimiter = optarg;
613                                 break;
614                         case 'f':
615                                 input = fopen(optarg, "r");
616                                 if (!input) {
617                                         perror("uci");
618                                         return 1;
619                                 }
620                                 break;
621                         case 'm':
622                                 flags |= CLI_FLAG_MERGE;
623                                 break;
624                         case 's':
625                                 ctx->flags |= UCI_FLAG_STRICT;
626                                 break;
627                         case 'S':
628                                 ctx->flags &= ~UCI_FLAG_STRICT;
629                                 ctx->flags |= UCI_FLAG_PERROR;
630                                 break;
631                         case 'n':
632                                 ctx->flags |= UCI_FLAG_EXPORT_NAME;
633                                 break;
634                         case 'N':
635                                 ctx->flags &= ~UCI_FLAG_EXPORT_NAME;
636                                 break;
637                         case 'p':
638                                 uci_add_history_path(ctx, optarg);
639                                 break;
640                         case 'P':
641                                 uci_add_history_path(ctx, ctx->savedir);
642                                 uci_set_savedir(ctx, optarg);
643                                 flags |= CLI_FLAG_NOCOMMIT;
644                                 break;
645                         case 'q':
646                                 flags |= CLI_FLAG_QUIET;
647                                 break;
648                         case 'X':
649                                 flags &= ~CLI_FLAG_SHOW_EXT;
650                                 break;
651                         default:
652                                 uci_usage();
653                                 return 0;
654                 }
655         }
656         if (optind > 1)
657                 argv[optind - 1] = argv[0];
658         argv += optind - 1;
659         argc -= optind - 1;
660
661         if (argc < 2) {
662                 uci_usage();
663                 return 0;
664         }
665         ret = uci_cmd(argc - 1, argv + 1);
666         if (input != stdin)
667                 fclose(input);
668         if (ret == 255) {
669                 uci_usage();
670                 return 0;
671         }
672
673         uci_free_context(ctx);
674
675         return ret;
676 }