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