diff --git a/src/include/pv-internal.h b/src/include/pv-internal.h index 1faf224..21ea133 100644 --- a/src/include/pv-internal.h +++ b/src/include/pv-internal.h @@ -555,6 +555,7 @@ struct pvdisplay_component_s { }; void pv_error(char *, ...); +void pv_perror(char *, ...); int pv_main_loop(pvstate_t); void pv_calculate_transfer_rate(pvtransfercalc_t, readonly_pvtransferstate_t, readonly_pvcontrol_t, readonly_pvdisplay_t, bool); diff --git a/src/pv/cursor.c b/src/pv/cursor.c index 6e5c5ef..8108621 100644 --- a/src/pv/cursor.c +++ b/src/pv/cursor.c @@ -65,7 +65,7 @@ static void pv_crs_open_lockfile(pvcursorstate_t cursor, readonly_pvcontrol_t co ttydev = ttyname(fd); if (!ttydev) { if (!control->force) { - pv_error("%s: %s", _("failed to get terminal name"), strerror(errno)); + pv_perror("%s", _("failed to get terminal name")); } /* * If the terminal name is unknown, then neither IPC nor a @@ -110,7 +110,7 @@ static void pv_crs_open_lockfile(pvcursorstate_t cursor, readonly_pvcontrol_t co */ if (cursor->lock_fd < 0) { - pv_error("%s: %s: %s", cursor->lock_file, _("failed to open lock file"), strerror(errno)); + pv_perror("%s: %s", cursor->lock_file, _("failed to open lock file")); cursor->disable = true; return; } @@ -143,7 +143,7 @@ static void pv_crs_lock(pvcursorstate_t cursor, readonly_pvcontrol_t control, in lock_fd = cursor->lock_fd; } } else { - pv_error("%s: %s", _("lock attempt failed"), strerror(errno)); + pv_perror("%s", _("lock attempt failed")); return; } } @@ -405,7 +405,7 @@ void pv_crs_init(pvcursorstate_t cursor, readonly_pvcontrol_t control, pvtransie */ if (terminalfd < 0) { - pv_error("%s: %s: %s", _("failed to open terminal"), ttyfile, strerror(errno)); + pv_perror("%s: %s", ttyfile, _("failed to open terminal")); cursor->disable = true; return; } diff --git a/src/pv/display.c b/src/pv/display.c index c933a92..c61ef31 100644 --- a/src/pv/display.c +++ b/src/pv/display.c @@ -54,7 +54,7 @@ * has been displayed yet, indicating whether any errors must be preceded by * a newline. */ -/*@only@ */ static /*@null@ */ char * pv__error_prefix = NULL; +/*@only@ */ static /*@null@ */ char *pv__error_prefix = NULL; static bool pv__output_produced = false; @@ -97,6 +97,33 @@ void pv_error(char *format, ...) } +/* + * Output an error message, like pv_error(), but following the message with + * a colon, a space, and the result of strerror(errno). + */ +void pv_perror(char *format, ...) +{ + va_list ap; + int orig_errno; + + orig_errno = errno; + + if (pv__output_produced) + fprintf(stderr, "\n"); + if (NULL != pv__error_prefix) + fprintf(stderr, "%s: ", pv__error_prefix); + va_start(ap, format); + (void) vfprintf(stderr, format, ap); /* flawfinder: ignore */ + va_end(ap); + + fprintf(stderr, ": %s\n", strerror(orig_errno)); + /* + * flawfinder: this function relies on callers always having a + * static format string, not directly subject to outside influences. + */ +} + + /* * Return true if either this is the foreground process on the terminal, or * if the output is not a terminal; false otherwise. @@ -1043,7 +1070,7 @@ bool pv_format(pvprogramstatus_t status, readonly_pvcontrol_t control, readonly_ new_buffer = malloc(new_size + 16); if (NULL == new_buffer) { - pv_error("%s: %s", _("buffer allocation failed"), strerror(errno)); + pv_perror("%s", _("buffer allocation failed")); status->exit_status |= PV_ERROREXIT_MEMORY; display->display_buffer = NULL; return false; diff --git a/src/pv/elapsedtime.c b/src/pv/elapsedtime.c index e6210e8..1281ff8 100644 --- a/src/pv/elapsedtime.c +++ b/src/pv/elapsedtime.c @@ -27,7 +27,7 @@ void pv_elapsedtime_read(struct timespec *return_time) { /*@-unrecog@ *//* splint doesn't know clock_gettime. */ if (0 != clock_gettime(CLOCK_MONOTONIC, return_time)) { - pv_error("%s: %s", "clock_gettime", strerror(errno)); + pv_perror("%s", "clock_gettime"); /*@-exitarg@ *//* The special exit status is explicitly chosen. */ exit(PV_ERROREXIT_TRANSFER); /*@+exitarg@ */ diff --git a/src/pv/file.c b/src/pv/file.c index 348c2ef..d6b8107 100644 --- a/src/pv/file.c +++ b/src/pv/file.c @@ -151,9 +151,9 @@ static off_t pv_calc_total_bytes(pvstate_t state) total = end_position; } if (lseek(state->control.output_fd, 0, SEEK_SET) != 0) { - pv_error("%s: %s: %s", - NULL == state->control.output_name ? "(null)" : state->control.output_name, - _("failed to seek to start of output"), strerror(errno)); + pv_perror("%s: %s", + NULL == state->control.output_name ? "(null)" : state->control.output_name, + _("failed to seek to start of output")); state->status.exit_status |= PV_ERROREXIT_ACCESS; } /* @@ -238,7 +238,7 @@ static off_t pv_calc_total_lines(pvstate_t state) * the bounding is OK. */ if (numread < 0) { - pv_error("%s: %s", state->files.filename[file_idx], strerror(errno)); + pv_perror("%s", state->files.filename[file_idx]); state->status.exit_status |= PV_ERROREXIT_ACCESS; break; } else if (0 == numread) { @@ -256,7 +256,7 @@ static off_t pv_calc_total_lines(pvstate_t state) } if (0 != lseek(fd, 0, SEEK_SET)) { - pv_error("%s: %s", state->files.filename[file_idx], strerror(errno)); + pv_perror("%s", state->files.filename[file_idx]); state->status.exit_status |= PV_ERROREXIT_ACCESS; } @@ -302,7 +302,7 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd) if (oldfd >= 0) { if (0 != close(oldfd)) { - pv_error("%s: %s", _("failed to close file"), strerror(errno)); + pv_perror("%s", _("failed to close file")); state->status.exit_status |= PV_ERROREXIT_TRANSITION; return -1; } @@ -334,22 +334,21 @@ int pv_next_file(pvstate_t state, unsigned int filenum, int oldfd) * open symlinks would be counterintuitive. */ if (fd < 0) { - pv_error("%s: %s: %s", _("failed to read file"), next_filename, strerror(errno)); + pv_perror("%s: %s", next_filename, _("failed to read file")); state->status.exit_status |= PV_ERROREXIT_ACCESS; return -1; } } if (0 != fstat(fd, &isb)) { - pv_error("%s: %s: %s", _("failed to stat file"), - NULL == next_filename ? "-" : next_filename, strerror(errno)); + pv_perror("%s: %s", NULL == next_filename ? "-" : next_filename, _("failed to stat file")); (void) close(fd); state->status.exit_status |= PV_ERROREXIT_ACCESS; return -1; } if (0 != fstat(state->control.output_fd, &osb)) { - pv_error("%s: %s", _("failed to stat output file"), strerror(errno)); + pv_perror("%s", _("failed to stat output file")); (void) close(fd); state->status.exit_status |= PV_ERROREXIT_ACCESS; return -1; diff --git a/src/pv/loop.c b/src/pv/loop.c index d9d42f4..46897fc 100644 --- a/src/pv/loop.c +++ b/src/pv/loop.c @@ -948,7 +948,7 @@ int pv_watchfd_loop(pvstate_t state) if (kill(watching[watch_idx].pid, 0) != 0) { /* Inaccessible PID - error, mark as finished. */ - pv_error("%s %u: %s", _("pid"), watching[watch_idx].pid, strerror(errno)); + pv_perror("%s %u", _("pid"), watching[watch_idx].pid); state->status.exit_status |= PV_ERROREXIT_ACCESS; watching[watch_idx].finished = true; continue; @@ -964,7 +964,7 @@ int pv_watchfd_loop(pvstate_t state) &(watching[watch_idx].info_array)); if (rc != 0) { /* Scan failed - error, mark as finished. */ - pv_error("%s %u: %s", _("pid"), watching[watch_idx].pid, strerror(errno)); + pv_perror("%s %u", _("pid"), watching[watch_idx].pid); state->status.exit_status |= PV_ERROREXIT_ACCESS; watching[watch_idx].finished = true; } diff --git a/src/pv/remote.c b/src/pv/remote.c index fba65a2..2b6e399 100644 --- a/src/pv/remote.c +++ b/src/pv/remote.c @@ -88,7 +88,7 @@ int pv_remote_set(pvstate_t state, pid_t remote) * Check that the remote process exists. */ if (kill((pid_t) (remote), 0) != 0) { - pv_error("%u: %s", remote, strerror(errno)); + pv_perror("%s %u", _("pid"), remote); return PV_ERROREXIT_REMOTE_OR_PID; } @@ -152,7 +152,7 @@ int pv_remote_set(pvstate_t state, pid_t remote) memset(control_filename, 0, sizeof(control_filename)); control_fptr = pv_open_controlfile(control_filename, sizeof(control_filename), (pid_t) getpid(), SIGUSR2, true); if (NULL == control_fptr) { - pv_error("%s", strerror(errno)); + pv_perror("%s", control_filename); return PV_ERROREXIT_REMOTE_OR_PID; } @@ -161,14 +161,14 @@ int pv_remote_set(pvstate_t state, pid_t remote) * it. */ if (1 != fwrite(&msgbuf, sizeof(msgbuf), 1, control_fptr)) { - pv_error("%s", strerror(errno)); + pv_perror("%s", control_filename); (void) fclose(control_fptr); (void) remove(control_filename); return PV_ERROREXIT_REMOTE_OR_PID; } if (0 != fclose(control_fptr)) { - pv_error("%s", strerror(errno)); + pv_perror("%s", control_filename); (void) remove(control_filename); return PV_ERROREXIT_REMOTE_OR_PID; } @@ -181,7 +181,7 @@ int pv_remote_set(pvstate_t state, pid_t remote) signal_sender = 0; (void) pv_sigusr2_received(state, &signal_sender); if (kill((pid_t) (remote), SIGUSR2) != 0) { - pv_error("%u: %s", remote, strerror(errno)); + pv_perror("%s %u", _("pid"), remote); (void) remove(control_filename); return PV_ERROREXIT_REMOTE_OR_PID; } @@ -220,7 +220,7 @@ int pv_remote_set(pvstate_t state, pid_t remote) */ debug("%s: %s", "removing", control_filename); if (0 != remove(control_filename)) { - pv_error("%s", strerror(errno)); + pv_perror("%s", control_filename); } /* @@ -271,7 +271,7 @@ static bool pv__rxsignal_usr2(pvstate_t state) memset(control_filename, 0, sizeof(control_filename)); control_fptr = pv_open_controlfile(control_filename, sizeof(control_filename), signal_sender, SIGUSR2, false); if (NULL == control_fptr) { - pv_error("%s: %s", control_filename, strerror(errno)); + pv_perror("%s", control_filename); return false; } @@ -280,13 +280,13 @@ static bool pv__rxsignal_usr2(pvstate_t state) * it. */ if (1 != fread(&msgbuf, sizeof(msgbuf), 1, control_fptr)) { - pv_error("%s", strerror(errno)); + pv_perror("%s", control_filename); (void) fclose(control_fptr); return false; } if (0 != fclose(control_fptr)) { - pv_error("%s", strerror(errno)); + pv_perror("%s", control_filename); return false; } @@ -528,7 +528,7 @@ int pv_remote_transferstate_fetch(pvstate_t state, pid_t query, /*@null@ */ off_ */ if (kill((pid_t) (query), 0) != 0) { if (!silent) - pv_error("%u: %s", query, strerror(errno)); + pv_perror("%s %u", _("pid"), query); return PV_ERROREXIT_REMOTE_OR_PID; } @@ -543,7 +543,7 @@ int pv_remote_transferstate_fetch(pvstate_t state, pid_t query, /*@null@ */ off_ control_fptr = pv_open_controlfile(control_filename, sizeof(control_filename), (pid_t) getpid(), SIGUSR1, true); if (NULL == control_fptr) { if (!silent) - pv_error("%s", strerror(errno)); + pv_perror("%s", control_filename); return PV_ERROREXIT_REMOTE_OR_PID; } @@ -552,7 +552,7 @@ int pv_remote_transferstate_fetch(pvstate_t state, pid_t query, /*@null@ */ off_ */ if (1 != fwrite(&msgbuf, sizeof(msgbuf), 1, control_fptr)) { if (!silent) - pv_error("%s", strerror(errno)); + pv_perror("%s", control_filename); (void) fclose(control_fptr); (void) remove(control_filename); return PV_ERROREXIT_REMOTE_OR_PID; @@ -560,7 +560,7 @@ int pv_remote_transferstate_fetch(pvstate_t state, pid_t query, /*@null@ */ off_ if (0 != fclose(control_fptr)) { if (!silent) - pv_error("%s", strerror(errno)); + pv_perror("%s", control_filename); (void) remove(control_filename); return PV_ERROREXIT_REMOTE_OR_PID; } @@ -574,7 +574,7 @@ int pv_remote_transferstate_fetch(pvstate_t state, pid_t query, /*@null@ */ off_ (void) pv_sigusr1_received(state, &signal_sender); if (kill((pid_t) (query), SIGUSR1) != 0) { if (!silent) - pv_error("%u: %s", query, strerror(errno)); + pv_perror("%s %u", _("pid"), query); (void) remove(control_filename); return PV_ERROREXIT_REMOTE_OR_PID; } diff --git a/src/pv/state.c b/src/pv/state.c index 8b37c4a..1263b99 100644 --- a/src/pv/state.c +++ b/src/pv/state.c @@ -42,7 +42,7 @@ static void pv_alloc_calc_history(pvtransfercalc_t calc) * unavoidable memory leak warnings, but they are mitigated * by the fact that each string is only translated once. */ - pv_error("%s: %s", _("history structure allocation failed"), strerror(errno)); + pv_perror("%s", _("history structure allocation failed")); /*@+mustfreefresh@ */ return; } @@ -348,9 +348,8 @@ void pv_state_free(pvstate_t state) pv_truncate_output(state); if (STDOUT_FILENO != state->control.output_fd) { if (close(state->control.output_fd) < 0) { - pv_error("%s: %s", - NULL == state->control.output_name ? "(null)" : state->control.output_name, - strerror(errno)); + pv_perror("%s", + NULL == state->control.output_name ? "(null)" : state->control.output_name); } } state->control.output_fd = -1; @@ -741,9 +740,7 @@ void pv_state_output_set(pvstate_t state, int fd, const char *name) pv_truncate_output(state); if (state->control.output_fd >= 0 && state->control.output_fd != STDOUT_FILENO) { if (close(state->control.output_fd) < 0) { - pv_error("%s: %s", - NULL == state->control.output_name ? "(null)" : state->control.output_name, - strerror(errno)); + pv_perror("%s", NULL == state->control.output_name ? "(null)" : state->control.output_name); } } if (NULL != state->control.output_name) @@ -831,7 +828,7 @@ void pv_state_inputfiles(pvstate_t state, unsigned int input_file_count, const c new_array = calloc((size_t) (input_file_count + 1), sizeof(char *)); if (NULL == new_array) { /*@-mustfreefresh@ *//* see similar _() issue above */ - pv_error("%s: %s", _("file list allocation failed"), strerror(errno)); + pv_perror("%s", _("file list allocation failed")); /*@+mustfreefresh@ */ return; } @@ -843,7 +840,7 @@ void pv_state_inputfiles(pvstate_t state, unsigned int input_file_count, const c new_string = pv_strdup(input_files[file_idx]); if (NULL == new_string) { /*@-mustfreefresh@ *//* see similar _() issue above */ - pv_error("%s: %s", _("file list allocation failed"), strerror(errno)); + pv_perror("%s", _("file list allocation failed")); /*@+mustfreefresh@ */ return; } @@ -879,7 +876,7 @@ void pv_state_watchfds(pvstate_t state, unsigned int watchfd_count, const pid_t new_array = malloc((1 + watchfd_count) * sizeof(*new_array)); if (NULL == new_array) { /*@-mustfreefresh@ *//* see similar _() issue above */ - pv_error("%s: %s", _("buffer allocation failed"), strerror(errno)); + pv_perror("%s", _("buffer allocation failed")); /*@+mustfreefresh@ */ return; } diff --git a/src/pv/transfer.c b/src/pv/transfer.c index a463d5d..dad322e 100644 --- a/src/pv/transfer.c +++ b/src/pv/transfer.c @@ -494,7 +494,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou * the end of the file was reached. */ if (do_not_skip_errors) { - pv_error("%s: %s: %s", pv_current_file_name(state), _("read failed"), strerror(errno)); + pv_perror("%s: %s", pv_current_file_name(state), _("read failed")); *eof_in = true; if (state->transfer.write_position >= state->transfer.read_position) { *eof_out = true; @@ -509,8 +509,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou amount_skipped = -1; if (!state->transfer.read_error_warning_shown) { - pv_error("%s: %s: %s", pv_current_file_name(state), _("warning: read errors detected"), - strerror(errno)); + pv_perror("%s: %s", pv_current_file_name(state), _("warning: read errors detected")); state->transfer.read_error_warning_shown = true; } @@ -522,7 +521,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou * reached. */ if (0 > orig_offset) { - pv_error("%s: %s: %s", pv_current_file_name(state), _("file is not seekable"), strerror(errno)); + pv_perror("%s: %s", pv_current_file_name(state), _("file is not seekable")); *eof_in = true; if (state->transfer.write_position >= state->transfer.read_position) { *eof_out = true; @@ -599,8 +598,7 @@ static int pv__transfer_read(pvstate_t state, int fd, bool *eof_in, bool *eof_ou * file was reached. */ if (EINVAL != errno) { - pv_error("%s: %s: %s", pv_current_file_name(state), _("failed to seek past error"), - strerror(errno)); + pv_perror("%s: %s", pv_current_file_name(state), _("failed to seek past error")); } } else { amount_skipped = skip_offset - orig_offset; @@ -749,7 +747,7 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long (long) (new_timer.it_value.tv_usec)); if (0 != setitimer(ITIMER_REAL, &new_timer, NULL)) { - pv_error("%s: %s", _("failed to set interval timer"), strerror(errno)); + pv_perror("%s", _("failed to set interval timer")); } #else /* ! HAVE_SETITIMER */ @@ -775,7 +773,7 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long new_timer.it_value.tv_sec = 0; new_timer.it_value.tv_usec = 0; if (0 != setitimer(ITIMER_REAL, &new_timer, NULL)) { - pv_error("%s: %s", _("failed to clear interval timer"), strerror(errno)); + pv_perror("%s", _("failed to clear interval timer")); } /*@+unrecog@ */ @@ -822,8 +820,7 @@ static int pv__transfer_write(pvstate_t state, bool *eof_in, bool *eof_out, long state->transfer.line_positions = calloc((size_t) (state->transfer.line_positions_capacity), sizeof(off_t)); if (NULL == state->transfer.line_positions) { - pv_error("%s: %s", _("line position buffer allocation failed"), - strerror(errno)); + pv_perror("%s", _("line position buffer allocation failed")); } /*@+mustfreeonly@ */ /* @@ -1148,7 +1145,7 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t state->transfer.transfer_buffer = pv__allocate_aligned_buffer(state->control.output_fd, fd, state->control.target_buffer_size + 32); if (NULL == state->transfer.transfer_buffer) { - pv_error("%s: %s", _("buffer allocation failed"), strerror(errno)); + pv_perror("%s", _("buffer allocation failed")); state->status.exit_status |= PV_ERROREXIT_MEMORY; return -1; } @@ -1248,7 +1245,7 @@ ssize_t pv_transfer(pvstate_t state, int fd, bool *eof_in, bool *eof_out, off_t /* * Any other error is reported and causes an early return. */ - pv_error("%s: %s: %d: %s", pv_current_file_name(state), _("select call failed"), n, strerror(errno)); + pv_perror("%s: %s", pv_current_file_name(state), _("select call failed")); state->status.exit_status |= PV_ERROREXIT_TRANSFER; diff --git a/src/pv/watchpid.c b/src/pv/watchpid.c index 2cf3729..d554d3b 100644 --- a/src/pv/watchpid.c +++ b/src/pv/watchpid.c @@ -118,7 +118,7 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic) if (kill(info->watch_pid, 0) != 0) { if (!automatic) - pv_error("%s %u: %s", _("pid"), info->watch_pid, strerror(errno)); + pv_perror("%s %u", _("pid"), info->watch_pid); return 1; } @@ -127,7 +127,7 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic) PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE); if (size != PROC_PIDFDVNODEPATHINFO_SIZE) { - pv_error("%s %u: %s %d: %s", _("pid"), info->watch_pid, _("fd"), info->watch_fd, strerror(errno)); + pv_perror("%s %u: %s %d", _("pid"), info->watch_pid, _("fd"), info->watch_fd); return 3; } @@ -137,9 +137,8 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic) if (!(0 == stat(info->file_fdpath, &(info->sb_fd)))) { if (!automatic) - pv_error("%s %u: %s %d: %s: %s", - _("pid"), - info->watch_pid, _("fd"), info->watch_fd, info->file_fdpath, strerror(errno)); + pv_perror("%s %u: %s %d: %s", + _("pid"), info->watch_pid, _("fd"), info->watch_fd, info->file_fdpath); return 3; } @@ -182,7 +181,7 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic) if (kill(info->watch_pid, 0) != 0) { if (!automatic) - pv_error("%s %u: %s", _("pid"), info->watch_pid, strerror(errno)); + pv_perror("%s %u", _("pid"), info->watch_pid); return 1; } (void) pv_snprintf(info->file_fdinfo, PV_SIZEOF_FILE_FDINFO, @@ -199,17 +198,15 @@ int pv_watchfd_info(pvstate_t state, pvwatchfd_t info, bool automatic) * while it is being read. */ if (!automatic) - pv_error("%s %u: %s %d: %s", - _("pid"), info->watch_pid, _("fd"), info->watch_fd, strerror(errno)); + pv_perror("%s %u: %s %d", _("pid"), info->watch_pid, _("fd"), info->watch_fd); return 2; } if (!((0 == stat(info->file_fd, &(info->sb_fd))) && (0 == lstat(info->file_fd, &(info->sb_fd_link))))) { if (!automatic) - pv_error("%s %u: %s %d: %s: %s", - _("pid"), - info->watch_pid, _("fd"), info->watch_fd, info->file_fdpath, strerror(errno)); + pv_perror("%s %u: %s %d: %s", + _("pid"), info->watch_pid, _("fd"), info->watch_fd, info->file_fdpath); return 3; } @@ -329,7 +326,7 @@ static int pidfds(pvstate_t state, unsigned int pid, struct proc_fdinfo **fds, i { int size_needed = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, 0, 0); if (size_needed == -1) { - pv_error("%s: unable to list pid fds: %s", _("pid"), strerror(errno)); + pv_perror("%s %u", _("pid"), pid); return -1; } @@ -337,7 +334,7 @@ static int pidfds(pvstate_t state, unsigned int pid, struct proc_fdinfo **fds, i *fds = (struct proc_fdinfo *) malloc(size_needed); if (*fds == NULL) { - pv_error("%s: alloc failed: %s", _("pid"), strerror(errno)); + pv_perror("%s %u", _("pid"), pid); return -1; } memset(*fds, 0, size_needed);