sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

dd.c (4897B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <ctype.h>
      3 #include <fcntl.h>
      4 #include <inttypes.h>
      5 #include <stdint.h>
      6 #include <string.h>
      7 #include <unistd.h>
      8 
      9 #include "util.h"
     10 
     11 static off_t ifull, ofull, ipart, opart;
     12 
     13 static void
     14 usage(void)
     15 {
     16 	eprintf("usage: %s [operand...]\n", argv0);
     17 }
     18 
     19 static size_t
     20 parsesize(char *expr)
     21 {
     22 	char *s = expr;
     23 	size_t n = 1;
     24 
     25 	for (;;) {
     26 		n *= strtoumax(s, &s, 10);
     27 		switch (*s) {
     28 		case 'k': n <<= 10; s++; break;
     29 		case 'b': n <<= 9; s++; break;
     30 		}
     31 		if (*s != 'x' || !s[1])
     32 			break;
     33 		s++;
     34 	}
     35 	if (*s || n == 0)
     36 		eprintf("invalid block size expression '%s'\n", expr);
     37 
     38 	return n;
     39 }
     40 
     41 static void
     42 bswap(unsigned char *buf, size_t len)
     43 {
     44 	int c;
     45 
     46 	for (len &= ~1; len > 0; buf += 2, len -= 2) {
     47 		c = buf[0];
     48 		buf[0] = buf[1];
     49 		buf[1] = c;
     50 	}
     51 }
     52 
     53 static void
     54 lcase(unsigned char *buf, size_t len)
     55 {
     56 	for (; len > 0; buf++, len--)
     57 		buf[0] = tolower(buf[0]);
     58 }
     59 
     60 static void
     61 ucase(unsigned char *buf, size_t len)
     62 {
     63 	for (; len > 0; buf++, len--)
     64 		buf[0] = toupper(buf[0]);
     65 }
     66 
     67 static void
     68 summary(void)
     69 {
     70 	fprintf(stderr, "%"PRIdMAX"+%"PRIdMAX" records in\n", (intmax_t)ifull, (intmax_t)ipart);
     71 	fprintf(stderr, "%"PRIdMAX"+%"PRIdMAX" records out\n", (intmax_t)ofull, (intmax_t)opart);
     72 }
     73 
     74 int
     75 main(int argc, char *argv[])
     76 {
     77 	enum {
     78 		LCASE   = 1 << 0,
     79 		UCASE   = 1 << 1,
     80 		SWAB    = 1 << 2,
     81 		NOERROR = 1 << 3,
     82 		NOTRUNC = 1 << 4,
     83 		SYNC    = 1 << 5,
     84 	} conv = 0;
     85 	char *arg, *val, *end;
     86 	const char *iname = "-", *oname = "-";
     87 	int ifd = 0, ofd = 1, eof = 0;
     88 	size_t len, bs = 0, ibs = 512, obs = 512, ipos = 0, opos = 0;
     89 	off_t skip = 0, seek = 0, count = -1;
     90 	ssize_t ret;
     91 	unsigned char *buf;
     92 
     93 	argv0 = argc ? (argc--, *argv++) : "dd";
     94 	for (; argc > 0; argc--, argv++) {
     95 		arg = *argv;
     96 		val = strchr(arg, '=');
     97 		if (!val)
     98 			usage();
     99 		*val++ = '\0';
    100 		if (strcmp(arg, "if") == 0) {
    101 			iname = val;
    102 		} else if (strcmp(arg, "of") == 0) {
    103 			oname = val;
    104 		} else if (strcmp(arg, "ibs") == 0) {
    105 			ibs = parsesize(val);
    106 		} else if (strcmp(arg, "obs") == 0) {
    107 			obs = parsesize(val);
    108 		} else if (strcmp(arg, "bs") == 0) {
    109 			bs = parsesize(val);
    110 		} else if (strcmp(arg, "skip") == 0) {
    111 			skip = estrtonum(val, 0, LLONG_MAX);
    112 		} else if (strcmp(arg, "seek") == 0) {
    113 			seek = estrtonum(val, 0, LLONG_MAX);
    114 		} else if (strcmp(arg, "count") == 0) {
    115 			count = estrtonum(val, 0, LLONG_MAX);
    116 		} else if (strcmp(arg, "conv") == 0) {
    117 			do {
    118 				end = strchr(val, ',');
    119 				if (end)
    120 					*end++ = '\0';
    121 				if (strcmp(val, "lcase") == 0)
    122 					conv |= LCASE;
    123 				else if (strcmp(val, "ucase") == 0)
    124 					conv |= UCASE;
    125 				else if (strcmp(val, "swab") == 0)
    126 					conv |= SWAB;
    127 				else if (strcmp(val, "noerror") == 0)
    128 					conv |= NOERROR;
    129 				else if (strcmp(val, "notrunc") == 0)
    130 					conv |= NOTRUNC;
    131 				else if (strcmp(val, "sync") == 0)
    132 					conv |= SYNC;
    133 				else
    134 					eprintf("unknown conv flag '%s'\n", val);
    135 				val = end;
    136 			} while (val);
    137 		} else {
    138 			weprintf("unknown operand '%s'\n", arg);
    139 			usage();
    140 		}
    141 	}
    142 
    143 	if (bs)
    144 		ibs = obs = bs;
    145 	if (strcmp(iname, "-") != 0) {
    146 		ifd = open(iname, O_RDONLY);
    147 		if (ifd < 0)
    148 			eprintf("open %s:", iname);
    149 	}
    150 	if (strcmp(oname, "-") != 0) {
    151 		ofd = open(oname, O_WRONLY | O_CREAT | (conv & NOTRUNC || seek ? 0 : O_TRUNC), 0666);
    152 		if (ofd < 0)
    153 			eprintf("open %s:", oname);
    154 	}
    155 
    156 	len = MAX(ibs, obs) + ibs;
    157 	buf = emalloc(len);
    158 	if (skip && lseek(ifd, skip * ibs, SEEK_SET) < 0) {
    159 		while (skip--) {
    160 			ret = read(ifd, buf, ibs);
    161 			if (ret < 0)
    162 				eprintf("read:");
    163 			if (ret == 0) {
    164 				eof = 1;
    165 				break;
    166 			}
    167 		}
    168 	}
    169 	if (seek) {
    170 		if (!(conv & NOTRUNC) && ftruncate(ofd, seek * ibs) != 0)
    171 			eprintf("ftruncate:");
    172 		if (lseek(ofd, seek * ibs, SEEK_SET) < 0)
    173 			eprintf("lseek:");
    174 		/* XXX: handle non-seekable files */
    175 	}
    176 	while (!eof && (count == -1 || ifull + ipart < count)) {
    177 		while (ipos - opos < obs) {
    178 			ret = read(ifd, buf + ipos, ibs);
    179 			if (ret == 0) {
    180 				eof = 1;
    181 				break;
    182 			}
    183 			if (ret < 0) {
    184 				weprintf("read:");
    185 				if (!(conv & NOERROR))
    186 					return 1;
    187 				summary();
    188 				if (!(conv & SYNC))
    189 					continue;
    190 				ret = 0;
    191 			}
    192 			if (ret < ibs) {
    193 				ipart++;
    194 				if (conv & SYNC) {
    195 					memset(buf + ipos + ret, 0, ibs - ret);
    196 					ret = ibs;
    197 				}
    198 			} else {
    199 				ifull++;
    200 			}
    201 			if (conv & SWAB)
    202 				bswap(buf + ipos, ret);
    203 			if (conv & LCASE)
    204 				lcase(buf + ipos, ret);
    205 			if (conv & UCASE)
    206 				ucase(buf + ipos, ret);
    207 			ipos += ret;
    208 			if (bs && !(conv & (SWAB | LCASE | UCASE)))
    209 				break;
    210 		}
    211 		if (ipos == opos)
    212 			break;
    213 		do {
    214 			ret = write(ofd, buf + opos, MIN(obs, ipos - opos));
    215 			if (ret < 0)
    216 				eprintf("write:");
    217 			if (ret == 0)
    218 				eprintf("write returned 0\n");
    219 			if (ret < obs)
    220 				opart++;
    221 			else
    222 				ofull++;
    223 			opos += ret;
    224 		} while ((eof && ipos < opos) || (!eof && ipos - opos >= obs));
    225 		if (opos < ipos)
    226 			memmove(buf, buf + opos, ipos - opos);
    227 		ipos -= opos;
    228 		opos = 0;
    229 	}
    230 	summary();
    231 
    232 	return 0;
    233 }