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