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