| 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 |  |