]> git.deb.at Git - pkg/abook.git/blob - options.c
Merge branch 'dh'
[pkg/abook.git] / options.c
1
2 /*
3  * $Id: options.c,v 1.33 2006/09/06 08:48:33 jheinonen Exp $
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
71         { NULL }
72 };
73
74 static unsigned char bool_opts[BOOL_MAX];
75 static int int_opts[INT_MAXIMUM];
76 static char *str_opts[STR_MAX];
77
78 static void
79 set_int(enum int_opts opt, int value)
80 {
81         assert(opt >= 0 && opt < INT_MAXIMUM);
82
83         int_opts[opt] = value;
84 }
85
86 static void
87 set_bool(enum bool_opts opt, bool value)
88 {
89         assert(opt >= 0 && opt < BOOL_MAX);
90
91         bool_opts[opt] = value;
92 }
93
94 static void
95 set_str(enum str_opts opt, char *value)
96 {
97         assert(opt >= 0 && opt < STR_MAX);
98
99         if(str_opts[opt])
100                 free(str_opts[opt]);
101
102         str_opts[opt] = xstrdup(value);
103 }
104
105 int
106 opt_get_int(enum int_opts opt)
107 {
108         assert(opt >= 0 && opt < INT_MAXIMUM);
109
110         return int_opts[opt];
111 }
112
113 bool
114 opt_get_bool(enum bool_opts opt)
115 {
116         assert(opt >= 0 && opt < BOOL_MAX);
117
118         return bool_opts[opt];
119 }
120
121 char *
122 opt_get_str(enum str_opts opt)
123 {
124         assert(opt >= 0 && opt < STR_MAX);
125
126         return str_opts[opt];
127 }
128
129 static void
130 restore_default(struct option *p)
131 {
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);
145         }
146 }
147
148 void
149 init_opts()
150 {
151         int i;
152
153         for(i = 0; abook_vars[i].option; i++)
154                 restore_default(&abook_vars[i]);
155 }
156
157 void
158 free_opts()
159 {
160         int i;
161
162         /*
163          * only strings need to be freed
164          */
165         for(i = 0; i < STR_MAX; i++) {
166                 free(str_opts[i]);
167                 str_opts[i] = NULL;
168         }
169 }
170
171 /*
172  * file parsing
173  */
174
175 typedef struct {
176         char *data, *ptr;
177 } buffer;
178
179 static void
180 opt_line_remove_comments(char *p)
181 {
182         bool in_quote = FALSE;
183         bool escape = FALSE;
184
185         assert(p != NULL);
186
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;
200                                 }
201                         default:
202                                 escape = FALSE;
203                 }
204         }
205 }
206
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
210  *
211  * If the TOKEN_ALLOC option is used, the original string is not mangled
212  * and memory is allocated for the token.
213  */
214 static char *
215 get_token(buffer *b, int options)
216 {
217         char quote = 0, c;
218         char *end = NULL;
219
220         assert(b);
221
222         SKIPWS(b->ptr);
223         if(*b->ptr && strchr("\"'", *b->ptr))
224                 quote = *(b->ptr++);
225         b->data = b->ptr;
226
227         while(1) {
228                 if(!(c = *b->ptr)) {
229                         end = b->ptr;
230                         break;
231                 }
232
233                 if(!quote && (
234                                 ISSPACE(c) ||
235                                 ((options & TOKEN_EQUAL) && (c == '=')) ||
236                                 ((options & TOKEN_COMMA) && (c == ',')))
237                                 ) {
238                         end = b->ptr;
239                         break;
240                 } else if(c == quote) {
241                         quote = 0;
242                         end = b->ptr++;
243                         break;
244                 }
245
246                 b->ptr++;
247         }
248
249         if(quote)
250                 return _("quote mismatch");
251
252         if(options & (TOKEN_EQUAL | TOKEN_COMMA))
253                 SKIPWS(b->ptr); /* whitespaces can precede the sign */
254
255         if((options & TOKEN_EQUAL) && (*b->ptr != '='))
256                 return _("no assignment character found");
257
258         if((options & TOKEN_COMMA) && *b->ptr && (*b->ptr != ','))
259                 return _("error in comma separated list");
260
261         if(b->ptr == b->data) {
262                 b->data = NULL;
263                 return NULL; /* no error, just end of parsing */
264         }
265
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;
270
271         b->ptr++; /* advance to next token */
272         SKIPWS(b->ptr);
273
274         return NULL;
275 }
276
277 static const char *
278 opt_set_set_option(char *p, struct option *opt)
279 {
280         int len;
281
282         assert(p);
283
284         strtrim(p);
285         len = strlen(p);
286
287         if(*p == '\"' && p[len - 1] == '\"') {
288                 if(len < 3)
289                         return _("invalid value");
290                 p[len - 1] = 0;
291                 p++;
292         }
293
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);
312         }
313
314         return NULL;
315 }
316
317 static const char *
318 opt_set_option(char *var, char *p)
319 {
320         int i;
321
322         assert(var);
323         assert(p);
324
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]);
328
329         return _("unknown option");
330 }
331
332 static int
333 check_options()
334 {
335         char *str;
336         int err = 0;
337
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++;
346         }
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++;
355         }
356
357         return err;
358 }
359
360 /*
361  * syntax: set <option> = <value>
362  */
363 static const char *
364 opt_parse_set(buffer *b)
365 {
366         char *var, *err;
367
368         if((err = get_token(b, TOKEN_EQUAL)))
369                 return err;
370
371         if((var = b->data) == NULL)
372                 return _("invalid value assignment");
373
374         return opt_set_option(var, b->ptr);
375 }
376
377 static const char *
378 opt_parse_customfield(buffer *b)
379 {
380         return _("customfield: obsolete command - please use the "
381                         "'field' and 'view' commands instead");
382 }
383
384 #include "views.h" /* needed for add_field_to_view */
385
386 /*
387  * syntax: view <tab name> = <field1> [ , <field2>, ... ]
388  */
389 static const char *
390 opt_parse_view(buffer *b)
391 {
392         char *err, *view;
393
394         if((err = get_token(b, TOKEN_EQUAL)))
395                 return err;
396
397         if((view = b->data) == NULL)
398                 return _("no view name provided");
399
400         while(1) {
401                 if((err = get_token(b, TOKEN_COMMA)))
402                         return err;
403
404                 if(b->data == NULL)
405                         break;
406
407                 if((err = add_field_to_view(view, b->data)))
408                         return err;
409         }
410
411         return NULL;
412 }
413
414 #include "database.h" /* needed for declare_new_field */
415
416 /*
417  * syntax: field <identifier> = <human readable name> [ , <type> ]
418  */
419 static const char *
420 opt_parse_field(buffer *b)
421 {
422         char *err, *field, *name;
423
424         if((err = get_token(b, TOKEN_EQUAL)))
425                 return err;
426
427         if((field = b->data) == NULL)
428                 return _("no field identifier provided");
429
430         if((err = get_token(b, TOKEN_COMMA)))
431                 return err;
432
433         if((name = b->data) == NULL)
434                 return _("no field name provided");
435
436         if((err = declare_new_field(field,
437                                         name,
438                                         b->ptr,
439                                         0 /* reject "standard" fields */)))
440                 return err;
441
442         return NULL;
443 }
444
445
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 }
455 };
456
457 static bool
458 opt_parse_line(char *line, int n, char *fn)
459 {
460         int i;
461         const char *err = NULL;
462         char *token;
463         buffer b;
464
465         assert(line && fn);
466
467         b.ptr = line;
468
469         if((err = get_token(&b, 0))) {
470                 fprintf(stderr, "%s\n", err);
471                 return FALSE;
472         }
473
474         if(b.data == NULL)
475                 return FALSE;
476
477         strtrim(b.data);
478         strtrim(b.ptr);
479
480         token = b.data;
481         b.data = b.ptr = b.ptr;
482
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;
488                 }
489
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);
495
496         return TRUE;
497 }
498
499 int
500 load_opts(char *filename)
501 {
502         FILE *in;
503         char *line = NULL;
504         int n;
505         int err = 0;
506
507         if((in = fopen(filename, "r")) == NULL)
508                 return -1;
509
510         for(n = 1;!feof(in); n++) {
511                 line = getaline(in);
512
513                 if(feof(in))
514                         break;
515
516                 if(line && *line) {
517                         opt_line_remove_comments(line);
518                         if(*line)
519                                 err += opt_parse_line(line, n, filename) ? 1:0;
520                 }
521
522                 free(line);
523                 line = NULL;
524         }
525
526         free(line);
527
528         /* post-initialization */
529         err += check_options();
530         if(!strcasecmp(opt_get_str(STR_PRESERVE_FIELDS), "standard"))
531                 init_standard_fields();
532
533         return err;
534 }
535