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