sources-request@mirror.UUCP (01/27/87)
Submitted by: Bob Larson <seismo!usc-oberon!blarson> Mod.sources: Volume 8, Issue 9 Archive-name: micrognu/Part02 #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # basic.c # buffer.c # cinfo.c # echo.c # extend.c # file.c # match.c # This archive created: Sat Nov 15 14:59:46 1986 export PATH; PATH=/bin:$PATH if test -f 'basic.c' then echo shar: will not over-write existing file "'basic.c'" else cat << \SHAR_EOF > 'basic.c' /* * Basic cursor motion commands. * * The routines in this file are the basic * command functions for moving the cursor around on * the screen, setting mark, and swapping dot with * mark. Only moves between lines, which might make the * current buffer framing bad, are hard. */ #include "def.h" /* * Go to beginning of line. */ /*ARGSUSED*/ gotobol(f, n, k) { curwp->w_doto = 0; return (TRUE); } /* * Move cursor backwards. Do the * right thing if the count is less than * 0. Error if you try to move back from * the beginning of the buffer. */ /*ARGSUSED*/ backchar(f, n, k) register int n; { register LINE *lp; if (n < 0) return (forwchar(f, -n, k)); while (n--) { if (curwp->w_doto == 0) { if ((lp=lback(curwp->w_dotp)) == curbp->b_linep) { if (k != KRANDOM) ewprintf("Beginning of buffer"); return (FALSE); } curwp->w_dotp = lp; curwp->w_doto = llength(lp); curwp->w_flag |= WFMOVE; } else curwp->w_doto--; } return (TRUE); } /* * Go to end of line. */ /*ARGSUSED*/ gotoeol(f, n, k) { curwp->w_doto = llength(curwp->w_dotp); return (TRUE); } /* * Move cursor forwards. Do the * right thing if the count is less than * 0. Error if you try to move forward * from the end of the buffer. */ /*ARGSUSED*/ forwchar(f, n, k) register int n; { if (n < 0) return (backchar(f, -n, k)); while (n--) { if (curwp->w_doto == llength(curwp->w_dotp)) { if (curwp->w_dotp == curbp->b_linep) { if (k != KRANDOM) ewprintf("End of buffer"); return (FALSE); } curwp->w_dotp = lforw(curwp->w_dotp); curwp->w_doto = 0; curwp->w_flag |= WFMOVE; } else curwp->w_doto++; } return (TRUE); } /* * Go to the beginning of the * buffer. Setting WFHARD is conservative, * but almost always the case. */ gotobob(f, n, k) { (VOID) setmark(f, n, k) ; curwp->w_dotp = lforw(curbp->b_linep); curwp->w_doto = 0; curwp->w_flag |= WFHARD; return (TRUE); } /* * Go to the end of the buffer. * Setting WFHARD is conservative, but * almost always the case. */ gotoeob(f, n, k) { (VOID) setmark(f, n, k) ; curwp->w_dotp = curbp->b_linep; curwp->w_doto = 0; curwp->w_flag |= WFHARD; return (TRUE); } /* * Move forward by full lines. * If the number of lines to move is less * than zero, call the backward line function to * actually do it. The last command controls how * the goal column is set. */ /*ARGSUSED*/ forwline(f, n, k) { register LINE *dlp; if (n < 0) return (backline(f, -n, KRANDOM)); if ((lastflag&CFCPCN) == 0) /* Fix goal. */ setgoal(); thisflag |= CFCPCN; dlp = curwp->w_dotp; while (n-- && dlp!=curbp->b_linep) dlp = lforw(dlp); curwp->w_dotp = dlp; curwp->w_doto = getgoal(dlp); curwp->w_flag |= WFMOVE; return (TRUE); } /* * This function is like "forwline", but * goes backwards. The scheme is exactly the same. * Check for arguments that are less than zero and * call your alternate. Figure out the new line and * call "movedot" to perform the motion. */ /*ARGSUSED*/ backline(f, n, k) { register LINE *dlp; if (n < 0) return (forwline(f, -n, KRANDOM)); if ((lastflag&CFCPCN) == 0) /* Fix goal. */ setgoal(); thisflag |= CFCPCN; dlp = curwp->w_dotp; while (n-- && lback(dlp)!=curbp->b_linep) dlp = lback(dlp); curwp->w_dotp = dlp; curwp->w_doto = getgoal(dlp); curwp->w_flag |= WFMOVE; return (TRUE); } /* * Set the current goal column, * which is saved in the external variable "curgoal", * to the current cursor column. The column is never off * the edge of the screen; it's more like display then * show position. */ setgoal() { curgoal = getcolpos() - 1; /* Get the position. */ if (curgoal >= ncol) /* Chop to tty width. */ curgoal = ncol-1; } /* * This routine looks at a line (pointed * to by the LINE pointer "dlp") and the current * vertical motion goal column (set by the "setgoal" * routine above) and returns the best offset to use * when a vertical motion is made into the line. */ getgoal(dlp) register LINE *dlp; { register int c; register int col; register int newcol; register int dbo; col = 0; dbo = 0; while (dbo != llength(dlp)) { c = lgetc(dlp, dbo); newcol = col; if (c == '\t') newcol |= 0x07; else if (ISCTRL(c) != FALSE) ++newcol; ++newcol; if (newcol > curgoal) break; col = newcol; ++dbo; } return (dbo); } /* * Scroll forward by a specified number * of lines, or by a full page if no argument. * The "2" is the window overlap (this is the default * value from ITS EMACS). Because the top line in * the window is zapped, we have to do a hard * update and get it back. */ /*ARGSUSED*/ forwpage(f, n, k) register int n; { register LINE *lp; if (f == FALSE) { n = curwp->w_ntrows - 2; /* Default scroll. */ if (n <= 0) /* Forget the overlap */ n = 1; /* if tiny window. */ } else if (n < 0) return (backpage(f, -n, KRANDOM)); #ifdef CVMVAS else /* Convert from pages */ n *= curwp->w_ntrows; /* to lines. */ #endif lp = curwp->w_linep; while (n-- && lp!=curbp->b_linep) lp = lforw(lp); curwp->w_linep = lp; curwp->w_dotp = lp; curwp->w_doto = 0; curwp->w_flag |= WFHARD; return (TRUE); } /* * This command is like "forwpage", * but it goes backwards. The "2", like above, * is the overlap between the two windows. The * value is from the ITS EMACS manual. The * hard update is done because the top line in * the window is zapped. */ /*ARGSUSED*/ backpage(f, n, k) register int n; { register LINE *lp; if (f == FALSE) { n = curwp->w_ntrows - 2; /* Default scroll. */ if (n <= 0) /* Don't blow up if the */ n = 1; /* window is tiny. */ } else if (n < 0) return (forwpage(f, -n, KRANDOM)); #ifdef CVMVAS else /* Convert from pages */ n *= curwp->w_ntrows; /* to lines. */ #endif lp = curwp->w_linep; while (n-- && lback(lp)!=curbp->b_linep) lp = lback(lp); curwp->w_linep = lp; curwp->w_dotp = lp; curwp->w_doto = 0; curwp->w_flag |= WFHARD; return (TRUE); } /* * Page the other window. Check to make sure it exists, then * nextwind, forwpage and prevwind. */ pagenext(f, n, k) { if (wheadp->w_wndp == NULL) { ewprintf("No other window"); return FALSE; } (VOID) nextwind(f, n, k); (VOID) forwpage(f, n, k); (VOID) prevwind(f, n, k); return TRUE; } /* * Set the mark in the current window * to the value of dot. A message is written to * the echo line unless we are running in a keyboard * macro, when it would be silly. */ /*ARGSUSED*/ setmark(f, n, k) { isetmark(); if (kbdmop == NULL) ewprintf("Mark set"); return (TRUE); } /* * Internal set mark routine, used by other functions (daveb). */ isetmark() { curwp->w_markp = curwp->w_dotp; curwp->w_marko = curwp->w_doto; } /* * Swap the values of "dot" and "mark" in * the current window. This is pretty easy, because * all of the hard work gets done by the standard routine * that moves the mark about. The only possible * error is "no mark". */ /*ARGSUSED*/ swapmark(f, n, k) { register LINE *odotp; register int odoto; if (curwp->w_markp == NULL) { ewprintf("No mark in this window"); return (FALSE); } odotp = curwp->w_dotp; odoto = curwp->w_doto; curwp->w_dotp = curwp->w_markp; curwp->w_doto = curwp->w_marko; curwp->w_markp = odotp; curwp->w_marko = odoto; curwp->w_flag |= WFMOVE; return (TRUE); } /* * Go to a specific line, mostly for * looking up errors in C programs, which give the * error a line number. If an argument is present, then * it is the line number, else prompt for a line number * to use. */ /*ARGSUSED*/ gotoline(f, n, k) register int n; { register LINE *clp; register int s; char buf[32]; if (f == FALSE) { if ((s=ereply("Goto line: ", buf, sizeof(buf))) != TRUE) return (s); n = atoi(buf); } clp = lforw(curbp->b_linep); /* "clp" is first line */ while (n > 1) { if (lforw(clp) == curbp->b_linep) break; clp = lforw(clp); --n; } curwp->w_dotp = clp; curwp->w_doto = 0; curwp->w_flag |= WFMOVE; return (TRUE); } SHAR_EOF fi # end of overwriting check if test -f 'buffer.c' then echo shar: will not over-write existing file "'buffer.c'" else cat << \SHAR_EOF > 'buffer.c' /* * Buffer handling. */ #include "def.h" /* * Attach a buffer to a window. The * values of dot and mark come from the buffer * if the use count is 0. Otherwise, they come * from some other window. */ /*ARGSUSED*/ usebuffer(f, n, k) { register BUFFER *bp; register int s; char bufn[NBUFN]; /* Get buffer to use from user */ if (curbp->b_altb != NULL) s=eread("Switch to buffer: (default %s) ", bufn, NBUFN, EFNEW|EFBUF, #ifdef VARARGS curbp->b_altb->b_bname #else &(curbp->b_altb->b_bname), (char *) NULL #endif ) ; else s=eread("Switch to buffer: ", bufn, NBUFN, EFNEW|EFBUF #ifndef VARARGS ,(char *) NULL #endif ); if (s == ABORT) return (s); if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb ; else if ((bp=bfind(bufn, TRUE)) == NULL) return (FALSE); /* and put it in current window */ curbp = bp; return showbuffer(bp, curwp, WFFORCE|WFHARD); } /* * pop to buffer asked for by the user. */ /*ARGSUSED*/ poptobuffer(f, n, k) { register BUFFER *bp; register WINDOW *wp; register int s; char bufn[NBUFN]; /* Get buffer to use from user */ if (curbp->b_altb != NULL) s=eread("Switch to buffer in other window: (default %s) ", bufn, NBUFN, EFNEW|EFBUF, #ifdef VARARGS curbp->b_altb->b_bname #else &(curbp->b_altb->b_bname) ,(char *) NULL #endif ) ; else s=eread("Switch to buffer in other window: ", bufn, NBUFN, EFNEW|EFBUF #ifndef VARARGS , (char *) NULL #endif ) ; if (s == ABORT) return (s); if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb ; else if ((bp=bfind(bufn, TRUE)) == NULL) return (FALSE); /* and put it in a new window */ if ((wp = popbuf(bp)) == NULL) return FALSE; curbp = bp; curwp = wp; return TRUE; } /* * Dispose of a buffer, by name. * Ask for the name. Look it up (don't get too * upset if it isn't there at all!). Clear the buffer (ask * if the buffer has been changed). Then free the header * line and the buffer header. Bound to "C-X K". */ /*ARGSUSED*/ killbuffer(f, n, k) { register BUFFER *bp; register BUFFER *bp1; register BUFFER *bp2; register WINDOW *wp; register int s; char bufn[NBUFN]; if ((s=eread("Kill buffer: (default %s) ", bufn, NBUFN, EFNEW|EFBUF, #ifdef VARARGS curbp->b_bname #else &(curbp->b_bname) #endif )) == ABORT) return (s); else if (s == FALSE) bp = curbp ; else if ((bp=bfind(bufn, FALSE)) == NULL) return FALSE; if (bp->b_nwnd != 0) { if ((bp1 = bp->b_altb) == NULL) return FALSE; if (bclear(bp) != TRUE) return TRUE; for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) { /* Special case - could use showbuf, but don't */ if (wp->w_bufp == bp) { --bp->b_nwnd; ++bp1->b_nwnd; wp->w_bufp = bp1 ; wp->w_dotp = bp1->b_dotp; wp->w_doto = bp1->b_doto; wp->w_markp = bp1->b_markp; wp->w_marko = bp1->b_marko; wp->w_linep = bp1->b_linep; wp->w_flag |= WFMODE|WFFORCE|WFHARD; } } } else if (bclear(bp) != TRUE) return TRUE; if (bp == curbp) curbp = bp->b_altb; free((char *) bp->b_linep); /* Release header line. */ bp1 = NULL; /* Find the header. */ bp2 = bheadp; while (bp2 != bp) { if (bp2->b_altb == bp) bp2->b_altb = (bp->b_altb == bp2) ? NULL : bp->b_altb; bp1 = bp2; bp2 = bp2->b_bufp; } bp2 = bp2->b_bufp; /* Next one in chain. */ if (bp1 == NULL) /* Unlink it. */ bheadp = bp2; else bp1->b_bufp = bp2; while (bp2 != NULL) { /* Finish with altb's */ if (bp2->b_altb == bp) bp2->b_altb = (bp->b_altb == bp2) ? NULL : bp->b_altb; bp2 = bp2->b_bufp; } free(bp->b_bname); /* Release name block */ free((char *) bp); /* Release buffer block */ return (TRUE); } /* * Save some buffers - just call anycb with the arg flag. */ /*ARGSUSED*/ savebuffers(f, n, k) { if (anycb(f) == ABORT) return ABORT; return TRUE; } /* * Display the buffer list. This is done * in two parts. The "makelist" routine figures out * the text, and puts it in a buffer. "popbuf" * then pops the data onto the screen. Bound to * "C-X C-B". */ /*ARGSUSED*/ listbuffers(f, n, k) { register BUFFER *bp; register WINDOW *wp; BUFFER *makelist(); WINDOW *popb(); if ((bp=makelist()) == NULL || (wp=popbuf(bp)) == NULL) return FALSE; wp->w_dotp = bp->b_dotp; /* fix up if window already on screen */ wp->w_doto = bp->b_doto; return TRUE; } /* * This routine rebuilds the text for the * list buffers command. Return TRUE if * everything works. Return FALSE if there * is an error (if there is no memory). */ BUFFER * makelist() { register char *cp1; register char *cp2; register int c; register BUFFER *bp; register LINE *lp; register RSIZE nbytes; BUFFER *blp; char b[6+1]; char line[128]; if ((blp = bfind("*Buffer List*", TRUE)) == NULL) return NULL; if (bclear(blp) != TRUE) return NULL; blp->b_flag &= ~BFCHG; /* Blow away old. */ (VOID) strcpy(line, " MR Buffer"); cp1 = line + 10; while(cp1 < line + 4 + NBUFN + 1) *cp1++ = ' '; (VOID) strcpy(cp1, "Size File"); if (addline(blp, line) == FALSE) return NULL; (VOID) strcpy(line, " -- ------"); cp1 = line + 10; while(cp1 < line + 4 + NBUFN + 1) *cp1++ = ' '; (VOID) strcpy(cp1, "---- ----"); if (addline(blp, line) == FALSE) return NULL; bp = bheadp; /* For all buffers */ while (bp != NULL) { cp1 = &line[0]; /* Start at left edge */ *cp1++ = (bp == curbp) ? '.' : ' '; *cp1++ = ((bp->b_flag&BFCHG) != 0) ? '*' : ' '; *cp1++ = ' '; /* Gap. */ *cp1++ = ' '; cp2 = &bp->b_bname[0]; /* Buffer name */ while ((c = *cp2++) != 0) *cp1++ = c; while (cp1 < &line[4+NBUFN+1]) *cp1++ = ' '; nbytes = 0; /* Count bytes in buf. */ if (bp != blp) { lp = lforw(bp->b_linep); while (lp != bp->b_linep) { nbytes += llength(lp)+1; lp = lforw(lp); } } (VOID) itor(b, 6, nbytes); /* 6 digit buffer size. */ cp2 = &b[0]; while ((c = *cp2++) != 0) *cp1++ = c; *cp1++ = ' '; /* Gap.. */ cp2 = &bp->b_fname[0]; /* File name */ if (*cp2 != 0) { while ((c = *cp2++) != 0) { if (cp1 < &line[128-1]) *cp1++ = c; } } *cp1 = 0; /* Add to the buffer. */ if (addline(blp, line) == FALSE) return NULL; bp = bp->b_bufp; } blp->b_dotp = lforw(blp->b_linep); /* put dot at beginning of buffer */ blp->b_doto = 0; return blp; /* All done */ } /* * Used above. */ static itor(buf, width, num) register char buf[]; register int width; register RSIZE num; { register RSIZE r; if (num / 10 == 0) { buf[0] = (num % 10) + '0'; for (r = 1; r < width; buf[r++] = ' ') ; buf[width] = '\0'; return 1; } else { buf[r = itor(buf, width, num / (RSIZE)10)] = (num % (RSIZE)10) + '0'; return r + 1; } } /* * The argument "text" points to * a string. Append this line to the * buffer. Handcraft the EOL * on the end. Return TRUE if it worked and * FALSE if you ran out of room. */ addline(bp, text) register BUFFER *bp; char *text; { register LINE *lp; register int i; register int ntext; ntext = strlen(text); if ((lp=lalloc((RSIZE) ntext)) == NULL) return (FALSE); for (i=0; i<ntext; ++i) lputc(lp, i, text[i]); bp->b_linep->l_bp->l_fp = lp; /* Hook onto the end */ lp->l_bp = bp->b_linep->l_bp; bp->b_linep->l_bp = lp; lp->l_fp = bp->b_linep; if (bp->b_dotp == bp->b_linep) /* If "." is at the end */ bp->b_dotp = lp; /* move it to new line */ if (bp->b_markp == bp->b_linep) /* ditto for mark */ bp->b_markp = lp; return (TRUE); } /* * Look through the list of buffers, giving the user * a chance to save them. Return TRUE if there are * any changed buffers afterwards. Buffers that don't * have an associated file don't count. Return FALSE * if there are no changed buffers. */ anycb(f) { register BUFFER *bp; register int s = FALSE, save = FALSE; char prompt[NFILEN + 11]; for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { if (*(bp->b_fname) != '\0' && (bp->b_flag&BFCHG) != 0) { (VOID) strcpy(prompt, "Save file "); (VOID) strcpy(prompt + 10, bp->b_fname); if ((f == TRUE || (save = eyorn(prompt)) == TRUE) && writeout(bp, bp->b_fname) == TRUE) { bp->b_flag &= ~BFCHG; upmodes(bp); } else s = TRUE; if (save == ABORT) return (save); save = TRUE; } } if (save == FALSE && kbdmop == NULL) ewprintf("(No files need saving)"); return s; } /* * Search for a buffer, by name. * If not found, and the "cflag" is TRUE, * create a buffer and put it in the list of * all buffers. Return pointer to the BUFFER * block for the buffer. */ BUFFER * bfind(bname, cflag) register char *bname; { register BUFFER *bp; char *malloc(); register LINE *lp; bp = bheadp; while (bp != NULL) { if (strcmp(bname, bp->b_bname) == 0) return (bp); bp = bp->b_bufp; } if (cflag!=TRUE) return NULL; /*NOSTRICT*/ if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL) { ewprintf("Can't get %d bytes", sizeof(BUFFER)); return NULL; } if ((bp->b_bname=malloc(strlen(bname)+1)) == NULL) { ewprintf("Can't get %d bytes", strlen(bname)+1); free((char *) bp); return NULL; } if ((lp=lalloc((RSIZE) 0)) == NULL) { free(bp->b_bname); free((char *) bp); return NULL; } bp->b_altb = bp->b_bufp = NULL; bp->b_dotp = lp; bp->b_doto = 0; bp->b_markp = NULL; bp->b_marko = 0; bp->b_flag = 0; bp->b_nwnd = 0; bp->b_linep = lp; (VOID) strcpy(bp->b_fname, ""); (VOID) strcpy(bp->b_bname, bname); lp->l_fp = lp; lp->l_bp = lp; bp->b_bufp = bheadp; bheadp = bp; return (bp); } /* * This routine blows away all of the text * in a buffer. If the buffer is marked as changed * then we ask if it is ok to blow it away; this is * to save the user the grief of losing text. The * window chain is nearly always wrong if this gets * called; the caller must arrange for the updates * that are required. Return TRUE if everything * looks good. */ bclear(bp) register BUFFER *bp; { register LINE *lp; register int s; if ((bp->b_flag&BFCHG) != 0 /* Changed. */ && (s=eyesno("Buffer modified; kill anyway")) != TRUE) return (s); bp->b_flag &= ~BFCHG; /* Not changed */ while ((lp=lforw(bp->b_linep)) != bp->b_linep) lfree(lp); bp->b_dotp = bp->b_linep; /* Fix "." */ bp->b_doto = 0; bp->b_markp = NULL; /* Invalidate "mark" */ bp->b_marko = 0; return (TRUE); } /* * Display the given buffer in the given window. Flags indicated * action on redisplay. */ showbuffer(bp, wp, flags) register BUFFER *bp; register WINDOW *wp; { register BUFFER *obp; register WINDOW *owp; if (wp->w_bufp == bp) { /* Easy case! */ wp->w_flag |= flags; return TRUE ; } /* First, dettach the old buffer from the window */ if ((bp->b_altb = obp = wp->w_bufp) != NULL) { if (--obp->b_nwnd == 0) { obp->b_dotp = wp->w_dotp; obp->b_doto = wp->w_doto; obp->b_markp = wp->w_markp; obp->b_marko = wp->w_marko; } } /* Now, attach the new buffer to the window */ wp->w_bufp = bp; if (bp->b_nwnd++ == 0) { /* First use. */ wp->w_dotp = bp->b_dotp; wp->w_doto = bp->b_doto; wp->w_markp = bp->b_markp; wp->w_marko = bp->b_marko; } else /* already on screen, steal values from other window */ for (owp = wheadp; owp != NULL; owp = wp->w_wndp) if (wp->w_bufp == bp && owp != wp) { wp->w_dotp = owp->w_dotp; wp->w_doto = owp->w_doto; wp->w_markp = owp->w_markp; wp->w_marko = owp->w_marko; break; } wp->w_flag |= WFMODE|flags; return TRUE; } /* * Pop the buffer we got passed onto the screen. * Returns a status. */ WINDOW * popbuf(bp) register BUFFER *bp; { register WINDOW *wp; if (bp->b_nwnd == 0) { /* Not on screen yet. */ if ((wp=wpopup()) == NULL) return NULL; } else for (wp = wheadp; wp != NULL; wp = wp->w_wndp) if (wp->w_bufp == bp) { wp->w_flag |= WFHARD|WFFORCE; return wp ; } if (showbuffer(bp, wp, WFHARD) != TRUE) return NULL; return wp; } /* * Insert another buffer at dot. Very useful. */ bufferinsert(f, n, k) { register BUFFER *bp; register LINE *clp; register int clo; register int nline; int s; char bufn[NBUFN]; /* Get buffer to use from user */ if (curbp->b_altb != NULL) s=eread("Insert buffer: (default %s) ", bufn, NBUFN, EFNEW|EFBUF, &(curbp->b_altb->b_bname), (char *) NULL) ; else s=eread("Insert buffer: ", bufn, NBUFN, EFNEW|EFBUF, (char *) NULL) ; if (s == ABORT) return (s); if (s == FALSE && curbp->b_altb != NULL) bp = curbp->b_altb; else if ((bp=bfind(bufn, FALSE)) == NULL) return (FALSE); if (bp==curbp) { ewprintf("Cannot insert buffer into self"); return (FALSE); } /* insert the buffer */ nline = 0; for (clp = lforw(bp->b_linep); clp != bp->b_linep; clp = lforw(clp)) { for (clo = 0; clo < llength(clp); clo++) if (linsert((RSIZE)1, lgetc(clp, clo)) == FALSE) return (FALSE); if (newline(FALSE, 1, KRANDOM) == FALSE) /* fake newline */ return (FALSE); nline++; } if (kbdmop==NULL) { if (nline == 1) ewprintf("[Inserted 1 line]"); else ewprintf("[Inserted %d lines]", nline); } clp = curwp->w_linep; /* cosmetic adjustment */ if (curwp->w_dotp == clp) { /* for offscreen insert */ while (nline-- && lback(clp)!=curbp->b_linep) clp = lback(clp); curwp->w_linep = clp; /* adjust framing. */ curwp->w_flag |= WFHARD; } return (TRUE); } /* * Turn off the dirty bit on this buffer. */ /*ARGSUSED*/ notmodified(f, n, k) { register WINDOW *wp; curbp->b_flag &= ~BFCHG; wp = wheadp; /* Update mode lines. */ while (wp != NULL) { if (wp->w_bufp == curbp) wp->w_flag |= WFMODE; wp = wp->w_wndp; } ewprintf("Modification-flag cleared"); return (TRUE); } SHAR_EOF fi # end of overwriting check if test -f 'cinfo.c' then echo shar: will not over-write existing file "'cinfo.c'" else cat << \SHAR_EOF > 'cinfo.c' /* * Character class tables. * Do it yourself character classification * macros, that understand the multinational character set, * and let me ask some questions the standard macros (in * ctype.h) don't let you ask. */ #include "def.h" /* * This table, indexed by a character drawn * from the 256 member character set, is used by my * own character type macros to answer questions about the * type of a character. It handles the full multinational * character set, and lets me ask some questions that the * standard "ctype" macros cannot ask. */ char cinfo[256] = { _C, _C, _C, _C, /* 0x0X */ _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, /* 0x1X */ _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, _C, 0, _P, 0, 0, /* 0x2X */ _W, _W, 0, _W, 0, 0, 0, 0, 0, 0, _P, 0, _W, _W, _W, _W, /* 0x3X */ _W, _W, _W, _W, _W, _W, 0, 0, 0, 0, 0, _P, 0, _U|_W, _U|_W, _U|_W, /* 0x4X */ _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, /* 0x5X */ _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, 0, 0, 0, 0, 0, 0, _L|_W, _L|_W, _L|_W, /* 0x6X */ _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, /* 0x7X */ _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, 0, 0, 0, 0, _C, 0, 0, 0, 0, /* 0x8X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x9X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xAX */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xBX */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _U|_W, _U|_W, _U|_W, _U|_W, /* 0xCX */ _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, 0, _U|_W, _U|_W, _U|_W, /* 0xDX */ _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, _U|_W, 0, _W, _L|_W, _L|_W, _L|_W, _L|_W, /* 0xEX */ _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, 0, _L|_W, _L|_W, _L|_W, /* 0xFX */ _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, _L|_W, 0, 0 }; SHAR_EOF fi # end of overwriting check if test -f 'echo.c' then echo shar: will not over-write existing file "'echo.c'" else cat << \SHAR_EOF > 'echo.c' /* * Echo line reading and writing. * * Common routines for reading * and writing characters in the echo line area * of the display screen. Used by the entire * known universe. */ #include "def.h" #ifdef VARARGS # include <varargs.h> static veread(); #endif static eformat(); int epresf = FALSE; /* Stuff in echo line flag. */ /* * Erase the echo line. */ eerase() { ttcolor(CTEXT); ttmove(nrow-1, 0); tteeol(); ttflush(); epresf = FALSE; } /* * Ask "yes" or "no" question. * Return ABORT if the user answers the question * with the abort ("^G") character. Return FALSE * for "no" and TRUE for "yes". No formatting * services are available. No newline required. */ eyorn(sp) char *sp; { register KEY s; if (kbdmop == NULL) ewprintf("%s? (y or n) ", sp); for (;;) { s = getkey(0); if (s == 'y' || s == 'Y') return (TRUE); if (s == 'n' || s == 'N') return (FALSE); if (s == (KCTRL|'G') || s == (KCTLX|KCTRL|'G') || s == (KMETA|KCTRL|'G')) { (VOID) ctrlg(FALSE, 1, KRANDOM); return ABORT; } if (kbdmop == NULL) ewprintf("Please answer y or n. %s? (y or n) ", sp); } } /* * Like eyorn, but for more important question. User must type either all of * "yes" or "no", and the trainling newline. */ eyesno(sp) char *sp; { register int s; char buf[64]; s = ereply("%s? (yes or no) ", buf, sizeof(buf), sp); for (;;) { if (s == ABORT) return ABORT; if (s != FALSE) { if ((buf[0] == 'y' || buf[0] == 'Y') && (buf[1] == 'e' || buf[1] == 'E') && (buf[2] == 's' || buf[2] == 'S')) return TRUE; if ((buf[0] == 'n' || buf[0] == 'N') && (buf[1] == 'o' || buf[0] == 'O')) return FALSE; } s = ereply("Please answer yes or no. %s? (yes or no) ", buf, sizeof(buf), sp); } } /* * Write out a prompt, and read back a * reply. The prompt is now written out with full "ewprintf" * formatting, although the arguments are in a rather strange * place. This is always a new message, there is no auto * completion, and the return is echoed as such. */ #ifdef VARARGS ereply(va_alist) va_dcl { register int i; va_list pvar; register char *fp, *buf; register int nbuf; va_start(pvar); fp = va_arg(pvar, char *); buf = va_arg(pvar, char *); nbuf = va_arg(pvar, int); i = veread(fp, buf, nbuf, EFNEW|EFCR, &pvar); va_end(pvar); return i; } #else /* VARARGS3 */ ereply(fp, buf, nbuf, arg) char *fp, *buf; int nbuf; long arg; { return (eread(fp, buf, nbuf, EFNEW|EFCR, (char *)&arg)); } #endif /* * This is the general "read input from the * echo line" routine. The basic idea is that the prompt * string "prompt" is written to the echo line, and a one * line reply is read back into the supplied "buf" (with * maximum length "len"). The "flag" contains EFNEW (a * new prompt), an EFFUNC (autocomplete), or EFCR (echo * the carriage return as CR). */ /* VARARGS4 */ #ifdef VARARGS eread(va_alist) va_dcl { va_list pvar; char *fp, *buf; int nbuf, flag, i; va_start(pvar); fp = va_arg(pvar, char *); buf = va_arg(pvar, char *); nbuf = va_arg(pvar, int); flag = va_arg(pvar, int); i = veread(fp, buf, nbuf, flag, &pvar); va_end(pvar); return i; } #endif #ifdef VARARGS static veread(fp, buf, nbuf, flag, ap) char *fp; char *buf; va_list *ap; { #else eread(fp, buf, nbuf, flag, ap) char *fp; char *buf; char *ap; { #endif register int cpos; register int i; register KEY c; cpos = 0; if (kbdmop != NULL) { /* In a macro. */ while ((c = *kbdmop++) != '\0') buf[cpos++] = (char) c; buf[cpos] = '\0'; goto done; } if ((flag&EFNEW)!=0 || ttrow!=nrow-1) { ttcolor(CTEXT); ttmove(nrow-1, 0); epresf = TRUE; } else eputc(' '); eformat(fp, ap); tteeol(); ttflush(); for (;;) { c = getkey(KQUOTE|KNOMAC); if ((flag&EFAUTO) != 0 && (c == ' ' || c == CCHR('I'))) { cpos += complete(flag, c, buf, cpos); continue; } switch (c) { case 0x0D: /* Return, done. */ if ((flag&EFFUNC) != 0) { if ((i = complete(flag, c, buf, cpos)) == 0) continue; if (i > 0) cpos += i; } buf[cpos] = '\0'; if ((flag&EFCR) != 0) { ttputc(0x0D); ttflush(); } if (kbdmip != NULL) { if (kbdmip+cpos+1 > &kbdm[NKBDM-3]) { ewprintf("Keyboard macro overflow"); ttflush(); return FALSE; } for (i = 0; i <= cpos; ++i) *kbdmip++ = (KEY) buf[i]; } goto done; case CCHR('G'): /* Bell, abort. */ eputc(CCHR('G')); (VOID) ctrlg(FALSE, 0, KRANDOM); ttflush(); return (ABORT); case 0x7F: /* Rubout, erase. */ if (cpos != 0) { ttputc('\b'); ttputc(' '); ttputc('\b'); --ttcol; if (ISCTRL(buf[--cpos]) != FALSE) { ttputc('\b'); ttputc(' '); ttputc('\b'); --ttcol; } ttflush(); } break; case CCHR('X'): /* C-X */ case CCHR('U'): /* C-U, kill line. */ while (cpos != 0) { ttputc('\b'); ttputc(' '); ttputc('\b'); --ttcol; if (ISCTRL(buf[--cpos]) != FALSE) { ttputc('\b'); ttputc(' '); ttputc('\b'); --ttcol; } } ttflush(); break; case CCHR('Q'): /* C-Q, quote next */ c = getkey(KQUOTE|KNOMAC) ; default: /* All the rest. */ if (cpos < nbuf-1) { buf[cpos++] = (char) c; eputc((char) c); ttflush(); } } } done: if (buf[0] == '\0') return (FALSE); return (TRUE); } /* * do completion on a list of objects. */ complete(flags, c, buf, cpos) register char *buf; register int cpos; { register LIST *lh, *lh2; #ifndef MANX register /* too many registers mess Manx up */ #endif int i, nxtra; int nhits, bxtra; int wflag = FALSE; int msglen, nshown; char *msg; if ((flags&EFFUNC) != 0) lh = &(symbol[0]->s_list); else if ((flags&EFBUF) != 0) lh = &(bheadp->b_list); else panic("broken complete call: flags"); if (c == ' ') wflag = TRUE; else if (c != '\t' && c != 0x0D) panic("broken complete call: c"); nhits = 0; nxtra = HUGE; while (lh != NULL) { for (i=0; i<cpos; ++i) { if (buf[i] != lh->l_name[i]) break; } if (i == cpos) { if (nhits == 0) lh2 = lh; ++nhits; if (lh->l_name[i] == '\0') nxtra = -1; else { bxtra = getxtra(lh, lh2, cpos, wflag); if (bxtra < nxtra) nxtra = bxtra; lh2 = lh; } } lh = lh->l_next; } if (nhits == 0) msg = " [No match]"; else if (nhits > 1 && nxtra == 0) msg = " [Ambiguous]"; else { /* Got a match, do it to it */ /* * Being lazy - ought to check length, but all things * autocompleted have known types/lengths. */ if (nxtra < 0 && nhits > 1 && c == ' ') nxtra = 1; for (i = 0; i < nxtra; ++i) { buf[cpos] = lh2->l_name[cpos]; eputc(buf[cpos++]); } ttflush(); if (nxtra < 0 && c != 0x0D) return 0; return nxtra; } /* Set up backspaces, etc., being mindful of echo line limit */ msglen = strlen(msg); nshown = (ttcol + msglen + 2 > ncol) ? ncol - ttcol - 2 : msglen; eputs(msg); ttcol -= (i = nshown); /* update ttcol! */ while (i--) /* move back before msg */ ttputc('\b'); ttflush(); /* display to user */ i = nshown; while (i--) /* blank out on next flush */ eputc(' '); ttcol -= (i = nshown); /* update ttcol on BS's */ while (i--) ttputc('\b'); /* update ttcol again! */ return 0; } /* * The "lp1" and "lp2" point to list structures. The * "cpos" is a horizontal position in the name. * Return the longest block of characters that can be * autocompleted at this point. Sometimes the two * symbols are the same, but this is normal. */ getxtra(lp1, lp2, cpos, wflag) register LIST *lp1, *lp2; register int wflag; { register int i; i = cpos; for (;;) { if (lp1->l_name[i] != lp2->l_name[i]) break; if (lp1->l_name[i] == '\0') break; ++i; if (wflag && !ISWORD(lp1->l_name[i-1])) break; } return (i - cpos); } /* * Special "printf" for the echo line. * Each call to "ewprintf" starts a new line in the * echo area, and ends with an erase to end of the * echo line. The formatting is done by a call * to the standard formatting routine. */ #ifdef VARARGS ewprintf(va_alist) va_dcl { va_list pvar; register char *fp; va_start(pvar); fp = va_arg(pvar, char *); #else /* VARARGS1 */ ewprintf(fp, arg) char *fp; { #endif ttcolor(CTEXT); ttmove(nrow-1, 0); #ifdef VARARGS eformat(fp, &pvar); va_end(pvar); #else eformat(fp, (char *)&arg); #endif tteeol(); ttflush(); epresf = TRUE; } /* * Printf style formatting. This is * called by both "ewprintf" and "ereply" to provide * formatting services to their clients. The move to the * start of the echo line, and the erase to the end of * the echo line, is done by the caller. * Note: %c works, and prints the "name" of the key. However * the key must be cast to an int to avoid tripping over * various oddities in C argument passing. */ static eformat(fp, ap) register char *fp; #ifdef VARARGS register va_list *ap; #else register char *ap; #endif { register int c; char kname[NKNAME]; while ((c = *fp++) != '\0') { if (c != '%') eputc(c); else { c = *fp++; switch (c) { case 'c': #ifdef VARARGS keyname(kname, va_arg(*ap, int)); #else /*NOSTRICT*/ keyname(kname, *(int *)ap); ap += sizeof(int); #endif eputs(kname); break; case 'd': #ifdef VARARGS eputi(va_arg(*ap, int), 10); #else /*NOSTRICT*/ eputi(*(int *)ap, 10); ap += sizeof(int); #endif break; case 'o': #ifdef VARARGS eputi(va_arg(*ap, int), 8); #else /*NOSTRICT*/ eputi(*(int *)ap, 8); ap += sizeof(int); #endif break; case 's': #ifdef VARARGS eputs(va_arg(*ap, char *)); #else /*NOSTRICT*/ eputs(*(char **)ap); ap += sizeof(char *); #endif break; case 'l':/* explicit longword */ c = *fp++; switch(c) { case 'd': #ifdef VARARGS eputl((long)va_arg(*ap, long), 10L); #else /*NOSTRICT*/ eputl(*(long *)ap, 10L); ap += sizeof(long); #endif break; default: eputc(c); break; } break; default: eputc(c); } } } } /* * Put integer, in radix "r". */ static eputi(i, r) register int i; register int r; { register int q; if ((q=i/r) != 0) eputi(q, r); eputc(i%r+'0'); } /* * Put long, in radix "r". */ static eputl(l, r) register long l; register long r; { register long q; if ((q=l/r) != 0) eputl(q, r); eputc((int)(l%r)+'0'); } /* * Put string. */ eputs(s) register char *s; { register int c; while ((c = *s++) != '\0') eputc(c); } /* * Put character. Watch for * control characters, and for the line * getting too long. */ static eputc(c) register char c; { if (ttcol+2 < ncol) { if (ISCTRL(c) != FALSE) { eputc('^'); c ^= 0x40; } ttputc(c); ++ttcol; } } SHAR_EOF fi # end of overwriting check if test -f 'extend.c' then echo shar: will not over-write existing file "'extend.c'" else cat << \SHAR_EOF > 'extend.c' /* * Extended (M-X) commands. */ #include "def.h" /* * This function modifies the keyboard * binding table, by adjusting the entries in the * big "bindings" array. Most of the grief deals with the * prompting for additional arguments. */ /*ARGSUSED*/ bindtokey(f, n, k) { register int s; register SYMBOL *sp; int c; char xname[NXNAME]; if (kbdmop == NULL) ewprintf("Set key globally: ") ; c = (int) getkey(0); if ((s=eread("Set key %c to command: ", xname, NXNAME, EFNEW|EFFUNC, #ifdef VARARGS c #else (char *) &c, (char *) NULL #endif )) != TRUE) return (s); if ((sp=symlookup(xname)) == NULL) { ewprintf("[No match]"); return (FALSE); } binding[(KEY) c] = sp; /* rebind new. */ return (TRUE); } /* * User function to unbind keys. Just call the unbind we already have. */ /*ARGSUSED*/ unsetkey(f, n, k) { register KEY key; if (kbdmop == NULL) ewprintf("Unset key globally: ") ; key = getkey(0); if (key == (KCTRL|'G') || key == (KCTLX|KCTRL|'G') || key == (KMETA|KCTRL|'G')) { (VOID) ctrlg(FALSE, 1, KRANDOM); return ABORT; } binding[key] = NULL; return TRUE; } /* * Extended command. Call the message line * routine to read in the command name and apply autocompletion * to it. When it comes back, look the name up in the symbol table * and run the command if it is found and has the right type. * Print an error if there is anything wrong. */ /*ARGSUSED*/ extend(f, n, k) { register SYMBOL *sp; register int s; char xname[NXNAME]; if (f == FALSE) s = eread("M-x ", xname, NXNAME, EFNEW|EFFUNC #ifndef VARARGS , (char *) NULL #endif ) ; else s = eread("%d M-x ", xname, NXNAME, EFNEW|EFFUNC, #ifdef VARARGS n #else (char *) &n, (char *) NULL #endif ) ; if (s != TRUE) return (s); if ((sp=symlookup(xname)) != NULL) return ((*sp->s_funcp)(f, n, KRANDOM)); ewprintf("[No match]"); return FALSE; } /* * Read a key from the keyboard, and look it * up in the binding table. Display the name of the function * currently bound to the key. Say that the key is not bound * if it is indeed not bound, or if the type is not a * "builtin". This is a bit of overkill, because this is the * only kind of function there is. */ /*ARGSUSED*/ desckey(f, n, k) { register SYMBOL *sp; register KEY c; if (kbdmop == NULL) ewprintf("Describe key briefly: "); c = getkey(0); if (kbdmop != NULL) return TRUE; if ((sp=binding[c]) == NULL) ewprintf("%c is undefined", (int) c); else ewprintf("%c runs the command %s", (int) c, sp->s_name); return (TRUE); } /* * This function creates a table, listing all * of the command keys and their current bindings, and stores * the table in the standard pop-op buffer (the one used by the * directory list command, the buffer list command, etc.). This * lets MicroEMACS produce it's own wall chart. The bindings to * "ins-self" are only displayed if there is an argument. */ /*ARGSUSED*/ wallchart(f, n, k) { register int key; register SYMBOL *sp; register char *cp1; register char *cp2; BUFFER *bp; char buf[64]; bp = bfind("*Help*", TRUE); if (bclear(bp) != TRUE) /* Clear it out. */ return TRUE; for (key=0; key<NKEYS; ++key) { /* For all keys. */ sp = binding[key]; if (sp != NULL && (f!=FALSE || strcmp(sp->s_name, "self-insert-command")!=0)) { keyname(buf, key); cp1 = &buf[0]; /* Find end. */ while (*cp1 != 0) ++cp1; while (cp1 < &buf[32]) /* Goto column 32. */ *cp1++ = ' '; cp2 = sp->s_name; /* Add function name. */ while (*cp1++ = *cp2++) ; if (addline(bp, buf) == FALSE) return (FALSE); } } return popbuf(bp) == NULL ? FALSE : TRUE; } #ifdef STARTUP /* * Define the commands needed to do startup-file processing. * This code is mostly a kludge just so we can get startup-file processing. * * If you're serious about having this code, you should rewrite it. * To wit: * It has lots of funny things in it to make the startup-file look * like a GNU startup file; mostly dealing with parens and semicolons. * This should all vanish. * * It uses the same buffer as keyboard macros. The fix is easy (make * a new function "execmacro" that takes a pointer to char and * does what ctlxe does on it. Make ctlxe and excline both call it.) * but would slow down the non-micro version. * * We define eval-expression because it's easy. It's pretty useless, * since it duplicates the functionality of execute-extended-command. * All of this is just to support startup files, and should be turned * off for micros. */ /* * evalexpr - get one line from the user, and run it. Identical in function * to extend, but easy. */ /*ARGSUSED*/ evalexpr(f, n, k) { register int s; char exbuf[NKBDM]; if ((s = ereply("Eval: ", exbuf, NKBDM)) != TRUE) return s; return excline(exbuf); } /* * evalbuffer - evaluate the current buffer as line commands. Useful * for testing startup files. */ /*ARGSUSED*/ evalbuffer(f, n, k) { register LINE *lp; register BUFFER *bp = curbp; register int s; static char excbuf[NKBDM]; char *strncpy(); for (lp = lforw(bp->b_linep); lp != bp->b_linep; lp = lforw(lp)) { if (llength(lp) >= NKBDM + 1) return FALSE ; (VOID) strncpy(excbuf, ltext(lp), NKBDM); if ((s = excline(excbuf)) != TRUE) return s; } return TRUE; } /* * evalfile - go get a file and evaluate it as line commands. You can * go get your own startup file if need be. */ /*ARGSUSED*/ evalfile(f, n, k) { register int s; char fname[NFILEN]; if ((s = ereply("Load file: ", fname, NFILEN)) != TRUE) return s; return load(fname); } /* * load - go load the file name we got passed. */ load(fname) char *fname; { register int s; char excbuf[NKBDM]; if (ffropen(fname) == FIOERR) return FALSE; while ((s = ffgetline(excbuf, NKBDM)) == FIOSUC) if (excline(excbuf) != TRUE) break; (VOID) ffclose(); return s == FIOEOF; } /* * excline - run a line from a load file or eval-expression. */ excline(line) register char *line; { register char *funcp, *argp = NULL; char *skipwhite(), *parsetoken(), *backquote(); int status; /* Don't know if it works; don't care - mwm */ if (kbdmip != NULL || kbdmop != NULL) { ewprintf("Not now!") ; return FALSE; } funcp = skipwhite(line); if (*funcp == '\0') return TRUE; /* No error on blank lines */ line = parsetoken(funcp); if (*line != '\0') { *line++ = '\0'; line = skipwhite(line); if ((*line >= '0' && *line <= '9') || *line == '-') { argp = line; line = parsetoken(line); } } kbdmip = &kbdm[0]; if (argp != NULL) { *kbdmip++ = (KEY) (KCTRL|'U'); *kbdmip++ = (KEY) atoi(argp); } *kbdmip++ = (KEY) (KMETA|'X'); /* Pack in function */ while (*funcp != '\0') if (kbdmip+1 <= &kbdm[NKBDM-3]) *kbdmip++ = (KEY) *funcp++; else { ewprintf("eval-expression macro overflow"); ttflush(); return FALSE; } *kbdmip++ = '\0'; /* done with function */ /* Pack away all the args now... */ while (*line != '\0') { argp = skipwhite(line); if (*argp == '\0') break ; line = parsetoken(argp) ; /* Slightly bogus for strings. But they should be SHORT! */ if (kbdmip+(line-argp)+1 > &kbdm[NKBDM-3]) { ewprintf("eval-expression macro overflow"); ttflush(); return FALSE; } if (*line != '\0') *line++ = '\0'; if (*argp != '"') { if (*argp == '\'') ++argp; while (*argp != '\0') *kbdmip++ = (KEY) *argp++; *kbdmip++ = '\0'; } else { /* Quoted strings special again */ ++argp; while (*argp != '"' && *argp != '\0') if (*argp != '\\') *kbdmip++ = (KEY) *argp++; else argp = backquote(++argp, TRUE); /* Quotes strings are gotkey'ed, so no trailing null */ } } *kbdmip++ = (KEY) (KCTLX|')'); *kbdmip++ = '\0'; kbdmip = NULL; status = ctlxe(FALSE, 1, KRANDOM); kbdm[0] = (KCTLX|')'); return status; } /* * a pair of utility functions for the above */ char * skipwhite(s) register char *s; { while ((*s == ' ' || *s == '\t' || *s == ')' || *s == '(') && *s != '\0') if (*s == ';') *s = '\0' ; else s++; return s; } char * parsetoken(s) register char *s; { if (*s != '"') while (*s != ' ' && *s != '\t' && *s != '(' && *s != ')' && *s != '\0') { if (*s == ';') *s = '\0'; else s++; } else /* Strings get special treatment */ do { /* Beware: You can \ out the end of the string! */ if (*s == '\\') ++s; if (ISLOWER(*s)) *s = TOUPPER(*s); } while (*++s != '"' && *s != '\0'); return s; } /* * Put a backquoted string element into the keyboard macro. Return pointer * to char following backquoted stuff. */ char * backquote(in, flag) char *in; { switch (*in++) { case 'T': *kbdmip++ = (KEY) (KCTRL|'I'); break; case 'N': *kbdmip++ = (KEY) (KCTRL|'J'); break; case 'R': *kbdmip++ = (KEY) (KCTRL|'M'); break; case '^': *kbdmip = (KEY) (KCTRL|*in++); if (flag != FALSE && *kbdmip == (KEY) (KCTRL|'X')) { if (*in == '\\') in = backquote(++in, FALSE); else *kbdmip++ = (KEY) *in++; kbdmip[-1] |= (KEY) KCTLX; } else ++kbdmip; break; case 'E': if (flag != TRUE) *kbdmip++ = (KEY) (KCTRL|'['); else if (*in != '\\') *kbdmip++ = (KEY) (KMETA|*in++); else { in = backquote(++in, FALSE); kbdmip[-1] |= (KEY) KMETA; } break; } return in; } #endif STARTUP SHAR_EOF fi # end of overwriting check if test -f 'file.c' then echo shar: will not over-write existing file "'file.c'" else cat << \SHAR_EOF > 'file.c' /* * File commands. */ #include "def.h" BUFFER *findbuffer(); /* * insert a file into the current buffer. Real easy - just call the * insertfile routine with the file name. */ /*ARGSUSED*/ fileinsert(f, n, k) { register int s; char fname[NFILEN]; if ((s=ereply("Insert file: ", fname, NFILEN)) != TRUE) return (s); adjustcase(fname); return (insertfile(fname, (char *) NULL)); /* don't set buffer name */ } /* * Select a file for editing. * Look around to see if you can find the * fine in another buffer; if you can find it * just switch to the buffer. If you cannot find * the file, create a new buffer, read in the * text, and switch to the new buffer. */ /*ARGSUSED*/ filevisit(f, n, k) { register BUFFER *bp; int s; char fname[NFILEN]; if ((s=ereply("Find file: ", fname, NFILEN)) != TRUE) return (s); if ((bp = findbuffer(fname, &s)) == NULL) return s; curbp = bp; if (showbuffer(bp, curwp, WFHARD) != TRUE) return FALSE; if (bp->b_fname[0] == 0) return (readin(fname)); /* Read it in. */ return TRUE; } /* * Pop to a file in the other window. Same as last function, just * popbuf instead of showbuffer. */ /*ARGSUSED*/ poptofile(f, n, k) { register BUFFER *bp; register WINDOW *wp; int s; char fname[NFILEN]; if ((s=ereply("Find file in other window: ", fname, NFILEN)) != TRUE) return (s); if ((bp = findbuffer(fname, &s)) == NULL) return s; if ((wp = popbuf(bp)) == NULL) return FALSE; curbp = bp; curwp = wp; if (bp->b_fname[0] == 0) return (readin(fname)); /* Read it in. */ return TRUE; } /* * given a file name, either find the buffer it uses, or create a new * empty buffer to put it in. */ BUFFER * findbuffer(fname, s) char *fname; int *s; { register BUFFER *bp; char bname[NBUFN]; adjustcase(fname); for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) { if (strcmp(bp->b_fname, fname) == 0) { return bp; } } makename(bname, fname); /* New buffer name. */ while ((bp=bfind(bname, FALSE)) != NULL) { *s = ereply("Buffer name: ", bname, NBUFN); if (*s == ABORT) /* ^G to just quit */ return NULL; if (*s == FALSE) { /* CR to clobber it */ bp->b_fname[0] = '\0'; break; } } if (bp == NULL) bp = bfind(bname, TRUE); *s = FALSE; return bp; } /* * Read the file "fname" into the current buffer. * Make all of the text in the buffer go away, after checking * for unsaved changes. This is called by the "read" command, the * "visit" command, and the mainline (for "uemacs file"). */ readin(fname) char *fname; { register int status; register WINDOW *wp; if (bclear(curbp) != TRUE) /* Might be old. */ return TRUE; status = insertfile(fname, fname) ; curbp->b_flag &= ~BFCHG; /* No change. */ for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) { if (wp->w_bufp == curbp) { wp->w_linep = lforw(curbp->b_linep); wp->w_dotp = lforw(curbp->b_linep); wp->w_doto = 0; wp->w_markp = NULL; wp->w_marko = 0; } } return status; } /* * insert a file in the current buffer, after dot. Set mark * at the end of the text inserted, point at the beginning. * Return a standard status. Print a summary (lines read, * error message) out as well. If the * BACKUP conditional is set, then this routine also does the read * end of backup processing. The BFBAK flag, if set in a buffer, * says that a backup should be taken. It is set when a file is * read in, but not on a new file (you don't need to make a backup * copy of nothing). * * Warning: Adds a trainling nl to files that don't end in one! * Need to fix, but later (I suspect that it will require a change * in the fileio files for all systems involved). */ insertfile(fname, newname) char fname[], newname[]; { register LINE *lp1; register LINE *lp2; LINE *olp; /* Line we started at */ int opos; /* and offset into it */ register WINDOW *wp; register int i; register int nbytes; int s, nline; BUFFER *bp; char line[NLINE]; bp = curbp; /* Cheap. */ if (newname != (char *) NULL) (VOID) strcpy(bp->b_fname, newname); if ((s=ffropen(fname)) == FIOERR) /* Hard file open. */ goto out; if (s == FIOFNF) { /* File not found. */ if (kbdmop == NULL) if (newname != NULL) ewprintf("(New file)"); else ewprintf("(File not found)"); goto out; } opos = curwp->w_doto; /* Open a new line, at point, and start inserting after it */ (VOID) lnewline(); olp = lback(curwp->w_dotp); nline = 0; /* Don't count fake line at end */ while ((s=ffgetline(line, NLINE)) == FIOSUC) { nbytes = strlen(line); if ((lp1=lalloc((RSIZE) nbytes)) == NULL) { s = FIOERR; /* Keep message on the */ break; /* display. */ } lp2 = lback(curwp->w_dotp); lp2->l_fp = lp1; lp1->l_fp = curwp->w_dotp; lp1->l_bp = lp2; curwp->w_dotp->l_bp = lp1; for (i=0; i<nbytes; ++i) lputc(lp1, i, line[i]); ++nline; } (VOID) ffclose(); /* Ignore errors. */ if (s==FIOEOF && kbdmop==NULL) { /* Don't zap an error. */ if (nline == 1) ewprintf("(Read 1 line)"); else ewprintf("(Read %d lines)", nline); } /* Set mark at the end of the text */ curwp->w_markp = curwp->w_dotp; curwp->w_marko = curwp->w_doto; /* Now, delete the results of the lnewline we started with */ curwp->w_dotp = olp; curwp->w_doto = opos; (VOID) ldelnewline(); curwp->w_doto = opos; /* and dot is right */ #ifdef BACKUP if (newname != NULL) bp->b_flag |= BFCHG | BFBAK; /* Need a backup. */ else bp->b_flag |= BFCHG; #else bp->b_flag |= BFCHG; #endif /* if the insert was at the end of buffer, set lp1 to the end of * buffer line, and lp2 to the beginning of the newly inserted * text. (Otherwise lp2 is set to NULL.) This is * used below to set pointers in other windows correctly if they * are also at the end of buffer. */ lp1 = bp->b_linep; if (curwp->w_markp == lp1) lp2 = curwp->w_dotp; else { out: lp2 = NULL; } for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) { if (wp->w_bufp == curbp) { wp->w_flag |= WFMODE|WFEDIT; if (wp != curwp && lp2 != NULL) { if (wp->w_dotp == lp1) wp->w_dotp = lp2; if (wp->w_markp == lp1) wp->w_markp = lp2; if (wp->w_linep == lp1) wp->w_linep = lp2; } } } if (s == FIOERR) /* False if error. */ return (FALSE); return (TRUE); } /* * Take a file name, and from it * fabricate a buffer name. This routine knows * about the syntax of file names on the target system. * BDC1 left scan delimiter. * BDC2 optional second left scan delimiter. * BDC3 optional right scan delimiter. */ makename(bname, fname) char bname[]; char fname[]; { register char *cp1; register char *cp2; cp1 = &fname[0]; while (*cp1 != 0) ++cp1; #ifdef BDC2 while (cp1!=&fname[0] && cp1[-1]!=BDC1 && cp1[-1]!=BDC2) --cp1; #else while (cp1!=&fname[0] && cp1[-1]!=BDC1) --cp1; #endif cp2 = &bname[0]; #ifdef BDC3 while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=BDC3) *cp2++ = *cp1++; #else while (cp2!=&bname[NBUFN-1] && *cp1!=0) *cp2++ = *cp1++; #endif *cp2 = 0; } /* * Ask for a file name, and write the * contents of the current buffer to that file. * Update the remembered file name and clear the * buffer changed flag. This handling of file names * is different from the earlier versions, and * is more compatable with Gosling EMACS than * with ITS EMACS. */ /*ARGSUSED*/ filewrite(f, n, k) { register int s; char fname[NFILEN]; if ((s=ereply("Write file: ", fname, NFILEN)) != TRUE) return (s); adjustcase(fname); if ((s=writeout(curbp, fname)) == TRUE) { (VOID) strcpy(curbp->b_fname, fname); #ifdef BACKUP curbp->b_flag &= ~(BFBAK | BFCHG); #else curbp->b_flag &= ~BFCHG; #endif upmodes(curbp); } return (s); } /* * Save the contents of the current buffer back into * its associated file. Do nothing if there have been no changes * (is this a bug, or a feature). Error if there is no remembered * file name. If this is the first write since the read or visit, * then a backup copy of the file is made. */ /*ARGSUSED*/ filesave(f, n, k) { register int s; if ((curbp->b_flag&BFCHG) == 0) { /* Return, no changes. */ if (kbdmop == NULL) ewprintf("(No changes need to be saved)"); return (TRUE); } if (curbp->b_fname[0] == 0) { /* Must have a name. */ ewprintf("No file name"); return (FALSE); } #ifdef BACKUP if ((curbp->b_flag&BFBAK) != 0) { s = fbackupfile(curbp->b_fname); if (s == ABORT) /* Hard error. */ return FALSE; if (s == FALSE /* Softer error. */ && (s=eyesno("Backup error, save anyway")) != TRUE) return (s); } #endif if ((s=writeout(curbp, curbp->b_fname)) == TRUE) { #ifdef BACKUP curbp->b_flag &= ~(BFCHG | BFBAK); #else curbp->b_flag &= ~BFCHG; #endif upmodes(curbp); } return (s); } /* * This function performs the details of file * writing; writing the file in buffer bp to * file fn. Uses the file management routines * in the "fileio.c" package. Most of the grief * is checking of some sort. */ writeout(bp, fn) register BUFFER *bp; char *fn; { register int s; register LINE *lp; if ((s=ffwopen(fn)) != FIOSUC) /* Open writes message. */ return (FALSE); lp = lforw(bp->b_linep); /* First line. */ while (lp != bp->b_linep) { if ((s=ffputline(&(ltext(lp))[0], llength(lp))) != FIOSUC) break; lp = lforw(lp); } if (s == FIOSUC) { /* No write error. */ s = ffclose(); if (s==FIOSUC && kbdmop==NULL) ewprintf("Wrote %s", fn); } else /* Ignore close error */ (VOID) ffclose(); /* if a write error. */ if (s != FIOSUC) /* Some sort of error. */ return (FALSE); return (TRUE); } /* * Tag all windows for bp (all windows if bp NULL) as needing their * mode line updated. */ upmodes(bp) register BUFFER *bp; { register WINDOW *wp; for (wp = wheadp; wp != NULL; wp = wp->w_wndp) if (bp == NULL || curwp->w_bufp == bp) wp->w_flag |= WFMODE; } SHAR_EOF fi # end of overwriting check if test -f 'match.c' then echo shar: will not over-write existing file "'match.c'" else cat << \SHAR_EOF > 'match.c' /* * Name: MicroEMACS * Limited parenthesis matching routines * Version: Gnu30 * Last edit: 13-Jul-86 * Created: 19-May-86 ...!ihnp4!seismo!ut-sally!ut-ngp!mic * * The hacks in this file implement automatic matching * of (), [], {}, and other characters. It would be * better to have a full-blown syntax table, but there's * enough overhead in the editor as it is. * * Since I often edit Scribe code, I've made it possible to * blink arbitrary characters -- just bind delimiter characters * to "blink-matching-paren-hack" */ #include "def.h" /* Balance table. When balance() encounters a character * that is to be matched, it first searches this table * for a balancing left-side character. If the character * is not in the table, the character is balanced by itself. * This is to allow delimiters in Scribe documents to be matched. */ static struct balance { char left, right; } bal[] = { { '(', ')' }, { '[', ']' }, { '{', '}' }, { '<', '>' }, { '\0','\0'} }; /* * Fake the GNU "blink-matching-paren" variable. * If the argument exists, nonzero means show, * zero means don't. If it doesn't exist, * pretend it's nonzero. */ blinkparen(f, n, k) { register char *command; register SYMBOL *sp; if (f == FALSE) n = 1; command = (n == 0) ? "self-insert-command" : "blink-matching-paren-hack"; if ((sp=symlookup(command)) == NULL) { ewprintf("blinkparen: no binding for %s",command); return (FALSE); } binding[(KEY) ')'] = sp; /* rebind paren */ return (TRUE); } /* * Self-insert character, then show matching character, * if any. Bound to "blink-matching-paren-command". */ showmatch(f, n, k) { register int i, s; if (k == KRANDOM) return(FALSE); for (i = 0; i < n; i++) { if ((s = selfinsert(f, 1, k)) != TRUE) return(s); if (balance(k) != TRUE) /* unbalanced -- warn user */ ttbeep(); } return (TRUE); } /* * Search for and display a matching character. * * This routine does the real work of searching backward * for a balancing character. If such a balancing character * is found, it uses displaymatch() to display the match. */ static balance(k) int k; { register LINE *clp; register int cbo; int c; int i; int rbal, lbal; int depth; rbal = k & KCHAR; if ((k&KCTRL)!=0 && rbal>='@' && rbal<='_') /* ASCII-ify. */ rbal -= '@'; /* See if there is a matching character -- default to the same */ lbal = rbal; for (i = 0; bal[i].right != '\0'; i++) if (bal[i].right == rbal) { lbal = bal[i].left; break; } /* Move behind the inserted character. We are always guaranteed */ /* that there is at least one character on the line, since one was */ /* just self-inserted by blinkparen. */ clp = curwp->w_dotp; cbo = curwp->w_doto - 1; depth = 0; /* init nesting depth */ for (;;) { if (cbo == 0) { /* beginning of line */ clp = lback(clp); if (clp == curbp->b_linep) return (FALSE); cbo = llength(clp)+1; } if (--cbo == llength(clp)) /* end of line */ c = '\n'; else c = lgetc(clp,cbo); /* somewhere in middle */ /* Check for a matching character. If still in a nested */ /* level, pop out of it and continue search. This check */ /* is done before the nesting check so single-character */ /* matches will work too. */ if (c == lbal) { if (depth == 0) { displaymatch(clp,cbo); return (TRUE); } else depth--; } /* Check for another level of nesting. */ if (c == rbal) depth++; } } /* * Display matching character. * Matching characters that are not in the current window * are displayed in the echo line. If in the current * window, move dot to the matching character, * sit there a while, then move back. */ static displaymatch(clp, cbo) register LINE *clp; register int cbo; { register LINE *tlp; register int tbo; register int cp; register int bufo; register int c; int inwindow; char buf[NLINE]; /* Figure out if matching char is in current window by */ /* searching from the top of the window to dot. */ inwindow = FALSE; for (tlp = curwp->w_linep; tlp != lforw(curwp->w_dotp); tlp = lforw(tlp)) if (tlp == clp) inwindow = TRUE; if (inwindow == TRUE) { tlp = curwp->w_dotp; /* save current position */ tbo = curwp->w_doto; curwp->w_dotp = clp; /* move to new position */ curwp->w_doto = cbo; curwp->w_flag |= WFMOVE; update(); /* show match */ sleep(1); /* wait a bit */ curwp->w_dotp = tlp; /* return to old position */ curwp->w_doto = tbo; curwp->w_flag |= WFMOVE; update(); } else { /* match not in this window so display line in echo area */ bufo = 0; for (cp = 0; cp < llength(clp); cp++) { /* expand tabs */ c = lgetc(clp,cp); if (c != '\t') buf[bufo++] = c; else do { buf[bufo++] = ' '; } while (bufo & 7); } buf[bufo++] = '\0'; ewprintf("Matches %s",buf); } return (TRUE); } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0