1 /***************************************************************************
2 helper.c - Helpers for backends
4 begin : Mon Dec 29 2003
5 copyright : (C) 2003 by Avi Alkalay
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 ***************************************************************************/
20 * @defgroup backendhelper KDB Backends :: Backend Helper for Elektra
21 * @brief Backend helper Methods for Elektra and Backends.
25 * #include <kdbbackend.h>
28 * These backend helper methods provide
29 * functionality commonly used by backends to make backend
30 * development easier and to provide the same behaviour
37 * Locks file for exclusive write mode.
39 * This function will block until all reader
40 * and writer have left the file.
42 * @param f is a valid filedescriptor
43 * @return 0 on success
44 * @return -1 on failure
45 * @ingroup backendhelper
46 * @err sets KDB_ERR_NOLOCK when locking failed
48 int kdbbWriteLock (FILE *f)
54 l.l_type = F_WRLCK; /*Do exclusive Lock*/
55 l.l_start= 0; /*Start at begin*/
56 l.l_whence = SEEK_SET;
57 l.l_len = 0; /*Do it with whole file*/
58 ret = fcntl (fd, F_SETLKW, &l);
66 * Locks file for read mode.
68 * Other processes and threads are allowed to read the
69 * file too simultaneous.
71 * @param f is a valid filedescriptor
72 * @return 0 on success
73 * @return -1 on failure
74 * @ingroup backendhelper
75 * @err sets KDB_ERR_NOLOCK when locking failed
77 int kdbbReadLock (FILE *f)
83 l.l_type = F_WRLCK; /*Do exclusive Lock*/
84 l.l_start= 0; /*Start at begin*/
85 l.l_whence = SEEK_SET;
86 l.l_len = 0; /*Do it with whole file*/
87 ret = fcntl (fd, F_SETLKW, &l);
98 * @param f is a valid filedescriptor
99 * @return 0 on success
100 * @return -1 on failure
101 * @ingroup backendhelper
102 * @err sets KDB_ERR_NOLOCK when locking failed
104 int kdbbUnlock (FILE *f)
110 l.l_type = F_UNLCK; /*Give Lock away*/
111 l.l_start= 0; /*Start at begin*/
112 l.l_whence = SEEK_SET;
113 l.l_len = 0; /*Do it with whole file*/
114 ret = fcntl (fd, F_SETLKW, &l);
124 * Encodes a buffer of data onto hexadecimal ASCII.
126 * The resulting data is made up of pairs of ASCII hex-digits,
127 * space- and newline-separated. This is the counterpart of
130 * The @c returned must allocated prior you call this function and won't
131 * be bigger than 3 times the size of the source @c kdbbDecoded + 1 byte.
134 * @param kdbbDecoded the source buffer.
135 * @param size the size of the source buffer in bytes.
136 * @param returned the preallocated destination for the ASCII-kdbbEncoded data.
137 * @return the amount of bytes used in the resulting kdbbEncoded buffer.
139 * @ingroup backendhelper
141 ssize_t kdbbEncode(void *kdbbDecoded, size_t size, char *returned)
143 char *readCursor=kdbbDecoded;
144 char *writeCursor=returned;
145 int blockStep=4; /* 4 bytes per block */
146 int lineStep=8*blockStep; /* 8 blocks per line */
147 int currentInBlock=0;
153 while ((readCursor-(char *)kdbbDecoded)<size) {
154 sprintf(writeCursor,"%02x",*(unsigned char *)readCursor);
159 if (currentInLine==lineStep) {
160 *writeCursor='\n'; writeCursor++;
164 if (currentInBlock==blockStep) {
165 *writeCursor=' '; writeCursor++;
171 return writeCursor-returned;
176 * UnkdbbEncodes a buffer of ASCII hexadecimal values into a byte stream.
178 * The allowed format for the hexadecimal values is just
179 * a stream of pairs of plain hex-digits, all together or
182 * The @c returned data won't be bigger than half the size of the
183 * source @c kdbbEncoded data.
185 * @param kdbbEncoded the source of ASCII hexadecimal digits.
186 * @param returned preallocated destination for the kdbbDecoded data.
187 * @return the amount of bytes kdbbDecoded
188 * @return -1 on failure
190 * @ingroup backendhelper
192 ssize_t kdbbDecode(char *kdbbEncoded,void *returned)
194 char byteInHexa[5]="0x";
195 char *readCursor=kdbbEncoded;
196 char *writeCursor=returned;
199 if (returned) *(char *)returned=0;
204 while (*readCursor) {
205 if (isspace((int)*readCursor))
210 if (isxdigit((int)*readCursor)) {
212 byteInHexa[2]=readCursor[0];
213 byteInHexa[3]=readCursor[1];
214 converted=strtol(byteInHexa,0,16); /* convert from hexa to a byte */
215 *writeCursor=(unsigned char)converted;
220 /* This is suposed to be a hex-digit stream. But is not, so return. */
221 /*errno=KDB_ERR_TYPEMISMATCH;*/
225 return (long int)writeCursor-(long int)returned;
229 * Checks if UTF-8 conversion is needed in current context.
230 * if nl_langinfo() is not available, no conversion is ever needed.
231 * If iconv usage is disabled there is no need to check if we need to convert.
232 * Furthermore, some systems have nl_langinfo(), but lacks ability to get
233 * CODESET through it.
234 * Look at the comments by the kdbbUTF8Engine() function for more information.
236 * @return 0 if not needed
237 * @return anything else if needed
238 * @ingroup backendhelper
240 int kdbbNeedsUTF8Conversion()
242 #if defined(HAVE_NL_LANGINFO) && defined(HAVE_ICONV) && defined(CODESET)
243 return strcmp(nl_langinfo(CODESET),"UTF-8");
251 * Converts string to (@p direction = @c UTF8_TO) and from
252 * (@p direction = @c UTF8_FROM) UTF-8.
254 * Since Elektra provides portability for key names and string values between
255 * different codesets, you should use this helper in your backend to convert
256 * to and from universal UTF-8 strings, when storing key names, values and
259 * Broken locales in applications can cause problems too. Make sure to load
260 * the environment locales in your application using
262 setlocale (LC_ALL, "");
265 * Otherwise kdbbUTF8Engine will quit with -1 leading that backends return
266 * with error when non-ascii characters appear. Binary values are not effected.
268 * If iconv() or nl_langinfo() is not available on your system, or if iconv()
269 * usage is disabled (--disable-iconv on build time) simply return 0
272 * @param direction must be @c UTF8_TO (convert from current non-UTF-8 to
273 * UTF-8) or @c UTF8_FROM (convert from UTF-8 to current non-UTF-8)
274 * @param string before the call: the string to be converted; after the call:
275 * reallocated to carry the converted string
276 * @param inputOutputByteSize before the call: the size of the string including
277 * leading NULL; after the call: the size of the converted string including
279 * @return 0 on success
280 * @return -1 on failure
281 * @ingroup backendhelper
284 int kdbbUTF8Engine(int direction, char **string, size_t *inputOutputByteSize)
286 /* Current solution is not very complete.
287 * Iconv might well be available when a usable nl_langinfo is not.
288 * In this case we it should be possible to determine charset through other means
289 * See http://www.cl.cam.ac.uk/~mgk25/unicode.html#activate for more info on a possible solution */
291 #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
292 char *currentCharset=0;
294 char *readCursor, *writeCursor;
298 if (kdbbNeedsUTF8Conversion() && *inputOutputByteSize) currentCharset=nl_langinfo(CODESET);
301 if (direction==UTF8_TO) converter=iconv_open("UTF-8",currentCharset);
302 else converter=iconv_open(currentCharset,"UTF-8");
304 if (converter == (iconv_t)(-1)) return -1;
306 /* work with worst case, when all chars are wide */
307 bufferSize=*inputOutputByteSize * 4;
308 converted=malloc(bufferSize);
309 if (!converted) return -1;
312 writeCursor=converted;
313 /* On some systems and with libiconv, arg1 is const char **.
314 * ICONV_CONST is defined by configure if the system needs this */
316 (ICONV_CONST char **)&readCursor,inputOutputByteSize,
317 &writeCursor,&bufferSize) == (size_t)(-1)) {
319 iconv_close(converter);
323 /* calculate the UTF-8 string byte size, that will be returned */
324 *inputOutputByteSize=writeCursor-converted;
325 /* store the current kdbbDecoded string for future free */
327 /* allocate an optimal size area to store the converted string */
328 *string=malloc(*inputOutputByteSize);
329 /* copy all that matters for returning */
330 memcpy(*string,converted,*inputOutputByteSize);
331 /* release memory used by passed string */
333 /* release buffer memory */
335 /* release the conversor engine */
336 iconv_close(converter);
346 * Encode '/', '\', '%', '+', ' ' char following
347 * RFC 2396 or copy char untouched if different.
349 * @param c Char to kdbbEncode
350 * @param buffer string wich will contain kdbbEncoded char
351 * @param bufSize Size of the buffer
352 * @return: Size of the kdbbEncoded string if success or -1
353 * if error * (then buffer is untouched)
354 * @ingroup backendhelper
355 * @see kdbiDecodeChar
357 * NOTE: No '\\0' is added at the end of buffer.
360 int kdbbEncodeChar(char c, char *buffer, size_t bufSize)
364 if ( bufSize >= (3*sizeof(char)) ) {
365 memcpy(buffer, "%25", sizeof("%25"));
366 return (3*sizeof(char));
371 if ( bufSize >= (3*sizeof(char)) ) {
372 memcpy(buffer, "%2B", sizeof("%2B"));
373 return (3*sizeof(char));
378 if ( bufSize >= 1*sizeof(char) ) {
380 return (1*sizeof(char));
385 if ( bufSize >= (3*sizeof(char)) ) {
386 memcpy(buffer, "%2F", sizeof("%2F"));
387 return (3*sizeof(char));
392 if ( bufSize >= (3*sizeof(char)) ) {
393 memcpy(buffer, "%5C", sizeof("%5C"));
394 return (3*sizeof(char));
399 if ( bufSize >= (1*sizeof(char)) ) {
401 return (1*sizeof(char));
412 * Decode one char from %25, %2B, %2F, %2C following
413 * RFC 2396 or copy char untouched if different.
415 * @param from String containing sequence to decode
416 * @param into Decoded char
417 * @return: Positive size of byte read from "from" for decoding
418 * the sequence if sucess or -1 if error (into untouched)
419 * @ingroup backendhelper
420 * @see kdbbEncodeChar
422 * NOTE: No '\\0' is added at the end of buffer.
425 int kdbbDecodeChar(const char *from, char *into)
429 if ( strlen(from) >= (3*sizeof(char)) ) {
431 case '5': *into = '%'; break;
432 case 'B': *into = '+'; break;
433 case 'F': *into = '/'; break;
434 case 'C': *into = '\\'; break;
440 return (3*sizeof(char));
446 return (1*sizeof(char));
450 return (1*sizeof(char));
457 * Translate a relative file name to a key name
460 * @param string Filename
461 * @param buffer decoded keyName
462 * @param bufSize Size of buffer
463 * @return 0 on success
464 * @return -1 on failure
465 * @ingroup backendhelper
466 * @see kdbbKeyNameToRelativeFilename
469 int kdbbFilenameToKeyName(const char *string, char *buffer, int bufSize)
474 while (*string != '\0')
476 if (bufSize <= sizeof(char))
478 /*errno = KDB_ERR_TOOLONG;*/
482 if ( *string == PATH_SEPARATOR ) {
483 /* Translate PATH_SEPARATOR into KEY_SEPARATOR */
484 *(buffer++) = KEY_SEPARATOR;
485 bufSize -= sizeof(char);
489 if ( (j = kdbbDecodeChar(string, &decoded)) != -1 ) {
491 *(buffer++) = decoded;
492 bufSize -= sizeof(char);
495 /*errno = KDB_ERR_CONVERT;*/
506 /**Calculates the keyname out of a relative filename.
508 * @param handle The kdb handle to work with
509 * @param forFilename needs to be the a null terminated string containing the relative filename
510 * @param parentKey is the key above the key which will be returned
511 * @param returned The proper keyname and owner will be stored in returned. A valid key must be passed.
512 * @return number of bytes written to the buffer, or 0 on error
513 * @ingroup backendhelper
514 * @return length of returned string on success
515 * @return -1 on failure
516 * @see kdbbKeyNameToRelativeFilename()
518 ssize_t kdbbGetFullKeyName (KDB *handle, const char *forFilename, const Key *parentKey, Key *returned)
521 char *transformedName=0;
524 /* Next 2 ifs are required to transform filename from UTF-8 */
525 transformedName = malloc(size=kdbiStrLen(forFilename));
526 strcpy(transformedName,forFilename);
528 if (kdbbUTF8Engine(UTF8_FROM,&transformedName,&size)) {
529 free(transformedName);
530 /*errno = KDB_ERR_CONVERT;*/
534 /* Translate from filename -> keyname and concat it into name */
535 name = (char *) malloc(size*3 + keyGetNameSize(parentKey));
536 strcpy (name, keyName(parentKey));
537 name[keyGetNameSize(parentKey)-1] = '/';
538 kdbbFilenameToKeyName(transformedName, name+keyGetNameSize(parentKey), size*3);
540 /* Now set the name and owner */
541 keySetName (returned, name);
542 keySetOwner(returned, keyOwner(parentKey));
544 free(transformedName);
550 * Translate a key name to a relative file name
553 * @param string Keyname
554 * @param buffer kdbbEncoded filename
555 * @param bufSize Size of buffer
556 * @return Number of byte written in buffer on success,
557 * @return -1 on failure
558 * @ingroup backendhelper
559 * @see kdbbKeyNameToRelativeFilename
562 int kdbbKeyNameToRelativeFilename(const char *string, char *buffer, size_t bufSize)
568 while (*string != '\0')
570 if (bufSize <= sizeof(char))
572 /*errno = KDB_ERR_TOOLONG;*/
576 if ( *string == ESCAPE_CHAR && *(string+1) == PATH_SEPARATOR ) {
577 /* Key delimiter escaped, kdbbEncode these two (escape + delim) */
578 if ( (j = kdbbEncodeChar(*(string++), buffer, bufSize)) != -1 ) {
579 bufSize -= j*sizeof(char);
581 written += j*sizeof(char);
583 /*errno = KDB_ERR_CONVERT;*/
587 if ( (j = kdbbEncodeChar(*(string++), buffer, bufSize)) != -1 ) {
588 bufSize -= j*sizeof(char);
589 written += j*sizeof(char);
592 /*errno = KDB_ERR_CONVERT;*/
596 } else if ( *string == PATH_SEPARATOR ) {
597 /* Replace unescaped KEY_DELIM to PATH_SEPARATOR */
598 *(buffer++) = PATH_SEPARATOR;
599 bufSize -= sizeof(char);
600 written += sizeof(char);
605 if ( (j = kdbbEncodeChar(*(string++), buffer, bufSize)) != -1 ) {
606 bufSize -= j*sizeof(char);
607 written += j*sizeof(char);
610 /*errno = KDB_ERR_CONVERT;*/
624 * This is a helper to kdbGetFullFilename()
626 * @param key has the relevant name for the relative filename
627 * @param relativeFilename the buffer to return the calculated filename
628 * @param maxSize maximum number of bytes that fit the buffer
629 * @see kdbGetFullFilename()
630 * @return number of bytes written to the buffer
631 * @return -1 on failure
632 * @ingroup backendhelper
634 ssize_t kdbbKeyCalcRelativeFilename(const Key *key,char *relativeFilename,size_t maxSize)
636 if (kdbbNeedsUTF8Conversion()) {
640 if (!(size=keyGetNameSize(key))) return -1;
642 converted = (char *) malloc(MAX_PATH_LENGTH);
643 size = kdbbKeyNameToRelativeFilename(keyName(key), converted,
646 /* memcpy(converted,relativeFilename,convertedSize); */
648 if (kdbbUTF8Engine(UTF8_TO,&converted,&size)) {
650 /*errno = KDB_ERR_CONVERT;*/
656 /*errno=KDB_ERR_TOOLONG;*/
660 memcpy(relativeFilename,converted,size);
665 return kdbbKeyNameToRelativeFilename(keyName(key), relativeFilename, maxSize);
675 * Calculate the real file name for a key.
677 * system/ keys will get the prefix KDB_DB_SYSTEM
679 * For the user/ keys the algorithm works as follow:
680 * 1.) When the override environment KDB_HOME exists the configuration will be searched below KDB_HOME/KDB_DB_USER
681 * 2.) When the owner of the key exists in the elektra user database steps a.) and b.) will be tested:
682 * a.) The specific value for configuration storage of the user below system/users/\<owner\>/kdb
683 * b.) The home variable in system/users/\<owner\>/home will be merged together with KDB_DB_USER
684 * 3.) When the environment HOME exists the configuration will be searched below HOME/KDB_DB_USER
685 * 4.) Otherwise the KDB_DB_HOME/\<owner\>/KDB_DB_USER will be used
687 * @param forKey the key object to work with
688 * @param handle the kdb handle to work with
689 * @param returned the buffer to return the calculated filename
690 * @param maxSize maximum number of bytes that fit the buffer
691 * @see kdbCalcRelativeFilename()
692 * @return number of bytes written to the buffer, or 0 on error
693 * @ingroup backendhelper
694 * @return length of returned string on success
695 * @return -1 on failure
697 ssize_t kdbbGetFullFilename(KDB *handle, const Key *forKey,char *returned,size_t maxSize) {
700 char buffer [MAX_PATH_LENGTH] = KDB_KEY_USERS;
703 switch (keyGetNamespace(forKey)) {
704 case KEY_NS_MEMORY: {
705 /* Prepare to use the 'system/ *' database */
706 strncpy(returned,KDB_DB_MEMORY,maxSize);
707 length=strlen(returned);
710 case KEY_NS_SYSTEM: {
711 /* Prepare to use the 'system/ *' database */
712 strncpy(returned,KDB_DB_SYSTEM,maxSize);
713 length=strlen(returned);
716 /* If we lack a usable concept of users we simply let the default handle it
717 * and hence disable the entire user/ hiarchy. */
719 #ifdef TUNNING_ELEKTRA_0_7
720 strncpy(returned,KDB_DB_USER,maxSize);
721 length=strlen(returned);
724 if ((home = getenv("KDB_DIR")) != 0)
726 length=snprintf(returned,maxSize,"%s",home);
730 if ((home = getenv("KDB_HOME")) != 0)
732 length=snprintf(returned,maxSize,"%s/%s",home,KDB_DB_USER);
736 strcat (buffer, "/");
737 strcat (buffer, keyOwner(forKey));
738 strcat (buffer, "/kdb");
739 length=kdbGetString (handle, buffer, returned, maxSize)-1;
745 strcpy (buffer, KDB_KEY_USERS);
746 strcat (buffer, "/");
747 strcat (buffer, keyOwner(forKey));
748 strcat (buffer, "/home");
749 length=kdbGetString (handle, buffer, returned, maxSize)-1;
752 strcpy (buffer, returned);
753 length=snprintf(returned,maxSize,"%s/%s",buffer, KDB_DB_USER);
757 if ((home = getenv("HOME")) != 0)
759 length=snprintf(returned,maxSize,"%s/%s",home,KDB_DB_USER);
763 length=snprintf(returned,maxSize,"%s/%s/%s",KDB_DB_HOME, keyOwner(forKey), KDB_DB_USER);
768 /*errno=KDB_ERR_INVALIDKEY;*/
773 returned[length]='/'; length++;
774 if (maxSize <= length)
776 /*errno=KDB_ERR_TOOLONG;*/
779 rc=kdbbKeyCalcRelativeFilename(forKey,returned+length,maxSize-length);
781 if (rc == -1) return -1;