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#include "ed.h"
30
31/* read_file: read a named file/pipe into the buffer; return line count */
32long
33read_file(char *fn, long n)
34{
35	FILE *fp;
36	long size;
37	int cs;
38
39	fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r");
40	if (fp == NULL) {
41		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
42		errmsg = "cannot open input file";
43		return ERR;
44	}
45	if ((size = read_stream(fp, n)) < 0) {
46		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
47		errmsg = "error reading input file";
48	}
49	if ((cs = (*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
50		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
51		errmsg = "cannot close input file";
52	}
53	if (size < 0 || cs < 0)
54		return ERR;
55	if (!scripted)
56		fprintf(stdout, "%lu\n", size);
57	return current_addr - n;
58}
59
60static char *sbuf;		/* file i/o buffer */
61static int sbufsz;		/* file i/o buffer size */
62int newline_added;		/* if set, newline appended to input file */
63
64/* read_stream: read a stream into the editor buffer; return status */
65long
66read_stream(FILE *fp, long n)
67{
68	line_t *lp = get_addressed_line_node(n);
69	undo_t *up = NULL;
70	unsigned long size = 0;
71	int o_newline_added = newline_added;
72	int o_isbinary = isbinary;
73	int appended = (n == addr_last);
74	int len;
75
76	isbinary = newline_added = 0;
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	return size;
106}
107
108
109/* get_stream_line: read a line of text from a stream; return line length */
110int
111get_stream_line(FILE *fp)
112{
113	int c;
114	int i = 0;
115
116	while (((c = getc(fp)) != EOF || (!feof(fp) && !ferror(fp))) &&
117	    c != '\n') {
118		REALLOC(sbuf, sbufsz, i + 1, ERR);
119		if (!(sbuf[i++] = c))
120			isbinary = 1;
121	}
122	REALLOC(sbuf, sbufsz, i + 2, ERR);
123	if (c == '\n')
124		sbuf[i++] = c;
125	else if (ferror(fp)) {
126		fprintf(stderr, "%s\n", strerror(errno));
127		errmsg = "cannot read input file";
128		return ERR;
129	} else if (i) {
130		sbuf[i++] = '\n';
131		newline_added = 1;
132	}
133	sbuf[i] = '\0';
134	return (isbinary && newline_added && i) ? --i : i;
135}
136
137
138/* write_file: write a range of lines to a named file/pipe; return line count */
139long
140write_file(char *fn, const char *mode, long n, long m)
141{
142	FILE *fp;
143	long size;
144	int cs;
145
146	fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode);
147	if (fp == NULL) {
148		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
149		errmsg = "cannot open output file";
150		return ERR;
151	}
152	if ((size = write_stream(fp, n, m)) < 0) {
153		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
154		errmsg = "error writing output file";
155	}
156	if ((cs = (*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
157		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
158		errmsg = "cannot close output file";
159	}
160	if (size < 0 || cs < 0)
161		return ERR;
162	if (!scripted)
163		fprintf(stdout, "%lu\n", size);
164	return n ? m - n + 1 : 0;
165}
166
167
168/* write_stream: write a range of lines to a stream; return status */
169long
170write_stream(FILE *fp, long n, long m)
171{
172	line_t *lp = get_addressed_line_node(n);
173	unsigned long size = 0;
174	char *s;
175	int len;
176
177	for (; n && n <= m; n++, lp = lp->q_forw) {
178		if ((s = get_sbuf_line(lp)) == NULL)
179			return ERR;
180		len = lp->len;
181		if (n != addr_last || !isbinary || !newline_added)
182			s[len++] = '\n';
183		if (put_stream_line(fp, s, len) < 0)
184			return ERR;
185		size += len;
186	}
187	return size;
188}
189
190
191/* put_stream_line: write a line of text to a stream; return status */
192int
193put_stream_line(FILE *fp, const char *s, int len)
194{
195	while (len--)
196		if (fputc(*s++, fp) < 0) {
197			fprintf(stderr, "%s\n", strerror(errno));
198			errmsg = "cannot write file";
199			return ERR;
200		}
201	return 0;
202}
203
204/* get_extended_line: get an extended line from stdin */
205char *
206get_extended_line(int *sizep, int nonl)
207{
208	static char *cvbuf = NULL;		/* buffer */
209	static int cvbufsz = 0;			/* buffer size */
210
211	int l, n;
212	char *t = ibufp;
213
214	while (*t++ != '\n')
215		;
216	if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) {
217		*sizep = l;
218		return ibufp;
219	}
220	*sizep = -1;
221	REALLOC(cvbuf, cvbufsz, l, NULL);
222	memcpy(cvbuf, ibufp, l);
223	*(cvbuf + --l - 1) = '\n'; 	/* strip trailing esc */
224	if (nonl) l--; 			/* strip newline */
225	for (;;) {
226		if ((n = get_tty_line()) < 0)
227			return NULL;
228		else if (n == 0 || ibuf[n - 1] != '\n') {
229			errmsg = "unexpected end-of-file";
230			return NULL;
231		}
232		REALLOC(cvbuf, cvbufsz, l + n, NULL);
233		memcpy(cvbuf + l, ibuf, n);
234		l += n;
235		if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1))
236			break;
237		*(cvbuf + --l - 1) = '\n'; 	/* strip trailing esc */
238		if (nonl) l--; 			/* strip newline */
239	}
240	REALLOC(cvbuf, cvbufsz, l + 1, NULL);
241	cvbuf[l] = '\0';
242	*sizep = l;
243	return cvbuf;
244}
245
246
247/* get_tty_line: read a line of text from stdin; return line length */
248int
249get_tty_line(void)
250{
251	int oi = 0;
252	int i = 0;
253	int c;
254
255	for (;;)
256		switch (c = getchar()) {
257		default:
258			oi = 0;
259			REALLOC(ibuf, ibufsz, i + 2, ERR);
260			if (!(ibuf[i++] = c)) isbinary = 1;
261			if (c != '\n')
262				continue;
263			lineno++;
264			ibuf[i] = '\0';
265			ibufp = ibuf;
266			return i;
267		case EOF:
268			if (ferror(stdin)) {
269				fprintf(stderr, "stdin: %s\n", strerror(errno));
270				errmsg = "cannot read stdin";
271				clearerr(stdin);
272				ibufp = NULL;
273				return ERR;
274			} else {
275				clearerr(stdin);
276				if (i != oi) {
277					oi = i;
278					continue;
279				} else if (i)
280					ibuf[i] = '\0';
281				ibufp = ibuf;
282				return i;
283			}
284		}
285}
286
287
288
289#define ESCAPES "\a\b\f\n\r\t\v\\"
290#define ESCCHARS "abfnrtv\\"
291
292/* put_tty_line: print text to stdout */
293int
294put_tty_line(const char *s, int l, long n, int gflag)
295{
296	int col = 0;
297	int lc = 0;
298	char *cp;
299
300	if (gflag & GNP) {
301		printf("%ld\t", n);
302		col = 8;
303	}
304	for (; l--; s++) {
305		if ((gflag & GLS) && ++col > cols) {
306			fputs("\\\n", stdout);
307			col = 1;
308#ifndef BACKWARDS
309			if (!scripted && !isglobal && ++lc > rows) {
310				lc = 0;
311				fputs("Press <RETURN> to continue... ", stdout);
312				fflush(stdout);
313				if (get_tty_line() < 0)
314					return ERR;
315			}
316#endif
317		}
318		if (gflag & GLS) {
319			if (31 < *s && *s < 127 && *s != '\\')
320				putchar(*s);
321			else {
322				putchar('\\');
323				col++;
324				if (*s && (cp = strchr(ESCAPES, *s)) != NULL)
325					putchar(ESCCHARS[cp - ESCAPES]);
326				else {
327					putchar((((unsigned char) *s & 0300) >> 6) + '0');
328					putchar((((unsigned char) *s & 070) >> 3) + '0');
329					putchar(((unsigned char) *s & 07) + '0');
330					col += 2;
331				}
332			}
333
334		} else
335			putchar(*s);
336	}
337#ifndef BACKWARDS
338	if (gflag & GLS)
339		putchar('$');
340#endif
341	putchar('\n');
342	return 0;
343}
344