tests: add more test coverage for `uci show' command.
[project/uci.git] / file.c
1 /*
2  * libuci - Library 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 Lesser General Public License version 2.1
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 Lesser General Public License for more details.
13  */
14
15 /*
16  * This file contains the code for parsing uci config files
17  */
18
19 #define _GNU_SOURCE
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/file.h>
23 #include <stdbool.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <glob.h>
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include "uci.h"
33 #include "uci_internal.h"
34
35 #define LINEBUF 32
36 #define LINEBUF_MAX     4096
37
38 /*
39  * Fetch a new line from the input stream and resize buffer if necessary
40  */
41 __private void uci_getln(struct uci_context *ctx, int offset)
42 {
43         struct uci_parse_context *pctx = ctx->pctx;
44         char *p;
45         int ofs;
46
47         if (pctx->buf == NULL) {
48                 pctx->buf = uci_malloc(ctx, LINEBUF);
49                 pctx->bufsz = LINEBUF;
50         }
51         /* It takes 2 slots for fgets to read 1 char. */
52         if (offset >= pctx->bufsz - 1) {
53                 pctx->bufsz *= 2;
54                 pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz);
55         }
56
57         ofs = offset;
58         do {
59                 p = &pctx->buf[ofs];
60                 p[0] = 0;
61
62                 p = fgets(p, pctx->bufsz - ofs, pctx->file);
63                 if (!p || !*p)
64                         return;
65
66                 ofs += strlen(p);
67                 if (pctx->buf[ofs - 1] == '\n') {
68                         pctx->line++;
69                         return;
70                 }
71
72                 if (pctx->bufsz > LINEBUF_MAX/2)
73                         uci_parse_error(ctx, "line too long");
74
75                 pctx->bufsz *= 2;
76                 pctx->buf = uci_realloc(ctx, pctx->buf, pctx->bufsz);
77         } while (1);
78 }
79
80 /*
81  * parse a character escaped by '\'
82  * returns true if the escaped character is to be parsed
83  * returns false if the escaped character is to be ignored
84  */
85 static bool parse_backslash(struct uci_context *ctx)
86 {
87         struct uci_parse_context *pctx = ctx->pctx;
88
89         /* skip backslash */
90         pctx->pos += 1;
91
92         /* undecoded backslash at the end of line, fetch the next line */
93         if (!pctx_cur_char(pctx) ||
94             pctx_cur_char(pctx) == '\n' ||
95             (pctx_cur_char(pctx) == '\r' &&
96              pctx_char(pctx, pctx_pos(pctx) + 1) == '\n' &&
97              !pctx_char(pctx, pctx_pos(pctx) + 2))) {
98                 uci_getln(ctx, pctx->pos);
99                 return false;
100         }
101
102         /* FIXME: decode escaped char, necessary? */
103         return true;
104 }
105
106 /*
107  * move the string pointer forward until a non-whitespace character or
108  * EOL is reached
109  */
110 static void skip_whitespace(struct uci_context *ctx)
111 {
112         struct uci_parse_context *pctx = ctx->pctx;
113
114         while (pctx_cur_char(pctx) && isspace(pctx_cur_char(pctx)))
115                 pctx->pos += 1;
116 }
117
118 static inline void addc(struct uci_context *ctx, int *pos_dest, int *pos_src)
119 {
120         struct uci_parse_context *pctx = ctx->pctx;
121
122         pctx_char(pctx, *pos_dest) = pctx_char(pctx, *pos_src);
123         *pos_dest += 1;
124         *pos_src += 1;
125 }
126
127 /*
128  * parse a double quoted string argument from the command line
129  */
130 static void parse_double_quote(struct uci_context *ctx, int *target)
131 {
132         struct uci_parse_context *pctx = ctx->pctx;
133         char c;
134
135         /* skip quote character */
136         pctx->pos += 1;
137
138         while (1) {
139                 c = pctx_cur_char(pctx);
140                 switch(c) {
141                 case '"':
142                         pctx->pos += 1;
143                         return;
144                 case 0:
145                         /* Multi-line str value */
146                         uci_getln(ctx, pctx->pos);
147                         if (!pctx_cur_char(pctx)) {
148                                 uci_parse_error(ctx, "EOF with unterminated \"");
149                         }
150                         break;
151                 case '\\':
152                         if (!parse_backslash(ctx))
153                                 continue;
154                         /* fall through */
155                 default:
156                         addc(ctx, target, &pctx->pos);
157                         break;
158                 }
159         }
160 }
161
162 /*
163  * parse a single quoted string argument from the command line
164  */
165 static void parse_single_quote(struct uci_context *ctx, int *target)
166 {
167         struct uci_parse_context *pctx = ctx->pctx;
168         char c;
169         /* skip quote character */
170         pctx->pos += 1;
171
172         while (1) {
173                 c = pctx_cur_char(pctx);
174                 switch(c) {
175                 case '\'':
176                         pctx->pos += 1;
177                         return;
178                 case 0:
179                         /* Multi-line str value */
180                         uci_getln(ctx, pctx->pos);
181                         if (!pctx_cur_char(pctx))
182                                 uci_parse_error(ctx, "EOF with unterminated '");
183
184                         break;
185                 default:
186                         addc(ctx, target, &pctx->pos);
187                 }
188         }
189 }
190
191 /*
192  * parse a string from the command line and detect the quoting style
193  */
194 static void parse_str(struct uci_context *ctx, int *target)
195 {
196         struct uci_parse_context *pctx = ctx->pctx;
197         bool next = true;
198         do {
199                 switch(pctx_cur_char(pctx)) {
200                 case '\'':
201                         parse_single_quote(ctx, target);
202                         break;
203                 case '"':
204                         parse_double_quote(ctx, target);
205                         break;
206                 case '#':
207                         pctx_cur_char(pctx) = 0;
208                         /* fall through */
209                 case 0:
210                         goto done;
211                 case ';':
212                         next = false;
213                         goto done;
214                 case '\\':
215                         if (!parse_backslash(ctx))
216                                 continue;
217                         /* fall through */
218                 default:
219                         addc(ctx, target, &pctx->pos);
220                         break;
221                 }
222         } while (pctx_cur_char(pctx) && !isspace(pctx_cur_char(pctx)));
223 done:
224
225         /*
226          * if the string was unquoted and we've stopped at a whitespace
227          * character, skip to the next one, because the whitespace will
228          * be overwritten by a null byte here
229          */
230         if (pctx_cur_char(pctx) && next)
231                 pctx->pos += 1;
232
233         /* terminate the parsed string */
234         pctx_char(pctx, *target) = 0;
235 }
236
237 /*
238  * extract the next argument from the command line
239  */
240 static int next_arg(struct uci_context *ctx, bool required, bool name)
241 {
242         struct uci_parse_context *pctx = ctx->pctx;
243         int val, ptr;
244
245         skip_whitespace(ctx);
246         val = ptr = pctx_pos(pctx);
247         if (pctx_cur_char(pctx) == ';') {
248                 pctx_cur_char(pctx) = 0;
249                 pctx->pos += 1;
250         } else {
251                 parse_str(ctx, &ptr);
252         }
253         if (!pctx_char(pctx, val)) {
254                 if (required)
255                         uci_parse_error(ctx, "insufficient arguments");
256                 goto done;
257         }
258
259         if (name && !uci_validate_name(pctx_str(pctx, val)))
260                 uci_parse_error(ctx, "invalid character in name field");
261
262 done:
263         return val;
264 }
265
266 int uci_parse_argument(struct uci_context *ctx, FILE *stream, char **str, char **result)
267 {
268         int ofs_result;
269
270         UCI_HANDLE_ERR(ctx);
271         UCI_ASSERT(ctx, str != NULL);
272         UCI_ASSERT(ctx, result != NULL);
273
274         if (ctx->pctx && (ctx->pctx->file != stream))
275                 uci_cleanup(ctx);
276
277         if (!ctx->pctx)
278                 uci_alloc_parse_context(ctx);
279
280         ctx->pctx->file = stream;
281
282         if (!*str) {
283                 uci_getln(ctx, 0);
284                 *str = ctx->pctx->buf;
285         } else {
286                 UCI_ASSERT(ctx, ctx->pctx->pos == *str - ctx->pctx->buf);
287         }
288
289         /*FIXME do we need to skip empty lines? */
290         ofs_result = next_arg(ctx, false, false);
291         *result = pctx_str(ctx->pctx, ofs_result);
292         *str = pctx_cur_str(ctx->pctx);
293
294         return 0;
295 }
296
297 static int
298 uci_fill_ptr(struct uci_context *ctx, struct uci_ptr *ptr, struct uci_element *e)
299 {
300         UCI_ASSERT(ctx, ptr != NULL);
301         UCI_ASSERT(ctx, e != NULL);
302
303         memset(ptr, 0, sizeof(struct uci_ptr));
304         switch(e->type) {
305         case UCI_TYPE_OPTION:
306                 ptr->o = uci_to_option(e);
307                 goto fill_option;
308         case UCI_TYPE_SECTION:
309                 ptr->s = uci_to_section(e);
310                 goto fill_section;
311         case UCI_TYPE_PACKAGE:
312                 ptr->p = uci_to_package(e);
313                 goto fill_package;
314         default:
315                 UCI_THROW(ctx, UCI_ERR_INVAL);
316         }
317
318 fill_option:
319         ptr->option = ptr->o->e.name;
320         ptr->s = ptr->o->section;
321 fill_section:
322         ptr->section = ptr->s->e.name;
323         ptr->p = ptr->s->package;
324 fill_package:
325         ptr->package = ptr->p->e.name;
326
327         ptr->flags |= UCI_LOOKUP_DONE;
328
329         return 0;
330 }
331
332
333
334 /*
335  * verify that the end of the line or command is reached.
336  * throw an error if extra arguments are given on the command line
337  */
338 static void assert_eol(struct uci_context *ctx)
339 {
340         char *tmp;
341         int ofs_tmp;
342
343         skip_whitespace(ctx);
344         ofs_tmp = next_arg(ctx, false, false);
345         tmp = pctx_str(ctx->pctx, ofs_tmp);
346         if (*tmp && (ctx->flags & UCI_FLAG_STRICT))
347                 uci_parse_error(ctx, "too many arguments");
348 }
349
350 /*
351  * switch to a different config, either triggered by uci_load, or by a
352  * 'package <...>' statement in the import file
353  */
354 static void uci_switch_config(struct uci_context *ctx)
355 {
356         struct uci_parse_context *pctx;
357         struct uci_element *e;
358         const char *name;
359
360         pctx = ctx->pctx;
361         name = pctx->name;
362
363         /* add the last config to main config file list */
364         if (pctx->package) {
365                 pctx->package->backend = ctx->backend;
366                 uci_list_add(&ctx->root, &pctx->package->e.list);
367
368                 pctx->package = NULL;
369                 pctx->section = NULL;
370         }
371
372         if (!name)
373                 return;
374
375         /*
376          * if an older config under the same name exists, unload it
377          * ignore errors here, e.g. if the config was not found
378          */
379         e = uci_lookup_list(&ctx->root, name);
380         if (e)
381                 UCI_THROW(ctx, UCI_ERR_DUPLICATE);
382         pctx->package = uci_alloc_package(ctx, name);
383 }
384
385 /*
386  * parse the 'package' uci command (next config package)
387  */
388 static void uci_parse_package(struct uci_context *ctx, bool single)
389 {
390         struct uci_parse_context *pctx = ctx->pctx;
391         int ofs_name;
392         char *name;
393
394         /* command string null-terminated by strtok */
395         pctx->pos += strlen(pctx_cur_str(pctx)) + 1;
396
397         ofs_name = next_arg(ctx, true, true);
398         name = pctx_str(pctx, ofs_name);
399         assert_eol(ctx);
400         if (single)
401                 return;
402
403         ctx->pctx->name = name;
404         uci_switch_config(ctx);
405 }
406
407 /*
408  * parse the 'config' uci command (open a section)
409  */
410 static void uci_parse_config(struct uci_context *ctx)
411 {
412         struct uci_parse_context *pctx = ctx->pctx;
413         struct uci_element *e;
414         struct uci_ptr ptr;
415         int ofs_name, ofs_type;
416         char *name;
417         char *type;
418
419         uci_fixup_section(ctx, ctx->pctx->section);
420         if (!ctx->pctx->package) {
421                 if (!ctx->pctx->name)
422                         uci_parse_error(ctx, "attempting to import a file without a package name");
423
424                 uci_switch_config(ctx);
425         }
426
427         /* command string null-terminated by strtok */
428         pctx->pos += strlen(pctx_cur_str(pctx)) + 1;
429
430         ofs_type = next_arg(ctx, true, false);
431         type = pctx_str(pctx, ofs_type);
432         if (!uci_validate_type(type))
433                 uci_parse_error(ctx, "invalid character in type field");
434
435         ofs_name = next_arg(ctx, false, true);
436         type = pctx_str(pctx, ofs_type);
437         name = pctx_str(pctx, ofs_name);
438         assert_eol(ctx);
439
440         if (!name || !name[0]) {
441                 ctx->internal = !pctx->merge;
442                 UCI_NESTED(uci_add_section, ctx, pctx->package, type, &pctx->section);
443         } else {
444                 uci_fill_ptr(ctx, &ptr, &pctx->package->e);
445                 e = uci_lookup_list(&pctx->package->sections, name);
446                 if (e)
447                         ptr.s = uci_to_section(e);
448                 ptr.section = name;
449                 ptr.value = type;
450
451                 ctx->internal = !pctx->merge;
452                 UCI_NESTED(uci_set, ctx, &ptr);
453                 pctx->section = uci_to_section(ptr.last);
454         }
455 }
456
457 /*
458  * parse the 'option' uci command (open a value)
459  */
460 static void uci_parse_option(struct uci_context *ctx, bool list)
461 {
462         struct uci_parse_context *pctx = ctx->pctx;
463         struct uci_element *e;
464         struct uci_ptr ptr;
465         int ofs_name, ofs_value;
466         char *name = NULL;
467         char *value = NULL;
468
469         if (!pctx->section)
470                 uci_parse_error(ctx, "option/list command found before the first section");
471
472         /* command string null-terminated by strtok */
473         pctx->pos += strlen(pctx_cur_str(pctx)) + 1;
474
475         ofs_name = next_arg(ctx, true, true);
476         ofs_value = next_arg(ctx, false, false);
477         name = pctx_str(pctx, ofs_name);
478         value = pctx_str(pctx, ofs_value);
479         assert_eol(ctx);
480
481         uci_fill_ptr(ctx, &ptr, &pctx->section->e);
482         e = uci_lookup_list(&pctx->section->options, name);
483         if (e)
484                 ptr.o = uci_to_option(e);
485         ptr.option = name;
486         ptr.value = value;
487
488         ctx->internal = !pctx->merge;
489         if (list)
490                 UCI_NESTED(uci_add_list, ctx, &ptr);
491         else
492                 UCI_NESTED(uci_set, ctx, &ptr);
493 }
494
495 /*
496  * parse a complete input line, split up combined commands by ';'
497  */
498 static void uci_parse_line(struct uci_context *ctx, bool single)
499 {
500         struct uci_parse_context *pctx = ctx->pctx;
501         char *word;
502
503         /* Skip whitespace characters at the start of line */
504         skip_whitespace(ctx);
505         do {
506                 word = strtok(pctx_cur_str(pctx), " \t");
507                 if (!word)
508                         return;
509
510                 switch(word[0]) {
511                         case 0:
512                         case '#':
513                                 return;
514                         case 'p':
515                                 if ((word[1] == 0) || !strcmp(word + 1, "ackage"))
516                                         uci_parse_package(ctx, single);
517                                 else
518                                         goto invalid;
519                                 break;
520                         case 'c':
521                                 if ((word[1] == 0) || !strcmp(word + 1, "onfig"))
522                                         uci_parse_config(ctx);
523                                 else
524                                         goto invalid;
525                                 break;
526                         case 'o':
527                                 if ((word[1] == 0) || !strcmp(word + 1, "ption"))
528                                         uci_parse_option(ctx, false);
529                                 else
530                                         goto invalid;
531                                 break;
532                         case 'l':
533                                 if ((word[1] == 0) || !strcmp(word + 1, "ist"))
534                                         uci_parse_option(ctx, true);
535                                 else
536                                         goto invalid;
537                                 break;
538                         default:
539                                 goto invalid;
540                 }
541                 continue;
542 invalid:
543                 uci_parse_error(ctx, "invalid command");
544         } while (1);
545 }
546
547 /* max number of characters that escaping adds to the string */
548 #define UCI_QUOTE_ESCAPE        "'\\''"
549
550 /*
551  * escape an uci string for export
552  */
553 static const char *uci_escape(struct uci_context *ctx, const char *str)
554 {
555         const char *end;
556         int ofs = 0;
557
558         if (!ctx->buf) {
559                 ctx->bufsz = LINEBUF;
560                 ctx->buf = malloc(LINEBUF);
561
562                 if (!ctx->buf)
563                         return str;
564         }
565
566         while (1) {
567                 int len;
568
569                 end = strchr(str, '\'');
570                 if (!end)
571                         end = str + strlen(str);
572                 len = end - str;
573
574                 /* make sure that we have enough room in the buffer */
575                 while (ofs + len + sizeof(UCI_QUOTE_ESCAPE) + 1 > ctx->bufsz) {
576                         ctx->bufsz *= 2;
577                         ctx->buf = uci_realloc(ctx, ctx->buf, ctx->bufsz);
578                 }
579
580                 /* copy the string until the character before the quote */
581                 memcpy(&ctx->buf[ofs], str, len);
582                 ofs += len;
583
584                 /* end of string? return the buffer */
585                 if (*end == 0)
586                         break;
587
588                 memcpy(&ctx->buf[ofs], UCI_QUOTE_ESCAPE, sizeof(UCI_QUOTE_ESCAPE));
589                 ofs += strlen(&ctx->buf[ofs]);
590                 str = end + 1;
591         }
592
593         ctx->buf[ofs] = 0;
594         return ctx->buf;
595 }
596
597 /*
598  * export a single config package to a file stream
599  */
600 static void uci_export_package(struct uci_package *p, FILE *stream, bool header)
601 {
602         struct uci_context *ctx = p->ctx;
603         struct uci_element *s, *o, *i;
604
605         if (header)
606                 fprintf(stream, "package %s\n", uci_escape(ctx, p->e.name));
607         uci_foreach_element(&p->sections, s) {
608                 struct uci_section *sec = uci_to_section(s);
609                 fprintf(stream, "\nconfig %s", uci_escape(ctx, sec->type));
610                 if (!sec->anonymous || (ctx->flags & UCI_FLAG_EXPORT_NAME))
611                         fprintf(stream, " '%s'", uci_escape(ctx, sec->e.name));
612                 fprintf(stream, "\n");
613                 uci_foreach_element(&sec->options, o) {
614                         struct uci_option *opt = uci_to_option(o);
615                         switch(opt->type) {
616                         case UCI_TYPE_STRING:
617                                 fprintf(stream, "\toption %s", uci_escape(ctx, opt->e.name));
618                                 fprintf(stream, " '%s'\n", uci_escape(ctx, opt->v.string));
619                                 break;
620                         case UCI_TYPE_LIST:
621                                 uci_foreach_element(&opt->v.list, i) {
622                                         fprintf(stream, "\tlist %s", uci_escape(ctx, opt->e.name));
623                                         fprintf(stream, " '%s'\n", uci_escape(ctx, i->name));
624                                 }
625                                 break;
626                         default:
627                                 fprintf(stream, "\t# unknown type for option '%s'\n", uci_escape(ctx, opt->e.name));
628                                 break;
629                         }
630                 }
631         }
632         fprintf(stream, "\n");
633 }
634
635 int uci_export(struct uci_context *ctx, FILE *stream, struct uci_package *package, bool header)
636 {
637         struct uci_element *e;
638
639         UCI_HANDLE_ERR(ctx);
640         UCI_ASSERT(ctx, stream != NULL);
641
642         if (package)
643                 uci_export_package(package, stream, header);
644         else {
645                 uci_foreach_element(&ctx->root, e) {
646                         uci_export_package(uci_to_package(e), stream, header);
647                 }
648         }
649
650         return 0;
651 }
652
653 int uci_import(struct uci_context *ctx, FILE *stream, const char *name, struct uci_package **package, bool single)
654 {
655         struct uci_parse_context *pctx;
656         UCI_HANDLE_ERR(ctx);
657
658         /* make sure no memory from previous parse attempts is leaked */
659         uci_cleanup(ctx);
660
661         uci_alloc_parse_context(ctx);
662         pctx = ctx->pctx;
663         pctx->file = stream;
664         if (package && *package && single) {
665                 pctx->package = *package;
666                 pctx->merge = true;
667         }
668
669         /*
670          * If 'name' was supplied, assume that the supplied stream does not contain
671          * the appropriate 'package <name>' string to specify the config name
672          * NB: the config file can still override the package name
673          */
674         if (name) {
675                 UCI_ASSERT(ctx, uci_validate_package(name));
676                 pctx->name = name;
677         }
678
679         while (!feof(pctx->file)) {
680                 pctx->pos = 0;
681                 uci_getln(ctx, 0);
682                 UCI_TRAP_SAVE(ctx, error);
683                 if (pctx->buf[0])
684                         uci_parse_line(ctx, single);
685                 UCI_TRAP_RESTORE(ctx);
686                 continue;
687 error:
688                 if (ctx->flags & UCI_FLAG_PERROR)
689                         uci_perror(ctx, NULL);
690                 if ((ctx->err != UCI_ERR_PARSE) ||
691                         (ctx->flags & UCI_FLAG_STRICT))
692                         UCI_THROW(ctx, ctx->err);
693         }
694
695         uci_fixup_section(ctx, ctx->pctx->section);
696         if (!pctx->package && name)
697                 uci_switch_config(ctx);
698         if (package)
699                 *package = pctx->package;
700         if (pctx->merge)
701                 pctx->package = NULL;
702
703         pctx->name = NULL;
704         uci_switch_config(ctx);
705
706         /* no error happened, we can get rid of the parser context now */
707         uci_cleanup(ctx);
708
709         return 0;
710 }
711
712
713 static char *uci_config_path(struct uci_context *ctx, const char *name)
714 {
715         char *filename;
716
717         UCI_ASSERT(ctx, uci_validate_package(name));
718         filename = uci_malloc(ctx, strlen(name) + strlen(ctx->confdir) + 2);
719         sprintf(filename, "%s/%s", ctx->confdir, name);
720
721         return filename;
722 }
723
724 static void uci_file_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite)
725 {
726         struct uci_package *p = *package;
727         FILE *f1, *f2 = NULL;
728         char *name = NULL;
729         char *path = NULL;
730         char *filename = NULL;
731         struct stat statbuf;
732         bool do_rename = false;
733
734         if (!p->path) {
735                 if (overwrite)
736                         p->path = uci_config_path(ctx, p->e.name);
737                 else
738                         UCI_THROW(ctx, UCI_ERR_INVAL);
739         }
740
741         if ((asprintf(&filename, "%s/.%s.uci-XXXXXX", ctx->confdir, p->e.name) < 0) || !filename)
742                 UCI_THROW(ctx, UCI_ERR_MEM);
743
744         if (!mktemp(filename))
745                 *filename = 0;
746
747         if (!*filename) {
748                 free(filename);
749                 UCI_THROW(ctx, UCI_ERR_IO);
750         }
751
752         if ((stat(filename, &statbuf) == 0) && ((statbuf.st_mode & S_IFMT) != S_IFREG))
753                 UCI_THROW(ctx, UCI_ERR_IO);
754
755         /* open the config file for writing now, so that it is locked */
756         f1 = uci_open_stream(ctx, p->path, NULL, SEEK_SET, true, true);
757
758         /* flush unsaved changes and reload from delta file */
759         UCI_TRAP_SAVE(ctx, done);
760         if (p->has_delta) {
761                 if (!overwrite) {
762                         name = uci_strdup(ctx, p->e.name);
763                         path = uci_strdup(ctx, p->path);
764                         /* dump our own changes to the delta file */
765                         if (!uci_list_empty(&p->delta))
766                                 UCI_INTERNAL(uci_save, ctx, p);
767
768                         /*
769                          * other processes might have modified the config
770                          * as well. dump and reload
771                          */
772                         uci_free_package(&p);
773                         uci_cleanup(ctx);
774                         UCI_INTERNAL(uci_import, ctx, f1, name, &p, true);
775
776                         p->path = path;
777                         p->has_delta = true;
778                         *package = p;
779
780                         /* freed together with the uci_package */
781                         path = NULL;
782                 }
783
784                 /* flush delta */
785                 if (!uci_load_delta(ctx, p, true))
786                         goto done;
787         }
788
789         f2 = uci_open_stream(ctx, filename, p->path, SEEK_SET, true, true);
790         uci_export(ctx, f2, p, false);
791
792         fflush(f2);
793         fsync(fileno(f2));
794         uci_close_stream(f2);
795
796         do_rename = true;
797
798         UCI_TRAP_RESTORE(ctx);
799
800 done:
801         free(name);
802         free(path);
803         uci_close_stream(f1);
804         if (do_rename && rename(filename, p->path)) {
805                 unlink(filename);
806                 UCI_THROW(ctx, UCI_ERR_IO);
807         }
808         free(filename);
809         sync();
810         if (ctx->err)
811                 UCI_THROW(ctx, ctx->err);
812 }
813
814
815 /*
816  * This function returns the filename by returning the string
817  * after the last '/' character. By checking for a non-'\0'
818  * character afterwards, directories are ignored (glob marks
819  * those with a trailing '/'
820  */
821 static inline char *get_filename(char *path)
822 {
823         char *p;
824
825         p = strrchr(path, '/');
826         p++;
827         if (!*p)
828                 return NULL;
829         return p;
830 }
831
832 static char **uci_list_config_files(struct uci_context *ctx)
833 {
834         char **configs;
835         glob_t globbuf;
836         int size, i;
837         char *buf;
838         char *dir;
839
840         dir = uci_malloc(ctx, strlen(ctx->confdir) + 1 + sizeof("/*"));
841         sprintf(dir, "%s/*", ctx->confdir);
842         if (glob(dir, GLOB_MARK, NULL, &globbuf) != 0) {
843                 free(dir);
844                 UCI_THROW(ctx, UCI_ERR_NOTFOUND);
845         }
846
847         size = sizeof(char *) * (globbuf.gl_pathc + 1);
848         for(i = 0; i < globbuf.gl_pathc; i++) {
849                 char *p;
850
851                 p = get_filename(globbuf.gl_pathv[i]);
852                 if (!p)
853                         continue;
854
855                 size += strlen(p) + 1;
856         }
857
858         configs = uci_malloc(ctx, size);
859         buf = (char *) &configs[globbuf.gl_pathc + 1];
860         for(i = 0; i < globbuf.gl_pathc; i++) {
861                 char *p;
862
863                 p = get_filename(globbuf.gl_pathv[i]);
864                 if (!p)
865                         continue;
866
867                 if (!uci_validate_package(p))
868                         continue;
869
870                 configs[i] = buf;
871                 strcpy(buf, p);
872                 buf += strlen(buf) + 1;
873         }
874         free(dir);
875         globfree(&globbuf);
876         return configs;
877 }
878
879 static struct uci_package *uci_file_load(struct uci_context *ctx, const char *name)
880 {
881         struct uci_package *package = NULL;
882         char *filename;
883         bool confdir;
884         FILE *file = NULL;
885
886         switch (name[0]) {
887         case '.':
888                 /* relative path outside of /etc/config */
889                 if (name[1] != '/')
890                         UCI_THROW(ctx, UCI_ERR_NOTFOUND);
891                 /* fall through */
892         case '/':
893                 /* absolute path outside of /etc/config */
894                 filename = uci_strdup(ctx, name);
895                 name = strrchr(name, '/') + 1;
896                 confdir = false;
897                 break;
898         default:
899                 /* config in /etc/config */
900                 filename = uci_config_path(ctx, name);
901                 confdir = true;
902                 break;
903         }
904
905         UCI_TRAP_SAVE(ctx, done);
906         file = uci_open_stream(ctx, filename, NULL, SEEK_SET, false, false);
907         ctx->err = 0;
908         UCI_INTERNAL(uci_import, ctx, file, name, &package, true);
909         UCI_TRAP_RESTORE(ctx);
910
911         if (package) {
912                 package->path = filename;
913                 package->has_delta = confdir;
914                 uci_load_delta(ctx, package, false);
915         }
916
917 done:
918         uci_close_stream(file);
919         if (ctx->err) {
920                 free(filename);
921                 UCI_THROW(ctx, ctx->err);
922         }
923         return package;
924 }
925
926 __private UCI_BACKEND(uci_file_backend, "file",
927         .load = uci_file_load,
928         .commit = uci_file_commit,
929         .list_configs = uci_list_config_files,
930 );