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 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
     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
     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*
    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*
    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
    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*
    144 skipSpaces( const char* p )
    145 {
    146     while (*p == ' ' || *p == '\t')
    147         p ++;
    148     return p;
    149 }
    150 
    151 static const char*
    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
    166 isKeyStartChar( int  c )
    167 {
    168     return ((unsigned)(c-'a') < 26 ||
    169             (unsigned)(c-'A') < 26 ||
    170             c == '_');
    171 }
    172 
    173 static int
    174 isKeyChar( int  c )
    175 {
    176     return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-');
    177 }
    178 
    179 IniFile*
    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*
    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 int
    302 iniFile_saveToFile( IniFile*  f, const char*  filepath )
    303 {
    304     FILE*  fp = fopen(filepath, "wt");
    305     IniPair*  pair    = f->pairs;
    306     IniPair*  pairEnd = pair + f->numPairs;
    307     int       result  = 0;
    308 
    309     if (fp == NULL) {
    310         D("could not create .ini file: %s: %s",
    311           filepath, strerror(errno));
    312         return -1;
    313     }
    314 
    315     for ( ; pair < pairEnd; pair++ ) {
    316         char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
    317         p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value);
    318         if (fwrite(temp, p - temp, 1, fp) != 1) {
    319             result = -1;
    320             break;
    321         }
    322     }
    323 
    324     fclose(fp);
    325     return result;
    326 }
    327 
    328 char*
    329 iniFile_getString( IniFile*  f, const char*  key, const char* defaultValue )
    330 {
    331     const char*  val = iniFile_getValue(f, key);
    332 
    333     if (!val) {
    334         if (!defaultValue)
    335             return NULL;
    336         val= defaultValue;
    337     }
    338 
    339     return ASTRDUP(val);
    340 }
    341 
    342 int
    343 iniFile_getInteger( IniFile*  f, const char*  key, int  defaultValue )
    344 {
    345     const char*  valueStr = iniFile_getValue(f, key);
    346     int          value    = defaultValue;
    347 
    348     if (valueStr != NULL) {
    349         char*  end;
    350         long   l = strtol(valueStr, &end, 10);
    351         if (end != NULL && end[0] == 0 && (int)l == l)
    352             value = l;
    353     }
    354     return value;
    355 }
    356 
    357 double
    358 iniFile_getDouble( IniFile*  f, const char*  key, double  defaultValue )
    359 {
    360     const char*  valueStr = iniFile_getValue(f, key);
    361     double       value    = defaultValue;
    362 
    363     if (valueStr != NULL) {
    364         char*   end;
    365         double  d = strtod(valueStr, &end);
    366         if (end != NULL && end[0] == 0)
    367             value = d;
    368     }
    369     return value;
    370 }
    371 
    372 int
    373 iniFile_getBoolean( IniFile*  f, const char*  key, const char*  defaultValue )
    374 {
    375     const char*  value  = iniFile_getValue(f, key);
    376 
    377     if (!value)
    378         value = defaultValue;
    379 
    380     if (!strcmp(value,"1")    ||
    381         !strcmp(value,"yes")  ||
    382         !strcmp(value,"YES")  ||
    383         !strcmp(value,"true") ||
    384         !strcmp(value,"TRUE"))
    385     {
    386         return 1;
    387     }
    388     else
    389         return 0;
    390 }
    391 
    392 int64_t
    393 iniFile_getDiskSize( IniFile*  f, const char*  key, const char*  defaultValue )
    394 {
    395     const char*  valStr = iniFile_getValue(f, key);
    396     int64_t      value  = 0;
    397 
    398     if (!valStr)
    399         valStr = defaultValue;
    400 
    401     if (valStr != NULL) {
    402         char*  end;
    403 
    404         value = strtoll(valStr, &end, 10);
    405         if (*end == 'k' || *end == 'K')
    406             value *= 1024ULL;
    407         else if (*end == 'm' || *end == 'M')
    408             value *= 1024*1024ULL;
    409         else if (*end == 'g' || *end == 'G')
    410             value *= 1024*1024*1024ULL;
    411     }
    412     return value;
    413 }
    414 
    415 int64_t
    416 iniFile_getInt64( IniFile*  f, const char*  key, int64_t  defaultValue )
    417 {
    418     const char*  valStr = iniFile_getValue(f, key);
    419     int64_t      value  = defaultValue;
    420 
    421     if (valStr != NULL) {
    422         char*    end;
    423         int64_t  d;
    424 
    425         d = strtoll(valStr, &end, 10);
    426         if (end != NULL && end[0] == 0)
    427             value = d;
    428     }
    429     return value;
    430 }
    431 
    432 void
    433 iniFile_setValue( IniFile* f, const char* key, const char* value )
    434 {
    435     IniPair* pair;
    436 
    437     if (f == NULL || key == NULL || value == NULL)
    438         return;
    439 
    440     pair = iniFile_getPair(f, key);
    441     if (pair != NULL) {
    442         iniPair_replaceValue(pair, value);
    443     } else {
    444         iniFile_addPair(f, key, strlen(key), value, strlen(value));
    445     }
    446 }
    447 
    448 void
    449 iniFile_setInteger( IniFile* f, const char* key, int value )
    450 {
    451     char temp[16];
    452     snprintf(temp, sizeof temp, "%d", value);
    453     iniFile_setValue(f, key, temp);
    454 }
    455 
    456 void
    457 iniFile_setInt64( IniFile* f, const char* key, int64_t value )
    458 {
    459     char temp[32];
    460     snprintf(temp, sizeof temp, "%" PRId64, value);
    461     iniFile_setValue(f, key, temp);
    462 }
    463 
    464 void
    465 iniFile_setDouble( IniFile* f, const char* key, double value )
    466 {
    467     char temp[32];
    468     snprintf(temp, sizeof temp, "%g", value);
    469     iniFile_setValue(f, key, temp);
    470 }
    471 
    472 void
    473 iniFile_setBoolean( IniFile* f, const char* key, int value )
    474 {
    475     iniFile_setValue(f, key, value ? "yes" : "no");
    476 }
    477 
    478 void
    479 iniFile_setDiskSize( IniFile* f, const char* key, int64_t size )
    480 {
    481     char     temp[32];
    482     int64_t  divisor = 0;
    483     const int64_t  kilo = 1024;
    484     const int64_t  mega = 1024*kilo;
    485     const int64_t  giga = 1024*mega;
    486     char     suffix = '\0';
    487 
    488     if (size >= giga && !(size % giga)) {
    489         divisor = giga;
    490         suffix = 'g';
    491     }
    492     else if (size >= mega && !(size % mega)) {
    493         divisor = mega;
    494         suffix  = 'm';
    495     }
    496     else if (size >= kilo && !(size % kilo)) {
    497         divisor = kilo;
    498         suffix = 'k';
    499     }
    500     if (divisor) {
    501         snprintf(temp, sizeof temp, "%" PRId64 "%c", size/divisor, suffix);
    502     } else {
    503         snprintf(temp, sizeof temp, "%" PRId64, size);
    504     }
    505     iniFile_setValue(f, key, temp);
    506 }
    507