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