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