[latex3-commits] [git/LaTeX3-latex3-latex3] main: Catch brace balance errors in all regex functions (fixes #377) (70c354fbc)
Bruno Le Floch
blflatex at gmail.com
Thu May 13 12:19:25 CEST 2021
Repository : https://github.com/latex3/latex3
On branch : main
Link : https://github.com/latex3/latex3/commit/70c354fbc9de825931f826c2dff958c51f0c1a06
>---------------------------------------------------------------
commit 70c354fbc9de825931f826c2dff958c51f0c1a06
Author: Bruno Le Floch <blflatex at gmail.com>
Date: Thu May 13 12:16:44 2021 +0200
Catch brace balance errors in all regex functions (fixes #377)
>---------------------------------------------------------------
70c354fbc9de825931f826c2dff958c51f0c1a06
l3kernel/CHANGELOG.md | 3 +
l3kernel/l3regex.dtx | 137 ++++++++++++++++++++---
l3kernel/testfiles/m3regex006.lvt | 3 +-
l3kernel/testfiles/m3regex006.tlg | 228 ++++++++++++++++++++++++++++++++++++++
4 files changed, 357 insertions(+), 14 deletions(-)
diff --git a/l3kernel/CHANGELOG.md b/l3kernel/CHANGELOG.md
index 69f8cd9f4..3902c1633 100644
--- a/l3kernel/CHANGELOG.md
+++ b/l3kernel/CHANGELOG.md
@@ -15,6 +15,9 @@ this project uses date-based 'snapshot' version identifiers.
`\ior_show:N`, `\ior_log:N`, `\iow_show:N`, `\iow_log:N`,
`\tl_log_analysis:N`, `\tl_log_analysis:n`
+### Fixed
+- Checking brace balance in all regex functions (issue #377)
+
## [2021-05-11]
### Added
diff --git a/l3kernel/l3regex.dtx b/l3kernel/l3regex.dtx
index 353afa112..599a2979b 100644
--- a/l3kernel/l3regex.dtx
+++ b/l3kernel/l3regex.dtx
@@ -6530,27 +6530,35 @@
%
% \begin{macro}{\@@_group_end_extract_seq:N}
% The end-points of submatches are stored as entries of two arrays
-% from \cs{l_@@_min_submatch_int} to
-% \cs{l_@@_submatch_int} (exclusive). Extract the relevant ranges
-% into \cs{l_@@_internal_a_tl}. We detect unbalanced results using
-% the two flags \texttt{__regex_begin} and \texttt{__regex_end}, raised
-% whenever we see too many begin-group or end-group tokens in a
-% submatch.
+% from \cs{l_@@_min_submatch_int} to \cs{l_@@_submatch_int}
+% (exclusive). Extract the relevant ranges into \cs{g_@@_internal_tl},
+% separated by \cs{@@_tmp:w} |{}|. We keep track in the two flags
+% \texttt{__regex_begin} and \texttt{__regex_end} of the number of
+% begin-group or end-group tokens added to make each of these items
+% overall balanced. At this step, |}{| is counted as being balanced
+% (same number of begin-group and end-group tokens). This problem is
+% caught by \cs{@@_extract_check:w}, explained later. After
+% complaining about any begin-group or end-group tokens we had to add,
+% we are ready to construct the user's sequence outside the group.
% \begin{macrocode}
\cs_new_protected:Npn \@@_group_end_extract_seq:N #1
{
\flag_clear:n { @@_begin }
\flag_clear:n { @@_end }
- \seq_set_from_function:NnN \l_@@_internal_seq
+ \cs_set_eq:NN \@@_tmp:w \scan_stop:
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
{
\int_step_function:nnN { \l_@@_min_submatch_int }
- { \l_@@_submatch_int - 1 }
+ { \l_@@_submatch_int - 1 } \@@_extract_seq_aux:n
+ \@@_tmp:w
}
- \@@_extract_seq_aux:n
\int_set:Nn \l_@@_added_begin_int
{ \flag_height:n { @@_begin } }
\int_set:Nn \l_@@_added_end_int
{ \flag_height:n { @@_end } }
+ \tex_afterassignment:D \@@_extract_check:w
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
+ { \g_@@_internal_tl \if_false: { \fi: } }
\int_compare:nNnT
{ \l_@@_added_begin_int + \l_@@_added_end_int } > 0
{
@@ -6559,10 +6567,10 @@
{ \int_use:N \l_@@_added_begin_int }
{ \int_use:N \l_@@_added_end_int }
}
- \seq_set_map_x:NNn \l_@@_internal_seq \l_@@_internal_seq {##1}
- \exp_args:NNNo
- \group_end:
- \tl_set:Nn #1 { \l_@@_internal_seq }
+ \group_end:
+ \cs_set_eq:NN \@@_tmp:w \@@_extract_map_loop:w
+ \seq_set_from_function:NnN #1
+ { \@@_extract_map:N } \exp_not:n
}
% \end{macrocode}
% \end{macro}
@@ -6575,6 +6583,7 @@
% \begin{macrocode}
\cs_new:Npn \@@_extract_seq_aux:n #1
{
+ \@@_tmp:w { }
\exp_after:wN \@@_extract_seq_aux:ww
\int_value:w \@@_submatch_balance:n {#1} ; #1;
}
@@ -6599,6 +6608,108 @@
% \end{macrocode}
% \end{macro}
%
+% \begin{macro}
+% {
+% \@@_extract_check:w, \@@_extract_check:n,
+% \@@_extract_check_loop:w, \@@_extract_check_end:w
+% }
+% In \cs{@@_group_end_extract_seq:N} we had to expand
+% \cs{g_@@_internal_tl} to turn \cs{if_false:} constructions into
+% actual begin-group and end-group tokens. This is done with a
+% \cs{__kernel_tl_gset:Nx} assignment, and \cs{@@_extract_check:w} is
+% run immediately after this assignment ends, thanks to the
+% \tn{afterassignment} primitive. If all of the items were properly
+% balanced (enough begin-group tokens before end-group tokens, so |}{|
+% is not) then \cs{@@_extract_check:w} is called just before the
+% closing brace of the \cs{__kernel_tl_gset:Nx} (thanks to our sneaky
+% \cs{if_false:} |{| \cs{fi:} |}| construction), and finds that there
+% is nothing left to expand. If any of the items is unbalanced, the
+% assignment gets ended early by an extra end-group token, and our
+% check finds more tokens needing to be expanded in a new
+% \cs{__kernel_tl_gset:Nx} assignment. We need to add a begin-group
+% and an end-group tokens to the unbalanced item, namely to the last
+% item found so far, which we reach through a loop.
+% \begin{macrocode}
+\cs_new_protected:Npn \@@_extract_check:w
+ {
+ \exp_after:wN \@@_extract_check:n
+ \exp_after:wN { \if_false: } \fi:
+ }
+\cs_new_protected:Npn \@@_extract_check:n #1
+ {
+ \tl_if_empty:nF {#1}
+ {
+ \int_incr:N \l_@@_added_begin_int
+ \int_incr:N \l_@@_added_end_int
+ \tex_afterassignment:D \@@_extract_check:w
+ \__kernel_tl_gset:Nx \g_@@_internal_tl
+ {
+ \exp_after:wN \@@_extract_check_loop:w
+ \g_@@_internal_tl
+ \@@_tmp:w \@@_extract_check_end:w
+ #1
+ }
+ }
+ }
+\cs_new:Npn \@@_extract_check_loop:w #1 \@@_tmp:w #2
+ {
+ #2
+ \exp_not:o {#1}
+ \@@_tmp:w { }
+ \@@_extract_check_loop:w \prg_do_nothing:
+ }
+% \end{macrocode}
+% Arguments of \cs{@@_extract_check_end:w} are: |#1| is the part of
+% the item before the extra end-group token; |#2| is junk; |#3| is
+% \cs{prg_do_nothing:} followed by the not-yet-expanded part of the
+% item after the extra end-group token. In the replacement text, the
+% first brace and the \cs{if_false:} |{| \cs{fi:} |}| construction are
+% the added begin-group and end-group tokens (the latter being not-yet
+% expanded, just like~|#3|), while the closing brace after
+% \cs{exp_not:o} |{#1}| replaces the extra end-group token that had
+% ended the assignment early. In particular this means that the
+% character code of that end-group token is lost.
+% \begin{macrocode}
+\cs_new:Npn \@@_extract_check_end:w
+ \exp_not:o #1#2 \@@_extract_check_loop:w #3 \@@_tmp:w
+ {
+ { \exp_not:o {#1} }
+ #3
+ \if_false: { \fi: }
+ \@@_tmp:w
+ }
+% \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+% {
+% \@@_extract_map:N,
+% \@@_extract_map_aux:NNn,
+% \@@_extract_map_loop:w
+% }
+% This receives a |seq| internal function and maps it over all items
+% in \cs{g_@@_internal_tl}. This token list takes the form
+% \cs{@@_tmp:w} |{}| \meta{item_1} \cs{@@_tmp:w} |{}| \meta{item_2}
+% \ldots{} \cs{@@_tmp:w}, and the calling code has set \cs{@@_tmp:w}
+% equal to \cs{@@_extract_map_loop:w}. The loop is otherwise pretty
+% standard, with \cs{prg_do_nothing:} to avoid losing braces.
+% \begin{macrocode}
+\cs_new:Npn \@@_extract_map:N #1
+ {
+ \exp_after:wN \@@_extract_map_aux:NNn
+ \exp_after:wN #1
+ \g_@@_internal_tl \use_none:nnn
+ }
+\cs_new:Npn \@@_extract_map_aux:NNn #1#2#3
+ { #3 #2 #1 \prg_do_nothing: }
+\cs_new:Npn \@@_extract_map_loop:w #1#2 \@@_tmp:w #3
+ {
+ \exp_after:wN #1 \exp_after:wN {#2}
+ #3 \@@_extract_map_loop:w #1 \prg_do_nothing:
+ }
+% \end{macrocode}
+% \end{macro}
+%
% \begin{macro}{\@@_extract:}
% Our task here is to store the list of end-points of submatches, and
% store them in appropriate array entries, from
diff --git a/l3kernel/testfiles/m3regex006.lvt b/l3kernel/testfiles/m3regex006.lvt
index a9c9a1800..e1848f83f 100644
--- a/l3kernel/testfiles/m3regex006.lvt
+++ b/l3kernel/testfiles/m3regex006.lvt
@@ -106,8 +106,9 @@
\test:nnNTF { ([^c].)a } { ab{{c}}ade } { \TRUE } { \ERROR }
\test:nnNTF { ([^c]..)a } { ab{c}ade } { \TRUE } { \ERROR }
\test:nnNTF { [c-z] } { ab{c}ade } { \TRUE } { \ERROR }
- % \test:nnNTF { \# (.). } { a#{b}c#d } { \TRUE } { \ERROR }
\test:nnNTF { \# (.*?)c } { a#{b}c#d } { \TRUE } { \ERROR }
+ \test:nnNTF { \# (.(..).) } { {{a#\XY}{\bbb}c{#{\YY{~#}}{}#}\dd\dd} }
+ { \TRUE } { \ERROR }
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/l3kernel/testfiles/m3regex006.tlg b/l3kernel/testfiles/m3regex006.tlg
index 2f1ea926f..d3c4b2291 100644
--- a/l3kernel/testfiles/m3regex006.tlg
+++ b/l3kernel/testfiles/m3regex006.tlg
@@ -624,6 +624,234 @@ TRUE
|[a] [{b}] [##d] |
|[a] [{b}] [##d] |
|[a] [{b}] [##d] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 3 left, 3 right.
+TRUE
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 3 left, 3 right.
+TRUE
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 3 left, 3 right.
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 3 left, 3 right.
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 12 left, 9 right.
+TRUE
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] [##{\YY { }}] [{\YY { }}] [\YY {}] [{{##}}{}] [{{}}{}] [{}{}] [{{##}\dd \dd }] [{{}\dd \dd }] [\dd \dd ] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 12 left, 9 right.
+TRUE
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] [##{\YY { }}] [{\YY { }}] [\YY {}] [{{##}}{}] [{{}}{}] [{}{}] [{{##}\dd \dd }] [{{}\dd \dd }] [\dd \dd ] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 12 left, 9 right.
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] [##{\YY { }}] [{\YY { }}] [\YY {}] [{{##}}{}] [{{}}{}] [{}{}] [{{##}\dd \dd }] [{{}\dd \dd }] [\dd \dd ] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 12 left, 9 right.
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] [##{\YY { }}] [{\YY { }}] [\YY {}] [{{##}}{}] [{{}}{}] [{}{}] [{{##}\dd \dd }] [{{}\dd \dd }] [\dd \dd ] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 8 left, 9 right.
+TRUE
+|[{{a}}] [{\XY }{\bbb }] [{}{}] [{}c{}] [{\YY { }}] [\YY {}] [] [{{}}{}] [{}{}] [] [{{}\dd \dd }] [\dd \dd ] [] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 8 left, 9 right.
+TRUE
+|[{{a}}] [{\XY }{\bbb }] [{}{}] [{}c{}] [{\YY { }}] [\YY {}] [] [{{}}{}] [{}{}] [] [{{}\dd \dd }] [\dd \dd ] [] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 8 left, 9 right.
+|[{{a}}] [{\XY }{\bbb }] [{}{}] [{}c{}] [{\YY { }}] [\YY {}] [] [{{}}{}] [{}{}] [] [{{}\dd \dd }] [\dd \dd ] [] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 8 left, 9 right.
+|[{{a}}] [{\XY }{\bbb }] [{}{}] [{}c{}] [{\YY { }}] [\YY {}] [] [{{}}{}] [{}{}] [] [{{}\dd \dd }] [\dd \dd ] [] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 3 left, 3 right.
+TRUE
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 3 left, 3 right.
+TRUE
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 3 left, 3 right.
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 3 left, 3 right.
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 12 left, 9 right.
+TRUE
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] [##{\YY { }}] [{\YY { }}] [\YY {}] [{{##}}{}] [{{}}{}] [{}{}] [{{##}\dd \dd }] [{{}\dd \dd }] [\dd \dd ] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 12 left, 9 right.
+TRUE
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] [##{\YY { }}] [{\YY { }}] [\YY {}] [{{##}}{}] [{{}}{}] [{}{}] [{{##}\dd \dd }] [{{}\dd \dd }] [\dd \dd ] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 12 left, 9 right.
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] [##{\YY { }}] [{\YY { }}] [\YY {}] [{{##}}{}] [{{}}{}] [{}{}] [{{##}\dd \dd }] [{{}\dd \dd }] [\dd \dd ] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 12 left, 9 right.
+|[{##\XY }{\bbb }] [{\XY }{\bbb }] [{}{}] [##{\YY { }}] [{\YY { }}] [\YY {}] [{{##}}{}] [{{}}{}] [{}{}] [{{##}\dd \dd }] [{{}\dd \dd }] [\dd \dd ] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 8 left, 9 right.
+TRUE
+|[{{a}}] [{\XY }{\bbb }] [{}{}] [{}c{}] [{\YY { }}] [\YY {}] [] [{{}}{}] [{}{}] [] [{{}\dd \dd }] [\dd \dd ] [] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 8 left, 9 right.
+TRUE
+|[{{a}}] [{\XY }{\bbb }] [{}{}] [{}c{}] [{\YY { }}] [\YY {}] [] [{{}}{}] [{}{}] [] [{{}\dd \dd }] [\dd \dd ] [] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 8 left, 9 right.
+|[{{a}}] [{\XY }{\bbb }] [{}{}] [{}c{}] [{\YY { }}] [\YY {}] [] [{{}}{}] [{}{}] [] [{{}\dd \dd }] [\dd \dd ] [] |
+! LaTeX3 Error: Missing brace inserted when splitting or extracting
+(LaTeX3) submatches.
+For immediate help type H <return>.
+ ...
+l. ... }
+LaTeX was asked to do some regular expression operation, and the resulting
+token list would not have the same number of begin-group and end-group tokens.
+Braces were inserted: 8 left, 9 right.
+|[{{a}}] [{\XY }{\bbb }] [{}{}] [{}c{}] [{\YY { }}] [\YY {}] [] [{{}}{}] [{}{}] [] [{{}\dd \dd }] [\dd \dd ] [] |
============================================================
============================================================
TEST 4: Replace once, all and split
More information about the latex3-commits
mailing list.