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