?? cli.c
字號:
/* * cli - Command Line Interface for the Unified Configuration Interface * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */#include <strings.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include "uci.h"#define MAX_ARGS 4 /* max command line arguments for batch mode */static const char *delimiter = " ";static const char *appname;static enum { CLI_FLAG_MERGE = (1 << 0), CLI_FLAG_QUIET = (1 << 1), CLI_FLAG_NOCOMMIT = (1 << 2), CLI_FLAG_BATCH = (1 << 3), CLI_FLAG_SHOW_EXT = (1 << 4),} flags;static FILE *input;static struct uci_context *ctx;enum { /* section cmds */ CMD_GET, CMD_SET, CMD_ADD_LIST, CMD_DEL, CMD_RENAME, CMD_REVERT, /* package cmds */ CMD_SHOW, CMD_CHANGES, CMD_EXPORT, CMD_COMMIT, /* other cmds */ CMD_ADD, CMD_IMPORT, CMD_HELP,};struct uci_type_list { unsigned int idx; const char *name; struct uci_type_list *next;};static struct uci_type_list *type_list = NULL;static char *typestr = NULL;static const char *cur_section_ref = NULL;static int uci_cmd(int argc, char **argv);static voiduci_reset_typelist(void){ struct uci_type_list *type; while (type_list != NULL) { type = type_list; type_list = type_list->next; free(type); } if (typestr) { free(typestr); typestr = NULL; } cur_section_ref = NULL;}static char *uci_lookup_section_ref(struct uci_section *s){ struct uci_type_list *ti = type_list; int maxlen; if (!s->anonymous || !(flags & CLI_FLAG_SHOW_EXT)) return s->e.name; /* look up in section type list */ while (ti) { if (strcmp(ti->name, s->type) == 0) break; ti = ti->next; } if (!ti) { ti = malloc(sizeof(struct uci_type_list)); memset(ti, 0, sizeof(struct uci_type_list)); ti->next = type_list; type_list = ti; ti->name = s->type; } maxlen = strlen(s->type) + 1 + 2 + 10; if (!typestr) { typestr = malloc(maxlen); } else { typestr = realloc(typestr, maxlen); } sprintf(typestr, "@%s[%d]", ti->name, ti->idx); ti->idx++; return typestr;}static void uci_usage(void){ fprintf(stderr, "Usage: %s [<options>] <command> [<arguments>]\n\n" "Commands:\n" "\tbatch\n" "\texport [<config>]\n" "\timport [<config>]\n" "\tchanges [<config>]\n" "\tcommit [<config>]\n" "\tadd <config> <section-type>\n" "\tadd_list <config>.<section>.<option>=<string>\n" "\tshow [<config>[.<section>[.<option>]]]\n" "\tget <config>.<section>[.<option>]\n" "\tset <config>.<section>[.<option>]=<value>\n" "\tdelete <config>[.<section[.<option>]]\n" "\trename <config>.<section>[.<option>]=<name>\n" "\trevert <config>[.<section>[.<option>]]\n" "\n" "Options:\n" "\t-c <path> set the search path for config files (default: /etc/config)\n" "\t-d <str> set the delimiter for list values in uci show\n" "\t-f <file> use <file> as input instead of stdin\n" "\t-m when importing, merge data into an existing package\n" "\t-n name unnamed sections on export (default)\n" "\t-N don't name unnamed sections\n" "\t-p <path> add a search path for config change files\n" "\t-P <path> add a search path for config change files and use as default\n" "\t-q quiet mode (don't print error messages)\n" "\t-s force strict mode (stop on parser errors, default)\n" "\t-S disable strict mode\n" "\t-X do not use extended syntax on 'show'\n" "\n", appname );}static void cli_perror(void){ if (flags & CLI_FLAG_QUIET) return; uci_perror(ctx, appname);}static void uci_show_value(struct uci_option *o){ struct uci_element *e; bool sep = false; switch(o->type) { case UCI_TYPE_STRING: printf("%s\n", o->v.string); break; case UCI_TYPE_LIST: uci_foreach_element(&o->v.list, e) { printf("%s%s", (sep ? delimiter : ""), e->name); sep = true; } printf("\n"); break; default: printf("<unknown>\n"); break; }}static void uci_show_option(struct uci_option *o){ printf("%s.%s.%s=", o->section->package->e.name, (cur_section_ref ? cur_section_ref : o->section->e.name), o->e.name); uci_show_value(o);}static void uci_show_section(struct uci_section *s){ struct uci_element *e; const char *cname; const char *sname; cname = s->package->e.name; sname = (cur_section_ref ? cur_section_ref : s->e.name); printf("%s.%s=%s\n", cname, sname, s->type); uci_foreach_element(&s->options, e) { uci_show_option(uci_to_option(e)); }}static void uci_show_package(struct uci_package *p){ struct uci_element *e; uci_reset_typelist(); uci_foreach_element( &p->sections, e) { struct uci_section *s = uci_to_section(e); cur_section_ref = uci_lookup_section_ref(s); uci_show_section(s); } uci_reset_typelist();}static void uci_show_changes(struct uci_package *p){ struct uci_element *e; uci_foreach_element(&p->saved_history, e) { struct uci_history *h = uci_to_history(e); char *prefix = ""; char *op = "="; switch(h->cmd) { case UCI_CMD_REMOVE: prefix = "-"; break; case UCI_CMD_LIST_ADD: op = "+="; break; default: break; } printf("%s%s.%s", prefix, p->e.name, h->section); if (e->name) printf(".%s", e->name); if (h->cmd != UCI_CMD_REMOVE) printf("%s%s", op, h->value); printf("\n"); }}static int package_cmd(int cmd, char *tuple){ struct uci_element *e = NULL; struct uci_ptr ptr; if (uci_lookup_ptr(ctx, &ptr, tuple, true) != UCI_OK) { cli_perror(); return 1; } e = ptr.last; switch(cmd) { case CMD_CHANGES: uci_show_changes(ptr.p); break; case CMD_COMMIT: if (flags & CLI_FLAG_NOCOMMIT) return 0; if (uci_commit(ctx, &ptr.p, false) != UCI_OK) cli_perror(); break; case CMD_EXPORT: uci_export(ctx, stdout, ptr.p, true); break; case CMD_SHOW: if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) { ctx->err = UCI_ERR_NOTFOUND; cli_perror(); return 1; } switch(e->type) { case UCI_TYPE_PACKAGE: uci_show_package(ptr.p); break; case UCI_TYPE_SECTION: uci_show_section(ptr.s); break; case UCI_TYPE_OPTION: uci_show_option(ptr.o); break; default: /* should not happen */ return 1; } break; } uci_unload(ctx, ptr.p); return 0;}static int uci_do_import(int argc, char **argv){ struct uci_package *package = NULL; char *name = NULL; int ret = UCI_OK; bool merge = false; if (argc > 2) return 255; if (argc == 2) name = argv[1]; else if (flags & CLI_FLAG_MERGE) /* need a package to merge */ return 255; if (flags & CLI_FLAG_MERGE) { if (uci_load(ctx, name, &package) != UCI_OK) package = NULL; else merge = true; } ret = uci_import(ctx, input, name, &package, (name != NULL)); if (ret == UCI_OK) { if (merge) { ret = uci_save(ctx, package); } else { struct uci_element *e; /* loop through all config sections and overwrite existing data */ uci_foreach_element(&ctx->root, e) { struct uci_package *p = uci_to_package(e); ret = uci_commit(ctx, &p, true); } } } if (ret != UCI_OK) { cli_perror(); return 1; } return 0;}static int uci_do_package_cmd(int cmd, int argc, char **argv){ char **configs = NULL; char **p; if (argc > 2) return 255; if (argc == 2) return package_cmd(cmd, argv[1]); if ((uci_list_configs(ctx, &configs) != UCI_OK) || !configs) { cli_perror(); return 1; } for (p = configs; *p; p++) { package_cmd(cmd, *p); } return 0;}static int uci_do_add(int argc, char **argv){ struct uci_package *p = NULL; struct uci_section *s = NULL; int ret; if (argc != 3) return 255; ret = uci_load(ctx, argv[1], &p); if (ret != UCI_OK) goto done; ret = uci_add_section(ctx, p, argv[2], &s); if (ret != UCI_OK) goto done; ret = uci_save(ctx, p);done: if (ret != UCI_OK) cli_perror(); else if (s) fprintf(stdout, "%s\n", s->e.name); return ret;}static int uci_do_section_cmd(int cmd, int argc, char **argv){ struct uci_element *e; struct uci_ptr ptr; int ret = UCI_OK; if (argc != 2) return 255; if (uci_lookup_ptr(ctx, &ptr, argv[1], true) != UCI_OK) { cli_perror(); return 1; } if (ptr.value && (cmd != CMD_SET) && (cmd != CMD_ADD_LIST) && (cmd != CMD_RENAME)) return 1; e = ptr.last; switch(cmd) { case CMD_GET: if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) { ctx->err = UCI_ERR_NOTFOUND; cli_perror(); return 1; } switch(e->type) { case UCI_TYPE_SECTION: printf("%s\n", ptr.s->type); break; case UCI_TYPE_OPTION: uci_show_value(ptr.o); break; default: break; } /* throw the value to stdout */ break; case CMD_RENAME: ret = uci_rename(ctx, &ptr); break; case CMD_REVERT: ret = uci_revert(ctx, &ptr); break; case CMD_SET: ret = uci_set(ctx, &ptr); break; case CMD_ADD_LIST: ret = uci_add_list(ctx, &ptr); break; case CMD_DEL: ret = uci_delete(ctx, &ptr); break; } /* no save necessary for get */ if ((cmd == CMD_GET) || (cmd == CMD_REVERT)) return 0; /* save changes, but don't commit them yet */ if (ret == UCI_OK) ret = uci_save(ctx, ptr.p); if (ret != UCI_OK) { cli_perror(); return 1; } return 0;}static int uci_batch_cmd(void){ char *argv[MAX_ARGS]; char *str = NULL; int ret = 0; int i, j; for(i = 0; i <= MAX_ARGS; i++) { if (i == MAX_ARGS) { fprintf(stderr, "Too many arguments\n"); return 1; } argv[i] = NULL; if ((ret = uci_parse_argument(ctx, input, &str, &argv[i])) != UCI_OK) { cli_perror(); i = 0; break; } if (!argv[i][0]) break; argv[i] = strdup(argv[i]); if (!argv[i]) { perror("uci"); return 1; } } argv[i] = NULL; if (i > 0) { if (!strcasecmp(argv[0], "exit")) return 254; ret = uci_cmd(i, argv); } else return 0; for (j = 0; j < i; j++) { if (argv[j]) free(argv[j]); } return ret;}static int uci_batch(void){ int ret = 0; flags |= CLI_FLAG_BATCH; while (!feof(input)) { struct uci_element *e, *tmp; ret = uci_batch_cmd(); if (ret == 254) return 0; else if (ret == 255) fprintf(stderr, "Unknown command\n"); /* clean up */ uci_foreach_element_safe(&ctx->root, tmp, e) { uci_unload(ctx, uci_to_package(e)); } } flags &= ~CLI_FLAG_BATCH; return 0;}static int uci_cmd(int argc, char **argv){ int cmd = 0; if (!strcasecmp(argv[0], "batch") && !(flags & CLI_FLAG_BATCH)) return uci_batch(); else if (!strcasecmp(argv[0], "show")) cmd = CMD_SHOW; else if (!strcasecmp(argv[0], "changes")) cmd = CMD_CHANGES; else if (!strcasecmp(argv[0], "export")) cmd = CMD_EXPORT; else if (!strcasecmp(argv[0], "commit")) cmd = CMD_COMMIT; else if (!strcasecmp(argv[0], "get")) cmd = CMD_GET; else if (!strcasecmp(argv[0], "set")) cmd = CMD_SET; else if (!strcasecmp(argv[0], "ren") || !strcasecmp(argv[0], "rename")) cmd = CMD_RENAME; else if (!strcasecmp(argv[0], "revert")) cmd = CMD_REVERT; else if (!strcasecmp(argv[0], "del") || !strcasecmp(argv[0], "delete")) cmd = CMD_DEL; else if (!strcasecmp(argv[0], "import")) cmd = CMD_IMPORT; else if (!strcasecmp(argv[0], "help")) cmd = CMD_HELP; else if (!strcasecmp(argv[0], "add")) cmd = CMD_ADD; else if (!strcasecmp(argv[0], "add_list")) cmd = CMD_ADD_LIST; else cmd = -1; switch(cmd) { case CMD_ADD_LIST: case CMD_GET: case CMD_SET: case CMD_DEL: case CMD_RENAME: case CMD_REVERT: return uci_do_section_cmd(cmd, argc, argv); case CMD_SHOW: case CMD_EXPORT: case CMD_COMMIT: case CMD_CHANGES: return uci_do_package_cmd(cmd, argc, argv); case CMD_IMPORT: return uci_do_import(argc, argv); case CMD_ADD: return uci_do_add(argc, argv); case CMD_HELP: uci_usage(); return 0; default: return 255; }}int main(int argc, char **argv){ int ret; int c; flags = CLI_FLAG_SHOW_EXT; appname = argv[0]; input = stdin; ctx = uci_alloc_context(); if (!ctx) { fprintf(stderr, "Out of memory\n"); return 1; } while((c = getopt(argc, argv, "c:d:f:mnNp:P:sSqX")) != -1) { switch(c) { case 'c': uci_set_confdir(ctx, optarg); break; case 'd': delimiter = optarg; break; case 'f': input = fopen(optarg, "r"); if (!input) { perror("uci"); return 1; } break; case 'm': flags |= CLI_FLAG_MERGE; break; case 's': ctx->flags |= UCI_FLAG_STRICT; break; case 'S': ctx->flags &= ~UCI_FLAG_STRICT; ctx->flags |= UCI_FLAG_PERROR; break; case 'n': ctx->flags |= UCI_FLAG_EXPORT_NAME; break; case 'N': ctx->flags &= ~UCI_FLAG_EXPORT_NAME; break; case 'p': uci_add_history_path(ctx, optarg); break; case 'P': uci_add_history_path(ctx, ctx->savedir); uci_set_savedir(ctx, optarg); flags |= CLI_FLAG_NOCOMMIT; break; case 'q': flags |= CLI_FLAG_QUIET; break; case 'X': flags &= ~CLI_FLAG_SHOW_EXT; break; default: uci_usage(); return 0; } } if (optind > 1) argv[optind - 1] = argv[0]; argv += optind - 1; argc -= optind - 1; if (argc < 2) { uci_usage(); return 0; } ret = uci_cmd(argc - 1, argv + 1); if (input != stdin) fclose(input); if (ret == 255) { uci_usage(); return 0; } uci_free_context(ctx); return ret;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -