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