fix a few bugs found during code review
[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 <stdlib.h>
16 #include <unistd.h>
17 #include "uci.h"
18
19 static const char *appname = "uci";
20 static enum {
21         CLI_FLAG_MERGE =    (1 << 0),
22         CLI_FLAG_QUIET =    (1 << 1),
23         CLI_FLAG_NOCOMMIT = (1 << 2),
24 } flags;
25 static FILE *input;
26
27 static struct uci_context *ctx;
28 enum {
29         /* section cmds */
30         CMD_GET,
31         CMD_SET,
32         CMD_DEL,
33         CMD_RENAME,
34         CMD_REVERT,
35         /* package cmds */
36         CMD_SHOW,
37         CMD_IMPORT,
38         CMD_EXPORT,
39         CMD_COMMIT,
40 };
41
42 static void uci_usage(int argc, char **argv)
43 {
44         fprintf(stderr,
45                 "Usage: %s [<options>] <command> [<arguments>]\n\n"
46                 "Commands:\n"
47                 "\texport     [<config>]\n"
48                 "\timport     [<config>]\n"
49                 "\tshow       [<config>[.<section>[.<option>]]]\n"
50                 "\tget        <config>.<section>[.<option>]\n"
51                 "\tset        <config>.<section>[.<option>]=<value>\n"
52                 "\trename     <config>.<section>[.<option>]=<name>\n"
53                 "\trevert     <config>[.<section>[.<option>]]\n"
54                 "\n"
55                 "Options:\n"
56                 "\t-f <file>  use <file> as input instead of stdin\n"
57                 "\t-m         when importing, merge data into an existing package\n"
58                 "\t-n         name unnamed sections on export (default)\n"
59                 "\t-N         don't name unnamed sections\n"
60                 "\t-p <path>  add a search path for config change files\n"
61                 "\t-P <path>  add a search path for config change files and use as default\n"
62                 "\t-q         quiet mode (don't print error messages)\n"
63                 "\t-s         force strict mode (stop on parser errors, default)\n"
64                 "\t-S         disable strict mode\n"
65                 "\n",
66                 argv[0]
67         );
68         exit(255);
69 }
70
71 static void cli_perror(void)
72 {
73         if (flags & CLI_FLAG_QUIET)
74                 return;
75
76         uci_perror(ctx, appname);
77 }
78
79 static void uci_show_section(struct uci_section *p)
80 {
81         struct uci_element *e;
82         const char *cname, *sname;
83
84         cname = p->package->e.name;
85         sname = p->e.name;
86         printf("%s.%s=%s\n", cname, sname, p->type);
87         uci_foreach_element(&p->options, e) {
88                 printf("%s.%s.%s=%s\n", cname, sname, e->name, uci_to_option(e)->value);
89         }
90 }
91
92 static void uci_show_package(struct uci_package *p)
93 {
94         struct uci_element *e;
95
96         uci_foreach_element( &p->sections, e) {
97                 uci_show_section(uci_to_section(e));
98         }
99 }
100
101
102 static int package_cmd(int cmd, char *package)
103 {
104         struct uci_package *p = NULL;
105
106         if (uci_load(ctx, package, &p) != UCI_OK) {
107                 cli_perror();
108                 return 1;
109         }
110         if (!p)
111                 return 0;
112         switch(cmd) {
113         case CMD_COMMIT:
114                 if (flags & CLI_FLAG_NOCOMMIT)
115                         return 0;
116                 if (uci_commit(ctx, &p, false) != UCI_OK)
117                         cli_perror();
118                 break;
119         case CMD_EXPORT:
120                 uci_export(ctx, stdout, p, true);
121                 break;
122         case CMD_SHOW:
123                 uci_show_package(p);
124                 break;
125         }
126
127         uci_unload(ctx, p);
128         return 0;
129 }
130
131 static int uci_do_import(int argc, char **argv)
132 {
133         struct uci_package *package = NULL;
134         char *name = NULL;
135         int ret = UCI_OK;
136
137         if (argc > 2)
138                 return 255;
139
140         if (argc == 2)
141                 name = argv[1];
142         else if (flags & CLI_FLAG_MERGE)
143                 /* need a package to merge */
144                 return 255;
145
146         if (flags & CLI_FLAG_MERGE) {
147                 if (uci_load(ctx, name, &package) != UCI_OK)
148                         package = NULL;
149         }
150         ret = uci_import(ctx, input, name, &package, (name != NULL));
151         if (ret == UCI_OK) {
152                 if (flags & CLI_FLAG_MERGE) {
153                         ret = uci_save(ctx, package);
154                 } else {
155                         struct uci_element *e;
156                         /* loop through all config sections and overwrite existing data */
157                         uci_foreach_element(&ctx->root, e) {
158                                 struct uci_package *p = uci_to_package(e);
159                                 ret = uci_commit(ctx, &p, true);
160                         }
161                 }
162         }
163
164         if (ret != UCI_OK) {
165                 cli_perror();
166                 return 1;
167         }
168
169         return 0;
170 }
171
172 static int uci_do_package_cmd(int cmd, int argc, char **argv)
173 {
174         char **configs = NULL;
175         char **p;
176
177         if (argc > 2)
178                 return 255;
179
180         if (argc == 2)
181                 return package_cmd(cmd, argv[1]);
182
183         if ((uci_list_configs(ctx, &configs) != UCI_OK) || !configs) {
184                 cli_perror();
185                 return 1;
186         }
187
188         for (p = configs; *p; p++) {
189                 package_cmd(cmd, *p);
190         }
191
192         return 0;
193 }
194
195
196 static int uci_do_section_cmd(int cmd, int argc, char **argv)
197 {
198         struct uci_package *p = NULL;
199         struct uci_element *e = NULL;
200         char *package = NULL;
201         char *section = NULL;
202         char *option = NULL;
203         char *value = NULL;
204         char **ptr = NULL;
205         int ret = UCI_OK;
206
207         if (argc != 2)
208                 return 255;
209
210         switch(cmd) {
211         case CMD_SET:
212         case CMD_RENAME:
213                 ptr = &value;
214                 break;
215         default:
216                 break;
217         }
218         if (uci_parse_tuple(ctx, argv[1], &package, &section, &option, ptr) != UCI_OK)
219                 return 1;
220
221         if (uci_load(ctx, package, &p) != UCI_OK) {
222                 cli_perror();
223                 return 1;
224         }
225         if (!p)
226                 return 0;
227
228         switch(cmd) {
229         case CMD_GET:
230                 if (uci_lookup(ctx, &e, p, section, option) != UCI_OK)
231                         return 1;
232
233                 switch(e->type) {
234                 case UCI_TYPE_SECTION:
235                         value = uci_to_section(e)->type;
236                         break;
237                 case UCI_TYPE_OPTION:
238                         value = uci_to_option(e)->value;
239                         break;
240                 default:
241                         /* should not happen */
242                         return 1;
243                 }
244                 /* throw the value to stdout */
245                 printf("%s\n", value);
246                 break;
247         case CMD_RENAME:
248                 ret = uci_rename(ctx, p, section, option, value);
249                 break;
250         case CMD_REVERT:
251                 ret = uci_revert(ctx, &p, section, option);
252                 break;
253         case CMD_SET:
254                 ret = uci_set(ctx, p, section, option, value);
255                 break;
256         case CMD_DEL:
257                 ret = uci_delete(ctx, p, section, option);
258                 break;
259         }
260
261         /* no save necessary for get */
262         if ((cmd == CMD_GET) || (cmd == CMD_REVERT))
263                 return 0;
264
265         /* save changes, but don't commit them yet */
266         if (ret == UCI_OK)
267                 ret = uci_save(ctx, p);
268
269         if (ret != UCI_OK) {
270                 cli_perror();
271                 return 1;
272         }
273
274         return 0;
275 }
276
277 static int uci_cmd(int argc, char **argv)
278 {
279         int cmd = 0;
280
281         if (!strcasecmp(argv[0], "show"))
282                 cmd = CMD_SHOW;
283         else if (!strcasecmp(argv[0], "export"))
284                 cmd = CMD_EXPORT;
285         else if (!strcasecmp(argv[0], "commit"))
286                 cmd = CMD_COMMIT;
287         else if (!strcasecmp(argv[0], "get"))
288                 cmd = CMD_GET;
289         else if (!strcasecmp(argv[0], "set"))
290                 cmd = CMD_SET;
291         else if (!strcasecmp(argv[0], "ren") ||
292                  !strcasecmp(argv[0], "rename"))
293                 cmd = CMD_RENAME;
294         else if (!strcasecmp(argv[0], "revert"))
295                 cmd = CMD_REVERT;
296         else if (!strcasecmp(argv[0], "del"))
297                 cmd = CMD_DEL;
298         else if (!strcasecmp(argv[0], "import"))
299                 cmd = CMD_IMPORT;
300         else
301                 cmd = -1;
302
303         switch(cmd) {
304                 case CMD_GET:
305                 case CMD_SET:
306                 case CMD_DEL:
307                 case CMD_RENAME:
308                 case CMD_REVERT:
309                         return uci_do_section_cmd(cmd, argc, argv);
310                 case CMD_SHOW:
311                 case CMD_EXPORT:
312                 case CMD_COMMIT:
313                         return uci_do_package_cmd(cmd, argc, argv);
314                 case CMD_IMPORT:
315                         return uci_do_import(argc, argv);
316                 default:
317                         return 255;
318         }
319 }
320
321 int main(int argc, char **argv)
322 {
323         int ret;
324         int c;
325
326         input = stdin;
327         ctx = uci_alloc_context();
328         if (!ctx) {
329                 fprintf(stderr, "Out of memory\n");
330                 return 1;
331         }
332
333         while((c = getopt(argc, argv, "f:mnNp:P:sSq")) != -1) {
334                 switch(c) {
335                         case 'f':
336                                 input = fopen(optarg, "r");
337                                 if (!input) {
338                                         perror("uci");
339                                         return 1;
340                                 }
341                                 break;
342                         case 'm':
343                                 flags |= CLI_FLAG_MERGE;
344                                 break;
345                         case 's':
346                                 ctx->flags |= UCI_FLAG_STRICT;
347                                 break;
348                         case 'S':
349                                 ctx->flags &= ~UCI_FLAG_STRICT;
350                                 ctx->flags |= UCI_FLAG_PERROR;
351                                 break;
352                         case 'n':
353                                 ctx->flags |= UCI_FLAG_EXPORT_NAME;
354                                 break;
355                         case 'N':
356                                 ctx->flags &= ~UCI_FLAG_EXPORT_NAME;
357                                 break;
358                         case 'p':
359                                 uci_add_history_path(ctx, optarg);
360                                 break;
361                         case 'P':
362                                 uci_add_history_path(ctx, ctx->savedir);
363                                 uci_set_savedir(ctx, optarg);
364                                 flags |= CLI_FLAG_NOCOMMIT;
365                                 break;
366                         case 'q':
367                                 flags |= CLI_FLAG_QUIET;
368                                 break;
369                         default:
370                                 uci_usage(argc, argv);
371                                 break;
372                 }
373         }
374         if (optind > 1)
375                 argv[optind - 1] = argv[0];
376         argv += optind - 1;
377         argc -= optind - 1;
378
379         if (argc < 2)
380                 uci_usage(argc, argv);
381         ret = uci_cmd(argc - 1, argv + 1);
382         if (input != stdin)
383                 fclose(input);
384         if (ret == 255)
385                 uci_usage(argc, argv);
386
387         uci_free_context(ctx);
388
389         return ret;
390 }