xref: /illumos-gate/usr/src/cmd/vi/port/ex_cmds2.c (revision 2a8bcb4e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 /* Copyright (c) 1981 Regents of the University of California */
31 
32 #include "ex.h"
33 #include "ex_argv.h"
34 #include "ex_temp.h"
35 #include "ex_tty.h"
36 #include "ex_vis.h"
37 #include <unistd.h>
38 
39 extern bool	pflag, nflag;		/* extern; also in ex_cmds.c */
40 extern int	poffset;		/* extern; also in ex_cmds.c */
41 extern short	slevel;			/* extern; has level of source() */
42 
43 /*
44  * Subroutines for major command loop.
45  */
46 
47 /*
48  * Is there a single letter indicating a named buffer next?
49  */
50 int
cmdreg(void)51 cmdreg(void)
52 {
53 	int c = 0;
54 	int wh = skipwh();
55 
56 #ifdef XPG4
57 	if (wh && isalpha(c = peekchar()) && isascii(c) && !isdigit(c))
58 #else /* XPG4 */
59 	if (wh && isalpha(c = peekchar()) && isascii(c))
60 #endif /* XPG4 */
61 		c = getchar();
62 
63 #ifdef XPG4
64 	if (isdigit(c)) {
65 		c = 0;
66 	}
67 #endif /* XPG4 */
68 	return (c);
69 }
70 
71 /*
72  * Tell whether the character ends a command
73  */
74 int
endcmd(int ch)75 endcmd(int ch)
76 {
77 	switch (ch) {
78 
79 	case '\n':
80 	case EOF:
81 		endline = 1;
82 		return (1);
83 
84 	case '|':
85 	case '"':
86 		endline = 0;
87 		return (1);
88 	}
89 	return (0);
90 }
91 
92 /*
93  * Insist on the end of the command.
94  */
95 void
eol(void)96 eol(void)
97 {
98 
99 	if (!skipend())
100 		error(value(vi_TERSE) ? gettext("Extra chars") :
101 			gettext("Extra characters at end of command"));
102 	ignnEOF();
103 }
104 
105 #ifdef XPG4
106 /*
107  * Print out the message in the error message file at str,
108  * with i an integer argument to printf.
109  */
110 /*VARARGS2*/
111 void
error(str,i)112 error(str, i)
113 	unsigned char *str;
114 	int i;
115 {
116 	tagflg = 0;
117 	errcnt++;
118 	noerror(str, i);
119 }
120 
121 /*
122  * noerror(): like error(), but doesn't inc errcnt.
123  * the reason why we created this routine, instead of fixing up errcnt
124  * after error() is called, is because we will do a longjmp, and
125  * not a return. it does other things closing file i/o, reset, etc;
126  * so we follow those procedures.
127  */
128 /*VARARGS2*/
129 void
noerror(str,i)130 noerror(str, i)
131 	unsigned char *str;
132 	int i;
133 {
134 
135 	error0();
136 	merror(str, i);
137 	if (writing) {
138 		serror((unsigned char *)
139 		    gettext(" [Warning - %s is incomplete]"), file);
140 		writing = 0;
141 	}
142 	error1(str);
143 }
144 
145 #else /* !XPG4 */
146 /*
147  * Print out the message in the error message file at str,
148  * with i an integer argument to printf.
149  */
150 /*VARARGS2*/
151 void
error(str,i)152 error(str, i)
153 	unsigned char *str;
154 	int i;
155 {
156 	tagflg = 0;
157 	errcnt++;
158 	error0();
159 	merror(str, i);
160 	if (writing) {
161 		serror((unsigned char *)
162 		    gettext(" [Warning - %s is incomplete]"), file);
163 		writing = 0;
164 	}
165 	error1(str);
166 }
167 #endif /* XPG4 */
168 
169 /*
170  * Rewind the argument list.
171  */
172 void
erewind(void)173 erewind(void)
174 {
175 
176 	argc = argc0;
177 	argv = argv0;
178 	args = args0;
179 	if (argc > 1 && !hush && cur_term) {
180 		viprintf(mesg(value(vi_TERSE) ? gettext("%d files") :
181 			gettext("%d files to edit")), argc);
182 		if (inopen)
183 			putchar(' ');
184 		else
185 			putNFL();
186 	}
187 }
188 
189 /*
190  * Guts of the pre-printing error processing.
191  * If in visual and catching errors, then we don't mung up the internals,
192  * just fixing up the echo area for the print.
193  * Otherwise we reset a number of externals, and discard unused input.
194  */
195 void
error0(void)196 error0(void)
197 {
198 
199 	if (laste) {
200 #ifdef VMUNIX
201 		tlaste();
202 #endif
203 		laste = 0;
204 		sync();
205 	}
206 	if (vcatch) {
207 		if (splitw == 0)
208 			fixech();
209 		if (!enter_standout_mode || !exit_bold)
210 			dingdong();
211 		return;
212 	}
213 	if (input) {
214 		input = strend(input) - 1;
215 		if (*input == '\n')
216 			setlastchar('\n');
217 		input = 0;
218 	}
219 	setoutt();
220 	flush();
221 	resetflav();
222 	if (!enter_standout_mode || !exit_bold)
223 		dingdong();
224 	if (inopen) {
225 		/*
226 		 * We are coming out of open/visual ungracefully.
227 		 * Restore columns, undo, and fix tty mode.
228 		 */
229 		columns = OCOLUMNS;
230 		undvis();
231 		ostop(normf);
232 		/* ostop should be doing this
233 		putpad(cursor_normal);
234 		putpad(key_eol);
235 		*/
236 		putnl();
237 	}
238 	inopen = 0;
239 	holdcm = 0;
240 }
241 
242 /*
243  * Post error printing processing.
244  * Close the i/o file if left open.
245  * If catching in visual then throw to the visual catch,
246  * else if a child after a fork, then exit.
247  * Otherwise, in the normal command mode error case,
248  * finish state reset, and throw to top.
249  */
250 void
error1(unsigned char * str)251 error1(unsigned char *str)
252 {
253 	bool die;
254 	extern short ttyindes;
255 
256 	if ((io > 0) && (io != ttyindes)) {
257 		close(io);
258 		io = -1;
259 	}
260 
261 	die = (getpid() != ppid);	/* Only children die */
262 	inappend = inglobal = 0;
263 	globp = vglobp = vmacp = 0;
264 	if (vcatch && !die) {
265 		inopen = 1;
266 		vcatch = 0;
267 		if (str)
268 			noonl();
269 		fixol();
270 		if (slevel > 0)
271 			reset();
272 		longjmp(vreslab,1);
273 	}
274 	if (str && !vcatch)
275 		putNFL();
276 	if (die)
277 		exit(++errcnt);
278 	lseek(0, 0L, 2);
279 	if (inglobal)
280 		setlastchar('\n');
281 
282 	if (inexrc) {
283 		/*
284 		 * Set inexrc to 0 so that this error is printed only
285 		 * once (eg. when stdin is redirected from /dev/null and
286 		 * vi prints "Input read error" because it is unable to
287 		 * read() the <CR>).
288 		 */
289 		inexrc = 0;
290 		lprintf(gettext(
291 		    "Error detected in .exrc.[Hit return to continue] "),
292 		    0);
293 		putNFL();
294 		getkey();
295 	}
296 
297 	while ((lastchar() != '\n') && (lastchar() != EOF))
298 		ignchar();
299 	ungetchar(0);
300 	endline = 1;
301 	reset();
302 }
303 
304 void
fixol(void)305 fixol(void)
306 {
307 	if (Outchar != vputchar) {
308 		flush();
309 		if (state == ONEOPEN || state == HARDOPEN)
310 			outline = destline = 0;
311 		Outchar = vputchar;
312 		vcontin(1);
313 		/*
314 		 * Outchar could be set to termchar() through vcontin().
315 		 * So reset it again.
316 		 */
317 		Outchar = vputchar;
318 	} else {
319 		if (destcol)
320 			vclreol();
321 		vclean();
322 	}
323 }
324 
325 /*
326  * Does an ! character follow in the command stream?
327  */
328 int
exclam(void)329 exclam(void)
330 {
331 
332 	if (peekchar() == '!') {
333 		ignchar();
334 		return (1);
335 	}
336 	return (0);
337 }
338 
339 /*
340  * Make an argument list for e.g. next.
341  */
342 void
makargs(void)343 makargs(void)
344 {
345 
346 	glob(&frob);
347 	argc0 = frob.argc0;
348 	argv0 = frob.argv;
349 	args0 = argv0[0];
350 	erewind();
351 }
352 
353 /*
354  * Advance to next file in argument list.
355  */
356 void
next(void)357 next(void)
358 {
359 	extern short isalt;	/* defined in ex_io.c */
360 
361 	if (argc == 0)
362 		error(value(vi_TERSE) ?
363 			(unsigned char *)gettext("No more files") :
364 			(unsigned char *)gettext("No more files to edit"));
365 	morargc = argc;
366 	isalt = (strcmp(altfile, args)==0) + 1;
367 	if (savedfile[0])
368 		CP(altfile, savedfile);
369 	(void) strlcpy(savedfile, args, sizeof (savedfile));
370 	argc--;
371 	args = argv ? *++argv : strend(args) + 1;
372 #if i386 || i286
373 	destcol = 0;
374 #endif
375 }
376 
377 /*
378  * Eat trailing flags and offsets after a command,
379  * saving for possible later post-command prints.
380  */
381 void
donewline(void)382 donewline(void)
383 {
384 	int c;
385 
386 	resetflav();
387 	for (;;) {
388 		c = getchar();
389 		switch (c) {
390 
391 		case '^':
392 		case '-':
393 			poffset--;
394 			break;
395 
396 		case '+':
397 			poffset++;
398 			break;
399 
400 		case 'l':
401 			listf++;
402 			break;
403 
404 		case '#':
405 			nflag++;
406 			break;
407 
408 		case 'p':
409 			listf = 0;
410 			break;
411 
412 		case ' ':
413 		case '\t':
414 			continue;
415 
416 		case '"':
417 			comment();
418 			setflav();
419 			return;
420 
421 		default:
422 			if (!endcmd(c))
423 			    serror(value(vi_TERSE) ?
424 				(unsigned char *)gettext("Extra chars") :
425 				(unsigned char *)gettext(
426 				"Extra characters at end of \"%s\" command"),
427 				    Command);
428 			if (c == EOF)
429 				ungetchar(c);
430 			setflav();
431 			return;
432 		}
433 		pflag++;
434 	}
435 }
436 
437 /*
438  * Before quit or respec of arg list, check that there are
439  * no more files in the arg list.
440  */
441 int
nomore(void)442 nomore(void)
443 {
444 
445 	if (argc == 0 || morargc == argc)
446 		return(0);
447 	morargc = argc;
448 	if (argc == 1) {
449 		merror(value(vi_TERSE) ? gettext("1 more file") :
450 		       gettext("1 more file to edit"), argc);
451 	} else {
452 		merror(value(vi_TERSE) ? gettext("%d more files") :
453 			gettext("%d more files to edit"), argc);
454 	}
455 	return(1);
456 }
457 
458 /*
459  * Before edit of new file check that either an ! follows
460  * or the file has not been changed.
461  */
462 int
quickly(void)463 quickly(void)
464 {
465 
466 	if (exclam())
467 		return (1);
468 	if (chng && dol > zero) {
469 /*
470 		chng = 0;
471 */
472 		xchng = 0;
473 		error(value(vi_TERSE) ? (unsigned char *)gettext("No write") :
474 		    (unsigned char *)
475 		    gettext("No write since last change (:%s! overrides)"),
476 		    Command);
477 	}
478 	return (0);
479 }
480 
481 /*
482  * Reset the flavor of the output to print mode with no numbering.
483  */
484 void
resetflav(void)485 resetflav(void)
486 {
487 
488 	if (inopen)
489 		return;
490 	listf = 0;
491 	nflag = 0;
492 	pflag = 0;
493 	poffset = 0;
494 	setflav();
495 }
496 
497 /*
498  * Print an error message with a %s type argument to printf.
499  * Message text comes from error message file.
500  */
501 void
serror(unsigned char * str,unsigned char * cp)502 serror(unsigned char *str, unsigned char *cp)
503 {
504 	tagflg = 0;
505 	error0();
506 	smerror(str, cp);
507 	error1(str);
508 }
509 
510 /*
511  * Set the flavor of the output based on the flags given
512  * and the number and list options to either number or not number lines
513  * and either use normally decoded (ARPAnet standard) characters or list mode,
514  * where end of lines are marked and tabs print as ^I.
515  */
516 void
setflav(void)517 setflav(void)
518 {
519 
520 	if (inopen)
521 		return;
522 	setnumb(nflag || value(vi_NUMBER));
523 	setlist(listf || value(vi_LIST));
524 	if (!inopen)
525 		setoutt();
526 }
527 
528 /*
529  * Skip white space and tell whether command ends then.
530  */
531 int
skipend(void)532 skipend(void)
533 {
534 
535 	pastwh();
536 	return (endcmd(peekchar()) && peekchar() != '"');
537 }
538 
539 /*
540  * Set the command name for non-word commands.
541  */
542 void
tailspec(int c)543 tailspec(int c)
544 {
545 	static unsigned char foocmd[2];
546 
547 	foocmd[0] = c;
548 	Command = foocmd;
549 }
550 
551 /*
552  * Try to read off the rest of the command word.
553  * If alphabetics follow, then this is not the command we seek.
554  */
555 void
tail(unsigned char * comm)556 tail(unsigned char *comm)
557 {
558 
559 	tailprim(comm, 1, 0);
560 }
561 
562 void
tail2of(unsigned char * comm)563 tail2of(unsigned char *comm)
564 {
565 
566 	tailprim(comm, 2, 0);
567 }
568 
569 unsigned char	tcommand[20];
570 
571 void
tailprim(unsigned char * comm,int i,bool notinvis)572 tailprim(unsigned char *comm, int i, bool notinvis)
573 {
574 	unsigned char *cp;
575 	int c;
576 
577 	Command = comm;
578 	for (cp = tcommand; i > 0; i--)
579 		*cp++ = *comm++;
580 	while (*comm && peekchar() == *comm)
581 		*cp++ = getchar(), comm++;
582 	c = peekchar();
583 	if (notinvis || (isalpha(c) && isascii(c))) {
584 		/*
585 		 * Of the trailing lp funny business, only dl and dp
586 		 * survive the move from ed to ex.
587 		 */
588 		if (tcommand[0] == 'd' && any(c, "lp"))
589 			goto ret;
590 		if (tcommand[0] == 's' && any(c, "gcr"))
591 			goto ret;
592 		while (cp < &tcommand[19] && isalpha(c = peekchar()) && isascii(c))
593 			*cp++ = getchar();
594 		*cp = 0;
595 		if (notinvis)
596 			serror(value(vi_TERSE) ?
597 			    (unsigned char *)gettext("What?") :
598 			    (unsigned char *)gettext(
599 			    "%s: No such command from open/visual"), tcommand);
600 		else
601 			serror(value(vi_TERSE) ?
602 			    (unsigned char *)gettext("What?") :
603 			    (unsigned char *)gettext(
604 			    "%s: Not an editor command"), tcommand);
605 	}
606 ret:
607 	*cp = 0;
608 }
609 
610 /*
611  * Continue after a : command from open/visual.
612  */
613 void
vcontin(bool ask)614 vcontin(bool ask)
615 {
616 
617 	if (vcnt > 0)
618 		vcnt = -vcnt;
619 	if (inopen) {
620 		if (state != VISUAL) {
621 			/*
622 			 * We don't know what a shell command may have left on
623 			 * the screen, so we move the cursor to the right place
624 			 * and then put out a newline.  But this makes an extra
625 			 * blank line most of the time so we only do it for :sh
626 			 * since the prompt gets left on the screen.
627 			 *
628 			 * BUG: :!echo longer than current line \\c
629 			 * will mess it up.
630 			 */
631 			if (state == CRTOPEN) {
632 				termreset();
633 				vgoto(WECHO, 0);
634 			}
635 			if (!ask) {
636 				(void) putch('\r');
637 				(void) putch('\n');
638 			}
639 			return;
640 		}
641 		if (ask) {
642 			merror(gettext("[Hit return to continue] "));
643 			flush();
644 		}
645 #ifndef CBREAK
646 		vraw();
647 #endif
648 		if (ask) {
649 #ifdef notdef
650 			/*
651 			 * Gobble ^Q/^S since the tty driver should be eating
652 			 * them (as far as the user can see)
653 			 */
654 			while (peekkey() == CTRL('Q') || peekkey() == CTRL('S'))
655 				ignore(getkey());
656 #endif
657 			if(getkey() == ':') {
658 				/* Extra newlines, but no other way */
659 				(void) putch('\n');
660 				outline = WECHO;
661 				ungetkey(':');
662 			}
663 		}
664 		vclrech(1);
665 		if (Peekkey != ':') {
666 			fixterm();
667 			putpad((unsigned char *)enter_ca_mode);
668 			tostart();
669 		}
670 	}
671 }
672 
673 /*
674  * Put out a newline (before a shell escape)
675  * if in open/visual.
676  */
677 void
vnfl(void)678 vnfl(void)
679 {
680 
681 	if (inopen) {
682 		if (state != VISUAL && state != CRTOPEN && destline <= WECHO)
683 			vclean();
684 		else
685 			vmoveitup(1, 0);
686 		vgoto(WECHO, 0);
687 		vclrbyte(vtube[WECHO], WCOLS);
688 		tostop();
689 		/* replaced by the ostop above
690 		putpad(cursor_normal);
691 		putpad(key_eol);
692 		*/
693 	}
694 	flush();
695 }
696