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