Fix a bug in counting DATA chunks.
[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  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
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
30 #include <gtk/gtk.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "recent.h"
35 #include <epan/epan.h>
36 #include <epan/filesystem.h>
37 #include "menu.h"
38 #include "main.h"
39 #include <epan/prefs.h>
40 #include <epan/prefs-int.h>
41 #include "ui_util.h"
42 #include "dlg_utils.h"
43 #include "cfilter_combo_utils.h"
44 #include "simple_dialog.h"
45
46 #define RECENT_KEY_MAIN_TOOLBAR_SHOW        "gui.toolbar_main_show"
47 #define RECENT_KEY_FILTER_TOOLBAR_SHOW      "gui.filter_toolbar_show"
48 #define RECENT_KEY_PACKET_LIST_SHOW         "gui.packet_list_show"
49 #define RECENT_KEY_TREE_VIEW_SHOW           "gui.tree_view_show"
50 #define RECENT_KEY_BYTE_VIEW_SHOW           "gui.byte_view_show"
51 #define RECENT_KEY_STATUSBAR_SHOW           "gui.statusbar_show"
52 #define RECENT_KEY_PACKET_LIST_COLORIZE     "gui.packet_list_colorize"
53 #define RECENT_GUI_TIME_FORMAT              "gui.time_format"
54 #define RECENT_GUI_ZOOM_LEVEL               "gui.zoom_level"
55 #define RECENT_GUI_GEOMETRY_MAIN_X          "gui.geometry_main_x"
56 #define RECENT_GUI_GEOMETRY_MAIN_Y          "gui.geometry_main_y"
57 #define RECENT_GUI_GEOMETRY_MAIN_WIDTH      "gui.geometry_main_width"
58 #define RECENT_GUI_GEOMETRY_MAIN_HEIGHT     "gui.geometry_main_height"
59 #define RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED  "gui.geometry_main_maximized"
60 #define RECENT_GUI_GEOMETRY_MAIN_UPPER_PANE "gui.geometry_main_upper_pane"
61 #define RECENT_GUI_GEOMETRY_MAIN_LOWER_PANE "gui.geometry_main_lower_pane"
62 #define RECENT_GUI_GEOMETRY_STATUS_PANE     "gui.geometry_status_pane"
63 #define RECENT_GUI_FILEOPEN_REMEMBERED_DIR  "gui.fileopen_remembered_dir"
64 #define RECENT_GUI_GEOMETRY "gui.geom."
65
66 #define RECENT_FILE_NAME "recent"
67
68
69 /* #include "../menu.h" */
70 extern void add_menu_recent_capture_file(gchar *file);
71
72 recent_settings_t recent;
73
74 static char *ts_type_text[] =
75         { "RELATIVE", "ABSOLUTE", "ABSOLUTE_WITH_DATE", "DELTA", NULL };
76
77 /* Takes an string and a pointer to an array of strings, and a default int value.
78  * The array must be terminated by a NULL string. If the string is found in the array
79  * of strings, the index of that string in the array is returned. Otherwise, the
80  * default value that was passed as the third argument is returned.
81  */
82 static int
83 find_index_from_string_array(char *needle, char **haystack, int default_value)
84 {
85         int i = 0;
86
87         while (haystack[i] != NULL) {
88                 if (strcmp(needle, haystack[i]) == 0) {
89                         return i;
90                 }
91                 i++;
92         }
93         return default_value;
94 }
95
96 /* Attempt to Write out "recent" to the user's recent file.
97    If we got an error report it with a dialog box and return FALSE,
98    otherwise return TRUE. */
99 gboolean
100 write_recent(void)
101 {
102   char        *pf_dir_path;
103   char        *rf_path;
104   FILE        *rf;
105
106   /* To do:
107    * - Split output lines longer than MAX_VAL_LEN
108    * - Create a function for the preference directory check/creation
109    *   so that duplication can be avoided with filter.c
110    */
111
112   /* Create the directory that holds personal configuration files, if
113      necessary.  */
114   if (create_persconffile_dir(&pf_dir_path) == -1) {
115      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
116       "Can't create directory\n\"%s\"\nfor recent file: %s.", pf_dir_path,
117       strerror(errno));
118      g_free(pf_dir_path);
119      return FALSE;
120   }
121
122   rf_path = get_persconffile_path(RECENT_FILE_NAME, TRUE);
123   if ((rf = fopen(rf_path, "w")) == NULL) {
124      simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
125       "Can't open recent file\n\"%s\": %s.", rf_path,
126       strerror(errno));
127     g_free(rf_path);
128     return FALSE;
129   }
130
131   fputs("# Recent settings file for Ethereal " VERSION ".\n"
132     "#\n"
133     "# This file is regenerated each time Ethereal is quit.\n"
134     "# So be careful, if you want to make manual changes here.\n"
135     "\n"
136     "######## Recent capture files (latest last) ########\n"
137     "\n", rf);
138
139   menu_recent_file_write_all(rf);
140
141   fputs("\n"
142     "######## Recent capture filters (latest last) ########\n"
143     "\n", rf);
144
145   cfilter_combo_recent_write_all(rf);
146
147   fputs("\n"
148     "######## Recent display filters (latest last) ########\n"
149     "\n", rf);
150
151   dfilter_recent_combo_write_all(rf);
152
153   fprintf(rf, "\n# Main Toolbar show (hide).\n");
154   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
155   fprintf(rf, RECENT_KEY_MAIN_TOOLBAR_SHOW ": %s\n",
156                   recent.main_toolbar_show == TRUE ? "TRUE" : "FALSE");
157
158   fprintf(rf, "\n# Filter Toolbar show (hide).\n");
159   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
160   fprintf(rf, RECENT_KEY_FILTER_TOOLBAR_SHOW ": %s\n",
161                   recent.filter_toolbar_show == TRUE ? "TRUE" : "FALSE");
162
163   fprintf(rf, "\n# Packet list show (hide).\n");
164   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
165   fprintf(rf, RECENT_KEY_PACKET_LIST_SHOW ": %s\n",
166                   recent.packet_list_show == TRUE ? "TRUE" : "FALSE");
167
168   fprintf(rf, "\n# Tree view show (hide).\n");
169   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
170   fprintf(rf, RECENT_KEY_TREE_VIEW_SHOW ": %s\n",
171                   recent.tree_view_show == TRUE ? "TRUE" : "FALSE");
172
173   fprintf(rf, "\n# Byte view show (hide).\n");
174   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
175   fprintf(rf, RECENT_KEY_BYTE_VIEW_SHOW ": %s\n",
176                   recent.byte_view_show == TRUE ? "TRUE" : "FALSE");
177
178   fprintf(rf, "\n# Statusbar show (hide).\n");
179   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
180   fprintf(rf, RECENT_KEY_STATUSBAR_SHOW ": %s\n",
181                   recent.statusbar_show == TRUE ? "TRUE" : "FALSE");
182
183   fprintf(rf, "\n# Packet list colorize (hide).\n");
184   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
185   fprintf(rf, RECENT_KEY_PACKET_LIST_COLORIZE ": %s\n",
186                   recent.packet_list_colorize == TRUE ? "TRUE" : "FALSE");
187
188   fprintf(rf, "\n# Timestamp display format.\n");
189   fprintf(rf, "# One of: RELATIVE, ABSOLUTE, ABSOLUTE_WITH_DATE, DELTA\n");
190   fprintf(rf, RECENT_GUI_TIME_FORMAT ": %s\n",
191           ts_type_text[recent.gui_time_format]);
192
193   fprintf(rf, "\n# Zoom level.\n");
194   fprintf(rf, "# A decimal number.\n");
195   fprintf(rf, RECENT_GUI_ZOOM_LEVEL ": %d\n",
196                   recent.gui_zoom_level);
197
198   fprintf(rf, "\n# Main window geometry.\n");
199   fprintf(rf, "# Decimal integers.\n");
200   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_X ": %d\n", recent.gui_geometry_main_x);
201   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_Y ": %d\n", recent.gui_geometry_main_y);
202   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_WIDTH ": %d\n",
203                   recent.gui_geometry_main_width);
204   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_HEIGHT ": %d\n",
205                   recent.gui_geometry_main_height);
206   
207   fprintf(rf, "\n# Main window maximized (GTK2 only).\n");
208   fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
209   fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED ": %s\n",
210                   recent.gui_geometry_main_maximized == TRUE ? "TRUE" : "FALSE");
211
212   fprintf(rf, "\n# Main window panes (GTK2 only).\n");
213   fprintf(rf, "# Decimal numbers.\n");
214   if (recent.gui_geometry_main_upper_pane != 0) {
215     fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_UPPER_PANE ": %d\n",
216                   recent.gui_geometry_main_upper_pane);
217   }
218   if (recent.gui_geometry_main_lower_pane != 0) {
219     fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_LOWER_PANE ": %d\n",
220                   recent.gui_geometry_main_lower_pane);
221   }
222   if (recent.gui_geometry_status_pane != 0) {
223     fprintf(rf, RECENT_GUI_GEOMETRY_STATUS_PANE ": %d\n",
224                   recent.gui_geometry_status_pane);
225   }
226
227   if (get_last_open_dir() != NULL) {
228     fprintf(rf, "\n# Last directory navigated to in File Open dialog.\n");
229     fprintf(rf, RECENT_GUI_FILEOPEN_REMEMBERED_DIR ": %s\n", get_last_open_dir());
230   }
231
232   window_geom_recent_write_all(rf);
233
234   fclose(rf);
235
236   /* XXX - catch I/O errors (e.g. "ran out of disk space") and return
237      an error indication, or maybe write to a new recent file and
238      rename that file on top of the old one only if there are not I/O
239      errors. */
240   return TRUE;
241 }
242
243
244 /* write the geometry values of a window to recent file */
245 void 
246 write_recent_geom(gpointer key _U_, gpointer value, gpointer rf)
247 {
248     window_geometry_t *geom = value;
249
250     fprintf(rf, "\n# Geometry and maximized state (GTK2 only) of %s window.\n", geom->key);
251     fprintf(rf, "# Decimal integers.\n");
252     fprintf(rf, RECENT_GUI_GEOMETRY "%s.x: %d\n", geom->key, geom->x);
253     fprintf(rf, RECENT_GUI_GEOMETRY "%s.y: %d\n", geom->key, geom->y);
254     fprintf(rf, RECENT_GUI_GEOMETRY "%s.width: %d\n", geom->key,
255               geom->width);
256     fprintf(rf, RECENT_GUI_GEOMETRY "%s.height: %d\n", geom->key,
257               geom->height);
258
259     fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
260     fprintf(rf, RECENT_GUI_GEOMETRY "%s.maximized: %s\n", geom->key,
261               geom->maximized == TRUE ? "TRUE" : "FALSE");
262
263 }
264
265
266 /* set one user's recent file key/value pair */
267 static int
268 read_set_recent_pair(gchar *key, gchar *value)
269 {
270   long num;
271   char *p;
272
273   if (strcmp(key, RECENT_KEY_CAPTURE_FILE) == 0) {
274         add_menu_recent_capture_file(value);
275   } else if (strcmp(key, RECENT_KEY_DISPLAY_FILTER) == 0) {
276         dfilter_combo_add_recent(value);
277   } else if (strcmp(key, RECENT_KEY_CAPTURE_FILTER) == 0) {
278         cfilter_combo_add_recent(value);
279   } else if (strcmp(key, RECENT_KEY_MAIN_TOOLBAR_SHOW) == 0) {
280     if (strcasecmp(value, "true") == 0) {
281         recent.main_toolbar_show = TRUE;
282     }
283     else {
284         recent.main_toolbar_show = FALSE;
285     }
286   } else if (strcmp(key, RECENT_KEY_FILTER_TOOLBAR_SHOW) == 0) {
287     if (strcasecmp(value, "true") == 0) {
288         recent.filter_toolbar_show = TRUE;
289     }
290     else {
291         recent.filter_toolbar_show = FALSE;
292     }
293   } else if (strcmp(key, RECENT_KEY_PACKET_LIST_SHOW) == 0) {
294     if (strcasecmp(value, "true") == 0) {
295         recent.packet_list_show = TRUE;
296     }
297     else {
298         recent.packet_list_show = FALSE;
299     }
300   } else if (strcmp(key, RECENT_KEY_TREE_VIEW_SHOW) == 0) {
301     if (strcasecmp(value, "true") == 0) {
302         recent.tree_view_show = TRUE;
303     }
304     else {
305         recent.tree_view_show = FALSE;
306     }
307   } else if (strcmp(key, RECENT_KEY_BYTE_VIEW_SHOW) == 0) {
308     if (strcasecmp(value, "true") == 0) {
309         recent.byte_view_show = TRUE;
310     }
311     else {
312         recent.byte_view_show = FALSE;
313     }
314   } else if (strcmp(key, RECENT_KEY_STATUSBAR_SHOW) == 0) {
315     if (strcasecmp(value, "true") == 0) {
316         recent.statusbar_show = TRUE;
317     }
318     else {
319         recent.statusbar_show = FALSE;
320     }
321   } else if (strcmp(key, RECENT_KEY_PACKET_LIST_COLORIZE) == 0) {
322     if (strcasecmp(value, "true") == 0) {
323         recent.packet_list_colorize = TRUE;
324     }
325     else {
326         recent.packet_list_colorize = FALSE;
327     }
328   } else if (strcmp(key, RECENT_GUI_TIME_FORMAT) == 0) {
329     recent.gui_time_format =
330         find_index_from_string_array(value, ts_type_text, TS_RELATIVE);
331   } else if (strcmp(key, RECENT_GUI_ZOOM_LEVEL) == 0) {
332     num = strtol(value, &p, 0);
333     if (p == value || *p != '\0')
334       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
335     recent.gui_zoom_level = num;
336   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED) == 0) {
337     if (strcasecmp(value, "true") == 0) {
338         recent.gui_geometry_main_maximized = TRUE;
339     }
340     else {
341         recent.gui_geometry_main_maximized = FALSE;
342     }
343
344   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_X) == 0) {
345     num = strtol(value, &p, 0);
346     if (p == value || *p != '\0')
347       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
348     recent.gui_geometry_main_x = num;
349   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_Y) == 0) {
350     num = strtol(value, &p, 0);
351     if (p == value || *p != '\0')
352       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
353     recent.gui_geometry_main_y = num;
354   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_WIDTH) == 0) {
355     num = strtol(value, &p, 0);
356     if (p == value || *p != '\0')
357       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
358     if (num <= 0)
359       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
360     recent.gui_geometry_main_width = num;
361   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_HEIGHT) == 0) {
362     num = strtol(value, &p, 0);
363     if (p == value || *p != '\0')
364       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
365     if (num <= 0)
366       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
367     recent.gui_geometry_main_height = num;
368   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_UPPER_PANE) == 0) {
369     num = strtol(value, &p, 0);
370     if (p == value || *p != '\0')
371       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
372     if (num <= 0)
373       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
374     recent.gui_geometry_main_upper_pane = num;
375   } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_LOWER_PANE) == 0) {
376     num = strtol(value, &p, 0);
377     if (p == value || *p != '\0')
378       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
379     if (num <= 0)
380       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
381     recent.gui_geometry_main_lower_pane = num;
382   } else if (strcmp(key, RECENT_GUI_GEOMETRY_STATUS_PANE) == 0) {
383     num = strtol(value, &p, 0);
384     if (p == value || *p != '\0')
385       return PREFS_SET_SYNTAX_ERR;      /* number was bad */
386     if (num <= 0)
387       return PREFS_SET_SYNTAX_ERR;      /* number must be positive */
388     recent.gui_geometry_status_pane = num;
389   } else if (strcmp(key, RECENT_GUI_FILEOPEN_REMEMBERED_DIR) == 0) {
390     set_last_open_dir(value);
391   } else if (strncmp(key, RECENT_GUI_GEOMETRY, sizeof(RECENT_GUI_GEOMETRY)-1) == 0) {
392     /* now have something like "gui.geom.main.x", split it into win and sub_key */
393     char *win = &key[sizeof(RECENT_GUI_GEOMETRY)-1];
394     char *sub_key = strchr(win, '.');
395     if(sub_key) {
396       *sub_key = '\0';
397       sub_key++;
398       window_geom_recent_read_pair(win, sub_key, value);
399     }
400   }
401
402   return PREFS_SET_OK;
403 }
404
405
406 /* opens the user's recent file and read it out */
407 void
408 read_recent(char **rf_path_return, int *rf_errno_return)
409 {
410   char       *rf_path;
411   FILE       *rf;
412
413
414   /* set defaults */
415   recent.main_toolbar_show      = TRUE;
416   recent.filter_toolbar_show    = TRUE;
417   recent.packet_list_show       = TRUE;
418   recent.tree_view_show         = TRUE;
419   recent.byte_view_show         = TRUE;
420   recent.statusbar_show         = TRUE;
421   recent.packet_list_colorize   = TRUE;
422   recent.gui_time_format        = TS_RELATIVE;
423   recent.gui_zoom_level         = 0;
424
425   recent.gui_geometry_main_x        =        20;
426   recent.gui_geometry_main_y        =        20;
427   recent.gui_geometry_main_width    = DEF_WIDTH;
428   recent.gui_geometry_main_height   = DEF_HEIGHT;
429   recent.gui_geometry_main_maximized=     FALSE;
430
431   /* pane size of zero will autodetect */
432   recent.gui_geometry_main_upper_pane   = 0;
433   recent.gui_geometry_main_lower_pane   = 0;
434   recent.gui_geometry_status_pane       = 0;
435
436   /* Construct the pathname of the user's recent file. */
437   rf_path = get_persconffile_path(RECENT_FILE_NAME, FALSE);
438
439   /* Read the user's recent file, if it exists. */
440   *rf_path_return = NULL;
441   if ((rf = fopen(rf_path, "r")) != NULL) {
442     /* We succeeded in opening it; read it. */
443     read_prefs_file(rf_path, rf, read_set_recent_pair);
444         /* set dfilter combobox to have an empty line */
445     dfilter_combo_add_empty();
446     fclose(rf);
447     g_free(rf_path);
448     rf_path = NULL;
449   } else {
450     /* We failed to open it.  If we failed for some reason other than
451        "it doesn't exist", return the errno and the pathname, so our
452        caller can report the error. */
453     if (errno != ENOENT) {
454       *rf_errno_return = errno;
455       *rf_path_return = rf_path;
456     }
457   }
458 }
459
460