--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <m17n-core.h>
+
+#define NUM_PROPS 2
+
+typedef struct
+{
+ int nchars;
+ int chars[30];
+ char props[30][NUM_PROPS + 1];
+} Text;
+
+int chars[] =
+ { 'A', 'B', 'C', 'D', /* 1-byte */
+ 0xA0, 0x100, 0x400, /* 2-byte */
+ 0x800, 0x1000, 0xF000, /* 3-byte */
+ 0x10000, 0x20000 /* 4-byte */
+ };
+
+int nchars = sizeof (chars) / sizeof (chars[0]);
+
+struct Log {
+ Text orig;
+ int from, to;
+ Text new;
+};
+
+void
+gen_text (MText *mt, Text *t)
+{
+ int i;
+
+ mtext_del (mt, 0, mtext_len (mt));
+ memset (t, 0, sizeof (Text));
+ for (i = 0; i < nchars; i++)
+ if (rand () % 2)
+ {
+ mtext_cat_char (mt, chars[i]);
+ t->chars[t->nchars++] = chars[i];
+ }
+ for (i = 0; i < t->nchars; i++)
+ {
+ int nprops = rand () % (NUM_PROPS + 1);
+
+ t->props[i][nprops] = '\0';
+ while (nprops-- > 0)
+ {
+ int val = '0' + rand () % 10;
+
+ mtext_push_prop (mt, i, i + 1, Mt, (void *) val);
+ t->props[i][nprops] = val;
+ }
+ }
+}
+
+void
+text_replace (Text *t1, int from1, int to1, Text *t2, int from2, int to2,
+ struct Log *log)
+{
+ int len1 = to1 - from1;
+ int len2 = to2 - from2;
+ int i;
+
+ log->orig = *t1;
+ log->from = from1, log->to = to1;
+ log->new.nchars = len2;
+ for (i = 0; i < len2; i++)
+ log->new.chars[i] = t2->chars[from2 + i];
+
+ if (len1 != len2)
+ {
+ memmove (t1->chars + from1 + len2, t1->chars + to1,
+ sizeof (int) * (t1->nchars - to1));
+ memmove (t1->props[from1 + len2], t1->props[to1],
+ (NUM_PROPS + 1) * (t1->nchars - to1));
+ }
+ memcpy (t1->chars + from1, t2->chars + from2,
+ sizeof (int) * len2);
+ if (len1 > 0)
+ {
+ to2 = from1 + len2;
+ for (i = to1; i < to2; i++)
+ memcpy (t1->props[i], t1->props[to1 - 1], NUM_PROPS + 1);
+ }
+ t1->nchars += len2 - len1;
+}
+
+void
+dump_text (const char *prefix, Text *t)
+{
+ int i;
+
+ printf ("%s", prefix);
+ if (t->nchars == 0)
+ printf (" null text\n");
+ else
+ {
+ for (i = 0; i < t->nchars; i++)
+ printf (" %04X", t->chars[i]);
+ printf ("\n%s", prefix);
+ for (i = 0; i < t->nchars; i++)
+ printf (" %-4s", t->props[i]);
+ printf ("\n");
+ }
+}
+
+void
+dump_mtext (const char *prefix, MText *mt)
+{
+ int len = mtext_len (mt), i;
+
+ printf ("%s", prefix);
+ for (i = 0; i < len; i++)
+ printf (" %04X", mtext_ref_char (mt, i));
+ printf ("\n%s", prefix);
+ for (i = 0; i < len; i++)
+ {
+ void *values[NUM_PROPS];
+ int j, n;
+
+ n = mtext_get_prop_values (mt, i, Mt, values, NUM_PROPS);
+ printf (" ");
+ for (j = n - 1; j >= 0; j--)
+ printf ("%c", (int) values[j]);
+ while (n++ < 4)
+ printf (" ");
+ }
+ printf ("\n");
+}
+
+void
+error_exit (int count, struct Log *log, MText *mt, Text *t)
+{
+ printf ("Count: %d, replace %d-%d with", count, log->from, log->to);
+ dump_text ("", &log->new);
+ dump_text ("Orig: ", &log->orig);
+ dump_text ("Text: ", t);
+ dump_mtext ("M-text:", mt);
+ exit (0);
+}
+
+int
+compare (MText *mt, Text *t)
+{
+ int i;
+
+ if (mtext_len (mt) != t->nchars)
+ return -1;
+ for (i = 0; i < t->nchars; i++)
+ {
+ void *values[NUM_PROPS];
+ int j, n;
+
+ if (mtext_ref_char (mt, i) != t->chars[i])
+ return -1;
+ n = mtext_get_prop_values (mt, i, Mt, values, NUM_PROPS);
+ for (j = 0; j < n; j++)
+ if ((int) values[j] != t->props[i][n - 1 - j])
+ return -1;
+ }
+ return 0;
+}
+
+#define DEFAULT_STEP 0x10000
+
+int
+main (int argc, char **argv)
+{
+ MText *mt1, *mt2;
+ Text t1, t2;
+ int len1, len2;
+ int from1, to1, from2, to2;
+ int count;
+ int step = DEFAULT_STEP;
+ struct Log log;
+
+ M17N_INIT ();
+ mt1 = mtext ();
+ mt2 = mtext ();
+
+ {
+ char *stepchar = getenv ("STEP");
+
+ if (stepchar)
+ {
+ step = atoi (stepchar);
+ if (step <= 0)
+ step = DEFAULT_STEP;
+ }
+ }
+
+ for (count = 0; ; count++)
+ {
+ if (((count + 1) % step) == 0)
+ printf ("Count 0x%X\n", count + 1);
+
+ gen_text (mt1, &t1);
+ len1 = t1.nchars;
+ gen_text (mt2, &t2);
+ len2 = t2.nchars;
+
+ if (len1 > 0)
+ from1 = rand () % len1, to1 = from1 + (rand () % (len1 - from1));
+ else
+ from1 = to1 = 0;
+ if (len2 > 0)
+ from2 = rand () % len2, to2 = from2 + (rand () % (len2 - from2));
+ else
+ from2 = to2 = 0;
+ text_replace (&t1, from1, to1, &t2, from2, to2, &log);
+ if (mtext_replace (mt1, from1, to1, mt2, from2, to2) < 0
+ || compare (mt1, &t1) < 0)
+ error_exit (count, &log, mt1, &t1);
+ }
+ exit (0);
+}