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