Git init
[pkgs/e/elektra.git] / src / libhelper / helper.c
1 /***************************************************************************
2             helper.c  -  Helpers for backends
3                              -------------------
4     begin                : Mon Dec 29 2003
5     copyright            : (C) 2003 by Avi Alkalay
6     email                : avi@unix.sh
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the BSD License (revised).                      *
13  *                                                                         *
14  ***************************************************************************/
15
16
17 #include "helper.h"
18
19 /**
20  * @defgroup backendhelper KDB Backends :: Backend Helper for Elektra
21  * @brief Backend helper Methods for Elektra and Backends.
22  *
23  * To use them:
24  * @code
25  * #include <kdbbackend.h>
26  * @endcode
27  *
28  * These backend helper methods provide
29  * functionality commonly used by backends to make backend
30  * development easier and to provide the same behaviour
31  * between backends.
32  *
33  */
34
35
36 /**
37  * Locks file for exclusive write mode.
38  *
39  * This function will block until all reader
40  * and writer have left the file.
41  *
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
47  */
48 int kdbbWriteLock (FILE *f)
49 {
50 #ifdef HAVE_FCNTL_H
51         int fd = fileno(f);
52         struct flock l;
53         int ret=0;
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);
59         return ret;
60 #else
61         return 0;
62 #endif
63 }
64
65 /**
66  * Locks file for read mode.
67  *
68  * Other processes and threads are allowed to read the
69  * file too simultaneous.
70  *
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
76  */
77 int kdbbReadLock (FILE *f)
78 {
79 #ifdef HAVE_FCNTL_H
80         int ret=0;
81         int fd = fileno(f);
82         struct flock l;
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);
88         return ret;
89 #else
90         return 0;
91 #endif
92 }
93
94
95 /**
96  * Unlocks file.
97  *
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
103  */
104 int kdbbUnlock (FILE *f)
105 {
106 #ifdef HAVE_FCNTL_H
107         int ret=0;
108         int fd = fileno(f);
109         struct flock l;
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);
115         return ret;
116 #else
117         return 0;
118 #endif
119 }
120
121
122
123 /**
124  * Encodes a buffer of data onto hexadecimal ASCII.
125  *
126  * The resulting data is made up of pairs of ASCII hex-digits,
127  * space- and newline-separated. This is the counterpart of
128  * kdbbDecode().
129  *
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.
132  *
133  *
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.
138  * @see kdbbDecode()
139  * @ingroup backendhelper
140  */
141 ssize_t kdbbEncode(void *kdbbDecoded, size_t size, char *returned)
142 {
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;
148         int currentInLine=0;
149
150         if ( size == 0 )
151                 return 0;
152         
153         while ((readCursor-(char *)kdbbDecoded)<size) {
154                 sprintf(writeCursor,"%02x",*(unsigned char *)readCursor);
155                 readCursor++;
156                 writeCursor+=2;
157                 currentInBlock++;
158                 currentInLine++;
159                 if (currentInLine==lineStep) {
160                         *writeCursor='\n'; writeCursor++;
161                         currentInLine=0;
162                         currentInBlock=0;
163                 }
164                 if (currentInBlock==blockStep) {
165                         *writeCursor=' '; writeCursor++;
166                         currentInBlock=0;
167                 }
168         }
169         *writeCursor='\n';
170         *++writeCursor=0;
171         return writeCursor-returned;
172 }
173
174
175 /**
176  * UnkdbbEncodes a buffer of ASCII hexadecimal values into a byte stream.
177  *
178  * The allowed format for the hexadecimal values is just
179  * a stream of pairs of plain hex-digits, all together or
180  * space-separated.
181  * 
182  * The @c returned data won't be bigger than half the size of the
183  * source @c kdbbEncoded data.
184  *
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
189  * @see kdbbEncode()
190  * @ingroup backendhelper
191  */
192 ssize_t kdbbDecode(char *kdbbEncoded,void *returned)
193 {
194         char byteInHexa[5]="0x";
195         char *readCursor=kdbbEncoded;
196         char *writeCursor=returned;
197
198         if (!kdbbEncoded) {
199                 if (returned) *(char *)returned=0;
200                 return 0;
201         }
202
203         byteInHexa[4]=0;
204         while (*readCursor) {
205                 if (isspace((int)*readCursor)) 
206                 {
207                 readCursor++;
208                 continue;
209                 }
210                 if (isxdigit((int)*readCursor)) {
211                         long int converted;
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;
216
217                         readCursor+=2;
218                         writeCursor++;
219                 } else {
220                         /* This is suposed to be a hex-digit stream. But is not, so return. */
221                         /*errno=KDB_ERR_TYPEMISMATCH;*/
222                         return -1;
223                 }
224         }
225         return (long int)writeCursor-(long int)returned;
226 }
227
228 /**
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.
235  *
236  * @return 0 if not needed
237  * @return anything else if needed
238  * @ingroup backendhelper
239  */
240 int kdbbNeedsUTF8Conversion()
241 {
242 #if defined(HAVE_NL_LANGINFO) && defined(HAVE_ICONV) && defined(CODESET)
243         return strcmp(nl_langinfo(CODESET),"UTF-8");
244 #else
245         return 0;
246 #endif
247 }
248
249
250 /**
251  * Converts string to (@p direction = @c UTF8_TO) and from
252  * (@p direction = @c UTF8_FROM) UTF-8.
253  * 
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
257  * comments.
258  *
259  * Broken locales in applications can cause problems too. Make sure to load
260  * the environment locales in your application using
261  * @code
262 setlocale (LC_ALL, "");
263  * @endcode
264  *
265  * Otherwise kdbbUTF8Engine will quit with -1 leading that backends return
266  * with error when non-ascii characters appear. Binary values are not effected.
267  *
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
270  * immediately.
271  *
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
278  *      leading NULL
279  * @return 0 on success
280  * @return -1 on failure
281  * @ingroup backendhelper
282  *
283  */
284 int kdbbUTF8Engine(int direction, char **string, size_t *inputOutputByteSize)
285 {
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 */
290  
291 #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
292         char *currentCharset=0;
293         char *converted=0;
294         char *readCursor, *writeCursor;
295         size_t bufferSize;
296         iconv_t converter;
297         
298         if (kdbbNeedsUTF8Conversion() && *inputOutputByteSize) currentCharset=nl_langinfo(CODESET);
299         else return 0;
300
301         if (direction==UTF8_TO) converter=iconv_open("UTF-8",currentCharset);
302         else converter=iconv_open(currentCharset,"UTF-8");
303
304         if (converter == (iconv_t)(-1)) return -1;
305
306         /* work with worst case, when all chars are wide */
307         bufferSize=*inputOutputByteSize * 4;
308         converted=malloc(bufferSize);
309         if (!converted) return -1;
310
311         readCursor=*string;
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 */
315         if (iconv(converter,
316                         (ICONV_CONST char **)&readCursor,inputOutputByteSize,
317                         &writeCursor,&bufferSize) == (size_t)(-1)) {
318                 free(converted);
319                 iconv_close(converter);
320                 return -1;
321         }
322
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 */
326         readCursor=*string;
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 */
332         free(readCursor);
333         /* release buffer memory */
334         free(converted);
335         /* release the conversor engine */
336         iconv_close(converter);
337 #endif
338         return 0;
339 }
340
341
342
343 /**
344  * Char encoding.
345  *
346  * Encode '/', '\', '%', '+', ' ' char following
347  * RFC 2396 or copy char untouched if different.
348  *
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
356  *
357  * NOTE: No '\\0' is added at the end of buffer.
358  *
359  */
360 int kdbbEncodeChar(char c, char *buffer, size_t bufSize)
361 {
362         switch(c) {
363                 case '%':
364                         if ( bufSize >= (3*sizeof(char)) ) {
365                                 memcpy(buffer, "%25", sizeof("%25"));
366                                 return (3*sizeof(char));
367                         }
368                         return -1;
369
370                 case '+':
371                         if ( bufSize >= (3*sizeof(char)) ) {
372                                 memcpy(buffer, "%2B", sizeof("%2B"));
373                                 return (3*sizeof(char));
374                         }
375                         return -1;
376
377                 case ' ':
378                         if ( bufSize >= 1*sizeof(char) ) {
379                                 *(buffer) = '+';
380                                 return (1*sizeof(char));
381                         }
382                         return -1;
383
384                 case '/':
385                         if ( bufSize >= (3*sizeof(char)) ) {
386                                 memcpy(buffer, "%2F", sizeof("%2F"));
387                                 return (3*sizeof(char));
388                         }
389                         return -1;
390
391                 case '\\':
392                         if ( bufSize >= (3*sizeof(char)) ) {
393                                 memcpy(buffer, "%5C", sizeof("%5C"));
394                                 return (3*sizeof(char));
395                         }
396                         return -1;
397
398                 default:
399                         if ( bufSize >= (1*sizeof(char)) ) {
400                                 *(buffer++) = c;
401                                 return (1*sizeof(char));
402                         }
403                         return -1;
404         }
405
406         return 0;
407 }
408
409 /**
410  * Char decoding.
411  *
412  * Decode one char from %25, %2B, %2F, %2C following
413  * RFC 2396 or copy char untouched if different.
414  *
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
421  *
422  * NOTE: No '\\0' is added at the end of buffer.
423  *
424  */
425 int kdbbDecodeChar(const char *from, char *into)
426 {
427         switch(*from) {
428                 case '%':
429                         if ( strlen(from) >= (3*sizeof(char)) ) {
430                                 switch(*(from+2)) {
431                                         case '5':       *into = '%';    break;
432                                         case 'B':       *into = '+';    break;
433                                         case 'F':       *into = '/';    break;
434                                         case 'C':       *into = '\\';   break;
435
436                                         default:
437                                                 return -1;
438                                 }
439
440                                 return (3*sizeof(char));
441                         }
442                         return -1;
443
444                 case '+':
445                         *into = ' ';
446                         return (1*sizeof(char));
447
448                 default:
449                         *into = *from;
450                         return (1*sizeof(char));
451         }
452
453         return 0;
454 }
455
456 /**
457  * Translate a relative file name to a key name
458  * applying decoding.
459  *
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
467  *
468  */
469 int kdbbFilenameToKeyName(const char *string, char *buffer, int bufSize)
470 {
471         char decoded;
472         int j;
473
474         while (*string != '\0')
475         {
476                 if (bufSize <= sizeof(char))
477                 {
478                         /*errno = KDB_ERR_TOOLONG;*/
479                         return -1;
480                 }
481
482                 if ( *string == PATH_SEPARATOR ) {
483                         /* Translate PATH_SEPARATOR into KEY_SEPARATOR */
484                         *(buffer++) = KEY_SEPARATOR;
485                         bufSize -= sizeof(char);
486                         string++;
487                 } else {
488                         /* Decode char */
489                         if ( (j = kdbbDecodeChar(string, &decoded)) != -1 ) {
490                                 string += j;
491                                 *(buffer++) = decoded;
492                                 bufSize -= sizeof(char);
493                         } else {
494                                 *(buffer) = '\0';
495                                 /*errno = KDB_ERR_CONVERT;*/
496                                 return -1;
497                         }
498                 }
499         }
500
501         *buffer = '\0';
502
503         return 0;
504 }
505
506 /**Calculates the keyname out of a relative filename.
507  *
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()
517  */
518 ssize_t kdbbGetFullKeyName (KDB *handle, const char *forFilename, const Key *parentKey, Key *returned)
519 {
520         size_t size=0;
521         char *transformedName=0;
522         char *name;
523
524         /* Next 2 ifs are required to transform filename from UTF-8 */
525         transformedName = malloc(size=kdbiStrLen(forFilename));
526         strcpy(transformedName,forFilename);
527
528         if (kdbbUTF8Engine(UTF8_FROM,&transformedName,&size)) {
529                 free(transformedName);
530                 /*errno = KDB_ERR_CONVERT;*/
531                 return -1; 
532         }
533
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);
539
540         /* Now set the name and owner */
541         keySetName (returned, name);
542         keySetOwner(returned, keyOwner(parentKey));
543
544         free(transformedName);
545         free(name);
546         return 0;
547 }
548
549 /**
550  * Translate a key name to a relative file name
551  * applying encoding.
552  *
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
560  *
561  **/
562 int kdbbKeyNameToRelativeFilename(const char *string, char *buffer, size_t bufSize)
563 {
564         size_t  written;
565         int     j;
566
567         written = 0;
568         while (*string != '\0')
569         {
570                 if (bufSize <= sizeof(char))
571                 {
572                         /*errno = KDB_ERR_TOOLONG;*/
573                         return -1;
574                 }
575
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);
580                                 buffer += j;
581                                 written += j*sizeof(char);
582                         } else {
583                                 /*errno = KDB_ERR_CONVERT;*/
584                                 return -1;
585                         }
586
587                         if ( (j = kdbbEncodeChar(*(string++), buffer, bufSize)) != -1 ) {
588                                 bufSize -= j*sizeof(char);
589                                 written += j*sizeof(char);
590                                 buffer += j;
591                         } else {
592                                 /*errno = KDB_ERR_CONVERT;*/
593                                 return -1;
594                         }
595
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);
601                         string++;
602
603                 } else {
604                         /* Encode ... */
605                         if ( (j = kdbbEncodeChar(*(string++), buffer, bufSize)) != -1 ) {
606                                 bufSize -= j*sizeof(char);
607                                 written += j*sizeof(char);
608                                 buffer += j;
609                         } else {
610                                 /*errno = KDB_ERR_CONVERT;*/
611                                 return -1;
612                         }
613                 }
614         }
615         *buffer = '\0';
616         written++;
617
618         return written;
619 }
620
621
622
623 /**
624  * This is a helper to kdbGetFullFilename()
625  *
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
633  */
634 ssize_t kdbbKeyCalcRelativeFilename(const Key *key,char *relativeFilename,size_t maxSize)
635 {
636         if (kdbbNeedsUTF8Conversion()) {
637                 char *converted;
638                 size_t size;
639
640                 if (!(size=keyGetNameSize(key))) return -1;
641
642                 converted = (char *) malloc(MAX_PATH_LENGTH);
643                 size = kdbbKeyNameToRelativeFilename(keyName(key), converted,
644                         MAX_PATH_LENGTH);
645
646 /*              memcpy(converted,relativeFilename,convertedSize); */
647
648                 if (kdbbUTF8Engine(UTF8_TO,&converted,&size)) {
649                         free(converted);
650                         /*errno = KDB_ERR_CONVERT;*/
651                         return -1;
652                 }
653
654                 if (size>maxSize) {
655                         free(converted);
656                         /*errno=KDB_ERR_TOOLONG;*/
657                         return -1;
658                 }
659
660                 memcpy(relativeFilename,converted,size);
661                 free(converted);
662
663                 return size;
664         } else {
665                 return kdbbKeyNameToRelativeFilename(keyName(key), relativeFilename, maxSize);
666         }
667
668         return -1;
669 }
670
671
672
673
674 /**
675  * Calculate the real file name for a key.
676  *
677  * system/ keys will get the prefix KDB_DB_SYSTEM
678  *
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
686  *
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
696  */
697 ssize_t kdbbGetFullFilename(KDB *handle, const Key *forKey,char *returned,size_t maxSize) {
698         ssize_t length=0;
699         char * home;
700         char buffer [MAX_PATH_LENGTH] = KDB_KEY_USERS;
701         ssize_t rc;
702
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);
708                         break;
709                 }
710                 case KEY_NS_SYSTEM: {
711                         /* Prepare to use the 'system/ *' database */
712                         strncpy(returned,KDB_DB_SYSTEM,maxSize);
713                         length=strlen(returned);
714                         break;
715                 }
716                 /* If we lack a usable concept of users we simply let the default handle it
717                  * and hence disable the entire user/ hiarchy. */
718                 case KEY_NS_USER: {
719 #ifdef TUNNING_ELEKTRA_0_7
720                         strncpy(returned,KDB_DB_USER,maxSize);
721                         length=strlen(returned);
722                         break;
723 #endif
724                         if ((home = getenv("KDB_DIR")) != 0)
725                         {
726                                 length=snprintf(returned,maxSize,"%s",home);
727                                 break;
728                         }
729
730                         if ((home = getenv("KDB_HOME")) != 0)
731                         {
732                                 length=snprintf(returned,maxSize,"%s/%s",home,KDB_DB_USER);
733                                 break;
734                         }
735
736                         strcat (buffer, "/");
737                         strcat (buffer, keyOwner(forKey));
738                         strcat (buffer, "/kdb");
739                         length=kdbGetString (handle, buffer, returned, maxSize)-1;
740                         if (length > 0)
741                         {
742                                 break;
743                         }
744
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;
750                         if (length > 0)
751                         {
752                                 strcpy (buffer, returned);
753                                 length=snprintf(returned,maxSize,"%s/%s",buffer, KDB_DB_USER);
754                                 break;
755                         }
756
757                         if ((home = getenv("HOME")) != 0)
758                         {
759                                 length=snprintf(returned,maxSize,"%s/%s",home,KDB_DB_USER);
760                                 break;
761                         }
762
763                         length=snprintf(returned,maxSize,"%s/%s/%s",KDB_DB_HOME, keyOwner(forKey), KDB_DB_USER);
764                         break;
765                 }
766
767                 default: {
768                         /*errno=KDB_ERR_INVALIDKEY;*/
769                         return -1;
770                 }
771         }
772
773         returned[length]='/'; length++;
774         if (maxSize <= length)
775         {
776                 /*errno=KDB_ERR_TOOLONG;*/
777                 return -1;
778         }
779         rc=kdbbKeyCalcRelativeFilename(forKey,returned+length,maxSize-length);
780
781         if (rc == -1) return -1;
782         else length += rc;
783
784         return length;
785 }
786