home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / e / elv17src.zip / CMD1.C < prev    next >
C/C++ Source or Header  |  1992-12-30  |  39KB  |  1,904 lines

  1. /* cmd1.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains some of the EX commands - mostly ones that deal with
  12.  * files, options, etc. -- anything except text.
  13.  */
  14.  
  15. #include "config.h"
  16. #include "ctype.h"
  17. #include "vi.h"
  18. #include "regexp.h"
  19.  
  20. #ifndef NO_TAGSTACK
  21. /* These describe the current state of the tag related commands          */
  22. #define MAXTAGS    15
  23.  
  24. struct Tag_item {
  25.     MARK        tag_mark;
  26.     char        *tag_file;
  27. };
  28.  
  29. static struct Tag_item    tag_stack[MAXTAGS];
  30. static int        curr_tag = -1;
  31. #endif /* !NO_TAGSTACK */
  32.  
  33. #ifdef DEBUG
  34. /* print the selected lines with info on the blocks */
  35. /*ARGSUSED*/
  36. void cmd_debug(frommark, tomark, cmd, bang, extra)
  37.     MARK    frommark;
  38.     MARK    tomark;
  39.     CMD    cmd;
  40.     int    bang;
  41.     char    *extra;
  42. {
  43.     REG char    *scan;
  44.     REG long    l;
  45.     REG int        i;
  46.     int        len;
  47.  
  48.     /* scan lnum[] to determine which block its in */
  49.     l = markline(frommark);
  50.     for (i = 1; l > lnum[i]; i++)
  51.     {
  52.     }
  53.  
  54.     do
  55.     {
  56.         /* fetch text of the block containing that line */
  57.         scan = blkget(i)->c;
  58.  
  59.         /* calculate its length */
  60.         if (scan[BLKSIZE - 1])
  61.         {
  62.             len = BLKSIZE;
  63.         }
  64.         else
  65.         {
  66.             len = strlen(scan);
  67.         }
  68.  
  69.         /* print block stats */
  70.         msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
  71.             i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
  72.         msg("##### len=%d, buf=0x%lx, %sdirty",
  73.             len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
  74.         if (bang)
  75.         {
  76.             while (--len >= 0)
  77.             {
  78.                 addch(*scan);
  79.                 scan++;
  80.             }
  81.         }
  82.         exrefresh();
  83.  
  84.         /* next block */
  85.         i++;
  86.     } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
  87. }
  88.  
  89.  
  90. /* This function checks a lot of conditions to make sure they aren't screwy */
  91. /*ARGSUSED*/
  92. void cmd_validate(frommark, tomark, cmd, bang, extra)
  93.     MARK    frommark;
  94.     MARK    tomark;
  95.     CMD    cmd;
  96.     int    bang;
  97.     char    *extra;
  98. {
  99.     char    *scan;
  100.     int    i;
  101.     int    nlcnt;    /* used to count newlines */
  102.     int    len;    /* counts non-NUL characters */
  103.  
  104.     /* check lnum[0] */
  105.     if (lnum[0] != 0L)
  106.     {
  107.         msg("lnum[0] = %ld", lnum[0]);
  108.     }
  109.  
  110.     /* check each block */
  111.     for (i = 1; lnum[i] <= nlines; i++)
  112.     {
  113.         scan = blkget(i)->c;
  114.         if (scan[BLKSIZE - 1])
  115.         {
  116.             msg("block %d has no NUL at the end", i);
  117.         }
  118.         else
  119.         {
  120.             for (nlcnt = len = 0; *scan; scan++, len++)
  121.             {
  122.                 if (*scan == '\n')
  123.                 {
  124.                     nlcnt++;
  125.                 }
  126.             }
  127.             if (scan[-1] != '\n')
  128.             {
  129.                 msg("block %d doesn't end with '\\n' (length %d)", i, len);
  130.             }
  131.             if (bang || nlcnt != lnum[i] - lnum[i - 1])
  132.             {
  133.                 msg("block %d (line %ld?) has %d lines, but should have %ld",
  134.                     i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
  135.             }
  136.         }
  137.         exrefresh();
  138.     }
  139.  
  140.     /* check lnum again */
  141.     if (lnum[i] != INFINITY)
  142.     {
  143.         msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
  144.             i, hdr.n[i], i, lnum[i]);
  145.     }
  146.  
  147.     msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
  148.     msg("V_from=%ld.%d, cursor=%ld.%d", markline(V_from), markidx(V_from), markline(cursor), markidx(cursor));
  149. }
  150. #endif /* DEBUG */
  151.  
  152.  
  153. /*ARGSUSED*/
  154. void cmd_mark(frommark, tomark, cmd, bang, extra)
  155.     MARK    frommark;
  156.     MARK    tomark;
  157.     CMD    cmd;
  158.     int    bang;
  159.     char    *extra;
  160. {
  161.     /* validate the name of the mark */
  162.     if (*extra == '"')
  163.     {
  164.         extra++;
  165.     }
  166.     /* valid mark names are lowercase ascii characters */
  167.     if (!isascii(*extra) || !islower(*extra) || extra[1])
  168.     {
  169.         msg("Invalid mark name");
  170.         return;
  171.     }
  172.  
  173.     mark[*extra - 'a'] = tomark;
  174. }
  175.  
  176. /*ARGSUSED*/
  177. void cmd_write(frommark, tomark, cmd, bang, extra)
  178.     MARK    frommark;
  179.     MARK    tomark;
  180.     CMD    cmd;
  181.     int    bang;
  182.     char    *extra;
  183. {
  184.     int        fd;
  185.     int        append;    /* boolean: write in "append" mode? */
  186.     REG long    l;
  187.     REG char    *scan;
  188.     REG int        i;
  189.  
  190.     /* if writing to a filter, then let filter() handle it */
  191.     if (*extra == '!')
  192.     {
  193.         filter(frommark, tomark, extra + 1, FALSE);
  194.         return;
  195.     }
  196.  
  197.     /* if all lines are to be written, use tmpsave() */
  198.     if (frommark == MARK_FIRST && tomark == MARK_LAST && cmd == CMD_WRITE)
  199.     {
  200.         tmpsave(extra, bang);
  201.         return;
  202.     }
  203.  
  204.     /* see if we're going to do this in append mode or not */
  205.     append = FALSE;
  206.     if (extra[0] == '>' && extra[1] == '>')
  207.     {
  208.         extra += 2;
  209.         append = TRUE;
  210.     }
  211.  
  212.     /* either the file must not exist, or we must have a ! or be appending */
  213.     if (*extra && access(extra, 0) == 0 && !bang && !append)
  214.     {
  215.         msg("File already exists - Use :w! to overwrite");
  216.         return;
  217.     }
  218.  
  219.     /* else do it line-by-line, like cmd_print() */
  220.     if (append)
  221.     {
  222. #ifdef O_APPEND
  223.         fd = open(extra, O_WRONLY|O_APPEND);
  224. #else
  225.         fd = open(extra, O_WRONLY);
  226.         if (fd >= 0)
  227.         {
  228.             lseek(fd, 0L, 2);
  229.         }
  230. #endif
  231.     }
  232.     else
  233.     {
  234.         fd = -1; /* so we know the file isn't open yet */
  235.     }
  236.  
  237.     if (fd < 0)
  238.     {
  239.         fd = creat(extra, FILEPERMS);
  240.         if (fd < 0)
  241.         {
  242.             msg("Can't write to \"%s\"", extra);
  243.             return;
  244.         }
  245.     }
  246.     for (l = markline(frommark); l <= markline(tomark); l++)
  247.     {
  248.         /* get the next line */
  249.         scan = fetchline(l);
  250.         i = strlen(scan);
  251.         scan[i++] = '\n';
  252.  
  253.         /* print the line */
  254.         if (twrite(fd, scan, i) < i)
  255.         {
  256.             msg("Write failed");
  257.             break;
  258.         }
  259.     }
  260.     rptlines = markline(tomark) - markline(frommark) + 1;
  261.     rptlabel = "written";
  262.     close(fd);
  263. }    
  264.  
  265.  
  266. /*ARGSUSED*/
  267. void cmd_shell(frommark, tomark, cmd, bang, extra)
  268.     MARK    frommark, tomark;
  269.     CMD    cmd;
  270.     int    bang;
  271.     char    *extra;
  272. {
  273.     static char    prevextra[80];
  274.  
  275.     /* special case: ":sh" means ":!sh" */
  276.     if (cmd == CMD_SHELL)
  277.     {
  278.         extra = o_shell;
  279.         frommark = tomark = 0L;
  280.     }
  281.  
  282.     /* if extra is "!", substitute previous command */
  283.     if (*extra == '!')
  284.     {
  285.         if (!*prevextra)
  286.         {
  287.             msg("No previous shell command to substitute for '!'");
  288.             return;
  289.         }
  290.         extra = prevextra;
  291.     }
  292.     else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
  293.     {
  294.         strcpy(prevextra, extra);
  295.     }
  296.  
  297.     /* warn the user if the file hasn't been saved yet */
  298.     if (*o_warn && tstflag(file, MODIFIED))
  299.     {
  300.         if (mode == MODE_VI)
  301.         {
  302.             mode = MODE_COLON;
  303.         }
  304.         msg("Warning: \"%s\" has been modified but not yet saved", origname);
  305.     }
  306.  
  307.     /* if no lines were specified, just run the command */
  308.     suspend_curses();
  309.     if (frommark == 0L)
  310.     {
  311.         system(extra);
  312.     }
  313.     else /* pipe lines from the file through the command */
  314.     {
  315.         filter(frommark, tomark, extra, TRUE);
  316.     }
  317.  
  318.     /* resume curses quietly for MODE_EX, but noisily otherwise */
  319.     resume_curses(mode == MODE_EX);
  320. }
  321.  
  322.  
  323. /*ARGSUSED*/
  324. void cmd_global(frommark, tomark, cmd, bang, extra)
  325.     MARK    frommark, tomark;
  326.     CMD    cmd;
  327.     int    bang;
  328.     char    *extra;    /* rest of the command line */
  329. {
  330.     char    *cmdptr;    /* the command from the command line */
  331.     char    cmdln[100];    /* copy of the command from the command line */
  332.     char    *line;        /* a line from the file */
  333.     long    l;        /* used as a counter to move through lines */
  334.     long    lqty;        /* quantity of lines to be scanned */
  335.     long    nchanged;    /* number of lines changed */
  336.     regexp    *re;        /* the compiled search expression */
  337.  
  338.     /* can't nest global commands */
  339.     if (doingglobal)
  340.     {
  341.         msg("Can't nest global commands.");
  342.         rptlines = -1L;
  343.         return;
  344.     }
  345.  
  346.     /* ":g! ..." is the same as ":v ..." */
  347.     if (bang)
  348.     {
  349.         cmd = CMD_VGLOBAL;
  350.     }
  351.  
  352.     /* make sure we got a search pattern */
  353.     if (*extra != '/' && *extra != '?')
  354.     {
  355.         msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
  356.         return;
  357.     }
  358.  
  359.     /* parse & compile the search pattern */
  360.     cmdptr = parseptrn(extra);
  361.     if (!extra[1])
  362.     {
  363.         msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
  364.         return;
  365.     }
  366.     re = regcomp(extra + 1);
  367.     if (!re)
  368.     {
  369.         /* regcomp found & described an error */
  370.         return;
  371.     }
  372.  
  373.     /* for each line in the range */
  374.     doingglobal = TRUE;
  375.     ChangeText
  376.     {
  377.         /* NOTE: we have to go through the lines in a forward order,
  378.          * otherwise "g/re/p" would look funny.  *BUT* for "g/re/d"
  379.          * to work, simply adding 1 to the line# on each loop won't
  380.          * work.  The solution: count lines relative to the end of
  381.          * the file.  Think about it.
  382.          */
  383.         for (l = nlines - markline(frommark),
  384.             lqty = markline(tomark) - markline(frommark) + 1L,
  385.             nchanged = 0L;
  386.              lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
  387.              l--, lqty--)
  388.         {
  389.             /* fetch the line */
  390.             line = fetchline(nlines - l);
  391.  
  392.             /* if it contains the search pattern... */
  393.             if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
  394.             {
  395.                 /* move the cursor to that line */
  396.                 cursor = MARK_AT_LINE(nlines - l);
  397.  
  398.                 /* do the ex command (without mucking up
  399.                  * the original copy of the command line)
  400.                  */
  401.                 strcpy(cmdln, cmdptr);
  402.                 rptlines = 0L;
  403.                 doexcmd(cmdln);
  404.                 nchanged += rptlines;
  405.             }
  406.         }
  407.     }
  408.     doingglobal = FALSE;
  409.  
  410.     /* free the regexp */
  411.     _free_(re);
  412.  
  413.     /* Reporting...*/
  414.     rptlines = nchanged;
  415. }
  416.  
  417.  
  418. /*ARGSUSED*/
  419. void cmd_file(frommark, tomark, cmd, bang, extra)
  420.     MARK    frommark, tomark;
  421.     CMD    cmd;
  422.     int    bang;
  423.     char    *extra;
  424. {
  425. #ifndef CRUNCH
  426.     /* if we're given a new filename, use it as this file's name */
  427.     if (extra && *extra)
  428.     {
  429.         strcpy(origname, extra);
  430.         storename(origname);
  431.         setflag(file, NOTEDITED);
  432.     }
  433. #endif
  434.     if (cmd == CMD_FILE)
  435.     {
  436. #ifndef CRUNCH
  437.         msg("\"%s\" %s%s%s line %ld of %ld [%ld%%]",
  438. #else
  439.         msg("\"%s\" %s%s line %ld of %ld [%ld%%]",
  440. #endif
  441.             *origname ? origname : "[NO FILE]",
  442.             tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
  443. #ifndef CRUNCH
  444.             tstflag(file, NOTEDITED) ?"[NOT EDITED]":"",
  445. #endif
  446.             tstflag(file, READONLY) ? "[READONLY]" : "",
  447.             markline(frommark),
  448.             nlines,
  449.             markline(frommark) * 100 / nlines);
  450.     }
  451. #ifndef CRUNCH
  452.     else if (markline(frommark) != markline(tomark))
  453.     {
  454.         msg("range \"%ld,%ld\" contains %ld lines",
  455.             markline(frommark),
  456.             markline(tomark),
  457.             markline(tomark) - markline(frommark) + 1L);
  458.     }
  459. #endif
  460.     else
  461.     {
  462.         msg("%ld", markline(frommark));
  463.     }
  464. }
  465.  
  466.  
  467. /*ARGSUSED*/
  468. void cmd_edit(frommark, tomark, cmd, bang, extra)
  469.     MARK    frommark, tomark;
  470.     CMD    cmd;
  471.     int    bang;
  472.     char    *extra;
  473. {
  474.     long    line = 1L;    /* might be set to prevline */
  475. #ifndef CRUNCH
  476.     char    *init = (char *)0;
  477. #endif
  478.  
  479.  
  480.     /* if ":vi", then switch to visual mode, and if no file is named
  481.      * then don't switch files.
  482.      */
  483.     if (cmd == CMD_VISUAL)
  484.     {
  485.         mode = MODE_VI;
  486.         msg("");
  487.         if (!*extra)
  488.         {
  489.             return;
  490.         }
  491.     }
  492.  
  493.     /* Editing previous file?  Then start at previous line */
  494.     if (!strcmp(extra, prevorig))
  495.     {
  496.         line = prevline;
  497.     }
  498.  
  499. #ifndef CRUNCH
  500.     /* if we were given an explicit starting line, then start there */
  501.     if (*extra == '+')
  502.     {
  503.         for (init = ++extra; !isspace(*extra); extra++)
  504.         {
  505.         }
  506.         while (isspace(*extra))
  507.         {
  508.             *extra++ = '\0';
  509.         }
  510.         if (!*init)
  511.         {
  512.             init = "$";
  513.         }
  514.         if (!extra)
  515.         {
  516.             extra = origname;
  517.         }
  518.     }
  519. #endif /* not CRUNCH */
  520.  
  521.     /* switch files */
  522.     if (tmpabort(bang))
  523.     {
  524.         tmpstart(extra);
  525.         if (line <= nlines && line >= 1L)
  526.         {
  527.             cursor = MARK_AT_LINE(line);
  528.         }
  529. #ifndef CRUNCH
  530.         if (init)
  531.         {
  532.             doexcmd(init);
  533.         }
  534. #endif
  535.     }
  536.     else
  537.     {
  538.         msg("Use edit! to abort changes, or w to save changes");
  539.  
  540.         /* so we can say ":e!#" next time... */
  541.         strcpy(prevorig, extra);
  542.         prevline = 1L;
  543.     }
  544. }
  545.  
  546. /* This code is also used for rewind -- GB */
  547.  
  548. /*ARGSUSED*/
  549. void cmd_next(frommark, tomark, cmd, bang, extra)
  550.     MARK    frommark, tomark;
  551.     CMD    cmd;
  552.     int    bang;
  553.     char    *extra;
  554. {
  555.     int    i, j;
  556.     char    *scan;
  557.  
  558.     /* if extra stuff given, use ":args" to define a new args list */
  559.     if (cmd == CMD_NEXT && extra && *extra)
  560.     {
  561.         cmd_args(frommark, tomark, cmd, bang, extra);
  562.     }
  563.  
  564.     /* move to the next arg */
  565.     if (cmd == CMD_NEXT)
  566.     {
  567.         i = argno + 1;
  568.     }
  569.     else if (cmd == CMD_PREVIOUS)
  570.     {
  571.         i = argno - 1;
  572.     }
  573.     else /* cmd == CMD_REWIND */
  574.     {
  575.         i = 0;
  576.     }    
  577.     if (i < 0 || i >= nargs)
  578.     {
  579.         msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
  580.         return;
  581.     }
  582.  
  583.     /* find & isolate the name of the file to edit */
  584.     for (j = i, scan = args; j > 0; j--)
  585.     {
  586.         while(*scan++)
  587.         {
  588.         }
  589.     }
  590.  
  591.     /* switch to the next file */
  592.     if (tmpabort(bang))
  593.     {
  594.         tmpstart(scan);
  595.         argno = i;
  596.     }
  597.     else
  598.     {
  599.         msg("Use :%s! to abort changes, or w to save changes",
  600.             cmd == CMD_NEXT ? "next" :
  601.             cmd == CMD_PREVIOUS ? "previous" :
  602.                     "rewind");
  603.     }
  604. }
  605.  
  606. /* also called for :wq -- always writes back in this case */
  607. /* also called for :q -- never writes back in that case */
  608. /*ARGSUSED*/
  609. void cmd_xit(frommark, tomark, cmd, bang, extra)
  610.     MARK    frommark, tomark;
  611.     CMD    cmd;
  612.     int    bang;
  613.     char    *extra;
  614. {
  615.     static long    whenwarned;    /* when the user was last warned of extra files */
  616.     int        oldflag;
  617.  
  618.     /* Unless the command is ":q", save the file if it has been modified */
  619.     if (cmd != CMD_QUIT
  620.      && (cmd == CMD_WQUIT || tstflag(file, MODIFIED))
  621.      && !tmpsave((char *)0, FALSE) && !bang)
  622.     {
  623.         msg("Could not save file -- use quit! to abort changes, or w filename");
  624.         return;
  625.     }
  626.  
  627.     /* If there are more files to edit, then warn user */
  628.     if (argno >= 0 && argno + 1 < nargs    /* more args */
  629.      && whenwarned != changes        /* user not already warned */
  630.      && (!bang || cmd != CMD_QUIT))        /* command not ":q!" */
  631.     {
  632.         msg("More files to edit -- Use \":n\" to go to next file");
  633.         whenwarned = changes;
  634.         return;
  635.     }
  636.  
  637.     /* Discard the temp file.  Note that we should already have saved the
  638.      * the file, unless the command is ":q", so the only way that tmpabort
  639.      * could fail would be if you did a ":q" on a modified file.
  640.      */
  641.     oldflag = *o_autowrite;
  642.     *o_autowrite = FALSE;
  643.     if (tmpabort(bang))
  644.     {
  645.         mode = MODE_QUIT;
  646.     }
  647.     else
  648.     {
  649.         msg("Use q! to abort changes, or wq to save changes");
  650.     }
  651.     *o_autowrite = oldflag;
  652. }
  653.  
  654.  
  655. /*ARGSUSED*/
  656. void cmd_args(frommark, tomark, cmd, bang, extra)
  657.     MARK    frommark, tomark;
  658.     CMD    cmd;
  659.     int    bang;
  660.     char    *extra;
  661. {
  662.     char    *scan;
  663.     int    col;
  664.     int    arg;
  665.     int    scrolled = FALSE;
  666.     int    width;
  667.  
  668.     /* if no extra names given, or just current name, then report the args
  669.      * we have now.
  670.      */
  671.     if (!extra || !*extra)
  672.     {
  673.         /* empty args list? */
  674.         if (nargs == 1 && !*args)
  675.         {
  676.             return;
  677.         }
  678.  
  679.         /* list the arguments */
  680.         for (scan = args, col = arg = 0;
  681.              arg < nargs;
  682.              scan += width + 1, col += width, arg++)
  683.         {
  684.             width = strlen(scan);
  685.             if (col + width >= COLS - 4)
  686.             {
  687.                 addch('\n');
  688.                 col = 0;
  689.                 scrolled = TRUE;
  690.             }
  691.             else if (col > 0)
  692.             {
  693.                 addch(' ');
  694.                 col++;
  695.             }
  696.             if (arg == argno)
  697.             {
  698.                 addch('[');
  699.                 addstr(scan);
  700.                 addch(']');
  701.                 col += 2;
  702.             }
  703.             else
  704.             {
  705.                 addstr(scan);
  706.             }
  707.         }
  708.  
  709.         /* write a trailing newline */
  710.         if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
  711.         {
  712.             addch('\n');
  713.         }
  714.         exrefresh();    
  715.     }
  716.     else /* new args list given */
  717.     {
  718.         for (scan = args, nargs = 1; *extra; )
  719.         {
  720.             if (isspace(*extra))
  721.             {
  722.                 *scan++ = '\0';
  723.                 while (isspace(*extra))
  724.                 {
  725.                     extra++;
  726.                 }
  727.                 if (*extra)
  728.                 {
  729.                     nargs++;
  730.                 }
  731.             }
  732.             else
  733.             {
  734.                 *scan++ = *extra++;
  735.             }
  736.         }
  737.         *scan = '\0';
  738.  
  739.         /* reset argno to before the first, so :next will go to first */
  740.         argno = -1;
  741.  
  742.         if (nargs != 1)
  743.         {
  744.                         msg("%d files to edit", nargs);
  745.         }
  746.     }
  747. }
  748.  
  749.  
  750. /*ARGSUSED*/
  751. void cmd_cd(frommark, tomark, cmd, bang, extra)
  752.     MARK    frommark, tomark;
  753.     CMD    cmd;
  754.     int    bang;
  755.     char    *extra;
  756. {
  757. #ifndef CRUNCH
  758.     /* if current file is modified, and no '!' was given, then error */
  759.     if (tstflag(file, MODIFIED) && !bang)
  760.     {
  761.         msg("File modified; use \"cd! %s\" to switch anyway", extra);
  762.     }
  763. #endif
  764.  
  765.     /* default directory name is $HOME */
  766.     if (!*extra)
  767.     {
  768.         extra = gethome((char *)0);
  769.         if (!extra)
  770.         {
  771.             msg("environment variable $HOME not set");
  772.             return;
  773.         }
  774.     }
  775.  
  776.     /* go to the directory */
  777.     if (chdir(extra) < 0)
  778.     {
  779.         perror(extra);
  780.     }
  781. }
  782.  
  783.  
  784. /*ARGSUSED*/
  785. void cmd_map(frommark, tomark, cmd, bang, extra)
  786.     MARK    frommark, tomark;
  787.     CMD    cmd;
  788.     int    bang;
  789.     char    *extra;
  790. {
  791.     char    *mapto;
  792.     char    *build, *scan;
  793. #ifndef NO_FKEY
  794.     static char *fnames[NFKEYS] =
  795.     {
  796.         "#10", "#1", "#2", "#3", "#4",
  797.         "#5", "#6", "#7", "#8", "#9",
  798. # ifndef NO_SHIFT_FKEY
  799.         "#10s", "#1s", "#2s", "#3s", "#4s",
  800.         "#5s", "#6s", "#7s", "#8s", "#9s",
  801. #  ifndef NO_CTRL_FKEY
  802.         "#10c", "#1c", "#2c", "#3c", "#4c",
  803.         "#5c", "#6c", "#7c", "#8c", "#9c",
  804. #   ifndef NO_ALT_FKEY
  805.         "#10a", "#1a", "#2a", "#3a", "#4a",
  806.         "#5a", "#6a", "#7a", "#8a", "#9a",
  807. #   endif
  808. #  endif
  809. # endif
  810.     };
  811.     int    key;
  812. #endif
  813.  
  814.     /* "map" with no extra will dump the map table contents */
  815.     if (!*extra)
  816.     {
  817. #ifndef NO_ABBR
  818.         if (cmd == CMD_ABBR)
  819.         {
  820.             dumpkey(bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, TRUE);
  821.         }
  822.         else
  823. #endif
  824.         {
  825.             dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, FALSE);
  826.         }
  827.     }
  828.     else
  829.     {
  830.         /* "extra" is key to map, followed by what it maps to */
  831.  
  832.         /* handle quoting inside the "raw" string */
  833.         for (build = mapto = extra;
  834.              *mapto && (*mapto != ' ' && *mapto != '\t');
  835.              *build++ = *mapto++)
  836.         {
  837.             if (*mapto == ctrl('V') && mapto[1])
  838.             {
  839.                 mapto++;
  840.             }
  841.         }
  842.  
  843.         /* skip whitespace, and mark the end of the "raw" string */
  844.         while ((*mapto == ' ' || *mapto == '\t'))
  845.         {
  846.             *mapto++ = '\0';
  847.         }
  848.         *build = '\0';
  849.  
  850.         /* strip ^Vs from the "cooked" string */
  851.         for (scan = build = mapto; *scan; *build++ = *scan++)
  852.         {
  853.             if (*scan == ctrl('V') && scan[1])
  854.             {
  855.                 scan++;
  856.             }
  857.         }
  858.         *build = '\0';
  859.  
  860. #ifndef NO_FKEY
  861.         /* if the mapped string is '#' and a number, then assume
  862.          * the user wanted that function key
  863.          */
  864.         if (extra[0] == '#' && isdigit(extra[1]))
  865.         {
  866.             key = atoi(extra + 1) % 10;
  867. # ifndef NO_SHIFT_FKEY
  868.             build = extra + strlen(extra) - 1;
  869.             if (*build == 's')
  870.                 key += 10;
  871. #  ifndef NO_CTRL_FKEY
  872.             else if (*build == 'c')
  873.                 key += 20;
  874. #   ifndef NO_ALT_FKEY
  875.             else if (*build == 'a')
  876.                 key += 30;
  877. #   endif
  878. #  endif
  879. # endif
  880.             if (FKEY[key])
  881.                 mapkey(FKEY[key], mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, fnames[key]);
  882.             else
  883.                 msg("This terminal has no %s key", fnames[key]);
  884.         }
  885.         else
  886. #endif
  887. #ifndef NO_ABBR
  888.         if (cmd == CMD_ABBR || cmd == CMD_UNABBR)
  889.         {
  890.             mapkey(extra, mapto, bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, "abbr");
  891.         }
  892.         else
  893. #endif
  894.         {
  895.             mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
  896.         }
  897.     }
  898. }
  899.  
  900.  
  901. /*ARGSUSED*/
  902. void cmd_set(frommark, tomark, cmd, bang, extra)
  903.     MARK    frommark, tomark;
  904.     CMD    cmd;
  905.     int    bang;
  906.     char    *extra;
  907. {
  908.     if (!*extra)
  909.     {
  910.         dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
  911.     }
  912.     else if (!strcmp(extra, "all"))
  913.     {
  914.         dumpopts(TRUE);    /* "TRUE" means "dump all" - even unset vars */
  915.     }
  916.     else
  917.     {
  918.         setopts(extra);
  919.  
  920.         /* That option may have affected the appearence of text */
  921.         changes++;
  922.     }
  923. }
  924.  
  925. /*ARGSUSED*/
  926. void cmd_tag(frommark, tomark, cmd, bang, extra)
  927.     MARK    frommark, tomark;
  928.     CMD    cmd;
  929.     int    bang;
  930.     char    *extra;
  931. {
  932.     int    fd;    /* file descriptor used to read the file */
  933.     char    *scan;    /* used to scan through the tmpblk.c */
  934. #ifdef INTERNAL_TAGS
  935.     char    *cmp;    /* char of tag name we're comparing, or NULL */
  936.     char    *end;    /* marks the end of chars in tmpblk.c */
  937.     char    file[128];    /* name of file containing tag */
  938.     int    found;        /* whether the tag has been found */
  939.     int    file_exists;    /* whether any tag file exists */
  940.     char    *s, *t;
  941. #else
  942. # ifndef NO_TAGSTACK
  943.     char    *s;
  944. # endif
  945.     int    i;
  946. #endif
  947. #ifndef NO_MAGIC
  948.     char    wasmagic; /* preserves the original state of o_magic */
  949. #endif
  950.     static char prevtag[30];
  951.  
  952.     /* if no tag is given, use the previous tag */
  953.     if (!extra || !*extra)
  954.     {
  955.         if (!*prevtag)
  956.         {
  957.             msg("No previous tag");
  958.             return;
  959.         }
  960.         extra = prevtag;
  961.     }
  962.     else
  963.     {
  964.         strncpy(prevtag, extra, sizeof prevtag);
  965.         prevtag[sizeof prevtag - 1] = '\0';
  966.     }
  967.  
  968. #ifndef INTERNAL_TAGS
  969.     /* use "ref" to look up the tag info for this tag */
  970.     sprintf(tmpblk.c, "ref -t %s%s %s", (*origname ? "-f" : ""),origname, prevtag);
  971.     fd = rpipe(tmpblk.c, 0);
  972.     if (fd < 0)
  973.     {
  974.         msg("Can't run \"%s\"", tmpblk.c);
  975.         return;
  976.     }
  977.  
  978.     /* try to read the tag info */
  979.     for (scan = tmpblk.c;
  980.          (i = tread(fd, scan, scan - tmpblk.c + BLKSIZE)) > 0;
  981.          scan += i)
  982.     {
  983.     }
  984.     *scan = '\0';
  985.  
  986.     /* close the pipe.  abort if error */
  987.     if (rpclose(fd) != 0)
  988.     {
  989.         msg("Trouble running \"ref\" -- Can't do tag lookup");
  990.         return;
  991.     }
  992.     else if (scan < tmpblk.c + 3)
  993.     {
  994.         msg("tag \"%s\" not found", extra);
  995.         return;
  996.     }
  997.  
  998. #else /* use internal code to look up the tag */
  999.     found = 0;
  1000.     file_exists = 0;
  1001.     s = o_tags;
  1002.     while (!found && *s != 0) {
  1003.         while (isspace(*s)) s++;
  1004.         for(t = file; s && *s && !isspace(*s); s++)
  1005.             *t++ = *s;
  1006.         *t = '\0';
  1007.  
  1008.         /* open the next tags file */
  1009.         fd = open(file, O_RDONLY);
  1010.         if (fd < 0)
  1011.             continue;
  1012.         else
  1013.             file_exists = 1;
  1014.  
  1015.         /* Hmmm... this would have been a lot easier with <stdio.h> */
  1016.     
  1017.         /* find the line with our tag in it */
  1018.         for(scan = end = tmpblk.c, cmp = extra; ; scan++)
  1019.         {
  1020.             /* read a block, if necessary */
  1021.             if (scan >= end)
  1022.             {
  1023.                 end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
  1024.                 scan = tmpblk.c;
  1025.                 if (scan >= end)
  1026.                 {
  1027.                     close(fd);
  1028.                     break;
  1029.                 }
  1030.             }
  1031.     
  1032.             /* if we're comparing, compare... */
  1033.             if (cmp)
  1034.             {
  1035.                 /* matched??? wow! */
  1036.                 if (!*cmp && *scan == '\t')
  1037.                 {
  1038.                     if ((s = strrchr(file, '/')) != 0 ||
  1039.                         (s = strrchr(file, '\\')) != 0)
  1040.                         ++s;
  1041.                     else
  1042.                         s = file;
  1043.                     *s = '\0';
  1044.                     found = 1;
  1045.                     break;
  1046.                 }
  1047.                 if (*cmp++ != *scan)
  1048.                 {
  1049.                     /* failed! skip to newline */
  1050.                     cmp = (char *)0;
  1051.                 }
  1052.             }
  1053.     
  1054.             /* if we're skipping to newline, do it fast! */
  1055.             if (!cmp)
  1056.             {
  1057.                 while (scan < end && *scan != '\n')
  1058.                 {
  1059.                     scan++;
  1060.                 }
  1061.                 if (scan < end)
  1062.                 {
  1063.                     cmp = extra;
  1064.                 }
  1065.             }
  1066.         }
  1067.     }
  1068.  
  1069.     if (!file_exists) {
  1070.         msg("No tags file");
  1071.         return;
  1072.     }
  1073.  
  1074.     if (!found) {
  1075.         msg("tag \"%s\" not found", extra);
  1076.         return;
  1077.     }
  1078.  
  1079.     /* found it! get the rest of the line into memory */
  1080.     for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
  1081.     {
  1082.         *cmp++ = *scan++;
  1083.     }
  1084.     if (scan == end)
  1085.     {
  1086.         tread(fd, cmp, BLKSIZE - (int)(cmp - tmpblk.c));
  1087.     }
  1088.     else
  1089.         *cmp = *scan;
  1090.  
  1091.     /* we can close the tags file now */
  1092.     close(fd);
  1093. #endif /* INTERNAL_TAGS */
  1094.  
  1095.     /* extract the filename from the line, and edit the file */
  1096.     for (scan = tmpblk.c; *scan != '\t'; scan++)
  1097.     {
  1098.     }
  1099.     *scan++ = '\0';
  1100.     if (strcmp(origname, tmpblk.c) != 0)
  1101.     {
  1102.         if (!tmpabort(bang))
  1103.         {
  1104.             msg("Use :tag! to abort changes, or :w to save changes");
  1105.             return;
  1106.         }
  1107.         tmpstart(tmpblk.c);
  1108. #ifdef NO_TAGSTACK
  1109.     }
  1110. #else /* tagstack enabled */
  1111.         s = prevorig;
  1112.     }
  1113.     else
  1114.         s = origname;
  1115.  
  1116.     if (frommark != MARK_UNSET && *s && *o_tagstack)
  1117.     {
  1118.         curr_tag++;
  1119.         if (curr_tag >= MAXTAGS)
  1120.         {
  1121.             /* discard the oldest tag position */
  1122.             free(tag_stack[0].tag_file);
  1123.             for (curr_tag = 0; curr_tag < MAXTAGS - 1; curr_tag++)
  1124.             {
  1125.                 tag_stack[curr_tag] = tag_stack[curr_tag + 1];
  1126.             }
  1127.             /* at this point, curr_tag = MAXTAGS-1 */
  1128.         }
  1129.         tag_stack[curr_tag].tag_file = (char *) malloc(strlen(s) + 1);
  1130.         strcpy(tag_stack[curr_tag].tag_file, s);
  1131.         tag_stack[curr_tag].tag_mark = frommark;
  1132.     }
  1133. #endif
  1134.  
  1135.     /* move to the desired line (or to line 1 if that fails) */
  1136. #ifndef NO_MAGIC
  1137.     wasmagic = *o_magic;
  1138.     *o_magic = FALSE;
  1139. #endif
  1140.     cursor = MARK_FIRST;
  1141.     linespec(scan, &cursor);
  1142.     if (cursor == MARK_UNSET)
  1143.     {
  1144.         cursor = MARK_FIRST;
  1145.         msg("Tag's address is out of date");
  1146.     }
  1147. #ifndef NO_MAGIC
  1148.     *o_magic = wasmagic;
  1149. #endif
  1150. }
  1151.  
  1152.  
  1153. #ifndef NO_TAGSTACK
  1154. /*ARGSUSED*/
  1155. void cmd_pop(frommark, tomark, cmd, bang, extra)
  1156.     MARK    frommark, tomark;
  1157.     CMD    cmd;
  1158.     int    bang;
  1159.     char    *extra;
  1160. {
  1161.     char    buf[8];
  1162.  
  1163.     if (!*o_tagstack)
  1164.     {
  1165.         msg("Tagstack not enabled");
  1166.         return;
  1167.     }
  1168.  
  1169.     if (curr_tag < 0)
  1170.         msg("Tagstack empty");
  1171.     else
  1172.     {
  1173.         if (strcmp(origname, tag_stack[curr_tag].tag_file) != 0)
  1174.         {
  1175.             if (!tmpabort(bang))
  1176.             {
  1177.                 msg("Use :pop! to abort changes, or :w to save changes");
  1178.                 return;
  1179.             }
  1180.             tmpstart(tag_stack[curr_tag].tag_file);
  1181.         }
  1182.         cursor = tag_stack[curr_tag].tag_mark;
  1183.         if (cursor < MARK_FIRST || cursor > MARK_LAST + BLKSIZE)
  1184.         {
  1185.             cursor = MARK_FIRST;
  1186.         }
  1187.         free(tag_stack[curr_tag--].tag_file);
  1188.     }
  1189. }
  1190. #endif
  1191.  
  1192.  
  1193.  
  1194. /* describe this version of the program */
  1195. /*ARGSUSED*/
  1196. void cmd_version(frommark, tomark, cmd, bang, extra)
  1197.     MARK    frommark;
  1198.     MARK    tomark;
  1199.     CMD    cmd;
  1200.     int    bang;
  1201.     char    *extra;
  1202. {
  1203.     msg("%s", VERSION);
  1204. #ifdef CREDIT
  1205.     msg("%s", CREDIT);
  1206. #endif
  1207. #ifdef CREDIT2
  1208.     msg("%s", CREDIT2);
  1209. #endif
  1210. #ifdef COMPILED_BY
  1211.     msg("Compiled by %s", COMPILED_BY);
  1212. #endif
  1213. #ifdef COPYING
  1214.     msg("%s", COPYING);
  1215. #endif
  1216. }
  1217.  
  1218.  
  1219. #ifndef NO_MKEXRC
  1220. /* make a .exrc file which describes the current configuration */
  1221. /*ARGSUSED*/
  1222. void cmd_mkexrc(frommark, tomark, cmd, bang, extra)
  1223.     MARK    frommark;
  1224.     MARK    tomark;
  1225.     CMD    cmd;
  1226.     int    bang;
  1227.     char    *extra;
  1228. {
  1229.     int    fd;
  1230.  
  1231.     /* the default name for the .exrc file EXRC */
  1232.     if (!*extra)
  1233.     {
  1234.         extra = EXRC;
  1235.     }
  1236.  
  1237.     /* create the .exrc file */
  1238.     fd = creat(extra, FILEPERMS);
  1239.     if (fd < 0)
  1240.     {
  1241.         msg("Couldn't create a new \"%s\" file", extra);
  1242.         return;
  1243.     }
  1244.  
  1245.     /* save stuff */
  1246.     saveopts(fd);
  1247.     savemaps(fd, FALSE);
  1248. #ifndef NO_ABBR
  1249.     savemaps(fd, TRUE);
  1250. #endif
  1251. #ifndef NO_DIGRAPH
  1252.     savedigs(fd);
  1253. #endif
  1254. #ifndef NO_COLOR
  1255.     savecolor(fd);
  1256. #endif
  1257.  
  1258.     /* close the file */
  1259.     close(fd);
  1260.     msg("Configuration saved");
  1261. }
  1262. #endif
  1263.  
  1264. #ifndef NO_DIGRAPH
  1265. /*ARGSUSED*/
  1266. void cmd_digraph(frommark, tomark, cmd, bang, extra)
  1267.     MARK    frommark;
  1268.     MARK    tomark;
  1269.     CMD    cmd;
  1270.     int    bang;
  1271.     char    *extra;
  1272. {
  1273.     do_digraph(bang, extra);
  1274. }
  1275. #endif
  1276.  
  1277.  
  1278. #ifndef NO_ERRLIST 
  1279. static char    errfile[256];    /* the name of a file containing an error */
  1280. static long    errline;    /* the line number for an error */
  1281. static int    errfd = -2;    /* fd of the errlist file */
  1282.  
  1283. /* This static function tries to parse an error message.
  1284.  *
  1285.  * For most compilers, the first word is taken to be the name of the erroneous
  1286.  * file, and the first number after that is taken to be the line number where
  1287.  * the error was detected.  The description of the error follows, possibly
  1288.  * preceded by an "error ... :" or "warning ... :" label which is skipped.
  1289.  *
  1290.  * For Coherent, error messages look like "line#: filename: message".
  1291.  *
  1292.  * For non-error lines, or unparsable error lines, this function returns NULL.
  1293.  * Normally, though, it alters errfile and errline, and returns a pointer to
  1294.  * the description.
  1295.  */
  1296. static char *parse_errmsg(text)
  1297.     REG char    *text;
  1298. {
  1299.     REG char    *cpy;
  1300.     long        atol();
  1301. # if COHERENT || TOS /* any Mark Williams compiler */
  1302.     /* Get the line number.  If no line number, then ignore this line. */
  1303.     errline = atol(text);
  1304.     if (errline == 0L)
  1305.         return (char *)0;
  1306.  
  1307.     /* Skip to the start of the filename */
  1308.     while (*text && *text++ != ':')
  1309.     {
  1310.     }
  1311.     if (!*text++)
  1312.         return (char *)0;
  1313.  
  1314.     /* copy the filename to errfile */
  1315.     for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
  1316.     {
  1317.     }
  1318.     if (!*text++)
  1319.         return (char *)0;
  1320.     cpy[-1] = '\0';
  1321.  
  1322.     return text;
  1323. # else /* not a Mark Williams compiler */
  1324.     char        *errmsg;
  1325.  
  1326.     /* the error message is the whole line, by default */
  1327.     errmsg = text;
  1328.  
  1329.     /* skip leading garbage */
  1330.     while (*text && !isalnum(*text))
  1331.     {
  1332.         text++;
  1333.     }
  1334.  
  1335.     /* copy over the filename */
  1336.     cpy = errfile;
  1337.     while(isalnum(*text) || *text == '.')
  1338.     {
  1339.         *cpy++ = *text++;
  1340.     }
  1341.     *cpy = '\0';
  1342.  
  1343.     /* ignore the name "Error" and filenames that contain a '/' */
  1344.     if (*text == '/' || !*errfile || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
  1345.     {
  1346.         return (char *)0;
  1347.     }
  1348.  
  1349.     /* skip garbage between filename and line number */
  1350.     while (*text && !isdigit(*text))
  1351.     {
  1352.         text++;
  1353.     }
  1354.  
  1355.     /* if the number is part of a larger word, then ignore this line */
  1356.     if (*text && (isalpha(text[-1]) || text[-1] == '_'))
  1357.     {
  1358.         return (char *)0;
  1359.     }
  1360.  
  1361.     /* get the error line */
  1362.     errline = 0L;
  1363.     while (isdigit(*text))
  1364.     {
  1365.         errline *= 10;
  1366.         errline += (*text - '0');
  1367.         text++;
  1368.     }
  1369.  
  1370.     /* any line which lacks a filename or line number should be ignored */
  1371.     if (!errfile[0] || !errline)
  1372.     {
  1373.         return (char *)0;
  1374.     }
  1375.  
  1376.     /* locate the beginning of the error description */
  1377.     while (*text && !isspace(*text))
  1378.     {
  1379.         text++;
  1380.     }
  1381.     while (*text)
  1382.     {
  1383. #  ifndef CRUNCH
  1384.         /* skip "error #:" and "warning #:" clauses */
  1385.         if (!strncmp(text + 1, "rror ", 5)
  1386.          || !strncmp(text + 1, "arning ", 7)
  1387.          || !strncmp(text + 1, "atal error", 10))
  1388.         {
  1389.             do
  1390.             {
  1391.                 text++;
  1392.             } while (*text && *text != ':');
  1393.             continue;
  1394.         }
  1395. #  endif
  1396.  
  1397.         /* anything other than whitespace or a colon is important */
  1398.         if (!isspace(*text) && *text != ':')
  1399.         {
  1400.             errmsg = text;
  1401.             break;
  1402.         }
  1403.  
  1404.         /* else keep looking... */
  1405.         text++;
  1406.     }
  1407.  
  1408.     return errmsg;
  1409. # endif /* not COHERENT */
  1410. }
  1411.  
  1412. /*ARGSUSED*/
  1413. void cmd_errlist(frommark, tomark, cmd, bang, extra)
  1414.     MARK    frommark, tomark;
  1415.     CMD    cmd;
  1416.     int    bang;
  1417.     char    *extra;
  1418. {
  1419.     static long    endline;/* original number of lines in this file */
  1420.     static long    offset;    /* offset of the next line in the errlist file */
  1421.     int        i;
  1422.     char        *errmsg;
  1423.  
  1424.     /* if a new errlist file is named, open it */
  1425.     if (extra && extra[0])
  1426.     {
  1427.         /* close the old one */
  1428.         if (errfd >= 0)
  1429.         {
  1430.             close(errfd);
  1431.         }
  1432.  
  1433.         /* open the new one */
  1434.         errfd = open(extra, O_RDONLY);
  1435.         offset = 0L;
  1436.         endline = nlines;
  1437.     }
  1438.     else if (errfd < 0)
  1439.     {
  1440.         /* open the default file */
  1441.         errfd = open(ERRLIST, O_RDONLY);
  1442.         offset = 0L;
  1443.         endline = nlines;
  1444.     }
  1445.  
  1446.     /* do we have an errlist file now? */
  1447.     if (errfd < 0)
  1448.     {
  1449.         msg("There is no errlist file");
  1450.         beep();
  1451.         return;
  1452.     }
  1453.  
  1454.     /* find the next error message in the file */
  1455.     do
  1456.     {
  1457.         /* read the next line from the errlist */
  1458.         lseek(errfd, offset, 0);
  1459.         if (tread(errfd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
  1460.         {
  1461.             msg("No more errors");
  1462.             beep();
  1463.             close(errfd);
  1464.             errfd = -2;
  1465.             return;
  1466.         }
  1467.         for (i = 0; tmpblk.c[i] != '\n'; i++)
  1468.         {
  1469.         }
  1470.         tmpblk.c[i++] = 0;
  1471.  
  1472.         /* look for an error message in the line */
  1473.         errmsg = parse_errmsg(tmpblk.c);
  1474.         if (!errmsg)
  1475.         {
  1476.             offset += i;
  1477.         }
  1478.  
  1479.     } while (!errmsg);
  1480.  
  1481.     /* switch to the file containing the error, if this isn't it */
  1482.     if (strcmp(origname, errfile))
  1483.     {
  1484.         if (!tmpabort(bang))
  1485.         {
  1486.             msg("Use :er! to abort changes, or :w to save changes");
  1487.             beep();
  1488.             return;
  1489.         }
  1490.         tmpstart(errfile);
  1491.         endline = nlines;
  1492.     }
  1493.     else if (endline == 0L)
  1494.     {
  1495.         endline = nlines;
  1496.     }
  1497.  
  1498.     /* go to the line where the error was detected */
  1499.     cursor = MARK_AT_LINE(errline + (nlines - endline));
  1500.     if (cursor > MARK_LAST)
  1501.     {
  1502.         cursor = MARK_LAST;
  1503.     }
  1504.     if (mode == MODE_VI)
  1505.     {
  1506.         redraw(cursor, FALSE);
  1507.     }
  1508.  
  1509.     /* display the error message */
  1510. #ifdef CRUNCH
  1511.     msg("%.70s", errmsg);
  1512. #else
  1513.     if (nlines > endline)
  1514.     {
  1515.         msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
  1516.     }
  1517.     else if (nlines < endline)
  1518.     {
  1519.         msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
  1520.     }
  1521.     else
  1522.     {
  1523.         msg("line %ld: %.65s", errline, errmsg);
  1524.     }
  1525. #endif
  1526.  
  1527.     /* remember where the NEXT error line will start */
  1528.     offset += i;
  1529. }
  1530.  
  1531.  
  1532. /*ARGSUSED*/
  1533. void cmd_make(frommark, tomark, cmd, bang, extra)
  1534.     MARK    frommark, tomark;
  1535.     CMD    cmd;
  1536.     int    bang;
  1537.     char    *extra;
  1538. {
  1539.     BLK    buf;
  1540.  
  1541.     /* if the file hasn't been saved, then complain unless ! */
  1542.     if (tstflag(file, MODIFIED) && !bang)
  1543.     {
  1544.         msg("\"%s\" not saved yet", origname);
  1545.         return;
  1546.     }
  1547.  
  1548.     /* build the command */
  1549.     sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
  1550.     qaddstr(buf.c);
  1551.     addch('\n');
  1552.  
  1553.     /* close the old errlist file, if any */
  1554.     if (errfd >= 0)
  1555.     {
  1556.         close(errfd);
  1557.         errfd = -3;
  1558.     }
  1559.  
  1560. #if MINT
  1561.     /* I guess MiNT can't depend on the shell for redirection? */
  1562.     close(creat(ERRLIST, 0666));
  1563.     if ((fd = open(ERRLIST, O_RDWR)) == -1)
  1564.     {
  1565.         unlink(ERRLIST);
  1566.         return;
  1567.     }
  1568.     suspend_curses();
  1569.     old2 = dup(2);
  1570.     dup2(fd, 2);
  1571.     system(buf.c);
  1572.     dup2(old2, 2);
  1573.     close(old2);
  1574.     close(fd);
  1575. #else
  1576.     /* run the command, with curses temporarily disabled */
  1577.     suspend_curses();
  1578.     system(buf.c);
  1579. #endif
  1580.     resume_curses(mode == MODE_EX);
  1581.     if (mode == MODE_COLON)
  1582.         /* ':' hit instead of CR, so let him escape... -nox */
  1583.         return;
  1584.  
  1585.     /* run the "errlist" command */
  1586.     cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
  1587.  
  1588.     /* avoid spurious `Hit <RETURN>' after 1st error message  -nox */
  1589.     /* (which happened when cmd_errlist didn't have to change files...)  */
  1590.     if (mode == MODE_VI)
  1591.         refresh();
  1592. }
  1593. #endif
  1594.  
  1595.  
  1596.  
  1597. #ifndef NO_COLOR
  1598.  
  1599. /* figure out the number of text colors we use with this configuration */
  1600. # ifndef NO_POPUP
  1601. #  ifndef NO_VISIBLE
  1602. #   define NCOLORS 7
  1603. #  else
  1604. #   define NCOLORS 6
  1605. #  endif
  1606. # else
  1607. #  ifndef NO_VISIBLE
  1608. #   define NCOLORS 6
  1609. #  else
  1610. #   define NCOLORS 5
  1611. #  endif
  1612. # endif
  1613.  
  1614. /* the attribute bytes used in each of "when"s */
  1615. static char bytes[NCOLORS];
  1616.  
  1617. static struct
  1618. {
  1619.     char    *word;    /* a legal word */
  1620.     int    type;    /* what type of word this is */
  1621.     int    val;    /* some other value */
  1622. }
  1623.     words[] =
  1624. {
  1625.     {"normal",    1,    A_NORMAL},    /* all "when" names must come */
  1626.     {"standout",    1,    A_STANDOUT},    /* at the top of the list.    */
  1627.     {"bold",    1,    A_BOLD},    /* The first 3 must be normal,*/
  1628.     {"underlined",    1,    A_UNDERLINE},    /* standout, and bold; the    */
  1629.     {"italics",    1,    A_ALTCHARSET},    /* remaining names follow.    */
  1630. #ifndef NO_POPUP
  1631.     {"popup",    1,    A_POPUP},
  1632. #endif
  1633. #ifndef NO_VISIBLE
  1634.     {"visible",    1,    A_VISIBLE},
  1635. #endif
  1636.  
  1637.     {"black",    3,    0x00},        /* The color names start right*/
  1638.     {"blue",    3,    0x01},        /* after the "when" names.    */
  1639.     {"green",    3,    0x02},
  1640.     {"cyan",    3,    0x03},
  1641.     {"red",        3,    0x04},
  1642.     {"magenta",    3,    0x05},
  1643.     {"brown",    3,    0x06},
  1644.     {"white",    3,    0x07},
  1645.     {"yellow",    3,    0x0E}, /* bright brown */
  1646.     {"gray",    3,    0x08}, /* bright black?  of course! */
  1647.     {"grey",    3,    0x08},
  1648.  
  1649.     {"bright",    2,    0x08},
  1650.     {"light",    2,    0x08},
  1651.     {"blinking",    2,    0x80},
  1652.     {"on",        0,    0},
  1653.     {"n",        1,    A_NORMAL},
  1654.     {"s",        1,    A_STANDOUT},
  1655.     {"b",        1,    A_BOLD},
  1656.     {"u",        1,    A_UNDERLINE},
  1657.     {"i",        1,    A_ALTCHARSET},
  1658. #ifndef NO_POPUP
  1659.     {"p",        1,    A_POPUP},
  1660.     {"menu",    1,    A_POPUP},
  1661. #endif
  1662. #ifndef NO_VISIBLE
  1663.     {"v",        1,    A_VISIBLE},
  1664. #endif
  1665.     {(char *)0,    0,    0}
  1666. };
  1667.  
  1668. /*ARGSUSED*/
  1669. void cmd_color(frommark, tomark, cmd, bang, extra)
  1670.     MARK    frommark, tomark;
  1671.     CMD    cmd;
  1672.     int    bang;
  1673.     char    *extra;
  1674. {
  1675.     int    attrbyte;
  1676.     int    cmode;
  1677.     int    nowbg;    /* BOOLEAN: is the next color background? */
  1678.  
  1679.     REG char *scan;
  1680.     REG    i;
  1681.  
  1682.  
  1683. #ifndef CRUNCH
  1684.     /* if no args are given, then report the current colors */
  1685.     if (!*extra)
  1686.     {
  1687.         /* if no colors are set, then say so */
  1688.         if (!bytes[0])
  1689.         {
  1690.             msg("no colors have been set");
  1691.             return;
  1692.         }
  1693.  
  1694.         /* report all five color combinations */
  1695.         for (i = 0; i < NCOLORS; i++)
  1696.         {
  1697.             qaddstr("color ");
  1698.             qaddstr(words[i].word);
  1699.             qaddch(' ');
  1700.             if (bytes[i] & 0x80)
  1701.                 qaddstr("blinking ");
  1702.             switch (bytes[i] & 0xf)
  1703.             {
  1704.               case 0x08:    qaddstr("gray");    break;
  1705.               case 0x0e:    qaddstr("yellow");    break;
  1706.               case 0x0f:    qaddstr("bright white");break;
  1707.               default:
  1708.                 if (bytes[i] & 0x08)
  1709.                     qaddstr("light ");
  1710.                 qaddstr(words[(bytes[i] & 0x07) + NCOLORS].word);
  1711.             }
  1712.             qaddstr(" on ");
  1713.             qaddstr(words[((bytes[i] >> 4) & 0x07) + NCOLORS].word);
  1714.             addch('\n');
  1715.             exrefresh();
  1716.         }
  1717.         return;
  1718.     }
  1719. #endif
  1720.  
  1721.     /* The default background color is the same as "normal" chars.
  1722.      * There is no default foreground color.
  1723.      */
  1724.     cmode = A_NORMAL;
  1725.     attrbyte = bytes[0] & 0x70;
  1726.     nowbg = FALSE;
  1727.  
  1728.     /* parse each word in the "extra" text */
  1729.     for (scan = extra; *extra; extra = scan)
  1730.     {
  1731.         /* locate the end of the word */
  1732.         while (*scan && *scan != ' ')
  1733.         {
  1734.             scan++;
  1735.         }
  1736.  
  1737.         /* skip whitespace at the end of the word */
  1738.         while(*scan == ' ')
  1739.         {
  1740.             *scan++ = '\0';
  1741.         }
  1742.  
  1743.         /* lookup the word */
  1744.         for (i = 0; words[i].word && strcmp(words[i].word, extra); i++)
  1745.         {
  1746.         }
  1747.  
  1748.         /* if not a word, then complain */
  1749.         if (!words[i].word)
  1750.         {
  1751.             msg("Invalid color name: %s", extra);
  1752.             return;
  1753.         }
  1754.  
  1755.         /* process the word */
  1756.         switch (words[i].type)
  1757.         {
  1758.           case 1:
  1759.             cmode = words[i].val;
  1760.             break;
  1761.  
  1762.           case 2:
  1763.             attrbyte |= words[i].val;
  1764.             break;
  1765.  
  1766.           case 3:
  1767.             if (nowbg)
  1768.                 attrbyte = ((attrbyte & ~0x70) | ((words[i].val & 0x07) << 4));
  1769.             else
  1770.                 attrbyte |= words[i].val;
  1771.             nowbg = TRUE;
  1772.             break;
  1773.         }
  1774.     }
  1775.  
  1776.     /* if nowbg isn't set now, then we were never given a foreground color */
  1777.     if (!nowbg)
  1778.     {
  1779.         msg("usage: color [when] [\"bright\"] [\"blinking\"] foreground [background]");
  1780.         return;
  1781.     }
  1782.  
  1783.     /* the first ":color" command MUST define the "normal" colors */
  1784.     if (!bytes[0])
  1785.         cmode = A_NORMAL;
  1786.  
  1787.     /* we should now have a cmode and an attribute byte... */
  1788.  
  1789.     /* set the color */
  1790.     setcolor(cmode, attrbyte);
  1791.  
  1792.     /* remember what we just did */
  1793.     bytes[cmode] = attrbyte;
  1794.  
  1795.     /* if the other colors haven't been set yet, then set them to defaults */
  1796.     if (!bytes[1])
  1797.     {
  1798.         /* standout is the opposite of normal */
  1799.         bytes[1] = ((attrbyte << 4) & 0x70 | (attrbyte >> 4) & 0x07);
  1800.         setcolor(A_STANDOUT, bytes[1]);
  1801.  
  1802.         /* if "normal" isn't bright, then bold defaults to normal+bright
  1803.          * else bold defaults to bright white.
  1804.          */
  1805.         bytes[2] = attrbyte | ((attrbyte & 0x08) ? 0x0f : 0x08);
  1806.         setcolor(A_BOLD, bytes[2]);
  1807.  
  1808.         /* all others default to the "standout" colors, without blinking */
  1809.         for (i = 3; i < NCOLORS; i++)
  1810.         {
  1811.             bytes[i] = (bytes[1] & 0x7f);
  1812.             setcolor(words[i].val, bytes[i]);
  1813.         }
  1814.     }
  1815.  
  1816.     /* force a redraw, so we see the new colors */
  1817.     redraw(MARK_UNSET, FALSE);
  1818. }
  1819.  
  1820.  
  1821.  
  1822. void savecolor(fd)
  1823.     int    fd;    /* file descriptor to write colors to */
  1824. {
  1825.     int    i;
  1826.     char    buf[80];
  1827.  
  1828.     /* if no colors are set, then return */
  1829.     if (!bytes[0])
  1830.     {
  1831.         return;
  1832.     }
  1833.  
  1834.     /* save all five color combinations */
  1835.     for (i = 0; i < NCOLORS; i++)
  1836.     {
  1837.         strcpy(buf, "color ");
  1838.         strcat(buf, words[i].word);
  1839.         strcat(buf, " ");
  1840.         if (bytes[i] & 0x80)
  1841.             strcat(buf, "blinking ");
  1842.         switch (bytes[i] & 0xf)
  1843.         {
  1844.           case 0x08:    strcat(buf, "gray");    break;
  1845.           case 0x0e:    strcat(buf, "yellow");    break;
  1846.           case 0x0f:    strcat(buf, "bright white");break;
  1847.           default:
  1848.             if (bytes[i] & 0x08)
  1849.                 strcat(buf, "light ");
  1850.             strcat(buf, words[(bytes[i] & 0x07) + NCOLORS].word);
  1851.         }
  1852.         strcat(buf, " on ");
  1853.         strcat(buf, words[((bytes[i] >> 4) & 0x07) + NCOLORS].word);
  1854.         strcat(buf, "\n");
  1855.         twrite(fd, buf, (unsigned)strlen(buf));
  1856.     }
  1857. }
  1858. #endif
  1859.  
  1860. #ifdef SIGTSTP
  1861. /* temporarily suspend elvis */
  1862. /*ARGSUSED*/
  1863. void cmd_suspend(frommark, tomark, cmd, bang, extra)
  1864.     MARK    frommark;
  1865.     MARK    tomark;
  1866.     CMD    cmd;
  1867.     int    bang;
  1868.     char    *extra;
  1869. {
  1870.     SIGTYPE    (*func)();    /* stores the previous setting of SIGTSTP */
  1871.  
  1872. #if ANY_UNIX
  1873.     /* the Bourne shell can't handle ^Z */
  1874.     if (!strcmp(o_shell, "/bin/sh"))
  1875.     {
  1876.         msg("The /bin/sh shell doesn't support ^Z");
  1877.         return;
  1878.     }
  1879. #endif
  1880.  
  1881.     move(LINES - 1, 0);
  1882.     if (tstflag(file, MODIFIED))
  1883.     {
  1884.         addstr("Warning: \"");
  1885.         addstr(origname);
  1886.         addstr("\" modified but not yet saved");
  1887.         clrtoeol();
  1888.     }
  1889.     refresh();
  1890.     suspend_curses();
  1891.     func = signal(SIGTSTP, SIG_DFL);
  1892.     kill (0, SIGTSTP);
  1893.  
  1894.     /* the process stops and resumes here */
  1895.  
  1896.     signal(SIGTSTP, func);
  1897.     resume_curses(TRUE);
  1898.     if (mode == MODE_VI || mode == MODE_COLON)
  1899.         redraw(MARK_UNSET, FALSE);
  1900.     else
  1901.         refresh ();
  1902. }
  1903. #endif
  1904.