summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpgen <p.gen.progs@gmail.com>2022-08-23 00:50:35 +0200
committerpgen <p.gen.progs@gmail.com>2022-08-24 00:25:17 +0200
commit637fc27e86627239a5b5197a5e6669624443fab9 (patch)
tree185722598546478d8892f83e1c7485801b56b471
parenta4a7a57956a39393f33039a6a78a48c4bf992f06 (diff)
Improve and fix the substitution code
-rw-r--r--smenu.129
-rw-r--r--smenu.c59
2 files changed, 78 insertions, 10 deletions
diff --git a/smenu.1 b/smenu.1
index 6c4a49f..391c85b 100644
--- a/smenu.1
+++ b/smenu.1
@@ -1430,6 +1430,19 @@ Post-processes the words by applying a regular expression based
substitution.
The argument must be formatted as in the \fBsed\fP editor.
+As in \fBsed\fP, matching groups and references to these groups
+(from \f(CB\\0\fP to \f(CB\\9\fP in \fIrepl\fP) are supported.
+These groups must be surrounded by \f(CB(\fP and \f(CB)\fP in \fIregex\fP.
+\f(CB\\0\fP and \f(CB&\fP are equivalent in \fIrepl\fP as in the GNU
+version of \fBsed\fP.
+
+Back reference example:
+
+.nf
+\f(CBR=$(echo "[A] [B] [C]" | ./smenu -S '/([^][]+)/:\\1:/')\fP
+will display \f(CR"[:A:] [:B:] [:C:]"\fP
+.fi
+
This option can be used more than once.
Each substitution will be applied in sequence on each word.
This sequence can be stopped if a \fBstop\fP flag is encountered.
@@ -1451,14 +1464,20 @@ more substitution will be allowed on this word even if another
The optional trailing \fBi\fP (for \fIi\fPgnore case) means that the
string search operation should ignore the case for this pattern.
-Small example:
+Small examples to explain the meaning of \fIv\fP:
+
+.nf
\f(CBR=$(echo a b c | smenu -S /b/B/)\fP
-will display \f(CR"a B c"\fP and \f(CBR\fP will contain \fIB\fP if \fI
-B\fP is
-selected meanwhile
+.fi
+will display \f(CR"a B c"\fP and \f(CBR\fP will contain \fIB\fP
+when \fIB\fP is selected meanwhile
+
+.nf
\f(CBR=$(echo a b c | smenu -S /b/B/\fBv\fP)\fR
+.fi
will display the same as above but \f(CBR\fP will contain the original
-word \fIb\fP if \fIB\fP is selected.
+word \fIb\fP when \fIB\fP is selected.
+
In both cases, only the word \fIB\fP will be searchable and not \fIb\fP.
.RE
.IP "\fB-I\fP|\fB-si\fP|\fB-subst_included\fP... \
diff --git a/smenu.c b/smenu.c
index 658c86a..1f89dd2 100644
--- a/smenu.c
+++ b/smenu.c
@@ -1983,6 +1983,8 @@ err:
/* match_start/end: offset in orig for the current matched string */
/* subs_nb: number of elements containing significant values in */
/* the array described above. */
+/* error: set to 0 if no error in replacement string and to 1 */
+/* otherwise. */
/* match: current match number in the original string. */
/* */
/* OUT: */
@@ -1990,7 +1992,7 @@ err:
/* ===================================================================== */
char *
build_repl_string(char * orig, char * repl, long match_start, long match_end,
- range_t * subs_a, long subs_nb, long match)
+ range_t * subs_a, long subs_nb, long match, int * error)
{
size_t allocated = 16;
size_t rsize = 0;
@@ -1999,10 +2001,12 @@ build_repl_string(char * orig, char * repl, long match_start, long match_end,
long offset = match * subs_nb; /* offset of the 1st sub *
| corresponding to the match. */
+ *error = 0;
+
if (*repl == '\0')
str = xstrdup("");
else
- while (*repl)
+ while (!*error && *repl)
{
switch (*repl)
{
@@ -2044,6 +2048,35 @@ build_repl_string(char * orig, char * repl, long match_start, long match_end,
rsize += delta;
str[rsize] = '\0';
}
+ else
+ {
+ *error = 1;
+ break;
+ }
+ special = 0;
+ }
+ else
+ {
+ if (allocated == rsize)
+ str = xrealloc(str, allocated += 16);
+ str[rsize] = *repl;
+ rsize++;
+ str[rsize] = '\0';
+ }
+ break;
+
+ case '0':
+ if (special)
+ {
+ long delta = match_end - match_start;
+
+ if (allocated <= rsize + delta)
+ str = xrealloc(str, allocated += (delta + 16));
+
+ memcpy(str + rsize, orig + match_start, delta);
+
+ rsize += delta;
+ str[rsize] = '\0';
special = 0;
}
@@ -2054,6 +2087,7 @@ build_repl_string(char * orig, char * repl, long match_start, long match_end,
str[rsize] = *repl;
rsize++;
str[rsize] = '\0';
+ special = 0;
}
break;
@@ -2069,24 +2103,29 @@ build_repl_string(char * orig, char * repl, long match_start, long match_end,
rsize += delta;
str[rsize] = '\0';
-
break;
}
+ else
+ special = 0;
/* No break here, '&' must be treated as a normal */
/* character when protected. */
/* '''''''''''''''''''''''''''''''''''''''''''''' */
default:
- special = 0;
if (allocated == rsize)
str = xrealloc(str, allocated += 16);
str[rsize] = *repl;
rsize++;
str[rsize] = '\0';
+ special = 0;
}
repl++;
}
+
+ if (special > 0)
+ *error = 1;
+
return str;
}
@@ -2149,11 +2188,21 @@ replace(char * orig, sed_t * sed)
{
size_t len;
size_t end;
+ int error;
exp_repl = build_repl_string(orig, sed->substitution,
matches_a[match].start,
matches_a[match].end, subs_a, subs_max,
- match);
+ match, &error);
+
+ if (error)
+ {
+ fprintf(stderr,
+ "Invalid matching group reference "
+ "in the replacement string \"%s\".\n",
+ sed->substitution);
+ exit(EXIT_FAILURE);
+ }
len = strlen(exp_repl);