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