Subversion Repositories planix.SVN

Rev

Rev 115 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
105 7u83 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
#if	!defined(lint) && defined(DOSCCS)
8
static char *sccsid = "@(#)ex_cmdsub.c	7.7 (Berkeley) 6/7/85";
9
#endif
10
 
11
#include "ex.h"
12
#include "ex_argv.h"
13
#include "ex_temp.h"
14
#include "ex_tty.h"
15
#include "ex_vis.h"
16
 
17
/*
18
 * Command mode subroutines implementing
19
 *	append, args, copy, delete, join, move, put,
20
 *	shift, tag, yank, z and undo
21
 */
22
 
23
bool	endline = 1;
24
line	*tad1;
25
static	jnoop();
26
 
27
/*
28
 * Append after line a lines returned by function f.
29
 * Be careful about intermediate states to avoid scramble
30
 * if an interrupt comes in.
31
 */
32
append(f, a)
33
	int (*f)();
34
	line *a;
35
{
36
	register line *a1, *a2, *rdot;
37
	int nline;
38
 
39
	nline = 0;
40
	dot = a;
41
	if(FIXUNDO && !inopen && f!=getsub) {
42
		undap1 = undap2 = dot + 1;
43
		undkind = UNDCHANGE;
44
	}
45
	while ((*f)() == 0) {
46
		if (truedol >= endcore) {
47
			if (morelines() < 0) {
48
				if (FIXUNDO && f == getsub) {
49
					undap1 = addr1;
50
					undap2 = addr2 + 1;
51
				}
52
				error("Out of memory@- too many lines in file");
53
			}
54
		}
55
		nline++;
56
		a1 = truedol + 1;
57
		a2 = a1 + 1;
58
		dot++;
59
		undap2++;
60
		dol++;
61
		unddol++;
62
		truedol++;
63
		for (rdot = dot; a1 > rdot;)
64
			*--a2 = *--a1;
65
		*rdot = 0;
66
		putmark(rdot);
67
		if (f == gettty) {
68
			dirtcnt++;
69
			TSYNC();
70
		}
71
	}
72
	return (nline);
73
}
74
 
75
appendnone()
76
{
77
 
78
	if(FIXUNDO) {
79
		undkind = UNDCHANGE;
80
		undap1 = undap2 = addr1;
81
	}
82
}
83
 
84
/*
85
 * Print out the argument list, with []'s around the current name.
86
 */
87
pargs()
88
{
89
	register char **av = argv0, *as = args0;
90
	register int ac;
91
 
92
	for (ac = 0; ac < argc0; ac++) {
93
		if (ac != 0)
94
			putchar(' ' | QUOTE);
95
		if (ac + argc == argc0 - 1)
96
			printf("[");
97
		lprintf("%s", as);
98
		if (ac + argc == argc0 - 1)
99
			printf("]");
100
		as = av ? *++av : strend(as) + 1;
101
	}
102
	noonl();
103
}
104
 
105
/*
106
 * Delete lines; two cases are if we are really deleting,
107
 * more commonly we are just moving lines to the undo save area.
108
 */
109
delete(hush)
110
	bool hush;
111
{
112
	register line *a1, *a2;
113
 
114
	nonzero();
115
	if(FIXUNDO) {
116
		register void (*dsavint)();
117
 
118
#ifdef TRACE
119
		if (trace)
120
			vudump("before delete");
121
#endif
122
		change();
123
		dsavint = signal(SIGINT, SIG_IGN);
124
		undkind = UNDCHANGE;
125
		a1 = addr1;
126
		squish();
127
		a2 = addr2;
128
		if (a2++ != dol) {
129
			reverse(a1, a2);
130
			reverse(a2, dol + 1);
131
			reverse(a1, dol + 1);
132
		}
133
		dol -= a2 - a1;
134
		unddel = a1 - 1;
135
		if (a1 > dol)
136
			a1 = dol;
137
		dot = a1;
138
		pkill[0] = pkill[1] = 0;
139
		signal(SIGINT, dsavint);
140
#ifdef TRACE
141
		if (trace)
142
			vudump("after delete");
143
#endif
144
	} else {
145
		register line *a3;
146
		register int i;
147
 
148
		change();
149
		a1 = addr1;
150
		a2 = addr2 + 1;
151
		a3 = truedol;
152
		i = a2 - a1;
153
		unddol -= i;
154
		undap2 -= i;
155
		dol -= i;
156
		truedol -= i;
157
		do
158
			*a1++ = *a2++;
159
		while (a2 <= a3);
160
		a1 = addr1;
161
		if (a1 > dol)
162
			a1 = dol;
163
		dot = a1;
164
	}
165
	if (!hush)
166
		killed();
167
}
168
 
169
deletenone()
170
{
171
 
172
	if(FIXUNDO) {
173
		undkind = UNDCHANGE;
174
		squish();
175
		unddel = addr1;
176
	}
177
}
178
 
179
/*
180
 * Crush out the undo save area, moving the open/visual
181
 * save area down in its place.
182
 */
183
squish()
184
{
185
	register line *a1 = dol + 1, *a2 = unddol + 1, *a3 = truedol + 1;
186
 
187
	if(FIXUNDO) {
188
		if (inopen == -1)
189
			return;
190
		if (a1 < a2 && a2 < a3)
191
			do
192
				*a1++ = *a2++;
193
			while (a2 < a3);
194
		truedol -= unddol - dol;
195
		unddol = dol;
196
	}
197
}
198
 
199
/*
200
 * Join lines.  Special hacks put in spaces, two spaces if
201
 * preceding line ends with '.', or no spaces if next line starts with ).
202
 */
203
static	int jcount, jnoop();
204
 
205
join(c)
206
	int c;
207
{
208
	register line *a1;
209
	register char *cp, *cp1;
210
 
211
	cp = genbuf;
212
	*cp = 0;
213
	for (a1 = addr1; a1 <= addr2; a1++) {
118 7u83 214
		ex_getline(*a1);
105 7u83 215
		cp1 = linebuf;
216
		if (a1 != addr1 && c == 0) {
217
			while (*cp1 == ' ' || *cp1 == '\t')
218
				cp1++;
219
			if (*cp1 && cp > genbuf && cp[-1] != ' ' && cp[-1] != '\t') {
220
				if (*cp1 != ')') {
221
					*cp++ = ' ';
222
					if (cp[-2] == '.')
223
						*cp++ = ' ';
224
				}
225
			}
226
		}
227
		while (*cp++ = *cp1++)
228
			if (cp > &genbuf[LBSIZE-2])
229
				error("Line overflow|Result line of join would be too long");
230
		cp--;
231
	}
232
	strcLIN(genbuf);
233
	delete(0);
234
	jcount = 1;
235
	if (FIXUNDO)
236
		undap1 = undap2 = addr1;
237
	ignore(append(jnoop, --addr1));
238
	if (FIXUNDO)
239
		vundkind = VMANY;
240
}
241
 
242
static
243
jnoop()
244
{
245
 
246
	return(--jcount);
247
}
248
 
249
/*
250
 * Move and copy lines.  Hard work is done by move1 which
251
 * is also called by undo.
252
 */
253
int	getcopy();
254
 
255
move()
256
{
257
	register line *adt;
258
	bool iscopy = 0;
259
 
260
	if (Command[0] == 'm') {
261
		setdot1();
262
		markpr(addr2 == dot ? addr1 - 1 : addr2 + 1);
263
	} else {
264
		iscopy++;
265
		setdot();
266
	}
267
	nonzero();
268
	adt = address((char*)0);
269
	if (adt == 0)
270
		serror("%s where?|%s requires a trailing address", Command);
271
	newline();
272
	move1(iscopy, adt);
273
	killed();
274
}
275
 
276
move1(cflag, addrt)
277
	int cflag;
278
	line *addrt;
279
{
280
	register line *adt, *ad1, *ad2;
281
	int lines;
282
 
283
	adt = addrt;
284
	lines = (addr2 - addr1) + 1;
285
	if (cflag) {
286
		tad1 = addr1;
287
		ad1 = dol;
288
		ignore(append(getcopy, ad1++));
289
		ad2 = dol;
290
	} else {
291
		ad2 = addr2;
292
		for (ad1 = addr1; ad1 <= ad2;)
293
			*ad1++ &= ~01;
294
		ad1 = addr1;
295
	}
296
	ad2++;
297
	if (adt < ad1) {
298
		if (adt + 1 == ad1 && !cflag && !inglobal)
299
			error("That move would do nothing!");
300
		dot = adt + (ad2 - ad1);
301
		if (++adt != ad1) {
302
			reverse(adt, ad1);
303
			reverse(ad1, ad2);
304
			reverse(adt, ad2);
305
		}
306
	} else if (adt >= ad2) {
307
		dot = adt++;
308
		reverse(ad1, ad2);
309
		reverse(ad2, adt);
310
		reverse(ad1, adt);
311
	} else
312
		error("Move to a moved line");
313
	change();
314
	if (!inglobal)
315
		if(FIXUNDO) {
316
			if (cflag) {
317
				undap1 = addrt + 1;
318
				undap2 = undap1 + lines;
319
				deletenone();
320
			} else {
321
				undkind = UNDMOVE;
322
				undap1 = addr1;
323
				undap2 = addr2;
324
				unddel = addrt;
325
				squish();
326
			}
327
		}
328
}
329
 
330
getcopy()
331
{
332
 
333
	if (tad1 > addr2)
334
		return (EOF);
118 7u83 335
	ex_getline(*tad1++);
105 7u83 336
	return (0);
337
}
338
 
339
/*
340
 * Put lines in the buffer from the undo save area.
341
 */
342
getput()
343
{
344
 
345
	if (tad1 > unddol)
346
		return (EOF);
118 7u83 347
	ex_getline(*tad1++);
105 7u83 348
	tad1++;
349
	return (0);
350
}
351
 
352
put()
353
{
354
	register int cnt;
355
 
356
	if (!FIXUNDO)
357
		error("Cannot put inside global/macro");
358
	cnt = unddol - dol;
359
	if (cnt && inopen && pkill[0] && pkill[1]) {
360
		pragged(1);
361
		return;
362
	}
363
	tad1 = dol + 1;
364
	ignore(append(getput, addr2));
365
	undkind = UNDPUT;
366
	notecnt = cnt;
367
	netchange(cnt);
368
}
369
 
370
/*
371
 * A tricky put, of a group of lines in the middle
372
 * of an existing line.  Only from open/visual.
373
 * Argument says pkills have meaning, e.g. called from
374
 * put; it is 0 on calls from putreg.
375
 */
376
pragged(kill)
377
	bool kill;
378
{
379
	extern char *cursor;
380
	register char *gp = &genbuf[cursor - linebuf];
381
 
382
	/*
383
	 * This kind of stuff is TECO's forte.
384
	 * We just grunge along, since it cuts
385
	 * across our line-oriented model of the world
386
	 * almost scrambling our addled brain.
387
	 */
388
	if (!kill)
389
		getDOT();
390
	strcpy(genbuf, linebuf);
118 7u83 391
	ex_getline(*unddol);
105 7u83 392
	if (kill)
393
		*pkill[1] = 0;
394
	strcat(linebuf, gp);
395
	putmark(unddol);
118 7u83 396
	ex_getline(dol[1]);
105 7u83 397
	if (kill)
398
		strcLIN(pkill[0]);
399
	strcpy(gp, linebuf);
400
	strcLIN(genbuf);
401
	putmark(dol+1);
402
	undkind = UNDCHANGE;
403
	undap1 = dot;
404
	undap2 = dot + 1;
405
	unddel = dot - 1;
406
	undo(1);
407
}
408
 
409
/*
410
 * Shift lines, based on c.
411
 * If c is neither < nor >, then this is a lisp aligning =.
412
 */
413
shift(c, cnt)
414
	int c;
415
	int cnt;
416
{
417
	register line *addr;
418
	register char *cp;
419
	char *dp;
420
	register int i;
421
 
422
	if(FIXUNDO)
423
		save12(), undkind = UNDCHANGE;
424
	cnt *= value(SHIFTWIDTH);
425
	for (addr = addr1; addr <= addr2; addr++) {
426
		dot = addr;
427
#ifdef LISPCODE
428
		if (c == '=' && addr == addr1 && addr != addr2)
429
			continue;
430
#endif
431
		getDOT();
432
		i = whitecnt(linebuf);
433
		switch (c) {
434
 
435
		case '>':
436
			if (linebuf[0] == 0)
437
				continue;
438
			cp = genindent(i + cnt);
439
			break;
440
 
441
		case '<':
442
			if (i == 0)
443
				continue;
444
			i -= cnt;
445
			cp = i > 0 ? genindent(i) : genbuf;
446
			break;
447
 
448
#ifdef LISPCODE
449
		default:
450
			i = lindent(addr);
451
			getDOT();
452
			cp = genindent(i);
453
			break;
454
#endif
455
		}
456
		if (cp + strlen(dp = vpastwh(linebuf)) >= &genbuf[LBSIZE - 2])
457
			error("Line too long|Result line after shift would be too long");
115 7u83 458
		CP(cp, dp);
105 7u83 459
		strcLIN(genbuf);
460
		putmark(addr);
461
	}
462
	killed();
463
}
464
 
465
/*
466
 * Find a tag in the tags file.
467
 * Most work here is in parsing the tags file itself.
468
 */
469
tagfind(quick)
470
	bool quick;
471
{
472
	char cmdbuf[BUFSIZ];
473
	char filebuf[FNSIZE];
474
	char tagfbuf[128];
475
	register int c, d;
476
	bool samef = 1;
477
	int tfcount = 0;
478
	int omagic;
479
	char *fn, *fne;
480
	struct stat sbuf;
481
#ifdef FASTTAG
482
	int iof;
483
	char iofbuf[MAXBSIZE];
484
	long mid;	/* assumed byte offset */
485
	long top, bot;	/* length of tag file */
486
#endif
487
 
488
	omagic = value(MAGIC);
489
	if (!skipend()) {
490
		register char *lp = lasttag;
491
 
492
		while (!iswhite(peekchar()) && !endcmd(peekchar()))
493
			if (lp < &lasttag[sizeof lasttag - 2])
494
				*lp++ = getchar();
495
			else
496
				ignchar();
497
		*lp++ = 0;
498
		if (!endcmd(peekchar()))
499
badtag:
500
			error("Bad tag|Give one tag per line");
501
	} else if (lasttag[0] == 0)
502
		error("No previous tag");
503
	c = getchar();
504
	if (!endcmd(c))
505
		goto badtag;
506
	if (c == EOF)
507
		ungetchar(c);
508
	clrstats();
509
 
510
	/*
511
	 * Loop once for each file in tags "path".
512
	 */
115 7u83 513
	CP(tagfbuf, svalue(TAGS));
105 7u83 514
	fne = tagfbuf - 1;
515
	while (fne) {
516
		fn = ++fne;
517
		while (*fne && *fne != ' ')
518
			fne++;
519
		if (*fne == 0)
520
			fne = 0;	/* done, quit after this time */
521
		else
522
			*fne = 0;	/* null terminate filename */
523
#ifdef FASTTAG
524
		iof = topen(fn, iofbuf);
525
		if (iof == -1)
526
			continue;
527
		tfcount++;
528
		fstat(iof, &sbuf);
529
		top = sbuf.st_size;
530
		if (top == 0L )
531
			top = -1L;
532
		bot = 0L;
533
		while (top >= bot) {
534
#else
535
		/*
536
		 * Avoid stdio and scan tag file linearly.
537
		 */
538
		io = open(fn, 0);
539
		if (io<0)
540
			continue;
541
		tfcount++;
542
		if (fstat(io, &sbuf) < 0)
543
			bsize = LBSIZE;
544
		else {
545
			bsize = sbuf.st_blksize;
546
			if (bsize <= 0)
547
				bsize = LBSIZE;
548
		}
549
		while (getfile() == 0) {
550
#endif
551
			/* loop for each tags file entry */
552
			register char *cp = linebuf;
553
			register char *lp = lasttag;
554
			char *oglobp;
555
 
556
#ifdef FASTTAG
557
			mid = (top + bot) / 2;
558
			tseek(iof, mid);
559
			if (mid > 0)	/* to get first tag in file to work */
560
				/* scan to next \n */
107 7u83 561
				if(tgets(linebuf, sizeof linebuf, iof)==0)
105 7u83 562
					goto goleft;
563
			/* get the line itself */
107 7u83 564
			if(tgets(linebuf, sizeof linebuf, iof)==0)
105 7u83 565
				goto goleft;
566
#ifdef TDEBUG
567
			printf("tag: %o %o %o %s\n", bot, mid, top, linebuf);
568
#endif
569
#endif
570
			while (*cp && *lp == *cp)
571
				cp++, lp++;
572
			if ((*lp || !iswhite(*cp)) && (value(TAGLENGTH)==0 ||
573
			    lp-lasttag < value(TAGLENGTH))) {
574
#ifdef FASTTAG
575
				if (*lp > *cp)
576
					bot = mid + 1;
577
				else
578
goleft:
579
					top = mid - 1;
580
#endif
581
				/* Not this tag.  Try the next */
582
				continue;
583
			}
584
 
585
			/*
586
			 * We found the tag.  Decode the line in the file.
587
			 */
588
#ifdef FASTTAG
589
			tclose(iof);
590
#else
591
			close(io);
592
#endif
593
			/* Rest of tag if abbreviated */
594
			while (!iswhite(*cp))
595
				cp++;
596
 
597
			/* name of file */
598
			while (*cp && iswhite(*cp))
599
				cp++;
600
			if (!*cp)
601
badtags:
602
				serror("%s: Bad tags file entry", lasttag);
603
			lp = filebuf;
604
			while (*cp && *cp != ' ' && *cp != '\t') {
605
				if (lp < &filebuf[sizeof filebuf - 2])
606
					*lp++ = *cp;
607
				cp++;
608
			}
609
			*lp++ = 0;
610
 
611
			if (*cp == 0)
612
				goto badtags;
613
			if (dol != zero) {
614
				/*
615
				 * Save current position in 't for ^^ in visual.
616
				 */
617
				names['t'-'a'] = *dot &~ 01;
618
				if (inopen) {
619
					extern char *ncols['z'-'a'+2];
620
					extern char *cursor;
621
 
622
					ncols['t'-'a'] = cursor;
623
				}
624
			}
625
			strcpy(cmdbuf, cp);
626
			if (strcmp(filebuf, savedfile) || !edited) {
627
				char cmdbuf2[sizeof filebuf + 10];
628
 
629
				/* Different file.  Do autowrite & get it. */
630
				if (!quick) {
631
					ckaw();
632
					if (chng && dol > zero)
633
						error("No write@since last change (:tag! overrides)");
634
				}
635
				oglobp = globp;
636
				strcpy(cmdbuf2, "e! ");
637
				strcat(cmdbuf2, filebuf);
638
				globp = cmdbuf2;
639
				d = peekc; ungetchar(0);
640
				commands(1, 1);
641
				peekc = d;
642
				globp = oglobp;
643
				value(MAGIC) = omagic;
644
				samef = 0;
645
			}
646
 
647
			/*
648
			 * Look for pattern in the current file.
649
			 */
650
			oglobp = globp;
651
			globp = cmdbuf;
652
			d = peekc; ungetchar(0);
653
			if (samef)
654
				markpr(dot);
655
			/*
656
			 * BUG: if it isn't found (user edited header
657
			 * line) we get left in nomagic mode.
658
			 */
659
			value(MAGIC) = 0;
660
			commands(1, 1);
661
			peekc = d;
662
			globp = oglobp;
663
			value(MAGIC) = omagic;
664
			return;
665
		}	/* end of "for each tag in file" */
666
 
667
		/*
668
		 * No such tag in this file.  Close it and try the next.
669
		 */
670
#ifdef FASTTAG
671
		tclose(iof);
672
#else
673
		close(io);
674
#endif
675
	}	/* end of "for each file in path" */
676
	if (tfcount <= 0)
677
		error("No tags file");
678
	else
679
		serror("%s: No such tag@in tags file", lasttag);
680
}
681
 
682
/*
683
 * Save lines from addr1 thru addr2 as though
684
 * they had been deleted.
685
 */
686
yank()
687
{
688
 
689
	if (!FIXUNDO)
690
		error("Can't yank inside global/macro");
691
	save12();
692
	undkind = UNDNONE;
693
	killcnt(addr2 - addr1 + 1);
694
}
695
 
696
/*
697
 * z command; print windows of text in the file.
698
 *
699
 * If this seems unreasonably arcane, the reasons
700
 * are historical.  This is one of the first commands
701
 * added to the first ex (then called en) and the
702
 * number of facilities here were the major advantage
703
 * of en over ed since they allowed more use to be
704
 * made of fast terminals w/o typing .,.22p all the time.
705
 */
706
bool	zhadpr;
707
bool	znoclear;
708
short	zweight;
709
 
710
zop(hadpr)
711
	int hadpr;
712
{
713
	register int c, lines, op;
714
	bool excl;
715
 
716
	zhadpr = hadpr;
717
	notempty();
718
	znoclear = 0;
719
	zweight = 0;
720
	excl = exclam();
721
	switch (c = op = getchar()) {
722
 
723
	case '^':
724
		zweight = 1;
725
	case '-':
726
	case '+':
727
		while (peekchar() == op) {
728
			ignchar();
729
			zweight++;
730
		}
731
	case '=':
732
	case '.':
733
		c = getchar();
734
		break;
735
 
736
	case EOF:
737
		znoclear++;
738
		break;
739
 
740
	default:
741
		op = 0;
742
		break;
743
	}
744
	if (isdigit(c)) {
745
		lines = c - '0';
746
		for(;;) {
747
			c = getchar();
748
			if (!isdigit(c))
749
				break;
750
			lines *= 10;
751
			lines += c - '0';
752
		}
753
		if (lines < LINES)
754
			znoclear++;
755
		value(WINDOW) = lines;
756
		if (op == '=')
757
			lines += 2;
758
	} else
759
		lines = op == EOF ? value(SCROLL) : excl ? LINES - 1 : 2*value(SCROLL);
760
	if (inopen || c != EOF) {
761
		ungetchar(c);
762
		newline();
763
	}
764
	addr1 = addr2;
765
	if (addr2 == 0 && dot < dol && op == 0)
766
		addr1 = addr2 = dot+1;
767
	setdot();
768
	zop2(lines, op);
769
}
770
 
771
static
772
splitit()
773
{
774
	register int l;
775
 
776
	for (l = COLUMNS > 80 ? 40 : COLUMNS / 2; l > 0; l--)
777
		putchar('-');
778
	putnl();
779
}
780
 
781
zop2(lines, op)
782
	register int lines;
783
	register int op;
784
{
785
	register line *split;
786
 
787
	split = NULL;
788
	switch (op) {
789
 
790
	case EOF:
791
		if (addr2 == dol)
792
			error("\nAt EOF");
793
	case '+':
794
		if (addr2 == dol)
795
			error("At EOF");
796
		addr2 += lines * zweight;
797
		if (addr2 > dol)
798
			error("Hit BOTTOM");
799
		addr2++;
800
	default:
801
		addr1 = addr2;
802
		addr2 += lines-1;
803
		dot = addr2;
804
		break;
805
 
806
	case '=':
807
	case '.':
808
		znoclear = 0;
809
		lines--;
810
		lines >>= 1;
811
		if (op == '=')
812
			lines--;
813
		addr1 = addr2 - lines;
814
		if (op == '=')
815
			dot = split = addr2;
816
		addr2 += lines;
817
		if (op == '.') {
818
			markDOT();
819
			dot = addr2;
820
		}
821
		break;
822
 
823
	case '^':
824
	case '-':
825
		addr2 -= lines * zweight;
826
		if (addr2 < one)
827
			error("Hit TOP");
828
		lines--;
829
		addr1 = addr2 - lines;
830
		dot = addr2;
831
		break;
832
	}
833
	if (addr1 <= zero)
834
		addr1 = one;
835
	if (addr2 > dol)
836
		addr2 = dol;
837
	if (dot > dol)
838
		dot = dol;
839
	if (addr1 > addr2)
840
		return;
841
	if (op == EOF && zhadpr) {
118 7u83 842
		ex_getline(*addr1);
105 7u83 843
		putchar('\r' | QUOTE);
844
		shudclob = 1;
845
	} else if (znoclear == 0 && CL != NOSTR && !inopen) {
846
		flush1();
847
		vclear();
848
	}
849
	if (addr2 - addr1 > 1)
850
		pstart();
851
	if (split) {
852
		plines(addr1, split - 1, 0);
853
		splitit();
854
		plines(split, split, 0);
855
		splitit();
856
		addr1 = split + 1;
857
	}
858
	plines(addr1, addr2, 0);
859
}
860
 
861
plines(adr1, adr2, movedot)
862
	line *adr1;
863
	register line *adr2;
864
	bool movedot;
865
{
866
	register line *addr;
867
 
868
	pofix();
869
	for (addr = adr1; addr <= adr2; addr++) {
118 7u83 870
		ex_getline(*addr);
105 7u83 871
		pline(lineno(addr));
872
		if (inopen)
873
			putchar('\n' | QUOTE);
874
		if (movedot)
875
			dot = addr;
876
	}
877
}
878
 
879
pofix()
880
{
881
 
882
	if (inopen && Outchar != termchar) {
883
		vnfl();
884
		setoutt();
885
	}
886
}
887
 
888
/*
889
 * Dudley doright to the rescue.
890
 * Undo saves the day again.
891
 * A tip of the hatlo hat to Warren Teitleman
892
 * who made undo as useful as do.
893
 *
894
 * Command level undo works easily because
895
 * the editor has a unique temporary file
896
 * index for every line which ever existed.
897
 * We don't have to save large blocks of text,
898
 * only the indices which are small.  We do this
899
 * by moving them to after the last line in the
900
 * line buffer array, and marking down info
901
 * about whence they came.
902
 *
903
 * Undo is its own inverse.
904
 */
905
undo(c)
906
	bool c;
907
{
908
	register int i;
909
	register line *jp, *kp;
910
	line *dolp1, *newdol, *newadot;
911
 
912
#ifdef TRACE
913
	if (trace)
914
		vudump("before undo");
915
#endif
916
	if (inglobal && inopen <= 0)
917
		error("Can't undo in global@commands");
918
	if (!c)
919
		somechange();
920
	pkill[0] = pkill[1] = 0;
921
	change();
922
	if (undkind == UNDMOVE) {
923
 		/*
924
		 * Command to be undone is a move command.
925
		 * This is handled as a special case by noting that
926
		 * a move "a,b m c" can be inverted by another move.
927
		 */
928
		if ((i = (jp = unddel) - undap2) > 0) {
929
			/*
930
			 * when c > b inverse is a+(c-b),c m a-1
931
			 */
932
			addr2 = jp;
933
			addr1 = (jp = undap1) + i;
934
			unddel = jp-1;
935
		} else {
936
			/*
937
			 * when b > c inverse is  c+1,c+1+(b-a) m b
938
			 */
939
			addr1 = ++jp;
940
			addr2 = jp + ((unddel = undap2) - undap1);
941
		}
942
		kp = undap1;
943
		move1(0, unddel);
944
		dot = kp;
945
		Command = "move";
946
		killed();
947
	} else {
948
		int cnt;
949
 
950
		newadot = dot;
951
		cnt = lineDOL();
952
		newdol = dol;
953
		dolp1 = dol + 1;
954
		/*
955
		 * Command to be undone is a non-move.
956
		 * All such commands are treated as a combination of
957
		 * a delete command and a append command.
958
		 * We first move the lines appended by the last command
959
		 * from undap1 to undap2-1 so that they are just before the
960
		 * saved deleted lines.
961
		 */
962
		if ((i = (kp = undap2) - (jp = undap1)) > 0) {
963
			if (kp != dolp1) {
964
				reverse(jp, kp);
965
				reverse(kp, dolp1);
966
				reverse(jp, dolp1);
967
			}
968
			/*
969
			 * Account for possible backward motion of target
970
			 * for restoration of saved deleted lines.
971
			 */
972
			if (unddel >= jp)
973
				unddel -= i;
974
			newdol -= i;
975
			/*
976
			 * For the case where no lines are restored, dot
977
			 * is the line before the first line deleted.
978
			 */
979
			dot = jp-1;
980
		}
981
		/*
982
		 * Now put the deleted lines, if any, back where they were.
983
		 * Basic operation is: dol+1,unddol m unddel
984
		 */
985
		if (undkind == UNDPUT) {
986
			unddel = undap1 - 1;
987
			squish();
988
		}
989
		jp = unddel + 1;
990
		if ((i = (kp = unddol) - dol) > 0) {
991
			if (jp != dolp1) {
992
				reverse(jp, dolp1);
993
				reverse(dolp1, ++kp);
994
				reverse(jp, kp);
995
			}
996
			/*
997
			 * Account for possible forward motion of the target
998
			 * for restoration of the deleted lines.
999
			 */
1000
			if (undap1 >= jp)
1001
				undap1 += i;
1002
			/*
1003
			 * Dot is the first resurrected line.
1004
			 */
1005
			dot = jp;
1006
			newdol += i;
1007
		}
1008
		/*
1009
		 * Clean up so we are invertible
1010
		 */
1011
		unddel = undap1 - 1;
1012
		undap1 = jp;
1013
		undap2 = jp + i;
1014
		dol = newdol;
1015
		netchHAD(cnt);
1016
		if (undkind == UNDALL) {
1017
			dot = undadot;
1018
			undadot = newadot;
1019
		} else
1020
			undkind = UNDCHANGE;
1021
	}
1022
	/*
1023
	 * Defensive programming - after a munged undadot.
1024
	 * Also handle empty buffer case.
1025
	 */
1026
	if ((dot <= zero || dot > dol) && dot != dol)
1027
		dot = one;
1028
#ifdef TRACE
1029
	if (trace)
1030
		vudump("after undo");
1031
#endif
1032
}
1033
 
1034
/*
1035
 * Be (almost completely) sure there really
1036
 * was a change, before claiming to undo.
1037
 */
1038
somechange()
1039
{
1040
	register line *ip, *jp;
1041
 
1042
	switch (undkind) {
1043
 
1044
	case UNDMOVE:
1045
		return;
1046
 
1047
	case UNDCHANGE:
1048
		if (undap1 == undap2 && dol == unddol)
1049
			break;
1050
		return;
1051
 
1052
	case UNDPUT:
1053
		if (undap1 != undap2)
1054
			return;
1055
		break;
1056
 
1057
	case UNDALL:
1058
		if (unddol - dol != lineDOL())
1059
			return;
1060
		for (ip = one, jp = dol + 1; ip <= dol; ip++, jp++)
1061
			if ((*ip &~ 01) != (*jp &~ 01))
1062
				return;
1063
		break;
1064
 
1065
	case UNDNONE:
1066
		error("Nothing to undo");
1067
	}
1068
	error("Nothing changed|Last undoable command didn't change anything");
1069
}
1070
 
1071
/*
1072
 * Map command:
1073
 * map src dest
1074
 */
1075
mapcmd(un, ab)
1076
	int un;	/* true if this is unmap command */
1077
	int ab;	/* true if this is abbr command */
1078
{
1079
	char lhs[100], rhs[100];	/* max sizes resp. */
1080
	register char *p;
1081
	register int c;		/* mjm: char --> int */
1082
	char *dname;
1083
	struct maps *mp;	/* the map structure we are working on */
1084
 
1085
	mp = ab ? abbrevs : exclam() ? immacs : arrows;
1086
	if (skipend()) {
1087
		int i;
1088
 
1089
		/* print current mapping values */
1090
		if (peekchar() != EOF)
1091
			ignchar();
1092
		if (un)
1093
			error("Missing lhs");
1094
		if (inopen)
1095
			pofix();
1096
		for (i=0; mp[i].mapto; i++)
1097
			if (mp[i].cap) {
1098
				lprintf("%s", mp[i].descr);
1099
				putchar('\t');
1100
				lprintf("%s", mp[i].cap);
1101
				putchar('\t');
1102
				lprintf("%s", mp[i].mapto);
1103
				putNFL();
1104
			}
1105
		return;
1106
	}
1107
 
1108
	ignore(skipwh());
1109
	for (p=lhs; ; ) {
1110
		c = getchar();
1111
		if (c == CTRL('v')) {
1112
			c = getchar();
1113
		} else if (!un && any(c, " \t")) {
1114
			/* End of lhs */
1115
			break;
1116
		} else if (endcmd(c) && c!='"') {
1117
			ungetchar(c);
1118
			if (un) {
1119
				newline();
1120
				*p = 0;
1121
				addmac(lhs, NOSTR, NOSTR, mp);
1122
				return;
1123
			} else
1124
				error("Missing rhs");
1125
		}
1126
		*p++ = c;
1127
	}
1128
	*p = 0;
1129
 
1130
	if (skipend())
1131
		error("Missing rhs");
1132
	for (p=rhs; ; ) {
1133
		c = getchar();
1134
		if (c == CTRL('v')) {
1135
			c = getchar();
1136
		} else if (endcmd(c) && c!='"') {
1137
			ungetchar(c);
1138
			break;
1139
		}
1140
		*p++ = c;
1141
	}
1142
	*p = 0;
1143
	newline();
1144
	/*
1145
	 * Special hack for function keys: #1 means key f1, etc.
1146
	 * If the terminal doesn't have function keys, we just use #1.
1147
	 */
1148
	if (lhs[0] == '#') {
1149
		char *fnkey;
1150
		char *fkey();
1151
		char funkey[3];
1152
 
1153
		fnkey = fkey(lhs[1] - '0');
1154
		funkey[0] = 'f'; funkey[1] = lhs[1]; funkey[2] = 0;
1155
		if (fnkey)
1156
			strcpy(lhs, fnkey);
1157
		dname = funkey;
1158
	} else {
1159
		dname = lhs;
1160
	}
1161
	addmac(lhs,rhs,dname,mp);
1162
}
1163
 
1164
/*
1165
 * Add a macro definition to those that already exist. The sequence of
1166
 * chars "src" is mapped into "dest". If src is already mapped into something
1167
 * this overrides the mapping. There is no recursion. Unmap is done by
1168
 * using NOSTR for dest.  Dname is what to show in listings.  mp is
1169
 * the structure to affect (arrows, etc).
1170
 */
1171
addmac(src,dest,dname,mp)
1172
	register char *src, *dest, *dname;
1173
	register struct maps *mp;
1174
{
1175
	register int slot, zer;
1176
 
1177
#ifdef TRACE
1178
	if (trace)
1179
		fprintf(trace, "addmac(src='%s', dest='%s', dname='%s', mp=%x\n", src, dest, dname, mp);
1180
#endif
1181
	if (dest && mp==arrows) {
1182
		/* Make sure user doesn't screw himself */
1183
		/*
1184
		 * Prevent tail recursion. We really should be
1185
		 * checking to see if src is a suffix of dest
1186
		 * but this makes mapping involving escapes that
1187
		 * is reasonable mess up.
1188
		 */
1189
		if (src[1] == 0 && src[0] == dest[strlen(dest)-1])
1190
			error("No tail recursion");
1191
		/*
1192
		 * We don't let the user rob himself of ":", and making
1193
		 * multi char words is a bad idea so we don't allow it.
1194
		 * Note that if user sets mapinput and maps all of return,
1195
		 * linefeed, and escape, he can screw himself. This is
1196
		 * so weird I don't bother to check for it.
1197
		 */
1198
		if (isalpha(src[0]) && src[1] || any(src[0],":"))
1199
			error("Too dangerous to map that");
1200
	}
1201
	else if (dest) {
1202
		/* check for tail recursion in input mode: fussier */
1203
		if (eq(src, dest+strlen(dest)-strlen(src)))
1204
			error("No tail recursion");
1205
	}
1206
	/*
1207
	 * If the src were null it would cause the dest to
1208
	 * be mapped always forever. This is not good.
1209
	 */
1210
	if (src == NOSTR || src[0] == 0)
1211
		error("Missing lhs");
1212
 
1213
	/* see if we already have a def for src */
1214
	zer = -1;
1215
	for (slot=0; mp[slot].mapto; slot++) {
1216
		if (mp[slot].cap) {
1217
			if (eq(src, mp[slot].cap) || eq(src, mp[slot].mapto))
1218
				break;	/* if so, reuse slot */
1219
		} else {
1220
			zer = slot;	/* remember an empty slot */
1221
		}
1222
	}
1223
 
1224
	if (dest == NOSTR) {
1225
		/* unmap */
1226
		if (mp[slot].cap) {
1227
			mp[slot].cap = NOSTR;
1228
			mp[slot].descr = NOSTR;
1229
		} else {
1230
			error("Not mapped|That macro wasn't mapped");
1231
		}
1232
		return;
1233
	}
1234
 
1235
	/* reuse empty slot, if we found one and src isn't already defined */
1236
	if (zer >= 0 && mp[slot].mapto == 0)
1237
		slot = zer;
1238
 
1239
	/* if not, append to end */
1240
	if (slot >= MAXNOMACS)
1241
		error("Too many macros");
1242
	if (msnext == 0)	/* first time */
1243
		msnext = mapspace;
1244
	/* Check is a bit conservative, we charge for dname even if reusing src */
1245
	if (msnext - mapspace + strlen(dest) + strlen(src) + strlen(dname) + 3 > MAXCHARMACS)
1246
		error("Too much macro text");
115 7u83 1247
	CP(msnext, src);
105 7u83 1248
	mp[slot].cap = msnext;
1249
	msnext += strlen(src) + 1;	/* plus 1 for null on the end */
115 7u83 1250
	CP(msnext, dest);
105 7u83 1251
	mp[slot].mapto = msnext;
1252
	msnext += strlen(dest) + 1;
1253
	if (dname) {
115 7u83 1254
		CP(msnext, dname);
105 7u83 1255
		mp[slot].descr = msnext;
1256
		msnext += strlen(dname) + 1;
1257
	} else {
1258
		/* default descr to string user enters */
1259
		mp[slot].descr = src;
1260
	}
1261
}
1262
 
1263
/*
1264
 * Implements macros from command mode. c is the buffer to
1265
 * get the macro from.
1266
 */
1267
cmdmac(c)
1268
char c;
1269
{
1270
	char macbuf[BUFSIZ];
1271
	line *ad, *a1, *a2;
1272
	char *oglobp;
1273
	short pk;
1274
	bool oinglobal;
1275
 
1276
	lastmac = c;
1277
	oglobp = globp;
1278
	oinglobal = inglobal;
1279
	pk = peekc; peekc = 0;
1280
	if (inglobal < 2)
1281
		inglobal = 1;
1282
	regbuf(c, macbuf, sizeof(macbuf));
1283
	a1 = addr1; a2 = addr2;
1284
	for (ad=a1; ad<=a2; ad++) {
1285
		globp = macbuf;
1286
		dot = ad;
1287
		commands(1,1);
1288
	}
1289
	globp = oglobp;
1290
	inglobal = oinglobal;
1291
	peekc = pk;
1292
}