home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / editor / stevie / cmdline.c < prev    next >
C/C++ Source or Header  |  1994-01-31  |  12KB  |  656 lines

  1. /* $Header: /nw/tony/src/stevie/src/RCS/cmdline.c,v 1.20 89/08/13 11:41:23 tony Exp $
  2.  *
  3.  * Routines to parse and execute "command line" commands, such as searches
  4.  * or colon commands.
  5.  */
  6.  
  7. #include "stevie.h"
  8.  
  9. static    char    *altfile = NULL;    /* alternate file */
  10. static    int    altline;        /* line # in alternate file */
  11.  
  12. static    char    *nowrtmsg = "No write since last change (use ! to override)";
  13. static    char    *nooutfile = "No output file";
  14. static    char    *morefiles = "more files to edit";
  15.  
  16. extern    char    **files;        /* used for "n" and "rew" */
  17. extern    int    numfiles, curfile;
  18.  
  19. #define    CMDSZ    100        /* size of the command buffer */
  20.  
  21. static    void    get_range();
  22. static    LPTR    *get_line();
  23.  
  24. /*
  25.  * getcmdln() - read a command line from the terminal
  26.  *
  27.  * Reads a command line started by typing '/', '?', '!', or ':'. Returns a
  28.  * pointer to the string that was read. For searches, an optional trailing
  29.  * '/' or '?' is removed.
  30.  */
  31. char *
  32. getcmdln(firstc)
  33. char    firstc;
  34. {
  35.     static    char    buff[CMDSZ];
  36.     register char    *p = buff;
  37.     register int    c;
  38.     register char    *q;
  39.  
  40.     gotocmd(TRUE, firstc);
  41.  
  42.     /* collect the command string, handling '\b' and @ */
  43.     do {
  44.         switch (c = vgetc()) {
  45.  
  46.         default:        /* a normal character */
  47.             outchar(c);
  48.             *p++ = c;
  49.             break;
  50.  
  51.         case BS:
  52.             if (p > buff) {
  53.                 /*
  54.                  * this is gross, but it relies
  55.                  * only on 'gotocmd'
  56.                  */
  57.                 p--;
  58.                 gotocmd(TRUE, firstc);
  59.                 for (q = buff; q < p ;q++)
  60.                     outchar(*q);
  61.             } else {
  62.                 msg("");
  63.                 return NULL;        /* back to cmd mode */
  64.             }
  65.             break;
  66.  
  67.         case '@':            /* line kill */
  68.             p = buff;
  69.             gotocmd(TRUE, firstc);
  70.             break;
  71.  
  72.         case ESC:            /* abandon command */
  73.             msg("");
  74.             return  NULL;
  75.             break;
  76.  
  77.         case NL:            /* done reading the line */
  78.         case CR:
  79.             break;
  80.         }
  81.     } while (c != NL && c != CR);
  82.  
  83.     *p = '\0';
  84.  
  85.     if (firstc == '/' || firstc == '?') {    /* did we do a search? */
  86.         /*
  87.          * Look for a terminating '/' or '?'. This will be the first
  88.          * one that isn't quoted. Truncate the search string there.
  89.          */
  90.         for (p = buff; *p ;) {
  91.             if (*p == firstc) {    /* we're done */
  92.                 *p = '\0';
  93.                 break;
  94.             } else if (*p == '\\')    /* next char quoted */
  95.                 p += 2;
  96.             else
  97.                 p++;        /* normal char */
  98.         }
  99.     }
  100.     return buff;
  101. }
  102.  
  103. /*
  104.  * docmdln() - handle a colon command
  105.  *
  106.  * Handles a colon command received interactively by getcmdln() or from
  107.  * the environment variable "EXINIT" (or eventually .virc).
  108.  */
  109. void
  110. docmdln(cmdline)
  111. char    *cmdline;
  112. {
  113.     char    buff[CMDSZ];
  114.     char    cmdbuf[CMDSZ];
  115.     char    argbuf[CMDSZ];
  116.     char    *cmd, *arg;
  117.     register char    *p;
  118.     /*
  119.      * The next two variables contain the bounds of any range given in a
  120.      * command. If no range was given, both contain null line pointers.
  121.      * If only a single line was given, u_pos will contain a null line
  122.      * pointer.
  123.      */
  124.     LPTR    l_pos, u_pos;
  125.  
  126.  
  127.     /*
  128.      * Clear the range variables.
  129.      */
  130.     l_pos.linep = (struct line *) NULL;
  131.     u_pos.linep = (struct line *) NULL;
  132.  
  133.     if (cmdline == NULL)
  134.         return;
  135.  
  136.     if (strlen(cmdline) > CMDSZ-2) {
  137.         msg("Error: command line too long");
  138.         return;
  139.     }
  140.     strcpy(buff, cmdline);
  141.  
  142.     /* skip any initial white space */
  143.     for (cmd = buff; *cmd != NUL && isspace(*cmd) ;cmd++)
  144.         ;
  145.  
  146.     if (*cmd == '%') {        /* change '%' to "1,$" */
  147.         strcpy(cmdbuf, "1,$");    /* kind of gross... */
  148.         strcat(cmdbuf, cmd+1);
  149.         strcpy(cmd, cmdbuf);
  150.     }
  151.  
  152.     while ((p=strchr(cmd, '%')) != NULL && *(p-1) != '\\') {
  153.                     /* change '%' to Filename */
  154.         if (Filename == NULL) {
  155.             emsg("No filename");
  156.             return;
  157.         }
  158.         *p= NUL;
  159.         strcpy (cmdbuf, cmd);
  160.         strcat (cmdbuf, Filename);
  161.         strcat (cmdbuf, p+1);
  162.         strcpy(cmd, cmdbuf);
  163.         msg(cmd);            /*repeat */
  164.     }
  165.  
  166.     while ((p=strchr(cmd, '#')) != NULL && *(p-1) != '\\') {
  167.                     /* change '#' to Altname */
  168.         if (altfile == NULL) {
  169.             emsg("No alternate file");
  170.             return;
  171.         }
  172.         *p= NUL;
  173.         strcpy (cmdbuf, cmd);
  174.         strcat (cmdbuf, altfile);
  175.         strcat (cmdbuf, p+1);
  176.         strcpy(cmd, cmdbuf);
  177.         msg(cmd);            /*repeat */
  178.     }
  179.  
  180.     /*
  181.      * Parse a range, if present (and update the cmd pointer).
  182.      */
  183.     get_range(&cmd, &l_pos, &u_pos);
  184.  
  185.     if (l_pos.linep != NULL) {
  186.         if (LINEOF(&l_pos) > LINEOF(&u_pos)) {
  187.             emsg("Invalid range");
  188.             return;
  189.         }
  190.     }
  191.  
  192.     strcpy(cmdbuf, cmd);    /* save the unmodified command */
  193.  
  194.     /* isolate the command and find any argument */
  195.     for ( p=cmd; *p != NUL && ! isspace(*p); p++ )
  196.         ;
  197.     if ( *p == NUL )
  198.         arg = NULL;
  199.     else {
  200.         *p = NUL;
  201.         for (p++; *p != NUL && isspace(*p) ;p++)
  202.             ;
  203.         if (*p == NUL)
  204.             arg = NULL;
  205.         else {
  206.             strcpy(argbuf, p);
  207.             arg = argbuf;
  208.         }
  209.     }
  210.     if (strcmp(cmd,"q!") == 0)
  211.         getout();
  212.     if (strcmp(cmd,"q") == 0) {
  213.         if (Changed)
  214.             emsg(nowrtmsg);
  215.         else {
  216.             if ((curfile + 1) < numfiles)
  217.                 emsg(morefiles);
  218.             else
  219.                 getout();
  220.         }
  221.         return;
  222.     }
  223.     if (strcmp(cmd,"w") == 0) {
  224.         if (arg == NULL) {
  225.             if (Filename != NULL) {
  226.                 writeit(Filename, &l_pos, &u_pos);
  227.             } else
  228.                 emsg(nooutfile);
  229.         }
  230.         else
  231.             writeit(arg, &l_pos, &u_pos);
  232.         return;
  233.     }
  234.     if (strcmp(cmd,"wq") == 0) {
  235.         if (Filename != NULL) {
  236.             if (writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
  237.                 getout();
  238.         } else
  239.             emsg(nooutfile);
  240.         return;
  241.     }
  242.     if (strcmp(cmd, "x") == 0) {
  243.         doxit();
  244.         return;
  245.     }
  246.  
  247.     if (strcmp(cmd,"f") == 0 && arg == NULL) {
  248.         fileinfo();
  249.         return;
  250.     }
  251.     if (*cmd == 'n') {
  252.         if ((curfile + 1) < numfiles) {
  253.             /*
  254.              * stuff ":e[!] FILE\n"
  255.              */
  256.             stuffin(":e");
  257.             if (cmd[1] == '!')
  258.                 stuffin("!");
  259.             stuffin(" ");
  260.             stuffin(files[++curfile]);
  261.             stuffin("\n");
  262.         } else
  263.             emsg("No more files!");
  264.         return;
  265.     }
  266.     if (*cmd == 'N') {
  267.         if (curfile > 0) {
  268.             /*
  269.              * stuff ":e[!] FILE\n"
  270.              */
  271.             stuffin(":e");
  272.             if (cmd[1] == '!')
  273.                 stuffin("!");
  274.             stuffin(" ");
  275.             stuffin(files[--curfile]);
  276.             stuffin("\n");
  277.         } else
  278.             emsg("No more files!");
  279.         return;
  280.     }
  281.     if (strncmp(cmd, "rew", 3) == 0) {
  282.         if (numfiles <= 1)        /* nothing to rewind */
  283.             return;
  284.         curfile = 0;
  285.         /*
  286.          * stuff ":e[!] FILE\n"
  287.          */
  288.         stuffin(":e");
  289.         if (cmd[3] == '!')
  290.             stuffin("!");
  291.         stuffin(" ");
  292.         stuffin(files[0]);
  293.         stuffin("\n");
  294.         return;
  295.     }
  296.     if (strcmp(cmd,"e") == 0 || strcmp(cmd,"e!") == 0) {
  297.         (void) doecmd(arg, cmd[1] == '!');
  298.         return;
  299.     }
  300.     /*
  301.      * The command ":e#" gets expanded to something like ":efile", so
  302.      * detect that case here.
  303.      */
  304.     if (*cmd == 'e' && arg == NULL) {
  305.         if (cmd[1] == '!')
  306.             (void) doecmd(&cmd[2], TRUE);
  307.         else
  308.             (void) doecmd(&cmd[1], FALSE);
  309.         return;
  310.     }
  311.     if (strcmp(cmd,"f") == 0) {
  312.         EnvEval (arg, CMDSZ);    /* expand environment vars */
  313.         Filename = strsave(arg);
  314.         filemess("");
  315.         return;
  316.     }
  317.     if (strcmp(cmd,"r") == 0) {
  318.         if (arg == NULL) {
  319.             badcmd();
  320.             return;
  321.         }
  322.         if (readfile(arg, Curschar, 1)) {
  323.             emsg("Can't open file");
  324.             return;
  325.         }
  326.         updatescreen();
  327.         CHANGED;
  328.         return;
  329.     }
  330.     if (strcmp(cmd,"=") == 0) {
  331.         smsg("%d", cntllines(Filemem, &l_pos));
  332.         return;
  333.     }
  334.     if (strncmp(cmd,"ta", 2) == 0) {
  335.         dotag(arg, cmd[2] == '!');
  336.         return;
  337.     }
  338.     if (strncmp(cmd,"untag", 5) == 0) {
  339.         if (P(P_TG))
  340.             dountag(cmd[5]);
  341.         else
  342.             emsg("Tag stacking not enabled");
  343.         return;
  344.     }
  345.     if (strncmp(cmd,"set", 2) == 0) {
  346.         doset(arg);
  347.         return;
  348.     }
  349.     if (strcmp(cmd,"help") == 0) {
  350.         if (help()) {
  351.             screenclear();
  352.             updatescreen();
  353.         }
  354.         return;
  355.     }
  356.     if (strncmp(cmd, "ve", 2) == 0) {
  357.         extern    char    *Version;
  358.  
  359.         msg(Version);
  360.         return;
  361.     }
  362.     if (strcmp(cmd, "sh") == 0) {
  363.         doshell(NULL);
  364.         return;
  365.     }
  366.     if (*cmd == '!') {
  367.         doshell(cmdbuf+1);
  368.         return;
  369.     }
  370.     if (strncmp(cmd, "s/", 2) == 0) {
  371.         dosub(&l_pos, &u_pos, cmdbuf+1);
  372.         return;
  373.     }
  374.     if (strncmp(cmd, "g/", 2) == 0) {
  375.         doglob(&l_pos, &u_pos, cmdbuf+1);
  376.         return;
  377.     }
  378.     /*
  379.      * If we got a line, but no command, then go to the line.
  380.      */
  381.     if (*cmd == NUL && l_pos.linep != NULL) {
  382.         *Curschar = l_pos;
  383.         return;
  384.     }
  385.  
  386.     badcmd();
  387. }
  388.  
  389.  
  390. doxit()
  391. {
  392.     if (Changed) {
  393.         if (Filename != NULL) {
  394.             if (!writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
  395.                 return;
  396.         } else {
  397.             emsg(nooutfile);
  398.             return;
  399.         }
  400.     }
  401.     if ((curfile + 1) < numfiles)
  402.         emsg(morefiles);
  403.     else
  404.         getout();
  405. }
  406.  
  407. /*
  408.  * get_range - parse a range specifier
  409.  *
  410.  * Ranges are of the form:
  411.  *
  412.  * addr[,addr]
  413.  *
  414.  * where 'addr' is:
  415.  *
  416.  * $  [+- NUM]
  417.  * 'x [+- NUM]    (where x denotes a currently defined mark)
  418.  * .  [+- NUM]
  419.  * NUM
  420.  *
  421.  * The pointer *cp is updated to point to the first character following
  422.  * the range spec. If an initial address is found, but no second, the
  423.  * upper bound is equal to the lower.
  424.  */
  425. static void
  426. get_range(cp, lower, upper)
  427. register char    **cp;
  428. LPTR    *lower, *upper;
  429. {
  430.     register LPTR    *l;
  431.     register char    *p;
  432.  
  433.     if ((l = get_line(cp)) == NULL)
  434.         return;
  435.  
  436.     *lower = *l;
  437.  
  438.     for (p = *cp; *p != NUL && isspace(*p) ;p++)
  439.         ;
  440.  
  441.     *cp = p;
  442.  
  443.     if (*p != ',') {        /* is there another line spec ? */
  444.         *upper = *lower;
  445.         return;
  446.     }
  447.  
  448.     *cp = ++p;
  449.  
  450.     if ((l = get_line(cp)) == NULL) {
  451.         *upper = *lower;
  452.         return;
  453.     }
  454.  
  455.     *upper = *l;
  456. }
  457.  
  458. static LPTR *
  459. get_line(cp)
  460. char    **cp;
  461. {
  462.     static    LPTR    pos;
  463.     LPTR    *lp;
  464.     register char    *p, c;
  465.     register int    lnum;
  466.  
  467.     pos.index = 0;        /* shouldn't matter... check back later */
  468.  
  469.     p = *cp;
  470.     /*
  471.      * Determine the basic form, if present.
  472.      */
  473.     switch (c = *p++) {
  474.  
  475.     case '$':
  476.         pos.linep = Fileend->linep->prev;
  477.         break;
  478.  
  479.     case '.':
  480.         pos.linep = Curschar->linep;
  481.         break;
  482.  
  483.     case '\'':
  484.         if ((lp = getmark(*p++)) == NULL) {
  485.             emsg("Unknown mark");
  486.             return (LPTR *) NULL;
  487.         }
  488.         pos = *lp;
  489.         break;
  490.  
  491.     case '0': case '1': case '2': case '3': case '4':
  492.     case '5': case '6': case '7': case '8': case '9':
  493.         for (lnum = c - '0'; isdigit(*p) ;p++)
  494.             lnum = (lnum * 10) + (*p - '0');
  495.  
  496.         pos = *gotoline(lnum);
  497.         break;
  498.  
  499.     default:
  500.         return (LPTR *) NULL;
  501.     }
  502.  
  503.     while (*p != NUL && isspace(*p))
  504.         p++;
  505.  
  506.     if (*p == '-' || *p == '+') {
  507.         bool_t    neg = (*p++ == '-');
  508.  
  509.         for (lnum = 0; isdigit(*p) ;p++)
  510.             lnum = (lnum * 10) + (*p - '0');
  511.  
  512.         if (neg)
  513.             lnum = -lnum;
  514.  
  515.         pos = *gotoline( cntllines(Filemem, &pos) + lnum );
  516.     }
  517.  
  518.     *cp = p;
  519.     return &pos;
  520. }
  521.  
  522. void
  523. badcmd()
  524. {
  525.     emsg("Unrecognized command");
  526. }
  527.  
  528. bool_t
  529. doecmd(arg, force)
  530. char    *arg;
  531. bool_t    force;
  532. {
  533.     int    line = 1;        /* line # to go to in new file */
  534.  
  535.     if (!force && Changed) {
  536.         emsg(nowrtmsg);
  537.         if (altfile)
  538.             free(altfile);
  539.         altfile = strsave(arg);
  540.         return FALSE;
  541.     }
  542.     if (arg != NULL) {
  543.         /*
  544.          * First detect a ":e" on the current file. This is mainly
  545.          * for ":ta" commands where the destination is within the
  546.          * current file.
  547.          */
  548.         if (Filename != NULL && strcmp(arg, Filename) == 0) {
  549.             if (!Changed || (Changed && !force))
  550.                 return TRUE;
  551.         }
  552.         if (altfile) {
  553.             if (strcmp (arg, altfile) == 0)
  554.                 line = altline;
  555.             free(altfile);
  556.         }
  557.         altfile = Filename;
  558.         altline = cntllines(Filemem, Curschar);
  559.         Filename = strsave(arg);
  560.     }
  561.     if (Filename == NULL) {
  562.         emsg("No filename");
  563.         return FALSE;
  564.     }
  565.  
  566.     /* clear mem and read file */
  567.     freeall();
  568.     filealloc();
  569.     UNCHANGED;
  570.  
  571.     if (readfile(Filename, Filemem, 0))
  572.         filemess("[New File]");
  573.  
  574.     *Topchar = *Curschar;
  575.     if (line != 1) {
  576.         stuffnum(line);
  577.         stuffin("G");
  578.     }
  579.     do_mlines();
  580.     setpcmark();
  581.     updatescreen();
  582.     return TRUE;
  583. }
  584.  
  585. void
  586. gotocmd(clr, firstc)
  587. bool_t  clr;
  588. char    firstc;
  589. {
  590.     windgoto(Rows-1,0);
  591.     if (clr)
  592.         CLEOL;        /* clear the bottom line */
  593.     if (firstc)
  594.         outchar(firstc);
  595. }
  596.  
  597. /*
  598.  * msg(s) - displays the string 's' on the status line
  599.  */
  600. void
  601. msg(s)
  602. char    *s;
  603. {
  604.     gotocmd(TRUE, 0);
  605.     outstr(s);
  606.     flushbuf();
  607. }
  608.  
  609. /*VARARGS1*/
  610. void
  611. smsg(s, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16)
  612. char    *s;
  613. int    a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16;
  614. {
  615.     char    sbuf[80];
  616.  
  617.     sprintf(sbuf, s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16);
  618.     msg(sbuf);
  619. }
  620.  
  621. /*
  622.  * emsg() - display an error message
  623.  *
  624.  * Rings the bell, if appropriate, and calls message() to do the real work
  625.  */
  626. void
  627. emsg(s)
  628. char    *s;
  629. {
  630.     if (P(P_EB))
  631.         beep();
  632.     msg(s);
  633. }
  634.  
  635. void
  636. wait_return()
  637. {
  638.     register char    c;
  639.  
  640.     if (got_int)
  641.         outstr("Interrupt: ");
  642.  
  643.     outstr("Press RETURN to continue");
  644.     do {
  645.         c = vgetc();
  646.     } while (c != CR && c != NL && c != ' ' && c != ':');
  647.  
  648.     if (c == ':') {
  649.         outchar(NL);
  650.         docmdln(getcmdln(c));
  651.     } else
  652.         screenclear();
  653.  
  654.     updatescreen();
  655. }
  656.