ce8bcc6a55c938bfd6f5d1e88eb6cfee73907a18
[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 <sys/types.h>
12 #include <sys/stat.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include "busybox.h"
19
20
21 static const struct suffix_mult dd_suffixes[] = {
22         { "c", 1 },
23         { "w", 2 },
24         { "b", 512 },
25         { "kD", 1000 },
26         { "k", 1024 },
27         { "MD", 1000000 },
28         { "M", 1048576 },
29         { "GD", 1000000000 },
30         { "G", 1073741824 },
31         { NULL, 0 }
32 };
33
34 int dd_main(int argc, char **argv)
35 {
36         size_t out_full = 0;
37         size_t out_part = 0;
38         size_t in_full = 0;
39         size_t in_part = 0;
40         size_t count = -1;
41         size_t bs = 512;
42         ssize_t n;
43         off_t seek = 0;
44         off_t skip = 0;
45         int sync_flag = FALSE;
46         int noerror = FALSE;
47         int trunc_flag = TRUE;
48         int oflag;
49         int ifd;
50         int ofd;
51         int i;
52         const char *infile = NULL;
53         const char *outfile = NULL;
54         char *buf;
55
56         for (i = 1; i < argc; i++) {
57                 if (strncmp("bs=", argv[i], 3) == 0)
58                         bs = bb_xparse_number(argv[i]+3, dd_suffixes);
59                 else if (strncmp("count=", argv[i], 6) == 0)
60                         count = bb_xparse_number(argv[i]+6, dd_suffixes);
61                 else if (strncmp("seek=", argv[i], 5) == 0)
62                         seek = bb_xparse_number(argv[i]+5, dd_suffixes);
63                 else if (strncmp("skip=", argv[i], 5) == 0)
64                         skip = bb_xparse_number(argv[i]+5, dd_suffixes);
65                 else if (strncmp("if=", argv[i], 3) == 0)
66                         infile = argv[i]+3;
67                 else if (strncmp("of=", argv[i], 3) == 0)
68                         outfile = argv[i]+3;
69                 else if (strncmp("conv=", argv[i], 5) == 0) {
70                         buf = argv[i]+5;
71                         while (1) {
72                                 if (strncmp("notrunc", buf, 7) == 0) {
73                                         trunc_flag = FALSE;
74                                         buf += 7;
75                                 } else if (strncmp("sync", buf, 4) == 0) {
76                                         sync_flag = TRUE;
77                                         buf += 4;
78                                 } else if (strncmp("noerror", buf, 7) == 0) {
79                                         noerror = TRUE;
80                                         buf += 7;
81                                 } else {
82                                         bb_error_msg_and_die("invalid conversion `%s'", argv[i]+5);
83                                 }
84                                 if (buf[0] == '\0')
85                                         break;
86                                 if (buf[0] == ',')
87                                         buf++;
88                         }
89                 } else
90                         bb_show_usage();
91         }
92
93         buf = xmalloc(bs);
94
95         if (infile != NULL) {
96                 ifd = bb_xopen(infile, O_RDONLY);
97         } else {
98                 ifd = STDIN_FILENO;
99                 infile = bb_msg_standard_input;
100         }
101
102         if (outfile != NULL) {
103                 oflag = O_WRONLY | O_CREAT;
104
105                 if (!seek && trunc_flag) {
106                         oflag |= O_TRUNC;
107                 }
108
109                 ofd = bb_xopen3(outfile, oflag, 0666);
110
111                 if (seek && trunc_flag) {
112                         if (ftruncate(ofd, seek * bs) < 0) {
113                                 struct stat st;
114
115                                 if (fstat (ofd, &st) < 0 || S_ISREG (st.st_mode) ||
116                                                 S_ISDIR (st.st_mode)) {
117                                         bb_perror_msg_and_die("%s", outfile);
118                                 }
119                         }
120                 }
121         } else {
122                 ofd = STDOUT_FILENO;
123                 outfile = bb_msg_standard_output;
124         }
125
126         if (skip) {
127                 if (lseek(ifd, skip * bs, SEEK_CUR) < 0) {
128                         bb_perror_msg_and_die("%s", infile);
129                 }
130         }
131
132         if (seek) {
133                 if (lseek(ofd, seek * bs, SEEK_CUR) < 0) {
134                         bb_perror_msg_and_die("%s", outfile);
135                 }
136         }
137
138         while (in_full + in_part != count) {
139                 if (noerror) {
140                         /* Pre-zero the buffer when doing the noerror thing */
141                         memset(buf, '\0', bs);
142                 }
143                 n = safe_read(ifd, buf, bs);
144                 if (n < 0) {
145                         if (noerror) {
146                                 n = bs;
147                                 bb_perror_msg("%s", infile);
148                         } else {
149                                 bb_perror_msg_and_die("%s", infile);
150                         }
151                 }
152                 if (n == 0) {
153                         break;
154                 }
155                 if ((size_t)n == bs) {
156                         in_full++;
157                 } else {
158                         in_part++;
159                 }
160                 if (sync_flag) {
161                         memset(buf + n, '\0', bs - n);
162                         n = bs;
163                 }
164                 n = bb_full_write(ofd, buf, n);
165                 if (n < 0) {
166                         bb_perror_msg_and_die("%s", outfile);
167                 }
168                 if ((size_t)n == bs) {
169                         out_full++;
170                 } else {
171                         out_part++;
172                 }
173         }
174
175         if (close (ifd) < 0) {
176                 bb_perror_msg_and_die("%s", infile);
177         }
178
179         if (close (ofd) < 0) {
180                 bb_perror_msg_and_die("%s", outfile);
181         }
182
183         fprintf(stderr, "%ld+%ld records in\n%ld+%ld records out\n",
184                         (long)in_full, (long)in_part,
185                         (long)out_full, (long)out_part);
186
187         return EXIT_SUCCESS;
188 }