OSDN Git Service

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