ed(1): Sync with FreeBSD.
[dragonfly.git] / bin / ed / buf.c
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  * @(#)buf.c,v 1.4 1994/02/01 00:34:35 alm Exp
29  * $FreeBSD: head/bin/ed/buf.c 241737 2012-10-19 14:49:42Z ed $
30  */
31
32 #include <sys/file.h>
33 #include <sys/stat.h>
34
35 #include "ed.h"
36
37
38 static FILE *sfp;                       /* scratch file pointer */
39 static off_t sfseek;                    /* scratch file position */
40 static int seek_write;                  /* seek before writing */
41 static line_t buffer_head;              /* incore buffer */
42
43 /* get_sbuf_line: get a line of text from the scratch file; return pointer
44    to the text */
45 char *
46 get_sbuf_line(line_t *lp)
47 {
48         static char *sfbuf = NULL;      /* buffer */
49         static int sfbufsz = 0;         /* buffer size */
50
51         int len, ct;
52
53         if (lp == &buffer_head)
54                 return NULL;
55         seek_write = 1;                         /* force seek on write */
56         /* out of position */
57         if (sfseek != lp->seek) {
58                 sfseek = lp->seek;
59                 if (fseeko(sfp, sfseek, SEEK_SET) < 0) {
60                         fprintf(stderr, "%s\n", strerror(errno));
61                         errmsg = "cannot seek temp file";
62                         return NULL;
63                 }
64         }
65         len = lp->len;
66         REALLOC(sfbuf, sfbufsz, len + 1, NULL);
67         if ((ct = fread(sfbuf, sizeof(char), len, sfp)) <  0 || ct != len) {
68                 fprintf(stderr, "%s\n", strerror(errno));
69                 errmsg = "cannot read temp file";
70                 return NULL;
71         }
72         sfseek += len;                          /* update file position */
73         sfbuf[len] = '\0';
74         return sfbuf;
75 }
76
77
78 /* put_sbuf_line: write a line of text to the scratch file and add a line node
79    to the editor buffer;  return a pointer to the end of the text */
80 const char *
81 put_sbuf_line(const char *cs)
82 {
83         line_t *lp;
84         int len, ct;
85         const char *s;
86
87         if ((lp = (line_t *) malloc(sizeof(line_t))) == NULL) {
88                 fprintf(stderr, "%s\n", strerror(errno));
89                 errmsg = "out of memory";
90                 return NULL;
91         }
92         /* assert: cs is '\n' terminated */
93         for (s = cs; *s != '\n'; s++)
94                 ;
95         if (s - cs >= LINECHARS) {
96                 errmsg = "line too long";
97                 free(lp);
98                 return NULL;
99         }
100         len = s - cs;
101         /* out of position */
102         if (seek_write) {
103                 if (fseeko(sfp, (off_t)0, SEEK_END) < 0) {
104                         fprintf(stderr, "%s\n", strerror(errno));
105                         errmsg = "cannot seek temp file";
106                         free(lp);
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                 free(lp);
118                 return NULL;
119         }
120         lp->len = len;
121         lp->seek  = sfseek;
122         add_line_node(lp);
123         sfseek += len;                  /* update file position */
124         return ++s;
125 }
126
127
128 /* add_line_node: add a line node in the editor buffer after the current line */
129 void
130 add_line_node(line_t *lp)
131 {
132         line_t *cp;
133
134         cp = get_addressed_line_node(current_addr);                             /* this get_addressed_line_node last! */
135         INSQUE(lp, cp);
136         addr_last++;
137         current_addr++;
138 }
139
140
141 /* get_line_node_addr: return line number of pointer */
142 long
143 get_line_node_addr(line_t *lp)
144 {
145         line_t *cp = &buffer_head;
146         long n = 0;
147
148         while (cp != lp && (cp = cp->q_forw) != &buffer_head)
149                 n++;
150         if (n && cp == &buffer_head) {
151                 errmsg = "invalid address";
152                 return ERR;
153          }
154          return n;
155 }
156
157
158 /* get_addressed_line_node: return pointer to a line node in the editor buffer */
159 line_t *
160 get_addressed_line_node(long n)
161 {
162         static line_t *lp = &buffer_head;
163         static long on = 0;
164
165         SPL1();
166         if (n > on)
167                 if (n <= (on + addr_last) >> 1)
168                         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
188 static char sfn[15] = "";                       /* scratch file name */
189
190 /* open_sbuf: open scratch file */
191 int
192 open_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 */
215 int
216 close_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 */
233 void
234 quit(int n)
235 {
236         if (sfp) {
237                 fclose(sfp);
238                 unlink(sfn);
239         }
240         exit(n);
241 }
242
243
244 static unsigned char ctab[256];         /* character translation table */
245
246 /* init_buffers: open scratch buffer; initialize line queue */
247 void
248 init_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 */
272 char *
273 translit_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 }