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