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