1 /* $NetBSD: misc.c,v 1.23 2011/11/07 22:24:23 jym Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Keith Muller of the University of California, San Diego and Lance 9 * Visser of Convex Computer Corporation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94"; 40 #else 41 __RCSID("$NetBSD: misc.c,v 1.23 2011/11/07 22:24:23 jym Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/types.h> 47 #include <sys/time.h> 48 49 #include <err.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <util.h> 55 #include <inttypes.h> 56 57 #include "dd.h" 58 #include "extern.h" 59 60 #define tv2mS(tv) ((tv).tv_sec * 1000LL + ((tv).tv_usec + 500) / 1000) 61 62 static void posix_summary(void); 63 #ifndef NO_MSGFMT 64 static void custom_summary(void); 65 static void human_summary(void); 66 static void quiet_summary(void); 67 68 static void buffer_write(const char *, size_t, int); 69 #endif /* NO_MSGFMT */ 70 71 void 72 summary(void) 73 { 74 75 if (progress) 76 (void)write(STDERR_FILENO, "\n", 1); 77 78 #ifdef NO_MSGFMT 79 return posix_summary(); 80 #else /* NO_MSGFMT */ 81 if (strncmp(msgfmt, "human", sizeof("human")) == 0) 82 return human_summary(); 83 84 if (strncmp(msgfmt, "posix", sizeof("posix")) == 0) 85 return posix_summary(); 86 87 if (strncmp(msgfmt, "quiet", sizeof("quiet")) == 0) 88 return quiet_summary(); 89 90 return custom_summary(); 91 #endif /* NO_MSGFMT */ 92 } 93 94 static void 95 posix_summary(void) 96 { 97 char buf[100]; 98 int64_t mS; 99 struct timeval tv; 100 101 if (progress) 102 (void)write(STDERR_FILENO, "\n", 1); 103 104 (void)gettimeofday(&tv, NULL); 105 mS = tv2mS(tv) - tv2mS(st.start); 106 if (mS == 0) 107 mS = 1; 108 109 /* Use snprintf(3) so that we don't reenter stdio(3). */ 110 (void)snprintf(buf, sizeof(buf), 111 "%llu+%llu records in\n%llu+%llu records out\n", 112 (unsigned long long)st.in_full, (unsigned long long)st.in_part, 113 (unsigned long long)st.out_full, (unsigned long long)st.out_part); 114 (void)write(STDERR_FILENO, buf, strlen(buf)); 115 if (st.swab) { 116 (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n", 117 (unsigned long long)st.swab, 118 (st.swab == 1) ? "block" : "blocks"); 119 (void)write(STDERR_FILENO, buf, strlen(buf)); 120 } 121 if (st.trunc) { 122 (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n", 123 (unsigned long long)st.trunc, 124 (st.trunc == 1) ? "block" : "blocks"); 125 (void)write(STDERR_FILENO, buf, strlen(buf)); 126 } 127 if (st.sparse) { 128 (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n", 129 (unsigned long long)st.sparse, 130 (st.sparse == 1) ? "block" : "blocks"); 131 (void)write(STDERR_FILENO, buf, strlen(buf)); 132 } 133 (void)snprintf(buf, sizeof(buf), 134 "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n", 135 (unsigned long long) st.bytes, 136 (long) (mS / 1000), 137 (int) (mS % 1000), 138 (unsigned long long) (st.bytes * 1000LL / mS)); 139 (void)write(STDERR_FILENO, buf, strlen(buf)); 140 } 141 142 /* ARGSUSED */ 143 void 144 summaryx(int notused) 145 { 146 147 summary(); 148 } 149 150 /* ARGSUSED */ 151 void 152 terminate(int signo) 153 { 154 155 summary(); 156 (void)raise_default_signal(signo); 157 _exit(127); 158 } 159 160 #ifndef NO_MSGFMT 161 /* 162 * Buffer write(2) calls 163 */ 164 static void 165 buffer_write(const char *str, size_t size, int flush) 166 { 167 static char wbuf[128]; 168 static size_t cnt = 0; /* Internal counter to allow wbuf to wrap */ 169 170 unsigned int i; 171 172 for (i = 0; i < size; i++) { 173 if (str != NULL) { 174 wbuf[cnt++] = str[i]; 175 } 176 if (cnt >= sizeof(wbuf)) { 177 (void)write(STDERR_FILENO, wbuf, cnt); 178 cnt = 0; 179 } 180 } 181 182 if (flush != 0) { 183 (void)write(STDERR_FILENO, wbuf, cnt); 184 cnt = 0; 185 } 186 } 187 188 /* 189 * Write summary to stderr according to format 'fmt'. If 'enable' is 0, it 190 * will not attempt to write anything. Can be used to validate the 191 * correctness of the 'fmt' string. 192 */ 193 int 194 dd_write_msg(const char *fmt, int enable) 195 { 196 char hbuf[7], nbuf[32]; 197 const char *ptr; 198 int64_t mS; 199 struct timeval tv; 200 201 (void)gettimeofday(&tv, NULL); 202 mS = tv2mS(tv) - tv2mS(st.start); 203 if (mS == 0) 204 mS = 1; 205 206 #define ADDC(c) do { if (enable != 0) buffer_write(&c, 1, 0); } \ 207 while (/*CONSTCOND*/0) 208 #define ADDS(p) do { if (enable != 0) buffer_write(p, strlen(p), 0); } \ 209 while (/*CONSTCOND*/0) 210 211 for (ptr = fmt; *ptr; ptr++) { 212 if (*ptr != '%') { 213 ADDC(*ptr); 214 continue; 215 } 216 217 switch (*++ptr) { 218 case 'b': 219 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 220 (unsigned long long)st.bytes); 221 ADDS(nbuf); 222 break; 223 case 'B': 224 if (humanize_number(hbuf, sizeof(hbuf), 225 st.bytes, "B", 226 HN_AUTOSCALE, HN_DECIMAL) == -1) 227 warnx("humanize_number (bytes transferred)"); 228 ADDS(hbuf); 229 break; 230 case 'e': 231 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 232 (unsigned long long) (st.bytes * 1000LL / mS)); 233 ADDS(nbuf); 234 break; 235 case 'E': 236 if (humanize_number(hbuf, sizeof(hbuf), 237 st.bytes * 1000LL / mS, "B", 238 HN_AUTOSCALE, HN_DECIMAL) == -1) 239 warnx("humanize_number (bytes per second)"); 240 ADDS(hbuf); ADDS("/sec"); 241 break; 242 case 'i': 243 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 244 (unsigned long long)st.in_part); 245 ADDS(nbuf); 246 break; 247 case 'I': 248 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 249 (unsigned long long)st.in_full); 250 ADDS(nbuf); 251 break; 252 case 'o': 253 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 254 (unsigned long long)st.out_part); 255 ADDS(nbuf); 256 break; 257 case 'O': 258 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 259 (unsigned long long)st.out_full); 260 ADDS(nbuf); 261 break; 262 case 's': 263 (void)snprintf(nbuf, sizeof(nbuf), "%li.%03d", 264 (long) (mS / 1000), (int) (mS % 1000)); 265 ADDS(nbuf); 266 break; 267 case 'p': 268 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 269 (unsigned long long)st.sparse); 270 ADDS(nbuf); 271 break; 272 case 't': 273 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 274 (unsigned long long)st.trunc); 275 ADDS(nbuf); 276 break; 277 case 'w': 278 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 279 (unsigned long long)st.swab); 280 ADDS(nbuf); 281 break; 282 case 'P': 283 ADDS("block"); 284 if (st.sparse != 1) ADDS("s"); 285 break; 286 case 'T': 287 ADDS("block"); 288 if (st.trunc != 1) ADDS("s"); 289 break; 290 case 'W': 291 ADDS("block"); 292 if (st.swab != 1) ADDS("s"); 293 break; 294 case '%': 295 ADDC(*ptr); 296 break; 297 default: 298 if (*ptr == '\0') 299 goto done; 300 errx(EXIT_FAILURE, "unknown specifier '%c' in " 301 "msgfmt string", *ptr); 302 /* NOTREACHED */ 303 } 304 } 305 306 done: 307 /* flush buffer */ 308 buffer_write(NULL, 0, 1); 309 return 0; 310 } 311 312 static void 313 custom_summary(void) 314 { 315 316 dd_write_msg(msgfmt, 1); 317 } 318 319 static void 320 human_summary(void) 321 { 322 (void)dd_write_msg("%I+%i records in\n%O+%o records out\n", 1); 323 if (st.swab) { 324 (void)dd_write_msg("%w odd length swab %W\n", 1); 325 } 326 if (st.trunc) { 327 (void)dd_write_msg("%t truncated %T\n", 1); 328 } 329 if (st.sparse) { 330 (void)dd_write_msg("%p sparse output %P\n", 1); 331 } 332 (void)dd_write_msg("%b bytes (%B) transferred in %s secs " 333 "(%e bytes/sec - %E)\n", 1); 334 } 335 336 static void 337 quiet_summary(void) 338 { 339 340 /* stay quiet */ 341 } 342 #endif /* NO_MSGFMT */ 343