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