[PATCH] progress callbacks for sign, verify, decrypt, encrypt

Marcus Brinkmann Marcus.Brinkmann at ruhr-uni-bochum.de
Thu Feb 13 02:35:01 CET 2003


Hi,

I implemented the proposed progress callbacks.  The implementation is done
by a progress filter.  The filter is installed via handle_progress(), which
tries to get the correct file size, although multifiles plus --set-filesize
might produce some extraordinary stupid results (but then the user asked for
it, right?).

The filter is installed before any other filters which might change the file
size (armor and text).  It is only used on input, although it is easy to
support output progress, too, if it will be desired.

It's currently installed on all sign, encrypt, verify and decrypt
operations.  I hope that I put it in the right places without potential harm
to anything.  The progress filter is quite unintrusive, so it should be
quite safe.

The progress is emitted at most once a second.  I decided for timestamps
rather than processed bytes as this is less dependant on processor power. 
Once a second should be good enough for any graphical user interface.

At least one progress message is always emitted, the "0 len" message. 
If the filesize is different from 0, the "total len" message is also
emitted.  If the size could be calculated correctly, total is equal to len,
but other cases are possible, too (with a pipe and --set-filesize, total can
be smaller, equal or larger than len).  The user is responsible to deal with
all cases correctly.

There is currently one glitch I would like to see fixed.  With a detached
signature, the progress meter is shown for the detached signature as well as
for the data file.  I would like to suppress the detached signature progress
meter, as a detached signature is always small and we don't care.  However,
I don't know how I can do that.  Here is an example of what I mean:
[GNUPG:] PROGRESS /tmp/abc.asc ? 0 193
[GNUPG:] PROGRESS /tmp/abc.asc ? 193 193
[GNUPG:] PROGRESS /tmp/abc ? 0 3338240
[GNUPG:] PROGRESS /tmp/abc ? 851968 3338240
[GNUPG:] PROGRESS /tmp/abc ? 3338240 3338240

It seems to me that there is no way to reliably suppress the first small
progress meter, which is somewhat unfortunate.

Also, the change to the open_sigfile interface is a bit uglier than I would
like it to be, in particular the need to dealloc the file name.  However,
I wanted to keep the changes small.  Some refactoring could be useful here.

I would like to use an 8 byte data type (u64?) for the calculation of the
amounts in the progress meter, so we can support files > 4 gb without any
problems.  However, I am unsure about portability issues, like with printf. 
This is a small change in progress_filter_context_t and the sprintf in
progress.c.

Below is the patch.  Any feedback is kindly appreciated.

Thanks,
Marcus


2003-02-13  Marcus Brinkmann  <marcus at g10code.de>

	* progress.c: New file.
	* Makefile.am (common_source): Add progress.c.
	* filter.h (progress_filter_context_t): New type.
	(progress_filter, handle_progress): New prototypes.
	* main.h (open_sigfile): New argument for prototype.
	* openfile.c (open_sigfile): New argument to install progress
	filter.
	* encode.c (encode_simple): New variable PFX.  Register
	progress filter.  Install text_filter after that.
	(encode_crypt): Likewise.
	* sign.c (sign_file): Likewise.
	(clearsign_file): Likewise.
	* decrypt.c (decrypt_message): Likewise.
	(decrypt_messages): Likewise.
	* verify.c (verify_signatures): Likewise.
	(verify_one_file): Likewise.
	* plaintext.c (hash_datafiles): Likewise.
	(ask_for_detached_datafile): Likewise.


Index: Makefile.am
===================================================================
RCS file: /cvs/gnupg/gnupg/g10/Makefile.am,v
retrieving revision 1.72
diff -u -p -r1.72 Makefile.am
--- Makefile.am	12 Feb 2003 05:10:30 -0000	1.72
+++ Makefile.am	13 Feb 2003 01:28:31 -0000
@@ -46,6 +46,7 @@ common_source =  \
 	      armor.c		\
 	      mdfilter.c	\
 	      textfilter.c	\
+	      progress.c	\
 	      misc.c		\
 	      options.h 	\
 	      openfile.c	\
Index: decrypt.c
===================================================================
RCS file: /cvs/gnupg/gnupg/g10/decrypt.c,v
retrieving revision 1.10
diff -u -p -r1.10 decrypt.c
--- decrypt.c	29 Jun 2002 13:46:33 -0000	1.10
+++ decrypt.c	13 Feb 2003 01:28:31 -0000
@@ -51,6 +51,7 @@ decrypt_message( const char *filename )
 {
     IOBUF fp;
     armor_filter_context_t afx;
+    progress_filter_context_t pfx;
     int rc;
     int no_out=0;
 
@@ -61,6 +62,8 @@ decrypt_message( const char *filename )
 	return G10ERR_OPEN_FILE;
     }
 
+    handle_progress (&pfx, fp, filename);
+
     if( !opt.no_armor ) {
 	if( use_armor_filter( fp ) ) {
 	    memset( &afx, 0, sizeof afx);
@@ -84,6 +87,7 @@ decrypt_messages(int nfiles, char **file
 {
   IOBUF fp;
   armor_filter_context_t afx;  
+  progress_filter_context_t pfx;
   char *p, *output = NULL;
   int rc = 0;
   
@@ -106,6 +110,9 @@ decrypt_messages(int nfiles, char **file
           log_error(_("can't open `%s'\n"), print_fname_stdin(*files));
           continue;
         }
+
+      handle_progress (&pfx, fp, *files);
+
       if (!opt.no_armor)
         {
           if (use_armor_filter(fp))
Index: encode.c
===================================================================
RCS file: /cvs/gnupg/gnupg/g10/encode.c,v
retrieving revision 1.73
diff -u -p -r1.73 encode.c
--- encode.c	3 Dec 2002 23:31:48 -0000	1.73
+++ encode.c	13 Feb 2003 01:28:33 -0000
@@ -164,6 +164,7 @@ encode_simple( const char *filename, int
     armor_filter_context_t afx;
     compress_filter_context_t zfx;
     text_filter_context_t tfx;
+    progress_filter_context_t pfx;
     int do_compress = opt.compress && !opt.rfc1991;
 
     memset( &cfx, 0, sizeof cfx);
@@ -179,6 +180,8 @@ encode_simple( const char *filename, int
 	return G10ERR_OPEN_FILE;
     }
 
+    handle_progress (&pfx, inp, filename);
+
     if( opt.textmode )
 	iobuf_push_filter( inp, text_filter, &tfx );
 
@@ -385,6 +388,7 @@ encode_crypt( const char *filename, STRL
     armor_filter_context_t afx;
     compress_filter_context_t zfx;
     text_filter_context_t tfx;
+    progress_filter_context_t pfx;
     PK_LIST pk_list,work_list;
     int do_compress = opt.compress && !opt.rfc1991;
 
@@ -420,6 +424,8 @@ encode_crypt( const char *filename, STRL
     }
     else if( opt.verbose )
 	log_info(_("reading from `%s'\n"), filename? filename: "[stdin]");
+
+    handle_progress (&pfx, inp, filename);
 
     if( opt.textmode )
 	iobuf_push_filter( inp, text_filter, &tfx );
Index: filter.h
===================================================================
RCS file: /cvs/gnupg/gnupg/g10/filter.h,v
retrieving revision 1.42
diff -u -p -r1.42 filter.h
--- filter.h	30 Aug 2002 16:34:13 -0000	1.42
+++ filter.h	13 Feb 2003 01:28:33 -0000
@@ -109,6 +109,14 @@ typedef struct {
 } text_filter_context_t;
 
 
+typedef struct {
+    char *what;			/* description */
+    u32 last_time;		/* last time reported */
+    unsigned long last;		/* last amount reported */
+    unsigned long offset;	/* current amount */
+    unsigned long total;	/* total amount */
+} progress_filter_context_t;
+
 /* encrypt_filter_context_t defined in main.h */
 
 /*-- mdfilter.c --*/
@@ -137,6 +145,10 @@ int text_filter( void *opaque, int contr
 int copy_clearsig_text( IOBUF out, IOBUF inp, MD_HANDLE md,
 			  int escape_dash, int escape_from, int pgp2mode );
 
-
+/*-- progress.c --*/
+int progress_filter (void *opaque, int control,
+		     IOBUF a, byte *buf, size_t *ret_len);
+void handle_progress (progress_filter_context_t *pfx,
+		      IOBUF inp, char *name);
 
 #endif /*G10_FILTER_H*/
Index: main.h
===================================================================
RCS file: /cvs/gnupg/gnupg/g10/main.h,v
retrieving revision 1.87
diff -u -p -r1.87 main.h
--- main.h	29 Dec 2002 15:58:44 -0000	1.87
+++ main.h	13 Feb 2003 01:28:33 -0000
@@ -141,7 +141,7 @@ int overwrite_filep( const char *fname )
 char *make_outfile_name( const char *iname );
 char *ask_outfile_name( const char *name, size_t namelen );
 int   open_outfile( const char *iname, int mode, IOBUF *a );
-IOBUF open_sigfile( const char *iname );
+IOBUF open_sigfile( const char *iname, progress_filter_context_t *pfx );
 void try_make_homedir( const char *fname );
 
 /*-- seskey.c --*/
Index: openfile.c
===================================================================
RCS file: /cvs/gnupg/gnupg/g10/openfile.c,v
retrieving revision 1.34
diff -u -p -r1.34 openfile.c
--- openfile.c	2 Jan 2003 18:28:29 -0000	1.34
+++ openfile.c	13 Feb 2003 01:28:34 -0000
@@ -264,7 +264,7 @@ open_outfile( const char *iname, int mod
  * Return NULL if such a file is not available.
  */
 IOBUF
-open_sigfile( const char *iname )
+open_sigfile( const char *iname, progress_filter_context_t *pfx )
 {
     IOBUF a = NULL;
     size_t len;
@@ -280,7 +280,10 @@ open_sigfile( const char *iname )
 	    a = iobuf_open( buf );
 	    if( a && opt.verbose )
 		log_info(_("assuming signed data in `%s'\n"), buf );
-	    m_free(buf);
+	    if (a && pfx)
+	      handle_progress (pfx, a, buf);
+	    else
+	      m_free(buf);
 	}
     }
     return a;
Index: plaintext.c
===================================================================
RCS file: /cvs/gnupg/gnupg/g10/plaintext.c,v
retrieving revision 1.42
diff -u -p -r1.42 plaintext.c
--- plaintext.c	25 Nov 2002 13:30:34 -0000	1.42
+++ plaintext.c	13 Feb 2003 01:28:35 -0000
@@ -357,11 +357,16 @@ int
 ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2,
 			   const char *inname, int textmode )
 {
+    progress_filter_context_t pfx;
+    int dealloc_pfx_name = 1;
     char *answer = NULL;
     IOBUF fp;
     int rc = 0;
 
-    fp = open_sigfile( inname ); /* open default file */
+    fp = open_sigfile( inname, &pfx ); /* open default file */
+    if (!fp)
+      dealloc_pfx_name = 0;
+
     if( !fp && !opt.batch ) {
 	int any=0;
 	tty_printf(_("Detached signature.\n"));
@@ -395,7 +400,8 @@ ask_for_detached_datafile( MD_HANDLE md,
     }
     do_hash( md, md2, fp, textmode );
     iobuf_close(fp);
-
+    if (dealloc_pfx_name)
+      m_free (pfx.what);
 
   leave:
     m_free(answer);
@@ -412,15 +418,17 @@ int
 hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files,
 		const char *sigfilename, int textmode )
 {
+    progress_filter_context_t pfx;
     IOBUF fp;
     STRLIST sl;
 
     if( !files ) {
 	/* check whether we can open the signed material */
-	fp = open_sigfile( sigfilename );
+	fp = open_sigfile( sigfilename, &pfx );
 	if( fp ) {
 	    do_hash( md, md2, fp, textmode );
 	    iobuf_close(fp);
+	    m_free (pfx.what);
 	    return 0;
 	}
         log_error (_("no signed data\n"));
@@ -435,6 +443,7 @@ hash_datafiles( MD_HANDLE md, MD_HANDLE 
 						print_fname_stdin(sl->d));
 	    return G10ERR_OPEN_FILE;
 	}
+        handle_progress (&pfx, fp, sl->d);
 	do_hash( md, md2, fp, textmode );
 	iobuf_close(fp);
     }
Index: sign.c
===================================================================
RCS file: /cvs/gnupg/gnupg/g10/sign.c,v
retrieving revision 1.97
diff -u -p -r1.97 sign.c
--- sign.c	16 Jan 2003 19:20:10 -0000	1.97
+++ sign.c	13 Feb 2003 01:28:38 -0000
@@ -631,6 +631,7 @@ sign_file( STRLIST filenames, int detach
     compress_filter_context_t zfx;
     md_filter_context_t mfx;
     text_filter_context_t tfx;
+    progress_filter_context_t pfx;
     encrypt_filter_context_t efx;
     IOBUF inp = NULL, out = NULL;
     PACKET pkt;
@@ -678,11 +679,15 @@ sign_file( STRLIST filenames, int detach
     /* prepare iobufs */
     if( multifile )  /* have list of filenames */
 	inp = NULL; /* we do it later */
-    else if( !(inp = iobuf_open(fname)) ) {
-	log_error("can't open %s: %s\n", fname? fname: "[stdin]",
-					strerror(errno) );
-	rc = G10ERR_OPEN_FILE;
-	goto leave;
+    else {
+        if( !(inp = iobuf_open(fname)) ) {
+	    log_error("can't open %s: %s\n", fname? fname: "[stdin]",
+		      strerror(errno) );
+	    rc = G10ERR_OPEN_FILE;
+	    goto leave;
+	}
+
+        handle_progress (&pfx, inp, fname);
     }
 
     if( outfile ) {
@@ -698,7 +703,7 @@ sign_file( STRLIST filenames, int detach
 	goto leave;
 
     /* prepare to calculate the MD over the input */
-    if( opt.textmode && !outfile )
+    if( opt.textmode && !outfile)
 	iobuf_push_filter( inp, text_filter, &tfx );
     mfx.md = md_open(0, 0);
 
@@ -816,6 +821,7 @@ sign_file( STRLIST filenames, int detach
 		    rc = G10ERR_OPEN_FILE;
 		    goto leave;
 		}
+                handle_progress (&pfx, inp, sl->d);
 		if( opt.verbose )
 		    fprintf(stderr, " `%s'", sl->d );
 		iobuf_push_filter( inp, md_filter, &mfx );
@@ -874,6 +880,7 @@ int
 clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
 {
     armor_filter_context_t afx;
+    progress_filter_context_t pfx;
     MD_HANDLE textmd = NULL;
     IOBUF inp = NULL, out = NULL;
     PACKET pkt;
@@ -911,6 +918,7 @@ clearsign_file( const char *fname, STRLI
 	rc = G10ERR_OPEN_FILE;
 	goto leave;
     }
+    handle_progress (&pfx, inp, fname);
 
     if( outfile ) {
 	if( !(out = iobuf_create( outfile )) ) {
@@ -1007,6 +1015,7 @@ int
 sign_symencrypt_file (const char *fname, STRLIST locusr)
 {
     armor_filter_context_t afx;
+    progress_filter_context_t pfx;
     compress_filter_context_t zfx;
     md_filter_context_t mfx;
     text_filter_context_t tfx;
@@ -1042,6 +1051,7 @@ sign_symencrypt_file (const char *fname,
 	rc = G10ERR_OPEN_FILE;
 	goto leave;
     }
+    handle_progress (&pfx, inp, fname);
 
     /* prepare key */
     s2k = m_alloc_clear( sizeof *s2k );
Index: verify.c
===================================================================
RCS file: /cvs/gnupg/gnupg/g10/verify.c,v
retrieving revision 1.10
diff -u -p -r1.10 verify.c
--- verify.c	29 Jun 2002 13:46:34 -0000	1.10
+++ verify.c	13 Feb 2003 01:28:38 -0000
@@ -56,6 +56,7 @@ verify_signatures( int nfiles, char **fi
 {
     IOBUF fp;
     armor_filter_context_t afx;
+    progress_filter_context_t pfx;
     const char *sigfile;
     int i, rc;
     STRLIST sl;
@@ -94,6 +95,7 @@ verify_signatures( int nfiles, char **fi
 	log_error(_("can't open `%s'\n"), print_fname_stdin(sigfile));
 	return G10ERR_OPEN_FILE;
     }
+    handle_progress (&pfx, fp, sigfile);
 
     if( !opt.no_armor && use_armor_filter( fp ) )
 	iobuf_push_filter( fp, armor_filter, &afx );
@@ -130,6 +132,7 @@ verify_one_file( const char *name )
 {
     IOBUF fp;
     armor_filter_context_t afx;
+    progress_filter_context_t pfx;
     int rc;
 
     print_file_status( STATUS_FILE_START, name, 1 );
@@ -139,6 +142,7 @@ verify_one_file( const char *name )
 	log_error(_("can't open `%s'\n"), print_fname_stdin(name));
 	return G10ERR_OPEN_FILE;
     }
+    handle_progress (&pfx, fp, name);
 
     if( !opt.no_armor ) {
 	if( use_armor_filter( fp ) ) {
--- /dev/null	2003-01-08 03:56:32.000000000 +0100
+++ progress.c	2003-02-13 02:17:39.000000000 +0100
@@ -0,0 +1,101 @@
+/* progress.c
+ * Copyright (C) 2003 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
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include "iobuf.h"
+#include "filter.h"
+#include "status.h"
+#include "options.h"
+
+/****************
+ * The filter is used to report progress to the user.
+ */
+int
+progress_filter (void *opaque, int control,
+		 IOBUF a, byte *buf, size_t *ret_len)
+{
+  int rc = 0;
+  progress_filter_context_t *pfx = opaque;
+
+  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->offset,
+	       pfx->total);
+      write_status_text (STATUS_PROGRESS, buffer);
+    }
+  else if (control == IOBUFCTRL_UNDERFLOW)
+    {
+      u32 timestamp = make_timestamp ();
+      int len = iobuf_read (a, buf, *ret_len);
+
+      if (len >= 0)
+	{
+	  pfx->offset += len;
+	  *ret_len = len;
+	}
+      else
+	{
+	  *ret_len = 0;
+	  rc = -1;
+	}
+      if ((len == -1 && pfx->offset != pfx->last)
+	  || timestamp - pfx->last_time > 0)
+	{
+	  char buffer[50];
+	  
+	  sprintf (buffer, "%.20s ? %lu %lu", pfx->what, pfx->offset,
+		   pfx->total);
+	  write_status_text (STATUS_PROGRESS, buffer);
+
+	  pfx->last = pfx->offset;
+	  pfx->last_time = timestamp;
+	}
+    }
+  else if (control == IOBUFCTRL_DESC)
+    *(char**)buf = "progress_filter";
+  return rc;
+}
+
+void
+handle_progress (progress_filter_context_t *pfx, IOBUF inp, char *name)
+{
+  off_t filesize = 0;
+
+  if (!is_status_enabled ())
+    return;
+
+  if (name)
+    filesize = iobuf_get_filelength (inp);
+  else if (opt.set_filesize)
+    filesize = opt.set_filesize;
+
+  /* register the progress filter */
+  pfx->what = name ? name : "stdin";
+  pfx->total = filesize;
+  iobuf_push_filter (inp, progress_filter, pfx);
+}


-- 
`Rhubarb is no Egyptian god.' GNU      http://www.gnu.org    marcus at gnu.org
Marcus Brinkmann              The Hurd http://www.gnu.org/software/hurd/
Marcus.Brinkmann at ruhr-uni-bochum.de
http://www.marcus-brinkmann.de/




More information about the Gnupg-devel mailing list