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.11 (Berkeley) 04/30/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 } 232 } 233 for (;;) { 234 if (prompt_str_flg == 1) 235 (void)printf("%s", prompt_string); 236 ss = getc(inputt); 237 *errnum = 0; 238 l_tempp = Start = End = NULL; 239 Start_default = End_default = 1; 240 241 /* 242 * This isn't nice and alphabetical mainly because of 243 * restrictions with 'G' and 'V' (see ed(1)). 244 */ 245 for (;;) { 246 switch (ss) { 247 case 'd': 248 d(inputt, errnum); 249 break; 250 case 'e': 251 case 'E': 252 e(inputt, errnum); 253 break; 254 case 'f': 255 f(inputt, errnum); 256 break; 257 case 'a': 258 case 'c': 259 case 'i': 260 case 'g': 261 case 'G': 262 case 'v': 263 case 'V': 264 if (GV_flag == 1) { 265 (void)sprintf(help_msg, 266 "command `%c' illegal in G/V", ss); 267 *errnum = -1; 268 break; 269 } 270 switch (ss) { 271 case 'a': 272 a(inputt, errnum); 273 break; 274 case 'c': 275 c(inputt, errnum); 276 break; 277 case 'i': 278 i(inputt, errnum); 279 break; 280 default: 281 g(inputt, errnum); 282 } 283 break; 284 case 'h': 285 if (rol(inputt, errnum)) 286 break; 287 if (help_msg[0]) 288 (void)printf("%s\n", help_msg); 289 *errnum = 1; 290 break; 291 case 'H': 292 if (rol(inputt, errnum)) 293 break; 294 if (help_flag == 0) { 295 help_flag = 1; 296 if (help_msg[0]) 297 (void)printf("%s\n", 298 help_msg); 299 } else 300 help_flag = 0; 301 *errnum = 1; 302 break; 303 case 'j': 304 j(inputt, errnum); 305 break; 306 case 'k': 307 set_mark(inputt, errnum); 308 break; 309 case 'l': 310 l(inputt, errnum); 311 break; 312 case 'm': 313 m(inputt, errnum); 314 break; 315 #ifdef POSIX 316 /* In POSIX-land 'P' toggles the prompt. */ 317 case 'P': 318 if (rol(inputt, errnum)) 319 break; 320 prompt_str_flg = prompt_str_flg ? 0 : 1; 321 *errnum = 1; 322 break; 323 #endif 324 case '\n': 325 if (GV_flag == 1) 326 return; 327 /* For 'p' to consume. */ 328 ungetc(ss, inputt); 329 if ((current == bottom) && (End == NULL)) { 330 strcpy(help_msg, "at end of buffer"); 331 *errnum = -1; 332 break; 333 } 334 current = current->below; 335 #ifdef BSD 336 /* In BSD 'P'=='p'. */ 337 case 'P': 338 #endif 339 case 'p': 340 p(inputt, errnum, 0); 341 break; 342 case 'n': 343 p(inputt, errnum, 1); 344 break; 345 /* 346 * An EOF means 'q' unless we're still in the middle 347 * of a global command, in which case it was just the 348 * end of the command list found. 349 */ 350 case EOF: 351 clearerr(inputt); 352 if (g_flag > 0) 353 return; 354 /*ss = 'q';*/ 355 case 'q': 356 case 'Q': 357 q(inputt, errnum); 358 break; 359 case 'r': 360 r(inputt, errnum); 361 break; 362 case 's': 363 s(inputt, errnum); 364 break; 365 case 't': 366 t(inputt, errnum); 367 break; 368 case 'u': 369 u(inputt, errnum); 370 break; 371 case 'w': 372 case 'W': 373 w(inputt, errnum); 374 break; 375 case 'z': 376 z(inputt, errnum); 377 break; 378 case '!': 379 bang(inputt, errnum); 380 break; 381 case '=': 382 equal(inputt, errnum); 383 break; 384 /* 385 * Control of address forms from here down. 386 * 387 * It's a head-game to understand why ";" and "," look 388 * as they do below, but a lot of it has to do with ";" 389 * and "," being special address pair forms themselves 390 * and the compatibility for address "chains". 391 */ 392 case ';': 393 if (End_default == 1 && Start_default == 1) { 394 Start = current; 395 End = bottom; 396 Start_default = End_default = 0; 397 } else { 398 Start = current = End; 399 Start_default = 0; 400 End_default = 1; 401 } 402 l_tempp = NULL; 403 break; 404 /* 405 * Note address ".,x" where x is a cmd is legal; not a 406 * bug - for backward compatability. 407 */ 408 case ',': 409 if (End_default == 1 && Start_default == 1) { 410 Start = top; 411 End = bottom; 412 Start_default = End_default = 0; 413 } else { 414 Start = End; 415 Start_default = 0; 416 End_default = 1; 417 } 418 l_tempp = NULL; 419 break; 420 case '%': 421 if (End_default == 0) { 422 strcpy(help_msg, 423 "'%' is an address pair"); 424 *errnum = -1; 425 break; 426 } 427 Start = top; 428 End = bottom; 429 Start_default = End_default = 0; 430 l_tempp = NULL; 431 break; 432 /* 433 * Within address_conv => l_last = '+', foobar, but 434 * historical and now POSIX... 435 */ 436 case ' ': 437 break; 438 case '0': 439 case '1': 440 case '2': 441 case '3': 442 case '4': 443 case '5': 444 case '6': 445 case '7': 446 case '8': 447 case '9': 448 case '-': 449 case '^': 450 case '+': 451 case '\'': 452 case '$': 453 case '?': 454 case '/': 455 case '.': 456 ungetc(ss, inputt); 457 if (Start_default == 0 && End_default == 0) { 458 strcpy(help_msg, 459 "badly formed address"); 460 *errnum = -1; 461 break; 462 } 463 ss = l_last; 464 l_tempp = address_conv(l_tempp, inputt, errnum); 465 if (*errnum < 0) 466 break; 467 End = l_tempp; 468 End_default = 0; 469 if (Start_default == 0) 470 *errnum = address_check(Start, End); 471 break; 472 default: 473 *errnum = -1; 474 strcpy(help_msg, "unknown command"); 475 break; 476 } /* end-switch(ss) */ 477 478 /* Things came out okay with the last command. */ 479 if (*errnum > 0) { 480 if (GV_flag == 1) 481 return; 482 /* Do the suffixes if there were any. */ 483 if (printsfx > 0) { 484 Start = End = current; 485 ungetc(ss, inputt); 486 if (printsfx == 1) 487 p(inputt, errnum, 0); 488 else 489 if (printsfx == 2) 490 p(inputt, errnum, 1); 491 else if (printsfx == 4) 492 l(inputt, errnum); 493 /* Unlikely it's needed, but... */ 494 if (*errnum < 0) 495 goto errmsg; 496 } 497 break; 498 } 499 /* There was a problem with the last command. */ 500 else if (*errnum < 0) { 501 errmsg: while (((ss = getc(inputt)) != '\n') && 502 (ss != EOF)); 503 exit_code = 4; 504 if (g_flag == 0) { 505 (void)printf("?\n"); 506 if (help_flag) 507 (void)printf("%s\n", help_msg); 508 } 509 /* for people wanting scripts to carry on after a cmd error, then 510 * define NOENDONSCRIPT on the compile line. 511 */ 512 #ifndef NOENDONSCRIPT 513 if (!isatty(STDIN_FILENO)) { 514 ss = 'Q'; 515 ungetc('\n', inputt); 516 q(inputt, errnum); 517 } 518 #endif 519 break; 520 } 521 l_last = ss; 522 ss = getc(inputt); 523 } 524 } 525 } 526 527 /* 528 * Exits ed and prints an appropriate message about the command line 529 * being malformed (see below). 530 */ 531 void 532 ed_exit(err) 533 int err; 534 { 535 switch (err) { 536 case 1: 537 (void)fprintf(stderr, "ed: illegal option\n"); 538 break; 539 case 2: 540 (void)fprintf(stderr, "ed: missing promptstring\n"); 541 break; 542 case 3: 543 (void)fprintf(stderr, "ed: too many filenames\n"); 544 break; 545 case 4: 546 (void)fprintf(stderr, "ed: out of memory error\n"); 547 break; 548 case 5: 549 (void)fprintf(stderr, "ed: unable to create buffer\n"); 550 break; 551 default: 552 (void)fprintf(stderr, "ed: command line error\n"); 553 break; 554 } 555 (void)fprintf(stderr, 556 "ed: ed [ -s ] [ -p promptstring ] [ filename ]\n"); 557 exit(1); 558 } 559 560 /* 561 * SIGINT is never turned off. We flag it happened and then pay attention 562 * to it at certain logical locations in the code we don't do more here 563 * cause some of our buffer pointer's may be in an inbetween state at the 564 * time of the SIGINT. So we flag it happened, let the local fn handle it 565 * and do a jump back to the cmd_loop 566 */ 567 static void 568 sigint_handler(signo) 569 int signo; 570 { 571 sigint_flag = 1; 572 if (sigspecial3) { 573 sigspecial3 = 0; 574 SIGINT_ILACTION; 575 } 576 else 577 if (sigspecial); 578 else 579 SIGINT_ACTION; 580 } 581 582 static void 583 sighup_handler(signo) 584 int signo; 585 { 586 sighup_flag = 1; 587 undo(); 588 do_hup(); 589 /* NOTREACHED */ 590 591 SIGHUP_ACTION; 592 } 593