* - Opens a specified file or named pipe
* - Applies a specfied DLT or "decode as" encapsulation
* - Reads frames prepended with a libpcap packet header.
- * - Prints a status line, followed by fields from a specified list.
+ * - Prints a status line, followed by fields from a specified list.
*/
#ifdef HAVE_CONFIG_H
#include "strerror.h"
#endif
-#ifdef NEED_GETOPT_H
-#include "getopt.h"
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+#include "wsgetopt.h"
#endif
#include <glib.h>
#include "clopts_common.h"
#include "cmdarg_err.h"
#include "version_info.h"
-#include <epan/conversation.h>
#include <epan/plugins.h>
#include "register.h"
#include "conditions.h"
*/
static const gchar decode_as_arg_template[] = "<layer_type>==<selector>,<decode_as_protocol>";
+static guint32 cum_bytes;
static nstime_t first_ts;
static nstime_t prev_dis_ts;
static nstime_t prev_cap_ts;
-static GString *comp_info_str, *runtime_info_str;
/*
* The way the packet decode is to be written.
WRITE_XML /* PDML or PSML */
/* Add CSV and the like here */
} output_action_e;
+
static gboolean line_buffered;
-static guint32 cum_bytes = 0;
static print_format_e print_format = PR_FMT_TEXT;
/*
gboolean for_writing);
static void failure_message(const char *msg_format, va_list ap);
static void read_failure_message(const char *filename, int err);
+static void write_failure_message(const char *filename, int err);
static void protocolinfo_init(char *field);
static gboolean parse_field_string_format(char *format);
fprintf(output, "Processing:\n");
fprintf(output, " -R <read filter> packet filter in Wireshark display filter syntax\n");
fprintf(output, " -F <field> field to display\n");
+ fprintf(output, " -s skip PCAP header on input\n");
fprintf(output, " -n disable all name resolution (def: all enabled)\n");
fprintf(output, " -N <name resolve flags> enable specific name resolution(s): \"mntC\"\n");
fprintf(output, " -d <encap:dlt>|<proto:protoname>\n");
/*fprintf(output, "\n");*/
fprintf(output, "Output:\n");
- fprintf(output, " -S format string for fields (%%D - name, %%S - stringval, %%N numval\n");
+ fprintf(output, " -S format string for fields (%%D - name, %%S - stringval, %%N numval)\n");
fprintf(output, " -t ad|a|r|d|dd|e output format of time stamps (def: r: rel. to first)\n");
fprintf(output, " -l flush output after each packet\n");
struct stat pipe_stat;
#else
char *pncopy, *pos;
+ DWORD err;
wchar_t *err_str;
HANDLE hPipe = NULL;
#endif
} else {
#ifndef _WIN32
if (ws_stat(pipe_name, &pipe_stat) < 0) {
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "The capture session could not be initiated "
- "due to error on pipe: %s", strerror(errno));
+ fprintf(stderr, "rawshark: The pipe %s could not be checked: %s\n",
+ pipe_name, strerror(errno));
return -1;
}
if (! S_ISFIFO(pipe_stat.st_mode)) {
*/
} else
{
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "The capture session could not be initiated because\n"
- "\"%s\" is neither an interface nor a pipe", pipe_name);
+ fprintf(stderr, "rawshark: \"%s\" is neither an interface nor a pipe\n",
+ pipe_name);
}
return -1;
}
rfd = ws_open(pipe_name, O_RDONLY | O_NONBLOCK, 0000 /* no creation so don't matter */);
if (rfd == -1) {
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "The capture session could not be initiated "
- "due to error on pipe open: %s", strerror(errno));
+ fprintf(stderr, "rawshark: \"%s\" could not be opened: %s\n",
+ pipe_name, strerror(errno));
return -1;
}
#else /* _WIN32 */
g_free(pncopy);
if (!pos) {
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "The capture session could not be initiated because\n"
- "\"%s\" is neither an interface nor a pipe", pipe_name);
+ fprintf(stderr, "rawshark: \"%s\" is neither an interface nor a pipe\n",
+ pipe_name);
return -1;
}
if (hPipe != INVALID_HANDLE_VALUE)
break;
- if (GetLastError() != ERROR_PIPE_BUSY) {
+ err = GetLastError();
+ if (err != ERROR_PIPE_BUSY) {
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
- NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "The capture session on \"%s\" could not be initiated "
- "due to error on pipe open: pipe busy: %s (error %d)",
- pipe_name, utf_16to8(err_str), GetLastError());
+ NULL, err, 0, (LPTSTR) &err_str, 0, NULL);
+ fprintf(stderr, "rawshark: \"%s\" could not be opened: %s (error %d)\n",
+ pipe_name, utf_16to8(err_str), err);
LocalFree(err_str);
return -1;
}
if (!WaitNamedPipe(utf_8to16(pipe_name), 30 * 1000)) {
+ err = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
- NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "The capture session could not be initiated "
- "due to error on pipe open: %s (error %d)",
- utf_16to8(err_str), GetLastError());
+ NULL, err, 0, (LPTSTR) &err_str, 0, NULL);
+ fprintf(stderr, "rawshark: \"%s\" could not be waited for: %s (error %d)\n",
+ pipe_name, utf_16to8(err_str), err);
LocalFree(err_str);
return -1;
}
rfd = _open_osfhandle((long) hPipe, _O_RDONLY);
if (rfd == -1) {
- g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG,
- "The capture session could not be initiated "
- "due to error on pipe open: %s", strerror(errno));
+ fprintf(stderr, "rawshark: \"%s\" could not be opened: %s\n",
+ pipe_name, strerror(errno));
return -1;
}
#endif /* _WIN32 */
static gboolean
set_link_type(const char *lt_arg) {
char *spec_ptr = strchr(lt_arg, ':');
+ char *p;
int dlt_val;
+ long val;
dissector_handle_t dhandle;
GString *pref_str;
spec_ptr++;
if (strncmp(lt_arg, "encap:", strlen("encap:")) == 0) {
-#ifdef HAVE_PCAP_DATALINK_NAME_TO_VAL
dlt_val = linktype_name_to_val(spec_ptr);
- if (dlt_val >= 0) {
- encap = dlt_val;
- return TRUE;
- }
-#endif
- dlt_val = strtol(spec_ptr, NULL, 10);
- if (errno != EINVAL && dlt_val >= 0) {
- encap = wtap_pcap_encap_to_wtap_encap(dlt_val);
- if (encap == WTAP_ENCAP_UNKNOWN) {
+ if (dlt_val == -1) {
+ errno = 0;
+ val = strtol(spec_ptr, &p, 10);
+ if (p == spec_ptr || *p != '\0' || errno != 0 || val > INT_MAX) {
return FALSE;
}
- return TRUE;
+ dlt_val = (int)val;
+ }
+ encap = wtap_pcap_encap_to_wtap_encap(dlt_val);
+ if (encap == WTAP_ENCAP_UNKNOWN) {
+ return FALSE;
}
+ return TRUE;
} else if (strncmp(lt_arg, "proto:", strlen("proto:")) == 0) {
dhandle = find_dissector(spec_ptr);
if (dhandle) {
return FALSE;
}
+static void
+show_version(GString *comp_info_str, GString *runtime_info_str)
+{
+ printf("Rawshark " VERSION "%s\n"
+ "\n"
+ "%s"
+ "\n"
+ "%s"
+ "\n"
+ "%s",
+ wireshark_svnversion, get_copyright_info(), comp_info_str->str,
+ runtime_info_str->str);
+}
+
int
main(int argc, char *argv[])
{
char *init_progfile_dir_error;
int opt, i;
- extern char *optarg;
gboolean arg_error = FALSE;
#ifdef _WIN32
GLogLevelFlags log_flags;
GPtrArray *disp_fields = g_ptr_array_new();
guint fc;
+ gboolean skip_pcap_header = FALSE;
- #define OPTSTRING_INIT "d:F:hlnN:o:r:R:S:t:v"
+ #define OPTSTRING_INIT "d:F:hlnN:o:r:R:sS:t:v"
static const char optstring[] = OPTSTRING_INIT;
/*
* Attempt to get the pathname of the executable file.
*/
- init_progfile_dir_error = init_progfile_dir(argv[0]);
+ init_progfile_dir_error = init_progfile_dir(argv[0], main);
if (init_progfile_dir_error != NULL) {
fprintf(stderr, "rawshark: Can't get pathname of rawshark program: %s.\n",
init_progfile_dir_error);
dissectors, and we must do it before we read the preferences, in
case any dissectors register preferences. */
epan_init(register_all_protocols, register_all_protocol_handoffs, NULL, NULL,
- failure_message, open_failure_message, read_failure_message);
+ failure_message, open_failure_message, read_failure_message,
+ write_failure_message);
/* Now register the preferences for any non-dissector modules.
We must do that before we read the preferences as well. */
load_wpcap();
#endif
- init_cap_file(&cfile);
-
- /* Assemble the compile-time version information string */
- comp_info_str = g_string_new("Compiled ");
- get_compiled_version_info(comp_info_str, get_epan_compiled_version_info);
-
- /* Assemble the run-time version information string */
- runtime_info_str = g_string_new("Running ");
- get_runtime_version_info(runtime_info_str, NULL);
+ cap_file_init(&cfile);
/* Print format defaults to this. */
print_format = PR_FMT_TEXT;
exit(1);
}
break;
+ case 's': /* Skip PCAP header */
+ skip_pcap_header = TRUE;
+ break;
case 'S': /* Print string representations */
if (!parse_field_string_format(optarg)) {
cmdarg_err("Invalid field string format");
}
break;
case 'v': /* Show version and exit */
- printf("Rawshark " VERSION "%s\n"
- "\n"
- "%s"
- "\n"
- "%s"
- "\n"
- "%s",
- wireshark_svnversion, get_copyright_info(), comp_info_str->str,
- runtime_info_str->str);
+ {
+ GString *comp_info_str;
+ GString *runtime_info_str;
+ /* Assemble the compile-time version information string */
+ comp_info_str = g_string_new("Compiled ");
+ get_compiled_version_info(comp_info_str, get_epan_compiled_version_info);
+
+ /* Assemble the run-time version information string */
+ runtime_info_str = g_string_new("Running ");
+ get_runtime_version_info(runtime_info_str, NULL);
+ show_version(comp_info_str, runtime_info_str);
+ g_string_free(comp_info_str, TRUE);
+ g_string_free(runtime_info_str, TRUE);
exit(0);
break;
-
+ }
default:
case '?': /* Bad flag - print usage message */
print_usage(TRUE);
}
}
+ /* Notify all registered modules that have had any of their preferences
+ changed either from one of the preferences file or from the command
+ line that their preferences have changed.
+ Initialize preferences before display filters, otherwise modules
+ like MATE won't work. */
+ prefs_apply_all();
+
/* Initialize our display fields */
for (fc = 0; fc < disp_fields->len; fc++) {
protocolinfo_init(g_ptr_array_index(disp_fields, fc));
WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
#endif /* _WIN32 */
- /* Notify all registered modules that have had any of their preferences
- changed either from one of the preferences file or from the command
- line that their preferences have changed. */
- prefs_apply_all();
-
/* At this point MATE will have registered its field array so we can
have a tap filter with one of MATE's late-registered fields as part
of the filter. We can now process all the "-z" arguments. */
}
/* Build the column format array */
- col_setup(&cfile.cinfo, prefs->num_cols);
- for (i = 0; i < cfile.cinfo.num_cols; i++) {
- cfile.cinfo.col_fmt[i] = get_column_format(i);
- cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
- cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
- NUM_COL_FMTS);
- get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
- cfile.cinfo.col_data[i] = NULL;
- if (cfile.cinfo.col_fmt[i] == COL_INFO)
- cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
- else
- cfile.cinfo.col_buf[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
- cfile.cinfo.col_fence[i] = 0;
- cfile.cinfo.col_expr.col_expr[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
- cfile.cinfo.col_expr.col_expr_val[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
- }
-
- for (i = 0; i < cfile.cinfo.num_cols; i++) {
- int j;
-
- for (j = 0; j < NUM_COL_FMTS; j++) {
- if (!cfile.cinfo.fmt_matx[i][j])
- continue;
-
- if (cfile.cinfo.col_first[j] == -1)
- cfile.cinfo.col_first[j] = i;
- cfile.cinfo.col_last[j] = i;
- }
- }
+ build_column_format_array(&cfile.cinfo, prefs->num_cols, TRUE);
if (n_rfilters != 0) {
for (i = 0; i < n_rfilters; i++) {
epan_cleanup();
exit(2);
}
- n_rfcodes++;
+ n_rfcodes++;
}
}
exit(2);
}
+ /* Do we need to PCAP header and magic? */
+ if (skip_pcap_header) {
+ guint bytes_left = sizeof(struct pcap_hdr) + sizeof(guint32);
+ gchar buf[sizeof(struct pcap_hdr) + sizeof(guint32)];
+ while (bytes_left > 0) {
+ guint bytes = read(fd, buf, bytes_left);
+ if (bytes <= 0) {
+ cmdarg_err("Not enough bytes for pcap header.");
+ exit(2);
+ }
+ bytes_left -= bytes;
+ }
+ }
+
/* Set timestamp precision; there should arguably be a command-line
option to let the user set this. */
#if 0
*/
static gboolean
raw_pipe_read(struct wtap_pkthdr *phdr, guchar * pd, int *err, gchar **err_info, gint64 *data_offset) {
- struct pcap_pkthdr hdr;
+ struct pcaprec_hdr hdr;
int bytes_read = 0;
- int bytes_needed = sizeof(struct pcap_pkthdr);
+ int bytes_needed = sizeof(struct pcaprec_hdr);
+ guchar *ptr = (guchar*)&hdr;
static gchar err_str[100];
/* Copied from capture_loop.c */
while (bytes_needed > 0) {
- bytes_read = read(fd, &hdr, bytes_needed);
+ bytes_read = read(fd, ptr, bytes_needed);
if (bytes_read == 0) {
*err = 0;
return FALSE;
}
bytes_needed -= bytes_read;
*data_offset += bytes_read;
+ ptr += bytes_read;
}
- bytes_read = 0;
- phdr->ts.secs = hdr.ts.tv_sec;
- phdr->ts.nsecs = hdr.ts.tv_usec * 1000;
- phdr->caplen = bytes_needed = hdr.caplen;
- phdr->len = hdr.len;
+ phdr->ts.secs = hdr.ts_sec;
+ phdr->ts.nsecs = hdr.ts_usec * 1000;
+ phdr->caplen = bytes_needed = hdr.incl_len;
+ phdr->len = hdr.orig_len;
phdr->pkt_encap = encap;
#if 0
return FALSE;
}
+ ptr = pd;
while (bytes_needed > 0) {
- bytes_read = read(fd, pd, bytes_needed);
+ bytes_read = read(fd, ptr, bytes_needed);
if (bytes_read == 0) {
*err = WTAP_ERR_SHORT_READ;
*err_info = "Got zero bytes reading data from pipe";
}
bytes_needed -= bytes_read;
*data_offset += bytes_read;
+ ptr += bytes_read;
}
return TRUE;
}
return err;
}
-static void
-fill_in_fdata(frame_data *fdata, capture_file *cf,
- const struct wtap_pkthdr *phdr, gint64 offset)
-{
- fdata->next = NULL;
- fdata->prev = NULL;
- fdata->pfd = NULL;
- fdata->num = cf->count;
- fdata->pkt_len = phdr->len;
- cum_bytes += phdr->len;
- fdata->cum_bytes = cum_bytes;
- fdata->cap_len = phdr->caplen;
- fdata->file_off = offset;
- fdata->lnk_t = phdr->pkt_encap;
- fdata->abs_ts.secs = phdr->ts.secs;
- fdata->abs_ts.nsecs = phdr->ts.nsecs;
- fdata->flags.passed_dfilter = 0;
- fdata->flags.encoding = CHAR_ASCII;
- fdata->flags.visited = 0;
- fdata->flags.marked = 0;
- fdata->flags.ref_time = 0;
- fdata->color_filter = NULL;
-
- /* If we don't have the time stamp of the first packet in the
- capture, it's because this is the first packet. Save the time
- stamp of this packet as the time stamp of the first packet. */
- if (nstime_is_unset(&first_ts)) {
- first_ts = fdata->abs_ts;
- }
-
- /* If we don't have the time stamp of the previous captured packet,
- it's because this is the first packet. Save the time
- stamp of this packet as the time stamp of the previous captured
- packet. */
- if (nstime_is_unset(&prev_cap_ts)) {
- prev_cap_ts = fdata->abs_ts;
- }
-
- /* Get the time elapsed between the first packet and this packet. */
- nstime_delta(&fdata->rel_ts, &fdata->abs_ts, &first_ts);
-
- /* If it's greater than the current elapsed time, set the elapsed time
- to it (we check for "greater than" so as not to be confused by
- time moving backwards). */
- if ((gint32)cf->elapsed_time.secs < fdata->rel_ts.secs
- || ((gint32)cf->elapsed_time.secs == fdata->rel_ts.secs && (gint32)cf->elapsed_time.nsecs < fdata->rel_ts.nsecs)) {
- cf->elapsed_time = fdata->rel_ts;
- }
-
- /* If we don't have the time stamp of the previous displayed packet,
- it's because this is the first packet that's being displayed. Save the time
- stamp of this packet as the time stamp of the previous displayed
- packet. */
- if (nstime_is_unset(&prev_dis_ts))
- prev_dis_ts = fdata->abs_ts;
-
- /* Get the time elapsed between the previous displayed packet and
- this packet. */
- nstime_delta(&fdata->del_dis_ts, &fdata->abs_ts, &prev_dis_ts);
-
- /* Get the time elapsed between the previous captured packet and
- this packet. */
- nstime_delta(&fdata->del_cap_ts, &fdata->abs_ts, &prev_cap_ts);
- prev_cap_ts = fdata->abs_ts;
-}
-
-/* Free up all data attached to a "frame_data" structure. */
-static void
-clear_fdata(frame_data *fdata)
-{
- if (fdata->pfd)
- g_slist_free(fdata->pfd);
-}
-
static gboolean
process_packet(capture_file *cf, gint64 offset, const struct wtap_pkthdr *whdr,
const guchar *pd)
{
frame_data fdata;
gboolean create_proto_tree;
- epan_dissect_t *edt;
+ epan_dissect_t edt;
gboolean passed;
union wtap_pseudo_header pseudo_header;
int i;
/* If we're going to print packet information, or we're going to
run a read filter, or we're going to process taps, set up to
do a dissection and do so. */
- fill_in_fdata(&fdata, cf, whdr, offset);
+ frame_data_init(&fdata, cf->count, whdr, offset, cum_bytes);
passed = TRUE;
create_proto_tree = TRUE;
/* The protocol tree will be "visible", i.e., printed, only if we're
printing packet details, which is true if we're in verbose mode ("verbose"
is true). */
- edt = epan_dissect_new(create_proto_tree, FALSE);
+ epan_dissect_init(&edt, create_proto_tree, FALSE);
/* If we're running a read filter, prime the epan_dissect_t with that
filter. */
if (n_rfilters > 0) {
for(i = 0; i < n_rfcodes; i++) {
- epan_dissect_prime_dfilter(edt, rfcodes[i]);
+ epan_dissect_prime_dfilter(&edt, rfcodes[i]);
}
}
- tap_queue_init(edt);
+ tap_queue_init(&edt);
- printf("%lu", (unsigned long int)cf->count);
+ printf("%lu", (unsigned long int) cf->count);
+
+ frame_data_set_before_dissect(&fdata, &cf->elapsed_time,
+ &first_ts, &prev_dis_ts, &prev_cap_ts);
/* We only need the columns if we're printing packet info but we're
*not* verbose; in verbose mode, we print the protocol tree, not
the protocol summary. */
- epan_dissect_run(edt, &pseudo_header, pd, &fdata, &cf->cinfo);
+ epan_dissect_run(&edt, &pseudo_header, pd, &fdata, &cf->cinfo);
+
+ tap_push_tapped_queue(&edt);
- tap_push_tapped_queue(edt);
+ frame_data_set_after_dissect(&fdata, &cum_bytes, &prev_dis_ts);
for(i = 0; i < n_rfilters; i++) {
/* Run the read filter if we have one. */
if (rfcodes[i])
- passed = dfilter_apply_edt(rfcodes[i], edt);
+ passed = dfilter_apply_edt(rfcodes[i], &edt);
else
passed = TRUE;
exit(2);
}
- epan_dissect_free(edt);
- clear_fdata(&fdata);
+ epan_dissect_cleanup(&edt);
+ frame_data_cleanup(&fdata);
return passed;
}
};
}
-char* base_display_e_to_string(base_display_e bd)
+static char* absolute_time_display_e_to_string(absolute_time_display_e atd)
+{
+ switch(atd) {
+ case ABSOLUTE_TIME_LOCAL:
+ return "ABSOLUTE_TIME_LOCAL";
+ case ABSOLUTE_TIME_UTC:
+ return "ABSOLUTE_TIME_UTC";
+ default:
+ return "n.a.";
+ }
+}
+
+static char* base_display_e_to_string(base_display_e bd)
{
switch(bd) {
case BASE_NONE:
exit(1);
}
- printf("%u %s %s - ",
- g_cmd_line_index,
- ftenum_to_string(hfi),
- base_display_e_to_string(hfi->display));
+ switch (hfi->type) {
+
+ case FT_ABSOLUTE_TIME:
+ printf("%u %s %s - ",
+ g_cmd_line_index,
+ ftenum_to_string(hfi),
+ absolute_time_display_e_to_string(hfi->display));
+ break;
+ break;
+
+ default:
+ printf("%u %s %s - ",
+ g_cmd_line_index,
+ ftenum_to_string(hfi),
+ base_display_e_to_string(hfi->display));
+ break;
+ }
rs=g_malloc(sizeof(pci_t));
rs->hf_index=hfi->id;
rs->filter=field;
rs->cmd_line_index = g_cmd_line_index++;
- error_string=register_tap_listener("frame", rs, rs->filter, NULL, protocolinfo_packet, NULL);
+ error_string=register_tap_listener("frame", rs, rs->filter, TL_REQUIRES_PROTO_TREE, NULL, protocolinfo_packet, NULL);
if(error_string){
/* error, we failed to attach to the tap. complain and clean up */
fprintf(stderr, "rawshark: Couldn't register field extraction tap: %s\n",
static gboolean
parse_field_string_format(gchar *format) {
GString *plain_s = g_string_new("");
- int len;
- int pos = 0;
+ size_t len;
+ size_t pos = 0;
if (!format) {
return FALSE;
/* The open succeeded. Fill in the information for this file. */
+ /* Cleanup all data structures used for dissection. */
+ cleanup_dissection();
/* Initialize all data structures used for dissection. */
init_dissection();
filename, strerror(err));
}
+/*
+ * Write errors are reported with an console message in Rawshark.
+ */
+static void
+write_failure_message(const char *filename, int err)
+{
+ cmdarg_err("An error occurred while writing to the file \"%s\": %s.",
+ filename, strerror(err));
+}
+
/*
* Report an error in command-line arguments.
*/