implement the uci changes command to display uncommitted changes
[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 #define MAX_ARGS        4 /* max command line arguments for batch mode */
20
21 static const char *appname;
22 static enum {
23         CLI_FLAG_MERGE =    (1 << 0),
24         CLI_FLAG_QUIET =    (1 << 1),
25         CLI_FLAG_NOCOMMIT = (1 << 2),
26         CLI_FLAG_BATCH =    (1 << 3),
27 } flags;
28
29 static FILE *input;
30
31 static struct uci_context *ctx;
32 enum {
33         /* section cmds */
34         CMD_GET,
35         CMD_SET,
36         CMD_DEL,
37         CMD_RENAME,
38         CMD_REVERT,
39         /* package cmds */
40         CMD_SHOW,
41         CMD_CHANGES,
42         CMD_EXPORT,
43         CMD_COMMIT,
44         /* other cmds */
45         CMD_IMPORT,
46         CMD_HELP,
47 };
48
49 static int uci_cmd(int argc, char **argv);
50
51 static void uci_usage(void)
52 {
53         fprintf(stderr,
54                 "Usage: %s [<options>] <command> [<arguments>]\n\n"
55                 "Commands:\n"
56                 "\tbatch\n"
57                 "\texport     [<config>]\n"
58                 "\timport     [<config>]\n"
59                 "\tshow       [<config>[.<section>[.<option>]]]\n"
60                 "\tget        <config>.<section>[.<option>]\n"
61                 "\tset        <config>.<section>[.<option>]=<value>\n"
62                 "\trename     <config>.<section>[.<option>]=<name>\n"
63                 "\trevert     <config>[.<section>[.<option>]]\n"
64                 "\n"
65                 "Options:\n"
66                 "\t-f <file>  use <file> as input instead of stdin\n"
67                 "\t-m         when importing, merge data into an existing package\n"
68                 "\t-n         name unnamed sections on export (default)\n"
69                 "\t-N         don't name unnamed sections\n"
70                 "\t-p <path>  add a search path for config change files\n"
71                 "\t-P <path>  add a search path for config change files and use as default\n"
72                 "\t-q         quiet mode (don't print error messages)\n"
73                 "\t-s         force strict mode (stop on parser errors, default)\n"
74                 "\t-S         disable strict mode\n"
75                 "\n",
76                 appname
77         );
78 }
79
80 static void cli_perror(void)
81 {
82         if (flags & CLI_FLAG_QUIET)
83                 return;
84
85         uci_perror(ctx, appname);
86 }
87
88 static void uci_show_section(struct uci_section *p)
89 {
90         struct uci_element *e;
91         const char *cname, *sname;
92
93         cname = p->package->e.name;
94         sname = p->e.name;
95         printf("%s.%s=%s\n", cname, sname, p->type);
96         uci_foreach_element(&p->options, e) {
97                 printf("%s.%s.%s=%s\n", cname, sname, e->name, uci_to_option(e)->value);
98         }
99 }
100
101 static void uci_show_package(struct uci_package *p)
102 {
103         struct uci_element *e;
104
105         uci_foreach_element( &p->sections, e) {
106                 uci_show_section(uci_to_section(e));
107         }
108 }
109
110 static void uci_show_changes(struct uci_package *p)
111 {
112         struct uci_element *e;
113
114         uci_foreach_element(&p->saved_history, e) {
115                 struct uci_history *h = uci_to_history(e);
116
117                 if (h->cmd == UCI_CMD_REMOVE)
118                         printf("-");
119                 printf("%s.%s", p->e.name, h->section);
120                 if (e->name)
121                         printf(".%s", e->name);
122                 if (h->cmd != UCI_CMD_REMOVE)
123                         printf("=%s", h->value);
124                 printf("\n");
125         }
126 }
127
128 static int package_cmd(int cmd, char *package)
129 {
130         struct uci_package *p = NULL;
131         int ret;
132
133         if (cmd == CMD_CHANGES)
134                 ctx->flags |= UCI_FLAG_SAVED_HISTORY;
135         ret = uci_load(ctx, package, &p);
136         if (cmd == CMD_CHANGES)
137                 ctx->flags &= ~UCI_FLAG_SAVED_HISTORY;
138
139         if (ret != UCI_OK) {
140                 cli_perror();
141                 return 1;
142         }
143         if (!p)
144                 return 0;
145         switch(cmd) {
146         case CMD_CHANGES:
147                 uci_show_changes(p);
148                 break;
149         case CMD_COMMIT:
150                 if (flags & CLI_FLAG_NOCOMMIT)
151                         return 0;
152                 if (uci_commit(ctx, &p, false) != UCI_OK)
153                         cli_perror();
154                 break;
155         case CMD_EXPORT:
156                 uci_export(ctx, stdout, p, true);
157                 break;
158         case CMD_SHOW:
159                 uci_show_package(p);
160                 break;
161         }
162
163         uci_unload(ctx, p);
164         return 0;
165 }
166
167 static int uci_do_import(int argc, char **argv)
168 {
169         struct uci_package *package = NULL;
170         char *name = NULL;
171         int ret = UCI_OK;
172
173         if (argc > 2)
174                 return 255;
175
176         if (argc == 2)
177                 name = argv[1];
178         else if (flags & CLI_FLAG_MERGE)
179                 /* need a package to merge */
180                 return 255;
181
182         if (flags & CLI_FLAG_MERGE) {
183                 if (uci_load(ctx, name, &package) != UCI_OK)
184                         package = NULL;
185         }
186         ret = uci_import(ctx, input, name, &package, (name != NULL));
187         if (ret == UCI_OK) {
188                 if (flags & CLI_FLAG_MERGE) {
189                         ret = uci_save(ctx, package);
190                 } else {
191                         struct uci_element *e;
192                         /* loop through all config sections and overwrite existing data */
193                         uci_foreach_element(&ctx->root, e) {
194                                 struct uci_package *p = uci_to_package(e);
195                                 ret = uci_commit(ctx, &p, true);
196                         }
197                 }
198         }
199
200         if (ret != UCI_OK) {
201                 cli_perror();
202                 return 1;
203         }
204
205         return 0;
206 }
207
208 static int uci_do_package_cmd(int cmd, int argc, char **argv)
209 {
210         char **configs = NULL;
211         char **p;
212
213         if (argc > 2)
214                 return 255;
215
216         if (argc == 2)
217                 return package_cmd(cmd, argv[1]);
218
219         if ((uci_list_configs(ctx, &configs) != UCI_OK) || !configs) {
220                 cli_perror();
221                 return 1;
222         }
223
224         for (p = configs; *p; p++) {
225                 package_cmd(cmd, *p);
226         }
227
228         return 0;
229 }
230
231
232 static int uci_do_section_cmd(int cmd, int argc, char **argv)
233 {
234         struct uci_package *p = NULL;
235         struct uci_element *e = NULL;
236         char *package = NULL;
237         char *section = NULL;
238         char *option = NULL;
239         char *value = NULL;
240         char **ptr = NULL;
241         int ret = UCI_OK;
242
243         if (argc != 2)
244                 return 255;
245
246         switch(cmd) {
247         case CMD_SET:
248         case CMD_RENAME:
249                 ptr = &value;
250                 break;
251         default:
252                 break;
253         }
254         if (uci_parse_tuple(ctx, argv[1], &package, &section, &option, ptr) != UCI_OK)
255                 return 1;
256
257         if (uci_load(ctx, package, &p) != UCI_OK) {
258                 cli_perror();
259                 return 1;
260         }
261         if (!p)
262                 return 0;
263
264         switch(cmd) {
265         case CMD_GET:
266                 if (uci_lookup(ctx, &e, p, section, option) != UCI_OK)
267                         return 1;
268
269                 switch(e->type) {
270                 case UCI_TYPE_SECTION:
271                         value = uci_to_section(e)->type;
272                         break;
273                 case UCI_TYPE_OPTION:
274                         value = uci_to_option(e)->value;
275                         break;
276                 default:
277                         /* should not happen */
278                         return 1;
279                 }
280                 /* throw the value to stdout */
281                 printf("%s\n", value);
282                 break;
283         case CMD_RENAME:
284                 ret = uci_rename(ctx, p, section, option, value);
285                 break;
286         case CMD_REVERT:
287                 ret = uci_revert(ctx, &p, section, option);
288                 break;
289         case CMD_SET:
290                 ret = uci_set(ctx, p, section, option, value);
291                 break;
292         case CMD_DEL:
293                 ret = uci_delete(ctx, p, section, option);
294                 break;
295         }
296
297         /* no save necessary for get */
298         if ((cmd == CMD_GET) || (cmd == CMD_REVERT))
299                 return 0;
300
301         /* save changes, but don't commit them yet */
302         if (ret == UCI_OK)
303                 ret = uci_save(ctx, p);
304
305         if (ret != UCI_OK) {
306                 cli_perror();
307                 return 1;
308         }
309
310         return 0;
311 }
312
313 static int uci_batch_cmd(void)
314 {
315         char *argv[MAX_ARGS];
316         char *str = NULL;
317         int ret = 0;
318         int i, j;
319
320         for(i = 0; i <= MAX_ARGS; i++) {
321                 if (i == MAX_ARGS) {
322                         fprintf(stderr, "Too many arguments\n");
323                         return 1;
324                 }
325                 argv[i] = NULL;
326                 if ((ret = uci_parse_argument(ctx, input, &str, &argv[i])) != UCI_OK) {
327                         cli_perror();
328                         i = 0;
329                         break;
330                 }
331                 if (!argv[i][0])
332                         break;
333                 argv[i] = strdup(argv[i]);
334                 if (!argv[i]) {
335                         perror("uci");
336                         return 1;
337                 }
338         }
339         argv[i] = NULL;
340
341         if (i > 0) {
342                 if (!strcasecmp(argv[0], "exit"))
343                         return 254;
344                 ret = uci_cmd(i, argv);
345         } else
346                 return 0;
347
348         for (j = 0; j < i; j++) {
349                 if (argv[j])
350                         free(argv[j]);
351         }
352
353         return ret;
354 }
355
356 static int uci_batch(void)
357 {
358         int ret = 0;
359
360         while (!feof(input)) {
361                 struct uci_element *e, *tmp;
362
363                 ret = uci_batch_cmd();
364                 if (ret == 254)
365                         return 0;
366                 else if (ret == 255)
367                         fprintf(stderr, "Unknown command\n");
368
369                 /* clean up */
370                 uci_cleanup(ctx);
371                 uci_foreach_element_safe(&ctx->root, tmp, e) {
372                         uci_unload(ctx, uci_to_package(e));
373                 }
374         }
375         return 0;
376 }
377
378 static int uci_cmd(int argc, char **argv)
379 {
380         int cmd = 0;
381
382         if (!strcasecmp(argv[0], "batch") && !(flags & CLI_FLAG_BATCH))
383                 return uci_batch();
384         else if (!strcasecmp(argv[0], "show"))
385                 cmd = CMD_SHOW;
386         else if (!strcasecmp(argv[0], "changes"))
387                 cmd = CMD_CHANGES;
388         else if (!strcasecmp(argv[0], "export"))
389                 cmd = CMD_EXPORT;
390         else if (!strcasecmp(argv[0], "commit"))
391                 cmd = CMD_COMMIT;
392         else if (!strcasecmp(argv[0], "get"))
393                 cmd = CMD_GET;
394         else if (!strcasecmp(argv[0], "set"))
395                 cmd = CMD_SET;
396         else if (!strcasecmp(argv[0], "ren") ||
397                  !strcasecmp(argv[0], "rename"))
398                 cmd = CMD_RENAME;
399         else if (!strcasecmp(argv[0], "revert"))
400                 cmd = CMD_REVERT;
401         else if (!strcasecmp(argv[0], "del"))
402                 cmd = CMD_DEL;
403         else if (!strcasecmp(argv[0], "import"))
404                 cmd = CMD_IMPORT;
405         else if (!strcasecmp(argv[0], "help"))
406                 cmd = CMD_HELP;
407         else
408                 cmd = -1;
409
410         switch(cmd) {
411                 case CMD_GET:
412                 case CMD_SET:
413                 case CMD_DEL:
414                 case CMD_RENAME:
415                 case CMD_REVERT:
416                         return uci_do_section_cmd(cmd, argc, argv);
417                 case CMD_SHOW:
418                 case CMD_EXPORT:
419                 case CMD_COMMIT:
420                 case CMD_CHANGES:
421                         return uci_do_package_cmd(cmd, argc, argv);
422                 case CMD_IMPORT:
423                         return uci_do_import(argc, argv);
424                 case CMD_HELP:
425                         uci_usage();
426                         return 0;
427                 default:
428                         return 255;
429         }
430 }
431
432 int main(int argc, char **argv)
433 {
434         int ret;
435         int c;
436
437         appname = argv[0];
438         input = stdin;
439         ctx = uci_alloc_context();
440         if (!ctx) {
441                 fprintf(stderr, "Out of memory\n");
442                 return 1;
443         }
444
445         while((c = getopt(argc, argv, "f:mnNp:P:sSq")) != -1) {
446                 switch(c) {
447                         case 'f':
448                                 input = fopen(optarg, "r");
449                                 if (!input) {
450                                         perror("uci");
451                                         return 1;
452                                 }
453                                 break;
454                         case 'm':
455                                 flags |= CLI_FLAG_MERGE;
456                                 break;
457                         case 's':
458                                 ctx->flags |= UCI_FLAG_STRICT;
459                                 break;
460                         case 'S':
461                                 ctx->flags &= ~UCI_FLAG_STRICT;
462                                 ctx->flags |= UCI_FLAG_PERROR;
463                                 break;
464                         case 'n':
465                                 ctx->flags |= UCI_FLAG_EXPORT_NAME;
466                                 break;
467                         case 'N':
468                                 ctx->flags &= ~UCI_FLAG_EXPORT_NAME;
469                                 break;
470                         case 'p':
471                                 uci_add_history_path(ctx, optarg);
472                                 break;
473                         case 'P':
474                                 uci_add_history_path(ctx, ctx->savedir);
475                                 uci_set_savedir(ctx, optarg);
476                                 flags |= CLI_FLAG_NOCOMMIT;
477                                 break;
478                         case 'q':
479                                 flags |= CLI_FLAG_QUIET;
480                                 break;
481                         default:
482                                 uci_usage();
483                                 return 0;
484                 }
485         }
486         if (optind > 1)
487                 argv[optind - 1] = argv[0];
488         argv += optind - 1;
489         argc -= optind - 1;
490
491         if (argc < 2) {
492                 uci_usage();
493                 return 0;
494         }
495         ret = uci_cmd(argc - 1, argv + 1);
496         if (input != stdin)
497                 fclose(input);
498         if (ret == 255) {
499                 uci_usage();
500                 return 0;
501         }
502
503         uci_free_context(ctx);
504
505         return ret;
506 }