Commit | Line | Data |
---|---|---|
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 | 12 | static 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 | ||
38 | int nn_max, nn_max_flag, start_default, End_default, address_flag; | |
e692f66f | 39 | int zsnum, filename_flag, add_flag=0, join_flag=0; |
23b7cf0a | 40 | int help_flag=0; |
e692f66f KB |
41 | #ifdef STDIO |
42 | FILE *fhtmp; | |
43 | int file_seek; | |
44 | #endif | |
23b7cf0a | 45 | |
e692f66f | 46 | #ifdef DBI |
23b7cf0a | 47 | DB *dbhtmp; |
e692f66f | 48 | #endif |
23b7cf0a KB |
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; | |
c842a051 KB |
73 | jmp_buf ctrl_position, ctrl_position2, ctrl_position3; /* For SIGnal handling. */ |
74 | int sigint_flag, sighup_flag, sigspecial=0, sigspecial2=0, sigspecial3=0; | |
23b7cf0a | 75 | |
ecbf4ad0 KB |
76 | static void sigint_handler __P((int)); |
77 | static 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 | */ | |
85 | int | |
86 | main(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 |
178 | void |
179 | cmd_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) { | |
480 | errmsg: 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 |
500 | void |
501 | ed_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 |
536 | static void |
537 | sigint_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 | ||
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 | } |