-/* progress.c
- * Copyright (C) 2003 Free Software Foundation, Inc.
+/* progress.c - emit progress status lines
+ * Copyright (C) 2003, 2006 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* GnuPG is distributed in the hope that it will be useful,
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
+#include "gpg.h"
#include "iobuf.h"
#include "filter.h"
#include "status.h"
#include "util.h"
#include "options.h"
+/* Create a new context for use with the progress filter. We need to
+ allocate such contexts on the heap because there is no guarantee
+ that at the end of a function the filter has already been popped
+ off. In general this will happen but with malformed packets it is
+ possible that a filter has not yet reached the end-of-stream when
+ the function has done all processing. Checking in each function
+ that end-of-stream has been reached would be to cumbersome.
+
+ What we also do is to shortcut the progress handler by having this
+ function return NULL if progress information has not been
+ requested.
+*/
+progress_filter_context_t *
+new_progress_context (void)
+{
+ progress_filter_context_t *pfx;
+
+ if (!opt.enable_progress_filter)
+ return NULL;
+
+ if (!is_status_enabled ())
+ return NULL;
+
+ pfx = xcalloc (1, sizeof *pfx);
+ pfx->refcount = 1;
+
+ return pfx;
+}
+
+/* Release a progress filter context. Passing NULL is explicitly
+ allowed and a no-op. */
+void
+release_progress_context (progress_filter_context_t *pfx)
+{
+ if (!pfx)
+ return;
+ log_assert (pfx->refcount);
+ if ( --pfx->refcount )
+ return;
+ xfree (pfx->what);
+ xfree (pfx);
+}
+
+
+static void
+write_status_progress (const char *what,
+ unsigned long current, unsigned long total_arg)
+{
+ char buffer[60];
+ char units[] = "BKMGTPEZY?";
+ int unitidx = 0;
+ uint64_t total = total_arg;
+
+ /* Although we use an unsigned long for the values, 32 bit
+ * applications using GPGME will use an "int" and thus are limited
+ * in the total size which can be represented. On Windows, where
+ * sizeof(int)==sizeof(long), this is even worse and will lead to an
+ * integer overflow for all files larger than 2 GiB. Although, the
+ * allowed value range of TOTAL and CURRENT is nowhere specified, we
+ * better protect applications from the need to handle negative
+ * values. The common usage pattern of the progress information is
+ * to display how many percent of the operation has been done and
+ * thus scaling CURRENT and TOTAL down before they get to large,
+ * should not have a noticeable effect except for rounding
+ * imprecision. */
+
+ if (!total && opt.input_size_hint)
+ total = opt.input_size_hint;
+
+ if (total)
+ {
+ if (current > total)
+ current = total;
+
+ while (total > 1024*1024)
+ {
+ total /= 1024;
+ current /= 1024;
+ unitidx++;
+ }
+ }
+ else
+ {
+ while (current > 1024*1024)
+ {
+ current /= 1024;
+ unitidx++;
+ }
+ }
+
+ if (unitidx > 9)
+ unitidx = 9;
+
+ snprintf (buffer, sizeof buffer, "%.20s ? %lu %lu %c%s",
+ what? what : "?", current, (unsigned long)total,
+ units[unitidx],
+ unitidx? "iB" : "");
+ write_status_text (STATUS_PROGRESS, buffer);
+}
+
+
/****************
* The filter is used to report progress to the user.
*/
-int
+static int
progress_filter (void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
if (control == IOBUFCTRL_INIT)
{
- char buffer[50];
-
pfx->last = 0;
pfx->offset = 0;
pfx->last_time = make_timestamp ();
- sprintf (buffer, "%.20s ? %lu %lu",
- pfx->what? pfx->what : "?",
- pfx->offset,
- pfx->total);
- write_status_text (STATUS_PROGRESS, buffer);
+ write_status_progress (pfx->what, pfx->offset, pfx->total);
}
else if (control == IOBUFCTRL_UNDERFLOW)
{
if ((len == -1 && pfx->offset != pfx->last)
|| timestamp - pfx->last_time > 0)
{
- char buffer[50];
-
- sprintf (buffer, "%.20s ? %lu %lu",
- pfx->what? pfx->what : "?",
- pfx->offset,
- pfx->total);
- write_status_text (STATUS_PROGRESS, buffer);
-
+ write_status_progress (pfx->what, pfx->offset, pfx->total);
pfx->last = pfx->offset;
pfx->last_time = timestamp;
}
}
else if (control == IOBUFCTRL_FREE)
{
- /* Note, that we must always dealloc resources of a filter
- within the filter handler and not anywhere else. (We set it
- to NULL and check all uses just in case.) */
- m_free (pfx->what);
- pfx->what = NULL;
+ release_progress_context (pfx);
}
else if (control == IOBUFCTRL_DESC)
- *(char**)buf = "progress_filter";
+ mem2str (buf, "progress_filter", *ret_len);
return rc;
}
{
off_t filesize = 0;
- if (!opt.enable_progress_filter)
+ if (!pfx)
return;
- if (!is_status_enabled ())
- return;
+ log_assert (opt.enable_progress_filter);
+ log_assert (is_status_enabled ());
if ( !iobuf_is_pipe_filename (name) && *name )
- filesize = iobuf_get_filelength (inp);
+ filesize = iobuf_get_filelength (inp, NULL);
else if (opt.set_filesize)
filesize = opt.set_filesize;
/* register the progress filter */
- pfx->what = m_strdup (name ? name : "stdin");
+ pfx->what = xstrdup (name ? name : "stdin");
pfx->total = filesize;
+ pfx->refcount++;
iobuf_push_filter (inp, progress_filter, pfx);
}