5 * 14407 SW Teal Blvd. #C
11 /* This file contains some of the commands - mostly ones that change text */
26 # include <sys/stat.h>
33 void cmd_substitute(frommark
, tomark
, cmd
, bang
, extra
)
38 char *extra
; /* rest of the command line */
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? */
57 /* for now, assume this will fail */
60 if (cmd
== CMD_SUBAGAIN
)
70 /* if visual "&", then turn off the "p" and "c" options */
76 else /* CMD_SUBSTITUTE */
78 /* make sure we got a search pattern */
79 if (*extra
!= '/' && *extra
!= '?')
81 msg("Usage: s/regular expression/new text/");
85 /* parse & compile the search pattern */
86 subst
= parseptrn(extra
);
87 re
= regcomp(extra
+ 1);
90 /* abort if RE error -- error message already given by regcomp() */
96 if (cmd
== CMD_SUBSTITUTE
)
98 /* parse the substitution string & find the option string */
99 for (opt
= subst
; *opt
&& *opt
!= *extra
; opt
++)
101 if (*opt
== '\\' && opt
[1])
111 /* analyse the option string */
112 if (!*o_edcompatible
)
114 optp
= optg
= optc
= FALSE
;
120 case 'p': optp
= !optp
; break;
121 case 'g': optg
= !optg
; break;
122 case 'c': optc
= !optc
; break;
126 msg("Subst options are p, c, and g -- not %c", opt
[-1]);
132 /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
133 if ((optc
|| optp
) && mode
== MODE_VI
)
141 /* reset the change counters */
144 /* for each selected line */
145 for (l
= markline(frommark
); l
<= markline(tomark
); l
++)
150 /* if it contains the search pattern... */
151 if (regexec(re
, line
, TRUE
))
153 /* increment the line change counter */
156 /* initialize the pointers */
160 /* do once or globally ... */
164 /* confirm, if necessary */
167 for (conf
= line
; conf
< re
->startp
[0]; conf
++)
170 for ( ; conf
< re
->endp
[0]; conf
++)
173 for (; *conf
; conf
++)
177 if (getkey(0) != 'y')
179 /* copy accross the original chars */
180 while (s
< re
->endp
[0])
183 /* skip to next match on this line, if any */
187 #endif /* not CRUNCH */
189 /* increment the substitution change counter */
192 /* copy stuff from before the match */
193 while (s
< re
->startp
[0])
198 /* substitute for the matched part */
199 regsub(re
, subst
, d
);
204 /* if this regexp could conceivably match
205 * a zero-length string, then require at
206 * least 1 unmatched character between
216 } while (optg
&& regexec(re
, s
, FALSE
));
218 /* copy stuff from after the match */
219 while (*d
++ = *s
++) /* yes, ASSIGNMENT! */
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.
232 /* replace the old version of the line with the new */
235 change(MARK_AT_LINE(l
), MARK_AT_LINE(l
+ 1), tmpblk
.c
);
238 l
+= nlines
- oldnlines
;
239 tomark
+= MARK_AT_LINE(nlines
- oldnlines
);
242 /* if supposed to print it, do so */
249 /* move the cursor to that line */
250 cursor
= MARK_AT_LINE(l
);
255 /* free the regexp */
258 /* if done from within a ":g" command, then finish silently */
262 rptlabel
= "changed";
269 msg("Substitution failed");
271 else if (chline
>= *o_report
)
273 msg("%ld substitutions on %ld lines", chsub
, chline
);
282 void cmd_delete(frommark
, tomark
, cmd
, bang
, extra
)
289 MARK curs2
; /* an altered form of the cursor */
291 /* choose your cut buffer */
301 /* make sure we're talking about whole lines here */
302 frommark
= frommark
& ~(BLKSIZE
- 1);
303 tomark
= (tomark
& ~(BLKSIZE
- 1)) + BLKSIZE
;
306 cut(frommark
, tomark
);
308 /* if CMD_DELETE then delete the lines */
314 /* delete the lines */
315 delete(frommark
, tomark
);
319 cursor
= curs2
- tomark
+ frommark
;
321 else if (curs2
> frommark
)
330 void cmd_append(frommark
, tomark
, cmd
, bang
, extra
)
337 long l
; /* line counter */
340 /* if '!' then toggle auto-indent */
343 *o_autoindent
= !*o_autoindent
;
349 /* if we're doing a change, delete the old version */
350 if (cmd
== CMD_CHANGE
)
353 cmd_delete(frommark
, tomark
, cmd
, bang
, extra
);
356 /* new lines start at the frommark line, or after it */
357 l
= markline(frommark
);
358 if (cmd
== CMD_APPEND
)
363 /* get lines until no more lines, or "." line, and insert them */
364 while (vgets('\0', tmpblk
.c
, BLKSIZE
) >= 0)
367 if (!strcmp(tmpblk
.c
, "."))
372 strcat(tmpblk
.c
, "\n");
373 add(MARK_AT_LINE(l
), tmpblk
.c
);
378 /* on the odd chance that we're calling this from vi mode ... */
379 redraw(MARK_UNSET
, FALSE
);
384 void cmd_put(frommark
, tomark
, cmd
, bang
, extra
)
391 /* choose your cut buffer */
404 cursor
= paste(frommark
, TRUE
, FALSE
);
410 void cmd_join(frommark
, tomark
, cmd
, bang
, extra
)
419 int len
; /* length of the new line */
421 /* if only one line is specified, assume the following one joins too */
422 if (markline(frommark
) == nlines
)
424 msg("Nothing to join with this line");
427 if (markline(frommark
) == markline(tomark
))
432 /* get the first line */
433 l
= markline(frommark
);
434 strcpy(tmpblk
.c
, fetchline(l
));
435 len
= strlen(tmpblk
.c
);
437 /* build the longer line */
438 while (++l
<= markline(tomark
))
440 /* get the next line */
443 /* remove any leading whitespace */
444 while (*scan
== '\t' || *scan
== ' ')
449 /* see if the line will fit */
450 if (strlen(scan
) + len
+ 3 > BLKSIZE
)
452 msg("Can't join -- the resulting line would be too long");
456 /* catenate it, with a space (or two) in between */
461 if (tmpblk
.c
[len
- 1] == '.'
462 || tmpblk
.c
[len
- 1] == '?'
463 || tmpblk
.c
[len
- 1] == '!')
465 tmpblk
.c
[len
++] = ' ';
467 tmpblk
.c
[len
++] = ' ';
470 strcpy(tmpblk
.c
+ len
, scan
);
473 tmpblk
.c
[len
++] = '\n';
474 tmpblk
.c
[len
] = '\0';
476 /* make the change */
479 frommark
&= ~(BLKSIZE
- 1);
480 tomark
&= ~(BLKSIZE
- 1);
482 change(frommark
, tomark
, tmpblk
.c
);
486 rptlines
= markline(tomark
) - markline(frommark
) - 1L;
493 void cmd_shift(frommark
, tomark
, cmd
, bang
, extra
)
500 long l
; /* line number counter */
501 int oldidx
; /* number of chars previously used for indent */
502 int newidx
; /* number of chars in the new indent string */
503 int oldcol
; /* previous indent amount */
504 int newcol
; /* new indent amount */
505 char *text
; /* pointer to the old line's text */
509 /* for each line to shift... */
510 for (l
= markline(frommark
); l
<= markline(tomark
); l
++)
512 /* get the line - ignore empty lines unless ! mode */
517 /* calc oldidx and oldcol */
518 for (oldidx
= 0, oldcol
= 0;
519 text
[oldidx
] == ' ' || text
[oldidx
] == '\t';
522 if (text
[oldidx
] == ' ')
528 oldcol
+= *o_tabstop
- (oldcol
% *o_tabstop
);
533 if (cmd
== CMD_SHIFTR
)
535 newcol
= oldcol
+ (*o_shiftwidth
& 0xff);
539 newcol
= oldcol
- (*o_shiftwidth
& 0xff);
544 /* if no change, then skip to next line */
545 if (oldcol
== newcol
)
548 /* build a new indent string */
552 while (newcol
>= *o_tabstop
)
554 tmpblk
.c
[newidx
++] = '\t';
555 newcol
-= *o_tabstop
;
560 tmpblk
.c
[newidx
++] = ' ';
563 tmpblk
.c
[newidx
] = '\0';
565 /* change the old indent string into the new */
566 change(MARK_AT_LINE(l
), MARK_AT_LINE(l
) + oldidx
, tmpblk
.c
);
571 rptlines
= markline(tomark
) - markline(frommark
) + 1L;
572 if (cmd
== CMD_SHIFTR
)
584 void cmd_read(frommark
, tomark
, cmd
, bang
, extra
)
591 int fd
, rc
; /* used while reading from the file */
592 char *scan
; /* used for finding NUL characters */
593 int hadnul
; /* boolean: any NULs found? */
594 int addnl
; /* boolean: forced to add newlines? */
595 int len
; /* number of chars in current line */
596 long lines
; /* number of lines in current block */
599 /* special case: if ":r !cmd" then let the filter() function do it */
602 filter(frommark
, MARK_UNSET
, extra
+ 1, TRUE
);
607 fd
= open(extra
, O_RDONLY
);
610 msg("Can't open \"%s\"", extra
);
615 if (stat(extra
, &statb
) < 0)
617 msg("Can't stat \"%s\"", extra
);
620 if (statb
.st_mode
& S_IJDIR
)
623 if (statb
.st_mode
& S_IFDIR
)
625 if ((statb
.st_mode
& S_IFMT
) != S_IFREG
)
629 msg("\"%s\" is not a regular file", extra
);
632 #endif /* not CRUNCH */
634 /* get blocks from the file, and add them */
637 /* insertion starts at the line following frommark */
638 tomark
= frommark
= (frommark
| (BLKSIZE
- 1L)) + 1L;
640 hadnul
= addnl
= FALSE
;
642 /* add an extra newline, so partial lines at the end of
643 * the file don't trip us up
647 /* for each chunk of text... */
648 while ((rc
= tread(fd
, tmpblk
.c
, BLKSIZE
- 1)) > 0)
650 /* count newlines, convert NULs, etc. ... */
651 for (lines
= 0, scan
= tmpblk
.c
; rc
> 0; rc
--, scan
++)
653 /* break up long lines */
654 if (*scan
!= '\n' && len
+ 2 > BLKSIZE
)
660 /* protect against NUL chars in file */
667 /* starting a new line? */
670 /* reset length at newline */
682 add(tomark
, tmpblk
.c
);
683 tomark
+= MARK_AT_LINE(lines
) + len
- markidx(tomark
);
686 /* if partial last line, then retain that first newline */
689 msg("Last line had no newline");
690 tomark
+= BLKSIZE
; /* <- for the rptlines calc */
692 else /* delete that first newline */
694 delete(tomark
, (tomark
| (BLKSIZE
- 1L)) + 1L);
702 rptlines
= markline(tomark
) - markline(frommark
);
706 cursor
= (tomark
& ~BLKSIZE
) - BLKSIZE
;
714 msg("Newlines were added to break up long lines");
716 msg("NULs were converted to 0x80");
722 void cmd_undo(frommark
, tomark
, cmd
, bang
, extra
)
733 /* print the selected lines */
735 void cmd_print(frommark
, tomark
, cmd
, bang
, extra
)
746 for (l
= markline(frommark
); l
<= markline(tomark
); l
++)
748 /* display a line number, if CMD_NUMBER */
749 if (cmd
== CMD_NUMBER
)
751 sprintf(tmpblk
.c
, "%6ld ", l
);
760 /* get the next line & display it */
761 for (scan
= fetchline(l
); *scan
; scan
++)
763 /* expand tabs to the proper width */
764 if (*scan
== '\t' && cmd
!= CMD_LIST
)
770 } while (col
% *o_tabstop
!= 0);
772 else if (*scan
> 0 && *scan
< ' ' || *scan
== '\177')
775 qaddch(*scan
^ 0x40);
778 else if ((*scan
& 0x80) && cmd
== CMD_LIST
)
780 sprintf(tmpblk
.c
, "\\%03o", UCHAR(*scan
));
790 /* wrap at the edge of the screen */
791 if (!has_AM
&& col
>= COLS
)
807 /* move or copy selected lines */
809 void cmd_move(frommark
, tomark
, cmd
, bang
, extra
)
818 /* parse the destination linespec. No defaults. Line 0 is okay */
820 if (!strcmp(extra
, "0"))
824 else if (linespec(extra
, &destmark
) == extra
|| !destmark
)
826 msg("invalid destination address");
830 /* flesh the marks out to encompass whole lines */
831 frommark
&= ~(BLKSIZE
- 1);
832 tomark
= (tomark
& ~(BLKSIZE
- 1)) + BLKSIZE
;
833 destmark
= (destmark
& ~(BLKSIZE
- 1)) + BLKSIZE
;
835 /* make sure the destination is valid */
836 if (cmd
== CMD_MOVE
&& destmark
>= frommark
&& destmark
< tomark
)
838 msg("invalid destination address");
844 /* save the text to a cut buffer */
846 cut(frommark
, tomark
);
848 /* if we're not copying, delete the old text & adjust destmark */
851 delete(frommark
, tomark
);
852 if (destmark
>= frommark
)
854 destmark
-= (tomark
- frommark
);
858 /* add the new text */
859 paste(destmark
, FALSE
, FALSE
);
862 /* move the cursor to the last line of the moved text */
863 cursor
= destmark
+ (tomark
- frommark
) - BLKSIZE
;
864 if (cursor
< MARK_FIRST
|| cursor
>= MARK_LAST
+ BLKSIZE
)
870 rptlabel
= ( (cmd
== CMD_COPY
) ? "copied" : "moved" );
875 /* execute EX commands from a file */
877 void cmd_source(frommark
, tomark
, cmd
, bang
, extra
)
884 /* must have a filename */
887 msg("\"source\" requires a filename");
897 void cmd_at(frommark
, tomark
, cmd
, bang
, extra
)
908 /* don't allow nested macros */
911 msg("@ macros can't be nested");
916 /* require a buffer name */
919 if (!*extra
|| !isascii(*extra
) ||!islower(*extra
))
921 msg("@ requires a cut buffer name (a-z)");
924 /* get the contents of the buffer */
925 result
= cb2str(*extra
, buf
, (unsigned)(sizeof buf
));
928 msg("buffer \"%c is empty", *extra
);
930 else if (result
>= sizeof buf
)
932 msg("buffer \"%c is too large to execute", *extra
);
936 /* execute the contents of the buffer as ex commands */
937 exstring(buf
, result
, '\\');