blob: 317d233ae11d079665ef1e8807222e13bb54840c [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* 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 */
31typedef struct {
32 char* key;
33 char* value;
34} IniPair;
35
36struct IniFile {
37 int numPairs;
38 int maxPairs;
39 IniPair* pairs;
40};
41
42void
43iniFile_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
55static IniFile*
56iniFile_alloc( void )
57{
58 IniFile* i;
59
60 ANEW0(i);
61 return i;
62}
63
64static void
65iniFile_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
91const char*
92iniFile_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
105int
106iniFile_getPairCount( IniFile* i )
107{
108 return i ? i->numPairs : 0;
109}
110
111void
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200112iniFile_getPair( IniFile* i,
113 int index,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800114 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
132static const char*
133skipSpaces( const char* p )
134{
135 while (*p == ' ' || *p == '\t')
136 p ++;
137 return p;
138}
139
140static const char*
141skipToEOL( 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
154static int
155isKeyStartChar( int c )
156{
157 return ((unsigned)(c-'a') < 26 ||
158 (unsigned)(c-'A') < 26 ||
159 c == '_');
160}
161
162static int
163isKeyChar( int c )
164{
165 return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-');
166}
167
168IniFile*
169iniFile_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
247IniFile*
248iniFile_newFromFile( const char* filepath )
249{
250 FILE* fp = fopen(filepath, "rt");
251 char* text;
252 long size;
253 IniFile* ini = NULL;
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200254 size_t len;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800255
256 if (fp == NULL) {
257 D("could not open .ini file: %s: %s",
258 filepath, strerror(errno));
259 return NULL;
260 }
261
262 fseek(fp, 0, SEEK_END);
263 size = ftell(fp);
264 fseek(fp, 0, SEEK_SET);
265
266 /* avoid reading a very large file that was passed by mistake
267 * this threshold is quite liberal.
268 */
269#define MAX_INI_FILE_SIZE 655360
270
271 if (size < 0 || size > MAX_INI_FILE_SIZE) {
272 W("hardware configuration file '%s' too large (%ld bytes)",
273 filepath, size);
274 goto EXIT;
275 }
276
277 /* read the file, add a sentinel at the end of it */
278 AARRAY_NEW(text, size+1);
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200279 len = fread(text, 1, size, fp);
280 text[len] = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800281
282 ini = iniFile_newFromMemory(text, filepath);
283 AFREE(text);
284
285EXIT:
286 fclose(fp);
287 return ini;
288}
289
290int
291iniFile_saveToFile( IniFile* f, const char* filepath )
292{
293 FILE* fp = fopen(filepath, "wt");
294 IniPair* pair = f->pairs;
295 IniPair* pairEnd = pair + f->numPairs;
296 int result = 0;
297
298 if (fp == NULL) {
299 D("could not create .ini file: %s: %s",
300 filepath, strerror(errno));
301 return -1;
302 }
303
304 for ( ; pair < pairEnd; pair++ ) {
305 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
306 p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value);
307 if (fwrite(temp, p - temp, 1, fp) != 1) {
308 result = -1;
309 break;
310 }
311 }
312
313 fclose(fp);
314 return result;
315}
316
317char*
318iniFile_getString( IniFile* f, const char* key )
319{
320 const char* val = iniFile_getValue(f, key);
321
322 if (!val)
323 return NULL;
324
325 return ASTRDUP(val);
326}
327
328int
329iniFile_getInteger( IniFile* f, const char* key, int defaultValue )
330{
331 const char* valueStr = iniFile_getValue(f, key);
332 int value = defaultValue;
333
334 if (valueStr != NULL) {
335 char* end;
336 long l = strtol(valueStr, &end, 10);
337 if (end != NULL && end[0] == 0 && (int)l == l)
338 value = l;
339 }
340 return value;
341}
342
343double
344iniFile_getDouble( IniFile* f, const char* key, double defaultValue )
345{
346 const char* valueStr = iniFile_getValue(f, key);
347 double value = defaultValue;
348
349 if (valueStr != NULL) {
350 char* end;
351 double d = strtod(valueStr, &end);
352 if (end != NULL && end[0] == 0)
353 value = d;
354 }
355 return value;
356}
357
358int
359iniFile_getBoolean( IniFile* f, const char* key, const char* defaultValue )
360{
361 const char* value = iniFile_getValue(f, key);
362
363 if (!value)
364 value = defaultValue;
365
366 if (!strcmp(value,"1") ||
367 !strcmp(value,"yes") ||
368 !strcmp(value,"YES") ||
369 !strcmp(value,"true") ||
370 !strcmp(value,"TRUE"))
371 {
372 return 1;
373 }
374 else
375 return 0;
376}
377
378int64_t
379iniFile_getDiskSize( IniFile* f, const char* key, const char* defaultValue )
380{
381 const char* valStr = iniFile_getValue(f, key);
382 int64_t value = 0;
383
384 if (!valStr)
385 valStr = defaultValue;
386
387 if (valStr != NULL) {
388 char* end;
389
390 value = strtoll(valStr, &end, 10);
391 if (*end == 'k' || *end == 'K')
392 value *= 1024ULL;
393 else if (*end == 'm' || *end == 'M')
394 value *= 1024*1024ULL;
395 else if (*end == 'g' || *end == 'G')
396 value *= 1024*1024*1024ULL;
397 }
398 return value;
399}
400
401int64_t
402iniFile_getInt64( IniFile* f, const char* key, int64_t defaultValue )
403{
404 const char* valStr = iniFile_getValue(f, key);
405 int64_t value = defaultValue;
406
407 if (valStr != NULL) {
408 char* end;
409 int64_t d;
410
411 d = strtoll(valStr, &end, 10);
412 if (end != NULL && end[0] == 0)
413 value = d;
414 }
415 return value;
416}
417