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