1 /* $NetBSD: buf.c,v 1.26 2006/03/17 14:37:14 rumble 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.26 2006/03/17 14:37:14 rumble 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 sprintf(errmsg, "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 sprintf(errmsg, "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 sprintf(errmsg, "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 sprintf(errmsg, "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 sprintf(errmsg, "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 sprintf(errmsg, "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 sprintf(errmsg, "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 sprintf(errmsg, "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 sprintf(errmsg, "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 sprintf(errmsg, "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