Skip to content

Commit e7ac10c

Browse files
jdesgatsagentzh
authored andcommitted
change: unmatched captures are set to false in the captures table.
Now unmatched capture groups in `ngx.re.match`, `ngx.re.gmatch`, `ngx.re.sub`, and `ngx.re.gsub` are set to `false` rather than nil. This prevents to create table with holes that don't play nice with `ipairs` or `#` operators. For consistency, unmatched trailing captures (for instance in `ngx.re.match("hello", "(hello)(.+)?")`) are also set to `false`. Named captures are not affected.
1 parent 701efd5 commit e7ac10c

8 files changed

+109
-39
lines changed

README.markdown

+4-5
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,6 @@ TODO
861861
* cosocket: pool-based backend concurrency level control: implement automatic `connect` queueing when the backend concurrency exceeds its connection pool limit.
862862
* cosocket: review and merge aviramc's [patch](https://github.com/openresty/lua-nginx-module/pull/290) for adding the `bsdrecv` method.
863863
* add new API function `ngx.resp.add_header` to emulate the standard `add_header` config directive.
864-
* [ngx.re](#ngxrematch) API: use `false` instead of `nil` in the resulting match table to indicate non-existent submatch captures, such that we can avoid "holes" in the array table.
865864
* review and apply Jader H. Silva's patch for `ngx.re.split()`.
866865
* review and apply vadim-pavlov's patch for [ngx.location.capture](#ngxlocationcapture)'s `extra_headers` option
867866
* use `ngx_hash_t` to optimize the built-in header look-up process for [ngx.req.set_header](#ngxreqset_header), [ngx.header.HEADER](#ngxheaderheader), and etc.
@@ -5524,16 +5523,16 @@ and are returned in the same Lua table as key-value pairs as the numbered captur
55245523
-- m["remaining"] == "234"
55255524
```
55265525

5527-
Unmatched subpatterns will have `nil` values in their `captures` table fields.
5526+
Unmatched subpatterns will have `false` values in their `captures` table fields.
55285527

55295528
```lua
55305529

55315530
local m, err = ngx.re.match("hello, world", "(world)|(hello)|(?<named>howdy)")
55325531
-- m[0] == "hello"
5533-
-- m[1] == nil
5532+
-- m[1] == false
55345533
-- m[2] == "hello"
5535-
-- m[3] == nil
5536-
-- m["named"] == nil
5534+
-- m[3] == false
5535+
-- m["named"] == false
55375536
```
55385537

55395538
Specify `options` to control how the match operation will be performed. The following option characters are supported:

doc/HttpLuaModule.wiki

+4-5
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,6 @@ phases.
698698
* cosocket: pool-based backend concurrency level control: implement automatic <code>connect</code> queueing when the backend concurrency exceeds its connection pool limit.
699699
* cosocket: review and merge aviramc's [https://github.com/openresty/lua-nginx-module/pull/290 patch] for adding the <code>bsdrecv</code> method.
700700
* add new API function <code>ngx.resp.add_header</code> to emulate the standard <code>add_header</code> config directive.
701-
* [[#ngx.re.match|ngx.re]] API: use <code>false</code> instead of <code>nil</code> in the resulting match table to indicate non-existent submatch captures, such that we can avoid "holes" in the array table.
702701
* review and apply Jader H. Silva's patch for <code>ngx.re.split()</code>.
703702
* review and apply vadim-pavlov's patch for [[#ngx.location.capture|ngx.location.capture]]'s <code>extra_headers</code> option
704703
* use <code>ngx_hash_t</code> to optimize the built-in header look-up process for [[#ngx.req.set_header|ngx.req.set_header]], [[#ngx.header.HEADER|ngx.header.HEADER]], and etc.
@@ -4593,15 +4592,15 @@ and are returned in the same Lua table as key-value pairs as the numbered captur
45934592
-- m["remaining"] == "234"
45944593
</geshi>
45954594
4596-
Unmatched subpatterns will have <code>nil</code> values in their <code>captures</code> table fields.
4595+
Unmatched subpatterns will have <code>false</code> values in their <code>captures</code> table fields.
45974596
45984597
<geshi lang="lua">
45994598
local m, err = ngx.re.match("hello, world", "(world)|(hello)|(?<named>howdy)")
46004599
-- m[0] == "hello"
4601-
-- m[1] == nil
4600+
-- m[1] == false
46024601
-- m[2] == "hello"
4603-
-- m[3] == nil
4604-
-- m["named"] == nil
4602+
-- m[3] == false
4603+
-- m["named"] == false
46054604
</geshi>
46064605
46074606
Specify <code>options</code> to control how the match operation will be performed. The following option characters are supported:

src/ngx_http_lua_regex.c

+17-12
Original file line numberDiff line numberDiff line change
@@ -592,14 +592,14 @@ ngx_http_lua_ngx_re_match_helper(lua_State *L, int wantcaps)
592592
}
593593

594594
if (res_tb_idx == 0) {
595-
lua_createtable(L, rc /* narr */, 0 /* nrec */);
595+
lua_createtable(L, re_comp.captures /* narr */, name_count /* nrec */);
596596
res_tb_idx = lua_gettop(L);
597597
}
598598

599-
for (i = 0, n = 0; i < rc; i++, n += 2) {
599+
for (i = 0, n = 0; i <= re_comp.captures; i++, n += 2) {
600600
dd("capture %d: %d %d", i, cap[n], cap[n + 1]);
601-
if (cap[n] < 0) {
602-
lua_pushnil(L);
601+
if (i >= rc || cap[n] < 0) {
602+
lua_pushboolean(L, 0);
603603

604604
} else {
605605
lua_pushlstring(L, (char *) &subj.data[cap[n]],
@@ -1116,12 +1116,12 @@ ngx_http_lua_ngx_re_gmatch_iterator(lua_State *L)
11161116

11171117
dd("rc = %d", (int) rc);
11181118

1119-
lua_createtable(L, rc /* narr */, 0 /* nrec */);
1119+
lua_createtable(L, ctx->ncaptures /* narr */, name_count /* nrec */);
11201120

1121-
for (i = 0, n = 0; i < rc; i++, n += 2) {
1121+
for (i = 0, n = 0; i <= ctx->ncaptures; i++, n += 2) {
11221122
dd("capture %d: %d %d", i, cap[n], cap[n + 1]);
1123-
if (cap[n] < 0) {
1124-
lua_pushnil(L);
1123+
if (i >= rc || cap[n] < 0) {
1124+
lua_pushboolean(L, 0);
11251125

11261126
} else {
11271127
lua_pushlstring(L, (char *) &subj.data[cap[n]],
@@ -1750,12 +1750,12 @@ ngx_http_lua_ngx_re_sub_helper(lua_State *L, unsigned global)
17501750
if (func) {
17511751
lua_pushvalue(L, 3);
17521752

1753-
lua_createtable(L, rc - 1 /* narr */, 1 /* nrec */);
1753+
lua_createtable(L, re_comp.captures /* narr */, name_count /* nrec */);
17541754

1755-
for (i = 0, n = 0; i < rc; i++, n += 2) {
1755+
for (i = 0, n = 0; i <= re_comp.captures; i++, n += 2) {
17561756
dd("capture %d: %d %d", (int) i, cap[n], cap[n + 1]);
1757-
if (cap[n] < 0) {
1758-
lua_pushnil(L);
1757+
if (i >= rc || cap[n] < 0) {
1758+
lua_pushboolean(L, 0);
17591759

17601760
} else {
17611761
lua_pushlstring(L, (char *) &subj.data[cap[n]],
@@ -2071,6 +2071,11 @@ ngx_http_lua_re_collect_named_captures(lua_State *L, int res_tb_idx,
20712071
}
20722072

20732073
if (flags & NGX_LUA_RE_MODE_DUPNAMES) {
2074+
/* unmatched groups are not stored in tables in DUPNAMES mode */
2075+
if (!lua_toboolean(L, -1)) {
2076+
lua_pop(L, 1);
2077+
continue;
2078+
}
20742079

20752080
lua_getfield(L, -2, name); /* big_tb cap small_tb */
20762081

t/034-match.t

+26-5
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ error: .*?unknown flag "H" \(flags "Hm"\)
409409
GET /re
410410
--- response_body
411411
hello
412-
nil
412+
false
413413
hello
414414

415415

@@ -814,7 +814,7 @@ hello-1234
814814

815815

816816

817-
=== TEST 38: named captures are nil
817+
=== TEST 38: named captures are false
818818
--- config
819819
location /re {
820820
content_by_lua '
@@ -834,10 +834,10 @@ hello-1234
834834
GET /re
835835
--- response_body
836836
hello
837-
nil
837+
false
838838
hello
839-
nil
840-
nil
839+
false
840+
false
841841

842842

843843

@@ -1148,3 +1148,24 @@ failed to match
11481148
1234
11491149
--- SKIP
11501150
1151+
1152+
1153+
=== TEST 49: trailing captures are false
1154+
--- config
1155+
location /re {
1156+
content_by_lua '
1157+
local m = ngx.re.match("hello", "(hello)(.+)?")
1158+
if m then
1159+
ngx.say(m[0])
1160+
ngx.say(m[1])
1161+
ngx.say(m[2])
1162+
end
1163+
';
1164+
}
1165+
--- request
1166+
GET /re
1167+
--- response_body
1168+
hello
1169+
hello
1170+
false
1171+

t/035-gmatch.t

+4-4
Original file line numberDiff line numberDiff line change
@@ -582,13 +582,13 @@ matched: []
582582
--- response_body
583583
1234
584584
1234
585-
nil
585+
false
586586
1234
587-
nil
587+
false
588588
abcd
589-
nil
589+
false
590590
abcd
591-
nil
591+
false
592592
abcd
593593

594594

t/036-sub.t

+24-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use Test::Nginx::Socket::Lua;
88

99
repeat_each(2);
1010

11-
plan tests => repeat_each() * (blocks() * 2 + 18);
11+
plan tests => repeat_each() * (blocks() * 2 + 19);
1212

1313
#no_diff();
1414
no_long_string();
@@ -732,3 +732,26 @@ GET /t
732732
bad argument type
733733
NYI
734734
735+
736+
737+
=== TEST 33: function replace (false for groups)
738+
--- config
739+
location /re {
740+
content_by_lua '
741+
local repl = function (m)
742+
print("group 1: ", m[2])
743+
return "[" .. m[0] .. "] [" .. m[1] .. "]"
744+
end
745+
746+
local s, n = ngx.re.sub("hello, 34", "([0-9])|(world)", repl)
747+
ngx.say(s)
748+
ngx.say(n)
749+
';
750+
}
751+
--- request
752+
GET /re
753+
--- response_body
754+
hello, [3] [3]4
755+
1
756+
--- error_log
757+
group 1: false

t/037-gsub.t

+25-2
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ use Test::Nginx::Socket::Lua;
44
#worker_connections(1014);
55
#master_on();
66
#workers(2);
7-
log_level('warn');
7+
#log_level('warn');
88

99
repeat_each(2);
1010

11-
plan tests => repeat_each() * (blocks() * 2 + 16);
11+
plan tests => repeat_each() * (blocks() * 2 + 17);
1212

1313
#no_diff();
1414
no_long_string();
@@ -674,3 +674,26 @@ GET /t
674674
--- no_error_log
675675
[error]
676676
677+
678+
679+
=== TEST 29: function replace (false for groups)
680+
--- config
681+
location /re {
682+
content_by_lua '
683+
local repl = function (m)
684+
print("group 1: ", m[2])
685+
return "[" .. m[0] .. "] [" .. m[1] .. "]"
686+
end
687+
688+
local s, n = ngx.re.gsub("hello, 34", "([0-9])|(world)", repl)
689+
ngx.say(s)
690+
ngx.say(n)
691+
';
692+
}
693+
--- request
694+
GET /re
695+
--- response_body
696+
hello, [3] [3][4] [4]
697+
2
698+
--- error_log
699+
group 1: false

t/038-match-o.t

+5-5
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ error: pcre_compile() failed: missing ) in "(abc"
384384
GET /re
385385
--- response_body
386386
hello
387-
nil
387+
false
388388
hello
389389

390390

@@ -716,7 +716,7 @@ hello-1234
716716

717717

718718

719-
=== TEST 33: named captures are nil
719+
=== TEST 33: named captures are false
720720
--- config
721721
location /re {
722722
content_by_lua '
@@ -736,8 +736,8 @@ hello-1234
736736
GET /re
737737
--- response_body
738738
hello
739-
nil
739+
false
740740
hello
741-
nil
742-
nil
741+
false
742+
false
743743

0 commit comments

Comments
 (0)