blob: 56e40f21d3a949228d4443627da1f474b473419d [file] [log] [blame]
/* Copyright (C) 2008 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
#include "android/utils/ini.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include "android/utils/debug.h"
#include "android/utils/system.h" /* for ASTRDUP */
#include "osdep.h"
/* W() is used to print warnings, D() to print debugging info */
#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(avd_config,__VA_ARGS__)
/* a simple .ini file parser and container for Android
* no sections support. see android/utils/ini.h for
* more details on the supported file format.
*/
typedef struct {
char* key;
char* value;
} IniPair;
struct IniFile {
int numPairs;
int maxPairs;
IniPair* pairs;
};
void
iniFile_free( IniFile* i )
{
int nn;
for (nn = 0; nn < i->numPairs; nn++) {
AFREE(i->pairs[nn].key);
i->pairs[nn].key = NULL;
i->pairs[nn].value = NULL;
}
AFREE(i->pairs);
AFREE(i);
}
static IniFile*
iniFile_alloc( void )
{
IniFile* i;
ANEW0(i);
return i;
}
static void
iniFile_addPair( IniFile* i, const char* key, int keyLen,
const char* value, int valueLen )
{
IniPair* pair;
if (i->numPairs >= i->maxPairs) {
int oldMax = i->maxPairs;
int newMax = oldMax + (oldMax >> 1) + 4;
AARRAY_RENEW(i->pairs, newMax);
i->maxPairs = newMax;
}
pair = i->pairs + i->numPairs;
AARRAY_NEW(pair->key, keyLen + valueLen + 2);
memcpy(pair->key, key, keyLen);
pair->key[keyLen] = 0;
pair->value = pair->key + keyLen + 1;
memcpy(pair->value, value, valueLen);
pair->value[valueLen] = 0;
i->numPairs += 1;
}
const char*
iniFile_getValue( IniFile* i, const char* key )
{
if (i && key) {
int nn;
for (nn = 0; nn < i->numPairs; nn++) {
if (!strcmp(i->pairs[nn].key,key))
return i->pairs[nn].value;
}
}
return NULL;
}
int
iniFile_getPairCount( IniFile* i )
{
return i ? i->numPairs : 0;
}
void
iniFile_getPair( IniFile* i,
int index,
const char* *pKey,
const char* *pValue )
{
const char* key = NULL;
const char* value = NULL;
if (i && index >= 0 && index < i->numPairs) {
key = i->pairs[index].key;
value = i->pairs[index].value;
}
*pKey = key;
*pValue = value;
}
/* NOTE: we avoid using <ctype.h> functions to avoid locale-specific
* behaviour that can be the source of strange bugs.
*/
static const char*
skipSpaces( const char* p )
{
while (*p == ' ' || *p == '\t')
p ++;
return p;
}
static const char*
skipToEOL( const char* p )
{
while (*p && (*p != '\n' && *p != '\r'))
p ++;
if (*p) {
p ++;
if (p[-1] == '\r' && p[0] == '\n')
p ++;
}
return p;
}
static int
isKeyStartChar( int c )
{
return ((unsigned)(c-'a') < 26 ||
(unsigned)(c-'A') < 26 ||
c == '_');
}
static int
isKeyChar( int c )
{
return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-');
}
IniFile*
iniFile_newFromMemory( const char* text, const char* fileName )
{
const char* p = text;
IniFile* ini = iniFile_alloc();
int lineno = 0;
if (!fileName)
fileName = "<unknownFile>";
D("%s: parsing as .ini file", fileName);
while (*p) {
const char* key;
int keyLen;
const char* value;
int valueLen;
lineno += 1;
/* skip leading whitespace */
p = skipSpaces(p);
/* skip comments and empty lines */
if (*p == 0 || *p == ';' || *p == '#' || *p == '\n' || *p == '\r') {
p = skipToEOL(p);
continue;
}
/* check the key name */
key = p++;
if (!isKeyStartChar(*key)) {
p = skipToEOL(p);
W("%s:%d: key name doesn't start with valid character. line ignored",
fileName, lineno);
continue;
}
while (isKeyChar(*p))
p++;
keyLen = p - key;
p = skipSpaces(p);
/* check the equal */
if (*p != '=') {
W("%s:%d: missing expected assignment operator (=). line ignored",
fileName, lineno);
p = skipToEOL(p);
continue;
}
p += 1;
/* skip spaces before the value */
p = skipSpaces(p);
value = p;
/* find the value */
while (*p && (*p != '\n' && *p != '\r'))
p += 1;
/* remove trailing spaces */
while (p > value && (p[-1] == ' ' || p[-1] == '\t'))
p --;
valueLen = p - value;
iniFile_addPair(ini, key, keyLen, value, valueLen);
D("%s:%d: KEY='%.*s' VALUE='%.*s'", fileName, lineno,
keyLen, key, valueLen, value);
p = skipToEOL(p);
}
return ini;
}
IniFile*
iniFile_newFromFile( const char* filepath )
{
FILE* fp = fopen(filepath, "rt");
char* text;
long size;
IniFile* ini = NULL;
if (fp == NULL) {
D("could not open .ini file: %s: %s",
filepath, strerror(errno));
return NULL;
}
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
/* avoid reading a very large file that was passed by mistake
* this threshold is quite liberal.
*/
#define MAX_INI_FILE_SIZE 655360
if (size < 0 || size > MAX_INI_FILE_SIZE) {
W("hardware configuration file '%s' too large (%ld bytes)",
filepath, size);
goto EXIT;
}
/* read the file, add a sentinel at the end of it */
AARRAY_NEW(text, size+1);
fread(text, 1, size, fp);
text[size] = 0;
ini = iniFile_newFromMemory(text, filepath);
AFREE(text);
EXIT:
fclose(fp);
return ini;
}
char*
iniFile_getString( IniFile* f, const char* key )
{
const char* val = iniFile_getValue(f, key);
if (!val)
return NULL;
return ASTRDUP(val);
}
int
iniFile_getInteger( IniFile* f, const char* key, int defaultValue )
{
const char* valueStr = iniFile_getValue(f, key);
int value = defaultValue;
if (valueStr != NULL) {
char* end;
long l = strtol(valueStr, &end, 10);
if (end != NULL && end[0] == 0 && (int)l == l)
value = l;
}
return value;
}
double
iniFile_getDouble( IniFile* f, const char* key, double defaultValue )
{
const char* valueStr = iniFile_getValue(f, key);
double value = defaultValue;
if (valueStr != NULL) {
char* end;
double d = strtod(valueStr, &end);
if (end != NULL && end[0] == 0)
value = d;
}
return value;
}
int
iniFile_getBoolean( IniFile* f, const char* key, const char* defaultValue )
{
const char* value = iniFile_getValue(f, key);
if (!value)
value = defaultValue;
if (!strcmp(value,"1") ||
!strcmp(value,"yes") ||
!strcmp(value,"YES") ||
!strcmp(value,"true") ||
!strcmp(value,"TRUE"))
{
return 1;
}
else
return 0;
}
int64_t
iniFile_getDiskSize( IniFile* f, const char* key, const char* defaultValue )
{
const char* valStr = iniFile_getValue(f, key);
int64_t value = 0;
if (!valStr)
valStr = defaultValue;
if (valStr != NULL) {
char* end;
value = strtoll(valStr, &end, 10);
if (*end == 'k' || *end == 'K')
value *= 1024ULL;
else if (*end == 'm' || *end == 'M')
value *= 1024*1024ULL;
else if (*end == 'g' || *end == 'G')
value *= 1024*1024*1024ULL;
}
return value;
}
int64_t
iniFile_getInt64( IniFile* f, const char* key, int64_t defaultValue )
{
const char* valStr = iniFile_getValue(f, key);
int64_t value = defaultValue;
if (valStr != NULL) {
char* end;
int64_t d;
d = strtoll(valStr, &end, 10);
if (end != NULL && end[0] == 0)
value = d;
}
return value;
}