Do not allow out-of-range character position in Fcompare_strings.

* src/fns.c (validate_subarray): Add prototype.
(Fcompare_substring): Use validate_subarray to check ranges.
Adjust comment to mention that the semantics was changed.  Also see
http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00447.html.
* lisp/files.el (dir-locals-find-file, file-relative-name):
* lisp/info.el (Info-complete-menu-item):
* lisp/minibuffer.el (completion-table-subvert): Prefer string-prefix-p
to compare-strings to avoid out-of-range errors.
* lisp/subr.el (string-prefix-p): Adjust to match strict range
checking in compare-strings.
* test/automated/fns-tests.el (fns-tests-compare-string): New test.
This commit is contained in:
Dmitry Antipov 2014-06-25 14:36:51 +04:00
parent 9a214b9800
commit 5697ca55cb
9 changed files with 88 additions and 62 deletions

View file

@ -1,3 +1,12 @@
2014-06-25 Dmitry Antipov <dmantipov@yandex.ru>
* files.el (dir-locals-find-file, file-relative-name):
* info.el (Info-complete-menu-item):
* minibuffer.el (completion-table-subvert): Prefer string-prefix-p
to compare-strings to avoid out-of-range errors.
* subr.el (string-prefix-p): Adjust to match strict range
checking in compare-strings.
2014-06-24 Leonard Randall <leonard.a.randall@gmail.com> (tiny change) 2014-06-24 Leonard Randall <leonard.a.randall@gmail.com> (tiny change)
* textmodes/reftex-parse.el (reftex-using-biblatex-p): Make search * textmodes/reftex-parse.el (reftex-using-biblatex-p): Make search

View file

@ -3659,10 +3659,9 @@ of no valid cache entry."
;;; (setq locals-file nil)) ;;; (setq locals-file nil))
;; Find the best cached value in `dir-locals-directory-cache'. ;; Find the best cached value in `dir-locals-directory-cache'.
(dolist (elt dir-locals-directory-cache) (dolist (elt dir-locals-directory-cache)
(when (and (eq t (compare-strings file nil (length (car elt)) (when (and (string-prefix-p (car elt) file
(car elt) nil nil
(memq system-type (memq system-type
'(windows-nt cygwin ms-dos)))) '(windows-nt cygwin ms-dos)))
(> (length (car elt)) (length (car dir-elt)))) (> (length (car elt)) (length (car dir-elt))))
(setq dir-elt elt))) (setq dir-elt elt)))
(if (and dir-elt (if (and dir-elt
@ -4507,18 +4506,14 @@ on a DOS/Windows machine, it returns FILENAME in expanded form."
(let ((ancestor ".") (let ((ancestor ".")
(filename-dir (file-name-as-directory filename))) (filename-dir (file-name-as-directory filename)))
(while (not (while (not
(or (or (string-prefix-p directory filename-dir fold-case)
(eq t (compare-strings filename-dir nil (length directory) (string-prefix-p directory filename fold-case)))
directory nil nil fold-case))
(eq t (compare-strings filename nil (length directory)
directory nil nil fold-case))))
(setq directory (file-name-directory (substring directory 0 -1)) (setq directory (file-name-directory (substring directory 0 -1))
ancestor (if (equal ancestor ".") ancestor (if (equal ancestor ".")
".." ".."
(concat "../" ancestor)))) (concat "../" ancestor))))
;; Now ancestor is empty, or .., or ../.., etc. ;; Now ancestor is empty, or .., or ../.., etc.
(if (eq t (compare-strings filename nil (length directory) (if (string-prefix-p directory filename fold-case)
directory nil nil fold-case))
;; We matched within FILENAME's directory part. ;; We matched within FILENAME's directory part.
;; Add the rest of FILENAME onto ANCESTOR. ;; Add the rest of FILENAME onto ANCESTOR.
(let ((rest (substring filename (length directory)))) (let ((rest (substring filename (length directory))))

View file

@ -2691,9 +2691,7 @@ Because of ambiguities, this should be concatenated with something like
(equal (nth 1 Info-complete-cache) Info-current-node) (equal (nth 1 Info-complete-cache) Info-current-node)
(equal (nth 2 Info-complete-cache) Info-complete-next-re) (equal (nth 2 Info-complete-cache) Info-complete-next-re)
(equal (nth 5 Info-complete-cache) Info-complete-nodes) (equal (nth 5 Info-complete-cache) Info-complete-nodes)
(let ((prev (nth 3 Info-complete-cache))) (string-prefix-p (nth 3 Info-complete-cache) string) t)
(eq t (compare-strings string 0 (length prev)
prev 0 nil t))))
;; We can reuse the previous list. ;; We can reuse the previous list.
(setq completions (nth 4 Info-complete-cache)) (setq completions (nth 4 Info-complete-cache))
;; The cache can't be used. ;; The cache can't be used.

View file

@ -244,8 +244,7 @@ The result is a completion table which completes strings of the
form (concat S1 S) in the same way as TABLE completes strings of form (concat S1 S) in the same way as TABLE completes strings of
the form (concat S2 S)." the form (concat S2 S)."
(lambda (string pred action) (lambda (string pred action)
(let* ((str (if (eq t (compare-strings string 0 (length s1) s1 nil nil (let* ((str (if (string-prefix-p s1 string completion-ignore-case)
completion-ignore-case))
(concat s2 (substring string (length s1))))) (concat s2 (substring string (length s1)))))
(res (if str (complete-with-action action table str pred)))) (res (if str (complete-with-action action table str pred))))
(when res (when res
@ -257,8 +256,7 @@ the form (concat S2 S)."
(+ beg (- (length s1) (length s2)))) (+ beg (- (length s1) (length s2))))
. ,(and (eq (car-safe res) 'boundaries) (cddr res))))) . ,(and (eq (car-safe res) 'boundaries) (cddr res)))))
((stringp res) ((stringp res)
(if (eq t (compare-strings res 0 (length s2) s2 nil nil (if (string-prefix-p s2 string completion-ignore-case)
completion-ignore-case))
(concat s1 (substring res (length s2))))) (concat s1 (substring res (length s2)))))
((eq action t) ((eq action t)
(let ((bounds (completion-boundaries str table pred ""))) (let ((bounds (completion-boundaries str table pred "")))

View file

@ -3677,12 +3677,14 @@ and replace a sub-expression, e.g.
(setq matches (cons (substring string start l) matches)) ; leftover (setq matches (cons (substring string start l) matches)) ; leftover
(apply #'concat (nreverse matches))))) (apply #'concat (nreverse matches)))))
(defun string-prefix-p (str1 str2 &optional ignore-case) (defun string-prefix-p (prefix string &optional ignore-case)
"Return non-nil if STR1 is a prefix of STR2. "Return non-nil if PREFIX is a prefix of STRING.
If IGNORE-CASE is non-nil, the comparison is done without paying attention If IGNORE-CASE is non-nil, the comparison is done without paying attention
to case differences." to case differences."
(eq t (compare-strings str1 nil nil (let ((prefix-length (length prefix)))
str2 0 (length str1) ignore-case))) (if (> prefix-length (length string)) nil
(eq t (compare-strings prefix 0 prefix-length string
0 prefix-length ignore-case)))))
(defun string-suffix-p (suffix string &optional ignore-case) (defun string-suffix-p (suffix string &optional ignore-case)
"Return non-nil if SUFFIX is a suffix of STRING. "Return non-nil if SUFFIX is a suffix of STRING.

View file

@ -1,3 +1,11 @@
2014-06-25 Dmitry Antipov <dmantipov@yandex.ru>
Do not allow out-of-range character position in Fcompare_strings.
* fns.c (validate_subarray): Add prototype.
(Fcompare_substring): Use validate_subarray to check ranges.
Adjust comment to mention that the semantics was changed. Also see
http://lists.gnu.org/archive/html/emacs-devel/2014-06/msg00447.html.
2014-06-24 Paul Eggert <eggert@cs.ucla.edu> 2014-06-24 Paul Eggert <eggert@cs.ucla.edu>
Be more consistent about the 'Qfoo' naming convention. Be more consistent about the 'Qfoo' naming convention.

View file

@ -50,7 +50,9 @@ static Lisp_Object Qcodeset, Qdays, Qmonths, Qpaper;
static Lisp_Object Qmd5, Qsha1, Qsha224, Qsha256, Qsha384, Qsha512; static Lisp_Object Qmd5, Qsha1, Qsha224, Qsha256, Qsha384, Qsha512;
static bool internal_equal (Lisp_Object, Lisp_Object, int, bool, Lisp_Object); static bool internal_equal (Lisp_Object, Lisp_Object, int, bool, Lisp_Object);
static void validate_subarray (Lisp_Object, Lisp_Object, Lisp_Object,
ptrdiff_t, EMACS_INT *, EMACS_INT *);
DEFUN ("identity", Fidentity, Sidentity, 1, 1, 0, DEFUN ("identity", Fidentity, Sidentity, 1, 1, 0,
doc: /* Return the argument unchanged. */) doc: /* Return the argument unchanged. */)
(Lisp_Object arg) (Lisp_Object arg)
@ -232,6 +234,7 @@ string STR1, compare the part between START1 (inclusive) and END1
\(exclusive). If START1 is nil, it defaults to 0, the beginning of \(exclusive). If START1 is nil, it defaults to 0, the beginning of
the string; if END1 is nil, it defaults to the length of the string. the string; if END1 is nil, it defaults to the length of the string.
Likewise, in string STR2, compare the part between START2 and END2. Likewise, in string STR2, compare the part between START2 and END2.
Like in `substring', negative values are counted from the end.
The strings are compared by the numeric values of their characters. The strings are compared by the numeric values of their characters.
For instance, STR1 is "less than" STR2 if its first differing For instance, STR1 is "less than" STR2 if its first differing
@ -244,43 +247,25 @@ If string STR1 is less, the value is a negative number N;
- 1 - N is the number of characters that match at the beginning. - 1 - N is the number of characters that match at the beginning.
If string STR1 is greater, the value is a positive number N; If string STR1 is greater, the value is a positive number N;
N - 1 is the number of characters that match at the beginning. */) N - 1 is the number of characters that match at the beginning. */)
(Lisp_Object str1, Lisp_Object start1, Lisp_Object end1, Lisp_Object str2, Lisp_Object start2, Lisp_Object end2, Lisp_Object ignore_case) (Lisp_Object str1, Lisp_Object start1, Lisp_Object end1, Lisp_Object str2,
Lisp_Object start2, Lisp_Object end2, Lisp_Object ignore_case)
{ {
register ptrdiff_t end1_char, end2_char; EMACS_INT from1, to1, from2, to2;
register ptrdiff_t i1, i1_byte, i2, i2_byte; ptrdiff_t i1, i1_byte, i2, i2_byte;
CHECK_STRING (str1); CHECK_STRING (str1);
CHECK_STRING (str2); CHECK_STRING (str2);
if (NILP (start1))
start1 = make_number (0);
if (NILP (start2))
start2 = make_number (0);
CHECK_NATNUM (start1);
CHECK_NATNUM (start2);
if (! NILP (end1))
CHECK_NATNUM (end1);
if (! NILP (end2))
CHECK_NATNUM (end2);
end1_char = SCHARS (str1); validate_subarray (str1, start1, end1, SCHARS (str1), &from1, &to1);
if (! NILP (end1) && end1_char > XINT (end1)) validate_subarray (str2, start2, end2, SCHARS (str2), &from2, &to2);
end1_char = XINT (end1);
if (end1_char < XINT (start1))
args_out_of_range (str1, start1);
end2_char = SCHARS (str2); i1 = from1;
if (! NILP (end2) && end2_char > XINT (end2)) i2 = from2;
end2_char = XINT (end2);
if (end2_char < XINT (start2))
args_out_of_range (str2, start2);
i1 = XINT (start1);
i2 = XINT (start2);
i1_byte = string_char_to_byte (str1, i1); i1_byte = string_char_to_byte (str1, i1);
i2_byte = string_char_to_byte (str2, i2); i2_byte = string_char_to_byte (str2, i2);
while (i1 < end1_char && i2 < end2_char) while (i1 < to1 && i2 < to2)
{ {
/* When we find a mismatch, we must compare the /* When we find a mismatch, we must compare the
characters, not just the bytes. */ characters, not just the bytes. */
@ -307,12 +292,8 @@ If string STR1 is greater, the value is a positive number N;
if (! NILP (ignore_case)) if (! NILP (ignore_case))
{ {
Lisp_Object tem; c1 = XINT (Fupcase (make_number (c1)));
c2 = XINT (Fupcase (make_number (c2)));
tem = Fupcase (make_number (c1));
c1 = XINT (tem);
tem = Fupcase (make_number (c2));
c2 = XINT (tem);
} }
if (c1 == c2) if (c1 == c2)
@ -322,15 +303,15 @@ If string STR1 is greater, the value is a positive number N;
past the character that we are comparing; past the character that we are comparing;
hence we don't add or subtract 1 here. */ hence we don't add or subtract 1 here. */
if (c1 < c2) if (c1 < c2)
return make_number (- i1 + XINT (start1)); return make_number (- i1 + from1);
else else
return make_number (i1 - XINT (start1)); return make_number (i1 - from1);
} }
if (i1 < end1_char) if (i1 < to1)
return make_number (i1 - XINT (start1) + 1); return make_number (i1 - from1 + 1);
if (i2 < end2_char) if (i2 < to2)
return make_number (- i1 + XINT (start1) - 1); return make_number (- i1 + from1 - 1);
return Qt; return Qt;
} }

View file

@ -1,3 +1,7 @@
2014-06-25 Dmitry Antipov <dmantipov@yandex.ru>
* automated/fns-tests.el (fns-tests-compare-string): New test.
2014-06-24 Michael Albinus <michael.albinus@gmx.de> 2014-06-24 Michael Albinus <michael.albinus@gmx.de>
* automated/tramp-tests.el (tramp-test26-process-file): Extend test * automated/tramp-tests.el (tramp-test26-process-file): Extend test

View file

@ -69,3 +69,34 @@
(nreverse A) (nreverse A)
(should (equal [nil nil nil nil nil t t t t t] (vconcat A))) (should (equal [nil nil nil nil nil t t t t t] (vconcat A)))
(should (equal [t t t t t nil nil nil nil nil] (vconcat (nreverse A)))))) (should (equal [t t t t t nil nil nil nil nil] (vconcat (nreverse A))))))
(ert-deftest fns-tests-compare-strings ()
(should-error (compare-strings))
(should-error (compare-strings "xyzzy" "xyzzy"))
(should-error (compare-strings "xyzzy" 0 10 "zyxxy" 0 5))
(should-error (compare-strings "xyzzy" 0 5 "zyxxy" -1 2))
(should-error (compare-strings "xyzzy" 'foo nil "zyxxy" 0 1))
(should-error (compare-strings "xyzzy" 0 'foo "zyxxy" 2 3))
(should-error (compare-strings "xyzzy" 0 2 "zyxxy" 'foo 3))
(should-error (compare-strings "xyzzy" nil 3 "zyxxy" 4 'foo))
(should (compare-strings "" nil nil "" nil nil))
(should (compare-strings "" 0 0 "" 0 0))
(should (compare-strings "test" nil nil "test" nil nil))
(should (compare-strings "test" nil nil "test" nil nil t))
(should (compare-strings "test" nil nil "test" nil nil nil))
(should (compare-strings "Test" nil nil "test" nil nil t))
(should (= (compare-strings "Test" nil nil "test" nil nil) -1))
(should (= (compare-strings "Test" nil nil "test" nil nil) -1))
(should (= (compare-strings "test" nil nil "Test" nil nil) 1))
(should (= (compare-strings "foobaz" nil nil "barbaz" nil nil) 1))
(should (= (compare-strings "barbaz" nil nil "foobar" nil nil) -1))
(should (= (compare-strings "foobaz" nil nil "farbaz" nil nil) 2))
(should (= (compare-strings "farbaz" nil nil "foobar" nil nil) -2))
(should (compare-strings "abcxyz" 0 2 "abcprq" 0 2))
(should (compare-strings "abcxyz" 0 -3 "abcprq" 0 -3))
(should (= (compare-strings "abcxyz" 0 6 "abcprq" 0 6) 4))
(should (= (compare-strings "abcprq" 0 6 "abcxyz" 0 6) -4))
(should (compare-strings "xyzzy" -3 4 "azza" -3 3))
(should (compare-strings "こんにちはコンニチハ" nil nil "こんにちはコンニチハ" nil nil))
(should (= (compare-strings "んにちはコンニチハこ" nil nil "こんにちはコンニチハ" nil nil) 1))
(should (= (compare-strings "こんにちはコンニチハ" nil nil "んにちはコンニチハこ" nil nil) -1)))