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