*** empty log message ***
[m17n/m17n-lib.git] / src / plist.c
1 /* plist.c -- plist module.
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 /***en
24     @addtogroup m17nPlist
25
26     @brief Property List objects and API for them.
27
28     A @e property @e list (or @e plist for short) is a list of zero or
29     more properties.  A property consists of a @e key and a @e value,
30     where key is a symbol and value is anything that can be cast to
31     <tt>(void *)</tt>.
32
33     If the key of a property is a @e managing @e key, its @e value is
34     a @e managed @e object.  A property list itself is a managed
35     objects.  */
36
37 /*=*/
38
39 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
40 /*** @addtogroup m17nInternal
41      @{ */
42
43 #include <stdio.h>
44 #include <string.h>
45
46 #include "m17n.h"
47 #include "m17n-misc.h"
48 #include "internal.h"
49 #include "character.h"
50 #include "mtext.h"
51 #include "symbol.h"
52 #include "plist.h"
53
54 static M17NObjectArray plist_table;
55
56 /** Set PLIST to a newly allocated plist object.  */
57
58 #define MPLIST_NEW(plist)                               \
59   do {                                                  \
60     M17N_OBJECT (plist, free_plist, MERROR_PLIST);      \
61     M17N_OBJECT_REGISTER (plist_table, plist);          \
62   } while (0)
63
64
65 /** Set the element of PLIST to KEY and VAL.  If PLIST is an anchor,
66     append a new anchor.  */
67
68 #define MPLIST_SET(plist, key, val)     \
69   do {                                  \
70     MPLIST_KEY (plist) = (key);         \
71     MPLIST_VAL (plist) = (val);         \
72     if (! (plist)->next)                \
73       MPLIST_NEW ((plist)->next);       \
74   } while (0)
75
76
77 /** Set the element of PLIST to KEY and VAL.  PLIST must be an anchor.
78     Append a new anchor and set PLIST to that anchor.  */
79
80 #define MPLIST_SET_ADVANCE(plist, key, val)     \
81   do {                                          \
82     MPLIST_KEY (plist) = (key);                 \
83     MPLIST_VAL (plist) = (val);                 \
84     MPLIST_NEW ((plist)->next);                 \
85     plist = (plist)->next;                      \
86   } while (0)
87
88
89 static void
90 free_plist (void *object)
91 {
92   MPlist *plist = (MPlist *) object;
93
94   do {
95     MPlist *next = plist->next;
96
97     if (MPLIST_KEY (plist) != Mnil && MPLIST_KEY (plist)->managing_key)
98       M17N_OBJECT_UNREF (MPLIST_VAL (plist));
99     M17N_OBJECT_UNREGISTER (plist_table, plist);
100     free (plist);
101     plist = next;
102   } while (plist && plist->control.ref_count == 1);
103   M17N_OBJECT_UNREF (plist);
104 }
105
106 \f
107
108 /* Load a plist from a string.  */
109
110 #define READ_CHUNK 0x10000
111
112 typedef struct
113 {
114   /* File pointer if the stream is associated with a file.  Otherwise
115      NULL.  */
116   FILE *fp;
117   int eof;
118   unsigned char buffer[READ_CHUNK];
119   unsigned char *p, *pend;
120 } MStream;
121
122 static int
123 get_byte (MStream *st)
124 {
125   int n;
126
127   if (! st->fp || st->eof)
128     return EOF;
129   n = fread (st->buffer, 1, READ_CHUNK, st->fp);
130   if (n <= 0)
131     {
132       st->eof = 1;
133       return EOF;
134     }
135   st->p = st->buffer + 1;
136   st->pend = st->buffer + n;
137   return st->buffer[0];
138 }
139
140 #define GETC(st)        \
141   ((st)->p < (st)->pend ? *(st)->p++ : get_byte (st))
142
143
144 #define UNGETC(c, st)   \
145   (*--(st)->p = (c))
146
147 /** Mapping table for reading a number.  Hexadecimal chars
148     (0..9,A..F,a..F) are mapped to the corresponding numbers.
149     Apostrophe (code 39) is mapped to 254.  All the other bytes are
150     mapped to 255.  */
151 unsigned char hex_mnemonic[256];
152
153 /** Mapping table for escaped characters.  Mnemonic characters (e, b,
154     f, n, r, or t) that follows '\' are mapped to the corresponding
155     character code.  All the other bytes are mapped to themselves.  */
156 unsigned char escape_mnemonic[256];
157
158
159 /** Read an integer from the stream ST.  It is assumed that we have
160     already read one character C.  */
161
162 static int
163 read_decimal (MStream *st, int c)
164 {
165   int num = 0;
166
167   while (c >= '0' && c <= '9')
168     {
169       num = (num * 10) + (c - '0');
170       c = GETC (st);
171     }
172
173   if (c != EOF)
174     UNGETC (c, st);
175   return num;
176 }
177
178 /** Read an unsigned from the stream ST.  */
179
180 static unsigned
181 read_hexadesimal (MStream *st)
182 {
183   int c;
184   unsigned num = 0, n;
185
186   while ((c = GETC (st)) != EOF
187          && (n = hex_mnemonic[c]) < 16)
188     num = (num << 4) | n;
189   if (c != EOF)
190     UNGETC (c, st);
191   return num;
192 }
193
194
195 /** Read an M-text element from ST, and add it to LIST.  Return a list
196     for the next element.  */
197
198 static MPlist *
199 read_mtext_element (MPlist *plist, MStream *st)
200 {
201   unsigned char buffer[1024];
202   int bufsize = 1024;
203   unsigned char *buf = buffer;
204   int c, i;
205
206   i = 0;
207   while ((c = GETC (st)) != EOF && c != '"')
208     {
209       if (i + MAX_UTF8_CHAR_BYTES >= bufsize)
210         {
211           bufsize *= 2;
212           if (buf == buffer)
213             {
214               MTABLE_MALLOC (buf, bufsize, MERROR_PLIST);
215               memcpy (buf, buffer, i);
216             }
217           else
218             MTABLE_REALLOC (buf, bufsize, MERROR_PLIST);
219         }
220
221       if (c == '\\')
222         {
223           c = GETC (st);
224           if (c == EOF)
225             break;
226           if (c == 'x')
227             {
228               int next_c;
229
230               c = read_hexadesimal (st);
231               next_c = GETC (st);
232               if (next_c != ' ')
233                 UNGETC (next_c, st);
234             }
235           else
236             c = escape_mnemonic[c];
237         }
238
239       buf[i++] = c;
240     }
241
242   MPLIST_SET_ADVANCE (plist, Mtext,
243                       mtext__from_data (buf, i, MTEXT_FORMAT_UTF_8, 1));
244   if (buf != buffer)
245     free (buf);
246   return plist;
247 }
248
249 static int
250 read_character (MStream *st, int c)
251 {
252   unsigned char buf[MAX_UTF8_CHAR_BYTES + 1];
253   int len = CHAR_BYTES_BY_HEAD (c);
254   int i;
255
256   buf[0] = c;
257   for (i = 1; i < len; i++)
258     {
259       c = GETC (st);
260       if (c == EOF
261           || (c & 0xC0) != 0x80)
262         break;
263       buf[i] = c;
264     }
265   if (i == len)
266     c = STRING_CHAR_UTF8 (buf);
267   else
268     c = buf[0];
269   return c;
270 }
271
272
273 /** Read an integer element from ST, and add it to LIST.  Return a
274     list for the next element.  It is assumed that we have already
275     read the character C. */
276
277 static MPlist *
278 read_integer_element (MPlist *plist, MStream *st, int c)
279 {
280   int num;
281
282   if (c == '0' || c == '#')
283     {
284       c = GETC (st);
285       if (c == 'x')
286         num = read_hexadesimal (st);
287       else
288         num = read_decimal (st, c);
289     }
290   else if (c == '?')
291     {
292       c = GETC (st);
293       if (c == EOF)
294         num = 0;
295       else if (c != '\\')
296         {
297           if (c < 128 || ! CHAR_UNITS_BY_HEAD_UTF8 (c))
298             num = c;
299           else
300             num = read_character (st, c);
301         }
302       else
303         {
304           c = GETC (st);
305           if (c == EOF)
306             num = '\\';
307           else if (c < 128 || ! CHAR_UNITS_BY_HEAD_UTF8 (c))
308             num = escape_mnemonic[c];
309           else
310             num = read_character (st, c);
311         }
312     }
313   else if (c == '-')
314     num = - read_decimal (st, GETC (st));
315   else
316     num = read_decimal (st, c);
317
318   MPLIST_SET_ADVANCE (plist, Minteger, (void *) num);
319   return plist;
320 }
321
322 /** Read a symbol element from ST, and add it to LIST.  Return a list
323     for the next element.  */
324
325 static MPlist *
326 read_symbol_element (MPlist *plist, MStream *st)
327 {
328   unsigned char buffer[1024];
329   int bufsize = 1024;
330   unsigned char *buf = buffer;
331   int c, i;
332
333   i = 0;
334   while ((c = GETC (st)) != EOF
335          && c > ' '
336          && c != ')' && c != '(' && c != '"')
337     {
338       if (i >= bufsize)
339         {
340           bufsize *= 2;
341           if (buf == buffer)
342             {
343               MTABLE_MALLOC (buf, bufsize, MERROR_PLIST);
344               memcpy (buf, buffer, i);
345             }
346           else
347             MTABLE_REALLOC (buf, bufsize, MERROR_PLIST);
348         }
349       if (c == '\\')
350         {
351           c = GETC (st);
352           if (c == EOF)
353             break;
354           c = escape_mnemonic[c];
355         }
356       buf[i++] = c;
357     }
358
359   buf[i] = 0;
360   MPLIST_SET_ADVANCE (plist, Msymbol, msymbol ((char *) buf));
361   if (buf != buffer)
362     free (buf);
363   if (c > ' ')
364     UNGETC (c, st);
365   return plist;
366 }
367
368 /* Read an element of various type from stream ST, and add it to LIST.
369    Return a list for the next element.  The element type is decided by
370    the first token character found as below:
371         '(': plist
372         '"': mtext
373         '0'..'9', '-': integer
374         '?': integer representing character code
375         the other ASCII letters: symbol
376 */
377
378 static MPlist *
379 read_element (MPlist *plist, MStream *st)
380 {
381   int c;
382
383   /* Skip separators and comments.  */
384   while (1)
385     {
386       while ((c = GETC (st)) != EOF && c <= ' ');
387       if (c != ';')
388         break;
389       while ((c = GETC (st)) != EOF && c != '\n');
390       if (c == EOF)
391         break;
392     }
393
394   if (c == '(')
395     {
396       MPlist *pl, *p;
397
398       MPLIST_NEW (pl);
399       p = pl;
400       while ((p = read_element (p, st)));
401       MPLIST_SET_ADVANCE (plist, Mplist, pl);
402       return plist;
403     }
404   if (c == '"')
405     return read_mtext_element (plist, st);
406   if ((c >= '0' && c <= '9') || c == '-' || c == '?' || c == '#')
407     return read_integer_element (plist, st, c);
408   if (c == EOF || c == ')')
409     return NULL;
410   UNGETC (c, st);
411   return read_symbol_element (plist, st);
412 }
413
414 void
415 write_element (MText *mt, MPlist *plist)
416 {
417   if (MPLIST_SYMBOL_P (plist))
418     {
419       MSymbol sym = MPLIST_SYMBOL (plist);
420
421       if (sym == Mnil)
422         {
423           MTEXT_CAT_ASCII (mt, "nil");
424         }
425       else
426         {
427           char *name = MSYMBOL_NAME (sym);
428           char *buf = alloca (MSYMBOL_NAMELEN (sym) * 2 + 1), *p = buf;
429
430           while (*name)
431             {
432               if (*name <= ' ' || *name == '"' || *name == ')' || *name == ')')
433                 *p++ = '\\';
434               *p++ = *name++;
435             }
436           *p = '\0';
437           MTEXT_CAT_ASCII (mt, buf);
438         }
439     }
440   else if (MPLIST_INTEGER_P (plist))
441     {
442       int num = MPLIST_INTEGER (plist);
443       char buf[128];
444
445       sprintf (buf, "%d", num);
446       MTEXT_CAT_ASCII (mt, buf);
447     }
448   else if (MPLIST_PLIST_P (plist))
449     {
450       MPlist *pl;
451
452       plist = MPLIST_PLIST (plist);
453       mtext_cat_char (mt, '(');
454       MPLIST_DO (pl, plist)
455         {
456           if (pl != plist)
457             mtext_cat_char (mt, ' ');
458           write_element (mt, pl);
459         }
460       mtext_cat_char (mt, ')');
461     }
462   else if (MPLIST_MTEXT_P (plist))
463     {
464       mtext_cat_char (mt, '"');
465       /* Not yet implemnted */
466       mtext_cat_char (mt, '"');
467     }
468 }
469
470 /* Support functions for mdebug_dump_plist.  */
471
472 static void
473 dump_string (char *str)
474 {
475   char *p = str, *pend = p + strlen (p), *new, *p1;
476
477   new = p1 = alloca ((pend - p) * 4 + 1);
478   while (p < pend)
479     {
480       if (*p < 0)
481         {
482           sprintf (p1, "\\x%02X", (unsigned char) *p);
483           p1 += 4;
484         }
485       else if (*p < ' ')
486         {
487           *p1++ = '^';
488           *p1++ = *p + '@';
489         }
490       else if (*p == ' ')
491         {
492           *p1++ = '\\';
493           *p1++ = ' ';
494         }
495       else
496         *p1++ = *p;
497       p++;
498     }
499   *p1 = '\0';
500   fprintf (stderr, "%s", new);
501 }
502
503 static void
504 dump_plist_element (MPlist *plist, int indent)
505 {
506   char *prefix = (char *) alloca (indent + 1);
507   MSymbol key;
508
509   memset (prefix, 32, indent);
510   prefix[indent] = 0;
511
512   key = MPLIST_KEY (plist);
513   fprintf (stderr, "(%s(#%d) ", msymbol_name (MPLIST_KEY (plist)),
514            plist->control.ref_count);
515   if (key == Msymbol)
516     dump_string (msymbol_name (MPLIST_SYMBOL (plist)));
517   else if (key == Mtext)
518     mdebug_dump_mtext (MPLIST_MTEXT (plist), indent, 0);
519   else if (key == Minteger)
520     fprintf (stderr, "%x", MPLIST_INTEGER (plist));
521   else if (key == Mstring) 
522     fprintf (stderr, "\"%s\"", MPLIST_STRING (plist));
523   else if (key == Mplist)
524     {
525       fprintf (stderr, "\n%s", prefix);
526       mdebug_dump_plist (MPLIST_PLIST (plist), indent);
527     }
528   else
529     fprintf (stderr, "0x%X", (unsigned) MPLIST_VAL (plist));
530   fprintf (stderr, ")");
531 }
532
533 \f
534 /* Internal API */
535 int
536 mplist__init ()
537 {
538   int i;
539
540   plist_table.count = 0;
541
542   Minteger = msymbol ("integer");
543   Mplist = msymbol_as_managing_key ("plist");
544   Mtext = msymbol_as_managing_key ("mtext");
545
546   for (i = 0; i < 256; i++)
547     hex_mnemonic[i] = 255;
548   for (i = '0'; i <= '9'; i++)
549     hex_mnemonic[i] = i - '0';
550   for (i = 'A'; i <= 'F'; i++)
551     hex_mnemonic[i] = i - 'A' + 10;
552   for (i = 'a'; i <= 'f'; i++)
553     hex_mnemonic[i] = i - 'a' + 10;
554   for (i = 0; i < 256; i++)
555     escape_mnemonic[i] = i;
556   escape_mnemonic['e'] = 27;
557   escape_mnemonic['b'] = '\b';
558   escape_mnemonic['f'] = '\f';
559   escape_mnemonic['n'] = '\n';
560   escape_mnemonic['r'] = '\r';
561   escape_mnemonic['t'] = '\t';
562   escape_mnemonic['\\'] = '\\';
563
564   return 0;
565 }
566
567 void
568 mplist__fini (void)
569 {
570   mdebug__report_object ("Plist", &plist_table);
571 }
572
573
574 /* Parse this form of PLIST:
575       (symbol:KEY1 TYPE1:VAL1 symbol:KEY2 TYPE2:VAL2 ...)
576    and return a newly created plist of this form:
577       (KEY1:VAL1 KEY2:VAL2 ...)  */
578
579 MPlist *
580 mplist__from_plist (MPlist *plist)
581 {
582   MPlist *pl, *p;
583
584   MPLIST_NEW (pl);
585   p = pl;
586   while (! MPLIST_TAIL_P (plist))
587     {
588       MSymbol key, type;
589
590       if (! MPLIST_SYMBOL_P (plist))
591         MERROR (MERROR_PLIST, NULL);
592       key = MPLIST_SYMBOL (plist);
593       plist = MPLIST_NEXT (plist);
594       type = MPLIST_KEY (plist);
595       if (type->managing_key)
596         M17N_OBJECT_REF (MPLIST_VAL (plist));
597       MPLIST_SET_ADVANCE (p, key, MPLIST_VAL (plist));
598       plist = MPLIST_NEXT (plist);
599     }
600   return pl;
601 }
602
603 /** Parse this form of PLIST:
604       ((symbol:KEY1 ANY:VAL1 ... ) (symbol:KEY2 ANY:VAL2 ...) ...)
605     and return a newly created plist of this form:
606       (KEY1:(ANY:VAL1 ...) KEY2:(ANY:VAL2 ...) ...)
607     ANY can be any type.  */
608
609 MPlist *
610 mplist__from_alist (MPlist *plist)
611 {
612   MPlist *pl, *p;
613
614   MPLIST_NEW (pl);
615   p = pl;
616   MPLIST_DO (plist, plist)
617     {
618       MPlist *elt;
619
620       if (! MPLIST_PLIST_P (plist))
621         MERROR (MERROR_PLIST, NULL);
622       elt = MPLIST_PLIST (plist);
623       if (! MPLIST_SYMBOL_P (elt))
624         MERROR (MERROR_PLIST, NULL);
625       MPLIST_SET_ADVANCE (p, MPLIST_SYMBOL (elt), MPLIST_NEXT (elt));
626       M17N_OBJECT_REF (MPLIST_NEXT (elt));
627     }
628   return pl;
629 }
630
631
632 MPlist *
633 mplist__from_file (FILE *fp)
634 {
635   MPlist *plist, *pl;
636   MStream st;
637
638   st.fp = fp;
639   st.eof = 0;
640   st.p = st.pend = st.buffer;
641   MPLIST_NEW (plist);
642   pl = plist;
643   while ((pl = read_element (pl, &st)));
644   return plist;
645 }
646
647
648 /** Parse $STR of $N bytes and return a property list object.  $FORMAT
649     must be either @c MTEXT_FORMAT_US_ASCII or @c MTEXT_FORMAT_UTF_8,
650     and controls how to produce @c STRING or @c M-TEXT in the
651     following definition.
652
653     The syntax of $STR is as follows.
654
655     PLIST ::= '(' ELEMENT * ')'
656
657     ELEMENT ::= SYMBOL | INTEGER | UNSIGNED | STRING | M-TEXT | PLIST
658
659     SYMBOL ::= ascii-character-sequence
660
661     INTEGER ::= '-' ? [ '0' | .. | '9' ] +
662
663     UNSIGNED ::= '0x' [ '0' | .. | '9' | 'A' | .. | 'F' | 'a' | .. | 'f' ] +
664
665     STRING ::= '"' byte-sequence '"'
666
667     M-TEXT ::= '"' byte-sequence '"'
668
669     Each kind of @c ELEMENT is assigned one of these keys:
670         @c Msymbol, @c Mint, @c Munsigned,
671         @c Mstring, @c Mtext, @c Mplist
672
673     In an ascii-character-sequence, a backslush (\) is used as the escape
674     character, which means that, for instance, <tt>"abc\ def"</tt>
675     produces a symbol whose name is of length seven with the fourth
676     character being a space.
677
678     In a byte-sequence, "\r", "\n", "\e", and "\t" are replaced by CR,
679     NL, ESC, and TAB character respectively, "\xXX" are replaced by
680     byte 0xXX.  After this replacement, the byte-sequence is decoded
681     into STRING or M-TEXT as below:
682
683     If $FORMAT is MTEXT_FORMAT_US_ASCII and the byte-sequence
684     contains only ASCII characters, it is decoded into STRING.
685     Otherwise, it is regarded as an UTF-8 sequence, and decoded into
686     M-TEXT.  */
687
688 MPlist *
689 mplist__from_string (unsigned char *str, int n)
690 {
691   MPlist *plist, *pl;
692   MStream st;
693
694   st.fp = NULL;
695   st.eof = 0;
696   st.p = str;
697   st.pend = str + n;
698   MPLIST_NEW (plist);
699   pl = plist;
700   while ((pl = read_element (pl, &st)));
701   return plist;
702 }
703
704 int
705 mplist__serialize (MText *mt, MPlist *plist)
706 {
707   MPlist *pl;
708
709   MPLIST_DO (pl, plist)
710     {
711       if (pl != plist)
712         mtext_cat_char (mt, ' ');
713       write_element (mt, pl);
714     }
715   return 0;
716 }
717
718 MPlist *
719 mplist__deserialize (MText *mt)
720 {
721   if (mt->format > MTEXT_FORMAT_UTF_8)
722     MERROR (MERROR_PLIST, NULL);
723   return mplist__from_string (MTEXT_DATA (mt), mtext_nbytes (mt));
724 }
725
726
727 /*** @} */
728 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
729
730 \f
731 /* External API */
732
733 /*** @addtogroup m17nPlist */
734 /*** @{ */
735 /*=*/
736
737 /***en
738     @brief Symbol whose name is "integer".
739
740     The symbol @c Minteger has the name <tt>"integer"</tt>.  A value
741     of a plist whose key is @c Minteger must be an integer.  */
742
743 MSymbol Minteger;
744 /*=*/
745
746 /***en
747     @brief Symbol whose name is "plist".
748
749     The symbol @c Mplist has the name <tt>"plist"</tt>.  It is a
750     managing key.  A value of a plist whose key is @c Mplist must be a
751     plist.  */
752
753 MSymbol Mplist;
754 /*=*/
755
756 /***en
757     @brief Symbol whose name is "mtext".
758
759     The symbol @c Mtext has the name <tt>"mtext"</tt>.  It is a
760     managing key.  A value of a plist whose key is @c Mtext must be an
761     M-text.  */
762
763 /***ja
764     @brief "text" ¤ò̾Á°¤È¤·¤Æ»ý¤Ä¥·¥ó¥Ü¥ë
765
766     ÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë @c Mtext ¤Ï <tt>"text"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä´ÉÍý
767     ¥­¡¼¤Ç¤¢¤ë¡£ */
768
769 MSymbol Mtext;
770
771
772 /*=*/
773 /***en
774     @brief Create a property list object.
775
776     The mplist () function returns a newly created property list
777     object of length zero.
778
779     @returns
780     This function returns a newly created property list.
781
782     @errors
783     This function never fails.  */
784
785 MPlist *
786 mplist ()
787 {
788   MPlist *plist;
789
790   MPLIST_NEW (plist);
791   return plist;
792 }  
793
794 /*=*/
795 /***en
796     @brief Copy a plist.
797
798     The mplist_copy () function copies $PLIST.  In the copy, the
799     values are the same as those of $PLIST.
800
801     @return
802     This function returns a newly created plist which is a copy of
803     $PLIST.  */
804 /***
805     @errors
806     This function never fails.  */ 
807
808 MPlist *
809 mplist_copy (MPlist *plist)
810 {
811   MPlist *copy = mplist (), *pl = copy;
812
813   MPLIST_DO (plist, plist)
814     pl = mplist_add (pl, MPLIST_KEY (plist), MPLIST_VAL (plist));
815   return copy;
816 }
817
818 /*=*/
819
820 /***en
821     @brief Set the value of a property in a property list object.
822
823     The mplist_put () function searches property list object $PLIST
824     from the beginning for a property whose key is $KEY.  If such a
825     property is found, its value is changed to $VALUE.  Otherwise, a
826     new property whose key is $KEY and value is $VALUE is appended at
827     the end of $PLIST.  See the documentation of mplist_add () for
828     the restriction on $KEY and $VAL.
829
830     If $KEY is a managing key, $VAL must be a managed object.  In this
831     case, the reference count of the old value, if not @c NULL, is
832     decremented by one, and that of $VAL is incremented by one.
833
834     @return
835     If the operation was successful, mplist_put () returns a sublist of
836     $PLIST whose first element is the just modified or added one.
837     Otherwise, it returns @c NULL.  */
838
839 MPlist *
840 mplist_put (MPlist *plist, MSymbol key, void *val)
841 {
842   if (key == Mnil)
843     MERROR (MERROR_PLIST, NULL);
844   MPLIST_FIND (plist, key);
845   if (key->managing_key)
846     {
847       if (! MPLIST_TAIL_P (plist))
848         M17N_OBJECT_UNREF (MPLIST_VAL (plist));
849       M17N_OBJECT_REF (val);
850     }
851   MPLIST_SET (plist, key, val);
852   return plist;
853 }
854
855 /*=*/
856
857 /***en
858     @brief Get the value of a property in a property list object.
859
860     The mplist_get () function searches property list object $PLIST
861     from the beginning for a property whose key is $KEY.  If such a
862     property is found, a pointer to its value is returned as the type
863     of <tt>(void *)</tt>.  If not found, @c NULL is returned.
864
865     When @c NULL is returned, there are two possibilities: one is the
866     case where no property is found (see above); the other is the case
867     where a property is found and its value is @c NULL.  In case that
868     these two cases must be distinguished, use the mplist_find_by_key ()
869     function.  */
870
871 /***
872     @seealso
873     mplist_find_by_key () */
874
875 void *
876 mplist_get (MPlist *plist, MSymbol key)
877 {
878   MPLIST_FIND (plist, key);
879   return (MPLIST_TAIL_P (plist) ? NULL : MPLIST_VAL (plist));
880 }
881
882 /*=*/
883
884 /***en
885     @brief Add a property at the end of a property list object.
886
887     The mplist_add () function appends at the end of $PLIST a property
888     whose key is $KEY and value is $VAL.  $KEY can be any symbol
889     other than @c Mnil.
890
891     If $KEY is a managing key, $VAL must be a managed object.  In this
892     case, the reference count of $VAL is incremented by one.
893
894     @return
895     If the operation was successful, mplist_add () returns a sublist of
896     $PLIST whose first element is the just added one.  Otherwise, it
897     returns @c NULL.  */
898
899 MPlist *
900 mplist_add (MPlist *plist, MSymbol key, void *val)
901 {
902   if (key == Mnil)
903     MERROR (MERROR_PLIST, NULL);
904   MPLIST_FIND (plist, Mnil);
905   if (key->managing_key)
906     M17N_OBJECT_REF (val);
907   MPLIST_KEY (plist) = key;
908   MPLIST_VAL (plist) = val;
909   MPLIST_NEW (plist->next);
910   return plist;
911 }
912
913 /*=*/
914
915 /***en
916     @brief Push a property to a property list object.
917
918     The mplist_push () function pushes at the top of $PLIST a
919     property whose key is $KEY and value si $VAL.
920
921     If $KEY is a managing key, $VAL must be a managed object.  In this
922     case, the reference count of $VAL is incremented by one.
923
924     @return
925     If the operation was successful, this function returns $PLIST.
926     Otherwise, it returns @c NULL.  */
927
928 MPlist *
929 mplist_push (MPlist *plist, MSymbol key, void *val)
930 {
931   MPlist *pl;
932
933   if (key == Mnil)
934     MERROR (MERROR_PLIST, NULL);
935   MPLIST_NEW (pl);
936   MPLIST_KEY (pl) = MPLIST_KEY (plist);
937   MPLIST_VAL (pl) = MPLIST_VAL (plist);
938   pl->next = plist->next;
939   plist->next = pl;
940   if (key->managing_key)
941     M17N_OBJECT_REF (val);
942   MPLIST_KEY (plist) = key;
943   MPLIST_VAL (plist) = val;
944   return plist;
945 }
946
947 /*=*/
948
949 /***en
950     @brief Pop a property from a property list object.
951
952     The mplist_pop () function pops the topmost property from $PLIST.
953     As a result, the key and value of $PLIST becomes those of the next
954     of $PLIST.
955
956     @return
957     If the operation was successful, this function return the value of
958     the just popped property.  Otherwise, it returns @c NULL.  */
959
960 void *
961 mplist_pop (MPlist *plist)
962 {
963   void *val;
964   MPlist *next;
965
966   if (MPLIST_TAIL_P (plist))
967     return NULL;
968   val = MPLIST_VAL (plist);
969   next = plist->next;
970   MPLIST_KEY (plist) = MPLIST_KEY (next);
971   MPLIST_VAL (plist) = MPLIST_VAL (next);
972   if (MPLIST_KEY (plist) != Mnil
973       && MPLIST_KEY (plist)->managing_key
974       && MPLIST_VAL (plist))
975     M17N_OBJECT_REF (MPLIST_VAL (plist));
976   plist->next = next->next;
977   if (plist->next)
978     M17N_OBJECT_REF (plist->next);
979   M17N_OBJECT_UNREF (next);
980   return val;
981 }
982
983 /*=*/
984 /***en
985     @brief Find a property of a specific key in a property list object.
986
987     The mplist_find_by_key () function searches property list object
988     $PLIST from the beginning for a property whose key is $KEY.  If
989     such a property is found, a sublist of $PLIST whose first element
990     is the found one is returned.  Otherwise, @c NULL is returned.
991
992     If $KEY is Mnil, it returns the last a sublist of $PLIST whose
993     first element is the last one of $PLIST.  */
994
995 MPlist *
996 mplist_find_by_key (MPlist *plist, MSymbol key)
997 {
998   MPLIST_FIND (plist, key);
999   return (MPLIST_TAIL_P (plist)
1000           ? (key == Mnil ? plist : NULL)
1001           : plist);
1002 }
1003
1004 /*=*/
1005 /***en
1006     @brief Find a property of a specific value in a property list object.
1007
1008     The mplist_find_by_value () function searches property list object
1009     $PLIST from the beginning for a property whose value is $VAL.  If
1010     such a property is found, a sublist of $PLIST whose first element
1011     is the found one is returned.  Otherwise, @c NULL is returned.  */
1012
1013 MPlist *
1014 mplist_find_by_value (MPlist *plist, void *val)
1015 {
1016   MPLIST_DO (plist, plist)
1017     {
1018       if (MPLIST_VAL (plist) == val)
1019         return plist;
1020     }
1021   return NULL;
1022 }
1023
1024 /*=*/
1025
1026 /***en
1027     @brief Return the next sublist of a plist.
1028
1029     The mplist_next () function returns a pointer to the sublist of
1030     $PLIST, which begins at the second element in $PLIST.  If the
1031     length of $PLIST is zero, it returns @c NULL.  */
1032
1033 MPlist *
1034 mplist_next (MPlist *plist)
1035 {
1036   return (MPLIST_TAIL_P (plist) ? NULL : plist->next);
1037 }
1038
1039 /*=*/
1040
1041 /***en
1042     @brief Set the first property in a property list object.
1043
1044     The mplist_set () function sets the key and value of the first
1045     property in property list object $PLIST to $KEY and $VALUE,
1046     respectively.  See the documentation of mplist_add () for the
1047     restriction on $KEY and $VAL.
1048
1049     @return
1050     If the operation was successful, mplist_set () returns $PLIST.
1051     Otherwise, it returns @c NULL.  */
1052
1053 MPlist *
1054 mplist_set (MPlist *plist, MSymbol key, void * val)
1055 {
1056   if (key == Mnil)
1057     {
1058       if (! MPLIST_TAIL_P (plist))
1059         {
1060           key = MPLIST_KEY (plist);
1061           M17N_OBJECT_UNREF (MPLIST_NEXT (plist));
1062           MPLIST_KEY (plist) = Mnil;
1063           if (key->managing_key && MPLIST_VAL (plist))
1064             M17N_OBJECT_UNREF (MPLIST_VAL (plist));
1065           plist->next = NULL;
1066         }
1067     }
1068   else
1069     {
1070       if (! MPLIST_TAIL_P (plist)
1071           && MPLIST_KEY (plist)->managing_key
1072           && MPLIST_VAL (plist))
1073         M17N_OBJECT_UNREF (MPLIST_VAL (plist));
1074       if (key->managing_key)
1075         M17N_OBJECT_REF (val);
1076       MPLIST_SET (plist, key, val);
1077     }
1078   return plist;
1079 }
1080
1081 /*=*/
1082
1083 /***en
1084     @brief Return the length of a plist.
1085
1086     The mplist_length () function returns the number of properties in
1087     property list object $PLIST.  */
1088
1089 int
1090 mplist_length (MPlist *plist)
1091 {
1092   int n;
1093
1094   for (n = 0; ! (MPLIST_TAIL_P (plist)); n++, plist = plist->next);
1095   return n;
1096 }
1097
1098 /*=*/
1099
1100 /***en
1101     @brief Return the key of the first property in a property list object.
1102
1103     The mplist_key () function returns the key of the first property
1104     in property list object $PLIST.  If the length of $PLIST is zero,
1105     it returns @c Mnil.  */
1106
1107 MSymbol
1108 mplist_key (MPlist *plist)
1109 {
1110   return MPLIST_KEY (plist);
1111 }
1112
1113 /*=*/
1114
1115 /***en
1116     @brief Return the value of the first property in a property list object.
1117
1118     The mplist_value () function returns the value of the first
1119     property in property list object $PLIST.  If the length of $PLIST
1120     is zero, it returns @c NULL.  */
1121
1122 void *
1123 mplist_value (MPlist *plist)
1124 {
1125   return MPLIST_VAL (plist);
1126 }
1127
1128 /*** @}  */
1129
1130 /*** @addtogroup m17nDebug */
1131 /*=*/
1132 /*** @{  */
1133
1134 /***en
1135     @brief Dump a plist.
1136
1137     The mdebug_dump_plist () function prints $PLIST in a human
1138     readable way to the stderr.  $INDENT specifies how many columns to
1139     indent the lines but the first one.
1140
1141     @return
1142     This function returns $PLIST.  */
1143
1144 MPlist *
1145 mdebug_dump_plist (MPlist *plist, int indent)
1146 {
1147   char *prefix = (char *) alloca (indent + 1);
1148   MPlist *pl;
1149   int first = 1;
1150
1151   memset (prefix, 32, indent);
1152   prefix[indent] = 0;
1153
1154   fprintf (stderr, "(");
1155   MPLIST_DO (pl, plist)
1156     {
1157       if (first)
1158         first = 0;
1159       else
1160         fprintf (stderr, "\n%s ", prefix);
1161       dump_plist_element (pl, indent + 2);
1162     }
1163   fprintf (stderr, ")");
1164   return plist;
1165 }
1166
1167 /*** @} */
1168
1169 /*
1170   Local Variables:
1171   coding: euc-japan
1172   End:
1173 */