xref: /netbsd-src/bin/ed/io.c (revision d47295cc35d0f14450e9b401e5c838084cc9e0d2)
1*d47295ccSrillig /*	$NetBSD: io.c,v 1.11 2024/10/03 20:14:01 rillig Exp $	*/
249f0ad86Scgd 
31357f155Salm /* io.c: This file contains the i/o routines for the ed line editor */
41357f155Salm /*-
51357f155Salm  * Copyright (c) 1993 Andrew Moore, Talke Studio.
61357f155Salm  * All rights reserved.
71357f155Salm  *
81357f155Salm  * Redistribution and use in source and binary forms, with or without
91357f155Salm  * modification, are permitted provided that the following conditions
101357f155Salm  * are met:
111357f155Salm  * 1. Redistributions of source code must retain the above copyright
121357f155Salm  *    notice, this list of conditions and the following disclaimer.
131357f155Salm  * 2. Redistributions in binary form must reproduce the above copyright
141357f155Salm  *    notice, this list of conditions and the following disclaimer in the
151357f155Salm  *    documentation and/or other materials provided with the distribution.
161357f155Salm  *
171357f155Salm  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
181357f155Salm  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191357f155Salm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201357f155Salm  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
211357f155Salm  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221357f155Salm  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231357f155Salm  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241357f155Salm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251357f155Salm  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261357f155Salm  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271357f155Salm  * SUCH DAMAGE.
281357f155Salm  */
291357f155Salm 
308b7ade1aSthorpej #include <sys/cdefs.h>
311357f155Salm #ifndef lint
3249f0ad86Scgd #if 0
331357f155Salm static char *rcsid = "@(#)io.c,v 1.1 1994/02/01 00:34:41 alm Exp";
3449f0ad86Scgd #else
35*d47295ccSrillig __RCSID("$NetBSD: io.c,v 1.11 2024/10/03 20:14:01 rillig Exp $");
3649f0ad86Scgd #endif
371357f155Salm #endif /* not lint */
381357f155Salm 
391357f155Salm #include "ed.h"
401357f155Salm 
411357f155Salm 
421357f155Salm /* read_file: read a named file/pipe into the buffer; return line count */
431357f155Salm long
44d33a7206Sxtraeme read_file(char *fn, long n)
451357f155Salm {
461357f155Salm 	FILE *fp;
471357f155Salm 	long size;
481357f155Salm 
491357f155Salm 
501357f155Salm 	fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r");
511357f155Salm 	if (fp == NULL) {
521357f155Salm 		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
53a3542600Sdholland 		seterrmsg("cannot open input file");
541357f155Salm 		return ERR;
551357f155Salm 	} else if ((size = read_stream(fp, n)) < 0)
561357f155Salm 		return ERR;
571357f155Salm 	 else if (((*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
581357f155Salm 		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
59a3542600Sdholland 		seterrmsg("cannot close input file");
601357f155Salm 		return ERR;
611357f155Salm 	}
62a86027b1Sjoerg 	if (!scripted)
63a86027b1Sjoerg 		fprintf(stderr, "%lu\n", size);
641357f155Salm 	return current_addr - n;
651357f155Salm }
661357f155Salm 
671357f155Salm 
681357f155Salm char *sbuf;			/* file i/o buffer */
691357f155Salm int sbufsz;			/* file i/o buffer size */
701357f155Salm int newline_added;		/* if set, newline appended to input file */
711357f155Salm 
721357f155Salm /* read_stream: read a stream into the editor buffer; return status */
731357f155Salm long
74d33a7206Sxtraeme read_stream(FILE *fp, long n)
751357f155Salm {
761357f155Salm 	line_t *lp = get_addressed_line_node(n);
771357f155Salm 	undo_t *up = NULL;
781357f155Salm 	unsigned long size = 0;
791357f155Salm 	int o_newline_added = newline_added;
801357f155Salm 	int o_isbinary = isbinary;
811357f155Salm 	int appended = (n == addr_last);
821357f155Salm 	int len;
831357f155Salm 
841357f155Salm 	isbinary = newline_added = 0;
851357f155Salm 	if (des)
861357f155Salm 		init_des_cipher();
871357f155Salm 	for (current_addr = n; (len = get_stream_line(fp)) > 0; size += len) {
881357f155Salm 		SPL1();
891357f155Salm 		if (put_sbuf_line(sbuf) == NULL) {
901357f155Salm 			SPL0();
911357f155Salm 			return ERR;
921357f155Salm 		}
931357f155Salm 		lp = lp->q_forw;
941357f155Salm 		if (up)
951357f155Salm 			up->t = lp;
961357f155Salm 		else if ((up = push_undo_stack(UADD, current_addr,
971357f155Salm 		    current_addr)) == NULL) {
981357f155Salm 			SPL0();
991357f155Salm 			return ERR;
1001357f155Salm 		}
1011357f155Salm 		SPL0();
1021357f155Salm 	}
1031357f155Salm 	if (len < 0)
1041357f155Salm 		return ERR;
1051357f155Salm 	if (appended && size && o_isbinary && o_newline_added)
1061357f155Salm 		fputs("newline inserted\n", stderr);
1078b7ade1aSthorpej 	else if (newline_added && (!appended || (!isbinary && !o_isbinary)))
1081357f155Salm 		fputs("newline appended\n", stderr);
1091357f155Salm 	if (isbinary && newline_added && !appended)
1101357f155Salm 	    	size += 1;
1111357f155Salm 	if (!size)
1121357f155Salm 		newline_added = 1;
1131357f155Salm 	newline_added = appended ? newline_added : o_newline_added;
1141357f155Salm 	isbinary = isbinary | o_isbinary;
1151357f155Salm 	if (des)
1161357f155Salm 		size += 8 - size % 8;			/* adjust DES size */
1171357f155Salm 	return size;
1181357f155Salm }
1191357f155Salm 
1201357f155Salm 
1211357f155Salm /* get_stream_line: read a line of text from a stream; return line length */
1221357f155Salm int
123d33a7206Sxtraeme get_stream_line(FILE *fp)
1241357f155Salm {
1253811362cStls 	int c;
1263811362cStls 	int i = 0;
1271357f155Salm 
1288b7ade1aSthorpej 	while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || (!feof(fp) &&
1298b7ade1aSthorpej 	    !ferror(fp))) && c != '\n') {
1301357f155Salm 		REALLOC(sbuf, sbufsz, i + 1, ERR);
1311357f155Salm 		if (!(sbuf[i++] = c))
1321357f155Salm 			isbinary = 1;
1331357f155Salm 	}
1341357f155Salm 	REALLOC(sbuf, sbufsz, i + 2, ERR);
1351357f155Salm 	if (c == '\n')
1361357f155Salm 		sbuf[i++] = c;
1371357f155Salm 	else if (ferror(fp)) {
1381357f155Salm 		fprintf(stderr, "%s\n", strerror(errno));
139a3542600Sdholland 		seterrmsg("cannot read input file");
1401357f155Salm 		return ERR;
1411357f155Salm 	} else if (i) {
1421357f155Salm 		sbuf[i++] = '\n';
1431357f155Salm 		newline_added = 1;
1441357f155Salm 	}
1451357f155Salm 	sbuf[i] = '\0';
1461357f155Salm 	return (isbinary && newline_added && i) ? --i : i;
1471357f155Salm }
1481357f155Salm 
1491357f155Salm 
1501357f155Salm /* write_file: write a range of lines to a named file/pipe; return line count */
1511357f155Salm long
1526310b596Schristos write_file(const char *fn, const char *mode, long n, long m)
1531357f155Salm {
1541357f155Salm 	FILE *fp;
1551357f155Salm 	long size;
1561357f155Salm 
1571357f155Salm 	fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode);
1581357f155Salm 	if (fp == NULL) {
1591357f155Salm 		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
160a3542600Sdholland 		seterrmsg("cannot open output file");
1611357f155Salm 		return ERR;
1621357f155Salm 	} else if ((size = write_stream(fp, n, m)) < 0)
1631357f155Salm 		return ERR;
1641357f155Salm 	 else if (((*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
1651357f155Salm 		fprintf(stderr, "%s: %s\n", fn, strerror(errno));
166a3542600Sdholland 		seterrmsg("cannot close output file");
1671357f155Salm 		return ERR;
1681357f155Salm 	}
169a86027b1Sjoerg 	if (!scripted)
170a86027b1Sjoerg 		fprintf(stderr, "%lu\n", size);
1711357f155Salm 	return n ? m - n + 1 : 0;
1721357f155Salm }
1731357f155Salm 
1741357f155Salm 
1751357f155Salm /* write_stream: write a range of lines to a stream; return status */
1761357f155Salm long
177d33a7206Sxtraeme write_stream(FILE *fp, long n, long m)
1781357f155Salm {
1791357f155Salm 	line_t *lp = get_addressed_line_node(n);
1801357f155Salm 	unsigned long size = 0;
1811357f155Salm 	char *s;
1821357f155Salm 	int len;
1831357f155Salm 
1841357f155Salm 	if (des)
1851357f155Salm 		init_des_cipher();
1861357f155Salm 	for (; n && n <= m; n++, lp = lp->q_forw) {
1871357f155Salm 		if ((s = get_sbuf_line(lp)) == NULL)
1881357f155Salm 			return ERR;
1891357f155Salm 		len = lp->len;
1901357f155Salm 		if (n != addr_last || !isbinary || !newline_added)
1911357f155Salm 			s[len++] = '\n';
1921357f155Salm 		if (put_stream_line(fp, s, len) < 0)
1931357f155Salm 			return ERR;
1941357f155Salm 		size += len;
1951357f155Salm 	}
1961357f155Salm 	if (des) {
1971357f155Salm 		flush_des_file(fp);			/* flush buffer */
1981357f155Salm 		size += 8 - size % 8;			/* adjust DES size */
1991357f155Salm 	}
2001357f155Salm 	return size;
2011357f155Salm }
2021357f155Salm 
2031357f155Salm 
2041357f155Salm /* put_stream_line: write a line of text to a stream; return status */
2051357f155Salm int
206d33a7206Sxtraeme put_stream_line(FILE *fp, char *s, int len)
2071357f155Salm {
2081357f155Salm 	while (len--)
2091357f155Salm 		if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) {
2101357f155Salm 			fprintf(stderr, "%s\n", strerror(errno));
211a3542600Sdholland 			seterrmsg("cannot write file");
2121357f155Salm 			return ERR;
2131357f155Salm 		}
2141357f155Salm 	return 0;
2151357f155Salm }
2161357f155Salm 
2171357f155Salm /* get_extended_line: get a an extended line from stdin */
2181357f155Salm char *
219d33a7206Sxtraeme get_extended_line(int *sizep, int nonl)
2201357f155Salm {
2211357f155Salm 	static char *cvbuf = NULL;		/* buffer */
2221357f155Salm 	static int cvbufsz = 0;			/* buffer size */
2231357f155Salm 
2241357f155Salm 	int l, n;
2251357f155Salm 	char *t = ibufp;
2261357f155Salm 
2271357f155Salm 	while (*t++ != '\n')
2281357f155Salm 		;
2291357f155Salm 	if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) {
2301357f155Salm 		*sizep = l;
2311357f155Salm 		return ibufp;
2321357f155Salm 	}
2331357f155Salm 	*sizep = -1;
2341357f155Salm 	REALLOC(cvbuf, cvbufsz, l, NULL);
2351357f155Salm 	memcpy(cvbuf, ibufp, l);
2361357f155Salm 	*(cvbuf + --l - 1) = '\n'; 	/* strip trailing esc */
2371357f155Salm 	if (nonl) l--; 			/* strip newline */
2381357f155Salm 	for (;;) {
2391357f155Salm 		if ((n = get_tty_line()) < 0)
2401357f155Salm 			return NULL;
2411357f155Salm 		else if (n == 0 || ibuf[n - 1] != '\n') {
242a3542600Sdholland 			seterrmsg("unexpected end-of-file");
2431357f155Salm 			return NULL;
2441357f155Salm 		}
2451357f155Salm 		REALLOC(cvbuf, cvbufsz, l + n, NULL);
2461357f155Salm 		memcpy(cvbuf + l, ibuf, n);
2471357f155Salm 		l += n;
2481357f155Salm 		if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1))
2491357f155Salm 			break;
2501357f155Salm 		*(cvbuf + --l - 1) = '\n'; 	/* strip trailing esc */
2511357f155Salm 		if (nonl) l--; 			/* strip newline */
2521357f155Salm 	}
2531357f155Salm 	REALLOC(cvbuf, cvbufsz, l + 1, NULL);
2541357f155Salm 	cvbuf[l] = '\0';
2551357f155Salm 	*sizep = l;
2561357f155Salm 	return cvbuf;
2571357f155Salm }
2581357f155Salm 
2591357f155Salm 
2601357f155Salm /* get_tty_line: read a line of text from stdin; return line length */
2611357f155Salm int
262d33a7206Sxtraeme get_tty_line(void)
2631357f155Salm {
2643811362cStls 	int oi = 0;
2653811362cStls 	int i = 0;
2661357f155Salm 	int c;
2671357f155Salm 
2681357f155Salm 	for (;;)
2691357f155Salm 		switch (c = getchar()) {
2701357f155Salm 		default:
2711357f155Salm 			oi = 0;
2721357f155Salm 			REALLOC(ibuf, ibufsz, i + 2, ERR);
2731357f155Salm 			if (!(ibuf[i++] = c)) isbinary = 1;
2741357f155Salm 			if (c != '\n')
2751357f155Salm 				continue;
2761357f155Salm 			lineno++;
2771357f155Salm 			ibuf[i] = '\0';
2781357f155Salm 			ibufp = ibuf;
2791357f155Salm 			return i;
2801357f155Salm 		case EOF:
2811357f155Salm 			if (ferror(stdin)) {
2821357f155Salm 				fprintf(stderr, "stdin: %s\n", strerror(errno));
283a3542600Sdholland 				seterrmsg("cannot read stdin");
2841357f155Salm 				clearerr(stdin);
2851357f155Salm 				ibufp = NULL;
2861357f155Salm 				return ERR;
2871357f155Salm 			} else {
2881357f155Salm 				clearerr(stdin);
2891357f155Salm 				if (i != oi) {
2901357f155Salm 					oi = i;
2911357f155Salm 					continue;
2921357f155Salm 				} else if (i)
2931357f155Salm 					ibuf[i] = '\0';
2941357f155Salm 				ibufp = ibuf;
2951357f155Salm 				return i;
2961357f155Salm 			}
2971357f155Salm 		}
2981357f155Salm }
2991357f155Salm 
3001357f155Salm 
3011357f155Salm 
3021357f155Salm #define ESCAPES "\a\b\f\n\r\t\v\\"
3031357f155Salm #define ESCCHARS "abfnrtv\\"
3041357f155Salm 
3051357f155Salm /* put_tty_line: print text to stdout */
3061357f155Salm int
307d33a7206Sxtraeme put_tty_line(char *s, int l, long n, int gflag)
3081357f155Salm {
3091357f155Salm 	int col = 0;
310*d47295ccSrillig 	const char *cp;
311d1631dd9Schristos #ifndef BACKWARDS
312d1631dd9Schristos 	int lc = 0;
313d1631dd9Schristos #endif
3141357f155Salm 
3151357f155Salm 	if (gflag & GNP) {
3161357f155Salm 		printf("%ld\t", n);
3171357f155Salm 		col = 8;
3181357f155Salm 	}
3191357f155Salm 	for (; l--; s++) {
3201357f155Salm 		if ((gflag & GLS) && ++col > cols) {
3211357f155Salm 			fputs("\\\n", stdout);
3221357f155Salm 			col = 1;
3231357f155Salm #ifndef BACKWARDS
3241357f155Salm 			if (!scripted && !isglobal && ++lc > rows) {
3251357f155Salm 				lc = 0;
3261357f155Salm 				fputs("Press <RETURN> to continue... ", stdout);
3271357f155Salm 				fflush(stdout);
3281357f155Salm 				if (get_tty_line() < 0)
3291357f155Salm 					return ERR;
3301357f155Salm 			}
3311357f155Salm #endif
3321357f155Salm 		}
3331357f155Salm 		if (gflag & GLS) {
3341357f155Salm 			if (31 < *s && *s < 127 && *s != '\\')
3351357f155Salm 				putchar(*s);
3361357f155Salm 			else {
3371357f155Salm 				putchar('\\');
3381357f155Salm 				col++;
3391357f155Salm 				if (*s && (cp = strchr(ESCAPES, *s)) != NULL)
3401357f155Salm 					putchar(ESCCHARS[cp - ESCAPES]);
3411357f155Salm 				else {
3421357f155Salm 					putchar((((unsigned char) *s & 0300) >> 6) + '0');
3431357f155Salm 					putchar((((unsigned char) *s & 070) >> 3) + '0');
3441357f155Salm 					putchar(((unsigned char) *s & 07) + '0');
3451357f155Salm 					col += 2;
3461357f155Salm 				}
3471357f155Salm 			}
3481357f155Salm 
3491357f155Salm 		} else
3501357f155Salm 			putchar(*s);
3511357f155Salm 	}
3521357f155Salm #ifndef BACKWARDS
3531357f155Salm 	if (gflag & GLS)
3541357f155Salm 		putchar('$');
3551357f155Salm #endif
3561357f155Salm 	putchar('\n');
3571357f155Salm 	return 0;
3581357f155Salm }
359