*** empty log message ***
[m17n/m17n-lib.git] / src / mtext.c
1 /* mtext.c -- M-text module.
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 /***en
24     @addtogroup m17nMtext
25     @brief M-text objects and API for them.
26
27     In the m17n library, text is represented as an object called @e
28     M-text rather than as a C-string (<tt>char *</tt> or <tt>unsigned
29     char *</tt>).  An M-text is a sequence of characters whose length
30     is equals to or more than 0, and can be coined from various
31     character sources, e.g. C-strings, files, character codes, etc.
32
33     M-texts are more useful than C-strings in the following points.
34
35     @li M-texts can handle mixture of characters of various scripts,
36     including all Unicode characters and more.  This is an
37     indispensable facility when handling multilingual text.
38
39     @li Each character in an M-text can have properties called @e text
40     @e properties. Text properties store various kinds of information
41     attached to parts of an M-text to provide application programs
42     with a unified view of those information.  As rich information can
43     be stored in M-texts in the form of text properties, functions in
44     application programs can be simple.
45
46     In addition, the library provides many functions to manipulate an
47     M-text just the same way as a C-string.  */
48
49 /***ja
50     @addtogroup m17nMtext
51
52     @brief M-text ¥ª¥Ö¥¸¥§¥¯¥È¤È¤½¤ì¤Ë´Ø¤¹¤ë API.
53
54     m17n ¥é¥¤¥Ö¥é¥ê¤Ï¡¢ C-string¡Ê<tt>char *</tt> ¤ä <tt>unsigned char
55     *</tt>¡Ë¤Ç¤Ï¤Ê¤¯ @e M-text ¤È¸Æ¤Ö¥ª¥Ö¥¸¥§¥¯¥È¤Ç¥Æ¥­¥¹¥È¤òɽ¸½¤¹¤ë¡£
56     M-text ¤ÏŤµ 0 °Ê¾å¤Îʸ»úÎó¤Ç¤¢¤ê¡¢¼ï¡¹¤Îʸ»ú¥½¡¼¥¹¡Ê¤¿¤È¤¨¤Ð 
57     C-string¡¢¥Õ¥¡¥¤¥ë¡¢Ê¸»ú¥³¡¼¥ÉÅù¡Ë¤«¤éºîÀ®¤Ç¤­¤ë¡£
58
59     M-text ¤Ë¤Ï¡¢C-string ¤Ë¤Ê¤¤°Ê²¼¤ÎÆÃħ¤¬¤¢¤ë¡£     
60
61     @li M-text ¤ÏÈó¾ï¤Ë¿¤¯¤Î¼ïÎà¤Îʸ»ú¤ò¡¢Æ±»þ¤Ë¡¢º®ºß¤µ¤»¤Æ¡¢Æ±Åù¤Ë
62     °·¤¦¤³¤È¤¬¤Ç¤­¤ë¡£Unicode ¤ÎÁ´¤Æ¤Îʸ»ú¤Ï¤â¤Á¤í¤ó¡¢¤è¤ê¿¤¯¤Îʸ»ú¤Þ
63     ¤Ç°·¤¨¤ë¡£¤³¤ì¤Ï¿¸À¸ì¥Æ¥­¥¹¥È¤ò°·¤¦¾å¤Ç¤Ïɬ¿Ü¤Îµ¡Ç½¤Ç¤¢¤ë¡£
64
65     @li M-text Æâ¤Î³Æʸ»ú¤Ï¡¢@e ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ ¤È¸Æ¤Ð¤ì¤ë¥×¥í¥Ñ¥Æ¥£
66     ¤ò»ý¤Ä¤³¤È¤¬¤Ç¤­¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ë¤è¤Ã¤Æ¡¢¥Æ¥­¥¹¥È¤Î³ÆÉô°Ì¤Ë
67     ´Ø¤¹¤ëÍÍ¡¹¤Ê¾ðÊó¤ò M-text Æâ¤ËÊÝ»ý¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î¤¿¤á¡¢¤½¤ì
68     ¤é¤Î¾ðÊó¤ò¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥àÆâ¤ÇÅý°ìŪ¤Ë°·¤¦¤³¤È¤¬¤Ç¤­¤ë¡£
69     ¤Þ¤¿¡¢M-text ¼«ÂΤ¬Ë­É٤ʾðÊó¤ò»ý¤Ä¤¿¤á¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é
70     ¥àÃæ¤Î³Æ´Ø¿ô¤ò´ÊÁDz½¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£
71
72     ¤µ¤é¤Ëm17n ¥é¥¤¥Ö¥é¥ê¤Ï¡¢ C-string ¤òÁàºî¤¹¤ë¤¿¤á¤ËÄ󶡤µ¤ì¤ë¼ï¡¹
73     ¤Î´Ø¿ô¤ÈƱÅù¤â¤Î¤ò M-text ¤òÁàºî¤¹¤ë¤¿¤á¤Ë¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¡£  */
74
75 /*=*/
76
77 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
78 /*** @addtogroup m17nInternal
79      @{ */
80
81 #include <config.h>
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <string.h>
85 #include <locale.h>
86
87 #include "m17n.h"
88 #include "m17n-misc.h"
89 #include "internal.h"
90 #include "textprop.h"
91 #include "character.h"
92 #include "mtext.h"
93 #include "plist.h"
94
95 static M17NObjectArray mtext_table;
96
97 static MSymbol M_charbag;
98
99 #ifdef WORDS_BIGENDIAN
100 static enum MTextFormat default_utf_16 = MTEXT_FORMAT_UTF_16BE;
101 static enum MTextFormat default_utf_32 = MTEXT_FORMAT_UTF_32BE;
102 #else
103 static enum MTextFormat default_utf_16 = MTEXT_FORMAT_UTF_16LE;
104 static enum MTextFormat default_utf_32 = MTEXT_FORMAT_UTF_32LE;
105 #endif
106
107 /** Increment character position CHAR_POS and byte position BYTE_POS
108     so that they point to the next character in M-text MT.  No range
109     check for CHAR_POS and BYTE_POS.  */
110
111 #define INC_POSITION(mt, char_pos, byte_pos)                    \
112   do {                                                          \
113     int c;                                                      \
114                                                                 \
115     if ((mt)->format == MTEXT_FORMAT_UTF_8)                     \
116       {                                                         \
117         c = (mt)->data[(byte_pos)];                             \
118         (byte_pos) += CHAR_UNITS_BY_HEAD_UTF8 (c);              \
119       }                                                         \
120     else                                                        \
121       {                                                         \
122         c = ((unsigned short *) ((mt)->data))[(byte_pos)];      \
123                                                                 \
124         if ((mt)->format != default_utf_16)                     \
125           c = SWAP_16 (c);                                      \
126         (byte_pos) += (c < 0xD800 || c >= 0xE000) ? 1 : 2;      \
127       }                                                         \
128     (char_pos)++;                                               \
129   } while (0)
130
131
132 /** Decrement character position CHAR_POS and byte position BYTE_POS
133     so that they point to the previous character in M-text MT.  No
134     range check for CHAR_POS and BYTE_POS.  */
135
136 #define DEC_POSITION(mt, char_pos, byte_pos)                            \
137   do {                                                                  \
138     if ((mt)->format == MTEXT_FORMAT_UTF_8)                             \
139       {                                                                 \
140         unsigned char *p1 = (mt)->data + (byte_pos);                    \
141         unsigned char *p0 = p1 - 1;                                     \
142                                                                         \
143         while (! CHAR_HEAD_P (p0)) p0--;                                \
144         (byte_pos) -= (p1 - p0);                                        \
145       }                                                                 \
146     else                                                                \
147       {                                                                 \
148         int c = ((unsigned short *) ((mt)->data))[(byte_pos) - 1];      \
149                                                                         \
150         if ((mt)->format != default_utf_16)                             \
151           c = SWAP_16 (c);                                              \
152         (byte_pos) -= (c < 0xD800 || c >= 0xE000) ? 1 : 2;              \
153       }                                                                 \
154     (char_pos)--;                                                       \
155   } while (0)
156
157
158 static int
159 compare (MText *mt1, int from1, int to1, MText *mt2, int from2, int to2)
160 {
161   if (mt1->format == mt2->format
162       && (mt1->format < MTEXT_FORMAT_UTF_8))
163     {
164       unsigned char *p1, *pend1, *p2, *pend2;
165
166       p1 = mt1->data + mtext__char_to_byte (mt1, from1);
167       pend1 = mt1->data + mtext__char_to_byte (mt1, to1);
168
169       p2 = mt2->data + mtext__char_to_byte (mt2, from2);
170       pend2 = mt2->data + mtext__char_to_byte (mt2, to2);
171
172       for (; p1 < pend1 && p2 < pend2; p1++, p2++)
173         if (*p1 != *p2)
174           return (*p1 > *p2 ? 1 : -1);
175       return (p2 == pend2 ? (p1 < pend1) : -1);
176     }
177   for (; from1 < to1 && from2 < to2; from1++, from2++)
178     {
179       int c1 = mtext_ref_char (mt1, from1);
180       int c2 = mtext_ref_char (mt2, from2);
181
182       if (c1 != c2)
183         return (c1 > c2 ? 1 : -1);
184     }
185   return (from2 == to2 ? (from1 < to1) : -1);
186 }
187
188 static MText *
189 copy (MText *mt1, int pos, MText *mt2, int from, int to)
190 {
191   int pos_byte = POS_CHAR_TO_BYTE (mt1, pos);
192   int nbytes;
193   struct MTextPlist *plist;
194   unsigned char *p;
195
196   if (mt2->format <= MTEXT_FORMAT_UTF_8)
197     {
198       int from_byte = POS_CHAR_TO_BYTE (mt2, from);
199
200       p = mt2->data + from_byte;
201       nbytes = POS_CHAR_TO_BYTE (mt2, to) - from_byte;
202     }
203   else
204     {
205       unsigned char *p1;
206       int pos1;
207
208       p = p1 = alloca (MAX_UNICODE_CHAR_BYTES * (to - from));
209       for (pos1 = from; pos1 < to; pos1++)
210         {
211           int c = mtext_ref_char (mt2, pos1);
212           p1 += CHAR_STRING (c, p1);
213         }
214       nbytes = p1 - p;
215     }
216
217   if (mt1->cache_char_pos > pos)
218     {
219       mt1->cache_char_pos = pos;
220       mt1->cache_byte_pos = pos_byte;
221     }
222
223   if (pos_byte + nbytes >= mt1->allocated)
224     {
225       mt1->allocated = pos_byte + nbytes + 1;
226       MTABLE_REALLOC (mt1->data, mt1->allocated, MERROR_MTEXT);
227     }
228   memcpy (mt1->data + pos_byte, p, nbytes);
229   mt1->nbytes = pos_byte + nbytes;
230   mt1->data[mt1->nbytes] = 0;
231
232   plist = mtext__copy_plist (mt2->plist, from, to, mt1, pos);
233   if (pos == 0)
234     {
235       if (mt1->plist)
236         mtext__free_plist (mt1);
237       mt1->plist = plist;
238     }
239   else
240     {
241       if (pos < mt1->nchars)
242         mtext__adjust_plist_for_delete (mt1, pos, mt1->nchars - pos);
243       if (from < to)
244         mtext__adjust_plist_for_insert (mt1, pos, to - from, plist);
245     }
246
247   mt1->nchars = pos + (to - from);
248   if (mt1->nchars < mt1->nbytes)
249     mt1->format = MTEXT_FORMAT_UTF_8;
250   return mt1;
251 }
252
253
254 static MCharTable *
255 get_charbag (MText *mt)
256 {
257   MTextProperty *prop = mtext_get_property (mt, 0, M_charbag);
258   MCharTable *table;
259   int i;
260
261   if (prop)
262     {
263       if (prop->end == mt->nchars)
264         return ((MCharTable *) prop->val);
265       mtext_detach_property (prop);
266     }
267
268   table = mchartable (Msymbol, (void *) 0);
269   for (i = mt->nchars - 1; i >= 0; i--)
270     mchartable_set (table, mtext_ref_char (mt, i), Mt);
271   prop = mtext_property (M_charbag, table, MTEXTPROP_VOLATILE_WEAK);
272   mtext_attach_property (mt, 0, mtext_nchars (mt), prop);
273   M17N_OBJECT_UNREF (prop);
274   return table;
275 }
276
277
278 /* span () : Number of consecutive chars starting at POS in MT1 that
279    are included (if NOT is Mnil) or not included (if NOT is Mt) in
280    MT2.  */
281
282 static int
283 span (MText *mt1, MText *mt2, int pos, MSymbol not)
284 {
285   int nchars = mtext_nchars (mt1);
286   MCharTable *table = get_charbag (mt2);
287   int i;
288
289   for (i = pos; i < nchars; i++)
290     if ((MSymbol) mchartable_lookup (table, mtext_ref_char (mt1, i)) == not)
291       break;
292   return (i - pos);
293 }
294
295
296 static int
297 count_utf_8_chars (void *data, int nitems)
298 {
299   unsigned char *p = (unsigned char *) data;
300   unsigned char *pend = p + nitems;
301   int nchars = 0;
302
303   while (p < pend)
304     {
305       int i, n;
306
307       for (; p < pend && *p < 128; nchars++, p++);
308       if (p == pend)
309         return nchars;
310       if (! CHAR_HEAD_P_UTF8 (p))
311         return -1;
312       n = CHAR_UNITS_BY_HEAD_UTF8 (*p);
313       if (p + n > pend)
314         return -1;
315       for (i = 1; i < n; i++)
316         if (CHAR_HEAD_P_UTF8 (p + i))
317           return -1;
318       p += n;
319       nchars++;
320     }
321   return nchars;
322 }
323
324 static int
325 count_utf_16_chars (void *data, int nitems, int swap)
326 {
327   unsigned short *p = (unsigned short *) data;
328   unsigned short *pend = p + nitems;
329   int nchars = 0;
330
331   while (p < pend)
332     {
333       unsigned b;
334
335       for (; p < pend; nchars++, p++)
336         {
337           b = swap ? *p & 0xFF : *p >> 8;
338
339           if (b >= 0xD8 && b < 0xE0)
340             {
341               if (b >= 0xDC)
342                 return -1;
343               break;
344             }
345         }
346       if (p == pend)
347         break;
348       if (p + 1 == pend)
349         return -1;
350       p++;
351       b = swap ? *p & 0xFF : *p >> 8;
352       if (b < 0xDC || b >= 0xE0)
353         return -1;
354       nchars++;
355       p++;
356     }
357
358   return nchars;
359 }
360
361
362 static int
363 find_char_forward (MText *mt, int from, int to, int c)
364 {
365   int from_byte = POS_CHAR_TO_BYTE (mt, from);
366
367   if (mt->format <= MTEXT_FORMAT_UTF_8)
368     {
369       unsigned char *p = mt->data + from_byte;
370
371       while (from < to && STRING_CHAR_ADVANCE_UTF8 (p) != c) from++;
372     }
373   else if (mt->format <= MTEXT_FORMAT_UTF_16LE)
374     {
375       unsigned short *p = (unsigned short *) (mt->data) + from_byte;
376
377       if (mt->format == default_utf_16)
378         {
379           unsigned short *p = (unsigned short *) (mt->data) + from_byte;
380
381           while (from < to && STRING_CHAR_ADVANCE_UTF16 (p) != c) from++;
382         }
383       else if (c < 0x10000)
384         {
385           c = SWAP_16 (c);
386           while (from < to && *p != c)
387             {
388               from++;
389               p += ((*p & 0xFF) < 0xD8 || (*p & 0xFF) >= 0xE0) ? 1 : 2;
390             }
391         }
392       else if (c < 0x110000)
393         {
394           int c1 = (c >> 10) + 0xD800;
395           int c2 = (c & 0x3FF) + 0xDC00;
396
397           c1 = SWAP_16 (c1);
398           c2 = SWAP_16 (c2);
399           while (from < to && (*p != c1 || p[1] != c2))
400             {
401               from++;
402               p += ((*p & 0xFF) < 0xD8 || (*p & 0xFF) >= 0xE0) ? 1 : 2;
403             }
404         }
405     }
406   else if (c < 0x110000)
407     {
408       unsigned *p = (unsigned *) (mt->data) + from_byte;
409       unsigned c1 = c;
410
411       if (mt->format != default_utf_32)
412         c1 = SWAP_32 (c1);
413       while (from < to && *p++ != c1) from++;
414     }
415
416   return (from < to ? from : -1);
417 }
418
419
420 static int
421 find_char_backward (MText *mt, int from, int to, int c)
422 {
423   int to_byte = POS_CHAR_TO_BYTE (mt, to);
424
425   if (mt->format <= MTEXT_FORMAT_UTF_8)
426     {
427       unsigned char *p = mt->data + to_byte;
428
429       while (from < to)
430         {
431           for (p--; ! CHAR_HEAD_P (p); p--);
432           if (c == STRING_CHAR (p))
433             break;
434           to--;
435         }
436     }
437   else if (mt->format <= MTEXT_FORMAT_UTF_16LE)
438     {
439       unsigned short *p = (unsigned short *) (mt->data) + to_byte;
440
441       if (mt->format == default_utf_16)
442         {
443           while (from < to)
444             {
445               p--;
446               if (*p >= 0xDC00 && *p < 0xE000)
447                 p--;
448               if (c == STRING_CHAR_UTF16 (p))
449                 break;
450               to--;
451             }
452         }
453       else if (c < 0x10000)
454         {
455           c = SWAP_16 (c);
456           while (from < to && p[-1] != c)
457             {
458               to--;
459               p -= ((p[-1] & 0xFF) < 0xD8 || (p[-1] & 0xFF) >= 0xE0) ? 1 : 2;
460             }
461         }
462       else if (c < 0x110000)
463         {
464           int c1 = (c >> 10) + 0xD800;
465           int c2 = (c & 0x3FF) + 0xDC00;
466
467           c1 = SWAP_32 (c1);
468           c2 = SWAP_32 (c2);
469           while (from < to && (p[-1] != c2 || p[-2] != c1))
470             {
471               to--;
472               p -= ((p[-1] & 0xFF) < 0xD8 || (p[-1] & 0xFF) >= 0xE0) ? 1 : 2;
473             }
474         }
475     }
476   else if (c < 0x110000)
477     {
478       unsigned *p = (unsigned *) (mt->data) + to_byte;
479       unsigned c1 = c;
480
481       if (mt->format != default_utf_32)
482         c1 = SWAP_32 (c1);
483       while (from < to && p[-1] != c1) to--, p--;
484     }
485
486   return (from < to ? to - 1 : -1);
487 }
488
489
490 static void
491 free_mtext (void *object)
492 {
493   MText *mt = (MText *) object;
494
495   if (mt->plist)
496     mtext__free_plist (mt);
497   if (mt->data && mt->allocated >= 0)
498     free (mt->data);
499   M17N_OBJECT_UNREGISTER (mtext_table, mt);
500   free (object);
501 }
502
503 /** Structure for an iterator used in case-fold comparison.  */
504
505 struct casecmp_iterator {
506   MText *mt;
507   int pos;
508   MText *folded;
509   unsigned char *foldedp;
510   int folded_len;
511 };
512
513 static int
514 next_char_from_it (struct casecmp_iterator *it)
515 {
516   int c, c1;
517
518   if (it->folded)
519     {
520       c = STRING_CHAR_AND_BYTES (it->foldedp, it->folded_len);
521       return c;
522     }
523
524   c = mtext_ref_char (it->mt, it->pos);
525   c1 = (int) mchar_get_prop (c, Msimple_case_folding);
526   if (c1 == 0xFFFF)
527     {
528       it->folded
529         = (MText *) mchar_get_prop (c, Mcomplicated_case_folding);
530       it->foldedp = it->folded->data;
531       c = STRING_CHAR_AND_BYTES (it->foldedp, it->folded_len);
532       return c;
533     }
534
535   if (c1 >= 0)
536     c = c1;
537   return c;
538 }
539
540 static void
541 advance_it (struct casecmp_iterator *it)
542 {
543   if (it->folded)
544     {
545       it->foldedp += it->folded_len;
546       if (it->foldedp == it->folded->data + it->folded->nbytes)
547         it->folded = NULL;
548     }
549   if (! it->folded)
550     {
551       it->pos++;
552     }
553 }
554
555 static int
556 case_compare (MText *mt1, int from1, int to1, MText *mt2, int from2, int to2)
557 {
558   struct casecmp_iterator it1, it2;
559
560   it1.mt = mt1, it1.pos = from1, it1.folded = NULL;
561   it2.mt = mt2, it2.pos = from2, it2.folded = NULL;
562
563   while (it1.pos < to1 && it2.pos < to2)
564     {
565       int c1 = next_char_from_it (&it1);
566       int c2 = next_char_from_it (&it2);
567
568       if (c1 != c2)
569         return (c1 > c2 ? 1 : -1);
570       advance_it (&it1);
571       advance_it (&it2);
572     }
573   return (it2.pos == to2 ? (it1.pos < to1) : -1);
574 }
575
576 \f
577 /* Internal API */
578
579 int
580 mtext__init ()
581 {
582   M_charbag = msymbol_as_managing_key ("  charbag");
583   mtext_table.count = 0;
584   return 0;
585 }
586
587
588 void
589 mtext__fini (void)
590 {
591   mdebug__report_object ("M-text", &mtext_table);
592 }
593
594
595 int
596 mtext__char_to_byte (MText *mt, int pos)
597 {
598   int char_pos, byte_pos;
599   int forward;
600
601   if (pos < mt->cache_char_pos)
602     {
603       if (mt->cache_char_pos == mt->cache_byte_pos)
604         return pos;
605       if (pos < mt->cache_char_pos - pos)
606         {
607           char_pos = byte_pos = 0;
608           forward = 1;
609         }
610       else
611         {
612           char_pos = mt->cache_char_pos;
613           byte_pos = mt->cache_byte_pos;
614           forward = 0;
615         }
616     }
617   else
618     {
619       if (mt->nchars - mt->cache_char_pos == mt->nbytes - mt->cache_byte_pos)
620         return (mt->cache_byte_pos + (pos - mt->cache_char_pos));
621       if (pos - mt->cache_char_pos < mt->nchars - pos)
622         {
623           char_pos = mt->cache_char_pos;
624           byte_pos = mt->cache_byte_pos;
625           forward = 1;
626         }
627       else
628         {
629           char_pos = mt->nchars;
630           byte_pos = mt->nbytes;
631           forward = 0;
632         }
633     }
634   if (forward)
635     while (char_pos < pos)
636       INC_POSITION (mt, char_pos, byte_pos);
637   else
638     while (char_pos > pos)
639       DEC_POSITION (mt, char_pos, byte_pos);
640   mt->cache_char_pos = char_pos;
641   mt->cache_byte_pos = byte_pos;
642   return byte_pos;
643 }
644
645 /* mtext__byte_to_char () */
646
647 int
648 mtext__byte_to_char (MText *mt, int pos_byte)
649 {
650   int char_pos, byte_pos;
651   int forward;
652
653   if (pos_byte < mt->cache_byte_pos)
654     {
655       if (mt->cache_char_pos == mt->cache_byte_pos)
656         return pos_byte;
657       if (pos_byte < mt->cache_byte_pos - pos_byte)
658         {
659           char_pos = byte_pos = 0;
660           forward = 1;
661         }
662       else
663         {
664           char_pos = mt->cache_char_pos;
665           byte_pos = mt->cache_byte_pos;
666           forward = 0;
667         }
668     }
669   else
670     {
671       if (mt->nchars - mt->cache_char_pos == mt->nbytes - mt->cache_byte_pos)
672         return (mt->cache_char_pos + (pos_byte - mt->cache_byte_pos));
673       if (pos_byte - mt->cache_byte_pos < mt->nbytes - pos_byte)
674         {
675           char_pos = mt->cache_char_pos;
676           byte_pos = mt->cache_byte_pos;
677           forward = 1;
678         }
679       else
680         {
681           char_pos = mt->nchars;
682           byte_pos = mt->nbytes;
683           forward = 0;
684         }
685     }
686   if (forward)
687     while (byte_pos < pos_byte)
688       INC_POSITION (mt, char_pos, byte_pos);
689   else
690     while (byte_pos > pos_byte)
691       DEC_POSITION (mt, char_pos, byte_pos);
692   mt->cache_char_pos = char_pos;
693   mt->cache_byte_pos = byte_pos;
694   return char_pos;
695 }
696
697 /* Estimated extra bytes that malloc will use for its own purpose on
698    each memory allocation.  */
699 #define MALLOC_OVERHEAD 4
700 #define MALLOC_MININUM_BYTES 12
701
702 void
703 mtext__enlarge (MText *mt, int nbytes)
704 {
705   nbytes += MAX_UTF8_CHAR_BYTES;
706   if (mt->allocated >= nbytes)
707     return;
708   if (nbytes < MALLOC_MININUM_BYTES)
709     nbytes = MALLOC_MININUM_BYTES;
710   while (mt->allocated < nbytes)
711     mt->allocated = mt->allocated * 2 + MALLOC_OVERHEAD;
712   MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT);
713 }
714
715 int
716 mtext__takein (MText *mt, int nchars, int nbytes)
717 {
718   if (mt->plist)
719     mtext__adjust_plist_for_insert (mt, mt->nchars, nchars, NULL);
720   mt->nchars += nchars;
721   mt->nbytes += nbytes;
722   mt->data[mt->nbytes] = 0;
723   return 0;
724 }
725
726
727 int
728 mtext__cat_data (MText *mt, unsigned char *p, int nbytes,
729                  enum MTextFormat format)
730 {
731   int nchars = -1;
732
733   if (mt->format > MTEXT_FORMAT_UTF_8)
734     MERROR (MERROR_MTEXT, -1);
735   if (format == MTEXT_FORMAT_US_ASCII)
736     nchars = nbytes;
737   else if (format == MTEXT_FORMAT_UTF_8)
738     nchars = count_utf_8_chars (p, nbytes);
739   if (nchars < 0)
740     MERROR (MERROR_MTEXT, -1);
741   mtext__enlarge (mt, mtext_nbytes (mt) + nbytes + 1);
742   memcpy (MTEXT_DATA (mt) + mtext_nbytes (mt), p, nbytes);
743   mtext__takein (mt, nchars, nbytes);
744   return nchars;
745 }
746
747 MText *
748 mtext__from_data (void *data, int nitems, enum MTextFormat format,
749                   int need_copy)
750 {
751   MText *mt;
752   int nchars = nitems;
753   int bytes = nitems;
754
755   if (format == MTEXT_FORMAT_US_ASCII)
756     {
757       char *p = (char *) data, *pend = p + nitems;
758
759       while (p < pend)
760         if (*p++ < 0)
761           MERROR (MERROR_MTEXT, NULL);
762     }
763   else if (format == MTEXT_FORMAT_UTF_8)
764     {
765       if ((nchars = count_utf_8_chars (data, nitems)) < 0)
766         MERROR (MERROR_MTEXT, NULL);
767     }
768   else if (format <= MTEXT_FORMAT_UTF_16BE)
769     {
770       if ((nchars = count_utf_16_chars (data, nitems,
771                                         format != default_utf_16)) < 0)
772         MERROR (MERROR_MTEXT, NULL);
773       bytes = sizeof (short) * nitems;
774     }
775   else if (format <= MTEXT_FORMAT_UTF_32BE)
776     {
777       unsigned *p = (unsigned *) data, *pend = p + nitems;
778       int swap = format != default_utf_32;
779
780       for (; p < pend; p++)
781         {
782           unsigned c = swap ? SWAP_32 (*p) : *p;
783
784           if ((c >= 0xD800 && c < 0xE000) || (c >= 0x110000))
785             MERROR (MERROR_MTEXT, NULL);
786         }
787       bytes = sizeof (unsigned) * nitems;
788     }
789   else
790     MERROR (MERROR_MTEXT, NULL);
791
792   mt = mtext ();
793   mt->format = format;
794   mt->allocated = need_copy ? bytes : -1;
795   mt->nchars = nchars;
796   mt->nbytes = nitems;
797   if (need_copy)
798     {
799       mt->data = malloc (bytes + 1);
800       memcpy (mt->data, data, bytes);
801       mt->data[bytes] = 0;
802     }
803   else
804     mt->data = (unsigned char *) data;
805   return mt;
806 }
807
808
809 /* Not yet implemented.  */
810
811 int
812 mtext__adjust_format (MText *mt, enum MTextFormat format)
813 {
814   if (mt->format == format)
815     return 0;
816   if (mt->format == MTEXT_FORMAT_US_ASCII)
817     {
818       if (format == MTEXT_FORMAT_UTF_8)
819         mt->format = MTEXT_FORMAT_UTF_8;
820       MERROR (MERROR_MTEXT, -1);
821     }
822   else if (mt->format == MTEXT_FORMAT_UTF_8)
823     {
824       MERROR (MERROR_MTEXT, -1);
825     }
826   else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
827     {
828       MERROR (MERROR_MTEXT, -1);
829     }
830   else
831     {
832       MERROR (MERROR_MTEXT, -1);
833     }
834   return 0;
835 }
836
837
838 int
839 mtext__replace (MText *mt, int from, int to, char *from_str, char *to_str)
840 {
841   int from_byte = POS_CHAR_TO_BYTE (mt, from);
842   int to_byte = POS_CHAR_TO_BYTE (mt, to);
843   unsigned char *p = MTEXT_DATA (mt) + from_byte;
844   unsigned char *endp = MTEXT_DATA (mt) + to_byte;
845   int from_str_len = strlen (from_str);
846   int to_str_len = strlen (to_str);
847   int diff = to_str_len - from_str_len;
848   unsigned char saved_byte;
849   int pos, pos_byte;
850
851   if (mtext_nchars (mt) == 0
852       || from_str_len == 0)
853     return 0;
854   M_CHECK_READONLY (mt, -1);
855   M_CHECK_RANGE (mt, from, to, -1, 0);
856
857   saved_byte = *endp;
858   *endp = '\0';
859   while ((p = (unsigned char *) strstr ((char *) p, from_str)) != NULL)
860     {
861       if (diff < 0)
862         {
863           pos_byte = p - MTEXT_DATA (mt);
864           pos = POS_BYTE_TO_CHAR (mt, pos_byte);
865           mtext_del (mt, pos, pos - diff);
866         }
867       else if (diff > 0)
868         {
869           pos_byte = p - MTEXT_DATA (mt);
870           pos = POS_BYTE_TO_CHAR (mt, pos_byte);
871           mtext_ins_char (mt, pos, ' ', diff);
872           /* The above may relocate mt->data.  */
873           endp += (MTEXT_DATA (mt) + pos_byte) - p;
874           p = MTEXT_DATA (mt) + pos_byte;
875         }
876       memmove (p, to_str, to_str_len);
877       p += to_str_len;
878       endp += diff;
879     }
880   *endp = saved_byte;
881   return 0;
882 }
883
884
885 /* Find the position of a character at the beginning of a line of
886    M-Text MT searching backward from POS.  */
887
888 int
889 mtext__bol (MText *mt, int pos)
890 {
891   int byte_pos;
892
893   if (pos == 0)
894     return pos;
895   byte_pos = POS_CHAR_TO_BYTE (mt, pos);
896   if (mt->format <= MTEXT_FORMAT_UTF_8)
897     {
898       unsigned char *p = mt->data + byte_pos;
899
900       if (p[-1] == '\n')
901         return pos;
902       p--;
903       while (p > mt->data && p[-1] != '\n')
904         p--;
905       if (p == mt->data)
906         return 0;
907       byte_pos = p - mt->data;
908       return POS_BYTE_TO_CHAR (mt, byte_pos);
909     }
910   else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
911     {
912       unsigned short *p = ((unsigned short *) (mt->data)) + byte_pos;
913       unsigned short newline = mt->format == default_utf_16 ? 0x0A00 : 0x000A;
914
915       if (p[-1] == newline)
916         return pos;
917       p--;
918       while (p > (unsigned short *) (mt->data) && p[-1] != newline)
919         p--;
920       if (p == (unsigned short *) (mt->data))
921         return 0;
922       byte_pos = p - (unsigned short *) (mt->data);
923       return POS_BYTE_TO_CHAR (mt, byte_pos);;
924     }
925   else
926     {
927       unsigned *p = ((unsigned *) (mt->data)) + byte_pos;
928       unsigned newline = mt->format == default_utf_32 ? 0x0A000000 : 0x0000000A;
929
930       if (p[-1] == newline)
931         return pos;
932       p--, pos--;
933       while (p > (unsigned *) (mt->data) && p[-1] != newline)
934         p--, pos--;
935       return pos;
936     }
937 }
938
939
940 /* Find the position of a character at the end of a line of M-Text MT
941    searching forward from POS.  */
942
943 int
944 mtext__eol (MText *mt, int pos)
945 {
946   int byte_pos;
947
948   if (pos == mt->nchars)
949     return pos;
950   byte_pos = POS_CHAR_TO_BYTE (mt, pos);
951   if (mt->format <= MTEXT_FORMAT_UTF_8)
952     {
953       unsigned char *p = mt->data + byte_pos;
954       unsigned char *endp;
955
956       if (*p == '\n')
957         return pos + 1;
958       p++;
959       endp = mt->data + mt->nbytes;
960       while (p < endp && *p != '\n')
961         p++;
962       if (p == endp)
963         return mt->nchars;
964       byte_pos = p + 1 - mt->data;
965       return POS_BYTE_TO_CHAR (mt, byte_pos);
966     }
967   else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
968     {
969       unsigned short *p = ((unsigned short *) (mt->data)) + byte_pos;
970       unsigned short *endp;
971       unsigned short newline = mt->format == default_utf_16 ? 0x0A00 : 0x000A;
972
973       if (*p == newline)
974         return pos + 1;
975       p++;
976       endp = (unsigned short *) (mt->data) + mt->nbytes;
977       while (p < endp && *p != newline)
978         p++;
979       if (p == endp)
980         return mt->nchars;
981       byte_pos = p + 1 - (unsigned short *) (mt->data);
982       return POS_BYTE_TO_CHAR (mt, byte_pos);
983     }
984   else
985     {
986       unsigned *p = ((unsigned *) (mt->data)) + byte_pos;
987       unsigned *endp;
988       unsigned newline = mt->format == default_utf_32 ? 0x0A000000 : 0x0000000A;
989
990       if (*p == newline)
991         return pos + 1;
992       p++, pos++;
993       endp = (unsigned *) (mt->data) + mt->nbytes;
994       while (p < endp && *p != newline)
995         p++, pos++;
996       return pos;
997     }
998 }
999
1000 /*** @} */
1001 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1002
1003 \f
1004 /* External API */
1005
1006 /*** @addtogroup m17nMtext */
1007 /*** @{ */
1008 /*=*/
1009
1010 /***en
1011     @brief Allocate a new M-text.
1012
1013     The mtext () function allocates a new M-text of length 0 and
1014     returns a pointer to it.  The allocated M-text will not be freed
1015     unless the user explicitly does so with the m17n_object_free ()
1016     function.  */
1017
1018 /***ja
1019     @brief ¿·¤·¤¤M-text¤ò³ä¤êÅö¤Æ¤ë.
1020
1021     ´Ø¿ô mtext () ¤Ï¡¢Ä¹¤µ 0 ¤Î¿·¤·¤¤ M-text ¤ò³ä¤êÅö¤Æ¡¢¤½¤ì¤Ø¤Î¥Ý¥¤
1022     ¥ó¥¿¤òÊÖ¤¹¡£³ä¤êÅö¤Æ¤é¤ì¤¿ M-text ¤Ï¡¢´Ø¿ô m17n_object_free () ¤Ë
1023     ¤è¤Ã¤Æ¥æ¡¼¥¶¤¬ÌÀ¼¨Åª¤Ë¹Ô¤Ê¤ï¤Ê¤¤¸Â¤ê¡¢²òÊü¤µ¤ì¤Ê¤¤¡£
1024
1025     @latexonly \IPAlabel{mtext} @endlatexonly  */
1026
1027 /***
1028     @seealso
1029     m17n_object_free ()  */
1030
1031 MText *
1032 mtext ()
1033 {
1034   MText *mt;
1035
1036   M17N_OBJECT (mt, free_mtext, MERROR_MTEXT);
1037   mt->format = MTEXT_FORMAT_UTF_8;
1038   M17N_OBJECT_REGISTER (mtext_table, mt);
1039   return mt;
1040 }
1041
1042 /***en
1043     @brief Allocate a new M-text with specified data.
1044
1045     The mtext_from_data () function allocates a new M-text whose
1046     character sequence is specified by array $DATA of $NITEMS
1047     elements.  $FORMAT specifies the format of $DATA.
1048
1049     When $FORMAT is either #MTEXT_FORMAT_US_ASCII or
1050     #MTEXT_FORMAT_UTF_8, the contents of $DATA must be of the type @c
1051     unsigned @c char, and $NITEMS counts by byte.
1052
1053     When $FORMAT is either #MTEXT_FORMAT_UTF_16LE or
1054     #MTEXT_FORMAT_UTF_16BE, the contents of $DATA must be of the type
1055     @c unsigned @c short, and $NITEMS counts by unsigned short.
1056
1057     When $FORMAT is either #MTEXT_FORMAT_UTF_32LE or
1058     #MTEXT_FORMAT_UTF_32BE, the contents of $DATA must be of the type
1059     @c unsigned, and $NITEMS counts by unsigned.
1060
1061     The character sequence of the M-text is not modifiable.  
1062     The contents of $DATA must not be modified while the M-text is alive.
1063
1064     The allocated M-text will not be freed unless the user explicitly
1065     does so with the m17n_object_free () function.  Even in that case,
1066     $DATA is not freed.
1067
1068     @return
1069     If the operation was successful, mtext_from_data () returns a
1070     pointer to the allocated M-text.  Otherwise it returns @c NULL and
1071     assigns an error code to the external variable #merror_code.  */
1072 /***ja
1073     @brief »ØÄê¤Î¥Ç¡¼¥¿¤ò¸µ¤Ë¿·¤·¤¤ M-text ¤ò³ä¤êÅö¤Æ¤ë.
1074
1075     ´Ø¿ô mtext_from_data () ¤Ï¡¢Í×ÁÇ¿ô $NITEMS ¤ÎÇÛÎó $DATA ¤Ç»ØÄꤵ¤ì
1076     ¤¿Ê¸»úÎó¤ò»ý¤Ä¿·¤·¤¤ M-text ¤ò³ä¤êÅö¤Æ¤ë¡£$FORMAT ¤Ï $DATA ¤Î¥Õ¥©¡¼
1077     ¥Þ¥Ã¥È¤ò¼¨¤¹¡£
1078
1079     $FORMAT ¤¬ #MTEXT_FORMAT_US_ASCII ¤« #MTEXT_FORMAT_UTF_8 ¤Ê¤é¤Ð¡¢
1080     $DATA ¤ÎÆâÍƤϠ@c unsigned @c char ·¿¤Ç¤¢¤ê¡¢$NITEMS ¤Ï¥Ð¥¤¥Èñ°Ì
1081     ¤Çɽ¤µ¤ì¤Æ¤¤¤ë¡£
1082
1083     $FORMAT ¤¬ #MTEXT_FORMAT_UTF_16LE ¤« #MTEXT_FORMAT_UTF_16BE ¤Ê¤é¤Ð¡¢
1084     $DATA ¤ÎÆâÍƤϠ@c unsigned @c short ·¿¤Ç¤¢¤ê¡¢$NITEMS ¤Ï unsigned
1085     short Ã±°Ì¤Ç¤¢¤ë¡£
1086
1087     $FORMAT ¤¬ #MTEXT_FORMAT_UTF_32LE ¤« #MTEXT_FORMAT_UTF_32BE ¤Ê¤é¤Ð¡¢
1088     $DATA ¤ÎÆâÍƤÏ@c unsigned ·¿¤Ç¤¢¤ê¡¢$NITEMS ¤Ï unsigned Ã±°Ì¤Ç¤¢¤ë¡£
1089
1090     ³ä¤êÅö¤Æ¤é¤ì¤¿ M-text ¤Îʸ»úÎó¤ÏÊѹ¹¤Ç¤­¤Ê¤¤¡£$DATA ¤ÎÆâÍƤϠ
1091     M-text ¤¬Í­¸ú¤Ê´Ö¤ÏÊѹ¹¤·¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1092
1093     ³ä¤êÅö¤Æ¤é¤ì¤¿ M-text ¤Ï¡¢´Ø¿ô m17n_object_free () ¤Ë¤è¤Ã¤Æ¥æ¡¼¥¶
1094     ¤¬ÌÀ¼¨Åª¤Ë¹Ô¤Ê¤ï¤Ê¤¤¸Â¤ê¡¢²òÊü¤µ¤ì¤Ê¤¤¡£¤½¤Î¾ì¹ç¤Ç¤â $DATA ¤Ï²òÊü
1095     ¤µ¤ì¤Ê¤¤¡£
1096
1097     @return 
1098     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_from_data () ¤Ï³ä¤êÅö¤Æ¤é¤ì¤¿M-text ¤Ø¤Î¥Ý
1099     ¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·³°ÉôÊÑ¿ô #merror_code ¤Ë
1100     ¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
1101
1102 /***
1103     @errors
1104     @c MERROR_MTEXT  */
1105
1106 MText *
1107 mtext_from_data (void *data, int nitems, enum MTextFormat format)
1108 {
1109   if (nitems < 0)
1110     MERROR (MERROR_MTEXT, NULL);
1111   if (nitems == 0)
1112     {
1113       if (format == MTEXT_FORMAT_US_ASCII
1114           || format == MTEXT_FORMAT_UTF_8)
1115         {
1116           unsigned char *p = data;
1117
1118           while (*p++) nitems++;
1119         }
1120       else if (format <= MTEXT_FORMAT_UTF_16BE)
1121         {
1122           unsigned short *p = data;
1123
1124           while (*p++) nitems++;
1125         }
1126       else if (format <= MTEXT_FORMAT_UTF_32BE)
1127         {
1128           unsigned *p = data;
1129
1130           while (*p++) nitems++;
1131         }
1132       else
1133         MERROR (MERROR_MTEXT, NULL);
1134     }
1135   return mtext__from_data (data, nitems, format, 0);
1136 }
1137
1138 /*=*/
1139
1140 /***en
1141     @brief Number of characters in M-text.
1142
1143     The mtext_len () function returns the number of characters in
1144     M-text $MT.  */
1145
1146 /***ja
1147     @brief M-text Ãæ¤Îʸ»ú¤Î¿ô.
1148
1149     ´Ø¿ô mtext_len () ¤Ï M-text $MT Ãæ¤Îʸ»ú¤Î¿ô¤òÊÖ¤¹¡£
1150
1151     @latexonly \IPAlabel{mtext_len} @endlatexonly  */
1152
1153 int
1154 mtext_len (MText *mt)
1155 {
1156   return (mt->nchars);
1157 }
1158
1159 /*=*/
1160
1161 /***en
1162     @brief Return the character at the specified position in an M-text.
1163
1164     The mtext_ref_char () function returns the character at $POS in
1165     M-text $MT.  If an error is detected, it returns -1 and assigns an
1166     error code to the external variable #merror_code.  */
1167
1168 /***ja
1169     @brief M-text Ãæ¤Î»ØÄꤵ¤ì¤¿°ÌÃÖ¤Îʸ»ú¤òÊÖ¤¹.
1170
1171     ´Ø¿ô mtext_ref_char () ¤Ï¡¢M-text $MT ¤Î°ÌÃÖ $POS ¤Îʸ»ú¤òÊÖ¤¹¡£
1172     ¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code
1173     ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1174
1175     @latexonly \IPAlabel{mtext_ref_char} @endlatexonly  */
1176
1177 /***
1178     @errors
1179     @c MERROR_RANGE  */
1180
1181 int
1182 mtext_ref_char (MText *mt, int pos)
1183 {
1184   int c;
1185
1186   M_CHECK_POS (mt, pos, -1);
1187   if (mt->format <= MTEXT_FORMAT_UTF_8)
1188     {
1189       unsigned char *p = mt->data + POS_CHAR_TO_BYTE (mt, pos);
1190
1191       c = STRING_CHAR (p);
1192     }
1193   else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
1194     {
1195       unsigned short *p
1196         = (unsigned short *) (mt->data) + POS_CHAR_TO_BYTE (mt, pos);
1197
1198       if (mt->format == default_utf_16)
1199         c = STRING_CHAR_UTF16 (p);
1200       else
1201         {
1202           c = (*p >> 8) | ((*p & 0xFF) << 8);
1203           if (c >= 0xD800 && c < 0xE000)
1204             {
1205               int c1 = (p[1] >> 8) | ((p[1] & 0xFF) << 8);
1206               c = ((c - 0xD800) << 10) + (c1 - 0xDC00) + 0x10000;
1207             }
1208         }
1209     }
1210   else
1211     {
1212       unsigned *p = (unsigned *) (mt->data) + POS_CHAR_TO_BYTE (mt, pos);
1213
1214       if (mt->format == default_utf_32)
1215         c = *p;
1216       else
1217         c = SWAP_32 (*p);
1218     }
1219   return c;
1220 }
1221
1222 /*=*/
1223
1224 /***en
1225     @brief Store a character into an M-text.
1226
1227     The mtext_set_char () function sets character $C, which has no
1228     text properties, at $POS in M-text $MT.
1229
1230     @return
1231     If the operation was successful, mtext_set_char () returns 0.
1232     Otherwise it returns -1 and assigns an error code to the external
1233     variable #merror_code.  */
1234
1235 /***ja
1236     @brief M-text ¤Ë°ìʸ»ú¤òÀßÄꤹ¤ë.
1237
1238     ´Ø¿ô mtext_set_char () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£Ìµ¤·¤Îʸ»ú $C ¤ò 
1239     M-text $MT ¤Î°ÌÃÖ $POS ¤ËÀßÄꤹ¤ë¡£
1240
1241     @return
1242     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mtext_set_char () ¤Ï 0 ¤òÊÖ¤¹¡£¼ºÇÔ¤¹¤ì¤Ð -1 ¤òÊÖ
1243     ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1244
1245     @latexonly \IPAlabel{mtext_set_char} @endlatexonly  */
1246
1247 /***
1248     @errors
1249     @c MERROR_RANGE */
1250
1251 int
1252 mtext_set_char (MText *mt, int pos, int c)
1253 {
1254   int byte_pos;
1255   int bytes_old, bytes_new;
1256   int delta;
1257   unsigned char str[MAX_UTF8_CHAR_BYTES];
1258   unsigned char *p;
1259   int i;
1260
1261   M_CHECK_POS (mt, pos, -1);
1262   M_CHECK_READONLY (mt, -1);
1263
1264   byte_pos = POS_CHAR_TO_BYTE (mt, pos);
1265   p = mt->data + byte_pos;
1266   bytes_old = CHAR_BYTES_AT (p);
1267   bytes_new = CHAR_STRING (c, str);
1268   delta = bytes_new - bytes_old;
1269
1270   /* mtext__adjust_plist_for_change (mt, pos, pos + 1);*/
1271
1272   if (delta)
1273     {
1274       int byte_pos_old = byte_pos + bytes_old;
1275       int byte_pos_new = byte_pos + bytes_new;
1276
1277       if (mt->cache_char_pos > pos)
1278         mt->cache_byte_pos += delta;
1279
1280       if ((mt->allocated - mt->nbytes) <= delta)
1281         {
1282           mt->allocated = mt->nbytes + delta + 1;
1283           MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT);
1284         }
1285
1286       memmove (mt->data + byte_pos_old, mt->data + byte_pos_new,
1287                mt->nbytes - byte_pos_old);
1288       mt->nbytes += delta;
1289       mt->data[mt->nbytes] = 0;
1290     }
1291   for (i = 0; i < bytes_new; i++)
1292     mt->data[byte_pos + i] = str[i];
1293   return 0;
1294 }
1295
1296 /*=*/
1297
1298 /***en
1299     @brief  Append a character to an M-text.
1300
1301     The mtext_cat_char () function appends character $C, which has no
1302     text properties, to the end of M-text $MT.
1303
1304     @return
1305     This function returns a pointer to the resulting M-text $MT.  If
1306     $C is an invalid character, it returns @c NULL.  */
1307
1308 /***ja
1309     @brief M-text ¤Ë°ìʸ»úÄɲ乤ë.
1310
1311     ´Ø¿ô mtext_cat_char () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£Ìµ¤·¤Îʸ»ú $C ¤ò 
1312     M-text $MT ¤ÎËöÈø¤ËÄɲ乤롣
1313
1314     @return
1315     ¤³¤Î´Ø¿ô¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£$C ¤¬Àµ¤·¤¤Ê¸
1316     »ú¤Ç¤Ê¤¤¾ì¹ç¤Ë¤Ï @c NULL ¤òÊÖ¤¹¡£  */
1317
1318 /***
1319     @seealso
1320     mtext_cat (), mtext_ncat ()  */
1321
1322 MText *
1323 mtext_cat_char (MText *mt, int c)
1324 {
1325   unsigned char buf[MAX_UTF8_CHAR_BYTES];
1326   int nbytes;
1327   int total_bytes;
1328
1329   M_CHECK_READONLY (mt, NULL);
1330   if (c < 0 || c > MCHAR_MAX)
1331     return NULL;
1332   nbytes = CHAR_STRING (c, buf);
1333
1334   total_bytes = mt->nbytes + nbytes;
1335
1336   mtext__adjust_plist_for_insert (mt, mt->nchars, 1, NULL);
1337
1338   if (total_bytes >= mt->allocated)
1339     {
1340       mt->allocated = total_bytes + 1;
1341       MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT);
1342     }
1343   memcpy (mt->data + mt->nbytes, buf, nbytes);
1344   mt->nbytes = total_bytes;
1345   mt->nchars++;
1346   mt->data[total_bytes] = 0;
1347   return mt;
1348 }
1349
1350 /*=*/
1351
1352 /***en
1353     @brief  Create a copy of an M-text.
1354
1355     The mtext_dup () function creates a copy of M-text $MT while
1356     inheriting all the text properties of $MT.
1357
1358     @return
1359     This function returns a pointer to the created copy.  */
1360
1361 /***ja
1362     @brief M-text ¤Î¥³¥Ô¡¼¤òºî¤ë.
1363
1364     ´Ø¿ô mtext_dup () ¤Ï¡¢M-text $MT ¤Î¥³¥Ô¡¼¤òºî¤ë¡£$MT ¤Î¥Æ¥­¥¹¥È¥×
1365     ¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£
1366
1367     @return
1368     ¤³¤Î´Ø¿ô¤Ïºî¤é¤ì¤¿¥³¥Ô¡¼¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1369
1370      @latexonly \IPAlabel{mtext_dup} @endlatexonly  */
1371
1372 /***
1373     @seealso
1374     mtext_duplicate ()  */
1375
1376 MText *
1377 mtext_dup (MText *mt)
1378 {
1379   return copy (mtext (), 0, mt, 0, mt->nchars);
1380 }
1381
1382 /*=*/
1383
1384 /***en
1385     @brief  Append an M-text to another.
1386
1387     The mtext_cat () function appends M-text $MT2 to the end of M-text
1388     $MT1 while inheriting all the text properties.  $MT2 itself is not
1389     modified.
1390
1391     @return
1392     This function returns a pointer to the resulting M-text $MT1.  */
1393
1394 /***ja
1395     @brief 2¸Ä¤Î M-text¤òÏ¢·ë¤¹¤ë.
1396
1397     ´Ø¿ô mtext_cat () ¤Ï¡¢ M-text $MT2 ¤ò M-text $MT1 ¤ÎËöÈø¤ËÉÕ¤±²Ã¤¨
1398     ¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê
1399     ¤¤¡£
1400
1401     @return
1402     ¤³¤Î´Ø¿ô¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1403
1404     @latexonly \IPAlabel{mtext_cat} @endlatexonly  */
1405
1406 /***
1407     @seealso
1408     mtext_ncat (), mtext_cat_char ()  */
1409
1410 MText *
1411 mtext_cat (MText *mt1, MText *mt2)
1412 {
1413   M_CHECK_READONLY (mt1, NULL);
1414
1415   return copy (mt1, mt1->nchars, mt2, 0, mt2->nchars);
1416 }
1417
1418
1419 /*=*/
1420
1421 /***en
1422     @brief Append a part of an M-text to another.
1423
1424     The mtext_ncat () function appends the first $N characters of
1425     M-text $MT2 to the end of M-text $MT1 while inheriting all the
1426     text properties.  If the length of $MT2 is less than $N, all
1427     characters are copied.  $MT2 is not modified.  
1428
1429     @return
1430     If the operation was successful, mtext_ncat () returns a pointer
1431     to the resulting M-text $MT1.  If an error is detected, it returns
1432     @c NULL and assigns an error code to the global variable @c
1433     merror_code.  */
1434
1435
1436 /***ja
1437     @brief M-text ¤Î°ìÉô¤òÊ̤ΠM-text ¤ËÉղ乤ë.
1438
1439     ´Ø¿ô mtext_ncat () ¤Ï¡¢M-text $MT2 ¤Î¤Ï¤¸¤á¤Î $N Ê¸»ú¤ò M-text
1440     $MT1 ¤ÎËöÈø¤ËÉÕ¤±²Ã¤¨¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì
1441     ¤ë¡£$MT2 ¤ÎŤµ¤¬ $N °Ê²¼¤Ê¤é¤Ð¡¢$MT2 ¤Î¤¹¤Ù¤Æ¤Îʸ»ú¤¬Éղ䵤ì¤ë¡£
1442     $MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
1443
1444     @return
1445     ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mtext_ncat () ¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 ¤Ø¤Î¥Ý
1446     ¥¤¥ó¥¿¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c
1447     merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1448
1449     @latexonly \IPAlabel{mtext_ncat} @endlatexonly  */
1450
1451 /***
1452     @errors
1453     @c MERROR_RANGE
1454
1455     @seealso
1456     mtext_cat (), mtext_cat_char ()  */
1457
1458 MText *
1459 mtext_ncat (MText *mt1, MText *mt2, int n)
1460 {
1461   M_CHECK_READONLY (mt1, NULL);
1462   if (n < 0)
1463     MERROR (MERROR_RANGE, NULL);
1464   return copy (mt1, mt1->nchars, mt2, 0, mt2->nchars < n ? mt2->nchars : n);
1465 }
1466
1467
1468 /*=*/
1469
1470 /***en
1471     @brief Copy an M-text to another.
1472
1473     The mtext_cpy () function copies M-text $MT2 to M-text $MT1 while
1474     inheriting all the text properties.  The old text in $MT1 is
1475     overwritten and the length of $MT1 is extended if necessary.  $MT2
1476     is not modified.
1477
1478     @return
1479     This function returns a pointer to the resulting M-text $MT1.  */
1480
1481 /***ja
1482     @brief M-text ¤òÊ̤ΠM-text ¤Ë¥³¥Ô¡¼¤¹¤ë.
1483
1484     ´Ø¿ô mtext_cpy () ¤Ï M-text $MT2 ¤ò M-text $MT1 ¤Ë¾å½ñ¤­¥³¥Ô¡¼¤¹¤ë¡£
1485     $MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT1 ¤ÎŤµ¤ÏɬÍפ˱þ
1486     ¤¸¤Æ¿­¤Ð¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
1487
1488     @return
1489     ¤³¤Î´Ø¿ô¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1490
1491     @latexonly \IPAlabel{mtext_cpy} @endlatexonly  */
1492
1493 /***
1494     @seealso
1495     mtext_ncpy (), mtext_copy ()  */
1496
1497 MText *
1498 mtext_cpy (MText *mt1, MText *mt2)
1499 {
1500   M_CHECK_READONLY (mt1, NULL);
1501   return copy (mt1, 0, mt2, 0, mt2->nchars);
1502 }
1503
1504 /*=*/
1505
1506 /***en
1507     @brief Copy the first some characters in an M-text to another.
1508
1509     The mtext_ncpy () function copies the first $N characters of
1510     M-text $MT2 to M-text $MT1 while inheriting all the text
1511     properties.  If the length of $MT2 is less than $N, all characters
1512     of $MT2 are copied.  The old text in $MT1 is overwritten and the
1513     length of $MT1 is extended if necessary.  $MT2 is not modified.
1514
1515     @return
1516     If the operation was successful, mtext_ncpy () returns a pointer
1517     to the resulting M-text $MT1.  If an error is detected, it returns
1518     @c NULL and assigns an error code to the global variable @c
1519     merror_code.  */
1520
1521 /***ja
1522     @brief M-text ¤Ë´Þ¤Þ¤ì¤ëºÇ½é¤Î²¿Ê¸»ú¤«¤ò¥³¥Ô¡¼¤¹¤ë.
1523
1524     ´Ø¿ô mtext_ncpy () ¤Ï¡¢M-text $MT2 ¤ÎºÇ½é¤Î $N Ê¸»ú¤ò M-text $MT1 
1525     ¤Ë¾å½ñ¤­¥³¥Ô¡¼¤¹¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£
1526     ¤â¤· $MT2 ¤ÎŤµ¤¬ $N ¤è¤ê¤â¾®¤µ¤±¤ì¤Ð $MT2 ¤Î¤¹¤Ù¤Æ¤Îʸ»ú¤ò¥³¥Ô¡¼
1527     ¤¹¤ë¡£$MT1 ¤ÎŤµ¤ÏɬÍפ˱þ¤¸¤Æ¿­¤Ð¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
1528
1529     @return 
1530     ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mtext_ncpy () ¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 ¤Ø¤Î¥Ý
1531     ¥¤¥ó¥¿¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô @c
1532     merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1533
1534     @latexonly \IPAlabel{mtext_ncpy} @endlatexonly  */
1535
1536 /***
1537     @errors
1538     @c MERROR_RANGE
1539
1540     @seealso
1541     mtext_cpy (), mtext_copy ()  */
1542
1543 MText *
1544 mtext_ncpy (MText *mt1, MText *mt2, int n)
1545 {
1546   M_CHECK_READONLY (mt1, NULL);
1547   if (n < 0)
1548     MERROR (MERROR_RANGE, NULL);
1549   return (copy (mt1, 0, mt2, 0, mt2->nchars < n ? mt2->nchars : n));
1550 }
1551
1552 /*=*/
1553
1554 /***en
1555     @brief Create a new M-text from a part of an existing M-text.
1556
1557     The mtext_duplicate () function creates a copy of sub-text of
1558     M-text $MT, starting at $FROM (inclusive) and ending at $TO
1559     (exclusive) while inheriting all the text properties of $MT.  $MT
1560     itself is not modified.
1561
1562     @return
1563     If the operation was successful, mtext_duplicate () returns a
1564     pointer to the created M-text.  If an error is detected, it returns 0
1565     and assigns an error code to the external variable #merror_code.  */
1566
1567 /***ja
1568     @brief ´û¸¤Î M-text ¤Î°ìÉô¤«¤é¿·¤·¤¤ M-text ¤ò¤Ä¤¯¤ë.
1569
1570     ´Ø¿ô mtext_duplicate () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤à¡Ë¤«¤é $TO 
1571     ¡Ê´Þ¤Þ¤Ê¤¤¡Ë¤Þ¤Ç¤ÎÉôʬ¤Î¥³¥Ô¡¼¤òºî¤ë¡£¤³¤Î¤È¤­ $MT ¤Î¥Æ¥­¥¹¥È¥×¥í
1572     ¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT ¤½¤Î¤â¤Î¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
1573
1574     @return
1575     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_duplicate () ¤Ïºî¤é¤ì¤¿ M-text ¤Ø¤Î¥Ý¥¤¥ó
1576     ¥¿¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô 
1577     #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1578
1579     @latexonly \IPAlabel{mtext_duplicate} @endlatexonly  */
1580
1581 /***
1582     @errors
1583     @c MERROR_RANGE
1584
1585     @seealso
1586     mtext_dup ()  */
1587
1588 MText *
1589 mtext_duplicate (MText *mt, int from, int to)
1590 {
1591   MText *new = mtext ();
1592
1593   M_CHECK_RANGE (mt, from, to, NULL, new);
1594   return copy (new, 0, mt, from, to);
1595 }
1596
1597 /*=*/
1598
1599 /***en
1600     @brief Copy characters in the specified range into an M-text.
1601
1602     The mtext_copy () function copies the text between $FROM
1603     (inclusive) and $TO (exclusive) in M-text $MT2 to the region
1604     starting at $POS in M-text $MT1 while inheriting the text
1605     properties.  The old text in $MT1 is overwritten and the length of
1606     $MT1 is extended if necessary.  $MT2 is not modified.
1607
1608     @return
1609     If the operation was successful, mtext_copy () returns a pointer
1610     to the modified $MT1.  Otherwise, it returns @c NULL and assigns
1611     an error code to the external variable #merror_code.  */
1612
1613 /***ja
1614     @brief M-text ¤Î»ØÄêÈϰϤÎʸ»ú¤ò¥³¥Ô¡¼¤¹¤ë.
1615
1616     ´Ø¿ô mtext_copy () ¤Ï¡¢ M-text $MT2 ¤Î $FROM ¡Ê´Þ¤à¡Ë¤«¤é $TO ¡Ê´Þ
1617     ¤Þ¤Ê¤¤¡Ë¤Þ¤Ç¤ÎÈϰϤΥƥ­¥¹¥È¤ò M-text $MT1 ¤Î°ÌÃÖ $POS ¤«¤é¾å½ñ¤­
1618     ¥³¥Ô¡¼¤¹¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT1 ¤ÎĹ
1619     ¤µ¤ÏɬÍפ˱þ¤¸¤Æ¿­¤Ð¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
1620
1621     @latexonly \IPAlabel{mtext_copy} @endlatexonly
1622
1623     @return
1624     ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mtext_copy () ¤ÏÊѹ¹¤µ¤ì¤¿ $MT1 ¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò
1625     ÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼
1626     ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
1627
1628 /***
1629     @errors
1630     @c MERROR_RANGE
1631
1632     @seealso
1633     mtext_cpy (), mtext_ncpy ()  */
1634
1635 MText *
1636 mtext_copy (MText *mt1, int pos, MText *mt2, int from, int to)
1637 {
1638   M_CHECK_POS_X (mt1, pos, NULL);
1639   M_CHECK_READONLY (mt1, NULL);
1640   M_CHECK_RANGE (mt2, from, to, NULL, mt1);
1641   return copy (mt1, pos, mt2, from, to);
1642 }
1643
1644 /*=*/
1645
1646
1647 /***en
1648     @brief Delete characters in the specified range destructively.
1649
1650     The mtext_del () function deletes the characters in the range
1651     $FROM (inclusive) and $TO (exclusive) from M-text $MT
1652     destructively.  As a result, the length of $MT shrinks by ($TO -
1653     $FROM) characters.
1654
1655     @return
1656     If the operation was successful, mtext_del () returns 0.
1657     Otherwise, it returns -1 and assigns an error code to the external
1658     variable #merror_code.  */
1659
1660 /***ja
1661     @brief »ØÄêÈϰϤÎʸ»ú¤òÇ˲õŪ¤Ë¼è¤ê½ü¤¯.
1662
1663     ´Ø¿ô mtext_del () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê´Þ¤à¡Ë¤«¤é $TO ¡Ê´Þ¤Þ
1664     ¤Ê¤¤¡Ë¤Þ¤Ç¤Îʸ»ú¤òÇ˲õŪ¤Ë¼è¤ê½ü¤¯¡£·ë²ÌŪ¤Ë $MT ¤ÏŤµ¤¬ ($TO @c
1665     - $FROM) ¤À¤±½Ì¤à¤³¤È¤Ë¤Ê¤ë¡£
1666
1667     @return
1668     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_del () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ
1669     ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
1670
1671 /***
1672     @errors
1673     @c MERROR_RANGE
1674
1675     @seealso
1676     mtext_ins ()  */
1677
1678 int
1679 mtext_del (MText *mt, int from, int to)
1680 {
1681   int from_byte, to_byte;
1682
1683   M_CHECK_READONLY (mt, -1);
1684   M_CHECK_RANGE (mt, from, to, -1, 0);
1685
1686   from_byte = POS_CHAR_TO_BYTE (mt, from);
1687   to_byte = POS_CHAR_TO_BYTE (mt, to);
1688
1689   if (mt->cache_char_pos >= to)
1690     {
1691       mt->cache_char_pos -= to - from;
1692       mt->cache_byte_pos -= to_byte - from_byte;
1693     }
1694   else if (mt->cache_char_pos > from)
1695     {
1696       mt->cache_char_pos -= from;
1697       mt->cache_byte_pos -= from_byte;
1698     }
1699
1700   mtext__adjust_plist_for_delete (mt, from, to - from);
1701   memmove (mt->data + from_byte, mt->data + to_byte, mt->nbytes - to_byte + 1);
1702   mt->nchars -= (to - from);
1703   mt->nbytes -= (to_byte - from_byte);
1704   mt->cache_char_pos = from;
1705   mt->cache_byte_pos = from_byte;
1706   return 0;
1707 }
1708
1709
1710 /*=*/
1711
1712 /***en
1713     @brief Insert an M-text into another M-text.
1714
1715     The mtext_ins () function inserts M-text $MT2 into M-text $MT1, at
1716     position $POS.  As a result, $MT1 is lengthen by the length of
1717     $MT2.  On insertion, all the text properties of $MT2 are
1718     inherited.  The original $MT2 is not modified.
1719
1720     @return
1721     If the operation was successful, mtext_ins () returns 0.
1722     Otherwise, it returns -1 and assigns an error code to the external
1723     variable #merror_code.  */
1724
1725 /***ja
1726     @brief M-text ¤òÊ̤ΠM-text ¤ËÁÞÆþ¤¹¤ë.
1727
1728     ´Ø¿ô mtext_ins () ¤Ï M-text $MT1 ¤Î $POS ¤Î°ÌÃ֤ˠÊ̤ΠM-text $MT2 
1729     ¤òÁÞÆþ¤¹¤ë¡£¤³¤Î·ë²Ì $MT1 ¤ÎŤµ¤Ï $MT2 ¤ÎŤµÊ¬¤À¤±Áý¤¨¤ë¡£ÁÞÆþ¤Î
1730     ºÝ¡¢$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT2 ¤½¤Î¤â¤Î¤ÏÊÑ
1731     ¹¹¤µ¤ì¤Ê¤¤¡£
1732
1733     @return
1734     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_ins () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ
1735     ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
1736
1737 /***
1738     @errors
1739     @c MERROR_RANGE
1740
1741     @seealso
1742     mtext_del ()  */
1743
1744 int
1745 mtext_ins (MText *mt1, int pos, MText *mt2)
1746 {
1747   int byte_pos;
1748   int total_bytes;
1749
1750   M_CHECK_READONLY (mt1, -1);
1751   M_CHECK_POS_X (mt1, pos, -1);
1752
1753   if (mt2->nchars == 0)
1754     return 0;
1755   mtext__adjust_plist_for_insert
1756     (mt1, pos, mt2->nchars,
1757      mtext__copy_plist (mt2->plist, 0, mt2->nchars, mt1, pos));
1758
1759   total_bytes = mt1->nbytes + mt2->nbytes;
1760   if (total_bytes >= mt1->allocated)
1761     {
1762       mt1->allocated = total_bytes + 1;
1763       MTABLE_REALLOC (mt1->data, mt1->allocated, MERROR_MTEXT);
1764     }
1765   byte_pos = POS_CHAR_TO_BYTE (mt1, pos);
1766   if (mt1->cache_char_pos > pos)
1767     {
1768       mt1->cache_char_pos += mt2->nchars;
1769       mt1->cache_byte_pos += mt2->nbytes;
1770     }
1771   memmove (mt1->data + byte_pos + mt2->nbytes, mt1->data + byte_pos,
1772            mt1->nbytes - byte_pos + 1);
1773   memcpy (mt1->data + byte_pos, mt2->data, mt2->nbytes);
1774   mt1->nbytes += mt2->nbytes;
1775   mt1->nchars += mt2->nchars;
1776   return 0;
1777 }
1778
1779
1780 /*=*/
1781
1782 /***en
1783     @brief Insert a character into an M-text.
1784
1785     The mtext_ins_char () function inserts $N copy of character $C
1786     into M-text $MT at position $POS.  As a result, $MT is lengthen by
1787     $N.
1788
1789     @return
1790     If the operation was successful, mtext_ins () returns 0.
1791     Otherwise, it returns -1 and assigns an error code to the external
1792     variable #merror_code.  */
1793
1794 /***ja
1795     @brief M-text ¤Ëʸ»ú¤òÁÞÆþ¤¹¤ë.
1796
1797     ´Ø¿ô mtext_ins_char () ¤Ï M-text $MT ¤Î $POS ¤Î°ÌÃÖ¤Ëʸ»ú $C ¤ò $N
1798     ¸ÄÁÞÆþ¤¹¤ë¡£¤³¤Î·ë²Ì $MT1 ¤ÎŤµ¤Ï $N ¤À¤±Áý¤¨¤ë¡£
1799
1800     @return
1801     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_ins_char () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1
1802     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
1803
1804 /***
1805     @errors
1806     @c MERROR_RANGE
1807
1808     @seealso
1809     mtext_ins, mtext_del ()  */
1810
1811 int
1812 mtext_ins_char (MText *mt, int pos, int c, int n)
1813 {
1814   int byte_pos;
1815   int nbytes, total_bytes;
1816   unsigned char *buf;
1817   int i;
1818
1819   M_CHECK_READONLY (mt, -1);
1820   M_CHECK_POS_X (mt, pos, -1);
1821   if (c < 0 || c > MCHAR_MAX)
1822     MERROR (MERROR_MTEXT, -1);
1823   if (n <= 0)
1824     return 0;
1825   mtext__adjust_plist_for_insert (mt, pos, n, NULL);
1826   buf = alloca (MAX_UTF8_CHAR_BYTES * n);
1827   for (i = 0, nbytes = 0; i < n; i++)
1828     nbytes += CHAR_STRING (c, buf + nbytes);
1829   total_bytes = mt->nbytes + nbytes;
1830   if (total_bytes >= mt->allocated)
1831     {
1832       mt->allocated = total_bytes + 1;
1833       MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT);
1834     }
1835   byte_pos = POS_CHAR_TO_BYTE (mt, pos);
1836   if (mt->cache_char_pos > pos)
1837     {
1838       mt->cache_char_pos++;
1839       mt->cache_byte_pos += nbytes;
1840     }
1841   memmove (mt->data + byte_pos + nbytes, mt->data + byte_pos,
1842            mt->nbytes - byte_pos + 1);
1843   memcpy (mt->data + byte_pos, buf, nbytes);
1844   mt->nbytes += nbytes;
1845   mt->nchars += n;
1846   return 0;
1847 }
1848
1849 /*=*/
1850
1851 /***en
1852     @brief Search a character in an M-text.
1853
1854     The mtext_character () function searches M-text $MT for character
1855     $C.  If $FROM is less than $TO, search begins at position $FROM
1856     and goes forward but does not exceed ($TO - 1).  Otherwise, search
1857     begins at position ($FROM - 1) and goes backward but does not
1858     exceed $TO.  An invalid position specification is regarded as both
1859     $FROM and $TO being 0.
1860
1861     @return
1862     If $C is found, mtext_character () returns the position of its
1863     first occurrence.  Otherwise it returns -1 without changing the
1864     external variable #merror_code.  If an error is detected, it returns -1 and
1865     assigns an error code to the external variable #merror_code.  */
1866
1867 /***ja
1868     @brief M-text Ãæ¤Çʸ»ú¤òõ¤¹.
1869
1870     ´Ø¿ô mtext_character () ¤Ï M-text $MT Ãæ¤Çʸ»ú $C ¤òõ¤¹¡£¤â¤· 
1871     $FROM ¤¬ $TO ¤è¤ê¾®¤µ¤±¤ì¤Ð¡¢Ãµº÷¤Ï°ÌÃÖ $FROM ¤«¤éËöÈøÊý¸þ¤Ø¡¢ºÇÂç 
1872     ($TO - 1) ¤Þ¤Ç¿Ê¤à¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð°ÌÃÖ ($FROM - 1) ¤«¤éÀèƬÊý¸þ¤Ø¡¢
1873     ºÇÂç $TO ¤Þ¤Ç¿Ê¤à¡£°ÌÃ֤λØÄê¤Ë¸í¤ê¤¬¤¢¤ë¾ì¹ç¤Ï¡¢$FROM ¤È $TO ¤Îξ
1874     Êý¤Ë 0 ¤¬»ØÄꤵ¤ì¤¿¤â¤Î¤È¸«¤Ê¤¹¡£
1875
1876     @return
1877     ¤â¤· $C ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_character () ¤Ï¤½¤ÎºÇ½é¤Î½Ð¸½°ÌÃÖ¤òÊÖ
1878     ¤¹¡£¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï³°ÉôÊÑ¿ô #merror_code ¤òÊѹ¹¤»¤º¤Ë -1 ¤òÊÖ
1879     ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼
1880     ¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
1881
1882 /***
1883     @seealso
1884     mtext_chr(), mtext_rchr ()  */
1885
1886 int
1887 mtext_character (MText *mt, int from, int to, int c)
1888 {
1889   if (from < to)
1890     {
1891       /* We do not use M_CHECK_RANGE () because this function should
1892          not set merror_code.  */
1893       if (from < 0 || to > mt->nchars)
1894         return -1;
1895       return find_char_forward (mt, from, to, c);
1896     }
1897   else
1898     {
1899       /* ditto */
1900       if (to < 0 || from > mt->nchars)
1901         return -1;
1902       return find_char_backward (mt, to, from, c);
1903     }
1904 }
1905
1906
1907 /*=*/
1908
1909 /***en
1910     @brief Return the position of the first occurrence of a character in an M-text.
1911
1912     The mtext_chr () function searches M-text $MT for character $C.
1913     Search starts from the beginning of $MT and goes toward the end.
1914
1915     @return
1916     If $C is found, mtext_chr () returns its position; otherwise it
1917     returns.  */
1918
1919 /***ja
1920     @brief M-text Ãæ¤Ç»ØÄꤵ¤ì¤¿Ê¸»ú¤¬ºÇ½é¤Ë¸½¤ì¤ë°ÌÃÖ¤òÊÖ¤¹.
1921
1922     ´Ø¿ô mtext_chr () ¤Ï M-text $MT Ãæ¤Çʸ»ú $C ¤òõ¤¹¡£Ãµº÷¤Ï $MT ¤Î
1923     ÀèƬ¤«¤éËöÈøÊý¸þ¤Ë¿Ê¤à¡£
1924
1925     @return
1926     ¤â¤· $C ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_chr () ¤Ï¤½¤Î½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¸«¤Ä¤«¤é
1927     ¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£
1928
1929     @latexonly \IPAlabel{mtext_chr} @endlatexonly  */
1930
1931 /***
1932     @errors
1933     @c MERROR_RANGE
1934
1935     @seealso
1936     mtext_rchr (), mtext_character ()  */
1937
1938 int
1939 mtext_chr (MText *mt, int c)
1940 {
1941   return find_char_forward (mt, 0, mt->nchars, c);
1942 }
1943
1944 /*=*/
1945
1946 /***en
1947     @brief Return the position of the last occurrence of a character in an M-text.
1948
1949     The mtext_rchr () function searches M-text $MT for character $C.
1950     Search starts from the end of $MT and goes backwardly toward the
1951     beginning.
1952
1953     @return
1954     If $C is found, mtext_rchr () returns its position; otherwise it
1955     returns -1.  */
1956
1957 /***ja
1958     @brief M-text Ãæ¤Ç»ØÄꤵ¤ì¤¿Ê¸»ú¤¬ºÇ¸å¤Ë¸½¤ì¤ë°ÌÃÖ¤òÊÖ¤¹.
1959
1960     ´Ø¿ô mtext_rchr () ¤Ï M-text $MT Ãæ¤Çʸ»ú $C ¤òõ¤¹¡£Ãµº÷¤Ï $MT ¤Î
1961     ºÇ¸å¤«¤éÀèƬÊý¸þ¤Ø¤È¸å¸þ¤­¤Ë¿Ê¤à¡£
1962
1963     @return
1964     ¤â¤· $C ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_rchr () ¤Ï¤½¤Î½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¸«¤Ä¤«¤é
1965     ¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£
1966
1967     @latexonly \IPAlabel{mtext_rchr} @endlatexonly  */
1968
1969 /***
1970     @errors
1971     @c MERROR_RANGE
1972
1973     @seealso
1974     mtext_chr (), mtext_character ()  */
1975
1976 int
1977 mtext_rchr (MText *mt, int c)
1978 {
1979   return find_char_backward (mt, mt->nchars, 0, c);
1980 }
1981
1982
1983 /*=*/
1984
1985 /***en
1986     @brief Compare two M-texts character-by-character.
1987
1988     The mtext_cmp () function compares M-texts $MT1 and $MT2 character
1989     by character.
1990
1991     @return
1992     This function returns 1, 0, or -1 if $MT1 is found greater than,
1993     equal to, or less than $MT2, respectively.  Comparison is based on
1994     character codes.  */
1995
1996 /***ja
1997     @brief Æó¤Ä¤Î M-text ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë.
1998
1999     ´Ø¿ô mtext_cmp () ¤Ï¡¢ M-text $MT1 ¤È $MT2 ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë¡£
2000
2001     @return
2002     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì
2003     ¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£Èæ³Ó¤Ïʸ»ú¥³¡¼¥É¤Ë´ð¤Å
2004     ¤¯¡£
2005
2006     @latexonly \IPAlabel{mtext_cmp} @endlatexonly  */
2007
2008 /***
2009     @seealso
2010     mtext_ncmp (), mtext_casecmp (), mtext_ncasecmp (),
2011     mtext_compare (), mtext_case_compare ()  */
2012
2013 int
2014 mtext_cmp (MText *mt1, MText *mt2)
2015 {
2016   return compare (mt1, 0, mt1->nchars, mt2, 0, mt2->nchars);
2017 }
2018
2019
2020 /*=*/
2021
2022 /***en
2023     @brief Compare initial parts of two M-texts character-by-character.
2024
2025     The mtext_ncmp () function is similar to mtext_cmp (), but
2026     compares at most $N characters from the beginning.
2027
2028     @return
2029     This function returns 1, 0, or -1 if $MT1 is found greater than,
2030     equal to, or less than $MT2, respectively.  */
2031
2032 /***ja
2033     @brief Æó¤Ä¤Î M-text ¤ÎÀèƬÉôʬ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë.
2034
2035     ´Ø¿ô mtext_ncmp () ¤Ï¡¢´Ø¿ô mtext_cmp () Æ±ÍͤΠM-text Æ±»Î¤ÎÈæ³Ó
2036     ¤òÀèƬ¤«¤éºÇÂç $N Ê¸»ú¤Þ¤Ç¤Ë´Ø¤·¤Æ¹Ô¤Ê¤¦¡£
2037
2038     @return
2039     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì
2040     ¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
2041
2042     @latexonly \IPAlabel{mtext_ncmp} @endlatexonly  */
2043
2044 /***
2045     @seealso
2046     mtext_cmp (), mtext_casecmp (), mtext_ncasecmp ()
2047     mtext_compare (), mtext_case_compare ()  */
2048
2049 int
2050 mtext_ncmp (MText *mt1, MText *mt2, int n)
2051 {
2052   if (n < 0)
2053     return 0;
2054   return compare (mt1, 0, (mt1->nchars < n ? mt1->nchars : n),
2055                   mt2, 0, (mt2->nchars < n ? mt2->nchars : n));
2056 }
2057
2058 /*=*/
2059
2060 /***en
2061     @brief Compare specified regions of two M-texts.
2062
2063     The mtext_compare () function compares two M-texts $MT1 and $MT2,
2064     character-by-character.  The compared regions are between $FROM1
2065     and $TO1 in $MT1 and $FROM2 to $TO2 in MT2.  $FROM1 and $FROM2 are
2066     inclusive, $TO1 and $TO2 are exclusive.  $FROM1 being equal to
2067     $TO1 (or $FROM2 being equal to $TO2) means an M-text of length
2068     zero.  An invalid region specification is regarded as both $FROM1
2069     and $TO1 (or $FROM2 and $TO2) being 0.
2070
2071     @return
2072     This function returns 1, 0, or -1 if $MT1 is found greater than,
2073     equal to, or less than $MT2, respectively.  Comparison is based on
2074     character codes.  */
2075
2076 /***ja
2077     @brief Æó¤Ä¤Î M-text ¤Î»ØÄꤷ¤¿ÎΰèƱ»Î¤òÈæ³Ó¤¹¤ë.
2078
2079     ´Ø¿ô mtext_compare () ¤ÏÆó¤Ä¤Î M-text $MT1 ¤È $MT2 ¤òʸ»úñ°Ì¤ÇÈæ
2080     ³Ó¤¹¤ë¡£Èæ³ÓÂоݤȤʤë¤Î¤Ï $MT1 ¤Ç¤Ï $FROM1 ¤«¤é $TO1 ¤Þ¤Ç¡¢$MT2 
2081     ¤Ç¤Ï $FROM2 ¤«¤é $TO2 ¤Þ¤Ç¤Ç¤¢¤ë¡£$FROM1 ¤È $FROM2 ¤Ï´Þ¤Þ¤ì¡¢$TO1 
2082     ¤È $TO2 ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£$FROM1 ¤È $TO1 ¡Ê¤¢¤ë¤¤¤Ï $FROM2 ¤È $TO2 ¡Ë
2083     ¤¬Åù¤·¤¤¾ì¹ç¤ÏŤµ¥¼¥í¤Î M-text ¤ò°ÕÌ£¤¹¤ë¡£ÈÏ°Ï»ØÄê¤Ë¸í¤ê¤¬¤¢¤ë¾ì
2084     ¹ç¤Ï¡¢$FROM1 ¤È $TO1 ¡Ê¤¢¤ë¤¤¤Ï $FROM2 ¤È $TO2 ¡Ë Î¾Êý¤Ë 0 ¤¬»ØÄꤵ
2085     ¤ì¤¿¤â¤Î¤È¸«¤Ê¤¹¡£
2086
2087     @return
2088     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì
2089     ¤Ð 1 ¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£Èæ³Ó¤Ïʸ»ú¥³¡¼¥É¤Ë´ð
2090     ¤Å¤¯¡£  */
2091
2092 /***
2093     @seealso
2094     mtext_cmp (), mtext_ncmp (), mtext_casecmp (), mtext_ncasecmp (),
2095     mtext_case_compare ()  */
2096
2097 int
2098 mtext_compare (MText *mt1, int from1, int to1, MText *mt2, int from2, int to2)
2099 {
2100   if (from1 < 0 || from1 > to1 || to1 > mt1->nchars)
2101     from1 = to1 = 0;
2102
2103   if (from2 < 0 || from2 > to2 || to2 > mt2->nchars)
2104     from2 = to2 = 0;
2105
2106   return compare (mt1, from1, to1, mt2, from2, to2);
2107 }
2108
2109 /*=*/
2110
2111 /***en
2112     @brief Search an M-text for a set of characters.
2113
2114     The mtext_spn () function returns the length of the initial
2115     segment of M-text $MT1 that consists entirely of characters in
2116     M-text $MT2.  */
2117
2118 /***ja
2119     @brief ¤¢¤ë½¸¹ç¤Îʸ»ú¤ò M-text ¤ÎÃæ¤Çõ¤¹.
2120
2121     ´Ø¿ô mtext_spn () ¤Ï¡¢M-text $MT1 ¤ÎÀèƬÉôʬ¤Ç M-text $MT2 ¤Ë´Þ¤Þ
2122     ¤ì¤ëʸ»ú¤À¤±¤Ç¤Ç¤­¤Æ¤¤¤ëÉôʬ¤ÎºÇÂ獵¤òÊÖ¤¹¡£
2123
2124     @latexonly \IPAlabel{mtext_spn} @endlatexonly  */
2125
2126 /***
2127     @seealso
2128     mtext_cspn ()  */
2129
2130 int
2131 mtext_spn (MText *mt, MText *accept)
2132 {
2133   return span (mt, accept, 0, Mnil);
2134 }
2135
2136 /*=*/
2137
2138 /***en
2139     @brief Search an M-text for the complement of a set of characters.
2140
2141     The mtext_cspn () returns the length of the initial segment of
2142     M-text $MT1 that consists entirely of characters not in M-text $MT2.  */
2143
2144 /***ja
2145     @brief ¤¢¤ë½¸¹ç¤Ë°¤µ¤Ê¤¤Ê¸»ú¤ò M-text ¤ÎÃæ¤Çõ¤¹.
2146
2147     ´Ø¿ô mtext_cspn () ¤Ï¡¢M-text $MT1 ¤ÎÀèƬÉôʬ¤Ç M-text $MT2 ¤Ë´Þ¤Þ
2148     ¤ì¤Ê¤¤Ê¸»ú¤À¤±¤Ç¤Ç¤­¤Æ¤¤¤ëÉôʬ¤ÎºÇÂ獵¤òÊÖ¤¹¡£
2149
2150     @latexonly \IPAlabel{mtext_cspn} @endlatexonly  */
2151
2152 /***
2153     @seealso
2154     mtext_spn ()  */
2155
2156 int
2157 mtext_cspn (MText *mt, MText *reject)
2158 {
2159   return span (mt, reject, 0, Mt);
2160 }
2161
2162 /*=*/
2163
2164 /***en
2165     @brief Search an M-text for any of a set of characters.
2166
2167     The mtext_pbrk () function locates the first occurrence in M-text
2168     $MT1 of any of the characters in M-text $MT2.
2169
2170     @return
2171     This function returns the position in $MT1 of the found character.
2172     If no such character is found, it returns -1. */
2173
2174 /***ja
2175     @brief ¤¢¤ë½¸¹ç¤Îʸ»ú¤Î¤É¤ì¤«¤ò M-text ¤ÎÃæ¤Çõ¤¹.
2176
2177     ´Ø¿ô mtext_pbrk () ¤Ï¡¢M-text $MT1 Ãæ¤Ç M-text $MT2 ¤Î¤¤¤º¤ì¤«¤Îʸ
2178     »ú¤¬ºÇ½é¤Ë¸½¤ì¤ë°ÌÃÖ¤òÄ´¤Ù¤ë¡£
2179
2180     @return 
2181     ¸«¤Ä¤«¤Ã¤¿Ê¸»ú¤Î¡¢$MT1 Æâ¤Ë¤ª¤±¤ë½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¤â¤·¤½¤Î¤è¤¦¤Êʸ
2182     »ú¤¬¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
2183
2184     @latexonly \IPAlabel{mtext_pbrk} @endlatexonly  */
2185
2186 int
2187 mtext_pbrk (MText *mt, MText *accept)
2188 {
2189   int nchars = mtext_nchars (mt);
2190   int len = span (mt, accept, 0, Mt);
2191
2192   return (len == nchars ? -1 : len);
2193 }
2194
2195 /*=*/
2196
2197 /***en
2198     @brief Look for a token in an M-text.
2199
2200     The mtext_tok () function searches a token that firstly occurs
2201     after position $POS in M-text $MT.  Here, a token means a
2202     substring each of which does not appear in M-text $DELIM.  Note
2203     that the type of $POS is not @c int but pointer to @c int.
2204
2205     @return
2206     If a token is found, mtext_tok () copies the corresponding part of
2207     $MT and returns a pointer to the copy.  In this case, $POS is set
2208     to the end of the found token.  If no token is found, it returns
2209     @c NULL without changing the external variable #merror_code.  If an
2210     error is detected, it returns @c NULL and assigns an error code
2211     to the external variable #merror_code. */
2212
2213 /***ja
2214     @brief M-text Ãæ¤Î¥È¡¼¥¯¥ó¤òõ¤¹.
2215
2216     ´Ø¿ô mtext_tok () ¤Ï¡¢M-text $MT ¤ÎÃæ¤Ç°ÌÃÖ $POS °Ê¹ßºÇ½é¤Ë¸½¤ì¤ë
2217     ¥È¡¼¥¯¥ó¤òõ¤¹¡£¤³¤³¤Ç¥È¡¼¥¯¥ó¤È¤Ï M-text $DELIM ¤ÎÃæ¤Ë¸½¤ï¤ì¤Ê¤¤
2218     Ê¸»ú¤À¤±¤«¤é¤Ê¤ëÉôʬʸ»úÎó¤Ç¤¢¤ë¡£$POS ¤Î·¿¤¬ @c int ¤Ç¤Ï¤Ê¤¯¤Æ @c
2219     int ¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¤³¤È¤ËÃí°Õ¡£
2220
2221     @return
2222     ¤â¤·¥È¡¼¥¯¥ó¤¬¸«¤Ä¤«¤ì¤Ð mtext_tok ()¤Ï¤½¤Î¥È¡¼¥¯¥ó¤ËÁêÅö¤¹¤ëÉôʬ
2223     ¤Î $MT ¤ò¥³¥Ô¡¼¤·¡¢¤½¤Î¥³¥Ô¡¼¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¾ì¹ç¡¢$POS ¤Ï
2224     ¸«¤Ä¤«¤Ã¤¿¥È¡¼¥¯¥ó¤Î½ªÃ¼¤Ë¥»¥Ã¥È¤µ¤ì¤ë¡£¥È¡¼¥¯¥ó¤¬¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿
2225     ¾ì¹ç¤Ï³°ÉôÊÑ¿ô #merror_code ¤òÊѤ¨¤º¤Ë @c NULL ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð
2226     ¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢ÊÑÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤ò
2227     ÀßÄꤹ¤ë¡£
2228
2229     @latexonly \IPAlabel{mtext_tok} @endlatexonly  */
2230
2231 /***
2232     @errors
2233     @c MERROR_RANGE  */
2234
2235 MText *
2236 mtext_tok (MText *mt, MText *delim, int *pos)
2237 {
2238   int nchars = mtext_nchars (mt);
2239   int pos2;
2240
2241   M_CHECK_POS (mt, *pos, NULL);
2242
2243   /*
2244     Skip delimiters starting at POS in MT.
2245     Never do *pos += span(...), or you will change *pos
2246     even though no token is found.
2247    */
2248   pos2 = *pos + span (mt, delim, *pos, Mnil);
2249
2250   if (pos2 == nchars)
2251     return NULL;
2252
2253   *pos = pos2 + span (mt, delim, pos2, Mt);
2254   return (copy (mtext (), 0, mt, pos2, *pos));
2255 }
2256
2257 /*=*/
2258
2259 /***en
2260     @brief Locate an M-text in another.
2261
2262     The mtext_text () function finds the first occurrence of M-text
2263     $MT2 in M-text $MT1 after the position $POS while ignoring
2264     difference of the text properties.
2265
2266     @return
2267     If $MT2 is found in $MT1, mtext_text () returns the position of it
2268     first occurrence.  Otherwise it returns -1.  If $MT2 is empty, it
2269     returns 0.  */
2270
2271 /***ja
2272     @brief M-text Ãæ¤ÇÊ̤ΠM-text ¤òõ¤¹.
2273
2274     ´Ø¿ô mtext_text () ¤Ï¡¢M-text $MT1 Ãæ¤Ç°ÌÃÖ $POS °Ê¹ß¤Ë¸½¤ï¤ì¤ë 
2275     M-text $MT2 ¤ÎºÇ½é¤Î°ÌÃÖ¤òÄ´¤Ù¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î°ã¤¤¤Ï̵»ë¤µ
2276     ¤ì¤ë¡£
2277
2278     @return
2279     $MT1 Ãæ¤Ë $MT2 ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_text() ¤Ï¤½¤ÎºÇ½é¤Î½Ð¸½°ÌÃÖ¤òÊÖ
2280     ¤¹¡£¸«¤Ä¤«¤é¤Ê¤¤¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£¤â¤· $MT2 ¤¬¶õ¤Ê¤é¤Ð 0 ¤òÊÖ¤¹¡£
2281
2282     @latexonly \IPAlabel{mtext_text} @endlatexonly  */
2283
2284 int
2285 mtext_text (MText *mt1, int pos, MText *mt2)
2286 {
2287   int from = pos;
2288   int pos_byte = POS_CHAR_TO_BYTE (mt1, pos);
2289   int c = mtext_ref_char (mt2, 0);
2290   int nbytes1 = mtext_nbytes (mt1);
2291   int nbytes2 = mtext_nbytes (mt2);
2292   int limit;
2293   int use_memcmp = (mt1->format == mt2->format
2294                     || (mt1->format < MTEXT_FORMAT_UTF_8
2295                         && mt2->format == MTEXT_FORMAT_UTF_8));
2296   int unit_bytes = (mt1->format <= MTEXT_FORMAT_UTF_8 ? 1
2297                     : mt1->format <= MTEXT_FORMAT_UTF_16BE ? 2
2298                     : 4);
2299
2300   if (nbytes2 > pos_byte + nbytes1)
2301     return -1;
2302   pos_byte = nbytes1 - nbytes2;
2303   limit = POS_BYTE_TO_CHAR (mt1, pos_byte);
2304
2305   while (1)
2306     {
2307       if ((pos = mtext_character (mt1, from, limit, c)) < 0)
2308         return -1;
2309       pos_byte = POS_CHAR_TO_BYTE (mt1, pos);
2310       if (use_memcmp
2311           ? ! memcmp (mt1->data + pos_byte * unit_bytes, 
2312                       mt2->data, nbytes2 * unit_bytes)
2313           : ! compare (mt1, pos, mt2->nchars, mt2, 0, mt2->nchars))
2314         break;
2315       from = pos + 1;
2316     }
2317   return pos;
2318 }
2319
2320 /***en
2321     @brief Locate an M-text in a specific range of another.
2322
2323     The mtext_search () function searches for the first occurrence of
2324     M-text $MT2 in M-text $MT1 in the region $FROM and $TO while
2325     ignoring difference of the text properties.  If $FROM is less than
2326     $TO, the forward search starts from $FROM, otherwise the backward
2327     search starts from $TO.
2328
2329     @return
2330     If $MT2 is found in $MT1, mtext_search () returns the position of the
2331     first occurrence.  Otherwise it returns -1.  If $MT2 is empty, it
2332     returns 0.  */
2333
2334 /***ja
2335     @brief M-text Ãæ¤ÎÆÃÄê¤ÎÎΰè¤ÇÊ̤ΠM-text ¤òõ¤¹.
2336
2337     ´Ø¿ô mtext_search () ¤Ï¡¢ searches for the first occurrence of
2338     M-text $MT2 in M-text $MT1 in the region $FROM and $TO while
2339     ignoring difference of the text properties.  If $FROM is less than
2340     $TO, the forward search starts from $FROM, otherwise the backward
2341     search starts from $TO.
2342
2343     ´Ø¿ô mtext_text () ¤Ï¡¢M-text $MT1 Ãæ¤Î $FROM ¤«¤é $TO ¤Þ¤Ç¤Î´Ö¤Î
2344     Îΰè¤ÇM-text $MT2 ¤¬ºÇ½é¤Ë¸½¤ï¤ì¤ë°ÌÃÖ¤òÄ´¤Ù¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£
2345     ¤Î°ã¤¤¤Ï̵»ë¤µ¤ì¤ë¡£¤â¤· $FROM ¤¬ $TO ¤è¤ê¾®¤µ¤±¤ì¤Ðõº÷¤Ï°ÌÃÖ 
2346     $FROM ¤«¤éËöÈøÊý¸þ¤Ø¡¢¤½¤¦¤Ç¤Ê¤±¤ì¤Ð $TO ¤«¤éÀèƬÊý¸þ¤ØºÇÂç $TO ¤Þ
2347     ¤Ç¿Ê¤à¡£
2348
2349     @return
2350     $MT1 Ãæ¤Ë $MT2 ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_search() ¤Ï¤½¤ÎºÇ½é¤Î½Ð¸½°ÌÃÖ¤òÊÖ
2351     ¤¹¡£¸«¤Ä¤«¤é¤Ê¤¤¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£¤â¤· $MT2 ¤¬¶õ¤Ê¤é¤Ð 0 ¤òÊÖ¤¹¡£
2352     */
2353
2354 int
2355 mtext_search (MText *mt1, int from, int to, MText *mt2)
2356 {
2357   int c = mtext_ref_char (mt2, 0);
2358   int from_byte;
2359   int nbytes2 = mtext_nbytes (mt2);
2360
2361   if (mt1->format > MTEXT_FORMAT_UTF_8
2362       || mt2->format > MTEXT_FORMAT_UTF_8)
2363     MERROR (MERROR_MTEXT, -1);
2364
2365   if (from < to)
2366     {
2367       to -= mtext_nchars (mt2);
2368       if (from > to)
2369         return -1;
2370       while (1)
2371         {
2372           if ((from = find_char_forward (mt1, from, to, c)) < 0)
2373             return -1;
2374           from_byte = POS_CHAR_TO_BYTE (mt1, from);
2375           if (! memcmp (mt1->data + from_byte, mt2->data, nbytes2))
2376             break;
2377           from++;
2378         }
2379     }
2380   else if (from > to)
2381     {
2382       from -= mtext_nchars (mt2);
2383       if (from < to)
2384         return -1;
2385       while (1)
2386         {
2387           if ((from = find_char_backward (mt1, from, to, c)) < 0)
2388             return -1;
2389           from_byte = POS_CHAR_TO_BYTE (mt1, from);
2390           if (! memcmp (mt1->data + from_byte, mt2->data, nbytes2))
2391             break;
2392           from--;
2393         }
2394     }
2395
2396   return from;
2397 }
2398
2399 /*=*/
2400
2401 /***en
2402     @brief Compare two M-texts ignoring cases.
2403
2404     The mtext_casecmp () function is similar to mtext_cmp (), but
2405     ignores cases on comparison.
2406
2407     @return
2408     This function returns 1, 0, or -1 if $MT1 is found greater than,
2409     equal to, or less than $MT2, respectively.  */
2410
2411 /***ja
2412     @brief Æó¤Ä¤Î M-text ¤òÂçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤ÆÈæ³Ó¤¹¤ë.
2413
2414     ´Ø¿ô mtext_casecmp () ¤Ï¡¢´Ø¿ô mtext_cmp () Æ±ÍͤΠM-text Æ±»Î¤ÎÈæ
2415     ³Ó¤ò¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤Æ¹Ô¤Ê¤¦¡£
2416
2417     @return
2418     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì
2419     ¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
2420
2421     @latexonly \IPAlabel{mtext_casecmp} @endlatexonly  */
2422
2423 /***
2424     @seealso
2425     mtext_cmp (), mtext_ncmp (), mtext_ncasecmp ()
2426     mtext_compare (), mtext_case_compare ()  */
2427
2428 int
2429 mtext_casecmp (MText *mt1, MText *mt2)
2430 {
2431   return case_compare (mt1, 0, mt1->nchars, mt2, 0, mt2->nchars);
2432 }
2433
2434 /*=*/
2435
2436 /***en
2437     @brief Compare initial parts of two M-texts ignoring cases.
2438
2439     The mtext_ncasecmp () function is similar to mtext_casecmp (), but
2440     compares at most $N characters from the beginning.
2441
2442     @return
2443     This function returns 1, 0, or -1 if $MT1 is found greater than,
2444     equal to, or less than $MT2, respectively.  */
2445
2446 /***ja
2447     @brief Æó¤Ä¤Î M-text ¤ÎÀèƬÉôʬ¤òÂçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤ÆÈæ³Ó¤¹¤ë.
2448
2449     ´Ø¿ô mtext_ncasecmp () ¤Ï¡¢´Ø¿ô mtext_casecmp () Æ±ÍͤΠM-text Æ±
2450     »Î¤ÎÈæ³Ó¤òÀèƬ¤«¤éºÇÂç $N Ê¸»ú¤Þ¤Ç¤Ë´Ø¤·¤Æ¹Ô¤Ê¤¦¡£
2451
2452     @return
2453     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì
2454     ¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
2455
2456     @latexonly \IPAlabel{mtext_ncasecmp} @endlatexonly  */
2457
2458 /***
2459     @seealso
2460     mtext_cmp (), mtext_casecmp (), mtext_casecmp ()
2461     mtext_compare (), mtext_case_compare ()  */
2462
2463 int
2464 mtext_ncasecmp (MText *mt1, MText *mt2, int n)
2465 {
2466   if (n < 0)
2467     return 0;
2468   return case_compare (mt1, 0, (mt1->nchars < n ? mt1->nchars : n),
2469                        mt2, 0, (mt2->nchars < n ? mt2->nchars : n));
2470 }
2471
2472 /*=*/
2473
2474 /***en
2475     @brief Compare specified regions of two M-texts ignoring cases. 
2476
2477     The mtext_case_compare () function compares two M-texts $MT1 and
2478     $MT2, character-by-character, ignoring cases.  The compared
2479     regions are between $FROM1 and $TO1 in $MT1 and $FROM2 to $TO2 in
2480     MT2.  $FROM1 and $FROM2 are inclusive, $TO1 and $TO2 are
2481     exclusive.  $FROM1 being equal to $TO1 (or $FROM2 being equal to
2482     $TO2) means an M-text of length zero.  An invalid region
2483     specification is regarded as both $FROM1 and $TO1 (or $FROM2 and
2484     $TO2) being 0.
2485
2486     @return
2487     This function returns 1, 0, or -1 if $MT1 is found greater than,
2488     equal to, or less than $MT2, respectively.  Comparison is based on
2489     character codes.  */
2490
2491 /***ja 
2492     @brief Æó¤Ä¤Î M-text ¤Î»ØÄꤷ¤¿Îΰè¤ò¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤ÆÈæ³Ó¤¹¤ë.
2493
2494     ´Ø¿ô mtext_compare () ¤ÏÆó¤Ä¤Î M-text $MT1 ¤È $MT2 ¤ò¡¢Âçʸ»ú¡¿¾®
2495     Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤Ä¤Äʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë¡£Èæ³ÓÂоݤȤʤë¤Î¤Ï $MT1 
2496     ¤Ç¤Ï $FROM1 ¤«¤é $TO1 ¤Þ¤Ç¡¢$MT2 ¤Ç¤Ï $FROM2 ¤«¤é $TO2 ¤Þ¤Ç¤Ç¤¢¤ë¡£
2497     $FROM1 ¤È $FROM2 ¤Ï´Þ¤Þ¤ì¡¢$TO1 ¤È $TO2 ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£$FROM1 ¤È 
2498     $TO1 ¡Ê¤¢¤ë¤¤¤Ï $FROM2 ¤È $TO2 ¡Ë¤¬Åù¤·¤¤¾ì¹ç¤ÏŤµ¥¼¥í¤Î M-text 
2499     ¤ò°ÕÌ£¤¹¤ë¡£ÈÏ°Ï»ØÄê¤Ë¸í¤ê¤¬¤¢¤ë¾ì¹ç¤Ï¡¢$FROM1 ¤È $TO1 ¡Ê¤¢¤ë¤¤¤Ï 
2500     $FROM2 ¤È $TO2 ¡ËξÊý¤Ë 0 ¤¬»ØÄꤵ¤ì¤¿¤â¤Î¤È¸«¤Ê¤¹¡£
2501
2502     @return
2503     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì
2504     ¤Ð1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð-1¤òÊÖ¤¹¡£Èæ³Ó¤Ïʸ»ú¥³¡¼¥É¤Ë´ð¤Å¤¯¡£
2505
2506   @latexonly \IPAlabel{mtext_case_compare} @endlatexonly
2507 */
2508
2509 /***
2510     @seealso
2511     mtext_cmp (), mtext_ncmp (), mtext_casecmp (), mtext_ncasecmp (),
2512     mtext_compare ()  */
2513
2514 int
2515 mtext_case_compare (MText *mt1, int from1, int to1,
2516                     MText *mt2, int from2, int to2)
2517 {
2518   if (from1 < 0 || from1 > to1 || to1 > mt1->nchars)
2519     from1 = to1 = 0;
2520
2521   if (from2 < 0 || from2 > to2 || to2 > mt2->nchars)
2522     from2 = to2 = 0;
2523
2524   return case_compare (mt1, from1, to1, mt2, from2, to2);
2525 }
2526
2527 /*** @} */
2528
2529 #include <stdio.h>
2530
2531 /*** @addtogroup m17nDebug */
2532 /*=*/
2533 /*** @{  */
2534
2535 /***en
2536     @brief Dump an M-text.
2537
2538     The mdebug_dump_mtext () function prints the M-text $MT in a human
2539     readable way to the stderr.  $INDENT specifies how many columns to
2540     indent the lines but the first one.  If $FULLP is zero, this
2541     function prints only a character code sequence.  Otherwise, it
2542     prints the internal byte sequence and text properties as well.
2543
2544     @return
2545     This function returns $MT.  */
2546 /***ja
2547     @brief M-text ¤ò¥À¥ó¥×¤¹¤ë.
2548
2549     ´Ø¿ô mdebug_dump_mtext () ¤Ï M-text $MT ¤ò stderr ¤Ë¿Í´Ö¤Ë²ÄÆɤʠ
2550     ·Á¤Ç°õºþ¤¹¤ë¡£ $UNDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£$FULLP 
2551     ¤¬ 0 ¤Ê¤é¤Ð¡¢Ê¸»ú¥³¡¼¥ÉÎó¤À¤±¤ò°õºþ¤¹¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢ÆâÉô¥Ð¥¤
2552     ¥ÈÎó¤È¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤â°õºþ¤¹¤ë¡£
2553
2554     @return
2555     ¤³¤Î´Ø¿ô¤Ï $MT ¤òÊÖ¤¹¡£  */
2556
2557 MText *
2558 mdebug_dump_mtext (MText *mt, int indent, int fullp)
2559 {
2560   char *prefix = (char *) alloca (indent + 1);
2561   int i;
2562   unsigned char *p;
2563
2564   memset (prefix, 32, indent);
2565   prefix[indent] = 0;
2566
2567   if (! fullp)
2568     {
2569       fprintf (stderr, "\"");
2570       for (i = 0; i < mt->nbytes; i++)
2571         {
2572           int c = mt->data[i];
2573           if (c >= ' ' && c < 127)
2574             fprintf (stderr, "%c", c);
2575           else
2576             fprintf (stderr, "\\x%02X", c);
2577         }
2578       fprintf (stderr, "\"");
2579       return mt;
2580     }
2581
2582   fprintf (stderr,
2583            "(mtext (size %d %d %d) (cache %d %d)",
2584            mt->nchars, mt->nbytes, mt->allocated,
2585            mt->cache_char_pos, mt->cache_byte_pos);
2586   if (mt->nchars > 0)
2587     {
2588       fprintf (stderr, "\n%s (bytes \"", prefix);
2589       for (i = 0; i < mt->nbytes; i++)
2590         fprintf (stderr, "\\x%02x", mt->data[i]);
2591       fprintf (stderr, "\")\n");
2592       fprintf (stderr, "%s (chars \"", prefix);
2593       p = mt->data;
2594       for (i = 0; i < mt->nchars; i++)
2595         {
2596           int len;
2597           int c = STRING_CHAR_AND_BYTES (p, len);
2598
2599           if (c >= ' ' && c < 127 && c != '\\' && c != '"')
2600             fputc (c, stderr);
2601           else
2602             fprintf (stderr, "\\x%X", c);
2603           p += len;
2604         }
2605       fprintf (stderr, "\")");
2606       if (mt->plist)
2607         {
2608           fprintf (stderr, "\n%s ", prefix);
2609           dump_textplist (mt->plist, indent + 1);
2610         }
2611     }
2612   fprintf (stderr, ")");
2613   return mt;
2614 }
2615
2616 /*** @} */ 
2617
2618 /*
2619   Local Variables:
2620   coding: euc-japan
2621   End:
2622 */