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