1 |
/* io.c: This file contains the i/o routines for the ed line editor */ |
2 |
/*- |
3 |
* Copyright (c) 1993 Andrew Moore, Talke Studio. |
4 |
* All rights reserved. |
5 |
* |
6 |
* Redistribution and use in source and binary forms, with or without |
7 |
* modification, are permitted provided that the following conditions |
8 |
* are met: |
9 |
* 1. Redistributions of source code must retain the above copyright |
10 |
* notice, this list of conditions and the following disclaimer. |
11 |
* 2. Redistributions in binary form must reproduce the above copyright |
12 |
* notice, this list of conditions and the following disclaimer in the |
13 |
* documentation and/or other materials provided with the distribution. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
*/ |
27 |
|
28 |
#include <sys/cdefs.h> |
29 |
__FBSDID("$FreeBSD: release/10.0.0/bin/ed/io.c 241737 2012-10-19 14:49:42Z ed $"); |
30 |
|
31 |
#include "ed.h" |
32 |
|
33 |
/* read_file: read a named file/pipe into the buffer; return line count */ |
34 |
long |
35 |
read_file(char *fn, long n) |
36 |
{ |
37 |
FILE *fp; |
38 |
long size; |
39 |
|
40 |
|
41 |
fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r"); |
42 |
if (fp == NULL) { |
43 |
fprintf(stderr, "%s: %s\n", fn, strerror(errno)); |
44 |
errmsg = "cannot open input file"; |
45 |
return ERR; |
46 |
} else if ((size = read_stream(fp, n)) < 0) |
47 |
return ERR; |
48 |
else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { |
49 |
fprintf(stderr, "%s: %s\n", fn, strerror(errno)); |
50 |
errmsg = "cannot close input file"; |
51 |
return ERR; |
52 |
} |
53 |
if (!scripted) |
54 |
fprintf(stdout, "%lu\n", size); |
55 |
return current_addr - n; |
56 |
} |
57 |
|
58 |
static char *sbuf; /* file i/o buffer */ |
59 |
static int sbufsz; /* file i/o buffer size */ |
60 |
int newline_added; /* if set, newline appended to input file */ |
61 |
|
62 |
/* read_stream: read a stream into the editor buffer; return status */ |
63 |
long |
64 |
read_stream(FILE *fp, long n) |
65 |
{ |
66 |
line_t *lp = get_addressed_line_node(n); |
67 |
undo_t *up = NULL; |
68 |
unsigned long size = 0; |
69 |
int o_newline_added = newline_added; |
70 |
int o_isbinary = isbinary; |
71 |
int appended = (n == addr_last); |
72 |
int len; |
73 |
|
74 |
isbinary = newline_added = 0; |
75 |
if (des) |
76 |
init_des_cipher(); |
77 |
for (current_addr = n; (len = get_stream_line(fp)) > 0; size += len) { |
78 |
SPL1(); |
79 |
if (put_sbuf_line(sbuf) == NULL) { |
80 |
SPL0(); |
81 |
return ERR; |
82 |
} |
83 |
lp = lp->q_forw; |
84 |
if (up) |
85 |
up->t = lp; |
86 |
else if ((up = push_undo_stack(UADD, current_addr, |
87 |
current_addr)) == NULL) { |
88 |
SPL0(); |
89 |
return ERR; |
90 |
} |
91 |
SPL0(); |
92 |
} |
93 |
if (len < 0) |
94 |
return ERR; |
95 |
if (appended && size && o_isbinary && o_newline_added) |
96 |
fputs("newline inserted\n", stderr); |
97 |
else if (newline_added && (!appended || (!isbinary && !o_isbinary))) |
98 |
fputs("newline appended\n", stderr); |
99 |
if (isbinary && newline_added && !appended) |
100 |
size += 1; |
101 |
if (!size) |
102 |
newline_added = 1; |
103 |
newline_added = appended ? newline_added : o_newline_added; |
104 |
isbinary = isbinary | o_isbinary; |
105 |
if (des) |
106 |
size += 8 - size % 8; /* adjust DES size */ |
107 |
return size; |
108 |
} |
109 |
|
110 |
|
111 |
/* get_stream_line: read a line of text from a stream; return line length */ |
112 |
int |
113 |
get_stream_line(FILE *fp) |
114 |
{ |
115 |
int c; |
116 |
int i = 0; |
117 |
|
118 |
while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || (!feof(fp) && |
119 |
!ferror(fp))) && c != '\n') { |
120 |
REALLOC(sbuf, sbufsz, i + 1, ERR); |
121 |
if (!(sbuf[i++] = c)) |
122 |
isbinary = 1; |
123 |
} |
124 |
REALLOC(sbuf, sbufsz, i + 2, ERR); |
125 |
if (c == '\n') |
126 |
sbuf[i++] = c; |
127 |
else if (ferror(fp)) { |
128 |
fprintf(stderr, "%s\n", strerror(errno)); |
129 |
errmsg = "cannot read input file"; |
130 |
return ERR; |
131 |
} else if (i) { |
132 |
sbuf[i++] = '\n'; |
133 |
newline_added = 1; |
134 |
} |
135 |
sbuf[i] = '\0'; |
136 |
return (isbinary && newline_added && i) ? --i : i; |
137 |
} |
138 |
|
139 |
|
140 |
/* write_file: write a range of lines to a named file/pipe; return line count */ |
141 |
long |
142 |
write_file(char *fn, const char *mode, long n, long m) |
143 |
{ |
144 |
FILE *fp; |
145 |
long size; |
146 |
|
147 |
fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode); |
148 |
if (fp == NULL) { |
149 |
fprintf(stderr, "%s: %s\n", fn, strerror(errno)); |
150 |
errmsg = "cannot open output file"; |
151 |
return ERR; |
152 |
} else if ((size = write_stream(fp, n, m)) < 0) |
153 |
return ERR; |
154 |
else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { |
155 |
fprintf(stderr, "%s: %s\n", fn, strerror(errno)); |
156 |
errmsg = "cannot close output file"; |
157 |
return ERR; |
158 |
} |
159 |
if (!scripted) |
160 |
fprintf(stdout, "%lu\n", size); |
161 |
return n ? m - n + 1 : 0; |
162 |
} |
163 |
|
164 |
|
165 |
/* write_stream: write a range of lines to a stream; return status */ |
166 |
long |
167 |
write_stream(FILE *fp, long n, long m) |
168 |
{ |
169 |
line_t *lp = get_addressed_line_node(n); |
170 |
unsigned long size = 0; |
171 |
char *s; |
172 |
int len; |
173 |
|
174 |
if (des) |
175 |
init_des_cipher(); |
176 |
for (; n && n <= m; n++, lp = lp->q_forw) { |
177 |
if ((s = get_sbuf_line(lp)) == NULL) |
178 |
return ERR; |
179 |
len = lp->len; |
180 |
if (n != addr_last || !isbinary || !newline_added) |
181 |
s[len++] = '\n'; |
182 |
if (put_stream_line(fp, s, len) < 0) |
183 |
return ERR; |
184 |
size += len; |
185 |
} |
186 |
if (des) { |
187 |
flush_des_file(fp); /* flush buffer */ |
188 |
size += 8 - size % 8; /* adjust DES size */ |
189 |
} |
190 |
return size; |
191 |
} |
192 |
|
193 |
|
194 |
/* put_stream_line: write a line of text to a stream; return status */ |
195 |
int |
196 |
put_stream_line(FILE *fp, const char *s, int len) |
197 |
{ |
198 |
while (len--) |
199 |
if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) { |
200 |
fprintf(stderr, "%s\n", strerror(errno)); |
201 |
errmsg = "cannot write file"; |
202 |
return ERR; |
203 |
} |
204 |
return 0; |
205 |
} |
206 |
|
207 |
/* get_extended_line: get an extended line from stdin */ |
208 |
char * |
209 |
get_extended_line(int *sizep, int nonl) |
210 |
{ |
211 |
static char *cvbuf = NULL; /* buffer */ |
212 |
static int cvbufsz = 0; /* buffer size */ |
213 |
|
214 |
int l, n; |
215 |
char *t = ibufp; |
216 |
|
217 |
while (*t++ != '\n') |
218 |
; |
219 |
if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) { |
220 |
*sizep = l; |
221 |
return ibufp; |
222 |
} |
223 |
*sizep = -1; |
224 |
REALLOC(cvbuf, cvbufsz, l, NULL); |
225 |
memcpy(cvbuf, ibufp, l); |
226 |
*(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ |
227 |
if (nonl) l--; /* strip newline */ |
228 |
for (;;) { |
229 |
if ((n = get_tty_line()) < 0) |
230 |
return NULL; |
231 |
else if (n == 0 || ibuf[n - 1] != '\n') { |
232 |
errmsg = "unexpected end-of-file"; |
233 |
return NULL; |
234 |
} |
235 |
REALLOC(cvbuf, cvbufsz, l + n, NULL); |
236 |
memcpy(cvbuf + l, ibuf, n); |
237 |
l += n; |
238 |
if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1)) |
239 |
break; |
240 |
*(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ |
241 |
if (nonl) l--; /* strip newline */ |
242 |
} |
243 |
REALLOC(cvbuf, cvbufsz, l + 1, NULL); |
244 |
cvbuf[l] = '\0'; |
245 |
*sizep = l; |
246 |
return cvbuf; |
247 |
} |
248 |
|
249 |
|
250 |
/* get_tty_line: read a line of text from stdin; return line length */ |
251 |
int |
252 |
get_tty_line(void) |
253 |
{ |
254 |
int oi = 0; |
255 |
int i = 0; |
256 |
int c; |
257 |
|
258 |
for (;;) |
259 |
switch (c = getchar()) { |
260 |
default: |
261 |
oi = 0; |
262 |
REALLOC(ibuf, ibufsz, i + 2, ERR); |
263 |
if (!(ibuf[i++] = c)) isbinary = 1; |
264 |
if (c != '\n') |
265 |
continue; |
266 |
lineno++; |
267 |
ibuf[i] = '\0'; |
268 |
ibufp = ibuf; |
269 |
return i; |
270 |
case EOF: |
271 |
if (ferror(stdin)) { |
272 |
fprintf(stderr, "stdin: %s\n", strerror(errno)); |
273 |
errmsg = "cannot read stdin"; |
274 |
clearerr(stdin); |
275 |
ibufp = NULL; |
276 |
return ERR; |
277 |
} else { |
278 |
clearerr(stdin); |
279 |
if (i != oi) { |
280 |
oi = i; |
281 |
continue; |
282 |
} else if (i) |
283 |
ibuf[i] = '\0'; |
284 |
ibufp = ibuf; |
285 |
return i; |
286 |
} |
287 |
} |
288 |
} |
289 |
|
290 |
|
291 |
|
292 |
#define ESCAPES "\a\b\f\n\r\t\v\\" |
293 |
#define ESCCHARS "abfnrtv\\" |
294 |
|
295 |
/* put_tty_line: print text to stdout */ |
296 |
int |
297 |
put_tty_line(const char *s, int l, long n, int gflag) |
298 |
{ |
299 |
int col = 0; |
300 |
int lc = 0; |
301 |
char *cp; |
302 |
|
303 |
if (gflag & GNP) { |
304 |
printf("%ld\t", n); |
305 |
col = 8; |
306 |
} |
307 |
for (; l--; s++) { |
308 |
if ((gflag & GLS) && ++col > cols) { |
309 |
fputs("\\\n", stdout); |
310 |
col = 1; |
311 |
#ifndef BACKWARDS |
312 |
if (!scripted && !isglobal && ++lc > rows) { |
313 |
lc = 0; |
314 |
fputs("Press <RETURN> to continue... ", stdout); |
315 |
fflush(stdout); |
316 |
if (get_tty_line() < 0) |
317 |
return ERR; |
318 |
} |
319 |
#endif |
320 |
} |
321 |
if (gflag & GLS) { |
322 |
if (31 < *s && *s < 127 && *s != '\\') |
323 |
putchar(*s); |
324 |
else { |
325 |
putchar('\\'); |
326 |
col++; |
327 |
if (*s && (cp = strchr(ESCAPES, *s)) != NULL) |
328 |
putchar(ESCCHARS[cp - ESCAPES]); |
329 |
else { |
330 |
putchar((((unsigned char) *s & 0300) >> 6) + '0'); |
331 |
putchar((((unsigned char) *s & 070) >> 3) + '0'); |
332 |
putchar(((unsigned char) *s & 07) + '0'); |
333 |
col += 2; |
334 |
} |
335 |
} |
336 |
|
337 |
} else |
338 |
putchar(*s); |
339 |
} |
340 |
#ifndef BACKWARDS |
341 |
if (gflag & GLS) |
342 |
putchar('$'); |
343 |
#endif |
344 |
putchar('\n'); |
345 |
return 0; |
346 |
} |