commit 6932c940fda848422cb6c66c81c9d7a108e8320b Author: Yuan Fu Date: Sat May 23 22:40:11 2026 -0700 Fold calls to fix_position into treesit_check_position (bug#80830) * src/treesit.c (treesit_check_position): Return the validated value. (Ftreesit_node_first_child_for_pos): (Ftreesit_node_descendant_for_range): (Ftreesit_query_capture): (Ftreesit__linecol_at): Fold calls to fix_position into treesit_check_position in treesit.c. diff --git a/src/treesit.c b/src/treesit.c index 00b97da2c5c..4b1bce4fe2c 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -2917,13 +2917,15 @@ treesit_check_node (Lisp_Object obj) } /* Check that OBJ is a positive integer/marker and it is within the - visible portion of BUF. */ -static void + visible portion of BUF. Signal if invalid, return the value if + valid. */ +static ptrdiff_t treesit_check_position (Lisp_Object obj, struct buffer *buf) { ptrdiff_t pos = fix_position (obj); if (pos < BUF_BEGV (buf) || pos > BUF_ZV (buf)) xsignal1 (Qargs_out_of_range, obj); + return pos; } bool @@ -3346,10 +3348,10 @@ Note that this function returns an immediate child, not the smallest struct buffer *buf = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer); ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg; - treesit_check_position (pos, buf); + ptrdiff_t fixpos = treesit_check_position (pos, buf); treesit_initialize (); - ptrdiff_t byte_pos = buf_charpos_to_bytepos (buf, fix_position (pos)); + ptrdiff_t byte_pos = buf_charpos_to_bytepos (buf, fixpos); TSNode treesit_node = XTS_NODE (node)->node; TSTreeCursor cursor = ts_tree_cursor_new (treesit_node); @@ -3382,13 +3384,13 @@ If NODE is nil, return nil. */) struct buffer *buf = XBUFFER (XTS_PARSER (XTS_NODE (node)->parser)->buffer); ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (node)->parser)->visible_beg; - treesit_check_position (beg, buf); - treesit_check_position (end, buf); + ptrdiff_t fixpos_beg = treesit_check_position (beg, buf); + ptrdiff_t fixpos_end = treesit_check_position (end, buf); treesit_initialize (); - ptrdiff_t byte_beg = buf_charpos_to_bytepos (buf, fix_position (beg)); - ptrdiff_t byte_end = buf_charpos_to_bytepos (buf, fix_position (end)); + ptrdiff_t byte_beg = buf_charpos_to_bytepos (buf, fixpos_beg); + ptrdiff_t byte_end = buf_charpos_to_bytepos (buf, fixpos_end); TSNode treesit_node = XTS_NODE (node)->node; TSNode child; if (NILP (named)) @@ -4060,10 +4062,12 @@ the query. */) /* Check BEG and END. */ struct buffer *buf = XBUFFER (XTS_PARSER (lisp_parser)->buffer); + ptrdiff_t fixpos_beg = 0; + ptrdiff_t fixpos_end = 0; if (!NILP (beg)) - treesit_check_position (beg, buf); + fixpos_beg = treesit_check_position (beg, buf); if (!NILP (end)) - treesit_check_position (end, buf); + fixpos_end = treesit_check_position (end, buf); /* Initialize query objects. At the end of this block, we should have a working TSQuery and a TSQueryCursor. */ @@ -4085,8 +4089,8 @@ the query. */) { ptrdiff_t visible_beg = XTS_PARSER (XTS_NODE (lisp_node)->parser)->visible_beg; - ptrdiff_t beg_byte = CHAR_TO_BYTE (fix_position (beg)); - ptrdiff_t end_byte = CHAR_TO_BYTE (fix_position (end)); + ptrdiff_t beg_byte = CHAR_TO_BYTE (fixpos_beg); + ptrdiff_t end_byte = CHAR_TO_BYTE (fixpos_end); /* In ts_query_cursor_set_byte_range, if end_byte = 0, it's set to UINT32_MAX for some reason. But range (1, 1) shouldn't capture anything. So in this case just return Qnil. (bug#80798) */ @@ -5181,9 +5185,9 @@ return the line and column in the form of This is used for internal testing and debugging ONLY. */) (Lisp_Object pos) { - treesit_check_position (pos, current_buffer); + ptrdiff_t fixpos = treesit_check_position (pos, current_buffer); struct ts_linecol pos_linecol - = treesit_linecol_of_pos (CHAR_TO_BYTE (fix_position (pos)), + = treesit_linecol_of_pos (CHAR_TO_BYTE (fixpos), BUF_TS_LINECOL_POINT (current_buffer)); return Fcons (make_fixnum (pos_linecol.line), make_fixnum (pos_linecol.col)); } commit e7a333f18e96fc88d98e574be5c8e355efa96c36 Author: Paul Eggert Date: Sat May 23 18:48:45 2026 -0700 EVENT_INIT via a compound literal This pacifies GCC 16.1.1 x86-64 -Warray-bounds when compiling with -fsanitize=address. It’s also cleaner on more-typical platforms. * src/termhooks.h (EVENT_INIT): Define via a compound literal rather than via a memset plus an assignment. This evaluates the argument lvalue only once, and is more likely to catch type errors. diff --git a/src/termhooks.h b/src/termhooks.h index 38c9df9cad2..ebac777ab4c 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -411,8 +411,7 @@ struct input_event Lisp_Object device; }; -#define EVENT_INIT(event) (memset (&(event), 0, sizeof (struct input_event)), \ - (event).device = Qt) +#define EVENT_INIT(event) ((event) = (struct input_event) {.device = Qt}) /* Bits in the modifiers member of the input_event structure. Note that reorder_modifiers assumes that the bits are in canonical commit 7bfde4d50b5e188211e5154a735619905e2a734d Author: Paul Eggert Date: Sat May 23 17:59:14 2026 -0700 sfnt.c eassert vs assert Use eassert uniformly, instead of assert. * src/sfnt.c [!TEST]: Do not include or use assert. (eassert) [TEST]: New macro. diff --git a/src/sfnt.c b/src/sfnt.c index 5c3c2c4b972..4900daee6f6 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -21,7 +21,6 @@ along with GNU Emacs. If not, see . */ #include "sfnt.h" -#include #include #include #include @@ -48,6 +47,7 @@ along with GNU Emacs. If not, see . */ #ifdef TEST +#include #include #include #include @@ -129,6 +129,8 @@ xfree (void *ptr) /* Needed for tests. */ #define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0]) +#define eassert(expr) assert (expr) + /* Also necessary. */ #define AVOID _Noreturn ATTRIBUTE_COLD void @@ -4362,7 +4364,7 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, if ((left & ~SFNT_POLY_MASK) == (right & ~SFNT_POLY_MASK)) { /* Assert that start does not exceed the end of the row. */ - assert (start <= row_end); + eassert (start <= row_end); w = coverage[right - left]; a = *start + w; @@ -4378,7 +4380,7 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, if (left & SFNT_POLY_MASK) { /* Assert that start does not exceed the end of the row. */ - assert (start <= row_end); + eassert (start <= row_end); /* Compute the coverage for the first pixel, and move left past it. The coverage is a number from 1 to 7 describing how @@ -4405,7 +4407,7 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, while (left + SFNT_POLY_MASK < right) { /* Assert that start does not exceed the end of the row. */ - assert (start <= row_end); + eassert (start <= row_end); a = *start + w; *start++ = sfnt_saturate_short (a); @@ -4417,7 +4419,7 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, if (right & SFNT_POLY_MASK) { /* Assert that start does not exceed the end of the row. */ - assert (start <= row_end); + eassert (start <= row_end); w = coverage[right - left]; a = *start + w; @@ -12598,7 +12600,7 @@ sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph, advance phantom points. */ num_points = context->num_points - base_index; num_contours = context->num_end_points - base_contour; - assert (num_points >= 2); + eassert (num_points >= 2); /* Nothing to instruct! */ if (!num_points && !num_contours) @@ -12669,7 +12671,7 @@ sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph, } /* Copy S1 and S2 into the glyph zone. */ - assert (num_points >= 2); + eassert (num_points >= 2); zone->x_points[num_points - 1] = s2; zone->x_points[num_points - 2] = s1; @@ -12960,7 +12962,7 @@ sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, the outline ultimately produced, they are temporarily appended to the outline here, so as to enable defer_offsets below to refer to them. */ - assert (value->num_points >= 2); + eassert (value->num_points >= 2); last_point = value->num_points - 2; number_of_contours = value->num_contours; @@ -13015,7 +13017,7 @@ sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, /* Assert the child anchor is within the confines of the zone. */ - assert (point2 < value->num_points); + eassert (point2 < value->num_points); /* Get the points and use them to compute the offsets. */ @@ -13137,7 +13139,7 @@ sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, /* Subtract the two phantom points from context->num_points. This behavior is correct, as only the subglyph's phantom points may be provided as anchor points. */ - assert (context->num_points - contour_start >= 2); + eassert (context->num_points - contour_start >= 2); context->num_points -= 2; sfnt_transform_f26dot6 (component, commit c4e20777c26548722a37b03db93243e83a0d6188 Author: Paul Eggert Date: Sat May 23 17:55:11 2026 -0700 Better size overflow checking for sfnt.c * src/sfnt.c (memory_full_up) [TEST]: New static function. (xmalloc, xcalloc, xrealloc): Use it instead of aborting. (eassert) [TEST]: Remove; no longer needed. (xaddmalloc, xaddrealloc): New static convenience functions. (sfnt_read_cmap_format_12, sfnt_read_loca_table_short) (sfnt_read_loca_table_long, sfnt_read_glyf_table) (sfnt_read_simple_glyph, sfnt_read_compound_glyph) (sfnt_read_glyph, sfnt_build_append, sfnt_build_outline_edges) (sfnt_raster_glyph_outline, sfnt_build_outline_fedges) (sfnt_raster_glyph_outline_exact, sfnt_read_hmtx_table) (sfnt_read_name_table, sfnt_read_meta_table) (sfnt_read_ttc_header, sfnt_read_fpgm_table) (sfnt_read_prep_table, sfnt_create_uvs_context) (sfnt_read_gvar_table, sfnt_read_packed_deltas) (sfnt_vary_simple_glyph, sfnt_vary_compound_glyph): Use the new functions to test for size overflow more reliably. Use a cleaner way to decide whether a pointer addresses the heap not the stack and thus needs freeing. Fix a few more unlikely overflows. (sfnt_read_cmap_format_12): The recently-added eassert is no longer needed, so remove it. (sfnt_read_simple_glyph): Initialize glyph->simple early to simplify later code, as is done in similar functions. Complicate size test to avoid potential unsigned overflow. diff --git a/src/sfnt.c b/src/sfnt.c index 225b172cb40..5c3c2c4b972 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -56,6 +56,12 @@ along with GNU Emacs. If not, see . */ #include #include +static void +memory_full_up (void) +{ + abort (); +} + static void * xmalloc (size_t size) { @@ -64,24 +70,39 @@ xmalloc (size_t size) ptr = malloc (size); if (!ptr) - abort (); + memory_full_up (); return ptr; } -MAYBE_UNUSED static void * -xcalloc (ptrdiff_t n, ptrdiff_t s) +static void * +xcalloc (size_t n, size_t s) { void *ptr; ptr = calloc (n, s); if (!ptr) - abort (); + memory_full_up (); return ptr; } +static void * +xzalloc (size_t size) +{ + return xcalloc (1, size); +} + +static void * +xnmalloc (size_t n, size_t s) +{ + size_t size; + if (ckd_mul (&size, n, s)) + memory_full_up (); + return xmalloc (size); +} + static void * xrealloc (void *ptr, size_t size) { @@ -90,7 +111,7 @@ xrealloc (void *ptr, size_t size) new_ptr = realloc (ptr, size); if (!new_ptr) - abort (); + memory_full_up (); return new_ptr; } @@ -111,13 +132,29 @@ xfree (void *ptr) /* Also necessary. */ #define AVOID _Noreturn ATTRIBUTE_COLD void -#define eassert(expr) assert (expr) - #else #define TEST_STATIC #include "lisp.h" #endif +static void * +xaddmalloc (size_t base, size_t additional) +{ + size_t size; + if (ckd_add (&size, additional, base)) + memory_full_up (); + return xmalloc (size); +} + +static void * +xaddrealloc (void *ptr, size_t base, size_t additional) +{ + size_t size; + if (ckd_add (&size, additional, base)) + memory_full_up (); + return xrealloc (ptr, size); +} + #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -738,8 +775,7 @@ sfnt_read_cmap_format_12 (int fd, return NULL; /* Allocate a buffer of sufficient size. */ - eassert (length < UINT32_MAX - sizeof *format12); - format12 = xmalloc (length + sizeof *format12); + format12 = xaddmalloc (sizeof *format12, length); format12->format = header->format; format12->reserved = header->length; format12->length = length; @@ -1594,7 +1630,7 @@ sfnt_read_loca_table_short (int fd, struct sfnt_offset_subtable *subtable) return NULL; /* Figure out how many glyphs there are based on the length. */ - loca = xmalloc (sizeof *loca + directory->length); + loca = xaddmalloc (sizeof *loca, directory->length); loca->offsets = (uint16_t *) (loca + 1); loca->num_offsets = directory->length / 2; @@ -1639,7 +1675,7 @@ sfnt_read_loca_table_long (int fd, struct sfnt_offset_subtable *subtable) return NULL; /* Figure out how many glyphs there are based on the length. */ - loca = xmalloc (sizeof *loca + directory->length); + loca = xaddmalloc (sizeof *loca, directory->length); loca->offsets = (uint32_t *) (loca + 1); loca->num_offsets = directory->length / 4; @@ -1771,7 +1807,7 @@ sfnt_read_glyf_table (int fd, struct sfnt_offset_subtable *subtable) return NULL; /* Allocate enough to hold everything. */ - glyf = xmalloc (sizeof *glyf + directory->length); + glyf = xaddmalloc (sizeof *glyf, directory->length); glyf->size = directory->length; glyf->glyphs = (unsigned char *) (glyf + 1); @@ -1885,6 +1921,8 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, unsigned char *vec_start; int16_t delta, x, y; + glyph->simple = NULL; + /* Calculate the minimum size of the glyph data. This is the size of the instruction length field followed by glyph->number_of_contours * sizeof (uint16_t). */ @@ -1893,14 +1931,11 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, + sizeof (uint16_t)); /* Check that the size is big enough. */ - if (glyf->size < offset + min_size) - { - glyph->simple = NULL; - return; - } + if (glyf->size < min_size || glyf->size - min_size < offset) + return; /* Allocate enough to read at least that. */ - simple = xmalloc (sizeof *simple + min_size); + simple = xaddmalloc (sizeof *simple, min_size); simple->end_pts_of_contours = (uint16_t *) (simple + 1); memcpy (simple->end_pts_of_contours, glyf->glyphs + offset, min_size); @@ -1928,16 +1963,15 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, else number_of_points = 0; - min_size_2 = (simple->instruction_length - + number_of_points - + (number_of_points - * sizeof (uint16_t) * 2)); - - /* Set simple->number_of_points. */ simple->number_of_points = number_of_points; /* Make simple big enough. */ - simple = xrealloc (simple, sizeof *simple + min_size + min_size_2); + size_t size; + if (ckd_mul (&min_size_2, number_of_points, sizeof (uint16_t) * 2 + 1) + || ckd_add (&min_size_2, min_size_2, simple->instruction_length) + || ckd_add (&size, min_size, min_size_2)) + memory_full_up (); + simple = xaddrealloc (simple, sizeof *simple, size); simple->end_pts_of_contours = (uint16_t *) (simple + 1); /* Set the instruction data pointer and other pointers. @@ -1958,7 +1992,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, || (instructions_start + simple->instruction_length >= glyf->glyphs + glyf->size)) { - glyph->simple = NULL; xfree (simple); return; } @@ -1973,7 +2006,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, if (flags_start >= glyf->glyphs + glyf->size) { - glyph->simple = NULL; xfree (simple); return; } @@ -1997,7 +2029,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, if (flags_start + 1 >= glyf->glyphs + glyf->size) { - glyph->simple = NULL; xfree (simple); return; } @@ -2025,7 +2056,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, if (i != number_of_points) { - glyph->simple = NULL; xfree (simple); return; } @@ -2051,7 +2081,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, if (vec_start + 1 > glyf->glyphs + glyf->size) { - glyph->simple = NULL; xfree (simple); return; } @@ -2068,7 +2097,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, if (vec_start + 2 > glyf->glyphs + glyf->size) { - glyph->simple = NULL; xfree (simple); return; } @@ -2102,7 +2130,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, if (vec_start + 1 > glyf->glyphs + glyf->size) { - glyph->simple = NULL; xfree (simple); return; } @@ -2119,7 +2146,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, if (vec_start + 2 > glyf->glyphs + glyf->size) { - glyph->simple = NULL; xfree (simple); return; } @@ -2139,7 +2165,6 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, /* All done. */ simple->y_coordinates_end = simple->y_coordinates + i; glyph->simple = simple; - return; } /* Read the compound glyph outline from the glyph GLYPH from the @@ -2209,8 +2234,7 @@ sfnt_read_compound_glyph (struct sfnt_glyph *glyph, } /* Now allocate the buffer to hold all the glyph data. */ - glyph->compound = xmalloc (sizeof *glyph->compound - + required_bytes); + glyph->compound = xaddmalloc (sizeof *glyph->compound, required_bytes); glyph->compound->components = (struct sfnt_compound_glyph_component *) (glyph->compound + 1); glyph->compound->num_components = num_components; @@ -2431,9 +2455,8 @@ sfnt_read_glyph (sfnt_glyph glyph_code, glyph.ymax = 0; glyph.advance_distortion = 0; glyph.origin_distortion = 0; - glyph.simple = xmalloc (sizeof *glyph.simple); + glyph.simple = xzalloc (sizeof *glyph.simple); glyph.compound = NULL; - memset (glyph.simple, 0, sizeof *glyph.simple); memory = xmalloc (sizeof *memory); *memory = glyph; return memory; @@ -3608,17 +3631,19 @@ sfnt_build_append (int flags, sfnt_fixed x, sfnt_fixed y) outline->outline_used++; - /* See if the outline has to be extended. Checking for overflow - should not be necessary. */ + /* See if the outline has to be extended. */ if (outline->outline_used > outline->outline_size) { + /* This can't overflow, as the old value did not overflow when + multiplied by sizeof *outline->outline. */ outline->outline_size = outline->outline_used * 2; /* Extend the outline to some size past the new size. */ - outline = xrealloc (outline, (sizeof *outline - + (outline->outline_size - * sizeof *outline->outline))); + size_t size; + if (ckd_mul (&size, outline->outline_size, sizeof *outline->outline)) + memory_full_up (); + outline = xaddrealloc (outline, sizeof *outline, size); outline->outline = (struct sfnt_glyph_outline_command *) (outline + 1); } @@ -4039,7 +4064,12 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline, sfnt_fixed dx, dy, bot, step_x, ymin, xmin; size_t top, bottom, y; - edges = alloca (outline->outline_used * sizeof *edges); + void *edges_alloc = NULL; + if (outline->outline_used < 1024 * 16 / sizeof *edges) + edges = alloca (outline->outline_used * sizeof *edges); + else + edges = edges_alloc = xnmalloc (outline->outline_used, sizeof *edges); + edge = 0; /* ymin and xmin must be the same as the offset used to set offy and @@ -4132,6 +4162,7 @@ sfnt_build_outline_edges (struct sfnt_glyph_outline *outline, if (edge) edge_proc (edges, edge, dcontext); + xfree (edges_alloc); } /* Sort an array of SIZE edges to increase by bottom Y position, in @@ -4484,11 +4515,15 @@ sfnt_raster_glyph_outline (struct sfnt_glyph_outline *outline) /* Get the raster parameters. */ sfnt_prepare_raster (&raster, outline); - /* Allocate the raster data. */ - data = xmalloc (sizeof *data + raster.stride * raster.height); + /* Allocate the raster data. Clear the raster; it is easier to use + xzalloc and clear everything. */ + size_t size; + if (ckd_mul (&size, raster.stride, raster.height) + || ckd_add (&size, size, sizeof *data)) + memory_full_up (); + data = xzalloc (size); *data = raster; data->cells = (unsigned char *) (data + 1); - memset (data->cells, 0, raster.stride * raster.height); /* Generate edges for the outline, polying each array of edges to the raster. */ @@ -4688,7 +4723,12 @@ sfnt_build_outline_fedges (struct sfnt_glyph_outline *outline, sfnt_fixed dx, dy, step_x, step_y, ymin, xmin; size_t top, bottom; - edges = alloca (outline->outline_used * sizeof *edges); + void *edges_alloc = NULL; + if (outline->outline_used < 1024 * 16 / sizeof *edges) + edges = alloca (outline->outline_used * sizeof *edges); + else + edges = edges_alloc = xnmalloc (outline->outline_used, sizeof *edges); + edge = 0; /* ymin and xmin must be the same as the offset used to set offy and @@ -4773,6 +4813,7 @@ sfnt_build_outline_fedges (struct sfnt_glyph_outline *outline, if (edge) edge_proc (edges, edge, dcontext); + xfree (edges_alloc); } typedef void (*sfnt_step_raster_proc) (struct sfnt_step_raster *, void *); @@ -5376,15 +5417,21 @@ TEST_STATIC struct sfnt_raster * sfnt_raster_glyph_outline_exact (struct sfnt_glyph_outline *outline) { struct sfnt_raster raster, *data; + size_t size; /* Get the raster parameters. */ sfnt_prepare_raster (&raster, outline); - /* Allocate the raster data. */ - data = xmalloc (sizeof *data + raster.stride * raster.height); + /* Allocate the raster data. Clear the raster; it is easier to use + xcalloc and clear everything. */ + if (ckd_mul (&size, raster.stride, raster.height) + || ckd_add (&size, size, sizeof *data)) + return NULL; + data = xcalloc (1, size); + if (!data) + return NULL; *data = raster; data->cells = (unsigned char *) (data + 1); - memset (data->cells, 0, raster.stride * raster.height); /* Generate edges for the outline, polying each array of edges to the raster. */ @@ -5444,7 +5491,7 @@ sfnt_read_hmtx_table (int fd, struct sfnt_offset_subtable *subtable, /* Now allocate enough to hold all of that along with the table directory structure. */ - hmtx = xmalloc (sizeof *hmtx + size); + hmtx = xaddmalloc (sizeof *hmtx, size); /* Read into hmtx + 1. */ rc = read (fd, hmtx + 1, size); @@ -5591,13 +5638,9 @@ sfnt_read_name_table (int fd, struct sfnt_offset_subtable *subtable) if (directory->length < required) return NULL; - /* Avoid overflow in xmalloc argument below. */ - if (directory->length > UINT_MAX - sizeof *name) - return NULL; - /* Allocate enough to hold the name table and variable length data. */ - name = xmalloc (sizeof *name + directory->length); + name = xaddmalloc (sizeof *name, directory->length); /* Read the fixed length data. */ rc = read (fd, name, required); @@ -5671,10 +5714,11 @@ sfnt_read_name_table (int fd, struct sfnt_offset_subtable *subtable) - (name->count * sizeof *name->name_records))) { - name = xrealloc (name, (sizeof *name - + (name->count - * sizeof *name->name_records) - + required)); + size_t size; + if (ckd_mul (&size, name->count, sizeof *name->name_records) + || ckd_add (&size, size, required)) + memory_full_up (); + name = xaddrealloc (name, sizeof *name, size); name->name_records = (struct sfnt_name_record *) (name + 1); } @@ -5801,7 +5845,7 @@ sfnt_read_meta_table (int fd, struct sfnt_offset_subtable *subtable) if (ckd_mul (&map_size, sizeof *meta->data_maps, meta->num_data_maps) /* Do so while checking for overflow from bad sfnt files. */ - || ckd_add (&data_size, map_size, sizeof *meta) + || directory->length - required < map_size || ckd_add (&data_size, data_size, directory->length)) { xfree (meta); @@ -5809,15 +5853,7 @@ sfnt_read_meta_table (int fd, struct sfnt_offset_subtable *subtable) } /* Do the reallocation. */ - meta = xrealloc (meta, data_size); - - /* Check that the remaining data is big enough to hold the data - maps. */ - if (directory->length - required < map_size) - { - xfree (meta); - return NULL; - } + meta = xaddrealloc (meta, sizeof *meta, data_size); /* Set pointers to data_maps and data. */ meta->data_maps = (struct sfnt_meta_data_map *) (meta + 1); @@ -5945,7 +5981,7 @@ sfnt_read_ttc_header (int fd) return NULL; } - ttc = xrealloc (ttc, sizeof *ttc + size); + ttc = xaddrealloc (ttc, sizeof *ttc, size); ttc->offset_table = (uint32_t *) (ttc + 1); rc = read (fd, ttc->offset_table, size); if (rc == -1 || rc < size) @@ -6113,7 +6149,7 @@ sfnt_read_fpgm_table (int fd, struct sfnt_offset_subtable *subtable) return NULL; /* Allocate enough for that much data. */ - fpgm = xmalloc (sizeof *fpgm + directory->length); + fpgm = xaddmalloc (sizeof *fpgm, directory->length); /* Now set fpgm->num_instructions as appropriate, and make fpgm->instructions point to the right place. */ @@ -6161,7 +6197,7 @@ sfnt_read_prep_table (int fd, struct sfnt_offset_subtable *subtable) return NULL; /* Allocate enough for that much data. */ - prep = xmalloc (sizeof *prep + directory->length); + prep = xaddmalloc (sizeof *prep, directory->length); /* Now set prep->num_instructions as appropriate, and make prep->instructions point to the right place. */ @@ -13498,22 +13534,17 @@ TEST_STATIC struct sfnt_uvs_context * sfnt_create_uvs_context (struct sfnt_cmap_format_14 *cmap, int fd) { struct sfnt_table_offset_rec *table_offsets, *rec, template; - size_t size, i, nmemb, j; + size_t i, nmemb, j; off_t offset; struct sfnt_uvs_context *context; - if (ckd_mul (&size, cmap->num_var_selector_records, - sizeof *table_offsets) - || ckd_mul (&size, size, 2)) - return NULL; - context = NULL; /* First, record and sort the UVS and nondefault UVS table offsets in ascending order. */ - table_offsets = xmalloc (size); - memset (table_offsets, 0, size); + table_offsets = xcalloc (cmap->num_var_selector_records, + 2 * sizeof *table_offsets); nmemb = cmap->num_var_selector_records * 2; j = 0; @@ -14196,13 +14227,12 @@ sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable) goto bail; /* Figure out how big gvar needs to be. */ - if (ckd_add (&min_bytes, coordinate_size, sizeof *gvar) - || ckd_add (&min_bytes, min_bytes, off_size) + if (ckd_add (&min_bytes, coordinate_size, off_size) || ckd_add (&min_bytes, min_bytes, data_size)) goto bail; /* Now allocate enough for all of this extra data. */ - gvar = xrealloc (gvar, min_bytes); + gvar = xaddrealloc (gvar, sizeof *gvar, min_bytes); /* Start reading offsets. */ @@ -14534,7 +14564,7 @@ sfnt_read_packed_deltas (unsigned char *restrict data, if (data >= end) return NULL; - deltas = xmalloc (sizeof *deltas * n); + deltas = xnmalloc (n, sizeof *deltas); i = 0; while (i < n) @@ -15326,7 +15356,7 @@ sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start, { size_t i, pair_start, pair_end, pair_first; - pair_start = pair_first = -1; + pair_start = pair_first = SIZE_MAX; /* Look for pairs of touched points. */ @@ -15335,7 +15365,7 @@ sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start, if (!touched[i]) continue; - if (pair_start == -1) + if (pair_start == SIZE_MAX) { pair_first = i; goto next; @@ -15538,10 +15568,11 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* Start reading each tuple. */ tuple = gvar->glyph_variation_data + offset + sizeof header; - if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) - coords = xmalloc (gvar->axis_count * sizeof *coords * 3); - else + void *coords_alloc = NULL; + if (gvar->axis_count < 1024 * 16 / (3 * sizeof *coords)) coords = alloca (gvar->axis_count * sizeof *coords * 3); + else + coords = coords_alloc = xnmalloc (gvar->axis_count, 3 * sizeof *coords); intermediate_start = coords + gvar->axis_count; intermediate_end = intermediate_start + gvar->axis_count; @@ -15551,6 +15582,8 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, touched = NULL; original_x = NULL; original_y = NULL; + void *touched_alloc = NULL; + void *original_x_alloc = NULL; while (ntuples--) { @@ -15702,21 +15735,23 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, if (!original_x) { - if ((glyph->simple->number_of_points - * sizeof *touched) >= 1024 * 16) - touched = xmalloc (sizeof *touched - * glyph->simple->number_of_points); - else + if (glyph->simple->number_of_points + < 1024 * 16 / sizeof *touched) touched = alloca (sizeof *touched * glyph->simple->number_of_points); - - if ((sizeof *original_x * 2 - * glyph->simple->number_of_points) >= 1024 * 16) - original_x = xmalloc (sizeof *original_x * 2 - * glyph->simple->number_of_points); else + touched = touched_alloc + = xnmalloc (glyph->simple->number_of_points, + sizeof *touched); + + if (glyph->simple->number_of_points + < 1024 * 16 / (2 * sizeof *original_x)) original_x = alloca (sizeof *original_x * 2 * glyph->simple->number_of_points); + else + original_x = original_x_alloc + = xnmalloc (glyph->simple->number_of_points, + 2 * sizeof *original_x); original_y = original_x + glyph->simple->number_of_points; } @@ -15777,16 +15812,9 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* Return success. */ - if ((glyph->simple->number_of_points - * sizeof *touched) >= 1024 * 16) - xfree (touched); - - if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) - xfree (coords); - - if ((sizeof *original_x * 2 - * glyph->simple->number_of_points) >= 1024 * 16) - xfree (original_x); + xfree (touched_alloc); + xfree (coords_alloc); + xfree (original_x_alloc); if (points != (uint16_t *) -1) xfree (points); @@ -15803,16 +15831,9 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, xfree (local_points); fail1: - if ((glyph->simple->number_of_points - * sizeof *touched) >= 1024 * 16) - xfree (touched); - - if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) - xfree (coords); - - if ((sizeof *original_x * 2 - * glyph->simple->number_of_points) >= 1024 * 16) - xfree (original_x); + xfree (touched_alloc); + xfree (coords_alloc); + xfree (original_x_alloc); if (points != (uint16_t *) -1) xfree (points); @@ -15916,10 +15937,11 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* Start reading each tuple. */ tuple = gvar->glyph_variation_data + offset + sizeof header; - if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) - coords = xmalloc (gvar->axis_count * sizeof *coords * 3); - else + void *coords_alloc = NULL; + if (gvar->axis_count < 1024 * 16 / (3 * sizeof *coords)) coords = alloca (gvar->axis_count * sizeof *coords * 3); + else + coords = coords_alloc = xnmalloc (gvar->axis_count, 3 * sizeof *coords); intermediate_start = coords + gvar->axis_count; intermediate_end = intermediate_start + gvar->axis_count; @@ -16157,9 +16179,7 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* Return success. */ - if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) - xfree (coords); - + xfree (coords_alloc); if (points != (uint16_t *) -1) xfree (points); @@ -16175,9 +16195,7 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, xfree (local_points); fail1: - if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) - xfree (coords); - + xfree (coords_alloc); if (points != (uint16_t *) -1) xfree (points); commit 7e0d4fae01f2f1c7411297b1fb99ecdf82bfdea4 Author: Paul Eggert Date: Sat May 23 11:52:09 2026 -0700 Simplify sfnt.c by using long long Even the earliest Android had plain ‘long long’, so use that instead of doing it by hand. * src/sfnt.c (struct sfnt_large_integer, sfnt_multiply_divide_1) (sfnt_multiply_divide_2, sfnt_large_integer_add) (sfnt_multiply_divide_round) [!INT64_MAX]: Remove. (sfnt_multiply_divide, sfnt_multiply_divide_rounded) (sfnt_mul_fixed, sfnt_mul_fixed_round, sfnt_div_fixed) (sfnt_div_f26dot6, sfnt_mul_f26dot6, sfnt_mul_f26dot6_round) (sfnt_mul_f2dot14, sfnt_dot_fix_14): Simplify by using long long. diff --git a/src/sfnt.c b/src/sfnt.c index e46ebc3a08b..225b172cb40 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -3647,161 +3647,23 @@ sfnt_build_append (int flags, sfnt_fixed x, sfnt_fixed y) return outline; } -#ifndef INT64_MAX - -/* 64 bit integer type. */ - -struct sfnt_large_integer -{ - unsigned int high, low; -}; - -/* Calculate (A * B), placing the result in *VALUE. */ - -static void -sfnt_multiply_divide_1 (unsigned int a, unsigned int b, - struct sfnt_large_integer *value) -{ - unsigned int lo1, hi1, lo2, hi2, lo, hi, i1, i2; - - lo1 = a & 0x0000ffffu; - hi1 = a >> 16; - lo2 = b & 0x0000ffffu; - hi2 = b >> 16; - - lo = lo1 * lo2; - i1 = lo1 * hi2; - i2 = lo2 * hi1; - hi = hi1 * hi2; - - /* Check carry overflow of i1 + i2. */ - i1 += i2; - hi += (unsigned int) (i1 < i2) << 16; - - hi += i1 >> 16; - i1 = i1 << 16; - - /* Check carry overflow of i1 + lo. */ - lo += i1; - hi += (lo < i1); - - value->low = lo; - value->high = hi; -} - -/* Calculate AB / C. Value is a 32 bit unsigned integer. */ - -static unsigned int -sfnt_multiply_divide_2 (struct sfnt_large_integer *ab, - unsigned int c) -{ - unsigned int hi, lo; - int i; - unsigned int r, q; /* Remainder and quotient. */ - - hi = ab->high; - lo = ab->low; - - i = stdc_leading_zeros (hi); - r = (hi << i) | (lo >> (32 - i)); - lo <<= i; - q = r / c; - r -= q * c; - i = 32 - i; - - do - { - q <<= 1; - r = (r << 1) | (lo >> 31); - lo <<= 1; - - if (r >= c) - { - r -= c; - q |= 1; - } - } - while (--i); - - return q; -} - -/* Add the specified unsigned 32-bit N to the large integer - INTEGER. */ - -static void -sfnt_large_integer_add (struct sfnt_large_integer *integer, - uint32_t n) -{ - struct sfnt_large_integer number; - - number.low = integer->low + n; - number.high = integer->high + (number.low - < integer->low); - - *integer = number; -} - -#endif /* !INT64_MAX */ - -/* Calculate (A * B) / C with no rounding and return the result, using - a 64 bit integer if necessary. */ +/* Calculate (A * B) / C with no rounding and return the result. */ static unsigned int sfnt_multiply_divide (unsigned int a, unsigned int b, unsigned int c) { -#ifndef INT64_MAX - struct sfnt_large_integer temp; - - sfnt_multiply_divide_1 (a, b, &temp); - return sfnt_multiply_divide_2 (&temp, c); -#else /* INT64_MAX */ - uint64_t temp; - - temp = (uint64_t) a * (uint64_t) b; - return temp / c; -#endif /* !INT64_MAX */ + return a * (unsigned long long int) {b} / c; } -/* Calculate (A * B) / C with rounding and return the result, using a - 64 bit integer if necessary. */ +/* Calculate (A * B) / C with rounding and return the result. */ static unsigned int sfnt_multiply_divide_rounded (unsigned int a, unsigned int b, unsigned int c) { -#ifndef INT64_MAX - struct sfnt_large_integer temp; - - sfnt_multiply_divide_1 (a, b, &temp); - sfnt_large_integer_add (&temp, c / 2); - return sfnt_multiply_divide_2 (&temp, c); -#else /* INT64_MAX */ - uint64_t temp; - - temp = (uint64_t) a * (uint64_t) b + c / 2; - return temp / c; -#endif /* !INT64_MAX */ -} - -#ifndef INT64_MAX - -/* Calculate (A * B) / C, rounding the result with a threshold of N. - Use a 64 bit temporary. */ - -static unsigned int -sfnt_multiply_divide_round (unsigned int a, unsigned int b, - unsigned int n, unsigned int c) -{ - struct sfnt_large_integer temp; - - sfnt_multiply_divide_1 (a, b, &temp); - sfnt_large_integer_add (&temp, n); - return sfnt_multiply_divide_2 (&temp, c); + return (a * (unsigned long long int) {b} + c / 2) / c; } -#endif /* !INT64_MAX */ - /* The same as sfnt_multiply_divide_rounded, but handle signed values instead. */ @@ -3831,27 +3693,7 @@ sfnt_multiply_divide_signed (int a, int b, int c) static sfnt_fixed sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y) { -#ifdef INT64_MAX - int64_t product; - - product = (int64_t) x * (int64_t) y; - - /* This can be done quickly with int64_t. */ - return product / (int64_t) 65536; -#else /* !INT64_MAX */ - int sign; - - sign = 1; - - if (x < 0) - sign = -sign; - - if (y < 0) - sign = -sign; - - return sfnt_multiply_divide (abs (x), abs (y), - 65536) * sign; -#endif /* INT64_MAX */ + return x * (long long int) {y} / (1 << 16); } /* Multiply the two 16.16 fixed point numbers X and Y, with rounding @@ -3860,28 +3702,8 @@ sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y) static sfnt_fixed sfnt_mul_fixed_round (sfnt_fixed x, sfnt_fixed y) { -#ifdef INT64_MAX - int64_t product, round; - - product = (int64_t) x * (int64_t) y; - round = product < 0 ? -32768 : 32768; - - /* This can be done quickly with int64_t. */ - return (product + round) / (int64_t) 65536; -#else /* !INT64_MAX */ - int sign; - - sign = 1; - - if (x < 0) - sign = -sign; - - if (y < 0) - sign = -sign; - - return sfnt_multiply_divide_round (abs (x), abs (y), - 32768, 65536) * sign; -#endif /* INT64_MAX */ + long long int product = x * (long long int) {y}; + return (product + (product < 0 ? -(1 << 15) : 1 << 15)) / (1 << 16); } /* Set the pen size to the specified point and return. POINT will be @@ -3924,29 +3746,7 @@ sfnt_line_to_and_build (struct sfnt_point point, void *dcontext) static sfnt_fixed sfnt_div_fixed (sfnt_fixed x, sfnt_fixed y) { -#ifdef INT64_MAX - int64_t result; - - result = ((int64_t) x * 65536) / y; - - return result; -#else - int sign; - unsigned int a, b; - - sign = 1; - - if (x < 0) - sign = -sign; - - if (y < 0) - sign = -sign; - - a = abs (x); - b = abs (y); - - return sfnt_multiply_divide (a, 65536, b) * sign; -#endif + return x * (1LL << 16) / y; } /* Return the ceiling value of the specified fixed point number X. */ @@ -6391,29 +6191,7 @@ sfnt_read_prep_table (int fd, struct sfnt_offset_subtable *subtable) static sfnt_f26dot6 sfnt_div_f26dot6 (sfnt_f26dot6 x, sfnt_f26dot6 y) { -#ifdef INT64_MAX - int64_t result; - - result = ((int64_t) x * 64) / y; - - return result; -#else - int sign; - unsigned int a, b; - - sign = 1; - - if (x < 0) - sign = -sign; - - if (y < 0) - sign = -sign; - - a = abs (x); - b = abs (y); - - return sfnt_multiply_divide (a, 64, b) * sign; -#endif + return x * (1LL << 6) / y; } /* Multiply the specified two 26.6 fixed point numbers A and B. @@ -6422,27 +6200,7 @@ sfnt_div_f26dot6 (sfnt_f26dot6 x, sfnt_f26dot6 y) static sfnt_f26dot6 sfnt_mul_f26dot6 (sfnt_f26dot6 a, sfnt_f26dot6 b) { -#ifdef INT64_MAX - int64_t product; - - product = (int64_t) a * (int64_t) b; - - /* This can be done quickly with int64_t. */ - return product / (int64_t) 64; -#else - int sign; - - sign = 1; - - if (a < 0) - sign = -sign; - - if (b < 0) - sign = -sign; - - return sfnt_multiply_divide (abs (a), abs (b), - 64) * sign; -#endif + return a * (long long int) {b} / (1 << 6); } /* Multiply the specified two 26.6 fixed point numbers A and B, with @@ -6452,27 +6210,7 @@ sfnt_mul_f26dot6 (sfnt_f26dot6 a, sfnt_f26dot6 b) static sfnt_f26dot6 sfnt_mul_f26dot6_round (sfnt_f26dot6 a, sfnt_f26dot6 b) { -#ifdef INT64_MAX - int64_t product; - - product = (int64_t) a * (int64_t) b; - - /* This can be done quickly with int64_t. */ - return (product + 32) / (int64_t) 64; -#else /* !INT64_MAX */ - int sign; - - sign = 1; - - if (a < 0) - sign = -sign; - - if (b < 0) - sign = -sign; - - return sfnt_multiply_divide_round (abs (a), abs (b), - 32, 64) * sign; -#endif /* INT64_MAX */ + return (a * (long long int) {b} + (1 << 5)) / (1 << 6); } /* Multiply the specified 2.14 number with another signed 32 bit @@ -6481,26 +6219,7 @@ sfnt_mul_f26dot6_round (sfnt_f26dot6 a, sfnt_f26dot6 b) static int32_t sfnt_mul_f2dot14 (sfnt_f2dot14 a, int32_t b) { -#ifdef INT64_MAX - int64_t product; - - product = (int64_t) a * (int64_t) b; - - return product / (int64_t) 16384; -#else - int sign; - - sign = 1; - - if (a < 0) - sign = -sign; - - if (b < 0) - sign = -sign; - - return sfnt_multiply_divide (abs (a), abs (b), - 16384) * sign; -#endif + return a * (long long int) {b} / (1 << 14); } /* Multiply the specified 26.6 fixed point number X by the specified @@ -10596,46 +10315,9 @@ sfnt_project_onto_y_axis_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy, static int32_t sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by) { -#ifndef INT64_MAX - int32_t m, s, hi1, hi2, hi; - uint32_t l, lo1, lo2, lo; - - - /* Compute ax*bx as 64-bit value. */ - l = (uint32_t) ((ax & 0xffffu) * bx); - m = (ax >> 16) * bx; - - lo1 = l + ((uint32_t) m << 16); - hi1 = (m >> 16) + ((int32_t) l >> 31) + (lo1 < l); - - /* Compute ay*by as 64-bit value. */ - l = (uint32_t) ((ay & 0xffffu) * by); - m = (ay >> 16) * by; - - lo2 = l + ((uint32_t) m << 16); - hi2 = (m >> 16) + ((int32_t) l >> 31) + (lo2 < l); - - /* Add them. */ - lo = lo1 + lo2; - hi = hi1 + hi2 + (lo < lo1); - - /* Divide the result by 2^14 with rounding. */ - s = hi >> 31; - l = lo + (uint32_t) s; - hi += s + (l < lo); - lo = l; - - l = lo + 0x2000u; - hi += (l < lo); - - return (int32_t) (((uint32_t) hi << 18) | (l >> 14)); -#else - int64_t xx, yy; - int64_t temp; - - xx = (int64_t) ax * bx; - yy = (int64_t) ay * by; - + long long int + xx = ax * (long long int) {bx}, + yy = ay * (long long int) {by}; xx += yy; yy = xx >> 63; xx += 0x2000 + yy; @@ -10643,10 +10325,9 @@ sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by) /* TrueType fonts rely on "division" here truncating towards negative infinity, so compute the arithmetic right shift in place of division. */ - temp = -(xx < 0); + long long int temp = -(xx < 0); temp = (temp ^ xx) >> 14 ^ temp; - return (int32_t) (temp); -#endif + return temp; } /* Project the specified vector VX and VY onto the unit vector that is commit 2e91ed5f129a4c03f7345d15d61232bd15028a4e Author: Paul Eggert Date: Sat May 23 09:59:25 2026 -0700 Prefer ptrdiff_t to size_t when either will do Signed types are a bit safer, as they avoid some comparison confusion and -fsanitize=undefined can check more misuses of them. * src/alloc.c (lisp_malloc, lisp_align_malloc) (allocate_string_data, allocate_vector_from_block, object_bytes): * src/coding.c (from_unicode_buffer): * src/decompress.c (acc_size, accumulate_and_process_md5): * src/emacs.c (load_seccomp, shut_down_emacs): * src/fns.c (sxhash_bignum): * src/ftfont.c (get_adstyle_property): * src/image.c (lookup_image, xpm_init_color_cache) (xpm_cache_color): * src/json.c (json_out_str, struct json_parser) (json_make_object_workspace_for_slow_path) (json_make_object_workspace_for, json_parse_array) (json_parse_object): * src/sysdep.c (get_current_dir_name_or_unreachable) (init_sys_modes, convert_speed): * src/termchar.h (struct tty_display_info): * src/textconv.h (struct textconv_conversion_text): * src/xfns.c (struct x_xim_text_conversion_data) (x_encode_xim_text): * src/xselect.c (struct transfer, c_size_for_format) (x_size_for_format, selection_data_for_offset) (selection_data_size, x_start_selection_transfer) (x_continue_selection_transfer): Prefer ptrdiff_t to size_t when either will do. * src/term.c (Ftty__set_output_buffer_size): Limit output buffer size to PTRDIFF_MAX as well as to SIZE_MAX. diff --git a/src/alloc.c b/src/alloc.c index d6f11d06766..ed0d6f4976d 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -899,7 +899,7 @@ void *lisp_malloc_loser EXTERNALLY_VISIBLE; L == make_lisp_ptr (P, T), then XPNTR (L) == P and XTYPE (L) == T. */ static void * -lisp_malloc (size_t nbytes, bool clearit, enum mem_type type) +lisp_malloc (ptrdiff_t nbytes, bool clearit, enum mem_type type) { register void *val; @@ -1083,7 +1083,7 @@ pointer_align (void *ptr, int alignment) Alignment is on a multiple of BLOCK_ALIGN and `nbytes' has to be smaller or equal to BLOCK_BYTES. */ static void * -lisp_align_malloc (size_t nbytes, enum mem_type type) +lisp_align_malloc (ptrdiff_t nbytes, enum mem_type type) { void *base, *val; struct ablocks *abase; @@ -1522,7 +1522,7 @@ sdata_size (ptrdiff_t n) /* Exact bound on the number of bytes in a string, not counting the terminating null. A string cannot contain more bytes than - STRING_BYTES_BOUND, nor can it be so long that the size_t + STRING_BYTES_BOUND, nor can it be so long that the arithmetic in allocate_string_data would overflow while it is calculating a value to be passed to malloc. */ static ptrdiff_t const STRING_BYTES_MAX = @@ -1768,7 +1768,7 @@ allocate_string_data (struct Lisp_String *s, if (nbytes > LARGE_STRING_BYTES || immovable) { - size_t size = FLEXSIZEOF (struct sblock, data, needed); + ptrdiff_t size = FLEXSIZEOF (struct sblock, data, needed); #ifdef DOUG_LEA_MALLOC if (!mmap_lisp_allowed_p ()) @@ -2996,7 +2996,7 @@ allocate_vector_from_block (ptrdiff_t nbytes) { struct Lisp_Vector *vector; struct vector_block *block; - size_t index, restbytes; + ptrdiff_t index, restbytes; eassume (VBLOCK_BYTES_MIN <= nbytes && nbytes <= VBLOCK_BYTES_MAX); eassume (nbytes % roundup_size == 0); @@ -3022,7 +3022,7 @@ allocate_vector_from_block (ptrdiff_t nbytes) { /* This vector is larger than requested. */ vector = vector_free_lists[index]; - size_t vector_nbytes = pseudovector_nbytes (&vector->header); + ptrdiff_t vector_nbytes = pseudovector_nbytes (&vector->header); eassert (vector_nbytes > nbytes); ASAN_UNPOISON_VECTOR_CONTENTS (vector, nbytes - header_size); vector_free_lists[index] = next_vector (vector); @@ -5465,10 +5465,10 @@ inhibit_garbage_collection (void) } /* Return the number of bytes in N objects each of size S, guarding - against overflow if size_t is narrower than byte_ct. */ + against overflow if ptrdiff_t is narrower than byte_ct. */ static byte_ct -object_bytes (object_ct n, size_t s) +object_bytes (object_ct n, ptrdiff_t s) { byte_ct b = s; return n * b; diff --git a/src/coding.c b/src/coding.c index 9aba2a7eacc..7d2c7040ab8 100644 --- a/src/coding.c +++ b/src/coding.c @@ -8554,19 +8554,17 @@ from_unicode_buffer (const wchar_t *wstr) strings are extended to 32-bit wchar_t. */ uint16_t *words; - size_t length, i; - - length = wcslen (wstr) + 1; + ptrdiff_t length = wcslen (wstr); USE_SAFE_ALLOCA; - SAFE_NALLOCA (words, sizeof *words, length); + SAFE_NALLOCA (words, sizeof *words, length + 1); - for (i = 0; i < length - 1; ++i) + for (ptrdiff_t i = 0; i < length; i++) words[i] = wstr[i]; + words[length] = '\0'; - words[i] = '\0'; AUTO_STRING_WITH_LEN (str, (char *) words, - (length - 1) * sizeof *words); + length * sizeof *words); return unbind_to (sa_count, from_unicode (str)); #endif } diff --git a/src/decompress.c b/src/decompress.c index c81559c25f0..bb3ed6d7d6d 100644 --- a/src/decompress.c +++ b/src/decompress.c @@ -70,10 +70,10 @@ init_zlib_functions (void) # define MD5_BLOCKSIZE 32768 /* From md5.c */ static char acc_buff[2 * MD5_BLOCKSIZE]; -static size_t acc_size; +static ptrdiff_t acc_size; static void -accumulate_and_process_md5 (void *data, size_t len, struct md5_ctx *ctxt) +accumulate_and_process_md5 (void *data, ptrdiff_t len, struct md5_ctx *ctxt) { eassert (len <= MD5_BLOCKSIZE); /* We may optimize this saving some of these memcpy/move using diff --git a/src/emacs.c b/src/emacs.c index a9b970effb4..d50f817cefc 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1235,39 +1235,33 @@ load_seccomp (const char *file) file, (long) stat.st_size); goto out; } - size_t size = stat.st_size; - size_t count = size / sizeof *program.filter; - eassert (0 < count && count < SIZE_MAX); - if (USHRT_MAX < count) + if (ckd_add (&program.len, stat.st_size / sizeof *program.filter, 0)) { fprintf (stderr, "seccomp filter %s is too big\n", file); goto out; } /* Try reading one more byte to detect file size changes. */ + ptrdiff_t size = stat.st_size; buffer = malloc (size + 1); if (buffer == NULL) { emacs_perror ("malloc"); goto out; } - ptrdiff_t read = read_full (fd, buffer, size + 1); - if (read < 0) + ptrdiff_t nread = read_full (fd, buffer, size + 1); + if (nread != size) { - emacs_perror ("read"); - goto out; - } - eassert (read <= SIZE_MAX); - if (read != size) - { - fprintf (stderr, - "seccomp filter %s changed size while reading\n", - file); + if (nread < 0) + emacs_perror ("read"); + else + fprintf (stderr, + "seccomp filter %s changed size while reading\n", + file); goto out; } if (emacs_close (fd) != 0) emacs_perror ("close"); /* not a fatal error */ fd = -1; - program.len = count; program.filter = buffer; /* See man page of `seccomp' why this is necessary. Note that we @@ -3131,7 +3125,7 @@ shut_down_emacs (int sig, Lisp_Object stuff) + INT_STRLEN_BOUND (int) + 1), min (PIPE_BUF, MAX_ALLOCA))]; char const *sig_desc = safe_strsignal (sig); - size_t sig_desclen = strlen (sig_desc); + ptrdiff_t sig_desclen = strlen (sig_desc); int nlen = sprintf (buf, fmt, sig); if (nlen + sig_desclen < sizeof buf - 1) { diff --git a/src/fns.c b/src/fns.c index 98671fc1318..e3d297102ca 100644 --- a/src/fns.c +++ b/src/fns.c @@ -5480,10 +5480,10 @@ static EMACS_UINT sxhash_bignum (Lisp_Object bignum) { mpz_t const *n = xbignum_val (bignum); - size_t i, nlimbs = mpz_size (*n); - EMACS_UINT hash = mpz_sgn(*n) < 0; + ptrdiff_t nlimbs = mpz_size (*n); + EMACS_UINT hash = mpz_sgn (*n) < 0; - for (i = 0; i < nlimbs; ++i) + for (ptrdiff_t i = 0; i < nlimbs; i++) hash = sxhash_combine (hash, mpz_getlimbn (*n, i)); return hash; diff --git a/src/ftfont.c b/src/ftfont.c index 6de7110e5bb..44c22d359b5 100644 --- a/src/ftfont.c +++ b/src/ftfont.c @@ -150,7 +150,6 @@ get_adstyle_property (FcPattern *p) { FcChar8 *fcstr; char *str, *end, *tmp; - size_t i; Lisp_Object adstyle; #ifdef FC_FONTFORMAT @@ -173,7 +172,7 @@ get_adstyle_property (FcPattern *p) and therefore must be replaced by substitutes. (bug#70989) */ USE_SAFE_ALLOCA; tmp = SAFE_ALLOCA (end - str); - for (i = 0; i < end - str; ++i) + for (ptrdiff_t i = 0; i < end - str; i++) tmp[i] = ((str[i] != '?' && str[i] != '*' && str[i] != '"' diff --git a/src/image.c b/src/image.c index 38f9d1416a7..f41a08eb1a8 100644 --- a/src/image.c +++ b/src/image.c @@ -3562,7 +3562,7 @@ lookup_image (struct frame *f, Lisp_Object spec, int face_id) img->face_font_size = font_size; img->face_font_height = face->font->height; img->face_font_width = face->font->average_width; - size_t len = strlen (font_family) + 1; + ptrdiff_t len = strlen (font_family) + 1; img->face_font_family = xmalloc (len); memcpy (img->face_font_family, font_family, len); img->load_failed_p = ! img->type->load_img (f, img); @@ -5538,7 +5538,7 @@ static struct xpm_cached_color **xpm_color_cache; static void xpm_init_color_cache (struct frame *f, XpmAttributes *attrs) { - size_t nbytes = XPM_COLOR_CACHE_BUCKETS * sizeof *xpm_color_cache; + ptrdiff_t nbytes = XPM_COLOR_CACHE_BUCKETS * sizeof *xpm_color_cache; xpm_color_cache = xzalloc (nbytes); init_color_table (); @@ -5598,8 +5598,8 @@ xpm_cache_color (struct frame *f, char *color_name, XColor *color, int bucket) if (bucket < 0) bucket = xpm_color_bucket (color_name); - size_t len = strlen (color_name) + 1; - size_t nbytes = FLEXSIZEOF (struct xpm_cached_color, name, len); + ptrdiff_t len = strlen (color_name) + 1; + ptrdiff_t nbytes = FLEXSIZEOF (struct xpm_cached_color, name, len); struct xpm_cached_color *p = xmalloc (nbytes); memcpy (p->name, color_name, len); p->color = *color; diff --git a/src/json.c b/src/json.c index 720654bac43..5186667a9d2 100644 --- a/src/json.c +++ b/src/json.c @@ -273,7 +273,7 @@ json_make_room (json_out_t *jo, ptrdiff_t bytes) /* Add `bytes` bytes from `str` to the buffer. */ static void -json_out_str (json_out_t *jo, const char *str, size_t bytes) +json_out_str (json_out_t *jo, const char *str, ptrdiff_t bytes) { json_make_room (jo, bytes); memcpy (jo->buf + jo->size, str, bytes); @@ -693,7 +693,7 @@ struct json_parser struct json_configuration conf; - size_t additional_bytes_count; + ptrdiff_t additional_bytes_count; /* Lisp_Objects are collected in this area during object/array parsing. To avoid allocations, initially @@ -704,8 +704,8 @@ struct json_parser Lisp_Object internal_object_workspace [JSON_PARSER_INTERNAL_OBJECT_WORKSPACE_SIZE]; Lisp_Object *object_workspace; - size_t object_workspace_size; - size_t object_workspace_current; + ptrdiff_t object_workspace_size; + ptrdiff_t object_workspace_current; /* String and number parsing uses this workspace. The idea behind internal_byte_workspace is the same as the idea behind @@ -805,10 +805,9 @@ json_make_object_workspace_for_slow_path (struct json_parser *parser, { bool internal = (parser->object_workspace_size == JSON_PARSER_INTERNAL_OBJECT_WORKSPACE_SIZE); - ptrdiff_t new_workspace_size = parser->object_workspace_size; Lisp_Object *new_workspace_ptr = xpalloc (internal ? NULL : parser->object_workspace, - &new_workspace_size, + &parser->object_workspace_size, size - (parser->object_workspace_size - parser->object_workspace_current), -1, sizeof (Lisp_Object)); @@ -816,12 +815,11 @@ json_make_object_workspace_for_slow_path (struct json_parser *parser, memcpy (new_workspace_ptr, parser->object_workspace, sizeof (Lisp_Object) * parser->object_workspace_current); parser->object_workspace = new_workspace_ptr; - parser->object_workspace_size = new_workspace_size; } INLINE void json_make_object_workspace_for (struct json_parser *parser, - size_t size) + ptrdiff_t size) { if (parser->object_workspace_size - parser->object_workspace_current < size) @@ -1350,7 +1348,7 @@ json_parse_array (struct json_parser *parser) { int c = json_skip_whitespace (parser); - const size_t first = parser->object_workspace_current; + const ptrdiff_t first = parser->object_workspace_current; Lisp_Object result = Qnil; if (c != ']') @@ -1402,10 +1400,10 @@ json_parse_array (struct json_parser *parser) { case json_array_array: { - size_t number_of_elements + ptrdiff_t number_of_elements = parser->object_workspace_current - first; result = make_vector (number_of_elements, Qnil); - for (size_t i = 0; i < number_of_elements; i++) + for (ptrdiff_t i = 0; i < number_of_elements; i++) { rarely_quit (i); ASET (result, i, parser->object_workspace[first + i]); @@ -1441,7 +1439,7 @@ json_parse_object (struct json_parser *parser) { int c = json_skip_whitespace (parser); - const size_t first = parser->object_workspace_current; + const ptrdiff_t first = parser->object_workspace_current; Lisp_Object result = Qnil; if (c != '}') @@ -1517,10 +1515,10 @@ json_parse_object (struct json_parser *parser) { case json_object_hashtable: { - EMACS_INT value = (parser->object_workspace_current - first) / 2; + EMACS_INT value = (parser->object_workspace_current - first) >> 1; result = make_hash_table (&hashtest_equal, value, Weak_None); struct Lisp_Hash_Table *h = XHASH_TABLE (result); - for (size_t i = first; i < parser->object_workspace_current; i += 2) + for (ptrdiff_t i = first; i < parser->object_workspace_current; i += 2) { hash_hash_t hash; Lisp_Object key = parser->object_workspace[i]; diff --git a/src/sysdep.c b/src/sysdep.c index b2cd769784c..2f48b7c6681 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -304,7 +304,7 @@ get_current_dir_name_or_unreachable (void) } # endif - size_t pwdlen; + ptrdiff_t pwdlen; struct stat dotstat, pwdstat; pwd = getenv ("PWD"); @@ -1310,9 +1310,9 @@ init_sys_modes (struct tty_display_info *tty_out) } #endif /* F_GETOWN */ - const size_t buffer_size = (tty_out->output_buffer_size - ? tty_out->output_buffer_size - : BUFSIZ); + const ptrdiff_t buffer_size = (tty_out->output_buffer_size + ? tty_out->output_buffer_size + : BUFSIZ); setvbuf (tty_out->output, NULL, _IOFBF, buffer_size); if (tty_out->terminal->set_terminal_modes_hook) @@ -3136,7 +3136,7 @@ static const struct speed_struct speeds[] = static speed_t convert_speed (speed_t speed) { - for (size_t i = 0; i < ARRAYELTS (speeds); i++) + for (ptrdiff_t i = 0; i < ARRAYELTS (speeds); i++) { if (speed == speeds[i].internal) return speed; diff --git a/src/term.c b/src/term.c index e4fdb85831c..8047e4102a1 100644 --- a/src/term.c +++ b/src/term.c @@ -2613,7 +2613,7 @@ This function temporarily suspends and resumes the terminal device. */) (Lisp_Object size, Lisp_Object tty) { - if (!TYPE_RANGED_FIXNUMP (size_t, size)) + if (!RANGED_FIXNUMP (0, size, min (PTRDIFF_MAX, SIZE_MAX))) error ("Invalid output buffer size"); Fsuspend_tty (tty); struct terminal *terminal = decode_tty_terminal (tty); diff --git a/src/termchar.h b/src/termchar.h index d9390db17b2..5bc2dd84bee 100644 --- a/src/termchar.h +++ b/src/termchar.h @@ -56,7 +56,7 @@ struct tty_display_info /* Size of output buffer. A value of zero means use the default of BUFIZE. If non-zero, also minimize writes to the tty by avoiding calls to flush. */ - size_t output_buffer_size; + ptrdiff_t output_buffer_size; FILE *termscript; /* If nonzero, send all terminal output characters to this stream also. */ diff --git a/src/textconv.h b/src/textconv.h index bd6bf06bb76..5ab2c052fa3 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -80,7 +80,7 @@ enum textconv_operation struct textconv_conversion_text { /* Length of the text in characters and bytes. */ - size_t length, bytes; + ptrdiff_t length, bytes; /* Pointer to the text data. This must be deallocated by the caller. */ diff --git a/src/xfns.c b/src/xfns.c index ba3bf211f55..976218819dd 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -3467,7 +3467,7 @@ struct x_xim_text_conversion_data struct coding_system *coding; char *source; struct x_display_info *dpyinfo; - size_t size; + ptrdiff_t size; }; static Lisp_Object @@ -3600,7 +3600,7 @@ x_xim_text_to_utf8_unix (struct x_display_info *dpyinfo, static char * x_encode_xim_text (struct x_display_info *dpyinfo, char *text, - size_t size, ptrdiff_t *length, + ptrdiff_t size, ptrdiff_t *length, ptrdiff_t *chars) { struct coding_system coding; diff --git a/src/xselect.c b/src/xselect.c index 9e343f16544..27b83825b34 100644 --- a/src/xselect.c +++ b/src/xselect.c @@ -522,7 +522,7 @@ struct transfer /* The current offset in items into the selection data, and the number of items to send with each ChangeProperty request. */ - size_t offset, items_per_request; + ptrdiff_t offset, items_per_request; /* The display info associated with the transfer. */ struct x_display_info *dpyinfo; @@ -669,7 +669,7 @@ x_selection_request_lisp_error (void) -static size_t +static ptrdiff_t c_size_for_format (int format) { switch (format) @@ -687,7 +687,7 @@ c_size_for_format (int format) emacs_abort (); } -static size_t +static ptrdiff_t x_size_for_format (int format) { switch (format) @@ -712,10 +712,10 @@ x_size_for_format (int format) static unsigned char * selection_data_for_offset (struct selection_data *data, - size_t offset, size_t *remaining) + ptrdiff_t offset, ptrdiff_t *remaining) { unsigned char *base; - size_t size; + ptrdiff_t size; if (!NILP (data->string)) { @@ -746,10 +746,10 @@ selection_data_for_offset (struct selection_data *data, FIXME: Silent truncation is bad. */ -static size_t +static ptrdiff_t selection_data_size (struct selection_data *data) { - size_t scratch; + ptrdiff_t scratch; ptrdiff_t max_selection_size = min (min (PTRDIFF_MAX, SIZE_MAX), X_ULONG_MAX); @@ -856,7 +856,7 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, intmax_t timeout; intmax_t secs; int nsecs; - size_t remaining, max_size; + ptrdiff_t remaining, max_size; unsigned char *xdata; unsigned long data_size; @@ -892,9 +892,9 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, max_size = selection_quantum (dpyinfo->display); - size_t seldata_size = selection_data_size (&transfer->data); + ptrdiff_t seldata_size = selection_data_size (&transfer->data); TRACE3 (" x_start_selection_transfer: transferring to 0x%lx. " - "transfer consists of %zu bytes, quantum being %zu", + "transfer consists of %tu bytes, quantum being %tu", requestor, seldata_size, max_size); if (max_size < seldata_size) @@ -905,7 +905,7 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, transfer->items_per_request = (max_size / x_size_for_format (transfer->data.format)); TRACE1 (" x_start_selection_transfer: starting incremental" - " selection transfer, with %zu items per request", + " selection transfer, with %tu items per request", transfer->items_per_request); /* Next, link the transfer onto the list of pending selection @@ -954,7 +954,7 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, eassert (remaining <= INT_MAX); TRACE1 (" x_start_selection_transfer: writing" - " %zu elements directly to requestor window", + " %tu elements directly to requestor window", remaining); x_ignore_errors_for_next_request (dpyinfo, 0); @@ -977,7 +977,7 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, static void x_continue_selection_transfer (struct transfer *transfer) { - size_t remaining; + ptrdiff_t remaining; unsigned char *xdata; xdata = selection_data_for_offset (&transfer->data, @@ -1006,8 +1006,8 @@ x_continue_selection_transfer (struct transfer *transfer) } else { - TRACE2 (" x_continue_selection_transfer: writing %zu items" - "; current offset is %zu", remaining, transfer->offset); + TRACE2 (" x_continue_selection_transfer: writing %tu items" + "; current offset is %tu", remaining, transfer->offset); eassert (remaining <= INT_MAX); transfer->offset += remaining; commit 82ad01b631a4ff4508bb3d37d32925d0cc6771ee Author: Paul Eggert Date: Sat May 23 09:38:36 2026 -0700 Fix format typos in never-executed textconv.c * src/textconv.c (really_commit_text, really_replace_text): Use %td not %zd. diff --git a/src/textconv.c b/src/textconv.c index 1331eb06a65..7a6dd4f05e6 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -633,7 +633,7 @@ really_commit_text (struct frame *f, EMACS_INT position, otherwise. */ mark = get_mark (); - TEXTCONV_DEBUG ("the mark is: %zd", mark); + TEXTCONV_DEBUG ("the mark is: %td", mark); if (MARKERP (f->conversion.compose_region_start) || mark != -1) { /* Replace its contents. Set START and END to the start and end @@ -651,7 +651,7 @@ really_commit_text (struct frame *f, EMACS_INT position, end = max (mark, PT); } - TEXTCONV_DEBUG ("replacing text in composing region: %zd, %zd", + TEXTCONV_DEBUG ("replacing text in composing region: %td, %td", start, end); /* If it transpires that the start of the compose region is not @@ -774,7 +774,7 @@ really_commit_text (struct frame *f, EMACS_INT position, call0 (Qdeactivate_mark); /* Print some debugging information. */ - TEXTCONV_DEBUG ("text inserted: %s, point now: %zd", + TEXTCONV_DEBUG ("text inserted: %s, point now: %td", SSDATA (text), PT); /* Update the ephemeral last point. */ @@ -1438,7 +1438,7 @@ really_replace_text (struct frame *f, ptrdiff_t start, ptrdiff_t end, } /* Print some debugging information. */ - TEXTCONV_DEBUG ("text inserted: %s, point now: %zd", + TEXTCONV_DEBUG ("text inserted: %s, point now: %td", SSDATA (text), PT); /* Update the ephemeral last point. */ commit fbd2f781b2a75554cc5571490198f7dfff18f9f3 Author: Paul Eggert Date: Sat May 23 09:34:30 2026 -0700 Be more careful about X selection sizes * src/xselect.c (selection_data_for_offset): Offset is size_t, not long, since that’s what caller passes. (selection_data_size): Truncate large selection sizes to a value that is more likely to work without involving undefined behavior. Do not exceed X_ULONG_MAX which is all X can handle, or PTRDIFF_MAX which can confuse underlying code. (x_start_selection_transfer): Invoke selection_data_size just once. diff --git a/src/xselect.c b/src/xselect.c index 5be009af0d7..9e343f16544 100644 --- a/src/xselect.c +++ b/src/xselect.c @@ -712,7 +712,7 @@ x_size_for_format (int format) static unsigned char * selection_data_for_offset (struct selection_data *data, - long offset, size_t *remaining) + size_t offset, size_t *remaining) { unsigned char *base; size_t size; @@ -741,12 +741,17 @@ selection_data_for_offset (struct selection_data *data, /* Return the size, in bytes transferred to the X server, of data->size items of selection data in data->format-bit - quantities. */ + quantities. If this size is too large, silently return + the largest supported size in bytes for this format. + + FIXME: Silent truncation is bad. */ static size_t selection_data_size (struct selection_data *data) { size_t scratch; + ptrdiff_t max_selection_size = min (min (PTRDIFF_MAX, SIZE_MAX), + X_ULONG_MAX); if (!NILP (data->string)) return SBYTES (data->string); @@ -754,17 +759,19 @@ selection_data_size (struct selection_data *data) switch (data->format) { case 8: - return (size_t) data->size; + return min (data->size, max_selection_size); case 16: - if (ckd_mul (&scratch, data->size, 2)) - return SIZE_MAX; + if (ckd_mul (&scratch, data->size, 2) + || max_selection_size - max_selection_size % 2 < scratch) + return max_selection_size - max_selection_size % 2; return scratch; case 32: - if (ckd_mul (&scratch, data->size, 4)) - return SIZE_MAX; + if (ckd_mul (&scratch, data->size, 4) + || max_selection_size - max_selection_size % 4 < scratch) + return max_selection_size - max_selection_size % 4; return scratch; } @@ -885,12 +892,12 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, max_size = selection_quantum (dpyinfo->display); + size_t seldata_size = selection_data_size (&transfer->data); TRACE3 (" x_start_selection_transfer: transferring to 0x%lx. " "transfer consists of %zu bytes, quantum being %zu", - requestor, selection_data_size (&transfer->data), - max_size); + requestor, seldata_size, max_size); - if (selection_data_size (&transfer->data) > max_size) + if (max_size < seldata_size) { /* Begin incremental selection transfer. First, calculate how many elements it is ok to write for every ChangeProperty @@ -918,7 +925,7 @@ x_start_selection_transfer (struct x_display_info *dpyinfo, Window requestor, /* Now, write the INCR property to begin incremental selection transfer. offset is currently 0. */ - data_size = selection_data_size (&transfer->data); + data_size = seldata_size; /* Set SELECTED_EVENTS before the actual XSelectInput request. */ commit 1bee33c1c801adf5e3deeba7328b811b80c55f70 Author: Paul Eggert Date: Fri May 22 18:11:09 2026 -0700 sfnt_parse_languages does not need USE_SAFE_ALLOCA * src/sfntfont.c (sfnt_parse_languages): Simplify so that no local array is needed. diff --git a/src/sfntfont.c b/src/sfntfont.c index 4f49f03d744..858c5449ac7 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -581,20 +581,18 @@ static void sfnt_parse_languages (struct sfnt_meta_table *meta, struct sfnt_font_desc *desc) { - char *data, *metadata, *tag; + char *data; struct sfnt_meta_data_map map; - char *saveptr; /* Look up the ``design languages'' metadata. This is a comma (and possibly space) separated list of scripts that the font was designed for. Here is an example of one such tag: - zh-Hans,Jpan,Kore + zh-Hans,Japn,Kore for a font that covers Simplified Chinese, along with Japanese and Korean text. */ - saveptr = NULL; data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_DLNG, &map); @@ -608,34 +606,13 @@ sfnt_parse_languages (struct sfnt_meta_table *meta, return; } - USE_SAFE_ALLOCA; - - /* Now copy metadata and add a trailing NULL byte. */ - - if (map.data_length >= SIZE_MAX) - memory_full_up (); - - metadata = SAFE_ALLOCA ((size_t) map.data_length + 1); - memcpy (metadata, data, map.data_length); - metadata[map.data_length] = '\0'; - - /* Loop through each script-language tag. Note that there may be - extra leading spaces. */ - while ((tag = strtok_r (metadata, ",", &saveptr))) - { - metadata = NULL; - - if (strstr (tag, "Hans") || strstr (tag, "Hant")) - desc->languages = Fcons (Qzh, desc->languages); - - if (strstr (tag, "Japn")) - desc->languages = Fcons (Qja, desc->languages); - - if (strstr (tag, "Kore")) - desc->languages = Fcons (Qko, desc->languages); - } - - SAFE_FREE (); + if (memmem (data, map.data_length, "Hans", 4) + || memmem (data, map.data_length, "Hant", 4)) + desc->languages = Fcons (Qzh, desc->languages); + if (memmem (data, map.data_length, "Japn", 4)) + desc->languages = Fcons (Qja, desc->languages); + if (memmem (data, map.data_length, "Kore", 4)) + desc->languages = Fcons (Qko, desc->languages); } /* Return the font registry corresponding to the encoding subtable commit ece22174e52debbd96f2129c16bb539f48e9b849 Author: Paul Eggert Date: Fri May 22 17:32:45 2026 -0700 Omit useless android_get_image casts * src/android.c (android_get_image): Omit useless and confusing casts. diff --git a/src/android.c b/src/android.c index cd950d3c51c..7ff7f26b527 100644 --- a/src/android.c +++ b/src/android.c @@ -4943,9 +4943,7 @@ android_get_image (android_drawable handle, if (bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8) { - if (ckd_mul (&byte_size, - (size_t) bitmap_info.stride, - (size_t) bitmap_info.height)) + if (ckd_mul (&byte_size, bitmap_info.stride, bitmap_info.height)) { ANDROID_DELETE_LOCAL_REF (bitmap); memory_full_up (); commit 4e5103a980765633a72a90b9f616bcc055306aa0 Author: Paul Eggert Date: Fri May 22 17:19:33 2026 -0700 Document PTRDIFF_MAX <= SIZE_MAX assumption * src/alloc.c: New static_assert. diff --git a/src/alloc.c b/src/alloc.c index 1f4e5434e74..d6f11d06766 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -638,6 +638,14 @@ static_assert (LISP_ALIGNMENT % GCALIGNMENT == 0); enum { MALLOC_IS_LISP_ALIGNED = alignof (max_align_t) % LISP_ALIGNMENT == 0 }; static_assert (MALLOC_IS_LISP_ALIGNED); +/* Most of Emacs does not assume PTRDIFF_MAX <= SIZE_MAX, and may use + expressions like min (PTRDIFF_MAX, SIZE_MAX) to port even to + theoretical platforms where the assumption does not hold. + However, some parts of Emacs pass nonnegative ptrdiff_t values to + allocator functions like xmalloc that expect size_t. + This is portable in practice; check it here to document the assumption. */ +static_assert (PTRDIFF_MAX <= SIZE_MAX); + #define MALLOC_PROBE(size) \ do { \ if (profiler_memory_running) \ commit d12e8a94f7042a8490f3e2e40726390d3b735f58 Author: Paul Eggert Date: Fri May 22 12:46:15 2026 -0700 Update src/alloc.c comments diff --git a/src/alloc.c b/src/alloc.c index 6a81fed2d69..1f4e5434e74 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -644,7 +644,7 @@ static_assert (MALLOC_IS_LISP_ALIGNED); malloc_probe (size); \ } while (0) -/* Like malloc but check for no memory and block interrupt input. */ +/* Like malloc but check for no memory, and profile allocations. */ void * xmalloc (size_t size) @@ -686,7 +686,7 @@ xcalloc (size_t n, size_t s) return val; } -/* Like realloc but check for no memory and block interrupt input. */ +/* Like realloc but check for no memory, and profile allocations. */ void * xrealloc (void *block, size_t size) @@ -699,7 +699,7 @@ xrealloc (void *block, size_t size) } -/* Like free but block interrupt input. */ +/* Like free but do not free pdumper objects. */ void xfree (void *block) @@ -721,7 +721,7 @@ static_assert (INT_MAX <= PTRDIFF_MAX); /* Allocate an array of NITEMS items, each of size ITEM_SIZE. - Signal an error on memory exhaustion, and block interrupt input. */ + Signal an error on memory exhaustion, and profile allocations. */ void * xnmalloc (ptrdiff_t nitems, ptrdiff_t item_size) @@ -735,7 +735,7 @@ xnmalloc (ptrdiff_t nitems, ptrdiff_t item_size) /* Reallocate an array PA to make it of NITEMS items, each of size ITEM_SIZE. - Signal an error on memory exhaustion, and block interrupt input. */ + Signal an error on memory exhaustion, and profile allocations. */ void * xnrealloc (void *pa, ptrdiff_t nitems, ptrdiff_t item_size) @@ -761,7 +761,7 @@ xnrealloc (void *pa, ptrdiff_t nitems, ptrdiff_t item_size) If PA is null, then allocate a new array instead of reallocating the old one. - Block interrupt input as needed. If memory exhaustion occurs, set + Profile memory allocations. If memory exhaustion occurs, set *NITEMS to zero if PA is null, and signal an error (i.e., do not return). commit 19264b6912a111cfe426063ca431d202817f747d Author: Paul Eggert Date: Fri May 22 12:29:37 2026 -0700 adjust_glyph_matrix reallocation improvement * src/dispnew.c (adjust_glyph_matrix): Use xfree + xcalloc instead of xnrealloc + memset, as there is no need to preserve the old contents. diff --git a/src/dispnew.c b/src/dispnew.c index 7930fa59968..fb313bfd1af 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -500,16 +500,15 @@ adjust_glyph_matrix (struct window *w, struct glyph_matrix *matrix, int x, int y while (row < end) { - /* Only realloc if matrix got wider or taller (bug#77961). */ + /* Realloc if matrix got wider or taller (bug#77961). */ if (dim.width > matrix->matrix_w || new_rows) { - row->glyphs[LEFT_MARGIN_AREA] - = xnrealloc (row->glyphs[LEFT_MARGIN_AREA], - dim.width, sizeof (struct glyph)); + xfree (row->glyphs[LEFT_MARGIN_AREA]); + row->glyphs[LEFT_MARGIN_AREA] = NULL; /* We actually need to clear only the 'frame' member, but it's easier to clear everything. */ - memset (row->glyphs[LEFT_MARGIN_AREA], 0, - dim.width * sizeof (struct glyph)); + row->glyphs[LEFT_MARGIN_AREA] + = xcalloc (dim.width, sizeof (struct glyph)); } if ((row == matrix->rows + dim.height - 1 commit 64eb869b68835e15b46a217a6d955605b78044bc Author: Paul Eggert Date: Thu May 21 17:13:42 2026 -0700 Be more careful about size multiplication * src/alloc.c (xcalloc): New function. * src/dispnew.c (save_current_matrix): * src/fns.c (Finternal__hash_table_histogram): * src/nsfns.m (Fns_display_monitor_attributes_list): * src/pgtkfns.c (Fpgtk_display_monitor_attributes_list): * src/pgtkselect.c (pgtk_own_selection): * src/profiler.c (make_log): * src/sfnt.c (sfnt_poly_edges_exact): * src/xfns.c (x_get_monitor_attributes_xinerama) (x_get_monitor_attributes_xrandr, Fx_display_monitor_attributes_list): Use it instead of multiplying by hand, conceivably with overflow. * src/profiler.c (make_log): Check for overflow in internal size calculations. Use xnmalloc instead of multiply + xmalloc. * src/sfnt.c (xzalloc) [TEST]: Remove, replacing with ... (xicalloc) [TEST]: ... this new function. All callers changed. (eassert) [TEST]: New macro. * src/treesit.c (treesit_calloc_wrapper): Remove, replacing its use with xcalloc. diff --git a/src/alloc.c b/src/alloc.c index 387b196bbee..6a81fed2d69 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -656,7 +656,10 @@ xmalloc (size_t size) return val; } -/* Like the above, but zeroes out the memory just allocated. */ +/* Like the above, but zero out the memory just allocated. + Calling this can be faster than allocating and zeroing, + as the calloc implementation can avoid the zeroing overhead + when obtaining memory directly from the operating system. */ void * xzalloc (size_t size) @@ -668,6 +671,21 @@ xzalloc (size_t size) return val; } +/* Like xzalloc, but for an array of N objects each of size S. */ + +void * +xcalloc (size_t n, size_t s) +{ + void *val = calloc (n, s); + if (!val) + { + size_t size; + memory_full (ckd_mul (&size, n, s) ? SIZE_MAX : size); + } + MALLOC_PROBE (n * s); + return val; +} + /* Like realloc but check for no memory and block interrupt input. */ void * diff --git a/src/dispnew.c b/src/dispnew.c index be15a5ab694..7930fa59968 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -1963,7 +1963,7 @@ save_current_matrix (struct frame *f) int i; struct glyph_matrix *saved = xzalloc (sizeof *saved); saved->nrows = f->current_matrix->nrows; - saved->rows = xzalloc (saved->nrows * sizeof *saved->rows); + saved->rows = xcalloc (saved->nrows, sizeof *saved->rows); for (i = 0; i < saved->nrows; ++i) { diff --git a/src/fns.c b/src/fns.c index 8e7e5f980a1..98671fc1318 100644 --- a/src/fns.c +++ b/src/fns.c @@ -5996,7 +5996,7 @@ DEFUN ("internal--hash-table-histogram", { struct Lisp_Hash_Table *h = check_hash_table (hash_table); ptrdiff_t size = HASH_TABLE_SIZE (h); - ptrdiff_t *freq = xzalloc (size * sizeof *freq); + ptrdiff_t *freq = xcalloc (size, sizeof *freq); ptrdiff_t index_size = hash_table_index_size (h); for (ptrdiff_t i = 0; i < index_size; i++) { diff --git a/src/lisp.h b/src/lisp.h index bf80cfec3ad..d4978460f68 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -5536,6 +5536,8 @@ extern void *xmalloc (size_t) ATTRIBUTE_MALLOC_SIZE ((1)) ATTRIBUTE_RETURNS_NONNULL; extern void *xzalloc (size_t) ATTRIBUTE_MALLOC_SIZE ((1)) ATTRIBUTE_RETURNS_NONNULL; +extern void *xcalloc (size_t, size_t) + ATTRIBUTE_MALLOC_SIZE ((1,2)) ATTRIBUTE_RETURNS_NONNULL; extern void *xrealloc (void *, size_t) ATTRIBUTE_ALLOC_SIZE ((2)) ATTRIBUTE_RETURNS_NONNULL; extern void xfree (void *); diff --git a/src/nsfns.m b/src/nsfns.m index efe622782f7..045d167e6b4 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -2768,7 +2768,7 @@ Frames are listed from topmost (first) to bottommost (last). */) if (n_monitors == 0) return Qnil; - monitors = xzalloc (n_monitors * sizeof *monitors); + monitors = xcalloc (n_monitors, sizeof *monitors); for (i = 0; i < [screens count]; ++i) { diff --git a/src/pgtkfns.c b/src/pgtkfns.c index 4257dc45f98..e1766d2b1a6 100644 --- a/src/pgtkfns.c +++ b/src/pgtkfns.c @@ -2432,7 +2432,7 @@ Internal use only, use `display-monitor-attributes-list' instead. */) gdpy = dpyinfo->gdpy; n_monitors = gdk_display_get_n_monitors (gdpy); monitor_frames = make_nil_vector (n_monitors); - monitors = xzalloc (n_monitors * sizeof *monitors); + monitors = xcalloc (n_monitors, sizeof *monitors); FOR_EACH_FRAME (rest, frame) { diff --git a/src/pgtkselect.c b/src/pgtkselect.c index 425da3b8fd4..7e32252616b 100644 --- a/src/pgtkselect.c +++ b/src/pgtkselect.c @@ -213,7 +213,7 @@ pgtk_own_selection (Lisp_Object selection_name, Lisp_Object selection_value, if (VECTORP (targets)) { - gtargets = xzalloc (sizeof *gtargets * ASIZE (targets)); + gtargets = xcalloc (ASIZE (targets), sizeof *gtargets); ntargets = 0; for (i = 0; i < ASIZE (targets); ++i) diff --git a/src/profiler.c b/src/profiler.c index 67049f487ee..b8339a3c544 100644 --- a/src/profiler.c +++ b/src/profiler.c @@ -75,21 +75,24 @@ make_log (int size, int depth) int index_size = size * 2 + 1; log->index_size = index_size; - log->trace = xmalloc (depth * sizeof *log->trace); + log->trace = xnmalloc (depth, sizeof *log->trace); - log->index = xmalloc (index_size * sizeof *log->index); + log->index = xnmalloc (index_size, sizeof *log->index); for (int i = 0; i < index_size; i++) log->index[i] = -1; - log->next = xmalloc (size * sizeof *log->next); + log->next = xnmalloc (size, sizeof *log->next); for (int i = 0; i < size - 1; i++) log->next[i] = i + 1; log->next[size - 1] = -1; log->next_free = 0; - log->hash = xmalloc (size * sizeof *log->hash); - log->keys = xzalloc (size * depth * sizeof *log->keys); - log->counts = xzalloc (size * sizeof *log->counts); + log->hash = xnmalloc (size, sizeof *log->hash); + size_t size_x_depth; + if (ckd_mul (&size_x_depth, size, depth)) + memory_full_up (); + log->keys = xcalloc (size_x_depth, sizeof *log->keys); + log->counts = xcalloc (size, sizeof *log->counts); return log; } diff --git a/src/sfnt.c b/src/sfnt.c index ab6a2d5e7bc..e46ebc3a08b 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -70,11 +70,11 @@ xmalloc (size_t size) } MAYBE_UNUSED static void * -xzalloc (size_t size) +xcalloc (ptrdiff_t n, ptrdiff_t s) { void *ptr; - ptr = calloc (1, size); + ptr = calloc (n, s); if (!ptr) abort (); @@ -111,6 +111,8 @@ xfree (void *ptr) /* Also necessary. */ #define AVOID _Noreturn ATTRIBUTE_COLD void +#define eassert(expr) assert (expr) + #else #define TEST_STATIC #include "lisp.h" @@ -5047,7 +5049,7 @@ sfnt_poly_edges_exact (struct sfnt_fedge *edges, size_t nedges, sfnt_step_raster_proc proc, void *dcontext) { int y; - size_t size, e, edges_processed; + size_t e, edges_processed; struct sfnt_fedge *active, **prev, *a, sentinel; struct sfnt_step_raster raster; struct sfnt_step_chunk *next, *last; @@ -5065,11 +5067,7 @@ sfnt_poly_edges_exact (struct sfnt_fedge *edges, size_t nedges, raster.scanlines = height; raster.chunks = NULL; - - if (ckd_mul (&size, height, sizeof *raster.steps)) - abort (); - - raster.steps = xzalloc (size); + raster.steps = xcalloc (height, sizeof *raster.steps); for (; y != height; y += 1) { diff --git a/src/treesit.c b/src/treesit.c index 3d342be3dcc..00b97da2c5c 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -559,19 +559,13 @@ load_tree_sitter_if_necessary (bool required) #endif } -static void * -treesit_calloc_wrapper (size_t n, size_t size) -{ - return xzalloc (n * size); -} - static void treesit_initialize (void) { if (!treesit_initialized) { load_tree_sitter_if_necessary (true); - ts_set_allocator (xmalloc, treesit_calloc_wrapper, xrealloc, xfree); + ts_set_allocator (xmalloc, xcalloc, xrealloc, xfree); treesit_initialized = true; } } diff --git a/src/xfns.c b/src/xfns.c index 7ec6025ab66..ba3bf211f55 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -6254,7 +6254,7 @@ x_get_monitor_attributes_xinerama (struct x_display_info *dpyinfo) / x_display_pixel_width (dpyinfo)); mm_height_per_pixel = ((double) HeightMMOfScreen (dpyinfo->screen) / x_display_pixel_height (dpyinfo)); - monitors = xzalloc (n_monitors * sizeof *monitors); + monitors = xcalloc (n_monitors, sizeof *monitors); for (i = 0; i < n_monitors; ++i) { struct MonitorInfo *mi = &monitors[i]; @@ -6328,7 +6328,7 @@ x_get_monitor_attributes_xrandr (struct x_display_info *dpyinfo) if (!rr_monitors) goto fallback; - monitors = xzalloc (n_monitors * sizeof *monitors); + monitors = xcalloc (n_monitors, sizeof *monitors); #ifdef USE_XCB atom_name_cookies = alloca (n_monitors * sizeof *atom_name_cookies); #endif @@ -6427,7 +6427,7 @@ x_get_monitor_attributes_xrandr (struct x_display_info *dpyinfo) return Qnil; } n_monitors = resources->noutput; - monitors = xzalloc (n_monitors * sizeof *monitors); + monitors = xcalloc (n_monitors, sizeof *monitors); #if RANDR13_LIBRARY if (randr13_avail) @@ -6644,7 +6644,7 @@ Internal use only, use `display-monitor-attributes-list' instead. */) / x_display_pixel_height (dpyinfo)); #endif monitor_frames = make_nil_vector (n_monitors); - monitors = xzalloc (n_monitors * sizeof *monitors); + monitors = xcalloc (n_monitors, sizeof *monitors); FOR_EACH_FRAME (rest, frame) { diff --git a/src/xterm.c b/src/xterm.c index 941297a77ce..06fe480646e 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -5730,7 +5730,7 @@ x_cache_xi_devices (struct x_display_info *dpyinfo) return; } - dpyinfo->devices = xzalloc (sizeof *dpyinfo->devices * ndevices); + dpyinfo->devices = xcalloc (ndevices, sizeof *dpyinfo->devices); for (i = 0; i < ndevices; ++i) { @@ -13881,7 +13881,7 @@ xi_disable_devices (struct x_display_info *dpyinfo, return; ndevices = 0; - devices = xzalloc (sizeof *devices * dpyinfo->num_devices); + devices = xcalloc (dpyinfo->num_devices, sizeof *devices); /* Loop through every device currently in DPYINFO, and copy it to DEVICES if it is not in TO_DISABLE. Note that this function commit c72e6cdc464e295d821da24232a2913bbcecf3f8 Author: Paul Eggert Date: Thu May 21 09:21:18 2026 -0700 Avoid memsets in coding.c * src/coding.c (detect_coding_utf_16, Fset_coding_system_priority): Rewrite memset to initializers. diff --git a/src/coding.c b/src/coding.c index aea4c0cea5f..9aba2a7eacc 100644 --- a/src/coding.c +++ b/src/coding.c @@ -1534,11 +1534,9 @@ detect_coding_utf_16 (struct coding_system *coding, { /* We check the dispersion of Eth and Oth bytes where E is even and O is odd. If both are high, we assume binary data.*/ - unsigned char e[256], o[256]; + unsigned char e[256] = {0}, o[256] = {0}; unsigned e_num = 1, o_num = 1; - memset (e, 0, 256); - memset (o, 0, 256); e[c1] = 1; o[c2] = 1; @@ -10886,11 +10884,9 @@ usage: (set-coding-system-priority &rest coding-systems) */) (ptrdiff_t nargs, Lisp_Object *args) { ptrdiff_t i, j; - bool changed[coding_category_max]; + bool changed[coding_category_max] = {0}; enum coding_category priorities[coding_category_max]; - memset (changed, 0, sizeof changed); - for (i = j = 0; i < nargs; i++) { enum coding_category category; commit ced12fa1140879d92a66475cf2a167e699e8e1c0 Author: Paul Eggert Date: Thu May 21 09:18:15 2026 -0700 Avoid memsets in charset.c * src/charset.c (load_charset_map_from_file) (load_charset_map_from_vector, Fdefine_charset_internal): Rewrite memset to xzalloc or initializers. diff --git a/src/charset.c b/src/charset.c index b86304024fe..971dafc2b9d 100644 --- a/src/charset.c +++ b/src/charset.c @@ -496,11 +496,9 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile, set_unwind_protect_ptr (count, fclose_unwind, fp); unbind_to (specpdl_ref_add (count, 1), Qnil); - /* Use record_xmalloc, as `charset_map_entries' is - large (larger than MAX_ALLOCA). */ - head = record_xmalloc (sizeof *head); - entries = head; - memset (entries, 0, sizeof (struct charset_map_entries)); + /* charset_map_entries is large, so don't SAFE_ALLOCA. */ + entries = head = xzalloc (sizeof *head); + record_unwind_protect_ptr (xfree, entries); n_entries = 0; int ch = -1; @@ -532,9 +530,8 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile, if (n_entries == 0x10000) { - entries->next = record_xmalloc (sizeof *entries->next); - entries = entries->next; - memset (entries, 0, sizeof (struct charset_map_entries)); + entries = entries->next = xzalloc (sizeof *entries->next); + record_unwind_protect_ptr (xfree, entries); n_entries = 0; } int idx = n_entries; @@ -559,7 +556,7 @@ load_charset_map_from_vector (struct charset *charset, Lisp_Object vec, int cont int n_entries; int len = ASIZE (vec); int i; - USE_SAFE_ALLOCA; + specpdl_ref count = SPECPDL_INDEX (); if (len % 2 == 1) { @@ -567,11 +564,9 @@ load_charset_map_from_vector (struct charset *charset, Lisp_Object vec, int cont return; } - /* Use SAFE_ALLOCA instead of alloca, as `charset_map_entries' is - large (larger than MAX_ALLOCA). */ - head = SAFE_ALLOCA (sizeof *head); - entries = head; - memset (entries, 0, sizeof (struct charset_map_entries)); + /* charset_map_entries is large, so don't SAFE_ALLOCA. */ + entries = head = xzalloc (sizeof *head); + record_unwind_protect_ptr (xfree, entries); n_entries = 0; for (i = 0; i < len; i += 2) @@ -600,9 +595,8 @@ load_charset_map_from_vector (struct charset *charset, Lisp_Object vec, int cont if (n_entries > 0 && (n_entries % 0x10000) == 0) { - entries->next = SAFE_ALLOCA (sizeof *entries->next); - entries = entries->next; - memset (entries, 0, sizeof (struct charset_map_entries)); + entries = entries->next = xzalloc (sizeof *entries->next); + record_unwind_protect_ptr (xfree, entries); } idx = n_entries % 0x10000; entries->entry[idx].from = from; @@ -612,7 +606,7 @@ load_charset_map_from_vector (struct charset *charset, Lisp_Object vec, int cont } load_charset_map (charset, head, n_entries, control_flag); - SAFE_FREE (); + unbind_to (count, Qnil); } @@ -852,14 +846,12 @@ usage: (define-charset-internal ...) */) Lisp_Object val; struct Lisp_Hash_Table *hash_table = XHASH_TABLE (Vcharset_hash_table); int i, j; - struct charset charset; + struct charset charset = {0}; int id; int dimension; bool new_definition_p; int nchars; - memset (&charset, 0, sizeof (charset)); - if (nargs != charset_arg_max) Fsignal (Qwrong_number_of_arguments, Fcons (Qdefine_charset_internal, commit 42a8e12088b4ce08262ed158b56d60fa00e8c654 Author: Paul Eggert Date: Thu May 21 08:55:27 2026 -0700 Avoid memsets in atimer.c * src/atimer.c (start_atimer, turn_on_atimers): Rewrite memset+assignments to xzalloc or initializers. diff --git a/src/atimer.c b/src/atimer.c index 72e33656b4a..e59817049f0 100644 --- a/src/atimer.c +++ b/src/atimer.c @@ -126,12 +126,12 @@ start_atimer (enum atimer_type type, struct timespec timestamp, { t = free_atimers; free_atimers = t->next; + memset (t, 0, sizeof *t); } else - t = xmalloc (sizeof *t); + t = xzalloc (sizeof *t); /* Fill the atimer structure. */ - memset (t, 0, sizeof *t); t->type = type; t->fn = fn; t->client_data = client_data; @@ -470,8 +470,7 @@ turn_on_atimers (bool on) else { #ifdef HAVE_ITIMERSPEC - struct itimerspec ispec; - memset (&ispec, 0, sizeof ispec); + struct itimerspec ispec = {0}; if (alarm_timer_ok) timer_settime (alarm_timer, TIMER_ABSTIME, &ispec, 0); # ifdef HAVE_TIMERFD commit 52ccc1b8d38906d9a30ce091a153915bbd85c945 Author: Paul Eggert Date: Thu May 21 08:35:50 2026 -0700 Avoid memsets in pop.c * lib-src/pop.c (socket_connection): Rewrite memset+assignments to designated initializers. diff --git a/lib-src/pop.c b/lib-src/pop.c index 6fe487e1e21..2a4e9b4dd75 100644 --- a/lib-src/pop.c +++ b/lib-src/pop.c @@ -975,10 +975,8 @@ static int socket_connection (char *host, int flags) { struct addrinfo *res, *it; - struct addrinfo hints; int ret; struct servent *servent; - struct sockaddr_in addr; char found_port = 0; const char *service; int sock; @@ -1012,9 +1010,6 @@ socket_connection (char *host, int flags) } #endif - memset (&addr, 0, sizeof (addr)); - addr.sin_family = AF_INET; - /** "kpop" service is never used: look for 20060515 to see why **/ #ifdef KERBEROS service = (flags & POP_NO_KERBEROS) ? POP_SERVICE : KPOP_SERVICE; @@ -1022,6 +1017,8 @@ socket_connection (char *host, int flags) service = POP_SERVICE; #endif + struct sockaddr_in addr = {.sin_family = AF_INET}; + #ifdef HESIOD if (! (flags & POP_NO_HESIOD)) { @@ -1063,10 +1060,12 @@ socket_connection (char *host, int flags) } - memset (&hints, 0, sizeof (hints)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - hints.ai_family = AF_INET; + struct addrinfo hints = + { + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME, + .ai_family = AF_INET, + }; do { ret = getaddrinfo (host, service, &hints, &res); commit 17215532dc72cc2de1a1c235a0eba9cbfb05ab8c Author: Paul Eggert Date: Thu May 21 08:28:40 2026 -0700 Avoid a memset in emacsclient get_server_config * lib-src/emacsclient.c (get_server_config): Rewrite memset+assignments to compound literal. diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index 0769c94a89d..e0da2c88121 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c @@ -1034,10 +1034,12 @@ get_server_config (const char *config_file, struct sockaddr_in *server, exit (EXIT_FAILURE); } - memset (server, 0, sizeof *server); - server->sin_family = AF_INET; - server->sin_addr.s_addr = inet_addr (dotted); - server->sin_port = htons (atoi (port)); + *server = (struct sockaddr_in) + { + .sin_family = AF_INET, + .sin_addr.s_addr = inet_addr (dotted), + .sin_port = htons (atoi (port)) + }; free (dotted); if (! fread (authentication, AUTH_KEY_LENGTH, 1, config)) commit ad8af430e6c867bde8dc6acc6cd74b9573ce84d2 Author: Paul Eggert Date: Thu May 21 08:23:05 2026 -0700 Avoid a memset in alloc-colors.c * admin/alloc-colors.c (main): Simplify. diff --git a/admin/alloc-colors.c b/admin/alloc-colors.c index bcbc899cf3d..b479535724a 100644 --- a/admin/alloc-colors.c +++ b/admin/alloc-colors.c @@ -52,7 +52,7 @@ main (int argc, char **argv) int opt, ncolors = 0, i; XColor *allocated; int nallocated; - XColor color; + XColor color = {0}; Colormap cmap; while ((opt = getopt (argc, argv, "n:")) != EOF) @@ -76,7 +76,6 @@ main (int argc, char **argv) allocated = malloc (ncolors * sizeof *allocated); nallocated = 0; - memset (&color, 0, sizeof color); while (nallocated < ncolors && color.red < 65536) commit 8c69ba718e85749d704bdc8a6d310f6b802520ab Author: Paul Eggert Date: Thu May 21 08:20:15 2026 -0700 Fix emit_static_object comment (no bzero call) diff --git a/src/comp.c b/src/comp.c index c70b37bfdac..545d13b6bfd 100644 --- a/src/comp.c +++ b/src/comp.c @@ -2823,8 +2823,7 @@ emit_static_object (const char *name, Lisp_Object obj) /* If strlen returned 0 that means that the static object contains a NULL byte. In that case just move over to the next block. We can rely on the byte being zero because - of the previous call to bzero and because the dynamic - linker cleared it. */ + the dynamic linker cleared it. */ p++; i++; gcc_jit_block_add_assignment ( commit 1e0b0bed2874098fa5a0b322d6d0f69bd805ef50 Author: Paul Eggert Date: Thu May 21 08:17:05 2026 -0700 Avoid a memset in allocate_widget_instance * lwlib/lwlib.c (allocate_widget_instance): Simplify via xzalloc. diff --git a/lwlib/lwlib.c b/lwlib/lwlib.c index c7b80a83338..5fb6b5f7f49 100644 --- a/lwlib/lwlib.c +++ b/lwlib/lwlib.c @@ -205,9 +205,7 @@ mark_widget_destroyed (Widget widget, XtPointer closure, XtPointer call_data) static widget_instance * allocate_widget_instance (widget_info* info, Widget parent, Boolean pop_up_p) { - widget_instance* instance = - (widget_instance*) xmalloc (sizeof (widget_instance)); - memset (instance, 0, sizeof *instance); + widget_instance *instance = xzalloc (sizeof *instance); instance->parent = parent; instance->pop_up_p = pop_up_p; instance->info = info; commit 3461b450c5eae3ed53192aa9514e0b1ac1b1c8f2 Author: Paul Eggert Date: Wed May 20 20:52:28 2026 -0700 Don’t silently truncate file names in exec.c * exec/exec.c (format_pid): Simplify. No need for a local array. (exec_0): Shrink local buffer. If names are too long, fail instead of silently truncating them. Be cautious in case symlink is zero length (shouldn’t be possible in Android, but it’s easy to be safe). diff --git a/exec/exec.c b/exec/exec.c index 7736c0dab27..ace62dd0191 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -863,32 +863,25 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, -/* Format PID, an unsigned process identifier, in base 10. Place the - result in *IN, and return a pointer to the byte after the - result. REM should be NULL. */ +/* Format PID, a nonnegative process identifier, in base 10. + Place the result in *IN. Do not null-terminate the result. + Possibly modify the bytes in IN that are after the result. + Return a pointer to the byte after the result. */ char * -format_pid (char *in, unsigned int pid) +format_pid (char in[INT_STRLEN_BOUND (pid_t)], pid_t pid) { - unsigned int digits[32], *fill; + char *pend = in + INT_STRLEN_BOUND (pid_t); + char *p = pend; - fill = digits; - - for (; pid != 0; pid = pid / 10) - *fill++ = pid % 10; - - /* Insert 0 if the number would otherwise be empty. */ - - if (fill == digits) - *fill++ = 0; + do + *--p = '0' + pid % 10; + while ((pid /= 10) != 0); - while (fill != digits) - { - --fill; - *in++ = '0' + *fill; - } + do + *in++ = *p++; + while (p < pend); - *in = '\0'; return in; } @@ -904,10 +897,13 @@ format_pid (char *in, unsigned int pid) Finally, use REGS to add the required interpreter arguments to the caller's argv. + NAME must be a null-terminated string in a buffer of size PATH_MAX. + It might be updated to be a string no longer than PATH_MAX - 1. + Value is NULL upon failure, with errno set accordingly. */ char * -exec_0 (char *name, struct exec_tracee *tracee, +exec_0 (char name[PATH_MAX], struct exec_tracee *tracee, size_t *size, USER_REGS_STRUCT *regs) { int fd, rc, i; @@ -916,14 +912,13 @@ exec_0 (char *name, struct exec_tracee *tracee, program_header program; USER_WORD entry, program_entry, offset; USER_WORD header_offset; + ptrdiff_t nlen; USER_WORD name_len, aligned_len; struct exec_jump_command jump; /* This also encompasses !__LP64__. */ #if defined __mips__ && !defined MIPS_NABI int fpu_mode; #endif /* defined __mips__ && !defined MIPS_NABI */ - char buffer[80], buffer1[PATH_MAX + 80], *rewrite; - ssize_t link_size; size_t remaining; /* If the process is trying to run /proc/self/exe, make it run @@ -931,8 +926,13 @@ exec_0 (char *name, struct exec_tracee *tracee, if (!strcmp (name, "/proc/self/exe") && tracee->exec_file) { - strncpy (name, tracee->exec_file, PATH_MAX - 1); - name[PATH_MAX] = '\0'; + nlen = strnlen (tracee->exec_file, PATH_MAX); + if (PATH_MAX <= nlen) + { + errno = ENAMETOOLONG; + return NULL; + } + memcpy (name, tracee->exec_file, nlen + 1); } else { @@ -940,45 +940,45 @@ exec_0 (char *name, struct exec_tracee *tracee, cwd. Do not use sprintf at it is not reentrant and it mishandles results longer than INT_MAX. */ + nlen = strlen (name); + if (name[0] && name[0] != '/') { - /* Clear both buffers. */ - memset (buffer, 0, sizeof buffer); - memset (buffer1, 0, sizeof buffer1); + char buffer[sizeof "/proc//cwd" + INT_STRLEN_BOUND (pid_t)]; + char buffer1[PATH_MAX]; - /* Copy over /proc, the PID, and /cwd/. */ - rewrite = stpcpy (buffer, "/proc/"); + /* Copy over "/proc/", the PID, and "/cwd". */ + char *rewrite = stpcpy (buffer, "/proc/"); rewrite = format_pid (rewrite, tracee->pid); strcpy (rewrite, "/cwd"); /* Resolve this symbolic link. */ - link_size = readlink (buffer, buffer1, - PATH_MAX + 1); - + ssize_t link_size = readlink (buffer, buffer1, sizeof buffer1); if (link_size < 0) return NULL; - /* Check that the name is a reasonable size. */ + /* Check that the link is reasonable. */ - if (link_size > PATH_MAX) + if (link_size == 0 || buffer1[0] != '/') { - /* The name is too long. */ - errno = ENAMETOOLONG; + errno = EINVAL; return NULL; } - /* Add a directory separator if necessary. */ - - if (!link_size || buffer1[link_size - 1] != '/') - buffer1[link_size] = '/', link_size++; - - rewrite = buffer1 + link_size; - remaining = buffer1 + sizeof buffer1 - rewrite - 1; - memcpy (rewrite, name, strnlen (name, remaining)); + ptrdiff_t link_len = link_size - (buffer1[link_size - 1] == '/'); + if (PATH_MAX <= link_len + 1 + nlen) + { + errno = ENAMETOOLONG; + return NULL; + } - /* Replace name with buffer1. */ - strcpy (name, buffer1); + /* Replace name with link contents, + then '/' if needed, then name. */ + memmove (name + link_len + 1, name, nlen + 1); + memcpy (name, buffer1, link_len); + name[link_len] = '/'; + nlen += link_len + 1; } } @@ -1151,7 +1151,7 @@ exec_0 (char *name, struct exec_tracee *tracee, loader_area_used += sizeof jump; /* Copy the length of NAME and NAME itself to the loader area. */ - name_len = strlen (name); + name_len = nlen; aligned_len = ((name_len + 1 + sizeof name_len - 1) & -sizeof name_len); if (sizeof loader_area - loader_area_used diff --git a/exec/trace.c b/exec/trace.c index da9ac96c6ff..d3d6f223eb8 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -732,7 +732,7 @@ check_signal (struct exec_tracee *tracee, int status) static int handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) { - char buffer[PATH_MAX + 80], *area; + char buffer[PATH_MAX], *area; USER_REGS_STRUCT original; size_t size, loader_size; USER_WORD loader; commit 25a07c30e5e19f13c32c7ab561391aefa38bc33d Author: Paul Eggert Date: Wed May 20 19:22:37 2026 -0700 Don’t use VLA in etags.c mercury_decl * lib-src/etags.c (mercury_decl): Don’t use a VLA, as C11+ says VLAs are optional. Instead, redo to omit the need for an array at all. diff --git a/lib-src/etags.c b/lib-src/etags.c index f218dba2902..4114ba6b655 100644 --- a/lib-src/etags.c +++ b/lib-src/etags.c @@ -6654,6 +6654,14 @@ static const char *Mercury_decl_tags[] = {"type", "solver type", "pred", "initialise", "finalise", "mutable", "module", "interface", "implementation", "import_module", "use_module", "include_module", "end_module", "some", "all"}; +/* Return true if array of char BUF, of length LEN, equals STR. */ + +static bool +memstreq (char const *buf, ptrdiff_t len, char const *str) +{ + return strlen (str) == len && memeq (buf, str, len); +} + static mercury_pos_t mercury_decl (char *s, size_t pos) { @@ -6661,27 +6669,24 @@ mercury_decl (char *s, size_t pos) if (s == NULL) return null_pos; - size_t origpos; - origpos = pos; + size_t origpos = pos; + char *decl_type = s + origpos; while (c_isalnum (s[pos]) || s[pos] == '_') pos++; - unsigned char decl_type_length = pos - origpos; - char buf[decl_type_length + 1]; - memset (buf, 0, decl_type_length + 1); + ptrdiff_t decl_type_length = pos - origpos; /* Mercury declaration tags. Consume them, then check the declaration item following :- is legitimate, then go on as in the prolog case. */ - memcpy (buf, &s[origpos], decl_type_length); - bool found_decl_tag = false; if (is_mercury_quantifier) { - if (strcmp (buf, "pred") != 0 && strcmp (buf, "func") != 0) /* Bad syntax. */ - return null_pos; + if (! (memstreq (decl_type, decl_type_length, "pred") + || memstreq (decl_type, decl_type_length, "func"))) + return null_pos; /* Bad syntax. */ is_mercury_quantifier = false; /* Reset to base value. */ found_decl_tag = true; @@ -6690,14 +6695,14 @@ mercury_decl (char *s, size_t pos) { for (int j = 0; j < sizeof (Mercury_decl_tags) / sizeof (char*); ++j) { - if (strcmp (buf, Mercury_decl_tags[j]) == 0) + if (memstreq (decl_type, decl_type_length, Mercury_decl_tags[j])) { found_decl_tag = true; - if (strcmp (buf, "type") == 0) + if (memstreq (decl_type, decl_type_length, "type")) is_mercury_type = true; - if (strcmp (buf, "some") == 0 - || strcmp (buf, "all") == 0) + if (memstreq (decl_type, decl_type_length, "some") + || memstreq (decl_type, decl_type_length, "all")) { is_mercury_quantifier = true; } @@ -6707,18 +6712,15 @@ mercury_decl (char *s, size_t pos) else /* 'solver type' has a blank in the middle, so this is the hard case. */ - if (strcmp (buf, "solver") == 0) + if (memstreq (decl_type, decl_type_length, "solver")) { do pos++; while (c_isalnum (s[pos]) || s[pos] == '_'); decl_type_length = pos - origpos; - char buf2[decl_type_length + 1]; - memset (buf2, 0, decl_type_length + 1); - memcpy (buf2, &s[origpos], decl_type_length); - if (strcmp (buf2, "solver type") == 0) + if (memstreq (decl_type, decl_type_length, "solver type")) { found_decl_tag = false; break; /* Found declaration tag of rank j. */ commit b1d338d89ae1e6484306e0ba7c32bd4163b3b54d Author: Paul Eggert Date: Wed May 20 09:24:19 2026 -0700 Fix misleading x_dnd_begin_drag_and_drop API * src/xterm.c (x_dnd_begin_drag_and_drop): The n_ask_actions arg is an int, not a size_t, as XChangeProperty supports only int and our caller passes an int. diff --git a/src/xterm.c b/src/xterm.c index 6580bda9fef..941297a77ce 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -12696,7 +12696,7 @@ x_dnd_process_quit (struct frame *f, Time timestamp) Lisp_Object x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, Lisp_Object return_frame, Atom *ask_action_list, - const char **ask_action_names, size_t n_ask_actions, + const char **ask_action_names, int n_ask_actions, bool allow_current_frame, Atom *target_atoms, int ntargets, Lisp_Object selection_target_list, bool follow_tooltip) @@ -12710,7 +12710,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, char *atom_name, *ask_actions; Lisp_Object action, ltimestamp, val; specpdl_ref ref, count, base; - ptrdiff_t i, end, fill; + ptrdiff_t end, fill; XTextProperty prop; Lisp_Object frame_object, x, y, frame, local_value; bool signals_were_pending, need_sync; @@ -12802,7 +12802,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, end = 0; count = SPECPDL_INDEX (); - for (i = 0; i < n_ask_actions; ++i) + for (int i = 0; i < n_ask_actions; i++) { fill = end; end += strlen (ask_action_names[i]) + 1; diff --git a/src/xterm.h b/src/xterm.h index 28b720c9222..a81bf589b13 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1858,7 +1858,7 @@ extern void x_handle_pending_selection_requests (void); extern bool x_detect_pending_selection_requests (void); extern Lisp_Object x_dnd_begin_drag_and_drop (struct frame *, Time, Atom, Lisp_Object, Atom *, const char **, - size_t, bool, Atom *, int, + int, bool, Atom *, int, Lisp_Object, bool); extern int x_display_pixel_height (struct x_display_info *); extern int x_display_pixel_width (struct x_display_info *); commit 4d85084509a13fdc15ca38e6885a6a8e30e77425 Author: Paul Eggert Date: Wed May 20 09:09:21 2026 -0700 Fix unlikely json.c size overflow calculations * src/json.c (json_out_grow_buf) (json_make_object_workspace_for_slow_path) (json_byte_workspace_put_slow_path): Use xpalloc rather than doing it by hand. * src/json.c (json_out_grow_buf): Change arg from needed bytes to minimum increment of bytes. Caller changed. diff --git a/src/json.c b/src/json.c index 9b07f8ced26..720654bac43 100644 --- a/src/json.c +++ b/src/json.c @@ -244,13 +244,10 @@ symset_add (json_out_t *jo, symset_t *ss, Lisp_Object sym) } static NO_INLINE void -json_out_grow_buf (json_out_t *jo, ptrdiff_t bytes) +json_out_grow_buf (json_out_t *jo, ptrdiff_t incr_min) { - ptrdiff_t need = jo->size + bytes; - ptrdiff_t new_size = max (jo->capacity, 512); - while (new_size < need) - new_size <<= 1; - jo->buf = xrealloc (jo->buf, new_size); + ptrdiff_t new_size = jo->capacity; + jo->buf = xpalloc (jo->buf, &new_size, incr_min, -1, 1); jo->capacity = new_size; } @@ -267,8 +264,9 @@ cleanup_json_out (void *arg) static void json_make_room (json_out_t *jo, ptrdiff_t bytes) { - if (bytes > jo->capacity - jo->size) - json_out_grow_buf (jo, bytes); + ptrdiff_t avail = jo->capacity - jo->size; + if (avail < bytes) + json_out_grow_buf (jo, bytes - avail); } #define JSON_OUT_STR(jo, str) (json_out_str (jo, str, sizeof (str) - 1)) @@ -803,36 +801,20 @@ json_parser_done (void *parser) Lisp_Objects */ NO_INLINE static void json_make_object_workspace_for_slow_path (struct json_parser *parser, - size_t size) -{ - size_t needed_workspace_size - = (parser->object_workspace_current + size); - size_t new_workspace_size = parser->object_workspace_size; - while (new_workspace_size < needed_workspace_size) - { - if (ckd_mul (&new_workspace_size, new_workspace_size, 2)) - { - json_signal_error (parser, Qjson_out_of_memory); - } - } - - Lisp_Object *new_workspace_ptr; - if (parser->object_workspace_size - == JSON_PARSER_INTERNAL_OBJECT_WORKSPACE_SIZE) - { - new_workspace_ptr - = xnmalloc (new_workspace_size, sizeof (Lisp_Object)); - memcpy (new_workspace_ptr, parser->object_workspace, - (sizeof (Lisp_Object) - * parser->object_workspace_current)); - } - else - { - new_workspace_ptr - = xnrealloc (parser->object_workspace, new_workspace_size, - sizeof (Lisp_Object)); - } - + ptrdiff_t size) +{ + bool internal = (parser->object_workspace_size + == JSON_PARSER_INTERNAL_OBJECT_WORKSPACE_SIZE); + ptrdiff_t new_workspace_size = parser->object_workspace_size; + Lisp_Object *new_workspace_ptr + = xpalloc (internal ? NULL : parser->object_workspace, + &new_workspace_size, + size - (parser->object_workspace_size + - parser->object_workspace_current), + -1, sizeof (Lisp_Object)); + if (internal) + memcpy (new_workspace_ptr, parser->object_workspace, + sizeof (Lisp_Object) * parser->object_workspace_current); parser->object_workspace = new_workspace_ptr; parser->object_workspace_size = new_workspace_size; } @@ -854,33 +836,23 @@ json_byte_workspace_reset (struct json_parser *parser) parser->byte_workspace_current = parser->byte_workspace; } -/* Puts 'value' into the byte_workspace. If there is no space - available, it allocates space */ +/* Put VALUE into the byte_workspace, allocating space. */ NO_INLINE static void json_byte_workspace_put_slow_path (struct json_parser *parser, unsigned char value) { - size_t new_workspace_size + ptrdiff_t new_workspace_size = parser->byte_workspace_end - parser->byte_workspace; - if (ckd_mul (&new_workspace_size, new_workspace_size, 2)) - { - json_signal_error (parser, Qjson_out_of_memory); - } - - size_t offset + ptrdiff_t offset = parser->byte_workspace_current - parser->byte_workspace; - if (parser->byte_workspace == parser->internal_byte_workspace) - { - parser->byte_workspace = xmalloc (new_workspace_size); - memcpy (parser->byte_workspace, parser->internal_byte_workspace, - offset); - } - else - { - parser->byte_workspace - = xrealloc (parser->byte_workspace, new_workspace_size); - } + bool internal = parser->byte_workspace == parser->internal_byte_workspace; + unsigned char *new + = xpalloc (internal ? NULL : parser->byte_workspace, + &new_workspace_size, 1, -1, 1); + if (internal) + memcpy (new, parser->byte_workspace, offset); + parser->byte_workspace = new; parser->byte_workspace_end = parser->byte_workspace + new_workspace_size; parser->byte_workspace_current = parser->byte_workspace + offset; commit 5fd1e0bbef85119527129da8bc645c102434ab28 Author: Paul Eggert Date: Wed May 20 08:39:44 2026 -0700 Coalesce load_seccomp comparisons * src/emacs.c (load_seccomp): One comparison, not two. diff --git a/src/emacs.c b/src/emacs.c index 465a7a0b108..a9b970effb4 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1228,8 +1228,7 @@ load_seccomp (const char *file) goto out; } struct sock_fprog program; - if (stat.st_size <= 0 || SIZE_MAX <= stat.st_size - || PTRDIFF_MAX <= stat.st_size + if (stat.st_size <= 0 || min (PTRDIFF_MAX, SIZE_MAX) <= stat.st_size || stat.st_size % sizeof *program.filter != 0) { fprintf (stderr, "seccomp filter %s has invalid size %ld\n", commit 9851c5ea3410589dadfc43e809fd65ee66b58d28 Author: Paul Eggert Date: Wed May 20 08:32:41 2026 -0700 Shrink STRING_BYTES_MAX slightly * src/alloc.c (STRING_BYTES_MAX): Also don’t allow sizes to exceed PTRDIFF_MAX in internal calculations when calling malloc, as those are problematic even if the final number of bytes does not exceed PTRDIFF_MAX. diff --git a/src/alloc.c b/src/alloc.c index a73e7df1dc7..387b196bbee 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -1501,7 +1501,7 @@ sdata_size (ptrdiff_t n) calculating a value to be passed to malloc. */ static ptrdiff_t const STRING_BYTES_MAX = min (STRING_BYTES_BOUND, - ((SIZE_MAX + ((min (PTRDIFF_MAX, SIZE_MAX) - GC_STRING_EXTRA - offsetof (struct sblock, data) - SDATA_DATA_OFFSET) commit 59b2f8f1dc4513df32e81a7a0d38d5b09c0d6049 Author: Paul Eggert Date: Tue May 19 22:17:40 2026 -0700 Plug default_PATH memory leak * src/emacs.c (default_PATH): Fix very-unlikely memory leak. diff --git a/src/emacs.c b/src/emacs.c index 11fe567737a..465a7a0b108 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -753,21 +753,24 @@ default_PATH (void) { #ifdef _CS_PATH char *buf = staticbuf; - size_t bufsize = sizeof staticbuf, s; + size_t bufsize = sizeof staticbuf; - /* If necessary call confstr a second time with a bigger buffer. */ - while (bufsize < (s = confstr (_CS_PATH, buf, bufsize))) + /* If necessary call confstr again with a bigger buffer. */ + for (size_t s; + ! (s = confstr (_CS_PATH, buf, bufsize)) || bufsize < s; ) { + if (buf != staticbuf) + xfree (buf); + if (!s) + { + staticbuf[0] = 1; + buf = NULL; + break; + } buf = xmalloc (s); bufsize = s; } - if (s == 0) - { - staticbuf[0] = 1; - buf = NULL; - } - path = buf; #elif defined DOS_NT commit 1eb2e052bb55184d62c1dec265f6d327be4e9113 Author: Paul Eggert Date: Tue May 19 18:39:09 2026 -0700 New function memory_full_up * src/alloc.c (memory_full_up): New function. Replace all callers of memory_full (SIZE_MAX) with callers to this function. This simplifies callers and should make future changes easier. It also saves a whopping 296 bytes in executable size with gcc 16.1.1 20260515 (Red Hat 16.1.1-2) x86-64. diff --git a/src/alloc.c b/src/alloc.c index 5da38cadb5d..a73e7df1dc7 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -711,7 +711,7 @@ xnmalloc (ptrdiff_t nitems, ptrdiff_t item_size) eassert (0 <= nitems && 0 < item_size); ptrdiff_t nbytes; if (ckd_mul (&nbytes, nitems, item_size) || SIZE_MAX < nbytes) - memory_full (SIZE_MAX); + memory_full_up (); return xmalloc (nbytes); } @@ -725,7 +725,7 @@ xnrealloc (void *pa, ptrdiff_t nitems, ptrdiff_t item_size) eassert (0 <= nitems && 0 < item_size); ptrdiff_t nbytes; if (ckd_mul (&nbytes, nitems, item_size) || SIZE_MAX < nbytes) - memory_full (SIZE_MAX); + memory_full_up (); return xrealloc (pa, nbytes); } @@ -792,7 +792,7 @@ xpalloc (void *pa, ptrdiff_t *nitems, ptrdiff_t nitems_incr_min, && (ckd_add (&n, n0, nitems_incr_min) || (0 <= nitems_max && nitems_max < n) || ckd_mul (&nbytes, n, item_size))) - memory_full (SIZE_MAX); + memory_full_up (); pa = xrealloc (pa, nbytes); *nitems = n; return pa; @@ -1111,7 +1111,7 @@ lisp_align_malloc (size_t nbytes, enum mem_type type) { lisp_malloc_loser = base; free (base); - memory_full (SIZE_MAX); + memory_full_up (); } } #endif @@ -2181,7 +2181,7 @@ LENGTH must be a number. INIT matters only in whether it is t or nil. */) CHECK_FIXNAT (length); EMACS_INT len = XFIXNAT (length); if (BOOL_VECTOR_LENGTH_MAX < len) - memory_full (SIZE_MAX); + memory_full_up (); Lisp_Object val = make_clear_bool_vector (len, NILP (init)); return NILP (init) ? val : bool_vector_fill (val, init); } @@ -2193,7 +2193,7 @@ usage: (bool-vector &rest OBJECTS) */) (ptrdiff_t nargs, Lisp_Object *args) { if (BOOL_VECTOR_LENGTH_MAX < nargs) - memory_full (SIZE_MAX); + memory_full_up (); Lisp_Object vector = make_clear_bool_vector (nargs, true); for (ptrdiff_t i = 0; i < nargs; i++) if (!NILP (args[i])) @@ -3397,7 +3397,7 @@ allocate_clear_vector (ptrdiff_t len, bool clearit) if (len == 0) return XVECTOR (zero_vector); if (VECTOR_ELTS_MAX < len) - memory_full (SIZE_MAX); + memory_full_up (); struct Lisp_Vector *v = allocate_vectorlike (len, clearit); v->header.size = len; return v; @@ -4142,6 +4142,16 @@ memory_full (size_t nbytes) xsignal (Qnil, Vmemory_signal_data); } +/* Report memory exhaustion because size calculations overflowed, + or perhaps malloc was invoked successfully but the + resulting pointer had problems fitting into a tagged EMACS_INT. */ + +void +memory_full_up (void) +{ + memory_full (SIZE_MAX); +} + /* If we released our reserve (due to running out of memory), and we have a fair amount free once again, try to set aside another reserve in case we run out once more. diff --git a/src/android.c b/src/android.c index c1b2b9c98ac..cd950d3c51c 100644 --- a/src/android.c +++ b/src/android.c @@ -4948,7 +4948,7 @@ android_get_image (android_drawable handle, (size_t) bitmap_info.height)) { ANDROID_DELETE_LOCAL_REF (bitmap); - memory_full (0); + memory_full_up (); } } else diff --git a/src/androidselect.c b/src/androidselect.c index 8bdbc0fcd36..ce02c4f42ba 100644 --- a/src/androidselect.c +++ b/src/androidselect.c @@ -618,7 +618,7 @@ does not have any corresponding data. In that case, use if (ckd_add (&length, length, rc) || PTRDIFF_MAX - length < BUFSIZ) - memory_full (PTRDIFF_MAX); + memory_full_up (); if (rc < 0) return unbind_to (ref, Qnil); diff --git a/src/androidterm.c b/src/androidterm.c index a74b595d499..18d6d2eb56a 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -6831,7 +6831,7 @@ android_term_init (void) static char const at[] = " at "; ptrdiff_t nbytes = sizeof (title) + sizeof (at); if (ckd_add (&nbytes, nbytes, SBYTES (system_name))) - memory_full (SIZE_MAX); + memory_full_up (); dpyinfo->x_id_name = xmalloc (nbytes); sprintf (dpyinfo->x_id_name, "%s%s%s", title, at, SDATA (system_name)); diff --git a/src/buffer.c b/src/buffer.c index ec26ff82c78..8963ec4e197 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3413,7 +3413,7 @@ record_overlay_string (struct sortstrlist *ssl, Lisp_Object str, nbytes = SBYTES (str); if (ckd_add (&nbytes, nbytes, ssl->bytes)) - memory_full (SIZE_MAX); + memory_full_up (); ssl->bytes = nbytes; if (STRINGP (str2)) @@ -3427,7 +3427,7 @@ record_overlay_string (struct sortstrlist *ssl, Lisp_Object str, nbytes = SBYTES (str2); if (ckd_add (&nbytes, nbytes, ssl->bytes)) - memory_full (SIZE_MAX); + memory_full_up (); ssl->bytes = nbytes; } } @@ -3499,7 +3499,7 @@ overlay_strings (ptrdiff_t pos, struct window *w, unsigned char **pstr) ptrdiff_t total; if (ckd_add (&total, overlay_heads.bytes, overlay_tails.bytes)) - memory_full (SIZE_MAX); + memory_full_up (); if (total > overlay_str_len) overlay_str_buf = xpalloc (overlay_str_buf, &overlay_str_len, total - overlay_str_len, -1, 1); diff --git a/src/callint.c b/src/callint.c index 398bfde468b..1746dd57704 100644 --- a/src/callint.c +++ b/src/callint.c @@ -431,7 +431,7 @@ invoke it (via an `interactive' spec that contains, for instance, an if (MOST_POSITIVE_FIXNUM < min (PTRDIFF_MAX, SIZE_MAX) / word_size && MOST_POSITIVE_FIXNUM < nargs) - memory_full (SIZE_MAX); + memory_full_up (); /* ARGS will contain the array of arguments to pass to the function. VISARGS will contain the same list but in a nicer form, so that if we diff --git a/src/ccl.c b/src/ccl.c index c581d6ecd95..2fe26be5759 100644 --- a/src/ccl.c +++ b/src/ccl.c @@ -2166,7 +2166,7 @@ usage: (ccl-execute-on-string CCL-PROGRAM STATUS STRING &optional CONTINUE UNIBY outbufsize = str_bytes; if (ckd_mul (&outbufsize, outbufsize, buf_magnification) || ckd_add (&outbufsize, outbufsize, 256)) - memory_full (SIZE_MAX); + memory_full_up (); outp = outbuf = xmalloc (outbufsize); consumed_chars = consumed_bytes = 0; diff --git a/src/coding.c b/src/coding.c index dd767c80ab4..aea4c0cea5f 100644 --- a/src/coding.c +++ b/src/coding.c @@ -7045,7 +7045,7 @@ produce_chars (struct coding_system *coding, Lisp_Object translation_table, ptrdiff_t dst_size; if (ckd_mul (&dst_size, to_nchars, MAX_MULTIBYTE_LENGTH) || ckd_add (&dst_size, dst_size, buf_end - buf)) - memory_full (SIZE_MAX); + memory_full_up (); dst = alloc_destination (coding, dst_size, dst); if (EQ (coding->src_object, coding->dst_object) /* Input and output are not C buffers, which are safe to diff --git a/src/composite.c b/src/composite.c index e36e1670d8d..2898ea9651e 100644 --- a/src/composite.c +++ b/src/composite.c @@ -315,7 +315,7 @@ get_composition_id (ptrdiff_t charpos, ptrdiff_t bytepos, ptrdiff_t nchars, : ASIZE (key)); if (GLYPH_LEN_MAX < glyph_len) - memory_full (SIZE_MAX); + memory_full_up (); /* Register the composition in composition_table. */ cmp = xmalloc (sizeof *cmp); diff --git a/src/dispnew.c b/src/dispnew.c index 284a0eb175f..be15a5ab694 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -1403,7 +1403,7 @@ realloc_glyph_pool (struct glyph_pool *pool, struct dim matrix_dim) /* Enlarge the glyph pool. */ if (ckd_mul (&needed, matrix_dim.height, matrix_dim.width)) - memory_full (SIZE_MAX); + memory_full_up (); if (needed > pool->nglyphs) { ptrdiff_t old_nglyphs = pool->nglyphs; @@ -5412,7 +5412,7 @@ scrolling_window (struct window *w, int tab_line_p) - next_almost_prime_increment_max); ptrdiff_t current_nrows_max = row_table_max - desired_matrix->nrows; if (current_nrows_max < current_matrix->nrows) - memory_full (SIZE_MAX); + memory_full_up (); } /* Reallocate vectors, tables etc. if necessary. */ diff --git a/src/editfns.c b/src/editfns.c index cad41a36f7f..341e241dfcb 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -2080,7 +2080,7 @@ a buffer or a string. But this is deprecated. */) ptrdiff_t bytes_needed; if (ckd_mul (&bytes_needed, diags, 2 * sizeof *buffer) || ckd_add (&bytes_needed, bytes_needed, del_bytes + ins_bytes)) - memory_full (SIZE_MAX); + memory_full_up (); USE_SAFE_ALLOCA; buffer = SAFE_ALLOCA (bytes_needed); unsigned char *deletions_insertions = memset (buffer + 2 * diags, 0, @@ -3510,7 +3510,7 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool message) v |= ckd_add (&alloca_size, info_size, format_and_discarded_size); v |= SIZE_MAX < alloca_size; if (v) - memory_full (SIZE_MAX); + memory_full_up (); /* The info table. */ info = SAFE_ALLOCA (alloca_size); /* A copy of the format string's bytes, needed because the original diff --git a/src/eval.c b/src/eval.c index b61bda4a024..1fdc6653cfc 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1601,7 +1601,7 @@ internal_lisp_condition_case (Lisp_Object var, Lisp_Object bodyform, SAFE_ALLOCA won't work here due to the setjmp, so impose a MAX_ALLOCA limit. */ if (MAX_ALLOCA / word_size < clausenb) - memory_full (SIZE_MAX); + memory_full_up (); Lisp_Object volatile *clauses = alloca (clausenb * sizeof *clauses); clauses += clausenb; *--clauses = make_fixnum (0); diff --git a/src/fns.c b/src/fns.c index a2312ffa1b9..8e7e5f980a1 100644 --- a/src/fns.c +++ b/src/fns.c @@ -902,7 +902,7 @@ concat_to_string (ptrdiff_t nargs, Lisp_Object *args) result_len += len; if (MOST_POSITIVE_FIXNUM < result_len) - memory_full (SIZE_MAX); + memory_full_up (); } if (dest_multibyte && some_unibyte) @@ -1122,7 +1122,7 @@ concat_to_vector (ptrdiff_t nargs, Lisp_Object *args) EMACS_INT len = XFIXNAT (Flength (arg)); result_len += len; if (MOST_POSITIVE_FIXNUM < result_len) - memory_full (SIZE_MAX); + memory_full_up (); } /* Create the output vector. */ @@ -4672,7 +4672,7 @@ larger_vector (Lisp_Object vec, ptrdiff_t incr_min, ptrdiff_t nitems_max) incr_max = n_max - old_size; incr = max (incr_min, min (old_size >> 1, incr_max)); if (incr_max < incr) - memory_full (SIZE_MAX); + memory_full_up (); new_size = old_size + incr; v = allocate_vector (new_size); memcpy (v->contents, XVECTOR (vec)->contents, old_size * sizeof *v->contents); diff --git a/src/gnutls.c b/src/gnutls.c index 4a4567a5174..3ba9b1b290f 100644 --- a/src/gnutls.c +++ b/src/gnutls.c @@ -2424,7 +2424,7 @@ gnutls_symmetric_aead (bool encrypting, gnutls_cipher_algorithm_t gca, ptrdiff_t tagged_size; if (ckd_add (&tagged_size, isize, cipher_tag_size) || SIZE_MAX < tagged_size) - memory_full (SIZE_MAX); + memory_full_up (); size_t storage_length = tagged_size; USE_SAFE_ALLOCA; char *storage = SAFE_ALLOCA (storage_length); diff --git a/src/gtkutil.c b/src/gtkutil.c index 4fc6b3e0108..df41fb91110 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -698,7 +698,7 @@ get_utf8_string (const char *str) if (ckd_mul (&alloc, nr_bad, 4) || ckd_add (&alloc, alloc, len + 1) || SIZE_MAX < alloc) - memory_full (SIZE_MAX); + memory_full_up (); up = utf8_str = xmalloc (alloc); p = (unsigned char *)str; @@ -4382,7 +4382,7 @@ xg_store_widget_in_map (GtkWidget *w) { ptrdiff_t new_size; if (TYPE_MAXIMUM (Window) - ID_TO_WIDGET_INCR < id_to_widget.max_size) - memory_full (SIZE_MAX); + memory_full_up (); new_size = id_to_widget.max_size + ID_TO_WIDGET_INCR; id_to_widget.widgets = xnrealloc (id_to_widget.widgets, diff --git a/src/haikufns.c b/src/haikufns.c index e24dfd2193e..3568c1bc0bc 100644 --- a/src/haikufns.c +++ b/src/haikufns.c @@ -2025,7 +2025,7 @@ haiku_create_colored_cursor (struct user_cursor_bitmap_info *info, bitmap = BBitmap_new (width, height, false); if (!bitmap) - memory_full (SIZE_MAX); + memory_full_up (); for (y = 0; y < height; ++y) { diff --git a/src/haikufont.c b/src/haikufont.c index cc9cb47b395..09edffc08a9 100644 --- a/src/haikufont.c +++ b/src/haikufont.c @@ -1033,7 +1033,7 @@ haikufont_shape (Lisp_Object lgstring, Lisp_Object direction) len = i; if (INT_MAX / 2 < len) - memory_full (SIZE_MAX); + memory_full_up (); block_input (); diff --git a/src/haikuselect.c b/src/haikuselect.c index 93449357806..f8dac897d97 100644 --- a/src/haikuselect.c +++ b/src/haikuselect.c @@ -249,7 +249,7 @@ haiku_message_to_lisp (void *message) case 'MSGG': msg = be_get_message_message (message, name, j); if (!msg) - memory_full (SIZE_MAX); + memory_full_up (); t1 = haiku_message_to_lisp (msg); BMessage_delete (msg); @@ -270,7 +270,7 @@ haiku_message_to_lisp (void *message) } if (!pbuf) - memory_full (SIZE_MAX); + memory_full_up (); t1 = DECODE_FILE (build_string (pbuf)); @@ -837,7 +837,7 @@ haiku_report_system_error (status_t code, const char *format) break; case B_NO_MEMORY: - memory_full (SIZE_MAX); + memory_full_up (); break; default: diff --git a/src/haikuterm.c b/src/haikuterm.c index b2b864188af..065a2d262b2 100644 --- a/src/haikuterm.c +++ b/src/haikuterm.c @@ -2909,7 +2909,7 @@ haiku_define_fringe_bitmap (int which, unsigned short *bits, block_input (); fringe_bmps[which] = BBitmap_new (wd, h, 1); if (!fringe_bmps[which]) - memory_full (SIZE_MAX); + memory_full_up (); BBitmap_import_fringe_bitmap (fringe_bmps[which], bits, wd, h); unblock_input (); } @@ -4526,7 +4526,7 @@ haiku_term_init (void) nbytes = sizeof "GNU Emacs" + sizeof " at "; if (ckd_add (&nbytes, nbytes, SBYTES (system_name))) - memory_full (SIZE_MAX); + memory_full_up (); name_buffer = alloca (nbytes); sprintf (name_buffer, "%s%s%s", "GNU Emacs", diff --git a/src/image.c b/src/image.c index 9d0a620188f..38f9d1416a7 100644 --- a/src/image.c +++ b/src/image.c @@ -6950,7 +6950,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p) if (ckd_mul (&nbytes, sizeof *colors, img->width) || ckd_mul (&nbytes, nbytes, img->height) || SIZE_MAX < nbytes) - memory_full (SIZE_MAX); + memory_full_up (); colors = xmalloc (nbytes); /* Get the X image or create a memory device context for IMG. */ @@ -7100,7 +7100,7 @@ image_detect_edges (struct frame *f, struct image *img, if (ckd_mul (&nbytes, sizeof *new, img->width) || ckd_mul (&nbytes, nbytes, img->height)) - memory_full (SIZE_MAX); + memory_full_up (); new = xmalloc (nbytes); for (y = 0; y < img->height; ++y) @@ -8486,7 +8486,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) /* Allocate memory for the image. */ if (ckd_mul (&nbytes, row_bytes, sizeof *pixels) || ckd_mul (&nbytes, nbytes, height)) - memory_full (SIZE_MAX); + memory_full_up (); c->pixels = pixels = xmalloc (nbytes); c->rows = rows = xmalloc (height * sizeof *rows); for (i = 0; i < height; ++i) diff --git a/src/json.c b/src/json.c index ccbbae615d0..9b07f8ced26 100644 --- a/src/json.c +++ b/src/json.c @@ -142,7 +142,7 @@ make_symset_table (int bits, struct symset_tbl *up) { int maxbits = min (SIZE_WIDTH - 2 - (word_size < 8 ? 2 : 3), 32); if (bits > maxbits) - memory_full (PTRDIFF_MAX); /* Will never happen in practice. */ + memory_full_up (); /* Will never happen in practice. */ struct symset_tbl *st = xmalloc (sizeof *st + (sizeof *st->entries << bits)); st->up = up; ptrdiff_t size = symset_size (bits); diff --git a/src/keymap.c b/src/keymap.c index 9c2aa7634fc..dc40d68089a 100644 --- a/src/keymap.c +++ b/src/keymap.c @@ -2109,7 +2109,7 @@ For an approximate inverse of this, see `kbd'. */) /* This has one extra element at the end that we don't pass to Fconcat. */ ptrdiff_t size4; if (ckd_mul (&size4, nkeys + nprefix, 4)) - memory_full (SIZE_MAX); + memory_full_up (); SAFE_ALLOCA_LISP (args, size4); /* In effect, this computes diff --git a/src/lisp.h b/src/lisp.h index a5082146da7..bf80cfec3ad 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4430,6 +4430,7 @@ extern void parse_str_as_multibyte (const unsigned char *, ptrdiff_t, extern intptr_t garbage_collection_inhibited; extern void malloc_warning (const char *); extern AVOID memory_full (size_t); +extern AVOID memory_full_up (void); extern AVOID buffer_memory_full (ptrdiff_t); extern bool survives_gc_p (Lisp_Object); extern void mark_object (Lisp_Object); @@ -5704,7 +5705,7 @@ safe_free_unbind_to (specpdl_ref count, specpdl_ref sa_count, Lisp_Object val) ptrdiff_t alloca_nbytes; \ if (ckd_mul (&alloca_nbytes, nelt, word_size) \ || SIZE_MAX < alloca_nbytes) \ - memory_full (SIZE_MAX); \ + memory_full_up (); \ else if (alloca_nbytes <= sa_avail) \ (buf) = AVAIL_ALLOCA (alloca_nbytes); \ else \ diff --git a/src/macfont.m b/src/macfont.m index 84e3e0e8e21..1916e5c0287 100644 --- a/src/macfont.m +++ b/src/macfont.m @@ -3119,7 +3119,7 @@ So we use CTFontDescriptorCreateMatchingFontDescriptor (no len = i; if (INT_MAX / 2 < len) - memory_full (SIZE_MAX); + memory_full_up (); unichars = alloca (sizeof (UniChar) * (len + nonbmp_len)); nonbmp_indices = alloca (sizeof (CFIndex) * (nonbmp_len + 1)); diff --git a/src/nsfont.m b/src/nsfont.m index 655a4c3b569..71ebf9d8b3d 100644 --- a/src/nsfont.m +++ b/src/nsfont.m @@ -1484,7 +1484,7 @@ is false when (FROM > 0 || TO < S->nchars). */ len = i; if (INT_MAX / 2 < len) - memory_full (SIZE_MAX); + memory_full_up (); block_input (); diff --git a/src/pgtkterm.c b/src/pgtkterm.c index 757ff57a9f2..6362f795eff 100644 --- a/src/pgtkterm.c +++ b/src/pgtkterm.c @@ -7150,7 +7150,7 @@ pgtk_term_init (Lisp_Object display_name, char *resource_name) Lisp_Object system_name = Fsystem_name (); ptrdiff_t nbytes; if (ckd_add (&nbytes, SBYTES (Vinvocation_name), SBYTES (system_name) + 2)) - memory_full (SIZE_MAX); + memory_full_up (); dpyinfo->x_id = ++x_display_id; dpyinfo->x_id_name = xmalloc (nbytes); char *nametail = lispstpcpy (dpyinfo->x_id_name, Vinvocation_name); diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index 3a5daa67ff8..30cf1876191 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -79,7 +79,7 @@ static size_t max_scanline_buffer_size; size_t _size; \ \ if (ckd_mul (&_size, height, stride)) \ - memory_full (SIZE_MAX); \ + memory_full_up (); \ \ if (_size < MAX_ALLOCA) \ (buffer) = alloca (_size); \ @@ -113,7 +113,7 @@ static size_t max_scanline_buffer_size; void *_temp; \ \ if (ckd_mul (&_size, height, stride)) \ - memory_full (SIZE_MAX); \ + memory_full_up (); \ \ if (_size > scanline_buffer.buffer_size) \ { \ diff --git a/src/sfntfont.c b/src/sfntfont.c index 0cbb41a1f2e..4f49f03d744 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -613,7 +613,7 @@ sfnt_parse_languages (struct sfnt_meta_table *meta, /* Now copy metadata and add a trailing NULL byte. */ if (map.data_length >= SIZE_MAX) - memory_full (SIZE_MAX); + memory_full_up (); metadata = SAFE_ALLOCA ((size_t) map.data_length + 1); memcpy (metadata, data, map.data_length); diff --git a/src/term.c b/src/term.c index 354375085e2..e4fdb85831c 100644 --- a/src/term.c +++ b/src/term.c @@ -554,7 +554,7 @@ encode_terminal_code (struct glyph *src, int src_len, Vglyph_table contains a string or a composite glyph is encountered. */ if (ckd_mul (&required, src_len, MAX_MULTIBYTE_LENGTH)) - memory_full (SIZE_MAX); + memory_full_up (); if (encode_terminal_src_size < required) encode_terminal_src = xpalloc (encode_terminal_src, &encode_terminal_src_size, @@ -1245,7 +1245,7 @@ calculate_costs (struct frame *frame) max_frame_cols = max (max_frame_cols, FRAME_COLS (frame)); if ((min (PTRDIFF_MAX, SIZE_MAX) / sizeof (int) - 1) / 2 < max_frame_cols) - memory_full (SIZE_MAX); + memory_full_up (); char_ins_del_vector = xrealloc (char_ins_del_vector, diff --git a/src/timefns.c b/src/timefns.c index 4bfb267c5c1..88f5504fe35 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -213,7 +213,7 @@ emacs_localtime_rz (timezone_t tz, time_t const *t, struct tm *tm) #endif tm = localtime_rz (tz, t, tm); if (!tm && errno == ENOMEM) - memory_full (SIZE_MAX); + memory_full_up (); return tm; } @@ -317,7 +317,7 @@ tzlookup (Lisp_Object zone, bool settz) if (!new_tz) { if (errno == ENOMEM) - memory_full (SIZE_MAX); + memory_full_up (); invalid_time_zone_specification (zone); } } @@ -367,7 +367,7 @@ time_error (int err) { switch (err) { - case ENOMEM: memory_full (SIZE_MAX); + case ENOMEM: memory_full_up (); case EOVERFLOW: time_overflow (); default: time_spec_invalid (); } diff --git a/src/tparam.c b/src/tparam.c index 261ef02a55e..d37fb67cfac 100644 --- a/src/tparam.c +++ b/src/tparam.c @@ -173,7 +173,7 @@ tparam1 (const char *string, char *outstring, int len, else doleft++, append_len_incr = strlen (left); if (ckd_add (&append_len, append_len, append_len_incr)) - memory_full (SIZE_MAX); + memory_full_up (); } } *op++ = tem ? tem : 0200; diff --git a/src/w32term.c b/src/w32term.c index 43d2440854b..ccf5d14cb10 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -7820,7 +7820,7 @@ w32_initialize_display_info (Lisp_Object display_name) static char const at[] = " at "; ptrdiff_t nbytes = sizeof (title) + sizeof (at); if (ckd_add (&nbytes, nbytes, SCHARS (Vsystem_name))) - memory_full (SIZE_MAX); + memory_full_up (); dpyinfo->w32_id_name = xmalloc (nbytes); sprintf (dpyinfo->w32_id_name, "%s%s%s", title, at, SDATA (Vsystem_name)); } diff --git a/src/xselect.c b/src/xselect.c index 93057c2d6c5..5be009af0d7 100644 --- a/src/xselect.c +++ b/src/xselect.c @@ -1882,7 +1882,7 @@ x_get_window_property (Display *display, Window window, Atom property, if (data) xfree (data); unblock_input (); - memory_full (SIZE_MAX); + memory_full_up (); } /* Use xfree, not XFree, to free the data obtained with this function. */ @@ -1903,7 +1903,7 @@ receive_incremental_selection (struct x_display_info *dpyinfo, Display *display = dpyinfo->display; if (min (PTRDIFF_MAX, SIZE_MAX) < min_size_bytes) - memory_full (SIZE_MAX); + memory_full_up (); *data_ret = xmalloc (min_size_bytes); *size_bytes_ret = min_size_bytes; @@ -3063,7 +3063,7 @@ x_property_data_to_lisp (struct frame *f, const unsigned char *data, ptrdiff_t format_bytes = format >> 3; ptrdiff_t data_bytes; if (ckd_mul (&data_bytes, size, format_bytes)) - memory_full (SIZE_MAX); + memory_full_up (); return selection_data_to_lisp_data (FRAME_DISPLAY_INFO (f), data, data_bytes, type, format); } diff --git a/src/xsmfns.c b/src/xsmfns.c index c3e6224b49a..5fec0e8913e 100644 --- a/src/xsmfns.c +++ b/src/xsmfns.c @@ -224,7 +224,7 @@ smc_save_yourself_CB (SmcConn smcConn, props[props_idx]->type = xstrdup (SmLISTofARRAY8); /* /path/to/emacs, --smid=xxx --no-splash --chdir=dir ... */ if (ckd_add (&i, initial_argc, 3)) - memory_full (SIZE_MAX); + memory_full_up (); props[props_idx]->num_vals = i; vp = xnmalloc (i, sizeof *vp); props[props_idx]->vals = vp; diff --git a/src/xterm.c b/src/xterm.c index b2a0d2cadcc..6580bda9fef 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -30989,7 +30989,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name) static char const at[] = " at "; ptrdiff_t nbytes = sizeof (title) + sizeof (at); if (ckd_add (&nbytes, nbytes, SBYTES (system_name))) - memory_full (SIZE_MAX); + memory_full_up (); dpyinfo->x_id_name = xmalloc (nbytes); sprintf (dpyinfo->x_id_name, "%s%s%s", title, at, SDATA (system_name)); } diff --git a/src/xwidget.c b/src/xwidget.c index 0b890375c30..e554dc63bbf 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -2462,7 +2462,7 @@ webkit_js_to_lisp (JSCValue *value) Lisp_Object obj; if (! (0 <= dlen && dlen < G_MAXINT32)) - memory_full (SIZE_MAX); + memory_full_up (); ptrdiff_t n = dlen; struct Lisp_Vector *p = allocate_nil_vector (n); commit a96fc7d5465f97908a9a6d5d2cf3be8cbf6b0ebe Merge: 7fe595465bc 4f13f52a3aa Author: Sean Whitton Date: Sat May 23 17:30:21 2026 +0100 Merge from origin/emacs-31 4f13f52a3aa * build-aux/git-hooks/commit-msg: Replace Markdown-style ... dd42133315b vc-test--rename-file: Disable part of test for SCCS eb653865c3a markdown-ts-mode: Don't enable unconditionally by default # Conflicts: # etc/NEWS commit 4f13f52a3aade6e43e42f14f9f94b0c43d6b4b12 Author: Sean Whitton Date: Sat May 23 17:25:07 2026 +0100 * build-aux/git-hooks/commit-msg: Replace Markdown-style quotation. diff --git a/build-aux/git-hooks/commit-msg b/build-aux/git-hooks/commit-msg index 159990b1406..ddde1b4e586 100755 --- a/build-aux/git-hooks/commit-msg +++ b/build-aux/git-hooks/commit-msg @@ -75,6 +75,7 @@ exec $awk \ } c_lower = "abcdefghijklmnopqrstuvwxyz" unsafe_gnu_url = "(http|ftp)://([" c_lower ".]*\\.)?(gnu|fsf)\\.org" + markdown_quotation = "(^|[^\\\\])`[^'\''`]+`" } { input[NR] = $0 } @@ -92,11 +93,6 @@ exec $awk \ status = 1 } - /(^|[^\\])`[^'\''`]+`/ { - print "Markdown-style quotes in commit message" - status = 1 - } - nlines == 0 && $0 !~ non_space { next } { nlines++ } @@ -141,7 +137,7 @@ exec $awk \ status = 1 } - $0 ~ unsafe_gnu_url { + $0 ~ unsafe_gnu_url || $0 ~ markdown_quotation { needs_rewriting = 1 } @@ -167,7 +163,13 @@ exec $awk \ suffix = substr(line, RSTART) line = prefix "https:" substr(suffix, 5 + (suffix ~ /^http:/)) } - print line >file + while (match(line, markdown_quotation)) { + prefix = substr(line, 1, RSTART) + within = substr(line, RSTART + 2, RLENGTH - 3) + suffix = substr(line, RSTART + RLENGTH) + line = prefix "'\''" within "'\''" suffix + } + print line >file } if (close(file) != 0) { print "Cannot rewrite: " file commit dd42133315b9aea8ed8ce54191a5e85249417a0c Author: Sean Whitton Date: Sat May 23 16:53:36 2026 +0100 vc-test--rename-file: Disable part of test for SCCS * test/lisp/vc/vc-tests/vc-tests.el (vc-test--rename-file): Disable part of test for SCCS. diff --git a/test/lisp/vc/vc-tests/vc-tests.el b/test/lisp/vc/vc-tests/vc-tests.el index 1fb17842478..8e2ae2c4454 100644 --- a/test/lisp/vc/vc-tests/vc-tests.el +++ b/test/lisp/vc/vc-tests/vc-tests.el @@ -592,8 +592,8 @@ This checks also `vc-backend' and `vc-responsible-backend'." 'added)))) ;; Test OK-IF-ALREADY-EXISTS. - ;; RCS and SRC don't support `vc-delete-file'. - (unless (memq backend '(RCS SRC)) + ;; RCS, SRC and SCCS don't support `vc-delete-file'. + (unless (memq backend '(RCS SRC SCCS)) (let ((tmp-name (expand-file-name "qux" default-directory)) (new-name (expand-file-name "quuux" default-directory))) (write-region "qux" nil tmp-name nil 'nomessage) commit eb653865c3a35af115360273fb5147b4943ba2ef Author: Rahul Martim Juliato Date: Sat May 23 09:18:40 2026 -0300 markdown-ts-mode: Don't enable unconditionally by default * lisp/textmodes/markdown-ts-mode.el (markdown-ts-mode-maybe): New function. (auto-mode-alist): Bind ".md", ".markdown", and ".mdx" to 'markdown-ts-mode-maybe' instead of 'markdown-ts-mode'. * etc/NEWS: Update the 'markdown-ts-mode' entry. diff --git a/etc/NEWS b/etc/NEWS index 083e6b50ea4..7fc998ff547 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3938,19 +3938,12 @@ A major mode based on 'conf-mode' for editing ".npmrc" files. *** New major mode 'markdown-ts-mode'. A major mode based on the tree-sitter library for editing Markdown -files. This is now the default major mode for Markdown files. If you -don't have the necessary tree-sitter grammar libraries installed, or if -your Emacs was built without tree-sitter support, Emacs will now show a -warning to that effect when you visit a Markdown file. If you don't -want to use this mode and want to avoid these warnings, add the -following to your init file: - - (add-to-list 'auto-mode-alist '("\\.md\\'" . fundamental-mode)) - (add-to-list 'auto-mode-alist '("\\.markdown\\'" . fundamental-mode)) - (add-to-list 'auto-mode-alist '("\\.mdx\\'" . fundamental-mode)) - -This will cause Emacs to visit Markdown files in Fundamental mode, which -was the default before this mode was added to Emacs. +files. Markdown files are visited using this mode when the required +tree-sitter grammars ('markdown' and 'markdown-inline') are available, +or when the user has opted in via 'treesit-enabled-modes'. Otherwise, +Markdown files fall back to 'text-mode'. + +To install the grammars, use 'M-x markdown-ts-mode-install-parsers'. *** New major mode 'mhtml-ts-mode'. An optional major mode based on the tree-sitter library for editing HTML diff --git a/lisp/textmodes/markdown-ts-mode.el b/lisp/textmodes/markdown-ts-mode.el index be2247b870e..fed6ded192c 100644 --- a/lisp/textmodes/markdown-ts-mode.el +++ b/lisp/textmodes/markdown-ts-mode.el @@ -5401,14 +5401,14 @@ With a prefix argument, ARG, if needed, install parsers for `html', (cond ((treesit-ready-p '(markdown markdown-inline) t) (markdown-ts--set-up)) (t - (warn "markdown-ts-mode cannot be set up; using fundamental-mode. + (warn "markdown-ts-mode cannot be set up; using text-mode. %s." (if (treesit-available-p) "The tree-sitter parsers `markdown' and `markdown-inline' were not found. Use the command `markdown-ts-mode-install-parsers' to install them. With a prefix argument, it can also install optional parsers" "Emacs was built without Tree-sitter support, or could not load Tree-sitter")) - (fundamental-mode))))) + (text-mode))))) ;;;###autoload (define-derived-mode markdown-ts-mode text-mode "Markdown" @@ -5619,11 +5619,25 @@ If non-nil and `point' is in a table, enable (remove-hook 'post-command-hook #'markdown-ts--enable-in-table-mode 'local)))) +;;;###autoload +(defun markdown-ts-mode-maybe () + "Enable `markdown-ts-mode' when its grammars are available. +Also propose to install the grammars when `treesit-enabled-modes' +is t or contains the mode name." + (declare-function treesit-language-available-p "treesit.c") + (if (or (and (treesit-language-available-p 'markdown) + (treesit-language-available-p 'markdown-inline)) + (eq treesit-enabled-modes t) + (memq 'markdown-ts-mode treesit-enabled-modes)) + (markdown-ts-mode) + (text-mode))) + ;;;###autoload (when (boundp 'treesit-major-mode-remap-alist) - (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-ts-mode)) - (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-ts-mode)) - (add-to-list 'auto-mode-alist '("\\.mdx\\'" . markdown-ts-mode)) + (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-ts-mode-maybe)) + (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-ts-mode-maybe)) + (add-to-list 'auto-mode-alist '("\\.mdx\\'" . markdown-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: (add-to-list 'treesit-major-mode-remap-alist '(markdown-mode . markdown-ts-mode))) commit 7fe595465bcca3a7ef59feadfd29b38a75315c65 Author: Sean Whitton Date: Mon May 18 22:35:22 2026 +0100 vc-refresh-state: Use cond* This is okay with regard to bootstrapping because vc-hooks.el is loaded after loaddefs.el in loadup.el. * lisp/emacs-lisp/cond-star.el (cl-lib): Don't require, so we can use cond* in preloaded files. (cond*-convert-condition): Replace calls to cl-assert. * lisp/vc/vc-hooks.el (vc-refresh-state): Use cond*. diff --git a/lisp/emacs-lisp/cond-star.el b/lisp/emacs-lisp/cond-star.el index 98d4b93583a..ef0af260e89 100644 --- a/lisp/emacs-lisp/cond-star.el +++ b/lisp/emacs-lisp/cond-star.el @@ -48,8 +48,6 @@ ;;; Code: -(require 'cl-lib) ; for cl-assert - ;;;###autoload (defmacro cond* (&rest clauses) "Extended form of traditional Lisp `cond' construct. @@ -370,12 +368,13 @@ This is used for conditional exit clauses." ;; where ELSE is supposed to run after THEN also (and ;; with access to `x' and `y'). (error ":non-exit not supported with `pcase*'")) - (cl-assert (or (null iffalse) rest)) + (unless (or (null iffalse) rest) + (error "Assertion failed: (or (null iffalse) rest)")) `(pcase ,(nth 2 condition) (,(nth 1 condition) ,@true-exps) (_ ,iffalse))) - (cl-assert (null iffalse)) - (cl-assert (null rest)) + (unless (and (null iffalse) (null rest)) + (error "Assertion failed: (and (null iffalse) (null rest))")) `(pcase-let ((,(nth 1 condition) ,(nth 2 condition))) (cond* . ,uncondit-clauses)))) ((eq pat-type 'match*) diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el index 64ad4d5daec..7267c37851d 100644 --- a/lisp/vc/vc-hooks.el +++ b/lisp/vc/vc-hooks.el @@ -951,62 +951,58 @@ In the latter case, VC mode is deactivated for this buffer." (vc-file-clearprops buffer-file-name) ;; FIXME: Why use a hook? Why pass it buffer-file-name? (add-hook 'vc-mode-line-hook #'vc-mode-line nil t) - (let (backend) - (cond - ((setq backend (with-demoted-errors "VC refresh error: %S" - (vc-backend buffer-file-name))) - ;; When `auto-revert-handler' calls us then `default-directory' - ;; may be let-bound to something else for the purpose of some - ;; command that's currently doing some minibuffer prompting. - ;; Backend find-file-hook and mode-line-string functions should - ;; not need to be written so as to handle that possibility. - (let ((default-directory (buffer-local-toplevel-value 'default-directory))) - ;; Let the backend setup any buffer-local things it needs. - (vc-call-backend backend 'find-file-hook) - ;; Compute the state and put it in the mode line. - (vc-mode-line buffer-file-name backend)) - (unless vc-make-backup-files - ;; Use this variable, not make-backup-files, - ;; because this is for things that depend on the file name. - (setq-local backup-inhibited t))) - ((let* ((truename (and buffer-file-truename - (expand-file-name buffer-file-truename))) - (link-type (and truename - (not (equal buffer-file-name truename)) - (vc-backend truename)))) - (cond ((not link-type) nil) ;Nothing to do. - ((not vc-follow-symlinks) - (message "Warning: symbolic link to %s-controlled source file" - link-type)) - ((or (not (eq vc-follow-symlinks 'ask)) - ;; Assume we cannot ask, default to yes. - noninteractive - ;; Copied from server-start. Seems like there should - ;; be a better way to ask "can we get user input?"... - ;; Use `frame-initial-p'? - (and (daemonp) - (null (cdr (frame-list))) - (eq (selected-frame) terminal-frame)) - ;; If we already visited this file by following - ;; the link, don't ask again if we try to visit - ;; it again. GUD does that, and repeated questions - ;; are painful. - (get-file-buffer - (abbreviate-file-name - (file-chase-links buffer-file-name)))) - - (vc-follow-link) - (message "Followed link to %s" buffer-file-name) - (vc-refresh-state)) - (t - (if (yes-or-no-p (format - "Symbolic link to %s-controlled source file; follow link? " link-type)) - (progn (vc-follow-link) - (message "Followed link to %s" buffer-file-name) - (vc-refresh-state)) - (message - "Warning: editing through the link bypasses version control") - ))))))))) + (cond* + ((bind-and* (backend (with-demoted-errors "VC refresh error: %S" + (vc-backend buffer-file-name)))) + ;; When `auto-revert-handler' calls us then `default-directory' + ;; may be let-bound to something else for the purpose of some + ;; command that's currently doing some minibuffer prompting. + ;; Backend find-file-hook and mode-line-string functions should + ;; not need to be written so as to handle that possibility. + (let ((default-directory (buffer-local-toplevel-value 'default-directory))) + ;; Let the backend setup any buffer-local things it needs. + (vc-call-backend backend 'find-file-hook) + ;; Compute the state and put it in the mode line. + (vc-mode-line buffer-file-name backend)) + (unless vc-make-backup-files + ;; Use this variable, not make-backup-files, + ;; because this is for things that depend on the file name. + (setq-local backup-inhibited t))) + ((bind* (truename (and buffer-file-truename + (expand-file-name buffer-file-truename))) + (link-type (and truename + (not (equal buffer-file-name truename)) + (vc-backend truename))))) + ((null link-type) nil) ; Nothing to do. + ((not vc-follow-symlinks) + (message "Warning: symbolic link to %s-controlled source file" + link-type)) + ((or (not (eq vc-follow-symlinks 'ask)) + ;; Assume we cannot ask, default to yes. + noninteractive + ;; Copied from server-start. Seems like there should + ;; be a better way to ask "can we get user input?"... + ;; Use `frame-initial-p'? + (and (daemonp) + (null (cdr (frame-list))) + (eq (selected-frame) terminal-frame)) + ;; If we already visited this file by following the link, + ;; don't ask again if we try to visit it again. + ;; GUD does that, and repeated questions are painful. + (get-file-buffer + (abbreviate-file-name + (file-chase-links buffer-file-name)))) + (vc-follow-link) + (message "Followed link to %s" buffer-file-name) + (vc-refresh-state)) + ((yes-or-no-p + (format "Symbolic link to %s-controlled source file; follow link? " + link-type)) + (vc-follow-link) + (message "Followed link to %s" buffer-file-name) + (vc-refresh-state)) + (t + (message "Warning: editing through the link bypasses version control"))))) (add-hook 'find-file-hook #'vc-refresh-state) (define-obsolete-function-alias 'vc-find-file-hook #'vc-refresh-state "25.1") commit ccc94458fb5eebdc63f417d86fd8ce0579acc40c Merge: 741feca4972 7a17f97baa7 Author: Eli Zaretskii Date: Sat May 23 07:23:33 2026 -0400 Merge from origin/emacs-31 7a17f97baa7 Prettify special glyphs f13287fde0d Revert "sh-script: Mark + and * as punctuation rather tha... 70b79b3ed8d Rename `icalendar-recur' type and related functions 3d2bb233f27 ; Minor Tramp changes f6281d757d3 ; * etc/NEWS: Tell how to disable 'markdown-ts-mode'. 142b1e0d4c3 Fix Lisp injection via X-Draft-From in Gnus d6f7b2d99bd Save/restore old_buffer slot via window configurations (B... e0fbecaf658 Adapt ert-remote-temporary-file-directory settings 3de7f0ce5e5 Fix warning message in 'markdown-ts-mode--initialize' 7df8604ea63 ; Improve documentation of lazy-highlight in search and r... 2936b36164d Fix "assertion 'GTK_IS_WINDOW (window)' failed" 98348a0bdc9 [Xt] Fix child frame resizing glitch 13b29eebc16 Eglot: use standard face for completion annotations (bug#... # Conflicts: # etc/NEWS commit 7a17f97baa7d483cba5cde3cd22c34e0597e60b5 Author: Manuel Giraud Date: Mon May 4 10:14:36 2026 +0200 Prettify special glyphs * lisp/disp-table.el (prettify-special-glyphs-mode): New mode to display nicer special glyphs. (special-glyphs): New face for displaying special glyphs when the minor mode is active. (prettify-special-glyphs-saved-truncation) (prettify-special-glyphs-saved-continuation): Internal variables to save previous special glyphs. * etc/NEWS: Announce the change. (Bug#80628) diff --git a/etc/NEWS b/etc/NEWS index 1f99eb22a38..083e6b50ea4 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -169,6 +169,12 @@ behavior, customize 'find-function-mode-lower-precedence' to non-nil. --- ** 'find-function' can now find 'cl-defmethod' invocations inside macros. +--- +** New minor mode 'prettify-special-glyphs-mode'. +The new minor mode prettifies the special character glyphs (truncation +and continuation) on TTY frames (and GUI frames without fringes). You +can customize the associated new face 'special-glyphs'. + ** Minibuffer and Completions +++ diff --git a/lisp/disp-table.el b/lisp/disp-table.el index 9f2971a6fc3..467430f30ef 100644 --- a/lisp/disp-table.el +++ b/lisp/disp-table.el @@ -458,6 +458,50 @@ which characters can be displayed and which cannot." (insert ")\n")) (pop-to-buffer buf))) +(defface special-glyphs + '((t :inherit (shadow default))) + "Face for displaying special glyphs." + :group 'basic-faces + :version "31.1") + +(defvar prettify-special-glyphs-saved-truncation) +(defvar prettify-special-glyphs-saved-continuation) + +;;;###autoload +(define-minor-mode prettify-special-glyphs-mode + "Mode to display pretty special character glyphs. +If you have already customized your special character glyphs, only the +`special-glyphs' face is applied to them. This mode only applies to the +`standard-display-table'. Window or buffer display table, if defined, +still take precedence." + :global t + :group 'display + (if prettify-special-glyphs-mode + (let ((tbl standard-display-table) + truncation wrap) + ;; Save current glyphs. + (setq prettify-special-glyphs-saved-truncation + (display-table-slot tbl 'truncation) + prettify-special-glyphs-saved-continuation + (display-table-slot tbl 'wrap)) + ;; Prepare new ones with face. + (setq truncation + (if prettify-special-glyphs-saved-truncation + (make-glyph-code prettify-special-glyphs-saved-truncation + 'special-glyphs) + (make-glyph-code ?→ 'special-glyphs)) + wrap + (if prettify-special-glyphs-saved-continuation + (make-glyph-code prettify-special-glyphs-saved-continuation + 'special-glyphs) + (make-glyph-code ?↩ 'special-glyphs))) + ;; Alter display-table. + (set-display-table-slot tbl 'truncation truncation) + (set-display-table-slot tbl 'wrap wrap)) + (let ((tbl standard-display-table)) + ;; Reset saved glyphs. + (set-display-table-slot tbl 'truncation prettify-special-glyphs-saved-truncation) + (set-display-table-slot tbl 'wrap prettify-special-glyphs-saved-continuation)))) (provide 'disp-table) commit 741feca4972ad046fa463b9015942c4196c83bcc Author: Elias Gabriel Perez Date: Thu Mar 19 23:04:27 2026 -0600 New tool bar icons for artist-mode * etc/images/artist-mode/README: * etc/images/artist-mode/char-for-spray.xpm: * etc/images/artist-mode/char-to-fill.xpm: * etc/images/artist-mode/ellipse.xpm: * etc/images/artist-mode/eraser.xpm: * etc/images/artist-mode/fill.xpm: * etc/images/artist-mode/line.xpm: * etc/images/artist-mode/pen.xpm: * etc/images/artist-mode/poly-line.xpm: * etc/images/artist-mode/rectangle.xpm: * etc/images/artist-mode/spray.xpm: * etc/images/artist-mode/square.xpm: * etc/images/artist-mode/text.xpm: * etc/images/artist-mode/char-for-spray.pbm: * etc/images/artist-mode/char-to-fill.pbm: * etc/images/artist-mode/ellipse.pbm: * etc/images/artist-mode/eraser.pbm: * etc/images/artist-mode/fill.pbm: * etc/images/artist-mode/line.pbm: * etc/images/artist-mode/pen.pbm: * etc/images/artist-mode/poly-line.pbm: * etc/images/artist-mode/rectangle.pbm: * etc/images/artist-mode/spray.pbm: * etc/images/artist-mode/square.pbm: * etc/images/artist-mode/text.pbm: New files. * lisp/textmodes/artist.el (artist-tool-bar-map): New variable. (artist-mode, artist-mode-exit): Use it (bug#80644). diff --git a/etc/images/artist-mode/README b/etc/images/artist-mode/README new file mode 100644 index 00000000000..3123cfc76c8 --- /dev/null +++ b/etc/images/artist-mode/README @@ -0,0 +1,19 @@ +COPYRIGHT AND LICENSE INFORMATION FOR IMAGE FILES -*- coding: utf-8 -*- + +The following icons were derived from GIMP 3.2.X icons, modified for +Emacs by Elías Gabriel Pérez . +Copyright (C) 2026 Free Software Foundation, Inc. +License: GNU General Public License version 3 or later (see COPYING) + + char-for-spray.xpm char-for-spray.pbm + char-to-fill.xpm char-to-fill.pbm + ellipse.xpm ellipse.pbm + eraser.xpm eraser.pbm + fill.xpm fill.pbm + line.xpm line.pbm + pen.xpm pen.pbm + poly-line.xpm poly-line.pbm + rectangle.xpm rectangle.pbm + spray.xpm spray.pbm + square.xpm square.pbm + text.xpm text.pbm diff --git a/etc/images/artist-mode/char-for-spray.pbm b/etc/images/artist-mode/char-for-spray.pbm new file mode 100644 index 00000000000..f41de01ca49 Binary files /dev/null and b/etc/images/artist-mode/char-for-spray.pbm differ diff --git a/etc/images/artist-mode/char-for-spray.xpm b/etc/images/artist-mode/char-for-spray.xpm new file mode 100644 index 00000000000..e0b9534fc24 --- /dev/null +++ b/etc/images/artist-mode/char-for-spray.xpm @@ -0,0 +1,281 @@ +/* XPM */ +static char * char_for_spray_xpm[] = { +"26 24 254 2", +" c None", +". c #A1A39E", +"+ c #CDCECC", +"@ c #CFD0CD", +"# c #CDCDCB", +"$ c #A1A29F", +"% c #E1E1DF", +"& c #CCCEC8", +"* c #C8CAC4", +"= c #E0E1DF", +"- c #959691", +"; c #E4E5E3", +"> c #CACCC7", +", c #BDBEBA", +"' c #ABADA9", +") c #BDBFBB", +"! c #939792", +"~ c #E6E7E4", +"{ c #CCCEC9", +"] c #575856", +"^ c #333432", +"/ c #444543", +"( c #212120", +"_ c #4E4E4C", +": c #CBCDC8", +"< c #959792", +"[ c #E7E8E5", +"} c #CED0CB", +"| c #282827", +"1 c #959793", +"2 c #E8E9E6", +"3 c #D0D2CD", +"4 c #9A9C98", +"5 c #3B3B3A", +"6 c #2F302F", +"7 c #373837", +"8 c #141413", +"9 c #767775", +"0 c #CDCFCA", +"a c #000000", +"b c #C9CBC6", +"c c #E9EAE7", +"d c #C7C9C4", +"e c #090909", +"f c #AAACA8", +"g c #D2D4CF", +"h c #3C3D3B", +"i c #737472", +"j c #CFD1CC", +"k c #959893", +"l c #EAEBE9", +"m c #C0C1BD", +"n c #050505", +"o c #BEBEBB", +"p c #D5D6D2", +"q c #A5A6A3", +"r c #060606", +"s c #757573", +"t c #D2D3CF", +"u c #8B8B89", +"v c #2E2E2D", +"w c #393938", +"x c #B4B4B1", +"y c #DDDEDC", +"z c #494D4C", +"A c #2F3235", +"B c #979893", +"C c #EBECEA", +"D c #D7D8D4", +"E c #767774", +"F c #1E1E1E", +"G c #313130", +"H c #50504F", +"I c #5E5E5C", +"J c #898A88", +"K c #D4D5D1", +"L c #5D5D5B", +"M c #030303", +"N c #232322", +"O c #C4C5C2", +"P c #686B6B", +"Q c #4A4D4D", +"R c #303537", +"S c #979895", +"T c #ECEDEB", +"U c #D9DAD6", +"V c #D6D7D3", +"W c #BBBCB9", +"X c #414140", +"Y c #0D1012", +"Z c #5C605F", +"` c #575A58", +" . c #323939", +".. c #EDEEEC", +"+. c #DBDCD8", +"@. c #D8D9D5", +"#. c #010101", +"$. c #C4C4C1", +"%. c #4D4D4C", +"&. c #333839", +"*. c #555957", +"=. c #454948", +"-. c #3B4040", +";. c #2F3536", +">. c #979A95", +",. c #EEEFED", +"'. c #DDDEDA", +"). c #AFB0AD", +"!. c #474746", +"~. c #424241", +"{. c #373736", +"]. c #A8A9A6", +"^. c #DADBD7", +"/. c #171A1C", +"(. c #303538", +"_. c #7A7C7A", +":. c #34393A", +"<. c #525654", +"[. c #3E4343", +"}. c #6D716E", +"|. c #989A95", +"1. c #EFF0EE", +"2. c #DFE0DD", +"3. c #A4A4A2", +"4. c #111110", +"5. c #BCBDBA", +"6. c #40403F", +"7. c #939492", +"8. c #DBDCDA", +"9. c #000101", +"0. c #293030", +"a. c #3D4242", +"b. c #434848", +"c. c #484C4B", +"d. c #353B3B", +"e. c #B3B4B3", +"f. c #989A97", +"g. c #F0F1EF", +"h. c #E1E2DF", +"i. c #DEDFDC", +"j. c #181818", +"k. c #737372", +"l. c #B5B6B3", +"m. c #D2D3D0", +"n. c #BABBB8", +"o. c #696B68", +"p. c #6B6D6D", +"q. c #3B4141", +"r. c #A8A9A7", +"s. c #F1F2F0", +"t. c #E3E4E1", +"u. c #A9AAA8", +"v. c #B9BAB8", +"w. c #D3D5D2", +"x. c #818380", +"y. c #C1C1C1", +"z. c #DEDEDE", +"A. c #A4A4A3", +"B. c #878988", +"C. c #9A9B97", +"D. c #F3F3F1", +"E. c #E6E6E3", +"F. c #939391", +"G. c #DDDDDA", +"H. c #E3E3E0", +"I. c #D9D9D7", +"J. c #D5D5D2", +"K. c #888986", +"L. c #CCCCCC", +"M. c #E4E4E4", +"N. c #BFBFBE", +"O. c #B5B5B5", +"P. c #7C7C7B", +"Q. c #CBCBC9", +"R. c #232323", +"S. c #F4F4F2", +"T. c #E8E8E5", +"U. c #AEAEAC", +"V. c #CACAC7", +"W. c #747473", +"X. c #737371", +"Y. c #E7E7E4", +"Z. c #C3C3C0", +"`. c #A3A5A3", +" + c #E6E6E5", +".+ c #A3A4A2", +"++ c #7A7B79", +"@+ c #ADAEAD", +"#+ c #8F8F8F", +"$+ c #AFAFAD", +"%+ c #8B8B88", +"&+ c #5D5D5D", +"*+ c #585858", +"=+ c #9A9B98", +"-+ c #F5F5F4", +";+ c #EAEAE8", +">+ c #666665", +",+ c #2D2D2D", +"'+ c #767675", +")+ c #575756", +"!+ c #6C6C6B", +"~+ c #E7E7E5", +"{+ c #757774", +"]+ c #B0B3AD", +"^+ c #A0A19E", +"/+ c #B0B0AE", +"(+ c #BBBBB9", +"_+ c #6A6B68", +":+ c #626262", +"<+ c #C3C3C2", +"[+ c #595959", +"}+ c #7F7F7F", +"|+ c #9B9D99", +"1+ c #ECECEA", +"2+ c #E5E5E3", +"3+ c #C9C9C7", +"4+ c #E3E3E1", +"5+ c #777775", +"6+ c #939591", +"7+ c #B9BAB7", +"8+ c #4D4E4D", +"9+ c #767676", +"0+ c #747474", +"a+ c #777777", +"b+ c #545454", +"c+ c #EBEBEA", +"d+ c #EFEFED", +"e+ c #EEEEEC", +"f+ c #E8E9E8", +"g+ c #9AA2B5", +"h+ c #BDBDBB", +"i+ c #B7B8B5", +"j+ c #EBEBE9", +"k+ c #EDEDEB", +"l+ c #C0C0BE", +"m+ c #4B4B4A", +"n+ c #595958", +"o+ c #3F3F3F", +"p+ c #969894", +"q+ c #D0D1D0", +"r+ c #D9D9D8", +"s+ c #DADAD9", +"t+ c #C6C8CB", +"u+ c #A5ABB8", +"v+ c #466293", +"w+ c #818DA6", +"x+ c #D7D7D6", +"y+ c #D8D8D7", +"z+ c #939390", +"A+ c #375A8F", +"B+ c #38598F", +"C+ c #37578C", +"D+ c #37588E", +"E+ c #355887", +" ", +" . + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # $ ", +" % & * * * * * * * * * * * * * * * * & = ", +" - ; > > , ' ) > > > > > > > > > > > > > ; - ", +" ! ~ { ] ^ / ( _ : { { { { { { { { { { { ~ ! ", +" < [ } > } } { | 1 } } } } } } } } } } } [ < ", +" < 2 3 4 5 6 7 8 9 3 3 3 0 a a b 3 3 3 3 2 < ", +" 1 c d e f g g h i g g g j a a : g g g g c 1 ", +" k l m n o p q r s p p p t a a u v w x p y z A ", +" B C D E F G H I J D D D K a a G L M N O P Q R ", +" S T U U U U U U U U U U V a a W U X Y Z ` z . ", +" S ..+.+.+.+.+.+.+.+.+.+.@.a #.$.D %.&.*.=.-.;. ", +" >.,.'.'.'.'.).!.H ~.{.].^.a /.(._.:.<.z [.}. ", +" |.1.2.2.2.3.4.5.2.2.6.7.8.9.0.:.a.b.c.d.e.|. ", +" f.g.h.h.i.j.k.h.h.h.l.m.h.m.n.o.e.p.q.r...f. ", +" f.s.t.t.u.a v.t.t.t.t.t.t.w.x.y.z.A.B.t.s.f. ", +" C.D.E.E.F.a G.E.E.H.I.E.J.K.L.M.N.O.P.Q.D.C. R.", +" C.S.T.T.U.a V.T.T.W.X.Y.Z.`. +.+++@+#+$+D.%+&+*+", +" =+-+;+;+;+>+,+'+)+!+~+% {+]+^+/+(+_+#+:+<+[+}+ ", +" |+-+1+1+1+1+2+3+4+1+1+4+5+6+7+1+1+(+8+9+0+a+b+ ", +" c+d+e+e+e+e+e+e+T f+g+h+i+j+e+e+k+l+m+n+o+ ", +" p+q+r+s+r+r+r+t+u+v+w+x+y+r+r+r+s+y+# z+ ", +" A+B+C+ ", +" D+E+ "}; diff --git a/etc/images/artist-mode/char-to-fill.pbm b/etc/images/artist-mode/char-to-fill.pbm new file mode 100644 index 00000000000..91a3eae85d8 Binary files /dev/null and b/etc/images/artist-mode/char-to-fill.pbm differ diff --git a/etc/images/artist-mode/char-to-fill.xpm b/etc/images/artist-mode/char-to-fill.xpm new file mode 100644 index 00000000000..cb3ed3185b8 --- /dev/null +++ b/etc/images/artist-mode/char-to-fill.xpm @@ -0,0 +1,301 @@ +/* XPM */ +static char * char_to_fill_xpm[] = { +"26 24 274 2", +" c None", +". c #A1A39E", +"+ c #CDCECC", +"@ c #CFD0CD", +"# c #CDCDCB", +"$ c #A1A29F", +"% c #E1E1DF", +"& c #CCCEC8", +"* c #C8CAC4", +"= c #E0E1DF", +"- c #959691", +"; c #E4E5E3", +"> c #CACCC7", +", c #BDBEBA", +"' c #ABADA9", +") c #BDBFBB", +"! c #939792", +"~ c #E6E7E4", +"{ c #CCCEC9", +"] c #575856", +"^ c #333432", +"/ c #444543", +"( c #212120", +"_ c #4E4E4C", +": c #CBCDC8", +"< c #959792", +"[ c #E7E8E5", +"} c #CED0CB", +"| c #282827", +"1 c #959793", +"2 c #E8E9E6", +"3 c #D0D2CD", +"4 c #9A9C98", +"5 c #3B3B3A", +"6 c #2F302F", +"7 c #373837", +"8 c #141413", +"9 c #767775", +"0 c #CDCFCA", +"a c #000000", +"b c #C9CBC6", +"c c #E9EAE7", +"d c #C7C9C4", +"e c #090909", +"f c #AAACA8", +"g c #D2D4CF", +"h c #3C3D3B", +"i c #737472", +"j c #CFD1CC", +"k c #959893", +"l c #EAEBE9", +"m c #C0C1BD", +"n c #050505", +"o c #BEBEBB", +"p c #D5D6D2", +"q c #A5A6A3", +"r c #060606", +"s c #757573", +"t c #D2D3CF", +"u c #8B8B89", +"v c #2E2E2D", +"w c #393938", +"x c #B4B4B1", +"y c #979893", +"z c #EBECEA", +"A c #D7D8D4", +"B c #767774", +"C c #1E1E1E", +"D c #313130", +"E c #50504F", +"F c #5E5E5C", +"G c #898A88", +"H c #D4D5D1", +"I c #5D5D5B", +"J c #30312E", +"K c #454744", +"L c #979995", +"M c #C7C8C6", +"N c #979895", +"O c #ECEDEB", +"P c #D9DAD6", +"Q c #D6D7D3", +"R c #BBBCB9", +"S c #BEBFBB", +"T c #4D4E4C", +"U c #353533", +"V c #A2A3A0", +"W c #B2B4B1", +"X c #80817E", +"Y c #EDEEEC", +"Z c #DBDCD8", +"` c #D8D9D5", +" . c #CCCCC9", +".. c #ADAEAB", +"+. c #5D5E5C", +"@. c #858684", +"#. c #969795", +"$. c #DDDEDC", +"%. c #747673", +"&. c #979A95", +"*. c #EEEFED", +"=. c #DDDEDA", +"-. c #AFB0AD", +";. c #474746", +">. c #424241", +",. c #373736", +"'. c #A8A9A6", +"). c #DADBD7", +"!. c #0A0A09", +"~. c #70706E", +"{. c #787A77", +"]. c #C5C5C4", +"^. c #E8E8E8", +"/. c #BABBB9", +"(. c #747773", +"_. c #989A95", +":. c #EFF0EE", +"<. c #DFE0DD", +"[. c #A4A4A2", +"}. c #111110", +"|. c #BCBDBA", +"1. c #C8CBCE", +"2. c #4A5269", +"3. c #808592", +"4. c #B7BAC2", +"5. c #363C4A", +"6. c #6B6D6C", +"7. c #BABAB9", +"8. c #ADAEAD", +"9. c #D1D1D1", +"0. c #DBDBDB", +"a. c #9EA09E", +"b. c #737571", +"c. c #989A97", +"d. c #F0F1EF", +"e. c #E1E2DF", +"f. c #DEDFDC", +"g. c #181818", +"h. c #737372", +"i. c #C4C7CC", +"j. c #91A1C0", +"k. c #8AA6D0", +"l. c #7190BF", +"m. c #667289", +"n. c #A6A9AD", +"o. c #E7E7E7", +"p. c #F2F3F3", +"q. c #B5B6B5", +"r. c #C0C0C0", +"s. c #D0D0D0", +"t. c #D8D8D8", +"u. c #C5C6C5", +"v. c #757774", +"w. c #F1F2F0", +"x. c #E3E4E1", +"y. c #A9AAA8", +"z. c #B9BAB8", +"A. c #B1B5C1", +"B. c #AABFDE", +"C. c #95AED0", +"D. c #7F868F", +"E. c #D2D3D3", +"F. c #F2F2F2", +"G. c #F9F9F9", +"H. c #797A78", +"I. c #C7C7C6", +"J. c #CDCDCD", +"K. c #E0E0E0", +"L. c #949593", +"M. c #9A9B97", +"N. c #F3F3F1", +"O. c #E6E6E3", +"P. c #939391", +"Q. c #DDDDDA", +"R. c #B0B4C0", +"S. c #AFC5E3", +"T. c #93AACC", +"U. c #A4A5A8", +"V. c #EAEAEA", +"W. c #F0F0F0", +"X. c #8B8C8A", +"Y. c #8F908F", +"Z. c #8E8F8E", +"`. c #959795", +" + c #D2D2D2", +".+ c #D0D1D0", +"++ c #F4F4F2", +"@+ c #E8E8E5", +"#+ c #AEAEAC", +"$+ c #CACAC7", +"%+ c #B0C5E3", +"&+ c #94AED6", +"*+ c #787D8A", +"=+ c #D9DAD9", +"-+ c #DFDFDF", +";+ c #E6E6E6", +">+ c #959594", +",+ c #8D8E8C", +"'+ c #A4A5A4", +")+ c #CECECE", +"!+ c #DBDBDA", +"~+ c #A5A6A4", +"{+ c #9A9B98", +"]+ c #F5F5F4", +"^+ c #EAEAE8", +"/+ c #666665", +"(+ c #2D2D2D", +"_+ c #767675", +":+ c #58617C", +"<+ c #B0B4BC", +"[+ c #A4A5A3", +"}+ c #DEDEDE", +"|+ c #AFB0AF", +"1+ c #B1B2B1", +"2+ c #E1E1E0", +"3+ c #DDDDDD", +"4+ c #D3D3D3", +"5+ c #757673", +"6+ c #9B9D99", +"7+ c #ECECEA", +"8+ c #E5E5E3", +"9+ c #C9C9C7", +"0+ c #AEB2BF", +"a+ c #93ADD5", +"b+ c #C4C6CE", +"c+ c #C3C3C1", +"d+ c #C8C8C7", +"e+ c #F8F8F8", +"f+ c #F7F7F7", +"g+ c #EDEDED", +"h+ c #E3E3E3", +"i+ c #D9D9D9", +"j+ c #D7D7D7", +"k+ c #777875", +"l+ c #EBEBEA", +"m+ c #EFEFED", +"n+ c #EEEEEC", +"o+ c #BEC2CC", +"p+ c #9AADCF", +"q+ c #8AA0C8", +"r+ c #CCCED4", +"s+ c #B4B4B2", +"t+ c #EFEFEF", +"u+ c #FAFAFA", +"v+ c #F3F3F3", +"w+ c #E1E2E1", +"x+ c #9C9D9B", +"y+ c #656764", +"z+ c #969894", +"A+ c #D9D9D8", +"B+ c #DADAD9", +"C+ c #CECFD2", +"D+ c #A6ABBA", +"E+ c #AAAEBD", +"F+ c #D1D2D3", +"G+ c #BBBBBA", +"H+ c #CFCFCF", +"I+ c #DCDCDC", +"J+ c #EAEBEA", +"K+ c #C6C7C6", +"L+ c #7A7A78", +"M+ c #838583", +"N+ c #E6E6E5", +"O+ c #E1E1E1", +"P+ c #D6D6D5", +"Q+ c #979896", +"R+ c #636561", +"S+ c #50534B", +"T+ c #595C57", +"U+ c #A1A3A1", +"V+ c #DDDEDD", +"W+ c #6D6E6B", +"X+ c #50534C", +"Y+ c #4F524A", +" ", +" . + @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ # $ ", +" % & * * * * * * * * * * * * * * * * & = ", +" - ; > > , ' ) > > > > > > > > > > > > > ; - ", +" ! ~ { ] ^ / ( _ : { { { { { { { { { { { ~ ! ", +" < [ } > } } { | 1 } } } } } } } } } } } [ < ", +" < 2 3 4 5 6 7 8 9 3 3 3 0 a a b 3 3 3 3 2 < ", +" 1 c d e f g g h i g g g j a a : g g g g c 1 ", +" k l m n o p q r s p p p t a a u v w x p l k ", +" y z A B C D E F G A A A H a a D I J K L M - ", +" N O P P P P P P P P P P Q a a R S T U V W X ", +" N Y Z Z Z Z Z Z Z Z Z Z ` a a ...+.@.#.$.%. ", +" &.*.=.=.=.=.-.;.E >.,.'.).a !.~.{.].^./.+ (. ", +" _.:.<.<.<.[.}.|.$.1.2.3.4.5.6.7.8.].9.0.a.b. ", +" c.d.e.e.f.g.h.e.i.j.k.l.m.n.o.p.q.r.s.t.u.v. ", +" c.w.x.x.y.a z.x.A.B.C.D.E.F.G.t.H.@.I.J.K.L. ", +" M.N.O.O.P.a Q.O.R.S.T.U.V.K.W.X.Y.Z.`. +9..+H. ", +" M.++@+@+#+a $+@+A.%+&+*+=+-+;+>+,+G '+t.)+!+~+ ", +" {+]+^+^+^+/+(+_+:+%+&+<+[+;+}+K.|+1+2+3+4+ + +5+", +" 6+]+7+7+7+7+8+9+0+S.a+b+c+d+-+;+e+f+g+h+i+j+h+k+", +" l+m+n+n+n+n+n+o+p+q+r+8+s+3+3+t+u+v+V.o.w+x+y+", +" z+.+A+B+A+A+A+C+D+E+F+A+G+H+I+h+f+G.J+K+L+ ", +" M+N+O+F.P+Q+R+ ", +" S+S+T+U+V+x+W+X+Y+S+ "}; diff --git a/etc/images/artist-mode/ellipse.pbm b/etc/images/artist-mode/ellipse.pbm new file mode 100644 index 00000000000..dc6251208a3 Binary files /dev/null and b/etc/images/artist-mode/ellipse.pbm differ diff --git a/etc/images/artist-mode/ellipse.xpm b/etc/images/artist-mode/ellipse.xpm new file mode 100644 index 00000000000..ed63280de4f --- /dev/null +++ b/etc/images/artist-mode/ellipse.xpm @@ -0,0 +1,29 @@ +/* XPM */ +static char * ellipse_xpm[] = { +"24 24 2 1", +" c None", +". c #204A88", +" ", +" ....... ", +" ........... ", +" ... ... ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" .. .. ", +" ... ... ", +" ........... ", +" ....... ", +" ", +" "}; diff --git a/etc/images/artist-mode/eraser.pbm b/etc/images/artist-mode/eraser.pbm new file mode 100644 index 00000000000..e49d546b53b Binary files /dev/null and b/etc/images/artist-mode/eraser.pbm differ diff --git a/etc/images/artist-mode/eraser.xpm b/etc/images/artist-mode/eraser.xpm new file mode 100644 index 00000000000..130926153f9 --- /dev/null +++ b/etc/images/artist-mode/eraser.xpm @@ -0,0 +1,138 @@ +/* XPM */ +static char * eraser_xpm[] = { +"24 24 111 2", +" c None", +". c #ED2F2F", +"+ c #EF2929", +"@ c #EF2C2B", +"# c #ED3030", +"$ c #EE2C2C", +"% c #EF6968", +"& c #EDD0CC", +"* c #EDD6D1", +"= c #EDCFCB", +"- c #EE5252", +"; c #EF3535", +"> c #EDCEC8", +", c #E4B6AE", +"' c #E1ABA2", +") c #E1ACA2", +"! c #EDCFCA", +"~ c #F28D8B", +"{ c #EF2D2D", +"] c #EF2E2E", +"^ c #EEA29F", +"/ c #EAC5BE", +"( c #E2ABA2", +"_ c #E6BBB3", +": c #EEC5C2", +"< c #F49491", +"[ c #F03F3E", +"} c #EE5857", +"| c #EDD1CC", +"1 c #E3AEA5", +"2 c #E2AAA1", +"3 c #E2ABA1", +"4 c #ECCEC9", +"5 c #F3A2A0", +"6 c #F59290", +"7 c #F05453", +"8 c #F02F2E", +"9 c #EFC3BF", +"0 c #E6BAB3", +"a c #E3A9A1", +"b c #EEC5C1", +"c c #F5908D", +"d c #F48C8A", +"e c #ED2E2E", +"f c #ED2C2C", +"g c #EF8B89", +"h c #EBC9C3", +"i c #E3A9A0", +"j c #E3AAA0", +"k c #EDCDC8", +"l c #F49F9D", +"m c #F58F8C", +"n c #F15F5E", +"o c #DB2B2B", +"p c #EF4242", +"q c #EDD1CB", +"r c #E5B0A8", +"s c #E4A8A0", +"t c #E7B9B2", +"u c #EFC4C0", +"v c #F68C8A", +"w c #F68A88", +"x c #EC302F", +"y c #F02E2E", +"z c #F0B3B0", +"A c #E9BEB6", +"B c #E5A89F", +"C c #E5A99F", +"D c #EDCEC9", +"E c #F49D9A", +"F c #F68B89", +"G c #F26463", +"H c #DC2B2B", +"I c #EE2A2A", +"J c #EDD5D1", +"K c #E6A8A1", +"L c #E5A79F", +"M c #E8B8B1", +"N c #F78886", +"O c #F68886", +"P c #EC3131", +"Q c #EDD2CD", +"R c #F59794", +"S c #F78785", +"T c #F46766", +"U c #E32C2C", +"V c #FB7575", +"W c #FA7776", +"X c #FA7877", +"Y c #FA7979", +"Z c #FA7A7A", +"` c #F97C7B", +" . c #F97D7C", +".. c #F97E7D", +"+. c #F97F7E", +"@. c #F88180", +"#. c #F88281", +"$. c #F88382", +"%. c #F88583", +"&. c #F78684", +"*. c #EC3636", +"=. c #FB7474", +"-. c #F56A68", +";. c #E52B2B", +">. c #EE2B2B", +",. c #F86363", +"'. c #F77D7C", +"). c #ED3636", +"!. c #A01B1B", +"~. c #ED2B2B", +"{. c #C82727", +" ", +" ", +" ", +" ", +" . + + + + + + + + + + @ # ", +" $ % & * * * * * * * * * = - ", +" ; > , ' ' ' ' ' ' ' ' ) ! ~ { ", +" ] ^ / ( ( ( ( ( ( ( ( ( _ : < [ ", +" } | 1 2 2 2 2 2 2 2 2 3 4 5 6 7 ", +" 8 9 0 a a a a a a a a a 0 b c d e ", +" f g h i i i i i i i i i j k l m n o ", +" p q r s s s s s s s s s t u v w x ", +" y z A B B B B B B B B B C D E F G H ", +" I J K L L L L L L L L L M 9 N O P ", +" + * * * * * * * * * * * Q R S T U ", +" + V W X Y Z ` ...+.@.#.$.%.&.*. ", +" + =.V W X Y Z ` ...+.@.#.$.-.;. ", +" >.,.=.V W X Y Z ` ...+.@.'.).!. ", +" ~.+ + + + + + + + + + + ~.{. ", +" ", +" ", +" ", +" ", +" "}; diff --git a/etc/images/artist-mode/fill.pbm b/etc/images/artist-mode/fill.pbm new file mode 100644 index 00000000000..7c387794951 Binary files /dev/null and b/etc/images/artist-mode/fill.pbm differ diff --git a/etc/images/artist-mode/fill.xpm b/etc/images/artist-mode/fill.xpm new file mode 100644 index 00000000000..3266c875a7c --- /dev/null +++ b/etc/images/artist-mode/fill.xpm @@ -0,0 +1,186 @@ +/* XPM */ +static char * fill_xpm[] = { +"24 24 159 2", +" c None", +". c #555753", +"+ c #585A56", +"@ c #595A57", +"# c #555653", +"$ c #5A5C59", +"% c #9FA09F", +"& c #A8A9A8", +"* c #595B57", +"= c #5C5F5B", +"- c #565854", +"; c #E3E3E2", +"> c #F2F2F2", +", c #F6F6F6", +"' c #676965", +") c #595B58", +"! c #626460", +"~ c #D2D3D2", +"{ c #DDDDDD", +"] c #CBCBCB", +"^ c #DFDFDF", +"/ c #CDCECD", +"( c #5A5C57", +"_ c #5F719F", +": c #596C9B", +"< c #586A9A", +"[ c #566485", +"} c #5C5F61", +"| c #B0B2B1", +"1 c #FAFAFA", +"2 c #F3F3F3", +"3 c #D1D2D1", +"4 c #CFCFCF", +"5 c #C9C9C9", +"6 c #F5F5F5", +"7 c #777976", +"8 c #5E709F", +"9 c #8B9FC4", +"0 c #A0BCE3", +"a c #8DB2E2", +"b c #6E92C2", +"c c #5C636E", +"d c #838684", +"e c #F4F4F4", +"f c #FDFDFD", +"g c #E9E9E9", +"h c #D6D6D6", +"i c #D3D3D3", +"j c #CCCCCC", +"k c #D7D7D7", +"l c #E0E0E0", +"m c #5B6D9C", +"n c #BACFEB", +"o c #A4C0E5", +"p c #677589", +"q c #666866", +"r c #D6D7D6", +"s c #FBFBFB", +"t c #F7F7F7", +"u c #FCFCFC", +"v c #EEEEEE", +"w c #858683", +"x c #575855", +"y c #838582", +"z c #D1D1D1", +"A c #D0D0D0", +"B c #C8C8C8", +"C c #F0F0F0", +"D c #838481", +"E c #C0D5F0", +"F c #A3BEE2", +"G c #585B5B", +"H c #E5E5E5", +"I c #ECECEC", +"J c #8D8E8B", +"K c #666865", +"L c #8A8B8A", +"M c #6E6F6D", +"N c #7E807D", +"O c #D4D4D4", +"P c #CDCDCD", +"Q c #EDEEED", +"R c #5D5F5B", +"S c #A6C3E9", +"T c #57637D", +"U c #90918E", +"V c #E4E4E4", +"W c #6D6E6B", +"X c #8B8C8B", +"Y c #BBBBBB", +"Z c #A3A4A3", +"` c #666864", +" . c #D8D8D8", +".. c #A8AAA7", +"+. c #A6C2E9", +"@. c #576999", +"#. c #5A5B58", +"$. c #E2E2E2", +"%. c #DCDCDC", +"&. c #EBEBEB", +"*. c #8D8E8C", +"=. c #6B6C6A", +"-. c #9FA09E", +";. c #747673", +">. c #898B88", +",. c #D5D5D5", +"'. c #A5C2E8", +"). c #7B7D7A", +"!. c #969895", +"~. c #727470", +"{. c #8C8D8B", +"]. c #E1E1E1", +"^. c #D9D9D9", +"/. c #D2D2D2", +"(. c #CACACA", +"_. c #BFC0BF", +":. c #5B5C58", +"<. c #DADADA", +"[. c #CECECE", +"}. c #5B5D58", +"|. c #A4C1E7", +"1. c #6D6F6C", +"2. c #F8F8F8", +"3. c #EFEFEF", +"4. c #FEFEFE", +"5. c #EAEAEA", +"6. c #E3E3E3", +"7. c #5A5C58", +"8. c #5D6F9E", +"9. c #A2B6D7", +"0. c #92ACD4", +"a. c #5A6D9D", +"b. c #595B56", +"c. c #C7C7C6", +"d. c #E7E7E7", +"e. c #EDEDED", +"f. c #C9CAC9", +"g. c #5F605C", +"h. c #5C6E9D", +"i. c #5B6D9D", +"j. c #646662", +"k. c #F9F9F9", +"l. c #797A77", +"m. c #5B5C59", +"n. c #B6B7B6", +"o. c #E8E8E8", +"p. c #A3A5A3", +"q. c #5D5F5C", +"r. c #F4F4F3", +"s. c #DBDBDB", +"t. c #CFCFCE", +"u. c #666763", +"v. c #535650", +"w. c #535652", +"x. c #838683", +"y. c #F6F7F6", +"z. c #E0E0DF", +"A. c #797A76", +"B. c #50554D", +" ", +" . . . . ", +" . . . . ", +" . + @ . ", +" # $ % & * . ", +" = - ; > , ' . ", +" ) ! ~ * { ] ^ / ( . ", +" _ : < < < [ } | 1 2 + 3 4 5 6 7 # ", +" 8 9 0 a b c d e f 2 g + h i j k l . ", +" m n o p q r s t u v w x y z A B C D ", +" < E F G H v ^ I s J K L M N O P z Q R ", +" < E S T U 6 h V > W X Y Z ` .z 5 g .. ", +" < E +.@.#.$.l %.&.*.=.-.;.>.%.,.P j e ! ", +" < E '.< ).t ,.$.I !.~.{.$.].^./.(.$._.* ", +" < E '.< :.,.$.<.g t s 2 I H { h [.P > }. ", +" < E |.< 1.2.O ].3.4.2.C g ].<.,.5.6.7. ", +" 8.9.0.a. b.c.H ^.d.6 u e e.H g 2.f.g. ", +" h.i. j.t O ^ e.u k.2 k.5.l.m. ", +" + n.o.k H e 4.k.p.R ", +" q.r.s.o.1 t.u.v. ", +" w.x.y.z.A.* ", +" - ( #.B. ", +" ", +" "}; diff --git a/etc/images/artist-mode/line.pbm b/etc/images/artist-mode/line.pbm new file mode 100644 index 00000000000..0952ab5e761 Binary files /dev/null and b/etc/images/artist-mode/line.pbm differ diff --git a/etc/images/artist-mode/line.xpm b/etc/images/artist-mode/line.xpm new file mode 100644 index 00000000000..f21cf5c50a8 --- /dev/null +++ b/etc/images/artist-mode/line.xpm @@ -0,0 +1,29 @@ +/* XPM */ +static char * line_xpm[] = { +"24 24 2 1", +" c None", +". c #204A88", +" ", +" . ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" ... ", +" . ", +" "}; diff --git a/etc/images/artist-mode/pen.pbm b/etc/images/artist-mode/pen.pbm new file mode 100644 index 00000000000..11a7865c9d8 Binary files /dev/null and b/etc/images/artist-mode/pen.pbm differ diff --git a/etc/images/artist-mode/pen.xpm b/etc/images/artist-mode/pen.xpm new file mode 100644 index 00000000000..e38835c12bd --- /dev/null +++ b/etc/images/artist-mode/pen.xpm @@ -0,0 +1,130 @@ +/* XPM */ +static char * pen_xpm[] = { +"24 24 103 2", +" c None", +". c #683907", +"+ c #764108", +"@ c #6B3B07", +"# c #673907", +"$ c #985B1B", +"% c #854A0A", +"& c #9C652C", +"* c #D48E45", +"= c #6B3D0D", +"- c #673908", +"; c #95632D", +"> c #E49745", +", c #996731", +"' c #885C2D", +") c #E79137", +"! c #B67836", +"~ c #71471B", +"{ c #7B5226", +"] c #D48839", +"^ c #BB6C19", +"/ c #8C653A", +"( c #693A09", +"_ c #764D1F", +": c #C1803B", +"< c #C97113", +"[ c #956430", +"} c #7C5021", +"| c #734719", +"1 c #AF763C", +"2 c #DB7C18", +"3 c #985916", +"4 c #876239", +"5 c #6A3B09", +"6 c #6E4213", +"7 c #9B6D3C", +"8 c #E8851D", +"9 c #A45B0D", +"0 c #785631", +"a c #86541E", +"b c #837868", +"c c #897862", +"d c #CB7B27", +"e c #B6640E", +"f c #7D5223", +"g c #976632", +"h c #8A8C88", +"i c #B2B3B1", +"j c #AEAFAC", +"k c #8A8A83", +"l c #845B2E", +"m c #966A3B", +"n c #70400D", +"o c #92948F", +"p c #F4F4F4", +"q c #E7E7E7", +"r c #ADADAC", +"s c #898982", +"t c #845A2B", +"u c #6D3C07", +"v c #8B8E89", +"w c #C8C8C7", +"x c #F9F9F9", +"y c #C6C6C6", +"z c #A3A3A3", +"A c #9FA19D", +"B c #868073", +"C c #8B8D88", +"D c #F6F6F6", +"E c #E0E0E0", +"F c #AFAFAF", +"G c #A6A7A5", +"H c #8C8E89", +"I c #898D88", +"J c #C1C2C0", +"K c #F8F8F8", +"L c #BBBBBB", +"M c #9E9F9D", +"N c #8B8C88", +"O c #1E1E1E", +"P c #3E3E3D", +"Q c #595958", +"R c #A2A2A2", +"S c #999A98", +"T c #161616", +"U c #757575", +"V c #848484", +"W c #3F3F3F", +"X c #202020", +"Y c #6E706B", +"Z c #000000", +"` c #1A1A1A", +" . c #929292", +".. c #484848", +"+. c #242424", +"@. c #101010", +"#. c #0D0D0D", +"$. c #2B2B2B", +"%. c #3B3B3B", +"&. c #303030", +"*. c #0A0A0A", +"=. c #141414", +"-. c #0E0E0E", +" ", +" . ", +" . + @ ", +" # $ % ", +" # & * = ", +" - ; > , # ", +" # ' ) ! ~ ", +" # { ] ^ / ( ", +" # _ : < [ } ", +" # | 1 2 3 4 5 ", +" 6 7 8 9 0 a @ ", +" b c d e f g @ ", +" h i j k l m n ", +" o p q r s t u ", +" v w x y z A B ", +" C D E F G H ", +" I J K L M N ", +" O P Q R S C ", +" T U V W X Y ", +" Z ` .z ..+.@. ", +" Z Z #.$.%.&.T ", +" Z Z Z *.=.-.Z ", +" Z Z Z Z Z Z Z ", +" "}; diff --git a/etc/images/artist-mode/poly-line.pbm b/etc/images/artist-mode/poly-line.pbm new file mode 100644 index 00000000000..a06f19e95a9 Binary files /dev/null and b/etc/images/artist-mode/poly-line.pbm differ diff --git a/etc/images/artist-mode/poly-line.xpm b/etc/images/artist-mode/poly-line.xpm new file mode 100644 index 00000000000..2e0566d76bd --- /dev/null +++ b/etc/images/artist-mode/poly-line.xpm @@ -0,0 +1,29 @@ +/* XPM */ +static char * poly_line_xpm[] = { +"24 24 2 1", +" c None", +". c #204A88", +" ", +" ... ", +" ........ ", +" .. ........... ", +" .. ........... ", +" .. ........ ", +" . ... ", +" .. .. ", +" .. . ", +" .. .. ", +" .. .. ", +" .. . ", +" . .. .. ", +" . ..... .. ", +" . . .... . ", +" . .. ..... ", +" .. . .. ", +" .. .. ", +" ... ", +" .. ", +" .. ", +" . ", +" ", +" "}; diff --git a/etc/images/artist-mode/rectangle.pbm b/etc/images/artist-mode/rectangle.pbm new file mode 100644 index 00000000000..fb1ad35e52a Binary files /dev/null and b/etc/images/artist-mode/rectangle.pbm differ diff --git a/etc/images/artist-mode/rectangle.xpm b/etc/images/artist-mode/rectangle.xpm new file mode 100644 index 00000000000..2271700f4ea --- /dev/null +++ b/etc/images/artist-mode/rectangle.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char * rectangle_xpm[] = { +"24 24 3 1", +" c None", +". c #204A88", +"+ c #719FCF", +" ", +" ", +" ", +" ", +" ", +" ...................... ", +" ...................... ", +" ..++++++++++++++++++.. ", +" ..++++++++++++++++++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++++++++++++++++++.. ", +" ..++++++++++++++++++.. ", +" ...................... ", +" ...................... ", +" ", +" ", +" ", +" ", +" "}; diff --git a/etc/images/artist-mode/spray.pbm b/etc/images/artist-mode/spray.pbm new file mode 100644 index 00000000000..72935a12e5e Binary files /dev/null and b/etc/images/artist-mode/spray.pbm differ diff --git a/etc/images/artist-mode/spray.xpm b/etc/images/artist-mode/spray.xpm new file mode 100644 index 00000000000..59aaccf1033 --- /dev/null +++ b/etc/images/artist-mode/spray.xpm @@ -0,0 +1,139 @@ +/* XPM */ +static char * spray_xpm[] = { +"24 24 112 2", +" c None", +". c #2D3335", +"+ c #2F3235", +"@ c #3F4444", +"# c #33383A", +"$ c #2D3336", +"% c #414646", +"& c #6B6D6A", +"* c #303638", +"= c #2F3536", +"- c #6B6D69", +"; c #313739", +"> c #3B413F", +", c #2D3435", +"' c #6A6C69", +") c #323739", +"! c #2E3436", +"~ c #2C3434", +"{ c #696C69", +"] c #3C4140", +"^ c #2D3537", +"/ c #353A3A", +"( c #696B68", +"_ c #323839", +": c #2D3434", +"< c #2F3436", +"[ c #4B4E4B", +"} c #393F3F", +"| c #444949", +"1 c #545651", +"2 c #838482", +"3 c #DADADA", +"4 c #33393A", +"5 c #545752", +"6 c #EEEEEE", +"7 c #DDDDDD", +"8 c #999A98", +"9 c #3B3F40", +"0 c #F5F5F5", +"a c #E4E4E4", +"b c #B4B5B3", +"c c #AEAEAD", +"d c #4E504E", +"e c #545652", +"f c #80827F", +"g c #F9F9F9", +"h c #EBEBEB", +"i c #A2A3A1", +"j c #C2C2C2", +"k c #C1C1C1", +"l c #747474", +"m c #313531", +"n c #1A1A1A", +"o c #252525", +"p c #555652", +"q c #D2D3D2", +"r c #F1F1F1", +"s c #A3A4A2", +"t c #565753", +"u c #6A6D69", +"v c #B9B9B9", +"w c #ABABAB", +"x c #313231", +"y c #303030", +"z c #7E7E7E", +"A c #585855", +"B c #777C73", +"C c #C4C7C1", +"D c #A6A7A5", +"E c #565754", +"F c #555753", +"G c #5D5F5C", +"H c #A7A7A7", +"I c #797979", +"J c #121212", +"K c #131313", +"L c #898989", +"M c #545653", +"N c #D0D0CE", +"O c #ABAEA7", +"P c #60635E", +"Q c #3D3F3A", +"R c #494B49", +"S c #949494", +"T c #636363", +"U c #3E3E3E", +"V c #7D7D7D", +"W c #757575", +"X c #555551", +"Y c #5C5F59", +"Z c #222522", +"` c #393939", +" . c #8E8E8E", +".. c #919191", +"+. c #818181", +"@. c #292929", +"#. c #365990", +"$. c #52524E", +"%. c #080D08", +"&. c #050505", +"*. c #101010", +"=. c #1B1B1B", +"-. c #385A90", +";. c #375A90", +">. c #375A8F", +",. c #365A90", +"'. c #385990", +"). c #37588F", +"!. c #375A8D", +"~. c #37598E", +"{. c #37568D", +"]. c #355A8D", +" ", +" ", +" . + ", +" . @ # . ", +" $ % & * = ", +" $ % - ; > , ", +" . % ' ) > ! ", +" $ ~ . % { ) ] ! ", +" , $ ^ / % ( _ > ! ", +" : < [ } | _ > , ", +" 1 2 3 % 4 ! ", +" 5 2 6 7 8 9 ", +" 5 2 0 a b c d ", +" e f g h i j k l m n o ", +" p q r s t u v w x y z ", +" A B C D E F G H I J K L ", +" M N O P Q R S T U V W ", +" X ' Y Z ` ...+.@. ", +" #. $. %.&.*.=. ", +" -.;.>. ", +" ,.-.'.). ", +" !.~.~. ", +" {.]. ", +" "}; diff --git a/etc/images/artist-mode/square.pbm b/etc/images/artist-mode/square.pbm new file mode 100644 index 00000000000..f3761b7dc07 Binary files /dev/null and b/etc/images/artist-mode/square.pbm differ diff --git a/etc/images/artist-mode/square.xpm b/etc/images/artist-mode/square.xpm new file mode 100644 index 00000000000..c0da53b9153 --- /dev/null +++ b/etc/images/artist-mode/square.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char * square_xpm[] = { +"24 24 3 1", +" c None", +". c #204A88", +"+ c #719FCF", +" ", +" ", +" ..................... ", +" ..................... ", +" ..+++++++++++++++++.. ", +" ..+++++++++++++++++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..++ ++.. ", +" ..+++++++++++++++++.. ", +" ..+++++++++++++++++.. ", +" ..................... ", +" ..................... ", +" "}; diff --git a/etc/images/artist-mode/text.pbm b/etc/images/artist-mode/text.pbm new file mode 100644 index 00000000000..7a4110a977f Binary files /dev/null and b/etc/images/artist-mode/text.pbm differ diff --git a/etc/images/artist-mode/text.xpm b/etc/images/artist-mode/text.xpm new file mode 100644 index 00000000000..84f63b9113e --- /dev/null +++ b/etc/images/artist-mode/text.xpm @@ -0,0 +1,164 @@ +/* XPM */ +static char * text_xpm[] = { +"24 24 137 2", +" c None", +". c #9C9C9C", +"+ c #585858", +"@ c #828282", +"# c #BEBEBE", +"$ c #BFBFBF", +"% c #272727", +"& c #393939", +"* c #4E4E4E", +"= c #4C4C4C", +"- c #4B4B4B", +"; c #444444", +"> c #070707", +", c #B3B3B3", +"' c #A0A0A0", +") c #121212", +"! c #949494", +"~ c #808080", +"{ c #7D7D7D", +"] c #7A7A7A", +"^ c #787878", +"/ c #212121", +"( c #717171", +"_ c #BDBDBD", +": c #505050", +"< c #838383", +"[ c #545454", +"} c #515151", +"| c #4D4D4D", +"1 c #4A4A4A", +"2 c #5A5A5A", +"3 c #363636", +"4 c #252525", +"5 c #BABABA", +"6 c #0E0E0E", +"7 c #818181", +"8 c #656565", +"9 c #474747", +"0 c #3F3F3F", +"a c #050505", +"b c #9F9F9F", +"c c #7B7B7B", +"d c #2C2C2C", +"e c #8B8B8B", +"f c #484848", +"g c #373737", +"h c #353535", +"i c #3E3E3E", +"j c #3B3B3B", +"k c #161616", +"l c #535353", +"m c #2D2D2D", +"n c #626262", +"o c #6C6C6C", +"p c #464646", +"q c #424242", +"r c #141414", +"s c #131313", +"t c #383838", +"u c #313131", +"v c #222222", +"w c #0D0D0D", +"x c #A3A3A3", +"y c #0C0C0C", +"z c #878787", +"A c #404040", +"B c #030303", +"C c #040404", +"D c #2A2A2A", +"E c #2F2F2F", +"F c #2B2B2B", +"G c #282828", +"H c #0A0A0A", +"I c #575757", +"J c #434343", +"K c #767676", +"L c #3A3A3A", +"M c #1D1D1D", +"N c #333333", +"O c #3D3D3D", +"P c #292929", +"Q c #262626", +"R c #101010", +"S c #0F0F0F", +"T c #707070", +"U c #343434", +"V c #7C7C7C", +"W c #888888", +"X c #080808", +"Y c #202020", +"Z c #1F1F1F", +"` c #1B1B1B", +" . c #ADADAD", +".. c #232323", +"+. c #7F7F7F", +"@. c #323232", +"#. c #242424", +"$. c #B1B1B1", +"%. c #151515", +"&. c #676767", +"*. c #C0C0C0", +"=. c #5F5F5F", +"-. c #616161", +";. c #1A1A1A", +">. c #A9A9A9", +",. c #777777", +"'. c #ABABAB", +"). c #020202", +"!. c #1C1C1C", +"~. c #1E1E1E", +"{. c #969696", +"]. c #696969", +"^. c #010101", +"/. c #000000", +"(. c #606060", +"_. c #595959", +":. c #181818", +"<. c #B6B6B6", +"[. c #8A8A8A", +"}. c #060606", +"|. c #111111", +"1. c #494949", +"2. c #6B6B6B", +"3. c #909090", +"4. c #A6A6A6", +"5. c #666666", +"6. c #B5B5B5", +"7. c #5E5E5E", +"8. c #191919", +"9. c #686868", +"0. c #979797", +"a. c #090909", +"b. c #B4B4B4", +"c. c #B9B9B9", +"d. c #8C8C8C", +"e. c #8E8E8E", +"f. c #B8B8B8", +" ", +" ", +" . + + + + + + @ # ", +" $ % & * * = - ; > , ", +" ' ) ! ~ { ] ^ ~ / ( ", +" _ : : < [ } | 1 2 3 4 $ ", +" 5 6 7 8 * - 9 ; ; 0 a b ", +" c d e | f g h i j g k l ", +" _ m n o p q r s t h u v w 5 ", +" x y z | A h B C D E F G H < ", +" I J K i L M N O k P Q v R 3 $ ", +" 5 S T [ t U w V W X v Y Z ` B . ", +" < ..+.g @.#.> $. r %.Z Z Z 6 &. ", +" *.N } =.E d s p -.S Z Z Z r ;.$ ", +" >.X ,.j P 4 > < '.'.. ).!.Z Z ~.a {. ", +" =.g ].% #.` ^./././././.%.Z Z Z R 1 ", +" _ r (.O / Z 3 _._._._._._.; Z Z Z :.X <. ", +" [.:.=./ Z Z / % % % % % % v Z Z Z Z y V ", +" & O j Z Z ) }.}.}.}.}.}.}.}.S Z Z Z |.d $ ", +" .}.1.#.Z Z y 2.{.{.{.{.{.{.3.).!.Z Z !.B 4. ", +" 5.#.t Z Z ;.C 6. # Q ) Z Z Z 6 7. ", +"$ 8.d P Z Z |.N 9.R Z Z Z %.s # ", +"0./.R S S S }.,. x ).H H H a./.{.", +"b.@ +.+.+.+.@ c. {.d.d.d.d.e.f."}; diff --git a/lisp/textmodes/artist.el b/lisp/textmodes/artist.el index f01e636e981..7c44cca5014 100644 --- a/lisp/textmodes/artist.el +++ b/lisp/textmodes/artist.el @@ -567,6 +567,63 @@ This variable is initialized by the `artist-make-prev-next-op-alist' function.") ["Characters for Spray" artist-select-spray-chars :help "Choose characters for sprayed by the spray-can"])) +(defvar artist-tool-bar-map + (let ((map (make-sparse-keymap))) + ;; Tools + (tool-bar-local-item "artist-mode/pen" + #'artist-select-op-pen-line + #'artist-select-op-pen-line + map :help "Use pen") + (tool-bar-local-item "artist-mode/spray" + #'artist-select-op-spray-can + #'artist-select-op-spray-can + map :help "Use spray") + (tool-bar-local-item "artist-mode/eraser" + #'artist-select-op-erase-char + #'artist-select-op-erase-char + map :help "Use eraser") + (tool-bar-local-item "artist-mode/fill" + #'artist-select-op-flood-fill + #'artist-select-op-flood-fill + map :help "Fill") + (tool-bar-local-item "artist-mode/text" + #'artist-select-op-text-overwrite + #'artist-select-op-text-overwrite + map :help "Insert Figlet Text (figlet must be installed)") + (define-key-after map [separator-1] menu-bar-separator) + ;; Shapes + (tool-bar-local-item "artist-mode/line" + #'artist-select-op-straight-line + #'artist-select-op-straight-line + map :help "Draw straight line") + (tool-bar-local-item "artist-mode/ellipse" + #'artist-select-op-ellipse + #'artist-select-op-ellipse + map :help "Draw ellipse") + (tool-bar-local-item "artist-mode/square" + #'artist-select-op-square + #'artist-select-op-square + map :help "Draw square") + (tool-bar-local-item "artist-mode/rectangle" + #'artist-select-op-rectangle + #'artist-select-op-rectangle + map :help "Draw rectangle") + (tool-bar-local-item "artist-mode/poly-line" + #'artist-select-op-poly-line + #'artist-select-op-poly-line + map :help "Draw poly lines") + (define-key-after map [separator-2] menu-bar-separator) + ;; Configurations + (tool-bar-local-item "artist-mode/char-to-fill" + #'artist-select-fill-char + #'artist-select-fill-char + map :help "Change current fill character") + (tool-bar-local-item "artist-mode/char-for-spray" + #'artist-select-spray-chars + #'artist-select-spray-chars + map :help "Change current spray characters") + map)) + (defvar artist-replacement-table (make-vector 256 0) "Replacement table for `artist-replace-char'.") @@ -1367,6 +1424,7 @@ Keymap summary (t ;; Turn mode on (artist-mode-init) + (setq-local tool-bar-map artist-tool-bar-map) (let* ((font (face-attribute 'default :font)) (spacing-prop (if (fontp font) (font-get font :spacing) @@ -1414,7 +1472,8 @@ Keymap summary "Exit Artist mode. This will call the hook `artist-mode-hook'." (if (and artist-picture-compatibility (eq major-mode 'picture-mode)) (picture-mode-exit)) - (kill-local-variable 'next-line-add-newlines)) + (kill-local-variable 'next-line-add-newlines) + (kill-local-variable 'tool-bar-map)) (defun artist-mode-off () "Turn Artist mode off." commit f13287fde0d0900fe834cda9df77a072dd35d685 Author: Eli Zaretskii Date: Sat May 23 13:27:49 2026 +0300 Revert "sh-script: Mark + and * as punctuation rather than a symbol constituent" This reverts commit b3c0aee42b086af4b3c6e26da1a5d81490b6128b. It caused regressions in 'sh-script', see bug#80794 and bug#80854. diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index ffa8407b9bc..8479c3cfd9a 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el @@ -406,8 +406,6 @@ name symbol." ;; to work fine. This is needed so that dabbrev-expand ;; $VARNAME works. ?$ "'" - ?* "." - ?+ "." ?! "." ?% "." ?: "." commit 70b79b3ed8d04aa852837177636061dfc43e9b0b Author: Richard Lawrence Date: Sun May 17 12:54:24 2026 +0200 Rename `icalendar-recur' type and related functions More context in Bug#80786 and: https://lists.gnu.org/archive/html/emacs-orgmode/2026-03/msg00286.html `icalendar-recur' as a type name for RRULE values was confusing and made the accessors for this type difficult to discover, because `icalendar-recur-' is also used as a prefix in icalendar-recur.el. This change renames the `icalendar-recur' type to `icalendar-rrule-value' and renames the accessor functions for these values appropriately. * lisp/calendar/icalendar-parser.el: Rename symbols as follows: (icalendar-recur): `icalendar-rrule-value' (icalendar-read-recur-rule-part): `icalendar-read-rrule-part' (icalendar-print-recur-rule-part): `icalendar-print-rrule-part' (icalendar-recur-rule-part): `icalendar-rrule-part' (icalendar-read-recur): `icalendar-read-rrule-value' (icalendar-print-recur): `icalendar-print-rrule-value' (icalendar--recur-value-types): `icalendar--rrule-value-types' (icalendar-recur-value-p): `icalendar-rrule-value-p' (icalendar-recur-freq): `icalendar-rrule-freq' (icalendar-recur-interval-size): `icalendar-rrule-interval-size' (icalendar-recur-until): `icalendar-rrule-until' (icalendar-recur-count): `icalendar-rrule-count' (icalendar-recur-weekstart): `icalendar-rrule-weekstart' (icalendar-recur-by*): `icalendar-rrule-by*'. (icalendar-rrule): (icalendar-index-insert): (icalendar-index-get): Update references. * lisp/calendar/icalendar-recur.el (icalendar-recur-find-interval): (icalendar-recur-nth-interval): (icalendar-recur-next-interval): (icalendar-recur-previous-interval): (icalendar-recur-refine-from-clauses): (icalendar-recur-recurrences-in-interval): (icalendar-recur-recurrences-in-window): (icalendar-recur-recurrences-to-count): (icalendar-recur-tz-observance-on): Update references. * lisp/calendar/diary-icalendar.el: Update references. * lisp/calendar/icalendar-shortdoc.el (icalendar): Update shortdoc examples. * lisp/gnus/gnus-icalendar.el: Update references. * test/lisp/calendar/diary-icalendar-tests.el: * test/lisp/calendar/icalendar-parser-tests.el: * test/lisp/calendar/icalendar-recur-tests.el: Update references in tests. diff --git a/lisp/calendar/diary-icalendar.el b/lisp/calendar/diary-icalendar.el index bc58e7b5924..9604dce8e4e 100644 --- a/lisp/calendar/diary-icalendar.el +++ b/lisp/calendar/diary-icalendar.el @@ -2690,12 +2690,12 @@ recurrence rule values in these nodes are adjusted NDAYS forward." :duration (ical:period-dur-value value))) (t (ical:date/time-add value :day ndays))))))) (ical:rrule - (let ((mdays (ical:recur-by* 'BYMONTHDAY value)) - (ydays (ical:recur-by* 'BYYEARDAY value)) - (dows (ical:recur-by* 'BYDAY value)) + (let ((mdays (ical:rrule-by* 'BYMONTHDAY value)) + (ydays (ical:rrule-by* 'BYYEARDAY value)) + (dows (ical:rrule-by* 'BYDAY value)) (bad-clause - (cond ((ical:recur-by* 'BYSETPOS value) 'BYSETPOS) - ((ical:recur-by* 'BYWEEKNO value) 'BYWEEKNO)))) + (cond ((ical:rrule-by* 'BYSETPOS value) 'BYSETPOS) + ((ical:rrule-by* 'BYWEEKNO value) 'BYWEEKNO)))) ;; We can't reliably subtract days in the following cases, so bail: (when (< 28 ndays) (di:signal-export-error @@ -2970,12 +2970,12 @@ nil, if MONTHS, DAYS and YEARS are all integers)." rdates (seq-remove (apply-partially #'equal dtstart) rdates)))) ;; Return the pair of nodes (DTSTART RRULE) or (DTSTART RDATE): - (let* ((recur-value + (let* ((rrule-value (delq nil `((FREQ ,freq) ,(when bymonth (list 'BYMONTH bymonth)) ,(when bymonthday (list 'BYMONTHDAY bymonthday))))) - (rrule-node (when freq (ical:make-property ical:rrule recur-value))) + (rrule-node (when freq (ical:make-property ical:rrule rrule-value))) (rdate-node (when rdates (ical:make-property ical:rdate rdates (ical:valuetypeparam rdate-type)))) @@ -3548,7 +3548,7 @@ values (of the same type as START)." (interval (icr:find-interval date start rule))) (cl-typecase start (ical:date - (if (ical:recur-count rule) + (if (ical:rrule-count rule) (when (member date (icr:recurrences-to-count vevent)) entry) (when (member date (icr:recurrences-in-interval interval vevent)) @@ -3581,7 +3581,7 @@ values (of the same type as START)." (ical:date/time-add-duration start duration)) (di:format-time-as-local start))) (date-entry (concat entry-time " " entry))) - (when (memq (ical:recur-freq date-rule) '(HOURLY MINUTELY SECONDLY)) + (when (memq (ical:rrule-freq date-rule) '(HOURLY MINUTELY SECONDLY)) (setf (alist-get 'FREQ date-rule) 'DAILY) (setf (alist-get 'INTERVAL date-rule) 1) (setf (alist-get 'BYHOUR date-rule nil t) nil) diff --git a/lisp/calendar/icalendar-parser.el b/lisp/calendar/icalendar-parser.el index 93293f19cbf..c6100c828e4 100644 --- a/lisp/calendar/icalendar-parser.el +++ b/lisp/calendar/icalendar-parser.el @@ -1352,9 +1352,9 @@ See `icalendar-read-weekdaynum' for the format of VAL." ;; number alone just stands for a day: (car (rassq val ical:weekday-numbers)))) -(defun ical:read-recur-rule-part (s) - "Read an `icalendar-recur-rule-part' from string S. -S should have been matched against `icalendar-recur-rule-part'. +(defun ical:read-rrule-part (s) + "Read an `icalendar-rrule-part' from string S. +S should have been matched against `icalendar-rrule-part'. The return value is a list (KEYWORD VALUE), where VALUE may itself be a list, depending on the values allowed by KEYWORD." ;; TODO: this smells like a design flaw. Silence the byte compiler for now. @@ -1376,7 +1376,7 @@ itself be a list, depending on the values allowed by KEYWORD." (rx ical:weekdaynum) ",")) (WKST (cdr (assoc values ical:weekday-numbers))))))) -(defun ical:print-recur-rule-part (part) +(defun ical:print-rrule-part (part) "Serialize recur rule part PART to a string." (let ((keyword (car part)) (values (cadr part)) @@ -1398,7 +1398,7 @@ itself be a list, depending on the values allowed by KEYWORD." (concat (symbol-name keyword) "=" values-str))) -(rx-define ical:recur-rule-part +(rx-define ical:rrule-part ;; Group 11: keyword ;; Group 12: value(s) (or (seq (group-n 11 "FREQ") "=" (group-n 12 ical:freq)) @@ -1423,14 +1423,12 @@ itself be a list, depending on the values allowed by KEYWORD." (ical:comma-list ical:yeardaynum))) (seq (group-n 11 "WKST") "=" (group-n 12 ical:weekday)))) -(defun ical:read-recur (s) +(defun ical:read-rrule-value (s) "Read a recurrence rule value from string S. S should be a match against rx `icalendar-recur'." - ;; TODO: let's switch to keywords and a plist, so we can more easily - ;; write these clauses also in diary sexp entries without so many parens - (ical:read-list-with #'ical:read-recur-rule-part s (rx ical:recur-rule-part) ";")) + (ical:read-list-with #'ical:read-rrule-part s (rx ical:rrule-part) ";")) -(defun ical:print-recur (val) +(defun ical:print-rrule-value (val) "Serialize a recurrence rule value VAL to a string." ;; RFC5545 sec. 3.3.10: "to ensure backward compatibility with ;; applications that pre-date this revision of iCalendar the @@ -1438,15 +1436,15 @@ S should be a match against rx `icalendar-recur'." ;; RECUR value." (string-join (cons - (ical:print-recur-rule-part (assq 'FREQ val)) - (mapcar #'ical:print-recur-rule-part + (ical:print-rrule-part (assq 'FREQ val)) + (mapcar #'ical:print-rrule-part (seq-filter (lambda (part) (not (eq 'FREQ (car part)))) val))) ";")) -(defconst ical:-recur-value-types +(defconst ical:-rrule-value-types ;; `list-of' is not a cl-type specifier, just a symbol here; it is - ;; handled specially when checking types in `ical:recur-value-p': + ;; handled specially when checking types in `ical:rrule-value-p': '(FREQ (member YEARLY MONTHLY WEEKLY DAILY HOURLY MINUTELY SECONDLY) UNTIL (or ical:date-time ical:date) COUNT (integer 1 *) @@ -1470,7 +1468,7 @@ DAYNO must be in [0..6] and OFFSET in [-53..53], excluding 0." (cl-typep (car val) '(integer 0 6)) (cl-typep (cdr val) '(or (integer -53 -1) (integer 1 53))))) -(defun ical:recur-value-p (vals) +(defun ical:rrule-value-p (vals) "Return non-nil if VALS is an iCalendar recurrence rule value." (and (listp vals) ;; FREQ is always required: @@ -1487,11 +1485,11 @@ DAYNO must be in [0..6] and OFFSET in [-53..53], excluding 0." (assq 'BYHOUR vals) (assq 'BYMINUTE vals) (assq 'BYSECOND vals)) - (let ((freq (ical:recur-freq vals)) - (byday (ical:recur-by* 'BYDAY vals)) - (byweekno (ical:recur-by* 'BYWEEKNO vals)) - (bymonthday (ical:recur-by* 'BYMONTHDAY vals)) - (byyearday (ical:recur-by* 'BYYEARDAY vals))) + (let ((freq (ical:rrule-freq vals)) + (byday (ical:rrule-by* 'BYDAY vals)) + (byweekno (ical:rrule-by* 'BYWEEKNO vals)) + (bymonthday (ical:rrule-by* 'BYMONTHDAY vals)) + (byyearday (ical:rrule-by* 'BYYEARDAY vals))) (and ;; "The BYDAY rule part MUST NOT be specified with a numeric ;; value when the FREQ rule part is not set to MONTHLY or @@ -1518,7 +1516,7 @@ DAYNO must be in [0..6] and OFFSET in [-53..53], excluding 0." (when (consp kv) (let* ((keyword (car kv)) (val (cadr kv)) - (type (plist-get ical:-recur-value-types keyword))) + (type (plist-get ical:-rrule-value-types keyword))) (and keyword val type (if (and (consp type) (eq (car type) 'list-of)) @@ -1526,7 +1524,14 @@ DAYNO must be in [0..6] and OFFSET in [-53..53], excluding 0." (cl-typep val type)))))) vals))) -(ical:define-type ical:recur "RECUR" +(ical:define-type ical:rrule-value + ;; Renamed from "ical:recur", which turns out to + ;; produce confusing names downstream. Thus I've + ;; deviated from the standard here, and call + ;; `ical:rrule-value' what the standard calls a RECUR + ;; value. (`ical:rrule' is not available because that + ;; names the *property* containing such a value.) + "RECUR" "Type for Recurrence Rule values. When printed, a recurrence rule value looks like @@ -1587,39 +1592,39 @@ Some examples: Notice that singleton values are still wrapped in a list when the KEY accepts a list of values, but not when the KEY always has a single (e.g. integer) value." - '(satisfies ical:recur-value-p) - (ical:semicolon-list ical:recur-rule-part) - :reader ical:read-recur - :printer ical:print-recur + '(satisfies ical:rrule-value-p) + (ical:semicolon-list ical:rrule-part) + :reader ical:read-rrule-value + :printer ical:print-rrule-value :link "https://www.rfc-editor.org/rfc/rfc5545#section-3.3.10") -(defun ical:recur-freq (recur-value) - "Return the frequency in RECUR-VALUE." - (car (alist-get 'FREQ recur-value))) +(defun ical:rrule-freq (rrule) + "Return the frequency in RRULE." + (car (alist-get 'FREQ rrule))) -(defun ical:recur-interval-size (recur-value) - "Return the interval size in RECUR-VALUE, or the default of 1." - (or (car (alist-get 'INTERVAL recur-value)) 1)) +(defun ical:rrule-interval-size (rrule) + "Return the interval size in RRULE, or the default of 1." + (or (car (alist-get 'INTERVAL rrule)) 1)) -(defun ical:recur-until (recur-value) - "Return the UNTIL date(-time) in RECUR-VALUE." - (car (alist-get 'UNTIL recur-value))) +(defun ical:rrule-until (rrule) + "Return the UNTIL date(-time) in RRULE." + (car (alist-get 'UNTIL rrule))) -(defun ical:recur-count (recur-value) - "Return the COUNT in RECUR-VALUE." - (car (alist-get 'COUNT recur-value))) +(defun ical:rrule-count (rrule) + "Return the COUNT in RRULE." + (car (alist-get 'COUNT rrule))) -(defun ical:recur-weekstart (recur-value) - "Return the weekday which starts the work week in RECUR-VALUE. -If no starting weekday is specified in RECUR-VALUE, returns the default, +(defun ical:rrule-weekstart (rrule) + "Return the weekday which starts the work week in RRULE. +If no starting weekday is specified in RRULE, returns the default, 1 (= Monday)." - (or (car (alist-get 'WKST recur-value)) 1)) + (or (car (alist-get 'WKST rrule)) 1)) -(defun ical:recur-by* (byunit recur-value) - "Return the values in the BYUNIT clause in RECUR-VALUE. +(defun ical:rrule-by* (byunit rrule) + "Return the values in the BYUNIT clause in RRULE. BYUNIT should be a symbol: \\='BYMONTH, \\='BYDAY, etc. -See `icalendar-recur' for all the possible BYUNIT values." - (car (alist-get byunit recur-value))) +See `icalendar-rrule-value' for all the possible BYUNIT values." + (car (alist-get byunit rrule))) ;;;; 3.3.11 Text (rx-define ical:escaped-char @@ -3272,7 +3277,7 @@ and times on which an `icalendar-vevent', `icalendar-todo', `icalendar-daylight' component recurs. Together with the `icalendar-dtstart', `icalendar-rdate', and `icalendar-exdate' properties, it defines the recurrence set of the component." - ical:recur + ical:rrule-value ;; TODO: faces for subexpressions? :child-spec (:zero-or-more (ical:otherparam)) :link "https://www.rfc-editor.org/rfc/rfc5545#section-3.8.5.3") @@ -4567,7 +4572,7 @@ which see." (ical:dtend :first dtend-node :value dtend) (ical:due :value due) (ical:duration :value duration) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:rdate :all rdate-nodes) (ical:exdate :all exdate-nodes) (ical:uid :value uid)) @@ -4592,7 +4597,7 @@ which see." ;; If the component has an RRULE that specifies a fixed number ;; of recurrences, compute them now and index them for each date ;; in each recurrence: - ((and recur-value (ical:recur-count recur-value)) + ((and rrule (ical:rrule-count rrule)) (let* ((tz (gethash (ical:with-param-of dtstart-node 'ical:tzidparam) tzid-index)) (recs (cons dtstart (icr:recurrences-to-count component tz)))) @@ -4605,7 +4610,7 @@ which see." (list (ical:date/time-to-date (ical:date/time-to-local rec)))))))))) ;; Same with RDATEs when there's no RRULE: - ((and rdates (not recur-value)) + ((and rdates (not rrule)) (dolist (rec (cons dtstart rdates)) (unless (or (cl-typep rec 'ical:period) (member rec exdates)) (let ((end-time @@ -4624,7 +4629,7 @@ which see." (setq dates (append dates (ical:dates-until start end t))))))) ;; A non-recurring event also gets an index entry for each date ;; until its end time: - ((not recur-value) + ((not rrule) (let ((end-time (or dtend due (when duration @@ -4703,13 +4708,13 @@ Only one keyword argument can be queried at a time." (dolist (component recurring) (ical:with-component component ((ical:dtstart :first dtstart-node :value dtstart) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:rdate :all rdate-nodes) (ical:duration :value duration)) (unless (ical:date/time<= date dtstart) (let* ((tz (ical:with-param-of dtstart-node 'ical:tzidparam nil (gethash value (plist-get index :bytzid)))) - (int (icr:find-interval date dtstart recur-value tz)) + (int (icr:find-interval date dtstart rrule tz)) (recs (icr:recurrences-in-interval int component tz))) (catch 'found (dolist (rec recs) diff --git a/lisp/calendar/icalendar-recur.el b/lisp/calendar/icalendar-recur.el index fcebbc9c6f0..b6c766962c9 100644 --- a/lisp/calendar/icalendar-recur.el +++ b/lisp/calendar/icalendar-recur.el @@ -451,29 +451,29 @@ See `icalendar-recur-find-interval' for arguments' meanings." ;; Return the bounds: (icr:make-interval low high next-low))) -(defun icr:find-interval (target dtstart recur-value &optional vtimezone) +(defun icr:find-interval (target dtstart rrule &optional vtimezone) "Return the recurrence interval around TARGET. TARGET and DTSTART should be `icalendar-date' or `icalendar-date-time' -values. RECUR-VALUE should be an `icalendar-recur'. +values. RRULE should be an `icalendar-recur'. The returned value is an interval [LOW HIGH NEXT-LOW] which represents the lower and upper bounds of a recurrence interval around TARGET. For some N, LOW is equal to START + N*INTERVALSIZE units, HIGH is equal to START + (N+1)*INTERVALSIZE units, and LOW <= TARGET < HIGH. -START here is a time derived from DTSTART depending on RECUR-VALUE's +START here is a time derived from DTSTART depending on RRULE's FREQ part: the first day of the year for a \\='YEARLY rule, first day of the month for a \\='MONTHLY rule, etc. -RECUR-VALUE's interval determines INTERVALSIZE, and its frequency +RRULE's interval determines INTERVALSIZE, and its frequency determines the units: a month for \\='MONTHLY, etc. If VTIMEZONE is provided, it is used to set time zone information in the returned interval bounds. Otherwise, the bounds contain no time zone information and represent floating local times." - (let ((freq (ical:recur-freq recur-value)) - (intsize (ical:recur-interval-size recur-value)) - (weekstart (ical:recur-weekstart recur-value))) + (let ((freq (ical:rrule-freq rrule)) + (intsize (ical:rrule-interval-size rrule)) + (weekstart (ical:rrule-weekstart rrule))) (cl-case freq (SECONDLY (icr:find-secondly-interval target dtstart intsize vtimezone)) (MINUTELY (icr:find-minutely-interval target dtstart intsize vtimezone)) @@ -484,22 +484,22 @@ information and represent floating local times." (MONTHLY (icr:find-monthly-interval target dtstart intsize vtimezone)) (YEARLY (icr:find-yearly-interval target dtstart intsize vtimezone))))) -(defun icr:nth-interval (n dtstart recur-value &optional vtimezone) +(defun icr:nth-interval (n dtstart rrule &optional vtimezone) "Return the Nth recurrence interval after DTSTART. The returned value is an interval [LOW HIGH NEXT-LOW] which is the Nth recurrence interval after DTSTART. LOW is equal to START + N*INTERVALSIZE units, HIGH is equal to START + (N+1)*INTERVALSIZE units, and LOW <= TARGET < HIGH. START here is a time derived from DTSTART -depending on RECUR-VALUE's FREQ part: the first day of the year for a +depending on RRULE's FREQ part: the first day of the year for a \\='YEARLY rule, first day of the month for a \\='MONTHLY rule, etc. -RECUR-VALUE's interval determines INTERVALSIZE, and its frequency +RRULE's interval determines INTERVALSIZE, and its frequency determines the units: a month for \\='MONTHLY, etc. N should be a non-negative integer. Interval 0 is the interval containing DTSTART. DTSTART should be an `icalendar-date' or -`icalendar-date-time' value. RECUR-VALUE should be an +`icalendar-date-time' value. RRULE should be an `icalendar-recur'. If VTIMEZONE is provided, it is used to set time zone information in the @@ -509,8 +509,8 @@ information and represent floating local times." (let* ((start-dt (if (cl-typep dtstart 'ical:date) (ical:date-to-date-time dtstart :tz vtimezone) dtstart)) - (freq (ical:recur-freq recur-value)) - (intervalsize (ical:recur-interval-size recur-value)) + (freq (ical:rrule-freq rrule)) + (intervalsize (ical:rrule-interval-size rrule)) (unit (cl-case freq (YEARLY :year) (MONTHLY :month) @@ -520,16 +520,16 @@ information and represent floating local times." (MINUTELY :minute) (SECONDLY :second))) (target (ical:date/time-add start-dt unit (* n intervalsize) vtimezone))) - (icr:find-interval target dtstart recur-value vtimezone))) + (icr:find-interval target dtstart rrule vtimezone))) -(defun icr:next-interval (interval recur-value &optional vtimezone) +(defun icr:next-interval (interval rrule &optional vtimezone) "Return the next recurrence interval after INTERVAL. Given a recurrence interval [LOW HIGH NEXT], returns the next interval [NEXT HIGHER HIGHER-NEXT], where HIGHER and HIGHER-NEXT are determined -by the frequency and interval sizes of RECUR-VALUE." +by the frequency and interval sizes of RRULE." (let* ((new-low (icr:interval-next interval)) - (freq (ical:recur-freq recur-value)) + (freq (ical:rrule-freq rrule)) (unit (cl-case freq (YEARLY :year) (MONTHLY :month) @@ -538,7 +538,7 @@ by the frequency and interval sizes of RECUR-VALUE." (HOURLY :hour) (MINUTELY :minute) (SECONDLY :second))) - (intervalsize (ical:recur-interval-size recur-value)) + (intervalsize (ical:rrule-interval-size rrule)) (new-high (ical:date/time-add new-low unit 1 vtimezone)) (new-next (when (< 1 intervalsize) @@ -552,17 +552,17 @@ by the frequency and interval sizes of RECUR-VALUE." (icr:make-interval new-low new-high new-next))) -(defun icr:previous-interval (interval recur-value dtstart &optional vtimezone) +(defun icr:previous-interval (interval rrule dtstart &optional vtimezone) "Given a recurrence INTERVAL, return the previous interval. For an interval [LOW HIGH NEXT-LOW], the previous interval is [PREV-LOW PREV-HIGH LOW], where PREV-LOW and PREV-HIGH are determined by -the frequency and interval sizes of RECUR-VALUE (see +the frequency and interval sizes of RRULE (see `icalendar-recur-find-interval'). If the resulting period of time between PREV-LOW and PREV-HIGH occurs entirely before DTSTART, then the interval does not exist; in this case nil is returned." (let* ((upper (icr:interval-low interval)) - (freq (ical:recur-freq recur-value)) + (freq (ical:rrule-freq rrule)) (unit (cl-case freq (YEARLY :year) (MONTHLY :month) @@ -571,7 +571,7 @@ interval does not exist; in this case nil is returned." (HOURLY :hour) (MINUTELY :minute) (SECONDLY :second))) - (intervalsize (ical:recur-interval-size recur-value)) + (intervalsize (ical:rrule-interval-size rrule)) (new-low (ical:date/time-add upper unit (* -1 intervalsize) vtimezone)) (new-high (if (< 1 intervalsize) @@ -1032,12 +1032,12 @@ The returned value is RECURRENCES filtered by index." (pop dts)) (nreverse r))) -(defun icr:refine-from-clauses (interval recur-value dtstart +(defun icr:refine-from-clauses (interval rrule dtstart &optional vtimezone) - "Resolve INTERVAL into subintervals based on the clauses in RECUR-VALUE. + "Resolve INTERVAL into subintervals based on the clauses in RRULE. The resulting list of subintervals represents all times in INTERVAL -which match the BY* clauses of RECUR-VALUE except BYSETPOS, as well as +which match the BY* clauses of RRULE except BYSETPOS, as well as the constraints implicit in DTSTART. (For example, if there is no BYMINUTE clause, subintervals will have the same minute value as DTSTART.) @@ -1047,14 +1047,14 @@ components and TZID should be the `icalendar-tzid' property value of one of those timezones. In this case, TZID states the time zone of DTSTART, and the offsets effective in that time zone on the dates and times of recurrences will be local to that time zone." - (let ((freq (ical:recur-freq recur-value)) - (weekstart (ical:recur-weekstart recur-value)) + (let ((freq (ical:rrule-freq rrule)) + (weekstart (ical:rrule-weekstart rrule)) (subintervals (list interval))) (dolist (byunit (list 'BYMONTH 'BYWEEKNO 'BYYEARDAY 'BYMONTHDAY 'BYDAY 'BYHOUR 'BYMINUTE 'BYSECOND)) - (let ((values (ical:recur-by* byunit recur-value)) + (let ((values (ical:rrule-by* byunit rrule)) (in-month nil)) ;; When there is no explicit BY* clause, use the value implicit ;; in DTSTART. (These conditions are adapted from RFC8984: @@ -1086,26 +1086,26 @@ recurrences will be local to that time zone." (setq values (list (ical:date/time-weekday dtstart)))) (when (and (eq byunit 'BYMONTHDAY) (eq freq 'MONTHLY) - (not (ical:recur-by* 'BYDAY recur-value)) + (not (ical:rrule-by* 'BYDAY rrule)) (not values)) (setq values (list (ical:date/time-monthday dtstart)))) (when (and (eq freq 'YEARLY) - (not (ical:recur-by* 'BYYEARDAY recur-value))) + (not (ical:rrule-by* 'BYYEARDAY rrule))) (when (and (eq byunit 'BYMONTH) (not values) - (not (ical:recur-by* 'BYWEEKNO recur-value)) - (or (ical:recur-by* 'BYMONTHDAY recur-value) - (not (ical:recur-by* 'BYDAY recur-value)))) + (not (ical:rrule-by* 'BYWEEKNO rrule)) + (or (ical:rrule-by* 'BYMONTHDAY rrule) + (not (ical:rrule-by* 'BYDAY rrule)))) (setq values (list (ical:date/time-month dtstart)))) (when (and (eq byunit 'BYMONTHDAY) (not values) - (not (ical:recur-by* 'BYWEEKNO recur-value)) - (not (ical:recur-by* 'BYDAY recur-value))) + (not (ical:rrule-by* 'BYWEEKNO rrule)) + (not (ical:rrule-by* 'BYDAY rrule))) (setq values (list (ical:date/time-monthday dtstart)))) (when (and (eq byunit 'BYDAY) (not values) - (ical:recur-by* 'BYWEEKNO recur-value) - (not (ical:recur-by* 'BYMONTHDAY recur-value))) + (ical:rrule-by* 'BYWEEKNO rrule) + (not (ical:rrule-by* 'BYMONTHDAY rrule))) (setq values (list (ical:date/time-weekday dtstart))))) ;; Handle offsets in a BYDAY clause: @@ -1120,7 +1120,7 @@ recurrences will be local to that time zone." (when (and (eq byunit 'BYDAY) (or (eq freq 'MONTHLY) (and (eq freq 'YEARLY) - (ical:recur-by* 'BYMONTH recur-value)))) + (ical:rrule-by* 'BYMONTH rrule)))) (setq in-month t)) ;; On each iteration of the loop, we refine the subintervals @@ -1246,10 +1246,10 @@ retrieved on subsequent calls with the same arguments." (ical:with-component component ((ical:dtstart :value dtstart) (ical:tzoffsetfrom :value offset-from) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:rdate :all rdate-nodes) ;; TODO: these can also be ical:period values (ical:exdate :all exdate-nodes)) - (if (not (or recur-value rdate-nodes)) + (if (not (or rrule rdate-nodes)) ;; No recurrences to calculate, so just return early: nil ;; Otherwise, calculate recurrences in the interval: @@ -1267,19 +1267,19 @@ retrieved on subsequent calls with the same arguments." (t (let* (;; Start by generating all the recurrences matching the ;; BY* clauses except for BYSETPOS: - (subs (icr:refine-from-clauses interval recur-value dtstart + (subs (icr:refine-from-clauses interval rrule dtstart vtimezone)) (sub-recs (icr:subintervals-to-recurrences subs dtstart vtimezone)) ;; Apply any BYSETPOS clause to this set: - (keep-indices (ical:recur-by* 'BYSETPOS recur-value)) + (keep-indices (ical:rrule-by* 'BYSETPOS rrule)) (pos-recs (if keep-indices (icr:bysetpos-filter keep-indices sub-recs) sub-recs)) ;; Remove any recurrences before DTSTART or after UNTIL ;; (both of which are inclusive bounds): - (until (ical:recur-until recur-value)) + (until (ical:rrule-until rrule)) (until-recs (seq-filter (lambda (rec) (and (ical:date/time<= dtstart rec) @@ -1345,9 +1345,9 @@ UTC offsets local to that time zone." (ical:with-component component ((ical:dtstart :value dtstart) (ical:tzoffsetfrom :value offset-from) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:rdate :all rdate-nodes)) - (if (not (or recur-value rdate-nodes)) + (if (not (or rrule rdate-nodes)) ;; No recurrences to calculate, so just return early: nil ;; Otherwise, calculate the recurrences in the window: @@ -1362,11 +1362,11 @@ UTC offsets local to that time zone." (let* (;; don't look for nonexistent intervals: (low-start (if (ical:date/time< lower dtstart) dtstart lower)) - (until (ical:recur-until recur-value)) + (until (ical:rrule-until rrule)) (high-end (if (and until (ical:date/time< until upper)) until upper)) - (curr-interval (icr:find-interval low-start dtstart recur-value + (curr-interval (icr:find-interval low-start dtstart rrule vtimezone)) - (high-interval (icr:find-interval high-end dtstart recur-value + (high-interval (icr:find-interval high-end dtstart rrule vtimezone)) (high-intbound (icr:interval-high high-interval)) (recurrences nil)) @@ -1376,7 +1376,7 @@ UTC offsets local to that time zone." (nconc (icr:recurrences-in-interval curr-interval component vtimezone) recurrences)) - (setq curr-interval (icr:next-interval curr-interval recur-value + (setq curr-interval (icr:next-interval curr-interval rrule vtimezone))) ;; exclude any recurrences inside the first and last intervals but @@ -1447,7 +1447,7 @@ UTC offsets local to that time zone." (ical:with-component component ((ical:dtstart :value dtstart) (ical:tzoffsetfrom :value offset-from) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:rdate :all rdate-nodes)) (when (memq (ical:ast-node-type component) '(ical:standard ical:daylight)) ;; in time zone observances, set the zone field in dtstart @@ -1457,18 +1457,18 @@ UTC offsets local to that time zone." :zone offset-from :dst (not (ical:daylight-component-p component))))) - (unless (or recur-value rdate-nodes) + (unless (or rrule rdate-nodes) (error "No recurrence data in component: %s" component)) - (unless (ical:recur-count recur-value) + (unless (ical:rrule-count rrule) (error "Recurrence rule has no COUNT clause")) - (let ((count (ical:recur-count recur-value)) - (int (icr:nth-interval 0 dtstart recur-value vtimezone)) + (let ((count (ical:rrule-count rrule)) + (int (icr:nth-interval 0 dtstart rrule vtimezone)) recs) (while (length< recs count) (setq recs (nconc recs (icr:recurrences-in-interval int component vtimezone (- count (length recs))))) - (setq int (icr:next-interval int recur-value vtimezone))) + (setq int (icr:next-interval int rrule vtimezone))) recs))) @@ -1771,7 +1771,7 @@ ignored." (dolist (obs (append stds dls)) (ical:with-component obs ((ical:dtstart :value start) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:rdate :all rdate-nodes) (ical:tzoffsetfrom :value offset-from)) ;; DTSTART of the observance must be given as local time, and is @@ -1781,7 +1781,7 @@ ignored." (effective-start (ical:date-time-variant start :zone offset-from :dst (not is-daylight))) - (until (ical:recur-until recur-value)) + (until (ical:rrule-until rrule)) (bound ;; Optimization: compute a rough upper bound for when ;; an observance might apply, thus allowing us to skip @@ -1796,8 +1796,8 @@ ignored." (when until (ical:date-time-variant until :year (+ (decoded-time-year until) - (ical:recur-interval-size - recur-value))))) + (ical:rrule-interval-size + rrule))))) (observance-might-apply (if given-clock-time (icr:-w/in-locally-p given-clock-time effective-start bound) @@ -1859,11 +1859,11 @@ ignored." ;; start of each observance onset), which ;; `icr:tz-set-zone' knows to handle specially without ;; calling this function. - (when recur-value + (when rrule (let* ((target (or given-clock-time (decode-time given-abs-time offset-from))) (int (icr:find-interval - target effective-start recur-value offset-from)) + target effective-start rrule offset-from)) (<=given (if given-clock-time (lambda (rec) @@ -1883,7 +1883,7 @@ ignored." ;; actually be in the previous interval, e.g. ;; if `dt' is in January after an annual change to ;; Standard Time in November. So check that as well. - (setq int (icr:previous-interval int recur-value + (setq int (icr:previous-interval int rrule effective-start offset-from)) (setq int-recs diff --git a/lisp/calendar/icalendar-shortdoc.el b/lisp/calendar/icalendar-shortdoc.el index ef6f23cdfb9..cf706f54672 100644 --- a/lisp/calendar/icalendar-shortdoc.el +++ b/lisp/calendar/icalendar-shortdoc.el @@ -252,22 +252,22 @@ "(icalendar-recur-recurrences-to-count '(1 1 2026) '(12 31 2026) vevent)" :eg-result-string "((1 10 2026) (2 10 2026) (3 10 2026))") - (icalendar-recur-freq + (icalendar-rrule-freq :eval - (icalendar-recur-freq '((FREQ MONTHLY) (INTERVAL 3) (BYDAY ((5 . -1)))))) - (icalendar-recur-interval-size - :eval (icalendar-recur-interval-size '((FREQ MONTHLY) (BYDAY ((5 . -1))))) - :eval (icalendar-recur-interval-size '((FREQ MONTHLY) (INTERVAL 3)))) - (icalendar-recur-count - :eval (icalendar-recur-count '((FREQ MONTHLY) (INTERVAL 2) (COUNT 6)))) - (icalendar-recur-until - :eval (icalendar-recur-until '((FREQ WEEKLY) (UNTIL (12 31 2026))))) - (icalendar-recur-by* - :eval (icalendar-recur-by* 'BYDAY '((FREQ MONTHLY) (BYDAY ((5 . -1)))))) - (icalendar-recur-weekstart + (icalendar-rrule-freq '((FREQ MONTHLY) (INTERVAL 3) (BYDAY ((5 . -1)))))) + (icalendar-rrule-interval-size + :eval (icalendar-rrule-interval-size '((FREQ MONTHLY) (BYDAY ((5 . -1))))) + :eval (icalendar-rrule-interval-size '((FREQ MONTHLY) (INTERVAL 3)))) + (icalendar-rrule-count + :eval (icalendar-rrule-count '((FREQ MONTHLY) (INTERVAL 2) (COUNT 6)))) + (icalendar-rrule-until + :eval (icalendar-rrule-until '((FREQ WEEKLY) (UNTIL (12 31 2026))))) + (icalendar-rrule-by* + :eval (icalendar-rrule-by* 'BYDAY '((FREQ MONTHLY) (BYDAY ((5 . -1)))))) + (icalendar-rrule-weekstart :eval - (icalendar-recur-weekstart '((FREQ WEEKLY) (UNTIL (12 31 2026)) (WKST 0))) + (icalendar-rrule-weekstart '((FREQ WEEKLY) (UNTIL (12 31 2026)) (WKST 0))) :eval - (icalendar-recur-weekstart '((FREQ WEEKLY) (UNTIL (12 31 2026)))))) + (icalendar-rrule-weekstart '((FREQ WEEKLY) (UNTIL (12 31 2026)))))) (provide 'icalendar-shortdoc) diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el index 0097f590b43..54f105f6fda 100644 --- a/lisp/gnus/gnus-icalendar.el +++ b/lisp/gnus/gnus-icalendar.el @@ -131,18 +131,18 @@ (cl-defmethod gnus-icalendar-event:recurring-freq ((event gnus-icalendar-event)) "Return recurring frequency of EVENT." - (ical:recur-freq (gnus-icalendar-event:recur event))) + (ical:rrule-freq (gnus-icalendar-event:recur event))) (cl-defmethod gnus-icalendar-event:recurring-interval ((event gnus-icalendar-event)) "Return recurring interval of EVENT." - (ical:recur-interval-size (gnus-icalendar-event:recur event))) + (ical:rrule-interval-size (gnus-icalendar-event:recur event))) (cl-defmethod gnus-icalendar-event:recurring-days ((event gnus-icalendar-event)) "Return, when available, the week day numbers on which the EVENT recurs." (let ((rrule (gnus-icalendar-event:recur event))) (when rrule (mapcar (lambda (el) (if (consp el) (car el) el)) - (ical:recur-by* 'BYDAY rrule))))) + (ical:rrule-by* 'BYDAY rrule))))) (cl-defmethod gnus-icalendar-event:start ((event gnus-icalendar-event)) (format-time-string "%Y-%m-%d %H:%M" (gnus-icalendar-event:start-time event))) diff --git a/test/lisp/calendar/diary-icalendar-tests.el b/test/lisp/calendar/diary-icalendar-tests.el index 22faeb7aa23..06272a39cf4 100644 --- a/test/lisp/calendar/diary-icalendar-tests.el +++ b/test/lisp/calendar/diary-icalendar-tests.el @@ -817,8 +817,8 @@ SOURCE, if given, should be a symbol; it is used to name the test." (should (equal (ical:date-time-to-date dtstart) (calendar-nth-named-day 1 4 1 di:recurring-start-year))) (should (= 16 (decoded-time-hour dtstart))) - (should (eq (ical:recur-freq rrule) 'WEEKLY)) - (should (equal (ical:recur-by* 'BYDAY rrule) (list 4))))) + (should (eq (ical:rrule-freq rrule) 'WEEKLY)) + (should (equal (ical:rrule-by* 'BYDAY rrule) (list 4))))) (dit:parse-test ;; Multiline entry, parsed as one event: @@ -961,10 +961,10 @@ SOURCE, if given, should be a symbol; it is used to name the test." :tests (ical:with-component (car parsed) ((ical:dtstart :value dtstart) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:summary :value summary)) (should (equal dtstart '(5 28 1995))) - (should (eq (ical:recur-freq recur-value) 'YEARLY)) + (should (eq (ical:rrule-freq rrule) 'YEARLY)) (should (equal summary "H's birthday")))) (dit:parse-test @@ -977,11 +977,11 @@ SOURCE, if given, should be a symbol; it is used to name the test." :tests (ical:with-component (car parsed) ((ical:dtstart :value dtstart) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:summary :value summary)) (should (equal dtstart '(6 24 2012))) - (should (equal (ical:recur-freq recur-value) 'DAILY)) - (should (equal (ical:recur-until recur-value) '(7 10 2012))) + (should (equal (ical:rrule-freq rrule) 'DAILY)) + (should (equal (ical:rrule-until rrule) '(7 10 2012))) (should (equal summary "Vacation")))) (dit:parse-test @@ -994,11 +994,11 @@ SOURCE, if given, should be a symbol; it is used to name the test." :tests (ical:with-component (car parsed) ((ical:dtstart :value dtstart) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:summary :value summary)) (should (equal dtstart '(3 1 2012))) - (should (eq (ical:recur-freq recur-value) 'DAILY)) - (should (eq (ical:recur-interval-size recur-value) 50)) + (should (eq (ical:rrule-freq rrule) 'DAILY)) + (should (eq (ical:rrule-interval-size rrule) 50)) (should (equal summary "Renew medication")))) (dit:parse-test @@ -1011,13 +1011,13 @@ SOURCE, if given, should be a symbol; it is used to name the test." :tests (ical:with-component (car parsed) ((ical:dtstart :value dtstart) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:summary :value summary)) (should (equal dtstart (calendar-nth-named-day 4 4 11 di:recurring-start-year))) - (should (eq (ical:recur-freq recur-value) 'MONTHLY)) - (should (equal (ical:recur-by* 'BYMONTH recur-value) (list 11))) - (should (equal (ical:recur-by* 'BYDAY recur-value) (list '(4 . 4)))) + (should (eq (ical:rrule-freq rrule) 'MONTHLY)) + (should (equal (ical:rrule-by* 'BYMONTH rrule) (list 11))) + (should (equal (ical:rrule-by* 'BYDAY rrule) (list '(4 . 4)))) (should (equal summary "American Thanksgiving")))) (dit:parse-test @@ -1030,13 +1030,13 @@ SOURCE, if given, should be a symbol; it is used to name the test." :tests (ical:with-component (car parsed) ((ical:dtstart :value dtstart) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:summary :value summary)) (should (equal dtstart (calendar-nth-named-day 4 5 1 di:recurring-start-year))) - (should (eq (ical:recur-freq recur-value) 'MONTHLY)) + (should (eq (ical:rrule-freq rrule) 'MONTHLY)) ;; day 3 is Wednesday, so offset of 2 means Friday (=5): - (should (equal (ical:recur-by* 'BYDAY recur-value) (list '(5 . 4)))) + (should (equal (ical:rrule-by* 'BYDAY rrule) (list '(5 . 4)))) (should (equal summary "Monthly committee meeting")))) (dit:parse-test @@ -1052,11 +1052,11 @@ SOURCE, if given, should be a symbol; it is used to name the test." :tests (ical:with-component (car parsed) ((ical:dtstart :value dtstart) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:exdate :values exdates) (ical:summary :value summary)) (should (equal dtstart '(11 11 2024))) - (should (eq (ical:recur-freq recur-value) 'WEEKLY)) + (should (eq (ical:rrule-freq rrule) 'WEEKLY)) (should (equal exdates '((12 23 2024) (12 30 2024)))) (should (equal summary "Reading group")))) @@ -1070,12 +1070,12 @@ SOURCE, if given, should be a symbol; it is used to name the test." :tests (ical:with-component (car parsed) ((ical:dtstart :value dtstart) - (ical:rrule :value recur-value) + (ical:rrule :value rrule) (ical:summary :value summary)) (should (equal dtstart (list 10 22 di:recurring-start-year))) - (should (eq (ical:recur-freq recur-value) 'YEARLY)) - (should (equal (ical:recur-by* 'BYMONTH recur-value) (list 10 11 12))) - (should (equal (ical:recur-by* 'BYMONTHDAY recur-value) (list 22))) + (should (eq (ical:rrule-freq rrule) 'YEARLY)) + (should (equal (ical:rrule-by* 'BYMONTH rrule) (list 10 11 12))) + (should (equal (ical:rrule-by* 'BYMONTHDAY rrule) (list 22))) (should (equal summary "Rake leaves")))) (dit:parse-test diff --git a/test/lisp/calendar/icalendar-parser-tests.el b/test/lisp/calendar/icalendar-parser-tests.el index f3c5de35c87..8215f977e26 100644 --- a/test/lisp/calendar/icalendar-parser-tests.el +++ b/test/lisp/calendar/icalendar-parser-tests.el @@ -388,21 +388,21 @@ test." (ipt:parse/print-test "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1" -:type icalendar-recur +:type icalendar-rrule-value :parser icalendar-parse-value-node :printer icalendar-print-value-node :source rfc5545-sec3.3.10/1) (ipt:parse/print-test "FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30" -:type icalendar-recur +:type icalendar-rrule-value :parser icalendar-parse-value-node :printer icalendar-print-value-node :source rfc5545-sec3.3.10/2) (ipt:parse/print-test "FREQ=DAILY;COUNT=10;INTERVAL=2" -:type icalendar-recur +:type icalendar-rrule-value :parser icalendar-parse-value-node :printer icalendar-print-value-node :source rfc5545-sec3.3.10/3) diff --git a/test/lisp/calendar/icalendar-recur-tests.el b/test/lisp/calendar/icalendar-recur-tests.el index c1f7bb90974..199d6c4aa25 100644 --- a/test/lisp/calendar/icalendar-recur-tests.el +++ b/test/lisp/calendar/icalendar-recur-tests.el @@ -1383,7 +1383,7 @@ END:VTIMEZONE (ts-obs/onset (icr:tz-observance-on ts ict:tz-eastern))) (should (eq 'ical:daylight (ical:ast-node-type obs))) (should (equal dt onset)) - (should (equal end (ical:recur-until + (should (equal end (ical:rrule-until (ical:with-property-of obs 'ical:rrule nil value)))) (should (equal obs/onset ts-obs/onset))) @@ -1534,10 +1534,10 @@ SOURCE should be a symbol; it is used to name the test." ,(format "Parse and evaluate recur-value example from `%s':\n%s" source doc) :tags ,tags - (let* ((parsed (ical:parse-from-string 'ical:recur ,recur-string)) + (let* ((parsed (ical:parse-from-string 'ical:rrule-value ,recur-string)) (recvalue (ical:ast-node-value parsed)) - (until (ical:recur-until recvalue)) - (count (ical:recur-count recvalue)) + (until (ical:rrule-until recvalue)) + (count (ical:rrule-count recvalue)) (dtstart ,dtstart) (tzid (when (cl-typep dtstart 'ical:date-time) commit 646702f70b38e904754a1addab138d0ed3af9411 Author: Augusto Stoffel Date: Thu May 7 09:13:32 2026 +0200 let-alist.el: Use 'elt' instead of 'nth' The advantage is that this works also for mixtures of alists and vectors, as one obtains, e.g., from 'json-parse-buffer' and 'json-parse-string'. * lisp/emacs-lisp/let-alist.el (let-alist--list-to-sexp): Use 'elt' instead of 'nth'. (let-alist): Adapt doc string. (Bug#80992) diff --git a/lisp/emacs-lisp/let-alist.el b/lisp/emacs-lisp/let-alist.el index 3140e2b243d..3e1c6f0181e 100644 --- a/lisp/emacs-lisp/let-alist.el +++ b/lisp/emacs-lisp/let-alist.el @@ -48,7 +48,7 @@ ;; ;; essentially expands to ;; -;; (let ((.title.0 (nth 0 (cdr (assq 'title alist)))) +;; (let ((.title.0 (elt (cdr (assq 'title alist)) 0)) ;; (.body (cdr (assq 'body alist))) ;; (.site (cdr (assq 'site alist))) ;; (.site.contents (cdr (assq 'contents (cdr (assq 'site alist)))))) @@ -103,7 +103,7 @@ symbol, and each cdr is the same symbol without the `.'." (rest (if (cdr list) (let-alist--list-to-sexp (cdr list) var) var))) (cond - ((numberp sym) `(nth ,sym ,rest)) + ((numberp sym) `(elt ,rest ,sym)) (t `(cdr (assq ',sym ,rest)))))) (defun let-alist--remove-dot (symbol) @@ -136,7 +136,7 @@ For instance, the following code essentially expands to - (let ((.title.0 (nth 0 (cdr (assq \\='title alist)))) + (let ((.title.0 (elt (cdr (assq \\='title alist)) 0)) (.body (cdr (assq \\='body alist))) (.site (cdr (assq \\='site alist))) (.site.contents (cdr (assq \\='contents (cdr (assq \\='site alist)))))) commit 3d2bb233f27c00dac2bec0c70a569a6232f37c54 Author: Michael Albinus Date: Sat May 23 10:25:46 2026 +0200 ; Minor Tramp changes * doc/misc/tramp.texi (Frequently Asked Questions): google-drive has been disabled in GNOME 50. * lisp/net/tramp-cmds.el (tramp-enable-method): Upcase prompt. * lisp/net/tramp-sh.el (tramp-sh-handle-make-process) (tramp-sh-handle-process-file): Improve setting of environment variables. * test/lisp/net/tramp-tests.el (tramp-methods) : Add `tramp-tmpdir'. Adapt `tramp-login-program'. (ert-remote-temporary-file-directory): Improve expansion. (tramp-test35-remote-path): Adapt test. diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index ae65cf2a620..6daa2b010cf 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -6435,6 +6435,13 @@ You can change this directory by setting the user option "XDG_RUNTIME_DIR")}. +@item +I get an error @samp{Method `gdrive' not supported by GVFS}. + +@samp{google-drive} has been disabled in @acronym{GNOME} 50. It is +not clear yet whether and when it will be reenabled. +@c @uref{https://discourse.gnome.org/t/google-drive-in-gnome-50/34417} + @item How to ignore errors when changing file attributes? diff --git a/lisp/net/tramp-cmds.el b/lisp/net/tramp-cmds.el index 95e1c5ecad8..1fc77f0e80d 100644 --- a/lisp/net/tramp-cmds.el +++ b/lisp/net/tramp-cmds.el @@ -63,7 +63,7 @@ SYNTAX can be one of the symbols `default' (default), (interactive (list (completing-read - "method: " + "Method: " (tramp-compat-seq-keep (lambda (x) (when-let* ((name (symbol-name x)) diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 7b90ae9c11b..8d4dc557676 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -3107,6 +3107,11 @@ will be used." ,(concat "PS1=" (getenv-internal "PS1" env))))) (eenv (setenv-internal eenv "INSIDE_EMACS" nil nil)) (eenv (setenv-internal eenv "PS1" nil nil)) + vars + (eenv (dolist (item (reverse eenv) vars) + (setq item (split-string item "=" 'omit)) + (setcdr item (string-join (cdr item) "=")) + (push (format "%s %s" (car item) (cdr item)) vars))) (command (when (stringp program) (format "cd %s && %s exec %s %s env %s %s" @@ -3222,10 +3227,15 @@ will be used." (delete-region mark (point-max)) (narrow-to-region (point-max) (point-max)) ;; Send delayed environment. - (dolist (entry eenv) + (when eenv (tramp-send-command - v (format - "export %s" (tramp-shell-quote-argument entry)))) + v + (format + "while read var val; do export $var=\"$val\"; done <<'%s'\n%s\n%s" + tramp-end-of-heredoc + (string-join eenv "\n") + tramp-end-of-heredoc) + t)) ;; Now do it. (if command ;; Send the command. @@ -3350,6 +3360,8 @@ will be used." env "EMACSCLIENT_TRAMP" (tramp-make-tramp-file-name v 'noloc) 'keep))) (setq env (setenv-internal env "INSIDE_EMACS" (tramp-inside-emacs) 'keep)) + ;; Remove looong environment variables, for example from tramp-tests.el. + (setq env (seq-remove (lambda (x) (length> x 256)) env)) (when env (setq command (format diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index c0ad7205c5d..4d11faf64de 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -117,9 +117,10 @@ (t (add-to-list 'tramp-methods `("mock" - (tramp-login-program ,tramp-default-remote-shell) + (tramp-login-program ,tramp-encoding-shell) (tramp-login-args (("-i"))) (tramp-direct-async ("-c")) + (tramp-tmpdir ,temporary-file-directory) (tramp-remote-shell ,tramp-default-remote-shell) (tramp-remote-shell-args ("-c")) (tramp-connection-timeout 10))) @@ -225,7 +226,8 @@ auto-revert-use-notify t ert-batch-backtrace-right-margin nil ert-remote-temporary-file-directory - (expand-file-name ert-remote-temporary-file-directory) + (let ((tramp-show-ad-hoc-proxies t) (non-essential t)) + (expand-file-name ert-remote-temporary-file-directory)) password-cache-expiry nil remote-file-name-inhibit-cache nil tramp-allow-unsafe-temporary-files t @@ -6855,8 +6857,7 @@ INPUT, if non-nil, is a string sent to the process." "Check loooong `tramp-remote-path'." :tags '(:expensive-test) (skip-unless (tramp--test-enabled)) - (skip-unless (tramp--test-sh-p)) - (skip-unless (not (tramp--test-crypt-p))) + (skip-unless (tramp--test-supports-environment-variables-p)) (let* ((tmp-name1 (tramp--test-make-temp-name)) (default-directory ert-remote-temporary-file-directory) @@ -9330,9 +9331,6 @@ If INTERACTIVE is non-nil, the tests are run interactively." ;; Use `skip-when' starting with Emacs 30.1. -;; Starting with Emacs 29, use `ert-with-temp-file' and -;; `ert-with-temp-directory'. - (provide 'tramp-tests) ;;; tramp-tests.el ends here commit f6281d757d35bb93790732164f9d8d11c043c00c Author: Eli Zaretskii Date: Sat May 23 09:35:24 2026 +0300 ; * etc/NEWS: Tell how to disable 'markdown-ts-mode'. diff --git a/etc/NEWS b/etc/NEWS index 3021ad42a12..1f99eb22a38 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3931,7 +3931,20 @@ A major mode based on 'conf-mode' for editing ".npmrc" files. ** New major modes based on the tree-sitter library *** New major mode 'markdown-ts-mode'. -A major mode based on the tree-sitter library for editing Markdown files. +A major mode based on the tree-sitter library for editing Markdown +files. This is now the default major mode for Markdown files. If you +don't have the necessary tree-sitter grammar libraries installed, or if +your Emacs was built without tree-sitter support, Emacs will now show a +warning to that effect when you visit a Markdown file. If you don't +want to use this mode and want to avoid these warnings, add the +following to your init file: + + (add-to-list 'auto-mode-alist '("\\.md\\'" . fundamental-mode)) + (add-to-list 'auto-mode-alist '("\\.markdown\\'" . fundamental-mode)) + (add-to-list 'auto-mode-alist '("\\.mdx\\'" . fundamental-mode)) + +This will cause Emacs to visit Markdown files in Fundamental mode, which +was the default before this mode was added to Emacs. *** New major mode 'mhtml-ts-mode'. An optional major mode based on the tree-sitter library for editing HTML commit 142b1e0d4c3f63fd5aa07ce748915137fea1ec52 Author: Jacek Migacz Date: Thu May 21 10:44:55 2026 +0000 Fix Lisp injection via X-Draft-From in Gnus * lisp/gnus/gnus-msg.el (gnus-inews-make-draft-meta-information): Escape the group name with prin1-to-string to prevent arbitrary Lisp injection through crafted group names. The unescaped group name was embedded into a Lisp-readable string, parsed back with read-from-string in gnus-draft-setup, and eventually eval'd via message-do-actions, allowing code execution when a draft was sent. diff --git a/lisp/gnus/gnus-msg.el b/lisp/gnus/gnus-msg.el index 99f1735dfec..a478093fc6c 100644 --- a/lisp/gnus/gnus-msg.el +++ b/lisp/gnus/gnus-msg.el @@ -444,7 +444,7 @@ only affect the Gcc copy, but not the original message." (defun gnus-inews-make-draft-meta-information (group articles) (when (numberp articles) (setq articles (list articles))) - (concat "(\"" group "\"" + (concat "(" (prin1-to-string (or group "")) (if articles (concat " " (mapconcat commit d6f7b2d99bdbcc29e8784185612282a508cc3e84 Author: Martin Rudalics Date: Fri May 22 19:33:57 2026 +0200 Save/restore old_buffer slot via window configurations (Bug#81097) With Emacs 31 the old_buffer slot of a window gets overwritten with the buffer unshown in that window when that window is deleted. Fset_window_configuration triggers that when calling delete_all_child_windows. If a window configuration gets saved and restored in one and the same redisplay cycle, the change time stamps of the window and its frame will be equal and 'window-buffer-change-functions' may wrongly decide that the window's buffer has not changed because its buffer and old_buffer slots refer to the same buffer (Bug#81097). Fix that by saving and restoring the old_buffer slot. * src/window.c (struct saved_window): Add 'old_buffer' slot. (Fset_window_configuration): Restore old_buffer slot. (save_window_save): Save old_buffer slot. diff --git a/src/window.c b/src/window.c index 3dbf1530d78..792c43d0555 100644 --- a/src/window.c +++ b/src/window.c @@ -7611,7 +7611,7 @@ struct saved_window { union vectorlike_header header; - Lisp_Object window, buffer, start, pointm, old_pointm; + Lisp_Object window, buffer, old_buffer, start, pointm, old_pointm; Lisp_Object pixel_left, pixel_top, pixel_height, pixel_width; Lisp_Object left_col, top_line, total_cols, total_lines; Lisp_Object normal_cols, normal_lines; @@ -7835,6 +7835,7 @@ the return value is nil. Otherwise the value is t. */) /* If we squirreled away the buffer, restore it now. */ if (BUFFERP (w->combination_limit)) wset_buffer (w, w->combination_limit); + wset_old_buffer (w, p->old_buffer); w->pixel_left = XFIXNAT (p->pixel_left); w->pixel_top = XFIXNAT (p->pixel_top); w->pixel_width = XFIXNAT (p->pixel_width); @@ -8221,6 +8222,7 @@ save_window_save (Lisp_Object window, struct Lisp_Vector *vector, ptrdiff_t i) wset_temslot (w, make_fixnum (i)); i++; p->window = window; p->buffer = (WINDOW_LEAF_P (w) ? w->contents : Qnil); + p->old_buffer = w->old_buffer; p->pixel_left = make_fixnum (w->pixel_left); p->pixel_top = make_fixnum (w->pixel_top); p->pixel_width = make_fixnum (w->pixel_width); commit e0fbecaf658b95d3c53aa9f1bc078a3326a3f77c Author: Michael Albinus Date: Fri May 22 18:37:36 2026 +0200 Adapt ert-remote-temporary-file-directory settings * lisp/emacs-lisp/ert-x.el (tramp-default-remote-shell) (tramp-encoding-shell): Declare. (tramp-methods) : Add `tramp-tmpdir'. Adapt `tramp-login-program' and `tramp-remote-shell'. diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el index 0952033a6cc..b6d52b8b3c5 100644 --- a/lisp/emacs-lisp/ert-x.el +++ b/lisp/emacs-lisp/ert-x.el @@ -379,6 +379,8 @@ The same keyword arguments are supported as in (ffap--gcc-is-clang-p)) (defvar tramp-default-host-alist) +(defvar tramp-default-remote-shell) +(defvar tramp-encoding-shell) (defvar tramp-methods) (defvar tramp-remote-path) @@ -394,16 +396,17 @@ The same keyword arguments are supported as in (cond ((getenv "REMOTE_TEMPORARY_FILE_DIRECTORY")) ((eq system-type 'windows-nt) null-device) - ;; Android's built-in shell is far too dysfunctional to support + ;; Android's built-in shell is far too dysfunctional to support. ;; Tramp. ((eq system-type 'android) null-device) (t (add-to-list 'tramp-methods - '("mock" - (tramp-login-program "sh") + `("mock" + (tramp-login-program ,tramp-encoding-shell) (tramp-login-args (("-i"))) - (tramp-direct-async ("-c")) - (tramp-remote-shell "/bin/sh") + (tramp-direct-async ("-c")) + (tramp-tmpdir ,temporary-file-directory) + (tramp-remote-shell ,tramp-default-remote-shell) (tramp-remote-shell-args ("-c")) (tramp-connection-timeout 10))) (add-to-list commit 3de7f0ce5e5fca16045b3f96ceab607d66782d4d Author: Eli Zaretskii Date: Fri May 22 19:12:12 2026 +0300 Fix warning message in 'markdown-ts-mode--initialize' * lisp/textmodes/markdown-ts-mode.el (markdown-ts-mode--initialize): Tweak the warning message when Tree-sitter is not available. (Bug#81100) diff --git a/lisp/textmodes/markdown-ts-mode.el b/lisp/textmodes/markdown-ts-mode.el index 7f87ff5d0bc..be2247b870e 100644 --- a/lisp/textmodes/markdown-ts-mode.el +++ b/lisp/textmodes/markdown-ts-mode.el @@ -5402,9 +5402,12 @@ With a prefix argument, ARG, if needed, install parsers for `html', (markdown-ts--set-up)) (t (warn "markdown-ts-mode cannot be set up; using fundamental-mode. -The tree-sitter parsers `markdown' and `markdown-inline' were not found. +%s." + (if (treesit-available-p) + "The tree-sitter parsers `markdown' and `markdown-inline' were not found. Use the command `markdown-ts-mode-install-parsers' to install them. -With a prefix argument, it can also install optional parsers.") +With a prefix argument, it can also install optional parsers" + "Emacs was built without Tree-sitter support, or could not load Tree-sitter")) (fundamental-mode))))) ;;;###autoload commit 7df8604ea635e7940af19e0fe06e5f644181f32e Author: Eli Zaretskii Date: Fri May 22 14:46:13 2026 +0300 ; Improve documentation of lazy-highlight in search and replace commands * lisp/isearch.el (lazy-highlight, lazy-highlight-initial-delay) (lazy-highlight-buffer, isearch-lazy-highlight, isearch-forward): * lisp/replace.el (query-replace, query-replace-lazy-highlight) (query-replace, query-replace-regexp): Doc fixes. * doc/emacs/search.texi (Search Customizations): Document 'lazy-highlight-buffer' and 'lazy-highlight-buffer-max-at-a-time'. Use @vtable to avoid the need of indexing each variable separately. diff --git a/doc/emacs/search.texi b/doc/emacs/search.texi index 2bd35380780..5f7aa1f1ef5 100644 --- a/doc/emacs/search.texi +++ b/doc/emacs/search.texi @@ -2202,24 +2202,20 @@ variable @code{isearch-lazy-highlight} to @code{nil} disables this highlighting. Here are some other variables that customize the lazy highlighting: -@table @code +@vtable @code @item lazy-highlight-initial-delay -@vindex lazy-highlight-initial-delay Time in seconds to wait before highlighting visible matches. Applies only if the search string is less than @code{lazy-highlight-no-delay-length} characters long. @item lazy-highlight-no-delay-length -@vindex lazy-highlight-no-delay-length For search strings at least as long as the value of this variable, lazy highlighting of matches starts immediately. @item lazy-highlight-interval -@vindex lazy-highlight-interval Time in seconds between highlighting successive matches. @item lazy-highlight-max-at-a-time -@vindex lazy-highlight-max-at-a-time The maximum number of matches to highlight before checking for input. A large number can take some time to highlight, so if you want to continue searching and type @kbd{C-s} or @kbd{C-r} during that time, @@ -2227,17 +2223,28 @@ Emacs will not respond until it finishes highlighting all those matches. Thus, smaller values make Emacs more responsive. @item isearch-lazy-count -@vindex isearch-lazy-count Show the current match number and the total number of matches in the search prompt. @item lazy-count-prefix-format @itemx lazy-count-suffix-format -@vindex lazy-count-prefix-format -@vindex lazy-count-suffix-format These two variables determine the format of showing the current and the total number of matches for @code{isearch-lazy-count}. -@end table + +@item lazy-highlight-buffer +If non-@code{nil}, lazy highlighting highlights the matches in the +entire buffer, not only those visible on display of the current window +(so, for example, they will also become visible in other windows showing +the same buffer). + +@item lazy-highlight-buffer-max-at-a-time +Like @code{lazy-highlight-max-at-a-time}, but used for highlighting +matches not currently visible in the window when +@code{lazy-highlight-buffer} is non-@code{nil}. It defaults to 200; set +to @code{nil} to highlight all the matches in a buffer without checking +for input. @strong{Warning:} this could make Emacs not responsive when +searching large buffers. +@end vtable @vindex search-nonincremental-instead Normally, entering @key{RET} within incremental search when the diff --git a/lisp/isearch.el b/lisp/isearch.el index 785d324cac3..92bd9af6643 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -347,6 +347,9 @@ you can define more of these faces using the same numbering scheme." When non-nil, all text currently visible on the screen matching the current search string is highlighted lazily (see `lazy-highlight-initial-delay' and `lazy-highlight-interval'). +However, if `lazy-highlight-buffer' is non-nil, all text in the +entire buffer matching the search string is highlighted lazily. +The highlighting uses the `lazy-highlight' face. When multiple windows display the current buffer, the highlighting is displayed only on the selected window, unless @@ -385,8 +388,8 @@ If this is nil, extra highlighting can be \"manually\" removed with (defcustom lazy-highlight-initial-delay 0.25 "Seconds to wait before beginning to lazily highlight all matches. -This setting only has effect when the search string is less than -`lazy-highlight-no-delay-length' characters long." +This setting only has effect when the search string is shorter than +`lazy-highlight-no-delay-length' characters." :type 'number :group 'lazy-highlight) @@ -428,7 +431,9 @@ When non-nil, all text in the buffer matching the current search string is highlighted lazily (see `lazy-highlight-initial-delay', `lazy-highlight-interval' and `lazy-highlight-buffer-max-at-a-time'). This is useful when `lazy-highlight-cleanup' is customized to nil -and doesn't remove full-buffer highlighting after a search." +and doesn't remove full-buffer highlighting after a search. +If this is nil (the default), only the text currently visible in +the window is highlighted, subject to `isearch-lazy-highlight'." :type 'boolean :group 'lazy-highlight :version "27.1") @@ -443,7 +448,9 @@ and doesn't remove full-buffer highlighting after a search." (((class color) (min-colors 8)) (:background "turquoise3" :distant-foreground "white")) (t (:underline t))) - "Face for lazy highlighting of matches other than the current one." + "Face for lazy highlighting of matches other than the current one. +Used in Isearch when `isearch-lazy-highlight' is non-nil, +and in `query-replace' when `query-replace-lazy-highlight' is non-nil." :group 'lazy-highlight :group 'basic-faces) @@ -1006,6 +1013,9 @@ Each element is an `isearch--state' struct where the slots are With a prefix argument, do an incremental regular expression search instead. \\ As you type characters, they add to the search string and are found. +Current match for the search string is highlighted using the `isearch' face, +and if `isearch-lazy-highlight' is non-nil, the other matches are +highlighted using the `lazy-highlight' face. The following non-printing keys are bound in `isearch-mode-map'. Type \\[isearch-delete-char] to cancel last input item from end of search string. diff --git a/lisp/replace.el b/lisp/replace.el index 933249d824c..48e158de531 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -133,9 +133,11 @@ you can define more of these faces using the same numbering scheme." (defcustom query-replace-lazy-highlight t "Controls the lazy-highlighting during query replacements. -When non-nil, all text in the buffer matching the current match -is highlighted lazily using isearch lazy highlighting (see -`lazy-highlight-initial-delay' and `lazy-highlight-interval')." +When non-nil, all text matching the current match that is +currently visible in the window is highlighted lazily using +isearch lazy highlighting (see `lazy-highlight-initial-delay' +and `lazy-highlight-interval'). Uses the `lazy-highlight' face +to highlight matching text." :type 'boolean :group 'lazy-highlight :group 'matching @@ -143,7 +145,9 @@ is highlighted lazily using isearch lazy highlighting (see (defface query-replace '((t (:inherit isearch))) - "Face for highlighting query replacement matches." + "Face for highlighting query replacement matches. +Used in `query-replace' and `query-replace-regexp' +when `query-replace-highlight' is non-nil" :group 'matching :version "22.1") @@ -427,6 +431,11 @@ In Transient Mark mode, if the mark is active, operate on the contents of the region. Otherwise, operate from point to the end of the buffer's accessible portion. +The current match of FROM-STRING is highlighted using +the `query-replace' face. Other matches of FROM-STRING are highlighted +using the `lazy-highlight' face if `query-replace-lazy-highlight' is +non-nil. + In interactive use, the prefix arg (non-nil DELIMITED in non-interactive use), means replace only matches surrounded by word boundaries. A negative prefix arg means replace backward. @@ -508,6 +517,11 @@ accessible portion. When invoked interactively, matching a newline with `\\n' will not work; use \\`C-q C-j' instead. To match a tab character (`\\t'), just press \\`TAB'. +The current match of REGEXP is highlighted using +the `query-replace' face. Other matches of REGEXP are highlighted +using the `lazy-highlight' face if `query-replace-lazy-highlight' is +non-nil. + Use \\\\[next-history-element] \ to pull the last incremental search regexp to the minibuffer that reads REGEXP, or invoke replacements from commit 2936b36164ded60e0257f1ee872ff333bc4f4abd Author: Dmitry Gutov Date: Fri May 22 06:46:47 2026 +0300 Fix "assertion 'GTK_IS_WINDOW (window)' failed" * src/gtkutil.c (xg_frame_set_size_and_position): Remove a gtk_window_resize call which used a wrong value type (GdkX11Window instead of GtkWindow). The original motivation for that line seems to be fixed by later changes (bug#80662). diff --git a/src/gtkutil.c b/src/gtkutil.c index daa3fd1b993..4fc6b3e0108 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c @@ -1352,8 +1352,6 @@ xg_frame_set_size_and_position (struct frame *f, int width, int height) gdk_window_move_resize (gwin, x, y, outer_width, outer_height); if (FRAME_PARENT_FRAME (f)) { - /* Record the dimensions for GTK to remember after remapping. */ - gtk_window_resize (GTK_WINDOW (gwin), outer_width, outer_height); /* Resize all inner widgets and Cairo surface right away so the next redisplay drawing isn't clipped to the old size. */ GtkAllocation alloc = {0, 0, outer_width, outer_height}; commit 98348a0bdc97f305d3d18569c0d93afc8af1bac9 Author: Dmitry Gutov Date: Fri May 22 01:50:03 2026 +0300 [Xt] Fix child frame resizing glitch * src/widget.c (EmacsFrameResize): Exit early for child frames (bug#81077). diff --git a/src/widget.c b/src/widget.c index b843bca1fb9..f05818fcc76 100644 --- a/src/widget.c +++ b/src/widget.c @@ -428,8 +428,7 @@ EmacsFrameResize (Widget widget) ew->core.width, ew->core.height, f->new_width, f->new_height); - if (FRAME_PIXEL_WIDTH (f) == ew->core.width - && FRAME_PIXEL_HEIGHT (f) == ew->core.height) + if (FRAME_PARENT_FRAME (f)) /* Size always up to date. */ return; change_frame_size (f, ew->core.width, ew->core.height, commit 13b29eebc1663152ca10f55f8fcd659f48b80d68 Author: João Távora Date: Thu May 21 10:33:37 2026 +0100 Eglot: use standard face for completion annotations (bug#81088) * lisp/progmodes/eglot.el (eglot-completion-at-point): Use completions-annotations face, not font-lock-function-name-face. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index a913271a41a..e945dfb9739 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -4014,7 +4014,7 @@ for which LSP on-type-formatting should be requested." (when annotation (concat " " (propertize annotation - 'face 'font-lock-function-name-face)))))) + 'face 'completions-annotations)))))) :company-kind ;; Associate each lsp-item with a lsp-kind symbol. (lambda (proxy)