40288fd4ddf6aae575949b2585a94cc596e192b6
[oweals/busybox.git] / coreutils / dd.c
1 /*
2  * Copyright (c) 1999 by David I. Bell
3  * Permission is granted to use, distribute, or modify this source,
4  * provided that this copyright notice remains intact.
5  *
6  * The "dd" command, originally taken from sash.
7  *
8  * Permission to distribute this code under the GPL has been granted.
9  * Mostly rewritten and bugs fixed for busybox by Erik Andersen <andersee@debian.org>
10  */
11
12 #include "internal.h"
13 #include <stdio.h>
14 #include <fcntl.h>
15 #include <errno.h>
16
17 const char dd_usage[] =
18     "Copy a file, converting and formatting according to options\n\
19 \n\
20 usage: [if=name] [of=name] [bs=n] [count=n]\n\
21 \tif=FILE\tread from FILE instead of stdin\n\
22 \tof=FILE\twrite to FILE instead of stout\n\
23 \tbs=n\tread and write N bytes at a time\n\
24 \tcount=n\tcopy only n input blocks\n\
25 \tskip=n\tskip n input blocks\n\
26 \n\
27 BYTES may be suffixed: by k for x1024, b for x512, and w for x2.\n";
28
29
30
31
32 /*
33  * Read a number with a possible multiplier.
34  * Returns -1 if the number format is illegal.
35  */
36 static long getNum (const char *cp)
37 {
38     long value;
39
40     if (!isDecimal (*cp))
41         return -1;
42
43     value = 0;
44
45     while (isDecimal (*cp))
46         value = value * 10 + *cp++ - '0';
47
48     switch (*cp++) {
49     case 'k':
50         value *= 1024;
51         break;
52
53     case 'b':
54         value *= 512;
55         break;
56
57     case 'w':
58         value *= 2;
59         break;
60
61     case '\0':
62         return value;
63
64     default:
65         return -1;
66     }
67
68     if (*cp)
69         return -1;
70
71     return value;
72 }
73
74
75 extern int dd_main (int argc, char **argv)
76 {
77     const char *inFile;
78     const char *outFile;
79     char *cp;
80     int inFd;
81     int outFd;
82     int inCc = 0;
83     int outCc;
84     int skipBlocks;
85     int blockSize;
86     long count;
87     long intotal;
88     long outTotal;
89     unsigned char *buf;
90
91     inFile = NULL;
92     outFile = NULL;
93     blockSize = 512;
94     skipBlocks = 0;
95     count = 1;
96
97
98     argc--;
99     argv++;
100
101     /* Parse any options */
102     while (argc) {
103         if (inFile == NULL && (strncmp("if", *argv, 2) == 0))
104             inFile=*argv;
105         else if (outFile == NULL && (strncmp("of", *argv, 2) == 0))
106             outFile=*argv;
107         else if (strncmp("count", *argv, 5) == 0) {
108             count = getNum (*argv);
109             if (count <= 0) {
110                 fprintf (stderr, "Bad count value %ld\n", count);
111                 goto usage;
112             }
113         }
114         else if (strncmp("bs", *argv, 2) == 0) {
115             blockSize = getNum(*argv);
116             if (blockSize <= 0) {
117                 fprintf (stderr, "Bad block size value %d\n", blockSize);
118                 goto usage;
119             }
120         }
121         else if (strncmp("skip", *argv, 4) == 0) {
122             skipBlocks = atoi( *argv); 
123             if (skipBlocks <= 0) {
124                 fprintf (stderr, "Bad skip value %d\n", skipBlocks);
125                 goto usage;
126             }
127
128         }
129         else {
130             fprintf (stderr, "Got here. argv=%s\n", *argv);
131             goto usage;
132
133         argc--;
134         argv++;
135         }
136     }
137     if ( inFile == NULL || outFile == NULL)
138         goto usage;
139
140     buf = malloc (blockSize);
141     if (buf == NULL) {
142         fprintf (stderr, "Cannot allocate buffer\n");
143         return( FALSE);
144     }
145
146     intotal = 0;
147     outTotal = 0;
148
149     if (!inFile)
150         inFd = STDIN;
151     else
152         inFd = open (inFile, 0);
153
154     if (inFd < 0) {
155         perror (inFile);
156         free (buf);
157         return( FALSE);
158     }
159
160     if (!outFile)
161         outFd = STDOUT;
162     else
163         outFd = creat (outFile, 0666);
164
165     if (outFd < 0) {
166         perror (outFile);
167         close (inFd);
168         free (buf);
169         return( FALSE);
170     }
171
172     lseek(inFd, skipBlocks*blockSize, SEEK_SET);
173     while (outTotal < count * blockSize) {
174         inCc = read (inFd, buf, blockSize);
175         if (inCc < 0) {
176             perror (inFile);
177             goto cleanup;
178         }
179         intotal += inCc;
180         cp = buf;
181
182         while (intotal > outTotal) {
183             if (outTotal + inCc > count * blockSize)
184                 inCc = count * blockSize - outTotal;
185             outCc = write (outFd, cp, inCc);
186             if (outCc < 0) {
187                 perror (outFile);
188                 goto cleanup;
189             }
190
191             inCc -= outCc;
192             cp += outCc;
193             outTotal += outCc;
194         }
195     }
196
197     if (inCc < 0)
198         perror (inFile);
199
200   cleanup:
201     close (inFd);
202     close (outFd);
203     free (buf);
204
205     printf ("%ld+%d records in\n", intotal / blockSize,
206             (intotal % blockSize) != 0);
207     printf ("%ld+%d records out\n", outTotal / blockSize,
208             (outTotal % blockSize) != 0);
209     exit( TRUE);
210   usage:
211
212     fprintf (stderr, "%s", dd_usage);
213     exit( FALSE);
214 }
215
216