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
iniFile_free(IniFile * i)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*
iniFile_alloc(void)56 iniFile_alloc( void )
57 {
58 IniFile* i;
59
60 ANEW0(i);
61 return i;
62 }
63
64 static void
iniPair_init(IniPair * pair,const char * key,int keyLen,const char * value,int valueLen)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
iniPair_replaceValue(IniPair * pair,const char * value)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
iniFile_addPair(IniFile * i,const char * key,int keyLen,const char * value,int valueLen)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*
iniFile_getPair(IniFile * i,const char * key)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*
iniFile_getValue(IniFile * i,const char * key)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
iniFile_getPairCount(IniFile * i)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*
skipSpaces(const char * p)144 skipSpaces( const char* p )
145 {
146 while (*p == ' ' || *p == '\t')
147 p ++;
148 return p;
149 }
150
151 static const char*
skipToEOL(const char * p)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
isKeyStartChar(int c)166 isKeyStartChar( int c )
167 {
168 return ((unsigned)(c-'a') < 26 ||
169 (unsigned)(c-'A') < 26 ||
170 c == '_');
171 }
172
173 static int
isKeyChar(int c)174 isKeyChar( int c )
175 {
176 return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-');
177 }
178
179 IniFile*
iniFile_newFromMemory(const char * text,const char * fileName)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*
iniFile_newFromFile(const char * filepath)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
iniFile_saveToFileCommon(IniFile * f,const char * filepath,int strip)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
iniFile_saveToFile(IniFile * f,const char * filepath)341 iniFile_saveToFile( IniFile* f, const char* filepath )
342 {
343 return iniFile_saveToFileCommon(f, filepath, 0);
344 }
345
346 int
iniFile_saveToFileClean(IniFile * f,const char * filepath)347 iniFile_saveToFileClean( IniFile* f, const char* filepath )
348 {
349 return iniFile_saveToFileCommon(f, filepath, 1);
350 }
351
352 int
iniFile_getEntry(IniFile * f,int index,char ** key,char ** value)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*
iniFile_getString(IniFile * f,const char * key,const char * defaultValue)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
iniFile_getInteger(IniFile * f,const char * key,int defaultValue)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
iniFile_getDouble(IniFile * f,const char * key,double defaultValue)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
iniFile_getBoolean(IniFile * f,const char * key,const char * defaultValue)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
iniFile_getDiskSize(IniFile * f,const char * key,const char * defaultValue)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
iniFile_getInt64(IniFile * f,const char * key,int64_t defaultValue)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
iniFile_setValue(IniFile * f,const char * key,const char * value)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
iniFile_setInteger(IniFile * f,const char * key,int value)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
iniFile_setInt64(IniFile * f,const char * key,int64_t value)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
iniFile_setDouble(IniFile * f,const char * key,double value)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
iniFile_setBoolean(IniFile * f,const char * key,int value)512 iniFile_setBoolean( IniFile* f, const char* key, int value )
513 {
514 iniFile_setValue(f, key, value ? "yes" : "no");
515 }
516
517 void
iniFile_setDiskSize(IniFile * f,const char * key,int64_t size)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