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.10 (Berkeley) 04/28/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; 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 u_stk = NULL; 124 d_stk = NULL; 125 u_current = u_top = u_bottom = NULL; 126 u_set = 0; /* for in d after a j */ 127 filename_flag = 0; 128 filename_current = NULL; 129 130 l_num = 1; 131 for (;;) { 132 /* Process the command line options */ 133 if (l_num >= argc) 134 break; 135 switch (argv[l_num][0]) { 136 case '-': 137 switch (argv[l_num][1]) { 138 case '\0': /* this is why 'getopt' not used */ 139 case 's': 140 explain_flag = 0; 141 break; 142 case 'p': 143 if (++l_num < argc) { 144 prompt_string = 145 calloc(strlen(argv[l_num]), 146 sizeof(char)); 147 if (prompt_string == NULL) 148 ed_exit(4); 149 strcpy(prompt_string, argv[l_num]); 150 prompt_str_flg = 1; 151 break; 152 } 153 l_err = 1; 154 case 'v': 155 #ifdef BSD 156 printf("ed: in BSD mode:\n"); 157 #endif 158 #ifdef POSIX 159 printf("ed: in POSIX mode:\n"); 160 #endif 161 break; 162 default: 163 l_err++; 164 ed_exit(l_err); 165 } 166 break; 167 default: 168 if (name_set) 169 ed_exit(3); 170 strcpy(l_fnametmp, argv[l_num]); 171 filename_current = l_fnametmp; 172 name_set = 1; 173 if (prompt_str_flg) 174 break; 175 /* default ed prompt */ 176 prompt_string = (char *) calloc(3, sizeof(char)); 177 strcpy(prompt_string, "*"); 178 break; 179 } 180 l_num++; 181 } 182 183 start_up_flag = 1; 184 cmd_loop(stdin, &errnum); 185 /* NOTREACHED */ 186 } 187 188 /* 189 * The command loop. What the command is that the user has specified 190 * is determined here. This is not just for commands coming from 191 * the terminal but any standard i/o stream; see the global commands. 192 * Some of the commands are handled within here (i.e. 'H') while most 193 * are handled in their own functions (as called). 194 */ 195 void 196 cmd_loop(inputt, errnum) 197 FILE *inputt; 198 int *errnum; 199 { 200 LINE *l_tempp; 201 int l_last, l_jmp_flag; 202 203 l_last = 0; /* value in l_last may be clobbered (reset to = 0) by longjump, but that's okay */ 204 205 if (g_flag == 0) { /* big, BIG trouble if we don't check! think. */ 206 /* set the jump point for the signals */ 207 l_jmp_flag = setjmp(ctrl_position); 208 signal(SIGINT, sigint_handler); 209 signal(SIGHUP, sighup_handler); 210 switch (l_jmp_flag) { 211 case JMP_SET: 212 break; 213 /* Some general cleanup not specific to the jmp pt. */ 214 case INTERUPT: 215 sigint_flag = 0; 216 GV_flag = 0; /* safest place to do these flags */ 217 g_flag = 0; 218 printf("\n?\n"); 219 break; 220 case HANGUP: /* shouldn't get here. */ 221 break; 222 default: 223 (void)fprintf(stderr, "Signal jump problem\n"); 224 } 225 /* Only do this once! */ 226 if (start_up_flag) { 227 start_up_flag = 0; 228 /* simulate the 'e' at startup */ 229 e2(inputt, errnum); 230 } 231 } 232 for (;;) { 233 if (prompt_str_flg == 1) 234 (void)printf("%s", prompt_string); 235 ss = getc(inputt); 236 *errnum = 0; 237 l_tempp = Start = End = NULL; 238 Start_default = End_default = 1; 239 240 /* 241 * This isn't nice and alphabetical mainly because of 242 * restrictions with 'G' and 'V' (see ed(1)). 243 */ 244 for (;;) { 245 switch (ss) { 246 case 'd': 247 d(inputt, errnum); 248 break; 249 case 'e': 250 case 'E': 251 e(inputt, errnum); 252 break; 253 case 'f': 254 f(inputt, errnum); 255 break; 256 case 'a': 257 case 'c': 258 case 'i': 259 case 'g': 260 case 'G': 261 case 'v': 262 case 'V': 263 if (GV_flag == 1) { 264 (void)sprintf(help_msg, 265 "command `%c' illegal in G/V", ss); 266 *errnum = -1; 267 break; 268 } 269 switch (ss) { 270 case 'a': 271 a(inputt, errnum); 272 break; 273 case 'c': 274 c(inputt, errnum); 275 break; 276 case 'i': 277 i(inputt, errnum); 278 break; 279 default: 280 g(inputt, errnum); 281 } 282 break; 283 case 'h': 284 if (rol(inputt, errnum)) 285 break; 286 (void)printf("%s\n", help_msg); 287 *errnum = 1; 288 break; 289 case 'H': 290 if (rol(inputt, errnum)) 291 break; 292 if (help_flag == 0) { 293 help_flag = 1; 294 printf("?: %s\n", help_msg); 295 } else 296 help_flag = 0; 297 *errnum = 1; 298 break; 299 case 'j': 300 j(inputt, errnum); 301 break; 302 case 'k': 303 set_mark(inputt, errnum); 304 break; 305 case 'l': 306 l(inputt, errnum); 307 break; 308 case 'm': 309 m(inputt, errnum); 310 break; 311 #ifdef POSIX 312 /* In POSIX-land 'P' toggles the prompt. */ 313 case 'P': 314 if (rol(inputt, errnum)) 315 break; 316 prompt_str_flg = prompt_str_flg ? 0 : 1; 317 *errnum = 1; 318 break; 319 #endif 320 case '\n': 321 if (GV_flag == 1) 322 return; 323 /* For 'p' to consume. */ 324 ungetc(ss, inputt); 325 if ((current == bottom) && (End == NULL)) { 326 strcpy(help_msg, "at end of buffer"); 327 *errnum = -1; 328 break; 329 } 330 current = current->below; 331 #ifdef BSD 332 /* In BSD 'P'=='p'. */ 333 case 'P': 334 #endif 335 case 'p': 336 p(inputt, errnum, 0); 337 break; 338 case 'n': 339 p(inputt, errnum, 1); 340 break; 341 /* 342 * An EOF means 'q' unless we're still in the middle 343 * of a global command, in which case it was just the 344 * end of the command list found. 345 */ 346 case EOF: 347 clearerr(inputt); 348 if (g_flag > 0) 349 return; 350 /*ss = 'q';*/ 351 case 'q': 352 case 'Q': 353 q(inputt, errnum); 354 break; 355 case 'r': 356 r(inputt, errnum); 357 break; 358 case 's': 359 s(inputt, errnum); 360 break; 361 case 't': 362 t(inputt, errnum); 363 break; 364 case 'u': 365 u(inputt, errnum); 366 break; 367 case 'w': 368 case 'W': 369 w(inputt, errnum); 370 break; 371 case 'z': 372 z(inputt, errnum); 373 break; 374 case '!': 375 bang(inputt, errnum); 376 break; 377 case '=': 378 equal(inputt, errnum); 379 break; 380 /* 381 * Control of address forms from here down. 382 * 383 * It's a head-game to understand why ";" and "," look 384 * as they do below, but a lot of it has to do with ";" 385 * and "," being special address pair forms themselves 386 * and the compatibility for address "chains". 387 */ 388 case ';': 389 if (End_default == 1 && Start_default == 1) { 390 Start = current; 391 End = bottom; 392 Start_default = End_default = 0; 393 } else { 394 Start = current = End; 395 Start_default = 0; 396 End_default = 1; 397 } 398 l_tempp = NULL; 399 break; 400 /* 401 * Note address ".,x" where x is a cmd is legal; not a 402 * bug - for backward compatability. 403 */ 404 case ',': 405 if (End_default == 1 && Start_default == 1) { 406 Start = top; 407 End = bottom; 408 Start_default = End_default = 0; 409 } else { 410 Start = End; 411 Start_default = 0; 412 End_default = 1; 413 } 414 l_tempp = NULL; 415 break; 416 case '%': 417 if (End_default == 0) { 418 strcpy(help_msg, 419 "'%' is an address pair"); 420 *errnum = -1; 421 break; 422 } 423 Start = top; 424 End = bottom; 425 Start_default = End_default = 0; 426 l_tempp = NULL; 427 break; 428 /* 429 * Within address_conv => l_last = '+', foobar, but 430 * historical and now POSIX... 431 */ 432 case ' ': 433 break; 434 case '0': 435 case '1': 436 case '2': 437 case '3': 438 case '4': 439 case '5': 440 case '6': 441 case '7': 442 case '8': 443 case '9': 444 case '-': 445 case '^': 446 case '+': 447 case '\'': 448 case '$': 449 case '?': 450 case '/': 451 case '.': 452 ungetc(ss, inputt); 453 if (Start_default == 0 && End_default == 0) { 454 strcpy(help_msg, 455 "badly formed address"); 456 *errnum = -1; 457 break; 458 } 459 ss = l_last; 460 l_tempp = address_conv(l_tempp, inputt, errnum); 461 if (*errnum < 0) 462 break; 463 End = l_tempp; 464 End_default = 0; 465 if (Start_default == 0) 466 *errnum = address_check(Start, End); 467 break; 468 default: 469 *errnum = -1; 470 strcpy(help_msg, "unknown command"); 471 break; 472 } /* end-switch(ss) */ 473 474 /* Things came out okay with the last command. */ 475 if (*errnum > 0) { 476 if (GV_flag == 1) 477 return; 478 /* Do the suffixes if there were any. */ 479 if (printsfx > 0) { 480 Start = End = current; 481 ungetc(ss, inputt); 482 if (printsfx == 1) 483 p(inputt, errnum, 0); 484 else 485 if (printsfx == 2) 486 p(inputt, errnum, 1); 487 else if (printsfx == 4) 488 l(inputt, errnum); 489 /* Unlikely it's needed, but... */ 490 if (*errnum < 0) 491 goto errmsg; 492 } 493 break; 494 } 495 /* There was a problem with the last command. */ 496 else if (*errnum < 0) { 497 errmsg: while (((ss = getc(inputt)) != '\n') && 498 (ss != EOF)); 499 exit_code = 4; 500 if (help_flag == 1) 501 printf("?: %s\n", help_msg); 502 else 503 printf("?\n"); 504 /* for people wanting scripts to carry on after a cmd error, then 505 * define NOENDONSCRIPT on the compile line. 506 */ 507 #ifndef NOENDONSCRIPT 508 if (!isatty(STDIN_FILENO)) { 509 ss = 'Q'; 510 ungetc('\n', inputt); 511 q(inputt, errnum); 512 } 513 #endif 514 if (g_flag > 0) 515 return; 516 break; 517 } 518 l_last = ss; 519 ss = getc(inputt); 520 } 521 } 522 } 523 524 /* 525 * Exits ed and prints an appropriate message about the command line 526 * being malformed (see below). 527 */ 528 void 529 ed_exit(err) 530 int err; 531 { 532 switch (err) { 533 case 1: 534 (void)fprintf(stderr, "ed: illegal option\n"); 535 break; 536 case 2: 537 (void)fprintf(stderr, "ed: missing promptstring\n"); 538 break; 539 case 3: 540 (void)fprintf(stderr, "ed: too many filenames\n"); 541 break; 542 case 4: 543 (void)fprintf(stderr, "ed: out of memory error\n"); 544 break; 545 case 5: 546 (void)fprintf(stderr, "ed: unable to create buffer\n"); 547 break; 548 default: 549 (void)fprintf(stderr, "ed: command line error\n"); 550 break; 551 } 552 (void)fprintf(stderr, 553 "ed: ed [ -s ] [ -p promptstring ] [ filename ]\n"); 554 exit(1); 555 } 556 557 /* 558 * SIGINT is never turned off. We flag it happened and then pay attention 559 * to it at certain logical locations in the code we don't do more here 560 * cause some of our buffer pointer's may be in an inbetween state at the 561 * time of the SIGINT. So we flag it happened, let the local fn handle it 562 * and do a jump back to the cmd_loop 563 */ 564 static void 565 sigint_handler(signo) 566 int signo; 567 { 568 sigint_flag = 1; 569 if (sigspecial3) { 570 sigspecial3 = 0; 571 SIGINT_ILACTION; 572 } 573 else 574 if (sigspecial); 575 else 576 SIGINT_ACTION; 577 } 578 579 static void 580 sighup_handler(signo) 581 int signo; 582 { 583 sighup_flag = 1; 584 undo(); 585 do_hup(); 586 /* NOTREACHED */ 587 588 SIGHUP_ACTION; 589 } 590