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