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