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 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 18 static char sccsid[] = "@(#)main.c 5.12 (Berkeley) 05/11/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 110 Start = End = NULL; 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); 121 Start_default = End_default = 0; 122 zsnum = 22; /* for the 'z' command */ 123 help_msg[0] = '\0'; 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; 155 case 'v': 156 #ifdef BSD 157 (void)printf("ed: in BSD mode:\n"); 158 #endif 159 #ifdef POSIX 160 (void)printf("ed: in POSIX mode:\n"); 161 #endif 162 break; 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 } 188 189 /* 190 * The command loop. What the command is that the user has specified 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 */ 196 void 197 cmd_loop(inputt, errnum) 198 FILE *inputt; 199 int *errnum; 200 { 201 LINE *l_tempp; 202 int l_last, l_jmp_flag; 203 204 l_last = 0; /* value in l_last may be clobbered (reset to = 0) by longjump, but that's okay */ 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; 219 (void)printf("\n?\n"); 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); 231 if (*errnum == 0) 232 goto errmsg2; 233 } 234 } 235 for (;;) { 236 if (prompt_str_flg == 1) 237 (void)printf("%s", prompt_string); 238 ss = getc(inputt); 239 *errnum = 0; 240 l_tempp = Start = End = NULL; 241 Start_default = End_default = 1; 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); 255 if (*errnum == 0) 256 goto errmsg2; 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; 291 if (help_msg[0]) 292 (void)printf("%s\n", help_msg); 293 *errnum = 1; 294 break; 295 case 'H': 296 if (rol(inputt, errnum)) 297 break; 298 if (help_flag == 0) { 299 help_flag = 1; 300 if (help_msg[0]) 301 (void)printf("%s\n", 302 help_msg); 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; 319 #ifdef POSIX 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; 327 #endif 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; 339 #ifdef BSD 340 /* In BSD 'P'=='p'. */ 341 case 'P': 342 #endif 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; 358 /*ss = 'q';*/ 359 case 'q': 360 case 'Q': 361 if ((!isatty(STDIN_FILENO)) && (ss == 'q')) 362 ss = 'Q'; 363 q(inputt, errnum); 364 break; 365 case 'r': 366 r(inputt, errnum); 367 if (*errnum == 0) 368 goto errmsg2; 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 ';': 401 if (End_default == 1 && Start_default == 1) { 402 Start = current; 403 End = bottom; 404 Start_default = End_default = 0; 405 } else { 406 Start = current = End; 407 Start_default = 0; 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 ',': 417 if (End_default == 1 && Start_default == 1) { 418 Start = top; 419 End = bottom; 420 Start_default = End_default = 0; 421 } else { 422 Start = End; 423 Start_default = 0; 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 } 435 Start = top; 436 End = bottom; 437 Start_default = End_default = 0; 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); 465 if (Start_default == 0 && End_default == 0) { 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; 477 if (Start_default == 0) 478 *errnum = address_check(Start, End); 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) { 492 Start = End = current; 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)); 511 (void)printf("?\n"); 512 errmsg2: if (help_flag) 513 (void)printf("%s\n", help_msg); 514 exit_code = 4; 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 525 break; 526 } 527 l_last = ss; 528 ss = getc(inputt); 529 } 530 } 531 } 532 533 /* 534 * Exits ed and prints an appropriate message about the command line 535 * being malformed (see below). 536 */ 537 void 538 ed_exit(err) 539 int err; 540 { 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; 554 case 5: 555 (void)fprintf(stderr, "ed: unable to create buffer\n"); 556 break; 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 } 565 566 /* 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 572 */ 573 static void 574 sigint_handler(signo) 575 int signo; 576 { 577 sigint_flag = 1; 578 if (sigspecial3) { 579 sigspecial3 = 0; 580 SIGINT_ILACTION; 581 } 582 else 583 if (sigspecial); 584 else 585 SIGINT_ACTION; 586 } 587 588 static void 589 sighup_handler(signo) 590 int signo; 591 { 592 sighup_flag = 1; 593 undo(); 594 do_hup(); 595 /* NOTREACHED */ 596 597 SIGHUP_ACTION; 598 } 599