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