allbery@ncoast.UUCP (06/15/87)
: #!/bin/sh # shar+ created from directory /usr2/davidsen/emacs38i # 13:42 on Thu Jun 11, 1987 by davidsen echo 'x - line.c (text)' sed << 'E!O!F' 's/^X//' > line.c X/* X * The functions in this file are a general set of line management utilities. X * They are the only routines that touch the text. They also touch the buffer X * and window structures, to make sure that the necessary updating gets done. X * There are routines in this file that handle the kill buffer too. It isn't X * here for any good reason. X * X * Note that this code only updates the dot and mark values in the window list. X * Since all the code acts on the current window, the buffer that we are X * editing must be being displayed, which means that "b_nwnd" is non zero, X * which means that the dot and mark values in the buffer headers are nonsense. X */ X X#include <stdio.h> X#include "estruct.h" X#include "edef.h" X XKILL *ykbuf; /* ptr to current kill buffer chunk being yanked */ Xint ykboff; /* offset into that chunk */ X X/* X * This routine allocates a block of memory large enough to hold a LINE X * containing "used" characters. The block is always rounded up a bit. Return X * a pointer to the new block, or NULL if there isn't any memory left. Print a X * message in the message line if no space. X */ XLINE * Xlalloc(used) Xregister int used; X{ X register LINE *lp; X register int size; X char *malloc(); X X size = (used+NBLOCK-1) & ~(NBLOCK-1); X if (size == 0) /* Assume that an empty */ X size = NBLOCK; /* line is for type-in. */ X if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) { X mlwrite("Cannot allocate %d bytes", size); X return (NULL); X } X lp->l_size = size; X lp->l_used = used; X return (lp); X} X X/* X * Delete line "lp". Fix all of the links that might point at it (they are X * moved to offset 0 of the next line. Unlink the line from whatever buffer it X * might be in. Release the memory. The buffers are updated too; the magic X * conditions described in the above comments don't hold here. X */ Xlfree(lp) Xregister LINE *lp; X{ X register BUFFER *bp; X register WINDOW *wp; X X wp = wheadp; X while (wp != NULL) { X if (wp->w_linep == lp) X wp->w_linep = lp->l_fp; X if (wp->w_dotp == lp) { X wp->w_dotp = lp->l_fp; X wp->w_doto = 0; X } X if (wp->w_markp == lp) { X wp->w_markp = lp->l_fp; X wp->w_marko = 0; X } X wp = wp->w_wndp; X } X bp = bheadp; X while (bp != NULL) { X if (bp->b_nwnd == 0) { X if (bp->b_dotp == lp) { X bp->b_dotp = lp->l_fp; X bp->b_doto = 0; X } X if (bp->b_markp == lp) { X bp->b_markp = lp->l_fp; X bp->b_marko = 0; X } X } X bp = bp->b_bufp; X } X lp->l_bp->l_fp = lp->l_fp; X lp->l_fp->l_bp = lp->l_bp; X free((char *) lp); X} X X/* X * This routine gets called when a character is changed in place in the current X * buffer. It updates all of the required flags in the buffer and window X * system. The flag used is passed as an argument; if the buffer is being X * displayed in more than 1 window we change EDIT t HARD. Set MODE if the X * mode line needs to be updated (the "*" has to be set). X */ Xlchange(flag) Xregister int flag; X{ X register WINDOW *wp; X X if (curbp->b_nwnd != 1) /* Ensure hard. */ X flag = WFHARD; X if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */ X flag |= WFMODE; /* update mode lines. */ X curbp->b_flag |= BFCHG; X } X wp = wheadp; X while (wp != NULL) { X if (wp->w_bufp == curbp) X wp->w_flag |= flag; X wp = wp->w_wndp; X } X} X Xinsspace(f, n) /* insert spaces forward into text */ X Xint f, n; /* default flag and numeric argument */ X X{ X linsert(n, ' '); X backchar(f, n); X} X X/* X * Insert "n" copies of the character "c" at the current location of dot. In X * the easy case all that happens is the text is stored in the line. In the X * hard case, the line has to be reallocated. When the window list is updated, X * take special care; I screwed it up once. You always update dot in the X * current window. You update mark, and a dot in another window, if it is X * greater than the place where you did the insert. Return TRUE if all is X * well, and FALSE on errors. X */ Xlinsert(n, c) X{ X register char *cp1; X register char *cp2; X register LINE *lp1; X register LINE *lp2; X register LINE *lp3; X register int doto; X register int i; X register WINDOW *wp; X X if (curbp->b_mode&MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X lchange(WFEDIT); X lp1 = curwp->w_dotp; /* Current line */ X if (lp1 == curbp->b_linep) { /* At the end: special */ X if (curwp->w_doto != 0) { X mlwrite("bug: linsert"); X return (FALSE); X } X if ((lp2=lalloc(n)) == NULL) /* Allocate new line */ X return (FALSE); X lp3 = lp1->l_bp; /* Previous line */ X lp3->l_fp = lp2; /* Link in */ X lp2->l_fp = lp1; X lp1->l_bp = lp2; X lp2->l_bp = lp3; X for (i=0; i<n; ++i) X lp2->l_text[i] = c; X curwp->w_dotp = lp2; X curwp->w_doto = n; X return (TRUE); X } X doto = curwp->w_doto; /* Save for later. */ X if (lp1->l_used+n > lp1->l_size) { /* Hard: reallocate */ X if ((lp2=lalloc(lp1->l_used+n)) == NULL) X return (FALSE); X cp1 = &lp1->l_text[0]; X cp2 = &lp2->l_text[0]; X while (cp1 != &lp1->l_text[doto]) X *cp2++ = *cp1++; X cp2 += n; X while (cp1 != &lp1->l_text[lp1->l_used]) X *cp2++ = *cp1++; X lp1->l_bp->l_fp = lp2; X lp2->l_fp = lp1->l_fp; X lp1->l_fp->l_bp = lp2; X lp2->l_bp = lp1->l_bp; X free((char *) lp1); X } else { /* Easy: in place */ X lp2 = lp1; /* Pretend new line */ X lp2->l_used += n; X cp2 = &lp1->l_text[lp1->l_used]; X cp1 = cp2-n; X while (cp1 != &lp1->l_text[doto]) X *--cp2 = *--cp1; X } X for (i=0; i<n; ++i) /* Add the characters */ X lp2->l_text[doto+i] = c; X wp = wheadp; /* Update windows */ X while (wp != NULL) { X if (wp->w_linep == lp1) X wp->w_linep = lp2; X if (wp->w_dotp == lp1) { X wp->w_dotp = lp2; X if (wp==curwp || wp->w_doto>doto) X wp->w_doto += n; X } X if (wp->w_markp == lp1) { X wp->w_markp = lp2; X if (wp->w_marko > doto) X wp->w_marko += n; X } X wp = wp->w_wndp; X } X return (TRUE); X} X X/* X * Insert a newline into the buffer at the current location of dot in the X * current window. The funny ass-backwards way it does things is not a botch; X * it just makes the last line in the file not a special case. Return TRUE if X * everything works out and FALSE on error (memory allocation failure). The X * update of dot and mark is a bit easier then in the above case, because the X * split forces more updating. X */ Xlnewline() X{ X register char *cp1; X register char *cp2; X register LINE *lp1; X register LINE *lp2; X register int doto; X register WINDOW *wp; X X if (curbp->b_mode&MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X lchange(WFHARD); X lp1 = curwp->w_dotp; /* Get the address and */ X doto = curwp->w_doto; /* offset of "." */ X if ((lp2=lalloc(doto)) == NULL) /* New first half line */ X return (FALSE); X cp1 = &lp1->l_text[0]; /* Shuffle text around */ X cp2 = &lp2->l_text[0]; X while (cp1 != &lp1->l_text[doto]) X *cp2++ = *cp1++; X cp2 = &lp1->l_text[0]; X while (cp1 != &lp1->l_text[lp1->l_used]) X *cp2++ = *cp1++; X lp1->l_used -= doto; X lp2->l_bp = lp1->l_bp; X lp1->l_bp = lp2; X lp2->l_bp->l_fp = lp2; X lp2->l_fp = lp1; X wp = wheadp; /* Windows */ X while (wp != NULL) { X if (wp->w_linep == lp1) X wp->w_linep = lp2; X if (wp->w_dotp == lp1) { X if (wp->w_doto < doto) X wp->w_dotp = lp2; X else X wp->w_doto -= doto; X } X if (wp->w_markp == lp1) { X if (wp->w_marko < doto) X wp->w_markp = lp2; X else X wp->w_marko -= doto; X } X wp = wp->w_wndp; X } X return (TRUE); X} X X/* X * This function deletes "n" bytes, starting at dot. It understands how do deal X * with end of lines, etc. It returns TRUE if all of the characters were X * deleted, and FALSE if they were not (because dot ran into the end of the X * buffer. The "kflag" is TRUE if the text should be put in the kill buffer. X */ Xldelete(n, kflag) X Xlong n; /* # of chars to delete */ Xint kflag; /* put killed text in kill buffer flag */ X X{ X register char *cp1; X register char *cp2; X register LINE *dotp; X register int doto; X register int chunk; X register WINDOW *wp; X X if (curbp->b_mode&MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X while (n != 0) { X dotp = curwp->w_dotp; X doto = curwp->w_doto; X if (dotp == curbp->b_linep) /* Hit end of buffer. */ X return (FALSE); X chunk = dotp->l_used-doto; /* Size of chunk. */ X if (chunk > n) X chunk = n; X if (chunk == 0) { /* End of line, merge. */ X lchange(WFHARD); X if (ldelnewline() == FALSE X || (kflag!=FALSE && kinsert('\n')==FALSE)) X return (FALSE); X --n; X continue; X } X lchange(WFEDIT); X cp1 = &dotp->l_text[doto]; /* Scrunch text. */ X cp2 = cp1 + chunk; X if (kflag != FALSE) { /* Kill? */ X while (cp1 != cp2) { X if (kinsert(*cp1) == FALSE) X return (FALSE); X ++cp1; X } X cp1 = &dotp->l_text[doto]; X } X while (cp2 != &dotp->l_text[dotp->l_used]) X *cp1++ = *cp2++; X dotp->l_used -= chunk; X wp = wheadp; /* Fix windows */ X while (wp != NULL) { X if (wp->w_dotp==dotp && wp->w_doto>=doto) { X wp->w_doto -= chunk; X if (wp->w_doto < doto) X wp->w_doto = doto; X } X if (wp->w_markp==dotp && wp->w_marko>=doto) { X wp->w_marko -= chunk; X if (wp->w_marko < doto) X wp->w_marko = doto; X } X wp = wp->w_wndp; X } X n -= chunk; X } X return (TRUE); X} X X/* X * Delete a newline. Join the current line with the next line. If the next line X * is the magic header line always return TRUE; merging the last line with the X * header line can be thought of as always being a successful operation, even X * if nothing is done, and this makes the kill buffer work "right". Easy cases X * can be done by shuffling data around. Hard cases require that lines be moved X * about in memory. Return FALSE on error and TRUE if all looks ok. Called by X * "ldelete" only. X */ Xldelnewline() X{ X register char *cp1; X register char *cp2; X register LINE *lp1; X register LINE *lp2; X register LINE *lp3; X register WINDOW *wp; X X if (curbp->b_mode&MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X lp1 = curwp->w_dotp; X lp2 = lp1->l_fp; X if (lp2 == curbp->b_linep) { /* At the buffer end. */ X if (lp1->l_used == 0) /* Blank line. */ X lfree(lp1); X return (TRUE); X } X if (lp2->l_used <= lp1->l_size-lp1->l_used) { X cp1 = &lp1->l_text[lp1->l_used]; X cp2 = &lp2->l_text[0]; X while (cp2 != &lp2->l_text[lp2->l_used]) X *cp1++ = *cp2++; X wp = wheadp; X while (wp != NULL) { X if (wp->w_linep == lp2) X wp->w_linep = lp1; X if (wp->w_dotp == lp2) { X wp->w_dotp = lp1; X wp->w_doto += lp1->l_used; X } X if (wp->w_markp == lp2) { X wp->w_markp = lp1; X wp->w_marko += lp1->l_used; X } X wp = wp->w_wndp; X } X lp1->l_used += lp2->l_used; X lp1->l_fp = lp2->l_fp; X lp2->l_fp->l_bp = lp1; X free((char *) lp2); X return (TRUE); X } X if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL) X return (FALSE); X cp1 = &lp1->l_text[0]; X cp2 = &lp3->l_text[0]; X while (cp1 != &lp1->l_text[lp1->l_used]) X *cp2++ = *cp1++; X cp1 = &lp2->l_text[0]; X while (cp1 != &lp2->l_text[lp2->l_used]) X *cp2++ = *cp1++; X lp1->l_bp->l_fp = lp3; X lp3->l_fp = lp2->l_fp; X lp2->l_fp->l_bp = lp3; X lp3->l_bp = lp1->l_bp; X wp = wheadp; X while (wp != NULL) { X if (wp->w_linep==lp1 || wp->w_linep==lp2) X wp->w_linep = lp3; X if (wp->w_dotp == lp1) X wp->w_dotp = lp3; X else if (wp->w_dotp == lp2) { X wp->w_dotp = lp3; X wp->w_doto += lp1->l_used; X } X if (wp->w_markp == lp1) X wp->w_markp = lp3; X else if (wp->w_markp == lp2) { X wp->w_markp = lp3; X wp->w_marko += lp1->l_used; X } X wp = wp->w_wndp; X } X free((char *) lp1); X free((char *) lp2); X return (TRUE); X} X X/* X * Delete all of the text saved in the kill buffer. Called by commands when a X * new kill context is being created. The kill buffer array is released, just X * in case the buffer has grown to immense size. No errors. X */ Xkdelete() X{ X KILL *kp; /* ptr to scan kill buffer chunk list */ X X if (kbufh != NULL) { X X /* first, delete all the chunks */ X kbufp = kbufh; X while (kbufp != NULL) { X kp = kbufp->d_next; X free(kbufp); X kbufp = kp; X } X X /* and reset all the kill buffer pointers */ X kbufh = kbufp = NULL; X kused = KBLOCK; X } X} X X/* X * Insert a character to the kill buffer, allocating new chunks as needed. X * Return TRUE if all is well, and FALSE on errors. X */ X Xkinsert(c) X Xint c; /* character to insert in the kill buffer */ X X{ X KILL *nchunk; /* ptr to newly malloced chunk */ X X /* check to see if we need a new chunk */ X if (kused >= KBLOCK) { X if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL) X return(FALSE); X if (kbufh == NULL) /* set head ptr if first time */ X kbufh = nchunk; X if (kbufp != NULL) /* point the current to this new one */ X kbufp->d_next = nchunk; X kbufp = nchunk; X kbufp->d_next = NULL; X kused = 0; X } X X /* and now insert the character */ X kbufp->d_chunk[kused++] = c; X return(TRUE); X} X X/* X * Yank text back from the kill buffer. This is really easy. All of the work X * is done by the standard insert routines. All you do is run the loop, and X * check for errors. Bound to "C-Y". X */ Xyank(f, n) X{ X register int c; X register int i; X register char *sp; /* pointer into string to insert */ X KILL *kp; /* pointer into kill buffer */ X X if (curbp->b_mode&MDVIEW) /* don't allow this command if */ X return(rdonly()); /* we are in read only mode */ X if (n < 0) X return (FALSE); X /* make sure there is something to yank */ X if (kbufh == NULL) X return(TRUE); /* not an error, just nothing */ X X /* for each time.... */ X while (n--) { X kp = kbufh; X while (kp != NULL) { X if (kp->d_next == NULL) X i = kused; X else X i = KBLOCK; X sp = kp->d_chunk; X while (i--) { X if ((c = *sp++) == '\n') { X if (lnewline() == FALSE) X return (FALSE); X } else { X if (linsert(1, c) == FALSE) X return (FALSE); X } X } X kp = kp->d_next; X } X } X return (TRUE); X} X X E!O!F newsize=`wc -c < line.c` if [ $newsize -ne 18815 ] then echo "File line.c was $newsize bytes, 18815 expected" fi echo 'x - lock.c (text)' sed << 'E!O!F' 's/^X//' > lock.c X/* LOCK: File locking command routines for MicroEMACS X written by Daniel Lawrence X */ X X#include <stdio.h> X#include "estruct.h" X#include "edef.h" X X#if FILOCK X#if BSD X#include <sys/errno.h> X Xextern int sys_nerr; /* number of system error messages defined */ Xextern char *sys_errlist[]; /* list of message texts */ Xextern int errno; /* current error */ X Xchar *lname[NLOCKS]; /* names of all locked files */ Xint numlocks; /* # of current locks active */ X X/* lockchk: check a file for locking and add it to the list */ X Xlockchk(fname) X Xchar *fname; /* file to check for a lock */ X X{ X register int i; /* loop indexes */ X register int status; /* return status */ X char *undolock(); X X /* check to see if that file is already locked here */ X if (numlocks > 0) X for (i=0; i < numlocks; ++i) X if (strcmp(fname, lname[i]) == 0) X return(TRUE); X X /* if we have a full locking table, bitch and leave */ X if (numlocks == NLOCKS) { X mlwrite("LOCK ERROR: Lock table full"); X return(ABORT); X } X X /* next, try to lock it */ X status = lock(fname); X if (status == ABORT) /* file is locked, no override */ X return(ABORT); X if (status == FALSE) /* locked, overriden, dont add to table */ X return(TRUE); X X /* we have now locked it, add it to our table */ X lname[++numlocks - 1] = (char *)malloc(strlen(fname) + 1); X if (lname[numlocks - 1] == NULL) { /* malloc failure */ X undolock(fname); /* free the lock */ X mlwrite("Cannot lock, out of memory"); X --numlocks; X return(ABORT); X } X X /* everthing is cool, add it to the table */ X strcpy(lname[numlocks-1], fname); X return(TRUE); X} X X/* lockrel: release all the file locks so others may edit */ X Xlockrel() X X{ X register int i; /* loop index */ X register int status; /* status of locks */ X register int s; /* status of one unlock */ X X status = TRUE; X if (numlocks > 0) X for (i=0; i < numlocks; ++i) { X if ((s = unlock(lname[i])) != TRUE) X status = s; X free(lname[i]); X } X numlocks = 0; X return(status); X} X X/* lock: Check and lock a file from access by others X returns TRUE = files was not locked and now is X FALSE = file was locked and overridden X ABORT = file was locked, abort command X*/ X Xlock(fname) X Xchar *fname; /* file name to lock */ X X{ X register char *locker; /* lock error message */ X register int status; /* return status */ X char msg[NSTRING]; /* message string */ X char *dolock(); X X /* attempt to lock the file */ X locker = dolock(fname); X if (locker == NULL) /* we win */ X return(TRUE); X X /* file failed...abort */ X if (strncmp(locker, "LOCK", 4) == 0) { X lckerror(locker); X return(ABORT); X } X X /* someone else has it....override? */ X strcpy(msg, "File in use by "); X strcat(msg, locker); X strcat(msg, ", overide?"); X status = mlyesno(msg); /* ask them */ X if (status == TRUE) X return(FALSE); X else X return(ABORT); X} X X/* unlock: Unlock a file X this only warns the user if it fails X */ X Xunlock(fname) X Xchar *fname; /* file to unlock */ X X{ X register char *locker; /* undolock return string */ X char *undolock(); X X /* unclock and return */ X locker = undolock(fname); X if (locker == NULL) X return(TRUE); X X /* report the error and come back */ X lckerror(locker); X return(FALSE); X} X Xlckerror(errstr) /* report a lock error */ X Xchar *errstr; /* lock error string to print out */ X X{ X char obuf[NSTRING]; /* output buffer for error message */ X X strcpy(obuf, errstr); X strcat(obuf, " - "); X if (errno < sys_nerr) X strcat(obuf, sys_errlist[errno]); X else X strcat(obuf, "[can not get system error message]"); X mlwrite(obuf); X} X#endif X#else Xlckhello() /* dummy function */ X{ X} X#endif E!O!F newsize=`wc -c < lock.c` if [ $newsize -ne 3565 ] then echo "File lock.c was $newsize bytes, 3565 expected" fi bill davidsen (wedu@ge-crd.arpa) {chinet | philabs | sesimo}!steinmetz!crdos1!davidsen "Stupidity, like virtue, is its own reward" -me