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 static char sccsid[] = "@(#)main.c 5.7 (Berkeley) 03/18/93"; 13 #endif /* not lint */ 14 15 #include <sys/types.h> 16 #include <sys/ioctl.h> 17 18 #include <regex.h> 19 #include <setjmp.h> 20 #include <signal.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <limits.h> 25 26 #ifdef DBI 27 #include <db.h> 28 #endif 29 30 #include "ed.h" 31 #include "extern.h" 32 33 /* 34 * This is where all of the "global" variables are declared. They are 35 * set for extern in the ed.h header file (so everyone can get them). 36 */ 37 38 int nn_max, nn_max_flag, Start_default, End_default, address_flag; 39 int zsnum, filename_flag, add_flag=0, join_flag=0; 40 int help_flag=0; 41 #ifdef STDIO 42 FILE *fhtmp; 43 int file_seek; 44 #endif 45 46 #ifdef DBI 47 DB *dbhtmp; 48 #endif 49 50 LINE *nn_max_start, *nn_max_end; 51 52 struct MARK mark_matrix[26]; /* in init set all to null */ 53 54 char *text; 55 char *filename_current, *prompt_string=NULL, help_msg[130]; 56 char *template=NULL; 57 int prompt_str_flg=0, start_up_flag=0, name_set=0; 58 59 LINE *top, *current, *bottom, *Start, *End; 60 struct u_layer *u_stk; 61 struct d_layer *d_stk; 62 LINE *u_current, *u_top, *u_bottom; 63 int u_set; 64 regex_t RE_comp; 65 regmatch_t RE_match[RE_SEC]; 66 int RE_sol=0, RE_flag=0; 67 char *RE_patt=NULL; 68 69 int ss; /* for the getc() */ 70 int explain_flag=1, g_flag=0, GV_flag=0, printsfx=0; 71 long change_flag=0L; 72 int line_length; 73 jmp_buf ctrl_position, ctrl_position2, ctrl_position3; /* For SIGnal handling. */ 74 int sigint_flag, sighup_flag, sigspecial=0, sigspecial2=0, sigspecial3=0; 75 76 static void sigint_handler __P((int)); 77 static void sighup_handler __P((int)); 78 79 /* 80 * Starts the whole show going. Set things up as the arguments spec 81 * in the shell and set a couple of the global variables. 82 * 83 * Note naming viol'n with errnum for consistancy. 84 */ 85 int 86 main(argc, argv) 87 int argc; 88 char *argv[]; 89 { 90 int l_num, errnum = 0, l_err = 0; 91 char *l_fnametmp, *l_col, buf[2]; 92 struct winsize win; 93 94 setbuffer(stdin, buf, 1); 95 line_length = ((l_col = getenv("COLUMNS")) == NULL ? 0 : atoi(l_col)); 96 if (line_length == 0 && isatty(STDOUT_FILENO) && 97 ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1) 98 line_length = win.ws_col; 99 if (line_length == 0) 100 line_length = 78; 101 102 Start = End = NULL; 103 top = bottom = NULL; 104 current = NULL; 105 nn_max_flag = 0; 106 nn_max_start = nn_max_end = NULL; 107 l_fnametmp = calloc(FILENAME_LEN, sizeof(char)); 108 if (l_fnametmp == NULL) 109 ed_exit(4); 110 text = calloc(NN_MAX_START + 2, sizeof(char)); 111 if (text == NULL) 112 ed_exit(4); 113 Start_default = End_default = 0; 114 zsnum = 22; /* for the 'z' command */ 115 u_stk = NULL; 116 d_stk = NULL; 117 u_current = u_top = u_bottom = NULL; 118 u_set = 0; /* for in d after a j */ 119 filename_flag = 0; 120 filename_current = NULL; 121 122 l_num = 1; 123 for (;;) { 124 /* Process the command line options */ 125 if (l_num >= argc) 126 break; 127 switch (argv[l_num][0]) { 128 case '-': 129 switch (argv[l_num][1]) { 130 case '\0': /* this is why 'getopt' not used */ 131 case 's': 132 explain_flag = 0; 133 break; 134 case 'p': 135 if (++l_num < argc) { 136 prompt_string = 137 calloc(strlen(argv[l_num]), 138 sizeof(char)); 139 if (prompt_string == NULL) 140 ed_exit(4); 141 strcpy(prompt_string, argv[l_num]); 142 prompt_str_flg = 1; 143 break; 144 } 145 l_err = 1; 146 case 'v': 147 #ifdef BSD 148 printf("ed: in BSD mode:\n"); 149 #endif 150 #ifdef POSIX 151 printf("ed: in POSIX mode:\n"); 152 #endif 153 default: 154 l_err++; 155 ed_exit(l_err); 156 } 157 break; 158 default: 159 if (name_set) 160 ed_exit(3); 161 strcpy(l_fnametmp, argv[l_num]); 162 filename_current = l_fnametmp; 163 name_set = 1; 164 if (prompt_str_flg) 165 break; 166 /* default ed prompt */ 167 prompt_string = (char *) calloc(3, sizeof(char)); 168 strcpy(prompt_string, "*"); 169 break; 170 } 171 l_num++; 172 } 173 174 start_up_flag = 1; 175 cmd_loop(stdin, &errnum); 176 /* NOTREACHED */ 177 } 178 179 /* 180 * The command loop. What the command is that the user has specified 181 * is determined here. This is not just for commands coming from 182 * the terminal but any standard i/o stream; see the global commands. 183 * Some of the commands are handled within here (i.e. 'H') while most 184 * are handled in their own functions (as called). 185 */ 186 void 187 cmd_loop(inputt, errnum) 188 FILE *inputt; 189 int *errnum; 190 { 191 LINE *l_tempp; 192 int l_last, l_jmp_flag; 193 194 l_last = 0; /* value in l_last may be clobbered (reset to = 0) by longjump, but that's okay */ 195 196 if (g_flag == 0) { /* big, BIG trouble if we don't check! think. */ 197 /* set the jump point for the signals */ 198 l_jmp_flag = setjmp(ctrl_position); 199 signal(SIGINT, sigint_handler); 200 signal(SIGHUP, sighup_handler); 201 switch (l_jmp_flag) { 202 case JMP_SET: 203 break; 204 /* Some general cleanup not specific to the jmp pt. */ 205 case INTERUPT: 206 sigint_flag = 0; 207 GV_flag = 0; /* safest place to do these flags */ 208 g_flag = 0; 209 printf("\n?\n"); 210 break; 211 case HANGUP: /* shouldn't get here. */ 212 break; 213 default: 214 (void)fprintf(stderr, "Signal jump problem\n"); 215 } 216 /* Only do this once! */ 217 if (start_up_flag) { 218 start_up_flag = 0; 219 /* simulate the 'e' at startup */ 220 e2(inputt, errnum); 221 } 222 } 223 for (;;) { 224 if (prompt_str_flg == 1) 225 (void)printf("%s", prompt_string); 226 ss = getc(inputt); 227 *errnum = 0; 228 l_tempp = Start = End = NULL; 229 Start_default = End_default = 1; 230 231 /* 232 * This isn't nice and alphabetical mainly because of 233 * restrictions with 'G' and 'V' (see ed(1)). 234 */ 235 for (;;) { 236 switch (ss) { 237 case 'd': 238 d(inputt, errnum); 239 break; 240 case 'e': 241 case 'E': 242 e(inputt, errnum); 243 break; 244 case 'f': 245 f(inputt, errnum); 246 break; 247 case 'a': 248 case 'c': 249 case 'i': 250 case 'g': 251 case 'G': 252 case 'v': 253 case 'V': 254 if (GV_flag == 1) { 255 (void)sprintf(help_msg, 256 "command `%c' illegal in G/V", ss); 257 *errnum = -1; 258 break; 259 } 260 switch (ss) { 261 case 'a': 262 a(inputt, errnum); 263 break; 264 case 'c': 265 c(inputt, errnum); 266 break; 267 case 'i': 268 i(inputt, errnum); 269 break; 270 default: 271 g(inputt, errnum); 272 } 273 break; 274 case 'h': 275 if (rol(inputt, errnum)) 276 break; 277 (void)printf("%s\n", help_msg); 278 *errnum = 1; 279 break; 280 case 'H': 281 if (rol(inputt, errnum)) 282 break; 283 if (help_flag == 0) { 284 help_flag = 1; 285 printf("%?: %s\n", help_msg); 286 } else 287 help_flag = 0; 288 *errnum = 1; 289 break; 290 case 'j': 291 j(inputt, errnum); 292 break; 293 case 'k': 294 set_mark(inputt, errnum); 295 break; 296 case 'l': 297 l(inputt, errnum); 298 break; 299 case 'm': 300 m(inputt, errnum); 301 break; 302 #ifdef POSIX 303 /* In POSIX-land 'P' toggles the prompt. */ 304 case 'P': 305 if (rol(inputt, errnum)) 306 break; 307 prompt_str_flg = prompt_str_flg ? 0 : 1; 308 *errnum = 1; 309 break; 310 #endif 311 case '\n': 312 if (GV_flag == 1) 313 return; 314 /* For 'p' to consume. */ 315 ungetc(ss, inputt); 316 if ((current == bottom) && (End == NULL)) { 317 strcpy(help_msg, "at end of buffer"); 318 *errnum = -1; 319 break; 320 } 321 current = current->below; 322 #ifdef BSD 323 /* In BSD 'P'=='p'. */ 324 case 'P': 325 #endif 326 case 'p': 327 p(inputt, errnum, 0); 328 break; 329 case 'n': 330 p(inputt, errnum, 1); 331 break; 332 /* 333 * An EOF means 'q' unless we're still in the middle 334 * of a global command, in which case it was just the 335 * end of the command list found. 336 */ 337 case EOF: 338 clearerr(inputt); 339 if (g_flag > 0) 340 return; 341 /*ss = 'q';*/ 342 case 'q': 343 case 'Q': 344 q(inputt, errnum); 345 break; 346 case 'r': 347 r(inputt, errnum); 348 break; 349 case 's': 350 s(inputt, errnum); 351 break; 352 case 't': 353 t(inputt, errnum); 354 break; 355 case 'u': 356 u(inputt, errnum); 357 break; 358 case 'w': 359 case 'W': 360 w(inputt, errnum); 361 break; 362 case 'z': 363 z(inputt, errnum); 364 break; 365 case '!': 366 bang(inputt, errnum); 367 break; 368 case '=': 369 equal(inputt, errnum); 370 break; 371 /* 372 * Control of address forms from here down. 373 * 374 * It's a head-game to understand why ";" and "," look 375 * as they do below, but a lot of it has to do with ";" 376 * and "," being special address pair forms themselves 377 * and the compatibility for address "chains". 378 */ 379 case ';': 380 if (End_default == 1 && Start_default == 1) { 381 Start = current; 382 End = bottom; 383 Start_default = End_default = 0; 384 } else { 385 Start = current = End; 386 Start_default = 0; 387 End_default = 1; 388 } 389 l_tempp = NULL; 390 break; 391 /* 392 * Note address ".,x" where x is a cmd is legal; not a 393 * bug - for backward compatability. 394 */ 395 case ',': 396 if (End_default == 1 && Start_default == 1) { 397 Start = top; 398 End = bottom; 399 Start_default = End_default = 0; 400 } else { 401 Start = End; 402 Start_default = 0; 403 End_default = 1; 404 } 405 l_tempp = NULL; 406 break; 407 case '%': 408 if (End_default == 0) { 409 strcpy(help_msg, 410 "'%' is an address pair"); 411 *errnum = -1; 412 break; 413 } 414 Start = top; 415 End = bottom; 416 Start_default = End_default = 0; 417 l_tempp = NULL; 418 break; 419 /* 420 * Within address_conv => l_last = '+', foobar, but 421 * historical and now POSIX... 422 */ 423 case ' ': 424 break; 425 case '0': 426 case '1': 427 case '2': 428 case '3': 429 case '4': 430 case '5': 431 case '6': 432 case '7': 433 case '8': 434 case '9': 435 case '-': 436 case '^': 437 case '+': 438 case '\'': 439 case '$': 440 case '?': 441 case '/': 442 case '.': 443 ungetc(ss, inputt); 444 if (Start_default == 0 && End_default == 0) { 445 strcpy(help_msg, 446 "badly formed address"); 447 *errnum = -1; 448 break; 449 } 450 ss = l_last; 451 l_tempp = address_conv(l_tempp, inputt, errnum); 452 if (*errnum < 0) 453 break; 454 End = l_tempp; 455 End_default = 0; 456 if (Start_default == 0) 457 *errnum = address_check(Start, End); 458 break; 459 default: 460 *errnum = -1; 461 strcpy(help_msg, "unknown command"); 462 break; 463 } /* end-switch(ss) */ 464 465 /* Things came out okay with the last command. */ 466 if (*errnum > 0) { 467 if (GV_flag == 1) 468 return; 469 /* Do the suffixes if there were any. */ 470 if (printsfx > 0) { 471 Start = End = current; 472 ungetc(ss, inputt); 473 if (printsfx == 1) 474 p(inputt, errnum, 0); 475 else 476 if (printsfx == 2) 477 p(inputt, errnum, 1); 478 else if (printsfx == 4) 479 l(inputt, errnum); 480 /* Unlikely it's needed, but... */ 481 if (*errnum < 0) 482 goto errmsg; 483 } 484 break; 485 } 486 /* There was a problem with the last command. */ 487 else if (*errnum < 0) { 488 errmsg: while (((ss = getc(inputt)) != '\n') && 489 (ss != EOF)); 490 if (help_flag == 1) 491 printf("%?: %s\n", help_msg); 492 else 493 printf("?\n"); 494 if (g_flag > 0) 495 return; 496 break; 497 } 498 l_last = ss; 499 ss = getc(inputt); 500 } 501 } 502 } 503 504 /* 505 * Exits ed and prints an appropriate message about the command line 506 * being malformed (see below). 507 */ 508 void 509 ed_exit(err) 510 int err; 511 { 512 switch (err) { 513 case 1: 514 (void)fprintf(stderr, "ed: illegal option\n"); 515 break; 516 case 2: 517 (void)fprintf(stderr, "ed: missing promptstring\n"); 518 break; 519 case 3: 520 (void)fprintf(stderr, "ed: too many filenames\n"); 521 break; 522 case 4: 523 (void)fprintf(stderr, "ed: out of memory error\n"); 524 break; 525 case 5: 526 (void)fprintf(stderr, "ed: unable to create buffer\n"); 527 break; 528 default: 529 (void)fprintf(stderr, "ed: command line error\n"); 530 break; 531 } 532 (void)fprintf(stderr, 533 "ed: ed [ -s ] [ -p promptstring ] [ filename ]\n"); 534 exit(1); 535 } 536 537 /* 538 * SIGINT is never turned off. We flag it happened and then pay attention 539 * to it at certain logical locations in the code we don't do more here 540 * cause some of our buffer pointer's may be in an inbetween state at the 541 * time of the SIGINT. So we flag it happened, let the local fn handle it 542 * and do a jump back to the cmd_loop 543 */ 544 static void 545 sigint_handler(signo) 546 int signo; 547 { 548 sigint_flag = 1; 549 if (sigspecial3) { 550 sigspecial3 = 0; 551 SIGINT_ILACTION; 552 } 553 else 554 if (sigspecial); 555 else 556 SIGINT_ACTION; 557 } 558 559 static void 560 sighup_handler(signo) 561 int signo; 562 { 563 (void)fprintf(stderr,"\n SIGHUP \n"); 564 sighup_flag = 1; 565 undo(); 566 do_hup(); 567 /* NOTREACHED */ 568 569 SIGHUP_ACTION; 570 } 571