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
iniFile_addPair(IniFile * i,const char * key,int keyLen,const char * value,int valueLen)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*
iniFile_getValue(IniFile * i,const char * key)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
iniFile_getPairCount(IniFile * i)106 iniFile_getPairCount( IniFile* i )
107 {
108 return i ? i->numPairs : 0;
109 }
110
111 void
iniFile_getPair(IniFile * i,int index,const char ** pKey,const char ** pValue)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*
skipSpaces(const char * p)133 skipSpaces( const char* p )
134 {
135 while (*p == ' ' || *p == '\t')
136 p ++;
137 return p;
138 }
139
140 static const char*
skipToEOL(const char * p)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
isKeyStartChar(int c)155 isKeyStartChar( int c )
156 {
157 return ((unsigned)(c-'a') < 26 ||
158 (unsigned)(c-'A') < 26 ||
159 c == '_');
160 }
161
162 static int
isKeyChar(int c)163 isKeyChar( int c )
164 {
165 return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-');
166 }
167
168 IniFile*
iniFile_newFromMemory(const char * text,const char * fileName)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*
iniFile_newFromFile(const char * filepath)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
iniFile_saveToFile(IniFile * f,const char * filepath)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*
iniFile_getString(IniFile * f,const char * key)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
iniFile_getInteger(IniFile * f,const char * key,int defaultValue)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
iniFile_getDouble(IniFile * f,const char * key,double defaultValue)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
iniFile_getBoolean(IniFile * f,const char * key,const char * defaultValue)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
iniFile_getDiskSize(IniFile * f,const char * key,const char * defaultValue)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
iniFile_getInt64(IniFile * f,const char * key,int64_t defaultValue)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