Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

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