home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / APPS / elvis_1.4.tar.Z / elvis_1.4.tar / cmd1.c < prev    next >
C/C++ Source or Header  |  1990-12-06  |  24KB  |  1,269 lines

  1. /* cmd1.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains some of the EX commands - mostly ones that deal with
  12.  * files, options, etc. -- anything except text.
  13.  */
  14.  
  15. #include "config.h"
  16. #include <ctype.h>
  17. #include "vi.h"
  18. #include "regexp.h"
  19.  
  20. #if MSDOS
  21. #define    DATE __DATE__
  22. #endif
  23.  
  24. #ifdef DEBUG
  25. /* print the selected lines with info on the blocks */
  26. /*ARGSUSED*/
  27. void cmd_debug(frommark, tomark, cmd, bang, extra)
  28.     MARK    frommark;
  29.     MARK    tomark;
  30.     CMD    cmd;
  31.     int    bang;
  32.     char    *extra;
  33. {
  34.     REG char    *scan;
  35.     REG long    l;
  36.     REG int        i;
  37.     int        len;
  38.  
  39.     /* scan lnum[] to determine which block its in */
  40.     l = markline(frommark);
  41.     for (i = 1; l > lnum[i]; i++)
  42.     {
  43.     }
  44.  
  45.     do
  46.     {
  47.         /* fetch text of the block containing that line */
  48.         scan = blkget(i)->c;
  49.  
  50.         /* calculate its length */
  51.         if (scan[BLKSIZE - 1])
  52.         {
  53.             len = BLKSIZE;
  54.         }
  55.         else
  56.         {
  57.             len = strlen(scan);
  58.         }
  59.  
  60.         /* print block stats */
  61.         msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)",
  62.             i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]);
  63.         msg("##### len=%d, buf=0x%lx, %sdirty",
  64.             len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not ");
  65.         if (bang)
  66.         {
  67.             while (--len >= 0)
  68.             {
  69.                 addch(*scan);
  70.                 scan++;
  71.             }
  72.         }
  73.         exrefresh();
  74.  
  75.         /* next block */
  76.         i++;
  77.     } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark));
  78. }
  79.  
  80.  
  81. /* This function checks a lot of conditions to make sure they aren't screwy */
  82. /*ARGSUSED*/
  83. void cmd_validate(frommark, tomark, cmd, bang, extra)
  84.     MARK    frommark;
  85.     MARK    tomark;
  86.     CMD    cmd;
  87.     int    bang;
  88.     char    *extra;
  89. {
  90.     char    *scan;
  91.     int    i;
  92.     int    nlcnt;    /* used to count newlines */
  93.     int    len;    /* counts non-NUL characters */
  94.  
  95.     /* check lnum[0] */
  96.     if (lnum[0] != 0L)
  97.     {
  98.         msg("lnum[0] = %ld", lnum[0]);
  99.     }
  100.  
  101.     /* check each block */
  102.     for (i = 1; lnum[i] <= nlines; i++)
  103.     {
  104.         scan = blkget(i)->c;
  105.         if (scan[BLKSIZE - 1])
  106.         {
  107.             msg("block %d has no NUL at the end", i);
  108.         }
  109.         else
  110.         {
  111.             for (nlcnt = len = 0; *scan; scan++, len++)
  112.             {
  113.                 if (*scan == '\n')
  114.                 {
  115.                     nlcnt++;
  116.                 }
  117.             }
  118.             if (scan[-1] != '\n')
  119.             {
  120.                 msg("block %d doesn't end with '\\n' (length %d)", i, len);
  121.             }
  122.             if (bang || nlcnt != lnum[i] - lnum[i - 1])
  123.             {
  124.                 msg("block %d (line %ld?) has %d lines, but should have %ld",
  125.                     i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]);
  126.             }
  127.         }
  128.         exrefresh();
  129.     }
  130.  
  131.     /* check lnum again */
  132.     if (lnum[i] != INFINITY)
  133.     {
  134.         msg("hdr.n[%d] = %d, but lnum[%d] = %ld",
  135.             i, hdr.n[i], i, lnum[i]);
  136.     }
  137.  
  138.     msg("# = \"%s\", %% = \"%s\"", prevorig, origname);
  139. }
  140. #endif /* DEBUG */
  141.  
  142.  
  143. /*ARGSUSED*/
  144. void cmd_mark(frommark, tomark, cmd, bang, extra)
  145.     MARK    frommark;
  146.     MARK    tomark;
  147.     CMD    cmd;
  148.     int    bang;
  149.     char    *extra;
  150. {
  151.     /* validate the name of the mark */
  152.     if (!extra || *extra < 'a' || *extra > 'z' || extra[1])
  153.     {
  154.         msg("Invalid mark name");
  155.         return;
  156.     }
  157.  
  158.     mark[*extra - 'a'] = tomark;
  159. }
  160.  
  161. /*ARGSUSED*/
  162. void cmd_write(frommark, tomark, cmd, bang, extra)
  163.     MARK    frommark;
  164.     MARK    tomark;
  165.     CMD    cmd;
  166.     int    bang;
  167.     char    *extra;
  168. {
  169.     int        fd;
  170.     int        append;    /* boolean: write in "append" mode? */
  171.     REG long    l;
  172.     REG char    *scan;
  173.     REG int        i;
  174.  
  175.     /* if all lines are to be written, use tmpsave() */
  176.     if (frommark == MARK_FIRST && tomark == MARK_LAST)
  177.     {
  178.         tmpsave(extra, bang);
  179.         return;
  180.     }
  181.  
  182.     /* see if we're going to do this in append mode or not */
  183.     append = FALSE;
  184.     if (extra[0] == '>' && extra[1] == '>')
  185.     {
  186.         extra += 2;
  187.         append = TRUE;
  188.     }
  189.  
  190.     /* either the file must not exist, or we must have a ! or be appending */
  191.     if (access(extra, 0) == 0 && !bang && !append)
  192.     {
  193.         msg("File already exists - Use :w! to overwrite");
  194.         return;
  195.     }
  196.  
  197.     /* else do it line-by-line, like cmd_print() */
  198.     if (append)
  199.     {
  200. #ifdef O_APPEND
  201.         fd = open(extra, O_WRONLY|O_APPEND);
  202. #else
  203.         fd = open(extra, O_WRONLY);
  204.         if (fd >= 0)
  205.         {
  206.             lseek(fd, 0L, 2);
  207.         }
  208. #endif
  209.     }
  210.     else
  211.     {
  212.         fd = -1; /* so we know the file isn't open yet */
  213.     }
  214.  
  215.     if (fd < 0)
  216.     {
  217.         fd = creat(extra, FILEPERMS);
  218.         if (fd < 0)
  219.         {
  220.             msg("Can't write to \"%s\"", extra);
  221.             return;
  222.         }
  223.     }
  224.     for (l = markline(frommark); l <= markline(tomark); l++)
  225.     {
  226.         /* get the next line */
  227.         scan = fetchline(l);
  228.         i = strlen(scan);
  229.         scan[i++] = '\n';
  230.  
  231.         /* print the line */
  232.         twrite(fd, scan, i);
  233.     }
  234.     close(fd);
  235. }    
  236.  
  237.  
  238. /*ARGSUSED*/
  239. void cmd_shell(frommark, tomark, cmd, bang, extra)
  240.     MARK    frommark, tomark;
  241.     CMD    cmd;
  242.     int    bang;
  243.     char    *extra;
  244. {
  245.     static char    prevextra[80];
  246.  
  247.     /* special case: ":sh" means ":!sh" */
  248.     if (cmd == CMD_SHELL)
  249.     {
  250.         extra = o_shell;
  251.         frommark = tomark = 0L;
  252.     }
  253.  
  254.     /* if extra is "!", substute previous command */
  255.     if (*extra == '!')
  256.     {
  257.         if (!*prevextra)
  258.         {
  259.             msg("No previous shell command to substitute for '!'");
  260.             return;
  261.         }
  262.         extra = prevextra;
  263.     }
  264.     else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1)
  265.     {
  266.         strcpy(prevextra, extra);
  267.     }
  268.  
  269.     /* if no lines were specified, just run the command */
  270.     suspend_curses();
  271.     if (frommark == 0L)
  272.     {
  273.         system(extra);
  274.     }
  275.     else /* pipe lines from the file through the command */
  276.     {
  277.         filter(frommark, tomark, extra);
  278.     }
  279.  
  280.     /* resume curses quietly for MODE_EX, but noisily otherwise */
  281.     resume_curses(mode == MODE_EX);
  282. }
  283.  
  284.  
  285. /*ARGSUSED*/
  286. void cmd_global(frommark, tomark, cmd, bang, extra)
  287.     MARK    frommark, tomark;
  288.     CMD    cmd;
  289.     int    bang;
  290.     char    *extra;    /* rest of the command line */
  291. {
  292.     char    *cmdptr;    /* the command from the command line */
  293.     char    cmdln[100];    /* copy of the command from the command line */
  294.     char    *line;        /* a line from the file */
  295.     long    l;        /* used as a counter to move through lines */
  296.     long    lqty;        /* quantity of lines to be scanned */
  297.     long    nchanged;    /* number of lines changed */
  298.     regexp    *re;        /* the compiled search expression */
  299.  
  300.     /* can't nest global commands */
  301.     if (doingglobal)
  302.     {
  303.         msg("Can't nest global commands.");
  304.         rptlines = -1L;
  305.         return;
  306.     }
  307.  
  308.     /* ":g! ..." is the same as ":v ..." */
  309.     if (bang)
  310.     {
  311.         cmd = CMD_VGLOBAL;
  312.     }
  313.  
  314.     /* make sure we got a search pattern */
  315.     if (*extra != '/' && *extra != '?')
  316.     {
  317.         msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v');
  318.         return;
  319.     }
  320.  
  321.     /* parse & compile the search pattern */
  322.     cmdptr = parseptrn(extra);
  323.     if (!extra[1])
  324.     {
  325.         msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v');
  326.         return;
  327.     }
  328.     re = regcomp(extra + 1);
  329.     if (!re)
  330.     {
  331.         /* regcomp found & described an error */
  332.         return;
  333.     }
  334.  
  335.     /* for each line in the range */
  336.     doingglobal = TRUE;
  337.     ChangeText
  338.     {
  339.         /* NOTE: we have to go through the lines in a forward order,
  340.          * otherwise "g/re/p" would look funny.  *BUT* for "g/re/d"
  341.          * to work, simply adding 1 to the line# on each loop won't
  342.          * work.  The solution: count lines relative to the end of
  343.          * the file.  Think about it.
  344.          */
  345.         for (l = nlines - markline(frommark),
  346.             lqty = markline(tomark) - markline(frommark) + 1L,
  347.             nchanged = 0L;
  348.              lqty > 0 && nlines - l >= 0 && nchanged >= 0L;
  349.              l--, lqty--)
  350.         {
  351.             /* fetch the line */
  352.             line = fetchline(nlines - l);
  353.  
  354.             /* if it contains the search pattern... */
  355.             if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL))
  356.             {
  357.                 /* move the cursor to that line */
  358.                 cursor = MARK_AT_LINE(nlines - l);
  359.  
  360.                 /* do the ex command (without mucking up
  361.                  * the original copy of the command line)
  362.                  */
  363.                 strcpy(cmdln, cmdptr);
  364.                 rptlines = 0L;
  365.                 doexcmd(cmdln);
  366.                 nchanged += rptlines;
  367.             }
  368.         }
  369.     }
  370.     doingglobal = FALSE;
  371.  
  372.     /* free the regexp */
  373.     free(re);
  374.  
  375.     /* Reporting...*/
  376.     rptlines = nchanged;
  377. }
  378.  
  379.  
  380. /*ARGSUSED*/
  381. void cmd_file(frommark, tomark, cmd, bang, extra)
  382.     MARK    frommark, tomark;
  383.     CMD    cmd;
  384.     int    bang;
  385.     char    *extra;
  386. {
  387. #ifndef CRUNCH
  388.     /* if we're given a new filename, use it as this file's name */
  389.     if (extra && *extra)
  390.     {
  391.         strcpy(origname, extra);
  392.     }
  393. #endif
  394.     if (cmd == CMD_FILE)
  395.     {
  396.         msg("\"%s\" %s%s %ld lines,  line %ld [%ld%%]",
  397.             *origname ? origname : "[NO FILE]",
  398.             tstflag(file, MODIFIED) ? "[MODIFIED]" : "",
  399.             tstflag(file, READONLY) ? "[READONLY]" : "",
  400.             nlines,
  401.             markline(frommark),
  402.             markline(frommark) * 100 / nlines);
  403.     }
  404.     else if (markline(frommark) == markline(tomark))
  405.     {
  406.         msg("%ld", markline(frommark));
  407.     }
  408.     else
  409.     {
  410.         msg("range \"%ld,%ld\" contains %ld lines",
  411.             markline(frommark),
  412.             markline(tomark),
  413.             markline(tomark) - markline(frommark) + 1L);
  414.     }
  415. }
  416.  
  417.  
  418. /*ARGSUSED*/
  419. void cmd_edit(frommark, tomark, cmd, bang, extra)
  420.     MARK    frommark, tomark;
  421.     CMD    cmd;
  422.     int    bang;
  423.     char    *extra;
  424. {
  425.     long    line = 1L;    /* might be set to prevline */
  426.  
  427.     /* Editing previous file?  Then start at previous line */
  428.     if (!strcmp(extra, prevorig))
  429.     {
  430.         line = prevline;
  431.     }
  432.  
  433. #ifndef CRUNCH
  434.     /* if we were given an explicit starting line, then start there */
  435.     if (*extra == '+')
  436.     {
  437.         for (extra++, line = 0L; *extra >= '0' && *extra <= '9'; extra++)
  438.         {
  439.             line *= 10L;
  440.             line += (*extra - '0');
  441.         }
  442.         while (isascii(*extra) && isspace(*extra))
  443.         {
  444.             extra++;
  445.         }
  446.     }
  447. #endif /* not CRUNCH */
  448.  
  449.     /* switch files */
  450.     if (tmpabort(bang))
  451.     {
  452.         tmpstart(extra);
  453.         if (line <= nlines && line >= 1L)
  454.         {
  455.             cursor = MARK_AT_LINE(line);
  456.         }
  457.     }
  458.     else
  459.     {
  460.         msg("Use edit! to abort changes, or w to save changes");
  461.  
  462.         /* so we can say ":e!#" next time... */
  463.         strcpy(prevorig, extra);
  464.         prevline = 1L;
  465.     }
  466. }
  467.  
  468. /* This code is also used for rewind -- GB */
  469.  
  470. /*ARGSUSED*/
  471. void cmd_next(frommark, tomark, cmd, bang, extra)
  472.     MARK    frommark, tomark;
  473.     CMD    cmd;
  474.     int    bang;
  475.     char    *extra;
  476. {
  477.     int    i, j;
  478.     char    *scan;
  479.     char    *build;
  480.  
  481.     /* if extra stuff given, use ":args" to define a new args list */
  482.     if (cmd == CMD_NEXT && extra && *extra)
  483.     {
  484.         cmd_args(frommark, tomark, cmd, bang, extra);
  485.     }
  486.  
  487.     /* move to the next arg */
  488.     if (cmd == CMD_NEXT)
  489.     {
  490.         i = argno + 1;
  491.     }
  492.     else if (cmd == CMD_PREVIOUS)
  493.     {
  494.         i = argno - 1;
  495.     }
  496.     else /* cmd == CMD_REWIND */
  497.     {
  498.         i = 0;
  499.     }    
  500.     if (i < 0 || i >= nargs)
  501.     {
  502.         msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more ");
  503.         return;
  504.     }
  505.  
  506.     /* find & isolate the name of the file to edit */
  507.     for (j = i, scan = args; j > 0; j--)
  508.     {
  509.         while(!isascii(*scan) || !isspace(*scan))
  510.         {
  511.             scan++;
  512.         }
  513.         while (isascii(*scan) && isspace(*scan))
  514.         {
  515.             scan++;
  516.         }
  517.     }
  518.     for (build = tmpblk.c; *scan && (!isascii(*scan) || !isspace(*scan)); )
  519.     {
  520.         *build++ = *scan++;
  521.     }
  522.     *build = '\0';
  523.  
  524.     /* switch to the next file */
  525.     if (tmpabort(bang))
  526.     {
  527.         tmpstart(tmpblk.c);
  528.         argno = i;
  529.     }
  530.     else
  531.     {
  532.         msg("Use :%s! to abort changes, or w to save changes",
  533.             cmd == CMD_NEXT ? "next" :
  534.             cmd == CMD_PREVIOUS ? "previous" :
  535.                     "rewind");
  536.     }
  537. }
  538.  
  539. /* also called from :wq -- always writes back in this case */
  540.  
  541. /*ARGSUSED*/
  542. void cmd_xit(frommark, tomark, cmd, bang, extra)
  543.     MARK    frommark, tomark;
  544.     CMD    cmd;
  545.     int    bang;
  546.     char    *extra;
  547. {
  548.     static long    whenwarned;    /* when the user was last warned of extra files */
  549.     int        oldflag;
  550.  
  551.     /* if there are more files to edit, then warn user */
  552.     if (argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT))
  553.     {
  554.         msg("More files to edit -- Use \":n\" to go to next file");
  555.         whenwarned = changes;
  556.         return;
  557.     }
  558.  
  559.     if (cmd == CMD_QUIT)
  560.     {
  561.         if (tmpabort(bang))
  562.         {
  563.             mode = MODE_QUIT;
  564.         }
  565.         else
  566.         {
  567.             msg("Use q! to abort changes, or wq to save changes");
  568.         }
  569.     }
  570.     else
  571.     {
  572.         /* else try to save this file */
  573.         oldflag = tstflag(file, MODIFIED);
  574.         if (cmd == CMD_WQUIT)
  575.             setflag(file, MODIFIED);
  576.         if (tmpend(bang))
  577.         {
  578.             mode = MODE_QUIT;
  579.         }
  580.         else
  581.         {
  582.             msg("Could not save file -- use quit! to abort changes, or w filename");
  583.         }
  584.         if (!oldflag)
  585.             clrflag(file, MODIFIED);
  586.     }
  587. }
  588.  
  589.  
  590. /*ARGSUSED*/
  591. void cmd_args(frommark, tomark, cmd, bang, extra)
  592.     MARK    frommark, tomark;
  593.     CMD    cmd;
  594.     int    bang;
  595.     char    *extra;
  596. {
  597.     char    *scan;
  598.     char    *eow;
  599.     int    col;
  600.     int    arg;
  601.     int    addcols;
  602.     int    scrolled = 0;
  603.  
  604.     /* if no extra names given, or just current name, then report the args
  605.      * we have now.
  606.      */
  607.     if (!extra || !*extra)
  608.     {
  609.         for (scan = args, col=arg=0; *scan; )
  610.         {
  611.             while (*scan && isascii(*scan) && isspace(*scan))
  612.                 scan++;
  613.             eow = scan;
  614.             while (*eow && (!isascii(*++eow) || !isspace(*eow)))
  615.                 ;
  616.             if (arg == argno)
  617.                 addcols = 2;
  618.             else
  619.                 addcols = 0;    
  620.             if (col+addcols+(int)(eow-scan)+1>=COLS)
  621.             {
  622.                 addch('\n');
  623.                 scrolled=1;
  624.                 col=0;
  625.             }
  626.             else if (arg)
  627.             {    qaddch(' ');
  628.                 col++;
  629.             }
  630.             if (arg == argno)
  631.                 qaddch('[');
  632.             while (scan < eow)
  633.             {    qaddch(*scan++);
  634.                 col++;
  635.             }
  636.             if (arg == argno)
  637.                 qaddch(']');    
  638.             arg++;    
  639.             col+=addcols;
  640.         }
  641.         /* write a trailing newline */
  642.         if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col)
  643.             addch('\n');
  644.         exrefresh();    
  645.     }
  646.     else /* new args list given */
  647.     {
  648.         strcpy(args, extra);
  649.         argno = -1; /* before the first, so :next will go to first */
  650.  
  651.         /* count the names */
  652.         for (nargs = 0, scan = args; *scan; nargs++)
  653.         {
  654.             while (*scan && (!isascii(*scan) || !isspace(*scan)))
  655.             {
  656.                 scan++;
  657.             }
  658.             while (isascii(*scan) && isspace(*scan))
  659.             {
  660.                 scan++;
  661.             }
  662.         }
  663.         msg("%d files to edit", nargs);
  664.     }
  665. }
  666.  
  667.  
  668. /*ARGSUSED*/
  669. void cmd_cd(frommark, tomark, cmd, bang, extra)
  670.     MARK    frommark, tomark;
  671.     CMD    cmd;
  672.     int    bang;
  673.     char    *extra;
  674. {
  675.     char    *getenv();
  676.  
  677.     /* default directory name is $HOME */
  678.     if (!*extra)
  679.     {
  680.         extra = getenv("HOME");
  681.         if (!extra)
  682.         {
  683.             msg("environment variable $HOME not set");
  684.             return;
  685.         }
  686.     }
  687.  
  688.     /* go to the directory */
  689.     if (chdir(extra) < 0)
  690.     {
  691.         perror(extra);
  692.     }
  693. }
  694.  
  695.  
  696. /*ARGSUSED*/
  697. void cmd_map(frommark, tomark, cmd, bang, extra)
  698.     MARK    frommark, tomark;
  699.     CMD    cmd;
  700.     int    bang;
  701.     char    *extra;
  702. {
  703.     char    *mapto;
  704.  
  705.     /* "map" with no extra will dump the map table contents */
  706.     if (!*extra)
  707.     {
  708.         dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD);
  709.     }
  710.     else
  711.     {
  712.         /* "extra" is key to map, followed my what it maps to */
  713.         for (mapto = extra; *mapto && *mapto != ' ' && *mapto!= '\t'; mapto++)
  714.         {
  715.         }
  716.         while (*mapto == ' ' || *mapto == '\t')
  717.         {
  718.             *mapto++ = '\0';
  719.         }
  720.  
  721.         mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0);
  722.     }
  723. }
  724.  
  725.  
  726. /*ARGSUSED*/
  727. void cmd_set(frommark, tomark, cmd, bang, extra)
  728.     MARK    frommark, tomark;
  729.     CMD    cmd;
  730.     int    bang;
  731.     char    *extra;
  732. {
  733.     if (!*extra)
  734.     {
  735.         dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */
  736.     }
  737.     else if (!strcmp(extra, "all"))
  738.     {
  739.         dumpopts(TRUE);    /* "TRUE" means "dump all" - even unset vars */
  740.     }
  741.     else
  742.     {
  743.         setopts(extra);
  744.  
  745.         /* That option may have affected the appearence of text */
  746.         changes++;
  747.     }
  748. }
  749.  
  750. /*ARGSUSED*/
  751. void cmd_tag(frommark, tomark, cmd, bang, extra)
  752.     MARK    frommark, tomark;
  753.     CMD    cmd;
  754.     int    bang;
  755.     char    *extra;
  756. {
  757.     char    *scan;    /* used to scan through the tmpblk.c */
  758.     char    *cmp;    /* char of tag name we're comparing, or NULL */
  759.     char    *end;    /* marks the end of chars in tmpblk.c */
  760.     int    fd;    /* file descriptor used to read the file */
  761. #ifndef NO_MAGIC
  762.     char    wasmagic; /* preserves the original state of o_magic */
  763. #endif
  764.     static char prevtag[30];
  765.  
  766.     /* if no tag is given, use the previous tag */
  767.     if (!extra || !*extra)
  768.     {
  769.         if (!*prevtag)
  770.         {
  771.             msg("No previous tag");
  772.             return;
  773.         }
  774.         extra = prevtag;
  775.     }
  776.     else
  777.     {
  778.         strncpy(prevtag, extra, sizeof prevtag);
  779.     }
  780.  
  781.     /* open the tags file */
  782.     fd = open(TAGS, O_RDONLY);
  783.     if (fd < 0)
  784.     {
  785.         msg("No tags file");
  786.         return;
  787.     }
  788.  
  789.     /* Hmmm... this would have been a lot easier with <stdio.h> */
  790.  
  791.     /* find the line with our tag in it */
  792.     for(scan = end = tmpblk.c, cmp = extra; ; scan++)
  793.     {
  794.         /* read a block, if necessary */
  795.         if (scan >= end)
  796.         {
  797.             end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE);
  798.             scan = tmpblk.c;
  799.             if (scan >= end)
  800.             {
  801.                 msg("tag \"%s\" not found", extra);
  802.                 close(fd);
  803.                 return;
  804.             }
  805.         }
  806.  
  807.         /* if we're comparing, compare... */
  808.         if (cmp)
  809.         {
  810.             /* matched??? wow! */
  811.             if (!*cmp && *scan == '\t')
  812.             {
  813.                 break;
  814.             }
  815.             if (*cmp++ != *scan)
  816.             {
  817.                 /* failed! skip to newline */
  818.                 cmp = (char *)0;
  819.             }
  820.         }
  821.  
  822.         /* if we're skipping to newline, do it fast! */
  823.         if (!cmp)
  824.         {
  825.             while (scan < end && *scan != '\n')
  826.             {
  827.                 scan++;
  828.             }
  829.             if (scan < end)
  830.             {
  831.                 cmp = extra;
  832.             }
  833.         }
  834.     }
  835.  
  836.     /* found it! get the rest of the line into memory */
  837.     for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; )
  838.     {
  839.         *cmp++ = *scan++;
  840.     }
  841.     if (scan == end)
  842.     {
  843.         tread(fd, cmp, BLKSIZE - (cmp - tmpblk.c));
  844.     }
  845.  
  846.     /* we can close the tags file now */
  847.     close(fd);
  848.  
  849.     /* extract the filename from the line, and edit the file */
  850.     for (cmp = tmpblk.c; *cmp != '\t'; cmp++)
  851.     {
  852.     }
  853.     *cmp++ = '\0';
  854.     if (strcmp(origname, tmpblk.c) != 0)
  855.     {
  856.         if (!tmpabort(bang))
  857.         {
  858.             msg("Use :tag! to abort changes, or :w to save changes");
  859.             return;
  860.         }
  861.         tmpstart(tmpblk.c);
  862.     }
  863.  
  864.     /* move to the desired line (or to line 1 if that fails) */
  865. #ifndef NO_MAGIC
  866.     wasmagic = *o_magic;
  867.     *o_magic = FALSE;
  868. #endif
  869.     cursor = MARK_FIRST;
  870.     linespec(cmp, &cursor);
  871.     if (cursor == MARK_UNSET)
  872.     {
  873.         cursor = MARK_FIRST;
  874.     }
  875. #ifndef NO_MAGIC
  876.     *o_magic = wasmagic;
  877. #endif
  878. }
  879.  
  880.  
  881.  
  882. /*ARGSUSED*/
  883. void cmd_visual(frommark, tomark, cmd, bang, extra)
  884.     MARK    frommark, tomark;
  885.     CMD    cmd;
  886.     int    bang;
  887.     char    *extra;
  888. {
  889.     mode = MODE_VI;
  890.     msg("");
  891. }
  892.  
  893.  
  894.  
  895.  
  896.  
  897. /* describe this version of the program */
  898. /*ARGSUSED*/
  899. void cmd_version(frommark, tomark, cmd, bang, extra)
  900.     MARK    frommark;
  901.     MARK    tomark;
  902.     CMD    cmd;
  903.     int    bang;
  904.     char    *extra;
  905. {
  906. #ifndef DATE
  907.     msg("%s", VERSION);
  908. #else
  909.     msg("%s  (%s)", VERSION, DATE);
  910. #endif
  911. #ifdef COMPILED_BY
  912.     msg("Compiled by %s", COMPILED_BY);
  913. #endif
  914. #ifdef CREDIT
  915.     msg("%s", CREDIT);
  916. #endif
  917. #ifdef COPYING
  918.     msg("%s", COPYING);
  919. #endif
  920. }
  921.  
  922.  
  923. #ifndef NO_MKEXRC
  924. /* make a .exrc file which describes the current configuration */
  925. /*ARGSUSED*/
  926. void cmd_mkexrc(frommark, tomark, cmd, bang, extra)
  927.     MARK    frommark;
  928.     MARK    tomark;
  929.     CMD    cmd;
  930.     int    bang;
  931.     char    *extra;
  932. {
  933.     int    fd;
  934.  
  935.     /* the default name for the .exrc file EXRC */
  936.     if (!*extra)
  937.     {
  938.         extra = EXRC;
  939.     }
  940.  
  941.     /* create the .exrc file */
  942.     fd = creat(extra, FILEPERMS);
  943.     if (fd < 0)
  944.     {
  945.         msg("Couldn't create a new \"%s\" file", extra);
  946.         return;
  947.     }
  948.  
  949.     /* save stuff */
  950.     savekeys(fd);
  951.     saveopts(fd);
  952. #ifndef NO_DIGRAPH
  953.     savedigs(fd);
  954. #endif
  955. #ifndef    NO_ABBR
  956.     saveabbr(fd);
  957. #endif
  958.  
  959.     /* close the file */
  960.     close(fd);
  961.     msg("Created a new \"%s\" file", extra);
  962. }
  963. #endif
  964.  
  965. #ifndef NO_DIGRAPH
  966. /*ARGSUSED*/
  967. void cmd_digraph(frommark, tomark, cmd, bang, extra)
  968.     MARK    frommark;
  969.     MARK    tomark;
  970.     CMD    cmd;
  971.     int    bang;
  972.     char    *extra;
  973. {
  974.     do_digraph(bang, extra);
  975. }
  976. #endif
  977.  
  978.  
  979. #ifndef NO_ERRLIST 
  980. static char    errfile[256];    /* the name of a file containing an error */
  981. static long    errline;    /* the line number for an error */
  982.  
  983. /* This static function tries to parse an error message.
  984.  *
  985.  * For most compilers, the first word is taken to be the name of the erroneous
  986.  * file, and the first number after that is taken to be the line number where
  987.  * the error was detected.  The description of the error follows, possibly
  988.  * preceded by an "error ... :" or "warning ... :" label which is skipped.
  989.  *
  990.  * For Coherent, error messages look like "line#: filename: message".
  991.  *
  992.  * For non-error lines, or unparsable error lines, this function returns NULL.
  993.  * Normally, though, it alters errfile and errline, and returns a pointer to
  994.  * the description.
  995.  */
  996. static char *parse_errmsg(text)
  997.     REG char    *text;
  998. {
  999.     REG char    *cpy;
  1000.     long        atol();
  1001. # if COHERENT || TOS /* any Mark Williams compiler */
  1002.     /* Get the line number.  If no line number, then ignore this line. */
  1003.     errline = atol(text);
  1004.     if (errline == 0L)
  1005.         return (char *)0;
  1006.  
  1007.     /* Skip to the start of the filename */
  1008.     while (*text && *text++ != ':')
  1009.     {
  1010.     }
  1011.     if (!*text++)
  1012.         return (char *)0;
  1013.  
  1014.     /* copy the filename to errfile */
  1015.     for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; )
  1016.     {
  1017.     }
  1018.     if (!*text++)
  1019.         return (char *)0;
  1020.     cpy[-1] = '\0';
  1021.  
  1022.     return text;
  1023. # else /* not a Mark Williams compiler */
  1024.     char        *errmsg;
  1025.  
  1026.     /* the error message is the whole line, by default */
  1027.     errmsg = text;
  1028.  
  1029.     /* skip leading garbage */
  1030.     while (*text && !(isascii(*text) && isalnum(*text)))
  1031.     {
  1032.         text++;
  1033.     }
  1034.  
  1035.     /* copy over the filename */
  1036.     cpy = errfile;
  1037.     while(isascii(*text) && isalnum(*text) || *text == '.')
  1038.     {
  1039.         *cpy++ = *text++;
  1040.     }
  1041.     *cpy = '\0';
  1042.  
  1043.     /* ignore the name "Error" and filenames that contain a '/' */
  1044.     if (*text == '/' || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0)
  1045.     {
  1046.         return (char *)0;
  1047.     }
  1048.  
  1049.     /* skip garbage between filename and line number */
  1050.     while (*text && !(isascii(*text) && isdigit(*text)))
  1051.     {
  1052.         text++;
  1053.     }
  1054.  
  1055.     /* if the number is part of a larger word, then ignore this line */
  1056.     if (*text && isascii(text[-1]) && isalpha(text[-1]))
  1057.     {
  1058.         return (char *)0;
  1059.     }
  1060.  
  1061.     /* get the error line */
  1062.     errline = 0L;
  1063.     while (isascii(*text) && isdigit(*text))
  1064.     {
  1065.         errline *= 10;
  1066.         errline += (*text - '0');
  1067.         text++;
  1068.     }
  1069.  
  1070.     /* any line which lacks a filename or line number should be ignored */
  1071.     if (!errfile[0] || !errline)
  1072.     {
  1073.         return (char *)0;
  1074.     }
  1075.  
  1076.     /* locate the beginning of the error description */
  1077.     while (*text && isascii(*text) && !isspace(*text))
  1078.     {
  1079.         text++;
  1080.     }
  1081.     while (*text)
  1082.     {
  1083. #  ifndef CRUNCH
  1084.         /* skip "error #:" and "warning #:" clauses */
  1085.         if (!strncmp(text + 1, "rror ", 5)
  1086.          || !strncmp(text + 1, "arning ", 7)
  1087.          || !strncmp(text + 1, "atal error", 10))
  1088.         {
  1089.             do
  1090.             {
  1091.                 text++;
  1092.             } while (*text && *text != ':');
  1093.             continue;
  1094.         }
  1095. #  endif
  1096.  
  1097.         /* anything other than whitespace or a colon is important */
  1098.         if (!isascii(*text) || (!isspace(*text) && *text != ':'))
  1099.         {
  1100.             errmsg = text;
  1101.             break;
  1102.         }
  1103.  
  1104.         /* else keep looking... */
  1105.         text++;
  1106.     }
  1107.  
  1108.     return errmsg;
  1109. # endif /* not COHERENT */
  1110. }
  1111.  
  1112. /*ARGSUSED*/
  1113. void cmd_errlist(frommark, tomark, cmd, bang, extra)
  1114.     MARK    frommark, tomark;
  1115.     CMD    cmd;
  1116.     int    bang;
  1117.     char    *extra;
  1118. {
  1119.     static long    endline;/* original number of lines in this file */
  1120.     static long    offset;    /* offset of the next line in the errlist file */
  1121.     static int    fd = -2;/* fd of the errlist file */
  1122.     int        i;
  1123.     char        *errmsg;
  1124.  
  1125.     /* if a new errlist file is named, open it */
  1126.     if (extra && extra[0])
  1127.     {
  1128.         /* close the old one */
  1129.         if (fd >= 0)
  1130.         {
  1131.             close(fd);
  1132.         }
  1133.  
  1134.         fd = open(extra, O_RDONLY);
  1135.         offset = 0L;
  1136.     }
  1137.     else if (fd < 0)
  1138.     {
  1139.         fd = open(ERRLIST, O_RDONLY);
  1140.         offset = 0L;
  1141.     }
  1142.  
  1143.     /* do we have an errlist file now? */
  1144.     if (fd < 0)
  1145.     {
  1146.         msg("There is no errlist file");
  1147.         beep();
  1148.         return;
  1149.     }
  1150.  
  1151.     /* find the next error message in the file */
  1152.     do
  1153.     {
  1154.         /* read the next line from the errlist */
  1155.         lseek(fd, offset, 0);
  1156.         if (tread(fd, tmpblk.c, (unsigned)BLKSIZE) <= 0)
  1157.         {
  1158.             msg("No more errors");
  1159.             beep();
  1160.             close(fd);
  1161.             return;
  1162.         }
  1163.         for (i = 0; tmpblk.c[i] != '\n'; i++)
  1164.         {
  1165.         }
  1166.         tmpblk.c[i++] = 0;
  1167.  
  1168.         /* look for an error message in the line */
  1169.         errmsg = parse_errmsg(tmpblk.c);
  1170.         if (!errmsg)
  1171.         {
  1172.             offset += i;
  1173.         }
  1174.  
  1175.     } while (!errmsg);
  1176.  
  1177.     /* switch to the file containing the error, if this isn't it */
  1178.     if (strcmp(origname, errfile))
  1179.     {
  1180.         if (!tmpabort(bang))
  1181.         {
  1182.             msg("Use :er! to abort changes, or :w to save changes");
  1183.             beep();
  1184.             return;
  1185.         }
  1186.         tmpstart(errfile);
  1187.         endline = nlines;
  1188.     }
  1189.     else if (endline == 0L)
  1190.     {
  1191.         endline = nlines;
  1192.     }
  1193.  
  1194.     /* go to the line where the error was detected */
  1195.     cursor = MARK_AT_LINE(errline + (nlines - endline));
  1196.     if (cursor > MARK_LAST)
  1197.     {
  1198.         cursor = MARK_LAST;
  1199.     }
  1200.     if (mode == MODE_VI)
  1201.     {
  1202.         redraw(cursor, FALSE);
  1203.     }
  1204.  
  1205.     /* display the error message */
  1206.     if (nlines > endline)
  1207.     {
  1208.         msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg);
  1209.     }
  1210.     else if (nlines < endline)
  1211.     {
  1212.         msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg);
  1213.     }
  1214.     else
  1215.     {
  1216.         msg("line %ld: %.65s", errline, errmsg);
  1217.     }
  1218.  
  1219.     /* remember where the NEXT error line will start */
  1220.     offset += i;
  1221. }
  1222.  
  1223.  
  1224. /*ARGSUSED*/
  1225. void cmd_make(frommark, tomark, cmd, bang, extra)
  1226.     MARK    frommark, tomark;
  1227.     CMD    cmd;
  1228.     int    bang;
  1229.     char    *extra;
  1230. {
  1231.     BLK    buf;
  1232.  
  1233.     /* if the file hasn't been saved, then complain unless ! */
  1234.     if (tstflag(file, MODIFIED) && !bang)
  1235.     {
  1236.         msg("\"%s\" not saved yet", origname);
  1237.         return;
  1238.     }
  1239.  
  1240.     /* build the command */
  1241.     sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST);
  1242.     qaddstr(buf.c);
  1243.     addch('\n');
  1244.  
  1245.     /* run the command, with curses temporarily disabled */
  1246.     suspend_curses();
  1247.     system(buf.c);
  1248.     resume_curses(mode == MODE_EX);
  1249.     if (mode == MODE_COLON)
  1250.         mode = MODE_VI;
  1251.  
  1252.     /* run the "errlist" command */
  1253.     cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST);
  1254. }
  1255. #endif
  1256.  
  1257.  
  1258. #ifndef NO_ABBR
  1259. /*ARGSUSED*/
  1260. void cmd_abbr(frommark, tomark, cmd, bang, extra)
  1261.     MARK    frommark, tomark;
  1262.     CMD    cmd;
  1263.     int    bang;
  1264.     char    *extra;
  1265. {
  1266.     do_abbr(extra);
  1267. }
  1268. #endif
  1269.