/* textprop.c -- text property module. Copyright (C) 2003, 2004 National Institute of Advanced Industrial Science and Technology (AIST) Registration Number H15PRO112 This file is part of the m17n library. The m17n library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The m17n library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the m17n library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 02111-1307, USA. */ /***en @addtogroup m17nTextProperty @brief Function to handle text properties. Each character in an M-text can have properties called @e text @e properties. Text properties store various kinds of information attached to parts of an M-text to provide application programs with a unified view of those information. As rich information can be stored in M-texts in the form of text properties, functions in application programs can be simple. A text property consists of a @e key and @e values, where key is a symbol and values are anything that can be cast to <tt>(void *) </tt>. Unlike other types of properties, a text property can have multiple values. "The text property whose key is K" may be shortened to "K property". */ /***ja @addtogroup m17nTextProperty @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÁàºî¤¹¤ë¤¿¤á¤Î´Ø¿ô. M-text Æâ¤Î³ÆÊ¸»ú¤Ï¡¢@e ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£ ¤È¸Æ¤Ð¤ì¤ë¥×¥í¥Ñ¥Æ¥£¤ò »ý¤Ä¤³¤È¤¬¤Ç¤¤ë¡£¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¡¢M-text ¤Î³ÆÉô°Ì¤ËÉղäµ¤ì ¤¿¤µ¤Þ¤¶¤Þ¤Ê¾ðÊó¤òÊÝ»ý¤·¤Æ¤ª¤ê¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¤½¤ì¤é ¤Î¾ðÊó¤òÅý°ìŪ¤Ë°·¤¦¤³¤È¤¬¤Ç¤¤ë¡£M-text ¼«ÂΤ¬ËÉ٤ʾðÊó¤ò»ý¤Ä¤¿ ¤á¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥àÃæ¤Î´Ø¿ô¤ò´ÊÁDz½¤¹¤ë¤³¤È¤¬¤Ç¤¤ë¡£ ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï @e ¥¡¼ ¤È @e ÃÍ ¤«¤é¤Ê¤ë¡£¥¡¼¤Ï¥·¥ó¥Ü¥ë¤Ç¤¢ ¤ê¡¢ÃÍ¤Ï <tt>(void *)</tt> ·¿¤Ë¥¥ã¥¹¥È¤Ç¤¤ë¤â¤Î¤Ê¤é²¿¤Ç¤â¤è¤¤¡£ ¾¤Î¥¿¥¤¥×¤Î¥×¥í¥Ñ¥Æ¥£¤È°Û¤Ê¤ê¡¢°ì¤Ä¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬Ê£¿ô¤ÎÃÍ ¤ò»ý¤Ä¤³¤È¤¬µö¤µ¤ì¤ë¡£¡Ö¥¡¼¤¬ K ¤Ç¤¢¤ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¡×¤Î¤³¤È ¤ò´Êñ¤Ë¡ÖK ¥×¥í¥Ñ¥Æ¥£¡×¤È¸Æ¤Ö¤³¤È¤¬¤¢¤ë¡£ */ /*=*/ #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE) /*** @addtogroup m17nInternal @{ */ #include <config.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef HAVE_XML2 #include <libxml/tree.h> #include <libxml/parser.h> #include <libxml/xmlmemory.h> #include <libxml/xpath.h> #endif #include "m17n.h" #include "m17n-misc.h" #include "internal.h" #include "symbol.h" #include "mtext.h" #include "textprop.h" #define TEXT_PROP_DEBUG #undef xassert #ifdef TEXT_PROP_DEBUG #define xassert(X) do {if (!(X)) mdebug_hook ();} while (0) #else #define xassert(X) (void) 0 #endif /* not FONTSET_DEBUG */ /* Hierarchy of objects (MText, MTextPlist, MInterval, MTextProperty) MText | key/a key/b key/x +--> MTextPlist -> MTextPlist -> ... -> MTextPlist | | | +- tail <-----------------------------------------+ | | | | +- head <--> MInterval <--> ... <--> MInterval <--+ | +- tail --------------------------------------------------------+ | | +- head --> MInterval <--> MInterval <--> ... <--> MInterval <--+ | | +---------------+------------> MTextProperty +--> MTextProperty ... Examples: MTextProperty a/A [AAAAAAAAAAAAAAAAAAAAA] MTextProperty a/B [BBBBBBBBBBBBBBBBB] MTextPlist a |--intvl1--|-intvl2-|-intvl3-|---intvl4---|-intvl5-| MTextProperty b/A [AAAAAAAAAA] MTextProperty b/B [BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB] MTextPlist b |-intvl1-|--intvl2--|--intvl3--|-intvl4-|--intvl5--| M-text |--------------------------------------------------| (intvl == MInterval) */ /* The structure MTextProperty is defined in textprop.h. */ /** MInterval is the structure for an interval that holds text properties of the same key in a specific range of M-text. All intervals are stored in MIntervalPool. */ typedef struct MInterval MInterval; struct MInterval { /** Stack of pointers to text properties. If the interval does not have any text properties, this member is NULL or contains random values. */ MTextProperty **stack; /** How many values are in <stack>. */ int nprops; /** Length of <stack>. */ int stack_length; /** Start and end character positions of the interval. If <end> is negative, this interval is not in use. */ int start, end; /** Pointers to the previous and next intervals. If <start> is 0, <prev> is NULL and this interval is pointed by MTextPlist->head. If <end> is the size of the M-text, <next> is NULL, and this interval is pointed by MTextPlist->tail. */ MInterval *prev, *next; }; /** MTextPlist is a structure to hold text properties of an M-text by chain. Each element in the chain is for a specific key. */ typedef struct MTextPlist MTextPlist; struct MTextPlist { /** Key of the property. */ MSymbol key; /** The head and tail intervals. <head>->start is always 0. <tail->end is always MText->nchars. */ MInterval *head, *tail; /** Lastly accessed interval. */ MInterval *cache; /* Not yet implemented. */ int (*modification_hook) (MText *mt, MSymbol key, int from, int to); /** Pointer to the next property in the chain, or NULL if the property is the last one in the chain. */ MTextPlist *next; }; /** How many intervals one interval-pool can contain. */ #define INTERVAL_POOL_SIZE 1024 typedef struct MIntervalPool MIntervalPool; /** MIntervalPool is the structure for an interval-pool which store intervals. Each interval-pool contains INTERVAL_POOL_SIZE number of intervals, and is chained from the root #interval_pool. */ struct MIntervalPool { /** Array of intervals. */ MInterval intervals[INTERVAL_POOL_SIZE]; /** The smallest index to an unused interval. */ int free_slot; /** Pointer to the next interval-pool. */ MIntervalPool *next; }; /** Root of interval-pools. */ static MIntervalPool interval_pool_root; /* For debugging. */ static M17NObjectArray text_property_table; /** Return a newly allocated interval pool. */ static MIntervalPool * new_interval_pool () { MIntervalPool *pool; int i; MSTRUCT_CALLOC (pool, MERROR_TEXTPROP); for (i = 0; i < INTERVAL_POOL_SIZE; i++) pool->intervals[i].end = -1; pool->free_slot = 0; pool->next = NULL; return pool; } /** Return a new interval for the region START and END. */ static MInterval * new_interval (int start, int end) { MIntervalPool *pool; MInterval *interval; for (pool = &interval_pool_root; pool->free_slot >= INTERVAL_POOL_SIZE; pool = pool->next) { if (! pool->next) pool->next = new_interval_pool (); } interval = &(pool->intervals[pool->free_slot]); interval->stack = NULL; interval->nprops = 0; interval->stack_length = 0; interval->prev = interval->next = NULL; interval->start = start; interval->end = end; pool->free_slot++; while (pool->free_slot < INTERVAL_POOL_SIZE && pool->intervals[pool->free_slot].end >= 0) pool->free_slot++; return interval; } /** Free INTERVAL and return INTERVAL->next. It assumes that INTERVAL has no properties. */ static MInterval * free_interval (MInterval *interval) { MIntervalPool *pool = &interval_pool_root; int i; xassert (interval->nprops == 0); if (interval->stack) free (interval->stack); while ((interval < pool->intervals || interval >= pool->intervals + INTERVAL_POOL_SIZE) && pool->next) pool = pool->next; i = interval - pool->intervals; interval->end = -1; if (i < pool->free_slot) pool->free_slot = i; return interval->next; } /** If necessary, allocate a stack for INTERVAL so that it can contain NUM number of text properties. */ #define PREPARE_INTERVAL_STACK(interval, num) \ do { \ if ((num) > (interval)->stack_length) \ { \ MTABLE_REALLOC ((interval)->stack, (num), MERROR_TEXTPROP); \ (interval)->stack_length = (num); \ } \ } while (0) /** Return a copy of INTERVAL. The copy still shares text properties with INTERVAL. If MASK_BITS is not zero, don't copy such text properties whose control flags contains bits in MASK_BITS. */ static MInterval * copy_interval (MInterval *interval, int mask_bits) { MInterval *new = new_interval (interval->start, interval->end); int nprops = interval->nprops; MTextProperty **props = alloca (sizeof (MTextProperty *) * nprops); int i, n; for (i = n = 0; i < nprops; i++) if (! (interval->stack[i]->control.flag & mask_bits)) props[n++] = interval->stack[i]; new->nprops = n; if (n > 0) { PREPARE_INTERVAL_STACK (new, n); memcpy (new->stack, props, sizeof (MTextProperty *) * n); } return new; } /** Free text property OBJECT. */ static void free_text_property (void *object) { MTextProperty *prop = (MTextProperty *) object; if (prop->key->managing_key) M17N_OBJECT_UNREF (prop->val); M17N_OBJECT_UNREGISTER (text_property_table, prop); free (object); } /** Return a newly allocated text property whose key is KEY and value is VAL. */ static MTextProperty * new_text_property (MText *mt, int from, int to, MSymbol key, void *val, int control_bits) { MTextProperty *prop; M17N_OBJECT (prop, free_text_property, MERROR_TEXTPROP); prop->control.flag = control_bits; prop->attach_count = 0; prop->mt = mt; prop->start = from; prop->end = to; prop->key = key; prop->val = val; if (key->managing_key) M17N_OBJECT_REF (val); M17N_OBJECT_REGISTER (text_property_table, prop); return prop; } /** Return a newly allocated copy of text property PROP. */ #define COPY_TEXT_PROPERTY(prop) \ new_text_property ((prop)->mt, (prop)->start, (prop)->end, \ (prop)->key, (prop)->val, (prop)->control.flag) /** Split text property PROP at position INTERVAL->start, and make all the following intervals contain the copy of PROP instead of PROP. It assumes that PROP starts before INTERVAL. */ static void split_property (MTextProperty *prop, MInterval *interval) { int end = prop->end; MTextProperty *copy; int i; prop->end = interval->start; copy = COPY_TEXT_PROPERTY (prop); copy->start = interval->start; copy->end = end; /* Check all stacks of the following intervals, and if it contains PROP, change it to the copy of it. */ for (; interval && interval->start < end; interval = interval->next) for (i = 0; i < interval->nprops; i++) if (interval->stack[i] == prop) { interval->stack[i] = copy; M17N_OBJECT_REF (copy); copy->attach_count++; prop->attach_count--; M17N_OBJECT_UNREF (prop); } M17N_OBJECT_UNREF (copy); } /** Divide INTERVAL of PLIST at POS if POS is in between the range of INTERVAL. */ static void divide_interval (MTextPlist *plist, MInterval *interval, int pos) { MInterval *new; int i; if (pos == interval->start || pos == interval->end) return; new = copy_interval (interval, 0); interval->end = new->start = pos; new->prev = interval; new->next = interval->next; interval->next = new; if (new->next) new->next->prev = new; if (plist->tail == interval) plist->tail = new; for (i = 0; i < new->nprops; i++) { new->stack[i]->attach_count++; M17N_OBJECT_REF (new->stack[i]); } } /** Check if INTERVAL of PLIST can be merged with INTERVAL->next. If mergeable, extend INTERVAL to the end of INTEVAL->next, free INTERVAL->next, and return INTERVAL. Otherwise, return INTERVAL->next. */ static MInterval * maybe_merge_interval (MTextPlist *plist, MInterval *interval) { int nprops = interval->nprops; MInterval *next = interval->next; int i, j; if (! next || nprops != next->nprops) return next; for (i = 0; i < nprops; i++) { MTextProperty *prop = interval->stack[i]; MTextProperty *old = next->stack[i]; if (prop != old && (prop->val != old->val || prop->end != old->start || prop->control.flag & MTEXTPROP_NO_MERGE || old->control.flag & MTEXTPROP_NO_MERGE)) return interval->next; } for (i = 0; i < nprops; i++) { MTextProperty *prop = interval->stack[i]; MTextProperty *old = next->stack[i]; if (prop != old) { MInterval *tail; for (tail = next->next; tail && tail->start < old->end; tail = tail->next) for (j = 0; j < tail->nprops; j++) if (tail->stack[j] == old) { old->attach_count--; xassert (old->attach_count); tail->stack[j] = prop; prop->attach_count++; M17N_OBJECT_REF (prop); } xassert (old->attach_count == 1); old->mt = NULL; prop->end = old->end; } old->attach_count--; M17N_OBJECT_UNREF (old); } interval->end = next->end; interval->next = next->next; if (next->next) next->next->prev = interval; if (plist->tail == next) plist->tail = interval; plist->cache = interval; next->nprops = 0; free_interval (next); return interval; } /** Adjust start and end positions of intervals between HEAD and TAIL (both inclusive) by diff. Adjust also start and end positions of text properties belonging to those intervals. */ static void adjust_intervals (MInterval *head, MInterval *tail, int diff) { int i; MTextProperty *prop; if (diff < 0) { /* Adjust end positions of properties starting before HEAD. */ for (i = 0; i < head->nprops; i++) { prop = head->stack[i]; if (prop->start < head->start) prop->end += diff; } /* Adjust start and end positions of properties starting at HEAD, and adjust HEAD itself. */ while (1) { for (i = 0; i < head->nprops; i++) { prop = head->stack[i]; if (prop->start == head->start) prop->start += diff, prop->end += diff; } head->start += diff; head->end += diff; if (head == tail) break; head = head->next; } } else { /* Adjust start poistions of properties ending after TAIL. */ for (i = 0; i < tail->nprops; i++) { prop = tail->stack[i]; if (prop->end > tail->end) prop->start += diff; } /* Adjust start and end positions of properties ending at TAIL, and adjust TAIL itself. */ while (1) { for (i = 0; i < tail->nprops; i++) { prop = tail->stack[i]; if (prop->end == tail->end) prop->start += diff, prop->end += diff; } tail->start += diff; tail->end += diff; if (tail == head) break; tail = tail->prev; } } } /* Return an interval of PLIST that covers the position POS. */ static MInterval * find_interval (MTextPlist *plist, int pos) { MInterval *interval; MInterval *highest; if (pos < plist->head->end) return plist->head; if (pos >= plist->tail->start) return (pos < plist->tail->end ? plist->tail : NULL); interval = plist->cache; if (pos < interval->start) highest = interval->prev, interval = plist->head->next; else if (pos < interval->end) return interval; else highest = plist->tail->prev, interval = interval->next; if (pos - interval->start < highest->end - pos) { while (interval->end <= pos) /* Here, we are sure that POS is not included in PLIST->tail, thus, INTERVAL->next always points a valid next interval. */ interval = interval->next; } else { while (highest->start > pos) highest = highest->prev; interval = highest; } plist->cache = interval; return interval; } /* Push text property PROP on the stack of INTERVAL. */ #define PUSH_PROP(interval, prop) \ do { \ int n = (interval)->nprops; \ \ PREPARE_INTERVAL_STACK ((interval), n + 1); \ (interval)->stack[n] = (prop); \ (interval)->nprops += 1; \ (prop)->attach_count++; \ M17N_OBJECT_REF (prop); \ if ((prop)->start > (interval)->start) \ (prop)->start = (interval)->start; \ if ((prop)->end < (interval)->end) \ (prop)->end = (interval)->end; \ } while (0) /* Pop the topmost text property of INTERVAL from the stack. If it ends after INTERVAL->end, split it. */ #define POP_PROP(interval) \ do { \ MTextProperty *prop; \ \ (interval)->nprops--; \ prop = (interval)->stack[(interval)->nprops]; \ xassert (prop->control.ref_count > 0); \ xassert (prop->attach_count > 0); \ if (prop->start < (interval)->start) \ { \ if (prop->end > (interval)->end) \ split_property (prop, (interval)->next); \ prop->end = (interval)->start; \ } \ else if (prop->end > (interval)->end) \ prop->start = (interval)->end; \ prop->attach_count--; \ if (! prop->attach_count) \ prop->mt = NULL; \ M17N_OBJECT_UNREF (prop); \ } while (0) #define REMOVE_PROP(interval, prop) \ do { \ int i; \ \ for (i = (interval)->nprops - 1; i >= 0; i--) \ if ((interval)->stack[i] == (prop)) \ break; \ if (i < 0) \ break; \ (interval)->nprops--; \ for (; i < (interval)->nprops; i++) \ (interval)->stack[i] = (interval)->stack[i + 1]; \ (prop)->attach_count--; \ if (! (prop)->attach_count) \ (prop)->mt = NULL; \ M17N_OBJECT_UNREF (prop); \ } while (0) #ifdef TEXT_PROP_DEBUG static int check_plist (MTextPlist *plist, int start) { MInterval *interval = plist->head; MInterval *cache = plist->cache; int cache_found = 0; if (interval->start != start || interval->start >= interval->end) return mdebug_hook (); while (interval) { int i; if (interval == interval->next) return mdebug_hook (); if (interval == cache) cache_found = 1; if (interval->start >= interval->end) return mdebug_hook (); if ((interval->next ? (interval->end != interval->next->start || interval != interval->next->prev) : interval != plist->tail)) return mdebug_hook (); for (i = 0; i < interval->nprops; i++) { if (interval->stack[i]->start > interval->start || interval->stack[i]->end < interval->end) return mdebug_hook (); if (! interval->stack[i]->attach_count) return mdebug_hook (); if (! interval->stack[i]->mt) return mdebug_hook (); if (interval->stack[i]->start == interval->start) { MTextProperty *prop = interval->stack[i]; int count = prop->attach_count - 1; MInterval *interval2; for (interval2 = interval->next; interval2 && interval2->start < prop->end; count--, interval2 = interval2->next) if (count == 0) return mdebug_hook (); } if (interval->stack[i]->end > interval->end) { MTextProperty *prop = interval->stack[i]; MInterval *interval2; int j; for (interval2 = interval->next; interval2 && interval2->start < prop->end; interval2 = interval2->next) { for (j = 0; j < interval2->nprops; j++) if (interval2->stack[j] == prop) break; if (j == interval2->nprops) return mdebug_hook (); } } if (interval->stack[i]->start < interval->start) { MTextProperty *prop = interval->stack[i]; MInterval *interval2; int j; for (interval2 = interval->prev; interval2 && interval2->end > prop->start; interval2 = interval2->prev) { for (j = 0; j < interval2->nprops; j++) if (interval2->stack[j] == prop) break; if (j == interval2->nprops) return mdebug_hook (); } } } interval = interval->next; } if (! cache_found) return mdebug_hook (); if (plist->head->prev || plist->tail->next) return mdebug_hook (); return 0; } #endif /** Return a copy of plist that contains intervals between FROM and TO of PLIST. The copy goes to the position POS of M-text MT. */ static MTextPlist * copy_single_property (MTextPlist *plist, int from, int to, MText *mt, int pos) { MTextPlist *new; MInterval *interval1, *interval2; MTextProperty *prop; int diff = pos - from; int i, j; int mask_bits = MTEXTPROP_VOLATILE_STRONG | MTEXTPROP_VOLATILE_WEAK; MSTRUCT_CALLOC (new, MERROR_TEXTPROP); new->key = plist->key; new->next = NULL; interval1 = find_interval (plist, from); new->head = copy_interval (interval1, mask_bits); for (interval1 = interval1->next, interval2 = new->head; interval1 && interval1->start < to; interval1 = interval1->next, interval2 = interval2->next) { interval2->next = copy_interval (interval1, mask_bits); interval2->next->prev = interval2; } new->tail = interval2; new->head->start = from; new->tail->end = to; for (interval1 = new->head; interval1; interval1 = interval1->next) for (i = 0; i < interval1->nprops; i++) if (interval1->start == interval1->stack[i]->start || interval1 == new->head) { prop = interval1->stack[i]; interval1->stack[i] = COPY_TEXT_PROPERTY (prop); interval1->stack[i]->mt = mt; interval1->stack[i]->attach_count++; if (interval1->stack[i]->start < from) interval1->stack[i]->start = from; if (interval1->stack[i]->end > to) interval1->stack[i]->end = to; for (interval2 = interval1->next; interval2; interval2 = interval2->next) for (j = 0; j < interval2->nprops; j++) if (interval2->stack[j] == prop) { interval2->stack[j] = interval1->stack[i]; interval1->stack[i]->attach_count++; M17N_OBJECT_REF (interval1->stack[i]); } } adjust_intervals (new->head, new->tail, diff); new->cache = new->head; for (interval1 = new->head; interval1 && interval1->next; interval1 = maybe_merge_interval (new, interval1)); xassert (check_plist (new, pos) == 0); if (new->head == new->tail && new->head->nprops == 0) { free_interval (new->head); free (new); new = NULL; } return new; } /** Return a newly allocated plist whose key is KEY on M-text MT. */ static MTextPlist * new_plist (MText *mt, MSymbol key) { MTextPlist *plist; MSTRUCT_MALLOC (plist, MERROR_TEXTPROP); plist->key = key; plist->head = new_interval (0, mtext_nchars (mt)); plist->tail = plist->head; plist->cache = plist->head; plist->next = mt->plist; mt->plist = plist; return plist; } /* Free PLIST and return PLIST->next. */ static MTextPlist * free_textplist (MTextPlist *plist) { MTextPlist *next = plist->next; MInterval *interval = plist->head; while (interval) { while (interval->nprops > 0) POP_PROP (interval); interval = free_interval (interval); } free (plist); return next; } /** Return a plist that contains the property KEY of M-text MT. If such a plist does not exist and CREATE is nonzero, create a new plist and return it. */ static MTextPlist * get_plist_create (MText *mt, MSymbol key, int create) { MTextPlist *plist; plist = mt->plist; while (plist && plist->key != key) plist = plist->next; /* If MT does not have PROP, make one. */ if (! plist && create) plist = new_plist (mt, key); return plist; } /* Detach PROP. INTERVAL (if not NULL) contains PROP. */ static void detach_property (MTextPlist *plist, MTextProperty *prop, MInterval *interval) { MInterval *head; int to = prop->end; xassert (prop->mt); xassert (plist); M17N_OBJECT_REF (prop); if (interval) while (interval->start > prop->start) interval = interval->prev; else interval = find_interval (plist, prop->start); head = interval; while (1) { REMOVE_PROP (interval, prop); if (interval->end == to) break; interval = interval->next; } xassert (prop->attach_count == 0 && prop->mt == NULL); M17N_OBJECT_UNREF (prop); while (head && head->end <= to) head = maybe_merge_interval (plist, head); xassert (check_plist (plist, 0) == 0); } /* Delete text properties of PLIST between FROM and TO. MASK_BITS specifies what kind of properties to delete. If DELETING is nonzero, delete such properties too that are completely included in the region. If the resulting PLIST still has any text properties, return 1, else return 0. */ static int delete_properties (MTextPlist *plist, int from, int to, int mask_bits, int deleting) { MInterval *interval; int modified = 0; int modified_from = from; int modified_to = to; int i; retry: for (interval = find_interval (plist, from); interval && interval->start < to; interval = interval->next) for (i = 0; i < interval->nprops; i++) { MTextProperty *prop = interval->stack[i]; if (prop->control.flag & mask_bits) { if (prop->start < modified_from) modified_from = prop->start; if (prop->end > modified_to) modified_to = prop->end; detach_property (plist, prop, interval); modified++; goto retry; } else if (deleting && prop->start >= from && prop->end <= to) { detach_property (plist, prop, interval); modified++; goto retry; } } if (modified) { interval = find_interval (plist, modified_from); while (interval && interval->start < modified_to) interval = maybe_merge_interval (plist, interval); } return (plist->head != plist->tail || plist->head->nprops > 0); } static void pop_interval_properties (MInterval *interval) { while (interval->nprops > 0) POP_PROP (interval); } MInterval * pop_all_properties (MTextPlist *plist, int from, int to) { MInterval *interval; /* Be sure to have interval boundary at TO. */ interval = find_interval (plist, to); if (interval && interval->start < to) divide_interval (plist, interval, to); /* Be sure to have interval boundary at FROM. */ interval = find_interval (plist, from); if (interval->start < from) { divide_interval (plist, interval, from); interval = interval->next; } pop_interval_properties (interval); while (interval->end < to) { MInterval *next = interval->next; pop_interval_properties (next); interval->end = next->end; interval->next = next->next; if (interval->next) interval->next->prev = interval; if (next == plist->tail) plist->tail = interval; if (plist->cache == next) plist->cache = interval; free_interval (next); } return interval; } /* Delete volatile text properties between FROM and TO. If DELETING is nonzero, we are going to delete text, thus both strongly and weakly volatile properties must be deleted. Otherwise we are going to modify a text property KEY, thus only strongly volatile properties whose key is not KEY must be deleted. */ static void prepare_to_modify (MText *mt, int from, int to, MSymbol key, int deleting) { MTextPlist *plist = mt->plist, *prev = NULL; int mask_bits = MTEXTPROP_VOLATILE_STRONG; if (deleting) mask_bits |= MTEXTPROP_VOLATILE_WEAK; while (plist) { if (plist->key != key && ! delete_properties (plist, from, to, mask_bits, deleting)) { if (prev) plist = prev->next = free_textplist (plist); else plist = mt->plist = free_textplist (plist); } else prev = plist, plist = plist->next; } } void extract_text_properties (MText *mt, int from, int to, MSymbol key, MPlist *plist) { MPlist *top; MTextPlist *list = get_plist_create (mt, key, 0); MInterval *interval; if (! list) return; interval = find_interval (list, from); if (interval->nprops == 0 && interval->start <= from && interval->end >= to) return; top = plist; while (interval && interval->start < to) { if (interval->nprops == 0) top = mplist_find_by_key (top, Mnil); else { MPlist *current = top, *place; int i; for (i = 0; i < interval->nprops; i++) { MTextProperty *prop = interval->stack[i]; place = mplist_find_by_value (current, prop); if (place) current = MPLIST_NEXT (place); else { place = mplist_find_by_value (top, prop); if (place) { mplist_pop (place); if (MPLIST_NEXT (place) == MPLIST_NEXT (current)) current = place; } mplist_push (current, Mt, prop); current = MPLIST_NEXT (current); } } } interval = interval->next; } return; } #define XML_TEMPLATE "<?xml version=\"1.0\" ?>\n\ <!DOCTYPE mtext [\n\ <!ELEMENT mtext (property*,body+)>\n\ <!ELEMENT property EMPTY>\n\ <!ELEMENT body (#PCDATA)>\n\ <!ATTLIST property key CDATA #REQUIRED>\n\ <!ATTLIST property value CDATA #REQUIRED>\n\ <!ATTLIST property from CDATA #REQUIRED>\n\ <!ATTLIST property to CDATA #REQUIRED>\n\ <!ATTLIST property control CDATA #REQUIRED>\n\ ]>\n\ <mtext>\n\ </mtext>" /* for debugging... */ #include <stdio.h> void dump_interval (MInterval *interval, int indent) { char *prefix = (char *) alloca (indent + 1); int i; memset (prefix, 32, indent); prefix[indent] = 0; fprintf (stderr, "(interval %d-%d (%d)", interval->start, interval->end, interval->nprops); for (i = 0; i < interval->nprops; i++) fprintf (stderr, "\n%s (%d %d/%d %d-%d 0x%x)", prefix, i, interval->stack[i]->control.ref_count, interval->stack[i]->attach_count, interval->stack[i]->start, interval->stack[i]->end, (unsigned) interval->stack[i]->val); fprintf (stderr, ")"); } void dump_textplist (MTextPlist *plist, int indent) { char *prefix = (char *) alloca (indent + 1); memset (prefix, 32, indent); prefix[indent] = 0; fprintf (stderr, "(properties"); if (! plist) fprintf (stderr, ")\n"); else { fprintf (stderr, "\n"); while (plist) { MInterval *interval = plist->head; fprintf (stderr, "%s (%s", prefix, msymbol_name (plist->key)); while (interval) { fprintf (stderr, " (%d %d", interval->start, interval->end); if (interval->nprops > 0) { int i; for (i = 0; i < interval->nprops; i++) fprintf (stderr, " 0x%x", (int) interval->stack[i]->val); } fprintf (stderr, ")"); interval = interval->next; } fprintf (stderr, ")\n"); xassert (check_plist (plist, 0) == 0); plist = plist->next; } } } /* Internal API */ int mtext__prop_init () { M17N_OBJECT_ADD_ARRAY (text_property_table, "Text property"); Mtext_prop_serializer = msymbol ("text-prop-serializer"); Mtext_prop_deserializer = msymbol ("text-prop-deserializer"); return 0; } void mtext__prop_fini () { MIntervalPool *pool = interval_pool_root.next; while (pool) { MIntervalPool *next = pool->next; free (pool); pool = next; } interval_pool_root.next = NULL; } /** Free all plists. */ void mtext__free_plist (MText *mt){ MTextPlist *plist = mt->plist; while (plist) plist = free_textplist (plist); mt->plist = NULL; } /** Extract intervals between FROM and TO of all properties (except for volatile ones) in PLIST, and make a new plist from them for M-text MT. */ MTextPlist * mtext__copy_plist (MTextPlist *plist, int from, int to, MText *mt, int pos) { MTextPlist *copy, *this; if (from == to) return NULL; for (copy = NULL; plist && ! copy; plist = plist->next) copy = copy_single_property (plist, from, to, mt, pos); if (! plist) return copy; for (; plist; plist = plist->next) if ((this = copy_single_property (plist, from, to, mt, pos))) { this->next = copy; copy = this; } return copy; } void mtext__adjust_plist_for_delete (MText *mt, int pos, int len) { MTextPlist *plist; int to; if (len == 0 || pos == mt->nchars) return; if (len == mt->nchars) { mtext__free_plist (mt); return; } to = pos + len; prepare_to_modify (mt, pos, to, Mnil, 1); for (plist = mt->plist; plist; plist = plist->next) { MInterval *interval = pop_all_properties (plist, pos, to); MInterval *prev = interval->prev, *next = interval->next; if (prev) prev->next = next; else plist->head = next; if (next) { adjust_intervals (next, plist->tail, -len); next->prev = prev; } else plist->tail = prev; if (prev && next) next = maybe_merge_interval (plist, prev); plist->cache = next ? next : prev; free_interval (interval); xassert (check_plist (plist, 0) == 0); } } void mtext__adjust_plist_for_insert (MText *mt, int pos, int nchars, MTextPlist *plist) { MTextPlist *pl, *pl_last, *pl2, *p; int i; MInterval *interval; if (mt->nchars == 0) { mtext__free_plist (mt); mt->plist = plist; return; } if (pos > 0 && pos < mtext_nchars (mt)) prepare_to_modify (mt, pos, pos, Mnil, 0); for (pl_last = NULL, pl = mt->plist; pl; pl_last = pl, pl = pl->next) { MInterval *interval, *prev, *next, *head, *tail; if (pos == 0) prev = NULL, next = pl->head; else if (pos == mtext_nchars (mt)) prev = pl->tail, next = NULL; else { next = find_interval (pl, pos); if (next->start < pos) { divide_interval (pl, next, pos); next = next->next; } for (i = 0; i < next->nprops; i++) if (next->stack[i]->start < pos) split_property (next->stack[i], next); prev = next->prev; } xassert (check_plist (pl, 0) == 0); for (p = NULL, pl2 = plist; pl2 && pl->key != pl2->key; p = pl2, pl2 = p->next); if (pl2) { xassert (check_plist (pl2, pl2->head->start) == 0); if (p) p->next = pl2->next; else plist = plist->next; head = pl2->head; tail = pl2->tail; free (pl2); } else { head = tail = new_interval (pos, pos + nchars); } head->prev = prev; tail->next = next; if (prev) prev->next = head; else pl->head = head; if (next) next->prev = tail; else pl->tail = tail; if (next) adjust_intervals (next, pl->tail, nchars); xassert (check_plist (pl, 0) == 0); if (prev && prev->nprops > 0) { for (interval = prev; interval->next != next && interval->next->nprops == 0; interval = interval->next) for (i = 0; i < interval->nprops; i++) { MTextProperty *prop = interval->stack[i]; if (prop->control.flag & MTEXTPROP_REAR_STICKY) PUSH_PROP (interval->next, prop); } } xassert (check_plist (pl, 0) == 0); if (next && next->nprops > 0) { for (interval = next; interval->prev != prev && interval->prev->nprops == 0; interval = interval->prev) for (i = 0; i < interval->nprops; i++) { MTextProperty *prop = interval->stack[i]; if (prop->control.flag & MTEXTPROP_FRONT_STICKY) PUSH_PROP (interval->prev, prop); } } interval = prev ? prev : pl->head; pl->cache = interval; while (interval && interval->start <= pos + nchars) interval = maybe_merge_interval (pl, interval); xassert (check_plist (pl, 0) == 0); } if (pl_last) pl_last->next = plist; else mt->plist = plist; for (; plist; plist = plist->next) { plist->cache = plist->head; if (pos > 0) { if (plist->head->nprops) { interval = new_interval (0, pos); interval->next = plist->head; plist->head->prev = interval; plist->head = interval; } else plist->head->start = 0; } if (pos < mtext_nchars (mt)) { if (plist->tail->nprops) { interval = new_interval (pos + nchars, mtext_nchars (mt) + nchars); interval->prev = plist->tail; plist->tail->next = interval; plist->tail = interval; } else plist->tail->end = mtext_nchars (mt) + nchars; } xassert (check_plist (plist, 0) == 0); } } /* len1 > 0 && len2 > 0 */ void mtext__adjust_plist_for_change (MText *mt, int pos, int len1, int len2) { int pos2 = pos + len1; prepare_to_modify (mt, pos, pos2, Mnil, 0); if (len1 < len2) { int diff = len2 - len1; MTextPlist *plist; for (plist = mt->plist; plist; plist = plist->next) { MInterval *head = find_interval (plist, pos2); MInterval *tail = plist->tail; MTextProperty *prop; int i; if (head) { if (head->start == pos2) head = head->prev; while (tail != head) { for (i = 0; i < tail->nprops; i++) { prop = tail->stack[i]; if (prop->start == tail->start) prop->start += diff, prop->end += diff; } tail->start += diff; tail->end += diff; tail = tail->prev; } } for (i = 0; i < tail->nprops; i++) tail->stack[i]->end += diff; tail->end += diff; } } else if (len1 > len2) { mtext__adjust_plist_for_delete (mt, pos + len2, len1 - len2); } } /*** @} */ #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ /** External API */ /*** @addtogroup m17nTextProperty */ /*** @{ */ /*=*/ /***en @brief Get the value of the topmost text property. The mtext_get_prop () function searches the character at $POS in M-text $MT for the text property whose key is $KEY. @return If a text property is found, mtext_get_prop () returns the value of the property. If the property has multiple values, it returns the topmost one. If no such property is found, it returns @c NULL without changing the external variable #merror_code. If an error is detected, mtext_get_prop () returns @c NULL and assigns an error code to the external variable #merror_code. @note If @c NULL is returned without an error, there are two possibilities: @li the character at $POS does not have a property whose key is $KEY, or @li the character does have such a property and its value is @c NULL. If you need to distinguish these two cases, use the mtext_get_prop_values () function instead. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î°ìÈÖ¾å¤ÎÃͤòÆÀ¤ë. ´Ø¿ô mtext_get_prop () ¤Ï¡¢M-text $MT Æâ¤Î°ÌÃÖ $POS ¤Ë¤¢¤ëʸ»ú¤Î¥Æ ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£ @return ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬¤ß¤Ä¤«¤ì¤Ð¡¢mtext_get_prop () ¤Ï¤½¤Î¥×¥í¥Ñ¥Æ¥£ ¤ÎÃͤòÊÖ¤¹¡£Ãͤ¬Ê£¿ô¸ºß¤¹¤ë¤È¤¤Ï¡¢°ìÈÖ¾å¤ÎÃͤòÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤± ¤ì¤Ð³°ÉôÊÑ¿ô #merror_code ¤òÊѹ¹¤¹¤ë¤³¤È¤Ê¤¯ @c NULL ¤òÊÖ¤¹¡£ ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç mtext_get_prop () ¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ @note ¥¨¥é¡¼¤Ê¤·¤Ç @c NULL ¤¬ÊÖ¤µ¤ì¤¿¾ì¹ç¤Ë¤ÏÆó¤Ä¤Î²ÄǽÀ¤¬¤¢¤ë¡£ @li $POS ¤Î°ÌÃÖ¤Îʸ»ú¤Ï $KEY ¤ò¥¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¡£ @li ¤½¤Îʸ»ú¤Ï¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Á¡¢¤½¤ÎÃͤ¬ @c NULL ¤Ç¤¢¤ë¡£ ¤³¤ÎÆó¤Ä¤ò¶èÊ̤¹¤ëɬÍפ¬¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢´Ø¿ô mtext_get_prop_values () ¤òÂå¤ï¤ê¤Ë»ÈÍѤ¹¤ë¤³¤È¡£ @latexonly \IPAlabel{mtext_get_prop} @endlatexonly */ /*** @errors @c MERROR_RANGE, @c MERROR_SYMBOL @seealso mtext_get_prop_values (), mtext_put_prop (), mtext_put_prop_values (), mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */ void * mtext_get_prop (MText *mt, int pos, MSymbol key) { MTextPlist *plist; MInterval *interval; void *val; M_CHECK_POS (mt, pos, NULL); plist = get_plist_create (mt, key, 0); if (! plist) return NULL; interval = find_interval (plist, pos); val = (interval->nprops ? interval->stack[interval->nprops - 1]->val : NULL); return val; } /*=*/ /***en @brief Get multiple values of a text property. The mtext_get_prop_values () function searches the character at $POS in M-text $MT for the property whose key is $KEY. If such a property is found, its values are stored in the memory area pointed to by $VALUES. $NUM limits the maximum number of stored values. @return If the operation was successful, mtext_get_prop_values () returns the number of actually stored values. If the character at $POS does not have a property whose key is $KEY, the return value is 0. If an error is detected, mtext_get_prop_values () returns -1 and assigns an error code to the external variable #merror_code. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊ£¿ô¸ÄÆÀ¤ë. ´Ø¿ô mtext_get_prop_values () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤È¤¤¤¦°ÌÃÖ ¤Ë¤¢¤ëʸ»ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥¡¼¤¬ $KEY ¤Ç¤¢¤ë¤â¤Î¤òõ¤¹¡£¤â¤·¤½ ¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬¸«¤Ä¤«¤ì¤Ð¡¢¤½¤ì¤¬»ý¤ÄÃÍ (Ê£¿ô²Ä) ¤ò $VALUES ¤Î»Ø¤¹¥á¥â¥êÎΰè¤Ë³ÊǼ¤¹¤ë¡£$NUM ¤Ï³ÊǼ¤¹¤ëÃͤοô¤Î¾å¸Â¤Ç¤¢¤ë¡£ @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_get_prop_values () ¤Ï¼ÂºÝ¤Ë¥á¥â¥ê¤Ë³ÊǼ¤µ ¤ì¤¿Ãͤοô¤òÊÖ¤¹¡£$POS ¤Î°ÌÃÖ¤Îʸ»ú¤¬ $KEY ¤ò¥¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£ ¤ò»ý¤¿¤Ê¤±¤ì¤Ð 0 ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°Éô ÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ @latexonly \IPAlabel{mtext_get_prop_values} @endlatexonly */ /*** @errors @c MERROR_RANGE, @c MERROR_SYMBOL @seealso mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (), mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */ int mtext_get_prop_values (MText *mt, int pos, MSymbol key, void **values, int num) { MTextPlist *plist; MInterval *interval; int nprops; int i; int offset; M_CHECK_POS (mt, pos, -1); plist = get_plist_create (mt, key, 0); if (! plist) return 0; interval = find_interval (plist, pos); /* It is assured that INTERVAL is not NULL. */ nprops = interval->nprops; if (nprops == 0 || num <= 0) return 0; if (nprops == 1 || num == 1) { values[0] = interval->stack[nprops - 1]->val; return 1; } if (nprops <= num) num = nprops, offset = 0; else offset = nprops - num; for (i = 0; i < num; i++) values[i] = interval->stack[offset + i]->val; return num; } /*=*/ /***en @brief Get a list of text property keys at a position of an M-text. The mtext_get_prop_keys () function creates an array whose elements are the keys of text properties found at position $POS in M-text $MT, and sets *$KEYS to the address of the created array. The user is responsible to free the memory allocated for the array. @returns If the operation was successful, mtext_get_prop_keys () returns the length of the key list. Otherwise it returns -1 and assigns an error code to the external variable #merror_code. */ /***ja @brief M-text ¤Î»ØÄꤷ¤¿°ÌÃ֤Υƥ¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤Î¥ê¥¹¥È¤òÆÀ¤ë. ´Ø¿ô mtext_get_prop_keys () ¤Ï¡¢M-text $MT Æâ¤Ç $POS ¤Î°ÌÃ֤ˤ¢¤ë ¤¹¤Ù¤Æ¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤òÍ×ÁǤȤ¹¤ëÇÛÎó¤òºî¤ê¡¢¤½¤ÎÇÛÎó¤Î ¥¢¥É¥ì¥¹¤ò *$KEYS ¤ËÀßÄꤹ¤ë¡£¤³¤ÎÇÛÎó¤Î¤¿¤á¤Ë³ÎÊݤµ¤ì¤¿¥á¥â¥ê¤ò²ò Êü¤¹¤ë¤Î¤Ï¥æ¡¼¥¶¤ÎÀÕǤ¤Ç¤¢¤ë¡£ @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_get_prop_keys () ¤ÏÆÀ¤é¤ì¤¿¥ê¥¹¥È¤ÎŤµ¤òÊÖ ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò ÀßÄꤹ¤ë¡£ */ /*** @errors @c MERROR_RANGE @seealso mtext_get_prop (), mtext_put_prop (), mtext_put_prop_values (), mtext_get_prop_values (), mtext_push_prop (), mtext_pop_prop () */ int mtext_get_prop_keys (MText *mt, int pos, MSymbol **keys) { MTextPlist *plist; int i; M_CHECK_POS (mt, pos, -1); for (i = 0, plist = mt->plist; plist; i++, plist = plist->next); if (i == 0) { *keys = NULL; return 0; } MTABLE_MALLOC (*keys, i, MERROR_TEXTPROP); for (i = 0, plist = mt->plist; plist; plist = plist->next) { MInterval *interval = find_interval (plist, pos); if (interval->nprops) (*keys)[i++] = plist->key; } return i; } /*=*/ /***en @brief Set a text property. The mtext_put_prop () function sets a text property to the characters between $FROM (inclusive) and $TO (exclusive) in M-text $MT. $KEY and $VAL specify the key and the value of the text property. With this function, @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <------------------ OLD_VAL --------------------> @endverbatim becomes @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL--> @endverbatim @return If the operation was successful, mtext_put_prop () returns 0. Otherwise it returns -1 and assigns an error code to the external variable #merror_code. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ¤ë. ´Ø¿ô mtext_put_prop () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¤è ¤¦¤Ê¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ¤ë¡£¤³¤Î´Ø¿ô¤Ë¤è¤Ã¤Æ @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP: <------------------ OLD_VAL --------------------> @endverbatim ¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¡£ @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP: <-- OLD_VAL-><-------- VAL -------><-- OLD_VAL--> @endverbatim @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_put_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ @latexonly \IPAlabel{mtext_put_prop} @endlatexonly */ /*** @errors @c MERROR_RANGE, @c MERROR_SYMBOL @seealso mtext_put_prop_values (), mtext_get_prop (), mtext_get_prop_values (), mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */ int mtext_put_prop (MText *mt, int from, int to, MSymbol key, void *val) { MTextPlist *plist; MTextProperty *prop; MInterval *interval; M_CHECK_RANGE (mt, from, to, -1, 0); prepare_to_modify (mt, from, to, key, 0); plist = get_plist_create (mt, key, 1); interval = pop_all_properties (plist, from, to); prop = new_text_property (mt, from, to, key, val, 0); PUSH_PROP (interval, prop); M17N_OBJECT_UNREF (prop); if (interval->next) maybe_merge_interval (plist, interval); if (interval->prev) maybe_merge_interval (plist, interval->prev); xassert (check_plist (plist, 0) == 0); return 0; } /*=*/ /***en @brief Set multiple text properties with the same key. The mtext_put_prop_values () function sets a text property to the characters between $FROM (inclusive) and $TO (exclusive) in M-text $MT. $KEY and $VALUES specify the key and the values of the text property. $NUM specifies the number of property values to be set. @return If the operation was successful, mtext_put_prop_values () returns 0. Otherwise it returns -1 and assigns an error code to the external variable #merror_code. */ /***ja @brief Ʊ¤¸¥¡¼¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÊ£¿ôÀßÄꤹ¤ë. ´Ø¿ô mtext_put_prop_values () ¤Ï¡¢M-Text $MT ¤Î$FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë ¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¡¢¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀßÄꤹ ¤ë¡£¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤Ï $KEY ¤Ë¤è¤Ã¤Æ¡¢ÃÍ(Ê£¿ô²Ä)¤Ï $VALUES ¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤ë¡£$NUM ¤ÏÀßÄꤵ¤ì¤ëÃͤθĿô¤Ç¤¢¤ë¡£ @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_put_prop_values () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤± ¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ @latexonly \IPAlabel{mtext_put_prop_values} @endlatexonly */ /*** @errors @c MERROR_RANGE, @c MERROR_SYMBOL @seealso mtext_put_prop (), mtext_get_prop (), mtext_get_prop_values (), mtext_push_prop (), mtext_pop_prop (), mtext_prop_range () */ int mtext_put_prop_values (MText *mt, int from, int to, MSymbol key, void **values, int num) { MTextPlist *plist; MInterval *interval; int i; M_CHECK_RANGE (mt, from, to, -1, 0); prepare_to_modify (mt, from, to, key, 0); plist = get_plist_create (mt, key, 1); interval = pop_all_properties (plist, from, to); if (num > 0) { PREPARE_INTERVAL_STACK (interval, num); for (i = 0; i < num; i++) { MTextProperty *prop = new_text_property (mt, from, to, key, values[i], 0); PUSH_PROP (interval, prop); M17N_OBJECT_UNREF (prop); } } if (interval->next) maybe_merge_interval (plist, interval); if (interval->prev) maybe_merge_interval (plist, interval->prev); xassert (check_plist (plist, 0) == 0); return 0; } /*=*/ /***en @brief Push a text property. The mtext_push_prop () function pushes a text property whose key is $KEY and value is $VAL to the characters between $FROM (inclusive) and $TO (exclusive) in M-text $MT. With this function, @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <------------------ OLD_VAL --------------------> @endverbatim becomes @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <------------------- OLD_VAL -------------------> PROP : <-------- VAL -------> @endverbatim @return If the operation was successful, mtext_push_prop () returns 0. Otherwise it returns -1 and assigns an error code to the external variable #merror_code. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥×¥Ã¥·¥å¤¹¤ë. ´Ø¿ô mtext_push_prop () ¤Ï¡¢¥¡¼¤¬ $KEY ¤ÇÃͤ¬ $VAL ¤Ç¤¢¤ë¥Æ¥¥¹¥È ¥×¥í¥Ñ¥Æ¥£¤ò¡¢M-text $MT Ãæ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê ¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤Ë¥×¥Ã¥·¥å¤¹¤ë¡£¤³¤Î´Ø¿ô¤Ë¤è¤Ã¤Æ @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <------------------ OLD_VAL --------------------> @endverbatim ¤Ï¼¡¤Î¤è¤¦¤Ë¤Ê¤ë¡£ @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <------------------- OLD_VAL -------------------> PROP : <-------- VAL -------> @endverbatim @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_push_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ @latexonly \IPAlabel{mtext_push_prop} @endlatexonly */ /*** @errors @c MERROR_RANGE, @c MERROR_SYMBOL @seealso mtext_put_prop (), mtext_put_prop_values (), mtext_get_prop (), mtext_get_prop_values (), mtext_pop_prop (), mtext_prop_range () */ int mtext_push_prop (MText *mt, int from, int to, MSymbol key, void *val) { MTextPlist *plist; MInterval *head, *tail, *interval; MTextProperty *prop; int check_head, check_tail; M_CHECK_RANGE (mt, from, to, -1, 0); prepare_to_modify (mt, from, to, key, 0); plist = get_plist_create (mt, key, 1); /* Find an interval that covers the position FROM. */ head = find_interval (plist, from); /* If the found interval starts before FROM, divide it at FROM. */ if (head->start < from) { divide_interval (plist, head, from); head = head->next; check_head = 0; } else check_head = 1; /* Find an interval that ends at TO. If TO is not at the end of an interval, make one that ends at TO. */ if (head->end == to) { tail = head; check_tail = 1; } else if (head->end > to) { divide_interval (plist, head, to); tail = head; check_tail = 0; } else { tail = find_interval (plist, to); if (! tail) { tail = plist->tail; check_tail = 0; } else if (tail->start == to) { tail = tail->prev; check_tail = 1; } else { divide_interval (plist, tail, to); check_tail = 0; } } prop = new_text_property (mt, from, to, key, val, 0); /* Push PROP to the current values of intervals between HEAD and TAIL (both inclusive). */ for (interval = head; ; interval = interval->next) { PUSH_PROP (interval, prop); if (interval == tail) break; } M17N_OBJECT_UNREF (prop); /* If there is a possibility that TAIL now has the same value as the next one, check it and concatenate them if necessary. */ if (tail->next && check_tail) maybe_merge_interval (plist, tail); /* If there is a possibility that HEAD now has the same value as the previous one, check it and concatenate them if necessary. */ if (head->prev && check_head) maybe_merge_interval (plist, head->prev); xassert (check_plist (plist, 0) == 0); return 0; } /*=*/ /***en @brief Pop a text property. The mtext_pop_prop () function removes the topmost text property whose key is $KEY from the characters between $FROM (inclusive) and and $TO (exclusive) in $MT. This function does nothing if characters in the region have no such text property. With this function, @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <------------------ OLD_VAL --------------------> @endverbatim becomes @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <--OLD_VAL-->| |<--OLD_VAL-->| @endverbatim @return If the operation was successful, mtext_pop_prop () return 0. Otherwise it returns -1 and assigns an error code to the external variable #merror_code. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ý¥Ã¥×¤¹¤ë. ´Ø¿ô mtext_pop_prop () ¤Ï¡¢¥¡¼¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î ¤¦¤Á°ìÈÖ¾å¤Î¤â¤Î¤ò¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO¡Ê´Þ¤Þ ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤÎʸ»ú¤«¤é¼è¤ê½ü¤¯¡£ »ØÄêÈϰϤÎʸ»ú¤¬¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿¤Ê¤¤¤Ê¤é¤Ð¡¢¤³¤Î´Ø¿ô¤Ï²¿ ¤â¤·¤Ê¤¤¡£¤³¤Î´Ø¿ô¤Ë¤è¤Ã¤Æ¡¢ @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <------------------ OLD_VAL --------------------> @endverbatim ¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£ @verbatim FROM TO M-text: |<------------|-------- MT ---------|------------>| PROP : <--OLD_VAL-->| |<--OLD_VAL-->| @endverbatim @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_pop_prop () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ @latexonly \IPAlabel{mtext_pop_prop} @endlatexonly */ /*** @errors @c MERROR_RANGE, @c MERROR_SYMBOL @seealso mtext_put_prop (), mtext_put_prop_values (), mtext_get_prop (), mtext_get_prop_values (), mtext_push_prop (), mtext_prop_range () */ int mtext_pop_prop (MText *mt, int from, int to, MSymbol key) { MTextPlist *plist; MInterval *head, *tail; int check_head = 1; if (key == Mnil) MERROR (MERROR_TEXTPROP, -1); M_CHECK_RANGE (mt, from, to, -1, 0); plist = get_plist_create (mt, key, 0); if (! plist) return 0; /* Find an interval that covers the position FROM. */ head = find_interval (plist, from); if (head->end >= to && head->nprops == 0) /* No property to pop. */ return 0; prepare_to_modify (mt, from, to, key, 0); /* If the found interval starts before FROM and has value(s), divide it at FROM. */ if (head->start < from) { if (head->nprops > 0) { divide_interval (plist, head, from); check_head = 0; } else from = head->end; head = head->next; } /* Pop the topmost text property from each interval following HEAD. Stop at an interval that ends after TO. */ for (tail = head; tail && tail->end <= to; tail = tail->next) if (tail->nprops > 0) POP_PROP (tail); if (tail) { if (tail->start < to) { if (tail->nprops > 0) { divide_interval (plist, tail, to); POP_PROP (tail); } to = tail->start; } else to = tail->end; } else to = plist->tail->start; /* If there is a possibility that HEAD now has the same text properties as the previous one, check it and concatenate them if necessary. */ if (head->prev && check_head) head = head->prev; while (head && head->end <= to) head = maybe_merge_interval (plist, head); xassert (check_plist (plist, 0) == 0); return 0; } /*=*/ /***en @brief Find the range where the value of a text property is the same. The mtext_prop_range () function investigates the extent where all characters have the same value for a text property. It first finds the value of the property specified by $KEY of the character at $POS in M-text $MT. Then it checks if adjacent characters have the same value for the property $KEY. The beginning and the end of the found range are stored to the variable pointed to by $FROM and $TO. The character position stored in $FROM is inclusive but that in $TO is exclusive; this fashion is compatible with the range specification in the mtext_put_prop () function, etc. If $DEEPER is not 0, not only the topmost but also all the stacked properties whose key is $KEY are compared. If $FROM is @c NULL, the beginning of range is not searched for. If $TO is @c NULL, the end of range is not searched for. @return If the operation was successful, mtext_prop_range () returns the number of values the property $KEY has at pos. Otherwise it returns -1 and assigns an error code to the external variable @c merror_code. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬Æ±¤¸Ãͤò¤È¤ëÈϰϤòÄ´¤Ù¤ë. ´Ø¿ô mtext_prop_range () ¤Ï¡¢»ØÄꤷ¤¿¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤ¬Æ±¤¸ ¤Ç¤¢¤ëϢ³¤·¤¿Ê¸»ú¤ÎÈϰϤòÄ´¤Ù¤ë¡£¤Þ¤º M-text $MT ¤Î $POS ¤Î°ÌÃÖ¤Ë ¤¢¤ëʸ»ú¤Î¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á¡¢¥¡¼ $KEY ¤Ç»ØÄꤵ¤ì¤¿¤â¤ÎÃͤò¸«¤Ä¤± ¤ë¡£¤½¤·¤ÆÁ°¸å¤Îʸ»ú¤â $KEY ¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤ¬Æ±¤¸¤Ç¤¢¤ë¤«¤É¤¦¤«¤ò Ä´¤Ù¤ë¡£¸«¤Ä¤±¤¿ÈϰϤκǽé¤ÈºÇ¸å¤ò¡¢¤½¤ì¤¾¤ì $FROM ¤È $TO ¤Ë¥Ý¥¤¥ó ¥È¤µ¤ì¤ëÊÑ¿ô¤ËÊݸ¤¹¤ë¡£$FROM ¤ËÊݸ¤µ¤ì¤ëʸ»ú¤Î°ÌÃ֤ϸ«¤Ä¤±¤¿ÈÏ°Ï ¤Ë´Þ¤Þ¤ì¤ë¤¬¡¢$TO ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£¡Ê$TO ¤ÎÁ°¤ÇƱ¤¸Ãͤò¤È¤ëÈϰϤϽª¤ï ¤ë¡£¡Ë¤³¤ÎÈϰϻØÄêË¡¤Ï¡¢´Ø¿ô mtext_put_prop () ¤Ê¤É¤È¶¦Ä̤Ǥ¢¤ë¡£ $DEEPER ¤¬ 0 ¤Ç¤Ê¤±¤ì¤Ð¡¢$KEY ¤È¤¤¤¦¥¡¼¤ò»ý¤Ä¥×¥í¥Ñ¥Æ¥£¤Î¤¦¤Á°ìÈÖ ¾å¤Î¤â¤Î¤À¤±¤Ç¤Ê¤¯¡¢¥¹¥¿¥Ã¥¯Ãæ¤Î¤¹¤Ù¤Æ¤Î¤â¤Î¤¬Èæ³Ó¤µ¤ì¤ë¡£ $FROM ¤¬ @c NULL ¤Ê¤é¤Ð¡¢ÈϰϤλϤޤê¤Ïõº÷¤·¤Ê¤¤¡£$TO ¤¬ @c NULL ¤Ê¤é¤Ð¡¢ÈϰϤνª¤ê¤Ïõº÷¤·¤Ê¤¤¡£ @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_prop_range () ¤Ï $KEY ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤοô¤ò ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð-1 ¤òÊÖ¤·¡¢ ³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼ ¥É¤òÀßÄꤹ¤ë¡£ @latexonly \IPAlabel{mtext_prop_range} @endlatexonly */ /*** @errors @c MERROR_RANGE, @c MERROR_SYMBOL @seealso mtext_put_prop (), mtext_put_prop_values (), mtext_get_prop (), mtext_get_prop_values (), mtext_pop_prop (), mtext_push_prop () */ int mtext_prop_range (MText *mt, MSymbol key, int pos, int *from, int *to, int deeper) { MTextPlist *plist; MInterval *interval, *temp; void *val; int nprops; M_CHECK_POS (mt, pos, -1); plist = get_plist_create (mt, key, 0); if (! plist) { if (from) *from = 0; if (to) *to = mtext_nchars (mt); return 0; } interval = find_interval (plist, pos); nprops = interval->nprops; if (deeper || ! nprops) { if (from) *from = interval->start; if (to) *to = interval->end; return interval->nprops; } val = nprops ? interval->stack[nprops - 1] : NULL; if (from) { for (temp = interval; temp->prev && (temp->prev->nprops ? (nprops && (val == temp->prev->stack[temp->prev->nprops - 1])) : ! nprops); temp = temp->prev); *from = temp->start; } if (to) { for (temp = interval; temp->next && (temp->next->nprops ? (nprops && val == temp->next->stack[temp->next->nprops - 1]) : ! nprops); temp = temp->next); *to = temp->end; } return nprops; } /***en @brief Create a text property. The mtext_property () function returns a newly allocated text property whose key is $KEY and value is $VAL. The created text property is not attached to any M-text, i.e. it is detached. $CONTROL_BITS must be 0 or logical OR of @c enum @c MTextPropertyControl. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÀ¸À®¤¹¤ë. ´Ø¿ô mtext_property () ¤Ï $KEY ¤ò¥¡¼¡¢$VAL ¤òÃͤȤ¹¤ë¿·¤·¤¯³ä¤êÅö ¤Æ¤é¤ì¤¿¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÊÖ¤¹¡£À¸À®¤·¤¿¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¤¤« ¤Ê¤ë M-text ¤Ë¤âÉղäµ¤ì¤Æ¤¤¤Ê¤¤¡¢¤¹¤Ê¤ï¤ÁʬΥ¤·¤Æ (detached) ¤¤¤ë¡£ $CONTROL_BITS ¤Ï 0 ¤Ç¤¢¤ë¤« @c enum @c MTextPropertyControl ¤ÎÏÀÍý OR ¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ */ MTextProperty * mtext_property (MSymbol key, void *val, int control_bits) { return new_text_property (NULL, 0, 0, key, val, control_bits); } /***en @brief Return the M-text of a text property. The mtext_property_mtext () function returns the M-text to which text property $PROP is attached. If $PROP is currently detached, NULL is returned. */ /***ja @brief ¤¢¤ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä M-text ¤òÊÖ¤¹. ´Ø¿ô mtext_property_mtext () ¤Ï¡¢¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£$PROP ¤¬Éղäµ ¤ì¤Æ¤¤¤ë M-text ¤òÊÖ¤¹¡£¤½¤Î»þÅÀ¤Ç $PROP ¤¬Ê¬Î¥¤·¤Æ¤¤¤ì¤Ð NULL ¤ò ÊÖ¤¹¡£ */ MText * mtext_property_mtext (MTextProperty *prop) { return prop->mt; } /***en @brief Return the key of a text property. The mtext_property_key () function returns the key (symbol) of text property $PROP. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î¥¡¼¤òÊÖ¤¹. ´Ø¿ô mtext_property_key () ¤Ï¡¢¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤Î¥¡¼¡Ê¥· ¥ó¥Ü¥ë¡Ë¤òÊÖ¤¹¡£ */ MSymbol mtext_property_key (MTextProperty *prop) { return prop->key; } /***en @brief Return the value of a text property. The mtext_property_value () function returns the value of text property $PROP. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ÎÃͤòÊÖ¤¹. ´Ø¿ô mtext_property_value () ¤Ï¡¢¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤ÎÃͤòÊÖ ¤¹¡£ */ void * mtext_property_value (MTextProperty *prop) { return prop->val; } /***en @brief Return the start position of a text property. The mtext_property_start () function returns the start position of text property $PROP. The start position is a character position of an M-text where $PROP begins. If $PROP is detached, it returns -1. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î³«»Ï°ÌÃÖ¤òÊÖ¤¹. ´Ø¿ô mtext_property_start () ¤Ï¡¢¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤Î³«»Ï°Ì ÃÖ¤òÊÖ¤¹¡£³«»Ï°ÌÃÖ¤È¤Ï M-text Ãæ¤Ç $PROP ¤¬»Ï¤Þ¤ëʸ»ú°ÌÃ֤Ǥ¢¤ë¡£ $PROP ¤¬Ê¬Î¥¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢-1 ¤òÊÖ¤¹¡£ */ int mtext_property_start (MTextProperty *prop) { return (prop->mt ? prop->start : -1); } /***en @brief Return the end position of a text property. The mtext_property_end () function returns the end position of text property $PROP. The end position is a character position of an M-text where $PROP ends. If $PROP is detached, it returns -1. */ /***ja @brief ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î½ªÎ»°ÌÃÖ¤òÊÖ¤¹. ´Ø¿ô mtext_property_end () ¤Ï¡¢¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤Î½ªÎ»°ÌÃÖ ¤òÊÖ¤¹¡£½ªÎ»°ÌÃÖ¤È¤Ï M-text Ãæ¤Ç $PROP ¤¬½ª¤ëʸ»ú°ÌÃ֤Ǥ¢¤ë¡£$PROP ¤¬Ê¬Î¥¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢-1 ¤òÊÖ¤¹¡£ */ int mtext_property_end (MTextProperty *prop) { return (prop->mt ? prop->end : -1); } /***en @brief Get the topmost text property. The mtext_get_property () function searches the character at position $POS in M-text $MT for a text property whose key is $KEY. @return If a text property is found, mtext_get_property () returns it. If there are multiple text properties, it returns the topmost one. If no such property is found, it returns @c NULL without changing the external variable #merror_code. If an error is detected, mtext_get_property () returns @c NULL and assigns an error code to the external variable #merror_code. */ /***ja @brief °ìÈÖ¾å¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÆÀ¤ë. ´Ø¿ô mtext_get_property () ¤Ï M-text $MT ¤Î°ÌÃÖ $POS ¤Îʸ»ú¤¬¥¡¼ ¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¤«¤É¤¦¤«¤òÄ´¤Ù¤ë¡£ @return ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_get_property () ¤Ï¤½¤ì¤òÊÖ¤¹¡£ Ê£¿ô¤¢¤ë¾ì¹ç¤Ë¤Ï¡¢°ìÈÖ¾å¤Î¤â¤Î¤òÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤±¤ì¤Ð¡¢³°ÉôÊÑ¿ô #merror_code ¤òÊѤ¨¤ë¤³¤È¤Ê¤¯ @c NULL ¤òÊÖ¤¹¡£ ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç mtext_get_property () ¤Ï @c NULL ¤òÊÖ¤·¡¢³° ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ MTextProperty * mtext_get_property (MText *mt, int pos, MSymbol key) { MTextPlist *plist; MInterval *interval; M_CHECK_POS (mt, pos, NULL); plist = get_plist_create (mt, key, 0); if (! plist) return NULL; interval = find_interval (plist, pos); if (! interval->nprops) return NULL; return interval->stack[interval->nprops - 1]; } /***en @brief Get multiple text properties. The mtext_get_properties () function searches the character at $POS in M-text $MT for properties whose key is $KEY. If such properties are found, they are stored in the memory area pointed to by $PROPS. $NUM limits the maximum number of stored properties. @return If the operation was successful, mtext_get_properties () returns the number of actually stored properties. If the character at $POS does not have a property whose key is $KEY, the return value is 0. If an error is detected, mtext_get_properties () returns -1 and assigns an error code to the external variable #merror_code. */ /***ja @brief Ê£¿ô¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÆÀ¤ë. ´Ø¿ô mtext_get_properties () ¤Ï M-text $MT ¤Î°ÌÃÖ $POS ¤Îʸ»ú¤¬¥¡¼ ¤¬ $KEY ¤Ç¤¢¤ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¤«¤É¤¦¤«¤òÄ´¤Ù¤ë¡£¤½¤Î¤è¤¦¤Ê ¥×¥í¥Ñ¥Æ¥£¤¬¤ß¤Ä¤«¤ì¤Ð¡¢$PROPS ¤¬»Ø¤¹¥á¥â¥êÎΰè¤ËÊݸ¤¹¤ë¡£$NUM ¤Ï Êݸ¤µ¤ì¤ë¥×¥í¥Ñ¥Æ¥£¤Î¿ô¤Î¾å¸Â¤Ç¤¢¤ë¡£ @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_get_properties () ¤Ï¼ÂºÝ¤ËÊݸ¤·¤¿¥×¥í¥Ñ¥Æ¥£ ¤Î¿ô¤òÊÖ¤¹¡£$POS ¤Î°ÌÃÖ¤Îʸ»ú¤¬¥¡¼¤¬ $KEY ¤Ç¤¢¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤¿ ¤Ê¤±¤ì¤Ð¡¢0 ¤¬Ê֤롣¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ë¤Ï¡¢ mtext_get_properties () ¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼ ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ int mtext_get_properties (MText *mt, int pos, MSymbol key, MTextProperty **props, int num) { MTextPlist *plist; MInterval *interval; int nprops; int i; int offset; M_CHECK_POS (mt, pos, -1); plist = get_plist_create (mt, key, 0); if (! plist) return 0; interval = find_interval (plist, pos); /* It is assured that INTERVAL is not NULL. */ nprops = interval->nprops; if (nprops == 0 || num <= 0) return 0; if (nprops == 1 || num == 1) { props[0] = interval->stack[nprops - 1]; return 1; } if (nprops <= num) num = nprops, offset = 0; else offset = nprops - num; for (i = 0; i < num; i++) props[i] = interval->stack[offset + i]; return num; } /***en @brief Attach a text property to an M-text. The mtext_attach_property () function attaches text property $PROP to the range between $FROM and $TO in M-text $MT. If $PROP is already attached to an M-text, it is detached before attached to $MT. @return If the operation was successful, mtext_attach_property () returns 0. Otherwise it returns -1 and assigns an error code to the external variable #merror_code. */ /***ja @brief M-text¤Ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òÉղ乤ë. ´Ø¿ô mtext_attach_property () ¤Ï¡¢M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ ¤Ç¤ÎÎΰè¤Ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤òÉղ乤롣¤â¤· $PROP ¤¬´û¤Ë M-text ¤ËÉղäµ¤ì¤Æ¤¤¤ì¤Ð¡¢$MT ¤ËÉղ乤ëÁ°¤ËʬΥ¤µ¤ì¤ë¡£ @return ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_attach_property () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤± ¤ì¤Ð -1 ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ int mtext_attach_property (MText *mt, int from, int to, MTextProperty *prop) { MTextPlist *plist; MInterval *interval; M_CHECK_RANGE (mt, from, to, -1, 0); M17N_OBJECT_REF (prop); if (prop->mt) mtext_detach_property (prop); prepare_to_modify (mt, from, to, prop->key, 0); plist = get_plist_create (mt, prop->key, 1); xassert (check_plist (plist, 0) == 0); interval = pop_all_properties (plist, from, to); xassert (check_plist (plist, 0) == 0); prop->mt = mt; prop->start = from; prop->end = to; PUSH_PROP (interval, prop); M17N_OBJECT_UNREF (prop); xassert (check_plist (plist, 0) == 0); if (interval->next) maybe_merge_interval (plist, interval); if (interval->prev) maybe_merge_interval (plist, interval->prev); xassert (check_plist (plist, 0) == 0); return 0; } /***en @brief Detach a text property from an M-text. The mtext_detach_property () function makes text property $PROP detached. @return This function always returns 0. */ /***ja @brief M-text ¤«¤é¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤òʬΥ¤¹¤ë. ´Ø¿ô mtext_detach_property () ¤Ï¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤òʬΥ¤¹¤ë¡£ @return ¤³¤Î´Ø¿ô¤Ï¾ï¤Ë 0 ¤òÊÖ¤¹¡£ */ int mtext_detach_property (MTextProperty *prop) { MTextPlist *plist; int start = prop->start, end = prop->end; if (! prop->mt) return 0; prepare_to_modify (prop->mt, start, end, prop->key, 0); plist = get_plist_create (prop->mt, prop->key, 0); xassert (plist); detach_property (plist, prop, NULL); return 0; } /***en @brief Push a text property onto an M-text. The mtext_push_property () function pushes text property $PROP to the characters between $FROM (inclusive) and $TO (exclusive) in M-text $MT. @return If the operation was successful, mtext_push_property () returns 0. Otherwise it returns -1 and assigns an error code to the external variable #merror_code. */ /***ja @brief M-text ¤Ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥×¥Ã¥·¥å¤¹¤ë. ´Ø¿ô mtext_push_property () ¤Ï¡¢¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£ $PROP ¤ò¡¢ M-text $MT Ãæ¤Î $FROM ¡Ê´Þ¤Þ¤ì¤ë¡Ë¤«¤é $TO ¡Ê´Þ¤Þ¤ì¤Ê¤¤¡Ë¤ÎÈϰϤΠʸ»ú¤Ë¥×¥Ã¥·¥å¤¹¤ë¡£ @return ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_push_property () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤± ¤ì¤Ð -1 ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ */ int mtext_push_property (MText *mt, int from, int to, MTextProperty *prop) { MTextPlist *plist; MInterval *head, *tail, *interval; int check_head, check_tail; M_CHECK_RANGE (mt, from, to, -1, 0); M17N_OBJECT_REF (prop); if (prop->mt) mtext_detach_property (prop); prepare_to_modify (mt, from, to, prop->key, 0); plist = get_plist_create (mt, prop->key, 1); prop->mt = mt; prop->start = from; prop->end = to; /* Find an interval that covers the position FROM. */ head = find_interval (plist, from); /* If the found interval starts before FROM, divide it at FROM. */ if (head->start < from) { divide_interval (plist, head, from); head = head->next; check_head = 0; } else check_head = 1; /* Find an interval that ends at TO. If TO is not at the end of an interval, make one that ends at TO. */ if (head->end == to) { tail = head; check_tail = 1; } else if (head->end > to) { divide_interval (plist, head, to); tail = head; check_tail = 0; } else { tail = find_interval (plist, to); if (! tail) { tail = plist->tail; check_tail = 0; } else if (tail->start == to) { tail = tail->prev; check_tail = 1; } else { divide_interval (plist, tail, to); check_tail = 0; } } /* Push PROP to the current values of intervals between HEAD and TAIL (both inclusive). */ for (interval = head; ; interval = interval->next) { PUSH_PROP (interval, prop); if (interval == tail) break; } /* If there is a possibility that TAIL now has the same value as the next one, check it and concatenate them if necessary. */ if (tail->next && check_tail) maybe_merge_interval (plist, tail); /* If there is a possibility that HEAD now has the same value as the previous one, check it and concatenate them if necessary. */ if (head->prev && check_head) maybe_merge_interval (plist, head->prev); M17N_OBJECT_UNREF (prop); xassert (check_plist (plist, 0) == 0); return 0; } /***en @brief Symbol for specifying serializer functions. To serialize a text property, the user must supply a serializer function for that text property. This is done by giving a symbol property whose key is #Mtext_prop_serializer and value is a pointer to an appropriate serializer function. @seealso mtext_serialize (), #MTextPropSerializeFunc */ /***ja @brief ¥·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë. ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¤¿¤á¤Ë¤Ï¡¢¤½¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ ¥Æ¥£ÍѤΥ·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤òÍ¿¤¨¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¶ñÂÎŪ¤Ë¤Ï¡¢ #Mtext_prop_serializer ¤ò¥¡¼¤È¤·¡¢Å¬Àڤʥ·¥ê¥¢¥é¥¤¥º´Ø¿ô¤Ø¤Î¥Ý¥¤ ¥ó¥¿¤òÃͤȤ¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¡£ @seealso mtext_serialize (), #MTextPropSerializeFunc */ MSymbol Mtext_prop_serializer; /***en @brief Symbol for specifying deserializer functions. To deserialize a text property, the user must supply a deserializer function for that text property. This is done by giving a symbol property whose key is #Mtext_prop_deserializer and value is a pointer to an appropriate deserializer function. @seealso mtext_deserialize (), #MTextPropSerializeFunc */ /***ja @brief ¥Ç¥·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë. ¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ç¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¤¿¤á¤Ë¤Ï¡¢¤½¤Î¥Æ¥¥¹¥È¥×¥í ¥Ñ¥Æ¥£ÍѤΥǥ·¥ê¥¢¥é¥¤¥¶´Ø¿ô¤òÍ¿¤¨¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¶ñÂÎŪ¤Ë¤Ï¡¢ #Mtext_prop_deserializer ¤ò¥¡¼¤È¤·¡¢Å¬Àڤʥǥ·¥ê¥¢¥é¥¤¥º´Ø¿ô¤Ø¤Î ¥Ý¥¤¥ó¥¿¤òÃͤȤ¹¤ë¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£¤ò»ØÄꤹ¤ë¡£ @seealso mtext_deserialize (), #MTextPropSerializeFunc */ MSymbol Mtext_prop_deserializer; /***en @brief Serialize text properties in an M-text. The mtext_serialize () function serializes the text between $FROM and $TO in M-text $MT. The serialized result is an M-text in a form of XML. $PROPERTY_LIST limits the text properties to be serialized. Only those text properties whose key @li appears as the value of an element in $PROPERTY_LIST, and @li has the symbol property #Mtext_prop_serializer are serialized as a "property" element in the resulting XML representation. The DTD of the generated XML is as follows: @verbatim <!DOCTYPE mtext [ <!ELEMENT mtext (property*,body+)> <!ELEMENT property EMPTY> <!ELEMENT body (#PCDATA)> <!ATTLIST property key CDATA #REQUIRED> <!ATTLIST property value CDATA #REQUIRED> <!ATTLIST property from CDATA #REQUIRED> <!ATTLIST property to CDATA #REQUIRED> <!ATTLIST property control CDATA #REQUIRED> ]> @endverbatim This function depends on the libxml2 library. If the m17n library is configured without libxml2, this function always fails. @return If the operation was successful, mtext_serialize () returns an M-text in the form of XML. Otherwise it returns @c NULL and assigns an error code to the external variable #merror_code. @seealso mtext_deserialize (), #Mtext_prop_serializer */ /***ja @brief M-text Ãæ¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥·¥ê¥¢¥é¥¤¥º¤¹¤ë. ´Ø¿ô mtext_serialize () ¤Ï M-text $MT ¤Î $FROM ¤«¤é $TO ¤Þ¤Ç¤Î¥Æ¥ ¥¹¥È¤ò¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¡£¥·¥ê¥¢¥é¥¤¥º¤·¤¿·ë²Ì¤Ï XML ·Á¼°¤Î M-text ¤Ç ¤¢¤ë¡£ $PROPERTY_LIST ¤Ï¥·¥ê¥¢¥é¥¤¥º¤µ¤ì¤ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¸ÂÄê ¤¹¤ë¡£ÂÐ¾Ý¤È¤Ê¤ë¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¡¢¤½¤Î¥¡¼¤¬ @li $PROPERTY_LIST ¤ÎÍ×ÁǤÎÃͤȤ·¤Æ¸½¤ï¤ì¡¢¤«¤Ä @li ¥·¥ó¥Ü¥ë¥×¥í¥Ñ¥Æ¥£ #Mtext_prop_serializer ¤ò»ý¤Ä ¤â¤Î¤Î¤ß¤Ç¤¢¤ë¡£¤³¤Î¾ò·ï¤òËþ¤¿¤¹¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¡¢À¸À®¤µ¤ì¤ë XML ɽ¸½Ãæ¤Ç "property" Í×ÁǤ˥·¥ê¥¢¥é¥¤¥º¤µ¤ì¤ë¡£ À¸À®¤µ¤ì¤ë XML ¤Î DTD ¤Ï°Ê²¼¤ÎÄ̤ê: @verbatim <!DOCTYPE mtext [ <!ELEMENT mtext (property*,body+)> <!ELEMENT property EMPTY> <!ELEMENT body (#PCDATA)> <!ATTLIST property key CDATA #REQUIRED> <!ATTLIST property value CDATA #REQUIRED> <!ATTLIST property from CDATA #REQUIRED> <!ATTLIST property to CDATA #REQUIRED> <!ATTLIST property control CDATA #REQUIRED> ]> @endverbatim ¤³¤Î´Ø¿ô¤Ï libxml2 ¥é¥¤¥Ö¥é¥ê¤Ë°Í¸¤¹¤ë¡£m17n ¥é¥¤¥Ö¥é¥ê¤¬libxml2 ̵¤·¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤Î´Ø¿ô¤Ï¾ï¤Ë¼ºÇÔ¤¹¤ë¡£ @return ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_serialize () ¤Ï XML ·Á¼°¤Ç M-text ¤òÊÖ¤¹¡£ ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô#merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É ¤òÀßÄꤹ¤ë¡£ @seealso mtext_deserialize (), #Mtext_prop_serializer */ MText * mtext_serialize (MText *mt, int from, int to, MPlist *property_list) { #ifdef HAVE_XML2 MPlist *plist, *pl; MTextPropSerializeFunc func; MText *work; xmlDocPtr doc; xmlNodePtr node; unsigned char *ptr; int n; M_CHECK_RANGE (mt, from, to, NULL, NULL); if (mt->format != MTEXT_FORMAT_US_ASCII && mt->format != MTEXT_FORMAT_UTF_8) mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8); if (MTEXT_DATA (mt)[mtext_nbytes (mt)] != 0) MTEXT_DATA (mt)[mtext_nbytes (mt)] = 0; doc = xmlParseMemory (XML_TEMPLATE, strlen (XML_TEMPLATE) + 1); node = xmlDocGetRootElement (doc); plist = mplist (); MPLIST_DO (pl, property_list) { MSymbol key = MPLIST_VAL (pl); func = ((MTextPropSerializeFunc) msymbol_get_func (key, Mtext_prop_serializer)); if (func) extract_text_properties (mt, from, to, key, plist); } work = mtext (); MPLIST_DO (pl, plist) { MTextProperty *prop = MPLIST_VAL (pl); char buf[256]; MPlist *serialized_plist; xmlNodePtr child; func = ((MTextPropSerializeFunc) msymbol_get_func (prop->key, Mtext_prop_serializer)); serialized_plist = (func) (prop->val); if (! serialized_plist) continue; mtext_reset (work); mplist__serialize (work, serialized_plist, 0); child = xmlNewChild (node, NULL, (xmlChar *) "property", NULL); xmlSetProp (child, (xmlChar *) "key", (xmlChar *) MSYMBOL_NAME (prop->key)); xmlSetProp (child, (xmlChar *) "value", (xmlChar *) MTEXT_DATA (work)); sprintf (buf, "%d", prop->start - from); xmlSetProp (child, (xmlChar *) "from", (xmlChar *) buf); sprintf (buf, "%d", prop->end - from); xmlSetProp (child, (xmlChar *) "to", (xmlChar *) buf); sprintf (buf, "%d", prop->control.flag); xmlSetProp (child, (xmlChar *) "control", (xmlChar *) buf); xmlAddChild (node, xmlNewText ((xmlChar *) "\n")); M17N_OBJECT_UNREF (serialized_plist); } M17N_OBJECT_UNREF (plist); if (from > 0 || to < mtext_nchars (mt)) mtext_copy (work, 0, mt, from, to); else { M17N_OBJECT_UNREF (work); work = mt; } for (from = 0, to = mtext_nchars (mt); from <= to; from++) { ptr = MTEXT_DATA (mt) + POS_CHAR_TO_BYTE (mt, from); xmlNewTextChild (node, NULL, (xmlChar *) "body", (xmlChar *) ptr); from = mtext_character (mt, from, to, 0); if (from < 0) from = to; } xmlDocDumpMemoryEnc (doc, (xmlChar **) &ptr, &n, "UTF-8"); if (work == mt) work = mtext (); mtext__cat_data (work, ptr, n, MTEXT_FORMAT_UTF_8); return work; #else /* not HAVE_XML2 */ MERROR (MERROR_TEXTPROP, NULL); #endif /* not HAVE_XML2 */ } /***en @brief Deserialize text properties in an M-text. The mtext_deserialize () function deserializes M-text $MT. $MT must be an XML having the following DTD. @verbatim <!DOCTYPE mtext [ <!ELEMENT mtext (property*,body+)> <!ELEMENT property EMPTY> <!ELEMENT body (#PCDATA)> <!ATTLIST property key CDATA #REQUIRED> <!ATTLIST property value CDATA #REQUIRED> <!ATTLIST property from CDATA #REQUIRED> <!ATTLIST property to CDATA #REQUIRED> <!ATTLIST property control CDATA #REQUIRED> ]> @endverbatim This function depends on the libxml2 library. If the m17n library is configured without libxml2, this function always fail. @return If the operation was successful, mtext_deserialize () returns the resulting M-text. Otherwise it returns @c NULL and assigns an error code to the external variable #merror_code. @seealso mtext_serialize (), #Mtext_prop_deserializer */ /***ja @brief M-text Ãæ¤Î¥Æ¥¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò¥Ç¥·¥ê¥¢¥é¥¤¥º¤¹¤ë. ´Ø¿ô mtext_deserialize () ¤Ï M-text $MT ¤ò¥Ç¥·¥ê¥¢¥é¥¤¥º¤¹¤ë¡£$MT ¤Ï¼¡¤Î DTD ¤ò»ý¤Ä XML ¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ @verbatim <!DOCTYPE mtext [ <!ELEMENT mtext (property*,body+)> <!ELEMENT property EMPTY> <!ELEMENT body (#PCDATA)> <!ATTLIST property key CDATA #REQUIRED> <!ATTLIST property value CDATA #REQUIRED> <!ATTLIST property from CDATA #REQUIRED> <!ATTLIST property to CDATA #REQUIRED> <!ATTLIST property control CDATA #REQUIRED> ]> @endverbatim ¤³¤Î´Ø¿ô¤Ï libxml2 ¥é¥¤¥Ö¥é¥ê¤Ë°Í¸¤¹¤ë¡£m17n ¥é¥¤¥Ö¥é¥ê¤¬libxml2 ̵¤·¤ËÀßÄꤵ¤ì¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤Î´Ø¿ô¤Ï¾ï¤Ë¼ºÇÔ¤¹¤ë¡£ @return ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð¡¢mtext_serialize () ¤ÏÆÀ¤é¤ì¤¿ M-text ¤ò ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¤Æ³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼ ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£ @seealso mtext_serialize (), #Mtext_prop_deserializer */ MText * mtext_deserialize (MText *mt) { #ifdef HAVE_XML2 xmlDocPtr doc; xmlNodePtr node; xmlXPathContextPtr context; xmlXPathObjectPtr result; xmlChar *body_str, *key_str, *val_str, *from_str, *to_str, *ctl_str; int i; if (mt->format > MTEXT_FORMAT_UTF_8) MERROR (MERROR_TEXTPROP, NULL); doc = xmlParseMemory ((char *) MTEXT_DATA (mt), mtext_nbytes (mt)); if (! doc) MERROR (MERROR_TEXTPROP, NULL); node = xmlDocGetRootElement (doc); if (! node) { xmlFreeDoc (doc); MERROR (MERROR_TEXTPROP, NULL); } if (xmlStrcmp (node->name, (xmlChar *) "mtext")) { xmlFreeDoc (doc); MERROR (MERROR_TEXTPROP, NULL); } context = xmlXPathNewContext (doc); result = xmlXPathEvalExpression ((xmlChar *) "//body", context); if (xmlXPathNodeSetIsEmpty (result->nodesetval)) { xmlFreeDoc (doc); MERROR (MERROR_TEXTPROP, NULL); } for (i = 0, mt = mtext (); i < result->nodesetval->nodeNr; i++) { if (i > 0) mtext_cat_char (mt, 0); node = (xmlNodePtr) result->nodesetval->nodeTab[i]; body_str = xmlNodeListGetString (doc, node->xmlChildrenNode, 1); if (body_str) { mtext__cat_data (mt, body_str, strlen ((char *) body_str), MTEXT_FORMAT_UTF_8); xmlFree (body_str); } } result = xmlXPathEvalExpression ((xmlChar *) "//property", context); if (! xmlXPathNodeSetIsEmpty (result->nodesetval)) for (i = 0; i < result->nodesetval->nodeNr; i++) { MSymbol key; MTextPropDeserializeFunc func; MTextProperty *prop; MPlist *plist; int from, to, control; void *val; key_str = xmlGetProp (result->nodesetval->nodeTab[i], (xmlChar *) "key"); val_str = xmlGetProp (result->nodesetval->nodeTab[i], (xmlChar *) "value"); from_str = xmlGetProp (result->nodesetval->nodeTab[i], (xmlChar *) "from"); to_str = xmlGetProp (result->nodesetval->nodeTab[i], (xmlChar *) "to"); ctl_str = xmlGetProp (result->nodesetval->nodeTab[i], (xmlChar *) "control"); key = msymbol ((char *) key_str); func = ((MTextPropDeserializeFunc) msymbol_get_func (key, Mtext_prop_deserializer)); if (! func) continue; plist = mplist__from_string (val_str, strlen ((char *) val_str)); if (! plist) continue; if (sscanf ((char *) from_str, "%d", &from) != 1 || from < 0 || from >= mtext_nchars (mt)) continue; if (sscanf ((char *) to_str, "%d", &to) != 1 || to <= from || to > mtext_nchars (mt)) continue; if (sscanf ((char *) ctl_str, "%d", &control) != 1 || control < 0 || control > MTEXTPROP_CONTROL_MAX) continue; val = (func) (plist); M17N_OBJECT_UNREF (plist); prop = mtext_property (key, val, control); if (key->managing_key) M17N_OBJECT_UNREF (val); mtext_push_property (mt, from, to, prop); M17N_OBJECT_UNREF (prop); xmlFree (key_str); xmlFree (val_str); xmlFree (from_str); xmlFree (to_str); xmlFree (ctl_str); } xmlXPathFreeContext (context); xmlFreeDoc (doc); return mt; #else /* not HAVE_XML2 */ MERROR (MERROR_TEXTPROP, NULL); #endif /* not HAVE_XML2 */ } /*** @} */ /* Local Variables: coding: euc-japan End: */