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