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 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 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 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 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 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