xref: /openbsd/bin/ed/buf.c (revision e6c7c102)
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 
46 static FILE *sfp;			/* scratch file pointer */
47 static off_t sfseek;			/* scratch file position */
48 static int seek_write;			/* seek before writing */
49 static 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 */
53 char *
get_sbuf_line(line_t * lp)54 get_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 */
87 char *
put_sbuf_line(char * cs)88 put_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 */
136 void
add_line_node(line_t * lp)137 add_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 */
150 int
get_line_node_addr(line_t * lp)151 get_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 */
167 line_t *
get_addressed_line_node(int n)168 get_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 
198 extern int newline_added;
199 
200 #define SCRATCH_TEMPLATE      "/tmp/ed.XXXXXXXXXX"
201 static char sfn[sizeof(SCRATCH_TEMPLATE)+1] = "";	/* scratch file name */
202 
203 /* open_sbuf: open scratch file */
204 int
open_sbuf(void)205 open_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 */
224 int
close_sbuf(void)225 close_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 */
242 void
quit(int n)243 quit(int n)
244 {
245 	if (sfp) {
246 		fclose(sfp);
247 		unlink(sfn);
248 	}
249 	exit(n);
250 }
251 
252 
253 static unsigned char ctab[256];		/* character translation table */
254 
255 /* init_buffers: open scratch buffer; initialize line queue */
256 void
init_buffers(void)257 init_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 */
277 char *
translit_text(char * s,int len,int from,int to)278 translit_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