xref: /original-bsd/usr.bin/ex/ex_temp.c (revision 506128eb)
1 /*-
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)ex_temp.c	8.1.1.1 (Berkeley) 08/19/93";
10 #endif /* not lint */
11 
12 #include "ex.h"
13 #include "ex_temp.h"
14 #include "ex_vis.h"
15 #include "ex_tty.h"
16 #include "pathnames.h"
17 
18 /*
19  * Editor temporary file routines.
20  * Very similar to those of ed, except uses 2 input buffers.
21  */
22 #define	READ	0
23 #define	WRITE	1
24 
25 #ifndef vms
26 #define	EPOSITION	7
27 #else
28 #define	EPOSITION	13
29 #endif
30 
31 char	tfname[40];
32 char	rfname[40];
33 int	havetmp;
34 short	tfile = -1;
35 short	rfile = -1;
36 
fileinit()37 fileinit()
38 {
39 	register char *p;
40 	register int i, j;
41 	struct stat stbuf;
42 
43 	if (tline == INCRMT * (HBLKS+2))
44 		return;
45 	cleanup(0);
46 	if (tfile >= 0)
47 		close(tfile);
48 	tline = INCRMT * (HBLKS+2);
49 	blocks[0] = HBLKS;
50 	blocks[1] = HBLKS+1;
51 	blocks[2] = -1;
52 	dirtcnt = 0;
53 	iblock = -1;
54 	iblock2 = -1;
55 	oblock = -1;
56 	CP(tfname, svalue(DIRECTORY));
57 #ifndef vms
58 	if (stat(tfname, &stbuf))
59 #else
60 	goto vms_no_check_dir;
61 #endif
62 	{
63 dumbness:
64 		if (setexit() == 0)
65 			filioerr(tfname);
66 		else
67 			putNFL();
68 		cleanup(1);
69 		ex_exit(1);
70 	}
71 #ifndef	vms
72 	if ((stbuf.st_mode & S_IFMT) != S_IFDIR) {
73 		errno = ENOTDIR;
74 		goto dumbness;
75 	}
76 #else
77 vms_no_check_dir:
78 #endif
79 	ichanged = 0;
80 	ichang2 = 0;
81 #ifndef	vms
82 	ignore(strcat(tfname, "/ExXXXXX"));
83 #else
84 	ignore(strcat(tfname, "ExXXXXX"));
85 #endif
86 	for (p = strend(tfname), i = 5, j = getpid(); i > 0; i--, j /= 10)
87 		*--p = j % 10 | '0';
88 #ifdef vms
89 	ignore(strcat(tfname, ".txt.1"));
90 	unlink(tfname);
91 #endif
92 	tfile = creat(tfname, 0600);
93 	if (tfile < 0)
94 		goto dumbness;
95 #ifdef VMUNIX
96 	{
97 		extern stilinc;		/* see below */
98 		stilinc = 0;
99 	}
100 #endif
101 	havetmp = 1;
102 	if (tfile >= 0)
103 		close(tfile);
104 	tfile = open(tfname, 2);
105 	if (tfile < 0)
106 		goto dumbness;
107 #ifdef UNIX_SBRK
108 /* 	brk((char *)fendcore); */
109 #endif
110 }
111 
cleanup(all)112 cleanup(all)
113 	bool all;
114 {
115 	if (all) {
116 		putpad(TE);
117 		flush();
118 	}
119 	if (havetmp) {
120 		if (tfile >= 0)
121 			close(tfile);
122 		unlink(tfname);
123 	}
124 	havetmp = 0;
125 	if (all && rfile >= 0) {
126 		if (rfile >= 0)
127 			close(rfile);
128 		unlink(rfname);
129 		rfile = -1;
130 	}
131 }
132 
getline(tl)133 getline(tl)
134 	line tl;
135 {
136 	register char *bp, *lp;
137 	register int nl;
138 
139 	lp = linebuf;
140 	bp = getblock(tl, READ);
141 	nl = nleft;
142 	tl &= ~OFFMSK;
143 	while (*lp++ = *bp++)
144 		if (--nl == 0) {
145 			bp = getblock(tl += INCRMT, READ);
146 			nl = nleft;
147 		}
148 }
149 
putline()150 putline()
151 {
152 	register char *bp, *lp;
153 	register int nl;
154 	line tl;
155 
156 	dirtcnt++;
157 	lp = linebuf;
158 	change();
159 	tl = tline;
160 	bp = getblock(tl, WRITE);
161 	nl = nleft;
162 	tl &= ~OFFMSK;
163 	while (*bp = *lp++) {
164 		if (*bp++ == '\n') {
165 			*--bp = 0;
166 			linebp = lp;
167 			break;
168 		}
169 		if (--nl == 0) {
170 			bp = getblock(tl += INCRMT, WRITE);
171 			nl = nleft;
172 		}
173 	}
174 	tl = tline;
175 	tline += (((lp - linebuf) + BNDRY - 1) >> SHFT) & 077776;
176 	return (tl);
177 }
178 
179 int	read();
180 int	write();
181 
182 char *
getblock(atl,iof)183 getblock(atl, iof)
184 	line atl;
185 	int iof;
186 {
187 	register int bno, off;
188 
189 	bno = (atl >> OFFBTS) & BLKMSK;
190 	off = (atl << SHFT) & LBTMSK;
191 	if (bno >= NMBLKS)
192 		error(" Tmp file too large");
193 	nleft = BUFSIZ - off;
194 	if (bno == iblock) {
195 		ichanged |= iof;
196 		hitin2 = 0;
197 		return (ibuff + off);
198 	}
199 	if (bno == iblock2) {
200 		ichang2 |= iof;
201 		hitin2 = 1;
202 		return (ibuff2 + off);
203 	}
204 	if (bno == oblock)
205 		return (obuff + off);
206 	if (iof == READ) {
207 		if (hitin2 == 0) {
208 			if (ichang2) {
209 				blkio(iblock2, ibuff2, write);
210 			}
211 			ichang2 = 0;
212 			iblock2 = bno;
213 			blkio(bno, ibuff2, read);
214 			hitin2 = 1;
215 			return (ibuff2 + off);
216 		}
217 		hitin2 = 0;
218 		if (ichanged) {
219 			blkio(iblock, ibuff, write);
220 		}
221 		ichanged = 0;
222 		iblock = bno;
223 		blkio(bno, ibuff, read);
224 		return (ibuff + off);
225 	}
226 	if (oblock >= 0) {
227 			blkio(oblock, obuff, write);
228 	}
229 	oblock = bno;
230 	return (obuff + off);
231 }
232 
233 #ifdef	VMUNIX
234 #ifdef	vms
235 #define	INCORB	32
236 #else
237 #define	INCORB	64
238 #endif
239 char	incorb[INCORB+1][BUFSIZ];
240 #define	pagrnd(a)	((char *)(((int)a)&~(BUFSIZ-1)))
241 int	stilinc;	/* up to here not written yet */
242 #endif
243 
blkio(b,buf,iofcn)244 blkio(b, buf, iofcn)
245 	short b;
246 	char *buf;
247 	int (*iofcn)();
248 {
249 
250 #ifdef VMUNIX
251 	if (b < INCORB) {
252 		if (iofcn == read) {
253 			bcopy(pagrnd(incorb[b+1]), buf, BUFSIZ);
254 			return;
255 		}
256 		bcopy(buf, pagrnd(incorb[b+1]), BUFSIZ);
257 		if (laste) {
258 			if (b >= stilinc)
259 				stilinc = b + 1;
260 			return;
261 		}
262 	} else if (stilinc)
263 		tflush();
264 #endif
265 	lseek(tfile, (long) (unsigned) b * BUFSIZ, 0);
266 	if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
267 		filioerr(tfname);
268 }
269 
270 #ifdef VMUNIX
tlaste()271 tlaste()
272 {
273 
274 	if (stilinc)
275 		dirtcnt = 0;
276 }
277 
tflush()278 tflush()
279 {
280 	int i = stilinc;
281 
282 	stilinc = 0;
283 	lseek(tfile, (long) 0, 0);
284 	if (write(tfile, pagrnd(incorb[1]), i * BUFSIZ) != (i * BUFSIZ))
285 		filioerr(tfname);
286 }
287 #endif
288 
289 /*
290  * Synchronize the state of the temporary file in case
291  * a crash occurs.
292  */
synctmp()293 synctmp()
294 {
295 	register int cnt;
296 	register line *a;
297 	register short *bp;
298 
299 #ifdef VMUNIX
300 	if (stilinc)
301 		return;
302 #endif
303 	if (dol == zero)
304 		return;
305 	if (ichanged)
306 		blkio(iblock, ibuff, write);
307 	ichanged = 0;
308 	if (ichang2)
309 		blkio(iblock2, ibuff2, write);
310 	ichang2 = 0;
311 	if (oblock != -1)
312 		blkio(oblock, obuff, write);
313 	time(&H.Time);
314 	uid = getuid();
315 	*zero = (line) H.Time;
316 	for (a = zero, bp = blocks; a <= dol; a += BUFSIZ / sizeof *a, bp++) {
317 		if (*bp < 0) {
318 			tline = (tline + OFFMSK) &~ OFFMSK;
319 			*bp = ((tline >> OFFBTS) & BLKMSK);
320 			if (*bp > NMBLKS)
321 				error(" Tmp file too large");
322 			tline += INCRMT;
323 			oblock = *bp + 1;
324 			bp[1] = -1;
325 		}
326 		lseek(tfile, (long) (unsigned) *bp * BUFSIZ, 0);
327 		cnt = ((dol - a) + 2) * sizeof (line);
328 		if (cnt > BUFSIZ)
329 			cnt = BUFSIZ;
330 		if (write(tfile, (char *) a, cnt) != cnt) {
331 oops:
332 			*zero = 0;
333 			filioerr(tfname);
334 		}
335 		*zero = 0;
336 	}
337 	flines = lineDOL();
338 	lseek(tfile, 0l, 0);
339 	if (write(tfile, (char *) &H, sizeof H) != sizeof H)
340 		goto oops;
341 #ifdef notdef
342 	/*
343 	 * This will insure that exrecover gets as much
344 	 * back after a crash as is absolutely possible,
345 	 * but can result in pregnant pauses between commands
346 	 * when the TSYNC call is made, so...
347 	 */
348 #ifndef vms
349 	(void) fsync(tfile);
350 #endif
351 #endif
352 }
353 
TSYNC()354 TSYNC()
355 {
356 
357 	if (dirtcnt > MAXDIRT) {	/* mjm: 12 --> MAXDIRT */
358 #ifdef VMUNIX
359 		if (stilinc)
360 			tflush();
361 #endif
362 		dirtcnt = 0;
363 		synctmp();
364 	}
365 }
366 
367 /*
368  * Named buffer routines.
369  * These are implemented differently than the main buffer.
370  * Each named buffer has a chain of blocks in the register file.
371  * Each block contains roughly 508 chars of text,
372  * and a previous and next block number.  We also have information
373  * about which blocks came from deletes of multiple partial lines,
374  * e.g. deleting a sentence or a LISP object.
375  *
376  * We maintain a free map for the temp file.  To free the blocks
377  * in a register we must read the blocks to find how they are chained
378  * together.
379  *
380  * BUG:		The default savind of deleted lines in numbered
381  *		buffers may be rather inefficient; it hasn't been profiled.
382  */
383 struct	strreg {
384 	short	rg_flags;
385 	short	rg_nleft;
386 	short	rg_first;
387 	short	rg_last;
388 } strregs[('z'-'a'+1) + ('9'-'0'+1)], *strp;
389 
390 struct	rbuf {
391 	short	rb_prev;
392 	short	rb_next;
393 	char	rb_text[BUFSIZ - 2 * sizeof (short)];
394 } *rbuf, KILLrbuf, putrbuf, YANKrbuf, regrbuf;
395 #ifdef VMUNIX
396 short	rused[256];
397 #else
398 short	rused[32];
399 #endif
400 short	rnleft;
401 short	rblock;
402 short	rnext;
403 char	*rbufcp;
404 
regio(b,iofcn)405 regio(b, iofcn)
406 	short b;
407 	int (*iofcn)();
408 {
409 
410 	if (rfile == -1) {
411 		CP(rfname, tfname);
412 		*(strend(rfname) - EPOSITION) = 'R';
413 		rfile = creat(rfname, 0600);
414 		if (rfile < 0)
415 oops:
416 			filioerr(rfname);
417 		else
418 			close(rfile);
419 		rfile = open(rfname, 2);
420 		if (rfile < 0)
421 			goto oops;
422 	}
423 	lseek(rfile, (long) b * BUFSIZ, 0);
424 	if ((*iofcn)(rfile, rbuf, BUFSIZ) != BUFSIZ)
425 		goto oops;
426 	rblock = b;
427 }
428 
REGblk()429 REGblk()
430 {
431 	register int i, j, m;
432 
433 	for (i = 0; i < sizeof rused / sizeof rused[0]; i++) {
434 		m = (rused[i] ^ 0177777) & 0177777;
435 		if (i == 0)
436 			m &= ~1;
437 		if (m != 0) {
438 			j = 0;
439 			while ((m & 1) == 0)
440 				j++, m >>= 1;
441 			rused[i] |= (1 << j);
442 #ifdef RDEBUG
443 			ex_printf("allocating block %d\n", i * 16 + j);
444 #endif
445 			return (i * 16 + j);
446 		}
447 	}
448 	error("Out of register space (ugh)");
449 	/*NOTREACHED*/
450 }
451 
452 struct	strreg *
mapreg(c)453 mapreg(c)
454 	register int c;
455 {
456 
457 	if (isupper(c))
458 		c = tolower(c);
459 	return (isdigit(c) ? &strregs[('z'-'a'+1)+(c-'0')] : &strregs[c-'a']);
460 }
461 
462 int	shread();
463 
KILLreg(c)464 KILLreg(c)
465 	register int c;
466 {
467 	register struct strreg *sp;
468 
469 	rbuf = &KILLrbuf;
470 	sp = mapreg(c);
471 	rblock = sp->rg_first;
472 	sp->rg_first = sp->rg_last = 0;
473 	sp->rg_flags = sp->rg_nleft = 0;
474 	while (rblock != 0) {
475 #ifdef RDEBUG
476 		ex_printf("freeing block %d\n", rblock);
477 #endif
478 		rused[rblock / 16] &= ~(1 << (rblock % 16));
479 		regio(rblock, shread);
480 		rblock = rbuf->rb_next;
481 	}
482 }
483 
484 /*VARARGS*/
shread()485 shread()
486 {
487 	struct front { short a; short b; };
488 
489 	if (read(rfile, (char *) rbuf, sizeof (struct front)) == sizeof (struct front))
490 		return (sizeof (struct rbuf));
491 	return (0);
492 }
493 
494 int	getREG();
495 
putreg(c)496 putreg(c)
497 	char c;
498 {
499 	register line *odot = dot;
500 	register line *odol = dol;
501 	register int cnt;
502 
503 	deletenone();
504 	appendnone();
505 	rbuf = &putrbuf;
506 	rnleft = 0;
507 	rblock = 0;
508 	rnext = mapreg(c)->rg_first;
509 	if (rnext == 0) {
510 		if (inopen) {
511 			splitw++;
512 			vclean();
513 			vgoto(WECHO, 0);
514 		}
515 		vreg = -1;
516 		error("Nothing in register %c", c);
517 	}
518 	if (inopen && partreg(c)) {
519 		if (!FIXUNDO) {
520 			splitw++; vclean(); vgoto(WECHO, 0); vreg = -1;
521 			error("Can't put partial line inside macro");
522 		}
523 		squish();
524 		addr1 = addr2 = dol;
525 	}
526 	cnt = append(getREG, addr2);
527 	if (inopen && partreg(c)) {
528 		unddol = dol;
529 		dol = odol;
530 		dot = odot;
531 		pragged(0);
532 	}
533 	killcnt(cnt);
534 	notecnt = cnt;
535 }
536 
partreg(c)537 partreg(c)
538 	char c;
539 {
540 
541 	return (mapreg(c)->rg_flags);
542 }
543 
notpart(c)544 notpart(c)
545 	register int c;
546 {
547 
548 	if (c)
549 		mapreg(c)->rg_flags = 0;
550 }
551 
getREG()552 getREG()
553 {
554 	register char *lp = linebuf;
555 	register int c;
556 
557 	for (;;) {
558 		if (rnleft == 0) {
559 			if (rnext == 0)
560 				return (EOF);
561 			regio(rnext, read);
562 			rnext = rbuf->rb_next;
563 			rbufcp = rbuf->rb_text;
564 			rnleft = sizeof rbuf->rb_text;
565 		}
566 		c = *rbufcp;
567 		if (c == 0)
568 			return (EOF);
569 		rbufcp++, --rnleft;
570 		if (c == '\n') {
571 			*lp++ = 0;
572 			return (0);
573 		}
574 		*lp++ = c;
575 	}
576 }
577 
YANKreg(c)578 YANKreg(c)
579 	register int c;
580 {
581 	register line *addr;
582 	register struct strreg *sp;
583 	char savelb[LBSIZE];
584 
585 	if (isdigit(c))
586 		kshift();
587 	if (islower(c))
588 		KILLreg(c);
589 	strp = sp = mapreg(c);
590 	sp->rg_flags = inopen && cursor && wcursor;
591 	rbuf = &YANKrbuf;
592 	if (sp->rg_last) {
593 		regio(sp->rg_last, read);
594 		rnleft = sp->rg_nleft;
595 		rbufcp = &rbuf->rb_text[sizeof rbuf->rb_text - rnleft];
596 	} else {
597 		rblock = 0;
598 		rnleft = 0;
599 	}
600 	CP(savelb,linebuf);
601 	for (addr = addr1; addr <= addr2; addr++) {
602 		getline(*addr);
603 		if (sp->rg_flags) {
604 			if (addr == addr2)
605 				*wcursor = 0;
606 			if (addr == addr1)
607 				strcpy(linebuf, cursor);
608 		}
609 		YANKline();
610 	}
611 	rbflush();
612 	killed();
613 	CP(linebuf,savelb);
614 }
615 
kshift()616 kshift()
617 {
618 	register int i;
619 
620 	KILLreg('9');
621 	for (i = '8'; i >= '0'; i--)
622 		copy(mapreg(i+1), mapreg(i), sizeof (struct strreg));
623 }
624 
YANKline()625 YANKline()
626 {
627 	register char *lp = linebuf;
628 	register struct rbuf *rp = rbuf;
629 	register int c;
630 
631 	do {
632 		c = *lp++;
633 		if (c == 0)
634 			c = '\n';
635 		if (rnleft == 0) {
636 			rp->rb_next = REGblk();
637 			rbflush();
638 			rblock = rp->rb_next;
639 			rp->rb_next = 0;
640 			rp->rb_prev = rblock;
641 			rnleft = sizeof rp->rb_text;
642 			rbufcp = rp->rb_text;
643 		}
644 		*rbufcp++ = c;
645 		--rnleft;
646 	} while (c != '\n');
647 	if (rnleft)
648 		*rbufcp = 0;
649 }
650 
rbflush()651 rbflush()
652 {
653 	register struct strreg *sp = strp;
654 
655 	if (rblock == 0)
656 		return;
657 	regio(rblock, write);
658 	if (sp->rg_first == 0)
659 		sp->rg_first = rblock;
660 	sp->rg_last = rblock;
661 	sp->rg_nleft = rnleft;
662 }
663 
664 /* Register c to char buffer buf of size buflen */
regbuf(c,buf,buflen)665 regbuf(c, buf, buflen)
666 char c;
667 char *buf;
668 int buflen;
669 {
670 	register char *p, *lp;
671 
672 	rbuf = &regrbuf;
673 	rnleft = 0;
674 	rblock = 0;
675 	rnext = mapreg(c)->rg_first;
676 	if (rnext==0) {
677 		*buf = 0;
678 		error("Nothing in register %c",c);
679 	}
680 	p = buf;
681 	while (getREG()==0) {
682 		for (lp=linebuf; *lp;) {
683 			if (p >= &buf[buflen])
684 				error("Register too long@to fit in memory");
685 			*p++ = *lp++;
686 		}
687 		*p++ = '\n';
688 	}
689 	if (partreg(c)) p--;
690 	*p = '\0';
691 	getDOT();
692 }
693 
694 /*
695  * Encryption routines.  These are essentially unmodified from ed.
696  */
697 
698