home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d1xx / d166 / stevie.lha / Stevie / source / cmdline.c < prev    next >
C/C++ Source or Header  |  1988-11-22  |  13KB  |  633 lines

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