OSDN Git Service

9990a0f49d37b1118a16fe51bfbd8caa2c7c6cf4
[android-x86/external-toybox.git] / toys / pending / dd.c
1 /* dd.c - program to convert and copy a file.
2  *
3  * Copyright 2013 Ashwini Kumar <ak.ashwini@gmail.com>
4  * Copyright 2013 Kyungwan Han <asura321@gmail.com>
5  *
6  * See  http://opengroup.org/onlinepubs/9699919799/utilities/dd.html
7
8 USE_DD(NEWTOY(dd, NULL, TOYFLAG_USR|TOYFLAG_BIN))
9
10 config DD
11   bool "dd"
12   default n
13   help
14     usage: dd [if=FILE] [of=FILE] [ibs=N] [obs=N] [bs=N] [count=N] [skip=N]
15             [seek=N] [conv=notrunc|noerror|sync|fsync] [status=noxfer|none]
16
17     Options:
18     if=FILE   Read from FILE instead of stdin
19     of=FILE   Write to FILE instead of stdout
20     bs=N      Read and write N bytes at a time
21     ibs=N     Read N bytes at a time
22     obs=N     Write N bytes at a time
23     count=N   Copy only N input blocks
24     skip=N    Skip N input blocks
25     seek=N    Skip N output blocks
26     conv=notrunc  Don't truncate output file
27     conv=noerror  Continue after read errors
28     conv=sync     Pad blocks with zeros
29     conv=fsync    Physically write data out before finishing
30     status=noxfer Don't show transfer rate
31     status=none   Don't show transfer rate or records in/out
32
33     Numbers may be suffixed by c (*1), w (*2), b (*512), kD (*1000), k (*1024),
34     MD (*1000*1000), M (*1024*1024), GD (*1000*1000*1000) or G (*1024*1024*1024).
35 */
36
37 #define FOR_dd
38 #include "toys.h"
39
40 GLOBALS(
41   int show_xfer;
42   int show_records;
43   unsigned long long bytes, c_count, in_full, in_part, out_full, out_part;
44   struct timeval start;
45   struct {
46     char *name;
47     int fd;
48     unsigned char *buff, *bp;
49     long sz, count;
50     unsigned long long offset;
51   } in, out;
52 );
53
54 #define C_CONV    0x0000
55 #define C_BS      0x0001
56 #define C_COUNT   0x0002
57 #define C_IBS     0x0004
58 #define C_OBS     0x0008
59 #define C_IF      0x0010
60 #define C_OF      0x0020
61 #define C_SEEK    0x0040
62 #define C_SKIP    0x0080
63 #define C_SYNC    0x0100
64 #define C_FSYNC   0x0200
65 #define C_NOERROR 0x0400
66 #define C_NOTRUNC 0x0800
67 #define C_STATUS  0x1000
68
69 struct pair {
70   char *name;
71   unsigned val;
72 };
73
74 static struct pair suffixes[] = {
75   { "c", 1 }, { "w", 2 }, { "b", 512 },
76   { "kD", 1000 }, { "k", 1024 }, { "K", 1024 },
77   { "MD", 1000000 }, { "M", 1048576 },
78   { "GD", 1000000000 }, { "G", 1073741824 }
79 };
80
81 static struct pair clist[] = {
82   { "fsync",    C_FSYNC },
83   { "noerror",  C_NOERROR },
84   { "notrunc",  C_NOTRUNC },
85   { "sync",     C_SYNC },
86 };
87
88 static struct pair operands[] = {
89   // keep the array sorted by name, bsearch() can be used.
90   { "bs",      C_BS    },
91   { "conv",    C_CONV  },
92   { "count",   C_COUNT },
93   { "ibs",     C_IBS   },
94   { "if",      C_IF    },
95   { "obs",     C_OBS   },
96   { "of",      C_OF    },
97   { "seek",    C_SEEK  },
98   { "skip",    C_SKIP  },
99   { "status",  C_STATUS},
100 };
101
102 static unsigned long long strsuftoll(char *arg, int def, unsigned long long max)
103 {
104   unsigned long long result;
105   char *p = arg;
106   int i, idx = -1;
107
108   while (isspace(*p)) p++;
109   if (*p == '-') error_exit("invalid number '%s'", arg);
110
111   errno = 0;
112   result = strtoull(p, &p, 0);
113   if (errno == ERANGE || result > max || result < def)
114     perror_exit("invalid number '%s'", arg);
115   if (*p != '\0') {
116     for (i = 0; i < ARRAY_LEN(suffixes); i++)
117       if (!strcmp(p, suffixes[i].name)) idx = i;
118     if (idx == -1 || (max/suffixes[idx].val < result)) 
119       error_exit("invalid number '%s'", arg);
120     result *= suffixes[idx].val;
121   }
122   return result;
123 }
124
125 static void status()
126 {
127   double seconds;
128   struct timeval now;
129
130   gettimeofday(&now, NULL);
131   seconds = ((now.tv_sec * 1000000 + now.tv_usec) -
132       (TT.start.tv_sec * 1000000 + TT.start.tv_usec))/1000000.0;
133
134   if (TT.show_records)
135     fprintf(stderr, "%llu+%llu records in\n%llu+%llu records out\n",
136             TT.in_full, TT.in_part, TT.out_full, TT.out_part);
137
138   if (TT.show_xfer) {
139     human_readable(toybuf, TT.bytes, HR_SPACE|HR_B);
140     fprintf(stderr, "%llu bytes (%s) copied, ", TT.bytes, toybuf);
141     human_readable(toybuf, TT.bytes/seconds, HR_SPACE|HR_B);
142     fprintf(stderr, "%f s, %s/s\n", seconds, toybuf);
143   }
144 }
145
146 static int xmove_fd(int fd)
147 {
148   int newfd;
149
150   if (fd > STDERR_FILENO) return fd;
151   if ((newfd = fcntl(fd, F_DUPFD, 3) < 0)) perror_exit("dupfd IO");
152   close(fd);
153   return newfd;
154 }
155
156 static void setup_inout()
157 {
158   /* for C_BS, in/out is done as it is. so only in.sz is enough.
159    * With Single buffer there will be overflow in a read following partial read
160    */
161   TT.in.buff = TT.out.buff = xmalloc(TT.in.sz + ((toys.optflags & C_BS)? 0: TT.out.sz));
162   TT.in.bp = TT.out.bp = TT.in.buff;
163   //setup input
164   if (!TT.in.name) {
165     TT.in.name = "stdin";
166     TT.in.fd = STDIN_FILENO;
167   } else {
168     TT.in.fd = xopen(TT.in.name, O_RDONLY);
169     TT.in.fd = xmove_fd(TT.in.fd);
170   }
171   //setup outout
172   if (!TT.out.name) {
173     TT.out.name = "stdout";
174     TT.out.fd = STDOUT_FILENO;
175   } else {
176     int flags = O_WRONLY|O_CREAT;
177     if (!(toys.optflags&C_NOTRUNC)) flags |= O_TRUNC;
178     TT.out.fd = xcreate(TT.out.name, flags, 0666);
179     TT.out.fd = xmove_fd(TT.out.fd);
180   }
181
182   if (TT.in.offset) {
183     if (lseek(TT.in.fd, (off_t)(TT.in.offset * TT.in.sz), SEEK_CUR) < 0) {
184       while (TT.in.offset--) {
185         ssize_t n = read(TT.in.fd, TT.in.bp, TT.in.sz);
186
187         if (n < 0) {
188           if (toys.optflags & C_NOERROR) { //warn message and status
189             error_msg("%s: read error", TT.in.name);
190             status();
191           } else perror_exit("%s: read error", TT.in.name);
192         } else if (!n) {
193           xprintf("%s: Can't skip\n", TT.in.name);
194           exit(0);
195         }
196       }
197     }
198   }
199
200   if (TT.out.offset) xlseek(TT.out.fd, (off_t)(TT.out.offset * TT.out.sz), SEEK_CUR);
201 }
202
203 static void write_out(int all)
204 {
205   TT.out.bp = TT.out.buff;
206   while (TT.out.count) {
207     ssize_t nw = writeall(TT.out.fd, TT.out.bp, ((all)? TT.out.count : TT.out.sz));
208
209     all = 0; //further writes will be on obs
210     if (nw <= 0) perror_exit("%s: write error", TT.out.name);
211     if (nw == TT.out.sz) TT.out_full++;
212     else TT.out_part++;
213     TT.out.count -= nw;
214     TT.out.bp += nw;
215     TT.bytes += nw;
216     if (TT.out.count < TT.out.sz) break;
217   }
218   if (TT.out.count) memmove(TT.out.buff, TT.out.bp, TT.out.count); //move remainder to front
219 }
220
221 static int comp(const void *a, const void *b) //const to shut compiler up
222 {
223   return strcmp(((struct pair*)a)->name, ((struct pair*)b)->name);
224 }
225
226 void dd_main()
227 {
228   struct pair *res, key;
229   char *arg;
230   long sz;
231
232   TT.show_xfer = TT.show_records = 1;
233
234   TT.in.sz = TT.out.sz = 512; //default io block size
235   while (*toys.optargs) {
236     if (!(arg = strchr(*toys.optargs, '='))) error_exit("unknown arg %s", *toys.optargs);
237     *arg++ = '\0';
238     if (!*arg) help_exit(0);
239     key.name = *toys.optargs;
240     if (!(res = bsearch(&key, operands, ARRAY_LEN(operands), sizeof(struct pair),
241             comp))) error_exit("unknown arg %s", key.name);
242
243     toys.optflags |= res->val;
244     switch (res->val) {
245       case C_BS:
246         TT.in.sz = TT.out.sz = strsuftoll(arg, 1, LONG_MAX);
247         break;
248       case C_IBS:
249         sz = strsuftoll(arg, 1, LONG_MAX);
250         if (!(toys.optflags & C_BS)) TT.in.sz = sz;
251         break;
252       case C_OBS:
253         sz = strsuftoll(arg, 1, LONG_MAX);
254         if (!(toys.optflags & C_BS)) TT.out.sz = sz;
255         break;
256       case C_COUNT:
257         TT.c_count = strsuftoll(arg, 0, ULLONG_MAX);
258         break;
259       case C_IF:
260         TT.in.name = arg;
261         break;
262       case C_OF:
263         TT.out.name = arg;
264         break;
265       case C_SEEK:
266         TT.out.offset = strsuftoll(arg, 0, ULLONG_MAX);
267         break;
268       case C_SKIP:
269         TT.in.offset = strsuftoll(arg, 0, ULLONG_MAX);
270         break;
271       case C_STATUS:
272         if (!strcmp(arg, "noxfer")) TT.show_xfer = 0;
273         else if (!strcmp(arg, "none")) TT.show_xfer = TT.show_records = 0;
274         else error_exit("unknown status '%s'", arg);
275         break;
276       case C_CONV:
277         while (arg) {
278           key.name = strsep(&arg, ",");
279           if (!(res = bsearch(&key, clist, ARRAY_LEN(clist), 
280                   sizeof(struct pair), comp)))
281             error_exit("unknown conversion %s", key.name);
282
283           toys.optflags |= res->val;
284         }            
285         break;
286     }
287     toys.optargs++;
288   }
289
290   signal(SIGINT, generic_signal);
291   signal(SIGUSR1, generic_signal);
292
293   setup_inout();
294   gettimeofday(&TT.start, NULL);
295
296   if ((toys.optflags&C_SEEK) && !(toys.optflags & C_NOTRUNC))
297     ftruncate(TT.out.fd, TT.out.offset * TT.out.sz);
298
299   while (!(toys.optflags & C_COUNT) || (TT.in_full + TT.in_part) < TT.c_count) {
300     ssize_t n;
301
302     if (toys.signal) {
303       // Show progress and exit on SIGINT or just continue on SIGUSR1.
304       status();
305       if (toys.signal == SIGINT) exit_signal(toys.signal);
306       toys.signal = 0;
307     }
308
309     TT.in.bp = TT.in.buff + TT.in.count;
310     if (toys.optflags & C_SYNC) memset(TT.in.bp, 0, TT.in.sz);
311     if (!(n = read(TT.in.fd, TT.in.bp, TT.in.sz))) break;
312     if (n < 0) { 
313       if (errno == EINTR) continue;
314       //read error case.
315       perror_msg("%s: read error", TT.in.name);
316       if (!(toys.optflags & C_NOERROR)) exit(1);
317       status();
318       xlseek(TT.in.fd, TT.in.sz, SEEK_CUR);
319       if (!(toys.optflags & C_SYNC)) continue;
320       // if SYNC, then treat as full block of nuls
321       n = TT.in.sz;
322     }
323     if (n == TT.in.sz) {
324       TT.in_full++;
325       TT.in.count += n;
326     } else {
327       TT.in_part++;
328       if (toys.optflags & C_SYNC) TT.in.count += TT.in.sz;
329       else TT.in.count += n;
330     }
331
332     TT.out.count = TT.in.count;
333     if (toys.optflags & C_BS) {
334       write_out(1);
335       TT.in.count = 0;
336       continue;
337     }
338
339     if (TT.in.count >= TT.out.sz) {
340       write_out(0);
341       TT.in.count = TT.out.count;
342     }
343   }
344   if (TT.out.count) write_out(1); //write any remaining input blocks
345   if (toys.optflags & C_FSYNC && fsync(TT.out.fd) < 0) 
346     perror_exit("%s: fsync fail", TT.out.name);
347
348   close(TT.in.fd);
349   close(TT.out.fd);
350   if (TT.in.buff) free(TT.in.buff);
351
352   status();
353 }