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