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 | |
afdef93a KB |
12 | char 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 | 18 | static 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 | 45 | int nn_max, nn_max_flag, Start_default, End_default, address_flag; |
e692f66f | 46 | int zsnum, filename_flag, add_flag=0, join_flag=0; |
71fc9f52 | 47 | int help_flag=0, gut_num=-1; |
e692f66f KB |
48 | #ifdef STDIO |
49 | FILE *fhtmp; | |
50 | int file_seek; | |
51 | #endif | |
23b7cf0a | 52 | |
e692f66f | 53 | #ifdef DBI |
23b7cf0a | 54 | DB *dbhtmp; |
e692f66f | 55 | #endif |
23b7cf0a KB |
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; | |
afdef93a | 62 | LINE **gut=NULL; |
23b7cf0a KB |
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 | ||
035e94f8 | 67 | LINE *top, *current, *bottom, *Start, *End; |
23b7cf0a KB |
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() */ | |
afdef93a | 78 | int explain_flag=1, g_flag=0, GV_flag=0, printsfx=0, exit_code=0; |
23b7cf0a KB |
79 | long change_flag=0L; |
80 | int line_length; | |
c842a051 KB |
81 | jmp_buf ctrl_position, ctrl_position2, ctrl_position3; /* For SIGnal handling. */ |
82 | int sigint_flag, sighup_flag, sigspecial=0, sigspecial2=0, sigspecial3=0; | |
23b7cf0a | 83 | |
ecbf4ad0 KB |
84 | static void sigint_handler __P((int)); |
85 | static 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 | */ | |
93 | int | |
94 | main(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 |
196 | void |
197 | cmd_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) { | |
509 | errmsg: while (((ss = getc(inputt)) != '\n') && | |
510 | (ss != EOF)); | |
05ee7127 KB |
511 | (void)printf("?\n"); |
512 | errmsg2: 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 |
537 | void |
538 | ed_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 |
573 | static void |
574 | sigint_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 | ||
588 | static void | |
589 | sighup_handler(signo) | |
590 | int signo; | |
591 | { | |
ecbf4ad0 KB |
592 | sighup_flag = 1; |
593 | undo(); | |
594 | do_hup(); | |
595 | /* NOTREACHED */ | |
596 | ||
597 | SIGHUP_ACTION; | |
598 | } |