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