Save column alignment in the recent file.
[obnox/wireshark/wip.git] / gtk / recent.c
1 /* recent.c
2  * Recent "preference" handling routines
3  * Copyright 2004, Ulf Lamping <ulf.lamping@web.de>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
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 #include <stdio.h>
30 #include <string.h>
31 #include <ctype.h>
32
33 #include <gtk/gtk.h>
34
35 #include <epan/epan.h>
36 #include <epan/filesystem.h>
37 #include <epan/emem.h>
38 #include <epan/prefs.h>
39 #include <epan/prefs-int.h>
40 #include <epan/column.h>
41
42 #include "../simple_dialog.h"
43 #include <wsutil/file_util.h>
44
45 #include "gtk/recent.h"
46 #include "gtk/main.h"
47 #include "gtk/menus.h"
48 #include "gtk/gui_utils.h"
49 #ifdef NEW_PACKET_LIST
50 #include "gtk/new_packet_list.h"
51 #else
52 #include "gtk/main_packet_list.h"
53 #endif /*NEW_PACKET_LIST */
54 #include "gtk/file_dlg.h"
55 #include "gtk/cfilter_combo_utils.h"
56 #include "gtk/u3.h"
57
58 #ifdef HAVE_PCAP_REMOTE
59 #include "gtk/capture_dlg.h"
60 #endif
61
62 #define RECENT_KEY_MAIN_TOOLBAR_SHOW        "gui.toolbar_main_show"
63 #define RECENT_KEY_FILTER_TOOLBAR_SHOW      "gui.filter_toolbar_show"
64 #define RECENT_KEY_AIRPCAP_TOOLBAR_SHOW     "gui.airpcap_toolbar_show"
65 #define RECENT_KEY_DRIVER_CHECK_SHOW        "gui.airpcap_driver_check_show"
66 #define RECENT_KEY_PACKET_LIST_SHOW         "gui.packet_list_show"
67 #define RECENT_KEY_TREE_VIEW_SHOW           "gui.tree_view_show"
68 #define RECENT_KEY_BYTE_VIEW_SHOW           "gui.byte_view_show"
69 #define RECENT_KEY_STATUSBAR_SHOW           "gui.statusbar_show"
70 #define RECENT_KEY_PACKET_LIST_COLORIZE     "gui.packet_list_colorize"
71 #define RECENT_GUI_TIME_FORMAT              "gui.time_format"
72 #define RECENT_GUI_TIME_PRECISION           "gui.time_precision"
73 #define RECENT_GUI_ZOOM_LEVEL               "gui.zoom_level"
74 #define RECENT_GUI_BYTES_VIEW               "gui.bytes_view"
75 #define RECENT_GUI_GEOMETRY_MAIN_X          "gui.geometry_main_x"
76 #define RECENT_GUI_GEOMETRY_MAIN_Y          "gui.geometry_main_y"
77 #define RECENT_GUI_GEOMETRY_MAIN_WIDTH      "gui.geometry_main_width"
78 #define RECENT_GUI_GEOMETRY_MAIN_HEIGHT     "gui.geometry_main_height"
79 #define RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED  "gui.geometry_main_maximized"
80 #define RECENT_GUI_GEOMETRY_MAIN_UPPER_PANE "gui.geometry_main_upper_pane"
81 #define RECENT_GUI_GEOMETRY_MAIN_LOWER_PANE "gui.geometry_main_lower_pane"
82 #define RECENT_GUI_GEOMETRY_STATUS_PANE_LEFT  "gui.geometry_status_pane"
83 #define RECENT_GUI_GEOMETRY_STATUS_PANE_RIGHT "gui.geometry_status_pane_right"
84 #define RECENT_GUI_GEOMETRY_WLAN_STATS_PANE "gui.geometry_status_wlan_stats_pane"
85 #define RECENT_LAST_USED_PROFILE            "gui.last_used_profile"
86 #define RECENT_GUI_FILEOPEN_REMEMBERED_DIR  "gui.fileopen_remembered_dir"
87 #define RECENT_GUI_GEOMETRY                 "gui.geom."
88 #define RECENT_KEY_PRIVS_WARN_IF_ELEVATED   "privs.warn_if_elevated"
89 #define RECENT_KEY_PRIVS_WARN_IF_NO_NPF     "privs.warn_if_no_npf"
90
91 #define RECENT_FILE_NAME "recent"
92 #define RECENT_COMMON_FILE_NAME "recent_common"
93
94 recent_settings_t recent;
95
96 static const char *ts_type_text[] =
97   { "RELATIVE", "ABSOLUTE", "ABSOLUTE_WITH_DATE", "DELTA", "DELTA_DIS", "EPOCH", NULL };
98
99 static const char *ts_precision_text[] =
100         { "AUTO", "SEC", "DSEC", "CSEC", "MSEC", "USEC", "NSEC", NULL };
101
102 /* Takes an string and a pointer to an array of strings, and a default int value.
103  * The array must be terminated by a NULL string. If the string is found in the array
104  * of strings, the index of that string in the array is returned. Otherwise, the
105  * default value that was passed as the third argument is returned.
106  */
107 static int
108 find_index_from_string_array(const char *needle, const char **haystack, int default_value)
109 {
110         int i = 0;
111
112         while (haystack[i] != NULL) {
113                 if (strcmp(needle, haystack[i]) == 0) {
114                         return i;
115                 }
116                 i++;
117         }
118         return default_value;
119 }
120
121 static void
122 free_col_width_info(recent_settings_t *rs)
123 {
124   col_width_data *cfmt;
125
126   while (rs->col_width_list != NULL) {
127     cfmt = rs->col_width_list->data;
128     g_free(cfmt->cfield);
129     g_free(cfmt);
130     rs->col_width_list = g_list_remove_link(rs->col_width_list, rs->col_width_list);
131   }
132   g_list_free(rs->col_width_list);
133   rs->col_width_list = NULL;
134 }
135
136 /* Attempt to Write out "recent common" to the user's recent common file.
137    If we got an error report it with a dialog box and return FALSE,
138    otherwise return TRUE. */
139 gboolean
140 write_recent(void)
141 {
142   char        *pf_dir_path;
143   char        *rf_path;
144   FILE        *rf;
145
146   /* To do:
147    * - Split output lines longer than MAX_VAL_LEN
148    * - Create a function for the preference directory check/creation
149    *   so that duplication can be avoided with filter.c
150    */
151
152   /* Create the directory that holds personal configuration files, if
153      necessary.  */
154   if (create_persconffile_dir(&pf_dir_path) == -1) {
155      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
156       "Can't create directory\n\"%s\"\nfor recent file: %s.", pf_dir_path,
157       strerror(errno));
158      g_free(pf_dir_path);
159      return FALSE;
160   }
161
162   rf_path = get_persconffile_path(RECENT_COMMON_FILE_NAME, FALSE, TRUE);
163   if ((rf = ws_fopen(rf_path, "w")) == NULL) {
164      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
165       "Can't open recent file\n\"%s\": %s.", rf_path,
166       strerror(errno));
167     g_free(rf_path);
168     return FALSE;
169   }
170   g_free(rf_path);
171
172   fputs("# Recent settings file for Wireshark " VERSION ".\n"
173     "#\n"
174     "# This file is regenerated each time Wireshark is quit.\n"
175     "# So be careful, if you want to make manual changes here.\n"
176     "\n"
177     "######## Recent capture files (latest last), cannot be altered through command line ########\n"
178     "\n", rf);
179
180   menu_recent_file_write_all(rf);
181
182   fputs("\n"
183     "######## Recent capture filters (latest last), cannot be altered through command line ########\n"
184     "\n", rf);
185
186   cfilter_combo_recent_write_all(rf);
187
188   fputs("\n"
189     "######## Recent display filters (latest last), cannot be altered through command line ########\n"
190     "\n", rf);
191
192   dfilter_recent_combo_write_all(rf);
193
194 #ifdef HAVE_PCAP_REMOTE
195   fputs("\n"
196     "######## Recent remote hosts, cannot be altered through command line ########\n"
197     "\n", rf);
198         
199   capture_remote_combo_recent_write_all(rf);
200 #endif
201
202   fprintf(rf, "\n# Main window geometry.\n");
203   fprintf(rf, "# Decimal numbers.\n");
204   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_X ": %d\n", recent.gui_geometry_main_x);
205   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_Y ": %d\n", recent.gui_geometry_main_y);
206   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_WIDTH ": %d\n",
207                   recent.gui_geometry_main_width);
208   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_HEIGHT ": %d\n",
209                   recent.gui_geometry_main_height);
210
211   fprintf(rf, "\n# Main window maximized.\n");
212   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
213   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED ": %s\n",
214                   recent.gui_geometry_main_maximized == TRUE ? "TRUE" : "FALSE");
215
216   fprintf(rf, "\n# Statusbar left pane size.\n");
217   fprintf(rf, "# Decimal number.\n");
218   if (recent.gui_geometry_status_pane_left != 0) {
219     fprintf(rf, RECENT_GUI_GEOMETRY_STATUS_PANE_LEFT ": %d\n",
220                   recent.gui_geometry_status_pane_left);
221   }
222   fprintf(rf, "\n# Statusbar middle pane size.\n");
223   fprintf(rf, "# Decimal number.\n");
224   if (recent.gui_geometry_status_pane_right != 0) {
225     fprintf(rf, RECENT_GUI_GEOMETRY_STATUS_PANE_RIGHT ": %d\n",
226                   recent.gui_geometry_status_pane_right);
227   }
228
229   fprintf(rf, "\n# Last used Configuration Profile.\n");
230   fprintf(rf, RECENT_LAST_USED_PROFILE ": %s\n", get_profile_name());
231
232   fprintf(rf, "\n# WLAN statistics upper pane size.\n");
233   fprintf(rf, "# Decimal number.\n");
234   fprintf(rf, RECENT_GUI_GEOMETRY_WLAN_STATS_PANE ": %d\n",
235           recent.gui_geometry_wlan_stats_pane);
236
237   fprintf(rf, "\n# Warn if running with elevated permissions (e.g. as root).\n");
238   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
239   fprintf(rf, RECENT_KEY_PRIVS_WARN_IF_ELEVATED ": %s\n",
240                   recent.privs_warn_if_elevated == TRUE ? "TRUE" : "FALSE");
241
242   fprintf(rf, "\n# Warn if npf.sys isn't loaded on Windows >= 6.0.\n");
243   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
244   fprintf(rf, RECENT_KEY_PRIVS_WARN_IF_NO_NPF ": %s\n",
245                   recent.privs_warn_if_no_npf == TRUE ? "TRUE" : "FALSE");
246
247   window_geom_recent_write_all(rf);
248
249   fclose(rf);
250
251   /* XXX - catch I/O errors (e.g. "ran out of disk space") and return
252      an error indication, or maybe write to a new recent file and
253      rename that file on top of the old one only if there are not I/O
254      errors. */
255   return TRUE;
256 }
257
258
259 /* Attempt to Write out profile "recent" to the user's profile recent file.
260    If we got an error report it with a dialog box and return FALSE,
261    otherwise return TRUE. */
262 gboolean
263 write_profile_recent(void)
264 {
265   char        *pf_dir_path;
266   char        *rf_path;
267   FILE        *rf;
268
269   /* To do:
270    * - Split output lines longer than MAX_VAL_LEN
271    * - Create a function for the preference directory check/creation
272    *   so that duplication can be avoided with filter.c
273    */
274
275   /* Create the directory that holds personal configuration files, if
276      necessary.  */
277   if (create_persconffile_dir(&pf_dir_path) == -1) {
278      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
279       "Can't create directory\n\"%s\"\nfor recent file: %s.", pf_dir_path,
280       strerror(errno));
281      g_free(pf_dir_path);
282      return FALSE;
283   }
284
285   rf_path = get_persconffile_path(RECENT_FILE_NAME, TRUE, TRUE);
286   if ((rf = ws_fopen(rf_path, "w")) == NULL) {
287      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
288       "Can't open recent file\n\"%s\": %s.", rf_path,
289       strerror(errno));
290     g_free(rf_path);
291     return FALSE;
292   }
293   g_free(rf_path);
294
295   fputs("# Recent settings file for Wireshark " VERSION ".\n"
296     "#\n"
297     "# This file is regenerated each time Wireshark is quit\n"
298     "# and when changing configuration profile.\n"
299     "# So be careful, if you want to make manual changes here.\n"
300     "\n", rf);
301
302   fprintf(rf, "\n# Main Toolbar show (hide).\n");
303   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
304   fprintf(rf, RECENT_KEY_MAIN_TOOLBAR_SHOW ": %s\n",
305                   recent.main_toolbar_show == TRUE ? "TRUE" : "FALSE");
306
307   fprintf(rf, "\n# Filter Toolbar show (hide).\n");
308   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
309   fprintf(rf, RECENT_KEY_FILTER_TOOLBAR_SHOW ": %s\n",
310                   recent.filter_toolbar_show == TRUE ? "TRUE" : "FALSE");
311
312 #ifdef HAVE_AIRPCAP
313   fprintf(rf, "\n# Wireless Settings Toolbar show (hide).\n");
314   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
315   fprintf(rf, RECENT_KEY_AIRPCAP_TOOLBAR_SHOW ": %s\n",
316                   recent.airpcap_toolbar_show == TRUE ? "TRUE" : "FALSE");
317 #endif
318
319 #ifdef HAVE_AIRPCAP
320   fprintf(rf, "\n# Show (hide) old AirPcap driver warning dialog box.\n");
321   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
322   fprintf(rf, RECENT_KEY_DRIVER_CHECK_SHOW ": %s\n",
323                   recent.airpcap_driver_check_show == TRUE ? "TRUE" : "FALSE");
324 #endif
325
326   fprintf(rf, "\n# Packet list show (hide).\n");
327   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
328   fprintf(rf, RECENT_KEY_PACKET_LIST_SHOW ": %s\n",
329                   recent.packet_list_show == TRUE ? "TRUE" : "FALSE");
330
331   fprintf(rf, "\n# Tree view show (hide).\n");
332   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
333   fprintf(rf, RECENT_KEY_TREE_VIEW_SHOW ": %s\n",
334                   recent.tree_view_show == TRUE ? "TRUE" : "FALSE");
335
336   fprintf(rf, "\n# Byte view show (hide).\n");
337   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
338   fprintf(rf, RECENT_KEY_BYTE_VIEW_SHOW ": %s\n",
339                   recent.byte_view_show == TRUE ? "TRUE" : "FALSE");
340
341   fprintf(rf, "\n# Statusbar show (hide).\n");
342   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
343   fprintf(rf, RECENT_KEY_STATUSBAR_SHOW ": %s\n",
344                   recent.statusbar_show == TRUE ? "TRUE" : "FALSE");
345
346   fprintf(rf, "\n# Packet list colorize (hide).\n");
347   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
348   fprintf(rf, RECENT_KEY_PACKET_LIST_COLORIZE ": %s\n",
349                   recent.packet_list_colorize == TRUE ? "TRUE" : "FALSE");
350
351   fprintf(rf, "\n# Timestamp display format.\n");
352   fprintf(rf, "# One of: RELATIVE, ABSOLUTE, ABSOLUTE_WITH_DATE, DELTA, DELTA_DIS, EPOCH\n");
353   fprintf(rf, RECENT_GUI_TIME_FORMAT ": %s\n",
354           ts_type_text[recent.gui_time_format]);
355
356   fprintf(rf, "\n# Timestamp display precision.\n");
357   fprintf(rf, "# One of: AUTO, SEC, DSEC, CSEC, MSEC, USEC, NSEC\n");
358   fprintf(rf, RECENT_GUI_TIME_PRECISION ": %s\n",
359           ts_precision_text[recent.gui_time_precision]);
360
361   fprintf(rf, "\n# Zoom level.\n");
362   fprintf(rf, "# A decimal number.\n");
363   fprintf(rf, RECENT_GUI_ZOOM_LEVEL ": %d\n",
364                   recent.gui_zoom_level);
365
366   fprintf(rf, "\n# Bytes view.\n");
367   fprintf(rf, "# A decimal number.\n");
368   fprintf(rf, RECENT_GUI_BYTES_VIEW ": %d\n",
369                   recent.gui_bytes_view);
370
371   fprintf(rf, "\n# Main window upper (or leftmost) pane size.\n");
372   fprintf(rf, "# Decimal number.\n");
373   if (recent.gui_geometry_main_upper_pane != 0) {
374     fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_UPPER_PANE ": %d\n",
375                   recent.gui_geometry_main_upper_pane);
376   }
377   fprintf(rf, "\n# Main window middle pane size.\n");
378   fprintf(rf, "# Decimal number.\n");
379   if (recent.gui_geometry_main_lower_pane != 0) {
380     fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_LOWER_PANE ": %d\n",
381                   recent.gui_geometry_main_lower_pane);
382   }
383
384   fprintf(rf, "\n# Packet list column pixel widths.\n");
385   fprintf(rf, "# Each pair of strings consists of a column format and its pixel width.\n");
386 #ifdef NEW_PACKET_LIST
387   new_packet_list_recent_write_all(rf);
388 #else
389   packet_list_recent_write_all(rf);
390 #endif
391
392   if (get_last_open_dir() != NULL) {
393     fprintf(rf, "\n# Last directory navigated to in File Open dialog.\n");
394
395     if(u3_active())
396       fprintf(rf, RECENT_GUI_FILEOPEN_REMEMBERED_DIR ": %s\n", u3_contract_device_path(get_last_open_dir()));
397     else
398       fprintf(rf, RECENT_GUI_FILEOPEN_REMEMBERED_DIR ": %s\n", get_last_open_dir());
399   }
400
401   fclose(rf);
402
403   /* XXX - catch I/O errors (e.g. "ran out of disk space") and return
404      an error indication, or maybe write to a new recent file and
405      rename that file on top of the old one only if there are not I/O
406      errors. */
407   return TRUE;
408 }
409
410
411 /* write the geometry values of a window to recent file */
412 void
413 write_recent_geom(gpointer key _U_, gpointer value, gpointer rf)
414 {
415     window_geometry_t *geom = value;
416
417     fprintf(rf, "\n# Geometry and maximized state of %s window.\n", geom->key);
418     fprintf(rf, "# Decimal integers.\n");
419     fprintf(rf, RECENT_GUI_GEOMETRY "%s.x: %d\n", geom->key, geom->x);
420     fprintf(rf, RECENT_GUI_GEOMETRY "%s.y: %d\n", geom->key, geom->y);
421     fprintf(rf, RECENT_GUI_GEOMETRY "%s.width: %d\n", geom->key,
422               geom->width);
423     fprintf(rf, RECENT_GUI_GEOMETRY "%s.height: %d\n", geom->key,
424               geom->height);
425
426     fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
427     fprintf(rf, RECENT_GUI_GEOMETRY "%s.maximized: %s\n", geom->key,
428               geom->maximized == TRUE ? "TRUE" : "FALSE");
429
430 }
431
432 /* set one user's recent common file key/value pair */
433 static prefs_set_pref_e
434 read_set_recent_common_pair_static(gchar *key, gchar *value, void *private_data _U_)
435 {
436   long num;
437   char *p;
438
439   if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED) == 0) {
440     if (g_ascii_strcasecmp(value, "true") == 0) {
441         recent.gui_geometry_main_maximized = TRUE;
442     }
443     else {
444         recent.gui_geometry_main_maximized = FALSE;
445     }
446
447   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_X) == 0) {
448     num = strtol(value, &p, 0);
449     if (p == value || *p != '\0')
450       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
451     recent.gui_geometry_main_x = num;
452   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_Y) == 0) {
453     num = strtol(value, &p, 0);
454     if (p == value || *p != '\0')
455       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
456     recent.gui_geometry_main_y = num;
457   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_WIDTH) == 0) {
458     num = strtol(value, &p, 0);
459     if (p == value || *p != '\0')
460       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
461     if (num <= 0)
462       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
463     recent.gui_geometry_main_width = num;
464   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_HEIGHT) == 0) {
465     num = strtol(value, &p, 0);
466     if (p == value || *p != '\0')
467       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
468     if (num <= 0)
469       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
470     recent.gui_geometry_main_height = num;
471   } else if (strcmp(key, RECENT_GUI_GEOMETRY_STATUS_PANE_RIGHT) == 0) {
472     num = strtol(value, &p, 0);
473     if (p == value || *p != '\0')
474       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
475     if (num <= 0)
476       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
477     recent.gui_geometry_status_pane_right = num;
478     recent.has_gui_geometry_status_pane = TRUE;
479   } else if (strcmp(key, RECENT_GUI_GEOMETRY_STATUS_PANE_LEFT) == 0) {
480     num = strtol(value, &p, 0);
481     if (p == value || *p != '\0')
482       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
483     if (num <= 0)
484       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
485     recent.gui_geometry_status_pane_left = num;
486     recent.has_gui_geometry_status_pane = TRUE;
487   } else if (strcmp(key, RECENT_LAST_USED_PROFILE) == 0) {
488     if ((strcmp(value, DEFAULT_PROFILE) != 0) && profile_exists (value)) {
489       set_profile_name (value);
490     }
491   } else if (strcmp(key, RECENT_GUI_GEOMETRY_WLAN_STATS_PANE) == 0) {
492     num = strtol(value, &p, 0);
493     if (p == value || *p != '\0')
494       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
495     if (num <= 0)
496       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
497     recent.gui_geometry_wlan_stats_pane = num;
498   } else if (strncmp(key, RECENT_GUI_GEOMETRY, sizeof(RECENT_GUI_GEOMETRY)-1) == 0) {
499     /* now have something like "gui.geom.main.x", split it into win and sub_key */
500     char *win = &key[sizeof(RECENT_GUI_GEOMETRY)-1];
501     char *sub_key = strchr(win, '.');
502     if(sub_key) {
503       *sub_key = '\0';
504       sub_key++;
505       window_geom_recent_read_pair(win, sub_key, value);
506     }
507   } else if (strcmp(key, RECENT_KEY_PRIVS_WARN_IF_ELEVATED) == 0) {
508     if (g_ascii_strcasecmp(value, "true") == 0) {
509         recent.privs_warn_if_elevated = TRUE;
510     }
511     else {
512         recent.privs_warn_if_elevated = FALSE;
513     }
514   } else if (strcmp(key, RECENT_KEY_PRIVS_WARN_IF_NO_NPF) == 0) {
515     if (g_ascii_strcasecmp(value, "true") == 0) {
516         recent.privs_warn_if_no_npf = TRUE;
517     }
518     else {
519         recent.privs_warn_if_no_npf = FALSE;
520     }
521   }
522
523   return PREFS_SET_OK;
524 }
525
526 /* set one user's recent file key/value pair */
527 static prefs_set_pref_e
528 read_set_recent_pair_static(gchar *key, gchar *value, void *private_data _U_)
529 {
530   long num;
531   char *p;
532   GList *col_l, *col_l_elt;
533   col_width_data *cfmt;
534   const gchar *cust_format = col_format_to_string(COL_CUSTOM);
535   int cust_format_len = (int) strlen(cust_format);
536
537   if (strcmp(key, RECENT_KEY_MAIN_TOOLBAR_SHOW) == 0) {
538     if (g_ascii_strcasecmp(value, "true") == 0) {
539         recent.main_toolbar_show = TRUE;
540     }
541     else {
542         recent.main_toolbar_show = FALSE;
543     }
544   } else if (strcmp(key, RECENT_KEY_FILTER_TOOLBAR_SHOW) == 0) {
545     if (g_ascii_strcasecmp(value, "true") == 0) {
546         recent.filter_toolbar_show = TRUE;
547     }
548     else {
549         recent.filter_toolbar_show = FALSE;
550     }
551   } else if (strcmp(key, RECENT_KEY_AIRPCAP_TOOLBAR_SHOW) == 0) {
552     if (g_ascii_strcasecmp(value, "true") == 0) {
553         recent.airpcap_toolbar_show = TRUE;
554     }
555     else {
556         recent.airpcap_toolbar_show = FALSE;
557     }
558   } else if (strcmp(key, RECENT_KEY_DRIVER_CHECK_SHOW) == 0) {
559     if (g_ascii_strcasecmp(value, "true") == 0) {
560         recent.airpcap_driver_check_show = TRUE;
561     }
562     else {
563         recent.airpcap_driver_check_show = FALSE;
564     }
565   } else if (strcmp(key, RECENT_KEY_PACKET_LIST_SHOW) == 0) {
566     if (g_ascii_strcasecmp(value, "true") == 0) {
567         recent.packet_list_show = TRUE;
568     }
569     else {
570         recent.packet_list_show = FALSE;
571     }
572   } else if (strcmp(key, RECENT_KEY_TREE_VIEW_SHOW) == 0) {
573     if (g_ascii_strcasecmp(value, "true") == 0) {
574         recent.tree_view_show = TRUE;
575     }
576     else {
577         recent.tree_view_show = FALSE;
578     }
579   } else if (strcmp(key, RECENT_KEY_BYTE_VIEW_SHOW) == 0) {
580     if (g_ascii_strcasecmp(value, "true") == 0) {
581         recent.byte_view_show = TRUE;
582     }
583     else {
584         recent.byte_view_show = FALSE;
585     }
586   } else if (strcmp(key, RECENT_KEY_STATUSBAR_SHOW) == 0) {
587     if (g_ascii_strcasecmp(value, "true") == 0) {
588         recent.statusbar_show = TRUE;
589     }
590     else {
591         recent.statusbar_show = FALSE;
592     }
593   } else if (strcmp(key, RECENT_KEY_PACKET_LIST_COLORIZE) == 0) {
594     if (g_ascii_strcasecmp(value, "true") == 0) {
595         recent.packet_list_colorize = TRUE;
596     }
597     else {
598         recent.packet_list_colorize = FALSE;
599     }
600   } else if (strcmp(key, RECENT_GUI_TIME_FORMAT) == 0) {
601     recent.gui_time_format =
602         find_index_from_string_array(value, ts_type_text, TS_RELATIVE);
603   } else if (strcmp(key, RECENT_GUI_TIME_PRECISION) == 0) {
604     recent.gui_time_precision =
605         find_index_from_string_array(value, ts_precision_text, TS_PREC_AUTO);
606   } else if (strcmp(key, RECENT_GUI_ZOOM_LEVEL) == 0) {
607     num = strtol(value, &p, 0);
608     if (p == value || *p != '\0')
609       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
610     recent.gui_zoom_level = num;
611   } else if (strcmp(key, RECENT_GUI_BYTES_VIEW) == 0) {
612     num = strtol(value, &p, 0);
613     if (p == value || *p != '\0')
614       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
615     recent.gui_bytes_view = num;
616   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED) == 0) {
617     if (g_ascii_strcasecmp(value, "true") == 0) {
618         recent.gui_geometry_main_maximized = TRUE;
619     }
620     else {
621         recent.gui_geometry_main_maximized = FALSE;
622     }
623
624   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_UPPER_PANE) == 0) {
625     num = strtol(value, &p, 0);
626     if (p == value || *p != '\0')
627       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
628     if (num <= 0)
629       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
630     recent.gui_geometry_main_upper_pane = num;
631     recent.has_gui_geometry_main_upper_pane = TRUE;
632   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_LOWER_PANE) == 0) {
633     num = strtol(value, &p, 0);
634     if (p == value || *p != '\0')
635       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
636     if (num <= 0)
637       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
638     recent.gui_geometry_main_lower_pane = num;
639     recent.has_gui_geometry_main_lower_pane = TRUE;
640   }
641   else if (strcmp(key, RECENT_KEY_COL_WIDTH) == 0) {
642     col_l = prefs_get_string_list(value);
643     if (col_l == NULL)
644       return PREFS_SET_SYNTAX_ERR;
645     if ((g_list_length(col_l) % 2) != 0) {
646       /* A title didn't have a matching width.  */
647       prefs_clear_string_list(col_l);
648       return PREFS_SET_SYNTAX_ERR;
649     }
650     /* Check to make sure all column formats are valid.  */
651     col_l_elt = g_list_first(col_l);
652     while(col_l_elt) {
653       /* Make sure the format isn't empty.  */
654       if (strcmp(col_l_elt->data, "") == 0) {
655         /* It is.  */
656         prefs_clear_string_list(col_l);
657         return PREFS_SET_SYNTAX_ERR;
658       }
659
660       /* Check the format.  */
661       if (strncmp(col_l_elt->data, cust_format, cust_format_len) != 0) {
662         if (get_column_format_from_str(col_l_elt->data) == -1) {
663           /* It's not a valid column format.  */
664           prefs_clear_string_list(col_l);
665           return PREFS_SET_SYNTAX_ERR;
666         }
667       }
668
669       /* Go past the format.  */
670       col_l_elt = col_l_elt->next;
671
672       /* Go past the width.  */
673       col_l_elt = col_l_elt->next;
674     }
675     free_col_width_info(&recent);
676     recent.col_width_list = NULL;
677     col_l_elt = g_list_first(col_l);
678     while(col_l_elt) {
679       gchar *fmt = g_strdup(col_l_elt->data);
680       cfmt = (col_width_data *) g_malloc(sizeof(col_width_data));
681       if (strncmp(fmt, cust_format, cust_format_len) != 0) {
682         cfmt->cfmt   = get_column_format_from_str(fmt);
683         cfmt->cfield = NULL;
684       } else {
685         cfmt->cfmt   = COL_CUSTOM;
686         cfmt->cfield = g_strdup(&fmt[cust_format_len+1]);  /* add 1 for ':' */
687       }
688       g_free (fmt);
689       if (cfmt->cfmt == -1) {
690         g_free(cfmt->cfield);
691         g_free(cfmt);
692         return PREFS_SET_SYNTAX_ERR;   /* string was bad */
693       }
694
695       col_l_elt      = col_l_elt->next;
696       cfmt->width    = strtol(col_l_elt->data, &p, 0);
697       if (p == col_l_elt->data || (*p != '\0' && *p != ':')) {
698         g_free(cfmt->cfield);
699         g_free(cfmt);
700         return PREFS_SET_SYNTAX_ERR;    /* number was bad */
701       }
702
703       if (*p == ':') {
704         cfmt->xalign = *(++p);
705       } else {
706         cfmt->xalign = 0;
707       }
708
709       col_l_elt      = col_l_elt->next;
710       recent.col_width_list = g_list_append(recent.col_width_list, cfmt);
711     }
712     prefs_clear_string_list(col_l);
713   }
714
715   return PREFS_SET_OK;
716 }
717
718
719 /* set one user's recent file key/value pair */
720 static prefs_set_pref_e
721 read_set_recent_pair_dynamic(gchar *key, gchar *value, void *private_data _U_)
722 {
723   if (strcmp(key, RECENT_KEY_CAPTURE_FILE) == 0) {
724     if(u3_active())
725       add_menu_recent_capture_file(u3_expand_device_path(value));
726     else
727       add_menu_recent_capture_file(value);
728   } else if (strcmp(key, RECENT_KEY_DISPLAY_FILTER) == 0) {
729         dfilter_combo_add_recent(value);
730   } else if (strcmp(key, RECENT_KEY_CAPTURE_FILTER) == 0) {
731         cfilter_combo_add_recent(value);
732 #ifdef HAVE_PCAP_REMOTE
733   } else if (strcmp(key, RECENT_KEY_REMOTE_HOST) == 0) {
734         capture_remote_combo_add_recent(value);
735 #endif
736   }
737
738   return PREFS_SET_OK;
739 }
740
741
742 /*
743  * Given a string of the form "<recent name>:<recent value>", as might appear
744  * as an argument to a "-o" option, parse it and set the recent value in
745  * question.  Return an indication of whether it succeeded or failed
746  * in some fashion.
747  */
748 int
749 recent_set_arg(char *prefarg)
750 {
751         gchar *p, *colonp;
752         int ret;
753
754         colonp = strchr(prefarg, ':');
755         if (colonp == NULL)
756                 return PREFS_SET_SYNTAX_ERR;
757
758         p = colonp;
759         *p++ = '\0';
760
761         /*
762          * Skip over any white space (there probably won't be any, but
763          * as we allow it in the preferences file, we might as well
764          * allow it here).
765          */
766         while (isspace((guchar)*p))
767                 p++;
768         if (*p == '\0') {
769                 /*
770                  * Put the colon back, so if our caller uses, in an
771                  * error message, the string they passed us, the message
772                  * looks correct.
773                  */
774                 *colonp = ':';
775                 return PREFS_SET_SYNTAX_ERR;
776         }
777
778         ret = read_set_recent_pair_static(prefarg, p, NULL);
779         *colonp = ':';  /* put the colon back */
780         return ret;
781 }
782
783
784 /* opens the user's recent common file and read the first part */
785 void
786 recent_read_static(char **rf_path_return, int *rf_errno_return)
787 {
788   char       *rf_path;
789   FILE       *rf;
790
791   /* set defaults */
792   recent.gui_geometry_main_x        =        20;
793   recent.gui_geometry_main_y        =        20;
794   recent.gui_geometry_main_width    = DEF_WIDTH;
795   recent.gui_geometry_main_height   = DEF_HEIGHT;
796   recent.gui_geometry_main_maximized=     FALSE;
797
798   recent.gui_geometry_status_pane_left  = (DEF_WIDTH/3);
799   recent.gui_geometry_status_pane_right = (DEF_WIDTH/3);
800   recent.gui_geometry_wlan_stats_pane = 200;
801
802   recent.privs_warn_if_elevated = TRUE;
803   recent.privs_warn_if_no_npf = TRUE;
804
805   recent.col_width_list = NULL;
806
807   /* Construct the pathname of the user's recent common file. */
808   rf_path = get_persconffile_path(RECENT_COMMON_FILE_NAME, FALSE, FALSE);
809
810   /* Read the user's recent common file, if it exists. */
811   *rf_path_return = NULL;
812   if ((rf = ws_fopen(rf_path, "r")) != NULL) {
813     /* We succeeded in opening it; read it. */
814     read_prefs_file(rf_path, rf, read_set_recent_common_pair_static, NULL);
815
816     fclose(rf);
817     g_free(rf_path);
818     rf_path = NULL;
819   } else {
820     /* We failed to open it.  If we failed for some reason other than
821        "it doesn't exist", return the errno and the pathname, so our
822        caller can report the error. */
823     if (errno != ENOENT) {
824       *rf_errno_return = errno;
825       *rf_path_return = rf_path;
826     }
827   }
828 }
829
830
831
832 /* opens the user's recent file and read the first part */
833 void
834 recent_read_profile_static(char **rf_path_return, int *rf_errno_return)
835 {
836   char       *rf_path, *rf_common_path;
837   FILE       *rf;
838
839   /* set defaults */
840   recent.main_toolbar_show      = TRUE;
841   recent.filter_toolbar_show    = TRUE;
842   recent.airpcap_toolbar_show   = FALSE;
843   recent.airpcap_driver_check_show   = TRUE;
844   recent.packet_list_show       = TRUE;
845   recent.tree_view_show         = TRUE;
846   recent.byte_view_show         = TRUE;
847   recent.statusbar_show         = TRUE;
848   recent.packet_list_colorize   = TRUE;
849   recent.gui_time_format        = TS_RELATIVE;
850   recent.gui_time_precision     = TS_PREC_AUTO;
851   recent.gui_zoom_level         = 0;
852   recent.gui_bytes_view         = 0;
853
854   /* pane size of zero will autodetect */
855   recent.gui_geometry_main_upper_pane   = 0;
856   recent.gui_geometry_main_lower_pane   = 0;
857
858   recent.has_gui_geometry_main_upper_pane = TRUE;
859   recent.has_gui_geometry_main_lower_pane = TRUE;
860   recent.has_gui_geometry_status_pane = TRUE;
861
862   if (recent.col_width_list) {
863     free_col_width_info(&recent);
864   }
865
866   /* Construct the pathname of the user's profile recent file. */
867   rf_path = get_persconffile_path(RECENT_FILE_NAME, TRUE, FALSE);
868
869   /* Read the user's recent file, if it exists. */
870   *rf_path_return = NULL;
871   if ((rf = ws_fopen(rf_path, "r")) != NULL) {
872     /* We succeeded in opening it; read it. */
873     read_prefs_file(rf_path, rf, read_set_recent_pair_static, NULL);
874     fclose(rf);
875
876     /* XXX: The following code doesn't actually do anything since
877      *  the "recent common file" always exists. Presumably the 
878      *  "if (!file_exists())" should actually be "if (file_exists())".
879      *  However, I've left the code as is because this 
880      *  behaviour has existed for quite some time and I don't
881      *  know what's supposed to happen at this point.
882      *  ToDo: Determine if the "recent common file" should be read at this point
883      */
884     rf_common_path = get_persconffile_path(RECENT_COMMON_FILE_NAME, FALSE, FALSE);
885     if (!file_exists(rf_common_path)) {
886       /* Read older common settings from recent file */
887       rf = ws_fopen(rf_path, "r");
888       read_prefs_file(rf_path, rf, read_set_recent_common_pair_static, NULL);
889       fclose(rf);
890     }
891     g_free(rf_common_path);
892     g_free(rf_path);
893     rf_path = NULL;
894   } else {
895     /* We failed to open it.  If we failed for some reason other than
896        "it doesn't exist", return the errno and the pathname, so our
897        caller can report the error. */
898     if (errno != ENOENT) {
899       *rf_errno_return = errno;
900       *rf_path_return = rf_path;
901     }
902   }
903 }
904
905 /* opens the user's recent file and read it out */
906 void
907 recent_read_dynamic(char **rf_path_return, int *rf_errno_return)
908 {
909   char       *rf_path;
910   FILE       *rf;
911
912
913   /* Construct the pathname of the user's recent common file. */
914   rf_path = get_persconffile_path(RECENT_COMMON_FILE_NAME, FALSE, FALSE);
915   if (!file_exists (rf_path)) {
916     /* Recent common file does not exist, read from default recent */
917     g_free (rf_path);
918     rf_path = get_persconffile_path(RECENT_FILE_NAME, FALSE, FALSE);
919   }
920
921   /* Read the user's recent file, if it exists. */
922   *rf_path_return = NULL;
923   if ((rf = ws_fopen(rf_path, "r")) != NULL) {
924     /* We succeeded in opening it; read it. */
925     read_prefs_file(rf_path, rf, read_set_recent_pair_dynamic, NULL);
926     /* set dfilter combobox to have an empty line */
927     dfilter_combo_add_empty();
928     fclose(rf);
929     g_free(rf_path);
930     rf_path = NULL;
931   } else {
932     /* We failed to open it.  If we failed for some reason other than
933        "it doesn't exist", return the errno and the pathname, so our
934        caller can report the error. */
935     if (errno != ENOENT) {
936       *rf_errno_return = errno;
937       *rf_path_return = rf_path;
938     }
939   }
940 }
941
942 gint
943 recent_get_column_width(gint col)
944 {
945   GList *col_l;
946   col_width_data *col_w;
947   gint cfmt;
948   const gchar *cfield = NULL;
949
950   cfmt = get_column_format(col);
951   if (cfmt == COL_CUSTOM) {
952     cfield = get_column_custom_field(col);
953   }
954
955   col_l = g_list_first(recent.col_width_list);
956   while (col_l) {
957     col_w = (col_width_data *) col_l->data;
958     if (col_w->cfmt == cfmt) {
959       if (cfmt != COL_CUSTOM || strcmp (cfield, col_w->cfield) == 0) {
960         return col_w->width;
961       }
962     }
963     col_l = col_l->next;
964   }
965
966   return -1;
967 }
968
969 void
970 recent_set_column_width(gint col, gint width)
971 {
972   GList *col_l;
973   col_width_data *col_w;
974   gint cfmt;
975   const gchar *cfield = NULL;
976   gboolean found = FALSE;
977
978   cfmt = get_column_format(col);
979   if (cfmt == COL_CUSTOM) {
980     cfield = get_column_custom_field(col);
981   }
982
983   col_l = g_list_first(recent.col_width_list);
984   while (col_l) {
985     col_w = (col_width_data *) col_l->data;
986     if (col_w->cfmt == cfmt) {
987       if (cfmt != COL_CUSTOM || strcmp (cfield, col_w->cfield) == 0) {
988         col_w->width = width;
989         found = TRUE;
990         break;
991       }
992     }
993     col_l = col_l->next;
994   }
995
996   if (!found) {
997     col_w = (col_width_data *) g_malloc(sizeof(col_width_data));
998     col_w->cfmt = cfmt;
999     if (cfield) {
1000       col_w->cfield = g_strdup(cfield);
1001     } else {
1002       col_w->cfield = NULL;
1003     }
1004     col_w->width = width;
1005     col_w->xalign = 0;
1006     recent.col_width_list = g_list_append(recent.col_width_list, col_w);
1007   }
1008 }
1009
1010 gchar
1011 recent_get_column_xalign(gint col)
1012 {
1013   GList *col_l;
1014   col_width_data *col_w;
1015   gint cfmt;
1016   const gchar *cfield = NULL;
1017
1018   cfmt = get_column_format(col);
1019   if (cfmt == COL_CUSTOM) {
1020     cfield = get_column_custom_field(col);
1021   }
1022
1023   col_l = g_list_first(recent.col_width_list);
1024   while (col_l) {
1025     col_w = (col_width_data *) col_l->data;
1026     if (col_w->cfmt == cfmt) {
1027       if (cfmt != COL_CUSTOM || strcmp (cfield, col_w->cfield) == 0) {
1028         return col_w->xalign;
1029       }
1030     }
1031     col_l = col_l->next;
1032   }
1033
1034   return 0;
1035 }
1036
1037 void
1038 recent_set_column_xalign(gint col, gchar xalign)
1039 {
1040   GList *col_l;
1041   col_width_data *col_w;
1042   gint cfmt;
1043   const gchar *cfield = NULL;
1044   gboolean found = FALSE;
1045
1046   cfmt = get_column_format(col);
1047   if (cfmt == COL_CUSTOM) {
1048     cfield = get_column_custom_field(col);
1049   }
1050
1051   col_l = g_list_first(recent.col_width_list);
1052   while (col_l) {
1053     col_w = (col_width_data *) col_l->data;
1054     if (col_w->cfmt == cfmt) {
1055       if (cfmt != COL_CUSTOM || strcmp (cfield, col_w->cfield) == 0) {
1056         col_w->xalign = xalign;
1057         found = TRUE;
1058         break;
1059       }
1060     }
1061     col_l = col_l->next;
1062   }
1063
1064   if (!found) {
1065     col_w = (col_width_data *) g_malloc(sizeof(col_width_data));
1066     col_w->cfmt = cfmt;
1067     if (cfield) {
1068       col_w->cfield = g_strdup(cfield);
1069     } else {
1070       col_w->cfield = NULL;
1071     }
1072     col_w->width = 40;
1073     col_w->xalign = xalign;
1074     recent.col_width_list = g_list_append(recent.col_width_list, col_w);
1075   }
1076 }
1077