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