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