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