+static MCharTable *tricky_chars, *cased, *soft_dotted, *case_mapping;
+static MCharTable *combining_class;
+
+/* Languages that require special handling in case-conversion. */
+static MSymbol Mlt, Mtr, Maz;
+
+static MText *gr03A3;
+static MText *lt0049, *lt004A, *lt012E, *lt00CC, *lt00CD, *lt0128;
+static MText *tr0130, *tr0049, *tr0069;
+
+static int
+init_case_conversion ()
+{
+ Mlt = msymbol ("lt");
+ Mtr = msymbol ("tr");
+ Maz = msymbol ("az");
+
+ gr03A3 = mtext ();
+ mtext_cat_char (gr03A3, 0x03C2);
+
+ lt0049 = mtext ();
+ mtext_cat_char (lt0049, 0x0069);
+ mtext_cat_char (lt0049, 0x0307);
+
+ lt004A = mtext ();
+ mtext_cat_char (lt004A, 0x006A);
+ mtext_cat_char (lt004A, 0x0307);
+
+ lt012E = mtext ();
+ mtext_cat_char (lt012E, 0x012F);
+ mtext_cat_char (lt012E, 0x0307);
+
+ lt00CC = mtext ();
+ mtext_cat_char (lt00CC, 0x0069);
+ mtext_cat_char (lt00CC, 0x0307);
+ mtext_cat_char (lt00CC, 0x0300);
+
+ lt00CD = mtext ();
+ mtext_cat_char (lt00CD, 0x0069);
+ mtext_cat_char (lt00CD, 0x0307);
+ mtext_cat_char (lt00CD, 0x0301);
+
+ lt0128 = mtext ();
+ mtext_cat_char (lt0128, 0x0069);
+ mtext_cat_char (lt0128, 0x0307);
+ mtext_cat_char (lt0128, 0x0303);
+
+ tr0130 = mtext ();
+ mtext_cat_char (tr0130, 0x0069);
+
+ tr0049 = mtext ();
+ mtext_cat_char (tr0049, 0x0131);
+
+ tr0069 = mtext ();
+ mtext_cat_char (tr0069, 0x0130);
+
+ if (! (cased = mchar_get_prop_table (msymbol ("cased"), NULL)))
+ return -1;
+ if (! (soft_dotted = mchar_get_prop_table (msymbol ("soft-dotted"), NULL)))
+ return -1;
+ if (! (case_mapping = mchar_get_prop_table (msymbol ("case-mapping"), NULL)))
+ return -1;
+ if (! (combining_class = mchar_get_prop_table (Mcombining_class, NULL)))
+ return -1;
+
+ tricky_chars = mchartable (Mnil, 0);
+ mchartable_set (tricky_chars, 0x0049, (void *) 1);
+ mchartable_set (tricky_chars, 0x004A, (void *) 1);
+ mchartable_set (tricky_chars, 0x00CC, (void *) 1);
+ mchartable_set (tricky_chars, 0x00CD, (void *) 1);
+ mchartable_set (tricky_chars, 0x0128, (void *) 1);
+ mchartable_set (tricky_chars, 0x012E, (void *) 1);
+ mchartable_set (tricky_chars, 0x0130, (void *) 1);
+ mchartable_set (tricky_chars, 0x0307, (void *) 1);
+ mchartable_set (tricky_chars, 0x03A3, (void *) 1);
+ return 0;
+}
+
+#define CASE_CONV_INIT(ret) \
+ do { \
+ if (! tricky_chars \
+ && init_case_conversion () < 0) \
+ MERROR (MERROR_MTEXT, ret); \
+ } while (0)
+
+/* Replace the character at POS of MT with VAR and increment I and LEN. */
+
+#define REPLACE(var) \
+ do { \
+ int varlen = var->nchars; \
+ \
+ mtext_replace (mt, pos, pos + 1, var, 0, varlen); \
+ pos += varlen; \
+ end += varlen - 1; \
+ } while (0)
+
+/* Delete the character at POS of MT and decrement LEN. */
+
+#define DELETE \
+ do { \
+ mtext_del (mt, pos, pos + 1); \
+ end--; \
+ } while (0)
+
+#define LOOKUP \
+ do { \
+ MPlist *pl = (MPlist *) mchartable_lookup (case_mapping, c); \
+ \
+ if (pl) \
+ { \
+ /* Lowercase is the 1st element. */ \
+ MText *lower = MPLIST_VAL ((MPlist *) MPLIST_VAL (pl)); \
+ int llen = mtext_nchars (lower); \
+ \
+ if (mtext_ref_char (lower, 0) != c || llen > 1) \
+ { \
+ mtext_replace (mt, pos, pos + 1, lower, 0, llen); \
+ pos += llen; \
+ end += llen - 1; \
+ } \
+ else \
+ pos++; \
+ } \
+ else \
+ pos++; \
+ } while (0)
+
+
+int
+uppercase_precheck (MText *mt, int pos, int end)
+{
+ for (; pos < end; pos++)
+ if (mtext_ref_char (mt, pos) == 0x0307 &&
+ (MSymbol) mtext_get_prop (mt, pos, Mlanguage) == Mlt)
+ return 1;
+ return 0;
+}
+
+int
+lowercase_precheck (MText *mt, int pos, int end)
+{
+ int c;
+ MSymbol lang;
+
+ for (; pos < end; pos++)
+ {
+ c = mtext_ref_char (mt, pos);
+
+ if ((int) mchartable_lookup (tricky_chars, c) == 1)
+ {
+ if (c == 0x03A3)
+ return 1;
+
+ lang = mtext_get_prop (mt, pos, Mlanguage);
+
+ if (lang == Mlt &&
+ (c == 0x0049 || c == 0x004A || c == 0x012E))
+ return 1;
+
+ if ((lang == Mtr || lang == Maz) &&
+ (c == 0x0307 || c == 0x0049))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#define CASED 1
+#define CASE_IGNORABLE 2
+
+int
+final_sigma (MText *mt, int pos)
+{
+ int i, len = mtext_len (mt);
+ int c;
+
+ for (i = pos - 1; i >= 0; i--)
+ {
+ c = (int) mchartable_lookup (cased, mtext_ref_char (mt, i));
+ if (c == -1)
+ c = 0;
+ if (c & CASED)
+ break;
+ if (! (c & CASE_IGNORABLE))
+ return 0;
+ }
+
+ if (i == -1)
+ return 0;
+
+ for (i = pos + 1; i < len; i++)
+ {
+ c = (int) mchartable_lookup (cased, mtext_ref_char (mt, i));
+ if (c == -1)
+ c = 0;
+ if (c & CASED)
+ return 0;
+ if (! (c & CASE_IGNORABLE))
+ return 1;
+ }
+
+ return 1;
+}
+
+int
+after_soft_dotted (MText *mt, int i)
+{
+ int c, class;
+
+ for (i--; i >= 0; i--)
+ {
+ c = mtext_ref_char (mt, i);
+ if ((MSymbol) mchartable_lookup (soft_dotted, c) == Mt)
+ return 1;
+ class = (int) mchartable_lookup (combining_class, c);
+ if (class == 0 || class == 230)
+ return 0;
+ }
+
+ return 0;
+}
+
+int
+more_above (MText *mt, int i)
+{
+ int class, len = mtext_len (mt);
+
+ for (i++; i < len; i++)
+ {
+ class = (int) mchartable_lookup (combining_class,
+ mtext_ref_char (mt, i));
+ if (class == 230)
+ return 1;
+ if (class == 0)
+ return 0;
+ }
+
+ return 0;
+}
+
+int
+before_dot (MText *mt, int i)
+{
+ int c, class, len = mtext_len (mt);
+
+ for (i++; i < len; i++)
+ {
+ c = mtext_ref_char (mt, i);
+ if (c == 0x0307)
+ return 1;
+ class = (int) mchartable_lookup (combining_class, c);
+ if (class == 230 || class == 0)
+ return 0;
+ }
+
+ return 0;
+}
+
+int
+after_i (MText *mt, int i)
+{
+ int c, class;
+
+ for (i--; i >= 0; i--)
+ {
+ c = mtext_ref_char (mt, i);
+ if (c == (int) 'I')
+ return 1;
+ class = (int) mchartable_lookup (combining_class, c);
+ if (class == 230 || class == 0)
+ return 0;
+ }
+
+ return 0;
+}
+