Update copyright years
[m17n/m17n-lib.git] / src / plist.c
index 3947726..1e449db 100644 (file)
@@ -1,5 +1,5 @@
 /* plist.c -- plist module.
 /* plist.c -- plist module.
-   Copyright (C) 2003, 2004
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
@@ -17,7 +17,7 @@
 
    You should have received a copy of the GNU Lesser General Public
    License along with the m17n library; if not, write to the Free
 
    You should have received a copy of the GNU Lesser General Public
    License along with the m17n library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    02111-1307, USA.  */
 
 /***en
    02111-1307, USA.  */
 
 /***en
 
     If the key of a property is a @e managing @e key, its @e value is
     a @e managed @e object.  A property list itself is a managed
 
     If the key of a property is a @e managing @e key, its @e value is
     a @e managed @e object.  A property list itself is a managed
-    objects.  */
+    objects.
+
+    If each key of a plist is one of #Msymbol, #Mtext, #Minteger, and
+    #Mplist, the plist is called as @e well-formed and represented by
+    the following notation in the documentation.
+
+@verbatim
+      PLIST ::= '(' ELEMENT * ')'
+
+      ELEMENT ::= INTEGER | SYMBOL | M-TEXT | PLIST
+
+      M-TEXT ::= '"' text data ... '"'
+@endverbatim
+
+    For instance, if a plist has four elements; integer -20, symbol of
+    name "sym", M-text of contents "abc", and plist of integer 10 and
+    symbol of name "another-symbol", it is represented as this:
+
+      (-20 sym "abc" (10 another-symbol))
+
+  */
 /***ja
     @addtogroup m17nPlist
 
 /***ja
     @addtogroup m17nPlist
 
@@ -55,6 +75,7 @@
 
 #include <stdio.h>
 #include <string.h>
 
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "config.h"
 #include "m17n.h"
 
 #include "config.h"
 #include "m17n.h"
@@ -108,7 +129,8 @@ free_plist (void *object)
   do {
     MPlist *next = plist->next;
 
   do {
     MPlist *next = plist->next;
 
-    if (MPLIST_KEY (plist) != Mnil && MPLIST_KEY (plist)->managing_key)
+    if (MPLIST_KEY (plist) != Mnil
+       && MPLIST_KEY (plist)->managing_key)
       M17N_OBJECT_UNREF (MPLIST_VAL (plist));
     M17N_OBJECT_UNREGISTER (plist_table, plist);
     free (plist);
       M17N_OBJECT_UNREF (MPLIST_VAL (plist));
     M17N_OBJECT_UNREGISTER (plist_table, plist);
     free (plist);
@@ -211,24 +233,22 @@ read_hexadesimal (MStream *st)
 static MPlist *
 read_mtext_element (MPlist *plist, MStream *st, int skip)
 {
 static MPlist *
 read_mtext_element (MPlist *plist, MStream *st, int skip)
 {
-  union {
-    int chars[READ_MTEXT_BUF_SIZE];
-    unsigned char bytes[sizeof (int) * READ_MTEXT_BUF_SIZE];
-  } buffer;
-  unsigned char *bytes = buffer.bytes;
-  int nbytes = sizeof (int) * READ_MTEXT_BUF_SIZE;
-  int *chars = NULL;
-  int nchars = 0;
-  int c, i, j;
+  unsigned char buffer[READ_MTEXT_BUF_SIZE], *buf = buffer;
+  int nbytes = READ_MTEXT_BUF_SIZE;
+  int c, i;
 
   i = 0;
   while ((c = GETC (st)) != EOF && c != '"')
     {
 
   i = 0;
   while ((c = GETC (st)) != EOF && c != '"')
     {
+      int is_char = 0;
+
       if (c == '\\')
        {
          c = GETC (st);
          if (c == EOF)
            break;
       if (c == '\\')
        {
          c = GETC (st);
          if (c == EOF)
            break;
+         if (c == '\n')
+           continue;
          if (c == 'x' || c == 'u')
            {
              int next_c;
          if (c == 'x' || c == 'u')
            {
              int next_c;
@@ -237,6 +257,8 @@ read_mtext_element (MPlist *plist, MStream *st, int skip)
              next_c = GETC (st);
              if (next_c != ' ')
                UNGETC (next_c, st);
              next_c = GETC (st);
              if (next_c != ' ')
                UNGETC (next_c, st);
+             if (c >= 0x80)
+               is_char = 1;
            }
          else
            c = escape_mnemonic[c];
            }
          else
            c = escape_mnemonic[c];
@@ -244,65 +266,34 @@ read_mtext_element (MPlist *plist, MStream *st, int skip)
 
       if (! skip)
        {
 
       if (! skip)
        {
-         if (c >= 0x80 && ! chars)
+         if (i + MAX_UTF8_CHAR_BYTES >= nbytes)
            {
            {
-             chars = buffer.chars;
-             for (j = i - 1; j >= 0; j--)
-               chars[j] = bytes[j];
-             nchars = i;
-             if (bytes != buffer.bytes)
-               free (bytes);
-           }
-
-         if (chars)
-           {
-             if (i + 1 >= nchars)
+             if (buf == buffer)
                {
                {
-                 nchars *= 2;
-                 if (chars == buffer.chars)
-                   {
-                     MTABLE_MALLOC (chars, nchars, MERROR_PLIST);
-                     memcpy (chars, buffer.chars, sizeof (int) * i);
-                   }
-                 else
-                   MTABLE_REALLOC (chars, nchars, MERROR_PLIST);
+                 nbytes *= 2;
+                 buf = malloc (nbytes);
+                 memcpy (buf, buffer, i);
                }
                }
-             chars[i++] = c;
-           }
-         else
-           {
-             if (i + MAX_UTF8_CHAR_BYTES >= nbytes)
+             else
                {
                {
-                 nbytes *= 2;
-                 if (bytes == buffer.bytes)
-                   {
-                     MTABLE_MALLOC (bytes, nbytes, MERROR_PLIST);
-                     memcpy (bytes, buffer.bytes, i);
-                   }
-                 else
-                   MTABLE_REALLOC (bytes, nbytes, MERROR_PLIST);
+                 nbytes += READ_MTEXT_BUF_SIZE;
+                 buf = realloc (buf, nbytes);
                }
                }
-             bytes[i++] = c;
            }
            }
+
+         if (is_char)
+           i += CHAR_STRING_UTF8 (c, buf);
+         else
+           buf[i++] = c;
        }
     }
 
   if (! skip)
     {
        }
     }
 
   if (! skip)
     {
-      MText *mt;
-
-      if (chars)
-       {
-         mt = mtext__from_data (chars, i, MTEXT_FORMAT_UTF_32, 1);
-         if (chars != buffer.chars)
-           free (chars);
-       }         
-      else
-       {
-         mt = mtext__from_data (bytes, i, MTEXT_FORMAT_UTF_8, 1);
-         if (bytes != buffer.bytes)
-           free (bytes);
-       }
+      MText *mt = mtext__from_data (buf, i, MTEXT_FORMAT_UTF_8,
+                                   (buf == buffer));
+      if (buf != buffer)
+       mt->allocated = nbytes;
       MPLIST_SET_ADVANCE (plist, Mtext, mt);
     }
   return plist;
       MPLIST_SET_ADVANCE (plist, Mtext, mt);
     }
   return plist;
@@ -332,69 +323,19 @@ read_character (MStream *st, int c)
 }
 
 
 }
 
 
-/** Read an integer element from ST, and add it to LIST.  Return a
-    list for the next element.  It is assumed that we have already
-    read the character C. */
-
-static MPlist *
-read_integer_element (MPlist *plist, MStream *st, int c, int skip)
-{
-  int num;
-
-  if (c == '0' || c == '#')
-    {
-      c = GETC (st);
-      if (c == 'x')
-       num = read_hexadesimal (st);
-      else
-       num = read_decimal (st, c);
-    }
-  else if (c == '?')
-    {
-      c = GETC (st);
-      if (c == EOF)
-       num = 0;
-      else if (c != '\\')
-       {
-         if (c < 128 || ! CHAR_UNITS_BY_HEAD_UTF8 (c))
-           num = c;
-         else
-           num = read_character (st, c);
-       }
-      else
-       {
-         c = GETC (st);
-         if (c == EOF)
-           num = '\\';
-         else if (c < 128 || ! CHAR_UNITS_BY_HEAD_UTF8 (c))
-           num = escape_mnemonic[c];
-         else
-           num = read_character (st, c);
-       }
-    }
-  else if (c == '-')
-    num = - read_decimal (st, GETC (st));
-  else
-    num = read_decimal (st, c);
-
-  if (! skip)
-    MPLIST_SET_ADVANCE (plist, Minteger, (void *) num);
-  return plist;
-}
-
 /** Read a symbol element from ST, and add it to LIST.  Return a list
     for the next element.  */
 
 static MPlist *
 /** Read a symbol element from ST, and add it to LIST.  Return a list
     for the next element.  */
 
 static MPlist *
-read_symbol_element (MPlist *plist, MStream *st, int skip)
+read_symbol_element (MPlist *plist, MStream *st, int c, int skip)
 {
   unsigned char buffer[1024];
   int bufsize = 1024;
   unsigned char *buf = buffer;
 {
   unsigned char buffer[1024];
   int bufsize = 1024;
   unsigned char *buf = buffer;
-  int c, i;
+  int i;
 
   i = 0;
 
   i = 0;
-  while ((c = GETC (st)) != EOF
+  while (c != EOF
         && c > ' '
         && c != ')' && c != '(' && c != '"')
     {
         && c > ' '
         && c != ')' && c != '(' && c != '"')
     {
@@ -418,6 +359,7 @@ read_symbol_element (MPlist *plist, MStream *st, int skip)
        }
       if (! skip)
        buf[i++] = c;
        }
       if (! skip)
        buf[i++] = c;
+      c = GETC (st);
     }
 
   if (c > ' ')
     }
 
   if (c > ' ')
@@ -432,6 +374,71 @@ read_symbol_element (MPlist *plist, MStream *st, int skip)
   return plist;
 }
 
   return plist;
 }
 
+/** Read an integer element from ST, and add it to LIST.  Return a
+    list for the next element.  It is assumed that we have already
+    read the character C. */
+
+static MPlist *
+read_integer_element (MPlist *plist, MStream *st, int c, int skip)
+{
+  int num;
+
+  if (c == '#')
+    {
+      c = GETC (st);
+      if (c != 'x')
+       {
+         UNGETC (c, st);
+         return read_symbol_element (plist, st, '#', skip);
+       }
+      num = read_hexadesimal (st);
+    }
+  else if (c == '0')
+    {
+      c = GETC (st);
+      num = (c == 'x' ? read_hexadesimal (st) : read_decimal (st, c));
+    }
+  else if (c == '?')
+    {
+      c = GETC (st);
+      if (c == EOF)
+       num = 0;
+      else if (c != '\\')
+       {
+         if (c < 128 || ! CHAR_UNITS_BY_HEAD_UTF8 (c))
+           num = c;
+         else
+           num = read_character (st, c);
+       }
+      else
+       {
+         c = GETC (st);
+         if (c == EOF)
+           num = '\\';
+         else if (c < 128 || ! CHAR_UNITS_BY_HEAD_UTF8 (c))
+           num = escape_mnemonic[c];
+         else
+           num = read_character (st, c);
+       }
+    }
+  else if (c == '-')
+    {
+      c = GETC (st);
+      if (c < '0' || c > '9')
+       {
+         UNGETC (c, st);
+         return read_symbol_element (plist, st, '-', skip);
+       }
+      num = - read_decimal (st, c);
+    }
+  else
+    num = read_decimal (st, c);
+
+  if (! skip)
+    MPLIST_SET_ADVANCE (plist, Minteger, (void *) num);
+  return plist;
+}
+
 /* Read an element of various type from stream ST, and add it to LIST.
    Return a list for the next element.  The element type is decided by
    the first token character found as below:
 /* Read an element of various type from stream ST, and add it to LIST.
    Return a list for the next element.  The element type is decided by
    the first token character found as below:
@@ -471,18 +478,31 @@ read_element (MPlist *plist, MStream *st, MPlist *keys)
       p = read_element (p, st, NULL);
       if (keys && p && MPLIST_SYMBOL_P (pl))
        {
       p = read_element (p, st, NULL);
       if (keys && p && MPLIST_SYMBOL_P (pl))
        {
-         MPlist *p0 = keys;
-         MPLIST_FIND (p0, MPLIST_SYMBOL (pl));
-         if (! MPLIST_TAIL_P (p0) && ! MPLIST_VAL (p0))
+         if (MPLIST_TAIL_P (keys))
            {
            {
-             M17N_OBJECT_UNREF (pl);
+             while ((p = read_element (p, st, NULL)));
+             MPLIST_SET_ADVANCE (plist, Mplist, pl);
              return NULL;
            }
              return NULL;
            }
-         while ((p = read_element (p, st, NULL)));
-         if (! MPLIST_TAIL_P (p0))
-           MPLIST_SET_ADVANCE (plist, Mplist, pl);
          else
          else
-           M17N_OBJECT_UNREF (pl);
+           {
+             MPlist *p0 = keys;
+
+             MPLIST_FIND (p0, MPLIST_SYMBOL (pl));
+             if (! MPLIST_TAIL_P (p0) && ! MPLIST_VAL (p0))
+               {
+                 M17N_OBJECT_UNREF (pl);
+                 return NULL;
+               }
+             while ((p = read_element (p, st, NULL)));
+             if (! MPLIST_TAIL_P (p0))
+               {
+                 MPLIST_SET_ADVANCE (plist, Mplist, pl);
+                 return NULL;
+               }
+             else
+               M17N_OBJECT_UNREF (pl);
+           }
        }
       else
        {
        }
       else
        {
@@ -498,35 +518,56 @@ read_element (MPlist *plist, MStream *st, MPlist *keys)
     return (read_integer_element (plist, st, c, keys ? 1 : 0));
   if (c == EOF || c == ')')
     return NULL;
     return (read_integer_element (plist, st, c, keys ? 1 : 0));
   if (c == EOF || c == ')')
     return NULL;
-  UNGETC (c, st);
-  return (read_symbol_element (plist, st, keys ? 1 : 0));
+  return (read_symbol_element (plist, st, c, keys ? 1 : 0));
 }
 
 }
 
-void
-write_element (MText *mt, MPlist *plist)
+#define PUTC(MT, C)                    \
+  do {                                 \
+    if (MT)                            \
+      mtext_cat_char ((MT), (C));      \
+    else                               \
+      putc ((C), mdebug__output);      \
+  } while (0);
+
+#define PUTS(MT, STR)                  \
+  do {                                 \
+    if (MT)                            \
+      MTEXT_CAT_ASCII ((MT), (STR));   \
+    else                               \
+      fputs ((STR), mdebug__output);   \
+  } while (0)
+
+
+static void 
+write_symbol (MText *mt, MSymbol sym)
 {
 {
-  if (MPLIST_SYMBOL_P (plist))
+  if (sym == Mnil)
     {
     {
-      MSymbol sym = MPLIST_SYMBOL (plist);
+      PUTS (mt, "nil");
+    }
+  else
+    {
+      char *name = MSYMBOL_NAME (sym);
 
 
-      if (sym == Mnil)
+      if (isdigit (*name))
+       PUTC (mt, '\\');
+      while (*name)
        {
        {
-         MTEXT_CAT_ASCII (mt, "nil");
+         if (*name <= ' ' || *name == '\\' || *name == '"'
+             || *name == '(' || *name == ')')
+           PUTC (mt, '\\');
+         PUTC (mt, *name); 
+         name++;
        }
        }
-      else
-       {
-         char *name = MSYMBOL_NAME (sym);
-         char *buf = alloca (MSYMBOL_NAMELEN (sym) * 2 + 1), *p = buf;
+    }
+}
 
 
-         while (*name)
-           {
-             if (*name <= ' ' || *name == '"' || *name == ')' || *name == ')')
-               *p++ = '\\';
-             *p++ = *name++;
-           }
-         *p = '\0';
-         MTEXT_CAT_ASCII (mt, buf);
-       }
+static void
+write_element (MText *mt, MPlist *plist, int indent)
+{
+  if (MPLIST_SYMBOL_P (plist))
+    {
+      write_symbol (mt, MPLIST_SYMBOL (plist));
     }
   else if (MPLIST_INTEGER_P (plist))
     {
     }
   else if (MPLIST_INTEGER_P (plist))
     {
@@ -534,91 +575,122 @@ write_element (MText *mt, MPlist *plist)
       char buf[128];
 
       sprintf (buf, "%d", num);
       char buf[128];
 
       sprintf (buf, "%d", num);
-      MTEXT_CAT_ASCII (mt, buf);
+      PUTS (mt, buf);
     }
     }
-  else if (MPLIST_PLIST_P (plist))
+  else if (MPLIST_PLIST_P (plist)
+          || MPLIST_NESTED_P (plist))
     {
       MPlist *pl;
     {
       MPlist *pl;
+      int newline = 0;
 
 
+      if (MPLIST_NESTED_P (plist))
+       {
+         write_symbol (mt, MPLIST_KEY (plist));
+         PUTC (mt, ':');
+       }
       plist = MPLIST_PLIST (plist);
       plist = MPLIST_PLIST (plist);
-      mtext_cat_char (mt, '(');
+      PUTC (mt, '(');
+      if (indent >= 0)
+       indent++;
       MPLIST_DO (pl, plist)
        {
          if (pl != plist)
       MPLIST_DO (pl, plist)
        {
          if (pl != plist)
-           mtext_cat_char (mt, ' ');
-         write_element (mt, pl);
+           {
+             if (indent > 0 && (MPLIST_PLIST_P (pl) || MPLIST_MTEXT_P (pl)))
+               newline = 1;
+             if (newline)
+               {
+                 int i;
+
+                 PUTC (mt, '\n');
+                 for (i = 1; i < indent; i++)
+                   PUTC (mt, ' ');
+               }
+             PUTC (mt, ' ');
+           }
+         write_element (mt, pl, indent);
+         if (indent >= 0)
+           newline = (MPLIST_PLIST_P (pl) || MPLIST_MTEXT_P (pl));
        }
        }
-      mtext_cat_char (mt, ')');
+      PUTC (mt, ')');
     }
   else if (MPLIST_MTEXT_P (plist))
     {
     }
   else if (MPLIST_MTEXT_P (plist))
     {
-      mtext_cat_char (mt, '"');
-      /* Not yet implemnted */
-      mtext_cat_char (mt, '"');
-    }
-}
-
-/* Support functions for mdebug_dump_plist.  */
+      MText *this_mt = MPLIST_MTEXT (plist);
+      int from = 0, to = mtext_nchars (this_mt);
+      int stop1 = 0, stop2 = 0;
 
 
-static void
-dump_string (char *str)
-{
-  char *p = str, *pend = p + strlen (p), *new, *p1;
-
-  new = p1 = alloca ((pend - p) * 4 + 1);
-  while (p < pend)
-    {
-      if (*p < 0)
+      if (! mt && this_mt->format > MTEXT_FORMAT_UTF_8)
        {
        {
-         sprintf (p1, "\\x%02X", (unsigned char) *p);
-         p1 += 4;
+         this_mt = mtext_dup (this_mt);
+         mtext__adjust_format (this_mt, MTEXT_FORMAT_UTF_8);
        }
        }
-      else if (*p < ' ')
+
+      PUTC (mt, '"');
+      while (1)
        {
        {
-         *p1++ = '^';
-         *p1++ = *p + '@';
+         int stop, escaped;
+
+         if (from == stop1)
+           {
+             if ((stop1 = mtext_character (this_mt, from, to, '"')) < 0)
+               stop1 = to;
+           }
+         if (from == stop2)
+           {
+             if ((stop2 = mtext_character (this_mt, from, to, '\\')) < 0)
+               stop2 = to;
+           }
+         if (stop1 < stop2)
+           stop = stop1++, escaped = '"';
+         else
+           stop = stop2++, escaped = '\\';
+         if (mt)
+           mtext_copy (mt, mtext_nchars (mt), this_mt, from, stop);
+         else
+           {
+             unsigned char *data = MTEXT_DATA (this_mt);
+             unsigned char *beg = data + mtext__char_to_byte (this_mt, from);
+             unsigned char *end = data + mtext__char_to_byte (this_mt, stop);
+
+             while (beg < end)
+               putc (*beg, mdebug__output), beg++;
+           }
+         if (stop == to)
+           break;
+         PUTC (mt, '\\');
+         PUTC (mt, escaped);
+         from = stop + 1;
        }
        }
-      else if (*p == ' ')
+      PUTC (mt, '"');
+      if (this_mt != MPLIST_MTEXT (plist))
+       M17N_OBJECT_UNREF (this_mt);
+    }
+  else if (MPLIST_STRING_P (plist))
+    {
+      char *str = MPLIST_STRING (plist);
+
+      if (mt)
        {
        {
-         *p1++ = '\\';
-         *p1++ = ' ';
+         MText *this_mt = mtext__from_data (str, strlen (str),
+                                            MTEXT_FORMAT_UTF_8, 0);
+
+         mtext_copy (mt, mtext_nchars (mt),
+                     this_mt, 0, mtext_nchars (this_mt));
+         M17N_OBJECT_UNREF (this_mt);
        }
       else
        }
       else
-       *p1++ = *p;
-      p++;
+       fprintf (mdebug__output, "%s", str);
     }
     }
-  *p1 = '\0';
-  fprintf (stderr, "%s", new);
-}
-
-static void
-dump_plist_element (MPlist *plist, int indent)
-{
-  char *prefix = (char *) alloca (indent + 1);
-  MSymbol key;
-
-  memset (prefix, 32, indent);
-  prefix[indent] = 0;
-
-  key = MPLIST_KEY (plist);
-  fprintf (stderr, "(%s(#%d) ", msymbol_name (MPLIST_KEY (plist)),
-          plist->control.ref_count);
-  if (key == Msymbol)
-    dump_string (msymbol_name (MPLIST_SYMBOL (plist)));
-  else if (key == Mtext)
-    mdebug_dump_mtext (MPLIST_MTEXT (plist), indent, 0);
-  else if (key == Minteger)
-    fprintf (stderr, "%x", MPLIST_INTEGER (plist));
-  else if (key == Mstring) 
-    fprintf (stderr, "\"%s\"", MPLIST_STRING (plist));
-  else if (key == Mplist)
+  else 
     {
     {
-      fprintf (stderr, "\n%s", prefix);
-      mdebug_dump_plist (MPLIST_PLIST (plist), indent);
+      char buf[128];
+
+      write_symbol (mt, MPLIST_KEY (plist));
+      PUTC (mt, ':');
+      sprintf (buf, "%04X", (unsigned) MPLIST_VAL (plist));
+      PUTS (mt, buf);
     }
     }
-  else
-    fprintf (stderr, "0x%X", (unsigned) MPLIST_VAL (plist));
-  fprintf (stderr, ")");
 }
 
 \f
 }
 
 \f
@@ -684,6 +756,8 @@ mplist__from_plist (MPlist *plist)
       type = MPLIST_KEY (plist);
       if (type->managing_key && MPLIST_VAL (plist))
        M17N_OBJECT_REF (MPLIST_VAL (plist));
       type = MPLIST_KEY (plist);
       if (type->managing_key && MPLIST_VAL (plist))
        M17N_OBJECT_REF (MPLIST_VAL (plist));
+      if (type == Mplist)
+       MPLIST_SET_NESTED_P (p);
       MPLIST_SET_ADVANCE (p, key, MPLIST_VAL (plist));
       plist = MPLIST_NEXT (plist);
     }
       MPLIST_SET_ADVANCE (p, key, MPLIST_VAL (plist));
       plist = MPLIST_NEXT (plist);
     }
@@ -712,6 +786,7 @@ mplist__from_alist (MPlist *plist)
       elt = MPLIST_PLIST (plist);
       if (! MPLIST_SYMBOL_P (elt))
        MERROR (MERROR_PLIST, NULL);
       elt = MPLIST_PLIST (plist);
       if (! MPLIST_SYMBOL_P (elt))
        MERROR (MERROR_PLIST, NULL);
+      MPLIST_SET_NESTED_P (p);
       MPLIST_SET_ADVANCE (p, MPLIST_SYMBOL (elt), MPLIST_NEXT (elt));
       M17N_OBJECT_REF (MPLIST_NEXT (elt));
     }
       MPLIST_SET_ADVANCE (p, MPLIST_SYMBOL (elt), MPLIST_NEXT (elt));
       M17N_OBJECT_REF (MPLIST_NEXT (elt));
     }
@@ -784,19 +859,101 @@ mplist__from_string (unsigned char *str, int n)
 }
 
 int
 }
 
 int
-mplist__serialize (MText *mt, MPlist *plist)
+mplist__serialize (MText *mt, MPlist *plist, int pretty)
 {
   MPlist *pl;
 {
   MPlist *pl;
+  int separator = pretty ? '\n' : ' ';
 
   MPLIST_DO (pl, plist)
     {
       if (pl != plist)
 
   MPLIST_DO (pl, plist)
     {
       if (pl != plist)
-       mtext_cat_char (mt, ' ');
-      write_element (mt, pl);
+       mtext_cat_char (mt, separator);
+      write_element (mt, pl, pretty ? 0 : -1);
     }
     }
+  if (pretty)
+    mtext_cat_char (mt, separator);
   return 0;
 }
 
   return 0;
 }
 
+/**en
+    @brief Concatenate two plists.
+
+    The mplist__conc () function concatenates plist $TAIL at the end of
+    plist $PLIST and return $PLIST.  If $TAIL is empty, return $PLIST
+    without modifying it.  */
+
+MPlist *
+mplist__conc (MPlist *plist, MPlist *tail)
+{
+  MPlist *pl;
+
+  if (MPLIST_TAIL_P (tail))
+    return plist;
+  MPLIST_DO (pl, plist);
+  MPLIST_KEY (pl) = MPLIST_KEY (tail);
+  MPLIST_VAL (pl) = MPLIST_VAL (tail);
+  if (MPLIST_KEY (pl)->managing_key && MPLIST_VAL (pl))
+    M17N_OBJECT_REF (MPLIST_VAL (pl));
+  if (MPLIST_NESTED_P (tail))
+    MPLIST_SET_NESTED_P (pl);
+  tail = MPLIST_NEXT (tail);
+  MPLIST_NEXT (pl) = tail;
+  M17N_OBJECT_REF (tail);
+  return plist;
+}
+
+/*=*/
+/**en
+    @brief Discard a property at the beginning of a property list.
+
+    The mplist__pop_unref () function removes a property at the
+    beginning of property list $PLIST, and if the property value is a
+    managed object, unref it.  As a result, the second key and value
+    of the original $PLIST become the first of those of the new
+    $PLIST.  */
+
+void
+mplist__pop_unref (MPlist *plist)
+{
+  MSymbol key;
+  void *val;
+
+  if (MPLIST_TAIL_P (plist))
+    return;
+  key = MPLIST_KEY (plist);
+  val = mplist_pop (plist);
+  if (key->managing_key)
+    M17N_OBJECT_UNREF (val);
+}
+
+/**en
+    @brief Search for an element of an alist represented by a plist.
+
+    The mplist__assq () function treats $PLIST as an association list
+    (elements are plists (key is #Mplist) whose first element is a
+    symbol (key is #Msymbol)), and find an element whose first element
+    has key #Msymbol and value $KEY.
+
+    Non-plist elements of $PLIST are ignored.
+
+    @return
+    This function returns a found element or NULL if no element
+    matches with $KEY.  */
+
+MPlist *
+mplist__assq (MPlist *plist, MSymbol key)
+{
+  MPLIST_DO (plist, plist)
+    if (MPLIST_PLIST_P (plist))
+      {
+       MPlist *pl = MPLIST_PLIST (plist);
+
+       if (MPLIST_SYMBOL_P (pl) && MPLIST_SYMBOL (pl) == key)
+         return plist;
+      }
+  return NULL;
+}
+
 /*** @} */
 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
 
 /*** @} */
 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
 
@@ -877,7 +1034,7 @@ MSymbol Mtext;
     ¤³¤Î´Ø¿ô¤Ï·è¤·¤Æ¼ºÇÔ¤·¤Ê¤¤¡£     */
 
 MPlist *
     ¤³¤Î´Ø¿ô¤Ï·è¤·¤Æ¼ºÇÔ¤·¤Ê¤¤¡£     */
 
 MPlist *
-mplist ()
+mplist (void)
 {
   MPlist *plist;
 
 {
   MPlist *plist;
 
@@ -916,7 +1073,11 @@ mplist_copy (MPlist *plist)
   MPlist *copy = mplist (), *pl = copy;
 
   MPLIST_DO (plist, plist)
   MPlist *copy = mplist (), *pl = copy;
 
   MPLIST_DO (plist, plist)
-    pl = mplist_add (pl, MPLIST_KEY (plist), MPLIST_VAL (plist));
+    {
+      if (MPLIST_NESTED_P (plist))
+       MPLIST_SET_NESTED_P (pl);
+      pl = mplist_add (pl, MPLIST_KEY (plist), MPLIST_VAL (plist));
+    }
   return copy;
 }
 
   return copy;
 }
 
@@ -979,10 +1140,10 @@ mplist_put (MPlist *plist, MSymbol key, void *val)
 /***en
     @brief Get the value of a property in a property list.
 
 /***en
     @brief Get the value of a property in a property list.
 
-    The mplist_get () function searches property list $PLIST
-    from the beginning for a property whose key is $KEY.  If such a
-    property is found, a pointer to its value is returned as the type
-    of <tt>(void *)</tt>.  If not found, @c NULL is returned.
+    The mplist_get () function searches property list $PLIST from the
+    beginning for a property whose key is $KEY.  If such a property is
+    found, its value is returned as the type of <tt>(void *)</tt>.  If
+    not found, @c NULL is returned.
 
     When @c NULL is returned, there are two possibilities: one is the
     case where no property is found (see above); the other is the case
 
     When @c NULL is returned, there are two possibilities: one is the
     case where no property is found (see above); the other is the case
@@ -992,8 +1153,8 @@ mplist_put (MPlist *plist, MSymbol key, void *val)
 /***ja
     @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥ÈÃæ¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë.
 
 /***ja
     @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥ÈÃæ¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë.
 
-    ´Ø¿ô mplist_get () ¤Ï¡¢¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤ò»Ï¤á¤«¤éõ¤·¤Æ¡¢¥­¡¼¤¬
-    $KEY ¤Ç¤¢¤ë¥×¥í¥Ñ¥Æ¥£¤ò¸«¤Ä¤±¤ë¡£¸«¤Ä¤«¤ì¤Ð¡¢¤½¤ÎÃͤؤΥݥ¤¥ó¥¿¤ò
+    ´Ø¿ô mplist_get () ¤Ï¡¢¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤ò»Ï¤á¤«¤éõ¤·¤Æ¡¢¥­¡¼
+    ¤¬ $KEY ¤Ç¤¢¤ë¥×¥í¥Ñ¥Æ¥£¤ò¸«¤Ä¤±¤ë¡£¸«¤Ä¤«¤ì¤Ð¡¢¤½¤ÎÃͤò
     <tt>(void *)</tt> ·¿¤ÇÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
 
     @c NULL ¤¬Ê֤俺ݤˤÏÆó¤Ä¤Î²ÄǽÀ­¤¬¤¢¤ë: 
     <tt>(void *)</tt> ·¿¤ÇÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
 
     @c NULL ¤¬Ê֤俺ݤˤÏÆó¤Ä¤Î²ÄǽÀ­¤¬¤¢¤ë: 
@@ -1015,6 +1176,67 @@ mplist_get (MPlist *plist, MSymbol key)
 /*=*/
 
 /***en
 /*=*/
 
 /***en
+    @brief Set the value (function pointer) of a property in a property list.
+
+    The mplist_put_func () function is similar to mplist_put () but for
+    setting function pointer $FUNC in property list $PLIST for key
+    $KEY.  $KEY must not be a managing key.  */
+
+/***ja
+    @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥ÈÃæ¤Î¥×¥í¥Ñ¥Æ¥£¤Ë´Ø¿ô¥Ý¥¤¥ó¥¿¤Ç¤¢¤ëÃͤòÀßÄꤹ¤ë.
+
+    ´Ø¿ô mplist_put_func () ¤Ï´Ø¿ô mplist_put () Æ±ÍÍ¡¢¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST
+    Ãæ¤Ç¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¥×¥í¥Ñ¥Æ¥£¤ËÃͤòÀßÄꤹ¤ë¡£Ã¢¤·¤½¤ÎÃͤϴؿô¥Ý¥¤¥ó¥¿
+    $FUNC ¤Ç¤¢¤ë¡£$KEY ¤Ï´ÉÍý¥­¡¼¤Ç¤¢¤Ã¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£  */
+
+
+/***
+    @seealso
+    mplist_put (), M17N_FUNC ()  */
+
+MPlist *
+mplist_put_func (MPlist *plist, MSymbol key, M17NFunc func)
+{
+  if (key == Mnil || key->managing_key)
+    MERROR (MERROR_PLIST, NULL);
+  MPLIST_FIND (plist, key);
+  MPLIST_KEY (plist) = key;
+  MPLIST_FUNC (plist) = func;
+  MPLIST_SET_VAL_FUNC_P (plist);
+  if (! plist->next)
+    MPLIST_NEW ((plist)->next);
+  return plist;
+}
+
+/*=*/
+
+/***en
+    @brief Get the value (function pointer) of a property in a property list.
+
+    The mplist_get_func () function is similar to mplist_get () but for
+    getting a function pointer from property list $PLIST by key $KEY.  */
+
+/***ja
+    @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤«¤é¥×¥í¥Ñ¥Æ¥£¤Î´Ø¿ô¥Ý¥¤¥ó¥¿¤Ç¤¢¤ëÃͤòÆÀ¤ë.
+
+    ´Ø¿ô mplist_get_func () ¤Ï´Ø¿ô mplist_get () ¤ÈƱÍͤˡ¢¥×¥í¥Ñ¥Æ¥£¥ê
+    ¥¹¥È $PLIST Ãæ¤Ç¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¥×¥í¥Ñ¥Æ¥£¤ÎÃÍ¡¢Ã¢¤·´Ø¿ô¥Ý¥¤¥ó¥¿¡¢
+    ¤òÆÀ¤ë¡£ */
+
+
+/***
+    @seealso
+    mplist_get () */
+M17NFunc
+mplist_get_func (MPlist *plist, MSymbol key)
+{
+  MPLIST_FIND (plist, key);
+  return (MPLIST_TAIL_P (plist) ? NULL : MPLIST_FUNC (plist));
+}
+
+/*=*/
+
+/***en
     @brief Add a property at the end of a property list.
 
     The mplist_add () function appends at the end of property list
     @brief Add a property at the end of a property list.
 
     The mplist_add () function appends at the end of property list
@@ -1092,6 +1314,8 @@ mplist_push (MPlist *plist, MSymbol key, void *val)
   MPLIST_NEW (pl);
   MPLIST_KEY (pl) = MPLIST_KEY (plist);
   MPLIST_VAL (pl) = MPLIST_VAL (plist);
   MPLIST_NEW (pl);
   MPLIST_KEY (pl) = MPLIST_KEY (plist);
   MPLIST_VAL (pl) = MPLIST_VAL (plist);
+  if (MPLIST_NESTED_P (plist))
+    MPLIST_SET_NESTED_P (pl);
   MPLIST_NEXT (pl) = MPLIST_NEXT (plist);
   plist->next = pl;
   if (val && key->managing_key)
   MPLIST_NEXT (pl) = MPLIST_NEXT (plist);
   plist->next = pl;
   if (val && key->managing_key)
@@ -1108,7 +1332,7 @@ mplist_push (MPlist *plist, MSymbol key, void *val)
 
     The mplist_pop () function removes a property at the beginning of
     property list $PLIST.  As a result, the second key and value of
 
     The mplist_pop () function removes a property at the beginning of
     property list $PLIST.  As a result, the second key and value of
-    the original $PLIST become the first of those of the new $PLIST.
+    the $PLIST become the first ones.
 
     @return
     If the operation was successful, this function return the value of
 
     @return
     If the operation was successful, this function return the value of
@@ -1116,9 +1340,8 @@ mplist_push (MPlist *plist, MSymbol key, void *val)
 /***ja
     @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤ÎÀèƬ¤«¤é¥×¥í¥Ñ¥Æ¥£¤òºï½ü¤¹¤ë.
 
 /***ja
     @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤ÎÀèƬ¤«¤é¥×¥í¥Ñ¥Æ¥£¤òºï½ü¤¹¤ë.
 
-    ´Ø¿ô mplist_pop () ¤Ï¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST 
-    ¤ÎÀèƬ¤Î¥×¥í¥Ñ¥Æ¥£¤òºï½ü¤¹¤ë¡£·ë²Ì¤È¤·¤Æ¡¢¸µ¤Î $PLIST ¤Î2ÈÖÌܤΥ­¡¼¤ÈÃͤ¬¡¢¿·¤·¤¤ 
-    $PLIST ¤ÎÀèƬ¤Î¥­¡¼¤ÈÃͤˤʤ롣
+    ´Ø¿ô mplist_pop () ¤Ï¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤ÎÀèƬ¤Î¥×¥í¥Ñ¥Æ¥£¤òºï
+    ½ü¤¹¤ë¡£·ë²Ì¤È¤·¤Æ¡¢¸µ¤Î2ÈÖÌܤΥ­¡¼¤ÈÃͤ¬ÀèƬ¤Î¥­¡¼¤ÈÃͤˤʤ롣
 
     @return 
     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ïºï½ü¤µ¤ì¤¿¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð
 
     @return 
     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ïºï½ü¤µ¤ì¤¿¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð
@@ -1265,11 +1488,11 @@ mplist_set (MPlist *plist, MSymbol key, void * val)
     }
   else
     {
     }
   else
     {
+      if (val && key->managing_key)
+       M17N_OBJECT_REF (val);
       if (! MPLIST_TAIL_P (plist)
          && MPLIST_KEY (plist)->managing_key)
        M17N_OBJECT_UNREF (MPLIST_VAL (plist));
       if (! MPLIST_TAIL_P (plist)
          && MPLIST_KEY (plist)->managing_key)
        M17N_OBJECT_UNREF (MPLIST_VAL (plist));
-      if (val && key->managing_key)
-       M17N_OBJECT_REF (val);
       MPLIST_SET (plist, key, val);
     }
   return plist;
       MPLIST_SET (plist, key, val);
     }
   return plist;
@@ -1338,7 +1561,7 @@ mplist_value (MPlist *plist)
 }
 
 /***en
 }
 
 /***en
-    @brief Generate a property list by deserializaing an M-text.
+    @brief Generate a property list by deserializing an M-text.
 
     The mplist_deserialize () function parses M-text $MT and returns a
     property list.
 
     The mplist_deserialize () function parses M-text $MT and returns a
     property list.
@@ -1360,7 +1583,7 @@ mplist_value (MPlist *plist)
     Msymbol, @c Minteger, @c Mtext, @c Mplist
 
     In an ascii-character-sequence, a backslash (\) is used as the escape
     Msymbol, @c Minteger, @c Mtext, @c Mplist
 
     In an ascii-character-sequence, a backslash (\) is used as the escape
-    character, which means that, for instance, <tt>"abc\ def"</tt>
+    character, which means that, for instance, <tt>abc\ def</tt>
     produces a symbol whose name is of length seven with the fourth
     character being a space.  */
 /***ja
     produces a symbol whose name is of length seven with the fourth
     character being a space.  */
 /***ja
@@ -1385,7 +1608,7 @@ mplist_value (MPlist *plist)
     @c Mplist ¤Î¤¤¤º¤ì¤«¤ò³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤ë¡£
 
     ¥¢¥¹¥­¡¼Ê¸»úÎóÆâ¤Ç¤Ï¡¢¥Ð¥Ã¥¯¥¹¥é¥Ã¥·¥å (\) ¤¬¥¨¥¹¥±¡¼¥×ʸ»ú¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£¤¿¤È¤¨¤Ð
     @c Mplist ¤Î¤¤¤º¤ì¤«¤ò³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤ë¡£
 
     ¥¢¥¹¥­¡¼Ê¸»úÎóÆâ¤Ç¤Ï¡¢¥Ð¥Ã¥¯¥¹¥é¥Ã¥·¥å (\) ¤¬¥¨¥¹¥±¡¼¥×ʸ»ú¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£¤¿¤È¤¨¤Ð
-    <tt>"abc\ def"</tt> ¤Ï 4 Ê¸»úÌܤ¬¶õÇòʸ»ú¤Ç¤¢¤êŤµ¤¬ 7 
+    <tt>abc\ def</tt> ¤Ï 4 Ê¸»úÌܤ¬¶õÇòʸ»ú¤Ç¤¢¤êŤµ¤¬ 7 
     ¤Ç¤¢¤ë»ý¤Ä̾Á°¤ò»ý¤Ä¥·¥ó¥Ü¥ë¤òÀ¸À®¤¹¤ë¡£   */
 
 MPlist *
     ¤Ç¤¢¤ë»ý¤Ä̾Á°¤ò»ý¤Ä¥·¥ó¥Ü¥ë¤òÀ¸À®¤¹¤ë¡£   */
 
 MPlist *
@@ -1417,16 +1640,18 @@ mplist_deserialize (MText *mt)
     @brief Dump a property list.
 
     The mdebug_dump_plist () function prints a property list $PLIST in
     @brief Dump a property list.
 
     The mdebug_dump_plist () function prints a property list $PLIST in
-    a human readable way to the stderr.  $INDENT specifies how many
-    columns to indent the lines but the first one.
+    a human readable way to the stderr or to what specified by the
+    environment variable MDEBUG_OUTPUT_FILE.  $INDENT specifies how
+    many columns to indent the lines but the first one.
 
     @return
     This function returns $PLIST.  */
 /***ja
     @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤ò¥À¥ó¥×¤¹¤ë.
 
 
     @return
     This function returns $PLIST.  */
 /***ja
     @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤ò¥À¥ó¥×¤¹¤ë.
 
-    ´Ø¿ô mdebug_dump_plist () ¤Ï¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤ò stderr 
-    ¤Ë¿Í´Ö¤Ë²ÄÆɤʷÁ¤Ç°õºþ¤¹¤ë¡£ $INDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£
+    ´Ø¿ô mdebug_dump_plist () ¤Ï¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤òɸ½à¥¨¥é¡¼½Ð
+    ÎϤ⤷¤¯¤Ï´Ä¶­ÊÑ¿ô MDEBUG_DUMP_FONT ¤Ç»ØÄꤵ¤ì¤¿¥Õ¥¡¥¤¥ë¤Ë¿Í´Ö¤Ë²Ä
+    ÆɤʷÁ¤Ç°õºþ¤¹¤ë¡£ $INDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£
 
     @return
     ¤³¤Î´Ø¿ô¤Ï $PLIST ¤òÊÖ¤¹¡£  */
 
     @return
     ¤³¤Î´Ø¿ô¤Ï $PLIST ¤òÊÖ¤¹¡£  */
@@ -1435,21 +1660,18 @@ mdebug_dump_plist (MPlist *plist, int indent)
 {
   char *prefix = (char *) alloca (indent + 1);
   MPlist *pl;
 {
   char *prefix = (char *) alloca (indent + 1);
   MPlist *pl;
-  int first = 1;
 
   memset (prefix, 32, indent);
   prefix[indent] = 0;
 
 
   memset (prefix, 32, indent);
   prefix[indent] = 0;
 
-  fprintf (stderr, "(");
+  fprintf (mdebug__output, "(");
   MPLIST_DO (pl, plist)
     {
   MPLIST_DO (pl, plist)
     {
-      if (first)
-       first = 0;
-      else
-       fprintf (stderr, "\n%s ", prefix);
-      dump_plist_element (pl, indent + 2);
+      if (pl != plist)
+       fprintf (mdebug__output, "\n%s ", prefix);
+      write_element (NULL, pl, indent + 1);
     }
     }
-  fprintf (stderr, ")");
+  fprintf (mdebug__output, ")");
   return plist;
 }
 
   return plist;
 }