diff options
Diffstat (limited to 'lstrlib.c')
-rw-r--r-- | lstrlib.c | 130 |
1 files changed, 84 insertions, 46 deletions
@@ -660,25 +660,46 @@ static const char *lmemfind (const char *s1, size_t l1, | |||
660 | } | 660 | } |
661 | 661 | ||
662 | 662 | ||
663 | static void push_onecapture (MatchState *ms, int i, const char *s, | 663 | /* |
664 | const char *e) { | 664 | ** get information about the i-th capture. If there are no captures |
665 | ** and 'i==0', return information about the whole match, which | ||
666 | ** is the range 's'..'e'. If the capture is a string, return | ||
667 | ** its length and put its address in '*cap'. If it is an integer | ||
668 | ** (a position), push it on the stack and return CAP_POSITION. | ||
669 | */ | ||
670 | static size_t get_onecapture (MatchState *ms, int i, const char *s, | ||
671 | const char *e, const char **cap) { | ||
665 | if (i >= ms->level) { | 672 | if (i >= ms->level) { |
666 | if (i == 0) /* ms->level == 0, too */ | 673 | if (i != 0) |
667 | lua_pushlstring(ms->L, s, e - s); /* add whole match */ | ||
668 | else | ||
669 | luaL_error(ms->L, "invalid capture index %%%d", i + 1); | 674 | luaL_error(ms->L, "invalid capture index %%%d", i + 1); |
675 | *cap = s; | ||
676 | return e - s; | ||
670 | } | 677 | } |
671 | else { | 678 | else { |
672 | ptrdiff_t l = ms->capture[i].len; | 679 | ptrdiff_t capl = ms->capture[i].len; |
673 | if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); | 680 | *cap = ms->capture[i].init; |
674 | if (l == CAP_POSITION) | 681 | if (capl == CAP_UNFINISHED) |
682 | luaL_error(ms->L, "unfinished capture"); | ||
683 | else if (capl == CAP_POSITION) | ||
675 | lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); | 684 | lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); |
676 | else | 685 | return capl; |
677 | lua_pushlstring(ms->L, ms->capture[i].init, l); | ||
678 | } | 686 | } |
679 | } | 687 | } |
680 | 688 | ||
681 | 689 | ||
690 | /* | ||
691 | ** Push the i-th capture on the stack. | ||
692 | */ | ||
693 | static void push_onecapture (MatchState *ms, int i, const char *s, | ||
694 | const char *e) { | ||
695 | const char *cap; | ||
696 | ptrdiff_t l = get_onecapture(ms, i, s, e, &cap); | ||
697 | if (l != CAP_POSITION) | ||
698 | lua_pushlstring(ms->L, cap, l); | ||
699 | /* else position was already pushed */ | ||
700 | } | ||
701 | |||
702 | |||
682 | static int push_captures (MatchState *ms, const char *s, const char *e) { | 703 | static int push_captures (MatchState *ms, const char *s, const char *e) { |
683 | int i; | 704 | int i; |
684 | int nlevels = (ms->level == 0 && s) ? 1 : ms->level; | 705 | int nlevels = (ms->level == 0 && s) ? 1 : ms->level; |
@@ -817,60 +838,72 @@ static int gmatch (lua_State *L) { | |||
817 | 838 | ||
818 | static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, | 839 | static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, |
819 | const char *e) { | 840 | const char *e) { |
820 | size_t l, i; | 841 | size_t l; |
821 | lua_State *L = ms->L; | 842 | lua_State *L = ms->L; |
822 | const char *news = lua_tolstring(L, 3, &l); | 843 | const char *news = lua_tolstring(L, 3, &l); |
823 | for (i = 0; i < l; i++) { | 844 | const char *p; |
824 | if (news[i] != L_ESC) | 845 | while ((p = (char *)memchr(news, L_ESC, l)) != NULL) { |
825 | luaL_addchar(b, news[i]); | 846 | luaL_addlstring(b, news, p - news); |
826 | else { | 847 | p++; /* skip ESC */ |
827 | i++; /* skip ESC */ | 848 | if (*p == L_ESC) /* '%%' */ |
828 | if (!isdigit(uchar(news[i]))) { | 849 | luaL_addchar(b, *p); |
829 | if (news[i] != L_ESC) | 850 | else if (*p == '0') /* '%0' */ |
830 | luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); | 851 | luaL_addlstring(b, s, e - s); |
831 | luaL_addchar(b, news[i]); | 852 | else if (isdigit(uchar(*p))) { /* '%n' */ |
832 | } | 853 | const char *cap; |
833 | else if (news[i] == '0') | 854 | ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap); |
834 | luaL_addlstring(b, s, e - s); | 855 | if (resl == CAP_POSITION) |
835 | else { | 856 | luaL_addvalue(b); /* add position to accumulated result */ |
836 | push_onecapture(ms, news[i] - '1', s, e); | 857 | else |
837 | luaL_tolstring(L, -1, NULL); /* if number, convert it to string */ | 858 | luaL_addlstring(b, cap, resl); |
838 | lua_remove(L, -2); /* remove original value */ | ||
839 | luaL_addvalue(b); /* add capture to accumulated result */ | ||
840 | } | ||
841 | } | 859 | } |
860 | else | ||
861 | luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); | ||
862 | l -= p + 1 - news; | ||
863 | news = p + 1; | ||
842 | } | 864 | } |
865 | luaL_addlstring(b, news, l); | ||
843 | } | 866 | } |
844 | 867 | ||
845 | 868 | ||
846 | static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, | 869 | /* |
847 | const char *e, int tr) { | 870 | ** Add the replacement value to the string buffer 'b'. |
871 | ** Return true if the original string was changed. (Function calls and | ||
872 | ** table indexing resulting in nil or false do not change the subject.) | ||
873 | */ | ||
874 | static int add_value (MatchState *ms, luaL_Buffer *b, const char *s, | ||
875 | const char *e, int tr) { | ||
848 | lua_State *L = ms->L; | 876 | lua_State *L = ms->L; |
849 | switch (tr) { | 877 | switch (tr) { |
850 | case LUA_TFUNCTION: { | 878 | case LUA_TFUNCTION: { /* call the function */ |
851 | int n; | 879 | int n; |
852 | lua_pushvalue(L, 3); | 880 | lua_pushvalue(L, 3); /* push the function */ |
853 | n = push_captures(ms, s, e); | 881 | n = push_captures(ms, s, e); /* all captures as arguments */ |
854 | lua_call(L, n, 1); | 882 | lua_call(L, n, 1); /* call it */ |
855 | break; | 883 | break; |
856 | } | 884 | } |
857 | case LUA_TTABLE: { | 885 | case LUA_TTABLE: { /* index the table */ |
858 | push_onecapture(ms, 0, s, e); | 886 | push_onecapture(ms, 0, s, e); /* first capture is the index */ |
859 | lua_gettable(L, 3); | 887 | lua_gettable(L, 3); |
860 | break; | 888 | break; |
861 | } | 889 | } |
862 | default: { /* LUA_TNUMBER or LUA_TSTRING */ | 890 | default: { /* LUA_TNUMBER or LUA_TSTRING */ |
863 | add_s(ms, b, s, e); | 891 | add_s(ms, b, s, e); /* add value to the buffer */ |
864 | return; | 892 | return 1; /* something changed */ |
865 | } | 893 | } |
866 | } | 894 | } |
867 | if (!lua_toboolean(L, -1)) { /* nil or false? */ | 895 | if (!lua_toboolean(L, -1)) { /* nil or false? */ |
868 | lua_pop(L, 1); | 896 | lua_pop(L, 1); /* remove value */ |
869 | lua_pushlstring(L, s, e - s); /* keep original text */ | 897 | luaL_addlstring(b, s, e - s); /* keep original text */ |
898 | return 0; /* no changes */ | ||
870 | } | 899 | } |
871 | else if (!lua_isstring(L, -1)) | 900 | else if (!lua_isstring(L, -1)) |
872 | luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); | 901 | return luaL_error(L, "invalid replacement value (a %s)", |
873 | luaL_addvalue(b); /* add result to accumulator */ | 902 | luaL_typename(L, -1)); |
903 | else { | ||
904 | luaL_addvalue(b); /* add result to accumulator */ | ||
905 | return 1; /* something changed */ | ||
906 | } | ||
874 | } | 907 | } |
875 | 908 | ||
876 | 909 | ||
@@ -883,6 +916,7 @@ static int str_gsub (lua_State *L) { | |||
883 | lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ | 916 | lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ |
884 | int anchor = (*p == '^'); | 917 | int anchor = (*p == '^'); |
885 | lua_Integer n = 0; /* replacement count */ | 918 | lua_Integer n = 0; /* replacement count */ |
919 | int changed = 0; /* change flag */ | ||
886 | MatchState ms; | 920 | MatchState ms; |
887 | luaL_Buffer b; | 921 | luaL_Buffer b; |
888 | luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || | 922 | luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || |
@@ -898,7 +932,7 @@ static int str_gsub (lua_State *L) { | |||
898 | reprepstate(&ms); /* (re)prepare state for new match */ | 932 | reprepstate(&ms); /* (re)prepare state for new match */ |
899 | if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ | 933 | if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ |
900 | n++; | 934 | n++; |
901 | add_value(&ms, &b, src, e, tr); /* add replacement to buffer */ | 935 | changed = add_value(&ms, &b, src, e, tr) | changed; |
902 | src = lastmatch = e; | 936 | src = lastmatch = e; |
903 | } | 937 | } |
904 | else if (src < ms.src_end) /* otherwise, skip one character */ | 938 | else if (src < ms.src_end) /* otherwise, skip one character */ |
@@ -906,8 +940,12 @@ static int str_gsub (lua_State *L) { | |||
906 | else break; /* end of subject */ | 940 | else break; /* end of subject */ |
907 | if (anchor) break; | 941 | if (anchor) break; |
908 | } | 942 | } |
909 | luaL_addlstring(&b, src, ms.src_end-src); | 943 | if (!changed) /* no changes? */ |
910 | luaL_pushresult(&b); | 944 | lua_pushvalue(L, 1); /* return original string */ |
945 | else { /* something changed */ | ||
946 | luaL_addlstring(&b, src, ms.src_end-src); | ||
947 | luaL_pushresult(&b); /* create and return new string */ | ||
948 | } | ||
911 | lua_pushinteger(L, n); /* number of substitutions */ | 949 | lua_pushinteger(L, n); /* number of substitutions */ |
912 | return 2; | 950 | return 2; |
913 | } | 951 | } |