/*========================================================================*\
Copyright (c) 1997 Paul Vojta
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
PAUL VOJTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\*========================================================================*/
#include "vi.h"
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <io.h>
#include <errno.h>
#ifdef THOROUGHSHELL
extern int cdecl system3(const char *command, int fd1, int fd2,
Boolean thoroughshell);
#else
extern int cdecl system3(const char *command, int fd1, int fd2);
#endif
#define CPMEOF 26
static char *p;
static char *pmax; /* end + 1 of this command */
static int ex_argc;
static bigint ex_argv[2];
static Boolean bang;
static char *cmdname;
typedef char filename[81];
static filename edname[2]; /* current and alt file names */
static bigint ednumber[2]; /* line number memory for the above */
static int edindex = -1; /* index of current file name */
static Boolean morefiles = False; /* if "n more files ..." issued recently */
/* :set option variables (the local ones) */
static Boolean autowrite = False;
#ifdef BINARY
static Boolean binary = False;
static Boolean cpmeof = False;
#endif
static Boolean dosesc = False;
static Boolean ignorecase = False;
#ifdef THOROUGHSHELL
static Boolean thoroughshell = False;
#endif
#ifdef DEFAULTDIR
char defaultdir[] =DEFAULTDIR;
#else
char defaultdir[] ="";
#endif
/*
* ALLOCFILE - Allocate a buffer for the file. Replaces the system-
* provided one.
*/
/*int filebuffer[174/2+1];
char *allocfil(void) {
return (*((int *) filebuffer) == 0) ? (void *) filebuffer : Null;
}
*/
/*
* SKIPSPACES - Skip spaces and tabs.
*/
static void skipspaces(void) {
while (*p == ' ' || *p == '\t') ++p;
}
/*
* SETPMAX Find the extent of this ex command.
*/
static void setpmax(void) {
register int i;
register char *p2;
pmax = p + (i = strlen(p));
if ((p2 = memchr(p, '|', i)) != Null) pmax = p2;
}
/*
* BANGCHECK - Print message and quit unless overridden.
*/
static void bangcheck(char *msg) {
if (!bang) {
botattr = ATTRHIGH;
botco(' ');
botputs(msg);
botputs(" (:");
botputs(cmdname);
botputs("! overrides)");
botattr = ATTRNORM;
raise_err();
}
}
/*
* EXPECTEND - Issue diagnostic if we're not at the end of a command.
*/
static void expectend(void) {
if (p < pmax) msg_raise("Excess characters at end of command");
}
/*
* GETARG Determine argument.
*/
static char *getarg(char *fn, int len) {
if (len != 0) {
if (memchr(fn, ' ', len) != Null || memchr(fn, '\t', len) != Null)
msg_raise("Too many file names");
if (memchr(fn, '*', len) != Null || memchr(fn, '?', len) != Null)
msg_raise("Wild cards not allowed");
memmove(fn - 1, fn, len);
--fn;
fn[len] = '\0';
return fn;
}
else {
if (edindex < 0) msg_raise("No current filename");
return edname[edindex];
}
}
/*
* LINCHAR Print lines and characters message.
*/
static void linchar(bigint nlines, bigint nbytes) {
botputd(nlines);
botputs(" line");
if (nlines != 1) botco('s');
botputs(", ");
botputd(nbytes);
botputs(" character");
if (nbytes != 1) botco('s');
botputs(".\n");
}
/*
* FDREAD Read from a given file descriptor into a buffer. The argument
* "show" tells whether to put out the customary message on number
* of lines and characters.
*/
static void fdread(int fd, bufptr buffer, Boolean show) {
Boolean lcl_eolateof; /* if CR at end of file */
Boolean cr_at_end = False;
bigint llen;
int len;
slabseg ss = Null;
jmp_buf errbufsav;
if (fd != -1) {
errno = 0;
memcpy(errbufsav, errbuf, sizeof(jmp_buf));
if (!setjmp(errbuf)) for (;;) {
ss = newslab();
len = read(fd, SSPTR(ss), SLABLEN);
if (cr_at_end && (len == 0 || *SSPTR(ss) != '\n'))
addbytes(buffer, "\r", 1, 0);
if (len == 0 || errno != 0) break;
#ifdef BINARY
if (!binary) { /* end-of-line conversion */
ubyte far *p;
ubyte far *p1;
ubyte far *p2;
ubyte far *p_end;
p = p1 = p2 = SSPTR(ss);
p_end = p + len;
for (;;) {
p = memchr(p, '\r', p_end - p);
if (p == Null) p = p_end;
else if (p >= p_end - 1 || p[1] != '\n') {
++p;
continue;
}
len = p - p1;
if (p1 != p2) memmove(p2, p1, len);
p1 = ++p;
p2 += len;
if (p1 >= p_end) break;
}
cr_at_end = False;
len = p2 - SSPTR(ss);
if (p2[-1] == '\r') {
cr_at_end = True;
--len;
}
}
#endif /* BINARY */
catslab(buffer, ss, len);
ss = Null;
}
free_ss(ss);
llen = tell(fd);
close(fd);
memcpy(errbuf, errbufsav, sizeof(jmp_buf));
if (!heapfull && errno == 0) { /* trim Control-Z & \n */
#ifdef BINARY
register slabptr sl = (slabptr) (buffer->addr + buffer->len);
ubyte lastbyte = '\0';
int newlen;
if (binary) {
if (buffer->nbytes != 0) {
--sl;
loadslab(sl);
lastbyte = SSPTR(sl->slab)[sl->len - 1];
}
}
else {
while (--sl >= (slabptr) buffer->addr) {
loadslab(sl);
while (sl->len > 0 &&
(lastbyte = SSPTR(sl->slab)[sl->len - 1])
== CPMEOF) {
--buffer->nbytes;
--sl->len;
}
if (lastbyte != CPMEOF) break;
free_sl(sl);
}
if ((newlen = (ubyte *) (sl + 1) - buffer->addr) <
buffer->len)
bufrealloc(buffer, newlen);
}
#else /* !BINARY */
register slabptr sl = (slabptr) (buffer->addr + buffer->len);
ubyte lastbyte = '\0';
if (buffer->nbytes != 0) {
--sl;
loadslab(sl);
lastbyte = SSPTR(sl->slab)[sl->len - 1];
}
#endif /* !BINARY */
lcl_eolateof = True;
if (lastbyte != '\n') {
addbytes(buffer, "\n", 1, 1);
lcl_eolateof = False;
}
}
}
if (heapfull || errno != 0) {
freebuf(buffer);
MAINBUF->slabs = True;
lcl_eolateof = True;
if (errno == ENOENT) errno = 0;
if (errno != 0) botperror(Null);
}
else if (show) {
linchar(buffer->nlines, llen);
if (!lcl_eolateof) botputs("Incomplete last line.\n");
}
if (buffer == MAINBUF) eolateof = lcl_eolateof;
}
/*
* FDWRITE Create and write to a given file descriptor from MAINBUF.
* Returns the file descriptor in *fd_ret.
*/
static void fdwrite(posptr pos1, posptr pos2, char *arg, int *fd_ret) {
int fd;
slabptr sl, sl_end;
int skip;
int len;
#ifdef BINARY
#define BUFLEN 512
int len1;
ubyte buffer[BUFLEN + 1];
ubyte *q = buffer;
#endif /* BINARY */
*fd_ret = fd = open(arg,
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IWRITE);
if (fd == -1) return;
errno = 0;
sl = (slabptr) (MAINBUF->addr + pos1->slabno);
sl_end = (slabptr) (MAINBUF->addr + pos2->slabno);
skip = pos1->offset;
for (; sl <= sl_end; ++sl) {
len = (sl < sl_end ? sl->len : pos2->offset) - skip;
loadslab(sl);
#ifdef BINARY
if (binary) len1 = write(fd, SSPTR(sl->slab) + skip, len);
else {
ubyte far *p;
ubyte far *p1;
ubyte far *p2;
ubyte far *p_end;
p = p1 = SSPTR(sl->slab) + skip;
p_end = p + len;
for (;;) {
p2 = memchr(p1, '\n', p_end - p1);
if (p2 == Null) p1 = p_end;
else {
*p2 = '\r';
p1 = p2 + 1;
}
/* copy from p to p1 - 1 */
if (p1 - p > buffer + BUFLEN - q) {
if ((len = q - buffer) > 0 &&
(len1 = write(fd, buffer, len)) < len) break;
q = buffer;
}
if ((len1 = len = p1 - p) > BUFLEN) {
if ((len1 = write(fd, p, len)) < len) break;
}
else {
memmove(q, p, len);
q += len;
}
if (p2 == Null) break;
*p2 = '\n';
p = p2;
}
if (p2 != Null) *p2 = '\n';
}
if (errno != 0) return;
if (len1 < len) {
errno = -1;
return;
}
skip = 0;
#else /* !BINARY */
if (write(fd, SSPTR(sl->slab) + skip, len) < len || errno != 0) {
if (errno == 0) errno = -1;
return;
}
#endif /* !BINARY */
}
#ifdef BINARY
if (!binary) {
if (cpmeof) *q++ = CPMEOF;
if (write(fd, buffer, q - buffer) < q - buffer) {
if (errno == 0) errno = -1;
return;
}
}
#endif /* BINARY */
}
/*
* BANGFILTER - Filter MAINBUF[pos1..pos2] through the given command and
* produce an answer in ANSWBUF.
*/
void bangfilter(const char *cmd, posptr pos1, posptr pos2, bufptr answbuf,
Boolean warn, Boolean keep) {
static char oldbang[BANGLEN];
int fd1, fd2; /* file handles */
char n1[OPTLEN + 6]; /* input file name */
char n2[OPTLEN + 7]; /* output file name */
if (keep) {
if (*cmd != '!') strcpy(oldbang, cmd);
else if (*oldbang == '\0')
msg_raise("No previous command to substitute for !");
cmd = oldbang;
}
errno = 0;
fd1 = fd2 = -1;
if (pos1 != Null) {
fdwrite(pos1, pos2, formname(n1, "in"), &fd1);
if (errno < 0) msg_raise("Cannot write to pipe file.");
if (!errno) lseek(fd1, 0L, SEEK_SET);
}
if (!errno) {
if (answbuf != Null) fd2 = creat(formname(n2, "out"), S_IWRITE);
else {
endvid();
botlines = window1;
}
}
if (!errno) {
if (warn) botputs("[No write since last change]\n");
pre_shell();
#ifdef THOROUGHSHELL
(void) system3(cmd, fd1, fd2, keep & thoroughshell);
#else
(void) system3(cmd, fd1, fd2);
#endif
}
if (fd1 >= 0) close(fd1);
if (pos1 != Null) {
int errno_save = errno;
unlink(n1);
errno = errno_save;
}
if (!errno && answbuf != Null) {
lseek(fd2, 0L, SEEK_SET);
freebuf(answbuf);
answbuf->slabs = True;
fdread(fd2, answbuf, False);
}
if (fd2 >= 0) unlink(n2);
if (errno) {
botperror(Null);
raise_err();
}
}
/*
* RDFILE Read file into buffer. Returns empty buffer if file not found.
*/
static void rdfile(char *arg, bufptr buffer) {
freebuf(buffer);
buffer->slabs = True;
if (*arg == '\0') return;
botco('"'); botputs(arg); botputs("\": ");
fdread(open(arg, O_RDONLY | O_BINARY, 0), buffer, True);
}
/*
* WTFILE Write buffer to file.
*/
static void wtfile(char *fn, int fnlen, bigint first, bigint last) {
struct posrec pos1, pos2;
char *arg;
int fd;
bigint llen;
findline(first, &pos1);
findline(last, &pos2);
if (!eolateof && last >= MAINBUF->nlines) (void) posadjust(-1, &pos2);
if (fnlen > 0 && *fn == '!') {
bangfilter(fn + 1, &pos1, &pos2, Null, False, True);
p += strlen(p); /* that's it for this string of commands */
return;
}
arg = getarg(fn, fnlen);
botco('"'); botputs(arg); botputs("\": ");
fdwrite(&pos1, &pos2, arg, &fd);
if (errno == 0) llen = tell(fd);
if (fd != -1) close(fd);
if (errno != 0) {
int errnosav = errno;
unlink(arg);
errno = errnosav;
if (errno == -1) msg_raise("Cannot write file");
botperror(Null);
raise_err();
}
linchar(last - first, llen);
if (first <= 0 && last == MAINBUF->nlines) {
edited = True;
old_modif = True;
modif = False;
}
}
/*
* CMDEDIT Process :e command.
*/
static void cmdedit(char *arg, char *pplus) {
bigint lineno;
fixybuf();
rdfile(arg, MAINBUF);
edited = True;
modif = False;
if (MAINBUF->len == 0) { /* file not found or out of memory */
addbytes(MAINBUF, "\n", 1, 1);
if (errno == 0 && !heapfull && *arg != '\0') botputs("new file.\n");
errno = 0;
heapfull = False;
}
lineno = 0;
if (edindex >= 0) {
ednumber[edindex] = cp.line;
if (pplus == Null && strcmp(arg, edname[1 - edindex]) == 0) {
lineno = ednumber[1 - edindex];
if (lineno >= MAINBUF->nlines) lineno = MAINBUF->nlines - 1;
}
}
if (arg == edname[0]) edindex = 0;
else if (arg == edname[1]) edindex = 1;
else {
edindex = (edindex == 0);
strcpy(edname[edindex], arg);
}
cp = pos0; /* don't fool setcp */
screengood = False;
setcp(lineno);
dispset();
}
/*
* EX_EDIT Process :edit command.
*/
static void ex_edit(void) {
char *pplus = Null;
int pluslen;
if (*p == '+') {
pplus = p + 1;
p = strchr(p, ' ');
if (p == Null) p = pmax;
else setpmax();
pluslen = p - pplus;
if (pluslen == 0) {
pplus = "$";
pluslen = 1;
}
skipspaces();
}
if (*p == '#' && pmax == p + 1) {
if (edindex < 0 || edname[1 - edindex][0] == '\0')
msg_raise("No alternate file name");
cmdedit(edname[1 - edindex], pplus);
}
else cmdedit(getarg(p, pmax - p), pplus);
if (pplus != Null) {
p = pmax - pluslen;
memmove(p, pplus, pluslen);
pmax = --p;
}
}
/*
* EX_NEXT :next command.
*/
static void ex_next(void) {
++argindex;
if (argindex >= argc) msg_raise("No more files");
cmdedit(argv[argindex], Null);
}
/*
* EX_REW :rewind command.
*/
static void ex_rew(void) {
argindex = -1;
ex_next();
}
/*
* EX_WRITE - Process :write command.
*/
static void ex_write(void) {
if (ex_argc < 2) {
ex_argv[1] = MAINBUF->nlines;
if (ex_argc < 1) ex_argv[0] = 1;
}
wtfile(p, pmax - p, ex_argv[0] - (ex_argv[0] != 0), ex_argv[1]);
}
/*
* EX_READ - Process :read command (reads from another file).
*/
static void ex_read(void) {
if (*p == '!') {
bangfilter(p + 1, Null, Null, XBUF1, False, True);
p += strlen(p); /* that's it for this string of commands */
}
else {
rdfile(p, XBUF1);
if (XBUF1->len == 0) {
if (!heapfull && errno == 0) botputs("not found.\n");
errno = 0;
heapfull = False;
return;
}
}
if (ybuf == XBUF1) ybuf = Null;
cbyte1 = cp.byte;
if (ex_argc != 0) --ex_argc;
findline(ex_argv[ex_argc], &cp);
XBUF1->linebuf = True;
dochange(XBUF1, XBUF2, &cp, True, False);
rollfind();
}
/*
* STATUS Process :file and ^G commands (w/o arguments).
*/
void status(void) {
if (edindex >= 0) {
botco('"'); botputs(edname[edindex]); botco('"');
/* if (!writable) botputs(" [read only]"); */
if (!edited) botputs(" [not edited]");
if (modif) botputs(" [modified]");
botco(':');
}
else botputs("No file");
botputs(" line "); botputd(cp.line + 1);
botputs(" of "); botputd(MAINBUF->nlines);
botputs(" --");
botputd((long) (cp.line + 1) * 100 / MAINBUF->nlines);
botputs("%-- col ");
botputd((long) colof(&cp) + 1);
botputs("\n");
}
/*
* EX_FILE Process :file command.
*/
static void ex_file(void) {
if (p < pmax) { /* file argument */
if (edindex < 0) edindex = 0;
strcpy(edname[edindex], getarg(p, pmax - p));
edited = False;
}
status();
}
/*
* EX_QUIT Erase funny lines at end of screen and quit.
*/
static void ex_quit(void) {
int i;
i = argc - argindex - 1;
if (i > 0 && !morefiles) {
morefiles = True;
botattr = ATTRHIGH;
botco(' ');
botputd(i);
botputs(" more file");
if (i > 1) botco('s');
botputs(" to edit");
botattr = ATTRNORM;
raise_err();
}
i = linedata[ldisp].lrow;
if (i + botlines < window1) mvup(i, window1, window1 - i - botlines);
else if (vidcompat == 2) { /* do a normal scroll */
mvup(0, window1, 1);
--i;
}
putcur((i + botlines) * COLS);
rmfiles();
endvid();
exit(0);
}
/*
* EX_WQ Write and quit.
*/
static void ex_wq(void) {
ex_write();
ex_quit();
}
/*
* EX_X Process ending command.
*/
static void ex_x(void) {
if (modif) ex_write();
ex_quit();
}
/*
* EX_VERSION - Print copyright message at end of screen.
*/
void ex_version(void) {
botattr = ATTRBOLD;
botputs("Calvin version 2.4. (C) Copyright 1988-1997 Paul Vojta.");
botnewline();
botputs(" For copying conditions, please see the documentation file.");
botnewline();
botattr = ATTRNORM;
}
/*
* EX_MOVE Process motion command.
*/
static void ex_move(void) {
if (ex_argc != 0) --ex_argc;
setcp(ex_argv[ex_argc] - (ex_argv[ex_argc] != 0));
if (screengood) rollfind();
}
/*
* EX_EQUALS - Process "=" command.
*/
static void ex_equals(void) {
if (ex_argc != 0) --ex_argc;
botputd(ex_argv[ex_argc]);
botco(' ');
}
/*
* BANGIT Common routine for "!" and :shell commands.
*/
static void bangit(char *cmd, Boolean keep) {
Boolean warn = False;
if (modif)
if (autowrite) wtfile(p, 0, 0, MAINBUF->nlines);
else warn = True;
bangfilter(cmd, Null, Null, Null, warn, keep);
}
/*
* EX_BANG Process "!" command.
*/
static void ex_bang(void) {
bangit(p, True);
p += strlen(p); /* that's it for this string of commands */
}
/*
* EX_SHELL - Process :shell command.
*/
static void ex_shell(void) {
char cmd[81];
char *p;
strcpy(cmd, getenv(SHELLVAR));
p = strrchr(cmd, '.');
if (p != Null) *p = '\0';
bangit(cmd, False);
}
/*
* MATCHSTRING - Find best match from list.
*/
static char **matchstring(char **tbl, int tblwid, int tbllen) {
char *p0;
int len;
char **ansptr = Null;
int count = 0;
int n;
p0 = p;
while (*p >= 'a' && *p <= 'z') ++p;
len = p - p0;
if (len == 0) return Null;
while (--tbllen >= 0) {
n = strlen(*tbl);
if (n >= len && memcmp(p0, *tbl, len) == 0) {
if (n == len) return tbl; /* exact match */
ansptr = tbl;
++count;
}
*((char **) &tbl) += tblwid;
}
return count <= 1 ? ansptr : Null;
}
/*
* EX_SET Process :set command.
*/
Boolean setall = False; /* a real kludge */
struct setrec {
char *setvar;
enum {SET_bool, SET_int, SET_str} set_typ;
caddr_t dflt_val;
void *val;
enum {SETsyn, SETnop, SETall, SETsg, SETic, SETdir, SETesc,
SETvc}
set_action;
} settable[] = {
{"autowrite", SET_bool, False, &autowrite, SETnop},
#ifdef BINARY
{"binary", SET_bool, False, &binary, SETnop},
{"cpmeof", SET_bool, False, &cpmeof, SETnop},
#endif
{"directory", SET_str, defaultdir, directory, SETdir},
{"dosesc", SET_bool, False, &dosesc, SETesc},
{"eolateof", SET_bool, (caddr_t) True, &eolateof, SETnop},
{"ignorecase", SET_bool, False, &ignorecase, SETic},
{"marginbell", SET_int, 0, &marginbell, SETnop},
{"printable", SET_int, (caddr_t) '~', &printable, SETsg},
{"showmode", SET_bool, False, &showmode, SETnop},
{"tabstop", SET_int, (caddr_t) 8, &tabstop, SETsg},
#ifdef THOROUGHSHELL
{"thoroughshell", SET_bool, False, &thoroughshell, SETnop},
#endif
{"vidcompat", SET_int, 0, &vidcompat, SETvc},
{"all", SET_bool, False, &setall, SETall},
{"aw", SET_bool, False, &autowrite, SETsyn},
{"ic", SET_bool, False, &ignorecase, SETsyn},
{"mb", SET_int, 0, &marginbell, SETsyn},
};
static char *show_opt(struct setrec *setptr, char *p) {
char s[33];
if (setptr->set_typ == SET_bool && *((Boolean *) setptr->val) == False)
p = stpcpy(p, "no");
p = stpcpy(p, setptr->setvar);
if (setptr->set_typ != SET_bool) {
*p++ = '=';
p = stpcpy(p,
setptr->set_typ == SET_int
? ltoa(*((int *) setptr->val), s, 10)
: (char *) setptr->val);
}
return p;
}
static void ex_set(void) {
struct setrec *setptr;
Boolean boolval;
char s[81];
Boolean written = False;
if (*p == '\0' || *p == '|') /* show altered options */
for (setptr = settable; setptr->set_action != SETall; ++setptr) {
if (setptr->set_typ == SET_str ?
(strcmp((char *) setptr->val, (char *) setptr->dflt_val)
!= 0) :
(setptr->set_typ == SET_bool
? *((Boolean *) setptr->val)
: *((int *) setptr->val))
!= (int) setptr->dflt_val) {
if (written) botco(' ');
written = True;
(void) show_opt(setptr, s);
botputs(s);
}
}
else
for (; *p != '\0' && *p != '|'; skipspaces()) {
boolval = True;
if (p[0] == 'n' && p[1] == 'o') {
boolval = False;
p += 2;
}
setptr = (struct setrec *) matchstring((char **) settable,
sizeof(struct setrec), XtNumber(settable));
if (setptr == Null)
msg_raise(
"No such option--\"set all\" gives all option values");
if (setptr->set_action == SETsyn) { /* process synonyms */
void *oval = setptr->val;
do --setptr; while (setptr->val != oval);
}
skipspaces();
if (*p == '?') {
++p;
if (written) botco(' ');
written = True;
(void) show_opt(setptr, s);
botputs(s);
continue;
}
if (setptr->set_typ == SET_bool) {
if (*((Boolean *) setptr->val) == boolval) continue;
*((Boolean *) setptr->val) = boolval;
}
else {
if (*p == '=') {
++p;
skipspaces();
}
if (setptr->set_typ == SET_int) {
int intval;
intval = getnum(&p);
if (*((int *) setptr->val) == intval) continue;
*((int *) setptr->val) = intval;
}
else { /* process string */
char strval[OPTLEN];
char *q = strval;
while (p < pmax && *p != ' ' && *p != '\t') {
if (q >= strval + OPTLEN - 1)
msg_raise("String too long");
*q++ = *p++;
}
*q = '\0';
if (strcmp((char *) setptr->val, strval) == 0) continue;
if (setptr->set_action == SETdir) {
#ifdef DEFAULTDIR
if (!startup)
msg_raise("Too late to change directory.");
#else
if (swaphandle != -1)
msg_raise("Can't change directory twice.");
#endif
}
strcpy((char *) setptr->val, strval);
}
}
switch (setptr->set_action) {
case SETesc: setdosesc(dosesc); break;
case SETic:
mem_chr = memchr;
memr_chr = memrchr;
mem_cmp = memcmp;
if (ignorecase) {
mem_chr = memichr;
memr_chr = memrichr;
mem_cmp = memicmp;
}
break;
case SETdir:
#ifndef DEFAULTDIR
initswap();
#endif
break;
case SETvc:
if (visual) {
endvid();
invid(vidcompat);
}
/* control falls through */
case SETsg: screengood = False; break;
case SETall: {
struct setrec *sp = settable;
int rows = ((setptr - settable - 1) / 3 + 1);
if (written || (visual && botcurs > pos2480))
botnewline();
written = False;
for (; sp < settable + rows; ++sp) {
char *p;
p = show_opt(sp, s);
memset(p, ' ', s + 27 - p);
p = show_opt(sp + rows, s + 27);
if (sp + 2 * rows < setptr) {
memset(p, ' ', s + 54 - p);
(void) show_opt(sp + 2 * rows, s + 54);
}
botputs(s);
botnewline();
}
setall = False;
}
break;
case SETnop: ;
}
}
/* end if */
if (written) botend();
}
typedef void (*excommandproc)(void);
#define FLG_NOARG 1 /* no arguments allowed */
#define FLG_NEEDARG 2 /* argument required */
#define FLG_AW 4 /* autowrite */
#define FLG_ADDR 8 /* address allowed */
static struct cmdrec {
char *cmdnam;
excommandproc proc;
sbyte flags;
}
cmdtable[] = {
{"edit", ex_edit, FLG_AW},
{"file", ex_file, 0},
{"next", ex_next, FLG_NOARG | FLG_AW},
{"quit", ex_quit, FLG_NOARG | FLG_AW},
{"r", Null, 1},
{"read", ex_read, FLG_ADDR | FLG_NEEDARG},
{"rewind", ex_rew, FLG_NOARG | FLG_AW},
{"set", ex_set, 0},
{"shell", ex_shell, FLG_NOARG},
{"version", ex_version, FLG_NOARG},
{"wq", ex_wq, 0},
{"w", Null, 1},
{"write", ex_write, FLG_ADDR},
{"x", ex_x, 0},
},
cmdequals = {"=", ex_equals, FLG_ADDR | FLG_NOARG},
cmdbang = {"!", ex_bang, FLG_NEEDARG | FLG_ADDR},
cmdmove = {"", ex_move, FLG_ADDR | FLG_NOARG};
/*
* EXCOMMAND - Process "ex" command.
*/
void excommand(char *str) {
struct cmdrec *cmdptr;
bigint addr;
int i;
char c;
p = (char *) str;
skipspaces();
if (*p == ':') ++p;
for (;;) {
skipspaces();
if (*p == '\0') break;
ex_argc = 0;
ex_argv[0] = ex_argv[1] = cp.line + 1;
while (strchr("1234567890/?'.$+-", c = *p) != Null) {
addr = cp.line + 1;
switch (c) {
case '\'': {
struct posrec pos;
markptr mark = Null;
++p;
if (*p == c) mark = markpos;
else if ((c = *p) >= 'a' && c <= 'z')
mark = markpos + (c - ('a' - 1));
if (mark == Null || mark->byte < 0)
msg_raise("No such mark");
findbyte(mark->byte, &pos);
addr = pos.line + 1;
++p;
}
break;
case '/':
case '?': {
int len;
struct posrec pos;
Boolean wrap;
p = parsesearch(locpat, &len, p + 1, p + strlen(p),
c);
if (len != 0) loclen = len;
if (c == '/') {
findline(cp.line + 1, &pos);
pos.line += posadjust(-1, &pos);
locdir = 1;
}
else {
findline(cp.line, &pos);
locdir = -1;
}
dosearch(locdir, &pos, &wrap);
addr = pos.line + 1;
}
break;
case '$': addr = MAINBUF->nlines; /* ++p; break; */
case '.': ++p; break;
default: if (c >= '0' && c <= '9') addr = getnum(&p);
}
for (;;) {
c = *p;
if (c == '+') {
c = *++p;
if (c >= '0' && c <= '9') addr += getnum(&p);
else ++addr;
}
else if (c == '-') {
c = *++p;
if (c >= '0' && c <= '9') addr -= getnum(&p);
else --addr;
}
else break;
}
if (addr < 0)
msg_raise("Negative address--first buffer line is 1");
if (addr > MAINBUF->nlines)
msg_raise("Not that many lines in buffer");
if (ex_argc < 2) ++ex_argc;
else ex_argv[0] = ex_argv[1];
ex_argv[ex_argc - 1] = addr;
skipspaces();
if (*p != ',') break;
++p;
}
if (ex_argc == 2 && ex_argv[0] > ex_argv[1])
msg_raise("First address exceeds second");
setpmax();
cmdptr = Null;
bang = False;
if (*p < 'a' || *p > 'z') {
if (*p == '=') {
++p;
cmdptr = &cmdequals;
}
else if (*p == '!') {
++p;
cmdptr = &cmdbang;
}
else if (p >= pmax) cmdptr = &cmdmove;
}
else {
cmdptr = (struct cmdrec *) matchstring((char **) cmdtable,
sizeof(struct cmdrec), XtNumber(cmdtable));
if (*p == '!') {
++p;
bang = True;
}
}
if (cmdptr == Null) msg_raise("Illegal command");
if (cmdptr->proc == Null) cmdptr += cmdptr->flags; /* synonyms */
cmdname = cmdptr->cmdnam;
skipspaces(); /* get argument */
i = cmdptr->flags;
if (i & FLG_NOARG) expectend();
if ((i & FLG_NEEDARG) && p >= pmax) msg_raise("Argument missing");
if (ex_argc != 0 && !(i & FLG_ADDR))
msg_raise("No address allowed on this command");
if ((i & FLG_AW) && modif && !bang)
if (autowrite) wtfile(p, 0, 0, MAINBUF->nlines);
else bangcheck("No write since last change");
(*(cmdptr->proc))(); /* this does the command */
if (*pmax == '\0') break;
p = pmax + 1;
}
morefiles = False;
}