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$");
30
31#include "ed.h"
32
33/* read_file: read a named file/pipe into the buffer; return line count */
34long
35read_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
58static char *sbuf;		/* file i/o buffer */
59static int sbufsz;		/* file i/o buffer size */
60int newline_added;		/* if set, newline appended to input file */
61
62/* read_stream: read a stream into the editor buffer; return status */
63long
64read_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 */
112int
113get_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 */
141long
142write_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 */
166long
167write_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 */
195int
196put_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 */
208char *
209get_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 */
251int
252get_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 */
296int
297put_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}
347