home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / elvis184.zip / src / cmd1.c < prev    next >
C/C++ Source or Header  |  1995-05-26  |  40KB  |  2,030 lines

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