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