Print this page
2556 dd should accept M,G,T,... for sizes
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/dd/dd.c
+++ new/usr/src/cmd/dd/dd.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License, Version 1.0 only
6 6 * (the "License"). You may not use this file except in compliance
7 7 * with the License.
8 8 *
9 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 10 * or http://www.opensolaris.org/os/licensing.
11 11 * See the License for the specific language governing permissions
12 12 * and limitations under the License.
13 13 *
14 14 * When distributing Covered Code, include this CDDL HEADER in each
15 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
↓ open down ↓ |
15 lines elided |
↑ open up ↑ |
16 16 * If applicable, add the following below this CDDL HEADER, with the
17 17 * fields enclosed by brackets "[]" replaced with your own identifying
18 18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 19 *
20 20 * CDDL HEADER END
21 21 */
22 22
23 23 /*
24 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
25 25 * Use is subject to license terms.
26 + * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
26 27 */
27 28
28 29 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 30 /* All Rights Reserved */
30 31
31 32 #pragma ident "%Z%%M% %I% %E% SMI"
32 33
33 34 /*
34 35 * convert and copy
35 36 */
36 37
37 38 #include <stdio.h>
38 39 #include <signal.h>
39 40 #include <fcntl.h>
40 41 #include <sys/param.h>
41 42 #include <sys/types.h>
42 43 #include <sys/sysmacros.h>
43 44 #include <sys/stat.h>
44 45 #include <unistd.h>
45 46 #include <stdlib.h>
46 47 #include <locale.h>
47 48 #include <string.h>
48 49
49 50 /* The BIG parameter is machine dependent. It should be a long integer */
50 51 /* constant that can be used by the number parser to check the validity */
51 52 /* of numeric parameters. On 16-bit machines, it should probably be */
52 53 /* the maximum unsigned integer, 0177777L. On 32-bit machines where */
53 54 /* longs are the same size as ints, the maximum signed integer is more */
54 55 /* appropriate. This value is 017777777777L. In 64 bit environments, */
55 56 /* the maximum signed integer value is 0777777777777777777777LL */
56 57
57 58 #define BIG 0777777777777777777777LL
58 59
59 60 #define BSIZE 512
60 61
61 62 /* Option parameters */
62 63
63 64 #define COPY 0 /* file copy, preserve input block size */
64 65 #define REBLOCK 1 /* file copy, change block size */
65 66 #define LCREBLOCK 2 /* file copy, convert to lower case */
66 67 #define UCREBLOCK 3 /* file copy, convert to upper case */
67 68 #define NBASCII 4 /* file copy, convert from EBCDIC to ASCII */
68 69 #define LCNBASCII 5 /* file copy, EBCDIC to lower case ASCII */
69 70 #define UCNBASCII 6 /* file copy, EBCDIC to upper case ASCII */
70 71 #define NBEBCDIC 7 /* file copy, convert from ASCII to EBCDIC */
71 72 #define LCNBEBCDIC 8 /* file copy, ASCII to lower case EBCDIC */
72 73 #define UCNBEBCDIC 9 /* file copy, ASCII to upper case EBCDIC */
73 74 #define NBIBM 10 /* file copy, convert from ASCII to IBM */
74 75 #define LCNBIBM 11 /* file copy, ASCII to lower case IBM */
75 76 #define UCNBIBM 12 /* file copy, ASCII to upper case IBM */
76 77 #define UNBLOCK 13 /* convert blocked ASCII to ASCII */
77 78 #define LCUNBLOCK 14 /* convert blocked ASCII to lower case ASCII */
78 79 #define UCUNBLOCK 15 /* convert blocked ASCII to upper case ASCII */
79 80 #define ASCII 16 /* convert blocked EBCDIC to ASCII */
80 81 #define LCASCII 17 /* convert blocked EBCDIC to lower case ASCII */
81 82 #define UCASCII 18 /* convert blocked EBCDIC to upper case ASCII */
82 83 #define BLOCK 19 /* convert ASCII to blocked ASCII */
83 84 #define LCBLOCK 20 /* convert ASCII to lower case blocked ASCII */
84 85 #define UCBLOCK 21 /* convert ASCII to upper case blocked ASCII */
85 86 #define EBCDIC 22 /* convert ASCII to blocked EBCDIC */
86 87 #define LCEBCDIC 23 /* convert ASCII to lower case blocked EBCDIC */
87 88 #define UCEBCDIC 24 /* convert ASCII to upper case blocked EBCDIC */
88 89 #define IBM 25 /* convert ASCII to blocked IBM */
89 90 #define LCIBM 26 /* convert ASCII to lower case blocked IBM */
90 91 #define UCIBM 27 /* convert ASCII to upper case blocked IBM */
91 92 #define LCASE 01 /* flag - convert to lower case */
92 93 #define UCASE 02 /* flag - convert to upper case */
93 94 #define SWAB 04 /* flag - swap bytes before conversion */
94 95 #define NERR 010 /* flag - proceed on input errors */
95 96 #define SYNC 020 /* flag - pad short input blocks with nulls */
96 97 #define BADLIMIT 5 /* give up if no progress after BADLIMIT trys */
97 98 #define SVR4XLATE 0 /* use default EBCDIC translation */
98 99 #define BSDXLATE 1 /* use BSD-compatible EBCDIC translation */
99 100
100 101 #define USAGE\
101 102 "usage: dd [if=file] [of=file] [ibs=n|nk|nb|nxm] [obs=n|nk|nb|nxm]\n"\
102 103 " [bs=n|nk|nb|nxm] [cbs=n|nk|nb|nxm] [files=n] [skip=n]\n"\
103 104 " [iseek=n] [oseek=n] [seek=n] [count=n] [conv=[ascii]\n"\
104 105 " [,ebcdic][,ibm][,asciib][,ebcdicb][,ibmb]\n"\
105 106 " [,block|unblock][,lcase|ucase][,swab]\n"\
106 107 " [,noerror][,notrunc][,sync]]\n"
107 108
108 109 /* Global references */
109 110
110 111 /* Local routine declarations */
111 112
112 113 static int match(char *);
113 114 static void term();
114 115 static unsigned long long number();
115 116 static unsigned char *flsh();
116 117 static void stats();
117 118
118 119 /* Local data definitions */
119 120
120 121 static unsigned ibs; /* input buffer size */
121 122 static unsigned obs; /* output buffer size */
122 123 static unsigned bs; /* buffer size, overrules ibs and obs */
123 124 static unsigned cbs; /* conversion buffer size, used for block conversions */
124 125 static unsigned ibc; /* number of bytes still in the input buffer */
125 126 static unsigned obc; /* number of bytes in the output buffer */
126 127 static unsigned cbc; /* number of bytes in the conversion buffer */
127 128
128 129 static int ibf; /* input file descriptor */
129 130 static int obf; /* output file descriptor */
130 131 static int cflag; /* conversion option flags */
131 132 static int skipf; /* if skipf == 1, skip rest of input line */
132 133 static unsigned long long nifr; /* count of full input records */
133 134 static unsigned long long nipr; /* count of partial input records */
134 135 static unsigned long long nofr; /* count of full output records */
135 136 static unsigned long long nopr; /* count of partial output records */
136 137 static unsigned long long ntrunc; /* count of truncated input lines */
137 138 static unsigned long long nbad; /* count of bad records since last */
138 139 /* good one */
139 140 static int files; /* number of input files to concatenate (tape only) */
140 141 static off_t skip; /* number of input records to skip */
141 142 static off_t iseekn; /* number of input records to seek past */
142 143 static off_t oseekn; /* number of output records to seek past */
143 144 static unsigned long long count; /* number of input records to copy */
144 145 /* (0 = all) */
145 146 static int trantype; /* BSD or SVr4 compatible EBCDIC */
146 147
147 148 static char *string; /* command arg pointer */
148 149 static char *ifile; /* input file name pointer */
149 150 static char *ofile; /* output file name pointer */
150 151 static unsigned char *ibuf; /* input buffer pointer */
151 152 static unsigned char *obuf; /* output buffer pointer */
152 153
153 154 /* This is an EBCDIC to ASCII conversion table */
154 155 /* from a proposed BTL standard April 16, 1979 */
155 156
156 157 static unsigned char svr4_etoa [] =
157 158 {
158 159 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177,
159 160 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017,
160 161 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207,
161 162 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037,
162 163 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033,
163 164 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007,
164 165 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004,
165 166 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032,
166 167 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246,
167 168 0247, 0250, 0325, 0056, 0074, 0050, 0053, 0174,
168 169 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
169 170 0260, 0261, 0041, 0044, 0052, 0051, 0073, 0176,
170 171 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267,
171 172 0270, 0271, 0313, 0054, 0045, 0137, 0076, 0077,
172 173 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301,
173 174 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042,
174 175 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
175 176 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311,
176 177 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160,
177 178 0161, 0162, 0136, 0314, 0315, 0316, 0317, 0320,
178 179 0321, 0345, 0163, 0164, 0165, 0166, 0167, 0170,
179 180 0171, 0172, 0322, 0323, 0324, 0133, 0326, 0327,
180 181 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
181 182 0340, 0341, 0342, 0343, 0344, 0135, 0346, 0347,
182 183 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
183 184 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355,
184 185 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120,
185 186 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363,
186 187 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130,
187 188 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371,
188 189 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
189 190 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377,
190 191 };
191 192
192 193 /* This is an ASCII to EBCDIC conversion table */
193 194 /* from a proposed BTL standard April 16, 1979 */
194 195
195 196 static unsigned char svr4_atoe [] =
196 197 {
197 198 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057,
198 199 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017,
199 200 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046,
200 201 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037,
201 202 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175,
202 203 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141,
203 204 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
204 205 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157,
205 206 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
206 207 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326,
207 208 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346,
208 209 0347, 0350, 0351, 0255, 0340, 0275, 0232, 0155,
209 210 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
210 211 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226,
211 212 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246,
212 213 0247, 0250, 0251, 0300, 0117, 0320, 0137, 0007,
213 214 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027,
214 215 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033,
215 216 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010,
216 217 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341,
217 218 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110,
218 219 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
219 220 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147,
220 221 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165,
221 222 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215,
222 223 0216, 0217, 0220, 0152, 0233, 0234, 0235, 0236,
223 224 0237, 0240, 0252, 0253, 0254, 0112, 0256, 0257,
224 225 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
225 226 0270, 0271, 0272, 0273, 0274, 0241, 0276, 0277,
226 227 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333,
227 228 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355,
228 229 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377,
229 230 };
230 231
231 232 /* Table for ASCII to IBM (alternate EBCDIC) code conversion */
232 233
233 234 static unsigned char svr4_atoibm[] =
234 235 {
235 236 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057,
236 237 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017,
237 238 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046,
238 239 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037,
239 240 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175,
240 241 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141,
241 242 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
242 243 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157,
243 244 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
244 245 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326,
245 246 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346,
246 247 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155,
247 248 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
248 249 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226,
249 250 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246,
250 251 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007,
251 252 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027,
252 253 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033,
253 254 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010,
254 255 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341,
255 256 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110,
256 257 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
257 258 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147,
258 259 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165,
259 260 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215,
260 261 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236,
261 262 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257,
262 263 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
263 264 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
264 265 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333,
265 266 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355,
266 267 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377,
267 268 };
268 269
269 270 /* Table for conversion of ASCII to lower case ASCII */
270 271
271 272 static unsigned char utol[] =
272 273 {
273 274 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
274 275 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
275 276 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
276 277 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
277 278 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
278 279 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
279 280 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
280 281 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
281 282 0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
282 283 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
283 284 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
284 285 0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
285 286 0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
286 287 0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
287 288 0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
288 289 0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
289 290 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
290 291 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
291 292 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
292 293 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
293 294 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
294 295 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
295 296 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
296 297 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
297 298 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
298 299 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
299 300 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
300 301 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
301 302 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
302 303 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
303 304 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
304 305 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
305 306 };
306 307
307 308 /* Table for conversion of ASCII to upper case ASCII */
308 309
309 310 static unsigned char ltou[] =
310 311 {
311 312 0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
312 313 0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
313 314 0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
314 315 0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
315 316 0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
316 317 0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
317 318 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
318 319 0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
319 320 0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
320 321 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
321 322 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
322 323 0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
323 324 0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
324 325 0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
325 326 0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
326 327 0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
327 328 0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
328 329 0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
329 330 0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
330 331 0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
331 332 0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
332 333 0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
333 334 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
334 335 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
335 336 0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
336 337 0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
337 338 0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
338 339 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
339 340 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
340 341 0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
341 342 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
342 343 0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
343 344 };
344 345
345 346 /* BSD-compatible EBCDIC to ASCII translate table */
346 347
347 348 static unsigned char bsd_etoa[] =
348 349 {
349 350 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177,
350 351 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017,
351 352 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207,
352 353 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037,
353 354 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033,
354 355 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007,
355 356 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004,
356 357 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032,
357 358 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246,
358 359 0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041,
359 360 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
360 361 0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136,
361 362 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267,
362 363 0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077,
363 364 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301,
364 365 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042,
365 366 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
366 367 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311,
367 368 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160,
368 369 0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320,
369 370 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170,
370 371 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327,
371 372 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
372 373 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
373 374 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
374 375 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355,
375 376 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120,
376 377 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363,
377 378 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130,
378 379 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371,
379 380 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
380 381 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377,
381 382 };
382 383
383 384 /* BSD-compatible ASCII to EBCDIC translate table */
384 385
385 386 static unsigned char bsd_atoe[] =
386 387 {
387 388 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057,
388 389 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017,
389 390 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046,
390 391 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037,
391 392 0100, 0117, 0177, 0173, 0133, 0154, 0120, 0175,
392 393 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141,
393 394 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
394 395 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157,
395 396 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
396 397 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326,
397 398 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346,
398 399 0347, 0350, 0351, 0112, 0340, 0132, 0137, 0155,
399 400 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
400 401 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226,
401 402 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246,
402 403 0247, 0250, 0251, 0300, 0152, 0320, 0241, 0007,
403 404 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027,
404 405 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033,
405 406 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010,
406 407 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341,
407 408 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110,
408 409 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
409 410 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147,
410 411 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165,
411 412 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215,
412 413 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236,
413 414 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257,
414 415 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
415 416 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
416 417 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333,
417 418 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355,
418 419 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377,
419 420 };
420 421
421 422 /* BSD-compatible ASCII to IBM translate table */
422 423
423 424 static unsigned char bsd_atoibm[] =
424 425 {
425 426 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057,
426 427 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017,
427 428 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046,
428 429 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037,
429 430 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175,
430 431 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141,
431 432 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
432 433 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157,
433 434 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
434 435 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326,
435 436 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346,
436 437 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155,
437 438 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
438 439 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226,
439 440 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246,
440 441 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007,
441 442 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027,
442 443 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033,
443 444 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010,
444 445 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341,
445 446 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110,
446 447 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
447 448 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147,
448 449 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165,
449 450 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215,
450 451 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236,
451 452 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257,
452 453 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
453 454 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
454 455 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333,
455 456 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355,
456 457 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377,
457 458 };
458 459
459 460 /* set up to use SVr4 ascii-ebcdic translation by default */
460 461
461 462 static unsigned char *atoe = svr4_atoe;
462 463 static unsigned char *etoa = svr4_etoa;
463 464 static unsigned char *atoibm = svr4_atoibm;
464 465
465 466
466 467 int
467 468 main(int argc, char **argv)
468 469 {
469 470 unsigned char *ip, *op; /* input and output buffer pointers */
470 471 int c; /* character counter */
471 472 int ic; /* input character */
472 473 int conv; /* conversion option code */
473 474 int trunc; /* whether output file is truncated */
474 475 struct stat file_stat;
475 476
476 477 /* Set option defaults */
477 478
478 479 ibs = BSIZE;
479 480 obs = BSIZE;
480 481 files = 1;
481 482 conv = COPY;
482 483 trunc = 1; /* default: truncate output file */
483 484 trantype = SVR4XLATE; /* use SVR4 EBCDIC by default */
484 485
485 486 /* Parse command options */
486 487
487 488 (void) setlocale(LC_ALL, "");
488 489 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
489 490 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
490 491 #endif
491 492 (void) textdomain(TEXT_DOMAIN);
492 493
493 494 while ((c = getopt(argc, argv, "")) != EOF)
494 495 switch (c) {
495 496 case '?':
496 497 (void) fprintf(stderr, USAGE);
497 498 exit(2);
498 499 }
499 500
500 501 /* not getopt()'ed because dd has no options but only operand(s) */
501 502
502 503 for (c = optind; c < argc; c++)
503 504 {
504 505 string = argv[c];
505 506 if (match("ibs="))
506 507 {
507 508 ibs = (unsigned)number(BIG);
508 509 continue;
509 510 }
510 511 if (match("obs="))
511 512 {
512 513 obs = (unsigned)number(BIG);
513 514 continue;
514 515 }
515 516 if (match("cbs="))
516 517 {
517 518 cbs = (unsigned)number(BIG);
518 519 continue;
519 520 }
520 521 if (match("bs="))
521 522 {
522 523 bs = (unsigned)number(BIG);
523 524 continue;
524 525 }
525 526 if (match("if="))
526 527 {
527 528 ifile = string;
528 529 continue;
529 530 }
530 531 if (match("of="))
531 532 {
532 533 ofile = string;
533 534 continue;
534 535 }
535 536 if (match("skip="))
536 537 {
537 538 skip = number(BIG);
538 539 continue;
539 540 }
540 541 if (match("iseek="))
541 542 {
542 543 iseekn = number(BIG);
543 544 continue;
544 545 }
545 546 if (match("oseek="))
546 547 {
547 548 oseekn = number(BIG);
548 549 continue;
549 550 }
550 551 if (match("seek=")) /* retained for compatibility */
551 552 {
552 553 oseekn = number(BIG);
553 554 continue;
554 555 }
555 556 if (match("count="))
556 557 {
557 558 count = number(BIG);
558 559 continue;
559 560 }
560 561 if (match("files="))
561 562 {
562 563 files = (int)number(BIG);
563 564 continue;
564 565 }
565 566 if (match("conv="))
566 567 {
567 568 for (;;)
568 569 {
569 570 if (match(","))
570 571 {
571 572 continue;
572 573 }
573 574 if (*string == '\0')
574 575 {
575 576 break;
576 577 }
577 578 if (match("block"))
578 579 {
579 580 conv = BLOCK;
580 581 continue;
581 582 }
582 583 if (match("unblock"))
583 584 {
584 585 conv = UNBLOCK;
585 586 continue;
586 587 }
587 588
588 589 /* ebcdicb, ibmb, and asciib must precede */
589 590 /* ebcdic, ibm, and ascii in this test */
590 591
591 592 if (match("ebcdicb"))
592 593 {
593 594 conv = EBCDIC;
594 595 trantype = BSDXLATE;
595 596 continue;
596 597 }
597 598 if (match("ibmb"))
598 599 {
599 600 conv = IBM;
600 601 trantype = BSDXLATE;
601 602 continue;
602 603 }
603 604 if (match("asciib"))
604 605 {
605 606 conv = ASCII;
606 607 trantype = BSDXLATE;
607 608 continue;
608 609 }
609 610 if (match("ebcdic"))
610 611 {
611 612 conv = EBCDIC;
612 613 trantype = SVR4XLATE;
613 614 continue;
614 615 }
615 616 if (match("ibm"))
616 617 {
617 618 conv = IBM;
618 619 trantype = SVR4XLATE;
619 620 continue;
620 621 }
621 622 if (match("ascii"))
622 623 {
623 624 conv = ASCII;
624 625 trantype = SVR4XLATE;
625 626 continue;
626 627 }
627 628 if (match("lcase"))
628 629 {
629 630 cflag |= LCASE;
630 631 continue;
631 632 }
632 633 if (match("ucase"))
633 634 {
634 635 cflag |= UCASE;
635 636 continue;
636 637 }
637 638 if (match("swab"))
638 639 {
639 640 cflag |= SWAB;
640 641 continue;
641 642 }
642 643 if (match("noerror"))
643 644 {
644 645 cflag |= NERR;
645 646 continue;
646 647 }
647 648 if (match("notrunc"))
648 649 {
649 650 trunc = 0;
650 651 continue;
651 652 }
652 653 if (match("sync"))
653 654 {
654 655 cflag |= SYNC;
655 656 continue;
656 657 }
657 658 goto badarg;
658 659 }
659 660 continue;
660 661 }
661 662 badarg:
662 663 (void) fprintf(stderr, "dd: %s \"%s\"\n",
663 664 gettext("bad argument:"), string);
664 665 exit(2);
665 666 }
666 667
667 668 /* Perform consistency checks on options, decode strange conventions */
668 669
669 670 if (bs)
670 671 {
671 672 ibs = obs = bs;
672 673 }
673 674 if ((ibs == 0) || (obs == 0))
674 675 {
675 676 (void) fprintf(stderr, "dd: %s\n",
676 677 gettext("buffer sizes cannot be zero"));
677 678 exit(2);
678 679 }
679 680 if (conv == COPY)
680 681 {
681 682 if ((bs == 0) || (cflag&(LCASE|UCASE)))
682 683 {
683 684 conv = REBLOCK;
684 685 }
685 686 }
686 687 if (cbs == 0)
687 688 {
688 689 switch (conv)
689 690 {
690 691 case BLOCK:
691 692 case UNBLOCK:
692 693 conv = REBLOCK;
693 694 break;
694 695
695 696 case ASCII:
696 697 conv = NBASCII;
697 698 break;
698 699
699 700 case EBCDIC:
700 701 conv = NBEBCDIC;
701 702 break;
702 703
703 704 case IBM:
704 705 conv = NBIBM;
705 706 break;
706 707 }
707 708 }
708 709
709 710 /* Expand options into lower and upper case versions if necessary */
710 711
711 712 switch (conv)
712 713 {
713 714 case REBLOCK:
714 715 if (cflag&LCASE)
715 716 conv = LCREBLOCK;
716 717 else if (cflag&UCASE)
717 718 conv = UCREBLOCK;
718 719 break;
719 720
720 721 case UNBLOCK:
721 722 if (cflag&LCASE)
722 723 conv = LCUNBLOCK;
723 724 else if (cflag&UCASE)
724 725 conv = UCUNBLOCK;
725 726 break;
726 727
727 728 case BLOCK:
728 729 if (cflag&LCASE)
729 730 conv = LCBLOCK;
730 731 else if (cflag&UCASE)
731 732 conv = UCBLOCK;
732 733 break;
733 734
734 735 case ASCII:
735 736 if (cflag&LCASE)
736 737 conv = LCASCII;
737 738 else if (cflag&UCASE)
738 739 conv = UCASCII;
739 740 break;
740 741
741 742 case NBASCII:
742 743 if (cflag&LCASE)
743 744 conv = LCNBASCII;
744 745 else if (cflag&UCASE)
745 746 conv = UCNBASCII;
746 747 break;
747 748
748 749 case EBCDIC:
749 750 if (cflag&LCASE)
750 751 conv = LCEBCDIC;
751 752 else if (cflag&UCASE)
752 753 conv = UCEBCDIC;
753 754 break;
754 755
755 756 case NBEBCDIC:
756 757 if (cflag&LCASE)
757 758 conv = LCNBEBCDIC;
758 759 else if (cflag&UCASE)
759 760 conv = UCNBEBCDIC;
760 761 break;
761 762
762 763 case IBM:
763 764 if (cflag&LCASE)
764 765 conv = LCIBM;
765 766 else if (cflag&UCASE)
766 767 conv = UCIBM;
767 768 break;
768 769
769 770 case NBIBM:
770 771 if (cflag&LCASE)
771 772 conv = LCNBIBM;
772 773 else if (cflag&UCASE)
773 774 conv = UCNBIBM;
774 775 break;
775 776 }
776 777
777 778 /* If BSD-compatible translation is selected, change the tables */
778 779
779 780 if (trantype == BSDXLATE) {
780 781 atoe = bsd_atoe;
781 782 atoibm = bsd_atoibm;
782 783 etoa = bsd_etoa;
783 784 }
784 785 /* Open the input file, or duplicate standard input */
785 786
786 787 ibf = -1;
787 788 if (ifile)
788 789 {
789 790 ibf = open(ifile, 0);
790 791 }
791 792 #ifndef STANDALONE
792 793 else
793 794 {
794 795 ifile = "";
795 796 ibf = dup(0);
796 797 }
797 798 #endif
798 799 if (ibf == -1)
799 800 {
800 801 (void) fprintf(stderr, "dd: %s: ", ifile);
801 802 perror("open");
802 803 exit(2);
803 804 }
804 805
805 806 /* Open the output file, or duplicate standard output */
806 807
807 808 obf = -1;
808 809 if (ofile)
809 810 {
810 811 if (trunc == 0) /* do not truncate output file */
811 812 obf = open(ofile, (O_WRONLY|O_CREAT),
812 813 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH));
813 814 else if (oseekn && (trunc == 1))
814 815 {
815 816 obf = open(ofile, O_WRONLY|O_CREAT,
816 817 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH));
817 818 if (obf == -1)
818 819 {
819 820 (void) fprintf(stderr, "dd: %s: ", ofile);
820 821 perror("open");
821 822 exit(2);
822 823 }
823 824 (void) fstat(obf, &file_stat);
824 825 if (((file_stat.st_mode & S_IFMT) == S_IFREG) &&
825 826 (ftruncate(obf, (((off_t)oseekn) * ((off_t)obs)))
826 827 == -1))
827 828 {
828 829 perror("ftruncate");
829 830 exit(2);
830 831 }
831 832 }
832 833 else
833 834 obf = creat(ofile,
834 835 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH));
835 836 }
836 837 #ifndef STANDALONE
837 838 else
838 839 {
839 840 ofile = "";
840 841 obf = dup(1);
841 842 }
842 843 #endif
843 844 if (obf == -1)
844 845 {
845 846 (void) fprintf(stderr, "dd: %s: ", ofile);
846 847 perror("open");
847 848 exit(2);
848 849 }
849 850
850 851 /* Expand memory to get an input buffer */
851 852
852 853 ibuf = (unsigned char *)valloc(ibs + 10);
853 854
854 855 /* If no conversions, the input buffer is the output buffer */
855 856
856 857 if (conv == COPY)
857 858 {
858 859 obuf = ibuf;
859 860 }
860 861
861 862 /* Expand memory to get an output buffer. Leave enough room at the */
862 863 /* end to convert a logical record when doing block conversions. */
863 864
864 865 else
865 866 {
866 867 obuf = (unsigned char *)valloc(obs + cbs + 10);
867 868 }
868 869 if ((ibuf == (unsigned char *)NULL) || (obuf == (unsigned char *)NULL))
869 870 {
870 871 (void) fprintf(stderr,
871 872 "dd: %s\n", gettext("not enough memory"));
872 873 exit(2);
873 874 }
874 875
875 876 /* Enable a statistics message on SIGINT */
876 877
877 878 #ifndef STANDALONE
878 879 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
879 880 {
880 881 (void) signal(SIGINT, term);
881 882 }
882 883 #endif
883 884 /* Skip input blocks */
884 885
885 886 while (skip)
886 887 {
887 888 ibc = read(ibf, (char *)ibuf, ibs);
888 889 if (ibc == (unsigned)-1)
889 890 {
890 891 if (++nbad > BADLIMIT)
891 892 {
892 893 (void) fprintf(stderr, "dd: %s\n",
893 894 gettext("skip failed"));
894 895 exit(2);
895 896 }
896 897 else
897 898 {
898 899 perror("read");
899 900 }
900 901 }
901 902 else
902 903 {
903 904 if (ibc == 0)
904 905 {
905 906 (void) fprintf(stderr, "dd: %s\n",
906 907 gettext("cannot skip past end-of-file"));
907 908 exit(3);
908 909 }
909 910 else
910 911 {
911 912 nbad = 0;
912 913 }
913 914 }
914 915 skip--;
915 916 }
916 917
917 918 /* Seek past input blocks */
918 919
919 920 if (iseekn && lseek(ibf, (((off_t)iseekn) * ((off_t)ibs)), 1) == -1)
920 921 {
921 922 perror("lseek");
922 923 exit(2);
923 924 }
924 925
925 926 /* Seek past output blocks */
926 927
927 928 if (oseekn && lseek(obf, (((off_t)oseekn) * ((off_t)obs)), 1) == -1)
928 929 {
929 930 perror("lseek");
930 931 exit(2);
931 932 }
932 933
933 934 /* Initialize all buffer pointers */
934 935
935 936 skipf = 0; /* not skipping an input line */
936 937 ibc = 0; /* no input characters yet */
937 938 obc = 0; /* no output characters yet */
938 939 cbc = 0; /* the conversion buffer is empty */
939 940 op = obuf; /* point to the output buffer */
940 941
941 942 /* Read and convert input blocks until end of file(s) */
942 943
943 944 for (;;)
944 945 {
945 946 if ((count == 0) || (nifr+nipr < count))
946 947 {
947 948 /* If proceed on error is enabled, zero the input buffer */
948 949
949 950 if (cflag&NERR)
950 951 {
951 952 ip = ibuf + ibs;
952 953 c = ibs;
953 954 if (c & 1) /* if the size is odd, */
954 955 {
955 956 *--ip = 0; /* clear the odd byte */
956 957 }
957 958 if (c >>= 1) /* divide by two */
958 959 {
959 960 do { /* clear two at a time */
960 961 *--ip = 0;
961 962 *--ip = 0;
962 963 } while (--c);
963 964 }
964 965 }
965 966
966 967 /* Read the next input block */
967 968
968 969 ibc = read(ibf, (char *)ibuf, ibs);
969 970
970 971 /* Process input errors */
971 972
972 973 if (ibc == (unsigned)-1)
973 974 {
974 975 perror("read");
975 976 if (((cflag&NERR) == 0) || (++nbad > BADLIMIT))
976 977 {
977 978 while (obc)
978 979 {
979 980 (void) flsh();
980 981 }
981 982 term(2);
982 983 }
983 984 else
984 985 {
985 986 stats();
986 987 ibc = ibs; /* assume a full block */
987 988 }
988 989 }
989 990 else
990 991 {
991 992 nbad = 0;
992 993 }
993 994 }
994 995
995 996 /* Record count satisfied, simulate end of file */
996 997
997 998 else
998 999 {
999 1000 ibc = 0;
1000 1001 files = 1;
1001 1002 }
1002 1003
1003 1004 /* Process end of file */
1004 1005
1005 1006 if (ibc == 0)
1006 1007 {
1007 1008 switch (conv)
1008 1009 {
1009 1010 case UNBLOCK:
1010 1011 case LCUNBLOCK:
1011 1012 case UCUNBLOCK:
1012 1013 case ASCII:
1013 1014 case LCASCII:
1014 1015 case UCASCII:
1015 1016
1016 1017 /* Trim trailing blanks from the last line */
1017 1018
1018 1019 if ((c = cbc) != 0)
1019 1020 {
1020 1021 do {
1021 1022 if ((*--op) != ' ')
1022 1023 {
1023 1024 op++;
1024 1025 break;
1025 1026 }
1026 1027 } while (--c);
1027 1028 *op++ = '\n';
1028 1029 obc -= cbc - c - 1;
1029 1030 cbc = 0;
1030 1031
1031 1032 /* Flush the output buffer if full */
1032 1033
1033 1034 while (obc >= obs)
1034 1035 {
1035 1036 op = flsh();
1036 1037 }
1037 1038 }
1038 1039 break;
1039 1040
1040 1041 case BLOCK:
1041 1042 case LCBLOCK:
1042 1043 case UCBLOCK:
1043 1044 case EBCDIC:
1044 1045 case LCEBCDIC:
1045 1046 case UCEBCDIC:
1046 1047 case IBM:
1047 1048 case LCIBM:
1048 1049 case UCIBM:
1049 1050
1050 1051 /* Pad trailing blanks if the last line is short */
1051 1052
1052 1053 if (cbc)
1053 1054 {
1054 1055 obc += c = cbs - cbc;
1055 1056 cbc = 0;
1056 1057 if (c > 0)
1057 1058 {
1058 1059 /* Use the right kind of blank */
1059 1060
1060 1061 switch (conv)
1061 1062 {
1062 1063 case BLOCK:
1063 1064 case LCBLOCK:
1064 1065 case UCBLOCK:
1065 1066 ic = ' ';
1066 1067 break;
1067 1068
1068 1069 case EBCDIC:
1069 1070 case LCEBCDIC:
1070 1071 case UCEBCDIC:
1071 1072 ic = atoe[' '];
1072 1073 break;
1073 1074
1074 1075 case IBM:
1075 1076 case LCIBM:
1076 1077 case UCIBM:
1077 1078 ic = atoibm[' '];
1078 1079 break;
1079 1080 }
1080 1081
1081 1082 /* Pad with trailing blanks */
1082 1083
1083 1084 do {
1084 1085 *op++ = ic;
1085 1086 } while (--c);
1086 1087 }
1087 1088 }
1088 1089
1089 1090
1090 1091 /* Flush the output buffer if full */
1091 1092
1092 1093 while (obc >= obs)
1093 1094 {
1094 1095 op = flsh();
1095 1096 }
1096 1097 break;
1097 1098 }
1098 1099
1099 1100 /* If no more files to read, flush the output buffer */
1100 1101
1101 1102 if (--files <= 0)
1102 1103 {
1103 1104 (void) flsh();
1104 1105 if ((close(obf) != 0) || (fclose(stdout) != 0))
1105 1106 {
1106 1107 perror(gettext("dd: close error"));
1107 1108 exit(2);
1108 1109 }
1109 1110 term(0); /* successful exit */
1110 1111 }
1111 1112 else
1112 1113 {
1113 1114 continue; /* read the next file */
1114 1115 }
1115 1116 }
1116 1117
1117 1118 /* Normal read, check for special cases */
1118 1119
1119 1120 else if (ibc == ibs)
1120 1121 {
1121 1122 nifr++; /* count another full input record */
1122 1123 }
1123 1124 else
1124 1125 {
1125 1126 nipr++; /* count a partial input record */
1126 1127
1127 1128 /* If `sync' enabled, pad nulls */
1128 1129
1129 1130 if ((cflag&SYNC) && ((cflag&NERR) == 0))
1130 1131 {
1131 1132 c = ibs - ibc;
1132 1133 ip = ibuf + ibs;
1133 1134 do {
1134 1135 if ((conv == BLOCK) || (conv == UNBLOCK))
1135 1136 *--ip = ' ';
1136 1137 else
1137 1138 *--ip = '\0';
1138 1139 } while (--c);
1139 1140 ibc = ibs;
1140 1141 }
1141 1142 }
1142 1143
1143 1144 /* Swap the bytes in the input buffer if necessary */
1144 1145
1145 1146 if (cflag&SWAB)
1146 1147 {
1147 1148 ip = ibuf;
1148 1149 if (ibc & 1) /* if the byte count is odd, */
1149 1150 {
1150 1151 ip[ibc] = 0; /* make it even, pad with zero */
1151 1152 }
1152 1153 c = ibc >> 1; /* compute the pair count */
1153 1154 do {
1154 1155 ic = *ip++;
1155 1156 ip[-1] = *ip;
1156 1157 *ip++ = ic;
1157 1158 } while (--c); /* do two bytes at a time */
1158 1159 }
1159 1160
1160 1161 /* Select the appropriate conversion loop */
1161 1162
1162 1163 ip = ibuf;
1163 1164 switch (conv)
1164 1165 {
1165 1166
1166 1167 /* Simple copy: no conversion, preserve the input block size */
1167 1168
1168 1169 case COPY:
1169 1170 obc = ibc;
1170 1171 (void) flsh();
1171 1172 break;
1172 1173
1173 1174 /* Simple copy: pack all output into equal sized blocks */
1174 1175
1175 1176 case REBLOCK:
1176 1177 case LCREBLOCK:
1177 1178 case UCREBLOCK:
1178 1179 case NBASCII:
1179 1180 case LCNBASCII:
1180 1181 case UCNBASCII:
1181 1182 case NBEBCDIC:
1182 1183 case LCNBEBCDIC:
1183 1184 case UCNBEBCDIC:
1184 1185 case NBIBM:
1185 1186 case LCNBIBM:
1186 1187 case UCNBIBM:
1187 1188 while ((c = ibc) != 0)
1188 1189 {
1189 1190 if (c > (obs - obc))
1190 1191 {
1191 1192 c = obs - obc;
1192 1193 }
1193 1194 ibc -= c;
1194 1195 obc += c;
1195 1196 switch (conv)
1196 1197 {
1197 1198 case REBLOCK:
1198 1199 do {
1199 1200 *op++ = *ip++;
1200 1201 } while (--c);
1201 1202 break;
1202 1203
1203 1204 case LCREBLOCK:
1204 1205 do {
1205 1206 *op++ = utol[*ip++];
1206 1207 } while (--c);
1207 1208 break;
1208 1209
1209 1210 case UCREBLOCK:
1210 1211 do {
1211 1212 *op++ = ltou[*ip++];
1212 1213 } while (--c);
1213 1214 break;
1214 1215
1215 1216 case NBASCII:
1216 1217 do {
1217 1218 *op++ = etoa[*ip++];
1218 1219 } while (--c);
1219 1220 break;
1220 1221
1221 1222 case LCNBASCII:
1222 1223 do {
1223 1224 *op++ = utol[etoa[*ip++]];
1224 1225 } while (--c);
1225 1226 break;
1226 1227
1227 1228 case UCNBASCII:
1228 1229 do {
1229 1230 *op++ = ltou[etoa[*ip++]];
1230 1231 } while (--c);
1231 1232 break;
1232 1233
1233 1234 case NBEBCDIC:
1234 1235 do {
1235 1236 *op++ = atoe[*ip++];
1236 1237 } while (--c);
1237 1238 break;
1238 1239
1239 1240 case LCNBEBCDIC:
1240 1241 do {
1241 1242 *op++ = atoe[utol[*ip++]];
1242 1243 } while (--c);
1243 1244 break;
1244 1245
1245 1246 case UCNBEBCDIC:
1246 1247 do {
1247 1248 *op++ = atoe[ltou[*ip++]];
1248 1249 } while (--c);
1249 1250 break;
1250 1251
1251 1252 case NBIBM:
1252 1253 do {
1253 1254 *op++ = atoibm[*ip++];
1254 1255 } while (--c);
1255 1256 break;
1256 1257
1257 1258 case LCNBIBM:
1258 1259 do {
1259 1260 *op++ = atoibm[utol[*ip++]];
1260 1261 } while (--c);
1261 1262 break;
1262 1263
1263 1264 case UCNBIBM:
1264 1265 do {
1265 1266 *op++ = atoibm[ltou[*ip++]];
1266 1267 } while (--c);
1267 1268 break;
1268 1269 }
1269 1270 if (obc >= obs)
1270 1271 {
1271 1272 op = flsh();
1272 1273 }
1273 1274 }
1274 1275 break;
1275 1276
1276 1277 /* Convert from blocked records to lines terminated by newline */
1277 1278
1278 1279 case UNBLOCK:
1279 1280 case LCUNBLOCK:
1280 1281 case UCUNBLOCK:
1281 1282 case ASCII:
1282 1283 case LCASCII:
1283 1284 case UCASCII:
1284 1285 while ((c = ibc) != 0)
1285 1286 {
1286 1287 if (c > (cbs - cbc))
1287 1288 /* if more than one record, */
1288 1289 {
1289 1290 c = cbs - cbc;
1290 1291 /* only copy one record */
1291 1292 }
1292 1293 ibc -= c;
1293 1294 cbc += c;
1294 1295 obc += c;
1295 1296 switch (conv)
1296 1297 {
1297 1298 case UNBLOCK:
1298 1299 do {
1299 1300 *op++ = *ip++;
1300 1301 } while (--c);
1301 1302 break;
1302 1303
1303 1304 case LCUNBLOCK:
1304 1305 do {
1305 1306 *op++ = utol[*ip++];
1306 1307 } while (--c);
1307 1308 break;
1308 1309
1309 1310 case UCUNBLOCK:
1310 1311 do {
1311 1312 *op++ = ltou[*ip++];
1312 1313 } while (--c);
1313 1314 break;
1314 1315
1315 1316 case ASCII:
1316 1317 do {
1317 1318 *op++ = etoa[*ip++];
1318 1319 } while (--c);
1319 1320 break;
1320 1321
1321 1322 case LCASCII:
1322 1323 do {
1323 1324 *op++ = utol[etoa[*ip++]];
1324 1325 } while (--c);
1325 1326 break;
1326 1327
1327 1328 case UCASCII:
1328 1329 do {
1329 1330 *op++ = ltou[etoa[*ip++]];
1330 1331 } while (--c);
1331 1332 break;
1332 1333 }
1333 1334
1334 1335 /* Trim trailing blanks if the line is full */
1335 1336
1336 1337 if (cbc == cbs)
1337 1338 {
1338 1339 c = cbs; /* `do - while' is usually */
1339 1340 do { /* faster than `for' */
1340 1341 if ((*--op) != ' ')
1341 1342 {
1342 1343 op++;
1343 1344 break;
1344 1345 }
1345 1346 } while (--c);
1346 1347 *op++ = '\n';
1347 1348 obc -= cbs - c - 1;
1348 1349 cbc = 0;
1349 1350
1350 1351 /* Flush the output buffer if full */
1351 1352
1352 1353 while (obc >= obs)
1353 1354 {
1354 1355 op = flsh();
1355 1356 }
1356 1357 }
1357 1358 }
1358 1359 break;
1359 1360
1360 1361 /* Convert to blocked records */
1361 1362
1362 1363 case BLOCK:
1363 1364 case LCBLOCK:
1364 1365 case UCBLOCK:
1365 1366 case EBCDIC:
1366 1367 case LCEBCDIC:
1367 1368 case UCEBCDIC:
1368 1369 case IBM:
1369 1370 case LCIBM:
1370 1371 case UCIBM:
1371 1372 while ((c = ibc) != 0)
1372 1373 {
1373 1374 int nlflag = 0;
1374 1375
1375 1376 /* We may have to skip to the end of a long line */
1376 1377
1377 1378 if (skipf)
1378 1379 {
1379 1380 do {
1380 1381 if ((ic = *ip++) == '\n')
1381 1382 {
1382 1383 skipf = 0;
1383 1384 c--;
1384 1385 break;
1385 1386 }
1386 1387 } while (--c);
1387 1388 if ((ibc = c) == 0)
1388 1389 {
1389 1390 continue;
1390 1391 /* read another block */
1391 1392 }
1392 1393 }
1393 1394
1394 1395 /* If anything left, copy until newline */
1395 1396
1396 1397 if (c > (cbs - cbc + 1))
1397 1398 {
1398 1399 c = cbs - cbc + 1;
1399 1400 }
1400 1401 ibc -= c;
1401 1402 cbc += c;
1402 1403 obc += c;
1403 1404
1404 1405 switch (conv)
1405 1406 {
1406 1407 case BLOCK:
1407 1408 do {
1408 1409 if ((ic = *ip++) != '\n')
1409 1410 {
1410 1411 *op++ = ic;
1411 1412 }
1412 1413 else
1413 1414 {
1414 1415 nlflag = 1;
1415 1416 break;
1416 1417 }
1417 1418 } while (--c);
1418 1419 break;
1419 1420
1420 1421 case LCBLOCK:
1421 1422 do {
1422 1423 if ((ic = *ip++) != '\n')
1423 1424 {
1424 1425 *op++ = utol[ic];
1425 1426 }
1426 1427 else
1427 1428 {
1428 1429 nlflag = 1;
1429 1430 break;
1430 1431 }
1431 1432 } while (--c);
1432 1433 break;
1433 1434
1434 1435 case UCBLOCK:
1435 1436 do {
1436 1437 if ((ic = *ip++) != '\n')
1437 1438 {
1438 1439 *op++ = ltou[ic];
1439 1440 }
1440 1441 else
1441 1442 {
1442 1443 nlflag = 1;
1443 1444 break;
1444 1445 }
1445 1446 } while (--c);
1446 1447 break;
1447 1448
1448 1449 case EBCDIC:
1449 1450 do {
1450 1451 if ((ic = *ip++) != '\n')
1451 1452 {
1452 1453 *op++ = atoe[ic];
1453 1454 }
1454 1455 else
1455 1456 {
1456 1457 nlflag = 1;
1457 1458 break;
1458 1459 }
1459 1460 } while (--c);
1460 1461 break;
1461 1462
1462 1463 case LCEBCDIC:
1463 1464 do {
1464 1465 if ((ic = *ip++) != '\n')
1465 1466 {
1466 1467 *op++ = atoe[utol[ic]];
1467 1468 }
1468 1469 else
1469 1470 {
1470 1471 nlflag = 1;
1471 1472 break;
1472 1473 }
1473 1474 } while (--c);
1474 1475 break;
1475 1476
1476 1477 case UCEBCDIC:
1477 1478 do {
1478 1479 if ((ic = *ip++) != '\n')
1479 1480 {
1480 1481 *op++ = atoe[ltou[ic]];
1481 1482 }
1482 1483 else
1483 1484 {
1484 1485 nlflag = 1;
1485 1486 break;
1486 1487 }
1487 1488 } while (--c);
1488 1489 break;
1489 1490
1490 1491 case IBM:
1491 1492 do {
1492 1493 if ((ic = *ip++) != '\n')
1493 1494 {
1494 1495 *op++ = atoibm[ic];
1495 1496 }
1496 1497 else
1497 1498 {
1498 1499 nlflag = 1;
1499 1500 break;
1500 1501 }
1501 1502 } while (--c);
1502 1503 break;
1503 1504
1504 1505 case LCIBM:
1505 1506 do {
1506 1507 if ((ic = *ip++) != '\n')
1507 1508 {
1508 1509 *op++ = atoibm[utol[ic]];
1509 1510 }
1510 1511 else
1511 1512 {
1512 1513 nlflag = 1;
1513 1514 break;
1514 1515 }
1515 1516 } while (--c);
1516 1517 break;
1517 1518
1518 1519 case UCIBM:
1519 1520 do {
1520 1521 if ((ic = *ip++) != '\n')
1521 1522 {
1522 1523 *op++ = atoibm[ltou[ic]];
1523 1524 }
1524 1525 else
1525 1526 {
1526 1527 nlflag = 1;
1527 1528 break;
1528 1529 }
1529 1530 } while (--c);
1530 1531 break;
1531 1532 }
1532 1533
1533 1534 /* If newline found, update all the counters and */
1534 1535 /* pointers, pad with trailing blanks if necessary */
1535 1536
1536 1537 if (nlflag)
1537 1538 {
1538 1539 ibc += c - 1;
1539 1540 obc += cbs - cbc;
1540 1541 c += cbs - cbc;
1541 1542 cbc = 0;
1542 1543 if (c > 0)
1543 1544 {
1544 1545 /* Use the right kind of blank */
1545 1546
1546 1547 switch (conv)
1547 1548 {
1548 1549 case BLOCK:
1549 1550 case LCBLOCK:
1550 1551 case UCBLOCK:
1551 1552 ic = ' ';
1552 1553 break;
1553 1554
1554 1555 case EBCDIC:
1555 1556 case LCEBCDIC:
1556 1557 case UCEBCDIC:
1557 1558 ic = atoe[' '];
1558 1559 break;
1559 1560
1560 1561 case IBM:
1561 1562 case LCIBM:
1562 1563 case UCIBM:
1563 1564 ic = atoibm[' '];
1564 1565 break;
1565 1566 }
1566 1567
1567 1568 /* Pad with trailing blanks */
1568 1569
1569 1570 do {
1570 1571 *op++ = ic;
1571 1572 } while (--c);
1572 1573 }
1573 1574 }
1574 1575
1575 1576 /* If not end of line, this line may be too long */
1576 1577
1577 1578 else if (cbc > cbs)
1578 1579 {
1579 1580 skipf = 1; /* note skip in progress */
1580 1581 obc--;
1581 1582 op--;
1582 1583 cbc = 0;
1583 1584 ntrunc++; /* count another long line */
1584 1585 }
1585 1586
1586 1587 /* Flush the output buffer if full */
1587 1588
1588 1589 while (obc >= obs)
1589 1590 {
1590 1591 op = flsh();
1591 1592 }
1592 1593 }
1593 1594 break;
1594 1595 }
1595 1596 }
1596 1597 /* NOTREACHED */
1597 1598 return (0);
1598 1599 }
1599 1600
1600 1601 /* match ************************************************************** */
1601 1602 /* */
1602 1603 /* Compare two text strings for equality */
1603 1604 /* */
1604 1605 /* Arg: s - pointer to string to match with a command arg */
1605 1606 /* Global arg: string - pointer to command arg */
1606 1607 /* */
1607 1608 /* Return: 1 if match, 0 if no match */
1608 1609 /* If match, also reset `string' to point to the text */
1609 1610 /* that follows the matching text. */
1610 1611 /* */
1611 1612 /* ******************************************************************** */
1612 1613
1613 1614 static int
1614 1615 match(s)
1615 1616 char *s;
1616 1617 {
1617 1618 char *cs;
1618 1619
1619 1620 cs = string;
1620 1621 while (*cs++ == *s)
1621 1622 {
1622 1623 if (*s++ == '\0')
1623 1624 {
1624 1625 goto true;
1625 1626 }
1626 1627 }
1627 1628 if (*s != '\0')
1628 1629 {
1629 1630 return (0);
1630 1631 }
1631 1632
1632 1633 true:
1633 1634 cs--;
1634 1635 string = cs;
↓ open down ↓ |
1599 lines elided |
↑ open up ↑ |
1635 1636 return (1);
1636 1637 }
1637 1638
1638 1639 /* number ************************************************************* */
1639 1640 /* */
1640 1641 /* Convert a numeric arg to binary */
1641 1642 /* */
1642 1643 /* Arg: big - maximum valid input number */
1643 1644 /* Global arg: string - pointer to command arg */
1644 1645 /* */
1645 -/* Valid forms: 123 | 123k | 123w | 123b | 123*123 | 123x123 */
1646 +/* Valid forms: 123 | 123k | 123M | 123G | 123T | 123P | 123E | 123Z | */
1647 +/* 123w | 123b | 123*123 | 123x123 */
1646 1648 /* plus combinations such as 2b*3kw*4w */
1647 1649 /* */
1648 1650 /* Return: converted number */
1649 1651 /* */
1650 1652 /* ******************************************************************** */
1651 1653
1652 1654 static unsigned long long
1653 1655 number(big)
1654 1656 long long big;
1655 1657 {
1656 1658 char *cs;
1657 1659 long long n;
1658 1660 long long cut = BIG / 10; /* limit to avoid overflow */
1659 1661
1660 1662 cs = string;
↓ open down ↓ |
5 lines elided |
↑ open up ↑ |
1661 1663 n = 0;
1662 1664 while ((*cs >= '0') && (*cs <= '9') && (n <= cut))
1663 1665 {
1664 1666 n = n*10 + *cs++ - '0';
1665 1667 }
1666 1668 for (;;)
1667 1669 {
1668 1670 switch (*cs++)
1669 1671 {
1670 1672
1673 + case 'Z':
1674 + n *= 1024;
1675 + /* FALLTHROUGH */
1676 +
1677 + case 'E':
1678 + n *= 1024;
1679 + /* FALLTHROUGH */
1680 +
1681 + case 'P':
1682 + n *= 1024;
1683 + /* FALLTHROUGH */
1684 +
1685 + case 'T':
1686 + n *= 1024;
1687 + /* FALLTHROUGH */
1688 +
1689 + case 'G':
1690 + n *= 1024;
1691 + /* FALLTHROUGH */
1692 +
1693 + case 'M':
1694 + n *= 1024;
1695 + /* FALLTHROUGH */
1696 +
1671 1697 case 'k':
1672 1698 n *= 1024;
1673 1699 continue;
1674 1700
1675 1701 case 'w':
1676 1702 n *= 2;
1677 1703 continue;
1678 1704
1679 1705 case 'b':
1680 1706 n *= BSIZE;
1681 1707 continue;
1682 1708
1683 1709 case '*':
1684 1710 case 'x':
1685 1711 string = cs;
1686 1712 n *= number(BIG);
1687 1713
1688 1714 /* FALLTHROUGH */
1689 1715 /* Fall into exit test, recursion has read rest of string */
1690 1716 /* End of string, check for a valid number */
1691 1717
1692 1718 case '\0':
1693 1719 if ((n > big) || (n < 0))
1694 1720 {
1695 1721 (void) fprintf(stderr, "dd: %s \"%llu\"\n",
1696 1722 gettext("argument out of range:"), n);
1697 1723 exit(2);
1698 1724 }
1699 1725 return (n);
1700 1726
1701 1727 default:
1702 1728 (void) fprintf(stderr, "dd: %s \"%s\"\n",
1703 1729 gettext("bad numeric argument:"), string);
1704 1730 exit(2);
1705 1731 }
1706 1732 } /* never gets here */
1707 1733 }
1708 1734
1709 1735 /* flsh *************************************************************** */
1710 1736 /* */
1711 1737 /* Flush the output buffer, move any excess bytes down to the beginning */
1712 1738 /* */
1713 1739 /* Arg: none */
1714 1740 /* Global args: obuf, obc, obs, nofr, nopr */
1715 1741 /* */
1716 1742 /* Return: Pointer to the first free byte in the output buffer. */
1717 1743 /* Also reset `obc' to account for moved bytes. */
1718 1744 /* */
1719 1745 /* ******************************************************************** */
1720 1746
1721 1747 static unsigned char
1722 1748 *flsh()
1723 1749 {
1724 1750 unsigned char *op, *cp;
1725 1751 int bc;
1726 1752 unsigned int oc;
1727 1753
1728 1754 if (obc) /* don't flush if the buffer is empty */
1729 1755 {
1730 1756 if (obc >= obs) {
1731 1757 oc = obs;
1732 1758 nofr++; /* count a full output buffer */
1733 1759 }
1734 1760 else
1735 1761 {
1736 1762 oc = obc;
1737 1763 nopr++; /* count a partial output buffer */
1738 1764 }
1739 1765 bc = write(obf, (char *)obuf, oc);
1740 1766 if (bc != oc) {
1741 1767 if (bc < 0)
1742 1768 perror("write");
1743 1769 else
1744 1770 (void) fprintf(stderr,
1745 1771 gettext("dd: unexpected short write, "
1746 1772 "wrote %d bytes, expected %d\n"), bc, oc);
1747 1773 term(2);
1748 1774 }
1749 1775 obc -= oc;
1750 1776 op = obuf;
1751 1777
1752 1778 /* If any data in the conversion buffer, move it into */
1753 1779 /* the output buffer */
1754 1780
1755 1781 if (obc) {
1756 1782 cp = obuf + obs;
1757 1783 bc = obc;
1758 1784 do {
1759 1785 *op++ = *cp++;
1760 1786 } while (--bc);
1761 1787 }
1762 1788 return (op);
1763 1789 }
1764 1790 return (obuf);
1765 1791 }
1766 1792
1767 1793 /* term *************************************************************** */
1768 1794 /* */
1769 1795 /* Write record statistics, then exit */
1770 1796 /* */
1771 1797 /* Arg: c - exit status code */
1772 1798 /* */
1773 1799 /* Return: no return, calls exit */
1774 1800 /* */
1775 1801 /* ******************************************************************** */
1776 1802
1777 1803 static void
1778 1804 term(c)
1779 1805 int c;
1780 1806 {
1781 1807 stats();
1782 1808 exit(c);
1783 1809 }
1784 1810
1785 1811 /* stats ************************************************************** */
1786 1812 /* */
1787 1813 /* Write record statistics onto standard error */
1788 1814 /* */
1789 1815 /* Args: none */
1790 1816 /* Global args: nifr, nipr, nofr, nopr, ntrunc */
1791 1817 /* */
1792 1818 /* Return: void */
1793 1819 /* */
1794 1820 /* ******************************************************************** */
1795 1821
1796 1822 static void
1797 1823 stats()
1798 1824 {
1799 1825 (void) fprintf(stderr, gettext("%llu+%llu records in\n"), nifr, nipr);
1800 1826 (void) fprintf(stderr, gettext("%llu+%llu records out\n"), nofr, nopr);
1801 1827 if (ntrunc) {
1802 1828 (void) fprintf(stderr,
1803 1829 gettext("%llu truncated record(s)\n"), ntrunc);
1804 1830 }
1805 1831 }
↓ open down ↓ |
125 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX