diff options
| author | Paul Eggert | 2019-09-22 10:43:21 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-09-22 10:45:14 -0700 |
| commit | 2f600e97e7ca43965f55f019759582d93d8bca73 (patch) | |
| tree | d885dcef77f04a60da6cec56a2750b19a8e64192 | |
| parent | dddff96a585531608d5e8d27375a6363679a9fb5 (diff) | |
| download | emacs-2f600e97e7ca43965f55f019759582d93d8bca73.tar.gz emacs-2f600e97e7ca43965f55f019759582d93d8bca73.zip | |
Avoid crashes when casifying noncontiguous regions
This is a followon fix for Bug#37477.
* lisp/simple.el (region-extract-function):
Use setq here, since the var is now defined in C code.
* src/casefiddle.c (casify_pnc_region): New function.
(Fupcase_region, Fdowncase_region, Fcapitalize_region)
(Fupcase_initials_region): Use it.
(Fupcase_initials_region): Add region-noncontiguous-p flag
for consistency with the others. All uses changed.
(syms_of_casefiddle): Define Qbounds, Vregion_extract_function.
* src/insdel.c (prepare_to_modify_buffer_1):
* src/keyboard.c (command_loop_1):
Use Vregion_extraction_function.
* src/insdel.c (syms_of_insdel): No need to define
Qregion_extract_function.
* test/src/casefiddle-tests.el (casefiddle-oldfunc): New var.
(casefiddle-loopfunc, casefiddle-badfunc): New functions.
(casefiddle-invalid-region-extract-function): New test.
| -rw-r--r-- | etc/NEWS | 3 | ||||
| -rw-r--r-- | lisp/simple.el | 16 | ||||
| -rw-r--r-- | src/casefiddle.c | 104 | ||||
| -rw-r--r-- | src/insdel.c | 4 | ||||
| -rw-r--r-- | src/keyboard.c | 2 | ||||
| -rw-r--r-- | src/search.c | 2 | ||||
| -rw-r--r-- | test/src/casefiddle-tests.el | 17 |
7 files changed, 73 insertions, 75 deletions
| @@ -488,7 +488,8 @@ interface that's more like functions like 'search-forward'. | |||
| 488 | --- | 488 | --- |
| 489 | ** More commands support noncontiguous rectangular regions, namely | 489 | ** More commands support noncontiguous rectangular regions, namely |
| 490 | 'upcase-dwim', 'downcase-dwim', 'capitalize-dwim', 'capitalize-region', | 490 | 'upcase-dwim', 'downcase-dwim', 'capitalize-dwim', 'capitalize-region', |
| 491 | 'replace-string', 'replace-regexp', and 'delimit-columns-region'. | 491 | 'upcase-initials-region', 'replace-string', 'replace-regexp', and |
| 492 | 'delimit-columns-region'. | ||
| 492 | 493 | ||
| 493 | +++ | 494 | +++ |
| 494 | ** When asked to visit a large file, Emacs now offers visiting it literally. | 495 | ** When asked to visit a large file, Emacs now offers visiting it literally. |
diff --git a/lisp/simple.el b/lisp/simple.el index 31e3b2bbaba..ecd7eb797e8 100644 --- a/lisp/simple.el +++ b/lisp/simple.el | |||
| @@ -1087,7 +1087,7 @@ instead of deleted." | |||
| 1087 | :group 'killing | 1087 | :group 'killing |
| 1088 | :version "24.1") | 1088 | :version "24.1") |
| 1089 | 1089 | ||
| 1090 | (defvar region-extract-function | 1090 | (setq region-extract-function |
| 1091 | (lambda (method) | 1091 | (lambda (method) |
| 1092 | (when (region-beginning) | 1092 | (when (region-beginning) |
| 1093 | (cond | 1093 | (cond |
| @@ -1096,19 +1096,7 @@ instead of deleted." | |||
| 1096 | ((eq method 'delete-only) | 1096 | ((eq method 'delete-only) |
| 1097 | (delete-region (region-beginning) (region-end))) | 1097 | (delete-region (region-beginning) (region-end))) |
| 1098 | (t | 1098 | (t |
| 1099 | (filter-buffer-substring (region-beginning) (region-end) method))))) | 1099 | (filter-buffer-substring (region-beginning) (region-end) method)))))) |
| 1100 | "Function to get the region's content. | ||
| 1101 | Called with one argument METHOD which can be: | ||
| 1102 | - nil: return the content as a string (list of strings for | ||
| 1103 | non-contiguous regions). | ||
| 1104 | - `delete-only': delete the region; the return value is undefined. | ||
| 1105 | - `bounds': return the boundaries of the region as a list of one | ||
| 1106 | or more cons cells of the form (START . END). | ||
| 1107 | - anything else: delete the region and return its content | ||
| 1108 | as a string (or list of strings for non-contiguous regions), | ||
| 1109 | after filtering it with `filter-buffer-substring', which | ||
| 1110 | is called, for each contiguous sub-region, with METHOD as its | ||
| 1111 | 3rd argument.") | ||
| 1112 | 1100 | ||
| 1113 | (defvar region-insert-function | 1101 | (defvar region-insert-function |
| 1114 | (lambda (lines) | 1102 | (lambda (lines) |
diff --git a/src/casefiddle.c b/src/casefiddle.c index 3a1724b306d..774906df04d 100644 --- a/src/casefiddle.c +++ b/src/casefiddle.c | |||
| @@ -516,34 +516,43 @@ casify_region (enum case_action flag, Lisp_Object b, Lisp_Object e) | |||
| 516 | return orig_end + added; | 516 | return orig_end + added; |
| 517 | } | 517 | } |
| 518 | 518 | ||
| 519 | DEFUN ("upcase-region", Fupcase_region, Supcase_region, 2, 3, | 519 | /* Casify a possibly noncontiguous region according to FLAG. BEG and |
| 520 | "(list (region-beginning) (region-end) (region-noncontiguous-p))", | 520 | END specify the bounds, except that if REGION_NONCONTIGUOUS_P is |
| 521 | doc: /* Convert the region to upper case. In programs, wants two arguments. | 521 | non-nil, the region's bounds are specified by (funcall |
| 522 | These arguments specify the starting and ending character numbers of | 522 | region-extract-function 'bounds) instead. */ |
| 523 | the region to operate on. When used as a command, the text between | ||
| 524 | point and the mark is operated on. | ||
| 525 | See also `capitalize-region'. */) | ||
| 526 | (Lisp_Object beg, Lisp_Object end, Lisp_Object region_noncontiguous_p) | ||
| 527 | { | ||
| 528 | Lisp_Object bounds = Qnil; | ||
| 529 | 523 | ||
| 524 | static Lisp_Object | ||
| 525 | casify_pnc_region (enum case_action flag, Lisp_Object beg, Lisp_Object end, | ||
| 526 | Lisp_Object region_noncontiguous_p) | ||
| 527 | { | ||
| 530 | if (!NILP (region_noncontiguous_p)) | 528 | if (!NILP (region_noncontiguous_p)) |
| 531 | { | 529 | { |
| 532 | bounds = call1 (Fsymbol_value (Qregion_extract_function), | 530 | Lisp_Object bounds = call1 (Vregion_extract_function, Qbounds); |
| 533 | intern ("bounds")); | 531 | FOR_EACH_TAIL (bounds) |
| 534 | |||
| 535 | while (CONSP (bounds)) | ||
| 536 | { | 532 | { |
| 537 | casify_region (CASE_UP, XCAR (XCAR (bounds)), XCDR (XCAR (bounds))); | 533 | CHECK_CONS (XCAR (bounds)); |
| 538 | bounds = XCDR (bounds); | 534 | casify_region (flag, XCAR (XCAR (bounds)), XCDR (XCAR (bounds))); |
| 539 | } | 535 | } |
| 536 | CHECK_LIST_END (bounds, bounds); | ||
| 540 | } | 537 | } |
| 541 | else | 538 | else |
| 542 | casify_region (CASE_UP, beg, end); | 539 | casify_region (flag, beg, end); |
| 543 | 540 | ||
| 544 | return Qnil; | 541 | return Qnil; |
| 545 | } | 542 | } |
| 546 | 543 | ||
| 544 | DEFUN ("upcase-region", Fupcase_region, Supcase_region, 2, 3, | ||
| 545 | "(list (region-beginning) (region-end) (region-noncontiguous-p))", | ||
| 546 | doc: /* Convert the region to upper case. In programs, wants two arguments. | ||
| 547 | These arguments specify the starting and ending character numbers of | ||
| 548 | the region to operate on. When used as a command, the text between | ||
| 549 | point and the mark is operated on. | ||
| 550 | See also `capitalize-region'. */) | ||
| 551 | (Lisp_Object beg, Lisp_Object end, Lisp_Object region_noncontiguous_p) | ||
| 552 | { | ||
| 553 | return casify_pnc_region (CASE_UP, beg, end, region_noncontiguous_p); | ||
| 554 | } | ||
| 555 | |||
| 547 | DEFUN ("downcase-region", Fdowncase_region, Sdowncase_region, 2, 3, | 556 | DEFUN ("downcase-region", Fdowncase_region, Sdowncase_region, 2, 3, |
| 548 | "(list (region-beginning) (region-end) (region-noncontiguous-p))", | 557 | "(list (region-beginning) (region-end) (region-noncontiguous-p))", |
| 549 | doc: /* Convert the region to lower case. In programs, wants two arguments. | 558 | doc: /* Convert the region to lower case. In programs, wants two arguments. |
| @@ -552,23 +561,7 @@ the region to operate on. When used as a command, the text between | |||
| 552 | point and the mark is operated on. */) | 561 | point and the mark is operated on. */) |
| 553 | (Lisp_Object beg, Lisp_Object end, Lisp_Object region_noncontiguous_p) | 562 | (Lisp_Object beg, Lisp_Object end, Lisp_Object region_noncontiguous_p) |
| 554 | { | 563 | { |
| 555 | Lisp_Object bounds = Qnil; | 564 | return casify_pnc_region (CASE_DOWN, beg, end, region_noncontiguous_p); |
| 556 | |||
| 557 | if (!NILP (region_noncontiguous_p)) | ||
| 558 | { | ||
| 559 | bounds = call1 (Fsymbol_value (Qregion_extract_function), | ||
| 560 | intern ("bounds")); | ||
| 561 | |||
| 562 | while (CONSP (bounds)) | ||
| 563 | { | ||
| 564 | casify_region (CASE_DOWN, XCAR (XCAR (bounds)), XCDR (XCAR (bounds))); | ||
| 565 | bounds = XCDR (bounds); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | else | ||
| 569 | casify_region (CASE_DOWN, beg, end); | ||
| 570 | |||
| 571 | return Qnil; | ||
| 572 | } | 565 | } |
| 573 | 566 | ||
| 574 | DEFUN ("capitalize-region", Fcapitalize_region, Scapitalize_region, 2, 3, | 567 | DEFUN ("capitalize-region", Fcapitalize_region, Scapitalize_region, 2, 3, |
| @@ -580,38 +573,23 @@ In programs, give two arguments, the starting and ending | |||
| 580 | character positions to operate on. */) | 573 | character positions to operate on. */) |
| 581 | (Lisp_Object beg, Lisp_Object end, Lisp_Object region_noncontiguous_p) | 574 | (Lisp_Object beg, Lisp_Object end, Lisp_Object region_noncontiguous_p) |
| 582 | { | 575 | { |
| 583 | Lisp_Object bounds = Qnil; | 576 | return casify_pnc_region (CASE_CAPITALIZE, beg, end, region_noncontiguous_p); |
| 584 | |||
| 585 | if (!NILP (region_noncontiguous_p)) | ||
| 586 | { | ||
| 587 | bounds = call1 (Fsymbol_value (Qregion_extract_function), | ||
| 588 | intern ("bounds")); | ||
| 589 | |||
| 590 | while (CONSP (bounds)) | ||
| 591 | { | ||
| 592 | casify_region (CASE_CAPITALIZE, XCAR (XCAR (bounds)), XCDR (XCAR (bounds))); | ||
| 593 | bounds = XCDR (bounds); | ||
| 594 | } | ||
| 595 | } | ||
| 596 | else | ||
| 597 | casify_region (CASE_CAPITALIZE, beg, end); | ||
| 598 | |||
| 599 | return Qnil; | ||
| 600 | } | 577 | } |
| 601 | 578 | ||
| 602 | /* Like Fcapitalize_region but change only the initials. */ | 579 | /* Like Fcapitalize_region but change only the initials. */ |
| 603 | 580 | ||
| 604 | DEFUN ("upcase-initials-region", Fupcase_initials_region, | 581 | DEFUN ("upcase-initials-region", Fupcase_initials_region, |
| 605 | Supcase_initials_region, 2, 2, "r", | 582 | Supcase_initials_region, 2, 3, |
| 583 | "(list (region-beginning) (region-end) (region-noncontiguous-p))", | ||
| 606 | doc: /* Upcase the initial of each word in the region. | 584 | doc: /* Upcase the initial of each word in the region. |
| 607 | This means that each word's first character is converted to either | 585 | This means that each word's first character is converted to either |
| 608 | title case or upper case, and the rest are left unchanged. | 586 | title case or upper case, and the rest are left unchanged. |
| 609 | In programs, give two arguments, the starting and ending | 587 | In programs, give two arguments, the starting and ending |
| 610 | character positions to operate on. */) | 588 | character positions to operate on. */) |
| 611 | (Lisp_Object beg, Lisp_Object end) | 589 | (Lisp_Object beg, Lisp_Object end, Lisp_Object region_noncontiguous_p) |
| 612 | { | 590 | { |
| 613 | casify_region (CASE_CAPITALIZE_UP, beg, end); | 591 | return casify_pnc_region (CASE_CAPITALIZE_UP, beg, end, |
| 614 | return Qnil; | 592 | region_noncontiguous_p); |
| 615 | } | 593 | } |
| 616 | 594 | ||
| 617 | static Lisp_Object | 595 | static Lisp_Object |
| @@ -668,12 +646,28 @@ With negative argument, capitalize previous words but do not move. */) | |||
| 668 | void | 646 | void |
| 669 | syms_of_casefiddle (void) | 647 | syms_of_casefiddle (void) |
| 670 | { | 648 | { |
| 649 | DEFSYM (Qbounds, "bounds"); | ||
| 671 | DEFSYM (Qidentity, "identity"); | 650 | DEFSYM (Qidentity, "identity"); |
| 672 | DEFSYM (Qtitlecase, "titlecase"); | 651 | DEFSYM (Qtitlecase, "titlecase"); |
| 673 | DEFSYM (Qspecial_uppercase, "special-uppercase"); | 652 | DEFSYM (Qspecial_uppercase, "special-uppercase"); |
| 674 | DEFSYM (Qspecial_lowercase, "special-lowercase"); | 653 | DEFSYM (Qspecial_lowercase, "special-lowercase"); |
| 675 | DEFSYM (Qspecial_titlecase, "special-titlecase"); | 654 | DEFSYM (Qspecial_titlecase, "special-titlecase"); |
| 676 | 655 | ||
| 656 | DEFVAR_LISP ("region-extract-function", Vregion_extract_function, | ||
| 657 | doc: /* Function to get the region's content. | ||
| 658 | Called with one argument METHOD which can be: | ||
| 659 | - nil: return the content as a string (list of strings for | ||
| 660 | non-contiguous regions). | ||
| 661 | - `delete-only': delete the region; the return value is undefined. | ||
| 662 | - `bounds': return the boundaries of the region as a list of one | ||
| 663 | or more cons cells of the form (START . END). | ||
| 664 | - anything else: delete the region and return its content | ||
| 665 | as a string (or list of strings for non-contiguous regions), | ||
| 666 | after filtering it with `filter-buffer-substring', which | ||
| 667 | is called, for each contiguous sub-region, with METHOD as its | ||
| 668 | 3rd argument. */); | ||
| 669 | Vregion_extract_function = Qnil; /* simple.el sets this. */ | ||
| 670 | |||
| 677 | defsubr (&Supcase); | 671 | defsubr (&Supcase); |
| 678 | defsubr (&Sdowncase); | 672 | defsubr (&Sdowncase); |
| 679 | defsubr (&Scapitalize); | 673 | defsubr (&Scapitalize); |
diff --git a/src/insdel.c b/src/insdel.c index 093b841d6d6..ebfd022ac6b 100644 --- a/src/insdel.c +++ b/src/insdel.c | |||
| @@ -2002,7 +2002,7 @@ prepare_to_modify_buffer_1 (ptrdiff_t start, ptrdiff_t end, | |||
| 2002 | : (!NILP (Vselect_active_regions) | 2002 | : (!NILP (Vselect_active_regions) |
| 2003 | && !NILP (Vtransient_mark_mode)))) | 2003 | && !NILP (Vtransient_mark_mode)))) |
| 2004 | Vsaved_region_selection | 2004 | Vsaved_region_selection |
| 2005 | = call1 (Fsymbol_value (Qregion_extract_function), Qnil); | 2005 | = call1 (Vregion_extract_function, Qnil); |
| 2006 | 2006 | ||
| 2007 | signal_before_change (start, end, preserve_ptr); | 2007 | signal_before_change (start, end, preserve_ptr); |
| 2008 | Fset (Qdeactivate_mark, Qt); | 2008 | Fset (Qdeactivate_mark, Qt); |
| @@ -2401,7 +2401,5 @@ handling of the active region per `select-active-regions'. */); | |||
| 2401 | inhibit_modification_hooks = 0; | 2401 | inhibit_modification_hooks = 0; |
| 2402 | DEFSYM (Qinhibit_modification_hooks, "inhibit-modification-hooks"); | 2402 | DEFSYM (Qinhibit_modification_hooks, "inhibit-modification-hooks"); |
| 2403 | 2403 | ||
| 2404 | DEFSYM (Qregion_extract_function, "region-extract-function"); | ||
| 2405 | |||
| 2406 | defsubr (&Scombine_after_change_execute); | 2404 | defsubr (&Scombine_after_change_execute); |
| 2407 | } | 2405 | } |
diff --git a/src/keyboard.c b/src/keyboard.c index 1b9a603ca17..a16d13cc7b8 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -1535,7 +1535,7 @@ command_loop_1 (void) | |||
| 1535 | Vselection_inhibit_update_commands))) | 1535 | Vselection_inhibit_update_commands))) |
| 1536 | { | 1536 | { |
| 1537 | Lisp_Object txt | 1537 | Lisp_Object txt |
| 1538 | = call1 (Fsymbol_value (Qregion_extract_function), Qnil); | 1538 | = call1 (Vregion_extract_function, Qnil); |
| 1539 | if (XFIXNUM (Flength (txt)) > 0) | 1539 | if (XFIXNUM (Flength (txt)) > 0) |
| 1540 | /* Don't set empty selections. */ | 1540 | /* Don't set empty selections. */ |
| 1541 | call2 (Qgui_set_selection, QPRIMARY, txt); | 1541 | call2 (Qgui_set_selection, QPRIMARY, txt); |
diff --git a/src/search.c b/src/search.c index 9b674a58102..1e57d2ecbe5 100644 --- a/src/search.c +++ b/src/search.c | |||
| @@ -2739,7 +2739,7 @@ since only regular expressions have distinguished subexpressions. */) | |||
| 2739 | Qnil); | 2739 | Qnil); |
| 2740 | else if (case_action == cap_initial) | 2740 | else if (case_action == cap_initial) |
| 2741 | Fupcase_initials_region (make_fixnum (search_regs.start[sub]), | 2741 | Fupcase_initials_region (make_fixnum (search_regs.start[sub]), |
| 2742 | make_fixnum (newpoint)); | 2742 | make_fixnum (newpoint), Qnil); |
| 2743 | 2743 | ||
| 2744 | /* The replace_range etc. functions can trigger modification hooks | 2744 | /* The replace_range etc. functions can trigger modification hooks |
| 2745 | (see signal_before_change and signal_after_change). Try to error | 2745 | (see signal_before_change and signal_after_change). Try to error |
diff --git a/test/src/casefiddle-tests.el b/test/src/casefiddle-tests.el index ed9a2f93306..54793f2cda4 100644 --- a/test/src/casefiddle-tests.el +++ b/test/src/casefiddle-tests.el | |||
| @@ -259,5 +259,22 @@ | |||
| 259 | (should (eq tc (capitalize ch))) | 259 | (should (eq tc (capitalize ch))) |
| 260 | (should (eq tc (upcase-initials ch)))))) | 260 | (should (eq tc (upcase-initials ch)))))) |
| 261 | 261 | ||
| 262 | (defvar casefiddle-oldfunc region-extract-function) | ||
| 263 | |||
| 264 | (defun casefiddle-loopfunc (method) | ||
| 265 | (if (eq method 'bounds) | ||
| 266 | (let ((looping (list '(1 . 1)))) | ||
| 267 | (setcdr looping looping)) | ||
| 268 | (funcall casefiddle-oldfunc method))) | ||
| 269 | |||
| 270 | (defun casefiddle-badfunc (method) | ||
| 271 | (if (eq method 'bounds) | ||
| 272 | '(()) | ||
| 273 | (funcall casefiddle-oldfunc method))) | ||
| 274 | |||
| 275 | (ert-deftest casefiddle-invalid-region-extract-function () | ||
| 276 | (dolist (region-extract-function '(casefiddle-badfunc casefiddle-loopfunc)) | ||
| 277 | (with-temp-buffer | ||
| 278 | (should-error (upcase-region nil nil t))))) | ||
| 262 | 279 | ||
| 263 | ;;; casefiddle-tests.el ends here | 280 | ;;; casefiddle-tests.el ends here |