1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  27 /*        All Rights Reserved   */
  28 
  29 /*
  30  * Editor
  31  */
  32 
  33 #include        <crypt.h>
  34 #include        <libgen.h>
  35 #include        <wait.h>
  36 #include        <string.h>
  37 #include        <sys/types.h>
  38 #include        <locale.h>
  39 #include        <regexpr.h>
  40 #include        <regex.h>
  41 #include        <errno.h>
  42 #include        <paths.h>
  43 
  44 static const    char    *msgtab[] =
  45 {
  46         "write or open on pipe failed",                 /*  0 */
  47         "warning: expecting `w'",                       /*  1 */
  48         "mark not lower case ascii",                    /*  2 */
  49         "Cannot open input file",                       /*  3 */
  50         "PWB spec problem",                             /*  4 */
  51         "nothing to undo",                              /*  5 */
  52         "restricted shell",                             /*  6 */
  53         "cannot create output file",                    /*  7 */
  54         "filesystem out of space!",                     /*  8 */
  55         "cannot open file",                             /*  9 */
  56         "cannot link",                                  /* 10 */
  57         "Range endpoint too large",                     /* 11 */
  58         "unknown command",                              /* 12 */
  59         "search string not found",                      /* 13 */
  60         "-",                                            /* 14 */
  61         "line out of range",                            /* 15 */
  62         "bad number",                                   /* 16 */
  63         "bad range",                                    /* 17 */
  64         "Illegal address count",                        /* 18 */
  65         "incomplete global expression",                 /* 19 */
  66         "illegal suffix",                               /* 20 */
  67         "illegal or missing filename",                  /* 21 */
  68         "no space after command",                       /* 22 */
  69         "fork failed - try again",                      /* 23 */
  70         "maximum of 64 characters in file names",       /* 24 */
  71         "`\\digit' out of range",                       /* 25 */
  72         "interrupt",                                    /* 26 */
  73         "line too long",                                /* 27 */
  74         "illegal character in input file",              /* 28 */
  75         "write error",                                  /* 29 */
  76         "out of memory for append",                     /* 30 */
  77         "temp file too big",                            /* 31 */
  78         "I/O error on temp file",                       /* 32 */
  79         "multiple globals not allowed",                 /* 33 */
  80         "global too long",                              /* 34 */
  81         "no match",                                     /* 35 */
  82         "illegal or missing delimiter",                 /* 36 */
  83         "-",                                            /* 37 */
  84         "replacement string too long",                  /* 38 */
  85         "illegal move destination",                     /* 39 */
  86         "-",                                            /* 40 */
  87         "no remembered search string",                  /* 41 */
  88         "'\\( \\)' imbalance",                          /* 42 */
  89         "Too many `\\(' s",                             /* 43 */
  90         "more than 2 numbers given",                    /* 44 */
  91         "'\\}' expected",                               /* 45 */
  92         "first number exceeds second",                  /* 46 */
  93         "incomplete substitute",                        /* 47 */
  94         "newline unexpected",                           /* 48 */
  95         "'[ ]' imbalance",                              /* 49 */
  96         "regular expression overflow",                  /* 50 */
  97         "regular expression error",                     /* 51 */
  98         "command expected",                             /* 52 */
  99         "a, i, or c not allowed in G",                  /* 53 */
 100         "end of line expected",                         /* 54 */
 101         "no remembered replacement string",             /* 55 */
 102         "no remembered command",                        /* 56 */
 103         "illegal redirection",                          /* 57 */
 104         "possible concurrent update",                   /* 58 */
 105         "-",                                            /* 59 */
 106         "the x command has become X (upper case)",      /* 60 */
 107         "Warning: 'w' may destroy input file "
 108         "(due to `illegal char' read earlier)",
 109                                                         /* 61 */
 110         "Caution: 'q' may lose data in buffer;"
 111         " 'w' may destroy input file",
 112                                                         /* 62 */
 113         "Encryption of string failed",                  /* 63 */
 114         "Encryption facility not available",            /* 64 */
 115         "Cannot encrypt temporary file",                /* 65 */
 116         "Enter key:",                                   /* 66 */
 117         "Illegal byte sequence",                        /* 67 */
 118         "File does not exist",                          /* 68 */
 119         "tempnam failed",                               /* 69 */
 120         "Cannot open temporary file",                   /* 70 */
 121         0
 122 };
 123 
 124 #include <stdlib.h>
 125 #include <limits.h>
 126 #include <stdio.h>
 127 #include <signal.h>
 128 #include <sys/types.h>
 129 #include <sys/stat.h>
 130 #include <sys/statvfs.h>
 131 #include <unistd.h>
 132 #include <termio.h>
 133 #include <ctype.h>
 134 #include <setjmp.h>
 135 #include <fcntl.h>
 136 #include <wchar.h>        /* I18N */
 137 #include <wctype.h>       /* I18N */
 138 #include <widec.h>        /* I18N */
 139 
 140 #define FTYPE(A)        (A.st_mode)
 141 #define FMODE(A)        (A.st_mode)
 142 #define IDENTICAL(A, B) (A.st_dev == B.st_dev && A.st_ino == B.st_ino)
 143 #define ISBLK(A)        ((A.st_mode & S_IFMT) == S_IFBLK)
 144 #define ISCHR(A)        ((A.st_mode & S_IFMT) == S_IFCHR)
 145 #define ISDIR(A)        ((A.st_mode & S_IFMT) == S_IFDIR)
 146 #define ISFIFO(A)       ((A.st_mode & S_IFMT) == S_IFIFO)
 147 #define ISREG(A)        ((A.st_mode & S_IFMT) == S_IFREG)
 148 
 149 #define PUTM()  if (xcode >= 0) puts(gettext(msgtab[xcode]))
 150 #define UNGETC(c)       (peekc = c)
 151 #define FNSIZE  PATH_MAX
 152 #define LBSIZE  LINE_MAX
 153 
 154 /* size of substitution replacement pattern buffer */
 155 #define RHSIZE  (LINE_MAX*2)
 156 
 157 #define KSIZE   8
 158 
 159 #define READ    0
 160 #define WRITE   1
 161 
 162 extern  char    *optarg;        /* Value of argument */
 163 extern  int     optind;         /* Indicator of argument */
 164 
 165 struct  Fspec   {
 166         char    Ftabs[22];
 167         char    Fdel;
 168         unsigned char   Flim;
 169         char    Fmov;
 170         char    Ffill;
 171 };
 172 static struct  Fspec   fss;
 173 
 174 static char     *fsp;
 175 static int      fsprtn;
 176 static char     line[70];
 177 static char     *linp = line;
 178 static int      sig;
 179 static int      Xqt = 0;
 180 static int      lastc;
 181 static char     savedfile[FNSIZE];
 182 static char     file[FNSIZE];
 183 static char     funny[FNSIZE];
 184 static int      funlink = 0;
 185 static char     linebuf[LBSIZE];
 186 static char     *tstring = linebuf;
 187 
 188 static char *expbuf;
 189 
 190 static char     rhsbuf[RHSIZE];
 191 struct  lin     {
 192         long cur;
 193         long sav;
 194 };
 195 typedef struct lin *LINE;
 196 static LINE     zero;
 197 static LINE     dot;
 198 static LINE     dol;
 199 static LINE     endcore;
 200 static LINE     fendcore;
 201 static LINE     addr1;
 202 static LINE     addr2;
 203 static LINE     savdol, savdot;
 204 static int      globflg;
 205 static int      initflg;
 206 static char     genbuf[LBSIZE];
 207 static long     count;
 208 static int      numpass;        /* Number of passes thru dosub(). */
 209 static int      gsubf;          /* Occurrence value. LBSIZE-1=all. */
 210 static int      ocerr1; /* Allows lines NOT changed by dosub() to NOT be put */
 211                         /* out. Retains last line changed as current line. */
 212 static int      ocerr2; /* Flags if ANY line changed by substitute(). 0=nc. */
 213 static char     *nextip;
 214 static char     *linebp;
 215 static int      ninbuf;
 216 static int      peekc;
 217 static int      io;
 218 static void     (*oldhup)(), (*oldintr)();
 219 static void     (*oldquit)(), (*oldpipe)();
 220 static void     quit(int) __NORETURN;
 221 static int      vflag = 1;
 222 static int      xflag;
 223 static int      xtflag;
 224 static int      kflag;
 225 static int      crflag;
 226                 /* Flag for determining if file being read is encrypted */
 227 static int      hflag;
 228 static int      xcode = -1;
 229 static char     crbuf[LBSIZE];
 230 static  int     perm[2];
 231 static int      tperm[2];
 232 static int      permflag;
 233 static int      tpermflag;
 234 static int      col;
 235 static char     *globp;
 236 static int      tfile = -1;
 237 static int      tline;
 238 static char     *tfname;
 239 extern char     *locs;
 240 static char     ibuff[LBSIZE];
 241 static int      iblock = -1;
 242 static char     obuff[LBSIZE];
 243 static int      oblock = -1;
 244 static int      ichanged;
 245 static int      nleft;
 246 static long     savnames[26], names[26];
 247 static int      anymarks;
 248 static long     subnewa;
 249 static int      fchange;
 250 static int      nline;
 251 static int      fflg, shflg;
 252 static char     prompt[16] = "*";
 253 static int      rflg;
 254 static int      readflg;
 255 static int      eflg;
 256 static int      qflg = 0;
 257 static int      ncflg;
 258 static int      listn;
 259 static int      listf;
 260 static int      pflag;
 261 static int      flag28 = 0; /* Prevents write after a partial read */
 262 static int      save28 = 0; /* Flag whether buffer empty at start of read */
 263 static long     savtime;
 264 static char     *name = "SHELL";
 265 static char     *rshell = "/usr/lib/rsh";
 266 static char     *val;
 267 static char     *home;
 268 static int      nodelim;
 269 
 270 int     makekey(int *);
 271 int     _mbftowc(char *, wchar_t *, int (*)(), int *);
 272 static int      error(int code);
 273 static void     tlist(struct Fspec *);
 274 static void     tstd(struct Fspec *);
 275 static void     gdelete(void);
 276 static void     delete(void);
 277 static void     exfile(void);
 278 static void     filename(int comm);
 279 static void     newline(void);
 280 static int      gettty(void);
 281 static void     commands(void);
 282 static void     undo(void);
 283 static void     save(void);
 284 static void     strcopy(char *source, char *dest);
 285 static int      strequal(char **scan1, char *str);
 286 static int      stdtab(char *, char *);
 287 static int      lenchk(char *, struct Fspec *);
 288 static void     clear(struct Fspec *);
 289 static int      expnd(char *, char *, int *, struct Fspec *);
 290 static void     tincr(int, struct Fspec *);
 291 static void     targ(struct Fspec *);
 292 static int      numb(void);
 293 static int      fspec(char *, struct Fspec *, int);
 294 static void     red(char *);
 295 static void     newtime(void);
 296 static void     chktime(void);
 297 static void     getime(void);
 298 static void     mkfunny(void);
 299 static int      eopen(char *, int);
 300 static void     eclose(int f);
 301 static void     globaln(int);
 302 static char     *getkey(const char *);
 303 static int      execute(int, LINE);
 304 static void     error1(int);
 305 static int      getcopy(void);
 306 static void     move(int);
 307 static void     dosub(void);
 308 static int      getsub(void);
 309 static int      compsub(void);
 310 static void     substitute(int);
 311 static void     join(void);
 312 static void     global(int);
 313 static void     init(void);
 314 static void     rdelete(LINE, LINE);
 315 static void     append(int (*)(void), LINE);
 316 static int      getfile(void);
 317 static void     putfile(void);
 318 static void     onpipe(int);
 319 static void     onhup(int);
 320 static void     onintr(int);
 321 static void     setdot(void);
 322 static void     setall(void);
 323 static void     setnoaddr(void);
 324 static void     nonzero(void);
 325 static void     setzeroasone(void);
 326 static long     putline(void);
 327 static LINE     address(void);
 328 static char     *getaline(long);
 329 static char     *getblock(long, long);
 330 static char     *place(char *, char *, char *);
 331 static void     comple(wchar_t);
 332 static void     putchr(unsigned char);
 333 static void     putwchr(wchar_t);
 334 static int      getchr(void);
 335 static void     unixcom(void);
 336 static void     blkio(int, char *, ssize_t (*)());
 337 static void     reverse(LINE, LINE);
 338 static void     putd();
 339 static wchar_t  get_wchr(void);
 340 
 341 static struct stat      Fl, Tf;
 342 #ifndef RESEARCH
 343 static struct statvfs   U;
 344 static int      Short = 0;
 345 static mode_t   oldmask; /* No umask while writing */
 346 #endif
 347 static jmp_buf  savej;
 348 
 349 #ifdef  NULLS
 350 int     nulls;  /* Null count */
 351 #endif
 352 static long     ccount;
 353 
 354 static int      errcnt = 0;
 355 
 356 
 357 static void
 358 onpipe(int sig)
 359 {
 360         (int)error(0);
 361 }
 362 
 363 int
 364 main(int argc, char **argv)
 365 {
 366         char *p1, *p2;
 367         int c;
 368 
 369         (void) setlocale(LC_ALL, "");
 370 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 371 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
 372 #endif
 373         (void) textdomain(TEXT_DOMAIN);
 374 
 375         oldquit = signal(SIGQUIT, SIG_IGN);
 376         oldhup = signal(SIGHUP, SIG_IGN);
 377         oldintr = signal(SIGINT, SIG_IGN);
 378         oldpipe = signal(SIGPIPE, onpipe);
 379         if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
 380                 signal(SIGTERM, quit);
 381         p1 = *argv;
 382         while (*p1++)
 383                 ;
 384         while (--p1 >= *argv)
 385                 if (*p1 == '/')
 386                         break;
 387         *argv = p1 + 1;
 388         /* if SHELL set in environment and is /usr/lib/rsh, set rflg */
 389         if ((val = getenv(name)) != NULL)
 390                 if (strcmp(val, rshell) == 0)
 391                         rflg++;
 392         if (**argv == 'r')
 393                 rflg++;
 394         home = getenv("HOME");
 395         while (1) {
 396                 while ((c = getopt(argc, argv, "sp:qxC")) != EOF) {
 397                         switch (c) {
 398 
 399                         case 's':
 400                                 vflag = 0;
 401                                 break;
 402 
 403                         case 'p':
 404                                 strncpy(prompt, optarg, sizeof (prompt)-1);
 405                                 shflg = 1;
 406                                 break;
 407 
 408                         case 'q':
 409                                 signal(SIGQUIT, SIG_DFL);
 410                                 vflag = 1;
 411                                 break;
 412 
 413                         case 'x':
 414                                 crflag = -1;
 415                                 xflag = 1;
 416                                 break;
 417 
 418                         case 'C':
 419                                 crflag = 1;
 420                                 xflag = 1;
 421                                 break;
 422 
 423                         case '?':
 424                                 (void) fprintf(stderr, gettext(
 425                 "Usage:   ed [- | -s] [-p string] [-x] [-C] [file]\n"
 426                 "         red [- | -s] [-p string] [-x] [-C] [file]\n"));
 427                                 exit(2);
 428                         }
 429                 }
 430                 if (argv[optind] && strcmp(argv[optind], "-") == 0 &&
 431                     strcmp(argv[optind-1], "--") != 0) {
 432                         vflag = 0;
 433                         optind++;
 434                         continue;
 435                 }
 436                 break;
 437         }
 438         argc = argc - optind;
 439         argv = &argv[optind];
 440 
 441         if (xflag) {
 442                 if (permflag)
 443                         crypt_close(perm);
 444                 permflag = 1;
 445                 kflag = run_setkey(&perm[0], getkey(msgtab[66]));
 446                 if (kflag == -1) {
 447                         puts(gettext(msgtab[64]));
 448                         xflag = 0;
 449                         kflag = 0;
 450                 }
 451                 if (kflag == 0)
 452                         crflag = 0;
 453         }
 454 
 455         if (argc > 0) {
 456                 p1 = *argv;
 457                 if (strlen(p1) >= (size_t)FNSIZE) {
 458                         puts(gettext("file name too long"));
 459                         if (kflag)
 460                                 crypt_close(perm);
 461                         exit(2);
 462                 }
 463                 p2 = savedfile;
 464                 while (*p2++ = *p1++)
 465                         ;
 466                 globp = "e";
 467                 fflg++;
 468         } else  /* editing with no file so set savtime to 0 */
 469                 savtime = 0;
 470         eflg++;
 471         if ((tfname = tempnam("", "ea")) == NULL) {
 472                 puts(gettext(msgtab[69]));
 473                 exit(2);
 474         }
 475 
 476         fendcore = (LINE)sbrk(0);
 477         init();
 478         if (oldintr != SIG_IGN)
 479                 signal(SIGINT, onintr);
 480         if (oldhup != SIG_IGN)
 481                 signal(SIGHUP, onhup);
 482         setjmp(savej);
 483         commands();
 484         quit(sig);
 485         return (0);
 486 }
 487 
 488 static void
 489 commands(void)
 490 {
 491         LINE a1;
 492         int c;
 493         char *p1, *p2;
 494         int fsave, m, n;
 495 
 496         for (;;) {
 497         nodelim = 0;
 498         if (pflag) {
 499                 pflag = 0;
 500                 addr1 = addr2 = dot;
 501                 goto print;
 502         }
 503         if (shflg && globp == 0)
 504                 write(1, gettext(prompt), strlen(gettext(prompt)));
 505         addr1 = 0;
 506         addr2 = 0;
 507         if ((c = getchr()) == ',') {
 508                 addr1 = zero + 1;
 509                 addr2 = dol;
 510 #ifdef XPG6
 511         /* XPG4 - it was an error if the second address was */
 512         /* input and the first address was ommitted     */
 513         /* Parse second address */
 514                 if ((a1 = address()) != 0) {
 515                         addr2 = a1;
 516                 }
 517 #endif
 518                 c = getchr();
 519                 goto swch;
 520         } else if (c == ';') {
 521                 addr1 = dot;
 522                 addr2 = dol;
 523 #ifdef XPG6
 524         /* XPG4 - it was an error if the second address was */
 525         /* input and the first address was ommitted     */
 526         /* Parse second address */
 527                 if ((a1 = address()) != 0) {
 528                         addr2 = a1;
 529                 }
 530 #endif
 531                 c = getchr();
 532                 goto swch;
 533         } else
 534                 peekc = c;
 535         do {
 536                 addr1 = addr2;
 537                 if ((a1 = address()) == 0) {
 538                         c = getchr();
 539                         break;
 540                 }
 541                 addr2 = a1;
 542                 if ((c = getchr()) == ';') {
 543                         c = ',';
 544                         dot = a1;
 545                 }
 546         } while (c == ',');
 547         if (addr1 == 0)
 548                 addr1 = addr2;
 549 swch:
 550         switch (c) {
 551 
 552         case 'a':
 553                 setdot();
 554                 newline();
 555                 if (!globflg) save();
 556                 append(gettty, addr2);
 557                 continue;
 558 
 559         case 'c':
 560 #ifdef XPG6
 561                 setzeroasone();
 562 #endif
 563                 delete();
 564                 append(gettty, addr1-1);
 565 
 566                 /* XPG4 - If no new lines are inserted, then the current */
 567                 /* line becomes the line after the lines deleted. */
 568 
 569                 if (((linebuf[0] != '.') || (dot == (addr1-1))) &&
 570                     (addr2 <= dol))
 571                         dot = addr1;
 572                 continue;
 573 
 574         case 'd':
 575                 delete();
 576                 continue;
 577 
 578         case 'E':
 579                 fchange = 0;
 580                 c = 'e';
 581                 /* FALLTHROUGH */
 582         case 'e':
 583                 fflg++;
 584                 setnoaddr();
 585                 if (vflag && fchange) {
 586                         fchange = 0;
 587                         (void) error(1);
 588                 }
 589                 filename(c);
 590                 eflg++;
 591                 init();
 592                 addr2 = zero;
 593                 goto caseread;
 594 
 595         case 'f':
 596                 setnoaddr();
 597                 filename(c);
 598                 if (!ncflg)  /* there is a filename */
 599                         getime();
 600                 else
 601                         ncflg--;
 602                 puts(savedfile);
 603                 continue;
 604 
 605         case 'g':
 606                 global(1);
 607                 continue;
 608         case 'G':
 609                 globaln(1);
 610                 continue;
 611 
 612         case 'h':
 613                 newline();
 614                 setnoaddr();
 615                 PUTM();
 616                 continue;
 617 
 618         case 'H':
 619                 newline();
 620                 setnoaddr();
 621                 if (!hflag) {
 622                         hflag = 1;
 623                         PUTM();
 624                 }
 625                 else
 626                         hflag = 0;
 627                 continue;
 628 
 629         case 'i':
 630 #ifdef XPG6
 631                 setzeroasone();
 632 #endif
 633                 setdot();
 634                 nonzero();
 635                 newline();
 636                 if (!globflg) save();
 637                 append(gettty, addr2-1);
 638                 if (dot == addr2-1)
 639                         dot += 1;
 640                 continue;
 641 
 642         case 'j':
 643                 if (addr2 == 0) {
 644                         addr1 = dot;
 645                         addr2 = dot+1;
 646                 }
 647                 setdot();
 648                 newline();
 649                 nonzero();
 650                 if (!globflg) save();
 651                 join();
 652                 continue;
 653 
 654         case 'k':
 655                 if ((c = getchr()) < 'a' || c > 'z')
 656                         (void) error(2);
 657                 newline();
 658                 setdot();
 659                 nonzero();
 660                 names[c-'a'] = addr2->cur & ~01;
 661                 anymarks |= 01;
 662                 continue;
 663 
 664         case 'm':
 665                 move(0);
 666                 continue;
 667 
 668         case '\n':
 669                 if (addr2 == 0)
 670                         addr2 = dot+1;
 671                 addr1 = addr2;
 672                 goto print;
 673 
 674         case 'n':
 675                 listn++;
 676                 newline();
 677                 goto print;
 678 
 679         case 'l':
 680                 listf++;
 681                 /* FALLTHROUGH */
 682         case 'p':
 683                 newline();
 684         print:
 685                 setdot();
 686                 nonzero();
 687                 a1 = addr1;
 688                 do {
 689                         if (listn) {
 690                                 count = a1 - zero;
 691                                 putd();
 692                                 putchr('\t');
 693                         }
 694                         puts(getaline((a1++)->cur));
 695                 } while (a1 <= addr2);
 696                 dot = addr2;
 697                 pflag = 0;
 698                 listn = 0;
 699                 listf = 0;
 700                 continue;
 701 
 702         case 'Q':
 703                 fchange = 0;
 704                 /* FALLTHROUGH */
 705         case 'q':
 706                 setnoaddr();
 707                 newline();
 708                 quit(sig);
 709 
 710         case 'r':
 711                 filename(c);
 712         caseread:
 713                 readflg = 1;
 714                 save28 = (dol != fendcore);
 715                 if (crflag == 2 || crflag == -2)
 716                         crflag = -1; /* restore crflag for next file */
 717                 errno = 0;
 718                 if ((io = eopen(file, O_RDONLY)) < 0) {
 719                         lastc = '\n';
 720                         /* if first entering editor and file does not exist */
 721                         /* set saved access time to 0 */
 722                         if (eflg) {
 723                                 savtime = 0;
 724                                 eflg  = 0;
 725                                 if (c == 'e' && vflag == 0)
 726                                         qflg = 1;
 727                         }
 728                         if (errno == ENOENT) {
 729                                 (void) error(68);
 730                         } else {
 731                                 (void) error(3);
 732                         }
 733                 }
 734                 /* get last mod time of file */
 735                 /* eflg - entered editor with ed or e  */
 736                 if (eflg) {
 737                         eflg = 0;
 738                         getime();
 739                 }
 740                 setall();
 741                 ninbuf = 0;
 742                 n = zero != dol;
 743 #ifdef NULLS
 744                 nulls = 0;
 745 #endif
 746                 if (!globflg && (c == 'r')) save();
 747                 append(getfile, addr2);
 748                 exfile();
 749                 readflg = 0;
 750                 fchange = n;
 751                 continue;
 752 
 753         case 's':
 754                 setdot();
 755                 nonzero();
 756                 if (!globflg) save();
 757                 substitute(globp != 0);
 758                 continue;
 759 
 760         case 't':
 761                 move(1);
 762                 continue;
 763 
 764         case 'u':
 765                 setdot();
 766                 newline();
 767                 if (!initflg)
 768                         undo();
 769                 else
 770                         (void) error(5);
 771                 fchange = 1;
 772                 continue;
 773 
 774         case 'v':
 775                 global(0);
 776                 continue;
 777         case 'V':
 778                 globaln(0);
 779                 continue;
 780 
 781         case 'W':
 782         case 'w':
 783                 if (flag28) {
 784                         flag28 = 0;
 785                         fchange = 0;
 786                         (void) error(61);
 787                 }
 788                 setall();
 789 
 790                 /* on NULL-RE condition do not generate error */
 791 
 792                 if ((linebuf[0] != '.') && (zero != dol) &&
 793                     (addr1 <= zero || addr2 > dol))
 794                         (void) error(15);
 795                 filename(c);
 796                 if (Xqt) {
 797                         io = eopen(file, O_WRONLY);
 798                         n = 1;  /* set n so newtime will not execute */
 799                 } else {
 800                         struct stat lFl;
 801                         fstat(tfile, &Tf);
 802                         if (stat(file, &Fl) < 0) {
 803                                 if ((io = creat(file, S_IRUSR|S_IWUSR|S_IRGRP
 804                                     |S_IWGRP|S_IROTH|S_IWOTH)) < 0)
 805                                         (void) error(7);
 806                                 fstat(io, &Fl);
 807                                 Fl.st_mtime = 0;
 808                                 lFl = Fl;
 809                                 close(io);
 810                         } else {
 811 #ifndef RESEARCH
 812                                 oldmask = umask(0);
 813                                 /*
 814                                  * Must determine if file is
 815                                  * a symbolic link
 816                                  */
 817                                 lstat(file, &lFl);
 818 #endif
 819                         }
 820 #ifndef RESEARCH
 821                         /*
 822                          * Determine if there are enough free blocks on system
 823                          */
 824                         if (!Short && statvfs(file, &U) == 0 &&
 825                             U.f_bfree < ((Tf.st_size/U.f_frsize) + 100)) {
 826                                 Short = 1;
 827                                 (void) error(8);
 828                         }
 829                         Short = 0;
 830 #endif
 831                         p1 = savedfile;         /* The current filename */
 832                         p2 = file;
 833                         m = strcmp(p1, p2);
 834                         if (c == 'w' && Fl.st_nlink == 1 && ISREG(lFl)) {
 835                                 if (close(open(file, O_WRONLY)) < 0)
 836                                         (void) error(9);
 837                                 if (!(n = m))
 838                                         chktime();
 839                                 mkfunny();
 840                                 /*
 841                                  * If funlink equals one it means that
 842                                  * funny points to a valid file which must
 843                                  * be unlinked when interrupted.
 844                                  */
 845 
 846                                 funlink = 1;
 847                                 if ((io = creat(funny, FMODE(Fl))) >= 0) {
 848                                         chown(funny, Fl.st_uid, Fl.st_gid);
 849                                         chmod(funny, FMODE(Fl));
 850                                         putfile();
 851                                         exfile();
 852 
 853                                         if (rename(funny, file))
 854                                                 (void) error(10);
 855                                         funlink = 0;
 856                                         /* if filenames are the same */
 857                                         if (!n)
 858                                                 newtime();
 859                                         /* check if entire buffer was written */
 860                                         fsave = fchange;
 861                                         if (((addr1 == zero) ||
 862                                             (addr1 == (zero + 1))) &&
 863                                             (addr2 == dol))
 864                                                 fchange = 0;
 865                                         else
 866                                                 fchange = 1;
 867                                         if (fchange == 1 && m != 0)
 868                                                 fchange = fsave;
 869                                         continue;
 870                                 }
 871                         } else {
 872                                 n = 1;  /* set n so newtime will not execute */
 873                         }
 874                         if ((io = open(file,
 875                             (c == 'w') ? O_WRONLY|O_CREAT|O_TRUNC
 876                             : O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR
 877                             |S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0)
 878                                 (void) error(7);
 879                 }
 880                 putfile();
 881                 exfile();
 882                 if (!n)
 883                         newtime();
 884                 fsave = fchange;
 885                 fchange = (((addr1 == zero) || (addr1 == (zero + 1))) &&
 886                     (addr2 == dol)) ? 0 : 1;
 887         /* Leave fchange alone if partial write was to another file */
 888                 if (fchange == 1 && m != 0) fchange = fsave;
 889                 continue;
 890 
 891         case 'C':
 892                 crflag = 1;
 893                         /*
 894                          * C is same as X, but always assume input files are
 895                          * ciphertext
 896                          */
 897                 goto encrypt;
 898 
 899         case 'X':
 900                 crflag = -1;
 901 encrypt:
 902                 setnoaddr();
 903                 newline();
 904                 xflag = 1;
 905                 if (permflag)
 906                         (void) crypt_close(perm);
 907                 permflag = 1;
 908                 if ((kflag = run_setkey(&perm[0], getkey(msgtab[66]))) == -1) {
 909                         xflag = 0;
 910                         kflag = 0;
 911                         crflag = 0;
 912                         (void) error(64);
 913                 }
 914                 if (kflag == 0)
 915                         crflag = 0;
 916                 continue;
 917 
 918         case '=':
 919                 setall();
 920                 newline();
 921                 count = (addr2-zero)&077777;
 922                 putd();
 923                 putchr('\n');
 924                 continue;
 925 
 926         case '!':
 927                 unixcom();
 928                 continue;
 929 
 930         case EOF:
 931                 return;
 932 
 933         case 'P':
 934                 setnoaddr();
 935                 newline();
 936                 if (shflg)
 937                         shflg = 0;
 938                 else
 939                         shflg++;
 940                 continue;
 941         }
 942         if (c == 'x')
 943                 (void) error(60);
 944         else
 945                 (void) error(12);
 946         }
 947 }
 948 
 949 LINE
 950 address(void)
 951 {
 952         int minus, c;
 953         LINE a1;
 954         int n, relerr, retval;
 955 
 956         minus = 0;
 957         a1 = 0;
 958         for (;;) {
 959                 c = getchr();
 960                 if ('0' <= c && c <= '9') {
 961                         n = 0;
 962                         do {
 963                                 n *= 10;
 964                                 n += c - '0';
 965                         } while ((c = getchr()) >= '0' && c <= '9');
 966                         peekc = c;
 967                         if (a1 == 0)
 968                                 a1 = zero;
 969                         if (minus < 0)
 970                                 n = -n;
 971                         a1 += n;
 972                         minus = 0;
 973                         continue;
 974                 }
 975                 relerr = 0;
 976                 if (a1 || minus)
 977                         relerr++;
 978                 switch (c) {
 979                 case ' ':
 980                 case '\t':
 981                         continue;
 982 
 983                 case '+':
 984                         minus++;
 985                         if (a1 == 0)
 986                                 a1 = dot;
 987                         continue;
 988 
 989                 case '-':
 990                 case '^':
 991                         minus--;
 992                         if (a1 == 0)
 993                                 a1 = dot;
 994                         continue;
 995 
 996                 case '?':
 997                 case '/':
 998                         comple(c);
 999                         a1 = dot;
1000                         for (;;) {
1001                                 if (c == '/') {
1002                                         a1++;
1003                                         if (a1 > dol)
1004                                                 a1 = zero;
1005                                 } else {
1006                                         a1--;
1007                                         if (a1 < zero)
1008                                                 a1 = dol;
1009                                 }
1010 
1011                                 if (execute(0, a1))
1012                                         break;
1013                                 if (a1 == dot)
1014                                         (void) error(13);
1015                         }
1016                         break;
1017 
1018                 case '$':
1019                         a1 = dol;
1020                         break;
1021 
1022                 case '.':
1023                         a1 = dot;
1024                         break;
1025 
1026                 case '\'':
1027                         if ((c = getchr()) < 'a' || c > 'z')
1028                                 (void) error(2);
1029                         for (a1 = zero; a1 <= dol; a1++)
1030                                 if (names[c-'a'] == (a1->cur & ~01))
1031                                         break;
1032                         break;
1033 
1034                 default:
1035                         peekc = c;
1036                         if (a1 == 0)
1037                                 return (0);
1038                         a1 += minus;
1039 
1040                         /* on NULL-RE condition do not generate error */
1041 
1042                         if ((linebuf[0] != '.') && (a1 < zero || a1 > dol))
1043                                 (void) error(15);
1044                         return (a1);
1045                 }
1046                 if (relerr)
1047                         (void) error(16);
1048         }
1049 }
1050 
1051 static void
1052 setdot(void)
1053 {
1054         if (addr2 == 0)
1055                 addr1 = addr2 = dot;
1056         if (addr1 > addr2)
1057                 (void) error(17);
1058 }
1059 
1060 static void
1061 setall(void)
1062 {
1063         if (addr2 == 0) {
1064                 addr1 = zero+1;
1065                 addr2 = dol;
1066                 if (dol == zero)
1067                         addr1 = zero;
1068         }
1069         setdot();
1070 }
1071 
1072 static void
1073 setnoaddr(void)
1074 {
1075         if (addr2)
1076                 (void) error(18);
1077 }
1078 
1079 static void
1080 nonzero(void)
1081 {
1082         /* on NULL-RE condition do not generate error */
1083 
1084         if ((linebuf[0] != '.') && (addr1 <= zero || addr2 > dol))
1085                 (void) error(15);
1086 }
1087 
1088 static void
1089 setzeroasone(void)
1090 {
1091 /* for the c and i commands 0 equal to 1 address */
1092         if (addr1 == zero) {
1093                 addr1 = zero+1;
1094         }
1095         if (addr2 == zero) {
1096                 addr2 = zero+1;
1097         }
1098 }
1099 
1100 
1101 static void
1102 newline(void)
1103 {
1104         int c;
1105 
1106         if ((c = getchr()) == '\n')
1107                 return;
1108         if (c == 'p' || c == 'l' || c == 'n') {
1109                 pflag++;
1110                 if (c == 'l') listf++;
1111                 if (c == 'n') listn++;
1112                 if ((c = getchr()) == '\n')
1113                         return;
1114         }
1115         (void) error(20);
1116 }
1117 
1118 static void
1119 filename(int comm)
1120 {
1121         char *p1, *p2;
1122         int c;
1123         int i = 0;
1124 
1125         count = 0;
1126         c = getchr();
1127         if (c == '\n' || c == EOF) {
1128                 p1 = savedfile;
1129                 if (*p1 == 0 && comm != 'f')
1130                         (void) error(21);
1131                 /* ncflg set means do not get mod time of file */
1132                 /* since no filename followed f */
1133                 if (comm == 'f')
1134                         ncflg++;
1135                 p2 = file;
1136                 while (*p2++ = *p1++)
1137                         ;
1138                 red(savedfile);
1139                 return;
1140         }
1141         if (c != ' ')
1142                 (void) error(22);
1143         while ((c = getchr()) == ' ')
1144                 ;
1145         if (c == '!')
1146                 ++Xqt, c = getchr();
1147         if (c == '\n')
1148                 (void) error(21);
1149         p1 = file;
1150         do {
1151                 if (++i >= FNSIZE)
1152                         (void) error(24);
1153                 *p1++ = c;
1154                 if (c == EOF || (c == ' ' && !Xqt))
1155                         (void) error(21);
1156         } while ((c = getchr()) != '\n');
1157         *p1++ = 0;
1158         if (Xqt)
1159                 if (comm == 'f') {
1160                         --Xqt;
1161                         (void) error(57);
1162                 }
1163                 else
1164                         return;
1165         if (savedfile[0] == 0 || comm == 'e' || comm == 'f') {
1166                 p1 = savedfile;
1167                 p2 = file;
1168                 while (*p1++ = *p2++)
1169                         ;
1170         }
1171         red(file);
1172 }
1173 
1174 
1175 static void
1176 exfile(void)
1177 {
1178 #ifdef NULLS
1179         int c;
1180 #endif
1181 
1182 #ifndef RESEARCH
1183         if (oldmask) {
1184                 umask(oldmask);
1185                 oldmask = 0;
1186         }
1187 #endif
1188         eclose(io);
1189         io = -1;
1190         if (vflag) {
1191                 putd();
1192                 putchr('\n');
1193 #ifdef NULLS
1194                 if (nulls) {
1195                         c = count;
1196                         count = nulls;
1197                         nulls = 0;
1198                         putd();
1199                         puts(gettext(" nulls replaced by '\\0'"));
1200                         count = c;
1201                 }
1202 #endif
1203         }
1204 }
1205 
1206 static void
1207 onintr(int sig)
1208 {
1209         signal(SIGINT, onintr);
1210         putchr('\n');
1211         lastc = '\n';
1212         globflg = 0;
1213         if (funlink) unlink(funny); /* remove tmp file */
1214         /* if interrupted a read, only part of file may be in buffer */
1215         if (readflg) {
1216                 sprintf(tstring, "\007read may be incomplete - beware!\007");
1217                 puts(gettext(tstring));
1218                 fchange = 0;
1219         }
1220         (void) error(26);
1221 }
1222 
1223 static void
1224 onhup(int sig)
1225 {
1226         signal(SIGINT, SIG_IGN);
1227         signal(SIGHUP, SIG_IGN);
1228         /*
1229          * if there are lines in file and file was not written
1230          * since last update, save in ed.hup, or $HOME/ed.hup
1231          */
1232         if (dol > zero && fchange == 1) {
1233                 addr1 = zero+1;
1234                 addr2 = dol;
1235                 io = creat("ed.hup",
1236                     S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
1237                 if (io < 0 && home) {
1238                         char    *fn;
1239 
1240                         fn = (char *)calloc(strlen(home) + 8, sizeof (char));
1241                         if (fn) {
1242                                 strcpy(fn, home);
1243                                 strcat(fn, "/ed.hup");
1244                                 io = creat(fn, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP
1245                                     |S_IROTH|S_IWOTH);
1246                                 free(fn);
1247                         }
1248                 }
1249                 if (io > 0)
1250                         putfile();
1251         }
1252         fchange = 0;
1253         ++errcnt;
1254         quit(sig);
1255 }
1256 
1257 static int
1258 error(int code)
1259 {
1260         int c;
1261 
1262         if (code == 28 && save28 == 0) {
1263                 fchange = 0;
1264                 flag28++;
1265         }
1266         readflg = 0;
1267         ++errcnt;
1268         listf = listn = 0;
1269         pflag = 0;
1270 #ifndef RESEARCH
1271         if (oldmask) {
1272                 umask(oldmask);
1273                 oldmask = 0;
1274         }
1275 #endif
1276 #ifdef NULLS    /* Not really nulls, but close enough */
1277         /* This is a bug because of buffering */
1278         if (code == 28) /* illegal char. */
1279                 putd();
1280 #endif
1281         /* Cant open file or file does not exist */
1282         if ((code == 3) || (code == 68)) {
1283                 if (qflg == 0) {
1284                         putchr('?');
1285                         puts(file);
1286                 }
1287                 else
1288                         qflg = 0;
1289         }
1290         else
1291         {
1292                 putchr('?');
1293                 putchr('\n');
1294         }
1295         count = 0;
1296         lseek(0, (long)0, 2);
1297         if (globp)
1298                 lastc = '\n';
1299         globp = 0;
1300         peekc = lastc;
1301         if (lastc)
1302                 while ((c = getchr()) != '\n' && c != EOF)
1303                         ;
1304         if (io) {
1305                 eclose(io);
1306                 io = -1;
1307         }
1308         xcode = code;
1309         if (hflag)
1310                 PUTM();
1311         if (code == 4)
1312                 return (0);     /* Non-fatal error. */
1313         longjmp(savej, 1);
1314         /* NOTREACHED */
1315 }
1316 
1317 static int
1318 getchr(void)
1319 {
1320         char c;
1321         if (lastc = peekc) {
1322                 peekc = 0;
1323                 return (lastc);
1324         }
1325         if (globp) {
1326                 if ((lastc = (unsigned char)*globp++) != 0)
1327                         return (lastc);
1328                 globp = 0;
1329                 return (EOF);
1330         }
1331         if (read(0, &c, 1) <= 0)
1332                 return (lastc = EOF);
1333         lastc = (unsigned char)c;
1334         return (lastc);
1335 }
1336 
1337 static int
1338 gettty(void)
1339 {
1340         int c;
1341         char *gf;
1342         char *p;
1343 
1344         p = linebuf;
1345         gf = globp;
1346         while ((c = getchr()) != '\n') {
1347                 if (c == EOF) {
1348                         if (gf)
1349                                 peekc = c;
1350                         return (c);
1351                 }
1352                 if (c == 0)
1353                         continue;
1354                 *p++ = c;
1355 
1356                 if (p > &linebuf[LBSIZE-1])
1357                         (void) error(27);
1358         }
1359         *p++ = 0;
1360         if (linebuf[0] == '.' && linebuf[1] == 0)
1361                 return (EOF);
1362 
1363         /*
1364          * POSIX.2/XPG4 explicitly says no to this:
1365          *
1366          * in Solaris backslash followed by special character "." is
1367          * special character "." itself; (so terminating input mode can be
1368          * "\.\n").
1369          *
1370          * however, POSIX2/XPG4 says, input mode is terminated by
1371          * entering line consisting of only 2 characters: ".\n"
1372          *
1373          * if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) {
1374          *      linebuf[0] = '.';
1375          *      linebuf[1] = 0;
1376          * }
1377          */
1378         return (0);
1379 }
1380 
1381 static int
1382 getfile(void)
1383 {
1384         char c;
1385         char *lp, *fp;
1386 
1387         lp = linebuf;
1388         fp = nextip;
1389         do {
1390                 if (--ninbuf < 0) {
1391                         if ((ninbuf = read(io, genbuf, LBSIZE)-1) < 0)
1392                                 if (lp > linebuf) {
1393                                         puts(gettext("'\\n' appended"));
1394                                         *genbuf = '\n';
1395                                 }
1396                                 else
1397                                         return (EOF);
1398                         if (crflag == -1) {
1399                                 if (isencrypt(genbuf, ninbuf + 1))
1400                                         crflag = 2;
1401                                 else
1402                                         crflag = -2;
1403                         }
1404                         fp = genbuf;
1405                         if (crflag > 0)
1406                         if (run_crypt(count, genbuf, ninbuf+1, perm) == -1)
1407                                 (void) error(63);
1408                 }
1409                 if (lp >= &linebuf[LBSIZE]) {
1410                         lastc = '\n';
1411                         (void) error(27);
1412                 }
1413                 if ((*lp++ = c = *fp++) == 0) {
1414 #ifdef NULLS
1415                         lp[-1] = '\\';
1416                         *lp++ = '0';
1417                         nulls++;
1418 #else
1419                         lp--;
1420                         continue;
1421 #endif
1422                 }
1423                 count++;
1424         } while (c != '\n');
1425         *--lp = 0;
1426         nextip = fp;
1427         if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1428                 write(1, gettext("line too long: lno = "),
1429                     strlen(gettext("line too long: lno = ")));
1430                 ccount = count;
1431                 count = (++dot-zero)&077777;
1432                 dot--;
1433                 putd();
1434                 count = ccount;
1435                 putchr('\n');
1436         }
1437         return (0);
1438 }
1439 
1440 static void
1441 putfile(void)
1442 {
1443         int n;
1444         LINE a1;
1445         char *fp, *lp;
1446         int nib;
1447 
1448         nib = LBSIZE;
1449         fp = genbuf;
1450         a1 = addr1;
1451         do {
1452                 lp = getaline(a1++->cur);
1453                 if (fss.Ffill && fss.Flim && lenchk(linebuf, &fss) < 0) {
1454                         write(1, gettext("line too long: lno = "),
1455                             strlen(gettext("line too long: lno = ")));
1456                         ccount = count;
1457                         count = (a1-zero-1)&077777;
1458                         putd();
1459                         count = ccount;
1460                         putchr('\n');
1461                 }
1462                 for (;;) {
1463                         if (--nib < 0) {
1464                                 n = fp-genbuf;
1465                                 if (kflag)
1466                                 if (run_crypt(count-n, genbuf, n, perm) == -1)
1467                                         (void) error(63);
1468                                 if (write(io, genbuf, n) != n)
1469                                         (void) error(29);
1470                                 nib = LBSIZE - 1;
1471                                 fp = genbuf;
1472                         }
1473                         if (dol->cur == 0L)break; /* Allow write of null file */
1474                         count++;
1475                         if ((*fp++ = *lp++) == 0) {
1476                                 fp[-1] = '\n';
1477                                 break;
1478                         }
1479                 }
1480         } while (a1 <= addr2);
1481         n = fp-genbuf;
1482         if (kflag)
1483                 if (run_crypt(count-n, genbuf, n, perm) == -1)
1484                         (void) error(63);
1485         if (write(io, genbuf, n) != n)
1486                 (void) error(29);
1487 }
1488 
1489 static void
1490 append(int (*f)(void), LINE a)
1491 {
1492         LINE a1, a2, rdot;
1493         long tl;
1494 
1495         nline = 0;
1496         dot = a;
1497         while ((*f)() == 0) {
1498                 if (dol >= endcore) {
1499                         if ((int)sbrk(512 * sizeof (struct lin)) == -1) {
1500                                 lastc = '\n';
1501                                 (void) error(30);
1502                         }
1503                         endcore += 512;
1504                 }
1505                 tl = putline();
1506                 nline++;
1507                 a1 = ++dol;
1508                 a2 = a1+1;
1509                 rdot = ++dot;
1510                 while (a1 > rdot)
1511                         (--a2)->cur = (--a1)->cur;
1512                 rdot->cur = tl;
1513         }
1514 }
1515 
1516 static void
1517 unixcom(void)
1518 {
1519         void (*savint)();
1520         pid_t   pid, rpid;
1521         int retcode;
1522         static char savcmd[LBSIZE];     /* last command */
1523         char curcmd[LBSIZE];            /* current command */
1524         char *psavcmd, *pcurcmd, *psavedfile;
1525         int endflg = 1, shflg = 0;
1526         wchar_t c;
1527         int     len;
1528 
1529         setnoaddr();
1530         if (rflg)
1531                 (void) error(6);
1532         pcurcmd = curcmd;
1533         /* read command til end */
1534 
1535         /*
1536          * a '!' found in beginning of command is replaced with the saved
1537          * command.  a '%' found in command is replaced with the current
1538          * filename
1539          */
1540 
1541         c = getchr();
1542         if (c == '!') {
1543                 if (savcmd[0] == 0)
1544                         (void) error(56);
1545                 else {
1546                         psavcmd = savcmd;
1547                         while (*pcurcmd++ = *psavcmd++)
1548                                 ;
1549                         --pcurcmd;
1550                         shflg = 1;
1551                 }
1552         } else
1553                 UNGETC(c);  /* put c back */
1554         while (endflg == 1) {
1555                 while ((c = get_wchr()) != '\n' && c != '%' && c != '\\') {
1556                         if ((len = wctomb(pcurcmd, c)) <= 0) {
1557                                 *pcurcmd = (unsigned char)c;
1558                                 len = 1;
1559                         }
1560                         pcurcmd += len;
1561                 }
1562 
1563                 if (c == '%') {
1564                         if (savedfile[0] == 0)
1565                                 (void) error(21);
1566                         else {
1567                                 psavedfile = savedfile;
1568                                 while (pcurcmd < curcmd + LBSIZE &&
1569                                     (*pcurcmd++ = *psavedfile++))
1570                                         ;
1571                                 --pcurcmd;
1572                                 shflg = 1;
1573                         }
1574                 } else if (c == '\\') {
1575                         c = get_wchr();
1576                         if (c != '%')
1577                                 *pcurcmd++ = '\\';
1578                         if ((len = wctomb(pcurcmd, c)) <= 0) {
1579                                 *pcurcmd = (unsigned char)c;
1580                                 len = 1;
1581                         }
1582                         pcurcmd += len;
1583                 }
1584                 else
1585                         /* end of command hit */
1586                         endflg = 0;
1587         }
1588         *pcurcmd++ = 0;
1589         if (shflg == 1)
1590                 puts(curcmd);
1591         /* save command */
1592         strcpy(savcmd, curcmd);
1593 
1594         if ((pid = fork()) == 0) {
1595                 signal(SIGHUP, oldhup);
1596                 signal(SIGQUIT, oldquit);
1597                 close(tfile);
1598                 execlp(_PATH_BSHELL, "sh", "-c", curcmd, NULL);
1599                 exit(0100);
1600         }
1601         savint = signal(SIGINT, SIG_IGN);
1602         while ((rpid = wait(&retcode)) != pid && rpid != (pid_t)-1)
1603                 ;
1604         signal(SIGINT, savint);
1605         if (vflag) puts("!");
1606 }
1607 
1608 static void
1609 quit(int sig)
1610 {
1611         if (vflag && fchange) {
1612                 fchange = 0;
1613                 if (flag28) {
1614                         flag28 = 0;
1615                         (void) error(62);
1616                 }
1617 
1618                 /*
1619                  * For case where user reads in BOTH a good
1620                  * file & a bad file
1621                  */
1622                 (void) error(1);
1623         }
1624         unlink(tfname);
1625         if (kflag)
1626                 crypt_close(perm);
1627         if (xtflag)
1628                 crypt_close(tperm);
1629         exit(errcnt? 2: 0);
1630 }
1631 
1632 static void
1633 delete(void)
1634 {
1635         setdot();
1636         newline();
1637         nonzero();
1638         if (!globflg)
1639                 save();
1640         rdelete(addr1, addr2);
1641 }
1642 
1643 static void
1644 rdelete(LINE ad1, LINE ad2)
1645 {
1646         LINE a1, a2, a3;
1647 
1648         a1 = ad1;
1649         a2 = ad2+1;
1650         a3 = dol;
1651         dol -= a2 - a1;
1652         do {
1653                 (a1++)->cur = (a2++)->cur;
1654         } while (a2 <= a3);
1655         a1 = ad1;
1656         if (a1 > dol)
1657                 a1 = dol;
1658         dot = a1;
1659         fchange = 1;
1660 }
1661 
1662 static void
1663 gdelete(void)
1664 {
1665         LINE a1, a2, a3;
1666 
1667         a3 = dol;
1668         for (a1 = zero+1; (a1->cur&01) == 0; a1++)
1669                 if (a1 >= a3)
1670                         return;
1671         for (a2 = a1 + 1; a2 <= a3; ) {
1672                 if (a2->cur & 01) {
1673                         a2++;
1674                         dot = a1;
1675                 } else
1676                         (a1++)->cur = (a2++)->cur;
1677         }
1678         dol = a1-1;
1679         if (dot > dol)
1680                 dot = dol;
1681         fchange = 1;
1682 }
1683 
1684 static char *
1685 getaline(long tl)
1686 {
1687         char *bp, *lp;
1688         int nl;
1689 
1690         lp = linebuf;
1691         bp = getblock(tl, READ);
1692         nl = nleft;
1693         tl &= ~0377;
1694         while (*lp++ = *bp++)
1695                 if (--nl == 0) {
1696                         bp = getblock(tl += 0400, READ);
1697                         nl = nleft;
1698                 }
1699         return (linebuf);
1700 }
1701 
1702 static long
1703 putline(void)
1704 {
1705         char *bp, *lp;
1706         int nl;
1707         long tl;
1708 
1709         fchange = 1;
1710         lp = linebuf;
1711         tl = tline;
1712         bp = getblock(tl, WRITE);
1713         nl = nleft;
1714         tl &= ~0377;
1715         while (*bp = *lp++) {
1716                 if (*bp++ == '\n') {
1717                         *--bp = 0;
1718                         linebp = lp;
1719                         break;
1720                 }
1721                 if (--nl == 0) {
1722                         bp = getblock(tl += 0400, WRITE);
1723                         nl = nleft;
1724                 }
1725         }
1726         nl = tline;
1727         tline += (((lp-linebuf)+03)>>1)&077776;
1728         return (nl);
1729 }
1730 
1731 static char *
1732 getblock(long atl, long iof)
1733 {
1734         int bno, off;
1735         char *p1, *p2;
1736         int n;
1737 
1738         bno = atl >> 8;
1739         off = (atl<<1)&0774;
1740 
1741         /* bno is limited to 16 bits */
1742         if (bno >= 65535) {
1743                 lastc = '\n';
1744                 (void) error(31);
1745         }
1746         nleft = 512 - off;
1747         if (bno == iblock) {
1748                 ichanged |= iof;
1749                 return (ibuff+off);
1750         }
1751         if (bno == oblock)
1752                 return (obuff+off);
1753         if (iof == READ) {
1754                 if (ichanged) {
1755                         if (xtflag)
1756                                 if (run_crypt(0L, ibuff, 512, tperm) == -1)
1757                                         (void) error(63);
1758                         blkio(iblock, ibuff, write);
1759                 }
1760                 ichanged = 0;
1761                 iblock = bno;
1762                 blkio(bno, ibuff, read);
1763                 if (xtflag)
1764                         if (run_crypt(0L, ibuff, 512, tperm) == -1)
1765                                 (void) error(63);
1766                 return (ibuff+off);
1767         }
1768         if (oblock >= 0) {
1769                 if (xtflag) {
1770                         p1 = obuff;
1771                         p2 = crbuf;
1772                         n = 512;
1773                         while (n--)
1774                                 *p2++ = *p1++;
1775                         if (run_crypt(0L, crbuf, 512, tperm) == -1)
1776                                 (void) error(63);
1777                         blkio(oblock, crbuf, write);
1778                 } else
1779                         blkio(oblock, obuff, write);
1780         }
1781         oblock = bno;
1782         return (obuff+off);
1783 }
1784 
1785 static void
1786 blkio(int b, char *buf, ssize_t (*iofcn)())
1787 {
1788         lseek(tfile, (long)b<<9, 0);
1789         if ((*iofcn)(tfile, buf, 512) != 512) {
1790                 if (dol != zero)
1791                         (void) error(32); /* Bypass this if writing null file */
1792         }
1793 }
1794 
1795 static void
1796 init(void)
1797 {
1798         long *markp;
1799         mode_t omask;
1800 
1801         if (tfile != -1) {
1802                 (void) close(tfile);
1803                 (void) unlink(tfname);
1804         }
1805 
1806         tline = 2;
1807         for (markp = names; markp < &names[26]; )
1808                 *markp++ = 0L;
1809         subnewa = 0L;
1810         anymarks = 0;
1811         iblock = -1;
1812         oblock = -1;
1813         ichanged = 0;
1814         initflg = 1;
1815         omask = umask(0);
1816 
1817         if ((tfile = open(tfname, O_CREAT|O_EXCL|O_RDWR,
1818             S_IRUSR|S_IWUSR)) < 0) {
1819                 puts(gettext(msgtab[70]));
1820                 exit(2);
1821         }
1822 
1823         umask(omask);
1824         if (xflag) {
1825                 xtflag = 1;
1826                 if (tpermflag)
1827                         (void) crypt_close(tperm);
1828                 tpermflag = 1;
1829                 if (makekey(tperm)) {
1830                         xtflag = 0;
1831                         puts(gettext(msgtab[65]));
1832                 }
1833         }
1834         brk((char *)fendcore);
1835         dot = zero = dol = savdot = savdol = fendcore;
1836         flag28 = save28 = 0;
1837         endcore = fendcore - sizeof (struct lin);
1838 }
1839 
1840 static void
1841 global(int k)
1842 {
1843         char *gp;
1844         wchar_t l;
1845         char multic[MB_LEN_MAX];
1846         wchar_t c;
1847         LINE a1;
1848         char globuf[LBSIZE];
1849         int n;
1850         int     len;
1851 
1852         if (globp)
1853                 (void) error(33);
1854         setall();
1855         nonzero();
1856         if ((n = _mbftowc(multic, &l, getchr, &peekc)) <= 0)
1857                 (void) error(67);
1858         if (l == '\n')
1859                 (void) error(19);
1860         save();
1861         comple(l);
1862         gp = globuf;
1863         while ((c = get_wchr()) != '\n') {
1864                 if (c == EOF)
1865                         (void) error(19);
1866 
1867                 /* '\\' has special meaning only if preceding a '\n' */
1868                 if (c == '\\') {
1869                         c = get_wchr();
1870                         if (c != '\n')
1871                                 *gp++ = '\\';
1872                 }
1873                 if ((gp + (unsigned int)MB_CUR_MAX) >= &globuf[LBSIZE-1])
1874                         (void) error(34);
1875                 if ((len = wctomb(gp, c)) <= 0) {
1876                         *gp = (unsigned char)c;
1877                         len = 1;
1878                 }
1879                 gp += len;
1880         }
1881         if (gp == globuf)
1882                 *gp++ = 'p';
1883         *gp++ = '\n';
1884         *gp++ = 0;
1885         for (a1 = zero; a1 <= dol; a1++) {
1886                 a1->cur &= ~01;
1887                 if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
1888                         a1->cur |= 01;
1889         }
1890         /*
1891          * Special case: g/.../d (avoid n^2 algorithm)
1892          */
1893         if (globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == '\0') {
1894                 gdelete();
1895                 return;
1896         }
1897         for (a1 = zero; a1 <= dol; a1++) {
1898                 if (a1->cur & 01) {
1899                         a1->cur &= ~01;
1900                         dot = a1;
1901                         globp = globuf;
1902                         globflg = 1;
1903                         commands();
1904                         globflg = 0;
1905                         a1 = zero;
1906                 }
1907         }
1908 }
1909 
1910 static void
1911 join(void)
1912 {
1913         char *gp, *lp;
1914         LINE a1;
1915 
1916         if (addr1 == addr2)
1917                 return;
1918         gp = genbuf;
1919         for (a1 = addr1; a1 <= addr2; a1++) {
1920                 lp = getaline(a1->cur);
1921                 while (*gp = *lp++)
1922                         if (gp++ > &genbuf[LBSIZE-1])
1923                                 (void) error(27);
1924         }
1925         lp = linebuf;
1926         gp = genbuf;
1927         while (*lp++ = *gp++)
1928                 ;
1929         addr1->cur = putline();
1930         if (addr1 < addr2)
1931                 rdelete(addr1+1, addr2);
1932         dot = addr1;
1933 }
1934 
1935 static void
1936 substitute(int inglob)
1937 {
1938         int nl;
1939         LINE a1;
1940         long *markp;
1941         int ingsav;             /* For saving arg. */
1942 
1943         ingsav = inglob;
1944         ocerr2 = 0;
1945         gsubf = compsub();
1946         for (a1 = addr1; a1 <= addr2; a1++) {
1947                 if (execute(0, a1) == 0)
1948                         continue;
1949                 numpass = 0;
1950                 ocerr1 = 0;
1951                 inglob |= 01;
1952                 dosub();
1953                 if (gsubf) {
1954                         while (*loc2) {
1955                                 if (execute(1, (LINE)0) == 0)
1956                                         break;
1957                                 dosub();
1958                         }
1959                 }
1960                 if (ocerr1 == 0)continue;       /* Don't put out-not changed. */
1961                 subnewa = putline();
1962                 a1->cur &= ~01;
1963                 if (anymarks) {
1964                         for (markp = names; markp < &names[26]; markp++)
1965                                 if (*markp == a1->cur)
1966                                         *markp = subnewa;
1967                 }
1968                 a1->cur = subnewa;
1969                 append(getsub, a1);
1970                 nl = nline;
1971                 a1 += nl;
1972                 addr2 += nl;
1973         }
1974         if (ingsav)
1975                 return; /* Was in global-no error msg allowed. */
1976         if (inglob == 0)
1977                 (void) error(35);       /* Not in global, but not found. */
1978         if (ocerr2 == 0)
1979                 (void) error(35); /* RE found, but occurrence match failed. */
1980 }
1981 
1982 static int
1983 compsub(void)
1984 {
1985         int c;
1986         wchar_t seof;
1987         char *p;
1988         char multic[MB_LEN_MAX];
1989         int n;
1990         static char remem[RHSIZE];
1991         static int remflg = -1;
1992         int i;
1993 
1994         if ((n = _mbftowc(multic, &seof, getchr, &peekc)) <= 0)
1995                 (void) error(67);
1996         if (seof == '\n' || seof == ' ')
1997                 (void) error(36);
1998         comple(seof);
1999         p = rhsbuf;
2000         for (;;) {
2001                 wchar_t cl;
2002                 if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2003                         (void) error(67);
2004                 if (cl == '\\') {
2005                         *p++ = '\\';
2006                         if (p >= &rhsbuf[RHSIZE])
2007                                 (void) error(38);
2008                         if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2009                                 (void) error(67);
2010                 } else if (cl == '\n') {
2011                         if (nodelim == 1) {
2012                                 nodelim = 0;
2013                                 (void) error(36);
2014                         }
2015                         if (!(globp && globp[0])) {
2016                                 UNGETC('\n');
2017                                 pflag++;
2018                                 break;
2019                         }
2020                 } else if (cl == seof)
2021                         break;
2022                 if (p + n > &rhsbuf[RHSIZE])
2023                         (void) error(38);
2024                 (void) strncpy(p, multic, n);
2025                 p += n;
2026         }
2027         *p++ = 0;
2028         if (rhsbuf[0] == '%' && rhsbuf[1] == 0)
2029                 /*
2030                  * If there isn't a remembered string, it is an error;
2031                  * otherwise the right hand side is the previous right
2032                  * hand side.
2033                  */
2034 
2035                 if (remflg == -1)
2036                         (void) error(55);
2037                 else
2038                         strcpy(rhsbuf, remem);
2039         else {
2040                 strcpy(remem, rhsbuf);
2041                 remflg = 0;
2042         }
2043         c = 0;
2044         peekc = getchr();       /* Gets char after third delimiter. */
2045         if (peekc == 'g') {
2046                 c = LBSIZE; peekc = 0;
2047         }
2048         if (peekc >= '1' && peekc <= '9') {
2049                 c = peekc-'0';
2050                 peekc = 0;      /* Allows getchr() to get next char. */
2051                 while (1) {
2052                         i = getchr();
2053                         if (i < '0' || i > '9')
2054                                 break;
2055                         c = c*10 + i-'0';
2056                         if (c > LBSIZE-1)
2057                                 (void) error(20);       /* "Illegal suffix" */
2058                         }
2059                 peekc = i;      /* Effectively an unget. */
2060                 }
2061         newline();
2062         return (c);
2063 
2064         /*
2065          * Returns occurrence value. 0 & 1 both do first occurrence
2066          * only: c = 0 if ordinary substitute; c = 1
2067          * if use 1 in global sub(s/a/b/1). 0 in global form is illegal.
2068          */
2069 }
2070 
2071 static int
2072 getsub(void)
2073 {
2074         char *p1, *p2;
2075 
2076         p1 = linebuf;
2077         if ((p2 = linebp) == 0)
2078                 return (EOF);
2079         while (*p1++ = *p2++)
2080                 ;
2081         linebp = 0;
2082         return (0);
2083 }
2084 
2085 static void
2086 dosub(void)
2087 {
2088         char *lp, *sp, *rp;
2089         int c;
2090 
2091         if (gsubf > 0 && gsubf < LBSIZE) {
2092                 numpass++;
2093                 if (gsubf != numpass)
2094                         return;
2095         }
2096         ocerr1++;
2097         ocerr2++;
2098         lp = linebuf;
2099         sp = genbuf;
2100         rp = rhsbuf;
2101         while (lp < loc1)
2102                 *sp++ = *lp++;
2103         while (c = *rp++) {
2104                 if (c == '&') {
2105                         sp = place(sp, loc1, loc2);
2106                         continue;
2107                 } else if (c == '\\') {
2108                         c = *rp++;
2109                         if (c >= '1' && c < nbra + '1') {
2110                                 sp = place(sp, braslist[c-'1'],
2111                                     braelist[c-'1']);
2112                                 continue;
2113                         }
2114                 }
2115                 *sp++ = c;
2116                 if (sp >= &genbuf[LBSIZE])
2117                         (void) error(27);
2118         }
2119         lp = loc2;
2120         loc2 = sp - genbuf + linebuf;
2121         while (*sp++ = *lp++)
2122                 if (sp >= &genbuf[LBSIZE])
2123                         (void) error(27);
2124         lp = linebuf;
2125         sp = genbuf;
2126         while (*lp++ = *sp++)
2127                 ;
2128 }
2129 
2130 static char *
2131 place(char *sp, char *l1, char *l2)
2132 {
2133 
2134         while (l1 < l2) {
2135                 *sp++ = *l1++;
2136                 if (sp >= &genbuf[LBSIZE])
2137                         (void) error(27);
2138         }
2139         return (sp);
2140 }
2141 
2142 static void
2143 comple(wchar_t seof)
2144 {
2145         int cclass = 0;
2146         wchar_t c;
2147         int n;
2148         char *cp = genbuf;
2149         char multic[MB_LEN_MAX];
2150 
2151         while (1) {
2152                 if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2153                         error1(67);
2154                 if (n == 0 || c == '\n') {
2155                         if (cclass)
2156                                 error1(49);
2157                         else
2158                                 break;
2159                 }
2160                 if (c == seof && !cclass)
2161                         break;
2162                 if (cclass && c == ']') {
2163                         cclass = 0;
2164                         if (cp > &genbuf[LBSIZE-1])
2165                                 error1(50);
2166                         *cp++ = ']';
2167                         continue;
2168                 }
2169                 if (c == '[' && !cclass) {
2170                         cclass = 1;
2171                         if (cp > &genbuf[LBSIZE-1])
2172                                 error1(50);
2173                         *cp++ = '[';
2174                         if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2175                                 error1(67);
2176                         if (n == 0 || c == '\n')
2177                                 error1(49);
2178                 }
2179                 if (c == '\\' && !cclass) {
2180                         if (cp > &genbuf[LBSIZE-1])
2181                                 error1(50);
2182                         *cp++ = '\\';
2183                         if ((n = _mbftowc(multic, &c, getchr, &peekc)) < 0)
2184                                 error1(67);
2185                         if (n == 0 || c == '\n')
2186                                 error1(36);
2187                 }
2188                 if (cp + n > &genbuf[LBSIZE-1])
2189                         error1(50);
2190                 (void) strncpy(cp, multic, n);
2191                 cp += n;
2192         }
2193         *cp = '\0';
2194         if (n != 0 && c == '\n')
2195                 UNGETC('\n');
2196         if (n == 0 || c == '\n')
2197                 nodelim = 1;
2198 
2199         /*
2200          * NULL RE: do not compile a null regular expression; but process
2201          * input with last regular expression encountered
2202          */
2203 
2204         if (genbuf[0] != '\0') {
2205                 if (expbuf)
2206                         free(expbuf);
2207                 expbuf = compile(genbuf, (char *)0, (char *)0);
2208         }
2209         if (regerrno)
2210                 error1(regerrno);
2211 }
2212 
2213 static void
2214 move(int cflag)
2215 {
2216         LINE adt, ad1, ad2;
2217 
2218         setdot();
2219         nonzero();
2220         if ((adt = address()) == 0)
2221                 (void) error(39);
2222         newline();
2223         if (!globflg) save();
2224         if (cflag) {
2225                 ad1 = dol;
2226                 append(getcopy, ad1++);
2227                 ad2 = dol;
2228         } else {
2229                 ad2 = addr2;
2230                 for (ad1 = addr1; ad1 <= ad2; )
2231                         (ad1++)->cur &= ~01;
2232                 ad1 = addr1;
2233         }
2234         ad2++;
2235         if (adt < ad1) {
2236                 dot = adt + (ad2-ad1);
2237                 if ((++adt) == ad1)
2238                         return;
2239                 reverse(adt, ad1);
2240                 reverse(ad1, ad2);
2241                 reverse(adt, ad2);
2242         } else if (adt >= ad2) {
2243                 dot = adt++;
2244                 reverse(ad1, ad2);
2245                 reverse(ad2, adt);
2246                 reverse(ad1, adt);
2247         } else
2248                 (void) error(39);
2249         fchange = 1;
2250 }
2251 
2252 static void
2253 reverse(LINE a1, LINE a2)
2254 {
2255         long t;
2256 
2257         for (;;) {
2258                 t = (--a2)->cur;
2259                 if (a2 <= a1)
2260                         return;
2261                 a2->cur = a1->cur;
2262                 (a1++)->cur = t;
2263         }
2264 }
2265 
2266 static int
2267 getcopy(void)
2268 {
2269 
2270         if (addr1 > addr2)
2271                 return (EOF);
2272         (void) getaline((addr1++)->cur);
2273         return (0);
2274 }
2275 
2276 
2277 /*
2278  * Handles error code returned from comple() routine: regular expression
2279  * compile and match routines
2280  */
2281 
2282 static void
2283 error1(int code)
2284 {
2285         nbra = 0;
2286         (void) error(code);
2287 }
2288 
2289 
2290 static int
2291 execute(int gf, LINE addr)
2292 {
2293         char *p1;
2294         int c;
2295 
2296         for (c = 0; c < nbra; c++) {
2297                 braslist[c] = 0;
2298                 braelist[c] = 0;
2299         }
2300         if (gf)
2301                 locs = p1 = loc2;
2302         else {
2303                 if (addr == zero)
2304                         return (0);
2305                 p1 = getaline(addr->cur);
2306                 locs = 0;
2307         }
2308         return (step(p1, expbuf));
2309 }
2310 
2311 
2312 static void
2313 putd()
2314 {
2315         int r;
2316 
2317         r = (int)(count%10);
2318         count /= 10;
2319         if (count)
2320                 putd();
2321         putchr(r + '0');
2322 }
2323 
2324 
2325 int
2326 puts(const char *sp)
2327 {
2328         int n;
2329         wchar_t c;
2330         int sz, i;
2331         if (fss.Ffill && (listf == 0)) {
2332 
2333                 /* deliberate attempt to remove constness of sp because */
2334                 /* it needs to be expanded */
2335 
2336                 if ((i = expnd((char *)sp, funny, &sz, &fss)) == -1) {
2337                         write(1, funny, fss.Flim & 0377);
2338                         putchr('\n');
2339                         write(1, gettext("too long"),
2340                             strlen(gettext("too long")));
2341                 }
2342                 else
2343                         write(1, funny, sz);
2344                 putchr('\n');
2345                 if (i == -2)
2346                         write(1, gettext("tab count\n"),
2347                             strlen(gettext("tab count\n")));
2348                 return (0);
2349         }
2350         col = 0;
2351         while (*sp) {
2352                 n = mbtowc(&c, sp, MB_LEN_MAX);
2353                 if (listf) {
2354                         if (n < 1)
2355                                 (void) error(28);
2356                         else if (n == 1)
2357                                 putchr((unsigned char)*sp++);
2358                         else {
2359                                 sp += n;
2360                                 putwchr(c);
2361                         }
2362                 } else {
2363                         putchr((unsigned char)*sp++);
2364                 }
2365         }
2366 #ifndef XPG6
2367         if (listf)
2368                 putchr('$');    /* end of line is marked with a $ */
2369 #else
2370         if (listf) {
2371         /* xpg6 - ensure that the end of line $ is not preceeded with a "\" */
2372         /* by doing a putchr() with listf=0, thereby avoiding the $ case */
2373         /* statement  in putchr() */
2374                 listf = 0;
2375                 putchr('$');    /* end of line is marked with a $ */
2376                 listf++;
2377         }
2378 #endif
2379         putchr('\n');
2380         return (1);
2381 }
2382 
2383 
2384 static void
2385 putwchr(wchar_t ac)
2386 {
2387         char buf[MB_LEN_MAX], *p;
2388         char *lp;
2389         wchar_t c;
2390         short len;
2391 
2392         lp = linp;
2393         c = ac;
2394         if (listf) {
2395                 if (!iswprint(c)) {
2396                         p = &buf[0];
2397                         if ((len = wctomb(p, c)) <= 0) {
2398                                 *p = (unsigned char)c;
2399                                 len = 1;
2400                         };
2401                         while (len--) {
2402                                 if (col + 4 >= 72) {
2403                                         col = 0;
2404                                         *lp++ = '\\';
2405                                         *lp++ = '\n';
2406                                 }
2407                                 (void) sprintf(lp, "\\%03o",
2408                                     *(unsigned char *)p++);
2409                                 col += 4;
2410                                 lp += 4;
2411                         }
2412                 } else {
2413                         if ((len = wcwidth(c)) <= 0)
2414                                 len = 0;
2415                         if (col + len >= 72) {
2416                                 col = 0;
2417                                 *lp++ = '\\';
2418                                 *lp++ = '\n';
2419                         }
2420                         col += len;
2421                         if ((len = wctomb(lp, c)) <= 0) {
2422                                 *lp = (unsigned char)c;
2423                                 len = 1;
2424                         }
2425                         lp += len;
2426                 }
2427         } else {
2428                 if ((len = wctomb(lp, c)) <= 0) {
2429                         *lp = (unsigned char)c;
2430                         len = 1;
2431                 }
2432                 lp += len;
2433         }
2434         if (c == '\n' || lp >= &line[64]) {
2435                 linp = line;
2436                 len = lp - line;
2437                 write(1, line, len);
2438                 return;
2439         }
2440         linp = lp;
2441 }
2442 
2443 
2444 static void
2445 putchr(unsigned char c)
2446 {
2447         char *lp;
2448         int len;
2449 
2450         lp = linp;
2451         if (listf && c != '\n') {
2452                 switch (c) {
2453                         case '\\' :
2454                                 *lp++ = '\\';
2455                                 *lp++ = '\\';
2456                                 col += 2;
2457                                 break;
2458                         case '\007' :
2459                                 *lp++ = '\\';
2460                                 *lp++ = 'a';
2461                                 col += 2;
2462                                 break;
2463                         case '\b' :
2464                                 *lp++ = '\\';
2465                                 *lp++ = 'b';
2466                                 col += 2;
2467                                 break;
2468                         case '\f' :
2469                                 *lp++ = '\\';
2470                                 *lp++ = 'f';
2471                                 col += 2;
2472                                 break;
2473                         case '\r' :
2474                                 *lp++ = '\\';
2475                                 *lp++ = 'r';
2476                                 col += 2;
2477                                 break;
2478                         case '\t' :
2479                                 *lp++ = '\\';
2480                                 *lp++ = 't';
2481                                 col += 2;
2482                                 break;
2483                         case '\v' :
2484                                 *lp++ = '\\';
2485                                 *lp++ = 'v';
2486                                 col += 2;
2487                                 break;
2488 #ifdef XPG6
2489                 /* if $ characters are within the line preceed with \ */
2490                         case '$' :
2491                                 *lp++ = '\\';
2492                                 *lp++ = '$';
2493                                 col += 2;
2494                                 break;
2495 #endif
2496                         default:
2497                                 if (isprint(c)) {
2498                                         *lp++ = c;
2499                                         col += 1;
2500                                 } else {
2501                                         (void) sprintf(lp, "\\%03o", c);
2502                                         col += 4;
2503                                         lp += 4;
2504                                 }
2505                                 break;
2506                 }
2507 
2508         /*
2509          * long lines are folded w/ pt of folding indicated by writing
2510          * backslash/newline character
2511          */
2512 
2513                 if (col + 1 >= 72) {
2514                         col = 0;
2515                         *lp++ = '\\';
2516                         *lp++ = '\n';
2517                 }
2518         } else
2519                 *lp++ = c;
2520         if (c == '\n' || lp >= &line[64]) {
2521                 linp = line;
2522                 len = lp - line;
2523                 (void) write(1, line, len);
2524                 return;
2525         }
2526         linp = lp;
2527 }
2528 
2529 
2530 static char *
2531 getkey(const char *prompt)
2532 {
2533         struct termio b;
2534         int save;
2535         void (*sig)();
2536         static char key[KSIZE+1];
2537         char *p;
2538         int c;
2539 
2540         sig = signal(SIGINT, SIG_IGN);
2541         ioctl(0, TCGETA, &b);
2542         save = b.c_lflag;
2543         b.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
2544         ioctl(0, TCSETAW, &b);
2545         write(1, gettext(prompt), strlen(gettext(prompt)));
2546         p = key;
2547         while (((c = getchr()) != EOF) && (c != '\n')) {
2548                 if (p < &key[KSIZE])
2549                         *p++ = c;
2550         }
2551         *p = 0;
2552         write(1, "\n", 1);
2553         b.c_lflag = save;
2554         ioctl(0, TCSETAW, &b);
2555         signal(SIGINT, sig);
2556         return (key);
2557 }
2558 
2559 
2560 static void
2561 globaln(int k)
2562 {
2563         char *gp;
2564         int c;
2565         int n;
2566         wchar_t cl;
2567         LINE a1;
2568         int  nfirst;
2569         char globuf[LBSIZE];
2570         char multic[MB_LEN_MAX];
2571         int     len;
2572         int pflag_save = 0;
2573         int listf_save = 0;
2574         int listn_save = 0;
2575 
2576         if (globp)
2577                 (void) error(33);
2578         setall();
2579         nonzero();
2580         if ((n = _mbftowc(multic, &cl, getchr, &peekc)) <= 0)
2581                 (void) error(67);
2582         if (cl == '\n')
2583                 (void) error(19);
2584         save();
2585         comple(cl);
2586         for (a1 = zero; a1 <= dol; a1++) {
2587                 a1->cur &= ~01;
2588                 if (a1 >= addr1 && a1 <= addr2 && execute(0, a1) == k)
2589                         a1->cur |= 01;
2590         }
2591         nfirst = 0;
2592         newline();
2593         /*
2594          * preserve the p, l, and n suffix commands of the G and V
2595          * commands during the interactive section and restore
2596          * on completion of the G and V command.
2597          */
2598         pflag_save = pflag;
2599         listf_save = listf;
2600         listn_save = listn;
2601         pflag = 0;
2602         listf = 0;
2603         listn = 0;
2604         for (a1 = zero; a1 <= dol; a1++) {
2605                 if (a1->cur & 01) {
2606                         a1->cur &= ~01;
2607                         dot = a1;
2608                         puts(getaline(a1->cur));
2609                         if ((c = get_wchr()) == EOF)
2610                                 (void) error(52);
2611                         if (c == 'a' || c == 'i' || c == 'c')
2612                                 (void) error(53);
2613                         if (c == '\n') {
2614                                 a1 = zero;
2615                                 continue;
2616                         }
2617                         if (c != '&') {
2618                                 gp = globuf;
2619                                 if ((len = wctomb(gp, c)) <= 0) {
2620                                         *gp = (unsigned char)c;
2621                                         len = 1;
2622                                 }
2623                                 gp += len;
2624                                 while ((c = get_wchr()) != '\n') {
2625 
2626                         /* '\\' has special meaning only if preceding a '\n' */
2627                                         if (c == '\\') {
2628                                                 c = get_wchr();
2629                                                 if (c != '\n')
2630                                                         *gp++ = '\\';
2631                                         }
2632                                         if ((gp + (unsigned int)MB_CUR_MAX) >=
2633                                             &globuf[LBSIZE-1])
2634                                                 (void) error(34);
2635 
2636                                         if ((len = wctomb(gp, c)) <= 0) {
2637                                                 *gp = (unsigned char)c;
2638                                                 len = 1;
2639                                         }
2640                                         gp += len;
2641                                 }
2642                                 *gp++ = '\n';
2643                                 *gp++ = 0;
2644                                 nfirst = 1;
2645                         } else if ((c = get_wchr()) != '\n')
2646                                 (void) error(54);
2647                         globp = globuf;
2648                         if (nfirst) {
2649                                 globflg = 1;
2650                                 commands();
2651                                 globflg = 0;
2652                         } else
2653                                 (void) error(56);
2654                         globp = 0;
2655                         a1 = zero;
2656                 }
2657         }
2658         pflag = pflag_save;
2659         listf = listf_save;
2660         listn = listn_save;
2661 }
2662 
2663 
2664 static int
2665 eopen(char *string, int rw)
2666 {
2667 #define w_or_r(a, b) (rw ? a : b)
2668         int pf[2];
2669         pid_t i;
2670         int io;
2671         int chcount;    /* # of char read. */
2672 
2673         if (rflg) {     /* restricted shell */
2674                 if (Xqt) {
2675                         Xqt = 0;
2676                         (void) error(6);
2677                 }
2678         }
2679         if (!Xqt) {
2680                 if ((io = open(string, rw)) >= 0) {
2681                         if (fflg) {
2682                                 chcount = read(io, crbuf, LBSIZE);
2683                                 if (crflag == -1) {
2684                                         if (isencrypt(crbuf, chcount))
2685                                                 crflag = 2;
2686                                         else
2687                                                 crflag = -2;
2688                                 }
2689                                 if (crflag > 0)
2690                                 if (run_crypt(0L, crbuf, chcount, perm) == -1)
2691                                                 (void) error(63);
2692                                 if (fspec(crbuf, &fss, 0) < 0) {
2693                                         fss.Ffill = 0;
2694                                         fflg = 0;
2695                                         (void) error(4);
2696                                 }
2697                                 lseek(io, 0L, 0);
2698                         }
2699                 }
2700                 fflg = 0;
2701                 return (io);
2702         }
2703         if (pipe(pf) < 0)
2704 xerr:           (void) error(0);
2705         if ((i = fork()) == 0) {
2706                 signal(SIGHUP, oldhup);
2707                 signal(SIGQUIT, oldquit);
2708                 signal(SIGPIPE, oldpipe);
2709                 signal(SIGINT, (void (*)()) 0);
2710                 close(w_or_r(pf[1], pf[0]));
2711                 close(w_or_r(0, 1));
2712                 dup(w_or_r(pf[0], pf[1]));
2713                 close(w_or_r(pf[0], pf[1]));
2714                 execlp(_PATH_BSHELL, "sh", "-c", string, (char *)0);
2715                 exit(1);
2716         }
2717         if (i == (pid_t)-1)
2718                 goto xerr;
2719         close(w_or_r(pf[0], pf[1]));
2720         return (w_or_r(pf[1], pf[0]));
2721 }
2722 
2723 
2724 static void
2725 eclose(int f)
2726 {
2727         close(f);
2728         if (Xqt)
2729                 Xqt = 0, wait((int *)0);
2730 }
2731 
2732 
2733 static void
2734 mkfunny(void)
2735 {
2736         char *p, *p1, *p2;
2737 
2738         p2 = p1 = funny;
2739         p = file;
2740         /*
2741          * Go to end of file name
2742          */
2743         while (*p)
2744                 p++;
2745         while (*--p  == '/')    /* delete trailing slashes */
2746                 *p = '\0';
2747         /*
2748          * go back to beginning of file
2749          */
2750         p = file;
2751         /*
2752          * Copy file name to funny setting p2 at
2753          * basename of file.
2754          */
2755         while (*p1++ = *p)
2756                 if (*p++ == '/')
2757                         p2 = p1;
2758         /*
2759          * Set p1 to point to basename of tfname.
2760          */
2761         p1 = strrchr(tfname, '/');
2762         if (strlen(tfname) > (size_t)6)
2763                 p1 = &tfname[strlen(tfname)-6];
2764         p1++;
2765         *p2 = '\007'; /* add unprintable char for funny  a unique name */
2766         /*
2767          * Copy tfname to file.
2768          */
2769         while (*++p2 = *p1++)
2770                 ;
2771 }
2772 
2773 
2774 static void
2775 getime(void) /* get modified time of file and save */
2776 {
2777         if (stat(file, &Fl) < 0)
2778                 savtime = 0;
2779         else
2780                 savtime = Fl.st_mtime;
2781 }
2782 
2783 
2784 static void
2785 chktime(void) /* check saved mod time against current mod time */
2786 {
2787         if (savtime != 0 && Fl.st_mtime != 0) {
2788                 if (savtime != Fl.st_mtime)
2789                         (void) error(58);
2790         }
2791 }
2792 
2793 
2794 static void
2795 newtime(void) /* get new mod time and save */
2796 {
2797         stat(file, &Fl);
2798         savtime = Fl.st_mtime;
2799 }
2800 
2801 
2802 /*
2803  * restricted - check for '/' in name and delete trailing '/'
2804  */
2805 static void
2806 red(char *op)
2807 {
2808         char *p;
2809 
2810         p = op;
2811         while (*p)
2812                 if (*p++ == '/'&& rflg) {
2813                         *op = 0;
2814                         (void) error(6);
2815                 }
2816         /* delete trailing '/' */
2817         while (p > op) {
2818                 if (*--p == '/')
2819                         *p = '\0';
2820                 else break;
2821         }
2822 }
2823 
2824 
2825 /*
2826  * Searches thru beginning of file looking for a string of the form
2827  *      <: values... :>
2828  *
2829  * where "values" are
2830  *
2831  *      \b      ignored
2832  *      s<num>  sets the Flim to <num>
2833  *      t???    sets tab stop stuff
2834  *      d       ignored
2835  *      m<num>  ignored
2836  *      e       ignored
2837  */
2838 
2839 static int
2840 fspec(char line[], struct Fspec *f, int up)
2841 {
2842         struct termio arg;
2843         int havespec, n;
2844         int     len;
2845 
2846         if (!up) clear(f);
2847 
2848         havespec = fsprtn = 0;
2849         for (fsp = line; *fsp && *fsp != '\n'; fsp += len) {
2850                 if ((len = mblen(fsp, MB_CUR_MAX)) <= 0)
2851                         len = 1;
2852                 switch (*fsp) {
2853 
2854                         case '<':    if (havespec)
2855                                                 return (-1);
2856                                         if (*(fsp+1) == ':') {
2857                                                 havespec = 1;
2858                                                 clear(f);
2859                                                 if (!ioctl(1, TCGETA, &arg) &&
2860                                                     ((arg.c_oflag & TAB3) ==
2861                                                     TAB3))
2862                                                         f->Ffill = 1;
2863                                                 fsp++;
2864                                         }
2865                                         continue;
2866 
2867                         case ' ':       continue;
2868 
2869                         case 's':       if (havespec && (n = numb()) >= 0)
2870                                                 f->Flim = n;
2871                                         continue;
2872 
2873                         case 't':       if (havespec) targ(f);
2874                                         continue;
2875 
2876                         case 'd':       continue;
2877 
2878                         case 'm':       if (havespec)  n = numb();
2879                                         continue;
2880 
2881                         case 'e':       continue;
2882                         case ':':       if (!havespec) continue;
2883                                         if (*(fsp+1) != '>') fsprtn = -1;
2884                                         return (fsprtn);
2885 
2886                         default:        if (!havespec) continue;
2887                                         return (-1);
2888                 }
2889         }
2890         return (1);
2891 }
2892 
2893 
2894 static int
2895 numb(void)
2896 {
2897         int n;
2898 
2899         n = 0;
2900         while (*++fsp >= '0' && *fsp <= '9')
2901                 n = 10*n + *fsp-'0';
2902         fsp--;
2903         return (n);
2904 }
2905 
2906 
2907 static void
2908 targ(struct Fspec *f)
2909 {
2910 
2911         if (*++fsp == '-') {
2912                 if (*(fsp + 1) >= '0' && *(fsp+1) <= '9') tincr(numb(), f);
2913                 else tstd(f);
2914                 return;
2915         }
2916         if (*fsp >= '0' && *fsp <= '9') {
2917                 tlist(f);
2918                 return;
2919         }
2920         fsprtn = -1;
2921         fsp--;
2922 }
2923 
2924 
2925 static void
2926 tincr(int n, struct Fspec *f)
2927 {
2928         int l, i;
2929 
2930         l = 1;
2931         for (i = 0; i < 20; i++)
2932                 f->Ftabs[i] = l += n;
2933         f->Ftabs[i] = 0;
2934 }
2935 
2936 
2937 static void
2938 tstd(struct Fspec *f)
2939 {
2940         char std[3];
2941 
2942         std[0] = *++fsp;
2943         if (*(fsp+1) >= '0' && *(fsp+1) <= '9')  {
2944                                                 std[1] = *++fsp;
2945                                                 std[2] = '\0';
2946         } else {
2947                 std[1] = '\0';
2948         }
2949         fsprtn = stdtab(std, f->Ftabs);
2950 }
2951 
2952 
2953 static void
2954 tlist(struct Fspec *f)
2955 {
2956         int n, last, i;
2957 
2958         fsp--;
2959         last = i = 0;
2960 
2961         do {
2962                 if ((n = numb()) <= last || i >= 20) {
2963                         fsprtn = -1;
2964                         return;
2965                 }
2966                 f->Ftabs[i++] = last = n;
2967         } while (*++fsp == ',');
2968 
2969         f->Ftabs[i] = 0;
2970         fsp--;
2971 }
2972 
2973 
2974 static int
2975 expnd(char line[], char buf[], int *sz, struct Fspec *f)
2976 {
2977         char *l, *t;
2978         int b;
2979 
2980         l = line - 1;
2981         b = 1;
2982         t = f->Ftabs;
2983         fsprtn = 0;
2984 
2985         while (*++l && *l != '\n' && b < 511) {
2986                 if (*l == '\t') {
2987                         while (*t && b >= *t)
2988                                 t++;
2989                         if (*t == 0)
2990                                 fsprtn = -2;
2991                         do {
2992                                 buf[b-1] = ' ';
2993                         } while (++b < *t);
2994                 } else {
2995                         buf[b++ - 1] = *l;
2996                 }
2997         }
2998 
2999         buf[b] = '\0';
3000         *sz = b;
3001         if (*l != '\0' && *l != '\n') {
3002                 buf[b-1] = '\n';
3003                 return (-1);
3004         }
3005         buf[b-1] = *l;
3006         if (f->Flim && (b-1 > (int)f->Flim))
3007                 return (-1);
3008         return (fsprtn);
3009 }
3010 
3011 
3012 static void
3013 clear(struct Fspec *f)
3014 {
3015         f->Ftabs[0] = f->Fdel = f->Fmov = f->Ffill = 0;
3016         f->Flim = 0;
3017 }
3018 
3019 
3020 static int
3021 lenchk(char line[], struct Fspec *f)
3022 {
3023         char *l, *t;
3024         int b;
3025 
3026         l = line - 1;
3027         b = 1;
3028         t = f->Ftabs;
3029 
3030         while (*++l && *l != '\n' && b < 511) {
3031                 if (*l == '\t') {
3032                         while (*t && b >= *t)
3033                                 t++;
3034                         while (++b < *t)
3035                                 ;
3036                 } else {
3037                         b++;
3038                 }
3039         }
3040 
3041         if ((*l != '\0' && *l != '\n') || (f->Flim && (b-1 > (int)f->Flim)))
3042                 return (-1);
3043         return (0);
3044 }
3045 #define NTABS 21
3046 
3047 
3048 /*
3049  *      stdtabs: standard tabs table
3050  *      format: option code letter(s), null, tabs, null
3051  */
3052 
3053 static char stdtabs[] = {
3054 'a', 0, 1, 10, 16, 36, 72, 0,                   /* IBM 370 Assembler */
3055 'a', '2', 0, 1, 10, 16, 40, 72, 0,              /* IBM Assembler alternative */
3056 'c', 0, 1, 8, 12, 16, 20, 55, 0,                /* COBOL, normal */
3057 'c', '2', 0, 1, 6, 10, 14, 49, 0,               /* COBOL, crunched */
3058 'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50,
3059         54, 58, 62, 67, 0,
3060 'f', 0, 1, 7, 11, 15, 19, 23, 0,                /* FORTRAN */
3061 'p', 0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
3062                                                 /* PL/I */
3063 's', 0, 1, 10, 55, 0,                           /* SNOBOL */
3064 'u', 0, 1, 12, 20, 44, 0,                       /* UNIVAC ASM */
3065 0 };
3066 
3067 
3068 /*
3069  *      stdtab: return tab list for any "canned" tab option.
3070  *              entry: option points to null-terminated option string
3071  *              tabvect points to vector to be filled in
3072  *      exit: return (0) if legal, tabvect filled, ending with zero
3073  *              return (-1) if unknown option
3074  */
3075 
3076 
3077 static int
3078 stdtab(char option[], char tabvect[NTABS])
3079 {
3080         char *scan;
3081         tabvect[0] = 0;
3082         scan = stdtabs;
3083         while (*scan) {
3084                 if (strequal(&scan, option)) {
3085                         strcopy(scan, tabvect);
3086                         break;
3087                 } else
3088                         while (*scan++)         /* skip over tab specs */
3089                                 ;
3090         }
3091 
3092 /*      later: look up code in /etc/something */
3093         return (tabvect[0] ? 0 : -1);
3094 }
3095 
3096 
3097 /*
3098  *      strequal: checks strings for equality
3099  *              entry: scan1 points to scan pointer, str points to string
3100  *      exit: return (1) if equal, return (0) if not
3101  *              *scan1 is advanced to next nonzero byte after null
3102  */
3103 
3104 
3105 static int
3106 strequal(char **scan1, char *str)
3107 {
3108         char c, *scan;
3109         scan = *scan1;
3110         while ((c = *scan++) == *str && c)
3111                 str++;
3112         *scan1 = scan;
3113         if (c == 0 && *str == 0)
3114                 return (1);
3115         if (c)
3116                 while (*scan++)
3117                         ;
3118         *scan1 = scan;
3119         return (0);
3120 }
3121 
3122 
3123 /*      strcopy: copy source to destination */
3124 
3125 
3126 static void
3127 strcopy(char *source, char *dest)
3128 {
3129         while (*dest++ = *source++)
3130                 ;
3131 }
3132 
3133 
3134 /* This is called before a buffer modifying command so that the */
3135 /* current array of line ptrs is saved in sav and dot and dol are saved */
3136 
3137 
3138 static void
3139 save(void)
3140 {
3141         LINE i;
3142         int     j;
3143 
3144         savdot = dot;
3145         savdol = dol;
3146         for (j = 0; j <= 25; j++)
3147                 savnames[j] = names[j];
3148 
3149         for (i = zero + 1; i <= dol; i++)
3150                 i->sav = i->cur;
3151         initflg = 0;
3152 }
3153 
3154 
3155 /* The undo command calls this to restore the previous ptr array sav */
3156 /* and swap with cur - dot and dol are swapped also. This allows user to */
3157 /* undo an undo */
3158 
3159 
3160 static void
3161 undo(void)
3162 {
3163         int j;
3164         long tmp;
3165         LINE i, tmpdot, tmpdol;
3166 
3167         tmpdot = dot; dot = savdot; savdot = tmpdot;
3168         tmpdol = dol; dol = savdol; savdol = tmpdol;
3169         /* swap arrays using the greater of dol or savdol as upper limit */
3170         for (i = zero + 1; i <= ((dol > savdol) ? dol : savdol); i++) {
3171                 tmp = i->cur;
3172                 i->cur = i->sav;
3173                 i->sav = tmp;
3174         }
3175                 /*
3176                  * If the current text lines are swapped with the
3177                  * text lines in the save buffer, then swap the current
3178                  * marks with those in the save area.
3179                  */
3180 
3181                 for (j = 0; j <= 25; j++) {
3182                         tmp = names[j];
3183                         names[j] = savnames[j];
3184                         savnames[j] = tmp;
3185                 }
3186 }
3187 
3188 static wchar_t
3189 get_wchr(void)
3190 {
3191         wchar_t wc;
3192         char    multi[MB_LEN_MAX];
3193 
3194         if (_mbftowc(multi, &wc, getchr, &peekc) <= 0)
3195                 wc = getchr();
3196         return (wc);
3197 }