cli.c 15 KB

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