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 /* 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
    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
    341 iniFile_saveToFile( IniFile*  f, const char*  filepath )
    342 {
    343     return iniFile_saveToFileCommon(f, filepath, 0);
    344 }
    345 
    346 int
    347 iniFile_saveToFileClean( IniFile*  f, const char*  filepath )
    348 {
    349     return iniFile_saveToFileCommon(f, filepath, 1);
    350 }
    351 
    352 int
    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*
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    512 iniFile_setBoolean( IniFile* f, const char* key, int value )
    513 {
    514     iniFile_setValue(f, key, value ? "yes" : "no");
    515 }
    516 
    517 void
    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