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