]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libxo/libxo/xo_encoder.c
Add liblutok a lightweight C++ API for lua.
[FreeBSD/FreeBSD.git] / contrib / libxo / libxo / xo_encoder.c
1 /*
2  * Copyright (c) 2015, Juniper Networks, Inc.
3  * All rights reserved.
4  * This SOFTWARE is licensed under the LICENSE provided in the
5  * ../Copyright file. By downloading, installing, copying, or otherwise
6  * using the SOFTWARE, you agree to be bound by the terms of that
7  * LICENSE.
8  * Phil Shafer, August 2015
9  */
10
11 /**
12  * libxo includes a number of fixed encoding styles.  But other
13  * external encoders are need to deal with new encoders.  Rather
14  * than expose a swarm of libxo internals, we create a distinct
15  * API, with a simpler API than we use internally.
16  */
17
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <sys/queue.h>
22 #include <sys/param.h>
23 #include <dlfcn.h>
24
25 #include "xo_config.h"
26 #include "xo.h"
27 #include "xo_encoder.h"
28
29 #ifdef HAVE_DLFCN_H
30 #include <dlfcn.h>
31 #if !defined(HAVE_DLFUNC)
32 #define dlfunc(_p, _n)          dlsym(_p, _n)
33 #endif
34 #else /* HAVE_DLFCN_H */
35 #define dlopen(_n, _f)          NULL /* Fail */
36 #define dlsym(_p, _n)           NULL /* Fail */
37 #define dlfunc(_p, _n)          NULL /* Fail */
38 #endif /* HAVE_DLFCN_H */
39
40 static void xo_encoder_setup (void); /* Forward decl */
41
42 /*
43  * Need a simple string collection
44  */
45 typedef struct xo_string_node_s {
46     TAILQ_ENTRY(xo_string_node_s) xs_link; /* Next string */
47     char xs_data[0];                  /* String data */
48 } xo_string_node_t;
49
50 typedef TAILQ_HEAD(xo_string_list_s, xo_string_node_s) xo_string_list_t;
51
52 static inline void
53 xo_string_list_init (xo_string_list_t *listp)
54 {
55     if (listp->tqh_last == NULL)
56         TAILQ_INIT(listp);
57 }
58
59 static inline xo_string_node_t *
60 xo_string_add (xo_string_list_t *listp, const char *str)
61 {
62     if (listp == NULL || str == NULL)
63         return NULL;
64
65     xo_string_list_init(listp);
66     size_t len = strlen(str);
67     xo_string_node_t *xsp;
68
69     xsp = xo_realloc(NULL, sizeof(*xsp) + len + 1);
70     if (xsp) {
71         memcpy(xsp->xs_data, str, len);
72         xsp->xs_data[len] = '\0';
73         TAILQ_INSERT_TAIL(listp, xsp, xs_link);
74     }
75
76     return xsp;
77 }
78
79 #define XO_STRING_LIST_FOREACH(_xsp, _listp) \
80     xo_string_list_init(_listp); \
81     TAILQ_FOREACH(_xsp, _listp, xs_link)
82
83 static inline void
84 xo_string_list_clean (xo_string_list_t *listp)
85 {
86     xo_string_node_t *xsp;
87
88     xo_string_list_init(listp);
89
90     for (;;) {
91         xsp = TAILQ_FIRST(listp);
92         if (xsp == NULL)
93             break;
94         TAILQ_REMOVE(listp, xsp, xs_link);
95         xo_free(xsp);
96     }
97 }
98
99 static xo_string_list_t xo_encoder_path;
100
101 void
102 xo_encoder_path_add (const char *path)
103 {
104     xo_encoder_setup();
105
106     if (path)
107         xo_string_add(&xo_encoder_path, path);
108 }
109
110 /* ---------------------------------------------------------------------- */
111
112 typedef struct xo_encoder_node_s {
113     TAILQ_ENTRY(xo_encoder_node_s) xe_link; /* Next session */
114     char *xe_name;                      /* Name for this encoder */
115     xo_encoder_func_t xe_handler;       /* Callback function */
116     void *xe_dlhandle;                  /* dlopen handle */
117 } xo_encoder_node_t;
118
119 typedef TAILQ_HEAD(xo_encoder_list_s, xo_encoder_node_s) xo_encoder_list_t;
120
121 #define XO_ENCODER_LIST_FOREACH(_xep, _listp) \
122     xo_encoder_list_init(_listp); \
123     TAILQ_FOREACH(_xep, _listp, xe_link)
124
125 static xo_encoder_list_t xo_encoders;
126
127 static void
128 xo_encoder_list_init (xo_encoder_list_t *listp)
129 {
130     if (listp->tqh_last == NULL)
131         TAILQ_INIT(listp);
132 }
133
134 static xo_encoder_node_t *
135 xo_encoder_list_add (const char *name)
136 {
137     if (name == NULL)
138         return NULL;
139
140     xo_encoder_node_t *xep = xo_realloc(NULL, sizeof(*xep));
141     if (xep) {
142         ssize_t len = strlen(name) + 1;
143         xep->xe_name = xo_realloc(NULL, len);
144         if (xep->xe_name == NULL) {
145             xo_free(xep);
146             return NULL;
147         }
148
149         memcpy(xep->xe_name, name, len);
150
151         TAILQ_INSERT_TAIL(&xo_encoders, xep, xe_link);
152     }
153
154     return xep;
155 }
156
157 void
158 xo_encoders_clean (void)
159 {
160     xo_encoder_node_t *xep;
161
162     xo_encoder_setup();
163
164     for (;;) {
165         xep = TAILQ_FIRST(&xo_encoders);
166         if (xep == NULL)
167             break;
168
169         TAILQ_REMOVE(&xo_encoders, xep, xe_link);
170
171         if (xep->xe_dlhandle)
172             dlclose(xep->xe_dlhandle);
173
174         xo_free(xep);
175     }
176
177     xo_string_list_clean(&xo_encoder_path);
178 }
179
180 static void
181 xo_encoder_setup (void)
182 {
183     static int initted;
184     if (!initted) {
185         initted = 1;
186
187         xo_string_list_init(&xo_encoder_path);
188         xo_encoder_list_init(&xo_encoders);
189
190         xo_encoder_path_add(XO_ENCODERDIR);
191     }
192 }
193
194 static xo_encoder_node_t *
195 xo_encoder_find (const char *name)
196 {
197     xo_encoder_node_t *xep;
198
199     xo_encoder_list_init(&xo_encoders);
200
201     XO_ENCODER_LIST_FOREACH(xep, &xo_encoders) {
202         if (xo_streq(xep->xe_name, name))
203             return xep;
204     }
205
206     return NULL;
207 }
208
209 static xo_encoder_node_t *
210 xo_encoder_discover (const char *name)
211 {
212     void *dlp = NULL;
213     char buf[MAXPATHLEN];
214     xo_string_node_t *xsp;
215     xo_encoder_node_t *xep = NULL;
216
217     XO_STRING_LIST_FOREACH(xsp, &xo_encoder_path) {
218         static const char fmt[] = "%s/%s.enc";
219         char *dir = xsp->xs_data;
220         size_t len = snprintf(buf, sizeof(buf), fmt, dir, name);
221
222         if (len > sizeof(buf))  /* Should not occur */
223             continue;
224
225         dlp = dlopen((const char *) buf, RTLD_NOW);
226         if (dlp)
227             break;
228     }
229
230     if (dlp) {
231         /*
232          * If the library exists, find the initializer function and
233          * call it.
234          */
235         xo_encoder_init_func_t func;
236
237         func = (xo_encoder_init_func_t) dlfunc(dlp, XO_ENCODER_INIT_NAME);
238         if (func) {
239             xo_encoder_init_args_t xei;
240
241             bzero(&xei, sizeof(xei));
242
243             xei.xei_version = XO_ENCODER_VERSION;
244             ssize_t rc = func(&xei);
245             if (rc == 0 && xei.xei_handler) {
246                 xep = xo_encoder_list_add(name);
247                 if (xep) {
248                     xep->xe_handler = xei.xei_handler;
249                     xep->xe_dlhandle = dlp;
250                 }
251             }
252         }
253
254         if (xep == NULL)
255             dlclose(dlp);
256     }
257
258     return xep;
259 }
260
261 void
262 xo_encoder_register (const char *name, xo_encoder_func_t func)
263 {
264     xo_encoder_setup();
265
266     xo_encoder_node_t *xep = xo_encoder_find(name);
267
268     if (xep)                    /* "We alla-ready got one" */
269         return;
270
271     xep = xo_encoder_list_add(name);
272     if (xep)
273         xep->xe_handler = func;
274 }
275
276 void
277 xo_encoder_unregister (const char *name)
278 {
279     xo_encoder_setup();
280
281     xo_encoder_node_t *xep = xo_encoder_find(name);
282     if (xep) {
283         TAILQ_REMOVE(&xo_encoders, xep, xe_link);
284         xo_free(xep);
285     }
286 }
287
288 int
289 xo_encoder_init (xo_handle_t *xop, const char *name)
290 {
291     xo_encoder_setup();
292
293     char opts_char = '\0';
294     const char *col_opts = strchr(name, ':');
295     const char *plus_opts = strchr(name, '+');
296
297     /*
298      * Find the option-separating character (plus or colon) which
299      * appears first in the options string.
300      */
301     const char *opts = (col_opts == NULL) ? plus_opts
302         : (plus_opts == NULL) ? col_opts
303         : (plus_opts < col_opts) ? plus_opts : col_opts;
304
305     if (opts) {
306         opts_char = *opts;
307
308         /* Make a writable copy of the name */
309         size_t len = strlen(name);
310         char *copy = alloca(len + 1);
311         memcpy(copy, name, len);
312         copy[len] = '\0';
313
314         char *opts_copy = copy + (opts - name); /* Move to ':' */
315         *opts_copy++ = '\0';                    /* Trim it off */
316
317         opts = opts_copy;       /* Use copy as options */
318         name = copy;            /* Use trimmed copy as name */
319     }
320
321     /* Can't have names containing '/' or ':' */
322     if (strchr(name, '/') != NULL || strchr(name, ':') != NULL) {
323         xo_failure(xop, "invalid encoder name: %s", name);
324         return -1;
325     }
326
327    /*
328      * First we look on the list of known (registered) encoders.
329      * If we don't find it, we follow the set of paths to find
330      * the encoding library.
331      */
332     xo_encoder_node_t *xep = xo_encoder_find(name);
333     if (xep == NULL) {
334         xep = xo_encoder_discover(name);
335         if (xep == NULL) {
336             xo_failure(xop, "encoder not founde: %s", name);
337             return -1;
338         }
339     }
340
341     xo_set_encoder(xop, xep->xe_handler);
342
343     int rc = xo_encoder_handle(xop, XO_OP_CREATE, name, NULL, 0);
344     if (rc == 0 && opts != NULL) {
345         xo_encoder_op_t op;
346
347         /* Encoder API is limited, so we're stuck with two different options */
348         op = (opts_char == '+') ? XO_OP_OPTIONS_PLUS : XO_OP_OPTIONS;
349         rc = xo_encoder_handle(xop, op, name, opts, 0);
350     }
351
352     return rc;
353 }
354
355 /*
356  * A couple of function varieties here, to allow for multiple
357  * use cases.  This variant is for when the main program knows
358  * its own encoder needs.
359  */
360 xo_handle_t *
361 xo_encoder_create (const char *name, xo_xof_flags_t flags)
362 {
363     xo_handle_t *xop;
364
365     xop = xo_create(XO_STYLE_ENCODER, flags);
366     if (xop) {
367         if (xo_encoder_init(xop, name)) {
368             xo_destroy(xop);
369             xop = NULL;
370         }
371     }
372
373     return xop;
374 }
375
376 int
377 xo_encoder_handle (xo_handle_t *xop, xo_encoder_op_t op,
378                    const char *name, const char *value, xo_xff_flags_t flags)
379 {
380     void *private = xo_get_private(xop);
381     xo_encoder_func_t func = xo_get_encoder(xop);
382
383     if (func == NULL)
384         return -1;
385
386     return func(xop, op, name, value, private, flags);
387 }
388
389 const char *
390 xo_encoder_op_name (xo_encoder_op_t op)
391 {
392     static const char *names[] = {
393         /*  0 */ "unknown",
394         /*  1 */ "create",
395         /*  2 */ "open_container",
396         /*  3 */ "close_container",
397         /*  4 */ "open_list",
398         /*  5 */ "close_list",
399         /*  6 */ "open_leaf_list",
400         /*  7 */ "close_leaf_list",
401         /*  8 */ "open_instance",
402         /*  9 */ "close_instance",
403         /* 10 */ "string",
404         /* 11 */ "content",
405         /* 12 */ "finish",
406         /* 13 */ "flush",
407         /* 14 */ "destroy",
408         /* 15 */ "attr",
409         /* 16 */ "version",
410         /* 17 */ "options",
411     };
412
413     if (op > sizeof(names) / sizeof(names[0]))
414         return "unknown";
415
416     return names[op];
417 }