Subversion Repositories planix.SVN

Rev

Rev 113 | 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_vmain.c	7.7 (Berkeley) 6/7/85";
9
#endif
10
 
11
#include "ex.h"
12
#include "ex_tty.h"
13
#include "ex_vis.h"
14
 
15
#ifdef	BIT8
16
short *ss_strcpy(dest, src)
17
short *dest;
18
const short *src;
19
{
20
	unsigned int i = 0;
21
 
22
	do {
23
		dest[i] = src[i];
24
	} while (src[i++] != '\0');
25
	return dest;
26
}
27
 
28
short *sc_strcpy(dest, src)
29
short *dest;
30
const char *src;
31
{
32
	unsigned int i = 0;
33
 
34
	do {
35
		dest[i] = src[i];
36
	} while (src[i++] != '\0');
37
	return dest;
38
}
39
 
40
char *cs_strcpy(dest, src)
41
char *dest;
42
const short *src;
43
{
44
	unsigned int i = 0;
45
 
46
	do {
47
		dest[i] = src[i];
48
	} while (src[i++] != '\0');
49
	return dest;
50
}
51
 
52
unsigned int s_strlen(s)
53
const short *s;
54
{
55
	unsigned int i = 0;
56
 
57
	while (s[i] != '\0')
58
		i++;
59
	return i;
60
}
61
 
62
short *sc_strcat(dest, src)
63
short *dest;
64
const char *src;
65
{
66
	return sc_strcpy(dest + s_strlen((const short*)dest), src);
67
}
68
#endif
69
 
70
/*
71
 * This is the main routine for visual.
72
 * We here decode the count and possible named buffer specification
73
 * preceding a command and interpret a few of the commands.
74
 * Commands which involve a target (i.e. an operator) are decoded
75
 * in the routine operate in ex_voperate.c.
76
 */
77
 
78
#define	forbid(a)	{ if (a) goto fonfon; }
79
 
80
vmain()
81
{
82
	register int c, cnt, i;
83
#ifndef	BIT8
84
	char esave[TUBECOLS];
85
	char *oglobp;
86
#else
87
	short esave[TUBECOLS];
88
	short *oglobp;
89
#endif
90
	short d;
91
	line *addr;
92
	int ind, nlput;
93
	int shouldpo = 0;
115 7u83 94
	int onumber, olist, (*OPline)(), (*OPutchar)();
105 7u83 95
 
96
	vch_mac = VC_NOTINMAC;
97
 
98
	/*
99
	 * If we started as a vi command (on the command line)
100
	 * then go process initial commands (recover, next or tag).
101
	 */
102
	if (initev) {
103
		oglobp = globp;
104
		globp = initev;
105
		hadcnt = cnt = 0;
106
		i = tchng;
107
		addr = dot;
108
		goto doinit;
109
	}
110
 
111
	/*
112
	 * NB:
113
	 *
114
	 * The current line is always in the line buffer linebuf,
115
	 * and the cursor at the position cursor.  You should do
116
	 * a vsave() before moving off the line to make sure the disk
117
	 * copy is updated if it has changed, and a getDOT() to get
118
	 * the line back if you mung linebuf.  The motion
119
	 * routines in ex_vwind.c handle most of this.
120
	 */
121
	for (;;) {
122
		/*
123
		 * Decode a visual command.
124
		 * First sync the temp file if there has been a reasonable
125
		 * amount of change.  Clear state for decoding of next
126
		 * command.
127
		 */
128
		TSYNC();
129
		vglobp = 0;
130
		vreg = 0;
131
		hold = 0;
132
		seenprompt = 1;
133
		wcursor = 0;
134
		Xhadcnt = hadcnt = 0;
135
		Xcnt = cnt = 1;
136
		splitw = 0;
137
		if (i = holdupd) {
138
			if (state == VISUAL)
139
				ignore(peekkey());
140
			holdupd = 0;
141
/*
142
			if (LINE(0) < ZERO) {
143
				vclear();
144
				vcnt = 0;
145
				i = 3;
146
			}
147
*/
148
			if (state != VISUAL) {
149
				vcnt = 0;
150
				vsave();
151
				vrepaint(cursor);
152
			} else if (i == 3)
153
				vredraw(WTOP);
154
			else
155
				vsync(WTOP);
156
			vfixcurs();
157
		}
158
 
159
		/*
160
		 * Gobble up counts and named buffer specifications.
161
		 */
162
		for (;;) {
163
looptop:
164
#ifdef MDEBUG
165
			if (trace)
166
				fprintf(trace, "pc=%c",peekkey());
167
#endif
168
			if (isdigit(peekkey()) && peekkey() != '0') {
169
				hadcnt = 1;
170
				cnt = vgetcnt();
171
				forbid (cnt <= 0);
172
			}
173
			if (peekkey() != '"')
174
				break;
175
			ignore(getkey()), c = getkey();
176
			/*
177
			 * Buffer names be letters or digits.
178
			 * But not '0' as that is the source of
179
			 * an 'empty' named buffer spec in the routine
180
			 * kshift (see ex_temp.c).
181
			 */
182
			forbid (c == '0' || !isalpha(c) && !isdigit(c));
183
			vreg = c;
184
		}
185
reread:
186
		/*
187
		 * Come to reread from below after some macro expansions.
188
		 * The call to map allows use of function key pads
189
		 * by performing a terminal dependent mapping of inputs.
190
		 */
191
#ifdef MDEBUG
192
		if (trace)
193
			fprintf(trace,"pcb=%c,",peekkey());
194
#endif
195
		op = getkey();
196
		maphopcnt = 0;
197
		do {
198
			/*
199
			 * Keep mapping the char as long as it changes.
200
			 * This allows for double mappings, e.g., q to #,
201
			 * #1 to something else.
202
			 */
203
			c = op;
204
			op = map(c,arrows);
205
#ifdef MDEBUG
206
			if (trace)
207
				fprintf(trace,"pca=%c,",c);
208
#endif
209
			/*
210
			 * Maybe the mapped to char is a count. If so, we have
211
			 * to go back to the "for" to interpret it. Likewise
212
			 * for a buffer name.
213
			 */
214
			if ((isdigit(c) && c!='0') || c == '"') {
215
				ungetkey(c);
216
				goto looptop;
217
			}
218
			if (!value(REMAP)) {
219
				c = op;
220
				break;
221
			}
222
			if (++maphopcnt > 256)
223
				error("Infinite macro loop");
224
		} while (c != op);
225
 
226
		/*
227
		 * Begin to build an image of this command for possible
228
		 * later repeat in the buffer workcmd.  It will be copied
229
		 * to lastcmd by the routine setLAST
230
		 * if/when completely specified.
231
		 */
232
		lastcp = workcmd;
233
		if (!vglobp)
234
			*lastcp++ = c;
235
 
236
		/*
237
		 * First level command decode.
238
		 */
239
		switch (c) {
240
 
241
		/*
242
		 * ^L		Clear screen e.g. after transmission error.
243
		 */
244
 
245
		/*
246
		 * ^R		Retype screen, getting rid of @ lines.
247
		 *		If in open, equivalent to ^L.
248
		 *		On terminals where the right arrow key sends
249
		 *		^L we make ^R act like ^L, since there is no
250
		 *		way to get ^L.  These terminals (adm31, tvi)
251
		 *		are intelligent so ^R is useless.  Soroc
252
		 *		will probably foul this up, but nobody has
253
		 *		one of them.
254
		 */
255
		case CTRL('l'):
256
		case CTRL('r'):
257
			if (c == CTRL('l') || (KR && *KR==CTRL('l'))) {
258
				vclear();
259
				vdirty(0, vcnt);
260
			}
261
			if (state != VISUAL) {
262
				/*
263
				 * Get a clean line, throw away the
264
				 * memory of what is displayed now,
265
				 * and move back onto the current line.
266
				 */
267
				vclean();
268
				vcnt = 0;
269
				vmoveto(dot, cursor, 0);
270
				continue;
271
			}
272
			vredraw(WTOP);
273
			/*
274
			 * Weird glitch -- when we enter visual
275
			 * in a very small window we may end up with
276
			 * no lines on the screen because the line
277
			 * at the top is too long.  This forces the screen
278
			 * to be expanded to make room for it (after
279
			 * we have printed @'s ick showing we goofed).
280
			 */
281
			if (vcnt == 0)
282
				vrepaint(cursor);
283
			vfixcurs();
284
			continue;
285
 
286
		/*
287
		 * $		Escape just cancels the current command
288
		 *		with a little feedback.
289
		 */
290
		case ESCAPE:
291
			beep();
292
			continue;
293
 
294
		/*
295
		 * @   		Macros. Bring in the macro and put it
296
		 *		in vmacbuf, point vglobp there and punt.
297
		 */
298
		 case '@':
299
			c = getesc();
300
			if (c == 0)
301
				continue;
302
			if (c == '@')
303
				c = lastmac;
304
			if (isupper(c))
305
				c = tolower(c);
306
			forbid(!islower(c));
307
			lastmac = c;
308
			vsave();
309
			CATCH
310
				char tmpbuf[BUFSIZ];
311
 
312
				regbuf(c,tmpbuf,sizeof(vmacbuf));
313
				macpush(tmpbuf, 1);
314
			ONERR
315
				lastmac = 0;
316
				splitw = 0;
317
				getDOT();
318
				vrepaint(cursor);
319
				continue;
320
			ENDCATCH
321
			vmacp = vmacbuf;
322
			goto reread;
323
 
324
		/*
325
		 * .		Repeat the last (modifying) open/visual command.
326
		 */
327
		case '.':
328
			/*
329
			 * Check that there was a last command, and
330
			 * take its count and named buffer unless they
331
			 * were given anew.  Special case if last command
332
			 * referenced a numeric named buffer -- increment
333
			 * the number and go to a named buffer again.
334
			 * This allows a sequence like "1pu.u.u...
335
			 * to successively look for stuff in the kill chain
336
			 * much as one does in EMACS with C-Y and M-Y.
337
			 */
338
			forbid (lastcmd[0] == 0);
339
			if (hadcnt)
340
				lastcnt = cnt;
341
			if (vreg)
342
				lastreg = vreg;
343
			else if (isdigit(lastreg) && lastreg < '9')
344
				lastreg++;
345
			vreg = lastreg;
346
			cnt = lastcnt;
347
			hadcnt = lasthad;
348
			vglobp = lastcmd;
349
			goto reread;
350
 
351
		/*
352
		 * ^U		Scroll up.  A count sticks around for
353
		 *		future scrolls as the scroll amount.
354
		 *		Attempt to hold the indentation from the
355
		 *		top of the screen (in logical lines).
356
		 *
357
		 * BUG:		A ^U near the bottom of the screen
358
		 *		on a dumb terminal (which can't roll back)
359
		 *		causes the screen to be cleared and then
360
		 *		redrawn almost as it was.  In this case
361
		 *		one should simply move the cursor.
362
		 */
363
		case CTRL('u'):
364
			if (hadcnt)
365
				vSCROLL = cnt;
366
			cnt = vSCROLL;
367
			if (state == VISUAL)
368
				ind = vcline, cnt += ind;
369
			else
370
				ind = 0;
371
			vmoving = 0;
372
			vup(cnt, ind, 1);
373
			vnline(NOSTR);
374
			continue;
375
 
376
		/*
377
		 * ^D		Scroll down.  Like scroll up.
378
		 */
379
		case CTRL('d'):
380
#ifdef TRACE
381
		if (trace)
382
			fprintf(trace, "before vdown in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
383
#endif
384
			if (hadcnt)
385
				vSCROLL = cnt;
386
			cnt = vSCROLL;
387
			if (state == VISUAL)
388
				ind = vcnt - vcline - 1, cnt += ind;
389
			else
390
				ind = 0;
391
			vmoving = 0;
392
			vdown(cnt, ind, 1);
393
#ifdef TRACE
394
		if (trace)
395
			fprintf(trace, "before vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
396
#endif
397
			vnline(NOSTR);
398
#ifdef TRACE
399
		if (trace)
400
			fprintf(trace, "after vnline in ^D, dot=%d, wdot=%d, dol=%d\n", lineno(dot), lineno(wdot), lineno(dol));
401
#endif
402
			continue;
403
 
404
		/*
405
		 * ^E		Glitch the screen down (one) line.
406
		 *		Cursor left on same line in file.
407
		 */
408
		case CTRL('e'):
409
			if (state != VISUAL)
410
				continue;
411
			if (!hadcnt)
412
				cnt = 1;
413
			/* Bottom line of file already on screen */
414
			forbid(lineDOL()-lineDOT() <= vcnt-1-vcline);
415
			ind = vcnt - vcline - 1 + cnt;
416
			vdown(ind, ind, 1);
417
			vnline(cursor);
418
			continue;
419
 
420
		/*
421
		 * ^Y		Like ^E but up
422
		 */
423
		case CTRL('y'):
424
			if (state != VISUAL)
425
				continue;
426
			if (!hadcnt)
427
				cnt = 1;
428
			forbid(lineDOT()-1<=vcline); /* line 1 already there */
429
			ind = vcline + cnt;
430
			vup(ind, ind, 1);
431
			vnline(cursor);
432
			continue;
433
 
434
 
435
		/*
436
		 * m		Mark position in mark register given
437
		 *		by following letter.  Return is
438
		 *		accomplished via ' or `; former
439
		 *		to beginning of line where mark
440
		 *		was set, latter to column where marked.
441
		 */
442
		case 'm':
443
			/*
444
			 * Getesc is generally used when a character
445
			 * is read as a latter part of a command
446
			 * to allow one to hit rubout/escape to cancel
447
			 * what you have typed so far.  These characters
448
			 * are mapped to 0 by the subroutine.
449
			 */
450
			c = getesc();
451
			if (c == 0)
452
				continue;
453
 
454
			/*
455
			 * Markreg checks that argument is a letter
456
			 * and also maps ' and ` to the end of the range
457
			 * to allow '' or `` to reference the previous
458
			 * context mark.
459
			 */
460
			c = markreg(c);
461
			forbid (c == 0);
462
			vsave();
463
			names[c - 'a'] = (*dot &~ 01);
464
			ncols[c - 'a'] = cursor;
465
			anymarks = 1;
466
			continue;
467
 
468
		/*
469
		 * ^F		Window forwards, with 2 lines of continuity.
470
		 *		Count repeats.
471
		 */
472
		case CTRL('f'):
473
			vsave();
474
			if (vcnt > 2) {
475
				addr = dot + (vcnt - vcline) - 2 + (cnt-1)*basWLINES;
476
				forbid(addr > dol);
477
				dot = addr;
478
				vcnt = vcline = 0;
479
			}
480
			vzop(0, 0, '+');
481
			continue;
482
 
483
		/*
484
		 * ^B		Window backwards, with 2 lines of continuity.
485
		 *		Inverse of ^F.
486
		 */
487
		case CTRL('b'):
488
			vsave();
489
			if (one + vcline != dot && vcnt > 2) {
490
				addr = dot - vcline + 2 - (cnt-1)*basWLINES;
491
				forbid (addr <= zero);
492
				dot = addr;
493
				vcnt = vcline = 0;
494
			}
495
			vzop(0, 0, '^');
496
			continue;
497
 
498
		/*
499
		 * z		Screen adjustment, taking a following character:
500
		 *			z<CR>		current line to top
501
		 *			z<NL>		like z<CR>
502
		 *			z-		current line to bottom
503
		 *		also z+, z^ like ^F and ^B.
504
		 *		A preceding count is line to use rather
505
		 *		than current line.  A count between z and
506
		 *		specifier character changes the screen size
507
		 *		for the redraw.
508
		 *
509
		 */
510
		case 'z':
511
			if (state == VISUAL) {
512
				i = vgetcnt();
513
				if (i > 0)
514
					vsetsiz(i);
515
				c = getesc();
516
				if (c == 0)
517
					continue;
518
			}
519
			vsave();
520
			vzop(hadcnt, cnt, c);
521
			continue;
522
 
523
		/*
524
		 * Y		Yank lines, abbreviation for y_ or yy.
525
		 *		Yanked lines can be put later if no
526
		 *		changes intervene, or can be put in named
527
		 *		buffers and put anytime in this session.
528
		 */
529
		case 'Y':
530
			ungetkey('_');
531
			c = 'y';
532
			break;
533
 
534
		/*
535
		 * J		Join lines, 2 by default.  Count is number
536
		 *		of lines to join (no join operator sorry.)
537
		 */
538
		case 'J':
539
			forbid (dot == dol);
540
			if (cnt == 1)
541
				cnt = 2;
542
			if (cnt > (i = dol - dot + 1))
543
				cnt = i;
544
			vsave();
545
			vmacchng(1);
546
			setLAST();
547
			cursor = strend(linebuf);
548
			vremote(cnt, join, 0);
549
			notenam = "join";
550
			vmoving = 0;
551
			killU();
552
			vreplace(vcline, cnt, 1);
553
			if (!*cursor && cursor > linebuf)
554
				cursor--;
555
			if (notecnt == 2)
556
				notecnt = 0;
557
			vrepaint(cursor);
558
			continue;
559
 
560
		/*
561
		 * S		Substitute text for whole lines, abbrev for c_.
562
		 *		Count is number of lines to change.
563
		 */
564
		case 'S':
565
			ungetkey('_');
566
			c = 'c';
567
			break;
568
 
569
		/*
570
		 * O		Create a new line above current and accept new
571
		 *		input text, to an escape, there.
572
		 *		A count specifies, for dumb terminals when
573
		 *		slowopen is not set, the number of physical
574
		 *		line space to open on the screen.
575
		 *
576
		 * o		Like O, but opens lines below.
577
		 */
578
		case 'O':
579
		case 'o':
580
			vmacchng(1);
581
			voOpen(c, cnt);
582
			continue;
583
 
584
		/*
585
		 * C		Change text to end of line, short for c$.
586
		 */
587
		case 'C':
588
			if (*cursor) {
589
				ungetkey('$'), c = 'c';
590
				break;
591
			}
592
			goto appnd;
593
 
594
		/*
595
		 * ~	Switch case of letter under cursor
596
		 */
597
		case '~':
598
			{
599
				char mbuf[4];
600
				setLAST();
601
				mbuf[0] = 'r';
602
				mbuf[1] = *cursor;
603
				mbuf[2] = cursor[1]==0 ? 0 : ' ';
604
				mbuf[3] = 0;
605
				if (isalpha(mbuf[1]))
606
					mbuf[1] ^= ' ';	/* toggle the case */
607
				macpush(mbuf, 1);
608
			}
609
			continue;
610
 
611
 
612
		/*
613
		 * A		Append at end of line, short for $a.
614
		 */
615
		case 'A':
616
			operate('$', 1);
617
appnd:
618
			c = 'a';
619
			/* fall into ... */
620
 
621
		/*
622
		 * a		Appends text after cursor.  Text can continue
623
		 *		through arbitrary number of lines.
624
		 */
625
		case 'a':
626
			if (*cursor) {
627
				if (state == HARDOPEN)
628
					putchar(*cursor);
629
				cursor++;
630
			}
631
			goto insrt;
632
 
633
		/*
634
		 * I		Insert at beginning of whitespace of line,
635
		 *		short for ^i.
636
		 */
637
		case 'I':
638
			operate('^', 1);
639
			c = 'i';
640
			/* fall into ... */
641
 
642
		/*
643
		 * R		Replace characters, one for one, by input
644
		 *		(logically), like repeated r commands.
645
		 *
646
		 * BUG:		This is like the typeover mode of many other
647
		 *		editors, and is only rarely useful.  Its
648
		 *		implementation is a hack in a low level
649
		 *		routine and it doesn't work very well, e.g.
650
		 *		you can't move around within a R, etc.
651
		 */
652
		case 'R':
653
			/* fall into... */
654
 
655
		/*
656
		 * i		Insert text to an escape in the buffer.
657
		 *		Text is arbitrary.  This command reminds of
658
		 *		the i command in bare teco.
659
		 */
660
		case 'i':
661
insrt:
662
			/*
663
			 * Common code for all the insertion commands.
664
			 * Save for redo, position cursor, prepare for append
665
			 * at command and in visual undo.  Note that nothing
666
			 * is doomed, unless R when all is, and save the
667
			 * current line in a the undo temporary buffer.
668
			 */
669
			vmacchng(1);
670
			setLAST();
671
			vcursat(cursor);
672
			prepapp();
673
			vnoapp();
674
			doomed = c == 'R' ? 10000 : 0;
675
			if(FIXUNDO)
676
				vundkind = VCHNG;
677
			vmoving = 0;
678
#ifndef	BIT8
115 7u83 679
			CP(vutmp, linebuf);
105 7u83 680
#else
681
			sc_strcpy(vutmp, linebuf);
682
#endif
683
 
684
			/*
685
			 * If this is a repeated command, then suppress
686
			 * fake insert mode on dumb terminals which looks
687
			 * ridiculous and wastes lots of time even at 9600B.
688
			 */
689
			if (vglobp)
690
				hold = HOLDQIK;
691
			vappend(c, cnt, 0);
692
			continue;
693
 
694
		/*
695
		 * ^?		An attention, normally a ^?, just beeps.
696
		 *		If you are a vi command within ex, then
697
		 *		two ATTN's will drop you back to command mode.
698
		 */
699
		case ATTN:
700
			beep();
701
			if (initev || peekkey() != ATTN)
702
				continue;
703
			/* fall into... */
704
 
705
		/*
706
		 * ^\		A quit always gets command mode.
707
		 */
708
		case QUIT:
709
			/*
710
			 * Have to be careful if we were called
711
			 *	g/xxx/vi
712
			 * since a return will just start up again.
713
			 * So we simulate an interrupt.
714
			 */
715
			if (inglobal)
716
				onintr();
717
			/* fall into... */
718
 
719
#ifdef notdef
720
		/*
721
		 * q		Quit back to command mode, unless called as
722
		 *		vi on command line in which case dont do it
723
		 */
724
		case 'q':	/* quit */
725
			if (initev) {
726
				vsave();
727
				CATCH
728
					error("Q gets ex command mode, :q leaves vi");
729
				ENDCATCH
730
				splitw = 0;
731
				getDOT();
732
				vrepaint(cursor);
733
				continue;
734
			}
735
#endif
736
			/* fall into... */
737
 
738
		/*
739
		 * Q		Is like q, but always gets to command mode
740
		 *		even if command line invocation was as vi.
741
		 */
742
		case 'Q':
743
			vsave();
744
			/*
745
			 * If we are in the middle of a macro, throw away
746
			 * the rest and fix up undo.
747
			 * This code copied from getbr().
748
			 */
749
			if (vmacp) {
750
				vmacp = 0;
751
				if (inopen == -1)	/* don't screw up undo for esc esc */
752
					vundkind = VMANY;
753
				inopen = 1;	/* restore old setting now that macro done */
754
			}
755
			return;
756
 
757
 
758
		/*
759
		 * ZZ		Like :x
760
		 */
761
		 case 'Z':
762
			forbid(getkey() != 'Z');
763
			oglobp = globp;
764
#ifndef	BIT8
765
			globp = "x";
766
#else
767
			globp[0] = 'x', globp[1] = '\0';
768
#endif
769
			vclrech(0);
770
			goto gogo;
771
 
772
		/*
773
		 * P		Put back text before cursor or before current
774
		 *		line.  If text was whole lines goes back
775
		 *		as whole lines.  If part of a single line
776
		 *		or parts of whole lines splits up current
777
		 *		line to form many new lines.
778
		 *		May specify a named buffer, or the delete
779
		 *		saving buffers 1-9.
780
		 *
781
		 * p		Like P but after rather than before.
782
		 */
783
		case 'P':
784
		case 'p':
785
			vmoving = 0;
786
#ifdef notdef
787
			forbid (!vreg && value(UNDOMACRO) && inopen < 0);
788
#endif
789
			/*
790
			 * If previous delete was partial line, use an
791
			 * append or insert to put it back so as to
792
			 * use insert mode on intelligent terminals.
793
			 */
794
			if (!vreg && DEL[0]) {
795
				forbid ((DEL[0] & (QUOTE|TRIM)) == OVERBUF);
796
				vglobp = DEL;
797
				ungetkey(c == 'p' ? 'a' : 'i');
798
				goto reread;
799
			}
800
 
801
			/*
802
			 * If a register wasn't specified, then make
803
			 * sure there is something to put back.
804
			 */
805
			forbid (!vreg && unddol == dol);
806
			/*
807
			 * If we just did a macro the whole buffer is in
808
			 * the undo save area.  We don't want to put THAT.
809
			 */
810
			forbid (vundkind == VMANY && undkind==UNDALL);
811
			vsave();
812
			vmacchng(1);
813
			setLAST();
814
			i = 0;
815
			if (vreg && partreg(vreg) || !vreg && pkill[0]) {
816
				/*
817
				 * Restoring multiple lines which were partial
818
				 * lines; will leave cursor in middle
819
				 * of line after shoving restored text in to
820
				 * split the current line.
821
				 */
822
				i++;
823
				if (c == 'p' && *cursor)
824
					cursor++;
825
			} else {
826
				/*
827
				 * In whole line case, have to back up dot
828
				 * for P; also want to clear cursor so
829
				 * cursor will eventually be positioned
830
				 * at the beginning of the first put line.
831
				 */
832
				cursor = 0;
833
				if (c == 'P') {
834
					dot--, vcline--;
835
					c = 'p';
836
				}
837
			}
838
			killU();
839
 
840
			/*
841
			 * The call to putreg can potentially
842
			 * bomb since there may be nothing in a named buffer.
843
			 * We thus put a catch in here.  If we didn't and
844
			 * there was an error we would end up in command mode.
845
			 */
846
			addr = dol;	/* old dol */
847
			CATCH
848
				vremote(1, vreg ? putreg : put, vreg);
849
			ONERR
850
				if (vreg == -1) {
851
					splitw = 0;
852
					if (op == 'P')
853
						dot++, vcline++;
854
					goto pfixup;
855
				}
856
			ENDCATCH
857
			splitw = 0;
858
			nlput = dol - addr + 1;
859
			if (!i) {
860
				/*
861
				 * Increment undap1, undap2 to make up
862
				 * for their incorrect initialization in the
863
				 * routine vremote before calling put/putreg.
864
				 */
865
				if (FIXUNDO)
866
					undap1++, undap2++;
867
				vcline++;
868
				nlput--;
869
 
870
				/*
871
				 * After a put want current line first line,
872
				 * and dot was made the last line put in code
873
				 * run so far.  This is why we increment vcline
874
				 * above and decrease dot here.
875
				 */
876
				dot -= nlput - 1;
877
			}
878
#ifdef TRACE
879
			if (trace)
880
				fprintf(trace, "vreplace(%d, %d, %d), undap1=%d, undap2=%d, dot=%d\n", vcline, i, nlput, lineno(undap1), lineno(undap2), lineno(dot));
881
#endif
882
			vreplace(vcline, i, nlput);
883
			if (state != VISUAL) {
884
				/*
885
				 * Special case in open mode.
886
				 * Force action on the screen when a single
887
				 * line is put even if it is identical to
888
				 * the current line, e.g. on YP; otherwise
889
				 * you can't tell anything happened.
890
				 */
891
				vjumpto(dot, cursor, '.');
892
				continue;
893
			}
894
pfixup:
895
			vrepaint(cursor);
896
			vfixcurs();
897
			continue;
898
 
899
		/*
900
		 * ^^		Return to previous file.
901
		 *		Like a :e #, and thus can be used after a
902
		 *		"No Write" diagnostic.
903
		 */
904
		case CTRL('^'):
905
			forbid (hadcnt);
906
			vsave();
907
			ckaw();
908
			oglobp = globp;
909
			if (value(AUTOWRITE))
910
#ifndef	BIT8
911
				globp = "e! #";
912
			else
913
				globp = "e #";
914
#else
915
				sc_strcpy(globp, "e! #");
916
			else
917
				sc_strcpy(globp, "e #");
918
#endif
919
			goto gogo;
920
 
921
		/*
922
		 * ^]		Takes word after cursor as tag, and then does
923
		 *		tag command.  Read ``go right to''.
924
		 */
925
		case CTRL(']'):
926
			grabtag();
927
			oglobp = globp;
928
#ifndef	BIT8
929
			globp = "tag";
930
#else
931
			sc_strcpy(globp, "tag");
932
#endif
933
			goto gogo;
934
 
935
		/*
936
		 * &		Like :&
937
		 */
938
		 case '&':
939
			oglobp = globp;
940
#ifndef	BIT8
941
			globp = "&";
942
#else
943
			globp[0] = '&', globp[1] = '\0';
944
#endif
945
			goto gogo;
946
 
947
		/*
948
		 * ^G		Bring up a status line at the bottom of
949
		 *		the screen, like a :file command.
950
		 *
951
		 * BUG:		Was ^S but doesn't work in cbreak mode
952
		 */
953
		case CTRL('g'):
954
			oglobp = globp;
955
#ifndef	BIT8
956
			globp = "file";
957
#else
958
			sc_strcpy(globp, "file");
959
#endif
960
gogo:
961
			addr = dot;
962
			vsave();
963
			goto doinit;
964
 
965
#ifdef SIGTSTP
966
		/*
967
		 * ^Z:	suspend editor session and temporarily return
968
		 * 	to shell.  Only works with Berkeley/IIASA process
969
		 *	control in kernel.
970
		 */
971
		case CTRL('z'):
972
			forbid(dosusp == 0 || !ldisc);
973
			vsave();
974
			oglobp = globp;
975
#ifndef	BIT8
976
			globp = "stop";
977
#else
978
			sc_strcpy(globp, "stop");
979
#endif
980
			goto gogo;
981
#endif
982
 
983
		/*
984
		 * :		Read a command from the echo area and
985
		 *		execute it in command mode.
986
		 */
987
		case ':':
988
			forbid (hadcnt);
989
			vsave();
990
			i = tchng;
991
			addr = dot;
992
			if (readecho(c)) {
993
				esave[0] = 0;
994
				goto fixup;
995
			}
996
			getDOT();
997
#ifndef	BIT8
998
			/*
999
			 * Use the visual undo buffer to store the global
1000
			 * string for command mode, since it is idle right now.
1001
			 */
1002
			oglobp = globp; strcpy(vutmp, genbuf+1); globp = vutmp;
1003
#else
1004
			oglobp = globp; sc_strcpy(vutmp, genbuf+1);
1005
			globp = vutmp;
1006
#endif
1007
doinit:
1008
			esave[0] = 0;
1009
			fixech();
1010
 
1011
			/*
1012
			 * Have to finagle around not to lose last
1013
			 * character after this command (when run from ex
1014
			 * command mode).  This is clumsy.
1015
			 */
1016
			d = peekc; ungetchar(0);
1017
			if (shouldpo) {
1018
				/*
1019
				 * So after a "Hit return..." ":", we do
1020
				 * another "Hit return..." the next time
1021
				 */
1022
				pofix();
1023
				shouldpo = 0;
1024
			}
1025
			CATCH
1026
				/*
1027
				 * Save old values of options so we can
1028
				 * notice when they change; switch into
1029
				 * cooked mode so we are interruptible.
1030
				 */
1031
				onumber = value(NUMBER);
1032
				olist = value(LIST);
1033
				OPline = Pline;
1034
				OPutchar = Putchar;
1035
#ifndef CBREAK
1036
				vcook();
1037
#endif
1038
				commands(1, 1);
1039
				if (dot == zero && dol > zero)
1040
					dot = one;
1041
#ifndef CBREAK
1042
				vraw();
1043
#endif
1044
			ONERR
1045
#ifndef CBREAK
1046
				vraw();
1047
#endif
1048
				copy(esave, vtube[WECHO], TUBECOLS);
1049
			ENDCATCH
1050
			fixol();
1051
			Pline = OPline;
1052
			Putchar = OPutchar;
1053
			ungetchar(d);
1054
			globp = oglobp;
1055
 
1056
			/*
1057
			 * If we ended up with no lines in the buffer, make
1058
			 * a line, and don't consider the buffer changed.
1059
			 */
1060
			if (dot == zero) {
1061
				fixzero();
1062
				sync();
1063
			}
1064
			splitw = 0;
1065
 
1066
			/*
1067
			 * Special case: did list/number options change?
1068
			 */
1069
			if (onumber != value(NUMBER))
1070
				setnumb(value(NUMBER));
1071
			if (olist != value(LIST))
1072
				setlist(value(LIST));
1073
 
1074
fixup:
1075
			/*
1076
			 * If a change occurred, other than
1077
			 * a write which clears changes, then
1078
			 * we should allow an undo even if .
1079
			 * didn't move.
1080
			 *
1081
			 * BUG: You can make this wrong by
1082
			 * tricking around with multiple commands
1083
			 * on one line of : escape, and including
1084
			 * a write command there, but its not
1085
			 * worth worrying about.
1086
			 */
1087
			if (FIXUNDO && tchng && tchng != i)
1088
				vundkind = VMANY, cursor = 0;
1089
 
1090
			/*
1091
			 * If we are about to do another :, hold off
1092
			 * updating of screen.
1093
			 */
1094
			if (vcnt < 0 && Peekkey == ':') {
1095
				getDOT();
1096
				shouldpo = 1;
1097
				continue;
1098
			}
1099
			shouldpo = 0;
1100
 
1101
			/*
1102
			 * In the case where the file being edited is
1103
			 * new; e.g. if the initial state hasn't been
1104
			 * saved yet, then do so now.
1105
			 */
1106
			if (unddol == truedol) {
1107
				vundkind = VNONE;
1108
				Vlines = lineDOL();
1109
				if (!inglobal)
1110
					savevis();
1111
				addr = zero;
1112
				vcnt = 0;
1113
				if (esave[0] == 0)
1114
					copy(esave, vtube[WECHO], TUBECOLS);
1115
			}
1116
 
1117
			/*
1118
			 * If the current line moved reset the cursor position.
1119
			 */
1120
			if (dot != addr) {
1121
				vmoving = 0;
1122
				cursor = 0;
1123
			}
1124
 
1125
			/*
1126
			 * If current line is not on screen or if we are
1127
			 * in open mode and . moved, then redraw.
1128
			 */
1129
			i = vcline + (dot - addr);
1130
			if (i < 0 || i >= vcnt && i >= -vcnt || state != VISUAL && dot != addr) {
1131
				if (state == CRTOPEN)
1132
					vup1();
1133
				if (vcnt > 0)
1134
					vcnt = 0;
1135
				vjumpto(dot, (char *) 0, '.');
1136
			} else {
1137
				/*
1138
				 * Current line IS on screen.
1139
				 * If we did a [Hit return...] then
1140
				 * restore vcnt and clear screen if in visual
1141
				 */
1142
				vcline = i;
1143
				if (vcnt < 0) {
1144
					vcnt = -vcnt;
1145
					if (state == VISUAL)
1146
						vclear();
1147
					else if (state == CRTOPEN) {
1148
						vcnt = 0;
1149
					}
1150
				}
1151
 
1152
				/*
1153
				 * Limit max value of vcnt based on $
1154
				 */
1155
				i = vcline + lineDOL() - lineDOT() + 1;
1156
				if (i < vcnt)
1157
					vcnt = i;
1158
 
1159
				/*
1160
				 * Dirty and repaint.
1161
				 */
1162
				vdirty(0, LINES);
1163
				vrepaint(cursor);
1164
			}
1165
 
1166
			/*
1167
			 * If in visual, put back the echo area
1168
			 * if it was clobberred.
1169
			 */
1170
			if (state == VISUAL) {
1171
				int sdc = destcol, sdl = destline;
1172
 
1173
				splitw++;
1174
				vigoto(WECHO, 0);
1175
				for (i = 0; i < TUBECOLS - 1; i++) {
1176
					if (esave[i] == 0)
1177
						break;
1178
					vputchar(esave[i]);
1179
				}
1180
				splitw = 0;
1181
				vgoto(sdl, sdc);
1182
			}
1183
			continue;
1184
 
1185
		/*
1186
		 * u		undo the last changing command.
1187
		 */
1188
		case 'u':
1189
			vundo(1);
1190
			continue;
1191
 
1192
		/*
1193
		 * U		restore current line to initial state.
1194
		 */
1195
		case 'U':
1196
			vUndo();
1197
			continue;
1198
 
1199
fonfon:
1200
			beep();
1201
			vmacp = 0;
1202
			inopen = 1;	/* might have been -1 */
1203
			continue;
1204
		}
1205
 
1206
		/*
1207
		 * Rest of commands are decoded by the operate
1208
		 * routine.
1209
		 */
1210
		operate(c, cnt);
1211
	}
1212
}
1213
 
1214
/*
1215
 * Grab the word after the cursor so we can look for it as a tag.
1216
 */
1217
grabtag()
1218
{
1219
	register char *cp, *dp;
1220
 
1221
	cp = vpastwh(cursor);
1222
	if (*cp) {
1223
		dp = lasttag;
1224
		do {
1225
			if (dp < &lasttag[sizeof lasttag - 2])
1226
				*dp++ = *cp;
1227
			cp++;
1228
		} while (isalpha(*cp) || isdigit(*cp) || *cp == '_'
1229
#ifdef LISPCODE
1230
			|| (value(LISP) && *cp == '-')
108 7u83 1231
#endif /*LISPCODE*/
105 7u83 1232
			);
1233
		*dp++ = 0;
1234
	}
1235
}
1236
 
1237
/*
1238
 * Before appending lines, set up addr1 and
1239
 * the command mode undo information.
1240
 */
1241
prepapp()
1242
{
1243
 
1244
	addr1 = dot;
1245
	deletenone();
1246
	addr1++;
1247
	appendnone();
1248
}
1249
 
1250
/*
1251
 * Execute function f with the address bounds addr1
1252
 * and addr2 surrounding cnt lines starting at dot.
1253
 */
1254
vremote(cnt, f, arg)
115 7u83 1255
	int cnt, (*f)(), arg;
105 7u83 1256
{
1257
	register int oing = inglobal;
1258
 
1259
	addr1 = dot;
1260
	addr2 = dot + cnt - 1;
1261
	inglobal = 0;
1262
	if (FIXUNDO)
1263
		undap1 = undap2 = dot;
1264
	(*f)(arg);
1265
	inglobal = oing;
1266
	if (FIXUNDO)
1267
		vundkind = VMANY;
1268
	vmcurs = 0;
1269
}
1270
 
1271
/*
1272
 * Save the current contents of linebuf, if it has changed.
1273
 */
1274
vsave()
1275
{
1276
	char temp[LBSIZE];
1277
 
115 7u83 1278
	CP(temp, linebuf);
105 7u83 1279
	if (FIXUNDO && vundkind == VCHNG || vundkind == VCAPU) {
1280
		/*
1281
		 * If the undo state is saved in the temporary buffer
1282
		 * vutmp, then we sync this into the temp file so that
1283
		 * we will be able to undo even after we have moved off
1284
		 * the line.  It would be possible to associate a line
1285
		 * with vutmp but we assume that vutmp is only associated
1286
		 * with line dot (e.g. in case ':') above, so beware.
1287
		 */
1288
		prepapp();
1289
#ifndef	BIT8
1290
		strcLIN(vutmp);
1291
#else
1292
		cs_strcpy(linebuf, vutmp);
1293
#endif
1294
		putmark(dot);
1295
		vremote(1, yank, 0);
1296
		vundkind = VMCHNG;
1297
		notecnt = 0;
1298
		undkind = UNDCHANGE;
1299
	}
1300
	/*
1301
	 * Get the line out of the temp file and do nothing if it hasn't
1302
	 * changed.  This may seem like a loss, but the line will
1303
	 * almost always be in a read buffer so this may well avoid disk i/o.
1304
	 */
1305
	getDOT();
1306
	if (strcmp(linebuf, temp) == 0)
1307
		return;
1308
	strcLIN(temp);
1309
	putmark(dot);
1310
}
1311
 
1312
#undef	forbid
1313
#define	forbid(a)	if (a) { beep(); return; }
1314
 
1315
/*
1316
 * Do a z operation.
1317
 * Code here is rather long, and very uninteresting.
1318
 */
1319
vzop(hadcnt, cnt, c)
1320
	bool hadcnt;
1321
	int cnt;
1322
	register int c;
1323
{
1324
	register line *addr;
1325
 
1326
	if (state != VISUAL) {
1327
		/*
1328
		 * Z from open; always like a z=.
1329
		 * This code is a mess and should be cleaned up.
1330
		 */
1331
		vmoveitup(1, 1);
1332
		vgoto(outline, 0);
1333
		ostop(normf);
1334
		setoutt();
1335
		addr2 = dot;
1336
		vclear();
1337
		destline = WECHO;
1338
		zop2(Xhadcnt ? Xcnt : value(WINDOW) - 1, '=');
1339
		if (state == CRTOPEN)
1340
			putnl();
1341
		putNFL();
1342
		termreset();
1343
		Outchar = vputchar;
1344
		ignore(ostart());
1345
		vcnt = 0;
1346
		outline = destline = 0;
1347
		vjumpto(dot, cursor, 0);
1348
		return;
1349
	}
1350
	if (hadcnt) {
1351
		addr = zero + cnt;
1352
		if (addr < one)
1353
			addr = one;
1354
		if (addr > dol)
1355
			addr = dol;
1356
		markit(addr);
1357
	} else
1358
		switch (c) {
1359
 
1360
		case '+':
1361
			addr = dot + vcnt - vcline;
1362
			break;
1363
 
1364
		case '^':
1365
			addr = dot - vcline - 1;
1366
			forbid (addr < one);
1367
			c = '-';
1368
			break;
1369
 
1370
		default:
1371
			addr = dot;
1372
			break;
1373
		}
1374
	switch (c) {
1375
 
1376
	case '.':
1377
	case '-':
1378
		break;
1379
 
1380
	case '^':
1381
		forbid (addr <= one);
1382
		break;
1383
 
1384
	case '+':
1385
		forbid (addr >= dol);
1386
		/* fall into ... */
1387
 
1388
	case CR:
1389
	case NL:
1390
		c = CR;
1391
		break;
1392
 
1393
	default:
1394
		beep();
1395
		return;
1396
	}
1397
	vmoving = 0;
1398
	vjumpto(addr, NOSTR, c);
1399
}