Home | History | Annotate | Download | only in utils
      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
     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*
     56 iniFile_alloc( void )
     57 {
     58     IniFile*  i;
     59 
     60     ANEW0(i);
     61     return i;
     62 }
     63 
     64 static void
     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*
     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
    106 iniFile_getPairCount( IniFile*  i )
    107 {
    108     return i ? i->numPairs : 0;
    109 }
    110 
    111 void
    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*
    133 skipSpaces( const char* p )
    134 {
    135     while (*p == ' ' || *p == '\t')
    136         p ++;
    137     return p;
    138 }
    139 
    140 static const char*
    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
    155 isKeyStartChar( int  c )
    156 {
    157     return ((unsigned)(c-'a') < 26 ||
    158             (unsigned)(c-'A') < 26 ||
    159             c == '_');
    160 }
    161 
    162 static int
    163 isKeyChar( int  c )
    164 {
    165     return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-');
    166 }
    167 
    168 IniFile*
    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*
    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
    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*
    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
    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
    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
    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
    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
    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