Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: 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 # 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 #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; il_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; il_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= 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 #include #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 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 #include #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.) * exit from Isearch. * 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; cipw_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("[,] 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 #include #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