/*
- * $Id: options.c,v 1.7 2001/08/23 09:39:56 jheinonen Exp $
+ * $Id: options.c,v 1.27.4.1 2006/04/09 18:57:34 jheinonen Exp $
*
* by JH <jheinonen@users.sourceforge.net>
*
* Copyright (C) Jaakko Heinonen
+ *
*/
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
-#include "abook_curses.h"
-#include "abook.h"
+#include <ctype.h>
+#include <assert.h>
#include "options.h"
-#ifdef HAVE_CONFIG_H
-# include "config.h"
+#include "abook.h"
+#include "gettext.h"
+#include "misc.h"
+#include "xmalloc.h"
+
+#ifndef FALSE
+# define FALSE 0
#endif
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#define UL (unsigned long)
+
+/*
+ * option types
+ */
+
+enum opt_type {
+ OT_BOOL,
+ OT_STR,
+ OT_INT
+};
+
+struct option {
+ char *option;
+ enum opt_type type;
+ unsigned int data;
+ unsigned long init;
+};
+
+static struct option abook_vars[] = {
+ { "autosave", OT_BOOL, BOOL_AUTOSAVE, TRUE },
+
+ { "show_all_emails", OT_BOOL, BOOL_SHOW_ALL_EMAILS, TRUE },
+ { "emailpos", OT_INT, INT_EMAILPOS, 25 },
+ { "extra_column", OT_STR, STR_EXTRA_COLUMN, UL "phone" },
+ { "extra_alternative", OT_STR, STR_EXTRA_ALTERNATIVE, UL "-1" },
+ { "extrapos", OT_INT, INT_EXTRAPOS, 65 },
-struct conff_node *abook_config;
+ { "mutt_command", OT_STR, STR_MUTT_COMMAND, UL "mutt" },
+ { "mutt_return_all_emails", OT_BOOL, BOOL_MUTT_RETURN_ALL_EMAILS,
+ TRUE },
-static int rcfile_exist();
-static void default_options();
+ { "print_command", OT_STR, STR_PRINT_COMMAND, UL "lpr" },
-extern char *rcfile;
+ { "www_command", OT_STR, STR_WWW_COMMAND, UL "lynx" },
-static char *
-abook_opt_conff_get_val(char *key)
+ { "address_style", OT_STR, STR_ADDRESS_STYLE, UL "eu" },
+
+ { "use_ascii_only", OT_BOOL, BOOL_USE_ASCII_ONLY, FALSE },
+
+ { "add_email_prevent_duplicates", OT_BOOL, BOOL_ADD_EMAIL_PREVENT_DUPLICATES, FALSE },
+ { "sort_field", OT_STR, STR_SORT_FIELD, UL "nick" },
+ { "show_cursor", OT_BOOL, BOOL_SHOW_CURSOR, FALSE },
+
+ { NULL }
+};
+
+static unsigned char bool_opts[BOOL_MAX];
+static int int_opts[INT_MAXIMUM];
+static char *str_opts[STR_MAX];
+
+static void
+set_int(enum int_opts opt, int value)
{
- int tried;
- char *value = NULL;
+ assert(opt >= 0 && opt < INT_MAXIMUM);
+ int_opts[opt] = value;
+}
- for(tried = 0; tried < 2; ) {
- if( ( value = conff_get_value(abook_config, key) )
- == 0 ) {
- tried ++;
- default_options(); /* try with defaults */
- } else
- return value;
- }
- return NULL;
+static void
+set_bool(enum bool_opts opt, bool value)
+{
+ assert(opt >= 0 && opt < BOOL_MAX);
+
+ bool_opts[opt] = value;
+}
+
+static void
+set_str(enum str_opts opt, char *value)
+{
+ assert(opt >= 0 && opt < STR_MAX);
+
+ if(str_opts[opt])
+ free(str_opts[opt]);
+
+ str_opts[opt] = xstrdup(value);
}
int
-options_get_int(char *key)
+opt_get_int(enum int_opts opt)
{
- char *value;
- int ret;
-
- if( ( value = abook_opt_conff_get_val(key) )
- == NULL)
- return 1;
+ assert(opt >= 0 && opt < INT_MAXIMUM);
- if( !strcasecmp(value, "true") )
- ret = 1;
- else
- if( !strcasecmp(value, "false") )
- ret = 0;
- else
- ret = safe_atoi(value);
-
- return ret;
+ return int_opts[opt];
}
-
+
+bool
+opt_get_bool(enum bool_opts opt)
+{
+ assert(opt >= 0 && opt < BOOL_MAX);
+
+ return bool_opts[opt];
+}
+
char *
-options_get_str(char *key)
+opt_get_str(enum str_opts opt)
+{
+ assert(opt >= 0 && opt < STR_MAX);
+
+ return str_opts[opt];
+}
+
+static void
+restore_default(struct option *p)
{
- return abook_opt_conff_get_val(key);
+ switch(p -> type) {
+ case OT_BOOL:
+ set_bool(p -> data, (bool)p -> init);
+ break;
+ case OT_INT:
+ set_int(p -> data, (int)p -> init);
+ break;
+ case OT_STR:
+ if(p -> init)
+ set_str(p -> data, (char *) p -> init);
+ break;
+ default:
+ assert(0);
+ }
}
-
+
void
-init_options()
+init_opts()
{
- abook_config = NULL;
+ int i;
- if( rcfile_exist() )
- load_options();
- else
- default_options();
+ for(i = 0; abook_vars[i].option; i++)
+ restore_default(&abook_vars[i]);
}
void
-close_config()
+free_opts()
{
- save_options();
+ int i;
- conff_free_nodes(abook_config);
+ /*
+ * only strings need to be freed
+ */
+ for(i = 0; i < STR_MAX; i++) {
+ free(str_opts[i]);
+ str_opts[i] = NULL;
+ }
}
-static int
-rcfile_exist()
+/*
+ * file parsing
+ */
+
+typedef struct {
+ char *data, *ptr;
+} buffer;
+
+static void
+opt_line_remove_comments(char *p)
{
- return ( (0 == access(SYSWIDE_RCFILE, F_OK)) ||
- (0 == access(rcfile, F_OK)) );
+ bool in_quote = FALSE;
+ bool escape = FALSE;
+
+ assert(p != NULL);
+
+ for(; *p; p++) {
+ switch(*p) {
+ case '\"':
+ if(!escape)
+ in_quote = !in_quote;
+ break;
+ case '\\':
+ escape = TRUE;
+ break;
+ case '#':
+ if(!in_quote) {
+ *p = 0;
+ return;
+ }
+ default:
+ escape = FALSE;
+ }
+ }
}
void
-load_options()
-{
- int ret;
-
- if( (ret = conff_load_file(&abook_config, rcfile,
- REPLACE_KEY)) > 0) {
- fprintf(stderr, "%s: parse error at line %d\n", rcfile, ret);
- exit(1);
+find_token_start(buffer *b)
+{
+ assert(b);
+
+ for(; ISSPACE(*b -> ptr); b -> ptr ++);
+}
+
+void
+find_token_end(buffer *b)
+{
+ assert(b);
+
+ for(find_token_start(b); *(b -> ptr); b -> ptr ++) {
+ if(ISSPACE(*(b -> ptr))) {
+ break;
+ }
+ }
+}
+
+static const char *
+opt_set_set_option(char *var, char *p, struct option *opt)
+{
+ int len;
+
+ strtrim(p);
+
+ len = strlen(p);
+
+ if(p[len - 1] == '\"' && *p == '\"') {
+ if(len < 3)
+ return _("invalid value");
+ p[len - 1] = 0;
+ p++;
}
- if( (ret = conff_load_file(&abook_config, SYSWIDE_RCFILE,
- DONT_REPLACE_KEY )) > 0) {
- fprintf(stderr, "%s: parse error at line %d\n",
- SYSWIDE_RCFILE, ret);
- exit(1);
+ switch(opt -> type) {
+ case OT_STR:
+ set_str(opt -> data, p);
+ break;
+ case OT_INT:
+ set_int(opt -> data, safe_atoi(p));
+ break;
+ case OT_BOOL:
+ if(!strcasecmp(p, "true") || !strcasecmp(p, "on"))
+ set_bool(opt -> data, TRUE);
+ else if(!strcasecmp(p, "false") ||
+ !strcasecmp(p, "off"))
+ set_bool(opt -> data, FALSE);
+ else
+ return _("invalid value");
+ break;
+ default:
+ assert(0);
}
+
+ return NULL;
}
-void
-save_options()
+static const char *
+opt_parse_set(buffer *b)
{
- if( rcfile_exist() ) /* don't overwrite existing config */
- return;
+ int i;
+ char *p;
- conff_save_file(abook_config, rcfile);
+ find_token_start(b);
+ if((p = strchr(b -> ptr, '=')))
+ *p++ = 0;
+ else
+ return _("invalid value assignment");
+
+ strtrim(b -> ptr);
+
+ for(i = 0;abook_vars[i].option; i++)
+ if(!strcmp(abook_vars[i].option, b -> ptr))
+ return opt_set_set_option(b -> ptr, p, &abook_vars[i]);
+
+ return _("unknown option");
}
-static void
-options_add_key(char *key, char *value)
+#include "database.h" /* needed for change_custom_field_name */
+
+static const char *
+opt_parse_customfield(buffer *b)
{
- const int flags = DONT_REPLACE_KEY;
+ char *p, num[5];
+ int n;
+ size_t len;
- conff_add_key(&abook_config, key, value, flags);
+ find_token_start(b);
+ p = b -> ptr;
+ find_token_end(b);
+
+ memset(num, 0, sizeof(num));
+
+ len = (b -> ptr - p);
+ strncpy(num, p, min(sizeof(num) - 1, len));
+ n = safe_atoi(num);
+
+ find_token_start(b);
+
+ if(change_custom_field_name(b->ptr, n) == -1)
+ return _("invalid custom field number");
+
+ return NULL;
}
-static void
-default_options()
+static struct {
+ char *token;
+ const char * (*func) (buffer *line);
+} opt_parsers[] = {
+ { "set", opt_parse_set },
+ { "customfield", opt_parse_customfield },
+ { NULL }
+};
+
+static bool
+opt_parse_line(char *line, int n, char *fn)
{
- options_add_key("autosave", "true");
+ int i;
+ const char *err = NULL;
+ char *token;
+ buffer b;
+
+ assert(line && fn);
+
+ b.ptr = line;
- options_add_key("show_all_emails", "true");
- options_add_key("emailpos", "25");
- options_add_key("extra_column", "7");
- options_add_key("extra_alternative", "-1");
- options_add_key("extrapos", "65");
+ find_token_start(&b);
+ b.data = b.ptr;
+ find_token_end(&b);
+ *b.ptr++ = 0;
- options_add_key("mutt_command", "mutt");
- options_add_key("mutt_return_all_emails", "true");
+ if(!*line)
+ return FALSE;
- options_add_key("print_command", "lpr");
+ strtrim(b.data);
+ strtrim(b.ptr);
- options_add_key("filesel_sort", "false");
+ token = b.data;
+ b.data = b.ptr = b.ptr;
- options_add_key("www_command", "lynx");
+ for(i = 0; opt_parsers[i].token; i++)
+ if(!strcmp(opt_parsers[i].token, token)) {
+ if(!(err = opt_parsers[i].func(&b)))
+ return FALSE;
+ break;
+ }
- options_add_key("address_style", "eu");
+ fprintf(stderr, _("%s: parse error at line %d: "), fn, n);
+ if(err)
+ fprintf(stderr, "%s\n", err);
+ else
+ fprintf(stderr, _("unknown token %s\n"), token);
- options_add_key("use_ascii_only", "false");
+ return TRUE;
}
+
+int
+load_opts(char *filename)
+{
+ FILE *in;
+ char *line = NULL;
+ int n;
+ int err = 0;
+
+ if((in = fopen(filename, "r")) == NULL)
+ return -1;
+
+
+ for(n = 1;!feof(in); n++) {
+ line = getaline(in);
+
+ if(feof(in))
+ break;
+
+ if(line && *line) {
+ opt_line_remove_comments(line);
+ if(*line)
+ err += opt_parse_line(line, n, filename) ? 1:0;
+ }
+
+ free(line);
+ line = NULL;
+ }
+
+ free(line);
+
+ return err;
+}
+