home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume37 / ed / part01 < prev    next >
Encoding:
Text File  |  1993-05-31  |  52.4 KB  |  2,338 lines

  1. Newsgroups: comp.sources.misc
  2. From: alm@netcom.com (Andrew Moore)
  3. Subject: v37i095:  ed - POSIX-compliant line editor, Part01/03
  4. Message-ID: <csm-v37i095=ed.215318@sparky.IMD.Sterling.COM>
  5. X-Md4-Signature: 64f346280da090e7913cdedd422f7d0c
  6. Keywords: ed(1) POSIX editor
  7. Sender: kent@sparky.imd.sterling.com (Kent Landfield)
  8. Organization: Talke Studio
  9. Date: Mon, 31 May 1993 02:53:40 GMT
  10. Approved: kent@sparky.imd.sterling.com
  11.  
  12. Submitted-by: alm@netcom.com (Andrew Moore)
  13. Posting-number: Volume 37, Issue 95
  14. Archive-name: ed/part01
  15. Environment: POSIX, sun, linux, bsd
  16.  
  17. ed is an 8-bit-clean, POSIX-compliant line editor.  It should work with
  18. any regular expression package that conforms to the POSIX interface
  19. standard, such as GNU regex(3).
  20.  
  21. ---- Cut Here ----
  22. #! /bin/sh
  23. # This is a shell archive.  Remove anything before this line, then feed it
  24. # into a shell via "sh file" or similar.  To overwrite existing files,
  25. # type "sh file -c".
  26. # Contents:  README Makefile.sun ed.c
  27. # Wrapped by kent@sparky on Sun May 30 21:42:59 1993
  28. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin ; export PATH
  29. echo If this archive is complete, you will see the following message:
  30. echo '          "shar: End of archive 1 (of 3)."'
  31. if test -f 'README' -a "${1}" != "-c" ; then 
  32.   echo shar: Will not clobber existing file \"'README'\"
  33. else
  34.   echo shar: Extracting \"'README'\" \(816 characters\)
  35.   sed "s/^X//" >'README' <<'END_OF_FILE'
  36. Xed is an 8-bit-clean, POSIX-compliant line editor.  It should work with
  37. Xany regular expression package that conforms to the POSIX interface
  38. Xstandard, such as GNU regex(3).
  39. X
  40. XIf reliable signals are supported (e.g., POSIX sigaction(2)), it should
  41. Xcompile with little trouble.  Otherwise, the macros spl1() and spl0()
  42. Xshould be redefined to disable interrupts.
  43. X
  44. XThe following compiler directives are recognized:
  45. XGNU_REGEX    - use with GNU regex(3)
  46. XDES        - use to add encryption support (requires crypt(3))
  47. XNO_REALLOC_NULL    - use if realloc(3) does not accept a NULL pointer
  48. XBACKWARDS    - use for backwards compatibility
  49. X
  50. XThe file `POSIX' describes extensions to and deviations from the POSIX
  51. Xstandard.
  52. X
  53. XFor a description of the ed algorithm, see Kernighan and Plauger's book
  54. X"Software Tools in Pascal," Addison-Wesley, 1981.
  55. END_OF_FILE
  56.   if test 816 -ne `wc -c <'README'`; then
  57.     echo shar: \"'README'\" unpacked with wrong size!
  58.   fi
  59.   # end of 'README'
  60. fi
  61. if test -f 'Makefile.sun' -a "${1}" != "-c" ; then 
  62.   echo shar: Will not clobber existing file \"'Makefile.sun'\"
  63. else
  64.   echo shar: Extracting \"'Makefile.sun'\" \(439 characters\)
  65.   sed "s/^X//" >'Makefile.sun' <<'END_OF_FILE'
  66. XPROG   =ed
  67. XCC    =gcc
  68. XCFLAGS=-O -I. -DGNU_REGEX -DNO_REALLOC_NULL -DVI_BANG -DHAVE_STRING_H=1 -DHAVE_ALLOCA_H=1
  69. XSRCS=    ed.c re.c buf.c cbc.c signal.c regex.c
  70. XOBJS=    ed.o re.o buf.o cbc.o signal.o regex.c
  71. X
  72. Xall: ${PROG} doc
  73. X
  74. X${PROG}: ${OBJS}
  75. X    ${CC} -o $@ ${CFLAGS} ${OBJS} ${LDADD}
  76. X
  77. Xed.o: ed.c ed.h
  78. X
  79. Xre.o: re.c ed.h
  80. X
  81. Xbuf.o: buf.c ed.h
  82. X
  83. Xcbc.o: cbc.c ed.h
  84. X
  85. Xdoc:
  86. X    nroff -man ${PROG}.1 >${PROG}.0
  87. X
  88. Xclean:
  89. X    rm -f *.o ${PROG}.0 ${PROG} [Ee]rrs core *~
  90. END_OF_FILE
  91.   if test 439 -ne `wc -c <'Makefile.sun'`; then
  92.     echo shar: \"'Makefile.sun'\" unpacked with wrong size!
  93.   fi
  94.   # end of 'Makefile.sun'
  95. fi
  96. if test -f 'ed.c' -a "${1}" != "-c" ; then 
  97.   echo shar: Will not clobber existing file \"'ed.c'\"
  98. else
  99.   echo shar: Extracting \"'ed.c'\" \(47458 characters\)
  100.   sed "s/^X//" >'ed.c' <<'END_OF_FILE'
  101. X/* ed.c: This file contains the main control and user-interface routines
  102. X   for the ed line editor. */
  103. X/*-
  104. X * Copyright (c) 1993 The Regents of the University of California.
  105. X * All rights reserved.
  106. X *
  107. X * This code is derived from software contributed to Berkeley
  108. X * by Andrew Moore, Talke Studio.
  109. X *
  110. X * Redistribution and use in source and binary forms, with or without
  111. X * modification, are permitted provided that the following conditions
  112. X * are met:
  113. X * 1. Redistributions of source code must retain the above copyright
  114. X *    notice, this list of conditions and the following disclaimer.
  115. X * 2. Redistributions in binary form must reproduce the above copyright
  116. X *    notice, this list of conditions and the following disclaimer in the
  117. X *    documentation and/or other materials provided with the distribution.
  118. X * 3. All advertising materials mentioning features or use of this software
  119. X *    must display the following acknowledgement:
  120. X *    This product includes software developed by the University of
  121. X *    California, Berkeley and its contributors.
  122. X * 4. Neither the name of the University nor the names of its contributors
  123. X *    may be used to endorse or promote products derived from this software
  124. X *    without specific prior written permission.
  125. X *
  126. X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  127. X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  128. X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  129. X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  130. X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  131. X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  132. X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  133. X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  134. X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  135. X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  136. X * SUCH DAMAGE.
  137. X */
  138. X/*-
  139. X * Kernighan/Plauger, "Software Tools in Pascal," (c) 1981 by
  140. X * Addison-Wesley Publishing Company, Inc.  Reprinted with permission of
  141. X * the publisher.
  142. X */
  143. X
  144. X#ifndef lint
  145. Xchar copyright1[] =
  146. X"@(#) Copyright (c) 1993 The Regents of the University of California.\n\
  147. X All rights reserved.\n";
  148. Xchar copyright2[] =
  149. X"@(#) Kernighan/Plauger, Software Tools in Pascal, (c) 1981 by\n\
  150. X Addison-Wesley Publishing Company, Inc.  Reprinted with permission of\n\
  151. X the publisher.\n";
  152. X#endif /* not lint */
  153. X
  154. X#ifndef lint
  155. Xstatic char sccsid[] = "@(#)ed.c    5.5 (Berkeley) 3/28/93";
  156. X#endif /* not lint */
  157. X
  158. X/*
  159. X * CREDITS
  160. X *    The buf.c algorithm is attributed to Rodney Ruddock of
  161. X *    the University of Guelph, Guelph, Ontario.
  162. X *
  163. X *    The cbc.c encryption code is adapted from
  164. X *    the bdes program by Matt Bishop of Dartmouth College,
  165. X *    Hanover, NH.
  166. X *
  167. X *    Addison-Wesley Publishing Company generously granted
  168. X *    permission to distribute this program over Internet.
  169. X *
  170. X */
  171. X
  172. X#include <unistd.h>
  173. X#include <stdio.h>
  174. X#include <stdlib.h>
  175. X#include <string.h>
  176. X#include <ctype.h>
  177. X#include <setjmp.h>
  178. X#include <pwd.h>
  179. X#include <sys/ioctl.h>
  180. X
  181. X#include "ed.h"
  182. X
  183. X#ifdef _POSIX_SOURCE
  184. Xsigjmp_buf env;
  185. X#else
  186. Xjmp_buf env;
  187. X#endif
  188. X
  189. X/* static buffers */
  190. Xchar *shcmd;            /* shell command buffer */
  191. Xint shcmdsz;            /* shell command buffer size */
  192. Xint shcmdi;            /* shell command buffer index */
  193. Xchar *cvbuf;            /* global command buffer */
  194. Xint cvbufsz;            /* global command buffer size */
  195. Xchar *lhbuf;            /* lhs buffer */
  196. Xint lhbufsz;            /* lhs buffer size */
  197. Xchar *rhbuf;            /* rhs buffer */
  198. Xint rhbufsz;            /* rhs buffer size */
  199. Xint rhbufi;            /* rhs buffer index */
  200. Xchar *rbuf;            /* regsub buffer */
  201. Xint rbufsz;            /* regsub buffer size */
  202. Xchar *sbuf;            /* file i/o buffer */
  203. Xint sbufsz;            /* file i/o buffer size */
  204. Xchar *ibuf;            /* ed command-line buffer */
  205. Xint ibufsz;            /* ed command-line buffer size */
  206. Xchar *ibufp;            /* pointer to ed command-line buffer */
  207. X
  208. X/* global flags */
  209. Xint isbinary;            /* if set, buffer contains ASCII NULs */
  210. Xint modified;            /* if set, buffer modified since last write */
  211. Xint garrulous = 0;        /* if set, print all error messages */
  212. Xint scripted = 0;        /* if set, suppress diagnostics */
  213. Xint des = 0;            /* if set, use crypt(3) for i/o */
  214. Xint mutex = 0;            /* if set, signals set "sigflags" */
  215. Xint sigflags = 0;        /* if set, signals received while mutex set */
  216. Xint sigactive = 0;        /* if set, signal handlers are enabled */
  217. Xint red = 0;            /* if set, restrict shell/directory access */
  218. X
  219. Xchar dfn[MAXFNAME + 1] = "";    /* default filename */
  220. Xlong curln;            /* current address */
  221. Xlong lastln;            /* last address */
  222. Xint lineno;            /* script line number */
  223. Xchar *prompt;            /* command-line prompt */
  224. Xchar *dps = "*";        /* default command-line prompt */
  225. X
  226. Xchar *usage = "usage: %s [-] [-sx] [-p string] [name]\n";
  227. X
  228. Xextern char errmsg[];
  229. Xextern int optind;
  230. Xextern char *optarg;
  231. X
  232. X/* ed: line editor */
  233. Xmain(argc, argv)
  234. X    int argc;
  235. X    char **argv;
  236. X{
  237. X    int c, n;
  238. X    long status = 0;
  239. X
  240. X    red = (n = strlen(argv[0])) > 2 && argv[0][n - 3] == 'r';
  241. Xtop:
  242. X    while ((c = getopt(argc, argv, "p:sx")) != EOF)
  243. X        switch(c) {
  244. X        case 'p':                /* set prompt */
  245. X            prompt = optarg;
  246. X            break;
  247. X        case 's':                /* run script */
  248. X            scripted = 1;
  249. X            break;
  250. X        case 'x':                /* use crypt */
  251. X#ifdef DES
  252. X            des = getkey();
  253. X#else
  254. X            fprintf(stderr, "crypt unavailable\n?\n");
  255. X#endif
  256. X            break;
  257. X
  258. X        default:
  259. X            fprintf(stderr, usage, argv[0]);
  260. X            exit(1);
  261. X        }
  262. X    argv += optind;
  263. X    argc -= optind;
  264. X    if (argc && **argv == '-') {
  265. X        scripted = 1;
  266. X        if (argc > 1) {
  267. X            optind = 1;
  268. X            goto top;
  269. X        }
  270. X        argv++;
  271. X        argc--;
  272. X    }
  273. X    /* assert: reliable signals! */
  274. X#ifdef SIGWINCH
  275. X    dowinch(SIGWINCH);
  276. X    if (isatty(0)) signal(SIGWINCH, dowinch);
  277. X#endif
  278. X    signal(SIGHUP, onhup);
  279. X    signal(SIGQUIT, SIG_IGN);
  280. X    signal(SIGINT, onintr);
  281. X#ifdef _POSIX_SOURCE
  282. X    if (status = sigsetjmp(env, 1))
  283. X#else
  284. X    if (status = setjmp(env))
  285. X#endif
  286. X    {
  287. X        fputs("\n?\n", stderr);
  288. X        sprintf(errmsg, "interrupt");
  289. X    } else {
  290. X        init_buf();
  291. X        sigactive = 1;            /* enable signal handlers */
  292. X        if (argc && **argv && ckfn(*argv)) {
  293. X            if (doread(0, *argv) < 0 && !isatty(0))
  294. X                quit(2);
  295. X            else if (**argv != '!')
  296. X                strcpy(dfn, *argv);
  297. X        } else if (argc) {
  298. X            fputs("?\n", stderr);
  299. X            if (**argv == '\0')
  300. X                sprintf(errmsg, "invalid filename");
  301. X            if (!isatty(0))
  302. X                quit(2);
  303. X        }
  304. X    }
  305. X    for (;;) {
  306. X        if (status < 0 && garrulous)
  307. X            fprintf(stderr, "%s\n", errmsg);
  308. X        if (prompt) {
  309. X            printf("%s", prompt);
  310. X            fflush(stdout);
  311. X        }
  312. X        if ((n = getline()) < 0) {
  313. X            status = ERR;
  314. X            continue;
  315. X        } else if (n == 0) {
  316. X            if (modified && !scripted) {
  317. X                fputs("?\n", stderr);
  318. X                sprintf(errmsg, "warning: file modified");
  319. X                if (!isatty(0)) {
  320. X                    fprintf(stderr, garrulous ? "script, line %d: %s\n"
  321. X                        : "", lineno, errmsg);
  322. X                    quit(2);
  323. X                }
  324. X                clearerr(stdin);
  325. X                modified = 0;
  326. X                status = EMOD;
  327. X                continue;
  328. X            } else
  329. X                quit(0);
  330. X        } else if (ibuf[n - 1] != '\n') {
  331. X            /* discard line */
  332. X            sprintf(errmsg, "unexpected end-of-file");
  333. X            clearerr(stdin);
  334. X            status = ERR;
  335. X            continue;
  336. X        }
  337. X        if ((n = getlist()) >= 0 && (status = ckglob()) != 0) {
  338. X            if (status > 0 && (status = doglob(status)) >= 0) {
  339. X                curln = status;
  340. X                continue;
  341. X            }
  342. X        } else if ((status = n) >= 0 && (status = docmd(0)) >= 0) {
  343. X            if (!status || status
  344. X             && (status = doprint(curln, curln, status)) >= 0)
  345. X                continue;
  346. X        }
  347. X        switch (status) {
  348. X        case EOF:
  349. X            quit(0);
  350. X        case EMOD:
  351. X            modified = 0;
  352. X            fputs("?\n", stderr);        /* give warning */
  353. X            sprintf(errmsg, "warning: file modified");
  354. X            if (!isatty(0)) {
  355. X                fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg);
  356. X                quit(2);
  357. X            }
  358. X            break;
  359. X        case FATAL:
  360. X            if (!isatty(0))
  361. X                fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg);
  362. X            else
  363. X                fprintf(stderr, garrulous ? "%s\n" : "", errmsg);
  364. X            quit(3);
  365. X        default:
  366. X            fputs("?\n", stderr);
  367. X            if (!isatty(0)) {
  368. X                fprintf(stderr, garrulous ? "script, line %d: %s\n" : "", lineno, errmsg);
  369. X                quit(2);
  370. X            }
  371. X            break;
  372. X        }
  373. X    }
  374. X    /*NOTREACHED*/
  375. X}
  376. X
  377. X
  378. Xlong line1, line2, nlines;
  379. X
  380. X/* getlist: get line numbers from the command buffer until an illegal
  381. X   address is seen.  return range status */
  382. Xgetlist()
  383. X{
  384. X    long num;
  385. X
  386. X    nlines = line2 = 0;
  387. X    while ((num = getone()) >= 0) {
  388. X        line1 = line2;
  389. X        line2 = num;
  390. X        nlines++;
  391. X        if (*ibufp != ',' && *ibufp != ';')
  392. X            break;
  393. X        else if (*ibufp++ == ';')
  394. X            curln = num;
  395. X    }
  396. X    nlines = min(nlines, 2);
  397. X    if (nlines == 0)
  398. X        line2 = curln;
  399. X    if (nlines <= 1)
  400. X        line1 = line2;
  401. X    return  (num == ERR) ? ERR : nlines;
  402. X}
  403. X
  404. X
  405. X/*  getone: return the next line number in the command buffer */
  406. Xlong
  407. Xgetone()
  408. X{
  409. X    int c;
  410. X    long i, num;
  411. X
  412. X    if ((num = getnum(1)) < 0)
  413. X        return num;
  414. X    for (;;) {
  415. X        c = isspace(*ibufp);
  416. X        skipblanks();
  417. X        c = c && isdigit(*ibufp);
  418. X        if (!c && *ibufp != '+' && *ibufp != '-' && *ibufp != '^')
  419. X            break;
  420. X        c = c ? '+' : *ibufp++;
  421. X        if ((i = getnum(0)) < 0) {
  422. X            sprintf(errmsg, "invalid address");
  423. X            return  i;
  424. X        }
  425. X        if (c == '+')
  426. X            num += i;
  427. X        else    num -= i;
  428. X    }
  429. X    if (num > lastln || num < 0) {
  430. X        sprintf(errmsg, "invalid address");
  431. X        return ERR;
  432. X    }
  433. X    return num;
  434. X}
  435. X
  436. X
  437. X#define MAXMARK 26            /* max number of marks */
  438. X
  439. Xline_t    *mark[MAXMARK];            /* line markers */
  440. Xint markno;                /* line marker count */
  441. X
  442. X/* getnum:  return a relative line number from the command buffer */
  443. Xlong
  444. Xgetnum(first)
  445. X    int first;
  446. X{
  447. X    pattern_t *pat;
  448. X    char c;
  449. X
  450. X    skipblanks();
  451. X    if (isdigit(*ibufp))
  452. X        return strtol(ibufp, &ibufp, 10);
  453. X    switch(c = *ibufp) {
  454. X    case '.':
  455. X        ibufp++;
  456. X        return first ? curln : ERR;
  457. X    case '$':
  458. X        ibufp++;
  459. X        return first ? lastln : ERR;
  460. X    case '/':
  461. X    case '?':
  462. X        if ((pat = optpat()) == NULL)
  463. X            return ERR;
  464. X        else if (*ibufp == c)
  465. X            ibufp++;
  466. X        return first ? patscan(pat, (c == '/') ? 1 : 0) : ERR;
  467. X    case '^':
  468. X    case '-':
  469. X    case '+':
  470. X        return first ? curln : 1;
  471. X    case '\'':
  472. X        ibufp++;
  473. X        return (first && islower(*ibufp)) ? getaddr(mark[*ibufp++ - 'a']) : ERR;
  474. X    case '%':
  475. X    case ',':
  476. X    case ';':
  477. X        if (first) {
  478. X            ibufp++;
  479. X            line2 = (c == ';') ? curln : 1;
  480. X            nlines++;
  481. X            return lastln;
  482. X        }
  483. X        return 1;
  484. X    default:
  485. X        return  first ? EOF : 1;
  486. X    }
  487. X}
  488. X
  489. X
  490. X/* gflags */
  491. X#define GLB 001        /* global command */
  492. X#define GPR 002        /* print after command */
  493. X#define GLS 004        /* list after command */
  494. X#define GNP 010        /* enumerate after command */
  495. X#define GSG 020        /* global substitute */
  496. X
  497. X
  498. X/* VRFYCMD: verify the command suffix in the command buffer */
  499. X#define VRFYCMD() { \
  500. X    int done = 0; \
  501. X    do { \
  502. X        switch(*ibufp) { \
  503. X        case 'p': \
  504. X            gflag |= GPR, ibufp++; \
  505. X            break; \
  506. X        case 'l': \
  507. X            gflag |= GLS, ibufp++; \
  508. X            break; \
  509. X        case 'n': \
  510. X            gflag |= GNP, ibufp++; \
  511. X            break; \
  512. X        default: \
  513. X            done++; \
  514. X        } \
  515. X    } while (!done); \
  516. X    if (*ibufp++ != '\n') { \
  517. X        sprintf(errmsg, "invalid command suffix"); \
  518. X        return ERR; \
  519. X    } \
  520. X}
  521. X
  522. X
  523. X/* ckglob:  set lines matching a pattern in the command buffer; return
  524. X   global status  */
  525. Xckglob()
  526. X{
  527. X    pattern_t *pat;
  528. X    char c, delim;
  529. X    char *s;
  530. X    int nomatch;
  531. X    long n;
  532. X    line_t *lp;
  533. X    int gflag = 0;            /* print suffix of interactive cmd */
  534. X
  535. X    if ((c = *ibufp) == 'V' || c == 'G')
  536. X        gflag = GLB;
  537. X    else if (c != 'g' && c != 'v')
  538. X        return 0;
  539. X    if (ckrange(1, lastln) < 0)
  540. X        return ERR;
  541. X    else if ((delim = *++ibufp) == ' ' || delim == '\n') {
  542. X        sprintf(errmsg, "invalid pattern delimiter");
  543. X        return ERR;
  544. X    } else if ((pat = optpat()) == NULL)
  545. X        return ERR;
  546. X    else if (*ibufp == delim)
  547. X        ibufp++;
  548. X    if (gflag)
  549. X        VRFYCMD();            /* get print suffix */
  550. X    for (lp = getlp(n = 1); n <= lastln; n++, lp = lp->next) {
  551. X        if ((s = gettxt(lp)) == NULL)
  552. X            return ERR;
  553. X        lp->len &= ~ACTV;            /* zero ACTV  bit */
  554. X        if (isbinary)
  555. X            s = nultonl(s, lp->len & ~ACTV);
  556. X        if (line1 <= n && n <= line2
  557. X         && (!(nomatch = regexec(pat, s, 0, NULL, 0))
  558. X         && (c == 'g'  || c == 'G')
  559. X         || nomatch && (c == 'v' || c == 'V')))
  560. X            lp->len |= ACTV;
  561. X    }
  562. X    return gflag | GSG;
  563. X}
  564. X
  565. X
  566. X/* doglob: apply command list in the command buffer to the active
  567. X   lines in a range; return command status */
  568. Xlong
  569. Xdoglob(gflag)
  570. X    int gflag;
  571. X{
  572. X    static char *ocmd = NULL;
  573. X    static int ocmdsz = 0;
  574. X
  575. X    line_t *lp = NULL;
  576. X    long lc;
  577. X    int status;
  578. X    int n;
  579. X    int interact = gflag & ~GSG;        /* GLB & gflag ? */
  580. X    char *cmd = NULL;
  581. X
  582. X    if (!interact && (cmd = getcmdv(&n, 0)) == NULL)
  583. X        return ERR;
  584. X    ureset();
  585. X    for (;;) {
  586. X        for (lp = getlp(lc = 1); lc <= lastln; lc++, lp = lp->next)
  587. X            if (lp->len & ACTV)        /* active line */
  588. X                break;
  589. X        if (lc > lastln)
  590. X            break;
  591. X        lp->len ^= ACTV;            /* zero ACTV bit */
  592. X        curln = lc;
  593. X        if (interact) {
  594. X            /* print curln and get a command in global syntax */
  595. X            if (doprint(curln, curln, 0) < 0)
  596. X                return ERR;
  597. X            while ((n = getline()) > 0
  598. X                && ibuf[n - 1] != '\n')
  599. X                clearerr(stdin);
  600. X            if (n < 0)
  601. X                return ERR;
  602. X            else if (n == 0) {
  603. X                sprintf(errmsg, "unexpected end-of-file");
  604. X                return ERR;
  605. X            } else if (n == 1 && !strcmp(ibuf, "\n"))
  606. X                continue;
  607. X            else if (n == 2 && !strcmp(ibuf, "&\n")) {
  608. X                if (cmd == NULL) {
  609. X                    sprintf(errmsg, "no previous command");
  610. X                    return ERR;
  611. X                } else cmd = ocmd;
  612. X            } else if ((cmd = getcmdv(&n, 0)) == NULL)
  613. X                return ERR;
  614. X            else {
  615. X                CKBUF(ocmd, ocmdsz, n + 1, ERR);
  616. X                memcpy(ocmd, cmd, n + 1);
  617. X                cmd = ocmd;
  618. X            }
  619. X
  620. X        }
  621. X        ibufp = cmd;
  622. X        for (; *ibufp;)
  623. X            if ((status = getlist()) < 0
  624. X             || (status = docmd(1)) < 0
  625. X             || (status > 0
  626. X             && (status = doprint(curln, curln, status)) < 0))
  627. X                return status;
  628. X    }
  629. X    return ((interact & ~GLB ) && doprint(curln, curln, interact) < 0) ? ERR : curln;
  630. X}
  631. X
  632. X
  633. X/* GETLINE3: get a legal address from the command buffer */
  634. X#define GETLINE3(num) \
  635. X{ \
  636. X    long ol1, ol2; \
  637. X\
  638. X    ol1 = line1, ol2 = line2; \
  639. X    if (getlist() < 0) \
  640. X        return ERR; \
  641. X    if (line2 < 0 || lastln < line2) { \
  642. X        sprintf(errmsg, "invalid address"); \
  643. X        return ERR; \
  644. X    } \
  645. X    num = line2; \
  646. X    line1 = ol1, line2 = ol2; \
  647. X}
  648. X
  649. X/* sgflags */
  650. X#define SGG 001        /* complement previous global substitute suffix */
  651. X#define SGP 002        /* complement previous print suffix */
  652. X#define SGR 004        /* use last regex instead of last pat */
  653. X#define SGF 010        /* newline found */
  654. X
  655. Xlong ucurln = -1;    /* if >= 0, undo enabled */
  656. Xlong ulastln = -1;    /* if >= 0, undo enabled */
  657. Xint usw = 0;        /* if set, undo last undo */
  658. Xint patlock = 0;    /* if set, pattern not released by optpat() */
  659. X
  660. Xlong rows = 22;        /* scroll length: ws_row - 2 */
  661. X
  662. X/* docmd: execute the next command in command buffer; return print
  663. X   request, if any */
  664. Xdocmd(glob)
  665. X    int glob;
  666. X{
  667. X    static pattern_t *pat = NULL;
  668. X    static int sgflag = 0;
  669. X
  670. X    pattern_t *tpat;
  671. X    char *fnp;
  672. X    int gflag = 0;
  673. X    int sflags = 0;
  674. X    long num = 0;
  675. X    int n = 0;
  676. X    int c;
  677. X
  678. X    skipblanks();
  679. X    switch(c = *ibufp++) {
  680. X    case 'a':
  681. X        VRFYCMD();
  682. X        if (!glob) ureset();
  683. X        if (append(line2, glob) < 0)
  684. X            return ERR;
  685. X        break;
  686. X    case 'c':
  687. X        if (ckrange(curln, curln) < 0)
  688. X            return ERR;
  689. X        VRFYCMD();
  690. X        if (!glob) ureset();
  691. X        if (lndelete(line1, line2) < 0 || append(curln, glob) < 0)
  692. X            return ERR;
  693. X        break;
  694. X    case 'd':
  695. X        if (ckrange(curln, curln) < 0)
  696. X            return ERR;
  697. X        VRFYCMD();
  698. X        if (!glob) ureset();
  699. X        if (lndelete(line1, line2) < 0)
  700. X            return ERR;
  701. X        else if (nextln(curln, lastln) != 0)
  702. X            curln = nextln(curln, lastln);
  703. X        modified = 1;
  704. X        break;
  705. X    case 'e':
  706. X        if (modified && !scripted)
  707. X            return EMOD;
  708. X        /* fall through */
  709. X    case 'E':
  710. X        if (nlines > 0) {
  711. X            sprintf(errmsg, "unexpected address");
  712. X            return ERR;
  713. X        } else if (!isspace(*ibufp)) {
  714. X            sprintf(errmsg, "unexpected command suffix");
  715. X            return ERR;
  716. X        } else if ((fnp = getfn()) == NULL)
  717. X            return ERR;
  718. X        VRFYCMD();
  719. X        memset(mark, 0, sizeof mark);
  720. X        lndelete(1, lastln);
  721. X        ureset();
  722. X        if (sbclose() < 0)
  723. X            return ERR;
  724. X        else if (sbopen() < 0)
  725. X            return FATAL;
  726. X        if (*fnp && *fnp != '!') strcpy(dfn, fnp);
  727. X#ifdef BACKWARDS
  728. X        if (*fnp == '\0' && *dfn == '\0') {
  729. X            sprintf(errmsg, "no current filename");
  730. X            return ERR;
  731. X        }
  732. X#endif
  733. X        if (doread(0, *fnp ? fnp : dfn) < 0)
  734. X            return ERR;
  735. X        ureset();
  736. X        modified = 0;
  737. X        ucurln = ulastln = -1;
  738. X        break;
  739. X    case 'f':
  740. X        if (nlines > 0) {
  741. X            sprintf(errmsg, "unexpected address");
  742. X            return ERR;
  743. X        } else if (!isspace(*ibufp)) {
  744. X            sprintf(errmsg, "unexpected command suffix");
  745. X            return ERR;
  746. X        } else if ((fnp = getfn()) == NULL)
  747. X            return ERR;
  748. X        else if (*fnp == '!') {
  749. X            sprintf(errmsg, "invalid redirection");
  750. X            return ERR;
  751. X        }
  752. X        VRFYCMD();
  753. X        if (*fnp) strcpy(dfn, fnp);
  754. X        printf("%s\n", esctos(dfn));
  755. X        break;
  756. X    case 'g':
  757. X    case 'G':
  758. X        sprintf(errmsg, "cannot nest global commands");
  759. X        return ERR;
  760. X    case 'h':
  761. X        if (nlines > 0) {
  762. X            sprintf(errmsg, "unexpected address");
  763. X            return ERR;
  764. X        }
  765. X        VRFYCMD();
  766. X        if (*errmsg) fprintf(stderr, "%s\n", errmsg);
  767. X        break;
  768. X    case 'H':
  769. X        if (nlines > 0) {
  770. X            sprintf(errmsg, "unexpected address");
  771. X            return ERR;
  772. X        }
  773. X        VRFYCMD();
  774. X        if ((garrulous = 1 - garrulous) && *errmsg)
  775. X            fprintf(stderr, "%s\n", errmsg);
  776. X        break;
  777. X    case 'i':
  778. X        if (line2 == 0) {
  779. X            sprintf(errmsg, "invalid address");
  780. X            return ERR;
  781. X        }
  782. X        VRFYCMD();
  783. X        if (!glob) ureset();
  784. X        if (append(prevln(line2, lastln), glob) < 0)
  785. X            return ERR;
  786. X        break;
  787. X    case 'j':
  788. X        if (ckrange(curln, curln + 1) < 0)
  789. X            return ERR;
  790. X        VRFYCMD();
  791. X        if (!glob) ureset();
  792. X        if (line1 != line2 && join(line1, line2) < 0)
  793. X            return ERR;
  794. X        break;
  795. X    case 'k':
  796. X        c = *ibufp++;
  797. X        if (line2 == 0) {
  798. X            sprintf(errmsg, "invalid address");
  799. X            return ERR;
  800. X        } else if (!islower(c)) {
  801. X            sprintf(errmsg, "invalid mark character");
  802. X            return ERR;
  803. X        }
  804. X        VRFYCMD();
  805. X        if (!mark[c - 'a']) markno++;
  806. X        mark[c - 'a'] = getlp(line2);
  807. X        break;
  808. X    case 'l':
  809. X        if (ckrange(curln, curln) < 0)
  810. X            return ERR;
  811. X        VRFYCMD();
  812. X        if (doprint(line1, line2, gflag | GLS) < 0)
  813. X            return ERR;
  814. X        gflag = 0;
  815. X        break;
  816. X    case 'm':
  817. X        if (ckrange(curln, curln) < 0)
  818. X            return ERR;
  819. X        GETLINE3(num);
  820. X        if (line1 <= num && num < line2) {
  821. X            sprintf(errmsg, "invalid destination");
  822. X            return ERR;
  823. X        }
  824. X        VRFYCMD();
  825. X        if (!glob) ureset();
  826. X        if (num == line1 - 1 || num == line2)
  827. X            curln = line2;
  828. X        else if (move(num) < 0)
  829. X            return ERR;
  830. X        else
  831. X            modified = 1;
  832. X        break;
  833. X    case 'n':
  834. X        if (ckrange(curln, curln) < 0)
  835. X            return ERR;
  836. X        VRFYCMD();
  837. X        if (doprint(line1, line2, gflag | GNP) < 0)
  838. X            return ERR;
  839. X        gflag = 0;
  840. X        break;
  841. X    case 'p':
  842. X        if (ckrange(curln, curln) < 0)
  843. X            return ERR;
  844. X        VRFYCMD();
  845. X        if (doprint(line1, line2, gflag | GPR) < 0)
  846. X            return ERR;
  847. X        gflag = 0;
  848. X        break;
  849. X    case 'P':
  850. X        if (nlines > 0) {
  851. X            sprintf(errmsg, "unexpected address");
  852. X            return ERR;
  853. X        }
  854. X        VRFYCMD();
  855. X        prompt = prompt ? NULL : optarg ? optarg : dps;
  856. X        break;
  857. X    case 'q':
  858. X    case 'Q':
  859. X        if (nlines > 0) {
  860. X            sprintf(errmsg, "unexpected address");
  861. X            return ERR;
  862. X        }
  863. X        VRFYCMD();
  864. X        gflag =  (modified && !scripted && c == 'q') ? EMOD : EOF;
  865. X        break;
  866. X    case 'r':
  867. X        if (!isspace(*ibufp)) {
  868. X            sprintf(errmsg, "unexpected command suffix");
  869. X            return ERR;
  870. X        } else if (nlines == 0)
  871. X            line2 = lastln;
  872. X        if ((fnp = getfn()) == NULL)
  873. X            return ERR;
  874. X        VRFYCMD();
  875. X        if (!glob) ureset();
  876. X        if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp);
  877. X#ifdef BACKWARDS
  878. X        if (*fnp == '\0' && *dfn == '\0') {
  879. X            sprintf(errmsg, "no current filename");
  880. X            return ERR;
  881. X        }
  882. X#endif
  883. X        if ((num = doread(line2, *fnp ? fnp : dfn)) < 0)
  884. X            return ERR;
  885. X        else if (num && num != lastln)
  886. X            modified = 1;
  887. X        break;
  888. X    case 's':
  889. X        do {
  890. X            switch(*ibufp) {
  891. X            case '\n':
  892. X                sflags |=SGF;
  893. X                break;
  894. X            case 'g':
  895. X                sflags |= SGG;
  896. X                ibufp++;
  897. X                break;
  898. X            case 'p':
  899. X                sflags |= SGP;
  900. X                ibufp++;
  901. X                break;
  902. X            case 'r':
  903. X                sflags |= SGR;
  904. X                ibufp++;
  905. X                break;
  906. X            default:
  907. X                if (sflags) {
  908. X                    sprintf(errmsg, "invalid command suffix");
  909. X                    return ERR;
  910. X                }
  911. X            }
  912. X        } while (sflags && *ibufp != '\n');
  913. X        if (sflags && !pat) {
  914. X            sprintf(errmsg, "no previous substitution");
  915. X            return ERR;
  916. X        } else if (!(sflags & SGF))
  917. X            sgflag &= 0xff;
  918. X        tpat = pat;
  919. X        spl1();
  920. X        if ((!sflags || (sflags & SGR))
  921. X         && (tpat = optpat()) == NULL)
  922. X            return ERR;
  923. X        else if (tpat != pat) {
  924. X            if (pat) {
  925. X                 regfree(pat);
  926. X                 free(pat);
  927. X             }
  928. X            pat = tpat;
  929. X            patlock = 1;        /* reserve pattern */
  930. X        } else if (pat == NULL) {
  931. X            /* NOTREACHED */
  932. X            sprintf(errmsg, "no previous substitution");
  933. X            return ERR;
  934. X        }
  935. X        spl0();
  936. X        if (!sflags && (sgflag = getrhs(glob)) < 0)
  937. X            return ERR;
  938. X        else if (glob)
  939. X            sgflag |= GLB;
  940. X        else
  941. X            sgflag &= ~GLB;
  942. X        if (sflags & SGG)
  943. X            sgflag ^= GSG;
  944. X        if (sflags & SGP)
  945. X            sgflag ^= GPR, sgflag &= ~(GLS | GNP);
  946. X        do {
  947. X            switch(*ibufp) {
  948. X            case 'p':
  949. X                sgflag |= GPR, ibufp++;
  950. X                break;
  951. X            case 'l':
  952. X                sgflag |= GLS, ibufp++;
  953. X                break;
  954. X            case 'n':
  955. X                sgflag |= GNP, ibufp++;
  956. X                break;
  957. X            default:
  958. X                n++;
  959. X            }
  960. X        } while (!n);
  961. X        if (ckrange(curln, curln) < 0)
  962. X            return ERR;
  963. X        VRFYCMD();
  964. X        if (!glob) ureset();
  965. X        if ((n = subst(pat, sgflag)) < 0)
  966. X            return ERR;
  967. X        else if (n)
  968. X            modified = 1;
  969. X        break;
  970. X    case 't':
  971. X        if (ckrange(curln, curln) < 0)
  972. X            return ERR;
  973. X        GETLINE3(num);
  974. X        VRFYCMD();
  975. X        if (!glob) ureset();
  976. X        if (transfer(num) < 0)
  977. X            return ERR;
  978. X        modified = 1;
  979. X        break;
  980. X    case 'u':
  981. X        if (nlines > 0) {
  982. X            sprintf(errmsg, "unexpected address");
  983. X            return ERR;
  984. X        }
  985. X        VRFYCMD();
  986. X        if (undo() < 0)
  987. X            return ERR;
  988. X        break;
  989. X    case 'v':
  990. X    case 'V':
  991. X        sprintf(errmsg, "cannot nest global commands");
  992. X        return ERR;
  993. X    case 'w':
  994. X    case 'W':
  995. X        if ((n = *ibufp) == 'q' || n == 'Q') {
  996. X            gflag = EOF;
  997. X            ibufp++;
  998. X        }
  999. X        if (!isspace(*ibufp)) {
  1000. X            sprintf(errmsg, "unexpected command suffix");
  1001. X            return ERR;
  1002. X        } else if ((fnp = getfn()) == NULL)
  1003. X            return ERR;
  1004. X        if (nlines == 0 && !lastln)
  1005. X            line1 = line2 = 0;
  1006. X        else if (ckrange(1, lastln) < 0)
  1007. X            return ERR;
  1008. X        VRFYCMD();
  1009. X        if (*dfn == '\0' && *fnp != '!') strcpy(dfn, fnp);
  1010. X#ifdef BACKWARDS
  1011. X        if (*fnp == '\0' && *dfn == '\0') {
  1012. X            sprintf(errmsg, "no current filename");
  1013. X            return ERR;
  1014. X        }
  1015. X#endif
  1016. X        if ((num = dowrite(line1, line2, *fnp ? fnp : dfn, (c == 'W') ? "a" : "w")) < 0)
  1017. X            return ERR;
  1018. X        else if (num == lastln)
  1019. X            modified = 0;
  1020. X        else if (modified && !scripted && n == 'q')
  1021. X            gflag = EMOD;
  1022. X        break;
  1023. X    case 'x':
  1024. X        if (nlines > 0) {
  1025. X            sprintf(errmsg, "unexpected address");
  1026. X            return ERR;
  1027. X        }
  1028. X        VRFYCMD();
  1029. X#ifdef DES
  1030. X        des = getkey();
  1031. X#else
  1032. X        sprintf(errmsg, "crypt unavailable");
  1033. X        return ERR;
  1034. X#endif
  1035. X        break;
  1036. X    case 'z':
  1037. X        if (ckrange(line1 = 1, curln + !glob) < 0)
  1038. X            return ERR;
  1039. X        else if ('0' < *ibufp && *ibufp <= '9')
  1040. X            rows = strtol(ibufp, &ibufp, 10);
  1041. X        VRFYCMD();
  1042. X        if (doprint(line2, min(lastln, line2 + rows - 1), gflag) < 0)
  1043. X            return ERR;
  1044. X        gflag = 0;
  1045. X        break;
  1046. X    case '=':
  1047. X        VRFYCMD();
  1048. X        printf("%d\n", nlines ? line2 : lastln);
  1049. X        break;
  1050. X    case '!':
  1051. X#ifndef VI_BANG
  1052. X        if (nlines > 0) {
  1053. X            sprintf(errmsg, "unexpected address");
  1054. X            return ERR;
  1055. X        }
  1056. X#endif
  1057. X        if ((sflags = getshcmd()) < 0)
  1058. X            return ERR;
  1059. X        VRFYCMD();
  1060. X        if (sflags) printf("%s\n", shcmd + 1);
  1061. X#ifdef VI_BANG
  1062. X        if (nlines == 0) {
  1063. X#endif
  1064. X            system(shcmd + 1);
  1065. X            if (!scripted) printf("!\n");
  1066. X            break;
  1067. X#ifdef VI_BANG
  1068. X        }
  1069. X        if (!lastln && !line1 && !line2) {
  1070. X            if (!glob) ureset();
  1071. X        } else if (ckrange(curln, curln) < 0)
  1072. X            return ERR;
  1073. X        else {
  1074. X            if (!glob) ureset();
  1075. X            if (lndelete(line1, line2) < 0)
  1076. X                return ERR;
  1077. X            line2 = curln;
  1078. X            modified = 1;
  1079. X        }
  1080. X        if ((num = doread(line2, shcmd)) < 0)
  1081. X            return ERR;
  1082. X        else if (num && num != lastln)
  1083. X            modified = 1;
  1084. X        break;
  1085. X#endif
  1086. X    case '\n':
  1087. X        if (ckrange(line1 = 1, curln + !glob) < 0
  1088. X         || doprint(line2, line2, 0) < 0)
  1089. X            return ERR;
  1090. X        break;
  1091. X    default:
  1092. X        sprintf(errmsg, "unknown command");
  1093. X        return ERR;
  1094. X    }
  1095. X    return gflag;
  1096. X}
  1097. X
  1098. X
  1099. X/* ckrange: return status of line number range check */
  1100. Xckrange(def1, def2)
  1101. X    long def1, def2;
  1102. X{
  1103. X    if (nlines == 0) {
  1104. X        line1 = def1;
  1105. X        line2 = def2;
  1106. X    }
  1107. X    if (line1 > line2 || 1 > line1 || line2 > lastln) {
  1108. X        sprintf(errmsg, "invalid address");
  1109. X        return ERR;
  1110. X    }
  1111. X    return 0;
  1112. X}
  1113. X
  1114. X
  1115. X/* patscan: return the number of the next line matching a pattern in a
  1116. X   given direction.  wrap around begin/end of line queue if necessary */
  1117. Xlong
  1118. Xpatscan(pat, dir)
  1119. X    pattern_t *pat;
  1120. X    int dir;
  1121. X{
  1122. X    char *s;
  1123. X    long n = curln;
  1124. X    line_t *lp;
  1125. X
  1126. X    do {
  1127. X        if (n = dir ? nextln(n, lastln) : prevln(n, lastln)) {
  1128. X            if ((s = gettxt(lp = getlp(n))) == NULL)
  1129. X                return ERR;
  1130. X            if (isbinary)
  1131. X                s = nultonl(s, lp->len & ~ACTV);
  1132. X            if (!regexec(pat, s, 0, NULL, 0))
  1133. X                return n;
  1134. X        }
  1135. X    } while (n != curln);
  1136. X    sprintf(errmsg, "no match");
  1137. X    return  ERR;
  1138. X}
  1139. X
  1140. X
  1141. X/* getfn: return pointer to copy of filename in the command buffer */
  1142. Xchar *
  1143. Xgetfn()
  1144. X{
  1145. X    static char *file = NULL;
  1146. X    static int filesz = 0;
  1147. X
  1148. X    int n;
  1149. X
  1150. X    if (*ibufp != '\n') {
  1151. X        skipblanks();
  1152. X        if (*ibufp == '\n') {
  1153. X            sprintf(errmsg, "invalid filename");
  1154. X            return NULL;
  1155. X        } else if ((ibufp = getcmdv(&n, 1)) == NULL)
  1156. X            return NULL;
  1157. X#ifdef VI_BANG
  1158. X        else if (*ibufp == '!') {
  1159. X            ibufp++;
  1160. X            if ((n = getshcmd()) < 0)
  1161. X                return NULL;
  1162. X            if (n) printf("%s\n", shcmd + 1);
  1163. X            return shcmd;
  1164. X        }
  1165. X#endif
  1166. X        else if (n - 1 > MAXFNAME) {
  1167. X            sprintf(errmsg, "filename too long");
  1168. X            return  NULL;
  1169. X        }
  1170. X    }
  1171. X#ifndef BACKWARDS
  1172. X    else if (*dfn == '\0') {
  1173. X        sprintf(errmsg, "no current filename");
  1174. X        return  NULL;
  1175. X    }
  1176. X#endif
  1177. X    CKBUF(file, filesz, MAXFNAME + 1, NULL);
  1178. X    for (n = 0; *ibufp != '\n';)
  1179. X        file[n++] = *ibufp++;
  1180. X    file[n] = '\0';
  1181. X    return ckfn(file);
  1182. X}
  1183. X
  1184. X
  1185. X/* getrhs: extract substitution template from the command buffer */
  1186. Xgetrhs(glob)
  1187. X    int glob;
  1188. X{
  1189. X    char delim;
  1190. X
  1191. X    if ((delim = *ibufp) == '\n') {
  1192. X        rhbufi = 0;
  1193. X        return GPR;
  1194. X    } else if (makesub(glob) == NULL)
  1195. X        return  ERR;
  1196. X    else if (*ibufp == '\n')
  1197. X        return GPR;
  1198. X    else if (*ibufp == delim)
  1199. X        ibufp++;
  1200. X    if ('1' <= *ibufp && *ibufp <= '9')
  1201. X        return (int) strtol(ibufp, &ibufp, 10) << 8;
  1202. X    else if (*ibufp == 'g') {
  1203. X        ibufp++;
  1204. X        return GSG;
  1205. X    }
  1206. X    return  0;
  1207. X}
  1208. X
  1209. X
  1210. X/* makesub: return pointer to copy of substitution template in the command
  1211. X   buffer */
  1212. Xchar *
  1213. Xmakesub(glob)
  1214. X    int glob;
  1215. X{
  1216. X    int n = 0;
  1217. X    int i = 0;
  1218. X    char delim = *ibufp++;
  1219. X    char c;
  1220. X
  1221. X    if (*ibufp == '%' && *(ibufp + 1) == delim) {
  1222. X        ibufp++;
  1223. X        if (!rhbuf) sprintf(errmsg, "no previous substitution");
  1224. X        return rhbuf;
  1225. X    }
  1226. X    while (*ibufp != delim) {
  1227. X        CKBUF(rhbuf, rhbufsz, i + 2, NULL);
  1228. X        if ((c = rhbuf[i++] = *ibufp++) == '\n' && *ibufp == '\0') {
  1229. X            i--, ibufp--;
  1230. X            break;
  1231. X        } else if (c != '\\')
  1232. X            ;
  1233. X        else if ((rhbuf[i++] = *ibufp++) != '\n')
  1234. X            ;
  1235. X        else if (!glob) {
  1236. X            while ((n = getline()) == 0
  1237. X                || n > 0 && ibuf[n - 1] != '\n')
  1238. X                clearerr(stdin);
  1239. X            if (n < 0)
  1240. X                return NULL;
  1241. X        } else
  1242. X            /*NOTREACHED*/
  1243. X            ;
  1244. X    }
  1245. X    CKBUF(rhbuf, rhbufsz, i + 1, NULL);
  1246. X    rhbuf[rhbufi = i] = '\0';
  1247. X    return  rhbuf;
  1248. X}
  1249. X
  1250. X
  1251. X/* getshcmd: read a shell command up a maximum size from stdin; return
  1252. X   substitution status */
  1253. Xint
  1254. Xgetshcmd()
  1255. X{
  1256. X    static char *buf = NULL;
  1257. X    static int n = 0;
  1258. X
  1259. X    char *s;            /* substitution char pointer */
  1260. X    int i = 0;
  1261. X    int j = 0;
  1262. X
  1263. X    if (red) {
  1264. X        sprintf(errmsg, "shell access restricted");
  1265. X        return ERR;
  1266. X    } else if ((s = ibufp = getcmdv(&j, 1)) == NULL)
  1267. X        return ERR;
  1268. X    CKBUF(buf, n, j + 1, ERR);
  1269. X    buf[i++] = '!';            /* prefix command w/ bang */
  1270. X    while (*ibufp != '\n')
  1271. X        switch (*ibufp) {
  1272. X        default:
  1273. X            CKBUF(buf, n, i + 2, ERR);
  1274. X            buf[i++] = *ibufp;
  1275. X            if (*ibufp++ == '\\')
  1276. X                buf[i++] = *ibufp++;
  1277. X            break;
  1278. X        case '!':
  1279. X            if (s != ibufp) {
  1280. X                CKBUF(buf, n, i + 1, ERR);
  1281. X                buf[i++] = *ibufp++;
  1282. X            }
  1283. X#ifdef BACKWARDS
  1284. X            else if (shcmd == NULL || *(shcmd + 1) == '\0')
  1285. X#else
  1286. X            else if (shcmd == NULL)
  1287. X#endif
  1288. X            {
  1289. X                sprintf(errmsg, "no previous command");
  1290. X                return ERR;
  1291. X            } else {
  1292. X                CKBUF(buf, n, i + shcmdi, ERR);
  1293. X                for (s = shcmd + 1; s < shcmd + shcmdi;)
  1294. X                    buf[i++] = *s++;
  1295. X                s = ibufp++;
  1296. X            }
  1297. X            break;
  1298. X        case '%':
  1299. X            if (*dfn  == '\0') {
  1300. X                sprintf(errmsg, "no current filename");
  1301. X                return ERR;
  1302. X            }
  1303. X            j = strlen(s = esctos(dfn));
  1304. X            CKBUF(buf, n, i + j, ERR);
  1305. X            while (j--)
  1306. X                buf[i++] = *s++;
  1307. X            s = ibufp++;
  1308. X            break;
  1309. X        }
  1310. X    CKBUF(shcmd, shcmdsz, i + 1, ERR);
  1311. X    memcpy(shcmd, buf, i);
  1312. X    shcmd[shcmdi = i] = '\0';
  1313. X    return *s == '!' || *s == '%';
  1314. X}
  1315. X
  1316. X
  1317. X/* append: insert text from stdin to after line n; stop when either a
  1318. X   single period is read or EOF; return status */
  1319. Xappend(n, glob)
  1320. X    long n;
  1321. X    int  glob;
  1322. X{
  1323. X    int l;
  1324. X    char *lp = ibuf;
  1325. X    char *eot;
  1326. X    undo_t *up = NULL;
  1327. X
  1328. X    for (curln = n;;) {
  1329. X        if (!glob) {
  1330. X            if ((l = getline()) < 0)
  1331. X                return ERR;
  1332. X            else if (l == 0 || ibuf[l - 1] != '\n') {
  1333. X                clearerr(stdin);
  1334. X                return  l ? EOF : 0;
  1335. X            }
  1336. X            lp = ibuf;
  1337. X        } else if (*(lp = ibufp) == '\0')
  1338. X            return 0;
  1339. X        else {
  1340. X            while (*ibufp++ != '\n')
  1341. X                ;
  1342. X            l = ibufp - lp;
  1343. X        }
  1344. X        if (l == 2 && lp[0] == '.' && lp[1] == '\n') {
  1345. X            return 0;
  1346. X        }
  1347. X        eot = lp + l;
  1348. X        spl1();
  1349. X        do {
  1350. X            if ((lp = puttxt(lp)) == NULL) {
  1351. X                spl0();
  1352. X                return ERR;
  1353. X            } else if (up)
  1354. X                up->t = getlp(curln);
  1355. X            else if ((up = upush(UADD, curln, curln)) == NULL) {
  1356. X                spl0();
  1357. X                return ERR;
  1358. X            }
  1359. X        } while (lp != eot);
  1360. X        spl0();
  1361. X        modified = 1;
  1362. X    }
  1363. X}
  1364. X
  1365. X
  1366. X#ifdef sun
  1367. X/* subst: change all text matching a pattern in a range of lines according to
  1368. X   a substitution template; return status  */
  1369. Xsubst(pat, gflag)
  1370. X    pattern_t *pat;
  1371. X    int gflag;
  1372. X{
  1373. X    undo_t *up = NULL;
  1374. X    char *txt;
  1375. X    char *eot;
  1376. X    line_t *bp, *ep, *np;
  1377. X    long ocl;
  1378. X    long nsubs = 0;
  1379. X    int len;
  1380. X
  1381. X    ep = getlp(curln = line2);
  1382. X    for (bp = getlp(line1); bp != ep->next; bp = bp->next)
  1383. X        if ((len = regsub(pat, bp, gflag)) < 0)
  1384. X            return ERR;
  1385. X        else if (!len) {
  1386. X            /* add copy of bp after current line - this avoids
  1387. X               overloading the undo structure, since only two
  1388. X               undo nodes are needed for the whole substitution;
  1389. X               the cost is high, but the less than if undo is
  1390. X               overloaded on a Sun evidently. XXX */
  1391. X            if ((np = lpdup(bp)) == NULL)
  1392. X                return ERR;
  1393. X            spl1();
  1394. X            lpqueue(np);
  1395. X            if (up)
  1396. X                up->t = getlp(curln);
  1397. X            else if ((up = upush(UADD, curln, curln)) == NULL) {
  1398. X                spl0();
  1399. X                return ERR;
  1400. X            }
  1401. X            spl0();
  1402. X        } else {
  1403. X            txt = rbuf;
  1404. X            eot = rbuf + len;
  1405. X            spl1();
  1406. X            do {
  1407. X                if ((txt = puttxt(txt)) == NULL) {
  1408. X                    spl0();
  1409. X                    return ERR;
  1410. X                } else if (up)
  1411. X                    up->t = getlp(curln);
  1412. X                else if ((up = upush(UADD, curln, curln)) == NULL) {
  1413. X                    spl0();
  1414. X                    return ERR;
  1415. X                }
  1416. X            } while (txt != eot);
  1417. X            spl0();
  1418. X            nsubs++;
  1419. X        }
  1420. X    ocl = curln;
  1421. X    lndelete(line1, line2);
  1422. X    curln = ocl - (line2 - line1 + 1);
  1423. X    if  (nsubs == 0 && !(gflag & GLB)) {
  1424. X        sprintf(errmsg, "no match");
  1425. X        return ERR;
  1426. X    } else if ((gflag & (GPR | GLS | GNP))
  1427. X     && doprint(curln, curln, gflag) < 0)
  1428. X        return ERR;
  1429. X    return 1;
  1430. X}
  1431. X#else    /* sun */
  1432. X
  1433. X
  1434. X/* subst: change all text matching a pattern in a range of lines according to
  1435. X   a substitution template; return status  */
  1436. Xsubst(pat, gflag)
  1437. X    pattern_t *pat;
  1438. X    int gflag;
  1439. X{
  1440. X    undo_t *up;
  1441. X    char *txt;
  1442. X    char *eot;
  1443. X    long lc;
  1444. X    int nsubs = 0;
  1445. X    line_t *lp;
  1446. X    int len;
  1447. X
  1448. X    curln = prevln(line1, lastln);
  1449. X    for (lc = 0; lc <= line2 - line1; lc++) {
  1450. X        lp = getlp(curln = nextln(curln, lastln));
  1451. X        if ((len = regsub(pat, lp, gflag)) < 0)
  1452. X            return ERR;
  1453. X        else if (len) {
  1454. X            up = NULL;
  1455. X            lndelete(curln, curln);
  1456. X            txt = rbuf;
  1457. X            eot = rbuf + len;
  1458. X            spl1();
  1459. X            do {
  1460. X                if ((txt = puttxt(txt)) == NULL) {
  1461. X                    spl0();
  1462. X                    return ERR;
  1463. X                } else if (up)
  1464. X                    up->t = getlp(curln);
  1465. X                else if ((up = upush(UADD, curln, curln)) == NULL) {
  1466. X                    spl0();
  1467. X                    return ERR;
  1468. X                }
  1469. X            } while (txt != eot);
  1470. X            spl0();
  1471. X            nsubs++;
  1472. X        }
  1473. X    }
  1474. X    if  (nsubs == 0 && !(gflag & GLB)) {
  1475. X        sprintf(errmsg, "no match");
  1476. X        return ERR;
  1477. X    } else if ((gflag & (GPR | GLS | GNP))
  1478. X     && doprint(curln, curln, gflag) < 0)
  1479. X        return ERR;
  1480. X    return 1;
  1481. X}
  1482. X#endif    /* sun */
  1483. X
  1484. X
  1485. X/* regsub: replace text matched by a pattern according to a substitution
  1486. X   template; return pointer to the modified text */
  1487. Xregsub(pat, lp, gflag)
  1488. X    pattern_t *pat;
  1489. X    line_t *lp;
  1490. X    int gflag;
  1491. X{
  1492. X    int off = 0;
  1493. X    int kth = gflag >> 8;        /* substitute kth match only */
  1494. X    int chngd = 0;
  1495. X    int matchno = 0;
  1496. X    int len;
  1497. X    int i = 0;
  1498. X    regmatch_t rm[SE_MAX];
  1499. X    char *txt;
  1500. X    char *eot;
  1501. X
  1502. X    if ((txt = gettxt(lp)) == NULL)
  1503. X        return ERR;
  1504. X    len = lp->len & ~ACTV;
  1505. X    eot = txt + len;
  1506. X    if (isbinary) txt = nultonl(txt, len);
  1507. X    if (!regexec(pat, txt, SE_MAX, rm, 0)) {
  1508. X        do {
  1509. X            if (!kth || kth == ++matchno) {
  1510. X                chngd++;
  1511. X                i = rm[0].rm_so;
  1512. X                CKBUF(rbuf, rbufsz, off + i, ERR);
  1513. X                if (isbinary) txt = nltonul(txt, rm[0].rm_eo);
  1514. X                memcpy(rbuf + off, txt, i);
  1515. X                if ((off = catsub(txt, rm, off += i)) < 0)
  1516. X                    return ERR;
  1517. X            } else {
  1518. X                i = rm[0].rm_eo;
  1519. X                CKBUF(rbuf, rbufsz, off + i, ERR);
  1520. X                if (isbinary) txt = nltonul(txt, i);
  1521. X                memcpy(rbuf + off, txt, i);
  1522. X                off += i;
  1523. X            }
  1524. X            txt += rm[0].rm_eo;
  1525. X        } while (*txt && (!chngd || (gflag & GSG) && rm[0].rm_eo)
  1526. X              && !regexec(pat, txt, SE_MAX, rm, REG_NOTBOL));
  1527. X        i = eot - txt;
  1528. X        CKBUF(rbuf, rbufsz, off + i + 2, ERR);
  1529. X        if (i > 0 && !rm[0].rm_eo && (gflag & GSG)) {
  1530. X            sprintf(errmsg, "infinite substitution loop");
  1531. X            return  ERR;
  1532. X        }
  1533. X        if (isbinary) txt = nltonul(txt, i);
  1534. X        memcpy(rbuf + off, txt, i);
  1535. X        memcpy(rbuf + off + i, "\n", 2);
  1536. X    }
  1537. X    return chngd ? off + i + 1 : 0;
  1538. X}
  1539. X
  1540. X
  1541. X/* join: replace a range of lines with the joined text of those lines */
  1542. Xjoin(from, to)
  1543. X    long from;
  1544. X    long to;
  1545. X{
  1546. X    static char *buf = NULL;
  1547. X    static int n;
  1548. X
  1549. X    char *s;
  1550. X    int len = 0;
  1551. X    int size = 0;
  1552. X    line_t *bp, *ep;
  1553. X
  1554. X    ep = getlp(nextln(to, lastln));
  1555. X    for (bp = getlp(from); bp != ep; bp = bp->next, size += len) {
  1556. X        if ((s = gettxt(bp)) == NULL)
  1557. X            return ERR;
  1558. X        len = bp->len & ~ACTV;
  1559. X        CKBUF(buf, n, size + len, ERR);
  1560. X        memcpy(buf + size, s, len);
  1561. X    }
  1562. X    CKBUF(buf, n, size + 2, ERR);
  1563. X    memcpy(buf + size, "\n", 2);
  1564. X    lndelete(from, to);
  1565. X    curln = from - 1;
  1566. X    spl1();
  1567. X    if (puttxt(buf) == NULL
  1568. X     || upush(UADD, curln, curln) == NULL) {
  1569. X        spl0();
  1570. X        return ERR;
  1571. X    }
  1572. X    spl0();
  1573. X    modified = 1;
  1574. X    return 0;
  1575. X}
  1576. X
  1577. X
  1578. X/* move: move a range of lines */
  1579. Xmove(num)
  1580. X    long num;
  1581. X{
  1582. X    line_t *b1, *a1, *b2, *a2;
  1583. X    long n = nextln(line2, lastln);
  1584. X    long p = prevln(line1, lastln);
  1585. X
  1586. X    spl1();
  1587. X    if (upush(UMOV, p, n) == NULL
  1588. X     || upush(UMOV, num, nextln(num, lastln)) == NULL) {
  1589. X         spl0();
  1590. X         return ERR;
  1591. X    }
  1592. X    a1 = getlp(n);
  1593. X    if (num < line1)
  1594. X        b1 = getlp(p), b2 = getlp(num);    /* this getlp last! */
  1595. X    else    b2 = getlp(num), b1 = getlp(p);    /* this getlp last! */
  1596. X    a2 = b2->next;
  1597. X    requeue(b2, b1->next);
  1598. X    requeue(a1->prev, a2);
  1599. X    requeue(b1, a1);
  1600. X    curln = num + ((num < line1) ? line2 - line1 + 1 : 0);
  1601. X    spl0();
  1602. X    return 0;
  1603. X}
  1604. X
  1605. X
  1606. X/* transfer: copy a range of lines; return status */
  1607. Xtransfer(num)
  1608. X    long num;
  1609. X{
  1610. X    line_t *lp;
  1611. X    long nl, nt, lc;
  1612. X    long mid = (num < line2) ? num : line2;
  1613. X    undo_t *up = NULL;
  1614. X
  1615. X    curln = num;
  1616. X    for (nt = 0, nl = line1; nl <= mid; nl++, nt++) {
  1617. X        spl1();
  1618. X        if ((lp = lpdup(getlp(nl))) == NULL) {
  1619. X            spl0();
  1620. X            return ERR;
  1621. X        }
  1622. X        lpqueue(lp);
  1623. X        if (up)
  1624. X            up->t = lp;
  1625. X        else if ((up = upush(UADD, curln, curln)) == NULL) {
  1626. X            spl0();
  1627. X            return ERR;
  1628. X        }
  1629. X        spl0();
  1630. X    }
  1631. X    for (nl += nt, lc = line2 + nt; nl <= lc; nl += 2, lc++) {
  1632. X        spl1();
  1633. X        if ((lp = lpdup(getlp(nl))) == NULL) {
  1634. X            spl0();
  1635. X            return ERR;
  1636. X        }
  1637. X        lpqueue(lp);
  1638. X        if (up)
  1639. X            up->t = lp;
  1640. X        else if ((up = upush(UADD, curln, curln)) == NULL) {
  1641. X            spl0();
  1642. X            return ERR;
  1643. X        }
  1644. X        spl0();
  1645. X    }
  1646. X    return 0;
  1647. X}
  1648. X
  1649. X
  1650. X/* lndelete: delete a range of lines */
  1651. Xlndelete(from, to)
  1652. X    long from, to;
  1653. X{
  1654. X    line_t *before, *after;
  1655. X
  1656. X    spl1();
  1657. X    if (upush(UDEL, from, to) == NULL) {
  1658. X        spl0();
  1659. X        return ERR;
  1660. X    }
  1661. X    after = getlp(nextln(to, lastln));
  1662. X    before = getlp(prevln(from, lastln));        /* this getlp last! */
  1663. X    requeue(before, after);
  1664. X    lastln -= to - from + 1;
  1665. X    curln = prevln(from, lastln);
  1666. X    spl0();
  1667. X    return 0;
  1668. X}
  1669. X
  1670. X
  1671. X/* catsub: modify text according to a substitution template;
  1672. X   return offset to end of modified text */
  1673. Xcatsub(boln, rm, off)
  1674. X    char *boln;
  1675. X    regmatch_t *rm;
  1676. X    int off;
  1677. X{
  1678. X    int j = 0;
  1679. X    int k = 0;
  1680. X    char *sub = rhbuf;
  1681. X
  1682. X    for (; sub - rhbuf < rhbufi; sub++)
  1683. X        if (*sub == '&') {
  1684. X            j = rm[0].rm_so;
  1685. X            k = rm[0].rm_eo;
  1686. X            CKBUF(rbuf, rbufsz, off + k - j, ERR);
  1687. X            while (j < k)
  1688. X                rbuf[off++] = boln[j++];
  1689. X        } else if (*sub == '\\' && '1' <= *++sub && *sub <= '9'
  1690. X              && rm[*sub - '0'].rm_so >= 0
  1691. X              && rm[*sub - '0'].rm_eo >= 0) {
  1692. X            j = rm[*sub - '0'].rm_so;
  1693. X            k = rm[*sub - '0'].rm_eo;
  1694. X            CKBUF(rbuf, rbufsz, off + k - j, ERR);
  1695. X            while (j < k)
  1696. X                rbuf[off++] = boln[j++];
  1697. X        } else {
  1698. X            CKBUF(rbuf, rbufsz, off + 1, ERR);
  1699. X            rbuf[off++] = *sub;
  1700. X        }
  1701. X    CKBUF(rbuf, rbufsz, off + 1, ERR);
  1702. X    rbuf[off] = '\0';
  1703. X    return off;
  1704. X}
  1705. X
  1706. X/* doprint: print a range of lines to stdout */
  1707. Xdoprint(from, to, gflag)
  1708. X    long from;
  1709. X    long to;
  1710. X    int gflag;
  1711. X{
  1712. X    line_t *bp;
  1713. X    line_t *ep;
  1714. X    char *s;
  1715. X
  1716. X    if (!from) {
  1717. X        sprintf(errmsg, "invalid address");
  1718. X        return ERR;
  1719. X    }
  1720. X    ep = getlp(nextln(to, lastln));
  1721. X    for (bp = getlp(from); bp != ep; bp = bp->next) {
  1722. X        if ((s = gettxt(bp)) == NULL)
  1723. X            return ERR;
  1724. X        putstr(s, bp->len & ~ACTV, curln = from++, gflag);
  1725. X    }
  1726. X    return 0;
  1727. X}
  1728. X
  1729. X
  1730. Xint cols = 72;        /* wrap column: ws_col - 8 */
  1731. X
  1732. X/* putstr: print text to stdout */
  1733. Xvoid
  1734. Xputstr(s, l, n, gflag)
  1735. X    char *s;
  1736. X    int l;
  1737. X    long n;
  1738. X    int gflag;
  1739. X{
  1740. X    int col = 0;
  1741. X
  1742. X    if (gflag & GNP) {
  1743. X        printf("%ld\t", n);
  1744. X        col = 8;
  1745. X    }
  1746. X    for (; l--; s++) {
  1747. X        if ((gflag & GLS) && ++col > cols) {
  1748. X            fputs("\\\n", stdout);
  1749. X            col = 1;
  1750. X        }
  1751. X        if (gflag & GLS) {
  1752. X            switch (*s) {
  1753. X            case '\b':
  1754. X                fputs("\\b", stdout);
  1755. X                break;
  1756. X            case '\f':
  1757. X                fputs("\\f", stdout);
  1758. X                break;
  1759. X            case '\n':
  1760. X                fputs("\\n", stdout);
  1761. X                break;
  1762. X            case '\r':
  1763. X                fputs("\\r", stdout);
  1764. X                break;
  1765. X            case '\t':
  1766. X                fputs("\\t", stdout);
  1767. X                break;
  1768. X            case '\v':
  1769. X                fputs("\\v", stdout);
  1770. X                break;
  1771. X            default:
  1772. X                if (*s < 32 || 126 < *s) {
  1773. X                    putchar('\\');
  1774. X                    putchar((((unsigned char) *s & 0300) >> 6) + '0');
  1775. X                    putchar((((unsigned char) *s & 070) >> 3) + '0');
  1776. X                    putchar(((unsigned char) *s & 07) + '0');
  1777. X                    col += 2;
  1778. X                } else if (*s == '\\')
  1779. X                    fputs("\\\\", stdout);
  1780. X                else {
  1781. X                    putchar(*s);
  1782. X                    col--;
  1783. X                }
  1784. X            }
  1785. X            col++;
  1786. X        } else
  1787. X            putchar(*s);
  1788. X    }
  1789. X#ifndef BACKWARDS
  1790. X    if (gflag & GLS)
  1791. X        putchar('$');
  1792. X#endif
  1793. X    putchar('\n');
  1794. X}
  1795. X
  1796. X
  1797. Xint newline_added;        /* set if newline appended to input file */
  1798. X
  1799. X/* doread: read a text file into the editor buffer; return line count */
  1800. Xlong
  1801. Xdoread(n, fn)
  1802. X    long n;
  1803. X    char *fn;
  1804. X{
  1805. X    FILE *fp;
  1806. X    line_t *lp = getlp(n);
  1807. X    unsigned long size = 0;
  1808. X    undo_t *up = NULL;
  1809. X    int len;
  1810. X
  1811. X    isbinary = newline_added = 0;
  1812. X    if ((fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(esctos(fn), "r")) == NULL) {
  1813. X        fprintf(stderr, "%s: %s\n", fn, strerror(errno));
  1814. X        sprintf(errmsg, "cannot open input file");
  1815. X        return ERR;
  1816. X    } else if (des)
  1817. X        desinit();
  1818. X    for (curln = n; (len = sgetline(fp)) > 0; size += len) {
  1819. X        spl1();
  1820. X        if (puttxt(sbuf) == NULL) {
  1821. X            spl0();
  1822. X            return ERR;
  1823. X        }
  1824. X        lp = lp->next;
  1825. X        if (up)
  1826. X            up->t = lp;
  1827. X        else if ((up = upush(UADD, curln, curln)) == NULL) {
  1828. X            spl0();
  1829. X            return ERR;
  1830. X        }
  1831. X        spl0();
  1832. X    }
  1833. X    if (((*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
  1834. X        fprintf(stderr, "%s: %s\n", fn, strerror(errno));
  1835. X        sprintf(errmsg, "cannot close input file");
  1836. X        return ERR;
  1837. X    }
  1838. X    if (newline_added && !isbinary)
  1839. X        fputs("newline appended\n", stderr);
  1840. X    if (des) size += 8 - size % 8;
  1841. X    fprintf(stderr, !scripted ? "%lu\n" : "", size);
  1842. X    return  (len < 0) ? ERR : curln - n;
  1843. X}
  1844. X
  1845. X
  1846. X/* dowrite: write the text of a range of lines to a file; return line count */
  1847. Xlong
  1848. Xdowrite(n, m, fn, mode)
  1849. X    long n;
  1850. X    long m;
  1851. X    char *fn;
  1852. X    char *mode;
  1853. X{
  1854. X    FILE *fp;
  1855. X    line_t *lp;
  1856. X    unsigned long size = 0;
  1857. X    long lc = n ? m - n + 1 : 0;
  1858. X    char *s = NULL;
  1859. X    int len;
  1860. X    int ct;
  1861. X
  1862. X    if ((fp = ((*fn == '!') ? popen(fn + 1, "w") : fopen(esctos(fn), mode))) == NULL) {
  1863. X        fprintf(stderr, "%s: %s\n", fn, strerror(errno));
  1864. X        sprintf(errmsg, "cannot open output file");
  1865. X        return ERR;
  1866. X    } else if (des)
  1867. X        desinit();
  1868. X    if (n && !des)
  1869. X        for (lp = getlp(n); n <= m; n++, lp = lp->next) {
  1870. X            if ((s = gettxt(lp)) == NULL)
  1871. X                return ERR;
  1872. X            len = lp->len & ~ACTV;
  1873. X            if (n != lastln || !isbinary || !newline_added)
  1874. X                s[len++] = '\n';
  1875. X            if ((ct = fwrite(s, sizeof(char), len, fp)) < 0 || ct != len) {
  1876. X                fprintf(stderr, "%s: %s\n", fn, strerror(errno));
  1877. X                sprintf(errmsg, "cannot write file");
  1878. X                return ERR;
  1879. X            }
  1880. X            size += len;
  1881. X        }
  1882. X    else if (n)
  1883. X        for (lp = getlp(n); n <= m; n++, lp = lp->next) {
  1884. X            if ((s = gettxt(lp)) == NULL)
  1885. X                return ERR;
  1886. X            len = lp->len & ~ACTV;
  1887. X            while (len--) {
  1888. X                if (desputc(*s++, fp) == EOF && ferror(fp)) {
  1889. X                    fprintf(stderr, "%s: %s\n", fn, strerror(errno));
  1890. X                    sprintf(errmsg, "cannot write file");
  1891. X                    return ERR;
  1892. X                }
  1893. X            }
  1894. X            if (n != lastln || !isbinary || !newline_added) {
  1895. X                if (desputc('\n', fp) < 0) {
  1896. X                    fprintf(stderr, "%s: %s\n", fn, strerror(errno));
  1897. X                    sprintf(errmsg, "cannot write file");
  1898. X                    return ERR;
  1899. X                }
  1900. X                size++;            /* for '\n' */
  1901. X            }
  1902. X            size += (lp->len & ~ACTV);
  1903. X        }
  1904. X    if (des) {
  1905. X        desflush(fp);                /* flush buffer */
  1906. X        size += 8 - size % 8;            /* adjust DES size */
  1907. X    }
  1908. X    if (((*fn == '!') ?  pclose(fp) : fclose(fp)) < 0) {
  1909. X        fprintf(stderr, "%s: %s\n", fn, strerror(errno));
  1910. X        sprintf(errmsg, "cannot close output file");
  1911. X        return ERR;
  1912. X    }
  1913. X    fprintf(stderr, !scripted ? "%lu\n" : "", size);
  1914. X    return  lc;
  1915. X}
  1916. X
  1917. X
  1918. X#define USIZE 100                /* undo stack size */
  1919. Xundo_t *ustack = NULL;                /* undo stack */
  1920. Xlong usize = 0;                    /* stack size variable */
  1921. Xlong u_p = 0;                    /* undo stack pointer */
  1922. X
  1923. X
  1924. X/* upush: return pointer to intialized undo node */
  1925. Xundo_t *
  1926. Xupush(type, from, to)
  1927. X    int type;
  1928. X    long from;
  1929. X    long to;
  1930. X{
  1931. X    undo_t *t;
  1932. X
  1933. X#if defined(sun) || defined(NO_REALLOC_NULL)
  1934. X    if (ustack == NULL
  1935. X     && (ustack = (undo_t *) malloc((usize = USIZE) * sizeof(undo_t))) == NULL) {
  1936. X        fprintf(stderr, "%s\n", strerror(errno));
  1937. X        sprintf(errmsg, "out of memory");
  1938. X        return NULL;
  1939. X    }
  1940. X#endif
  1941. X    t = ustack;
  1942. X    if (u_p < usize
  1943. X     || (t = (undo_t *) realloc(ustack, (usize += USIZE) * sizeof(undo_t))) != NULL) {
  1944. X        ustack = t;
  1945. X        ustack[u_p].type = type;
  1946. X        ustack[u_p].t = getlp(to);
  1947. X        ustack[u_p].h = getlp(from);
  1948. X        return ustack + u_p++;
  1949. X    }
  1950. X    /* out of memory - release undo stack */
  1951. X    fprintf(stderr, "%s\n", strerror(errno));
  1952. X    sprintf(errmsg, "out of memory");
  1953. X    ureset();
  1954. X    free(ustack);
  1955. X    ustack = NULL;
  1956. X    usize = 0;
  1957. X    return NULL;
  1958. X}
  1959. X
  1960. X/* undo: undo last change to the editor buffer */
  1961. Xundo()
  1962. X{
  1963. X    long n = usw ? 0 : u_p - 1;
  1964. X    int i = usw ? 1 : -1;
  1965. X    long j = u_p;
  1966. X    long ocurln = curln;
  1967. X    long olastln = lastln;
  1968. X
  1969. X    if (ucurln == -1 || ulastln == -1) {
  1970. X        sprintf(errmsg, "nothing to undo");
  1971. X        return ERR;
  1972. X    } else if (u_p)
  1973. X        modified = 1;
  1974. X    getlp(0);                /* this getlp last! */
  1975. X    spl1();
  1976. X    for (; j-- > 0; n += i)
  1977. X        switch(ustack[n].type ^ usw) {
  1978. X        case UADD:
  1979. X            requeue(ustack[n].h->prev, ustack[n].t->next);
  1980. X            break;
  1981. X        case UDEL:
  1982. X            requeue(ustack[n].h->prev, ustack[n].h);
  1983. X            requeue(ustack[n].t, ustack[n].t->next);
  1984. X            break;
  1985. X        case UMOV:
  1986. X        case VMOV:
  1987. X            requeue(ustack[n + i].h, ustack[n].h->next);
  1988. X            requeue(ustack[n].t->prev, ustack[n + i].t);
  1989. X            requeue(ustack[n].h, ustack[n].t);
  1990. X            n += i, j--;
  1991. X            break;
  1992. X        default:
  1993. X            /*NOTREACHED*/
  1994. X            ;
  1995. X        }
  1996. X    usw = 1 - usw;
  1997. X    curln = ucurln, ucurln = ocurln;
  1998. X    lastln = ulastln, ulastln = olastln;
  1999. X    spl0();
  2000. X    return 0;
  2001. X}
  2002. X
  2003. X
  2004. X/* ureset: clear the undo stack */
  2005. Xvoid
  2006. Xureset()
  2007. X{
  2008. X    line_t *lp, *ep, *tl;
  2009. X    int i;
  2010. X
  2011. X    while (u_p--)
  2012. X        if ((ustack[u_p].type ^ usw) == UDEL) {
  2013. X            ep = ustack[u_p].t->next;
  2014. X            for (lp = ustack[u_p].h; lp != ep; lp = tl) {
  2015. X                if (markno)
  2016. X                    for (i = 0; i < MAXMARK; i++)
  2017. X                        if (mark[i] == lp) {
  2018. X                            mark[i] = NULL;
  2019. X                            markno--;
  2020. X                        }
  2021. X                tl = lp->next;
  2022. X                free(lp);
  2023. X            }
  2024. X        }
  2025. X    u_p = usw = 0;
  2026. X    ucurln = curln;
  2027. X    ulastln = lastln;
  2028. X}
  2029. X
  2030. X
  2031. X
  2032. X/* sgetline: read a line of text up a maximum size from a file; return
  2033. X   line length */
  2034. Xsgetline(fp)
  2035. X    FILE *fp;
  2036. X{
  2037. X    register int c;
  2038. X    register int i = 0;
  2039. X
  2040. X    while (((c = des ? desgetc(fp) : getc(fp)) != EOF || !feof(fp) && !ferror(fp)) && c != '\n') {
  2041. X        CKBUF(sbuf, sbufsz, i + 1, ERR);
  2042. X        if (!(sbuf[i++] = c)) isbinary = 1;
  2043. X    }
  2044. X    CKBUF(sbuf, sbufsz, i + 2, ERR);
  2045. X    if (c == '\n')
  2046. X        sbuf[i++] = c;
  2047. X    else if (feof(fp) && i) {
  2048. X        sbuf[i++] = '\n';
  2049. X        newline_added = 1;
  2050. X    } else if (ferror(fp)) {
  2051. X        fprintf(stderr, "%s\n", strerror(errno));
  2052. X        sprintf(errmsg, "cannot read input file");
  2053. X        return ERR;
  2054. X    }
  2055. X    sbuf[i] = '\0';
  2056. X    return (isbinary && newline_added && i) ? --i : i;
  2057. X}
  2058. X
  2059. X
  2060. X/* getline: read a line of text up a maximum size from stdin; return
  2061. X   line length */
  2062. Xgetline()
  2063. X{
  2064. X    register int i = 0;
  2065. X    register int oi = 0;
  2066. X    char c;
  2067. X
  2068. X    /* Read one character at a time to avoid i/o contention with shell
  2069. X       escapes invoked by nonterminal input, e.g.,
  2070. X       ed - <<EOF
  2071. X       !cat
  2072. X       hello, world
  2073. X       EOF */
  2074. X    for (;;)
  2075. X        switch (read(0, &c, 1)) {
  2076. X        default:
  2077. X            oi = 0;
  2078. X            CKBUF(ibuf, ibufsz, i + 2, ERR);
  2079. X            if (!(ibuf[i++] = c)) isbinary = 1;
  2080. X            if (c != '\n')
  2081. X                continue;
  2082. X            lineno++;        /* script line no. */
  2083. X            ibuf[i] = '\0';
  2084. X            ibufp = ibuf;
  2085. X            return i;
  2086. X        case 0:
  2087. X            if (i != oi) {
  2088. X                oi = i;
  2089. X                continue;
  2090. X            } else if (i)
  2091. X                ibuf[i] = '\0';
  2092. X            ibufp = ibuf;
  2093. X            return i;
  2094. X        case -1:
  2095. X            fprintf(stderr, "%s\n", strerror(errno));
  2096. X            sprintf(errmsg, "cannot read standard input");
  2097. X            clearerr(stdin);
  2098. X            ibufp = NULL;
  2099. X            return ERR;
  2100. X        }
  2101. X}
  2102. X
  2103. X
  2104. X/* getcmdv: get a command vector */
  2105. Xchar *
  2106. Xgetcmdv(sizep, nonl)
  2107. X    int *sizep;
  2108. X    int nonl;
  2109. X{
  2110. X    int l, n;
  2111. X    char *t = ibufp;
  2112. X
  2113. X    while (*t++ != '\n')
  2114. X        ;
  2115. X    if ((l = t - ibufp) < 2 || !oddesc(ibufp, ibufp + l - 1)) {
  2116. X        *sizep = l;
  2117. X        return ibufp;
  2118. X    }
  2119. X    *sizep = -1;
  2120. X    CKBUF(cvbuf, cvbufsz, l, NULL);
  2121. X    memcpy(cvbuf, ibufp, l);
  2122. X    *(cvbuf + --l - 1) = '\n';     /* strip trailing esc */
  2123. X    if (nonl) l--;             /* strip newline */
  2124. X    for (;;) {
  2125. X        if ((n = getline()) < 0)
  2126. X            return NULL;
  2127. X        else if (n == 0 || ibuf[n - 1] != '\n') {
  2128. X            sprintf(errmsg, "unexpected end-of-file");
  2129. X            return NULL;
  2130. X        }
  2131. X        CKBUF(cvbuf, cvbufsz, l + n, NULL);
  2132. X        memcpy(cvbuf + l, ibuf, n);
  2133. X        if (n < 2 || !oddesc(cvbuf, cvbuf + (l += n) - 1))
  2134. X            break;
  2135. X        *(cvbuf + --l - 1) = '\n';     /* strip trailing esc */
  2136. X        if (nonl) l--;             /* strip newline */
  2137. X    }
  2138. X    CKBUF(cvbuf, cvbufsz, l + 1, NULL);
  2139. X    cvbuf[l] = '\0';
  2140. X    *sizep = l;
  2141. X    return cvbuf;
  2142. X}
  2143. X
  2144. X
  2145. X/* lpdup: return a pointer to a copy of a line node */
  2146. Xline_t *
  2147. Xlpdup(lp)
  2148. X    line_t *lp;
  2149. X{
  2150. X    line_t *np;
  2151. X
  2152. X    if ((np = (line_t *) malloc(sizeof(line_t))) == NULL) {
  2153. X        fprintf(stderr, "%s\n", strerror(errno));
  2154. X        sprintf(errmsg, "out of memory");
  2155. X        return NULL;
  2156. X    }
  2157. X    np->seek = lp->seek;
  2158. X    np->len = (lp->len & ~ACTV);    /* zero ACTV bit */
  2159. X    return np;
  2160. X}
  2161. X
  2162. X
  2163. X/* oddesc:  return the parity of escapes preceding a character in a
  2164. X   string */
  2165. Xoddesc(s, t)
  2166. X    char *s;
  2167. X    char *t;
  2168. X{
  2169. X    return (s == t || *(t - 1) != '\\') ? 0 : !oddesc(s, t - 1);
  2170. X}
  2171. X
  2172. X
  2173. X/* esctos: return copy of escaped string */
  2174. Xchar *
  2175. Xesctos(s)
  2176. X    char *s;
  2177. X{
  2178. X    static char *file = NULL;
  2179. X    static int filesz = 0;
  2180. X
  2181. X    int i = 0;
  2182. X
  2183. X    CKBUF(file, filesz, MAXFNAME + 1, NULL);
  2184. X    /* assert: no trailing escape */
  2185. X    while (file[i++] = (*s == '\\') ? *++s : *s)
  2186. X        s++;
  2187. X    return file;
  2188. X}
  2189. X
  2190. X
  2191. Xvoid
  2192. Xonhup(signo)
  2193. X    int signo;
  2194. X{
  2195. X    if (mutex)
  2196. X        sigflags |= (1 << signo);
  2197. X    else    dohup(signo);
  2198. X}
  2199. X
  2200. X
  2201. Xvoid
  2202. Xonintr(signo)
  2203. X    int signo;
  2204. X{
  2205. X    if (mutex)
  2206. X        sigflags |= (1 << signo);
  2207. X    else    dointr(signo);
  2208. X}
  2209. X
  2210. X
  2211. X
  2212. Xvoid
  2213. Xdohup(signo)
  2214. X    int signo;
  2215. X{
  2216. X    char *hup = NULL;        /* hup filename */
  2217. X    char *s;
  2218. X    int n;
  2219. X
  2220. X    if (!sigactive)
  2221. X        quit(1);
  2222. X    sigflags &= ~(1 << signo);
  2223. X    if (lastln && dowrite(1, lastln, "ed.hup", "w") < 0
  2224. X     && (s = getenv("HOME")) != NULL
  2225. X     && (n = strlen(s)) + 8 <= MAXFNAME    /* "ed.hup" + '/' */
  2226. X     && (hup = (char *) malloc(n + 10)) != NULL) {
  2227. X        strcpy(hup, s);
  2228. X        if (hup[n - 1] != '/')
  2229. X            hup[n] = '/', hup[n+1] = '\0';
  2230. X        strcat(hup, "ed.hup");
  2231. X        dowrite(1, lastln, hup, "w");
  2232. X    }
  2233. X    quit(2);
  2234. X}
  2235. X
  2236. X
  2237. Xvoid
  2238. Xdointr(signo)
  2239. X    int signo;
  2240. X{
  2241. X    if (!sigactive)
  2242. X        quit(1);
  2243. X    sigflags &= ~(1 << signo);
  2244. X#ifdef _POSIX_SOURCE
  2245. X    siglongjmp(env, -1);
  2246. X#else
  2247. X    longjmp(env, -1);
  2248. X#endif
  2249. X}
  2250. X
  2251. X
  2252. Xstruct winsize ws;        /* window size structure */
  2253. X
  2254. Xvoid
  2255. Xdowinch(signo)
  2256. X    int signo;
  2257. X{
  2258. X    sigflags &= ~(1 << signo);
  2259. X    if (ioctl(0, TIOCGWINSZ, (char *) &ws) >= 0) {
  2260. X        if (ws.ws_row > 2) rows = ws.ws_row - 2;
  2261. X        if (ws.ws_col > 8) cols = ws.ws_col - 8;
  2262. X    }
  2263. X}
  2264. X
  2265. X
  2266. Xunsigned char ctab[256];        /* character translation table */
  2267. X
  2268. X/* translit: translate characters in a string */
  2269. Xchar *
  2270. Xtranslit(s, len, from, to)
  2271. X    char *s;
  2272. X    int len;
  2273. X    int from;
  2274. X    int to;
  2275. X{
  2276. X    static int i = 0;
  2277. X
  2278. X    unsigned char *us;
  2279. X
  2280. X    ctab[i] = i;            /* restore table to initial state */
  2281. X    ctab[i = from] = to;
  2282. X    for (us = (unsigned char *) s; len-- > 0; us++)
  2283. X        *us = ctab[*us];
  2284. X    return s;
  2285. X}
  2286. X
  2287. X
  2288. Xline_t line0;            /* initial node of line queue */
  2289. X
  2290. X/* init_buf: open scratch buffer; initialize line queue */
  2291. Xvoid
  2292. Xinit_buf()
  2293. X{
  2294. X    int i = 0;
  2295. X
  2296. X    if (sbopen() < 0)
  2297. X        quit(2);
  2298. X    requeue(&line0, &line0);
  2299. X    for (i = 0; i < 256; i++)
  2300. X        ctab[i] = i;
  2301. X}
  2302. X
  2303. X
  2304. X/* ckfn: return a legal filename */
  2305. Xchar *
  2306. Xckfn(s)
  2307. X    char *s;
  2308. X{
  2309. X    if (red && (*s == '!' || !strcmp(s, "..") || strchr(s, '/'))) {
  2310. X        sprintf(errmsg, "shell access restricted");
  2311. X        return NULL;
  2312. X    }
  2313. X    return s;
  2314. X}
  2315. END_OF_FILE
  2316.   if test 47458 -ne `wc -c <'ed.c'`; then
  2317.     echo shar: \"'ed.c'\" unpacked with wrong size!
  2318.   fi
  2319.   # end of 'ed.c'
  2320. fi
  2321. echo shar: End of archive 1 \(of 3\).
  2322. cp /dev/null ark1isdone
  2323. MISSING=""
  2324. for I in 1 2 3 ; do
  2325.     if test ! -f ark${I}isdone ; then
  2326.     MISSING="${MISSING} ${I}"
  2327.     fi
  2328. done
  2329. if test "${MISSING}" = "" ; then
  2330.     echo You have unpacked all 3 archives.
  2331.     rm -f ark[1-9]isdone
  2332. else
  2333.     echo You still must unpack the following archives:
  2334.     echo "        " ${MISSING}
  2335. fi
  2336. exit 0
  2337. exit 0 # Just in case...
  2338.