Add "tethereal", a tty-oriented derivative of Ethereal that works like
[obnox/wireshark/wip.git] / tethereal.c
1 /* tethereal.c
2  *
3  * $Id: tethereal.c,v 1.1 2000/01/14 06:45:52 guy Exp $
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@zing.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * Text-mode variant, by Gilbert Ramirez <gram@verdict.uthscsa.edu>.
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
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include <signal.h>
44
45 #ifdef NEED_SNPRINTF_H
46 # ifdef HAVE_STDARG_H
47 #  include <stdarg.h>
48 # else
49 #  include <varargs.h>
50 # endif
51 # include "snprintf.h"
52 #endif
53
54 #if defined(HAVE_UCD_SNMP_SNMP_H)
55 #ifdef HAVE_UCD_SNMP_VERSION_H
56 #include <ucd-snmp/version.h>
57 #endif /* HAVE_UCD_SNMP_VERSION_H */
58 #elif defined(HAVE_SNMP_SNMP_H)
59 #ifdef HAVE_SNMP_VERSION_H
60 #include <snmp/version.h>
61 #endif /* HAVE_SNMP_VERSION_H */
62 #endif /* SNMP */
63
64 #ifdef NEED_STRERROR_H
65 #include "strerror.h"
66 #endif
67
68 #include "globals.h"
69 #include "timestamp.h"
70 #include "packet.h"
71 #include "file.h"
72 #include "prefs.h"
73 #include "column.h"
74 #include "print.h"
75 #include "resolv.h"
76 #include "follow.h"
77 #include "util.h"
78 #include "ui_util.h"
79 #include "conversation.h"
80
81 static guint32 firstsec, firstusec;
82 static guint32 prevsec, prevusec;
83 static gchar   comp_info_str[256];
84 static gboolean verbose;
85 static int     packet_count;
86
87 #ifdef HAVE_LIBPCAP
88 typedef struct _loop_data {
89   gint           linktype;
90   pcap_t        *pch;
91   wtap_dumper   *pdh;
92 } loop_data;
93
94 static loop_data ld;
95
96 static int capture(void);
97 static void capture_pcap_cb(u_char *, const struct pcap_pkthdr *,
98   const u_char *);
99 static void capture_cleanup(int);
100 #endif
101
102 static int load_cap_file(capture_file *);
103 static void wtap_dispatch_cb(u_char *, const struct wtap_pkthdr *, int,
104     const u_char *);
105 static gchar *col_info(frame_data *, gint);
106
107 packet_info  pi;
108 capture_file cf;
109 FILE        *data_out_file = NULL;
110 guint        main_ctx, file_ctx;
111 ts_type timestamp_type = RELATIVE;
112
113 /* call initialization routines at program startup time */
114 static void
115 ethereal_proto_init(void) {
116   init_dissect_rpc();
117   proto_init();
118   init_dissect_udp();
119   dfilter_init();
120 }
121
122 static void
123 ethereal_proto_cleanup(void) {
124         proto_cleanup();
125         dfilter_cleanup();
126 }
127
128 static void 
129 print_usage(void) {
130
131   fprintf(stderr, "This is GNU %s %s, compiled with %s\n", PACKAGE,
132           VERSION, comp_info_str);
133   fprintf(stderr, "t%s [-v] [-h] [-n]\n",
134           PACKAGE);
135   fprintf(stderr, "         [-r infile] [-t <time stamp format>]\n");
136 }
137
138 int
139 main(int argc, char *argv[])
140 {
141   int                  opt, i;
142   extern char         *optarg;
143   gboolean             arg_error = FALSE;
144 #ifdef HAVE_LIBPCAP
145   extern char          pcap_version[];
146 #endif
147   char                *pf_path;
148   int                  err;
149 #ifndef HAVE_LIBPCAP
150   gboolean             capture_option_specified = FALSE;
151 #endif
152   gchar               *cf_name = NULL, *rfilter = NULL;
153   dfilter             *rfcode = NULL;
154   e_prefs             *prefs;
155
156   /* If invoked with the "-G" flag, we dump out a glossary of
157      display filter symbols.
158
159      We do this here to mirror what happens in the GTK+ version, although
160      it's not necessary here. */
161   if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
162     ethereal_proto_init();
163     proto_registrar_dump();
164     exit(0);
165   }
166
167   prefs = read_prefs(&pf_path);
168   if (pf_path != NULL) {
169     fprintf(stderr, "Can't open preferences file \"%s\": %s.\n", pf_path,
170         strerror(errno));
171   }
172     
173   /* Initialize the capture file struct */
174   cf.plist              = NULL;
175   cf.plist_end          = NULL;
176   cf.wth                = NULL;
177   cf.fh                 = NULL;
178   cf.filename           = NULL;
179   cf.user_saved         = FALSE;
180   cf.is_tempfile        = FALSE;
181   cf.rfcode             = NULL;
182   cf.dfilter            = NULL;
183   cf.dfcode             = NULL;
184 #ifdef HAVE_LIBPCAP
185   cf.cfilter            = NULL;
186 #endif
187   cf.iface              = NULL;
188   cf.save_file          = NULL;
189   cf.save_file_fd       = -1;
190   cf.snap               = WTAP_MAX_PACKET_SIZE;
191   cf.count              = 0;
192   cf.cinfo.num_cols     = prefs->num_cols;
193   cf.cinfo.col_fmt      = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
194   cf.cinfo.fmt_matx     = (gboolean **) g_malloc(sizeof(gboolean *) * cf.cinfo.num_cols);
195   cf.cinfo.col_width    = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
196   cf.cinfo.col_title    = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
197   cf.cinfo.col_data     = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
198
199   /* Assemble the compile-time options */
200   snprintf(comp_info_str, 256,
201 #ifdef GTK_MAJOR_VERSION
202     "GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
203     GTK_MICRO_VERSION,
204 #else
205     "GTK+ (version unknown), %s%s, %s%s, %s%s",
206 #endif
207
208 #ifdef HAVE_LIBPCAP
209    "with libpcap ", pcap_version,
210 #else
211    "without libpcap", "",
212 #endif
213
214 #ifdef HAVE_LIBZ
215 #ifdef ZLIB_VERSION
216    "with libz ", ZLIB_VERSION,
217 #else /* ZLIB_VERSION */
218    "with libz ", "(version unknown)",
219 #endif /* ZLIB_VERSION */
220 #else /* HAVE_LIBZ */
221    "without libz", "",
222 #endif /* HAVE_LIBZ */
223
224 /* Oh, this is pretty */
225 #if defined(HAVE_UCD_SNMP_SNMP_H)
226 #ifdef HAVE_UCD_SNMP_VERSION_H
227    "with UCD SNMP ", VersionInfo
228 #else /* HAVE_UCD_SNMP_VERSION_H */
229    "with UCD SNMP ", "(version unknown)"
230 #endif /* HAVE_UCD_SNMP_VERSION_H */
231 #elif defined(HAVE_SNMP_SNMP_H)
232 #ifdef HAVE_SNMP_VERSION_H
233    "with CMU SNMP ", snmp_Version()
234 #else /* HAVE_SNMP_VERSION_H */
235    "with CMU SNMP ", "(version unknown)"
236 #endif /* HAVE_SNMP_VERSION_H */
237 #else /* no SNMP */
238    "without SNMP", ""
239 #endif
240    );
241     
242   /* Now get our args */
243   while ((opt = getopt(argc, argv, "c:f:hi:nr:R:s:t:vw:V")) != EOF) {
244     switch (opt) {
245       case 'c':        /* Capture xxx packets */
246 #ifdef HAVE_LIBPCAP
247         packet_count = atoi(optarg);
248 #else
249         capture_option_specified = TRUE;
250         arg_error = TRUE;
251 #endif
252         break;
253       case 'f':
254 #ifdef HAVE_LIBPCAP
255         cf.cfilter = g_strdup(optarg);
256 #else
257         capture_option_specified = TRUE;
258         arg_error = TRUE;
259 #endif
260         break;
261       case 'h':        /* Print help and exit */
262         print_usage();
263         exit(0);
264         break;
265       case 'i':        /* Use interface xxx */
266 #ifdef HAVE_LIBPCAP
267         cf.iface = g_strdup(optarg);
268 #else
269         capture_option_specified = TRUE;
270         arg_error = TRUE;
271 #endif
272         break;
273       case 'n':        /* No name resolution */
274         g_resolving_actif = 0;
275         break;
276       case 'r':        /* Read capture file xxx */
277         cf_name = g_strdup(optarg);
278         break;
279       case 'R':        /* Read file filter */
280         rfilter = optarg;
281         break;
282       case 's':        /* Set the snapshot (capture) length */
283 #ifdef HAVE_LIBPCAP
284         cf.snap = atoi(optarg);
285 #else
286         capture_option_specified = TRUE;
287         arg_error = TRUE;
288 #endif
289         break;
290       case 't':        /* Time stamp type */
291         if (strcmp(optarg, "r") == 0)
292           timestamp_type = RELATIVE;
293         else if (strcmp(optarg, "a") == 0)
294           timestamp_type = ABSOLUTE;
295         else if (strcmp(optarg, "d") == 0)
296           timestamp_type = DELTA;
297         else {
298           fprintf(stderr, "tethereal: Invalid time stamp type \"%s\"\n",
299             optarg);
300           fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
301           fprintf(stderr, "or \"d\" for delta.\n");
302           exit(1);
303         }
304         break;
305       case 'v':        /* Show version and exit */
306         printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
307         exit(0);
308         break;
309       case 'w':        /* Write to capture file xxx */
310 #ifdef HAVE_LIBPCAP
311         cf.save_file = g_strdup(optarg);
312 #else
313         capture_option_specified = TRUE;
314         arg_error = TRUE;
315 #endif
316         break;
317       case 'V':        /* Verbose */
318         verbose = TRUE;
319         break;
320     }
321   }
322   
323 #ifndef HAVE_LIBPCAP
324   if (capture_option_specified)
325     fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
326 #endif
327   if (arg_error)
328     print_usage();
329
330   /* Build the column format array */  
331   for (i = 0; i < cf.cinfo.num_cols; i++) {
332     cf.cinfo.col_fmt[i] = get_column_format(i);
333     cf.cinfo.col_title[i] = g_strdup(get_column_title(i));
334     cf.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
335       NUM_COL_FMTS);
336     get_column_format_matches(cf.cinfo.fmt_matx[i], cf.cinfo.col_fmt[i]);
337     if (cf.cinfo.col_fmt[i] == COL_INFO)
338       cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
339     else
340       cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
341   }
342
343   if (cf.snap < 1)
344     cf.snap = WTAP_MAX_PACKET_SIZE;
345   else if (cf.snap < MIN_PACKET_SIZE)
346     cf.snap = MIN_PACKET_SIZE;
347   
348   ethereal_proto_init();   /* Init anything that needs initializing */
349
350   if (cf_name) {
351     if (rfilter != NULL) {
352       if (dfilter_compile(rfilter, &rfcode) != 0) {
353         fprintf(stderr, "tethereal: %s\n", dfilter_error_msg);
354         ethereal_proto_cleanup();
355         exit(2);
356       }
357     }
358     err = open_cap_file(cf_name, FALSE, &cf);
359     if (err != 0) {
360       ethereal_proto_cleanup();
361       exit(2);
362     }
363     cf.rfcode = rfcode;
364     err = load_cap_file(&cf);
365     if (err != 0) {
366       ethereal_proto_cleanup();
367       exit(2);
368     }
369     cf_name[0] = '\0';
370 #ifdef HAVE_LIBPCAP
371   } else {
372     capture();
373 #endif
374   }
375
376   ethereal_proto_cleanup();
377
378   exit(0);
379 }
380
381 #ifdef HAVE_LIBPCAP
382 /* Do the low-level work of a capture.
383    Returns TRUE if it succeeds, FALSE otherwise. */
384 static int
385 capture(void)
386 {
387   gchar       err_str[PCAP_ERRBUF_SIZE];
388   bpf_u_int32 netnum, netmask;
389   void        (*oldhandler)(int);
390   int         err, inpkts;
391   char        errmsg[1024+1];
392
393   ld.linktype       = WTAP_ENCAP_UNKNOWN;
394   ld.pdh            = NULL;
395
396   /* Open the network interface to capture from it. */
397   ld.pch = pcap_open_live(cf.iface, cf.snap, 1, 1000, err_str);
398
399   if (ld.pch == NULL) {
400     /* Well, we couldn't start the capture.
401        If this is a child process that does the capturing in sync
402        mode or fork mode, it shouldn't do any UI stuff until we pop up the
403        capture-progress window, and, since we couldn't start the
404        capture, we haven't popped it up. */
405     snprintf(errmsg, sizeof errmsg,
406       "The capture session could not be initiated (%s).\n"
407       "Please check to make sure you have sufficient permissions, and that\n"
408       "you have the proper interface specified.", err_str);
409     goto error;
410   }
411
412   if (cf.cfilter) {
413     /* A capture filter was specified; set it up. */
414     if (pcap_lookupnet (cf.iface, &netnum, &netmask, err_str) < 0) {
415       snprintf(errmsg, sizeof errmsg,
416         "Can't use filter:  Couldn't obtain netmask info (%s).", err_str);
417       goto error;
418     }
419     if (pcap_compile(ld.pch, &cf.fcode, cf.cfilter, 1, netmask) < 0) {
420       snprintf(errmsg, sizeof errmsg, "Unable to parse filter string (%s).",
421         pcap_geterr(ld.pch));
422       goto error;
423     }
424     if (pcap_setfilter(ld.pch, &cf.fcode) < 0) {
425       snprintf(errmsg, sizeof errmsg, "Can't install filter (%s).",
426         pcap_geterr(ld.pch));
427       goto error;
428     }
429   }
430
431   ld.linktype = wtap_pcap_encap_to_wtap_encap(pcap_datalink(ld.pch));
432   if (cf.save_file != NULL) {
433     /* Set up to write to the capture file. */
434     if (ld.linktype == WTAP_ENCAP_UNKNOWN) {
435       strcpy(errmsg, "The network you're capturing from is of a type"
436                " that Ethereal doesn't support.");
437       goto error;
438     }
439     ld.pdh = wtap_dump_open(cf.save_file, WTAP_FILE_PCAP,
440                 ld.linktype, pcap_snapshot(ld.pch), &err);
441
442     if (ld.pdh == NULL) {
443       /* We couldn't set up to write to the capture file. */
444       switch (err) {
445
446       case WTAP_ERR_CANT_OPEN:
447         strcpy(errmsg, "The file to which the capture would be saved"
448                  " couldn't be created for some unknown reason.");
449         break;
450
451       case WTAP_ERR_SHORT_WRITE:
452         strcpy(errmsg, "A full header couldn't be written to the file"
453                  " to which the capture would be saved.");
454         break;
455
456       default:
457         if (err < 0) {
458           sprintf(errmsg, "The file to which the capture would be"
459                        " saved (\"%s\") could not be opened: Error %d.",
460                         cf.save_file, err);
461         } else {
462           sprintf(errmsg, "The file to which the capture would be"
463                        " saved (\"%s\") could not be opened: %s.",
464                         cf.save_file, strerror(err));
465         }
466         break;
467       }
468       goto error;
469     }
470   }
471
472   /* Catch SIGINT and SIGTERM and, if we get either of them, clean up
473      and exit.
474      XXX - deal with signal semantics on various platforms.  Or just
475      use "sigaction()" and be done with it? */
476   signal(SIGTERM, capture_cleanup);
477   signal(SIGINT, capture_cleanup);
478   if ((oldhandler = signal(SIGHUP, capture_cleanup)) != SIG_DFL)
479     signal(SIGHUP, oldhandler);
480   inpkts = pcap_loop(ld.pch, packet_count, capture_pcap_cb, (u_char *) &ld);
481   pcap_close(ld.pch);
482
483   return TRUE;
484
485 error:
486   g_free(cf.save_file);
487   cf.save_file = NULL;
488   fprintf(stderr, "tethereal: %s\n", errmsg);
489   if (ld.pch != NULL)
490     pcap_close(ld.pch);
491
492   return FALSE;
493 }
494
495 static void
496 capture_pcap_cb(u_char *user, const struct pcap_pkthdr *phdr,
497   const u_char *pd)
498 {
499   struct wtap_pkthdr whdr;
500   loop_data *ld = (loop_data *) user;
501   int err;
502
503   whdr.ts = phdr->ts;
504   whdr.caplen = phdr->caplen;
505   whdr.len = phdr->len;
506   whdr.pkt_encap = ld->linktype;
507
508   if (ld->pdh) {
509     /* XXX - do something if this fails */
510     wtap_dump(ld->pdh, &whdr, pd, &err);
511     cf.count++;
512     printf("\r%u ", cf.count);
513     fflush(stdout);
514   } else {
515     wtap_dispatch_cb((u_char *)&cf, &whdr, 0, pd);
516   }
517 }
518
519 static void
520 capture_cleanup(int signum)
521 {
522   int err;
523
524   printf("\n");
525   pcap_close(ld.pch);
526   if (ld.pdh != NULL)
527     wtap_dump_close(ld.pdh, &err);
528   /* XXX - complain if this fails */
529   exit(0);
530 }
531 #endif /* HAVE_LIBPCAP */
532
533 static int
534 load_cap_file(capture_file *cf)
535 {
536   int     err;
537   int     success;
538   char   *errmsg;
539   char    errmsg_errno[1024+1];
540
541   success = wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf, &err);
542   if (!success) {
543     /* Print up a message box noting that the read failed somewhere along
544        the line. */
545     switch (err) {
546
547     case WTAP_ERR_CANT_READ:
548       errmsg = "An attempt to read from the file failed for"
549                " some unknown reason.";
550       break;
551
552     case WTAP_ERR_SHORT_READ:
553       errmsg = "The capture file appears to have been cut short"
554                " in the middle of a packet.";
555       break;
556
557     case WTAP_ERR_BAD_RECORD:
558       errmsg = "The capture file appears to be damaged or corrupt.";
559       break;
560
561     default:
562       sprintf(errmsg_errno, "An error occurred while reading the"
563                               " capture file: %s.", wtap_strerror(err));
564       errmsg = errmsg_errno;
565       break;
566     }
567     fprintf(stderr, "tethereal: %s\n", errmsg);
568   }
569   wtap_close(cf->wth);
570   cf->wth = NULL;
571
572   return err;
573 }
574
575 static void
576 wtap_dispatch_cb(u_char *user, const struct wtap_pkthdr *phdr, int offset,
577   const u_char *buf)
578 {
579   frame_data    fdata;
580   gint          i;
581   capture_file *cf = (capture_file *) user;
582   proto_tree   *protocol_tree;
583   gboolean      passed;
584   print_args_t print_args;
585
586   cf->count++;
587
588   fdata.next = NULL;
589   fdata.prev = NULL;
590   fdata.pkt_len  = phdr->len;
591   fdata.cap_len  = phdr->caplen;
592   fdata.file_off = offset;
593   fdata.lnk_t = phdr->pkt_encap;
594   fdata.abs_secs  = phdr->ts.tv_sec;
595   fdata.abs_usecs = phdr->ts.tv_usec;
596   fdata.encoding = CHAR_ASCII;
597   fdata.pseudo_header = phdr->pseudo_header;
598   fdata.cinfo = NULL;
599
600   fdata.num = cf->count;
601
602   /* If we don't have the time stamp of the first packet in the
603      capture, it's because this is the first packet.  Save the time
604      stamp of this packet as the time stamp of the first packet. */
605   if (!firstsec && !firstusec) {
606     firstsec  = fdata.abs_secs;
607     firstusec = fdata.abs_usecs;
608   }
609
610   /* Get the time elapsed between the first packet and this packet. */
611   cf->esec = fdata.abs_secs - firstsec;
612   if (firstusec <= fdata.abs_usecs) {
613     cf->eusec = fdata.abs_usecs - firstusec;
614   } else {
615     cf->eusec = (fdata.abs_usecs + 1000000) - firstusec;
616     cf->esec--;
617   }
618   
619   fdata.cinfo = &cf->cinfo;
620   for (i = 0; i < fdata.cinfo->num_cols; i++) {
621     fdata.cinfo->col_data[i][0] = '\0';
622   }
623
624   /* If we don't have the time stamp of the previous displayed packet,
625      it's because this is the first displayed packet.  Save the time
626      stamp of this packet as the time stamp of the previous displayed
627      packet. */
628   if (!prevsec && !prevusec) {
629     prevsec  = fdata.abs_secs;
630     prevusec = fdata.abs_usecs;
631   }
632
633   /* Get the time elapsed between the first packet and this packet. */
634   fdata.rel_secs = cf->esec;
635   fdata.rel_usecs = cf->eusec;
636   
637   /* Get the time elapsed between the previous displayed packet and
638      this packet. */
639   fdata.del_secs = fdata.abs_secs - prevsec;
640   if (prevusec <= fdata.abs_usecs) {
641     fdata.del_usecs = fdata.abs_usecs - prevusec;
642   } else {
643     fdata.del_usecs = (fdata.abs_usecs + 1000000) - prevusec;
644     fdata.del_secs--;
645   }
646   prevsec = fdata.abs_secs;
647   prevusec = fdata.abs_usecs;
648
649   passed = TRUE;
650   if (cf->rfcode || verbose)
651     protocol_tree = proto_tree_create_root();
652   else
653     protocol_tree = NULL;
654   dissect_packet(buf, &fdata, protocol_tree);
655   if (cf->rfcode)
656     passed = dfilter_apply(cf->rfcode, protocol_tree, buf);
657   if (passed) {
658     /* The packet passed the read filter. */
659     if (verbose) {
660       /* Print the information in the protocol tree. */
661       print_args.to_file = TRUE;
662       print_args.format = PR_FMT_TEXT;
663       print_args.print_summary = FALSE;
664       print_args.print_hex = FALSE;
665       print_args.expand_all = TRUE;
666       proto_tree_print(FALSE, &print_args, (GNode *)protocol_tree,
667                         buf, &fdata, stdout);
668       printf("\n");
669     } else {
670       /* Just fill in the columns. */
671       fill_in_columns(&fdata);
672       if (cf->iface == NULL) {
673          printf("%3s %10s %12s -> %-12s %s %s\n",
674                   col_info(&fdata, COL_NUMBER),
675                   col_info(&fdata, COL_CLS_TIME),
676                   col_info(&fdata, COL_DEF_SRC),
677                   col_info(&fdata, COL_DEF_DST),
678                   col_info(&fdata, COL_PROTOCOL),
679                   col_info(&fdata, COL_INFO));
680       } else {
681         printf("%12s -> %-12s %s %s\n",
682                   col_info(&fdata, COL_DEF_SRC),
683                   col_info(&fdata, COL_DEF_DST),
684                   col_info(&fdata, COL_PROTOCOL),
685                   col_info(&fdata, COL_INFO));
686       }
687     }
688     fdata.cinfo = NULL;
689   }
690   if (protocol_tree != NULL)
691     proto_tree_free(protocol_tree);
692 }
693
694 char *
695 file_open_error_message(int err, int for_writing)
696 {
697   char *errmsg;
698   static char errmsg_errno[1024+1];
699
700   switch (err) {
701
702   case WTAP_ERR_NOT_REGULAR_FILE:
703     errmsg = "The file \"%s\" is invalid.";
704     break;
705
706   case WTAP_ERR_FILE_UNKNOWN_FORMAT:
707   case WTAP_ERR_UNSUPPORTED:
708     /* Seen only when opening a capture file for reading. */
709     errmsg = "The file \"%s\" is not a capture file in a format Ethereal understands.";
710     break;
711
712   case WTAP_ERR_UNSUPPORTED_FILE_TYPE:
713     /* Seen only when opening a capture file for writing. */
714     errmsg = "Ethereal does not support writing capture files in that format.";
715     break;
716
717   case WTAP_ERR_UNSUPPORTED_ENCAP:
718   case WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED:
719     /* Seen only when opening a capture file for writing. */
720     errmsg = "Ethereal cannot save this capture in that format.";
721     break;
722
723   case WTAP_ERR_BAD_RECORD:
724     errmsg = "The file \"%s\" appears to be damaged or corrupt.";
725     break;
726
727   case WTAP_ERR_CANT_OPEN:
728     if (for_writing)
729       errmsg = "The file \"%s\" could not be created for some unknown reason.";
730     else
731       errmsg = "The file \"%s\" could not be opened for some unknown reason.";
732     break;
733
734   case WTAP_ERR_SHORT_READ:
735     errmsg = "The file \"%s\" appears to have been cut short"
736              " in the middle of a packet.";
737     break;
738
739   case WTAP_ERR_SHORT_WRITE:
740     errmsg = "A full header couldn't be written to the file \"%s\".";
741     break;
742
743   case ENOENT:
744     if (for_writing)
745       errmsg = "The path to the file \"%s\" does not exist.";
746     else
747       errmsg = "The file \"%s\" does not exist.";
748     break;
749
750   case EACCES:
751     if (for_writing)
752       errmsg = "You do not have permission to create or write to the file \"%s\".";
753     else
754       errmsg = "You do not have permission to read the file \"%s\".";
755     break;
756
757   default:
758     sprintf(errmsg_errno, "The file \"%%s\" could not be opened: %s.",
759                                 wtap_strerror(err));
760     errmsg = errmsg_errno;
761     break;
762   }
763   return errmsg;
764 }
765
766 int
767 open_cap_file(char *fname, gboolean is_tempfile, capture_file *cf)
768 {
769   wtap       *wth;
770   int         err;
771   FILE_T      fh;
772   int         fd;
773   struct stat cf_stat;
774   char        err_msg[2048+1];
775
776   wth = wtap_open_offline(fname, &err);
777   if (wth == NULL)
778     goto fail;
779
780   /* Find the size of the file. */
781   fh = wtap_file(wth);
782   fd = wtap_fd(wth);
783   if (fstat(fd, &cf_stat) < 0) {
784     err = errno;
785     wtap_close(wth);
786     goto fail;
787   }
788
789   /* The open succeeded.  Fill in the information for this file. */
790
791   /* Initialize the table of conversations. */
792   conversation_init();
793
794   /* Initialize protocol-specific variables */
795   init_all_protocols();
796
797   cf->wth = wth;
798   cf->fh = fh;
799   cf->filed = fd;
800   cf->f_len = cf_stat.st_size;
801
802   /* Set the file name because we need it to set the follow stream filter.
803      XXX - is that still true?  We need it for other reasons, though,
804      in any case. */
805   cf->filename = g_strdup(fname);
806
807   /* Indicate whether it's a permanent or temporary file. */
808   cf->is_tempfile = is_tempfile;
809
810   /* If it's a temporary capture buffer file, mark it as not saved. */
811   cf->user_saved = !is_tempfile;
812
813   cf->cd_t      = wtap_file_type(cf->wth);
814   cf->count     = 0;
815   cf->drops     = 0;
816   cf->esec      = 0;
817   cf->eusec     = 0;
818   cf->snap      = wtap_snapshot_length(cf->wth);
819   cf->update_progbar = FALSE;
820   cf->progbar_quantum = 0;
821   cf->progbar_nextstep = 0;
822   firstsec = 0, firstusec = 0;
823   prevsec = 0, prevusec = 0;
824  
825   return (0);
826
827 fail:
828   snprintf(err_msg, sizeof err_msg, file_open_error_message(err, FALSE), fname);
829   fprintf(stderr, "tethereal: %s\n", err_msg);
830   return (err);
831 }
832
833 /* Get the text in a given column */
834 static gchar *
835 col_info(frame_data *fd, gint el) {
836   int i;
837   
838   if (fd->cinfo) {
839     for (i = 0; i < fd->cinfo->num_cols; i++) {
840       if (fd->cinfo->fmt_matx[i][el])
841         return fd->cinfo->col_data[i];
842     }
843   }
844   return NULL;
845 }