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