Magellan Linux

Contents of /trunk/mkinitrd-magellan/klibc/usr/utils/dd.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1297 - (show annotations) (download)
Fri May 27 15:12:11 2011 UTC (12 years, 11 months ago) by niro
File MIME type: text/plain
File size: 10187 byte(s)
-updated to klibc-1.5.22 with mntproc definitions patch included
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 close(rd_fd);
490 return 1;
491 }
492 }
493
494 /*
495 * Skip obs-sized blocks of output file.
496 */
497 if (OPT_SEEK->str && skip_blocks(wr_fd, out_buf, seek, obs)) {
498 close(rd_fd);
499 close(wr_fd);
500 return 1;
501 }
502
503 /*
504 * Skip ibs-sized blocks of input file.
505 */
506 if (OPT_SKIP->str && skip_blocks(rd_fd, in_buf, skip, ibs)) {
507 close(rd_fd);
508 close(wr_fd);
509 return 1;
510 }
511
512 memset(&stats, 0, sizeof(stats));
513
514 /*
515 * Do the real work
516 */
517 ret = dd(rd_fd, wr_fd, &stats);
518
519 if (close(rd_fd) == -1)
520 perror(OPT_IF->str ? OPT_IF->str : "stdin");
521 if (close(wr_fd) == -1)
522 perror(OPT_OF->str ? OPT_OF->str : "stdout");
523
524 fprintf(stderr, "%u+%u records in\n", stats.in_full, stats.in_partial);
525 fprintf(stderr, "%u+%u records out\n",
526 stats.out_full, stats.out_partial);
527 if (stats.truncated)
528 fprintf(stderr, "%u truncated record%s\n",
529 stats.truncated, stats.truncated == 1 ? "" : "s");
530
531 /*
532 * ret will be -SIGINT if we got a SIGINT. Raise
533 * the signal again to cause us to terminate with
534 * SIGINT status.
535 */
536 if (ret == -SIGINT)
537 raise(SIGINT);
538
539 return ret;
540 }