xref: /original-bsd/usr.bin/ex/ex_cmds2.c (revision 80dd9c38)
1 /*-
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.proprietary.c%
6  */
7 
8 #ifndef lint
9 static char sccsid[] = "@(#)ex_cmds2.c	8.1 (Berkeley) 06/09/93";
10 #endif /* not lint */
11 
12 #include "ex.h"
13 #include "ex_argv.h"
14 #include "ex_temp.h"
15 #include "ex_tty.h"
16 #include "ex_vis.h"
17 
18 extern bool	pflag, nflag;		/* mjm: extern; also in ex_cmds.c */
19 extern int	poffset;		/* mjm: extern; also in ex_cmds.c */
20 
21 /*
22  * Subroutines for major command loop.
23  */
24 
25 /*
26  * Is there a single letter indicating a named buffer next?
27  */
28 cmdreg()
29 {
30 	register int c = 0;
31 	register int wh = skipwh();
32 
33 	if (wh && isalpha(peekchar()))
34 		c = ex_getchar();
35 	return (c);
36 }
37 
38 /*
39  * Tell whether the character ends a command
40  */
41 endcmd(ch)
42 	int ch;
43 {
44 	switch (ch) {
45 
46 	case '\n':
47 	case EOF:
48 		endline = 1;
49 		return (1);
50 
51 	case '|':
52 	case '"':
53 		endline = 0;
54 		return (1);
55 	}
56 	return (0);
57 }
58 
59 /*
60  * Insist on the end of the command.
61  */
62 eol()
63 {
64 
65 	if (!skipend())
66 		error("Extra chars|Extra characters at end of command");
67 	ignnEOF();
68 }
69 
70 /*
71  * Print out the message in the error message file at str,
72  * with i an integer argument to printf.
73  */
74 /*VARARGS2*/
75 error(str, i)
76 #ifndef EXSTRINGS
77 	char *str;
78 #else
79 # ifdef lint
80 	char *str;
81 # else
82 	int str;
83 # endif
84 #endif
85 	int i;
86 {
87 
88 	error0();
89 	merror(str, i);
90 	if (writing) {
91 		serror(" [Warning - %s is incomplete]", file);
92 		writing = 0;
93 	}
94 	error1(str);
95 }
96 
97 /*
98  * Rewind the argument list.
99  */
100 erewind()
101 {
102 
103 	argc = argc0;
104 	argv = argv0;
105 	args = args0;
106 	if (argc > 1 && !hush) {
107 		ex_printf(mesg("%d files@to edit"), argc);
108 		if (inopen)
109 			ex_putchar(' ');
110 		else
111 			putNFL();
112 	}
113 }
114 
115 /*
116  * Guts of the pre-printing error processing.
117  * If in visual and catching errors, then we dont mung up the internals,
118  * just fixing up the echo area for the print.
119  * Otherwise we reset a number of externals, and discard unused input.
120  */
121 error0()
122 {
123 
124 	if (vcatch) {
125 		if (splitw == 0)
126 			fixech();
127 		if (!SO || !SE)
128 			dingdong();
129 		return;
130 	}
131 	if (input) {
132 		input = strend(input) - 1;
133 		if (*input == '\n')
134 			setlastchar('\n');
135 		input = 0;
136 	}
137 	setoutt();
138 	flush();
139 	resetflav();
140 	if (!SO || !SE)
141 		dingdong();
142 	if (inopen) {
143 		/*
144 		 * We are coming out of open/visual ungracefully.
145 		 * Restore COLUMNS, undo, and fix tty mode.
146 		 */
147 		COLUMNS = OCOLUMNS;
148 		undvis();
149 		ostop(normf);
150 		/* ostop should be doing this
151 		putpad(VE);
152 		putpad(KE);
153 		*/
154 		putnl();
155 	}
156 	inopen = 0;
157 	holdcm = 0;
158 }
159 
160 /*
161  * Post error printing processing.
162  * Close the i/o file if left open.
163  * If catching in visual then throw to the visual catch,
164  * else if a child after a fork, then exit.
165  * Otherwise, in the normal command mode error case,
166  * finish state reset, and throw to top.
167  */
168 error1(str)
169 	char *str;
170 {
171 	bool die;
172 
173 	if (io > 0) {
174 		close(io);
175 		io = -1;
176 	}
177 	die = (getpid() != ppid);	/* Only children die */
178 	inappend = inglobal = 0;
179 	globp = vglobp = vmacp = 0;
180 	if (vcatch && !die) {
181 		inopen = 1;
182 		vcatch = 0;
183 		if (str)
184 			noonl();
185 		fixol();
186 		longjmp(vreslab,1);
187 	}
188 	if (str && !vcatch)
189 		putNFL();
190 	if (die)
191 		ex_exit(1);
192 	lseek(0, 0L, 2);
193 	if (inglobal)
194 		setlastchar('\n');
195 	while (lastchar() != '\n' && lastchar() != EOF)
196 		ignchar();
197 	ungetchar(0);
198 	endline = 1;
199 	reset();
200 }
201 
202 fixol()
203 {
204 	if (Outchar != vputchar) {
205 		flush();
206 		if (state == ONEOPEN || state == HARDOPEN)
207 			outline = destline = 0;
208 		Outchar = vputchar;
209 		vcontin(1);
210 	} else {
211 		if (destcol)
212 			vclreol();
213 		vclean();
214 	}
215 }
216 
217 /*
218  * Does an ! character follow in the command stream?
219  */
220 exclam()
221 {
222 
223 	if (peekchar() == '!') {
224 		ignchar();
225 		return (1);
226 	}
227 	return (0);
228 }
229 
230 /*
231  * Make an argument list for e.g. next.
232  */
233 makargs()
234 {
235 
236 	glob(&frob);
237 	argc0 = frob.argc0;
238 	argv0 = frob.argv;
239 	args0 = argv0[0];
240 	erewind();
241 }
242 
243 /*
244  * Advance to next file in argument list.
245  */
246 next()
247 {
248 	extern short isalt;	/* defined in ex_io.c */
249 
250 	if (argc == 0)
251 		error("No more files@to edit");
252 	morargc = argc;
253 	isalt = (strcmp(altfile, args)==0) + 1;
254 	if (savedfile[0])
255 		CP(altfile, savedfile);
256 	CP(savedfile, args);
257 	argc--;
258 	args = argv ? *++argv : strend(args) + 1;
259 }
260 
261 /*
262  * Eat trailing flags and offsets after a command,
263  * saving for possible later post-command prints.
264  */
265 newline()
266 {
267 	register int c;
268 
269 	resetflav();
270 	for (;;) {
271 		c = ex_getchar();
272 		switch (c) {
273 
274 		case '^':
275 		case '-':
276 			poffset--;
277 			break;
278 
279 		case '+':
280 			poffset++;
281 			break;
282 
283 		case 'l':
284 			listf++;
285 			break;
286 
287 		case '#':
288 			nflag++;
289 			break;
290 
291 		case 'p':
292 			listf = 0;
293 			break;
294 
295 		case ' ':
296 		case '\t':
297 			continue;
298 
299 		case '"':
300 			comment();
301 			setflav();
302 			return;
303 
304 		default:
305 			if (!endcmd(c))
306 serror("Extra chars|Extra characters at end of \"%s\" command", Command);
307 			if (c == EOF)
308 				ungetchar(c);
309 			setflav();
310 			return;
311 		}
312 		pflag++;
313 	}
314 }
315 
316 /*
317  * Before quit or respec of arg list, check that there are
318  * no more files in the arg list.
319  */
320 nomore()
321 {
322 
323 	if (argc == 0 || morargc == argc)
324 		return;
325 	morargc = argc;
326 	merror("%d more file", argc);
327 	serror("%s@to edit", plural((long) argc));
328 }
329 
330 /*
331  * Before edit of new file check that either an ! follows
332  * or the file has not been changed.
333  */
334 quickly()
335 {
336 
337 	if (exclam())
338 		return (1);
339 	if (chng && dol > zero) {
340 /*
341 		chng = 0;
342 */
343 		xchng = 0;
344 		serror("No write@since last change (:%s! overrides)", Command);
345 	}
346 	return (0);
347 }
348 
349 /*
350  * Reset the flavor of the output to print mode with no numbering.
351  */
352 resetflav()
353 {
354 
355 	if (inopen)
356 		return;
357 	listf = 0;
358 	nflag = 0;
359 	pflag = 0;
360 	poffset = 0;
361 	setflav();
362 }
363 
364 /*
365  * Print an error message with a %s type argument to printf.
366  * Message text comes from error message file.
367  */
368 serror(str, cp)
369 #ifdef lint
370 	register char *str;
371 #else
372 	register int str;
373 #endif
374 	char *cp;
375 {
376 
377 	error0();
378 	smerror(str, cp);
379 	error1(str);
380 }
381 
382 /*
383  * Set the flavor of the output based on the flags given
384  * and the number and list options to either number or not number lines
385  * and either use normally decoded (ARPAnet standard) characters or list mode,
386  * where end of lines are marked and tabs print as ^I.
387  */
388 setflav()
389 {
390 
391 	if (inopen)
392 		return;
393 	ignorf(setnumb(nflag || value(NUMBER)));
394 	ignorf(setlist(listf || value(LIST)));
395 	setoutt();
396 }
397 
398 /*
399  * Skip white space and tell whether command ends then.
400  */
401 skipend()
402 {
403 
404 	pastwh();
405 	return (endcmd(peekchar()) && peekchar() != '"');
406 }
407 
408 /*
409  * Set the command name for non-word commands.
410  */
411 tailspec(c)
412 	int c;
413 {
414 	static char foocmd[2];
415 
416 	foocmd[0] = c;
417 	Command = foocmd;
418 }
419 
420 /*
421  * Try to read off the rest of the command word.
422  * If alphabetics follow, then this is not the command we seek.
423  */
424 tail(comm)
425 	char *comm;
426 {
427 
428 	tailprim(comm, 1, 0);
429 }
430 
431 tail2of(comm)
432 	char *comm;
433 {
434 
435 	tailprim(comm, 2, 0);
436 }
437 
438 char	tcommand[20];
439 
440 tailprim(comm, i, notinvis)
441 	register char *comm;
442 	int i;
443 	bool notinvis;
444 {
445 	register char *cp;
446 	register int c;
447 
448 	Command = comm;
449 	for (cp = tcommand; i > 0; i--)
450 		*cp++ = *comm++;
451 	while (*comm && peekchar() == *comm)
452 		*cp++ = ex_getchar(), comm++;
453 	c = peekchar();
454 	if (notinvis || isalpha(c)) {
455 		/*
456 		 * Of the trailing lp funny business, only dl and dp
457 		 * survive the move from ed to ex.
458 		 */
459 		if (tcommand[0] == 'd' && any(c, "lp"))
460 			goto ret;
461 		if (tcommand[0] == 's' && any(c, "gcr"))
462 			goto ret;
463 		while (cp < &tcommand[19] && isalpha(peekchar()))
464 			*cp++ = ex_getchar();
465 		*cp = 0;
466 		if (notinvis)
467 			serror("What?|%s: No such command from open/visual", tcommand);
468 		else
469 			serror("What?|%s: Not an editor command", tcommand);
470 	}
471 ret:
472 	*cp = 0;
473 }
474 
475 /*
476  * Continue after a : command from open/visual.
477  */
478 vcontin(ask)
479 	bool ask;
480 {
481 
482 	if (vcnt > 0)
483 		vcnt = -vcnt;
484 	if (inopen) {
485 		if (state != VISUAL) {
486 			/*
487 			 * We don't know what a shell command may have left on
488 			 * the screen, so we move the cursor to the right place
489 			 * and then put out a newline.  But this makes an extra
490 			 * blank line most of the time so we only do it for :sh
491 			 * since the prompt gets left on the screen.
492 			 *
493 			 * BUG: :!echo longer than current line \\c
494 			 * will screw it up, but be reasonable!
495 			 */
496 			if (state == CRTOPEN) {
497 				termreset();
498 				vgoto(WECHO, 0);
499 			}
500 			if (!ask) {
501 				putch('\r');
502 				putch('\n');
503 			}
504 			return;
505 		}
506 		if (ask) {
507 			merror("[Hit return to continue] ");
508 			flush();
509 		}
510 #ifndef CBREAK
511 		vraw();
512 #endif
513 		if (ask) {
514 #ifdef EATQS
515 			/*
516 			 * Gobble ^Q/^S since the tty driver should be eating
517 			 * them (as far as the user can see)
518 			 */
519 			while (peekkey() == CTRL('Q') || peekkey() == CTRL('S'))
520 				ignore(getkey());
521 #endif
522 			if(getkey() == ':') {
523 				/* Ugh. Extra newlines, but no other way */
524 				putch('\n');
525 				outline = WECHO;
526 				ungetkey(':');
527 			}
528 		}
529 		vclrech(1);
530 		if (Peek_key != ':') {
531 			putpad(TI);
532 			tostart();
533 			/* replaced by ostart.
534 			putpad(VS);
535 			putpad(KS);
536 			*/
537 		}
538 	}
539 }
540 
541 /*
542  * Put out a newline (before a shell escape)
543  * if in open/visual.
544  */
545 vnfl()
546 {
547 
548 	if (inopen) {
549 		if (state != VISUAL && state != CRTOPEN && destline <= WECHO)
550 			vclean();
551 		else
552 			vmoveitup(1, 0);
553 		vgoto(WECHO, 0);
554 		vclrbyte(vtube[WECHO], WCOLS);
555 		tostop();
556 		/* replaced by the ostop above
557 		putpad(VE);
558 		putpad(KE);
559 		*/
560 	}
561 	flush();
562 }
563