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