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