Fix skipping directories in uci_list_config_files
[project/uci.git] / libuci.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 some common code for the uci library
17  */
18
19 #define _GNU_SOURCE
20 #include <sys/types.h>
21 #include <stdbool.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <dlfcn.h>
26 #include <glob.h>
27 #include "uci.h"
28
29 static const char *uci_errstr[] = {
30         [UCI_OK] =            "Success",
31         [UCI_ERR_MEM] =       "Out of memory",
32         [UCI_ERR_INVAL] =     "Invalid argument",
33         [UCI_ERR_NOTFOUND] =  "Entry not found",
34         [UCI_ERR_IO] =        "I/O error",
35         [UCI_ERR_PARSE] =     "Parse error",
36         [UCI_ERR_DUPLICATE] = "Duplicate entry",
37         [UCI_ERR_UNKNOWN] =   "Unknown error",
38 };
39
40 #include "uci_internal.h"
41 #include "list.c"
42
43 __private const char *uci_confdir = UCI_CONFDIR;
44 __private const char *uci_savedir = UCI_SAVEDIR;
45
46 /* exported functions */
47 struct uci_context *uci_alloc_context(void)
48 {
49         struct uci_context *ctx;
50
51         ctx = (struct uci_context *) malloc(sizeof(struct uci_context));
52         if (!ctx)
53                 return NULL;
54
55         memset(ctx, 0, sizeof(struct uci_context));
56         uci_list_init(&ctx->root);
57         uci_list_init(&ctx->delta_path);
58         uci_list_init(&ctx->backends);
59         ctx->flags = UCI_FLAG_STRICT | UCI_FLAG_SAVED_DELTA;
60
61         ctx->confdir = (char *) uci_confdir;
62         ctx->savedir = (char *) uci_savedir;
63         uci_add_delta_path(ctx, uci_savedir);
64
65         uci_list_add(&ctx->backends, &uci_file_backend.e.list);
66         ctx->backend = &uci_file_backend;
67
68         return ctx;
69 }
70
71 void uci_free_context(struct uci_context *ctx)
72 {
73         struct uci_element *e, *tmp;
74
75         if (ctx->confdir != uci_confdir)
76                 free(ctx->confdir);
77         if (ctx->savedir != uci_savedir)
78                 free(ctx->savedir);
79
80         uci_cleanup(ctx);
81         UCI_TRAP_SAVE(ctx, ignore);
82         uci_foreach_element_safe(&ctx->root, tmp, e) {
83                 struct uci_package *p = uci_to_package(e);
84                 uci_free_package(&p);
85         }
86         uci_foreach_element_safe(&ctx->delta_path, tmp, e) {
87                 uci_free_element(e);
88         }
89         UCI_TRAP_RESTORE(ctx);
90         free(ctx);
91
92 ignore:
93         return;
94 }
95
96 int uci_set_confdir(struct uci_context *ctx, const char *dir)
97 {
98         char *cdir;
99
100         UCI_HANDLE_ERR(ctx);
101         UCI_ASSERT(ctx, dir != NULL);
102
103         cdir = uci_strdup(ctx, dir);
104         if (ctx->confdir != uci_confdir)
105                 free(ctx->confdir);
106         ctx->confdir = cdir;
107         return 0;
108 }
109
110 __private void uci_cleanup(struct uci_context *ctx)
111 {
112         struct uci_parse_context *pctx;
113
114         if (ctx->buf) {
115                 free(ctx->buf);
116                 ctx->buf = NULL;
117                 ctx->bufsz = 0;
118         }
119
120         pctx = ctx->pctx;
121         if (!pctx)
122                 return;
123
124         ctx->pctx = NULL;
125         if (pctx->package)
126                 uci_free_package(&pctx->package);
127
128         if (pctx->buf)
129                 free(pctx->buf);
130
131         free(pctx);
132 }
133
134 void
135 uci_perror(struct uci_context *ctx, const char *str)
136 {
137         uci_get_errorstr(ctx, NULL, str);
138 }
139
140 void
141 uci_get_errorstr(struct uci_context *ctx, char **dest, const char *prefix)
142 {
143         static char error_info[128];
144         int err;
145         const char *format =
146                 "%s%s" /* prefix */
147                 "%s%s" /* function */
148                 "%s" /* error */
149                 "%s"; /* details */
150
151         error_info[0] = 0;
152
153         if (!ctx)
154                 err = UCI_ERR_INVAL;
155         else
156                 err = ctx->err;
157
158         if ((err < 0) || (err >= UCI_ERR_LAST))
159                 err = UCI_ERR_UNKNOWN;
160
161         switch (err) {
162         case UCI_ERR_PARSE:
163                 if (ctx->pctx) {
164                         snprintf(error_info, sizeof(error_info) - 1, " (%s) at line %d, byte %d", (ctx->pctx->reason ? ctx->pctx->reason : "unknown"), ctx->pctx->line, ctx->pctx->byte);
165                         break;
166                 }
167                 break;
168         default:
169                 break;
170         }
171         if (dest) {
172                 err = asprintf(dest, format,
173                         (prefix ? prefix : ""), (prefix ? ": " : ""),
174                         (ctx && ctx->func ? ctx->func : ""), (ctx && ctx->func ? ": " : ""),
175                         uci_errstr[err],
176                         error_info);
177                 if (err < 0)
178                         *dest = NULL;
179         } else {
180                 strcat(error_info, "\n");
181                 fprintf(stderr, format,
182                         (prefix ? prefix : ""), (prefix ? ": " : ""),
183                         (ctx && ctx->func ? ctx->func : ""), (ctx && ctx->func ? ": " : ""),
184                         uci_errstr[err],
185                         error_info);
186         }
187 }
188
189 int uci_list_configs(struct uci_context *ctx, char ***list)
190 {
191         UCI_HANDLE_ERR(ctx);
192         UCI_ASSERT(ctx, list != NULL);
193         UCI_ASSERT(ctx, ctx->backend && ctx->backend->list_configs);
194         *list = ctx->backend->list_configs(ctx);
195         return 0;
196 }
197
198 int uci_commit(struct uci_context *ctx, struct uci_package **package, bool overwrite)
199 {
200         struct uci_package *p;
201         UCI_HANDLE_ERR(ctx);
202         UCI_ASSERT(ctx, package != NULL);
203         p = *package;
204         UCI_ASSERT(ctx, p != NULL);
205         UCI_ASSERT(ctx, p->backend && p->backend->commit);
206         p->backend->commit(ctx, package, overwrite);
207         return 0;
208 }
209
210 int uci_load(struct uci_context *ctx, const char *name, struct uci_package **package)
211 {
212         struct uci_package *p;
213
214         UCI_HANDLE_ERR(ctx);
215         UCI_ASSERT(ctx, ctx->backend && ctx->backend->load);
216         p = ctx->backend->load(ctx, name);
217         if (package)
218                 *package = p;
219
220         return 0;
221 }
222
223 int uci_set_backend(struct uci_context *ctx, const char *name)
224 {
225         struct uci_element *e;
226
227         UCI_HANDLE_ERR(ctx);
228         UCI_ASSERT(ctx, name != NULL);
229         e = uci_lookup_list(&ctx->backends, name);
230         if (!e)
231                 UCI_THROW(ctx, UCI_ERR_NOTFOUND);
232         ctx->backend = uci_to_backend(e);
233         return 0;
234 }