/* This file contains some of the commands - mostly ones that change text */
void cmd_substitute(frommark
, tomark
, cmd
, bang
, extra
)
char *extra
; /* rest of the command line */
char *line
; /* a line from the file */
regexp
*re
; /* the compiled search expression */
char *subst
; /* the substitution string */
char *opt
; /* substitution options */
long l
; /* a line number */
char *s
, *d
; /* used during subtitutions */
char *conf
; /* used during confirmation */
long chline
; /* # of lines changed */
long chsub
; /* # of substitutions made */
static optp
; /* boolean option: print when done? */
static optg
; /* boolean option: substitute globally in line? */
static optc
; /* boolean option: confirm before subst? */
/* for now, assume this will fail */
/* if visual "&", then turn off the "p" and "c" options */
else /* CMD_SUBSTITUTE */
/* make sure we got a search pattern */
if (*extra
!= '/' && *extra
!= '?')
msg("Usage: s/regular expression/new text/");
/* parse & compile the search pattern */
subst
= parseptrn(extra
);
/* abort if RE error -- error message already given by regcomp() */
if (cmd
== CMD_SUBSTITUTE
)
/* parse the substitution string & find the option string */
for (opt
= subst
; *opt
&& *opt
!= *extra
; opt
++)
if (*opt
== '\\' && opt
[1])
/* analyse the option string */
optp
= optg
= optc
= FALSE
;
case 'p': optp
= !optp
; break;
case 'g': optg
= !optg
; break;
case 'c': optc
= !optc
; break;
msg("Subst options are p, c, and g -- not %c", opt
[-1]);
/* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */
if ((optc
|| optp
) && mode
== MODE_VI
)
/* reset the change counters */
/* for each selected line */
for (l
= markline(frommark
); l
<= markline(tomark
); l
++)
/* if it contains the search pattern... */
if (regexec(re
, line
, TRUE
))
/* increment the line change counter */
/* initialize the pointers */
/* do once or globally ... */
/* confirm, if necessary */
for (conf
= line
; conf
< re
->startp
[0]; conf
++)
for ( ; conf
< re
->endp
[0]; conf
++)
/* copy accross the original chars */
/* skip to next match on this line, if any */
/* increment the substitution change counter */
/* copy stuff from before the match */
while (s
< re
->startp
[0])
/* substitute for the matched part */
/* if this regexp could conceivably match
* a zero-length string, then require at
* least 1 unmatched character between
} while (optg
&& regexec(re
, s
, FALSE
));
/* copy stuff from after the match */
while (*d
++ = *s
++) /* yes, ASSIGNMENT! */
/* NOTE: since the substitution text is allowed to have ^Ms which are
* translated into newlines, it is possible that the number of lines
* in the file will increase after each line has been substituted.
* we need to adjust for this.
/* replace the old version of the line with the new */
change(MARK_AT_LINE(l
), MARK_AT_LINE(l
+ 1), tmpblk
.c
);
tomark
+= MARK_AT_LINE(nlines
- oldnlines
);
/* if supposed to print it, do so */
/* move the cursor to that line */
cursor
= MARK_AT_LINE(l
);
/* if done from within a ":g" command, then finish silently */
msg("Substitution failed");
else if (chline
>= *o_report
)
msg("%ld substitutions on %ld lines", chsub
, chline
);
void cmd_delete(frommark
, tomark
, cmd
, bang
, extra
)
MARK curs2
; /* an altered form of the cursor */
/* choose your cut buffer */
/* make sure we're talking about whole lines here */
frommark
= frommark
& ~(BLKSIZE
- 1);
tomark
= (tomark
& ~(BLKSIZE
- 1)) + BLKSIZE
;
/* if CMD_DELETE then delete the lines */
delete(frommark
, tomark
);
cursor
= curs2
- tomark
+ frommark
;
else if (curs2
> frommark
)
void cmd_append(frommark
, tomark
, cmd
, bang
, extra
)
long l
; /* line counter */
/* if '!' then toggle auto-indent */
*o_autoindent
= !*o_autoindent
;
/* if we're doing a change, delete the old version */
cmd_delete(frommark
, tomark
, cmd
, bang
, extra
);
/* new lines start at the frommark line, or after it */
/* get lines until no more lines, or "." line, and insert them */
while (vgets('\0', tmpblk
.c
, BLKSIZE
) >= 0)
if (!strcmp(tmpblk
.c
, "."))
add(MARK_AT_LINE(l
), tmpblk
.c
);
/* on the odd chance that we're calling this from vi mode ... */
redraw(MARK_UNSET
, FALSE
);
void cmd_put(frommark
, tomark
, cmd
, bang
, extra
)
/* choose your cut buffer */
cursor
= paste(frommark
, TRUE
, FALSE
);
void cmd_join(frommark
, tomark
, cmd
, bang
, extra
)
int len
; /* length of the new line */
/* if only one line is specified, assume the following one joins too */
if (markline(frommark
) == nlines
)
msg("Nothing to join with this line");
if (markline(frommark
) == markline(tomark
))
strcpy(tmpblk
.c
, fetchline(l
));
/* build the longer line */
while (++l
<= markline(tomark
))
/* remove any leading whitespace */
while (*scan
== '\t' || *scan
== ' ')
/* see if the line will fit */
if (strlen(scan
) + len
+ 3 > BLKSIZE
)
msg("Can't join -- the resulting line would be too long");
/* catenate it, with a space (or two) in between */
if (tmpblk
.c
[len
- 1] == '.'
|| tmpblk
.c
[len
- 1] == '?'
|| tmpblk
.c
[len
- 1] == '!')
strcpy(tmpblk
.c
+ len
, scan
);
frommark
&= ~(BLKSIZE
- 1);
tomark
&= ~(BLKSIZE
- 1);
change(frommark
, tomark
, tmpblk
.c
);
rptlines
= markline(tomark
) - markline(frommark
) - 1L;
void cmd_shift(frommark
, tomark
, cmd
, bang
, extra
)
long l
; /* line number counter */
int oldidx
; /* number of chars previously used for indent */
int newidx
; /* number of chars in the new indent string */
int oldcol
; /* previous indent amount */
int newcol
; /* new indent amount */
char *text
; /* pointer to the old line's text */
/* for each line to shift... */
for (l
= markline(frommark
); l
<= markline(tomark
); l
++)
/* get the line - ignore empty lines unless ! mode */
/* calc oldidx and oldcol */
for (oldidx
= 0, oldcol
= 0;
text
[oldidx
] == ' ' || text
[oldidx
] == '\t';
oldcol
+= *o_tabstop
- (oldcol
% *o_tabstop
);
newcol
= oldcol
+ (*o_shiftwidth
& 0xff);
newcol
= oldcol
- (*o_shiftwidth
& 0xff);
/* if no change, then skip to next line */
/* build a new indent string */
while (newcol
>= *o_tabstop
)
tmpblk
.c
[newidx
++] = '\t';
tmpblk
.c
[newidx
++] = ' ';
/* change the old indent string into the new */
change(MARK_AT_LINE(l
), MARK_AT_LINE(l
) + oldidx
, tmpblk
.c
);
rptlines
= markline(tomark
) - markline(frommark
) + 1L;
void cmd_read(frommark
, tomark
, cmd
, bang
, extra
)
int fd
, rc
; /* used while reading from the file */
char *scan
; /* used for finding NUL characters */
int hadnul
; /* boolean: any NULs found? */
int addnl
; /* boolean: forced to add newlines? */
int len
; /* number of chars in current line */
long lines
; /* number of lines in current block */
/* special case: if ":r !cmd" then let the filter() function do it */
filter(frommark
, MARK_UNSET
, extra
+ 1, TRUE
);
fd
= open(extra
, O_RDONLY
);
msg("Can't open \"%s\"", extra
);
if (stat(extra
, &statb
) < 0)
msg("Can't stat \"%s\"", extra
);
if (statb
.st_mode
& S_IJDIR
)
if (statb
.st_mode
& S_IFDIR
)
if ((statb
.st_mode
& S_IFMT
) != S_IFREG
)
msg("\"%s\" is not a regular file", extra
);
/* get blocks from the file, and add them */
/* insertion starts at the line following frommark */
tomark
= frommark
= (frommark
| (BLKSIZE
- 1L)) + 1L;
/* add an extra newline, so partial lines at the end of
* the file don't trip us up
/* for each chunk of text... */
while ((rc
= tread(fd
, tmpblk
.c
, BLKSIZE
- 1)) > 0)
/* count newlines, convert NULs, etc. ... */
for (lines
= 0, scan
= tmpblk
.c
; rc
> 0; rc
--, scan
++)
/* break up long lines */
if (*scan
!= '\n' && len
+ 2 > BLKSIZE
)
/* protect against NUL chars in file */
/* starting a new line? */
/* reset length at newline */
tomark
+= MARK_AT_LINE(lines
) + len
- markidx(tomark
);
/* if partial last line, then retain that first newline */
msg("Last line had no newline");
tomark
+= BLKSIZE
; /* <- for the rptlines calc */
else /* delete that first newline */
delete(tomark
, (tomark
| (BLKSIZE
- 1L)) + 1L);
rptlines
= markline(tomark
) - markline(frommark
);
cursor
= (tomark
& ~BLKSIZE
) - BLKSIZE
;
msg("Newlines were added to break up long lines");
msg("NULs were converted to 0x80");
void cmd_undo(frommark
, tomark
, cmd
, bang
, extra
)
/* print the selected lines */
void cmd_print(frommark
, tomark
, cmd
, bang
, extra
)
for (l
= markline(frommark
); l
<= markline(tomark
); l
++)
/* display a line number, if CMD_NUMBER */
sprintf(tmpblk
.c
, "%6ld ", l
);
/* get the next line & display it */
for (scan
= fetchline(l
); *scan
; scan
++)
/* expand tabs to the proper width */
if (*scan
== '\t' && cmd
!= CMD_LIST
)
} while (col
% *o_tabstop
!= 0);
else if (*scan
> 0 && *scan
< ' ' || *scan
== '\177')
else if ((*scan
& 0x80) && cmd
== CMD_LIST
)
sprintf(tmpblk
.c
, "\\%03o", UCHAR(*scan
));
/* wrap at the edge of the screen */
if (!has_AM
&& col
>= COLS
)
/* move or copy selected lines */
void cmd_move(frommark
, tomark
, cmd
, bang
, extra
)
/* parse the destination linespec. No defaults. Line 0 is okay */
else if (linespec(extra
, &destmark
) == extra
|| !destmark
)
msg("invalid destination address");
/* flesh the marks out to encompass whole lines */
frommark
&= ~(BLKSIZE
- 1);
tomark
= (tomark
& ~(BLKSIZE
- 1)) + BLKSIZE
;
destmark
= (destmark
& ~(BLKSIZE
- 1)) + BLKSIZE
;
/* make sure the destination is valid */
if (cmd
== CMD_MOVE
&& destmark
>= frommark
&& destmark
< tomark
)
msg("invalid destination address");
/* save the text to a cut buffer */
/* if we're not copying, delete the old text & adjust destmark */
delete(frommark
, tomark
);
if (destmark
>= frommark
)
destmark
-= (tomark
- frommark
);
paste(destmark
, FALSE
, FALSE
);
/* move the cursor to the last line of the moved text */
cursor
= destmark
+ (tomark
- frommark
) - BLKSIZE
;
if (cursor
< MARK_FIRST
|| cursor
>= MARK_LAST
+ BLKSIZE
)
rptlabel
= ( (cmd
== CMD_COPY
) ? "copied" : "moved" );
/* execute EX commands from a file */
void cmd_source(frommark
, tomark
, cmd
, bang
, extra
)
/* must have a filename */
msg("\"source\" requires a filename");
void cmd_at(frommark
, tomark
, cmd
, bang
, extra
)
/* don't allow nested macros */
msg("@ macros can't be nested");
/* require a buffer name */
if (!*extra
|| !isascii(*extra
) ||!islower(*extra
))
msg("@ requires a cut buffer name (a-z)");
/* get the contents of the buffer */
result
= cb2str(*extra
, buf
, (unsigned)(sizeof buf
));
msg("buffer \"%c is empty", *extra
);
else if (result
>= sizeof buf
)
msg("buffer \"%c is too large to execute", *extra
);
/* execute the contents of the buffer as ex commands */
exstring(buf
, result
, '\\');