Contents of /tags/mkinitrd-6_3_0/klibc/usr/utils/dd.c
Parent Directory | Revision Log
Revision 1139 -
(show annotations)
(download)
Thu Aug 19 10:14:02 2010 UTC (13 years, 10 months ago) by niro
File MIME type: text/plain
File size: 10096 byte(s)
Thu Aug 19 10:14:02 2010 UTC (13 years, 10 months ago) by niro
File MIME type: text/plain
File size: 10096 byte(s)
tagged 'mkinitrd-6_3_0'
1 | #include <ctype.h> |
2 | #include <errno.h> |
3 | #include <fcntl.h> |
4 | #include <limits.h> |
5 | #include <setjmp.h> |
6 | #include <signal.h> |
7 | #include <stdint.h> |
8 | #include <stdio.h> |
9 | #include <stdlib.h> |
10 | #include <string.h> |
11 | |
12 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) |
13 | |
14 | static char *progname; |
15 | |
16 | struct option { |
17 | const char *opt; |
18 | char *str; |
19 | char *arg; |
20 | }; |
21 | |
22 | struct conv { |
23 | const char str[8]; |
24 | unsigned int set; |
25 | unsigned int exclude; |
26 | }; |
27 | |
28 | #define CONV_BLOCK (1<<0) |
29 | #define CONV_UNBLOCK (1<<1) |
30 | |
31 | #define CONV_LCASE (1<<2) |
32 | #define CONV_UCASE (1<<3) |
33 | |
34 | #define CONV_SWAB (1<<4) |
35 | #define CONV_NOERROR (1<<5) |
36 | #define CONV_NOTRUNC (1<<6) |
37 | #define CONV_SYNC (1<<7) |
38 | |
39 | static struct option options[] = { |
40 | {"bs", NULL, NULL}, |
41 | #define OPT_BS (&options[0]) |
42 | {"cbs", NULL, NULL}, |
43 | #define OPT_CBS (&options[1]) |
44 | {"conv", NULL, NULL}, |
45 | #define OPT_CONV (&options[2]) |
46 | {"count", NULL, NULL}, |
47 | #define OPT_COUNT (&options[3]) |
48 | {"ibs", NULL, NULL}, |
49 | #define OPT_IBS (&options[4]) |
50 | {"if", NULL, NULL}, |
51 | #define OPT_IF (&options[5]) |
52 | {"obs", NULL, NULL}, |
53 | #define OPT_OBS (&options[6]) |
54 | {"of", NULL, NULL}, |
55 | #define OPT_OF (&options[7]) |
56 | {"seek", NULL, NULL}, |
57 | #define OPT_SEEK (&options[8]) |
58 | {"skip", NULL, NULL} |
59 | #define OPT_SKIP (&options[9]) |
60 | }; |
61 | |
62 | static const struct conv conv_opts[] = { |
63 | {"block", CONV_BLOCK, CONV_UNBLOCK}, |
64 | {"unblock", CONV_UNBLOCK, CONV_BLOCK}, |
65 | {"lcase", CONV_LCASE, CONV_UCASE}, |
66 | {"ucase", CONV_UCASE, CONV_LCASE}, |
67 | {"swab", CONV_SWAB, 0}, |
68 | {"noerror", CONV_NOERROR, 0}, |
69 | {"notrunc", CONV_NOTRUNC, 0}, |
70 | {"sync", CONV_SYNC, 0}, |
71 | }; |
72 | |
73 | static size_t cbs; |
74 | static unsigned int conv; |
75 | static unsigned int count; |
76 | static size_t ibs = 512; |
77 | static size_t obs = 512; |
78 | static unsigned int seek; |
79 | static unsigned int skip; |
80 | static char *in_buf; |
81 | static char *out_buf; |
82 | |
83 | static size_t parse_bs(struct option *opt) |
84 | { |
85 | unsigned long val, realval = 1; |
86 | char *str = opt->str; |
87 | int err = 0; |
88 | |
89 | do { |
90 | char *s = str; |
91 | val = strtoul(str, &str, 10); |
92 | if (s == str || (val == ULONG_MAX && errno == ERANGE)) { |
93 | err = 1; |
94 | break; |
95 | } |
96 | |
97 | /* |
98 | * This option may be followed by |
99 | * 'b', 'k' or 'x' |
100 | */ |
101 | if (*str == 'b') { |
102 | val *= 512; |
103 | str++; |
104 | } else if (*str == 'k') { |
105 | val *= 1024; |
106 | str++; |
107 | } |
108 | realval *= val; |
109 | if (*str != 'x') |
110 | break; |
111 | str++; |
112 | } while (1); |
113 | |
114 | if (*str != '\0') |
115 | err = 1; |
116 | |
117 | if (err) { |
118 | fprintf(stderr, "%s: bad operand `%s'\n", progname, opt->arg); |
119 | exit(1); |
120 | } |
121 | |
122 | return (size_t) realval; |
123 | } |
124 | |
125 | static unsigned int parse_num(struct option *opt) |
126 | { |
127 | unsigned long val; |
128 | char *str = opt->str; |
129 | |
130 | val = strtoul(str, &str, 10); |
131 | if (str == opt->str || (val == ULONG_MAX && errno == ERANGE) || |
132 | val > UINT_MAX) { |
133 | fprintf(stderr, "%s: bad operand `%s'\n", progname, opt->arg); |
134 | exit(1); |
135 | } |
136 | |
137 | return (unsigned int)val; |
138 | } |
139 | |
140 | static int parse_options(int argc, char *argv[]) |
141 | { |
142 | unsigned int i; |
143 | char *p, *s; |
144 | int arg; |
145 | |
146 | /* |
147 | * We cheat here; we don't parse the operand values |
148 | * themselves here. We merely split the operands |
149 | * up. This means that bs=foo bs=1 won't produce |
150 | * an error. |
151 | */ |
152 | for (arg = 1; arg < argc; arg++) { |
153 | unsigned int len; |
154 | |
155 | s = strchr(argv[arg], '='); |
156 | if (!s) |
157 | s = argv[arg]; /* don't recognise this arg */ |
158 | |
159 | len = s - argv[arg]; |
160 | for (i = 0; i < ARRAY_SIZE(options); i++) { |
161 | if (strncmp(options[i].opt, argv[arg], len) != 0) |
162 | continue; |
163 | |
164 | options[i].str = s + 1; |
165 | options[i].arg = argv[arg]; |
166 | break; |
167 | } |
168 | |
169 | if (i == ARRAY_SIZE(options)) { |
170 | fprintf(stderr, "%s: bad operand `%s'\n", |
171 | progname, argv[arg]); |
172 | return 1; |
173 | } |
174 | } |
175 | |
176 | /* |
177 | * Translate numeric operands. |
178 | */ |
179 | if (OPT_IBS->str) |
180 | ibs = parse_bs(OPT_IBS); |
181 | if (OPT_OBS->str) |
182 | obs = parse_bs(OPT_OBS); |
183 | if (OPT_CBS->str) |
184 | cbs = parse_bs(OPT_CBS); |
185 | if (OPT_COUNT->str) |
186 | count = parse_num(OPT_COUNT); |
187 | if (OPT_SEEK->str) |
188 | seek = parse_num(OPT_SEEK); |
189 | if (OPT_SKIP->str) |
190 | skip = parse_num(OPT_SKIP); |
191 | |
192 | /* |
193 | * If bs= is specified, it overrides ibs= and obs= |
194 | */ |
195 | if (OPT_BS->str) |
196 | ibs = obs = parse_bs(OPT_BS); |
197 | |
198 | /* |
199 | * And finally conv= |
200 | */ |
201 | if (OPT_CONV->str) { |
202 | p = OPT_CONV->str; |
203 | |
204 | while ((s = strsep(&p, ",")) != NULL) { |
205 | for (i = 0; i < ARRAY_SIZE(conv_opts); i++) { |
206 | if (strcmp(s, conv_opts[i].str) != 0) |
207 | continue; |
208 | conv &= ~conv_opts[i].exclude; |
209 | conv |= conv_opts[i].set; |
210 | break; |
211 | } |
212 | |
213 | if (i == ARRAY_SIZE(conv_opts)) { |
214 | fprintf(stderr, "%s: bad conversion `%s'\n", |
215 | progname, s); |
216 | return 1; |
217 | } |
218 | } |
219 | } |
220 | |
221 | if (conv & (CONV_BLOCK | CONV_UNBLOCK) && cbs == 0) { |
222 | fprintf(stderr, "%s: block/unblock conversion with zero cbs\n", |
223 | progname); |
224 | return 1; |
225 | } |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | static int safe_read(int fd, void *buf, size_t size) |
231 | { |
232 | int ret, count = 0; |
233 | char *p = buf; |
234 | |
235 | while (size) { |
236 | ret = read(fd, p, size); |
237 | |
238 | /* |
239 | * If we got EINTR, go again. |
240 | */ |
241 | if (ret == -1 && errno == EINTR) |
242 | continue; |
243 | |
244 | /* |
245 | * If we encountered an error condition |
246 | * or read 0 bytes (EOF) return what we |
247 | * have. |
248 | */ |
249 | if (ret == -1 || ret == 0) |
250 | return count ? count : ret; |
251 | |
252 | /* |
253 | * We read some bytes. |
254 | */ |
255 | count += ret; |
256 | size -= ret; |
257 | p += ret; |
258 | } |
259 | |
260 | return count; |
261 | } |
262 | |
263 | static int skip_blocks(int fd, void *buf, unsigned int blks, size_t size) |
264 | { |
265 | unsigned int blk; |
266 | int ret = 0; |
267 | |
268 | /* |
269 | * Try to seek. |
270 | */ |
271 | for (blk = 0; blk < blks; blk++) { |
272 | ret = lseek(fd, size, SEEK_CUR); |
273 | if (ret == -1) |
274 | break; |
275 | } |
276 | |
277 | /* |
278 | * If we failed to seek, read instead. |
279 | * FIXME: we don't handle short reads here, or |
280 | * EINTR correctly. |
281 | */ |
282 | if (blk == 0 && ret == -1 && errno == ESPIPE) { |
283 | for (blk = 0; blk < blks; blk++) { |
284 | ret = safe_read(fd, buf, size); |
285 | if (ret != (int)size) |
286 | break; |
287 | } |
288 | } |
289 | |
290 | if (ret == -1) { |
291 | perror("seek/skip"); |
292 | return 1; |
293 | } |
294 | return 0; |
295 | } |
296 | |
297 | struct stats { |
298 | unsigned int in_full; |
299 | unsigned int in_partial; |
300 | unsigned int out_full; |
301 | unsigned int out_partial; |
302 | unsigned int truncated; |
303 | }; |
304 | |
305 | static int do_dd(int rd, int wr, struct stats *stats) |
306 | { |
307 | unsigned int i; |
308 | int ret; |
309 | int fill_val = 0; |
310 | size_t out_size = 0; |
311 | size_t in_size; |
312 | char *buf; |
313 | |
314 | if (conv & (CONV_BLOCK | CONV_UNBLOCK)) |
315 | fill_val = ' '; |
316 | |
317 | while (!OPT_COUNT->str || count-- != 0) { |
318 | buf = in_buf; |
319 | |
320 | /* |
321 | * 1. read ibs-sized buffer |
322 | */ |
323 | in_size = ret = read(rd, in_buf, ibs); |
324 | if (ret == -1 || (ret == 0 && (conv & CONV_NOERROR) == 0)) |
325 | break; |
326 | |
327 | if (in_size == ibs) { |
328 | stats->in_full++; |
329 | } else { |
330 | stats->in_partial++; |
331 | |
332 | /* |
333 | * 2. zero (or append spaces) |
334 | */ |
335 | if (conv & CONV_SYNC) { |
336 | memset(in_buf + in_size, fill_val, |
337 | ibs - in_size); |
338 | in_size = ibs; |
339 | } |
340 | } |
341 | |
342 | /* |
343 | * 4. swab conversion. With an odd number of bytes, |
344 | * last byte does not get swapped. |
345 | */ |
346 | if (conv & CONV_SWAB) { |
347 | char c; |
348 | |
349 | for (i = 1; i < in_size; i += 2) { |
350 | c = in_buf[i - 1]; |
351 | in_buf[i - 1] = in_buf[i]; |
352 | in_buf[i] = c; |
353 | } |
354 | } |
355 | |
356 | /* |
357 | * 5. remaining conversions. |
358 | */ |
359 | if (conv & CONV_LCASE) |
360 | for (i = 0; i < in_size; i++) |
361 | in_buf[i] = tolower(in_buf[i]); |
362 | |
363 | if (conv & CONV_UCASE) |
364 | for (i = 0; i < in_size; i++) |
365 | in_buf[i] = toupper(in_buf[i]); |
366 | |
367 | /* block/unblock ? */ |
368 | |
369 | /* |
370 | * 6. Aggregate into obs sized buffers. |
371 | * If the in_size is obs-sized and we have no |
372 | * data waiting, just write "buf" to the output. |
373 | */ |
374 | if (out_size == 0 && in_size == obs) { |
375 | write(wr, buf, obs); |
376 | stats->out_full++; |
377 | } else { |
378 | /* |
379 | * We had data waiting, or we didn't have an |
380 | * obs-sized input block. We need to append |
381 | * the input data to the output buffer. |
382 | */ |
383 | unsigned int space; |
384 | char *in_ptr = in_buf; |
385 | |
386 | do { |
387 | space = obs - out_size; |
388 | if (space > in_size) |
389 | space = in_size; |
390 | |
391 | memcpy(out_buf + out_size, in_ptr, space); |
392 | out_size += space; |
393 | in_size -= space; |
394 | in_ptr += space; |
395 | |
396 | if (out_size == obs) { |
397 | write(wr, out_buf, obs); |
398 | stats->out_full++; |
399 | out_size = 0; |
400 | } |
401 | } while (out_size == 0 && in_size); |
402 | |
403 | if (in_size) { |
404 | memcpy(out_buf, in_ptr, in_size); |
405 | out_size = in_size; |
406 | } |
407 | } |
408 | } |
409 | |
410 | if (out_size) { |
411 | write(wr, out_buf, out_size); |
412 | stats->out_partial++; |
413 | } |
414 | |
415 | return 0; |
416 | } |
417 | |
418 | static sigjmp_buf jmp; |
419 | |
420 | static void sigint_handler(int sig) |
421 | { |
422 | siglongjmp(jmp, -sig); |
423 | } |
424 | |
425 | static int dd(int rd_fd, int wr_fd, struct stats *stats) |
426 | { |
427 | int ret; |
428 | |
429 | ret = sigsetjmp(jmp, 1); |
430 | if (ret == 0) { |
431 | sysv_signal(SIGINT, sigint_handler); |
432 | ret = do_dd(rd_fd, wr_fd, stats); |
433 | } |
434 | |
435 | sysv_signal(SIGINT, SIG_DFL); |
436 | return ret; |
437 | } |
438 | |
439 | int main(int argc, char *argv[]) |
440 | { |
441 | struct stats stats; |
442 | int ret; |
443 | int rd_fd = 0, wr_fd = 1; |
444 | |
445 | progname = argv[0]; |
446 | |
447 | ret = parse_options(argc, argv); |
448 | if (ret) |
449 | return ret; |
450 | |
451 | if (conv & (CONV_BLOCK | CONV_UNBLOCK)) { |
452 | fprintf(stderr, "%s: block/unblock not implemented\n", |
453 | progname); |
454 | return 1; |
455 | } |
456 | |
457 | in_buf = malloc(ibs); |
458 | if (!in_buf) { |
459 | perror("malloc ibs"); |
460 | return 1; |
461 | } |
462 | |
463 | out_buf = malloc(obs); |
464 | if (!out_buf) { |
465 | perror("malloc obs"); |
466 | return 1; |
467 | } |
468 | |
469 | /* |
470 | * Open the input file, if specified. |
471 | */ |
472 | if (OPT_IF->str) { |
473 | rd_fd = open(OPT_IF->str, O_RDONLY); |
474 | if (rd_fd == -1) { |
475 | perror("open input file"); |
476 | return 1; |
477 | } |
478 | } |
479 | |
480 | /* |
481 | * Open the output file, if specified. |
482 | */ |
483 | if (OPT_OF->str) { |
484 | int flags = O_WRONLY|O_CREAT; |
485 | flags |= (conv & CONV_NOTRUNC) ? 0 : O_TRUNC; |
486 | wr_fd = open(OPT_OF->str, flags, 0666); |
487 | if (wr_fd == -1) { |
488 | perror("open output file"); |
489 | return 1; |
490 | } |
491 | } |
492 | |
493 | /* |
494 | * Skip obs-sized blocks of output file. |
495 | */ |
496 | if (OPT_SEEK->str && skip_blocks(wr_fd, out_buf, seek, obs)) |
497 | return 1; |
498 | |
499 | /* |
500 | * Skip ibs-sized blocks of input file. |
501 | */ |
502 | if (OPT_SKIP->str && skip_blocks(rd_fd, in_buf, skip, ibs)) |
503 | return 1; |
504 | |
505 | memset(&stats, 0, sizeof(stats)); |
506 | |
507 | /* |
508 | * Do the real work |
509 | */ |
510 | ret = dd(rd_fd, wr_fd, &stats); |
511 | |
512 | if (close(rd_fd) == -1) |
513 | perror(OPT_IF->str ? OPT_IF->str : "stdin"); |
514 | if (close(wr_fd) == -1) |
515 | perror(OPT_OF->str ? OPT_OF->str : "stdout"); |
516 | |
517 | fprintf(stderr, "%u+%u records in\n", stats.in_full, stats.in_partial); |
518 | fprintf(stderr, "%u+%u records out\n", |
519 | stats.out_full, stats.out_partial); |
520 | if (stats.truncated) |
521 | fprintf(stderr, "%u truncated record%s\n", |
522 | stats.truncated, stats.truncated == 1 ? "" : "s"); |
523 | |
524 | /* |
525 | * ret will be -SIGINT if we got a SIGINT. Raise |
526 | * the signal again to cause us to terminate with |
527 | * SIGINT status. |
528 | */ |
529 | if (ret == -SIGINT) |
530 | raise(SIGINT); |
531 | |
532 | return ret; |
533 | } |