Path: seismo!harvard!talcott!panda!genrad!decvax!minow (Martin Minow) From: sources-request@panda.UUCP Newsgroups: mod.sources Subject: MicroEmacs (Part 2 of 6) Message-ID: <1657@panda.UUCP> Date: 13 Apr 86 16:29:19 GMT Sender: jpn@panda.UUCP Lines: 2052 Approved: jpn@panda.UUCP Mod.sources: Volume 4, Issue 69 Submitted by: decvax!minow (Martin Minow) #! /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: # echo.c # extend.c # file.c # kbd.c # line.c # main.c # This archive created: Sun Apr 13 11:15:48 1986 export PATH; PATH=/bin:$PATH echo shar: extracting "'echo.c'" '(8844 characters)' if test -f 'echo.c' then echo shar: will not over-write existing file "'echo.c'" else cat << \SHAR_EOF > 'echo.c' /* * Name: MicroEMACS * Echo line reading and writing. * Version: 29 * Last edit: 14-Feb-86 * By: rex::conroy * decvax!decwrl!dec-rhea!dec-rex!conroy * * 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" int epresf = FALSE; /* Stuff in echo line flag. */ int nmsg = 0; /* Size of occupied msg. area. */ int curmsgf = FALSE; /* Current alert state. */ int newmsgf = FALSE; /* New alert state. */ char msg[NMSG]; /* Random message storage. */ /* * Send a string to the message system. * Add a free newline to the end of the message string. * Return TRUE if it fits, and FALSE if it does not. * Perhaps the message buffer should know how to get * larger, just like the kill buffer? */ writemsg(sp) register char *sp; { register int c; if (nmsg+strlen(sp)+1 > NMSG) /* "+1" for the "\n". */ return (FALSE); while ((c = *sp++) != '\0') msg[nmsg++] = c; msg[nmsg++] = '\n'; newmsgf = TRUE; /* Update mode line. */ return (TRUE); } /* * Read messages. The message lines are * displayed, one line at a time, in the message line. * A special sub-mode is entered, in which the keys have * the following meanings: * ^P Go backward 1 line. * BS Go backward 1 line. * ^N Go forward 1 line. Quit if at the end. * SP Go forward 1 line. Quit if at the end. * CR Go forward 1 line. Quit if at the end. * ^G Abort, leave old text. * ^C Quit, delete anything already read. * Return TRUE if you left this mode in a reasonable * way (not ^G), and ABORT if you quit the mode with a * ^G. */ readmsg() { register int c; register int i; register int j; if (nmsg == 0) /* Duck out if none. */ return (TRUE); newmsgf = FALSE; /* Kill alert, and do */ update(); /* a redisplay. */ ttcolor(CTEXT); i = 0; while (i < nmsg) { ttmove(nrow-1, 0); /* Display 1 line. */ while (is_name[i]) break; } if (i == cpos) { if (nhits == 0) sp2 = sp1; ++nhits; bxtra = getxtra(sp1, sp2, cpos); if (bxtra < nxtra) nxtra = bxtra; } sp1 = sp1->s_symp; } } if (nhits == 0) /* No completion. */ continue; for (i=0; is_name[cpos]; buf[cpos++] = c; eputc(c); } ttflush(); if (nhits != 1) /* Fake a CR if there */ continue; /* is 1 choice. */ c = 0x0D; } switch (c) { case 0x0D: /* Return, done. */ buf[cpos] = '\0'; if (kbdmip != NULL) { if (kbdmip+cpos+1 > &kbdm[NKBDM-3]) { (void) ctrlg(FALSE, 0, KRANDOM); ttflush(); return (ABORT); } for (i=0; is_name[i] != sp2->s_name[i]) break; if (sp1->s_name[i] == '\0') break; ++i; } return (i - cpos); } /* * Special "printf" for the echo line. * Each call to "eprintf" 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. */ /* VARARGS1 */ eprintf(fp, arg) char *fp; { ttcolor(CTEXT); ttmove(nrow-1, 0); eformat(fp, (char *)&arg); tteeol(); ttflush(); epresf = TRUE; } /* * Printf style formatting. This is * called by both "eprintf" 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. */ eformat(fp, ap) register char *fp; register char *ap; { register int c; while ((c = *fp++) != '\0') { if (c != '%') eputc(c); else { c = *fp++; switch (c) { case 'd': eputi(*(int *)ap, 10); ap += sizeof(int); break; case 'o': eputi(*(int *)ap, 8); ap += sizeof(int); break; case 's': eputs(*(char **)ap); ap += sizeof(char *); break; default: eputc(c); } } } } /* * Put integer, in radix "r". */ 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 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. */ eputc(c) register int c; { if (ttcol < ncol) { if (ISCTRL(c) != FALSE) { eputc('^'); c ^= 0x40; } ttputc(c); ++ttcol; } } SHAR_EOF if test 8844 -ne "`wc -c < 'echo.c'`" then echo shar: error transmitting "'echo.c'" '(should have been 8844 characters)' fi fi echo shar: extracting "'extend.c'" '(3416 characters)' if test -f 'extend.c' then echo shar: will not over-write existing file "'extend.c'" else cat << \SHAR_EOF > 'extend.c' /* * Name: MicroEMACS * Extended (M-X) commands. * Version: 29 * Last edit: 14-Feb-86 * By: rex::conroy * decvax!decwrl!dec-rhea!dec-rex!conroy */ #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. This code does not * work right if there is a keyboard macro floating around. * Should be fixed. */ bindtokey(f, n, k) { register int s; register char *cp; register SYMBOL *sp; register int c; char xname[NXNAME]; if (kbdmip!=NULL || kbdmop!=NULL) { eprintf("Not now"); return (FALSE); } if ((s=eread("Function: ", xname, NXNAME, EFAUTO, NULL)) != TRUE) return (s); if ((sp=symlookup(xname)) == NULL) { eprintf("Unknown function for binding"); return (FALSE); } eputc(' '); eputc('K'); eputc('e'); eputc('y'); eputc(':'); eputc(' '); ttflush(); c = getkey(); /* Read key. */ keyname(xname, c); /* Display keyname. */ eputs(xname); ttflush(); if (binding[c] != NULL) /* Unbind old, and */ --binding[c]->s_nkey; binding[c] = sp; /* rebind new. */ ++sp->s_nkey; 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. */ extend(f, n, k) { register SYMBOL *sp; register int s; char xname[NXNAME]; if ((s=eread(": ", xname, NXNAME, EFNEW|EFAUTO, NULL)) != TRUE) return (s); if ((sp=symlookup(xname)) != NULL) return ((*sp->s_funcp)(f, n, KRANDOM)); eprintf("Unknown extended command"); return (ABORT); } /* * 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. */ help(f, n, k) { register SYMBOL *sp; register int c; char b[20]; c = getkey(); keyname(b, c); if ((sp=binding[c]) == NULL) eprintf("[%s is unbound]", b); else eprintf("[%s is bound to %s]", b, 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. */ wallchart(f, n, k) { register int s; register int key; register SYMBOL *sp; register char *cp1; register char *cp2; char buf[64]; if ((s=bclear(blistp)) != TRUE) /* Clear it out. */ return (s); (void) strcpy(blistp->b_fname, ""); for (key=0; keys_name, "ins-self")!=0)) { keyname(buf, key); cp1 = &buf[0]; /* Find end. */ while (*cp1 != 0) ++cp1; while (cp1 < &buf[16]) /* Goto column 16. */ *cp1++ = ' '; cp2 = sp->s_name; /* Add function name. */ while (*cp1++ = *cp2++) ; if (addline(buf) == FALSE) return (FALSE); } } return (popblist()); } SHAR_EOF if test 3416 -ne "`wc -c < 'extend.c'`" then echo shar: error transmitting "'extend.c'" '(should have been 3416 characters)' fi fi echo shar: extracting "'file.c'" '(9404 characters)' if test -f 'file.c' then echo shar: will not over-write existing file "'file.c'" else cat << \SHAR_EOF > 'file.c' /* * Name: MicroEMACS * File commands. * Version: 29 * Last edit: 05-Feb-86 * By: rex::conroy * decvax!decwrl!dec-rhea!dec-rex!conroy */ #include "def.h" /* * Read a file into the current * buffer. This is really easy; all you do it * find the name of the file, and call the standard * "read a file into the current buffer" code. */ fileread(f, n, k) { register int s; char fname[NFILEN]; if ((s=ereply("Read file: ", fname, NFILEN)) != TRUE) return (s); adjustcase(fname); return (readin(fname)); } /* * 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. */ filevisit(f, n, k) { register BUFFER *bp; register WINDOW *wp; register LINE *lp; register int i; register int s; char bname[NBUFN]; char fname[NFILEN]; if ((s=ereply("Visit file: ", fname, NFILEN)) != TRUE) return (s); adjustcase(fname); for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) { if (strcmp(bp->b_fname, fname) == 0) { if (--curbp->b_nwnd == 0) { curbp->b_dotp = curwp->w_dotp; curbp->b_doto = curwp->w_doto; curbp->b_markp = curwp->w_markp; curbp->b_marko = curwp->w_marko; } curbp = bp; curwp->w_bufp = bp; if (bp->b_nwnd++ == 0) { curwp->w_dotp = bp->b_dotp; curwp->w_doto = bp->b_doto; curwp->w_markp = bp->b_markp; curwp->w_marko = bp->b_marko; } else { wp = wheadp; while (wp != NULL) { if (wp!=curwp && wp->w_bufp==bp) { curwp->w_dotp = wp->w_dotp; curwp->w_doto = wp->w_doto; curwp->w_markp = wp->w_markp; curwp->w_marko = wp->w_marko; break; } wp = wp->w_wndp; } } lp = curwp->w_dotp; i = curwp->w_ntrows/2; while (i-- && lback(lp)!=curbp->b_linep) lp = lback(lp); curwp->w_linep = lp; curwp->w_flag |= WFMODE|WFHARD; if (kbdmop == NULL) eprintf("[Old buffer]"); return (TRUE); } } 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 (s); if (s == FALSE) { /* CR to clobber it */ makename(bname, fname); break; } } if (bp==NULL && (bp=bfind(bname, TRUE))==NULL) { eprintf("Cannot create buffer"); return (FALSE); } if (--curbp->b_nwnd == 0) { /* Undisplay. */ curbp->b_dotp = curwp->w_dotp; curbp->b_doto = curwp->w_doto; curbp->b_markp = curwp->w_markp; curbp->b_marko = curwp->w_marko; } curbp = bp; /* Switch to it. */ curwp->w_bufp = bp; curbp->b_nwnd++; return (readin(fname)); /* Read it in. */ } /* * 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"). 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). Return a standard status. Print a summary * (lines read, error message) out as well. */ readin(fname) char fname[]; { register LINE *lp1; register LINE *lp2; register int i; register WINDOW *wp; register BUFFER *bp; register int s; register int nbytes; register int nline; char line[NLINE]; bp = curbp; /* Cheap. */ if ((s=bclear(bp)) != TRUE) /* Might be old. */ return (s); #if BACKUP bp->b_flag &= ~(BFCHG|BFBAK); /* No change, backup. */ #else bp->b_flag &= ~BFCHG; /* No change. */ #endif strcpy(bp->b_fname, fname); if ((s=ffropen(fname)) == FIOERR) /* Hard file open. */ goto out; if (s == FIOFNF) { /* File not found. */ if (kbdmop == NULL) eprintf("[New file]"); goto out; } nline = 0; while ((s=ffgetline(line, NLINE)) == FIOSUC) { nbytes = strlen(line); if ((lp1=lalloc(nbytes)) == NULL) { s = FIOERR; /* Keep message on the */ break; /* display. */ } lp2 = lback(curbp->b_linep); lp2->l_fp = lp1; lp1->l_fp = curbp->b_linep; lp1->l_bp = lp2; curbp->b_linep->l_bp = lp1; for (i=0; ib_flag |= BFBAK; /* Need a backup. */ #endif out: 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; wp->w_flag |= WFMODE|WFHARD; } } 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. */ filewrite(f, n, k) { register WINDOW *wp; register int s; char fname[NFILEN]; if ((s=ereply("Write file: ", fname, NFILEN)) != TRUE) return (s); adjustcase(fname); if ((s=writeout(fname)) == TRUE) { strcpy(curbp->b_fname, fname); 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; } } #if BACKUP curbp->b_flag &= ~BFBAK; /* No backup. */ #endif 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. */ filesave(f, n, k) { register WINDOW *wp; register int s; if ((curbp->b_flag&BFCHG) == 0) /* Return, no changes. */ return (TRUE); if (curbp->b_fname[0] == 0) { /* Must have a name. */ eprintf("No file name"); return (FALSE); } #if BACKUP if ((curbp->b_flag&BFBAK) != 0) { s = fbackupfile(curbp->b_fname); if (s == ABORT) /* Hard error. */ return (s); if (s == FALSE /* Softer error. */ && (s=eyesno("Backup error, save anyway")) != TRUE) return (s); } #endif if ((s=writeout(curbp->b_fname)) == TRUE) { 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; } } #if BACKUP curbp->b_flag &= ~BFBAK; /* No backup. */ #endif return (s); } /* * This function performs the details of file * writing. Uses the file management routines in the * "fileio.c" package. The number of lines written is * displayed. Sadly, it looks inside a LINE; provide * a macro for this. Most of the grief is error * checking of some sort. */ writeout(fn) char *fn; { register int s; register LINE *lp; register int nline; if ((s=ffwopen(fn)) != FIOSUC) /* Open writes message. */ return (FALSE); lp = lforw(curbp->b_linep); /* First line. */ nline = 0; /* Number of lines. */ while (lp != curbp->b_linep) { if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC) break; ++nline; lp = lforw(lp); } if (s == FIOSUC) { /* No write error. */ s = ffclose(); if (s==FIOSUC && kbdmop==NULL) { if (nline == 1) eprintf("[Wrote 1 line]"); else eprintf("[Wrote %d lines]", nline); } } else /* Ignore close error */ ffclose(); /* if a write error. */ if (s != FIOSUC) /* Some sort of error. */ return (FALSE); return (TRUE); } /* * The command allows the user * to modify the file name associated with * the current buffer. It is like the "f" command * in UNIX "ed". The operation is simple; just zap * the name in the BUFFER structure, and mark the windows * as needing an update. You can type a blank line at the * prompt if you wish. */ filename(f, n, k) { register WINDOW *wp; register int s; char fname[NFILEN]; if ((s=ereply("Name: ", fname, NFILEN)) == ABORT) return (s); adjustcase(fname); strcpy(curbp->b_fname, fname); /* Fix name. */ wp = wheadp; /* Update mode lines. */ while (wp != NULL) { if (wp->w_bufp == curbp) wp->w_flag |= WFMODE; wp = wp->w_wndp; } #if BACKUP curbp->b_flag &= ~BFBAK; /* No backup. */ #endif return (TRUE); } SHAR_EOF if test 9404 -ne "`wc -c < 'file.c'`" then echo shar: error transmitting "'file.c'" '(should have been 9404 characters)' fi fi echo shar: extracting "'kbd.c'" '(2689 characters)' if test -f 'kbd.c' then echo shar: will not over-write existing file "'kbd.c'" else cat << \SHAR_EOF > 'kbd.c' /* * Name: MicroEMACS * Terminal independent keyboard handling. * Version: 29 * Last edit: 05-Feb-86 * By: rex::conroy * decvax!decwrl!dec-rhea!dec-rex!conroy */ #include "def.h" /* * Read in a key, doing the terminal * independent prefix handling. The terminal specific * "getkbd" routine gets the first swing, and may return * one of the special codes used by the special keys * on the keyboard. The "getkbd" routine returns the * C0 controls as received; this routine moves them to * the right spot in 11 bit code. */ getkey() { register int c; c = getkbd(); if (c == METACH) /* M- */ c = KMETA | getctl(); else if (c == CTRLCH) /* C- */ c = KCTRL | getctl(); else if (c == CTMECH) /* C-M- */ c = KCTRL | KMETA | getctl(); else if (c>=0x00 && c<=0x1F) /* Relocate control. */ c = KCTRL | (c+'@'); if (c == (KCTRL|'X')) /* C-X */ c = KCTLX | getctl(); return (c); } /* * Used above. */ getctl() { register int c; c = ttgetc(); if (ISLOWER(c) != FALSE) c = TOUPPER(c); if (c>=0x00 && c<=0x1F) /* Relocate control. */ c = KCTRL | (c+'@'); return (c); } /* * Transform a key code into a name, * using a table for the special keys and combination * of some hard code and some general processing for * the rest. None of this code is terminal specific any * more. This makes adding keys easier. */ keyname(cp, k) register char *cp; register int k; { register char *np; char nbuf[3]; static char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; if ((k&KCTLX) != 0) { /* C-X prefix. */ *cp++ = 'C'; *cp++ = '-'; *cp++ = 'X'; *cp++ = ' '; k &= ~KCTLX; } if ((k&KCHAR)>=KFIRST && (k&KCHAR)<=KLAST) { if ((np=keystrings[(k&KCHAR)-KFIRST]) != NULL) { if ((k&KCTRL) != 0) { *cp++ = 'C'; *cp++ = '-'; } if ((k&KMETA) != 0) { *cp++ = 'M'; *cp++ = '-'; } strcpy(cp, np); return; } } if ((k&~KMETA) == (KCTRL|'I')) /* Some specials. */ np = "Tab"; else if ((k&~KMETA) == (KCTRL|'M')) np = "Return"; else if ((k&~KMETA) == (KCTRL|'H')) np = "Backspace"; else if ((k&~KMETA) == ' ') np = "Space"; else if ((k&~KMETA) == 0x7F) np = "Rubout"; else { if ((k&KCTRL) != 0) { /* Add C- mark. */ *cp++ = 'C'; *cp++ = '-'; } np = &nbuf[0]; if (((k&KCHAR)>=0x20 && (k&KCHAR)<=0x7E) || ((k&KCHAR)>=0xA0 && (k&KCHAR)<=0xFE)) { nbuf[0] = k&KCHAR; /* Graphic. */ nbuf[1] = 0; } else { /* Non graphic. */ nbuf[0] = hex[(k>>4)&0x0F]; nbuf[1] = hex[k&0x0F]; nbuf[2] = 0; } } if ((k&KMETA) != 0) { /* Add M- mark. */ *cp++ = 'M'; *cp++ = '-'; } strcpy(cp, np); } SHAR_EOF if test 2689 -ne "`wc -c < 'kbd.c'`" then echo shar: error transmitting "'kbd.c'" '(should have been 2689 characters)' fi fi echo shar: extracting "'line.c'" '(14554 characters)' if test -f 'line.c' then echo shar: will not over-write existing file "'line.c'" else cat << \SHAR_EOF > 'line.c' /* * Name: MicroEMACS * Text line handling. * Version: 29 * Last edit: 14-Feb-86 * By: rex::conroy, vox::ellison * decvax!decwrl!dec-rhea!dec-rex!conroy * ...!dec-vox!ellison * * 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 "def.h" #define NBLOCK 16 /* Line block chunk size */ #ifndef KBLOCK #define KBLOCK 256 /* Kill buffer block size. */ #endif 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) { eprintf("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 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) { eprintf("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 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); } /* * 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, KRANDOM); 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, KRANDOM); 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); } /* * 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. Print a message on * errors. */ kinsert(c) { register char *nbufp; register int i; if (kused == ksize) { if ((nbufp=malloc(ksize+KBLOCK)) == NULL) { eprintf("Too many kills"); return (FALSE); } for (i=0; i= kused) return (-1); return (kbufp[n] & 0xFF); } SHAR_EOF if test 14554 -ne "`wc -c < 'line.c'`" then echo shar: error transmitting "'line.c'" '(should have been 14554 characters)' fi fi echo shar: extracting "'main.c'" '(6208 characters)' if test -f 'main.c' then echo shar: will not over-write existing file "'main.c'" else cat << \SHAR_EOF > 'main.c' /* * Name: MicroEMACS * Mainline, macro commands. * Version: 29 * Last edit: 05-Feb-86 * By: rex::conroy * decvax!decwrl!dec-rhea!dec-rex!conroy */ #include "def.h" int thisflag; /* Flags, this command */ int lastflag; /* Flags, last command */ int curgoal; /* Goal column */ BUFFER *curbp; /* Current buffer */ WINDOW *curwp; /* Current window */ BUFFER *bheadp; /* BUFFER listhead */ WINDOW *wheadp; /* WINDOW listhead */ BUFFER *blistp; /* Buffer list BUFFER */ short kbdm[NKBDM] = (KCTLX|')'); /* Macro */ short *kbdmip; /* Input for above */ short *kbdmop; /* Output for above */ char pat[NPAT]; /* Pattern */ SYMBOL *symbol[NSHASH]; /* Symbol table listhead. */ SYMBOL *binding[NKEYS]; /* Key bindings. */ main(argc, argv) char *argv[]; { register int c; register int f; register int n; register int mflag; char bname[NBUFN]; strcpy(bname, "main"); /* Get buffer name. */ if (argc > 1) makename(bname, argv[1]); vtinit(); /* Virtual terminal. */ edinit(bname); /* Buffers, windows. */ keymapinit(); /* Symbols, bindings. */ if (argc > 1) { update(); readin(argv[1]); } lastflag = 0; /* Fake last flags. */ loop: update(); /* Fix up the screen. */ c = getkey(); if (epresf != FALSE) { eerase(); update(); } f = FALSE; n = 1; if (c == (KCTRL|'U')) { /* ^U, start argument. */ f = TRUE; n = 4; while ((c=getkey()) == (KCTRL|'U')) n *= 4; if ((c>='0' && c<='9') || c=='-') { if (c == '-') { n = 0; mflag = TRUE; } else { n = c - '0'; mflag = FALSE; } while ((c=getkey())>='0' && c<='9') n = 10*n + c - '0'; if (mflag != FALSE) n = -n; } } if (kbdmip != NULL) { /* Save macro strokes. */ if (c!=(KCTLX|')') && kbdmip>&kbdm[NKBDM-6]) { ctrlg(FALSE, 0, KRANDOM); goto loop; } if (f != FALSE) { *kbdmip++ = (KCTRL|'U'); *kbdmip++ = n; } *kbdmip++ = c; } execute(c, f, n); /* Do it. */ goto loop; } /* * Command execution. Look up the binding in the the * binding array, and do what it says. Return a very bad status * if there is no binding, or if the symbol has a type that * is not usable (there is no way to get this into a symbol table * entry now). Also fiddle with the flags. */ execute(c, f, n) { register SYMBOL *sp; register int status; if ((sp=binding[c]) != NULL) { thisflag = 0; status = (*sp->s_funcp)(f, n, c); lastflag = thisflag; return (status); } lastflag = 0; return (ABORT); } /* * Initialize all of the buffers * and windows. The buffer name is passed down as * an argument, because the main routine may have been * told to read in a file by default, and we want the * buffer name to be right. */ edinit(bname) char bname[]; { register BUFFER *bp; register WINDOW *wp; bp = bfind(bname, TRUE); /* Text buffer. */ blistp = bcreate(""); /* Special list buffer. */ wp = (WINDOW *) malloc(sizeof(WINDOW)); /* Initial window. */ if (bp==NULL || wp==NULL || blistp==NULL) abort(); curbp = bp; /* Current ones. */ wheadp = wp; curwp = wp; wp->w_wndp = NULL; /* Initialize window. */ wp->w_bufp = bp; bp->b_nwnd = 1; /* Displayed. */ wp->w_linep = bp->b_linep; wp->w_dotp = bp->b_linep; wp->w_doto = 0; wp->w_markp = NULL; wp->w_marko = 0; wp->w_toprow = 0; wp->w_ntrows = nrow-2; /* 2 = mode, echo. */ wp->w_force = 0; wp->w_flag = WFMODE|WFHARD; /* Full. */ } /* * Fancy quit command, as implemented * by Jeff. If the current buffer has changed * do a write current buffer. Otherwise run a command * interpreter in a subjob. Two of these will get you * out. Bound to "C-Z". */ jeffexit(f, n, k) { if ((curbp->b_flag&BFCHG) != 0) /* Changed. */ return (filesave(f, n, KRANDOM)); return (spawncli(f, n, KRANDOM)); /* Suspend. */ } /* * Quit command. If an argument, always * quit. Otherwise confirm if a buffer has been * changed and not written out. Normally bound * to "C-X C-C". */ quit(f, n, k) { register int s; if (f != FALSE /* Argument forces it. */ || anycb() == FALSE /* All buffers clean. */ || (s=eyesno("Quit")) == TRUE) { /* User says it's OK. */ vttidy(); exit(GOOD); } return (s); } /* * Begin a keyboard macro. * Error if not at the top level * in keyboard processing. Set up * variables and return. */ ctlxlp(f, n, k) { if (kbdmip!=NULL || kbdmop!=NULL) { eprintf("Not now"); return (FALSE); } eprintf("[Start macro]"); kbdmip = &kbdm[0]; return (TRUE); } /* * End keyboard macro. Check for * the same limit conditions as the * above routine. Set up the variables * and return to the caller. */ ctlxrp(f, n, k) { if (kbdmip == NULL) { eprintf("Not now"); return (FALSE); } eprintf("[End macro]"); kbdmip = NULL; return (TRUE); } /* * Execute a macro. * The command argument is the * number of times to loop. Quit as * soon as a command gets an error. * Return TRUE if all ok, else * FALSE. */ ctlxe(f, n, k) { register int c; register int af; register int an; register int s; if (kbdmip!=NULL || kbdmop!=NULL) { eprintf("Not now"); return (FALSE); } if (n <= 0) return (TRUE); do { kbdmop = &kbdm[0]; do { af = FALSE; an = 1; if ((c = *kbdmop++) == (KCTRL|'U')) { af = TRUE; an = *kbdmop++; c = *kbdmop++; } s = TRUE; } while (c!=(KCTLX|')') && (s=execute(c, af, an))==TRUE); kbdmop = NULL; } while (s==TRUE && --n); return (s); } /* * Abort. * Beep the beeper. * Kill off any keyboard macro, * etc., that is in progress. * Sometimes called as a routine, * to do general aborting of * stuff. */ ctrlg(f, n, k) { ttbeep(); if (kbdmip != NULL) { kbdm[0] = (KCTLX|')'); kbdmip = NULL; } return (ABORT); } /* * Display the version. All this does * is copy the text in the external "version" array into * the message system, and call the message reading code. * Don't call display if there is an argument. */ showversion(f, n, k) { register char **cpp; register char *cp; cpp = &version[0]; while ((cp = *cpp++) != NULL) { if (writemsg(cp) == FALSE) return (FALSE); } if (f != FALSE) /* No display if arg. */ return (TRUE); return (readmsg()); } SHAR_EOF if test 6208 -ne "`wc -c < 'main.c'`" then echo shar: error transmitting "'main.c'" '(should have been 6208 characters)' fi fi exit 0 # End of shell archive