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