/* * STEVIE - Simply Try this Editor for VI Enthusiasts * * Code Contributions By : Tim Thompson twitch!tjt * Tony Andrews onecom!wldrdg!tony * G. R. (Fred) Walter watmath!grwalter */ #ifdef __ORCAC__ segment "seg2"; #include #endif #include "stevie.h" static char *altfile = NULL; /* alternate file */ static int altline; /* line # in alternate file */ static char *nowrtmsg = "No write since last change (use ! to override)"; extern char **files; /* used for "n" and "rew" */ extern int curfile; extern int numfiles; #ifdef WILD_CARDS char **cmd_files = NULL; /* list of input files */ int cmd_numfiles = 0; /* number of input files */ #endif /* * The next two variables contain the bounds of any range given in a command. * If no range was given, both contain null line pointers. If only a single * line was given, u_pos will contain a null line pointer. */ static LPtr l_pos, u_pos; static bool_t interactive; /* TRUE if we're reading a real command line */ static bool_t doecmd(char *); static void badcmd(void); static void doshell(void); static void get_range(char **); static LPtr *get_line(char **); #ifdef MEGAMAX overlay "cmdline" #endif /* * readcmdline() - accept a command line starting with ':', '/', or '?' * * readcmdline() accepts and processes colon commands and searches. If * 'cmdline' is null, the command line is read here. Otherwise, cmdline * points to a complete command line that should be used. This is used in * main() to handle initialization commands in the environment variable * "EXINIT". */ void readcmdline(char firstc, char *cmdline) /*char firstc; /* either ':', '/', or '?' */ /*char *cmdline; /* optional command string */ { char c; char buff[CMDBUFFSIZE]; char cmdbuf[CMDBUFFSIZE]; char argbuf[CMDBUFFSIZE]; char *p, *q, *cmd, *arg; bool_t literal_next_flag = FALSE; /* * Clear the range variables. */ l_pos.linep = (LINE *) NULL; u_pos.linep = (LINE *) NULL; interactive = (cmdline == NULL); if (interactive) gotocmdline(YES, firstc); p = buff; if (firstc != ':') *p++ = firstc; if (interactive) { /* collect the command string, handling '\b' and @ */ for (;;) { c = vgetc(); if (c == CTRL('V') && !literal_next_flag) { literal_next_flag = TRUE; outchar('^'); continue; } if (c == '\n' || ((c == '\r' || c == ESC) && (!literal_next_flag))) break; if ((c == '\b') && (!literal_next_flag)) { if (p > buff + (firstc != ':')) { p--; /* * this is gross, but it relies only on 'gotocmdline' */ gotocmdline(YES, firstc == ':' ? ':' : NUL); for (q = buff; q < p; q++) outstr(chars[*q].ch_str); } else { msg(""); return; /* back to cmd mode */ } continue; } if ((c == '@') && (!literal_next_flag)) { p = buff; if (firstc != ':') *p++ = firstc; gotocmdline(YES, firstc); continue; } if (literal_next_flag) { literal_next_flag = FALSE; outchar('\b'); } outstr(chars[c].ch_str); *p++ = c; } *p = '\0'; } else { if (strlen(cmdline) > CMDBUFFSIZE - 2) /* should really do something * better here... */ return; strcpy(p, cmdline); } /* skip any initial white space */ for (cmd = buff; *cmd != NUL && isspace(*cmd); cmd++); /* search commands */ c = *cmd; if (c == '/' || c == '?') { cmd++; /* was the command was '//' or '??' (I.E. repeat last search) */ if ((*cmd == c) || (*cmd == NUL)) { if (c == '/') searchagain(FORWARD); else searchagain(BACKWARD); return; } /* If there is a matching '/' or '?' at the end, toss it */ p = strchr(cmd, NUL); if (*(p - 1) == c && *(p - 2) != '\\') *(p - 1) = NUL; dosearch((c == '/') ? FORWARD : BACKWARD, cmd); return; } /* * Parse a range, if present (and update the cmd pointer). */ get_range(&cmd); if (l_pos.linep != NULL) { if (LINEOF(&l_pos) > LINEOF(&u_pos)) { emsg("Invalid range"); return; } } strcpy(cmdbuf, cmd); /* save the unmodified command */ /* isolate the command and find any argument */ for (p = cmd; *p != NUL && !isspace(*p); p++); if (*p == NUL) arg = NULL; else { *p = NUL; for (p++; *p != NUL && isspace(*p); p++); if (*p == NUL) { arg = NULL; } else { strcpy(argbuf, p); arg = argbuf; } } if (strcmp(cmd, "q!") == 0) { getout(0); } if (strcmp(cmd, "q") == 0) { if (Changed) { emsg(nowrtmsg); } else { getout(0); } return; } if (strcmp(cmd, "w") == 0) { if (arg == NULL) { if (Filename != NULL) { if (!writeit(Filename, &l_pos, &u_pos)) { emsg("Problems occured while writing output file"); } } else { emsg("No output file"); } } else { (void) writeit(arg, &l_pos, &u_pos); } return; } if (strcmp(cmd, "wq") == 0) { if (Filename != NULL) { if (writeit(Filename, (LPtr *) NULL, (LPtr *) NULL)) { getout(0); } } else { emsg("No output file"); } return; } if (strcmp(cmd, "x") == 0) { if (Changed) { if (Filename != NULL) { if (!writeit(Filename, (LPtr *) NULL, (LPtr *) NULL)) { emsg("Problems occured while writing output file"); return; } } else { emsg("No output file"); return; } } getout(0); } if (strcmp(cmd, "f") == 0 && arg == NULL) { fileinfo(); return; } if (*cmd == 'n') { if ((curfile + 1) < numfiles) { /* * stuff ":e[!] FILE\n" */ stuffReadbuff(":e"); if (cmd[1] == '!') stuffReadbuff("!"); stuffReadbuff(" "); stuffReadbuff(files[++curfile]); stuffReadbuff("\n"); } else emsg("No more files!"); return; } if (*cmd == 'p') { if (curfile > 0) { /* * stuff ":e[!] FILE\n" */ stuffReadbuff(":e"); if (cmd[1] == '!') stuffReadbuff("!"); stuffReadbuff(" "); stuffReadbuff(files[--curfile]); stuffReadbuff("\n"); } else emsg("No more files!"); return; } if (strncmp(cmd, "rew", 3) == 0) { if (numfiles <= 1) /* nothing to rewind */ return; curfile = 0; /* * stuff ":e[!] FILE\n" */ stuffReadbuff(":e"); if (cmd[3] == '!') stuffReadbuff("!"); stuffReadbuff(" "); stuffReadbuff(files[0]); stuffReadbuff("\n"); return; } if (strcmp(cmd, "e") == 0) { if (Changed) { emsg(nowrtmsg); } else { if (strcmp(arg, "%") == 0) { (void) doecmd(NULL); return; } #ifdef WILD_CARDS if (strcmp(arg, "#") != 0) { ExpandWildCards(1, &arg, &cmd_numfiles, &cmd_files); if (cmd_numfiles == 0) { emsg("Can't open file"); return; } else if (cmd_numfiles == 1) { arg = cmd_files[0]; } else { emsg("Too many file names"); } } #endif (void) doecmd(arg); } return; } if (strcmp(cmd, "e!") == 0) { if (strcmp(arg, "%") == 0) { if (!doecmd(NULL)) ResetBuffers(); return; } #ifdef WILD_CARDS if (strcmp(arg, "#") != 0) { ExpandWildCards(1, &arg, &cmd_numfiles, &cmd_files); if (cmd_numfiles == 0) { emsg("Can't open file"); return; } else if (cmd_numfiles == 1) { arg = cmd_files[0]; } else { emsg("Too many file names"); } } #endif if (!doecmd(arg)) ResetBuffers(); return; } if (strcmp(cmd, "f") == 0) { Filename = strsave(arg); filemess(""); return; } if (strcmp(cmd, "r") == 0 || strcmp(cmd, ".r") == 0) { if (arg == NULL) { badcmd(); return; } #ifdef WILD_CARDS if (strcmp(arg, "#") != 0) { ExpandWildCards(1, &arg, &cmd_numfiles, &cmd_files); if (cmd_numfiles == 0) { emsg("Can't open file"); return; } else if (cmd_numfiles == 1) { arg = cmd_files[0]; } else { emsg("Too many file names"); } } #endif if (readfile(arg, Curschar, 1)) { emsg("Can't open file"); return; } ResetBuffers(); CHANGED; return; } if (strcmp(cmd, ".=") == 0) { smsg("line %d", cntllines(Filemem, Curschar)); return; } if (strcmp(cmd, "$=") == 0) { smsg("%d", cntllines(Filemem, Fileend) - 1); return; } if (strncmp(cmd, "ta", 2) == 0) { dotag(arg, cmd[2] == '!'); return; } if (strcmp(cmd, "set") == 0) { doset(arg, interactive); return; } if (strcmp(cmd, "help") == 0) { if (help()) s_clear(); return; } if (strcmp(cmd, "version") == 0) { extern char *Version; msg(Version); return; } if (strcmp(cmd, "sh") == 0) { doshell(); return; } if (strncmp(cmd, "d", 1) == 0) { LINE *cp; int n; if (l_pos.linep == NULL) l_pos = *Curschar; if (u_pos.linep == NULL) u_pos = l_pos; ResetBuffers(); n = RowNumber(&l_pos); AppendPositionToUndoUndobuff(0, n); AppendPositionToUndobuff(0, n); if ((Filetop->linep->next == l_pos.linep) && (u_pos.linep->next == Fileend->linep)) AppendToUndobuff("a"); else if (u_pos.linep->next == Fileend->linep) AppendToUndobuff("o"); else AppendToUndobuff("O"); n = 0; cp = l_pos.linep; for (; cp != NULL && cp != Fileend->linep; cp = cp->next) { AppendToUndobuff(cp->s); n++; if (cp == u_pos.linep) break; AppendToUndobuff(NL_STR); } AppendToUndobuff(ESC_STR); if (n > 1) AppendNumberToUndoUndobuff(n); AppendToUndoUndobuff("dd"); *Curschar = l_pos; delline(n); S_NOT_VALID; return; } if (strncmp(cmd, "s/", 2) == 0) { dosub(&l_pos, &u_pos, cmdbuf + 1); return; } if (strncmp(cmd, "g/", 2) == 0) { doglob(&l_pos, &u_pos, cmdbuf + 1); return; } if (cmd[0] == '!') { if (cmd[1] == '\0') { emsg("Incomplete shell escape command"); return; } outstr("\n"); flushbuf(); #ifdef BSD set_ostate(); #endif #ifdef UNIX set_ostate(); #endif (void) system(&cmd[1]); #ifdef BSD set_nstate(); #endif #ifdef UNIX set_nstate(); #endif wait_return(); return; } /* * If we got a line, but no command, then go to the line. */ if (*cmd == NUL && l_pos.linep != NULL) { if (u_pos.linep != NULL) *Curschar = u_pos; else *Curschar = l_pos; S_CHECK_TOPCHAR_AND_BOTCHAR; return; } badcmd(); } /* * get_range - parse a range specifier * * Ranges are of the form: * * addr[,addr] * * where 'addr' is: * * % (entire file) * $ [+-NUM] * 'x [+-NUM] (where x denotes a currently defined mark) * . [+-NUM] * NUM * * The pointer *cp is updated to point to the first character following the * range spec. If an initial address is found, but no second, the upper bound * is equal to the lower. */ static void get_range(char **cp) { LPtr *l; char *p; if (**cp == '%') { l_pos.index = 0; l_pos.linep = Filetop->linep->next; u_pos.index = 0; u_pos.linep = Fileend->linep->prev; (*cp)++; return; } if ((l = get_line(cp)) == NULL) return; l_pos = *l; for (p = *cp; *p != NUL && isspace(*p); p++); *cp = p; if (*p != ',') { /* is there another line spec ? */ u_pos = l_pos; return; } *cp = ++p; if ((l = get_line(cp)) == NULL) { u_pos = l_pos; return; } u_pos = *l; } static LPtr * get_line(char **cp) { static LPtr pos; LPtr *lp; char *p, c; int lnum; pos.index = 0; /* shouldn't matter... check back later */ p = *cp; /* * Determine the basic form, if present. */ switch (c = *p++) { case '$': pos.linep = Fileend->linep->prev; break; case '.': pos.linep = Curschar->linep; break; case '\'': if ((lp = getmark(*p++)) == NULL) { emsg("Unknown mark"); return (LPtr *) NULL; } pos = *lp; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (lnum = c - '0'; isdigit(*p); p++) lnum = (lnum * 10) + (*p - '0'); if (lnum == 0) lnum = 1; pos = *gotoline(lnum); break; default: return (LPtr *) NULL; } while (*p != NUL && isspace(*p)) p++; if (*p == '-' || *p == '+') { bool_t neg = (*p++ == '-'); for (lnum = 0; isdigit(*p); p++) lnum = (lnum * 10) + (*p - '0'); if (neg) lnum = -lnum; pos = *gotoline(cntllines(Filemem, &pos) + lnum); } *cp = p; return &pos; } static void badcmd(void) { if (interactive) emsg("Unrecognized command"); } /* * dotag(tag, force) - goto tag */ void dotag(char *tag, bool_t force) /*char *tag; bool_t force;*/ { FILE *tp, *fopen(); char lbuf[LSIZE]; char *fname, *str; if ((tp = fopen("tags", "r")) == NULL) { emsg("Can't open tags file"); return; } while (fgets(lbuf, LSIZE, tp) != NULL) { if ((fname = strchr(lbuf, TAB)) == NULL) { emsg("Format error in tags file"); return; } *fname++ = '\0'; if ((str = strchr(fname, TAB)) == NULL) { emsg("Format error in tags file"); return; } *str++ = '\0'; if (strcmp(lbuf, tag) == 0) { if (!force && Changed) { emsg(nowrtmsg); return; } if (doecmd(fname)) { stuffReadbuff(str); /* str has \n at end */ stuffReadbuff("\007"); /* CTRL('G') */ fclose(tp); return; } } } emsg("tag not found"); fclose(tp); } static bool_t doecmd(char *arg) { int line = 1; /* line # to go to in new file */ if (arg != NULL) { /* * First detect a ":e" on the current file. This is mainly for ":ta" * commands where the destination is within the current file. */ if (Filename != NULL) { if (strcmp(arg, Filename) == 0) { if (!Changed) { altfile = Filename; altline = cntllines(Filemem, Curschar); return TRUE; } } } if (strcmp(arg, "#") == 0) { /* alternate */ char *s = Filename; if (altfile == NULL) { emsg("No alternate file"); return FALSE; } if (strcmp(altfile, Filename) == 0) { if (!Changed) { line = altline; altline = cntllines(Filemem, Curschar); goto DO_THE_STUFF_THING; } } Filename = altfile; altfile = s; line = altline; altline = cntllines(Filemem, Curschar); } else { altfile = Filename; altline = cntllines(Filemem, Curschar); Filename = strsave(arg); } } if (Filename == NULL) { emsg("No filename"); return FALSE; } /* clear mem and read file */ freeall(); filealloc(); UNCHANGED; if (readfile(Filename, Filemem, 0)) { emsg("Can't open file"); return FALSE; } *Topchar = *Curschar; if (line != 1) { DO_THE_STUFF_THING: stuffnumReadbuff(line); stuffReadbuff("G"); } setpcmark(); return TRUE; } static void doshell(void) { char *sh, *getenv(); sh = getenv("SHELL"); if (sh == NULL) { emsg("Shell variable not set"); return; } gotocmdline(YES, NUL); if (system(sh) < 0) { emsg("Exec failed"); return; } wait_return(); } void gotocmdline(bool_t clr, char firstc) { windgoto(Rows - 1, 0); if (clr) toutstr(T_EL); /* clear the bottom line */ if (firstc) outchar(firstc); } /* * msg(s) - displays the string 's' on the status line */ void msg(char *s) { gotocmdline(YES, NUL); outstr(s); #ifdef AMIGA flushbuf(); #endif #ifdef BSD flushbuf(); #endif } /* VARARGS */ #ifdef __ORCAC__ void smsg(char *s, ...) { static char sbuf[MAX_COLUMNS+1]; va_list ap; va_start(ap,s); vsprintf(sbuf,s,ap); msg(sbuf); va_end(ap); } #else void smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9) char *s; int a1, a2, a3, a4, a5, a6, a7, a8, a9; { char sbuf[MAX_COLUMNS + 1]; sprintf(sbuf, s, a1, a2, a3, a4, a5, a6, a7, a8, a9); msg(sbuf); } #endif /* * emsg() - display an error message * * Rings the bell, if appropriate, and calls message() to do the real work */ void emsg(char *s) { UndoInProgress = FALSE; RedrawingDisabled = FALSE; if (P(P_EB)) beep(); toutstr(T_TI); msg(s); toutstr(T_TP); #ifdef AMIGA flushbuf(); #endif #ifdef BSD flushbuf(); #endif } void wait_return(void) { char c; outstr("Press RETURN to continue"); do { c = vgetc(); } while (c != '\r' && c != '\n'); s_clear(); }