* ... - new cfgreader.c/CFG_GetForeach()
[gnokii.git] / common / cfgreader.c
1 /*
2
3   $Id$
4
5   G N O K I I
6
7   A Linux/Unix toolset and driver for Nokia mobile phones.
8
9   Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml.
10
11   Released under the terms of the GNU GPL, see file COPYING for more details.
12
13   Config file (/etc/gnokiirc and ~/.gnokiirc) reader.
14
15   Modified from code by Tim Potter.
16
17   $Log$
18   Revision 1.1.1.1.8.1  2001/11/25 23:10:58  short
19       * ... - new cfgreader.c/CFG_GetForeach()
20   some "const" keywords written - they are missing almost everywhere in Gnokii!
21   "CFG_info" is now global public symbol
22    * Global symbols became IMO obsolete: {model,port,initlength,connection,bindir}
23   stdout/stderr are de-buffered (cfgreading is upon startup)
24    * I know that it doesn't belong here but currently there is now generic
25      application init function anywhere.
26    * stdout/stderr de-buffering is already donw at various weird places all
27      through the project
28
29   Revision 1.1.1.1  2001/11/25 21:58:58  short
30   :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001
31
32   Revision 1.16  2001/11/14 10:46:12  pkot
33   Small cleanup with __unices__
34
35   Revision 1.15  2001/06/10 11:24:57  machek
36   Kill "slash star" inside comment.
37
38   Revision 1.14  2001/03/19 23:43:45  pkot
39   Solaris/ *BSD '#if defined' cleanup
40
41   Revision 1.13  2001/03/13 01:21:38  pkot
42   *BSD updates (Bert Driehuis)
43
44   Revision 1.12  2001/01/08 15:11:36  pkot
45   Documentation updates.
46   Fixed some bugs and removed FIXMEs.
47   We need to move some stuff from configure.in to aclocal.m4
48
49   Revision 1.11  2001/01/02 09:09:07  pkot
50   Misc fixes and updates.
51
52   Revision 1.10  2000/12/19 16:18:15  pkot
53   configure script updates and added shared function for configfile reading
54
55
56 */
57
58 #include "misc.h"
59
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <ctype.h>
64 #include <errno.h>
65
66 #include "cfgreader.h"
67
68 struct CFG_Header *CFG_Info;
69
70 /* Read configuration information from a ".INI" style file */
71 struct CFG_Header *CFG_ReadFile(char *filename)
72 {
73         FILE *handle;
74         char *line;
75         char *buf;
76         struct CFG_Header *cfg_info = NULL, *cfg_head = NULL;
77
78         /* Error check */
79         if (filename == NULL) {
80                 return NULL;
81         }
82
83         /* Initialisation */
84         if ((buf = (char *)malloc(255)) == NULL) {
85                 return NULL;
86         }
87     
88         /* Open file */
89         if ((handle = fopen(filename, "r")) == NULL) {
90 #ifdef DEBUG
91                 fprintf( stderr, "CFG_ReadFile - open %s: %s\n", filename, strerror(errno));
92 #endif /* DEBUG */
93                 return NULL;
94         }
95 #ifdef DEBUG
96         else
97                 fprintf( stderr, "Opened configuration file %s\n", filename );
98 #endif /* DEBUG */
99
100         /* Iterate over lines in the file */
101         while (fgets(buf, 255, handle) != NULL) {
102
103                 line = buf;
104
105                 /* Strip leading, trailing whitespace */
106                 while(isspace((int) *line))
107                         line++;
108
109                 while((strlen(line) > 0) && isspace((int) line[strlen(line) - 1]))
110                         line[strlen(line) - 1] = '\0';
111         
112                 /* Ignore blank lines and comments */
113                 if ((*line == '\n') || (*line == '\0') || (*line == '#'))
114                         continue;
115
116                 /* Look for "headings" enclosed in square brackets */
117                 if ((line[0] == '[') && (line[strlen(line) - 1] == ']')) {
118                         struct CFG_Header *heading;
119
120                         /* Allocate new heading entry */
121                         if ((heading = (struct CFG_Header *)malloc(sizeof(*heading))) == NULL) {
122                                 return NULL;
123                         }
124
125                         /* Fill in fields */
126                         memset(heading, '\0', sizeof(*heading));
127             
128                         line++;
129                         line[strlen(line) - 1] = '\0';
130
131                         /* FIXME: strdup is not ANSI C compliant. */
132                         heading->section = strdup(line);
133
134                         /* Add to tail of list  */
135                         heading->prev = cfg_info;
136
137                         if (cfg_info != NULL) {
138                                 cfg_info->next = heading;
139                         } else {
140                                 /* Store copy of head of list for return value */
141                                 cfg_head = heading;
142                         }
143
144                         cfg_info = heading;
145
146 #ifdef DEBUG
147                         fprintf(stderr, "Added new section %s\n", heading->section);
148 #endif
149                         /* Go on to next line */
150
151                         continue;
152                 }
153
154                 /* Process key/value line */
155
156                 if ((strchr(line, '=') != NULL) && cfg_info != NULL) {
157                         struct CFG_Entry *entry;
158                         char *value;
159
160                         /* Allocate new entry */
161                         if ((entry = (struct CFG_Entry *)malloc(sizeof(*entry))) == NULL) {
162                                 return NULL;
163                         }
164
165                         /* Fill in fields */
166                         memset(entry, '\0', sizeof(*entry));
167
168                         value = strchr(line, '=');
169                         *value = '\0';                /* Split string */
170                         value++;
171             
172                         while(isspace((int) *value)) {      /* Remove leading white */
173                                 value++;
174                         }
175
176                         entry->value = strdup(value);
177
178                         while((strlen(line) > 0) && isspace((int) line[strlen(line) - 1])) {
179                                 line[strlen(line) - 1] = '\0';  /* Remove trailing white */
180                         }
181
182                         /* FIXME: strdup is not ANSI C compliant. */
183                         entry->key = strdup(line);
184
185                         /* Add to head of list */
186
187                         entry->next = cfg_info->entries;
188
189                         if (cfg_info->entries != NULL) {
190                                 cfg_info->entries->prev = entry;
191                         }
192
193                         cfg_info->entries = entry;
194
195 #ifdef DEBUG
196                         fprintf(stderr, "Adding key/value %s/%s\n", entry->key, entry->value);
197 #endif
198                         /* Go on to next line */
199                         continue;
200                 }
201
202                         /* Line not part of any heading */
203                 fprintf(stderr, "Orphaned line: %s\n", line);
204         }
205
206         /* Return pointer to configuration information */
207         return cfg_head;
208 }
209
210 /*  Write configuration information to a config file */
211
212 int CFG_WriteFile(struct CFG_Header *cfg, char *filename)
213 {
214   /* Not implemented - tricky to do and preserve comments */
215
216   return 0;
217 }
218
219 /* 
220  * Find the value of a key in a config file.  Return value associated
221  * with key or NULL if no such key exists. 
222  */
223
224 char *CFG_Get(struct CFG_Header *cfg, const char *section, const char *key)
225 {
226         struct CFG_Header *h;
227         struct CFG_Entry *e;
228
229         if ((cfg == NULL) || (section == NULL) || (key == NULL)) {
230                 return NULL;
231         }
232
233         /* Search for section name */
234         for (h = cfg; h != NULL; h = h->next) {
235                 if (strcmp(section, h->section) == 0) {
236                         /* Search for key within section */
237                         for (e = h->entries; e != NULL; e = e->next) {
238                                 if (strcmp(key, e->key) == 0) {
239                                         /* Found! */
240                                         return e->value;
241                                 }
242                         }
243                 }
244         }
245         /* Key not found in section */
246         return NULL;
247 }
248
249 /* 
250  * Return all the entries of the fiven section.
251  */
252
253 void CFG_GetForeach(struct CFG_Header *cfg, const char *section, CFG_GetForeach_func func)
254 {
255         struct CFG_Header *h;
256         struct CFG_Entry *e;
257
258         if ((cfg == NULL) || (section == NULL) || (func == NULL)) {
259                 return;
260         }
261
262         /* Search for section name */
263         for (h = cfg; h != NULL; h = h->next) {
264                 if (strcmp(section, h->section) == 0) {
265                         /* Search for key within section */
266                         for (e = h->entries; e != NULL; e = e->next)
267                                 (*func)(section,e->key,e->value);
268                 }
269         }
270 }
271
272 /*  Set the value of a key in a config file.  Return the new value if
273     the section/key can be found, else return NULL.  */
274
275 char *CFG_Set(struct CFG_Header *cfg, char *section, char *key, 
276                     char *value)
277 {
278         struct CFG_Header *h;
279         struct CFG_Entry *e;
280
281         if ((cfg == NULL) || (section == NULL) || (key == NULL) || 
282             (value == NULL)) {
283                 return NULL;
284         }
285
286         /* Search for section name */
287         for (h = cfg; h != NULL; h = h->next) {
288                 if (strcmp(section, h->section) == 0) {
289                         /* Search for key within section */
290                         for (e = h->entries; e != NULL; e = e->next) {
291                                 if ((e->key != NULL) && strcmp(key, e->key) == 0) {
292                                         /* Found - set value */
293                                         free(e->key);
294                                         /* FIXME: strdup is not ANSI C compliant. */
295                                         e->key = strdup(value);
296                                         return e->value;
297                                 }
298                         }
299                 }
300         }
301         /* Key not found in section */
302         return NULL;    
303 }
304
305 int readconfig(char **model, char **port, char **initlength,
306                char **connection, char **bindir)
307 {
308         char *homedir;
309         char rcfile[200];
310         char *DefaultConnection = "serial";
311         char *DefaultBindir     = "/usr/local/sbin/";
312
313         /* I know that it doesn't belong here but currently there is now generic
314          * application init function anywhere.
315          */
316         setvbuf(stdout, NULL, _IONBF, 0);
317         setvbuf(stderr, NULL, _IONBF, 0);
318
319 #ifdef WIN32
320         homedir = getenv("HOMEDRIVE");
321         strncpy(rcfile, homedir ? homedir : "", 200);
322         homedir = getenv("HOMEPATH");
323         strncat(rcfile, homedir ? homedir : "", 200);
324         strncat(rcfile, "\\_gnokiirc", 200);
325 #else
326         homedir = getenv("HOME");
327         if (homedir) strncpy(rcfile, homedir, 200);
328         strncat(rcfile, "/.gnokiirc", 200);
329 #endif
330
331         /* Try opening .gnokirc from users home directory first */
332         if ((CFG_Info = CFG_ReadFile(rcfile)) == NULL) {
333                 /* It failed so try for /etc/gnokiirc */
334                 if ((CFG_Info = CFG_ReadFile("/etc/gnokiirc")) == NULL) {
335                         /* That failed too so exit */
336                         fprintf(stderr, _("Couldn't open %s or /etc/gnokiirc. Exiting now...\n"), rcfile);
337                         return -1;
338                 }
339         }
340
341         (char *)*model = CFG_Get(CFG_Info, "global", "model");
342         if (!*model) {
343                 fprintf(stderr, _("Config error - no model specified. Exiting now...\n"));
344                 return -2;
345         }
346
347         (char *)*port = CFG_Get(CFG_Info, "global", "port");
348         if (!*port) {
349                 fprintf(stderr, _("Config error - no port specified. Exiting now...\n"));
350                 return -3;
351         }
352
353         (char *)*initlength = CFG_Get(CFG_Info, "global", "initlength");
354         if (!*initlength) (char *)*initlength = "default";
355
356         (char *)*connection = CFG_Get(CFG_Info, "global", "connection");
357         if (!*connection) (char *)*connection = DefaultConnection;
358
359         (char *)*bindir = CFG_Get(CFG_Info, "global", "bindir");
360         if (!*bindir) (char *)*bindir = DefaultBindir;
361
362         return 0;
363 }