cttyhack: add missing ';'
[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 <signal.h>  /* For FEATURE_DD_SIGNAL_HANDLING */
12 #include "libbb.h"
13
14 /* This is a NOEXEC applet. Be very careful! */
15
16
17 enum {
18         ifd = STDIN_FILENO,
19         ofd = STDOUT_FILENO,
20 };
21
22 static const struct suffix_mult dd_suffixes[] = {
23         { "c", 1 },
24         { "w", 2 },
25         { "b", 512 },
26         { "kD", 1000 },
27         { "k", 1024 },
28         { "K", 1024 },  /* compat with coreutils dd */
29         { "MD", 1000000 },
30         { "M", 1048576 },
31         { "GD", 1000000000 },
32         { "G", 1073741824 },
33         { }
34 };
35
36 struct globals {
37         off_t out_full, out_part, in_full, in_part;
38 };
39 #define G (*(struct globals*)&bb_common_bufsiz1)
40 /* We have to zero it out because of NOEXEC */
41 #define INIT_G() memset(&G, 0, sizeof(G))
42
43
44 static void dd_output_status(int ATTRIBUTE_UNUSED cur_signal)
45 {
46         /* Deliberately using %u, not %d */
47         fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n"
48                         "%"OFF_FMT"u+%"OFF_FMT"u records out\n",
49                         G.in_full, G.in_part,
50                         G.out_full, G.out_part);
51 }
52
53 static ssize_t full_write_or_warn(const void *buf, size_t len,
54         const char *const filename)
55 {
56         ssize_t n = full_write(ofd, buf, len);
57         if (n < 0)
58                 bb_perror_msg("writing '%s'", filename);
59         return n;
60 }
61
62 static bool write_and_stats(const void *buf, size_t len, size_t obs,
63         const char *filename)
64 {
65         ssize_t n = full_write_or_warn(buf, len, filename);
66         if (n < 0)
67                 return 1;
68         if (n == obs)
69                 G.out_full++;
70         else if (n) /* > 0 */
71                 G.out_part++;
72         return 0;
73 }
74
75 #if ENABLE_LFS
76 #define XATOU_SFX xatoull_sfx
77 #else
78 #define XATOU_SFX xatoul_sfx
79 #endif
80
81 int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
82 int dd_main(int argc, char **argv)
83 {
84         enum {
85                 FLAG_SYNC    = 1 << 0,
86                 FLAG_NOERROR = 1 << 1,
87                 FLAG_NOTRUNC = 1 << 2,
88                 FLAG_TWOBUFS = 1 << 3,
89                 FLAG_COUNT   = 1 << 4,
90         };
91         static const char keywords[] ALIGN1 =
92                 "bs=\0""count=\0""seek=\0""skip=\0""if=\0""of=\0"
93 #if ENABLE_FEATURE_DD_IBS_OBS
94                 "ibs=\0""obs=\0""conv=\0""notrunc\0""sync\0""noerror\0"
95 #endif
96                 ;
97         enum {
98                 OP_bs = 1,
99                 OP_count,
100                 OP_seek,
101                 OP_skip,
102                 OP_if,
103                 OP_of,
104 #if ENABLE_FEATURE_DD_IBS_OBS
105                 OP_ibs,
106                 OP_obs,
107                 OP_conv,
108                 OP_conv_notrunc,
109                 OP_conv_sync,
110                 OP_conv_noerror,
111 #endif
112         };
113         int exitcode = EXIT_FAILURE;
114         size_t ibs = 512, obs = 512;
115         ssize_t n, w;
116         char *ibuf, *obuf;
117         /* And these are all zeroed at once! */
118         struct {
119                 int flags;
120                 size_t oc;
121                 off_t count;
122                 off_t seek, skip;
123                 const char *infile, *outfile;
124 #if ENABLE_FEATURE_DD_SIGNAL_HANDLING
125                 struct sigaction sigact;
126 #endif
127         } Z;
128 #define flags   (Z.flags  )
129 #define oc      (Z.oc     )
130 #define count   (Z.count  )
131 #define seek    (Z.seek   )
132 #define skip    (Z.skip   )
133 #define infile  (Z.infile )
134 #define outfile (Z.outfile)
135 #define sigact  (Z.sigact )
136
137         memset(&Z, 0, sizeof(Z));
138         INIT_G();
139         //fflush(NULL); - is this needed because of NOEXEC?
140
141 #if ENABLE_FEATURE_DD_SIGNAL_HANDLING
142         sigact.sa_handler = dd_output_status;
143         sigact.sa_flags = SA_RESTART;
144         sigemptyset(&sigact.sa_mask);
145         sigaction(SIGUSR1, &sigact, NULL);
146 #endif
147
148         for (n = 1; n < argc; n++) {
149                 smalluint key_len;
150                 smalluint what;
151                 char *key;
152                 char *arg = argv[n];
153
154 //XXX:FIXME: we reject plain "dd --" This would cost ~20 bytes, so..
155 //if (*arg == '-' && *++arg == '-' && !*++arg) continue;
156                 key = strstr(arg, "=");
157                 if (key == NULL)
158                         bb_show_usage();
159                 key_len = key - arg + 1;
160                 key = xstrndup(arg, key_len);
161                 what = index_in_strings(keywords, key) + 1;
162                 if (ENABLE_FEATURE_CLEAN_UP)
163                         free(key);
164                 if (what == 0)
165                         bb_show_usage();
166                 arg += key_len;
167                 /* Must fit into positive ssize_t */
168 #if ENABLE_FEATURE_DD_IBS_OBS
169                         if (what == OP_ibs) {
170                                 ibs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes);
171                                 continue;
172                         }
173                         if (what == OP_obs) {
174                                 obs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes);
175                                 continue;
176                         }
177                         if (what == OP_conv) {
178                                 while (1) {
179                                         /* find ',', replace them with nil so we can use arg for
180                                          * index_in_strings() without copying.
181                                          * We rely on arg being non-null, else strchr would fault.
182                                          */
183                                         key = strchr(arg, ',');
184                                         if (key)
185                                                 *key = '\0';
186                                         what = index_in_strings(keywords, arg) + 1;
187                                         if (what < OP_conv_notrunc)
188                                                 bb_error_msg_and_die(bb_msg_invalid_arg, arg, "conv");
189                                         if (what == OP_conv_notrunc)
190                                                 flags |= FLAG_NOTRUNC;
191                                         if (what == OP_conv_sync)
192                                                 flags |= FLAG_SYNC;
193                                         if (what == OP_conv_noerror)
194                                                 flags |= FLAG_NOERROR;
195                                         if (!key) /* no ',' left, so this was the last specifier */
196                                                 break;
197                                         arg = key + 1; /* skip this keyword and ',' */
198                                 }
199                                 continue;
200                         }
201 #endif
202                 if (what == OP_bs) {
203                         ibs = obs = xatoul_range_sfx(arg, 1, ((size_t)-1L)/2, dd_suffixes);
204                         continue;
205                 }
206                 /* These can be large: */
207                 if (what == OP_count) {
208                         flags |= FLAG_COUNT;
209                         count = XATOU_SFX(arg, dd_suffixes);
210                         continue;
211                 }
212                 if (what == OP_seek) {
213                         seek = XATOU_SFX(arg, dd_suffixes);
214                         continue;
215                 }
216                 if (what == OP_skip) {
217                         skip = XATOU_SFX(arg, dd_suffixes);
218                         continue;
219                 }
220                 if (what == OP_if) {
221                         infile = arg;
222                         continue;
223                 }
224                 if (what == OP_of)
225                         outfile = arg;
226         }
227 //XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
228         ibuf = obuf = xmalloc(ibs);
229         if (ibs != obs) {
230                 flags |= FLAG_TWOBUFS;
231                 obuf = xmalloc(obs);
232         }
233         if (infile != NULL)
234                 xmove_fd(xopen(infile, O_RDONLY), ifd);
235         else {
236                 infile = bb_msg_standard_input;
237         }
238         if (outfile != NULL) {
239                 int oflag = O_WRONLY | O_CREAT;
240
241                 if (!seek && !(flags & FLAG_NOTRUNC))
242                         oflag |= O_TRUNC;
243
244                 xmove_fd(xopen(outfile, oflag), ofd);
245
246                 if (seek && !(flags & FLAG_NOTRUNC)) {
247                         if (ftruncate(ofd, seek * obs) < 0) {
248                                 struct stat st;
249
250                                 if (fstat(ofd, &st) < 0 || S_ISREG(st.st_mode) ||
251                                                 S_ISDIR(st.st_mode))
252                                         goto die_outfile;
253                         }
254                 }
255         } else {
256                 outfile = bb_msg_standard_output;
257         }
258         if (skip) {
259                 if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
260                         while (skip-- > 0) {
261                                 n = safe_read(ifd, ibuf, ibs);
262                                 if (n < 0)
263                                         goto die_infile;
264                                 if (n == 0)
265                                         break;
266                         }
267                 }
268         }
269         if (seek) {
270                 if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
271                         goto die_outfile;
272         }
273
274         while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
275                 if (flags & FLAG_NOERROR) /* Pre-zero the buffer if conv=noerror */
276                         memset(ibuf, 0, ibs);
277                 n = safe_read(ifd, ibuf, ibs);
278                 if (n == 0)
279                         break;
280                 if (n < 0) {
281                         if (!(flags & FLAG_NOERROR))
282                                 goto die_infile;
283                         n = ibs;
284                         bb_simple_perror_msg(infile);
285                 }
286                 if ((size_t)n == ibs)
287                         G.in_full++;
288                 else {
289                         G.in_part++;
290                         if (flags & FLAG_SYNC) {
291                                 memset(ibuf + n, '\0', ibs - n);
292                                 n = ibs;
293                         }
294                 }
295                 if (flags & FLAG_TWOBUFS) {
296                         char *tmp = ibuf;
297                         while (n) {
298                                 size_t d = obs - oc;
299
300                                 if (d > n)
301                                         d = n;
302                                 memcpy(obuf + oc, tmp, d);
303                                 n -= d;
304                                 tmp += d;
305                                 oc += d;
306                                 if (oc == obs) {
307                                         if (write_and_stats(obuf, obs, obs, outfile))
308                                                 goto out_status;
309                                         oc = 0;
310                                 }
311                         }
312                 } else if (write_and_stats(ibuf, n, obs, outfile))
313                         goto out_status;
314         }
315
316         if (ENABLE_FEATURE_DD_IBS_OBS && oc) {
317                 w = full_write_or_warn(obuf, oc, outfile);
318                 if (w < 0) goto out_status;
319                 if (w > 0)
320                         G.out_part++;
321         }
322         if (close(ifd) < 0) {
323  die_infile:
324                 bb_simple_perror_msg_and_die(infile);
325         }
326
327         if (close(ofd) < 0) {
328  die_outfile:
329                 bb_simple_perror_msg_and_die(outfile);
330         }
331
332         exitcode = EXIT_SUCCESS;
333  out_status:
334         dd_output_status(0);
335
336         return exitcode;
337 }