🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.sys.atari.st
Article #24808

Uemail source (11 of 12)

#24808
From: RDROYA01
Date: Fri, 06 Feb 1987 16:40
1948 lines
65971 bytes
#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       line.c
#       word.c
#       search.c
#       wc.c
# This archive created: Tue Feb  3 17:57:11 1987
cat << \SHAR_EOF > line.c
/*
 * The functions in this file
 * are a general set of line management
 * utilities. They are the only routines that
 * touch the text. They also touch the buffer
 * and window structures, to make sure that the
 * necessary updating gets done. There are routines
 * in this file that handle the kill buffer too.
 * It isn't here for any good reason.
 *
 * Note that this code only updates the dot and
 * mark values in the window list. Since all the code
 * acts on the current window, the buffer that we
 * are editing must be being displayed, which means
 * that "b_nwnd" is non zero, which means that the
 * dot and mark values in the buffer headers are
 * nonsense.
 */
#include        <stdio.h>
#include        "ed.h"
 
#define NBLOCK  16                      /* Line block chunk size        */
#define KBLOCK  256                     /* Kill buffer block size       */
 
char    *kbufp  = NULL;                 /* Kill buffer data             */
int     kused   = 0;                    /* # of bytes used in KB        */
int     ksize   = 0;                    /* # of bytes allocated in KB   */
 
/*
 * This routine allocates a block
 * of memory large enough to hold a LINE
 * containing "used" characters. The block is
 * always rounded up a bit. Return a pointer
 * to the new block, or NULL if there isn't
 * any memory left. Print a message in the
 * message line if no space.
 */
LINE    *
lalloc(used)
register int    used;
{
        register LINE   *lp;
        register int    size;
 
        size = (used+NBLOCK-1) & ~(NBLOCK-1);
        if (size == 0)                          /* Assume that an empty */
                size = NBLOCK;                  /* line is for type-in. */
        if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
                mlwrite("Cannot allocate %d bytes", size);
                return (NULL);
        }
        lp->l_size = size;
        lp->l_used = used;
        return (lp);
}
 
/*
 * Delete line "lp". Fix all of the
 * links that might point at it (they are
 * moved to offset 0 of the next line.
 * Unlink the line from whatever buffer it
 * might be in. Release the memory. The
 * buffers are updated too; the magic conditions
 * described in the above comments don't hold
 * here.
 */
lfree(lp)
register LINE   *lp;
{
        register BUFFER *bp;
        register WINDOW *wp;
 
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_linep == lp)
                        wp->w_linep = lp->l_fp;
                if (wp->w_dotp  == lp) {
                        wp->w_dotp  = lp->l_fp;
                        wp->w_doto  = 0;
                }
                if (wp->w_markp == lp) {
                        wp->w_markp = lp->l_fp;
                        wp->w_marko = 0;
                }
                wp = wp->w_wndp;
        }
        bp = bheadp;
        while (bp != NULL) {
                if (bp->b_nwnd == 0) {
                        if (bp->b_dotp  == lp) {
                                bp->b_dotp = lp->l_fp;
                                bp->b_doto = 0;
                        }
                        if (bp->b_markp == lp) {
                                bp->b_markp = lp->l_fp;
                                bp->b_marko = 0;
                        }
                }
                bp = bp->b_bufp;
        }
        lp->l_bp->l_fp = lp->l_fp;
        lp->l_fp->l_bp = lp->l_bp;
        free((char *) lp);
}
 
/*
 * This routine gets called when
 * a character is changed in place in the
 * current buffer. It updates all of the required
 * flags in the buffer and window system. The flag
 * used is passed as an argument; if the buffer is being
 * displayed in more than 1 window we change EDIT to
 * HARD. Set MODE if the mode line needs to be
 * updated (the "*" has to be set).
 */
lchange(flag)
register int    flag;
{
        register WINDOW *wp;
 
        if (curbp->b_nwnd != 1)                 /* Ensure hard.         */
                flag = WFHARD;
        if ((curbp->b_flag&BFCHG) == 0) {       /* First change, so     */
                flag |= WFMODE;                 /* update mode lines.   */
                curbp->b_flag |= BFCHG;
        }
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_bufp == curbp)
                        wp->w_flag |= flag;
                wp = wp->w_wndp;
        }
}
 
/*
 * Insert "n" copies of the character "c"
 * at the current location of dot. In the easy case
 * all that happens is the text is stored in the line.
 * In the hard case, the line has to be reallocated.
 * When the window list is updated, take special
 * care; I screwed it up once. You always update dot
 * in the current window. You update mark, and a
 * dot in another window, if it is greater than
 * the place where you did the insert. Return TRUE
 * if all is well, and FALSE on errors.
 */
linsert(n, c)
register int n, c;
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *lp1;
        register LINE   *lp2;
        register LINE   *lp3;
        register int    doto;
        register int    i;
        register WINDOW *wp;
 
        lchange(WFEDIT);
        lp1 = curwp->w_dotp;                    /* Current line         */
        if (lp1 == curbp->b_linep) {            /* At the end: special  */
                if (curwp->w_doto != 0) {
                        mlwrite("bug: linsert");
                        return (FALSE);
                }
                if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
                        return (FALSE);
                lp3 = lp1->l_bp;                /* Previous line        */
                lp3->l_fp = lp2;                /* Link in              */
                lp2->l_fp = lp1;
                lp1->l_bp = lp2;
                lp2->l_bp = lp3;
                for (i=0; i<n; ++i)
                        lp2->l_text[i] = c;
                curwp->w_dotp = lp2;
                curwp->w_doto = n;
                return (TRUE);
        }
        doto = curwp->w_doto;                   /* Save for later.      */
        if (lp1->l_used+n > lp1->l_size) {      /* Hard: reallocate     */
                if ((lp2=lalloc(lp1->l_used+n)) == NULL)
                        return (FALSE);
                cp1 = &lp1->l_text[0];
                cp2 = &lp2->l_text[0];
                while (cp1 != &lp1->l_text[doto])
                        *cp2++ = *cp1++;
                cp2 += n;
                while (cp1 != &lp1->l_text[lp1->l_used])
                        *cp2++ = *cp1++;
                lp1->l_bp->l_fp = lp2;
                lp2->l_fp = lp1->l_fp;
                lp1->l_fp->l_bp = lp2;
                lp2->l_bp = lp1->l_bp;
                free((char *) lp1);
        } else {                                /* Easy: in place       */
                lp2 = lp1;                      /* Pretend new line     */
                lp2->l_used += n;
                cp2 = &lp1->l_text[lp1->l_used];
                cp1 = cp2-n;
                while (cp1 != &lp1->l_text[doto])
                        *--cp2 = *--cp1;
        }
        for (i=0; i<n; ++i)                     /* Add the characters   */
                lp2->l_text[doto+i] = c;
        wp = wheadp;                            /* Update windows       */
        while (wp != NULL) {
                if (wp->w_linep == lp1)
                        wp->w_linep = lp2;
                if (wp->w_dotp == lp1) {
                        wp->w_dotp = lp2;
                        if (wp==curwp || wp->w_doto>doto)
                                wp->w_doto += n;
                }
                if (wp->w_markp == lp1) {
                        wp->w_markp = lp2;
                        if (wp->w_marko > doto)
                                wp->w_marko += n;
                }
                wp = wp->w_wndp;
        }
        return (TRUE);
}
 
/*
 * Insert a newline into the buffer
 * at the current location of dot in the current
 * window. The funny ass-backwards way it does things
 * is not a botch; it just makes the last line in
 * the file not a special case. Return TRUE if everything
 * works out and FALSE on error (memory allocation
 * failure). The update of dot and mark is a bit
 * easier then in the above case, because the split
 * forces more updating.
 */
lnewline()
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *lp1;
        register LINE   *lp2;
        register int    doto;
        register WINDOW *wp;
 
        lchange(WFHARD);
        lp1  = curwp->w_dotp;                   /* Get the address and  */
        doto = curwp->w_doto;                   /* offset of "."        */
        if ((lp2=lalloc(doto)) == NULL)         /* New first half line  */
                return (FALSE);
        cp1 = &lp1->l_text[0];                  /* Shuffle text around  */
        cp2 = &lp2->l_text[0];
        while (cp1 != &lp1->l_text[doto])
                *cp2++ = *cp1++;
        cp2 = &lp1->l_text[0];
        while (cp1 != &lp1->l_text[lp1->l_used])
                *cp2++ = *cp1++;
        lp1->l_used -= doto;
        lp2->l_bp = lp1->l_bp;
        lp1->l_bp = lp2;
        lp2->l_bp->l_fp = lp2;
        lp2->l_fp = lp1;
        wp = wheadp;                            /* Windows              */
        while (wp != NULL) {
                if (wp->w_linep == lp1)
                        wp->w_linep = lp2;
                if (wp->w_dotp == lp1) {
                        if (wp->w_doto < doto)
                                wp->w_dotp = lp2;
                        else
                                wp->w_doto -= doto;
                }
                if (wp->w_markp == lp1) {
                        if (wp->w_marko < doto)
                                wp->w_markp = lp2;
                        else
                                wp->w_marko -= doto;
                }
                wp = wp->w_wndp;
        }
        return (TRUE);
}
 
/*
 * This function deletes "n" bytes,
 * starting at dot. It understands how do deal
 * with end of lines, etc. It returns TRUE if all
 * of the characters were deleted, and FALSE if
 * they were not (because dot ran into the end of
 * the buffer. The "kflag" is TRUE if the text
 * should be put in the kill buffer.
 */
ldelete(n, kflag)
register int n, kflag;
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *dotp;
        register int    doto;
        register int    chunk;
        register WINDOW *wp;
 
        while (n != 0) {
                dotp = curwp->w_dotp;
                doto = curwp->w_doto;
                if (dotp == curbp->b_linep)     /* Hit end of buffer.   */
                        return (FALSE);
                chunk = dotp->l_used-doto;      /* Size of chunk.       */
                if (chunk > n)
                        chunk = n;
                if (chunk == 0) {               /* End of line, merge.  */
                        lchange(WFHARD);
                        if (ldelnewline() == FALSE
                        || (kflag!=FALSE && kinsert('\n')==FALSE))
                                return (FALSE);
                        --n;
                        continue;
                }
                lchange(WFEDIT);
                cp1 = &dotp->l_text[doto];      /* Scrunch text.        */
                cp2 = cp1 + chunk;
                if (kflag != FALSE) {           /* Kill?                */
                        while (cp1 != cp2) {
                                if (kinsert(*cp1) == FALSE)
                                        return (FALSE);
                                ++cp1;
                        }
                        cp1 = &dotp->l_text[doto];
                }
                while (cp2 != &dotp->l_text[dotp->l_used])
                        *cp1++ = *cp2++;
                dotp->l_used -= chunk;
                wp = wheadp;                    /* Fix windows          */
                while (wp != NULL) {
                        if (wp->w_dotp==dotp && wp->w_doto>=doto) {
                                wp->w_doto -= chunk;
                                if (wp->w_doto < doto)
                                        wp->w_doto = doto;
                        }
                        if (wp->w_markp==dotp && wp->w_marko>=doto) {
                                wp->w_marko -= chunk;
                                if (wp->w_marko < doto)
                                        wp->w_marko = doto;
                        }
                        wp = wp->w_wndp;
                }
                n -= chunk;
        }
        return (TRUE);
}
 
/*
 * Delete a newline. Join the current line
 * with the next line. If the next line is the magic
 * header line always return TRUE; merging the last line
 * with the header line can be thought of as always being a
 * successful operation, even if nothing is done, and this makes
 * the kill buffer work "right". Easy cases can be done by
 * shuffling data around. Hard cases require that lines be moved
 * about in memory. Return FALSE on error and TRUE if all
 * looks ok. Called by "ldelete" only.
 */
ldelnewline()
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *lp1;
        register LINE   *lp2;
        register LINE   *lp3;
        register WINDOW *wp;
 
        lp1 = curwp->w_dotp;
        lp2 = lp1->l_fp;
        if (lp2 == curbp->b_linep) {            /* At the buffer end.   */
                if (lp1->l_used == 0)           /* Blank line.          */
                        lfree(lp1);
                return (TRUE);
        }
        if (lp2->l_used <= lp1->l_size-lp1->l_used) {
                cp1 = &lp1->l_text[lp1->l_used];
                cp2 = &lp2->l_text[0];
                while (cp2 != &lp2->l_text[lp2->l_used])
                        *cp1++ = *cp2++;
                wp = wheadp;
                while (wp != NULL) {
                        if (wp->w_linep == lp2)
                                wp->w_linep = lp1;
                        if (wp->w_dotp == lp2) {
                                wp->w_dotp  = lp1;
                                wp->w_doto += lp1->l_used;
                        }
                        if (wp->w_markp == lp2) {
                                wp->w_markp  = lp1;
                                wp->w_marko += lp1->l_used;
                        }
                        wp = wp->w_wndp;
                }
                lp1->l_used += lp2->l_used;
                lp1->l_fp = lp2->l_fp;
                lp2->l_fp->l_bp = lp1;
                free((char *) lp2);
                return (TRUE);
        }
        if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
                return (FALSE);
        cp1 = &lp1->l_text[0];
        cp2 = &lp3->l_text[0];
        while (cp1 != &lp1->l_text[lp1->l_used])
                *cp2++ = *cp1++;
        cp1 = &lp2->l_text[0];
        while (cp1 != &lp2->l_text[lp2->l_used])
                *cp2++ = *cp1++;
        lp1->l_bp->l_fp = lp3;
        lp3->l_fp = lp2->l_fp;
        lp2->l_fp->l_bp = lp3;
        lp3->l_bp = lp1->l_bp;
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_linep==lp1 || wp->w_linep==lp2)
                        wp->w_linep = lp3;
                if (wp->w_dotp == lp1)
                        wp->w_dotp  = lp3;
                else if (wp->w_dotp == lp2) {
                        wp->w_dotp  = lp3;
                        wp->w_doto += lp1->l_used;
                }
                if (wp->w_markp == lp1)
                        wp->w_markp  = lp3;
                else if (wp->w_markp == lp2) {
                        wp->w_markp  = lp3;
                        wp->w_marko += lp1->l_used;
                }
                wp = wp->w_wndp;
        }
        free((char *) lp1);
        free((char *) lp2);
        return (TRUE);
}
 
/*
 * Delete all of the text
 * saved in the kill buffer. Called by commands
 * when a new kill context is being created. The kill
 * buffer array is released, just in case the buffer has
 * grown to immense size. No errors.
 */
kdelete()
{
        if (kbufp != NULL) {
                free((char *) kbufp);
                kbufp = NULL;
                kused = 0;
                ksize = 0;
        }
}
 
/*
 * Insert a character to the kill buffer,
 * enlarging the buffer if there isn't any room. Always
 * grow the buffer in chunks, on the assumption that if you
 * put something in the kill buffer you are going to put
 * more stuff there too later. Return TRUE if all is
 * well, and FALSE on errors.
 */
kinsert(c)
register int c;
{
        register char   *nbufp;
        register int    i;
 
        if (kused == ksize) {
                if ((nbufp =  malloc(ksize+KBLOCK)) == NULL)
                        return (FALSE);
                for (i=0; i<ksize; ++i)
                        nbufp[i] = kbufp[i];
                if (kbufp != NULL)
                        free((char *) kbufp);
                kbufp  = nbufp;
                ksize += KBLOCK;
        }
        kbufp[kused++] = c;
        return (TRUE);
}
 
/*
 * This function gets characters from
 * the kill buffer. If the character index "n" is
 * off the end, it returns "-1". This lets the caller
 * just scan along until it gets a "-1" back.
 */
kremove(n)
register int n;
{
        if (n >= kused)
                return (-1);
        else
                return (kbufp[n] & 0xFF);
}
fp = NULL;
                kused = 0;
                ksize = 0;
        }
}
 
SHAR_EOF
cat << \SHAR_EOF > word.c
/*
 * The routines in this file
 * implement commands that work word at
 * a time. There are all sorts of word mode
 * commands. If I do any sentence and/or paragraph
 * mode commands, they are likely to be put in
 * this file.  Added R.D.R Feb. 1986.
 */
#include        <stdio.h>
#include        <ctype.h>
#include        "ed.h"
 
 
/* Word wrap or fill wrap depending on f flag value.
 * Back-over whatever precedes the point on the current line and
 * stop on the first word-break or the beginning of the line.
 * If we reach the beginning of the line, jump back to the end of the
 * word and start a new line.  Otherwise, break the line at the
 * word-break, eat it, and jump back to the end of the word.
 * Returns TRUE on success, FALSE on errors.
 */
wrapword(f, n)
register int f, n;
{
        int oldp;
        oldp = curwp->w_dotp;
 
        if (! backwword(NULL, 1)) /* punctuation marks */
                return(FALSE);
        if (oldp != curwp->w_dotp && curwp->w_doto)
                {
                if (! backdel(NULL, 1))
                        return(FALSE);
                if (! newline(TRUE, 1))
                        return(FALSE);
                }
        if(f)
                return(forwwword(NULL, 1));
        return(forwwword(NULL, 1) && forwchar(NULL, 1) && backdel(NULL, 1));
}
 
/* FILLPAR Meta command  Fill paragraph to specified fill column and indent
 * column.  Bound to M-Q.
 */
 
fillpar(f, n)
register int f, n;
{
        register int s;
        register short omo;
        register LINE *omp;
 
        if (n > 1)
                setfillcol(f, n);
 
        if(fillcol == 0)
                {
                mlwrite("Fill column not set");
                (*term.t_beep)();
                return(FALSE);
                }
        omp = curwp->w_markp;
        omo = curwp->w_marko;
        curwp->w_markp = curwp->w_dotp;
        curwp->w_marko = curwp->w_doto;
 
        gotbop(FALSE, 1);
        forwchar(FALSE, 1);
        while((n = ltrw(FALSE, 1)) != EOF && n != NULL)
                forwline(NULL, 1);
        gotbop(FALSE, 1);
        forwchar(FALSE, 1);
        while (TRUE)
                {
                if (llength(curwp->w_dotp) == NULL)
                        break;
                if (curwp->w_dotp == curbp->b_linep)
                        break;
                if (getccol(FALSE) > fillcol)
                        {
                        if (wrapword(TRUE, NULL) == FALSE)
                                break;
                        continue;
                        }
                if (curwp->w_doto ==  llength(curwp->w_dotp)
                        && getccol(FALSE) <= fillcol)
                        {
                        if (forwchar(FALSE, 1) == FALSE)
                                break;
                        if (curwp->w_dotp == curbp->b_linep)
                                break;  /* @ EOB */
                        if (llength(curwp->w_dotp) == NULL)
                                break;  /* @ EOP */
                        if (backchar(FALSE, 1) == FALSE)
                                break;
                        if (clowsp(FALSE, NULL) == FALSE)
                                break;
                        continue;
                        }
                if (forwchar(FALSE, 1) == FALSE)
                        break;
                }
        curwp->w_dotp = curwp->w_markp;
        curwp->w_doto = curwp->w_marko;
        curwp->w_markp = omp;
        curwp->w_marko = omo;
        curwp->w_flag |= WFHARD;
        curgoal = getccol(FALSE);
        return(TRUE);
}
 
/*
 * PRIVATE VERSION OF INWORD() FOR WRAPWORD(), FORWWORD(), AND BACKWWORD()
 * Return FALSE if the character at dot
 * is a space character or above 0x7e.
 * Otherwise return TRUE.  Any printing
 * character below <DEL> that is not
 * a space character may appear inside a
 * word to be wrapped.  Using a special version
 * of inword() allows us to keep the usual meaning
 * of word for regular movement.
 */
static
inwword()
{
        register int    c;
 
        if (curwp->w_doto == llength(curwp->w_dotp))
                return (FALSE);
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isspace(c) || c> '~')
                return (FALSE);
        return (TRUE);
}
 
/*
 * PRIVATE VERSION OF BACKWORD() FOR WRAPWORD() AND FORWWWORD()
 * Move the cursor backward by
 * "n" words. All of the details of motion
 * are performed by the "backchar" and "forwchar"
 * routines. Error if you try to move beyond
 * the buffers.
 */
static
backwword(f, n)
register int f, n;
{
        if (backchar(FALSE, 1) == FALSE)
                return (FALSE);
        while (inwword() == FALSE)
                {
                if (backchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        while (inwword() != FALSE)
                {
                if (backchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        return (forwchar(FALSE, 1));
}
 
/* PRIVATE VERSION OF FORWWORD() FOR WRAPWORD() AND BACKWWORD()
 * Move the cursor forward by
 * the specified number of words. All of the
 * motion is done by "forwchar". Error if you
 * try and move beyond the buffer's end.
 */
static
forwwword(f, n)
register int f, n;
{
        while (inwword() == FALSE)
                {
                if (forwchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        while (inwword() != FALSE)
                {
                if (forwchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        return (TRUE);
}
 
/*
 * Move the cursor forward by
 * the specified number of words. All of the
 * motion is done by "forwchar". Error if you
 * try and move beyond the buffer's end.
 */
forwword(f, n)
register int f, n;
{
        if (n < 0)
                return (backword(f, -n));
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (TRUE);
}
 
/*
 * Move the cursor backward by
 * "n" words. All of the details of motion
 * are performed by the "backchar" and "forwchar"
 * routines. Error if you try to move beyond
 * the buffers.
 */
backword(f, n)
register int f, n;
{
        if (n < 0)
                return (forwword(f, -n));
        if (backchar(FALSE, 1) == FALSE)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (forwchar(FALSE, 1));
}
 
/*
 * Move the cursor forward by
 * the specified number of words. As you move,
 * convert any characters to upper case. Error
 * if you try and move beyond the end of the
 * buffer. Bound to "M-U".
 */
upperword(f, n)
register int f, n;
{
        register int    c;
 
        if (n < 0)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if (islower(c)) {
                                c = toupper(c);
                                lputc(curwp->w_dotp, curwp->w_doto, c);
                                lchange(WFHARD);
                        }
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (TRUE);
}
 
/*
 * Move the cursor forward by
 * the specified number of words. As you move
 * convert characters to lower case. Error if you
 * try and move over the end of the buffer.
 * Bound to "M-L".
 */
lowerword(f, n)
register int f, n;
{
        register int    c;
 
        if (n < 0)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if (isupper(c)) {
                                c = tolower(c);
                                lputc(curwp->w_dotp, curwp->w_doto, c);
                                lchange(WFHARD);
                        }
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (TRUE);
}
 
/*
 * Move the cursor forward by
 * the specified number of words. As you move
 * convert the first character of the word to upper
 * case, and subsequent characters to lower case. Error
 * if you try and move past the end of the buffer.
 * Bound to "M-C".
 */
capword(f, n)
register int f, n;
{
        register int    c;
 
        if (n < 0)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                if (inword() != FALSE) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if (islower(c)) {
                                c = toupper(c);
                                lputc(curwp->w_dotp, curwp->w_doto, c);
                                lchange(WFHARD);
                        }
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        while (inword() != FALSE) {
                                c = lgetc(curwp->w_dotp, curwp->w_doto);
                                if (isupper(c)) {
                                        c = tolower(c);
                                        lputc(curwp->w_dotp, curwp->w_doto, c);
                                        lchange(WFHARD);
                                }
                                if (forwchar(FALSE, 1) == FALSE)
                                        return (FALSE);
                        }
                }
        }
        return (TRUE);
}
 
/*
 * Kill forward by "n" words.
 * Remember the location of dot. Move forward
 * by the right number of words. Put dot back where
 * it was and issue the kill command for the
 * right number of characters. Bound to "M-D".
 */
delfword(f, n)
register int f, n;
{
        register int    size;
        register LINE   *dotp;
        register int    doto;
 
        if (n < 0)
                return (FALSE);
        dotp = curwp->w_dotp;
        doto = curwp->w_doto;
        size = 0;
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
                while (inword() != FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
        }
        curwp->w_dotp = dotp;
        curwp->w_doto = doto;
        return (ldelete(size, TRUE));
}
 
/*
 * Kill backwards by "n" words.
 * Move backwards by the desired number of
 * words, counting the characters. When dot is
 * finally moved to its resting place, fire off
 * the kill command. Bound to "M-Rubout" and
 * to "M-Backspace".
 */
delbword(f, n)
register int f, n;
{
        register int    size;
 
        if (n < 0)
                return (FALSE);
        if (backchar(FALSE, 1) == FALSE)
                return (FALSE);
        size = 0;
        while (n--) {
                while (inword() == FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
                while (inword() != FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
        }
        if (forwchar(FALSE, 1) == FALSE)
                return (FALSE);
        return (ldelete(size, TRUE));
}
 
/*
 * Return TRUE if the character at dot
 * is a character that is considered to be
 * part of a word. The word character list is hard
 * coded. Should be setable.
 */
inword()
{
        register int    c;
 
        if (curwp->w_doto == llength(curwp->w_dotp))
                return (FALSE);
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isalnum(c))
                return (TRUE);
        if (c=='$' || c=='_')   /* For identifiers */
                return (TRUE);
        return (FALSE);
}
 
/* FTOPUNCT : eXtended Command  Move forward to next punctuation mark.  Bound
 * to CTLX - >.
 */
 
ftopunct(f, n)
register int f, n;
{
        register int c;
 
        do      {
                if(forwchar(FALSE, 1) == FALSE)
                        return(FALSE);
                c = lgetc(curwp->w_dotp, curwp->w_doto);
                }
                while (! ispunct(c) || isspace(c))
                        ;
        return(TRUE);
}
 
/* BTOPUNCT : eXtended Command  Move backward to last punctuation mark.  Bound
 * to CTLX - <.
 */
 
btopunct(f, n)
register int f, n;
{
        register int c;
 
        do      {
                if(backchar(FALSE, 1) == FALSE)
                        return(FALSE);
                c = lgetc(curwp->w_dotp, curwp->w_doto);
                }
                while (! ispunct(c) || isspace(c))
                        ;
        return(TRUE);
}
 
/* FORWSENT : Meta Command  Move forward to end punctuation mark.
 * A sentence is defined by a full stop followed by white space.
 * Bound to M-E.
 */
 
forwsent(f, n)
register int f, n;
{
        register int c;
 
        if( n < 0)
                return(backsent(f, -n));
loop:   while(n--)
        {
                while(forwchar(FALSE, 1) != FALSE)
                        {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if(llength(curwp->w_dotp) == NULL)
                                goto loop;
                        if(c=='.' ||  c=='!' || c=='?')
                                {
                                if (forwchar(FALSE, 1) == FALSE)
                                        return(FALSE);
                                c = lgetc(curwp->w_dotp, curwp->w_doto);
                                if (c <= ' ' || c == '\"'
                                    || curwp->w_doto == llength(curwp->w_dotp))
                                        goto loop;
                                }
                        }
                return(FALSE);  /* EOF or no sentence terminator found */
        }
        return(TRUE);
}
 
/* BACKSENT : Meta Command  Move backward to last terminal punctuation mark.
 * A sentence is defined by a full stop followed by white space.  This
 * function has trouble with some sentences at ends of lines.  Bound to
 * M-A.
 */
 
backsent(f, n)
register int f, n;
{
        register int c;
        register int d;
 
        if (n < 0)
                return (forwsent(f, -n));
 
        d = 'a';        /* d must begin as an alpha */
loop:   while (n--)
                {
                while(backchar(FALSE, 1) != FALSE)
                        {
                        if(llength(curwp->w_dotp) == NULL)
                                goto loop;
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if(c=='.' ||  c=='?' || c=='!')
                                if (d <= ' ' || d == '\"'
                                    || curwp->w_doto+1 == llength(curwp->w_dotp)
)
                                        {
                                        forwchar(FALSE, 1);
                                        goto loop;
                                        }
                        d = c;  /* d becomes the last fetched char */
                        }
                return(FALSE);  /* BOF or no sentence terminator found */
                }
        return(TRUE);
}
 
/* GOTEOP : META command Goto end of paragraph.  Each paragraph is a
 * block of text bordered by blanklines.  Bound to M-N.
 */
 
goteop(f, n)
register f, n;
{
        if ( n < 0)
                return(gotbop(f, -n));
        while (n)
                {
                if (curwp->w_dotp != curbp->b_linep)
                        forwline(NULL, 1);
                else
                        return(FALSE);
                if (llength(curwp->w_dotp) == NULL)
                        --n;
                }
        return(TRUE);
}
 
/* GOTBOP : META command Goto beginning of paragraph.  Each paragraph is
 * a block of text bordered by blanklines.  Bound to M-P.
 */
 
gotbop(f, n)
register f, n;
{
        if ( n < 0)
                return(goteop(f, -n));
        while (n)
                {
                if (lback(curwp->w_dotp) != curbp->b_linep)
                        backline(NULL, 1);
                else
                        return(FALSE);
                if (llength(curwp->w_dotp) == NULL)
                        --n;
                }
        return(TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > search.c
/*
 * The functions in this file
 * implement commands that search in the
 * forward and backward directions. There are
 * no special characters in the search strings.
 * Probably should have a regular expression
 * search, or something like that.
 */
#include        <stdio.h>
#include        <ctype.h>
#include        "ed.h"
 
 
#define CCHR(x)  ((x)-'@')
 
#define SRCH_BEGIN      (0)                  /* Search sub-codes.    */
#define SRCH_FORW       (-1)
#define SRCH_BACK       (-2)
#define SRCH_PREV       (-3)
#define SRCH_NEXT       (-4)
#define SRCH_NOPR       (-5)
#define SRCH_ACCM       (-6)
#define NSRCH   128                     /* Undoable search commands.    */
 
typedef struct  {
        int     s_code;
        LINE    *s_dotp;
        int     s_doto;
}       SRCHCOM;
 
static  SRCHCOM cmds[NSRCH];
static  int     cip;
 
int     srch_lastdir = SRCH_NOPR;              /* Last search flags.   */
 
/*
 * Search forward.
 * Get a search string from the user, and search for it,
 * starting at ".". If found, "." gets moved to just after the
 * matched characters, and display does all the hard stuff.
 * If not found, it just prints a message.
 */
forwsearch(f, n)
register int f, n;
{
 
        if ((f=readpattern("Search", &pat)) != TRUE)
                return (f);
        if (forwsrch() == FALSE) {
                mlwrite("Not found");
                return (FALSE);
        }
        return (TRUE);
}
 
/*
 * Reverse search.
 * Get a search string from the  user, and search, starting at "."
 * and proceeding toward the front of the buffer. If found "." is left
 * pointing at the first character of the pattern [the last character that
 * was matched].
 */
backsearch(f, n)
register int f, n;
{
 
        if ((f=readpattern("Reverse search", &pat)) != TRUE)
                return (f);
        if (backsrch() == FALSE) {
                mlwrite("Not found");
                return (FALSE);
        }
        return (TRUE);
}
 
 
/*
 * This routine does the real work of a
 * forward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
forwsrch()
{
        register LINE   *clp;
        register int    cbo;
        register LINE   *tlp;
        register int    tbo;
        register char   *pp;
        register int    c;
 
        clp = curwp->w_dotp;
        cbo = curwp->w_doto;
        while (clp != curbp->b_linep) {
                if (cbo == llength(clp)) {
                        clp = lforw(clp);
                        cbo = 0;
                        c = '\n';
                } else
                        c = lgetc(clp, cbo++);
                if (eq(c, pat[0]) != FALSE) {
                        tlp = clp;
                        tbo = cbo;
                        pp  = &pat[1];
                        while (*pp != 0) {
                                if (tlp == curbp->b_linep)
                                        goto fail;
                                if (tbo == llength(tlp)) {
                                        tlp = lforw(tlp);
                                        if (tlp == curbp->b_linep)
                                                goto fail;
                                        tbo = 0;
                                        c = '\n';
                                } else
                                        c = lgetc(tlp, tbo++);
                                if (eq(c, *pp++) == FALSE)
                                        goto fail;
                        }
                        curwp->w_dotp  = tlp;
                        curwp->w_doto  = tbo;
                        curwp->w_flag |= WFMOVE;
                        return (TRUE);
                }
        fail:   ;
        }
        return (FALSE);
}
 
/*
 * This routine does the real work of a
 * backward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
backsrch()
{
        register LINE   *clp;
        register int    cbo;
        register LINE   *tlp;
        register int    tbo;
        register int    c;
        register char   *epp;
        register char   *pp;
 
        for (epp = &pat[0]; epp[1] != 0; ++epp)
                ;
        clp = curwp->w_dotp;
        cbo = curwp->w_doto;
        for (;;) {
                if (cbo == 0) {
                        clp = lback(clp);
                        if (clp == curbp->b_linep)
                                return (FALSE);
                        cbo = llength(clp)+1;
                }
                if (--cbo == llength(clp))
                        c = '\n';
                else
                        c = lgetc(clp,cbo);
                if (eq(c, *epp) != FALSE) {
                        tlp = clp;
                        tbo = cbo;
                        pp  = epp;
                        while (pp != &pat[0]) {
                                if (tbo == 0) {
                                        tlp = lback(tlp);
                                        if (tlp == curbp->b_linep)
                                                goto fail;
                                        tbo = llength(tlp)+1;
                                }
                                if (--tbo == llength(tlp))
                                        c = '\n';
                                else
                                        c = lgetc(tlp,tbo);
                                if (eq(c, *--pp) == FALSE)
                                        goto fail;
                        }
                        curwp->w_dotp  = tlp;
                        curwp->w_doto  = tbo;
                        curwp->w_flag |= WFMOVE;
                        return (TRUE);
                }
        fail:   ;
        }
}
 
/*
 * Compare two characters.
 * The "bc" comes from the buffer.
 * It has it's case folded out. The
 * "pc" is from the pattern.
 */
eq(bc, pc)
register int    bc;
register int    pc;
{
        if (bc>='a' && bc<='z')
                bc -= 0x20;
        if (pc>='a' && pc<='z')
                pc -= 0x20;
        if (bc == pc)
                return (TRUE);
        return (FALSE);
}
 
/*
 * Read a pattern.
 * Stash it in an external
 * variable. The calling function
 * passes the address of inpat. The "pat" is
 * not updated if the user types in
 * an empty line. If the user typed
 * an empty line, and there is no
 * old pattern, it is an error.
 * Display the old pattern, in the
 * style of Jeff Lomicka. There is
 * some do-it-yourself control
 * expansion.
 */
readpattern(prompt, inpat)
char    *prompt;
char    *inpat;
{
        register char   *cp1;
        register char   *cp2;
        register int    c;
        register int    s;
        char            tpat[NPAT+20];
 
        cp1 = &tpat[0];                         /* Copy prompt          */
        cp2 = prompt;
        while ((c = *cp2++) != '\0')
                *cp1++ = c;
        if (inpat[0] != '\0') {                 /* Old pattern          */
                *cp1++ = ' ';
                *cp1++ = '[';
                cp2 = &inpat[0];
                while ((c = *cp2++) != 0) {
                        if (cp1 < &tpat[NPAT+20-6]) {   /* "??]: \0"    */
                                if (c<0x20 || c==0x7F) {
                                        *cp1++ = '^';
                                        c ^= 0x40;
                                } else if (c == '%')    /* Map "%" to   */
                                        *cp1++ = c;     /* "%%".        */
                                *cp1++ = c;
                        }
                }
                *cp1++ = ']';
        }
        *cp1++ = ':';                           /* Finish prompt        */
        *cp1++ = ' ';
        *cp1++ = '\0';
        s = mlreply(tpat, tpat, NPAT);          /* Read pattern         */
        if (s == TRUE)                          /* Specified            */
                strcpy(inpat, tpat);
        else if (s==FALSE && inpat[0]!=0)               /* CR, but old one */
                s = TRUE;
        return (s);
}
 
/*
 * Use incremental searching, initially in the forward direction.
 * isearch ignores any explicit arguments.
 */
forwisearch(f, n)
register int f, n;
{
        return (isearch(SRCH_FORW));
}
 
/*
 * Use incremental searching, initially in the reverse direction.
 * isearch ignores any explicit arguments.
 */
backisearch(f, n)
register int f, n;
{
        return (isearch(SRCH_BACK));
}
 
/*
 * Incremental Search.
 *      dir is used as the initial direction to search.
 *      ^N      find next occurance  (if first thing typed reuse old string).
 *      ^P      find prev occurance  (if first thing typed reuse old string).
 *      ^S      switch direction to forward, find next
 *      ^R      switch direction to reverse, find prev
 *      ^Q      quote next character (allows searching for ^N etc.)
 *      <ESC>   exit from Isearch.
 *      <DEL>   undoes last character typed. (tricky job to do this correctly).
 *      else    accumulate into search string
 */
isearch(dir)
int dir;
{
        register int    c;
        register LINE   *clp;
        register int    cbo;
        register int    success;
        int          pptr;
 
        for (cip=0; cip<NSRCH; cip++)
                cmds[cip].s_code = SRCH_NOPR;
        cip = 0;
        pptr = -1;
        clp = curwp->w_dotp;
        cbo = curwp->w_doto;
        is_lpush();
        is_cpush(SRCH_BEGIN);
        success = TRUE;
        is_prompt(dir, TRUE, success);
        for (;;) {
                update();
                switch (c = ttgetc()) {
                case CCHR('M'):
                case METACH:
                        srch_lastdir = dir;
                        mlwrite("[Done]");
                        return (TRUE);
 
                case CCHR('G'):
                        curwp->w_dotp = clp;
                        curwp->w_doto = cbo;
                        curwp->w_flag |= WFMOVE;
                        srch_lastdir = dir;
                        ctrlg(FALSE, 0);
                        return (FALSE);
 
                case CCHR('S'):
                case CCHR('F'):
                        if (dir == SRCH_BACK) {
                                dir = SRCH_FORW;
                                is_lpush();
                                is_cpush(SRCH_FORW);
                                success = TRUE;
                        }
                        /* Drop through to find next. */
                case CCHR('N'):
                        if (success==FALSE && dir==SRCH_FORW)
                                break;
                        is_lpush();
                        forwchar(FALSE, 1);
                        if (is_find(SRCH_NEXT) != FALSE) {
                                is_cpush(SRCH_NEXT);
                                pptr = strlen(pat);
                        } else {
                                backchar(FALSE, 1);
                                (*term.t_beep)();
                                success = FALSE;
                        }
                        is_prompt(dir, FALSE, success);
                        break;
 
                case CCHR('R'):
                case CCHR('B'):
                        if (dir == SRCH_FORW) {
                                dir = SRCH_BACK;
                                is_lpush();
                                is_cpush(SRCH_BACK);
                                success = TRUE;
                        }
                        /* Drop through to find previous. */
                case CCHR('P'):
                        if (success==FALSE && dir==SRCH_BACK)
                                break;
                        is_lpush();
                        backchar(FALSE, 1);
                        if (is_find(SRCH_PREV) != FALSE) {
                                is_cpush(SRCH_PREV);
                                pptr = strlen(pat);
                        } else {
                                forwchar(FALSE, 1);
                                (*term.t_beep)();
                                success = FALSE;
                        }
                        is_prompt(dir,FALSE,success);
                        break;
 
                case 0x7F:
                        if (is_undo(&pptr, &dir) != TRUE)
                                return (ABORT);
                        if (is_peek() != SRCH_ACCM)
                                success = TRUE;
                        is_prompt(dir, FALSE, success);
                        break;
 
                case CCHR('^'):
                case CCHR('Q'):
                        c = ttgetc();
                case CCHR('U'):
                case CCHR('X'):
                case CCHR('J'):
                        goto  addchar;
 
                default:
                        if (iscntrl(c) != FALSE) {
                                c += '@';
                                c |= CTRL;
                                success = execute(c, FALSE, 1);
                                curwp->w_flag |= WFMOVE;
                                return (success);
                        }
                addchar:
                        if (pptr == -1)
                                pptr = 0;
                        if (pptr == 0)
                                success = TRUE;
                        pat[pptr++] = c;
                        if (pptr == NPAT) {
                                mlwrite("Pattern too long");
                                ctrlg(FALSE, 0);
                                return (ABORT);
                        }
                        pat[pptr] = '\0';
                        is_lpush();
                        if (success != FALSE) {
                                if (is_find(dir) != FALSE)
                                        is_cpush(c);
                                else {
                                        success = FALSE;
                                        (*term.t_beep)();
                                        is_cpush(SRCH_ACCM);
                                }
                        } else
                                is_cpush(SRCH_ACCM);
                        is_prompt(dir, FALSE, success);
                }
        }
}
 
is_cpush(cmd)
register int    cmd;
{
        if (++cip >= NSRCH)
                cip = 0;
        cmds[cip].s_code = cmd;
}
 
is_lpush()
{
        register int    ctp;
 
        ctp = cip+1;
        if (ctp >= NSRCH)
                ctp = 0;
        cmds[ctp].s_code = SRCH_NOPR;
        cmds[ctp].s_doto = curwp->w_doto;
        cmds[ctp].s_dotp = curwp->w_dotp;
}
 
is_pop()
{
        if (cmds[cip].s_code != SRCH_NOPR) {
                curwp->w_doto  = cmds[cip].s_doto;
                curwp->w_dotp  = cmds[cip].s_dotp;
                curwp->w_flag |= WFMOVE;
                cmds[cip].s_code = SRCH_NOPR;
        }
        if (--cip <= 0)
                cip = NSRCH-1;
}
 
is_peek()
{
        if (cip == 0)
                return (cmds[NSRCH-1].s_code);
        else
                return (cmds[cip-1].s_code);
}
 
is_undo(pptr, dir)
register int    *pptr;
register int    *dir;
{
        switch (cmds[cip].s_code) {
        case SRCH_NOPR:
        case SRCH_BEGIN:
        case SRCH_NEXT:
        case SRCH_PREV:
                break;
 
        case SRCH_FORW:
                *dir = SRCH_BACK;
                break;
 
        case SRCH_BACK:
                *dir = SRCH_FORW;
                break;
 
        case SRCH_ACCM:
        default:
                *pptr -= 1;
                if (*pptr < 0)
                        *pptr = 0;
                pat[*pptr] = '\0';
                break;
        }
        is_pop();
        return (TRUE);
}
 
is_find(dir)
register int    dir;
{
        register int    plen;
 
        plen = strlen(pat);
        if (plen != 0) {
                if (dir==SRCH_FORW || dir==SRCH_NEXT) {
                        backchar(FALSE, plen);
                        if (forwsrch() == FALSE) {
                                forwchar(FALSE, plen);
                                return (FALSE);
                        }
                        return (TRUE);
                }
                if (dir==SRCH_BACK || dir==SRCH_PREV) {
                        forwchar(FALSE, plen);
                        if (backsrch() == FALSE) {
                                backchar(FALSE, plen);
                                return (FALSE);
                        }
                        return (TRUE);
                }
                mlwrite("bad call to is_find");
                ctrlg(FALSE, 0);
                return (FALSE);
        }
        return (FALSE);
}
 
/*
 * If called with "dir" not one of SRCH_FORW
 * or SRCH_BACK, this routine used to print an error
 * message. It also used to return TRUE or FALSE,
 * depending on if it liked the "dir". However, none
 * of the callers looked at the status, so I just
 * made the checking vanish.
 */
is_prompt(dir, flag, success)
{
        if (dir == SRCH_FORW) {
                if (success != FALSE)
                        is_dspl("i-search forward", flag);
                else
                        is_dspl("failing i-search forward", flag);
        } else if (dir == SRCH_BACK) {
                if (success != FALSE)
                        is_dspl("i-search backward", flag);
                else
                        is_dspl("failing i-search backward", flag);
        }
}
 
/*
 * Prompt writing routine for the incremental search.
 * The "prompt" is just a string. The "flag" determines
 * if a "[ ]" or ":" embelishment is used.
 */
is_dspl(prompt, flag)
char    *prompt;
{
        if (flag != FALSE)
                mlwrite("%s [%s]", prompt, pat);
        else
                mlwrite("%s: %s", prompt, pat);
}
 
/* META Command Search and replace in forward direction only.  Prompts
 * before replacement allows user to ABORT or continue.  Calling with an
 * argument prevents case distinctions.  Bound to M-R.
 */
 
replace(f, n)
register int f, n;
{
        register int    s;
        register int    kludge;         /* Watch for saved line move    */
        register LINE   *clp;           /* saved line pointer           */
        char         toprompt[81];      /* temporary string             */
        char         prompt[81];        /* final prompt                 */
        char         *perptr, *index(); /* remap '%' to '%%' in prompt  */
        int          cbo;               /* offset into the saved line   */
        int          rcnt = 0;          /* Replacements made so far     */
        int          plen;              /* length of found string       */
 
        strcpy(prompt,"Query Replace ");
        if ((s=readpattern(prompt, &pat)) != TRUE)
                return (s);
        strcat(prompt,pat);
        strcat(prompt," with");
        if ((perptr=index(prompt,'%'))!=NULL)
                {
                *perptr = '\0';                 /* terminate prompt     */
                strcpy(toprompt,"%%");          /* setup mapping chars  */
                strcat(toprompt,++perptr);      /* append end of prompt */
                strcat(prompt,toprompt);        /* append end to prompt */
                }
        if ((s=readpattern(prompt, &rpat)) == ABORT)
                return (s);
        if (s == FALSE)
                rpat[0] = '\0';
        plen = strlen(pat);
 
        /*
         * Search forward repeatedly, checking each time whether to insert
         * or not.  The "!" case makes the check always true, so it gets put
         * into a tighter loop for efficiency.
         *
         * If we change the line that is the remembered value of dot, then
         * it is possible for the remembered value to move.  This causes great
         * pain when trying to return to the non-existant line.
         *
         * possible fixes:
         * 1) put a single, relocated marker in the WINDOW structure, handled
         *    like mark.  The problem now becomes a what if two are needed...
         * 2) link markers into a list that gets updated (auto structures for
         *    the nodes)
         * 3) Expand the mark into a stack of marks and add pushmark, popmark.
         */
 
        clp = curwp->w_dotp;        /* save the return location     */
        cbo = curwp->w_doto;
        while (forwsrch() == TRUE) {
        retry:
                update();
                switch (ttgetc()) {
                case ' ':
                case ',':
                        kludge = (curwp->w_dotp == clp);
                        if (lreplace(plen, rpat, f) == FALSE)
                                return (FALSE);
                        rcnt++;
                        if (kludge != FALSE)
                                clp = curwp->w_dotp;
                        break;
 
                case '.':
                        kludge = (curwp->w_dotp == clp);
                        if (lreplace(plen, rpat, f) == FALSE)
                                return (FALSE);
                        rcnt++;
                        if (kludge != FALSE)
                                clp = curwp->w_dotp;
                        goto stopsearch;
 
                case 0x07:
                        ctrlg(FALSE, 0);
                        goto stopsearch;
 
                case '!':
                        do {
                                kludge = (curwp->w_dotp == clp);
                                if (lreplace(plen, rpat, f) == FALSE)
                                        return (FALSE);
                                rcnt++;
                                if (kludge != FALSE)
                                        clp = curwp->w_dotp;
                        } while (forwsrch() == TRUE);
                        goto stopsearch;
 
                case 'n':
                        break;
 
                default:
mlwrite("<SP>[,] replace, [.] rep-end, [n] don't, [!] repl rest [C-G] quit");
                        goto retry;
                }
        }
stopsearch:
        curwp->w_dotp = clp;
        curwp->w_doto = cbo;
        curwp->w_flag |= WFHARD;
        update();
        if (rcnt == 0)
                mlwrite("[No replacements done]");
        else if (rcnt == 1)
                mlwrite("[1 replacement done]");
        else
                mlwrite("[%d replacements done]", rcnt);
        return (TRUE);
}
 
/*
 * Replace plen characters before dot with argument string.
 * Control-J characters in st are interpreted as newlines.
 * There is a casehack disable flag (normally it likes to match
 * case of replacement to what was there).
 */
lreplace(plen, st, f)
register int    plen;              /* length to remove       */
char        *st;                    /* replacement string          */
int          f;               /* case hack disable          */
{
        register int    rlen;      /* replacement length           */
        register int    rtype;    /* capitalization            */
        register int    c;            /* used for random characters   */
        register int    doto;      /* offset into line       */
 
        /*
         * Find the capitalization of the word that was found.
         * f says use exact case of replacement string (same thing that
         * happens with lowercase found), so bypass check.
         */
        backchar(TRUE, plen);
        rtype = __l;
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isupper(c)!=FALSE  &&  f==FALSE) {
                rtype = __u|__l;
                if (curwp->w_doto+1 < llength(curwp->w_dotp)) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto+1);
                        if (isupper(c) != FALSE) {
                                rtype = __u;
                        }
                }
        }
 
        /*
         * make the string lengths match (either pad the line
         * so that it will fit, or scrunch out the excess).
         * be careful with dot's offset.
         */
        rlen = strlen(st);
        doto = curwp->w_doto;
        if (plen > rlen)
                ldelete(plen-rlen, FALSE);
        else if (plen < rlen) {
                if (linsert(rlen-plen, ' ') == FALSE)
                        return (FALSE);
        }
        curwp->w_doto = doto;
 
        /*
         * do the replacement:  If was capital, then place first
         * char as if upper, and subsequent chars as if lower.
         * If inserting upper, check replacement for case.
         */
        while ((c = *st++&0xff) != '\0') {
                if ((rtype&__u)!=0  &&  islower(c)!=0)
                        c = toupper(c);
                if (rtype == (__u|__l))
                        rtype = __l;
                if (c == '\n') {
                        if (curwp->w_doto == llength(curwp->w_dotp))
                                forwchar(FALSE, 1);
                        else {
                                ldelete(1, FALSE);
                                lnewline();
                        }
                } else if (curwp->w_dotp == curbp->b_linep) {
                        linsert(1, c);
                } else if (curwp->w_doto == llength(curwp->w_dotp)) {
                        ldelete(1, FALSE);
                        linsert(1, c);
                } else
                        lputc(curwp->w_dotp, curwp->w_doto++, c);
        }
        lchange(WFHARD);
        return (TRUE);
}
 
SHAR_EOF
cat << \SHAR_EOF > wc.c
/* File: wc.c
 * This file originally appeared in "The C Programming Language" (c) 1978.
 * Modified to become part of MicroEMACS.  Includes line and word information.
 * Searches current buffer only.
 */
 
#include <stdio.h>
#include <ctype.h>
#include "ed.h"
 
wc(f, n)
register int f, n;
{
 
        static double lpw = (0.00);
        static double tp1,tp2;
        register long nc, na, nw;
        register LINE *dlp;
        char lpwbuf[6];
        short dlo, inword;
 
        /*
         * nc = # of characters
         * f = # lines
         * nw = # words
         * na = # alpha characters
         * inword = "Are we in a word?"
        */
 
        inword = NO;
        f = 00;
        na = nw = nc = 0L;
        dlp = curwp->w_dotp;
        dlo = curwp->w_doto;
 
        gotobob(NULL, 1);
 
        while (curwp->w_dotp != curbp->b_linep)
                {
                ++nc;
                n = lgetc(curwp->w_dotp, curwp->w_doto);
                if (curwp->w_doto == llength(curwp->w_dotp))
                        {
                        ++f;
                        inword = NO;
                        }
                if (isspace(n))
                        inword = NO;
                else    if (inword == NO)
                                {
                                inword = YES;
                                ++nw;
                                }
                if (isalpha(n))
                        ++na;
                forwchar(NULL, 1);
                }
        --nw;
        curwp->w_dotp = dlp;
        curwp->w_doto = dlo;
        curwp->w_flag |= WFHARD;
        if (nw > 0L)            /* avoid division by zero */
                {
                tp1 = (double)na;
                tp2 = (double)nw;
                lpw = (double)(tp1/tp2);
                }
        ftoa(lpw, lpwbuf, 2);
        n = mlwrite("Length:=%D Lines:=%d Words:=%D Letters:=%D \
Avg. word:=%s letters", nc, f, nw, na, lpwbuf);
        return(n);
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000
Technical Details
Message-ID: <8702061913.AA06137@ucbvax.Berkeley.EDU>
Path: arc1-ca.pugleaf.net!utzoo!watmath!clyde!cbatt!ucbvax!ULKYVX.BITNET!RDROYA01

Show Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: arc1-ca.pugleaf.net!utzoo!watmath!clyde!cbatt!ucbvax!ULKYVX.BITNET!RDROYA01
From: RDROYA01@ULKYVX.BITNET.UUCP
Newsgroups: comp.sys.atari.st
Subject: Uemail source (11 of 12)
Message-ID: <8702061913.AA06137@ucbvax.Berkeley.EDU>
Date: Fri, 6-Feb-87 11:40:00 EST
Article-I.D.: ucbvax.8702061913.AA06137
Posted: Fri Feb  6 11:40:00 1987
Date-Received: Sun, 8-Feb-87 01:40:21 EST
Sender: daemon@ucbvax.BERKELEY.EDU
Organization: University of Louisville
Lines: 1948