• 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
iniPair_init(IniPair * pair,const char * key,int keyLen,const char * value,int valueLen)65 iniPair_init( IniPair* pair, const char* key, int keyLen,
66                              const char* value, int valueLen )
67 {
68     AARRAY_NEW(pair->key, keyLen + valueLen + 2);
69     memcpy(pair->key, key, keyLen);
70     pair->key[keyLen] = 0;
71 
72     pair->value = pair->key + keyLen + 1;
73     memcpy(pair->value, value, valueLen);
74     pair->value[valueLen] = 0;
75 }
76 
77 static void
iniPair_replaceValue(IniPair * pair,const char * value)78 iniPair_replaceValue( IniPair* pair, const char* value )
79 {
80     char* key      = pair->key;
81     int   keyLen   = strlen(key);
82     int   valueLen = strlen(value);
83 
84     iniPair_init(pair, key, keyLen, value, valueLen);
85     AFREE(key);
86 }
87 
88 static void
iniFile_addPair(IniFile * i,const char * key,int keyLen,const char * value,int valueLen)89 iniFile_addPair( IniFile*  i,
90                  const char*  key,   int  keyLen,
91                  const char*  value, int  valueLen )
92 {
93     IniPair*  pair;
94 
95     if (i->numPairs >= i->maxPairs) {
96         int       oldMax = i->maxPairs;
97         int       newMax = oldMax + (oldMax >> 1) + 4;
98 
99         AARRAY_RENEW(i->pairs, newMax);
100         i->maxPairs = newMax;
101     }
102 
103     pair = i->pairs + i->numPairs;
104     iniPair_init(pair, key, keyLen, value, valueLen);
105 
106     i->numPairs += 1;
107 }
108 
109 static IniPair*
iniFile_getPair(IniFile * i,const char * key)110 iniFile_getPair( IniFile* i, const char* key )
111 {
112     if (i && key) {
113         int  nn;
114 
115         for (nn = 0; nn < i->numPairs; nn++) {
116             if (!strcmp(i->pairs[nn].key,key))
117                 return &i->pairs[nn];
118         }
119     }
120     return NULL;
121 }
122 
123 const char*
iniFile_getValue(IniFile * i,const char * key)124 iniFile_getValue( IniFile*  i, const char*  key )
125 {
126     IniPair* pair = iniFile_getPair(i, key);
127     if (pair)
128         return pair->value;
129     else
130         return NULL;
131 }
132 
133 int
iniFile_getPairCount(IniFile * i)134 iniFile_getPairCount( IniFile*  i )
135 {
136     return i ? i->numPairs : 0;
137 }
138 
139 /* NOTE: we avoid using <ctype.h> functions to avoid locale-specific
140  *       behaviour that can be the source of strange bugs.
141  */
142 
143 static const char*
skipSpaces(const char * p)144 skipSpaces( const char* p )
145 {
146     while (*p == ' ' || *p == '\t')
147         p ++;
148     return p;
149 }
150 
151 static const char*
skipToEOL(const char * p)152 skipToEOL( const char*  p )
153 {
154     while (*p && (*p != '\n' && *p != '\r'))
155         p ++;
156 
157     if (*p) {
158         p ++;
159         if (p[-1] == '\r' && p[0] == '\n')
160             p ++;
161     }
162     return p;
163 }
164 
165 static int
isKeyStartChar(int c)166 isKeyStartChar( int  c )
167 {
168     return ((unsigned)(c-'a') < 26 ||
169             (unsigned)(c-'A') < 26 ||
170             c == '_');
171 }
172 
173 static int
isKeyChar(int c)174 isKeyChar( int  c )
175 {
176     return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-');
177 }
178 
179 IniFile*
iniFile_newFromMemory(const char * text,const char * fileName)180 iniFile_newFromMemory( const char*  text, const char*  fileName )
181 {
182     const char*  p      = text;
183     IniFile*     ini    = iniFile_alloc();
184     int          lineno = 0;
185 
186     if (!fileName)
187         fileName = "<memoryFile>";
188 
189     D("%s: parsing as .ini file", fileName);
190 
191     while (*p) {
192         const char*  key;
193         int          keyLen;
194         const char*  value;
195         int          valueLen;
196 
197         lineno += 1;
198 
199         /* skip leading whitespace */
200         p = skipSpaces(p);
201 
202         /* skip comments and empty lines */
203         if (*p == 0 || *p == ';' || *p == '#' || *p == '\n' || *p == '\r') {
204             p = skipToEOL(p);
205             continue;
206         }
207 
208         /* check the key name */
209         key = p++;
210         if (!isKeyStartChar(*key)) {
211             p = skipToEOL(p);
212             W("%4d: key name doesn't start with valid character. line ignored",
213               lineno);
214             continue;
215         }
216 
217         while (isKeyChar(*p))
218             p++;
219 
220         keyLen = p - key;
221         p      = skipSpaces(p);
222 
223         /* check the equal */
224         if (*p != '=') {
225             W("%4d: missing expected assignment operator (=). line ignored",
226               lineno);
227             p = skipToEOL(p);
228             continue;
229         }
230         p += 1;
231 
232         /* skip spaces before the value */
233         p     = skipSpaces(p);
234         value = p;
235 
236         /* find the value */
237         while (*p && (*p != '\n' && *p != '\r'))
238             p += 1;
239 
240         /* remove trailing spaces */
241         while (p > value && (p[-1] == ' ' || p[-1] == '\t'))
242             p --;
243 
244         valueLen = p - value;
245 
246         iniFile_addPair(ini, key, keyLen, value, valueLen);
247         D("%4d: KEY='%.*s' VALUE='%.*s'", lineno,
248           keyLen, key, valueLen, value);
249 
250         p = skipToEOL(p);
251     }
252 
253     D("%s: parsing finished", fileName);
254 
255     return ini;
256 }
257 
258 IniFile*
iniFile_newFromFile(const char * filepath)259 iniFile_newFromFile( const char*  filepath )
260 {
261     FILE*        fp = fopen(filepath, "rt");
262     char*        text;
263     long         size;
264     IniFile*     ini = NULL;
265     size_t       len;
266 
267     if (fp == NULL) {
268         D("could not open .ini file: %s: %s",
269           filepath, strerror(errno));
270         return NULL;
271     }
272 
273     fseek(fp, 0, SEEK_END);
274     size = ftell(fp);
275     fseek(fp, 0, SEEK_SET);
276 
277     /* avoid reading a very large file that was passed by mistake
278      * this threshold is quite liberal.
279      */
280 #define  MAX_INI_FILE_SIZE  655360
281 
282     if (size < 0 || size > MAX_INI_FILE_SIZE) {
283         W("hardware configuration file '%s' too large (%ld bytes)",
284           filepath, size);
285         goto EXIT;
286     }
287 
288     /* read the file, add a sentinel at the end of it */
289     AARRAY_NEW(text, size+1);
290     len = fread(text, 1, size, fp);
291     text[len] = 0;
292 
293     ini = iniFile_newFromMemory(text, filepath);
294     AFREE(text);
295 
296 EXIT:
297     fclose(fp);
298     return ini;
299 }
300 
301 /* Common routine for saving IniFile instance to the given file.
302  * Param:
303  *  f - IniFile instance to save.
304  *  filepath - Path to a file where to save the instance.
305  *  strip - If 1, ignore (don't save) pairs with empty values. If 0, save all
306  *      pairs found in the IniFile instance, including the ones that contain
307  *      empty values.
308  * Returns:
309  *  0 on success, -1 on error (see errno for error code)
310  */
311 static int
iniFile_saveToFileCommon(IniFile * f,const char * filepath,int strip)312 iniFile_saveToFileCommon( IniFile*  f, const char*  filepath, int strip )
313 {
314     FILE*  fp = fopen(filepath, "wt");
315     IniPair*  pair    = f->pairs;
316     IniPair*  pairEnd = pair + f->numPairs;
317     int       result  = 0;
318 
319     if (fp == NULL) {
320         D("could not create .ini file: %s: %s",
321           filepath, strerror(errno));
322         return -1;
323     }
324 
325     for ( ; pair < pairEnd; pair++ ) {
326         if ((pair->value && *pair->value) || !strip) {
327             char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
328             p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value);
329             if (fwrite(temp, p - temp, 1, fp) != 1) {
330                 result = -1;
331                 break;
332             }
333         }
334     }
335 
336     fclose(fp);
337     return result;
338 }
339 
340 int
iniFile_saveToFile(IniFile * f,const char * filepath)341 iniFile_saveToFile( IniFile*  f, const char*  filepath )
342 {
343     return iniFile_saveToFileCommon(f, filepath, 0);
344 }
345 
346 int
iniFile_saveToFileClean(IniFile * f,const char * filepath)347 iniFile_saveToFileClean( IniFile*  f, const char*  filepath )
348 {
349     return iniFile_saveToFileCommon(f, filepath, 1);
350 }
351 
352 int
iniFile_getEntry(IniFile * f,int index,char ** key,char ** value)353 iniFile_getEntry(IniFile* f, int index, char** key, char** value)
354 {
355     if (index >= f->numPairs) {
356         D("Index %d exceeds the number of ini file entries %d",
357           index, f->numPairs);
358         return -1;
359     }
360 
361     *key = ASTRDUP(f->pairs[index].key);
362     *value = ASTRDUP(f->pairs[index].value);
363 
364     return 0;
365 }
366 
367 char*
iniFile_getString(IniFile * f,const char * key,const char * defaultValue)368 iniFile_getString( IniFile*  f, const char*  key, const char* defaultValue )
369 {
370     const char*  val = iniFile_getValue(f, key);
371 
372     if (!val) {
373         if (!defaultValue)
374             return NULL;
375         val= defaultValue;
376     }
377 
378     return ASTRDUP(val);
379 }
380 
381 int
iniFile_getInteger(IniFile * f,const char * key,int defaultValue)382 iniFile_getInteger( IniFile*  f, const char*  key, int  defaultValue )
383 {
384     const char*  valueStr = iniFile_getValue(f, key);
385     int          value    = defaultValue;
386 
387     if (valueStr != NULL) {
388         char*  end;
389         long   l = strtol(valueStr, &end, 10);
390         if (end != NULL && end[0] == 0 && (int)l == l)
391             value = l;
392     }
393     return value;
394 }
395 
396 double
iniFile_getDouble(IniFile * f,const char * key,double defaultValue)397 iniFile_getDouble( IniFile*  f, const char*  key, double  defaultValue )
398 {
399     const char*  valueStr = iniFile_getValue(f, key);
400     double       value    = defaultValue;
401 
402     if (valueStr != NULL) {
403         char*   end;
404         double  d = strtod(valueStr, &end);
405         if (end != NULL && end[0] == 0)
406             value = d;
407     }
408     return value;
409 }
410 
411 int
iniFile_getBoolean(IniFile * f,const char * key,const char * defaultValue)412 iniFile_getBoolean( IniFile*  f, const char*  key, const char*  defaultValue )
413 {
414     const char*  value  = iniFile_getValue(f, key);
415 
416     if (!value)
417         value = defaultValue;
418 
419     if (!strcmp(value,"1")    ||
420         !strcmp(value,"yes")  ||
421         !strcmp(value,"YES")  ||
422         !strcmp(value,"true") ||
423         !strcmp(value,"TRUE"))
424     {
425         return 1;
426     }
427     else
428         return 0;
429 }
430 
431 int64_t
iniFile_getDiskSize(IniFile * f,const char * key,const char * defaultValue)432 iniFile_getDiskSize( IniFile*  f, const char*  key, const char*  defaultValue )
433 {
434     const char*  valStr = iniFile_getValue(f, key);
435     int64_t      value  = 0;
436 
437     if (!valStr)
438         valStr = defaultValue;
439 
440     if (valStr != NULL) {
441         char*  end;
442 
443         value = strtoll(valStr, &end, 10);
444         if (*end == 'k' || *end == 'K')
445             value *= 1024ULL;
446         else if (*end == 'm' || *end == 'M')
447             value *= 1024*1024ULL;
448         else if (*end == 'g' || *end == 'G')
449             value *= 1024*1024*1024ULL;
450     }
451     return value;
452 }
453 
454 int64_t
iniFile_getInt64(IniFile * f,const char * key,int64_t defaultValue)455 iniFile_getInt64( IniFile*  f, const char*  key, int64_t  defaultValue )
456 {
457     const char*  valStr = iniFile_getValue(f, key);
458     int64_t      value  = defaultValue;
459 
460     if (valStr != NULL) {
461         char*    end;
462         int64_t  d;
463 
464         d = strtoll(valStr, &end, 10);
465         if (end != NULL && end[0] == 0)
466             value = d;
467     }
468     return value;
469 }
470 
471 void
iniFile_setValue(IniFile * f,const char * key,const char * value)472 iniFile_setValue( IniFile* f, const char* key, const char* value )
473 {
474     IniPair* pair;
475 
476     if (f == NULL || key == NULL || value == NULL)
477         return;
478 
479     pair = iniFile_getPair(f, key);
480     if (pair != NULL) {
481         iniPair_replaceValue(pair, value);
482     } else {
483         iniFile_addPair(f, key, strlen(key), value, strlen(value));
484     }
485 }
486 
487 void
iniFile_setInteger(IniFile * f,const char * key,int value)488 iniFile_setInteger( IniFile* f, const char* key, int value )
489 {
490     char temp[16];
491     snprintf(temp, sizeof temp, "%d", value);
492     iniFile_setValue(f, key, temp);
493 }
494 
495 void
iniFile_setInt64(IniFile * f,const char * key,int64_t value)496 iniFile_setInt64( IniFile* f, const char* key, int64_t value )
497 {
498     char temp[32];
499     snprintf(temp, sizeof temp, "%" PRId64, value);
500     iniFile_setValue(f, key, temp);
501 }
502 
503 void
iniFile_setDouble(IniFile * f,const char * key,double value)504 iniFile_setDouble( IniFile* f, const char* key, double value )
505 {
506     char temp[32];
507     snprintf(temp, sizeof temp, "%g", value);
508     iniFile_setValue(f, key, temp);
509 }
510 
511 void
iniFile_setBoolean(IniFile * f,const char * key,int value)512 iniFile_setBoolean( IniFile* f, const char* key, int value )
513 {
514     iniFile_setValue(f, key, value ? "yes" : "no");
515 }
516 
517 void
iniFile_setDiskSize(IniFile * f,const char * key,int64_t size)518 iniFile_setDiskSize( IniFile* f, const char* key, int64_t size )
519 {
520     char     temp[32];
521     int64_t  divisor = 0;
522     const int64_t  kilo = 1024;
523     const int64_t  mega = 1024*kilo;
524     const int64_t  giga = 1024*mega;
525     char     suffix = '\0';
526 
527     if (size >= giga && !(size % giga)) {
528         divisor = giga;
529         suffix = 'g';
530     }
531     else if (size >= mega && !(size % mega)) {
532         divisor = mega;
533         suffix  = 'm';
534     }
535     else if (size >= kilo && !(size % kilo)) {
536         divisor = kilo;
537         suffix = 'k';
538     }
539     if (divisor) {
540         snprintf(temp, sizeof temp, "%" PRId64 "%c", size/divisor, suffix);
541     } else {
542         snprintf(temp, sizeof temp, "%" PRId64, size);
543     }
544     iniFile_setValue(f, key, temp);
545 }
546