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