1 /***************************************************************************
2 stream.c - Streaming Methods
4 begin : Sat Nov 23 2007
5 copyright : (C) 2007 by Markus Raab
6 email : elektra@markus-raab.org
7 ***************************************************************************/
9 /***************************************************************************
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the BSD License (revised). *
14 ***************************************************************************/
23 #error need unistd for getuid and getgid
43 #include <kdbbackend.h>
47 #define ksToStream libelektratools_LTX_ksToStream
48 #define ksOutput libelektratools_LTX_ksOutput
49 #define ksGenerate libelektratools_LTX_ksGenerate
50 #define keyToStream libelektratools_LTX_keyToStream
51 #define keyOutput libelektratools_LTX_keyOutput
52 #define keyGenerate libelektratools_LTX_keyGenerate
54 #endif /* ELEKTRA_STATIC */
59 * @defgroup stream Streaming
60 * @brief Methods to output, generate and toXML Keys and Keysets.
62 * Here are some functions that are in a separate library because they
63 * depend on non-basic libraries as libxml. Link against kdbtools library if your
64 * program won't be installed in /bin, or is not essential in early boot
67 * It is also possible to have a dynamic linkage, see the
68 * sourcecode from kdb-tool or testing framework how to achieve
71 * Output prints keys line per line, meaned to be read by humans.
75 * Generate prints keys and keysets as C-Structs, meaned to be
76 * read by a C-Compiler.
80 * toXML prints keys and keysets as XML, meaned to be used
98 /*********************************************
99 * Textual XML methods *
100 *********************************************/
104 * Prints an XML representation of the key.
106 * String generated is of the form:
108 <key name="system/sw/xorg/Monitor/Monitor0/Name"
109 type="string" uid="root" gid="root" mode="0660">
111 <value>Samsung TFT panel</value>
112 <comment>My monitor</comment>
118 <key parent="system/sw/xorg/Monitor/Monitor0" basename="Name"
119 type="string" uid="root" gid="root" mode="0660">
121 <value>Samsung TFT panel</value>
122 <comment>My monitor</comment>
125 * @param key the key object to work with
126 * @param stream where to write output: a file or stdout
127 * @param options Some #option_t ORed:
128 * - @p option_t::KDB_O_NUMBERS \n
129 * Do not convert UID and GID into user and group names
130 * - @p option_t::KDB_O_CONDENSED \n
131 * Less human readable, more condensed output
132 * - @p option_t::KDB_O_FULLNAME \n
133 * The @p user keys are exported with their full names (including
137 * @return number of bytes written to output
140 ssize_t keyToStream(const Key *key, FILE* stream, option_t options)
142 return keyToStreamBasename(key,stream,0,0,options);
150 * Same as keyToStream() but tries to strip @p parentSize bytes from
151 * @p key name if it matches @p parent .
153 * Taking the example from keyToStream(), if @p parent is
154 * @c "system/sw/xorg", the generated string is of the form:
156 <key basename="Monitor/Monitor0/Name"
157 type="string" uid="root" gid="root" mode="0660">
159 <value>Samsung TFT panel</value>
160 <comment>My monitor</comment>
163 * It usefull to produce more human readable XML output of a key when
164 * it is being represented in a context that defines the parent key name.
168 <keyset parent="user/sw">
169 <key basename="kdbedit"..../>
170 <key basename="phototools"..../>
171 <key basename="myapp"..../>
172 </keyset>@endverbatim
174 * In the bove example, each @p @<key@> entry was generated by a call to
175 * keyToStreamBasename() having @c "user/sw" as @p parent .
177 * This method is used when ksToStream() is called with
178 * KDBOption::KDB_O_HIER option.
180 * @param key the key object to work with
181 * @param stream the FILE where to send the stream
182 * @param parentSize the maximum size of @p parent that will be used.
183 * If 0, the entire @p parent will be used.
184 * @param parent the string (or part of it, defined by @p parentSize ) that
185 * will be used to strip from the key name.
186 * @param options Some #option_t ORed:
187 * - @p option_t::KDB_O_NUMBERS \n
188 * Do not convert UID and GID into user and group names
189 * - @p option_t::KDB_O_CONDENSED \n
190 * Less human readable, more condensed output
191 * - @p option_t::KDB_O_FULLNAME \n
192 * The @p user keys are exported with their full names (including
195 * @return number of bytes written to output
198 ssize_t keyToStreamBasename(const Key *key, FILE *stream, const char *parent,
199 const size_t parentSize, option_t options) {
201 char buffer[MAX_PATH_LENGTH];
203 uid_t uid = getuid();
204 gid_t gid = getgid();
208 /* some logic to see if we should print only the relative basename */
210 size_t skip=parentSize ? parentSize : kdbiStrLen(parent)-1;
212 found=memcmp(parent,key->key,skip);
214 while (*(key->key+skip) == PATH_SEPARATOR) ++skip;
216 if (*(key->key+skip) != 0) /* we don't want a null basename */
217 written+=fprintf(stream,"<key basename=\"%s\"",
222 if (written == 0) { /* no "<key basename=..." was written so far */
223 if (options & KDB_O_FULLNAME) {
224 keyGetFullName(key,buffer,sizeof(buffer));
225 written+=fprintf(stream,"<key name=\"%s\"", buffer);
226 } else written+=fprintf(stream,"<key name=\"%s\"", key->key);
230 if (options & KDB_O_CONDENSED) written+=fprintf(stream," ");
231 else written+=fprintf(stream,"\n ");
236 TODO: xml schema does not output type
237 if (options & KDB_O_NUMBERS) {
238 written+=fprintf(stream," type=\"%d\"", key->type);
242 if (key->type & KEY_TYPE_DIR) written+=fprintf(stream, " isdir=\"yes\"");
243 if (key->type & KEY_TYPE_REMOVE) written+=fprintf(stream, " isremove=\"yes\"");
244 if (key->type & KEY_TYPE_BINARY) written+=fprintf(stream, " isbinary=\"yes\"");
248 if (keyGetUID (key) != uid) written+=fprintf(stream," uid=\"%d\"", (int)keyGetUID (key));
249 if (keyGetGID (key) != gid) written+=fprintf(stream," gid=\"%d\"", (int)keyGetGID (key));
251 #if defined(S_IRWXU) && defined(S_IRWXG) && defined(S_IRWXO)
252 written+=fprintf(stream," mode=\"0%o\"",
253 key->mode & (S_IRWXU|S_IRWXG|S_IRWXO));
255 written+=fprintf(stream," mode=\"0%o\"",
260 if (!key->data && !key->comment) { /* no data AND no comment */
261 written+=fprintf(stream,"/>");
262 if (!(options & KDB_O_CONDENSED))
263 written+=fprintf(stream,"\n\n");
265 return written; /* end of <key/> */
268 if ((key->dataSize <= 16) && keyIsString (key) && /*TODO: is this for string?*/
269 !strchr(key->data,'\n')) {
271 /* we'll use a "value" attribute instead of a <value> node,
272 for readability, so the cut size will be 16, which is
273 the maximum size of an IPv4 address */
275 if (options & KDB_O_CONDENSED) written+=fprintf(stream," ");
276 else written+=fprintf(stream,"\n\t");
278 written+=fprintf(stream,"value=\"%s\"",(char *)key->data);
280 if (key->comment) written+=fprintf(stream,">\n");
282 written+=fprintf(stream,"/>");
283 if (!(options & KDB_O_CONDENSED))
284 written+=fprintf(stream,"\n");
288 } else { /* value is bigger than 16 bytes: deserves own <value> */
289 written+=fprintf(stream,">");
290 if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n\n ");
292 written+=fprintf(stream,"<value>");
293 if (keyIsString(key)) { /*TODO: is this for string?*/
294 written+=fprintf(stream,"<![CDATA[");
296 /* must chop ending \\0 */
297 written+=fwrite(key->data,sizeof(char),key->dataSize-1,stream);
298 written+=fprintf(stream,"]]>");
301 char *encoded=malloc(3*key->dataSize);
304 written+=fprintf(stream,"\n");
305 encodedSize=kdbbEncode(key->data,key->dataSize,encoded);
307 written+=fwrite(encoded,sizeof(char),encodedSize,stream);
309 written+=fprintf(stream,"\n");
311 /* fflush(stream); */
312 written+=fprintf(stream,"</value>");
314 } else { /* we have no data */
316 written+=fprintf(stream,">");
317 if (!(options & KDB_O_CONDENSED))
318 written+=fprintf(stream,"\n");
320 written+=fprintf(stream,"/>");
321 if (!(options & KDB_O_CONDENSED))
322 written+=fprintf(stream,"\n\n");
329 if (!(options & KDB_O_CONDENSED)) {
330 written+=fprintf(stream,"\n");
331 if (key->comment) written+=fprintf(stream," ");
335 written+=fprintf(stream,"<comment><![CDATA[%s]]></comment>", key->comment);
336 if (!(options & KDB_O_CONDENSED))
337 written+=fprintf(stream,"\n");
340 written+=fprintf(stream,"</key>");
342 if (!(options & KDB_O_CONDENSED))
343 written+=fprintf(stream,"\n\n");
349 * Writes to @p stream an XML version of the @p ks object.
351 * String generated is of the form:
354 <key name=...>...</key>
355 <key name=...>...</key>
356 <key name=...>...</key>
361 * or if KDB_O_HIER is used, the form will be:
363 <keyset parent="user/smallest/parent/name">
365 <key basename=...>...</key>
366 <key name=...>...</key> <!-- a key thats not under this keyset's parent -->
367 <key basename=...>...</key>
372 * KDB_O_HEADER will additionally generate a header like:
374 <?xml version="1.0" encoding="UTF-8"?>
375 <!-- Generated by Elektra API. Total of n keys. -->
376 <keyset xmlns="http://www.libelektra.org"
377 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
378 xsi:schemaLocation="http://www.libelektra.org elektra.xsd">
381 * @param ks the KeySet to serialize
382 * @param stream where to write output: a file or stdout
383 * @param options accepted #option_t ORed:
384 * - @p option_t::KDB_O_NUMBERS \n
385 * Do not convert UID and GID into user and group names.
386 * - @p option_t::KDB_O_FULLNAME \n
387 * The @c user keys are exported with their full names (including
389 * - @p option_t::KDB_O_CONDENSED \n
390 * Less human readable, more condensed output.
391 * - @p option_t::KDB_O_XMLHEADERS \n
392 * Exclude the correct XML headers in the output. If not used, the
393 * <?xml?> and schema info inside the <keyset> object will not be generated.
394 * - @p option_t::KDB_O_HIER \n
395 * Will generate a <keyset> node containing a @c parent attribute, and
396 * <key> nodes with a @c basename relative to that @c parent. The @c parent
397 * is calculated by taking the smallest key name in the keyset, so it is a
398 * good idea to have only related keys on the keyset. Otherwise, a valid
399 * consistent XML document still will be generated with regular absolute
400 * @c name attribute for the <key> nodes, due to a
401 * clever keyToStreamBasename() implementation.
404 * @see commandList() for usage example
405 * @return number of bytes written to output, or -1 if some error occurs
407 * @param ks The keyset to output
408 * @param stream the file pointer where to send the stream
409 * @param options see above text
411 ssize_t ksToStream(const KeySet *ks, FILE* stream, option_t options) {
414 char *codeset = "UTF-8";
415 KeySet *cks = ksDup (ks);
417 if (ksNeedSort(cks)) ksSort (cks);
420 if (options & KDB_O_HEADER) {
421 written+=fprintf(stream,"<?xml version=\"1.0\" encoding=\"%s\"?>",
423 if (~options & KDB_O_CONDENSED)
424 written+=fprintf(stream,
425 "\n<!-- Generated by Elektra API. Total of %d keys. -->\n",(int)cks->size);
426 if (~options & KDB_O_CONDENSED)
427 written+=fprintf(stream,"<keyset xmlns=\"http://www.libelektra.org\"\n"
428 "\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
429 "\txsi:schemaLocation=\"http://www.libelektra.org elektra.xsd\"\n");
431 written+=fprintf(stream,"<keyset xmlns=\"http://www.libelektra.org\""
432 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
433 " xsi:schemaLocation=\"http://www.libelektra.org elektra.xsd\"");
434 } else written+=fprintf(stream,"<keyset");
436 if (options & KDB_O_HIER) {
437 char commonParent[MAX_PATH_LENGTH];
439 ksGetCommonParentName(cks,commonParent,sizeof(commonParent));
441 if (commonParent[0]) {
442 written+=fprintf(stream," parent=\"%s\">\n",
445 while ((key=ksNext (cks)) != 0)
446 written+=keyToStreamBasename(key,stream,commonParent,0,options);
448 written+=fprintf(stream,">\n");
450 while ((key=ksNext (cks)) != 0)
451 written+=keyToStream(key,stream,options);
453 } else { /* No KDB_O_HIER*/
454 written+=fprintf(stream,">\n");
456 while ((key=ksNext (cks)) != 0)
457 written+=keyToStream(key,stream,options);
460 written+=fprintf(stream,"</keyset>\n");
466 * Output every information of a single key depending on options.
468 * The format is not very strict and only intend to be read
469 * by human eyes for debugging purposes. Don't rely on the
470 * format in your applications.
472 * @param k the key object to work with
473 * @param stream the file pointer where to send the stream
474 * @param options see text above
476 * @return 1 on success
477 * @return -1 on allocation errors
480 int keyOutput (const Key * k, FILE *stream, option_t options)
493 n = keyGetNameSize (k);
496 nam = (char*) malloc (n);
497 if (nam == NULL) return -1;
498 keyGetName (k, nam, n);
500 fprintf(stream,"Name[%d]: %s : ", (int)n, nam);
505 s = keyGetValueSize (k);
506 if (options & KEY_VALUE && s>1)
508 str = (char*) malloc (s);
509 if (str == NULL) return -1;
514 bin = (char*) malloc (s*3+1);
515 keyGetBinary(k, str, s);
516 kdbbEncode (str, s, bin);
519 keyGetBinary (k, str, s);
520 fprintf(stream,"Binary[%d]: %s : ", (int)s, str);
522 keyGetString (k, str, s);
523 fprintf(stream,"String[%d]: %s : ", (int)s, str);
529 c = keyGetCommentSize (k);
530 if (options & KEY_COMMENT && c>1)
532 com = (char*) malloc (c);
533 if (com == NULL) return -1;
534 keyGetComment (k, com, c);
536 fprintf(stream,"Comment[%d]: %s : ", (int)c, com);
542 if (options & KDB_O_SHOWMETA) fprintf(stream," : ");
543 if (options & KEY_TYPE) fprintf(stream,"Type: %d : ", keyGetType (k));
544 if (options & KEY_UID) fprintf(stream,"UID: %d : ", (int)keyGetUID (k));
545 if (options & KEY_GID) fprintf(stream,"GID: %d : ", (int)keyGetGID (k));
546 if (options & KEY_MODE) fprintf(stream,"Mode: %o : ", (int)keyGetMode (k));
548 if (options & KEY_ATIME)
553 fprintf(stream,"ATime: %s : ", tmc);
556 if (options & KEY_MTIME)
561 fprintf(stream,"MTime: %s : ", tmc);
564 if (options & KEY_CTIME)
569 fprintf(stream,"CTime: %s : ", tmc);
572 if (options & KDB_O_SHOWFLAGS)
574 if (!(options & KDB_O_SHOWMETA)) fprintf(stream, " ");
575 fprintf (stream,"Flags: ");
576 if (keyIsBinary(k)) fprintf(stream,"b");
577 if (keyIsString(k)) fprintf(stream,"s");
578 if (keyIsDir(k)) fprintf(stream,"d");
579 if (keyIsInactive(k)) fprintf(stream,"i");
580 if (keyNeedRemove(k)) fprintf(stream,"r");
581 if (keyNeedSync(k)) fprintf(stream,"s");
584 fprintf(stream,"\n");
590 * Output all information of a keyset.
592 * The format is not very strict and only intend to be read
593 * by human eyes for debugging purposes. Don't rely on the
594 * format in your applications.
596 * Keys are printed line per line with keyOutput().
598 * The same options as keyOutput() are taken and passed
601 * Additional KDB_O_HEADER will print the number of keys
604 * @param ks the keyset to work with
605 * @param stream the file pointer where to send the stream
608 * @return 1 on success
609 * @return -1 on allocation errors
612 int ksOutput(const KeySet *ks, FILE *stream, option_t options)
615 KeySet *cks = ksDup (ks);
618 if (ksNeedSort(cks)) ksSort (cks);
621 if (KDB_O_HEADER & options) {
622 fprintf(stream,"Output keyset of size %d\n", (int)ksGetSize(cks));
624 while ( (key = ksNext(cks)) != NULL)
626 if (options & KDB_O_SHOWINDICES) fprintf(stream, "[%d] ", (int)size);
627 keyOutput (key,stream,options);
636 * Generate a C-Style key and stream it.
638 * This keyset can be used to include as c-code for
639 * applikations using elektra.
641 * @param key the key object to work with
642 * @param stream the file pointer where to send the stream
643 * @param options KDB_O_SHOWINDICES, KDB_O_IGNORE_COMMENT, KDB_O_SHOWINFO
644 * @return 1 on success
647 int keyGenerate(const Key * key, FILE *stream, option_t options)
658 n = keyGetNameSize (key);
661 nam = (char*) malloc (n);
662 if (nam == NULL) return -1;
663 keyGetName (key, nam, n);
664 fprintf(stream,"\n\tkeyNew (\"%s\"", nam);
668 if (keyIsDir(key)) fprintf(stream,"\n\t\t, KEY_DIR");
670 s = keyGetValueSize (key);
673 str = (char*) malloc (s);
674 if (str == NULL) return -1;
675 if (keyIsBinary(key)) keyGetBinary(key, str, s);
676 else keyGetString (key, str, s);
677 fprintf(stream,"\n\t\t, KEY_VALUE, \"%s\"", str);
681 c = keyGetCommentSize (key);
684 com = (char*) malloc (c);
685 if (com == NULL) return -1;
686 keyGetComment (key, com, c);
687 fprintf(stream,"\n\t\t, KEY_COMMENT, \"%s\"", com);
691 if (keyGetType(key) == KEY_TYPE_BINARY) fprintf(stream,"\n\t\t, KEY_TYPE, KEY_TYPE_BINARY");
692 else if (keyGetType(key) == KEY_TYPE_STRING) fprintf(stream,"\n\t\t, KEY_TYPE, KEY_TYPE_STRING");
693 else fprintf(stream,"\n\t\t, KEY_TYPE, %d", keyGetType(key));
695 // if (keyIsInactive(key)) fprintf(stream,"inactive");
697 // if (keyIsSystem(key)) fprintf(stream,"system");
698 // if (keyIsUser(key)) fprintf(stream,"user");
700 if (keyNeedRemove(key)) fprintf(stream, "\n\t\t, KEY_REMOVE");
701 if (keyNeedStat(key)) fprintf(stream, "\n\t\t, KEY_STAT");
703 if (! (keyGetMode(key) == 0664 || (keyGetMode(key) == 0775 && keyIsDir(key))))
705 fprintf(stream,"\n\t\t, KEY_MODE, 0%3o", keyGetMode(key));
708 fprintf(stream,"\n\t, KEY_END)");
710 if (options == 0) return 1; /* dummy to make icc happy */
716 * Generate a C-Style keyset and stream it.
718 * This keyset can be used to include as c-code for
719 * applikations using elektra.
721 * The options takes the same options as kdbGet()
724 * @param ks the keyset to work with
725 * @param stream the file pointer where to send the stream
726 * @param options which keys not to output
727 * @return 1 on success
730 int ksGenerate (const KeySet *ks, FILE *stream, option_t options)
734 KeySet *cks = ksDup (ks);
736 if (ksNeedSort(cks)) ksSort (cks);
739 fprintf(stream,"ksNew( %d ,", (int)ksGetSize(cks)+10);
740 while ((key=ksNext(cks)) != 0)
742 if (options & KDB_O_NODIR) if (key && keyIsDir (key)) continue;
743 if (options & KDB_O_DIRONLY) if (key && !keyIsDir (key)) continue;
744 if (options & KDB_O_INACTIVE) if (key && keyIsInactive (key)) continue;
745 if (options & KDB_O_STATONLY)
747 keySetRaw (key, "", 0);
748 keySetComment (key, "");
753 keyGenerate(key, stream, options);
756 fprintf(stream,"KS_END);\n");