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