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