tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: humanize_number(3) for dd(1) summary?



On 05.11.2011 14:10, Izumi Tsutsui wrote:
I'd like smaller SMALLPROG one, for people who want dd(1)
on rescue media to zap leftover labels on their disks etc.

okay, last call, this time with an updated dd(1).

Looks fine. Thanks.

Some late comments for minor nits:

--- args.c      16 Sep 2011 16:06:23 -0000      1.35
+++ args.c      5 Nov 2011 01:42:13 -0000
@@ -64,11 +64,13 @@ static void f_cbs(char *);
  #ifdef        NO_CONV
  __dead
  #endif
+
  static void   f_conv(char *);
[snip]
or leave no newline?

Updated.

--- dd.1        22 Dec 2010 09:42:53 -0000      1.23
+++ dd.1        5 Nov 2011 01:42:14 -0000
@@ -32,7 +32,7 @@
  .\"
  .\"      @(#)dd.1        8.2 (Berkeley) 1/13/94
  .\"
-.Dd December 22, 2010
+.Dd November 05, 2011

wizd(8) would say November 5, 2011 ?

Obviously; thanks!

Someone pointed me off list that my current design has a limitation: it will truncate any msgfmt bigger than the internal buffer I am using (1024). I did not expect the "fmt" to be bigger than 1024 but... I was kindly asked to handle this.

So I changed the implementation and dropped the dd_snprintf() stuff to directly write(2) the result to stderr. This remains functionally equivalent to the previous dd(1) except that I am buffering writes to lower the number of write(2) calls.

--
Jean-Yves Migeon
jeanyves.migeon%free.fr@localhost
? .gdbinit
? dd
? dd.cat1
? dd.html1
? rump.dd
Index: Makefile
===================================================================
RCS file: /cvsroot/src/bin/dd/Makefile,v
retrieving revision 1.15
diff -u -p -r1.15 Makefile
--- Makefile    4 Feb 2011 19:42:12 -0000       1.15
+++ Makefile    5 Nov 2011 19:31:09 -0000
@@ -8,7 +8,7 @@ DPADD+= ${LIBUTIL}
 LDADD+=        -lutil
 
 .ifdef SMALLPROG
-CPPFLAGS+=     -DNO_CONV -DSMALL
+CPPFLAGS+=     -DNO_CONV -DNO_MSGFMT -DSMALL
 .else
 SRCS+=         conv_tab.c
 .ifndef CRUNCHEDPROG
Index: args.c
===================================================================
RCS file: /cvsroot/src/bin/dd/args.c,v
retrieving revision 1.35
diff -u -p -r1.35 args.c
--- args.c      16 Sep 2011 16:06:23 -0000      1.35
+++ args.c      5 Nov 2011 19:31:09 -0000
@@ -56,15 +56,22 @@ __RCSID("$NetBSD: args.c,v 1.35 2011/09/
 #include "extern.h"
 
 static int     c_arg(const void *, const void *);
-#ifndef        NO_CONV
+
+#ifdef NO_MSGFMT
+static void    f_msgfmt(char *) __dead;
+#else
+static void    f_msgfmt(char *);
+#endif /* NO_MSGFMT */
+
+#ifdef NO_CONV
+static void    f_conv(char *) __dead;
+#else
+static void    f_conv(char *);
 static int     c_conv(const void *, const void *);
-#endif
+#endif /* NO_CONV */
+
 static void    f_bs(char *);
 static void    f_cbs(char *);
-#ifdef NO_CONV
-__dead
-#endif
-static void    f_conv(char *);
 static void    f_count(char *);
 static void    f_files(char *);
 static void    f_ibs(char *);
@@ -90,6 +97,7 @@ static const struct arg {
        { "ibs",        f_ibs,          C_IBS,   C_BS|C_IBS },
        { "if",         f_if,           C_IF,    C_IF },
        { "iseek",      f_skip,         C_SKIP,  C_SKIP },
+       { "msgfmt",     f_msgfmt,       C_SKIP,  C_SKIP },
        { "obs",        f_obs,          C_OBS,   C_BS|C_OBS },
        { "of",         f_of,           C_OF,    C_OF },
        { "oseek",      f_seek,         C_SEEK,  C_SEEK },
@@ -252,6 +260,24 @@ f_if(char *arg)
        in.name = arg;
 }
 
+#ifdef NO_MSGFMT
+/* Build a small version (i.e. for a ramdisk root) */
+static void
+f_msgfmt(char *arg)
+{
+
+       errx(EXIT_FAILURE, "msgfmt option disabled");
+       /* NOTREACHED */
+}
+#else  /* NO_MSGFMT */
+static void
+f_msgfmt(char *arg)
+{
+
+       msgfmt = arg;
+}
+#endif /* NO_MSGFMT */
+
 static void
 f_obs(char *arg)
 {
Index: dd.1
===================================================================
RCS file: /cvsroot/src/bin/dd/dd.1,v
retrieving revision 1.23
diff -u -p -r1.23 dd.1
--- dd.1        22 Dec 2010 09:42:53 -0000      1.23
+++ dd.1        5 Nov 2011 19:31:09 -0000
@@ -32,7 +32,7 @@
 .\"
 .\"    @(#)dd.1        8.2 (Berkeley) 1/13/94
 .\"
-.Dd December 22, 2010
+.Dd November 5, 2011
 .Dt DD 1
 .Os
 .Sh NAME
@@ -97,6 +97,74 @@ Seek on the input file
 blocks.
 This is synonymous with
 .Cm skip= Ns Ar n .
+.It Cm msgfmt= Ns Ar fmt
+Specify the message format
+.Ar fmt
+to be used when writing information to standard output.
+Possible values are:
+.Bl -tag -width xxxxx -offset indent -compact
+.It quiet
+turns off information summary report except for errors and
+.Cm progress .
+.It posix
+default information summary report as specified by POSIX.
+.It human
+default information summary report extended with human-readable
+values.
+.El
+.Pp
+When
+.Ar fmt
+does not correspond to any value given above,
+it contains a string that will be used as format specifier
+for the information summary output.
+Each conversion specification is introduced by the character
+.Cm % .
+The following ones are available:
+.Bl -tag -width xx -offset indent -compact
+.It b
+total number of bytes transferred
+.It B
+total number of bytes transferred in
+.Xr humanize_number 3
+format
+.It e
+speed transfer
+.It E
+speed transfer in
+.Xr humanize_number 3
+format
+.It i
+number of partial input block(s)
+.It I
+number of full input block(s)
+.It o
+number of partial output block(s)
+.It O
+number of full output block(s)
+.It s
+time elapsed since the beginning in
+.Do seconds.ms Dc
+format
+.It p
+number of sparse output blocks
+.It t
+number of truncated blocks
+.It w
+number of odd-length swab blocks
+.It P
+singular/plural of
+.Do block Dc ,
+depending on number of sparse blocks
+.It T
+singular/plural of
+.Do block Dc ,
+depending on number of truncated blocks
+.It W
+singular/plural of
+.Do block Dc ,
+depending on number of swab blocks
+.El
 .It Cm obs= Ns Ar n
 Set the output block size to
 .Va n
@@ -370,6 +438,18 @@ will exit.
 The
 .Nm
 utility exits 0 on success and \*[Gt]0 if an error occurred.
+.Sh EXAMPLES
+To print summary information in human-readable form:
+.Pp
+.Dl dd if=/dev/zero of=/dev/null count=1 msgfmt=human 
+.Pp
+To customize the information summary output and print it through
+.Xr unvis 3 :
+.Pp
+.Bd -literal -offset indent
+dd if=/dev/zero of=/dev/null count=1 \e
+     msgfmt='speed:%E, in %s seconds\en' 2\*[Gt]\*[Am]1 | unvis
+.Ed
 .Sh SEE ALSO
 .Xr cp 1 ,
 .Xr mt 1 ,
@@ -382,7 +462,9 @@ utility is expected to be a superset of 
 standard.
 The
 .Cm files
-operand and the
+and
+.Cm msgfmt
+operands and the
 .Cm ascii ,
 .Cm ebcdic ,
 .Cm ibm ,
Index: dd.c
===================================================================
RCS file: /cvsroot/src/bin/dd/dd.c,v
retrieving revision 1.47
diff -u -p -r1.47 dd.c
--- dd.c        4 Feb 2011 19:42:12 -0000       1.47
+++ dd.c        5 Nov 2011 19:31:09 -0000
@@ -86,6 +86,7 @@ u_int         files_cnt = 1;          /* # of files to 
 uint64_t       progress = 0;           /* display sign of life */
 const u_char   *ctab;                  /* conversion table */
 sigset_t       infoset;                /* a set blocking SIGINFO */
+const char     *msgfmt = "posix";      /* default summary() message format */
 
 /*
  * Ops for stdin/stdout and crunch'd dd.  These are always host ops.
Index: extern.h
===================================================================
RCS file: /cvsroot/src/bin/dd/extern.h,v
retrieving revision 1.20
diff -u -p -r1.20 extern.h
--- extern.h    16 Sep 2011 16:06:23 -0000      1.20
+++ extern.h    5 Nov 2011 19:31:09 -0000
@@ -74,3 +74,4 @@ extern const u_char   a2e_32V[], a2e_POSIX
 extern const u_char    e2a_32V[], e2a_POSIX[];
 extern const u_char    a2ibm_32V[], a2ibm_POSIX[];
 extern u_char          casetab[];
+extern const char      *msgfmt;
Index: misc.c
===================================================================
RCS file: /cvsroot/src/bin/dd/misc.c,v
retrieving revision 1.21
diff -u -p -r1.21 misc.c
--- misc.c      5 Oct 2007 07:23:09 -0000       1.21
+++ misc.c      5 Nov 2011 19:31:09 -0000
@@ -59,9 +59,42 @@ __RCSID("$NetBSD: misc.c,v 1.21 2007/10/
 
 #define        tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000)
 
+static void posix_summary(void);
+#ifndef NO_MSGFMT
+static void custom_summary(void);
+static void human_summary(void);
+static void quiet_summary(void);
+
+static void buffer_write(const char *, size_t, int);
+static int  dd_write_msg(const char *);
+#endif /* NO_MSGFMT */
+
 void
 summary(void)
 {
+
+       if (progress)
+               (void)write(STDERR_FILENO, "\n", 1);
+
+#ifdef NO_MSGFMT
+       return posix_summary();
+#else /* NO_MSGFMT */
+       if (strncmp(msgfmt, "human", sizeof("human")) == 0)
+               return human_summary();
+
+       if (strncmp(msgfmt, "posix", sizeof("posix")) == 0)
+               return posix_summary();
+
+       if (strncmp(msgfmt, "quiet", sizeof("quiet")) == 0)
+               return quiet_summary();
+
+       return custom_summary();
+#endif /* NO_MSGFMT */
+}
+
+static void
+posix_summary(void)
+{
        char buf[100];
        int64_t mS;
        struct timeval tv;
@@ -73,6 +106,7 @@ summary(void)
        mS = tv2mS(tv) - tv2mS(st.start);
        if (mS == 0)
                mS = 1;
+
        /* Use snprintf(3) so that we don't reenter stdio(3). */
        (void)snprintf(buf, sizeof(buf),
            "%llu+%llu records in\n%llu+%llu records out\n",
@@ -123,3 +157,174 @@ terminate(int signo)
        (void)raise_default_signal(signo);
        _exit(127);
 }
+
+#ifndef NO_MSGFMT
+/*
+ * Buffer write(2) calls
+ */
+static void
+buffer_write(const char *str, size_t size, int flush)
+{
+       static char wbuf[128];
+       static size_t cnt = 0; /* Internal counter to allow wbuf to wrap */
+       
+       unsigned int i;
+
+       for (i = 0; i < size; i++) {
+               wbuf[cnt++] = str[i];
+               if (cnt >= sizeof(wbuf) || flush == 1) {
+                       (void)write(STDERR_FILENO, wbuf, cnt);
+                       cnt = 0;
+               }
+       }
+}
+
+static int
+dd_write_msg(const char *fmt)
+{
+       char hbuf[7], nbuf[32];
+       const char *ptr;
+       int64_t mS;
+        struct timeval tv;
+
+       (void)gettimeofday(&tv, NULL);
+       mS = tv2mS(tv) - tv2mS(st.start);
+       if (mS == 0)
+               mS = 1;
+
+#define ADDC(c) do { buffer_write(&c, 1, 0); } \
+       while (/*CONSTCOND*/0)
+#define ADDS(p) do { buffer_write(p, strlen(p), 0); } \
+       while (/*CONSTCOND*/0)
+
+       for (ptr = fmt; *ptr; ptr++) {
+               if (*ptr != '%') {
+                       ADDC(*ptr);
+                       continue;
+               }
+
+               switch (*++ptr) {
+               case 'b':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.bytes);
+                       ADDS(nbuf);
+                       break;
+               case 'B':
+                       if (humanize_number(hbuf, sizeof(hbuf),
+                           st.bytes, "B",
+                           HN_AUTOSCALE, HN_DECIMAL) == -1)
+                               warnx("humanize_number (bytes transferred)");
+                       ADDS(hbuf);
+                       break;
+               case 'e':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long) (st.bytes * 1000LL / mS));
+                       ADDS(nbuf);
+                       break;
+               case 'E':
+                       if (humanize_number(hbuf, sizeof(hbuf),
+                           st.bytes * 1000LL / mS, "B",
+                           HN_AUTOSCALE, HN_DECIMAL) == -1)
+                               warnx("humanize_number (bytes per second)");
+                       ADDS(hbuf); ADDS("/sec");
+                       break;
+               case 'i':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.in_part);
+                       ADDS(nbuf);
+                       break;
+               case 'I':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.in_full);
+                       ADDS(nbuf);
+                       break;
+               case 'o':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.out_part);
+                       ADDS(nbuf);
+                       break;
+               case 'O':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.out_full);
+                       ADDS(nbuf);
+                       break;
+               case 's':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%li.%03d",
+                           (long) (mS / 1000), (int) (mS % 1000));
+                       ADDS(nbuf);
+                       break;
+               case 'p':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.sparse);
+                       ADDS(nbuf);
+                       break;
+               case 't':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.trunc);
+                       ADDS(nbuf);
+                       break;
+               case 'w':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.swab);
+                       ADDS(nbuf);
+                       break;
+               case 'P':
+                       ADDS("block");
+                       if (st.sparse != 1) ADDS("s");
+                       break;
+               case 'T':
+                       ADDS("block");
+                       if (st.trunc != 1) ADDS("s");
+                       break;
+               case 'W':
+                       ADDS("block");
+                       if (st.swab != 1) ADDS("s");
+                       break;
+               default:
+                       ADDS("%");
+                       if (*ptr == '\0')
+                               goto done;
+                       /*FALLTHROUGH*/
+               case '%':
+                       ADDC(*ptr);
+                       break;
+               }
+       }
+
+done:
+       /* flush buffer */
+       buffer_write("\0", 1, 1);
+       return 0;
+}
+
+static void
+custom_summary(void)
+{
+
+       dd_write_msg(msgfmt);
+}
+
+static void
+human_summary(void)
+{
+       (void)dd_write_msg("%I+%i records in\n%O+%o records out\n");
+       if (st.swab) {
+               (void)dd_write_msg("%w odd length swab %W\n");
+       }
+       if (st.trunc) {
+               (void)dd_write_msg("%t truncated %T\n");
+       }
+       if (st.sparse) {
+               (void)dd_write_msg("%p sparse output %P\n");
+       }
+       (void)dd_write_msg("%b bytes (%B) transferred in %s secs "
+           "(%e bytes/sec - %E)\n");
+}
+
+static void
+quiet_summary(void)
+{
+
+       /* stay quiet */
+}
+#endif /* NO_MSGFMT */


Home | Main Index | Thread Index | Old Index