Article View: comp.sys.atari.st
Article #24810Uemail source (11 of 12)
From: RDROYA01
Date: Fri, 06 Feb 1987 16:45
Date: Fri, 06 Feb 1987 16:45
1942 lines
65886 bytes
65886 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);
}
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:
<8702061901.AA05899@ucbvax.Berkeley.EDU>Path:
arc1-ca.pugleaf.net!utzoo!watmath!clyde!cbatt!ucbvax!ULKYVX.BITNET!RDROYA01Show 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: <8702061901.AA05899@ucbvax.Berkeley.EDU> Date: Fri, 6-Feb-87 11:45:00 EST Article-I.D.: ucbvax.8702061901.AA05899 Posted: Fri Feb 6 11:45:00 1987 Date-Received: Sun, 8-Feb-87 01:43:32 EST Sender: daemon@ucbvax.BERKELEY.EDU Organization: University of Louisville Lines: 1942