146684Skris/* buf.c: This file contains the scratch-file buffer routines for the
216Salm   ed line editor. */
316Salm/*-
41057Salm * Copyright (c) 1993 Andrew Moore, Talke Studio.
516Salm * All rights reserved.
616Salm *
716Salm * Redistribution and use in source and binary forms, with or without
816Salm * modification, are permitted provided that the following conditions
916Salm * are met:
1016Salm * 1. Redistributions of source code must retain the above copyright
1116Salm *    notice, this list of conditions and the following disclaimer.
1216Salm * 2. Redistributions in binary form must reproduce the above copyright
1316Salm *    notice, this list of conditions and the following disclaimer in the
1416Salm *    documentation and/or other materials provided with the distribution.
1516Salm *
161057Salm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1716Salm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1816Salm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191057Salm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2016Salm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2116Salm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2216Salm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2316Salm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2416Salm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2516Salm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2616Salm * SUCH DAMAGE.
2716Salm */
2827963Ssteve
2999109Sobrien#include <sys/cdefs.h>
3099109Sobrien__FBSDID("$FreeBSD$");
3116Salm
3216Salm#include <sys/file.h>
331297Salm#include <sys/stat.h>
3416Salm
3516Salm#include "ed.h"
3616Salm
3716Salm
38241720Sedstatic FILE *sfp;			/* scratch file pointer */
39241720Sedstatic off_t sfseek;			/* scratch file position */
40241720Sedstatic int seek_write;			/* seek before writing */
41241720Sedstatic line_t buffer_head;		/* incore buffer */
4216Salm
431057Salm/* get_sbuf_line: get a line of text from the scratch file; return pointer
4416Salm   to the text */
4516Salmchar *
4690109Simpget_sbuf_line(line_t *lp)
4716Salm{
481057Salm	static char *sfbuf = NULL;	/* buffer */
491057Salm	static int sfbufsz = 0;		/* buffer size */
501057Salm
5116Salm	int len, ct;
5216Salm
531057Salm	if (lp == &buffer_head)
5416Salm		return NULL;
5516Salm	seek_write = 1;				/* force seek on write */
5616Salm	/* out of position */
5716Salm	if (sfseek != lp->seek) {
5816Salm		sfseek = lp->seek;
5982771Sache		if (fseeko(sfp, sfseek, SEEK_SET) < 0) {
6016Salm			fprintf(stderr, "%s\n", strerror(errno));
6181220Smike			errmsg = "cannot seek temp file";
6216Salm			return NULL;
6316Salm		}
6416Salm	}
651057Salm	len = lp->len;
661057Salm	REALLOC(sfbuf, sfbufsz, len + 1, NULL);
6716Salm	if ((ct = fread(sfbuf, sizeof(char), len, sfp)) <  0 || ct != len) {
6816Salm		fprintf(stderr, "%s\n", strerror(errno));
6981220Smike		errmsg = "cannot read temp file";
7016Salm		return NULL;
7116Salm	}
7216Salm	sfseek += len;				/* update file position */
7316Salm	sfbuf[len] = '\0';
7416Salm	return sfbuf;
7516Salm}
7616Salm
7716Salm
781057Salm/* put_sbuf_line: write a line of text to the scratch file and add a line node
7916Salm   to the editor buffer;  return a pointer to the end of the text */
8081220Smikeconst char *
8190109Simpput_sbuf_line(const char *cs)
8216Salm{
8316Salm	line_t *lp;
8416Salm	int len, ct;
8581220Smike	const char *s;
8616Salm
8716Salm	if ((lp = (line_t *) malloc(sizeof(line_t))) == NULL) {
8816Salm		fprintf(stderr, "%s\n", strerror(errno));
8981220Smike		errmsg = "out of memory";
9016Salm		return NULL;
9116Salm	}
9216Salm	/* assert: cs is '\n' terminated */
9316Salm	for (s = cs; *s != '\n'; s++)
9416Salm		;
9516Salm	if (s - cs >= LINECHARS) {
9681220Smike		errmsg = "line too long";
97225215Sbrueffer		free(lp);
9816Salm		return NULL;
9916Salm	}
1001057Salm	len = s - cs;
10116Salm	/* out of position */
10216Salm	if (seek_write) {
10382771Sache		if (fseeko(sfp, (off_t)0, SEEK_END) < 0) {
10416Salm			fprintf(stderr, "%s\n", strerror(errno));
10581220Smike			errmsg = "cannot seek temp file";
106225215Sbrueffer			free(lp);
10716Salm			return NULL;
10816Salm		}
10982771Sache		sfseek = ftello(sfp);
11016Salm		seek_write = 0;
11116Salm	}
1121057Salm	/* assert: SPL1() */
11316Salm	if ((ct = fwrite(cs, sizeof(char), len, sfp)) < 0 || ct != len) {
11416Salm		sfseek = -1;
11516Salm		fprintf(stderr, "%s\n", strerror(errno));
11681220Smike		errmsg = "cannot write temp file";
117225215Sbrueffer		free(lp);
11816Salm		return NULL;
11916Salm	}
12016Salm	lp->len = len;
12116Salm	lp->seek  = sfseek;
1221057Salm	add_line_node(lp);
12316Salm	sfseek += len;			/* update file position */
12416Salm	return ++s;
12516Salm}
12616Salm
12716Salm
1281057Salm/* add_line_node: add a line node in the editor buffer after the current line */
12916Salmvoid
13090109Simpadd_line_node(line_t *lp)
13116Salm{
13216Salm	line_t *cp;
13316Salm
1341057Salm	cp = get_addressed_line_node(current_addr);				/* this get_addressed_line_node last! */
1351297Salm	INSQUE(lp, cp);
1361057Salm	addr_last++;
1371057Salm	current_addr++;
13816Salm}
13916Salm
14016Salm
1411057Salm/* get_line_node_addr: return line number of pointer */
14216Salmlong
14390109Simpget_line_node_addr(line_t *lp)
14416Salm{
1451057Salm	line_t *cp = &buffer_head;
14616Salm	long n = 0;
14716Salm
1481057Salm	while (cp != lp && (cp = cp->q_forw) != &buffer_head)
14916Salm		n++;
1501057Salm	if (n && cp == &buffer_head) {
15181220Smike		errmsg = "invalid address";
15249Salm		return ERR;
15349Salm	 }
15449Salm	 return n;
15516Salm}
15616Salm
15716Salm
1581057Salm/* get_addressed_line_node: return pointer to a line node in the editor buffer */
15916Salmline_t *
16090109Simpget_addressed_line_node(long n)
16116Salm{
1621057Salm	static line_t *lp = &buffer_head;
16316Salm	static long on = 0;
16416Salm
1651057Salm	SPL1();
16616Salm	if (n > on)
1671057Salm		if (n <= (on + addr_last) >> 1)
16816Salm			for (; on < n; on++)
1691057Salm				lp = lp->q_forw;
17016Salm		else {
1711057Salm			lp = buffer_head.q_back;
1721057Salm			for (on = addr_last; on > n; on--)
1731057Salm				lp = lp->q_back;
17416Salm		}
17516Salm	else
17616Salm		if (n >= on >> 1)
17716Salm			for (; on > n; on--)
1781057Salm				lp = lp->q_back;
17916Salm		else {
1801057Salm			lp = &buffer_head;
18116Salm			for (on = 0; on < n; on++)
1821057Salm				lp = lp->q_forw;
18316Salm		}
1841057Salm	SPL0();
18516Salm	return lp;
18616Salm}
18716Salm
188241720Sedstatic char sfn[15] = "";			/* scratch file name */
18916Salm
1901057Salm/* open_sbuf: open scratch file */
1911057Salmint
19290109Simpopen_sbuf(void)
19316Salm{
19498481Sjmallett	int fd;
1951297Salm	int u;
1961297Salm
1971057Salm	isbinary = newline_added = 0;
1981297Salm	u = umask(077);
19916Salm	strcpy(sfn, "/tmp/ed.XXXXXX");
20030247Seivind	if ((fd = mkstemp(sfn)) == -1 ||
20124181Simp	    (sfp = fdopen(fd, "w+")) == NULL) {
20224181Simp		if (fd != -1)
20324181Simp			close(fd);
20424181Simp		perror(sfn);
20581220Smike		errmsg = "cannot open temp file";
2061297Salm		umask(u);
20716Salm		return ERR;
20816Salm	}
2091297Salm	umask(u);
21016Salm	return 0;
21116Salm}
21216Salm
21316Salm
2141057Salm/* close_sbuf: close scratch file */
2151057Salmint
21690109Simpclose_sbuf(void)
21716Salm{
21816Salm	if (sfp) {
21916Salm		if (fclose(sfp) < 0) {
22016Salm			fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
22181220Smike			errmsg = "cannot close temp file";
22216Salm			return ERR;
22316Salm		}
22416Salm		sfp = NULL;
22516Salm		unlink(sfn);
22616Salm	}
22716Salm	sfseek = seek_write = 0;
22816Salm	return 0;
22916Salm}
23016Salm
23116Salm
2321057Salm/* quit: remove_lines scratch file and exit */
23316Salmvoid
23490109Simpquit(int n)
23516Salm{
23616Salm	if (sfp) {
23716Salm		fclose(sfp);
23816Salm		unlink(sfn);
23916Salm	}
24016Salm	exit(n);
24116Salm}
24287Salm
24387Salm
244241720Sedstatic unsigned char ctab[256];		/* character translation table */
24587Salm
2461057Salm/* init_buffers: open scratch buffer; initialize line queue */
24787Salmvoid
24890109Simpinit_buffers(void)
24987Salm{
25087Salm	int i = 0;
25187Salm
2528855Srgrimes	/* Read stdin one character at a time to avoid i/o contention
2531057Salm	   with shell escapes invoked by nonterminal input, e.g.,
2541057Salm	   ed - <<EOF
2551057Salm	   !cat
2561057Salm	   hello, world
2571057Salm	   EOF */
2581057Salm	setbuffer(stdin, stdinbuf, 1);
25932138Shelbig
26032138Shelbig	/* Ensure stdout is line buffered. This avoids bogus delays
26132138Shelbig	   of output if stdout is piped through utilities to a terminal. */
26232138Shelbig	setvbuf(stdout, NULL, _IOLBF, 0);
2631057Salm	if (open_sbuf() < 0)
26487Salm		quit(2);
2651057Salm	REQUE(&buffer_head, &buffer_head);
26687Salm	for (i = 0; i < 256; i++)
26787Salm		ctab[i] = i;
26887Salm}
26987Salm
27087Salm
2711057Salm/* translit_text: translate characters in a string */
27287Salmchar *
27390109Simptranslit_text(char *s, int len, int from, int to)
27487Salm{
27587Salm	static int i = 0;
27687Salm
27787Salm	unsigned char *us;
27887Salm
27987Salm	ctab[i] = i;			/* restore table to initial state */
28087Salm	ctab[i = from] = to;
28187Salm	for (us = (unsigned char *) s; len-- > 0; us++)
28287Salm		*us = ctab[*us];
28387Salm	return s;
28487Salm}
285