home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 2 / crawlyvol2.bin / apps / text_ed / elv16b2 / st / cmd2.c < prev    next >
C/C++ Source or Header  |  1992-05-13  |  18KB  |  951 lines

  1. /* cmd2.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 commands - mostly ones that change text */
  12.  
  13. #include "config.h"
  14. #include "ctype.h"
  15. #include "vi.h"
  16. #include "regexp.h"
  17. #if TOS
  18. # include <stat.h>
  19. #else
  20. # if OSK
  21. #  include "osk.h"
  22. # else
  23. #  if AMIGA
  24. #   include "amistat.h"
  25. #  else
  26. #   include <sys/stat.h>
  27. #  endif
  28. # endif
  29. #endif
  30.  
  31.  
  32. /*ARGSUSED*/
  33. void cmd_substitute(frommark, tomark, cmd, bang, extra)
  34.     MARK    frommark;
  35.     MARK    tomark;
  36.     CMD    cmd;
  37.     int    bang;
  38.     char    *extra;    /* rest of the command line */
  39. {
  40.     char    *line;    /* a line from the file */
  41.     regexp    *re;    /* the compiled search expression */
  42.     char    *subst;    /* the substitution string */
  43.     char    *opt;    /* substitution options */
  44.     long    l;    /* a line number */
  45.     char    *s, *d;    /* used during subtitutions */
  46.     char    *conf;    /* used during confirmation */
  47.     long    chline;    /* # of lines changed */
  48.     long    chsub;    /* # of substitutions made */
  49.     static    optp;    /* boolean option: print when done? */
  50.     static    optg;    /* boolean option: substitute globally in line? */
  51.     static    optc;    /* boolean option: confirm before subst? */
  52. #ifndef CRUNCH
  53.     long    oldnlines;
  54. #endif
  55.  
  56.  
  57.     /* for now, assume this will fail */
  58.     rptlines = -1L;
  59.  
  60.     if (cmd == CMD_SUBAGAIN)
  61.     {
  62. #ifndef NO_MAGIC
  63.         if (*o_magic)
  64.             subst = "~";
  65.         else
  66. #endif
  67.         subst = "\\~";
  68.         re = regcomp("");
  69.  
  70.         /* if visual "&", then turn off the "p" and "c" options */
  71.         if (bang)
  72.         {
  73.             optp = optc = FALSE;
  74.         }
  75.     }
  76.     else /* CMD_SUBSTITUTE */
  77.     {
  78.         /* make sure we got a search pattern */
  79.         if (*extra != '/' && *extra != '?')
  80.         {
  81.             msg("Usage: s/regular expression/new text/");
  82.             return;
  83.         }
  84.  
  85.         /* parse & compile the search pattern */
  86.         subst = parseptrn(extra);
  87.         re = regcomp(extra + 1);
  88.     }
  89.  
  90.     /* abort if RE error -- error message already given by regcomp() */
  91.     if (!re)
  92.     {
  93.         return;
  94.     }
  95.  
  96.     if (cmd == CMD_SUBSTITUTE)
  97.     {
  98.         /* parse the substitution string & find the option string */
  99.         for (opt = subst; *opt && *opt != *extra; opt++)
  100.         {
  101.             if (*opt == '\\' && opt[1])
  102.             {
  103.                 opt++;
  104.             }
  105.         }
  106.         if (*opt)
  107.         {
  108.             *opt++ = '\0';
  109.         }
  110.  
  111.         /* analyse the option string */
  112.         if (!*o_edcompatible)
  113.         {
  114.             optp = optg = optc = FALSE;
  115.         }
  116.         while (*opt)
  117.         {
  118.             switch (*opt++)
  119.             {
  120.               case 'p':    optp = !optp;    break;
  121.               case 'g':    optg = !optg;    break;
  122.               case 'c':    optc = !optc;    break;
  123.               case ' ':
  124.               case '\t':            break;
  125.               default:
  126.                 msg("Subst options are p, c, and g -- not %c", opt[-1]);
  127.                 return;
  128.             }
  129.         }
  130.     }
  131.  
  132.     /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
  133.     if ((optc || optp) && mode == MODE_VI)
  134.     {
  135.         addch('\n');
  136.         exrefresh();
  137.     }
  138.  
  139.     ChangeText
  140.     {
  141.         /* reset the change counters */
  142.         chline = chsub = 0L;
  143.  
  144.         /* for each selected line */
  145.         for (l = markline(frommark); l <= markline(tomark); l++)
  146.         {
  147.             /* fetch the line */
  148.             line = fetchline(l);
  149.  
  150.             /* if it contains the search pattern... */
  151.             if (regexec(re, line, TRUE))
  152.             {
  153.                 /* increment the line change counter */
  154.                 chline++;
  155.  
  156.                 /* initialize the pointers */
  157.                 s = line;
  158.                 d = tmpblk.c;
  159.  
  160.                 /* do once or globally ... */
  161.                 do
  162.                 {
  163. #ifndef CRUNCH
  164.                     /* confirm, if necessary */
  165.                     if (optc)
  166.                     {
  167.                         for (conf = line; conf < re->startp[0]; conf++)
  168.                             addch(*conf);
  169.                         standout();
  170.                         for ( ; conf < re->endp[0]; conf++)
  171.                             addch(*conf);
  172.                         standend();
  173.                         for (; *conf; conf++)
  174.                             addch(*conf);
  175.                         addch('\n');
  176.                         exrefresh();
  177.                         if (getkey(0) != 'y')
  178.                         {
  179.                             /* copy accross the original chars */
  180.                             while (s < re->endp[0])
  181.                                 *d++ = *s++;
  182.  
  183.                             /* skip to next match on this line, if any */
  184.                             goto Continue;
  185.                         }
  186.                     }
  187. #endif /* not CRUNCH */
  188.  
  189.                     /* increment the substitution change counter */
  190.                     chsub++;
  191.  
  192.                     /* copy stuff from before the match */
  193.                     while (s < re->startp[0])
  194.                     {
  195.                         *d++ = *s++;
  196.                     }
  197.  
  198.                     /* substitute for the matched part */
  199.                     regsub(re, subst, d);
  200.                     s = re->endp[0];
  201.                     d += strlen(d);
  202.  
  203. Continue:
  204.                     /* if this regexp could conceivably match
  205.                      * a zero-length string, then require at
  206.                      * least 1 unmatched character between
  207.                      * matches.
  208.                      */
  209.                     if (re->minlen == 0)
  210.                     {
  211.                         if (!*s)
  212.                             break;
  213.                         *d++ = *s++;
  214.                     }
  215.  
  216.                 } while (optg && regexec(re, s, FALSE));
  217.  
  218.                 /* copy stuff from after the match */
  219.                 while (*d++ = *s++)    /* yes, ASSIGNMENT! */
  220.                 {
  221.                 }
  222.  
  223. #ifndef CRUNCH
  224.                 /* NOTE: since the substitution text is allowed to have ^Ms which are
  225.                  * translated into newlines, it is possible that the number of lines
  226.                  * in the file will increase after each line has been substituted.
  227.                  * we need to adjust for this.
  228.                  */
  229.                 oldnlines = nlines;
  230. #endif
  231.  
  232.                 /* replace the old version of the line with the new */
  233.                 d[-1] = '\n';
  234.                 d[0] = '\0';
  235.                 change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
  236.  
  237. #ifndef CRUNCH
  238.                 l += nlines - oldnlines;
  239.                 tomark += MARK_AT_LINE(nlines - oldnlines);
  240. #endif
  241.  
  242.                 /* if supposed to print it, do so */
  243.                 if (optp)
  244.                 {
  245.                     addstr(tmpblk.c);
  246.                     exrefresh();
  247.                 }
  248.  
  249.                 /* move the cursor to that line */
  250.                 cursor = MARK_AT_LINE(l);
  251.             }
  252.         }
  253.     }
  254.  
  255.     /* free the regexp */
  256.     _free_(re);
  257.  
  258.     /* if done from within a ":g" command, then finish silently */
  259.     if (doingglobal)
  260.     {
  261.         rptlines = chline;
  262.         rptlabel = "changed";
  263.         return;
  264.     }
  265.  
  266.     /* Reporting */
  267.     if (chsub == 0)
  268.     {
  269.         msg("Substitution failed");
  270.     }
  271.     else if (chline >= *o_report)
  272.     {
  273.         msg("%ld substitutions on %ld lines", chsub, chline);
  274.     }
  275.     rptlines = 0L;
  276. }
  277.  
  278.  
  279.  
  280.  
  281. /*ARGSUSED*/
  282. void cmd_delete(frommark, tomark, cmd, bang, extra)
  283.     MARK    frommark;
  284.     MARK    tomark;
  285.     CMD    cmd;
  286.     int    bang;
  287.     char    *extra;
  288. {
  289.     MARK    curs2;    /* an altered form of the cursor */
  290.  
  291.     /* choose your cut buffer */
  292.     if (*extra == '"')
  293.     {
  294.         extra++;
  295.     }
  296.     if (*extra)
  297.     {
  298.         cutname(*extra);
  299.     }
  300.  
  301.     /* make sure we're talking about whole lines here */
  302.     frommark = frommark & ~(BLKSIZE - 1);
  303.     tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
  304.  
  305.     /* yank the lines */
  306.     cut(frommark, tomark);
  307.  
  308.     /* if CMD_DELETE then delete the lines */
  309.     if (cmd != CMD_YANK)
  310.     {
  311.         curs2 = cursor;
  312.         ChangeText
  313.         {
  314.             /* delete the lines */
  315.             delete(frommark, tomark);
  316.         }
  317.         if (curs2 > tomark)
  318.         {
  319.             cursor = curs2 - tomark + frommark;
  320.         }
  321.         else if (curs2 > frommark)
  322.         {
  323.             cursor = frommark;
  324.         }
  325.     }
  326. }
  327.  
  328.  
  329. /*ARGSUSED*/
  330. void cmd_append(frommark, tomark, cmd, bang, extra)
  331.     MARK    frommark;
  332.     MARK    tomark;
  333.     CMD    cmd;
  334.     int    bang;
  335.     char    *extra;
  336. {
  337.     long    l;    /* line counter */
  338.  
  339. #ifndef CRUNCH
  340.     /* if '!' then toggle auto-indent */
  341.     if (bang)
  342.     {
  343.         *o_autoindent = !*o_autoindent;
  344.     }
  345. #endif
  346.  
  347.     ChangeText
  348.     {
  349.         /* if we're doing a change, delete the old version */
  350.         if (cmd == CMD_CHANGE)
  351.         {
  352.             /* delete 'em */
  353.             cmd_delete(frommark, tomark, cmd, bang, extra);
  354.         }
  355.  
  356.         /* new lines start at the frommark line, or after it */
  357.         l = markline(frommark);
  358.         if (cmd == CMD_APPEND)
  359.         {
  360.              l++;
  361.         }
  362.  
  363.         /* get lines until no more lines, or "." line, and insert them */
  364.         while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
  365.         {
  366.             addch('\n');
  367.             if (!strcmp(tmpblk.c, "."))
  368.             {
  369.                 break;
  370.             }
  371.  
  372.             strcat(tmpblk.c, "\n");
  373.             add(MARK_AT_LINE(l), tmpblk.c);
  374.             l++;
  375.         }
  376.     }
  377.  
  378.     /* on the odd chance that we're calling this from vi mode ... */
  379.     redraw(MARK_UNSET, FALSE);
  380. }
  381.  
  382.  
  383. /*ARGSUSED*/
  384. void cmd_put(frommark, tomark, cmd, bang, extra)
  385.     MARK    frommark;
  386.     MARK    tomark;
  387.     CMD    cmd;
  388.     int    bang;
  389.     char    *extra;
  390. {
  391.     /* choose your cut buffer */
  392.     if (*extra == '"')
  393.     {
  394.         extra++;
  395.     }
  396.     if (*extra)
  397.     {
  398.         cutname(*extra);
  399.     }
  400.  
  401.     /* paste it */
  402.     ChangeText
  403.     {
  404.         cursor = paste(frommark, TRUE, FALSE);
  405.     }
  406. }
  407.  
  408.  
  409. /*ARGSUSED*/
  410. void cmd_join(frommark, tomark, cmd, bang, extra)
  411.     MARK    frommark;
  412.     MARK    tomark;
  413.     CMD    cmd;
  414.     int    bang;
  415.     char    *extra;
  416. {
  417.     long    l;
  418.     char    *scan;
  419.     int    len;    /* length of the new line */
  420.  
  421.     /* if only one line is specified, assume the following one joins too */
  422.     if (markline(frommark) == nlines)
  423.     {
  424.         msg("Nothing to join with this line");
  425.         return;
  426.     }
  427.     if (markline(frommark) == markline(tomark))
  428.     {
  429.         tomark += BLKSIZE;
  430.     }
  431.  
  432.     /* get the first line */
  433.     l = markline(frommark);
  434.     strcpy(tmpblk.c, fetchline(l));
  435.     len = strlen(tmpblk.c);
  436.  
  437.     /* build the longer line */
  438.     while (++l <= markline(tomark))
  439.     {
  440.         /* get the next line */
  441.         scan = fetchline(l);
  442.  
  443.         /* remove any leading whitespace */
  444.         while (*scan == '\t' || *scan == ' ')
  445.         {
  446.             scan++;
  447.         }
  448.  
  449.         /* see if the line will fit */
  450.         if (strlen(scan) + len + 3 > BLKSIZE)
  451.         {
  452.             msg("Can't join -- the resulting line would be too long");
  453.             return;
  454.         }
  455.  
  456.         /* catenate it, with a space (or two) in between */
  457.         if (!bang)
  458.         {
  459.             if (len >= 1)
  460.             {
  461.                 if (tmpblk.c[len - 1] == '.'
  462.                  || tmpblk.c[len - 1] == '?'
  463.                  || tmpblk.c[len - 1] == '!')
  464.                 {
  465.                      tmpblk.c[len++] = ' ';
  466.                      tmpblk.c[len++] = ' ';
  467.                 }
  468.                 else if (tmpblk.c[len - 1] != ' ')
  469.                 {
  470.                      tmpblk.c[len++] = ' ';
  471.                 }
  472.             }
  473.         }
  474.         strcpy(tmpblk.c + len, scan);
  475.         len += strlen(scan);
  476.     }
  477.     tmpblk.c[len++] = '\n';
  478.     tmpblk.c[len] = '\0';
  479.  
  480.     /* make the change */
  481.     ChangeText
  482.     {
  483.         frommark &= ~(BLKSIZE - 1);
  484.         tomark &= ~(BLKSIZE - 1);
  485.         tomark += BLKSIZE;
  486.         change(frommark, tomark, tmpblk.c);
  487.     }
  488.  
  489.     /* Reporting... */
  490.     rptlines = markline(tomark) - markline(frommark) - 1L;
  491.     rptlabel = "joined";
  492. }
  493.  
  494.  
  495.  
  496. /*ARGSUSED*/
  497. void cmd_shift(frommark, tomark, cmd, bang, extra)
  498.     MARK    frommark;
  499.     MARK    tomark;
  500.     CMD    cmd;
  501.     int    bang;
  502.     char    *extra;
  503. {
  504.     long    l;    /* line number counter */
  505.     int    oldidx;    /* number of chars previously used for indent */
  506.     int    newidx;    /* number of chars in the new indent string */
  507.     int    oldcol;    /* previous indent amount */
  508.     int    newcol;    /* new indent amount */
  509.     char    *text;    /* pointer to the old line's text */
  510.  
  511.     ChangeText
  512.     {
  513.         /* for each line to shift... */
  514.         for (l = markline(frommark); l <= markline(tomark); l++)
  515.         {
  516.             /* get the line - ignore empty lines unless ! mode */
  517.             text = fetchline(l);
  518.             if (!*text && !bang)
  519.                 continue;
  520.  
  521.             /* calc oldidx and oldcol */
  522.             for (oldidx = 0, oldcol = 0;
  523.                  text[oldidx] == ' ' || text[oldidx] == '\t';
  524.                  oldidx++)
  525.             {
  526.                 if (text[oldidx] == ' ')
  527.                 {
  528.                     oldcol += 1;
  529.                 }
  530.                 else
  531.                 {
  532.                     oldcol += *o_tabstop - (oldcol % *o_tabstop);
  533.                 }
  534.             }
  535.  
  536.             /* calc newcol */
  537.             if (cmd == CMD_SHIFTR)
  538.             {
  539.                 newcol = oldcol + (*o_shiftwidth & 0xff);
  540.             }
  541.             else
  542.             {
  543.                 newcol = oldcol - (*o_shiftwidth & 0xff);
  544.                 if (newcol < 0)
  545.                     newcol = 0;
  546.             }
  547.  
  548.             /* if no change, then skip to next line */
  549.             if (oldcol == newcol)
  550.                 continue;
  551.  
  552.             /* build a new indent string */
  553.             newidx = 0;
  554.             if (*o_autotab)
  555.             {
  556.                 while (newcol >= *o_tabstop)
  557.                 {
  558.                     tmpblk.c[newidx++] = '\t';
  559.                     newcol -= *o_tabstop;
  560.                 }
  561.             }
  562.             while (newcol > 0)
  563.             {
  564.                 tmpblk.c[newidx++] = ' ';
  565.                 newcol--;
  566.             }
  567.             tmpblk.c[newidx] = '\0';
  568.  
  569.             /* change the old indent string into the new */
  570.             change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
  571.         }
  572.     }
  573.  
  574.     /* Reporting... */
  575.     rptlines = markline(tomark) - markline(frommark) + 1L;
  576.     if (cmd == CMD_SHIFTR)
  577.     {
  578.         rptlabel = ">ed";
  579.     }
  580.     else
  581.     {
  582.         rptlabel = "<ed";
  583.     }
  584. }
  585.  
  586.  
  587. /*ARGSUSED*/
  588. void cmd_read(frommark, tomark, cmd, bang, extra)
  589.     MARK    frommark;
  590.     MARK    tomark;
  591.     CMD    cmd;
  592.     int    bang;
  593.     char    *extra;
  594. {
  595.     int    fd, rc;    /* used while reading from the file */
  596.     char    *scan;    /* used for finding NUL characters */
  597.     int    hadnul;    /* boolean: any NULs found? */
  598.     int    addnl;    /* boolean: forced to add newlines? */
  599.     int    len;    /* number of chars in current line */
  600.     long    lines;    /* number of lines in current block */
  601.     struct stat statb;
  602.  
  603.     /* special case: if ":r !cmd" then let the filter() function do it */
  604.     if (extra[0] == '!')
  605.     {
  606.         filter(frommark, MARK_UNSET, extra + 1, TRUE);
  607.         return;
  608.     }
  609.  
  610.     /* open the file */
  611.     fd = open(extra, O_RDONLY);
  612.     if (fd < 0)
  613.     {
  614.         msg("Can't open \"%s\"", extra);
  615.         return;
  616.     }
  617.  
  618. #ifndef CRUNCH
  619.     if (stat(extra, &statb) < 0)
  620.     {
  621.         msg("Can't stat \"%s\"", extra);
  622.     }
  623. # if TOS
  624.     if (statb.st_mode & S_IJDIR)
  625. # else
  626. #  if OSK
  627.     if (statb.st_mode & S_IFDIR)
  628. #  else
  629.     if ((statb.st_mode & S_IFMT) != S_IFREG)
  630. #  endif
  631. # endif
  632.     {
  633.         msg("\"%s\" is not a regular file", extra);
  634.         return;
  635.     }
  636. #endif /* not CRUNCH */
  637.  
  638.     /* get blocks from the file, and add them */
  639.     ChangeText
  640.     {
  641.         /* insertion starts at the line following frommark */
  642.         tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
  643.         len = 0;
  644.         hadnul = addnl = FALSE;
  645.  
  646.         /* add an extra newline, so partial lines at the end of
  647.          * the file don't trip us up
  648.          */
  649.         add(tomark, "\n");
  650.  
  651.         /* for each chunk of text... */
  652. #if MINT
  653.         while ((rc = (*o_crlf ? tread : read)(fd, tmpblk.c, BLKSIZE - 1)) > 0)
  654. #else
  655.         while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
  656. #endif
  657.         {
  658.             /* count newlines, convert NULs, etc. ... */
  659.             for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
  660.             {
  661.                 /* break up long lines */
  662.                 if (*scan != '\n' && len + 2 > BLKSIZE)
  663.                 {
  664.                     *scan = '\n';
  665.                     addnl = TRUE;
  666.                 }
  667.  
  668.                 /* protect against NUL chars in file */
  669.                 if (!*scan)
  670.                 {
  671.                     *scan = 0x80;
  672.                     hadnul = TRUE;
  673.                 }
  674.  
  675.                 /* starting a new line? */
  676.                 if (*scan == '\n')
  677.                 {
  678.                     /* reset length at newline */
  679.                     len = 0;
  680.                     lines++;
  681.                 }
  682.                 else
  683.                 {
  684.                     len++;
  685.                 }
  686.             }
  687.  
  688.             /* add the text */
  689.             *scan = '\0';
  690.             add(tomark, tmpblk.c);
  691.             tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
  692.         }
  693.  
  694.         /* if partial last line, then retain that first newline */
  695.         if (len > 0)
  696.         {
  697.             msg("Last line had no newline");
  698.             tomark += BLKSIZE; /* <- for the rptlines calc */
  699.         }
  700.         else /* delete that first newline */
  701.         {
  702.             delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
  703.         }
  704.     }
  705.  
  706.     /* close the file */
  707.     close(fd);
  708.  
  709.     /* Reporting... */
  710.     rptlines = markline(tomark) - markline(frommark);
  711.     rptlabel = "read";
  712.     if (mode == MODE_EX)
  713.     {
  714.         cursor = (tomark & ~BLKSIZE) - BLKSIZE;
  715.     }
  716.     else
  717.     {
  718.         cursor = frommark;
  719.     }
  720.  
  721.     if (addnl)
  722.         msg("Newlines were added to break up long lines");
  723.     if (hadnul)
  724.         msg("NULs were converted to 0x80");
  725. }
  726.  
  727.  
  728.  
  729. /*ARGSUSED*/
  730. void cmd_undo(frommark, tomark, cmd, bang, extra)
  731.     MARK    frommark;
  732.     MARK    tomark;
  733.     CMD    cmd;
  734.     int    bang;
  735.     char    *extra;
  736. {
  737.     undo();
  738. }
  739.  
  740.  
  741. /* print the selected lines */
  742. /*ARGSUSED*/
  743. void cmd_print(frommark, tomark, cmd, bang, extra)
  744.     MARK    frommark;
  745.     MARK    tomark;
  746.     CMD    cmd;
  747.     int    bang;
  748.     char    *extra;
  749. {
  750.     REG char    *scan;
  751.     REG long    l;
  752.     REG int        col;
  753.  
  754.     for (l = markline(frommark); l <= markline(tomark); l++)
  755.     {
  756.         /* display a line number, if CMD_NUMBER */
  757.         if (cmd == CMD_NUMBER)
  758.         {
  759.             sprintf(tmpblk.c, "%6ld  ", l);
  760.             qaddstr(tmpblk.c);
  761.             col = 8;
  762.         }
  763.         else
  764.         {
  765.             col = 0;
  766.         }
  767.  
  768.         /* get the next line & display it */
  769.         for (scan = fetchline(l); *scan; scan++)
  770.         {
  771.             /* expand tabs to the proper width */
  772.             if (*scan == '\t' && cmd != CMD_LIST)
  773.             {
  774.                 do
  775.                 {
  776.                     qaddch(' ');
  777.                     col++;
  778.                 } while (col % *o_tabstop != 0);
  779.             }
  780.             else if (*scan > 0 && *scan < ' ' || *scan == '\177')
  781.             {
  782.                 qaddch('^');
  783.                 qaddch(*scan ^ 0x40);
  784.                 col += 2;
  785.             }
  786.             else if ((*scan & 0x80) && cmd == CMD_LIST)
  787.             {
  788.                 sprintf(tmpblk.c, "\\%03o", UCHAR(*scan));
  789.                 qaddstr(tmpblk.c);
  790.                 col += 4;
  791.             }
  792.             else
  793.             {
  794.                 qaddch(*scan);
  795.                 col++;
  796.             }
  797.  
  798.             /* wrap at the edge of the screen */
  799.             if (!has_AM && col >= COLS)
  800.             {
  801.                 addch('\n');
  802.                 col -= COLS;
  803.             }
  804.         }
  805.         if (cmd == CMD_LIST)
  806.         {
  807.             qaddch('$');
  808.         }
  809.         addch('\n');
  810.         exrefresh();
  811.     }
  812. }
  813.  
  814.  
  815. /* move or copy selected lines */
  816. /*ARGSUSED*/
  817. void cmd_move(frommark, tomark, cmd, bang, extra)
  818.     MARK    frommark;
  819.     MARK    tomark;
  820.     CMD    cmd;
  821.     int    bang;
  822.     char    *extra;
  823. {
  824.     MARK    destmark;
  825.  
  826.     /* parse the destination linespec.  No defaults.  Line 0 is okay */
  827.     destmark = cursor;
  828.     if (!strcmp(extra, "0"))
  829.     {
  830.         destmark = 0L;
  831.     }
  832.     else if (linespec(extra, &destmark) == extra || !destmark)
  833.     {
  834.         msg("invalid destination address");
  835.         return;
  836.     }
  837.  
  838.     /* flesh the marks out to encompass whole lines */
  839.     frommark &= ~(BLKSIZE - 1);
  840.     tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
  841.     destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
  842.  
  843.     /* make sure the destination is valid */
  844.     if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
  845.     {
  846.         msg("invalid destination address");
  847.     }
  848.  
  849.     /* Do it */
  850.     ChangeText
  851.     {
  852.         /* save the text to a cut buffer */
  853.         cutname('\0');
  854.         cut(frommark, tomark);
  855.  
  856.         /* if we're not copying, delete the old text & adjust destmark */
  857.         if (cmd != CMD_COPY)
  858.         {
  859.             delete(frommark, tomark);
  860.             if (destmark >= frommark)
  861.             {
  862.                 destmark -= (tomark - frommark);
  863.             }
  864.         }
  865.  
  866.         /* add the new text */
  867.         paste(destmark, FALSE, FALSE);
  868.     }
  869.  
  870.     /* move the cursor to the last line of the moved text */
  871.     cursor = destmark + (tomark - frommark) - BLKSIZE;
  872.     if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
  873.     {
  874.         cursor = MARK_LAST;
  875.     }
  876.  
  877.     /* Reporting... */
  878.     rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
  879. }
  880.  
  881.  
  882.  
  883. /* execute EX commands from a file */
  884. /*ARGSUSED*/
  885. void cmd_source(frommark, tomark, cmd, bang, extra)
  886.     MARK    frommark;
  887.     MARK    tomark;
  888.     CMD    cmd;
  889.     int    bang;
  890.     char    *extra;
  891. {
  892.     /* must have a filename */
  893.     if (!*extra)
  894.     {
  895.         msg("\"source\" requires a filename");
  896.         return;
  897.     }
  898.  
  899.     doexrc(extra);
  900. }
  901.  
  902.  
  903. #ifndef NO_AT
  904. /*ARGSUSED*/
  905. void cmd_at(frommark, tomark, cmd, bang, extra)
  906.     MARK    frommark;
  907.     MARK    tomark;
  908.     CMD    cmd;
  909.     int    bang;
  910.     char    *extra;
  911. {
  912.     static    nest = FALSE;
  913.     int    result;
  914.     char    buf[MAXRCLEN];
  915.  
  916.     /* don't allow nested macros */
  917.     if (nest)
  918.     {
  919.         msg("@ macros can't be nested");
  920.         return;
  921.     }
  922.     nest = TRUE;
  923.  
  924.     /* require a buffer name */
  925.     if (*extra == '"')
  926.         extra++;
  927.     if (!*extra || !isascii(*extra) ||!islower(*extra))
  928.     {
  929.         msg("@ requires a cut buffer name (a-z)");
  930.     }
  931.  
  932.     /* get the contents of the buffer */
  933.     result = cb2str(*extra, buf, (unsigned)(sizeof buf));
  934.     if (result <= 0)
  935.     {
  936.         msg("buffer \"%c is empty", *extra);
  937.     }
  938.     else if (result >= sizeof buf)
  939.     {
  940.         msg("buffer \"%c is too large to execute", *extra);
  941.     }
  942.     else
  943.     {
  944.         /* execute the contents of the buffer as ex commands */
  945.         exstring(buf, result, '\\');
  946.     }
  947.  
  948.     nest = FALSE;
  949. }
  950. #endif
  951.