Added in rudimentary category support for dotdesktop file emitting; ie: PXML's catego...
[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_nil,          NULL },
18 };
19
20 char *pnd_conf_query_searchpath ( void ) {
21   pnd_conf_handle ch;
22
23   // fetch base config
24   ch = pnd_conf_fetch_by_id ( pnd_conf_conf, PND_CONF_SEARCHPATH );
25
26   if ( ! ch ) {
27     //printf ( "Couldn't locate base conf file '%s'\n", PND_CONF_FILE );
28     return ( strdup ( PND_CONF_SEARCHPATH ) );
29   }
30
31   // can we find a user-specified config path? if so, use it.. if not, fall back!
32   char *searchpath;
33   char *temp;
34
35   temp = pnd_conf_get_as_char ( ch, PND_CONF_KEY );
36
37   if ( searchpath ) {
38     searchpath = strdup ( temp );
39   } else {
40     searchpath = strdup ( PND_CONF_SEARCHPATH );
41   }
42
43   return ( searchpath );
44 }
45
46 pnd_conf_handle pnd_conf_fetch_by_id ( pnd_conf_filename_e id, char *searchpath ) {
47   pnd_conf_filename_t *p = pnd_conf_filenames;
48
49   while ( p -> filename ) {
50
51     /* found the filename associated to the id? */
52     if ( p -> id == id ) {
53       return ( pnd_conf_fetch_by_name ( p -> filename, searchpath ) );
54     }
55
56     /* next! */
57     p++;
58   } /* while */
59
60   return ( NULL );
61 }
62
63 pnd_conf_handle pnd_conf_fetch_by_name ( char *filename, char *searchpath ) {
64
65   /* the fun part here is that we get to cheat; while we have to search through all the directories
66    * listed in the search path, we can stop at the first matching file. Nothign really fancy going on, and
67    * no need for comprehensive directory crawling. yay!
68    */
69   pnd_conf_handle conf;
70
71   //printf ( "Search path: '%s'\n", searchpath );
72
73   SEARCHPATH_PRE
74   {
75
76     strncat ( buffer, "/", FILENAME_MAX - 1 );
77     strncat ( buffer, filename, FILENAME_MAX - 1 );
78     conf = pnd_conf_fetch_by_path ( buffer );
79
80     if ( conf ) {
81       return ( conf );
82     }
83
84   }
85   SEARCHPATH_POST
86
87   return ( NULL );
88 }
89
90 pnd_conf_handle pnd_conf_fetch_by_path ( char *fullpath ) {
91   FILE *f;
92   char section [ 256 ] = "";
93   char buffer [ FILENAME_MAX ];
94   char inbuffer [ FILENAME_MAX ];
95   char *c, *head, *tail, *mid;
96
97   //printf ( "Attempt to load config from fullpath '%s'\n", fullpath );
98
99   /* WARN:
100    * No check yet to verify the directory is actually mounted and readable; either way
101    * this should not block or take up much time, though SD cards might be slow to open over
102    * and over again .. perhaps need some smarts or caching of results or somesuch, since this
103    * call gets spammed over and over...
104    */
105   f = fopen ( fullpath, "r" );
106
107   if ( ! f ) {
108     return ( NULL );
109   }
110
111   // damn, we actually found a file, so need to try to parse it. Shucks. Give back a box-handle
112   // so the consumer has some lists to look at
113   pnd_box_handle h;
114   h = pnd_box_new ( fullpath );
115
116   // inhale file
117   while ( fgets ( inbuffer, FILENAME_MAX, f ) ) {
118
119     // strip line-endings and DOSisms
120     if ( ( c = strchr ( inbuffer, '\r' ) ) ) {
121       *c = '\0';
122     }
123
124     if ( ( c = strchr ( inbuffer, '\n' ) ) ) {
125       *c = '\0';
126     }
127
128     //printf ( "config line: '%s'\n", inbuffer );
129
130     // strip comments
131     if ( ( c = strchr ( inbuffer, '#' ) ) ) {
132       *c = '\0';
133     }
134
135     // strip leading and trailing spaces
136     head = inbuffer;
137     while ( *head && isspace ( *head ) ) {
138       head++;
139     }
140
141     if ( inbuffer [ 0 ] == '\0' ) {
142       //printf ( "  -> discard\n" );
143       continue; // skip, the line was pure comment or blank
144     }
145
146     tail = strchr ( inbuffer, '\0' ) - 1;
147     while ( *tail && isspace ( *tail ) ) {
148       *tail = '\0';
149       tail--;
150     }
151
152     if ( inbuffer [ 0 ] == '\0' ) {
153       //printf ( "  -> discard\n" );
154       continue; // skip, the line was pure comment or blank
155     }
156
157     // decorated, ie: a section?
158     if ( *head == '[' && *tail == ']' ) {
159       // note: handle the nil-section
160
161       memset ( section, '\0', 256 );
162
163       if ( tail == head + 1 ) {
164         section [ 0 ] = '\0';
165       } else {
166         strncpy ( section, head + 1, tail - head - 1 );
167       }
168
169       //printf ( " -> section '%s'\n", section );
170
171     } else {
172
173       // must be a key (and likely a value) .. find the division
174       mid = head;
175       while ( *mid && ! isspace ( *mid ) ) {
176         mid++;
177       }
178       *mid = '\0';
179       mid++;
180
181       //printf ( "key head: '%s'\n", head );
182       //printf ( "key mid: '%s'\n", mid );
183
184       // is thjis a key/value pair, or just a key?
185       if ( mid [ 0 ] ) {
186         // key/value pairing
187         char *v;
188
189         // form the actual new key
190         if ( section [ 0 ] ) {
191           snprintf ( buffer, FILENAME_MAX - 1, "%s.%s", section, head );
192         } else {
193           strncpy ( buffer, head, FILENAME_MAX - 1 );
194         }
195
196         //printf ( "Found key '%s' in config file\n", buffer );
197
198         // alloc node into the box
199         v = pnd_box_allocinsert ( h, buffer, strlen ( mid ) + 1 ); // allow for trailing null
200
201         if ( v ) {
202           strcpy ( v, mid );
203         } else {
204           return ( NULL ); // OOM while reading conf is either sad, or really scary conf (also sad.)
205         }
206
207       } else {
208         // key/value pairing
209         char *v;
210
211         // form the actual new key
212         if ( section [ 0 ] ) {
213           snprintf ( buffer, FILENAME_MAX - 1, "%s.%s", section, head );
214         } else {
215           strncpy ( buffer, head, FILENAME_MAX - 1 );
216         }
217
218         //printf ( "Found key with no value '%s' in config file\n", buffer );
219
220         // alloc node into the box
221         v = pnd_box_allocinsert ( h, buffer, 0 ); // zero b/c of no payload
222
223         if ( ! v ) {
224           return ( NULL ); // OOM while reading conf is either sad, or really scary conf (also sad.)
225         }
226
227       } // key or key/value?
228
229     } // section or key/value line?
230     
231   } // while
232
233   // clean up a trifle
234   fclose ( f );
235
236   return ( h );
237 }
238
239 char *pnd_conf_get_as_char ( pnd_conf_handle c, char *key ) {
240   return ( pnd_box_find_by_key ( c, key ) );
241 }