#include <assert.h>
#include "options.h"
#include "abook.h"
+#include "gettext.h"
#include "misc.h"
+#include "views.h"
+#include "xmalloc.h"
#ifndef FALSE
# define FALSE 0
{ "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 },
-
+ { "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 }
};
if(str_opts[opt])
free(str_opts[opt]);
- str_opts[opt] = strdup(value);
+ str_opts[opt] = xstrdup(value);
}
int
for(; *p; p++) {
switch(*p) {
case '\"':
- if(!escape) {
+ if(!escape)
in_quote = !in_quote;
- escape = FALSE;
- }
break;
case '\\':
escape = TRUE;
}
}
-void
-find_token_start(buffer *b)
+/* 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)
{
- assert(b);
-
- for(; ISSPACE(*b -> ptr); b -> ptr ++);
-}
+ char quote = 0, c;
+ char *end = NULL;
-void
-find_token_end(buffer *b)
-{
assert(b);
- for(find_token_start(b); *(b -> ptr); b -> ptr ++) {
- if(ISSPACE(*(b -> ptr))) {
+ 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(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;
}
-static char *
-opt_set_set_option(char *var, char *p, struct option *opt)
+static const char *
+opt_set_set_option(char *p, struct option *opt)
{
int len;
-
- strtrim(p);
+ assert(p);
+
+ strtrim(p);
len = strlen(p);
- if(p[len - 1] == '\"' && *p == '\"') {
+ if(*p == '\"' && p[len - 1] == '\"') {
if(len < 3)
- return "invalid value";
+ return _("invalid value");
p[len - 1] = 0;
p++;
}
!strcasecmp(p, "off"))
set_bool(opt -> data, FALSE);
else
- return "invalid value";
+ return _("invalid value");
break;
+ default:
+ assert(0);
}
-
+
return NULL;
}
-static char *
-opt_parse_set(buffer *b)
+static const char *
+opt_set_option(char *var, char *p)
{
int i;
- char *p;
- 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";
+ 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]);
+
+ return _("unknown option");
}
-static char *
+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)
{
- char *p, num[5];
- int n;
- size_t len;
+ 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;
- find_token_start(b);
+ 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;
+}
- p = b -> ptr;
+#include "database.h" /* needed for declare_new_field */
- find_token_end(b);
+/*
+ * syntax: field <identifier> = <human readable name> [ , <type> ]
+ */
+static const char *
+opt_parse_field(buffer *b)
+{
+ char *err, *field, *name;
- memset(num, 0, sizeof(num));
+ if((err = get_token(b, TOKEN_EQUAL)))
+ return err;
- if((len = (b -> ptr - p)) > sizeof(num))
- return "invalid custom field number";
+ if((field = b->data) == NULL)
+ return _("no field identifier provided");
- strncpy(num, p, min(sizeof(num), len));
- n = atoi(num);
+ if((err = get_token(b, TOKEN_COMMA)))
+ return err;
- find_token_start(b);
+ if((name = b->data) == NULL)
+ return _("no field name provided");
- if(change_custom_field_name(b->ptr, n) == -1)
- return "invalid custom field number";
+ if((err = declare_new_field(field,
+ name,
+ b->ptr,
+ 0 /* reject "standard" fields */)))
+ return err;
return NULL;
}
+
static struct {
char *token;
- char * (*func) (buffer *line);
+ const char * (*func) (buffer *line);
} opt_parsers[] = {
{ "set", opt_parse_set },
- { "customfield", opt_parse_customfield },
+ { "customfield", opt_parse_customfield }, /* obsolete */
+ { "view", opt_parse_view },
+ { "field", opt_parse_field },
{ NULL }
};
opt_parse_line(char *line, int n, char *fn)
{
int i;
- char *err = NULL;
+ const char *err = NULL;
char *token;
buffer b;
-
+
assert(line && fn);
b.ptr = line;
- find_token_start(&b);
- b.data = b.ptr;
- find_token_end(&b);
- *b.ptr++ = 0;
+ if((err = get_token(&b, 0))) {
+ fprintf(stderr, "%s\n", err);
+ return FALSE;
+ }
- if(!*line)
+ if(b.data == NULL)
return FALSE;
strtrim(b.data);
return FALSE;
break;
}
-
- fprintf(stderr, "%s: parse error at line %d: ", fn, n);
+
+ 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);
+ fprintf(stderr, _("unknown token %s\n"), token);
return TRUE;
}
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);
err += opt_parse_line(line, n, filename) ? 1:0;
}
- my_free(line);
+ 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;
}