aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert2017-02-05 13:25:37 -0800
committerPaul Eggert2017-02-05 13:30:29 -0800
commit14dd9101ec4838f75addf25bf6b06ef33f8a7e97 (patch)
tree500eb382ab23735af5b722b5ad7882c05bdfa965 /src
parentb7fa6b1f1cee9d1b71553fa665843774d2e5cf3d (diff)
downloademacs-14dd9101ec4838f75addf25bf6b06ef33f8a7e97.tar.gz
emacs-14dd9101ec4838f75addf25bf6b06ef33f8a7e97.zip
Signal list cycles in ‘length’ etc.
Use macros like FOR_EACH_TAIL instead of maybe_quit to catch list cycles automatically instead of relying on the user becoming impatient and typing C-g (Bug#25606). * src/fns.c (Flength, Fmember, Fmemq, Fmemql, Fassq, Fassoc, Frassq) (Frassoc, Fdelete, Freverse): Use FOR_EACH_TAIL instead of maybe_quit. (Fnreverse): Use simple EQ to check for circular list instead of rarely_quit, as this suffices in this unusual case. (Fplist_put, Flax_plist_put, Flax_plist_put): Use FOR_EACH_TAIL_CONS instead of maybe_quit. (internal_equal): Use FOR_EACH_TAIL_CONS to check lists, instead of by-hand tail recursion that did not catch cycles. * src/fns.c (Fsafe_length, Fplist_get): * src/xdisp.c (display_mode_element): Use FOR_EACH_TAIL_SAFE instead of by-hand Floyd’s algorithm. * src/lisp.h (QUIT_COUNT_HEURISTIC): Remove; no longer needed. (rarely_quit): Simply count toward USHRT_MAX + 1, since the fancier versions are no longer needed. (FOR_EACH_TAIL_CONS, FOR_EACH_TAIL_SAFE) (FOR_EACH_TAIL_INTERNAL): New macros, the last with definiens mostly taken from FOR_EACH_TAIL. (FOR_EACH_TAIL): Rewrite in terms of FOR_EACH_TAIL_INTERNAL.
Diffstat (limited to 'src')
-rw-r--r--src/fns.c290
-rw-r--r--src/lisp.h35
-rw-r--r--src/xdisp.c37
3 files changed, 146 insertions, 216 deletions
diff --git a/src/fns.c b/src/fns.c
index 4de74a5967f..b5508fb56ab 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -108,23 +108,11 @@ To get the number of bytes, use `string-bytes'. */)
108 XSETFASTINT (val, ASIZE (sequence) & PSEUDOVECTOR_SIZE_MASK); 108 XSETFASTINT (val, ASIZE (sequence) & PSEUDOVECTOR_SIZE_MASK);
109 else if (CONSP (sequence)) 109 else if (CONSP (sequence))
110 { 110 {
111 EMACS_INT i = 0; 111 intptr_t i = 0;
112 112 FOR_EACH_TAIL (sequence)
113 do 113 i++;
114 { 114 if (MOST_POSITIVE_FIXNUM < i)
115 ++i; 115 error ("List too long");
116 if ((i & (QUIT_COUNT_HEURISTIC - 1)) == 0)
117 {
118 if (MOST_POSITIVE_FIXNUM < i)
119 error ("List too long");
120 maybe_quit ();
121 }
122 sequence = XCDR (sequence);
123 }
124 while (CONSP (sequence));
125
126 CHECK_LIST_END (sequence, sequence);
127
128 val = make_number (i); 116 val = make_number (i);
129 } 117 }
130 else if (NILP (sequence)) 118 else if (NILP (sequence))
@@ -142,38 +130,10 @@ it returns 0. If LIST is circular, it returns a finite value
142which is at least the number of distinct elements. */) 130which is at least the number of distinct elements. */)
143 (Lisp_Object list) 131 (Lisp_Object list)
144{ 132{
145 Lisp_Object tail, halftail; 133 intptr_t len = 0;
146 double hilen = 0; 134 FOR_EACH_TAIL_SAFE (list)
147 uintmax_t lolen = 1; 135 len++;
148 136 return make_fixnum_or_float (len);
149 if (! CONSP (list))
150 return make_number (0);
151
152 /* halftail is used to detect circular lists. */
153 for (tail = halftail = list; ; )
154 {
155 tail = XCDR (tail);
156 if (! CONSP (tail))
157 break;
158 if (EQ (tail, halftail))
159 break;
160 lolen++;
161 if ((lolen & 1) == 0)
162 {
163 halftail = XCDR (halftail);
164 if ((lolen & (QUIT_COUNT_HEURISTIC - 1)) == 0)
165 {
166 maybe_quit ();
167 if (lolen == 0)
168 hilen += UINTMAX_MAX + 1.0;
169 }
170 }
171 }
172
173 /* If the length does not fit into a fixnum, return a float.
174 On all known practical machines this returns an upper bound on
175 the true length. */
176 return hilen ? make_float (hilen + lolen) : make_fixnum_or_float (lolen);
177} 137}
178 138
179DEFUN ("string-bytes", Fstring_bytes, Sstring_bytes, 1, 1, 0, 139DEFUN ("string-bytes", Fstring_bytes, Sstring_bytes, 1, 1, 0,
@@ -1383,15 +1343,9 @@ DEFUN ("member", Fmember, Smember, 2, 2, 0,
1383The value is actually the tail of LIST whose car is ELT. */) 1343The value is actually the tail of LIST whose car is ELT. */)
1384 (Lisp_Object elt, Lisp_Object list) 1344 (Lisp_Object elt, Lisp_Object list)
1385{ 1345{
1386 unsigned short int quit_count = 0; 1346 FOR_EACH_TAIL (list)
1387 Lisp_Object tail; 1347 if (! NILP (Fequal (elt, XCAR (li.tail))))
1388 for (tail = list; CONSP (tail); tail = XCDR (tail)) 1348 return li.tail;
1389 {
1390 if (! NILP (Fequal (elt, XCAR (tail))))
1391 return tail;
1392 rarely_quit (++quit_count);
1393 }
1394 CHECK_LIST_END (tail, list);
1395 return Qnil; 1349 return Qnil;
1396} 1350}
1397 1351
@@ -1400,15 +1354,9 @@ DEFUN ("memq", Fmemq, Smemq, 2, 2, 0,
1400The value is actually the tail of LIST whose car is ELT. */) 1354The value is actually the tail of LIST whose car is ELT. */)
1401 (Lisp_Object elt, Lisp_Object list) 1355 (Lisp_Object elt, Lisp_Object list)
1402{ 1356{
1403 unsigned short int quit_count = 0; 1357 FOR_EACH_TAIL (list)
1404 Lisp_Object tail; 1358 if (EQ (XCAR (li.tail), elt))
1405 for (tail = list; CONSP (tail); tail = XCDR (tail)) 1359 return li.tail;
1406 {
1407 if (EQ (XCAR (tail), elt))
1408 return tail;
1409 rarely_quit (++quit_count);
1410 }
1411 CHECK_LIST_END (tail, list);
1412 return Qnil; 1360 return Qnil;
1413} 1361}
1414 1362
@@ -1420,16 +1368,12 @@ The value is actually the tail of LIST whose car is ELT. */)
1420 if (!FLOATP (elt)) 1368 if (!FLOATP (elt))
1421 return Fmemq (elt, list); 1369 return Fmemq (elt, list);
1422 1370
1423 unsigned short int quit_count = 0; 1371 FOR_EACH_TAIL (list)
1424 Lisp_Object tail;
1425 for (tail = list; CONSP (tail); tail = XCDR (tail))
1426 { 1372 {
1427 Lisp_Object tem = XCAR (tail); 1373 Lisp_Object tem = XCAR (li.tail);
1428 if (FLOATP (tem) && internal_equal (elt, tem, 0, 0, Qnil)) 1374 if (FLOATP (tem) && internal_equal (elt, tem, 0, 0, Qnil))
1429 return tail; 1375 return li.tail;
1430 rarely_quit (++quit_count);
1431 } 1376 }
1432 CHECK_LIST_END (tail, list);
1433 return Qnil; 1377 return Qnil;
1434} 1378}
1435 1379
@@ -1439,15 +1383,9 @@ The value is actually the first element of LIST whose car is KEY.
1439Elements of LIST that are not conses are ignored. */) 1383Elements of LIST that are not conses are ignored. */)
1440 (Lisp_Object key, Lisp_Object list) 1384 (Lisp_Object key, Lisp_Object list)
1441{ 1385{
1442 unsigned short int quit_count = 0; 1386 FOR_EACH_TAIL (list)
1443 Lisp_Object tail; 1387 if (CONSP (XCAR (li.tail)) && EQ (XCAR (XCAR (li.tail)), key))
1444 for (tail = list; CONSP (tail); tail = XCDR (tail)) 1388 return XCAR (li.tail);
1445 {
1446 if (CONSP (XCAR (tail)) && EQ (XCAR (XCAR (tail)), key))
1447 return XCAR (tail);
1448 rarely_quit (++quit_count);
1449 }
1450 CHECK_LIST_END (tail, list);
1451 return Qnil; 1389 return Qnil;
1452} 1390}
1453 1391
@@ -1468,17 +1406,13 @@ DEFUN ("assoc", Fassoc, Sassoc, 2, 2, 0,
1468The value is actually the first element of LIST whose car equals KEY. */) 1406The value is actually the first element of LIST whose car equals KEY. */)
1469 (Lisp_Object key, Lisp_Object list) 1407 (Lisp_Object key, Lisp_Object list)
1470{ 1408{
1471 unsigned short int quit_count = 0; 1409 FOR_EACH_TAIL (list)
1472 Lisp_Object tail;
1473 for (tail = list; CONSP (tail); tail = XCDR (tail))
1474 { 1410 {
1475 Lisp_Object car = XCAR (tail); 1411 Lisp_Object car = XCAR (li.tail);
1476 if (CONSP (car) 1412 if (CONSP (car)
1477 && (EQ (XCAR (car), key) || !NILP (Fequal (XCAR (car), key)))) 1413 && (EQ (XCAR (car), key) || !NILP (Fequal (XCAR (car), key))))
1478 return car; 1414 return car;
1479 rarely_quit (++quit_count);
1480 } 1415 }
1481 CHECK_LIST_END (tail, list);
1482 return Qnil; 1416 return Qnil;
1483} 1417}
1484 1418
@@ -1503,15 +1437,9 @@ DEFUN ("rassq", Frassq, Srassq, 2, 2, 0,
1503The value is actually the first element of LIST whose cdr is KEY. */) 1437The value is actually the first element of LIST whose cdr is KEY. */)
1504 (Lisp_Object key, Lisp_Object list) 1438 (Lisp_Object key, Lisp_Object list)
1505{ 1439{
1506 unsigned short int quit_count = 0; 1440 FOR_EACH_TAIL (list)
1507 Lisp_Object tail; 1441 if (CONSP (XCAR (li.tail)) && EQ (XCDR (XCAR (li.tail)), key))
1508 for (tail = list; CONSP (tail); tail = XCDR (tail)) 1442 return XCAR (li.tail);
1509 {
1510 if (CONSP (XCAR (tail)) && EQ (XCDR (XCAR (tail)), key))
1511 return XCAR (tail);
1512 rarely_quit (++quit_count);
1513 }
1514 CHECK_LIST_END (tail, list);
1515 return Qnil; 1443 return Qnil;
1516} 1444}
1517 1445
@@ -1520,17 +1448,13 @@ DEFUN ("rassoc", Frassoc, Srassoc, 2, 2, 0,
1520The value is actually the first element of LIST whose cdr equals KEY. */) 1448The value is actually the first element of LIST whose cdr equals KEY. */)
1521 (Lisp_Object key, Lisp_Object list) 1449 (Lisp_Object key, Lisp_Object list)
1522{ 1450{
1523 unsigned short int quit_count = 0; 1451 FOR_EACH_TAIL (list)
1524 Lisp_Object tail;
1525 for (tail = list; CONSP (tail); tail = XCDR (tail))
1526 { 1452 {
1527 Lisp_Object car = XCAR (tail); 1453 Lisp_Object car = XCAR (li.tail);
1528 if (CONSP (car) 1454 if (CONSP (car)
1529 && (EQ (XCDR (car), key) || !NILP (Fequal (XCDR (car), key)))) 1455 && (EQ (XCDR (car), key) || !NILP (Fequal (XCDR (car), key))))
1530 return car; 1456 return car;
1531 rarely_quit (++quit_count);
1532 } 1457 }
1533 CHECK_LIST_END (tail, list);
1534 return Qnil; 1458 return Qnil;
1535} 1459}
1536 1460
@@ -1668,23 +1592,20 @@ changing the value of a sequence `foo'. */)
1668 } 1592 }
1669 else 1593 else
1670 { 1594 {
1671 unsigned short int quit_count = 0; 1595 Lisp_Object prev = Qnil;
1672 Lisp_Object tail, prev;
1673 1596
1674 for (tail = seq, prev = Qnil; CONSP (tail); tail = XCDR (tail)) 1597 FOR_EACH_TAIL (seq)
1675 { 1598 {
1676 if (!NILP (Fequal (elt, XCAR (tail)))) 1599 if (!NILP (Fequal (elt, (XCAR (li.tail)))))
1677 { 1600 {
1678 if (NILP (prev)) 1601 if (NILP (prev))
1679 seq = XCDR (tail); 1602 seq = XCDR (li.tail);
1680 else 1603 else
1681 Fsetcdr (prev, XCDR (tail)); 1604 Fsetcdr (prev, XCDR (li.tail));
1682 } 1605 }
1683 else 1606 else
1684 prev = tail; 1607 prev = li.tail;
1685 rarely_quit (++quit_count);
1686 } 1608 }
1687 CHECK_LIST_END (tail, seq);
1688 } 1609 }
1689 1610
1690 return seq; 1611 return seq;
@@ -1702,15 +1623,17 @@ This function may destructively modify SEQ to produce the value. */)
1702 return Freverse (seq); 1623 return Freverse (seq);
1703 else if (CONSP (seq)) 1624 else if (CONSP (seq))
1704 { 1625 {
1705 unsigned short int quit_count = 0;
1706 Lisp_Object prev, tail, next; 1626 Lisp_Object prev, tail, next;
1707 1627
1708 for (prev = Qnil, tail = seq; CONSP (tail); tail = next) 1628 for (prev = Qnil, tail = seq; CONSP (tail); tail = next)
1709 { 1629 {
1710 next = XCDR (tail); 1630 next = XCDR (tail);
1631 /* If SEQ contains a cycle, attempting to reverse it
1632 in-place will inevitably come back to SEQ. */
1633 if (EQ (next, seq))
1634 circular_list (seq);
1711 Fsetcdr (tail, prev); 1635 Fsetcdr (tail, prev);
1712 prev = tail; 1636 prev = tail;
1713 rarely_quit (++quit_count);
1714 } 1637 }
1715 CHECK_LIST_END (tail, seq); 1638 CHECK_LIST_END (tail, seq);
1716 seq = prev; 1639 seq = prev;
@@ -1753,13 +1676,9 @@ See also the function `nreverse', which is used more often. */)
1753 return Qnil; 1676 return Qnil;
1754 else if (CONSP (seq)) 1677 else if (CONSP (seq))
1755 { 1678 {
1756 unsigned short int quit_count = 0; 1679 new = Qnil;
1757 for (new = Qnil; CONSP (seq); seq = XCDR (seq)) 1680 FOR_EACH_TAIL (seq)
1758 { 1681 new = Fcons (XCAR (li.tail), new);
1759 new = Fcons (XCAR (seq), new);
1760 rarely_quit (++quit_count);
1761 }
1762 CHECK_LIST_END (seq, seq);
1763 } 1682 }
1764 else if (VECTORP (seq)) 1683 else if (VECTORP (seq))
1765 { 1684 {
@@ -2011,18 +1930,14 @@ corresponding to the given PROP, or nil if PROP is not one of the
2011properties on the list. This function never signals an error. */) 1930properties on the list. This function never signals an error. */)
2012 (Lisp_Object plist, Lisp_Object prop) 1931 (Lisp_Object plist, Lisp_Object prop)
2013{ 1932{
2014 Lisp_Object tail, halftail; 1933 FOR_EACH_TAIL_SAFE (plist)
2015
2016 /* halftail is used to detect circular lists. */
2017 tail = halftail = plist;
2018 while (CONSP (tail) && CONSP (XCDR (tail)))
2019 { 1934 {
2020 if (EQ (prop, XCAR (tail))) 1935 if (! CONSP (XCDR (li.tail)))
2021 return XCAR (XCDR (tail)); 1936 break;
2022 1937 if (EQ (prop, XCAR (li.tail)))
2023 tail = XCDR (XCDR (tail)); 1938 return XCAR (XCDR (li.tail));
2024 halftail = XCDR (halftail); 1939 li.tail = XCDR (li.tail);
2025 if (EQ (tail, halftail)) 1940 if (EQ (li.tail, li.tortoise))
2026 break; 1941 break;
2027 } 1942 }
2028 1943
@@ -2048,19 +1963,22 @@ use `(setq x (plist-put x prop val))' to be sure to use the new value.
2048The PLIST is modified by side effects. */) 1963The PLIST is modified by side effects. */)
2049 (Lisp_Object plist, Lisp_Object prop, Lisp_Object val) 1964 (Lisp_Object plist, Lisp_Object prop, Lisp_Object val)
2050{ 1965{
2051 unsigned short int quit_count = 0;
2052 Lisp_Object prev = Qnil; 1966 Lisp_Object prev = Qnil;
2053 for (Lisp_Object tail = plist; CONSP (tail) && CONSP (XCDR (tail)); 1967 FOR_EACH_TAIL_CONS (plist)
2054 tail = XCDR (XCDR (tail)))
2055 { 1968 {
2056 if (EQ (prop, XCAR (tail))) 1969 if (! CONSP (XCDR (li.tail)))
1970 break;
1971
1972 if (EQ (prop, XCAR (li.tail)))
2057 { 1973 {
2058 Fsetcar (XCDR (tail), val); 1974 Fsetcar (XCDR (li.tail), val);
2059 return plist; 1975 return plist;
2060 } 1976 }
2061 1977
2062 prev = tail; 1978 prev = li.tail;
2063 rarely_quit (++quit_count); 1979 li.tail = XCDR (li.tail);
1980 if (EQ (li.tail, li.tortoise))
1981 circular_list (plist);
2064 } 1982 }
2065 Lisp_Object newcell 1983 Lisp_Object newcell
2066 = Fcons (prop, Fcons (val, NILP (prev) ? plist : XCDR (XCDR (prev)))); 1984 = Fcons (prop, Fcons (val, NILP (prev) ? plist : XCDR (XCDR (prev))));
@@ -2089,20 +2007,16 @@ corresponding to the given PROP, or nil if PROP is not
2089one of the properties on the list. */) 2007one of the properties on the list. */)
2090 (Lisp_Object plist, Lisp_Object prop) 2008 (Lisp_Object plist, Lisp_Object prop)
2091{ 2009{
2092 unsigned short int quit_count = 0; 2010 FOR_EACH_TAIL_CONS (plist)
2093 Lisp_Object tail;
2094
2095 for (tail = plist;
2096 CONSP (tail) && CONSP (XCDR (tail));
2097 tail = XCDR (XCDR (tail)))
2098 { 2011 {
2099 if (! NILP (Fequal (prop, XCAR (tail)))) 2012 if (! CONSP (XCDR (li.tail)))
2100 return XCAR (XCDR (tail)); 2013 break;
2101 rarely_quit (++quit_count); 2014 if (! NILP (Fequal (prop, XCAR (li.tail))))
2015 return XCAR (XCDR (li.tail));
2016 li.tail = XCDR (li.tail);
2017 if (EQ (li.tail, li.tortoise))
2018 circular_list (plist);
2102 } 2019 }
2103
2104 CHECK_LIST_END (tail, prop);
2105
2106 return Qnil; 2020 return Qnil;
2107} 2021}
2108 2022
@@ -2116,19 +2030,22 @@ use `(setq x (lax-plist-put x prop val))' to be sure to use the new value.
2116The PLIST is modified by side effects. */) 2030The PLIST is modified by side effects. */)
2117 (Lisp_Object plist, Lisp_Object prop, Lisp_Object val) 2031 (Lisp_Object plist, Lisp_Object prop, Lisp_Object val)
2118{ 2032{
2119 unsigned short int quit_count = 0;
2120 Lisp_Object prev = Qnil; 2033 Lisp_Object prev = Qnil;
2121 for (Lisp_Object tail = plist; CONSP (tail) && CONSP (XCDR (tail)); 2034 FOR_EACH_TAIL_CONS (plist)
2122 tail = XCDR (XCDR (tail)))
2123 { 2035 {
2124 if (! NILP (Fequal (prop, XCAR (tail)))) 2036 if (! CONSP (XCDR (li.tail)))
2037 break;
2038
2039 if (! NILP (Fequal (prop, XCAR (li.tail))))
2125 { 2040 {
2126 Fsetcar (XCDR (tail), val); 2041 Fsetcar (XCDR (li.tail), val);
2127 return plist; 2042 return plist;
2128 } 2043 }
2129 2044
2130 prev = tail; 2045 prev = li.tail;
2131 rarely_quit (++quit_count); 2046 li.tail = XCDR (li.tail);
2047 if (EQ (li.tail, li.tortoise))
2048 circular_list (plist);
2132 } 2049 }
2133 Lisp_Object newcell = list2 (prop, val); 2050 Lisp_Object newcell = list2 (prop, val);
2134 if (NILP (prev)) 2051 if (NILP (prev))
@@ -2206,9 +2123,7 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, int depth, bool props,
2206 } 2123 }
2207 } 2124 }
2208 2125
2209 unsigned short int quit_count = 0;
2210 tail_recurse: 2126 tail_recurse:
2211 rarely_quit (++quit_count);
2212 if (EQ (o1, o2)) 2127 if (EQ (o1, o2))
2213 return 1; 2128 return 1;
2214 if (XTYPE (o1) != XTYPE (o2)) 2129 if (XTYPE (o1) != XTYPE (o2))
@@ -2228,12 +2143,24 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, int depth, bool props,
2228 } 2143 }
2229 2144
2230 case Lisp_Cons: 2145 case Lisp_Cons:
2231 if (!internal_equal (XCAR (o1), XCAR (o2), depth + 1, props, ht)) 2146 {
2232 return 0; 2147 Lisp_Object tail1 = o1;
2233 o1 = XCDR (o1); 2148 FOR_EACH_TAIL_CONS (o1)
2234 o2 = XCDR (o2); 2149 {
2235 /* FIXME: This inf-loops in a circular list! */ 2150 if (! CONSP (o2))
2236 goto tail_recurse; 2151 return false;
2152 if (! internal_equal (XCAR (li.tail), XCAR (o2), depth + 1,
2153 props, ht))
2154 return false;
2155 tail1 = XCDR (li.tail);
2156 o2 = XCDR (o2);
2157 if (EQ (tail1, o2))
2158 return true;
2159 }
2160 o1 = tail1;
2161 depth++;
2162 goto tail_recurse;
2163 }
2237 2164
2238 case Lisp_Misc: 2165 case Lisp_Misc:
2239 if (XMISCTYPE (o1) != XMISCTYPE (o2)) 2166 if (XMISCTYPE (o1) != XMISCTYPE (o2))
@@ -2247,6 +2174,7 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, int depth, bool props,
2247 return 0; 2174 return 0;
2248 o1 = XOVERLAY (o1)->plist; 2175 o1 = XOVERLAY (o1)->plist;
2249 o2 = XOVERLAY (o2)->plist; 2176 o2 = XOVERLAY (o2)->plist;
2177 depth++;
2250 goto tail_recurse; 2178 goto tail_recurse;
2251 } 2179 }
2252 if (MARKERP (o1)) 2180 if (MARKERP (o1))
@@ -2397,7 +2325,6 @@ Only the last argument is not altered, and need not be a list.
2397usage: (nconc &rest LISTS) */) 2325usage: (nconc &rest LISTS) */)
2398 (ptrdiff_t nargs, Lisp_Object *args) 2326 (ptrdiff_t nargs, Lisp_Object *args)
2399{ 2327{
2400 unsigned short int quit_count = 0;
2401 Lisp_Object val = Qnil; 2328 Lisp_Object val = Qnil;
2402 2329
2403 for (ptrdiff_t argnum = 0; argnum < nargs; argnum++) 2330 for (ptrdiff_t argnum = 0; argnum < nargs; argnum++)
@@ -2413,13 +2340,8 @@ usage: (nconc &rest LISTS) */)
2413 CHECK_CONS (tem); 2340 CHECK_CONS (tem);
2414 2341
2415 Lisp_Object tail; 2342 Lisp_Object tail;
2416 do 2343 FOR_EACH_TAIL_CONS (tem)
2417 { 2344 tail = li.tail;
2418 tail = tem;
2419 tem = XCDR (tail);
2420 rarely_quit (++quit_count);
2421 }
2422 while (CONSP (tem));
2423 2345
2424 tem = args[argnum + 1]; 2346 tem = args[argnum + 1];
2425 Fsetcdr (tail, tem); 2347 Fsetcdr (tail, tem);
@@ -2841,14 +2763,20 @@ property and a property with the value nil.
2841The value is actually the tail of PLIST whose car is PROP. */) 2763The value is actually the tail of PLIST whose car is PROP. */)
2842 (Lisp_Object plist, Lisp_Object prop) 2764 (Lisp_Object plist, Lisp_Object prop)
2843{ 2765{
2844 unsigned short int quit_count = 0; 2766 FOR_EACH_TAIL (plist)
2845 while (CONSP (plist) && !EQ (XCAR (plist), prop))
2846 { 2767 {
2847 plist = XCDR (plist); 2768 if (EQ (XCAR (li.tail), prop))
2848 plist = CDR (plist); 2769 return li.tail;
2849 rarely_quit (++quit_count); 2770 if (!CONSP (XCDR (li.tail)))
2771 {
2772 CHECK_LIST_END (XCDR (li.tail), plist);
2773 return Qnil;
2774 }
2775 li.tail = XCDR (li.tail);
2776 if (EQ (li.tail, li.tortoise))
2777 circular_list (plist);
2850 } 2778 }
2851 return plist; 2779 return Qnil;
2852} 2780}
2853 2781
2854DEFUN ("widget-put", Fwidget_put, Swidget_put, 3, 3, 0, 2782DEFUN ("widget-put", Fwidget_put, Swidget_put, 3, 3, 0,
diff --git a/src/lisp.h b/src/lisp.h
index 102e8bd70ef..13fca0b29e0 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -3129,20 +3129,14 @@ extern void maybe_quit (void);
3129 3129
3130#define QUITP (!NILP (Vquit_flag) && NILP (Vinhibit_quit)) 3130#define QUITP (!NILP (Vquit_flag) && NILP (Vinhibit_quit))
3131 3131
3132/* Heuristic on how many iterations of a tight loop can be safely done
3133 before it's time to do a quit. This must be a power of 2. It
3134 is nice but not necessary for it to equal USHRT_MAX + 1. */
3135
3136enum { QUIT_COUNT_HEURISTIC = 1 << 16 };
3137
3138/* Process a quit rarely, based on a counter COUNT, for efficiency. 3132/* Process a quit rarely, based on a counter COUNT, for efficiency.
3139 "Rarely" means once per QUIT_COUNT_HEURISTIC or per USHRT_MAX + 1 3133 "Rarely" means once per USHRT_MAX + 1 times; this is somewhat
3140 times, whichever is smaller (somewhat arbitrary, but often faster). */ 3134 arbitrary, but efficient. */
3141 3135
3142INLINE void 3136INLINE void
3143rarely_quit (unsigned short int count) 3137rarely_quit (unsigned short int count)
3144{ 3138{
3145 if (! (count & (QUIT_COUNT_HEURISTIC - 1))) 3139 if (! count)
3146 maybe_quit (); 3140 maybe_quit ();
3147} 3141}
3148 3142
@@ -4598,13 +4592,32 @@ enum
4598 http://maths-people.anu.edu.au/~brent/pd/rpb051i.pdf */ 4592 http://maths-people.anu.edu.au/~brent/pd/rpb051i.pdf */
4599 4593
4600#define FOR_EACH_TAIL(list) \ 4594#define FOR_EACH_TAIL(list) \
4595 FOR_EACH_TAIL_INTERNAL (list, CHECK_LIST_END (li.tail, list), \
4596 circular_list (list))
4597
4598/* Like FOR_EACH_TAIL (LIST), except check only for cycles. */
4599
4600#define FOR_EACH_TAIL_CONS(list) \
4601 FOR_EACH_TAIL_INTERNAL (list, (void) 0, circular_list (list))
4602
4603/* Like FOR_EACH_TAIL (LIST), except check for neither dotted lists
4604 nor cycles. */
4605
4606#define FOR_EACH_TAIL_SAFE(list) \
4607 FOR_EACH_TAIL_INTERNAL (list, (void) 0, (void) (li.tail = Qnil))
4608
4609/* Like FOR_EACH_TAIL (LIST), except evaluate DOTTED or CYCLE,
4610 respectively, if a dotted list or cycle is found. This is an
4611 internal macro intended for use only by the above macros. */
4612
4613#define FOR_EACH_TAIL_INTERNAL(list, dotted, cycle) \
4601 for (struct { Lisp_Object tail, tortoise; intptr_t n, max; } li \ 4614 for (struct { Lisp_Object tail, tortoise; intptr_t n, max; } li \
4602 = { list, list, 2, 2 }; \ 4615 = { list, list, 2, 2 }; \
4603 CONSP (li.tail) || (CHECK_LIST_END (li.tail, list), false); \ 4616 CONSP (li.tail) || (dotted, false); \
4604 (li.tail = XCDR (li.tail), \ 4617 (li.tail = XCDR (li.tail), \
4605 (li.n-- == 0 \ 4618 (li.n-- == 0 \
4606 ? (void) (li.n = li.max <<= 1, li.tortoise = li.tail) \ 4619 ? (void) (li.n = li.max <<= 1, li.tortoise = li.tail) \
4607 : EQ (li.tail, li.tortoise) ? circular_list (list) : (void) 0))) 4620 : EQ (li.tail, li.tortoise) ? (cycle) : (void) 0)))
4608 4621
4609/* Do a `for' loop over alist values. */ 4622/* Do a `for' loop over alist values. */
4610 4623
diff --git a/src/xdisp.c b/src/xdisp.c
index 0e329dfe6e9..5e1207f29e3 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -23033,30 +23033,19 @@ display_mode_element (struct it *it, int depth, int field_width, int precision,
23033 goto tail_recurse; 23033 goto tail_recurse;
23034 } 23034 }
23035 else if (STRINGP (car) || CONSP (car)) 23035 else if (STRINGP (car) || CONSP (car))
23036 { 23036 FOR_EACH_TAIL_SAFE (elt)
23037 Lisp_Object halftail = elt; 23037 {
23038 int len = 0; 23038 if (0 < precision && precision <= n)
23039 23039 break;
23040 while (CONSP (elt) 23040 n += display_mode_element (it, depth,
23041 && (precision <= 0 || n < precision)) 23041 /* Pad after only the last
23042 { 23042 list element. */
23043 n += display_mode_element (it, depth, 23043 (! CONSP (XCDR (li.tail))
23044 /* Do padding only after the last 23044 ? field_width - n
23045 element in the list. */ 23045 : 0),
23046 (! CONSP (XCDR (elt)) 23046 precision - n, XCAR (li.tail),
23047 ? field_width - n 23047 props, risky);
23048 : 0), 23048 }
23049 precision - n, XCAR (elt),
23050 props, risky);
23051 elt = XCDR (elt);
23052 len++;
23053 if ((len & 1) == 0)
23054 halftail = XCDR (halftail);
23055 /* Check for cycle. */
23056 if (EQ (halftail, elt))
23057 break;
23058 }
23059 }
23060 } 23049 }
23061 break; 23050 break;
23062 23051