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