*** empty log message ***
[m17n/m17n-lib.git] / src / mtext.c
1 /* mtext.c -- M-text module.
2    Copyright (C) 2003, 2004, 2005
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
55     char *</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     ¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ë¤è¤Ã¤Æ¡¢¥Æ¥­¥¹¥È¤Î³ÆÉô°Ì¤Ë´Ø¤¹¤ëÍÍ¡¹¤Ê¾ðÊó¤ò
68     M-text Æâ¤ËÊÝ»ý¤¹¤ë¤³¤È¤¬²Äǽ¤Ë¤Ê¤ë¡£
69     ¤½¤Î¤¿¤á¡¢¤½¤ì¤é¤Î¾ðÊó¤ò¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥àÆâ¤ÇÅý°ìŪ¤Ë°·¤¦¤³¤È¤¬²Äǽ¤Ë¤Ê¤ë¡£
70     ¤Þ¤¿¡¢M-text 
71     ¼«ÂΤ¬Ë­É٤ʾðÊó¤ò»ý¤Ä¤¿¤á¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥àÃæ¤Î³Æ´Ø¿ô¤ò´ÊÁDz½¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£
72
73     ¤µ¤é¤Ëm17n ¥é¥¤¥Ö¥é¥ê¤Ï¡¢ C-string 
74     ¤òÁàºî¤¹¤ë¤¿¤á¤ËÄ󶡤µ¤ì¤ë¼ï¡¹¤Î´Ø¿ô¤ÈƱÅù¤Î¤â¤Î¤ò M-text 
75     ¤òÁàºî¤¹¤ë¤¿¤á¤Ë¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤ë¡£  */
76
77 /*=*/
78
79 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
80 /*** @addtogroup m17nInternal
81      @{ */
82
83 #include <config.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <locale.h>
88
89 #include "m17n.h"
90 #include "m17n-misc.h"
91 #include "internal.h"
92 #include "textprop.h"
93 #include "character.h"
94 #include "mtext.h"
95 #include "plist.h"
96
97 static M17NObjectArray mtext_table;
98
99 static MSymbol M_charbag;
100
101 /** Increment character position CHAR_POS and unit position UNIT_POS
102     so that they point to the next character in M-text MT.  No range
103     check for CHAR_POS and UNIT_POS.  */
104
105 #define INC_POSITION(mt, char_pos, unit_pos)                    \
106   do {                                                          \
107     int c;                                                      \
108                                                                 \
109     if ((mt)->format <= MTEXT_FORMAT_UTF_8)                     \
110       {                                                         \
111         c = (mt)->data[(unit_pos)];                             \
112         (unit_pos) += CHAR_UNITS_BY_HEAD_UTF8 (c);              \
113       }                                                         \
114     else if ((mt)->format <= MTEXT_FORMAT_UTF_16BE)             \
115       {                                                         \
116         c = ((unsigned short *) ((mt)->data))[(unit_pos)];      \
117                                                                 \
118         if ((mt)->format != MTEXT_FORMAT_UTF_16)                \
119           c = SWAP_16 (c);                                      \
120         (unit_pos) += CHAR_UNITS_BY_HEAD_UTF16 (c);             \
121       }                                                         \
122     else                                                        \
123       (unit_pos)++;                                             \
124     (char_pos)++;                                               \
125   } while (0)
126
127
128 /** Decrement character position CHAR_POS and unit position UNIT_POS
129     so that they point to the previous character in M-text MT.  No
130     range check for CHAR_POS and UNIT_POS.  */
131
132 #define DEC_POSITION(mt, char_pos, unit_pos)                            \
133   do {                                                                  \
134     if ((mt)->format <= MTEXT_FORMAT_UTF_8)                             \
135       {                                                                 \
136         unsigned char *p1 = (mt)->data + (unit_pos);                    \
137         unsigned char *p0 = p1 - 1;                                     \
138                                                                         \
139         while (! CHAR_HEAD_P (p0)) p0--;                                \
140         (unit_pos) -= (p1 - p0);                                        \
141       }                                                                 \
142     else if ((mt)->format <= MTEXT_FORMAT_UTF_16BE)                     \
143       {                                                                 \
144         int c = ((unsigned short *) ((mt)->data))[(unit_pos) - 1];      \
145                                                                         \
146         if ((mt)->format != MTEXT_FORMAT_UTF_16)                        \
147           c = SWAP_16 (c);                                              \
148         (unit_pos) -= 2 - (c < 0xD800 || c >= 0xE000);                  \
149       }                                                                 \
150     else                                                                \
151       (unit_pos)--;                                                     \
152     (char_pos)--;                                                       \
153   } while (0)
154
155 #define FORMAT_COVERAGE(fmt)                                    \
156   (fmt == MTEXT_FORMAT_UTF_8 ? MTEXT_COVERAGE_FULL              \
157    : fmt == MTEXT_FORMAT_US_ASCII ? MTEXT_COVERAGE_ASCII        \
158    : fmt >= MTEXT_FORMAT_UTF_32LE ? MTEXT_COVERAGE_FULL         \
159    : MTEXT_COVERAGE_UNICODE)
160
161 /* Compoare sub-texts in MT1 (range FROM1 and TO1) and MT2 (range
162    FROM2 to TO2). */
163
164 static int
165 compare (MText *mt1, int from1, int to1, MText *mt2, int from2, int to2)
166 {
167   if (mt1->format == mt2->format
168       && (mt1->format <= MTEXT_FORMAT_UTF_8))
169     {
170       unsigned char *p1, *pend1, *p2, *pend2;
171       int unit_bytes = UNIT_BYTES (mt1->format);
172       int nbytes;
173       int result;
174
175       p1 = mt1->data + mtext__char_to_byte (mt1, from1) * unit_bytes;
176       pend1 = mt1->data + mtext__char_to_byte (mt1, to1) * unit_bytes;
177
178       p2 = mt2->data + mtext__char_to_byte (mt2, from2) * unit_bytes;
179       pend2 = mt2->data + mtext__char_to_byte (mt2, to2) * unit_bytes;
180
181       if (pend1 - p1 < pend2 - p2)
182         nbytes = pend1 - p1;
183       else
184         nbytes = pend2 - p2;
185       result = memcmp (p1, p2, nbytes);
186       if (result)
187         return result;
188       return ((pend1 - p1) - (pend2 - p2));
189     }
190   for (; from1 < to1 && from2 < to2; from1++, from2++)
191     {
192       int c1 = mtext_ref_char (mt1, from1);
193       int c2 = mtext_ref_char (mt2, from2);
194
195       if (c1 != c2)
196         return (c1 > c2 ? 1 : -1);
197     }
198   return (from2 == to2 ? (from1 < to1) : -1);
199 }
200
201
202 /* Return how many units are required in UTF-8 to represent characters
203    between FROM and TO of MT.  */
204
205 static int
206 count_by_utf_8 (MText *mt, int from, int to)
207 {
208   int n, c;
209
210   for (n = 0; from < to; from++)
211     {
212       c = mtext_ref_char (mt, from);
213       n += CHAR_UNITS_UTF8 (c);
214     }
215   return n;
216 }
217
218
219 /* Return how many units are required in UTF-16 to represent
220    characters between FROM and TO of MT.  */
221
222 static int
223 count_by_utf_16 (MText *mt, int from, int to)
224 {
225   int n, c;
226
227   for (n = 0; from < to; from++)
228     {
229       c = mtext_ref_char (mt, from);
230       n += CHAR_UNITS_UTF16 (c);
231     }
232   return n;
233 }
234
235
236 /* Insert text between FROM and TO of MT2 at POS of MT1.  */
237
238 static MText *
239 insert (MText *mt1, int pos, MText *mt2, int from, int to)
240 {
241   int pos_unit = POS_CHAR_TO_BYTE (mt1, pos);
242   int from_unit = POS_CHAR_TO_BYTE (mt2, from);
243   int new_units = POS_CHAR_TO_BYTE (mt2, to) - from_unit;
244   int unit_bytes;
245
246   if (mt1->nchars == 0)
247     mt1->format = mt2->format, mt1->coverage = mt2->coverage;
248   else if (mt1->format != mt2->format)
249     {
250       /* Be sure to make mt1->format sufficient to contain all
251          characters in mt2.  */
252       if (mt1->format == MTEXT_FORMAT_UTF_8
253           || mt1->format == MTEXT_FORMAT_UTF_32
254           || (mt1->format == MTEXT_FORMAT_UTF_16
255               && mt2->format <= MTEXT_FORMAT_UTF_16BE
256               && mt2->format != MTEXT_FORMAT_UTF_8))
257         ;
258       else if (mt1->format == MTEXT_FORMAT_US_ASCII)
259         {
260           if (mt2->format == MTEXT_FORMAT_UTF_8)
261             mt1->format = MTEXT_FORMAT_UTF_8, mt1->coverage = mt2->coverage;
262           else if (mt2->format == MTEXT_FORMAT_UTF_16
263                    || mt2->format == MTEXT_FORMAT_UTF_32)
264             mtext__adjust_format (mt1, mt2->format);
265           else
266             mtext__adjust_format (mt1, MTEXT_FORMAT_UTF_8);
267         }
268       else
269         {
270           mtext__adjust_format (mt1, MTEXT_FORMAT_UTF_8);
271           pos_unit = POS_CHAR_TO_BYTE (mt1, pos);
272         }
273     }
274
275   unit_bytes = UNIT_BYTES (mt1->format);
276
277   if (mt1->format == mt2->format)
278     {
279       int pos_byte = pos_unit * unit_bytes;
280       int total_bytes = (mt1->nbytes + new_units) * unit_bytes;
281       int new_bytes = new_units * unit_bytes;
282
283       if (total_bytes + unit_bytes > mt1->allocated)
284         {
285           mt1->allocated = total_bytes + unit_bytes;
286           MTABLE_REALLOC (mt1->data, mt1->allocated, MERROR_MTEXT);
287         }
288       if (pos < mt1->nchars)
289         memmove (mt1->data + pos_byte + new_bytes, mt1->data + pos_byte,
290                  (mt1->nbytes - pos_unit + 1) * unit_bytes);
291       memcpy (mt1->data + pos_byte, mt2->data + from_unit * unit_bytes,
292               new_bytes);
293     }
294   else if (mt1->format == MTEXT_FORMAT_UTF_8)
295     {
296       unsigned char *p;
297       int total_bytes, i, c;
298
299       new_units = count_by_utf_8 (mt2, from, to);
300       total_bytes = mt1->nbytes + new_units;
301
302       if (total_bytes + 1 > mt1->allocated)
303         {
304           mt1->allocated = total_bytes + 1;
305           MTABLE_REALLOC (mt1->data, mt1->allocated, MERROR_MTEXT);
306         }
307       p = mt1->data + pos_unit;
308       memmove (p + new_units, p, mt1->nbytes - pos_unit + 1);
309       for (i = from; i < to; i++)
310         {
311           c = mtext_ref_char (mt2, i);
312           p += CHAR_STRING_UTF8 (c, p);
313         }
314     }
315   else if (mt1->format == MTEXT_FORMAT_UTF_16)
316     {
317       unsigned short *p;
318       int total_bytes, i, c;
319
320       new_units = count_by_utf_16 (mt2, from, to);
321       total_bytes = (mt1->nbytes + new_units) * USHORT_SIZE;
322
323       if (total_bytes + USHORT_SIZE > mt1->allocated)
324         {
325           mt1->allocated = total_bytes + USHORT_SIZE;
326           MTABLE_REALLOC (mt1->data, mt1->allocated, MERROR_MTEXT);
327         }
328       p = (unsigned short *) mt1->data + pos_unit;
329       memmove (p + new_units, p,
330                (mt1->nbytes - pos_unit + 1) * USHORT_SIZE);
331       for (i = from; i < to; i++)
332         {
333           c = mtext_ref_char (mt2, i);
334           p += CHAR_STRING_UTF16 (c, p);
335         }
336     }
337   else                          /* MTEXT_FORMAT_UTF_32 */
338     {
339       unsigned int *p;
340       int total_bytes, i;
341
342       new_units = to - from;
343       total_bytes = (mt1->nbytes + new_units) * UINT_SIZE;
344
345       if (total_bytes + UINT_SIZE > mt1->allocated)
346         {
347           mt1->allocated = total_bytes + UINT_SIZE;
348           MTABLE_REALLOC (mt1->data, mt1->allocated, MERROR_MTEXT);
349         }
350       p = (unsigned *) mt1->data + pos_unit;
351       memmove (p + new_units, p,
352                (mt1->nbytes - pos_unit + 1) * UINT_SIZE);
353       for (i = from; i < to; i++)
354         *p++ = mtext_ref_char (mt2, i);
355     }
356
357   mtext__adjust_plist_for_insert
358     (mt1, pos, to - from,
359      mtext__copy_plist (mt2->plist, from, to, mt1, pos));
360   mt1->nchars += to - from;
361   mt1->nbytes += new_units;
362   if (mt1->cache_char_pos > pos)
363     {
364       mt1->cache_char_pos += to - from;
365       mt1->cache_byte_pos += new_units;
366     }
367
368   return mt1;
369 }
370
371
372 static MCharTable *
373 get_charbag (MText *mt)
374 {
375   MTextProperty *prop = mtext_get_property (mt, 0, M_charbag);
376   MCharTable *table;
377   int i;
378
379   if (prop)
380     {
381       if (prop->end == mt->nchars)
382         return ((MCharTable *) prop->val);
383       mtext_detach_property (prop);
384     }
385
386   table = mchartable (Msymbol, (void *) 0);
387   for (i = mt->nchars - 1; i >= 0; i--)
388     mchartable_set (table, mtext_ref_char (mt, i), Mt);
389   prop = mtext_property (M_charbag, table, MTEXTPROP_VOLATILE_WEAK);
390   mtext_attach_property (mt, 0, mtext_nchars (mt), prop);
391   M17N_OBJECT_UNREF (prop);
392   return table;
393 }
394
395
396 /* span () : Number of consecutive chars starting at POS in MT1 that
397    are included (if NOT is Mnil) or not included (if NOT is Mt) in
398    MT2.  */
399
400 static int
401 span (MText *mt1, MText *mt2, int pos, MSymbol not)
402 {
403   int nchars = mtext_nchars (mt1);
404   MCharTable *table = get_charbag (mt2);
405   int i;
406
407   for (i = pos; i < nchars; i++)
408     if ((MSymbol) mchartable_lookup (table, mtext_ref_char (mt1, i)) == not)
409       break;
410   return (i - pos);
411 }
412
413
414 static int
415 count_utf_8_chars (const void *data, int nitems)
416 {
417   unsigned char *p = (unsigned char *) data;
418   unsigned char *pend = p + nitems;
419   int nchars = 0;
420
421   while (p < pend)
422     {
423       int i, n;
424
425       for (; p < pend && *p < 128; nchars++, p++);
426       if (p == pend)
427         return nchars;
428       if (! CHAR_HEAD_P_UTF8 (p))
429         return -1;
430       n = CHAR_UNITS_BY_HEAD_UTF8 (*p);
431       if (p + n > pend)
432         return -1;
433       for (i = 1; i < n; i++)
434         if (CHAR_HEAD_P_UTF8 (p + i))
435           return -1;
436       p += n;
437       nchars++;
438     }
439   return nchars;
440 }
441
442 static int
443 count_utf_16_chars (const void *data, int nitems, int swap)
444 {
445   unsigned short *p = (unsigned short *) data;
446   unsigned short *pend = p + nitems;
447   int nchars = 0;
448   int prev_surrogate = 0;
449
450   for (; p < pend; p++)
451     {
452       int c = *p;
453
454       if (swap)
455         c = SWAP_16 (c);
456       if (prev_surrogate)
457         {
458           if (c < 0xDC00 || c >= 0xE000)
459             /* Invalid surrogate */
460             nchars++;
461         }
462       else
463         {
464           if (c >= 0xD800 && c < 0xDC00)
465             prev_surrogate = 1;
466           nchars++;
467         }
468     }
469   if (prev_surrogate)
470     nchars++;
471   return nchars;
472 }
473
474
475 static int
476 find_char_forward (MText *mt, int from, int to, int c)
477 {
478   int from_byte = POS_CHAR_TO_BYTE (mt, from);
479
480   if (mt->format <= MTEXT_FORMAT_UTF_8)
481     {
482       unsigned char *p = mt->data + from_byte;
483
484       while (from < to && STRING_CHAR_ADVANCE_UTF8 (p) != c) from++;
485     }
486   else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
487     {
488       unsigned short *p = (unsigned short *) (mt->data) + from_byte;
489
490       if (mt->format == MTEXT_FORMAT_UTF_16)
491         while (from < to && STRING_CHAR_ADVANCE_UTF16 (p) != c) from++;
492       else if (c < 0x10000)
493         {
494           c = SWAP_16 (c);
495           while (from < to && *p != c)
496             {
497               from++;
498               p += ((*p & 0xFF) < 0xD8 || (*p & 0xFF) >= 0xE0) ? 1 : 2;
499             }
500         }
501       else if (c < 0x110000)
502         {
503           int c1 = (c >> 10) + 0xD800;
504           int c2 = (c & 0x3FF) + 0xDC00;
505
506           c1 = SWAP_16 (c1);
507           c2 = SWAP_16 (c2);
508           while (from < to && (*p != c1 || p[1] != c2))
509             {
510               from++;
511               p += ((*p & 0xFF) < 0xD8 || (*p & 0xFF) >= 0xE0) ? 1 : 2;
512             }
513         }
514       else
515         from = to;
516     }
517   else
518     {
519       unsigned *p = (unsigned *) (mt->data) + from_byte;
520       unsigned c1 = c;
521
522       if (mt->format != MTEXT_FORMAT_UTF_32)
523         c1 = SWAP_32 (c1);
524       while (from < to && *p++ != c1) from++;
525     }
526
527   return (from < to ? from : -1);
528 }
529
530
531 static int
532 find_char_backward (MText *mt, int from, int to, int c)
533 {
534   int to_byte = POS_CHAR_TO_BYTE (mt, to);
535
536   if (mt->format <= MTEXT_FORMAT_UTF_8)
537     {
538       unsigned char *p = mt->data + to_byte;
539
540       while (from < to)
541         {
542           for (p--; ! CHAR_HEAD_P (p); p--);
543           if (c == STRING_CHAR (p))
544             break;
545           to--;
546         }
547     }
548   else if (mt->format <= MTEXT_FORMAT_UTF_16LE)
549     {
550       unsigned short *p = (unsigned short *) (mt->data) + to_byte;
551
552       if (mt->format == MTEXT_FORMAT_UTF_16)
553         {
554           while (from < to)
555             {
556               p--;
557               if (*p >= 0xDC00 && *p < 0xE000)
558                 p--;
559               if (c == STRING_CHAR_UTF16 (p))
560                 break;
561               to--;
562             }
563         }
564       else if (c < 0x10000)
565         {
566           c = SWAP_16 (c);
567           while (from < to && p[-1] != c)
568             {
569               to--;
570               p -= ((p[-1] & 0xFF) < 0xD8 || (p[-1] & 0xFF) >= 0xE0) ? 1 : 2;
571             }
572         }
573       else if (c < 0x110000)
574         {
575           int c1 = (c >> 10) + 0xD800;
576           int c2 = (c & 0x3FF) + 0xDC00;
577
578           c1 = SWAP_16 (c1);
579           c2 = SWAP_16 (c2);
580           while (from < to && (p[-1] != c2 || p[-2] != c1))
581             {
582               to--;
583               p -= ((p[-1] & 0xFF) < 0xD8 || (p[-1] & 0xFF) >= 0xE0) ? 1 : 2;
584             }
585         }
586     }
587   else
588     {
589       unsigned *p = (unsigned *) (mt->data) + to_byte;
590       unsigned c1 = c;
591
592       if (mt->format != MTEXT_FORMAT_UTF_32)
593         c1 = SWAP_32 (c1);
594       while (from < to && p[-1] != c1) to--, p--;
595     }
596
597   return (from < to ? to - 1 : -1);
598 }
599
600
601 static void
602 free_mtext (void *object)
603 {
604   MText *mt = (MText *) object;
605
606   if (mt->plist)
607     mtext__free_plist (mt);
608   if (mt->data && mt->allocated >= 0)
609     free (mt->data);
610   M17N_OBJECT_UNREGISTER (mtext_table, mt);
611   free (object);
612 }
613
614 /** Case handler (case-folding comparison and case conversion) */
615
616 /** Structure for an iterator used in case-fold comparison.  */
617
618 struct casecmp_iterator {
619   MText *mt;
620   int pos;
621   MText *folded;
622   unsigned char *foldedp;
623   int folded_len;
624 };
625
626 static int
627 next_char_from_it (struct casecmp_iterator *it)
628 {
629   int c, c1;
630
631   if (it->folded)
632     {
633       c = STRING_CHAR_AND_BYTES (it->foldedp, it->folded_len);
634       return c;
635     }
636
637   c = mtext_ref_char (it->mt, it->pos);
638   c1 = (int) mchar_get_prop (c, Msimple_case_folding);
639   if (c1 == 0xFFFF)
640     {
641       it->folded
642         = (MText *) mchar_get_prop (c, Mcomplicated_case_folding);
643       it->foldedp = it->folded->data;
644       c = STRING_CHAR_AND_BYTES (it->foldedp, it->folded_len);
645       return c;
646     }
647
648   if (c1 >= 0)
649     c = c1;
650   return c;
651 }
652
653 static void
654 advance_it (struct casecmp_iterator *it)
655 {
656   if (it->folded)
657     {
658       it->foldedp += it->folded_len;
659       if (it->foldedp == it->folded->data + it->folded->nbytes)
660         it->folded = NULL;
661     }
662   if (! it->folded)
663     {
664       it->pos++;
665     }
666 }
667
668 static int
669 case_compare (MText *mt1, int from1, int to1, MText *mt2, int from2, int to2)
670 {
671   struct casecmp_iterator it1, it2;
672
673   it1.mt = mt1, it1.pos = from1, it1.folded = NULL;
674   it2.mt = mt2, it2.pos = from2, it2.folded = NULL;
675
676   while (it1.pos < to1 && it2.pos < to2)
677     {
678       int c1 = next_char_from_it (&it1);
679       int c2 = next_char_from_it (&it2);
680
681       if (c1 != c2)
682         return (c1 > c2 ? 1 : -1);
683       advance_it (&it1);
684       advance_it (&it2);
685     }
686   return (it2.pos == to2 ? (it1.pos < to1) : -1);
687 }
688
689 static MCharTable *tricky_chars, *cased, *soft_dotted, *case_mapping;
690 static MCharTable *combining_class;
691
692 /* Languages that require special handling in case-conversion.  */
693 static MSymbol Mlt, Mtr, Maz;
694
695 static MText *gr03A3;
696 static MText *lt0049, *lt004A, *lt012E, *lt00CC, *lt00CD, *lt0128;
697 static MText *tr0130, *tr0049, *tr0069;
698
699 static int
700 init_case_conversion ()
701 {
702   Mlt = msymbol ("lt");
703   Mtr = msymbol ("tr");
704   Maz = msymbol ("az");
705
706   gr03A3 = mtext ();
707   mtext_cat_char (gr03A3, 0x03C2);
708
709   lt0049 = mtext ();
710   mtext_cat_char (lt0049, 0x0069);
711   mtext_cat_char (lt0049, 0x0307);
712
713   lt004A = mtext ();
714   mtext_cat_char (lt004A, 0x006A);
715   mtext_cat_char (lt004A, 0x0307);
716
717   lt012E = mtext ();
718   mtext_cat_char (lt012E, 0x012F);
719   mtext_cat_char (lt012E, 0x0307);
720
721   lt00CC = mtext ();
722   mtext_cat_char (lt00CC, 0x0069);
723   mtext_cat_char (lt00CC, 0x0307);
724   mtext_cat_char (lt00CC, 0x0300);
725
726   lt00CD = mtext ();
727   mtext_cat_char (lt00CD, 0x0069);
728   mtext_cat_char (lt00CD, 0x0307);
729   mtext_cat_char (lt00CD, 0x0301);
730
731   lt0128 = mtext ();
732   mtext_cat_char (lt0128, 0x0069);
733   mtext_cat_char (lt0128, 0x0307);
734   mtext_cat_char (lt0128, 0x0303);
735
736   tr0130 = mtext ();
737   mtext_cat_char (tr0130, 0x0069);
738
739   tr0049 = mtext ();
740   mtext_cat_char (tr0049, 0x0131);
741
742   tr0069 = mtext ();
743   mtext_cat_char (tr0069, 0x0130);
744
745   if (! (cased = mchar_get_prop_table (msymbol ("cased"), NULL)))
746     return -1;
747   if (! (soft_dotted = mchar_get_prop_table (msymbol ("soft-dotted"), NULL)))
748     return -1;
749   if (! (case_mapping = mchar_get_prop_table (msymbol ("case-mapping"), NULL)))
750     return -1;
751   if (! (combining_class = mchar_get_prop_table (Mcombining_class, NULL)))
752     return -1;
753
754   tricky_chars = mchartable (Mnil, 0);
755   mchartable_set (tricky_chars, 0x0049, (void *) 1);
756   mchartable_set (tricky_chars, 0x004A, (void *) 1);
757   mchartable_set (tricky_chars, 0x00CC, (void *) 1);
758   mchartable_set (tricky_chars, 0x00CD, (void *) 1);
759   mchartable_set (tricky_chars, 0x0128, (void *) 1);
760   mchartable_set (tricky_chars, 0x012E, (void *) 1);
761   mchartable_set (tricky_chars, 0x0130, (void *) 1);
762   mchartable_set (tricky_chars, 0x0307, (void *) 1);
763   mchartable_set (tricky_chars, 0x03A3, (void *) 1);
764   return 0;
765 }
766
767 #define CASE_CONV_INIT(ret)             \
768   do {                                  \
769     if (! tricky_chars                  \
770         && init_case_conversion () < 0) \
771       MERROR (MERROR_MTEXT, ret);       \
772   } while (0)
773
774
775 /* Replace the character at I of MT with VAR, increment I and LEN,
776    and set MODIFIED to 1.  */
777
778 #define REPLACE(var)                                    \
779   do {                                                  \
780     int varlen = mtext_nchars (var);                    \
781                                                         \
782     mtext_replace (mt, i, i + 1, var, 0, varlen);       \
783     i += varlen;                                        \
784     len += varlen - 1;                                  \
785     modified = 1;                                       \
786   } while (0)
787
788 /* Delete the character at I of MT, decrement LEN,
789    and set MODIFIED to 1.  */
790
791 #define DELETE()                \
792   do {                          \
793     mtext_del (mt, i, i + 1);   \
794     len--;                      \
795     modified = 1;               \
796   } while (0)
797
798 #define LOOKUP()                                                \
799   do {                                                          \
800     MPlist *pl = mchartable_lookup (case_mapping, c);           \
801                                                                 \
802     if (pl)                                                     \
803       {                                                         \
804         /* Lowercase is the 1st element. */                     \
805         MText *lower = MPLIST_VAL ((MPlist *) MPLIST_VAL (pl)); \
806         int llen = mtext_nchars (lower);                        \
807                                                                 \
808         if (mtext_ref_char (lower, 0) != c || llen > 1)         \
809           {                                                     \
810             mtext_replace (mt, i, i + 1, lower, 0, llen);       \
811             i += llen;                                          \
812             len += llen - 1;                                    \
813             modified = 1;                                       \
814           }                                                     \
815         else                                                    \
816           i++;                                                  \
817       }                                                         \
818     else                                                        \
819       i++;                                                      \
820   } while (0)
821
822
823 int
824 uppercase_precheck (MText *mt)
825 {
826   int len = mtext_nchars (mt), i;
827
828   for (i = 0; i < len; i++)
829     if (mtext_ref_char (mt, i) == 0x0307 &&
830         (MSymbol) mtext_get_prop (mt, i, Mlanguage) == Mlt)
831       return 1;
832   return 0;
833 }
834
835 int
836 lowercase_precheck (MText *mt, int from, int to)
837 {
838   for (; from < to; from++)
839     {
840       int c = mtext_ref_char (mt, from);
841
842       if ((int) mchartable_lookup (tricky_chars, c) == 1)
843       {
844         MSymbol lang;
845
846         if (c == 0x03A3)
847           return 1;
848
849         lang = mtext_get_prop (mt, from, Mlanguage);
850
851         if (lang == Mlt &&
852             (c == 0x0049 || c == 0x004A || c == 0x012E ||
853              c == 0x00CC || c == 0x00CD || c == 0x0128))
854           return 1;
855
856         if ((lang == Mtr || lang == Maz) &&
857             (c == 0x0130 || c == 0x0307 || c == 0x0049))
858           return 1;
859       }
860     }
861   return 0;
862 }
863
864 #define CASED 1
865 #define CASE_IGNORABLE 2
866
867 int
868 final_sigma (MText *mt, int pos)
869 {
870   int i, len = mtext_len (mt);
871   int c;
872
873   for (i = pos - 1; i >= 0; i--)
874     {
875       c = (int) mchartable_lookup (cased, mtext_ref_char (mt, i));
876       if (c == -1)
877         c = 0;
878       if (c & CASED)
879         break;
880       if (! (c & CASE_IGNORABLE))
881         return 0;
882     }
883
884   if (i == -1)
885     return 0;
886
887   for (i = pos + 1; i < len; i++)
888     {
889       c = (int) mchartable_lookup (cased, mtext_ref_char (mt, i));
890       if (c == -1)
891         c = 0;
892       if (c & CASED)
893         return 0;
894       if (! (c & CASE_IGNORABLE))
895         return 1;
896     }
897
898   return 1;
899 }
900
901 int
902 after_soft_dotted (MText *mt, int i)
903 {
904   int c, class;
905
906   for (i--; i >= 0; i--)
907     {
908       c = mtext_ref_char (mt, i);
909       if ((MSymbol) mchartable_lookup (soft_dotted, c) == Mt)
910         return 1;
911       class = (int) mchartable_lookup (combining_class, c);
912       if (class == 0 || class == 230)
913         return 0;
914     }
915
916   return 0;
917 }
918
919 int
920 more_above (MText *mt, int i)
921 {
922   int class, len = mtext_len (mt);
923
924   for (i++; i < len; i++)
925     {
926       class = (int) mchartable_lookup (combining_class,
927                                        mtext_ref_char (mt, i));
928       if (class == 230)
929         return 1;
930       if (class == 0)
931         return 0;
932     }
933
934   return 0;
935 }
936
937 int
938 before_dot (MText *mt, int i)
939 {
940   int c, class, len = mtext_len (mt);
941
942   for (i++; i < len; i++)
943     {
944       c = mtext_ref_char (mt, i);
945       if (c == 0x0307)
946         return 1;
947       class = (int) mchartable_lookup (combining_class, c);
948       if (class == 230 || class == 0)
949         return 0;
950     }
951
952   return 0;
953 }
954
955 int
956 after_i (MText *mt, int i)
957 {
958   int c, class;
959
960   for (i--; i >= 0; i--)
961     {
962       c = mtext_ref_char (mt, i);
963       if (c == (int) 'I')
964         return 1;
965       class = (int) mchartable_lookup (combining_class, c);
966       if (class == 230 || class == 0)
967         return 0;
968     }
969
970   return 0;
971 }
972
973 \f
974 /* Internal API */
975
976 int
977 mtext__init ()
978 {
979   M17N_OBJECT_ADD_ARRAY (mtext_table, "M-text");
980   M_charbag = msymbol_as_managing_key ("  charbag");
981   mtext_table.count = 0;
982   return 0;
983 }
984
985
986 void
987 mtext__fini (void)
988 {
989   mtext__wseg_fini ();
990 }
991
992
993 int
994 mtext__char_to_byte (MText *mt, int pos)
995 {
996   int char_pos, byte_pos;
997   int forward;
998
999   if (pos < mt->cache_char_pos)
1000     {
1001       if (mt->cache_char_pos == mt->cache_byte_pos)
1002         return pos;
1003       if (pos < mt->cache_char_pos - pos)
1004         {
1005           char_pos = byte_pos = 0;
1006           forward = 1;
1007         }
1008       else
1009         {
1010           char_pos = mt->cache_char_pos;
1011           byte_pos = mt->cache_byte_pos;
1012           forward = 0;
1013         }
1014     }
1015   else
1016     {
1017       if (mt->nchars - mt->cache_char_pos == mt->nbytes - mt->cache_byte_pos)
1018         return (mt->cache_byte_pos + (pos - mt->cache_char_pos));
1019       if (pos - mt->cache_char_pos < mt->nchars - pos)
1020         {
1021           char_pos = mt->cache_char_pos;
1022           byte_pos = mt->cache_byte_pos;
1023           forward = 1;
1024         }
1025       else
1026         {
1027           char_pos = mt->nchars;
1028           byte_pos = mt->nbytes;
1029           forward = 0;
1030         }
1031     }
1032   if (forward)
1033     while (char_pos < pos)
1034       INC_POSITION (mt, char_pos, byte_pos);
1035   else
1036     while (char_pos > pos)
1037       DEC_POSITION (mt, char_pos, byte_pos);
1038   mt->cache_char_pos = char_pos;
1039   mt->cache_byte_pos = byte_pos;
1040   return byte_pos;
1041 }
1042
1043 /* mtext__byte_to_char () */
1044
1045 int
1046 mtext__byte_to_char (MText *mt, int pos_byte)
1047 {
1048   int char_pos, byte_pos;
1049   int forward;
1050
1051   if (pos_byte < mt->cache_byte_pos)
1052     {
1053       if (mt->cache_char_pos == mt->cache_byte_pos)
1054         return pos_byte;
1055       if (pos_byte < mt->cache_byte_pos - pos_byte)
1056         {
1057           char_pos = byte_pos = 0;
1058           forward = 1;
1059         }
1060       else
1061         {
1062           char_pos = mt->cache_char_pos;
1063           byte_pos = mt->cache_byte_pos;
1064           forward = 0;
1065         }
1066     }
1067   else
1068     {
1069       if (mt->nchars - mt->cache_char_pos == mt->nbytes - mt->cache_byte_pos)
1070         return (mt->cache_char_pos + (pos_byte - mt->cache_byte_pos));
1071       if (pos_byte - mt->cache_byte_pos < mt->nbytes - pos_byte)
1072         {
1073           char_pos = mt->cache_char_pos;
1074           byte_pos = mt->cache_byte_pos;
1075           forward = 1;
1076         }
1077       else
1078         {
1079           char_pos = mt->nchars;
1080           byte_pos = mt->nbytes;
1081           forward = 0;
1082         }
1083     }
1084   if (forward)
1085     while (byte_pos < pos_byte)
1086       INC_POSITION (mt, char_pos, byte_pos);
1087   else
1088     while (byte_pos > pos_byte)
1089       DEC_POSITION (mt, char_pos, byte_pos);
1090   mt->cache_char_pos = char_pos;
1091   mt->cache_byte_pos = byte_pos;
1092   return char_pos;
1093 }
1094
1095 /* Estimated extra bytes that malloc will use for its own purpose on
1096    each memory allocation.  */
1097 #define MALLOC_OVERHEAD 4
1098 #define MALLOC_MININUM_BYTES 12
1099
1100 void
1101 mtext__enlarge (MText *mt, int nbytes)
1102 {
1103   nbytes += MAX_UTF8_CHAR_BYTES;
1104   if (mt->allocated >= nbytes)
1105     return;
1106   if (nbytes < MALLOC_MININUM_BYTES)
1107     nbytes = MALLOC_MININUM_BYTES;
1108   while (mt->allocated < nbytes)
1109     mt->allocated = mt->allocated * 2 + MALLOC_OVERHEAD;
1110   MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT);
1111 }
1112
1113 int
1114 mtext__takein (MText *mt, int nchars, int nbytes)
1115 {
1116   if (mt->plist)
1117     mtext__adjust_plist_for_insert (mt, mt->nchars, nchars, NULL);
1118   mt->nchars += nchars;
1119   mt->nbytes += nbytes;
1120   mt->data[mt->nbytes] = 0;
1121   return 0;
1122 }
1123
1124
1125 int
1126 mtext__cat_data (MText *mt, unsigned char *p, int nbytes,
1127                  enum MTextFormat format)
1128 {
1129   int nchars = -1;
1130
1131   if (mt->format > MTEXT_FORMAT_UTF_8)
1132     MERROR (MERROR_MTEXT, -1);
1133   if (format == MTEXT_FORMAT_US_ASCII)
1134     nchars = nbytes;
1135   else if (format == MTEXT_FORMAT_UTF_8)
1136     nchars = count_utf_8_chars (p, nbytes);
1137   if (nchars < 0)
1138     MERROR (MERROR_MTEXT, -1);
1139   mtext__enlarge (mt, mtext_nbytes (mt) + nbytes + 1);
1140   memcpy (MTEXT_DATA (mt) + mtext_nbytes (mt), p, nbytes);
1141   mtext__takein (mt, nchars, nbytes);
1142   return nchars;
1143 }
1144
1145 MText *
1146 mtext__from_data (const void *data, int nitems, enum MTextFormat format,
1147                   int need_copy)
1148 {
1149   MText *mt;
1150   int nchars, nbytes, unit_bytes;
1151
1152   if (format == MTEXT_FORMAT_US_ASCII)
1153     {
1154       const char *p = (char *) data, *pend = p + nitems;
1155
1156       while (p < pend)
1157         if (*p++ < 0)
1158           MERROR (MERROR_MTEXT, NULL);
1159       nchars = nbytes = nitems;
1160       unit_bytes = 1;
1161     }
1162   else if (format == MTEXT_FORMAT_UTF_8)
1163     {
1164       if ((nchars = count_utf_8_chars (data, nitems)) < 0)
1165         MERROR (MERROR_MTEXT, NULL);
1166       nbytes = nitems;
1167       unit_bytes = 1;
1168     }
1169   else if (format <= MTEXT_FORMAT_UTF_16BE)
1170     {
1171       if ((nchars = count_utf_16_chars (data, nitems,
1172                                         format != MTEXT_FORMAT_UTF_16)) < 0)
1173         MERROR (MERROR_MTEXT, NULL);
1174       nbytes = USHORT_SIZE * nitems;
1175       unit_bytes = USHORT_SIZE;
1176     }
1177   else                          /* MTEXT_FORMAT_UTF_32XX */
1178     {
1179       nchars = nitems;
1180       nbytes = UINT_SIZE * nitems;
1181       unit_bytes = UINT_SIZE;
1182     }
1183
1184   mt = mtext ();
1185   mt->format = format;
1186   mt->coverage = FORMAT_COVERAGE (format);
1187   mt->allocated = need_copy ? nbytes + unit_bytes : -1;
1188   mt->nchars = nchars;
1189   mt->nbytes = nitems;
1190   if (need_copy)
1191     {
1192       MTABLE_MALLOC (mt->data, mt->allocated, MERROR_MTEXT);
1193       memcpy (mt->data, data, nbytes);
1194       mt->data[nbytes] = 0;
1195     }
1196   else
1197     mt->data = (unsigned char *) data;
1198   return mt;
1199 }
1200
1201
1202 void
1203 mtext__adjust_format (MText *mt, enum MTextFormat format)
1204 {
1205   int i, c;
1206
1207   if (mt->nchars > 0)
1208     switch (format)
1209       {
1210       case MTEXT_FORMAT_US_ASCII:
1211         {
1212           unsigned char *p = mt->data;
1213
1214           for (i = 0; i < mt->nchars; i++)
1215             *p++ = mtext_ref_char (mt, i);
1216           mt->nbytes = mt->nchars;
1217           mt->cache_byte_pos = mt->cache_char_pos;
1218           break;
1219         }
1220
1221       case MTEXT_FORMAT_UTF_8:
1222         {
1223           unsigned char *p0, *p1;
1224
1225           i = count_by_utf_8 (mt, 0, mt->nchars) + 1;
1226           MTABLE_MALLOC (p0, i, MERROR_MTEXT);
1227           mt->allocated = i;
1228           for (i = 0, p1 = p0; i < mt->nchars; i++)
1229             {
1230               c = mtext_ref_char (mt, i);
1231               p1 += CHAR_STRING_UTF8 (c, p1);
1232             }
1233           *p1 = '\0';
1234           free (mt->data);
1235           mt->data = p0;
1236           mt->nbytes = p1 - p0;
1237           mt->cache_char_pos = mt->cache_byte_pos = 0;
1238           break;
1239         }
1240
1241       default:
1242         if (format == MTEXT_FORMAT_UTF_16)
1243           {
1244             unsigned short *p0, *p1;
1245
1246             i = (count_by_utf_16 (mt, 0, mt->nchars) + 1) * USHORT_SIZE;
1247             MTABLE_MALLOC (p0, i, MERROR_MTEXT);
1248             mt->allocated = i;
1249             for (i = 0, p1 = p0; i < mt->nchars; i++)
1250               {
1251                 c = mtext_ref_char (mt, i);
1252                 p1 += CHAR_STRING_UTF16 (c, p1);
1253               }
1254             *p1 = 0;
1255             free (mt->data);
1256             mt->data = (unsigned char *) p0;
1257             mt->nbytes = p1 - p0;
1258             mt->cache_char_pos = mt->cache_byte_pos = 0;
1259             break;
1260           }
1261         else
1262           {
1263             unsigned int *p;
1264
1265             mt->allocated = (mt->nchars + 1) * UINT_SIZE;
1266             MTABLE_MALLOC (p, mt->allocated, MERROR_MTEXT);
1267             for (i = 0; i < mt->nchars; i++)
1268               p[i] = mtext_ref_char (mt, i);
1269             p[i] = 0;
1270             free (mt->data);
1271             mt->data = (unsigned char *) p;
1272             mt->nbytes = mt->nchars;
1273             mt->cache_byte_pos = mt->cache_char_pos;
1274           }
1275       }
1276   mt->format = format;
1277   mt->coverage = FORMAT_COVERAGE (format);
1278 }
1279
1280
1281 /* Find the position of a character at the beginning of a line of
1282    M-Text MT searching backward from POS.  */
1283
1284 int
1285 mtext__bol (MText *mt, int pos)
1286 {
1287   int byte_pos;
1288
1289   if (pos == 0)
1290     return pos;
1291   byte_pos = POS_CHAR_TO_BYTE (mt, pos);
1292   if (mt->format <= MTEXT_FORMAT_UTF_8)
1293     {
1294       unsigned char *p = mt->data + byte_pos;
1295
1296       if (p[-1] == '\n')
1297         return pos;
1298       p--;
1299       while (p > mt->data && p[-1] != '\n')
1300         p--;
1301       if (p == mt->data)
1302         return 0;
1303       byte_pos = p - mt->data;
1304       return POS_BYTE_TO_CHAR (mt, byte_pos);
1305     }
1306   else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
1307     {
1308       unsigned short *p = ((unsigned short *) (mt->data)) + byte_pos;
1309       unsigned short newline = (mt->format == MTEXT_FORMAT_UTF_16
1310                                 ? 0x0A00 : 0x000A);
1311
1312       if (p[-1] == newline)
1313         return pos;
1314       p--;
1315       while (p > (unsigned short *) (mt->data) && p[-1] != newline)
1316         p--;
1317       if (p == (unsigned short *) (mt->data))
1318         return 0;
1319       byte_pos = p - (unsigned short *) (mt->data);
1320       return POS_BYTE_TO_CHAR (mt, byte_pos);;
1321     }
1322   else
1323     {
1324       unsigned *p = ((unsigned *) (mt->data)) + byte_pos;
1325       unsigned newline = (mt->format == MTEXT_FORMAT_UTF_32
1326                           ? 0x0A000000 : 0x0000000A);
1327
1328       if (p[-1] == newline)
1329         return pos;
1330       p--, pos--;
1331       while (p > (unsigned *) (mt->data) && p[-1] != newline)
1332         p--, pos--;
1333       return pos;
1334     }
1335 }
1336
1337
1338 /* Find the position of a character at the end of a line of M-Text MT
1339    searching forward from POS.  */
1340
1341 int
1342 mtext__eol (MText *mt, int pos)
1343 {
1344   int byte_pos;
1345
1346   if (pos == mt->nchars)
1347     return pos;
1348   byte_pos = POS_CHAR_TO_BYTE (mt, pos);
1349   if (mt->format <= MTEXT_FORMAT_UTF_8)
1350     {
1351       unsigned char *p = mt->data + byte_pos;
1352       unsigned char *endp;
1353
1354       if (*p == '\n')
1355         return pos + 1;
1356       p++;
1357       endp = mt->data + mt->nbytes;
1358       while (p < endp && *p != '\n')
1359         p++;
1360       if (p == endp)
1361         return mt->nchars;
1362       byte_pos = p + 1 - mt->data;
1363       return POS_BYTE_TO_CHAR (mt, byte_pos);
1364     }
1365   else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
1366     {
1367       unsigned short *p = ((unsigned short *) (mt->data)) + byte_pos;
1368       unsigned short *endp;
1369       unsigned short newline = (mt->format == MTEXT_FORMAT_UTF_16
1370                                 ? 0x0A00 : 0x000A);
1371
1372       if (*p == newline)
1373         return pos + 1;
1374       p++;
1375       endp = (unsigned short *) (mt->data) + mt->nbytes;
1376       while (p < endp && *p != newline)
1377         p++;
1378       if (p == endp)
1379         return mt->nchars;
1380       byte_pos = p + 1 - (unsigned short *) (mt->data);
1381       return POS_BYTE_TO_CHAR (mt, byte_pos);
1382     }
1383   else
1384     {
1385       unsigned *p = ((unsigned *) (mt->data)) + byte_pos;
1386       unsigned *endp;
1387       unsigned newline = (mt->format == MTEXT_FORMAT_UTF_32
1388                           ? 0x0A000000 : 0x0000000A);
1389
1390       if (*p == newline)
1391         return pos + 1;
1392       p++, pos++;
1393       endp = (unsigned *) (mt->data) + mt->nbytes;
1394       while (p < endp && *p != newline)
1395         p++, pos++;
1396       return pos;
1397     }
1398 }
1399
1400 int
1401 mtext__lowercase (MText *mt, int from, int to)
1402
1403 {
1404   int i, j, len = to - from;
1405   int c;
1406   int modified = 0;
1407   MText *orig;
1408   MSymbol lang;
1409
1410   if (lowercase_precheck (mt, from, to))
1411     orig = mtext_duplicate (mt, from, to);
1412
1413   /* i moves over mt, j moves over orig. */
1414   for (i = from, j = 0; i < len; j++)
1415     {
1416       c = mtext_ref_char (mt, i);
1417       lang = (MSymbol) mtext_get_prop (mt, i, Mlanguage);
1418
1419       if (c == 0x03A3 && final_sigma (orig, j))
1420         REPLACE (gr03A3);
1421
1422       else if (lang == Mlt)
1423         {
1424           if (c == 0x00CC)
1425             REPLACE (lt00CC);
1426           else if (c == 0x00CD)
1427             REPLACE (lt00CD);
1428           else if (c == 0x0128)
1429             REPLACE (lt0128);
1430           else if (orig && more_above (orig, j))
1431             {
1432               if (c == 0x0049)
1433                 REPLACE (lt0049);
1434               else if (c == 0x004A)
1435                 REPLACE (lt004A);
1436               else if (c == 0x012E)
1437                 REPLACE (lt012E);
1438               else
1439                 LOOKUP ();
1440             }
1441           else
1442             LOOKUP ();
1443         }
1444
1445       else if (lang == Mtr || lang == Maz)
1446         {
1447           if (c == 0x0130)
1448             REPLACE (tr0130);
1449           else if (c == 0x0307 && after_i (orig, j))
1450             DELETE ();
1451           else if (c == 0x0049 && ! before_dot (orig, j))
1452             REPLACE (tr0049);
1453           else
1454             LOOKUP ();
1455         }
1456
1457       else
1458         LOOKUP ();
1459     }
1460
1461   return modified;
1462 }
1463
1464 /*** @} */
1465 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1466
1467 \f
1468 /* External API */
1469
1470 #ifdef WORDS_BIGENDIAN
1471 const enum MTextFormat MTEXT_FORMAT_UTF_16 = MTEXT_FORMAT_UTF_16BE;
1472 #else
1473 const enum MTextFormat MTEXT_FORMAT_UTF_16 = MTEXT_FORMAT_UTF_16LE;
1474 #endif
1475
1476 #ifdef WORDS_BIGENDIAN
1477 const int MTEXT_FORMAT_UTF_32 = MTEXT_FORMAT_UTF_32BE;
1478 #else
1479 const int MTEXT_FORMAT_UTF_32 = MTEXT_FORMAT_UTF_32LE;
1480 #endif
1481
1482 /*** @addtogroup m17nMtext */
1483 /*** @{ */
1484 /*=*/
1485
1486 /***en
1487     @brief Allocate a new M-text.
1488
1489     The mtext () function allocates a new M-text of length 0 and
1490     returns a pointer to it.  The allocated M-text will not be freed
1491     unless the user explicitly does so with the m17n_object_unref ()
1492     function.  */
1493
1494 /***ja
1495     @brief ¿·¤·¤¤M-text¤ò³ä¤êÅö¤Æ¤ë.
1496
1497     ´Ø¿ô mtext () ¤Ï¡¢Ä¹¤µ 0 ¤Î¿·¤·¤¤ M-text 
1498     ¤ò³ä¤êÅö¤Æ¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£³ä¤êÅö¤Æ¤é¤ì¤¿ M-text ¤Ï¡¢´Ø¿ô
1499     m17n_object_unref () ¤Ë¤è¤Ã¤Æ¥æ¡¼¥¶¤¬ÌÀ¼¨Åª¤Ë¹Ô¤Ê¤ï¤Ê¤¤¸Â¤ê¡¢²òÊü¤µ¤ì¤Ê¤¤¡£
1500
1501     @latexonly \IPAlabel{mtext} @endlatexonly  */
1502
1503 /***
1504     @seealso
1505     m17n_object_unref ()  */
1506
1507 MText *
1508 mtext ()
1509 {
1510   MText *mt;
1511
1512   M17N_OBJECT (mt, free_mtext, MERROR_MTEXT);
1513   mt->format = MTEXT_FORMAT_US_ASCII;
1514   mt->coverage = MTEXT_COVERAGE_ASCII;
1515   M17N_OBJECT_REGISTER (mtext_table, mt);
1516   return mt;
1517 }
1518
1519 /***en
1520     @brief Allocate a new M-text with specified data.
1521
1522     The mtext_from_data () function allocates a new M-text whose
1523     character sequence is specified by array $DATA of $NITEMS
1524     elements.  $FORMAT specifies the format of $DATA.
1525
1526     When $FORMAT is either #MTEXT_FORMAT_US_ASCII or
1527     #MTEXT_FORMAT_UTF_8, the contents of $DATA must be of the type @c
1528     unsigned @c char, and $NITEMS counts by byte.
1529
1530     When $FORMAT is either #MTEXT_FORMAT_UTF_16LE or
1531     #MTEXT_FORMAT_UTF_16BE, the contents of $DATA must be of the type
1532     @c unsigned @c short, and $NITEMS counts by unsigned short.
1533
1534     When $FORMAT is either #MTEXT_FORMAT_UTF_32LE or
1535     #MTEXT_FORMAT_UTF_32BE, the contents of $DATA must be of the type
1536     @c unsigned, and $NITEMS counts by unsigned.
1537
1538     The character sequence of the M-text is not modifiable.  
1539     The contents of $DATA must not be modified while the M-text is alive.
1540
1541     The allocated M-text will not be freed unless the user explicitly
1542     does so with the m17n_object_unref () function.  Even in that case,
1543     $DATA is not freed.
1544
1545     @return
1546     If the operation was successful, mtext_from_data () returns a
1547     pointer to the allocated M-text.  Otherwise it returns @c NULL and
1548     assigns an error code to the external variable #merror_code.  */
1549 /***ja
1550     @brief »ØÄê¤Î¥Ç¡¼¥¿¤ò¸µ¤Ë¿·¤·¤¤ M-text ¤ò³ä¤êÅö¤Æ¤ë.
1551
1552     ´Ø¿ô mtext_from_data () ¤Ï¡¢Í×ÁÇ¿ô $NITEMS ¤ÎÇÛÎó $DATA 
1553     ¤Ç»ØÄꤵ¤ì¤¿Ê¸»úÎó¤ò»ý¤Ä¿·¤·¤¤ M-text ¤ò³ä¤êÅö¤Æ¤ë¡£$FORMAT ¤Ï $DATA
1554     ¤Î¥Õ¥©¡¼¥Þ¥Ã¥È¤ò¼¨¤¹¡£
1555
1556     $FORMAT ¤¬ #MTEXT_FORMAT_US_ASCII ¤« #MTEXT_FORMAT_UTF_8 ¤Ê¤é¤Ð¡¢
1557     $DATA ¤ÎÆâÍƤϠ@c unsigned @c char ·¿¤Ç¤¢¤ê¡¢$NITEMS 
1558     ¤Ï¥Ð¥¤¥Èñ°Ì¤Çɽ¤µ¤ì¤Æ¤¤¤ë¡£
1559
1560     $FORMAT ¤¬ #MTEXT_FORMAT_UTF_16LE ¤« #MTEXT_FORMAT_UTF_16BE ¤Ê¤é¤Ð¡¢
1561     $DATA ¤ÎÆâÍƤϠ@c unsigned @c short ·¿¤Ç¤¢¤ê¡¢$NITEMS ¤Ï unsigned
1562     short Ã±°Ì¤Ç¤¢¤ë¡£
1563
1564     $FORMAT ¤¬ #MTEXT_FORMAT_UTF_32LE ¤« #MTEXT_FORMAT_UTF_32BE ¤Ê¤é¤Ð¡¢
1565     $DATA ¤ÎÆâÍƤÏ@c unsigned ·¿¤Ç¤¢¤ê¡¢$NITEMS ¤Ï unsigned Ã±°Ì¤Ç¤¢¤ë¡£
1566
1567     ³ä¤êÅö¤Æ¤é¤ì¤¿ M-text ¤Îʸ»úÎó¤ÏÊѹ¹¤Ç¤­¤Ê¤¤¡£$DATA ¤ÎÆâÍƤϠ
1568     M-text ¤¬Í­¸ú¤Ê´Ö¤ÏÊѹ¹¤·¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1569
1570     ³ä¤êÅö¤Æ¤é¤ì¤¿ M-text ¤Ï¡¢´Ø¿ô m17n_object_unref () 
1571     ¤Ë¤è¤Ã¤Æ¥æ¡¼¥¶¤¬ÌÀ¼¨Åª¤Ë¹Ô¤Ê¤ï¤Ê¤¤¸Â¤ê¡¢²òÊü¤µ¤ì¤Ê¤¤¡£¤½¤Î¾ì¹ç¤Ç¤â $DATA ¤Ï²òÊü¤µ¤ì¤Ê¤¤¡£
1572
1573     @return 
1574     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_from_data () ¤Ï³ä¤êÅö¤Æ¤é¤ì¤¿M-text 
1575     ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·³°ÉôÊÑ¿ô #merror_code 
1576     ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
1577
1578 /***
1579     @errors
1580     @c MERROR_MTEXT  */
1581
1582 MText *
1583 mtext_from_data (const void *data, int nitems, enum MTextFormat format)
1584 {
1585   if (nitems < 0
1586       || format < MTEXT_FORMAT_US_ASCII || format >= MTEXT_FORMAT_MAX)
1587     MERROR (MERROR_MTEXT, NULL);
1588   return mtext__from_data (data, nitems, format, 0);
1589 }
1590
1591 /*=*/
1592
1593 /***en
1594     @brief Get information about the text data in M-text.
1595
1596     The mtext_data () function returns a pointer to the text data of
1597     M-text $MT.  If $FMT is not NULL, the format of the text data is
1598     stored in it.  If $NUNITS is not NULL, the number of units of the
1599     text data is stored in it.
1600
1601     If $POS_IDX is not NULL and it points to a non-negative number,
1602     what it points to is a character position.  In this case, the
1603     return value is a pointer to the text data of a character at that
1604     position.
1605
1606     Otherwise, if $UNIT_IDX is not NULL, it points to a unit position.
1607     In this case, the return value is a pointer to the text data of a
1608     character containing that unit.
1609
1610     The character position and unit position of the return value are
1611     stored in $POS_IDX and $UNIT_DIX respectively if they are not
1612     NULL.
1613
1614     <ul>
1615
1616     <li> If the format of the text data is MTEXT_FORMAT_US_ASCII or
1617     MTEXT_FORMAT_UTF_8, one unit is unsigned char.
1618
1619     <li> If the format is MTEXT_FORMAT_UTF_16LE or
1620     MTEXT_FORMAT_UTF_16BE, one unit is unsigned short.
1621
1622     <li> If the format is MTEXT_FORMAT_UTF_32LE or
1623     MTEXT_FORMAT_UTF_32BE, one unit is unsigned int.
1624
1625     </ul> */
1626
1627 void *
1628 mtext_data (MText *mt, enum MTextFormat *fmt, int *nunits,
1629             int *pos_idx, int *unit_idx)
1630 {
1631   void *data;
1632   int pos = 0, unit_pos = 0;
1633
1634   if (fmt)
1635     *fmt = mt->format;
1636   data = MTEXT_DATA (mt);
1637   if (pos_idx && *pos_idx >= 0)
1638     {
1639       pos = *pos_idx;
1640       if (pos > mtext_nchars (mt))
1641         MERROR (MERROR_MTEXT, NULL);
1642       unit_pos = POS_CHAR_TO_BYTE (mt, pos);
1643     }
1644   else if (unit_idx)
1645     {
1646       unit_pos = *unit_idx;
1647
1648       if (unit_pos < 0 || unit_pos > mtext_nbytes (mt))
1649         MERROR (MERROR_MTEXT, NULL);
1650       pos = POS_BYTE_TO_CHAR (mt, unit_pos);
1651       unit_pos = POS_CHAR_TO_BYTE (mt, pos);
1652     }
1653   if (nunits)
1654     *nunits = mtext_nbytes (mt) - unit_pos;
1655   if (pos_idx)
1656     *pos_idx = pos;
1657   if (unit_idx)
1658     *unit_idx = unit_pos;
1659   if (unit_pos > 0)
1660     {
1661       if (mt->format <= MTEXT_FORMAT_UTF_8)
1662         data = (unsigned char *) data + unit_pos;
1663       else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
1664         data = (unsigned short *) data + unit_pos;
1665       else
1666         data = (unsigned int *) data + unit_pos;
1667     }
1668   return data;
1669 }
1670
1671 /*=*/
1672
1673 /***en
1674     @brief Number of characters in M-text.
1675
1676     The mtext_len () function returns the number of characters in
1677     M-text $MT.  */
1678
1679 /***ja
1680     @brief M-text Ãæ¤Îʸ»ú¤Î¿ô.
1681
1682     ´Ø¿ô mtext_len () ¤Ï M-text $MT Ãæ¤Îʸ»ú¤Î¿ô¤òÊÖ¤¹¡£
1683
1684     @latexonly \IPAlabel{mtext_len} @endlatexonly  */
1685
1686 int
1687 mtext_len (MText *mt)
1688 {
1689   return (mt->nchars);
1690 }
1691
1692 /*=*/
1693
1694 /***en
1695     @brief Return the character at the specified position in an M-text.
1696
1697     The mtext_ref_char () function returns the character at $POS in
1698     M-text $MT.  If an error is detected, it returns -1 and assigns an
1699     error code to the external variable #merror_code.  */
1700
1701 /***ja
1702     @brief M-text Ãæ¤Î»ØÄꤵ¤ì¤¿°ÌÃÖ¤Îʸ»ú¤òÊÖ¤¹.
1703
1704     ´Ø¿ô mtext_ref_char () ¤Ï¡¢M-text $MT ¤Î°ÌÃÖ $POS 
1705     ¤Îʸ»ú¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code
1706     ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1707
1708     @latexonly \IPAlabel{mtext_ref_char} @endlatexonly  */
1709
1710 /***
1711     @errors
1712     @c MERROR_RANGE  */
1713
1714 int
1715 mtext_ref_char (MText *mt, int pos)
1716 {
1717   int c;
1718
1719   M_CHECK_POS (mt, pos, -1);
1720   if (mt->format <= MTEXT_FORMAT_UTF_8)
1721     {
1722       unsigned char *p = mt->data + POS_CHAR_TO_BYTE (mt, pos);
1723
1724       c = STRING_CHAR_UTF8 (p);
1725     }
1726   else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
1727     {
1728       unsigned short *p
1729         = (unsigned short *) (mt->data) + POS_CHAR_TO_BYTE (mt, pos);
1730       unsigned short p1[2];
1731
1732       if (mt->format != MTEXT_FORMAT_UTF_16)
1733         {
1734           p1[0] = SWAP_16 (*p);
1735           if (p1[0] >= 0xD800 || p1[0] < 0xDC00)
1736             p1[1] = SWAP_16 (p[1]);
1737           p = p1;
1738         }
1739       c = STRING_CHAR_UTF16 (p);
1740     }
1741   else
1742     {
1743       c = ((unsigned *) (mt->data))[pos];
1744       if (mt->format != MTEXT_FORMAT_UTF_32)
1745         c = SWAP_32 (c);
1746     }
1747   return c;
1748 }
1749
1750 /*=*/
1751
1752 /***en
1753     @brief Store a character into an M-text.
1754
1755     The mtext_set_char () function sets character $C, which has no
1756     text properties, at $POS in M-text $MT.
1757
1758     @return
1759     If the operation was successful, mtext_set_char () returns 0.
1760     Otherwise it returns -1 and assigns an error code to the external
1761     variable #merror_code.  */
1762
1763 /***ja
1764     @brief M-text ¤Ë°ìʸ»ú¤òÀßÄꤹ¤ë.
1765
1766     ´Ø¿ô mtext_set_char () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£Ìµ¤·¤Îʸ»ú $C ¤ò 
1767     M-text $MT ¤Î°ÌÃÖ $POS ¤ËÀßÄꤹ¤ë¡£
1768
1769     @return
1770     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mtext_set_char () ¤Ï 0 ¤òÊÖ¤¹¡£¼ºÇÔ¤¹¤ì¤Ð -1 
1771     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
1772
1773     @latexonly \IPAlabel{mtext_set_char} @endlatexonly  */
1774
1775 /***
1776     @errors
1777     @c MERROR_RANGE */
1778
1779 int
1780 mtext_set_char (MText *mt, int pos, int c)
1781 {
1782   int pos_unit;
1783   int old_units, new_units;
1784   int delta;
1785   unsigned char *p;
1786   int unit_bytes;
1787
1788   M_CHECK_POS (mt, pos, -1);
1789   M_CHECK_READONLY (mt, -1);
1790
1791   mtext__adjust_plist_for_change (mt, pos, 1, 1);
1792
1793   if (mt->format <= MTEXT_FORMAT_UTF_8)
1794     {
1795       if (c >= 0x80)
1796         mt->format = MTEXT_FORMAT_UTF_8, mt->coverage = MTEXT_COVERAGE_FULL;
1797     }
1798   else if (mt->format <= MTEXT_FORMAT_UTF_16BE)
1799     {
1800       if (c >= 0x110000)
1801         mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
1802       else if (mt->format != MTEXT_FORMAT_UTF_16)
1803         mtext__adjust_format (mt, MTEXT_FORMAT_UTF_16);
1804     }
1805   else if (mt->format != MTEXT_FORMAT_UTF_32)
1806     mtext__adjust_format (mt, MTEXT_FORMAT_UTF_32);
1807
1808   unit_bytes = UNIT_BYTES (mt->format);
1809   pos_unit = POS_CHAR_TO_BYTE (mt, pos);
1810   p = mt->data + pos_unit * unit_bytes;
1811   old_units = CHAR_UNITS_AT (mt, p);
1812   new_units = CHAR_UNITS (c, mt->format);
1813   delta = new_units - old_units;
1814
1815   if (delta)
1816     {
1817       if (mt->cache_char_pos > pos)
1818         mt->cache_byte_pos += delta;
1819
1820       if ((mt->nbytes + delta + 1) * unit_bytes > mt->allocated)
1821         {
1822           mt->allocated = (mt->nbytes + delta + 1) * unit_bytes;
1823           MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT);
1824         }
1825
1826       memmove (mt->data + (pos_unit + new_units) * unit_bytes, 
1827                mt->data + (pos_unit + old_units) * unit_bytes,
1828                (mt->nbytes - pos_unit - old_units + 1) * unit_bytes);
1829       mt->nbytes += delta;
1830       mt->data[mt->nbytes * unit_bytes] = 0;
1831     }
1832   switch (mt->format)
1833     {
1834     case MTEXT_FORMAT_US_ASCII:
1835       mt->data[pos_unit] = c;
1836       break;
1837     case MTEXT_FORMAT_UTF_8:
1838       {
1839         unsigned char *p = mt->data + pos_unit;
1840         CHAR_STRING_UTF8 (c, p);
1841         break;
1842       }
1843     default:
1844       if (mt->format == MTEXT_FORMAT_UTF_16)
1845         {
1846           unsigned short *p = (unsigned short *) mt->data + pos_unit;
1847
1848           CHAR_STRING_UTF16 (c, p);
1849         }
1850       else
1851         ((unsigned *) mt->data)[pos_unit] = c;
1852     }
1853   return 0;
1854 }
1855
1856 /*=*/
1857
1858 /***en
1859     @brief  Append a character to an M-text.
1860
1861     The mtext_cat_char () function appends character $C, which has no
1862     text properties, to the end of M-text $MT.
1863
1864     @return
1865     This function returns a pointer to the resulting M-text $MT.  If
1866     $C is an invalid character, it returns @c NULL.  */
1867
1868 /***ja
1869     @brief M-text ¤Ë°ìʸ»úÄɲ乤ë.
1870
1871     ´Ø¿ô mtext_cat_char () ¤Ï¡¢¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£Ìµ¤·¤Îʸ»ú $C ¤ò 
1872     M-text $MT ¤ÎËöÈø¤ËÄɲ乤롣
1873
1874     @return
1875     ¤³¤Î´Ø¿ô¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£$C 
1876     ¤¬Àµ¤·¤¤Ê¸»ú¤Ç¤Ê¤¤¾ì¹ç¤Ë¤Ï @c NULL ¤òÊÖ¤¹¡£  */
1877
1878 /***
1879     @seealso
1880     mtext_cat (), mtext_ncat ()  */
1881
1882 MText *
1883 mtext_cat_char (MText *mt, int c)
1884 {
1885   int nunits;
1886   int unit_bytes = UNIT_BYTES (mt->format);
1887
1888   M_CHECK_READONLY (mt, NULL);
1889   if (c < 0 || c > MCHAR_MAX)
1890     return NULL;
1891   mtext__adjust_plist_for_insert (mt, mt->nchars, 1, NULL);
1892
1893   if (c >= 0x80
1894       && (mt->format == MTEXT_FORMAT_US_ASCII
1895           || (c >= 0x10000
1896               && (mt->format == MTEXT_FORMAT_UTF_16LE
1897                   || mt->format == MTEXT_FORMAT_UTF_16BE))))
1898
1899     {
1900       mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
1901       unit_bytes = 1;
1902     }
1903   else if (mt->format >= MTEXT_FORMAT_UTF_32LE)
1904     {
1905       if (mt->format != MTEXT_FORMAT_UTF_32)
1906         mtext__adjust_format (mt, MTEXT_FORMAT_UTF_32);
1907     }
1908   else if (mt->format >= MTEXT_FORMAT_UTF_16LE)
1909     {
1910       if (mt->format != MTEXT_FORMAT_UTF_16)
1911         mtext__adjust_format (mt, MTEXT_FORMAT_UTF_16);
1912     }
1913
1914   nunits = CHAR_UNITS (c, mt->format);
1915   if ((mt->nbytes + nunits + 1) * unit_bytes > mt->allocated)
1916     {
1917       mt->allocated = (mt->nbytes + nunits + 1) * unit_bytes;
1918       MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT);
1919     }
1920   
1921   if (mt->format <= MTEXT_FORMAT_UTF_8)
1922     {
1923       unsigned char *p = mt->data + mt->nbytes;
1924       p += CHAR_STRING_UTF8 (c, p);
1925       *p = 0;
1926     }
1927   else if (mt->format == MTEXT_FORMAT_UTF_16)
1928     {
1929       unsigned short *p = (unsigned short *) mt->data + mt->nbytes;
1930       p += CHAR_STRING_UTF16 (c, p);
1931       *p = 0;
1932     }
1933   else
1934     {
1935       unsigned *p = (unsigned *) mt->data + mt->nbytes;
1936       *p++ = c;
1937       *p = 0;
1938     }
1939
1940   mt->nchars++;
1941   mt->nbytes += nunits;
1942   return mt;
1943 }
1944
1945 /*=*/
1946
1947 /***en
1948     @brief  Create a copy of an M-text.
1949
1950     The mtext_dup () function creates a copy of M-text $MT while
1951     inheriting all the text properties of $MT.
1952
1953     @return
1954     This function returns a pointer to the created copy.  */
1955
1956 /***ja
1957     @brief M-text ¤Î¥³¥Ô¡¼¤òºî¤ë.
1958
1959     ´Ø¿ô mtext_dup () ¤Ï¡¢M-text $MT ¤Î¥³¥Ô¡¼¤òºî¤ë¡£$MT 
1960     ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£
1961
1962     @return
1963     ¤³¤Î´Ø¿ô¤Ïºî¤é¤ì¤¿¥³¥Ô¡¼¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1964
1965      @latexonly \IPAlabel{mtext_dup} @endlatexonly  */
1966
1967 /***
1968     @seealso
1969     mtext_duplicate ()  */
1970
1971 MText *
1972 mtext_dup (MText *mt)
1973 {
1974   return mtext_duplicate (mt, 0, mtext_nchars (mt));
1975 }
1976
1977 /*=*/
1978
1979 /***en
1980     @brief  Append an M-text to another.
1981
1982     The mtext_cat () function appends M-text $MT2 to the end of M-text
1983     $MT1 while inheriting all the text properties.  $MT2 itself is not
1984     modified.
1985
1986     @return
1987     This function returns a pointer to the resulting M-text $MT1.  */
1988
1989 /***ja
1990     @brief 2¸Ä¤Î M-text¤òÏ¢·ë¤¹¤ë.
1991
1992     ´Ø¿ô mtext_cat () ¤Ï¡¢ M-text $MT2 ¤ò M-text $MT1 
1993     ¤ÎËöÈø¤ËÉÕ¤±²Ã¤¨¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
1994
1995     @return
1996     ¤³¤Î´Ø¿ô¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1997
1998     @latexonly \IPAlabel{mtext_cat} @endlatexonly  */
1999
2000 /***
2001     @seealso
2002     mtext_ncat (), mtext_cat_char ()  */
2003
2004 MText *
2005 mtext_cat (MText *mt1, MText *mt2)
2006 {
2007   M_CHECK_READONLY (mt1, NULL);
2008
2009   if (mt2->nchars > 0)
2010     insert (mt1, mt1->nchars, mt2, 0, mt2->nchars);
2011   return mt1;
2012 }
2013
2014
2015 /*=*/
2016
2017 /***en
2018     @brief Append a part of an M-text to another.
2019
2020     The mtext_ncat () function appends the first $N characters of
2021     M-text $MT2 to the end of M-text $MT1 while inheriting all the
2022     text properties.  If the length of $MT2 is less than $N, all
2023     characters are copied.  $MT2 is not modified.  
2024
2025     @return 
2026     If the operation was successful, mtext_ncat () returns a
2027     pointer to the resulting M-text $MT1.  If an error is detected, it
2028     returns @c NULL and assigns an error code to the global variable
2029     #merror_code.  */
2030
2031 /***ja
2032     @brief M-text ¤Î°ìÉô¤òÊ̤ΠM-text ¤ËÉղ乤ë.
2033
2034     ´Ø¿ô mtext_ncat () ¤Ï¡¢M-text $MT2 ¤Î¤Ï¤¸¤á¤Î $N Ê¸»ú¤ò M-text
2035     $MT1 ¤ÎËöÈø¤ËÉÕ¤±²Ã¤¨¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT2
2036     ¤ÎŤµ¤¬ $N °Ê²¼¤Ê¤é¤Ð¡¢$MT2 ¤Î¤¹¤Ù¤Æ¤Îʸ»ú¤¬Éղ䵤ì¤ë¡£ $MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
2037
2038     @return
2039     ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mtext_ncat () ¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1
2040     ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô
2041     #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
2042
2043     @latexonly \IPAlabel{mtext_ncat} @endlatexonly  */
2044
2045 /***
2046     @errors
2047     @c MERROR_RANGE
2048
2049     @seealso
2050     mtext_cat (), mtext_cat_char ()  */
2051
2052 MText *
2053 mtext_ncat (MText *mt1, MText *mt2, int n)
2054 {
2055   M_CHECK_READONLY (mt1, NULL);
2056   if (n < 0)
2057     MERROR (MERROR_RANGE, NULL);
2058   if (mt2->nchars > 0)
2059     insert (mt1, mt1->nchars, mt2, 0, mt2->nchars < n ? mt2->nchars : n);
2060   return mt1;
2061 }
2062
2063
2064 /*=*/
2065
2066 /***en
2067     @brief Copy an M-text to another.
2068
2069     The mtext_cpy () function copies M-text $MT2 to M-text $MT1 while
2070     inheriting all the text properties.  The old text in $MT1 is
2071     overwritten and the length of $MT1 is extended if necessary.  $MT2
2072     is not modified.
2073
2074     @return
2075     This function returns a pointer to the resulting M-text $MT1.  */
2076
2077 /***ja
2078     @brief M-text ¤òÊ̤ΠM-text ¤Ë¥³¥Ô¡¼¤¹¤ë.
2079
2080     ´Ø¿ô mtext_cpy () ¤Ï M-text $MT2 ¤ò M-text $MT1 ¤Ë¾å½ñ¤­¥³¥Ô¡¼¤¹¤ë¡£
2081     $MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT1 
2082     ¤ÎŤµ¤ÏɬÍפ˱þ¤¸¤Æ¿­¤Ð¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
2083
2084     @return
2085     ¤³¤Î´Ø¿ô¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
2086
2087     @latexonly \IPAlabel{mtext_cpy} @endlatexonly  */
2088
2089 /***
2090     @seealso
2091     mtext_ncpy (), mtext_copy ()  */
2092
2093 MText *
2094 mtext_cpy (MText *mt1, MText *mt2)
2095 {
2096   M_CHECK_READONLY (mt1, NULL);
2097   mtext_del (mt1, 0, mt1->nchars);
2098   if (mt2->nchars > 0)
2099     insert (mt1, 0, mt2, 0, mt2->nchars);
2100   return mt1;
2101 }
2102
2103 /*=*/
2104
2105 /***en
2106     @brief Copy the first some characters in an M-text to another.
2107
2108     The mtext_ncpy () function copies the first $N characters of
2109     M-text $MT2 to M-text $MT1 while inheriting all the text
2110     properties.  If the length of $MT2 is less than $N, all characters
2111     of $MT2 are copied.  The old text in $MT1 is overwritten and the
2112     length of $MT1 is extended if necessary.  $MT2 is not modified.
2113
2114     @return
2115     If the operation was successful, mtext_ncpy () returns a pointer
2116     to the resulting M-text $MT1.  If an error is detected, it returns
2117     @c NULL and assigns an error code to the global variable 
2118     #merror_code.  */
2119
2120 /***ja
2121     @brief M-text ¤Ë´Þ¤Þ¤ì¤ëºÇ½é¤Î²¿Ê¸»ú¤«¤ò¥³¥Ô¡¼¤¹¤ë.
2122
2123     ´Ø¿ô mtext_ncpy () ¤Ï¡¢M-text $MT2 ¤ÎºÇ½é¤Î $N Ê¸»ú¤ò M-text $MT1 
2124     ¤Ë¾å½ñ¤­¥³¥Ô¡¼¤¹¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£¤â¤· $MT2
2125     ¤ÎŤµ¤¬ $N ¤è¤ê¤â¾®¤µ¤±¤ì¤Ð $MT2 ¤Î¤¹¤Ù¤Æ¤Îʸ»ú¤ò¥³¥Ô¡¼¤¹¤ë¡£$MT1 
2126     ¤ÎŤµ¤ÏɬÍפ˱þ¤¸¤Æ¿­¤Ð¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
2127
2128     @return 
2129     ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mtext_ncpy () ¤ÏÊѹ¹¤µ¤ì¤¿ M-text $MT1 
2130     ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô 
2131     #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
2132
2133     @latexonly \IPAlabel{mtext_ncpy} @endlatexonly  */
2134
2135 /***
2136     @errors
2137     @c MERROR_RANGE
2138
2139     @seealso
2140     mtext_cpy (), mtext_copy ()  */
2141
2142 MText *
2143 mtext_ncpy (MText *mt1, MText *mt2, int n)
2144 {
2145   M_CHECK_READONLY (mt1, NULL);
2146   if (n < 0)
2147     MERROR (MERROR_RANGE, NULL);
2148   mtext_del (mt1, 0, mt1->nchars);
2149   if (mt2->nchars > 0)
2150     insert (mt1, 0, mt2, 0, mt2->nchars < n ? mt2->nchars : n);
2151   return mt1;
2152 }
2153
2154 /*=*/
2155
2156 /***en
2157     @brief Create a new M-text from a part of an existing M-text.
2158
2159     The mtext_duplicate () function creates a copy of sub-text of
2160     M-text $MT, starting at $FROM (inclusive) and ending at $TO
2161     (exclusive) while inheriting all the text properties of $MT.  $MT
2162     itself is not modified.
2163
2164     @return If the operation was successful, mtext_duplicate ()
2165     returns a pointer to the created M-text.  If an error is detected,
2166     it returns NULL and assigns an error code to the external variable
2167     #merror_code.  */
2168
2169 /***ja
2170     @brief ´û¸¤Î M-text ¤Î°ìÉô¤«¤é¿·¤·¤¤ M-text ¤ò¤Ä¤¯¤ë.
2171
2172     ´Ø¿ô mtext_duplicate () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê$FROM ¼«ÂΤâ´Þ¤à¡Ë¤«¤é
2173     $TO ¡Ê$TO ¼«ÂΤϴޤޤʤ¤¡Ë¤Þ¤Ç¤ÎÉôʬ¤Î¥³¥Ô¡¼¤òºî¤ë¡£¤³¤Î¤È¤­ $MT 
2174     ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT ¤½¤Î¤â¤Î¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
2175
2176     @return
2177     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_duplicate () ¤Ïºî¤é¤ì¤¿ M-text 
2178     ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô 
2179     #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
2180
2181     @latexonly \IPAlabel{mtext_duplicate} @endlatexonly  */
2182
2183 /***
2184     @errors
2185     @c MERROR_RANGE
2186
2187     @seealso
2188     mtext_dup ()  */
2189
2190 MText *
2191 mtext_duplicate (MText *mt, int from, int to)
2192 {
2193   MText *new = mtext ();
2194
2195   M_CHECK_RANGE (mt, from, to, NULL, new);
2196   new->format = mt->format;
2197   new->coverage = mt->coverage;
2198   insert (new, 0, mt, from, to);
2199   return new;
2200 }
2201
2202 /*=*/
2203
2204 /***en
2205     @brief Copy characters in the specified range into an M-text.
2206
2207     The mtext_copy () function copies the text between $FROM
2208     (inclusive) and $TO (exclusive) in M-text $MT2 to the region
2209     starting at $POS in M-text $MT1 while inheriting the text
2210     properties.  The old text in $MT1 is overwritten and the length of
2211     $MT1 is extended if necessary.  $MT2 is not modified.
2212
2213     @return
2214     If the operation was successful, mtext_copy () returns a pointer
2215     to the modified $MT1.  Otherwise, it returns @c NULL and assigns
2216     an error code to the external variable #merror_code.  */
2217
2218 /***ja
2219     @brief M-text ¤Ë»ØÄêÈϰϤÎʸ»ú¤ò¥³¥Ô¡¼¤¹¤ë.
2220
2221     ´Ø¿ô mtext_copy () ¤Ï¡¢ M-text $MT2 ¤Î $FROM ¡Ê$FROM ¼«ÂΤâ´Þ¤à¡Ë¤«¤é 
2222     $TO ¡Ê$TO ¼«ÂΤϴޤޤʤ¤¡Ë¤Þ¤Ç¤ÎÈϰϤΥƥ­¥¹¥È¤ò M-text $MT1 ¤Î°ÌÃÖ $POS
2223     ¤«¤é¾å½ñ¤­¥³¥Ô¡¼¤¹¤ë¡£$MT2 ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT1 
2224     ¤ÎŤµ¤ÏɬÍפ˱þ¤¸¤Æ¿­¤Ð¤µ¤ì¤ë¡£$MT2 ¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
2225
2226     @latexonly \IPAlabel{mtext_copy} @endlatexonly
2227
2228     @return
2229     ½èÍý¤¬À®¸ù¤·¤¿¾ì¹ç¡¢mtext_copy () ¤ÏÊѹ¹¤µ¤ì¤¿ $MT1 
2230     ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code 
2231     ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
2232
2233 /***
2234     @errors
2235     @c MERROR_RANGE
2236
2237     @seealso
2238     mtext_cpy (), mtext_ncpy ()  */
2239
2240 MText *
2241 mtext_copy (MText *mt1, int pos, MText *mt2, int from, int to)
2242 {
2243   M_CHECK_POS_X (mt1, pos, NULL);
2244   M_CHECK_READONLY (mt1, NULL);
2245   M_CHECK_RANGE_X (mt2, from, to, NULL);
2246   mtext_del (mt1, pos, mt1->nchars);
2247   return insert (mt1, pos, mt2, from, to);
2248 }
2249
2250 /*=*/
2251
2252
2253 /***en
2254     @brief Delete characters in the specified range destructively.
2255
2256     The mtext_del () function deletes the characters in the range
2257     $FROM (inclusive) and $TO (exclusive) from M-text $MT
2258     destructively.  As a result, the length of $MT shrinks by ($TO -
2259     $FROM) characters.
2260
2261     @return
2262     If the operation was successful, mtext_del () returns 0.
2263     Otherwise, it returns -1 and assigns an error code to the external
2264     variable #merror_code.  */
2265
2266 /***ja
2267     @brief »ØÄêÈϰϤÎʸ»ú¤òÇ˲õŪ¤Ë¼è¤ê½ü¤¯.
2268
2269     ´Ø¿ô mtext_del () ¤Ï¡¢M-text $MT ¤Î $FROM ¡Ê$FROM ¼«ÂΤâ´Þ¤à¡Ë¤«¤é $TO
2270     ¡Ê$TO ¼«ÂΤϴޤޤʤ¤¡Ë¤Þ¤Ç¤Îʸ»ú¤òÇ˲õŪ¤Ë¼è¤ê½ü¤¯¡£·ë²ÌŪ¤Ë $MT ¤ÏŤµ¤¬ ($TO @c
2271     - $FROM) ¤À¤±½Ì¤à¤³¤È¤Ë¤Ê¤ë¡£
2272
2273     @return
2274     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_del () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 
2275     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
2276
2277 /***
2278     @errors
2279     @c MERROR_RANGE
2280
2281     @seealso
2282     mtext_ins ()  */
2283
2284 int
2285 mtext_del (MText *mt, int from, int to)
2286 {
2287   int from_byte, to_byte;
2288   int unit_bytes = UNIT_BYTES (mt->format);
2289
2290   M_CHECK_READONLY (mt, -1);
2291   M_CHECK_RANGE (mt, from, to, -1, 0);
2292
2293   from_byte = POS_CHAR_TO_BYTE (mt, from);
2294   to_byte = POS_CHAR_TO_BYTE (mt, to);
2295
2296   if (mt->cache_char_pos >= to)
2297     {
2298       mt->cache_char_pos -= to - from;
2299       mt->cache_byte_pos -= to_byte - from_byte;
2300     }
2301   else if (mt->cache_char_pos > from)
2302     {
2303       mt->cache_char_pos -= from;
2304       mt->cache_byte_pos -= from_byte;
2305     }
2306
2307   mtext__adjust_plist_for_delete (mt, from, to - from);
2308   memmove (mt->data + from_byte * unit_bytes, 
2309            mt->data + to_byte * unit_bytes,
2310            (mt->nbytes - to_byte + 1) * unit_bytes);
2311   mt->nchars -= (to - from);
2312   mt->nbytes -= (to_byte - from_byte);
2313   mt->cache_char_pos = from;
2314   mt->cache_byte_pos = from_byte;
2315   return 0;
2316 }
2317
2318
2319 /*=*/
2320
2321 /***en
2322     @brief Insert an M-text into another M-text.
2323
2324     The mtext_ins () function inserts M-text $MT2 into M-text $MT1, at
2325     position $POS.  As a result, $MT1 is lengthen by the length of
2326     $MT2.  On insertion, all the text properties of $MT2 are
2327     inherited.  The original $MT2 is not modified.
2328
2329     @return
2330     If the operation was successful, mtext_ins () returns 0.
2331     Otherwise, it returns -1 and assigns an error code to the external
2332     variable #merror_code.  */
2333
2334 /***ja
2335     @brief M-text ¤òÊ̤ΠM-text ¤ËÁÞÆþ¤¹¤ë.
2336
2337     ´Ø¿ô mtext_ins () ¤Ï M-text $MT1 ¤Î $POS ¤Î°ÌÃÖ¤ËÊ̤ΠM-text $MT2 
2338     ¤òÁÞÆþ¤¹¤ë¡£¤³¤Î·ë²Ì $MT1 ¤ÎŤµ¤Ï $MT2 ¤ÎŤµÊ¬¤À¤±Áý¤¨¤ë¡£ÁÞÆþ¤ÎºÝ¡¢$MT2
2339     ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£$MT2 ¤½¤Î¤â¤Î¤ÏÊѹ¹¤µ¤ì¤Ê¤¤¡£
2340
2341     @return
2342     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_ins () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 
2343     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
2344
2345 /***
2346     @errors
2347     @c MERROR_RANGE , @c MERROR_MTEXT
2348
2349     @seealso
2350     mtext_del () , mtext_insert ()  */
2351
2352 int
2353 mtext_ins (MText *mt1, int pos, MText *mt2)
2354 {
2355   M_CHECK_READONLY (mt1, -1);
2356   M_CHECK_POS_X (mt1, pos, -1);
2357
2358   if (mt2->nchars == 0)
2359     return 0;
2360   insert (mt1, pos, mt2, 0, mt2->nchars);
2361   return 0;
2362 }
2363
2364 /*=*/
2365
2366 /***en
2367     @brief Insert sub-text of an M-text into another M-text.
2368
2369     The mtext_insert () function inserts sub-text of M-text $MT2
2370     between $FROM (inclusive) and $TO (exclusive) into M-text $MT1, at
2371     position $POS.  As a result, $MT1 is lengthen by ($TO - $FROM).
2372     On insertion, all the text properties of the sub-text of $MT2 are
2373     inherited.
2374
2375     @return If the operation was successful, mtext_insert () returns
2376     0.  Otherwise, it returns -1 and assigns an error code to the
2377     external variable #merror_code.  */
2378
2379 /***ja
2380     @brief M-text ¤Î°ìÉô¤òÊ̤ΠM-text ¤ËÁÞÆþ¤¹¤ë.
2381
2382     ´Ø¿ô mtext_insert () ¤Ï M-text $MT1 Ãæ¤Î $POS ¤Î°ÌÃ֤ˡ¢Ê̤Π
2383     M-text $MT2 ¤Î $FROM ¡Ê$FROM ¼«ÂΤâ´Þ¤à¡Ë¤«¤é $TO ¡Ê$TO ¼«ÂΤϴޤÞ
2384     ¤Ê¤¤¡Ë¤Þ¤Ç¤Îʸ»ú¤òÁÞÆþ¤¹¤ë¡£·ë²ÌŪ¤Ë $MT1 ¤ÏŤµ¤¬ ($TO - $FROM) 
2385     ¤À¤±¿­¤Ó¤ë¡£ÁÞÆþ¤ÎºÝ¡¢ $MT2 Ãæ¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì
2386     ¤ë¡£
2387
2388     @return
2389     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢mtext_insert () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 
2390     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
2391
2392 /***
2393     @errors
2394     @c MERROR_MTEXT , @c MERROR_RANGE
2395
2396     @seealso
2397     mtext_ins ()  */
2398
2399 int
2400 mtext_insert (MText *mt1, int pos, MText *mt2, int from, int to)
2401 {
2402   M_CHECK_READONLY (mt1, -1);
2403   M_CHECK_POS_X (mt1, pos, -1);
2404   M_CHECK_RANGE (mt2, from, to, -1, 0);
2405
2406   insert (mt1, pos, mt2, from, to);
2407   return 0;
2408 }
2409
2410 /*=*/
2411
2412 /***en
2413     @brief Insert a character into an M-text.
2414
2415     The mtext_ins_char () function inserts $N copies of character $C
2416     into M-text $MT at position $POS.  As a result, $MT is lengthen by
2417     $N.
2418
2419     @return
2420     If the operation was successful, mtext_ins () returns 0.
2421     Otherwise, it returns -1 and assigns an error code to the external
2422     variable #merror_code.  */
2423
2424 /***ja
2425     @brief M-text ¤Ëʸ»ú¤òÁÞÆþ¤¹¤ë.
2426
2427     ´Ø¿ô mtext_ins_char () ¤Ï M-text $MT ¤Î $POS ¤Î°ÌÃÖ¤Ëʸ»ú $C ¤Î¥³¥Ô¡¼¤ò $N
2428     ¸ÄÁÞÆþ¤¹¤ë¡£¤³¤Î·ë²Ì $MT1 ¤ÎŤµ¤Ï $N ¤À¤±Áý¤¨¤ë¡£
2429
2430     @return
2431     ½èÍý¤¬À®¸ù¤¹¤ì¤Ð mtext_ins_char () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1
2432     ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
2433
2434 /***
2435     @errors
2436     @c MERROR_RANGE
2437
2438     @seealso
2439     mtext_ins, mtext_del ()  */
2440
2441 int
2442 mtext_ins_char (MText *mt, int pos, int c, int n)
2443 {
2444   int nunits;
2445   int unit_bytes = UNIT_BYTES (mt->format);
2446   int pos_unit;
2447   int i;
2448
2449   M_CHECK_READONLY (mt, -1);
2450   M_CHECK_POS_X (mt, pos, -1);
2451   if (c < 0 || c > MCHAR_MAX)
2452     MERROR (MERROR_MTEXT, -1);
2453   if (n <= 0)
2454     return 0;
2455   mtext__adjust_plist_for_insert (mt, pos, n, NULL);
2456
2457   if (c >= 0x80
2458       && (mt->format == MTEXT_FORMAT_US_ASCII
2459           || (c >= 0x10000 && (mt->format == MTEXT_FORMAT_UTF_16LE
2460                                || mt->format == MTEXT_FORMAT_UTF_16BE))))
2461     {
2462       mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
2463       unit_bytes = 1;
2464     }
2465   else if (mt->format >= MTEXT_FORMAT_UTF_32LE)
2466     {
2467       if (mt->format != MTEXT_FORMAT_UTF_32)
2468         mtext__adjust_format (mt, MTEXT_FORMAT_UTF_32);
2469     }
2470   else if (mt->format >= MTEXT_FORMAT_UTF_16LE)
2471     {
2472       if (mt->format != MTEXT_FORMAT_UTF_16)
2473         mtext__adjust_format (mt, MTEXT_FORMAT_UTF_16);
2474     }
2475
2476   nunits = CHAR_UNITS (c, mt->format);
2477   if ((mt->nbytes + nunits * n + 1) * unit_bytes > mt->allocated)
2478     {
2479       mt->allocated = (mt->nbytes + nunits * n + 1) * unit_bytes;
2480       MTABLE_REALLOC (mt->data, mt->allocated, MERROR_MTEXT);
2481     }
2482   pos_unit = POS_CHAR_TO_BYTE (mt, pos);
2483   if (mt->cache_char_pos > pos)
2484     {
2485       mt->cache_char_pos += n;
2486       mt->cache_byte_pos += nunits * n;
2487     }
2488   memmove (mt->data + (pos_unit + nunits * n) * unit_bytes,
2489            mt->data + pos_unit * unit_bytes,
2490            (mt->nbytes - pos_unit + 1) * unit_bytes);
2491   if (mt->format <= MTEXT_FORMAT_UTF_8)
2492     {
2493       unsigned char *p = mt->data + pos_unit;
2494
2495       for (i = 0; i < n; i++)
2496         p += CHAR_STRING_UTF8 (c, p);
2497     }
2498   else if (mt->format == MTEXT_FORMAT_UTF_16)
2499     {
2500       unsigned short *p = (unsigned short *) mt->data + pos_unit;
2501
2502       for (i = 0; i < n; i++)
2503         p += CHAR_STRING_UTF16 (c, p);
2504     }
2505   else
2506     {
2507       unsigned *p = (unsigned *) mt->data + pos_unit;
2508
2509       for (i = 0; i < n; i++)
2510         *p++ = c;
2511     }
2512   mt->nchars += n;
2513   mt->nbytes += nunits * n;
2514   return 0;
2515 }
2516
2517 /*=*/
2518
2519 /***en
2520     @brief Replace sub-text of M-text with another.
2521
2522     The mtext_replace () function replaces sub-text of M-text $MT1
2523     between $FROM1 (inclusive) and $TO1 (exclusinve) with the sub-text
2524     of M-text $MT2 between $FROM2 (inclusive) and $TO2 (exclusinve).
2525     The new sub-text inherits text properties of the old sub-text.
2526
2527     @return If the operation was successful, mtext_replace () 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 ¤Î°ìÉô¤òÊ̤ΠM-text ¤Î°ìÉô¤ÇÃÖ´¹¤¹¤ë.
2533
2534     ´Ø¿ô mtext_replace () ¤Ï¡¢ M-text $MT1 ¤Î $FROM1 ¡Ê$FROM1 ¼«ÂΤâ´Þ
2535     ¤à¡Ë¤«¤é $TO1 ¡Ê$TO1 ¼«ÂΤϴޤޤʤ¤¡Ë¤Þ¤Ç¤ò¡¢ M-text $MT2 ¤Î 
2536     $FROM2 ¡Ê$FROM2 ¼«ÂΤâ´Þ¤à¡Ë¤«¤é $TO2 ¡Ê$TO2 ¼«ÂΤϴޤޤʤ¤¡Ë¤ÇÃÖ
2537     ¤­´¹¤¨¤ë¡£¿·¤·¤¯ÁÞÆþ¤µ¤ì¤¿Éôʬ¤Ï¡¢ÃÖ¤­´¹¤¨¤ëÁ°¤Î¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£
2538     ¤¹¤Ù¤Æ¤ò·Ñ¾µ¤¹¤ë¡£
2539
2540     @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð¡¢ mtext_replace () ¤Ï 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê
2541     ¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
2542
2543 /***
2544     @errors
2545     @c MERROR_MTEXT , @c MERROR_RANGE
2546
2547     @seealso
2548     mtext_insert ()  */
2549
2550 int
2551 mtext_replace (MText *mt1, int from1, int to1,
2552                MText *mt2, int from2, int to2)
2553 {
2554   int len1, len2;
2555   int from1_byte, from2_byte, old_bytes, new_bytes;
2556   int unit_bytes, total_bytes;
2557   unsigned char *p;
2558   int free_mt2 = 0;
2559
2560   M_CHECK_READONLY (mt1, -1);
2561   M_CHECK_RANGE_X (mt1, from1, to1, -1);
2562   M_CHECK_RANGE_X (mt2, from2, to2, -1);
2563
2564   if (from1 == to1)
2565     {
2566       struct MTextPlist *saved = mt2->plist;
2567
2568       mt2->plist = NULL;
2569       insert (mt1, from1, mt2, from2, to2);
2570       mt2->plist = saved;
2571       return 0;
2572     }
2573
2574   if (from2 == to2)
2575     {
2576       return mtext_del (mt1, from1, to1);
2577     }
2578
2579   if (mt1 == mt2)
2580     {
2581       mt2 = mtext_duplicate (mt2, from2, to2);
2582       to2 -= from2;
2583       from2 = 0;
2584       free_mt2 = 1;
2585     }
2586
2587   if (mt1->format != mt2->format
2588       && mt1->format == MTEXT_FORMAT_US_ASCII)
2589     mt1->format = MTEXT_FORMAT_UTF_8;
2590   if (mt1->format != mt2->format
2591       && mt1->coverage < mt2->coverage)
2592     mtext__adjust_format (mt1, mt2->format);
2593   if (mt1->format != mt2->format)
2594     {
2595       mt2 = mtext_duplicate (mt2, from2, to2);
2596       mtext__adjust_format (mt2, mt1->format);
2597       to2 -= from2;
2598       from2 = 0;
2599       free_mt2 = 1;
2600     }
2601
2602   len1 = to1 - from1;
2603   len2 = to2 - from2;
2604   mtext__adjust_plist_for_change (mt1, from1, len1, len2);
2605
2606   unit_bytes = UNIT_BYTES (mt1->format);
2607   from1_byte = POS_CHAR_TO_BYTE (mt1, from1) * unit_bytes;
2608   from2_byte = POS_CHAR_TO_BYTE (mt2, from2) * unit_bytes;
2609   old_bytes = POS_CHAR_TO_BYTE (mt1, to1) * unit_bytes - from1_byte;
2610   new_bytes = POS_CHAR_TO_BYTE (mt2, to2) * unit_bytes - from2_byte;
2611   total_bytes = mt1->nbytes * unit_bytes + (new_bytes - old_bytes);
2612   if (total_bytes + unit_bytes > mt1->allocated)
2613     {
2614       mt1->allocated = total_bytes + unit_bytes;
2615       MTABLE_REALLOC (mt1->data, mt1->allocated, MERROR_MTEXT);
2616     }
2617   p = mt1->data + from1_byte;
2618   if (to1 < mt1->nchars
2619       && old_bytes != new_bytes)
2620     memmove (p + new_bytes, p + old_bytes,
2621              (mt1->nbytes + 1) * unit_bytes - (from1_byte + old_bytes));
2622   memcpy (p, mt2->data + from2_byte, new_bytes);
2623   mt1->nchars += len2 - len1;
2624   mt1->nbytes += (new_bytes - old_bytes) / unit_bytes;
2625   if (mt1->cache_char_pos >= to1)
2626     {
2627       mt1->cache_char_pos += len2 - len1;
2628       mt1->cache_byte_pos += new_bytes - old_bytes;
2629     }
2630   else if (mt1->cache_char_pos > from1)
2631     {
2632       mt1->cache_char_pos = from1;
2633       mt1->cache_byte_pos = from1_byte;
2634     }
2635
2636   if (free_mt2)
2637     M17N_OBJECT_UNREF (mt2);
2638   return 0;
2639 }
2640
2641 /*=*/
2642
2643 /***en
2644     @brief Search a character in an M-text.
2645
2646     The mtext_character () function searches M-text $MT for character
2647     $C.  If $FROM is less than $TO, the search begins at position $FROM
2648     and goes forward but does not exceed ($TO - 1).  Otherwise, the search
2649     begins at position ($FROM - 1) and goes backward but does not
2650     exceed $TO.  An invalid position specification is regarded as both
2651     $FROM and $TO being 0.
2652
2653     @return
2654     If $C is found, mtext_character () returns the position of its
2655     first occurrence.  Otherwise it returns -1 without changing the
2656     external variable #merror_code.  If an error is detected, it returns -1 and
2657     assigns an error code to the external variable #merror_code.  */
2658
2659 /***ja
2660     @brief M-text Ãæ¤Çʸ»ú¤òõ¤¹.
2661
2662     ´Ø¿ô mtext_character () ¤Ï M-text $MT Ãæ¤Çʸ»ú $C ¤òõ¤¹¡£¤â¤· 
2663     $FROM ¤¬ $TO ¤è¤ê¾®¤µ¤±¤ì¤Ð¡¢Ãµº÷¤Ï°ÌÃÖ $FROM ¤«¤éËöÈøÊý¸þ¤Ø¡¢ºÇÂç 
2664     ($TO - 1) ¤Þ¤Ç¿Ê¤à¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð°ÌÃÖ ($FROM - 1) ¤«¤éÀèƬÊý¸þ¤Ø¡¢ºÇÂç
2665     $TO ¤Þ¤Ç¿Ê¤à¡£°ÌÃ֤λØÄê¤Ë¸í¤ê¤¬¤¢¤ë¾ì¹ç¤Ï¡¢$FROM ¤È $TO 
2666     ¤ÎξÊý¤Ë 0 ¤¬»ØÄꤵ¤ì¤¿¤â¤Î¤È¤ß¤Ê¤¹¡£
2667
2668     @return
2669     ¤â¤· $C ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_character () 
2670     ¤Ï¤½¤ÎºÇ½é¤Î½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï³°ÉôÊÑ¿ô #merror_code
2671     ¤òÊѹ¹¤»¤º¤Ë -1 ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤·¡¢³°ÉôÊÑ¿ô
2672     #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£  */
2673
2674 /***
2675     @seealso
2676     mtext_chr(), mtext_rchr ()  */
2677
2678 int
2679 mtext_character (MText *mt, int from, int to, int c)
2680 {
2681   if (from < to)
2682     {
2683       /* We do not use M_CHECK_RANGE () because this function should
2684          not set merror_code.  */
2685       if (from < 0 || to > mt->nchars)
2686         return -1;
2687       return find_char_forward (mt, from, to, c);
2688     }
2689   else
2690     {
2691       /* ditto */
2692       if (to < 0 || from > mt->nchars)
2693         return -1;
2694       return find_char_backward (mt, to, from, c);
2695     }
2696 }
2697
2698
2699 /*=*/
2700
2701 /***en
2702     @brief Return the position of the first occurrence of a character in an M-text.
2703
2704     The mtext_chr () function searches M-text $MT for character $C.
2705     The search starts from the beginning of $MT and goes toward the end.
2706
2707     @return
2708     If $C is found, mtext_chr () returns its position; otherwise it
2709     returns -1.  */
2710
2711 /***ja
2712     @brief M-text Ãæ¤Ç»ØÄꤵ¤ì¤¿Ê¸»ú¤¬ºÇ½é¤Ë¸½¤ì¤ë°ÌÃÖ¤òÊÖ¤¹.
2713
2714     ´Ø¿ô mtext_chr () ¤Ï M-text $MT Ãæ¤Çʸ»ú $C ¤òõ¤¹¡£Ãµº÷¤Ï $MT 
2715     ¤ÎÀèƬ¤«¤éËöÈøÊý¸þ¤Ë¿Ê¤à¡£
2716
2717     @return
2718     ¤â¤· $C ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_chr () 
2719     ¤Ï¤½¤Î½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£
2720
2721     @latexonly \IPAlabel{mtext_chr} @endlatexonly  */
2722
2723 /***
2724     @errors
2725     @c MERROR_RANGE
2726
2727     @seealso
2728     mtext_rchr (), mtext_character ()  */
2729
2730 int
2731 mtext_chr (MText *mt, int c)
2732 {
2733   return find_char_forward (mt, 0, mt->nchars, c);
2734 }
2735
2736 /*=*/
2737
2738 /***en
2739     @brief Return the position of the last occurrence of a character in an M-text.
2740
2741     The mtext_rchr () function searches M-text $MT for character $C.
2742     The search starts from the end of $MT and goes backwardly toward the
2743     beginning.
2744
2745     @return
2746     If $C is found, mtext_rchr () returns its position; otherwise it
2747     returns -1.  */
2748
2749 /***ja
2750     @brief M-text Ãæ¤Ç»ØÄꤵ¤ì¤¿Ê¸»ú¤¬ºÇ¸å¤Ë¸½¤ì¤ë°ÌÃÖ¤òÊÖ¤¹.
2751
2752     ´Ø¿ô mtext_rchr () ¤Ï M-text $MT Ãæ¤Çʸ»ú $C ¤òõ¤¹¡£Ãµº÷¤Ï $MT 
2753     ¤ÎºÇ¸å¤«¤éÀèƬÊý¸þ¤Ø¤È¸å¸þ¤­¤Ë¿Ê¤à¡£
2754
2755     @return
2756     ¤â¤· $C ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_rchr () 
2757     ¤Ï¤½¤Î½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£
2758
2759     @latexonly \IPAlabel{mtext_rchr} @endlatexonly  */
2760
2761 /***
2762     @errors
2763     @c MERROR_RANGE
2764
2765     @seealso
2766     mtext_chr (), mtext_character ()  */
2767
2768 int
2769 mtext_rchr (MText *mt, int c)
2770 {
2771   return find_char_backward (mt, mt->nchars, 0, c);
2772 }
2773
2774
2775 /*=*/
2776
2777 /***en
2778     @brief Compare two M-texts character-by-character.
2779
2780     The mtext_cmp () function compares M-texts $MT1 and $MT2 character
2781     by character.
2782
2783     @return
2784     This function returns 1, 0, or -1 if $MT1 is found greater than,
2785     equal to, or less than $MT2, respectively.  Comparison is based on
2786     character codes.  */
2787
2788 /***ja
2789     @brief Æó¤Ä¤Î M-text ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë.
2790
2791     ´Ø¿ô mtext_cmp () ¤Ï¡¢ M-text $MT1 ¤È $MT2 ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë¡£
2792
2793     @return
2794     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì¤Ð
2795     1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£Èæ³Ó¤Ïʸ»ú¥³¡¼¥É¤Ë´ð¤Å¤¯¡£
2796
2797     @latexonly \IPAlabel{mtext_cmp} @endlatexonly  */
2798
2799 /***
2800     @seealso
2801     mtext_ncmp (), mtext_casecmp (), mtext_ncasecmp (),
2802     mtext_compare (), mtext_case_compare ()  */
2803
2804 int
2805 mtext_cmp (MText *mt1, MText *mt2)
2806 {
2807   return compare (mt1, 0, mt1->nchars, mt2, 0, mt2->nchars);
2808 }
2809
2810
2811 /*=*/
2812
2813 /***en
2814     @brief Compare initial parts of two M-texts character-by-character.
2815
2816     The mtext_ncmp () function is similar to mtext_cmp (), but
2817     compares at most $N characters from the beginning.
2818
2819     @return
2820     This function returns 1, 0, or -1 if $MT1 is found greater than,
2821     equal to, or less than $MT2, respectively.  */
2822
2823 /***ja
2824     @brief Æó¤Ä¤Î M-text ¤ÎÀèƬÉôʬ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë.
2825
2826     ´Ø¿ô mtext_ncmp () ¤Ï¡¢´Ø¿ô mtext_cmp () Æ±ÍͤΠM-text 
2827     Æ±»Î¤ÎÈæ³Ó¤òÀèƬ¤«¤éºÇÂç $N Ê¸»ú¤Þ¤Ç¤Ë´Ø¤·¤Æ¹Ô¤Ê¤¦¡£
2828
2829     @return
2830     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì¤Ð 
2831     1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
2832
2833     @latexonly \IPAlabel{mtext_ncmp} @endlatexonly  */
2834
2835 /***
2836     @seealso
2837     mtext_cmp (), mtext_casecmp (), mtext_ncasecmp ()
2838     mtext_compare (), mtext_case_compare ()  */
2839
2840 int
2841 mtext_ncmp (MText *mt1, MText *mt2, int n)
2842 {
2843   if (n < 0)
2844     return 0;
2845   return compare (mt1, 0, (mt1->nchars < n ? mt1->nchars : n),
2846                   mt2, 0, (mt2->nchars < n ? mt2->nchars : n));
2847 }
2848
2849 /*=*/
2850
2851 /***en
2852     @brief Compare specified regions of two M-texts.
2853
2854     The mtext_compare () function compares two M-texts $MT1 and $MT2,
2855     character-by-character.  The compared regions are between $FROM1
2856     and $TO1 in $MT1 and $FROM2 to $TO2 in MT2.  $FROM1 and $FROM2 are
2857     inclusive, $TO1 and $TO2 are exclusive.  $FROM1 being equal to
2858     $TO1 (or $FROM2 being equal to $TO2) means an M-text of length
2859     zero.  An invalid region specification is regarded as both $FROM1
2860     and $TO1 (or $FROM2 and $TO2) being 0.
2861
2862     @return
2863     This function returns 1, 0, or -1 if $MT1 is found greater than,
2864     equal to, or less than $MT2, respectively.  Comparison is based on
2865     character codes.  */
2866
2867 /***ja
2868     @brief Æó¤Ä¤Î M-text ¤Î»ØÄꤷ¤¿ÎΰèƱ»Î¤òÈæ³Ó¤¹¤ë.
2869
2870     ´Ø¿ô mtext_compare () ¤ÏÆó¤Ä¤Î M-text $MT1 ¤È $MT2 
2871     ¤òʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë¡£Èæ³Ó¤ÎÂоݤϠ$MT1 ¤Î¤¦¤Á $FROM1 ¤«¤é $TO1 ¤Þ¤Ç¤È¡¢$MT2 
2872     ¤Î¤¦¤Á $FROM2 ¤«¤é $TO2 ¤Þ¤Ç¤Ç¤¢¤ë¡£$FROM1 ¤È $FROM2 ¤Ï´Þ¤Þ¤ì¡¢$TO1 
2873     ¤È $TO2 ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£$FROM1 ¤È $TO1 ¡Ê¤¢¤ë¤¤¤Ï $FROM2 ¤È $TO2 
2874     ¡Ë¤¬Åù¤·¤¤¾ì¹ç¤ÏŤµ¥¼¥í¤Î M-text ¤ò°ÕÌ£¤¹¤ë¡£ÈÏ°Ï»ØÄê¤Ë¸í¤ê¤¬¤¢¤ë¾ì¹ç¤Ï¡¢
2875     $FROM1 ¤È $TO1 ¡Ê¤¢¤ë¤¤¤Ï $FROM2 ¤È $TO2 ¡Ë Î¾Êý¤Ë 0 ¤¬»ØÄꤵ¤ì¤¿¤â¤Î¤È¤ß¤Ê¤¹¡£
2876
2877     @return
2878     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì¤Ð
2879     1 ¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£Èæ³Ó¤Ïʸ»ú¥³¡¼¥É¤Ë´ð¤Å¤¯¡£  */
2880
2881 /***
2882     @seealso
2883     mtext_cmp (), mtext_ncmp (), mtext_casecmp (), mtext_ncasecmp (),
2884     mtext_case_compare ()  */
2885
2886 int
2887 mtext_compare (MText *mt1, int from1, int to1, MText *mt2, int from2, int to2)
2888 {
2889   if (from1 < 0 || from1 > to1 || to1 > mt1->nchars)
2890     from1 = to1 = 0;
2891
2892   if (from2 < 0 || from2 > to2 || to2 > mt2->nchars)
2893     from2 = to2 = 0;
2894
2895   return compare (mt1, from1, to1, mt2, from2, to2);
2896 }
2897
2898 /*=*/
2899
2900 /***en
2901     @brief Search an M-text for a set of characters.
2902
2903     The mtext_spn () function returns the length of the initial
2904     segment of M-text $MT1 that consists entirely of characters in
2905     M-text $MT2.  */
2906
2907 /***ja
2908     @brief ¤¢¤ë½¸¹ç¤Îʸ»ú¤ò M-text ¤ÎÃæ¤Çõ¤¹.
2909
2910     ´Ø¿ô mtext_spn () ¤Ï¡¢M-text $MT1 ¤ÎÀèƬ¤«¤é M-text $MT2 
2911     ¤Ë´Þ¤Þ¤ì¤ëʸ»ú¤À¤±¤Ç¤Ç¤­¤Æ¤¤¤ëÉôʬ¤ÎŤµ¤òÊÖ¤¹¡£
2912
2913     @latexonly \IPAlabel{mtext_spn} @endlatexonly  */
2914
2915 /***
2916     @seealso
2917     mtext_cspn ()  */
2918
2919 int
2920 mtext_spn (MText *mt, MText *accept)
2921 {
2922   return span (mt, accept, 0, Mnil);
2923 }
2924
2925 /*=*/
2926
2927 /***en
2928     @brief Search an M-text for the complement of a set of characters.
2929
2930     The mtext_cspn () returns the length of the initial segment of
2931     M-text $MT1 that consists entirely of characters not in M-text $MT2.  */
2932
2933 /***ja
2934     @brief ¤¢¤ë½¸¹ç¤Ë°¤µ¤Ê¤¤Ê¸»ú¤ò M-text ¤ÎÃæ¤Çõ¤¹.
2935
2936     ´Ø¿ô mtext_cspn () ¤Ï¡¢M-text $MT1 ¤ÎÀèƬÉôʬ¤Ç M-text $MT2
2937     ¤Ë´Þ¤Þ¤ì¤Ê¤¤Ê¸»ú¤À¤±¤Ç¤Ç¤­¤Æ¤¤¤ëÉôʬ¤ÎŤµ¤òÊÖ¤¹¡£
2938
2939     @latexonly \IPAlabel{mtext_cspn} @endlatexonly  */
2940
2941 /***
2942     @seealso
2943     mtext_spn ()  */
2944
2945 int
2946 mtext_cspn (MText *mt, MText *reject)
2947 {
2948   return span (mt, reject, 0, Mt);
2949 }
2950
2951 /*=*/
2952
2953 /***en
2954     @brief Search an M-text for any of a set of characters.
2955
2956     The mtext_pbrk () function locates the first occurrence in M-text
2957     $MT1 of any of the characters in M-text $MT2.
2958
2959     @return
2960     This function returns the position in $MT1 of the found character.
2961     If no such character is found, it returns -1. */
2962
2963 /***ja
2964     @brief ¤¢¤ë½¸¹ç¤Ë°¤¹Ê¸»ú¤ò M-text ¤ÎÃ椫¤éõ¤¹.
2965
2966     ´Ø¿ô mtext_pbrk () ¤Ï¡¢M-text $MT1 Ãæ¤Ç M-text $MT2 
2967     ¤Îʸ»ú¤Î¤É¤ì¤«¤¬ºÇ½é¤Ë¸½¤ì¤ë°ÌÃÖ¤òÄ´¤Ù¤ë¡£
2968
2969     @return 
2970     ¸«¤Ä¤«¤Ã¤¿Ê¸»ú¤Î¡¢$MT1 
2971     Æâ¤Ë¤ª¤±¤ë½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¤â¤·¤½¤Î¤è¤¦¤Êʸ»ú¤¬¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
2972
2973     @latexonly \IPAlabel{mtext_pbrk} @endlatexonly  */
2974
2975 int
2976 mtext_pbrk (MText *mt, MText *accept)
2977 {
2978   int nchars = mtext_nchars (mt);
2979   int len = span (mt, accept, 0, Mt);
2980
2981   return (len == nchars ? -1 : len);
2982 }
2983
2984 /*=*/
2985
2986 /***en
2987     @brief Look for a token in an M-text.
2988
2989     The mtext_tok () function searches a token that firstly occurs
2990     after position $POS in M-text $MT.  Here, a token means a
2991     substring each of which does not appear in M-text $DELIM.  Note
2992     that the type of $POS is not @c int but pointer to @c int.
2993
2994     @return
2995     If a token is found, mtext_tok () copies the corresponding part of
2996     $MT and returns a pointer to the copy.  In this case, $POS is set
2997     to the end of the found token.  If no token is found, it returns
2998     @c NULL without changing the external variable #merror_code.  If an
2999     error is detected, it returns @c NULL and assigns an error code
3000     to the external variable #merror_code. */
3001
3002 /***ja
3003     @brief M-text Ãæ¤Î¥È¡¼¥¯¥ó¤òõ¤¹.
3004
3005     ´Ø¿ô mtext_tok () ¤Ï¡¢M-text $MT ¤ÎÃæ¤Ç°ÌÃÖ $POS 
3006     °Ê¹ßºÇ½é¤Ë¸½¤ì¤ë¥È¡¼¥¯¥ó¤òõ¤¹¡£¤³¤³¤Ç¥È¡¼¥¯¥ó¤È¤Ï M-text $DELIM
3007     ¤ÎÃæ¤Ë¸½¤ï¤ì¤Ê¤¤Ê¸»ú¤À¤±¤«¤é¤Ê¤ëÉôʬʸ»úÎó¤Ç¤¢¤ë¡£$POS ¤Î·¿¤¬ @c int ¤Ç¤Ï¤Ê¤¯¤Æ @c
3008     int ¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¤³¤È¤ËÃí°Õ¡£
3009
3010     @return
3011     ¤â¤·¥È¡¼¥¯¥ó¤¬¸«¤Ä¤«¤ì¤Ð mtext_tok ()¤Ï¤½¤Î¥È¡¼¥¯¥ó¤ËÁêÅö¤¹¤ëÉôʬ¤Î 
3012     $MT ¤ò¥³¥Ô¡¼¤·¡¢¤½¤Î¥³¥Ô¡¼¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¾ì¹ç¡¢$POS 
3013     ¤Ï¸«¤Ä¤«¤Ã¤¿¥È¡¼¥¯¥ó¤Î½ªÃ¼¤Ë¥»¥Ã¥È¤µ¤ì¤ë¡£¥È¡¼¥¯¥ó¤¬¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï³°ÉôÊÑ¿ô
3014     #merror_code ¤òÊѤ¨¤º¤Ë @c NULL ¤òÊÖ¤¹¡£¥¨¥é¡¼¤¬¸¡½Ð¤µ¤ì¤¿¾ì¹ç¤Ï 
3015     @c NULL ¤òÊÖ¤·¡¢ÊÑÉôÊÑ¿ô #merror_code ¤Ë¥¨¥é¡¼¥³¡¼¥É¤òÀßÄꤹ¤ë¡£
3016
3017     @latexonly \IPAlabel{mtext_tok} @endlatexonly  */
3018
3019 /***
3020     @errors
3021     @c MERROR_RANGE  */
3022
3023 MText *
3024 mtext_tok (MText *mt, MText *delim, int *pos)
3025 {
3026   int nchars = mtext_nchars (mt);
3027   int pos2;
3028
3029   M_CHECK_POS (mt, *pos, NULL);
3030
3031   /*
3032     Skip delimiters starting at POS in MT.
3033     Never do *pos += span(...), or you will change *pos
3034     even though no token is found.
3035    */
3036   pos2 = *pos + span (mt, delim, *pos, Mnil);
3037
3038   if (pos2 == nchars)
3039     return NULL;
3040
3041   *pos = pos2 + span (mt, delim, pos2, Mt);
3042   return (insert (mtext (), 0, mt, pos2, *pos));
3043 }
3044
3045 /*=*/
3046
3047 /***en
3048     @brief Locate an M-text in another.
3049
3050     The mtext_text () function finds the first occurrence of M-text
3051     $MT2 in M-text $MT1 after the position $POS while ignoring
3052     difference of the text properties.
3053
3054     @return
3055     If $MT2 is found in $MT1, mtext_text () returns the position of it
3056     first occurrence.  Otherwise it returns -1.  If $MT2 is empty, it
3057     returns 0.  */
3058
3059 /***ja
3060     @brief M-text Ãæ¤ÇÊ̤ΠM-text ¤òõ¤¹.
3061
3062     ´Ø¿ô mtext_text () ¤Ï¡¢M-text $MT1 Ãæ¤Ç°ÌÃÖ $POS °Ê¹ß¤Ë¸½¤ï¤ì¤ë 
3063     M-text $MT2 ¤ÎºÇ½é¤Î°ÌÃÖ¤òÄ´¤Ù¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î°ã¤¤¤Ï̵»ë¤µ¤ì¤ë¡£
3064
3065     @return
3066     $MT1 Ãæ¤Ë $MT2 ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_text() 
3067     ¤Ï¤½¤ÎºÇ½é¤Î½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤¤¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£¤â¤· $MT2 ¤¬¶õ¤Ê¤é¤Ð 0 ¤òÊÖ¤¹¡£
3068
3069     @latexonly \IPAlabel{mtext_text} @endlatexonly  */
3070
3071 int
3072 mtext_text (MText *mt1, int pos, MText *mt2)
3073 {
3074   int from = pos;
3075   int c = mtext_ref_char (mt2, 0);
3076   int nbytes2 = mtext_nbytes (mt2);
3077   int limit;
3078   int use_memcmp = (mt1->format == mt2->format
3079                     || (mt1->format < MTEXT_FORMAT_UTF_8
3080                         && mt2->format == MTEXT_FORMAT_UTF_8));
3081   int unit_bytes = UNIT_BYTES (mt1->format);
3082
3083   if (from + mtext_nchars (mt2) > mtext_nchars (mt1))
3084     return -1;
3085   limit = mtext_nchars (mt1) - mtext_nchars (mt2) + 1;
3086
3087   while (1)
3088     {
3089       int pos_byte;
3090
3091       if ((pos = mtext_character (mt1, from, limit, c)) < 0)
3092         return -1;
3093       pos_byte = POS_CHAR_TO_BYTE (mt1, pos);
3094       if (use_memcmp
3095           ? ! memcmp (mt1->data + pos_byte * unit_bytes, 
3096                       mt2->data, nbytes2 * unit_bytes)
3097           : ! compare (mt1, pos, mt2->nchars, mt2, 0, mt2->nchars))
3098         break;
3099       from = pos + 1;
3100     }
3101   return pos;
3102 }
3103
3104 /***en
3105     @brief Locate an M-text in a specific range of another.
3106
3107     The mtext_search () function searches for the first occurrence of
3108     M-text $MT2 in M-text $MT1 in the region $FROM and $TO while
3109     ignoring difference of the text properties.  If $FROM is less than
3110     $TO, the forward search starts from $FROM, otherwise the backward
3111     search starts from $TO.
3112
3113     @return
3114     If $MT2 is found in $MT1, mtext_search () returns the position of the
3115     first occurrence.  Otherwise it returns -1.  If $MT2 is empty, it
3116     returns 0.  */
3117
3118 /***ja
3119     @brief M-text Ãæ¤ÎÆÃÄê¤ÎÎΰè¤ÇÊ̤ΠM-text ¤òõ¤¹.
3120
3121     ´Ø¿ô mtext_search () ¤Ï¡¢M-text $MT1 Ãæ¤Î $FROM ¤«¤é $TO 
3122     ¤Þ¤Ç¤Î´Ö¤ÎÎΰè¤ÇM-text $MT2 
3123     ¤¬ºÇ½é¤Ë¸½¤ï¤ì¤ë°ÌÃÖ¤òÄ´¤Ù¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Î°ã¤¤¤Ï̵»ë¤µ¤ì¤ë¡£¤â¤·
3124     $FROM ¤¬ $TO ¤è¤ê¾®¤µ¤±¤ì¤Ðõº÷¤Ï°ÌÃÖ $FROM ¤«¤éËöÈøÊý¸þ¤Ø¡¢¤½¤¦¤Ç¤Ê¤±¤ì¤Ð
3125     $TO ¤«¤éÀèƬÊý¸þ¤Ø¿Ê¤à¡£
3126
3127     @return
3128     $MT1 Ãæ¤Ë $MT2 ¤¬¸«¤Ä¤«¤ì¤Ð¡¢mtext_search() 
3129     ¤Ï¤½¤ÎºÇ½é¤Î½Ð¸½°ÌÃÖ¤òÊÖ¤¹¡£¸«¤Ä¤«¤é¤Ê¤¤¾ì¹ç¤Ï -1 ¤òÊÖ¤¹¡£¤â¤· $MT2 ¤¬¶õ¤Ê¤é¤Ð 0 ¤òÊÖ¤¹¡£
3130     */
3131
3132 int
3133 mtext_search (MText *mt1, int from, int to, MText *mt2)
3134 {
3135   int c = mtext_ref_char (mt2, 0);
3136   int from_byte;
3137   int nbytes2 = mtext_nbytes (mt2);
3138
3139   if (mt1->format > MTEXT_FORMAT_UTF_8
3140       || mt2->format > MTEXT_FORMAT_UTF_8)
3141     MERROR (MERROR_MTEXT, -1);
3142
3143   if (from < to)
3144     {
3145       to -= mtext_nchars (mt2);
3146       if (from > to)
3147         return -1;
3148       while (1)
3149         {
3150           if ((from = find_char_forward (mt1, from, to, c)) < 0)
3151             return -1;
3152           from_byte = POS_CHAR_TO_BYTE (mt1, from);
3153           if (! memcmp (mt1->data + from_byte, mt2->data, nbytes2))
3154             break;
3155           from++;
3156         }
3157     }
3158   else if (from > to)
3159     {
3160       from -= mtext_nchars (mt2);
3161       if (from < to)
3162         return -1;
3163       while (1)
3164         {
3165           if ((from = find_char_backward (mt1, to, from + 1, c)) < 0)
3166             return -1;
3167           from_byte = POS_CHAR_TO_BYTE (mt1, from);
3168           if (! memcmp (mt1->data + from_byte, mt2->data, nbytes2))
3169             break;
3170           from--;
3171         }
3172     }
3173
3174   return from;
3175 }
3176
3177 /*=*/
3178
3179 /***en
3180     @brief Compare two M-texts ignoring cases.
3181
3182     The mtext_casecmp () function is similar to mtext_cmp (), but
3183     ignores cases on comparison.
3184
3185     @return
3186     This function returns 1, 0, or -1 if $MT1 is found greater than,
3187     equal to, or less than $MT2, respectively.  */
3188
3189 /***ja
3190     @brief Æó¤Ä¤Î M-text ¤òÂçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤ÆÈæ³Ó¤¹¤ë.
3191
3192     ´Ø¿ô mtext_casecmp () ¤Ï¡¢´Ø¿ô mtext_cmp () Æ±ÍͤΠM-text 
3193     Æ±»Î¤ÎÈæ³Ó¤ò¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤Æ¹Ô¤Ê¤¦¡£
3194
3195     @return
3196     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 
3197     ¤è¤êÂ礭¤±¤ì¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
3198
3199     @latexonly \IPAlabel{mtext_casecmp} @endlatexonly  */
3200
3201 /***
3202     @seealso
3203     mtext_cmp (), mtext_ncmp (), mtext_ncasecmp ()
3204     mtext_compare (), mtext_case_compare ()  */
3205
3206 int
3207 mtext_casecmp (MText *mt1, MText *mt2)
3208 {
3209   return case_compare (mt1, 0, mt1->nchars, mt2, 0, mt2->nchars);
3210 }
3211
3212 /*=*/
3213
3214 /***en
3215     @brief Compare initial parts of two M-texts ignoring cases.
3216
3217     The mtext_ncasecmp () function is similar to mtext_casecmp (), but
3218     compares at most $N characters from the beginning.
3219
3220     @return
3221     This function returns 1, 0, or -1 if $MT1 is found greater than,
3222     equal to, or less than $MT2, respectively.  */
3223
3224 /***ja
3225     @brief Æó¤Ä¤Î M-text ¤ÎÀèƬÉôʬ¤òÂçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤ÆÈæ³Ó¤¹¤ë.
3226
3227     ´Ø¿ô mtext_ncasecmp () ¤Ï¡¢´Ø¿ô mtext_casecmp () Æ±ÍͤΠM-text 
3228     Æ±»Î¤ÎÈæ³Ó¤òÀèƬ¤«¤éºÇÂç $N Ê¸»ú¤Þ¤Ç¤Ë´Ø¤·¤Æ¹Ô¤Ê¤¦¡£
3229
3230     @return
3231     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 
3232     ¤è¤êÂ礭¤±¤ì¤Ð 1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
3233
3234     @latexonly \IPAlabel{mtext_ncasecmp} @endlatexonly  */
3235
3236 /***
3237     @seealso
3238     mtext_cmp (), mtext_casecmp (), mtext_casecmp ()
3239     mtext_compare (), mtext_case_compare ()  */
3240
3241 int
3242 mtext_ncasecmp (MText *mt1, MText *mt2, int n)
3243 {
3244   if (n < 0)
3245     return 0;
3246   return case_compare (mt1, 0, (mt1->nchars < n ? mt1->nchars : n),
3247                        mt2, 0, (mt2->nchars < n ? mt2->nchars : n));
3248 }
3249
3250 /*=*/
3251
3252 /***en
3253     @brief Compare specified regions of two M-texts ignoring cases. 
3254
3255     The mtext_case_compare () function compares two M-texts $MT1 and
3256     $MT2, character-by-character, ignoring cases.  The compared
3257     regions are between $FROM1 and $TO1 in $MT1 and $FROM2 to $TO2 in
3258     MT2.  $FROM1 and $FROM2 are inclusive, $TO1 and $TO2 are
3259     exclusive.  $FROM1 being equal to $TO1 (or $FROM2 being equal to
3260     $TO2) means an M-text of length zero.  An invalid region
3261     specification is regarded as both $FROM1 and $TO1 (or $FROM2 and
3262     $TO2) being 0.
3263
3264     @return
3265     This function returns 1, 0, or -1 if $MT1 is found greater than,
3266     equal to, or less than $MT2, respectively.  Comparison is based on
3267     character codes.  */
3268
3269 /***ja 
3270     @brief Æó¤Ä¤Î M-text ¤Î»ØÄꤷ¤¿Îΰè¤ò¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤ÆÈæ³Ó¤¹¤ë.
3271
3272     ´Ø¿ô mtext_compare () ¤ÏÆó¤Ä¤Î M-text $MT1 ¤È $MT2 
3273     ¤ò¡¢Âçʸ»ú¡¿¾®Ê¸»ú¤Î¶èÊ̤ò̵»ë¤·¤Æʸ»úñ°Ì¤ÇÈæ³Ó¤¹¤ë¡£Èæ³Ó¤ÎÂоݤϠ$MT1 
3274     ¤Î $FROM1 ¤«¤é $TO1 ¤Þ¤Ç¡¢$MT2 ¤Î $FROM2 ¤«¤é $TO2 ¤Þ¤Ç¤Ç¤¢¤ë¡£
3275     $FROM1 ¤È $FROM2 ¤Ï´Þ¤Þ¤ì¡¢$TO1 ¤È $TO2 ¤Ï´Þ¤Þ¤ì¤Ê¤¤¡£$FROM1 ¤È $TO1
3276     ¡Ê¤¢¤ë¤¤¤Ï $FROM2 ¤È $TO2 ¡Ë¤¬Åù¤·¤¤¾ì¹ç¤ÏŤµ¥¼¥í¤Î M-text 
3277     ¤ò°ÕÌ£¤¹¤ë¡£ÈÏ°Ï»ØÄê¤Ë¸í¤ê¤¬¤¢¤ë¾ì¹ç¤Ï¡¢$FROM1 ¤È $TO1 ¡Ê¤¢¤ë¤¤¤Ï 
3278     $FROM2 ¤È $TO2 ¡ËξÊý¤Ë 0 ¤¬»ØÄꤵ¤ì¤¿¤â¤Î¤È¸«¤Ê¤¹¡£
3279
3280     @return
3281     ¤³¤Î´Ø¿ô¤Ï¡¢$MT1 ¤È $MT2 ¤¬Åù¤·¤±¤ì¤Ð 0¡¢$MT1 ¤¬ $MT2 ¤è¤êÂ礭¤±¤ì¤Ð
3282     1¡¢$MT1 ¤¬ $MT2 ¤è¤ê¾®¤µ¤±¤ì¤Ð -1¤òÊÖ¤¹¡£Èæ³Ó¤Ïʸ»ú¥³¡¼¥É¤Ë´ð¤Å¤¯¡£
3283
3284   @latexonly \IPAlabel{mtext_case_compare} @endlatexonly
3285 */
3286
3287 /***
3288     @seealso
3289     mtext_cmp (), mtext_ncmp (), mtext_casecmp (), mtext_ncasecmp (),
3290     mtext_compare ()  */
3291
3292 int
3293 mtext_case_compare (MText *mt1, int from1, int to1,
3294                     MText *mt2, int from2, int to2)
3295 {
3296   if (from1 < 0 || from1 > to1 || to1 > mt1->nchars)
3297     from1 = to1 = 0;
3298
3299   if (from2 < 0 || from2 > to2 || to2 > mt2->nchars)
3300     from2 = to2 = 0;
3301
3302   return case_compare (mt1, from1, to1, mt2, from2, to2);
3303 }
3304
3305 /*=*/
3306
3307 /***en
3308     @brief Uppercase an M-text.
3309
3310
3311     The mtext_uppercase () function destructively converts each
3312     character in M-text $MT to uppercase.  Adjacent characters in $MT
3313     may affect the case conversion.  If the Mlanguage text property is
3314     attached to $MT, it may also affect the conversion.  The length of
3315     $MT may change.  Characters that cannot be converted to uppercase
3316     is left unchanged.  All the text properties are inherited.
3317
3318     @return
3319     If more than one character is converted, 1 is returned.
3320     Otherwise, 0 is returned.
3321 */
3322
3323 /***ja
3324     @brief M-text ¤òÂçʸ»ú¤Ë¤¹¤ë.
3325
3326     ´Ø¿ô mtext_uppercase () ¤Ï M-text $MT Ãæ¤Î³Æʸ»ú¤òÇ˲õŪ¤ËÂçʸ»ú¤ËÊÑ
3327     ´¹¤¹¤ë¡£ÊÑ´¹¤ËºÝ¤·¤ÆÎÙÀܤ¹¤ëʸ»ú¤Î±Æ¶Á¤ò¼õ¤±¤ë¤³¤È¤¬¤¢¤ë¡£$MT ¤Ë¥Æ
3328     ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ Mlanguage ¤¬ÉÕ¤¤¤Æ¤¤¤ë¾ì¹ç¤Ï¡¢¤½¤ì¤âÊÑ´¹¤Ë±Æ¶Á¤ò
3329     Í¿¤¨¤¦¤ë¡£$MT ¤ÎŤµ¤ÏÊѤï¤ë¤³¤È¤¬¤¢¤ë¡£Âçʸ»ú¤ËÊÑ´¹¤Ç¤­¤Ê¤«¤Ã¤¿Ê¸
3330     »ú¤Ï¤½¤Î¤Þ¤Þ»Ä¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£
3331
3332     @return
3333     1ʸ»ú°Ê¾å¤¬ÊÑ´¹¤µ¤ì¤¿¾ì¹ç¤Ï1¤¬ÊÖ¤µ¤ì¤ë¡£¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¤Ï0¤¬ÊÖ¤µ¤ì¤ë¡£
3334 */
3335
3336 /***
3337     @seealso mtext_lowercase (), mtext_titlecase ()
3338 */
3339
3340 int
3341 mtext_uppercase (MText *mt)
3342 {
3343   int len = mtext_len (mt), i, j;
3344   int c;
3345   int modified = 0;
3346   MText *orig;
3347   MSymbol lang;
3348
3349   CASE_CONV_INIT (-1);
3350
3351   if (uppercase_precheck (mt))
3352     orig = mtext_dup (mt);
3353
3354   /* i moves over mt, j moves over orig. */
3355   for (i = 0, j = 0; i < len; j++)
3356     {
3357       c = mtext_ref_char (mt, i);
3358       lang = (MSymbol) mtext_get_prop (mt, i, Mlanguage);
3359
3360       if (lang == Mlt && c == 0x0307 && after_soft_dotted (orig, j))
3361         DELETE ();
3362
3363       else if ((lang == Mtr || lang == Maz) && c == 0x0069)
3364         REPLACE (tr0069);
3365                
3366       else
3367         {
3368           MPlist *pl = (MPlist *) mchartable_lookup (case_mapping, c);
3369
3370           if (pl)
3371             {
3372               MText *upper;
3373               int ulen;
3374
3375               /* Uppercase is the 3rd element. */
3376               upper = (MText *) mplist_value (mplist_next (mplist_next (mplist_value (pl))));
3377               ulen = mtext_len (upper);
3378
3379               if (mtext_ref_char (upper, 0) != c || ulen > 1)
3380                 {
3381                   mtext_replace (mt, i, i + 1, upper, 0, ulen);
3382                   modified = 1;
3383                   i += ulen;
3384                   len += ulen - 1;
3385                 }
3386
3387               else
3388                 i++;
3389             }
3390
3391           else                                           /* pl == NULL */
3392             i++;
3393         }
3394     }
3395
3396   if (orig)
3397     m17n_object_unref (orig);
3398   return modified;
3399 }
3400
3401 /*=*/
3402
3403 /***en
3404     @brief Titlecase an M-text.
3405
3406     The mtext_titlecase () function destructively converts the first
3407     character in M-text $MT to titlecase and the others to lowercase.
3408     The length of $MT may change.  If the character cannot be
3409     converted to titlercase, it is left unchanged.  All the text
3410     properties are inherited.
3411
3412     @return
3413     If the character is converted, 1 is returned.  Otherwise, 0 is
3414     returned.
3415 */
3416
3417 /***ja
3418     @brief M-text ¤ò¥¿¥¤¥È¥ë¥±¡¼¥¹¤Ë¤¹¤ë.
3419
3420     ´Ø¿ô mtext_titlecase () ¤Ï M-text $MT ¤ÎÀèƬ¤Îʸ»ú¤ò¥¿¥¤¥È¥ë¥±¡¼¥¹
3421     ¤Ë¡¢¤½¤·¤Æ¤½¤ì°Ê¹ß¤Îʸ»ú¤ò¾®Ê¸»ú¤ËÇ˲õŪ¤ËÊÑ´¹¤¹¤ë¡£$MT ¤ÎŤµ¤ÏÊÑ
3422     ¤ï¤ë¤³¤È¤¬¤¢¤ë¡£¥¿¥¤¥È¥ë¥±¡¼¥¹¤Ë¤ËÊÑ´¹¤Ç¤­¤Ê¤«¤Ã¤¿¾ì¹ç¤Ï¤½¤Î¤Þ¤Þ¤Ç
3423     ÊѤï¤é¤Ê¤¤¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£
3424
3425     @return
3426     Ê¸»ú¤¬ÊÑ´¹¤µ¤ì¤¿¾ì¹ç¤Ï1¤¬ÊÖ¤µ¤ì¤ë¡£¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¤Ï0¤¬ÊÖ¤µ¤ì¤ë¡£
3427 */
3428
3429 /***
3430     @seealso mtext_lowercase (), mtext_uppercase ()
3431 */
3432
3433 int
3434 mtext_titlecase (MText *mt)
3435 {
3436   int len;
3437   int c;
3438   MSymbol lang;
3439   MPlist *pl;
3440   int modified = 0;
3441
3442   CASE_CONV_INIT (-1);
3443
3444   len = mtext_len (mt);
3445
3446   if (len == 0)
3447     return 0;
3448
3449   c = mtext_ref_char (mt, 0);
3450   lang = mtext_get_prop (mt, 0, Mlanguage);
3451
3452   if ((lang == Mtr || lang == Maz) && c == 0x0069)
3453     {
3454       mtext_replace (mt, 0, 1, tr0069, 0, 1);
3455       modified = 1;
3456     }
3457
3458   else if ((pl = mchartable_lookup (case_mapping, c)))
3459     {
3460       /* Titlecase is the 2nd element. */
3461       MText *title = (MText *) mplist_value (mplist_next (mplist_value (pl)));
3462       int tlen = mtext_len (title);
3463
3464       if (mtext_ref_char (title, 0) != c || tlen > 1)
3465         {
3466           mtext_replace (mt, 0, 1, title, 0, tlen);
3467           modified = 1;
3468         }
3469     }
3470
3471   if (len == 1)
3472     return modified;
3473   else
3474     return modified | mtext__lowercase (mt, 1, len);
3475 }
3476
3477 /*=*/
3478
3479 /***en
3480     @brief Lowercase an M-text.
3481
3482     The mtext_lowercase () function destructively converts each
3483     character in M-text $MT to lowercase.  Adjacent characters in $MT
3484     may affect the case conversion.  If the Mlanguage text property is
3485     attached to $MT, it may also affect the conversion.  The length of
3486     $MT may change.  Characters that cannot be converted to lowercase
3487     is left unchanged.  All the text properties are inherited.
3488
3489     @return
3490     If more than one character is converted, 1 is returned.
3491     Otherwise, 0 is returned.
3492 */
3493
3494 /***ja
3495     @brief M-text ¤ò¾®Ê¸»ú¤Ë¤¹¤ë.
3496
3497     ´Ø¿ô mtext_lowercase () ¤Ï M-text $MT Ãæ¤Î³Æʸ»ú¤òÇ˲õŪ¤Ë¾®Ê¸»ú¤ËÊÑ
3498     ´¹¤¹¤ë¡£ÊÑ´¹¤ËºÝ¤·¤ÆÎÙÀܤ¹¤ëʸ»ú¤Î±Æ¶Á¤ò¼õ¤±¤ë¤³¤È¤¬¤¢¤ë¡£$MT ¤Ë¥Æ
3499     ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£ Mlanguage ¤¬ÉÕ¤¤¤Æ¤¤¤ë¾ì¹ç¤Ï¡¢¤½¤ì¤âÊÑ´¹¤Ë±Æ¶Á¤ò
3500     Í¿¤¨¤¦¤ë¡£$MT ¤ÎŤµ¤ÏÊѤï¤ë¤³¤È¤¬¤¢¤ë¡£¾®Ê¸»ú¤ËÊÑ´¹¤Ç¤­¤Ê¤«¤Ã¤¿Ê¸
3501     »ú¤Ï¤½¤Î¤Þ¤Þ»Ä¤ë¡£¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤Ï¤¹¤Ù¤Æ·Ñ¾µ¤µ¤ì¤ë¡£
3502
3503     @return
3504     1ʸ»ú°Ê¾å¤¬ÊÑ´¹¤µ¤ì¤¿¾ì¹ç¤Ï1¤¬ÊÖ¤µ¤ì¤ë¡£¤½¤¦¤Ç¤Ê¤¤¾ì¹ç¤Ï0¤¬ÊÖ¤µ¤ì¤ë¡£
3505 */
3506
3507 /***
3508     @seealso mtext_titlecase (), mtext_uppercase ()
3509 */
3510
3511 int
3512 mtext_lowercase (MText *mt)
3513
3514 {
3515   CASE_CONV_INIT (-1);
3516
3517   return mtext__lowercase (mt, 0, mtext_len (mt));
3518 }
3519
3520 /*** @} */
3521
3522 #include <stdio.h>
3523
3524 /*** @addtogroup m17nDebug */
3525 /*=*/
3526 /*** @{  */
3527
3528 /***en
3529     @brief Dump an M-text.
3530
3531     The mdebug_dump_mtext () function prints the M-text $MT in a human
3532     readable way to the stderr.  $INDENT specifies how many columns to
3533     indent the lines but the first one.  If $FULLP is zero, this
3534     function prints only a character code sequence.  Otherwise, it
3535     prints the internal byte sequence and text properties as well.
3536
3537     @return
3538     This function returns $MT.  */
3539 /***ja
3540     @brief M-text ¤ò¥À¥ó¥×¤¹¤ë.
3541
3542     ´Ø¿ô mdebug_dump_mtext () ¤Ï M-text $MT ¤ò stderr 
3543     ¤Ë¿Í´Ö¤Ë²ÄÆɤʷÁ¤Ç°õºþ¤¹¤ë¡£ $INDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£
3544     $FULLP ¤¬ 0 ¤Ê¤é¤Ð¡¢Ê¸»ú¥³¡¼¥ÉÎó¤À¤±¤ò°õºþ¤¹¤ë¡£
3545     ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢ÆâÉô¥Ð¥¤¥ÈÎó¤È¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤â°õºþ¤¹¤ë¡£
3546
3547     @return
3548     ¤³¤Î´Ø¿ô¤Ï $MT ¤òÊÖ¤¹¡£  */
3549
3550 MText *
3551 mdebug_dump_mtext (MText *mt, int indent, int fullp)
3552 {
3553   char *prefix = (char *) alloca (indent + 1);
3554   int i;
3555   unsigned char *p;
3556
3557   memset (prefix, 32, indent);
3558   prefix[indent] = 0;
3559
3560   fprintf (stderr,
3561            "(mtext (size %d %d %d) (cache %d %d)",
3562            mt->nchars, mt->nbytes, mt->allocated,
3563            mt->cache_char_pos, mt->cache_byte_pos);
3564   if (! fullp)
3565     {
3566       fprintf (stderr, " \"");
3567       for (i = 0; i < mt->nchars; i++)
3568         {
3569           int c = mtext_ref_char (mt, i);
3570           if (c >= ' ' && c < 127)
3571             fprintf (stderr, "%c", c);
3572           else
3573             fprintf (stderr, "\\x%02X", c);
3574         }
3575       fprintf (stderr, "\"");
3576     }
3577   else if (mt->nchars > 0)
3578     {
3579       fprintf (stderr, "\n%s (bytes \"", prefix);
3580       for (i = 0; i < mt->nbytes; i++)
3581         fprintf (stderr, "\\x%02x", mt->data[i]);
3582       fprintf (stderr, "\")\n");
3583       fprintf (stderr, "%s (chars \"", prefix);
3584       p = mt->data;
3585       for (i = 0; i < mt->nchars; i++)
3586         {
3587           int len;
3588           int c = STRING_CHAR_AND_BYTES (p, len);
3589
3590           if (c >= ' ' && c < 127 && c != '\\' && c != '\"')
3591             fputc (c, stderr);
3592           else
3593             fprintf (stderr, "\\x%X", c);
3594           p += len;
3595         }
3596       fprintf (stderr, "\")");
3597       if (mt->plist)
3598         {
3599           fprintf (stderr, "\n%s ", prefix);
3600           dump_textplist (mt->plist, indent + 1);
3601         }
3602     }
3603   fprintf (stderr, ")");
3604   return mt;
3605 }
3606
3607 /*** @} */ 
3608
3609 /*
3610   Local Variables:
3611   coding: euc-japan
3612   End:
3613 */