misc.c revision 1.26
1/* $NetBSD: misc.c,v 1.26 2021/10/09 20:44:55 rillig 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 39static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94"; 40#else 41__RCSID("$NetBSD: misc.c,v 1.26 2021/10/09 20:44:55 rillig 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 62static void posix_summary(void); 63#ifndef NO_MSGFMT 64static void custom_summary(void); 65static void human_summary(void); 66static void quiet_summary(void); 67 68static void buffer_write(const char *, size_t, int); 69#endif /* NO_MSGFMT */ 70 71void 72summary(void) 73{ 74 75 if (progress) 76 (void)write(STDERR_FILENO, "\n", 1); 77 78#ifdef NO_MSGFMT 79 posix_summary(); 80#else /* NO_MSGFMT */ 81 if (strcmp(msgfmt, "human") == 0) 82 human_summary(); 83 else if (strcmp(msgfmt, "posix") == 0) 84 posix_summary(); 85 else if (strcmp(msgfmt, "quiet") == 0) 86 quiet_summary(); 87 else 88 custom_summary(); 89#endif /* NO_MSGFMT */ 90} 91 92static void 93posix_summary(void) 94{ 95 char buf[100]; 96 int64_t mS; 97 struct timeval tv; 98 99 if (progress) 100 (void)write(STDERR_FILENO, "\n", 1); 101 102 (void)gettimeofday(&tv, NULL); 103 mS = tv2mS(tv) - tv2mS(st.start); 104 if (mS == 0) 105 mS = 1; 106 107 /* Use snprintf(3) so that we don't reenter stdio(3). */ 108 (void)snprintf(buf, sizeof(buf), 109 "%llu+%llu records in\n%llu+%llu records out\n", 110 (unsigned long long)st.in_full, (unsigned long long)st.in_part, 111 (unsigned long long)st.out_full, (unsigned long long)st.out_part); 112 (void)write(STDERR_FILENO, buf, strlen(buf)); 113 if (st.swab) { 114 (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n", 115 (unsigned long long)st.swab, 116 (st.swab == 1) ? "block" : "blocks"); 117 (void)write(STDERR_FILENO, buf, strlen(buf)); 118 } 119 if (st.trunc) { 120 (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n", 121 (unsigned long long)st.trunc, 122 (st.trunc == 1) ? "block" : "blocks"); 123 (void)write(STDERR_FILENO, buf, strlen(buf)); 124 } 125 if (st.sparse) { 126 (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n", 127 (unsigned long long)st.sparse, 128 (st.sparse == 1) ? "block" : "blocks"); 129 (void)write(STDERR_FILENO, buf, strlen(buf)); 130 } 131 (void)snprintf(buf, sizeof(buf), 132 "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n", 133 (unsigned long long) st.bytes, 134 (long) (mS / 1000), 135 (int) (mS % 1000), 136 (unsigned long long) (st.bytes * 1000LL / mS)); 137 (void)write(STDERR_FILENO, buf, strlen(buf)); 138} 139 140/* ARGSUSED */ 141void 142summaryx(int notused) 143{ 144 145 summary(); 146} 147 148/* ARGSUSED */ 149void 150terminate(int signo) 151{ 152 153 summary(); 154 (void)raise_default_signal(signo); 155 _exit(127); 156} 157 158#ifndef NO_MSGFMT 159/* 160 * Buffer write(2) calls 161 */ 162static void 163buffer_write(const char *str, size_t size, int flush) 164{ 165 static char wbuf[128]; 166 static size_t cnt = 0; /* Internal counter to allow wbuf to wrap */ 167 168 unsigned int i; 169 170 for (i = 0; i < size; i++) { 171 if (str != NULL) { 172 wbuf[cnt++] = str[i]; 173 } 174 if (cnt >= sizeof(wbuf)) { 175 (void)write(STDERR_FILENO, wbuf, cnt); 176 cnt = 0; 177 } 178 } 179 180 if (flush != 0) { 181 (void)write(STDERR_FILENO, wbuf, cnt); 182 cnt = 0; 183 } 184} 185 186/* 187 * Write summary to stderr according to format 'fmt'. If 'enable' is 0, it 188 * will not attempt to write anything. Can be used to validate the 189 * correctness of the 'fmt' string. 190 */ 191int 192dd_write_msg(const char *fmt, int enable) 193{ 194 char hbuf[7], nbuf[32]; 195 const char *ptr; 196 int64_t mS; 197 struct timeval tv; 198 199 (void)gettimeofday(&tv, NULL); 200 mS = tv2mS(tv) - tv2mS(st.start); 201 if (mS == 0) 202 mS = 1; 203 204#define ADDC(c) do { if (enable != 0) buffer_write(&c, 1, 0); } \ 205 while (0) 206#define ADDS(p) do { if (enable != 0) buffer_write(p, strlen(p), 0); } \ 207 while (0) 208 209 for (ptr = fmt; *ptr; ptr++) { 210 if (*ptr != '%') { 211 ADDC(*ptr); 212 continue; 213 } 214 215 switch (*++ptr) { 216 case 'b': 217 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 218 (unsigned long long)st.bytes); 219 ADDS(nbuf); 220 break; 221 case 'B': 222 if (humanize_number(hbuf, sizeof(hbuf), 223 st.bytes, "B", 224 HN_AUTOSCALE, HN_DECIMAL) == -1) 225 warnx("humanize_number (bytes transferred)"); 226 ADDS(hbuf); 227 break; 228 case 'e': 229 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 230 (unsigned long long) (st.bytes * 1000LL / mS)); 231 ADDS(nbuf); 232 break; 233 case 'E': 234 if (humanize_number(hbuf, sizeof(hbuf), 235 st.bytes * 1000LL / mS, "B", 236 HN_AUTOSCALE, HN_DECIMAL) == -1) 237 warnx("humanize_number (bytes per second)"); 238 ADDS(hbuf); ADDS("/sec"); 239 break; 240 case 'i': 241 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 242 (unsigned long long)st.in_part); 243 ADDS(nbuf); 244 break; 245 case 'I': 246 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 247 (unsigned long long)st.in_full); 248 ADDS(nbuf); 249 break; 250 case 'o': 251 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 252 (unsigned long long)st.out_part); 253 ADDS(nbuf); 254 break; 255 case 'O': 256 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 257 (unsigned long long)st.out_full); 258 ADDS(nbuf); 259 break; 260 case 's': 261 (void)snprintf(nbuf, sizeof(nbuf), "%li.%03d", 262 (long) (mS / 1000), (int) (mS % 1000)); 263 ADDS(nbuf); 264 break; 265 case 'p': 266 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 267 (unsigned long long)st.sparse); 268 ADDS(nbuf); 269 break; 270 case 't': 271 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 272 (unsigned long long)st.trunc); 273 ADDS(nbuf); 274 break; 275 case 'w': 276 (void)snprintf(nbuf, sizeof(nbuf), "%llu", 277 (unsigned long long)st.swab); 278 ADDS(nbuf); 279 break; 280 case 'P': 281 ADDS("block"); 282 if (st.sparse != 1) ADDS("s"); 283 break; 284 case 'T': 285 ADDS("block"); 286 if (st.trunc != 1) ADDS("s"); 287 break; 288 case 'W': 289 ADDS("block"); 290 if (st.swab != 1) ADDS("s"); 291 break; 292 case '%': 293 ADDC(*ptr); 294 break; 295 default: 296 if (*ptr == '\0') 297 goto done; 298 errx(EXIT_FAILURE, "unknown specifier '%c' in " 299 "msgfmt string", *ptr); 300 /* NOTREACHED */ 301 } 302 } 303 304done: 305 /* flush buffer */ 306 buffer_write(NULL, 0, 1); 307 return 0; 308} 309 310static void 311custom_summary(void) 312{ 313 314 dd_write_msg(msgfmt, 1); 315} 316 317static void 318human_summary(void) 319{ 320 (void)dd_write_msg("%I+%i records in\n%O+%o records out\n", 1); 321 if (st.swab) { 322 (void)dd_write_msg("%w odd length swab %W\n", 1); 323 } 324 if (st.trunc) { 325 (void)dd_write_msg("%t truncated %T\n", 1); 326 } 327 if (st.sparse) { 328 (void)dd_write_msg("%p sparse output %P\n", 1); 329 } 330 (void)dd_write_msg("%b bytes (%B) transferred in %s secs " 331 "(%e bytes/sec - %E)\n", 1); 332} 333 334static void 335quiet_summary(void) 336{ 337 338 /* stay quiet */ 339} 340#endif /* NO_MSGFMT */ 341