]> git.deb.at Git - pkg/abook.git/blobdiff - options.c
Merge remote-tracking branch 'upstream/master' into upstream
[pkg/abook.git] / options.c
index 103c180848df07b1861e0459f28245c018ebf326..6f75a06db368f215c85f6731b3415222badb2a41 100644 (file)
--- a/options.c
+++ b/options.c
 /*
  * $Id$
  *
- * by JH <jheinonen@bigfoot.com>
+ * 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 "views.h"
+#include "xmalloc.h"
+
+#ifndef FALSE
+#      define FALSE    0
+#endif
+#ifndef TRUE
+#      define TRUE     1
 #endif
 
-struct conff_node *abook_config;
+#define UL     (unsigned long)
 
-static int     rcfile_exist();
-static void    default_options();
+/*
+ * option types
+ */
 
-extern char *rcfile;
+enum opt_type {
+       OT_BOOL,
+       OT_STR,
+       OT_INT
+};
 
-static char *
-abook_opt_conff_get_val(char *key)
+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 },
+       { "index_format", OT_STR, STR_INDEX_FORMAT, UL " {name:22} {email:40} {phone:12|workphone|mobile}" },
+       { "mutt_command", OT_STR, STR_MUTT_COMMAND, UL "mutt" },
+       { "mutt_return_all_emails", OT_BOOL, BOOL_MUTT_RETURN_ALL_EMAILS,
+               TRUE },
+
+       { "print_command", OT_STR, STR_PRINT_COMMAND, UL "lpr" },
+
+       { "www_command", OT_STR, STR_WWW_COMMAND, UL "lynx" },
+
+       { "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 },
+       { "preserve_fields", OT_STR, STR_PRESERVE_FIELDS, UL "standard" },
+       { "sort_field", OT_STR, STR_SORT_FIELD, UL "nick" },
+       { "show_cursor", OT_BOOL, BOOL_SHOW_CURSOR, FALSE },
+       { "use_mouse", OT_BOOL, BOOL_USE_MOUSE, FALSE },
+       { "scroll_speed", OT_INT, INT_SCROLL_SPEED, UL 2 },
+       { "use_colors", OT_BOOL, BOOL_USE_COLORS, FALSE },
+       { "color_header_fg", OT_STR, STR_COLOR_HEADER_FG, UL "blue" },
+       { "color_header_fg", OT_STR, STR_COLOR_HEADER_FG, UL "blue" },
+       { "color_header_bg", OT_STR, STR_COLOR_HEADER_BG, UL "red" },
+       { "color_footer_fg", OT_STR, STR_COLOR_FOOTER_FG, UL "red" },
+       { "color_footer_bg", OT_STR, STR_COLOR_FOOTER_BG, UL "default" },
+       { "color_list_even_fg", OT_STR, STR_COLOR_LIST_EVEN_FG, UL "yellow" },
+       { "color_list_even_bg", OT_STR, STR_COLOR_LIST_EVEN_BG, UL "default" },
+       { "color_list_odd_fg", OT_STR, STR_COLOR_LIST_ODD_FG, UL "default" },
+       { "color_list_odd_bg", OT_STR, STR_COLOR_LIST_ODD_BG, UL "default" },
+       { "color_list_header_fg", OT_STR, STR_COLOR_LIST_HEADER_FG, UL "white" },
+       { "color_list_header_bg", OT_STR, STR_COLOR_LIST_HEADER_BG, UL "blue" },
+       { "color_list_highlight_fg", OT_STR, STR_COLOR_LIST_HIGHLIGHT_FG, UL "black" },
+       { "color_list_highlight_bg", OT_STR, STR_COLOR_LIST_HIGHLIGHT_BG, UL "green" },
+       { "color_tab_border_fg", OT_STR, STR_COLOR_TAB_BORDER_FG, UL "cyan" },
+       { "color_tab_border_bg", OT_STR, STR_COLOR_TAB_BORDER_BG, UL "default" },
+       { "color_tab_label_fg", OT_STR, STR_COLOR_TAB_LABEL_FG, UL "magenta" },
+       { "color_tab_label_bg", OT_STR, STR_COLOR_TAB_LABEL_BG, UL "default" },
+       { "color_field_name_fg", OT_STR, STR_COLOR_FIELD_NAME_FG, UL "yellow" },
+       { "color_field_name_bg", OT_STR, STR_COLOR_FIELD_NAME_BG, UL "default" },
+       { "color_field_value_fg", OT_STR, STR_COLOR_FIELD_VALUE_FG, UL "green" },
+       { "color_field_value_bg", OT_STR, STR_COLOR_FIELD_VALUE_BG, UL "default" },
+       { 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)
 {
-       return abook_opt_conff_get_val(key); 
+       assert(opt >= 0 && opt < STR_MAX);
+
+       return str_opts[opt];
 }
-               
+
+static void
+restore_default(struct option *p)
+{
+       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);
+/* After calling,
+ * - b->data points to the found token, or NULL is end of parsing
+ * - b->ptr  points to the begining of next token
+ *
+ * If the TOKEN_ALLOC option is used, the original string is not mangled
+ * and memory is allocated for the token.
+ */
+static char *
+get_token(buffer *b, int options)
+{
+       char quote = 0, c;
+       char *end = NULL;
+
+       assert(b);
+
+       SKIPWS(b->ptr);
+       if(*b->ptr && strchr("\"'", *b->ptr))
+               quote = *(b->ptr++);
+       b->data = b->ptr;
+
+       while(1) {
+               if(!(c = *b->ptr)) {
+                       end = b->ptr;
+                       break;
+               }
+
+               if(!quote && (
+                               ISSPACE(c) ||
+                               ((options & TOKEN_EQUAL) && (c == '=')) ||
+                               ((options & TOKEN_COMMA) && (c == ',')))
+                               ) {
+                       end = b->ptr;
+                       break;
+               } else if(c == quote) {
+                       quote = 0;
+                       end = b->ptr++;
+                       break;
+               }
+
+               b->ptr++;
        }
 
-       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);
+       if(quote)
+               return _("quote mismatch");
+
+       if(options & (TOKEN_EQUAL | TOKEN_COMMA))
+               SKIPWS(b->ptr); /* whitespaces can precede the sign */
+
+       if((options & TOKEN_EQUAL) && (*b->ptr != '='))
+               return _("no assignment character found");
+
+       if((options & TOKEN_COMMA) && *b->ptr && (*b->ptr != ','))
+               return _("error in comma separated list");
+
+       if(b->ptr == b->data) {
+               b->data = NULL;
+               return NULL; /* no error, just end of parsing */
        }
+
+       if(options & TOKEN_ALLOC) /* freeing is the caller's responsibility */
+               b->data = xstrndup(b->data, end - b->data);
+       else
+               *end = 0;
+
+       b->ptr++; /* advance to next token */
+       SKIPWS(b->ptr);
+
+       return NULL;
 }
 
-void
-save_options()
+static const char *
+opt_set_set_option(char *p, struct option *opt)
 {
-       if( rcfile_exist() ) /* don't overwrite existing config */
-               return;
+       int len;
+
+       assert(p);
+
+       strtrim(p);
+       len = strlen(p);
 
-       conff_save_file(abook_config, rcfile);
+       if(*p == '\"' && p[len - 1] == '\"') {
+               if(len < 3)
+                       return _("invalid value");
+               p[len - 1] = 0;
+               p++;
+       }
+
+       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;
 }
 
-static void
-options_add_key(char *key, char *value)
+static const char *
+opt_set_option(char *var, char *p)
 {
-       const int flags = DONT_REPLACE_KEY;
+       int i;
+
+       assert(var);
+       assert(p);
+
+       for(i = 0; abook_vars[i].option; i++)
+               if(!strcmp(abook_vars[i].option, var))
+                       return opt_set_set_option(p, &abook_vars[i]);
 
-       conff_add_key(&abook_config, key, value, flags);
+       return _("unknown option");
 }
 
-static void
-default_options()
+static int
+check_options()
+{
+       char *str;
+       int err = 0;
+
+       str = opt_get_str(STR_PRESERVE_FIELDS);
+       if(strcasecmp(str, "all") && strcasecmp(str, "none") &&
+                       strcasecmp(str, "standard")) {
+               fprintf(stderr, _("valid values for the 'preserve_fields' "
+                                       "option are 'all', 'standard' "
+                                       "(default), and 'none'\n"));
+               restore_default(&abook_vars[STR_PRESERVE_FIELDS]);
+               err++;
+       }
+       str = opt_get_str(STR_ADDRESS_STYLE);
+       if(strcasecmp(str, "eu") && strcasecmp(str, "uk") &&
+                       strcasecmp(str, "us")) {
+               fprintf(stderr, _("valid values for the 'address_style' "
+                                       "option are 'eu' (default), 'uk', "
+                                       "and 'us'\n"));
+               restore_default(&abook_vars[STR_ADDRESS_STYLE]);
+               err++;
+       }
+
+       return err;
+}
+
+/*
+ * syntax: set <option> = <value>
+ */
+static const char *
+opt_parse_set(buffer *b)
+{
+       char *var, *err;
+
+       if((err = get_token(b, TOKEN_EQUAL)))
+               return err;
+
+       if((var = b->data) == NULL)
+               return _("invalid value assignment");
+
+       return opt_set_option(var, b->ptr);
+}
+
+static const char *
+opt_parse_customfield(buffer *b)
+{
+       return _("customfield: obsolete command - please use the "
+                       "'field' and 'view' commands instead");
+}
+
+#include "views.h" /* needed for add_field_to_view */
+
+/*
+ * syntax: view <tab name> = <field1> [ , <field2>, ... ]
+ */
+static const char *
+opt_parse_view(buffer *b)
+{
+       char *err, *view;
+
+       if((err = get_token(b, TOKEN_EQUAL)))
+               return err;
+
+       if((view = b->data) == NULL)
+               return _("no view name provided");
+
+       while(1) {
+               if((err = get_token(b, TOKEN_COMMA)))
+                       return err;
+
+               if(b->data == NULL)
+                       break;
+
+               if((err = add_field_to_view(view, b->data)))
+                       return err;
+       }
+
+       return NULL;
+}
+
+#include "database.h" /* needed for declare_new_field */
+
+/*
+ * syntax: field <identifier> = <human readable name> [ , <type> ]
+ */
+static const char *
+opt_parse_field(buffer *b)
+{
+       char *err, *field, *name;
+
+       if((err = get_token(b, TOKEN_EQUAL)))
+               return err;
+
+       if((field = b->data) == NULL)
+               return _("no field identifier provided");
+
+       if((err = get_token(b, TOKEN_COMMA)))
+               return err;
+
+       if((name = b->data) == NULL)
+               return _("no field name provided");
+
+       if((err = declare_new_field(field,
+                                       name,
+                                       b->ptr,
+                                       0 /* reject "standard" fields */)))
+               return err;
+
+       return NULL;
+}
+
+
+static struct {
+       char *token;
+       const char * (*func) (buffer *line);
+} opt_parsers[] = {
+       { "set", opt_parse_set },
+       { "customfield", opt_parse_customfield }, /* obsolete */
+       { "view", opt_parse_view },
+       { "field", opt_parse_field },
+       { 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;
 
-       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");
+       assert(line && fn);
 
-       options_add_key("mutt_command", "mutt");
-       options_add_key("mutt_return_all_emails", "true");
+       b.ptr = line;
 
-       options_add_key("print_command", "lpr");
+       if((err = get_token(&b, 0))) {
+               fprintf(stderr, "%s\n", err);
+               return FALSE;
+       }
+
+       if(b.data == NULL)
+               return FALSE;
+
+       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;
+               }
+
+       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("address_style", "eu");
+       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);
+
+       /* post-initialization */
+       err += check_options();
+       if(!strcasecmp(opt_get_str(STR_PRESERVE_FIELDS), "standard"))
+               init_standard_fields();
+
+       return err;
+}
+