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
74static void
75summary(void)
76{
77 fprintf(stderr, "%" PRIdMAX "+%" PRIdMAX " records in\n", (intmax_t)ifull, (intmax_t)ipart);
78 fprintf(stderr, "%" PRIdMAX "+%" PRIdMAX " records out\n", (intmax_t)ofull, (intmax_t)opart);
79}
80
81// ?man dd: convert and copy a file
82// ?man arguments: operand...
83// ?man copy a file while performing conversions
84int
85main(int argc, char *argv[])
86{
87 enum {
88 LCASE = 1 << 0,
89 UCASE = 1 << 1,
90 SWAB = 1 << 2,
91 NOERROR = 1 << 3,
92 NOTRUNC = 1 << 4,
93 SYNC = 1 << 5,
94 } conv = 0;
95 char *arg, *val, *end;
96 const char *iname = "-", *oname = "-";
97 int ifd = 0, ofd = 1, eof = 0;
98 size_t len, bs = 0, ibs = 512, obs = 512, ipos = 0, opos = 0;
99 off_t skip = 0, seek = 0, count = -1;
100 ssize_t ret;
101 unsigned char *buf;
102
103 argv0 = argc ? (argc--, *argv++) : "dd";
104 for (; argc > 0; argc--, argv++) {
105 arg = *argv;
106 val = strchr(arg, '=');
107 if (!val)
108 usage();
109 *val++ = '\0';
110 if (strcmp(arg, "if") == 0) {
111 iname = val;
112 } else if (strcmp(arg, "of") == 0) {
113 oname = val;
114 } else if (strcmp(arg, "ibs") == 0) {
115 ibs = parsesize(val);
116 } else if (strcmp(arg, "obs") == 0) {
117 obs = parsesize(val);
118 } else if (strcmp(arg, "bs") == 0) {
119 bs = parsesize(val);
120 } else if (strcmp(arg, "skip") == 0) {
121 skip = estrtonum(val, 0, LLONG_MAX);
122 } else if (strcmp(arg, "seek") == 0) {
123 seek = estrtonum(val, 0, LLONG_MAX);
124 } else if (strcmp(arg, "count") == 0) {
125 count = estrtonum(val, 0, LLONG_MAX);
126 } else if (strcmp(arg, "conv") == 0) {
127 do {
128 end = strchr(val, ',');
129 if (end)
130 *end++ = '\0';
131 if (strcmp(val, "lcase") == 0)
132 conv |= LCASE;
133 else if (strcmp(val, "ucase") == 0)
134 conv |= UCASE;
135 else if (strcmp(val, "swab") == 0)
136 conv |= SWAB;
137 else if (strcmp(val, "noerror") == 0)
138 conv |= NOERROR;
139 else if (strcmp(val, "notrunc") == 0)
140 conv |= NOTRUNC;
141 else if (strcmp(val, "sync") == 0)
142 conv |= SYNC;
143 else
144 eprintf("unknown conv flag '%s'\n", val);
145 val = end;
146 } while (val);
147 } else {
148 weprintf("unknown operand '%s'\n", arg);
149 usage();
150 }
151 }
152
153 if (bs)
154 ibs = obs = bs;
155 if (strcmp(iname, "-") != 0) {
156 ifd = open(iname, O_RDONLY);
157 if (ifd < 0)
158 eprintf("open %s:", iname);
159 }
160 if (strcmp(oname, "-") != 0) {
161 ofd = open(oname, O_WRONLY | O_CREAT | (conv & NOTRUNC || seek ? 0 : O_TRUNC), 0666);
162 if (ofd < 0)
163 eprintf("open %s:", oname);
164 }
165
166 len = MAX(ibs, obs) + ibs;
167 buf = emalloc(len);
168 if (skip && lseek(ifd, skip * ibs, SEEK_SET) < 0) {
169 while (skip--) {
170 ret = read(ifd, buf, ibs);
171 if (ret < 0)
172 eprintf("read:");
173 if (ret == 0) {
174 eof = 1;
175 break;
176 }
177 }
178 }
179 if (seek) {
180 if (!(conv & NOTRUNC) && ftruncate(ofd, seek * ibs) != 0)
181 eprintf("ftruncate:");
182 if (lseek(ofd, seek * ibs, SEEK_SET) < 0)
183 eprintf("lseek:");
184 /* XXX: handle non-seekable files */
185 }
186 while (!eof) {
187 while (ipos - opos < obs) {
188 if (ifull + ipart == count) {
189 eof = 1;
190 break;
191 }
192 ret = read(ifd, buf + ipos, ibs);
193 if (ret == 0) {
194 eof = 1;
195 break;
196 }
197 if (ret < 0) {
198 weprintf("read:");
199 if (!(conv & NOERROR))
200 return 1;
201 summary();
202 if (!(conv & SYNC))
203 continue;
204 ret = 0;
205 }
206 if ((size_t)ret < ibs) {
207 ipart++;
208 if (conv & SYNC) {
209 memset(buf + ipos + ret, 0, ibs - ret);
210 ret = ibs;
211 }
212 } else {
213 ifull++;
214 }
215 if (conv & SWAB)
216 bswap(buf + ipos, ret);
217 if (conv & LCASE)
218 lcase(buf + ipos, ret);
219 if (conv & UCASE)
220 ucase(buf + ipos, ret);
221 ipos += ret;
222 if (bs && !(conv & (SWAB | LCASE | UCASE)))
223 break;
224 }
225 if (ipos == opos)
226 break;
227 do {
228 ret = write(ofd, buf + opos, MIN(obs, ipos - opos));
229 if (ret < 0)
230 eprintf("write:");
231 if (ret == 0)
232 eprintf("write returned 0\n");
233 if ((size_t)ret < obs)
234 opart++;
235 else
236 ofull++;
237 opos += ret;
238 } while (ipos - opos >= (eof ? 1 : obs));
239 if (opos < ipos)
240 memmove(buf, buf + opos, ipos - opos);
241 ipos -= opos;
242 opos = 0;
243 }
244 summary();
245
246 return 0;
247}