Wildcard matching is tricky - you have to try wildcarding both the
[obnox/wireshark/wip.git] / prefs.c
1 /* prefs.c
2  * Routines for handling preferences
3  *
4  * $Id: prefs.c,v 1.43 2000/10/15 08:46:18 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33
34 #ifdef HAVE_DIRECT_H
35 #include <direct.h>
36 #endif
37
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <errno.h>
42
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #ifdef HAVE_SYS_STAT_H
48 #include <sys/stat.h>
49 #endif
50
51 #include <epan.h>
52 #include "globals.h"
53 #include "packet.h"
54 #include "file.h"
55 #include "prefs.h"
56 #include "column.h"
57 #include "print.h"
58 #include "util.h"
59
60 #include "prefs-int.h"
61
62 /* Internal functions */
63 static int    set_pref(gchar*, gchar*);
64 static GList *get_string_list(gchar *);
65 static void   clear_string_list(GList *);
66 static void   free_col_info(e_prefs *);
67
68 #define PF_NAME "preferences"
69
70 #define GPF_PATH        DATAFILE_DIR "/ethereal.conf"
71
72 static gboolean init_prefs = TRUE;
73 static gchar *pf_path = NULL;
74
75 e_prefs prefs;
76
77 gchar   *gui_ptree_line_style_text[] =
78         { "NONE", "SOLID", "DOTTED", "TABBED", NULL };
79
80 gchar   *gui_ptree_expander_style_text[] =
81         { "NONE", "SQUARE", "TRIANGLE", "CIRCULAR", NULL };
82
83 gchar   *gui_hex_dump_highlight_style_text[] =
84         { "BOLD", "INVERSE", NULL };
85
86 /*
87  * List of modules with preference settings.
88  */
89 static GList *modules;
90
91 /*
92  * Register a module that will have preferences.
93  * Specify the name used for the module in the preferences file, the
94  * title used in the tab for it in a preferences dialog box, and a
95  * routine to call back when we apply the preferences.
96  */
97 module_t *
98 prefs_register_module(const char *name, const char *title,
99     void (*apply_cb)(void))
100 {
101         module_t *module;
102
103         module = g_malloc(sizeof (module_t));
104         module->name = name;
105         module->title = title;
106         module->apply_cb = apply_cb;
107         module->prefs = NULL;   /* no preferences, to start */
108         module->numprefs = 0;
109         module->prefs_changed = FALSE;
110
111         modules = g_list_append(modules, module);
112
113         return module;
114 }
115
116 /*
117  * Find a module, given its name.
118  */
119 static gint
120 module_match(gconstpointer a, gconstpointer b)
121 {
122         const module_t *module = a;
123         const char *name = b;
124
125         return strcmp(name, module->name);
126 }
127
128 static module_t *
129 find_module(char *name)
130 {
131         GList *list_entry;
132
133         list_entry = g_list_find_custom(modules, name, module_match);
134         if (list_entry == NULL)
135                 return NULL;    /* no such module */
136         return (module_t *) list_entry->data;
137 }
138
139 typedef struct {
140         module_cb callback;
141         gpointer user_data;
142 } module_cb_arg_t;
143
144 static void
145 do_module_callback(gpointer data, gpointer user_data)
146 {
147         module_t *module = data;
148         module_cb_arg_t *arg = user_data;
149
150         (*arg->callback)(module, arg->user_data);
151 }
152
153 /*
154  * Call a callback function, with a specified argument, for each module.
155  */
156 void
157 prefs_module_foreach(module_cb callback, gpointer user_data)
158 {
159         module_cb_arg_t arg;
160
161         arg.callback = callback;
162         arg.user_data = user_data;
163         g_list_foreach(modules, do_module_callback, &arg);
164 }
165
166 static void
167 call_apply_cb(gpointer data, gpointer user_data)
168 {
169         module_t *module = data;
170
171         if (module->prefs_changed) {
172                 if (module->apply_cb != NULL)
173                         (*module->apply_cb)();
174                 module->prefs_changed = FALSE;
175         }
176 }
177
178 /*
179  * Call the "apply" callback function for each module if any of its
180  * preferences have changed, and then clear the flag saying its
181  * preferences have changed, as the module has been notified of that
182  * fact.
183  */
184 void
185 prefs_apply_all(void)
186 {
187         g_list_foreach(modules, call_apply_cb, NULL);
188 }
189
190 /*
191  * Register a preference in a module's list of preferences.
192  */
193 static pref_t *
194 register_preference(module_t *module, const char *name, const char *title,
195     const char *description)
196 {
197         pref_t *preference;
198
199         preference = g_malloc(sizeof (pref_t));
200         preference->name = name;
201         preference->title = title;
202         preference->description = description;
203         preference->ordinal = module->numprefs;
204
205         module->prefs = g_list_append(module->prefs, preference);
206         module->numprefs++;
207
208         return preference;
209 }
210
211 /*
212  * Find a preference in a module's list of preferences, given the module
213  * and the preference's name.
214  */
215 static gint
216 preference_match(gconstpointer a, gconstpointer b)
217 {
218         const pref_t *pref = a;
219         const char *name = b;
220
221         return strcmp(name, pref->name);
222 }
223
224 static struct preference *
225 find_preference(module_t *module, char *name)
226 {
227         GList *list_entry;
228
229         list_entry = g_list_find_custom(module->prefs, name, preference_match);
230         if (list_entry == NULL)
231                 return NULL;    /* no such preference */
232         return (struct preference *) list_entry->data;
233 }
234
235 /*
236  * Returns TRUE if the given protocol has registered preferences
237  */
238 gboolean
239 prefs_is_registered_protocol(char *name)
240 {
241         return (find_module(name) != NULL);
242 }
243
244 /*
245  * Returns the module title of a registered protocol
246  */
247 const char *
248 prefs_get_title_by_name(char *name)
249 {
250         module_t *m = find_module(name);
251         return  (m) ? m->title : NULL;
252 }
253
254 /*
255  * Register a preference with an unsigned integral value.
256  */
257 void
258 prefs_register_uint_preference(module_t *module, const char *name,
259     const char *title, const char *description, guint base, guint *var)
260 {
261         pref_t *preference;
262
263         preference = register_preference(module, name, title, description);
264         preference->type = PREF_UINT;
265         preference->varp.uint = var;
266         preference->info.base = base;
267 }
268
269 /*
270  * Register a preference with an Boolean value.
271  */
272 void
273 prefs_register_bool_preference(module_t *module, const char *name,
274     const char *title, const char *description, gboolean *var)
275 {
276         pref_t *preference;
277
278         preference = register_preference(module, name, title, description);
279         preference->type = PREF_BOOL;
280         preference->varp.bool = var;
281 }
282
283 /*
284  * Register a preference with an enumerated value.
285  */
286 void
287 prefs_register_enum_preference(module_t *module, const char *name,
288     const char *title, const char *description, gint *var,
289     const enum_val *enumvals, gboolean radio_buttons)
290 {
291         pref_t *preference;
292
293         preference = register_preference(module, name, title, description);
294         preference->type = PREF_ENUM;
295         preference->varp.enump = var;
296         preference->info.enum_info.enumvals = enumvals;
297         preference->info.enum_info.radio_buttons = radio_buttons;
298 }
299
300 /*
301  * Register a preference with a character-string value.
302  */
303 void
304 prefs_register_string_preference(module_t *module, const char *name,
305     const char *title, const char *description, char **var)
306 {
307         pref_t *preference;
308
309         preference = register_preference(module, name, title, description);
310         preference->type = PREF_STRING;
311         preference->varp.string = var;
312         preference->saved_val.string = NULL;
313 }
314
315 typedef struct {
316         pref_cb callback;
317         gpointer user_data;
318 } pref_cb_arg_t;
319
320 static void
321 do_pref_callback(gpointer data, gpointer user_data)
322 {
323         pref_t *pref = data;
324         pref_cb_arg_t *arg = user_data;
325
326         (*arg->callback)(pref, arg->user_data);
327 }
328
329 /*
330  * Call a callback function, with a specified argument, for each preference
331  * in a given module.
332  */
333 void
334 prefs_pref_foreach(module_t *module, pref_cb callback, gpointer user_data)
335 {
336         pref_cb_arg_t arg;
337
338         arg.callback = callback;
339         arg.user_data = user_data;
340         g_list_foreach(module->prefs, do_pref_callback, &arg);
341 }
342
343 /*
344  * Register all non-dissector modules' preferences.
345  */
346 void
347 prefs_register_modules(void)
348 {
349 }
350
351 /* Parse through a list of comma-separated, quoted strings.  Return a
352    list of the string data */
353 static GList *
354 get_string_list(gchar *str) {
355   enum { PRE_QUOT, IN_QUOT, POST_QUOT };
356
357   gint      state = PRE_QUOT, i = 0, j = 0;
358   gboolean  backslash = FALSE;
359   gchar     cur_c, *slstr = NULL;
360   GList    *sl = NULL;
361   
362   while ((cur_c = str[i]) != '\0') {
363     if (cur_c == '"' && ! backslash) {
364       switch (state) {
365         case PRE_QUOT:
366           state = IN_QUOT;
367           slstr = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
368           j = 0;
369           break;
370         case IN_QUOT:
371           state  = POST_QUOT;
372           slstr[j] = '\0';
373           sl = g_list_append(sl, slstr);
374           break;
375         case POST_QUOT:
376           clear_string_list(sl);
377           return NULL;
378           break;
379         default:
380           break;
381       }
382     } else if (cur_c == '\\' && ! backslash) {
383       backslash = TRUE;
384     } else if (cur_c == ',' && state == POST_QUOT) {
385       state = PRE_QUOT;
386     } else if (state == IN_QUOT && j < COL_MAX_LEN) {
387       slstr[j] = str[i];
388       j++;
389     }
390     i++;
391   }
392   if (state != POST_QUOT) {
393     clear_string_list(sl);
394   }
395   return(sl);
396 }
397
398 void
399 clear_string_list(GList *sl) {
400   GList *l = sl;
401   
402   while (l) {
403     g_free(l->data);
404     l = g_list_remove_link(l, l);
405   }
406 }
407
408 /*
409  * Takes a string, a pointer to an array of "enum_val"s, and a default gint
410  * value.
411  * The array must be terminated by an entry with a null "name" string.
412  * If the string matches a "name" strings in an entry, the value from that
413  * entry is returned. Otherwise, the default value that was passed as the
414  * third argument is returned.
415  */
416 gint
417 find_val_for_string(const char *needle, const enum_val *haystack,
418     gint default_value)
419 {
420         int i = 0;
421
422         while (haystack[i].name != NULL) {
423                 if (strcasecmp(needle, haystack[i].name) == 0) {
424                         return haystack[i].value;
425                 }
426                 i++;    
427         }
428         return default_value;
429 }
430
431 /* Takes an string and a pointer to an array of strings, and a default int value.
432  * The array must be terminated by a NULL string. If the string is found in the array
433  * of strings, the index of that string in the array is returned. Otherwise, the
434  * default value that was passed as the third argument is returned.
435  */
436 static int
437 find_index_from_string_array(char *needle, char **haystack, int default_value)
438 {
439         int i = 0;
440
441         while (haystack[i] != NULL) {
442                 if (strcmp(needle, haystack[i]) == 0) {
443                         return i;
444                 }
445                 i++;    
446         }
447         return default_value;
448 }
449
450 /* Preferences file format:
451  * - Configuration directives start at the beginning of the line, and 
452  *   are terminated with a colon.
453  * - Directives can be continued on the next line by preceding them with
454  *   whitespace.
455  *
456  * Example:
457
458 # This is a comment line
459 print.command: lpr
460 print.file: /a/very/long/path/
461         to/ethereal-out.ps
462  *
463  */
464
465 #define MAX_VAR_LEN    48
466 #define MAX_VAL_LEN  1024
467
468 #define DEF_NUM_COLS    6
469
470 static void read_prefs_file(const char *pf_path, FILE *pf);
471
472 /* Read the preferences file, fill in "prefs", and return a pointer to it.
473
474    If we got an error (other than "it doesn't exist") trying to read
475    the global preferences file, stuff the errno into "*gpf_errno_return"
476    and a pointer to the path of the file into "*gpf_path_return", and
477    return NULL.
478
479    If we got an error (other than "it doesn't exist") trying to read
480    the user's preferences file, stuff the errno into "*pf_errno_return"
481    and a pointer to the path of the file into "*pf_path_return", and
482    return NULL. */
483 e_prefs *
484 read_prefs(int *gpf_errno_return, char **gpf_path_return,
485            int *pf_errno_return, char **pf_path_return)
486 {
487   int       i;
488   FILE     *pf;
489   fmt_data *cfmt;
490   gchar    *col_fmt[] = {"No.",      "%m", "Time",        "%t",
491                          "Source",   "%s", "Destination", "%d",
492                          "Protocol", "%p", "Info",        "%i"};
493
494   
495   if (init_prefs) {
496     /* Initialize preferences to wired-in default values.
497        They may be overridded by the global preferences file or the
498        user's preferences file. */
499     init_prefs       = FALSE;
500     prefs.pr_format  = PR_FMT_TEXT;
501     prefs.pr_dest    = PR_DEST_CMD;
502     prefs.pr_file    = g_strdup("ethereal.out");
503     prefs.pr_cmd     = g_strdup("lpr");
504     prefs.col_list = NULL;
505     for (i = 0; i < DEF_NUM_COLS; i++) {
506       cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
507       cfmt->title = g_strdup(col_fmt[i * 2]);
508       cfmt->fmt   = g_strdup(col_fmt[(i * 2) + 1]);
509       prefs.col_list = g_list_append(prefs.col_list, cfmt);
510     }
511     prefs.num_cols  = DEF_NUM_COLS;
512     prefs.st_client_fg.pixel =     0;
513     prefs.st_client_fg.red   = 32767;
514     prefs.st_client_fg.green =     0;
515     prefs.st_client_fg.blue  =     0;
516     prefs.st_client_bg.pixel = 65535;
517     prefs.st_client_bg.red   = 65535;
518     prefs.st_client_bg.green = 65535;
519     prefs.st_client_bg.blue  = 65535;
520     prefs.st_server_fg.pixel =     0;
521     prefs.st_server_fg.red   =     0;
522     prefs.st_server_fg.green =     0;
523     prefs.st_server_fg.blue  = 32767;
524     prefs.st_server_bg.pixel = 65535;
525     prefs.st_server_bg.red   = 65535;
526     prefs.st_server_bg.green = 65535;
527     prefs.st_server_bg.blue  = 65535;
528     prefs.gui_scrollbar_on_right = TRUE;
529     prefs.gui_plist_sel_browse = FALSE;
530     prefs.gui_ptree_sel_browse = FALSE;
531     prefs.gui_ptree_line_style = 0;
532     prefs.gui_ptree_expander_style = 1;
533     prefs.gui_hex_dump_highlight_style = 1;
534 #ifdef WIN32
535     prefs.gui_font_name = g_strdup("-*-lucida console-medium-r-*-*-*-100-*-*-*-*-*-*");
536 #else
537     /*
538      * XXX - for now, we make the initial font name a pattern that matches
539      * only ISO 8859/1 fonts, so that we don't match 2-byte fonts such
540      * as ISO 10646 fonts.
541      *
542      * Users in locales using other one-byte fonts will have to choose
543      * a different font from the preferences dialog - or put the font
544      * selection in the global preferences file to make that font the
545      * default for all users who don't explicitly specify a different
546      * font.
547      *
548      * Making this a font set rather than a font has two problems:
549      *
550      *  1) as far as I know, you can't select font sets with the
551      *     font selection dialog;
552      *
553      *  2) if you use a font set, the text to be drawn must be a
554      *     multi-byte string in the appropriate locale, but
555      *     Ethereal does *NOT* guarantee that's the case - in
556      *     the hex-dump window, each character in the text portion
557      *     of the display must be a *single* byte, and in the
558      *     packet-list and protocol-tree windows, text extracted
559      *     from the packet is not necessarily in the right format.
560      *
561      * "Doing this right" may, for the packet-list and protocol-tree
562      * windows, require that dissectors know what the locale is
563      * *AND* know what locale and text representation is used in
564      * the packets they're dissecting, and may be impossible in
565      * the hex-dump window (except by punting and displaying only
566      * ASCII characters).
567      *
568      * GTK+ 2.0 may simplify part of the problem, as it will, as I
569      * understand it, use UTF-8-encoded Unicode as its internal
570      * character set; however, we'd still have to know whatever
571      * character set and encoding is used in the packet (which
572      * may differ for different protocols, e.g. SMB might use
573      * PC code pages for some strings and Unicode for others, whilst
574      * NFS might use some UNIX character set encoding, e.g. ISO 8859/x,
575      * or one of the EUC character sets for Asian languages, or one
576      * of the other multi-byte character sets, or UTF-8, or...).
577      *
578      * I.e., as far as I can tell, "internationalizing" the packet-list,
579      * protocol-tree, and hex-dump windows involves a lot more than, say,
580      * just using font sets rather than fonts.
581      */
582     prefs.gui_font_name = g_strdup("-*-fixed-medium-r-semicondensed-*-*-120-*-*-*-*-iso8859-1");
583 #endif
584     prefs.gui_marked_fg.pixel = 65535;
585     prefs.gui_marked_fg.red   = 65535;
586     prefs.gui_marked_fg.green = 65535;
587     prefs.gui_marked_fg.blue  = 65535;
588     prefs.gui_marked_bg.pixel =     0;
589     prefs.gui_marked_bg.red   =     0;
590     prefs.gui_marked_bg.green =     0;
591     prefs.gui_marked_bg.blue  =     0;
592
593   }
594
595   /* Read the global preferences file, if it exists. */
596   *gpf_path_return = NULL;
597   if ((pf = fopen(GPF_PATH, "r")) != NULL) {
598     /* We succeeded in opening it; read it. */
599     read_prefs_file(GPF_PATH, pf);
600     fclose(pf);
601   } else {
602     /* We failed to open it.  If we failed for some reason other than
603        "it doesn't exist", return the errno and the pathname, so our
604        caller can report the error. */
605     if (errno != ENOENT) {
606       *gpf_errno_return = errno;
607       *gpf_path_return = GPF_PATH;
608     }
609   }
610
611   /* Construct the pathname of the user's preferences file. */
612   if (! pf_path) {
613     pf_path = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(PF_DIR) +
614       strlen(PF_NAME) + 4);
615     sprintf(pf_path, "%s/%s/%s", get_home_dir(), PF_DIR, PF_NAME);
616   }
617     
618   /* Read the user's preferences file, if it exists. */
619   *pf_path_return = NULL;
620   if ((pf = fopen(pf_path, "r")) != NULL) {
621     /* We succeeded in opening it; read it. */
622     read_prefs_file(pf_path, pf);
623     fclose(pf);
624   } else {
625     /* We failed to open it.  If we failed for some reason other than
626        "it doesn't exist", return the errno and the pathname, so our
627        caller can report the error. */
628     if (errno != ENOENT) {
629       *pf_errno_return = errno;
630       *pf_path_return = pf_path;
631     }
632   }
633   
634   return &prefs;
635 }
636
637 static void
638 read_prefs_file(const char *pf_path, FILE *pf)
639 {
640   enum { START, IN_VAR, PRE_VAL, IN_VAL, IN_SKIP };
641   gchar     cur_var[MAX_VAR_LEN], cur_val[MAX_VAL_LEN];
642   int       got_c, state = START;
643   gboolean  got_val = FALSE;
644   gint      var_len = 0, val_len = 0, fline = 1, pline = 1;
645
646   while ((got_c = getc(pf)) != EOF) {
647     if (got_c == '\n') {
648       state = START;
649       fline++;
650       continue;
651     }
652     if (var_len >= MAX_VAR_LEN) {
653       g_warning ("%s line %d: Variable too long", pf_path, fline);
654       state = IN_SKIP;
655       var_len = 0;
656       continue;
657     }
658     if (val_len >= MAX_VAL_LEN) {
659       g_warning ("%s line %d: Value too long", pf_path, fline);
660       state = IN_SKIP;
661       var_len = 0;
662       continue;
663     }
664     
665     switch (state) {
666       case START:
667         if (isalnum(got_c)) {
668           if (var_len > 0) {
669             if (got_val) {
670               cur_var[var_len] = '\0';
671               cur_val[val_len] = '\0';
672               switch (set_pref(cur_var, cur_val)) {
673
674               case PREFS_SET_SYNTAX_ERR:
675                 g_warning ("%s line %d: Syntax error", pf_path, pline);
676                 break;
677
678               case PREFS_SET_NO_SUCH_PREF:
679                 g_warning ("%s line %d: No such preference \"%s\"", pf_path,
680                                 pline, cur_var);
681                 break;
682               }
683             } else {
684               g_warning ("%s line %d: Incomplete preference", pf_path, pline);
685             }
686           }
687           state      = IN_VAR;
688           got_val    = FALSE;
689           cur_var[0] = got_c;
690           var_len    = 1;
691           pline = fline;
692         } else if (isspace(got_c) && var_len > 0 && got_val) {
693           state = PRE_VAL;
694         } else if (got_c == '#') {
695           state = IN_SKIP;
696         } else {
697           g_warning ("%s line %d: Malformed line", pf_path, fline);
698         }
699         break;
700       case IN_VAR:
701         if (got_c != ':') {
702           cur_var[var_len] = got_c;
703           var_len++;
704         } else {
705           state   = PRE_VAL;
706           val_len = 0;
707           got_val = TRUE;
708         }
709         break;
710       case PRE_VAL:
711         if (!isspace(got_c)) {
712           state = IN_VAL;
713           cur_val[val_len] = got_c;
714           val_len++;
715         }
716         break;
717       case IN_VAL:
718         if (got_c != '#')  {
719           cur_val[val_len] = got_c;
720           val_len++;
721         } else {
722           while (isspace((guchar)cur_val[val_len]) && val_len > 0)
723             val_len--;
724           state = IN_SKIP;
725         }
726         break;
727     }
728   }
729   if (var_len > 0) {
730     if (got_val) {
731       cur_var[var_len] = '\0';
732       cur_val[val_len] = '\0';
733       switch (set_pref(cur_var, cur_val)) {
734
735       case PREFS_SET_SYNTAX_ERR:
736         g_warning ("%s line %d: Syntax error", pf_path, pline);
737         break;
738
739       case PREFS_SET_NO_SUCH_PREF:
740         g_warning ("%s line %d: No such preference \"%s\"", pf_path,
741                         pline, cur_var);
742         break;
743       }
744     } else {
745       g_warning ("%s line %d: Incomplete preference", pf_path, pline);
746     }
747   }
748 }
749
750 /*
751  * Given a string of the form "<pref name>:<pref value>", as might appear
752  * as an argument to a "-o" option, parse it and set the preference in
753  * question.  Return an indication of whether it succeeded or failed
754  * in some fashion.
755  */
756 int
757 prefs_set_pref(char *prefarg)
758 {
759         u_char *p, *colonp;
760         int ret;
761
762         colonp = strchr(prefarg, ':');
763         if (colonp == NULL)
764                 return PREFS_SET_SYNTAX_ERR;
765
766         p = colonp;
767         *p++ = '\0';
768
769         /*
770          * Skip over any white space (there probably won't be any, but
771          * as we allow it in the preferences file, we might as well
772          * allow it here).
773          */
774         while (isspace(*p))
775                 p++;
776         if (*p == '\0') {
777                 /*
778                  * Put the colon back, so if our caller uses, in an
779                  * error message, the string they passed us, the message
780                  * looks correct.
781                  */
782                 *colonp = ':';
783                 return PREFS_SET_SYNTAX_ERR;
784         }
785
786         ret = set_pref(prefarg, p);
787         *colonp = ':';  /* put the colon back */
788         return ret;
789 }
790
791 #define PRS_PRINT_FMT    "print.format"
792 #define PRS_PRINT_DEST   "print.destination"
793 #define PRS_PRINT_FILE   "print.file"
794 #define PRS_PRINT_CMD    "print.command"
795 #define PRS_COL_FMT      "column.format"
796 #define PRS_STREAM_CL_FG "stream.client.fg"
797 #define PRS_STREAM_CL_BG "stream.client.bg"
798 #define PRS_STREAM_SR_FG "stream.server.fg"
799 #define PRS_STREAM_SR_BG "stream.server.bg"
800 #define PRS_GUI_SCROLLBAR_ON_RIGHT "gui.scrollbar_on_right"
801 #define PRS_GUI_PLIST_SEL_BROWSE "gui.packet_list_sel_browse"
802 #define PRS_GUI_PTREE_SEL_BROWSE "gui.protocol_tree_sel_browse"
803 #define PRS_GUI_PTREE_LINE_STYLE "gui.protocol_tree_line_style"
804 #define PRS_GUI_PTREE_EXPANDER_STYLE "gui.protocol_tree_expander_style"
805 #define PRS_GUI_HEX_DUMP_HIGHLIGHT_STYLE "gui.hex_dump_highlight_style"
806 #define PRS_GUI_FONT_NAME "gui.font_name"
807 #define PRS_GUI_MARKED_FG "gui.marked_frame.fg"
808 #define PRS_GUI_MARKED_BG "gui.marked_frame.bg"
809
810 #define RED_COMPONENT(x)   ((((x) >> 16) & 0xff) * 65535 / 255)
811 #define GREEN_COMPONENT(x) ((((x) >>  8) & 0xff) * 65535 / 255)
812 #define BLUE_COMPONENT(x)   (((x)        & 0xff) * 65535 / 255)
813
814 static gchar *pr_formats[] = { "text", "postscript" };
815 static gchar *pr_dests[]   = { "command", "file" };
816
817 static int
818 set_pref(gchar *pref_name, gchar *value)
819 {
820   GList    *col_l;
821   gint      llen;
822   fmt_data *cfmt;
823   unsigned long int cval;
824   guint    uval;
825   gboolean bval;
826   gint     enum_val;
827   char     *p;
828   gchar    *dotp;
829   module_t *module;
830   pref_t   *pref;
831
832   if (strcmp(pref_name, PRS_PRINT_FMT) == 0) {
833     if (strcmp(value, pr_formats[PR_FMT_TEXT]) == 0) {
834       prefs.pr_format = PR_FMT_TEXT;
835     } else if (strcmp(value, pr_formats[PR_FMT_PS]) == 0) {
836       prefs.pr_format = PR_FMT_PS;
837     } else {
838       return PREFS_SET_SYNTAX_ERR;
839     }
840   } else if (strcmp(pref_name, PRS_PRINT_DEST) == 0) {
841     if (strcmp(value, pr_dests[PR_DEST_CMD]) == 0) {
842       prefs.pr_dest = PR_DEST_CMD;
843     } else if (strcmp(value, pr_dests[PR_DEST_FILE]) == 0) {
844       prefs.pr_dest = PR_DEST_FILE;
845     } else {
846       return PREFS_SET_SYNTAX_ERR;
847     }
848   } else if (strcmp(pref_name, PRS_PRINT_FILE) == 0) {
849     if (prefs.pr_file) g_free(prefs.pr_file);
850     prefs.pr_file = g_strdup(value);
851   } else if (strcmp(pref_name, PRS_PRINT_CMD) == 0) {
852     if (prefs.pr_cmd) g_free(prefs.pr_cmd);
853     prefs.pr_cmd = g_strdup(value);
854   } else if (strcmp(pref_name, PRS_COL_FMT) == 0) {
855     if ((col_l = get_string_list(value)) && (g_list_length(col_l) % 2) == 0) {
856       free_col_info(&prefs);
857       prefs.col_list = NULL;
858       llen             = g_list_length(col_l);
859       prefs.num_cols   = llen / 2;
860       col_l = g_list_first(col_l);
861       while(col_l) {
862         cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
863         cfmt->title    = g_strdup(col_l->data);
864         col_l          = col_l->next;
865         cfmt->fmt      = g_strdup(col_l->data);
866         col_l          = col_l->next;
867         prefs.col_list = g_list_append(prefs.col_list, cfmt);
868       }
869       /* To do: else print some sort of error? */
870     }
871     clear_string_list(col_l);
872   } else if (strcmp(pref_name, PRS_STREAM_CL_FG) == 0) {
873     cval = strtoul(value, NULL, 16);
874     prefs.st_client_fg.pixel = 0;
875     prefs.st_client_fg.red   = RED_COMPONENT(cval);
876     prefs.st_client_fg.green = GREEN_COMPONENT(cval);
877     prefs.st_client_fg.blue  = BLUE_COMPONENT(cval);
878   } else if (strcmp(pref_name, PRS_STREAM_CL_BG) == 0) {
879     cval = strtoul(value, NULL, 16);
880     prefs.st_client_bg.pixel = 0;
881     prefs.st_client_bg.red   = RED_COMPONENT(cval);
882     prefs.st_client_bg.green = GREEN_COMPONENT(cval);
883     prefs.st_client_bg.blue  = BLUE_COMPONENT(cval);
884   } else if (strcmp(pref_name, PRS_STREAM_SR_FG) == 0) {
885     cval = strtoul(value, NULL, 16);
886     prefs.st_server_fg.pixel = 0;
887     prefs.st_server_fg.red   = RED_COMPONENT(cval);
888     prefs.st_server_fg.green = GREEN_COMPONENT(cval);
889     prefs.st_server_fg.blue  = BLUE_COMPONENT(cval);
890   } else if (strcmp(pref_name, PRS_STREAM_SR_BG) == 0) {
891     cval = strtoul(value, NULL, 16);
892     prefs.st_server_bg.pixel = 0;
893     prefs.st_server_bg.red   = RED_COMPONENT(cval);
894     prefs.st_server_bg.green = GREEN_COMPONENT(cval);
895     prefs.st_server_bg.blue  = BLUE_COMPONENT(cval);
896   } else if (strcmp(pref_name, PRS_GUI_SCROLLBAR_ON_RIGHT) == 0) {
897     if (strcmp(value, "TRUE") == 0) {
898             prefs.gui_scrollbar_on_right = TRUE;
899     }
900     else {
901             prefs.gui_scrollbar_on_right = FALSE;
902     }
903   } else if (strcmp(pref_name, PRS_GUI_PLIST_SEL_BROWSE) == 0) {
904     if (strcmp(value, "TRUE") == 0) {
905             prefs.gui_plist_sel_browse = TRUE;
906     }
907     else {
908             prefs.gui_plist_sel_browse = FALSE;
909     }
910   } else if (strcmp(pref_name, PRS_GUI_PTREE_SEL_BROWSE) == 0) {
911     if (strcmp(value, "TRUE") == 0) {
912             prefs.gui_ptree_sel_browse = TRUE;
913     }
914     else {
915             prefs.gui_ptree_sel_browse = FALSE;
916     }
917   } else if (strcmp(pref_name, PRS_GUI_PTREE_LINE_STYLE) == 0) {
918           prefs.gui_ptree_line_style =
919                   find_index_from_string_array(value, gui_ptree_line_style_text, 0);
920   } else if (strcmp(pref_name, PRS_GUI_PTREE_EXPANDER_STYLE) == 0) {
921           prefs.gui_ptree_expander_style =
922                   find_index_from_string_array(value, gui_ptree_expander_style_text, 1);
923   } else if (strcmp(pref_name, PRS_GUI_HEX_DUMP_HIGHLIGHT_STYLE) == 0) {
924           prefs.gui_hex_dump_highlight_style =
925                   find_index_from_string_array(value, gui_hex_dump_highlight_style_text, 1);
926   } else if (strcmp(pref_name, PRS_GUI_FONT_NAME) == 0) {
927           if (prefs.gui_font_name != NULL)
928                 g_free(prefs.gui_font_name);
929           prefs.gui_font_name = g_strdup(value);
930   } else if (strcmp(pref_name, PRS_GUI_MARKED_FG) == 0) {
931     cval = strtoul(value, NULL, 16);
932     prefs.gui_marked_fg.pixel = 0;
933     prefs.gui_marked_fg.red   = RED_COMPONENT(cval);
934     prefs.gui_marked_fg.green = GREEN_COMPONENT(cval);
935     prefs.gui_marked_fg.blue  = BLUE_COMPONENT(cval);
936   } else if (strcmp(pref_name, PRS_GUI_MARKED_BG) == 0) {
937     cval = strtoul(value, NULL, 16);
938     prefs.gui_marked_bg.pixel = 0;
939     prefs.gui_marked_bg.red   = RED_COMPONENT(cval);
940     prefs.gui_marked_bg.green = GREEN_COMPONENT(cval);
941     prefs.gui_marked_bg.blue  = BLUE_COMPONENT(cval);
942   } else {
943     /* To which module does this preference belong? */
944     dotp = strchr(pref_name, '.');
945     if (dotp == NULL)
946       return PREFS_SET_SYNTAX_ERR;      /* no ".", so no module/name separator */
947     *dotp = '\0';               /* separate module and preference name */
948     module = find_module(pref_name);
949     *dotp = '.';                /* put the preference string back */
950     if (module == NULL)
951       return PREFS_SET_NO_SUCH_PREF;    /* no such module */
952     dotp++;                     /* skip past separator to preference name */
953     pref = find_preference(module, dotp);
954     if (pref == NULL)
955       return PREFS_SET_NO_SUCH_PREF;    /* no such preference */
956
957     switch (pref->type) {
958
959     case PREF_UINT:
960       uval = strtoul(value, &p, pref->info.base);
961       if (p == value || *p != '\0')
962         return PREFS_SET_SYNTAX_ERR;    /* number was bad */
963       if (*pref->varp.uint != uval) {
964         module->prefs_changed = TRUE;
965         *pref->varp.uint = uval;
966       }
967       break;
968
969     case PREF_BOOL:
970       /* XXX - give an error if it's neither "true" nor "false"? */
971       if (strcasecmp(value, "true") == 0)
972         bval = TRUE;
973       else
974         bval = FALSE;
975       if (*pref->varp.bool != bval) {
976         module->prefs_changed = TRUE;
977         *pref->varp.bool = bval;
978       }
979       break;
980
981     case PREF_ENUM:
982       /* XXX - give an error if it doesn't match? */
983       enum_val = find_val_for_string(value,
984                                         pref->info.enum_info.enumvals, 1);
985       if (*pref->varp.enump != enum_val) {
986         module->prefs_changed = TRUE;
987         *pref->varp.enump = enum_val;
988       }
989       break;
990
991     case PREF_STRING:
992       if (*pref->varp.string == NULL || strcmp(*pref->varp.string, value) != 0) {
993         module->prefs_changed = TRUE;
994         if (*pref->varp.string != NULL)
995           g_free(*pref->varp.string);
996         *pref->varp.string = g_strdup(value);
997       }
998       break;
999     }
1000   }
1001   
1002   return PREFS_SET_OK;
1003 }
1004
1005 typedef struct {
1006         module_t *module;
1007         FILE    *pf;
1008 } write_pref_arg_t;
1009
1010 /*
1011  * Write out a single preference.
1012  */
1013 static void
1014 write_pref(gpointer data, gpointer user_data)
1015 {
1016         pref_t *pref = data;
1017         write_pref_arg_t *arg = user_data;
1018         const enum_val *enum_valp;
1019         const char *val_string;
1020
1021         fprintf(arg->pf, "\n# %s\n", pref->description);
1022
1023         switch (pref->type) {
1024
1025         case PREF_UINT:
1026                 switch (pref->info.base) {
1027
1028                 case 10:
1029                         fprintf(arg->pf, "# A decimal number.\n");
1030                         fprintf(arg->pf, "%s.%s: %u\n", arg->module->name,
1031                             pref->name, *pref->varp.uint);
1032                         break;
1033
1034                 case 8:
1035                         fprintf(arg->pf, "# An octal number.\n");
1036                         fprintf(arg->pf, "%s.%s: %#o\n", arg->module->name,
1037                             pref->name, *pref->varp.uint);
1038                         break;
1039
1040                 case 16:
1041                         fprintf(arg->pf, "# A hexadecimal number.\n");
1042                         fprintf(arg->pf, "%s.%s: %#x\n", arg->module->name,
1043                             pref->name, *pref->varp.uint);
1044                         break;
1045                 }
1046                 break;
1047
1048         case PREF_BOOL:
1049                 fprintf(arg->pf, "# TRUE or FALSE (case-insensitive).\n");
1050                 fprintf(arg->pf, "%s.%s: %s\n", arg->module->name, pref->name,
1051                     *pref->varp.bool ? "TRUE" : "FALSE");
1052                 break;
1053
1054         case PREF_ENUM:
1055                 fprintf(arg->pf, "# One of: ");
1056                 enum_valp = pref->info.enum_info.enumvals;
1057                 val_string = NULL;
1058                 while (enum_valp->name != NULL) {
1059                         if (enum_valp->value == *pref->varp.enump)
1060                                 val_string = enum_valp->name;
1061                         fprintf(arg->pf, "%s", enum_valp->name);
1062                         enum_valp++;
1063                         if (enum_valp->name == NULL)
1064                                 fprintf(arg->pf, "\n");
1065                         else
1066                                 fprintf(arg->pf, ", ");
1067                 }
1068                 fprintf(arg->pf, "# (case-insensitive).\n");
1069                 fprintf(arg->pf, "%s.%s: %s\n", arg->module->name, pref->name,
1070                     val_string);
1071                 break;
1072
1073         case PREF_STRING:
1074                 fprintf(arg->pf, "# A string.\n");
1075                 fprintf(arg->pf, "%s.%s: %s\n", arg->module->name, pref->name,
1076                     *pref->varp.string);
1077                 break;
1078         }
1079 }
1080
1081 static void
1082 write_module_prefs(gpointer data, gpointer user_data)
1083 {
1084         write_pref_arg_t arg;
1085
1086         arg.module = data;
1087         arg.pf = user_data;
1088         g_list_foreach(arg.module->prefs, write_pref, &arg);
1089 }
1090
1091 /* Write out "prefs" to the user's preferences file, and return 0.
1092
1093    If we got an error, stuff a pointer to the path of the preferences file
1094    into "*pf_path_return", and return the errno. */
1095 int
1096 write_prefs(char **pf_path_return)
1097 {
1098   FILE        *pf;
1099   struct stat  s_buf;
1100   
1101   /* To do:
1102    * - Split output lines longer than MAX_VAL_LEN
1103    * - Create a function for the preference directory check/creation
1104    *   so that duplication can be avoided with filter.c
1105    */
1106
1107   if (! pf_path) {
1108     pf_path = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(PF_DIR) +
1109       strlen(PF_NAME) + 4);
1110   }
1111
1112   sprintf(pf_path, "%s/%s", get_home_dir(), PF_DIR);
1113   if (stat(pf_path, &s_buf) != 0)
1114 #ifdef WIN32
1115     mkdir(pf_path);
1116 #else
1117     mkdir(pf_path, 0755);
1118 #endif
1119
1120   sprintf(pf_path, "%s/%s/%s", get_home_dir(), PF_DIR, PF_NAME);
1121   if ((pf = fopen(pf_path, "w")) == NULL) {
1122     *pf_path_return = pf_path;
1123     return errno;
1124   }
1125     
1126   fputs("# Configuration file for Ethereal " VERSION ".\n"
1127     "#\n"
1128     "# This file is regenerated each time preferences are saved within\n"
1129     "# Ethereal.  Making manual changes should be safe, however.\n"
1130     "\n"
1131     "######## Printing ########\n"
1132     "\n", pf);
1133
1134   fprintf (pf, "# Can be one of \"text\" or \"postscript\".\n"
1135     "print.format: %s\n\n", pr_formats[prefs.pr_format]);
1136
1137   fprintf (pf, "# Can be one of \"command\" or \"file\".\n"
1138     "print.destination: %s\n\n", pr_dests[prefs.pr_dest]);
1139
1140   fprintf (pf, "# This is the file that gets written to when the "
1141     "destination is set to \"file\"\n"
1142     "%s: %s\n\n", PRS_PRINT_FILE, prefs.pr_file);
1143
1144   fprintf (pf, "# Output gets piped to this command when the destination "
1145     "is set to \"command\"\n"
1146     "%s: %s\n\n", PRS_PRINT_CMD, prefs.pr_cmd);
1147
1148   fprintf (pf, "# Packet list column format.  Each pair of strings consists "
1149     "of a column title \n# and its format.\n"
1150     "%s: %s\n\n", PRS_COL_FMT, col_format_to_pref_str());
1151
1152   fprintf (pf, "# TCP stream window color preferences.  Each value is a six "
1153     "digit hexadecimal value in the form rrggbb.\n");
1154   fprintf (pf, "%s: %02x%02x%02x\n", PRS_STREAM_CL_FG,
1155     (prefs.st_client_fg.red * 255 / 65535),
1156     (prefs.st_client_fg.green * 255 / 65535),
1157     (prefs.st_client_fg.blue * 255 / 65535));
1158   fprintf (pf, "%s: %02x%02x%02x\n", PRS_STREAM_CL_BG,
1159     (prefs.st_client_bg.red * 255 / 65535),
1160     (prefs.st_client_bg.green * 255 / 65535),
1161     (prefs.st_client_bg.blue * 255 / 65535));
1162   fprintf (pf, "%s: %02x%02x%02x\n", PRS_STREAM_SR_FG,
1163     (prefs.st_server_fg.red * 255 / 65535),
1164     (prefs.st_server_fg.green * 255 / 65535),
1165     (prefs.st_server_fg.blue * 255 / 65535));
1166   fprintf (pf, "%s: %02x%02x%02x\n", PRS_STREAM_SR_BG,
1167     (prefs.st_server_bg.red * 255 / 65535),
1168     (prefs.st_server_bg.green * 255 / 65535),
1169     (prefs.st_server_bg.blue * 255 / 65535));
1170
1171   fprintf(pf, "\n# Vertical scrollbars should be on right side? TRUE/FALSE\n");
1172   fprintf(pf, PRS_GUI_SCROLLBAR_ON_RIGHT ": %s\n",
1173                   prefs.gui_scrollbar_on_right == TRUE ? "TRUE" : "FALSE");
1174
1175   fprintf(pf, "\n# Packet-list selection bar can be used to browse w/o selecting? TRUE/FALSE\n");
1176   fprintf(pf, PRS_GUI_PLIST_SEL_BROWSE ": %s\n",
1177                   prefs.gui_plist_sel_browse == TRUE ? "TRUE" : "FALSE");
1178
1179   fprintf(pf, "\n# Protocol-tree selection bar can be used to browse w/o selecting? TRUE/FALSE\n");
1180   fprintf(pf, PRS_GUI_PTREE_SEL_BROWSE ": %s\n",
1181                   prefs.gui_ptree_sel_browse == TRUE ? "TRUE" : "FALSE");
1182
1183   fprintf(pf, "\n# Protocol-tree line style. One of: NONE, SOLID, DOTTED, TABBED\n");
1184   fprintf(pf, PRS_GUI_PTREE_LINE_STYLE ": %s\n",
1185                   gui_ptree_line_style_text[prefs.gui_ptree_line_style]);
1186
1187   fprintf(pf, "\n# Protocol-tree expander style. One of: NONE, SQUARE, TRIANGLE, CIRCULAR\n");
1188   fprintf(pf, PRS_GUI_PTREE_EXPANDER_STYLE ": %s\n",
1189                   gui_ptree_expander_style_text[prefs.gui_ptree_expander_style]);
1190
1191   fprintf(pf, "\n# Hex dump highlight style. One of: BOLD, INVERSE\n");
1192   fprintf(pf, PRS_GUI_HEX_DUMP_HIGHLIGHT_STYLE ": %s\n",
1193                   gui_hex_dump_highlight_style_text[prefs.gui_hex_dump_highlight_style]);
1194
1195   fprintf(pf, "\n# Font name for packet list, protocol tree, and hex dump panes.\n");
1196   fprintf(pf, PRS_GUI_FONT_NAME ": %s\n", prefs.gui_font_name);
1197
1198   fprintf (pf, "\n# Color preferences for a marked frame.  Each value is a six "
1199     "digit hexadecimal value in the form rrggbb.\n");
1200   fprintf (pf, "%s: %02x%02x%02x\n", PRS_GUI_MARKED_FG,
1201     (prefs.gui_marked_fg.red * 255 / 65535),
1202     (prefs.gui_marked_fg.green * 255 / 65535),
1203     (prefs.gui_marked_fg.blue * 255 / 65535));
1204   fprintf (pf, "%s: %02x%02x%02x\n", PRS_GUI_MARKED_BG,
1205     (prefs.gui_marked_bg.red * 255 / 65535),
1206     (prefs.gui_marked_bg.green * 255 / 65535),
1207     (prefs.gui_marked_bg.blue * 255 / 65535));
1208
1209   g_list_foreach(modules, write_module_prefs, pf);
1210
1211   fclose(pf);
1212
1213   /* XXX - catch I/O errors (e.g. "ran out of disk space") and return
1214      an error indication, or maybe write to a new preferences file and
1215      rename that file on top of the old one only if there are not I/O
1216      errors. */
1217   return 0;
1218 }
1219
1220 /* Copy a set of preferences. */
1221 void
1222 copy_prefs(e_prefs *dest, e_prefs *src)
1223 {
1224   fmt_data *src_cfmt, *dest_cfmt;
1225   GList *entry;
1226
1227   dest->pr_format = src->pr_format;
1228   dest->pr_dest = src->pr_dest;
1229   dest->pr_file = g_strdup(src->pr_file);
1230   dest->pr_cmd = g_strdup(src->pr_cmd);
1231   dest->col_list = NULL;
1232   for (entry = src->col_list; entry != NULL; entry = g_list_next(entry)) {
1233     src_cfmt = entry->data;
1234     dest_cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
1235     dest_cfmt->title = g_strdup(src_cfmt->title);
1236     dest_cfmt->fmt = g_strdup(src_cfmt->fmt);
1237     dest->col_list = g_list_append(dest->col_list, dest_cfmt);
1238   }
1239   dest->num_cols = src->num_cols;
1240   dest->st_client_fg = src->st_client_fg;
1241   dest->st_client_bg = src->st_client_bg;
1242   dest->st_server_fg = src->st_server_fg;
1243   dest->st_server_bg = src->st_server_bg;
1244   dest->gui_scrollbar_on_right = src->gui_scrollbar_on_right;
1245   dest->gui_plist_sel_browse = src->gui_plist_sel_browse;
1246   dest->gui_ptree_sel_browse = src->gui_ptree_sel_browse;
1247   dest->gui_ptree_line_style = src->gui_ptree_line_style;
1248   dest->gui_ptree_expander_style = src->gui_ptree_expander_style;
1249   dest->gui_hex_dump_highlight_style = src->gui_hex_dump_highlight_style;
1250   dest->gui_font_name = g_strdup(src->gui_font_name);
1251   dest->gui_marked_fg = src->gui_marked_fg;
1252   dest->gui_marked_bg = src->gui_marked_bg;
1253 }
1254
1255 /* Free a set of preferences. */
1256 void
1257 free_prefs(e_prefs *pr)
1258 {
1259   if (pr->pr_file != NULL) {
1260     g_free(pr->pr_file);
1261     pr->pr_file = NULL;
1262   }
1263   if (pr->pr_cmd != NULL) {
1264     g_free(pr->pr_cmd);
1265     pr->pr_cmd = NULL;
1266   }
1267   free_col_info(pr);
1268   if (pr->gui_font_name != NULL) {
1269     g_free(pr->gui_font_name);
1270     pr->gui_font_name = NULL;
1271   }
1272 }
1273
1274 static void
1275 free_col_info(e_prefs *pr)
1276 {
1277   fmt_data *cfmt;
1278
1279   while (pr->col_list != NULL) {
1280     cfmt = pr->col_list->data;
1281     g_free(cfmt->title);
1282     g_free(cfmt->fmt);
1283     g_free(cfmt);
1284     pr->col_list = g_list_remove_link(pr->col_list, pr->col_list);
1285   }
1286   g_list_free(pr->col_list);
1287   pr->col_list = NULL;
1288 }