- pull from busybox_scratch: r15829:15850
[oweals/busybox.git] / coreutils / dd.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini dd implementation for busybox
4  *
5  *
6  * Copyright (C) 2000,2001  Matt Kraai
7  *
8  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
9  */
10
11 #include "busybox.h"
12 #include <signal.h>  /* For FEATURE_DD_SIGNAL_HANDLING */
13
14 static const struct suffix_mult dd_suffixes[] = {
15         { "c", 1 },
16         { "w", 2 },
17         { "b", 512 },
18         { "kD", 1000 },
19         { "k", 1024 },
20         { "MD", 1000000 },
21         { "M", 1048576 },
22         { "GD", 1000000000 },
23         { "G", 1073741824 },
24         { NULL, 0 }
25 };
26
27 static size_t out_full, out_part, in_full, in_part;
28
29 static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal)
30 {
31         bb_fprintf(stderr, "%ld+%ld records in\n%ld+%ld records out\n",
32                         (long)in_full, (long)in_part,
33                         (long)out_full, (long)out_part);
34 }
35
36 int dd_main(int argc, char **argv)
37 {
38 #define sync_flag       (1<<0)
39 #define noerror         (1<<1)
40 #define trunc_flag      (1<<2)
41 #define twobufs_flag (1<<3)
42         int flags = trunc_flag;
43         size_t count = -1, oc = 0, ibs = 512, obs = 512;
44         ssize_t n;
45         off_t seek = 0, skip = 0;
46         int oflag, ifd, ofd;
47         const char *infile = NULL, *outfile = NULL;
48         char *ibuf, *obuf;
49
50         if (ENABLE_FEATURE_DD_SIGNAL_HANDLING) {
51                 struct sigaction sa;
52
53                 memset(&sa, 0, sizeof(sa));
54                 sa.sa_handler = dd_output_status;
55                 sa.sa_flags = SA_RESTART;
56                 sigemptyset(&sa.sa_mask);
57                 sigaction(SIGUSR1, &sa, 0);
58         }
59
60         for (n = 1; n < argc; n++) {
61                 if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("ibs=", argv[n], 4)) {
62                         ibs = bb_xparse_number(argv[n]+4, dd_suffixes);
63                         flags |= twobufs_flag;
64                 } else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("obs=", argv[n], 4)) {
65                         obs = bb_xparse_number(argv[n]+4, dd_suffixes);
66                         flags |= twobufs_flag;
67                 } else if (!strncmp("bs=", argv[n], 3))
68                         ibs = obs = bb_xparse_number(argv[n]+3, dd_suffixes);
69                 else if (!strncmp("count=", argv[n], 6))
70                         count = bb_xparse_number(argv[n]+6, dd_suffixes);
71                 else if (!strncmp("seek=", argv[n], 5))
72                         seek = bb_xparse_number(argv[n]+5, dd_suffixes);
73                 else if (!strncmp("skip=", argv[n], 5))
74                         skip = bb_xparse_number(argv[n]+5, dd_suffixes);
75                 else if (!strncmp("if=", argv[n], 3))
76                         infile = argv[n]+3;
77                 else if (!strncmp("of=", argv[n], 3))
78                         outfile = argv[n]+3;
79                 else if (ENABLE_FEATURE_DD_IBS_OBS && !strncmp("conv=", argv[n], 5)) {
80                         ibuf = argv[n]+5;
81                         while (1) {
82                                 if (!strncmp("notrunc", ibuf, 7)) {
83                                         flags ^= trunc_flag;
84                                         ibuf += 7;
85                                 } else if (!strncmp("sync", ibuf, 4)) {
86                                         flags |= sync_flag;
87                                         ibuf += 4;
88                                 } else if (!strncmp("noerror", ibuf, 7)) {
89                                         flags |= noerror;
90                                         ibuf += 7;
91                                 } else {
92                                         bb_error_msg_and_die(bb_msg_invalid_arg, argv[n]+5, "conv");
93                                 }
94                                 if (ibuf[0] == '\0') break;
95                                 if (ibuf[0] == ',') ibuf++;
96                         }
97                 } else
98                         bb_show_usage();
99         }
100         ibuf = xmalloc(ibs);
101
102         if (flags & twobufs_flag)
103                 obuf = xmalloc(obs);
104         else
105                 obuf = ibuf;
106
107         if (infile != NULL)
108                 ifd = xopen(infile, O_RDONLY);
109         else {
110                 ifd = STDIN_FILENO;
111                 infile = bb_msg_standard_input;
112         }
113
114         if (outfile != NULL) {
115                 oflag = O_WRONLY | O_CREAT;
116
117                 if (!seek && (flags & trunc_flag))
118                         oflag |= O_TRUNC;
119
120                 ofd = xopen3(outfile, oflag, 0666);
121
122                 if (seek && (flags & trunc_flag)) {
123                         if (ftruncate(ofd, seek * obs) < 0) {
124                                 struct stat st;
125
126                                 if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) ||
127                                                 S_ISDIR(st.st_mode))
128                                         goto die_outfile;
129                         }
130                 }
131         } else {
132                 ofd = STDOUT_FILENO;
133                 outfile = bb_msg_standard_output;
134         }
135
136         if (skip) {
137                 if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
138                         while (skip-- > 0) {
139                                 n = safe_read(ifd, ibuf, ibs);
140                                 if (n < 0)
141                                         bb_perror_msg_and_die("%s", infile);
142                                 if (n == 0)
143                                         break;
144                         }
145                 }
146         }
147
148         if (seek) {
149                 if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
150                         goto die_outfile;
151         }
152
153         while (in_full + in_part != count) {
154                 if (flags & noerror) {
155                         /* Pre-zero the buffer when doing the noerror thing */
156                         memset(ibuf, '\0', ibs);
157                 }
158
159                 n = safe_read(ifd, ibuf, ibs);
160                 if (n == 0)
161                         break;
162                 if (n < 0) {
163                         if (flags & noerror) {
164                                 n = ibs;
165                                 bb_perror_msg("%s", infile);
166                         } else
167                                 bb_perror_msg_and_die("%s", infile);
168                 }
169                 if ((size_t)n == ibs)
170                         in_full++;
171                 else {
172                         in_part++;
173                         if (sync_flag) {
174                                 memset(ibuf + n, '\0', ibs - n);
175                                 n = ibs;
176                         }
177                 }
178                 if (flags & twobufs_flag) {
179                         char *tmp = ibuf;
180                         while (n) {
181                                 size_t d = obs - oc;
182
183                                 if (d > n)
184                                         d = n;
185                                 memcpy(obuf + oc, tmp, d);
186                                 n -= d;
187                                 tmp += d;
188                                 oc += d;
189                                 if (oc == obs) {
190                                         xwrite(ofd, obuf, obs);
191                                         out_full++;
192                                         oc = 0;
193                                 }
194                         }
195                 } else {
196                         xwrite(ofd, ibuf, n);
197                         if (n == ibs)
198                                 out_full++;
199                         else
200                                 out_part++;
201                 }
202         }
203         
204         if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
205                 xwrite(ofd, obuf, oc);
206                 out_part++;
207         }
208         if (close (ifd) < 0) {
209                 bb_perror_msg_and_die("%s", infile);
210         }
211
212         if (close (ofd) < 0) {
213 die_outfile:
214                 bb_perror_msg_and_die("%s", outfile);
215         }
216
217         dd_output_status(0);
218
219         return EXIT_SUCCESS;
220 }