gpg,gpgsm: Fix compliance check for DSA and avoid an assert.
[gnupg.git] / g10 / progress.c
1 /* progress.c - emit progress status lines
2  * Copyright (C) 2003, 2006 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG 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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <https://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22
23 #include "gpg.h"
24 #include "../common/iobuf.h"
25 #include "filter.h"
26 #include "../common/status.h"
27 #include "../common/util.h"
28 #include "options.h"
29
30 /* Create a new context for use with the progress filter.  We need to
31    allocate such contexts on the heap because there is no guarantee
32    that at the end of a function the filter has already been popped
33    off.  In general this will happen but with malformed packets it is
34    possible that a filter has not yet reached the end-of-stream when
35    the function has done all processing.  Checking in each function
36    that end-of-stream has been reached would be to cumbersome.
37
38    What we also do is to shortcut the progress handler by having this
39    function return NULL if progress information has not been
40    requested.
41 */
42 progress_filter_context_t *
43 new_progress_context (void)
44 {
45   progress_filter_context_t *pfx;
46
47   if (!opt.enable_progress_filter)
48     return NULL;
49
50   if (!is_status_enabled ())
51     return NULL;
52
53   pfx = xcalloc (1, sizeof *pfx);
54   pfx->refcount = 1;
55
56   return pfx;
57 }
58
59 /* Release a progress filter context.  Passing NULL is explicitly
60    allowed and a no-op.  */
61 void
62 release_progress_context (progress_filter_context_t *pfx)
63 {
64   if (!pfx)
65     return;
66   log_assert (pfx->refcount);
67   if ( --pfx->refcount )
68     return;
69   xfree (pfx->what);
70   xfree (pfx);
71 }
72
73
74 static void
75 write_status_progress (const char *what,
76                        unsigned long current, unsigned long total_arg)
77 {
78   char buffer[60];
79   char units[] = "BKMGTPEZY?";
80   int unitidx = 0;
81   uint64_t total = total_arg;
82
83   /* Although we use an unsigned long for the values, 32 bit
84    * applications using GPGME will use an "int" and thus are limited
85    * in the total size which can be represented.  On Windows, where
86    * sizeof(int)==sizeof(long), this is even worse and will lead to an
87    * integer overflow for all files larger than 2 GiB.  Although, the
88    * allowed value range of TOTAL and CURRENT is nowhere specified, we
89    * better protect applications from the need to handle negative
90    * values.  The common usage pattern of the progress information is
91    * to display how many percent of the operation has been done and
92    * thus scaling CURRENT and TOTAL down before they get to large,
93    * should not have a noticeable effect except for rounding
94    * imprecision. */
95
96   if (!total && opt.input_size_hint)
97     total = opt.input_size_hint;
98
99   if (total)
100     {
101       if (current > total)
102         current = total;
103
104       while (total > 1024*1024)
105         {
106           total /= 1024;
107           current /= 1024;
108           unitidx++;
109         }
110     }
111   else
112     {
113       while (current > 1024*1024)
114         {
115           current /= 1024;
116           unitidx++;
117         }
118     }
119
120   if (unitidx > 9)
121     unitidx = 9;
122
123   snprintf (buffer, sizeof buffer, "%.20s ? %lu %lu %c%s",
124             what? what : "?", current, (unsigned long)total,
125             units[unitidx],
126             unitidx? "iB" : "");
127   write_status_text (STATUS_PROGRESS, buffer);
128 }
129
130
131 /****************
132  * The filter is used to report progress to the user.
133  */
134 static int
135 progress_filter (void *opaque, int control,
136                  IOBUF a, byte *buf, size_t *ret_len)
137 {
138   int rc = 0;
139   progress_filter_context_t *pfx = opaque;
140
141   if (control == IOBUFCTRL_INIT)
142     {
143       pfx->last = 0;
144       pfx->offset = 0;
145       pfx->last_time = make_timestamp ();
146
147       write_status_progress (pfx->what, pfx->offset, pfx->total);
148     }
149   else if (control == IOBUFCTRL_UNDERFLOW)
150     {
151       u32 timestamp = make_timestamp ();
152       int len = iobuf_read (a, buf, *ret_len);
153
154       if (len >= 0)
155         {
156           pfx->offset += len;
157           *ret_len = len;
158         }
159       else
160         {
161           *ret_len = 0;
162           rc = -1;
163         }
164       if ((len == -1 && pfx->offset != pfx->last)
165           || timestamp - pfx->last_time > 0)
166         {
167           write_status_progress (pfx->what, pfx->offset, pfx->total);
168           pfx->last = pfx->offset;
169           pfx->last_time = timestamp;
170         }
171     }
172   else if (control == IOBUFCTRL_FREE)
173     {
174       release_progress_context (pfx);
175     }
176   else if (control == IOBUFCTRL_DESC)
177     mem2str (buf, "progress_filter", *ret_len);
178   return rc;
179 }
180
181 void
182 handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name)
183 {
184   off_t filesize = 0;
185
186   if (!pfx)
187     return;
188
189   log_assert (opt.enable_progress_filter);
190   log_assert (is_status_enabled ());
191
192   if ( !iobuf_is_pipe_filename (name) && *name )
193     filesize = iobuf_get_filelength (inp, NULL);
194   else if (opt.set_filesize)
195     filesize = opt.set_filesize;
196
197   /* register the progress filter */
198   pfx->what = xstrdup (name ? name : "stdin");
199   pfx->total = filesize;
200   pfx->refcount++;
201   iobuf_push_filter (inp, progress_filter, pfx);
202 }