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