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