update from Rodney Ruddock (rodney@snowhite.cis.uoguelph.ca)
[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
afdef93a
KB
12char 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
05ee7127 18static char sccsid[] = "@(#)main.c 5.12 (Berkeley) %G%";
23b7cf0a
KB
19#endif /* not lint */
20
ecbf4ad0 21#include <sys/types.h>
e692f66f 22#include <sys/ioctl.h>
ecbf4ad0 23
29d42347 24#include <limits.h>
ecbf4ad0
KB
25#include <regex.h>
26#include <setjmp.h>
27#include <signal.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
29d42347 31#include <unistd.h>
ecbf4ad0 32
e692f66f
KB
33#ifdef DBI
34#include <db.h>
35#endif
36
23b7cf0a 37#include "ed.h"
ecbf4ad0 38#include "extern.h"
23b7cf0a
KB
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
035e94f8 45int nn_max, nn_max_flag, Start_default, End_default, address_flag;
e692f66f 46int zsnum, filename_flag, add_flag=0, join_flag=0;
71fc9f52 47int help_flag=0, gut_num=-1;
e692f66f
KB
48#ifdef STDIO
49FILE *fhtmp;
50int file_seek;
51#endif
23b7cf0a 52
e692f66f 53#ifdef DBI
23b7cf0a 54DB *dbhtmp;
e692f66f 55#endif
23b7cf0a
KB
56
57LINE *nn_max_start, *nn_max_end;
58
59struct MARK mark_matrix[26]; /* in init set all to null */
60
61char *text;
afdef93a 62LINE **gut=NULL;
23b7cf0a
KB
63char *filename_current, *prompt_string=NULL, help_msg[130];
64char *template=NULL;
65int prompt_str_flg=0, start_up_flag=0, name_set=0;
66
035e94f8 67LINE *top, *current, *bottom, *Start, *End;
23b7cf0a
KB
68struct u_layer *u_stk;
69struct d_layer *d_stk;
70LINE *u_current, *u_top, *u_bottom;
71int u_set;
72regex_t RE_comp;
73regmatch_t RE_match[RE_SEC];
74int RE_sol=0, RE_flag=0;
75char *RE_patt=NULL;
76
77int ss; /* for the getc() */
afdef93a 78int explain_flag=1, g_flag=0, GV_flag=0, printsfx=0, exit_code=0;
23b7cf0a
KB
79long change_flag=0L;
80int line_length;
c842a051
KB
81jmp_buf ctrl_position, ctrl_position2, ctrl_position3; /* For SIGnal handling. */
82int sigint_flag, sighup_flag, sigspecial=0, sigspecial2=0, sigspecial3=0;
23b7cf0a 83
ecbf4ad0
KB
84static void sigint_handler __P((int));
85static void sighup_handler __P((int));
23b7cf0a 86
ecbf4ad0
KB
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 */
93int
94main(argc, argv)
95 int argc;
96 char *argv[];
23b7cf0a 97{
05ee7127 98 int l_num, errnum=0, l_err=0;
672cc1c0 99 char *l_fnametmp, *l_col, buf[2];
e692f66f 100 struct winsize win;
ecbf4ad0 101
672cc1c0 102 setbuffer(stdin, buf, 1);
e692f66f 103 line_length = ((l_col = getenv("COLUMNS")) == NULL ? 0 : atoi(l_col));
afdef93a
KB
104 if ((line_length == 0 && isatty(STDOUT_FILENO) &&
105 ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1))
e692f66f
KB
106 line_length = win.ws_col;
107 if (line_length == 0)
108 line_length = 78;
ecbf4ad0 109
035e94f8 110 Start = End = NULL;
ecbf4ad0
KB
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);
035e94f8 121 Start_default = End_default = 0;
ecbf4ad0 122 zsnum = 22; /* for the 'z' command */
71fc9f52 123 help_msg[0] = '\0';
ecbf4ad0
KB
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;
672cc1c0
KB
155 case 'v':
156#ifdef BSD
71fc9f52 157 (void)printf("ed: in BSD mode:\n");
672cc1c0
KB
158#endif
159#ifdef POSIX
71fc9f52 160 (void)printf("ed: in POSIX mode:\n");
672cc1c0 161#endif
afdef93a 162 break;
ecbf4ad0
KB
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}
23b7cf0a
KB
188
189/*
ecbf4ad0 190 * The command loop. What the command is that the user has specified
23b7cf0a
KB
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 */
23b7cf0a
KB
196void
197cmd_loop(inputt, errnum)
ecbf4ad0
KB
198 FILE *inputt;
199 int *errnum;
23b7cf0a 200{
ecbf4ad0
KB
201 LINE *l_tempp;
202 int l_last, l_jmp_flag;
203
e692f66f 204 l_last = 0; /* value in l_last may be clobbered (reset to = 0) by longjump, but that's okay */
ecbf4ad0
KB
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;
71fc9f52 219 (void)printf("\n?\n");
ecbf4ad0
KB
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);
05ee7127
KB
231 if (*errnum == 0)
232 goto errmsg2;
ecbf4ad0
KB
233 }
234 }
235 for (;;) {
236 if (prompt_str_flg == 1)
237 (void)printf("%s", prompt_string);
ecbf4ad0 238 ss = getc(inputt);
ecbf4ad0 239 *errnum = 0;
035e94f8
RC
240 l_tempp = Start = End = NULL;
241 Start_default = End_default = 1;
ecbf4ad0
KB
242
243 /*
244 * This isn't nice and alphabetical mainly because of
245 * restrictions with 'G' and 'V' (see ed(1)).
246 */
247 for (;;) {
248 switch (ss) {
249 case 'd':
250 d(inputt, errnum);
251 break;
252 case 'e':
253 case 'E':
254 e(inputt, errnum);
05ee7127
KB
255 if (*errnum == 0)
256 goto errmsg2;
ecbf4ad0
KB
257 break;
258 case 'f':
259 f(inputt, errnum);
260 break;
261 case 'a':
262 case 'c':
263 case 'i':
264 case 'g':
265 case 'G':
266 case 'v':
267 case 'V':
268 if (GV_flag == 1) {
269 (void)sprintf(help_msg,
270 "command `%c' illegal in G/V", ss);
271 *errnum = -1;
272 break;
273 }
274 switch (ss) {
275 case 'a':
276 a(inputt, errnum);
277 break;
278 case 'c':
279 c(inputt, errnum);
280 break;
281 case 'i':
282 i(inputt, errnum);
283 break;
284 default:
285 g(inputt, errnum);
286 }
287 break;
288 case 'h':
289 if (rol(inputt, errnum))
290 break;
71fc9f52
KB
291 if (help_msg[0])
292 (void)printf("%s\n", help_msg);
ecbf4ad0
KB
293 *errnum = 1;
294 break;
295 case 'H':
296 if (rol(inputt, errnum))
297 break;
298 if (help_flag == 0) {
299 help_flag = 1;
71fc9f52
KB
300 if (help_msg[0])
301 (void)printf("%s\n",
302 help_msg);
ecbf4ad0
KB
303 } else
304 help_flag = 0;
305 *errnum = 1;
306 break;
307 case 'j':
308 j(inputt, errnum);
309 break;
310 case 'k':
311 set_mark(inputt, errnum);
312 break;
313 case 'l':
314 l(inputt, errnum);
315 break;
316 case 'm':
317 m(inputt, errnum);
318 break;
23b7cf0a 319#ifdef POSIX
ecbf4ad0
KB
320 /* In POSIX-land 'P' toggles the prompt. */
321 case 'P':
322 if (rol(inputt, errnum))
323 break;
324 prompt_str_flg = prompt_str_flg ? 0 : 1;
325 *errnum = 1;
326 break;
23b7cf0a 327#endif
ecbf4ad0
KB
328 case '\n':
329 if (GV_flag == 1)
330 return;
331 /* For 'p' to consume. */
332 ungetc(ss, inputt);
333 if ((current == bottom) && (End == NULL)) {
334 strcpy(help_msg, "at end of buffer");
335 *errnum = -1;
336 break;
337 }
338 current = current->below;
23b7cf0a 339#ifdef BSD
ecbf4ad0
KB
340 /* In BSD 'P'=='p'. */
341 case 'P':
23b7cf0a 342#endif
ecbf4ad0
KB
343 case 'p':
344 p(inputt, errnum, 0);
345 break;
346 case 'n':
347 p(inputt, errnum, 1);
348 break;
349 /*
350 * An EOF means 'q' unless we're still in the middle
351 * of a global command, in which case it was just the
352 * end of the command list found.
353 */
354 case EOF:
355 clearerr(inputt);
356 if (g_flag > 0)
357 return;
672cc1c0 358 /*ss = 'q';*/
ecbf4ad0
KB
359 case 'q':
360 case 'Q':
05ee7127
KB
361 if ((!isatty(STDIN_FILENO)) && (ss == 'q'))
362 ss = 'Q';
ecbf4ad0
KB
363 q(inputt, errnum);
364 break;
365 case 'r':
366 r(inputt, errnum);
05ee7127
KB
367 if (*errnum == 0)
368 goto errmsg2;
ecbf4ad0
KB
369 break;
370 case 's':
371 s(inputt, errnum);
372 break;
373 case 't':
374 t(inputt, errnum);
375 break;
376 case 'u':
377 u(inputt, errnum);
378 break;
379 case 'w':
380 case 'W':
381 w(inputt, errnum);
382 break;
383 case 'z':
384 z(inputt, errnum);
385 break;
386 case '!':
387 bang(inputt, errnum);
388 break;
389 case '=':
390 equal(inputt, errnum);
391 break;
392 /*
393 * Control of address forms from here down.
394 *
395 * It's a head-game to understand why ";" and "," look
396 * as they do below, but a lot of it has to do with ";"
397 * and "," being special address pair forms themselves
398 * and the compatibility for address "chains".
399 */
400 case ';':
035e94f8
RC
401 if (End_default == 1 && Start_default == 1) {
402 Start = current;
ecbf4ad0 403 End = bottom;
035e94f8 404 Start_default = End_default = 0;
ecbf4ad0 405 } else {
035e94f8
RC
406 Start = current = End;
407 Start_default = 0;
ecbf4ad0
KB
408 End_default = 1;
409 }
410 l_tempp = NULL;
411 break;
412 /*
413 * Note address ".,x" where x is a cmd is legal; not a
414 * bug - for backward compatability.
415 */
416 case ',':
035e94f8
RC
417 if (End_default == 1 && Start_default == 1) {
418 Start = top;
ecbf4ad0 419 End = bottom;
035e94f8 420 Start_default = End_default = 0;
ecbf4ad0 421 } else {
035e94f8
RC
422 Start = End;
423 Start_default = 0;
ecbf4ad0
KB
424 End_default = 1;
425 }
426 l_tempp = NULL;
427 break;
428 case '%':
429 if (End_default == 0) {
430 strcpy(help_msg,
431 "'%' is an address pair");
432 *errnum = -1;
433 break;
434 }
035e94f8 435 Start = top;
ecbf4ad0 436 End = bottom;
035e94f8 437 Start_default = End_default = 0;
ecbf4ad0
KB
438 l_tempp = NULL;
439 break;
440 /*
441 * Within address_conv => l_last = '+', foobar, but
442 * historical and now POSIX...
443 */
444 case ' ':
445 break;
446 case '0':
447 case '1':
448 case '2':
449 case '3':
450 case '4':
451 case '5':
452 case '6':
453 case '7':
454 case '8':
455 case '9':
456 case '-':
457 case '^':
458 case '+':
459 case '\'':
460 case '$':
461 case '?':
462 case '/':
463 case '.':
464 ungetc(ss, inputt);
035e94f8 465 if (Start_default == 0 && End_default == 0) {
ecbf4ad0
KB
466 strcpy(help_msg,
467 "badly formed address");
468 *errnum = -1;
469 break;
470 }
471 ss = l_last;
472 l_tempp = address_conv(l_tempp, inputt, errnum);
473 if (*errnum < 0)
474 break;
475 End = l_tempp;
476 End_default = 0;
035e94f8
RC
477 if (Start_default == 0)
478 *errnum = address_check(Start, End);
ecbf4ad0
KB
479 break;
480 default:
481 *errnum = -1;
482 strcpy(help_msg, "unknown command");
483 break;
484 } /* end-switch(ss) */
485
486 /* Things came out okay with the last command. */
487 if (*errnum > 0) {
488 if (GV_flag == 1)
489 return;
490 /* Do the suffixes if there were any. */
491 if (printsfx > 0) {
035e94f8 492 Start = End = current;
ecbf4ad0
KB
493 ungetc(ss, inputt);
494 if (printsfx == 1)
495 p(inputt, errnum, 0);
496 else
497 if (printsfx == 2)
498 p(inputt, errnum, 1);
499 else if (printsfx == 4)
500 l(inputt, errnum);
501 /* Unlikely it's needed, but... */
502 if (*errnum < 0)
503 goto errmsg;
504 }
505 break;
506 }
507 /* There was a problem with the last command. */
508 else if (*errnum < 0) {
509errmsg: while (((ss = getc(inputt)) != '\n') &&
510 (ss != EOF));
05ee7127
KB
511 (void)printf("?\n");
512errmsg2: if (help_flag)
513 (void)printf("%s\n", help_msg);
afdef93a 514 exit_code = 4;
afdef93a
KB
515/* for people wanting scripts to carry on after a cmd error, then
516 * define NOENDONSCRIPT on the compile line.
517 */
518#ifndef NOENDONSCRIPT
519 if (!isatty(STDIN_FILENO)) {
520 ss = 'Q';
521 ungetc('\n', inputt);
522 q(inputt, errnum);
523 }
524#endif
e692f66f 525 break;
ecbf4ad0
KB
526 }
527 l_last = ss;
528 ss = getc(inputt);
529 }
530 }
531}
23b7cf0a 532
ecbf4ad0
KB
533/*
534 * Exits ed and prints an appropriate message about the command line
23b7cf0a
KB
535 * being malformed (see below).
536 */
23b7cf0a
KB
537void
538ed_exit(err)
ecbf4ad0 539 int err;
23b7cf0a 540{
ecbf4ad0
KB
541 switch (err) {
542 case 1:
543 (void)fprintf(stderr, "ed: illegal option\n");
544 break;
545 case 2:
546 (void)fprintf(stderr, "ed: missing promptstring\n");
547 break;
548 case 3:
549 (void)fprintf(stderr, "ed: too many filenames\n");
550 break;
551 case 4:
552 (void)fprintf(stderr, "ed: out of memory error\n");
553 break;
e692f66f
KB
554 case 5:
555 (void)fprintf(stderr, "ed: unable to create buffer\n");
556 break;
ecbf4ad0
KB
557 default:
558 (void)fprintf(stderr, "ed: command line error\n");
559 break;
560 }
561 (void)fprintf(stderr,
562 "ed: ed [ -s ] [ -p promptstring ] [ filename ]\n");
563 exit(1);
564}
23b7cf0a
KB
565
566/*
ecbf4ad0
KB
567 * SIGINT is never turned off. We flag it happened and then pay attention
568 * to it at certain logical locations in the code we don't do more here
569 * cause some of our buffer pointer's may be in an inbetween state at the
570 * time of the SIGINT. So we flag it happened, let the local fn handle it
571 * and do a jump back to the cmd_loop
23b7cf0a 572 */
ecbf4ad0
KB
573static void
574sigint_handler(signo)
575 int signo;
23b7cf0a 576{
ecbf4ad0 577 sigint_flag = 1;
c842a051
KB
578 if (sigspecial3) {
579 sigspecial3 = 0;
580 SIGINT_ILACTION;
581 }
e692f66f
KB
582 else
583 if (sigspecial);
584 else
585 SIGINT_ACTION;
ecbf4ad0
KB
586}
587
588static void
589sighup_handler(signo)
590 int signo;
591{
ecbf4ad0
KB
592 sighup_flag = 1;
593 undo();
594 do_hup();
595 /* NOTREACHED */
596
597 SIGHUP_ACTION;
598}