1/*	$OpenBSD: buf.c,v 1.25 2024/04/23 13:34:50 jsg Exp $	*/
2/*	$NetBSD: buf.c,v 1.15 1995/04/23 10:07:28 cgd Exp $	*/
3
4/* buf.c: This file contains the scratch-file buffer routines for the
5   ed line editor. */
6/*-
7 * Copyright (c) 1993 Andrew Moore, Talke Studio.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/types.h>
33#include <sys/stat.h>
34
35#include <limits.h>
36#include <regex.h>
37#include <signal.h>
38#include <stdlib.h>
39#include <stdio.h>
40#include <string.h>
41#include <unistd.h>
42
43#include "ed.h"
44
45
46static FILE *sfp;			/* scratch file pointer */
47static off_t sfseek;			/* scratch file position */
48static int seek_write;			/* seek before writing */
49static line_t buffer_head;		/* incore buffer */
50
51/* get_sbuf_line: get a line of text from the scratch file; return pointer
52   to the text */
53char *
54get_sbuf_line(line_t *lp)
55{
56	static char *sfbuf = NULL;	/* buffer */
57	static int sfbufsz = 0;		/* buffer size */
58	int len;
59
60	if (lp == &buffer_head)
61		return NULL;
62	seek_write = 1;				/* force seek on write */
63	/* out of position */
64	if (sfseek != lp->seek) {
65		sfseek = lp->seek;
66		if (fseeko(sfp, sfseek, SEEK_SET) == -1) {
67			perror(NULL);
68			seterrmsg("cannot seek temp file");
69			return NULL;
70		}
71	}
72	len = lp->len;
73	REALLOC(sfbuf, sfbufsz, len + 1, NULL);
74	if (fread(sfbuf, sizeof(char), len, sfp) != len) {
75		perror(NULL);
76		seterrmsg("cannot read temp file");
77		return NULL;
78	}
79	sfseek += len;				/* update file position */
80	sfbuf[len] = '\0';
81	return sfbuf;
82}
83
84
85/* put_sbuf_line: write a line of text to the scratch file and add a line node
86   to the editor buffer;  return a pointer to the end of the text */
87char *
88put_sbuf_line(char *cs)
89{
90	line_t *lp;
91	int len;
92	char *s;
93
94	if ((lp = malloc(sizeof(line_t))) == NULL) {
95		perror(NULL);
96		seterrmsg("out of memory");
97		return NULL;
98	}
99	/* assert: cs is '\n' terminated */
100	for (s = cs; *s != '\n'; s++)
101		;
102	if (s - cs >= LINECHARS) {
103		seterrmsg("line too long");
104		free(lp);
105		return NULL;
106	}
107	len = s - cs;
108	/* out of position */
109	if (seek_write) {
110		if (fseek(sfp, 0L, SEEK_END) == -1) {
111			perror(NULL);
112			seterrmsg("cannot seek temp file");
113			free(lp);
114			return NULL;
115		}
116		sfseek = ftello(sfp);
117		seek_write = 0;
118	}
119	/* assert: SPL1() */
120	if (fwrite(cs, sizeof(char), len, sfp) != len) {
121		sfseek = -1;
122		perror(NULL);
123		seterrmsg("cannot write temp file");
124		free(lp);
125		return NULL;
126	}
127	lp->len = len;
128	lp->seek  = sfseek;
129	add_line_node(lp);
130	sfseek += len;			/* update file position */
131	return ++s;
132}
133
134
135/* add_line_node: add a line node in the editor buffer after the current line */
136void
137add_line_node(line_t *lp)
138{
139	line_t *cp;
140
141	/* this get_addressed_line_node last! */
142	cp = get_addressed_line_node(current_addr);
143	INSQUE(lp, cp);
144	addr_last++;
145	current_addr++;
146}
147
148
149/* get_line_node_addr: return line number of pointer */
150int
151get_line_node_addr(line_t *lp)
152{
153	line_t *cp = &buffer_head;
154	int n = 0;
155
156	while (cp != lp && (cp = cp->q_forw) != &buffer_head)
157		n++;
158	if (n && cp == &buffer_head) {
159		seterrmsg("invalid address");
160		return ERR;
161	}
162	return n;
163}
164
165
166/* get_addressed_line_node: return pointer to a line node in the editor buffer */
167line_t *
168get_addressed_line_node(int n)
169{
170	static line_t *lp = &buffer_head;
171	static int on = 0;
172
173	SPL1();
174	if (n > on) {
175		if (n <= (on + addr_last) >> 1)
176			for (; on < n; on++)
177				lp = lp->q_forw;
178		else {
179			lp = buffer_head.q_back;
180			for (on = addr_last; on > n; on--)
181				lp = lp->q_back;
182		}
183	} else {
184		if (n >= on >> 1)
185			for (; on > n; on--)
186				lp = lp->q_back;
187		else {
188			lp = &buffer_head;
189			for (on = 0; on < n; on++)
190				lp = lp->q_forw;
191		}
192	}
193	SPL0();
194	return lp;
195}
196
197
198extern int newline_added;
199
200#define SCRATCH_TEMPLATE      "/tmp/ed.XXXXXXXXXX"
201static char sfn[sizeof(SCRATCH_TEMPLATE)+1] = "";	/* scratch file name */
202
203/* open_sbuf: open scratch file */
204int
205open_sbuf(void)
206{
207	int fd = -1;
208
209	isbinary = newline_added = 0;
210	strlcpy(sfn, SCRATCH_TEMPLATE, sizeof sfn);
211	if ((fd = mkstemp(sfn)) == -1 ||
212	    (sfp = fdopen(fd, "w+")) == NULL) {
213		if (fd != -1)
214			close(fd);
215		perror(sfn);
216		seterrmsg("cannot open temp file");
217		return ERR;
218	}
219	return 0;
220}
221
222
223/* close_sbuf: close scratch file */
224int
225close_sbuf(void)
226{
227	if (sfp) {
228		if (fclose(sfp) == EOF) {
229			perror(sfn);
230			seterrmsg("cannot close temp file");
231			return ERR;
232		}
233		sfp = NULL;
234		unlink(sfn);
235	}
236	sfseek = seek_write = 0;
237	return 0;
238}
239
240
241/* quit: remove_lines scratch file and exit */
242void
243quit(int n)
244{
245	if (sfp) {
246		fclose(sfp);
247		unlink(sfn);
248	}
249	exit(n);
250}
251
252
253static unsigned char ctab[256];		/* character translation table */
254
255/* init_buffers: open scratch buffer; initialize line queue */
256void
257init_buffers(void)
258{
259	int i = 0;
260
261	/* Read stdin one character at a time to avoid i/o contention
262	   with shell escapes invoked by nonterminal input, e.g.,
263	   ed - <<EOF
264	   !cat
265	   hello, world
266	   EOF */
267	setvbuf(stdin, NULL, _IONBF, 0);
268	if (open_sbuf() < 0)
269		quit(2);
270	REQUE(&buffer_head, &buffer_head);
271	for (i = 0; i < 256; i++)
272		ctab[i] = i;
273}
274
275
276/* translit_text: translate characters in a string */
277char *
278translit_text(char *s, int len, int from, int to)
279{
280	static int i = 0;
281
282	unsigned char *us;
283
284	ctab[i] = i;			/* restore table to initial state */
285	ctab[i = from] = to;
286	for (us = (unsigned char *) s; len-- > 0; us++)
287		*us = ctab[*us];
288	return s;
289}
290