cli.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  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. }
  277. break;
  278. case CMD_EXPORT:
  279. uci_export(ctx, stdout, ptr.p, true);
  280. break;
  281. case CMD_SHOW:
  282. if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) {
  283. ctx->err = UCI_ERR_NOTFOUND;
  284. cli_perror();
  285. goto out;
  286. }
  287. switch(e->type) {
  288. case UCI_TYPE_PACKAGE:
  289. uci_show_package(ptr.p);
  290. break;
  291. case UCI_TYPE_SECTION:
  292. uci_show_section(ptr.s);
  293. break;
  294. case UCI_TYPE_OPTION:
  295. uci_show_option(ptr.o, true);
  296. break;
  297. default:
  298. /* should not happen */
  299. goto out;
  300. }
  301. break;
  302. }
  303. out:
  304. if (ptr.p)
  305. uci_unload(ctx, ptr.p);
  306. return ret;
  307. }
  308. static int uci_do_import(int argc, char **argv)
  309. {
  310. struct uci_package *package = NULL;
  311. char *name = NULL;
  312. int ret = UCI_OK;
  313. bool merge = false;
  314. if (argc > 2)
  315. return 255;
  316. if (argc == 2)
  317. name = argv[1];
  318. else if (flags & CLI_FLAG_MERGE)
  319. /* need a package to merge */
  320. return 255;
  321. if (flags & CLI_FLAG_MERGE) {
  322. if (uci_load(ctx, name, &package) != UCI_OK)
  323. package = NULL;
  324. else
  325. merge = true;
  326. }
  327. ret = uci_import(ctx, input, name, &package, (name != NULL));
  328. if (ret == UCI_OK) {
  329. if (merge) {
  330. ret = uci_save(ctx, package);
  331. } else {
  332. struct uci_element *e;
  333. /* loop through all config sections and overwrite existing data */
  334. uci_foreach_element(&ctx->root, e) {
  335. struct uci_package *p = uci_to_package(e);
  336. ret = uci_commit(ctx, &p, true);
  337. }
  338. }
  339. }
  340. if (ret != UCI_OK) {
  341. cli_perror();
  342. return 1;
  343. }
  344. return 0;
  345. }
  346. static int uci_do_package_cmd(int cmd, int argc, char **argv)
  347. {
  348. char **configs = NULL;
  349. char **p;
  350. int ret = 1;
  351. if (argc > 2)
  352. return 255;
  353. if (argc == 2)
  354. return package_cmd(cmd, argv[1]);
  355. if ((uci_list_configs(ctx, &configs) != UCI_OK) || !configs) {
  356. cli_perror();
  357. goto out;
  358. }
  359. for (p = configs; *p; p++) {
  360. package_cmd(cmd, *p);
  361. }
  362. ret = 0;
  363. out:
  364. free(configs);
  365. return ret;
  366. }
  367. static int uci_do_add(int argc, char **argv)
  368. {
  369. struct uci_package *p = NULL;
  370. struct uci_section *s = NULL;
  371. int ret;
  372. if (argc != 3)
  373. return 255;
  374. ret = uci_load(ctx, argv[1], &p);
  375. if (ret != UCI_OK)
  376. goto done;
  377. ret = uci_add_section(ctx, p, argv[2], &s);
  378. if (ret != UCI_OK)
  379. goto done;
  380. ret = uci_save(ctx, p);
  381. done:
  382. if (ret != UCI_OK)
  383. cli_perror();
  384. else if (s)
  385. fprintf(stdout, "%s\n", s->e.name);
  386. return ret;
  387. }
  388. static int uci_do_section_cmd(int cmd, int argc, char **argv)
  389. {
  390. struct uci_element *e;
  391. struct uci_ptr ptr;
  392. int ret = UCI_OK;
  393. int dummy;
  394. if (argc != 2)
  395. return 255;
  396. if (uci_lookup_ptr(ctx, &ptr, argv[1], true) != UCI_OK) {
  397. cli_perror();
  398. return 1;
  399. }
  400. if (ptr.value && (cmd != CMD_SET) && (cmd != CMD_DEL) &&
  401. (cmd != CMD_ADD_LIST) && (cmd != CMD_DEL_LIST) &&
  402. (cmd != CMD_RENAME) && (cmd != CMD_REORDER))
  403. return 1;
  404. e = ptr.last;
  405. switch(cmd) {
  406. case CMD_GET:
  407. if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) {
  408. ctx->err = UCI_ERR_NOTFOUND;
  409. cli_perror();
  410. return 1;
  411. }
  412. switch(e->type) {
  413. case UCI_TYPE_SECTION:
  414. printf("%s\n", ptr.s->type);
  415. break;
  416. case UCI_TYPE_OPTION:
  417. uci_show_value(ptr.o, false);
  418. break;
  419. default:
  420. break;
  421. }
  422. /* throw the value to stdout */
  423. break;
  424. case CMD_RENAME:
  425. ret = uci_rename(ctx, &ptr);
  426. break;
  427. case CMD_REVERT:
  428. ret = uci_revert(ctx, &ptr);
  429. break;
  430. case CMD_SET:
  431. ret = uci_set(ctx, &ptr);
  432. break;
  433. case CMD_ADD_LIST:
  434. ret = uci_add_list(ctx, &ptr);
  435. break;
  436. case CMD_DEL_LIST:
  437. ret = uci_del_list(ctx, &ptr);
  438. break;
  439. case CMD_REORDER:
  440. if (!ptr.s || !ptr.value) {
  441. ctx->err = UCI_ERR_NOTFOUND;
  442. cli_perror();
  443. return 1;
  444. }
  445. ret = uci_reorder_section(ctx, ptr.s, strtoul(ptr.value, NULL, 10));
  446. break;
  447. case CMD_DEL:
  448. if (ptr.value && !sscanf(ptr.value, "%d", &dummy))
  449. return 1;
  450. ret = uci_delete(ctx, &ptr);
  451. break;
  452. }
  453. /* no save necessary for get */
  454. if ((cmd == CMD_GET) || (cmd == CMD_REVERT))
  455. return 0;
  456. /* save changes, but don't commit them yet */
  457. if (ret == UCI_OK)
  458. ret = uci_save(ctx, ptr.p);
  459. if (ret != UCI_OK) {
  460. cli_perror();
  461. return 1;
  462. }
  463. return 0;
  464. }
  465. static int uci_batch_cmd(void)
  466. {
  467. char *argv[MAX_ARGS + 2];
  468. char *str = NULL;
  469. int ret = 0;
  470. int i, j;
  471. for(i = 0; i <= MAX_ARGS; i++) {
  472. if (i == MAX_ARGS) {
  473. fprintf(stderr, "Too many arguments\n");
  474. return 1;
  475. }
  476. argv[i] = NULL;
  477. if ((ret = uci_parse_argument(ctx, input, &str, &argv[i])) != UCI_OK) {
  478. cli_perror();
  479. i = 0;
  480. break;
  481. }
  482. if (!argv[i][0])
  483. break;
  484. argv[i] = strdup(argv[i]);
  485. if (!argv[i]) {
  486. perror("uci");
  487. return 1;
  488. }
  489. }
  490. argv[i] = NULL;
  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. for (j = 0; j < i; j++) {
  498. free(argv[j]);
  499. }
  500. return ret;
  501. }
  502. static int uci_batch(void)
  503. {
  504. int ret = 0;
  505. flags |= CLI_FLAG_BATCH;
  506. while (!feof(input)) {
  507. struct uci_element *e, *tmp;
  508. ret = uci_batch_cmd();
  509. if (ret == 254)
  510. return 0;
  511. else if (ret == 255)
  512. fprintf(stderr, "Unknown command\n");
  513. /* clean up */
  514. uci_foreach_element_safe(&ctx->root, tmp, e) {
  515. uci_unload(ctx, uci_to_package(e));
  516. }
  517. }
  518. flags &= ~CLI_FLAG_BATCH;
  519. return 0;
  520. }
  521. static int uci_cmd(int argc, char **argv)
  522. {
  523. int cmd = 0;
  524. if (!strcasecmp(argv[0], "batch") && !(flags & CLI_FLAG_BATCH))
  525. return uci_batch();
  526. else if (!strcasecmp(argv[0], "show"))
  527. cmd = CMD_SHOW;
  528. else if (!strcasecmp(argv[0], "changes"))
  529. cmd = CMD_CHANGES;
  530. else if (!strcasecmp(argv[0], "export"))
  531. cmd = CMD_EXPORT;
  532. else if (!strcasecmp(argv[0], "commit"))
  533. cmd = CMD_COMMIT;
  534. else if (!strcasecmp(argv[0], "get"))
  535. cmd = CMD_GET;
  536. else if (!strcasecmp(argv[0], "set"))
  537. cmd = CMD_SET;
  538. else if (!strcasecmp(argv[0], "ren") ||
  539. !strcasecmp(argv[0], "rename"))
  540. cmd = CMD_RENAME;
  541. else if (!strcasecmp(argv[0], "revert"))
  542. cmd = CMD_REVERT;
  543. else if (!strcasecmp(argv[0], "reorder"))
  544. cmd = CMD_REORDER;
  545. else if (!strcasecmp(argv[0], "del") ||
  546. !strcasecmp(argv[0], "delete"))
  547. cmd = CMD_DEL;
  548. else if (!strcasecmp(argv[0], "import"))
  549. cmd = CMD_IMPORT;
  550. else if (!strcasecmp(argv[0], "help"))
  551. cmd = CMD_HELP;
  552. else if (!strcasecmp(argv[0], "add"))
  553. cmd = CMD_ADD;
  554. else if (!strcasecmp(argv[0], "add_list"))
  555. cmd = CMD_ADD_LIST;
  556. else if (!strcasecmp(argv[0], "del_list"))
  557. cmd = CMD_DEL_LIST;
  558. else
  559. cmd = -1;
  560. switch(cmd) {
  561. case CMD_ADD_LIST:
  562. case CMD_DEL_LIST:
  563. case CMD_GET:
  564. case CMD_SET:
  565. case CMD_DEL:
  566. case CMD_RENAME:
  567. case CMD_REVERT:
  568. case CMD_REORDER:
  569. return uci_do_section_cmd(cmd, argc, argv);
  570. case CMD_SHOW:
  571. case CMD_EXPORT:
  572. case CMD_COMMIT:
  573. case CMD_CHANGES:
  574. return uci_do_package_cmd(cmd, argc, argv);
  575. case CMD_IMPORT:
  576. return uci_do_import(argc, argv);
  577. case CMD_ADD:
  578. return uci_do_add(argc, argv);
  579. case CMD_HELP:
  580. uci_usage();
  581. return 0;
  582. default:
  583. return 255;
  584. }
  585. }
  586. int main(int argc, char **argv)
  587. {
  588. int ret;
  589. int c;
  590. flags = CLI_FLAG_SHOW_EXT;
  591. appname = argv[0];
  592. input = stdin;
  593. ctx = uci_alloc_context();
  594. if (!ctx) {
  595. fprintf(stderr, "Out of memory\n");
  596. return 1;
  597. }
  598. while((c = getopt(argc, argv, "c:d:f:LmnNp:P:sSqX")) != -1) {
  599. switch(c) {
  600. case 'c':
  601. uci_set_confdir(ctx, optarg);
  602. break;
  603. case 'd':
  604. delimiter = optarg;
  605. break;
  606. case 'f':
  607. if (input != stdin) {
  608. perror("uci");
  609. return 1;
  610. }
  611. input = fopen(optarg, "r");
  612. if (!input) {
  613. perror("uci");
  614. return 1;
  615. }
  616. break;
  617. case 'm':
  618. flags |= CLI_FLAG_MERGE;
  619. break;
  620. case 's':
  621. ctx->flags |= UCI_FLAG_STRICT;
  622. break;
  623. case 'S':
  624. ctx->flags &= ~UCI_FLAG_STRICT;
  625. ctx->flags |= UCI_FLAG_PERROR;
  626. break;
  627. case 'n':
  628. ctx->flags |= UCI_FLAG_EXPORT_NAME;
  629. break;
  630. case 'N':
  631. ctx->flags &= ~UCI_FLAG_EXPORT_NAME;
  632. break;
  633. case 'p':
  634. uci_add_delta_path(ctx, optarg);
  635. break;
  636. case 'P':
  637. uci_add_delta_path(ctx, ctx->savedir);
  638. uci_set_savedir(ctx, optarg);
  639. flags |= CLI_FLAG_NOCOMMIT;
  640. break;
  641. case 'q':
  642. flags |= CLI_FLAG_QUIET;
  643. break;
  644. case 'X':
  645. flags &= ~CLI_FLAG_SHOW_EXT;
  646. break;
  647. default:
  648. uci_usage();
  649. return 0;
  650. }
  651. }
  652. if (optind > 1)
  653. argv[optind - 1] = argv[0];
  654. argv += optind - 1;
  655. argc -= optind - 1;
  656. if (argc < 2) {
  657. uci_usage();
  658. return 0;
  659. }
  660. ret = uci_cmd(argc - 1, argv + 1);
  661. if (input != stdin)
  662. fclose(input);
  663. if (ret == 255)
  664. uci_usage();
  665. uci_free_context(ctx);
  666. return ret;
  667. }