xref: /titanic_52/usr/src/cmd/vi/port/ex_temp.c (revision 505d05c73a6e56769f263d4803b22eddd168ee24)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 /* Copyright (c) 1981 Regents of the University of California */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include "ex.h"
36 #include "ex_temp.h"
37 #include "ex_vis.h"
38 #include "ex_tty.h"
39 
40 /*
41  * Editor temporary file routines.
42  * Very similar to those of ed, except uses 2 input buffers.
43  */
44 #define	READ	0
45 #define	WRITE	1
46 
47 unsigned char	tfname[PATH_MAX+1];
48 static unsigned char	rfname[PATH_MAX+1];
49 static unsigned char	tempname[PATH_MAX+1];
50 int	havetmp;
51 short	tfile = -1;
52 static short	rfile = -1;
53 
54 extern int junk();
55 extern int checkjunk();
56 
57 fileinit()
58 {
59 	register unsigned char *p;
60 	register pid_t j;
61 	register int i;
62 	struct stat64 stbuf;
63 
64 	if (tline == INCRMT * (HBLKS+2))
65 		return;
66 	cleanup(0);
67 	if (tfile != -1)
68 		close(tfile);
69 	tline = INCRMT * (HBLKS+2);
70 	blocks[0] = HBLKS;
71 	blocks[1] = HBLKS+1;
72 	blocks[2] = -1;
73 	dirtcnt = 0;
74 	iblock = -1;
75 	iblock2 = -1;
76 	oblock = -1;
77 	if (strlen(svalue(vi_DIRECTORY)) > (PATH_MAX -13))
78 		error(gettext("User set directory too long"));
79 	CP(tfname, svalue(vi_DIRECTORY));
80 	if (stat64((char *)tfname, &stbuf)) {
81 dumbness:
82 		if (setexit() == 0)
83 			filioerr(tfname);
84 		else
85 			putNFL();
86 		cleanup(1);
87 		exit(++errcnt);
88 	}
89 	if (!ISDIR(stbuf)) {
90 		errno = ENOTDIR;
91 		goto dumbness;
92 	}
93 	CP(tempname, tfname);
94 	ichanged = 0;
95 	ichang2 = 0;
96 	(void) strcat(tfname, "/ExXXXXXX");
97 	if ((tfile = mkstemp((char *)tfname)) < 0)
98 		goto dumbness;
99 #ifdef VMUNIX
100 	{
101 		extern stilinc;		/* see below */
102 		stilinc = 0;
103 	}
104 #endif
105 	havetmp = 1;
106 /* 	brk((unsigned char *)fendcore); */
107 }
108 
109 cleanup(all)
110 	bool all;
111 {
112 	pid_t pgrp;
113 	if (all) {
114 		if (kflag)
115 			crypt_close(perm);
116 		if (xtflag)
117 			crypt_close(tperm);
118 		putpad(exit_ca_mode);
119 		flush();
120 		if (ioctl(2, TIOCGPGRP, &pgrp) == 0) {
121 			if (pgrp == getpgid(0)) {
122 #ifdef XPG4
123 				if (envlines != -1 || envcolumns != -1) {
124 					struct winsize jwin;
125 					jwin.ws_row = oldlines;
126 					jwin.ws_col = oldcolumns;
127 					ioctl(0, TIOCSWINSZ, &jwin);
128 				}
129 #endif /* XPG4 */
130 				resetterm();
131 				normtty--;
132 			}
133 		} else {
134 #ifdef XPG4
135 			if (envlines != -1 || envcolumns != -1) {
136 				struct winsize jwin;
137 				jwin.ws_row = oldlines;
138 				jwin.ws_col = oldcolumns;
139 				ioctl(0, TIOCSWINSZ, &jwin);
140 			}
141 #endif /* XPG4 */
142 			resetterm();
143 			normtty--;
144 		}
145 	}
146 	if (havetmp)
147 		unlink(tfname);
148 	havetmp = 0;
149 	if (all && rfile >= 0) {
150 		unlink(rfname);
151 		close(rfile);
152 		rfile = -1;
153 	}
154 	if (all == 1)
155 		exit(errcnt);
156 }
157 
158 getline(tl)
159 	line tl;
160 {
161 	register unsigned char *bp, *lp;
162 	register int nl;
163 
164 	lp = linebuf;
165 	bp = getblock(tl, READ);
166 	nl = nleft;
167 	tl &= ~OFFMSK;
168 	while (*lp++ = *bp++)
169 		if (--nl == 0) {
170 			bp = getblock(tl += INCRMT, READ);
171 			nl = nleft;
172 		}
173 }
174 
175 int
176 putline(void)
177 {
178 	unsigned char *bp, *lp;
179 	unsigned char tmpbp;
180 	int nl;
181 	line tl;
182 
183 	dirtcnt++;
184 	lp = linebuf;
185 	change();
186 	tl = tline;
187 	bp = getblock(tl, WRITE);
188 	nl = nleft;
189 	tl &= ~OFFMSK;
190 	while (*bp = *lp++) {
191 		tmpbp = *bp;
192 		if (tmpbp == '\n') {
193 			*bp = 0;
194 			linebp = lp;
195 			break;
196 		} else if (junk(*bp++)) {
197 			(void) checkjunk(tmpbp);
198 			*--bp;
199 		}
200 		if (--nl == 0) {
201 			bp = getblock(tl += INCRMT, WRITE);
202 			nl = nleft;
203 		}
204 	}
205 	tl = tline;
206 	tline += (((lp - linebuf) + BNDRY - 1) >> SHFT) & 077776;
207 	return (tl);
208 }
209 
210 int	read();
211 int	write();
212 
213 unsigned char *
214 getblock(atl, iof)
215 	line atl;
216 	int iof;
217 {
218 	register int bno, off;
219 	register unsigned char *p1, *p2;
220 	register int n;
221 	line *tmpptr;
222 
223 	bno = (atl >> OFFBTS) & BLKMSK;
224 	off = (atl << SHFT) & LBTMSK;
225 	if (bno >= NMBLKS) {
226 		/*
227 		 * When we overflow tmpfile buffers,
228 		 * throw away line which could not be
229 		 * put into buffer.
230 		 */
231 		for (tmpptr = dot; tmpptr < unddol; tmpptr++)
232 			*tmpptr = *(tmpptr+1);
233 		if (dot == dol)
234 			dot--;
235 		dol--;
236 		unddol--;
237 		error(gettext(" Tmp file too large"));
238 	}
239 	nleft = BUFSIZE - off;
240 	if (bno == iblock) {
241 		ichanged |= iof;
242 		hitin2 = 0;
243 		return (ibuff + off);
244 	}
245 	if (bno == iblock2) {
246 		ichang2 |= iof;
247 		hitin2 = 1;
248 		return (ibuff2 + off);
249 	}
250 	if (bno == oblock)
251 		return (obuff + off);
252 	if (iof == READ) {
253 		if (hitin2 == 0) {
254 			if (ichang2) {
255 				if (xtflag)
256 					if (run_crypt(0L, ibuff2,
257 							CRSIZE, tperm) == -1)
258 						filioerr(tfname);
259 				blkio(iblock2, ibuff2, write);
260 			}
261 			ichang2 = 0;
262 			iblock2 = bno;
263 			blkio(bno, ibuff2, read);
264 			if (xtflag)
265 				if (run_crypt(0L, ibuff2, CRSIZE, tperm) == -1)
266 					filioerr(tfname);
267 			hitin2 = 1;
268 			return (ibuff2 + off);
269 		}
270 		hitin2 = 0;
271 		if (ichanged) {
272 			if (xtflag)
273 				if (run_crypt(0L, ibuff, CRSIZE, tperm) == -1)
274 					filioerr(tfname);
275 			blkio(iblock, ibuff, write);
276 		}
277 		ichanged = 0;
278 		iblock = bno;
279 		blkio(bno, ibuff, read);
280 		if (xtflag)
281 			if (run_crypt(0L, ibuff, CRSIZE, tperm) == -1)
282 				filioerr(tfname);
283 		return (ibuff + off);
284 	}
285 	if (oblock >= 0) {
286 		if (xtflag) {
287 			/*
288 			 * Encrypt block before writing, so some devious
289 			 * person can't look at temp file while editing.
290 			 */
291 			p1 = obuff;
292 			p2 = crbuf;
293 			n = CRSIZE;
294 			while (n--)
295 				*p2++ = *p1++;
296 			if (run_crypt(0L, crbuf, CRSIZE, tperm) == -1)
297 				filioerr(tfname);
298 			blkio(oblock, crbuf, write);
299 		} else
300 			blkio(oblock, obuff, write);
301 	}
302 	oblock = bno;
303 	return (obuff + off);
304 }
305 
306 #ifdef	VMUNIX
307 #define	INCORB	64
308 unsigned char	incorb[INCORB+1][BUFSIZE];
309 #define	pagrnd(a)	((unsigned char *)(((int)a)&~(BUFSIZE-1)))
310 int	stilinc;	/* up to here not written yet */
311 #endif
312 
313 blkio(b, buf, iofcn)
314 	short b;
315 	unsigned char *buf;
316 	int (*iofcn)();
317 {
318 
319 #ifdef VMUNIX
320 	if (b < INCORB) {
321 		if (iofcn == read) {
322 			bcopy(pagrnd(incorb[b+1]), buf, BUFSIZE);
323 			return;
324 		}
325 		bcopy(buf, pagrnd(incorb[b+1]), BUFSIZE);
326 		if (laste) {
327 			if (b >= stilinc)
328 				stilinc = b + 1;
329 			return;
330 		}
331 	} else if (stilinc)
332 		tflush();
333 #endif
334 	lseek(tfile, (long)(unsigned)b * BUFSIZE, 0);
335 	if ((*iofcn)(tfile, buf, BUFSIZE) != BUFSIZE)
336 		filioerr(tfname);
337 }
338 
339 #ifdef VMUNIX
340 tlaste()
341 {
342 
343 	if (stilinc)
344 		dirtcnt = 0;
345 }
346 
347 tflush()
348 {
349 	int i = stilinc;
350 
351 	stilinc = 0;
352 	lseek(tfile, (long)0, 0);
353 	if (write(tfile, pagrnd(incorb[1]), i * BUFSIZE) != (i * BUFSIZE))
354 		filioerr(tfname);
355 }
356 #endif
357 
358 /*
359  * Synchronize the state of the temporary file in case
360  * a crash occurs.
361  */
362 synctmp()
363 {
364 	register int cnt;
365 	register line *a;
366 	register short *bp;
367 	register unsigned char *p1, *p2;
368 	register int n;
369 
370 #ifdef VMUNIX
371 	if (stilinc)
372 		return;
373 #endif
374 	if (dol == zero)
375 		return;
376 	/*
377 	 * In theory, we need to encrypt iblock and iblock2 before writing
378 	 * them out, as well as oblock, but in practice ichanged and ichang2
379 	 * can never be set, so this isn't really needed.  Likewise, the
380 	 * code in getblock above for iblock+iblock2 isn't needed.
381 	 */
382 	if (ichanged)
383 		blkio(iblock, ibuff, write);
384 	ichanged = 0;
385 	if (ichang2)
386 		blkio(iblock2, ibuff2, write);
387 	ichang2 = 0;
388 	if (oblock != -1)
389 	if (xtflag) {
390 		/*
391 		 * Encrypt block before writing, so some devious
392 		 * person can't look at temp file while editing.
393 		 */
394 		p1 = obuff;
395 		p2 = crbuf;
396 		n = CRSIZE;
397 		while (n--)
398 			*p2++ = *p1++;
399 			if (run_crypt(0L, crbuf, CRSIZE, tperm) == -1)
400 				filioerr(tfname);
401 		blkio(oblock, crbuf, write);
402 	} else
403 		blkio(oblock, obuff, write);
404 	time(&H.Time);
405 	uid = getuid();
406 	if (xtflag)
407 		H.encrypted = 1;
408 	else
409 		H.encrypted = 0;
410 	*zero = (line) H.Time;
411 	for (a = zero, bp = blocks; a <= dol;
412 	    a += BUFSIZE / sizeof (*a), bp++) {
413 		if (bp >= &H.Blocks[LBLKS-1])
414 			error(gettext(
415 			    "file too large to recover with -r option"));
416 		if (*bp < 0) {
417 			tline = (tline + OFFMSK) &~ OFFMSK;
418 			*bp = ((tline >> OFFBTS) & BLKMSK);
419 			if (*bp > NMBLKS)
420 				error(gettext(" Tmp file too large"));
421 			tline += INCRMT;
422 			oblock = *bp + 1;
423 			bp[1] = -1;
424 		}
425 		lseek(tfile, (long)(unsigned)*bp * BUFSIZE, 0);
426 		cnt = ((dol - a) + 2) * sizeof (line);
427 		if (cnt > BUFSIZE)
428 			cnt = BUFSIZE;
429 		if (write(tfile, (char *)a, cnt) != cnt) {
430 oops:
431 			*zero = 0;
432 			filioerr(tfname);
433 		}
434 		*zero = 0;
435 	}
436 	flines = lineDOL();
437 	lseek(tfile, 0l, 0);
438 	if (write(tfile, (char *)&H, sizeof (H)) != sizeof (H))
439 		goto oops;
440 }
441 
442 TSYNC()
443 {
444 
445 	if (dirtcnt > MAXDIRT) {
446 #ifdef VMUNIX
447 		if (stilinc)
448 			tflush();
449 #endif
450 		dirtcnt = 0;
451 		synctmp();
452 	}
453 }
454 
455 /*
456  * Named buffer routines.
457  * These are implemented differently than the main buffer.
458  * Each named buffer has a chain of blocks in the register file.
459  * Each block contains roughly 508 chars of text,
460  * and a previous and next block number.  We also have information
461  * about which blocks came from deletes of multiple partial lines,
462  * e.g. deleting a sentence or a LISP object.
463  *
464  * We maintain a free map for the temp file.  To free the blocks
465  * in a register we must read the blocks to find how they are chained
466  * together.
467  *
468  * BUG:		The default savind of deleted lines in numbered
469  *		buffers may be rather inefficient; it hasn't been profiled.
470  */
471 struct	strreg {
472 	short	rg_flags;
473 	short	rg_nleft;
474 	short	rg_first;
475 	short	rg_last;
476 } strregs[('z'-'a'+1) + ('9'-'0'+1)], *strp;
477 
478 struct	rbuf {
479 	short	rb_prev;
480 	short	rb_next;
481 	unsigned char	rb_text[BUFSIZE - 2 * sizeof (short)];
482 } *rbuf, KILLrbuf, putrbuf, YANKrbuf, regrbuf;
483 #ifdef VMUNIX
484 short	rused[256];
485 #else
486 short	rused[32];
487 #endif
488 short	rnleft;
489 short	rblock;
490 short	rnext;
491 unsigned char	*rbufcp;
492 
493 regio(b, iofcn)
494 	short b;
495 	int (*iofcn)();
496 {
497 
498 	if (rfile == -1) {
499 		CP(rfname, tempname);
500 		(void) strcat(rfname, "/RxXXXXXX");
501 		if ((rfile = mkstemp((char *)rfname)) < 0)
502 			filioerr(rfname);
503 	}
504 	lseek(rfile, (long)b * BUFSIZE, 0);
505 	if ((*iofcn)(rfile, rbuf, BUFSIZE) != BUFSIZE)
506 		filioerr(rfname);
507 	rblock = b;
508 }
509 
510 REGblk()
511 {
512 	register int i, j, m;
513 
514 	for (i = 0; i < sizeof (rused) / sizeof (rused[0]); i++) {
515 		m = (rused[i] ^ 0177777) & 0177777;
516 		if (i == 0)
517 			m &= ~1;
518 		if (m != 0) {
519 			j = 0;
520 			while ((m & 1) == 0)
521 				j++, m >>= 1;
522 			rused[i] |= (1 << j);
523 #ifdef RDEBUG
524 			printf("allocating block %d\n", i * 16 + j);
525 #endif
526 			return (i * 16 + j);
527 		}
528 	}
529 	error(gettext("Out of register space (ugh)"));
530 	/*NOTREACHED*/
531 }
532 
533 struct	strreg *
534 mapreg(c)
535 	register int c;
536 {
537 
538 	if (isupper(c))
539 		c = tolower(c);
540 	return (isdigit(c) ? &strregs[('z'-'a'+1)+(c-'0')] : &strregs[c-'a']);
541 }
542 
543 int	shread();
544 
545 KILLreg(c)
546 	register int c;
547 {
548 	register struct strreg *sp;
549 
550 	rbuf = &KILLrbuf;
551 	sp = mapreg(c);
552 	rblock = sp->rg_first;
553 	sp->rg_first = sp->rg_last = 0;
554 	sp->rg_flags = sp->rg_nleft = 0;
555 	while (rblock != 0) {
556 #ifdef RDEBUG
557 		printf("freeing block %d\n", rblock);
558 #endif
559 		rused[rblock / 16] &= ~(1 << (rblock % 16));
560 		regio(rblock, shread);
561 		rblock = rbuf->rb_next;
562 	}
563 }
564 
565 /*VARARGS*/
566 shread()
567 {
568 	struct front { short a; short b; };
569 
570 	if (read(rfile, (char *)rbuf, sizeof (struct front)) ==
571 	    sizeof (struct front))
572 		return (sizeof (struct rbuf));
573 	return (0);
574 }
575 
576 int	getREG();
577 
578 putreg(c)
579 	unsigned char c;
580 {
581 	register line *odot = dot;
582 	register line *odol = dol;
583 	register int cnt;
584 
585 	deletenone();
586 	appendnone();
587 	rbuf = &putrbuf;
588 	rnleft = 0;
589 	rblock = 0;
590 	rnext = mapreg(c)->rg_first;
591 	if (rnext == 0) {
592 		if (inopen) {
593 			splitw++;
594 			vclean();
595 			vgoto(WECHO, 0);
596 		}
597 		vreg = -1;
598 		error(gettext("Nothing in register %c"), c);
599 	}
600 	if (inopen && partreg(c)) {
601 		if (!FIXUNDO) {
602 			splitw++; vclean(); vgoto(WECHO, 0); vreg = -1;
603 			error(gettext("Can't put partial line inside macro"));
604 		}
605 		squish();
606 		addr1 = addr2 = dol;
607 	}
608 	cnt = append(getREG, addr2);
609 	if (inopen && partreg(c)) {
610 		unddol = dol;
611 		dol = odol;
612 		dot = odot;
613 		pragged(0);
614 	}
615 	killcnt(cnt);
616 	notecnt = cnt;
617 }
618 
619 partreg(c)
620 	unsigned char c;
621 {
622 
623 	return (mapreg(c)->rg_flags);
624 }
625 
626 notpart(c)
627 	register int c;
628 {
629 
630 	if (c)
631 		mapreg(c)->rg_flags = 0;
632 }
633 
634 getREG()
635 {
636 	register unsigned char *lp = linebuf;
637 	register int c;
638 
639 	for (;;) {
640 		if (rnleft == 0) {
641 			if (rnext == 0)
642 				return (EOF);
643 			regio(rnext, read);
644 			rnext = rbuf->rb_next;
645 			rbufcp = rbuf->rb_text;
646 			rnleft = sizeof (rbuf->rb_text);
647 		}
648 		c = *rbufcp;
649 		if (c == 0)
650 			return (EOF);
651 		rbufcp++, --rnleft;
652 		if (c == '\n') {
653 			*lp++ = 0;
654 			return (0);
655 		}
656 		*lp++ = c;
657 	}
658 }
659 
660 YANKreg(c)
661 	register int c;
662 {
663 	register line *addr;
664 	register struct strreg *sp;
665 	unsigned char savelb[LBSIZE];
666 
667 	if (isdigit(c))
668 		kshift();
669 	if (islower(c))
670 		KILLreg(c);
671 	strp = sp = mapreg(c);
672 	sp->rg_flags = inopen && cursor && wcursor;
673 	rbuf = &YANKrbuf;
674 	if (sp->rg_last) {
675 		regio(sp->rg_last, read);
676 		rnleft = sp->rg_nleft;
677 		rbufcp = &rbuf->rb_text[sizeof (rbuf->rb_text) - rnleft];
678 	} else {
679 		rblock = 0;
680 		rnleft = 0;
681 	}
682 	CP(savelb, linebuf);
683 	for (addr = addr1; addr <= addr2; addr++) {
684 		getline(*addr);
685 		if (sp->rg_flags) {
686 			if (addr == addr2)
687 				*wcursor = 0;
688 			if (addr == addr1)
689 				strcpy(linebuf, cursor);
690 		}
691 		YANKline();
692 	}
693 	rbflush();
694 	killed();
695 	CP(linebuf, savelb);
696 }
697 
698 kshift()
699 {
700 	register int i;
701 
702 	KILLreg('9');
703 	for (i = '8'; i >= '0'; i--)
704 		copy(mapreg(i+1), mapreg(i), sizeof (struct strreg));
705 }
706 
707 YANKline()
708 {
709 	register unsigned char *lp = linebuf;
710 	register struct rbuf *rp = rbuf;
711 	register int c;
712 
713 	do {
714 		c = *lp++;
715 		if (c == 0)
716 			c = '\n';
717 		if (rnleft == 0) {
718 			rp->rb_next = REGblk();
719 			rbflush();
720 			rblock = rp->rb_next;
721 			rp->rb_next = 0;
722 			rp->rb_prev = rblock;
723 			rnleft = sizeof (rp->rb_text);
724 			rbufcp = rp->rb_text;
725 		}
726 		*rbufcp++ = c;
727 		--rnleft;
728 	} while (c != '\n');
729 	if (rnleft)
730 		*rbufcp = 0;
731 }
732 
733 rbflush()
734 {
735 	register struct strreg *sp = strp;
736 
737 	if (rblock == 0)
738 		return;
739 	regio(rblock, write);
740 	if (sp->rg_first == 0)
741 		sp->rg_first = rblock;
742 	sp->rg_last = rblock;
743 	sp->rg_nleft = rnleft;
744 }
745 
746 /* Register c to char buffer buf of size buflen */
747 regbuf(c, buf, buflen)
748 unsigned char c;
749 unsigned char *buf;
750 int buflen;
751 {
752 	register unsigned char *p, *lp;
753 
754 	rbuf = &regrbuf;
755 	rnleft = 0;
756 	rblock = 0;
757 	rnext = mapreg(c)->rg_first;
758 	if (rnext == 0) {
759 		*buf = 0;
760 		error(gettext("Nothing in register %c"), c);
761 	}
762 	p = buf;
763 	while (getREG() == 0) {
764 		lp = linebuf;
765 		while (*lp) {
766 			if (p >= &buf[buflen])
767 				error(value(vi_TERSE) ?
768 gettext("Register too long") : gettext("Register too long to fit in memory"));
769 			*p++ = *lp++;
770 		}
771 		*p++ = '\n';
772 	}
773 	if (partreg(c)) p--;
774 	*p = '\0';
775 	getDOT();
776 }
777 
778 #ifdef TRACE
779 
780 /*
781  * Test code for displaying named registers.
782  */
783 
784 shownam()
785 {
786 	int k;
787 
788 	printf("\nRegister   Contents\n");
789 	printf("========   ========\n");
790 	for (k = 'a'; k <= 'z'; k++) {
791 		rbuf = &putrbuf;
792 		rnleft = 0;
793 		rblock = 0;
794 		rnext = mapreg(k)->rg_first;
795 		printf(" %c:", k);
796 		if (rnext == 0)
797 			printf("\t\tNothing in register.\n");
798 		while (getREG() == 0) {
799 			printf("\t\t%s\n", linebuf);
800 		}
801 	}
802 	return (0);
803 }
804 
805 /*
806  * Test code for displaying numbered registers.
807  */
808 
809 shownbr()
810 {
811 	int k;
812 
813 	printf("\nRegister   Contents\n");
814 	printf("========   ========\n");
815 	for (k = '1'; k <= '9'; k++) {
816 		rbuf = &putrbuf;
817 		rnleft = 0;
818 		rblock = 0;
819 		rnext = mapreg(k)->rg_first;
820 		printf(" %c:", k);
821 		if (rnext == 0)
822 			printf("\t\tNothing in register.\n");
823 		while (getREG() == 0) {
824 			printf("\t\t%s\n", linebuf);
825 		}
826 	}
827 	return (0);
828 }
829 #endif
830