Copyright years udpated.
[m17n/m17n-lib.git] / src / textprop.c
1 /* textprop.c -- text property module.
2    Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
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., 51 Franklin Street, Fifth Floor,
21    02111-1307, USA.  */
22
23 /***en
24     @addtogroup m17nTextProperty
25     @brief Function to handle text properties.
26
27     Each character in an M-text can have properties called @e text @e
28     properties.  Text properties store various kinds of information
29     attached to parts of an M-text to provide application programs
30     with a unified view of those information.  As rich information can
31     be stored in M-texts in the form of text properties, functions in
32     application programs can be simple.
33
34     A text property consists of a @e key and @e values, where key is a
35     symbol and values are anything that can be cast to <tt>(void *)
36     </tt>.  Unlike other types of properties, a text property can
37     have multiple values.  "The text property whose key is K" may be
38     shortened to "K property".  */
39
40 /***ja
41     @addtogroup m17nTextProperty
42     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÁàºî¤¹¤ë¤¿¤á¤Î´Ø¿ô.
43
44     M-text Æâ¤Î³Æʸ»ú¤Ï¡¢@e ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ ¤È¸Æ¤Ð¤ì¤ë¥×¥í¥Ñ¥Æ¥£¤ò
45     »ý¤Ä¤³¤È¤¬¤Ç¤­¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¡¢M-text ¤Î³ÆÉô°Ì¤ËÉղ䵤ì
46     ¤¿¤µ¤Þ¤¶¤Þ¤Ê¾ðÊó¤òÊÝ»ý¤·¤Æ¤ª¤ê¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¤½¤ì¤é
47     ¤Î¾ðÊó¤òÅý°ìŪ¤Ë°·¤¦¤³¤È¤¬¤Ç¤­¤ë¡£M-text ¼«ÂΤ¬Ë­É٤ʾðÊó¤ò»ý¤Ä¤¿
48     ¤á¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥àÃæ¤Î´Ø¿ô¤ò´ÊÁDz½¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£
49
50     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï @e ¥­¡¼ ¤È @e ÃÍ ¤«¤é¤Ê¤ë¡£¥­¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢
51     ¤ê¡¢ÃͤϠ<tt>(void *)</tt> ·¿¤Ë¥­¥ã¥¹¥È¤Ç¤­¤ë¤â¤Î¤Ê¤é²¿¤Ç¤â¤è¤¤¡£
52     Â¾¤Î¥¿¥¤¥×¤Î¥×¥í¥Ñ¥Æ¥£¤È°Û¤Ê¤ê¡¢°ì¤Ä¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬Ê£¿ô¤ÎÃÍ
53     ¤ò»ý¤Ä¤³¤È¤¬µö¤µ¤ì¤ë¡£¡Ö¥­¡¼¤¬ K ¤Ç¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¡×¤Î¤³¤È
54     ¤ò´Êñ¤Ë¡ÖK ¥×¥í¥Ñ¥Æ¥£¡×¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£  */
55
56 /*=*/
57
58 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
59 /*** @addtogroup m17nInternal
60      @{ */
61
62 #include <config.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66
67 #ifdef HAVE_XML2
68 #include <libxml/tree.h>
69 #include <libxml/parser.h>
70 #include <libxml/xmlmemory.h>
71 #include <libxml/xpath.h>
72 #endif
73
74 #include "m17n.h"
75 #include "m17n-misc.h"
76 #include "internal.h"
77 #include "symbol.h"
78 #include "mtext.h"
79 #include "textprop.h"
80
81 #define TEXT_PROP_DEBUG
82
83 #undef xassert
84 #ifdef TEXT_PROP_DEBUG
85 #define xassert(X)      do {if (!(X)) mdebug_hook ();} while (0)
86 #else
87 #define xassert(X)      (void) 0
88 #endif  /* not FONTSET_DEBUG */
89
90 /* Hierarchy of objects (MText, MTextPlist, MInterval, MTextProperty)
91
92 MText
93   |    key/a         key/b                key/x
94   +--> MTextPlist -> MTextPlist -> ... -> MTextPlist
95          |             |
96          |             +- tail <-----------------------------------------+
97          |             |                                                 |
98          |             +- head <--> MInterval <--> ... <--> MInterval <--+
99          |
100          +- tail --------------------------------------------------------+
101          |                                                               |
102          +- head --> MInterval <--> MInterval <--> ... <--> MInterval <--+
103                        |               |
104                        +---------------+------------> MTextProperty
105                        +--> MTextProperty
106                        ...
107
108
109 Examples:
110
111 MTextProperty a/A                    [AAAAAAAAAAAAAAAAAAAAA]
112 MTextProperty a/B           [BBBBBBBBBBBBBBBBB] 
113 MTextPlist a     |--intvl1--|-intvl2-|-intvl3-|---intvl4---|-intvl5-|
114                 
115
116 MTextProperty b/A                    [AAAAAAAAAA]
117 MTextProperty b/B         [BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB] 
118 MTextPlist b     |-intvl1-|--intvl2--|--intvl3--|-intvl4-|--intvl5--|
119
120     M-text       |--------------------------------------------------|
121
122     (intvl == MInterval)
123
124 */
125
126 /* The structure MTextProperty is defined in textprop.h.  */
127
128 /** MInterval is the structure for an interval that holds text
129     properties of the same key in a specific range of M-text.
130     All intervals are stored in MIntervalPool.  */
131
132 typedef struct MInterval MInterval;
133
134 struct MInterval
135 {
136   /** Stack of pointers to text properties.  If the interval does not
137       have any text properties, this member is NULL or contains random
138       values.  */
139   MTextProperty **stack;
140
141   /** How many values are in <stack>.  */
142   int nprops;
143
144   /** Length of <stack>.  */
145   int stack_length;
146
147   /** Start and end character positions of the interval.  If <end> is
148       negative, this interval is not in use.  */
149   int start, end;
150
151   /** Pointers to the previous and next intervals.  If <start> is 0,
152       <prev> is NULL and this interval is pointed by MTextPlist->head.
153       If <end> is the size of the M-text, <next> is NULL, and this
154       interval is pointed by MTextPlist->tail.  */
155   MInterval *prev, *next;
156 };  
157
158 /** MTextPlist is a structure to hold text properties of an M-text by
159    chain.  Each element in the chain is for a specific key.  */
160
161 typedef struct MTextPlist MTextPlist;
162
163 struct MTextPlist
164 {
165   /** Key of the property.  */
166   MSymbol key;
167
168   /** The head and tail intervals.  <head>->start is always 0.
169       <tail->end is always MText->nchars.  */
170   MInterval *head, *tail;
171
172   /** Lastly accessed interval.  */
173   MInterval *cache;
174
175   /* Not yet implemented.  */
176   int (*modification_hook) (MText *mt, MSymbol key, int from, int to);
177
178   /** Pointer to the next property in the chain, or NULL if the
179       property is the last one in the chain.  */
180   MTextPlist *next;
181 };
182
183
184 /** How many intervals one interval-pool can contain. */
185
186 #define INTERVAL_POOL_SIZE 1024
187
188 typedef struct MIntervalPool MIntervalPool;
189
190
191 /** MIntervalPool is the structure for an interval-pool which store
192     intervals.  Each interval-pool contains INTERVAL_POOL_SIZE number
193     of intervals, and is chained from the root #interval_pool.  */
194
195 struct MIntervalPool
196 {
197   /** Array of intervals.  */
198   MInterval intervals[INTERVAL_POOL_SIZE];
199
200   /** The smallest index to an unused interval.  */
201   int free_slot;
202
203   /** Pointer to the next interval-pool.  */
204   MIntervalPool *next;
205 };
206
207
208 /** Root of interval-pools.  */
209
210 static MIntervalPool interval_pool_root;
211
212 /* For debugging. */
213
214 static M17NObjectArray text_property_table;
215
216 /** Return a newly allocated interval pool.  */
217
218 static MIntervalPool *
219 new_interval_pool ()
220 {
221   MIntervalPool *pool;
222   int i;
223
224   MSTRUCT_CALLOC (pool, MERROR_TEXTPROP);
225   for (i = 0; i < INTERVAL_POOL_SIZE; i++)
226     pool->intervals[i].end = -1;
227   pool->free_slot = 0;
228   pool->next = NULL;
229   return pool;
230 }
231
232
233 /** Return a new interval for the region START and END.  */
234
235 static MInterval *
236 new_interval (int start, int end)
237 {
238   MIntervalPool *pool;
239   MInterval *interval;
240
241   for (pool = &interval_pool_root;
242        pool->free_slot >= INTERVAL_POOL_SIZE;
243        pool = pool->next)
244     {
245       if (! pool->next)
246         pool->next = new_interval_pool ();
247     }
248
249   interval = &(pool->intervals[pool->free_slot]);
250   interval->stack = NULL;
251   interval->nprops = 0;
252   interval->stack_length = 0;
253   interval->prev = interval->next = NULL;
254   interval->start = start;
255   interval->end = end;
256
257   pool->free_slot++;
258   while (pool->free_slot < INTERVAL_POOL_SIZE
259          && pool->intervals[pool->free_slot].end >= 0)
260     pool->free_slot++;
261
262   return interval;
263 }
264
265
266 /** Free INTERVAL and return INTERVAL->next.  It assumes that INTERVAL
267     has no properties.  */
268
269 static MInterval *
270 free_interval (MInterval *interval)
271 {
272   MIntervalPool *pool = &interval_pool_root;
273   int i;
274
275   xassert (interval->nprops == 0);
276   if (interval->stack)
277     free (interval->stack);
278   while ((interval < pool->intervals
279           || interval >= pool->intervals + INTERVAL_POOL_SIZE)
280          && pool->next)
281     pool = pool->next;
282
283   i = interval - pool->intervals;
284   interval->end = -1;
285   if (i < pool->free_slot)
286     pool->free_slot = i;
287   return interval->next;
288 }
289
290
291 /** If necessary, allocate a stack for INTERVAL so that it can contain
292    NUM number of text properties.  */
293
294 #define PREPARE_INTERVAL_STACK(interval, num)                           \
295   do {                                                                  \
296     if ((num) > (interval)->stack_length)                               \
297       {                                                                 \
298         MTABLE_REALLOC ((interval)->stack, (num), MERROR_TEXTPROP);     \
299         (interval)->stack_length = (num);                               \
300       }                                                                 \
301   } while (0)
302
303
304 /** Return a copy of INTERVAL.  The copy still shares text properties
305     with INTERVAL.  If MASK_BITS is not zero, don't copy such text
306     properties whose control flags contains bits in MASK_BITS.  */
307
308 static MInterval *
309 copy_interval (MInterval *interval, int mask_bits)
310 {
311   MInterval *new = new_interval (interval->start, interval->end);
312   int nprops = interval->nprops;
313   MTextProperty **props = alloca (sizeof (MTextProperty *) * nprops);
314   int i, n;
315
316   for (i = n = 0; i < nprops; i++)
317     if (! (interval->stack[i]->control.flag & mask_bits))
318       props[n++] = interval->stack[i];
319   new->nprops = n;
320   if (n > 0)
321     {
322       PREPARE_INTERVAL_STACK (new, n);
323       memcpy (new->stack, props, sizeof (MTextProperty *) * n);
324     }   
325
326   return new;
327 }
328
329
330 /** Free text property OBJECT.  */
331
332 static void
333 free_text_property (void *object)
334 {
335   MTextProperty *prop = (MTextProperty *) object;
336
337   if (prop->key->managing_key)
338     M17N_OBJECT_UNREF (prop->val);
339   M17N_OBJECT_UNREGISTER (text_property_table, prop);
340   free (object);
341 }
342
343
344 /** Return a newly allocated text property whose key is KEY and value
345     is VAL.  */
346
347 static MTextProperty *
348 new_text_property (MText *mt, int from, int to, MSymbol key, void *val,
349                    int control_bits)
350 {
351   MTextProperty *prop;
352
353   M17N_OBJECT (prop, free_text_property, MERROR_TEXTPROP);
354   prop->control.flag = control_bits;
355   prop->attach_count = 0;
356   prop->mt = mt;
357   prop->start = from;
358   prop->end = to;
359   prop->key = key;
360   prop->val = val;
361   if (key->managing_key)
362     M17N_OBJECT_REF (val);    
363   M17N_OBJECT_REGISTER (text_property_table, prop);
364   return prop;
365 }
366
367
368 /** Return a newly allocated copy of text property PROP.  */
369
370 #define COPY_TEXT_PROPERTY(prop)                                \
371   new_text_property ((prop)->mt, (prop)->start, (prop)->end,    \
372                      (prop)->key, (prop)->val, (prop)->control.flag)
373
374
375 /** Split text property PROP at position INTERVAL->start, and make all
376     the following intervals contain the copy of PROP instead of PROP.
377     It assumes that PROP starts before INTERVAL.  */
378
379 static void
380 split_property (MTextProperty *prop, MInterval *interval)
381 {
382   int end = prop->end;
383   MTextProperty *copy;
384   int i;
385
386   prop->end = interval->start;
387   copy = COPY_TEXT_PROPERTY (prop);
388   copy->start = interval->start;
389   copy->end = end;
390   /* Check all stacks of the following intervals, and if it contains
391      PROP, change it to the copy of it.  */
392   for (; interval && interval->start < end; interval = interval->next)
393     for (i = 0; i < interval->nprops; i++)
394       if (interval->stack[i] == prop)
395         {
396           interval->stack[i] = copy;
397           M17N_OBJECT_REF (copy);
398           copy->attach_count++;
399           prop->attach_count--;
400           M17N_OBJECT_UNREF (prop);
401         }
402   M17N_OBJECT_UNREF (copy);
403 }
404
405 /** Divide INTERVAL of PLIST at POS if POS is in between the range of
406     INTERVAL.  */
407
408 static void
409 divide_interval (MTextPlist *plist, MInterval *interval, int pos)
410 {
411   MInterval *new;
412   int i;
413
414   if (pos == interval->start || pos == interval->end)
415     return;
416   new = copy_interval (interval, 0);
417   interval->end = new->start = pos;
418   new->prev = interval;
419   new->next = interval->next;
420   interval->next = new;
421   if (new->next)
422     new->next->prev = new;
423   if (plist->tail == interval)
424     plist->tail = new;
425   for (i = 0; i < new->nprops; i++)
426     {
427       new->stack[i]->attach_count++;
428       M17N_OBJECT_REF (new->stack[i]);
429     }
430 }
431
432
433 /** Check if INTERVAL of PLIST can be merged with INTERVAL->next.  If
434     mergeable, extend INTERVAL to the end of INTEVAL->next, free
435     INTERVAL->next, and return INTERVAL.  Otherwise, return
436     INTERVAL->next.  */
437
438 static MInterval *
439 maybe_merge_interval (MTextPlist *plist, MInterval *interval)
440 {
441   int nprops = interval->nprops;
442   MInterval *next = interval->next;
443   int i, j;
444
445   if (! next || nprops != next->nprops)
446     return next;
447
448   for (i = 0; i < nprops; i++)
449     {
450       MTextProperty *prop = interval->stack[i];
451       MTextProperty *old = next->stack[i];
452
453       if (prop != old
454           && (prop->val != old->val
455               || prop->end != old->start
456               || prop->control.flag & MTEXTPROP_NO_MERGE
457               || old->control.flag & MTEXTPROP_NO_MERGE))
458         return interval->next;
459     }
460
461
462   for (i = 0; i < nprops; i++)
463     {
464       MTextProperty *prop = interval->stack[i];
465       MTextProperty *old = next->stack[i];
466
467       if (prop != old)
468         {
469           MInterval *tail;
470
471           for (tail = next->next; tail && tail->start < old->end;
472                tail = tail->next)
473             for (j = 0; j < tail->nprops; j++)
474               if (tail->stack[j] == old)
475                 {
476                   old->attach_count--;
477                   xassert (old->attach_count);
478                   tail->stack[j] = prop;
479                   prop->attach_count++;
480                   M17N_OBJECT_REF (prop);
481                 }
482           xassert (old->attach_count == 1);
483           old->mt = NULL;
484           prop->end = old->end;
485         }
486       old->attach_count--;
487       M17N_OBJECT_UNREF (old);
488     }
489
490   interval->end = next->end;
491   interval->next = next->next;
492   if (next->next)
493     next->next->prev = interval;
494   if (plist->tail == next)
495     plist->tail = interval;
496   plist->cache = interval;
497   next->nprops = 0;
498   free_interval (next);
499   return interval;
500 }
501
502
503 /** Adjust start and end positions of intervals between HEAD and TAIL
504      (both inclusive) by diff.  Adjust also start and end positions
505      of text properties belonging to those intervals.  */
506
507 static void
508 adjust_intervals (MInterval *head, MInterval *tail, int diff)
509 {
510   int i;
511   MTextProperty *prop;
512
513   if (diff < 0)
514     {
515       /* Adjust end positions of properties starting before HEAD.  */
516       for (i = 0; i < head->nprops; i++)
517         {
518           prop = head->stack[i];
519           if (prop->start < head->start)
520             prop->end += diff;
521         }
522
523       /* Adjust start and end positions of properties starting at
524          HEAD, and adjust HEAD itself.  */
525       while (1)
526         {
527           for (i = 0; i < head->nprops; i++)
528             {
529               prop = head->stack[i];
530               if (prop->start == head->start)
531                 prop->start += diff, prop->end += diff;
532             }
533           head->start += diff;
534           head->end += diff;
535           if (head == tail)
536             break;
537           head = head->next;
538         }
539     }
540   else
541     {
542       /* Adjust start poistions of properties ending after TAIL.  */
543       for (i = 0; i < tail->nprops; i++)
544         {
545           prop = tail->stack[i];
546           if (prop->end > tail->end)
547             prop->start += diff;
548         }
549
550       /* Adjust start and end positions of properties ending at
551          TAIL, and adjust TAIL itself.  */
552       while (1)
553         {
554           for (i = 0; i < tail->nprops; i++)
555             {
556               prop = tail->stack[i];
557               if (prop->end == tail->end)
558                 prop->start += diff, prop->end += diff;
559             }
560           tail->start += diff;
561           tail->end += diff;
562           if (tail == head)
563             break;
564           tail = tail->prev;
565         }
566     }
567 }
568
569 /* Return an interval of PLIST that covers the position POS.  */
570
571 static MInterval *
572 find_interval (MTextPlist *plist, int pos)
573 {
574   MInterval *interval;
575   MInterval *highest;
576
577   if (pos < plist->head->end)
578     return plist->head;
579   if (pos >= plist->tail->start)
580     return (pos < plist->tail->end ? plist->tail : NULL);
581
582   interval = plist->cache;
583
584   if (pos < interval->start)
585     highest = interval->prev, interval = plist->head->next;
586   else if (pos < interval->end)
587     return interval;
588   else
589     highest = plist->tail->prev, interval = interval->next;
590
591   if (pos - interval->start < highest->end - pos)
592     {
593       while (interval->end <= pos)
594         /* Here, we are sure that POS is not included in PLIST->tail,
595            thus, INTERVAL->next always points a valid next
596            interval.  */
597         interval = interval->next;
598     }
599   else
600     {
601       while (highest->start > pos)
602         highest = highest->prev;
603       interval = highest;
604     }
605   plist->cache = interval;
606   return interval;
607 }
608
609 /* Push text property PROP on the stack of INTERVAL.  */
610
611 #define PUSH_PROP(interval, prop)               \
612   do {                                          \
613     int n = (interval)->nprops;                 \
614                                                 \
615     PREPARE_INTERVAL_STACK ((interval), n + 1); \
616     (interval)->stack[n] = (prop);              \
617     (interval)->nprops += 1;                    \
618     (prop)->attach_count++;                     \
619     M17N_OBJECT_REF (prop);                     \
620     if ((prop)->start > (interval)->start)      \
621       (prop)->start = (interval)->start;        \
622     if ((prop)->end < (interval)->end)          \
623       (prop)->end = (interval)->end;            \
624   } while (0)
625
626
627 /* Pop the topmost text property of INTERVAL from the stack.  If it
628    ends after INTERVAL->end, split it.  */
629
630 #define POP_PROP(interval)                              \
631   do {                                                  \
632     MTextProperty *prop;                                \
633                                                         \
634     (interval)->nprops--;                               \
635     prop = (interval)->stack[(interval)->nprops];       \
636     xassert (prop->control.ref_count > 0);              \
637     xassert (prop->attach_count > 0);                   \
638     if (prop->start < (interval)->start)                \
639       {                                                 \
640         if (prop->end > (interval)->end)                \
641           split_property (prop, (interval)->next);      \
642         prop->end = (interval)->start;                  \
643       }                                                 \
644     else if (prop->end > (interval)->end)               \
645       prop->start = (interval)->end;                    \
646     prop->attach_count--;                               \
647     if (! prop->attach_count)                           \
648       prop->mt = NULL;                                  \
649     M17N_OBJECT_UNREF (prop);                           \
650   } while (0)
651
652
653 #define REMOVE_PROP(interval, prop)                     \
654   do {                                                  \
655     int i;                                              \
656                                                         \
657     for (i = (interval)->nprops - 1; i >= 0; i--)       \
658       if ((interval)->stack[i] == (prop))               \
659         break;                                          \
660     if (i < 0)                                          \
661       break;                                            \
662     (interval)->nprops--;                               \
663     for (; i < (interval)->nprops; i++)                 \
664       (interval)->stack[i] = (interval)->stack[i + 1];  \
665     (prop)->attach_count--;                             \
666     if (! (prop)->attach_count)                         \
667       (prop)->mt = NULL;                                \
668     M17N_OBJECT_UNREF (prop);                           \
669   } while (0)
670
671
672 #ifdef TEXT_PROP_DEBUG
673 static int
674 check_plist (MTextPlist *plist, int start)
675 {
676   MInterval *interval = plist->head;
677   MInterval *cache = plist->cache;
678   int cache_found = 0;
679
680   if (interval->start != start
681       || interval->start >= interval->end)
682     return mdebug_hook ();
683   while (interval)
684     {
685       int i;
686
687       if (interval == interval->next)
688         return mdebug_hook ();
689
690       if (interval == cache)
691         cache_found = 1;
692
693       if (interval->start >= interval->end)
694         return mdebug_hook ();
695       if ((interval->next
696            ? (interval->end != interval->next->start
697               || interval != interval->next->prev)
698            : interval != plist->tail))
699         return mdebug_hook ();
700       for (i = 0; i < interval->nprops; i++)
701         {
702           if (interval->stack[i]->start > interval->start
703               || interval->stack[i]->end < interval->end)
704             return mdebug_hook ();
705
706           if (! interval->stack[i]->attach_count)
707             return mdebug_hook ();
708           if (! interval->stack[i]->mt)
709             return mdebug_hook ();
710           if (interval->stack[i]->start == interval->start)
711             {
712               MTextProperty *prop = interval->stack[i];
713               int count = prop->attach_count - 1;
714               MInterval *interval2;
715
716               for (interval2 = interval->next;
717                    interval2 && interval2->start < prop->end;
718                    count--, interval2 = interval2->next)
719                 if (count == 0)
720                   return mdebug_hook ();
721             }         
722
723           if (interval->stack[i]->end > interval->end)
724             {
725               MTextProperty *prop = interval->stack[i];
726               MInterval *interval2;
727               int j;
728
729               for (interval2 = interval->next;
730                    interval2 && interval2->start < prop->end;
731                    interval2 = interval2->next)
732                 {
733                   for (j = 0; j < interval2->nprops; j++)
734                     if (interval2->stack[j] == prop)
735                       break;
736                   if (j == interval2->nprops)
737                     return mdebug_hook ();
738                 }
739             }
740           if (interval->stack[i]->start < interval->start)
741             {
742               MTextProperty *prop = interval->stack[i];
743               MInterval *interval2;
744               int j;
745
746               for (interval2 = interval->prev;
747                    interval2 && interval2->end > prop->start;
748                    interval2 = interval2->prev)
749                 {
750                   for (j = 0; j < interval2->nprops; j++)
751                     if (interval2->stack[j] == prop)
752                       break;
753                   if (j == interval2->nprops)
754                     return mdebug_hook ();
755                 }
756             }
757         }
758       interval = interval->next;
759     }
760   if (! cache_found)
761     return mdebug_hook ();
762   if (plist->head->prev || plist->tail->next)
763     return mdebug_hook ();    
764   return 0;
765 }
766 #endif
767
768
769 /** Return a copy of plist that contains intervals between FROM and TO
770     of PLIST.  The copy goes to the position POS of M-text MT.  */
771
772 static MTextPlist *
773 copy_single_property (MTextPlist *plist, int from, int to, MText *mt, int pos)
774 {
775   MTextPlist *new;
776   MInterval *interval1, *interval2;
777   MTextProperty *prop;
778   int diff = pos - from;
779   int i, j;
780   int mask_bits = MTEXTPROP_VOLATILE_STRONG | MTEXTPROP_VOLATILE_WEAK;
781
782   MSTRUCT_CALLOC (new, MERROR_TEXTPROP);
783   new->key = plist->key;
784   new->next = NULL;
785
786   interval1 = find_interval (plist, from);
787   new->head = copy_interval (interval1, mask_bits);
788   for (interval1 = interval1->next, interval2 = new->head;
789        interval1 && interval1->start < to;
790        interval1 = interval1->next, interval2 = interval2->next)
791     {
792       interval2->next = copy_interval (interval1, mask_bits);
793       interval2->next->prev = interval2;
794     }
795   new->tail = interval2;
796   new->head->start = from;
797   new->tail->end = to;
798   for (interval1 = new->head; interval1; interval1 = interval1->next)
799     for (i = 0; i < interval1->nprops; i++)
800       if (interval1->start == interval1->stack[i]->start
801           || interval1 == new->head)
802         {
803           prop = interval1->stack[i];
804           interval1->stack[i] = COPY_TEXT_PROPERTY (prop);
805           interval1->stack[i]->mt = mt;
806           interval1->stack[i]->attach_count++;
807           if (interval1->stack[i]->start < from)
808             interval1->stack[i]->start = from;
809           if (interval1->stack[i]->end > to)
810             interval1->stack[i]->end = to;
811           for (interval2 = interval1->next; interval2;
812                interval2 = interval2->next)
813             for (j = 0; j < interval2->nprops; j++)
814               if (interval2->stack[j] == prop)
815                 {
816                   interval2->stack[j] = interval1->stack[i];
817                   interval1->stack[i]->attach_count++;
818                   M17N_OBJECT_REF (interval1->stack[i]);
819                 }
820         }
821   adjust_intervals (new->head, new->tail, diff);
822   new->cache = new->head;
823   for (interval1 = new->head; interval1 && interval1->next;
824        interval1 = maybe_merge_interval (new, interval1));
825   xassert (check_plist (new, pos) == 0);
826   if (new->head == new->tail
827       && new->head->nprops == 0)
828     {
829       free_interval (new->head);
830       free (new);
831       new = NULL;
832     }
833
834   return new;
835 }
836
837 /** Return a newly allocated plist whose key is KEY on M-text MT.  */
838
839 static MTextPlist *
840 new_plist (MText *mt, MSymbol key)
841 {
842   MTextPlist *plist;
843
844   MSTRUCT_MALLOC (plist, MERROR_TEXTPROP);
845   plist->key = key;
846   plist->head = new_interval (0, mtext_nchars (mt));
847   plist->tail = plist->head;
848   plist->cache = plist->head;
849   plist->next = mt->plist;
850   mt->plist = plist;
851   return plist;
852 }
853
854 /* Free PLIST and return PLIST->next.  */
855
856 static MTextPlist *
857 free_textplist (MTextPlist *plist)
858 {
859   MTextPlist *next = plist->next;
860   MInterval *interval = plist->head;
861
862   while (interval)
863     {
864       while (interval->nprops > 0)
865         POP_PROP (interval);
866       interval = free_interval (interval);
867     }
868   free (plist);
869   return next;
870 }
871
872 /** Return a plist that contains the property KEY of M-text MT.  If
873     such a plist does not exist and CREATE is nonzero, create a new
874     plist and return it.  */
875
876 static MTextPlist *
877 get_plist_create (MText *mt, MSymbol key, int create)
878 {
879   MTextPlist *plist;
880
881   plist = mt->plist;
882   while (plist && plist->key != key)
883     plist = plist->next;
884
885   /* If MT does not have PROP, make one.  */
886   if (! plist && create)
887     plist = new_plist (mt, key);
888   return plist;
889 }
890
891 /* Detach PROP.  INTERVAL (if not NULL) contains PROP.  */
892
893 static void
894 detach_property (MTextPlist *plist, MTextProperty *prop, MInterval *interval)
895 {
896   MInterval *head;
897   int to = prop->end;
898
899   xassert (prop->mt);
900   xassert (plist);
901
902   M17N_OBJECT_REF (prop);
903   if (interval)
904     while (interval->start > prop->start)
905       interval = interval->prev;
906   else
907     interval = find_interval (plist, prop->start);
908   head = interval;
909   while (1)
910     {
911       REMOVE_PROP (interval, prop);
912       if (interval->end == to)
913         break;
914       interval = interval->next;
915     }
916   xassert (prop->attach_count == 0 && prop->mt == NULL);
917   M17N_OBJECT_UNREF (prop);
918
919   while (head && head->end <= to)
920     head = maybe_merge_interval (plist, head);
921   xassert (check_plist (plist, 0) == 0);
922 }
923
924 /* Delete text properties of PLIST between FROM and TO.  MASK_BITS
925    specifies what kind of properties to delete.  If DELETING is
926    nonzero, delete such properties too that are completely included in
927    the region.
928
929    If the resulting PLIST still has any text properties, return 1,
930    else return 0.  */
931
932 static int
933 delete_properties (MTextPlist *plist, int from, int to,
934                    int mask_bits, int deleting)
935 {
936   MInterval *interval;
937   int modified = 0;
938   int modified_from = from;
939   int modified_to = to;
940   int i;
941
942  retry:
943   for (interval = find_interval (plist, from);
944        interval && interval->start < to;
945        interval = interval->next)
946     for (i = 0; i < interval->nprops; i++)
947       {
948         MTextProperty *prop = interval->stack[i];
949
950         if (prop->control.flag & mask_bits)
951           {
952             if (prop->start < modified_from)
953               modified_from = prop->start;
954             if (prop->end > modified_to)
955               modified_to = prop->end;
956             detach_property (plist, prop, interval);
957             modified++;
958             goto retry;
959           }
960         else if (deleting && prop->start >= from && prop->end <= to)
961           {
962             detach_property (plist, prop, interval);
963             modified++;
964             goto retry;
965           }
966       }
967
968   if (modified)
969     {
970       interval = find_interval (plist, modified_from);
971       while (interval && interval->start < modified_to)
972         interval = maybe_merge_interval (plist, interval);
973     }
974
975   return (plist->head != plist->tail || plist->head->nprops > 0);
976 }
977
978 static void
979 pop_interval_properties (MInterval *interval)
980 {
981   while (interval->nprops > 0)
982     POP_PROP (interval);
983 }
984
985
986 MInterval *
987 pop_all_properties (MTextPlist *plist, int from, int to)
988 {
989   MInterval *interval;
990
991   /* Be sure to have interval boundary at TO.  */
992   interval = find_interval (plist, to);
993   if (interval && interval->start < to)
994     divide_interval (plist, interval, to);
995
996   /* Be sure to have interval boundary at FROM.  */
997   interval = find_interval (plist, from);
998   if (interval->start < from)
999     {
1000       divide_interval (plist, interval, from);
1001       interval = interval->next;
1002     }
1003
1004   pop_interval_properties (interval);
1005   while (interval->end < to)
1006     {
1007       MInterval *next = interval->next;
1008
1009       pop_interval_properties (next);
1010       interval->end = next->end;
1011       interval->next = next->next;
1012       if (interval->next)
1013         interval->next->prev = interval;
1014       if (next == plist->tail)
1015         plist->tail = interval;
1016       if (plist->cache == next)
1017         plist->cache = interval;
1018       free_interval (next);
1019     }
1020   return interval;
1021 }
1022
1023
1024 /* Delete volatile text properties between FROM and TO.  If DELETING
1025    is nonzero, we are going to delete text, thus both strongly and
1026    weakly volatile properties must be deleted.  Otherwise we are going
1027    to modify a text property KEY, thus only strongly volatile
1028    properties whose key is not KEY must be deleted.  */
1029
1030 static void
1031 prepare_to_modify (MText *mt, int from, int to, MSymbol key, int deleting)
1032 {
1033   MTextPlist *plist = mt->plist, *prev = NULL;
1034   int mask_bits = MTEXTPROP_VOLATILE_STRONG;
1035
1036   if (deleting)
1037     mask_bits |= MTEXTPROP_VOLATILE_WEAK;
1038   while (plist)
1039     {
1040       if (plist->key != key
1041           && ! delete_properties (plist, from, to, mask_bits, deleting))
1042         {
1043           if (prev)
1044             plist = prev->next = free_textplist (plist);
1045           else
1046             plist = mt->plist = free_textplist (plist);
1047         }
1048       else
1049         prev = plist, plist = plist->next;
1050     }
1051 }
1052
1053 void
1054 extract_text_properties (MText *mt, int from, int to, MSymbol key,
1055                          MPlist *plist)
1056 {
1057   MPlist *top;
1058   MTextPlist *list = get_plist_create (mt, key, 0);
1059   MInterval *interval;
1060
1061   if (! list)
1062     return;
1063   interval = find_interval (list, from);
1064   if (interval->nprops == 0
1065       && interval->start <= from && interval->end >= to)
1066     return;
1067   top = plist;
1068   while (interval && interval->start < to)
1069     {
1070       if (interval->nprops == 0)
1071         top = mplist_find_by_key (top, Mnil);
1072       else
1073         {
1074           MPlist *current = top, *place;
1075           int i;
1076
1077           for (i = 0; i < interval->nprops; i++)
1078             {
1079               MTextProperty *prop = interval->stack[i];
1080
1081               place = mplist_find_by_value (current, prop);
1082               if (place)
1083                 current = MPLIST_NEXT (place);
1084               else
1085                 {
1086                   place = mplist_find_by_value (top, prop);
1087                   if (place)
1088                     {
1089                       mplist_pop (place);
1090                       if (MPLIST_NEXT (place) == MPLIST_NEXT (current))
1091                         current = place;
1092                     }
1093                   mplist_push (current, Mt, prop);
1094                   current = MPLIST_NEXT (current);
1095                 }
1096             }
1097         }
1098       interval = interval->next;
1099     }
1100   return;
1101 }
1102
1103 #define XML_TEMPLATE "<?xml version=\"1.0\" ?>\n\
1104 <!DOCTYPE mtext [\n\
1105   <!ELEMENT mtext (property*,body+)>\n\
1106   <!ELEMENT property EMPTY>\n\
1107   <!ELEMENT body (#PCDATA)>\n\
1108   <!ATTLIST property key CDATA #REQUIRED>\n\
1109   <!ATTLIST property value CDATA #REQUIRED>\n\
1110   <!ATTLIST property from CDATA #REQUIRED>\n\
1111   <!ATTLIST property to CDATA #REQUIRED>\n\
1112   <!ATTLIST property control CDATA #REQUIRED>\n\
1113  ]>\n\
1114 <mtext>\n\
1115 </mtext>"
1116
1117 \f
1118 /* for debugging... */
1119 #include <stdio.h>
1120
1121 void
1122 dump_interval (MInterval *interval, int indent)
1123 {
1124   char *prefix = (char *) alloca (indent + 1);
1125   int i;
1126
1127   memset (prefix, 32, indent);
1128   prefix[indent] = 0;
1129
1130   fprintf (stderr, "(interval %d-%d (%d)", interval->start, interval->end,
1131            interval->nprops);
1132   for (i = 0; i < interval->nprops; i++)
1133     fprintf (stderr, "\n%s (%d %d/%d %d-%d 0x%x)",
1134              prefix, i,
1135              interval->stack[i]->control.ref_count,
1136              interval->stack[i]->attach_count,
1137              interval->stack[i]->start, interval->stack[i]->end,
1138              (unsigned) interval->stack[i]->val);
1139   fprintf (stderr, ")");
1140 }
1141
1142 void
1143 dump_textplist (MTextPlist *plist, int indent)
1144 {
1145   char *prefix = (char *) alloca (indent + 1);
1146
1147   memset (prefix, 32, indent);
1148   prefix[indent] = 0;
1149
1150   fprintf (stderr, "(properties");
1151   if (! plist)
1152     fprintf (stderr, ")\n");
1153   else
1154     {
1155       fprintf (stderr, "\n");
1156       while (plist)
1157         {
1158           MInterval *interval = plist->head;
1159
1160           fprintf (stderr, "%s (%s", prefix, msymbol_name (plist->key));
1161           while (interval)
1162             {
1163               fprintf (stderr, " (%d %d", interval->start, interval->end);
1164               if (interval->nprops > 0)
1165                 {
1166                   int i;
1167
1168                   for (i = 0; i < interval->nprops; i++)
1169                     fprintf (stderr, " 0x%x", (int) interval->stack[i]->val);
1170                 }
1171               fprintf (stderr, ")");
1172               interval = interval->next;
1173             }
1174           fprintf (stderr, ")\n");
1175           xassert (check_plist (plist, 0) == 0);
1176           plist = plist->next;
1177         }
1178     }
1179 }
1180
1181 \f
1182 /* Internal API */
1183
1184 int
1185 mtext__prop_init ()
1186 {
1187   M17N_OBJECT_ADD_ARRAY (text_property_table, "Text property");
1188   Mtext_prop_serializer = msymbol ("text-prop-serializer");
1189   Mtext_prop_deserializer = msymbol ("text-prop-deserializer");
1190   return 0;
1191 }
1192
1193 void
1194 mtext__prop_fini ()
1195 {
1196   MIntervalPool *pool = interval_pool_root.next;
1197
1198   while (pool)
1199     {
1200       MIntervalPool *next = pool->next;
1201       free (pool);
1202       pool = next;
1203     }
1204   interval_pool_root.next = NULL;  
1205 }
1206
1207
1208 /** Free all plists.  */
1209
1210 void
1211 mtext__free_plist (MText *mt){
1212
1213   MTextPlist *plist = mt->plist;
1214
1215   while (plist)
1216     plist = free_textplist (plist);
1217   mt->plist = NULL;
1218 }
1219
1220
1221 /** Extract intervals between FROM and TO of all properties (except
1222     for volatile ones) in PLIST, and make a new plist from them for
1223     M-text MT.  */
1224
1225 MTextPlist *
1226 mtext__copy_plist (MTextPlist *plist, int from, int to, MText *mt, int pos)
1227 {
1228   MTextPlist *copy, *this;
1229
1230   if (from == to)
1231     return NULL;
1232   for (copy = NULL; plist && ! copy; plist = plist->next)
1233     copy = copy_single_property (plist, from, to, mt, pos);
1234   if (! plist)
1235     return copy;
1236   for (; plist; plist = plist->next)
1237     if ((this = copy_single_property (plist, from, to, mt, pos)))
1238       {
1239         this->next = copy;
1240         copy = this;
1241       }
1242
1243   return copy;
1244 }
1245
1246 void
1247 mtext__adjust_plist_for_delete (MText *mt, int pos, int len)
1248 {
1249   MTextPlist *plist;
1250   int to;
1251
1252   if (len == 0 || pos == mt->nchars)
1253     return;
1254   if (len == mt->nchars)
1255     {
1256       mtext__free_plist (mt);
1257       return;
1258     }      
1259
1260   to = pos + len;
1261   prepare_to_modify (mt, pos, to, Mnil, 1);
1262   for (plist = mt->plist; plist; plist = plist->next)
1263     {
1264       MInterval *interval = pop_all_properties (plist, pos, to);
1265       MInterval *prev = interval->prev, *next = interval->next;
1266
1267       if (prev)
1268         prev->next = next;
1269       else
1270         plist->head = next;
1271       if (next)
1272         {
1273           adjust_intervals (next, plist->tail, -len);
1274           next->prev = prev;
1275         }
1276       else
1277         plist->tail = prev;
1278       if (prev && next)
1279         next = maybe_merge_interval (plist, prev);
1280       plist->cache = next ? next : prev;
1281       free_interval (interval);
1282       xassert (check_plist (plist, 0) == 0);
1283     }
1284 }
1285
1286 void
1287 mtext__adjust_plist_for_insert (MText *mt, int pos, int nchars,
1288                                 MTextPlist *plist)
1289 {
1290   MTextPlist *pl, *pl_last, *pl2, *p;
1291   int i;
1292   MInterval *interval;
1293
1294   if (mt->nchars == 0)
1295     {
1296       mtext__free_plist (mt);
1297       mt->plist = plist;
1298       return;
1299     }
1300   if (pos > 0 && pos < mtext_nchars (mt))
1301     prepare_to_modify (mt, pos, pos, Mnil, 0);
1302
1303   for (pl_last = NULL, pl = mt->plist; pl; pl_last = pl, pl = pl->next)
1304     {
1305       MInterval *interval, *prev, *next, *head, *tail;
1306
1307       if (pos == 0)
1308         prev = NULL, next = pl->head;
1309       else if (pos == mtext_nchars (mt))
1310         prev = pl->tail, next = NULL;
1311       else
1312         {
1313           next = find_interval (pl, pos);
1314           if (next->start < pos)
1315             {
1316               divide_interval (pl, next, pos);
1317               next = next->next;
1318             }
1319           for (i = 0; i < next->nprops; i++)
1320             if (next->stack[i]->start < pos)
1321               split_property (next->stack[i], next);
1322           prev = next->prev;
1323         }
1324
1325       xassert (check_plist (pl, 0) == 0);
1326       for (p = NULL, pl2 = plist; pl2 && pl->key != pl2->key;
1327            p = pl2, pl2 = p->next);
1328       if (pl2)
1329         {
1330           xassert (check_plist (pl2, pl2->head->start) == 0);
1331           if (p)
1332             p->next = pl2->next;
1333           else
1334             plist = plist->next;
1335
1336           head = pl2->head;
1337           tail = pl2->tail;
1338           free (pl2);
1339         }
1340       else
1341         {
1342           head = tail = new_interval (pos, pos + nchars);
1343         }
1344       head->prev = prev;
1345       tail->next = next;
1346       if (prev)
1347         prev->next = head;
1348       else
1349         pl->head = head;
1350       if (next)
1351         next->prev = tail;
1352       else
1353         pl->tail = tail;
1354       if (next)
1355         adjust_intervals (next, pl->tail, nchars);
1356
1357       xassert (check_plist (pl, 0) == 0);
1358       if (prev && prev->nprops > 0)
1359         {
1360           for (interval = prev;
1361                interval->next != next && interval->next->nprops == 0;
1362                interval = interval->next)
1363             for (i = 0; i < interval->nprops; i++)
1364               {
1365                 MTextProperty *prop = interval->stack[i];
1366
1367                 if (prop->control.flag & MTEXTPROP_REAR_STICKY)
1368                   PUSH_PROP (interval->next, prop);
1369               }
1370         }
1371       xassert (check_plist (pl, 0) == 0);
1372       if (next && next->nprops > 0)
1373         {
1374           for (interval = next;
1375                interval->prev != prev && interval->prev->nprops == 0;
1376                interval = interval->prev)
1377             for (i = 0; i < interval->nprops; i++)
1378               {
1379                 MTextProperty *prop = interval->stack[i];
1380
1381                 if (prop->control.flag & MTEXTPROP_FRONT_STICKY)
1382                   PUSH_PROP (interval->prev, prop);
1383               }
1384         }
1385
1386       interval = prev ? prev : pl->head;
1387       pl->cache = interval;
1388       while (interval && interval->start <= pos + nchars)
1389         interval = maybe_merge_interval (pl, interval);
1390       xassert (check_plist (pl, 0) == 0);
1391     }
1392
1393   if (pl_last)
1394     pl_last->next = plist;
1395   else
1396     mt->plist = plist;
1397
1398   for (; plist; plist = plist->next)
1399     {
1400       plist->cache = plist->head;
1401       if (pos > 0)
1402         {
1403           if (plist->head->nprops)
1404             {
1405               interval = new_interval (0, pos);
1406               interval->next = plist->head;
1407               plist->head->prev = interval;
1408               plist->head = interval;
1409             }
1410           else
1411             plist->head->start = 0;
1412         }
1413       if (pos < mtext_nchars (mt))
1414         {
1415           if (plist->tail->nprops)
1416             {
1417               interval = new_interval (pos + nchars,
1418                                        mtext_nchars (mt) + nchars);
1419               interval->prev = plist->tail;
1420               plist->tail->next = interval;
1421               plist->tail = interval;
1422             }
1423           else
1424             plist->tail->end = mtext_nchars (mt) + nchars;
1425         }
1426       xassert (check_plist (plist, 0) == 0);
1427     }
1428 }
1429
1430 /* len1 > 0 && len2 > 0 */
1431
1432 void
1433 mtext__adjust_plist_for_change (MText *mt, int pos, int len1, int len2)
1434 {
1435   int pos2 = pos + len1;
1436
1437   prepare_to_modify (mt, pos, pos2, Mnil, 0);
1438
1439   if (len1 < len2)
1440     {
1441       int diff = len2 - len1;
1442       MTextPlist *plist;
1443
1444       for (plist = mt->plist; plist; plist = plist->next)
1445         {
1446           MInterval *head = find_interval (plist, pos2);
1447           MInterval *tail = plist->tail;
1448           MTextProperty *prop;
1449           int i;
1450
1451           if (head)
1452             {
1453               if (head->start == pos2)
1454                 head = head->prev;
1455               while (tail != head)
1456                 {
1457                   for (i = 0; i < tail->nprops; i++)
1458                     {
1459                       prop = tail->stack[i];
1460                       if (prop->start == tail->start)
1461                         prop->start += diff, prop->end += diff;
1462                     }
1463                   tail->start += diff;
1464                   tail->end += diff;
1465                   tail = tail->prev;
1466                 }
1467             }
1468           for (i = 0; i < tail->nprops; i++)
1469             tail->stack[i]->end += diff;
1470           tail->end += diff;
1471         }
1472     }
1473   else if (len1 > len2)
1474     {
1475       mtext__adjust_plist_for_delete (mt, pos + len2, len1 - len2);
1476     }
1477 }
1478
1479
1480 /*** @} */
1481 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1482
1483 \f
1484 /** External API */
1485
1486 /*** @addtogroup m17nTextProperty */
1487 /*** @{  */
1488
1489 /*=*/
1490 /***en
1491     @brief Get the value of the topmost text property.
1492
1493     The mtext_get_prop () function searches the character at $POS in
1494     M-text $MT for the text property whose key is $KEY.
1495
1496     @return
1497     If a text property is found, mtext_get_prop () returns the value
1498     of the property.  If the property has multiple values, it returns
1499     the topmost one.  If no such property is found, it returns @c NULL
1500     without changing the external variable #merror_code.
1501
1502     If an error is detected, mtext_get_prop () returns @c NULL and
1503     assigns an error code to the external variable #merror_code.
1504
1505     @note If @c NULL is returned without an error, there are two
1506     possibilities:
1507
1508     @li  the character at $POS does not have a property whose key is $KEY, or 
1509
1510     @li  the character does have such a property and its value is @c NULL.  
1511
1512     If you need to distinguish these two cases, use the
1513     mtext_get_prop_values () function instead.  */
1514
1515 /***ja
1516     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î°ìÈÖ¾å¤ÎÃͤòÆÀ¤ë.
1517
1518     ´Ø¿ô mtext_get_prop () ¤Ï¡¢M-text $MT Æâ¤Î°ÌÃÖ $POS ¤Ë¤¢¤ëʸ»ú¤Î¥Æ
1519     ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£
1520
1521     @return
1522     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬¤ß¤Ä¤«¤ì¤Ð¡¢mtext_get_prop () ¤Ï¤½¤Î¥×¥í¥Ñ¥Æ¥£
1523     ¤ÎÃͤòÊÖ¤¹¡£Ãͤ¬Ê£¿ô¸ºß¤¹¤ë¤È¤­¤Ï¡¢°ìÈÖ¾å¤ÎÃͤòÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±
1524     ¤ì¤Ð³°ÉôÊÑ¿ô #merror_code ¤òÊѹ¹¤¹¤ë¤³¤È¤Ê¤¯ @c NULL ¤òÊÖ¤¹¡£
1525
1526     ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç mtext_get_prop () ¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ
1527     ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1528
1529     @note ¥¨¥é¡¼¤Ê¤·¤Ç @c NULL ¤¬ÊÖ¤µ¤ì¤¿¾ì¹ç¤Ë¤ÏÆó¤Ä¤Î²ÄǽÀ­¤¬¤¢¤ë¡£
1530
1531     @li $POS ¤Î°ÌÃÖ¤Îʸ»ú¤Ï $KEY ¤ò¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¡£
1532
1533     @li ¤½¤Îʸ»ú¤Ï¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Á¡¢¤½¤ÎÃͤ¬ @c NULL ¤Ç¤¢¤ë¡£
1534
1535     ¤³¤ÎÆó¤Ä¤ò¶èÊ̤¹¤ëɬÍפ¬¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢´Ø¿ô mtext_get_prop_values ()
1536     ¤òÂå¤ï¤ê¤Ë»ÈÍѤ¹¤ë¤³¤È¡£
1537
1538      @latexonly \IPAlabel{mtext_get_prop} @endlatexonly  */
1539
1540 /***
1541     @errors
1542     @c MERROR_RANGE, @c MERROR_SYMBOL
1543
1544     @seealso
1545     mtext_get_prop_values (), mtext_put_prop (), mtext_put_prop_values (),
1546     mtext_push_prop (), mtext_pop_prop (), mtext_prop_range ()  */
1547
1548 void *
1549 mtext_get_prop (MText *mt, int pos, MSymbol key)
1550 {
1551   MTextPlist *plist;
1552   MInterval *interval;
1553   void *val;
1554
1555   M_CHECK_POS (mt, pos, NULL);
1556
1557   plist = get_plist_create (mt, key, 0);
1558   if (! plist)
1559     return NULL;
1560
1561   interval = find_interval (plist, pos);
1562   val = (interval->nprops
1563          ? interval->stack[interval->nprops - 1]->val : NULL);
1564   return val;
1565 }
1566
1567 /*=*/
1568
1569 /***en
1570     @brief Get multiple values of a text property.
1571
1572     The mtext_get_prop_values () function searches the character at
1573     $POS in M-text $MT for the property whose key is $KEY.  If such
1574     a property is found, its values are stored in the memory area
1575     pointed to by $VALUES.  $NUM limits the maximum number of stored
1576     values.
1577
1578     @return
1579     If the operation was successful, mtext_get_prop_values () returns
1580     the number of actually stored values.  If the character at $POS
1581     does not have a property whose key is $KEY, the return value is
1582     0. If an error is detected, mtext_get_prop_values () returns -1 and
1583     assigns an error code to the external variable #merror_code.  */
1584
1585 /***ja
1586     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊ£¿ô¸ÄÆÀ¤ë.
1587
1588     ´Ø¿ô mtext_get_prop_values () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤È¤¤¤¦°ÌÃÖ
1589     ¤Ë¤¢¤ëʸ»ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£¤â¤·¤½
1590     ¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬¸«¤Ä¤«¤ì¤Ð¡¢¤½¤ì¤¬»ý¤ÄÃÍ (Ê£¿ô²Ä) ¤ò $VALUES 
1591     ¤Î»Ø¤¹¥á¥â¥êÎΰè¤Ë³ÊǼ¤¹¤ë¡£$NUM ¤Ï³ÊǼ¤¹¤ëÃͤοô¤Î¾å¸Â¤Ç¤¢¤ë¡£
1592
1593     @return
1594     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_get_prop_values () ¤Ï¼ÂºÝ¤Ë¥á¥â¥ê¤Ë³ÊǼ¤µ
1595     ¤ì¤¿Ãͤοô¤òÊÖ¤¹¡£$POS ¤Î°ÌÃÖ¤Îʸ»ú¤¬ $KEY ¤ò¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£
1596     ¤ò»ý¤¿¤Ê¤±¤ì¤Ð 0 ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°Éô
1597     ÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1598
1599     @latexonly \IPAlabel{mtext_get_prop_values} @endlatexonly  */
1600
1601 /***
1602     @errors
1603     @c MERROR_RANGE, @c MERROR_SYMBOL
1604
1605     @seealso
1606     mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (),
1607     mtext_push_prop (), mtext_pop_prop (), mtext_prop_range ()  */
1608
1609 int
1610 mtext_get_prop_values (MText *mt, int pos, MSymbol key,
1611                        void **values, int num)
1612 {
1613   MTextPlist *plist;
1614   MInterval *interval;
1615   int nprops;
1616   int i;
1617   int offset;
1618
1619   M_CHECK_POS (mt, pos, -1);
1620
1621   plist = get_plist_create (mt, key, 0);
1622   if (! plist)
1623     return 0;
1624
1625   interval = find_interval (plist, pos);
1626   /* It is assured that INTERVAL is not NULL.  */
1627   nprops = interval->nprops;
1628   if (nprops == 0 || num <= 0)
1629     return 0;
1630   if (nprops == 1 || num == 1)
1631     {
1632       values[0] = interval->stack[nprops - 1]->val;
1633       return 1;
1634     }
1635
1636   if (nprops <= num)
1637     num = nprops, offset = 0;
1638   else
1639     offset = nprops - num;
1640   for (i = 0; i < num; i++)
1641     values[i] = interval->stack[offset + i]->val;
1642   return num;
1643 }
1644
1645 /*=*/
1646
1647 /***en
1648     @brief Get a list of text property keys at a position of an M-text.
1649
1650     The mtext_get_prop_keys () function creates an array whose
1651     elements are the keys of text properties found at position $POS in
1652     M-text $MT, and sets *$KEYS to the address of the created array.
1653     The user is responsible to free the memory allocated for
1654     the array.
1655
1656     @returns
1657     If the operation was successful, mtext_get_prop_keys () returns
1658     the length of the key list.  Otherwise it returns -1 and assigns
1659     an error code to the external variable #merror_code.
1660
1661 */
1662
1663 /***ja
1664     @brief M-text ¤Î»ØÄꤷ¤¿°ÌÃ֤Υƥ­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤Î¥ê¥¹¥È¤òÆÀ¤ë.
1665
1666     ´Ø¿ô mtext_get_prop_keys () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤Î°ÌÃ֤ˤ¢¤ë
1667     ¤¹¤Ù¤Æ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤òÍ×ÁǤȤ¹¤ëÇÛÎó¤òºî¤ê¡¢¤½¤ÎÇÛÎó¤Î
1668     ¥¢¥É¥ì¥¹¤ò *$KEYS ¤ËÀßÄꤹ¤ë¡£¤³¤ÎÇÛÎó¤Î¤¿¤á¤Ë³ÎÊݤµ¤ì¤¿¥á¥â¥ê¤ò²ò
1669     Êü¤¹¤ë¤Î¤Ï¥æ¡¼¥¶¤ÎÀÕǤ¤Ç¤¢¤ë¡£
1670
1671     @return
1672     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_get_prop_keys () ¤ÏÆÀ¤é¤ì¤¿¥ê¥¹¥È¤ÎŤµ¤òÊÖ
1673     ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò
1674     ÀßÄꤹ¤ë¡£
1675 */
1676
1677 /***
1678     @errors
1679     @c MERROR_RANGE
1680
1681     @seealso
1682     mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (),
1683     mtext_get_prop_values (), mtext_push_prop (), mtext_pop_prop ()  */
1684
1685 int
1686 mtext_get_prop_keys (MText *mt, int pos, MSymbol **keys)
1687 {
1688   MTextPlist *plist;
1689   int i;
1690
1691   M_CHECK_POS (mt, pos, -1);
1692   for (i = 0, plist = mt->plist; plist; i++, plist = plist->next);
1693   if (i == 0)
1694     {
1695       *keys = NULL;
1696       return 0;
1697     }
1698   MTABLE_MALLOC (*keys, i, MERROR_TEXTPROP);
1699   for (i = 0, plist = mt->plist; plist; plist = plist->next)
1700     {
1701       MInterval *interval = find_interval (plist, pos);
1702
1703       if (interval->nprops)
1704         (*keys)[i++] = plist->key;
1705     }
1706   return i;
1707 }
1708
1709 /*=*/
1710
1711 /***en
1712     @brief Set a text property.
1713
1714     The mtext_put_prop () function sets a text property to the
1715     characters between $FROM (inclusive) and $TO (exclusive) in M-text
1716     $MT.  $KEY and $VAL specify the key and the value of the text
1717     property.  With this function,
1718
1719 @verbatim
1720                      FROM                   TO
1721 M-text: |<------------|-------- MT ---------|------------>|
1722 PROP  :  <------------------ OLD_VAL -------------------->
1723 @endverbatim
1724
1725    becomes
1726
1727 @verbatim
1728                      FROM                   TO
1729 M-text: |<------------|-------- MT ---------|------------>|
1730 PROP  :  <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL-->
1731 @endverbatim
1732
1733     @return
1734     If the operation was successful, mtext_put_prop () returns 0.
1735     Otherwise it returns -1 and assigns an error code to the external
1736     variable #merror_code.  */
1737
1738 /***ja
1739     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ¤ë.
1740
1741     ´Ø¿ô mtext_put_prop () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é 
1742     $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥­¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¤è
1743     ¤¦¤Ê¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ¤ë¡£¤³¤Î´Ø¿ô¤Ë¤è¤Ã¤Æ
1744
1745
1746 @verbatim
1747                          FROM                    TO
1748 M-text:      |<------------|-------- MT ---------|------------>|
1749 PROP:         <------------------ OLD_VAL -------------------->
1750 @endverbatim
1751
1752 ¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¡£
1753
1754 @verbatim
1755                          FROM                    TO
1756 M-text:       |<------------|-------- MT ---------|------------>|
1757 PROP:          <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL-->
1758 @endverbatim
1759
1760     @return
1761     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_put_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 
1762     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1763
1764     @latexonly \IPAlabel{mtext_put_prop} @endlatexonly  */
1765
1766 /***
1767     @errors
1768     @c MERROR_RANGE, @c MERROR_SYMBOL
1769
1770     @seealso
1771     mtext_put_prop_values (), mtext_get_prop (),
1772     mtext_get_prop_values (), mtext_push_prop (),
1773     mtext_pop_prop (), mtext_prop_range ()  */
1774
1775 int
1776 mtext_put_prop (MText *mt, int from, int to, MSymbol key, void *val)
1777 {
1778   MTextPlist *plist;
1779   MTextProperty *prop;
1780   MInterval *interval;
1781
1782   M_CHECK_RANGE (mt, from, to, -1, 0);
1783
1784   prepare_to_modify (mt, from, to, key, 0);
1785   plist = get_plist_create (mt, key, 1);
1786   interval = pop_all_properties (plist, from, to);
1787   prop = new_text_property (mt, from, to, key, val, 0);
1788   PUSH_PROP (interval, prop);
1789   M17N_OBJECT_UNREF (prop);
1790   if (interval->next)
1791     maybe_merge_interval (plist, interval);
1792   if (interval->prev)
1793     maybe_merge_interval (plist, interval->prev);
1794   xassert (check_plist (plist, 0) == 0);
1795   return 0;
1796 }
1797
1798 /*=*/
1799
1800 /***en
1801     @brief Set multiple text properties with the same key.
1802
1803     The mtext_put_prop_values () function sets a text property to the
1804     characters between $FROM (inclusive) and $TO (exclusive) in M-text
1805     $MT.  $KEY and $VALUES specify the key and the values of the text
1806     property.  $NUM specifies the number of property values to be set.
1807
1808     @return
1809     If the operation was successful, mtext_put_prop_values () returns
1810     0.  Otherwise it returns -1 and assigns an error code to the
1811     external variable #merror_code.  */
1812
1813 /***ja
1814     @brief Æ±¤¸¥­¡¼¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÊ£¿ôÀßÄꤹ¤ë.
1815
1816     ´Ø¿ô mtext_put_prop_values () ¤Ï¡¢M-Text $MT ¤Î$FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë
1817     ¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ
1818     ¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤Ï $KEY ¤Ë¤è¤Ã¤Æ¡¢ÃÍ(Ê£¿ô²Ä)¤Ï $VALUES 
1819     ¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤ë¡£$NUM ¤ÏÀßÄꤵ¤ì¤ëÃͤθĿô¤Ç¤¢¤ë¡£
1820
1821     @return
1822     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_put_prop_values () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±
1823     ¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1824
1825     @latexonly \IPAlabel{mtext_put_prop_values} @endlatexonly  */
1826
1827 /***
1828     @errors
1829     @c MERROR_RANGE, @c MERROR_SYMBOL
1830
1831     @seealso
1832     mtext_put_prop (), mtext_get_prop (), mtext_get_prop_values (),
1833     mtext_push_prop (), mtext_pop_prop (), mtext_prop_range ()  */
1834
1835 int
1836 mtext_put_prop_values (MText *mt, int from, int to,
1837                        MSymbol key, void **values, int num)
1838 {
1839   MTextPlist *plist;
1840   MInterval *interval;
1841   int i;
1842
1843   M_CHECK_RANGE (mt, from, to, -1, 0);
1844
1845   prepare_to_modify (mt, from, to, key, 0);
1846   plist = get_plist_create (mt, key, 1);
1847   interval = pop_all_properties (plist, from, to);
1848   if (num > 0)
1849     {
1850       PREPARE_INTERVAL_STACK (interval, num);
1851       for (i = 0; i < num; i++)
1852         {
1853           MTextProperty *prop
1854             = new_text_property (mt, from, to, key, values[i], 0);
1855           PUSH_PROP (interval, prop);
1856           M17N_OBJECT_UNREF (prop);
1857         }
1858     }
1859   if (interval->next)
1860     maybe_merge_interval (plist, interval);
1861   if (interval->prev)
1862     maybe_merge_interval (plist, interval->prev);
1863   xassert (check_plist (plist, 0) == 0);
1864   return 0;
1865 }
1866
1867 /*=*/
1868
1869 /***en
1870     @brief Push a text property.
1871
1872     The mtext_push_prop () function pushes a text property whose key
1873     is $KEY and value is $VAL to the characters between $FROM
1874     (inclusive) and $TO (exclusive) in M-text $MT.  With this
1875     function,
1876
1877 @verbatim
1878                     FROM                    TO
1879 M-text: |<------------|-------- MT ---------|------------>|
1880 PROP  :  <------------------ OLD_VAL -------------------->
1881 @endverbatim
1882
1883     becomes
1884
1885 @verbatim 
1886                     FROM                    TO
1887 M-text: |<------------|-------- MT ---------|------------>|
1888 PROP  :  <------------------- OLD_VAL ------------------->
1889 PROP  :               <-------- VAL ------->
1890 @endverbatim
1891
1892     @return
1893     If the operation was successful, mtext_push_prop () returns 0.
1894     Otherwise it returns -1 and assigns an error code to the external
1895     variable #merror_code.  */
1896
1897 /***ja
1898     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥×¥Ã¥·¥å¤¹¤ë.
1899
1900     ´Ø¿ô mtext_push_prop () ¤Ï¡¢¥­¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¥Æ¥­¥¹¥È
1901     ¥×¥í¥Ñ¥Æ¥£¤ò¡¢M-text $MT Ãæ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê
1902     ¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¥×¥Ã¥·¥å¤¹¤ë¡£¤³¤Î´Ø¿ô¤Ë¤è¤Ã¤Æ
1903
1904 @verbatim
1905                     FROM                    TO
1906 M-text: |<------------|-------- MT ---------|------------>|
1907 PROP  :  <------------------ OLD_VAL -------------------->
1908 @endverbatim
1909  ¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¡£
1910 @verbatim 
1911                     FROM                    TO
1912 M-text: |<------------|-------- MT ---------|------------>|
1913 PROP  :  <------------------- OLD_VAL ------------------->
1914 PROP  :               <-------- VAL ------->
1915 @endverbatim
1916
1917     @return
1918     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_push_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð 
1919     -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1920
1921     @latexonly \IPAlabel{mtext_push_prop} @endlatexonly  */
1922
1923 /***
1924     @errors
1925     @c MERROR_RANGE, @c MERROR_SYMBOL
1926
1927     @seealso
1928     mtext_put_prop (), mtext_put_prop_values (),
1929     mtext_get_prop (), mtext_get_prop_values (),
1930     mtext_pop_prop (), mtext_prop_range ()  */
1931
1932 int
1933 mtext_push_prop (MText *mt, int from, int to,
1934                  MSymbol key, void *val)
1935 {
1936   MTextPlist *plist;
1937   MInterval *head, *tail, *interval;
1938   MTextProperty *prop;
1939   int check_head, check_tail;
1940
1941   M_CHECK_RANGE (mt, from, to, -1, 0);
1942
1943   prepare_to_modify (mt, from, to, key, 0);
1944   plist = get_plist_create (mt, key, 1);
1945
1946   /* Find an interval that covers the position FROM.  */
1947   head = find_interval (plist, from);
1948
1949   /* If the found interval starts before FROM, divide it at FROM.  */
1950   if (head->start < from)
1951     {
1952       divide_interval (plist, head, from);
1953       head = head->next;
1954       check_head = 0;
1955     }
1956   else
1957     check_head = 1;
1958
1959   /* Find an interval that ends at TO.  If TO is not at the end of an
1960      interval, make one that ends at TO.  */
1961   if (head->end == to)
1962     {
1963       tail = head;
1964       check_tail = 1;
1965     }
1966   else if (head->end > to)
1967     {
1968       divide_interval (plist, head, to);
1969       tail = head;
1970       check_tail = 0;
1971     }
1972   else
1973     {
1974       tail = find_interval (plist, to);
1975       if (! tail)
1976         {
1977           tail = plist->tail;
1978           check_tail = 0;
1979         }
1980       else if (tail->start == to)
1981         {
1982           tail = tail->prev;
1983           check_tail = 1;
1984         }
1985       else
1986         {
1987           divide_interval (plist, tail, to);
1988           check_tail = 0;
1989         }
1990     }
1991
1992   prop = new_text_property (mt, from, to, key, val, 0);
1993
1994   /* Push PROP to the current values of intervals between HEAD and TAIL
1995      (both inclusive).  */
1996   for (interval = head; ; interval = interval->next)
1997     {
1998       PUSH_PROP (interval, prop);
1999       if (interval == tail)
2000         break;
2001     }
2002
2003   M17N_OBJECT_UNREF (prop);
2004
2005   /* If there is a possibility that TAIL now has the same value as the
2006      next one, check it and concatenate them if necessary.  */
2007   if (tail->next && check_tail)
2008     maybe_merge_interval (plist, tail);
2009
2010   /* If there is a possibility that HEAD now has the same value as the
2011      previous one, check it and concatenate them if necessary.  */
2012   if (head->prev && check_head)
2013     maybe_merge_interval (plist, head->prev);
2014
2015   xassert (check_plist (plist, 0) == 0);
2016   return 0;
2017 }
2018
2019 /*=*/
2020
2021 /***en
2022     @brief Pop a text property.
2023
2024     The mtext_pop_prop () function removes the topmost text property
2025     whose key is $KEY from the characters between $FROM (inclusive)
2026     and and $TO (exclusive) in $MT.
2027
2028     This function does nothing if characters in the region have no
2029     such text property. With this function,
2030
2031 @verbatim
2032                     FROM                    TO
2033 M-text: |<------------|-------- MT ---------|------------>|
2034 PROP  :  <------------------ OLD_VAL -------------------->
2035 @endverbatim
2036
2037     becomes
2038
2039 @verbatim 
2040                     FROM                    TO
2041 M-text: |<------------|-------- MT ---------|------------>|
2042 PROP  :  <--OLD_VAL-->|                     |<--OLD_VAL-->|
2043 @endverbatim
2044
2045     @return
2046     If the operation was successful, mtext_pop_prop () return 0.
2047     Otherwise it returns -1 and assigns an error code to the external
2048     variable #merror_code.  */
2049
2050 /***ja
2051     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ý¥Ã¥×¤¹¤ë.
2052
2053     ´Ø¿ô mtext_pop_prop () ¤Ï¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î
2054     ¤¦¤Á°ìÈÖ¾å¤Î¤â¤Î¤ò¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO¡Ê´Þ¤Þ
2055     ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤«¤é¼è¤ê½ü¤¯¡£
2056
2057     »ØÄêÈϰϤÎʸ»ú¤¬¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¤Ê¤é¤Ð¡¢¤³¤Î´Ø¿ô¤Ï²¿
2058     ¤â¤·¤Ê¤¤¡£¤³¤Î´Ø¿ô¤Ë¤è¤Ã¤Æ¡¢
2059
2060 @verbatim
2061                     FROM                    TO
2062 M-text: |<------------|-------- MT ---------|------------>|
2063 PROP  :  <------------------ OLD_VAL -------------------->
2064 @endverbatim
2065  ¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£
2066 @verbatim 
2067                     FROM                    TO
2068 M-text: |<------------|-------- MT ---------|------------>|
2069 PROP  :  <--OLD_VAL-->|                     |<--OLD_VAL-->|
2070 @endverbatim
2071
2072     @return
2073     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_pop_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 
2074     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
2075
2076     @latexonly \IPAlabel{mtext_pop_prop} @endlatexonly  */
2077
2078 /***
2079     @errors
2080     @c MERROR_RANGE, @c MERROR_SYMBOL
2081
2082     @seealso
2083     mtext_put_prop (), mtext_put_prop_values (),
2084     mtext_get_prop (), mtext_get_prop_values (),
2085     mtext_push_prop (), mtext_prop_range ()  */
2086
2087 int
2088 mtext_pop_prop (MText *mt, int from, int to, MSymbol key)
2089 {
2090   MTextPlist *plist;
2091   MInterval *head, *tail;
2092   int check_head = 1;
2093
2094   if (key == Mnil)
2095     MERROR (MERROR_TEXTPROP, -1);
2096   M_CHECK_RANGE (mt, from, to, -1, 0);
2097   plist = get_plist_create (mt, key, 0);
2098   if (! plist)
2099     return 0;
2100
2101   /* Find an interval that covers the position FROM.  */
2102   head = find_interval (plist, from);
2103   if (head->end >= to
2104       && head->nprops == 0)
2105     /* No property to pop.  */
2106     return 0;
2107
2108   prepare_to_modify (mt, from, to, key, 0);
2109
2110   /* If the found interval starts before FROM and has value(s), divide
2111      it at FROM.  */
2112   if (head->start < from)
2113     {
2114       if (head->nprops > 0)
2115         {
2116           divide_interval (plist, head, from);
2117           check_head = 0;
2118         }
2119       else
2120         from = head->end;
2121       head = head->next;
2122     }
2123
2124   /* Pop the topmost text property from each interval following HEAD.
2125      Stop at an interval that ends after TO.  */
2126   for (tail = head; tail && tail->end <= to; tail = tail->next)
2127     if (tail->nprops > 0)
2128       POP_PROP (tail);
2129
2130   if (tail)
2131     {
2132       if (tail->start < to)
2133         {
2134           if (tail->nprops > 0)
2135             {
2136               divide_interval (plist, tail, to);
2137               POP_PROP (tail);
2138             }
2139           to = tail->start;
2140         }
2141       else
2142         to = tail->end;
2143     }
2144   else
2145     to = plist->tail->start;
2146
2147   /* If there is a possibility that HEAD now has the same text
2148      properties as the previous one, check it and concatenate them if
2149      necessary.  */
2150   if (head->prev && check_head)
2151     head = head->prev;
2152   while (head && head->end <= to)
2153     head = maybe_merge_interval (plist, head);
2154
2155   xassert (check_plist (plist, 0) == 0);
2156   return 0;
2157 }
2158
2159 /*=*/
2160
2161 /***en
2162     @brief Find the range where the value of a text property is the same.
2163
2164     The mtext_prop_range () function investigates the extent where all
2165     characters have the same value for a text property.  It first
2166     finds the value of the property specified by $KEY of the character
2167     at $POS in M-text $MT.  Then it checks if adjacent characters have
2168     the same value for the property $KEY.  The beginning and the end
2169     of the found range are stored to the variable pointed to by $FROM
2170     and $TO.  The character position stored in $FROM is inclusive but
2171     that in $TO is exclusive; this fashion is compatible with the
2172     range specification in the mtext_put_prop () function, etc.
2173
2174     If $DEEPER is not 0, not only the topmost but also all the stacked
2175     properties whose key is $KEY are compared.
2176
2177     If $FROM is @c NULL, the beginning of range is not searched for.  If
2178     $TO is @c NULL, the end of range is not searched for.
2179
2180     @return
2181
2182     If the operation was successful, mtext_prop_range () returns the
2183     number of values the property $KEY has at pos.  Otherwise it
2184     returns -1 and assigns an error code to the external variable @c
2185     merror_code.  */
2186
2187 /***ja
2188     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬Æ±¤¸Ãͤò¤È¤ëÈϰϤòÄ´¤Ù¤ë.
2189
2190     ´Ø¿ô mtext_prop_range () ¤Ï¡¢»ØÄꤷ¤¿¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤ¬Æ±¤¸
2191     ¤Ç¤¢¤ëϢ³¤·¤¿Ê¸»ú¤ÎÈϰϤòÄ´¤Ù¤ë¡£¤Þ¤º M-text $MT ¤Î $POS ¤Î°ÌÃÖ¤Ë
2192     ¤¢¤ëʸ»ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼ $KEY ¤Ç»ØÄꤵ¤ì¤¿¤â¤ÎÃͤò¸«¤Ä¤±
2193     ¤ë¡£¤½¤·¤ÆÁ°¸å¤Îʸ»ú¤â $KEY ¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤ¬Æ±¤¸¤Ç¤¢¤ë¤«¤É¤¦¤«¤ò
2194     Ä´¤Ù¤ë¡£¸«¤Ä¤±¤¿ÈϰϤκǽé¤ÈºÇ¸å¤ò¡¢¤½¤ì¤¾¤ì $FROM ¤È $TO ¤Ë¥Ý¥¤¥ó
2195     ¥È¤µ¤ì¤ëÊÑ¿ô¤ËÊݸ¤¹¤ë¡£$FROM ¤ËÊݸ¤µ¤ì¤ëʸ»ú¤Î°ÌÃ֤ϸ«¤Ä¤±¤¿ÈÏ°Ï
2196     ¤Ë´Þ¤Þ¤ì¤ë¤¬¡¢$TO ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£¡Ê$TO ¤ÎÁ°¤ÇƱ¤¸Ãͤò¤È¤ëÈϰϤϽª¤ï
2197     ¤ë¡£¡Ë¤³¤ÎÈÏ°Ï»ØÄêË¡¤Ï¡¢´Ø¿ô mtext_put_prop () ¤Ê¤É¤È¶¦Ä̤Ǥ¢¤ë¡£
2198
2199     $DEEPER ¤¬ 0 ¤Ç¤Ê¤±¤ì¤Ð¡¢$KEY ¤È¤¤¤¦¥­¡¼¤ò»ý¤Ä¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á°ìÈÖ
2200     ¾å¤Î¤â¤Î¤À¤±¤Ç¤Ê¤¯¡¢¥¹¥¿¥Ã¥¯Ãæ¤Î¤¹¤Ù¤Æ¤Î¤â¤Î¤¬Èæ³Ó¤µ¤ì¤ë¡£
2201
2202     $FROM ¤¬ @c NULL ¤Ê¤é¤Ð¡¢ÈϰϤλϤޤê¤Ïõº÷¤·¤Ê¤¤¡£$TO ¤¬ @c NULL 
2203     ¤Ê¤é¤Ð¡¢ÈϰϤνª¤ê¤Ïõº÷¤·¤Ê¤¤¡£
2204
2205     @return
2206     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_prop_range () ¤Ï $KEY ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤοô¤ò
2207     ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð-1 ¤òÊÖ¤·¡¢ ³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼
2208     ¥É¤òÀßÄꤹ¤ë¡£
2209
2210     @latexonly \IPAlabel{mtext_prop_range} @endlatexonly  */
2211
2212 /***
2213     @errors
2214     @c MERROR_RANGE, @c MERROR_SYMBOL
2215
2216     @seealso
2217     mtext_put_prop (), mtext_put_prop_values (),
2218     mtext_get_prop (), mtext_get_prop_values (), 
2219     mtext_pop_prop (), mtext_push_prop ()  */
2220
2221 int
2222 mtext_prop_range (MText *mt, MSymbol key, int pos,
2223                   int *from, int *to, int deeper)
2224 {
2225   MTextPlist *plist;
2226   MInterval *interval, *temp;
2227   void *val;
2228   int nprops;
2229
2230   M_CHECK_POS (mt, pos, -1);
2231
2232   plist = get_plist_create (mt, key, 0);
2233   if (! plist)
2234     {
2235       if (from) *from = 0;
2236       if (to) *to = mtext_nchars (mt);
2237       return 0;
2238     }
2239
2240   interval = find_interval (plist, pos);
2241   nprops = interval->nprops;
2242   if (deeper || ! nprops)
2243     {
2244       if (from) *from = interval->start;
2245       if (to) *to = interval->end;
2246       return interval->nprops;
2247     }
2248
2249   val = nprops ? interval->stack[nprops - 1] : NULL;
2250
2251   if (from)
2252     {
2253       for (temp = interval;
2254            temp->prev
2255              && (temp->prev->nprops
2256                  ? (nprops
2257                     && (val == temp->prev->stack[temp->prev->nprops - 1]))
2258                  : ! nprops);
2259            temp = temp->prev);
2260       *from = temp->start;
2261     }
2262
2263   if (to)
2264     {
2265       for (temp = interval;
2266            temp->next
2267              && (temp->next->nprops
2268                  ? (nprops
2269                     && val == temp->next->stack[temp->next->nprops - 1])
2270                  : ! nprops);
2271            temp = temp->next);
2272       *to = temp->end;
2273     }
2274
2275   return nprops;
2276 }
2277
2278 /***en
2279     @brief Create a text property.
2280
2281     The mtext_property () function returns a newly allocated text
2282     property whose key is $KEY and value is $VAL.  The created text 
2283     property is not attached to any M-text, i.e. it is detached.
2284
2285     $CONTROL_BITS must be 0 or logical OR of @c enum @c
2286     MTextPropertyControl.  */
2287
2288 /***ja
2289     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀ¸À®¤¹¤ë.
2290
2291     ´Ø¿ô mtext_property () ¤Ï $KEY ¤ò¥­¡¼¡¢$VAL ¤òÃͤȤ¹¤ë¿·¤·¤¯³ä¤êÅö
2292     ¤Æ¤é¤ì¤¿¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÊÖ¤¹¡£À¸À®¤·¤¿¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¤¤«
2293     ¤Ê¤ë M-text ¤Ë¤âÉղ䵤ì¤Æ¤¤¤Ê¤¤¡¢¤¹¤Ê¤ï¤ÁʬΥ¤·¤Æ (detached) ¤¤¤ë¡£
2294
2295     $CONTROL_BITS ¤Ï 0 ¤Ç¤¢¤ë¤« @c enum @c MTextPropertyControl ¤ÎÏÀÍý 
2296     OR ¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£  */
2297
2298 MTextProperty *
2299 mtext_property (MSymbol key, void *val, int control_bits)
2300 {
2301   return new_text_property (NULL, 0, 0, key, val, control_bits);
2302 }
2303
2304 /***en
2305     @brief Return the M-text of a text property.
2306
2307     The mtext_property_mtext () function returns the M-text to which
2308     text property $PROP is attached.  If $PROP is currently detached,
2309     NULL is returned.  */
2310
2311 /***ja
2312     @brief ¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä M-text ¤òÊÖ¤¹.
2313
2314     ´Ø¿ô mtext_property_mtext () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£$PROP ¤¬Éղäµ
2315     ¤ì¤Æ¤¤¤ë M-text ¤òÊÖ¤¹¡£¤½¤Î»þÅÀ¤Ç $PROP ¤¬Ê¬Î¥¤·¤Æ¤¤¤ì¤Ð NULL ¤ò
2316     ÊÖ¤¹¡£  */
2317
2318 MText *
2319 mtext_property_mtext (MTextProperty *prop)
2320 {
2321   return prop->mt;
2322 }
2323
2324 /***en
2325     @brief Return the key of a text property.
2326
2327     The mtext_property_key () function returns the key (symbol) of
2328     text property $PROP.  */
2329
2330 /***ja
2331     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤òÊÖ¤¹.
2332
2333     ´Ø¿ô mtext_property_key () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤Î¥­¡¼¡Ê¥·
2334     ¥ó¥Ü¥ë¡Ë¤òÊÖ¤¹¡£  */
2335
2336 MSymbol
2337 mtext_property_key (MTextProperty *prop)
2338 {
2339   return prop->key;
2340 }
2341
2342 /***en
2343     @brief Return the value of a text property.
2344
2345     The mtext_property_value () function returns the value of text
2346     property $PROP.  */
2347
2348 /***ja
2349     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊÖ¤¹.
2350
2351     ´Ø¿ô mtext_property_value () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤ÎÃͤòÊÖ
2352     ¤¹¡£  */
2353
2354 void *
2355 mtext_property_value (MTextProperty *prop)
2356 {
2357   return prop->val;
2358 }
2359
2360 /***en
2361     @brief Return the start position of a text property.
2362
2363     The mtext_property_start () function returns the start position of
2364     text property $PROP.  The start position is a character position
2365     of an M-text where $PROP begins.  If $PROP is detached, it returns
2366     -1.  */
2367
2368 /***ja
2369     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î³«»Ï°ÌÃÖ¤òÊÖ¤¹.
2370
2371     ´Ø¿ô mtext_property_start () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤Î³«»Ï°Ì
2372     ÃÖ¤òÊÖ¤¹¡£³«»Ï°ÌÃ֤ȤϠM-text Ãæ¤Ç $PROP ¤¬»Ï¤Þ¤ëʸ»ú°ÌÃ֤Ǥ¢¤ë¡£
2373     $PROP ¤¬Ê¬Î¥¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢-1 ¤òÊÖ¤¹¡£  */
2374
2375 int
2376 mtext_property_start (MTextProperty *prop)
2377 {
2378   return (prop->mt ? prop->start : -1);
2379 }
2380
2381 /***en
2382     @brief Return the end position of a text property.
2383
2384     The mtext_property_end () function returns the end position of
2385     text property $PROP.  The end position is a character position of
2386     an M-text where $PROP ends.  If $PROP is detached, it returns
2387     -1.  */
2388
2389 /***ja
2390     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î½ªÎ»°ÌÃÖ¤òÊÖ¤¹.
2391
2392     ´Ø¿ô mtext_property_end () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤Î½ªÎ»°ÌÃÖ
2393     ¤òÊÖ¤¹¡£½ªÎ»°ÌÃ֤ȤϠM-text Ãæ¤Ç $PROP ¤¬½ª¤ëʸ»ú°ÌÃ֤Ǥ¢¤ë¡£$PROP 
2394     ¤¬Ê¬Î¥¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢-1 ¤òÊÖ¤¹¡£  */
2395
2396 int
2397 mtext_property_end (MTextProperty *prop)
2398 {
2399   return (prop->mt ? prop->end : -1);
2400 }
2401
2402 /***en
2403     @brief Get the topmost text property.
2404
2405     The mtext_get_property () function searches the character at
2406     position $POS in M-text $MT for a text property whose key is $KEY.
2407
2408     @return
2409     If a text property is found, mtext_get_property () returns it.  If
2410     there are multiple text properties, it returns the topmost one.
2411     If no such property is found, it returns @c NULL without changing
2412     the external variable #merror_code.
2413
2414     If an error is detected, mtext_get_property () returns @c NULL and
2415     assigns an error code to the external variable #merror_code.  */
2416
2417 /***ja
2418     @brief °ìÈÖ¾å¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÆÀ¤ë.
2419
2420     ´Ø¿ô mtext_get_property () ¤Ï M-text $MT ¤Î°ÌÃÖ $POS ¤Îʸ»ú¤¬¥­¡¼
2421     ¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¤«¤É¤¦¤«¤òÄ´¤Ù¤ë¡£
2422
2423     @return 
2424     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_get_property () ¤Ï¤½¤ì¤òÊÖ¤¹¡£
2425     Ê£¿ô¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢°ìÈÖ¾å¤Î¤â¤Î¤òÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð¡¢³°ÉôÊÑ¿ô 
2426     #merror_code ¤òÊѤ¨¤ë¤³¤È¤Ê¤¯ @c NULL ¤òÊÖ¤¹¡£
2427
2428     ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç mtext_get_property () ¤Ï @c NULL ¤òÊÖ¤·¡¢³°
2429     ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£    */
2430
2431 MTextProperty *
2432 mtext_get_property (MText *mt, int pos, MSymbol key)
2433 {
2434   MTextPlist *plist;
2435   MInterval *interval;
2436
2437   M_CHECK_POS (mt, pos, NULL);
2438
2439   plist = get_plist_create (mt, key, 0);
2440   if (! plist)
2441     return NULL;
2442
2443   interval = find_interval (plist, pos);
2444   if (! interval->nprops)
2445     return NULL;
2446   return interval->stack[interval->nprops - 1];
2447 }
2448
2449 /***en
2450     @brief Get multiple text properties.
2451
2452     The mtext_get_properties () function searches the character at
2453     $POS in M-text $MT for properties whose key is $KEY.  If such
2454     properties are found, they are stored in the memory area pointed
2455     to by $PROPS.  $NUM limits the maximum number of stored
2456     properties.
2457
2458     @return
2459     If the operation was successful, mtext_get_properties () returns
2460     the number of actually stored properties.  If the character at
2461     $POS does not have a property whose key is $KEY, the return value
2462     is 0. If an error is detected, mtext_get_properties () returns -1
2463     and assigns an error code to the external variable #merror_code.  */
2464
2465 /***ja
2466     @brief Ê£¿ô¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÆÀ¤ë.
2467
2468     ´Ø¿ô mtext_get_properties () ¤Ï M-text $MT ¤Î°ÌÃÖ $POS ¤Îʸ»ú¤¬¥­¡¼
2469     ¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¤«¤É¤¦¤«¤òÄ´¤Ù¤ë¡£¤½¤Î¤è¤¦¤Ê
2470     ¥×¥í¥Ñ¥Æ¥£¤¬¤ß¤Ä¤«¤ì¤Ð¡¢$PROPS ¤¬»Ø¤¹¥á¥â¥êÎΰè¤ËÊݸ¤¹¤ë¡£$NUM ¤Ï
2471     Êݸ¤µ¤ì¤ë¥×¥í¥Ñ¥Æ¥£¤Î¿ô¤Î¾å¸Â¤Ç¤¢¤ë¡£
2472
2473     @return 
2474     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_get_properties () ¤Ï¼ÂºÝ¤ËÊݸ¤·¤¿¥×¥í¥Ñ¥Æ¥£
2475     ¤Î¿ô¤òÊÖ¤¹¡£$POS ¤Î°ÌÃÖ¤Îʸ»ú¤¬¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿
2476     ¤Ê¤±¤ì¤Ð¡¢0 ¤¬Ê֤롣¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ë¤Ï¡¢
2477     mtext_get_properties () ¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼
2478     ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
2479
2480 int
2481 mtext_get_properties (MText *mt, int pos, MSymbol key,
2482                       MTextProperty **props, int num)
2483 {
2484   MTextPlist *plist;
2485   MInterval *interval;
2486   int nprops;
2487   int i;
2488   int offset;
2489
2490   M_CHECK_POS (mt, pos, -1);
2491
2492   plist = get_plist_create (mt, key, 0);
2493   if (! plist)
2494     return 0;
2495
2496   interval = find_interval (plist, pos);
2497   /* It is assured that INTERVAL is not NULL.  */
2498   nprops = interval->nprops;
2499   if (nprops == 0 || num <= 0)
2500     return 0;
2501   if (nprops == 1 || num == 1)
2502     {
2503       props[0] = interval->stack[nprops - 1];
2504       return 1;
2505     }
2506
2507   if (nprops <= num)
2508     num = nprops, offset = 0;
2509   else
2510     offset = nprops - num;
2511   for (i = 0; i < num; i++)
2512     props[i] = interval->stack[offset + i];
2513   return num;
2514 }
2515
2516 /***en
2517     @brief Attach a text property to an M-text.
2518
2519     The mtext_attach_property () function attaches text property $PROP
2520     to the range between $FROM and $TO in M-text $MT.  If $PROP is
2521     already attached to an M-text, it is detached before attached to
2522     $MT.
2523
2524     @return
2525     If the operation was successful, mtext_attach_property () returns
2526     0.  Otherwise it returns -1 and assigns an error code to the
2527     external variable #merror_code.  */
2528
2529 /***ja
2530     @brief  M-text¤Ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÉղ乤ë.
2531
2532     ´Ø¿ô mtext_attach_property () ¤Ï¡¢M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ
2533     ¤Ç¤ÎÎΰè¤Ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤òÉղ乤롣¤â¤· $PROP ¤¬´û¤Ë
2534     M-text ¤ËÉղ䵤ì¤Æ¤¤¤ì¤Ð¡¢$MT ¤ËÉղ乤ëÁ°¤ËʬΥ¤µ¤ì¤ë¡£
2535
2536     @return 
2537     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_attach_property () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±
2538     ¤ì¤Ð -1 ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£      */
2539
2540
2541 int
2542 mtext_attach_property (MText *mt, int from, int to, MTextProperty *prop)
2543 {     
2544   MTextPlist *plist;
2545   MInterval *interval;
2546
2547   M_CHECK_RANGE (mt, from, to, -1, 0);
2548
2549   M17N_OBJECT_REF (prop);
2550   if (prop->mt)
2551     mtext_detach_property (prop);
2552   prepare_to_modify (mt, from, to, prop->key, 0);
2553   plist = get_plist_create (mt, prop->key, 1);
2554   xassert (check_plist (plist, 0) == 0);
2555   interval = pop_all_properties (plist, from, to);
2556   xassert (check_plist (plist, 0) == 0);
2557   prop->mt = mt;
2558   prop->start = from;
2559   prop->end = to;
2560   PUSH_PROP (interval, prop);
2561   M17N_OBJECT_UNREF (prop);
2562   xassert (check_plist (plist, 0) == 0);
2563   if (interval->next)
2564     maybe_merge_interval (plist, interval);
2565   if (interval->prev)
2566     maybe_merge_interval (plist, interval->prev);
2567   xassert (check_plist (plist, 0) == 0);
2568   return 0;
2569 }
2570
2571 /***en
2572     @brief Detach a text property from an M-text.
2573
2574     The mtext_detach_property () function makes text property $PROP
2575     detached.
2576
2577     @return
2578     This function always returns 0.  */
2579
2580 /***ja
2581     @brief  M-text ¤«¤é¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òʬΥ¤¹¤ë.
2582
2583     ´Ø¿ô mtext_detach_property () ¤Ï¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤òʬΥ¤¹¤ë¡£
2584
2585     @return
2586     ¤³¤Î´Ø¿ô¤Ï¾ï¤Ë 0 ¤òÊÖ¤¹¡£  */
2587
2588 int
2589 mtext_detach_property (MTextProperty *prop)
2590 {
2591   MTextPlist *plist;
2592   int start = prop->start, end = prop->end;
2593
2594   if (! prop->mt)
2595     return 0;
2596   prepare_to_modify (prop->mt, start, end, prop->key, 0);
2597   plist = get_plist_create (prop->mt, prop->key, 0);
2598   xassert (plist);
2599   detach_property (plist, prop, NULL);
2600   return 0;
2601 }
2602
2603 /***en
2604     @brief Push a text property onto an M-text.
2605
2606     The mtext_push_property () function pushes text property $PROP to
2607     the characters between $FROM (inclusive) and $TO (exclusive) in
2608     M-text $MT.
2609
2610     @return
2611     If the operation was successful, mtext_push_property () returns
2612     0.  Otherwise it returns -1 and assigns an error code to the
2613     external variable #merror_code.  */
2614
2615 /***ja
2616     @brief M-text ¤Ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥×¥Ã¥·¥å¤¹¤ë.
2617
2618     ´Ø¿ô mtext_push_property () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤ò¡¢
2619     M-text $MT Ãæ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎ
2620     Ê¸»ú¤Ë¥×¥Ã¥·¥å¤¹¤ë¡£
2621
2622     @return
2623     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_push_property () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±
2624     ¤ì¤Ð -1 ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£      */
2625
2626
2627 int
2628 mtext_push_property (MText *mt, int from, int to, MTextProperty *prop)
2629 {
2630   MTextPlist *plist;
2631   MInterval *head, *tail, *interval;
2632   int check_head, check_tail;
2633
2634   M_CHECK_RANGE (mt, from, to, -1, 0);
2635
2636   M17N_OBJECT_REF (prop);
2637   if (prop->mt)
2638     mtext_detach_property (prop);
2639   prepare_to_modify (mt, from, to, prop->key, 0);
2640   plist = get_plist_create (mt, prop->key, 1);
2641   prop->mt = mt;
2642   prop->start = from;
2643   prop->end = to;
2644
2645   /* Find an interval that covers the position FROM.  */
2646   head = find_interval (plist, from);
2647
2648   /* If the found interval starts before FROM, divide it at FROM.  */
2649   if (head->start < from)
2650     {
2651       divide_interval (plist, head, from);
2652       head = head->next;
2653       check_head = 0;
2654     }
2655   else
2656     check_head = 1;
2657
2658   /* Find an interval that ends at TO.  If TO is not at the end of an
2659      interval, make one that ends at TO.  */
2660   if (head->end == to)
2661     {
2662       tail = head;
2663       check_tail = 1;
2664     }
2665   else if (head->end > to)
2666     {
2667       divide_interval (plist, head, to);
2668       tail = head;
2669       check_tail = 0;
2670     }
2671   else
2672     {
2673       tail = find_interval (plist, to);
2674       if (! tail)
2675         {
2676           tail = plist->tail;
2677           check_tail = 0;
2678         }
2679       else if (tail->start == to)
2680         {
2681           tail = tail->prev;
2682           check_tail = 1;
2683         }
2684       else
2685         {
2686           divide_interval (plist, tail, to);
2687           check_tail = 0;
2688         }
2689     }
2690
2691   /* Push PROP to the current values of intervals between HEAD and TAIL
2692      (both inclusive).  */
2693   for (interval = head; ; interval = interval->next)
2694     {
2695       PUSH_PROP (interval, prop);
2696       if (interval == tail)
2697         break;
2698     }
2699
2700   /* If there is a possibility that TAIL now has the same value as the
2701      next one, check it and concatenate them if necessary.  */
2702   if (tail->next && check_tail)
2703     maybe_merge_interval (plist, tail);
2704
2705   /* If there is a possibility that HEAD now has the same value as the
2706      previous one, check it and concatenate them if necessary.  */
2707   if (head->prev && check_head)
2708     maybe_merge_interval (plist, head->prev);
2709
2710   M17N_OBJECT_UNREF (prop);
2711   xassert (check_plist (plist, 0) == 0);
2712   return 0;
2713 }
2714
2715 /***en
2716     @brief Symbol for specifying serializer functions.
2717
2718     To serialize a text property, the user must supply a serializer
2719     function for that text property.  This is done by giving a symbol
2720     property whose key is #Mtext_prop_serializer and value is a
2721     pointer to an appropriate serializer function.
2722
2723     @seealso
2724     mtext_serialize (), #MTextPropSerializeFunc
2725   */
2726
2727 /***ja
2728     @brief ¥·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë.
2729
2730     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¤¿¤á¤Ë¤Ï¡¢¤½¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ
2731     ¥Æ¥£ÍѤΥ·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤òÍ¿¤¨¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¶ñÂÎŪ¤Ë¤Ï¡¢
2732     #Mtext_prop_serializer ¤ò¥­¡¼¤È¤·¡¢Å¬Àڤʥ·¥ê¥¢¥é¥¤¥º´Ø¿ô¤Ø¤Î¥Ý¥¤
2733     ¥ó¥¿¤òÃͤȤ¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¡£
2734
2735     @seealso
2736     mtext_serialize (), #MTextPropSerializeFunc
2737   */
2738 MSymbol Mtext_prop_serializer;
2739
2740 /***en
2741     @brief Symbol for specifying deserializer functions.
2742
2743     To deserialize a text property, the user must supply a deserializer
2744     function for that text property.  This is done by giving a symbol
2745     property whose key is #Mtext_prop_deserializer and value is a
2746     pointer to an appropriate deserializer function.
2747
2748     @seealso
2749     mtext_deserialize (), #MTextPropSerializeFunc
2750   */
2751
2752 /***ja
2753     @brief ¥Ç¥·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë.
2754
2755     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ç¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¤¿¤á¤Ë¤Ï¡¢¤½¤Î¥Æ¥­¥¹¥È¥×¥í
2756     ¥Ñ¥Æ¥£ÍѤΥǥ·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤òÍ¿¤¨¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¶ñÂÎŪ¤Ë¤Ï¡¢
2757     #Mtext_prop_deserializer ¤ò¥­¡¼¤È¤·¡¢Å¬Àڤʥǥ·¥ê¥¢¥é¥¤¥º´Ø¿ô¤Ø¤Î
2758     ¥Ý¥¤¥ó¥¿¤òÃͤȤ¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¡£
2759
2760     @seealso
2761     mtext_deserialize (), #MTextPropSerializeFunc
2762   */
2763 MSymbol Mtext_prop_deserializer;
2764
2765 /***en
2766     @brief Serialize text properties in an M-text.
2767
2768     The mtext_serialize () function serializes the text between $FROM
2769     and $TO in M-text $MT.  The serialized result is an M-text in a
2770     form of XML.  $PROPERTY_LIST limits the text properties to be
2771     serialized. Only those text properties whose key 
2772
2773     @li appears as the value of an element in $PROPERTY_LIST, and
2774     @li has the symbol property #Mtext_prop_serializer
2775
2776     are serialized as a "property" element in the resulting XML
2777     representation.
2778
2779     The DTD of the generated XML is as follows:
2780
2781 @verbatim
2782 <!DOCTYPE mtext [
2783   <!ELEMENT mtext (property*,body+)>
2784   <!ELEMENT property EMPTY>
2785   <!ELEMENT body (#PCDATA)>
2786   <!ATTLIST property key CDATA #REQUIRED>
2787   <!ATTLIST property value CDATA #REQUIRED>
2788   <!ATTLIST property from CDATA #REQUIRED>
2789   <!ATTLIST property to CDATA #REQUIRED>
2790   <!ATTLIST property control CDATA #REQUIRED>
2791  ]>
2792 @endverbatim
2793
2794     This function depends on the libxml2 library.  If the m17n library
2795     is configured without libxml2, this function always fails.
2796
2797     @return
2798     If the operation was successful, mtext_serialize () returns an
2799     M-text in the form of XML.  Otherwise it returns @c NULL and assigns an
2800     error code to the external variable #merror_code.
2801
2802     @seealso
2803     mtext_deserialize (), #Mtext_prop_serializer  */
2804
2805 /***ja
2806     @brief M-text Ãæ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥·¥ê¥¢¥é¥¤¥º¤¹¤ë.
2807
2808     ´Ø¿ô mtext_serialize () ¤Ï M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ¤Ç¤Î¥Æ¥­
2809     ¥¹¥È¤ò¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¡£¥·¥ê¥¢¥é¥¤¥º¤·¤¿·ë²Ì¤Ï XML ·Á¼°¤Î M-text ¤Ç
2810     ¤¢¤ë¡£ $PROPERTY_LIST ¤Ï¥·¥ê¥¢¥é¥¤¥º¤µ¤ì¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¸ÂÄê
2811     ¤¹¤ë¡£ÂоݤȤʤë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¡¢¤½¤Î¥­¡¼¤¬
2812
2813     @li $PROPERTY_LIST ¤ÎÍ×ÁǤÎÃͤȤ·¤Æ¸½¤ï¤ì¡¢¤«¤Ä
2814     @li ¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£ #Mtext_prop_serializer ¤ò»ý¤Ä
2815     
2816     ¤â¤Î¤Î¤ß¤Ç¤¢¤ë¡£¤³¤Î¾ò·ï¤òËþ¤¿¤¹¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¡¢À¸À®¤µ¤ì¤ë 
2817     XML É½¸½Ãæ¤Ç "property" Í×ÁǤ˥·¥ê¥¢¥é¥¤¥º¤µ¤ì¤ë¡£
2818
2819     À¸À®¤µ¤ì¤ë XML ¤Î DTD ¤Ï°Ê²¼¤ÎÄ̤ê:
2820
2821 @verbatim
2822 <!DOCTYPE mtext [
2823   <!ELEMENT mtext (property*,body+)>
2824   <!ELEMENT property EMPTY>
2825   <!ELEMENT body (#PCDATA)>
2826   <!ATTLIST property key CDATA #REQUIRED>
2827   <!ATTLIST property value CDATA #REQUIRED>
2828   <!ATTLIST property from CDATA #REQUIRED>
2829   <!ATTLIST property to CDATA #REQUIRED>
2830   <!ATTLIST property control CDATA #REQUIRED>
2831  ]>
2832 @endverbatim
2833
2834     ¤³¤Î´Ø¿ô¤Ï libxml2 ¥é¥¤¥Ö¥é¥ê¤Ë°Í¸¤¹¤ë¡£m17n ¥é¥¤¥Ö¥é¥ê¤¬libxml2 
2835     Ìµ¤·¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤Î´Ø¿ô¤Ï¾ï¤Ë¼ºÇÔ¤¹¤ë¡£
2836
2837     @return 
2838     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_serialize () ¤Ï XML ·Á¼°¤Ç M-text ¤òÊÖ¤¹¡£
2839     ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É
2840     ¤òÀßÄꤹ¤ë¡£
2841
2842     @seealso
2843     mtext_deserialize (), #Mtext_prop_serializer  */
2844
2845 MText *
2846 mtext_serialize (MText *mt, int from, int to, MPlist *property_list)
2847 {
2848 #ifdef HAVE_XML2
2849   MPlist *plist, *pl;
2850   MTextPropSerializeFunc func;
2851   MText *work;
2852   xmlDocPtr doc;
2853   xmlNodePtr node;
2854   unsigned char *ptr;
2855   int n;
2856
2857   M_CHECK_RANGE (mt, from, to, NULL, NULL);
2858   if (mt->format != MTEXT_FORMAT_US_ASCII
2859       && mt->format != MTEXT_FORMAT_UTF_8)
2860     mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
2861   if (MTEXT_DATA (mt)[mtext_nbytes (mt)] != 0)
2862     MTEXT_DATA (mt)[mtext_nbytes (mt)] = 0;
2863   doc = xmlParseMemory (XML_TEMPLATE, strlen (XML_TEMPLATE) + 1);
2864   node = xmlDocGetRootElement (doc);
2865
2866   plist = mplist ();
2867   MPLIST_DO (pl, property_list)
2868     {
2869       MSymbol key = MPLIST_VAL (pl);
2870
2871       func = ((MTextPropSerializeFunc)
2872               msymbol_get_func (key, Mtext_prop_serializer));
2873       if (func)
2874         extract_text_properties (mt, from, to, key, plist);
2875     }
2876
2877   work = mtext ();
2878   MPLIST_DO (pl, plist)
2879     {
2880       MTextProperty *prop = MPLIST_VAL (pl);
2881       char buf[256];
2882       MPlist *serialized_plist;
2883       xmlNodePtr child;
2884
2885       func = ((MTextPropSerializeFunc)
2886               msymbol_get_func (prop->key, Mtext_prop_serializer));
2887       serialized_plist = (func) (prop->val);
2888       if (! serialized_plist)
2889         continue;
2890       mtext_reset (work);
2891       mplist__serialize (work, serialized_plist, 0);
2892       child = xmlNewChild (node, NULL, (xmlChar *) "property", NULL);
2893       xmlSetProp (child, (xmlChar *) "key",
2894                   (xmlChar *) MSYMBOL_NAME (prop->key));
2895       xmlSetProp (child, (xmlChar *) "value", (xmlChar *) MTEXT_DATA (work));
2896       sprintf (buf, "%d", prop->start - from);
2897       xmlSetProp (child, (xmlChar *) "from", (xmlChar *) buf);
2898       sprintf (buf, "%d", prop->end - from);
2899       xmlSetProp (child, (xmlChar *) "to", (xmlChar *) buf);
2900       sprintf (buf, "%d", prop->control.flag);
2901       xmlSetProp (child, (xmlChar *) "control", (xmlChar *) buf);
2902       xmlAddChild (node, xmlNewText ((xmlChar *) "\n"));
2903
2904       M17N_OBJECT_UNREF (serialized_plist);
2905     }
2906   M17N_OBJECT_UNREF (plist);
2907
2908   if (from > 0 || to < mtext_nchars (mt))
2909     mtext_copy (work, 0, mt, from, to);
2910   else
2911     {
2912       M17N_OBJECT_UNREF (work);
2913       work = mt;
2914     }
2915   for (from = 0, to = mtext_nchars (mt); from <= to; from++)
2916     {
2917       ptr = MTEXT_DATA (mt) + POS_CHAR_TO_BYTE (mt, from);
2918       xmlNewTextChild (node, NULL, (xmlChar *) "body", (xmlChar *) ptr);
2919       from = mtext_character (mt, from, to, 0);
2920       if (from < 0)
2921         from = to;
2922     }
2923
2924   xmlDocDumpMemoryEnc (doc, (xmlChar **) &ptr, &n, "UTF-8");
2925   if (work == mt)
2926     work = mtext ();
2927   mtext__cat_data (work, ptr, n, MTEXT_FORMAT_UTF_8);
2928   return work;
2929 #else  /* not HAVE_XML2 */
2930   MERROR (MERROR_TEXTPROP, NULL);
2931 #endif  /* not HAVE_XML2 */
2932 }
2933
2934 /***en
2935     @brief Deserialize text properties in an M-text.
2936
2937     The mtext_deserialize () function deserializes M-text $MT.  $MT
2938     must be an XML having the following DTD.
2939
2940 @verbatim
2941 <!DOCTYPE mtext [
2942   <!ELEMENT mtext (property*,body+)>
2943   <!ELEMENT property EMPTY>
2944   <!ELEMENT body (#PCDATA)>
2945   <!ATTLIST property key CDATA #REQUIRED>
2946   <!ATTLIST property value CDATA #REQUIRED>
2947   <!ATTLIST property from CDATA #REQUIRED>
2948   <!ATTLIST property to CDATA #REQUIRED>
2949   <!ATTLIST property control CDATA #REQUIRED>
2950  ]>
2951 @endverbatim
2952
2953     This function depends on the libxml2 library.  If the m17n library
2954     is configured without libxml2, this function always fail.
2955
2956     @return
2957     If the operation was successful, mtext_deserialize () returns the
2958     resulting M-text.  Otherwise it returns @c NULL and assigns an error
2959     code to the external variable #merror_code.
2960
2961     @seealso
2962     mtext_serialize (), #Mtext_prop_deserializer  */
2963
2964 /***ja
2965     @brief M-text Ãæ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ç¥·¥ê¥¢¥é¥¤¥º¤¹¤ë.
2966
2967     ´Ø¿ô mtext_deserialize () ¤Ï M-text $MT ¤ò¥Ç¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¡£$MT
2968     ¤Ï¼¡¤Î DTD ¤ò»ý¤Ä XML ¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
2969  
2970 @verbatim
2971 <!DOCTYPE mtext [
2972   <!ELEMENT mtext (property*,body+)>
2973   <!ELEMENT property EMPTY>
2974   <!ELEMENT body (#PCDATA)>
2975   <!ATTLIST property key CDATA #REQUIRED>
2976   <!ATTLIST property value CDATA #REQUIRED>
2977   <!ATTLIST property from CDATA #REQUIRED>
2978   <!ATTLIST property to CDATA #REQUIRED>
2979   <!ATTLIST property control CDATA #REQUIRED>
2980  ]>
2981 @endverbatim
2982
2983     ¤³¤Î´Ø¿ô¤Ï libxml2 ¥é¥¤¥Ö¥é¥ê¤Ë°Í¸¤¹¤ë¡£m17n ¥é¥¤¥Ö¥é¥ê¤¬libxml2 
2984     Ìµ¤·¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤Î´Ø¿ô¤Ï¾ï¤Ë¼ºÇÔ¤¹¤ë¡£
2985
2986     @return 
2987     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_serialize () ¤ÏÆÀ¤é¤ì¤¿ M-text ¤ò
2988     ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼
2989     ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
2990
2991     @seealso
2992     mtext_serialize (), #Mtext_prop_deserializer  */
2993
2994 MText *
2995 mtext_deserialize (MText *mt)
2996 {
2997 #ifdef HAVE_XML2
2998   xmlDocPtr doc;
2999   xmlNodePtr node;
3000   xmlXPathContextPtr context;
3001   xmlXPathObjectPtr result;
3002   xmlChar *body_str, *key_str, *val_str, *from_str, *to_str, *ctl_str;
3003   int i;
3004
3005   if (mt->format > MTEXT_FORMAT_UTF_8)
3006     MERROR (MERROR_TEXTPROP, NULL);
3007   doc = xmlParseMemory ((char *) MTEXT_DATA (mt), mtext_nbytes (mt));
3008   if (! doc)
3009     MERROR (MERROR_TEXTPROP, NULL);
3010   node = xmlDocGetRootElement (doc);
3011   if (! node)
3012     {
3013       xmlFreeDoc (doc);
3014       MERROR (MERROR_TEXTPROP, NULL);
3015     }
3016   if (xmlStrcmp (node->name, (xmlChar *) "mtext"))
3017     {
3018       xmlFreeDoc (doc);
3019       MERROR (MERROR_TEXTPROP, NULL);
3020     }
3021
3022   context = xmlXPathNewContext (doc);
3023   result = xmlXPathEvalExpression ((xmlChar *) "//body", context);
3024   if (xmlXPathNodeSetIsEmpty (result->nodesetval))
3025     {
3026       xmlFreeDoc (doc);
3027       MERROR (MERROR_TEXTPROP, NULL);
3028     }
3029   for (i = 0, mt = mtext (); i < result->nodesetval->nodeNr; i++)
3030     {
3031       if (i > 0)
3032         mtext_cat_char (mt, 0);
3033       node = (xmlNodePtr) result->nodesetval->nodeTab[i];
3034       body_str = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
3035       if (body_str)
3036         {
3037           mtext__cat_data (mt, body_str, strlen ((char *) body_str),
3038                            MTEXT_FORMAT_UTF_8);
3039           xmlFree (body_str);
3040         }
3041     }
3042
3043   result = xmlXPathEvalExpression ((xmlChar *) "//property", context);
3044   if (! xmlXPathNodeSetIsEmpty (result->nodesetval))
3045     for (i = 0; i < result->nodesetval->nodeNr; i++)
3046       {
3047         MSymbol key;
3048         MTextPropDeserializeFunc func;
3049         MTextProperty *prop;
3050         MPlist *plist;
3051         int from, to, control;
3052         void *val;
3053
3054         key_str = xmlGetProp (result->nodesetval->nodeTab[i],
3055                               (xmlChar *) "key");
3056         val_str = xmlGetProp (result->nodesetval->nodeTab[i],
3057                               (xmlChar *) "value");
3058         from_str = xmlGetProp (result->nodesetval->nodeTab[i],
3059                                (xmlChar *) "from");
3060         to_str = xmlGetProp (result->nodesetval->nodeTab[i],
3061                              (xmlChar *) "to");
3062         ctl_str = xmlGetProp (result->nodesetval->nodeTab[i],
3063                               (xmlChar *) "control");
3064
3065         key = msymbol ((char *) key_str);
3066         func = ((MTextPropDeserializeFunc)
3067                 msymbol_get_func (key, Mtext_prop_deserializer));
3068         if (! func)
3069           continue;
3070         plist = mplist__from_string (val_str, strlen ((char *) val_str));
3071         if (! plist)
3072           continue;
3073         if (sscanf ((char *) from_str, "%d", &from) != 1
3074             || from < 0 || from >= mtext_nchars (mt))
3075           continue;
3076         if (sscanf ((char *) to_str, "%d", &to) != 1
3077             || to <= from || to > mtext_nchars (mt))
3078           continue;
3079         if (sscanf ((char *) ctl_str, "%d", &control) != 1
3080             || control < 0 || control > MTEXTPROP_CONTROL_MAX)
3081           continue;
3082         val = (func) (plist);
3083         M17N_OBJECT_UNREF (plist);
3084         prop = mtext_property (key, val, control);
3085         if (key->managing_key)
3086           M17N_OBJECT_UNREF (val);
3087         mtext_push_property (mt, from, to, prop);
3088         M17N_OBJECT_UNREF (prop);
3089
3090         xmlFree (key_str);
3091         xmlFree (val_str);
3092         xmlFree (from_str);
3093         xmlFree (to_str);
3094         xmlFree (ctl_str);
3095       }
3096   xmlXPathFreeContext (context);
3097   xmlFreeDoc (doc);
3098   return mt;
3099 #else  /* not HAVE_XML2 */
3100   MERROR (MERROR_TEXTPROP, NULL);
3101 #endif  /* not HAVE_XML2 */
3102 }
3103
3104 /*** @} */
3105
3106 /*
3107   Local Variables:
3108   coding: euc-japan
3109   End:
3110 */