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