2009-07-26 07:57:03 -07:00
|
|
|
/*
|
2010-12-29 13:17:12 -08:00
|
|
|
This file is part of Warzone 2100.
|
|
|
|
Copyright (C) 2010 Freddie Witherden <freddie@witherden.org>
|
|
|
|
Copyright (C) 2010 Warzone 2100 Project
|
|
|
|
|
|
|
|
Warzone 2100 is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Warzone 2100 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.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Warzone 2100; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
2009-07-26 07:57:03 -07:00
|
|
|
*/
|
2010-12-29 13:17:12 -08:00
|
|
|
|
2009-07-26 07:57:03 -07:00
|
|
|
#include <ctype.h>
|
2009-08-05 11:06:28 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
#include "lib/framework/frame.h"
|
2009-08-11 13:17:18 -07:00
|
|
|
#include "lib/framework/physfs_ext.h"
|
2010-12-29 13:17:12 -08:00
|
|
|
#include "lib/framework/stdio_ext.h"
|
2009-08-05 11:06:28 -07:00
|
|
|
|
2009-07-26 07:57:03 -07:00
|
|
|
#include "iniparser.h"
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
struct _inifile_entry
|
|
|
|
{
|
|
|
|
/// Section pointer for entry
|
|
|
|
char *sec;
|
|
|
|
/// Key of the entry
|
|
|
|
char *key;
|
|
|
|
/// Value of the entry
|
|
|
|
char *value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _inifile
|
|
|
|
{
|
|
|
|
/// The number of unique sections
|
|
|
|
int nsec;
|
|
|
|
/// The names of the sections in the inifile
|
|
|
|
char **sec;
|
|
|
|
|
|
|
|
// The current section number
|
|
|
|
char *currsec;
|
|
|
|
|
|
|
|
/// The number of entries in the file
|
|
|
|
int n;
|
|
|
|
/// The amount of space allocated for the keys and values
|
|
|
|
int size;
|
|
|
|
|
|
|
|
/// Entries
|
|
|
|
struct _inifile_entry *entry;
|
|
|
|
|
|
|
|
/// Path of the inifile
|
|
|
|
char *path;
|
|
|
|
};
|
2009-07-26 07:57:03 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This enum stores the status for each parsed line (internal use only).
|
|
|
|
*/
|
2010-12-29 13:17:12 -08:00
|
|
|
typedef enum
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
|
|
|
LINE_UNPROCESSED,
|
|
|
|
LINE_ERROR,
|
|
|
|
LINE_EMPTY,
|
|
|
|
LINE_COMMENT,
|
|
|
|
LINE_SECTION,
|
|
|
|
LINE_VALUE
|
2010-12-29 13:17:12 -08:00
|
|
|
} LINE_STATUS;
|
2009-07-26 07:57:03 -07:00
|
|
|
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
#define ASCIILINESZ (1024)
|
|
|
|
#define INIFILE_MIN_SIZE 64
|
|
|
|
|
|
|
|
// Utility function prototypes
|
|
|
|
static LINE_STATUS parse_line(const char *input_line,
|
|
|
|
char *section,
|
|
|
|
char *key,
|
|
|
|
char *value);
|
|
|
|
|
|
|
|
static char *strstrip(const char *s);
|
|
|
|
static char *strlwc(const char *s);
|
|
|
|
|
|
|
|
inifile *inifile_new()
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
inifile *inif = new inifile;
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
inif->nsec = 0;
|
|
|
|
inif->sec = NULL;
|
|
|
|
|
|
|
|
inif->currsec = NULL;
|
|
|
|
|
|
|
|
inif->n = 0;
|
|
|
|
inif->size = INIFILE_MIN_SIZE;
|
|
|
|
inif->entry = static_cast<struct _inifile_entry *>(
|
|
|
|
malloc(inif->size * sizeof(*inif->entry)));
|
|
|
|
|
|
|
|
ASSERT(inif->entry, "Failed to allocate inifile memory.");
|
|
|
|
|
|
|
|
inif->path = NULL;
|
|
|
|
|
|
|
|
return inif;
|
|
|
|
}
|
|
|
|
|
|
|
|
inifile *inifile_load(const char *path)
|
|
|
|
{
|
|
|
|
PHYSFS_File *in;
|
|
|
|
|
|
|
|
char line[ASCIILINESZ + 1];
|
|
|
|
char section[ASCIILINESZ + 1];
|
|
|
|
char key[ASCIILINESZ + 1];
|
|
|
|
char val[ASCIILINESZ + 1];
|
|
|
|
|
|
|
|
int last = 0;
|
|
|
|
int len;
|
|
|
|
int lineno = 0;
|
|
|
|
int errors = 0;
|
|
|
|
|
|
|
|
inifile *inif;
|
|
|
|
|
|
|
|
if ((in = PHYSFS_openRead(path)) == NULL)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
return NULL;
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
inif = inifile_new();
|
|
|
|
inif->path = strdup(path);
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
ASSERT(inif->path, "Failed to allocate inifile memory.");
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
memset(line, 0, ASCIILINESZ + 1);
|
|
|
|
memset(section, 0, ASCIILINESZ + 1);
|
|
|
|
memset(key, 0, ASCIILINESZ + 1);
|
|
|
|
memset(val, 0, ASCIILINESZ + 1);
|
|
|
|
last = 0 ;
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Loop over each line in the file
|
|
|
|
while (PHYSFS_fgets(line + last, ASCIILINESZ - last, in) != NULL)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
lineno++;
|
|
|
|
len = strlen(line) - 1;
|
|
|
|
|
|
|
|
// Ensure that we read the entire line
|
|
|
|
if (line[len] != '\n')
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"iniparser: input line too long in %s (%d)\n",
|
|
|
|
path,
|
|
|
|
lineno);
|
|
|
|
|
|
|
|
inifile_delete(inif);
|
|
|
|
inif = NULL;
|
|
|
|
|
2009-08-15 03:50:26 -07:00
|
|
|
break;
|
2010-12-29 13:17:12 -08:00
|
|
|
}
|
|
|
|
// Get rid of \n and spaces at end of line
|
|
|
|
while (len >= 0
|
|
|
|
&& (line[len] == '\n'
|
|
|
|
|| isspace(line[len])))
|
|
|
|
{
|
|
|
|
line[len] = 0;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
// Detect multi-line
|
|
|
|
if (line[len] == '\\')
|
|
|
|
{
|
|
|
|
last = len;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
last = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (parse_line(line, section, key, val))
|
|
|
|
{
|
|
|
|
case LINE_EMPTY:
|
|
|
|
case LINE_COMMENT:
|
|
|
|
break;
|
|
|
|
case LINE_SECTION:
|
|
|
|
inifile_set_current_section(inif, section);
|
|
|
|
break;
|
|
|
|
case LINE_VALUE:
|
|
|
|
inifile_set(inif, key, val);
|
|
|
|
break;
|
|
|
|
case LINE_ERROR:
|
|
|
|
fprintf(stderr, "inifile: syntax error in %s (%d):\n",
|
|
|
|
path,
|
|
|
|
lineno);
|
|
|
|
fprintf(stderr, "-> %s\n", line);
|
|
|
|
errors++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errors)
|
|
|
|
{
|
|
|
|
inifile_delete(inif);
|
|
|
|
inif = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(line, 0, ASCIILINESZ + 1);
|
|
|
|
last = 0;
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|
2010-12-29 13:17:12 -08:00
|
|
|
|
|
|
|
PHYSFS_close(in);
|
|
|
|
|
|
|
|
return inif;
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
int inifile_get_section_count(inifile *inif)
|
|
|
|
{
|
|
|
|
return inif->nsec;
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
const char *inifile_get_section(inifile *inif, int n)
|
|
|
|
{
|
|
|
|
return inif->sec[n];
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
const char *inifile_get_current_section(inifile *inif)
|
|
|
|
{
|
|
|
|
return inif->currsec;
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
void inifile_set_current_section(inifile *inif, const char *sec)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2009-08-15 03:50:26 -07:00
|
|
|
int i;
|
2010-12-29 13:17:12 -08:00
|
|
|
void *newsec;
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// See if the section exists in the file already
|
|
|
|
for (i = 0; i < inif->nsec; i++)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
// Found it
|
|
|
|
if (strcmp(strlwc(sec), inif->sec[i]) == 0)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
inif->currsec = inif->sec[i];
|
|
|
|
return;
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Otherwise we need to add a new section entry
|
|
|
|
newsec = realloc(inif->sec, sizeof(char *) * ++inif->nsec);
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
ASSERT(newsec, "Failed to allocate inifile memory.");
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
inif->sec = static_cast<char **>(newsec);
|
|
|
|
inif->sec[inif->nsec - 1] = strdup(sec);
|
|
|
|
|
|
|
|
// Make this new section entry current
|
|
|
|
inif->currsec = inif->sec[inif->nsec - 1];
|
|
|
|
|
|
|
|
ASSERT(inif->currsec, "Failed to allocate inifile memory.");
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL inifile_key_exists(inifile *inif, const char *key)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2009-08-15 03:50:26 -07:00
|
|
|
int i;
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
for (i = 0; i < inif->n; i++)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
if (strcmp(inif->entry[i].key, key) == 0)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
return true;
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
|
|
|
}
|
2010-12-29 13:17:12 -08:00
|
|
|
|
|
|
|
// Not found, return false
|
|
|
|
return false;
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
const char *inifile_get(inifile *inif, const char *key, const char *dflt)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
int i;
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Search for the entry
|
|
|
|
for (i = 0; i < inif->n; i++)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
if (inif->entry[i].sec == inif->currsec
|
|
|
|
&& strcmp(inif->entry[i].key, strlwc(key)) == 0)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
return inif->entry[i].value;
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Not found
|
|
|
|
return dflt;
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
void inifile_set(inifile *inif, const char *key, const char *value)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
int i;
|
|
|
|
int index;
|
|
|
|
|
|
|
|
// Ensure that key is valid
|
|
|
|
ASSERT(key, "Attempting to set a NULL key");
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// If value is NULL then replace it with an empty string
|
|
|
|
value = (value == NULL) ? "" : value;
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// If there is no active section we need to create one
|
|
|
|
if (!inif->currsec)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
inifile_set_current_section(inif, "main");
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
2010-12-29 13:17:12 -08:00
|
|
|
// If there is an active section then key may already exist
|
|
|
|
else
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
for (i = 0; i < inif->size; i++)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
if (inif->entry[i].sec == inif->currsec
|
|
|
|
&& strcmp(inif->entry[i].key, key) == 0)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
// Update the current value for the key
|
|
|
|
free(inif->entry[i].value);
|
|
|
|
inif->entry[i].value = strdup(value);
|
|
|
|
return;
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// See if necessary allocate some more space
|
|
|
|
if (inif->n == inif->size)
|
|
|
|
{
|
|
|
|
int newsize = inif->size * 2;
|
|
|
|
void *newentry = realloc(inif->entry, sizeof(*inif->entry) * newsize);
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
ASSERT(newentry, "Failed to allocate inifile memory.");
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
inif->size = newsize;
|
|
|
|
inif->entry = static_cast<struct _inifile_entry *>(newentry);
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Insert
|
|
|
|
index = inif->n++;
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
inif->entry[index].sec = inif->currsec;
|
|
|
|
inif->entry[index].key = strdup(key);
|
|
|
|
inif->entry[index].value = strdup(value);
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
ASSERT(inif->entry[index].key && inif->entry[index].value,
|
|
|
|
"Failed to allocate inifile memory.");
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
int inifile_get_as_int(inifile *inif, const char *key, int dflt)
|
|
|
|
{
|
|
|
|
const char *strint = inifile_get(inif, key, NULL);
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
if (strint == NULL)
|
|
|
|
{
|
|
|
|
return dflt;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return atoi(strint);
|
|
|
|
}
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
void inifile_set_as_int(inifile *inif, const char *key, int value)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
char *strval;
|
|
|
|
sasprintf(&strval, "%d", value);
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
inifile_set(inif, key, strval);
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
int inifile_get_as_bool(inifile *inif, const char *key, int dflt)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
const char *strbool = inifile_get(inif, key, NULL);
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
if (strbool == NULL)
|
|
|
|
{
|
|
|
|
return dflt;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
switch (tolower(strbool[0]))
|
|
|
|
{
|
|
|
|
case 'y':
|
|
|
|
case '1':
|
|
|
|
case 't':
|
|
|
|
return 1;
|
|
|
|
case 'n':
|
|
|
|
case '0':
|
|
|
|
case 'f':
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return dflt;
|
|
|
|
}
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
void inifile_unset(inifile *inif, const char *sec, const char *key)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
int i;
|
|
|
|
int secoffset = -1;
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// First get the section offset
|
|
|
|
for (i = 0; i < inif->nsec; i++)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
if (strcmp(inif->sec[i], sec) == 0)
|
|
|
|
{
|
|
|
|
secoffset = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we didn't find the section then give up
|
|
|
|
if (secoffset == -1)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look for the key in that section
|
|
|
|
for (i = 0; i < inif->n; i++)
|
|
|
|
{
|
|
|
|
if (inif->entry[i].sec == inif->sec[secoffset]
|
|
|
|
&& strcmp(inif->entry[i].key, key) == 0)
|
|
|
|
{
|
|
|
|
// Free the memory allocated by the entry
|
|
|
|
free(inif->entry[i].key);
|
|
|
|
free(inif->entry[i].value);
|
|
|
|
|
|
|
|
// Shift all subsequent entries back a slot
|
|
|
|
memmove(&inif->entry[i], &inif->entry[i + 1],
|
|
|
|
(inif->n - i - 1) * sizeof(*inif->entry));
|
|
|
|
|
|
|
|
inif->n--;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the section is not currently active then we need to see if any other
|
|
|
|
* entries are in the section and, if not, to delete the section.
|
|
|
|
*/
|
|
|
|
if (inif->currsec != inif->sec[secoffset])
|
|
|
|
{
|
|
|
|
void *newsec;
|
|
|
|
|
|
|
|
// Look for other entries in the section
|
|
|
|
for (i = 0; i < inif->n; i++)
|
|
|
|
{
|
|
|
|
if (inif->entry[i].sec == inif->sec[secoffset])
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, if there are none, get rid of the section
|
|
|
|
free(inif->sec[secoffset]);
|
|
|
|
memmove(&inif->sec[secoffset], &inif->sec[secoffset + 1],
|
|
|
|
(inif->nsec - secoffset - 1) * sizeof(char *));
|
|
|
|
|
|
|
|
newsec = realloc(inif->sec, --inif->nsec * sizeof(char *));
|
|
|
|
inif->sec = static_cast<char **>(newsec);
|
|
|
|
|
|
|
|
ASSERT(newsec, "Failed to allocate inifile memory.");
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
void inifile_save(inifile *inif)
|
|
|
|
{
|
|
|
|
ASSERT(inif->path, "Attempt to save an inifile with no valid (internal) path");
|
|
|
|
|
|
|
|
inifile_save_as(inif, inif->path);
|
|
|
|
}
|
|
|
|
|
|
|
|
void inifile_save_as(inifile *inif, const char *path)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
PHYSFS_file *f = PHYSFS_openWrite(path);
|
|
|
|
int i, j;
|
|
|
|
int nsec;
|
|
|
|
|
|
|
|
nsec = inifile_get_section_count(inif);
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Loop over each section of the inifile
|
|
|
|
for (i = 0; i < nsec; i++)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
const char *currsec = inifile_get_section(inif, i);
|
|
|
|
|
|
|
|
/*
|
2010-12-30 13:17:18 -08:00
|
|
|
* Print the section title if there are either multiple sections in the
|
|
|
|
* file or if the singular section has been explicitly named.
|
2010-12-29 13:17:12 -08:00
|
|
|
*/
|
2010-12-30 13:17:18 -08:00
|
|
|
if (nsec > 1 || strcmp("main", currsec) != 0)
|
2010-12-29 13:17:12 -08:00
|
|
|
{
|
|
|
|
// Print out the section title
|
|
|
|
PHYSFS_printf(f, "[%s]\n", currsec);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then the key => value pairs
|
|
|
|
for (j = 0; j < inif->n; j++)
|
|
|
|
{
|
|
|
|
if (inif->entry[j].sec == currsec)
|
|
|
|
{
|
|
|
|
PHYSFS_printf(f, "%-30s = %s\n", inif->entry[j].key,
|
|
|
|
inif->entry[j].value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PHYSFS_printf(f, "\n");
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
2010-12-29 13:17:12 -08:00
|
|
|
|
|
|
|
PHYSFS_close(f);
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
void inifile_delete(inifile *inif)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
int i;
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Free the sections
|
|
|
|
for (i = 0; i < inif->nsec; i++)
|
|
|
|
{
|
|
|
|
free(inif->sec[i]);
|
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Then the list of sections
|
|
|
|
free(inif->sec);
|
|
|
|
|
|
|
|
// Free the entries
|
|
|
|
for (i = 0; i < inif->n; i++)
|
|
|
|
{
|
|
|
|
free(inif->entry[i].key);
|
|
|
|
free(inif->entry[i].value);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then the list of entries
|
|
|
|
free(inif->entry);
|
|
|
|
|
|
|
|
// Finally the original file path (if given)
|
|
|
|
free(inif->path);
|
|
|
|
|
|
|
|
delete inif;
|
|
|
|
}
|
|
|
|
|
|
|
|
void inifile_test()
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
// Internal consistency check
|
|
|
|
inifile *inif = inifile_new();
|
|
|
|
|
|
|
|
inifile_set(inif, "string", "a string");
|
|
|
|
inifile_set_as_int(inif, "int", 31415);
|
|
|
|
|
|
|
|
ASSERT(inifile_get_section_count(inif) == 1,
|
|
|
|
"Section count unit test failed");
|
|
|
|
ASSERT(strcmp(inifile_get_current_section(inif), "main") == 0,
|
|
|
|
"Default section unit test failed");
|
|
|
|
|
|
|
|
ASSERT(strcmp(inifile_get(inif, "string", ""), "a string") == 0,
|
|
|
|
"String retrieval failed");
|
|
|
|
ASSERT(inifile_get_as_int(inif, "int", -1) == 31415,
|
|
|
|
"Int retrieval failed");
|
|
|
|
|
|
|
|
ASSERT(strcmp(inifile_get(inif, "invalid key", "dflt"), "dflt") == 0,
|
|
|
|
"Default value retrieval failed");
|
|
|
|
|
|
|
|
inifile_set_current_section(inif, "Casetest");
|
|
|
|
inifile_set(inif, "Upperkey", "Any String");
|
|
|
|
|
|
|
|
inifile_set_current_section(inif, "newsec");
|
|
|
|
inifile_set(inif, "third", "a third entry");
|
|
|
|
|
|
|
|
// Save the current inifile; we'll load it up again later
|
|
|
|
inifile_save_as(inif, "selftest.ini");
|
|
|
|
|
|
|
|
ASSERT(inifile_get_section_count(inif) == 3,
|
|
|
|
"Section addition test failed");
|
|
|
|
|
|
|
|
inifile_unset(inif, "main", "string");
|
|
|
|
inifile_unset(inif, "main", "int");
|
|
|
|
|
|
|
|
ASSERT(inifile_get_section_count(inif) == 2,
|
|
|
|
"Automatic section removal test failed");
|
|
|
|
ASSERT(inif->n == 2,
|
|
|
|
"Key deletion test failed");
|
|
|
|
ASSERT(strcmp(inifile_get(inif, "third", ""), "a third entry") == 0,
|
|
|
|
"Key deletion test failed");
|
|
|
|
|
|
|
|
inifile_delete(inif);
|
|
|
|
|
|
|
|
// Load up the file we saved earlier
|
|
|
|
inif = inifile_load("selftest.ini");
|
|
|
|
|
|
|
|
ASSERT(inifile_get_section_count(inif) == 3,
|
|
|
|
"Save/Load test failed");
|
|
|
|
|
|
|
|
inifile_set_current_section(inif, "newsec");
|
|
|
|
|
|
|
|
ASSERT(strcmp(inifile_get(inif, "third", ""), "a third entry") == 0,
|
|
|
|
"Save/Load test failed");
|
|
|
|
|
|
|
|
inifile_set_current_section(inif, "main");
|
|
|
|
inifile_unset(inif, "newsec", "third");
|
|
|
|
|
|
|
|
inifile_set_current_section(inif, "casetest");
|
|
|
|
ASSERT(strcmp(inifile_get(inif, "Upperkey", "1"), inifile_get(inif, "upperkey", "2")) == 0,
|
|
|
|
"Case insensitivity test failed.");
|
|
|
|
inifile_unset(inif, "Casetest", "Upperkey");
|
|
|
|
|
|
|
|
// Save again, this time there should be no section titles
|
|
|
|
inifile_save(inif);
|
|
|
|
inifile_delete(inif);
|
|
|
|
|
|
|
|
// Delete the saved file
|
|
|
|
PHYSFS_delete("selftest.ini");
|
|
|
|
|
|
|
|
fprintf(stdout, "\tinifile self-test: PASSED\n");
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@brief Load a single line from an INI file
|
|
|
|
@param input_line Input line, may be concatenated multi-line input
|
|
|
|
@param section Output space to store section
|
|
|
|
@param key Output space to store key
|
|
|
|
@param value Output space to store value
|
2010-12-29 13:17:12 -08:00
|
|
|
@return LINE_STATUS value
|
2009-07-26 07:57:03 -07:00
|
|
|
*/
|
2010-12-29 13:17:12 -08:00
|
|
|
LINE_STATUS parse_line(const char *input_line,
|
|
|
|
char *section, char *key, char *value)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
LINE_STATUS status = LINE_UNPROCESSED;
|
2009-08-15 03:50:26 -07:00
|
|
|
char line[ASCIILINESZ+1];
|
|
|
|
int len;
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Strip the line and copy it into line
|
2009-08-15 03:50:26 -07:00
|
|
|
strcpy(line, strstrip(input_line));
|
2010-12-29 13:17:12 -08:00
|
|
|
len = strlen(line);
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
// Empty line
|
2009-08-15 03:50:26 -07:00
|
|
|
if (len < 1)
|
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
status = LINE_EMPTY;
|
|
|
|
}
|
|
|
|
// Starts with a # and therefore a comment
|
|
|
|
else if (line[0] == '#')
|
|
|
|
{
|
|
|
|
status = LINE_COMMENT;
|
|
|
|
}
|
|
|
|
// Section identifier
|
|
|
|
else if (line[0] == '['
|
|
|
|
&& line[len-1] == ']')
|
|
|
|
{
|
2009-08-15 03:50:26 -07:00
|
|
|
sscanf(line, "[%[^]]", section);
|
|
|
|
strcpy(section, strstrip(section));
|
|
|
|
strcpy(section, strlwc(section));
|
2010-12-29 13:17:12 -08:00
|
|
|
status = LINE_SECTION;
|
|
|
|
}
|
|
|
|
// Of the form key=value # optional comment
|
|
|
|
else if (sscanf(line, "%[^=] = \"%[^\"]\"", key, value) == 2
|
|
|
|
|| sscanf(line, "%[^=] = '%[^\']'", key, value) == 2
|
|
|
|
|| sscanf(line, "%[^=] = %[^;#]", key, value) == 2)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
|
|
|
strcpy(key, strstrip(key));
|
|
|
|
strcpy(key, strlwc(key));
|
|
|
|
strcpy(value, strstrip(value));
|
|
|
|
/*
|
|
|
|
* sscanf cannot handle '' or "" as empty values
|
|
|
|
* this is done here
|
|
|
|
*/
|
|
|
|
if (!strcmp(value, "\"\"") || (!strcmp(value, "''")))
|
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
value[0] = '\0';
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
2010-12-29 13:17:12 -08:00
|
|
|
|
|
|
|
status = LINE_VALUE;
|
|
|
|
}
|
|
|
|
// Annoying special cases
|
|
|
|
else if (sscanf(line, "%[^=] = %[;#]", key, value) == 2
|
|
|
|
|| sscanf(line, "%[^=] %[=]", key, value) == 2)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Special cases:
|
|
|
|
* key=
|
|
|
|
* key=;
|
|
|
|
* key=#
|
|
|
|
*/
|
|
|
|
strcpy(key, strstrip(key));
|
|
|
|
strcpy(key, strlwc(key));
|
|
|
|
value[0] = 0;
|
2010-12-29 13:17:12 -08:00
|
|
|
status = LINE_VALUE;
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
2010-12-29 13:17:12 -08:00
|
|
|
// Otherwise it is invalid
|
|
|
|
else
|
|
|
|
{
|
|
|
|
status = LINE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|
|
|
|
|
2009-08-15 03:50:26 -07:00
|
|
|
/**
|
2010-12-29 13:17:12 -08:00
|
|
|
@brief Convert a string to lowercase.
|
|
|
|
@param s String to convert.
|
|
|
|
@return ptr to statically allocated string.
|
|
|
|
|
|
|
|
This function returns a pointer to a statically allocated string
|
|
|
|
containing a lowercased version of the input string. Do not free
|
|
|
|
or modify the returned string! Since the returned string is statically
|
|
|
|
allocated, it will be modified at each function call (not re-entrant).
|
2009-07-27 10:16:09 -07:00
|
|
|
*/
|
2010-12-29 13:17:12 -08:00
|
|
|
char *strlwc(const char *s)
|
2009-07-27 10:16:09 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
static char l[ASCIILINESZ+1];
|
|
|
|
int i;
|
2009-07-27 10:16:09 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
if (s == NULL) return NULL;
|
|
|
|
memset(l, 0, ASCIILINESZ + 1);
|
|
|
|
i = 0;
|
|
|
|
while (s[i] && i < ASCIILINESZ)
|
2009-07-27 10:16:09 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
l[i] = (char)tolower((int)s[i]);
|
|
|
|
i++;
|
2009-07-27 10:16:09 -07:00
|
|
|
}
|
2010-12-29 13:17:12 -08:00
|
|
|
l[ASCIILINESZ] = (char)0;
|
|
|
|
return l;
|
2009-07-27 10:16:09 -07:00
|
|
|
}
|
|
|
|
|
2009-07-26 07:57:03 -07:00
|
|
|
/**
|
2010-12-29 13:17:12 -08:00
|
|
|
@brief Remove blanks at the beginning and the end of a string.
|
|
|
|
@param s String to parse.
|
|
|
|
@return ptr to statically allocated string.
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
This function returns a pointer to a statically allocated string,
|
|
|
|
which is identical to the input string, except that all blank
|
|
|
|
characters at the end and the beg. of the string have been removed.
|
|
|
|
Do not free or modify the returned string! Since the returned string
|
|
|
|
is statically allocated, it will be modified at each function call
|
|
|
|
(not re-entrant).
|
2009-07-26 07:57:03 -07:00
|
|
|
*/
|
2010-12-29 13:17:12 -08:00
|
|
|
char *strstrip(const char *s)
|
2009-07-26 07:57:03 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
static char l[ASCIILINESZ+1];
|
|
|
|
char *last;
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
if (s == NULL)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
while (isspace(*s) && *s) s++;
|
2009-08-15 03:50:26 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
memset(l, 0, ASCIILINESZ + 1);
|
|
|
|
strncpy(l, s, sizeof(l));
|
|
|
|
last = l + strlen(l);
|
|
|
|
while (last > l)
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
2010-12-29 13:17:12 -08:00
|
|
|
if (!isspace(*(last - 1)))
|
2009-08-15 03:50:26 -07:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2010-12-29 13:17:12 -08:00
|
|
|
|
|
|
|
last--;
|
2009-08-15 03:50:26 -07:00
|
|
|
}
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
*last = '\0';
|
2009-07-26 07:57:03 -07:00
|
|
|
|
2010-12-29 13:17:12 -08:00
|
|
|
return l;
|
2009-07-26 07:57:03 -07:00
|
|
|
}
|