This replaces code that used macros and strings and token-pasting.
The change makes the C source a bit easier to follow,
and shrinks the Emacs executable a bit.
* alloc.c: Verify some properties of Lisp_Save_Value's representation.
(make_save_value): Change 1st arg from string to enum. All callers
changed.
(INTX): Remove.
(mark_object): Use if, not #if, for GC_MARK_STACK.
* lisp.h (SAVE_VALUEP, XSAVE_VALUE, XSAVE_POINTER, XSAVE_INTEGER)
(XSAVE_OBJECT): Now functions, not macros.
(STRING_BYTES_BOUND): Now just a macro, not a constant too;
the constant was never used.
(SAVE_SLOT_BITS, SAVE_VALUE_SLOTS, SAVE_TYPE_BITS, SAVE_TYPE_INT_INT)
(SAVE_TYPE_INT_INT_INT, SAVE_TYPE_OBJ_OBJ, SAVE_TYPE_OBJ_OBJ_OBJ)
(SAVE_TYPE_OBJ_OBJ_OBJ_OBJ, SAVE_TYPE_PTR_INT, SAVE_TYPE_PTR_OBJ)
(SAVE_TYPE_PTR_PTR, SAVE_TYPE_PTR_PTR_OBJ, SAVE_TYPE_MEMORY):
New constants.
(struct Lisp_Save_Value): Replace members area, type0, type1, type2,
type3 with a single member save_type. All uses changed.
(save_type, set_save_pointer, set_save_integer): New functions.
* print.c (PRINTX): Remove.
2013-03-21 Paul Eggert <eggert@cs.ucla.edu>
+ Use functions and constants to manipulate Lisp_Save_Value objects.
+ This replaces code that used macros and strings and token-pasting.
+ The change makes the C source a bit easier to follow,
+ and shrinks the Emacs executable a bit.
+ * alloc.c: Verify some properties of Lisp_Save_Value's representation.
+ (make_save_value): Change 1st arg from string to enum. All callers
+ changed.
+ (INTX): Remove.
+ (mark_object): Use if, not #if, for GC_MARK_STACK.
+ * lisp.h (SAVE_VALUEP, XSAVE_VALUE, XSAVE_POINTER, XSAVE_INTEGER)
+ (XSAVE_OBJECT): Now functions, not macros.
+ (STRING_BYTES_BOUND): Now just a macro, not a constant too;
+ the constant was never used.
+ (SAVE_SLOT_BITS, SAVE_VALUE_SLOTS, SAVE_TYPE_BITS, SAVE_TYPE_INT_INT)
+ (SAVE_TYPE_INT_INT_INT, SAVE_TYPE_OBJ_OBJ, SAVE_TYPE_OBJ_OBJ_OBJ)
+ (SAVE_TYPE_OBJ_OBJ_OBJ_OBJ, SAVE_TYPE_PTR_INT, SAVE_TYPE_PTR_OBJ)
+ (SAVE_TYPE_PTR_PTR, SAVE_TYPE_PTR_PTR_OBJ, SAVE_TYPE_MEMORY):
+ New constants.
+ (struct Lisp_Save_Value): Replace members area, type0, type1, type2,
+ type3 with a single member save_type. All uses changed.
+ (save_type, set_save_pointer, set_save_integer): New functions.
+ * print.c (PRINTX): Remove.
+
* alloc.c: Remove redundant static declarations.
2013-03-20 Dmitry Antipov <dmantipov@yandex.ru>
total_free_markers++;
}
+/* Verify properties of Lisp_Save_Value's representation
+ that are assumed here and elsewhere. */
+
+verify (SAVE_UNUSED == 0);
+verify ((SAVE_INTEGER | SAVE_POINTER | SAVE_OBJECT) >> SAVE_SLOT_BITS == 0);
+
/* Return a Lisp_Save_Value object with the data saved according to
- FMT. Format specifiers are `i' for an integer, `p' for a pointer
- and `o' for Lisp_Object. Up to 4 objects can be specified. */
+ DATA_TYPE. DATA_TYPE should be one of SAVE_TYPE_INT_INT, etc. */
Lisp_Object
-make_save_value (const char *fmt, ...)
+make_save_value (enum Lisp_Save_Type save_type, ...)
{
va_list ap;
- int len = strlen (fmt);
+ int i;
Lisp_Object val = allocate_misc (Lisp_Misc_Save_Value);
struct Lisp_Save_Value *p = XSAVE_VALUE (val);
- eassert (0 < len && len < 5);
- va_start (ap, fmt);
-
-#define INITX(index) \
- do { \
- if (len <= index) \
- p->type ## index = SAVE_UNUSED; \
- else \
- { \
- if (fmt[index] == 'i') \
- { \
- p->type ## index = SAVE_INTEGER; \
- p->data[index].integer = va_arg (ap, ptrdiff_t); \
- } \
- else if (fmt[index] == 'p') \
- { \
- p->type ## index = SAVE_POINTER; \
- p->data[index].pointer = va_arg (ap, void *); \
- } \
- else if (fmt[index] == 'o') \
- { \
- p->type ## index = SAVE_OBJECT; \
- p->data[index].object = va_arg (ap, Lisp_Object); \
- } \
- else \
- emacs_abort (); \
- } \
- } while (0)
+ eassert (0 < save_type
+ && (save_type < 1 << (SAVE_TYPE_BITS - 1)
+ || save_type == SAVE_TYPE_MEMORY));
+ p->save_type = save_type;
+ va_start (ap, save_type);
+ save_type &= ~ (1 << (SAVE_TYPE_BITS - 1));
+
+ for (i = 0; save_type; i++, save_type >>= SAVE_SLOT_BITS)
+ switch (save_type & ((1 << SAVE_SLOT_BITS) - 1))
+ {
+ case SAVE_POINTER:
+ p->data[i].pointer = va_arg (ap, void *);
+ break;
- INITX (0);
- INITX (1);
- INITX (2);
- INITX (3);
+ case SAVE_INTEGER:
+ p->data[i].integer = va_arg (ap, ptrdiff_t);
+ break;
-#undef INITX
+ case SAVE_OBJECT:
+ p->data[i].object = va_arg (ap, Lisp_Object);
+ break;
+
+ default:
+ emacs_abort ();
+ }
va_end (ap);
- p->area = 0;
return val;
}
{
Lisp_Object val = allocate_misc (Lisp_Misc_Save_Value);
struct Lisp_Save_Value *p = XSAVE_VALUE (val);
-
- p->area = 0;
- p->type0 = SAVE_POINTER;
+ p->save_type = SAVE_POINTER;
p->data[0].pointer = pointer;
- p->type1 = p->type2 = p->type3 = SAVE_UNUSED;
return val;
}
case Lisp_Misc_Save_Value:
XMISCANY (obj)->gcmarkbit = 1;
{
- register struct Lisp_Save_Value *ptr = XSAVE_VALUE (obj);
- /* If `area' is nonzero, `data[0].pointer' is the address
+ struct Lisp_Save_Value *ptr = XSAVE_VALUE (obj);
+ /* If `save_type' is zero, `data[0].pointer' is the address
of a memory area containing `data[1].integer' potential
Lisp_Objects. */
-#if GC_MARK_STACK
- if (ptr->area)
+ if (GC_MARK_STACK && ptr->save_type == SAVE_TYPE_MEMORY)
{
Lisp_Object *p = ptr->data[0].pointer;
ptrdiff_t nelt;
mark_maybe_object (*p);
}
else
-#endif /* GC_MARK_STACK */
{
/* Find Lisp_Objects in `data[N]' slots and mark them. */
- if (ptr->type0 == SAVE_OBJECT)
- mark_object (ptr->data[0].object);
- if (ptr->type1 == SAVE_OBJECT)
- mark_object (ptr->data[1].object);
- if (ptr->type2 == SAVE_OBJECT)
- mark_object (ptr->data[2].object);
- if (ptr->type3 == SAVE_OBJECT)
- mark_object (ptr->data[3].object);
+ int i;
+ for (i = 0; i < SAVE_VALUE_SLOTS; i++)
+ if (save_type (ptr, i) == SAVE_OBJECT)
+ mark_object (ptr->data[i].object);
}
}
break;
save_excursion_save (void)
{
return make_save_value
- ("oooo",
+ (SAVE_TYPE_OBJ_OBJ_OBJ_OBJ,
Fpoint_marker (),
/* Do not copy the mark if it points to nowhere. */
(XMARKER (BVAR (current_buffer, mark))->buffer
memcpy (buf, initial_buffer, used);
}
else
- XSAVE_POINTER (buf_save_value, 0) = buf = xrealloc (buf, bufsize);
+ {
+ buf = xrealloc (buf, bufsize);
+ set_save_pointer (buf_save_value, 0, buf);
+ }
p = buf + used;
}
to be signaled after decoding the text we read. */
nbytes = internal_condition_case_1
(read_non_regular,
- make_save_value ("iii", (ptrdiff_t) fd, inserted, trytry),
+ make_save_value (SAVE_TYPE_INT_INT_INT, (ptrdiff_t) fd,
+ inserted, trytry),
Qerror, read_non_regular_quit);
if (NILP (nbytes))
cache_data = xmalloc (sizeof *cache_data);
cache_data->ft_face = NULL;
cache_data->fc_charset = NULL;
- val = make_save_value ("pi", cache_data, 0);
+ val = make_save_value (SAVE_TYPE_PTR_INT, cache_data, 0);
cache = Fcons (Qnil, val);
Fputhash (key, cache, ft_face_cache);
}
return Qnil;
}
}
- XSAVE_INTEGER (val, 1)++;
+ set_save_integer (val, 1, XSAVE_INTEGER (val, 1) + 1);
size = XINT (AREF (entity, FONT_SIZE_INDEX));
if (size == 0)
size = pixel_size;
cache = ftfont_lookup_cache (val, FTFONT_CACHE_FOR_FACE);
eassert (CONSP (cache));
val = XCDR (cache);
- XSAVE_INTEGER (val, 1)--;
+ set_save_integer (val, 1, XSAVE_INTEGER (val, 1) - 1);
if (XSAVE_INTEGER (val, 1) == 0)
{
struct ftfont_cache_data *cache_data = XSAVE_POINTER (val, 0);
}
else if (CHAR_TABLE_P (binding))
map_char_table (map_keymap_char_table_item, Qnil, binding,
- make_save_value ("ppo", fun, data, args));
+ make_save_value (SAVE_TYPE_PTR_PTR_OBJ,
+ fun, data, args));
}
UNGCPRO;
return tail;
return num < lower ? lower : num <= upper ? num : upper;
}
+\f
+/* Forward declarations. */
+
+LISP_INLINE bool SAVE_VALUEP (Lisp_Object);
+LISP_INLINE struct Lisp_Save_Value *XSAVE_VALUE (Lisp_Object);
+
/* Extract a value or address from a Lisp_Object. */
#define XCONS(a) (eassert (CONSP (a)), \
#define XMISCTYPE(a) (XMISCANY (a)->type)
#define XMARKER(a) (eassert (MARKERP (a)), &(XMISC (a)->u_marker))
#define XOVERLAY(a) (eassert (OVERLAYP (a)), &(XMISC (a)->u_overlay))
-#define XSAVE_VALUE(a) (eassert (SAVE_VALUEP (a)), &(XMISC (a)->u_save_value))
/* Forwarding object types. */
would expose alloc.c internal details that we'd rather keep
private.
- This is a macro for use in static initializers, and a constant for
- visibility to GDB. The cast to ptrdiff_t ensures that
- the macro is signed. */
-static ptrdiff_t const STRING_BYTES_BOUND =
+ This is a macro for use in static initializers. The cast to
+ ptrdiff_t ensures that the macro is signed. */
#define STRING_BYTES_BOUND \
((ptrdiff_t) min (MOST_POSITIVE_FIXNUM, min (SIZE_MAX, PTRDIFF_MAX) - 1))
- STRING_BYTES_BOUND;
/* Mark STR as a unibyte string. */
#define STRING_SET_UNIBYTE(STR) \
SAVE_OBJECT
};
+/* Number of bits needed to store one of the above values. */
+enum { SAVE_SLOT_BITS = 2 };
+
+/* Number of slots in a save value where save_type is nonzero. */
+enum { SAVE_VALUE_SLOTS = 4 };
+
+/* Bit-width and values for struct Lisp_Save_Value's save_type member. */
+
+enum { SAVE_TYPE_BITS = SAVE_VALUE_SLOTS * SAVE_SLOT_BITS + 1 };
+
+enum Lisp_Save_Type
+ {
+ SAVE_TYPE_INT_INT = SAVE_INTEGER + (SAVE_INTEGER << SAVE_SLOT_BITS),
+ SAVE_TYPE_INT_INT_INT
+ = (SAVE_INTEGER + (SAVE_TYPE_INT_INT << SAVE_SLOT_BITS)),
+ SAVE_TYPE_OBJ_OBJ = SAVE_OBJECT + (SAVE_OBJECT << SAVE_SLOT_BITS),
+ SAVE_TYPE_OBJ_OBJ_OBJ = SAVE_OBJECT + (SAVE_TYPE_OBJ_OBJ << SAVE_SLOT_BITS),
+ SAVE_TYPE_OBJ_OBJ_OBJ_OBJ
+ = SAVE_OBJECT + (SAVE_TYPE_OBJ_OBJ_OBJ << SAVE_SLOT_BITS),
+ SAVE_TYPE_PTR_INT = SAVE_POINTER + (SAVE_INTEGER << SAVE_SLOT_BITS),
+ SAVE_TYPE_PTR_OBJ = SAVE_POINTER + (SAVE_OBJECT << SAVE_SLOT_BITS),
+ SAVE_TYPE_PTR_PTR = SAVE_POINTER + (SAVE_POINTER << SAVE_SLOT_BITS),
+ SAVE_TYPE_PTR_PTR_OBJ
+ = SAVE_POINTER + (SAVE_TYPE_PTR_OBJ << SAVE_SLOT_BITS),
+
+ /* This has an extra bit indicating it's raw memory. */
+ SAVE_TYPE_MEMORY = SAVE_TYPE_PTR_INT + (1 << (SAVE_TYPE_BITS - 1))
+ };
+
/* Special object used to hold a different values for later use.
This is mostly used to package C integers and pointers to call
If yon need to pass more than just one C pointer, you should
use make_save_value. This function allows you to pack up to
- 4 integers, pointers or Lisp_Objects and conveniently get them
- back with XSAVE_POINTER, XSAVE_INTEGER and XSAVE_OBJECT macros:
+ SAVE_VALUE_SLOTS integers, pointers or Lisp_Objects and
+ conveniently get them back with XSAVE_POINTER, XSAVE_INTEGER and
+ XSAVE_OBJECT macros:
...
struct my_data *md = get_my_data ();
- ptrdiff_t my_offset = get_my_offset ();
Lisp_Object my_object = get_my_object ();
record_unwind_protect
- (my_unwind, make_save_value ("pio", md, my_offset, my_object));
+ (my_unwind, make_save_value (SAVE_TYPE_PTR_OBJ, md, my_object));
...
Lisp_Object my_unwind (Lisp_Object arg)
{
struct my_data *md = XSAVE_POINTER (arg, 0);
- ptrdiff_t my_offset = XSAVE_INTEGER (arg, 1);
- Lisp_Object my_object = XSAVE_OBJECT (arg, 2);
+ Lisp_Object my_object = XSAVE_OBJECT (arg, 1);
...
}
If ENABLE_CHECKING is in effect, XSAVE_xxx macros do type checking of the
saved objects and raise eassert if type of the saved object doesn't match
the type which is extracted. In the example above, XSAVE_INTEGER (arg, 2)
- or XSAVE_OBJECT (arg, 1) are wrong because integer was saved in slot 1 and
- Lisp_Object was saved in slot 2 of ARG. */
+ or XSAVE_OBJECT (arg, 0) are wrong because nothing was saved in slot 2 and
+ Lisp_Object was saved in slot 1 of ARG. */
struct Lisp_Save_Value
{
ENUM_BF (Lisp_Misc_Type) type : 16; /* = Lisp_Misc_Save_Value */
unsigned gcmarkbit : 1;
- int spacer : 6;
- /* If `area' is nonzero, `data[0].pointer' is the address of a memory area
- containing `data[1].integer' potential Lisp_Objects. The rest of `data'
- fields are unused. */
- unsigned area : 1;
- /* If `area' is zero, `data[N]' may hold different objects which type is
- encoded in `typeN' fields as described by the anonymous enum above.
- E.g. if `type0' is SAVE_INTEGER, `data[0].integer' is in use. */
- unsigned type0 : 2;
- unsigned type1 : 2;
- unsigned type2 : 2;
- unsigned type3 : 2;
+ int spacer : 32 - (16 + 1 + SAVE_TYPE_BITS);
+
+ /* DATA[N] may hold up to SAVE_VALUE_SLOTS entries. The type of
+ V's Ith entry is given by save_type (V, I). E.g., if save_type
+ (V, 3) == SAVE_INTEGER, V->data[3].integer is in use.
+
+ If SAVE_TYPE == SAVE_TYPE_MEMORY, DATA[0].pointer is the address of
+ a memory area containing DATA[1].integer potential Lisp_Objects. */
+ ENUM_BF (Lisp_Save_Type) save_type : SAVE_TYPE_BITS;
union {
void *pointer;
ptrdiff_t integer;
Lisp_Object object;
- } data[4];
+ } data[SAVE_VALUE_SLOTS];
};
-/* Macro to set and extract Nth saved pointer. Type
- checking is ugly because it's used as an lvalue. */
+/* Return the type of V's Nth saved value. */
+LISP_INLINE int
+save_type (struct Lisp_Save_Value *v, int n)
+{
+ eassert (0 <= n && n < SAVE_VALUE_SLOTS);
+ return (v->save_type >> (SAVE_SLOT_BITS * n) & ((1 << SAVE_SLOT_BITS) - 1));
+}
+
+/* Get and set the Nth saved pointer. */
-#define XSAVE_POINTER(obj, n) \
- XSAVE_VALUE (obj)->data[(eassert (XSAVE_VALUE (obj)->type \
- ## n == SAVE_POINTER), n)].pointer
+LISP_INLINE void *
+XSAVE_POINTER (Lisp_Object obj, int n)
+{
+ eassert (save_type (XSAVE_VALUE (obj), n) == SAVE_POINTER);
+ return XSAVE_VALUE (obj)->data[n].pointer;;
+}
+LISP_INLINE void
+set_save_pointer (Lisp_Object obj, int n, void *val)
+{
+ eassert (save_type (XSAVE_VALUE (obj), n) == SAVE_POINTER);
+ XSAVE_VALUE (obj)->data[n].pointer = val;
+}
/* Likewise for the saved integer. */
-#define XSAVE_INTEGER(obj, n) \
- XSAVE_VALUE (obj)->data[(eassert (XSAVE_VALUE (obj)->type \
- ## n == SAVE_INTEGER), n)].integer
+LISP_INLINE ptrdiff_t
+XSAVE_INTEGER (Lisp_Object obj, int n)
+{
+ eassert (save_type (XSAVE_VALUE (obj), n) == SAVE_INTEGER);
+ return XSAVE_VALUE (obj)->data[n].integer;
+}
+LISP_INLINE void
+set_save_integer (Lisp_Object obj, int n, ptrdiff_t val)
+{
+ eassert (save_type (XSAVE_VALUE (obj), n) == SAVE_INTEGER);
+ XSAVE_VALUE (obj)->data[n].integer = val;
+}
-/* Macro to extract Nth saved object. This is never used as
- an lvalue, so we can do more convenient type checking. */
+/* Extract Nth saved object. */
-#define XSAVE_OBJECT(obj, n) \
- (eassert (XSAVE_VALUE (obj)->type ## n == SAVE_OBJECT), \
- XSAVE_VALUE (obj)->data[n].object)
+LISP_INLINE Lisp_Object
+XSAVE_OBJECT (Lisp_Object obj, int n)
+{
+ eassert (save_type (XSAVE_VALUE (obj), n) == SAVE_OBJECT);
+ return XSAVE_VALUE (obj)->data[n].object;
+}
/* A miscellaneous object, when it's on the free list. */
struct Lisp_Free
struct Lisp_Save_Value u_save_value;
};
+LISP_INLINE struct Lisp_Save_Value *
+XSAVE_VALUE (Lisp_Object a)
+{
+ eassert (SAVE_VALUEP (a));
+ return & XMISC (a)->u_save_value;
+}
+
/* Forwarding pointer to an int variable.
This is allowed only in the value cell of a symbol,
and it means that the symbol's value really lives in the
#define VECTORP(x) (VECTORLIKEP (x) && !(ASIZE (x) & PSEUDOVECTOR_FLAG))
#define OVERLAYP(x) (MISCP (x) && XMISCTYPE (x) == Lisp_Misc_Overlay)
#define MARKERP(x) (MISCP (x) && XMISCTYPE (x) == Lisp_Misc_Marker)
-#define SAVE_VALUEP(x) (MISCP (x) && XMISCTYPE (x) == Lisp_Misc_Save_Value)
+
+LISP_INLINE bool
+SAVE_VALUEP (Lisp_Object x)
+{
+ return MISCP (x) && XMISCTYPE (x) == Lisp_Misc_Save_Value;
+}
#define AUTOLOADP(x) (CONSP (x) && EQ (Qautoload, XCAR (x)))
extern Lisp_Object make_float (double);
extern void display_malloc_warning (void);
extern ptrdiff_t inhibit_garbage_collection (void);
-extern Lisp_Object make_save_value (const char *, ...);
+extern Lisp_Object make_save_value (enum Lisp_Save_Type, ...);
extern Lisp_Object make_save_pointer (void *);
extern Lisp_Object build_overlay (Lisp_Object, Lisp_Object, Lisp_Object);
extern void free_marker (Lisp_Object);
{ \
Lisp_Object arg_; \
buf = xmalloc ((nelt) * word_size); \
- arg_ = make_save_value ("pi", buf, nelt); \
- XSAVE_VALUE (arg_)->area = 1; \
+ arg_ = make_save_value (SAVE_TYPE_MEMORY, buf, nelt); \
sa_must_free = 1; \
record_unwind_protect (safe_alloca_unwind, arg_); \
} \
strout ("#<save-value ", -1, -1, printcharfun);
- if (v->area)
+ if (v->save_type == SAVE_TYPE_MEMORY)
{
ptrdiff_t amount = v->data[1].integer;
#if GC_MARK_STACK
- /* If GC_MARK_STACK, valid_lisp_object_p is quite reliable,
- and so we try to print up to 8 objects we have saved.
- Although valid_lisp_object_p is slow, this shouldn't be
- a real bottleneck because we do not use this code under
- normal circumstances. */
+ /* valid_lisp_object_p is reliable, so try to print up
+ to 8 saved objects. This code is rarely used, so
+ it's OK that valid_lisp_object_p is slow. */
int limit = min (amount, 8);
Lisp_Object *area = v->data[0].pointer;
#else /* not GC_MARK_STACK */
- /* If !GC_MARK_STACK, we have no reliable way to find
- whether Lisp_Object pointers points to an initialized
- objects, and so we do not ever trying to print them. */
+ /* There is no reliable way to determine whether the objects
+ are initialized, so do not try to print them. */
i = sprintf (buf, "with %"pD"d objects", amount);
strout (buf, i, i, printcharfun);
}
else
{
- /* Print each `data[N]' slot according to its type. */
-
-#define PRINTX(index) \
- do { \
- i = 0; \
- if (v->type ## index == SAVE_UNUSED) \
- i = sprintf (buf, "<unused>"); \
- else if (v->type ## index == SAVE_INTEGER) \
- i = sprintf (buf, "<integer %"pD"d>", v->data[index].integer); \
- else if (v->type ## index == SAVE_POINTER) \
- i = sprintf (buf, "<pointer %p>", v->data[index].pointer); \
- else /* SAVE_OBJECT */ \
- print_object (v->data[index].object, printcharfun, escapeflag); \
- if (i) \
- strout (buf, i, i, printcharfun); \
- } while (0)
-
- PRINTX (0);
- PRINTCHAR (' ');
- PRINTX (1);
- PRINTCHAR (' ');
- PRINTX (2);
- PRINTCHAR (' ');
- PRINTX (3);
+ /* Print each slot according to its type. */
+ int index;
+ for (index = 0; index < SAVE_VALUE_SLOTS; index++)
+ {
+ if (index)
+ PRINTCHAR (' ');
-#undef PRINTX
+ switch (save_type (v, index))
+ {
+ case SAVE_UNUSED:
+ i = sprintf (buf, "<unused>");
+ break;
+
+ case SAVE_POINTER:
+ i = sprintf (buf, "<pointer %p>",
+ v->data[index].pointer);
+ break;
+
+ case SAVE_INTEGER:
+ i = sprintf (buf, "<integer %"pD"d>",
+ v->data[index].integer);
+ break;
+
+ case SAVE_OBJECT:
+ print_object (v->data[index].object, printcharfun,
+ escapeflag);
+ continue;
+ }
+ strout (buf, i, i, printcharfun);
+ }
}
PRINTCHAR ('>');
}
#endif
record_unwind_protect (pop_down_menu,
- make_save_value ("pp", f, menu));
+ make_save_value (SAVE_TYPE_PTR_PTR, f, menu));
/* Help display under X won't work because XMenuActivate contains
a loop that doesn't give Emacs a chance to process it. */