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