diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index b332eec..3ee27f7 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -38,8 +38,8 @@ char* FAST_FUNC append_ext(char *filename, const char *expected_ext)
 int FAST_FUNC bbunpack(char **argv,
 	IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_aux_data_t *aux),
 	char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
-	const char *expected_ext
-)
+	const char *expected_ext, void (*print_info)(off_t bin, off_t bout,
+			const char* newname, const char* oldname))
 {
 	struct stat stat_buf;
 	IF_DESKTOP(long long) int status = 0;
@@ -51,6 +51,7 @@ int FAST_FUNC bbunpack(char **argv,
 		/* NB: new_name is *maybe* malloc'ed! */
 		new_name = NULL;
 		filename = *argv; /* can be NULL - 'streaming' bunzip2 */
+		status = -1;
 
 		if (filename && LONE_DASH(filename))
 			filename = NULL;
@@ -154,11 +155,11 @@ int FAST_FUNC bbunpack(char **argv,
 			}
 			xunlink(del);
 
-#if 0 /* Currently buggy - wrong name: "a.gz: 261% - replaced with a.gz" */
-			/* Extreme bloat for gunzip compat */
-			if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE) && status >= 0) {
-				fprintf(stderr, "%s: %u%% - replaced with %s\n",
-					filename, (unsigned)(stat_buf.st_size*100 / (status+1)), new_name);
+#if ENABLE_DESKTOP
+			if ((print_info != NULL) && (status >= 0)
+					&& (option_mask32 & OPT_VERBOSE))
+			{
+				print_info(stat_buf.st_size, status, new_name, *argv);
 			}
 #endif
 
@@ -166,6 +167,15 @@ int FAST_FUNC bbunpack(char **argv,
 			if (new_name != filename)
 				free(new_name);
 		}
+
+#if ENABLE_DESKTOP
+		/* Print to stdout */
+		if ((new_name == NULL) && (print_info != NULL) && (status >= 0)
+				&& option_mask32 & OPT_VERBOSE && option_mask32 & OPT_STDOUT)
+		{
+			print_info(stat_buf.st_size, status, NULL, *argv);
+		}
+#endif
 	} while (*argv && *++argv);
 
 	if (option_mask32 & OPT_STDOUT)
@@ -215,7 +225,8 @@ int uncompress_main(int argc UNUSED_PARAM, char **argv)
 	getopt32(argv, "cf");
 	argv += optind;
 
-	return bbunpack(argv, unpack_uncompress, make_new_name_generic, "Z");
+	return bbunpack(argv, unpack_uncompress, make_new_name_generic,
+			"Z", /* unused */ NULL);
 }
 #endif
 
@@ -272,10 +283,24 @@ int uncompress_main(int argc UNUSED_PARAM, char **argv)
 static
 char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
 {
-	char *extension = strrchr(filename, '.');
+	char *extension = 0;
+
+#if ENABLE_DESKTOP
+	/* This is done in order to get correct output
+	 * from gunzip in 'verbose' mode. We don't want the same buffer
+	 * to hold both old and new names.
+	 */
+	filename = xstrdup(filename);
+#endif
+	extension = strrchr(filename, '.');
 
 	if (!extension)
+	{
+#if ENABLE_DESKTOP
+		free(filename);
+#endif
 		return NULL;
+	}
 
 	extension++;
 	if (strcmp(extension, "tgz" + 1) == 0
@@ -299,6 +324,16 @@ IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(transformer_aux_data_t *aux)
 {
 	return unpack_gz_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 }
+#if ENABLE_DESKTOP
+static void print_verbose_gunzip(off_t bin, off_t bout,
+		const char* newname, const char* oldname)
+{
+	fprintf(stderr, "%s:\t%5.1f%%%s%s\n", oldname,
+			bout == 0 ? 0.0f : 100.0*(1.0-(double)bin/(double)bout),
+			newname == NULL ? "" : " -- replaced with ",
+			newname == NULL ? "" : newname);
+}
+#endif
 /*
  * Linux kernel build uses gzip -d -n. We accept and ignore it.
  * Man page says:
@@ -323,9 +358,14 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
 	 * But if seamless magic is enabled, then we are much more clever.
 	 */
 	if (applet_name[1] == 'c')
+	{
 		option_mask32 |= OPT_STDOUT | SEAMLESS_MAGIC;
+		option_mask32 &= ~OPT_VERBOSE;  /* Ignore 'verbose' in zcat. */
+	}
 
-	return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL);
+	return bbunpack(argv, unpack_gunzip, make_new_name_gunzip,
+			/*unused:*/ NULL,
+			IF_DESKTOP(print_verbose_gunzip) IF_NOT_DESKTOP(NULL));
 }
 #endif
 
@@ -349,6 +389,13 @@ int gunzip_main(int argc UNUSED_PARAM, char **argv)
 //applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
 //applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat))
 #if ENABLE_BUNZIP2
+#if ENABLE_DESKTOP
+static void print_verbose_bunzip2(off_t bin UNUSED_PARAM, off_t bout UNUSED_PARAM,
+		const char* newname UNUSED_PARAM, const char* oldname)
+{
+	fprintf(stderr, "  %s:\tdone\n", oldname);
+}
+#endif
 static
 IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(transformer_aux_data_t *aux)
 {
@@ -360,9 +407,13 @@ int bunzip2_main(int argc UNUSED_PARAM, char **argv)
 	getopt32(argv, "cfvqdt");
 	argv += optind;
 	if (applet_name[2] == 'c') /* bzcat */
+	{
 		option_mask32 |= OPT_STDOUT;
+		option_mask32 &= ~OPT_VERBOSE; /* Ignore 'verbose' in bzcat. */
+	}
 
-	return bbunpack(argv, unpack_bunzip2, make_new_name_generic, "bz2");
+	return bbunpack(argv, unpack_bunzip2, make_new_name_generic,
+			"bz2", IF_DESKTOP(print_verbose_bunzip2) IF_NOT_DESKTOP(NULL));
 }
 #endif
 
@@ -436,7 +487,8 @@ int unlzma_main(int argc UNUSED_PARAM, char **argv)
 		option_mask32 |= OPT_STDOUT;
 
 	argv += optind;
-	return bbunpack(argv, unpack_unlzma, make_new_name_generic, "lzma");
+	return bbunpack(argv, unpack_unlzma, make_new_name_generic,
+			"lzma", /* unused */ NULL);
 }
 #endif
 
@@ -461,6 +513,7 @@ int unxz_main(int argc UNUSED_PARAM, char **argv)
 		option_mask32 |= OPT_STDOUT;
 
 	argv += optind;
-	return bbunpack(argv, unpack_unxz, make_new_name_generic, "xz");
+	return bbunpack(argv, unpack_unxz, make_new_name_generic,
+			"xz", /* unused */ NULL);
 }
 #endif
diff --git a/archival/bzip2.c b/archival/bzip2.c
index dd77c8e..09f5966 100644
--- a/archival/bzip2.c
+++ b/archival/bzip2.c
@@ -146,6 +146,29 @@ IF_DESKTOP(long long) int FAST_FUNC compressStream(transformer_aux_data_t *aux U
 	return total;
 }
 
+#if ENABLE_DESKTOP
+static void print_verbose(off_t bin, off_t bout, const char* UNUSED UNUSED_PARAM,
+		const char* oldname)
+{
+	double dbin = (double)bin;
+	double dbout = (double)bout;
+
+	if (dbin == 0.0)
+	{
+		fprintf(stderr, "  %s:\tno data compressed.\n", oldname);
+	}
+	else
+	{
+		fprintf(stderr, "  %s:\t%6.3f:1, %6.3f bits/byte, "
+				"%5.2f%% saved, %llu in, %llu out.\n", oldname,
+				dbin/dbout, (8.0*dbout)/dbin,
+				100.0*(1.0 - dbout/dbin),
+				(unsigned long long)bin,
+				(unsigned long long)bout);
+	}
+}
+#endif
+
 int bzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int bzip2_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -186,5 +209,6 @@ int bzip2_main(int argc UNUSED_PARAM, char **argv)
 
 	argv += optind;
 	option_mask32 &= 0x7; /* ignore all except -cfv */
-	return bbunpack(argv, compressStream, append_ext, "bz2");
+	return bbunpack(argv, compressStream, append_ext,
+			"bz2", IF_DESKTOP(print_verbose) IF_NOT_DESKTOP(NULL));
 }
diff --git a/archival/gzip.c b/archival/gzip.c
index 31ccab3..7462192 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -1980,9 +1980,10 @@ static void ct_init(void)
  * IN assertions: the input and output buffers are cleared.
  */
 
-static void zip(ulg time_stamp)
+static ulg zip(ulg time_stamp)
 {
 	ush deflate_flags = 0;  /* pkzip -es, -en or -ex equivalent */
+	ulg bytes_out = 0;
 
 	G1.outcnt = 0;
 
@@ -2003,15 +2004,30 @@ static void zip(ulg time_stamp)
 	put_8bit(deflate_flags);	/* extra flags */
 	put_8bit(3);	/* OS identifier = 3 (Unix) */
 
-	deflate();
+	bytes_out = deflate();
 
 	/* Write the crc and uncompressed size */
 	put_32bit(~G1.crc);
 	put_32bit(G1.isize);
 
 	flush_outbuf();
+
+	return bytes_out;
 }
 
+/* ======================================================================== */
+#if ENABLE_DESKTOP
+static void print_verbose(off_t bin, off_t bout,
+		const char* newname, const char* oldname)
+{
+	fprintf(stderr, "%s%s%5.1f%%%s%s\n",
+			oldname == NULL ? "" : oldname,
+			oldname == NULL ? " " : ":\t",
+			bin == 0 ? 0.0f : 100.0*(1.0-(double)bout/(double)bin),
+			newname == NULL ? "" : " -- replaced with ",
+			newname == NULL ? "" : newname);
+}
+#endif
 
 /* ======================================================================== */
 static
@@ -2052,8 +2068,7 @@ IF_DESKTOP(long long) int FAST_FUNC pack_gzip(transformer_aux_data_t *aux UNUSED
 
 	s.st_ctime = 0;
 	fstat(STDIN_FILENO, &s);
-	zip(s.st_ctime);
-	return 0;
+	return (IF_DESKTOP(long long) int)zip(s.st_ctime) + 18; /* 18 = header + checksum */
 }
 
 #if ENABLE_FEATURE_GZIP_LONG_OPTIONS
@@ -2124,5 +2139,6 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
 	/* Initialize the CRC32 table */
 	global_crc32_table = crc32_filltable(NULL, 0);
 
-	return bbunpack(argv, pack_gzip, append_ext, "gz");
+	return bbunpack(argv, pack_gzip, append_ext, "gz",
+			IF_DESKTOP(print_verbose) IF_NOT_DESKTOP(NULL));
 }
diff --git a/archival/lzop.c b/archival/lzop.c
index 9b42e5f..6453831 100644
--- a/archival/lzop.c
+++ b/archival/lzop.c
@@ -1099,5 +1099,6 @@ int lzop_main(int argc UNUSED_PARAM, char **argv)
 		option_mask32 |= OPT_DECOMPRESS;
 
 	global_crc32_table = crc32_filltable(NULL, 0);
-	return bbunpack(argv, pack_lzop, make_new_name_lzop, /*unused:*/ NULL);
+	return bbunpack(argv, pack_lzop, make_new_name_lzop,
+			/*unused:*/ NULL, /* unused */ NULL);
 }
diff --git a/include/bb_archive.h b/include/bb_archive.h
index b82cfd8..cc085a1 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -225,7 +225,8 @@ char* append_ext(char *filename, const char *expected_ext) FAST_FUNC;
 int bbunpack(char **argv,
 		IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_aux_data_t *aux),
 		char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
-		const char *expected_ext
+		const char *expected_ext, void (*print_info)(off_t bin, off_t bout,
+			const char* newname, const char* oldname)
 ) FAST_FUNC;
 
 void check_errors_in_children(int signo);
