From Thomas Eberhardt <thomas@mathematik.uni-Bremen.de>
[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"
6e657cf2
AM
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
6e657cf2
AM
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 */
6e657cf2
AM
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 */
6e657cf2
AM
57#endif
58 char *eol;
15637ed4 59 char *subst; /* the substitution string */
6e657cf2 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
6e657cf2 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 = "\\~";
6e657cf2
AM
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("");
6e657cf2 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 */
08746e8b 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);
6e657cf2
AM
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);
6e657cf2 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);
6e657cf2 187 eol = line + strlen(line);
15637ed4
RG
188
189 /* if it contains the search pattern... */
6e657cf2
AM
190#ifdef REGEX
191 if (!regexec(re, line, SE_MAX, rm, 0))
192#else
15637ed4 193 if (regexec(re, line, TRUE))
6e657cf2 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 {
6e657cf2
AM
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 {
6e657cf2
AM
214#ifdef REGEX
215 for (conf = line; conf < startp; conf++)
216#else
15637ed4 217 for (conf = line; conf < re->startp[0]; conf++)
6e657cf2 218#endif
15637ed4
RG
219 addch(*conf);
220 standout();
6e657cf2
AM
221#ifdef REGEX
222 for ( ; conf < endp; conf++)
223#else
15637ed4 224 for ( ; conf < re->endp[0]; conf++)
6e657cf2 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 */
6e657cf2
AM
235#ifdef REGEX
236 while (s < endp)
237#else
15637ed4 238 while (s < re->endp[0])
6e657cf2 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 */
6e657cf2
AM
252#ifdef REGEX
253 while (s < startp)
254#else
15637ed4 255 while (s < re->startp[0])
6e657cf2 256#endif
15637ed4
RG
257 {
258 *d++ = *s++;
259 }
260
261 /* substitute for the matched part */
6e657cf2 262#ifdef REGEX
99668b43
AM
263 if (regsub(rm, startp, endp, subst, d) < 0)
264 return;
6e657cf2 265#else
99668b43
AM
266 if (regsub(re, subst, d) < 0)
267 return;
6e657cf2
AM
268#endif
269#ifdef REGEX
270 s = endp;
271#else
15637ed4 272 s = re->endp[0];
6e657cf2 273#endif
15637ed4
RG
274 d += strlen(d);
275
276Continue:
6e657cf2
AM
277;
278#ifndef REGEX
15637ed4
RG
279 /* if this regexp could conceivably match
280 * a zero-length string, then require at
281 * least 1 unmatched character between
282 * matches.
283 */
284 if (re->minlen == 0)
285 {
286 if (!*s)
287 break;
288 *d++ = *s++;
289 }
6e657cf2 290#endif
15637ed4 291
6e657cf2
AM
292 }
293#ifdef REGEX
294 while (*s && optg && rm[0].rm_eo && !regexec(re, s, SE_MAX, rm, REG_NOTBOL));
295 if (eol - s > 0 && !rm[0].rm_eo && optg) {
296 msg("RE error: line too long");
297 return;
298 }
299#else
300 while (optg && regexec(re, s, FALSE));
301#endif
15637ed4
RG
302
303 /* copy stuff from after the match */
304 while (*d++ = *s++) /* yes, ASSIGNMENT! */
305 {
306 }
307
308#ifndef CRUNCH
309 /* NOTE: since the substitution text is allowed to have ^Ms which are
310 * translated into newlines, it is possible that the number of lines
311 * in the file will increase after each line has been substituted.
312 * we need to adjust for this.
313 */
314 oldnlines = nlines;
315#endif
316
317 /* replace the old version of the line with the new */
318 d[-1] = '\n';
319 d[0] = '\0';
320 change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c);
321
322#ifndef CRUNCH
323 l += nlines - oldnlines;
324 tomark += MARK_AT_LINE(nlines - oldnlines);
325#endif
326
327 /* if supposed to print it, do so */
328 if (optp)
329 {
330 addstr(tmpblk.c);
331 exrefresh();
332 }
333
334 /* move the cursor to that line */
335 cursor = MARK_AT_LINE(l);
336 }
337 }
338 }
339
340 /* free the regexp */
6e657cf2 341#ifndef REGEX
08746e8b 342 _free_(re);
6e657cf2 343#endif
15637ed4
RG
344
345 /* if done from within a ":g" command, then finish silently */
346 if (doingglobal)
347 {
348 rptlines = chline;
349 rptlabel = "changed";
350 return;
351 }
352
353 /* Reporting */
354 if (chsub == 0)
355 {
356 msg("Substitution failed");
357 }
358 else if (chline >= *o_report)
359 {
360 msg("%ld substitutions on %ld lines", chsub, chline);
361 }
362 rptlines = 0L;
363}
364
365
366
367
368/*ARGSUSED*/
369void cmd_delete(frommark, tomark, cmd, bang, extra)
370 MARK frommark;
371 MARK tomark;
372 CMD cmd;
373 int bang;
374 char *extra;
375{
376 MARK curs2; /* an altered form of the cursor */
377
378 /* choose your cut buffer */
379 if (*extra == '"')
380 {
381 extra++;
382 }
383 if (*extra)
384 {
385 cutname(*extra);
386 }
387
388 /* make sure we're talking about whole lines here */
389 frommark = frommark & ~(BLKSIZE - 1);
390 tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
391
392 /* yank the lines */
393 cut(frommark, tomark);
394
395 /* if CMD_DELETE then delete the lines */
396 if (cmd != CMD_YANK)
397 {
398 curs2 = cursor;
399 ChangeText
400 {
401 /* delete the lines */
402 delete(frommark, tomark);
403 }
404 if (curs2 > tomark)
405 {
406 cursor = curs2 - tomark + frommark;
407 }
408 else if (curs2 > frommark)
409 {
410 cursor = frommark;
411 }
412 }
413}
414
415
416/*ARGSUSED*/
417void cmd_append(frommark, tomark, cmd, bang, extra)
418 MARK frommark;
419 MARK tomark;
420 CMD cmd;
421 int bang;
422 char *extra;
423{
424 long l; /* line counter */
425
426#ifndef CRUNCH
427 /* if '!' then toggle auto-indent */
428 if (bang)
429 {
430 *o_autoindent = !*o_autoindent;
431 }
432#endif
433
434 ChangeText
435 {
436 /* if we're doing a change, delete the old version */
437 if (cmd == CMD_CHANGE)
438 {
439 /* delete 'em */
440 cmd_delete(frommark, tomark, cmd, bang, extra);
441 }
442
443 /* new lines start at the frommark line, or after it */
444 l = markline(frommark);
445 if (cmd == CMD_APPEND)
446 {
447 l++;
448 }
449
450 /* get lines until no more lines, or "." line, and insert them */
451 while (vgets('\0', tmpblk.c, BLKSIZE) >= 0)
452 {
453 addch('\n');
454 if (!strcmp(tmpblk.c, "."))
455 {
456 break;
457 }
458
459 strcat(tmpblk.c, "\n");
460 add(MARK_AT_LINE(l), tmpblk.c);
461 l++;
462 }
463 }
464
465 /* on the odd chance that we're calling this from vi mode ... */
466 redraw(MARK_UNSET, FALSE);
467}
468
469
470/*ARGSUSED*/
471void cmd_put(frommark, tomark, cmd, bang, extra)
472 MARK frommark;
473 MARK tomark;
474 CMD cmd;
475 int bang;
476 char *extra;
477{
478 /* choose your cut buffer */
479 if (*extra == '"')
480 {
481 extra++;
482 }
483 if (*extra)
484 {
485 cutname(*extra);
486 }
487
488 /* paste it */
489 ChangeText
490 {
491 cursor = paste(frommark, TRUE, FALSE);
492 }
493}
494
495
496/*ARGSUSED*/
497void cmd_join(frommark, tomark, cmd, bang, extra)
498 MARK frommark;
499 MARK tomark;
500 CMD cmd;
501 int bang;
502 char *extra;
503{
504 long l;
505 char *scan;
506 int len; /* length of the new line */
507
508 /* if only one line is specified, assume the following one joins too */
509 if (markline(frommark) == nlines)
510 {
511 msg("Nothing to join with this line");
512 return;
513 }
514 if (markline(frommark) == markline(tomark))
515 {
516 tomark += BLKSIZE;
517 }
518
519 /* get the first line */
520 l = markline(frommark);
521 strcpy(tmpblk.c, fetchline(l));
522 len = strlen(tmpblk.c);
523
524 /* build the longer line */
525 while (++l <= markline(tomark))
526 {
527 /* get the next line */
528 scan = fetchline(l);
529
530 /* remove any leading whitespace */
531 while (*scan == '\t' || *scan == ' ')
532 {
533 scan++;
534 }
535
536 /* see if the line will fit */
08746e8b 537 if (strlen(scan) + len + 3 > (unsigned)BLKSIZE)
15637ed4
RG
538 {
539 msg("Can't join -- the resulting line would be too long");
540 return;
541 }
542
543 /* catenate it, with a space (or two) in between */
544 if (!bang)
545 {
546 if (len >= 1)
547 {
548 if (tmpblk.c[len - 1] == '.'
549 || tmpblk.c[len - 1] == '?'
550 || tmpblk.c[len - 1] == '!')
08746e8b
AM
551 {
552 tmpblk.c[len++] = ' ';
553 tmpblk.c[len++] = ' ';
554 }
555 else if (tmpblk.c[len - 1] != ' ')
15637ed4
RG
556 {
557 tmpblk.c[len++] = ' ';
558 }
15637ed4
RG
559 }
560 }
561 strcpy(tmpblk.c + len, scan);
562 len += strlen(scan);
563 }
564 tmpblk.c[len++] = '\n';
565 tmpblk.c[len] = '\0';
566
567 /* make the change */
568 ChangeText
569 {
570 frommark &= ~(BLKSIZE - 1);
571 tomark &= ~(BLKSIZE - 1);
572 tomark += BLKSIZE;
573 change(frommark, tomark, tmpblk.c);
574 }
575
576 /* Reporting... */
577 rptlines = markline(tomark) - markline(frommark) - 1L;
578 rptlabel = "joined";
579}
580
581
582
583/*ARGSUSED*/
584void cmd_shift(frommark, tomark, cmd, bang, extra)
585 MARK frommark;
586 MARK tomark;
587 CMD cmd;
588 int bang;
589 char *extra;
590{
591 long l; /* line number counter */
592 int oldidx; /* number of chars previously used for indent */
593 int newidx; /* number of chars in the new indent string */
594 int oldcol; /* previous indent amount */
595 int newcol; /* new indent amount */
596 char *text; /* pointer to the old line's text */
597
598 ChangeText
599 {
600 /* for each line to shift... */
601 for (l = markline(frommark); l <= markline(tomark); l++)
602 {
603 /* get the line - ignore empty lines unless ! mode */
604 text = fetchline(l);
605 if (!*text && !bang)
606 continue;
607
608 /* calc oldidx and oldcol */
609 for (oldidx = 0, oldcol = 0;
610 text[oldidx] == ' ' || text[oldidx] == '\t';
611 oldidx++)
612 {
613 if (text[oldidx] == ' ')
614 {
615 oldcol += 1;
616 }
617 else
618 {
619 oldcol += *o_tabstop - (oldcol % *o_tabstop);
620 }
621 }
622
623 /* calc newcol */
624 if (cmd == CMD_SHIFTR)
625 {
626 newcol = oldcol + (*o_shiftwidth & 0xff);
627 }
628 else
629 {
630 newcol = oldcol - (*o_shiftwidth & 0xff);
631 if (newcol < 0)
632 newcol = 0;
633 }
634
635 /* if no change, then skip to next line */
636 if (oldcol == newcol)
637 continue;
638
639 /* build a new indent string */
640 newidx = 0;
641 if (*o_autotab)
642 {
643 while (newcol >= *o_tabstop)
644 {
645 tmpblk.c[newidx++] = '\t';
646 newcol -= *o_tabstop;
647 }
648 }
649 while (newcol > 0)
650 {
651 tmpblk.c[newidx++] = ' ';
652 newcol--;
653 }
654 tmpblk.c[newidx] = '\0';
655
656 /* change the old indent string into the new */
657 change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c);
658 }
659 }
660
661 /* Reporting... */
662 rptlines = markline(tomark) - markline(frommark) + 1L;
663 if (cmd == CMD_SHIFTR)
664 {
665 rptlabel = ">ed";
666 }
667 else
668 {
669 rptlabel = "<ed";
670 }
671}
672
673
674/*ARGSUSED*/
675void cmd_read(frommark, tomark, cmd, bang, extra)
676 MARK frommark;
677 MARK tomark;
678 CMD cmd;
679 int bang;
680 char *extra;
681{
682 int fd, rc; /* used while reading from the file */
683 char *scan; /* used for finding NUL characters */
684 int hadnul; /* boolean: any NULs found? */
685 int addnl; /* boolean: forced to add newlines? */
686 int len; /* number of chars in current line */
687 long lines; /* number of lines in current block */
688 struct stat statb;
689
690 /* special case: if ":r !cmd" then let the filter() function do it */
691 if (extra[0] == '!')
692 {
693 filter(frommark, MARK_UNSET, extra + 1, TRUE);
694 return;
695 }
696
697 /* open the file */
698 fd = open(extra, O_RDONLY);
699 if (fd < 0)
700 {
701 msg("Can't open \"%s\"", extra);
702 return;
703 }
704
705#ifndef CRUNCH
706 if (stat(extra, &statb) < 0)
707 {
708 msg("Can't stat \"%s\"", extra);
709 }
710# if TOS
711 if (statb.st_mode & S_IJDIR)
712# else
713# if OSK
714 if (statb.st_mode & S_IFDIR)
715# else
716 if ((statb.st_mode & S_IFMT) != S_IFREG)
717# endif
718# endif
719 {
720 msg("\"%s\" is not a regular file", extra);
721 return;
722 }
723#endif /* not CRUNCH */
724
725 /* get blocks from the file, and add them */
726 ChangeText
727 {
728 /* insertion starts at the line following frommark */
729 tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L;
730 len = 0;
731 hadnul = addnl = FALSE;
732
733 /* add an extra newline, so partial lines at the end of
734 * the file don't trip us up
735 */
736 add(tomark, "\n");
737
738 /* for each chunk of text... */
739 while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
740 {
741 /* count newlines, convert NULs, etc. ... */
742 for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++)
743 {
744 /* break up long lines */
745 if (*scan != '\n' && len + 2 > BLKSIZE)
746 {
747 *scan = '\n';
748 addnl = TRUE;
749 }
750
751 /* protect against NUL chars in file */
752 if (!*scan)
753 {
754 *scan = 0x80;
755 hadnul = TRUE;
756 }
757
758 /* starting a new line? */
759 if (*scan == '\n')
760 {
761 /* reset length at newline */
762 len = 0;
763 lines++;
764 }
765 else
766 {
767 len++;
768 }
769 }
770
771 /* add the text */
772 *scan = '\0';
773 add(tomark, tmpblk.c);
774 tomark += MARK_AT_LINE(lines) + len - markidx(tomark);
775 }
776
777 /* if partial last line, then retain that first newline */
778 if (len > 0)
779 {
780 msg("Last line had no newline");
781 tomark += BLKSIZE; /* <- for the rptlines calc */
782 }
783 else /* delete that first newline */
784 {
785 delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L);
786 }
787 }
788
789 /* close the file */
790 close(fd);
791
792 /* Reporting... */
793 rptlines = markline(tomark) - markline(frommark);
794 rptlabel = "read";
795 if (mode == MODE_EX)
796 {
797 cursor = (tomark & ~BLKSIZE) - BLKSIZE;
798 }
799 else
800 {
801 cursor = frommark;
802 }
803
804 if (addnl)
805 msg("Newlines were added to break up long lines");
806 if (hadnul)
807 msg("NULs were converted to 0x80");
808}
809
810
811
812/*ARGSUSED*/
813void cmd_undo(frommark, tomark, cmd, bang, extra)
814 MARK frommark;
815 MARK tomark;
816 CMD cmd;
817 int bang;
818 char *extra;
819{
820 undo();
821}
822
823
824/* print the selected lines */
825/*ARGSUSED*/
826void cmd_print(frommark, tomark, cmd, bang, extra)
827 MARK frommark;
828 MARK tomark;
829 CMD cmd;
830 int bang;
831 char *extra;
832{
833 REG char *scan;
834 REG long l;
835 REG int col;
836
837 for (l = markline(frommark); l <= markline(tomark); l++)
838 {
839 /* display a line number, if CMD_NUMBER */
840 if (cmd == CMD_NUMBER)
841 {
842 sprintf(tmpblk.c, "%6ld ", l);
843 qaddstr(tmpblk.c);
844 col = 8;
845 }
846 else
847 {
848 col = 0;
849 }
850
851 /* get the next line & display it */
852 for (scan = fetchline(l); *scan; scan++)
853 {
854 /* expand tabs to the proper width */
855 if (*scan == '\t' && cmd != CMD_LIST)
856 {
857 do
858 {
859 qaddch(' ');
860 col++;
861 } while (col % *o_tabstop != 0);
862 }
08746e8b 863 else if (*scan >= 1 && *scan < ' ' || *scan == '\177')
15637ed4
RG
864 {
865 qaddch('^');
866 qaddch(*scan ^ 0x40);
867 col += 2;
868 }
869 else if ((*scan & 0x80) && cmd == CMD_LIST)
870 {
871 sprintf(tmpblk.c, "\\%03o", UCHAR(*scan));
872 qaddstr(tmpblk.c);
873 col += 4;
874 }
875 else
876 {
877 qaddch(*scan);
878 col++;
879 }
880
881 /* wrap at the edge of the screen */
882 if (!has_AM && col >= COLS)
883 {
884 addch('\n');
885 col -= COLS;
886 }
887 }
888 if (cmd == CMD_LIST)
889 {
890 qaddch('$');
891 }
892 addch('\n');
893 exrefresh();
894 }
08746e8b
AM
895
896 /* leave the cursor on the last line printed */
897 cursor = tomark;
15637ed4
RG
898}
899
900
901/* move or copy selected lines */
902/*ARGSUSED*/
903void cmd_move(frommark, tomark, cmd, bang, extra)
904 MARK frommark;
905 MARK tomark;
906 CMD cmd;
907 int bang;
908 char *extra;
909{
910 MARK destmark;
911
912 /* parse the destination linespec. No defaults. Line 0 is okay */
913 destmark = cursor;
914 if (!strcmp(extra, "0"))
915 {
916 destmark = 0L;
917 }
918 else if (linespec(extra, &destmark) == extra || !destmark)
919 {
920 msg("invalid destination address");
921 return;
922 }
923
924 /* flesh the marks out to encompass whole lines */
925 frommark &= ~(BLKSIZE - 1);
926 tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE;
927 destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE;
928
929 /* make sure the destination is valid */
930 if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark)
931 {
932 msg("invalid destination address");
933 }
934
935 /* Do it */
936 ChangeText
937 {
938 /* save the text to a cut buffer */
939 cutname('\0');
940 cut(frommark, tomark);
941
942 /* if we're not copying, delete the old text & adjust destmark */
943 if (cmd != CMD_COPY)
944 {
945 delete(frommark, tomark);
946 if (destmark >= frommark)
947 {
948 destmark -= (tomark - frommark);
949 }
950 }
951
952 /* add the new text */
953 paste(destmark, FALSE, FALSE);
954 }
955
956 /* move the cursor to the last line of the moved text */
957 cursor = destmark + (tomark - frommark) - BLKSIZE;
958 if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE)
959 {
960 cursor = MARK_LAST;
961 }
962
963 /* Reporting... */
964 rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" );
965}
966
967
968
969/* execute EX commands from a file */
970/*ARGSUSED*/
971void cmd_source(frommark, tomark, cmd, bang, extra)
972 MARK frommark;
973 MARK tomark;
974 CMD cmd;
975 int bang;
976 char *extra;
977{
978 /* must have a filename */
979 if (!*extra)
980 {
981 msg("\"source\" requires a filename");
982 return;
983 }
984
985 doexrc(extra);
986}
987
988
989#ifndef NO_AT
990/*ARGSUSED*/
991void cmd_at(frommark, tomark, cmd, bang, extra)
992 MARK frommark;
993 MARK tomark;
994 CMD cmd;
995 int bang;
996 char *extra;
997{
998 static nest = FALSE;
999 int result;
1000 char buf[MAXRCLEN];
1001
1002 /* don't allow nested macros */
1003 if (nest)
1004 {
1005 msg("@ macros can't be nested");
1006 return;
1007 }
1008 nest = TRUE;
1009
1010 /* require a buffer name */
1011 if (*extra == '"')
1012 extra++;
1013 if (!*extra || !isascii(*extra) ||!islower(*extra))
1014 {
1015 msg("@ requires a cut buffer name (a-z)");
1016 }
1017
1018 /* get the contents of the buffer */
1019 result = cb2str(*extra, buf, (unsigned)(sizeof buf));
1020 if (result <= 0)
1021 {
1022 msg("buffer \"%c is empty", *extra);
1023 }
1024 else if (result >= sizeof buf)
1025 {
1026 msg("buffer \"%c is too large to execute", *extra);
1027 }
1028 else
1029 {
1030 /* execute the contents of the buffer as ex commands */
1031 exstring(buf, result, '\\');
1032 }
1033
1034 nest = FALSE;
1035}
1036#endif