home *** CD-ROM | disk | FTP | other *** search
- /* $Header: /nw2/tony/src/stevie/src/RCS/cmdline.c,v 1.22 89/08/31 10:00:23 tony Exp $
- *
- * Routines to parse and execute "command line" commands, such as searches
- * or colon commands.
- */
-
- #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)";
- static char *nooutfile = "No output file";
- static char *morefiles = "more files to edit";
-
- extern char **files; /* used for "n" and "rew" */
- extern int numfiles, curfile;
-
- #define CMDSZ 100 /* size of the command buffer */
-
- static bool_t doecmd();
- static void badcmd(), get_range();
- static LPTR *get_line();
-
- /*
- * getcmdln() - read a command line from the terminal
- *
- * Reads a command line started by typing '/', '?', '!', or ':'. Returns a
- * pointer to the string that was read. For searches, an optional trailing
- * '/' or '?' is removed.
- */
- char *
- getcmdln(firstc)
- char firstc;
- {
- static char buff[CMDSZ];
- register char *p = buff;
- register int c;
- register char *q;
-
- gotocmd(TRUE, firstc);
-
- /* collect the command string, handling '\b' and @ */
- do {
- switch (c = vgetc()) {
-
- default: /* a normal character */
- outchar(c);
- *p++ = c;
- break;
-
- case BS:
- if (p > buff) {
- /*
- * this is gross, but it relies
- * only on 'gotocmd'
- */
- p--;
- gotocmd(TRUE, firstc);
- for (q = buff; q < p ;q++)
- outchar(*q);
- } else {
- msg("");
- return NULL; /* back to cmd mode */
- }
- break;
-
- case '@': /* line kill */
- p = buff;
- gotocmd(TRUE, firstc);
- break;
-
- case NL: /* done reading the line */
- case CR:
- break;
- }
- } while (c != NL && c != CR);
-
- *p = '\0';
-
- if (firstc == '/' || firstc == '?') { /* did we do a search? */
- /*
- * Look for a terminating '/' or '?'. This will be the first
- * one that isn't quoted. Truncate the search string there.
- */
- for (p = buff; *p ;) {
- if (*p == firstc) { /* we're done */
- *p = '\0';
- break;
- } else if (*p == '\\') /* next char quoted */
- p += 2;
- else
- p++; /* normal char */
- }
- }
- if (buff[0] == '\0')
- return NULL;
-
- return buff;
- }
-
- /*
- * docmdln() - handle a colon command
- *
- * Handles a colon command received interactively by getcmdln() or from
- * the environment variable "EXINIT" (or eventually .virc).
- */
- void
- docmdln(cmdline)
- char *cmdline;
- {
- char buff[CMDSZ];
- char cmdbuf[CMDSZ];
- char argbuf[CMDSZ];
- char *cmd, *arg;
- register char *p;
- /*
- * 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.
- */
- LPTR l_pos, u_pos;
-
-
- /*
- * Clear the range variables.
- */
- l_pos.linep = (struct line *) NULL;
- u_pos.linep = (struct line *) NULL;
-
- if (cmdline == NULL)
- return;
-
- if (strlen(cmdline) > CMDSZ-2) {
- msg("Error: command line too long");
- return;
- }
- strcpy(buff, cmdline);
-
- /* skip any initial white space */
- for (cmd = buff; *cmd != NUL && isspace(*cmd) ;cmd++)
- ;
-
- if (*cmd == '%') { /* change '%' to "1,$" */
- strcpy(cmdbuf, "1,$"); /* kind of gross... */
- strcat(cmdbuf, cmd+1);
- strcpy(cmd, cmdbuf);
- }
-
- while ((p=strchr(cmd, '%')) != NULL && *(p-1) != '\\') {
- /* change '%' to Filename */
- if (Filename == NULL) {
- emsg("No filename");
- return;
- }
- *p= NUL;
- strcpy (cmdbuf, cmd);
- strcat (cmdbuf, Filename);
- strcat (cmdbuf, p+1);
- strcpy(cmd, cmdbuf);
- msg(cmd); /*repeat */
- }
-
- while ((p=strchr(cmd, '#')) != NULL && *(p-1) != '\\') {
- /* change '#' to Altname */
- if (altfile == NULL) {
- emsg("No alternate file");
- return;
- }
- *p= NUL;
- strcpy (cmdbuf, cmd);
- strcat (cmdbuf, altfile);
- strcat (cmdbuf, p+1);
- strcpy(cmd, cmdbuf);
- msg(cmd); /*repeat */
- }
-
- /*
- * Parse a range, if present (and update the cmd pointer).
- */
- get_range(&cmd, &l_pos, &u_pos);
-
- 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();
- if (strcmp(cmd,"q") == 0) {
- if (Changed)
- emsg(nowrtmsg);
- else {
- if ((curfile + 1) < numfiles)
- emsg(morefiles);
- else
- getout();
- }
- return;
- }
- if (strcmp(cmd,"w") == 0) {
- if (arg == NULL) {
- if (Filename != NULL) {
- writeit(Filename, &l_pos, &u_pos);
- } else
- emsg(nooutfile);
- }
- else
- writeit(arg, &l_pos, &u_pos);
- return;
- }
- if (strcmp(cmd,"wq") == 0) {
- if (Filename != NULL) {
- if (writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
- getout();
- } else
- emsg(nooutfile);
- return;
- }
- if (strcmp(cmd, "x") == 0) {
- doxit();
- return;
- }
-
- if (strcmp(cmd,"f") == 0 && arg == NULL) {
- fileinfo();
- return;
- }
- if (*cmd == 'n') {
- if ((curfile + 1) < numfiles) {
- /*
- * stuff ":e[!] FILE\n"
- */
- stuffin(":e");
- if (cmd[1] == '!')
- stuffin("!");
- stuffin(" ");
- stuffin(files[++curfile]);
- stuffin("\n");
- } else
- emsg("No more files!");
- return;
- }
- if (*cmd == 'N') {
- if (curfile > 0) {
- /*
- * stuff ":e[!] FILE\n"
- */
- stuffin(":e");
- if (cmd[1] == '!')
- stuffin("!");
- stuffin(" ");
- stuffin(files[--curfile]);
- stuffin("\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"
- */
- stuffin(":e");
- if (cmd[3] == '!')
- stuffin("!");
- stuffin(" ");
- stuffin(files[0]);
- stuffin("\n");
- return;
- }
- if (strcmp(cmd,"e") == 0 || strcmp(cmd,"e!") == 0) {
- (void) doecmd(arg, cmd[1] == '!');
- return;
- }
- /*
- * The command ":e#" gets expanded to something like ":efile", so
- * detect that case here.
- */
- if (*cmd == 'e' && arg == NULL) {
- if (cmd[1] == '!')
- (void) doecmd(&cmd[2], TRUE);
- else
- (void) doecmd(&cmd[1], FALSE);
- return;
- }
- if (strcmp(cmd,"f") == 0) {
- Filename = strsave(arg);
- filemess("");
- return;
- }
- if (strcmp(cmd,"r") == 0) {
- if (arg == NULL) {
- badcmd();
- return;
- }
- if (readfile(arg, Curschar, 1)) {
- emsg("Can't open file");
- return;
- }
- updatescreen();
- CHANGED;
- return;
- }
- if (strcmp(cmd,"=") == 0) {
- smsg("%d", cntllines(Filemem, &l_pos));
- return;
- }
- if (strncmp(cmd,"ta", 2) == 0) {
- dotag(arg, cmd[2] == '!');
- return;
- }
- if (strncmp(cmd,"set", 2) == 0) {
- doset(arg);
- return;
- }
- if (strcmp(cmd,"help") == 0) {
- if (help()) {
- screenclear();
- updatescreen();
- }
- return;
- }
- if (strncmp(cmd, "ve", 2) == 0) {
- extern char *Version;
-
- msg(Version);
- return;
- }
- if (strcmp(cmd, "sh") == 0) {
- doshell(NULL);
- return;
- }
- if (*cmd == '!') {
- doshell(cmdbuf+1);
- 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 we got a line, but no command, then go to the line.
- */
- if (*cmd == NUL && l_pos.linep != NULL) {
- *Curschar = l_pos;
- return;
- }
-
- badcmd();
- }
-
-
- doxit()
- {
- if (Changed) {
- if (Filename != NULL) {
- if (!writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
- return;
- } else {
- emsg(nooutfile);
- return;
- }
- }
- if ((curfile + 1) < numfiles)
- emsg(morefiles);
- else
- getout();
- }
-
- /*
- * get_range - parse a range specifier
- *
- * Ranges are of the form:
- *
- * addr[,addr]
- *
- * where 'addr' is:
- *
- * $ [+- 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(cp, lower, upper)
- register char **cp;
- LPTR *lower, *upper;
- {
- register LPTR *l;
- register char *p;
-
- if ((l = get_line(cp)) == NULL)
- return;
-
- *lower = *l;
-
- for (p = *cp; *p != NUL && isspace(*p) ;p++)
- ;
-
- *cp = p;
-
- if (*p != ',') { /* is there another line spec ? */
- *upper = *lower;
- return;
- }
-
- *cp = ++p;
-
- if ((l = get_line(cp)) == NULL) {
- *upper = *lower;
- return;
- }
-
- *upper = *l;
- }
-
- static LPTR *
- get_line(cp)
- char **cp;
- {
- static LPTR pos;
- LPTR *lp;
- register char *p, c;
- register 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');
-
- 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()
- {
- emsg("Unrecognized command");
- }
-
- #define LSIZE 256 /* max. size of a line in the tags file */
-
- /*
- * dotag(tag, force) - goto tag
- */
- void
- dotag(tag, force)
- char *tag;
- bool_t force;
- {
- FILE *tp, *fopen();
- char lbuf[LSIZE]; /* line buffer */
- char pbuf[LSIZE]; /* search pattern buffer */
- register char *fname, *str;
- register char *p;
-
- 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) {
-
- /*
- * Scan through the search string. If we see a magic
- * char, we have to quote it. This lets us use "real"
- * implementations of ctags.
- */
- p = pbuf;
- *p++ = *str++; /* copy the '/' or '?' */
- *p++ = *str++; /* copy the '^' */
-
- for (; *str != NUL ;str++) {
- if (*str == '\\') {
- *p++ = *str++;
- *p++ = *str;
- } else if (strchr("/?", *str) != NULL) {
- if (str[1] != '\n') {
- *p++ = '\\';
- *p++ = *str;
- } else
- *p++ = *str;
- } else if (strchr("^()*.", *str) != NULL) {
- *p++ = '\\';
- *p++ = *str;
- } else
- *p++ = *str;
- }
- *p = NUL;
-
- /*
- * This looks out of order, but by calling stuffin()
- * before doecmd() we keep an extra screen update
- * from occuring. This stuffins() have no effect
- * until we get back to the main loop, anyway.
- */
- stuffin(pbuf); /* str has \n at end */
- stuffin("\007"); /* CTRL('g') */
-
- if (doecmd(fname, force)) {
- fclose(tp);
- return;
- } else
- stuffin(NULL); /* clear the input */
- }
- }
- emsg("tag not found");
- fclose(tp);
- }
-
- static bool_t
- doecmd(arg, force)
- char *arg;
- bool_t force;
- {
- int line = 1; /* line # to go to in new file */
-
- if (!force && Changed) {
- emsg(nowrtmsg);
- if (altfile)
- free(altfile);
- altfile = strsave(arg);
- return FALSE;
- }
- 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 && strcmp(arg, Filename) == 0) {
- if (!Changed || (Changed && !force))
- return TRUE;
- }
- if (altfile) {
- if (strcmp (arg, altfile) == 0)
- line = altline;
- free(altfile);
- }
- 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))
- filemess("[New File]");
-
- *Topchar = *Curschar;
- if (line != 1) {
- stuffnum(line);
- stuffin("G");
- }
- do_mlines();
- setpcmark();
- updatescreen();
- return TRUE;
- }
-
- void
- gotocmd(clr, firstc)
- bool_t clr;
- char firstc;
- {
- windgoto(Rows-1,0);
- if (clr)
- outcstr(T_EL); /* clear the bottom line */
- if (firstc)
- outchar(firstc);
- }
-
- /*
- * msg(s) - displays the string 's' on the status line
- */
- void
- msg(s)
- char *s;
- {
- gotocmd(TRUE, 0);
- outstr(s);
- flushbuf();
- }
-
- /*VARARGS1*/
- void
- smsg(s, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16)
- char *s;
- int a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16;
- {
- char sbuf[80];
-
- sprintf(sbuf, s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16);
- msg(sbuf);
- }
-
- /*
- * emsg() - display an error message
- *
- * Rings the bell, if appropriate, and calls message() to do the real work
- */
- void
- emsg(s)
- char *s;
- {
- if (P(P_EB))
- beep();
- msg(s);
- }
-
- void
- wait_return()
- {
- register char c;
-
- if (got_int)
- outstr("Interrupt: ");
-
- outstr("Press RETURN to continue");
- do {
- c = vgetc();
- } while (c != CR && c != NL && c != ' ' && c != ':');
-
- if (c == ':') {
- outchar(NL);
- docmdln(getcmdln(c));
- } else
- screenclear();
-
- updatescreen();
- }
-