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