1 /*-
2 * Copyright (c) 1991, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Keith Muller of the University of California, San Diego and Lance
7 * Visser of Convex Computer Corporation.
8 *
9 * %sccs.include.redist.c%
10 */
11
12 #ifndef lint
13 static char sccsid[] = "@(#)conv.c 8.3 (Berkeley) 04/02/94";
14 #endif /* not lint */
15
16 #include <sys/param.h>
17
18 #include <err.h>
19 #include <string.h>
20
21 #include "dd.h"
22 #include "extern.h"
23
24 /*
25 * def --
26 * Copy input to output. Input is buffered until reaches obs, and then
27 * output until less than obs remains. Only a single buffer is used.
28 * Worst case buffer calculation is (ibs + obs - 1).
29 */
30 void
def()31 def()
32 {
33 int cnt;
34 u_char *inp, *t;
35
36 if (t = ctab)
37 for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp)
38 *inp = t[*inp];
39
40 /* Make the output buffer look right. */
41 out.dbp = in.dbp;
42 out.dbcnt = in.dbcnt;
43
44 if (in.dbcnt >= out.dbsz) {
45 /* If the output buffer is full, write it. */
46 dd_out(0);
47
48 /*
49 * Ddout copies the leftover output to the beginning of
50 * the buffer and resets the output buffer. Reset the
51 * input buffer to match it.
52 */
53 in.dbp = out.dbp;
54 in.dbcnt = out.dbcnt;
55 }
56 }
57
58 void
def_close()59 def_close()
60 {
61 /* Just update the count, everything is already in the buffer. */
62 if (in.dbcnt)
63 out.dbcnt = in.dbcnt;
64 }
65
66 /*
67 * Copy variable length newline terminated records with a max size cbsz
68 * bytes to output. Records less than cbs are padded with spaces.
69 *
70 * max in buffer: MAX(ibs, cbsz)
71 * max out buffer: obs + cbsz
72 */
73 void
block()74 block()
75 {
76 static int intrunc;
77 int ch, cnt, maxlen;
78 u_char *inp, *outp, *t;
79
80 /*
81 * Record truncation can cross block boundaries. If currently in a
82 * truncation state, keep tossing characters until reach a newline.
83 * Start at the beginning of the buffer, as the input buffer is always
84 * left empty.
85 */
86 if (intrunc) {
87 for (inp = in.db, cnt = in.dbrcnt;
88 cnt && *inp++ != '\n'; --cnt);
89 if (!cnt) {
90 in.dbcnt = 0;
91 in.dbp = in.db;
92 return;
93 }
94 intrunc = 0;
95 /* Adjust the input buffer numbers. */
96 in.dbcnt = cnt - 1;
97 in.dbp = inp + cnt - 1;
98 }
99
100 /*
101 * Copy records (max cbsz size chunks) into the output buffer. The
102 * translation is done as we copy into the output buffer.
103 */
104 for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) {
105 maxlen = MIN(cbsz, in.dbcnt);
106 if (t = ctab)
107 for (cnt = 0;
108 cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
109 *outp++ = t[ch];
110 else
111 for (cnt = 0;
112 cnt < maxlen && (ch = *inp++) != '\n'; ++cnt)
113 *outp++ = ch;
114 /*
115 * Check for short record without a newline. Reassemble the
116 * input block.
117 */
118 if (ch != '\n' && in.dbcnt < cbsz) {
119 memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
120 break;
121 }
122
123 /* Adjust the input buffer numbers. */
124 in.dbcnt -= cnt;
125 if (ch == '\n')
126 --in.dbcnt;
127
128 /* Pad short records with spaces. */
129 if (cnt < cbsz)
130 (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt);
131 else {
132 /*
133 * If the next character wouldn't have ended the
134 * block, it's a truncation.
135 */
136 if (!in.dbcnt || *inp != '\n')
137 ++st.trunc;
138
139 /* Toss characters to a newline. */
140 for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt);
141 if (!in.dbcnt)
142 intrunc = 1;
143 else
144 --in.dbcnt;
145 }
146
147 /* Adjust output buffer numbers. */
148 out.dbp += cbsz;
149 if ((out.dbcnt += cbsz) >= out.dbsz)
150 dd_out(0);
151 outp = out.dbp;
152 }
153 in.dbp = in.db + in.dbcnt;
154 }
155
156 void
block_close()157 block_close()
158 {
159 /*
160 * Copy any remaining data into the output buffer and pad to a record.
161 * Don't worry about truncation or translation, the input buffer is
162 * always empty when truncating, and no characters have been added for
163 * translation. The bottom line is that anything left in the input
164 * buffer is a truncated record. Anything left in the output buffer
165 * just wasn't big enough.
166 */
167 if (in.dbcnt) {
168 ++st.trunc;
169 memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt);
170 (void)memset(out.dbp + in.dbcnt,
171 ctab ? ctab[' '] : ' ', cbsz - in.dbcnt);
172 out.dbcnt += cbsz;
173 }
174 }
175
176 /*
177 * Convert fixed length (cbsz) records to variable length. Deletes any
178 * trailing blanks and appends a newline.
179 *
180 * max in buffer: MAX(ibs, cbsz) + cbsz
181 * max out buffer: obs + cbsz
182 */
183 void
unblock()184 unblock()
185 {
186 int cnt;
187 u_char *inp, *t;
188
189 /* Translation and case conversion. */
190 if (t = ctab)
191 for (cnt = in.dbrcnt, inp = in.dbp; cnt--;)
192 *--inp = t[*inp];
193 /*
194 * Copy records (max cbsz size chunks) into the output buffer. The
195 * translation has to already be done or we might not recognize the
196 * spaces.
197 */
198 for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) {
199 for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t);
200 if (t >= inp) {
201 cnt = t - inp + 1;
202 memmove(out.dbp, inp, cnt);
203 out.dbp += cnt;
204 out.dbcnt += cnt;
205 }
206 ++out.dbcnt;
207 *out.dbp++ = '\n';
208 if (out.dbcnt >= out.dbsz)
209 dd_out(0);
210 }
211 if (in.dbcnt)
212 memmove(in.db, in.dbp - in.dbcnt, in.dbcnt);
213 in.dbp = in.db + in.dbcnt;
214 }
215
216 void
unblock_close()217 unblock_close()
218 {
219 int cnt;
220 u_char *t;
221
222 if (in.dbcnt) {
223 warnx("%s: short input record", in.name);
224 for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t);
225 if (t >= in.db) {
226 cnt = t - in.db + 1;
227 memmove(out.dbp, in.db, cnt);
228 out.dbp += cnt;
229 out.dbcnt += cnt;
230 }
231 ++out.dbcnt;
232 *out.dbp++ = '\n';
233 }
234 }
235