aboutsummaryrefslogtreecommitdiffstats
path: root/src/doc.c
diff options
context:
space:
mode:
authorPaul Eggert2016-08-13 23:26:00 -0500
committerPaul Eggert2016-08-13 23:32:36 -0500
commit2791580f5eaa65948a13ea4ea4952d03b4da795b (patch)
tree843389a61dd4c8d85e326b27a6bedee4e87467ac /src/doc.c
parentc1021ba910efac334027ad03ff6aed64306fad3c (diff)
downloademacs-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.c169
1 files changed, 77 insertions, 92 deletions
diff --git a/src/doc.c b/src/doc.c
index 6ffdad10f03..e591ffca172 100644
--- a/src/doc.c
+++ b/src/doc.c
@@ -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