xref: /original-bsd/contrib/ed/main.c (revision beb7df4f)
1 /*-
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rodney Ruddock of the University of Guelph.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 static char copyright[] =
13 "@(#) Copyright (c) 1992, 1993\n\
14 	The Regents of the University of California.  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)main.c	8.1 (Berkeley) 05/31/93";
19 #endif /* not lint */
20 
21 #include <sys/types.h>
22 #include <sys/ioctl.h>
23 
24 #include <limits.h>
25 #include <regex.h>
26 #include <setjmp.h>
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #ifdef DBI
34 #include <db.h>
35 #endif
36 
37 #include "ed.h"
38 #include "extern.h"
39 
40 /*
41  * This is where all of the "global" variables are declared. They are
42  * set for extern in the ed.h header file (so everyone can get them).
43  */
44 
45 int nn_max, nn_max_flag, Start_default, End_default, address_flag;
46 int zsnum, filename_flag, add_flag=0, join_flag=0;
47 int help_flag=0, gut_num=-1;
48 #ifdef STDIO
49 FILE *fhtmp;
50 int file_seek;
51 #endif
52 
53 #ifdef DBI
54 DB *dbhtmp;
55 #endif
56 
57 LINE *nn_max_start, *nn_max_end;
58 
59 struct MARK mark_matrix[26]; /* in init set all to null */
60 
61 char *text;
62 LINE **gut=NULL;
63 char *filename_current, *prompt_string=NULL, help_msg[130];
64 char *template=NULL;
65 int prompt_str_flg=0, start_up_flag=0, name_set=0;
66 
67 LINE *top, *current, *bottom, *Start, *End;
68 struct u_layer *u_stk;
69 struct d_layer *d_stk;
70 LINE *u_current, *u_top, *u_bottom;
71 int u_set;
72 regex_t RE_comp;
73 regmatch_t RE_match[RE_SEC];
74 int RE_sol=0, RE_flag=0;
75 char *RE_patt=NULL;
76 
77 int ss; /* for the getc() */
78 int explain_flag=1, g_flag=0, GV_flag=0, printsfx=0, exit_code=0;
79 long change_flag=0L;
80 int line_length;
81 jmp_buf ctrl_position, ctrl_position2, ctrl_position3; /* For SIGnal handling. */
82 int sigint_flag, sighup_flag, sigspecial=0, sigspecial2=0, sigspecial3=0;
83 
84 static void sigint_handler __P((int));
85 static void sighup_handler __P((int));
86 
87 /*
88  * Starts the whole show going. Set things up as the arguments spec
89  * in the shell and set a couple of the global variables.
90  *
91  * Note naming viol'n with errnum for consistancy.
92  */
93 int
main(argc,argv)94 main(argc, argv)
95 	int argc;
96 	char *argv[];
97 {
98 	int l_num, errnum=0, l_err=0;
99 	char *l_fnametmp, *l_col, buf[2];
100 	struct winsize win;
101 
102 	setbuffer(stdin, buf, 1);
103 	line_length = ((l_col = getenv("COLUMNS")) == NULL ? 0 : atoi(l_col));
104 	if ((line_length == 0 && isatty(STDOUT_FILENO) &&
105 		ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1))
106 		line_length = win.ws_col;
107 	if (line_length == 0)
108 		line_length = 78;
109 	line_length -= 3;	/* for the octal to break properly in 'l' */
110 
111 	Start = End = NULL;
112 	top = bottom = NULL;
113 	current = NULL;
114 	nn_max_flag = 0;
115 	nn_max_start = nn_max_end = NULL;
116 	l_fnametmp = calloc(FILENAME_LEN, sizeof(char));
117 	if (l_fnametmp == NULL)
118 		ed_exit(4);
119 	text = calloc(NN_MAX_START + 2, sizeof(char));
120 	if (text == NULL)
121 		ed_exit(4);
122 	Start_default = End_default = 0;
123 	zsnum = 22;		/* for the 'z' command */
124 	help_msg[0] = '\0';
125 	u_stk = NULL;
126 	d_stk = NULL;
127 	u_current = u_top = u_bottom = NULL;
128 	u_set = 0;		/* for in d after a j */
129 	filename_flag = 0;
130 	filename_current = NULL;
131 
132 	l_num = 1;
133 	for (;;) {
134 		/* Process the command line options */
135 		if (l_num >= argc)
136 			break;
137 		switch (argv[l_num][0]) {
138 		case '-':
139 			switch (argv[l_num][1]) {
140 			case '\0':	/* this is why 'getopt' not used */
141 			case 's':
142 				explain_flag = 0;
143 				break;
144 			case 'p':
145 				if (++l_num < argc) {
146 					prompt_string =
147 					    calloc(strlen(argv[l_num]),
148 					    sizeof(char));
149 					if (prompt_string == NULL)
150 						ed_exit(4);
151 					strcpy(prompt_string, argv[l_num]);
152 					prompt_str_flg = 1;
153 					break;
154 				}
155 				l_err = 1;
156 			case 'v':
157 #ifdef BSD
158 				(void)printf("ed: in BSD mode:\n");
159 #endif
160 #ifdef POSIX
161 				(void)printf("ed: in POSIX mode:\n");
162 #endif
163 				break;
164 			default:
165 				l_err++;
166 				ed_exit(l_err);
167 			}
168 			break;
169 		default:
170 			if (name_set)
171 				ed_exit(3);
172 			strcpy(l_fnametmp, argv[l_num]);
173 			filename_current = l_fnametmp;
174 			name_set = 1;
175 			if (prompt_str_flg)
176 				break;
177 			/* default ed prompt */
178 			prompt_string = (char *) calloc(3, sizeof(char));
179 			strcpy(prompt_string, "*");
180 			break;
181 		}
182 		l_num++;
183 	}
184 
185 	start_up_flag = 1;
186 	cmd_loop(stdin, &errnum);
187 	/* NOTREACHED */
188 }
189 
190 /*
191  * The command loop. What the command is that the user has specified
192  * is determined here. This is not just for commands coming from
193  * the terminal but any standard i/o stream; see the global commands.
194  * Some of the commands are handled within here (i.e. 'H') while most
195  * are handled in their own functions (as called).
196  */
197 void
cmd_loop(inputt,errnum)198 cmd_loop(inputt, errnum)
199 	FILE *inputt;
200 	int *errnum;
201 {
202 	LINE *l_tempp;
203 	int l_last, l_jmp_flag;
204 
205 	l_last = 0; /* value in l_last may be clobbered (reset to = 0) by longjump, but that's okay */
206 
207 	if (g_flag == 0) {	/* big, BIG trouble if we don't check! think. */
208 		/* set the jump point for the signals */
209 		l_jmp_flag = setjmp(ctrl_position);
210 		signal(SIGINT, sigint_handler);
211 		signal(SIGHUP, sighup_handler);
212 		switch (l_jmp_flag) {
213 		case JMP_SET:
214 			break;
215 		/* Some general cleanup not specific to the jmp pt. */
216 		case INTERUPT:
217 			sigint_flag = 0;
218 			GV_flag = 0;	/* safest place to do these flags */
219 			g_flag = 0;
220 			(void)printf("\n?\n");
221 			break;
222 		case HANGUP:		/* shouldn't get here. */
223 			break;
224 		default:
225 			(void)fprintf(stderr, "Signal jump problem\n");
226 		}
227 		/* Only do this once! */
228 		if (start_up_flag) {
229 			start_up_flag = 0;
230 			/* simulate the 'e' at startup */
231 			e2(inputt, errnum);
232 			if (*errnum == 0)
233 				goto errmsg2;
234 		}
235 	}
236 	for (;;) {
237 		if (prompt_str_flg == 1)
238 			(void)printf("%s", prompt_string);
239 		ss = getc(inputt);
240 		*errnum = 0;
241 		l_tempp = Start = End = NULL;
242 		Start_default = End_default = 1;
243 
244 		/*
245 		 * This isn't nice and alphabetical mainly because of
246 		 * restrictions with 'G' and 'V' (see ed(1)).
247 		 */
248 		for (;;) {
249 			switch (ss) {
250 			case 'd':
251 				d(inputt, errnum);
252 				break;
253 			case 'e':
254 			case 'E':
255 				e(inputt, errnum);
256 				if (*errnum == 0)
257 					goto errmsg2;
258 				break;
259 			case 'f':
260 				f(inputt, errnum);
261 				break;
262 			case 'a':
263 			case 'c':
264 			case 'i':
265 			case 'g':
266 			case 'G':
267 			case 'v':
268 			case 'V':
269 				if (GV_flag == 1) {
270 					(void)sprintf(help_msg,
271 					    "command `%c' illegal in G/V", ss);
272 					*errnum = -1;
273 					break;
274 				}
275 				switch (ss) {
276 				case 'a':
277 					a(inputt, errnum);
278 					break;
279 				case 'c':
280 					c(inputt, errnum);
281 					break;
282 				case 'i':
283 					i(inputt, errnum);
284 					break;
285 				default:
286 					g(inputt, errnum);
287 				}
288 				break;
289 			case 'h':
290 				if (rol(inputt, errnum))
291 					break;
292 				if (help_msg[0])
293 					(void)printf("%s\n", help_msg);
294 				*errnum = 1;
295 				break;
296 			case 'H':
297 				if (rol(inputt, errnum))
298 					break;
299 				if (help_flag == 0) {
300 					help_flag = 1;
301 					if (help_msg[0])
302 						(void)printf("%s\n",
303 						    help_msg);
304 				} else
305 					help_flag = 0;
306 				*errnum = 1;
307 				break;
308 			case 'j':
309 				j(inputt, errnum);
310 				break;
311 			case 'k':
312 				set_mark(inputt, errnum);
313 				break;
314 			case 'l':
315 				l(inputt, errnum);
316 				break;
317 			case 'm':
318 				m(inputt, errnum);
319 				break;
320 #ifdef POSIX
321 				/* In POSIX-land 'P' toggles the prompt. */
322 			case 'P':
323 				if (rol(inputt, errnum))
324 					break;
325 				prompt_str_flg = prompt_str_flg ? 0 : 1;
326 				*errnum = 1;
327 				break;
328 #endif
329 			case '\n':
330 				if (GV_flag == 1)
331 					return;
332 				/* For 'p' to consume. */
333 				ungetc(ss, inputt);
334 				if ((current == bottom) && (End == NULL)) {
335 					strcpy(help_msg, "at end of buffer");
336 					*errnum = -1;
337 					break;
338 				}
339 				current = current->below;
340 #ifdef BSD
341 				/* In BSD 'P'=='p'. */
342 			case 'P':
343 #endif
344 			case 'p':
345 				p(inputt, errnum, 0);
346 				break;
347 			case 'n':
348 				p(inputt, errnum, 1);
349 				break;
350 			/*
351 			 * An EOF means 'q' unless we're still in the middle
352 			 * of a global command, in which case it was just the
353 			 * end of the command list found.
354 			 */
355 			case EOF:
356 				clearerr(inputt);
357 				if (g_flag > 0)
358 					return;
359 				/*ss = 'q';*/
360 			case 'q':
361 			case 'Q':
362 				if ((!isatty(STDIN_FILENO)) && (ss == 'q'))
363 					ss = 'Q';
364 				q(inputt, errnum);
365 				break;
366 			case 'r':
367 				r(inputt, errnum);
368 				if (*errnum == 0)
369 					goto errmsg2;
370 				break;
371 			case 's':
372 				s(inputt, errnum);
373 				break;
374 			case 't':
375 				t(inputt, errnum);
376 				break;
377 			case 'u':
378 				u(inputt, errnum);
379 				break;
380 			case 'w':
381 			case 'W':
382 				w(inputt, errnum);
383 				break;
384 			case 'z':
385 				z(inputt, errnum);
386 				break;
387 			case '!':
388 				bang(inputt, errnum);
389 				break;
390 			case '=':
391 				equal(inputt, errnum);
392 				break;
393 			/*
394 			 * Control of address forms from here down.
395 			 *
396 			 * It's a head-game to understand why ";" and "," look
397 			 * as they do below, but a lot of it has to do with ";"
398 			 * and "," being special address pair forms themselves
399 			 * and the compatibility for address "chains".
400 			 */
401 			case ';':
402 				if (End_default == 1 && Start_default == 1) {
403 					Start = current;
404 					End = bottom;
405 					Start_default = End_default = 0;
406 				} else {
407 					Start = current = End;
408 					Start_default = 0;
409 					End_default = 1;
410 				}
411 				l_tempp = NULL;
412 				break;
413 			/*
414 			 * Note address ".,x" where x is a cmd is legal; not a
415 			 * bug - for backward compatability.
416 			 */
417 			case ',':
418 				if (End_default == 1 && Start_default == 1) {
419 					Start = top;
420 					End = bottom;
421 					Start_default = End_default = 0;
422 				} else {
423 					Start = End;
424 					Start_default = 0;
425 					End_default = 1;
426 				}
427 				l_tempp = NULL;
428 				break;
429 			case '%':
430 				if (End_default == 0) {
431 					strcpy(help_msg,
432 					    "'%' is an address pair");
433 					*errnum = -1;
434 					break;
435 				}
436 				Start = top;
437 				End = bottom;
438 				Start_default = End_default = 0;
439 				l_tempp = NULL;
440 				break;
441 			/*
442 			 * Within address_conv => l_last = '+', foobar, but
443 			 * historical and now POSIX...
444 			 */
445 			case ' ':
446 				break;
447 			case '0':
448 			case '1':
449 			case '2':
450 			case '3':
451 			case '4':
452 			case '5':
453 			case '6':
454 			case '7':
455 			case '8':
456 			case '9':
457 			case '-':
458 			case '^':
459 			case '+':
460 			case '\'':
461 			case '$':
462 			case '?':
463 			case '/':
464 			case '.':
465 				ungetc(ss, inputt);
466 				if (Start_default == 0 && End_default == 0) {
467 					strcpy(help_msg,
468 					    "badly formed address");
469 					*errnum = -1;
470 					break;
471 				}
472 				ss = l_last;
473 				l_tempp = address_conv(l_tempp, inputt, errnum);
474 				if (*errnum < 0)
475 					break;
476 				End = l_tempp;
477 				End_default = 0;
478 				if (Start_default == 0)
479 					*errnum = address_check(Start, End);
480 				break;
481 			default:
482 				*errnum = -1;
483 				strcpy(help_msg, "unknown command");
484 				break;
485 			}	/* end-switch(ss) */
486 
487 			/* Things came out okay with the last command. */
488 			if (*errnum > 0) {
489 				if (GV_flag == 1)
490 					return;
491 				/* Do the suffixes if there were any. */
492 				if (printsfx > 0) {
493 					Start = End = current;
494 					ungetc(ss, inputt);
495 					if (printsfx == 1)
496 						p(inputt, errnum, 0);
497 					else
498 						if (printsfx == 2)
499 							p(inputt, errnum, 1);
500 						else if (printsfx == 4)
501 							l(inputt, errnum);
502 					/* Unlikely it's needed, but... */
503 					if (*errnum < 0)
504 						goto errmsg;
505 				}
506 				break;
507 			}
508 			/* There was a problem with the last command. */
509 			else if (*errnum < 0) {
510 errmsg:				while (((ss = getc(inputt)) != '\n') &&
511 				    (ss != EOF));
512 				(void)printf("?\n");
513 errmsg2:			if (help_flag)
514 					(void)printf("%s\n", help_msg);
515 				exit_code = 4;
516 /* for people wanting scripts to carry on after a cmd error, then
517  * define NOENDONSCRIPT on the compile line.
518  */
519 #ifndef NOENDONSCRIPT
520 				if (!isatty(STDIN_FILENO)) {
521 					ss = 'Q';
522 					ungetc('\n', inputt);
523 					q(inputt, errnum);
524 				}
525 #endif
526 				break;
527 			}
528 			l_last = ss;
529 			ss = getc(inputt);
530 		}
531 	}
532 }
533 
534 /*
535  * Exits ed and prints an appropriate message about the command line
536  * being malformed (see below).
537  */
538 void
ed_exit(err)539 ed_exit(err)
540 	int err;
541 {
542 	switch (err) {
543           case 1:
544 		(void)fprintf(stderr, "ed: illegal option\n");
545 		break;
546           case 2:
547 		(void)fprintf(stderr, "ed: missing promptstring\n");
548 		break;
549           case 3:
550 		(void)fprintf(stderr, "ed: too many filenames\n");
551 		break;
552           case 4:
553 		(void)fprintf(stderr, "ed: out of memory error\n");
554 		break;
555 	  case 5:
556 		(void)fprintf(stderr, "ed: unable to create buffer\n");
557 		break;
558           default:
559 		(void)fprintf(stderr, "ed: command line error\n");
560 		break;
561         }
562 	(void)fprintf(stderr,
563 	    "ed: ed [ -s ] [ -p promptstring ] [ filename ]\n");
564 	exit(1);
565 }
566 
567 /*
568  * SIGINT is never turned off. We flag it happened and then pay attention
569  * to it at certain logical locations in the code we don't do more here
570  * cause some of our buffer pointer's may be in an inbetween state at the
571  * time of the SIGINT. So we flag it happened, let the local fn handle it
572  * and do a jump back to the cmd_loop
573  */
574 static void
sigint_handler(signo)575 sigint_handler(signo)
576 	int signo;
577 {
578 	sigint_flag = 1;
579 	if (sigspecial3) {
580 		sigspecial3 = 0;
581 		SIGINT_ILACTION;
582 	}
583 	else
584 		if (sigspecial);
585 		else
586 			SIGINT_ACTION;
587 }
588 
589 static void
sighup_handler(signo)590 sighup_handler(signo)
591 	int signo;
592 {
593 	sighup_flag = 1;
594 	undo();
595 	do_hup();
596 	/* NOTREACHED */
597 
598 	SIGHUP_ACTION;
599 }
600