diff options
| author | Paul Eggert | 2016-08-13 23:26:00 -0500 |
|---|---|---|
| committer | Paul Eggert | 2016-08-13 23:32:36 -0500 |
| commit | 2791580f5eaa65948a13ea4ea4952d03b4da795b (patch) | |
| tree | 843389a61dd4c8d85e326b27a6bedee4e87467ac /src/doc.c | |
| parent | c1021ba910efac334027ad03ff6aed64306fad3c (diff) | |
| download | emacs-2791580f5eaa65948a13ea4ea4952d03b4da795b.tar.gz emacs-2791580f5eaa65948a13ea4ea4952d03b4da795b.zip | |
Fix substitute-command-keys unibyte, alloc bugs
* src/doc.c (Fsubstitute_command_keys): Fix some problems with
unibyte strings and with buffer allocation. Make strings
multibyte, to avoid problems with unibyte strings that are not
valid UTF-8 (Bug#24206). Redo buffer allocation so that it is
O(N), not O(N**2). Avoid going past the end of the input string
when given invalid input. Avoid some unlikely problems in
accessing the wrong storage after a GC.
Diffstat (limited to 'src/doc.c')
| -rw-r--r-- | src/doc.c | 169 |
1 files changed, 77 insertions, 92 deletions
| @@ -738,20 +738,18 @@ Otherwise, return a new string. */) | |||
| 738 | unsigned char const *start; | 738 | unsigned char const *start; |
| 739 | ptrdiff_t length, length_byte; | 739 | ptrdiff_t length, length_byte; |
| 740 | Lisp_Object name; | 740 | Lisp_Object name; |
| 741 | bool multibyte; | ||
| 742 | ptrdiff_t nchars; | 741 | ptrdiff_t nchars; |
| 743 | 742 | ||
| 744 | if (NILP (string)) | 743 | if (NILP (string)) |
| 745 | return Qnil; | 744 | return Qnil; |
| 746 | 745 | ||
| 747 | CHECK_STRING (string); | 746 | Lisp_Object str = Fstring_make_multibyte (string); |
| 748 | tem = Qnil; | 747 | tem = Qnil; |
| 749 | keymap = Qnil; | 748 | keymap = Qnil; |
| 750 | name = Qnil; | 749 | name = Qnil; |
| 751 | 750 | ||
| 752 | enum text_quoting_style quoting_style = text_quoting_style (); | 751 | enum text_quoting_style quoting_style = text_quoting_style (); |
| 753 | 752 | ||
| 754 | multibyte = STRING_MULTIBYTE (string); | ||
| 755 | nchars = 0; | 753 | nchars = 0; |
| 756 | 754 | ||
| 757 | /* KEYMAP is either nil (which means search all the active keymaps) | 755 | /* KEYMAP is either nil (which means search all the active keymaps) |
| @@ -760,59 +758,53 @@ Otherwise, return a new string. */) | |||
| 760 | or from a \\<mapname> construct in STRING itself.. */ | 758 | or from a \\<mapname> construct in STRING itself.. */ |
| 761 | keymap = Voverriding_local_map; | 759 | keymap = Voverriding_local_map; |
| 762 | 760 | ||
| 763 | bsize = SBYTES (string); | 761 | ptrdiff_t strbytes = SBYTES (str); |
| 762 | bsize = strbytes; | ||
| 764 | 763 | ||
| 765 | /* Add some room for expansion due to quote replacement. */ | 764 | /* Fixed-size stack buffer. */ |
| 766 | enum { EXTRA_ROOM = 20 }; | 765 | char sbuf[MAX_ALLOCA]; |
| 767 | if (bsize <= STRING_BYTES_BOUND - EXTRA_ROOM) | ||
| 768 | bsize += EXTRA_ROOM; | ||
| 769 | 766 | ||
| 770 | bufp = buf = xmalloc (bsize); | 767 | /* Heap-allocated buffer, if any. */ |
| 768 | char *abuf; | ||
| 771 | 769 | ||
| 772 | strp = SDATA (string); | 770 | /* Extra room for expansion due to replacing ‘\[]’ with ‘M-x ’. */ |
| 773 | while (strp < SDATA (string) + SBYTES (string)) | 771 | enum { EXTRA_ROOM = sizeof "M-x " - sizeof "\\[]" }; |
| 772 | |||
| 773 | if (bsize <= sizeof sbuf - EXTRA_ROOM) | ||
| 774 | { | 774 | { |
| 775 | if (strp[0] == '\\' && strp[1] == '=') | 775 | abuf = NULL; |
| 776 | buf = sbuf; | ||
| 777 | bsize = sizeof sbuf; | ||
| 778 | } | ||
| 779 | else | ||
| 780 | buf = abuf = xpalloc (NULL, &bsize, EXTRA_ROOM, STRING_BYTES_BOUND, 1); | ||
| 781 | bufp = buf; | ||
| 782 | |||
| 783 | strp = SDATA (str); | ||
| 784 | while (strp < SDATA (str) + strbytes) | ||
| 785 | { | ||
| 786 | unsigned char *close_bracket; | ||
| 787 | |||
| 788 | if (strp[0] == '\\' && strp[1] == '=' | ||
| 789 | && strp + 2 < SDATA (str) + strbytes) | ||
| 776 | { | 790 | { |
| 777 | /* \= quotes the next character; | 791 | /* \= quotes the next character; |
| 778 | thus, to put in \[ without its special meaning, use \=\[. */ | 792 | thus, to put in \[ without its special meaning, use \=\[. */ |
| 779 | changed = nonquotes_changed = true; | 793 | changed = nonquotes_changed = true; |
| 780 | strp += 2; | 794 | strp += 2; |
| 781 | if (multibyte) | 795 | /* Fall through to copy one char. */ |
| 782 | { | ||
| 783 | int len; | ||
| 784 | |||
| 785 | STRING_CHAR_AND_LENGTH (strp, len); | ||
| 786 | if (len == 1) | ||
| 787 | *bufp = *strp; | ||
| 788 | else | ||
| 789 | memcpy (bufp, strp, len); | ||
| 790 | strp += len; | ||
| 791 | bufp += len; | ||
| 792 | nchars++; | ||
| 793 | } | ||
| 794 | else | ||
| 795 | *bufp++ = *strp++, nchars++; | ||
| 796 | } | 796 | } |
| 797 | else if (strp[0] == '\\' && strp[1] == '[') | 797 | else if (strp[0] == '\\' && strp[1] == '[' |
| 798 | && (close_bracket | ||
| 799 | = memchr (strp + 2, ']', | ||
| 800 | SDATA (str) + strbytes - (strp + 2)))) | ||
| 798 | { | 801 | { |
| 799 | ptrdiff_t start_idx; | ||
| 800 | bool follow_remap = 1; | 802 | bool follow_remap = 1; |
| 801 | 803 | ||
| 802 | strp += 2; /* skip \[ */ | 804 | start = strp + 2; |
| 803 | start = strp; | 805 | length_byte = close_bracket - start; |
| 804 | start_idx = start - SDATA (string); | 806 | idx = close_bracket + 1 - SDATA (str); |
| 805 | |||
| 806 | while ((strp - SDATA (string) | ||
| 807 | < SBYTES (string)) | ||
| 808 | && *strp != ']') | ||
| 809 | strp++; | ||
| 810 | length_byte = strp - start; | ||
| 811 | |||
| 812 | strp++; /* skip ] */ | ||
| 813 | 807 | ||
| 814 | /* Save STRP in IDX. */ | ||
| 815 | idx = strp - SDATA (string); | ||
| 816 | name = Fintern (make_string ((char *) start, length_byte), Qnil); | 808 | name = Fintern (make_string ((char *) start, length_byte), Qnil); |
| 817 | 809 | ||
| 818 | do_remap: | 810 | do_remap: |
| @@ -827,25 +819,16 @@ Otherwise, return a new string. */) | |||
| 827 | goto do_remap; | 819 | goto do_remap; |
| 828 | } | 820 | } |
| 829 | 821 | ||
| 830 | /* Note the Fwhere_is_internal can GC, so we have to take | 822 | /* Take relocation of string contents into account. */ |
| 831 | relocation of string contents into account. */ | 823 | strp = SDATA (str) + idx; |
| 832 | strp = SDATA (string) + idx; | 824 | start = strp - length_byte - 1; |
| 833 | start = SDATA (string) + start_idx; | ||
| 834 | 825 | ||
| 835 | if (NILP (tem)) /* but not on any keys */ | 826 | if (NILP (tem)) /* but not on any keys */ |
| 836 | { | 827 | { |
| 837 | ptrdiff_t offset = bufp - buf; | ||
| 838 | if (STRING_BYTES_BOUND - 4 < bsize) | ||
| 839 | string_overflow (); | ||
| 840 | buf = xrealloc (buf, bsize += 4); | ||
| 841 | bufp = buf + offset; | ||
| 842 | memcpy (bufp, "M-x ", 4); | 828 | memcpy (bufp, "M-x ", 4); |
| 843 | bufp += 4; | 829 | bufp += 4; |
| 844 | nchars += 4; | 830 | nchars += 4; |
| 845 | if (multibyte) | 831 | length = multibyte_chars_in_text (start, length_byte); |
| 846 | length = multibyte_chars_in_text (start, length_byte); | ||
| 847 | else | ||
| 848 | length = length_byte; | ||
| 849 | goto subst; | 832 | goto subst; |
| 850 | } | 833 | } |
| 851 | else | 834 | else |
| @@ -856,26 +839,20 @@ Otherwise, return a new string. */) | |||
| 856 | } | 839 | } |
| 857 | /* \{foo} is replaced with a summary of the keymap (symbol-value foo). | 840 | /* \{foo} is replaced with a summary of the keymap (symbol-value foo). |
| 858 | \<foo> just sets the keymap used for \[cmd]. */ | 841 | \<foo> just sets the keymap used for \[cmd]. */ |
| 859 | else if (strp[0] == '\\' && (strp[1] == '{' || strp[1] == '<')) | 842 | else if (strp[0] == '\\' && (strp[1] == '{' || strp[1] == '<') |
| 843 | && (close_bracket | ||
| 844 | = memchr (strp + 2, strp[1] == '{' ? '}' : '>', | ||
| 845 | SDATA (str) + strbytes - (strp + 2)))) | ||
| 860 | { | 846 | { |
| 861 | { | 847 | { |
| 848 | bool generate_summary = strp[1] == '{'; | ||
| 862 | /* This is for computing the SHADOWS arg for describe_map_tree. */ | 849 | /* This is for computing the SHADOWS arg for describe_map_tree. */ |
| 863 | Lisp_Object active_maps = Fcurrent_active_maps (Qnil, Qnil); | 850 | Lisp_Object active_maps = Fcurrent_active_maps (Qnil, Qnil); |
| 864 | ptrdiff_t count = SPECPDL_INDEX (); | 851 | ptrdiff_t count = SPECPDL_INDEX (); |
| 865 | 852 | ||
| 866 | strp += 2; /* skip \{ or \< */ | 853 | start = strp + 2; |
| 867 | start = strp; | 854 | length_byte = close_bracket - start; |
| 868 | ptrdiff_t start_idx = start - SDATA (string); | 855 | idx = close_bracket + 1 - SDATA (str); |
| 869 | |||
| 870 | while ((strp - SDATA (string) < SBYTES (string)) | ||
| 871 | && *strp != '}' && *strp != '>') | ||
| 872 | strp++; | ||
| 873 | |||
| 874 | length_byte = strp - start; | ||
| 875 | strp++; /* skip } or > */ | ||
| 876 | |||
| 877 | /* Save STRP in IDX. */ | ||
| 878 | idx = strp - SDATA (string); | ||
| 879 | 856 | ||
| 880 | /* Get the value of the keymap in TEM, or nil if undefined. | 857 | /* Get the value of the keymap in TEM, or nil if undefined. |
| 881 | Do this while still in the user's current buffer | 858 | Do this while still in the user's current buffer |
| @@ -886,12 +863,7 @@ Otherwise, return a new string. */) | |||
| 886 | { | 863 | { |
| 887 | tem = Fsymbol_value (name); | 864 | tem = Fsymbol_value (name); |
| 888 | if (! NILP (tem)) | 865 | if (! NILP (tem)) |
| 889 | { | 866 | tem = get_keymap (tem, 0, 1); |
| 890 | tem = get_keymap (tem, 0, 1); | ||
| 891 | /* Note that get_keymap can GC. */ | ||
| 892 | strp = SDATA (string) + idx; | ||
| 893 | start = SDATA (string) + start_idx; | ||
| 894 | } | ||
| 895 | } | 867 | } |
| 896 | 868 | ||
| 897 | /* Now switch to a temp buffer. */ | 869 | /* Now switch to a temp buffer. */ |
| @@ -912,9 +884,10 @@ Otherwise, return a new string. */) | |||
| 912 | SBYTES (name), 1); | 884 | SBYTES (name), 1); |
| 913 | AUTO_STRING (msg_suffix, "', which is not currently defined.\n"); | 885 | AUTO_STRING (msg_suffix, "', which is not currently defined.\n"); |
| 914 | insert1 (Fsubstitute_command_keys (msg_suffix)); | 886 | insert1 (Fsubstitute_command_keys (msg_suffix)); |
| 915 | if (start[-1] == '<') keymap = Qnil; | 887 | if (!generate_summary) |
| 888 | keymap = Qnil; | ||
| 916 | } | 889 | } |
| 917 | else if (start[-1] == '<') | 890 | else if (!generate_summary) |
| 918 | keymap = tem; | 891 | keymap = tem; |
| 919 | else | 892 | else |
| 920 | { | 893 | { |
| @@ -932,6 +905,7 @@ Otherwise, return a new string. */) | |||
| 932 | } | 905 | } |
| 933 | 906 | ||
| 934 | subst_string: | 907 | subst_string: |
| 908 | tem = Fstring_make_multibyte (tem); | ||
| 935 | start = SDATA (tem); | 909 | start = SDATA (tem); |
| 936 | length = SCHARS (tem); | 910 | length = SCHARS (tem); |
| 937 | length_byte = SBYTES (tem); | 911 | length_byte = SBYTES (tem); |
| @@ -941,15 +915,27 @@ Otherwise, return a new string. */) | |||
| 941 | changed = true; | 915 | changed = true; |
| 942 | { | 916 | { |
| 943 | ptrdiff_t offset = bufp - buf; | 917 | ptrdiff_t offset = bufp - buf; |
| 944 | if (STRING_BYTES_BOUND - length_byte < bsize) | 918 | ptrdiff_t avail = bsize - offset; |
| 919 | ptrdiff_t need = strbytes - idx; | ||
| 920 | if (INT_ADD_WRAPV (need, length_byte + EXTRA_ROOM, &need)) | ||
| 945 | string_overflow (); | 921 | string_overflow (); |
| 946 | buf = xrealloc (buf, bsize += length_byte); | 922 | if (avail < need) |
| 947 | bufp = buf + offset; | 923 | { |
| 924 | abuf = xpalloc (abuf, &bsize, need - avail, | ||
| 925 | STRING_BYTES_BOUND, 1); | ||
| 926 | if (buf == sbuf) | ||
| 927 | memcpy (abuf, sbuf, offset); | ||
| 928 | buf = abuf; | ||
| 929 | bufp = buf + offset; | ||
| 930 | } | ||
| 948 | memcpy (bufp, start, length_byte); | 931 | memcpy (bufp, start, length_byte); |
| 949 | bufp += length_byte; | 932 | bufp += length_byte; |
| 950 | nchars += length; | 933 | nchars += length; |
| 951 | /* Check STRING again in case gc relocated it. */ | 934 | |
| 952 | strp = SDATA (string) + idx; | 935 | /* Take relocation of string contents into account. */ |
| 936 | strp = SDATA (str) + idx; | ||
| 937 | |||
| 938 | continue; | ||
| 953 | } | 939 | } |
| 954 | } | 940 | } |
| 955 | else if ((strp[0] == '`' || strp[0] == '\'') | 941 | else if ((strp[0] == '`' || strp[0] == '\'') |
| @@ -958,7 +944,7 @@ Otherwise, return a new string. */) | |||
| 958 | start = (unsigned char const *) (strp[0] == '`' ? uLSQM : uRSQM); | 944 | start = (unsigned char const *) (strp[0] == '`' ? uLSQM : uRSQM); |
| 959 | length = 1; | 945 | length = 1; |
| 960 | length_byte = sizeof uLSQM - 1; | 946 | length_byte = sizeof uLSQM - 1; |
| 961 | idx = strp - SDATA (string) + 1; | 947 | idx = strp - SDATA (str) + 1; |
| 962 | goto subst_quote; | 948 | goto subst_quote; |
| 963 | } | 949 | } |
| 964 | else if (strp[0] == '`' && quoting_style == STRAIGHT_QUOTING_STYLE) | 950 | else if (strp[0] == '`' && quoting_style == STRAIGHT_QUOTING_STYLE) |
| @@ -967,15 +953,14 @@ Otherwise, return a new string. */) | |||
| 967 | strp++; | 953 | strp++; |
| 968 | nchars++; | 954 | nchars++; |
| 969 | changed = true; | 955 | changed = true; |
| 956 | continue; | ||
| 970 | } | 957 | } |
| 971 | else | 958 | |
| 972 | { | 959 | /* Copy one char. */ |
| 973 | *bufp++ = *strp++; | 960 | do |
| 974 | if (multibyte) | 961 | *bufp++ = *strp++; |
| 975 | while (! CHAR_HEAD_P (*strp)) | 962 | while (! CHAR_HEAD_P (*strp)); |
| 976 | *bufp++ = *strp++; | 963 | nchars++; |
| 977 | nchars++; | ||
| 978 | } | ||
| 979 | } | 964 | } |
| 980 | 965 | ||
| 981 | if (changed) /* don't bother if nothing substituted */ | 966 | if (changed) /* don't bother if nothing substituted */ |
| @@ -997,7 +982,7 @@ Otherwise, return a new string. */) | |||
| 997 | } | 982 | } |
| 998 | else | 983 | else |
| 999 | tem = string; | 984 | tem = string; |
| 1000 | xfree (buf); | 985 | xfree (abuf); |
| 1001 | return tem; | 986 | return tem; |
| 1002 | } | 987 | } |
| 1003 | 988 | ||