1 /* Specifier implementation
2 Copyright (C) 1994, 1995 Board of Trustees, University of Illinois.
3 Copyright (C) 1995, 1996 Ben Wing.
4 Copyright (C) 1995 Sun Microsystems, Inc.
6 This file is part of SXEmacs
8 SXEmacs is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 SXEmacs is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22 /* Synched up with: Not in FSF. */
24 /* Design by Ben Wing;
25 Original version by Chuck Thompson;
26 rewritten by Ben Wing;
27 Magic specifiers by Kirill Katsnelson;
37 #include "specifier.h"
42 Lisp_Object Qspecifierp;
43 Lisp_Object Qremove_tag_set_prepend, Qremove_tag_set_append;
44 Lisp_Object Qremove_locale, Qremove_locale_type;
46 Lisp_Object Qconsole_type, Qdevice_class;
48 Lisp_Object Qspecifier_syntax_error;
49 Lisp_Object Qspecifier_argument_error;
50 Lisp_Object Qspecifier_change_error;
52 static Lisp_Object Vuser_defined_tags;
54 typedef struct specifier_type_entry specifier_type_entry;
55 struct specifier_type_entry {
57 struct specifier_methods *meths;
61 Dynarr_declare(specifier_type_entry);
62 } specifier_type_entry_dynarr;
64 static specifier_type_entry_dynarr *the_specifier_type_entry_dynarr;
66 static const struct lrecord_description ste_description_1[] = {
67 {XD_LISP_OBJECT, offsetof(specifier_type_entry, symbol)},
68 {XD_STRUCT_PTR, offsetof(specifier_type_entry, meths), 1,
69 &specifier_methods_description},
73 static const struct struct_description ste_description = {
74 sizeof(specifier_type_entry),
78 static const struct lrecord_description sted_description_1[] = {
79 XD_DYNARR_DESC(specifier_type_entry_dynarr, &ste_description),
83 static const struct struct_description sted_description = {
84 sizeof(specifier_type_entry_dynarr),
88 static Lisp_Object Vspecifier_type_list;
90 static Lisp_Object Vcached_specifiers;
91 /* Do NOT mark through this, or specifiers will never be GC'd. */
92 static Lisp_Object Vall_specifiers;
94 static Lisp_Object Vunlock_ghost_specifiers;
96 /* #### The purpose of this is to check for inheritance loops
97 in specifiers that can inherit from other specifiers, but it's
100 #### Look into this for 19.14. */
101 /* static Lisp_Object_dynarr current_specifiers; */
103 static void recompute_cached_specifier_everywhere(Lisp_Object specifier);
105 EXFUN(Fspecifier_specs, 4);
106 EXFUN(Fremove_specifier, 4);
108 /************************************************************************/
109 /* Specifier object methods */
110 /************************************************************************/
112 /* Remove dead objects from the specified assoc list. */
114 static Lisp_Object cleanup_assoc_list(Lisp_Object list)
116 Lisp_Object loop, prev, retval;
118 loop = retval = list;
121 while (!NILP(loop)) {
122 Lisp_Object entry = XCAR(loop);
123 Lisp_Object key = XCAR(entry);
125 /* remember, dead windows can become alive again. */
126 if (!WINDOWP(key) && object_dead_p(key)) {
128 /* Removing the head. */
129 retval = XCDR(retval);
131 Fsetcdr(prev, XCDR(loop));
142 /* Remove dead objects from the various lists so that they
143 don't keep getting marked as long as this specifier exists and
144 therefore wasting memory. */
146 void cleanup_specifiers(void)
150 for (rest = Vall_specifiers;
151 !NILP(rest); rest = XSPECIFIER(rest)->next_specifier) {
152 Lisp_Specifier *sp = XSPECIFIER(rest);
153 /* This effectively changes the specifier specs.
154 However, there's no need to call
155 recompute_cached_specifier_everywhere() or the
156 after-change methods because the only specs we
157 are removing are for dead objects, and they can
158 never have any effect on the specifier values:
159 specifiers can only be instantiated over live
160 objects, and you can't derive a dead object
162 sp->device_specs = cleanup_assoc_list(sp->device_specs);
163 sp->frame_specs = cleanup_assoc_list(sp->frame_specs);
164 sp->buffer_specs = cleanup_assoc_list(sp->buffer_specs);
165 /* windows are handled specially because dead windows
166 can be resurrected */
170 void kill_specifier_buffer_locals(Lisp_Object buffer)
174 for (rest = Vall_specifiers;
175 !NILP(rest); rest = XSPECIFIER(rest)->next_specifier) {
176 Lisp_Specifier *sp = XSPECIFIER(rest);
178 /* Make sure we're actually going to be changing something.
179 Fremove_specifier() always calls
180 recompute_cached_specifier_everywhere() (#### but should
181 be smarter about this). */
182 if (!NILP(assq_no_quit(buffer, sp->buffer_specs)))
183 Fremove_specifier(rest, buffer, Qnil, Qnil);
187 static Lisp_Object mark_specifier(Lisp_Object obj)
189 Lisp_Specifier *specifier = XSPECIFIER(obj);
191 mark_object(specifier->global_specs);
192 mark_object(specifier->device_specs);
193 mark_object(specifier->frame_specs);
194 mark_object(specifier->window_specs);
195 mark_object(specifier->buffer_specs);
196 mark_object(specifier->magic_parent);
197 mark_object(specifier->fallback);
198 if (!GHOST_SPECIFIER_P(XSPECIFIER(obj)))
199 MAYBE_SPECMETH(specifier, mark, (obj));
203 /* The idea here is that the specifier specs point to locales
204 (windows, buffers, frames, and devices), and we want to make sure
205 that the specs disappear automatically when the associated locale
206 is no longer in use. For all but windows, "no longer in use"
207 corresponds exactly to when the object is deleted (non-deleted
208 objects are always held permanently in special lists, and deleted
209 objects are never on these lists and never reusable). To handle
210 this, we just have cleanup_specifiers() called periodically
211 (at the beginning of garbage collection); it removes all dead
214 For windows, however, it's trickier because dead objects can be
215 converted to live ones again if the dead object is in a window
216 configuration. Therefore, for windows, "no longer in use"
217 corresponds to when the window object is garbage-collected.
218 We now use weak lists for this purpose.
222 void prune_specifiers(void)
224 Lisp_Object rest, prev = Qnil;
226 for (rest = Vall_specifiers;
227 !NILP(rest); rest = XSPECIFIER(rest)->next_specifier) {
228 if (!marked_p(rest)) {
229 Lisp_Specifier *sp = XSPECIFIER(rest);
230 /* A bit of assertion that we're removing both parts of the
231 magic one altogether */
232 #if !defined HAVE_BDWGC || !defined EF_USE_BDWGC
233 # define MARKED_P(a) marked_p(a)
235 # define MARKED_P(a) 1
237 assert(!MAGIC_SPECIFIER_P(sp)
238 || ( BODILY_SPECIFIER_P(sp) &&
239 MARKED_P(sp->fallback) )
240 || ( GHOST_SPECIFIER_P(sp) &&
241 MARKED_P(sp->magic_parent)));
243 /* This specifier is garbage. Remove it from the list. */
245 Vall_specifiers = sp->next_specifier;
247 XSPECIFIER(prev)->next_specifier =
255 print_specifier(Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
257 Lisp_Specifier *sp = XSPECIFIER(obj);
258 int count = specpdl_depth();
259 Lisp_Object the_specs;
262 error("printing unreadable object #<%s-specifier 0x%x>",
263 sp->methods->name, sp->header.uid);
265 write_fmt_string(printcharfun, "#<%s-specifier global=", sp->methods->name);
266 specbind(Qprint_string_length, make_int(100));
267 specbind(Qprint_length, make_int(5));
268 the_specs = Fspecifier_specs(obj, Qglobal, Qnil, Qnil);
270 /* there are no global specs */
271 write_c_string("<unspecified>", printcharfun);
273 print_internal(the_specs, printcharfun, 1);
274 if (!NILP(sp->fallback)) {
275 write_c_string(" fallback=", printcharfun);
276 print_internal(sp->fallback, printcharfun, escapeflag);
278 unbind_to(count, Qnil);
279 write_fmt_str(printcharfun," 0x%x>", sp->header.uid);
282 static void finalize_specifier(void *header, int for_disksave)
284 Lisp_Specifier *sp = (Lisp_Specifier *) header;
285 /* don't be snafued by the disksave finalization. */
286 if (!for_disksave && !GHOST_SPECIFIER_P(sp) && sp->caching) {
292 static int specifier_equal(Lisp_Object obj1, Lisp_Object obj2, int depth)
294 Lisp_Specifier *s1 = XSPECIFIER(obj1);
295 Lisp_Specifier *s2 = XSPECIFIER(obj2);
297 Lisp_Object old_inhibit_quit = Vinhibit_quit;
299 /* This function can be called from within redisplay.
300 internal_equal can trigger a quit. That leads to Bad Things. */
305 (s1->methods == s2->methods &&
306 internal_equal(s1->global_specs, s2->global_specs, depth) &&
307 internal_equal(s1->device_specs, s2->device_specs, depth) &&
308 internal_equal(s1->frame_specs, s2->frame_specs, depth) &&
309 internal_equal(s1->window_specs, s2->window_specs, depth) &&
310 internal_equal(s1->buffer_specs, s2->buffer_specs, depth) &&
311 internal_equal(s1->fallback, s2->fallback, depth));
313 if (retval && HAS_SPECMETH_P(s1, equal))
314 retval = SPECMETH(s1, equal, (obj1, obj2, depth - 1));
316 Vinhibit_quit = old_inhibit_quit;
320 static unsigned long specifier_hash(Lisp_Object obj, int depth)
322 Lisp_Specifier *s = XSPECIFIER(obj);
324 /* specifier hashing is a bit problematic because there are so
325 many places where data can be stored. We pick what are perhaps
326 the most likely places where interesting stuff will be. */
327 return HASH5((HAS_SPECMETH_P(s, hash) ?
328 SPECMETH(s, hash, (obj, depth)) : 0),
329 (unsigned long)s->methods,
330 internal_hash(s->global_specs, depth + 1),
331 internal_hash(s->frame_specs, depth + 1),
332 internal_hash(s->buffer_specs, depth + 1));
336 aligned_sizeof_specifier(size_t specifier_type_specific_size)
338 return ALIGN_SIZE(offsetof(Lisp_Specifier, data)
339 + specifier_type_specific_size, ALIGNOF(max_align_t));
342 static size_t sizeof_specifier(const void *header)
344 const Lisp_Specifier *p = (const Lisp_Specifier *)header;
345 return aligned_sizeof_specifier(GHOST_SPECIFIER_P(p)
346 ? 0 : p->methods->extra_data_size);
349 static const struct lrecord_description specifier_methods_description_1[] = {
350 {XD_LISP_OBJECT, offsetof(struct specifier_methods, predicate_symbol)},
354 const struct struct_description specifier_methods_description = {
355 sizeof(struct specifier_methods),
356 specifier_methods_description_1
359 static const struct lrecord_description specifier_caching_description_1[] = {
363 static const struct struct_description specifier_caching_description = {
364 sizeof(struct specifier_caching),
365 specifier_caching_description_1
368 static const struct lrecord_description specifier_description[] = {
369 {XD_STRUCT_PTR, offsetof(Lisp_Specifier, methods), 1,
370 &specifier_methods_description},
371 {XD_LO_LINK, offsetof(Lisp_Specifier, next_specifier)},
372 {XD_LISP_OBJECT, offsetof(Lisp_Specifier, global_specs)},
373 {XD_LISP_OBJECT, offsetof(Lisp_Specifier, device_specs)},
374 {XD_LISP_OBJECT, offsetof(Lisp_Specifier, frame_specs)},
375 {XD_LISP_OBJECT, offsetof(Lisp_Specifier, window_specs)},
376 {XD_LISP_OBJECT, offsetof(Lisp_Specifier, buffer_specs)},
377 {XD_STRUCT_PTR, offsetof(Lisp_Specifier, caching), 1,
378 &specifier_caching_description},
379 {XD_LISP_OBJECT, offsetof(Lisp_Specifier, magic_parent)},
380 {XD_LISP_OBJECT, offsetof(Lisp_Specifier, fallback)},
384 const struct lrecord_description specifier_empty_extra_description[] = {
388 DEFINE_LRECORD_SEQUENCE_IMPLEMENTATION("specifier", specifier,
389 mark_specifier, print_specifier,
391 specifier_equal, specifier_hash,
392 specifier_description,
393 sizeof_specifier, Lisp_Specifier);
395 /************************************************************************/
396 /* Creating specifiers */
397 /************************************************************************/
399 static struct specifier_methods *decode_specifier_type(Lisp_Object type,
404 for (i = 0; i < Dynarr_length(the_specifier_type_entry_dynarr); i++) {
407 Dynarr_at(the_specifier_type_entry_dynarr, i).symbol))
408 return Dynarr_at(the_specifier_type_entry_dynarr,
412 maybe_signal_type_error(Qspecifier_argument_error,
413 "Invalid specifier type", type, Qspecifier,
419 static int valid_specifier_type_p(Lisp_Object type)
421 return decode_specifier_type(type, ERROR_ME_NOT) != 0;
424 DEFUN("valid-specifier-type-p", Fvalid_specifier_type_p, 1, 1, 0, /*
425 Given a SPECIFIER-TYPE, return non-nil if it is valid.
426 Valid types are 'generic, 'integer, 'boolean, 'color, 'font, 'image,
427 'face-boolean, and 'toolbar.
431 return valid_specifier_type_p(specifier_type) ? Qt : Qnil;
434 DEFUN("specifier-type-list", Fspecifier_type_list, 0, 0, 0, /*
435 Return a list of valid specifier types.
439 return Fcopy_sequence(Vspecifier_type_list);
443 add_entry_to_specifier_type_list(Lisp_Object symbol,
444 struct specifier_methods *meths)
446 struct specifier_type_entry entry;
448 entry.symbol = symbol;
450 Dynarr_add(the_specifier_type_entry_dynarr, entry);
451 Vspecifier_type_list = Fcons(symbol, Vspecifier_type_list);
455 make_specifier_internal(struct specifier_methods *spec_meths,
456 size_t data_size, int call_create_meth)
458 Lisp_Object specifier;
459 Lisp_Specifier *sp = (Lisp_Specifier *)
460 alloc_lcrecord(aligned_sizeof_specifier(data_size),
463 sp->methods = spec_meths;
464 sp->global_specs = Qnil;
465 sp->device_specs = Qnil;
466 sp->frame_specs = Qnil;
467 sp->window_specs = make_weak_list(WEAK_LIST_KEY_ASSOC);
468 sp->buffer_specs = Qnil;
470 sp->magic_parent = Qnil;
472 sp->next_specifier = Vall_specifiers;
474 XSETSPECIFIER(specifier, sp);
475 Vall_specifiers = specifier;
477 if (call_create_meth) {
480 MAYBE_SPECMETH(XSPECIFIER(specifier), create, (specifier));
486 static Lisp_Object make_specifier(struct specifier_methods *meths)
488 return make_specifier_internal(meths, meths->extra_data_size, 1);
491 Lisp_Object make_magic_specifier(Lisp_Object type)
493 /* This function can GC */
494 struct specifier_methods *meths = decode_specifier_type(type, ERROR_ME);
495 Lisp_Object bodily, ghost;
498 bodily = make_specifier(meths);
500 ghost = make_specifier_internal(meths, 0, 0);
503 /* Connect guys together */
504 XSPECIFIER(bodily)->magic_parent = Qt;
505 XSPECIFIER(bodily)->fallback = ghost;
506 XSPECIFIER(ghost)->magic_parent = bodily;
511 DEFUN("make-specifier", Fmake_specifier, 1, 1, 0, /*
512 Return a new specifier object of type TYPE.
514 A specifier is an object that can be used to keep track of a property
515 whose value can be per-buffer, per-window, per-frame, or per-device,
516 and can further be restricted to a particular console-type or
517 device-class. Specifiers are used, for example, for the various
518 built-in properties of a face; this allows a face to have different
519 values in different frames, buffers, etc.
521 When speaking of the value of a specifier, it is important to
522 distinguish between the *setting* of a specifier, called an
523 \"instantiator\", and the *actual value*, called an \"instance\". You
524 put various possible instantiators (i.e. settings) into a specifier
525 and associate them with particular locales (buffer, window, frame,
526 device, global), and then the instance (i.e. actual value) is
527 retrieved in a specific domain (window, frame, device) by looking
528 through the possible instantiators (i.e. settings). This process is
529 called \"instantiation\".
531 To put settings into a specifier, use `set-specifier', or the
532 lower-level functions `add-spec-to-specifier' and
533 `add-spec-list-to-specifier'. You can also temporarily bind a setting
534 to a specifier using `let-specifier'. To retrieve settings, use
535 `specifier-specs', or its lower-level counterpart
536 `specifier-spec-list'. To determine the actual value, use
537 `specifier-instance'.
539 For more information, see `set-specifier', `specifier-instance',
540 `specifier-specs', and `add-spec-to-specifier'; or, for a detailed
541 description of specifiers, including how exactly the instantiation
542 process works, see the chapter on specifiers in the SXEmacs Lisp
545 TYPE specifies the particular type of specifier, and should be one of
546 the symbols 'generic, 'integer, 'natnum, 'boolean, 'color, 'font,
547 'image, 'face-boolean, 'display-table, 'gutter, 'gutter-size,
548 'gutter-visible or 'toolbar.
550 For more information on particular types of specifiers, see the