4 * $Id: file.c,v 1.38 1999/07/13 03:08:05 gram Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@zing.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
47 #ifdef NEED_SNPRINTF_H
53 # include "snprintf.h"
56 #ifdef NEED_STRERROR_H
60 #ifdef HAVE_SYS_TYPES_H
61 # include <sys/types.h>
64 #ifdef HAVE_NETINET_IN_H
65 # include <netinet/in.h>
76 #include "packet-ncp.h"
78 #define TAIL_TIMEOUT 2000 /* msec */
80 extern GtkWidget *packet_list, *prog_bar, *info_bar, *byte_view, *tree_view;
81 extern GtkStyle *pl_style;
82 extern guint file_ctx;
84 extern int sync_pipe[];
86 guint cap_input_id, tail_timeout_id;
88 static guint32 firstsec, firstusec;
89 static guint32 lastsec, lastusec;
91 static void wtap_dispatch_cb(u_char *, const struct wtap_pkthdr *, int,
94 static void init_col_widths(capture_file *);
95 static void set_col_widths(capture_file *);
98 static gint tail_timeout_cb(gpointer);
102 open_cap_file(char *fname, capture_file *cf) {
105 /* First, make sure the file is valid */
106 if (stat(fname, &cf_stat))
109 if (! S_ISREG(cf_stat.st_mode) && ! S_ISFIFO(cf_stat.st_mode))
110 return (OPEN_CAP_FILE_NOT_REGULAR);
113 /* Next, try to open the file */
114 cf->fh = fopen(fname, "r");
118 fseek(cf->fh, 0L, SEEK_END);
119 cf->f_len = ftell(cf->fh);
122 /* set the file name beacuse we need it to set the follow stream filter */
123 cf->filename = g_strdup( fname );
125 /* Next, find out what type of file we're dealing with */
126 cf->cd_t = WTAP_FILE_UNKNOWN;
132 firstsec = 0, firstusec = 0;
133 lastsec = 0, lastusec = 0;
135 cf->wth = wtap_open_offline(fname);
136 if (cf->wth == NULL) {
138 /* XXX - we assume that, because we were able to open it above,
139 this must have failed because it's not a capture file in
140 a format we can read. */
141 return (OPEN_CAP_FILE_UNKNOWN_FORMAT);
144 cf->fh = wtap_file(cf->wth);
145 cf->cd_t = wtap_file_type(cf->wth);
146 cf->snap = wtap_snapshot_length(cf->wth);
151 free_packets_cb(gpointer data, gpointer user_data)
156 /* Reset everything to a pristine state */
158 close_cap_file(capture_file *cf, void *w, guint context) {
168 g_list_foreach(cf->plist, free_packets_cb, NULL);
169 g_list_free(cf->plist);
172 gtk_text_freeze(GTK_TEXT(byte_view));
173 gtk_text_set_point(GTK_TEXT(byte_view), 0);
174 gtk_text_forward_delete(GTK_TEXT(byte_view),
175 gtk_text_get_length(GTK_TEXT(byte_view)));
176 gtk_text_thaw(GTK_TEXT(byte_view));
177 gtk_tree_clear_items(GTK_TREE(tree_view), 0,
178 g_list_length(GTK_TREE(tree_view)->children));
180 gtk_clist_freeze(GTK_CLIST(packet_list));
181 gtk_clist_clear(GTK_CLIST(packet_list));
182 gtk_clist_thaw(GTK_CLIST(packet_list));
183 gtk_statusbar_pop(GTK_STATUSBAR(w), context);
187 load_cap_file(char *fname, capture_file *cf) {
188 gchar *name_ptr, *load_msg, *load_fmt = " Loading: %s...";
189 gchar *done_fmt = " File: %s Drops: %d";
190 gchar *err_fmt = " Error: Could not load '%s'";
195 close_cap_file(cf, info_bar, file_ctx);
197 /* Initialize protocol-specific variables */
200 if ((name_ptr = (gchar *) strrchr(fname, '/')) == NULL)
204 load_msg = g_malloc(strlen(name_ptr) + strlen(load_fmt) + 2);
205 sprintf(load_msg, load_fmt, name_ptr);
206 gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, load_msg);
208 timeout = gtk_timeout_add(250, file_progress_cb, (gpointer) &cf);
210 err = open_cap_file(fname, cf);
211 if ((err == 0) && (cf->cd_t != WTAP_FILE_UNKNOWN)) {
212 gtk_clist_freeze(GTK_CLIST(packet_list));
214 wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf);
217 cf->fh = fopen(fname, "r");
220 gtk_clist_thaw(GTK_CLIST(packet_list));
223 gtk_timeout_remove(timeout);
224 gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar), 0);
226 gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
229 msg_len = strlen(name_ptr) + strlen(done_fmt) + 64;
230 load_msg = g_realloc(load_msg, msg_len);
232 if (cf->user_saved || !cf->save_file)
233 snprintf(load_msg, msg_len, done_fmt, name_ptr, cf->drops);
235 snprintf(load_msg, msg_len, done_fmt, "<none>", cf->drops);
237 gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, load_msg);
240 /* name_ptr[-1] = '\0'; Why is this here? It causes problems with capture files */
241 set_menu_sensitivity("/File/Close", TRUE);
242 set_menu_sensitivity("/File/Reload", TRUE);
243 set_menu_sensitivity("/Tools/Summary", TRUE);
245 msg_len = strlen(name_ptr) + strlen(err_fmt) + 2;
246 load_msg = g_realloc(load_msg, msg_len);
247 snprintf(load_msg, msg_len, err_fmt, name_ptr);
248 gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, load_msg);
250 set_menu_sensitivity("/File/Close", FALSE);
251 set_menu_sensitivity("/File/Save", FALSE);
252 set_menu_sensitivity("/File/Save As...", FALSE);
253 set_menu_sensitivity("/File/Reload", FALSE);
254 set_menu_sensitivity("/Tools/Summary", FALSE);
261 cap_file_input_cb (gpointer data, gint source, GdkInputCondition condition) {
263 capture_file *cf = (capture_file *)data;
266 /* avoid reentrancy problems and stack overflow */
267 gtk_input_remove(cap_input_id);
268 if (tail_timeout_id != -1) gtk_timeout_remove(tail_timeout_id);
270 if (read(sync_pipe[0], buffer, 256) <= 0) {
272 /* process data until end of file and stop capture (restore menu items) */
273 gtk_clist_freeze(GTK_CLIST(packet_list));
275 wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf);
278 gtk_clist_thaw(GTK_CLIST(packet_list));
282 set_menu_sensitivity("/File/Open...", TRUE);
283 set_menu_sensitivity("/File/Close", TRUE);
284 set_menu_sensitivity("/File/Save As...", TRUE);
285 set_menu_sensitivity("/File/Reload", TRUE);
286 set_menu_sensitivity("/Capture/Start...", TRUE);
287 set_menu_sensitivity("/Tools/Capture...", TRUE);
288 set_menu_sensitivity("/Tools/Summary", TRUE);
289 gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, " File: <none>");
293 gtk_clist_freeze(GTK_CLIST(packet_list));
295 wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf);
298 gtk_clist_thaw(GTK_CLIST(packet_list));
300 /* restore pipe handler */
301 cap_input_id = gtk_input_add_full (sync_pipe[0],
308 /* only useful in case of low amount of captured data */
309 tail_timeout_id = gtk_timeout_add(TAIL_TIMEOUT, tail_timeout_cb, (gpointer) cf);
314 tail_timeout_cb(gpointer data) {
316 capture_file *cf = (capture_file *)data;
318 /* avoid reentrancy problems and stack overflow */
319 gtk_input_remove(cap_input_id);
321 gtk_clist_freeze(GTK_CLIST(packet_list));
323 wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf);
326 gtk_clist_thaw(GTK_CLIST(packet_list));
328 cap_input_id = gtk_input_add_full (sync_pipe[0],
339 tail_cap_file(char *fname, capture_file *cf) {
342 close_cap_file(cf, info_bar, file_ctx);
344 /* Initialize protocol-speficic variables */
347 err = open_cap_file(fname, cf);
348 if ((err == 0) && (cf->cd_t != WTAP_FILE_UNKNOWN)) {
350 set_menu_sensitivity("/File/Open...", FALSE);
351 set_menu_sensitivity("/File/Close", FALSE);
352 set_menu_sensitivity("/File/Reload", FALSE);
353 set_menu_sensitivity("/Capture/Start...", FALSE);
354 set_menu_sensitivity("/Tools/Capture...", FALSE);
355 set_menu_sensitivity("/Tools/Summary", FALSE);
357 cf->fh = fopen(fname, "r");
358 tail_timeout_id = -1;
359 cap_input_id = gtk_input_add_full (sync_pipe[0],
365 gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx,
366 " <live capture in progress>");
369 set_menu_sensitivity("/File/Close", FALSE);
370 set_menu_sensitivity("/File/Save", FALSE);
371 set_menu_sensitivity("/File/Save As...", FALSE);
372 set_menu_sensitivity("/File/Reload", FALSE);
373 set_menu_sensitivity("/Tools/Summary", FALSE);
381 compute_time_stamps(frame_data *fdata, capture_file *cf)
383 /* If we don't have the time stamp of the first packet, it's because this
384 is the first packet. Save the time stamp of this packet as the time
385 stamp of the first packet. */
386 if (!firstsec && !firstusec) {
387 firstsec = fdata->abs_secs;
388 firstusec = fdata->abs_usecs;
391 /* Do the same for the time stamp of the previous packet. */
392 if (!lastsec && !lastusec) {
393 lastsec = fdata->abs_secs;
394 lastusec = fdata->abs_usecs;
397 /* Get the time elapsed between the first packet and this packet. */
398 cf->esec = fdata->abs_secs - firstsec;
399 if (firstusec <= fdata->abs_usecs) {
400 cf->eusec = fdata->abs_usecs - firstusec;
402 cf->eusec = (fdata->abs_usecs + 1000000) - firstusec;
405 fdata->rel_secs = cf->esec;
406 fdata->rel_usecs = cf->eusec;
408 /* Do the same for the previous packet */
409 fdata->del_secs = fdata->abs_secs - lastsec;
410 if (lastusec <= fdata->abs_usecs) {
411 fdata->del_usecs = fdata->abs_usecs - lastusec;
413 fdata->del_usecs = (fdata->abs_usecs + 1000000) - lastusec;
416 lastsec = fdata->abs_secs;
417 lastusec = fdata->abs_usecs;
421 change_time_format_in_packet_list(frame_data *fdata, capture_file *cf)
425 /* XXX - there really should be a way of checking "cf->cinfo" for this;
426 the answer isn't going to change from packet to packet, so we should
427 simply skip all the "change_time_formats()" work if we're not
428 changing anything. */
429 fdata->cinfo = &cf->cinfo;
430 if (!check_col(fdata, COL_CLS_TIME)) {
431 /* There are no columns that show the time in the "command-line-specified"
432 format, so there's nothing we need to do. */
436 compute_time_stamps(fdata, cf);
438 for (i = 0; i < fdata->cinfo->num_cols; i++) {
439 fdata->cinfo->col_data[i][0] = '\0';
441 col_add_cls_time(fdata);
442 for (i = 0; i < fdata->cinfo->num_cols; i++) {
443 if (fdata->cinfo->fmt_matx[i][COL_CLS_TIME]) {
444 /* This is one of the columns that shows the time in
445 "command-line-specified" format; update it. */
446 col_width = gdk_string_width(pl_style->font, fdata->cinfo->col_data[i]);
447 if (col_width > fdata->cinfo->col_width[i])
448 fdata->cinfo->col_width[i] = col_width;
449 gtk_clist_set_text(GTK_CLIST(packet_list), cf->count - 1, i,
450 fdata->cinfo->col_data[i]);
457 add_packet_to_packet_list(frame_data *fdata, capture_file *cf, const u_char *buf)
459 gint i, col_width, row;
460 proto_tree *protocol_tree;
462 compute_time_stamps(fdata, cf);
464 fdata->cinfo = &cf->cinfo;
465 for (i = 0; i < fdata->cinfo->num_cols; i++) {
466 fdata->cinfo->col_data[i][0] = '\0';
468 if (check_col(fdata, COL_NUMBER))
469 col_add_fstr(fdata, COL_NUMBER, "%d", cf->count);
470 /* Apply the display filter */
472 protocol_tree = proto_tree_create_root();
473 dissect_packet(buf, fdata, protocol_tree);
474 fdata->passed_dfilter = dfilter_apply(cf->dfcode, protocol_tree, cf->pd);
477 dissect_packet(buf, fdata, NULL);
478 fdata->passed_dfilter = TRUE;
480 for (i = 0; i < fdata->cinfo->num_cols; i++) {
481 col_width = gdk_string_width(pl_style->font, fdata->cinfo->col_data[i]);
482 if (col_width > fdata->cinfo->col_width[i])
483 fdata->cinfo->col_width[i] = col_width;
485 if (fdata->passed_dfilter) {
486 row = gtk_clist_append(GTK_CLIST(packet_list), fdata->cinfo->col_data);
487 gtk_clist_set_row_data(GTK_CLIST(packet_list), row, fdata);
493 wtap_dispatch_cb(u_char *user, const struct wtap_pkthdr *phdr, int offset,
496 capture_file *cf = (capture_file *) user;
498 while (gtk_events_pending())
499 gtk_main_iteration();
501 /* Allocate the next list entry, and add it to the list. */
502 fdata = (frame_data *) g_malloc(sizeof(frame_data));
503 cf->plist = g_list_append(cf->plist, (gpointer) fdata);
508 fdata->pkt_len = phdr->len;
509 fdata->cap_len = phdr->caplen;
510 fdata->file_off = offset;
511 fdata->lnk_t = phdr->pkt_encap;
512 fdata->abs_secs = phdr->ts.tv_sec;
513 fdata->abs_usecs = phdr->ts.tv_usec;
516 add_packet_to_packet_list(fdata, cf, buf);
520 filter_packets_cb(gpointer data, gpointer user_data)
522 frame_data *fd = data;
523 capture_file *cf = user_data;
528 fseek(cf->fh, fd->file_off, SEEK_SET);
529 fread(cf->pd, sizeof(guint8), fd->cap_len, cf->fh);
531 add_packet_to_packet_list(fd, cf, cf->pd);
535 filter_packets(capture_file *cf)
537 if (cf->dfilter != NULL) {
539 * Compile the filter.
541 if (dfilter_compile(cf->dfilter, &cf->dfcode) != 0) {
542 simple_dialog(ESD_TYPE_WARN, NULL,
543 "Unable to parse filter string \"%s\".", cf->dfilter);
548 /* Freeze the packet list while we redo it, so we don't get any
549 screen updates while it happens. */
550 gtk_clist_freeze(GTK_CLIST(packet_list));
553 gtk_clist_clear(GTK_CLIST(packet_list));
556 * Iterate through the list of packets, calling a routine
557 * to run the filter on the packet, see if it matches, and
558 * put it in the display list if so.
565 g_list_foreach(cf->plist, filter_packets_cb, cf);
567 /* Unfreeze the packet list. */
568 gtk_clist_thaw(GTK_CLIST(packet_list));
572 change_time_formats_cb(gpointer data, gpointer user_data)
574 frame_data *fd = data;
575 capture_file *cf = user_data;
580 change_time_format_in_packet_list(fd, cf);
583 /* Scan through the packet list and change all columns that use the
584 "command-line-specified" time stamp format to use the current
585 value of that format. */
587 change_time_formats(capture_file *cf)
591 /* Freeze the packet list while we redo it, so we don't get any
592 screen updates while it happens. */
593 gtk_clist_freeze(GTK_CLIST(packet_list));
595 /* Zero out the column widths. */
599 * Iterate through the list of packets, calling a routine
600 * to run the filter on the packet, see if it matches, and
601 * put it in the display list if so.
608 g_list_foreach(cf->plist, change_time_formats_cb, cf);
610 /* Set the column widths of those columns that show the time in
611 "command-line-specified" format. */
612 for (i = 0; i < cf->cinfo.num_cols; i++) {
613 if (cf->cinfo.fmt_matx[i][COL_CLS_TIME]) {
614 gtk_clist_set_column_width(GTK_CLIST(packet_list), i,
615 cf->cinfo.col_width[i]);
619 /* Unfreeze the packet list. */
620 gtk_clist_thaw(GTK_CLIST(packet_list));
623 /* Initialize the maximum widths of the columns to the widths of their
626 init_col_widths(capture_file *cf)
630 /* XXX - this should use the column *title* font, not the font for
631 the items in the list.
633 Unfortunately, it's not clear how to get that font - it'd be
634 the font used for buttons; there doesn't seem to be a way to get
635 that from a clist, or to get one of the buttons in that clist from
636 the clist in order to get its font. */
637 for (i = 0; i < cf->cinfo.num_cols; i++)
638 cf->cinfo.col_width[i] = gdk_string_width(pl_style->font,
639 cf->cinfo.col_title[i]);
642 /* Set the widths of the columns to the maximum widths we found. */
644 set_col_widths(capture_file *cf)
648 for (i = 0; i < cf->cinfo.num_cols; i++) {
649 gtk_clist_set_column_width(GTK_CLIST(packet_list), i,
650 cf->cinfo.col_width[i]);
654 /* Tries to mv a file. If unsuccessful, tries to cp the file.
655 * Returns 0 on failure to do either, 1 on success of either
658 file_mv(char *from, char *to)
661 #define COPY_BUFFER_SIZE 8192
666 /* try a hard link */
667 retval = link(from, to);
672 retval = file_cp(from, to);
685 * Returns 0 on failure to do either, 1 on success of either
688 file_cp(char *from, char *to)
691 #define COPY_BUFFER_SIZE 8192
693 int from_fd, to_fd, nread, nwritten;
695 gint dialogue_button = ESD_BTN_OK;
697 buffer = g_malloc(COPY_BUFFER_SIZE);
699 from_fd = open(from, O_RDONLY);
701 simple_dialog(ESD_TYPE_WARN, &dialogue_button,
702 file_open_error_message(errno, TRUE), from);
706 to_fd = creat(to, 0644);
708 simple_dialog(ESD_TYPE_WARN, &dialogue_button,
709 file_open_error_message(errno, TRUE), to);
714 while( (nread = read(from_fd, buffer, COPY_BUFFER_SIZE)) > 0) {
715 nwritten = write(to_fd, buffer, nread);
716 if (nwritten < nread) {
718 simple_dialog(ESD_TYPE_WARN, &dialogue_button,
719 file_write_error_message(errno), to);
721 simple_dialog(ESD_TYPE_WARN, &dialogue_button,
722 "The file \"%s\" could not be saved: tried writing %d, wrote %d.\n",
723 to, nread, nwritten);
731 simple_dialog(ESD_TYPE_WARN, &dialogue_button,
732 file_read_error_message(errno), from);
744 file_open_error_message(int err, int for_writing)
747 static char errmsg_errno[1024+1];
751 case OPEN_CAP_FILE_NOT_REGULAR:
752 errmsg = "The file \"%s\" is invalid.";
755 case OPEN_CAP_FILE_UNKNOWN_FORMAT:
756 errmsg = "The file \"%s\" is not a capture file in a format Ethereal understands.";
761 errmsg = "The path to the file \"%s\" does not exist.";
763 errmsg = "The file \"%s\" does not exist.";
768 errmsg = "You do not have permission to create or write to the file \"%s\".";
770 errmsg = "You do not have permission to open the file \"%s\".";
774 sprintf(errmsg_errno, "The file \"%%s\" could not be opened: %s.", strerror(err));
775 errmsg = errmsg_errno;
782 file_read_error_message(int err)
784 static char errmsg_errno[1024+1];
786 sprintf(errmsg_errno, "An error occurred while reading from the file \"%%s\": %s.", strerror(err));
791 file_write_error_message(int err)
794 static char errmsg_errno[1024+1];
799 errmsg = "The file \"%s\" could not be saved because there is no space left on the file system.";
804 errmsg = "The file \"%s\" could not be saved because you are too close to, or over, your disk quota.";
809 sprintf(errmsg_errno, "An error occurred while writing to the file \"%%s\": %s.", strerror(err));
810 errmsg = errmsg_errno;