sed 'y' command, simplify some other code
This commit is contained in:
parent
0c51832b60
commit
f01b46d7dd
130
editors/sed.c
130
editors/sed.c
@ -97,9 +97,10 @@ typedef struct sed_cmd_s {
|
|||||||
unsigned int sub_g:1; /* sed -e 's/foo/bar/g' (global) */
|
unsigned int sub_g:1; /* sed -e 's/foo/bar/g' (global) */
|
||||||
unsigned int sub_p:2; /* sed -e 's/foo/bar/p' (print substitution) */
|
unsigned int sub_p:2; /* sed -e 's/foo/bar/p' (print substitution) */
|
||||||
|
|
||||||
/* GENERAL FIELDS */
|
/* TRANSLATE COMMAND */
|
||||||
char delimiter; /* The delimiter used to separate regexps */
|
char *translate;
|
||||||
|
|
||||||
|
/* GENERAL FIELDS */
|
||||||
/* the command */
|
/* the command */
|
||||||
char cmd; /* p,d,s (add more at your leisure :-) */
|
char cmd; /* p,d,s (add more at your leisure :-) */
|
||||||
|
|
||||||
@ -148,23 +149,23 @@ static void destroy_cmd_strs(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* index_of_next_unescaped_regexp_delim - walks left to right through a string
|
* index_of_next_unescaped_regexp_delim - walks left to right through a string
|
||||||
* beginning at a specified index and returns the index of the next regular
|
* beginning at a specified index and returns the index of the next regular
|
||||||
* expression delimiter (typically a forward * slash ('/')) not preceeded by
|
* expression delimiter (typically a forward * slash ('/')) not preceeded by
|
||||||
* a backslash ('\').
|
* a backslash ('\').
|
||||||
*/
|
*/
|
||||||
static int index_of_next_unescaped_regexp_delim(const char delimiter, const char *str, int idx)
|
static int index_of_next_unescaped_regexp_delim(const char delimiter, const char *str)
|
||||||
{
|
{
|
||||||
int bracket = -1;
|
int bracket = -1;
|
||||||
int escaped = 0;
|
int escaped = 0;
|
||||||
|
int idx = 0;
|
||||||
char ch;
|
char ch;
|
||||||
|
|
||||||
for ( ; (ch = str[idx]); idx++) {
|
for ( ; (ch = str[idx]); idx++) {
|
||||||
if (bracket != -1) {
|
if (bracket != -1) {
|
||||||
if (ch == ']' && !(bracket == idx - 1 ||
|
if (ch == ']' && !(bracket == idx - 1 ||
|
||||||
(bracket == idx - 2 && str[idx-1] == '^')))
|
(bracket == idx - 2 && str[idx-1] == '^')))
|
||||||
bracket = -1;
|
bracket = -1;
|
||||||
} else if (escaped)
|
} else if (escaped)
|
||||||
escaped = 0;
|
escaped = 0;
|
||||||
@ -180,10 +181,43 @@ static int index_of_next_unescaped_regexp_delim(const char delimiter, const char
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
|
||||||
|
{
|
||||||
|
const char *cmdstr_ptr = cmdstr;
|
||||||
|
char delimiter;
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
/* verify that the 's' is followed by something. That something
|
||||||
|
* (typically a 'slash') is now our regexp delimiter... */
|
||||||
|
if (*cmdstr == '\0')
|
||||||
|
bb_error_msg_and_die("bad format in substitution expression");
|
||||||
|
else
|
||||||
|
delimiter = *cmdstr_ptr;
|
||||||
|
|
||||||
|
cmdstr_ptr++;
|
||||||
|
|
||||||
|
/* save the match string */
|
||||||
|
idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
|
||||||
|
if (idx == -1) {
|
||||||
|
bb_error_msg_and_die("bad format in substitution expression");
|
||||||
|
}
|
||||||
|
*match = bb_xstrndup(cmdstr_ptr, idx);
|
||||||
|
|
||||||
|
/* save the replacement string */
|
||||||
|
cmdstr_ptr += idx + 1;
|
||||||
|
idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
|
||||||
|
if (idx == -1) {
|
||||||
|
bb_error_msg_and_die("bad format in substitution expression");
|
||||||
|
}
|
||||||
|
*replace = bb_xstrndup(cmdstr_ptr, idx);
|
||||||
|
|
||||||
|
return((cmdstr_ptr - cmdstr) + idx);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* returns the index in the string just past where the address ends.
|
* returns the index in the string just past where the address ends.
|
||||||
*/
|
*/
|
||||||
static int get_address(char *delimiter, char *my_str, int *linenum, regex_t **regex)
|
static int get_address(char *my_str, int *linenum, regex_t **regex)
|
||||||
{
|
{
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
if (isdigit(my_str[idx])) {
|
if (isdigit(my_str[idx])) {
|
||||||
@ -198,13 +232,15 @@ static int get_address(char *delimiter, char *my_str, int *linenum, regex_t **re
|
|||||||
}
|
}
|
||||||
else if (my_str[idx] == '/' || my_str[idx] == '\\') {
|
else if (my_str[idx] == '/' || my_str[idx] == '\\') {
|
||||||
int idx_start = 1;
|
int idx_start = 1;
|
||||||
|
char delimiter;
|
||||||
|
|
||||||
*delimiter = '/';
|
delimiter = '/';
|
||||||
if (my_str[idx] == '\\') {
|
if (my_str[idx] == '\\') {
|
||||||
idx_start++;
|
idx_start++;
|
||||||
*delimiter = my_str[++idx];
|
delimiter = my_str[++idx];
|
||||||
}
|
}
|
||||||
idx = index_of_next_unescaped_regexp_delim(*delimiter, my_str, ++idx);
|
idx++;
|
||||||
|
idx += index_of_next_unescaped_regexp_delim(delimiter, my_str + idx);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
bb_error_msg_and_die("unterminated match expression");
|
bb_error_msg_and_die("unterminated match expression");
|
||||||
}
|
}
|
||||||
@ -218,7 +254,6 @@ static int get_address(char *delimiter, char *my_str, int *linenum, regex_t **re
|
|||||||
|
|
||||||
static int parse_subst_cmd(sed_cmd_t * const sed_cmd, const char *substr)
|
static int parse_subst_cmd(sed_cmd_t * const sed_cmd, const char *substr)
|
||||||
{
|
{
|
||||||
int oldidx;
|
|
||||||
int cflags = 0;
|
int cflags = 0;
|
||||||
char *match;
|
char *match;
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
@ -233,19 +268,7 @@ static int parse_subst_cmd(sed_cmd_t * const sed_cmd, const char *substr)
|
|||||||
* (all three of the '/' slashes are mandatory)
|
* (all three of the '/' slashes are mandatory)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* verify that the 's' is followed by something. That something
|
idx = parse_regex_delim(substr, &match, &sed_cmd->replace);
|
||||||
* (typically a 'slash') is now our regexp delimiter... */
|
|
||||||
if (substr[idx] == '\0')
|
|
||||||
bb_error_msg_and_die("bad format in substitution expression");
|
|
||||||
else
|
|
||||||
sed_cmd->delimiter=substr[idx];
|
|
||||||
|
|
||||||
/* save the match string */
|
|
||||||
oldidx = idx+1;
|
|
||||||
idx = index_of_next_unescaped_regexp_delim(sed_cmd->delimiter, substr, ++idx);
|
|
||||||
if (idx == -1)
|
|
||||||
bb_error_msg_and_die("bad format in substitution expression");
|
|
||||||
match = bb_xstrndup(substr + oldidx, idx - oldidx);
|
|
||||||
|
|
||||||
/* determine the number of back references in the match string */
|
/* determine the number of back references in the match string */
|
||||||
/* Note: we compute this here rather than in the do_subst_command()
|
/* Note: we compute this here rather than in the do_subst_command()
|
||||||
@ -259,13 +282,6 @@ static int parse_subst_cmd(sed_cmd_t * const sed_cmd, const char *substr)
|
|||||||
sed_cmd->num_backrefs++;
|
sed_cmd->num_backrefs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save the replacement string */
|
|
||||||
oldidx = idx+1;
|
|
||||||
idx = index_of_next_unescaped_regexp_delim(sed_cmd->delimiter, substr, ++idx);
|
|
||||||
if (idx == -1)
|
|
||||||
bb_error_msg_and_die("bad format in substitution expression");
|
|
||||||
sed_cmd->replace = bb_xstrndup(substr + oldidx, idx - oldidx);
|
|
||||||
|
|
||||||
/* process the flags */
|
/* process the flags */
|
||||||
while (substr[++idx]) {
|
while (substr[++idx]) {
|
||||||
switch (substr[idx]) {
|
switch (substr[idx]) {
|
||||||
@ -297,6 +313,39 @@ out:
|
|||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void replace_slash_n(char *string)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int remaining = strlen(string);
|
||||||
|
|
||||||
|
for (i = 0; string[i]; i++) {
|
||||||
|
if ((string[i] == '\\') && (string[i + 1] == 'n')) {
|
||||||
|
string[i] = '\n';
|
||||||
|
memmove(string + i + 1, string + i + 1, remaining - 1);
|
||||||
|
} else {
|
||||||
|
remaining--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_translate_cmd(sed_cmd_t * const sed_cmd, const char *cmdstr)
|
||||||
|
{
|
||||||
|
char *match;
|
||||||
|
char *replace;
|
||||||
|
int idx;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
idx = parse_regex_delim(cmdstr, &match, &replace);
|
||||||
|
replace_slash_n(match);
|
||||||
|
replace_slash_n(replace);
|
||||||
|
sed_cmd->translate = xcalloc(1, (strlen(match) + 1) * 2);
|
||||||
|
for (i = 0; (match[i] != 0) && (replace[i] != 0); i++) {
|
||||||
|
sed_cmd->translate[i * 2] = match[i];
|
||||||
|
sed_cmd->translate[(i * 2) + 1] = replace[i];
|
||||||
|
}
|
||||||
|
return(idx);
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_edit_cmd(sed_cmd_t *sed_cmd, const char *editstr)
|
static int parse_edit_cmd(sed_cmd_t *sed_cmd, const char *editstr)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
@ -417,6 +466,10 @@ static char *parse_cmd_str(sed_cmd_t * const sed_cmd, char *cmdstr)
|
|||||||
strncpy(sed_cmd->label, cmdstr, length);
|
strncpy(sed_cmd->label, cmdstr, length);
|
||||||
cmdstr += length;
|
cmdstr += length;
|
||||||
}
|
}
|
||||||
|
/* translation command */
|
||||||
|
else if (sed_cmd->cmd == 'y') {
|
||||||
|
cmdstr += parse_translate_cmd(sed_cmd, cmdstr);
|
||||||
|
}
|
||||||
/* if it wasnt a single-letter command that takes no arguments
|
/* if it wasnt a single-letter command that takes no arguments
|
||||||
* then it must be an invalid command.
|
* then it must be an invalid command.
|
||||||
*/
|
*/
|
||||||
@ -430,7 +483,6 @@ static char *parse_cmd_str(sed_cmd_t * const sed_cmd, char *cmdstr)
|
|||||||
|
|
||||||
static char *add_cmd(sed_cmd_t *sed_cmd, char *cmdstr)
|
static char *add_cmd(sed_cmd_t *sed_cmd, char *cmdstr)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* Skip over leading whitespace and semicolons */
|
/* Skip over leading whitespace and semicolons */
|
||||||
cmdstr += strspn(cmdstr, semicolon_whitespace);
|
cmdstr += strspn(cmdstr, semicolon_whitespace);
|
||||||
|
|
||||||
@ -452,13 +504,13 @@ static char *add_cmd(sed_cmd_t *sed_cmd, char *cmdstr)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* first part (if present) is an address: either a '$', a number or a /regex/ */
|
/* first part (if present) is an address: either a '$', a number or a /regex/ */
|
||||||
cmdstr += get_address(&(sed_cmd->delimiter), cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
|
cmdstr += get_address(cmdstr, &sed_cmd->beg_line, &sed_cmd->beg_match);
|
||||||
|
|
||||||
/* second part (if present) will begin with a comma */
|
/* second part (if present) will begin with a comma */
|
||||||
if (*cmdstr == ',') {
|
if (*cmdstr == ',') {
|
||||||
int idx;
|
int idx;
|
||||||
cmdstr++;
|
cmdstr++;
|
||||||
idx = get_address(&(sed_cmd->delimiter), cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
|
idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
|
||||||
if (idx == 0) {
|
if (idx == 0) {
|
||||||
bb_error_msg_and_die("get_address: no address found in string\n"
|
bb_error_msg_and_die("get_address: no address found in string\n"
|
||||||
"\t(you probably didn't check the string you passed me)");
|
"\t(you probably didn't check the string you passed me)");
|
||||||
@ -911,6 +963,18 @@ static void process_file(FILE *file)
|
|||||||
sed_cmd = branch_to(sed_cmd->label);
|
sed_cmd = branch_to(sed_cmd->label);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'y': {
|
||||||
|
int i;
|
||||||
|
for (i = 0; line[i] != 0; i++) {
|
||||||
|
int j;
|
||||||
|
for (j = 0; sed_cmd->translate[j] ;j += 2) {
|
||||||
|
if (line[i] == sed_cmd->translate[j]) {
|
||||||
|
line[i] = sed_cmd->translate[j + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
// case ':':
|
// case ':':
|
||||||
// break;
|
// break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user