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
36static FILE *sfp;			/* scratch file pointer */
37static off_t sfseek;			/* scratch file position */
38static int seek_write;			/* seek before writing */
39static 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 */
43char *
44get_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 */
78const char *
79put_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 */
127void
128add_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 */
140long
141get_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 */
157line_t *
158get_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
186static char sfn[15] = "";			/* scratch file name */
187
188/* open_sbuf: open scratch file */
189int
190open_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 */
213int
214close_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 */
231void
232quit(int n)
233{
234	if (sfp) {
235		fclose(sfp);
236		unlink(sfn);
237	}
238	exit(n);
239}
240
241
242static unsigned char ctab[256];		/* character translation table */
243
244/* init_buffers: open scratch buffer; initialize line queue */
245void
246init_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 */
270char *
271translit_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