/** * Compare 2 keys. * * The returned flags bit array has 1s (differ) or 0s (equal) for each key * meta info compared, that can be logically ORed using @c #keyswitch_t flags. * @link keyswitch_t::KEY_NAME KEY_NAME @endlink, * @link keyswitch_t::KEY_VALUE KEY_VALUE @endlink, * @link keyswitch_t::KEY_OWNER KEY_OWNER @endlink, * @link keyswitch_t::KEY_COMMENT KEY_COMMENT @endlink, * @link keyswitch_t::KEY_UID KEY_UID @endlink, * @link keyswitch_t::KEY_GID KEY_GID @endlink, * @link keyswitch_t::KEY_MODE KEY_MODE @endlink and * * @par A very simple example would be * @code Key *key1, *key; uint32_t changes; // omited key1 and key2 initialization and manipulation changes=keyCompare(key1,key2); if (changes == 0) printf("key1 and key2 are identicall\n"); if (changes & KEY_VALUE) printf("key1 and key2 have different values\n"); if (changes & KEY_UID) printf("key1 and key2 have different UID\n"); * * @endcode * * * @par Example of very powerful specific Key lookup in a KeySet: * @code KDB *handle = kdbOpen(); KeySet *ks=ksNew(0); Key *base = keyNew ("user/sw/MyApp/something", KEY_END); Key *current; uint32_t match; uint32_t interests; kdbGetByName(handle, ks, "user/sw/MyApp", 0); // we are interested only in key type and access permissions interests=(KEY_TYPE | KEY_MODE); ksRewind(ks); // put cursor in the beginning while ((curren=ksNext(ks))) { match=keyCompare(current,base); if ((~match & interests) == interests) printf("Key %s has same type and permissions of base key",keyName(current)); // continue walking in the KeySet.... } // now we want same name and/or value interests=(KEY_NAME | KEY_VALUE); // we don't really need ksRewind(), since previous loop achieved end of KeySet ksRewind(ks); while ((current=ksNext(ks))) { match=keyCompare(current,base); if ((~match & interests) == interests) { printf("Key %s has same name, value, and sync status of base key",keyName(current)); } // continue walking in the KeySet.... } keyDel(base); ksDel(ks); kdbClose (handle); * @endcode * * @return a bit array pointing the differences * @param key1 first key * @param key2 second key * @see #keyswitch_t * @ingroup keytest */ keyswitch_t keyCompare(const Key *key1, const Key *key2) { if (!key1 && !key2) return 0; if (!key1 || !key2) return KEY_NULL; keyswitch_t ret=0; ssize_t nsize1 = keyGetNameSize(key1); ssize_t nsize2 = keyGetNameSize(key2); const char *name1 = keyName(key1); const char *name2 = keyName(key2); const char *comment1 = keyComment(key1); const char *comment2 = keyComment(key2); const char *owner1 = keyOwner(key1); const char *owner2 = keyOwner(key2); const void *value1 = keyValue(key1); const void *value2 = keyValue(key2); ssize_t size1 = keyGetValueSize(key1); ssize_t size2 = keyGetValueSize(key2); if (keyGetUID(key1) != keyGetUID(key2)) ret|=KEY_UID; if (keyGetGID(key1) != keyGetGID(key2)) ret|=KEY_GID; if (keyGetMode(key1)!= keyGetMode(key2)) ret|=KEY_MODE; if (nsize1 != nsize2) ret|=KEY_NAME; if (strcmp(name1, name2)) ret|=KEY_NAME; if (strcmp(comment1, comment2)) ret|=KEY_COMMENT; if (strcmp(owner1, owner2)) ret|=KEY_OWNER; if (size1 != size2) ret|=KEY_VALUE; if (memcmp(value1, value2, size1)) ret|=KEY_VALUE; return ret; }
void test_key () { KeySet * ks; Key * cur; int counter; printf ("Testing Key from xml\n"); ks = ksNew (0, KS_END); exit_if_fail (ksFromXMLfile (ks, srcdir_file ("xmltool/key.xml")) == 0, "ksFromXMLfile(key.xml) failed."); counter = 0; ksRewind (ks); while ((cur = ksNext (ks))) { counter++; /* Prepend key root */ // snprintf(buf, sizeof(buf), "%s/%s", root, keyName(cur)); // keySetName(cur, buf); /* Make tests ... */ // printf ("counter: %d - name: %s - value: %s\n", counter, (char*) keyName(cur), (char*)keyValue(cur)); switch (counter) { case 1: succeed_if (strcmp (keyName (cur), "user/tests/filesys/.HiddenBinaryKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "BinaryValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Binary key with hidden name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 1, "key is inactive"); succeed_if (keyIsBinary (cur) == 1, "key is not binary"); succeed_if (keyGetMode (cur) == 0440, "could not get mode"); break; case 2: succeed_if (strcmp (keyName (cur), "user/tests/filesys/.HiddenDirectoryKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "DirectoryValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Directory key with hidden name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 1, "key is inactive"); succeed_if (keyIsString (cur) == 1, "key is not string"); break; case 3: succeed_if (strcmp (keyName (cur), "user/tests/filesys/.HiddenStringKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "StringValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "String key with hidden name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 1, "key is inactive"); succeed_if (keyIsString (cur) == 1, "key is not string"); succeed_if (keyGetUID (cur) == 0, "could not get uid value"); succeed_if (keyGetGID (cur) == 20, "could not get gid value"); break; case 4: succeed_if (strcmp (keyName (cur), "user/tests/filesys/PerfectBinaryKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "BinaryValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Binary key with standard name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsBinary (cur) == 1, "key is not binary"); succeed_if (keyGetGID (cur) == 40, "could not get gid value"); break; case 5: succeed_if (strcmp (keyName (cur), "user/tests/filesys/PerfectDirectoryKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "DirectoryValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Directory key with standard name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsString (cur) == 1, "key is not string"); break; case 6: succeed_if (strcmp (keyName (cur), "user/tests/filesys/PerfectStringKey") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "StringValue") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "String key with\nstandard name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsString (cur) == 1, "key is not string"); succeed_if (keyGetUID (cur) == 20, "could not get uid value"); break; case 7: succeed_if (strcmp (keyName (cur), "user/tests/filesys/Ug.ly:Bin@a€ry Key") == 0, "wrong name"); succeed_if (keyValue (cur) == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Binary key with ugly name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsBinary (cur) == 1, "key is not binary"); succeed_if (keyGetMode (cur) == 230, "could not get mode"); // <key type="binary" basename="Ug.ly:Bin@a€ry Key"><comment>Binary key with ugly name</comment></key> break; case 8: succeed_if (strcmp (keyName (cur), "user/tests/filesys/Ug.ly:Dir@ect€ory Key") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "Directory with ugly name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsString (cur) == 1, "key is not string"); // <key type="directory" basename="Ug.ly:Dir@ect€ory Key"><comment>Directory with ugly name</comment></key> break; case 9: succeed_if (strcmp (keyName (cur), "user/tests/filesys/Ug.ly:St@ri€n.g Key") == 0, "wrong name"); succeed_if (strcmp (keyValue (cur), "With a string value") == 0, "value not correct"); succeed_if (strcmp (keyComment (cur), "string key with ugly name") == 0, "comment not correct"); succeed_if (keyIsInactive (cur) == 0, "key is active"); succeed_if (keyIsString (cur) == 1, "key is not string"); // <key type="string" basename="Ug.ly:St@ri€n.g Key" value="With a string value"><comment>string key with ugly // name</comment></key> break; } } ksDel (ks); }
/** * Output every information of a single key depending on options. * * The format is not very strict and only intend to be read * by human eyes for debugging purposes. Don't rely on the * format in your applications. * * @param k the key object to work with * @param stream the file pointer where to send the stream * @param options see text above * @see ksOutput() * @retval 1 on success * @retval -1 on allocation errors * @ingroup stream */ int keyOutput (const Key * k, FILE *stream, option_t options) { time_t t; size_t s; char * tmc; char * str; size_t c; char * com; size_t n; char * nam; n = keyGetNameSize (k); if (n>1) { nam = (char*) elektraMalloc (n); if (nam == NULL) return -1; keyGetName (k, nam, n); fprintf(stream,"Name[%d]: %s : ", (int)n, nam); elektraFree (nam); } s = keyGetValueSize (k); if (options & KEY_VALUE && s>1) { str = (char*) elektraMalloc (s); if (str == NULL) return -1; if (keyIsBinary(k)) { /* char * bin; bin = (char*) elektraMalloc (s*3+1); keyGetBinary(k, str, s); kdbbEncode (str, s, bin); elektraFree (bin); */ keyGetBinary (k, str, s); fprintf(stream,"Binary[%d]: %s : ", (int)s, str); } else { keyGetString (k, str, s); fprintf(stream,"String[%d]: %s : ", (int)s, str); } elektraFree (str); } c = keyGetCommentSize (k); if (options & KEY_COMMENT && c>1) { com = (char*) elektraMalloc (c); if (com == NULL) return -1; keyGetComment (k, com, c); fprintf(stream,"Comment[%d]: %s : ", (int)c, com); elektraFree (com); } if (options & KDB_O_SHOWMETA) fprintf(stream," : "); if (options & KEY_UID) fprintf(stream,"UID: %d : ", (int)keyGetUID (k)); if (options & KEY_GID) fprintf(stream,"GID: %d : ", (int)keyGetGID (k)); if (options & KEY_MODE) fprintf(stream,"Mode: %o : ", (int)keyGetMode (k)); if (options & KEY_ATIME) { t=keyGetATime(k); tmc = ctime (& t); tmc[24] = '\0'; fprintf(stream,"ATime: %s : ", tmc); } if (options & KEY_MTIME) { t=keyGetMTime(k); tmc = ctime (& t); tmc[24] = '\0'; fprintf(stream,"MTime: %s : ", tmc); } if (options & KEY_CTIME) { t=keyGetCTime(k); tmc = ctime (& t); tmc[24] = '\0'; fprintf(stream,"CTime: %s : ", tmc); } if (options & KDB_O_SHOWFLAGS) { if (!(options & KDB_O_SHOWMETA)) fprintf(stream, " "); fprintf (stream,"Flags: "); if (keyIsBinary(k)) fprintf(stream,"b"); if (keyIsString(k)) fprintf(stream,"s"); if (keyIsInactive(k)) fprintf(stream,"i"); if (keyNeedSync(k)) fprintf(stream,"s"); } fprintf(stream,"\n"); return 1; }
/** * Same as keyToStream() but tries to strip @p parentSize bytes from * @p key name if it matches @p parent . * * Taking the example from keyToStream(), if @p parent is * @c "system/sw/xorg", the generated string is of the form: * @verbatim <key basename="Monitor/Monitor0/Name" type="string" uid="root" gid="root" mode="0600"> <value>Samsung TFT panel</value> <comment>My monitor</comment> </key>@endverbatim * * It usefull to produce more human readable XML output of a key when * it is being represented in a context that defines the parent key name. * For example: * * @verbatim <keyset parent="user/sw"> <key basename="kdbedit"..../> <key basename="phototools"..../> <key basename="myapp"..../> </keyset>@endverbatim * * In the bove example, each @p @<key@> entry was generated by a call to * keyToStreamBasename() having @c "user/sw" as @p parent . * * This method is used when ksToStream() is called with * KDBOption::KDB_O_HIER option. * * @param key the key object to work with * @param stream the FILE where to send the stream * @param parentSize the maximum size of @p parent that will be used. * If 0, the entire @p parent will be used. * @param parent the string (or part of it, defined by @p parentSize ) that * will be used to strip from the key name. * @param options Some #option_t ORed: * - @p option_t::KDB_O_NUMBERS \n * Do not convert UID and GID into user and group names * - @p option_t::KDB_O_CONDENSED \n * Less human readable, more condensed output * - @p option_t::KDB_O_FULLNAME \n * The @p user keys are exported with their full names (including * user domains) * * @return number of bytes written to output */ ssize_t keyToStreamBasename(const Key *key, FILE *stream, const char *parent, const size_t parentSize, option_t options) { ssize_t written=0; char buffer[KDB_MAX_PATH_LENGTH]; /* Write key name */ if (parent) { /* some logic to see if we should print only the relative basename */ int found; size_t skip=parentSize ? parentSize : elektraStrLen(parent)-1; found=memcmp(parent,key->key,skip); if (found == 0) { while (*(key->key+skip) == KDB_PATH_SEPARATOR) ++skip; if (*(key->key+skip) != 0) /* we don't want a null basename */ written+=fprintf(stream,"<key basename=\"%s\"", key->key+skip); } } if (written == 0) { /* no "<key basename=..." was written so far */ if (options & KDB_O_FULLNAME) { keyGetFullName(key,buffer,sizeof(buffer)); written+=fprintf(stream,"<key name=\"%s\"", buffer); } else written+=fprintf(stream,"<key name=\"%s\"", key->key); } /* Key type TODO: xml schema does not output type if (options & KDB_O_NUMBERS) { written+=fprintf(stream," type=\"%d\"", key->type); } else { buffer[0]=0; if (key->type & KEY_TYPE_DIR) written+=fprintf(stream, " isdir=\"yes\""); if (key->type & KEY_TYPE_REMOVE) written+=fprintf(stream, " isremove=\"yes\""); if (key->type & KEY_TYPE_BINARY) written+=fprintf(stream, " isbinary=\"yes\""); } */ if (keyGetUID (key) != (uid_t)-1) written+=fprintf(stream," uid=\"%d\"", (int)keyGetUID (key)); if (keyGetGID (key) != (gid_t)-1) written+=fprintf(stream," gid=\"%d\"", (int)keyGetGID (key)); if (keyGetMode(key) != KDB_FILE_MODE) { written+=fprintf(stream," mode=\"0%o\"", keyGetMode(key)); } if (!key->data.v && !keyComment(key)) { /* no data AND no comment */ written+=fprintf(stream,"/>"); if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n\n"); return written; /* end of <key/> */ } else { if (key->data.v) { if ((key->dataSize <= 16) && keyIsString (key) && /*TODO: is this for string?*/ !strchr(key->data.c,'\n')) { /* we'll use a "value" attribute instead of a <value> node, for readability, so the cut size will be 16, which is the maximum size of an IPv4 address */ if (options & KDB_O_CONDENSED) written+=fprintf(stream," "); else written+=fprintf(stream,"\n\t"); written+=fprintf(stream,"value=\"%s\"",key->data.c); if (keyComment(key)) written+=fprintf(stream,">\n"); else { written+=fprintf(stream,"/>"); if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n"); return written; } } else { /* value is bigger than 16 bytes: deserves own <value> */ written+=fprintf(stream,">"); if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n\n "); written+=fprintf(stream,"<value>"); if (keyIsString(key)) { /*TODO: is this for string?*/ written+=fprintf(stream,"<![CDATA["); fflush(stream); /* must chop ending \\0 */ written+=fwrite(key->data.v,sizeof(char),key->dataSize-1,stream); written+=fprintf(stream,"]]>"); } else { /* TODO Binary values char *encoded=elektraMalloc(3*key->dataSize); size_t encodedSize; written+=fprintf(stream,"\n"); encodedSize=kdbbEncode(key->data.c,key->dataSize,encoded); fflush(stream); written+=fwrite(encoded,sizeof(char),encodedSize,stream); elektraFree (encoded); written+=fprintf(stream,"\n"); */ } /* fflush(stream); */ written+=fprintf(stream,"</value>"); } } else { /* we have no data */ if (keyComment(key)) { written+=fprintf(stream,">"); if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n"); } else { written+=fprintf(stream,"/>"); if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n\n"); return written; } } } if (!(options & KDB_O_CONDENSED)) { written+=fprintf(stream,"\n"); if (keyComment(key)) written+=fprintf(stream," "); } if (keyComment(key)) { written+=fprintf(stream,"<comment><![CDATA[%s]]></comment>", keyComment(key)); if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n"); } written+=fprintf(stream,"</key>"); if (!(options & KDB_O_CONDENSED)) written+=fprintf(stream,"\n\n"); return written; }