Git init
[pkgs/e/elektra.git] / src / backends / ini / ini.c
1 /***************************************************************************
2             ini.c  -  Backend for ini-style like files
3                              -------------------
4     begin                : 01.03.2005
5     updated              : 06.10.2005
6     copyright            : (C) 2005 by Markus Raab
7     email                : debian@markus-raab.org
8  ***************************************************************************/
9
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the BSD License (revised).                      *
14  *                                                                         *
15  ***************************************************************************/
16
17
18 #include <ini.h>
19
20
21 int kdbOpen_ini(KDB *handle)
22 {
23         KDBCap *cap = kdbhGetCapability (handle);
24
25         cap->onlyFullGet=1;
26         cap->noStat=1;
27
28         cap->onlyRemoveAll=1;
29
30         cap->onlyFullSet=1;
31         cap->onlyAddKeys=1;
32
33         cap->onlySystem=1;
34         cap->onlyUser=1;
35
36         cap->noOwner=1;
37         cap->noValue=1;
38         cap->noComment=1;
39         cap->noUID=1;
40         cap->noGID=1;
41         cap->noMode=1;
42         cap->noDir=1;
43         cap->noATime=1;
44         cap->noMTime=1;
45         cap->noCTime=1;
46         cap->noRemove=1;
47         cap->noMount=1;
48         cap->noBinary=1;
49         cap->noString=1;
50         cap->noTypes=1;
51         cap->noError=1;
52
53         cap->noLock=1;
54         cap->noThread=1;
55
56         kdbhSetBackendData (handle, malloc (sizeof (backendData)));
57
58         /* backend initialization logic */
59
60         return 0;
61 }
62
63 int kdbClose_ini(KDB *handle)
64 {
65         free (kdbhGetBackendData (handle));
66
67         /* free all backend resources and shut it down */
68
69         return 0; /* success */
70 }
71
72 ssize_t kdbGet_ini(KDB *handle, KeySet *returned, const Key *parentKey)
73 {
74         int rc = 0;
75         char t [MAX_PATH_LENGTH];
76         Key * write = keyDup (parentKey);
77         
78 #if DEBUG
79         file_name(write, t);
80         fprintf (stderr, "file_name: %s\n",     t);
81         base_name(write, t);
82         fprintf (stderr, "base_name: %s\n",     t);
83 #endif
84
85         /**Immediately call IniChooseFile (will work recursively)*/
86         rc = IniChooseFile (handle, write, returned, 0);
87         keyDel (write);
88
89         return rc;
90 }
91
92 ssize_t kdbSet_ini(KDB *handle, KeySet *ks, const Key *parentKey)
93 {
94         return IniSetKeys (handle, ks);
95 }
96
97
98 /**
99  * Returns a filename from the Key which can be opened for forKey
100  *
101  * The name returned should be correct, because
102  * it removes subdirs and the keyname.
103  *
104  * It even tests with kdbStatKey if the File exists.
105  * So be prepaired that stat information is filled
106  * within the key.
107  *
108  * @see IniSearchFileName
109  * @param filename must be allocated MAX_PATH_LENGTH space.
110  * Previous Content of Filename will be destroyed afterwards.
111  * 
112  * @ingroup ini
113  */
114 size_t IniSearchFileName (KDB *handle, Key * forKey, char * filename)
115 {
116         size_t length;
117         uint8_t info = 0;
118         char * end;
119
120         length = file_name (forKey, filename);
121         
122         do {
123 #if DEBUG
124                 fprintf (stderr, "Search %s\n", filename);
125 #endif
126                 end = strrchr (filename, '/');
127                 if (end == NULL) {
128 #if DEBUG
129                         fprintf (stderr, "Could not find any file\n");
130 #endif
131                         return -1;
132                 }
133                 *end= '\0';
134                 stat_file (forKey, filename);
135                 info = keyGetType (forKey);
136         } while (!(info & KEY_TYPE_FILE));
137
138         return length;
139 }
140
141
142 /**
143  * Get out all the keys of a file
144  * 
145  * @param keyFileName: Name of the file
146  * @param keyRoot: Name of the root of files
147  *  The root will be added before the keyName
148  * 
149  * @ingroup ini
150  */
151 ssize_t IniGetKeys (KDB *handle, char * keyFileName, char * keyRoot, KeySet * returned)
152 {
153         Key * key;
154         int pos;
155
156         if (open_file (handle, keyFileName, O_RDONLY) == -1)
157         {
158 #if DEBUG
159                 fprintf (stderr, "Could not open file %s\n", keyFileName);
160 #endif
161                 /*errno = KDB_ERR_NOTFOUND;*/
162                 return -1;
163         }
164         
165         key = keyNew(0);
166
167 #if DEBUG
168         fprintf (stderr, "Call read_key(%s)\n", keyRoot);
169 #endif
170         while ((pos=read_key (handle, key, keyRoot)) == 0)
171         {
172 #if DEBUG
173                 fprintf (stderr, "Append key\n");
174 #endif
175                 ksAppendKey(returned,key);
176
177                 key = keyNew(0);
178         }
179         
180         keyDel (key); /* delete the not used key left*/
181
182         close_file(handle);
183         
184         return 0; /* success */
185 }
186
187 /**
188  * Reads the contents of the whole file which Key
189  * points to.
190  *
191  * The Keys will be added in the Keyset.
192  *
193  * @ingroup ini
194  */
195 int IniReadFile (KDB *handle, Key * key, KeySet * returned, unsigned long options)
196 {
197         char filename [MAX_PATH_LENGTH];
198         char * keyname;
199         size_t keyLength;
200
201 #if DEBUG
202         fprintf (stderr, "IniReadfile\n");
203 #endif
204         
205         file_name(key, filename);
206
207         keyLength = keyGetNameSize (key);
208         keyname = malloc (keyLength);
209         keyGetName (key, keyname, keyGetNameSize (key));
210
211 #if DEBUG
212         fprintf (stderr, "Call IniGetKeys(filename: %s, keyRoot: %s,returned)\n", 
213                 filename, keyname);
214 #endif
215         IniGetKeys (handle, filename, keyname, returned);
216         
217         if (keyLength >0) free (keyname);
218
219         return 0;
220 }
221
222 /**
223  * This mapper chooses between the different
224  * styles of files to start the correct function.
225  *
226  * For files it starts IniReadFile
227  * For directorys it starts IniReadDir
228  *
229  * @ingroup ini
230  * */
231 int IniChooseFile(KDB *handle, Key * key, KeySet * returned, unsigned long options)
232 {
233         char filename [MAX_PATH_LENGTH];
234         char * keyname;
235         size_t keylength;
236         
237         file_name(key, filename);
238         stat_file (key, filename);
239
240 #if DEBUG
241         fprintf (stderr, "IniChooseFile, pathName: %s\n", filename);
242 #endif
243         
244         if (keyGetType (key) == KEY_TYPE_DIR)
245         {
246                 if (filename[strlen(filename)-1] != '/')
247                 {
248                         keylength = keyGetNameSize(key);
249                         keyname = malloc (keylength + 2);
250                         keyGetName (key, keyname, keylength);
251                         keyname[keylength-1] = '/';
252                         keyname[keylength] = '\0';
253                         keySetName (key, keyname);
254                         free (keyname);
255                 }
256                         
257                 return IniReadDir (handle, key, returned, options);
258         }
259
260         if (keyGetType (key) == KEY_TYPE_FILE)
261         {
262                 return IniReadFile (handle, key, returned, options);
263         }
264
265 #if DEBUG
266         fprintf (stderr, "Not a directory or file!");
267 #endif
268         return -1;
269 }
270
271
272 /**
273  * Reads all Keys of a directory.
274  *
275  * For every key the mapper IniChooseFile
276  * will be called.
277  * 
278  * @ingroup ini
279  * */
280 int IniReadDir(KDB *handle, Key * key, KeySet * returned, unsigned long options)
281 {
282         char pathName [MAX_PATH_LENGTH];
283         char keyname [MAX_PATH_LENGTH];
284         char keypath [MAX_PATH_LENGTH];
285         char filename [MAX_PATH_LENGTH];
286         void * dir;
287         int ret;
288
289 #if DEBUG
290         fprintf (stderr, "IniReadDir\n");
291 #endif
292         file_name (key, pathName);
293         dir = open_dir (pathName);
294         if (dir == NULL) {
295                 fprintf (stderr, "Could not open directory %s\n", pathName);
296                 return -1;
297         }
298         
299         keyGetName (key, keypath, MAX_PATH_LENGTH);
300         
301         while (read_dir (dir,filename) == 0)
302         {
303                 if (    strcmp(filename, ".")  == 0 || 
304                         strcmp(filename, "..") == 0)
305                         continue;
306                 
307                 if (filename[0] == '.' && !(options & KDB_O_INACTIVE))
308                         continue;
309
310 #if DEBUG
311                 fprintf (stderr, "Next entry filename: %s\n", filename);
312 #endif
313                 strncpy(keyname, keypath, MAX_PATH_LENGTH);
314                 strcat(keyname, filename);
315                 keySetName (key, keyname);
316
317 #if DEBUG
318                 fprintf (stderr, "New keyname: %s\n", keyname);
319 #endif
320                 ret = IniChooseFile (handle, key, returned, options);
321         }
322
323         if (close_dir (dir))
324         {
325                 fprintf (stderr, "Could not close directory\n");
326                 return -1;
327         }
328
329         return ret;
330 }
331
332
333
334 /**Walks through a file and lookups if the first key of a keyset is in the
335  * given Keyset. When found it overwrites the key. It also checks if any
336  * of the other keys can be written in that file under that context. If
337  * not, the keys will remain in the Keyset.
338  *
339  * So, the KeySet will be empty after complete successful writing. At
340  * least one key is guranteed to be written when it was successful.
341  * 
342  * The whole idea behind that concept is, that open/close and searching
343  * within a file is complete minimized. The disadvantage is, that a
344  * fast ksLookupKey is required, because every readed key will be compared
345  * with all keys in keyset (its not implemented in elektra right now,
346  * so it will consume linear more time, but only in memory, not on
347  * harddisc).
348  *
349  * When not found it writes out the keys before the sectionend.
350  * This can be:
351  * 
352  * setting keys new keys will introduce new folders and files
353  * as needed.
354  *
355  * At least one key will be written!
356  * 
357  * @return >=0 on success
358  * @return -1 on failure (Keyset may be changed then!)
359  * @return #nr when #nr keys could not written to file
360  *
361  * */
362 int IniSetKeys (KDB *handle, KeySet * origKeys)
363 {
364         char keyFileName [MAX_PATH_LENGTH];
365         
366         int pos;
367         int keySize;
368         char * keyFullName = NULL;
369         char * keyRoot = NULL;
370         char * end;
371
372         Key * origKey;  /*First key which will introduce opening file*/
373         Key * setKey;   /*Used for getting the keys which may be set*/
374         Key * key = keyNew(0);
375
376         long oldpos;
377         
378 #if DEBUG
379         fprintf (stderr, "IniSetKeys() entered\n");
380 #endif
381
382         ksRewind (origKeys);
383         origKey = ksNext (origKeys); /* Open file for this key*/
384         key = keyDup (origKey); /* for searching*/
385         
386         pos = IniSearchFileName(handle, key, keyFileName);
387
388 #if DEBUG
389         fprintf (stderr, "after SearchFileName ...\n");
390 #endif
391
392         if (pos == -1) /* no such file exists*/
393         {
394                 file_name(key,keyFileName);
395                 create_dir(keyFileName);        
396         }
397         
398         keySize = keyGetNameSize (key);
399         keyFullName = malloc (keySize+1);
400         if (keyFullName == NULL) goto memerror;
401         keyGetName(key, keyFullName, keySize);
402         
403         end = strrchr (keyFullName, '/');       /* dirname*/
404         if (end) *end = 0;
405         keyRoot = malloc (strlen (keyFullName));
406         if (keyRoot == NULL) goto memerror;
407         strcpy (keyRoot, keyFullName);
408         if (end) *end = '/';    /*revert keyname*/
409
410 #if DEBUG
411         fprintf (stderr, "keyRoot: %s, keyName: %s\n", keyRoot, keyFullName);
412         fprintf (stderr, "Set Key [%d] in File: %s\n",keySize, keyFileName);
413 #endif
414
415         if (open_file (handle, keyFileName, O_RDWR) == -1)
416         {
417 #if DEBUG
418                 fprintf (stderr, "Could not open file %s\n", keyFileName);
419 #endif
420                 ksAppendKey(origKeys, origKey);
421                 /*errno = KDB_ERR_NOTFOUND;*/
422                 goto fileerror;
423         }
424         
425         while ((pos=read_key (handle, key, keyRoot)) == 0)
426         {
427                 if ((setKey = ksLookupByName (origKeys, key->key, 0)) != NULL) 
428                 {       /* right Key found*/
429 #if DEBUG
430                         fprintf (stderr, "Key found\n");
431                         fprintf(stderr, "Name: (%s), Value: (%s), Comment: (%s)\n",
432                                 keyName (setKey), (char *) keyValue(setKey),
433                                 (char *) keyComment (setKey));
434 #endif
435                         
436                         write_key(handle, setKey, oldpos);
437
438                         if ((keyCompare (key, origKey) & KEY_NAME) == 0)
439                                 pos = 1; /*Start Key found, good!*/
440                 }
441                 oldpos = ftell (FILEPTR);
442         }
443         if (pos != 1) { /* key not found, add to the end*/
444 #if DEBUG
445                 fprintf (stderr, "Key not found!\n");
446 #endif
447                 fseek (FILEPTR, 0, SEEK_END);
448                 oldpos = ftell(FILEPTR);
449                 /*TODO: write key here if not found
450                  * write_key(setKey, oldpos);*/
451                 pos = 0;
452         } else if (pos == 1) { /* key found, everything went ok!*/
453                 pos = 0;
454         }
455         
456         close_file (handle);
457         
458         free (keyFullName);
459         free (keyRoot);
460         
461         keyDel (key);
462
463 #if DEBUG
464         fprintf (stderr, "leaving IniSetKeys()\n");
465 #endif
466         
467         return pos; /* success */
468
469 memerror:
470         /*errno=KDB_ERR_NOMEM;*/
471         close_file (handle);
472 #if DEBUG
473         fprintf (stderr, "Memory Error\n");
474 #endif
475 fileerror:
476         
477         free (keyFullName);
478         free (keyRoot);
479         
480         keyDel (key);
481         
482         return -1;
483 }
484
485 KDBEXPORT(ini)
486 {
487         return kdbBackendExport(BACKENDNAME,
488                 KDB_BE_OPEN,    &kdbOpen_ini,
489                 KDB_BE_CLOSE,   &kdbClose_ini,
490                 KDB_BE_GET,     &kdbGet_ini,
491                 KDB_BE_SET,     &kdbSet_ini,
492                 KDB_BE_VERSION,        BACKENDVERSION,
493                 KDB_BE_AUTHOR,  "Markus Raab <elektra@libelektra.org>",
494                 KDB_BE_LICENCE, "BSD",
495                 KDB_BE_DESCRIPTION, "Key/Value Pairs are stored in files in following scheme: key1=value1;comment",
496                 KDB_BE_END);
497 }
498