pndnotifyd: fix some crashes
[pandora-libraries.git] / lib / pnd_conf.c
1
2 #include <stdio.h> /* for NULL, printf, FILE, etc */
3 #include <stdlib.h> /* for malloc */
4 #include <string.h> /* for strdup */
5 #include <ctype.h> /* for isspace */
6
7 #include "pnd_conf.h"
8 #include "pnd_container.h"
9 #include "pnd_pathiter.h"
10
11 pnd_conf_filename_t pnd_conf_filenames[] = {
12   { pnd_conf_conf,         PND_CONF_FILE },
13   { pnd_conf_apps,         "apps" },
14   { pnd_conf_startup,      "startup" },
15   { pnd_conf_desktop,      "desktop" },
16   { pnd_conf_categories,   "categories" },
17   { pnd_conf_evmap,        "eventmap" },
18   { pnd_conf_nil,          NULL },
19 };
20
21 char *pnd_conf_query_searchpath ( void ) {
22   pnd_conf_handle ch;
23
24   // fetch base config
25   ch = pnd_conf_fetch_by_id ( pnd_conf_conf, PND_CONF_SEARCHPATH );
26
27   if ( ! ch ) {
28     //printf ( "Couldn't locate base conf file '%s'\n", PND_CONF_FILE );
29     return ( strdup ( PND_CONF_SEARCHPATH ) );
30   }
31
32   // can we find a user-specified config path? if so, use it.. if not, fall back!
33   char *searchpath;
34   char *temp;
35
36   temp = pnd_conf_get_as_char ( ch, PND_CONF_KEY );
37
38   if ( temp ) {
39     searchpath = strdup ( temp );
40   } else {
41     searchpath = strdup ( PND_CONF_SEARCHPATH );
42   }
43
44   return ( searchpath );
45 }
46
47 pnd_conf_handle pnd_conf_fetch_by_id ( pnd_conf_filename_e id, char *searchpath ) {
48   pnd_conf_filename_t *p = pnd_conf_filenames;
49
50   while ( p -> filename ) {
51
52     /* found the filename associated to the id? */
53     if ( p -> id == id ) {
54       return ( pnd_conf_fetch_by_name ( p -> filename, searchpath ) );
55     }
56
57     /* next! */
58     p++;
59   } /* while */
60
61   return ( NULL );
62 }
63
64 pnd_conf_handle pnd_conf_fetch_by_name ( char *filename, char *searchpath ) {
65
66   /* the fun part here is that we get to cheat; while we have to search through all the directories
67    * listed in the search path, we can stop at the first matching file. Nothign really fancy going on, and
68    * no need for comprehensive directory crawling. yay!
69    */
70   pnd_conf_handle conf;
71
72   //printf ( "Search path: '%s'\n", searchpath );
73
74   SEARCHPATH_PRE
75   {
76
77     strncat ( buffer, "/", FILENAME_MAX - 1 );
78     strncat ( buffer, filename, FILENAME_MAX - 1 );
79     conf = pnd_conf_fetch_by_path ( buffer );
80
81     if ( conf ) {
82       wordfree ( &_p );
83       return ( conf );
84     }
85
86   }
87   SEARCHPATH_POST
88
89   return ( NULL );
90 }
91
92 pnd_conf_handle pnd_conf_fetch_by_path ( char *fullpath ) {
93   FILE *f;
94   char section [ 256 ] = "";
95   char buffer [ FILENAME_MAX ];
96   char inbuffer [ FILENAME_MAX ];
97   char *c, *head, *tail, *mid;
98
99   //printf ( "Attempt to load config from fullpath '%s'\n", fullpath );
100
101   /* WARN:
102    * No check yet to verify the directory is actually mounted and readable; either way
103    * this should not block or take up much time, though SD cards might be slow to open over
104    * and over again .. perhaps need some smarts or caching of results or somesuch, since this
105    * call gets spammed over and over...
106    */
107   f = fopen ( fullpath, "r" );
108
109   if ( ! f ) {
110     return ( NULL );
111   }
112
113   // damn, we actually found a file, so need to try to parse it. Shucks. Give back a box-handle
114   // so the consumer has some lists to look at
115   pnd_box_handle h;
116   h = pnd_box_new ( fullpath );
117
118   // inhale file
119   while ( fgets ( inbuffer, FILENAME_MAX, f ) ) {
120
121     // strip line-endings and DOSisms
122     if ( ( c = strchr ( inbuffer, '\r' ) ) ) {
123       *c = '\0';
124     }
125
126     if ( ( c = strchr ( inbuffer, '\n' ) ) ) {
127       *c = '\0';
128     }
129
130     //printf ( "config line: '%s'\n", inbuffer );
131
132     // strip comments
133     if ( ( c = strchr ( inbuffer, '#' ) ) ) {
134       *c = '\0';
135     }
136
137     // strip leading and trailing spaces
138     head = inbuffer;
139     while ( *head && isspace ( *head ) ) {
140       head++;
141     }
142
143     if ( inbuffer [ 0 ] == '\0' ) {
144       //printf ( "  -> discard\n" );
145       continue; // skip, the line was pure comment or blank
146     }
147
148     tail = strchr ( inbuffer, '\0' ) - 1;
149     while ( *tail && isspace ( *tail ) ) {
150       *tail = '\0';
151       tail--;
152     }
153
154     if ( inbuffer [ 0 ] == '\0' ) {
155       //printf ( "  -> discard\n" );
156       continue; // skip, the line was pure comment or blank
157     }
158
159     // decorated, ie: a section?
160     if ( *head == '[' && *tail == ']' ) {
161       // note: handle the nil-section
162
163       memset ( section, '\0', 256 );
164
165       if ( tail == head + 1 ) {
166         section [ 0 ] = '\0';
167       } else {
168         strncpy ( section, head + 1, tail - head - 1 );
169       }
170
171       //printf ( " -> section '%s'\n", section );
172
173     } else {
174
175       // must be a key (and likely a value) .. find the division
176       mid = head;
177       while ( *mid && ! isspace ( *mid ) ) {
178         mid++;
179       }
180       *mid = '\0';
181       mid++;
182
183       // skip past any heading space for the key
184       while ( *mid && isspace ( *mid ) ) {
185         mid++;
186       }
187
188       //printf ( "key head: '%s'\n", head );
189       //printf ( "key mid: '%s'\n", mid );
190
191       // is thjis a key/value pair, or just a key?
192       if ( mid [ 0 ] ) {
193         // key/value pairing
194         char *v;
195
196         // form the actual new key
197         if ( section [ 0 ] ) {
198           snprintf ( buffer, FILENAME_MAX - 1, "%s.%s", section, head );
199         } else {
200           strncpy ( buffer, head, FILENAME_MAX - 1 );
201         }
202
203         //printf ( "Found key '%s' in config file\n", buffer );
204
205         // alloc node into the box
206         v = pnd_box_allocinsert ( h, buffer, strlen ( mid ) + 1 ); // allow for trailing null
207
208         if ( v ) {
209           strcpy ( v, mid );
210         } else {
211           return ( NULL ); // OOM while reading conf is either sad, or really scary conf (also sad.)
212         }
213
214       } else {
215         // key only
216         char *v;
217
218         // form the actual new key
219         if ( section [ 0 ] ) {
220           snprintf ( buffer, FILENAME_MAX - 1, "%s.%s", section, head );
221         } else {
222           strncpy ( buffer, head, FILENAME_MAX - 1 );
223         }
224
225         //printf ( "Found key with no value '%s' in config file\n", buffer );
226
227         // alloc node into the box
228         v = pnd_box_allocinsert ( h, buffer, 0 ); // zero b/c of no payload
229
230         if ( ! v ) {
231           return ( NULL ); // OOM while reading conf is either sad, or really scary conf (also sad.)
232         }
233
234       } // key or key/value?
235
236     } // section or key/value line?
237
238   } // while
239
240   // clean up a trifle
241   fclose ( f );
242
243   return ( h );
244 }
245
246 char *pnd_conf_get_as_char ( pnd_conf_handle c, char *key ) {
247   return ( pnd_box_find_by_key ( c, key ) );
248 }
249
250 int pnd_conf_get_as_int ( pnd_conf_handle c, char *key ) {
251   char *t = pnd_box_find_by_key ( c, key );
252
253   if ( ! t ) {
254     return ( PND_CONF_BADNUM ); // non-existant
255   }
256
257   int i = atoi ( t );
258
259   return ( i );
260 }
261
262 int pnd_conf_get_as_int_d ( pnd_conf_handle c, char *key, int def ) {
263   char *t = pnd_box_find_by_key ( c, key );
264
265   if ( ! t ) {
266     return ( def ); // non-existant
267   }
268
269   int i = atoi ( t );
270
271   return ( i );
272 }
273
274 int *pnd_conf_set_int ( pnd_conf_handle c, char *key, int v ) {
275
276   // key is already present? if so, delete it
277   void *kv = pnd_box_find_by_key ( c, key );
278
279   if ( kv ) {
280     pnd_box_delete_node ( c, kv );
281   }
282
283   // add the new node
284   int *nv = pnd_box_allocinsert ( c, key, sizeof(int) );
285
286   if ( nv ) {
287     *nv = v;
288     return ( nv );
289   }
290
291   return ( NULL );
292 }
293
294 char *pnd_conf_set_char ( pnd_conf_handle c, char *key, char *v ) {
295
296   // key is already present? if so, delete it
297   char *kv = pnd_box_find_by_key ( c, key );
298
299   if ( kv ) {
300     pnd_box_delete_node ( c, kv );
301   }
302
303   // add the new node
304   char *nv = pnd_box_allocinsert ( c, key, strlen ( v ) + 1 );
305
306   if ( nv ) {
307     strcpy ( nv, v );
308     return ( nv );
309   }
310
311   return ( NULL );
312 }
313
314 unsigned char pnd_conf_write ( pnd_conf_handle c, char *fullpath ) {
315   char *p = pnd_box_get_head ( c );
316   FILE *f;
317   char lastcategory [ 100 ] = "";
318
319   if ( ! p ) {
320     return ( 1 ); // nothing to save, so.. success?
321   }
322
323   f = fopen ( fullpath, "w" );
324
325   if ( ! f ) {
326     return ( 0 );
327   }
328
329   while ( p ) {
330     char *k = pnd_box_get_key ( p );
331     char *c = strchr ( k, '.' );
332
333     if ( c ) {
334       if ( strncmp ( k, lastcategory, c - k ) == 0 ) {
335         // same category
336       } else {
337         strncpy ( lastcategory, k, c - k );
338         fprintf ( f, "[%s]\n", lastcategory );
339       }
340       fprintf ( f, "%s\t%s\n", c + 1, p );
341     } else {
342       if ( lastcategory [ 0 ] ) {
343         fprintf ( f, "[]\n" );
344       }
345       lastcategory [ 0 ] = '\0';
346       fprintf ( f, "%s\t%s\n", k, p );
347     }
348
349     p = pnd_box_get_next ( p );
350   }
351
352   fclose ( f );
353
354   return ( 1 );
355 }