• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (C) 2008 The Android Open Source Project
2 **
3 ** This software is licensed under the terms of the GNU General Public
4 ** License version 2, as published by the Free Software Foundation, and
5 ** may be copied, distributed, and modified under those terms.
6 **
7 ** This program is distributed in the hope that it will be useful,
8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 ** GNU General Public License for more details.
11 */
12 #include "android/utils/ini.h"
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <limits.h>
17 #include <errno.h>
18 #include "android/utils/debug.h"
19 #include "android/utils/system.h" /* for ASTRDUP */
20 #include "android/utils/bufprint.h"
21 #include "osdep.h"
22 
23 /* W() is used to print warnings, D() to print debugging info */
24 #define  W(...)   dwarning(__VA_ARGS__)
25 #define  D(...)   VERBOSE_PRINT(avd_config,__VA_ARGS__)
26 
27 /* a simple .ini file parser and container for Android
28  * no sections support. see android/utils/ini.h for
29  * more details on the supported file format.
30  */
31 typedef struct {
32     char*  key;
33     char*  value;
34 } IniPair;
35 
36 struct IniFile {
37     int       numPairs;
38     int       maxPairs;
39     IniPair*  pairs;
40 };
41 
42 void
iniFile_free(IniFile * i)43 iniFile_free( IniFile*  i )
44 {
45     int  nn;
46     for (nn = 0; nn < i->numPairs; nn++) {
47         AFREE(i->pairs[nn].key);
48         i->pairs[nn].key   = NULL;
49         i->pairs[nn].value = NULL;
50     }
51     AFREE(i->pairs);
52     AFREE(i);
53 }
54 
55 static IniFile*
iniFile_alloc(void)56 iniFile_alloc( void )
57 {
58     IniFile*  i;
59 
60     ANEW0(i);
61     return i;
62 }
63 
64 static void
iniFile_addPair(IniFile * i,const char * key,int keyLen,const char * value,int valueLen)65 iniFile_addPair( IniFile*  i, const char*  key,   int  keyLen,
66                                     const char*  value, int  valueLen )
67 {
68     IniPair*  pair;
69 
70     if (i->numPairs >= i->maxPairs) {
71         int       oldMax = i->maxPairs;
72         int       newMax = oldMax + (oldMax >> 1) + 4;
73 
74         AARRAY_RENEW(i->pairs, newMax);
75         i->maxPairs = newMax;
76     }
77 
78     pair = i->pairs + i->numPairs;
79 
80     AARRAY_NEW(pair->key, keyLen + valueLen + 2);
81     memcpy(pair->key, key, keyLen);
82     pair->key[keyLen] = 0;
83 
84     pair->value = pair->key + keyLen + 1;
85     memcpy(pair->value, value, valueLen);
86     pair->value[valueLen] = 0;
87 
88     i->numPairs += 1;
89 }
90 
91 const char*
iniFile_getValue(IniFile * i,const char * key)92 iniFile_getValue( IniFile*  i, const char*  key )
93 {
94     if (i && key) {
95         int  nn;
96 
97         for (nn = 0; nn < i->numPairs; nn++) {
98             if (!strcmp(i->pairs[nn].key,key))
99                 return i->pairs[nn].value;
100         }
101     }
102     return NULL;
103 }
104 
105 int
iniFile_getPairCount(IniFile * i)106 iniFile_getPairCount( IniFile*  i )
107 {
108     return i ? i->numPairs : 0;
109 }
110 
111 void
iniFile_getPair(IniFile * i,int index,const char ** pKey,const char ** pValue)112 iniFile_getPair( IniFile*   i,
113                     int           index,
114                     const char*  *pKey,
115                     const char*  *pValue )
116 {
117     const char*  key   = NULL;
118     const char*  value = NULL;
119 
120     if (i && index >= 0 && index < i->numPairs) {
121         key   = i->pairs[index].key;
122         value = i->pairs[index].value;
123     }
124     *pKey   = key;
125     *pValue = value;
126 }
127 
128 /* NOTE: we avoid using <ctype.h> functions to avoid locale-specific
129  *       behaviour that can be the source of strange bugs.
130  */
131 
132 static const char*
skipSpaces(const char * p)133 skipSpaces( const char* p )
134 {
135     while (*p == ' ' || *p == '\t')
136         p ++;
137     return p;
138 }
139 
140 static const char*
skipToEOL(const char * p)141 skipToEOL( const char*  p )
142 {
143     while (*p && (*p != '\n' && *p != '\r'))
144         p ++;
145 
146     if (*p) {
147         p ++;
148         if (p[-1] == '\r' && p[0] == '\n')
149             p ++;
150     }
151     return p;
152 }
153 
154 static int
isKeyStartChar(int c)155 isKeyStartChar( int  c )
156 {
157     return ((unsigned)(c-'a') < 26 ||
158             (unsigned)(c-'A') < 26 ||
159             c == '_');
160 }
161 
162 static int
isKeyChar(int c)163 isKeyChar( int  c )
164 {
165     return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-');
166 }
167 
168 IniFile*
iniFile_newFromMemory(const char * text,const char * fileName)169 iniFile_newFromMemory( const char*  text, const char*  fileName )
170 {
171     const char*  p      = text;
172     IniFile*     ini    = iniFile_alloc();
173     int          lineno = 0;
174 
175     if (!fileName)
176         fileName = "<memoryFile>";
177 
178     D("%s: parsing as .ini file", fileName);
179 
180     while (*p) {
181         const char*  key;
182         int          keyLen;
183         const char*  value;
184         int          valueLen;
185 
186         lineno += 1;
187 
188         /* skip leading whitespace */
189         p = skipSpaces(p);
190 
191         /* skip comments and empty lines */
192         if (*p == 0 || *p == ';' || *p == '#' || *p == '\n' || *p == '\r') {
193             p = skipToEOL(p);
194             continue;
195         }
196 
197         /* check the key name */
198         key = p++;
199         if (!isKeyStartChar(*key)) {
200             p = skipToEOL(p);
201             W("%4d: key name doesn't start with valid character. line ignored",
202               lineno);
203             continue;
204         }
205 
206         while (isKeyChar(*p))
207             p++;
208 
209         keyLen = p - key;
210         p      = skipSpaces(p);
211 
212         /* check the equal */
213         if (*p != '=') {
214             W("%4d: missing expected assignment operator (=). line ignored",
215               lineno);
216             p = skipToEOL(p);
217             continue;
218         }
219         p += 1;
220 
221         /* skip spaces before the value */
222         p     = skipSpaces(p);
223         value = p;
224 
225         /* find the value */
226         while (*p && (*p != '\n' && *p != '\r'))
227             p += 1;
228 
229         /* remove trailing spaces */
230         while (p > value && (p[-1] == ' ' || p[-1] == '\t'))
231             p --;
232 
233         valueLen = p - value;
234 
235         iniFile_addPair(ini, key, keyLen, value, valueLen);
236         D("%4d: KEY='%.*s' VALUE='%.*s'", lineno,
237           keyLen, key, valueLen, value);
238 
239         p = skipToEOL(p);
240     }
241 
242     D("%s: parsing finished", fileName);
243 
244     return ini;
245 }
246 
247 IniFile*
iniFile_newFromFile(const char * filepath)248 iniFile_newFromFile( const char*  filepath )
249 {
250     FILE*        fp = fopen(filepath, "rt");
251     char*        text;
252     long         size;
253     IniFile*     ini = NULL;
254 
255     if (fp == NULL) {
256         D("could not open .ini file: %s: %s",
257           filepath, strerror(errno));
258         return NULL;
259     }
260 
261     fseek(fp, 0, SEEK_END);
262     size = ftell(fp);
263     fseek(fp, 0, SEEK_SET);
264 
265     /* avoid reading a very large file that was passed by mistake
266      * this threshold is quite liberal.
267      */
268 #define  MAX_INI_FILE_SIZE  655360
269 
270     if (size < 0 || size > MAX_INI_FILE_SIZE) {
271         W("hardware configuration file '%s' too large (%ld bytes)",
272           filepath, size);
273         goto EXIT;
274     }
275 
276     /* read the file, add a sentinel at the end of it */
277     AARRAY_NEW(text, size+1);
278     fread(text, 1, size, fp);
279     text[size] = 0;
280 
281     ini = iniFile_newFromMemory(text, filepath);
282     AFREE(text);
283 
284 EXIT:
285     fclose(fp);
286     return ini;
287 }
288 
289 int
iniFile_saveToFile(IniFile * f,const char * filepath)290 iniFile_saveToFile( IniFile*  f, const char*  filepath )
291 {
292     FILE*  fp = fopen(filepath, "wt");
293     IniPair*  pair    = f->pairs;
294     IniPair*  pairEnd = pair + f->numPairs;
295     int       result  = 0;
296 
297     if (fp == NULL) {
298         D("could not create .ini file: %s: %s",
299           filepath, strerror(errno));
300         return -1;
301     }
302 
303     for ( ; pair < pairEnd; pair++ ) {
304         char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
305         p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value);
306         if (fwrite(temp, p - temp, 1, fp) != 1) {
307             result = -1;
308             break;
309         }
310     }
311 
312     fclose(fp);
313     return result;
314 }
315 
316 char*
iniFile_getString(IniFile * f,const char * key)317 iniFile_getString( IniFile*  f, const char*  key )
318 {
319     const char*  val = iniFile_getValue(f, key);
320 
321     if (!val)
322         return NULL;
323 
324     return ASTRDUP(val);
325 }
326 
327 int
iniFile_getInteger(IniFile * f,const char * key,int defaultValue)328 iniFile_getInteger( IniFile*  f, const char*  key, int  defaultValue )
329 {
330     const char*  valueStr = iniFile_getValue(f, key);
331     int          value    = defaultValue;
332 
333     if (valueStr != NULL) {
334         char*  end;
335         long   l = strtol(valueStr, &end, 10);
336         if (end != NULL && end[0] == 0 && (int)l == l)
337             value = l;
338     }
339     return value;
340 }
341 
342 double
iniFile_getDouble(IniFile * f,const char * key,double defaultValue)343 iniFile_getDouble( IniFile*  f, const char*  key, double  defaultValue )
344 {
345     const char*  valueStr = iniFile_getValue(f, key);
346     double       value    = defaultValue;
347 
348     if (valueStr != NULL) {
349         char*   end;
350         double  d = strtod(valueStr, &end);
351         if (end != NULL && end[0] == 0)
352             value = d;
353     }
354     return value;
355 }
356 
357 int
iniFile_getBoolean(IniFile * f,const char * key,const char * defaultValue)358 iniFile_getBoolean( IniFile*  f, const char*  key, const char*  defaultValue )
359 {
360     const char*  value  = iniFile_getValue(f, key);
361 
362     if (!value)
363         value = defaultValue;
364 
365     if (!strcmp(value,"1")    ||
366         !strcmp(value,"yes")  ||
367         !strcmp(value,"YES")  ||
368         !strcmp(value,"true") ||
369         !strcmp(value,"TRUE"))
370     {
371         return 1;
372     }
373     else
374         return 0;
375 }
376 
377 int64_t
iniFile_getDiskSize(IniFile * f,const char * key,const char * defaultValue)378 iniFile_getDiskSize( IniFile*  f, const char*  key, const char*  defaultValue )
379 {
380     const char*  valStr = iniFile_getValue(f, key);
381     int64_t      value  = 0;
382 
383     if (!valStr)
384         valStr = defaultValue;
385 
386     if (valStr != NULL) {
387         char*  end;
388 
389         value = strtoll(valStr, &end, 10);
390         if (*end == 'k' || *end == 'K')
391             value *= 1024ULL;
392         else if (*end == 'm' || *end == 'M')
393             value *= 1024*1024ULL;
394         else if (*end == 'g' || *end == 'G')
395             value *= 1024*1024*1024ULL;
396     }
397     return value;
398 }
399 
400 int64_t
iniFile_getInt64(IniFile * f,const char * key,int64_t defaultValue)401 iniFile_getInt64( IniFile*  f, const char*  key, int64_t  defaultValue )
402 {
403     const char*  valStr = iniFile_getValue(f, key);
404     int64_t      value  = defaultValue;
405 
406     if (valStr != NULL) {
407         char*    end;
408         int64_t  d;
409 
410         d = strtoll(valStr, &end, 10);
411         if (end != NULL && end[0] == 0)
412             value = d;
413     }
414     return value;
415 }
416 
417