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
6 This file is part of the m17n library.
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.
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.
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
24 @addtogroup m17nTextProperty
25 @brief Function to handle text properties.
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.
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". */
41 @addtogroup m17nTextProperty
42 @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÁàºî¤¹¤ë¤¿¤á¤Î´Ø¿ô
44 M-text Æâ¤Î³Æʸ»ú¤Ï¡¢@e ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£ ¤È¸Æ¤Ð¤ì¤ë¥×¥í¥Ñ¥Æ¥£¤ò
45 »ý¤Ä¤³¤È¤¬¤Ç¤¤ë¡£¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ë¤è¤Ã¤Æ¡¢¥Æ¥¥¹¥È¤Î³ÆÉô°Ì¤Ë´Ø
46 ¤¹¤ëÍÍ¡¹¤Ê¾ðÊó¤ò M-text Æâ¤ËÊÝ»ý¤¹¤ë¤³¤È¤¬¤Ç¤¤ë¡£¤½¤Î¤¿¤á¡¢¤½¤ì¤é
47 ¤Î¾ðÊó¤ò¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥àÆâ¤ÇÅý°ìŪ¤Ë°·¤¦¤³¤È¤¬¤Ç¤¤ë¡£¤Þ
48 ¤¿¡¢M-text ¼«ÂΤ¬ËÉ٤ʾðÊó¤ò»ý¤Ä¤¿¤á¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à
49 Ãæ¤Î³Æ´Ø¿ô¤ò´ÊÁDz½¤¹¤ë¤³¤È¤¬¤Ç¤¤ë¡£
51 ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï @e ¥¡¼ ¤È @e ÃÍ ¤«¤é¤Ê¤ë¡£¥¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢
52 ¤ê¡¢ÃÍ¤Ï <tt>(void *)</tt> ·¿¤Ë¥¥ã¥¹¥È¤Ç¤¤ë¤â¤Î¤Ê¤é²¿¤Ç¤â¤è¤¤¡£
53 ¾¤Î¥¿¥¤¥×¤Î¥×¥í¥Ñ¥Æ¥£¤È°Û¤Ê¤ê¡¢Æ±°ì¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÏÊ£¿ô¤ÎÃÍ
54 ¤ò»ý¤Ä¤³¤È¤¬µö¤µ¤ì¤ë¡£¡Ö@c Mxxx ¤È¤¤¤¦¥¡¼¤ò»ý¤Ä¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¡×
55 ¤Î¤³¤È¤ò´Êñ¤Ë¡Öxxx ¥×¥í¥Ñ¥Æ¥£¡×¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£ */
59 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
60 /*** @addtogroup m17nInternal
69 #include <libxml/tree.h>
70 #include <libxml/parser.h>
71 #include <libxml/xmlmemory.h>
72 #include <libxml/xpath.h>
76 #include "m17n-misc.h"
82 #define TEXT_PROP_DEBUG
85 #ifdef TEXT_PROP_DEBUG
86 #define xassert(X) do {if (!(X)) mdebug_hook ();} while (0)
88 #define xassert(X) (void) 0
89 #endif /* not FONTSET_DEBUG */
91 /* Hierarchy of objects (MText, MTextPlist, MInterval, MTextProperty)
95 +--> MTextPlist -> MTextPlist -> ... -> MTextPlist
97 | +- tail <-----------------------------------------+
99 | +- head <--> MInterval <--> ... <--> MInterval <--+
101 +- tail --------------------------------------------------------+
103 +- head --> MInterval <--> MInterval <--> ... <--> MInterval <--+
105 +---------------+------------> MTextProperty
112 MTextProperty a/A [AAAAAAAAAAAAAAAAAAAAA]
113 MTextProperty a/B [BBBBBBBBBBBBBBBBB]
114 MTextPlist a |--intvl1--|-intvl2-|-intvl3-|---intvl4---|-intvl5-|
117 MTextProperty b/A [AAAAAAAAAA]
118 MTextProperty b/B [BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB]
119 MTextPlist b |-intvl1-|--intvl2--|--intvl3--|-intvl4-|--intvl5--|
121 M-text |--------------------------------------------------|
127 /* The structure MTextProperty is defined in textprop.h. */
129 /** MInterval is the structure for an interval that holds text
130 properties of the same key in a specific range of M-text.
131 All intervals are stored in MIntervalPool. */
133 typedef struct MInterval MInterval;
137 /** Stack of pointers to text properties. If the interval does not
138 have any text properties, this member is NULL or contains random
140 MTextProperty **stack;
142 /** How many values are in <stack>. */
145 /** Length of <stack>. */
148 /** Start and end character positions of the interval. If <end> is
149 negative, this interval is not in use. */
152 /** Pointers to the previous and next intervals. If <start> is 0,
153 <prev> is NULL and this interval is pointed by MTextPlist->head.
154 If <end> is the size of the M-text, <next> is NULL, and this
155 interval is pointed by MTextPlist->tail. */
156 MInterval *prev, *next;
159 /** MTextPlist is a structure to hold text properties of an M-text by
160 chain. Each element in the chain is for a specific key. */
162 typedef struct MTextPlist MTextPlist;
166 /** Key of the property. */
169 /** The head and tail intervals. <head>->start is always 0.
170 <tail->end is always MText->nchars. */
171 MInterval *head, *tail;
173 /** Lastly accessed interval. */
176 /* Not yet implemented. */
177 int (*modification_hook) (MText *mt, MSymbol key, int from, int to);
179 /** Pointer to the next property in the chain, or NULL if the
180 property is the last one in the chain. */
185 /** How many intervals one interval-pool can contain. */
187 #define INTERVAL_POOL_SIZE 1024
189 typedef struct MIntervalPool MIntervalPool;
192 /** MIntervalPool is the structure for an interval-pool which store
193 intervals. Each interval-pool contains INTERVAL_POOL_SIZE number
194 of intervals, and is chained from the root #interval_pool. */
198 /** Array of intervals. */
199 MInterval intervals[INTERVAL_POOL_SIZE];
201 /** The smallest index to an unused interval. */
204 /** Pointer to the next interval-pool. */
209 /** Root of interval-pools. */
211 static MIntervalPool interval_pool_root;
215 static M17NObjectArray text_property_table;
217 /** Return a newly allocated interval pool. */
219 static MIntervalPool *
225 MSTRUCT_CALLOC (pool, MERROR_TEXTPROP);
226 for (i = 0; i < INTERVAL_POOL_SIZE; i++)
227 pool->intervals[i].end = -1;
234 /** Return a new interval for the region START and END. */
237 new_interval (int start, int end)
242 for (pool = &interval_pool_root;
243 pool->free_slot >= INTERVAL_POOL_SIZE;
247 pool->next = new_interval_pool ();
250 interval = &(pool->intervals[pool->free_slot]);
251 interval->stack = NULL;
252 interval->nprops = 0;
253 interval->stack_length = 0;
254 interval->prev = interval->next = NULL;
255 interval->start = start;
259 while (pool->free_slot < INTERVAL_POOL_SIZE
260 && pool->intervals[pool->free_slot].end >= 0)
267 /** Free INTERVAL and return INTERVAL->next. It assumes that INTERVAL
268 has no properties. */
271 free_interval (MInterval *interval)
273 MIntervalPool *pool = &interval_pool_root;
276 xassert (interval->nprops == 0);
278 free (interval->stack);
279 while ((interval < pool->intervals
280 || interval >= pool->intervals + INTERVAL_POOL_SIZE)
284 i = interval - pool->intervals;
286 if (i < pool->free_slot)
288 return interval->next;
292 /** If necessary, allocate a stack for INTERVAL so that it can contain
293 NUM number of text properties. */
295 #define PREPARE_INTERVAL_STACK(interval, num) \
297 if ((num) > (interval)->stack_length) \
299 MTABLE_REALLOC ((interval)->stack, (num), MERROR_TEXTPROP); \
300 (interval)->stack_length = (num); \
305 /** Return a copy of INTERVAL. The copy still shares text properties
306 with INTERVAL. If MASK_BITS is not zero, don't copy such text
307 properties whose control flags contains bits in MASK_BITS. */
310 copy_interval (MInterval *interval, int mask_bits)
312 MInterval *new = new_interval (interval->start, interval->end);
313 int nprops = interval->nprops;
314 MTextProperty **props = alloca (sizeof (MTextProperty *) * nprops);
317 for (i = n = 0; i < nprops; i++)
318 if (! (interval->stack[i]->control.flag & mask_bits))
319 props[n++] = interval->stack[i];
323 PREPARE_INTERVAL_STACK (new, n);
324 memcpy (new->stack, props, sizeof (MTextProperty *) * n);
331 /** Free text property OBJECT. */
334 free_text_property (void *object)
336 MTextProperty *prop = (MTextProperty *) object;
338 if (prop->key->managing_key)
339 M17N_OBJECT_UNREF (prop->val);
340 M17N_OBJECT_UNREGISTER (text_property_table, prop);
345 /** Return a newly allocated text property whose key is KEY and value
348 static MTextProperty *
349 new_text_property (MText *mt, int from, int to, MSymbol key, void *val,
354 M17N_OBJECT (prop, free_text_property, MERROR_TEXTPROP);
355 prop->control.flag = control_bits;
356 prop->attach_count = 0;
362 if (key->managing_key)
363 M17N_OBJECT_REF (val);
364 M17N_OBJECT_REGISTER (text_property_table, prop);
369 /** Return a newly allocated copy of text property PROP. */
371 #define COPY_TEXT_PROPERTY(prop) \
372 new_text_property ((prop)->mt, (prop)->start, (prop)->end, \
373 (prop)->key, (prop)->val, (prop)->control.flag)
376 /** Split text property PROP at position INTERVAL->start, and make all
377 the following intervals contain the copy of PROP instead of PROP.
378 It assumes that PROP starts before INTERVAL. */
381 split_property (MTextProperty *prop, MInterval *interval)
387 prop->end = interval->start;
388 copy = COPY_TEXT_PROPERTY (prop);
389 copy->start = interval->start;
391 /* Check all stacks of the following intervals, and if it contains
392 PROP, change it to the copy of it. */
393 for (; interval && interval->start < end; interval = interval->next)
394 for (i = 0; i < interval->nprops; i++)
395 if (interval->stack[i] == prop)
397 interval->stack[i] = copy;
398 M17N_OBJECT_REF (copy);
399 copy->attach_count++;
400 prop->attach_count--;
401 M17N_OBJECT_UNREF (prop);
403 M17N_OBJECT_UNREF (copy);
406 /** Divide INTERVAL of PLIST at POS if POS is in between the range of
410 divide_interval (MTextPlist *plist, MInterval *interval, int pos)
415 if (pos == interval->start || pos == interval->end)
417 new = copy_interval (interval, 0);
418 interval->end = new->start = pos;
419 new->prev = interval;
420 new->next = interval->next;
421 interval->next = new;
423 new->next->prev = new;
424 if (plist->tail == interval)
426 for (i = 0; i < new->nprops; i++)
428 new->stack[i]->attach_count++;
429 M17N_OBJECT_REF (new->stack[i]);
434 /** Check if INTERVAL of PLIST can be merged with INTERVAL->next. If
435 mergeable, extend INTERVAL to the end of INTEVAL->next, free
436 INTERVAL->next, and return INTERVAL. Otherwise, return
440 maybe_merge_interval (MTextPlist *plist, MInterval *interval)
442 int nprops = interval->nprops;
443 MInterval *next = interval->next;
446 if (! next || nprops != next->nprops)
449 for (i = 0; i < nprops; i++)
451 MTextProperty *prop = interval->stack[i];
452 MTextProperty *old = next->stack[i];
455 && (prop->val != old->val
456 || prop->end != old->start
457 || prop->control.flag & MTEXTPROP_NO_MERGE
458 || old->control.flag & MTEXTPROP_NO_MERGE))
459 return interval->next;
463 for (i = 0; i < nprops; i++)
465 MTextProperty *prop = interval->stack[i];
466 MTextProperty *old = next->stack[i];
472 for (tail = next->next; tail && tail->start < old->end;
474 for (j = 0; j < tail->nprops; j++)
475 if (tail->stack[j] == old)
478 xassert (old->attach_count);
479 tail->stack[j] = prop;
480 prop->attach_count++;
481 M17N_OBJECT_REF (prop);
483 xassert (old->attach_count == 1);
485 prop->end = old->end;
488 M17N_OBJECT_UNREF (old);
491 interval->end = next->end;
492 interval->next = next->next;
494 next->next->prev = interval;
495 if (plist->tail == next)
496 plist->tail = interval;
497 plist->cache = interval;
499 free_interval (next);
504 /*** Adjust start and end positions of intervals between HEAD and TAIL
505 (both inclusive) by diff. Adjust also start and end positions
506 of text properties belonging to those intervals. */
509 adjust_intervals (MInterval *head, MInterval *tail, int diff)
516 /* Adjust end poistions of properties starting before HEAD. */
517 for (i = 0; i < head->nprops; i++)
519 prop = head->stack[i];
520 if (prop->start < head->start)
524 /* Adjust start and end positions of properties starting at
525 HEAD, and adjust HEAD itself. */
528 for (i = 0; i < head->nprops; i++)
530 prop = head->stack[i];
531 if (prop->start == head->start)
532 prop->start += diff, prop->end += diff;
543 /* Adjust start poistions of properties ending after TAIL. */
544 for (i = 0; i < tail->nprops; i++)
546 prop = tail->stack[i];
547 if (prop->end > tail->end)
551 /* Adjust start and end positions of properties ending at
552 TAIL, and adjust TAIL itself. */
555 for (i = 0; i < tail->nprops; i++)
557 prop = tail->stack[i];
558 if (prop->end == tail->end)
559 prop->start += diff, prop->end += diff;
570 /* Return an interval of PLIST that covers the position POS. */
573 find_interval (MTextPlist *plist, int pos)
578 if (pos < plist->head->end)
580 if (pos >= plist->tail->start)
581 return (pos < plist->tail->end ? plist->tail : NULL);
583 interval = plist->cache;
585 if (pos < interval->start)
586 highest = interval->prev, interval = plist->head->next;
587 else if (pos < interval->end)
590 highest = plist->tail->prev, interval = interval->next;
592 if (pos - interval->start < highest->end - pos)
594 while (interval->end <= pos)
595 /* Here, we are sure that POS is not included in PLIST->tail,
596 thus, INTERVAL->next always points a valid next
598 interval = interval->next;
602 while (highest->start > pos)
603 highest = highest->prev;
606 plist->cache = interval;
610 /* Push text property PROP on the stack of INTERVAL. */
612 #define PUSH_PROP(interval, prop) \
614 int n = (interval)->nprops; \
616 PREPARE_INTERVAL_STACK ((interval), n + 1); \
617 (interval)->stack[n] = (prop); \
618 (interval)->nprops += 1; \
619 (prop)->attach_count++; \
620 M17N_OBJECT_REF (prop); \
621 if ((prop)->start > (interval)->start) \
622 (prop)->start = (interval)->start; \
623 if ((prop)->end < (interval)->end) \
624 (prop)->end = (interval)->end; \
628 /* Pop the topmost text property of INTERVAL from the stack. If it
629 ends after INTERVAL->end, split it. */
631 #define POP_PROP(interval) \
633 MTextProperty *prop; \
635 (interval)->nprops--; \
636 prop = (interval)->stack[(interval)->nprops]; \
637 xassert (prop->control.ref_count > 0); \
638 xassert (prop->attach_count > 0); \
639 if (prop->start < (interval)->start) \
641 if (prop->end > (interval)->end) \
642 split_property (prop, (interval)->next); \
643 prop->end = (interval)->start; \
645 else if (prop->end > (interval)->end) \
646 prop->start = (interval)->end; \
647 prop->attach_count--; \
648 if (! prop->attach_count) \
650 M17N_OBJECT_UNREF (prop); \
654 #define REMOVE_PROP(interval, prop) \
658 for (i = (interval)->nprops - 1; i >= 0; i--) \
659 if ((interval)->stack[i] == (prop)) \
663 (interval)->nprops--; \
664 for (; i < (interval)->nprops; i++) \
665 (interval)->stack[i] = (interval)->stack[i + 1]; \
666 (prop)->attach_count--; \
667 if (! (prop)->attach_count) \
669 M17N_OBJECT_UNREF (prop); \
673 #ifdef TEXT_PROP_DEBUG
675 check_plist (MTextPlist *plist, int start)
677 MInterval *interval = plist->head;
678 MInterval *cache = plist->cache;
681 if (interval->start != start
682 || interval->start >= interval->end)
683 return mdebug_hook ();
688 if (interval == interval->next)
689 return mdebug_hook ();
691 if (interval == cache)
694 if (interval->start >= interval->end)
695 return mdebug_hook ();
697 ? (interval->end != interval->next->start
698 || interval != interval->next->prev)
699 : interval != plist->tail))
700 return mdebug_hook ();
701 for (i = 0; i < interval->nprops; i++)
703 if (interval->stack[i]->start > interval->start
704 || interval->stack[i]->end < interval->end)
705 return mdebug_hook ();
707 if (! interval->stack[i]->attach_count)
708 return mdebug_hook ();
709 if (! interval->stack[i]->mt)
710 return mdebug_hook ();
711 if (interval->stack[i]->start == interval->start)
713 MTextProperty *prop = interval->stack[i];
714 int count = prop->attach_count - 1;
715 MInterval *interval2;
717 for (interval2 = interval->next;
718 interval2 && interval2->start < prop->end;
719 count--, interval2 = interval2->next)
721 return mdebug_hook ();
724 if (interval->stack[i]->end > interval->end)
726 MTextProperty *prop = interval->stack[i];
727 MInterval *interval2;
730 for (interval2 = interval->next;
731 interval2 && interval2->start < prop->end;
732 interval2 = interval2->next)
734 for (j = 0; j < interval2->nprops; j++)
735 if (interval2->stack[j] == prop)
737 if (j == interval2->nprops)
738 return mdebug_hook ();
741 if (interval->stack[i]->start < interval->start)
743 MTextProperty *prop = interval->stack[i];
744 MInterval *interval2;
747 for (interval2 = interval->prev;
748 interval2 && interval2->end > prop->start;
749 interval2 = interval2->prev)
751 for (j = 0; j < interval2->nprops; j++)
752 if (interval2->stack[j] == prop)
754 if (j == interval2->nprops)
755 return mdebug_hook ();
759 interval = interval->next;
762 return mdebug_hook ();
763 if (plist->head->prev || plist->tail->next)
764 return mdebug_hook ();
770 /** Return a copy of plist that contains intervals between FROM and TO
771 of PLIST. The copy goes to the position POS of M-text MT. */
774 copy_single_property (MTextPlist *plist, int from, int to, MText *mt, int pos)
777 MInterval *interval1, *interval2;
779 int diff = pos - from;
781 int mask_bits = MTEXTPROP_VOLATILE_STRONG | MTEXTPROP_VOLATILE_WEAK;
783 MSTRUCT_CALLOC (new, MERROR_TEXTPROP);
784 new->key = plist->key;
787 interval1 = find_interval (plist, from);
788 new->head = copy_interval (interval1, mask_bits);
789 for (interval1 = interval1->next, interval2 = new->head;
790 interval1 && interval1->start < to;
791 interval1 = interval1->next, interval2 = interval2->next)
793 interval2->next = copy_interval (interval1, mask_bits);
794 interval2->next->prev = interval2;
796 new->tail = interval2;
797 new->head->start = from;
799 for (interval1 = new->head; interval1; interval1 = interval1->next)
800 for (i = 0; i < interval1->nprops; i++)
801 if (interval1->start == interval1->stack[i]->start
802 || interval1 == new->head)
804 prop = interval1->stack[i];
805 interval1->stack[i] = COPY_TEXT_PROPERTY (prop);
806 interval1->stack[i]->mt = mt;
807 interval1->stack[i]->attach_count++;
808 if (interval1->stack[i]->start < from)
809 interval1->stack[i]->start = from;
810 if (interval1->stack[i]->end > to)
811 interval1->stack[i]->end = to;
812 for (interval2 = interval1->next; interval2;
813 interval2 = interval2->next)
814 for (j = 0; j < interval2->nprops; j++)
815 if (interval2->stack[j] == prop)
817 interval2->stack[j] = interval1->stack[i];
818 interval1->stack[i]->attach_count++;
819 M17N_OBJECT_REF (interval1->stack[i]);
822 adjust_intervals (new->head, new->tail, diff);
823 new->cache = new->head;
824 for (interval1 = new->head; interval1 && interval1->next;
825 interval1 = maybe_merge_interval (new, interval1));
826 xassert (check_plist (new, pos) == 0);
827 if (new->head == new->tail
828 && new->head->nprops == 0)
830 free_interval (new->head);
838 /** Return a newly allocated plist whose key is KEY on M-text MT. */
841 new_plist (MText *mt, MSymbol key)
845 MSTRUCT_MALLOC (plist, MERROR_TEXTPROP);
847 plist->head = new_interval (0, mtext_nchars (mt));
848 plist->tail = plist->head;
849 plist->cache = plist->head;
850 plist->next = mt->plist;
855 /* Free PLIST and return PLIST->next. */
858 free_textplist (MTextPlist *plist)
860 MTextPlist *next = plist->next;
861 MInterval *interval = plist->head;
865 while (interval->nprops > 0)
867 interval = free_interval (interval);
873 /** Return a plist that contains the property KEY of M-text MT. If
874 such a plist does not exist and CREATE is nonzero, create a new
875 plist and return it. */
878 get_plist_create (MText *mt, MSymbol key, int create)
883 while (plist && plist->key != key)
886 /* If MT does not have PROP, make one. */
887 if (! plist && create)
888 plist = new_plist (mt, key);
892 /* Detach PROP. INTERVAL (if not NULL) contains PROP. */
895 detach_property (MTextPlist *plist, MTextProperty *prop, MInterval *interval)
903 M17N_OBJECT_REF (prop);
905 while (interval->start > prop->start)
906 interval = interval->prev;
908 interval = find_interval (plist, prop->start);
912 REMOVE_PROP (interval, prop);
913 if (interval->end == to)
915 interval = interval->next;
917 xassert (prop->attach_count == 0 && prop->mt == NULL);
918 M17N_OBJECT_UNREF (prop);
920 while (head && head->end <= to)
921 head = maybe_merge_interval (plist, head);
922 xassert (check_plist (plist, 0) == 0);
925 /* Delete text properties of PLIST between FROM and TO. MASK_BITS
926 specifies what kind of properties to delete. If DELETING is
927 nonzero, delete such properties too that are completely included in
930 If the resulting PLIST still has any text properties, return 1,
934 delete_properties (MTextPlist *plist, int from, int to,
935 int mask_bits, int deleting)
939 int modified_from = from;
940 int modified_to = to;
944 for (interval = find_interval (plist, from);
945 interval && interval->start < to;
946 interval = interval->next)
947 for (i = 0; i < interval->nprops; i++)
949 MTextProperty *prop = interval->stack[i];
951 if (prop->control.flag & mask_bits)
953 if (prop->start < modified_from)
954 modified_from = prop->start;
955 if (prop->end > modified_to)
956 modified_to = prop->end;
957 detach_property (plist, prop, interval);
961 else if (deleting && prop->start >= from && prop->end <= to)
963 detach_property (plist, prop, interval);
971 interval = find_interval (plist, modified_from);
972 while (interval && interval->start < modified_to)
973 interval = maybe_merge_interval (plist, interval);
976 return (plist->head != plist->tail || plist->head->nprops > 0);
980 pop_interval_properties (MInterval *interval)
982 while (interval->nprops > 0)
988 pop_all_properties (MTextPlist *plist, int from, int to)
992 /* Be sure to have interval boundary at TO. */
993 interval = find_interval (plist, to);
994 if (interval && interval->start < to)
995 divide_interval (plist, interval, to);
997 /* Be sure to have interval boundary at FROM. */
998 interval = find_interval (plist, from);
999 if (interval->start < from)
1001 divide_interval (plist, interval, from);
1002 interval = interval->next;
1005 pop_interval_properties (interval);
1006 while (interval->end < to)
1008 MInterval *next = interval->next;
1010 pop_interval_properties (next);
1011 interval->end = next->end;
1012 interval->next = next->next;
1014 interval->next->prev = interval;
1015 if (next == plist->tail)
1016 plist->tail = interval;
1017 if (plist->cache == next)
1018 plist->cache = interval;
1019 free_interval (next);
1025 /* Delete volatile text properties between FROM and TO. If KEY is
1026 Mnil, we are going to delete text, thus both strongly and weakly
1027 volatile properties must be deleted. Otherwise we are going to
1028 modify a text property KEY, thus only strongly volatile properties
1029 whose key is not KEY must be deleted. */
1032 prepare_to_modify (MText *mt, int from, int to, MSymbol key)
1034 MTextPlist *plist = mt->plist, *prev = NULL;
1035 int mask_bits = MTEXTPROP_VOLATILE_STRONG;
1036 int deleting = (key == Mnil) && (from < to);
1039 mask_bits |= MTEXTPROP_VOLATILE_WEAK;
1042 if (plist->key != key
1043 && ! delete_properties (plist, from, to, mask_bits, deleting))
1046 plist = prev->next = free_textplist (plist);
1048 plist = mt->plist = free_textplist (plist);
1051 prev = plist, plist = plist->next;
1056 extract_text_properties (MText *mt, int from, int to, MSymbol key,
1060 MTextPlist *list = get_plist_create (mt, key, 0);
1061 MInterval *interval;
1065 interval = find_interval (list, from);
1066 if (interval->nprops == 0
1067 && interval->start <= from && interval->end >= to)
1070 while (interval && interval->start < to)
1072 if (interval->nprops == 0)
1073 top = mplist_find_by_key (top, Mnil);
1076 MPlist *current = top, *place;
1079 for (i = 0; i < interval->nprops; i++)
1081 MTextProperty *prop = interval->stack[i];
1083 place = mplist_find_by_value (current, prop);
1085 current = MPLIST_NEXT (place);
1088 place = mplist_find_by_value (top, prop);
1092 if (MPLIST_NEXT (place) == MPLIST_NEXT (current))
1095 mplist_push (current, Mt, prop);
1096 current = MPLIST_NEXT (current);
1100 interval = interval->next;
1105 #define XML_TEMPLATE "<?xml version=\"1.0\" ?>\n\
1106 <!DOCTYPE mtext [\n\
1107 <!ELEMENT mtext (property*,body+)>\n\
1108 <!ELEMENT property EMPTY>\n\
1109 <!ELEMENT body (#PCDATA)>\n\
1110 <!ATTLIST property key CDATA #REQUIRED>\n\
1111 <!ATTLIST property value CDATA #REQUIRED>\n\
1112 <!ATTLIST property from CDATA #REQUIRED>\n\
1113 <!ATTLIST property to CDATA #REQUIRED>\n\
1114 <!ATTLIST property control CDATA #REQUIRED>\n\
1120 /* for debugging... */
1124 dump_interval (MInterval *interval, int indent)
1126 char *prefix = (char *) alloca (indent + 1);
1129 memset (prefix, 32, indent);
1132 fprintf (stderr, "(interval %d-%d (%d)", interval->start, interval->end,
1134 for (i = 0; i < interval->nprops; i++)
1135 fprintf (stderr, "\n%s (%d %d/%d %d-%d 0x%x)",
1137 interval->stack[i]->control.ref_count,
1138 interval->stack[i]->attach_count,
1139 interval->stack[i]->start, interval->stack[i]->end,
1140 (unsigned) interval->stack[i]->val);
1141 fprintf (stderr, ")");
1145 dump_textplist (MTextPlist *plist, int indent)
1147 char *prefix = (char *) alloca (indent + 1);
1149 memset (prefix, 32, indent);
1152 fprintf (stderr, "(properties");
1154 fprintf (stderr, ")\n");
1157 fprintf (stderr, "\n");
1160 MInterval *interval = plist->head;
1162 fprintf (stderr, "%s (%s", prefix, msymbol_name (plist->key));
1165 fprintf (stderr, " (%d %d", interval->start, interval->end);
1166 if (interval->nprops > 0)
1170 for (i = 0; i < interval->nprops; i++)
1171 fprintf (stderr, " 0x%x", (int) interval->stack[i]->val);
1173 fprintf (stderr, ")");
1174 interval = interval->next;
1176 fprintf (stderr, ")\n");
1177 xassert (check_plist (plist, 0) == 0);
1178 plist = plist->next;
1189 text_property_table.count = 0;
1190 Mtext_prop_serializer = msymbol ("text-prop-serializer");
1191 Mtext_prop_deserializer = msymbol ("text-prop-deserializer");
1198 MIntervalPool *pool = interval_pool_root.next;
1202 MIntervalPool *next = pool->next;
1206 interval_pool_root.next = NULL;
1207 mdebug__report_object ("Text property", &text_property_table);
1211 /** Free all plists. */
1214 mtext__free_plist (MText *mt){
1216 MTextPlist *plist = mt->plist;
1219 plist = free_textplist (plist);
1224 /** Extract intervals between FROM and TO of all properties (except
1225 for volatile ones) in PLIST, and make a new plist from them for
1229 mtext__copy_plist (MTextPlist *plist, int from, int to, MText *mt, int pos)
1231 MTextPlist *copy, *this;
1235 for (copy = NULL; plist && ! copy; plist = plist->next)
1236 copy = copy_single_property (plist, from, to, mt, pos);
1239 for (; plist; plist = plist->next)
1240 if ((this = copy_single_property (plist, from, to, mt, pos)))
1250 mtext__adjust_plist_for_delete (MText *mt, int pos, int len)
1255 if (len == 0 || pos == mt->nchars)
1257 if (len == mt->nchars)
1259 mtext__free_plist (mt);
1264 prepare_to_modify (mt, pos, to, Mnil);
1265 for (plist = mt->plist; plist; plist = plist->next)
1267 MInterval *interval = pop_all_properties (plist, pos, to);
1268 MInterval *prev = interval->prev, *next = interval->next;
1276 adjust_intervals (next, plist->tail, -len);
1282 next = maybe_merge_interval (plist, prev);
1283 plist->cache = next ? next : prev;
1284 free_interval (interval);
1285 xassert (check_plist (plist, 0) == 0);
1290 mtext__adjust_plist_for_insert (MText *mt, int pos, int nchars,
1293 MTextPlist *pl, *pl_last, *pl2, *p;
1295 MInterval *interval;
1297 if (mt->nchars == 0)
1299 mtext__free_plist (mt);
1303 if (pos > 0 && pos < mtext_nchars (mt))
1304 prepare_to_modify (mt, pos, pos, Mnil);
1306 for (pl_last = NULL, pl = mt->plist; pl; pl_last = pl, pl = pl->next)
1308 MInterval *interval, *prev, *next, *head, *tail;
1311 prev = NULL, next = pl->head;
1312 else if (pos == mtext_nchars (mt))
1313 prev = pl->tail, next = NULL;
1316 next = find_interval (pl, pos);
1317 if (next->start < pos)
1319 divide_interval (pl, next, pos);
1322 for (i = 0; i < next->nprops; i++)
1323 if (next->stack[i]->start < pos)
1324 split_property (next->stack[i], next);
1328 xassert (check_plist (pl, 0) == 0);
1329 for (p = NULL, pl2 = plist; pl2 && pl->key != pl2->key;
1330 p = pl2, pl2 = p->next);
1333 xassert (check_plist (pl2, pl2->head->start) == 0);
1335 p->next = pl2->next;
1337 plist = plist->next;
1345 head = tail = new_interval (pos, pos + nchars);
1358 adjust_intervals (next, pl->tail, nchars);
1360 xassert (check_plist (pl, 0) == 0);
1361 if (prev && prev->nprops > 0)
1363 for (interval = prev;
1364 interval->next != next && interval->next->nprops == 0;
1365 interval = interval->next)
1366 for (i = 0; i < interval->nprops; i++)
1368 MTextProperty *prop = interval->stack[i];
1370 if (prop->control.flag & MTEXTPROP_REAR_STICKY)
1371 PUSH_PROP (interval->next, prop);
1374 xassert (check_plist (pl, 0) == 0);
1375 if (next && next->nprops > 0)
1377 for (interval = next;
1378 interval->prev != prev && interval->prev->nprops == 0;
1379 interval = interval->prev)
1380 for (i = 0; i < interval->nprops; i++)
1382 MTextProperty *prop = interval->stack[i];
1384 if (prop->control.flag & MTEXTPROP_FRONT_STICKY)
1385 PUSH_PROP (interval->prev, prop);
1389 interval = prev ? prev : pl->head;
1390 pl->cache = interval;
1391 while (interval && interval->start <= pos + nchars)
1392 interval = maybe_merge_interval (pl, interval);
1393 xassert (check_plist (pl, 0) == 0);
1397 pl_last->next = plist;
1401 for (; plist; plist = plist->next)
1403 plist->cache = plist->head;
1406 if (plist->head->nprops)
1408 interval = new_interval (0, pos);
1409 interval->next = plist->head;
1410 plist->head->prev = interval;
1411 plist->head = interval;
1414 plist->head->start = 0;
1416 if (pos < mtext_nchars (mt))
1418 if (plist->tail->nprops)
1420 interval = new_interval (pos + nchars,
1421 mtext_nchars (mt) + nchars);
1422 interval->prev = plist->tail;
1423 plist->tail->next = interval;
1424 plist->tail = interval;
1427 plist->tail->end = mtext_nchars (mt) + nchars;
1429 xassert (check_plist (plist, 0) == 0);
1434 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1439 /*** @addtogroup m17nTextProperty */
1444 @brief Get the value of the topmost text property.
1446 The mtext_get_prop () function searches the character at $POS in
1447 M-text $MT for the text property whose key is $KEY.
1450 If a text property is found, mtext_get_prop () returns the value
1451 of the property. If the property has multiple values, it returns
1452 the topmost one. If no such property is found, it returns @c NULL
1453 without changing the external variable #merror_code.
1455 If an error is detected, mtext_get_prop () returns @c NULL and
1456 assigns an error code to the external variable #merror_code.
1458 @note If @c NULL is returned without an error, there are two
1461 @li the character at $POS does not have a property whose key is $KEY, or
1463 @li the character does have such a property and its value is @c NULL.
1465 If you need to distinguish these two cases, use the
1466 mtext_get_prop_values () function instead. */
1469 @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î°ìÈÖ¾å¤ÎÃͤòÆÀ¤ë
1471 ´Ø¿ô mtext_get_prop () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤È¤¤¤¦°ÌÃ֤ˤ¢¤ëʸ
1472 »ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£
1475 ¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬Â¸ºß¤¹¤ë¤Ê¤é¡¢mtext_get_prop () ¤Ï¤½¤ÎÃͤòÊÖ
1476 ¤¹¡£Ãͤ¬Ê£¿ô¸ºß¤¹¤ë¤È¤¤Ï¡¢°ìÈÖ¾å¤ÎÃͤòÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð³°Éô
1477 ÊÑ¿ô #merror_code ¤òÊѹ¹¤¹¤ë¤³¤È¤Ê¤¯ @c NULL ¤òÊÖ¤¹¡£
1479 ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë
1480 ¥¨¥é¡¼ ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1482 @note ¥¨¥é¡¼¤Ê¤·¤Ç @c NULL ¤¬ÊÖ¤µ¤ì¤¿¾ì¹ç¤Ë¤ÏÆó¤Ä¤Î²ÄǽÀ¤¬¤¢¤ë¡£
1484 @li $POS ¤Î°ÌÃÖ¤Îʸ»ú¤Ï $KEY ¤ò¥¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¡£
1486 @li ¤½¤Îʸ»ú¤Ï $KEY ¤ò¥¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¤¬¡¢¤½¤ÎÃͤ¬ @c
1489 ¤³¤ÎÆó¤Ä¤ò¶èÊ̤¹¤ëɬÍפ¬¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢´Ø¿ô mtext_get_prop_values ()
1492 @latexonly \IPAlabel{mtext_get_prop} @endlatexonly */
1496 @c MERROR_RANGE, @c MERROR_SYMBOL
1499 mtext_get_prop_values (), mtext_put_prop (), mtext_put_prop_values (),
1500 mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */
1503 mtext_get_prop (MText *mt, int pos, MSymbol key)
1506 MInterval *interval;
1509 M_CHECK_POS (mt, pos, NULL);
1511 plist = get_plist_create (mt, key, 0);
1515 interval = find_interval (plist, pos);
1516 val = (interval->nprops
1517 ? interval->stack[interval->nprops - 1]->val : NULL);
1524 @brief Get multiple values of a text property
1526 The mtext_get_prop_values () function searches the character at
1527 $POS in M-text $MT for the property whose key is $KEY. If such
1528 a property is found, its values are stored in the memory area
1529 pointed to by $VALUES. $NUM limits the maximum number of stored
1533 If the operation was successful, mtext_get_prop_values () returns
1534 the number of actually stored values. If the character at $POS
1535 does not have a property whose key is $KEY, the return value is
1536 0. If an error is detected, mtext_get_prop_values () returns -1 and
1537 assigns an error code to the external variable #merror_code. */
1540 @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊ£¿ôÆÀ¤ë
1542 ´Ø¿ô mtext_get_prop_values () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤È¤¤¤¦°ÌÃÖ
1543 ¤Ë¤¢¤ëʸ»ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£¤â¤·¤½
1544 ¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬¸«¤Ä¤«¤ì¤Ð¡¢¤½¤ì¤¬»ý¤ÄÃÍ(Ê£¿ô²Ä)¤ò $VALUES ¤Î
1545 »Ø¤¹¥á¥â¥êÎΰè¤Ë³ÊǼ¤¹¤ë¡£$NUM ¤Ï³ÊǼ¤¹¤ëÃͤοô¤Î¾å¸Â¤Ç¤¢¤ë¡£
1548 ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_get_prop_values () ¤Ï¼ÂºÝ¤Ë¥á¥â¥ê¤Ë³ÊǼ¤µ
1549 ¤ì¤¿Ãͤοô¤òÊÖ¤¹¡£$POS ¤Î°ÌÃÖ¤Îʸ»ú¤¬ $KEY ¤ò¥¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£
1550 ¤ò»ý¤¿¤Ê¤±¤ì¤Ð 0 ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô
1551 #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1553 @latexonly \IPAlabel{mtext_get_prop_values} @endlatexonly */
1557 @c MERROR_RANGE, @c MERROR_SYMBOL
1560 mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (),
1561 mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */
1564 mtext_get_prop_values (MText *mt, int pos, MSymbol key,
1565 void **values, int num)
1568 MInterval *interval;
1573 M_CHECK_POS (mt, pos, -1);
1575 plist = get_plist_create (mt, key, 0);
1579 interval = find_interval (plist, pos);
1580 /* It is assured that INTERVAL is not NULL. */
1581 nprops = interval->nprops;
1582 if (nprops == 0 || num <= 0)
1584 if (nprops == 1 || num == 1)
1586 values[0] = interval->stack[nprops - 1]->val;
1591 num = nprops, offset = 0;
1593 offset = nprops - num;
1594 for (i = 0; i < num; i++)
1595 values[i] = interval->stack[offset + i]->val;
1602 @brief Get list of text property keys at a position of an M-text.
1604 The mtext_get_prop_keys () function creates an array whose
1605 elements are the keys of text properties found at position $POS in
1606 M-text $MT, and sets *$KEYS to the address of the created array.
1607 The user is responsible to free the memory allocated for
1612 If the operation was successful, mtext_get_prop_keys () returns
1613 the length of the key list. Otherwise it returns -1 and assigns
1614 an error code to the external variable #merror_code.
1619 @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤Î¥ê¥¹¥È¤òÆÀ¤ë
1621 ´Ø¿ô mtext_get_prop_keys () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤Î°ÌÃ֤ˤ¢¤ë
1622 ʸ»ú¤¬»ý¤Ã¤Æ¤¤¤ë¤¹¤Ù¤Æ¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤«¤é¤Ê¤ëÇÛÎó¤òºî¤ê¡¢
1623 ¤½¤ÎÇÛÎó¤Î¥¢¥É¥ì¥¹¤ò *$KEYS ¤Ë¥»¥Ã¥È¤¹¤ë¡£¤³¤ÎÇÛÎó¤Î¤¿¤á¤Ë³ÎÊݤµ¤ì
1624 ¤¿¥á¥â¥ê¤ò²òÊü¤¹¤ë¤Î¤Ï¥æ¡¼¥¶¤ÎÀÕǤ¤Ç¤¢¤ë¡£
1627 ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_get_prop_keys () ¤ÏÆÀ¤é¤ì¤¿¥ê¥¹¥È¤ÎŤµ¤òÊÖ
1628 ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò
1637 mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (),
1638 mtext_get_prop_values (), mtext_push_prop (), mtext_pop_prop () */
1641 mtext_get_prop_keys (MText *mt, int pos, MSymbol **keys)
1646 M_CHECK_POS (mt, pos, -1);
1647 for (i = 0, plist = mt->plist; plist; i++, plist = plist->next);
1653 MTABLE_MALLOC (*keys, i, MERROR_TEXTPROP);
1654 for (i = 0, plist = mt->plist; plist; plist = plist->next)
1656 MInterval *interval = find_interval (plist, pos);
1658 if (interval->nprops)
1659 (*keys)[i++] = plist->key;
1667 @brief Set a text property
1669 The mtext_put_prop () function sets a text property to the
1670 characters between $FROM (inclusive) and $TO (exclusive) in M-text
1671 $MT. $KEY and $VAL specify the key and the value of the text
1676 M-text: |<------------|-------- MT ---------|------------>|
1677 PROP : <------------------ OLD_VAL -------------------->
1684 M-text: |<------------|-------- MT ---------|------------>|
1685 PROP : <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL-->
1689 If the operation was successful, mtext_put_prop () returns 0.
1690 Otherwise it returns -1 and assigns an error code to the external
1691 variable #merror_code. */
1694 @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ¤ë
1696 ´Ø¿ô mtext_put_prop () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é
1697 $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¤è
1698 ¤¦¤Ê¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥»¥Ã¥È¤¹¤ë¡£
1703 M-text: |<------------|-------- MT ---------|------------>|
1704 PROP: <------------------ OLD_VAL -------------------->
1711 M-text: |<------------|-------- MT ---------|------------>|
1712 PROP: <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL-->
1716 ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_put_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1
1717 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1719 @latexonly \IPAlabel{mtext_put_prop} @endlatexonly */
1723 @c MERROR_RANGE, @c MERROR_SYMBOL
1726 mtext_put_prop_values (), mtext_get_prop (),
1727 mtext_get_prop_values (), mtext_push_prop (),
1728 mtext_pop_prop (), mtext_prop_range () */
1731 mtext_put_prop (MText *mt, int from, int to, MSymbol key, void *val)
1734 MTextProperty *prop;
1735 MInterval *interval;
1737 M_CHECK_RANGE (mt, from, to, -1, 0);
1739 prepare_to_modify (mt, from, to, key);
1740 plist = get_plist_create (mt, key, 1);
1741 interval = pop_all_properties (plist, from, to);
1742 prop = new_text_property (mt, from, to, key, val, 0);
1743 PUSH_PROP (interval, prop);
1744 M17N_OBJECT_UNREF (prop);
1746 maybe_merge_interval (plist, interval);
1748 maybe_merge_interval (plist, interval->prev);
1749 xassert (check_plist (plist, 0) == 0);
1756 @brief Set multiple text properties with the same key
1758 The mtext_put_prop_values () function sets a text property to the
1759 characters between $FROM (inclusive) and $TO (exclusive) in M-text
1760 $MT. $KEY and $VALUES specify the key and the values of the text
1761 property. $NUM specifies the number of property values to be set.
1764 If the operation was successful, mtext_put_prop_values () returns
1765 0. Otherwise it returns -1 and assigns an error code to the
1766 external variable #merror_code. */
1769 @brief Ʊ¤¸¥¡¼¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÊ£¿ôÀßÄꤹ¤ë
1771 ´Ø¿ô mtext_put_prop_values () ¤Ï¡¢M-Text $MT ¤Î$FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë
1772 ¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥»¥Ã¥È
1773 ¤¹¤ë¡£¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤Ï$KEY¤Ë¤è¤Ã¤Æ¡¢ÃÍ(Ê£¿ô²Ä)¤Ï$VALUES
1774 ¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤ë¡£$NUM ¤ÏÀßÄꤵ¤ì¤ëÃͤθĿô¤Ç¤¢¤ë¡£
1777 ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_put_prop_values () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±
1778 ¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1780 @latexonly \IPAlabel{mtext_put_prop_values} @endlatexonly */
1784 @c MERROR_RANGE, @c MERROR_SYMBOL
1787 mtext_put_prop (), mtext_get_prop (), mtext_get_prop_values (),
1788 mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */
1791 mtext_put_prop_values (MText *mt, int from, int to,
1792 MSymbol key, void **values, int num)
1795 MInterval *interval;
1798 M_CHECK_RANGE (mt, from, to, -1, 0);
1800 prepare_to_modify (mt, from, to, key);
1801 plist = get_plist_create (mt, key, 1);
1802 interval = pop_all_properties (plist, from, to);
1805 PREPARE_INTERVAL_STACK (interval, num);
1806 for (i = 0; i < num; i++)
1809 = new_text_property (mt, from, to, key, values[i], 0);
1810 PUSH_PROP (interval, prop);
1811 M17N_OBJECT_UNREF (prop);
1815 maybe_merge_interval (plist, interval);
1817 maybe_merge_interval (plist, interval->prev);
1818 xassert (check_plist (plist, 0) == 0);
1825 @brief Push a text property
1827 The mtext_push_prop () function pushes a text property whose key
1828 is $KEY and value is $VAL to the characters between $FROM
1829 (inclusive) and $TO (exclusive) in $MT.
1833 M-text: |<------------|-------- MT ---------|------------>|
1834 PROP : <------------------ OLD_VAL -------------------->
1841 M-text: |<------------|-------- MT ---------|------------>|
1842 PROP : <------------------- OLD_VAL ------------------->
1843 PROP : <-------- VAL ------->
1847 If the operation was successful, mtext_push_prop () returns 0.
1848 Otherwise it returns -1 and assigns an error code to the external
1849 variable #merror_code. */
1852 @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥×¥Ã¥·¥å¤¹¤ë
1854 ´Ø¿ô mtext_push_prop () ¤Ï¡¢¥¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¥Æ¥¥¹¥È
1855 ¥×¥í¥Ñ¥Æ¥£¤ò¡¢M-text $MT Ãæ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê
1856 ¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¥×¥Ã¥·¥å¤¹¤ë¡£
1860 M-text: |<------------|-------- MT ---------|------------>|
1861 PROP : <------------------ OLD_VAL -------------------->
1866 M-text: |<------------|-------- MT ---------|------------>|
1867 PROP : <------------------- OLD_VAL ------------------->
1868 PROP : <-------- VAL ------->
1872 ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_push_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð
1873 -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1875 @latexonly \IPAlabel{mtext_push_prop} @endlatexonly */
1879 @c MERROR_RANGE, @c MERROR_SYMBOL
1882 mtext_put_prop (), mtext_put_prop_values (),
1883 mtext_get_prop (), mtext_get_prop_values (),
1884 mtext_pop_prop (), mtext_prop_range () */
1887 mtext_push_prop (MText *mt, int from, int to,
1888 MSymbol key, void *val)
1891 MInterval *head, *tail, *interval;
1892 MTextProperty *prop;
1893 int check_head, check_tail;
1895 M_CHECK_RANGE (mt, from, to, -1, 0);
1897 prepare_to_modify (mt, from, to, key);
1898 plist = get_plist_create (mt, key, 1);
1900 /* Find an interval that covers the position FROM. */
1901 head = find_interval (plist, from);
1903 /* If the found interval starts before FROM, divide it at FROM. */
1904 if (head->start < from)
1906 divide_interval (plist, head, from);
1913 /* Find an interval that ends at TO. If TO is not at the end of an
1914 interval, make one that ends at TO. */
1915 if (head->end == to)
1920 else if (head->end > to)
1922 divide_interval (plist, head, to);
1928 tail = find_interval (plist, to);
1934 else if (tail->start == to)
1941 divide_interval (plist, tail, to);
1946 prop = new_text_property (mt, from, to, key, val, 0);
1948 /* Push PROP to the current values of intervals between HEAD and TAIL
1949 (both inclusive). */
1950 for (interval = head; ; interval = interval->next)
1952 PUSH_PROP (interval, prop);
1953 if (interval == tail)
1957 M17N_OBJECT_UNREF (prop);
1959 /* If there is a possibility that TAIL now has the same value as the
1960 next one, check it and concatenate them if necessary. */
1961 if (tail->next && check_tail)
1962 maybe_merge_interval (plist, tail);
1964 /* If there is a possibility that HEAD now has the same value as the
1965 previous one, check it and concatenate them if necessary. */
1966 if (head->prev && check_head)
1967 maybe_merge_interval (plist, head->prev);
1969 xassert (check_plist (plist, 0) == 0);
1976 @brief Pop a text property
1978 The mtext_pop_prop () function removes the topmost text property
1979 whose key is $KEY from the characters between $FROM (inclusive)
1980 and and $TO (exclusive) in $MT.
1982 This function does nothing if characters in the region have no
1987 M-text: |<------------|-------- MT ---------|------------>|
1988 PROP : <------------------ OLD_VAL -------------------->
1995 M-text: |<------------|-------- MT ---------|------------>|
1996 PROP : <--OLD_VAL-->| |<--OLD_VAL-->|
2000 If the operation was successful, mtext_pop_prop () return 0.
2001 Otherwise it returns -1 and assigns an error code to the external
2002 variable #merror_code. */
2005 @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ý¥Ã¥×¤¹¤ë
2007 ´Ø¿ô mtext_pop_prop () ¤Ï¡¢¥¡¼¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î
2008 ¤¦¤Á°ìÈÖ¾å¤Î¤â¤Î¤ò¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO¡Ê´Þ¤Þ
2009 ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤«¤é¼è¤ê½ü¤¯¡£
2011 »ØÄêÈϰϤÎʸ»ú¤¬¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¤Ê¤é¤Ð¡¢¤³¤Î´Ø¿ô¤Ï²¿
2016 M-text: |<------------|-------- MT ---------|------------>|
2017 PROP : <------------------ OLD_VAL -------------------->
2019 ¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£
2022 M-text: |<------------|-------- MT ---------|------------>|
2023 PROP : <--OLD_VAL-->| |<--OLD_VAL-->|
2027 ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_pop_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1
2028 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
2030 @latexonly \IPAlabel{mtext_pop_prop} @endlatexonly */
2034 @c MERROR_RANGE, @c MERROR_SYMBOL
2037 mtext_put_prop (), mtext_put_prop_values (),
2038 mtext_get_prop (), mtext_get_prop_values (),
2039 mtext_push_prop (), mtext_prop_range () */
2042 mtext_pop_prop (MText *mt, int from, int to, MSymbol key)
2045 MInterval *head, *tail;
2049 MERROR (MERROR_TEXTPROP, -1);
2050 M_CHECK_RANGE (mt, from, to, -1, 0);
2051 plist = get_plist_create (mt, key, 0);
2055 /* Find an interval that covers the position FROM. */
2056 head = find_interval (plist, from);
2058 && head->nprops == 0)
2059 /* No property to pop. */
2062 prepare_to_modify (mt, from, to, key);
2064 /* If the found interval starts before FROM and has value(s), divide
2066 if (head->start < from)
2068 if (head->nprops > 0)
2070 divide_interval (plist, head, from);
2078 /* Pop the topmost text property from each interval following HEAD.
2079 Stop at an interval that ends after TO. */
2080 for (tail = head; tail && tail->end <= to; tail = tail->next)
2081 if (tail->nprops > 0)
2086 if (tail->start < to)
2088 if (tail->nprops > 0)
2090 divide_interval (plist, tail, to);
2099 to = plist->tail->start;
2101 /* If there is a possibility that HEAD now has the same text
2102 properties as the previous one, check it and concatenate them if
2104 if (head->prev && check_head)
2106 while (head && head->end <= to)
2107 head = maybe_merge_interval (plist, head);
2109 xassert (check_plist (plist, 0) == 0);
2116 @brief Find the range where the value of a text property is the same.
2118 The mtext_prop_range () function investigates the extent where all
2119 characters have the same value for a text property. It first
2120 finds the value of the property specified by $KEY of the character
2121 at $POS in M-text $MT. Then it checks if adjacent characters have
2122 the same value for the property $KEY. The beginning and the end
2123 of the found range are stored to the variable pointed to by $FROM
2124 and $TO. The character position stored in $FROM is inclusive but
2125 that in $TO is exclusive; this fashion is compatible with the
2126 range specification in the mtext_put_prop () function, etc.
2128 If $DEEPER is not 0, not only the topmost but also all the stacked
2129 properties whose key is $KEY are compared.
2131 If $FROM is @c NULL, the beginning of range is not searched for. If
2132 $TO is @c NULL, the end of range is not searched for.
2136 If the operation was successful, mtext_prop_range () returns the
2137 number of values the property $KEY has at pos. Otherwise it
2138 returns -1 and assigns an error code to the external variable @c
2142 @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬Æ±¤¸Ãͤò¤È¤ëÈϰϤòÄ´¤Ù¤ë.
2144 ´Ø¿ô mtext_prop_range () ¤Ï¡¢Ï¢Â³¤·¤¿Ê¸»ú¤¬Æ±¤¸¥×¥í¥Ñ¥Æ¥£¤ÎÃͤò»ý¤Ã
2145 ¤Æ¤¤¤ëÈϰϤòÄ´¤Ù¤ë¡£¤Þ¤º M-text $MT ¤Î $POS ¤Î°ÌÃ֤ˤ¢¤ëʸ»ú¤Î¥×¥í
2146 ¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥¡¼ $KEY ¤Ç»ØÄꤵ¤ì¤¿¤â¤Î¤ÎÃͤò¸«¤Ä¤±¤ë¡£¤½¤·¤ÆÁ°¸å
2147 ¤Îʸ»ú¤â$KEY ¤Î¥×¥í¥Ñ¥Æ¥£¤ËƱ¤¸Ãͤò¤â¤Ã¤Æ¤¤¤ë¤«¤É¤¦¤«¤òÄ´¤Ù¤ë¡£¸«
2148 ¤Ä¤±¤¿ÈϰϤκǽé¤ÈºÇ¸å¤ò¡¢¤½¤ì¤¾¤ì $FROM ¤È $TO ¤ò¥Ý¥¤¥ó¥¿¤È¤¹¤ëÊÑ
2149 ¿ô¤ËÊݸ¤¹¤ë¡£$FROM ¤ËÊݸ¤µ¤ì¤ëʸ»ú¤Î°ÌÃ֤ϸ«¤Ä¤±¤¿ÈϰϤ˴ޤޤì¤ë
2150 ¤¬¡¢$TO ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£¡Ê$TO ¤ÎÁ°¤ÇƱ¤¸Ãͤò¤È¤ëÈϰϤϽª¤ï¤ë¡£¡Ë¤³¤Î
2151 ÈÏ°Ï»ØÄê¤ÎÊýË¡¤Ï¡¢´Ø¿ô mtext_put_prop () ¤Ê¤É¤È¶¦Ä̤Τâ¤Î¤Ç¤¢¤ë¡£
2153 $DEEPER ¤¬ 0 ¤Ç¤Ê¤±¤ì¤Ð¡¢$KEY ¤È¤¤¤¦¥¡¼¤ò»ý¤Ä¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á°ìÈÖ
2154 ¾å¤Î¤â¤Î¤À¤±¤Ç¤Ê¤¯¡¢¥¹¥¿¥Ã¥¯Ãæ¤Î¤¹¤Ù¤Æ¤Î¤â¤Î¤¬Èæ³Ó¤µ¤ì¤ë¡£
2156 $FROM ¤¬ @c NULL ¤Ê¤é¤Ð¡¢ÈϰϤλϤޤê¤Ïõº÷¤·¤Ê¤¤¡£$TO ¤¬ @c NULL
2157 ¤Ê¤é¤Ð¡¢ÈϰϤνª¤ê¤Ïõº÷¤·¤Ê¤¤¡£
2160 ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_prop_range () ¤Ï $KEY ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤοô¤ò
2161 ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð-1 ¤òÊÖ¤·¡¢ ³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼
2164 @latexonly \IPAlabel{mtext_prop_range} @endlatexonly */
2168 @c MERROR_RANGE, @c MERROR_SYMBOL
2171 mtext_put_prop (), mtext_put_prop_values (),
2172 mtext_get_prop (), mtext_get_prop_values (),
2173 mtext_pop_prop (), mtext_push_prop () */
2176 mtext_prop_range (MText *mt, MSymbol key, int pos,
2177 int *from, int *to, int deeper)
2180 MInterval *interval, *temp;
2184 M_CHECK_POS (mt, pos, -1);
2186 plist = get_plist_create (mt, key, 0);
2189 if (from) *from = 0;
2190 if (to) *to = mtext_nchars (mt);
2194 interval = find_interval (plist, pos);
2195 nprops = interval->nprops;
2196 if (deeper || ! nprops)
2198 if (from) *from = interval->start;
2199 if (to) *to = interval->end;
2200 return interval->nprops;
2203 val = nprops ? interval->stack[nprops - 1] : NULL;
2207 for (temp = interval;
2209 && (temp->prev->nprops
2211 && (val == temp->prev->stack[temp->prev->nprops - 1]))
2214 *from = temp->start;
2219 for (temp = interval;
2221 && (temp->next->nprops
2223 && val == temp->next->stack[temp->next->nprops - 1])
2233 @brief Create a text property.
2235 The mtext_property () function returns a newly allocated text
2236 property whose key is $KEY and value is $VAL. The text created
2237 property is not attached to any M-text, i.e. it is detached.
2239 $CONTROL_BITS must be 0 or logical OR of @c enum @c
2240 MTextPropertyControl. */
2243 mtext_property (MSymbol key, void *val, int control_bits)
2245 return new_text_property (NULL, 0, 0, key, val, control_bits);
2249 @brief Return the M-text of a text property.
2251 The mtext_property_mtext () function returns the M-text to which
2252 text property $PROP is attached. If $PROP is currently detached,
2253 NULL is returned. */
2256 mtext_property_mtext (MTextProperty *prop)
2262 @brief Return the key of a text property.
2264 The mtext_property_key () function returns the key (symbol) of
2265 text property $PROP. */
2268 mtext_property_key (MTextProperty *prop)
2274 @brief Return the value of a text property.
2276 The mtext_property_value () function returns the value of text
2280 mtext_property_value (MTextProperty *prop)
2286 @brief Return the start position of a text property.
2288 The mtext_property_start () function returns the start position of
2289 text property $PROP. The start position is a character position
2290 of an M-text where $PROP begins. If $PROP is detached, it returns
2294 mtext_property_start (MTextProperty *prop)
2296 return (prop->mt ? prop->start : -1);
2300 @brief Return the end position of a text property.
2302 The mtext_property_end () function returns the end position of
2303 text property $PROP. The end position is a character position of
2304 an M-text where $PROP ends. If $PROP is detached, it returns
2308 mtext_property_end (MTextProperty *prop)
2310 return (prop->mt ? prop->end : -1);
2314 @brief Get the topmost text property.
2316 The mtext_get_property () function searches the character at $POS
2317 in M-text $MT for a text property whose key is $KEY.
2320 If a text property is found, mtext_get_property () returns it. If
2321 there are multiple text properties, it returns the topmost one.
2322 If no such property is found, it returns @c NULL without changing
2323 the external variable #merror_code.
2325 If an error is detected, mtext_get_property () returns @c NULL and
2326 assigns an error code to the external variable #merror_code. */
2329 mtext_get_property (MText *mt, int pos, MSymbol key)
2332 MInterval *interval;
2334 M_CHECK_POS (mt, pos, NULL);
2336 plist = get_plist_create (mt, key, 0);
2340 interval = find_interval (plist, pos);
2341 if (! interval->nprops)
2343 return interval->stack[interval->nprops - 1];
2347 @brief Get multiple text properties.
2349 The mtext_get_properties () function searches the character at
2350 $POS in M-text $MT for properties whose key is $KEY. If such
2351 properties are found, they are stored in the memory area pointed
2352 to by $PROPS. $NUM limits the maximum number of stored
2356 If the operation was successful, mtext_get_properties () returns
2357 the number of actually stored properties. If the character at
2358 $POS does not have a property whose key is $KEY, the return value
2359 is 0. If an error is detected, mtext_get_properties () returns -1
2360 and assigns an error code to the external variable #merror_code. */
2363 mtext_get_properties (MText *mt, int pos, MSymbol key,
2364 MTextProperty **props, int num)
2367 MInterval *interval;
2372 M_CHECK_POS (mt, pos, -1);
2374 plist = get_plist_create (mt, key, 0);
2378 interval = find_interval (plist, pos);
2379 /* It is assured that INTERVAL is not NULL. */
2380 nprops = interval->nprops;
2381 if (nprops == 0 || num <= 0)
2383 if (nprops == 1 || num == 1)
2385 props[0] = interval->stack[nprops - 1];
2390 num = nprops, offset = 0;
2392 offset = nprops - num;
2393 for (i = 0; i < num; i++)
2394 props[i] = interval->stack[offset + i];
2399 @brief Attach a text property to an M-text.
2401 The mtext_attach_property () function attaches text property $PROP
2402 to the range between $FROM and $TO in M-text $MT. If $PROP is
2403 already attached to an M-text, it is detached before attached to
2407 If the operation was successful, mtext_attach_property () returns
2408 0. Otherwise it returns -1 and assigns an error code to the
2409 external variable #merror_code. */
2412 mtext_attach_property (MText *mt, int from, int to, MTextProperty *prop)
2415 MInterval *interval;
2417 M_CHECK_RANGE (mt, from, to, -1, 0);
2419 M17N_OBJECT_REF (prop);
2421 mtext_detach_property (prop);
2422 prepare_to_modify (mt, from, to, prop->key);
2423 plist = get_plist_create (mt, prop->key, 1);
2424 xassert (check_plist (plist, 0) == 0);
2425 interval = pop_all_properties (plist, from, to);
2426 xassert (check_plist (plist, 0) == 0);
2430 PUSH_PROP (interval, prop);
2431 M17N_OBJECT_UNREF (prop);
2432 xassert (check_plist (plist, 0) == 0);
2434 maybe_merge_interval (plist, interval);
2436 maybe_merge_interval (plist, interval->prev);
2437 xassert (check_plist (plist, 0) == 0);
2442 @brief Detach a text property from an M-text.
2444 The mtext_detach_property () function makes text property $PROP
2448 This function always returns 0. */
2451 mtext_detach_property (MTextProperty *prop)
2454 int start = prop->start, end = prop->end;
2458 prepare_to_modify (prop->mt, start, end, prop->key);
2459 plist = get_plist_create (prop->mt, prop->key, 0);
2461 detach_property (plist, prop, NULL);
2466 @brief Push a text property onto an M-text.
2468 The mtext_push_property () function attaches text property $PROP on
2469 M-text MT by the "push" manner.
2472 If the operation was successful, mtext_push_property () returns
2473 0. Otherwise it returns -1 and assigns an error code to the
2474 external variable #merror_code. */
2477 mtext_push_property (MText *mt, int from, int to, MTextProperty *prop)
2480 MInterval *head, *tail, *interval;
2481 int check_head, check_tail;
2483 M_CHECK_RANGE (mt, from, to, -1, 0);
2485 M17N_OBJECT_REF (prop);
2487 mtext_detach_property (prop);
2488 prepare_to_modify (mt, from, to, prop->key);
2489 plist = get_plist_create (mt, prop->key, 1);
2494 /* Find an interval that covers the position FROM. */
2495 head = find_interval (plist, from);
2497 /* If the found interval starts before FROM, divide it at FROM. */
2498 if (head->start < from)
2500 divide_interval (plist, head, from);
2507 /* Find an interval that ends at TO. If TO is not at the end of an
2508 interval, make one that ends at TO. */
2509 if (head->end == to)
2514 else if (head->end > to)
2516 divide_interval (plist, head, to);
2522 tail = find_interval (plist, to);
2528 else if (tail->start == to)
2535 divide_interval (plist, tail, to);
2540 /* Push PROP to the current values of intervals between HEAD and TAIL
2541 (both inclusive). */
2542 for (interval = head; ; interval = interval->next)
2544 PUSH_PROP (interval, prop);
2545 if (interval == tail)
2549 /* If there is a possibility that TAIL now has the same value as the
2550 next one, check it and concatenate them if necessary. */
2551 if (tail->next && check_tail)
2552 maybe_merge_interval (plist, tail);
2554 /* If there is a possibility that HEAD now has the same value as the
2555 previous one, check it and concatenate them if necessary. */
2556 if (head->prev && check_head)
2557 maybe_merge_interval (plist, head->prev);
2559 M17N_OBJECT_UNREF (prop);
2560 xassert (check_plist (plist, 0) == 0);
2565 @brief Symbol for specifying serializer functions.
2567 To serialize a text property, the user must supply a serializer
2568 function for that text property. This is done by giving a symbol
2569 property whose key is #Mtext_prop_serializer and value is a
2570 pointer to an appropriate serializer function.
2572 @seealso Mtext_prop_serializer (), MTextPropSerializeFunc
2574 MSymbol Mtext_prop_serializer;
2577 @brief Symbol for specifying deserializer functions.
2579 To deserialize a text property, the user must supply a deserializer
2580 function for that text property. This is done by giving a symbol
2581 property whose key is #Mtext_prop_deserializer and value is a
2582 pointer to an appropriate deserializer function.
2584 @seealso Mtext_prop_serializer (), MTextPropSerializeFunc
2586 MSymbol Mtext_prop_deserializer;
2589 @brief Serialize text properties in an M-text.
2591 The mtext_serialize () function serializes the text between $FROM
2592 and $TO in M-text $MT. The serialized result is an M-text in the
2593 form of XML. $PROPERTY_LIST limits the text properties to be
2594 serialized. If a symbol 1) appears as the value of an element in
2595 $PROPERTY_LIST (the key must be @c Mt ) and 2) has the symbol
2596 property #Mtext_prop_serializer, a text property having that
2597 symbol as its key is turned into the "property" element in the
2598 resulting XML representation.
2600 The DTD of the generated XML is as follows:
2604 <!ELEMENT mtext (property*,body+)>
2605 <!ELEMENT property EMPTY>
2606 <!ELEMENT body (#PCDATA)>
2607 <!ATTLIST property key CDATA #REQUIRED>
2608 <!ATTLIST property value CDATA #REQUIRED>
2609 <!ATTLIST property from CDATA #REQUIRED>
2610 <!ATTLIST property to CDATA #REQUIRED>
2611 <!ATTLIST property control CDATA #REQUIRED>
2615 This function depends on the libxml2 library. If the m17n library
2616 is configured without libxml2, this function always fails.
2619 If the operation was successful, mtext_serialize () returns an
2620 M-text in the form of XML. Otherwise it returns @c NULL and assigns an
2621 error code to the external variable #merror_code.
2624 mtext_deserialize (), Mtext_prop_serializer */
2627 mtext_serialize (MText *mt, int from, int to, MPlist *property_list)
2631 MTextPropSerializeFunc func;
2638 M_CHECK_RANGE (mt, from, to, NULL, NULL);
2639 doc = xmlParseMemory (XML_TEMPLATE, strlen (XML_TEMPLATE) + 1);
2640 node = xmlDocGetRootElement (doc);
2643 MPLIST_DO (pl, property_list)
2645 MSymbol key = MPLIST_VAL (pl);
2647 func = (MTextPropSerializeFunc) msymbol_get (key, Mtext_prop_serializer);
2649 extract_text_properties (mt, from, to, key, plist);
2653 MPLIST_DO (pl, plist)
2655 MTextProperty *prop = MPLIST_VAL (pl);
2657 MPlist *serialized_plist;
2660 func = (MTextPropSerializeFunc) msymbol_get (prop->key,
2661 Mtext_prop_serializer);
2662 serialized_plist = (func) (prop->val);
2663 if (! serialized_plist)
2666 mplist__serialize (work, serialized_plist);
2667 child = xmlNewChild (node, NULL, (xmlChar *) "property", NULL);
2668 xmlSetProp (child, (xmlChar *) "key",
2669 (xmlChar *) MSYMBOL_NAME (prop->key));
2670 xmlSetProp (child, (xmlChar *) "value", (xmlChar *) MTEXT_DATA (work));
2671 sprintf (buf, "%d", prop->start - from);
2672 xmlSetProp (child, (xmlChar *) "from", (xmlChar *) buf);
2673 sprintf (buf, "%d", prop->end - from);
2674 xmlSetProp (child, (xmlChar *) "to", (xmlChar *) buf);
2675 sprintf (buf, "%d", prop->control.flag);
2676 xmlSetProp (child, (xmlChar *) "control", (xmlChar *) buf);
2677 xmlAddChild (node, xmlNewText ((xmlChar *) "\n"));
2679 M17N_OBJECT_UNREF (serialized_plist);
2681 M17N_OBJECT_UNREF (plist);
2683 if (from > 0 || to < mtext_nchars (mt))
2684 mtext_copy (work, 0, mt, from, to);
2687 M17N_OBJECT_UNREF (work);
2690 for (from = 0, to = mtext_nchars (mt); from <= to; from++)
2692 ptr = MTEXT_DATA (mt) + POS_CHAR_TO_BYTE (mt, from);
2693 xmlNewTextChild (node, NULL, (xmlChar *) "body", (xmlChar *) ptr);
2694 from = mtext_character (mt, from, to, 0);
2699 xmlDocDumpMemoryEnc (doc, (xmlChar **) &ptr, &n, "UTF-8");
2702 mtext__cat_data (work, ptr, n, MTEXT_FORMAT_UTF_8);
2704 #else /* not HAVE_XML2 */
2705 MERROR (MERROR_TEXTPROP, NULL);
2706 #endif /* not HAVE_XML2 */
2710 @brief Deserialize text properties in an M-text.
2712 The mtext_deserialize () function deserializes M-text $MT. $MT
2713 must be an XML having the followng DTD.
2717 <!ELEMENT mtext (property*,body+)>
2718 <!ELEMENT property EMPTY>
2719 <!ELEMENT body (#PCDATA)>
2720 <!ATTLIST property key CDATA #REQUIRED>
2721 <!ATTLIST property value CDATA #REQUIRED>
2722 <!ATTLIST property from CDATA #REQUIRED>
2723 <!ATTLIST property to CDATA #REQUIRED>
2724 <!ATTLIST property control CDATA #REQUIRED>
2728 This function depends on the libxml2 library. If the m17n library
2729 is configured without libxml2, this function always fail.
2732 If the operation was successful, mtext_deserialize () returns the
2733 resulting M-text. Otherwise it returns @c NULL and assigns an error
2734 code to the external variable #merror_code.
2737 mtext_serialize (), Mtext_prop_deserializer */
2740 mtext_deserialize (MText *mt)
2745 xmlXPathContextPtr context;
2746 xmlXPathObjectPtr result;
2747 xmlChar *body_str, *key_str, *val_str, *from_str, *to_str, *ctl_str;
2750 if (mt->format > MTEXT_FORMAT_UTF_8)
2751 MERROR (MERROR_TEXTPROP, NULL);
2752 doc = xmlParseMemory ((char *) MTEXT_DATA (mt), mtext_nbytes (mt));
2754 MERROR (MERROR_TEXTPROP, NULL);
2755 node = xmlDocGetRootElement (doc);
2759 MERROR (MERROR_TEXTPROP, NULL);
2761 if (xmlStrcmp (node->name, (xmlChar *) "mtext"))
2764 MERROR (MERROR_TEXTPROP, NULL);
2767 context = xmlXPathNewContext (doc);
2768 result = xmlXPathEvalExpression ((xmlChar *) "//body", context);
2769 if (xmlXPathNodeSetIsEmpty (result->nodesetval))
2772 MERROR (MERROR_TEXTPROP, NULL);
2774 for (i = 0, mt = mtext (); i < result->nodesetval->nodeNr; i++)
2777 mtext_cat_char (mt, 0);
2778 node = (xmlNodePtr) result->nodesetval->nodeTab[i];
2779 body_str = xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
2782 mtext__cat_data (mt, body_str, strlen ((char *) body_str),
2783 MTEXT_FORMAT_UTF_8);
2788 result = xmlXPathEvalExpression ((xmlChar *) "//property", context);
2789 if (! xmlXPathNodeSetIsEmpty (result->nodesetval))
2790 for (i = 0; i < result->nodesetval->nodeNr; i++)
2793 MTextPropDeserializeFunc func;
2794 MTextProperty *prop;
2796 int from, to, control;
2799 key_str = xmlGetProp (result->nodesetval->nodeTab[i],
2801 val_str = xmlGetProp (result->nodesetval->nodeTab[i],
2802 (xmlChar *) "value");
2803 from_str = xmlGetProp (result->nodesetval->nodeTab[i],
2804 (xmlChar *) "from");
2805 to_str = xmlGetProp (result->nodesetval->nodeTab[i],
2807 ctl_str = xmlGetProp (result->nodesetval->nodeTab[i],
2808 (xmlChar *) "control");
2810 key = msymbol ((char *) key_str);
2811 func = ((MTextPropDeserializeFunc)
2812 msymbol_get (key, Mtext_prop_deserializer));
2815 plist = mplist__from_string (val_str, strlen ((char *) val_str));
2818 if (sscanf ((char *) from_str, "%d", &from) != 1
2819 || from < 0 || from >= mtext_nchars (mt))
2821 if (sscanf ((char *) to_str, "%d", &to) != 1
2822 || to <= from || to > mtext_nchars (mt))
2824 if (sscanf ((char *) ctl_str, "%d", &control) != 1
2825 || control < 0 || control > MTEXTPROP_CONTROL_MAX)
2827 val = (func) (plist);
2828 M17N_OBJECT_UNREF (plist);
2829 prop = mtext_property (key, val, control);
2830 if (key->managing_key)
2831 M17N_OBJECT_UNREF (val);
2832 mtext_push_property (mt, from, to, prop);
2833 M17N_OBJECT_UNREF (prop);
2841 xmlXPathFreeContext (context);
2844 #else /* not HAVE_XML2 */
2845 MERROR (MERROR_TEXTPROP, NULL);
2846 #endif /* not HAVE_XML2 */