update.
[chise/xemacs-chise.git.1] / src / specifier.c
index 6c5942a..017663d 100644 (file)
@@ -41,15 +41,15 @@ Boston, MA 02111-1307, USA.  */
 #include "rangetab.h"
 
 Lisp_Object Qspecifierp;
-Lisp_Object Qprepend, Qappend, Qremove_tag_set_prepend, Qremove_tag_set_append;
-Lisp_Object Qremove_locale, Qremove_locale_type, Qremove_all;
-Lisp_Object Qfallback;
-
-/* Qinteger, Qboolean, Qgeneric defined in general.c. */
-Lisp_Object Qnatnum;
+Lisp_Object Qremove_tag_set_prepend, Qremove_tag_set_append;
+Lisp_Object Qremove_locale, Qremove_locale_type;
 
 Lisp_Object Qconsole_type, Qdevice_class;
 
+Lisp_Object Qspecifier_syntax_error;
+Lisp_Object Qspecifier_argument_error;
+Lisp_Object Qspecifier_change_error;
+
 static Lisp_Object Vuser_defined_tags;
 
 typedef struct specifier_type_entry specifier_type_entry;
@@ -64,7 +64,29 @@ typedef struct
   Dynarr_declare (specifier_type_entry);
 } specifier_type_entry_dynarr;
 
-specifier_type_entry_dynarr *the_specifier_type_entry_dynarr;
+static specifier_type_entry_dynarr *the_specifier_type_entry_dynarr;
+
+static const struct lrecord_description ste_description_1[] = {
+  { XD_LISP_OBJECT, offsetof (specifier_type_entry, symbol) },
+  { XD_STRUCT_PTR,  offsetof (specifier_type_entry, meths), 1,
+    &specifier_methods_description },
+  { XD_END }
+};
+
+static const struct struct_description ste_description = {
+  sizeof (specifier_type_entry),
+  ste_description_1
+};
+
+static const struct lrecord_description sted_description_1[] = {
+  XD_DYNARR_DESC (specifier_type_entry_dynarr, &ste_description),
+  { XD_END }
+};
+
+static const struct struct_description sted_description = {
+  sizeof (specifier_type_entry_dynarr),
+  sted_description_1
+};
 
 static Lisp_Object Vspecifier_type_list;
 
@@ -141,7 +163,7 @@ cleanup_specifiers (void)
        !NILP (rest);
        rest = XSPECIFIER (rest)->next_specifier)
     {
-      struct Lisp_Specifier *sp = XSPECIFIER (rest);
+      Lisp_Specifier *sp = XSPECIFIER (rest);
       /* This effectively changes the specifier specs.
         However, there's no need to call
         recompute_cached_specifier_everywhere() or the
@@ -168,7 +190,7 @@ kill_specifier_buffer_locals (Lisp_Object buffer)
        !NILP (rest);
        rest = XSPECIFIER (rest)->next_specifier)
     {
-      struct Lisp_Specifier *sp = XSPECIFIER (rest);
+      Lisp_Specifier *sp = XSPECIFIER (rest);
 
       /* Make sure we're actually going to be changing something.
         Fremove_specifier() always calls
@@ -180,19 +202,19 @@ kill_specifier_buffer_locals (Lisp_Object buffer)
 }
 
 static Lisp_Object
-mark_specifier (Lisp_Object obj, void (*markobj) (Lisp_Object))
+mark_specifier (Lisp_Object obj)
 {
-  struct Lisp_Specifier *specifier = XSPECIFIER (obj);
+  Lisp_Specifier *specifier = XSPECIFIER (obj);
 
-  markobj (specifier->global_specs);
-  markobj (specifier->device_specs);
-  markobj (specifier->frame_specs);
-  markobj (specifier->window_specs);
-  markobj (specifier->buffer_specs);
-  markobj (specifier->magic_parent);
-  markobj (specifier->fallback);
+  mark_object (specifier->global_specs);
+  mark_object (specifier->device_specs);
+  mark_object (specifier->frame_specs);
+  mark_object (specifier->window_specs);
+  mark_object (specifier->buffer_specs);
+  mark_object (specifier->magic_parent);
+  mark_object (specifier->fallback);
   if (!GHOST_SPECIFIER_P (XSPECIFIER (obj)))
-    MAYBE_SPECMETH (specifier, mark, (obj, markobj));
+    MAYBE_SPECMETH (specifier, mark, (obj));
   return Qnil;
 }
 
@@ -216,24 +238,24 @@ mark_specifier (Lisp_Object obj, void (*markobj) (Lisp_Object))
 */
 
 void
-prune_specifiers (int (*obj_marked_p) (Lisp_Object))
+prune_specifiers (void)
 {
   Lisp_Object rest, prev = Qnil;
 
   for (rest = Vall_specifiers;
-       !GC_NILP (rest);
+       !NILP (rest);
        rest = XSPECIFIER (rest)->next_specifier)
     {
-      if (! obj_marked_p (rest))
+      if (! marked_p (rest))
        {
-         struct Lisp_Specifier* sp = XSPECIFIER (rest);
+         Lisp_Specifier* sp = XSPECIFIER (rest);
          /* A bit of assertion that we're removing both parts of the
              magic one altogether */
-         assert (!GC_MAGIC_SPECIFIER_P(sp)
-                 || (GC_BODILY_SPECIFIER_P(sp) && obj_marked_p (sp->fallback))
-                 || (GC_GHOST_SPECIFIER_P(sp) && obj_marked_p (sp->magic_parent)));
+         assert (!MAGIC_SPECIFIER_P(sp)
+                 || (BODILY_SPECIFIER_P(sp) && marked_p (sp->fallback))
+                 || (GHOST_SPECIFIER_P(sp) && marked_p (sp->magic_parent)));
          /* This specifier is garbage.  Remove it from the list. */
-         if (GC_NILP (prev))
+         if (NILP (prev))
            Vall_specifiers = sp->next_specifier;
          else
            XSPECIFIER (prev)->next_specifier = sp->next_specifier;
@@ -246,7 +268,7 @@ prune_specifiers (int (*obj_marked_p) (Lisp_Object))
 static void
 print_specifier (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
 {
-  struct Lisp_Specifier *sp = XSPECIFIER (obj);
+  Lisp_Specifier *sp = XSPECIFIER (obj);
   char buf[100];
   int count = specpdl_depth ();
   Lisp_Object the_specs;
@@ -278,9 +300,9 @@ print_specifier (Lisp_Object obj, Lisp_Object printcharfun, int escapeflag)
 static void
 finalize_specifier (void *header, int for_disksave)
 {
-  struct Lisp_Specifier *sp = (struct Lisp_Specifier *) header;
+  Lisp_Specifier *sp = (Lisp_Specifier *) header;
   /* don't be snafued by the disksave finalization. */
-  if (!for_disksave && !GC_GHOST_SPECIFIER_P(sp) && sp->caching)
+  if (!for_disksave && !GHOST_SPECIFIER_P(sp) && sp->caching)
     {
       xfree (sp->caching);
       sp->caching = 0;
@@ -290,8 +312,8 @@ finalize_specifier (void *header, int for_disksave)
 static int
 specifier_equal (Lisp_Object obj1, Lisp_Object obj2, int depth)
 {
-  struct Lisp_Specifier *s1 = XSPECIFIER (obj1);
-  struct Lisp_Specifier *s2 = XSPECIFIER (obj2);
+  Lisp_Specifier *s1 = XSPECIFIER (obj1);
+  Lisp_Specifier *s2 = XSPECIFIER (obj2);
   int retval;
   Lisp_Object old_inhibit_quit = Vinhibit_quit;
 
@@ -319,7 +341,7 @@ specifier_equal (Lisp_Object obj1, Lisp_Object obj2, int depth)
 static unsigned long
 specifier_hash (Lisp_Object obj, int depth)
 {
-  struct Lisp_Specifier *s = XSPECIFIER (obj);
+  Lisp_Specifier *s = XSPECIFIER (obj);
 
   /* specifier hashing is a bit problematic because there are so
      many places where data can be stored.  We pick what are perhaps
@@ -332,24 +354,69 @@ specifier_hash (Lisp_Object obj, int depth)
                internal_hash (s->buffer_specs, depth + 1));
 }
 
+inline static size_t
+aligned_sizeof_specifier (size_t specifier_type_specific_size)
+{
+  return ALIGN_SIZE (offsetof (Lisp_Specifier, data)
+                    + specifier_type_specific_size,
+                    ALIGNOF (max_align_t));
+}
+
 static size_t
-sizeof_specifier (CONST void *header)
+sizeof_specifier (const void *header)
 {
-  if (GHOST_SPECIFIER_P ((struct Lisp_Specifier *) header))
-    return sizeof (struct Lisp_Specifier);
-  else
-    {
-      CONST struct Lisp_Specifier *p = (CONST struct Lisp_Specifier *) header;
-      return sizeof (*p) + p->methods->extra_data_size - 1;
-    }
+  const Lisp_Specifier *p = (const Lisp_Specifier *) header;
+  return aligned_sizeof_specifier (GHOST_SPECIFIER_P (p)
+                                  ? 0
+                                  : p->methods->extra_data_size);
 }
 
+static const struct lrecord_description specifier_methods_description_1[] = {
+  { XD_LISP_OBJECT, offsetof (struct specifier_methods, predicate_symbol) },
+  { XD_END }
+};
+
+const struct struct_description specifier_methods_description = {
+  sizeof (struct specifier_methods),
+  specifier_methods_description_1
+};
+
+static const struct lrecord_description specifier_caching_description_1[] = {
+  { XD_END }
+};
+
+static const struct struct_description specifier_caching_description = {
+  sizeof (struct specifier_caching),
+  specifier_caching_description_1
+};
+
+static const struct lrecord_description specifier_description[] = {
+  { XD_STRUCT_PTR,  offsetof (Lisp_Specifier, methods), 1,
+    &specifier_methods_description },
+  { XD_LO_LINK,     offsetof (Lisp_Specifier, next_specifier) },
+  { XD_LISP_OBJECT, offsetof (Lisp_Specifier, global_specs) },
+  { XD_LISP_OBJECT, offsetof (Lisp_Specifier, device_specs) },
+  { XD_LISP_OBJECT, offsetof (Lisp_Specifier, frame_specs) },
+  { XD_LISP_OBJECT, offsetof (Lisp_Specifier, window_specs) },
+  { XD_LISP_OBJECT, offsetof (Lisp_Specifier, buffer_specs) },
+  { XD_STRUCT_PTR,  offsetof (Lisp_Specifier, caching), 1,
+    &specifier_caching_description },
+  { XD_LISP_OBJECT, offsetof (Lisp_Specifier, magic_parent) },
+  { XD_LISP_OBJECT, offsetof (Lisp_Specifier, fallback) },
+  { XD_SPECIFIER_END }
+};
+
+const struct lrecord_description specifier_empty_extra_description[] = {
+  { XD_END }
+};
+
 DEFINE_LRECORD_SEQUENCE_IMPLEMENTATION ("specifier", specifier,
                                        mark_specifier, print_specifier,
                                        finalize_specifier,
-                                       specifier_equal, specifier_hash, 0,
+                                       specifier_equal, specifier_hash,
+                                       specifier_description,
                                        sizeof_specifier,
-                                       struct Lisp_Specifier);
+                                       Lisp_Specifier);
 \f
 /************************************************************************/
 /*                       Creating specifiers                            */
@@ -366,8 +433,8 @@ decode_specifier_type (Lisp_Object type, Error_behavior errb)
        return Dynarr_at (the_specifier_type_entry_dynarr, i).meths;
     }
 
-  maybe_signal_simple_error ("Invalid specifier type", type,
-                            Qspecifier, errb);
+  maybe_signal_type_error (Qspecifier_argument_error, "Invalid specifier type",
+                          type, Qspecifier, errb);
 
   return 0;
 }
@@ -380,7 +447,7 @@ valid_specifier_type_p (Lisp_Object type)
 
 DEFUN ("valid-specifier-type-p", Fvalid_specifier_type_p, 1, 1, 0, /*
 Given a SPECIFIER-TYPE, return non-nil if it is valid.
-Valid types are 'generic, 'integer, boolean, 'color, 'font, 'image,
+Valid types are 'generic, 'integer, 'boolean, 'color, 'font, 'image,
 'face-boolean, and 'toolbar.
 */
        (specifier_type))
@@ -413,9 +480,8 @@ make_specifier_internal (struct specifier_methods *spec_meths,
                         size_t data_size, int call_create_meth)
 {
   Lisp_Object specifier;
-  struct Lisp_Specifier *sp = (struct Lisp_Specifier *)
-    alloc_lcrecord (sizeof (struct Lisp_Specifier) +
-                   data_size - 1, &lrecord_specifier);
+  Lisp_Specifier *sp = (Lisp_Specifier *)
+    alloc_lcrecord (aligned_sizeof_specifier (data_size), &lrecord_specifier);
 
   sp->methods = spec_meths;
   sp->global_specs = Qnil;
@@ -473,29 +539,52 @@ Return a new specifier object of type TYPE.
 
 A specifier is an object that can be used to keep track of a property
 whose value can be per-buffer, per-window, per-frame, or per-device,
-and can further be restricted to a particular console-type or device-class.
-Specifiers are used, for example, for the various built-in properties of a
-face; this allows a face to have different values in different frames,
-buffers, etc.  For more information, see `specifier-instance',
+and can further be restricted to a particular console-type or
+device-class.  Specifiers are used, for example, for the various
+built-in properties of a face; this allows a face to have different
+values in different frames, buffers, etc.
+
+When speaking of the value of a specifier, it is important to
+distinguish between the *setting* of a specifier, called an
+\"instantiator\", and the *actual value*, called an \"instance\".  You
+put various possible instantiators (i.e. settings) into a specifier
+and associate them with particular locales (buffer, window, frame,
+device, global), and then the instance (i.e. actual value) is
+retrieved in a specific domain (window, frame, device) by looking
+through the possible instantiators (i.e. settings).  This process is
+called \"instantiation\".
+
+To put settings into a specifier, use `set-specifier', or the
+lower-level functions `add-spec-to-specifier' and
+`add-spec-list-to-specifier'.  You can also temporarily bind a setting
+to a specifier using `let-specifier'.  To retrieve settings, use
+`specifier-specs', or its lower-level counterpart
+`specifier-spec-list'.  To determine the actual value, use
+`specifier-instance'.
+
+For more information, see `set-specifier', `specifier-instance',
 `specifier-specs', and `add-spec-to-specifier'; or, for a detailed
-description of specifiers, including how they are instantiated over a
-particular domain (i.e. how their value in that domain is determined),
-see the chapter on specifiers in the XEmacs Lisp Reference Manual.
+description of specifiers, including how exactly the instantiation
+process works, see the chapter on specifiers in the XEmacs Lisp
+Reference Manual.
 
 TYPE specifies the particular type of specifier, and should be one of
-the symbols 'generic, 'integer, 'boolean, 'color, 'font, 'image,
-'face-boolean, or 'toolbar.
-
-For more information on particular types of specifiers, see the functions
-`generic-specifier-p', `integer-specifier-p', `boolean-specifier-p',
-`color-specifier-p', `font-specifier-p', `image-specifier-p',
-`face-boolean-specifier-p', and `toolbar-specifier-p'.
+the symbols 'generic, 'integer, 'natnum, 'boolean, 'color, 'font,
+'image, 'face-boolean, 'display-table, 'gutter, 'gutter-size,
+'gutter-visible or 'toolbar.
+
+For more information on particular types of specifiers, see the
+functions `make-generic-specifier', `make-integer-specifier',
+`make-natnum-specifier', `make-boolean-specifier',
+`make-color-specifier', `make-font-specifier', `make-image-specifier',
+`make-face-boolean-specifier', `make-gutter-size-specifier',
+`make-gutter-visible-specifier', `default-toolbar', `default-gutter',
+and `current-display-table'.
 */
        (type))
 {
   /* This function can GC */
-  struct specifier_methods *meths = decode_specifier_type (type,
-                                                          ERROR_ME);
+  struct specifier_methods *meths = decode_specifier_type (type, ERROR_ME);
 
   return make_specifier (meths);
 }
@@ -548,19 +637,24 @@ Valid locales are devices, frames, windows, buffers, and 'global.
 DEFUN ("valid-specifier-domain-p", Fvalid_specifier_domain_p, 1, 1, 0, /*
 Return t if DOMAIN is a valid specifier domain.
 A domain is used to instance a specifier (i.e. determine the specifier's
-value in that domain).  Valid domains are windows, frames, and devices.
-\(nil is not valid.)
+value in that domain).  Valid domains are image instances, windows, frames,
+and devices. \(nil is not valid.) image instances are pseudo-domains since
+instantiation will actually occur in the window the image instance itself is
+instantiated in.
 */
      (domain))
 {
   /* This cannot GC. */
   return ((DEVICEP (domain) && DEVICE_LIVE_P (XDEVICE (domain))) ||
          (FRAMEP  (domain) && FRAME_LIVE_P  (XFRAME  (domain))) ||
-         (WINDOWP (domain) && WINDOW_LIVE_P (XWINDOW (domain))))
+         (WINDOWP (domain) && WINDOW_LIVE_P (XWINDOW (domain))) ||
+         /* #### get image instances out of domains! */
+         IMAGE_INSTANCEP (domain))
     ? Qt : Qnil;
 }
 
-DEFUN ("valid-specifier-locale-type-p", Fvalid_specifier_locale_type_p, 1, 1, 0, /*
+DEFUN ("valid-specifier-locale-type-p", Fvalid_specifier_locale_type_p, 1, 1, 0,
+       /*
 Given a specifier LOCALE-TYPE, return non-nil if it is valid.
 Valid locale types are 'global, 'device, 'frame, 'window, and 'buffer.
 \(Note, however, that in functions that accept either a locale or a locale
@@ -584,7 +678,8 @@ check_valid_locale_or_locale_type (Lisp_Object locale)
       !NILP (Fvalid_specifier_locale_p (locale)) ||
       !NILP (Fvalid_specifier_locale_type_p (locale)))
     return;
-  signal_simple_error ("Invalid specifier locale or locale type", locale);
+  signal_type_error (Qspecifier_argument_error,
+                    "Invalid specifier locale or locale type", locale);
 }
 
 DEFUN ("specifier-locale-type-from-locale", Fspecifier_locale_type_from_locale,
@@ -595,7 +690,8 @@ Given a specifier LOCALE, return its type.
 {
   /* This cannot GC. */
   if (NILP (Fvalid_specifier_locale_p (locale)))
-    signal_simple_error ("Invalid specifier locale", locale);
+    signal_type_error (Qspecifier_argument_error, "Invalid specifier locale",
+                      locale);
   if (DEVICEP (locale)) return Qdevice;
   if (FRAMEP  (locale)) return Qframe;
   if (WINDOWP (locale)) return Qwindow;
@@ -613,7 +709,8 @@ decode_locale (Lisp_Object locale)
   else if (!NILP (Fvalid_specifier_locale_p (locale)))
     return locale;
   else
-    signal_simple_error ("Invalid specifier locale", locale);
+    signal_type_error (Qspecifier_argument_error, "Invalid specifier locale",
+                      locale);
 
   return Qnil;
 }
@@ -628,7 +725,8 @@ decode_locale_type (Lisp_Object locale_type)
   if (EQ (locale_type, Qwindow)) return LOCALE_WINDOW;
   if (EQ (locale_type, Qbuffer)) return LOCALE_BUFFER;
 
-  signal_simple_error ("Invalid specifier locale type", locale_type);
+  signal_type_error (Qspecifier_argument_error, "Invalid specifier locale type",
+                    locale_type);
   return LOCALE_GLOBAL; /* not reached */
 }
 
@@ -643,7 +741,6 @@ decode_locale_list (Lisp_Object locale)
     }
   else if (CONSP (locale))
     {
-      Lisp_Object elt;
       EXTERNAL_LIST_LOOP_2 (elt, locale)
        check_valid_locale_or_locale_type (elt);
       return locale;
@@ -665,10 +762,11 @@ static void
 check_valid_domain (Lisp_Object domain)
 {
   if (NILP (Fvalid_specifier_domain_p (domain)))
-    signal_simple_error ("Invalid specifier domain", domain);
+    signal_type_error (Qspecifier_argument_error, "Invalid specifier domain",
+                      domain);
 }
 
-static Lisp_Object
+Lisp_Object
 decode_domain (Lisp_Object domain)
 {
   if (NILP (domain))
@@ -741,7 +839,8 @@ decode_specifier_tag_set (Lisp_Object tag_set)
   if (!NILP (Fvalid_specifier_tag_p (tag_set)))
     return list1 (tag_set);
   if (NILP (Fvalid_specifier_tag_set_p (tag_set)))
-    signal_simple_error ("Invalid specifier tag-set", tag_set);
+    signal_type_error (Qspecifier_argument_error, "Invalid specifier tag-set",
+                      tag_set);
   return tag_set;
 }
 
@@ -804,7 +903,7 @@ sorting by symbol name and removing duplicates.)
        (tag_set))
 {
   if (NILP (Fvalid_specifier_tag_set_p (tag_set)))
-    signal_simple_error ("Invalid tag set", tag_set);
+    signal_type_error (Qspecifier_argument_error, "Invalid tag set", tag_set);
   return canonicalize_tag_set (tag_set);
 }
 
@@ -834,7 +933,8 @@ device_matches_specifier_tag_set_p (Lisp_Object device, Lisp_Object tag_set)
   return 1;
 }
 
-DEFUN ("device-matches-specifier-tag-set-p", Fdevice_matches_specifier_tag_set_p, 2, 2, 0, /*
+DEFUN ("device-matches-specifier-tag-set-p",
+       Fdevice_matches_specifier_tag_set_p, 2, 2, 0, /*
 Return non-nil if DEVICE matches specifier tag set TAG-SET.
 This means that DEVICE matches each tag in the tag set. (Every
 tag recognized by XEmacs has a predicate associated with it that
@@ -845,7 +945,7 @@ specifies which devices match it.)
   CHECK_LIVE_DEVICE (device);
 
   if (NILP (Fvalid_specifier_tag_set_p (tag_set)))
-    signal_simple_error ("Invalid tag set", tag_set);
+    signal_type_error (Qspecifier_argument_error, "Invalid tag set", tag_set);
 
   return device_matches_specifier_tag_set_p (device, tag_set) ? Qt : Qnil;
 }
@@ -868,12 +968,13 @@ and classes) or the symbols nil, t, 'all, or 'global.
   CHECK_SYMBOL (tag);
   if (valid_device_class_p (tag) ||
       valid_console_type_p (tag))
-    signal_simple_error ("Cannot redefine built-in specifier tags", tag);
+    signal_type_error (Qspecifier_change_error,
+                      "Cannot redefine built-in specifier tags", tag);
   /* Try to prevent common instantiators and locales from being
      redefined, to reduce ambiguity */
   if (NILP (tag) || EQ (tag, Qt) || EQ (tag, Qall) || EQ (tag, Qglobal))
-    signal_simple_error ("Cannot define nil, t, 'all, or 'global",
-                        tag);
+    signal_type_error (Qspecifier_change_error, "Cannot define nil, t, 'all, or 'global",
+                      tag);
   assoc = assq_no_quit (tag, Vuser_defined_tags);
   if (NILP (assoc))
     {
@@ -946,7 +1047,8 @@ setup_device_initial_specifier_tags (struct device *d)
     }
 }
 
-DEFUN ("device-matching-specifier-tag-list", Fdevice_matching_specifier_tag_list,
+DEFUN ("device-matching-specifier-tag-list",
+       Fdevice_matching_specifier_tag_list,
        0, 1, 0, /*
 Return a list of all specifier tags matching DEVICE.
 DEVICE defaults to the selected device if omitted.
@@ -1002,7 +1104,8 @@ Return the predicate for the given specifier tag.
   CHECK_SYMBOL (tag);
 
   if (NILP (Fvalid_specifier_tag_p (tag)))
-    signal_simple_error ("Invalid specifier tag", tag);
+    signal_type_error (Qspecifier_argument_error, "Invalid specifier tag",
+                      tag);
 
   /* Make up some predicates for the built-in types */
 
@@ -1129,19 +1232,22 @@ check_valid_inst_list (Lisp_Object inst_list, struct specifier_methods *meths,
 
       if (!CONSP (rest))
        {
-         maybe_signal_simple_error ("Invalid instantiator list", inst_list,
+         maybe_signal_type_error (Qspecifier_syntax_error,
+                                  "Invalid instantiator list", inst_list,
                                     Qspecifier, errb);
          return Qnil;
        }
       if (!CONSP (inst_pair = XCAR (rest)))
        {
-         maybe_signal_simple_error ("Invalid instantiator pair", inst_pair,
+         maybe_signal_type_error (Qspecifier_syntax_error,
+                                  "Invalid instantiator pair", inst_pair,
                                     Qspecifier, errb);
          return Qnil;
        }
       if (NILP (Fvalid_specifier_tag_set_p (tag_set = XCAR (inst_pair))))
        {
-         maybe_signal_simple_error ("Invalid specifier tag", tag_set,
+         maybe_signal_type_error (Qspecifier_syntax_error,
+                                  "Invalid specifier tag", tag_set,
                                     Qspecifier, errb);
          return Qnil;
        }
@@ -1184,13 +1290,15 @@ check_valid_spec_list (Lisp_Object spec_list, struct specifier_methods *meths,
       Lisp_Object spec, locale;
       if (!CONSP (rest) || !CONSP (spec = XCAR (rest)))
        {
-         maybe_signal_simple_error ("Invalid specification list", spec_list,
+         maybe_signal_type_error (Qspecifier_syntax_error,
+                                  "Invalid specification list", spec_list,
                                     Qspecifier, errb);
          return Qnil;
        }
       if (NILP (Fvalid_specifier_locale_p (locale = XCAR (spec))))
        {
-         maybe_signal_simple_error ("Invalid specifier locale", locale,
+         maybe_signal_type_error (Qspecifier_syntax_error,
+                                  "Invalid specifier locale", locale,
                                     Qspecifier, errb);
          return Qnil;
        }
@@ -1240,7 +1348,8 @@ decode_how_to_add_specification (Lisp_Object how_to_add)
   if (EQ (Qremove_all, how_to_add))
     return SPEC_REMOVE_ALL;
 
-  signal_simple_error ("Invalid `how-to-add' flag", how_to_add);
+  signal_type_error (Qspecifier_argument_error, "Invalid `how-to-add' flag",
+                    how_to_add);
 
   return SPEC_PREPEND;         /* not reached */
 }
@@ -1264,7 +1373,8 @@ check_modifiable_specifier (Lisp_Object spec)
 {
   if (NILP (Vunlock_ghost_specifiers)
       && GHOST_SPECIFIER_P (XSPECIFIER (spec)))
-    signal_simple_error ("Attempt to modify read-only specifier",
+    signal_type_error (Qspecifier_change_error,
+                      "Attempt to modify read-only specifier",
                         list1 (spec));
 }
 
@@ -1587,17 +1697,23 @@ build_up_processed_list (Lisp_Object specifier, Lisp_Object locale,
 {
   /* The return value of this function must be GCPRO'd. */
   Lisp_Object rest, list_to_build_up = Qnil;
-  struct Lisp_Specifier *sp = XSPECIFIER (specifier);
+  Lisp_Specifier *sp = XSPECIFIER (specifier);
   struct gcpro gcpro1;
 
   GCPRO1 (list_to_build_up);
   LIST_LOOP (rest, inst_list)
     {
       Lisp_Object tag_set = XCAR (XCAR (rest));
-      Lisp_Object instantiator = Fcopy_tree (XCDR (XCAR (rest)), Qt);
       Lisp_Object sub_inst_list = Qnil;
+      Lisp_Object instantiator;
       struct gcpro ngcpro1, ngcpro2;
 
+      if (HAS_SPECMETH_P (sp, copy_instantiator))
+       instantiator = SPECMETH (sp, copy_instantiator,
+                                (XCDR (XCAR (rest))));
+      else
+       instantiator = Fcopy_tree (XCDR (XCAR (rest)), Qt);
+
       NGCPRO2 (instantiator, sub_inst_list);
       /* call the will-add method; it may GC */
       sub_inst_list = HAS_SPECMETH_P (sp, going_to_add) ?
@@ -1638,7 +1754,7 @@ static void
 specifier_add_spec (Lisp_Object specifier, Lisp_Object locale,
                    Lisp_Object inst_list, enum spec_add_meth add_meth)
 {
-  struct Lisp_Specifier *sp = XSPECIFIER (specifier);
+  Lisp_Specifier *sp = XSPECIFIER (specifier);
   enum spec_locale_type type = locale_type_from_locale (locale);
   Lisp_Object *orig_inst_list, tem;
   Lisp_Object list_to_build_up = Qnil;
@@ -1670,7 +1786,10 @@ specifier_add_spec (Lisp_Object specifier, Lisp_Object locale,
   else if (add_meth == SPEC_APPEND)
     tem = nconc2 (*orig_inst_list, list_to_build_up);
   else
-    abort ();
+    {
+      ABORT ();
+      tem = Qnil;
+    }
 
   *orig_inst_list = tem;
 
@@ -1871,8 +1990,8 @@ with the function `specifier-spec-list' or `specifier-specs'.
 }
 
 DEFUN ("add-spec-list-to-specifier", Fadd_spec_list_to_specifier, 2, 3, 0, /*
-Add a spec-list (a list of specifications) to SPECIFIER.
-The format of a spec-list is
+Add SPEC-LIST (a list of specifications) to SPECIFIER.
+The format of SPEC-LIST is
 
   ((LOCALE (TAG-SET . INSTANTIATOR) ...) ...)
 
@@ -2278,7 +2397,8 @@ check_valid_specifier_matchspec (Lisp_Object matchspec,
     }
 }
 
-DEFUN ("check-valid-specifier-matchspec", Fcheck_valid_specifier_matchspec, 2, 2, 0, /*
+DEFUN ("check-valid-specifier-matchspec", Fcheck_valid_specifier_matchspec, 2,
+       2, 0, /*
 Signal an error if MATCHSPEC is invalid for SPECIFIER-TYPE.
 See `specifier-matching-instance' for a description of matchspecs.
 */
@@ -2309,7 +2429,7 @@ See `specifier-matching-instance' for a description of matchspecs.
 void
 set_specifier_fallback (Lisp_Object specifier, Lisp_Object fallback)
 {
-  struct Lisp_Specifier *sp = XSPECIFIER (specifier);
+  Lisp_Specifier *sp = XSPECIFIER (specifier);
   assert (SPECIFIERP (fallback) ||
          !NILP (Fvalid_inst_list_p (fallback, Fspecifier_type (specifier))));
   if (SPECIFIERP (fallback))
@@ -2359,7 +2479,7 @@ specifier_instance_from_inst_list (Lisp_Object specifier,
                                   Lisp_Object depth)
 {
   /* This function can GC */
-  struct Lisp_Specifier *sp;
+  Lisp_Specifier *sp;
   Lisp_Object device;
   Lisp_Object rest;
   int count = specpdl_depth ();
@@ -2368,13 +2488,13 @@ specifier_instance_from_inst_list (Lisp_Object specifier,
   GCPRO2 (specifier, inst_list);
 
   sp = XSPECIFIER (specifier);
-  device = DFW_DEVICE (domain);
+  device = DOMAIN_DEVICE (domain);
 
   if (no_quit)
   /* The instantiate method is allowed to call eval.  Since it
      is quite common for this function to get called from somewhere in
      redisplay we need to make sure that quits are ignored.  Otherwise
-     Fsignal will abort. */
+     Fsignal will ABORT. */
     specbind (Qinhibit_quit, Qt);
 
   LIST_LOOP (rest, inst_list)
@@ -2440,39 +2560,39 @@ specifier_instance (Lisp_Object specifier, Lisp_Object matchspec,
   Lisp_Object window = Qnil;
   Lisp_Object frame = Qnil;
   Lisp_Object device = Qnil;
-  Lisp_Object tag = Qnil;
-  struct device *d;
-  struct Lisp_Specifier *sp;
-
-  sp = XSPECIFIER (specifier);
+  Lisp_Object tag = Qnil;      /* #### currently unused */
+  Lisp_Specifier *sp = XSPECIFIER (specifier);
 
   /* Attempt to determine buffer, window, frame, and device from the
      domain. */
-  if (WINDOWP (domain))
+  /* #### get image instances out of domains! */
+  if (IMAGE_INSTANCEP (domain))
+    window = DOMAIN_WINDOW (domain);
+  else if (WINDOWP (domain))
     window = domain;
   else if (FRAMEP (domain))
     frame = domain;
   else if (DEVICEP (domain))
     device = domain;
   else
-    /* #### dmoore - dammit, this should just signal an error or something
-       shouldn't it?
-       #### No. Errors are handled in Lisp primitives implementation.
+    /* dmoore writes: [dammit, this should just signal an error or something
+       shouldn't it?]
+
+       No. Errors are handled in Lisp primitives implementation.
        Invalid domain is a design error here - kkm. */
-    abort ();
+    ABORT ();
 
   if (NILP (buffer) && !NILP (window))
-    buffer = XWINDOW (window)->buffer;
+    buffer = WINDOW_BUFFER (XWINDOW (window));
   if (NILP (frame) && !NILP (window))
     frame = XWINDOW (window)->frame;
   if (NILP (device))
     /* frame had better exist; if device is undeterminable, something
        really went wrong. */
-    device = XFRAME (frame)->device;
+    device = FRAME_DEVICE (XFRAME (frame));
 
-  /* device had better be determined by now; abort if not. */
-  d = XDEVICE (device);
-  tag = DEVICE_CLASS (d);
+  /* device had better be determined by now; ABORT if not. */
+  tag = DEVICE_CLASS (XDEVICE (device));
 
   depth = make_int (1 + XINT (depth));
   if (XINT (depth) > 20)
@@ -2485,7 +2605,7 @@ specifier_instance (Lisp_Object specifier, Lisp_Object matchspec,
       goto do_fallback;
     }
 
-retry:
+ retry:
   /* First see if we can generate one from the window specifiers. */
   if (!NILP (window))
     CHECK_INSTANCE_ENTRY (window, matchspec, LOCALE_WINDOW);
@@ -2504,7 +2624,7 @@ retry:
   /* Last and least try the global specifiers. */
   CHECK_INSTANCE_ENTRY (Qglobal, matchspec, LOCALE_GLOBAL);
 
-do_fallback:
+ do_fallback:
   /* We're out of specifiers and we still haven't generated an
      instance.  At least try the fallback ...  If this fails,
      then we just return Qunbound. */
@@ -2645,7 +2765,7 @@ you should not use this function; use `specifier-instance' instead.
        (specifier, domain, inst_list, default_))
 {
   Lisp_Object val = Qunbound;
-  struct Lisp_Specifier *sp = XSPECIFIER (specifier);
+  Lisp_Specifier *sp = XSPECIFIER (specifier);
   struct gcpro gcpro1;
   Lisp_Object built_up_list = Qnil;
 
@@ -2662,7 +2782,8 @@ you should not use this function; use `specifier-instance' instead.
   return UNBOUNDP (val) ? default_ : val;
 }
 
-DEFUN ("specifier-matching-instance-from-inst-list", Fspecifier_matching_instance_from_inst_list,
+DEFUN ("specifier-matching-instance-from-inst-list",
+       Fspecifier_matching_instance_from_inst_list,
        4, 5, 0, /*
 Attempt to convert a particular inst-list into an instance.
 This attempts to instantiate INST-LIST in the given DOMAIN
@@ -2677,7 +2798,7 @@ works.
        (specifier, matchspec, domain, inst_list, default_))
 {
   Lisp_Object val = Qunbound;
-  struct Lisp_Specifier *sp = XSPECIFIER (specifier);
+  Lisp_Specifier *sp = XSPECIFIER (specifier);
   struct gcpro gcpro1;
   Lisp_Object built_up_list = Qnil;
 
@@ -2715,9 +2836,10 @@ set_specifier_caching (Lisp_Object specifier, int struct_window_offset,
                       int struct_frame_offset,
                       void (*value_changed_in_frame)
                       (Lisp_Object specifier, struct frame *f,
-                       Lisp_Object oldval))
+                       Lisp_Object oldval),
+                      int always_recompute)
 {
-  struct Lisp_Specifier *sp = XSPECIFIER (specifier);
+  Lisp_Specifier *sp = XSPECIFIER (specifier);
   assert (!GHOST_SPECIFIER_P (sp));
 
   if (!sp->caching)
@@ -2726,6 +2848,7 @@ set_specifier_caching (Lisp_Object specifier, int struct_window_offset,
   sp->caching->value_changed_in_window = value_changed_in_window;
   sp->caching->offset_into_struct_frame = struct_frame_offset;
   sp->caching->value_changed_in_frame = value_changed_in_frame;
+  sp->caching->always_recompute = always_recompute;
   Vcached_specifiers = Fcons (specifier, Vcached_specifiers);
   if (BODILY_SPECIFIER_P (sp))
     GHOST_SPECIFIER(sp)->caching = sp->caching;
@@ -2737,7 +2860,7 @@ recompute_one_cached_specifier_in_window (Lisp_Object specifier,
                                          struct window *w)
 {
   Lisp_Object window;
-  Lisp_Object newval, *location;
+  Lisp_Object newval, *location, oldval;
 
   assert (!GHOST_SPECIFIER_P (XSPECIFIER (specifier)));
 
@@ -2751,9 +2874,16 @@ recompute_one_cached_specifier_in_window (Lisp_Object specifier,
      method. */
   location = (Lisp_Object *)
     ((char *) w + XSPECIFIER (specifier)->caching->offset_into_struct_window);
-  if (!EQ (newval, *location))
+  /* #### What's the point of this check, other than to optimize image
+     instance instantiation? Unless you specify a caching instantiate
+     method the instantiation that specifier_instance will do will
+     always create a new copy. Thus EQ will always fail. Unfortunately
+     calling equal is no good either as this doesn't take into account
+     things attached to the specifier - for instance strings on
+     extents. --andyp */
+  if (!EQ (newval, *location) || XSPECIFIER (specifier)->caching->always_recompute)
     {
-      Lisp_Object oldval = *location;
+      oldval = *location;
       *location = newval;
       (XSPECIFIER (specifier)->caching->value_changed_in_window)
        (specifier, w, oldval);
@@ -2765,7 +2895,7 @@ recompute_one_cached_specifier_in_frame (Lisp_Object specifier,
                                         struct frame *f)
 {
   Lisp_Object frame;
-  Lisp_Object newval, *location;
+  Lisp_Object newval, *location, oldval;
 
   assert (!GHOST_SPECIFIER_P (XSPECIFIER (specifier)));
 
@@ -2779,9 +2909,9 @@ recompute_one_cached_specifier_in_frame (Lisp_Object specifier,
      method. */
   location = (Lisp_Object *)
     ((char *) f + XSPECIFIER (specifier)->caching->offset_into_struct_frame);
-  if (!EQ (newval, *location))
+  if (!EQ (newval, *location) || XSPECIFIER (specifier)->caching->always_recompute)
     {
-      Lisp_Object oldval = *location;
+      oldval = *location;
       *location = newval;
       (XSPECIFIER (specifier)->caching->value_changed_in_frame)
        (specifier, f, oldval);
@@ -2885,8 +3015,9 @@ DEFINE_SPECIFIER_TYPE (generic);
 
    What really needs to be done is to write a function
    `make-specifier-type' that creates new specifier types.
-   #### I'll look into this for 19.14.
- */
+
+   #### [I'll look into this for 19.14.]  Well, sometime. (Currently
+   May 2000, 21.2 is in development.  19.14 was released in June 1996.) */
 
 "A generic specifier is a generalized kind of specifier with user-defined\n"
 "semantics.  The instantiator can be any kind of Lisp object, and the\n"
@@ -2923,8 +3054,8 @@ DEFINE_SPECIFIER_TYPE (generic);
 DEFUN ("generic-specifier-p", Fgeneric_specifier_p, 1, 1, 0, /*
 Return non-nil if OBJECT is a generic specifier.
 
-A generic specifier allows any kind of Lisp object as an instantiator,
-and returns back the Lisp object unchanged when it is instantiated.
+See `make-generic-specifier' for a description of possible generic
+instantiators.
 */
        (object))
 {
@@ -2946,6 +3077,9 @@ integer_validate (Lisp_Object instantiator)
 
 DEFUN ("integer-specifier-p", Finteger_specifier_p, 1, 1, 0, /*
 Return non-nil if OBJECT is an integer specifier.
+
+See `make-integer-specifier' for a description of possible integer
+instantiators.
 */
        (object))
 {
@@ -2966,6 +3100,9 @@ natnum_validate (Lisp_Object instantiator)
 
 DEFUN ("natnum-specifier-p", Fnatnum_specifier_p, 1, 1, 0, /*
 Return non-nil if OBJECT is a natnum (non-negative-integer) specifier.
+
+See `make-natnum-specifier' for a description of possible natnum
+instantiators.
 */
        (object))
 {
@@ -2982,11 +3119,15 @@ static void
 boolean_validate (Lisp_Object instantiator)
 {
   if (!EQ (instantiator, Qt) && !EQ (instantiator, Qnil))
-    signal_simple_error ("Must be t or nil", instantiator);
+    signal_type_error (Qspecifier_argument_error, "Must be t or nil",
+                      instantiator);
 }
 
 DEFUN ("boolean-specifier-p", Fboolean_specifier_p, 1, 1, 0, /*
 Return non-nil if OBJECT is a boolean specifier.
+
+See `make-boolean-specifier' for a description of possible boolean
+instantiators.
 */
        (object))
 {
@@ -2999,11 +3140,11 @@ Return non-nil if OBJECT is a boolean specifier.
 
 DEFINE_SPECIFIER_TYPE (display_table);
 
-#define VALID_SINGLE_DISPTABLE_INSTANTIATOR_P(instantiator)                    \
-  (VECTORP (instantiator)                                                      \
-   || (CHAR_TABLEP (instantiator)                                              \
-       && (XCHAR_TABLE_TYPE (instantiator) == CHAR_TABLE_TYPE_CHAR             \
-          || XCHAR_TABLE_TYPE (instantiator) == CHAR_TABLE_TYPE_GENERIC))      \
+#define VALID_SINGLE_DISPTABLE_INSTANTIATOR_P(instantiator)               \
+  (VECTORP (instantiator)                                                 \
+   || (CHAR_TABLEP (instantiator)                                         \
+       && (XCHAR_TABLE_TYPE (instantiator) == CHAR_TABLE_TYPE_CHAR        \
+          || XCHAR_TABLE_TYPE (instantiator) == CHAR_TABLE_TYPE_GENERIC)) \
    || RANGE_TABLEP (instantiator))
 
 static void
@@ -3027,7 +3168,8 @@ display_table_validate (Lisp_Object instantiator)
       if (!VALID_SINGLE_DISPTABLE_INSTANTIATOR_P (instantiator))
        {
        lose:
-         dead_wrong_type_argument (display_table_specifier_methods->predicate_symbol,
+         dead_wrong_type_argument
+           (display_table_specifier_methods->predicate_symbol,
                                    instantiator);
        }
     }
@@ -3035,6 +3177,9 @@ display_table_validate (Lisp_Object instantiator)
 
 DEFUN ("display-table-specifier-p", Fdisplay_table_specifier_p, 1, 1, 0, /*
 Return non-nil if OBJECT is a display-table specifier.
+
+See `current-display-table' for a description of possible display-table
+instantiators.
 */
        (object))
 {
@@ -3049,13 +3194,14 @@ Return non-nil if OBJECT is a display-table specifier.
 void
 syms_of_specifier (void)
 {
-  defsymbol (&Qspecifierp, "specifierp");
+  INIT_LRECORD_IMPLEMENTATION (specifier);
 
-  defsymbol (&Qconsole_type, "console-type");
-  defsymbol (&Qdevice_class, "device-class");
+  DEFSYMBOL (Qspecifierp);
 
-  /* Qinteger, Qboolean, Qgeneric defined in general.c */
-  defsymbol (&Qnatnum, "natnum");
+  DEFSYMBOL (Qconsole_type);
+  DEFSYMBOL (Qdevice_class);
+
+  /* specifier types defined in general.c. */
 
   DEFSUBR (Fvalid_specifier_type_p);
   DEFSUBR (Fspecifier_type_list);
@@ -3110,21 +3256,22 @@ syms_of_specifier (void)
 
   /* locales are defined in general.c. */
 
-  defsymbol (&Qprepend, "prepend");
-  defsymbol (&Qappend, "append");
-  defsymbol (&Qremove_tag_set_prepend, "remove-tag-set-prepend");
-  defsymbol (&Qremove_tag_set_append, "remove-tag-set-append");
-  defsymbol (&Qremove_locale, "remove-locale");
-  defsymbol (&Qremove_locale_type, "remove-locale-type");
-  defsymbol (&Qremove_all, "remove-all");
+  /* some how-to-add flags in general.c. */
+  DEFSYMBOL (Qremove_tag_set_prepend);
+  DEFSYMBOL (Qremove_tag_set_append);
+  DEFSYMBOL (Qremove_locale);
+  DEFSYMBOL (Qremove_locale_type);
 
-  defsymbol (&Qfallback, "fallback");
+  DEFERROR_STANDARD (Qspecifier_syntax_error, Qsyntax_error);
+  DEFERROR_STANDARD (Qspecifier_argument_error, Qinvalid_argument);
+  DEFERROR_STANDARD (Qspecifier_change_error, Qinvalid_change);
 }
 
 void
 specifier_type_create (void)
 {
   the_specifier_type_entry_dynarr = Dynarr_new (specifier_type_entry);
+  dump_add_root_struct_ptr (&the_specifier_type_entry_dynarr, &sted_description);
 
   Vspecifier_type_list = Qnil;
   staticpro (&Vspecifier_type_list);
@@ -3143,12 +3290,23 @@ specifier_type_create (void)
 
   SPECIFIER_HAS_METHOD (boolean, validate);
 
-  INITIALIZE_SPECIFIER_TYPE (display_table, "display-table", "display-table-p");
+  INITIALIZE_SPECIFIER_TYPE (display_table, "display-table",
+                            "display-table-p");
 
   SPECIFIER_HAS_METHOD (display_table, validate);
 }
 
 void
+reinit_specifier_type_create (void)
+{
+  REINITIALIZE_SPECIFIER_TYPE (generic);
+  REINITIALIZE_SPECIFIER_TYPE (integer);
+  REINITIALIZE_SPECIFIER_TYPE (natnum);
+  REINITIALIZE_SPECIFIER_TYPE (boolean);
+  REINITIALIZE_SPECIFIER_TYPE (display_table);
+}
+
+void
 vars_of_specifier (void)
 {
   Vcached_specifiers = Qnil;
@@ -3157,6 +3315,7 @@ vars_of_specifier (void)
   /* Do NOT mark through this, or specifiers will never be GC'd.
      This is the same deal as for weak hash tables. */
   Vall_specifiers = Qnil;
+  dump_add_weak_object_chain (&Vall_specifiers);
 
   Vuser_defined_tags = Qnil;
   staticpro (&Vuser_defined_tags);