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