]> git.deb.at Git - pkg/abook.git/blob - options.c
autotools update: 2/2: autofiles after `autoreconf -f`
[pkg/abook.git] / options.c
1
2 /*
3  * $Id$
4  *
5  * by JH <jheinonen@users.sourceforge.net>
6  *
7  * Copyright (C) Jaakko Heinonen
8  *
9  */
10
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"
22
23 #ifndef FALSE
24 #       define FALSE    0
25 #endif
26 #ifndef TRUE
27 #       define TRUE     1
28 #endif
29
30 #define UL      (unsigned long)
31
32 /*
33  * option types
34  */
35
36 enum opt_type {
37         OT_BOOL,
38         OT_STR,
39         OT_INT
40 };
41
42 struct option {
43         char *option;
44         enum opt_type type;
45         unsigned int data;
46         unsigned long init;
47 };
48
49 static struct option abook_vars[] = {
50         { "autosave", OT_BOOL, BOOL_AUTOSAVE, TRUE },
51
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 },
57
58         { "print_command", OT_STR, STR_PRINT_COMMAND, UL "lpr" },
59
60         { "www_command", OT_STR, STR_WWW_COMMAND, UL "lynx" },
61
62         { "address_style", OT_STR, STR_ADDRESS_STYLE, UL "eu" },
63
64         { "use_ascii_only", OT_BOOL, BOOL_USE_ASCII_ONLY, FALSE },
65
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 },
70         { "use_mouse", OT_BOOL, BOOL_USE_MOUSE, FALSE },
71         { "use_colors", OT_BOOL, BOOL_USE_COLORS, FALSE },
72         { "color_header_fg", OT_STR, STR_COLOR_HEADER_FG, UL "blue" },
73         { "color_header_fg", OT_STR, STR_COLOR_HEADER_FG, UL "blue" },
74         { "color_header_bg", OT_STR, STR_COLOR_HEADER_BG, UL "red" },
75         { "color_footer_fg", OT_STR, STR_COLOR_FOOTER_FG, UL "red" },
76         { "color_footer_bg", OT_STR, STR_COLOR_FOOTER_BG, UL "default" },
77         { "color_list_even_fg", OT_STR, STR_COLOR_LIST_EVEN_FG, UL "yellow" },
78         { "color_list_even_bg", OT_STR, STR_COLOR_LIST_EVEN_BG, UL "default" },
79         { "color_list_odd_fg", OT_STR, STR_COLOR_LIST_ODD_FG, UL "default" },
80         { "color_list_odd_bg", OT_STR, STR_COLOR_LIST_ODD_BG, UL "default" },
81         { "color_list_header_fg", OT_STR, STR_COLOR_LIST_HEADER_FG, UL "white" },
82         { "color_list_header_bg", OT_STR, STR_COLOR_LIST_HEADER_BG, UL "blue" },
83         { "color_list_highlight_fg", OT_STR, STR_COLOR_LIST_HIGHLIGHT_FG, UL "black" },
84         { "color_list_highlight_bg", OT_STR, STR_COLOR_LIST_HIGHLIGHT_BG, UL "green" },
85         { "color_tab_border_fg", OT_STR, STR_COLOR_TAB_BORDER_FG, UL "cyan" },
86         { "color_tab_border_bg", OT_STR, STR_COLOR_TAB_BORDER_BG, UL "default" },
87         { "color_tab_label_fg", OT_STR, STR_COLOR_TAB_LABEL_FG, UL "magenta" },
88         { "color_tab_label_bg", OT_STR, STR_COLOR_TAB_LABEL_BG, UL "default" },
89         { "color_field_name_fg", OT_STR, STR_COLOR_FIELD_NAME_FG, UL "yellow" },
90         { "color_field_name_bg", OT_STR, STR_COLOR_FIELD_NAME_BG, UL "default" },
91         { "color_field_value_fg", OT_STR, STR_COLOR_FIELD_VALUE_FG, UL "green" },
92         { "color_field_value_bg", OT_STR, STR_COLOR_FIELD_VALUE_BG, UL "default" },
93         { NULL }
94 };
95
96 static unsigned char bool_opts[BOOL_MAX];
97 static int int_opts[INT_MAXIMUM];
98 static char *str_opts[STR_MAX];
99
100 static void
101 set_int(enum int_opts opt, int value)
102 {
103         assert(opt >= 0 && opt < INT_MAXIMUM);
104
105         int_opts[opt] = value;
106 }
107
108 static void
109 set_bool(enum bool_opts opt, bool value)
110 {
111         assert(opt >= 0 && opt < BOOL_MAX);
112
113         bool_opts[opt] = value;
114 }
115
116 static void
117 set_str(enum str_opts opt, char *value)
118 {
119         assert(opt >= 0 && opt < STR_MAX);
120
121         if(str_opts[opt])
122                 free(str_opts[opt]);
123
124         str_opts[opt] = xstrdup(value);
125 }
126
127 int
128 opt_get_int(enum int_opts opt)
129 {
130         assert(opt >= 0 && opt < INT_MAXIMUM);
131
132         return int_opts[opt];
133 }
134
135 bool
136 opt_get_bool(enum bool_opts opt)
137 {
138         assert(opt >= 0 && opt < BOOL_MAX);
139
140         return bool_opts[opt];
141 }
142
143 char *
144 opt_get_str(enum str_opts opt)
145 {
146         assert(opt >= 0 && opt < STR_MAX);
147
148         return str_opts[opt];
149 }
150
151 static void
152 restore_default(struct option *p)
153 {
154         switch(p -> type) {
155                 case OT_BOOL:
156                         set_bool(p -> data, (bool)p -> init);
157                         break;
158                 case OT_INT:
159                         set_int(p -> data, (int)p -> init);
160                         break;
161                 case OT_STR:
162                         if(p -> init)
163                                 set_str(p -> data, (char *) p -> init);
164                         break;
165                 default:
166                         assert(0);
167         }
168 }
169
170 void
171 init_opts()
172 {
173         int i;
174
175         for(i = 0; abook_vars[i].option; i++)
176                 restore_default(&abook_vars[i]);
177 }
178
179 void
180 free_opts()
181 {
182         int i;
183
184         /*
185          * only strings need to be freed
186          */
187         for(i = 0; i < STR_MAX; i++) {
188                 free(str_opts[i]);
189                 str_opts[i] = NULL;
190         }
191 }
192
193 /*
194  * file parsing
195  */
196
197 typedef struct {
198         char *data, *ptr;
199 } buffer;
200
201 static void
202 opt_line_remove_comments(char *p)
203 {
204         bool in_quote = FALSE;
205         bool escape = FALSE;
206
207         assert(p != NULL);
208
209         for(; *p; p++) {
210                 switch(*p) {
211                         case '\"':
212                                 if(!escape)
213                                         in_quote = !in_quote;
214                                 break;
215                         case '\\':
216                                 escape = TRUE;
217                                 break;
218                         case '#':
219                                 if(!in_quote) {
220                                         *p = 0;
221                                         return;
222                                 }
223                         default:
224                                 escape = FALSE;
225                 }
226         }
227 }
228
229 /* After calling,
230  * - b->data points to the found token, or NULL is end of parsing
231  * - b->ptr  points to the begining of next token
232  *
233  * If the TOKEN_ALLOC option is used, the original string is not mangled
234  * and memory is allocated for the token.
235  */
236 static char *
237 get_token(buffer *b, int options)
238 {
239         char quote = 0, c;
240         char *end = NULL;
241
242         assert(b);
243
244         SKIPWS(b->ptr);
245         if(*b->ptr && strchr("\"'", *b->ptr))
246                 quote = *(b->ptr++);
247         b->data = b->ptr;
248
249         while(1) {
250                 if(!(c = *b->ptr)) {
251                         end = b->ptr;
252                         break;
253                 }
254
255                 if(!quote && (
256                                 ISSPACE(c) ||
257                                 ((options & TOKEN_EQUAL) && (c == '=')) ||
258                                 ((options & TOKEN_COMMA) && (c == ',')))
259                                 ) {
260                         end = b->ptr;
261                         break;
262                 } else if(c == quote) {
263                         quote = 0;
264                         end = b->ptr++;
265                         break;
266                 }
267
268                 b->ptr++;
269         }
270
271         if(quote)
272                 return _("quote mismatch");
273
274         if(options & (TOKEN_EQUAL | TOKEN_COMMA))
275                 SKIPWS(b->ptr); /* whitespaces can precede the sign */
276
277         if((options & TOKEN_EQUAL) && (*b->ptr != '='))
278                 return _("no assignment character found");
279
280         if((options & TOKEN_COMMA) && *b->ptr && (*b->ptr != ','))
281                 return _("error in comma separated list");
282
283         if(b->ptr == b->data) {
284                 b->data = NULL;
285                 return NULL; /* no error, just end of parsing */
286         }
287
288         if(options & TOKEN_ALLOC) /* freeing is the caller's responsibility */
289                 b->data = xstrndup(b->data, end - b->data);
290         else
291                 *end = 0;
292
293         b->ptr++; /* advance to next token */
294         SKIPWS(b->ptr);
295
296         return NULL;
297 }
298
299 static const char *
300 opt_set_set_option(char *p, struct option *opt)
301 {
302         int len;
303
304         assert(p);
305
306         strtrim(p);
307         len = strlen(p);
308
309         if(*p == '\"' && p[len - 1] == '\"') {
310                 if(len < 3)
311                         return _("invalid value");
312                 p[len - 1] = 0;
313                 p++;
314         }
315
316         switch(opt -> type) {
317                 case OT_STR:
318                         set_str(opt -> data, p);
319                         break;
320                 case OT_INT:
321                         set_int(opt -> data, safe_atoi(p));
322                         break;
323                 case OT_BOOL:
324                         if(!strcasecmp(p, "true") || !strcasecmp(p, "on"))
325                                 set_bool(opt -> data, TRUE);
326                         else if(!strcasecmp(p, "false") ||
327                                         !strcasecmp(p, "off"))
328                                 set_bool(opt -> data, FALSE);
329                         else
330                                 return _("invalid value");
331                         break;
332                 default:
333                         assert(0);
334         }
335
336         return NULL;
337 }
338
339 static const char *
340 opt_set_option(char *var, char *p)
341 {
342         int i;
343
344         assert(var);
345         assert(p);
346
347         for(i = 0; abook_vars[i].option; i++)
348                 if(!strcmp(abook_vars[i].option, var))
349                         return opt_set_set_option(p, &abook_vars[i]);
350
351         return _("unknown option");
352 }
353
354 static int
355 check_options()
356 {
357         char *str;
358         int err = 0;
359
360         str = opt_get_str(STR_PRESERVE_FIELDS);
361         if(strcasecmp(str, "all") && strcasecmp(str, "none") &&
362                         strcasecmp(str, "standard")) {
363                 fprintf(stderr, _("valid values for the 'preserve_fields' "
364                                         "option are 'all', 'standard' "
365                                         "(default), and 'none'\n"));
366                 restore_default(&abook_vars[STR_PRESERVE_FIELDS]);
367                 err++;
368         }
369         str = opt_get_str(STR_ADDRESS_STYLE);
370         if(strcasecmp(str, "eu") && strcasecmp(str, "uk") &&
371                         strcasecmp(str, "us")) {
372                 fprintf(stderr, _("valid values for the 'address_style' "
373                                         "option are 'eu' (default), 'uk', "
374                                         "and 'us'\n"));
375                 restore_default(&abook_vars[STR_ADDRESS_STYLE]);
376                 err++;
377         }
378
379         return err;
380 }
381
382 /*
383  * syntax: set <option> = <value>
384  */
385 static const char *
386 opt_parse_set(buffer *b)
387 {
388         char *var, *err;
389
390         if((err = get_token(b, TOKEN_EQUAL)))
391                 return err;
392
393         if((var = b->data) == NULL)
394                 return _("invalid value assignment");
395
396         return opt_set_option(var, b->ptr);
397 }
398
399 static const char *
400 opt_parse_customfield(buffer *b)
401 {
402         return _("customfield: obsolete command - please use the "
403                         "'field' and 'view' commands instead");
404 }
405
406 #include "views.h" /* needed for add_field_to_view */
407
408 /*
409  * syntax: view <tab name> = <field1> [ , <field2>, ... ]
410  */
411 static const char *
412 opt_parse_view(buffer *b)
413 {
414         char *err, *view;
415
416         if((err = get_token(b, TOKEN_EQUAL)))
417                 return err;
418
419         if((view = b->data) == NULL)
420                 return _("no view name provided");
421
422         while(1) {
423                 if((err = get_token(b, TOKEN_COMMA)))
424                         return err;
425
426                 if(b->data == NULL)
427                         break;
428
429                 if((err = add_field_to_view(view, b->data)))
430                         return err;
431         }
432
433         return NULL;
434 }
435
436 #include "database.h" /* needed for declare_new_field */
437
438 /*
439  * syntax: field <identifier> = <human readable name> [ , <type> ]
440  */
441 static const char *
442 opt_parse_field(buffer *b)
443 {
444         char *err, *field, *name;
445
446         if((err = get_token(b, TOKEN_EQUAL)))
447                 return err;
448
449         if((field = b->data) == NULL)
450                 return _("no field identifier provided");
451
452         if((err = get_token(b, TOKEN_COMMA)))
453                 return err;
454
455         if((name = b->data) == NULL)
456                 return _("no field name provided");
457
458         if((err = declare_new_field(field,
459                                         name,
460                                         b->ptr,
461                                         0 /* reject "standard" fields */)))
462                 return err;
463
464         return NULL;
465 }
466
467
468 static struct {
469         char *token;
470         const char * (*func) (buffer *line);
471 } opt_parsers[] = {
472         { "set", opt_parse_set },
473         { "customfield", opt_parse_customfield }, /* obsolete */
474         { "view", opt_parse_view },
475         { "field", opt_parse_field },
476         { NULL }
477 };
478
479 static bool
480 opt_parse_line(char *line, int n, char *fn)
481 {
482         int i;
483         const char *err = NULL;
484         char *token;
485         buffer b;
486
487         assert(line && fn);
488
489         b.ptr = line;
490
491         if((err = get_token(&b, 0))) {
492                 fprintf(stderr, "%s\n", err);
493                 return FALSE;
494         }
495
496         if(b.data == NULL)
497                 return FALSE;
498
499         strtrim(b.data);
500         strtrim(b.ptr);
501
502         token = b.data;
503         b.data = b.ptr = b.ptr;
504
505         for(i = 0; opt_parsers[i].token; i++)
506                 if(!strcmp(opt_parsers[i].token, token)) {
507                         if(!(err = opt_parsers[i].func(&b)))
508                                 return FALSE;
509                         break;
510                 }
511
512         fprintf(stderr, _("%s: parse error at line %d: "), fn, n);
513         if(err)
514                 fprintf(stderr, "%s\n", err);
515         else
516                 fprintf(stderr, _("unknown token %s\n"), token);
517
518         return TRUE;
519 }
520
521 int
522 load_opts(char *filename)
523 {
524         FILE *in;
525         char *line = NULL;
526         int n;
527         int err = 0;
528
529         if((in = fopen(filename, "r")) == NULL)
530                 return -1;
531
532         for(n = 1;!feof(in); n++) {
533                 line = getaline(in);
534
535                 if(feof(in))
536                         break;
537
538                 if(line && *line) {
539                         opt_line_remove_comments(line);
540                         if(*line)
541                                 err += opt_parse_line(line, n, filename) ? 1:0;
542                 }
543
544                 free(line);
545                 line = NULL;
546         }
547
548         free(line);
549
550         /* post-initialization */
551         err += check_options();
552         if(!strcasecmp(opt_get_str(STR_PRESERVE_FIELDS), "standard"))
553                 init_standard_fields();
554
555         return err;
556 }
557