e90f04a61e71e691dd5e96878e8423a13c3fc6a5
[m17n/m17n-lib.git] / src / textprop.c
1 /* textprop.c -- text property module.
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 /***en
24     @addtogroup 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 poistions 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 KEY is
1025    Mnil, we are going to delete text, thus both strongly and weakly
1026    volatile properties must be deleted.  Otherwise we are going to
1027    modify a text property KEY, thus only strongly volatile properties
1028    whose key is not KEY must be deleted.  */
1029
1030 static void
1031 prepare_to_modify (MText *mt, int from, int to, MSymbol key)
1032 {
1033   MTextPlist *plist = mt->plist, *prev = NULL;
1034   int mask_bits = MTEXTPROP_VOLATILE_STRONG;
1035   int deleting = (key == Mnil) && (from < to);
1036
1037   if (key == Mnil)
1038     mask_bits |= MTEXTPROP_VOLATILE_WEAK;
1039   while (plist)
1040     {
1041       if (plist->key != key
1042           && ! delete_properties (plist, from, to, mask_bits, deleting))
1043         {
1044           if (prev)
1045             plist = prev->next = free_textplist (plist);
1046           else
1047             plist = mt->plist = free_textplist (plist);
1048         }
1049       else
1050         prev = plist, plist = plist->next;
1051     }
1052 }
1053
1054 void
1055 extract_text_properties (MText *mt, int from, int to, MSymbol key,
1056                          MPlist *plist)
1057 {
1058   MPlist *top;
1059   MTextPlist *list = get_plist_create (mt, key, 0);
1060   MInterval *interval;
1061
1062   if (! list)
1063     return;
1064   interval = find_interval (list, from);
1065   if (interval->nprops == 0
1066       && interval->start <= from && interval->end >= to)
1067     return;
1068   top = plist;
1069   while (interval && interval->start < to)
1070     {
1071       if (interval->nprops == 0)
1072         top = mplist_find_by_key (top, Mnil);
1073       else
1074         {
1075           MPlist *current = top, *place;
1076           int i;
1077
1078           for (i = 0; i < interval->nprops; i++)
1079             {
1080               MTextProperty *prop = interval->stack[i];
1081
1082               place = mplist_find_by_value (current, prop);
1083               if (place)
1084                 current = MPLIST_NEXT (place);
1085               else
1086                 {
1087                   place = mplist_find_by_value (top, prop);
1088                   if (place)
1089                     {
1090                       mplist_pop (place);
1091                       if (MPLIST_NEXT (place) == MPLIST_NEXT (current))
1092                         current = place;
1093                     }
1094                   mplist_push (current, Mt, prop);
1095                   current = MPLIST_NEXT (current);
1096                 }
1097             }
1098         }
1099       interval = interval->next;
1100     }
1101   return;
1102 }
1103
1104 #define XML_TEMPLATE "<?xml version=\"1.0\" ?>\n\
1105 <!DOCTYPE mtext [\n\
1106   <!ELEMENT mtext (property*,body+)>\n\
1107   <!ELEMENT property EMPTY>\n\
1108   <!ELEMENT body (#PCDATA)>\n\
1109   <!ATTLIST property key CDATA #REQUIRED>\n\
1110   <!ATTLIST property value CDATA #REQUIRED>\n\
1111   <!ATTLIST property from CDATA #REQUIRED>\n\
1112   <!ATTLIST property to CDATA #REQUIRED>\n\
1113   <!ATTLIST property control CDATA #REQUIRED>\n\
1114  ]>\n\
1115 <mtext>\n\
1116 </mtext>"
1117
1118 \f
1119 /* for debugging... */
1120 #include <stdio.h>
1121
1122 void
1123 dump_interval (MInterval *interval, int indent)
1124 {
1125   char *prefix = (char *) alloca (indent + 1);
1126   int i;
1127
1128   memset (prefix, 32, indent);
1129   prefix[indent] = 0;
1130
1131   fprintf (stderr, "(interval %d-%d (%d)", interval->start, interval->end,
1132            interval->nprops);
1133   for (i = 0; i < interval->nprops; i++)
1134     fprintf (stderr, "\n%s (%d %d/%d %d-%d 0x%x)",
1135              prefix, i,
1136              interval->stack[i]->control.ref_count,
1137              interval->stack[i]->attach_count,
1138              interval->stack[i]->start, interval->stack[i]->end,
1139              (unsigned) interval->stack[i]->val);
1140   fprintf (stderr, ")");
1141 }
1142
1143 void
1144 dump_textplist (MTextPlist *plist, int indent)
1145 {
1146   char *prefix = (char *) alloca (indent + 1);
1147
1148   memset (prefix, 32, indent);
1149   prefix[indent] = 0;
1150
1151   fprintf (stderr, "(properties");
1152   if (! plist)
1153     fprintf (stderr, ")\n");
1154   else
1155     {
1156       fprintf (stderr, "\n");
1157       while (plist)
1158         {
1159           MInterval *interval = plist->head;
1160
1161           fprintf (stderr, "%s (%s", prefix, msymbol_name (plist->key));
1162           while (interval)
1163             {
1164               fprintf (stderr, " (%d %d", interval->start, interval->end);
1165               if (interval->nprops > 0)
1166                 {
1167                   int i;
1168
1169                   for (i = 0; i < interval->nprops; i++)
1170                     fprintf (stderr, " 0x%x", (int) interval->stack[i]->val);
1171                 }
1172               fprintf (stderr, ")");
1173               interval = interval->next;
1174             }
1175           fprintf (stderr, ")\n");
1176           xassert (check_plist (plist, 0) == 0);
1177           plist = plist->next;
1178         }
1179     }
1180 }
1181
1182 \f
1183 /* Internal API */
1184
1185 int
1186 mtext__prop_init ()
1187 {
1188   text_property_table.count = 0;
1189   Mtext_prop_serializer = msymbol ("text-prop-serializer");
1190   Mtext_prop_deserializer = msymbol ("text-prop-deserializer");
1191   return 0;
1192 }
1193
1194 void
1195 mtext__prop_fini ()
1196 {
1197   MIntervalPool *pool = interval_pool_root.next;
1198
1199   while (pool)
1200     {
1201       MIntervalPool *next = pool->next;
1202       free (pool);
1203       pool = next;
1204     }
1205   interval_pool_root.next = NULL;  
1206   mdebug__report_object ("Text property", &text_property_table);
1207 }
1208
1209
1210 /** Free all plists.  */
1211
1212 void
1213 mtext__free_plist (MText *mt){
1214
1215   MTextPlist *plist = mt->plist;
1216
1217   while (plist)
1218     plist = free_textplist (plist);
1219   mt->plist = NULL;
1220 }
1221
1222
1223 /** Extract intervals between FROM and TO of all properties (except
1224     for volatile ones) in PLIST, and make a new plist from them for
1225     M-text MT.  */
1226
1227 MTextPlist *
1228 mtext__copy_plist (MTextPlist *plist, int from, int to, MText *mt, int pos)
1229 {
1230   MTextPlist *copy, *this;
1231
1232   if (from == to)
1233     return NULL;
1234   for (copy = NULL; plist && ! copy; plist = plist->next)
1235     copy = copy_single_property (plist, from, to, mt, pos);
1236   if (! plist)
1237     return copy;
1238   for (; plist; plist = plist->next)
1239     if ((this = copy_single_property (plist, from, to, mt, pos)))
1240       {
1241         this->next = copy;
1242         copy = this;
1243       }
1244
1245   return copy;
1246 }
1247
1248 void
1249 mtext__adjust_plist_for_delete (MText *mt, int pos, int len)
1250 {
1251   MTextPlist *plist;
1252   int to;
1253
1254   if (len == 0 || pos == mt->nchars)
1255     return;
1256   if (len == mt->nchars)
1257     {
1258       mtext__free_plist (mt);
1259       return;
1260     }      
1261
1262   to = pos + len;
1263   prepare_to_modify (mt, pos, to, Mnil);
1264   for (plist = mt->plist; plist; plist = plist->next)
1265     {
1266       MInterval *interval = pop_all_properties (plist, pos, to);
1267       MInterval *prev = interval->prev, *next = interval->next;
1268
1269       if (prev)
1270         prev->next = next;
1271       else
1272         plist->head = next;
1273       if (next)
1274         {
1275           adjust_intervals (next, plist->tail, -len);
1276           next->prev = prev;
1277         }
1278       else
1279         plist->tail = prev;
1280       if (prev && next)
1281         next = maybe_merge_interval (plist, prev);
1282       plist->cache = next ? next : prev;
1283       free_interval (interval);
1284       xassert (check_plist (plist, 0) == 0);
1285     }
1286 }
1287
1288 void
1289 mtext__adjust_plist_for_insert (MText *mt, int pos, int nchars,
1290                                 MTextPlist *plist)
1291 {
1292   MTextPlist *pl, *pl_last, *pl2, *p;
1293   int i;
1294   MInterval *interval;
1295
1296   if (mt->nchars == 0)
1297     {
1298       mtext__free_plist (mt);
1299       mt->plist = plist;
1300       return;
1301     }
1302   if (pos > 0 && pos < mtext_nchars (mt))
1303     prepare_to_modify (mt, pos, pos, Mnil);
1304
1305   for (pl_last = NULL, pl = mt->plist; pl; pl_last = pl, pl = pl->next)
1306     {
1307       MInterval *interval, *prev, *next, *head, *tail;
1308
1309       if (pos == 0)
1310         prev = NULL, next = pl->head;
1311       else if (pos == mtext_nchars (mt))
1312         prev = pl->tail, next = NULL;
1313       else
1314         {
1315           next = find_interval (pl, pos);
1316           if (next->start < pos)
1317             {
1318               divide_interval (pl, next, pos);
1319               next = next->next;
1320             }
1321           for (i = 0; i < next->nprops; i++)
1322             if (next->stack[i]->start < pos)
1323               split_property (next->stack[i], next);
1324           prev = next->prev;
1325         }
1326
1327       xassert (check_plist (pl, 0) == 0);
1328       for (p = NULL, pl2 = plist; pl2 && pl->key != pl2->key;
1329            p = pl2, pl2 = p->next);
1330       if (pl2)
1331         {
1332           xassert (check_plist (pl2, pl2->head->start) == 0);
1333           if (p)
1334             p->next = pl2->next;
1335           else
1336             plist = plist->next;
1337
1338           head = pl2->head;
1339           tail = pl2->tail;
1340           free (pl2);
1341         }
1342       else
1343         {
1344           head = tail = new_interval (pos, pos + nchars);
1345         }
1346       head->prev = prev;
1347       tail->next = next;
1348       if (prev)
1349         prev->next = head;
1350       else
1351         pl->head = head;
1352       if (next)
1353         next->prev = tail;
1354       else
1355         pl->tail = tail;
1356       if (next)
1357         adjust_intervals (next, pl->tail, nchars);
1358
1359       xassert (check_plist (pl, 0) == 0);
1360       if (prev && prev->nprops > 0)
1361         {
1362           for (interval = prev;
1363                interval->next != next && interval->next->nprops == 0;
1364                interval = interval->next)
1365             for (i = 0; i < interval->nprops; i++)
1366               {
1367                 MTextProperty *prop = interval->stack[i];
1368
1369                 if (prop->control.flag & MTEXTPROP_REAR_STICKY)
1370                   PUSH_PROP (interval->next, prop);
1371               }
1372         }
1373       xassert (check_plist (pl, 0) == 0);
1374       if (next && next->nprops > 0)
1375         {
1376           for (interval = next;
1377                interval->prev != prev && interval->prev->nprops == 0;
1378                interval = interval->prev)
1379             for (i = 0; i < interval->nprops; i++)
1380               {
1381                 MTextProperty *prop = interval->stack[i];
1382
1383                 if (prop->control.flag & MTEXTPROP_FRONT_STICKY)
1384                   PUSH_PROP (interval->prev, prop);
1385               }
1386         }
1387
1388       interval = prev ? prev : pl->head;
1389       pl->cache = interval;
1390       while (interval && interval->start <= pos + nchars)
1391         interval = maybe_merge_interval (pl, interval);
1392       xassert (check_plist (pl, 0) == 0);
1393     }
1394
1395   if (pl_last)
1396     pl_last->next = plist;
1397   else
1398     mt->plist = plist;
1399
1400   for (; plist; plist = plist->next)
1401     {
1402       plist->cache = plist->head;
1403       if (pos > 0)
1404         {
1405           if (plist->head->nprops)
1406             {
1407               interval = new_interval (0, pos);
1408               interval->next = plist->head;
1409               plist->head->prev = interval;
1410               plist->head = interval;
1411             }
1412           else
1413             plist->head->start = 0;
1414         }
1415       if (pos < mtext_nchars (mt))
1416         {
1417           if (plist->tail->nprops)
1418             {
1419               interval = new_interval (pos + nchars,
1420                                        mtext_nchars (mt) + nchars);
1421               interval->prev = plist->tail;
1422               plist->tail->next = interval;
1423               plist->tail = interval;
1424             }
1425           else
1426             plist->tail->end = mtext_nchars (mt) + nchars;
1427         }
1428       xassert (check_plist (plist, 0) == 0);
1429     }
1430 }
1431
1432 void
1433 mtext__adjust_plist_for_change (MText *mt, int from, int to)
1434 {
1435   MTextPlist *plist;
1436
1437   prepare_to_modify (mt, from, to, Mnil);
1438   for (plist = mt->plist; plist; plist = plist->next)
1439     {
1440       pop_all_properties (plist, from, to);
1441       xassert (check_plist (plist, 0) == 0);
1442     }
1443 }
1444
1445
1446 /*** @} */
1447 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1448
1449 \f
1450 /** External API */
1451
1452 /*** @addtogroup m17nTextProperty */
1453 /*** @{  */
1454
1455 /*=*/
1456 /***en
1457     @brief Get the value of the topmost text property.
1458
1459     The mtext_get_prop () function searches the character at $POS in
1460     M-text $MT for the text property whose key is $KEY.
1461
1462     @return
1463     If a text property is found, mtext_get_prop () returns the value
1464     of the property.  If the property has multiple values, it returns
1465     the topmost one.  If no such property is found, it returns @c NULL
1466     without changing the external variable #merror_code.
1467
1468     If an error is detected, mtext_get_prop () returns @c NULL and
1469     assigns an error code to the external variable #merror_code.
1470
1471     @note If @c NULL is returned without an error, there are two
1472     possibilities:
1473
1474     @li  the character at $POS does not have a property whose key is $KEY, or 
1475
1476     @li  the character does have such a property and its value is @c NULL.  
1477
1478     If you need to distinguish these two cases, use the
1479     mtext_get_prop_values () function instead.  */
1480
1481 /***ja
1482     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î°ìÈÖ¾å¤ÎÃͤòÆÀ¤ë.
1483
1484     ´Ø¿ô mtext_get_prop () ¤Ï¡¢M-text $MT Æâ¤Î°ÌÃÖ $POS ¤Ë¤¢¤ëʸ»ú¤Î¥Æ
1485     ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£
1486
1487     @return
1488     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬¤ß¤Ä¤«¤ì¤Ð¡¢mtext_get_prop () ¤Ï¤½¤Î¥×¥í¥Ñ¥Æ¥£
1489     ¤ÎÃͤòÊÖ¤¹¡£Ãͤ¬Ê£¿ô¸ºß¤¹¤ë¤È¤­¤Ï¡¢°ìÈÖ¾å¤ÎÃͤòÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±
1490     ¤ì¤Ð³°ÉôÊÑ¿ô #merror_code ¤òÊѹ¹¤¹¤ë¤³¤È¤Ê¤¯ @c NULL ¤òÊÖ¤¹¡£
1491
1492     ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç mtext_get_prop () ¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ
1493     ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1494
1495     @note ¥¨¥é¡¼¤Ê¤·¤Ç @c NULL ¤¬ÊÖ¤µ¤ì¤¿¾ì¹ç¤Ë¤ÏÆó¤Ä¤Î²ÄǽÀ­¤¬¤¢¤ë¡£
1496
1497     @li $POS ¤Î°ÌÃÖ¤Îʸ»ú¤Ï $KEY ¤ò¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¡£
1498
1499     @li ¤½¤Îʸ»ú¤Ï¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Á¡¢¤½¤ÎÃͤ¬ @c NULL ¤Ç¤¢¤ë¡£
1500
1501     ¤³¤ÎÆó¤Ä¤ò¶èÊ̤¹¤ëɬÍפ¬¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢´Ø¿ô mtext_get_prop_values ()
1502     ¤òÂå¤ï¤ê¤Ë»ÈÍѤ¹¤ë¤³¤È¡£
1503
1504      @latexonly \IPAlabel{mtext_get_prop} @endlatexonly  */
1505
1506 /***
1507     @errors
1508     @c MERROR_RANGE, @c MERROR_SYMBOL
1509
1510     @seealso
1511     mtext_get_prop_values (), mtext_put_prop (), mtext_put_prop_values (),
1512     mtext_push_prop (), mtext_pop_prop (), mtext_prop_range ()  */
1513
1514 void *
1515 mtext_get_prop (MText *mt, int pos, MSymbol key)
1516 {
1517   MTextPlist *plist;
1518   MInterval *interval;
1519   void *val;
1520
1521   M_CHECK_POS (mt, pos, NULL);
1522
1523   plist = get_plist_create (mt, key, 0);
1524   if (! plist)
1525     return NULL;
1526
1527   interval = find_interval (plist, pos);
1528   val = (interval->nprops
1529          ? interval->stack[interval->nprops - 1]->val : NULL);
1530   return val;
1531 }
1532
1533 /*=*/
1534
1535 /***en
1536     @brief Get multiple values of a text property.
1537
1538     The mtext_get_prop_values () function searches the character at
1539     $POS in M-text $MT for the property whose key is $KEY.  If such
1540     a property is found, its values are stored in the memory area
1541     pointed to by $VALUES.  $NUM limits the maximum number of stored
1542     values.
1543
1544     @return
1545     If the operation was successful, mtext_get_prop_values () returns
1546     the number of actually stored values.  If the character at $POS
1547     does not have a property whose key is $KEY, the return value is
1548     0. If an error is detected, mtext_get_prop_values () returns -1 and
1549     assigns an error code to the external variable #merror_code.  */
1550
1551 /***ja
1552     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊ£¿ô¸ÄÆÀ¤ë.
1553
1554     ´Ø¿ô mtext_get_prop_values () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤È¤¤¤¦°ÌÃÖ
1555     ¤Ë¤¢¤ëʸ»ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£¤â¤·¤½
1556     ¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬¸«¤Ä¤«¤ì¤Ð¡¢¤½¤ì¤¬»ý¤ÄÃÍ (Ê£¿ô²Ä) ¤ò $VALUES 
1557     ¤Î»Ø¤¹¥á¥â¥êÎΰè¤Ë³ÊǼ¤¹¤ë¡£$NUM ¤Ï³ÊǼ¤¹¤ëÃͤοô¤Î¾å¸Â¤Ç¤¢¤ë¡£
1558
1559     @return
1560     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_get_prop_values () ¤Ï¼ÂºÝ¤Ë¥á¥â¥ê¤Ë³ÊǼ¤µ
1561     ¤ì¤¿Ãͤοô¤òÊÖ¤¹¡£$POS ¤Î°ÌÃÖ¤Îʸ»ú¤¬ $KEY ¤ò¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£
1562     ¤ò»ý¤¿¤Ê¤±¤ì¤Ð 0 ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°Éô
1563     ÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1564
1565     @latexonly \IPAlabel{mtext_get_prop_values} @endlatexonly  */
1566
1567 /***
1568     @errors
1569     @c MERROR_RANGE, @c MERROR_SYMBOL
1570
1571     @seealso
1572     mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (),
1573     mtext_push_prop (), mtext_pop_prop (), mtext_prop_range ()  */
1574
1575 int
1576 mtext_get_prop_values (MText *mt, int pos, MSymbol key,
1577                        void **values, int num)
1578 {
1579   MTextPlist *plist;
1580   MInterval *interval;
1581   int nprops;
1582   int i;
1583   int offset;
1584
1585   M_CHECK_POS (mt, pos, -1);
1586
1587   plist = get_plist_create (mt, key, 0);
1588   if (! plist)
1589     return 0;
1590
1591   interval = find_interval (plist, pos);
1592   /* It is assured that INTERVAL is not NULL.  */
1593   nprops = interval->nprops;
1594   if (nprops == 0 || num <= 0)
1595     return 0;
1596   if (nprops == 1 || num == 1)
1597     {
1598       values[0] = interval->stack[nprops - 1]->val;
1599       return 1;
1600     }
1601
1602   if (nprops <= num)
1603     num = nprops, offset = 0;
1604   else
1605     offset = nprops - num;
1606   for (i = 0; i < num; i++)
1607     values[i] = interval->stack[offset + i]->val;
1608   return num;
1609 }
1610
1611 /*=*/
1612
1613 /***en
1614     @brief Get a list of text property keys at a position of an M-text.
1615
1616     The mtext_get_prop_keys () function creates an array whose
1617     elements are the keys of text properties found at position $POS in
1618     M-text $MT, and sets *$KEYS to the address of the created array.
1619     The user is responsible to free the memory allocated for
1620     the array.
1621
1622     @returns
1623     If the operation was successful, mtext_get_prop_keys () returns
1624     the length of the key list.  Otherwise it returns -1 and assigns
1625     an error code to the external variable #merror_code.
1626
1627 */
1628
1629 /***ja
1630     @brief M-text ¤Î»ØÄꤷ¤¿°ÌÃ֤Υƥ­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤Î¥ê¥¹¥È¤òÆÀ¤ë.
1631
1632     ´Ø¿ô mtext_get_prop_keys () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤Î°ÌÃ֤ˤ¢¤ë
1633     ¤¹¤Ù¤Æ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤òÍ×ÁǤȤ¹¤ëÇÛÎó¤òºî¤ê¡¢¤½¤ÎÇÛÎó¤Î
1634     ¥¢¥É¥ì¥¹¤ò *$KEYS ¤ËÀßÄꤹ¤ë¡£¤³¤ÎÇÛÎó¤Î¤¿¤á¤Ë³ÎÊݤµ¤ì¤¿¥á¥â¥ê¤ò²ò
1635     Êü¤¹¤ë¤Î¤Ï¥æ¡¼¥¶¤ÎÀÕǤ¤Ç¤¢¤ë¡£
1636
1637     @return
1638     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_get_prop_keys () ¤ÏÆÀ¤é¤ì¤¿¥ê¥¹¥È¤ÎŤµ¤òÊÖ
1639     ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò
1640     ÀßÄꤹ¤ë¡£
1641 */
1642
1643 /***
1644     @errors
1645     @c MERROR_RANGE
1646
1647     @seealso
1648     mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (),
1649     mtext_get_prop_values (), mtext_push_prop (), mtext_pop_prop ()  */
1650
1651 int
1652 mtext_get_prop_keys (MText *mt, int pos, MSymbol **keys)
1653 {
1654   MTextPlist *plist;
1655   int i;
1656
1657   M_CHECK_POS (mt, pos, -1);
1658   for (i = 0, plist = mt->plist; plist; i++, plist = plist->next);
1659   if (i == 0)
1660     {
1661       *keys = NULL;
1662       return 0;
1663     }
1664   MTABLE_MALLOC (*keys, i, MERROR_TEXTPROP);
1665   for (i = 0, plist = mt->plist; plist; plist = plist->next)
1666     {
1667       MInterval *interval = find_interval (plist, pos);
1668
1669       if (interval->nprops)
1670         (*keys)[i++] = plist->key;
1671     }
1672   return i;
1673 }
1674
1675 /*=*/
1676
1677 /***en
1678     @brief Set a text property.
1679
1680     The mtext_put_prop () function sets a text property to the
1681     characters between $FROM (inclusive) and $TO (exclusive) in M-text
1682     $MT.  $KEY and $VAL specify the key and the value of the text
1683     property.  With this function,
1684
1685 @verbatim
1686                      FROM                   TO
1687 M-text: |<------------|-------- MT ---------|------------>|
1688 PROP  :  <------------------ OLD_VAL -------------------->
1689 @endverbatim
1690
1691    becomes
1692
1693 @verbatim
1694                      FROM                   TO
1695 M-text: |<------------|-------- MT ---------|------------>|
1696 PROP  :  <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL-->
1697 @endverbatim
1698
1699     @return
1700     If the operation was successful, mtext_put_prop () returns 0.
1701     Otherwise it returns -1 and assigns an error code to the external
1702     variable #merror_code.  */
1703
1704 /***ja
1705     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ¤ë.
1706
1707     ´Ø¿ô mtext_put_prop () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é 
1708     $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥­¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¤è
1709     ¤¦¤Ê¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ¤ë¡£¤³¤Î´Ø¿ô¤Ë¤è¤Ã¤Æ
1710
1711
1712 @verbatim
1713                          FROM                    TO
1714 M-text:      |<------------|-------- MT ---------|------------>|
1715 PROP:         <------------------ OLD_VAL -------------------->
1716 @endverbatim
1717
1718 ¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¡£
1719
1720 @verbatim
1721                          FROM                    TO
1722 M-text:       |<------------|-------- MT ---------|------------>|
1723 PROP:          <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL-->
1724 @endverbatim
1725
1726     @return
1727     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_put_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 
1728     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1729
1730     @latexonly \IPAlabel{mtext_put_prop} @endlatexonly  */
1731
1732 /***
1733     @errors
1734     @c MERROR_RANGE, @c MERROR_SYMBOL
1735
1736     @seealso
1737     mtext_put_prop_values (), mtext_get_prop (),
1738     mtext_get_prop_values (), mtext_push_prop (),
1739     mtext_pop_prop (), mtext_prop_range ()  */
1740
1741 int
1742 mtext_put_prop (MText *mt, int from, int to, MSymbol key, void *val)
1743 {
1744   MTextPlist *plist;
1745   MTextProperty *prop;
1746   MInterval *interval;
1747
1748   M_CHECK_RANGE (mt, from, to, -1, 0);
1749
1750   prepare_to_modify (mt, from, to, key);
1751   plist = get_plist_create (mt, key, 1);
1752   interval = pop_all_properties (plist, from, to);
1753   prop = new_text_property (mt, from, to, key, val, 0);
1754   PUSH_PROP (interval, prop);
1755   M17N_OBJECT_UNREF (prop);
1756   if (interval->next)
1757     maybe_merge_interval (plist, interval);
1758   if (interval->prev)
1759     maybe_merge_interval (plist, interval->prev);
1760   xassert (check_plist (plist, 0) == 0);
1761   return 0;
1762 }
1763
1764 /*=*/
1765
1766 /***en
1767     @brief Set multiple text properties with the same key.
1768
1769     The mtext_put_prop_values () function sets a text property to the
1770     characters between $FROM (inclusive) and $TO (exclusive) in M-text
1771     $MT.  $KEY and $VALUES specify the key and the values of the text
1772     property.  $NUM specifies the number of property values to be set.
1773
1774     @return
1775     If the operation was successful, mtext_put_prop_values () returns
1776     0.  Otherwise it returns -1 and assigns an error code to the
1777     external variable #merror_code.  */
1778
1779 /***ja
1780     @brief Æ±¤¸¥­¡¼¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÊ£¿ôÀßÄꤹ¤ë.
1781
1782     ´Ø¿ô mtext_put_prop_values () ¤Ï¡¢M-Text $MT ¤Î$FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë
1783     ¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ
1784     ¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤Ï $KEY ¤Ë¤è¤Ã¤Æ¡¢ÃÍ(Ê£¿ô²Ä)¤Ï $VALUES 
1785     ¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤ë¡£$NUM ¤ÏÀßÄꤵ¤ì¤ëÃͤθĿô¤Ç¤¢¤ë¡£
1786
1787     @return
1788     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_put_prop_values () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±
1789     ¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1790
1791     @latexonly \IPAlabel{mtext_put_prop_values} @endlatexonly  */
1792
1793 /***
1794     @errors
1795     @c MERROR_RANGE, @c MERROR_SYMBOL
1796
1797     @seealso
1798     mtext_put_prop (), mtext_get_prop (), mtext_get_prop_values (),
1799     mtext_push_prop (), mtext_pop_prop (), mtext_prop_range ()  */
1800
1801 int
1802 mtext_put_prop_values (MText *mt, int from, int to,
1803                        MSymbol key, void **values, int num)
1804 {
1805   MTextPlist *plist;
1806   MInterval *interval;
1807   int i;
1808
1809   M_CHECK_RANGE (mt, from, to, -1, 0);
1810
1811   prepare_to_modify (mt, from, to, key);
1812   plist = get_plist_create (mt, key, 1);
1813   interval = pop_all_properties (plist, from, to);
1814   if (num > 0)
1815     {
1816       PREPARE_INTERVAL_STACK (interval, num);
1817       for (i = 0; i < num; i++)
1818         {
1819           MTextProperty *prop
1820             = new_text_property (mt, from, to, key, values[i], 0);
1821           PUSH_PROP (interval, prop);
1822           M17N_OBJECT_UNREF (prop);
1823         }
1824     }
1825   if (interval->next)
1826     maybe_merge_interval (plist, interval);
1827   if (interval->prev)
1828     maybe_merge_interval (plist, interval->prev);
1829   xassert (check_plist (plist, 0) == 0);
1830   return 0;
1831 }
1832
1833 /*=*/
1834
1835 /***en
1836     @brief Push a text property.
1837
1838     The mtext_push_prop () function pushes a text property whose key
1839     is $KEY and value is $VAL to the characters between $FROM
1840     (inclusive) and $TO (exclusive) in M-text $MT.  With this
1841     function,
1842
1843 @verbatim
1844                     FROM                    TO
1845 M-text: |<------------|-------- MT ---------|------------>|
1846 PROP  :  <------------------ OLD_VAL -------------------->
1847 @endverbatim
1848
1849     becomes
1850
1851 @verbatim 
1852                     FROM                    TO
1853 M-text: |<------------|-------- MT ---------|------------>|
1854 PROP  :  <------------------- OLD_VAL ------------------->
1855 PROP  :               <-------- VAL ------->
1856 @endverbatim
1857
1858     @return
1859     If the operation was successful, mtext_push_prop () returns 0.
1860     Otherwise it returns -1 and assigns an error code to the external
1861     variable #merror_code.  */
1862
1863 /***ja
1864     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥×¥Ã¥·¥å¤¹¤ë.
1865
1866     ´Ø¿ô mtext_push_prop () ¤Ï¡¢¥­¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¥Æ¥­¥¹¥È
1867     ¥×¥í¥Ñ¥Æ¥£¤ò¡¢M-text $MT Ãæ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê
1868     ¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¥×¥Ã¥·¥å¤¹¤ë¡£¤³¤Î´Ø¿ô¤Ë¤è¤Ã¤Æ
1869
1870 @verbatim
1871                     FROM                    TO
1872 M-text: |<------------|-------- MT ---------|------------>|
1873 PROP  :  <------------------ OLD_VAL -------------------->
1874 @endverbatim
1875  ¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¡£
1876 @verbatim 
1877                     FROM                    TO
1878 M-text: |<------------|-------- MT ---------|------------>|
1879 PROP  :  <------------------- OLD_VAL ------------------->
1880 PROP  :               <-------- VAL ------->
1881 @endverbatim
1882
1883     @return
1884     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_push_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð 
1885     -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1886
1887     @latexonly \IPAlabel{mtext_push_prop} @endlatexonly  */
1888
1889 /***
1890     @errors
1891     @c MERROR_RANGE, @c MERROR_SYMBOL
1892
1893     @seealso
1894     mtext_put_prop (), mtext_put_prop_values (),
1895     mtext_get_prop (), mtext_get_prop_values (),
1896     mtext_pop_prop (), mtext_prop_range ()  */
1897
1898 int
1899 mtext_push_prop (MText *mt, int from, int to,
1900                  MSymbol key, void *val)
1901 {
1902   MTextPlist *plist;
1903   MInterval *head, *tail, *interval;
1904   MTextProperty *prop;
1905   int check_head, check_tail;
1906
1907   M_CHECK_RANGE (mt, from, to, -1, 0);
1908
1909   prepare_to_modify (mt, from, to, key);
1910   plist = get_plist_create (mt, key, 1);
1911
1912   /* Find an interval that covers the position FROM.  */
1913   head = find_interval (plist, from);
1914
1915   /* If the found interval starts before FROM, divide it at FROM.  */
1916   if (head->start < from)
1917     {
1918       divide_interval (plist, head, from);
1919       head = head->next;
1920       check_head = 0;
1921     }
1922   else
1923     check_head = 1;
1924
1925   /* Find an interval that ends at TO.  If TO is not at the end of an
1926      interval, make one that ends at TO.  */
1927   if (head->end == to)
1928     {
1929       tail = head;
1930       check_tail = 1;
1931     }
1932   else if (head->end > to)
1933     {
1934       divide_interval (plist, head, to);
1935       tail = head;
1936       check_tail = 0;
1937     }
1938   else
1939     {
1940       tail = find_interval (plist, to);
1941       if (! tail)
1942         {
1943           tail = plist->tail;
1944           check_tail = 0;
1945         }
1946       else if (tail->start == to)
1947         {
1948           tail = tail->prev;
1949           check_tail = 1;
1950         }
1951       else
1952         {
1953           divide_interval (plist, tail, to);
1954           check_tail = 0;
1955         }
1956     }
1957
1958   prop = new_text_property (mt, from, to, key, val, 0);
1959
1960   /* Push PROP to the current values of intervals between HEAD and TAIL
1961      (both inclusive).  */
1962   for (interval = head; ; interval = interval->next)
1963     {
1964       PUSH_PROP (interval, prop);
1965       if (interval == tail)
1966         break;
1967     }
1968
1969   M17N_OBJECT_UNREF (prop);
1970
1971   /* If there is a possibility that TAIL now has the same value as the
1972      next one, check it and concatenate them if necessary.  */
1973   if (tail->next && check_tail)
1974     maybe_merge_interval (plist, tail);
1975
1976   /* If there is a possibility that HEAD now has the same value as the
1977      previous one, check it and concatenate them if necessary.  */
1978   if (head->prev && check_head)
1979     maybe_merge_interval (plist, head->prev);
1980
1981   xassert (check_plist (plist, 0) == 0);
1982   return 0;
1983 }
1984
1985 /*=*/
1986
1987 /***en
1988     @brief Pop a text property.
1989
1990     The mtext_pop_prop () function removes the topmost text property
1991     whose key is $KEY from the characters between $FROM (inclusive)
1992     and and $TO (exclusive) in $MT.
1993
1994     This function does nothing if characters in the region have no
1995     such text property. With this function,
1996
1997 @verbatim
1998                     FROM                    TO
1999 M-text: |<------------|-------- MT ---------|------------>|
2000 PROP  :  <------------------ OLD_VAL -------------------->
2001 @endverbatim
2002
2003     becomes
2004
2005 @verbatim 
2006                     FROM                    TO
2007 M-text: |<------------|-------- MT ---------|------------>|
2008 PROP  :  <--OLD_VAL-->|                     |<--OLD_VAL-->|
2009 @endverbatim
2010
2011     @return
2012     If the operation was successful, mtext_pop_prop () return 0.
2013     Otherwise it returns -1 and assigns an error code to the external
2014     variable #merror_code.  */
2015
2016 /***ja
2017     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ý¥Ã¥×¤¹¤ë.
2018
2019     ´Ø¿ô mtext_pop_prop () ¤Ï¡¢¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î
2020     ¤¦¤Á°ìÈÖ¾å¤Î¤â¤Î¤ò¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO¡Ê´Þ¤Þ
2021     ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤«¤é¼è¤ê½ü¤¯¡£
2022
2023     »ØÄêÈϰϤÎʸ»ú¤¬¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¤Ê¤é¤Ð¡¢¤³¤Î´Ø¿ô¤Ï²¿
2024     ¤â¤·¤Ê¤¤¡£¤³¤Î´Ø¿ô¤Ë¤è¤Ã¤Æ¡¢
2025
2026 @verbatim
2027                     FROM                    TO
2028 M-text: |<------------|-------- MT ---------|------------>|
2029 PROP  :  <------------------ OLD_VAL -------------------->
2030 @endverbatim
2031  ¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£
2032 @verbatim 
2033                     FROM                    TO
2034 M-text: |<------------|-------- MT ---------|------------>|
2035 PROP  :  <--OLD_VAL-->|                     |<--OLD_VAL-->|
2036 @endverbatim
2037
2038     @return
2039     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_pop_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 
2040     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
2041
2042     @latexonly \IPAlabel{mtext_pop_prop} @endlatexonly  */
2043
2044 /***
2045     @errors
2046     @c MERROR_RANGE, @c MERROR_SYMBOL
2047
2048     @seealso
2049     mtext_put_prop (), mtext_put_prop_values (),
2050     mtext_get_prop (), mtext_get_prop_values (),
2051     mtext_push_prop (), mtext_prop_range ()  */
2052
2053 int
2054 mtext_pop_prop (MText *mt, int from, int to, MSymbol key)
2055 {
2056   MTextPlist *plist;
2057   MInterval *head, *tail;
2058   int check_head = 1;
2059
2060   if (key == Mnil)
2061     MERROR (MERROR_TEXTPROP, -1);
2062   M_CHECK_RANGE (mt, from, to, -1, 0);
2063   plist = get_plist_create (mt, key, 0);
2064   if (! plist)
2065     return 0;
2066
2067   /* Find an interval that covers the position FROM.  */
2068   head = find_interval (plist, from);
2069   if (head->end >= to
2070       && head->nprops == 0)
2071     /* No property to pop.  */
2072     return 0;
2073
2074   prepare_to_modify (mt, from, to, key);
2075
2076   /* If the found interval starts before FROM and has value(s), divide
2077      it at FROM.  */
2078   if (head->start < from)
2079     {
2080       if (head->nprops > 0)
2081         {
2082           divide_interval (plist, head, from);
2083           check_head = 0;
2084         }
2085       else
2086         from = head->end;
2087       head = head->next;
2088     }
2089
2090   /* Pop the topmost text property from each interval following HEAD.
2091      Stop at an interval that ends after TO.  */
2092   for (tail = head; tail && tail->end <= to; tail = tail->next)
2093     if (tail->nprops > 0)
2094       POP_PROP (tail);
2095
2096   if (tail)
2097     {
2098       if (tail->start < to)
2099         {
2100           if (tail->nprops > 0)
2101             {
2102               divide_interval (plist, tail, to);
2103               POP_PROP (tail);
2104             }
2105           to = tail->start;
2106         }
2107       else
2108         to = tail->end;
2109     }
2110   else
2111     to = plist->tail->start;
2112
2113   /* If there is a possibility that HEAD now has the same text
2114      properties as the previous one, check it and concatenate them if
2115      necessary.  */
2116   if (head->prev && check_head)
2117     head = head->prev;
2118   while (head && head->end <= to)
2119     head = maybe_merge_interval (plist, head);
2120
2121   xassert (check_plist (plist, 0) == 0);
2122   return 0;
2123 }
2124
2125 /*=*/
2126
2127 /***en
2128     @brief Find the range where the value of a text property is the same.
2129
2130     The mtext_prop_range () function investigates the extent where all
2131     characters have the same value for a text property.  It first
2132     finds the value of the property specified by $KEY of the character
2133     at $POS in M-text $MT.  Then it checks if adjacent characters have
2134     the same value for the property $KEY.  The beginning and the end
2135     of the found range are stored to the variable pointed to by $FROM
2136     and $TO.  The character position stored in $FROM is inclusive but
2137     that in $TO is exclusive; this fashion is compatible with the
2138     range specification in the mtext_put_prop () function, etc.
2139
2140     If $DEEPER is not 0, not only the topmost but also all the stacked
2141     properties whose key is $KEY are compared.
2142
2143     If $FROM is @c NULL, the beginning of range is not searched for.  If
2144     $TO is @c NULL, the end of range is not searched for.
2145
2146     @return
2147
2148     If the operation was successful, mtext_prop_range () returns the
2149     number of values the property $KEY has at pos.  Otherwise it
2150     returns -1 and assigns an error code to the external variable @c
2151     merror_code.  */
2152
2153 /***ja
2154     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬Æ±¤¸Ãͤò¤È¤ëÈϰϤòÄ´¤Ù¤ë.
2155
2156     ´Ø¿ô mtext_prop_range () ¤Ï¡¢»ØÄꤷ¤¿¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤ¬Æ±¤¸
2157     ¤Ç¤¢¤ëϢ³¤·¤¿Ê¸»ú¤ÎÈϰϤòÄ´¤Ù¤ë¡£¤Þ¤º M-text $MT ¤Î $POS ¤Î°ÌÃÖ¤Ë
2158     ¤¢¤ëʸ»ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥­¡¼ $KEY ¤Ç»ØÄꤵ¤ì¤¿¤â¤ÎÃͤò¸«¤Ä¤±
2159     ¤ë¡£¤½¤·¤ÆÁ°¸å¤Îʸ»ú¤â $KEY ¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤ¬Æ±¤¸¤Ç¤¢¤ë¤«¤É¤¦¤«¤ò
2160     Ä´¤Ù¤ë¡£¸«¤Ä¤±¤¿ÈϰϤκǽé¤ÈºÇ¸å¤ò¡¢¤½¤ì¤¾¤ì $FROM ¤È $TO ¤Ë¥Ý¥¤¥ó
2161     ¥È¤µ¤ì¤ëÊÑ¿ô¤ËÊݸ¤¹¤ë¡£$FROM ¤ËÊݸ¤µ¤ì¤ëʸ»ú¤Î°ÌÃ֤ϸ«¤Ä¤±¤¿ÈÏ°Ï
2162     ¤Ë´Þ¤Þ¤ì¤ë¤¬¡¢$TO ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£¡Ê$TO ¤ÎÁ°¤ÇƱ¤¸Ãͤò¤È¤ëÈϰϤϽª¤ï
2163     ¤ë¡£¡Ë¤³¤ÎÈÏ°Ï»ØÄêË¡¤Ï¡¢´Ø¿ô mtext_put_prop () ¤Ê¤É¤È¶¦Ä̤Ǥ¢¤ë¡£
2164
2165     $DEEPER ¤¬ 0 ¤Ç¤Ê¤±¤ì¤Ð¡¢$KEY ¤È¤¤¤¦¥­¡¼¤ò»ý¤Ä¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á°ìÈÖ
2166     ¾å¤Î¤â¤Î¤À¤±¤Ç¤Ê¤¯¡¢¥¹¥¿¥Ã¥¯Ãæ¤Î¤¹¤Ù¤Æ¤Î¤â¤Î¤¬Èæ³Ó¤µ¤ì¤ë¡£
2167
2168     $FROM ¤¬ @c NULL ¤Ê¤é¤Ð¡¢ÈϰϤλϤޤê¤Ïõº÷¤·¤Ê¤¤¡£$TO ¤¬ @c NULL 
2169     ¤Ê¤é¤Ð¡¢ÈϰϤνª¤ê¤Ïõº÷¤·¤Ê¤¤¡£
2170
2171     @return
2172     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_prop_range () ¤Ï $KEY ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤοô¤ò
2173     ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð-1 ¤òÊÖ¤·¡¢ ³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼
2174     ¥É¤òÀßÄꤹ¤ë¡£
2175
2176     @latexonly \IPAlabel{mtext_prop_range} @endlatexonly  */
2177
2178 /***
2179     @errors
2180     @c MERROR_RANGE, @c MERROR_SYMBOL
2181
2182     @seealso
2183     mtext_put_prop (), mtext_put_prop_values (),
2184     mtext_get_prop (), mtext_get_prop_values (), 
2185     mtext_pop_prop (), mtext_push_prop ()  */
2186
2187 int
2188 mtext_prop_range (MText *mt, MSymbol key, int pos,
2189                   int *from, int *to, int deeper)
2190 {
2191   MTextPlist *plist;
2192   MInterval *interval, *temp;
2193   void *val;
2194   int nprops;
2195
2196   M_CHECK_POS (mt, pos, -1);
2197
2198   plist = get_plist_create (mt, key, 0);
2199   if (! plist)
2200     {
2201       if (from) *from = 0;
2202       if (to) *to = mtext_nchars (mt);
2203       return 0;
2204     }
2205
2206   interval = find_interval (plist, pos);
2207   nprops = interval->nprops;
2208   if (deeper || ! nprops)
2209     {
2210       if (from) *from = interval->start;
2211       if (to) *to = interval->end;
2212       return interval->nprops;
2213     }
2214
2215   val = nprops ? interval->stack[nprops - 1] : NULL;
2216
2217   if (from)
2218     {
2219       for (temp = interval;
2220            temp->prev
2221              && (temp->prev->nprops
2222                  ? (nprops
2223                     && (val == temp->prev->stack[temp->prev->nprops - 1]))
2224                  : ! nprops);
2225            temp = temp->prev);
2226       *from = temp->start;
2227     }
2228
2229   if (to)
2230     {
2231       for (temp = interval;
2232            temp->next
2233              && (temp->next->nprops
2234                  ? (nprops
2235                     && val == temp->next->stack[temp->next->nprops - 1])
2236                  : ! nprops);
2237            temp = temp->next);
2238       *to = temp->end;
2239     }
2240
2241   return nprops;
2242 }
2243
2244 /***en
2245     @brief Create a text property.
2246
2247     The mtext_property () function returns a newly allocated text
2248     property whose key is $KEY and value is $VAL.  The created text 
2249     property is not attached to any M-text, i.e. it is detached.
2250
2251     $CONTROL_BITS must be 0 or logical OR of @c enum @c
2252     MTextPropertyControl.  */
2253
2254 /***ja
2255     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀ¸À®¤¹¤ë.
2256
2257     ´Ø¿ô mtext_property () ¤Ï $KEY ¤ò¥­¡¼¡¢$VAL ¤òÃͤȤ¹¤ë¿·¤·¤¯³ä¤êÅö
2258     ¤Æ¤é¤ì¤¿¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÊÖ¤¹¡£À¸À®¤·¤¿¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¤¤«
2259     ¤Ê¤ë M-text ¤Ë¤âÉղ䵤ì¤Æ¤¤¤Ê¤¤¡¢¤¹¤Ê¤ï¤ÁʬΥ¤·¤Æ (detached) ¤¤¤ë¡£
2260
2261     $CONTROL_BITS ¤Ï 0 ¤Ç¤¢¤ë¤« @c enum @c MTextPropertyControl ¤ÎÏÀÍý 
2262     OR ¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£  */
2263
2264 MTextProperty *
2265 mtext_property (MSymbol key, void *val, int control_bits)
2266 {
2267   return new_text_property (NULL, 0, 0, key, val, control_bits);
2268 }
2269
2270 /***en
2271     @brief Return the M-text of a text property.
2272
2273     The mtext_property_mtext () function returns the M-text to which
2274     text property $PROP is attached.  If $PROP is currently detached,
2275     NULL is returned.  */
2276
2277 /***ja
2278     @brief ¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä M-text ¤òÊÖ¤¹.
2279
2280     ´Ø¿ô mtext_property_mtext () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£$PROP ¤¬Éղäµ
2281     ¤ì¤Æ¤¤¤ë M-text ¤òÊÖ¤¹¡£¤½¤Î»þÅÀ¤Ç $PROP ¤¬Ê¬Î¥¤·¤Æ¤¤¤ì¤Ð NULL ¤ò
2282     ÊÖ¤¹¡£  */
2283
2284 MText *
2285 mtext_property_mtext (MTextProperty *prop)
2286 {
2287   return prop->mt;
2288 }
2289
2290 /***en
2291     @brief Return the key of a text property.
2292
2293     The mtext_property_key () function returns the key (symbol) of
2294     text property $PROP.  */
2295
2296 /***ja
2297     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤òÊÖ¤¹.
2298
2299     ´Ø¿ô mtext_property_key () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤Î¥­¡¼¡Ê¥·
2300     ¥ó¥Ü¥ë¡Ë¤òÊÖ¤¹¡£  */
2301
2302 MSymbol
2303 mtext_property_key (MTextProperty *prop)
2304 {
2305   return prop->key;
2306 }
2307
2308 /***en
2309     @brief Return the value of a text property.
2310
2311     The mtext_property_value () function returns the value of text
2312     property $PROP.  */
2313
2314 /***ja
2315     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊÖ¤¹.
2316
2317     ´Ø¿ô mtext_property_value () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤ÎÃͤòÊÖ
2318     ¤¹¡£  */
2319
2320 void *
2321 mtext_property_value (MTextProperty *prop)
2322 {
2323   return prop->val;
2324 }
2325
2326 /***en
2327     @brief Return the start position of a text property.
2328
2329     The mtext_property_start () function returns the start position of
2330     text property $PROP.  The start position is a character position
2331     of an M-text where $PROP begins.  If $PROP is detached, it returns
2332     -1.  */
2333
2334 /***ja
2335     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î³«»Ï°ÌÃÖ¤òÊÖ¤¹.
2336
2337     ´Ø¿ô mtext_property_start () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤Î³«»Ï°Ì
2338     ÃÖ¤òÊÖ¤¹¡£³«»Ï°ÌÃ֤ȤϠM-text Ãæ¤Ç $PROP ¤¬»Ï¤Þ¤ëʸ»ú°ÌÃ֤Ǥ¢¤ë¡£
2339     $PROP ¤¬Ê¬Î¥¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢-1 ¤òÊÖ¤¹¡£  */
2340
2341 int
2342 mtext_property_start (MTextProperty *prop)
2343 {
2344   return (prop->mt ? prop->start : -1);
2345 }
2346
2347 /***en
2348     @brief Return the end position of a text property.
2349
2350     The mtext_property_end () function returns the end position of
2351     text property $PROP.  The end position is a character position of
2352     an M-text where $PROP ends.  If $PROP is detached, it returns
2353     -1.  */
2354
2355 /***ja
2356     @brief ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î½ªÎ»°ÌÃÖ¤òÊÖ¤¹.
2357
2358     ´Ø¿ô mtext_property_end () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤Î½ªÎ»°ÌÃÖ
2359     ¤òÊÖ¤¹¡£½ªÎ»°ÌÃ֤ȤϠM-text Ãæ¤Ç $PROP ¤¬½ª¤ëʸ»ú°ÌÃ֤Ǥ¢¤ë¡£$PROP 
2360     ¤¬Ê¬Î¥¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢-1 ¤òÊÖ¤¹¡£  */
2361
2362 int
2363 mtext_property_end (MTextProperty *prop)
2364 {
2365   return (prop->mt ? prop->end : -1);
2366 }
2367
2368 /***en
2369     @brief Get the topmost text property.
2370
2371     The mtext_get_property () function searches the character at
2372     position $POS in M-text $MT for a text property whose key is $KEY.
2373
2374     @return
2375     If a text property is found, mtext_get_property () returns it.  If
2376     there are multiple text properties, it returns the topmost one.
2377     If no such property is found, it returns @c NULL without changing
2378     the external variable #merror_code.
2379
2380     If an error is detected, mtext_get_property () returns @c NULL and
2381     assigns an error code to the external variable #merror_code.  */
2382
2383 /***ja
2384     @brief °ìÈÖ¾å¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÆÀ¤ë.
2385
2386     ´Ø¿ô mtext_get_property () ¤Ï M-text $MT ¤Î°ÌÃÖ $POS ¤Îʸ»ú¤¬¥­¡¼
2387     ¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¤«¤É¤¦¤«¤òÄ´¤Ù¤ë¡£
2388
2389     @return 
2390     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_get_property () ¤Ï¤½¤ì¤òÊÖ¤¹¡£
2391     Ê£¿ô¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢°ìÈÖ¾å¤Î¤â¤Î¤òÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð¡¢³°ÉôÊÑ¿ô 
2392     #merror_code ¤òÊѤ¨¤ë¤³¤È¤Ê¤¯ @c NULL ¤òÊÖ¤¹¡£
2393
2394     ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç mtext_get_property () ¤Ï @c NULL ¤òÊÖ¤·¡¢³°
2395     ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£    */
2396
2397 MTextProperty *
2398 mtext_get_property (MText *mt, int pos, MSymbol key)
2399 {
2400   MTextPlist *plist;
2401   MInterval *interval;
2402
2403   M_CHECK_POS (mt, pos, NULL);
2404
2405   plist = get_plist_create (mt, key, 0);
2406   if (! plist)
2407     return NULL;
2408
2409   interval = find_interval (plist, pos);
2410   if (! interval->nprops)
2411     return NULL;
2412   return interval->stack[interval->nprops - 1];
2413 }
2414
2415 /***en
2416     @brief Get multiple text properties.
2417
2418     The mtext_get_properties () function searches the character at
2419     $POS in M-text $MT for properties whose key is $KEY.  If such
2420     properties are found, they are stored in the memory area pointed
2421     to by $PROPS.  $NUM limits the maximum number of stored
2422     properties.
2423
2424     @return
2425     If the operation was successful, mtext_get_properties () returns
2426     the number of actually stored properties.  If the character at
2427     $POS does not have a property whose key is $KEY, the return value
2428     is 0. If an error is detected, mtext_get_properties () returns -1
2429     and assigns an error code to the external variable #merror_code.  */
2430
2431 /***ja
2432     @brief Ê£¿ô¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÆÀ¤ë.
2433
2434     ´Ø¿ô mtext_get_properties () ¤Ï M-text $MT ¤Î°ÌÃÖ $POS ¤Îʸ»ú¤¬¥­¡¼
2435     ¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¤«¤É¤¦¤«¤òÄ´¤Ù¤ë¡£¤½¤Î¤è¤¦¤Ê
2436     ¥×¥í¥Ñ¥Æ¥£¤¬¤ß¤Ä¤«¤ì¤Ð¡¢$PROPS ¤¬»Ø¤¹¥á¥â¥êÎΰè¤ËÊݸ¤¹¤ë¡£$NUM ¤Ï
2437     Êݸ¤µ¤ì¤ë¥×¥í¥Ñ¥Æ¥£¤Î¿ô¤Î¾å¸Â¤Ç¤¢¤ë¡£
2438
2439     @return 
2440     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_get_properties () ¤Ï¼ÂºÝ¤ËÊݸ¤·¤¿¥×¥í¥Ñ¥Æ¥£
2441     ¤Î¿ô¤òÊÖ¤¹¡£$POS ¤Î°ÌÃÖ¤Îʸ»ú¤¬¥­¡¼¤¬ $KEY ¤Ç¤¢¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿
2442     ¤Ê¤±¤ì¤Ð¡¢0 ¤¬Ê֤롣¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ë¤Ï¡¢
2443     mtext_get_properties () ¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼
2444     ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
2445
2446 int
2447 mtext_get_properties (MText *mt, int pos, MSymbol key,
2448                       MTextProperty **props, int num)
2449 {
2450   MTextPlist *plist;
2451   MInterval *interval;
2452   int nprops;
2453   int i;
2454   int offset;
2455
2456   M_CHECK_POS (mt, pos, -1);
2457
2458   plist = get_plist_create (mt, key, 0);
2459   if (! plist)
2460     return 0;
2461
2462   interval = find_interval (plist, pos);
2463   /* It is assured that INTERVAL is not NULL.  */
2464   nprops = interval->nprops;
2465   if (nprops == 0 || num <= 0)
2466     return 0;
2467   if (nprops == 1 || num == 1)
2468     {
2469       props[0] = interval->stack[nprops - 1];
2470       return 1;
2471     }
2472
2473   if (nprops <= num)
2474     num = nprops, offset = 0;
2475   else
2476     offset = nprops - num;
2477   for (i = 0; i < num; i++)
2478     props[i] = interval->stack[offset + i];
2479   return num;
2480 }
2481
2482 /***en
2483     @brief Attach a text property to an M-text.
2484
2485     The mtext_attach_property () function attaches text property $PROP
2486     to the range between $FROM and $TO in M-text $MT.  If $PROP is
2487     already attached to an M-text, it is detached before attached to
2488     $MT.
2489
2490     @return
2491     If the operation was successful, mtext_attach_property () returns
2492     0.  Otherwise it returns -1 and assigns an error code to the
2493     external variable #merror_code.  */
2494
2495 /***ja
2496     @brief  M-text¤Ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÉղ乤ë.
2497
2498     ´Ø¿ô mtext_attach_property () ¤Ï¡¢M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ
2499     ¤Ç¤ÎÎΰè¤Ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤òÉղ乤롣¤â¤· $PROP ¤¬´û¤Ë
2500     M-text ¤ËÉղ䵤ì¤Æ¤¤¤ì¤Ð¡¢$MT ¤ËÉղ乤ëÁ°¤ËʬΥ¤µ¤ì¤ë¡£
2501
2502     @return 
2503     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_attach_property () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±
2504     ¤ì¤Ð -1 ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£      */
2505
2506
2507 int
2508 mtext_attach_property (MText *mt, int from, int to, MTextProperty *prop)
2509 {     
2510   MTextPlist *plist;
2511   MInterval *interval;
2512
2513   M_CHECK_RANGE (mt, from, to, -1, 0);
2514
2515   M17N_OBJECT_REF (prop);
2516   if (prop->mt)
2517     mtext_detach_property (prop);
2518   prepare_to_modify (mt, from, to, prop->key);
2519   plist = get_plist_create (mt, prop->key, 1);
2520   xassert (check_plist (plist, 0) == 0);
2521   interval = pop_all_properties (plist, from, to);
2522   xassert (check_plist (plist, 0) == 0);
2523   prop->mt = mt;
2524   prop->start = from;
2525   prop->end = to;
2526   PUSH_PROP (interval, prop);
2527   M17N_OBJECT_UNREF (prop);
2528   xassert (check_plist (plist, 0) == 0);
2529   if (interval->next)
2530     maybe_merge_interval (plist, interval);
2531   if (interval->prev)
2532     maybe_merge_interval (plist, interval->prev);
2533   xassert (check_plist (plist, 0) == 0);
2534   return 0;
2535 }
2536
2537 /***en
2538     @brief Detach a text property from an M-text.
2539
2540     The mtext_detach_property () function makes text property $PROP
2541     detached.
2542
2543     @return
2544     This function always returns 0.  */
2545
2546 /***ja
2547     @brief  M-text ¤«¤é¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òʬΥ¤¹¤ë.
2548
2549     ´Ø¿ô mtext_detach_property () ¤Ï¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤òʬΥ¤¹¤ë¡£
2550
2551     @return
2552     ¤³¤Î´Ø¿ô¤Ï¾ï¤Ë 0 ¤òÊÖ¤¹¡£  */
2553
2554 int
2555 mtext_detach_property (MTextProperty *prop)
2556 {
2557   MTextPlist *plist;
2558   int start = prop->start, end = prop->end;
2559
2560   if (! prop->mt)
2561     return 0;
2562   prepare_to_modify (prop->mt, start, end, prop->key);
2563   plist = get_plist_create (prop->mt, prop->key, 0);
2564   xassert (plist);
2565   detach_property (plist, prop, NULL);
2566   return 0;
2567 }
2568
2569 /***en
2570     @brief Push a text property onto an M-text.
2571
2572     The mtext_push_property () function pushes text property $PROP to
2573     the characters between $FROM (inclusive) and $TO (exclusive) in
2574     M-text $MT.
2575
2576     @return
2577     If the operation was successful, mtext_push_property () returns
2578     0.  Otherwise it returns -1 and assigns an error code to the
2579     external variable #merror_code.  */
2580
2581 /***ja
2582     @brief M-text ¤Ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥×¥Ã¥·¥å¤¹¤ë.
2583
2584     ´Ø¿ô mtext_push_property () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤ò¡¢
2585     M-text $MT Ãæ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎ
2586     Ê¸»ú¤Ë¥×¥Ã¥·¥å¤¹¤ë¡£
2587
2588     @return
2589     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_push_property () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±
2590     ¤ì¤Ð -1 ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£      */
2591
2592
2593 int
2594 mtext_push_property (MText *mt, int from, int to, MTextProperty *prop)
2595 {
2596   MTextPlist *plist;
2597   MInterval *head, *tail, *interval;
2598   int check_head, check_tail;
2599
2600   M_CHECK_RANGE (mt, from, to, -1, 0);
2601
2602   M17N_OBJECT_REF (prop);
2603   if (prop->mt)
2604     mtext_detach_property (prop);
2605   prepare_to_modify (mt, from, to, prop->key);
2606   plist = get_plist_create (mt, prop->key, 1);
2607   prop->mt = mt;
2608   prop->start = from;
2609   prop->end = to;
2610
2611   /* Find an interval that covers the position FROM.  */
2612   head = find_interval (plist, from);
2613
2614   /* If the found interval starts before FROM, divide it at FROM.  */
2615   if (head->start < from)
2616     {
2617       divide_interval (plist, head, from);
2618       head = head->next;
2619       check_head = 0;
2620     }
2621   else
2622     check_head = 1;
2623
2624   /* Find an interval that ends at TO.  If TO is not at the end of an
2625      interval, make one that ends at TO.  */
2626   if (head->end == to)
2627     {
2628       tail = head;
2629       check_tail = 1;
2630     }
2631   else if (head->end > to)
2632     {
2633       divide_interval (plist, head, to);
2634       tail = head;
2635       check_tail = 0;
2636     }
2637   else
2638     {
2639       tail = find_interval (plist, to);
2640       if (! tail)
2641         {
2642           tail = plist->tail;
2643           check_tail = 0;
2644         }
2645       else if (tail->start == to)
2646         {
2647           tail = tail->prev;
2648           check_tail = 1;
2649         }
2650       else
2651         {
2652           divide_interval (plist, tail, to);
2653           check_tail = 0;
2654         }
2655     }
2656
2657   /* Push PROP to the current values of intervals between HEAD and TAIL
2658      (both inclusive).  */
2659   for (interval = head; ; interval = interval->next)
2660     {
2661       PUSH_PROP (interval, prop);
2662       if (interval == tail)
2663         break;
2664     }
2665
2666   /* If there is a possibility that TAIL now has the same value as the
2667      next one, check it and concatenate them if necessary.  */
2668   if (tail->next && check_tail)
2669     maybe_merge_interval (plist, tail);
2670
2671   /* If there is a possibility that HEAD now has the same value as the
2672      previous one, check it and concatenate them if necessary.  */
2673   if (head->prev && check_head)
2674     maybe_merge_interval (plist, head->prev);
2675
2676   M17N_OBJECT_UNREF (prop);
2677   xassert (check_plist (plist, 0) == 0);
2678   return 0;
2679 }
2680
2681 /***en
2682     @brief Symbol for specifying serializer functions.
2683
2684     To serialize a text property, the user must supply a serializer
2685     function for that text property.  This is done by giving a symbol
2686     property whose key is #Mtext_prop_serializer and value is a
2687     pointer to an appropriate serializer function.
2688
2689     @seealso
2690     mtext_serialize (), MTextPropSerializeFunc
2691   */
2692
2693 /***ja
2694     @brief ¥·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë.
2695
2696     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¤¿¤á¤Ë¤Ï¡¢¤½¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ
2697     ¥Æ¥£ÍѤΥ·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤òÍ¿¤¨¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¶ñÂÎŪ¤Ë¤Ï¡¢
2698     #Mtext_prop_serializer ¤ò¥­¡¼¤È¤·¡¢Å¬Àڤʥ·¥ê¥¢¥é¥¤¥º´Ø¿ô¤Ø¤Î¥Ý¥¤
2699     ¥ó¥¿¤òÃͤȤ¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¡£
2700
2701     @seealso
2702     mtext_serialize (), MTextPropSerializeFunc
2703   */
2704 MSymbol Mtext_prop_serializer;
2705
2706 /***en
2707     @brief Symbol for specifying deserializer functions.
2708
2709     To deserialize a text property, the user must supply a deserializer
2710     function for that text property.  This is done by giving a symbol
2711     property whose key is #Mtext_prop_deserializer and value is a
2712     pointer to an appropriate deserializer function.
2713
2714     @seealso
2715     mtext_deserialize (), MTextPropSerializeFunc
2716   */
2717
2718 /***ja
2719     @brief ¥Ç¥·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë.
2720
2721     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ç¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¤¿¤á¤Ë¤Ï¡¢¤½¤Î¥Æ¥­¥¹¥È¥×¥í
2722     ¥Ñ¥Æ¥£ÍѤΥǥ·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤òÍ¿¤¨¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¶ñÂÎŪ¤Ë¤Ï¡¢
2723     #Mtext_prop_deserializer ¤ò¥­¡¼¤È¤·¡¢Å¬Àڤʥǥ·¥ê¥¢¥é¥¤¥º´Ø¿ô¤Ø¤Î
2724     ¥Ý¥¤¥ó¥¿¤òÃͤȤ¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¡£
2725
2726     @seealso
2727     mtext_deserialize (), MTextPropSerializeFunc
2728   */
2729 MSymbol Mtext_prop_deserializer;
2730
2731 /***en
2732     @brief Serialize text properties in an M-text.
2733
2734     The mtext_serialize () function serializes the text between $FROM
2735     and $TO in M-text $MT.  The serialized result is an M-text in a
2736     form of XML.  $PROPERTY_LIST limits the text properties to be
2737     serialized. Only those text properties whose key 
2738
2739     @li appears as the value of an element in $PROPERTY_LIST, and
2740     @li has the symbol property #Mtext_prop_serializer
2741
2742     are serialized as a "property" element in the resulting XML
2743     representation.
2744
2745     The DTD of the generated XML is as follows:
2746
2747 @verbatim
2748 <!DOCTYPE mtext [
2749   <!ELEMENT mtext (property*,body+)>
2750   <!ELEMENT property EMPTY>
2751   <!ELEMENT body (#PCDATA)>
2752   <!ATTLIST property key CDATA #REQUIRED>
2753   <!ATTLIST property value CDATA #REQUIRED>
2754   <!ATTLIST property from CDATA #REQUIRED>
2755   <!ATTLIST property to CDATA #REQUIRED>
2756   <!ATTLIST property control CDATA #REQUIRED>
2757  ]>
2758 @endverbatim
2759
2760     This function depends on the libxml2 library.  If the m17n library
2761     is configured without libxml2, this function always fails.
2762
2763     @return
2764     If the operation was successful, mtext_serialize () returns an
2765     M-text in the form of XML.  Otherwise it returns @c NULL and assigns an
2766     error code to the external variable #merror_code.
2767
2768     @seealso
2769     mtext_deserialize (), Mtext_prop_serializer  */
2770
2771 /***ja
2772     @brief M-text Ãæ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥·¥ê¥¢¥é¥¤¥º¤¹¤ë.
2773
2774     ´Ø¿ô mtext_serialize () ¤Ï M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ¤Ç¤Î¥Æ¥­
2775     ¥¹¥È¤ò¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¡£¥·¥ê¥¢¥é¥¤¥º¤·¤¿·ë²Ì¤Ï XML ·Á¼°¤Î M-text ¤Ç
2776     ¤¢¤ë¡£ $PROPERTY_LIST ¤Ï¥·¥ê¥¢¥é¥¤¥º¤µ¤ì¤ë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¸ÂÄê
2777     ¤¹¤ë¡£ÂоݤȤʤë¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¡¢¤½¤Î¥­¡¼¤¬
2778
2779     @li $PROPERTY_LIST ¤ÎÍ×ÁǤÎÃͤȤ·¤Æ¸½¤ï¤ì¡¢¤«¤Ä
2780     @li ¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£ #Mtext_prop_serializer ¤ò»ý¤Ä
2781     
2782     ¤â¤Î¤Î¤ß¤Ç¤¢¤ë¡£¤³¤Î¾ò·ï¤òËþ¤¿¤¹¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¡¢À¸À®¤µ¤ì¤ë 
2783     XML É½¸½Ãæ¤Ç "property" Í×ÁǤ˥·¥ê¥¢¥é¥¤¥º¤µ¤ì¤ë¡£
2784
2785     À¸À®¤µ¤ì¤ë XML ¤Î DTD ¤Ï°Ê²¼¤ÎÄ̤ê:
2786
2787 @verbatim
2788 <!DOCTYPE mtext [
2789   <!ELEMENT mtext (property*,body+)>
2790   <!ELEMENT property EMPTY>
2791   <!ELEMENT body (#PCDATA)>
2792   <!ATTLIST property key CDATA #REQUIRED>
2793   <!ATTLIST property value CDATA #REQUIRED>
2794   <!ATTLIST property from CDATA #REQUIRED>
2795   <!ATTLIST property to CDATA #REQUIRED>
2796   <!ATTLIST property control CDATA #REQUIRED>
2797  ]>
2798 @endverbatim
2799
2800     ¤³¤Î´Ø¿ô¤Ï libxml2 ¥é¥¤¥Ö¥é¥ê¤Ë°Í¸¤¹¤ë¡£m17n ¥é¥¤¥Ö¥é¥ê¤¬libxml2 
2801     Ìµ¤·¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤Î´Ø¿ô¤Ï¾ï¤Ë¼ºÇÔ¤¹¤ë¡£
2802
2803     @return 
2804     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_serialize () ¤Ï XML ·Á¼°¤Ç M-text ¤òÊÖ¤¹¡£
2805     ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É
2806     ¤òÀßÄꤹ¤ë¡£
2807
2808     @seealso
2809     mtext_deserialize (), Mtext_prop_serializer  */
2810
2811 MText *
2812 mtext_serialize (MText *mt, int from, int to, MPlist *property_list)
2813 {
2814 #ifdef HAVE_XML2
2815   MPlist *plist, *pl;
2816   MTextPropSerializeFunc func;
2817   MText *work;
2818   xmlDocPtr doc;
2819   xmlNodePtr node;
2820   unsigned char *ptr;
2821   int n;
2822
2823   M_CHECK_RANGE (mt, from, to, NULL, NULL);
2824   doc = xmlParseMemory (XML_TEMPLATE, strlen (XML_TEMPLATE) + 1);
2825   node = xmlDocGetRootElement (doc);
2826
2827   plist = mplist ();
2828   MPLIST_DO (pl, property_list)
2829     {
2830       MSymbol key = MPLIST_VAL (pl);
2831
2832       func = (MTextPropSerializeFunc) msymbol_get (key, Mtext_prop_serializer);
2833       if (func)
2834         extract_text_properties (mt, from, to, key, plist);
2835     }
2836
2837   work = mtext ();
2838   MPLIST_DO (pl, plist)
2839     {
2840       MTextProperty *prop = MPLIST_VAL (pl);
2841       char buf[256];
2842       MPlist *serialized_plist;
2843       xmlNodePtr child;
2844
2845       func = (MTextPropSerializeFunc) msymbol_get (prop->key,
2846                                                    Mtext_prop_serializer);
2847       serialized_plist = (func) (prop->val);
2848       if (! serialized_plist)
2849         continue;
2850       mtext_reset (work);
2851       mplist__serialize (work, serialized_plist);
2852       child = xmlNewChild (node, NULL, (xmlChar *) "property", NULL);
2853       xmlSetProp (child, (xmlChar *) "key",
2854                   (xmlChar *) MSYMBOL_NAME (prop->key));
2855       xmlSetProp (child, (xmlChar *) "value", (xmlChar *) MTEXT_DATA (work));
2856       sprintf (buf, "%d", prop->start - from);
2857       xmlSetProp (child, (xmlChar *) "from", (xmlChar *) buf);
2858       sprintf (buf, "%d", prop->end - from);
2859       xmlSetProp (child, (xmlChar *) "to", (xmlChar *) buf);
2860       sprintf (buf, "%d", prop->control.flag);
2861       xmlSetProp (child, (xmlChar *) "control", (xmlChar *) buf);
2862       xmlAddChild (node, xmlNewText ((xmlChar *) "\n"));
2863
2864       M17N_OBJECT_UNREF (serialized_plist);
2865     }
2866   M17N_OBJECT_UNREF (plist);
2867
2868   if (from > 0 || to < mtext_nchars (mt))
2869     mtext_copy (work, 0, mt, from, to);
2870   else
2871     {
2872       M17N_OBJECT_UNREF (work);
2873       work = mt;
2874     }
2875   for (from = 0, to = mtext_nchars (mt); from <= to; from++)
2876     {
2877       ptr = MTEXT_DATA (mt) + POS_CHAR_TO_BYTE (mt, from);
2878       xmlNewTextChild (node, NULL, (xmlChar *) "body", (xmlChar *) ptr);
2879       from = mtext_character (mt, from, to, 0);
2880       if (from < 0)
2881         from = to;
2882     }
2883
2884   xmlDocDumpMemoryEnc (doc, (xmlChar **) &ptr, &n, "UTF-8");
2885   if (work == mt)
2886     work = mtext ();
2887   mtext__cat_data (work, ptr, n, MTEXT_FORMAT_UTF_8);
2888   return work;
2889 #else  /* not HAVE_XML2 */
2890   MERROR (MERROR_TEXTPROP, NULL);
2891 #endif  /* not HAVE_XML2 */
2892 }
2893
2894 /***en
2895     @brief Deserialize text properties in an M-text.
2896
2897     The mtext_deserialize () function deserializes M-text $MT.  $MT
2898     must be an XML having the followng DTD.
2899
2900 @verbatim
2901 <!DOCTYPE mtext [
2902   <!ELEMENT mtext (property*,body+)>
2903   <!ELEMENT property EMPTY>
2904   <!ELEMENT body (#PCDATA)>
2905   <!ATTLIST property key CDATA #REQUIRED>
2906   <!ATTLIST property value CDATA #REQUIRED>
2907   <!ATTLIST property from CDATA #REQUIRED>
2908   <!ATTLIST property to CDATA #REQUIRED>
2909   <!ATTLIST property control CDATA #REQUIRED>
2910  ]>
2911 @endverbatim
2912
2913     This function depends on the libxml2 library.  If the m17n library
2914     is configured without libxml2, this function always fail.
2915
2916     @return
2917     If the operation was successful, mtext_deserialize () returns the
2918     resulting M-text.  Otherwise it returns @c NULL and assigns an error
2919     code to the external variable #merror_code.
2920
2921     @seealso
2922     mtext_serialize (), Mtext_prop_deserializer  */
2923
2924 /***ja
2925     @brief M-text Ãæ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ç¥·¥ê¥¢¥é¥¤¥º¤¹¤ë.
2926
2927     ´Ø¿ô mtext_deserialize () ¤Ï M-text $MT ¤ò¥Ç¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¡£$MT
2928     ¤Ï¼¡¤Î DTD ¤ò»ý¤Ä XML ¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
2929  
2930 @verbatim
2931 <!DOCTYPE mtext [
2932   <!ELEMENT mtext (property*,body+)>
2933   <!ELEMENT property EMPTY>
2934   <!ELEMENT body (#PCDATA)>
2935   <!ATTLIST property key CDATA #REQUIRED>
2936   <!ATTLIST property value CDATA #REQUIRED>
2937   <!ATTLIST property from CDATA #REQUIRED>
2938   <!ATTLIST property to CDATA #REQUIRED>
2939   <!ATTLIST property control CDATA #REQUIRED>
2940  ]>
2941 @endverbatim
2942
2943     ¤³¤Î´Ø¿ô¤Ï libxml2 ¥é¥¤¥Ö¥é¥ê¤Ë°Í¸¤¹¤ë¡£m17n ¥é¥¤¥Ö¥é¥ê¤¬libxml2 
2944     Ìµ¤·¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤Î´Ø¿ô¤Ï¾ï¤Ë¼ºÇÔ¤¹¤ë¡£
2945
2946     @return 
2947     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_serialize () ¤ÏÆÀ¤é¤ì¤¿ M-text ¤ò
2948     ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼
2949     ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
2950
2951     @seealso
2952     mtext_serialize (), Mtext_prop_deserializer  */
2953
2954 MText *
2955 mtext_deserialize (MText *mt)
2956 {
2957 #ifdef HAVE_XML2
2958   xmlDocPtr doc;
2959   xmlNodePtr node;
2960   xmlXPathContextPtr context;
2961   xmlXPathObjectPtr result;
2962   xmlChar *body_str, *key_str, *val_str, *from_str, *to_str, *ctl_str;
2963   int i;
2964
2965   if (mt->format > MTEXT_FORMAT_UTF_8)
2966     MERROR (MERROR_TEXTPROP, NULL);
2967   doc = xmlParseMemory ((char *) MTEXT_DATA (mt), mtext_nbytes (mt));
2968   if (! doc)
2969     MERROR (MERROR_TEXTPROP, NULL);
2970   node = xmlDocGetRootElement (doc);
2971   if (! node)
2972     {
2973       xmlFreeDoc (doc);
2974       MERROR (MERROR_TEXTPROP, NULL);
2975     }
2976   if (xmlStrcmp (node->name, (xmlChar *) "mtext"))
2977     {
2978       xmlFreeDoc (doc);
2979       MERROR (MERROR_TEXTPROP, NULL);
2980     }
2981
2982   context = xmlXPathNewContext (doc);
2983   result = xmlXPathEvalExpression ((xmlChar *) "//body", context);
2984   if (xmlXPathNodeSetIsEmpty (result->nodesetval))
2985     {
2986       xmlFreeDoc (doc);
2987       MERROR (MERROR_TEXTPROP, NULL);
2988     }
2989   for (i = 0, mt = mtext (); i < result->nodesetval->nodeNr; i++)
2990     {
2991       if (i > 0)
2992         mtext_cat_char (mt, 0);
2993       node = (xmlNodePtr) result->nodesetval->nodeTab[i];
2994       body_str = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
2995       if (body_str)
2996         {
2997           mtext__cat_data (mt, body_str, strlen ((char *) body_str),
2998                            MTEXT_FORMAT_UTF_8);
2999           xmlFree (body_str);
3000         }
3001     }
3002
3003   result = xmlXPathEvalExpression ((xmlChar *) "//property", context);
3004   if (! xmlXPathNodeSetIsEmpty (result->nodesetval))
3005     for (i = 0; i < result->nodesetval->nodeNr; i++)
3006       {
3007         MSymbol key;
3008         MTextPropDeserializeFunc func;
3009         MTextProperty *prop;
3010         MPlist *plist;
3011         int from, to, control;
3012         void *val;
3013
3014         key_str = xmlGetProp (result->nodesetval->nodeTab[i],
3015                               (xmlChar *) "key");
3016         val_str = xmlGetProp (result->nodesetval->nodeTab[i],
3017                               (xmlChar *) "value");
3018         from_str = xmlGetProp (result->nodesetval->nodeTab[i],
3019                                (xmlChar *) "from");
3020         to_str = xmlGetProp (result->nodesetval->nodeTab[i],
3021                              (xmlChar *) "to");
3022         ctl_str = xmlGetProp (result->nodesetval->nodeTab[i],
3023                               (xmlChar *) "control");
3024
3025         key = msymbol ((char *) key_str);
3026         func = ((MTextPropDeserializeFunc)
3027                 msymbol_get (key, Mtext_prop_deserializer));
3028         if (! func)
3029           continue;
3030         plist = mplist__from_string (val_str, strlen ((char *) val_str));
3031         if (! plist)
3032           continue;
3033         if (sscanf ((char *) from_str, "%d", &from) != 1
3034             || from < 0 || from >= mtext_nchars (mt))
3035           continue;
3036         if (sscanf ((char *) to_str, "%d", &to) != 1
3037             || to <= from || to > mtext_nchars (mt))
3038           continue;
3039         if (sscanf ((char *) ctl_str, "%d", &control) != 1
3040             || control < 0 || control > MTEXTPROP_CONTROL_MAX)
3041           continue;
3042         val = (func) (plist);
3043         M17N_OBJECT_UNREF (plist);
3044         prop = mtext_property (key, val, control);
3045         if (key->managing_key)
3046           M17N_OBJECT_UNREF (val);
3047         mtext_push_property (mt, from, to, prop);
3048         M17N_OBJECT_UNREF (prop);
3049
3050         xmlFree (key_str);
3051         xmlFree (val_str);
3052         xmlFree (from_str);
3053         xmlFree (to_str);
3054         xmlFree (ctl_str);
3055       }
3056   xmlXPathFreeContext (context);
3057   xmlFreeDoc (doc);
3058   return mt;
3059 #else  /* not HAVE_XML2 */
3060   MERROR (MERROR_TEXTPROP, NULL);
3061 #endif  /* not HAVE_XML2 */
3062 }
3063
3064 /*** @} */
3065
3066 /*
3067   Local Variables:
3068   coding: euc-japan
3069   End:
3070 */