Mercurial > hg > xemacs-beta
comparison src/specifier.c @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
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. | |
5 | |
6 This file is part of XEmacs. | |
7 | |
8 XEmacs is free software; you can redistribute it and/or modify it | |
9 under the terms of the GNU General Public License as published by the | |
10 Free Software Foundation; either version 2, or (at your option) any | |
11 later version. | |
12 | |
13 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 for more details. | |
17 | |
18 You should have received a copy of the GNU General Public License | |
19 along with XEmacs; see the file COPYING. If not, write to | |
20 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
21 Boston, MA 02111-1307, USA. */ | |
22 | |
23 /* Synched up with: Not in FSF. */ | |
24 | |
25 /* Design by Ben Wing; | |
26 Original version by Chuck Thompson; | |
27 rewritten by Ben Wing */ | |
28 | |
29 #include <config.h> | |
30 #include "lisp.h" | |
31 | |
32 #include "buffer.h" | |
33 #include "device.h" | |
34 #include "frame.h" | |
35 #include "opaque.h" | |
36 #include "specifier.h" | |
37 #include "window.h" | |
38 | |
39 Lisp_Object Qspecifierp; | |
40 Lisp_Object Qprepend, Qappend, Qremove_tag_set_prepend, Qremove_tag_set_append; | |
41 Lisp_Object Qremove_locale, Qremove_locale_type, Qremove_all; | |
42 Lisp_Object Qfallback; | |
43 | |
44 /* Qinteger, Qboolean, Qgeneric defined in general.c. */ | |
45 Lisp_Object Qnatnum; | |
46 | |
47 Lisp_Object Qconsole_type, Qdevice_class; | |
48 | |
49 Lisp_Object Vuser_defined_tags; | |
50 | |
51 MAC_DEFINE (struct Lisp_Specifier *, MTspecmeth_or_given) | |
52 MAC_DEFINE (struct Lisp_Specifier *, MTspecifier_data) | |
53 | |
54 struct specifier_type_entry | |
55 { | |
56 Lisp_Object symbol; | |
57 struct specifier_methods *meths; | |
58 }; | |
59 | |
60 typedef struct specifier_type_entry_dynarr_type | |
61 { | |
62 Dynarr_declare (struct specifier_type_entry); | |
63 } specifier_type_entry_dynarr; | |
64 | |
65 specifier_type_entry_dynarr *the_specifier_type_entry_dynarr; | |
66 | |
67 Lisp_Object Vspecifier_type_list; | |
68 | |
69 Lisp_Object Vcached_specifiers; | |
70 /* Do NOT mark through this, or specifiers will never be GC'd. */ | |
71 Lisp_Object Vall_specifiers; | |
72 | |
73 /* #### The purpose of this is to check for inheritance loops | |
74 in specifiers that can inherit from other specifiers, but it's | |
75 not yet implemented. | |
76 | |
77 #### Look into this for 19.14. */ | |
78 lisp_dynarr current_specifiers; | |
79 | |
80 static void recompute_cached_specifier_everywhere (Lisp_Object specifier); | |
81 | |
82 | |
83 /************************************************************************/ | |
84 /* Specifier object methods */ | |
85 /************************************************************************/ | |
86 | |
87 static Lisp_Object mark_specifier (Lisp_Object, void (*) (Lisp_Object)); | |
88 static void print_specifier (Lisp_Object, Lisp_Object, int); | |
89 static int specifier_equal (Lisp_Object, Lisp_Object, int depth); | |
90 static unsigned long specifier_hash (Lisp_Object obj, int depth); | |
91 static unsigned int sizeof_specifier (CONST void *header); | |
92 static void finalize_specifier (void *header, int for_disksave); | |
93 DEFINE_LRECORD_SEQUENCE_IMPLEMENTATION ("specifier", specifier, | |
94 mark_specifier, print_specifier, | |
95 finalize_specifier, | |
96 specifier_equal, specifier_hash, | |
97 sizeof_specifier, | |
98 struct Lisp_Specifier); | |
99 | |
100 /* Remove dead objects from the specified assoc list. */ | |
101 | |
102 static Lisp_Object | |
103 cleanup_assoc_list (Lisp_Object list) | |
104 { | |
105 Lisp_Object loop, prev, retval; | |
106 | |
107 loop = retval = list; | |
108 prev = Qnil; | |
109 | |
110 while (!NILP (loop)) | |
111 { | |
112 Lisp_Object entry = XCAR (loop); | |
113 Lisp_Object key = XCAR (entry); | |
114 | |
115 /* remember, dead windows can become alive again. */ | |
116 if (!WINDOWP (key) && object_dead_p (key)) | |
117 { | |
118 if (NILP (prev)) | |
119 { | |
120 /* Removing the head. */ | |
121 retval = XCDR (retval); | |
122 } | |
123 else | |
124 { | |
125 Fsetcdr (prev, XCDR (loop)); | |
126 } | |
127 } | |
128 else | |
129 prev = loop; | |
130 | |
131 loop = XCDR (loop); | |
132 } | |
133 | |
134 return retval; | |
135 } | |
136 | |
137 /* Remove dead objects from the various lists so that they | |
138 don't keep getting marked as long as this specifier exists and | |
139 therefore wasting memory. */ | |
140 | |
141 void | |
142 cleanup_specifiers (void) | |
143 { | |
144 Lisp_Object rest; | |
145 | |
146 for (rest = Vall_specifiers; | |
147 !NILP (rest); | |
148 rest = XSPECIFIER (rest)->next_specifier) | |
149 { | |
150 struct Lisp_Specifier *sp = XSPECIFIER (rest); | |
151 /* This effectively changes the specifier specs. | |
152 However, there's no need to call | |
153 recompute_cached_specifier_everywhere() or the | |
154 after-change methods because the only specs we | |
155 are removing are for dead objects, and they can | |
156 never have any effect on the specifier values: | |
157 specifiers can only be instantiated over live | |
158 objects, and you can't derive a dead object | |
159 from a live one. */ | |
160 sp->device_specs = cleanup_assoc_list (sp->device_specs); | |
161 sp->frame_specs = cleanup_assoc_list (sp->frame_specs); | |
162 sp->buffer_specs = cleanup_assoc_list (sp->buffer_specs); | |
163 /* windows are handled specially because dead windows | |
164 can be resurrected */ | |
165 } | |
166 } | |
167 | |
168 void | |
169 kill_specifier_buffer_locals (Lisp_Object buffer) | |
170 { | |
171 Lisp_Object rest; | |
172 | |
173 for (rest = Vall_specifiers; | |
174 !NILP (rest); | |
175 rest = XSPECIFIER (rest)->next_specifier) | |
176 { | |
177 struct Lisp_Specifier *sp = XSPECIFIER (rest); | |
178 | |
179 /* Make sure we're actually going to be changing something. | |
180 Fremove_specifier() always calls | |
181 recompute_cached_specifier_everywhere() (#### but should | |
182 be smarter about this). */ | |
183 if (!NILP (assq_no_quit (buffer, sp->buffer_specs))) | |
184 Fremove_specifier (rest, buffer, Qnil, Qnil); | |
185 } | |
186 } | |
187 | |
188 static Lisp_Object | |
189 mark_specifier (Lisp_Object obj, void (*markobj) (Lisp_Object)) | |
190 { | |
191 struct Lisp_Specifier *specifier = XSPECIFIER (obj); | |
192 | |
193 ((markobj) (specifier->global_specs)); | |
194 ((markobj) (specifier->device_specs)); | |
195 ((markobj) (specifier->frame_specs)); | |
196 ((markobj) (specifier->window_specs)); | |
197 ((markobj) (specifier->buffer_specs)); | |
198 ((markobj) (specifier->fallback)); | |
199 MAYBE_SPECMETH (specifier, mark, (obj, markobj)); | |
200 return Qnil; | |
201 } | |
202 | |
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 | |
212 objects. | |
213 | |
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. | |
219 | |
220 */ | |
221 | |
222 void | |
223 prune_specifiers (int (*obj_marked_p) (Lisp_Object)) | |
224 { | |
225 Lisp_Object rest, prev = Qnil; | |
226 | |
227 for (rest = Vall_specifiers; | |
228 !GC_NILP (rest); | |
229 rest = XSPECIFIER (rest)->next_specifier) | |
230 { | |
231 if (! ((*obj_marked_p) (rest))) | |
232 { | |
233 /* This specifier is garbage. Remove it from the list. */ | |
234 if (GC_NILP (prev)) | |
235 Vall_specifiers = XSPECIFIER (rest)->next_specifier; | |
236 else | |
237 XSPECIFIER (prev)->next_specifier = | |
238 XSPECIFIER (rest)->next_specifier; | |
239 } | |
240 } | |
241 } | |
242 | |
243 static void | |
244 print_specifier (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag) | |
245 { | |
246 struct Lisp_Specifier *sp = XSPECIFIER (obj); | |
247 char buf[100]; | |
248 int count = specpdl_depth (); | |
249 Lisp_Object the_specs; | |
250 | |
251 if (print_readably) | |
252 error ("printing unreadable object #<%s-specifier 0x%x>", | |
253 sp->methods->name, sp->header.uid); | |
254 | |
255 sprintf (buf, "#<%s-specifier global=", sp->methods->name); | |
256 write_c_string (buf, printcharfun); | |
257 specbind (Qprint_string_length, make_int (100)); | |
258 specbind (Qprint_length, make_int (5)); | |
259 the_specs = Fspecifier_specs (obj, Qglobal, Qnil, Qnil); | |
260 if (NILP (the_specs)) | |
261 /* there are no global specs */ | |
262 write_c_string ("<unspecified>", printcharfun); | |
263 else | |
264 print_internal (the_specs, printcharfun, 1); | |
265 if (!NILP (sp->fallback)) | |
266 { | |
267 write_c_string (" fallback=", printcharfun); | |
268 print_internal (sp->fallback, printcharfun, escapeflag); | |
269 } | |
270 unbind_to (count, Qnil); | |
271 sprintf (buf, " 0x%x>", sp->header.uid); | |
272 write_c_string (buf, printcharfun); | |
273 } | |
274 | |
275 static void | |
276 finalize_specifier (void *header, int for_disksave) | |
277 { | |
278 struct Lisp_Specifier *sp = (struct Lisp_Specifier *) header; | |
279 /* don't be snafued by the disksave finalization. */ | |
280 if (!for_disksave && sp->caching) | |
281 { | |
282 xfree (sp->caching); | |
283 sp->caching = 0; | |
284 } | |
285 } | |
286 | |
287 static int | |
288 specifier_equal (Lisp_Object o1, Lisp_Object o2, int depth) | |
289 { | |
290 struct Lisp_Specifier *s1 = XSPECIFIER (o1); | |
291 struct Lisp_Specifier *s2 = XSPECIFIER (o2); | |
292 int retval; | |
293 Lisp_Object old_inhibit_quit = Vinhibit_quit; | |
294 | |
295 /* This function can be called from within redisplay. | |
296 internal_equal can trigger a quit. That leads to Bad Things. */ | |
297 Vinhibit_quit = Qt; | |
298 | |
299 depth++; | |
300 if (s1->methods != s2->methods || | |
301 !internal_equal (s1->global_specs, s2->global_specs, depth) || | |
302 !internal_equal (s1->device_specs, s2->device_specs, depth) || | |
303 !internal_equal (s1->frame_specs, s2->frame_specs, depth) || | |
304 !internal_equal (s1->window_specs, s2->window_specs, depth) || | |
305 !internal_equal (s1->buffer_specs, s2->buffer_specs, depth) || | |
306 !SPECMETH_OR_GIVEN (s1, equal, (o1, o2, depth - 1), 1)) | |
307 retval = 0; | |
308 else | |
309 retval = 1; | |
310 | |
311 Vinhibit_quit = old_inhibit_quit; | |
312 return retval; | |
313 } | |
314 | |
315 static unsigned long | |
316 specifier_hash (Lisp_Object obj, int depth) | |
317 { | |
318 struct Lisp_Specifier *s = XSPECIFIER (obj); | |
319 | |
320 /* specifier hashing is a bit problematic because there are so | |
321 many places where data can be stored. We pick what are perhaps | |
322 the most likely places where interesting stuff will be. */ | |
323 return HASH5 (SPECMETH_OR_GIVEN (s, hash, (obj, depth), 0), | |
324 (unsigned long) s->methods, | |
325 internal_hash (s->global_specs, depth + 1), | |
326 internal_hash (s->frame_specs, depth + 1), | |
327 internal_hash (s->buffer_specs, depth + 1)); | |
328 } | |
329 | |
330 static unsigned int | |
331 sizeof_specifier (CONST void *header) | |
332 { | |
333 CONST struct Lisp_Specifier *p = (CONST struct Lisp_Specifier *) header; | |
334 return sizeof (*p) + p->methods->extra_data_size - 1; | |
335 } | |
336 | |
337 | |
338 /************************************************************************/ | |
339 /* Creating specifiers */ | |
340 /************************************************************************/ | |
341 | |
342 static struct specifier_methods * | |
343 decode_specifier_type (Lisp_Object type, Error_behavior errb) | |
344 { | |
345 int i; | |
346 | |
347 for (i = 0; i < Dynarr_length (the_specifier_type_entry_dynarr); i++) | |
348 { | |
349 if (EQ (type, Dynarr_at (the_specifier_type_entry_dynarr, i).symbol)) | |
350 return Dynarr_at (the_specifier_type_entry_dynarr, i).meths; | |
351 } | |
352 | |
353 maybe_signal_simple_error ("Invalid specifier type", type, | |
354 Qspecifier, errb); | |
355 | |
356 return 0; | |
357 } | |
358 | |
359 static int | |
360 valid_specifier_type_p (Lisp_Object type) | |
361 { | |
362 if (decode_specifier_type (type, ERROR_ME_NOT)) | |
363 return 1; | |
364 return 0; | |
365 } | |
366 | |
367 DEFUN ("valid-specifier-type-p", Fvalid_specifier_type_p, | |
368 Svalid_specifier_type_p, 1, 1, 0 /* | |
369 Given a SPECIFIER-TYPE, return non-nil if it is valid. | |
370 Valid types are 'generic, 'integer, boolean, 'color, 'font, 'image, | |
371 'face-boolean, and 'toolbar. | |
372 */ ) | |
373 (specifier_type) | |
374 Lisp_Object specifier_type; | |
375 { | |
376 if (valid_specifier_type_p (specifier_type)) | |
377 return Qt; | |
378 else | |
379 return Qnil; | |
380 } | |
381 | |
382 DEFUN ("specifier-type-list", Fspecifier_type_list, Sspecifier_type_list, | |
383 0, 0, 0 /* | |
384 Return a list of valid specifier types. | |
385 */ ) | |
386 () | |
387 { | |
388 return Fcopy_sequence (Vspecifier_type_list); | |
389 } | |
390 | |
391 void | |
392 add_entry_to_specifier_type_list (Lisp_Object symbol, | |
393 struct specifier_methods *meths) | |
394 { | |
395 struct specifier_type_entry entry; | |
396 | |
397 entry.symbol = symbol; | |
398 entry.meths = meths; | |
399 Dynarr_add (the_specifier_type_entry_dynarr, entry); | |
400 Vspecifier_type_list = Fcons (symbol, Vspecifier_type_list); | |
401 } | |
402 | |
403 static Lisp_Object | |
404 make_specifier (struct specifier_methods *spec_meths) | |
405 { | |
406 struct Lisp_Specifier *sp; | |
407 Lisp_Object specifier = Qnil; | |
408 struct gcpro gcpro1; | |
409 | |
410 sp = alloc_lcrecord (sizeof (struct Lisp_Specifier) + | |
411 spec_meths->extra_data_size - 1, lrecord_specifier); | |
412 | |
413 sp->methods = spec_meths; | |
414 sp->global_specs = Qnil; | |
415 sp->device_specs = Qnil; | |
416 sp->frame_specs = Qnil; | |
417 sp->window_specs = make_weak_list (WEAK_LIST_KEY_ASSOC); | |
418 sp->buffer_specs = Qnil; | |
419 sp->fallback = Qnil; | |
420 sp->caching = 0; | |
421 sp->next_specifier = Vall_specifiers; | |
422 | |
423 XSETSPECIFIER (specifier, sp); | |
424 Vall_specifiers = specifier; | |
425 | |
426 GCPRO1 (specifier); | |
427 MAYBE_SPECMETH (XSPECIFIER (specifier), create, (specifier)); | |
428 UNGCPRO; | |
429 return specifier; | |
430 } | |
431 | |
432 DEFUN ("make-specifier", Fmake_specifier, Smake_specifier, 1, 1, 0 /* | |
433 Create a new specifier. | |
434 | |
435 A specifier is an object that can be used to keep track of a property | |
436 whose value can be per-buffer, per-window, per-frame, or per-device, | |
437 and can further be restricted to a particular console-type or device-class. | |
438 Specifiers are used, for example, for the various built-in properties of a | |
439 face; this allows a face to have different values in different frames, | |
440 buffers, etc. For more information, see `specifier-instance', | |
441 `specifier-specs', and `add-spec-to-specifier'; or, for a detailed | |
442 description of specifiers, including how they are instantiated over a | |
443 particular domain (i.e. how their value in that domain is determined), | |
444 see the chapter on specifiers in the XEmacs Lisp Reference Manual. | |
445 | |
446 TYPE specifies the particular type of specifier, and should be one of | |
447 the symbols 'generic, 'integer, 'boolean, 'color, 'font, 'image, | |
448 'face-boolean, or 'toolbar. | |
449 | |
450 For more information on particular types of specifiers, see the functions | |
451 `generic-specifier-p', `integer-specifier-p', `boolean-specifier-p', | |
452 `color-specifier-p', `font-specifier-p', `image-specifier-p', | |
453 `face-boolean-specifier-p', and `toolbar-specifier-p'. | |
454 */ ) | |
455 (type) | |
456 Lisp_Object type; | |
457 { | |
458 /* This function can GC */ | |
459 struct specifier_methods *meths = decode_specifier_type (type, | |
460 ERROR_ME); | |
461 | |
462 return make_specifier (meths); | |
463 } | |
464 | |
465 DEFUN ("specifierp", Fspecifierp, Sspecifierp, 1, 1, 0 /* | |
466 Return non-nil if OBJECT is a specifier. | |
467 | |
468 A specifier is an object that can be used to keep track of a property | |
469 whose value can be per-buffer, per-window, per-frame, or per-device, | |
470 and can further be restricted to a particular console-type or device-class. | |
471 See `make-specifier'. | |
472 */ ) | |
473 (object) | |
474 Lisp_Object object; | |
475 { | |
476 if (!SPECIFIERP (object)) | |
477 return Qnil; | |
478 return Qt; | |
479 } | |
480 | |
481 DEFUN ("specifier-type", Fspecifier_type, Sspecifier_type, 1, 1, 0 /* | |
482 Return the type of SPECIFIER. | |
483 */ ) | |
484 (specifier) | |
485 Lisp_Object specifier; | |
486 { | |
487 CHECK_SPECIFIER (specifier); | |
488 return intern (XSPECIFIER (specifier)->methods->name); | |
489 } | |
490 | |
491 | |
492 /************************************************************************/ | |
493 /* Locales and domains */ | |
494 /************************************************************************/ | |
495 | |
496 DEFUN ("valid-specifier-locale-p", Fvalid_specifier_locale_p, | |
497 Svalid_specifier_locale_p, 1, 1, 0 /* | |
498 Return non-nil if LOCALE is a valid specifier locale. | |
499 Valid locales are a device, a frame, a window, a buffer, and 'global. | |
500 (nil is not valid.) | |
501 */ ) | |
502 (locale) | |
503 Lisp_Object locale; | |
504 { | |
505 /* This cannot GC. */ | |
506 if ((DEVICEP (locale) && DEVICE_LIVE_P (XDEVICE (locale))) || | |
507 (FRAMEP (locale) && FRAME_LIVE_P (XFRAME (locale))) || | |
508 (BUFFERP (locale) && BUFFER_LIVE_P (XBUFFER (locale))) || | |
509 /* dead windows are allowed because they may become live | |
510 windows again when a window configuration is restored */ | |
511 WINDOWP (locale) || | |
512 EQ (locale, Qglobal)) | |
513 return Qt; | |
514 else | |
515 return Qnil; | |
516 } | |
517 | |
518 DEFUN ("valid-specifier-domain-p", | |
519 Fvalid_specifier_domain_p, | |
520 Svalid_specifier_domain_p, 1, 1, 0 /* | |
521 Return non-nil if DOMAIN is a valid specifier domain. | |
522 A domain is used to instance a specifier (i.e. determine the specifier's | |
523 value in that domain). Valid domains are a window, frame, or device. | |
524 (nil is not valid.) | |
525 */ ) | |
526 (domain) | |
527 Lisp_Object domain; | |
528 { | |
529 /* This cannot GC. */ | |
530 if ((DEVICEP (domain) && DEVICE_LIVE_P (XDEVICE (domain))) || | |
531 (FRAMEP (domain) && FRAME_LIVE_P (XFRAME (domain))) || | |
532 (WINDOWP (domain) && WINDOW_LIVE_P (XWINDOW (domain)))) | |
533 return Qt; | |
534 else | |
535 return Qnil; | |
536 } | |
537 | |
538 DEFUN ("valid-specifier-locale-type-p", | |
539 Fvalid_specifier_locale_type_p, | |
540 Svalid_specifier_locale_type_p, 1, 1, 0 /* | |
541 Given a specifier LOCALE-TYPE, return non-nil if it is valid. | |
542 Valid locale types are 'global, 'device, 'frame, 'window, and 'buffer. | |
543 (Note, however, that in functions that accept either a locale or a locale | |
544 type, 'global is considered an individual locale.) | |
545 */ ) | |
546 (locale_type) | |
547 Lisp_Object locale_type; | |
548 { | |
549 /* This cannot GC. */ | |
550 if (EQ (locale_type, Qglobal) || | |
551 EQ (locale_type, Qdevice) || | |
552 EQ (locale_type, Qframe) || | |
553 EQ (locale_type, Qwindow) || | |
554 EQ (locale_type, Qbuffer)) | |
555 return Qt; | |
556 else | |
557 return Qnil; | |
558 } | |
559 | |
560 static void | |
561 check_valid_locale_or_locale_type (Lisp_Object locale) | |
562 { | |
563 /* This cannot GC. */ | |
564 if (EQ (locale, Qall) || | |
565 !NILP (Fvalid_specifier_locale_p (locale)) || | |
566 !NILP (Fvalid_specifier_locale_type_p (locale))) | |
567 return; | |
568 signal_simple_error ("Invalid specifier locale or locale type", locale); | |
569 } | |
570 | |
571 DEFUN ("specifier-locale-type-from-locale", | |
572 Fspecifier_locale_type_from_locale, | |
573 Sspecifier_locale_type_from_locale, 1, 1, 0 /* | |
574 Given a specifier LOCALE, return its type. | |
575 */ ) | |
576 (locale) | |
577 Lisp_Object locale; | |
578 { | |
579 /* This cannot GC. */ | |
580 if (NILP (Fvalid_specifier_locale_p (locale))) | |
581 signal_simple_error ("Invalid specifier locale", locale); | |
582 if (DEVICEP (locale)) | |
583 return Qdevice; | |
584 if (FRAMEP (locale)) | |
585 return Qframe; | |
586 if (WINDOWP (locale)) | |
587 return Qwindow; | |
588 if (BUFFERP (locale)) | |
589 return Qbuffer; | |
590 assert (EQ (locale, Qglobal)); | |
591 return Qglobal; | |
592 } | |
593 | |
594 Lisp_Object | |
595 decode_locale (Lisp_Object locale) | |
596 { | |
597 /* This cannot GC. */ | |
598 if (NILP (locale)) | |
599 return Qglobal; | |
600 else if (!NILP (Fvalid_specifier_locale_p (locale))) | |
601 return locale; | |
602 else | |
603 signal_simple_error ("Invalid specifier locale", locale); | |
604 | |
605 return Qnil; | |
606 } | |
607 | |
608 static enum spec_locale_type | |
609 decode_locale_type (Lisp_Object locale_type) | |
610 { | |
611 /* This cannot GC. */ | |
612 if (EQ (locale_type, Qglobal)) | |
613 return LOCALE_GLOBAL; | |
614 if (EQ (locale_type, Qdevice)) | |
615 return LOCALE_DEVICE; | |
616 if (EQ (locale_type, Qframe)) | |
617 return LOCALE_FRAME; | |
618 if (EQ (locale_type, Qwindow)) | |
619 return LOCALE_WINDOW; | |
620 if (EQ (locale_type, Qbuffer)) | |
621 return LOCALE_BUFFER; | |
622 signal_simple_error ("Invalid specifier locale type", locale_type); | |
623 return 0; | |
624 } | |
625 | |
626 Lisp_Object | |
627 decode_locale_list (Lisp_Object locale) | |
628 { | |
629 /* This cannot GC. */ | |
630 /* The return value of this function must be GCPRO'd. */ | |
631 if (NILP (locale)) | |
632 locale = list1 (Qall); | |
633 else | |
634 { | |
635 Lisp_Object rest; | |
636 if (!CONSP (locale)) | |
637 locale = list1 (locale); | |
638 EXTERNAL_LIST_LOOP (rest, locale) | |
639 check_valid_locale_or_locale_type (XCAR (rest)); | |
640 } | |
641 return locale; | |
642 } | |
643 | |
644 static enum spec_locale_type | |
645 locale_type_from_locale (Lisp_Object locale) | |
646 { | |
647 return decode_locale_type (Fspecifier_locale_type_from_locale (locale)); | |
648 } | |
649 | |
650 static void | |
651 check_valid_domain (Lisp_Object domain) | |
652 { | |
653 if (NILP (Fvalid_specifier_domain_p (domain))) | |
654 signal_simple_error ("Invalid specifier domain", domain); | |
655 } | |
656 | |
657 Lisp_Object | |
658 decode_domain (Lisp_Object domain) | |
659 { | |
660 if (NILP (domain)) | |
661 return Fselected_window (Qnil); | |
662 check_valid_domain (domain); | |
663 return domain; | |
664 } | |
665 | |
666 | |
667 /************************************************************************/ | |
668 /* Tags */ | |
669 /************************************************************************/ | |
670 | |
671 DEFUN ("valid-specifier-tag-p", Fvalid_specifier_tag_p, | |
672 Svalid_specifier_tag_p, | |
673 1, 1, 0 /* | |
674 Return non-nil if TAG is a valid specifier tag. | |
675 See also `valid-specifier-tag-set-p'. | |
676 */ ) | |
677 (tag) | |
678 Lisp_Object tag; | |
679 { | |
680 if (valid_console_type_p (tag) || | |
681 valid_device_class_p (tag) || | |
682 !NILP (assq_no_quit (tag, Vuser_defined_tags))) | |
683 return Qt; | |
684 return Qnil; | |
685 } | |
686 | |
687 DEFUN ("valid-specifier-tag-set-p", Fvalid_specifier_tag_set_p, | |
688 Svalid_specifier_tag_set_p, | |
689 1, 1, 0 /* | |
690 Return non-nil if TAG-SET is a valid specifier tag set. | |
691 | |
692 A specifier tag set is an entity that is attached to an instantiator | |
693 and can be used to restrict the scope of that instantiator to a | |
694 particular device class or device type and/or to mark instantiators | |
695 added by a particular package so that they can be later removed. | |
696 | |
697 A specifier tag set consists of a list of zero of more specifier tags, | |
698 each of which is a symbol that is recognized by XEmacs as a tag. | |
699 (The valid device types and device classes are always tags, as are | |
700 any tags defined by `define-specifier-tag'.) It is called a \"tag set\" | |
701 (as opposed to a list) because the order of the tags or the number of | |
702 times a particular tag occurs does not matter. | |
703 | |
704 Each tag has a predicate associated with it, which specifies whether | |
705 that tag applies to a particular device. The tags which are device types | |
706 and classes match devices of that type or class. User-defined tags can | |
707 have any predicate, or none (meaning that all devices match). When | |
708 attempting to instance a specifier, a particular instantiator is only | |
709 considered if the device of the domain being instanced over matches | |
710 all tags in the tag set attached to that instantiator. | |
711 | |
712 Most of the time, a tag set is not specified, and the instantiator | |
713 gets a null tag set, which matches all devices. | |
714 */ ) | |
715 (tag_set) | |
716 Lisp_Object tag_set; | |
717 { | |
718 Lisp_Object rest; | |
719 | |
720 for (rest = tag_set; !NILP (rest); rest = XCDR (rest)) | |
721 { | |
722 if (!CONSP (rest)) | |
723 return Qnil; | |
724 if (NILP (Fvalid_specifier_tag_p (XCAR (rest)))) | |
725 return Qnil; | |
726 QUIT; | |
727 } | |
728 return Qt; | |
729 } | |
730 | |
731 Lisp_Object | |
732 decode_specifier_tag_set (Lisp_Object tag_set) | |
733 { | |
734 /* The return value of this function must be GCPRO'd. */ | |
735 if (!NILP (Fvalid_specifier_tag_p (tag_set))) | |
736 return list1 (tag_set); | |
737 if (NILP (Fvalid_specifier_tag_set_p (tag_set))) | |
738 signal_simple_error ("Invalid specifier tag-set", tag_set); | |
739 return tag_set; | |
740 } | |
741 | |
742 static Lisp_Object | |
743 canonicalize_tag_set (Lisp_Object tag_set) | |
744 { | |
745 int len = XINT (Flength (tag_set)); | |
746 Lisp_Object *tags, rest; | |
747 int i, j; | |
748 | |
749 /* We assume in this function that the tag_set has already been | |
750 validated, so there are no surprises. */ | |
751 | |
752 if (len == 0 || len == 1) | |
753 /* most common case */ | |
754 return tag_set; | |
755 | |
756 tags = (Lisp_Object *) alloca (len * sizeof (Lisp_Object)); | |
757 | |
758 i = 0; | |
759 LIST_LOOP (rest, tag_set) | |
760 tags[i++] = XCAR (rest); | |
761 | |
762 /* Sort the list of tags. We use a bubble sort here (copied from | |
763 extent_fragment_update()) -- reduces the function call overhead, | |
764 and is the fastest sort for small numbers of items. */ | |
765 | |
766 for (i = 1; i < len; i++) | |
767 { | |
768 j = i - 1; | |
769 while (j >= 0 && | |
770 strcmp ((char *) string_data (XSYMBOL (tags[j])->name), | |
771 (char *) string_data (XSYMBOL (tags[j+1])->name)) > 0) | |
772 { | |
773 Lisp_Object tmp = tags[j]; | |
774 tags[j] = tags[j+1]; | |
775 tags[j+1] = tmp; | |
776 j--; | |
777 } | |
778 } | |
779 | |
780 /* Now eliminate duplicates. */ | |
781 | |
782 for (i = 1, j = 1; i < len; i++) | |
783 { | |
784 /* j holds the destination, i the source. */ | |
785 if (!EQ (tags[i], tags[i-1])) | |
786 tags[j++] = tags[i]; | |
787 } | |
788 | |
789 return Flist (j, tags); | |
790 } | |
791 | |
792 DEFUN ("canonicalize-tag-set", Fcanonicalize_tag_set, | |
793 Scanonicalize_tag_set, 1, 1, 0 /* | |
794 Canonicalize the given tag set. | |
795 Two canonicalized tag sets can be compared with `equal' to see if they | |
796 represent the same tag set. (Specifically, canonicalizing involves | |
797 sorting by symbol name and removing duplicates.) | |
798 */ ) | |
799 (tag_set) | |
800 Lisp_Object tag_set; | |
801 { | |
802 if (NILP (Fvalid_specifier_tag_set_p (tag_set))) | |
803 signal_simple_error ("Invalid tag set", tag_set); | |
804 return canonicalize_tag_set (tag_set); | |
805 } | |
806 | |
807 static int | |
808 device_matches_specifier_tag_set_p (Lisp_Object device, Lisp_Object tag_set) | |
809 { | |
810 Lisp_Object devtype, devclass, rest; | |
811 struct device *d = XDEVICE (device); | |
812 | |
813 devtype = DEVICE_TYPE (d); | |
814 devclass = DEVICE_CLASS (d); | |
815 | |
816 LIST_LOOP (rest, tag_set) | |
817 { | |
818 Lisp_Object tag = XCAR (rest); | |
819 Lisp_Object assoc; | |
820 | |
821 if (EQ (tag, devtype) || EQ (tag, devclass)) | |
822 continue; | |
823 assoc = assq_no_quit (tag, DEVICE_USER_DEFINED_TAGS (d)); | |
824 /* other built-in tags (device types/classes) are not in | |
825 the user-defined-tags list. */ | |
826 if (NILP (assoc) || NILP (XCDR (assoc))) | |
827 return 0; | |
828 } | |
829 | |
830 return 1; | |
831 } | |
832 | |
833 DEFUN ("device-matches-specifier-tag-set-p", | |
834 Fdevice_matches_specifier_tag_set_p, | |
835 Sdevice_matches_specifier_tag_set_p, | |
836 2, 2, 0 /* | |
837 Return non-nil if DEVICE matches specifier tag set TAG-SET. | |
838 This means that DEVICE matches each tag in the tag set. (Every | |
839 tag recognized by XEmacs has a predicate associated with it that | |
840 specifies which devices match it.) | |
841 */ ) | |
842 (device, tag_set) | |
843 Lisp_Object device, tag_set; | |
844 { | |
845 CHECK_LIVE_DEVICE (device); | |
846 | |
847 if (NILP (Fvalid_specifier_tag_set_p (tag_set))) | |
848 signal_simple_error ("Invalid tag set", tag_set); | |
849 | |
850 return device_matches_specifier_tag_set_p (device, tag_set) ? Qt : Qnil; | |
851 } | |
852 | |
853 DEFUN ("define-specifier-tag", Fdefine_specifier_tag, Sdefine_specifier_tag, | |
854 1, 2, 0 /* | |
855 Define a new specifier tag. | |
856 If PREDICATE is specified, it should be a function of one argument | |
857 (a device) that specifies whether the tag matches that particular | |
858 device. If PREDICATE is omitted, the tag matches all devices. | |
859 | |
860 You can redefine an existing user-defined specifier tag. However, | |
861 you cannot redefine the built-in specifier tags (the device types | |
862 and classes) or the symbols nil, t, 'all, or 'global. | |
863 */ ) | |
864 (tag, predicate) | |
865 Lisp_Object tag, predicate; | |
866 { | |
867 Lisp_Object assoc, devcons, concons; | |
868 int recompute = 0; | |
869 | |
870 CHECK_SYMBOL (tag); | |
871 if (valid_device_class_p (tag) || | |
872 valid_console_type_p (tag)) | |
873 signal_simple_error ("Cannot redefine built-in specifier tags", tag); | |
874 /* Try to prevent common instantiators and locales from being | |
875 redefined, to reduce ambiguity */ | |
876 if (NILP (tag) || EQ (tag, Qt) || EQ (tag, Qall) || EQ (tag, Qglobal)) | |
877 signal_simple_error ("Cannot define nil, t, 'all, or 'global", | |
878 tag); | |
879 assoc = assq_no_quit (tag, Vuser_defined_tags); | |
880 if (NILP (assoc)) | |
881 { | |
882 recompute = 1; | |
883 Vuser_defined_tags = Fcons (Fcons (tag, predicate), Vuser_defined_tags); | |
884 DEVICE_LOOP_NO_BREAK (devcons, concons) | |
885 { | |
886 struct device *d = XDEVICE (XCAR (devcons)); | |
887 /* Initially set the value to t in case of error | |
888 in predicate */ | |
889 DEVICE_USER_DEFINED_TAGS (d) = | |
890 Fcons (Fcons (tag, Qt), DEVICE_USER_DEFINED_TAGS (d)); | |
891 } | |
892 } | |
893 else if (!NILP (predicate) && !NILP (XCDR (assoc))) | |
894 { | |
895 recompute = 1; | |
896 XCDR (assoc) = predicate; | |
897 } | |
898 | |
899 /* recompute the tag values for all devices. However, in the special | |
900 case where both the old and new predicates are nil, we know that | |
901 we don't have to do this. (It's probably common for people to | |
902 call (define-specifier-tag) more than once on the same tag, | |
903 and the most common case is where PREDICATE is not specified.) */ | |
904 | |
905 if (recompute) | |
906 { | |
907 DEVICE_LOOP_NO_BREAK (devcons, concons) | |
908 { | |
909 Lisp_Object device = XCAR (devcons); | |
910 assoc = assq_no_quit (tag, | |
911 DEVICE_USER_DEFINED_TAGS (XDEVICE (device))); | |
912 assert (CONSP (assoc)); | |
913 if (NILP (predicate)) | |
914 XCDR (assoc) = Qt; | |
915 else | |
916 XCDR (assoc) = !NILP (call1 (predicate, device)) ? Qt : Qnil; | |
917 } | |
918 } | |
919 | |
920 return Qnil; | |
921 } | |
922 | |
923 /* Called at device-creation time to initialize the user-defined | |
924 tag values for the newly-created device. */ | |
925 | |
926 void | |
927 setup_device_initial_specifier_tags (struct device *d) | |
928 { | |
929 Lisp_Object rest, rest2; | |
930 Lisp_Object device = Qnil; | |
931 | |
932 XSETDEVICE (device, d); | |
933 | |
934 DEVICE_USER_DEFINED_TAGS (d) = Fcopy_alist (Vuser_defined_tags); | |
935 | |
936 /* Now set up the initial values */ | |
937 LIST_LOOP (rest, DEVICE_USER_DEFINED_TAGS (d)) | |
938 XCDR (XCAR (rest)) = Qt; | |
939 | |
940 for (rest = Vuser_defined_tags, rest2 = DEVICE_USER_DEFINED_TAGS (d); | |
941 !NILP (rest); rest = XCDR (rest), rest2 = XCDR (rest2)) | |
942 { | |
943 Lisp_Object predicate = XCDR (XCAR (rest)); | |
944 if (NILP (predicate)) | |
945 XCDR (XCAR (rest2)) = Qt; | |
946 else | |
947 XCDR (XCAR (rest2)) = !NILP (call1 (predicate, device)) ? Qt : Qnil; | |
948 } | |
949 } | |
950 | |
951 DEFUN ("device-matching-specifier-tag-list", | |
952 Fdevice_matching_specifier_tag_list, | |
953 Sdevice_matching_specifier_tag_list, | |
954 0, 1, 0 /* | |
955 Return a list of all specifier tags matching DEVICE. | |
956 DEVICE defaults to the selected device if omitted. | |
957 */ ) | |
958 (device) | |
959 Lisp_Object device; | |
960 { | |
961 struct device *d = decode_device (device); | |
962 Lisp_Object rest, list = Qnil; | |
963 struct gcpro gcpro1; | |
964 | |
965 GCPRO1 (list); | |
966 | |
967 LIST_LOOP (rest, DEVICE_USER_DEFINED_TAGS (d)) | |
968 { | |
969 if (!NILP (XCDR (XCAR (rest)))) | |
970 list = Fcons (XCAR (XCAR (rest)), list); | |
971 } | |
972 | |
973 list = Fnreverse (list); | |
974 list = Fcons (DEVICE_CLASS (d), list); | |
975 list = Fcons (DEVICE_TYPE (d), list); | |
976 | |
977 RETURN_UNGCPRO (list); | |
978 } | |
979 | |
980 DEFUN ("specifier-tag-list", Fspecifier_tag_list, Sspecifier_tag_list, | |
981 0, 0, 0 /* | |
982 Return a list of all currently-defined specifier tags. | |
983 This includes the built-in ones (the device types and classes). | |
984 */ ) | |
985 () | |
986 { | |
987 Lisp_Object list = Qnil, rest; | |
988 struct gcpro gcpro1; | |
989 | |
990 GCPRO1 (list); | |
991 | |
992 LIST_LOOP (rest, Vuser_defined_tags) | |
993 list = Fcons (XCAR (XCAR (rest)), list); | |
994 | |
995 list = Fnreverse (list); | |
996 list = nconc2 (Fcopy_sequence (Vdevice_class_list), list); | |
997 list = nconc2 (Fcopy_sequence (Vconsole_type_list), list); | |
998 | |
999 RETURN_UNGCPRO (list); | |
1000 } | |
1001 | |
1002 DEFUN ("specifier-tag-predicate", Fspecifier_tag_predicate, | |
1003 Sspecifier_tag_predicate, | |
1004 1, 1, 0 /* | |
1005 Return the predicate for the given specifier tag. | |
1006 */ ) | |
1007 (tag) | |
1008 Lisp_Object tag; | |
1009 { | |
1010 /* The return value of this function must be GCPRO'd. */ | |
1011 CHECK_SYMBOL (tag); | |
1012 | |
1013 if (NILP (Fvalid_specifier_tag_p (tag))) | |
1014 signal_simple_error ("Invalid specifier tag", tag); | |
1015 | |
1016 /* Make up some predicates for the built-in types */ | |
1017 | |
1018 if (valid_console_type_p (tag)) | |
1019 return list3 (Qlambda, list1 (Qdevice), | |
1020 list3 (Qeq, list2 (Qquote, tag), | |
1021 list2 (Qconsole_type, Qdevice))); | |
1022 | |
1023 if (valid_device_class_p (tag)) | |
1024 return list3 (Qlambda, list1 (Qdevice), | |
1025 list3 (Qeq, list2 (Qquote, tag), | |
1026 list2 (Qdevice_class, Qdevice))); | |
1027 | |
1028 return XCDR (assq_no_quit (tag, Vuser_defined_tags)); | |
1029 } | |
1030 | |
1031 /* Return true if A "matches" B. If EXACT_P is 0, A must be a subset of B. | |
1032 Otherwise, A must be `equal' to B. The sets must be canonicalized. */ | |
1033 static int | |
1034 tag_sets_match_p (Lisp_Object a, Lisp_Object b, int exact_p) | |
1035 { | |
1036 if (!exact_p) | |
1037 { | |
1038 while (!NILP (a) && !NILP (b)) | |
1039 { | |
1040 if (EQ (XCAR (a), XCAR (b))) | |
1041 a = XCDR (a); | |
1042 b = XCDR (b); | |
1043 } | |
1044 | |
1045 return NILP (a); | |
1046 } | |
1047 else | |
1048 { | |
1049 while (!NILP (a) && !NILP (b)) | |
1050 { | |
1051 if (!EQ (XCAR (a), XCAR (b))) | |
1052 return 0; | |
1053 a = XCDR (a); | |
1054 b = XCDR (b); | |
1055 } | |
1056 | |
1057 return NILP (a) && NILP (b); | |
1058 } | |
1059 } | |
1060 | |
1061 | |
1062 /************************************************************************/ | |
1063 /* Spec-lists and inst-lists */ | |
1064 /************************************************************************/ | |
1065 | |
1066 static Lisp_Object | |
1067 call_validate_method (Lisp_Object boxed_method, Lisp_Object instantiator) | |
1068 { | |
1069 ((void (*)(Lisp_Object)) get_opaque_ptr (boxed_method)) (instantiator); | |
1070 return Qt; | |
1071 } | |
1072 | |
1073 static Lisp_Object | |
1074 check_valid_instantiator (Lisp_Object instantiator, | |
1075 struct specifier_methods *meths, | |
1076 Error_behavior errb) | |
1077 { | |
1078 if (meths->validate_method) | |
1079 { | |
1080 Lisp_Object retval; | |
1081 | |
1082 if (ERRB_EQ (errb, ERROR_ME)) | |
1083 { | |
1084 (meths->validate_method) (instantiator); | |
1085 retval = Qt; | |
1086 } | |
1087 else | |
1088 { | |
1089 Lisp_Object opaque = make_opaque_ptr ((void *) | |
1090 meths->validate_method); | |
1091 struct gcpro gcpro1; | |
1092 | |
1093 GCPRO1 (opaque); | |
1094 retval = call_with_suspended_errors (call_validate_method, | |
1095 Qnil, | |
1096 Qspecifier, errb, 2, | |
1097 opaque, instantiator); | |
1098 free_opaque_ptr (opaque); | |
1099 UNGCPRO; | |
1100 } | |
1101 | |
1102 return retval; | |
1103 } | |
1104 return Qt; | |
1105 } | |
1106 | |
1107 DEFUN ("check-valid-instantiator", Fcheck_valid_instantiator, | |
1108 Scheck_valid_instantiator, | |
1109 2, 2, 0 /* | |
1110 Signal an error if INSTANTIATOR is invalid for SPECIFIER-TYPE. | |
1111 */ ) | |
1112 (instantiator, specifier_type) | |
1113 Lisp_Object instantiator, specifier_type; | |
1114 { | |
1115 struct specifier_methods *meths = decode_specifier_type (specifier_type, | |
1116 ERROR_ME); | |
1117 | |
1118 return check_valid_instantiator (instantiator, meths, ERROR_ME); | |
1119 } | |
1120 | |
1121 DEFUN ("valid-instantiator-p", Fvalid_instantiator_p, Svalid_instantiator_p, | |
1122 2, 2, 0 /* | |
1123 Return non-nil if INSTANTIATOR is valid for SPECIFIER-TYPE. | |
1124 */ ) | |
1125 (instantiator, specifier_type) | |
1126 Lisp_Object instantiator, specifier_type; | |
1127 { | |
1128 struct specifier_methods *meths = decode_specifier_type (specifier_type, | |
1129 ERROR_ME); | |
1130 | |
1131 return check_valid_instantiator (instantiator, meths, ERROR_ME_NOT); | |
1132 } | |
1133 | |
1134 static Lisp_Object | |
1135 check_valid_inst_list (Lisp_Object inst_list, struct specifier_methods *meths, | |
1136 Error_behavior errb) | |
1137 { | |
1138 Lisp_Object rest; | |
1139 | |
1140 LIST_LOOP (rest, inst_list) | |
1141 { | |
1142 if (!CONSP (rest) || !CONSP (XCAR (rest))) | |
1143 { | |
1144 maybe_signal_simple_error ("Invalid instantiator list", inst_list, | |
1145 Qspecifier, errb); | |
1146 return Qnil; | |
1147 } | |
1148 if (NILP (Fvalid_specifier_tag_set_p (XCAR (XCAR (rest))))) | |
1149 { | |
1150 maybe_signal_simple_error ("Invalid specifier tag", | |
1151 XCAR (XCAR (rest)), Qspecifier, errb); | |
1152 return Qnil; | |
1153 } | |
1154 | |
1155 if (NILP (check_valid_instantiator (XCDR (XCAR (rest)), meths, | |
1156 errb))) | |
1157 return Qnil; | |
1158 } | |
1159 | |
1160 return Qt; | |
1161 } | |
1162 | |
1163 DEFUN ("check-valid-inst-list", Fcheck_valid_inst_list, Scheck_valid_inst_list, | |
1164 2, 2, 0 /* | |
1165 Signal an error if INST-LIST is invalid for specifier type TYPE. | |
1166 */ ) | |
1167 (inst_list, type) | |
1168 Lisp_Object inst_list, type; | |
1169 { | |
1170 struct specifier_methods *meths = decode_specifier_type (type, ERROR_ME); | |
1171 | |
1172 return check_valid_inst_list (inst_list, meths, ERROR_ME); | |
1173 } | |
1174 | |
1175 DEFUN ("valid-inst-list-p", Fvalid_inst_list_p, Svalid_inst_list_p, | |
1176 2, 2, 0 /* | |
1177 Return non-nil if INST-LIST is valid for specifier type TYPE. | |
1178 */ ) | |
1179 (inst_list, type) | |
1180 Lisp_Object inst_list, type; | |
1181 { | |
1182 struct specifier_methods *meths = decode_specifier_type (type, ERROR_ME); | |
1183 | |
1184 return check_valid_inst_list (inst_list, meths, ERROR_ME_NOT); | |
1185 } | |
1186 | |
1187 static Lisp_Object | |
1188 check_valid_spec_list (Lisp_Object spec_list, struct specifier_methods *meths, | |
1189 Error_behavior errb) | |
1190 { | |
1191 Lisp_Object rest; | |
1192 | |
1193 LIST_LOOP (rest, spec_list) | |
1194 { | |
1195 if (!CONSP (rest) || !CONSP (XCAR (rest))) | |
1196 { | |
1197 maybe_signal_simple_error ("Invalid specification list", spec_list, | |
1198 Qspecifier, errb); | |
1199 return Qnil; | |
1200 } | |
1201 if (NILP (Fvalid_specifier_locale_p (XCAR (XCAR (rest))))) | |
1202 { | |
1203 maybe_signal_simple_error ("Invalid specifier locale", | |
1204 XCAR (XCAR (rest)), | |
1205 Qspecifier, errb); | |
1206 return Qnil; | |
1207 } | |
1208 | |
1209 if (NILP (check_valid_inst_list (XCDR (XCAR (rest)), meths, errb))) | |
1210 return Qnil; | |
1211 } | |
1212 | |
1213 return Qt; | |
1214 } | |
1215 | |
1216 DEFUN ("check-valid-spec-list", Fcheck_valid_spec_list, Scheck_valid_spec_list, | |
1217 2, 2, 0 /* | |
1218 Signal an error if SPEC-LIST is invalid for specifier type TYPE. | |
1219 */ ) | |
1220 (spec_list, type) | |
1221 Lisp_Object spec_list, type; | |
1222 { | |
1223 struct specifier_methods *meths = decode_specifier_type (type, ERROR_ME); | |
1224 | |
1225 return check_valid_spec_list (spec_list, meths, ERROR_ME); | |
1226 } | |
1227 | |
1228 DEFUN ("valid-spec-list-p", Fvalid_spec_list_p, Svalid_spec_list_p, | |
1229 2, 2, 0 /* | |
1230 Return non-nil if SPEC-LIST is valid for specifier type TYPE. | |
1231 */ ) | |
1232 (spec_list, type) | |
1233 Lisp_Object spec_list, type; | |
1234 { | |
1235 struct specifier_methods *meths = decode_specifier_type (type, ERROR_ME); | |
1236 | |
1237 return check_valid_spec_list (spec_list, meths, ERROR_ME_NOT); | |
1238 } | |
1239 | |
1240 enum spec_add_meth | |
1241 decode_how_to_add_specification (Lisp_Object how_to_add) | |
1242 { | |
1243 enum spec_add_meth add_meth = 0; | |
1244 | |
1245 if (NILP (how_to_add) || EQ (Qremove_tag_set_prepend, how_to_add)) | |
1246 add_meth = SPEC_REMOVE_TAG_SET_PREPEND; | |
1247 else if (EQ (Qremove_tag_set_append, how_to_add)) | |
1248 add_meth = SPEC_REMOVE_TAG_SET_APPEND; | |
1249 else if (EQ (Qappend, how_to_add)) | |
1250 add_meth = SPEC_APPEND; | |
1251 else if (EQ (Qprepend, how_to_add)) | |
1252 add_meth = SPEC_PREPEND; | |
1253 else if (EQ (Qremove_locale, how_to_add)) | |
1254 add_meth = SPEC_REMOVE_LOCALE; | |
1255 else if (EQ (Qremove_locale_type, how_to_add)) | |
1256 add_meth = SPEC_REMOVE_LOCALE_TYPE; | |
1257 else if (EQ (Qremove_all, how_to_add)) | |
1258 add_meth = SPEC_REMOVE_ALL; | |
1259 else | |
1260 signal_simple_error ("Invalid `how-to-add' flag", how_to_add); | |
1261 return add_meth; | |
1262 } | |
1263 | |
1264 /* This gets hit so much that the function call overhead had a | |
1265 measurable impact (according to Quantify). #### We should figure | |
1266 out the frequency with which this is called with the various types | |
1267 and reorder the check accordingly. */ | |
1268 #define SPECIFIER_GET_SPEC_LIST(specifier, type) \ | |
1269 (type == LOCALE_GLOBAL \ | |
1270 ? &(XSPECIFIER (specifier)->global_specs) \ | |
1271 : (type == LOCALE_DEVICE \ | |
1272 ? &(XSPECIFIER (specifier)->device_specs) \ | |
1273 : (type == LOCALE_FRAME \ | |
1274 ? &(XSPECIFIER (specifier)->frame_specs) \ | |
1275 : (type == LOCALE_WINDOW \ | |
1276 ? &(XWEAK_LIST_LIST (XSPECIFIER (specifier)->window_specs)) \ | |
1277 : (type == LOCALE_BUFFER \ | |
1278 ? &(XSPECIFIER (specifier)->buffer_specs) \ | |
1279 : 0))))) | |
1280 | |
1281 static Lisp_Object * | |
1282 specifier_get_inst_list (Lisp_Object specifier, Lisp_Object locale, | |
1283 enum spec_locale_type type) | |
1284 { | |
1285 Lisp_Object *spec_list = SPECIFIER_GET_SPEC_LIST (specifier, type); | |
1286 Lisp_Object specification; | |
1287 | |
1288 if (type == LOCALE_GLOBAL) | |
1289 return spec_list; | |
1290 /* Calling assq_no_quit when it is just going to return nil anyhow | |
1291 is extremely expensive. So sayeth Quantify. */ | |
1292 if (!CONSP (*spec_list)) | |
1293 return 0; | |
1294 specification = assq_no_quit (locale, *spec_list); | |
1295 if (NILP (specification)) | |
1296 return 0; | |
1297 return &XCDR (specification); | |
1298 } | |
1299 | |
1300 /* For the given INST_LIST, return a new INST_LIST containing all elements | |
1301 where TAG-SET matches the element's tag set. EXACT_P indicates whether | |
1302 the match must be exact (as opposed to a subset). SHORT_P indicates | |
1303 that the short form (for `specifier-specs') should be returned if | |
1304 possible. If COPY_TREE_P, `copy-tree' is used to ensure that no | |
1305 elements of the new list are shared with the initial list. | |
1306 */ | |
1307 | |
1308 static Lisp_Object | |
1309 specifier_process_inst_list (Lisp_Object inst_list, | |
1310 Lisp_Object tag_set, int exact_p, | |
1311 int short_p, int copy_tree_p) | |
1312 { | |
1313 Lisp_Object retval = Qnil; | |
1314 Lisp_Object rest; | |
1315 struct gcpro gcpro1; | |
1316 | |
1317 GCPRO1 (retval); | |
1318 LIST_LOOP (rest, inst_list) | |
1319 { | |
1320 Lisp_Object tagged_inst = XCAR (rest); | |
1321 Lisp_Object tagged_inst_tag = XCAR (tagged_inst); | |
1322 if (tag_sets_match_p (tag_set, tagged_inst_tag, exact_p)) | |
1323 { | |
1324 if (short_p && NILP (tagged_inst_tag)) | |
1325 retval = Fcons (copy_tree_p ? | |
1326 Fcopy_tree (XCDR (tagged_inst), Qt) : | |
1327 XCDR (tagged_inst), | |
1328 retval); | |
1329 else | |
1330 retval = Fcons (copy_tree_p ? Fcopy_tree (tagged_inst, Qt) : | |
1331 tagged_inst, retval); | |
1332 } | |
1333 } | |
1334 retval = Fnreverse (retval); | |
1335 UNGCPRO; | |
1336 /* If there is a single instantiator and the short form is | |
1337 requested, return just the instantiator (rather than a one-element | |
1338 list of it) unless it is nil (so that it can be distinguished from | |
1339 no instantiators at all). */ | |
1340 if (short_p && CONSP (retval) && !NILP (XCAR (retval)) && | |
1341 NILP (XCDR (retval))) | |
1342 return XCAR (retval); | |
1343 else | |
1344 return retval; | |
1345 } | |
1346 | |
1347 static Lisp_Object | |
1348 specifier_get_external_inst_list (Lisp_Object specifier, Lisp_Object locale, | |
1349 enum spec_locale_type type, | |
1350 Lisp_Object tag_set, int exact_p, | |
1351 int short_p, int copy_tree_p) | |
1352 { | |
1353 Lisp_Object *inst_list = specifier_get_inst_list (specifier, locale, | |
1354 type); | |
1355 if (!inst_list || NILP (*inst_list)) | |
1356 { | |
1357 /* nil for *inst_list should only occur in 'global */ | |
1358 assert (!inst_list || EQ (locale, Qglobal)); | |
1359 return Qnil; | |
1360 } | |
1361 | |
1362 return specifier_process_inst_list (*inst_list, tag_set, exact_p, | |
1363 short_p, copy_tree_p); | |
1364 } | |
1365 | |
1366 static Lisp_Object | |
1367 specifier_get_external_spec_list (Lisp_Object specifier, | |
1368 enum spec_locale_type type, | |
1369 Lisp_Object tag_set, int exact_p) | |
1370 { | |
1371 Lisp_Object *spec_list = SPECIFIER_GET_SPEC_LIST (specifier, type); | |
1372 Lisp_Object retval = Qnil; | |
1373 Lisp_Object rest; | |
1374 struct gcpro gcpro1; | |
1375 | |
1376 assert (type != LOCALE_GLOBAL); | |
1377 /* We're about to let stuff go external; make sure there aren't | |
1378 any dead objects */ | |
1379 *spec_list = cleanup_assoc_list (*spec_list); | |
1380 | |
1381 GCPRO1 (retval); | |
1382 LIST_LOOP (rest, *spec_list) | |
1383 { | |
1384 Lisp_Object spec = XCAR (rest); | |
1385 Lisp_Object inst_list = | |
1386 specifier_process_inst_list (XCDR (spec), tag_set, exact_p, 0, 1); | |
1387 if (!NILP (inst_list)) | |
1388 retval = Fcons (Fcons (XCAR (spec), inst_list), retval); | |
1389 } | |
1390 RETURN_UNGCPRO (Fnreverse (retval)); | |
1391 } | |
1392 | |
1393 static Lisp_Object * | |
1394 specifier_new_spec (Lisp_Object specifier, Lisp_Object locale, | |
1395 enum spec_locale_type type) | |
1396 { | |
1397 Lisp_Object *spec_list = SPECIFIER_GET_SPEC_LIST (specifier, type); | |
1398 Lisp_Object new_spec = Fcons (locale, Qnil); | |
1399 assert (type != LOCALE_GLOBAL); | |
1400 *spec_list = Fcons (new_spec, *spec_list); | |
1401 return &XCDR (new_spec); | |
1402 } | |
1403 | |
1404 /* For the given INST_LIST, return a new list comprised of elements | |
1405 where TAG_SET does not match the element's tag set. This operation | |
1406 is destructive. */ | |
1407 | |
1408 static Lisp_Object | |
1409 specifier_process_remove_inst_list (Lisp_Object inst_list, | |
1410 Lisp_Object tag_set, int exact_p, | |
1411 int *was_removed) | |
1412 { | |
1413 Lisp_Object prev = Qnil, rest; | |
1414 | |
1415 *was_removed = 0; | |
1416 | |
1417 LIST_LOOP (rest, inst_list) | |
1418 { | |
1419 if (tag_sets_match_p (tag_set, XCAR (XCAR (rest)), exact_p)) | |
1420 { | |
1421 /* time to remove. */ | |
1422 *was_removed = 1; | |
1423 if (NILP (prev)) | |
1424 inst_list = XCDR (rest); | |
1425 else | |
1426 XCDR (prev) = XCDR (rest); | |
1427 } | |
1428 else | |
1429 prev = rest; | |
1430 } | |
1431 | |
1432 return inst_list; | |
1433 } | |
1434 | |
1435 static void | |
1436 specifier_remove_spec (Lisp_Object specifier, Lisp_Object locale, | |
1437 enum spec_locale_type type, | |
1438 Lisp_Object tag_set, int exact_p) | |
1439 { | |
1440 Lisp_Object *spec_list = SPECIFIER_GET_SPEC_LIST (specifier, type); | |
1441 Lisp_Object assoc; | |
1442 int was_removed; | |
1443 | |
1444 if (type == LOCALE_GLOBAL) | |
1445 *spec_list = specifier_process_remove_inst_list (*spec_list, tag_set, | |
1446 exact_p, &was_removed); | |
1447 else | |
1448 { | |
1449 assoc = assq_no_quit (locale, *spec_list); | |
1450 if (NILP (assoc)) | |
1451 /* this locale is not found. */ | |
1452 return; | |
1453 XCDR (assoc) = specifier_process_remove_inst_list (XCDR (assoc), | |
1454 tag_set, exact_p, | |
1455 &was_removed); | |
1456 if (NILP (XCDR (assoc))) | |
1457 /* no inst-pairs left; remove this locale entirely. */ | |
1458 *spec_list = remassq_no_quit (locale, *spec_list); | |
1459 } | |
1460 | |
1461 if (was_removed) | |
1462 MAYBE_SPECMETH (XSPECIFIER (specifier), after_change, (specifier, locale)); | |
1463 } | |
1464 | |
1465 static void | |
1466 specifier_remove_locale_type (Lisp_Object specifier, | |
1467 enum spec_locale_type type, | |
1468 Lisp_Object tag_set, int exact_p) | |
1469 { | |
1470 Lisp_Object *spec_list = SPECIFIER_GET_SPEC_LIST (specifier, type); | |
1471 Lisp_Object prev = Qnil, rest; | |
1472 | |
1473 assert (type != LOCALE_GLOBAL); | |
1474 LIST_LOOP (rest, *spec_list) | |
1475 { | |
1476 int was_removed; | |
1477 int remove_spec = 0; | |
1478 Lisp_Object spec = XCAR (rest); | |
1479 | |
1480 /* There may be dead objects floating around */ | |
1481 /* remember, dead windows can become alive again. */ | |
1482 if (!WINDOWP (XCAR (spec)) && object_dead_p (XCAR (spec))) | |
1483 { | |
1484 remove_spec = 1; | |
1485 was_removed = 0; | |
1486 } | |
1487 else | |
1488 { | |
1489 XCDR (spec) = specifier_process_remove_inst_list (XCDR (spec), | |
1490 tag_set, exact_p, | |
1491 &was_removed); | |
1492 if (NILP (XCDR (spec))) | |
1493 remove_spec = 1; | |
1494 } | |
1495 | |
1496 if (remove_spec) | |
1497 { | |
1498 if (NILP (prev)) | |
1499 *spec_list = XCDR (rest); | |
1500 else | |
1501 XCDR (prev) = XCDR (rest); | |
1502 } | |
1503 else | |
1504 prev = rest; | |
1505 | |
1506 if (was_removed) | |
1507 MAYBE_SPECMETH (XSPECIFIER (specifier), after_change, | |
1508 (specifier, XCAR (spec))); | |
1509 } | |
1510 } | |
1511 | |
1512 /* NEW_LIST is going to be added to INST_LIST, with add method ADD_METH. | |
1513 Frob INST_LIST according to ADD_METH. No need to call an after-change | |
1514 function; the calling function will do this. Return either SPEC_PREPEND | |
1515 or SPEC_APPEND, indicating whether to prepend or append the NEW_LIST. */ | |
1516 | |
1517 static enum spec_add_meth | |
1518 handle_multiple_add_insts (Lisp_Object *inst_list, | |
1519 Lisp_Object new_list, | |
1520 enum spec_add_meth add_meth) | |
1521 { | |
1522 if (add_meth == SPEC_REMOVE_TAG_SET_PREPEND || | |
1523 add_meth == SPEC_REMOVE_TAG_SET_APPEND) | |
1524 { | |
1525 Lisp_Object rest; | |
1526 | |
1527 LIST_LOOP (rest, new_list) | |
1528 { | |
1529 Lisp_Object canontag = canonicalize_tag_set (XCAR (XCAR (rest))); | |
1530 struct gcpro gcpro1; | |
1531 | |
1532 GCPRO1 (canontag); | |
1533 /* pull out all elements from the existing list with the | |
1534 same tag as any tags in NEW_LIST. */ | |
1535 *inst_list = remassoc_no_quit (canontag, *inst_list); | |
1536 UNGCPRO; | |
1537 } | |
1538 if (add_meth == SPEC_REMOVE_TAG_SET_PREPEND) | |
1539 return SPEC_PREPEND; | |
1540 else | |
1541 return SPEC_APPEND; | |
1542 } | |
1543 else if (add_meth == SPEC_REMOVE_LOCALE) | |
1544 { | |
1545 *inst_list = Qnil; | |
1546 return SPEC_PREPEND; | |
1547 } | |
1548 if (add_meth == SPEC_APPEND) | |
1549 return add_meth; | |
1550 | |
1551 return SPEC_PREPEND; | |
1552 } | |
1553 | |
1554 /* Given a LOCALE and INST_LIST that is going to be added to SPECIFIER, | |
1555 copy, canonicalize, and call the going_to_add methods as necessary | |
1556 to produce a new list that is the one that really will be added | |
1557 to the specifier. */ | |
1558 | |
1559 static Lisp_Object | |
1560 build_up_processed_list (Lisp_Object specifier, Lisp_Object locale, | |
1561 Lisp_Object inst_list) | |
1562 { | |
1563 /* The return value of this function must be GCPRO'd. */ | |
1564 Lisp_Object rest, list_to_build_up = Qnil; | |
1565 struct gcpro gcpro1; | |
1566 | |
1567 GCPRO1 (list_to_build_up); | |
1568 LIST_LOOP (rest, inst_list) | |
1569 { | |
1570 Lisp_Object tag_set = XCAR (XCAR (rest)); | |
1571 Lisp_Object instantiator = Fcopy_tree (XCDR (XCAR (rest)), Qt); | |
1572 Lisp_Object sub_inst_list = Qnil; | |
1573 struct gcpro ngcpro1, ngcpro2; | |
1574 | |
1575 NGCPRO2 (instantiator, sub_inst_list); | |
1576 /* call the will-add method; it may GC */ | |
1577 sub_inst_list = SPECMETH_OR_GIVEN (XSPECIFIER (specifier), going_to_add, | |
1578 (specifier, locale, tag_set, | |
1579 instantiator), Qt); | |
1580 if (EQ (sub_inst_list, Qt)) | |
1581 /* no change here. */ | |
1582 sub_inst_list = list1 (Fcons (canonicalize_tag_set (tag_set), | |
1583 instantiator)); | |
1584 else | |
1585 { | |
1586 /* now canonicalize all the tag sets in the new objects */ | |
1587 Lisp_Object rest2; | |
1588 LIST_LOOP (rest2, sub_inst_list) | |
1589 XCAR (XCAR (rest2)) = canonicalize_tag_set (XCAR (XCAR (rest2))); | |
1590 } | |
1591 | |
1592 list_to_build_up = nconc2 (sub_inst_list, list_to_build_up); | |
1593 NUNGCPRO; | |
1594 } | |
1595 | |
1596 RETURN_UNGCPRO (Fnreverse (list_to_build_up)); | |
1597 } | |
1598 | |
1599 /* Add a specification (locale and instantiator list) to a specifier. | |
1600 ADD_METH specifies what to do with existing specifications in the | |
1601 specifier, and is an enum that corresponds to the values in | |
1602 `add-spec-to-specifier'. The calling routine is responsible for | |
1603 validating LOCALE and INST-LIST, but the tag-sets in INST-LIST | |
1604 do not need to be canonicalized. */ | |
1605 | |
1606 /* #### I really need to rethink the after-change | |
1607 functions to make them easier to use and more efficient. */ | |
1608 | |
1609 static void | |
1610 specifier_add_spec (Lisp_Object specifier, Lisp_Object locale, | |
1611 Lisp_Object inst_list, enum spec_add_meth add_meth) | |
1612 { | |
1613 struct Lisp_Specifier *sp = XSPECIFIER (specifier); | |
1614 enum spec_locale_type type; | |
1615 Lisp_Object *orig_inst_list; | |
1616 Lisp_Object list_to_build_up = Qnil; | |
1617 struct gcpro gcpro1; | |
1618 | |
1619 type = locale_type_from_locale (locale); | |
1620 | |
1621 GCPRO1 (list_to_build_up); | |
1622 list_to_build_up = build_up_processed_list (specifier, locale, inst_list); | |
1623 /* Now handle REMOVE_LOCALE_TYPE and REMOVE_ALL. These are the | |
1624 add-meth types that affect locales other than this one. */ | |
1625 if (add_meth == SPEC_REMOVE_LOCALE_TYPE) | |
1626 specifier_remove_locale_type (specifier, type, Qnil, 0); | |
1627 else if (add_meth == SPEC_REMOVE_ALL) | |
1628 { | |
1629 specifier_remove_locale_type (specifier, LOCALE_BUFFER, Qnil, 0); | |
1630 specifier_remove_locale_type (specifier, LOCALE_WINDOW, Qnil, 0); | |
1631 specifier_remove_locale_type (specifier, LOCALE_FRAME, Qnil, 0); | |
1632 specifier_remove_locale_type (specifier, LOCALE_DEVICE, Qnil, 0); | |
1633 specifier_remove_spec (specifier, Qglobal, LOCALE_GLOBAL, Qnil, 0); | |
1634 } | |
1635 | |
1636 orig_inst_list = specifier_get_inst_list (specifier, locale, type); | |
1637 if (!orig_inst_list) | |
1638 orig_inst_list = specifier_new_spec (specifier, locale, type); | |
1639 add_meth = handle_multiple_add_insts (orig_inst_list, list_to_build_up, | |
1640 add_meth); | |
1641 | |
1642 if (add_meth == SPEC_PREPEND) | |
1643 { | |
1644 *orig_inst_list = nconc2 (list_to_build_up, *orig_inst_list); | |
1645 } | |
1646 else if (add_meth == SPEC_APPEND) | |
1647 { | |
1648 *orig_inst_list = nconc2 (*orig_inst_list, list_to_build_up); | |
1649 } | |
1650 else | |
1651 abort (); | |
1652 | |
1653 UNGCPRO; | |
1654 | |
1655 /* call the after-change method */ | |
1656 MAYBE_SPECMETH (sp, after_change, (specifier, locale)); | |
1657 } | |
1658 | |
1659 static void | |
1660 specifier_copy_spec (Lisp_Object specifier, Lisp_Object dest, | |
1661 Lisp_Object locale, enum spec_locale_type type, | |
1662 Lisp_Object tag_set, int exact_p, | |
1663 enum spec_add_meth add_meth) | |
1664 { | |
1665 Lisp_Object inst_list = | |
1666 specifier_get_external_inst_list (specifier, locale, type, tag_set, | |
1667 exact_p, 0, 0); | |
1668 specifier_add_spec (dest, locale, inst_list, add_meth); | |
1669 } | |
1670 | |
1671 static void | |
1672 specifier_copy_locale_type (Lisp_Object specifier, Lisp_Object dest, | |
1673 enum spec_locale_type type, | |
1674 Lisp_Object tag_set, int exact_p, | |
1675 enum spec_add_meth add_meth) | |
1676 { | |
1677 Lisp_Object *src_list = SPECIFIER_GET_SPEC_LIST (specifier, type); | |
1678 Lisp_Object rest; | |
1679 | |
1680 /* This algorithm is O(n^2) in running time. | |
1681 It's certainly possible to implement an O(n log n) algorithm, | |
1682 but I doubt there's any need to. */ | |
1683 | |
1684 LIST_LOOP (rest, *src_list) | |
1685 { | |
1686 Lisp_Object spec = XCAR (rest); | |
1687 /* There may be dead objects floating around */ | |
1688 /* remember, dead windows can become alive again. */ | |
1689 if (WINDOWP (XCAR (spec)) || !object_dead_p (XCAR (spec))) | |
1690 specifier_add_spec | |
1691 (dest, XCAR (spec), | |
1692 specifier_process_inst_list (XCDR (spec), tag_set, exact_p, 0, 0), | |
1693 add_meth); | |
1694 } | |
1695 } | |
1696 | |
1697 /* map MAPFUN over the locales in SPECIFIER that are given in LOCALE. | |
1698 CLOSURE is passed unchanged to MAPFUN. LOCALE can be one of | |
1699 | |
1700 -- nil (same as 'all) | |
1701 -- a single locale, locale type, or 'all | |
1702 -- a list of locales, locale types, and/or 'all | |
1703 | |
1704 MAPFUN is called for each locale and locale type given; for 'all, | |
1705 it is called for the locale 'global and for the four possible | |
1706 locale types. In each invocation, either LOCALE will be a locale | |
1707 and LOCALE_TYPE will be the locale type of this locale, | |
1708 or LOCALE will be nil and LOCALE_TYPE will be a locale type. | |
1709 If MAPFUN ever returns non-zero, the mapping is halted and the | |
1710 value returned is returned from map_specifier(). Otherwise, the | |
1711 mapping proceeds to the end and map_specifier() returns 0. | |
1712 */ | |
1713 | |
1714 static int | |
1715 map_specifier (Lisp_Object specifier, Lisp_Object locale, | |
1716 int (*mapfun) (Lisp_Object specifier, | |
1717 Lisp_Object locale, | |
1718 enum spec_locale_type locale_type, | |
1719 Lisp_Object tag_set, | |
1720 int exact_p, | |
1721 void *closure), | |
1722 Lisp_Object tag_set, Lisp_Object exact_p, | |
1723 void *closure) | |
1724 { | |
1725 int retval = 0; | |
1726 Lisp_Object rest; | |
1727 struct gcpro gcpro1, gcpro2; | |
1728 | |
1729 GCPRO2 (tag_set, locale); | |
1730 locale = decode_locale_list (locale); | |
1731 tag_set = decode_specifier_tag_set (tag_set); | |
1732 tag_set = canonicalize_tag_set (tag_set); | |
1733 | |
1734 LIST_LOOP (rest, locale) | |
1735 { | |
1736 Lisp_Object theloc = XCAR (rest); | |
1737 if (!NILP (Fvalid_specifier_locale_p (theloc))) | |
1738 { | |
1739 retval = (*mapfun) (specifier, theloc, | |
1740 locale_type_from_locale (theloc), | |
1741 tag_set, !NILP (exact_p), closure); | |
1742 if (retval) | |
1743 break; | |
1744 } | |
1745 else if (!NILP (Fvalid_specifier_locale_type_p (theloc))) | |
1746 { | |
1747 retval = (*mapfun) (specifier, Qnil, | |
1748 decode_locale_type (theloc), tag_set, | |
1749 !NILP (exact_p), closure); | |
1750 if (retval) | |
1751 break; | |
1752 } | |
1753 else | |
1754 { | |
1755 assert (EQ (theloc, Qall)); | |
1756 retval = (*mapfun) (specifier, Qnil, LOCALE_BUFFER, tag_set, | |
1757 !NILP (exact_p), closure); | |
1758 if (retval) | |
1759 break; | |
1760 retval = (*mapfun) (specifier, Qnil, LOCALE_WINDOW, tag_set, | |
1761 !NILP (exact_p), closure); | |
1762 if (retval) | |
1763 break; | |
1764 retval = (*mapfun) (specifier, Qnil, LOCALE_FRAME, tag_set, | |
1765 !NILP (exact_p), closure); | |
1766 if (retval) | |
1767 break; | |
1768 retval = (*mapfun) (specifier, Qnil, LOCALE_DEVICE, tag_set, | |
1769 !NILP (exact_p), closure); | |
1770 if (retval) | |
1771 break; | |
1772 retval = (*mapfun) (specifier, Qglobal, LOCALE_GLOBAL, tag_set, | |
1773 !NILP (exact_p), closure); | |
1774 if (retval) | |
1775 break; | |
1776 } | |
1777 } | |
1778 | |
1779 UNGCPRO; | |
1780 return retval; | |
1781 } | |
1782 | |
1783 DEFUN ("add-spec-to-specifier", Fadd_spec_to_specifier, Sadd_spec_to_specifier, | |
1784 2, 5, 0 /* | |
1785 Add a specification to SPECIFIER. | |
1786 The specification maps from LOCALE (which should be a buffer, window, | |
1787 frame, device, or 'global, and defaults to 'global) to INSTANTIATOR, | |
1788 whose allowed values depend on the type of the specifier. Optional | |
1789 argument TAG-SET limits the instantiator to apply only to the specified | |
1790 tag set, which should be a list of tags all of which must match the | |
1791 device being instantiated over (tags are a device type, a device class, | |
1792 or tags defined with `define-specifier-tag'). Specifying a single | |
1793 symbol for TAG-SET is equivalent to specifying a one-element list | |
1794 containing that symbol. Optional argument HOW-TO-ADD specifies what to | |
1795 do if there are already specifications in the specifier. | |
1796 It should be one of | |
1797 | |
1798 'prepend Put at the beginning of the current list of | |
1799 instantiators for LOCALE. | |
1800 'append Add to the end of the current list of | |
1801 instantiators for LOCALE. | |
1802 'remove-tag-set-prepend (this is the default) | |
1803 Remove any existing instantiators whose tag set is | |
1804 the same as TAG-SET; then put the new instantiator | |
1805 at the beginning of the current list. (\"Same tag | |
1806 set\" means that they contain the same elements. | |
1807 The order may be different.) | |
1808 'remove-tag-set-append | |
1809 Remove any existing instantiators whose tag set is | |
1810 the same as TAG-SET; then put the new instantiator | |
1811 at the end of the current list. | |
1812 'remove-locale Remove all previous instantiators for this locale | |
1813 before adding the new spec. | |
1814 'remove-locale-type Remove all specifications for all locales of the | |
1815 same type as LOCALE (this includes LOCALE itself) | |
1816 before adding the new spec. | |
1817 'remove-all Remove all specifications from the specifier | |
1818 before adding the new spec. | |
1819 | |
1820 You can retrieve the specifications for a particular locale or locale type | |
1821 with the function `specifier-spec-list' or `specifier-specs'. | |
1822 */ ) | |
1823 (specifier, instantiator, locale, tag_set, how_to_add) | |
1824 Lisp_Object specifier, instantiator, locale, tag_set, how_to_add; | |
1825 { | |
1826 enum spec_add_meth add_meth; | |
1827 Lisp_Object inst_list; | |
1828 struct gcpro gcpro1; | |
1829 | |
1830 CHECK_SPECIFIER (specifier); | |
1831 locale = decode_locale (locale); | |
1832 check_valid_instantiator (instantiator, | |
1833 decode_specifier_type | |
1834 (Fspecifier_type (specifier), ERROR_ME), | |
1835 ERROR_ME); | |
1836 /* tag_set might be newly-created material, but it's part of inst_list | |
1837 so is properly GC-protected. */ | |
1838 tag_set = decode_specifier_tag_set (tag_set); | |
1839 add_meth = decode_how_to_add_specification (how_to_add); | |
1840 | |
1841 inst_list = list1 (Fcons (tag_set, instantiator)); | |
1842 GCPRO1 (inst_list); | |
1843 specifier_add_spec (specifier, locale, inst_list, add_meth); | |
1844 recompute_cached_specifier_everywhere (specifier); | |
1845 RETURN_UNGCPRO (Qnil); | |
1846 } | |
1847 | |
1848 DEFUN ("add-spec-list-to-specifier", Fadd_spec_list_to_specifier, | |
1849 Sadd_spec_list_to_specifier, 2, 3, 0 /* | |
1850 Add a spec-list (a list of specifications) to SPECIFIER. | |
1851 The format of a spec-list is | |
1852 | |
1853 ((LOCALE (TAG-SET . INSTANTIATOR) ...) ...) | |
1854 | |
1855 where | |
1856 LOCALE := a buffer, a window, a frame, a device, or 'global | |
1857 TAG-SET := an unordered list of zero or more TAGS, each of which | |
1858 is a symbol | |
1859 TAG := a device class (see `valid-device-class-p'), a device type | |
1860 (see `valid-console-type-p'), or a tag defined with | |
1861 `define-specifier-tag' | |
1862 INSTANTIATOR := format determined by the type of specifier | |
1863 | |
1864 The pair (TAG-SET . INSTANTIATOR) is called an `inst-pair'. | |
1865 A list of inst-pairs is called an `inst-list'. | |
1866 The pair (LOCALE . INST-LIST) is called a `specification' or `spec'. | |
1867 A spec-list, then, can be viewed as a list of specifications. | |
1868 | |
1869 HOW-TO-ADD specifies how to combine the new specifications with | |
1870 the existing ones, and has the same semantics as for | |
1871 `add-spec-to-specifier'. | |
1872 | |
1873 In many circumstances, the higher-level function `set-specifier' is | |
1874 more convenient and should be used instead. | |
1875 */ ) | |
1876 (specifier, spec_list, how_to_add) | |
1877 Lisp_Object specifier, spec_list, how_to_add; | |
1878 { | |
1879 enum spec_add_meth add_meth; | |
1880 Lisp_Object rest; | |
1881 | |
1882 CHECK_SPECIFIER (specifier); | |
1883 check_valid_spec_list (spec_list, | |
1884 decode_specifier_type | |
1885 (Fspecifier_type (specifier), ERROR_ME), | |
1886 ERROR_ME); | |
1887 add_meth = decode_how_to_add_specification (how_to_add); | |
1888 | |
1889 LIST_LOOP (rest, spec_list) | |
1890 { | |
1891 /* Placating the GCC god. */ | |
1892 Lisp_Object crock1 = specifier; | |
1893 Lisp_Object crock2 = XCAR (XCAR (rest)); | |
1894 Lisp_Object crock3 = XCDR (XCAR (rest)); | |
1895 | |
1896 specifier_add_spec (crock1, crock2, crock3, add_meth); | |
1897 } | |
1898 recompute_cached_specifier_everywhere (specifier); | |
1899 return Qnil; | |
1900 } | |
1901 | |
1902 struct specifier_spec_list_closure | |
1903 { | |
1904 Lisp_Object head, tail; | |
1905 }; | |
1906 | |
1907 static int | |
1908 specifier_spec_list_mapfun (Lisp_Object specifier, | |
1909 Lisp_Object locale, | |
1910 enum spec_locale_type locale_type, | |
1911 Lisp_Object tag_set, | |
1912 int exact_p, | |
1913 void *closure) | |
1914 { | |
1915 struct specifier_spec_list_closure *cl = | |
1916 (struct specifier_spec_list_closure *) closure; | |
1917 Lisp_Object partial; | |
1918 | |
1919 if (NILP (locale)) | |
1920 partial = specifier_get_external_spec_list (specifier, | |
1921 locale_type, | |
1922 tag_set, exact_p); | |
1923 else | |
1924 { | |
1925 partial = specifier_get_external_inst_list (specifier, locale, | |
1926 locale_type, tag_set, | |
1927 exact_p, 0, 1); | |
1928 if (!NILP (partial)) | |
1929 partial = list1 (Fcons (locale, partial)); | |
1930 } | |
1931 if (NILP (partial)) | |
1932 return 0; | |
1933 | |
1934 /* tack on the new list */ | |
1935 if (NILP (cl->tail)) | |
1936 cl->head = cl->tail = partial; | |
1937 else | |
1938 XCDR (cl->tail) = partial; | |
1939 /* find the new tail */ | |
1940 while (CONSP (XCDR (cl->tail))) | |
1941 cl->tail = XCDR (cl->tail); | |
1942 return 0; | |
1943 } | |
1944 | |
1945 /* For the given SPECIFIER create and return a list of all specs | |
1946 contained within it, subject to LOCALE. If LOCALE is a locale, only | |
1947 specs in that locale will be returned. If LOCALE is a locale type, | |
1948 all specs in all locales of that type will be returned. If LOCALE is | |
1949 nil, all specs will be returned. This always copies lists and never | |
1950 returns the actual lists, because we do not want someone manipulating | |
1951 the actual objects. This may cause a slight loss of potential | |
1952 functionality but if we were to allow it then a user could manage to | |
1953 violate our assertion that the specs contained in the actual | |
1954 specifier lists are all valid. */ | |
1955 | |
1956 DEFUN ("specifier-spec-list", Fspecifier_spec_list, Sspecifier_spec_list, | |
1957 1, 4, 0 /* | |
1958 Return the spec-list of specifications for SPECIFIER in LOCALE. | |
1959 | |
1960 If LOCALE is a particular locale (a buffer, window, frame, device, | |
1961 or 'global), a spec-list consisting of the specification for that | |
1962 locale will be returned. | |
1963 | |
1964 If LOCALE is a locale type (i.e. 'buffer, 'window, 'frame, or 'device), | |
1965 a spec-list of the specifications for all locales of that type will be | |
1966 returned. | |
1967 | |
1968 If LOCALE is nil or 'all, a spec-list of all specifications in SPECIFIER | |
1969 will be returned. | |
1970 | |
1971 LOCALE can also be a list of locales, locale types, and/or 'all; the | |
1972 result is as if `specifier-spec-list' were called on each element of the | |
1973 list and the results concatenated together. | |
1974 | |
1975 Only instantiators where TAG-SET (a list of zero or more tags) is a | |
1976 subset of (or possibly equal to) the instantiator's tag set are returned. | |
1977 (The default value of nil is a subset of all tag sets, so in this case | |
1978 no instantiators will be screened out.) If EXACT-P is non-nil, however, | |
1979 TAG-SET must be equal to an instantiator's tag set for the instantiator | |
1980 to be returned. | |
1981 */ ) | |
1982 (specifier, locale, tag_set, exact_p) | |
1983 Lisp_Object specifier, locale, tag_set, exact_p; | |
1984 { | |
1985 struct specifier_spec_list_closure cl; | |
1986 struct gcpro gcpro1, gcpro2; | |
1987 | |
1988 CHECK_SPECIFIER (specifier); | |
1989 cl.head = cl.tail = Qnil; | |
1990 GCPRO2 (cl.head, cl.tail); | |
1991 map_specifier (specifier, locale, specifier_spec_list_mapfun, | |
1992 tag_set, exact_p, &cl); | |
1993 UNGCPRO; | |
1994 return cl.head; | |
1995 } | |
1996 | |
1997 | |
1998 DEFUN ("specifier-specs", Fspecifier_specs, Sspecifier_specs, | |
1999 1, 4, 0 /* | |
2000 Return the specification(s) for SPECIFIER in LOCALE. | |
2001 | |
2002 If LOCALE is a single locale or is a list of one element containing a | |
2003 single locale, then a \"short form\" of the instantiators for that locale | |
2004 will be returned. Otherwise, this function is identical to | |
2005 `specifier-spec-list'. | |
2006 | |
2007 The \"short form\" is designed for readability and not for ease of use | |
2008 in Lisp programs, and is as follows: | |
2009 | |
2010 1. If there is only one instantiator, then an inst-pair (i.e. cons of | |
2011 tag and instantiator) will be returned; otherwise a list of | |
2012 inst-pairs will be returned. | |
2013 2. For each inst-pair returned, if the instantiator's tag is 'any, | |
2014 the tag will be removed and the instantiator itself will be returned | |
2015 instead of the inst-pair. | |
2016 3. If there is only one instantiator, its value is nil, and its tag is | |
2017 'any, a one-element list containing nil will be returned rather | |
2018 than just nil, to distinguish this case from there being no | |
2019 instantiators at all. | |
2020 */ ) | |
2021 (specifier, locale, tag_set, exact_p) | |
2022 Lisp_Object specifier, locale, tag_set, exact_p; | |
2023 { | |
2024 if (!NILP (Fvalid_specifier_locale_p (locale)) || | |
2025 (CONSP (locale) && !NILP (Fvalid_specifier_locale_p (XCAR (locale))) && | |
2026 NILP (XCDR (locale)))) | |
2027 { | |
2028 struct gcpro gcpro1; | |
2029 | |
2030 CHECK_SPECIFIER (specifier); | |
2031 if (CONSP (locale)) | |
2032 locale = XCAR (locale); | |
2033 GCPRO1 (tag_set); | |
2034 tag_set = decode_specifier_tag_set (tag_set); | |
2035 tag_set = canonicalize_tag_set (tag_set); | |
2036 RETURN_UNGCPRO | |
2037 (specifier_get_external_inst_list (specifier, locale, | |
2038 locale_type_from_locale (locale), | |
2039 tag_set, !NILP (exact_p), | |
2040 1, 1)); | |
2041 } | |
2042 else | |
2043 return Fspecifier_spec_list (specifier, locale, tag_set, exact_p); | |
2044 } | |
2045 | |
2046 static int | |
2047 remove_specifier_mapfun (Lisp_Object specifier, | |
2048 Lisp_Object locale, | |
2049 enum spec_locale_type locale_type, | |
2050 Lisp_Object tag_set, | |
2051 int exact_p, | |
2052 void *ignored_closure) | |
2053 { | |
2054 if (NILP (locale)) | |
2055 specifier_remove_locale_type (specifier, locale_type, tag_set, exact_p); | |
2056 else | |
2057 specifier_remove_spec (specifier, locale, locale_type, tag_set, exact_p); | |
2058 return 0; | |
2059 } | |
2060 | |
2061 DEFUN ("remove-specifier", Fremove_specifier, | |
2062 Sremove_specifier, 1, 4, 0 /* | |
2063 Remove specification(s) for SPECIFIER. | |
2064 | |
2065 If LOCALE is a particular locale (a buffer, window, frame, device, | |
2066 or 'global), the specification for that locale will be removed. | |
2067 | |
2068 If instead, LOCALE is a locale type (i.e. 'buffer, 'window, 'frame, | |
2069 or 'device), the specifications for all locales of that type will be | |
2070 removed. | |
2071 | |
2072 If LOCALE is nil or 'all, all specifications will be removed. | |
2073 | |
2074 LOCALE can also be a list of locales, locale types, and/or 'all; this | |
2075 is equivalent to calling `remove-specifier' for each of the elements | |
2076 in the list. | |
2077 | |
2078 Only instantiators where TAG-SET (a list of zero or more tags) is a | |
2079 subset of (or possibly equal to) the instantiator's tag set are removed. | |
2080 (The default value of nil is a subset of all tag sets, so in this case | |
2081 no instantiators will be screened out.) If EXACT-P is non-nil, however, | |
2082 TAG-SET must be equal to an instantiator's tag set for the instantiator | |
2083 to be removed. | |
2084 */ ) | |
2085 (specifier, locale, tag_set, exact_p) | |
2086 Lisp_Object specifier, locale, tag_set, exact_p; | |
2087 { | |
2088 CHECK_SPECIFIER (specifier); | |
2089 map_specifier (specifier, locale, remove_specifier_mapfun, tag_set, | |
2090 exact_p, 0); | |
2091 recompute_cached_specifier_everywhere (specifier); | |
2092 return Qnil; | |
2093 } | |
2094 | |
2095 struct copy_specifier_closure | |
2096 { | |
2097 Lisp_Object dest; | |
2098 enum spec_add_meth add_meth; | |
2099 int add_meth_is_nil; | |
2100 }; | |
2101 | |
2102 static int | |
2103 copy_specifier_mapfun (Lisp_Object specifier, | |
2104 Lisp_Object locale, | |
2105 enum spec_locale_type locale_type, | |
2106 Lisp_Object tag_set, | |
2107 int exact_p, | |
2108 void *closure) | |
2109 { | |
2110 struct copy_specifier_closure *cl = | |
2111 (struct copy_specifier_closure *) closure; | |
2112 | |
2113 if (NILP (locale)) | |
2114 specifier_copy_locale_type (specifier, cl->dest, locale_type, | |
2115 tag_set, exact_p, | |
2116 cl->add_meth_is_nil ? | |
2117 SPEC_REMOVE_LOCALE_TYPE : | |
2118 cl->add_meth); | |
2119 else | |
2120 specifier_copy_spec (specifier, cl->dest, locale, locale_type, | |
2121 tag_set, exact_p, | |
2122 cl->add_meth_is_nil ? SPEC_REMOVE_LOCALE : | |
2123 cl->add_meth); | |
2124 return 0; | |
2125 } | |
2126 | |
2127 DEFUN ("copy-specifier", Fcopy_specifier, Scopy_specifier, | |
2128 1, 6, 0 /* | |
2129 Copy SPECIFIER to DEST, or create a new one if DEST is nil. | |
2130 | |
2131 If DEST is nil or omitted, a new specifier will be created and the | |
2132 specifications copied into it. Otherwise, the specifications will be | |
2133 copied into the existing specifier in DEST. | |
2134 | |
2135 If LOCALE is nil or 'all, all specifications will be copied. If LOCALE | |
2136 is a particular locale, the specification for that particular locale will | |
2137 be copied. If LOCALE is a locale type, the specifications for all locales | |
2138 of that type will be copied. LOCALE can also be a list of locales, | |
2139 locale types, and/or 'all; this is equivalent to calling `copy-specifier' | |
2140 for each of the elements of the list. See `specifier-spec-list' for more | |
2141 information about LOCALE. | |
2142 | |
2143 Only instantiators where TAG-SET (a list of zero or more tags) is a | |
2144 subset of (or possibly equal to) the instantiator's tag set are copied. | |
2145 (The default value of nil is a subset of all tag sets, so in this case | |
2146 no instantiators will be screened out.) If EXACT-P is non-nil, however, | |
2147 TAG-SET must be equal to an instantiator's tag set for the instantiator | |
2148 to be copied. | |
2149 | |
2150 Optional argument HOW-TO-ADD specifies what to do with existing | |
2151 specifications in DEST. If nil, then whichever locales or locale types | |
2152 are copied will first be completely erased in DEST. Otherwise, it is | |
2153 the same as in `add-spec-to-specifier'. | |
2154 */ ) | |
2155 (specifier, dest, locale, tag_set, exact_p, how_to_add) | |
2156 Lisp_Object specifier, dest, locale, tag_set, exact_p, how_to_add; | |
2157 { | |
2158 struct gcpro gcpro1; | |
2159 struct copy_specifier_closure cl; | |
2160 | |
2161 CHECK_SPECIFIER (specifier); | |
2162 if (NILP (how_to_add)) | |
2163 cl.add_meth_is_nil = 1; | |
2164 else | |
2165 cl.add_meth_is_nil = 0; | |
2166 cl.add_meth = decode_how_to_add_specification (how_to_add); | |
2167 if (NILP (dest)) | |
2168 { | |
2169 /* #### What about copying the extra data? */ | |
2170 dest = make_specifier (XSPECIFIER (specifier)->methods); | |
2171 } | |
2172 else | |
2173 { | |
2174 CHECK_SPECIFIER (dest); | |
2175 if (XSPECIFIER (dest)->methods != XSPECIFIER (specifier)->methods) | |
2176 error ("Specifiers not of same type"); | |
2177 } | |
2178 | |
2179 cl.dest = dest; | |
2180 GCPRO1 (dest); | |
2181 map_specifier (specifier, locale, copy_specifier_mapfun, | |
2182 tag_set, exact_p, &cl); | |
2183 UNGCPRO; | |
2184 recompute_cached_specifier_everywhere (specifier); | |
2185 return dest; | |
2186 } | |
2187 | |
2188 | |
2189 /************************************************************************/ | |
2190 /* Instancing */ | |
2191 /************************************************************************/ | |
2192 | |
2193 static Lisp_Object | |
2194 call_validate_matchspec_method (Lisp_Object boxed_method, | |
2195 Lisp_Object matchspec) | |
2196 { | |
2197 ((void (*)(Lisp_Object)) get_opaque_ptr (boxed_method)) (matchspec); | |
2198 return Qt; | |
2199 } | |
2200 | |
2201 static Lisp_Object | |
2202 check_valid_specifier_matchspec (Lisp_Object matchspec, | |
2203 struct specifier_methods *meths, | |
2204 Error_behavior errb) | |
2205 { | |
2206 if (meths->validate_matchspec_method) | |
2207 { | |
2208 Lisp_Object retval; | |
2209 | |
2210 if (ERRB_EQ (errb, ERROR_ME)) | |
2211 { | |
2212 (meths->validate_matchspec_method) (matchspec); | |
2213 retval = Qt; | |
2214 } | |
2215 else | |
2216 { | |
2217 Lisp_Object opaque = | |
2218 make_opaque_ptr ((void *) meths->validate_matchspec_method); | |
2219 struct gcpro gcpro1; | |
2220 | |
2221 GCPRO1 (opaque); | |
2222 retval = call_with_suspended_errors (call_validate_matchspec_method, | |
2223 Qnil, | |
2224 Qspecifier, errb, 2, | |
2225 opaque, matchspec); | |
2226 free_opaque_ptr (opaque); | |
2227 UNGCPRO; | |
2228 } | |
2229 | |
2230 return retval; | |
2231 } | |
2232 else | |
2233 { | |
2234 maybe_signal_simple_error | |
2235 ("Matchspecs not allowed for this specifier type", | |
2236 intern (meths->name), Qspecifier, errb); | |
2237 return Qnil; | |
2238 } | |
2239 } | |
2240 | |
2241 DEFUN ("check-valid-specifier-matchspec", Fcheck_valid_specifier_matchspec, | |
2242 Scheck_valid_specifier_matchspec, | |
2243 2, 2, 0 /* | |
2244 Signal an error if MATCHSPEC is invalid for SPECIFIER-TYPE. | |
2245 See `specifier-matching-instance' for a description of matchspecs. | |
2246 */ ) | |
2247 (matchspec, specifier_type) | |
2248 Lisp_Object matchspec, specifier_type; | |
2249 { | |
2250 struct specifier_methods *meths = decode_specifier_type (specifier_type, | |
2251 ERROR_ME); | |
2252 | |
2253 return check_valid_specifier_matchspec (matchspec, meths, ERROR_ME); | |
2254 } | |
2255 | |
2256 DEFUN ("valid-specifier-matchspec-p", Fvalid_specifier_matchspec_p, | |
2257 Svalid_specifier_matchspec_p, | |
2258 2, 2, 0 /* | |
2259 Return non-nil if MATCHSPEC is valid for SPECIFIER-TYPE. | |
2260 See `specifier-matching-instance' for a description of matchspecs. | |
2261 */ ) | |
2262 (matchspec, specifier_type) | |
2263 Lisp_Object matchspec, specifier_type; | |
2264 { | |
2265 struct specifier_methods *meths = decode_specifier_type (specifier_type, | |
2266 ERROR_ME); | |
2267 | |
2268 return check_valid_specifier_matchspec (matchspec, meths, ERROR_ME_NOT); | |
2269 } | |
2270 | |
2271 /* This function is purposely not callable from Lisp. If a Lisp | |
2272 caller wants to set a fallback, they should just set the | |
2273 global value. */ | |
2274 | |
2275 void | |
2276 set_specifier_fallback (Lisp_Object specifier, Lisp_Object fallback) | |
2277 { | |
2278 struct Lisp_Specifier *sp = XSPECIFIER (specifier); | |
2279 assert (SPECIFIERP (fallback) || | |
2280 !NILP (Fvalid_inst_list_p (fallback, Fspecifier_type (specifier)))); | |
2281 if (SPECIFIERP (fallback)) | |
2282 assert (EQ (Fspecifier_type (specifier), Fspecifier_type (fallback))); | |
2283 sp->fallback = fallback; | |
2284 /* call the after-change method */ | |
2285 MAYBE_SPECMETH (sp, after_change, (specifier, Qfallback)); | |
2286 recompute_cached_specifier_everywhere (specifier); | |
2287 } | |
2288 | |
2289 DEFUN ("specifier-fallback", Fspecifier_fallback, Sspecifier_fallback, | |
2290 1, 1, 0 /* | |
2291 Return the fallback value for SPECIFIER. | |
2292 Fallback values are provided by the C code for certain built-in | |
2293 specifiers to make sure that instancing won't fail even if all | |
2294 specs are removed from the specifier, or to implement simple | |
2295 inheritance behavior (e.g. this method is used to ensure that | |
2296 faces other than 'default inherit their attributes from 'default). | |
2297 By design, you cannot change the fallback value, and specifiers | |
2298 created with `make-specifier' will never have a fallback (although | |
2299 a similar, Lisp-accessible capability may be provided in the future | |
2300 to allow for inheritance). | |
2301 | |
2302 The fallback value will be an inst-list that is instanced like | |
2303 any other inst-list, a specifier of the same type as SPECIFIER | |
2304 \(results in inheritance), or nil for no fallback. | |
2305 | |
2306 When you instance a specifier, you can explicitly request that the | |
2307 fallback not be consulted. (The C code does this, for example, when | |
2308 merging faces.) See `specifier-instance'. | |
2309 */ ) | |
2310 (specifier) | |
2311 Lisp_Object specifier; | |
2312 { | |
2313 CHECK_SPECIFIER (specifier); | |
2314 return Fcopy_tree (XSPECIFIER (specifier)->fallback, Qt); | |
2315 } | |
2316 | |
2317 static Lisp_Object | |
2318 specifier_instance_from_inst_list (Lisp_Object specifier, | |
2319 Lisp_Object matchspec, | |
2320 Lisp_Object domain, | |
2321 Lisp_Object inst_list, | |
2322 Error_behavior errb, int no_quit, | |
2323 Lisp_Object depth) | |
2324 { | |
2325 /* This function can GC */ | |
2326 struct Lisp_Specifier *sp; | |
2327 Lisp_Object device; | |
2328 Lisp_Object rest; | |
2329 int count = specpdl_depth (); | |
2330 struct gcpro gcpro1, gcpro2; | |
2331 | |
2332 GCPRO2 (specifier, inst_list); | |
2333 | |
2334 sp = XSPECIFIER (specifier); | |
2335 device = DFW_DEVICE (domain); | |
2336 | |
2337 if (no_quit) | |
2338 /* The instantiate method is allowed to call eval. Since it | |
2339 is quite common for this function to get called from somewhere in | |
2340 redisplay we need to make sure that quits are ignored. Otherwise | |
2341 Fsignal will abort. */ | |
2342 specbind (Qinhibit_quit, Qt); | |
2343 | |
2344 LIST_LOOP (rest, inst_list) | |
2345 { | |
2346 Lisp_Object tagged_inst = XCAR (rest); | |
2347 Lisp_Object tag_set = XCAR (tagged_inst); | |
2348 | |
2349 if (device_matches_specifier_tag_set_p (device, tag_set)) | |
2350 { | |
2351 Lisp_Object val = XCDR (tagged_inst); | |
2352 | |
2353 if (HAS_SPECMETH_P (sp, instantiate)) | |
2354 val = call_with_suspended_errors (RAW_SPECMETH (sp, instantiate), | |
2355 Qunbound, Qspecifier, errb, | |
2356 5, specifier, matchspec, domain, | |
2357 XCDR (tagged_inst), | |
2358 depth); | |
2359 | |
2360 if (!UNBOUNDP (val)) | |
2361 { | |
2362 unbind_to (count, Qnil); | |
2363 UNGCPRO; | |
2364 return val; | |
2365 } | |
2366 } | |
2367 } | |
2368 | |
2369 unbind_to (count, Qnil); | |
2370 UNGCPRO; | |
2371 return Qunbound; | |
2372 } | |
2373 | |
2374 /* Given a SPECIFIER and a DOMAIN, return a specific instance for that | |
2375 specifier. Try to find one by checking the specifier types from most | |
2376 specific (buffer) to most general (global). If we find an instance, | |
2377 return it. Otherwise return Qunbound. */ | |
2378 | |
2379 #define CHECK_INSTANCE_ENTRY(key, matchspec, type) \ | |
2380 do { \ | |
2381 Lisp_Object *__inst_list = \ | |
2382 specifier_get_inst_list (specifier, key, type); \ | |
2383 if (__inst_list) \ | |
2384 { \ | |
2385 Lisp_Object __val__ = \ | |
2386 specifier_instance_from_inst_list (specifier, matchspec, \ | |
2387 domain, *__inst_list, \ | |
2388 errb, no_quit, depth); \ | |
2389 if (!UNBOUNDP (__val__)) \ | |
2390 return __val__; \ | |
2391 } \ | |
2392 } while (0) | |
2393 | |
2394 /* We accept any window, frame or device domain and do our checking | |
2395 starting from as specific a locale type as we can determine from the | |
2396 domain we are passed and going on up through as many other locale types | |
2397 as we can determine. In practice, when called from redisplay the | |
2398 arg will usually be a window and occasionally a frame. If | |
2399 triggered by a user call, who knows what it will usually be. */ | |
2400 Lisp_Object | |
2401 specifier_instance (Lisp_Object specifier, Lisp_Object matchspec, | |
2402 Lisp_Object domain, Error_behavior errb, int no_quit, | |
2403 int no_fallback, Lisp_Object depth) | |
2404 { | |
2405 Lisp_Object buffer = Qnil; | |
2406 Lisp_Object window = Qnil; | |
2407 Lisp_Object frame = Qnil; | |
2408 Lisp_Object device = Qnil; | |
2409 Lisp_Object tag = Qnil; | |
2410 struct device *d; | |
2411 struct Lisp_Specifier *sp; | |
2412 | |
2413 sp = XSPECIFIER (specifier); | |
2414 | |
2415 /* Attempt to determine buffer, window, frame, and device from the | |
2416 domain. */ | |
2417 if (WINDOWP (domain)) | |
2418 window = domain; | |
2419 else if (FRAMEP (domain)) | |
2420 frame = domain; | |
2421 else if (DEVICEP (domain)) | |
2422 device = domain; | |
2423 else | |
2424 abort (); | |
2425 | |
2426 if (NILP (buffer) && !NILP (window)) | |
2427 buffer = XWINDOW (window)->buffer; | |
2428 if (NILP (frame) && !NILP (window)) | |
2429 frame = XWINDOW (window)->frame; | |
2430 if (NILP (device)) | |
2431 /* frame had better exist; if device is undeterminable, something | |
2432 really went wrong. */ | |
2433 device = XFRAME (frame)->device; | |
2434 | |
2435 /* device had better be determined by now; abort if not. */ | |
2436 d = XDEVICE (device); | |
2437 tag = DEVICE_CLASS (d); | |
2438 | |
2439 depth = make_int (1 + XINT (depth)); | |
2440 if (XINT (depth) > 20) | |
2441 { | |
2442 maybe_error (Qspecifier, errb, "Apparent loop in specifier inheritance"); | |
2443 /* The specification is fucked; at least try the fallback | |
2444 (which better not be fucked, because it's not changeable | |
2445 from Lisp). */ | |
2446 depth = Qzero; | |
2447 goto do_fallback; | |
2448 } | |
2449 | |
2450 try_again: | |
2451 /* First see if we can generate one from the buffer specifiers. */ | |
2452 if (!NILP (buffer)) | |
2453 CHECK_INSTANCE_ENTRY (buffer, matchspec, LOCALE_BUFFER); | |
2454 | |
2455 /* Next see if we can generate one from the window specifiers. */ | |
2456 if (!NILP (window)) | |
2457 CHECK_INSTANCE_ENTRY (window, matchspec, LOCALE_WINDOW); | |
2458 | |
2459 /* Next see if we can generate one from the frame specifiers. */ | |
2460 if (!NILP (frame)) | |
2461 CHECK_INSTANCE_ENTRY (frame, matchspec, LOCALE_FRAME); | |
2462 | |
2463 /* If we still haven't succeeded try with the device specifiers. */ | |
2464 CHECK_INSTANCE_ENTRY (device, matchspec, LOCALE_DEVICE); | |
2465 | |
2466 /* Last and least try the global specifiers. */ | |
2467 CHECK_INSTANCE_ENTRY (Qglobal, matchspec, LOCALE_GLOBAL); | |
2468 | |
2469 do_fallback: | |
2470 /* We're out of specifiers and we still haven't generated an | |
2471 instance. At least try the fallback ... If this fails, | |
2472 then we just return Qunbound. */ | |
2473 | |
2474 if (no_fallback || NILP (sp->fallback)) | |
2475 /* I said, I don't want the fallbacks. */ | |
2476 return Qunbound; | |
2477 | |
2478 if (SPECIFIERP (sp->fallback)) | |
2479 { | |
2480 /* If you introduced loops in the default specifier chain, | |
2481 then you're fucked, so you better not do this. */ | |
2482 specifier = sp->fallback; | |
2483 sp = XSPECIFIER (specifier); | |
2484 goto try_again; | |
2485 } | |
2486 | |
2487 assert (CONSP (sp->fallback)); | |
2488 return specifier_instance_from_inst_list (specifier, matchspec, domain, | |
2489 sp->fallback, errb, no_quit, | |
2490 depth); | |
2491 } | |
2492 #undef CHECK_INSTANCE_ENTRY | |
2493 | |
2494 Lisp_Object | |
2495 specifier_instance_no_quit (Lisp_Object specifier, Lisp_Object matchspec, | |
2496 Lisp_Object domain, Error_behavior errb, | |
2497 int no_fallback, Lisp_Object depth) | |
2498 { | |
2499 return specifier_instance (specifier, matchspec, domain, errb, | |
2500 1, no_fallback, depth); | |
2501 } | |
2502 | |
2503 DEFUN ("specifier-instance", Fspecifier_instance, Sspecifier_instance, | |
2504 1, 4, 0 /* | |
2505 Instantiate SPECIFIER (return its value) in DOMAIN. | |
2506 If no instance can be generated for this domain, return DEFAULT. | |
2507 | |
2508 DOMAIN should be a window, frame, or device. Other values that are legal | |
2509 as a locale (e.g. a buffer) are not valid as a domain because they do not | |
2510 provide enough information to identify a particular device (see | |
2511 `valid-specifier-domain-p'). DOMAIN defaults to the selected window | |
2512 if omitted. | |
2513 | |
2514 \"Instantiating\" a specifier in a particular domain means determining | |
2515 the specifier's \"value\" in that domain. This is accomplished by | |
2516 searching through the specifications in the specifier that correspond | |
2517 to all locales that can be derived from the given domain, from specific | |
2518 to general. In most cases, the domain is an Emacs window. In that case | |
2519 specifications are searched for as follows: | |
2520 | |
2521 1. A specification whose locale is the window's buffer; | |
2522 2. A specification whose locale is the window itself; | |
2523 3. A specification whose locale is the window's frame; | |
2524 4. A specification whose locale is the window's frame's device; | |
2525 5. A specification whose locale is 'global. | |
2526 | |
2527 If all of those fail, then the C-code-provided fallback value for | |
2528 this specifier is consulted (see `specifier-fallback'). If it is | |
2529 an inst-list, then this function attempts to instantiate that list | |
2530 just as when a specification is located in the first five steps above. | |
2531 If the fallback is a specifier, `specifier-instance' is called | |
2532 recursively on this specifier and the return value used. Note, | |
2533 however, that if the optional argument NO-FALLBACK is non-nil, | |
2534 the fallback value will not be consulted. | |
2535 | |
2536 Note that there may be more than one specification matching a particular | |
2537 locale; all such specifications are considered before looking for any | |
2538 specifications for more general locales. Any particular specification | |
2539 that is found may be rejected because its tag set does not match the | |
2540 device being instantiated over, or because the specification is not | |
2541 valid for the device of the given domain (e.g. the font or color name | |
2542 does not exist for this particular X server). | |
2543 | |
2544 The returned value is dependent on the type of specifier. For example, | |
2545 for a font specifier (as returned by the `face-font' function), the returned | |
2546 value will be a font-instance object. For glyphs, the returned value | |
2547 will be a string, pixmap, or subwindow. | |
2548 | |
2549 See also `specifier-matching-instance'. | |
2550 */ ) | |
2551 (specifier, domain, defalt, no_fallback) | |
2552 Lisp_Object specifier, domain, defalt, no_fallback; | |
2553 { | |
2554 Lisp_Object instance; | |
2555 | |
2556 CHECK_SPECIFIER (specifier); | |
2557 domain = decode_domain (domain); | |
2558 | |
2559 instance = specifier_instance (specifier, Qunbound, domain, ERROR_ME, 0, | |
2560 !NILP (no_fallback), Qzero); | |
2561 if (UNBOUNDP (instance)) | |
2562 return defalt; | |
2563 return instance; | |
2564 } | |
2565 | |
2566 DEFUN ("specifier-matching-instance", Fspecifier_matching_instance, | |
2567 Sspecifier_matching_instance, 2, 5, 0 /* | |
2568 Return an instance for SPECIFIER in DOMAIN that matches MATCHSPEC. | |
2569 If no instance can be generated for this domain, return DEFAULT. | |
2570 | |
2571 This function is identical to `specifier-instance' except that a | |
2572 specification will only be considered if it matches MATCHSPEC. | |
2573 The definition of \"match\", and allowed values for MATCHSPEC, are | |
2574 dependent on the particular type of specifier. Here are some examples: | |
2575 | |
2576 -- For chartable (e.g. display table) specifiers, MATCHSPEC should be a | |
2577 character, and the specification (a chartable) must give a value for | |
2578 that character in order to be considered. This allows you to specify, | |
2579 e.g., a buffer-local display table that only gives values for particular | |
2580 characters. All other characters are handled as if the buffer-local | |
2581 display table is not there. (Chartable specifiers are not yet | |
2582 implemented.) | |
2583 | |
2584 -- For font specifiers, MATCHSPEC should be a charset, and the specification | |
2585 (a font string) must have a registry that matches the charset's registry. | |
2586 (This only makes sense with Mule support.) This makes it easy to choose a | |
2587 font that can display a particular character. (This is what redisplay | |
2588 does, in fact.) | |
2589 */ ) | |
2590 (specifier, matchspec, domain, defalt, no_fallback) | |
2591 Lisp_Object specifier, matchspec, domain, defalt, no_fallback; | |
2592 { | |
2593 Lisp_Object instance; | |
2594 | |
2595 CHECK_SPECIFIER (specifier); | |
2596 check_valid_specifier_matchspec (matchspec, XSPECIFIER (specifier)->methods, | |
2597 ERROR_ME); | |
2598 domain = decode_domain (domain); | |
2599 | |
2600 instance = specifier_instance (specifier, matchspec, domain, ERROR_ME, 0, | |
2601 !NILP (no_fallback), Qzero); | |
2602 if (UNBOUNDP (instance)) | |
2603 return defalt; | |
2604 return instance; | |
2605 } | |
2606 | |
2607 DEFUN ("specifier-instance-from-inst-list", Fspecifier_instance_from_inst_list, | |
2608 Sspecifier_instance_from_inst_list, 3, 4, 0 /* | |
2609 Attempt to convert a particular inst-list into an instance. | |
2610 This attempts to instantiate INST-LIST in the given DOMAIN, | |
2611 as if INST-LIST existed in a specification in SPECIFIER. If | |
2612 the instantiation fails, DEFAULT is returned. In most circumstances, | |
2613 you should not use this function; use `specifier-instance' instead. | |
2614 */ ) | |
2615 (specifier, domain, inst_list, defalt) | |
2616 Lisp_Object specifier, domain, inst_list, defalt; | |
2617 { | |
2618 Lisp_Object val = Qunbound; | |
2619 struct Lisp_Specifier *sp = XSPECIFIER (specifier); | |
2620 struct gcpro gcpro1; | |
2621 Lisp_Object built_up_list = Qnil; | |
2622 | |
2623 CHECK_SPECIFIER (specifier); | |
2624 check_valid_domain (domain); | |
2625 check_valid_inst_list (inst_list, sp->methods, ERROR_ME); | |
2626 GCPRO1 (built_up_list); | |
2627 built_up_list = build_up_processed_list (specifier, domain, inst_list); | |
2628 if (!NILP (built_up_list)) | |
2629 val = specifier_instance_from_inst_list (specifier, Qunbound, domain, | |
2630 built_up_list, ERROR_ME, 0, | |
2631 Qzero); | |
2632 UNGCPRO; | |
2633 if (UNBOUNDP (val)) | |
2634 return defalt; | |
2635 return val; | |
2636 } | |
2637 | |
2638 DEFUN ("specifier-matching-instance-from-inst-list", | |
2639 Fspecifier_matching_instance_from_inst_list, | |
2640 Sspecifier_matching_instance_from_inst_list, 4, 5, 0 /* | |
2641 Attempt to convert a particular inst-list into an instance. | |
2642 This attempts to instantiate INST-LIST in the given DOMAIN | |
2643 (as if INST-LIST existed in a specification in SPECIFIER), | |
2644 matching the specifications against MATCHSPEC. | |
2645 | |
2646 This function is analogous to `specifier-instance-from-inst-list' | |
2647 but allows for specification-matching as in `specifier-matching-instance'. | |
2648 See that function for a description of exactly how the matching process | |
2649 works. | |
2650 */ ) | |
2651 (specifier, matchspec, domain, inst_list, defalt) | |
2652 Lisp_Object specifier, matchspec, domain, inst_list, defalt; | |
2653 { | |
2654 Lisp_Object val = Qunbound; | |
2655 struct Lisp_Specifier *sp = XSPECIFIER (specifier); | |
2656 struct gcpro gcpro1; | |
2657 Lisp_Object built_up_list = Qnil; | |
2658 | |
2659 CHECK_SPECIFIER (specifier); | |
2660 check_valid_specifier_matchspec (matchspec, XSPECIFIER (specifier)->methods, | |
2661 ERROR_ME); | |
2662 check_valid_domain (domain); | |
2663 check_valid_inst_list (inst_list, sp->methods, ERROR_ME); | |
2664 GCPRO1 (built_up_list); | |
2665 built_up_list = build_up_processed_list (specifier, domain, inst_list); | |
2666 if (!NILP (built_up_list)) | |
2667 val = specifier_instance_from_inst_list (specifier, matchspec, domain, | |
2668 built_up_list, ERROR_ME, 0, | |
2669 Qzero); | |
2670 UNGCPRO; | |
2671 if (UNBOUNDP (val)) | |
2672 return defalt; | |
2673 return val; | |
2674 } | |
2675 | |
2676 | |
2677 /************************************************************************/ | |
2678 /* Caching in the struct window or frame */ | |
2679 /************************************************************************/ | |
2680 | |
2681 /* Either STRUCT_WINDOW_OFFSET or STRUCT_FRAME_OFFSET can be 0 to indicate | |
2682 no caching in that sort of object. */ | |
2683 | |
2684 /* #### It would be nice if the specifier caching automatically knew | |
2685 about specifier fallbacks, so we didn't have to do it ourselves. */ | |
2686 | |
2687 void | |
2688 set_specifier_caching (Lisp_Object specifier, int struct_window_offset, | |
2689 void (*value_changed_in_window) | |
2690 (Lisp_Object specifier, struct window *w, | |
2691 Lisp_Object oldval), | |
2692 int struct_frame_offset, | |
2693 void (*value_changed_in_frame) | |
2694 (Lisp_Object specifier, struct frame *f, | |
2695 Lisp_Object oldval)) | |
2696 { | |
2697 struct Lisp_Specifier *sp = XSPECIFIER (specifier); | |
2698 | |
2699 if (!sp->caching) | |
2700 sp->caching = malloc_type_and_zero (struct specifier_caching); | |
2701 sp->caching->offset_into_struct_window = struct_window_offset; | |
2702 sp->caching->value_changed_in_window = value_changed_in_window; | |
2703 sp->caching->offset_into_struct_frame = struct_frame_offset; | |
2704 sp->caching->value_changed_in_frame = value_changed_in_frame; | |
2705 Vcached_specifiers = Fcons (specifier, Vcached_specifiers); | |
2706 recompute_cached_specifier_everywhere (specifier); | |
2707 } | |
2708 | |
2709 static void | |
2710 recompute_one_cached_specifier_in_window (Lisp_Object specifier, | |
2711 struct window *w) | |
2712 { | |
2713 Lisp_Object window = Qnil; | |
2714 Lisp_Object newval, *location; | |
2715 | |
2716 XSETWINDOW (window, w); | |
2717 | |
2718 newval = specifier_instance (specifier, Qunbound, window, ERROR_ME_WARN, | |
2719 0, 0, Qzero); | |
2720 /* If newval ended up Qunbound, then the calling functions | |
2721 better be able to deal. If not, set a default so this | |
2722 never happens or correct it in the value_changed_in_window | |
2723 method. */ | |
2724 location = (Lisp_Object *) | |
2725 ((char *) w + XSPECIFIER (specifier)->caching->offset_into_struct_window); | |
2726 if (!EQ (newval, *location)) | |
2727 { | |
2728 Lisp_Object oldval = *location; | |
2729 *location = newval; | |
2730 (XSPECIFIER (specifier)->caching->value_changed_in_window) | |
2731 (specifier, w, oldval); | |
2732 } | |
2733 } | |
2734 | |
2735 static void | |
2736 recompute_one_cached_specifier_in_frame (Lisp_Object specifier, | |
2737 struct frame *f) | |
2738 { | |
2739 Lisp_Object frame = Qnil; | |
2740 Lisp_Object newval, *location; | |
2741 | |
2742 XSETFRAME (frame, f); | |
2743 | |
2744 newval = specifier_instance (specifier, Qunbound, frame, ERROR_ME_WARN, | |
2745 0, 0, Qzero); | |
2746 /* If newval ended up Qunbound, then the calling functions | |
2747 better be able to deal. If not, set a default so this | |
2748 never happens or correct it in the value_changed_in_frame | |
2749 method. */ | |
2750 location = (Lisp_Object *) | |
2751 ((char *) f + XSPECIFIER (specifier)->caching->offset_into_struct_frame); | |
2752 if (!EQ (newval, *location)) | |
2753 { | |
2754 Lisp_Object oldval = *location; | |
2755 *location = newval; | |
2756 (XSPECIFIER (specifier)->caching->value_changed_in_frame) | |
2757 (specifier, f, oldval); | |
2758 } | |
2759 } | |
2760 | |
2761 void | |
2762 recompute_all_cached_specifiers_in_window (struct window *w) | |
2763 { | |
2764 Lisp_Object rest; | |
2765 | |
2766 LIST_LOOP (rest, Vcached_specifiers) | |
2767 { | |
2768 Lisp_Object specifier = XCAR (rest); | |
2769 if (XSPECIFIER (specifier)->caching->offset_into_struct_window) | |
2770 recompute_one_cached_specifier_in_window (specifier, w); | |
2771 } | |
2772 } | |
2773 | |
2774 void | |
2775 recompute_all_cached_specifiers_in_frame (struct frame *f) | |
2776 { | |
2777 Lisp_Object rest; | |
2778 | |
2779 LIST_LOOP (rest, Vcached_specifiers) | |
2780 { | |
2781 Lisp_Object specifier = XCAR (rest); | |
2782 if (XSPECIFIER (specifier)->caching->offset_into_struct_frame) | |
2783 recompute_one_cached_specifier_in_frame (specifier, f); | |
2784 } | |
2785 } | |
2786 | |
2787 static int | |
2788 recompute_cached_specifier_everywhere_mapfun (struct window *w, | |
2789 void *closure) | |
2790 { | |
2791 Lisp_Object specifier = Qnil; | |
2792 | |
2793 VOID_TO_LISP (specifier, closure); | |
2794 recompute_one_cached_specifier_in_window (specifier, w); | |
2795 return 0; | |
2796 } | |
2797 | |
2798 static void | |
2799 recompute_cached_specifier_everywhere (Lisp_Object specifier) | |
2800 { | |
2801 Lisp_Object frmcons, devcons, concons; | |
2802 | |
2803 if (!XSPECIFIER (specifier)->caching) | |
2804 return; | |
2805 | |
2806 if (XSPECIFIER (specifier)->caching->offset_into_struct_window) | |
2807 { | |
2808 FRAME_LOOP_NO_BREAK (frmcons, devcons, concons) | |
2809 map_windows (XFRAME (XCAR (frmcons)), | |
2810 recompute_cached_specifier_everywhere_mapfun, | |
2811 LISP_TO_VOID (specifier)); | |
2812 } | |
2813 | |
2814 if (XSPECIFIER (specifier)->caching->offset_into_struct_frame) | |
2815 { | |
2816 FRAME_LOOP_NO_BREAK (frmcons, devcons, concons) | |
2817 recompute_one_cached_specifier_in_frame (specifier, | |
2818 XFRAME (XCAR (frmcons))); | |
2819 } | |
2820 } | |
2821 | |
2822 DEFUN ("set-specifier-dirty-flag", Fset_specifier_dirty_flag, | |
2823 Sset_specifier_dirty_flag, 1, 1, 0 /* | |
2824 Force recomputation of any caches associated with SPECIFIER. | |
2825 Note that this automatically happens whenever you change a specification | |
2826 in SPECIFIER; you do not have to call this function then. | |
2827 One example of where this function is useful is when you have a | |
2828 toolbar button whose `active-p' field is an expression to be | |
2829 evaluated. Calling `set-specifier-dirty-flag' on the | |
2830 toolbar specifier will force the `active-p' fields to be | |
2831 recomputed. | |
2832 */ ) | |
2833 (specifier) | |
2834 Lisp_Object specifier; | |
2835 { | |
2836 CHECK_SPECIFIER (specifier); | |
2837 recompute_cached_specifier_everywhere (specifier); | |
2838 return Qnil; | |
2839 } | |
2840 | |
2841 | |
2842 /************************************************************************/ | |
2843 /* Generic specifier type */ | |
2844 /************************************************************************/ | |
2845 | |
2846 DEFINE_SPECIFIER_TYPE (generic); | |
2847 | |
2848 #if 0 | |
2849 | |
2850 /* This is the string that used to be in `generic-specifier-p'. | |
2851 The idea is good, but it doesn't quite work in the form it's | |
2852 in. (One major problem is that validating an instantiator | |
2853 is supposed to require only that the specifier type is passed, | |
2854 while with this approach the actual specifier is needed.) | |
2855 | |
2856 What really needs to be done is to write a function | |
2857 `make-specifier-type' that creates new specifier types. | |
2858 #### I'll look into this for 19.14. | |
2859 */ | |
2860 | |
2861 "A generic specifier is a generalized kind of specifier with user-defined\n" | |
2862 "semantics. The instantiator can be any kind of Lisp object, and the\n" | |
2863 "instance computed from it is likewise any kind of Lisp object. The\n" | |
2864 "SPECIFIER-DATA should be an alist of methods governing how the specifier\n" | |
2865 "works. All methods are optional, and reasonable default methods will be\n" | |
2866 "provided. Currently there are two defined methods: 'instantiate and\n" | |
2867 "'validate.\n" | |
2868 "\n" | |
2869 "'instantiate specifies how to do the instantiation; if omitted, the\n" | |
2870 "instantiator itself is simply returned as the instance. The method\n" | |
2871 "should be a function that accepts three parameters (a specifier, the\n" | |
2872 "instantiator that matched the domain being instantiated over, and that\n" | |
2873 "domain), and should return a one-element list containing the instance,\n" | |
2874 "or nil if no instance exists. Note that the domain passed to this function\n" | |
2875 "is the domain being instantiated over, which may not be the same as the\n" | |
2876 "locale contained in the specification corresponding to the instantiator\n" | |
2877 "(for example, the domain being instantiated over could be a window, but\n" | |
2878 "the locale corresponding to the passed instantiator could be the window's\n" | |
2879 "buffer or frame).\n" | |
2880 "\n" | |
2881 "'validate specifies whether a given instantiator is valid; if omitted,\n" | |
2882 "all instantiators are considered valid. It should be a function of\n" | |
2883 "two arguments: an instantiator and a flag CAN-SIGNAL-ERROR. If this\n" | |
2884 "flag is false, the function must simply return t or nil indicating\n" | |
2885 "whether the instantiator is valid. If this flag is true, the function\n" | |
2886 "is free to signal an error if it encounters an invalid instantiator\n" | |
2887 "(this can be useful for issuing a specific error about exactly why the\n" | |
2888 "instantiator is valid). It can also return nil to indicate an invalid\n" | |
2889 "instantiator; in this case, a general error will be signalled." | |
2890 | |
2891 #endif /* 0 */ | |
2892 | |
2893 DEFUN ("generic-specifier-p", Fgeneric_specifier_p, | |
2894 Sgeneric_specifier_p, 1, 1, 0 /* | |
2895 Return non-nil if OBJECT is a generic specifier. | |
2896 | |
2897 A generic specifier allows any kind of Lisp object as an instantiator, | |
2898 and returns back the Lisp object unchanged when it is instantiated. | |
2899 */ ) | |
2900 (object) | |
2901 Lisp_Object object; | |
2902 { | |
2903 return (GENERIC_SPECIFIERP (object) ? Qt : Qnil); | |
2904 } | |
2905 | |
2906 | |
2907 /************************************************************************/ | |
2908 /* Integer specifier type */ | |
2909 /************************************************************************/ | |
2910 | |
2911 DEFINE_SPECIFIER_TYPE (integer); | |
2912 | |
2913 static void | |
2914 integer_validate (Lisp_Object instantiator) | |
2915 { | |
2916 CHECK_INT (instantiator); | |
2917 } | |
2918 | |
2919 DEFUN ("integer-specifier-p", Finteger_specifier_p, | |
2920 Sinteger_specifier_p, 1, 1, 0 /* | |
2921 Return non-nil if OBJECT is an integer specifier. | |
2922 */ ) | |
2923 (object) | |
2924 Lisp_Object object; | |
2925 { | |
2926 return (INTEGER_SPECIFIERP (object) ? Qt : Qnil); | |
2927 } | |
2928 | |
2929 /************************************************************************/ | |
2930 /* Non-negative-integer specifier type */ | |
2931 /************************************************************************/ | |
2932 | |
2933 DEFINE_SPECIFIER_TYPE (natnum); | |
2934 | |
2935 static void | |
2936 natnum_validate (Lisp_Object instantiator) | |
2937 { | |
2938 CHECK_NATNUM (instantiator); | |
2939 } | |
2940 | |
2941 DEFUN ("natnum-specifier-p", Fnatnum_specifier_p, | |
2942 Snatnum_specifier_p, 1, 1, 0 /* | |
2943 Return non-nil if OBJECT is a natnum (non-negative-integer) specifier. | |
2944 */ ) | |
2945 (object) | |
2946 Lisp_Object object; | |
2947 { | |
2948 return (NATNUM_SPECIFIERP (object) ? Qt : Qnil); | |
2949 } | |
2950 | |
2951 /************************************************************************/ | |
2952 /* Boolean specifier type */ | |
2953 /************************************************************************/ | |
2954 | |
2955 DEFINE_SPECIFIER_TYPE (boolean); | |
2956 | |
2957 static void | |
2958 boolean_validate (Lisp_Object instantiator) | |
2959 { | |
2960 if (!EQ (instantiator, Qt) && !EQ (instantiator, Qnil)) | |
2961 signal_simple_error ("Must be t or nil", instantiator); | |
2962 } | |
2963 | |
2964 DEFUN ("boolean-specifier-p", Fboolean_specifier_p, | |
2965 Sboolean_specifier_p, 1, 1, 0 /* | |
2966 Return non-nil if OBJECT is an boolean specifier. | |
2967 */ ) | |
2968 (object) | |
2969 Lisp_Object object; | |
2970 { | |
2971 return (BOOLEAN_SPECIFIERP (object) ? Qt : Qnil); | |
2972 } | |
2973 | |
2974 | |
2975 /************************************************************************/ | |
2976 /* Initialization */ | |
2977 /************************************************************************/ | |
2978 | |
2979 void | |
2980 syms_of_specifier (void) | |
2981 { | |
2982 defsymbol (&Qspecifierp, "specifierp"); | |
2983 | |
2984 defsymbol (&Qconsole_type, "console-type"); | |
2985 defsymbol (&Qdevice_class, "device-class"); | |
2986 | |
2987 /* Qinteger, Qboolean, Qgeneric defined in general.c */ | |
2988 defsymbol (&Qnatnum, "natnum"); | |
2989 | |
2990 defsubr (&Svalid_specifier_type_p); | |
2991 defsubr (&Sspecifier_type_list); | |
2992 defsubr (&Smake_specifier); | |
2993 defsubr (&Sspecifierp); | |
2994 defsubr (&Sspecifier_type); | |
2995 | |
2996 defsubr (&Svalid_specifier_locale_p); | |
2997 defsubr (&Svalid_specifier_domain_p); | |
2998 defsubr (&Svalid_specifier_locale_type_p); | |
2999 defsubr (&Sspecifier_locale_type_from_locale); | |
3000 | |
3001 defsubr (&Svalid_specifier_tag_p); | |
3002 defsubr (&Svalid_specifier_tag_set_p); | |
3003 defsubr (&Scanonicalize_tag_set); | |
3004 defsubr (&Sdevice_matches_specifier_tag_set_p); | |
3005 defsubr (&Sdefine_specifier_tag); | |
3006 defsubr (&Sdevice_matching_specifier_tag_list); | |
3007 defsubr (&Sspecifier_tag_list); | |
3008 defsubr (&Sspecifier_tag_predicate); | |
3009 | |
3010 defsubr (&Scheck_valid_instantiator); | |
3011 defsubr (&Svalid_instantiator_p); | |
3012 defsubr (&Scheck_valid_inst_list); | |
3013 defsubr (&Svalid_inst_list_p); | |
3014 defsubr (&Scheck_valid_spec_list); | |
3015 defsubr (&Svalid_spec_list_p); | |
3016 defsubr (&Sadd_spec_to_specifier); | |
3017 defsubr (&Sadd_spec_list_to_specifier); | |
3018 defsubr (&Sspecifier_spec_list); | |
3019 defsubr (&Sspecifier_specs); | |
3020 defsubr (&Sremove_specifier); | |
3021 defsubr (&Scopy_specifier); | |
3022 | |
3023 defsubr (&Scheck_valid_specifier_matchspec); | |
3024 defsubr (&Svalid_specifier_matchspec_p); | |
3025 defsubr (&Sspecifier_fallback); | |
3026 defsubr (&Sspecifier_instance); | |
3027 defsubr (&Sspecifier_matching_instance); | |
3028 defsubr (&Sspecifier_instance_from_inst_list); | |
3029 defsubr (&Sspecifier_matching_instance_from_inst_list); | |
3030 defsubr (&Sset_specifier_dirty_flag); | |
3031 | |
3032 defsubr (&Sgeneric_specifier_p); | |
3033 defsubr (&Sinteger_specifier_p); | |
3034 defsubr (&Snatnum_specifier_p); | |
3035 defsubr (&Sboolean_specifier_p); | |
3036 | |
3037 /* Symbols pertaining to specifier creation. Specifiers are created | |
3038 in the syms_of() functions. */ | |
3039 | |
3040 /* locales are defined in general.c. */ | |
3041 | |
3042 defsymbol (&Qprepend, "prepend"); | |
3043 defsymbol (&Qappend, "append"); | |
3044 defsymbol (&Qremove_tag_set_prepend, "remove-tag-set-prepend"); | |
3045 defsymbol (&Qremove_tag_set_append, "remove-tag-set-append"); | |
3046 defsymbol (&Qremove_locale, "remove-locale"); | |
3047 defsymbol (&Qremove_locale_type, "remove-locale-type"); | |
3048 defsymbol (&Qremove_all, "remove-all"); | |
3049 | |
3050 defsymbol (&Qfallback, "fallback"); | |
3051 } | |
3052 | |
3053 void | |
3054 specifier_type_create (void) | |
3055 { | |
3056 the_specifier_type_entry_dynarr = Dynarr_new (struct specifier_type_entry); | |
3057 | |
3058 Vspecifier_type_list = Qnil; | |
3059 staticpro (&Vspecifier_type_list); | |
3060 | |
3061 INITIALIZE_SPECIFIER_TYPE (generic, "generic", "generic-specifier-p"); | |
3062 | |
3063 INITIALIZE_SPECIFIER_TYPE (integer, "integer", "integer-specifier-p"); | |
3064 | |
3065 SPECIFIER_HAS_METHOD (integer, validate); | |
3066 | |
3067 INITIALIZE_SPECIFIER_TYPE (natnum, "natnum", "natnum-specifier-p"); | |
3068 | |
3069 SPECIFIER_HAS_METHOD (natnum, validate); | |
3070 | |
3071 INITIALIZE_SPECIFIER_TYPE (boolean, "boolean", "boolean-specifier-p"); | |
3072 | |
3073 SPECIFIER_HAS_METHOD (boolean, validate); | |
3074 } | |
3075 | |
3076 void | |
3077 vars_of_specifier (void) | |
3078 { | |
3079 Vcached_specifiers = Qnil; | |
3080 staticpro (&Vcached_specifiers); | |
3081 | |
3082 /* Do NOT mark through this, or specifiers will never be GC'd. | |
3083 This is the same deal as for weak hashtables. */ | |
3084 Vall_specifiers = Qnil; | |
3085 | |
3086 Vuser_defined_tags = Qnil; | |
3087 staticpro (&Vuser_defined_tags); | |
3088 } |