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