1999-10-03 14:16:26 +00:00
|
|
|
/*
|
|
|
|
* prefs.cpp - Preferences handling
|
|
|
|
*
|
2001-02-02 20:53:00 +00:00
|
|
|
* Basilisk II (C) 1997-2001 Christian Bauer
|
1999-10-03 14:16:26 +00:00
|
|
|
*
|
|
|
|
* This program 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.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include "sysdeps.h"
|
|
|
|
#include "sys.h"
|
|
|
|
#include "prefs.h"
|
|
|
|
|
|
|
|
|
2000-07-25 15:19:43 +00:00
|
|
|
// Prefs items are stored in a linked list of these nodes
|
1999-10-03 14:16:26 +00:00
|
|
|
struct prefs_node {
|
|
|
|
prefs_node *next;
|
|
|
|
const char *name;
|
|
|
|
prefs_type type;
|
|
|
|
void *data;
|
|
|
|
};
|
|
|
|
|
|
|
|
// List of prefs nodes
|
2001-01-04 19:50:23 +00:00
|
|
|
static prefs_node *the_prefs = NULL;
|
|
|
|
|
|
|
|
// Prototypes
|
|
|
|
static const prefs_desc *find_prefs_desc(const char *name);
|
1999-10-03 14:16:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize preferences
|
|
|
|
*/
|
|
|
|
|
2001-04-01 12:11:45 +00:00
|
|
|
void PrefsInit(int &argc, char **&argv)
|
1999-10-03 14:16:26 +00:00
|
|
|
{
|
|
|
|
// Set defaults
|
2000-07-25 15:19:43 +00:00
|
|
|
AddPrefsDefaults();
|
1999-10-03 14:16:26 +00:00
|
|
|
AddPlatformPrefsDefaults();
|
|
|
|
|
|
|
|
// Load preferences from settings file
|
|
|
|
LoadPrefs();
|
2001-01-04 19:50:23 +00:00
|
|
|
|
2001-04-01 12:11:45 +00:00
|
|
|
// Override prefs with command line options
|
|
|
|
for (int i=1; i<argc; i++) {
|
2001-01-04 19:50:23 +00:00
|
|
|
|
2001-04-01 12:11:45 +00:00
|
|
|
// Options are of the form '--keyword'
|
|
|
|
const char *option = argv[i];
|
|
|
|
if (strlen(option) < 3 || option[0] != '-' || option[1] != '-')
|
2001-01-04 19:50:23 +00:00
|
|
|
continue;
|
2001-04-01 12:11:45 +00:00
|
|
|
const char *keyword = option + 2;
|
|
|
|
|
|
|
|
// Find descriptor for keyword
|
2001-01-04 19:50:23 +00:00
|
|
|
const prefs_desc *d = find_prefs_desc(keyword);
|
2001-04-01 12:11:45 +00:00
|
|
|
if (d == NULL)
|
|
|
|
continue;
|
|
|
|
argv[i] = NULL;
|
|
|
|
|
|
|
|
// Get value
|
|
|
|
i++;
|
|
|
|
if (i >= argc) {
|
|
|
|
fprintf(stderr, "Option '%s' must be followed by a value\n", option);
|
2001-01-04 19:50:23 +00:00
|
|
|
continue;
|
|
|
|
}
|
2001-04-01 12:11:45 +00:00
|
|
|
const char *value = argv[i];
|
|
|
|
argv[i] = NULL;
|
2001-01-04 19:50:23 +00:00
|
|
|
|
|
|
|
// Add/replace prefs item
|
|
|
|
switch (d->type) {
|
|
|
|
case TYPE_STRING:
|
|
|
|
if (d->multiple)
|
2001-04-01 12:11:45 +00:00
|
|
|
PrefsAddString(keyword, value);
|
2001-01-04 19:50:23 +00:00
|
|
|
else
|
2001-04-01 12:11:45 +00:00
|
|
|
PrefsReplaceString(keyword, value);
|
2001-01-04 19:50:23 +00:00
|
|
|
break;
|
|
|
|
|
2001-04-01 12:11:45 +00:00
|
|
|
case TYPE_BOOLEAN: {
|
|
|
|
if (!strcmp(value, "true") || !strcmp(value, "on") || !strcmp(value, "yes"))
|
2001-01-04 19:50:23 +00:00
|
|
|
PrefsReplaceBool(keyword, true);
|
2001-04-01 12:11:45 +00:00
|
|
|
else if (!strcmp(value, "false") || !strcmp(value, "off") || !strcmp(value, "no"))
|
|
|
|
PrefsReplaceBool(keyword, false);
|
|
|
|
else
|
|
|
|
fprintf(stderr, "Value for option '%s' must be 'true' or 'false'\n", option);
|
2001-01-04 19:50:23 +00:00
|
|
|
break;
|
2001-04-01 12:11:45 +00:00
|
|
|
}
|
2001-01-04 19:50:23 +00:00
|
|
|
|
|
|
|
case TYPE_INT32:
|
2001-04-01 12:11:45 +00:00
|
|
|
PrefsReplaceInt32(keyword, atoi(value));
|
2001-01-04 19:50:23 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2001-04-01 12:11:45 +00:00
|
|
|
|
|
|
|
// Remove processed arguments
|
|
|
|
for (int i=1; i<argc; i++) {
|
|
|
|
int k;
|
|
|
|
for (k=i; k<argc; k++)
|
|
|
|
if (argv[k] != NULL)
|
|
|
|
break;
|
|
|
|
if (k > i) {
|
|
|
|
k -= i;
|
|
|
|
for (int j=i+k; j<argc; j++)
|
|
|
|
argv[j-k] = argv[j];
|
|
|
|
argc -= k;
|
|
|
|
}
|
|
|
|
}
|
1999-10-03 14:16:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deinitialize preferences
|
|
|
|
*/
|
|
|
|
|
|
|
|
void PrefsExit(void)
|
|
|
|
{
|
|
|
|
// Free prefs list
|
|
|
|
prefs_node *p = the_prefs, *next;
|
|
|
|
while (p) {
|
|
|
|
next = p->next;
|
|
|
|
free((void *)p->name);
|
|
|
|
free(p->data);
|
|
|
|
delete p;
|
|
|
|
p = next;
|
|
|
|
}
|
2001-05-15 14:24:25 +00:00
|
|
|
the_prefs = NULL;
|
1999-10-03 14:16:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-04-01 12:11:45 +00:00
|
|
|
/*
|
|
|
|
* Print preferences options help
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void print_options(const prefs_desc *list)
|
|
|
|
{
|
|
|
|
while (list->type != TYPE_END) {
|
|
|
|
if (list->help) {
|
|
|
|
const char *typestr, *defstr;
|
|
|
|
char numstr[32];
|
|
|
|
switch (list->type) {
|
|
|
|
case TYPE_STRING:
|
|
|
|
typestr = "STRING";
|
|
|
|
defstr = PrefsFindString(list->name);
|
|
|
|
if (defstr == NULL)
|
|
|
|
defstr = "none";
|
|
|
|
break;
|
|
|
|
case TYPE_BOOLEAN:
|
|
|
|
typestr = "BOOL";
|
|
|
|
if (PrefsFindBool(list->name))
|
|
|
|
defstr = "true";
|
|
|
|
else
|
|
|
|
defstr = "false";
|
|
|
|
break;
|
|
|
|
case TYPE_INT32:
|
|
|
|
typestr = "NUMBER";
|
|
|
|
sprintf(numstr, "%d", PrefsFindInt32(list->name));
|
|
|
|
defstr = numstr;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
typestr = "<unknown>";
|
|
|
|
defstr = "none";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
printf(" --%s %s\n %s [default=%s]\n", list->name, typestr, list->help, defstr);
|
|
|
|
}
|
|
|
|
list++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrefsPrintUsage(void)
|
|
|
|
{
|
|
|
|
printf("\nGeneral options:\n");
|
|
|
|
print_options(common_prefs_items);
|
|
|
|
printf("\nPlatform-specific options:\n");
|
|
|
|
print_options(platform_prefs_items);
|
|
|
|
printf("\nBoolean options are specified as '--OPTION true|on|yes' or\n'--OPTION false|off|no'.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-10-03 14:16:26 +00:00
|
|
|
/*
|
|
|
|
* Find preferences descriptor by keyword
|
|
|
|
*/
|
|
|
|
|
|
|
|
static const prefs_desc *find_prefs_desc(const char *name, const prefs_desc *list)
|
|
|
|
{
|
|
|
|
while (list->type != TYPE_ANY) {
|
|
|
|
if (strcmp(list->name, name) == 0)
|
|
|
|
return list;
|
|
|
|
list++;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2001-01-04 19:50:23 +00:00
|
|
|
static const prefs_desc *find_prefs_desc(const char *name)
|
|
|
|
{
|
|
|
|
const prefs_desc *d = find_prefs_desc(name, common_prefs_items);
|
|
|
|
if (d == NULL)
|
|
|
|
d = find_prefs_desc(name, platform_prefs_items);
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
1999-10-03 14:16:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set prefs items
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void add_data(const char *name, prefs_type type, void *data, int size)
|
|
|
|
{
|
|
|
|
void *d = malloc(size);
|
|
|
|
if (d == NULL)
|
|
|
|
return;
|
|
|
|
memcpy(d, data, size);
|
|
|
|
prefs_node *p = new prefs_node;
|
|
|
|
p->next = 0;
|
|
|
|
p->name = strdup(name);
|
|
|
|
p->type = type;
|
|
|
|
p->data = d;
|
|
|
|
if (the_prefs) {
|
|
|
|
prefs_node *prev = the_prefs;
|
|
|
|
while (prev->next)
|
|
|
|
prev = prev->next;
|
|
|
|
prev->next = p;
|
|
|
|
} else
|
|
|
|
the_prefs = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrefsAddString(const char *name, const char *s)
|
|
|
|
{
|
|
|
|
add_data(name, TYPE_STRING, (void *)s, strlen(s) + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrefsAddBool(const char *name, bool b)
|
|
|
|
{
|
|
|
|
add_data(name, TYPE_BOOLEAN, &b, sizeof(bool));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrefsAddInt32(const char *name, int32 val)
|
|
|
|
{
|
|
|
|
add_data(name, TYPE_INT32, &val, sizeof(int32));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Replace prefs items
|
|
|
|
*/
|
|
|
|
|
|
|
|
static prefs_node *find_node(const char *name, prefs_type type, int index = 0)
|
|
|
|
{
|
|
|
|
prefs_node *p = the_prefs;
|
|
|
|
int i = 0;
|
|
|
|
while (p) {
|
|
|
|
if ((type == TYPE_ANY || p->type == type) && !strcmp(p->name, name)) {
|
|
|
|
if (i == index)
|
|
|
|
return p;
|
|
|
|
else
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrefsReplaceString(const char *name, const char *s, int index)
|
|
|
|
{
|
|
|
|
prefs_node *p = find_node(name, TYPE_STRING, index);
|
|
|
|
if (p) {
|
|
|
|
free(p->data);
|
|
|
|
p->data = strdup(s);
|
|
|
|
} else
|
|
|
|
add_data(name, TYPE_STRING, (void *)s, strlen(s) + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrefsReplaceBool(const char *name, bool b)
|
|
|
|
{
|
|
|
|
prefs_node *p = find_node(name, TYPE_BOOLEAN);
|
|
|
|
if (p)
|
|
|
|
*(bool *)(p->data) = b;
|
|
|
|
else
|
|
|
|
add_data(name, TYPE_BOOLEAN, &b, sizeof(bool));
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrefsReplaceInt32(const char *name, int32 val)
|
|
|
|
{
|
|
|
|
prefs_node *p = find_node(name, TYPE_INT32);
|
|
|
|
if (p)
|
|
|
|
*(int32 *)(p->data) = val;
|
|
|
|
else
|
|
|
|
add_data(name, TYPE_INT32, &val, sizeof(int32));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get prefs items
|
|
|
|
*/
|
|
|
|
|
|
|
|
const char *PrefsFindString(const char *name, int index)
|
|
|
|
{
|
|
|
|
prefs_node *p = find_node(name, TYPE_STRING, index);
|
|
|
|
if (p)
|
|
|
|
return (char *)(p->data);
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PrefsFindBool(const char *name)
|
|
|
|
{
|
|
|
|
prefs_node *p = find_node(name, TYPE_BOOLEAN, 0);
|
|
|
|
if (p)
|
|
|
|
return *(bool *)(p->data);
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 PrefsFindInt32(const char *name)
|
|
|
|
{
|
|
|
|
prefs_node *p = find_node(name, TYPE_INT32, 0);
|
|
|
|
if (p)
|
|
|
|
return *(int32 *)(p->data);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove prefs items
|
|
|
|
*/
|
|
|
|
|
|
|
|
void PrefsRemoveItem(const char *name, int index)
|
|
|
|
{
|
|
|
|
prefs_node *p = find_node(name, TYPE_ANY, index);
|
|
|
|
if (p) {
|
|
|
|
free((void *)p->name);
|
|
|
|
free(p->data);
|
|
|
|
prefs_node *q = the_prefs;
|
|
|
|
if (q == p) {
|
|
|
|
the_prefs = NULL;
|
|
|
|
delete p;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (q) {
|
|
|
|
if (q->next == p) {
|
|
|
|
q->next = p->next;
|
|
|
|
delete p;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
q = q->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load prefs from stream (utility function for LoadPrefs() implementation)
|
|
|
|
*/
|
|
|
|
|
|
|
|
void LoadPrefsFromStream(FILE *f)
|
|
|
|
{
|
|
|
|
char line[256];
|
|
|
|
while(fgets(line, 255, f)) {
|
|
|
|
// Read line
|
|
|
|
int len = strlen(line);
|
|
|
|
if (len == 0)
|
|
|
|
continue;
|
|
|
|
line[len-1] = 0;
|
|
|
|
|
|
|
|
// Comments begin with "#" or ";"
|
|
|
|
if (line[0] == '#' || line[0] == ';')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Terminate string after keyword
|
|
|
|
char *p = line;
|
|
|
|
while (!isspace(*p)) p++;
|
|
|
|
*p++ = 0;
|
|
|
|
|
|
|
|
// Skip whitespace until value
|
|
|
|
while (isspace(*p)) p++;
|
|
|
|
char *keyword = line;
|
|
|
|
char *value = p;
|
|
|
|
int32 i = atol(value);
|
|
|
|
|
2001-01-04 19:50:23 +00:00
|
|
|
// Look for keyword first in prefs item list
|
|
|
|
const prefs_desc *desc = find_prefs_desc(keyword);
|
1999-10-03 14:16:26 +00:00
|
|
|
if (desc == NULL) {
|
|
|
|
printf("WARNING: Unknown preferences keyword '%s'\n", keyword);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add item to prefs
|
|
|
|
switch (desc->type) {
|
|
|
|
case TYPE_STRING:
|
|
|
|
if (desc->multiple)
|
|
|
|
PrefsAddString(keyword, value);
|
|
|
|
else
|
|
|
|
PrefsReplaceString(keyword, value);
|
|
|
|
break;
|
|
|
|
case TYPE_BOOLEAN:
|
|
|
|
PrefsReplaceBool(keyword, !strcmp(value, "true"));
|
|
|
|
break;
|
|
|
|
case TYPE_INT32:
|
|
|
|
PrefsReplaceInt32(keyword, i);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save settings to stream (utility function for SavePrefs() implementation)
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void write_prefs(FILE *f, const prefs_desc *list)
|
|
|
|
{
|
|
|
|
while (list->type != TYPE_ANY) {
|
|
|
|
switch (list->type) {
|
|
|
|
case TYPE_STRING: {
|
|
|
|
int index = 0;
|
|
|
|
const char *str;
|
|
|
|
while ((str = PrefsFindString(list->name, index++)) != NULL)
|
|
|
|
fprintf(f, "%s %s\n", list->name, str);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TYPE_BOOLEAN:
|
|
|
|
fprintf(f, "%s %s\n", list->name, PrefsFindBool(list->name) ? "true" : "false");
|
|
|
|
break;
|
|
|
|
case TYPE_INT32:
|
2000-07-22 16:07:23 +00:00
|
|
|
fprintf(f, "%s %d\n", list->name, PrefsFindInt32(list->name));
|
1999-10-03 14:16:26 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
list++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SavePrefsToStream(FILE *f)
|
|
|
|
{
|
|
|
|
write_prefs(f, common_prefs_items);
|
|
|
|
write_prefs(f, platform_prefs_items);
|
|
|
|
}
|