xref: /original-bsd/contrib/ed/main.c (revision cb36a3b0)
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.11 (Berkeley) 04/30/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 		}
232 	}
233 	for (;;) {
234 		if (prompt_str_flg == 1)
235 			(void)printf("%s", prompt_string);
236 		ss = getc(inputt);
237 		*errnum = 0;
238 		l_tempp = Start = End = NULL;
239 		Start_default = End_default = 1;
240 
241 		/*
242 		 * This isn't nice and alphabetical mainly because of
243 		 * restrictions with 'G' and 'V' (see ed(1)).
244 		 */
245 		for (;;) {
246 			switch (ss) {
247 			case 'd':
248 				d(inputt, errnum);
249 				break;
250 			case 'e':
251 			case 'E':
252 				e(inputt, errnum);
253 				break;
254 			case 'f':
255 				f(inputt, errnum);
256 				break;
257 			case 'a':
258 			case 'c':
259 			case 'i':
260 			case 'g':
261 			case 'G':
262 			case 'v':
263 			case 'V':
264 				if (GV_flag == 1) {
265 					(void)sprintf(help_msg,
266 					    "command `%c' illegal in G/V", ss);
267 					*errnum = -1;
268 					break;
269 				}
270 				switch (ss) {
271 				case 'a':
272 					a(inputt, errnum);
273 					break;
274 				case 'c':
275 					c(inputt, errnum);
276 					break;
277 				case 'i':
278 					i(inputt, errnum);
279 					break;
280 				default:
281 					g(inputt, errnum);
282 				}
283 				break;
284 			case 'h':
285 				if (rol(inputt, errnum))
286 					break;
287 				if (help_msg[0])
288 					(void)printf("%s\n", help_msg);
289 				*errnum = 1;
290 				break;
291 			case 'H':
292 				if (rol(inputt, errnum))
293 					break;
294 				if (help_flag == 0) {
295 					help_flag = 1;
296 					if (help_msg[0])
297 						(void)printf("%s\n",
298 						    help_msg);
299 				} else
300 					help_flag = 0;
301 				*errnum = 1;
302 				break;
303 			case 'j':
304 				j(inputt, errnum);
305 				break;
306 			case 'k':
307 				set_mark(inputt, errnum);
308 				break;
309 			case 'l':
310 				l(inputt, errnum);
311 				break;
312 			case 'm':
313 				m(inputt, errnum);
314 				break;
315 #ifdef POSIX
316 				/* In POSIX-land 'P' toggles the prompt. */
317 			case 'P':
318 				if (rol(inputt, errnum))
319 					break;
320 				prompt_str_flg = prompt_str_flg ? 0 : 1;
321 				*errnum = 1;
322 				break;
323 #endif
324 			case '\n':
325 				if (GV_flag == 1)
326 					return;
327 				/* For 'p' to consume. */
328 				ungetc(ss, inputt);
329 				if ((current == bottom) && (End == NULL)) {
330 					strcpy(help_msg, "at end of buffer");
331 					*errnum = -1;
332 					break;
333 				}
334 				current = current->below;
335 #ifdef BSD
336 				/* In BSD 'P'=='p'. */
337 			case 'P':
338 #endif
339 			case 'p':
340 				p(inputt, errnum, 0);
341 				break;
342 			case 'n':
343 				p(inputt, errnum, 1);
344 				break;
345 			/*
346 			 * An EOF means 'q' unless we're still in the middle
347 			 * of a global command, in which case it was just the
348 			 * end of the command list found.
349 			 */
350 			case EOF:
351 				clearerr(inputt);
352 				if (g_flag > 0)
353 					return;
354 				/*ss = 'q';*/
355 			case 'q':
356 			case 'Q':
357 				q(inputt, errnum);
358 				break;
359 			case 'r':
360 				r(inputt, errnum);
361 				break;
362 			case 's':
363 				s(inputt, errnum);
364 				break;
365 			case 't':
366 				t(inputt, errnum);
367 				break;
368 			case 'u':
369 				u(inputt, errnum);
370 				break;
371 			case 'w':
372 			case 'W':
373 				w(inputt, errnum);
374 				break;
375 			case 'z':
376 				z(inputt, errnum);
377 				break;
378 			case '!':
379 				bang(inputt, errnum);
380 				break;
381 			case '=':
382 				equal(inputt, errnum);
383 				break;
384 			/*
385 			 * Control of address forms from here down.
386 			 *
387 			 * It's a head-game to understand why ";" and "," look
388 			 * as they do below, but a lot of it has to do with ";"
389 			 * and "," being special address pair forms themselves
390 			 * and the compatibility for address "chains".
391 			 */
392 			case ';':
393 				if (End_default == 1 && Start_default == 1) {
394 					Start = current;
395 					End = bottom;
396 					Start_default = End_default = 0;
397 				} else {
398 					Start = current = End;
399 					Start_default = 0;
400 					End_default = 1;
401 				}
402 				l_tempp = NULL;
403 				break;
404 			/*
405 			 * Note address ".,x" where x is a cmd is legal; not a
406 			 * bug - for backward compatability.
407 			 */
408 			case ',':
409 				if (End_default == 1 && Start_default == 1) {
410 					Start = top;
411 					End = bottom;
412 					Start_default = End_default = 0;
413 				} else {
414 					Start = End;
415 					Start_default = 0;
416 					End_default = 1;
417 				}
418 				l_tempp = NULL;
419 				break;
420 			case '%':
421 				if (End_default == 0) {
422 					strcpy(help_msg,
423 					    "'%' is an address pair");
424 					*errnum = -1;
425 					break;
426 				}
427 				Start = top;
428 				End = bottom;
429 				Start_default = End_default = 0;
430 				l_tempp = NULL;
431 				break;
432 			/*
433 			 * Within address_conv => l_last = '+', foobar, but
434 			 * historical and now POSIX...
435 			 */
436 			case ' ':
437 				break;
438 			case '0':
439 			case '1':
440 			case '2':
441 			case '3':
442 			case '4':
443 			case '5':
444 			case '6':
445 			case '7':
446 			case '8':
447 			case '9':
448 			case '-':
449 			case '^':
450 			case '+':
451 			case '\'':
452 			case '$':
453 			case '?':
454 			case '/':
455 			case '.':
456 				ungetc(ss, inputt);
457 				if (Start_default == 0 && End_default == 0) {
458 					strcpy(help_msg,
459 					    "badly formed address");
460 					*errnum = -1;
461 					break;
462 				}
463 				ss = l_last;
464 				l_tempp = address_conv(l_tempp, inputt, errnum);
465 				if (*errnum < 0)
466 					break;
467 				End = l_tempp;
468 				End_default = 0;
469 				if (Start_default == 0)
470 					*errnum = address_check(Start, End);
471 				break;
472 			default:
473 				*errnum = -1;
474 				strcpy(help_msg, "unknown command");
475 				break;
476 			}	/* end-switch(ss) */
477 
478 			/* Things came out okay with the last command. */
479 			if (*errnum > 0) {
480 				if (GV_flag == 1)
481 					return;
482 				/* Do the suffixes if there were any. */
483 				if (printsfx > 0) {
484 					Start = End = current;
485 					ungetc(ss, inputt);
486 					if (printsfx == 1)
487 						p(inputt, errnum, 0);
488 					else
489 						if (printsfx == 2)
490 							p(inputt, errnum, 1);
491 						else if (printsfx == 4)
492 							l(inputt, errnum);
493 					/* Unlikely it's needed, but... */
494 					if (*errnum < 0)
495 						goto errmsg;
496 				}
497 				break;
498 			}
499 			/* There was a problem with the last command. */
500 			else if (*errnum < 0) {
501 errmsg:				while (((ss = getc(inputt)) != '\n') &&
502 				    (ss != EOF));
503 				exit_code = 4;
504 				if (g_flag == 0) {
505 					(void)printf("?\n");
506 					if (help_flag)
507 						(void)printf("%s\n", help_msg);
508 				}
509 /* for people wanting scripts to carry on after a cmd error, then
510  * define NOENDONSCRIPT on the compile line.
511  */
512 #ifndef NOENDONSCRIPT
513 				if (!isatty(STDIN_FILENO)) {
514 					ss = 'Q';
515 					ungetc('\n', inputt);
516 					q(inputt, errnum);
517 				}
518 #endif
519 				break;
520 			}
521 			l_last = ss;
522 			ss = getc(inputt);
523 		}
524 	}
525 }
526 
527 /*
528  * Exits ed and prints an appropriate message about the command line
529  * being malformed (see below).
530  */
531 void
532 ed_exit(err)
533 	int err;
534 {
535 	switch (err) {
536           case 1:
537 		(void)fprintf(stderr, "ed: illegal option\n");
538 		break;
539           case 2:
540 		(void)fprintf(stderr, "ed: missing promptstring\n");
541 		break;
542           case 3:
543 		(void)fprintf(stderr, "ed: too many filenames\n");
544 		break;
545           case 4:
546 		(void)fprintf(stderr, "ed: out of memory error\n");
547 		break;
548 	  case 5:
549 		(void)fprintf(stderr, "ed: unable to create buffer\n");
550 		break;
551           default:
552 		(void)fprintf(stderr, "ed: command line error\n");
553 		break;
554         }
555 	(void)fprintf(stderr,
556 	    "ed: ed [ -s ] [ -p promptstring ] [ filename ]\n");
557 	exit(1);
558 }
559 
560 /*
561  * SIGINT is never turned off. We flag it happened and then pay attention
562  * to it at certain logical locations in the code we don't do more here
563  * cause some of our buffer pointer's may be in an inbetween state at the
564  * time of the SIGINT. So we flag it happened, let the local fn handle it
565  * and do a jump back to the cmd_loop
566  */
567 static void
568 sigint_handler(signo)
569 	int signo;
570 {
571 	sigint_flag = 1;
572 	if (sigspecial3) {
573 		sigspecial3 = 0;
574 		SIGINT_ILACTION;
575 	}
576 	else
577 		if (sigspecial);
578 		else
579 			SIGINT_ACTION;
580 }
581 
582 static void
583 sighup_handler(signo)
584 	int signo;
585 {
586 	sighup_flag = 1;
587 	undo();
588 	do_hup();
589 	/* NOTREACHED */
590 
591 	SIGHUP_ACTION;
592 }
593