1 /*
2  * Option handling within climm.
3  *
4  * climm Copyright (C) © 2003-2010 Rüdiger Kuhlmann
5  *
6  * climm is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 dated June, 1991.
9  *
10  * climm is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
13  * License for more details.
14  *
15  * In addition, as a special exception permission is granted to link the
16  * code of this release of climm with the OpenSSL project's "OpenSSL"
17  * library, and distribute the linked executables.  You must obey the GNU
18  * General Public License in all respects for all of the code used other
19  * than "OpenSSL".  If you modify this file, you may extend this exception
20  * to your version of the file, but you are not obligated to do so.  If you
21  * do not wish to do so, delete this exception statement from your version
22  * of this file.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this package; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27  * 02111-1307, USA.
28  *
29  * $Id: util_opts.c 2864 2010-03-16 22:48:25Z kuhlmann $
30  */
31 
32 #include "climm.h"
33 #include <assert.h>
34 #include <stdarg.h>
35 #include "util_opts.h"
36 #include "util_parse.h"
37 #include "conv.h"
38 #include "util_ui.h"
39 #include "preferences.h"
40 
41 static char **strtable = NULL;
42 static int strmax = 0;
43 static int struse = 0;
44 static int strmin = 0;
45 
46 typedef struct OptTable_s COT;
47 
48 struct OptEntry_s OptList[] = {
49   { "intimate",      CO_INTIMATE      },
50   { "hidefrom",      CO_HIDEFROM      },
51   { "ignore",        CO_IGNORE        },
52   { "logonoff",      CO_LOGONOFF      },
53   { "logchange",     CO_LOGCHANGE     },
54   { "logmess",       CO_LOGMESS       },
55   { "showonoff",     CO_SHOWONOFF     },
56   { "showchange",    CO_SHOWCHANGE    },
57   { "autoauto",      CO_AUTOAUTO      },
58   { "logstream",     CO_LOGSTREAM     },
59   { "hideack",       CO_HIDEACK       },
60   { "wantsbl",       CO_WANTSBL       },
61   { "peekme",        CO_PEEKME        },
62   { "obeysbl",       CO_OBEYSBL       },
63   { "awaycount",     CO_AWAYCOUNT     },
64   { "shadow",        CO_SHADOW        },
65   { "local",         CO_LOCAL         },
66   { "webaware",      CO_WEBAWARE      },
67   { "hideip",        CO_HIDEIP        },
68   { "dcauth",        CO_DCAUTH        },
69   { "dccont",        CO_DCCONT        },
70   { "tagressource",  CO_TAGRESSOURCE  },
71   { "revealtime",    CO_REVEALTIME    },
72   { "autoaway",      CO_AUTOAWAY      },
73   { "autona",        CO_AUTONA        },
74   { "autoocc",       CO_AUTOOCC       },
75   { "autodnd",       CO_AUTODND       },
76   { "autoffc",       CO_AUTOFFC       },
77   { "colornone",     CO_COLORNONE     },
78   { "colorserver",   CO_COLORSERVER   },
79   { "colorclient",   CO_COLORCLIENT   },
80   { "colorerror",    CO_COLORERROR    },
81   { "colordebug",    CO_COLORDEBUG    },
82   { "colorquote",    CO_COLORQUOTE    },
83   { "colorinvchar",  CO_COLORINVCHAR  },
84   { "colormessage",  CO_COLORMESSAGE  },
85   { "colorincoming", CO_COLORINCOMING },
86   { "colorsent",     CO_COLORSENT     },
87   { "colorack",      CO_COLORACK      },
88   { "colorcontact",  CO_COLORCONTACT  },
89   { "encoding",      CO_ENCODINGSTR   }, /* not CO_ENCODING */
90   { "colorscheme",   CO_CSCHEME       },
91   { "tabspool",      CO_TABSPOOL      },
92   { "timeseen",      CO_TIMESEEN      },
93   { "timeonline",    CO_TIMEONLINE    },
94   { "timeclimm",     CO_TIMECLIMM     },
95   { "oscar_dc_mode", CO_OSCAR_DC_MODE },
96   { "oscar_dc_port", CO_OSCAR_DC_PORT },
97   { "scripting",     CO_SCRIPT        },
98   { "scriptingpath", CO_SCRIPT_PATH   },
99   { "s5_use",        CO_S5USE         },
100   { "s5_port",       CO_S5PORT        },
101   { "s5_host",       CO_S5HOST        },
102   { "s5_name",       CO_S5NAME        },
103   { "s5_pass",       CO_S5PASS        },
104   { "privacylist",   CO_XMPP_PRIV     },
105 #ifdef ENABLE_OTR
106   { "otrpolicy",     CO_OTRPOLICY     },
107 #endif
108   { NULL, 0 }
109 };
110 
111 /*
112  * Create new contact options
113  */
OptC(void)114 Opt *OptC (void)
115 {
116     return calloc (sizeof (Opt), 1);
117 }
118 
119 /*
120  * Delete contact options
121  */
OptD(Opt * opt)122 void OptD (Opt *opt)
123 {
124     Opt *cot;
125     UDWORD flag;
126     val_t val;
127     int i;
128 
129     if (!opt)
130         return;
131     for (i = 0; OptList[i].name; i++)
132     {
133         if (~(flag = OptList[i].flag) & (COF_STRING | COF_COLOR))
134             continue;
135         if ((val = OptUndef (opt, flag)))
136         {
137             free (strtable[val]);
138             strtable[val] = NULL;
139             if (val + 1 == struse)
140                 while (!strtable[val])
141                     val--, struse--;
142             if (val < strmin)
143                 strmin = val;
144         }
145     }
146     while (opt)
147     {
148         cot = opt->next;
149         free (opt);
150         opt = cot;
151     }
152 }
153 
154 /*
155  * Import options from a string.
156  */
OptImport(Opt * opts,const char * args)157 int OptImport (Opt *opts, const char *args)
158 {
159     strc_t par;
160     char *argst;
161     const char *argstt;
162     UDWORD flag = 0;
163     int i, ret = 0;
164 
165     argst = strdup (args);
166     argstt = argst;
167 
168     while ((par = s_parse (&argstt)))
169     {
170         for (i = 0; OptList[i].name; i++)
171             if (!strcmp (par->txt, OptList[i].name))
172             {
173                 flag = OptList[i].flag;
174                 break;
175             }
176 
177         if (!OptList[i].name)
178         {
179             ret = 1;
180             break;
181         }
182 
183         if (!(par = s_parse (&argstt)))
184         {
185             ret = 1;
186             break;
187         }
188 
189         if (flag & COF_COLOR)
190         {
191             char *color = strdup (par->txt);
192             OptSetStr (opts, flag, OptC2S (color));
193             OptUndef  (opts, CO_CSCHEME);
194             free (color);
195         }
196         else if (flag == CO_ENCODINGSTR)
197         {
198             UWORD enc = ConvEnc (par->txt) & ~ENC_FLAGS;
199             OptSetVal (opts, CO_ENCODING, enc);
200             OptSetStr (opts, CO_ENCODINGSTR, ConvEncName (enc));
201         }
202         else if (flag & COF_NUMERIC)
203         {
204             val_t val = atoll (par->txt);
205 
206             if (flag == CO_CSCHEME)
207                 OptImport (opts, PrefSetColorScheme (val));
208 
209             OptSetVal (opts, flag, val);
210         }
211         else if (~flag & COF_BOOL)
212             OptSetStr (opts, flag, par->txt);
213         else if (!strcasecmp (par->txt, "on")  || !strcasecmp (par->txt, i18n (1085, "on")))
214             OptSetVal (opts, flag, 1);
215         else if (!strcasecmp (par->txt, "off") || !strcasecmp (par->txt, i18n (1086, "off")))
216             OptSetVal (opts, flag, 0);
217         else if (!strcasecmp (par->txt, "undef"))
218             OptUndef (opts, flag);
219         else
220         {
221             ret = 1;
222             break;
223         }
224     }
225     free (argst);
226     return ret;
227 }
228 
229 /*
230  * Export options into a string.
231  */
OptString(const Opt * opts)232 const char *OptString (const Opt *opts)
233 {
234     static str_s str;
235     int i, flag;
236     val_t val = 0;
237 
238     s_init (&str, "", 100);
239 
240     for (i = 0; OptList[i].name; i++)
241         if (OptGetVal (opts, flag = OptList[i].flag, &val))
242         {
243             if (flag & COF_BOOL)
244             {
245                 if (!*str.txt)
246                     s_cat (&str, "options");
247                 s_catf (&str, " %s %s", OptList[i].name, val ? "on" : "off");
248             }
249             else
250             {
251                 if (*str.txt)
252                     s_catc (&str, '\n');
253                 if (flag & COF_NUMERIC)
254                     s_catf (&str, "options %s %lu", OptList[i].name, UD2UL (val));
255                 else if (flag & COF_COLOR)
256                     s_catf (&str, "options %s %s", OptList[i].name, s_quote (OptS2C (strtable[val])));
257                 else
258                     s_catf (&str, "options %s %s", OptList[i].name, s_quote (strtable[val]));
259             }
260         }
261     if (*str.txt)
262         s_catc (&str, '\n');
263 
264     return str.txt;
265 }
266 
267 /*
268  * Get a (string) contact option.
269  */
270 #undef OptGetStr
OptGetStr(const Opt * opt,UDWORD flag,const char ** res DEBUGPARAM)271 BOOL OptGetStr (const Opt *opt, UDWORD flag, const char **res DEBUGPARAM)
272 {
273     val_t val = 0;
274 
275     assert (flag & (COF_STRING | COF_COLOR));
276 
277     if (!OptGetVal (opt, flag, &val))
278         return FALSE;
279 
280     if (val >= strmax)
281         return FALSE;
282 
283     *res = strtable[val];
284     if (~flag & 0x80)
285         Debug (DEB_OPTS, "(%p,%lx) = %s", opt, UD2UL (flag), s_quote (*res));
286     return TRUE;
287 }
288 
289 /*
290  * Set several contact options at once
291  */
OptSetVals(Opt * opt,UDWORD flag,...)292 Opt *OptSetVals (Opt *opt, UDWORD flag, ...)
293 {
294     const char *text;
295     va_list args;
296     val_t val;
297 
298     va_start (args, flag);
299     if (!opt)
300         opt = OptC ();
301     while (flag)
302     {
303         if (flag & COF_STRING)
304         {
305             text = va_arg (args, const char *);
306             OptSetStr (opt, flag, text);
307         }
308         else
309         {
310             val = va_arg (args, val_t);
311             OptSetVal (opt, flag, val);
312         }
313         flag = va_arg (args, UDWORD);
314     }
315     va_end (args);
316     return opt;
317 }
318 
319 /*
320  * Set a (string) contact option.
321  */
322 #undef OptSetStr
OptSetStr(Opt * opt,UDWORD flag,const char * text DEBUGPARAM)323 BOOL OptSetStr (Opt *opt, UDWORD flag, const char *text DEBUGPARAM)
324 {
325     val_t val;
326 
327     assert (flag & (COF_STRING | COF_COLOR));
328 
329     if ((val = OptUndef (opt, flag)))
330     {
331         free (strtable[val]);
332         strtable[val] = NULL;
333         if (val < strmin)
334             strmin = val;
335     }
336     if (!text)
337         return TRUE;
338 
339     val = strmin;
340     while (val < strmax && strtable[val])
341        val++;
342     strmin = val + 1;
343 
344     if (val == strmax)
345     {
346         int j, news = (strmax ? strmax * 2 : 128);
347         char **new = realloc (strtable, sizeof (char *) * news);
348         if (!new)
349             return FALSE;
350         for (j = strmax; j < news; j++)
351             new[j] = NULL;
352         strtable = new;
353         strmax = news;
354     }
355     if (!(strtable[val] = strdup (text)))
356         return FALSE;
357 
358     Debug (DEB_OPTS, "(%p,%lx) := %ld / %s", opt, UD2UL (flag), UD2UL (val), s_quote (strtable [val]));
359 
360     return OptSetVal (opt, flag, val);
361 }
362 
363 /*
364  * Get a contact option.
365  */
366 #undef OptGetVal
OptGetVal(const Opt * opt,UDWORD flag,val_t * res DEBUGPARAM)367 BOOL OptGetVal (const Opt *opt, UDWORD flag, val_t *res DEBUGPARAM)
368 {
369     const Opt *cot;
370     UBYTE tag = flag & 0xff;
371     int k = 0;
372 
373     for (cot = opt; cot; cot = cot->next)
374     {
375         for (k = 0; k < OPT_TABLESIZE; k++)
376             if (cot->tags[k] == tag)
377                 break;
378         if (k != OPT_TABLESIZE)
379             break;
380     }
381     if (!cot || ((flag & COF_BOOL) && (~cot->vals[k] & (flag & CO_BOOLMASK))))
382     {
383         Debug (DEB_OPTS, "(%p,%lx) undef", opt, UD2UL (flag));
384         return FALSE;
385     }
386     *res = (flag & COF_BOOL) ? (cot->vals[k] & (flag * 2) & CO_BOOLMASK) != 0 : cot->vals[k];
387     if (~tag & 0x80)
388         Debug (DEB_OPTS, "(%p,%lx) = %lx = %lu", opt, UD2UL (flag), UD2UL (*res), UD2UL (*res));
389     return TRUE;
390 }
391 
392 /*
393  * Set a contact option.
394  */
395 #undef OptSetVal
OptSetVal(Opt * opt,UDWORD flag,val_t val DEBUGPARAM)396 BOOL OptSetVal (Opt *opt, UDWORD flag, val_t val DEBUGPARAM)
397 {
398     Opt *cot, *cotold;
399     int k = 0;
400     UBYTE tag = flag & 0xff;
401 
402     cotold = NULL;
403     for (cot = opt; cot; cot = cot->next)
404     {
405         cotold = cot;
406         for (k = 0; k < OPT_TABLESIZE; k++)
407             if ((cot->tags[k] == tag) || !cot->tags[k])
408                 break;
409         if (k != OPT_TABLESIZE)
410             break;
411     }
412     if (!cot)
413     {
414         if (!(cot = calloc (sizeof (Opt), 1)))
415         {
416             Debug (DEB_OPTS, "(%p,%lx) != %lx = %lu <mem %p>", opt, UD2UL (flag), UD2UL (val), UD2UL (val), cot);
417             return FALSE;
418         }
419 
420         Debug (DEB_OPTS, "(%p,%lx) := %lx = %lu <new %p>", opt, UD2UL (flag), UD2UL (val), UD2UL (val), cot);
421         cotold->next = cot;
422         k = 0;
423     }
424     else
425         Debug (DEB_OPTS, "(%p,%lx) := %lx = %lu <%p>", opt, UD2UL (flag), UD2UL (val), UD2UL (val), cot);
426 
427     cot->tags[k] = tag;
428     if (flag & COF_BOOL)
429     {
430         cot->vals[k] |= flag & CO_BOOLMASK;
431         if (val)
432             cot->vals[k] |= (flag & CO_BOOLMASK) * 2;
433         else
434             cot->vals[k] &= ~((flag & CO_BOOLMASK) * 2);
435     }
436     else
437         cot->vals[k] = val;
438     return TRUE;
439 }
440 
441 /*
442  * Undefine a contact option.
443  */
444 #undef OptUndef
OptUndef(Opt * opt,UDWORD flag DEBUGPARAM)445 val_t OptUndef (Opt *opt, UDWORD flag DEBUGPARAM)
446 {
447     Opt *cot, *cotold;
448     UBYTE tag = flag & 0xff;
449     val_t old = 0;
450     int k = 0, m;
451 
452 
453     for (cot = opt; cot; cot = cot->next)
454     {
455         for (k = 0; k < OPT_TABLESIZE; k++)
456             if (cot->tags[k] == tag)
457                 break;
458         if (k != OPT_TABLESIZE)
459             break;
460     }
461     if (!cot)
462     {
463         Debug (DEB_OPTS, "(%p,%lx) := undef <unset>", opt, UD2UL (flag));
464         return 0;
465     }
466     if (flag & COF_BOOL)
467     {
468         cot->vals[k] &= ~(flag & CO_BOOLMASK);
469         cot->vals[k] &= ~((flag & CO_BOOLMASK) * 2);
470         Debug (DEB_OPTS, "(%p,%lx) := undef <bit>", opt, UD2UL (flag));
471         if (cot->vals[k])
472             return 0;
473     }
474     for (cotold = cot; cotold->next; )
475         cotold = cotold->next;
476     for (m = OPT_TABLESIZE - 1; m > 0; m--)
477         if (cotold->tags[m])
478             break;
479     old = cot->vals[k];
480     cot->tags[k] = cotold->tags[m];
481     cot->vals[k] = cotold->vals[m];
482     cotold->tags[m] = 0;
483     cotold->vals[m] = 0;
484     Debug (DEB_OPTS, "(%p,%lx) := undef <%ld>", opt, UD2UL (flag), UD2UL (old));
485 
486     return old;
487 }
488 
489 /*
490  * Convert a string describing a color into an escape sequence.
491  */
OptC2S(const char * color)492 const char *OptC2S (const char *color)
493 {
494     static str_s str;
495     strc_t par;
496     const char *cmd, *c;
497 
498     s_init (&str, "", 10);
499 
500     while ((par = s_parse (&color)))
501     {
502         cmd = par->txt;
503         if      (!strcasecmp (cmd, "black"))   c = BLACK;
504         else if (!strcasecmp (cmd, "red"))     c = RED;
505         else if (!strcasecmp (cmd, "green"))   c = GREEN;
506         else if (!strcasecmp (cmd, "yellow"))  c = YELLOW;
507         else if (!strcasecmp (cmd, "blue"))    c = BLUE;
508         else if (!strcasecmp (cmd, "magenta")) c = MAGENTA;
509         else if (!strcasecmp (cmd, "cyan"))    c = CYAN;
510         else if (!strcasecmp (cmd, "white"))   c = WHITE;
511         else if (!strcasecmp (cmd, "none"))    c = SGR0;
512         else if (!strcasecmp (cmd, "bold"))    c = BOLD;
513         else c = cmd;
514 
515         s_cat (&str, c);
516     }
517     return str.txt;
518 }
519 
520 /*
521  * Convert an escape sequence into a description of the color it selects
522  */
OptS2C(const char * text)523 const char *OptS2C (const char *text)
524 {
525     static str_s str;
526     const char *c;
527     int l;
528 
529     s_init (&str, "", 20);
530 
531     for ( ; *text; text += l)
532     {
533         if      (!strncmp (BLACK,   text, l = strlen (BLACK)))   c = "black";
534         else if (!strncmp (RED,     text, l = strlen (RED)))     c = "red";
535         else if (!strncmp (BLUE,    text, l = strlen (BLUE)))    c = "blue";
536         else if (!strncmp (GREEN,   text, l = strlen (GREEN)))   c = "green";
537         else if (!strncmp (YELLOW,  text, l = strlen (YELLOW)))  c = "yellow";
538         else if (!strncmp (MAGENTA, text, l = strlen (MAGENTA))) c = "magenta";
539         else if (!strncmp (CYAN,    text, l = strlen (CYAN)))    c = "cyan";
540         else if (!strncmp (WHITE,   text, l = strlen (WHITE)))   c = "white";
541         else if (!strncmp (SGR0,    text, l = strlen (SGR0)))    c = "none";
542         else if (!strncmp (BOLD,    text, l = strlen (BOLD)))    c = "bold";
543         else (c = text), (l = strlen (text));
544 
545         if (*str.txt)
546             s_catc (&str, ' ');
547         s_cat (&str, s_quote (c));
548     }
549     return str.txt;
550 }
551 
552