allow local mailer to specify $@host; don't add e_fromdomain on
[unix-history] / usr / src / contrib / ed / main.c
CommitLineData
23b7cf0a
KB
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
c842a051 12static char sccsid[] = "@(#)main.c 5.5 (Berkeley) %G%";
23b7cf0a
KB
13#endif /* not lint */
14
ecbf4ad0 15#include <sys/types.h>
e692f66f 16#include <sys/ioctl.h>
ecbf4ad0 17
ecbf4ad0
KB
18#include <regex.h>
19#include <setjmp.h>
20#include <signal.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
c842a051 24#include <limits.h>
ecbf4ad0 25
e692f66f
KB
26#ifdef DBI
27#include <db.h>
28#endif
29
23b7cf0a 30#include "ed.h"
ecbf4ad0 31#include "extern.h"
23b7cf0a
KB
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
38int nn_max, nn_max_flag, start_default, End_default, address_flag;
e692f66f 39int zsnum, filename_flag, add_flag=0, join_flag=0;
23b7cf0a 40int help_flag=0;
e692f66f
KB
41#ifdef STDIO
42FILE *fhtmp;
43int file_seek;
44#endif
23b7cf0a 45
e692f66f 46#ifdef DBI
23b7cf0a 47DB *dbhtmp;
e692f66f 48#endif
23b7cf0a
KB
49
50LINE *nn_max_start, *nn_max_end;
51
52struct MARK mark_matrix[26]; /* in init set all to null */
53
54char *text;
55char *filename_current, *prompt_string=NULL, help_msg[130];
56char *template=NULL;
57int prompt_str_flg=0, start_up_flag=0, name_set=0;
58
59LINE *top, *current, *bottom, *start, *End;
60struct u_layer *u_stk;
61struct d_layer *d_stk;
62LINE *u_current, *u_top, *u_bottom;
63int u_set;
64regex_t RE_comp;
65regmatch_t RE_match[RE_SEC];
66int RE_sol=0, RE_flag=0;
67char *RE_patt=NULL;
68
69int ss; /* for the getc() */
70int explain_flag=1, g_flag=0, GV_flag=0, printsfx=0;
71long change_flag=0L;
72int line_length;
c842a051
KB
73jmp_buf ctrl_position, ctrl_position2, ctrl_position3; /* For SIGnal handling. */
74int sigint_flag, sighup_flag, sigspecial=0, sigspecial2=0, sigspecial3=0;
23b7cf0a 75
ecbf4ad0
KB
76static void sigint_handler __P((int));
77static void sighup_handler __P((int));
23b7cf0a 78
ecbf4ad0
KB
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 */
85int
86main(argc, argv)
87 int argc;
88 char *argv[];
23b7cf0a 89{
e692f66f
KB
90 int l_num, errnum = 0, l_err = 0;
91 char *l_fnametmp, *l_col;
92 struct winsize win;
ecbf4ad0 93
e692f66f
KB
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;
ecbf4ad0
KB
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}
23b7cf0a
KB
170
171/*
ecbf4ad0 172 * The command loop. What the command is that the user has specified
23b7cf0a
KB
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 */
23b7cf0a
KB
178void
179cmd_loop(inputt, errnum)
ecbf4ad0
KB
180 FILE *inputt;
181 int *errnum;
23b7cf0a 182{
ecbf4ad0
KB
183 LINE *l_tempp;
184 int l_last, l_jmp_flag;
185
e692f66f 186 l_last = 0; /* value in l_last may be clobbered (reset to = 0) by longjump, but that's okay */
ecbf4ad0
KB
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;
e692f66f 201 printf("\n?\n");
ecbf4ad0
KB
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);
ecbf4ad0 218 ss = getc(inputt);
ecbf4ad0
KB
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;
23b7cf0a 294#ifdef POSIX
ecbf4ad0
KB
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;
23b7cf0a 302#endif
ecbf4ad0
KB
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;
23b7cf0a 314#ifdef BSD
ecbf4ad0
KB
315 /* In BSD 'P'=='p'. */
316 case 'P':
23b7cf0a 317#endif
ecbf4ad0
KB
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) {
480errmsg: while (((ss = getc(inputt)) != '\n') &&
481 (ss != EOF));
e692f66f
KB
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;
ecbf4ad0
KB
489 }
490 l_last = ss;
491 ss = getc(inputt);
492 }
493 }
494}
23b7cf0a 495
ecbf4ad0
KB
496/*
497 * Exits ed and prints an appropriate message about the command line
23b7cf0a
KB
498 * being malformed (see below).
499 */
23b7cf0a
KB
500void
501ed_exit(err)
ecbf4ad0 502 int err;
23b7cf0a 503{
ecbf4ad0
KB
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;
e692f66f
KB
517 case 5:
518 (void)fprintf(stderr, "ed: unable to create buffer\n");
519 break;
ecbf4ad0
KB
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}
23b7cf0a
KB
528
529/*
ecbf4ad0
KB
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
23b7cf0a 535 */
ecbf4ad0
KB
536static void
537sigint_handler(signo)
538 int signo;
23b7cf0a 539{
ecbf4ad0 540 sigint_flag = 1;
c842a051
KB
541 if (sigspecial3) {
542 sigspecial3 = 0;
543 SIGINT_ILACTION;
544 }
e692f66f
KB
545 if (sigspecial2) {
546 sigspecial2 = 0;
c842a051 547 SIGINT_ALACTION;
e692f66f
KB
548 }
549 else
550 if (sigspecial);
551 else
552 SIGINT_ACTION;
ecbf4ad0
KB
553}
554
555static void
556sighup_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}