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}