Module Name: src Committed By: jym Date: Sun Nov 6 21:22:23 UTC 2011
Modified Files: src/bin/dd: Makefile args.c dd.1 dd.c extern.h misc.c Log Message: Add a new command to dd(1): msgfmt. The command modifies the output of the information summary returned by dd(1). This can be used to specify messages in a more usable (or parseable) format like human-readable values. My intent is to re-use this for building image files and quick I/O benchmarking. Reviewed by tsutsui@ on tech-userlevel. See also http://mail-index.netbsd.org/tech-userlevel/2010/12/03/msg004179.html Some examples: $ dd if=/dev/zero of=/dev/null bs=1m count=1 msgfmt=human 1+0 records in 1+0 records out 1048576 bytes (1,0 MB) transferred in 0.001 secs (1048576000 bytes/sec - 1,0 GB/sec) $ dd if=/dev/zero of=/dev/null count=1 msgfmt=' > <speed>%E</speed> > <time>%s</time> > <bytes>%b</bytes> > ' <speed>500 KB/sec</speed> <time>0.001</time> <bytes>512</bytes> To generate a diff of this commit: cvs rdiff -u -r1.15 -r1.16 src/bin/dd/Makefile cvs rdiff -u -r1.35 -r1.36 src/bin/dd/args.c cvs rdiff -u -r1.23 -r1.24 src/bin/dd/dd.1 cvs rdiff -u -r1.47 -r1.48 src/bin/dd/dd.c cvs rdiff -u -r1.20 -r1.21 src/bin/dd/extern.h cvs rdiff -u -r1.21 -r1.22 src/bin/dd/misc.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/bin/dd/Makefile diff -u src/bin/dd/Makefile:1.15 src/bin/dd/Makefile:1.16 --- src/bin/dd/Makefile:1.15 Fri Feb 4 19:42:12 2011 +++ src/bin/dd/Makefile Sun Nov 6 21:22:23 2011 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.15 2011/02/04 19:42:12 pooka Exp $ +# $NetBSD: Makefile,v 1.16 2011/11/06 21:22:23 jym Exp $ # @(#)Makefile 8.1 (Berkeley) 5/31/93 RUMPPRG=dd @@ -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: src/bin/dd/args.c diff -u src/bin/dd/args.c:1.35 src/bin/dd/args.c:1.36 --- src/bin/dd/args.c:1.35 Fri Sep 16 16:06:23 2011 +++ src/bin/dd/args.c Sun Nov 6 21:22:23 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: args.c,v 1.35 2011/09/16 16:06:23 joerg Exp $ */ +/* $NetBSD: args.c,v 1.36 2011/11/06 21:22:23 jym Exp $ */ /*- * Copyright (c) 1991, 1993, 1994 @@ -38,7 +38,7 @@ #if 0 static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94"; #else -__RCSID("$NetBSD: args.c,v 1.35 2011/09/16 16:06:23 joerg Exp $"); +__RCSID("$NetBSD: args.c,v 1.36 2011/11/06 21:22:23 jym Exp $"); #endif #endif /* not lint */ @@ -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: src/bin/dd/dd.1 diff -u src/bin/dd/dd.1:1.23 src/bin/dd/dd.1:1.24 --- src/bin/dd/dd.1:1.23 Wed Dec 22 09:42:53 2010 +++ src/bin/dd/dd.1 Sun Nov 6 21:22:23 2011 @@ -1,4 +1,4 @@ -.\" $NetBSD: dd.1,v 1.23 2010/12/22 09:42:53 enami Exp $ +.\" $NetBSD: dd.1,v 1.24 2011/11/06 21:22:23 jym Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -32,7 +32,7 @@ .\" .\" @(#)dd.1 8.2 (Berkeley) 1/13/94 .\" -.Dd December 22, 2010 +.Dd November 6, 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: src/bin/dd/dd.c diff -u src/bin/dd/dd.c:1.47 src/bin/dd/dd.c:1.48 --- src/bin/dd/dd.c:1.47 Fri Feb 4 19:42:12 2011 +++ src/bin/dd/dd.c Sun Nov 6 21:22:23 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: dd.c,v 1.47 2011/02/04 19:42:12 pooka Exp $ */ +/* $NetBSD: dd.c,v 1.48 2011/11/06 21:22:23 jym Exp $ */ /*- * Copyright (c) 1991, 1993, 1994 @@ -43,7 +43,7 @@ __COPYRIGHT("@(#) Copyright (c) 1991, 19 #if 0 static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; #else -__RCSID("$NetBSD: dd.c,v 1.47 2011/02/04 19:42:12 pooka Exp $"); +__RCSID("$NetBSD: dd.c,v 1.48 2011/11/06 21:22:23 jym Exp $"); #endif #endif /* not lint */ @@ -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: src/bin/dd/extern.h diff -u src/bin/dd/extern.h:1.20 src/bin/dd/extern.h:1.21 --- src/bin/dd/extern.h:1.20 Fri Sep 16 16:06:23 2011 +++ src/bin/dd/extern.h Sun Nov 6 21:22:23 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: extern.h,v 1.20 2011/09/16 16:06:23 joerg Exp $ */ +/* $NetBSD: extern.h,v 1.21 2011/11/06 21:22:23 jym Exp $ */ /*- * Copyright (c) 1991, 1993, 1994 @@ -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: src/bin/dd/misc.c diff -u src/bin/dd/misc.c:1.21 src/bin/dd/misc.c:1.22 --- src/bin/dd/misc.c:1.21 Fri Oct 5 07:23:09 2007 +++ src/bin/dd/misc.c Sun Nov 6 21:22:23 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: misc.c,v 1.21 2007/10/05 07:23:09 lukem Exp $ */ +/* $NetBSD: misc.c,v 1.22 2011/11/06 21:22:23 jym Exp $ */ /*- * Copyright (c) 1991, 1993, 1994 @@ -38,7 +38,7 @@ #if 0 static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94"; #else -__RCSID("$NetBSD: misc.c,v 1.21 2007/10/05 07:23:09 lukem Exp $"); +__RCSID("$NetBSD: misc.c,v 1.22 2011/11/06 21:22:23 jym Exp $"); #endif #endif /* not lint */ @@ -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 */