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