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 |
34 | static 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 | |
50 | FILE *sfp; /* scratch file pointer */ |
51 | off_t sfseek; /* scratch file position */ |
52 | int seek_write; /* seek before writing */ |
53 | line_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 */ |
57 | char * |
58 | get_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 */ |
92 | char * |
93 | put_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 */ |
141 | void |
142 | add_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 */ |
154 | long |
155 | get_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 */ |
171 | line_t * |
172 | get_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 | |
202 | char *sfn = NULL; /* scratch file name */ |
203 | |
204 | /* open_sbuf: open scratch file */ |
205 | int |
206 | open_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 */ |
245 | int |
246 | close_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 */ |
267 | void |
268 | quit(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 | |
283 | unsigned char ctab[256]; /* character translation table */ |
284 | |
285 | /* init_buffers: open scratch buffer; initialize line queue */ |
286 | void |
287 | init_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 */ |
307 | char * |
308 | translit_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 | |