Update copyright years
[m17n/m17n-lib.git] / src / plist.c
index 3947726..1e449db 100644 (file)
@@ -1,5 +1,5 @@
 /* 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
 
@@ -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
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    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
-    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
 
@@ -55,6 +75,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "config.h"
 #include "m17n.h"
@@ -108,7 +129,8 @@ free_plist (void *object)
   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);
@@ -211,24 +233,22 @@ read_hexadesimal (MStream *st)
 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 != '"')
     {
+      int is_char = 0;
+
       if (c == '\\')
        {
          c = GETC (st);
          if (c == EOF)
            break;
+         if (c == '\n')
+           continue;
          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);
+             if (c >= 0x80)
+               is_char = 1;
            }
          else
            c = escape_mnemonic[c];
@@ -244,65 +266,34 @@ read_mtext_element (MPlist *plist, MStream *st, int 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)
     {
-      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;
@@ -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_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;
-  int c, i;
+  int i;
 
   i = 0;
-  while ((c = GETC (st)) != EOF
+  while (c != EOF
         && c > ' '
         && c != ')' && c != '(' && c != '"')
     {
@@ -418,6 +359,7 @@ read_symbol_element (MPlist *plist, MStream *st, int skip)
        }
       if (! skip)
        buf[i++] = c;
+      c = GETC (st);
     }
 
   if (c > ' ')
@@ -432,6 +374,71 @@ read_symbol_element (MPlist *plist, MStream *st, int skip)
   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:
@@ -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))
        {
-         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;
            }
-         while ((p = read_element (p, st, NULL)));
-         if (! MPLIST_TAIL_P (p0))
-           MPLIST_SET_ADVANCE (plist, Mplist, pl);
          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
        {
@@ -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;
-  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))
     {
@@ -534,91 +575,122 @@ write_element (MText *mt, MPlist *plist)
       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;
+      int newline = 0;
 
+      if (MPLIST_NESTED_P (plist))
+       {
+         write_symbol (mt, MPLIST_KEY (plist));
+         PUTC (mt, ':');
+       }
       plist = MPLIST_PLIST (plist);
-      mtext_cat_char (mt, '(');
+      PUTC (mt, '(');
+      if (indent >= 0)
+       indent++;
       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))
     {
-      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
-       *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
@@ -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));
+      if (type == Mplist)
+       MPLIST_SET_NESTED_P (p);
       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);
+      MPLIST_SET_NESTED_P (p);
       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
-mplist__serialize (MText *mt, MPlist *plist)
+mplist__serialize (MText *mt, MPlist *plist, int pretty)
 {
   MPlist *pl;
+  int separator = pretty ? '\n' : ' ';
 
   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;
 }
 
+/**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 */
 
@@ -877,7 +1034,7 @@ MSymbol Mtext;
     ¤³¤Î´Ø¿ô¤Ï·è¤·¤Æ¼ºÇÔ¤·¤Ê¤¤¡£     */
 
 MPlist *
-mplist ()
+mplist (void)
 {
   MPlist *plist;
 
@@ -916,7 +1073,11 @@ mplist_copy (MPlist *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;
 }
 
@@ -979,10 +1140,10 @@ mplist_put (MPlist *plist, MSymbol key, void *val)
 /***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
@@ -992,8 +1153,8 @@ mplist_put (MPlist *plist, MSymbol key, void *val)
 /***ja
     @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥ÈÃæ¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÆÀ¤ë.
 
-    ´Ø¿ô mplist_get () ¤Ï¡¢¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤ò»Ï¤á¤«¤éõ¤·¤Æ¡¢¥­¡¼¤¬
-    $KEY ¤Ç¤¢¤ë¥×¥í¥Ñ¥Æ¥£¤ò¸«¤Ä¤±¤ë¡£¸«¤Ä¤«¤ì¤Ð¡¢¤½¤ÎÃͤؤΥݥ¤¥ó¥¿¤ò
+    ´Ø¿ô mplist_get () ¤Ï¡¢¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤ò»Ï¤á¤«¤éõ¤·¤Æ¡¢¥­¡¼
+    ¤¬ $KEY ¤Ç¤¢¤ë¥×¥í¥Ñ¥Æ¥£¤ò¸«¤Ä¤±¤ë¡£¸«¤Ä¤«¤ì¤Ð¡¢¤½¤ÎÃͤò
     <tt>(void *)</tt> ·¿¤ÇÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
 
     @c NULL ¤¬Ê֤俺ݤˤÏÆó¤Ä¤Î²ÄǽÀ­¤¬¤¢¤ë: 
@@ -1015,6 +1176,67 @@ mplist_get (MPlist *plist, MSymbol key)
 /*=*/
 
 /***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
@@ -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);
+  if (MPLIST_NESTED_P (plist))
+    MPLIST_SET_NESTED_P (pl);
   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 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
@@ -1116,9 +1340,8 @@ mplist_push (MPlist *plist, MSymbol key, void *val)
 /***ja
     @brief ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤ÎÀèƬ¤«¤é¥×¥í¥Ñ¥Æ¥£¤òºï½ü¤¹¤ë.
 
-    ´Ø¿ô mplist_pop () ¤Ï¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST 
-    ¤ÎÀèƬ¤Î¥×¥í¥Ñ¥Æ¥£¤òºï½ü¤¹¤ë¡£·ë²Ì¤È¤·¤Æ¡¢¸µ¤Î $PLIST ¤Î2ÈÖÌܤΥ­¡¼¤ÈÃͤ¬¡¢¿·¤·¤¤ 
-    $PLIST ¤ÎÀèƬ¤Î¥­¡¼¤ÈÃͤˤʤ롣
+    ´Ø¿ô mplist_pop () ¤Ï¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤ÎÀèƬ¤Î¥×¥í¥Ñ¥Æ¥£¤òºï
+    ½ü¤¹¤ë¡£·ë²Ì¤È¤·¤Æ¡¢¸µ¤Î2ÈÖÌܤΥ­¡¼¤ÈÃͤ¬ÀèƬ¤Î¥­¡¼¤ÈÃͤˤʤ롣
 
     @return 
     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ïºï½ü¤µ¤ì¤¿¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð
@@ -1265,11 +1488,11 @@ mplist_set (MPlist *plist, MSymbol key, void * val)
     }
   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 (val && key->managing_key)
-       M17N_OBJECT_REF (val);
       MPLIST_SET (plist, key, val);
     }
   return plist;
@@ -1338,7 +1561,7 @@ mplist_value (MPlist *plist)
 }
 
 /***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.
@@ -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
-    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
@@ -1385,7 +1608,7 @@ mplist_value (MPlist *plist)
     @c Mplist ¤Î¤¤¤º¤ì¤«¤ò³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤ë¡£
 
     ¥¢¥¹¥­¡¼Ê¸»úÎóÆâ¤Ç¤Ï¡¢¥Ð¥Ã¥¯¥¹¥é¥Ã¥·¥å (\) ¤¬¥¨¥¹¥±¡¼¥×ʸ»ú¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£¤¿¤È¤¨¤Ð
-    <tt>"abc\ def"</tt> ¤Ï 4 Ê¸»úÌܤ¬¶õÇòʸ»ú¤Ç¤¢¤êŤµ¤¬ 7 
+    <tt>abc\ def</tt> ¤Ï 4 Ê¸»úÌܤ¬¶õÇòʸ»ú¤Ç¤¢¤êŤµ¤¬ 7 
     ¤Ç¤¢¤ë»ý¤Ä̾Á°¤ò»ý¤Ä¥·¥ó¥Ü¥ë¤òÀ¸À®¤¹¤ë¡£   */
 
 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
-    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 ¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤ò¥À¥ó¥×¤¹¤ë.
 
-    ´Ø¿ô mdebug_dump_plist () ¤Ï¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤ò stderr 
-    ¤Ë¿Í´Ö¤Ë²ÄÆɤʷÁ¤Ç°õºþ¤¹¤ë¡£ $INDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£
+    ´Ø¿ô mdebug_dump_plist () ¤Ï¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È $PLIST ¤òɸ½à¥¨¥é¡¼½Ð
+    ÎϤ⤷¤¯¤Ï´Ä¶­ÊÑ¿ô MDEBUG_DUMP_FONT ¤Ç»ØÄꤵ¤ì¤¿¥Õ¥¡¥¤¥ë¤Ë¿Í´Ö¤Ë²Ä
+    ÆɤʷÁ¤Ç°õºþ¤¹¤ë¡£ $INDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£
 
     @return
     ¤³¤Î´Ø¿ô¤Ï $PLIST ¤òÊÖ¤¹¡£  */
@@ -1435,21 +1660,18 @@ mdebug_dump_plist (MPlist *plist, int indent)
 {
   char *prefix = (char *) alloca (indent + 1);
   MPlist *pl;
-  int first = 1;
 
   memset (prefix, 32, indent);
   prefix[indent] = 0;
 
-  fprintf (stderr, "(");
+  fprintf (mdebug__output, "(");
   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;
 }