1218885Sdim/* buf.c: This file contains the scratch-file buffer routines for the
2218885Sdim   ed line editor. */
3218885Sdim/*-
4218885Sdim * Copyright (c) 1993 Andrew Moore, Talke Studio.
5218885Sdim * All rights reserved.
6218885Sdim *
7218885Sdim * Redistribution and use in source and binary forms, with or without
8218885Sdim * modification, are permitted provided that the following conditions
9218885Sdim * are met:
10218885Sdim * 1. Redistributions of source code must retain the above copyright
11218885Sdim *    notice, this list of conditions and the following disclaimer.
12218885Sdim * 2. Redistributions in binary form must reproduce the above copyright
13218885Sdim *    notice, this list of conditions and the following disclaimer in the
14218885Sdim *    documentation and/or other materials provided with the distribution.
15218885Sdim *
16218885Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17218885Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18218885Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19218885Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20218885Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21218885Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22226633Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23226633Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24226633Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25218885Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26218885Sdim * SUCH DAMAGE.
27263508Sdim */
28263508Sdim
29218885Sdim#include <sys/cdefs.h>
30263508Sdim__FBSDID("$FreeBSD$");
31263508Sdim
32218885Sdim#include <sys/file.h>
33234353Sdim#include <sys/stat.h>
34218885Sdim
35218885Sdim#include "ed.h"
36263508Sdim
37218885Sdim
38218885Sdimstatic FILE *sfp;			/* scratch file pointer */
39218885Sdimstatic off_t sfseek;			/* scratch file position */
40218885Sdimstatic int seek_write;			/* seek before writing */
41218885Sdimstatic line_t buffer_head;		/* incore buffer */
42263508Sdim
43263508Sdim/* get_sbuf_line: get a line of text from the scratch file; return pointer
44263508Sdim   to the text */
45263508Sdimchar *
46263508Sdimget_sbuf_line(line_t *lp)
47263508Sdim{
48263508Sdim	static char *sfbuf = NULL;	/* buffer */
49263508Sdim	static int sfbufsz = 0;		/* buffer size */
50218885Sdim
51263508Sdim	int len, ct;
52218885Sdim
53218885Sdim	if (lp == &buffer_head)
54234353Sdim		return NULL;
55234353Sdim	seek_write = 1;				/* force seek on write */
56234353Sdim	/* out of position */
57234353Sdim	if (sfseek != lp->seek) {
58218885Sdim		sfseek = lp->seek;
59234353Sdim		if (fseeko(sfp, sfseek, SEEK_SET) < 0) {
60234353Sdim			fprintf(stderr, "%s\n", strerror(errno));
61218885Sdim			errmsg = "cannot seek temp file";
62234353Sdim			return NULL;
63234353Sdim		}
64218885Sdim	}
65234353Sdim	len = lp->len;
66234353Sdim	REALLOC(sfbuf, sfbufsz, len + 1, NULL);
67234353Sdim	if ((ct = fread(sfbuf, sizeof(char), len, sfp)) <  0 || ct != len) {
68234353Sdim		fprintf(stderr, "%s\n", strerror(errno));
69234353Sdim		errmsg = "cannot read temp file";
70234353Sdim		return NULL;
71218885Sdim	}
72218885Sdim	sfseek += len;				/* update file position */
73234353Sdim	sfbuf[len] = '\0';
74234353Sdim	return sfbuf;
75234353Sdim}
76234353Sdim
77218885Sdim
78218885Sdim/* put_sbuf_line: write a line of text to the scratch file and add a line node
79234353Sdim   to the editor buffer;  return a pointer to the end of the text */
80234353Sdimconst char *
81234353Sdimput_sbuf_line(const char *cs)
82234353Sdim{
83218885Sdim	line_t *lp;
84218885Sdim	int len, ct;
85234353Sdim	const char *s;
86234353Sdim
87263508Sdim	if ((lp = (line_t *) malloc(sizeof(line_t))) == NULL) {
88234353Sdim		fprintf(stderr, "%s\n", strerror(errno));
89234353Sdim		errmsg = "out of memory";
90234353Sdim		return NULL;
91234353Sdim	}
92234353Sdim	/* assert: cs is '\n' terminated */
93234353Sdim	for (s = cs; *s != '\n'; s++)
94218885Sdim		;
95218885Sdim	if (s - cs >= LINECHARS) {
96234353Sdim		errmsg = "line too long";
97234353Sdim		free(lp);
98218885Sdim		return NULL;
99234353Sdim	}
100234353Sdim	len = s - cs;
101234353Sdim	/* out of position */
102218885Sdim	if (seek_write) {
103234353Sdim		if (fseeko(sfp, (off_t)0, SEEK_END) < 0) {
104234353Sdim			fprintf(stderr, "%s\n", strerror(errno));
105218885Sdim			errmsg = "cannot seek temp file";
106218885Sdim			free(lp);
107234353Sdim			return NULL;
108234353Sdim		}
109218885Sdim		sfseek = ftello(sfp);
110234353Sdim		seek_write = 0;
111218885Sdim	}
112234353Sdim	/* assert: SPL1() */
113234353Sdim	if ((ct = fwrite(cs, sizeof(char), len, sfp)) < 0 || ct != len) {
114234353Sdim		sfseek = -1;
115234353Sdim		fprintf(stderr, "%s\n", strerror(errno));
116234353Sdim		errmsg = "cannot write temp file";
117218885Sdim		free(lp);
118234353Sdim		return NULL;
119234353Sdim	}
120234353Sdim	lp->len = len;
121234353Sdim	lp->seek  = sfseek;
122234353Sdim	add_line_node(lp);
123218885Sdim	sfseek += len;			/* update file position */
124218885Sdim	return ++s;
125234353Sdim}
126234353Sdim
127234353Sdim
128218885Sdim/* add_line_node: add a line node in the editor buffer after the current line */
129234353Sdimvoid
130234353Sdimadd_line_node(line_t *lp)
131218885Sdim{
132234353Sdim	line_t *cp;
133218885Sdim
134234353Sdim	cp = get_addressed_line_node(current_addr);				/* this get_addressed_line_node last! */
135234353Sdim	INSQUE(lp, cp);
136234353Sdim	addr_last++;
137218885Sdim	current_addr++;
138218885Sdim}
139218885Sdim
140234353Sdim
141218885Sdim/* get_line_node_addr: return line number of pointer */
142234353Sdimlong
143234353Sdimget_line_node_addr(line_t *lp)
144234353Sdim{
145234353Sdim	line_t *cp = &buffer_head;
146234353Sdim	long n = 0;
147234353Sdim
148218885Sdim	while (cp != lp && (cp = cp->q_forw) != &buffer_head)
149218885Sdim		n++;
150218885Sdim	if (n && cp == &buffer_head) {
151218885Sdim		errmsg = "invalid address";
152218885Sdim		return ERR;
153218885Sdim	 }
154218885Sdim	 return n;
155218885Sdim}
156218885Sdim
157218885Sdim
158218885Sdim/* get_addressed_line_node: return pointer to a line node in the editor buffer */
159263508Sdimline_t *
160263508Sdimget_addressed_line_node(long n)
161263508Sdim{
162263508Sdim	static line_t *lp = &buffer_head;
163263508Sdim	static long on = 0;
164263508Sdim
165263508Sdim	SPL1();
166263508Sdim	if (n > on)
167263508Sdim		if (n <= (on + addr_last) >> 1)
168218885Sdim			for (; on < n; on++)
169				lp = lp->q_forw;
170		else {
171			lp = buffer_head.q_back;
172			for (on = addr_last; on > n; on--)
173				lp = lp->q_back;
174		}
175	else
176		if (n >= on >> 1)
177			for (; on > n; on--)
178				lp = lp->q_back;
179		else {
180			lp = &buffer_head;
181			for (on = 0; on < n; on++)
182				lp = lp->q_forw;
183		}
184	SPL0();
185	return lp;
186}
187
188static char sfn[15] = "";			/* scratch file name */
189
190/* open_sbuf: open scratch file */
191int
192open_sbuf(void)
193{
194	int fd;
195	int u;
196
197	isbinary = newline_added = 0;
198	u = umask(077);
199	strcpy(sfn, "/tmp/ed.XXXXXX");
200	if ((fd = mkstemp(sfn)) == -1 ||
201	    (sfp = fdopen(fd, "w+")) == NULL) {
202		if (fd != -1)
203			close(fd);
204		perror(sfn);
205		errmsg = "cannot open temp file";
206		umask(u);
207		return ERR;
208	}
209	umask(u);
210	return 0;
211}
212
213
214/* close_sbuf: close scratch file */
215int
216close_sbuf(void)
217{
218	if (sfp) {
219		if (fclose(sfp) < 0) {
220			fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
221			errmsg = "cannot close temp file";
222			return ERR;
223		}
224		sfp = NULL;
225		unlink(sfn);
226	}
227	sfseek = seek_write = 0;
228	return 0;
229}
230
231
232/* quit: remove_lines scratch file and exit */
233void
234quit(int n)
235{
236	if (sfp) {
237		fclose(sfp);
238		unlink(sfn);
239	}
240	exit(n);
241}
242
243
244static unsigned char ctab[256];		/* character translation table */
245
246/* init_buffers: open scratch buffer; initialize line queue */
247void
248init_buffers(void)
249{
250	int i = 0;
251
252	/* Read stdin one character at a time to avoid i/o contention
253	   with shell escapes invoked by nonterminal input, e.g.,
254	   ed - <<EOF
255	   !cat
256	   hello, world
257	   EOF */
258	setbuffer(stdin, stdinbuf, 1);
259
260	/* Ensure stdout is line buffered. This avoids bogus delays
261	   of output if stdout is piped through utilities to a terminal. */
262	setvbuf(stdout, NULL, _IOLBF, 0);
263	if (open_sbuf() < 0)
264		quit(2);
265	REQUE(&buffer_head, &buffer_head);
266	for (i = 0; i < 256; i++)
267		ctab[i] = i;
268}
269
270
271/* translit_text: translate characters in a string */
272char *
273translit_text(char *s, int len, int from, int to)
274{
275	static int i = 0;
276
277	unsigned char *us;
278
279	ctab[i] = i;			/* restore table to initial state */
280	ctab[i = from] = to;
281	for (us = (unsigned char *) s; len-- > 0; us++)
282		*us = ctab[*us];
283	return s;
284}
285