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