use automake 1.11, autoconf 2.65
[abook.git] / options.c
blob92d56703c22d1782728aa895597f83f41e3b87df
2 /*
3 * $Id: options.c,v 1.33 2006/09/06 08:48:33 jheinonen Exp $
5 * by JH <jheinonen@users.sourceforge.net>
7 * Copyright (C) Jaakko Heinonen
9 */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <assert.h>
16 #include "options.h"
17 #include "abook.h"
18 #include "gettext.h"
19 #include "misc.h"
20 #include "views.h"
21 #include "xmalloc.h"
23 #ifndef FALSE
24 # define FALSE 0
25 #endif
26 #ifndef TRUE
27 # define TRUE 1
28 #endif
30 #define UL (unsigned long)
33 * option types
36 enum opt_type {
37 OT_BOOL,
38 OT_STR,
39 OT_INT
42 struct option {
43 char *option;
44 enum opt_type type;
45 unsigned int data;
46 unsigned long init;
49 static struct option abook_vars[] = {
50 { "autosave", OT_BOOL, BOOL_AUTOSAVE, TRUE },
52 { "show_all_emails", OT_BOOL, BOOL_SHOW_ALL_EMAILS, TRUE },
53 { "index_format", OT_STR, STR_INDEX_FORMAT, UL " {name:22} {email:40} {phone:12|workphone|mobile}" },
54 { "mutt_command", OT_STR, STR_MUTT_COMMAND, UL "mutt" },
55 { "mutt_return_all_emails", OT_BOOL, BOOL_MUTT_RETURN_ALL_EMAILS,
56 TRUE },
58 { "print_command", OT_STR, STR_PRINT_COMMAND, UL "lpr" },
60 { "www_command", OT_STR, STR_WWW_COMMAND, UL "lynx" },
62 { "address_style", OT_STR, STR_ADDRESS_STYLE, UL "eu" },
64 { "use_ascii_only", OT_BOOL, BOOL_USE_ASCII_ONLY, FALSE },
66 { "add_email_prevent_duplicates", OT_BOOL, BOOL_ADD_EMAIL_PREVENT_DUPLICATES, FALSE },
67 { "preserve_fields", OT_STR, STR_PRESERVE_FIELDS, UL "standard" },
68 { "sort_field", OT_STR, STR_SORT_FIELD, UL "nick" },
69 { "show_cursor", OT_BOOL, BOOL_SHOW_CURSOR, FALSE },
71 { NULL }
74 static unsigned char bool_opts[BOOL_MAX];
75 static int int_opts[INT_MAXIMUM];
76 static char *str_opts[STR_MAX];
78 static void
79 set_int(enum int_opts opt, int value)
81 assert(opt >= 0 && opt < INT_MAXIMUM);
83 int_opts[opt] = value;
86 static void
87 set_bool(enum bool_opts opt, bool value)
89 assert(opt >= 0 && opt < BOOL_MAX);
91 bool_opts[opt] = value;
94 static void
95 set_str(enum str_opts opt, char *value)
97 assert(opt >= 0 && opt < STR_MAX);
99 if(str_opts[opt])
100 free(str_opts[opt]);
102 str_opts[opt] = xstrdup(value);
106 opt_get_int(enum int_opts opt)
108 assert(opt >= 0 && opt < INT_MAXIMUM);
110 return int_opts[opt];
113 bool
114 opt_get_bool(enum bool_opts opt)
116 assert(opt >= 0 && opt < BOOL_MAX);
118 return bool_opts[opt];
121 char *
122 opt_get_str(enum str_opts opt)
124 assert(opt >= 0 && opt < STR_MAX);
126 return str_opts[opt];
129 static void
130 restore_default(struct option *p)
132 switch(p -> type) {
133 case OT_BOOL:
134 set_bool(p -> data, (bool)p -> init);
135 break;
136 case OT_INT:
137 set_int(p -> data, (int)p -> init);
138 break;
139 case OT_STR:
140 if(p -> init)
141 set_str(p -> data, (char *) p -> init);
142 break;
143 default:
144 assert(0);
148 void
149 init_opts()
151 int i;
153 for(i = 0; abook_vars[i].option; i++)
154 restore_default(&abook_vars[i]);
157 void
158 free_opts()
160 int i;
163 * only strings need to be freed
165 for(i = 0; i < STR_MAX; i++) {
166 free(str_opts[i]);
167 str_opts[i] = NULL;
172 * file parsing
175 typedef struct {
176 char *data, *ptr;
177 } buffer;
179 static void
180 opt_line_remove_comments(char *p)
182 bool in_quote = FALSE;
183 bool escape = FALSE;
185 assert(p != NULL);
187 for(; *p; p++) {
188 switch(*p) {
189 case '\"':
190 if(!escape)
191 in_quote = !in_quote;
192 break;
193 case '\\':
194 escape = TRUE;
195 break;
196 case '#':
197 if(!in_quote) {
198 *p = 0;
199 return;
201 default:
202 escape = FALSE;
207 /* After calling,
208 * - b->data points to the found token, or NULL is end of parsing
209 * - b->ptr points to the begining of next token
211 * If the TOKEN_ALLOC option is used, the original string is not mangled
212 * and memory is allocated for the token.
214 static char *
215 get_token(buffer *b, int options)
217 char quote = 0, c;
218 char *end = NULL;
220 assert(b);
222 SKIPWS(b->ptr);
223 if(*b->ptr && strchr("\"'", *b->ptr))
224 quote = *(b->ptr++);
225 b->data = b->ptr;
227 while(1) {
228 if(!(c = *b->ptr)) {
229 end = b->ptr;
230 break;
233 if(!quote && (
234 ISSPACE(c) ||
235 ((options & TOKEN_EQUAL) && (c == '=')) ||
236 ((options & TOKEN_COMMA) && (c == ',')))
238 end = b->ptr;
239 break;
240 } else if(c == quote) {
241 quote = 0;
242 end = b->ptr++;
243 break;
246 b->ptr++;
249 if(quote)
250 return _("quote mismatch");
252 if(options & (TOKEN_EQUAL | TOKEN_COMMA))
253 SKIPWS(b->ptr); /* whitespaces can precede the sign */
255 if((options & TOKEN_EQUAL) && (*b->ptr != '='))
256 return _("no assignment character found");
258 if((options & TOKEN_COMMA) && *b->ptr && (*b->ptr != ','))
259 return _("error in comma separated list");
261 if(b->ptr == b->data) {
262 b->data = NULL;
263 return NULL; /* no error, just end of parsing */
266 if(options & TOKEN_ALLOC) /* freeing is the caller's responsibility */
267 b->data = xstrndup(b->data, end - b->data);
268 else
269 *end = 0;
271 b->ptr++; /* advance to next token */
272 SKIPWS(b->ptr);
274 return NULL;
277 static const char *
278 opt_set_set_option(char *p, struct option *opt)
280 int len;
282 assert(p);
284 strtrim(p);
285 len = strlen(p);
287 if(*p == '\"' && p[len - 1] == '\"') {
288 if(len < 3)
289 return _("invalid value");
290 p[len - 1] = 0;
291 p++;
294 switch(opt -> type) {
295 case OT_STR:
296 set_str(opt -> data, p);
297 break;
298 case OT_INT:
299 set_int(opt -> data, safe_atoi(p));
300 break;
301 case OT_BOOL:
302 if(!strcasecmp(p, "true") || !strcasecmp(p, "on"))
303 set_bool(opt -> data, TRUE);
304 else if(!strcasecmp(p, "false") ||
305 !strcasecmp(p, "off"))
306 set_bool(opt -> data, FALSE);
307 else
308 return _("invalid value");
309 break;
310 default:
311 assert(0);
314 return NULL;
317 static const char *
318 opt_set_option(char *var, char *p)
320 int i;
322 assert(var);
323 assert(p);
325 for(i = 0; abook_vars[i].option; i++)
326 if(!strcmp(abook_vars[i].option, var))
327 return opt_set_set_option(p, &abook_vars[i]);
329 return _("unknown option");
332 static int
333 check_options()
335 char *str;
336 int err = 0;
338 str = opt_get_str(STR_PRESERVE_FIELDS);
339 if(strcasecmp(str, "all") && strcasecmp(str, "none") &&
340 strcasecmp(str, "standard")) {
341 fprintf(stderr, _("valid values for the 'preserve_fields' "
342 "option are 'all', 'standard' "
343 "(default), and 'none'\n"));
344 restore_default(&abook_vars[STR_PRESERVE_FIELDS]);
345 err++;
347 str = opt_get_str(STR_ADDRESS_STYLE);
348 if(strcasecmp(str, "eu") && strcasecmp(str, "uk") &&
349 strcasecmp(str, "us")) {
350 fprintf(stderr, _("valid values for the 'address_style' "
351 "option are 'eu' (default), 'uk', "
352 "and 'us'\n"));
353 restore_default(&abook_vars[STR_ADDRESS_STYLE]);
354 err++;
357 return err;
361 * syntax: set <option> = <value>
363 static const char *
364 opt_parse_set(buffer *b)
366 char *var, *err;
368 if((err = get_token(b, TOKEN_EQUAL)))
369 return err;
371 if((var = b->data) == NULL)
372 return _("invalid value assignment");
374 return opt_set_option(var, b->ptr);
377 static const char *
378 opt_parse_customfield(buffer *b)
380 return _("customfield: obsolete command - please use the "
381 "'field' and 'view' commands instead");
384 #include "views.h" /* needed for add_field_to_view */
387 * syntax: view <tab name> = <field1> [ , <field2>, ... ]
389 static const char *
390 opt_parse_view(buffer *b)
392 char *err, *view;
394 if((err = get_token(b, TOKEN_EQUAL)))
395 return err;
397 if((view = b->data) == NULL)
398 return _("no view name provided");
400 while(1) {
401 if((err = get_token(b, TOKEN_COMMA)))
402 return err;
404 if(b->data == NULL)
405 break;
407 if((err = add_field_to_view(view, b->data)))
408 return err;
411 return NULL;
414 #include "database.h" /* needed for declare_new_field */
417 * syntax: field <identifier> = <human readable name> [ , <type> ]
419 static const char *
420 opt_parse_field(buffer *b)
422 char *err, *field, *name;
424 if((err = get_token(b, TOKEN_EQUAL)))
425 return err;
427 if((field = b->data) == NULL)
428 return _("no field identifier provided");
430 if((err = get_token(b, TOKEN_COMMA)))
431 return err;
433 if((name = b->data) == NULL)
434 return _("no field name provided");
436 if((err = declare_new_field(field,
437 name,
438 b->ptr,
439 0 /* reject "standard" fields */)))
440 return err;
442 return NULL;
446 static struct {
447 char *token;
448 const char * (*func) (buffer *line);
449 } opt_parsers[] = {
450 { "set", opt_parse_set },
451 { "customfield", opt_parse_customfield }, /* obsolete */
452 { "view", opt_parse_view },
453 { "field", opt_parse_field },
454 { NULL }
457 static bool
458 opt_parse_line(char *line, int n, char *fn)
460 int i;
461 const char *err = NULL;
462 char *token;
463 buffer b;
465 assert(line && fn);
467 b.ptr = line;
469 if((err = get_token(&b, 0))) {
470 fprintf(stderr, "%s\n", err);
471 return FALSE;
474 if(b.data == NULL)
475 return FALSE;
477 strtrim(b.data);
478 strtrim(b.ptr);
480 token = b.data;
481 b.data = b.ptr = b.ptr;
483 for(i = 0; opt_parsers[i].token; i++)
484 if(!strcmp(opt_parsers[i].token, token)) {
485 if(!(err = opt_parsers[i].func(&b)))
486 return FALSE;
487 break;
490 fprintf(stderr, _("%s: parse error at line %d: "), fn, n);
491 if(err)
492 fprintf(stderr, "%s\n", err);
493 else
494 fprintf(stderr, _("unknown token %s\n"), token);
496 return TRUE;
500 load_opts(char *filename)
502 FILE *in;
503 char *line = NULL;
504 int n;
505 int err = 0;
507 if((in = fopen(filename, "r")) == NULL)
508 return -1;
510 for(n = 1;!feof(in); n++) {
511 line = getaline(in);
513 if(feof(in))
514 break;
516 if(line && *line) {
517 opt_line_remove_comments(line);
518 if(*line)
519 err += opt_parse_line(line, n, filename) ? 1:0;
522 free(line);
523 line = NULL;
526 free(line);
528 /* post-initialization */
529 err += check_options();
530 if(!strcasecmp(opt_get_str(STR_PRESERVE_FIELDS), "standard"))
531 init_standard_fields();
533 return err;