xref: /openbsd-src/bin/dd/conv.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: conv.c,v 1.8 2001/08/07 14:39:27 hugh Exp $	*/
2 /*	$NetBSD: conv.c,v 1.6 1996/02/20 19:29:02 jtc Exp $	*/
3 
4 /*-
5  * Copyright (c) 1991, 1993, 1994
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Keith Muller of the University of California, San Diego and Lance
10  * Visser of Convex Computer Corporation.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)conv.c	8.3 (Berkeley) 4/2/94";
44 #else
45 static char rcsid[] = "$OpenBSD: conv.c,v 1.8 2001/08/07 14:39:27 hugh Exp $";
46 #endif
47 #endif /* not lint */
48 
49 #include <sys/param.h>
50 #include <sys/time.h>
51 
52 #include <err.h>
53 #include <string.h>
54 
55 #include "dd.h"
56 #include "extern.h"
57 
58 /*
59  * def --
60  * Copy input to output.  Input is buffered until reaches obs, and then
61  * output until less than obs remains.  Only a single buffer is used.
62  * Worst case buffer calculation is (ibs + obs - 1).
63  */
64 void
65 def()
66 {
67 	size_t cnt;
68 	u_char *inp;
69 	const u_char *t;
70 
71 	if ((t = ctab) != NULL)
72 		for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
73 			*inp = t[*inp];
74 
75 	/* Make the output buffer look right. */
76 	out.dbp = in.dbp;
77 	out.dbcnt = in.dbcnt;
78 
79 	if (in.dbcnt >= out.dbsz) {
80 		/* If the output buffer is full, write it. */
81 		dd_out(0);
82 
83 		/*
84 		 * Ddout copies the leftover output to the beginning of
85 		 * the buffer and resets the output buffer.  Reset the
86 		 * input buffer to match it.
87 	 	 */
88 		in.dbp = out.dbp;
89 		in.dbcnt = out.dbcnt;
90 	}
91 }
92 
93 void
94 def_close()
95 {
96 	/* Just update the count, everything is already in the buffer. */
97 	if (in.dbcnt)
98 		out.dbcnt = in.dbcnt;
99 }
100 
101 #ifdef	NO_CONV
102 /* Build a smaller version (i.e. for a miniroot) */
103 /* These can not be called, but just in case...  */
104 static char no_block[] = "unblock and -DNO_CONV?";
105 void block()       { errx(1, "%s", no_block + 2); }
106 void block_close() { errx(1, "%s", no_block + 2); }
107 void unblock()       { errx(1, "%s", no_block); }
108 void unblock_close() { errx(1, "%s", no_block); }
109 #else	/* NO_CONV */
110 
111 /*
112  * Copy variable length newline terminated records with a max size cbsz
113  * bytes to output.  Records less than cbs are padded with spaces.
114  *
115  * max in buffer:  MAX(ibs, cbsz)
116  * max out buffer: obs + cbsz
117  */
118 void
119 block()
120 {
121 	static int intrunc;
122 	int ch = -1;
123 	size_t cnt, maxlen;
124 	u_char *inp, *outp;
125 	const u_char *t;
126 
127 	/*
128 	 * Record truncation can cross block boundaries.  If currently in a
129 	 * truncation state, keep tossing characters until reach a newline.
130 	 * Start at the beginning of the buffer, as the input buffer is always
131 	 * left empty.
132 	 */
133 	if (intrunc) {
134 		for (inp = in.db, cnt = in.dbrcnt;
135 		    cnt && *inp++ != '\n'; --cnt);
136 		if (!cnt) {
137 			in.dbcnt = 0;
138 			in.dbp = in.db;
139 			return;
140 		}
141 		intrunc = 0;
142 		/* Adjust the input buffer numbers. */
143 		in.dbcnt = cnt - 1;
144 		in.dbp = inp + cnt - 1;
145 	}
146 
147 	/*
148 	 * Copy records (max cbsz size chunks) into the output buffer.  The
149 	 * translation is done as we copy into the output buffer.
150 	 */
151 	for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) {
152 		maxlen = MIN(cbsz, in.dbcnt);
153 		if ((t = ctab) != NULL)
154 			for (cnt = 0;
155 			    cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
156 				*outp++ = t[ch];
157 		else
158 			for (cnt = 0;
159 			    cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
160 				*outp++ = ch;
161 		/*
162 		 * Check for short record without a newline.  Reassemble the
163 		 * input block.
164 		 */
165 		if (ch != '\n' && in.dbcnt < cbsz) {
166 			(void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
167 			break;
168 		}
169 
170 		/* Adjust the input buffer numbers. */
171 		in.dbcnt -= cnt;
172 		if (ch == '\n')
173 			--in.dbcnt;
174 
175 		/* Pad short records with spaces. */
176 		if (cnt < cbsz)
177 			(void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt);
178 		else {
179 			/*
180 			 * If the next character wouldn't have ended the
181 			 * block, it's a truncation.
182 			 */
183 			if (!in.dbcnt || *inp != '\n')
184 				++st.trunc;
185 
186 			/* Toss characters to a newline. */
187 			for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt);
188 			if (!in.dbcnt)
189 				intrunc = 1;
190 			else
191 				--in.dbcnt;
192 		}
193 
194 		/* Adjust output buffer numbers. */
195 		out.dbp += cbsz;
196 		if ((out.dbcnt += cbsz) >= out.dbsz)
197 			dd_out(0);
198 		outp = out.dbp;
199 	}
200 	in.dbp = in.db + in.dbcnt;
201 }
202 
203 void
204 block_close()
205 {
206 	/*
207 	 * Copy any remaining data into the output buffer and pad to a record.
208 	 * Don't worry about truncation or translation, the input buffer is
209 	 * always empty when truncating, and no characters have been added for
210 	 * translation.  The bottom line is that anything left in the input
211 	 * buffer is a truncated record.  Anything left in the output buffer
212 	 * just wasn't big enough.
213 	 */
214 	if (in.dbcnt) {
215 		++st.trunc;
216 		(void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt);
217 		(void)memset(out.dbp + in.dbcnt,
218 		    ctab ? ctab[' '] : ' ', cbsz - in.dbcnt);
219 		out.dbcnt += cbsz;
220 	}
221 }
222 
223 /*
224  * Convert fixed length (cbsz) records to variable length.  Deletes any
225  * trailing blanks and appends a newline.
226  *
227  * max in buffer:  MAX(ibs, cbsz) + cbsz
228  * max out buffer: obs + cbsz
229  */
230 void
231 unblock()
232 {
233 	size_t cnt;
234 	u_char *inp;
235 	const u_char *t;
236 
237 	/* Translation and case conversion. */
238 	if ((t = ctab) != NULL)
239 		for (cnt = in.dbrcnt, inp = in.dbp - 1; cnt--; inp--)
240 			*inp = t[*inp];
241 	/*
242 	 * Copy records (max cbsz size chunks) into the output buffer.  The
243 	 * translation has to already be done or we might not recognize the
244 	 * spaces.
245 	 */
246 	for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) {
247 		for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t);
248 		if (t >= inp) {
249 			cnt = t - inp + 1;
250 			(void)memmove(out.dbp, inp, cnt);
251 			out.dbp += cnt;
252 			out.dbcnt += cnt;
253 		}
254 		++out.dbcnt;
255 		*out.dbp++ = '\n';
256 		if (out.dbcnt >= out.dbsz)
257 			dd_out(0);
258 	}
259 	if (in.dbcnt)
260 		(void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
261 	in.dbp = in.db + in.dbcnt;
262 }
263 
264 void
265 unblock_close()
266 {
267 	size_t cnt;
268 	u_char *t;
269 
270 	if (in.dbcnt) {
271 		warnx("%s: short input record", in.name);
272 		for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t);
273 		if (t >= in.db) {
274 			cnt = t - in.db + 1;
275 			(void)memmove(out.dbp, in.db, cnt);
276 			out.dbp += cnt;
277 			out.dbcnt += cnt;
278 		}
279 		++out.dbcnt;
280 		*out.dbp++ = '\n';
281 	}
282 }
283 
284 #endif	/* NO_CONV */
285