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