jutz@pogo.UUCP (Curt Jutzi) (03/20/86)
while (n != 0) { dotp = curwp->w_dotp; doto = curwp->w_doto; if (dotp == curbp->b_linep) /* Hit end of buffer. */ return (FALSE); chunk = dotp->l_used-doto; /* Size of chunk. */ if (chunk > n) chunk = n; if (chunk == 0) { /* End of line, merge. */ lchange(WFHARD); if (ldelnewline() == FALSE || (kflag!=FALSE && kinsert('\n')==FALSE)) return (FALSE); --n; continue; } lchange(WFEDIT); cp1 = &dotp->l_text[doto]; /* Scrunch text. */ cp2 = cp1 + chunk; if (kflag != FALSE) { /* Kill? */ while (cp1 != cp2) { if (kinsert(*cp1) == FALSE) return (FALSE); ++cp1; } cp1 = &dotp->l_text[doto]; } while (cp2 != &dotp->l_text[dotp->l_used]) *cp1++ = *cp2++; dotp->l_used -= chunk; wp = wheadp; /* Fix windows */ while (wp != NULL) { if (wp->w_dotp==dotp && wp->w_doto>=doto) { wp->w_doto -= chunk; if (wp->w_doto < doto) wp->w_doto = doto; } if (wp->w_markp==dotp && wp->w_marko>=doto) { wp->w_marko -= chunk; if (wp->w_marko < doto) wp->w_marko = doto; } wp = wp->w_wndp; } n -= chunk; } return (TRUE); } /* * Delete a newline. Join the current line with the next line. If the next line * is the magic header line always return TRUE; merging the last line with the * header line can be thought of as always being a successful operation, even * if nothing is done, and this makes the kill buffer work "right". Easy cases * can be done by shuffling data around. Hard cases require that lines be moved * about in memory. Return FALSE on error and TRUE if all looks ok. Called by * "ldelete" only. */ ldelnewline() { register char *cp1; register char *cp2; register LINE *lp1; register LINE *lp2; register LINE *lp3; register WINDOW *wp; lp1 = curwp->w_dotp; lp2 = lp1->l_fp; if (lp2 == curbp->b_linep) { /* At the buffer end. */ if (lp1->l_used == 0) /* Blank line. */ lfree(lp1); return (TRUE); } if (lp2->l_used <= lp1->l_size-lp1->l_used) { cp1 = &lp1->l_text[lp1->l_used]; cp2 = &lp2->l_text[0]; while (cp2 != &lp2->l_text[lp2->l_used]) *cp1++ = *cp2++; wp = wheadp; while (wp != NULL) { if (wp->w_linep == lp2) wp->w_linep = lp1; if (wp->w_dotp == lp2) { wp->w_dotp = lp1; wp->w_doto += lp1->l_used; } if (wp->w_markp == lp2) { wp->w_markp = lp1; wp->w_marko += lp1->l_used; } wp = wp->w_wndp; } lp1->l_used += lp2->l_used; lp1->l_fp = lp2->l_fp; lp2->l_fp->l_bp = lp1; free((char *) lp2); return (TRUE); } if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL) return (FALSE); cp1 = &lp1->l_text[0]; cp2 = &lp3->l_text[0]; while (cp1 != &lp1->l_text[lp1->l_used]) *cp2++ = *cp1++; cp1 = &lp2->l_text[0]; while (cp1 != &lp2->l_text[lp2->l_used]) *cp2++ = *cp1++; lp1->l_bp->l_fp = lp3; lp3->l_fp = lp2->l_fp; lp2->l_fp->l_bp = lp3; lp3->l_bp = lp1->l_bp; wp = wheadp; while (wp != NULL) { if (wp->w_linep==lp1 || wp->w_linep==lp2) wp->w_linep = lp3; if (wp->w_dotp == lp1) wp->w_dotp = lp3; else if (wp->w_dotp == lp2) { wp->w_dotp = lp3; wp->w_doto += lp1->l_used; } if (wp->w_markp == lp1) wp->w_markp = lp3; else if (wp->w_markp == lp2) { wp->w_markp = lp3; wp->w_marko += lp1->l_used; } wp = wp->w_wndp; } free((char *) lp1); free((char *) lp2); return (TRUE); } /* * Delete all of the text saved in the kill buffer. Called by commands when a * new kill context is being created. The kill buffer array is released, just * in case the buffer has grown to immense size. No errors. */ kdelete() { if (kbufp != NULL) { free((char *) kbufp); kbufp = NULL; kused = 0; ksize = 0; } } /* * Insert a character to the kill buffer, enlarging the buffer if there isn't * any room. Always grow the buffer in chunks, on the assumption that if you * put something in the kill buffer you are going to put more stuff there too * later. Return TRUE if all is well, and FALSE on errors. */ kinsert(c) { register char *nbufp; register int i; if (kused == ksize) { if ((nbufp=malloc(ksize+KBLOCK)) == NULL) return (FALSE); for (i=0; i<ksize; ++i) nbufp[i] = kbufp[i]; if (kbufp != NULL) free((char *) kbufp); kbufp = nbufp; ksize += KBLOCK; } kbufp[kused++] = c; return (TRUE); } /* * This function gets characters from the kill buffer. If the character index * "n" is off the end, it returns "-1". This lets the caller just scan along * until it gets a "-1" back. */ kremove(n) { if (n >= kused) return (-1); else return (kbufp[n] & 0xFF); } /* * * REGION.C MODULE * * */ /* * The routines in this file * deal with the region, that magic space * between "." and mark. Some functions are * commands. Some functions are just for * internal use. */ #include <stdio.h> #include "ed.h" /* * Kill the region. Ask "getregion" * to figure out the bounds of the region. * Move "." to the start, and kill the characters. * Bound to "C-W". */ killregion(f, n) { register int s; REGION region; if ((s=getregion(®ion)) != TRUE) return (s); if ((lastflag&CFKILL) == 0) /* This is a kill type */ kdelete(); /* command, so do magic */ thisflag |= CFKILL; /* kill buffer stuff. */ curwp->w_dotp = region.r_linep; curwp->w_doto = region.r_offset; return (ldelete(region.r_size, TRUE)); } /* * Copy all of the characters in the * region to the kill buffer. Don't move dot * at all. This is a bit like a kill region followed * by a yank. Bound to "M-W". */ copyregion(f, n) { register LINE *linep; register int loffs; register int s; REGION region; if ((s=getregion(®ion)) != TRUE) return (s); if ((lastflag&CFKILL) == 0) /* Kill type command. */ kdelete(); thisflag |= CFKILL; linep = region.r_linep; /* Current line. */ loffs = region.r_offset; /* Current offset. */ while (region.r_size--) { if (loffs == llength(linep)) { /* End of line. */ if ((s=kinsert('\n')) != TRUE) return (s); linep = lforw(linep); loffs = 0; } else { /* Middle of line. */ if ((s=kinsert(lgetc(linep, loffs))) != TRUE) return (s); ++loffs; } } return (TRUE); } /* * Lower case region. Zap all of the upper * case characters in the region to lower case. Use * the region code to set the limits. Scan the buffer, * doing the changes. Call "lchange" to ensure that * redisplay is done in all buffers. Bound to * "C-X C-L". */ lowerregion(f, n) { register LINE *linep; register int loffs; register int c; register int s; REGION region; if ((s=getregion(®ion)) != TRUE) return (s); lchange(WFHARD); linep = region.r_linep; loffs = region.r_offset; while (region.r_size--) { if (loffs == llength(linep)) { linep = lforw(linep); loffs = 0; } else { c = lgetc(linep, loffs); if (c>='A' && c<='Z') lputc(linep, loffs, c+'a'-'A'); ++loffs; } } return (TRUE); } /* * Upper case region. Zap all of the lower * case characters in the region to upper case. Use * the region code to set the limits. Scan the buffer, * doing the changes. Call "lchange" to ensure that * redisplay is done in all buffers. Bound to * "C-X C-L". */ upperregion(f, n) { register LINE *linep; register int loffs; register int c; register int s; REGION region; if ((s=getregion(®ion)) != TRUE) return (s); lchange(WFHARD); linep = region.r_linep; loffs = region.r_offset; while (region.r_size--) { if (loffs == llength(linep)) { linep = lforw(linep); loffs = 0; } else { c = lgetc(linep, loffs); if (c>='a' && c<='z') lputc(linep, loffs, c-'a'+'A'); ++loffs; } } return (TRUE); } /* * This routine figures out the * bounds of the region in the current window, and * fills in the fields of the "REGION" structure pointed * to by "rp". Because the dot and mark are usually very * close together, we scan outward from dot looking for * mark. This should save time. Return a standard code. * Callers of this routine should be prepared to get * an "ABORT" status; we might make this have the * conform thing later. */ getregion(rp) register REGION *rp; { register LINE *flp; register LINE *blp; register int fsize; register int bsize; if (curwp->w_markp == NULL) { mlwrite("No mark set in this window"); return (FALSE); } if (curwp->w_dotp == curwp->w_markp) { rp->r_linep = curwp->w_dotp; if (curwp->w_doto < curwp->w_marko) { rp->r_offset = curwp->w_doto; rp->r_size = curwp->w_marko-curwp->w_doto; } else { rp->r_offset = curwp->w_marko; rp->r_size = curwp->w_doto-curwp->w_marko; } return (TRUE); } blp = curwp->w_dotp; bsize = curwp->w_doto; flp = curwp->w_dotp; fsize = llength(flp)-curwp->w_doto+1; while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) { if (flp != curbp->b_linep) { flp = lforw(flp); if (flp == curwp->w_markp) { rp->r_linep = curwp->w_dotp; rp->r_offset = curwp->w_doto; rp->r_size = fsize+curwp->w_marko; return (TRUE); } fsize += llength(flp)+1; } if (lback(blp) != curbp->b_linep) { blp = lback(blp); bsize += llength(blp)+1; if (blp == curwp->w_markp) { rp->r_linep = blp; rp->r_offset = curwp->w_marko; rp->r_size = bsize - curwp->w_marko; return (TRUE); } } } mlwrite("Bug: lost mark"); return (FALSE); } /* * * SPAWN.C MODULE * */ /* * The routines in this file are called to create a subjob running a command * interpreter. This code is a big fat nothing on CP/M-86. You lose. */ #include <stdio.h> #include "ed.h" #if AMIGA #define NEW 1006 #endif #if VMS #define EFN 0 /* Event flag. */ #include <ssdef.h> /* Random headers. */ #include <stsdef.h> #include <descrip.h> #include <iodef.h> extern int oldmode[]; /* In "termio.c" */ extern int newmode[]; /* In "termio.c" */ extern short iochan; /* In "termio.c" */ #endif #if MSDOS #include <dos.h> #endif #if V7 #include <signal.h> #endif /* * Create a subjob with a copy of the command intrepreter in it. When the * command interpreter exits, mark the screen as garbage so that you do a full * repaint. Bound to "C-C". The message at the start in VMS puts out a newline. * Under some (unknown) condition, you don't get one free when DCL starts up. */ spawncli(f, n) { #if AMIGA long newcli; newcli = Open("CON:1/1/639/199/MicroEmacs Subprocess", NEW); mlwrite("[Starting new CLI]"); sgarbf = TRUE; Execute("", newcli, 0); Close(newcli); return(TRUE); #endif #if V7 register char *cp; char *getenv(); #endif #if VMS movecursor(term.t_nrow, 0); /* In last line. */ mlputs("[Starting DCL]\r\n"); (*term.t_flush)(); /* Ignore "ttcol". */ sgarbf = TRUE; return (sys(NULL)); /* NULL => DCL. */ #endif #if CPM mlwrite("Not in CP/M-86"); #endif #if MSDOS movecursor(term.t_nrow, 0); /* Seek to last line. */ (*term.t_flush)(); sys("\\command.com", ""); /* Run CLI. */ sgarbf = TRUE; return(TRUE); #endif #if V7 movecursor(term.t_nrow, 0); /* Seek to last line. */ (*term.t_flush)(); ttclose(); /* stty to old settings */ if ((cp = getenv("SHELL")) != NULL && *cp != '\0') system(cp); else system("exec /bin/sh"); sgarbf = TRUE; sleep(2); ttopen(); return(TRUE); #endif } /* * Run a one-liner in a subjob. When the command returns, wait for a single * character to be typed, then mark the screen as garbage so a full repaint is * done. Bound to "C-X !". */ spawn(f, n) { register int s; char line[NLINE]; #if AMIGA long newcli; newcli = Open("CON:1/1/639/199/MicroEmacs Subprocess", NEW); if ((s=mlreply("CLI command: ", line, NLINE)) != TRUE) return (s); Execute(line,0,newcli); Close(newcli); while ((*term.t_getchar)() != '\r') /* Pause. */ ; sgarbf = TRUE; return(TRUE); #endif #if VMS if ((s=mlreply("DCL command: ", line, NLINE)) != TRUE) return (s); (*term.t_putchar)('\n'); /* Already have '\r' */ (*term.t_flush)(); s = sys(line); /* Run the command. */ mlputs("\r\n\n[End]"); /* Pause. */ (*term.t_flush)(); while ((*term.t_getchar)() != '\r') ; sgarbf = TRUE; return (s); #endif #if CPM mlwrite("Not in CP/M-86"); return (FALSE); #endif #if MSDOS if ((s=mlreply("MS-DOS command: ", line, NLINE)) != TRUE) return (s); system(line); while ((*term.t_getchar)() != '\r') /* Pause. */ ; sgarbf = TRUE; return (TRUE); #endif #if V7 if ((s=mlreply("! ", line, NLINE)) != TRUE) return (s); (*term.t_putchar)('\n'); /* Already have '\r' */ (*term.t_flush)(); ttclose(); /* stty to old modes */ system(line); sleep(2); ttopen(); mlputs("[End]"); /* Pause. */ (*term.t_flush)(); while ((s = (*term.t_getchar)()) != '\r' && s != ' ') ; sgarbf = TRUE; return (TRUE); #endif } #if VMS /* * Run a command. The "cmd" is a pointer to a command string, or NULL if you * want to run a copy of DCL in the subjob (this is how the standard routine * LIB$SPAWN works. You have to do wierd stuff with the terminal on the way in * and the way out, because DCL does not want the channel to be in raw mode. */ sys(cmd) register char *cmd; { struct dsc$descriptor cdsc; struct dsc$descriptor *cdscp; long status; long substatus; long iosb[2]; status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0, oldmode, sizeof(oldmode), 0, 0, 0, 0); if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL) return (FALSE); cdscp = NULL; /* Assume DCL. */ if (cmd != NULL) { /* Build descriptor. */ cdsc.dsc$a_pointer = cmd; cdsc.dsc$w_length = strlen(cmd); cdsc.dsc$b_dtype = DSC$K_DTYPE_T; cdsc.dsc$b_class = DSC$K_CLASS_S; cdscp = &cdsc; } status = LIB$SPAWN(cdscp, 0, 0, 0, 0, 0, &substatus, 0, 0, 0); if (status != SS$_NORMAL) substatus = status; status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0, newmode, sizeof(newmode), 0, 0, 0, 0); if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL) return (FALSE); if ((substatus&STS$M_SUCCESS) == 0) /* Command failed. */ return (FALSE); return (TRUE); } #endif #if MSDOS /* * This routine, once again by Bob McNamara, is a C translation of the "system" * routine in the MWC-86 run time library. It differs from the "system" routine * in that it does not unconditionally append the string ".exe" to the end of * the command name. We needed to do this because we want to be able to spawn * off "command.com". We really do not understand what it does, but if you don't * do it exactly "malloc" starts doing very very strange things. */ sys(cmd, tail) char *cmd; char *tail; { #if MWC_86 register unsigned n; extern char *__end; n = __end + 15; n >>= 4; n = ((n + dsreg() + 16) & 0xFFF0) + 16; return(execall(cmd, tail, n)); #endif #if LATTICE return forklp(cmd, tail, NULL); #endif } #endif /* * * TCAP.C MODULE * */ #include <stdio.h> #include "ed.h" #if TERMCAP #define NROW 24 #define NCOL 80 #define BEL 0x07 #define ESC 0x1B extern int ttopen(); extern int ttgetc(); extern int ttputc(); extern int ttflush(); extern int ttclose(); extern int tcapmove(); extern int tcapeeol(); extern int tcapeeop(); extern int tcapbeep(); extern int tcapopen(); extern int tput(); extern char *tgoto(); #define TCAPSLEN 315 char tcapbuf[TCAPSLEN]; char PC, *CM, *CL, *CE, *UP, *CD; TERM term = { NROW-1, NCOL, tcapopen, ttclose, ttgetc, ttputc, ttflush, tcapmove, tcapeeol, tcapeeop, tcapbeep }; tcapopen() { char *getenv(); char *t, *p, *tgetstr(); char tcbuf[1024]; char *tv_stype; char err_str[72]; if ((tv_stype = getenv("TERM")) == NULL) { puts("Environment variable TERM not defined!"); exit(1); } if((tgetent(tcbuf, tv_stype)) != 1) { sprintf(err_str, "Unknown terminal type %s!", tv_stype); puts(err_str); exit(1); } p = tcapbuf; t = tgetstr("pc", &p); if(t) PC = *t; CD = tgetstr("cd", &p); CM = tgetstr("cm", &p); CE = tgetstr("ce", &p); UP = tgetstr("up", &p); if(CD == NULL || CM == NULL || CE == NULL || UP == NULL) { puts("Incomplete termcap entry\n"); exit(1); } if (p >= &tcapbuf[TCAPSLEN]) { puts("Terminal description too big!\n"); exit(1); } ttopen(); } tcapmove(row, col) register int row, col; { putpad(tgoto(CM, col, row)); } tcapeeol() { putpad(CE); } tcapeeop() { putpad(CD); } tcapbeep() { ttputc(BEL); } putpad(str) char *str; { tputs(str, 1, ttputc); } putnpad(str, n) char *str; { tputs(str, n, ttputc); } #endif TERMCAP /* * * BUFFER.C MODULE * */ /* * Buffer management. * Some of the functions are internal, * and some are actually attached to user * keys. Like everyone else, they set hints * for the display system. */ #include <stdio.h> #include "ed.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. */ usebuffer(f, n) { register int s; char bufn[NBUFN]; if ((s=mlreply("Use buffer: ", bufn, NBUFN)) != TRUE) return (s); return(useb(bufn)); } /* * 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. * */ useb(bufn) char bufn[]; { register BUFFER *bp; register WINDOW *wp; if ((bp=bfind(bufn, TRUE, 0)) == NULL) return (FALSE); if (--curbp->b_nwnd == 0) { /* Last use. */ 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. */ curwp->w_bufp = bp; curwp->w_linep = bp->b_linep; /* For macros, ignored. */ curwp->w_flag |= WFMODE|WFFORCE|WFHARD; /* Quite nasty. */ if (bp->b_nwnd++ == 0) { /* First use. */ curwp->w_dotp = bp->b_dotp; curwp->w_doto = bp->b_doto; curwp->w_markp = bp->b_markp; curwp->w_marko = bp->b_marko; return (TRUE); } wp = wheadp; /* Look for old. */ 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; } 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!). Get quite upset * if the buffer is being displayed. Clear the buffer (ask * if the buffer has been changed). Then free the header * line and the buffer header. Bound to "C-X K". */ killbuffer(f, n) { register BUFFER *bp; register BUFFER *bp1; register BUFFER *bp2; register int s; char bufn[NBUFN]; if ((s=mlreply("Kill buffer: ", bufn, NBUFN)) != TRUE) return (s); if ((bp=bfind(bufn, FALSE, 0)) == NULL) /* Easy if unknown. */ return (TRUE); if (bp->b_nwnd != 0) { /* Error if on screen. */ mlwrite("Buffer is being displayed"); return (FALSE); } if ((s=bclear(bp)) != TRUE) /* Blow text away. */ return (s); free((char *) bp->b_linep); /* Release header line. */ bp1 = NULL; /* Find the header. */ bp2 = bheadp; while (bp2 != bp) { 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; free((char *) bp); /* Release buffer block */ return (TRUE); } /* * List all of the active * buffers. First update the special * buffer that holds the list. Next make * sure at least 1 window is displaying the * buffer list, splitting the screen if this * is what it takes. Lastly, repaint all of * the windows that are displaying the * list. Bound to "C-X C-B". */ listbuffers(f, n) { register WINDOW *wp; register BUFFER *bp; register int s; if ((s=makelist()) != TRUE) return (s); if (blistp->b_nwnd == 0) { /* Not on screen yet. */ if ((wp=wpopup()) == NULL) return (FALSE); bp = wp->w_bufp; if (--bp->b_nwnd == 0) { bp->b_dotp = wp->w_dotp; bp->b_doto = wp->w_doto; bp->b_markp = wp->w_markp; bp->b_marko = wp->w_marko; } wp->w_bufp = blistp; ++blistp->b_nwnd; } wp = wheadp; while (wp != NULL) { if (wp->w_bufp == blistp) { wp->w_linep = lforw(blistp->b_linep); wp->w_dotp = lforw(blistp->b_linep); wp->w_doto = 0; wp->w_markp = NULL; wp->w_marko = 0; wp->w_flag |= WFMODE|WFHARD; } wp = wp->w_wndp; } return (TRUE); } /* * This routine rebuilds the * text in the special secret buffer * that holds the buffer list. It is called * by the list buffers command. Return TRUE * if everything works. Return FALSE if there * is an error (if there is no memory). */ makelist() { register char *cp1; register char *cp2; register int c; register BUFFER *bp; register LINE *lp; register int nbytes; register int s; register int type; char b[6+1]; char line[128]; blistp->b_flag &= ~BFCHG; /* Don't complain! */ if ((s=bclear(blistp)) != TRUE) /* Blow old text away */ return (s); strcpy(blistp->b_fname, ""); if (addline("C Size Buffer File") == FALSE || addline("- ---- ------ ----") == FALSE) return (FALSE); bp = bheadp; /* For all buffers */ while (bp != NULL) { if ((bp->b_flag&BFTEMP) != 0) { /* Skip magic ones. */ bp = bp->b_bufp; continue; } cp1 = &line[0]; /* Start at left edge */ if ((bp->b_flag&BFCHG) != 0) /* "*" if changed */ *cp1++ = '*'; else *cp1++ = ' '; *cp1++ = ' '; /* Gap. */ nbytes = 0; /* Count bytes in buf. */ lp = lforw(bp->b_linep); while (lp != bp->b_linep) { nbytes += llength(lp)+1; lp = lforw(lp); } itoa(b, 6, nbytes); /* 6 digit buffer size. */ cp2 = &b[0]; while ((c = *cp2++) != 0) *cp1++ = c; *cp1++ = ' '; /* Gap. */ cp2 = &bp->b_bname[0]; /* Buffer name */ while ((c = *cp2++) != 0) *cp1++ = c; cp2 = &bp->b_fname[0]; /* File name */ if (*cp2 != 0) { while (cp1 < &line[1+1+6+1+NBUFN+1]) *cp1++ = ' '; while ((c = *cp2++) != 0) { if (cp1 < &line[128-1]) *cp1++ = c; } } *cp1 = 0; /* Add to the buffer. */ if (addline(line) == FALSE) return (FALSE); bp = bp->b_bufp; } return (TRUE); /* All done */ } itoa(buf, width, num) register char buf[]; register int width; register int num;