Add routines to Wiretap to allow a client of Wiretap to get:

a pointer to the "wtap_pkthdr" structure for an open capture
	file;

	a pointer to the "wtap_pseudo_header" union for an open capture
	file;

	a pointer to the packet buffer for an open capture file;

so that a program using "wtap_read()" in a loop can get at those items.

Keep, in a "capture_file" structure, an indicator of whether:

	no file is open;

	a file is open, and being read;

	a file is open, and is being read, but the user tried to quit
	out of reading the file (e.g., by doing "File/Quit");

	a file is open, and has been completely read.

Abort if we try to close a capture that's being read if the user hasn't
tried to quit out of the read.

Have "File/Quit" check if a file is being read; if so, just set the
state indicator to "user tried to quit out of it", so that the code
reading the file can do what's appropriate to clean up, rather than
closing the file out from under that code and causing crashes.

Have "read_cap_file()" read the capture file with a loop using
"wtap_read()", rather than by using "wtap_loop()"; have it check after
reading each packet whether the user tried to abort the read and, if so,
close the capture and return an indication that the read was aborted by
the user.  Otherwise, return an indication of whether the read
completely succeeded or failed in the middle (and, if it failed, return
the error code through a pointer).

Have "continue_tail_cap_file()" read the capture file with a loop using
"wtap_read()", rather than by using "wtap_loop()"; have it check after
reading each packet whether the user tried to abort the read and, if so,
quit the loop, and after the loop finishes (even if it read no packets),
return an indication that the read was aborted by the user if that
happened.  Otherwise, return an indication of whether the read
completely succeeded or failed in the middle (and, if it failed, return
the error code through a pointer).

Have "finish_tail_cap_file()" read the capture file with a loop using
"wtap_read()", rather than by using "wtap_loop()"; have it check after
reading each packet whether the user tried to abort the read and, if so,
quit the loop, and after the loop finishes (even if it read no packets),
close the capture and return an indication that the read was aborted by
the user if that happened.  Otherwise, return an indication of whether
the read completely succeeded or failed in the middle (and, if it
failed, return the error code through a pointer).

Have their callers check whether the read was aborted or not and, if it
was, bail out in the appropriate fashion (exit if it's reading a file
specified by "-r" on the command line; exit the main loop if it's
reading a file specified with File->Open; kill the capture child if it's
"continue_tail_cap_file()"; exit the main loop if it's
"finish_tail_cap_file()".

svn path=/trunk/; revision=2095
This commit is contained in:
Guy Harris 2000-06-27 07:13:42 +00:00
parent 5af6a8d416
commit 7843ac6d0e
7 changed files with 299 additions and 73 deletions

View File

@ -1,7 +1,7 @@
/* capture.c
* Routines for packet capture windows
*
* $Id: capture.c,v 1.109 2000/06/27 04:35:42 guy Exp $
* $Id: capture.c,v 1.110 2000/06/27 07:13:12 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@ -113,6 +113,7 @@ static int sync_pipe[2]; /* used to sync father */
enum PIPES { READ, WRITE }; /* Constants 0 and 1 for READ and WRITE */
int quit_after_cap; /* Makes a "capture only mode". Implies -k */
gboolean capture_child; /* if this is the child for "-S" */
static int fork_child; /* In parent, process ID of child */
static guint cap_input_id;
#ifdef _WIN32
@ -182,7 +183,6 @@ do_capture(char *capfile_name)
cfile.save_file = capfile_name;
if (sync_mode) { /* do the capture in a child process */
int fork_child;
char ssnap[24];
char scount[24]; /* need a constant for len of numbers */
char save_file_fd[24];
@ -415,7 +415,21 @@ do_capture(char *capfile_name)
if ((err = open_cap_file(cfile.save_file, is_tempfile, &cfile)) == 0) {
/* Set the read filter to NULL. */
cfile.rfcode = NULL;
err = read_cap_file(&cfile);
switch (read_cap_file(&cfile, &err)) {
case READ_SUCCESS:
case READ_ERROR:
/* Just because we got an error, that doesn't mean we were unable
to read any of the file; we handle what we could get from the
file. */
break;
case READ_ABORTED:
/* Exit by leaving the main loop, so that any quit functions
we registered get called. */
gtk_main_quit();
return;
}
}
}
/* We're not doing a capture any more, so we don't have a save
@ -601,7 +615,21 @@ cap_file_input_cb(gpointer data, gint source, GdkInputCondition condition)
/* Read what remains of the capture file, and finish the capture.
XXX - do something if this fails? */
err = finish_tail_cap_file(cf);
switch (finish_tail_cap_file(cf, &err)) {
case READ_SUCCESS:
case READ_ERROR:
/* Just because we got an error, that doesn't mean we were unable
to read any of the file; we handle what we could get from the
file. */
break;
case READ_ABORTED:
/* Exit by leaving the main loop, so that any quit functions
we registered get called. */
gtk_main_quit();
return;
}
/* We're not doing a capture any more, so we don't have a save
file. */
@ -634,7 +662,27 @@ cap_file_input_cb(gpointer data, gint source, GdkInputCondition condition)
/* Read from the capture file the number of records the child told us
it added.
XXX - do something if this fails? */
err = continue_tail_cap_file(cf, to_read);
switch (continue_tail_cap_file(cf, to_read, &err)) {
case READ_SUCCESS:
case READ_ERROR:
/* Just because we got an error, that doesn't mean we were unable
to read any of the file; we handle what we could get from the
file.
XXX - abort on a read error? */
break;
case READ_ABORTED:
/* Kill the child capture process; the user wants to exit, and we
shouldn't just leave it running. */
#ifdef _WIN32
/* XXX - kill it. */
#else
kill(fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */
#endif
break;
}
/* restore pipe handler */
#ifdef _WIN32
@ -785,6 +833,12 @@ capture(void)
goto error;
}
/* XXX - capture SIGTERM and close the capture, in case we're on a
Linux 2.0[.x] system and you have to explicitly close the capture
stream in order to turn promiscuous mode off? We need to do that
in other places as well - and I don't think that works all the
time in any case, due to libpcap bugs. */
if (capture_child) {
/* Well, we should be able to start capturing.

146
file.c
View File

@ -1,7 +1,7 @@
/* file.c
* File I/O routines
*
* $Id: file.c,v 1.190 2000/06/27 04:35:44 guy Exp $
* $Id: file.c,v 1.191 2000/06/27 07:13:13 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@ -93,8 +93,7 @@ gboolean auto_scroll_live = FALSE;
static guint32 firstsec, firstusec;
static guint32 prevsec, prevusec;
static void wtap_dispatch_cb(u_char *, const struct wtap_pkthdr *, int,
union wtap_pseudo_header *, const u_char *);
static void read_packet(capture_file *cf, int offset, const u_char *buf);
static void set_selected_row(int row);
@ -143,6 +142,9 @@ open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
/* Initialize protocol-specific variables */
init_all_protocols();
/* We're about to start reading the file. */
cf->state = FILE_READ_IN_PROGRESS;
cf->wth = wth;
cf->filed = fd;
cf->f_len = cf_stat.st_size;
@ -188,6 +190,9 @@ fail:
void
close_cap_file(capture_file *cf, void *w)
{
/* Die if we're in the middle of reading a file. */
g_assert(cf->state != FILE_READ_IN_PROGRESS);
/* Destroy all popup packet windows, as they refer to packets in the
capture file we're closing. */
destroy_packet_wins();
@ -240,6 +245,9 @@ close_cap_file(capture_file *cf, void *w)
set_menus_for_captured_packets(FALSE);
set_menus_for_selected_packet(FALSE);
set_menus_for_capture_in_progress(FALSE);
/* We have no file open. */
cf->state = FILE_CLOSED;
}
/* Set the file name in the status line, in the name for the main window,
@ -277,16 +285,15 @@ set_display_filename(capture_file *cf)
g_free(win_name);
}
int
read_cap_file(capture_file *cf)
read_status_t
read_cap_file(capture_file *cf, int *err)
{
gchar *name_ptr, *load_msg, *load_fmt = " Loading: %s...";
int success;
int err;
size_t msg_len;
char *errmsg;
char errmsg_errno[1024+1];
gchar err_str[2048+1];
int data_offset;
name_ptr = get_basename(cf->filename);
@ -308,17 +315,30 @@ read_cap_file(capture_file *cf)
#endif
freeze_clist(cf);
success = wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf, &err);
while ((data_offset = wtap_read(cf->wth, err)) > 0) {
if (cf->state == FILE_READ_ABORTED) {
/* Well, the user decided to abort the read. Close the capture
file, and return READ_ABORTED so our caller can do whatever is
appropriate when that happens. */
close_cap_file(cf, info_bar);
return (READ_ABORTED);
}
read_packet(cf, data_offset, wtap_buf_ptr(cf->wth));
}
/* We're done reading sequentially through the file. */
cf->state = FILE_READ_DONE;
/* Close the sequential I/O side, to free up memory it requires. */
wtap_sequential_close(cf->wth);
/* Set the file encapsulation type now; we don't know what it is until
we've looked at all the packets, as we don't know until then whether
there's more than one type (and thus whether it's
WTAP_ENCAP_PER_PACKET). */
/* We're done reading sequentially through the file; close the
sequential I/O side, to free up memory it requires. */
wtap_sequential_close(cf->wth);
cf->lnk_t = wtap_file_encap(cf->wth);
cf->current_frame = cf->first_displayed;
thaw_clist(cf);
@ -341,11 +361,11 @@ read_cap_file(capture_file *cf)
if (cf->first_displayed != NULL)
gtk_signal_emit_by_name(GTK_OBJECT(packet_list), "select_row", 0);
if (!success) {
if (data_offset < 0) {
/* Put up a message box noting that the read failed somewhere along
the line. Don't throw out the stuff we managed to read, though,
if any. */
switch (err) {
switch (*err) {
case WTAP_ERR_UNSUPPORTED_ENCAP:
errmsg = "The capture file is for a network type that Ethereal doesn't support.";
@ -367,15 +387,15 @@ read_cap_file(capture_file *cf)
default:
sprintf(errmsg_errno, "An error occurred while reading the"
" capture file: %s.", wtap_strerror(err));
" capture file: %s.", wtap_strerror(*err));
errmsg = errmsg_errno;
break;
}
snprintf(err_str, sizeof err_str, errmsg);
simple_dialog(ESD_TYPE_WARN, NULL, err_str);
return (err);
return (READ_ERROR);
} else
return (0);
return (READ_SUCCESS);
}
#ifdef HAVE_LIBPCAP
@ -412,14 +432,23 @@ start_tail_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
return err;
}
int
continue_tail_cap_file(capture_file *cf, int to_read)
read_status_t
continue_tail_cap_file(capture_file *cf, int to_read, int *err)
{
int err;
int data_offset = 0;
gtk_clist_freeze(GTK_CLIST(packet_list));
wtap_loop(cf->wth, to_read, wtap_dispatch_cb, (u_char *) cf, &err);
while (to_read != 0 && (data_offset = wtap_read(cf->wth, err)) > 0) {
if (cf->state == FILE_READ_ABORTED) {
/* Well, the user decided to exit Ethereal. Break out of the
loop, and let the code below (which is called even if there
aren't any packets left to read) exit. */
break;
}
read_packet(cf, data_offset, wtap_buf_ptr(cf->wth));
to_read--;
}
gtk_clist_thaw(GTK_CLIST(packet_list));
@ -428,17 +457,48 @@ continue_tail_cap_file(capture_file *cf, int to_read)
if (auto_scroll_live && cf->plist_end != NULL)
gtk_clist_moveto(GTK_CLIST(packet_list),
GTK_CLIST(packet_list)->rows - 1, -1, 1.0, 1.0);
return err;
if (cf->state == FILE_READ_ABORTED) {
/* Well, the user decided to exit Ethereal. Return READ_ABORTED
so that our caller can kill off the capture child process;
this will cause an EOF on the pipe from the child, so
"finish_tail_cap_file()" will be called, and it will clean up
and exit. */
return READ_ABORTED;
} else if (data_offset < 0) {
/* We got an error reading the capture file.
XXX - pop up a dialog box? */
return (READ_ERROR);
} else
return (READ_SUCCESS);
}
int
finish_tail_cap_file(capture_file *cf)
read_status_t
finish_tail_cap_file(capture_file *cf, int *err)
{
int err;
int data_offset;
gtk_clist_freeze(GTK_CLIST(packet_list));
wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf, &err);
while ((data_offset = wtap_read(cf->wth, err)) > 0) {
if (cf->state == FILE_READ_ABORTED) {
/* Well, the user decided to abort the read. Break out of the
loop, and let the code below (which is called even if there
aren't any packets left to read) exit. */
break;
}
read_packet(cf, data_offset, wtap_buf_ptr(cf->wth));
}
if (cf->state == FILE_READ_ABORTED) {
/* Well, the user decided to abort the read. We're only called
when the child capture process closes the pipe to us (meaning
it's probably exited), so we can just close the capture
file; we return READ_ABORTED so our caller can do whatever
is appropriate when that happens. */
close_cap_file(cf, info_bar);
return READ_ABORTED;
}
thaw_clist(cf);
if (auto_scroll_live && cf->plist_end != NULL)
@ -447,6 +507,9 @@ finish_tail_cap_file(capture_file *cf)
gtk_clist_moveto(GTK_CLIST(packet_list),
GTK_CLIST(packet_list)->rows - 1, -1, 1.0, 1.0);
/* We're done reading sequentially through the file. */
cf->state = FILE_READ_DONE;
/* We're done reading sequentially through the file; close the
sequential I/O side, to free up memory it requires. */
wtap_sequential_close(cf->wth);
@ -471,7 +534,12 @@ finish_tail_cap_file(capture_file *cf)
set_menus_for_capture_file(TRUE);
set_menus_for_unsaved_capture_file(!cf->user_saved);
return err;
if (data_offset < 0) {
/* We got an error reading the capture file.
XXX - pop up a dialog box? */
return (READ_ERROR);
} else
return (READ_SUCCESS);
}
#endif /* HAVE_LIBPCAP */
@ -634,11 +702,11 @@ add_packet_to_packet_list(frame_data *fdata, capture_file *cf,
}
static void
wtap_dispatch_cb(u_char *user, const struct wtap_pkthdr *phdr, int offset,
union wtap_pseudo_header *pseudo_header, const u_char *buf)
read_packet(capture_file *cf, int offset, const u_char *buf)
{
const struct wtap_pkthdr *phdr = wtap_phdr(cf->wth);
union wtap_pseudo_header *pseudo_header = wtap_pseudoheader(cf->wth);
frame_data *fdata;
capture_file *cf = (capture_file *) user;
int passed;
proto_tree *protocol_tree;
frame_data *plist_end;
@ -1600,7 +1668,21 @@ done:
if ((err = open_cap_file(fname, FALSE, cf)) == 0) {
/* XXX - report errors if this fails? */
err = read_cap_file(cf);
switch (read_cap_file(cf, &err)) {
case READ_SUCCESS:
case READ_ERROR:
/* Just because we got an error, that doesn't mean we were unable
to read any of the file; we handle what we could get from the
file. */
break;
case READ_ABORTED:
/* Exit by leaving the main loop, so that any quit functions
we registered get called. */
gtk_main_quit();
return 0;
}
set_menus_for_unsaved_capture_file(FALSE);
}
}

25
file.h
View File

@ -1,7 +1,7 @@
/* file.h
* Definitions for file structures and routines
*
* $Id: file.h,v 1.68 2000/05/19 23:06:07 gram Exp $
* $Id: file.h,v 1.69 2000/06/27 07:13:14 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@ -69,7 +69,16 @@
typedef struct bpf_program bpf_prog;
/* Current state of file. */
typedef enum {
FILE_CLOSED, /* No file open */
FILE_READ_IN_PROGRESS, /* Reading a file we've opened */
FILE_READ_ABORTED, /* Read aborted by user */
FILE_READ_DONE /* Read completed */
} file_state;
typedef struct _capture_file {
file_state state; /* Current state of capture file */
int filed; /* File descriptor of capture file */
gchar *filename; /* Name of capture file */
gboolean is_tempfile; /* Is capture file a temporary file? */
@ -113,12 +122,20 @@ typedef struct _capture_file {
FILE *print_fh; /* File we're printing to */
} capture_file;
/* Return values from "read_cap_file()", "continue_tail_cap_file()",
and "finish_tail_cap_file()". */
typedef enum {
READ_SUCCESS, /* read succeeded */
READ_ERROR, /* read got an error */
READ_ABORTED /* read aborted by user */
} read_status_t;
int open_cap_file(char *, gboolean, capture_file *);
void close_cap_file(capture_file *, void *);
int read_cap_file(capture_file *);
read_status_t read_cap_file(capture_file *, int *);
int start_tail_cap_file(char *, gboolean, capture_file *);
int continue_tail_cap_file(capture_file *, int);
int finish_tail_cap_file(capture_file *);
read_status_t continue_tail_cap_file(capture_file *, int, int *);
read_status_t finish_tail_cap_file(capture_file *, int *);
/* size_t read_frame_header(capture_file *); */
int save_cap_file(char *, capture_file *, gboolean, guint);

View File

@ -1,7 +1,7 @@
/* file_dlg.c
* Dialog boxes for handling files
*
* $Id: file_dlg.c,v 1.25 2000/06/27 04:36:00 guy Exp $
* $Id: file_dlg.c,v 1.26 2000/06/27 07:13:25 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@ -221,7 +221,22 @@ file_open_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
gtk_widget_hide(GTK_WIDGET (fs));
gtk_widget_destroy(GTK_WIDGET (fs));
err = read_cap_file(&cfile);
switch (read_cap_file(&cfile, &err)) {
case READ_SUCCESS:
case READ_ERROR:
/* Just because we got an error, that doesn't mean we were unable
to read any of the file; we handle what we could get from the
file. */
break;
case READ_ABORTED:
/* Exit by leaving the main loop, so that any quit functions
we registered get called. */
gtk_main_quit();
return;
}
/* Save the name of the containing directory specified in the path name,
if any; we can write over cf_name, which is a good thing, given that
"get_dirname()" does write over its argument. */
@ -484,6 +499,7 @@ file_reload_cmd_cb(GtkWidget *w, gpointer data) {
GtkWidget *filter_te;
gchar *filename;
gboolean is_tempfile;
int err;
filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
@ -504,9 +520,23 @@ file_reload_cmd_cb(GtkWidget *w, gpointer data) {
filename = strdup(cfile.filename);
is_tempfile = cfile.is_tempfile;
cfile.is_tempfile = FALSE;
if (open_cap_file(filename, is_tempfile, &cfile) == 0)
read_cap_file(&cfile);
else {
if (open_cap_file(filename, is_tempfile, &cfile) == 0) {
switch (read_cap_file(&cfile, &err)) {
case READ_SUCCESS:
case READ_ERROR:
/* Just because we got an error, that doesn't mean we were unable
to read any of the file; we handle what we could get from the
file. */
break;
case READ_ABORTED:
/* Exit by leaving the main loop, so that any quit functions
we registered get called. */
gtk_main_quit();
return;
}
} else {
/* The open failed, so "cfile.is_tempfile" wasn't set to "is_tempfile".
Instead, the file was left open, so we should restore "cfile.is_tempfile"
ourselves.

View File

@ -1,6 +1,6 @@
/* main.c
*
* $Id: main.c,v 1.124 2000/06/27 04:40:15 guy Exp $
* $Id: main.c,v 1.125 2000/06/27 07:13:25 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@zing.org>
@ -1097,30 +1097,42 @@ file_quit_cmd_cb (GtkWidget *widget, gpointer data)
"main_window_delete_event_cb()" should return its
return value. */
/* Close any capture file we have open; on some OSes, you can't
unlink a temporary capture file if you have it open.
"close_cap_file()" will unlink it after closing it if
it's a temporary file.
/* Are we in the middle of reading a capture? */
if (cfile.state == FILE_READ_IN_PROGRESS) {
/* Yes, so we can't just close the file and quit, as
that may yank the rug out from under the read in
progress; instead, just set the state to
"FILE_READ_ABORTED" and return - the code doing the read
will check for that and, if it sees that, will clean
up and quit. */
cfile.state = FILE_READ_ABORTED;
} else {
/* Close any capture file we have open; on some OSes, you
can't unlink a temporary capture file if you have it
open.
"close_cap_file()" will unlink it after closing it if
it's a temporary file.
We do this here, rather than after the main loop returns,
as, after the main loop returns, the main window may have
been destroyed (if this is called due to a "destroy"
even on the main window rather than due to the user
selecting a menu item), and there may be a crash
or other problem when "close_cap_file()" tries to
clean up stuff in the main window.
We do this here, rather than after the main loop returns,
as, after the main loop returns, the main window may have
been destroyed (if this is called due to a "destroy"
even on the main window rather than due to the user
selecting a menu item), and there may be a crash
or other problem when "close_cap_file()" tries to
clean up stuff in the main window.
XXX - is there a better place to put this?
Or should we have a routine that *just* closes the
capture file, and doesn't do anything with the UI,
which we'd call here, and another routine that
calls that routine and also cleans up the UI, which
we'd call elsewhere? */
close_cap_file(&cfile, info_bar);
XXX - is there a better place to put this?
Or should we have a routine that *just* closes the
capture file, and doesn't do anything with the UI,
which we'd call here, and another routine that
calls that routine and also cleans up the UI, which
we'd call elsewhere? */
close_cap_file(&cfile, info_bar);
/* Exit by leaving the main loop, so that any quit functions
we registered get called. */
gtk_main_quit();
/* Exit by leaving the main loop, so that any quit functions
we registered get called. */
gtk_main_quit();
}
}
static void
@ -1567,7 +1579,20 @@ main(int argc, char *argv[])
capture file, and thus destroyed any previous read filter
attached to "cf". */
cfile.rfcode = rfcode;
err = read_cap_file(&cfile);
switch (read_cap_file(&cfile, &err)) {
case READ_SUCCESS:
case READ_ERROR:
/* Just because we got an error, that doesn't mean we were unable
to read any of the file; we handle what we could get from the
file. */
break;
case READ_ABORTED:
/* Exit now. */
gtk_exit(0);
break;
}
/* Save the name of the containing directory specified in the
path name, if any; we can write over cf_name, which is a
good thing, given that "get_dirname()" does write over its

View File

@ -1,6 +1,6 @@
/* wtap.c
*
* $Id: wtap.c,v 1.44 2000/05/25 09:00:23 guy Exp $
* $Id: wtap.c,v 1.45 2000/06/27 07:13:42 guy Exp $
*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
@ -228,7 +228,22 @@ void wtap_close(wtap *wth)
int wtap_read(wtap *wth, int *err)
{
return wth -> subtype_read(wth, err);
return wth->subtype_read(wth, err);
}
struct wtap_pkthdr *wtap_phdr(wtap *wth)
{
return &wth->phdr;
}
union wtap_pseudo_header *wtap_pseudoheader(wtap *wth)
{
return &wth->pseudo_header;
}
guint8 *wtap_buf_ptr(wtap *wth)
{
return buffer_start_ptr(wth->frame_buffer);
}
int wtap_loop(wtap *wth, int count, wtap_handler callback, u_char* user,
@ -239,7 +254,7 @@ int wtap_loop(wtap *wth, int count, wtap_handler callback, u_char* user,
/* Start be clearing error flag */
*err = 0;
while ((data_offset = wth->subtype_read(wth, err)) > 0) {
while ((data_offset = wtap_read(wth, err)) > 0) {
callback(user, &wth->phdr, data_offset,
&wth->pseudo_header, buffer_start_ptr(wth->frame_buffer));
if (count > 0 && ++loop >= count)

View File

@ -1,6 +1,6 @@
/* wtap.h
*
* $Id: wtap.h,v 1.73 2000/06/24 05:32:48 guy Exp $
* $Id: wtap.h,v 1.74 2000/06/27 07:13:42 guy Exp $
*
* Wiretap Library
* Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
@ -271,6 +271,9 @@ typedef struct wtap_dumper wtap_dumper;
struct wtap* wtap_open_offline(const char *filename, int *err, gboolean do_random);
int wtap_loop(wtap *wth, int, wtap_handler, u_char*, int*);
int wtap_read(wtap *wth, int *err);
struct wtap_pkthdr *wtap_phdr(wtap *wth);
union wtap_pseudo_header *wtap_pseudoheader(wtap *wth);
guint8 *wtap_buf_ptr(wtap *wth);
FILE* wtap_file(wtap *wth);
int wtap_fd(wtap *wth);