LCOV - code coverage report
Current view: top level - src/plugins/xmltool - stream.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 56 139 40.3 %
Date: 2022-05-21 16:19:22 Functions: 3 5 60.0 %

          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             :  *   &lt;?xml?&gt; and schema info inside the &lt;keyset&gt; object will not be generated.
     334             :  * - @p KDBStream::KDB_O_HIER \n
     335             :  *   Will generate a &lt;keyset&gt; node containing a @c parent attribute, and
     336             :  *   &lt;key&gt; 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 &lt;key&gt; 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             : }

Generated by: LCOV version 1.13