buf.c revision 98481
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#ifndef lint
30static const char rcsid[] =
31  "$FreeBSD: head/bin/ed/buf.c 98481 2002-06-20 07:15:55Z jmallett $";
32#endif /* not lint */
33
34#include <sys/file.h>
35#include <sys/stat.h>
36
37#include "ed.h"
38
39
40FILE *sfp;				/* scratch file pointer */
41off_t sfseek;				/* scratch file position */
42int seek_write;				/* seek before writing */
43line_t buffer_head;			/* incore buffer */
44
45/* get_sbuf_line: get a line of text from the scratch file; return pointer
46   to the text */
47char *
48get_sbuf_line(line_t *lp)
49{
50	static char *sfbuf = NULL;	/* buffer */
51	static int sfbufsz = 0;		/* buffer size */
52
53	int len, ct;
54
55	if (lp == &buffer_head)
56		return NULL;
57	seek_write = 1;				/* force seek on write */
58	/* out of position */
59	if (sfseek != lp->seek) {
60		sfseek = lp->seek;
61		if (fseeko(sfp, sfseek, SEEK_SET) < 0) {
62			fprintf(stderr, "%s\n", strerror(errno));
63			errmsg = "cannot seek temp file";
64			return NULL;
65		}
66	}
67	len = lp->len;
68	REALLOC(sfbuf, sfbufsz, len + 1, NULL);
69	if ((ct = fread(sfbuf, sizeof(char), len, sfp)) <  0 || ct != len) {
70		fprintf(stderr, "%s\n", strerror(errno));
71		errmsg = "cannot read temp file";
72		return NULL;
73	}
74	sfseek += len;				/* update file position */
75	sfbuf[len] = '\0';
76	return sfbuf;
77}
78
79
80/* put_sbuf_line: write a line of text to the scratch file and add a line node
81   to the editor buffer;  return a pointer to the end of the text */
82const char *
83put_sbuf_line(const char *cs)
84{
85	line_t *lp;
86	int len, ct;
87	const char *s;
88
89	if ((lp = (line_t *) malloc(sizeof(line_t))) == NULL) {
90		fprintf(stderr, "%s\n", strerror(errno));
91		errmsg = "out of memory";
92		return NULL;
93	}
94	/* assert: cs is '\n' terminated */
95	for (s = cs; *s != '\n'; s++)
96		;
97	if (s - cs >= LINECHARS) {
98		errmsg = "line too long";
99		return NULL;
100	}
101	len = s - cs;
102	/* out of position */
103	if (seek_write) {
104		if (fseeko(sfp, (off_t)0, SEEK_END) < 0) {
105			fprintf(stderr, "%s\n", strerror(errno));
106			errmsg = "cannot seek temp file";
107			return NULL;
108		}
109		sfseek = ftello(sfp);
110		seek_write = 0;
111	}
112	/* assert: SPL1() */
113	if ((ct = fwrite(cs, sizeof(char), len, sfp)) < 0 || ct != len) {
114		sfseek = -1;
115		fprintf(stderr, "%s\n", strerror(errno));
116		errmsg = "cannot write temp file";
117		return NULL;
118	}
119	lp->len = len;
120	lp->seek  = sfseek;
121	add_line_node(lp);
122	sfseek += len;			/* update file position */
123	return ++s;
124}
125
126
127/* add_line_node: add a line node in the editor buffer after the current line */
128void
129add_line_node(line_t *lp)
130{
131	line_t *cp;
132
133	cp = get_addressed_line_node(current_addr);				/* this get_addressed_line_node last! */
134	INSQUE(lp, cp);
135	addr_last++;
136	current_addr++;
137}
138
139
140/* get_line_node_addr: return line number of pointer */
141long
142get_line_node_addr(line_t *lp)
143{
144	line_t *cp = &buffer_head;
145	long n = 0;
146
147	while (cp != lp && (cp = cp->q_forw) != &buffer_head)
148		n++;
149	if (n && cp == &buffer_head) {
150		errmsg = "invalid address";
151		return ERR;
152	 }
153	 return n;
154}
155
156
157/* get_addressed_line_node: return pointer to a line node in the editor buffer */
158line_t *
159get_addressed_line_node(long n)
160{
161	static line_t *lp = &buffer_head;
162	static long on = 0;
163
164	SPL1();
165	if (n > on)
166		if (n <= (on + addr_last) >> 1)
167			for (; on < n; on++)
168				lp = lp->q_forw;
169		else {
170			lp = buffer_head.q_back;
171			for (on = addr_last; on > n; on--)
172				lp = lp->q_back;
173		}
174	else
175		if (n >= on >> 1)
176			for (; on > n; on--)
177				lp = lp->q_back;
178		else {
179			lp = &buffer_head;
180			for (on = 0; on < n; on++)
181				lp = lp->q_forw;
182		}
183	SPL0();
184	return lp;
185}
186
187
188extern int newline_added;
189
190char sfn[15] = "";				/* scratch file name */
191
192/* open_sbuf: open scratch file */
193int
194open_sbuf(void)
195{
196	int fd;
197	int u;
198
199	isbinary = newline_added = 0;
200	u = umask(077);
201	strcpy(sfn, "/tmp/ed.XXXXXX");
202	if ((fd = mkstemp(sfn)) == -1 ||
203	    (sfp = fdopen(fd, "w+")) == NULL) {
204		if (fd != -1)
205			close(fd);
206		perror(sfn);
207		errmsg = "cannot open temp file";
208		umask(u);
209		return ERR;
210	}
211	umask(u);
212	return 0;
213}
214
215
216/* close_sbuf: close scratch file */
217int
218close_sbuf(void)
219{
220	if (sfp) {
221		if (fclose(sfp) < 0) {
222			fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
223			errmsg = "cannot close temp file";
224			return ERR;
225		}
226		sfp = NULL;
227		unlink(sfn);
228	}
229	sfseek = seek_write = 0;
230	return 0;
231}
232
233
234/* quit: remove_lines scratch file and exit */
235void
236quit(int n)
237{
238	if (sfp) {
239		fclose(sfp);
240		unlink(sfn);
241	}
242	exit(n);
243}
244
245
246unsigned char ctab[256];		/* character translation table */
247
248/* init_buffers: open scratch buffer; initialize line queue */
249void
250init_buffers(void)
251{
252	int i = 0;
253
254	/* Read stdin one character at a time to avoid i/o contention
255	   with shell escapes invoked by nonterminal input, e.g.,
256	   ed - <<EOF
257	   !cat
258	   hello, world
259	   EOF */
260	setbuffer(stdin, stdinbuf, 1);
261
262	/* Ensure stdout is line buffered. This avoids bogus delays
263	   of output if stdout is piped through utilities to a terminal. */
264	setvbuf(stdout, NULL, _IOLBF, 0);
265	if (open_sbuf() < 0)
266		quit(2);
267	REQUE(&buffer_head, &buffer_head);
268	for (i = 0; i < 256; i++)
269		ctab[i] = i;
270}
271
272
273/* translit_text: translate characters in a string */
274char *
275translit_text(char *s, int len, int from, int to)
276{
277	static int i = 0;
278
279	unsigned char *us;
280
281	ctab[i] = i;			/* restore table to initial state */
282	ctab[i = from] = to;
283	for (us = (unsigned char *) s; len-- > 0; us++)
284		*us = ctab[*us];
285	return s;
286}
287