Line data Source code
1 : /**
2 : * @file
3 : *
4 : * @brief
5 : *
6 : * @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
7 : */
8 :
9 : #include "xmltool.h"
10 :
11 : #ifdef HAVE_KDBCONFIG_H
12 : #include "kdbconfig.h"
13 : #endif
14 :
15 : #include <stdio.h>
16 : #include <stdlib.h>
17 : #include <string.h>
18 : #include <time.h>
19 : #include <unistd.h>
20 :
21 : #include "kdbtools.h"
22 : #include <kdbinternal.h>
23 :
24 :
25 : /**
26 : * @brief Methods to output, generate and toXML Keys and Keysets.
27 : *
28 : * Here are some functions that are in a separate library because they
29 : * depend on non-basic libraries as libxml. Link against kdbtools library if your
30 : * program won't be installed in /bin, or is not essential in early boot
31 : * stages.
32 : *
33 : * It is also possible to have a dynamic linkage, see the
34 : * sourcecode from kdb-tool or testing framework how to achieve
35 : * that.
36 : *
37 : * Output prints keys line per line, meant to be read by humans.
38 : * - keyOutput()
39 : * - ksOutput()
40 : *
41 : * toXML prints keys and keysets as XML, meant to be used
42 : * as exchange format.
43 : * - keyToStream()
44 : * - ksToStream()
45 : *
46 : * To use them:
47 : * @code
48 : #include <kdbtools.h>
49 : * @endcode
50 : *
51 : *
52 : */
53 :
54 :
55 : /*********************************************
56 : * Textual XML methods *
57 : *********************************************/
58 :
59 :
60 : /**
61 : * Prints an XML representation of the key.
62 : *
63 : * String generated is of the form:
64 : * @verbatim
65 : <key name="system:/sw/xorg/Monitor/Monitor0/Name"
66 : type="string" uid="root" gid="root" mode="0600">
67 :
68 : <value>Samsung TFT panel</value>
69 : <comment>My monitor</comment>
70 : </key>@endverbatim
71 :
72 :
73 :
74 : * @verbatim
75 : <key parent="system:/sw/xorg/Monitor/Monitor0" basename="Name"
76 : type="string" uid="root" gid="root" mode="0600">
77 :
78 : <value>Samsung TFT panel</value>
79 : <comment>My monitor</comment>
80 : </key>@endverbatim
81 : *
82 : * @param key the key object to work with
83 : * @param stream where to write output: a file or stdout
84 : * @param options Some #KDBStream ORed:
85 : * - @p KDBStream::KDB_O_NUMBERS \n
86 : * Do not convert UID and GID into user and group names
87 : * - @p KDBStream::KDB_O_CONDENSED \n
88 : * Less human readable, more condensed output
89 : * - @p KDBStream::KDB_O_FULLNAME \n
90 : * The @p user keys are exported with their full names (including
91 : * user domains)
92 : *
93 : * @see ksToStream()
94 : * @return number of bytes written to output
95 : */
96 26 : ssize_t keyToStream (const Key * key, FILE * stream, KDBStream options)
97 : {
98 26 : return keyToStreamBasename (key, stream, 0, 0, options);
99 : }
100 :
101 :
102 : /**
103 : * Same as keyToStream() but tries to strip @p parentSize bytes from
104 : * @p key name if it matches @p parent .
105 : *
106 : * Taking the example from keyToStream(), if @p parent is
107 : * @c "system:/sw/xorg", the generated string is of the form:
108 : * @verbatim
109 : <key basename="Monitor/Monitor0/Name"
110 : type="string" uid="root" gid="root" mode="0600">
111 :
112 : <value>Samsung TFT panel</value>
113 : <comment>My monitor</comment>
114 : </key>@endverbatim
115 : *
116 : * It useful to produce more human readable XML output of a key when
117 : * it is being represented in a context that defines the parent key name.
118 : * For example:
119 : *
120 : * @verbatim
121 : <keyset parent="user:/sw">
122 : <key basename="kdbedit"..../>
123 : <key basename="phototools"..../>
124 : <key basename="myapp"..../>
125 : </keyset>@endverbatim
126 : *
127 : * In the above example, each @p @<key@> entry was generated by a call to
128 : * keyToStreamBasename() having @c "user:/sw" as @p parent .
129 : *
130 : * This method is used when ksToStream() is called with
131 : * KDBOption::KDB_O_HIER option.
132 : *
133 : * @param key the key object to work with
134 : * @param stream the FILE where to send the stream
135 : * @param parentSize the maximum size of @p parent that will be used.
136 : * If 0, the entire @p parent will be used.
137 : * @param parent the string (or part of it, defined by @p parentSize ) that
138 : * will be used to strip from the key name.
139 : * @param options Some #KDBStream ORed:
140 : * - @p KDBStream::KDB_O_NUMBERS \n
141 : * Do not convert UID and GID into user and group names
142 : * - @p KDBStream::KDB_O_CONDENSED \n
143 : * Less human readable, more condensed output
144 : * - @p KDBStream::KDB_O_FULLNAME \n
145 : * The @p user keys are exported with their full names (including
146 : * user domains)
147 : *
148 : * @return number of bytes written to output
149 : */
150 26 : ssize_t keyToStreamBasename (const Key * key, FILE * stream, const char * parent, const size_t parentSize, KDBStream options)
151 : {
152 26 : ssize_t written = 0;
153 :
154 : /* Write key name */
155 26 : if (parent)
156 : {
157 : /* some logic to see if we should print only the relative basename */
158 0 : int found;
159 0 : size_t skip = parentSize ? parentSize : elektraStrLen (parent) - 1;
160 :
161 0 : found = memcmp (parent, key->key, skip);
162 0 : if (found == 0)
163 : {
164 0 : while (*(key->key + skip) == KDB_PATH_SEPARATOR)
165 0 : ++skip;
166 :
167 0 : if (*(key->key + skip) != 0) /* we don't want a null basename */
168 0 : written += fprintf (stream, "<key basename=\"%s\"", key->key + skip);
169 : }
170 : }
171 :
172 0 : if (written == 0)
173 : { /* no "<key basename=..." was written so far */
174 26 : if (options & KDB_O_FULLNAME)
175 : {
176 0 : char buffer[KDB_MAX_PATH_LENGTH];
177 0 : keyGetName (key, buffer, sizeof (buffer));
178 0 : written += fprintf (stream, "<key name=\"%s\"", buffer);
179 : }
180 : else
181 26 : written += fprintf (stream, "<key name=\"%s\"", key->key);
182 : }
183 :
184 26 : if (!key->data.v && !keyComment (key))
185 : { /* no data AND no comment */
186 0 : written += fprintf (stream, "/>");
187 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n\n");
188 :
189 0 : return written; /* end of <key/> */
190 : }
191 : else
192 : {
193 26 : if (key->data.v)
194 : {
195 24 : if ((key->dataSize <= 16) && keyIsString (key) && /*TODO: is this for string?*/
196 20 : !strchr (key->data.c, '\n'))
197 : {
198 :
199 : /* we'll use a "value" attribute instead of a <value> node,
200 : for readability, so the cut size will be 16, which is
201 : the maximum size of an IPv4 address */
202 :
203 20 : if (options & KDB_O_CONDENSED)
204 0 : written += fprintf (stream, " ");
205 : else
206 20 : written += fprintf (stream, "\n\t");
207 :
208 20 : written += fprintf (stream, "value=\"%s\"", key->data.c);
209 :
210 20 : if (keyComment (key))
211 20 : written += fprintf (stream, ">\n");
212 : else
213 : {
214 0 : written += fprintf (stream, "/>");
215 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n");
216 :
217 0 : return written;
218 : }
219 : }
220 : else
221 : { /* value is bigger than 16 bytes: deserves own <value> */
222 4 : written += fprintf (stream, ">");
223 4 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n\n ");
224 :
225 4 : written += fprintf (stream, "<value>");
226 4 : if (keyIsString (key))
227 : { /*TODO: is this for string?*/
228 4 : written += fprintf (stream, "<![CDATA[");
229 4 : fflush (stream);
230 : /* must chop ending \\0 */
231 4 : written += fwrite (key->data.v, sizeof (char), key->dataSize - 1, stream);
232 4 : written += fprintf (stream, "]]>");
233 : }
234 : else
235 : {
236 : /* TODO Binary values
237 : char *encoded=elektraMalloc(3*key->dataSize);
238 : size_t encodedSize;
239 :
240 : written+=fprintf(stream,"\n");
241 : encodedSize=kdbbEncode(key->data.c,key->dataSize,encoded);
242 : fflush(stream);
243 : written+=fwrite(encoded,sizeof(char),encodedSize,stream);
244 : elektraFree (encoded);
245 : written+=fprintf(stream,"\n");
246 : */
247 4 : }
248 : /* fflush(stream); */
249 4 : written += fprintf (stream, "</value>");
250 : }
251 : }
252 : else
253 : { /* we have no data */
254 2 : if (keyComment (key))
255 : {
256 2 : written += fprintf (stream, ">");
257 2 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n");
258 : }
259 : else
260 : {
261 0 : written += fprintf (stream, "/>");
262 0 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n\n");
263 :
264 0 : return written;
265 : }
266 : }
267 : }
268 :
269 26 : if (!(options & KDB_O_CONDENSED))
270 : {
271 26 : written += fprintf (stream, "\n");
272 26 : if (keyComment (key)) written += fprintf (stream, " ");
273 : }
274 :
275 26 : if (keyComment (key))
276 : {
277 26 : written += fprintf (stream, "<comment><![CDATA[%s]]></comment>", keyComment (key));
278 26 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n");
279 : }
280 :
281 26 : written += fprintf (stream, "</key>");
282 :
283 26 : if (!(options & KDB_O_CONDENSED)) written += fprintf (stream, "\n\n");
284 :
285 : return written;
286 : }
287 :
288 : /**
289 : * Writes to @p stream an XML version of the @p ks object.
290 : *
291 : * String generated is of the form:
292 : * @verbatim
293 : <keyset>
294 : <key name=...>...</key>
295 : <key name=...>...</key>
296 : <key name=...>...</key>
297 :
298 : </keyset>
299 : * @endverbatim
300 : *
301 : * or if KDB_O_HIER is used, the form will be:
302 : * @verbatim
303 : <keyset parent="user:/smallest/parent/name">
304 :
305 : <key basename=...>...</key>
306 : <key name=...>...</key> <!-- a key thats not under this keyset's parent -->
307 : <key basename=...>...</key>
308 :
309 : </keyset>
310 : * @endverbatim
311 : *
312 : * KDB_O_HEADER will additionally generate a header like:
313 : * @verbatim
314 : <?xml version="1.0" encoding="UTF-8"?>
315 : <!-- Generated by Elektra API. Total of n keys. -->
316 : <keyset xmlns="https://www.libelektra.org"
317 : xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
318 : xsi:schemaLocation="https://www.libelektra.org elektra.xsd">
319 : * @endverbatim
320 : *
321 : * @param ks the KeySet to serialise
322 : * @param stream where to write output: a file or stdout
323 : * @param options accepted #KDBStream ORed:
324 : * - @p KDBStream::KDB_O_NUMBERS \n
325 : * Do not convert UID and GID into user and group names.
326 : * - @p KDBStream::KDB_O_FULLNAME \n
327 : * The @c user keys are exported with their full names (including
328 : * user domains)
329 : * - @p KDBStream::KDB_O_CONDENSED \n
330 : * Less human readable, more condensed output.
331 : * - @p KDBStream::KDB_O_XMLHEADERS \n
332 : * Exclude the correct XML headers in the output. If not used, the
333 : * <?xml?> and schema info inside the <keyset> object will not be generated.
334 : * - @p KDBStream::KDB_O_HIER \n
335 : * Will generate a <keyset> node containing a @c parent attribute, and
336 : * <key> nodes with a @c basename relative to that @c parent. The @c parent
337 : * is calculated by taking the smallest key name in the keyset, so it is a
338 : * good idea to have only related keys on the keyset. Otherwise, a valid
339 : * consistent XML document still will be generated with regular absolute
340 : * @c name attribute for the <key> nodes, due to a
341 : * clever keyToStreamBasename() implementation.
342 : *
343 : * @see keyToStream()
344 : * @see commandList() for usage example
345 : * @return number of bytes written to output, or -1 if some error occurs
346 : * @param ks The keyset to output
347 : * @param stream the file pointer where to send the stream
348 : * @param options see above text
349 : */
350 21 : ssize_t ksToStream (const KeySet * ks, FILE * stream, KDBStream options)
351 : {
352 21 : size_t written = 0;
353 21 : Key * key = 0;
354 21 : KeySet * cks = ksDup (ks);
355 :
356 21 : ksRewind (cks);
357 :
358 21 : if (options & KDB_O_HEADER)
359 : {
360 21 : written += fprintf (stream, "<?xml version=\"1.0\" encoding=\"%s\"?>", "UTF-8");
361 21 : if (~options & KDB_O_CONDENSED)
362 21 : written += fprintf (stream, "\n<!-- Generated by Elektra API. Total of %d keys. -->\n", (int) cks->size);
363 21 : if (~options & KDB_O_CONDENSED)
364 21 : written += fprintf (stream,
365 : "<keyset xmlns=\"https://www.libelektra.org\"\n"
366 : "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
367 : "\txsi:schemaLocation=\"https://www.libelektra.org elektra.xsd\"\n");
368 : else
369 0 : written += fprintf (stream,
370 : "<keyset xmlns=\"https://www.libelektra.org\""
371 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
372 : " xsi:schemaLocation=\"https://www.libelektra.org elektra.xsd\"");
373 : }
374 : else
375 0 : written += fprintf (stream, "<keyset");
376 :
377 21 : if (options & KDB_O_HIER)
378 : {
379 0 : char commonParent[KDB_MAX_PATH_LENGTH];
380 :
381 0 : ksGetCommonParentName (cks, commonParent, sizeof (commonParent));
382 :
383 0 : if (commonParent[0])
384 : {
385 0 : written += fprintf (stream, " parent=\"%s\">\n", commonParent);
386 0 : ksRewind (cks);
387 0 : while ((key = ksNext (cks)) != 0)
388 0 : written += keyToStreamBasename (key, stream, commonParent, 0, options);
389 : }
390 : else
391 : {
392 0 : written += fprintf (stream, ">\n");
393 0 : ksRewind (cks);
394 0 : while ((key = ksNext (cks)) != 0)
395 0 : written += keyToStream (key, stream, options);
396 : }
397 : }
398 : else
399 : { /* No KDB_O_HIER*/
400 21 : written += fprintf (stream, ">\n");
401 21 : ksRewind (cks);
402 47 : while ((key = ksNext (cks)) != 0)
403 26 : written += keyToStream (key, stream, options);
404 : }
405 :
406 21 : written += fprintf (stream, "</keyset>\n");
407 21 : ksDel (cks);
408 21 : return written;
409 : }
410 :
411 : /**
412 : * Output every information of a single key depending on options.
413 : *
414 : * The format is not very strict and only intend to be read
415 : * by human eyes for debugging purposes. Don't rely on the
416 : * format in your applications.
417 : *
418 : * @param k the key object to work with
419 : * @param stream the file pointer where to send the stream
420 : * @param options see text above
421 : * @see ksOutput()
422 : * @retval 1 on success
423 : * @retval -1 on allocation errors
424 : * @ingroup stream
425 : */
426 0 : int keyOutput (const Key * k, FILE * stream, KDBStream options)
427 : {
428 0 : size_t s, c, n;
429 :
430 0 : n = keyGetNameSize (k);
431 0 : if (n > 1)
432 : {
433 0 : char * nam = (char *) elektraMalloc (n);
434 0 : if (nam == NULL) return -1;
435 0 : keyGetName (k, nam, n);
436 :
437 0 : fprintf (stream, "Name[%d]: %s : ", (int) n, nam);
438 :
439 0 : elektraFree (nam);
440 : }
441 :
442 0 : s = keyGetValueSize (k);
443 0 : if (options & KEY_VALUE && s > 1)
444 : {
445 0 : char * str = (char *) elektraMalloc (s);
446 0 : if (str == NULL) return -1;
447 0 : if (keyIsBinary (k))
448 : {
449 : /*
450 : char * bin;
451 : bin = (char*) elektraMalloc (s*3+1);
452 : keyGetBinary(k, str, s);
453 : kdbbEncode (str, s, bin);
454 : elektraFree (bin);
455 : */
456 0 : keyGetBinary (k, str, s);
457 0 : fprintf (stream, "Binary[%d]: %s : ", (int) s, str);
458 : }
459 : else
460 : {
461 0 : keyGetString (k, str, s);
462 0 : fprintf (stream, "String[%d]: %s : ", (int) s, str);
463 : }
464 :
465 0 : elektraFree (str);
466 : }
467 :
468 0 : c = keyGetCommentSize (k);
469 0 : if (options & KEY_META && c > 1)
470 : {
471 0 : char * com = (char *) elektraMalloc (c);
472 0 : if (com == NULL) return -1;
473 0 : keyGetComment (k, com, c);
474 :
475 0 : fprintf (stream, "Comment[%d]: %s : ", (int) c, com);
476 :
477 0 : elektraFree (com);
478 : }
479 :
480 :
481 0 : if (options & KDB_O_SHOWMETA) fprintf (stream, " : ");
482 :
483 0 : if (options & KDB_O_SHOWFLAGS)
484 : {
485 0 : if (!(options & KDB_O_SHOWMETA)) fprintf (stream, " ");
486 0 : fprintf (stream, "Flags: ");
487 0 : if (keyIsBinary (k)) fprintf (stream, "b");
488 0 : if (keyIsString (k)) fprintf (stream, "s");
489 0 : if (keyNeedSync (k)) fprintf (stream, "s");
490 : }
491 :
492 0 : fprintf (stream, "\n");
493 0 : return 1;
494 : }
495 :
496 :
497 : /**
498 : * Output all information of a keyset.
499 : *
500 : * The format is not very strict and only intend to be read
501 : * by human eyes for debugging purposes. Don't rely on the
502 : * format in your applications.
503 : *
504 : * Keys are printed line per line with keyOutput().
505 : *
506 : * The same options as keyOutput() are taken and passed
507 : * to them.
508 : *
509 : * Additional KDB_O_HEADER will print the number of keys
510 : * as first line.
511 : *
512 : * @param ks the keyset to work with
513 : * @param stream the file pointer where to send the stream
514 : * @param options
515 : * @see keyOutput()
516 : * @retval 1 on success
517 : * @retval -1 on allocation errors
518 : * @ingroup stream
519 : */
520 0 : int ksOutput (const KeySet * ks, FILE * stream, KDBStream options)
521 : {
522 0 : Key * key;
523 0 : KeySet * cks = ksDup (ks);
524 0 : size_t size = 0;
525 :
526 0 : ksRewind (cks);
527 :
528 0 : if (KDB_O_HEADER & options)
529 : {
530 0 : fprintf (stream, "Output keyset of size %d\n", (int) ksGetSize (cks));
531 : }
532 0 : while ((key = ksNext (cks)) != NULL)
533 : {
534 0 : if (options & KDB_O_SHOWINDICES) fprintf (stream, "[%d] ", (int) size);
535 0 : keyOutput (key, stream, options);
536 0 : size++;
537 : }
538 :
539 0 : ksDel (cks);
540 0 : return 1;
541 : }
|