vi: expand '%' and '#' in colon commands

Track the current and alternate filenames.  The placeholders '%'
and '#' can be used in arguments to colon commands to represent
the current and alternate filenames respectively.  Backslash can
be used to allow literal '%' and '#' characters to be entered.

This feature is controlled by the configuration option
FEATURE_VI_COLON_EXPAND.

function                                             old     new   delta
expand_args                                            -     198    +198
colon                                               3751    3927    +176
update_filename                                        -      70     +70
init_filename                                          -      48     +48
.rodata                                           105218  105239     +21
get_one_char                                         115     124      +9
edit_file                                            835     838      +3
do_cmd                                              4724    4727      +3
init_text_buffer                                     190     172     -18
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 5/1 up/down: 528/-18)           Total: 510 bytes

Signed-off-by: Ron Yorston <rmy@pobox.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Ron Yorston 2021-04-25 11:55:42 +01:00 committed by Denys Vlasenko
parent 852ffbee34
commit acd3079fd1

View File

@ -53,6 +53,14 @@
//config: Enable a limited set of colon commands. This does not //config: Enable a limited set of colon commands. This does not
//config: provide an "ex" mode. //config: provide an "ex" mode.
//config: //config:
//config:config FEATURE_VI_COLON_EXPAND
//config: bool "Expand \"%\" and \"#\" in colon commands"
//config: default y
//config: depends on FEATURE_VI_COLON
//config: help
//config: Expand the special characters \"%\" (current filename)
//config: and \"#\" (alternate filename) in colon commands.
//config:
//config:config FEATURE_VI_YANKMARK //config:config FEATURE_VI_YANKMARK
//config: bool "Enable yank/put commands and mark cmds" //config: bool "Enable yank/put commands and mark cmds"
//config: default y //config: default y
@ -347,6 +355,9 @@ struct globals {
// [don't make smallint!] // [don't make smallint!]
int last_status_cksum; // hash of current status line int last_status_cksum; // hash of current status line
char *current_filename; char *current_filename;
#if ENABLE_FEATURE_VI_COLON_EXPAND
char *alt_filename;
#endif
char *screenbegin; // index into text[], of top line on the screen char *screenbegin; // index into text[], of top line on the screen
char *screen; // pointer to the virtual screen buffer char *screen; // pointer to the virtual screen buffer
int screensize; // and its size int screensize; // and its size
@ -471,6 +482,7 @@ struct globals {
#define have_status_msg (G.have_status_msg ) #define have_status_msg (G.have_status_msg )
#define last_status_cksum (G.last_status_cksum ) #define last_status_cksum (G.last_status_cksum )
#define current_filename (G.current_filename ) #define current_filename (G.current_filename )
#define alt_filename (G.alt_filename )
#define screen (G.screen ) #define screen (G.screen )
#define screensize (G.screensize ) #define screensize (G.screensize )
#define screenbegin (G.screenbegin ) #define screenbegin (G.screenbegin )
@ -2198,6 +2210,41 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
return p; return p;
} }
#if ENABLE_FEATURE_VI_COLON_EXPAND
static void init_filename(char *fn)
{
char *copy = xstrdup(fn);
if (current_filename == NULL) {
current_filename = copy;
} else {
free(alt_filename);
alt_filename = copy;
}
}
#else
# define init_filename(f) ((void)(0))
#endif
static void update_filename(char *fn)
{
#if ENABLE_FEATURE_VI_COLON_EXPAND
if (fn == NULL)
return;
if (current_filename == NULL || strcmp(fn, current_filename) != 0) {
free(alt_filename);
alt_filename = current_filename;
current_filename = xstrdup(fn);
}
#else
if (fn != current_filename) {
free(current_filename);
current_filename = xstrdup(fn);
}
#endif
}
// read text from file or create an empty buf // read text from file or create an empty buf
// will also update current_filename // will also update current_filename
static int init_text_buffer(char *fn) static int init_text_buffer(char *fn)
@ -2209,10 +2256,7 @@ static int init_text_buffer(char *fn)
text_size = 10240; text_size = 10240;
screenbegin = dot = end = text = xzalloc(text_size); screenbegin = dot = end = text = xzalloc(text_size);
if (fn != current_filename) { update_filename(fn);
free(current_filename);
current_filename = xstrdup(fn);
}
rc = file_insert(fn, text, 1); rc = file_insert(fn, text, 1);
if (rc < 0) { if (rc < 0) {
// file doesnt exist. Start empty buf with dummy line // file doesnt exist. Start empty buf with dummy line
@ -2556,6 +2600,43 @@ static void setops(char *args, int flg_no)
} }
# endif # endif
# if ENABLE_FEATURE_VI_COLON_EXPAND
static char *expand_args(char *args)
{
char *s, *t;
const char *replace;
args = xstrdup(args);
for (s = args; *s; s++) {
if (*s == '%') {
replace = current_filename;
} else if (*s == '#') {
replace = alt_filename;
} else {
if (*s == '\\' && s[1] != '\0') {
for (t = s++; *t; t++)
*t = t[1];
}
continue;
}
if (replace == NULL) {
free(args);
status_line_bold("No previous filename");
return NULL;
}
*s = '\0';
t = xasprintf("%s%s%s", args, replace, s+1);
s = t + (s - args) + strlen(replace);
free(args);
args = t;
}
return args;
}
# else
# define expand_args(a) (a)
# endif
#endif /* FEATURE_VI_COLON */ #endif /* FEATURE_VI_COLON */
// buf must be no longer than MAX_INPUT_LEN! // buf must be no longer than MAX_INPUT_LEN!
@ -2620,7 +2701,7 @@ static void colon(char *buf)
#else #else
char c, *buf1, *q, *r; char c, *buf1, *q, *r;
char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args; char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args, *exp = NULL;
int i, l, li, b, e; int i, l, li, b, e;
int useforce; int useforce;
@ -2708,9 +2789,12 @@ static void colon(char *buf)
else if (cmd[0] == '!') { // run a cmd else if (cmd[0] == '!') { // run a cmd
int retcode; int retcode;
// :!ls run the <cmd> // :!ls run the <cmd>
exp = expand_args(buf + 1);
if (exp == NULL)
goto ret;
go_bottom_and_clear_to_eol(); go_bottom_and_clear_to_eol();
cookmode(); cookmode();
retcode = system(buf + 1); // run the cmd retcode = system(exp); // run the cmd
if (retcode) if (retcode)
printf("\nshell returned %i\n\n", retcode); printf("\nshell returned %i\n\n", retcode);
rawmode(); rawmode();
@ -2739,7 +2823,9 @@ static void colon(char *buf)
} }
if (args[0]) { if (args[0]) {
// the user supplied a file name // the user supplied a file name
fn = args; fn = exp = expand_args(args);
if (exp == NULL)
goto ret;
} else if (current_filename == NULL) { } else if (current_filename == NULL) {
// no user file name, no current name- punt // no user file name, no current name- punt
status_line_bold("No current filename"); status_line_bold("No current filename");
@ -2763,7 +2849,7 @@ static void colon(char *buf)
status_line("'%s'%s" status_line("'%s'%s"
IF_FEATURE_VI_READONLY("%s") IF_FEATURE_VI_READONLY("%s")
" %uL, %uC", " %uL, %uC",
current_filename, fn,
(size < 0 ? " [New file]" : ""), (size < 0 ? " [New file]" : ""),
IF_FEATURE_VI_READONLY( IF_FEATURE_VI_READONLY(
((readonly_mode) ? " [Readonly]" : ""), ((readonly_mode) ? " [Readonly]" : ""),
@ -2777,8 +2863,10 @@ static void colon(char *buf)
} }
if (args[0]) { if (args[0]) {
// user wants a new filename // user wants a new filename
free(current_filename); exp = expand_args(args);
current_filename = xstrdup(args); if (exp == NULL)
goto ret;
update_filename(exp);
} else { } else {
// user wants file status info // user wants file status info
last_status_cksum = 0; // force status update last_status_cksum = 0; // force status update
@ -2862,7 +2950,10 @@ static void colon(char *buf)
if (args[0]) { if (args[0]) {
// the user supplied a file name // the user supplied a file name
fn = args; fn = exp = expand_args(args);
if (exp == NULL)
goto ret;
init_filename(fn);
} else if (current_filename == NULL) { } else if (current_filename == NULL) {
// no user file name, no current name- punt // no user file name, no current name- punt
status_line_bold("No current filename"); status_line_bold("No current filename");
@ -3042,12 +3133,16 @@ static void colon(char *buf)
if (args[0]) { if (args[0]) {
struct stat statbuf; struct stat statbuf;
if (!useforce && (fn == NULL || strcmp(fn, args) != 0) && exp = expand_args(args);
stat(args, &statbuf) == 0) { if (exp == NULL)
goto ret;
if (!useforce && (fn == NULL || strcmp(fn, exp) != 0) &&
stat(exp, &statbuf) == 0) {
status_line_bold("File exists (:w! overrides)"); status_line_bold("File exists (:w! overrides)");
goto ret; goto ret;
} }
fn = args; fn = exp;
init_filename(fn);
} }
# if ENABLE_FEATURE_VI_READONLY # if ENABLE_FEATURE_VI_READONLY
else if (readonly_mode && !useforce && fn) { else if (readonly_mode && !useforce && fn) {
@ -3109,6 +3204,9 @@ static void colon(char *buf)
not_implemented(cmd); not_implemented(cmd);
} }
ret: ret:
# if ENABLE_FEATURE_VI_COLON_EXPAND
free(exp);
# endif
dot = bound_dot(dot); // make sure "dot" is valid dot = bound_dot(dot); // make sure "dot" is valid
return; return;
# if ENABLE_FEATURE_VI_SEARCH # if ENABLE_FEATURE_VI_SEARCH