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