dmt@pegasus.ATT.COM (Dave Tutelman) (03/11/90)
Posting-number: Volume 11, Issue 10 Submitted-by: dmt@pegasus.ATT.COM (Dave Tutelman) Archive-name: stevie3.69a/part02 : This is a shar archive. Extract with sh, not csh. : The rest of this file will extract: : alloc.c cmdline.c edit.c enveval.c fileio.c help.c hexchars.c env.h keymap.h ops.h param.h regexp.h regmagic.h echo extracting - alloc.c sed 's/^X//' > alloc.c << '!EOR!' X/* $Header: /nw/tony/src/stevie/src/RCS/alloc.c,v 1.5 89/08/06 09:49:22 tony Exp $ X * X * Various allocation routines and routines returning information about X * allocated objects. X */ X X#include "stevie.h" X Xchar * Xalloc(size) Xunsigned size; X{ X char *p; /* pointer to new storage space */ X X p = malloc(size); X if ( p == (char *)NULL ) { /* if there is no more room... */ X emsg("alloc() is unable to find memory!"); X } X return(p); X} X Xchar * Xstrsave(string) Xchar *string; X{ X return(strcpy(alloc((unsigned)(strlen(string)+1)),string)); X} X Xscreenalloc() X{ X /* X * If we're changing the size of the screen, free the old arrays X */ X if (Realscreen != NULL) X free(Realscreen); X if (Nextscreen != NULL) X free(Nextscreen); X X Realscreen = malloc((unsigned)(Rows*Columns)); X Nextscreen = malloc((unsigned)(Rows*Columns)); X if (!Realscreen || !Nextscreen) X return (-1); X else return (0); X} X X/* X * Allocate and initialize a new line structure with room for X * 'nchars'+1 characters. We add one to nchars here to allow for X * null termination because all the callers would just do it otherwise. X */ XLINE * Xnewline(nchars) Xint nchars; X{ X register LINE *l; X X if ((l = (LINE *) alloc(sizeof(LINE))) == NULL) X return (LINE *) NULL; X X l->s = alloc((unsigned) (nchars+1)); /* the line is empty */ X if (l->s == NULL) return (LINE *) NULL; X l->s[0] = NUL; X l->size = nchars + 1; X X l->prev = (LINE *) NULL; /* should be initialized by caller */ X l->next = (LINE *) NULL; X X return l; X} X X/* X * filealloc() - construct an initial empty file buffer X */ Xvoid Xfilealloc() X{ X if ((Filemem->linep = newline(0)) == NULL) { X fprintf(stderr,"Unable to allocate file memory!\n"); X exit(1); X } X if ((Filetop->linep = newline(0)) == NULL) { X fprintf(stderr,"Unable to allocate file memory!\n"); X exit(1); X } X if ((Fileend->linep = newline(0)) == NULL) { X fprintf(stderr,"Unable to allocate file memory!\n"); X exit(1); X } X Filemem->index = 0; X Filetop->index = 0; X Fileend->index = 0; X X Filetop->linep->next = Filemem->linep; /* connect Filetop to Filemem */ X Filemem->linep->prev = Filetop->linep; X X Filemem->linep->next = Fileend->linep; /* connect Filemem to Fileend */ X Fileend->linep->prev = Filemem->linep; X X *Curschar = *Filemem; X *Topchar = *Filemem; X X Filemem->linep->num = 0; X Fileend->linep->num = 0xffff; X X clrall(); /* clear all marks */ X u_clear(); /* clear the undo buffer */ X} X X/* X * freeall() - free the current buffer X * X * Free all lines in the current buffer. X */ Xvoid Xfreeall() X{ X register LINE *lp, *xlp; X X for (lp = Filetop->linep; lp != NULL ;lp = xlp) { X if (lp->s != NULL) X free(lp->s); X xlp = lp->next; X free((char *)lp); X } X X Curschar->linep = NULL; /* clear pointers */ X Filetop->linep = NULL; X Filemem->linep = NULL; X Fileend->linep = NULL; X X u_clear(); X} X X/* X * bufempty() - return TRUE if the buffer is empty X */ Xbool_t Xbufempty() X{ X return (buf1line() && Filemem->linep->s[0] == NUL); X} X X/* X * buf1line() - return TRUE if there is only one line X */ Xbool_t Xbuf1line() X{ X return (Filemem->linep->next == Fileend->linep); X} X X/* X * lineempty() - return TRUE if the current line is empty X */ Xbool_t Xlineempty() X{ X return (Curschar->linep->s[0] == NUL); X} X X/* X * endofline() - return TRUE if the given position is at end of line X * X * This routine will probably never be called with a position resting X * on the NUL byte, but handle it correctly in case it happens. X */ Xbool_t Xendofline(p) Xregister LPTR *p; X{ X return (p->linep->s[p->index] == NUL || p->linep->s[p->index+1] == NUL); X} X/* X * canincrease(n) - returns TRUE if the current line can be increased 'n' bytes X * X * This routine returns immediately if the requested space is available. X * If not, it attempts to allocate the space and adjust the data structures X * accordingly. If everything fails it returns FALSE. X */ Xbool_t Xcanincrease(n) Xregister int n; X{ X register int nsize; X register char *s; /* pointer to new space */ X X nsize = strlen(Curschar->linep->s) + 1 + n; /* size required */ X X if (nsize <= Curschar->linep->size) X return TRUE; X X /* X * Need to allocate more space for the string. Allow some extra X * space on the assumption that we may need it soon. This avoids X * excessive numbers of calls to malloc while entering new text. X */ X if ((s = alloc((unsigned) (nsize + SLOP))) == NULL) { X emsg("Can't add anything, file is too big!"); X State = NORMAL; X return FALSE; X } X X Curschar->linep->size = nsize + SLOP; X strcpy(s, Curschar->linep->s); X free(Curschar->linep->s); X Curschar->linep->s = s; X X return TRUE; X} X Xchar * Xmkstr(c) Xchar c; X{ X static char s[2]; X X s[0] = c; X s[1] = NUL; X X return s; X} !EOR! echo extracting - cmdline.c sed 's/^X//' > cmdline.c << '!EOR!' X/* $Header: /nw/tony/src/stevie/src/RCS/cmdline.c,v 1.20 89/08/13 11:41:23 tony Exp $ X * X * Routines to parse and execute "command line" commands, such as searches X * or colon commands. X */ X X#include "stevie.h" X Xstatic char *altfile = NULL; /* alternate file */ Xstatic int altline; /* line # in alternate file */ X Xstatic char *nowrtmsg = "No write since last change (use ! to override)"; Xstatic char *nooutfile = "No output file"; Xstatic char *morefiles = "more files to edit"; X Xextern char **files; /* used for "n" and "rew" */ Xextern int numfiles, curfile; X X#define CMDSZ 100 /* size of the command buffer */ X Xstatic void get_range(); Xstatic LPTR *get_line(); X X/* X * getcmdln() - read a command line from the terminal X * X * Reads a command line started by typing '/', '?', '!', or ':'. Returns a X * pointer to the string that was read. For searches, an optional trailing X * '/' or '?' is removed. X */ Xchar * Xgetcmdln(firstc) Xchar firstc; X{ X static char buff[CMDSZ]; X register char *p = buff; X register int c; X register char *q; X X gotocmd(TRUE, firstc); X X /* collect the command string, handling '\b' and @ */ X do { X switch (c = vgetc()) { X X default: /* a normal character */ X outchar(c); X *p++ = c; X break; X X case BS: X if (p > buff) { X /* X * this is gross, but it relies X * only on 'gotocmd' X */ X p--; X gotocmd(TRUE, firstc); X for (q = buff; q < p ;q++) X outchar(*q); X } else { X msg(""); X return NULL; /* back to cmd mode */ X } X break; X X case '@': /* line kill */ X p = buff; X gotocmd(TRUE, firstc); X break; X X case ESC: /* abandon command */ X msg(""); X return NULL; X break; X X case NL: /* done reading the line */ X case CR: X break; X } X } while (c != NL && c != CR); X X *p = '\0'; X X if (firstc == '/' || firstc == '?') { /* did we do a search? */ X /* X * Look for a terminating '/' or '?'. This will be the first X * one that isn't quoted. Truncate the search string there. X */ X for (p = buff; *p ;) { X if (*p == firstc) { /* we're done */ X *p = '\0'; X break; X } else if (*p == '\\') /* next char quoted */ X p += 2; X else X p++; /* normal char */ X } X } X return buff; X} X X/* X * docmdln() - handle a colon command X * X * Handles a colon command received interactively by getcmdln() or from X * the environment variable "EXINIT" (or eventually .virc). X */ Xvoid Xdocmdln(cmdline) Xchar *cmdline; X{ X char buff[CMDSZ]; X char cmdbuf[CMDSZ]; X char argbuf[CMDSZ]; X char *cmd, *arg; X register char *p; X /* X * The next two variables contain the bounds of any range given in a X * command. If no range was given, both contain null line pointers. X * If only a single line was given, u_pos will contain a null line X * pointer. X */ X LPTR l_pos, u_pos; X X X /* X * Clear the range variables. X */ X l_pos.linep = (struct line *) NULL; X u_pos.linep = (struct line *) NULL; X X if (cmdline == NULL) X return; X X if (strlen(cmdline) > CMDSZ-2) { X msg("Error: command line too long"); X return; X } X strcpy(buff, cmdline); X X /* skip any initial white space */ X for (cmd = buff; *cmd != NUL && isspace(*cmd) ;cmd++) X ; X X if (*cmd == '%') { /* change '%' to "1,$" */ X strcpy(cmdbuf, "1,$"); /* kind of gross... */ X strcat(cmdbuf, cmd+1); X strcpy(cmd, cmdbuf); X } X X while ((p=strchr(cmd, '%')) != NULL && *(p-1) != '\\') { X /* change '%' to Filename */ X if (Filename == NULL) { X emsg("No filename"); X return; X } X *p= NUL; X strcpy (cmdbuf, cmd); X strcat (cmdbuf, Filename); X strcat (cmdbuf, p+1); X strcpy(cmd, cmdbuf); X msg(cmd); /*repeat */ X } X X while ((p=strchr(cmd, '#')) != NULL && *(p-1) != '\\') { X /* change '#' to Altname */ X if (altfile == NULL) { X emsg("No alternate file"); X return; X } X *p= NUL; X strcpy (cmdbuf, cmd); X strcat (cmdbuf, altfile); X strcat (cmdbuf, p+1); X strcpy(cmd, cmdbuf); X msg(cmd); /*repeat */ X } X X /* X * Parse a range, if present (and update the cmd pointer). X */ X get_range(&cmd, &l_pos, &u_pos); X X if (l_pos.linep != NULL) { X if (LINEOF(&l_pos) > LINEOF(&u_pos)) { X emsg("Invalid range"); X return; X } X } X X strcpy(cmdbuf, cmd); /* save the unmodified command */ X X /* isolate the command and find any argument */ X for ( p=cmd; *p != NUL && ! isspace(*p); p++ ) X ; X if ( *p == NUL ) X arg = NULL; X else { X *p = NUL; X for (p++; *p != NUL && isspace(*p) ;p++) X ; X if (*p == NUL) X arg = NULL; X else { X strcpy(argbuf, p); X arg = argbuf; X } X } X if (strcmp(cmd,"q!") == 0) X getout(); X if (strcmp(cmd,"q") == 0) { X if (Changed) X emsg(nowrtmsg); X else { X if ((curfile + 1) < numfiles) X emsg(morefiles); X else X getout(); X } X return; X } X if (strcmp(cmd,"w") == 0) { X if (arg == NULL) { X if (Filename != NULL) { X writeit(Filename, &l_pos, &u_pos); X } else X emsg(nooutfile); X } X else X writeit(arg, &l_pos, &u_pos); X return; X } X if (strcmp(cmd,"wq") == 0) { X if (Filename != NULL) { X if (writeit(Filename, (LPTR *)NULL, (LPTR *)NULL)) X getout(); X } else X emsg(nooutfile); X return; X } X if (strcmp(cmd, "x") == 0) { X doxit(); X return; X } X X if (strcmp(cmd,"f") == 0 && arg == NULL) { X fileinfo(); X return; X } X if (*cmd == 'n') { X if ((curfile + 1) < numfiles) { X /* X * stuff ":e[!] FILE\n" X */ X stuffin(":e"); X if (cmd[1] == '!') X stuffin("!"); X stuffin(" "); X stuffin(files[++curfile]); X stuffin("\n"); X } else X emsg("No more files!"); X return; X } X if (*cmd == 'N') { X if (curfile > 0) { X /* X * stuff ":e[!] FILE\n" X */ X stuffin(":e"); X if (cmd[1] == '!') X stuffin("!"); X stuffin(" "); X stuffin(files[--curfile]); X stuffin("\n"); X } else X emsg("No more files!"); X return; X } X if (strncmp(cmd, "rew", 3) == 0) { X if (numfiles <= 1) /* nothing to rewind */ X return; X curfile = 0; X /* X * stuff ":e[!] FILE\n" X */ X stuffin(":e"); X if (cmd[3] == '!') X stuffin("!"); X stuffin(" "); X stuffin(files[0]); X stuffin("\n"); X return; X } X if (strcmp(cmd,"e") == 0 || strcmp(cmd,"e!") == 0) { X (void) doecmd(arg, cmd[1] == '!'); X return; X } X /* X * The command ":e#" gets expanded to something like ":efile", so X * detect that case here. X */ X if (*cmd == 'e' && arg == NULL) { X if (cmd[1] == '!') X (void) doecmd(&cmd[2], TRUE); X else X (void) doecmd(&cmd[1], FALSE); X return; X } X if (strcmp(cmd,"f") == 0) { X EnvEval (arg, CMDSZ); /* expand environment vars */ X Filename = strsave(arg); X filemess(""); X return; X } X if (strcmp(cmd,"r") == 0) { X if (arg == NULL) { X badcmd(); X return; X } X if (readfile(arg, Curschar, 1)) { X emsg("Can't open file"); X return; X } X updatescreen(); X CHANGED; X return; X } X if (strcmp(cmd,"=") == 0) { X smsg("%d", cntllines(Filemem, &l_pos)); X return; X } X if (strncmp(cmd,"ta", 2) == 0) { X dotag(arg, cmd[2] == '!'); X return; X } X if (strncmp(cmd,"untag", 5) == 0) { X if (P(P_TG)) X dountag(cmd[5]); X else X emsg("Tag stacking not enabled"); X return; X } X if (strncmp(cmd,"set", 2) == 0) { X doset(arg); X return; X } X if (strcmp(cmd,"help") == 0) { X if (help()) { X screenclear(); X updatescreen(); X } X return; X } X if (strncmp(cmd, "ve", 2) == 0) { X extern char *Version; X X msg(Version); X return; X } X if (strcmp(cmd, "sh") == 0) { X doshell(NULL); X return; X } X if (*cmd == '!') { X doshell(cmdbuf+1); X return; X } X if (strncmp(cmd, "s/", 2) == 0) { X dosub(&l_pos, &u_pos, cmdbuf+1); X return; X } X if (strncmp(cmd, "g/", 2) == 0) { X doglob(&l_pos, &u_pos, cmdbuf+1); X return; X } X /* X * If we got a line, but no command, then go to the line. X */ X if (*cmd == NUL && l_pos.linep != NULL) { X *Curschar = l_pos; X return; X } X X badcmd(); X} X X Xdoxit() X{ X if (Changed) { X if (Filename != NULL) { X if (!writeit(Filename, (LPTR *)NULL, (LPTR *)NULL)) X return; X } else { X emsg(nooutfile); X return; X } X } X if ((curfile + 1) < numfiles) X emsg(morefiles); X else X getout(); X} X X/* X * get_range - parse a range specifier X * X * Ranges are of the form: X * X * addr[,addr] X * X * where 'addr' is: X * X * $ [+- NUM] X * 'x [+- NUM] (where x denotes a currently defined mark) X * . [+- NUM] X * NUM X * X * The pointer *cp is updated to point to the first character following X * the range spec. If an initial address is found, but no second, the X * upper bound is equal to the lower. X */ Xstatic void Xget_range(cp, lower, upper) Xregister char **cp; XLPTR *lower, *upper; X{ X register LPTR *l; X register char *p; X X if ((l = get_line(cp)) == NULL) X return; X X *lower = *l; X X for (p = *cp; *p != NUL && isspace(*p) ;p++) X ; X X *cp = p; X X if (*p != ',') { /* is there another line spec ? */ X *upper = *lower; X return; X } X X *cp = ++p; X X if ((l = get_line(cp)) == NULL) { X *upper = *lower; X return; X } X X *upper = *l; X} X Xstatic LPTR * Xget_line(cp) Xchar **cp; X{ X static LPTR pos; X LPTR *lp; X register char *p, c; X register int lnum; X X pos.index = 0; /* shouldn't matter... check back later */ X X p = *cp; X /* X * Determine the basic form, if present. X */ X switch (c = *p++) { X X case '$': X pos.linep = Fileend->linep->prev; X break; X X case '.': X pos.linep = Curschar->linep; X break; X X case '\'': X if ((lp = getmark(*p++)) == NULL) { X emsg("Unknown mark"); X return (LPTR *) NULL; X } X pos = *lp; X break; X X case '0': case '1': case '2': case '3': case '4': X case '5': case '6': case '7': case '8': case '9': X for (lnum = c - '0'; isdigit(*p) ;p++) X lnum = (lnum * 10) + (*p - '0'); X X pos = *gotoline(lnum); X break; X X default: X return (LPTR *) NULL; X } X X while (*p != NUL && isspace(*p)) X p++; X X if (*p == '-' || *p == '+') { X bool_t neg = (*p++ == '-'); X X for (lnum = 0; isdigit(*p) ;p++) X lnum = (lnum * 10) + (*p - '0'); X X if (neg) X lnum = -lnum; X X pos = *gotoline( cntllines(Filemem, &pos) + lnum ); X } X X *cp = p; X return &pos; X} X Xvoid Xbadcmd() X{ X emsg("Unrecognized command"); X} X Xbool_t Xdoecmd(arg, force) Xchar *arg; Xbool_t force; X{ X int line = 1; /* line # to go to in new file */ X X if (!force && Changed) { X emsg(nowrtmsg); X if (altfile) X free(altfile); X altfile = strsave(arg); X return FALSE; X } X if (arg != NULL) { X /* X * First detect a ":e" on the current file. This is mainly X * for ":ta" commands where the destination is within the X * current file. X */ X if (Filename != NULL && strcmp(arg, Filename) == 0) { X if (!Changed || (Changed && !force)) X return TRUE; X } X if (altfile) { X if (strcmp (arg, altfile) == 0) X line = altline; X free(altfile); X } X altfile = Filename; X altline = cntllines(Filemem, Curschar); X Filename = strsave(arg); X } X if (Filename == NULL) { X emsg("No filename"); X return FALSE; X } X X /* clear mem and read file */ X freeall(); X filealloc(); X UNCHANGED; X X if (readfile(Filename, Filemem, 0)) X filemess("[New File]"); X X *Topchar = *Curschar; X if (line != 1) { X stuffnum(line); X stuffin("G"); X } X do_mlines(); X setpcmark(); X updatescreen(); X return TRUE; X} X Xvoid Xgotocmd(clr, firstc) Xbool_t clr; Xchar firstc; X{ X windgoto(Rows-1,0); X if (clr) X CLEOL; /* clear the bottom line */ X if (firstc) X outchar(firstc); X} X X/* X * msg(s) - displays the string 's' on the status line X */ Xvoid Xmsg(s) Xchar *s; X{ X gotocmd(TRUE, 0); X outstr(s); X flushbuf(); X} X X/*VARARGS1*/ Xvoid Xsmsg(s, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16) Xchar *s; Xint a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16; X{ X char sbuf[80]; X X sprintf(sbuf, s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16); X msg(sbuf); X} X X/* X * emsg() - display an error message X * X * Rings the bell, if appropriate, and calls message() to do the real work X */ Xvoid Xemsg(s) Xchar *s; X{ X if (P(P_EB)) X beep(); X msg(s); X} X Xvoid Xwait_return() X{ X register char c; X X if (got_int) X outstr("Interrupt: "); X X outstr("Press RETURN to continue"); X do { X c = vgetc(); X } while (c != CR && c != NL && c != ' ' && c != ':'); X X if (c == ':') { X outchar(NL); X docmdln(getcmdln(c)); X } else X screenclear(); X X updatescreen(); X} !EOR! echo extracting - edit.c sed 's/^X//' > edit.c << '!EOR!' X/* $Header: /nw/tony/src/stevie/src/RCS/edit.c,v 1.11 89/08/02 19:57:12 tony Exp $ X * X * The main edit loop as well as some other simple cursor movement routines. X */ X X#include "stevie.h" X X/* X * This flag is used to make auto-indent work right on lines where only X * a <RETURN> or <ESC> is typed. It is set when an auto-indent is done, X * and reset when any other editting is done on the line. If an <ESC> X * or <RETURN> is received, and did_ai is TRUE, the line is truncated. X */ Xbool_t did_ai = FALSE; X Xvoid Xedit() X{ X extern bool_t need_redraw; X int c; X register char *p, *q; X X Prenum = 0; X X /* position the display and the cursor at the top of the file. */ X *Topchar = *Filemem; X *Curschar = *Filemem; X Cursrow = Curscol = 0; X X do_mlines(); /* check for mode lines before starting */ X X updatescreen(); X X for ( ;; ) { X X /* Figure out where the cursor is based on Curschar. */ X cursupdate(); X X if (need_redraw && !anyinput()) { X updatescreen(); X need_redraw = FALSE; X } X X if (!anyinput()) X windgoto(Cursrow,Curscol); X X X c = vgetc(); X X if (State == NORMAL) { X X /* We're in the normal (non-insert) mode. */ X X /* Pick up any leading digits and compute 'Prenum' */ X if ( (Prenum>0 && isdigit(c)) || (isdigit(c) && c!='0') ){ X Prenum = Prenum*10 + (c-'0'); X continue; X } X /* execute the command */ X normal(c); X Prenum = 0; X X } else { X X /* X * Insert or Replace mode. X */ X switch (c) { X X case ESC: /* an escape ends input mode */ X X /* X * If we just did an auto-indent, truncate the X * line, and put the cursor back. X */ X if (did_ai) { X Curschar->linep->s[0] = NUL; X Curschar->index = 0; X did_ai = FALSE; X } X X set_want_col = TRUE; X X /* Don't end up on a '\n' if you can help it. */ X if (gchar(Curschar) == NUL && Curschar->index != 0) X dec(Curschar); X X /* X * The cursor should end up on the last inserted X * character. This is an attempt to match the real X * 'vi', but it may not be quite right yet. X */ X if (Curschar->index != 0 && !endofline(Curschar)) X dec(Curschar); X X State = NORMAL; X msg(""); X X /* construct the Redo buffer */ X p=Redobuff; X q=Insbuff; X while ( q < Insptr ) X *p++ = *q++; X *p++ = ESC; X *p = NUL; X updatescreen(); X break; X X case CTRL('D'): X /* X * Control-D is treated as a backspace in insert X * mode to make auto-indent easier. This isn't X * completely compatible with vi, but it's a lot X * easier than doing it exactly right, and the X * difference isn't very noticeable. X */ X case BS: X /* can't backup past starting point */ X if (Curschar->linep == Insstart->linep && X Curschar->index <= Insstart->index) { X beep(); X break; X } X X /* can't backup to a previous line */ X if (Curschar->linep != Insstart->linep && X Curschar->index <= 0) { X beep(); X break; X } X X did_ai = FALSE; X dec(Curschar); X if (State == INSERT) X delchar(TRUE); X /* X * It's a little strange to put backspaces into X * the redo buffer, but it makes auto-indent a X * lot easier to deal with. X */ X *Insptr++ = BS; X Ninsert++; X cursupdate(); X updateline(); X break; X X case CR: X case NL: X if (State == REPLACE) /* DMT added, 12/89 */ X delchar(FALSE); X *Insptr++ = NL; X Ninsert++; X opencmd(FORWARD, TRUE); /* open a new line */ X break; X X default: X did_ai = FALSE; X insertchar(c); X break; X } X } X } X} X Xvoid Xinsertchar(c) Xint c; X{ X inschar(c); X *Insptr++ = c; X Ninsert++; X /* X * The following kludge avoids overflowing the statically X * allocated insert buffer. Just dump the user back into X * command mode, and print a message. X */ X if (Insptr+10 >= &Insbuff[1024]) { X stuffin(mkstr(ESC)); X emsg("No buffer space - returning to command mode"); X sleep(2); X } X updateline(); X} X Xvoid Xgetout() X{ X windgoto(Rows-1,0); X putchar('\r'); X putchar('\n'); X windexit(0); X} X Xvoid Xscrolldown(nlines) Xint nlines; X{ X register LPTR *p; X register int done = 0; /* total # of physical lines done */ X X /* Scroll up 'nlines' lines. */ X while (nlines--) { X if ((p = prevline(Topchar)) == NULL) X break; X done += plines(p); X *Topchar = *p; X /* X * If the cursor is on the bottom line, we need to X * make sure it gets moved up the appropriate number X * of lines so it stays on the screen. X */ X if (Curschar->linep == Botchar->linep->prev) { X int i = 0; X while (i < done) { X i += plines(Curschar); X *Curschar = *prevline(Curschar); X } X } X } X s_ins(0, done); X} X Xvoid Xscrollup(nlines) Xint nlines; X{ X register LPTR *p; X register int done = 0; /* total # of physical lines done */ X register int pl; /* # of plines for the current line */ X X /* Scroll down 'nlines' lines. */ X while (nlines--) { X pl = plines(Topchar); X if ((p = nextline(Topchar)) == NULL) X break; X done += pl; X if (Curschar->linep == Topchar->linep) X *Curschar = *p; X *Topchar = *p; X X } X s_del(0, done); X} X X/* X * oneright X * oneleft X * onedown X * oneup X * X * Move one char {right,left,down,up}. Return TRUE when X * sucessful, FALSE when we hit a boundary (of a line, or the file). X */ X Xbool_t Xoneright() X{ X set_want_col = TRUE; X X switch (inc(Curschar)) { X X case 0: X return TRUE; X X case 1: X dec(Curschar); /* crossed a line, so back up */ X /* fall through */ X case -1: X return FALSE; X } X /*NOTREACHED*/ X} X Xbool_t Xoneleft() X{ X set_want_col = TRUE; X X switch (dec(Curschar)) { X X case 0: X return TRUE; X X case 1: X inc(Curschar); /* crossed a line, so back up */ X /* fall through */ X case -1: X return FALSE; X } X /*NOTREACHED*/ X} X Xvoid Xbeginline(flag) Xbool_t flag; X{ X while ( oneleft() ) X ; X if (flag) { X while (isspace(gchar(Curschar)) && oneright()) X ; X } X set_want_col = TRUE; X} X Xbool_t Xoneup(n) Xint n; X{ X LPTR p, *np; X register int k; X X p = *Curschar; X for ( k=0; k<n; k++ ) { X /* Look for the previous line */ X if ( (np=prevline(&p)) == NULL ) { X /* If we've at least backed up a little .. */ X if ( k > 0 ) X break; /* to update the cursor, etc. */ X else X return FALSE; X } X p = *np; X } X *Curschar = p; X /* This makes sure Topchar gets updated so the complete line */ X /* is one the screen. */ X cursupdate(); X /* try to advance to the column we want to be at */ X *Curschar = *coladvance(&p, Curswant); X return TRUE; X} X Xbool_t Xonedown(n) Xint n; X{ X LPTR p, *np; X register int k; X X p = *Curschar; X for ( k=0; k<n; k++ ) { X /* Look for the next line */ X if ( (np=nextline(&p)) == NULL ) { X if ( k > 0 ) X break; X else X return FALSE; X } X p = *np; X } X /* try to advance to the column we want to be at */ X *Curschar = *coladvance(&p, Curswant); X return TRUE; X} !EOR! echo extracting - enveval.c sed 's/^X//' > enveval.c << '!EOR!' X/* X * Evaluate a string, expanding environment variables X * where encountered. X * We'll use the UNIX convention for representing environment X * variables: $xxx, where xxx is the shortest string that X * matches some environment variable. X */ X X#include <stdio.h> X#include <string.h> X Xchar *getenv(); X Xint XEnvEval (s, len) X char *s; X int len; X/*------------------------------------------------------------------ X * s= Pointer to buffer, currently containing string. It will be X * expanded in-place in the buffer. X * len=Maximum allowable length of the buffer. (In this version, we X * use a static buffer of 256 bytes internally.) X * X * RETURNS: X * 0 on success. X * -1 on failure. In this case, s may contain a partially X * converted string, but it won't contain a partial X * string. It will be the FULL string, with as X * many substitutions as we could find. X */ X X{ X#define LEN 256 X char buf [LEN]; X char *s1, *s2; X char *b1; X int done=0; X X if (len > LEN) X return (-1); X X s1 = s; X X /* Check for '$', and expand when we find one. */ X while (!done) { X if ((s1 = strchr (s1, '$')) == NULL) X done = 1; X else { X /* X * Here's where the real work gets done. X * We'll find the env.var., and convert X * it into buf, then copy back into s X * and continue. X */ X char c; X int need, got; X X /* Test successively longer strings, to see X * if they're env.vars. X */ X for (s2=++s1+1; ; s2++) { X c = *s2; /* save it */ X *s2 = '\0'; X b1 = getenv (s1); X *s2 = c; /* restore it */ X if (b1) /* found it */ X break; X if (!*s2) /* nothing to try */ X goto Failed; X } X --s1; /* Back to the '$' */ X X /* OK, we've found one (between s1 & s2, X * non-inclusive). Its value is in b1. X * Do the substitution into bufp, X * and copy back into s. X */ X need = strlen(b1) + strlen(s2) + 1; X got = len - (s1-s); X if (need > got) X goto Failed; X strcpy (buf, b1); X strcat (buf, s2); X strcpy (s1, buf); X } X } X X /* If we get here, the converted value is in s */ X return (0); X X Failed: X return (-1); X} X X X/* #define SAMPLE */ X#ifdef SAMPLE /***************************************************/ X Xmain (int argc, char **argv) X{ X int i, ret; X X for (i=1; i<argc; i++) { X printf ("Convert %s to", argv [i]); X ret = EnvEval (argv [i], 80); X printf (" %s", argv [i]); X if (ret) X printf (" - Failed"); X putchar ('\n'); X } X} X X#endif !EOR! echo extracting - fileio.c sed 's/^X//' > fileio.c << '!EOR!' X/* $Header: /nw/tony/src/stevie/src/RCS/fileio.c,v 1.12 89/08/06 09:50:01 tony Exp $ X * X * Basic file I/O routines. X */ X X#include <sys/types.h> /* For stat() and chmod() */ X#include <sys/stat.h> /* Ditto */ X#include "stevie.h" X Xvoid Xfilemess(s) Xchar *s; X{ X smsg("\"%s\" %s", (Filename == NULL) ? "" : Filename, s); X flushbuf(); X} X Xvoid Xrenum() X{ X LPTR *p; X unsigned long l = 0; X X for (p = Filemem; p != NULL ;p = nextline(p), l += LINEINC) X p->linep->num = l; X X Fileend->linep->num = 0xffffffff; X} X X#define MAXLINE 256 /* maximum size of a line */ X Xbool_t Xreadfile(fname,fromp,nochangename) X/*------------------------------------------------- X * Note that this will try to expand the file name using environment X * variables. For this reason, we copy it into an 80-byte buffer, X * so that there's room to expand it. X * X * It uses the environment-variable convention of UNIX, even X * under systems with other conventions. That is, your home directory X * would be called $HOME (even in DOS, where you might want to say %HOME%) X *-----------------------------------------------------*/ Xchar *fname; XLPTR *fromp; Xbool_t nochangename; /* if TRUE, don't change the Filename */ X{ X FILE *f, *fopen(); X register LINE *curr; X char buff[MAXLINE], buf2[80]; X char namebuf[80]; X register int i, c; X register long nchars = 0; X int linecnt = 0; X bool_t wasempty = bufempty(); X int nonascii = 0; /* count garbage characters */ X int nulls = 0; /* count nulls */ X bool_t incomplete = FALSE; /* was the last line incomplete? */ X bool_t toolong = FALSE; /* a line was too long */ X X curr = fromp->linep; X X strncpy (namebuf, fname, 80); X EnvEval (namebuf, 80); X X if ( ! nochangename ) X Filename = strsave(namebuf); X X if ( (f=fopen(fixname(namebuf),"r")) == NULL ) X return TRUE; X X filemess(""); X X i = 0; X do { X c = getc(f); X X if (c == EOF) { X if (i == 0) /* normal loop termination */ X break; X X /* X * If we get EOF in the middle of a line, note the X * fact and complete the line ourselves. X */ X incomplete = TRUE; X c = NL; X } X X /* X * Abort if we get an interrupt, but finished reading the X * current line first. X */ X if (got_int && i == 0) X break; X X if (c >= 0x80) { X c -= 0x80; X nonascii++; X } X X /* X * If we reached the end of the line, OR we ran out of X * space for it, then process the complete line. X */ X if (c == NL || i == (MAXLINE-1)) { X LINE *lp; X X if (c != NL) X toolong = TRUE; X X buff[i] = '\0'; X if ((lp = newline(strlen(buff))) == NULL) X exit(1); X X strcpy(lp->s, buff); X X curr->next->prev = lp; /* new line to next one */ X lp->next = curr->next; X X curr->next = lp; /* new line to prior one */ X lp->prev = curr; X X curr = lp; /* new line becomes current */ X i = 0; X linecnt++; X X } else if (c == NUL) X nulls++; /* count and ignore nulls */ X else { X buff[i++] = c; /* normal character */ X } X X nchars++; X X } while (!incomplete && !toolong); X X fclose(f); X X /* X * If the buffer was empty when we started, we have to go back X * and remove the "dummy" line at Filemem and patch up the ptrs. X */ X if (wasempty && nchars != 0) { X LINE *dummy = Filemem->linep; /* dummy line ptr */ X X free(dummy->s); /* free string space */ X Filemem->linep = Filemem->linep->next; X free((char *)dummy); /* free LINE struct */ X Filemem->linep->prev = Filetop->linep; X Filetop->linep->next = Filemem->linep; X X Curschar->linep = Filemem->linep; X Topchar->linep = Filemem->linep; X } X X renum(); X X if (got_int) { X smsg("\"%s\" Interrupt", namebuf); X got_int = FALSE; X return FALSE; /* an interrupt isn't really an error */ X } X X if (toolong) { X smsg("\"%s\" Line too long", namebuf); X return FALSE; X } X X sprintf(buff, "\"%s\" %s%d line%s, %ld character%s", X namebuf, X incomplete ? "[Incomplete last line] " : "", X linecnt, (linecnt != 1) ? "s" : "", X nchars, (nchars != 1) ? "s" : ""); X X buf2[0] = NUL; X X if (nonascii || nulls) { X if (nonascii) { X if (nulls) X sprintf(buf2, " (%d null, %d non-ASCII)", X nulls, nonascii); X else X sprintf(buf2, " (%d non-ASCII)", nonascii); X } else X sprintf(buf2, " (%d null)", nulls); X } X strcat(buff, buf2); X msg(buff); X X return FALSE; X} X X X/* X * writeit - write to file 'fname' lines 'start' through 'end' X * X * If either 'start' or 'end' contain null line pointers, the default X * is to use the start or end of the file respectively. X */ Xbool_t Xwriteit(fname, start, end) Xchar *fname; XLPTR *start, *end; X{ X FILE *f, *fopen(); X FILE *fopenb(); /* open in binary mode, where needed */ X char *backup; X register char *s; X register long nchars; X register int lines; X register LPTR *p; X struct stat statbuf; X int statres; X X smsg("\"%s\"", fname); X X /* Expand any environment variables left in the name. X * fname better be in a variable big enough to handle the X * expansion (80 bytes). X */ X EnvEval (fname, 80); X X /* If the file already exists, get what we need to know X * (like current mode). X */ X statres = stat (fname, &statbuf); X X /* X * Form the backup file name - change foo.* to foo.bak X */ X backup = alloc((unsigned) (strlen(fname) + 5)); X if (backup == NULL) { X emsg("Can't open file for writing!"); X return FALSE; X } X X strcpy(backup, fname); X for (s = backup; *s && *s != '.' ;s++) X ; X *s = NUL; X strcat(backup, ".bak"); X X /* X * Delete any existing backup and move the current version X * to the backup. For safety, we don't remove the backup X * until the write has finished successfully. And if the X * 'backup' option is set, leave it around. X */ X rename(fname, backup); X X X f = P(P_CR) ? fopen(fixname(fname), "w") : fopenb(fixname(fname), "w"); X X if (f == NULL) { X emsg("Can't open file for writing!"); X free(backup); X return FALSE; X } X X /* X * If we were given a bound, start there. Otherwise just X * start at the beginning of the file. X */ X if (start == NULL || start->linep == NULL) X p = Filemem; X else X p = start; X X lines = nchars = 0; X do { X fprintf(f, "%s\n", p->linep->s); X nchars += strlen(p->linep->s) + 1; X lines++; X X /* X * If we were given an upper bound, and we just did that X * line, then bag it now. X */ X if (end != NULL && end->linep != NULL) { X if (end->linep == p->linep) X break; X } X X } while ((p = nextline(p)) != NULL); X X fclose(f); X smsg("\"%s\" %d line%s, %ld character%s", fname, X lines, (lines > 1) ? "s" : "", X nchars, (nchars > 1) ? "s" : ""); X X UNCHANGED; X X /* X * Remove the backup unless they want it left around X */ X if (!P(P_BK)) X remove(backup); X X free(backup); X X /* X * Set the mode of the new file to agree with the old. X */ X if (statres==0) X chmod (fname, statbuf.st_mode); X X return TRUE; X} !EOR! echo extracting - help.c sed 's/^X//' > help.c << '!EOR!' X/* $Header: /nw/tony/src/stevie/src/RCS/help.c,v 1.9 89/08/06 09:50:09 tony Exp $ X * X * Routine to display a command summary. X * (Dave Tutelman note: X * I added the ability to page backwards and forwards through help. X * In order to minimize the abuse to the existing code, I used X * "goto"s and labeled each screen. It's not the way I would have X * done help from scratch, but it's not TOO ugly. X * ) X */ X X#include <ctype.h> X#include "stevie.h" X#include "ascii.h" X#include "keymap.h" X X/* Macro to show help screen 'n'. X * If C supported label types, it'd be cleaner to do it that way. */ X#define SHOWHELP( n ) switch(n) { \ X case 0: goto Screen0; \ X case 1: goto Screen1; \ X case 2: goto Screen2; \ X case 3: goto Screen3; \ X case 4: goto Screen4; \ X case 5: goto Screen5; \ X case 6: goto Screen6; \ X case 7: goto Screen7; \ X case 8: goto Screen8; \ X default: return (TRUE); } X Xextern char *Version; X Xstatic int helprow; Xstatic int lastscreen = 0; /* return to help in previous screen */ X X#ifdef HELP X Xstatic void longline(); X Xbool_t Xhelp() X{ X int k; X X SHOWHELP( lastscreen ); /* where did we quit help last ? */ X X/*********************************************************************** X * Zeroth Screen : Index to the help screens. X ***********************************************************************/ X XScreen0: X CLS; X windgoto(helprow = 0, 0); X Xlongline("\ X Index to HELP Screens\n\ X =====================\n\n"); Xlongline("\ X 0 Help index (this screen)\n\ X 1 Positioning within file, adjusting the screen\n\ X 2 Character positioning\n\ X 3 Line positioning, marking & returning, undo & redo\n"); Xlongline("\ X 4 Insert & replace, words, sentences, paragraphs\n\ X 5 Operators, miscellaneous operations, yank & put\n\ X 6 \"Ex\" command line operations\n\ X 7 Set parameters\n\ X 8 System-specific features\n"); X X windgoto(0, 52); X longline(Version); X X SHOWHELP( helpkey (0) ); X X X/*********************************************************************** X * First Screen: Positioning within file, Adjusting the Screen X ***********************************************************************/ X XScreen1: X CLS; X windgoto(helprow = 0, 0); X Xlongline("\ X Positioning within file\n\ X =======================\n\ X ^F Forward screenfull\n\ X ^B Backward screenfull\n"); Xlongline("\ X ^D scroll down half screen\n\ X ^U scroll up half screen\n"); Xlongline("\ X G Goto line (end default)\n\ X ]] next function\n\ X [[ previous function\n\ X /re next occurence of regular expression 're'\n"); Xlongline("\ X ?re prior occurence of regular expression 're'\n\ X n repeat last / or ?\n\ X N reverse last / or ?\n\ X % find matching (, ), {, }, [, or ]\n"); Xlongline("\ X\n\ X Adjusting the screen\n\ X ====================\n\ X ^L Redraw the screen\n\ X ^E scroll window down 1 line\n\ X ^Y scroll window up 1 line\n"); Xlongline("\ X z<RETURN> redraw, current line at top\n\ X z- ... at bottom\n\ X z. ... at center\n"); X X SHOWHELP( helpkey (1) ); X X X/*********************************************************************** X * Second Screen: Character positioning X ***********************************************************************/ X XScreen2: X CLS; X windgoto(helprow = 0, 0); X Xlongline("\ X Character Positioning\n\ X =====================\n\ X ^ first non-white\n\ X 0 beginning of line\n\ X $ end of line\n\ X h backward\n"); Xlongline("\ X l forward\n\ X ^H same as h\n\ X space same as l\n\ X fx find 'x' forward\n"); Xlongline("\ X Fx find 'x' backward\n\ X tx upto 'x' forward\n\ X Tx upto 'x' backward\n\ X ; Repeat last f, F, t, or T\n"); Xlongline("\ X , inverse of ;\n\ X | to specified column\n\ X % find matching (, ), {, }, [, or ]\n"); X X SHOWHELP( helpkey (2) ); X X X/*********************************************************************** X * Third Screen: Line Positioning, Marking and Returning X ***********************************************************************/ X XScreen3: X CLS; X windgoto(helprow = 0, 0); X Xlongline("\ X Line Positioning\n\ X ================\n\ X H home window line\n\ X L last window line\n\ X M middle window line\n"); Xlongline("\ X + next line, at first non-white\n\ X - previous line, at first non-white\n\ X CR return, same as +\n\ X j next line, same column\n\ X k previous line, same column\n"); X Xlongline("\ X\n\ X Marking and Returning\n\ X =====================\n\ X `` previous context\n\ X '' ... at first non-white in line\n"); Xlongline("\ X mx mark position with letter 'x'\n\ X `x to mark 'x'\n\ X 'x ... at first non-white in line\n"); X Xlongline("\n\ X Undo & Redo\n\ X =============\n\ X u undo last change\n\ X U restore current line\n\ X . repeat last change\n"); X X SHOWHELP( helpkey (3) ); X X X/*********************************************************************** X * Fourth Screen: Insert & Replace, X ***********************************************************************/ X XScreen4: X CLS; X windgoto(helprow = 0, 0); X Xlongline("\ X Insert and Replace\n\ X ==================\n\ X a append after cursor\n\ X i insert before cursor\n\ X A append at end of line\n\ X I insert before first non-blank\n"); Xlongline("\ X o open line below\n\ X O open line above\n\ X rx replace single char with 'x'\n\ X R replace characters\n"); Xif (! P(P_TO)) Xlongline("\ X ~ change case (upper/lower) of single char\n"); X Xlongline("\ X\n\ X Words, sentences, paragraphs\n\ X ============================\n\ X w word forward\n\ X b back word\n\ X e end of word\n\ X ) to next sentence\n\ X } to next paragraph\n"); Xlongline("\ X ( back sentence\n\ X { back paragraph\n\ X W blank delimited word\n\ X B back W\n\ X E to end of W\n"); X X SHOWHELP( helpkey (4) ); X X X/*********************************************************************** X * Fifth Screen: Operators, Misc. operations, Yank & Put X ***********************************************************************/ X XScreen5: X CLS; X windgoto(helprow = 0, 0); X Xlongline("\ X Operators (double to affect lines)\n\ X ==================================\n\ X d delete\n\ X c change\n"); Xlongline("\ X < left shift\n\ X > right shift\n\ X y yank to buffer\n\ X ! filter lines (command name follows)\n"); Xif (P(P_TO)) Xlongline("\ X ~ reverse case (upper/lower)\n"); X Xlongline("\n\ X Miscellaneous operations\n\ X ========================\n\ X C change rest of line\n\ X D delete rest of line\n\ X s substitute chars\n"); Xlongline("\ X S substitute lines (not yet)\n\ X J join lines\n\ X x delete characters\n\ X X ... before cursor\n"); X Xlongline("\n\ X Yank and Put\n\ X ============\n\ X p put back text\n\ X P put before\n\ X Y yank lines"); X X SHOWHELP( helpkey (5) ); X X X/*********************************************************************** X * Sixth Screen: Command-line operations X ***********************************************************************/ X XScreen