diff options
| author | Paul Eggert | 2019-10-30 14:40:06 -0700 |
|---|---|---|
| committer | Paul Eggert | 2019-10-30 14:43:14 -0700 |
| commit | f2a72bb8ed29223dd1197492d4270c171db5e443 (patch) | |
| tree | 60d90f60474f34750bf6c95b356c6128529cb1a4 /src | |
| parent | 581601e650cc8bdcf3ed83c6ae36744601c12ce9 (diff) | |
| download | emacs-f2a72bb8ed29223dd1197492d4270c171db5e443.tar.gz emacs-f2a72bb8ed29223dd1197492d4270c171db5e443.zip | |
Fix print.c infloop on circular lists
Fix infinite loops in print.c when a circular list is passed
to command-error-default-function or to error-message-string.
* src/print.c (print_error_message):
Use FOR_EACH_TAIL to avoid infloop on circular lists.
(print_object): Use FOR_EACH_TAIL_SAFE, as it uses
Brent’s teleporting tortoise-hare algorithm which is
asymptotically better than the classic tortoise-hare
algorithm that the code wsas using.
* test/src/print-tests.el (print-circle-2): When print-circle
is nil, do not insist on a particular cycle-detection heuristic.
(error-message-string-circular): New test.
Diffstat (limited to 'src')
| -rw-r--r-- | src/print.c | 62 |
1 files changed, 27 insertions, 35 deletions
diff --git a/src/print.c b/src/print.c index 77ddd93efba..a2c199c14ad 100644 --- a/src/print.c +++ b/src/print.c | |||
| @@ -966,13 +966,12 @@ print_error_message (Lisp_Object data, Lisp_Object stream, const char *context, | |||
| 966 | else | 966 | else |
| 967 | sep = NULL; | 967 | sep = NULL; |
| 968 | 968 | ||
| 969 | for (; CONSP (tail); tail = XCDR (tail), sep = ", ") | 969 | FOR_EACH_TAIL (tail) |
| 970 | { | 970 | { |
| 971 | Lisp_Object obj; | ||
| 972 | |||
| 973 | if (sep) | 971 | if (sep) |
| 974 | write_string (sep, stream); | 972 | write_string (sep, stream); |
| 975 | obj = XCAR (tail); | 973 | sep = ", "; |
| 974 | Lisp_Object obj = XCAR (tail); | ||
| 976 | if (!NILP (file_error) | 975 | if (!NILP (file_error) |
| 977 | || EQ (errname, Qend_of_file) || EQ (errname, Quser_error)) | 976 | || EQ (errname, Qend_of_file) || EQ (errname, Quser_error)) |
| 978 | Fprinc (obj, stream); | 977 | Fprinc (obj, stream); |
| @@ -2087,46 +2086,33 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag) | |||
| 2087 | { | 2086 | { |
| 2088 | printchar ('(', printcharfun); | 2087 | printchar ('(', printcharfun); |
| 2089 | 2088 | ||
| 2090 | Lisp_Object halftail = obj; | ||
| 2091 | |||
| 2092 | /* Negative values of print-length are invalid in CL. | 2089 | /* Negative values of print-length are invalid in CL. |
| 2093 | Treat them like nil, as CMUCL does. */ | 2090 | Treat them like nil, as CMUCL does. */ |
| 2094 | intmax_t print_length = (FIXNATP (Vprint_length) | 2091 | intmax_t print_length = (FIXNATP (Vprint_length) |
| 2095 | ? XFIXNAT (Vprint_length) | 2092 | ? XFIXNAT (Vprint_length) |
| 2096 | : INTMAX_MAX); | 2093 | : INTMAX_MAX); |
| 2097 | 2094 | Lisp_Object objtail = Qnil; | |
| 2098 | intmax_t i = 0; | 2095 | intmax_t i = 0; |
| 2099 | while (CONSP (obj)) | 2096 | FOR_EACH_TAIL_SAFE (obj) |
| 2100 | { | 2097 | { |
| 2101 | /* Detect circular list. */ | 2098 | if (i != 0) |
| 2102 | if (NILP (Vprint_circle)) | ||
| 2103 | { | ||
| 2104 | /* Simple but incomplete way. */ | ||
| 2105 | if (i != 0 && EQ (obj, halftail)) | ||
| 2106 | { | ||
| 2107 | int len = sprintf (buf, " . #%"PRIdMAX, i >> 1); | ||
| 2108 | strout (buf, len, len, printcharfun); | ||
| 2109 | goto end_of_list; | ||
| 2110 | } | ||
| 2111 | } | ||
| 2112 | else | ||
| 2113 | { | 2099 | { |
| 2114 | /* With the print-circle feature. */ | 2100 | printchar (' ', printcharfun); |
| 2115 | if (i != 0) | 2101 | |
| 2102 | if (!NILP (Vprint_circle)) | ||
| 2116 | { | 2103 | { |
| 2117 | Lisp_Object num = Fgethash (obj, Vprint_number_table, Qnil); | 2104 | /* With the print-circle feature. */ |
| 2105 | Lisp_Object num = Fgethash (obj, Vprint_number_table, | ||
| 2106 | Qnil); | ||
| 2118 | if (FIXNUMP (num)) | 2107 | if (FIXNUMP (num)) |
| 2119 | { | 2108 | { |
| 2120 | print_c_string (" . ", printcharfun); | 2109 | print_c_string (". ", printcharfun); |
| 2121 | print_object (obj, printcharfun, escapeflag); | 2110 | print_object (obj, printcharfun, escapeflag); |
| 2122 | goto end_of_list; | 2111 | goto end_of_list; |
| 2123 | } | 2112 | } |
| 2124 | } | 2113 | } |
| 2125 | } | 2114 | } |
| 2126 | 2115 | ||
| 2127 | if (i) | ||
| 2128 | printchar (' ', printcharfun); | ||
| 2129 | |||
| 2130 | if (print_length <= i) | 2116 | if (print_length <= i) |
| 2131 | { | 2117 | { |
| 2132 | print_c_string ("...", printcharfun); | 2118 | print_c_string ("...", printcharfun); |
| @@ -2135,17 +2121,23 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag) | |||
| 2135 | 2121 | ||
| 2136 | i++; | 2122 | i++; |
| 2137 | print_object (XCAR (obj), printcharfun, escapeflag); | 2123 | print_object (XCAR (obj), printcharfun, escapeflag); |
| 2124 | objtail = XCDR (obj); | ||
| 2125 | } | ||
| 2138 | 2126 | ||
| 2139 | obj = XCDR (obj); | 2127 | /* OBJTAIL non-nil here means it's the end of a dotted list |
| 2140 | if (!(i & 1)) | 2128 | or FOR_EACH_TAIL_SAFE detected a circular list. */ |
| 2141 | halftail = XCDR (halftail); | 2129 | if (!NILP (objtail)) |
| 2142 | } | ||
| 2143 | |||
| 2144 | /* OBJ non-nil here means it's the end of a dotted list. */ | ||
| 2145 | if (!NILP (obj)) | ||
| 2146 | { | 2130 | { |
| 2147 | print_c_string (" . ", printcharfun); | 2131 | print_c_string (" . ", printcharfun); |
| 2148 | print_object (obj, printcharfun, escapeflag); | 2132 | |
| 2133 | if (CONSP (objtail) && NILP (Vprint_circle)) | ||
| 2134 | { | ||
| 2135 | int len = sprintf (buf, "#%"PRIdMAX, i >> 1); | ||
| 2136 | strout (buf, len, len, printcharfun); | ||
| 2137 | goto end_of_list; | ||
| 2138 | } | ||
| 2139 | |||
| 2140 | print_object (objtail, printcharfun, escapeflag); | ||
| 2149 | } | 2141 | } |
| 2150 | 2142 | ||
| 2151 | end_of_list: | 2143 | end_of_list: |