Since ethereal is now dependent on GTK+-1.2.x (because of proto_tree and
[obnox/wireshark/wip.git] / file.c
1 /* file.c
2  * File I/O routines
3  *
4  * $Id: file.c,v 1.38 1999/07/13 03:08:05 gram Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1998 Gerald Combs
9  *
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
32 #include <stdio.h>
33
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #ifdef HAVE_IO_H
39 #include <io.h>
40 #endif
41
42 #include <string.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 #include <fcntl.h>
46
47 #ifdef NEED_SNPRINTF_H
48 # ifdef HAVE_STDARG_H
49 #  include <stdarg.h>
50 # else
51 #  include <varargs.h>
52 # endif
53 # include "snprintf.h"
54 #endif
55
56 #ifdef NEED_STRERROR_H
57 #include "strerror.h"
58 #endif
59
60 #ifdef HAVE_SYS_TYPES_H
61 # include <sys/types.h>
62 #endif
63
64 #ifdef HAVE_NETINET_IN_H
65 # include <netinet/in.h>
66 #endif
67
68 #include "ethereal.h"
69 #include "column.h"
70 #include "menu.h"
71 #include "packet.h"
72 #include "file.h"
73 #include "util.h"
74 #include "dfilter.h"
75
76 #include "packet-ncp.h"
77
78 #define TAIL_TIMEOUT    2000  /* msec */
79
80 extern GtkWidget *packet_list, *prog_bar, *info_bar, *byte_view, *tree_view;
81 extern GtkStyle  *pl_style;
82 extern guint      file_ctx;
83 extern int        sync_mode;
84 extern int        sync_pipe[];
85
86 guint cap_input_id, tail_timeout_id;
87
88 static guint32 firstsec, firstusec;
89 static guint32 lastsec, lastusec;
90
91 static void wtap_dispatch_cb(u_char *, const struct wtap_pkthdr *, int,
92     const u_char *);
93
94 static void init_col_widths(capture_file *);
95 static void set_col_widths(capture_file *);
96
97 #ifdef HAVE_LIBPCAP
98 static gint tail_timeout_cb(gpointer);
99 #endif
100
101 int
102 open_cap_file(char *fname, capture_file *cf) {
103   struct stat cf_stat;
104
105   /* First, make sure the file is valid */
106   if (stat(fname, &cf_stat))
107     return (errno);
108 #ifndef WIN32
109   if (! S_ISREG(cf_stat.st_mode) && ! S_ISFIFO(cf_stat.st_mode))
110     return (OPEN_CAP_FILE_NOT_REGULAR);
111 #endif
112
113   /* Next, try to open the file */
114   cf->fh = fopen(fname, "r");
115   if (cf->fh == NULL)
116     return (errno);
117
118   fseek(cf->fh, 0L, SEEK_END);
119   cf->f_len = ftell(cf->fh);
120   fclose(cf->fh);
121   cf->fh = NULL;
122   /* set the file name beacuse we need it to set the follow stream filter */
123   cf->filename = g_strdup( fname );
124
125   /* Next, find out what type of file we're dealing with */
126   cf->cd_t  = WTAP_FILE_UNKNOWN;
127   cf->count = 0;
128   cf->drops = 0;
129   cf->esec  = 0;
130   cf->eusec = 0;
131   cf->snap  = 0;
132   firstsec = 0, firstusec = 0;
133   lastsec = 0, lastusec = 0;
134  
135   cf->wth = wtap_open_offline(fname);
136   if (cf->wth == NULL) {
137
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);
142   }
143
144   cf->fh = wtap_file(cf->wth);
145   cf->cd_t = wtap_file_type(cf->wth);
146   cf->snap = wtap_snapshot_length(cf->wth);
147   return (0);
148 }
149
150 static void
151 free_packets_cb(gpointer data, gpointer user_data)
152 {
153   g_free(data);
154 }
155
156 /* Reset everything to a pristine state */
157 void
158 close_cap_file(capture_file *cf, void *w, guint context) {
159   if (cf->fh) {
160     fclose(cf->fh);
161     cf->fh = NULL;
162   }
163   if (cf->wth) {
164     wtap_close(cf->wth);
165     cf->wth = NULL;
166   }
167   if (cf->plist) {
168     g_list_foreach(cf->plist, free_packets_cb, NULL);
169     g_list_free(cf->plist);
170     cf->plist = NULL;
171   }
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));
179
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);
184 }
185
186 int
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'";
191   gint    timeout;
192   size_t  msg_len;
193   int     err;
194
195   close_cap_file(cf, info_bar, file_ctx);
196
197   /* Initialize protocol-specific variables */
198   ncp_init_protocol();
199
200   if ((name_ptr = (gchar *) strrchr(fname, '/')) == NULL)
201     name_ptr = fname;
202   else
203     name_ptr++;
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);
207   
208   timeout = gtk_timeout_add(250, file_progress_cb, (gpointer) &cf);
209   
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));
213     init_col_widths(cf);
214     wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf);
215     wtap_close(cf->wth);
216     cf->wth = NULL;
217     cf->fh = fopen(fname, "r");
218
219     set_col_widths(cf);
220     gtk_clist_thaw(GTK_CLIST(packet_list));
221   }
222   
223   gtk_timeout_remove(timeout);
224   gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar), 0);
225
226   gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
227
228   if (err == 0) {
229     msg_len = strlen(name_ptr) + strlen(done_fmt) + 64;
230     load_msg = g_realloc(load_msg, msg_len);
231
232     if (cf->user_saved || !cf->save_file)
233             snprintf(load_msg, msg_len, done_fmt, name_ptr, cf->drops);
234     else
235             snprintf(load_msg, msg_len, done_fmt, "<none>", cf->drops);
236
237     gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, load_msg);
238     g_free(load_msg);
239
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);
244   } else {
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);
249     g_free(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);
255   }
256   return err;
257 }
258
259 #ifdef HAVE_LIBPCAP
260 void 
261 cap_file_input_cb (gpointer data, gint source, GdkInputCondition condition) {
262   
263   capture_file *cf = (capture_file *)data;
264   char buffer[256];
265
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);
269
270   if (read(sync_pipe[0], buffer, 256) <= 0) {
271
272     /* process data until end of file and stop capture (restore menu items) */
273     gtk_clist_freeze(GTK_CLIST(packet_list));
274     init_col_widths(cf);
275     wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf);      
276
277     set_col_widths(cf);
278     gtk_clist_thaw(GTK_CLIST(packet_list));
279
280     wtap_close(cf->wth);
281     cf->wth = NULL;
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>");
290     return;
291   }
292
293   gtk_clist_freeze(GTK_CLIST(packet_list));
294   init_col_widths(cf);
295   wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf);      
296
297   set_col_widths(cf);
298   gtk_clist_thaw(GTK_CLIST(packet_list));
299
300   /* restore pipe handler */
301   cap_input_id = gtk_input_add_full (sync_pipe[0],
302                                      GDK_INPUT_READ,
303                                      cap_file_input_cb,
304                                      NULL,
305                                      (gpointer) cf,
306                                      NULL);
307
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);
310
311 }
312
313 gint
314 tail_timeout_cb(gpointer data) {
315
316   capture_file *cf = (capture_file *)data;
317
318   /* avoid reentrancy problems and stack overflow */
319   gtk_input_remove(cap_input_id);
320
321   gtk_clist_freeze(GTK_CLIST(packet_list));
322   init_col_widths(cf);
323   wtap_loop(cf->wth, 0, wtap_dispatch_cb, (u_char *) cf);      
324
325   set_col_widths(cf);
326   gtk_clist_thaw(GTK_CLIST(packet_list));
327
328   cap_input_id = gtk_input_add_full (sync_pipe[0],
329                                      GDK_INPUT_READ,
330                                      cap_file_input_cb,
331                                      NULL,
332                                      (gpointer) cf,
333                                      NULL);
334
335   return TRUE;
336 }
337
338 int
339 tail_cap_file(char *fname, capture_file *cf) {
340   int     err;
341
342   close_cap_file(cf, info_bar, file_ctx);
343
344   /* Initialize protocol-speficic variables */
345   ncp_init_protocol();
346
347   err = open_cap_file(fname, cf);
348   if ((err == 0) && (cf->cd_t != WTAP_FILE_UNKNOWN)) {
349
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);
356
357     cf->fh = fopen(fname, "r");
358     tail_timeout_id = -1;
359     cap_input_id = gtk_input_add_full (sync_pipe[0],
360                                        GDK_INPUT_READ,
361                                        cap_file_input_cb,
362                                        NULL,
363                                        (gpointer) cf,
364                                        NULL);
365     gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, 
366                        " <live capture in progress>");
367   }
368   else {
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);
374     close(sync_pipe[0]);
375   }
376   return err;
377 }
378 #endif
379
380 static void
381 compute_time_stamps(frame_data *fdata, capture_file *cf)
382 {
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;
389   }
390
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;
395   }
396
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;
401   } else {
402     cf->eusec = (fdata->abs_usecs + 1000000) - firstusec;
403     cf->esec--;
404   }
405   fdata->rel_secs = cf->esec;
406   fdata->rel_usecs = cf->eusec;
407   
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;
412   } else {
413     fdata->del_usecs = (fdata->abs_usecs + 1000000) - lastusec;
414     fdata->del_secs--;
415   }
416   lastsec = fdata->abs_secs;
417   lastusec = fdata->abs_usecs;
418 }
419
420 static void
421 change_time_format_in_packet_list(frame_data *fdata, capture_file *cf)
422 {
423   gint          i, col_width;
424
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. */
433     return;
434   }
435
436   compute_time_stamps(fdata, cf);
437
438   for (i = 0; i < fdata->cinfo->num_cols; i++) {
439     fdata->cinfo->col_data[i][0] = '\0';
440   }
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]);
451     }
452   }
453   fdata->cinfo = NULL;
454 }
455
456 static void
457 add_packet_to_packet_list(frame_data *fdata, capture_file *cf, const u_char *buf)
458 {
459   gint          i, col_width, row;
460   proto_tree   *protocol_tree;
461
462   compute_time_stamps(fdata, cf);
463
464   fdata->cinfo = &cf->cinfo;
465   for (i = 0; i < fdata->cinfo->num_cols; i++) {
466     fdata->cinfo->col_data[i][0] = '\0';
467   }
468   if (check_col(fdata, COL_NUMBER))
469     col_add_fstr(fdata, COL_NUMBER, "%d", cf->count);
470   /* Apply the display filter */
471   if (cf->dfcode) {
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);
475   }
476   else {
477         dissect_packet(buf, fdata, NULL);
478         fdata->passed_dfilter = TRUE;
479   }
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;
484   }
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);
488   }
489   fdata->cinfo = NULL;
490 }
491
492 static void
493 wtap_dispatch_cb(u_char *user, const struct wtap_pkthdr *phdr, int offset,
494   const u_char *buf) {
495   frame_data   *fdata;
496   capture_file *cf = (capture_file *) user;
497
498   while (gtk_events_pending())
499     gtk_main_iteration();
500
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);
504
505   cf->cur = fdata;
506   cf->count++;
507
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;
514   fdata->cinfo = NULL;
515
516   add_packet_to_packet_list(fdata, cf, buf);
517 }
518
519 static void
520 filter_packets_cb(gpointer data, gpointer user_data)
521 {
522   frame_data *fd = data;
523   capture_file *cf = user_data;
524
525   cf->cur = fd;
526   cf->count++;
527
528   fseek(cf->fh, fd->file_off, SEEK_SET);
529   fread(cf->pd, sizeof(guint8), fd->cap_len, cf->fh);
530
531   add_packet_to_packet_list(fd, cf, cf->pd);
532 }
533
534 void
535 filter_packets(capture_file *cf)
536 {
537   if (cf->dfilter != NULL) {
538     /*
539      * Compile the filter.
540      */
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);
544       return;
545     }
546   }
547
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));
551
552   /* Clear it out. */
553   gtk_clist_clear(GTK_CLIST(packet_list));
554
555   /*
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.
559    */
560   firstsec = 0;
561   firstusec = 0;
562   lastsec = 0;
563   lastusec = 0;
564   cf->count = 0;
565   g_list_foreach(cf->plist, filter_packets_cb, cf);
566
567   /* Unfreeze the packet list. */
568   gtk_clist_thaw(GTK_CLIST(packet_list));
569 }
570
571 static void
572 change_time_formats_cb(gpointer data, gpointer user_data)
573 {
574   frame_data *fd = data;
575   capture_file *cf = user_data;
576
577   cf->cur = fd;
578   cf->count++;
579
580   change_time_format_in_packet_list(fd, cf);
581 }
582
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. */
586 void
587 change_time_formats(capture_file *cf)
588 {
589   int i;
590
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));
594
595   /* Zero out the column widths. */
596   init_col_widths(cf);
597
598   /*
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.
602    */
603   firstsec = 0;
604   firstusec = 0;
605   lastsec = 0;
606   lastusec = 0;
607   cf->count = 0;
608   g_list_foreach(cf->plist, change_time_formats_cb, cf);
609
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]);
616     }
617   }
618
619   /* Unfreeze the packet list. */
620   gtk_clist_thaw(GTK_CLIST(packet_list));
621 }
622
623 /* Initialize the maximum widths of the columns to the widths of their
624    titles. */
625 static void
626 init_col_widths(capture_file *cf)
627 {
628   int i;
629
630   /* XXX - this should use the column *title* font, not the font for
631      the items in the list.
632
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]);
640 }
641
642 /* Set the widths of the columns to the maximum widths we found. */
643 static void
644 set_col_widths(capture_file *cf)
645 {
646   int i;
647
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]);
651   }
652 }
653
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
656  */
657 int
658 file_mv(char *from, char *to)
659 {
660
661 #define COPY_BUFFER_SIZE        8192
662
663         int retval;
664
665 #ifndef WIN32
666         /* try a hard link */
667         retval = link(from, to);
668
669         /* or try a copy */
670         if (retval < 0) {
671 #endif
672                 retval = file_cp(from, to);
673                 if (!retval) {
674                         return 0;
675                 }
676 #ifndef WIN32
677         }
678 #endif
679
680         unlink(from);
681         return 1;
682 }
683
684 /* Copies a file.
685  * Returns 0 on failure to do either, 1 on success of either
686  */
687 int
688 file_cp(char *from, char *to)
689 {
690
691 #define COPY_BUFFER_SIZE        8192
692
693         int from_fd, to_fd, nread, nwritten;
694         char *buffer;
695         gint dialogue_button = ESD_BTN_OK;
696
697         buffer = g_malloc(COPY_BUFFER_SIZE);
698
699         from_fd = open(from, O_RDONLY);
700         if (from_fd < 0) {
701                 simple_dialog(ESD_TYPE_WARN, &dialogue_button,
702                         file_open_error_message(errno, TRUE), from);
703                 return 0;
704         }
705
706         to_fd = creat(to, 0644);
707         if (to_fd < 0) {
708                 simple_dialog(ESD_TYPE_WARN, &dialogue_button,
709                         file_open_error_message(errno, TRUE), to);
710                 close(from_fd);
711                 return 0;
712         }
713
714         while( (nread = read(from_fd, buffer, COPY_BUFFER_SIZE)) > 0) {
715                 nwritten = write(to_fd, buffer, nread);
716                 if (nwritten < nread) {
717                         if (nwritten < 0) {
718                                 simple_dialog(ESD_TYPE_WARN, &dialogue_button,
719                                         file_write_error_message(errno), to);
720                         } else {
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);
724                         }
725                         close(from_fd);
726                         close(to_fd);
727                         return 0;
728                 }
729         }
730         if (nread < 0) {
731                 simple_dialog(ESD_TYPE_WARN, &dialogue_button,
732                         file_read_error_message(errno), from);
733                 close(from_fd);
734                 close(to_fd);
735                 return 0;
736         }
737         close(from_fd);
738         close(to_fd);
739
740         return 1;
741 }
742
743 char *
744 file_open_error_message(int err, int for_writing)
745 {
746   char *errmsg;
747   static char errmsg_errno[1024+1];
748
749   switch (err) {
750
751   case OPEN_CAP_FILE_NOT_REGULAR:
752     errmsg = "The file \"%s\" is invalid.";
753     break;
754
755   case OPEN_CAP_FILE_UNKNOWN_FORMAT:
756     errmsg = "The file \"%s\" is not a capture file in a format Ethereal understands.";
757     break;
758
759   case ENOENT:
760     if (for_writing)
761       errmsg = "The path to the file \"%s\" does not exist.";
762     else
763       errmsg = "The file \"%s\" does not exist.";
764     break;
765
766   case EACCES:
767     if (for_writing)
768       errmsg = "You do not have permission to create or write to the file \"%s\".";
769     else
770       errmsg = "You do not have permission to open the file \"%s\".";
771     break;
772
773   default:
774     sprintf(errmsg_errno, "The file \"%%s\" could not be opened: %s.", strerror(err));
775     errmsg = errmsg_errno;
776     break;
777   }
778   return errmsg;
779 }
780
781 char *
782 file_read_error_message(int err)
783 {
784   static char errmsg_errno[1024+1];
785
786   sprintf(errmsg_errno, "An error occurred while reading from the file \"%%s\": %s.", strerror(err));
787   return errmsg_errno;
788 }
789
790 char *
791 file_write_error_message(int err)
792 {
793   char *errmsg;
794   static char errmsg_errno[1024+1];
795
796   switch (err) {
797
798   case ENOSPC:
799     errmsg = "The file \"%s\" could not be saved because there is no space left on the file system.";
800     break;
801
802 #ifdef EDQUOT
803   case EDQUOT:
804     errmsg = "The file \"%s\" could not be saved because you are too close to, or over, your disk quota.";
805     break;
806 #endif
807
808   default:
809     sprintf(errmsg_errno, "An error occurred while writing to the file \"%%s\": %s.", strerror(err));
810     errmsg = errmsg_errno;
811     break;
812   }
813   return errmsg;
814 }