4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
37 #include <wsutil/file_util.h>
40 #include "ft_file_wrappers.h"
44 * Add an extension, and all compressed versions thereof, to a GSList
47 static GSList *add_extensions(GSList *extensions, const gchar *extension,
48 GSList *compressed_file_extensions)
50 GSList *compressed_file_extension;
53 * Add the specified extension.
55 extensions = g_slist_append(extensions, g_strdup(extension));
58 * Now add the extensions for compressed-file versions of
61 for (compressed_file_extension = compressed_file_extensions;
62 compressed_file_extension != NULL;
63 compressed_file_extension = g_slist_next(compressed_file_extension)) {
64 extensions = g_slist_append(extensions,
65 g_strdup_printf("%s.%s", extension,
66 (gchar *)compressed_file_extension->data));
73 * File types that can be identified by file extensions.
75 static const struct filetap_extension_info file_type_extensions_base[] = {
76 { "Wireshark/tcpdump/... - pcap", "pcap;cap;dmp" },
79 #define N_FILE_TYPE_EXTENSIONS (sizeof file_type_extensions_base / sizeof file_type_extensions_base[0])
81 static const struct filetap_extension_info* file_type_extensions = NULL;
83 static GArray* file_type_extensions_arr = NULL;
85 /* initialize the extensions array if it has not been initialized yet */
86 static void init_file_type_extensions(void) {
88 if (file_type_extensions_arr) return;
90 file_type_extensions_arr = g_array_new(FALSE,TRUE,sizeof(struct filetap_extension_info));
92 g_array_append_vals(file_type_extensions_arr,file_type_extensions_base,N_FILE_TYPE_EXTENSIONS);
94 file_type_extensions = (struct filetap_extension_info*)(void *)file_type_extensions_arr->data;
97 void ftap_register_file_type_extension(const struct filetap_extension_info *ei) {
98 init_file_type_extensions();
100 g_array_append_val(file_type_extensions_arr,*ei);
102 file_type_extensions = (const struct filetap_extension_info*)(void *)file_type_extensions_arr->data;
105 int ftap_get_num_file_type_extensions(void)
107 return file_type_extensions_arr->len;
110 const char *ftap_get_file_extension_type_name(int extension_type)
112 return file_type_extensions[extension_type].name;
115 static GSList *add_extensions_for_file_extensions_type(int extension_type,
116 GSList *extensions, GSList *compressed_file_extensions)
118 gchar **extensions_set, **extensionp, *extension;
121 * Split the extension-list string into a set of extensions.
123 extensions_set = g_strsplit(file_type_extensions[extension_type].extensions,
127 * Add each of those extensions to the list.
129 for (extensionp = extensions_set; *extensionp != NULL; extensionp++) {
130 extension = *extensionp;
133 * Add the extension, and all compressed variants
136 extensions = add_extensions(extensions, extension,
137 compressed_file_extensions);
140 g_strfreev(extensions_set);
144 /* Return a list of file extensions that are used by the specified file
147 All strings in the list are allocated with g_malloc() and must be freed
149 GSList *ftap_get_file_extension_type_extensions(guint extension_type)
151 GSList *compressed_file_extensions;
154 if (extension_type >= file_type_extensions_arr->len)
155 return NULL; /* not a valid extension type */
157 extensions = NULL; /* empty list, to start with */
160 * Get the list of compressed-file extensions.
162 compressed_file_extensions = ftap_get_compressed_file_extensions();
165 * Add all this file extension type's extensions, with compressed
168 extensions = add_extensions_for_file_extensions_type(extension_type,
169 extensions, compressed_file_extensions);
171 g_slist_free(compressed_file_extensions);
175 /* Return a list of all extensions that are used by all file types,
176 including compressed extensions, e.g. not just "pcap" but also
177 "pcap.gz" if we can read gzipped files.
179 All strings in the list are allocated with g_malloc() and must be freed
181 GSList *ftap_get_all_file_extensions_list(void)
183 GSList *compressed_file_extensions;
187 init_file_type_extensions();
189 extensions = NULL; /* empty list, to start with */
192 * Get the list of compressed-file extensions.
194 compressed_file_extensions = ftap_get_compressed_file_extensions();
196 for (i = 0; i < file_type_extensions_arr->len; i++) {
198 * Add all this file extension type's extensions, with
199 * compressed variants.
201 extensions = add_extensions_for_file_extensions_type(i,
202 extensions, compressed_file_extensions);
205 g_slist_free(compressed_file_extensions);
209 static int empty_open(ftap *wth _U_, int *err _U_, gchar **err_info _U_)
214 /* The open_file_* routines should return:
216 * -1 on an I/O error;
218 * 1 if the file they're reading is one of the types it handles;
220 * 0 if the file they're reading isn't the type they're checking for.
222 * If the routine handles this type of file, it should set the "file_type"
223 * field in the "struct ftap" to the type of the file.
225 * Note that the routine does not have to free the private data pointer on
226 * error. The caller takes care of that by calling ftap_close on error.
227 * (See https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8518)
229 * However, the caller does have to free the private data pointer when
230 * returning 0, since the next file type will be called and will likely
231 * just overwrite the pointer.
234 /* Files that have magic bytes in fixed locations. These
235 * are easy to identify. Only an open routine is needed.
237 static const ftap_open_routine_t magic_number_open_routines_base[] = {
238 empty_open/* libpcap_open, */
240 #define N_MAGIC_FILE_TYPES (sizeof magic_number_open_routines_base / sizeof magic_number_open_routines_base[0])
242 static ftap_open_routine_t* magic_number_open_routines = NULL;
244 static GArray* magic_number_open_routines_arr = NULL;
247 * Initialize the magic-number open routines array if it has not been
250 static void init_magic_number_open_routines(void) {
252 if (magic_number_open_routines_arr) return;
254 magic_number_open_routines_arr = g_array_new(FALSE,TRUE,sizeof(ftap_open_routine_t));
256 g_array_append_vals(magic_number_open_routines_arr,magic_number_open_routines_base,N_MAGIC_FILE_TYPES);
258 magic_number_open_routines = (ftap_open_routine_t*)(void *)magic_number_open_routines_arr->data;
261 void ftap_register_magic_number_open_routine(ftap_open_routine_t open_routine) {
262 init_magic_number_open_routines();
264 g_array_append_val(magic_number_open_routines_arr,open_routine);
266 magic_number_open_routines = (ftap_open_routine_t*)(void *)magic_number_open_routines_arr->data;
269 /* Files that don't have magic bytes at a fixed location,
270 * but that instead require a heuristic of some sort to
271 * identify them. This includes ASCII trace files.
273 * Entries for the ASCII trace files that would be, for example,
274 * saved copies of a Telnet session to some box are put after
275 * most of the other entries, as we don't want to treat a capture
276 * of such a session as a trace file from such a session
277 * merely because it has the right text in it. They still
278 * appear before the *really* weak entries, such as the VWR entry.
280 static const struct ftap_heuristic_open_info heuristic_open_info_base[] = {
281 { NULL, "(empty)", NULL},
283 #define N_HEURISTIC_FILE_TYPES (sizeof heuristic_open_info_base / sizeof heuristic_open_info_base[0])
285 static struct ftap_heuristic_open_info* heuristic_open_info = NULL;
287 static GArray* heuristic_open_info_arr = NULL;
290 * Initialize the heuristics array if it has not been initialized yet.
292 static void init_heuristic_open_info(void) {
294 struct ftap_heuristic_open_info *i_open;
296 if (heuristic_open_info_arr)
299 heuristic_open_info_arr = g_array_new(FALSE,TRUE,sizeof(struct ftap_heuristic_open_info));
301 g_array_append_vals(heuristic_open_info_arr,heuristic_open_info_base,N_HEURISTIC_FILE_TYPES);
303 heuristic_open_info = (struct ftap_heuristic_open_info*)(void *)heuristic_open_info_arr->data;
305 /* Populate the extensions_set list now */
306 for (i = 0, i_open = heuristic_open_info; i < heuristic_open_info_arr->len; i++, i_open++) {
307 if (i_open->extensions != NULL)
308 i_open->extensions_set = g_strsplit(i_open->extensions, ";", 0);
312 void ftap_register_heuristic_open_info(struct ftap_heuristic_open_info *hi) {
313 init_heuristic_open_info();
315 g_array_append_val(heuristic_open_info_arr,*hi);
317 if (hi->extensions != NULL)
318 hi->extensions_set = g_strsplit(hi->extensions, ";", 0);
320 heuristic_open_info = (struct ftap_heuristic_open_info*)(void *)heuristic_open_info_arr->data;
324 * Visual C++ on Win32 systems doesn't define these. (Old UNIX systems don't
325 * define them either.)
327 * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
330 #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
333 #define S_IFIFO _S_IFIFO
336 #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
339 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
342 static char *get_file_extension(const char *pathname)
347 GSList *compressed_file_extensions, *compressed_file_extension;
351 * Is the pathname empty?
353 if (strcmp(pathname, "") == 0)
354 return NULL; /* no extension */
357 * Find the last component of the pathname.
359 filename = g_path_get_basename(pathname);
362 * Does it have an extension?
364 if (strchr(filename, '.') == NULL) {
366 return NULL; /* no extension whatsoever */
370 * Yes. Split it into components separated by ".".
372 components = g_strsplit(filename, ".", 0);
376 * Count the components.
378 for (ncomponents = 0; components[ncomponents] != NULL; ncomponents++)
381 if (ncomponents == 0) {
382 g_strfreev(components);
383 return NULL; /* no components */
385 if (ncomponents == 1) {
386 g_strfreev(components);
387 return NULL; /* only one component, with no "." */
391 * Is the last component one of the extensions used for compressed
394 compressed_file_extensions = ftap_get_compressed_file_extensions();
395 if (compressed_file_extensions == NULL) {
397 * We don't support reading compressed files, so just
398 * return a copy of whatever extension we did find.
400 extensionp = g_strdup(components[ncomponents - 1]);
401 g_strfreev(components);
404 extensionp = components[ncomponents - 1];
405 for (compressed_file_extension = compressed_file_extensions;
406 compressed_file_extension != NULL;
407 compressed_file_extension = g_slist_next(compressed_file_extension)) {
408 if (strcmp(extensionp, (char *)compressed_file_extension->data) == 0) {
410 * Yes, it's one of the compressed-file extensions.
411 * Is there an extension before that?
413 if (ncomponents == 2) {
414 g_strfreev(components);
415 return NULL; /* no, only two components */
419 * Yes, return that extension.
421 extensionp = g_strdup(components[ncomponents - 2]);
422 g_strfreev(components);
428 * The extension isn't one of the compressed-file extensions;
431 extensionp = g_strdup(extensionp);
432 g_strfreev(components);
436 static gboolean heuristic_uses_extension(unsigned int i, const char *extension)
441 * Does this file type *have* any extensions?
443 if (heuristic_open_info[i].extensions == NULL)
444 return FALSE; /* no */
447 * Check each of them against the specified extension.
449 for (extensionp = heuristic_open_info[i].extensions_set; *extensionp != NULL;
451 if (strcmp(extension, *extensionp) == 0) {
452 return TRUE; /* it's one of them */
455 return FALSE; /* it's not one of them */
458 /* Opens a file and prepares a ftap struct.
459 If "do_random" is TRUE, it opens the file twice; the second open
460 allows the application to do random-access I/O without moving
461 the seek offset for sequential I/O, which is used by Wireshark
462 so that it can do sequential I/O to a capture file that's being
463 written to as new packets arrive independently of random I/O done
464 to display protocol trees for packets when they're selected. */
465 ftap* ftap_open_offline(const char *filename, int *err, char **err_info,
472 gboolean use_stdin = FALSE;
475 /* open standard input if filename is '-' */
476 if (strcmp(filename, "-") == 0)
479 /* First, make sure the file is valid */
481 if (ws_fstat64(0, &statb) < 0) {
486 if (ws_stat64(filename, &statb) < 0) {
491 if (S_ISFIFO(statb.st_mode)) {
493 * Opens of FIFOs are allowed only when not opening
496 * XXX - currently, we do seeking when trying to find
497 * out the file type, so we don't actually support
498 * opening FIFOs. However, we may eventually
499 * do buffering that allows us to do at least some
500 * file type determination even on pipes, so we
501 * allow FIFO opens and let things fail later when
505 *err = FTAP_ERR_RANDOM_OPEN_PIPE;
508 } else if (S_ISDIR(statb.st_mode)) {
510 * Return different errors for "this is a directory"
511 * and "this is some random special file type", so
512 * the user can get a potentially more helpful error.
516 } else if (! S_ISREG(statb.st_mode)) {
517 *err = FTAP_ERR_NOT_REGULAR_FILE;
522 * We need two independent descriptors for random access, so
523 * they have different file positions. If we're opening the
524 * standard input, we can only dup it to get additional
525 * descriptors, so we can't have two independent descriptors,
526 * and thus can't do random access.
528 if (use_stdin && do_random) {
529 *err = FTAP_ERR_RANDOM_OPEN_STDIN;
534 fth = (ftap *)g_malloc0(sizeof(ftap));
537 errno = FTAP_ERR_CANT_OPEN;
540 * We dup FD 0, so that we don't have to worry about
541 * a file_close of wfth->fh closing the standard
542 * input of the process.
551 if (_setmode(fd, O_BINARY) == -1) {
552 /* "Shouldn't happen" */
558 if (!(fth->fh = file_fdopen(fd))) {
565 if (!(fth->fh = file_open(filename))) {
573 if (!(fth->random_fh = file_open(filename))) {
580 fth->random_fh = NULL;
583 fth->file_encap = FTAP_ENCAP_UNKNOWN;
584 fth->subtype_sequential_close = NULL;
585 fth->subtype_close = NULL;
588 init_magic_number_open_routines();
589 init_heuristic_open_info();
590 if (fth->random_fh) {
591 fth->fast_seek = g_ptr_array_new();
593 file_set_random_access(fth->fh, FALSE, fth->fast_seek);
594 file_set_random_access(fth->random_fh, TRUE, fth->fast_seek);
597 /* Try all file types that support magic numbers */
598 for (i = 0; i < magic_number_open_routines_arr->len; i++) {
599 /* Seek back to the beginning of the file; the open routine
600 for the previous file type may have left the file
601 position somewhere other than the beginning, and the
602 open routine for this file type will probably want
603 to start reading at the beginning.
605 Initialize the data offset while we're at it. */
606 if (file_seek(fth->fh, 0, SEEK_SET, err) == -1) {
607 /* I/O error - give up */
612 switch ((*magic_number_open_routines[i])(fth, err, err_info)) {
615 /* I/O error - give up */
620 /* No I/O error, but not that type of file */
624 /* We found the file type */
629 /* Does this file's name have an extension? */
630 extension = get_file_extension(filename);
631 if (extension != NULL) {
632 /* Yes - try the heuristic types that use that extension first. */
633 for (i = 0; i < heuristic_open_info_arr->len; i++) {
634 /* Does this type use that extension? */
635 if (heuristic_uses_extension(i, extension)) {
637 if (file_seek(fth->fh, 0, SEEK_SET, err) == -1) {
638 /* I/O error - give up */
644 switch ((*heuristic_open_info[i].open_routine)(fth,
648 /* I/O error - give up */
654 /* No I/O error, but not that type of file */
658 /* We found the file type */
665 /* Now try the ones that don't use it. */
666 for (i = 0; i < heuristic_open_info_arr->len; i++) {
667 /* Does this type use that extension? */
668 if (!heuristic_uses_extension(i, extension)) {
670 if (file_seek(fth->fh, 0, SEEK_SET, err) == -1) {
671 /* I/O error - give up */
677 switch ((*heuristic_open_info[i].open_routine)(fth,
681 /* I/O error - give up */
687 /* No I/O error, but not that type of file */
691 /* We found the file type */
699 /* No - try all the heuristics types in order. */
700 for (i = 0; i < heuristic_open_info_arr->len; i++) {
701 if (file_seek(fth->fh, 0, SEEK_SET, err) == -1) {
702 /* I/O error - give up */
707 switch ((*heuristic_open_info[i].open_routine)(fth,
711 /* I/O error - give up */
716 /* No I/O error, but not that type of file */
720 /* We found the file type */
726 /* Well, it's not one of the types of file we know about. */
728 *err = FTAP_ERR_FILE_UNKNOWN_FORMAT;
732 fth->frame_buffer = (struct Buffer *)g_malloc(sizeof(struct Buffer));
733 buffer_init(fth->frame_buffer, 1500);
739 * Given the pathname of the file we just closed with ftap_fdclose(), attempt
740 * to reopen that file and assign the new file descriptor(s) to the sequential
741 * stream and, if do_random is TRUE, to the random stream. Used on Windows
742 * after the rename of a file we had open was done or if the rename of a
743 * file on top of a file we had open failed.
745 * This is only required by Wireshark, not TShark, and, at the point that
746 * Wireshark is doing this, the sequential stream is closed, and the
747 * random stream is open, so this refuses to open pipes, and only
748 * reopens the random stream.
751 ftap_fdreopen(ftap *fth, const char *filename, int *err)
756 * We need two independent descriptors for random access, so
757 * they have different file positions. If we're opening the
758 * standard input, we can only dup it to get additional
759 * descriptors, so we can't have two independent descriptors,
760 * and thus can't do random access.
762 if (strcmp(filename, "-") == 0) {
763 *err = FTAP_ERR_RANDOM_OPEN_STDIN;
767 /* First, make sure the file is valid */
768 if (ws_stat64(filename, &statb) < 0) {
772 if (S_ISFIFO(statb.st_mode)) {
774 * Opens of FIFOs are not allowed; see above.
776 *err = FTAP_ERR_RANDOM_OPEN_PIPE;
778 } else if (S_ISDIR(statb.st_mode)) {
780 * Return different errors for "this is a directory"
781 * and "this is some random special file type", so
782 * the user can get a potentially more helpful error.
786 } else if (! S_ISREG(statb.st_mode)) {
787 *err = FTAP_ERR_NOT_REGULAR_FILE;
792 errno = FTAP_ERR_CANT_OPEN;
793 if (!file_fdreopen(fth->random_fh, filename)) {
800 /* Table of the file types we know about.
801 Entries must be sorted by FTAP_FILE_TYPE_SUBTYPE_xxx values in ascending order */
802 static const struct ftap_file_type_subtype_info dump_open_table_base[] = {
803 /* FTAP_FILE_TYPE_SUBTYPE_UNKNOWN (only used internally for initialization) */
804 { NULL, NULL, NULL, NULL },
807 gint ftap_num_file_types_subtypes = sizeof(dump_open_table_base) / sizeof(struct ftap_file_type_subtype_info);
809 static GArray* dump_open_table_arr = NULL;
810 static const struct ftap_file_type_subtype_info* dump_open_table = dump_open_table_base;
812 /* initialize the file types array if it has not being initialized yet */
813 static void init_file_types_subtypes(void) {
815 if (dump_open_table_arr) return;
817 dump_open_table_arr = g_array_new(FALSE,TRUE,sizeof(struct ftap_file_type_subtype_info));
819 g_array_append_vals(dump_open_table_arr,dump_open_table_base,ftap_num_file_types_subtypes);
821 dump_open_table = (const struct ftap_file_type_subtype_info*)(void *)dump_open_table_arr->data;
824 int ftap_register_file_type_subtypes(const struct ftap_file_type_subtype_info* fi) {
825 init_file_types_subtypes();
827 g_array_append_val(dump_open_table_arr,*fi);
829 dump_open_table = (const struct ftap_file_type_subtype_info*)(void *)dump_open_table_arr->data;
831 return ftap_num_file_types_subtypes++;
834 int ftap_get_num_file_types_subtypes(void)
836 return ftap_num_file_types_subtypes;
840 * Given a GArray of FTAP_ENCAP_ types, return the per-file encapsulation
841 * type that would be needed to write out a file with those types. If
842 * there's only one type, it's that type, otherwise it's
843 * FTAP_ENCAP_PER_RECORD.
846 ftap_dump_file_encap_type(const GArray *file_encaps)
850 encap = FTAP_ENCAP_PER_RECORD;
851 if (file_encaps->len == 1) {
852 /* OK, use the one-and-only encapsulation type. */
853 encap = g_array_index(file_encaps, gint, 0);
858 /* Name that should be somewhat descriptive. */
859 const char *ftap_file_type_subtype_string(int file_type_subtype)
861 if (file_type_subtype < 0 || file_type_subtype >= ftap_num_file_types_subtypes) {
862 g_error("Unknown capture file type %d", file_type_subtype);
863 /** g_error() does an abort() and thus never returns **/
866 return dump_open_table[file_type_subtype].name;
869 /* Name to use in, say, a command-line flag specifying the type/subtype. */
870 const char *ftap_file_type_subtype_short_string(int file_type_subtype)
872 if (file_type_subtype < 0 || file_type_subtype >= ftap_num_file_types_subtypes)
875 return dump_open_table[file_type_subtype].short_name;
878 /* Translate a short name to a capture file type/subtype. */
879 int ftap_short_string_to_file_type_subtype(const char *short_name)
881 int file_type_subtype;
883 for (file_type_subtype = 0; file_type_subtype < ftap_num_file_types_subtypes; file_type_subtype++) {
884 if (dump_open_table[file_type_subtype].short_name != NULL &&
885 strcmp(short_name, dump_open_table[file_type_subtype].short_name) == 0)
886 return file_type_subtype;
889 return -1; /* no such file type, or we can't write it */
893 add_extensions_for_file_type_subtype(int file_type_subtype, GSList *extensions,
894 GSList *compressed_file_extensions)
896 gchar **extensions_set, **extensionp;
900 * Add the default extension, and all compressed variants of
903 extensions = add_extensions(extensions,
904 dump_open_table[file_type_subtype].default_file_extension,
905 compressed_file_extensions);
907 if (dump_open_table[file_type_subtype].additional_file_extensions != NULL) {
909 * We have additional extensions; add them.
911 * First, split the extension-list string into a set of
914 extensions_set = g_strsplit(dump_open_table[file_type_subtype].additional_file_extensions,
918 * Add each of those extensions to the list.
920 for (extensionp = extensions_set; *extensionp != NULL;
922 extension = *extensionp;
925 * Add the extension, and all compressed variants
928 extensions = add_extensions(extensions, extension,
929 compressed_file_extensions);
932 g_strfreev(extensions_set);
937 /* Return a list of file extensions that are used by the specified file type.
939 If include_compressed is TRUE, the list will include compressed
940 extensions, e.g. not just "pcap" but also "pcap.gz" if we can read
943 All strings in the list are allocated with g_malloc() and must be freed
945 GSList *ftap_get_file_extensions_list(int file_type_subtype, gboolean include_compressed)
947 GSList *compressed_file_extensions;
950 if (file_type_subtype < 0 || file_type_subtype >= ftap_num_file_types_subtypes)
951 return NULL; /* not a valid file type */
953 if (dump_open_table[file_type_subtype].default_file_extension == NULL)
954 return NULL; /* valid, but no extensions known */
956 extensions = NULL; /* empty list, to start with */
959 * If include_compressions is true, get the list of compressed-file
962 if (include_compressed)
963 compressed_file_extensions = ftap_get_compressed_file_extensions();
965 compressed_file_extensions = NULL;
968 * Add all this file type's extensions, with compressed
971 extensions = add_extensions_for_file_type_subtype(file_type_subtype, extensions,
972 compressed_file_extensions);
974 g_slist_free(compressed_file_extensions);
979 * Free a list returned by ftap_get_file_extension_type_extensions(),
980 * ftap_get_all_file_extensions_list, or ftap_get_file_extensions_list().
982 void ftap_free_extensions_list(GSList *extensions)
986 for (extension = extensions; extension != NULL;
987 extension = g_slist_next(extension)) {
988 g_free(extension->data);
990 g_slist_free(extensions);
993 /* Return the default file extension to use with the specified file type;
994 that's just the extension, without any ".". */
995 const char *ftap_default_file_extension(int file_type_subtype)
997 if (file_type_subtype < 0 || file_type_subtype >= ftap_num_file_types_subtypes)
1000 return dump_open_table[file_type_subtype].default_file_extension;