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