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