Line data Source code
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 "iobuf.h"
25 : #include "filter.h"
26 : #include "status.h"
27 : #include "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 495 : new_progress_context (void)
44 : {
45 : progress_filter_context_t *pfx;
46 :
47 495 : if (!opt.enable_progress_filter)
48 495 : return NULL;
49 :
50 0 : if (!is_status_enabled ())
51 0 : return NULL;
52 :
53 0 : pfx = xcalloc (1, sizeof *pfx);
54 0 : pfx->refcount = 1;
55 :
56 0 : return pfx;
57 : }
58 :
59 : /* Release a progress filter context. Passing NULL is explicitly
60 : allowed and a no-op. */
61 : void
62 492 : release_progress_context (progress_filter_context_t *pfx)
63 : {
64 492 : if (!pfx)
65 492 : return;
66 0 : log_assert (pfx->refcount);
67 0 : if ( --pfx->refcount )
68 0 : return;
69 0 : xfree (pfx->what);
70 0 : xfree (pfx);
71 : }
72 :
73 :
74 : static void
75 0 : write_status_progress (const char *what,
76 : unsigned long current, unsigned long total_arg)
77 : {
78 : char buffer[60];
79 0 : char units[] = "BKMGTPEZY?";
80 0 : int unitidx = 0;
81 0 : 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 0 : if (!total && opt.input_size_hint)
97 0 : total = opt.input_size_hint;
98 :
99 0 : if (total)
100 : {
101 0 : if (current > total)
102 0 : current = total;
103 :
104 0 : while (total > 1024*1024)
105 : {
106 0 : total /= 1024;
107 0 : current /= 1024;
108 0 : unitidx++;
109 : }
110 : }
111 : else
112 : {
113 0 : while (current > 1024*1024)
114 : {
115 0 : current /= 1024;
116 0 : unitidx++;
117 : }
118 : }
119 :
120 0 : if (unitidx > 9)
121 0 : unitidx = 9;
122 :
123 0 : snprintf (buffer, sizeof buffer, "%.20s ? %lu %lu %c%s",
124 : what? what : "?", current, (unsigned long)total,
125 0 : units[unitidx],
126 : unitidx? "iB" : "");
127 0 : write_status_text (STATUS_PROGRESS, buffer);
128 0 : }
129 :
130 :
131 : /****************
132 : * The filter is used to report progress to the user.
133 : */
134 : static int
135 0 : progress_filter (void *opaque, int control,
136 : IOBUF a, byte *buf, size_t *ret_len)
137 : {
138 0 : int rc = 0;
139 0 : progress_filter_context_t *pfx = opaque;
140 :
141 0 : if (control == IOBUFCTRL_INIT)
142 : {
143 0 : pfx->last = 0;
144 0 : pfx->offset = 0;
145 0 : pfx->last_time = make_timestamp ();
146 :
147 0 : write_status_progress (pfx->what, pfx->offset, pfx->total);
148 : }
149 0 : else if (control == IOBUFCTRL_UNDERFLOW)
150 : {
151 0 : u32 timestamp = make_timestamp ();
152 0 : int len = iobuf_read (a, buf, *ret_len);
153 :
154 0 : if (len >= 0)
155 : {
156 0 : pfx->offset += len;
157 0 : *ret_len = len;
158 : }
159 : else
160 : {
161 0 : *ret_len = 0;
162 0 : rc = -1;
163 : }
164 0 : if ((len == -1 && pfx->offset != pfx->last)
165 0 : || timestamp - pfx->last_time > 0)
166 : {
167 0 : write_status_progress (pfx->what, pfx->offset, pfx->total);
168 0 : pfx->last = pfx->offset;
169 0 : pfx->last_time = timestamp;
170 : }
171 : }
172 0 : else if (control == IOBUFCTRL_FREE)
173 : {
174 0 : release_progress_context (pfx);
175 : }
176 0 : else if (control == IOBUFCTRL_DESC)
177 0 : mem2str (buf, "progress_filter", *ret_len);
178 0 : return rc;
179 : }
180 :
181 : void
182 493 : handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name)
183 : {
184 493 : off_t filesize = 0;
185 :
186 493 : if (!pfx)
187 986 : return;
188 :
189 0 : log_assert (opt.enable_progress_filter);
190 0 : log_assert (is_status_enabled ());
191 :
192 0 : if ( !iobuf_is_pipe_filename (name) && *name )
193 0 : filesize = iobuf_get_filelength (inp, NULL);
194 0 : else if (opt.set_filesize)
195 0 : filesize = opt.set_filesize;
196 :
197 : /* register the progress filter */
198 0 : pfx->what = xstrdup (name ? name : "stdin");
199 0 : pfx->total = filesize;
200 0 : pfx->refcount++;
201 0 : iobuf_push_filter (inp, progress_filter, pfx);
202 : }
|