summaryrefslogtreecommitdiff
path: root/uci_internal.h
blob: ff4ee8cbb18c9a3e1813230dab99187fc3698124 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/*
 * libuci - Library 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 Lesser General Public License version 2.1
 * 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 Lesser General Public License for more details.
 */

#ifndef __UCI_INTERNAL_H
#define __UCI_INTERNAL_H

#define __private __attribute__((visibility("hidden")))
#define __public

struct uci_parse_context
{
	/* error context */
	const char *reason;
	int line;
	size_t byte;

	/* private: */
	struct uci_package *package;
	struct uci_section *section;
	bool merge;
	FILE *file;
	const char *name;
	char *buf;
	size_t bufsz;
	size_t buf_filled;
	size_t pos;
};
#define pctx_pos(pctx)		((pctx)->pos)
#define pctx_str(pctx, i)	(&(pctx)->buf[(i)])
#define pctx_cur_str(pctx)	pctx_str(pctx, pctx_pos(pctx))
#define pctx_char(pctx, i)	((pctx)->buf[(i)])
#define pctx_cur_char(pctx)	pctx_char(pctx, pctx_pos(pctx))

#define uci_alloc_element(ctx, type, name, datasize) \
	uci_to_ ## type (uci_alloc_generic(ctx, uci_type_ ## type, name, sizeof(struct uci_ ## type) + datasize))

extern const char *uci_confdir;
extern const char *uci_savedir;

__private void *uci_malloc(struct uci_context *ctx, size_t size);
__private void *uci_realloc(struct uci_context *ctx, void *ptr, size_t size);
__private char *uci_strdup(struct uci_context *ctx, const char *str);
__private bool uci_validate_str(const char *str, bool name, bool package);
__private void uci_add_delta(struct uci_context *ctx, struct uci_list *list, int cmd, const char *section, const char *option, const char *value);
__private void uci_free_delta(struct uci_delta *h);
__private struct uci_package *uci_alloc_package(struct uci_context *ctx, const char *name);

__private FILE *uci_open_stream(struct uci_context *ctx, const char *filename, const char *origfilename, int pos, bool write, bool create);
__private void uci_close_stream(FILE *stream);
__private void uci_getln(struct uci_context *ctx, size_t offset);

__private void uci_parse_error(struct uci_context *ctx, char *reason);
__private void uci_alloc_parse_context(struct uci_context *ctx);

__private void uci_cleanup(struct uci_context *ctx);
__private struct uci_element *uci_lookup_list(struct uci_list *list, const char *name);
__private void uci_free_package(struct uci_package **package);
__private struct uci_element *uci_alloc_generic(struct uci_context *ctx, int type, const char *name, int size);
__private void uci_free_element(struct uci_element *e);
__private struct uci_element *uci_expand_ptr(struct uci_context *ctx, struct uci_ptr *ptr, bool complete);

__private int uci_load_delta(struct uci_context *ctx, struct uci_package *p, bool flush);

static inline bool uci_validate_package(const char *str)
{
	return uci_validate_str(str, false, true);
}

static inline bool uci_validate_type(const char *str)
{
	return uci_validate_str(str, false, false);
}

static inline bool uci_validate_name(const char *str)
{
	return uci_validate_str(str, true, false);
}

/* initialize a list head/item */
static inline void uci_list_init(struct uci_list *ptr)
{
	ptr->prev = ptr;
	ptr->next = ptr;
}

/* inserts a new list entry after a given entry */
static inline void uci_list_insert(struct uci_list *list, struct uci_list *ptr)
{
	list->next->prev = ptr;
	ptr->prev = list;
	ptr->next = list->next;
	list->next = ptr;
}

/* inserts a new list entry at the tail of the list */
static inline void uci_list_add(struct uci_list *head, struct uci_list *ptr)
{
	/* NB: head->prev points at the tail */
	uci_list_insert(head->prev, ptr);
}

static inline void uci_list_del(struct uci_list *ptr)
{
	struct uci_list *next, *prev;

	next = ptr->next;
	prev = ptr->prev;

	prev->next = next;
	next->prev = prev;

	uci_list_init(ptr);
}


extern struct uci_backend uci_file_backend;

#ifdef UCI_PLUGIN_SUPPORT
/**
 * uci_add_backend: add an extra backend
 * @ctx: uci context
 * @name: name of the backend
 *
 * The default backend is "file", which uses /etc/config for config storage
 */
__private int uci_add_backend(struct uci_context *ctx, struct uci_backend *b);

/**
 * uci_add_backend: add an extra backend
 * @ctx: uci context
 * @name: name of the backend
 *
 * The default backend is "file", which uses /etc/config for config storage
 */
__private int uci_del_backend(struct uci_context *ctx, struct uci_backend *b);
#endif

#define UCI_BACKEND(_var, _name, ...)	\
struct uci_backend _var = {		\
	.e.list = {			\
		.next = &_var.e.list,	\
		.prev = &_var.e.list,	\
	},				\
	.e.name = _name,		\
	.e.type = UCI_TYPE_BACKEND,	\
	.ptr = &_var,			\
	__VA_ARGS__			\
}


/*
 * functions for debug and error handling, for internal use only
 */

#ifdef UCI_DEBUG
#define DPRINTF(...) fprintf(stderr, __VA_ARGS__)
#else
#define DPRINTF(...)
#endif

/*
 * throw an uci exception and store the error number
 * in the context.
 */
#define UCI_THROW(ctx, err) do { 	\
	DPRINTF("Exception: %s in %s, %s:%d\n", #err, __func__, __FILE__, __LINE__); \
	longjmp(ctx->trap, err); 	\
} while (0)

/*
 * store the return address for handling exceptions
 * needs to be called in every externally visible library function
 *
 * NB: this does not handle recursion at all. Calling externally visible
 * functions from other uci functions is only allowed at the end of the
 * calling function, or by wrapping the function call in UCI_TRAP_SAVE
 * and UCI_TRAP_RESTORE.
 */
#define UCI_HANDLE_ERR(ctx) do {	\
	DPRINTF("ENTER: %s\n", __func__); \
	int __val = 0;			\
	if (!ctx)			\
		return UCI_ERR_INVAL;	\
	ctx->err = 0;			\
	if (!ctx->internal && !ctx->nested) \
		__val = setjmp(ctx->trap); \
	ctx->internal = false;		\
	ctx->nested = false;		\
	if (__val) {			\
		DPRINTF("LEAVE: %s, ret=%d\n", __func__, __val); \
		ctx->err = __val;	\
		return __val;		\
	}				\
} while (0)

/*
 * In a block enclosed by UCI_TRAP_SAVE and UCI_TRAP_RESTORE, all exceptions
 * are intercepted and redirected to the label specified in 'handler'
 * after UCI_TRAP_RESTORE, or when reaching the 'handler' label, the old
 * exception handler is restored
 */
#define UCI_TRAP_SAVE(ctx, handler) do {   \
	jmp_buf	__old_trap;		\
	int __val;			\
	memcpy(__old_trap, ctx->trap, sizeof(ctx->trap)); \
	__val = setjmp(ctx->trap);	\
	if (__val) {			\
		ctx->err = __val;	\
		memcpy(ctx->trap, __old_trap, sizeof(ctx->trap)); \
		goto handler;		\
	} while(0)
#define UCI_TRAP_RESTORE(ctx)		\
	memcpy(ctx->trap, __old_trap, sizeof(ctx->trap)); \
} while(0)

/**
 * UCI_INTERNAL: Do an internal call of a public API function
 *
 * Sets Exception handling to passthrough mode.
 * Allows API functions to change behavior compared to public use
 */
#define UCI_INTERNAL(func, ctx, ...) do { \
	ctx->internal = true;		\
	func(ctx, __VA_ARGS__);		\
} while (0)

/**
 * UCI_NESTED: Do an normal nested call of a public API function
 *
 * Sets Exception handling to passthrough mode.
 * Allows API functions to change behavior compared to public use
 */
#define UCI_NESTED(func, ctx, ...) do { \
	ctx->nested = true;		\
	func(ctx, __VA_ARGS__);		\
} while (0)


/*
 * check the specified condition.
 * throw an invalid argument exception if it's false
 */
#define UCI_ASSERT(ctx, expr) do {	\
	if (!(expr)) {			\
		DPRINTF("[%s:%d] Assertion failed\n", __FILE__, __LINE__); \
		UCI_THROW(ctx, UCI_ERR_INVAL);	\
	}				\
} while (0)

#endif