Squelch some compiler warnings by declaring RADIUS AVP dissectors to
[obnox/wireshark/wip.git] / dumpcap.c
1 /* dumpcap.c
2  *
3  * $Id$
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@ethereal.com>
7  * Copyright 1998 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <glib.h>
29
30 #include <string.h>
31 #include <ctype.h>
32
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36
37 #ifdef NEED_GETOPT_H
38 #include "getopt.h"
39 #endif
40
41 #ifdef HAVE_NETDB_H
42 #include <netdb.h>
43 #endif
44
45 #ifdef _WIN32 /* Needed for console I/O */
46 #include <conio.h>
47 #endif
48
49 #include "ringbuffer.h"
50 #include "clopts_common.h"
51 #include "cmdarg_err.h"
52 #include "version_info.h"
53
54 #include <pcap.h>
55 #include "capture-pcap-util.h"
56
57 #ifdef _WIN32
58 #include "capture-wpcap.h"
59 #endif
60
61 #include "capture.h"
62 #include "capture_loop.h"
63 #include "capture_sync.h"
64
65 #include "simple_dialog.h"
66 #include "util.h"
67 #include "log.h"
68 #include "file_util.h"
69
70
71
72 gboolean capture_child; /* True if this is an Ethereal capture child */
73
74 /* Win32 console handling */
75 #ifdef _WIN32
76 static gboolean has_console = FALSE;            /* TRUE if app has console */
77 static void create_console(void);
78 static void destroy_console(void);
79 #endif
80 static void
81 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
82                     const char *message, gpointer user_data _U_);
83
84 /* capture related options */
85 capture_options global_capture_opts;
86 capture_options *capture_opts = &global_capture_opts;
87
88 #if __GNUC__ >= 2
89 void exit_main(int err) __attribute__ ((noreturn));
90 #else
91 void exit_main(int err);
92 #endif
93
94 const char *get_basename(const char *path);
95
96
97 static void
98 print_usage(gboolean print_ver) {
99
100   FILE *output;
101
102 #ifdef _WIN32
103   create_console();
104 #endif
105
106   if (print_ver) {
107     output = stdout;
108     fprintf(output,
109         "Dumpcap " VERSION "%s\n"
110         "Capture network packets and dump them into a libpcap file.\n"
111         "See http://www.ethereal.com for more information.\n",
112         svnversion);
113   } else {
114     output = stderr;
115   }
116   fprintf(output, "\nUsage: dumpcap [options] ...\n");
117   fprintf(output, "\n");
118   fprintf(output, "Capture interface:\n");
119   fprintf(output, "  -i <interface>           name or idx of interface (def: first none loopback)\n");
120   fprintf(output, "  -f <capture filter>      packet filter in libpcap filter syntax\n");
121   fprintf(output, "  -s <snaplen>             packet snapshot length (def: 65535)\n");
122   fprintf(output, "  -p                       don't capture in promiscuous mode\n");
123 #ifdef _WIN32
124   fprintf(output, "  -B <buffer size>         size of kernel buffer (def: 1MB)\n");
125 #endif
126   fprintf(output, "  -y <link type>           link layer type (def: first appropriate)\n");
127   fprintf(output, "  -D                       print list of interfaces and exit\n");
128   fprintf(output, "  -L                       print list of link-layer types of iface and exit\n");
129   fprintf(output, "\n");
130   fprintf(output, "Stop conditions:\n");
131   fprintf(output, "  -c <packet count>        stop after n packets (def: infinite)\n");
132   fprintf(output, "  -a <autostop cond.> ...  duration:NUM - stop after NUM seconds\n");
133   fprintf(output, "                           filesize:NUM - stop this file after NUM KB\n");
134   fprintf(output, "                              files:NUM - stop after NUM files\n");
135   /*fprintf(output, "\n");*/
136   fprintf(output, "Output (files):\n");
137   fprintf(output, "  -w <filename>            name of file to save (def: tempfile)\n");
138   fprintf(output, "  -b <ringbuffer opt.> ... duration:NUM - switch to next file after NUM secs\n");
139   fprintf(output, "                           filesize:NUM - switch to next file after NUM KB\n");
140   fprintf(output, "                              files:NUM - ringbuffer: replace after NUM files\n");
141   /*fprintf(output, "\n");*/
142   fprintf(output, "Miscellaneous:\n");
143   fprintf(output, "  -v                       print version information and exit\n");
144   fprintf(output, "  -h                       display this help and exit\n");
145   fprintf(output, "\n");
146   fprintf(output, "Example: dumpcap -i eth0 -a duration:60 -w output.pcap\n");
147   fprintf(output, "\"Capture network packets from interface eth0 until 60s passed into output.pcap\"\n");
148   fprintf(output, "\n");
149   fprintf(output, "Use Ctrl-C to stop capturing at any time.\n");
150 }
151
152 static void
153 show_version(GString *comp_info_str, GString *runtime_info_str)
154 {
155 #ifdef _WIN32
156   create_console();
157 #endif
158
159   printf(
160         "Dumpcap " VERSION "%s\n"
161         "\n"
162         "%s\n"
163         "%s\n"
164         "%s\n"
165         "See http://www.ethereal.com for more information.\n",
166         svnversion, get_copyright_info() ,comp_info_str->str, runtime_info_str->str);
167 }
168
169 /*
170  * Report an error in command-line arguments.
171  * Creates a console on Windows.
172  * XXX - pop this up in a window of some sort on UNIX+X11 if the controlling
173  * terminal isn't the standard error?
174  */
175 void
176 cmdarg_err(const char *fmt, ...)
177 {
178   va_list ap;
179
180 #ifdef _WIN32
181   create_console();
182 #endif
183   va_start(ap, fmt);
184   fprintf(stderr, "dumpcap: ");
185   vfprintf(stderr, fmt, ap);
186   fprintf(stderr, "\n");
187   va_end(ap);
188 }
189
190 /*
191  * Report additional information for an error in command-line arguments.
192  * Creates a console on Windows.
193  * XXX - pop this up in a window of some sort on UNIX+X11 if the controlling
194  * terminal isn't the standard error?
195  */
196 void
197 cmdarg_err_cont(const char *fmt, ...)
198 {
199   va_list ap;
200
201 #ifdef _WIN32
202   create_console();
203 #endif
204   va_start(ap, fmt);
205   vfprintf(stderr, fmt, ap);
206   fprintf(stderr, "\n");
207   va_end(ap);
208 }
209
210
211 #ifdef _WIN32
212 BOOL WINAPI ConsoleCtrlHandlerRoutine(DWORD dwCtrlType)
213 {
214     /*printf("Event: %u", dwCtrlType);*/
215     capture_loop_stop();
216
217     return TRUE;
218 }
219 #endif
220
221 void exit_main(int err)
222 {
223 #ifdef _WIN32
224   /* Shutdown windows sockets */
225   WSACleanup();
226
227   destroy_console();
228 #endif
229
230   /* can be helpful for debugging */
231   /* _getch(); */
232   exit(err);
233 }
234
235
236 /* And now our feature presentation... [ fade to music ] */
237 int
238 main(int argc, char *argv[])
239 {
240   int                  opt;
241   extern char         *optarg;
242   gboolean             arg_error = FALSE;
243   GString             *comp_info_str;
244   GString             *runtime_info_str;
245
246 #ifdef _WIN32
247   WSADATA              wsaData;
248 #endif  /* _WIN32 */
249
250   gboolean             start_capture = TRUE;
251   gboolean             stats_known;
252   struct pcap_stat     stats;
253   GLogLevelFlags       log_flags;
254   gboolean             list_link_layer_types = FALSE;
255   int                  status;
256
257 #define OPTSTRING_INIT "a:b:c:Df:hi:Lps:vw:y:"
258
259 #ifdef _WIN32
260 #define OPTSTRING_WIN32 "B:Z:"
261 #else
262 #define OPTSTRING_WIN32 ""
263 #endif  /* _WIN32 */
264
265   char optstring[sizeof(OPTSTRING_INIT) + sizeof(OPTSTRING_WIN32) - 1] =
266     OPTSTRING_INIT OPTSTRING_WIN32;
267
268
269   capture_child = (strcmp(get_basename(argv[0]), CHILD_NAME) == 0);
270
271 #ifdef _WIN32
272   /* Load wpcap if possible. Do this before collecting the run-time version information */
273   load_wpcap();
274
275   /* ... and also load the packet.dll from wpcap */
276   /* XXX - currently not required, may change later. */
277   /*wpcap_packet_load();*/
278
279   /* Start windows sockets */
280   WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
281
282   /* Set handler for Ctrl+C key */
283   SetConsoleCtrlHandler(&ConsoleCtrlHandlerRoutine, TRUE);
284 #endif  /* _WIN32 */
285
286   /* Assemble the compile-time version information string */
287   comp_info_str = g_string_new("Compiled ");
288   g_string_append(comp_info_str, "with ");
289   get_compiled_version_info(comp_info_str);
290
291   /* Assemble the run-time version information string */
292   runtime_info_str = g_string_new("Running ");
293   get_runtime_version_info(runtime_info_str);
294
295   /* Arrange that if we have no console window, and a GLib message logging
296      routine is called to log a message, we pop up a console window.
297
298      We do that by inserting our own handler for all messages logged
299      to the default domain; that handler pops up a console if necessary,
300      and then calls the default handler. */
301
302   /* We might want to have component specific log levels later ... */
303
304   /* the default_log_handler will use stdout, which makes trouble with the */
305   /* capture child, as it uses stdout for it's sync_pipe */
306   /* so do the filtering in the console_log_handler and not here */
307   log_flags = 
308                     G_LOG_LEVEL_ERROR|
309                     G_LOG_LEVEL_CRITICAL|
310                     G_LOG_LEVEL_WARNING|
311                     G_LOG_LEVEL_MESSAGE|
312                     G_LOG_LEVEL_INFO|
313                     G_LOG_LEVEL_DEBUG|
314                     G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION;
315
316   g_log_set_handler(NULL,
317                     log_flags,
318                     console_log_handler, NULL /* user_data */);
319   g_log_set_handler(LOG_DOMAIN_MAIN,
320                     log_flags,
321                     console_log_handler, NULL /* user_data */);
322   g_log_set_handler(LOG_DOMAIN_CAPTURE,
323                     log_flags,
324             console_log_handler, NULL /* user_data */);
325   g_log_set_handler(LOG_DOMAIN_CAPTURE_CHILD,
326                     log_flags,
327             console_log_handler, NULL /* user_data */);
328
329   /* Set the initial values in the capture_opts. This might be overwritten 
330      by the command line parameters. */
331   capture_opts_init(capture_opts, NULL);
332
333   capture_opts->snaplen             = MIN_PACKET_SIZE;
334   capture_opts->has_ring_num_files  = TRUE;
335
336   /* Now get our args */
337   while ((opt = getopt(argc, argv, optstring)) != -1) {
338     switch (opt) {
339       case 'h':        /* Print help and exit */
340         print_usage(TRUE);
341         exit_main(0);
342         break;
343       case 'v':        /* Show version and exit */
344         show_version(comp_info_str, runtime_info_str);
345         exit_main(0);
346         break;
347       /*** capture option specific ***/
348       case 'a':        /* autostop criteria */
349       case 'b':        /* Ringbuffer option */
350       case 'c':        /* Capture x packets */
351       case 'f':        /* capture filter */
352       case 'i':        /* Use interface x */
353       case 'p':        /* Don't capture in promiscuous mode */
354       case 's':        /* Set the snapshot (capture) length */
355       case 'w':        /* Write to capture file x */
356       case 'y':        /* Set the pcap data link type */
357 #ifdef _WIN32
358       case 'B':        /* Buffer size */
359       /* Hidden option supporting Sync mode */
360       case 'Z':        /* Write to pipe FD x */
361 #endif /* _WIN32 */
362         status = capture_opts_add_opt(capture_opts, opt, optarg, &start_capture);
363         if(status != 0) {
364             exit_main(status);
365         }
366         break;
367
368       /*** all non capture option specific ***/
369       case 'D':        /* Print a list of capture devices and exit */
370         status = capture_opts_list_interfaces();
371         exit_main(status);
372         break;
373       case 'L':        /* Print list of link-layer types and exit */
374         list_link_layer_types = TRUE;
375         break;
376       default:
377       case '?':        /* Bad flag - print usage message */
378         cmdarg_err("Invalid Option: %s", argv[optind-1]);
379         arg_error = TRUE;
380         break;
381     }
382   }
383   argc -= optind;
384   argv += optind;
385   if (argc >= 1) {
386       /* user specified file name as regular command-line argument */
387       /* XXX - use it as the capture file name (or something else)? */
388     argc--;
389     argv++;
390   }
391
392   if (argc != 0) {
393     /*
394      * Extra command line arguments were specified; complain.
395      */
396     cmdarg_err("Invalid argument: %s", argv[0]);
397     arg_error = TRUE;
398   }
399
400   if (arg_error) {
401     print_usage(FALSE);
402     exit_main(1);
403   }
404
405   if (list_link_layer_types) {
406     /* We're supposed to list the link-layer types for an interface;
407        did the user also specify a capture file to be read? */
408     /* No - did they specify a ring buffer option? */
409     if (capture_opts->multi_files_on) {
410       cmdarg_err("Ring buffer requested, but a capture isn't being done.");
411       exit_main(1);
412     }
413   } else {
414     /* No - was the ring buffer option specified and, if so, does it make
415        sense? */
416     if (capture_opts->multi_files_on) {
417       /* Ring buffer works only under certain conditions:
418          a) ring buffer does not work with temporary files;
419          b) it makes no sense to enable the ring buffer if the maximum
420             file size is set to "infinite". */
421       if (capture_opts->save_file == NULL) {
422         cmdarg_err("Ring buffer requested, but capture isn't being saved to a permanent file.");
423         capture_opts->multi_files_on = FALSE;
424       }
425       if (!capture_opts->has_autostop_filesize && !capture_opts->has_file_duration) {
426         cmdarg_err("Ring buffer requested, but no maximum capture file size or duration were specified.");
427 /* XXX - this must be redesigned as the conditions changed */
428 /*      capture_opts->multi_files_on = FALSE;*/
429       }
430     }
431   }
432
433   if (capture_opts_trim_iface(capture_opts, NULL) == FALSE) {
434         cmdarg_err("No capture interfaces available (maybe lack of privileges?).");
435     exit_main(1);
436   }
437
438   /* Let the user know what interface was chosen. */
439 /*  descr = get_interface_descriptive_name(capture_opts.iface);
440   fprintf(stderr, "Capturing on %s\n", descr);
441   g_free(descr);*/
442
443   if(!capture_child) {
444     fprintf(stderr, "Capturing on %s\n", capture_opts->iface);
445   }  
446
447   if (list_link_layer_types) {
448     status = capture_opts_list_link_layer_types(capture_opts);
449     exit_main(status);
450   }
451
452   capture_opts_trim_snaplen(capture_opts, MIN_PACKET_SIZE);
453   capture_opts_trim_ring_num_files(capture_opts);
454
455   /* Now start the capture. */
456
457   /* XXX - hand the stats to the parent process */
458   if(capture_loop_start(capture_opts, &stats_known, &stats) == TRUE) {
459       /* capture ok */
460       exit_main(0);
461   } else {
462       /* capture failed */
463       exit_main(1);
464   }
465 }
466
467 #ifdef _WIN32
468
469 /* We build this as a GUI subsystem application on Win32, so
470    "WinMain()", not "main()", gets called.
471
472    Hack shamelessly stolen from the Win32 port of the GIMP. */
473 #ifdef __GNUC__
474 #define _stdcall  __attribute__((stdcall))
475 #endif
476
477 int _stdcall
478 WinMain (struct HINSTANCE__ *hInstance,
479          struct HINSTANCE__ *hPrevInstance,
480          char               *lpszCmdLine,
481          int                 nCmdShow)
482 {
483   has_console = FALSE;
484   return main (__argc, __argv);
485 }
486
487 /*
488  * If this application has no console window to which its standard output
489  * would go, create one.
490  */
491 void
492 create_console(void)
493 {
494   if (!has_console) {
495     /* We have no console to which to print the version string, so
496        create one and make it the standard input, output, and error. */
497     if (!AllocConsole())
498       return;   /* couldn't create console */
499     eth_freopen("CONIN$", "r", stdin);
500     eth_freopen("CONOUT$", "w", stdout);
501     eth_freopen("CONOUT$", "w", stderr);
502
503     /* Well, we have a console now. */
504     has_console = TRUE;
505
506     /* Now register "destroy_console()" as a routine to be called just
507        before the application exits, so that we can destroy the console
508        after the user has typed a key (so that the console doesn't just
509        disappear out from under them, giving the user no chance to see
510        the message(s) we put in there). */
511     atexit(destroy_console);
512
513     SetConsoleTitle("Dumpcap Console");
514   }
515 }
516
517 static void
518 destroy_console(void)
519 {
520   if (has_console) {
521 /* XXX - doesn't make sense while we're linked as a console application */
522 /*    printf("\n\nPress any key to exit\n");
523     _getch();*/
524     FreeConsole();
525   }
526 }
527 #endif /* _WIN32 */
528
529
530 /* This routine should not be necessary, at least as I read the GLib
531    source code, as it looks as if GLib is, on Win32, *supposed* to
532    create a console window into which to display its output.
533
534    That doesn't happen, however.  I suspect there's something completely
535    broken about that code in GLib-for-Win32, and that it may be related
536    to the breakage that forces us to just call "printf()" on the message
537    rather than passing the message on to "g_log_default_handler()"
538    (which is the routine that does the aforementioned non-functional
539    console window creation). */
540 static void
541 console_log_handler(const char *log_domain, GLogLevelFlags log_level,
542                     const char *message, gpointer user_data _U_)
543 {
544   time_t curr;
545   struct tm *today;
546   const char *level;
547
548
549   /* ignore log message, if log_level isn't interesting */
550   if( !(log_level & G_LOG_LEVEL_MASK & ~(G_LOG_LEVEL_DEBUG|G_LOG_LEVEL_INFO))) {
551     return;
552   }
553
554   /* create a "timestamp" */
555   time(&curr);
556   today = localtime(&curr);    
557
558 #ifdef _WIN32
559   if(!capture_child) {
560     create_console();
561   }
562   if (has_console) {
563     /* For some unknown reason, the above doesn't appear to actually cause
564        anything to be sent to the standard output, so we'll just splat the
565        message out directly, just to make sure it gets out. */
566 #endif
567     switch(log_level & G_LOG_LEVEL_MASK) {
568     case G_LOG_LEVEL_ERROR:
569         level = "Err ";
570         break;
571     case G_LOG_LEVEL_CRITICAL:
572         level = "Crit";
573         break;
574     case G_LOG_LEVEL_WARNING:
575         level = "Warn";
576         break;
577     case G_LOG_LEVEL_MESSAGE:
578         level = "Msg ";
579         break;
580     case G_LOG_LEVEL_INFO:
581         level = "Info";
582         break;
583     case G_LOG_LEVEL_DEBUG:
584         level = "Dbg ";
585         break;
586     default:
587         fprintf(stderr, "unknown log_level %u\n", log_level);
588         level = NULL;
589         g_assert_not_reached();
590     }
591
592     /* don't use printf (stdout), as the capture child uses stdout for it's sync_pipe */
593     fprintf(stderr, "%02u:%02u:%02u %8s %s %s\n",
594             today->tm_hour, today->tm_min, today->tm_sec,
595             log_domain != NULL ? log_domain : "",
596             level, message);
597 #ifdef _WIN32
598   } else {
599     g_log_default_handler(log_domain, log_level, message, user_data);
600   }
601 #endif
602 }
603
604 /****************************************************************************************************************/
605 /* sync_pipe stubs */
606
607 /*
608  * Maximum length of sync pipe message data.  Must be < 2^24, as the
609  * message length is 3 bytes.
610  * XXX - this must be large enough to handle a Really Big Filter
611  * Expression, as the error message for an incorrect filter expression
612  * is a bit larger than the filter expression.
613  */
614 #define SP_MAX_MSG_LEN  4096
615
616
617  /* write a message to the recipient pipe in the standard format 
618    (3 digit message length (excluding length and indicator field), 
619    1 byte message indicator and the rest is the message) */
620 static void
621 pipe_write_block(int pipe, char indicator, int len, const char *msg)
622 {
623     guchar header[3+1]; /* indicator + 3-byte len */
624     int ret;
625
626     /*g_warning("write %d enter", pipe);*/
627
628     /* if we're not a capture child, we don't have to tell our none existing parent anything */
629     if(!capture_child)
630         return;
631
632     g_assert(indicator < '0' || indicator > '9');
633     g_assert(len <= SP_MAX_MSG_LEN);
634
635     /* write header (indicator + 3-byte len) */
636     header[0] = indicator;
637     header[1] = (len >> 16) & 0xFF;
638     header[2] = (len >> 8) & 0xFF;
639     header[3] = (len >> 0) & 0xFF;
640
641     ret = write(pipe, header, sizeof header);
642     if(ret == -1) {
643         return;
644     }
645
646     /* write value (if we have one) */
647     if(len) {
648         /*g_warning("write %d indicator: %c value len: %u msg: %s", pipe, indicator, len, msg);*/
649         ret = write(pipe, msg, len);
650         if(ret == -1) {
651             return;
652         }
653     } else {
654         /*g_warning("write %d indicator: %c no value", pipe, indicator);*/
655     }
656
657     /*g_warning("write %d leave", pipe);*/
658 }
659
660
661 void
662 sync_pipe_packet_count_to_parent(int packet_count)
663 {
664     char tmp[SP_DECISIZE+1+1];
665     static int count = 0;
666
667
668     if(!capture_child) {
669         count += packet_count;
670         fprintf(stderr, "\r%u", count);
671         /* stderr could be line buffered */
672         fflush(stderr);
673     }
674
675     g_snprintf(tmp, sizeof(tmp), "%d", packet_count);
676
677     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_packet_count_to_parent: %s", tmp);
678
679     pipe_write_block(1, SP_PACKET_COUNT, strlen(tmp)+1, tmp);
680 }
681
682 void
683 sync_pipe_filename_to_parent(const char *filename)
684 {
685
686     if(!capture_child) {
687         fprintf(stderr, "\nFile: %s\n", filename);
688         /* stderr could be line buffered */
689         fflush(stderr);
690     }
691
692     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, "File: %s", filename);
693
694     pipe_write_block(1, SP_FILE, strlen(filename)+1, filename);
695 }
696
697 void
698 sync_pipe_errmsg_to_parent(const char *errmsg)
699 {
700     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_errmsg_to_parent: %s", errmsg);
701
702     pipe_write_block(1, SP_ERROR_MSG, strlen(errmsg)+1, errmsg);
703 }
704
705 void
706 sync_pipe_drops_to_parent(int drops)
707 {
708     char tmp[SP_DECISIZE+1+1];
709
710
711     g_snprintf(tmp, sizeof(tmp), "%d", drops);
712
713     g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "sync_pipe_drops_to_parent: %s", tmp);
714
715     pipe_write_block(1, SP_DROPS, strlen(tmp)+1, tmp);
716 }
717
718
719
720 /****************************************************************************************************************/
721 /* simple_dialog stubs */
722
723
724 char *simple_dialog_primary_start(void)
725 {
726     return "";
727 }
728
729 char *simple_dialog_primary_end(void)
730 {
731     return "";
732 }
733
734 char *
735 simple_dialog_format_message(const char *msg)
736 {
737     char *str;
738
739     if (msg) {
740 #if GTK_MAJOR_VERSION < 2
741         str = g_strdup(msg);
742 #else
743         str = xml_escape(msg);
744 #endif
745     } else {
746         str = NULL;
747     }
748     return str;
749 }
750
751 /****************************************************************************************************************/
752 /* Stub functions */
753
754
755 const char *netsnmp_get_version(void) { return ""; }
756
757 gboolean dfilter_compile(const gchar *text, dfilter_t **dfp) { (void)text; (void)dfp; return FALSE; }
758
759 void dfilter_free(dfilter_t *df) { (void)df; }
760
761
762 /*
763  * Find out whether a hostname resolves to an ip or ipv6 address
764  * Return "ip6" if it is IPv6, "ip" otherwise (including the case
765  * that we don't know)
766  */
767 const char* host_ip_af(const char *host
768 #ifndef HAVE_GETHOSTBYNAME2
769 _U_
770 #endif
771 )
772 {
773 #ifdef HAVE_GETHOSTBYNAME2
774         struct hostent *h;
775         return (h = gethostbyname2(host, AF_INET6)) && h->h_addrtype == AF_INET6 ? "ip6" : "ip";
776 #else
777         return "ip";
778 #endif
779 }
780
781
782 /****************************************************************************************************************/
783 /* functions copied from epan */
784
785 /*
786  * Given a pathname, return a pointer to the last pathname separator
787  * character in the pathname, or NULL if the pathname contains no
788  * separators.
789  */
790 static char *
791 find_last_pathname_separator(const char *path)
792 {
793         char *separator;
794
795 #ifdef _WIN32
796         char c;
797
798         /*
799          * We have to scan for '\' or '/'.
800          * Get to the end of the string.
801          */
802         separator = strchr(path, '\0');         /* points to ending '\0' */
803         while (separator > path) {
804                 c = *--separator;
805                 if (c == '\\' || c == '/')
806                         return separator;       /* found it */
807         }
808
809         /*
810          * OK, we didn't find any, so no directories - but there might
811          * be a drive letter....
812          */
813         return strchr(path, ':');
814 #else
815         separator = strrchr(path, '/');
816 #endif
817         return separator;
818 }
819
820 /*
821  * Given a pathname, return the last component.
822  */
823 const char *
824 get_basename(const char *path)
825 {
826         const char *filename;
827
828         g_assert(path != NULL);
829         filename = find_last_pathname_separator(path);
830         if (filename == NULL) {
831                 /*
832                  * There're no directories, drive letters, etc. in the
833                  * name; the pathname *is* the file name.
834                  */
835                 filename = path;
836         } else {
837                 /*
838                  * Skip past the pathname or drive letter separator.
839                  */
840                 filename++;
841         }
842         return filename;
843 }
844