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