Merge changes from emacs-23 branch.
[bpt/emacs.git] / src / bidi.c
index fee97ae..1f14f6a 100644 (file)
@@ -26,13 +26,13 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    designed to be called once for every character in the buffer or
    string.
 
-   The main entry point is bidi_get_next_char_visually.  Each time it
+   The main entry point is bidi_move_to_visually_next.  Each time it
    is called, it finds the next character in the visual order, and
    returns its information in a special structure.  The caller is then
    expected to process this character for display or any other
-   purposes, and call bidi_get_next_char_visually for the next
-   character.  See the comments in bidi_get_next_char_visually for
-   more details about its algorithm that finds the next visual-order
+   purposes, and call bidi_move_to_visually_next for the next
+   character.  See the comments in bidi_move_to_visually_next for more
+   details about its algorithm that finds the next visual-order
    character by resolving their levels on the fly.
 
    The two other entry points are bidi_paragraph_init and
@@ -49,16 +49,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    rule X9 and to its modifications decribed in the "Implementation
    Notes" section of UAX#9, under "Retaining Format Codes".  */
 
-#ifdef HAVE_CONFIG_H
 #include <config.h>
-#endif
-
 #include <stdio.h>
-
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-
 #include <setjmp.h>
 
 #include "lisp.h"
@@ -68,7 +60,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 static int bidi_initialized = 0;
 
-static Lisp_Object bidi_type_table;
+static Lisp_Object bidi_type_table, bidi_mirror_table;
 
 /* FIXME: Remove these when bidi_explicit_dir_char uses a lookup table.  */
 #define LRM_CHAR   0x200E
@@ -86,10 +78,10 @@ static Lisp_Object bidi_type_table;
 
 /* What we need to know about the current paragraph.  */
 struct bidi_paragraph_info {
-  int start_bytepos;   /* byte position where it begins */
-  int end_bytepos;     /* byte position where it ends */
-  int embedding_level; /* its basic embedding level */
-  bidi_dir_t base_dir; /* its base direction */
+  EMACS_INT start_bytepos;     /* byte position where it begins */
+  EMACS_INT end_bytepos;       /* byte position where it ends */
+  int      embedding_level;    /* its basic embedding level */
+  bidi_dir_t base_dir;         /* its base direction */
 };
 
 /* Data type for describing the bidirectional character categories.  */
@@ -102,317 +94,44 @@ typedef enum {
 
 int bidi_ignore_explicit_marks_for_paragraph_level = 1;
 
-static Lisp_Object fallback_paragraph_start_re, fallback_paragraph_separate_re;
+static Lisp_Object paragraph_start_re, paragraph_separate_re;
 static Lisp_Object Qparagraph_start, Qparagraph_separate;
 
 static void
-bidi_initialize ()
+bidi_initialize (void)
 {
-  /* FIXME: This should come from the Unicode Database.  */
-  struct {
-    int from, to;
-    bidi_type_t type;
-  } bidi_type[] =
-      { { 0x0000, 0x0008, WEAK_BN },
-       { 0x0009, 0x0000, NEUTRAL_S },
-       { 0x000A, 0x0000, NEUTRAL_B },
-       { 0x000B, 0x0000, NEUTRAL_S },
-       { 0x000C, 0x0000, NEUTRAL_WS },
-       { 0x000D, 0x0000, NEUTRAL_B },
-       { 0x000E, 0x001B, WEAK_BN },
-       { 0x001C, 0x001E, NEUTRAL_B },
-       { 0x001F, 0x0000, NEUTRAL_S },
-       { 0x0020, 0x0000, NEUTRAL_WS },
-       { 0x0021, 0x0022, NEUTRAL_ON },
-       { 0x0023, 0x0025, WEAK_ET },
-       { 0x0026, 0x002A, NEUTRAL_ON },
-       { 0x002B, 0x0000, WEAK_ES },
-       { 0x002C, 0x0000, WEAK_CS },
-       { 0x002D, 0x0000, WEAK_ES },
-       { 0x002E, 0x002F, WEAK_CS },
-       { 0x0030, 0x0039, WEAK_EN },
-       { 0x003A, 0x0000, WEAK_CS },
-       { 0x003B, 0x0040, NEUTRAL_ON },
-       { 0x005B, 0x0060, NEUTRAL_ON },
-       { 0x007B, 0x007E, NEUTRAL_ON },
-       { 0x007F, 0x0084, WEAK_BN },
-       { 0x0085, 0x0000, NEUTRAL_B },
-       { 0x0086, 0x009F, WEAK_BN },
-       { 0x00A0, 0x0000, WEAK_CS },
-       { 0x00A1, 0x0000, NEUTRAL_ON },
-       { 0x00A2, 0x00A5, WEAK_ET },
-       { 0x00A6, 0x00A9, NEUTRAL_ON },
-       { 0x00AB, 0x00AC, NEUTRAL_ON },
-       { 0x00AD, 0x0000, WEAK_BN },
-       { 0x00AE, 0x00Af, NEUTRAL_ON },
-       { 0x00B0, 0x00B1, WEAK_ET },
-       { 0x00B2, 0x00B3, WEAK_EN },
-       { 0x00B4, 0x0000, NEUTRAL_ON },
-       { 0x00B6, 0x00B8, NEUTRAL_ON },
-       { 0x00B9, 0x0000, WEAK_EN },
-       { 0x00BB, 0x00BF, NEUTRAL_ON },
-       { 0x00D7, 0x0000, NEUTRAL_ON },
-       { 0x00F7, 0x0000, NEUTRAL_ON },
-       { 0x02B9, 0x02BA, NEUTRAL_ON },
-       { 0x02C2, 0x02CF, NEUTRAL_ON },
-       { 0x02D2, 0x02DF, NEUTRAL_ON },
-       { 0x02E5, 0x02ED, NEUTRAL_ON },
-       { 0x0300, 0x036F, WEAK_NSM },
-       { 0x0374, 0x0375, NEUTRAL_ON },
-       { 0x037E, 0x0385, NEUTRAL_ON },
-       { 0x0387, 0x0000, NEUTRAL_ON },
-       { 0x03F6, 0x0000, NEUTRAL_ON },
-       { 0x0483, 0x0489, WEAK_NSM },
-       { 0x058A, 0x0000, NEUTRAL_ON },
-       { 0x0591, 0x05BD, WEAK_NSM },
-       { 0x05BE, 0x0000, STRONG_R },
-       { 0x05BF, 0x0000, WEAK_NSM },
-       { 0x05C0, 0x0000, STRONG_R },
-       { 0x05C1, 0x05C2, WEAK_NSM },
-       { 0x05C3, 0x0000, STRONG_R },
-       { 0x05C4, 0x05C5, WEAK_NSM },
-       { 0x05C6, 0x0000, STRONG_R },
-       { 0x05C7, 0x0000, WEAK_NSM },
-       { 0x05D0, 0x05F4, STRONG_R },
-       { 0x060C, 0x0000, WEAK_CS },
-       { 0x061B, 0x064A, STRONG_AL },
-       { 0x064B, 0x0655, WEAK_NSM },
-       { 0x0660, 0x0669, WEAK_AN },
-       { 0x066A, 0x0000, WEAK_ET },
-       { 0x066B, 0x066C, WEAK_AN },
-       { 0x066D, 0x066F, STRONG_AL },
-       { 0x0670, 0x0000, WEAK_NSM },
-       { 0x0671, 0x06D5, STRONG_AL },
-       { 0x06D6, 0x06DC, WEAK_NSM },
-       { 0x06DD, 0x0000, STRONG_AL },
-       { 0x06DE, 0x06E4, WEAK_NSM },
-       { 0x06E5, 0x06E6, STRONG_AL },
-       { 0x06E7, 0x06E8, WEAK_NSM },
-       { 0x06E9, 0x0000, NEUTRAL_ON },
-       { 0x06EA, 0x06ED, WEAK_NSM },
-       { 0x06F0, 0x06F9, WEAK_EN },
-       { 0x06FA, 0x070D, STRONG_AL },
-       { 0x070F, 0x0000, WEAK_BN },
-       { 0x0710, 0x0000, STRONG_AL },
-       { 0x0711, 0x0000, WEAK_NSM },
-       { 0x0712, 0x072C, STRONG_AL },
-       { 0x0730, 0x074A, WEAK_NSM },
-       { 0x0780, 0x07A5, STRONG_AL },
-       { 0x07A6, 0x07B0, WEAK_NSM },
-       { 0x07B1, 0x0000, STRONG_AL },
-       { 0x0901, 0x0902, WEAK_NSM },
-       { 0x093C, 0x0000, WEAK_NSM },
-       { 0x0941, 0x0948, WEAK_NSM },
-       { 0x094D, 0x0000, WEAK_NSM },
-       { 0x0951, 0x0954, WEAK_NSM },
-       { 0x0962, 0x0963, WEAK_NSM },
-       { 0x0981, 0x0000, WEAK_NSM },
-       { 0x09BC, 0x0000, WEAK_NSM },
-       { 0x09C1, 0x09C4, WEAK_NSM },
-       { 0x09CD, 0x0000, WEAK_NSM },
-       { 0x09E2, 0x09E3, WEAK_NSM },
-       { 0x09F2, 0x09F3, WEAK_ET },
-       { 0x0A02, 0x0000, WEAK_NSM },
-       { 0x0A3C, 0x0000, WEAK_NSM },
-       { 0x0A41, 0x0A4D, WEAK_NSM },
-       { 0x0A70, 0x0A71, WEAK_NSM },
-       { 0x0A81, 0x0A82, WEAK_NSM },
-       { 0x0ABC, 0x0000, WEAK_NSM },
-       { 0x0AC1, 0x0AC8, WEAK_NSM },
-       { 0x0ACD, 0x0000, WEAK_NSM },
-       { 0x0B01, 0x0000, WEAK_NSM },
-       { 0x0B3C, 0x0000, WEAK_NSM },
-       { 0x0B3F, 0x0000, WEAK_NSM },
-       { 0x0B41, 0x0B43, WEAK_NSM },
-       { 0x0B4D, 0x0B56, WEAK_NSM },
-       { 0x0B82, 0x0000, WEAK_NSM },
-       { 0x0BC0, 0x0000, WEAK_NSM },
-       { 0x0BCD, 0x0000, WEAK_NSM },
-       { 0x0C3E, 0x0C40, WEAK_NSM },
-       { 0x0C46, 0x0C56, WEAK_NSM },
-       { 0x0CBF, 0x0000, WEAK_NSM },
-       { 0x0CC6, 0x0000, WEAK_NSM },
-       { 0x0CCC, 0x0CCD, WEAK_NSM },
-       { 0x0D41, 0x0D43, WEAK_NSM },
-       { 0x0D4D, 0x0000, WEAK_NSM },
-       { 0x0DCA, 0x0000, WEAK_NSM },
-       { 0x0DD2, 0x0DD6, WEAK_NSM },
-       { 0x0E31, 0x0000, WEAK_NSM },
-       { 0x0E34, 0x0E3A, WEAK_NSM },
-       { 0x0E3F, 0x0000, WEAK_ET },
-       { 0x0E47, 0x0E4E, WEAK_NSM },
-       { 0x0EB1, 0x0000, WEAK_NSM },
-       { 0x0EB4, 0x0EBC, WEAK_NSM },
-       { 0x0EC8, 0x0ECD, WEAK_NSM },
-       { 0x0F18, 0x0F19, WEAK_NSM },
-       { 0x0F35, 0x0000, WEAK_NSM },
-       { 0x0F37, 0x0000, WEAK_NSM },
-       { 0x0F39, 0x0000, WEAK_NSM },
-       { 0x0F3A, 0x0F3D, NEUTRAL_ON },
-       { 0x0F71, 0x0F7E, WEAK_NSM },
-       { 0x0F80, 0x0F84, WEAK_NSM },
-       { 0x0F86, 0x0F87, WEAK_NSM },
-       { 0x0F90, 0x0FBC, WEAK_NSM },
-       { 0x0FC6, 0x0000, WEAK_NSM },
-       { 0x102D, 0x1030, WEAK_NSM },
-       { 0x1032, 0x1037, WEAK_NSM },
-       { 0x1039, 0x0000, WEAK_NSM },
-       { 0x1058, 0x1059, WEAK_NSM },
-       { 0x1680, 0x0000, NEUTRAL_WS },
-       { 0x169B, 0x169C, NEUTRAL_ON },
-       { 0x1712, 0x1714, WEAK_NSM },
-       { 0x1732, 0x1734, WEAK_NSM },
-       { 0x1752, 0x1753, WEAK_NSM },
-       { 0x1772, 0x1773, WEAK_NSM },
-       { 0x17B7, 0x17BD, WEAK_NSM },
-       { 0x17C6, 0x0000, WEAK_NSM },
-       { 0x17C9, 0x17D3, WEAK_NSM },
-       { 0x17DB, 0x0000, WEAK_ET },
-       { 0x1800, 0x180A, NEUTRAL_ON },
-       { 0x180B, 0x180D, WEAK_NSM },
-       { 0x180E, 0x0000, WEAK_BN },
-       { 0x18A9, 0x0000, WEAK_NSM },
-       { 0x1FBD, 0x0000, NEUTRAL_ON },
-       { 0x1FBF, 0x1FC1, NEUTRAL_ON },
-       { 0x1FCD, 0x1FCF, NEUTRAL_ON },
-       { 0x1FDD, 0x1FDF, NEUTRAL_ON },
-       { 0x1FED, 0x1FEF, NEUTRAL_ON },
-       { 0x1FFD, 0x1FFE, NEUTRAL_ON },
-       { 0x2000, 0x200A, NEUTRAL_WS },
-       { 0x200B, 0x200D, WEAK_BN },
-       { 0x200F, 0x0000, STRONG_R },
-       { 0x2010, 0x2027, NEUTRAL_ON },
-       { 0x2028, 0x0000, NEUTRAL_WS },
-       { 0x2029, 0x0000, NEUTRAL_B },
-       { 0x202A, 0x0000, LRE },
-       { 0x202B, 0x0000, RLE },
-       { 0x202C, 0x0000, PDF },
-       { 0x202D, 0x0000, LRO },
-       { 0x202E, 0x0000, RLO },
-       { 0x202F, 0x0000, NEUTRAL_WS },
-       { 0x2030, 0x2034, WEAK_ET },
-       { 0x2035, 0x2057, NEUTRAL_ON },
-       { 0x205F, 0x0000, NEUTRAL_WS },
-       { 0x2060, 0x206F, WEAK_BN },
-       { 0x2070, 0x0000, WEAK_EN },
-       { 0x2074, 0x2079, WEAK_EN },
-       { 0x207A, 0x207B, WEAK_ET },
-       { 0x207C, 0x207E, NEUTRAL_ON },
-       { 0x2080, 0x2089, WEAK_EN },
-       { 0x208A, 0x208B, WEAK_ET },
-       { 0x208C, 0x208E, NEUTRAL_ON },
-       { 0x20A0, 0x20B1, WEAK_ET },
-       { 0x20D0, 0x20EA, WEAK_NSM },
-       { 0x2100, 0x2101, NEUTRAL_ON },
-       { 0x2103, 0x2106, NEUTRAL_ON },
-       { 0x2108, 0x2109, NEUTRAL_ON },
-       { 0x2114, 0x0000, NEUTRAL_ON },
-       { 0x2116, 0x2118, NEUTRAL_ON },
-       { 0x211E, 0x2123, NEUTRAL_ON },
-       { 0x2125, 0x0000, NEUTRAL_ON },
-       { 0x2127, 0x0000, NEUTRAL_ON },
-       { 0x2129, 0x0000, NEUTRAL_ON },
-       { 0x212E, 0x0000, WEAK_ET },
-       { 0x2132, 0x0000, NEUTRAL_ON },
-       { 0x213A, 0x0000, NEUTRAL_ON },
-       { 0x2140, 0x2144, NEUTRAL_ON },
-       { 0x214A, 0x215F, NEUTRAL_ON },
-       { 0x2190, 0x2211, NEUTRAL_ON },
-       { 0x2212, 0x2213, WEAK_ET },
-       { 0x2214, 0x2335, NEUTRAL_ON },
-       { 0x237B, 0x2394, NEUTRAL_ON },
-       { 0x2396, 0x244A, NEUTRAL_ON },
-       { 0x2460, 0x249B, WEAK_EN },
-       { 0x24EA, 0x0000, WEAK_EN },
-       { 0x24EB, 0x2FFB, NEUTRAL_ON },
-       { 0x3000, 0x0000, NEUTRAL_WS },
-       { 0x3001, 0x3004, NEUTRAL_ON },
-       { 0x3008, 0x3020, NEUTRAL_ON },
-       { 0x302A, 0x302F, WEAK_NSM },
-       { 0x3030, 0x0000, NEUTRAL_ON },
-       { 0x3036, 0x3037, NEUTRAL_ON },
-       { 0x303D, 0x303F, NEUTRAL_ON },
-       { 0x3099, 0x309A, WEAK_NSM },
-       { 0x309B, 0x309C, NEUTRAL_ON },
-       { 0x30A0, 0x0000, NEUTRAL_ON },
-       { 0x30FB, 0x0000, NEUTRAL_ON },
-       { 0x3251, 0x325F, NEUTRAL_ON },
-       { 0x32B1, 0x32BF, NEUTRAL_ON },
-       { 0xA490, 0xA4C6, NEUTRAL_ON },
-       { 0xFB1D, 0x0000, STRONG_R },
-       { 0xFB1E, 0x0000, WEAK_NSM },
-       { 0xFB1F, 0xFB28, STRONG_R },
-       { 0xFB29, 0x0000, WEAK_ET },
-       { 0xFB2A, 0xFB4F, STRONG_R },
-       { 0xFB50, 0xFD3D, STRONG_AL },
-       { 0xFD3E, 0xFD3F, NEUTRAL_ON },
-       { 0xFD50, 0xFDFC, STRONG_AL },
-       { 0xFE00, 0xFE23, WEAK_NSM },
-       { 0xFE30, 0xFE4F, NEUTRAL_ON },
-       { 0xFE50, 0x0000, WEAK_CS },
-       { 0xFE51, 0x0000, NEUTRAL_ON },
-       { 0xFE52, 0x0000, WEAK_CS },
-       { 0xFE54, 0x0000, NEUTRAL_ON },
-       { 0xFE55, 0x0000, WEAK_CS },
-       { 0xFE56, 0xFE5E, NEUTRAL_ON },
-       { 0xFE5F, 0x0000, WEAK_ET },
-       { 0xFE60, 0xFE61, NEUTRAL_ON },
-       { 0xFE62, 0xFE63, WEAK_ET },
-       { 0xFE64, 0xFE68, NEUTRAL_ON },
-       { 0xFE69, 0xFE6A, WEAK_ET },
-       { 0xFE6B, 0x0000, NEUTRAL_ON },
-       { 0xFE70, 0xFEFC, STRONG_AL },
-       { 0xFEFF, 0x0000, WEAK_BN },
-       { 0xFF01, 0xFF02, NEUTRAL_ON },
-       { 0xFF03, 0xFF05, WEAK_ET },
-       { 0xFF06, 0xFF0A, NEUTRAL_ON },
-       { 0xFF0B, 0x0000, WEAK_ET },
-       { 0xFF0C, 0x0000, WEAK_CS },
-       { 0xFF0D, 0x0000, WEAK_ET },
-       { 0xFF0E, 0x0000, WEAK_CS },
-       { 0xFF0F, 0x0000, WEAK_ES },
-       { 0xFF10, 0xFF19, WEAK_EN },
-       { 0xFF1A, 0x0000, WEAK_CS },
-       { 0xFF1B, 0xFF20, NEUTRAL_ON },
-       { 0xFF3B, 0xFF40, NEUTRAL_ON },
-       { 0xFF5B, 0xFF65, NEUTRAL_ON },
-       { 0xFFE0, 0xFFE1, WEAK_ET },
-       { 0xFFE2, 0xFFE4, NEUTRAL_ON },
-       { 0xFFE5, 0xFFE6, WEAK_ET },
-       { 0xFFE8, 0xFFEE, NEUTRAL_ON },
-       { 0xFFF9, 0xFFFB, WEAK_BN },
-       { 0xFFFC, 0xFFFD, NEUTRAL_ON },
-       { 0x1D167, 0x1D169, WEAK_NSM },
-       { 0x1D173, 0x1D17A, WEAK_BN },
-       { 0x1D17B, 0x1D182, WEAK_NSM },
-       { 0x1D185, 0x1D18B, WEAK_NSM },
-       { 0x1D1AA, 0x1D1AD, WEAK_NSM },
-       { 0x1D7CE, 0x1D7FF, WEAK_EN },
-       { 0xE0001, 0xE007F, WEAK_BN } };
+
+#include "biditype.h"
+#include "bidimirror.h"
+
   int i;
 
   bidi_type_table = Fmake_char_table (Qnil, make_number (STRONG_L));
   staticpro (&bidi_type_table);
 
   for (i = 0; i < sizeof bidi_type / sizeof bidi_type[0]; i++)
-    char_table_set_range (bidi_type_table, bidi_type[i].from,
-                         bidi_type[i].to ? bidi_type[i].to : bidi_type[i].from,
+    char_table_set_range (bidi_type_table, bidi_type[i].from, bidi_type[i].to,
                          make_number (bidi_type[i].type));
 
-  fallback_paragraph_start_re =
-    Fsymbol_value (Fintern_soft (build_string ("paragraph-start"), Qnil));
-  if (!STRINGP (fallback_paragraph_start_re))
-    fallback_paragraph_start_re = build_string ("\f\\|[ \t]*$");
-  staticpro (&fallback_paragraph_start_re);
+  bidi_mirror_table = Fmake_char_table (Qnil, Qnil);
+  staticpro (&bidi_mirror_table);
+
+  for (i = 0; i < sizeof bidi_mirror / sizeof bidi_mirror[0]; i++)
+    char_table_set (bidi_mirror_table, bidi_mirror[i].from,
+                   make_number (bidi_mirror[i].to));
+
   Qparagraph_start = intern ("paragraph-start");
   staticpro (&Qparagraph_start);
-  fallback_paragraph_separate_re =
-    Fsymbol_value (Fintern_soft (build_string ("paragraph-separate"), Qnil));
-  if (!STRINGP (fallback_paragraph_separate_re))
-    fallback_paragraph_separate_re = build_string ("[ \t\f]*$");
-  staticpro (&fallback_paragraph_separate_re);
+  paragraph_start_re = Fsymbol_value (Qparagraph_start);
+  if (!STRINGP (paragraph_start_re))
+    paragraph_start_re = build_string ("\f\\|[ \t]*$");
+  staticpro (&paragraph_start_re);
   Qparagraph_separate = intern ("paragraph-separate");
   staticpro (&Qparagraph_separate);
+  paragraph_separate_re = Fsymbol_value (Qparagraph_separate);
+  if (!STRINGP (paragraph_separate_re))
+    paragraph_separate_re = build_string ("[ \t\f]*$");
+  staticpro (&paragraph_separate_re);
   bidi_initialized = 1;
 }
 
@@ -503,24 +222,31 @@ bidi_get_category (bidi_type_t type)
     }
 }
 
-/* Return the mirrored character of C, if any.
-
-   Note: The conditions in UAX#9 clause L4 must be tested by the
-   caller.  */
-/* FIXME: exceedingly temporary!  Should consult the Unicode database
-   of character properties.  */
+/* Return the mirrored character of C, if it has one.  If C has no
+   mirrored counterpart, return C.
+   Note: The conditions in UAX#9 clause L4 regarding the surrounding
+   context must be tested by the caller.  */
 int
 bidi_mirror_char (int c)
 {
-  static const char mirrored_pairs[] = "()<>[]{}";
-  const char *p = c > 0 && c < 128 ? strchr (mirrored_pairs, c) : NULL;
+  Lisp_Object val;
+
+  if (c == BIDI_EOB)
+    return c;
+  if (c < 0 || c > MAX_CHAR)
+    abort ();
 
-  if (p)
+  val = CHAR_TABLE_REF (bidi_mirror_table, c);
+  if (INTEGERP (val))
     {
-      size_t i = p - mirrored_pairs;
+      int v = XINT (val);
+
+      if (v < 0 || v > MAX_CHAR)
+       abort ();
 
-      return mirrored_pairs [(i ^ 1)];
+      return v;
     }
+
   return c;
 }
 
@@ -542,9 +268,12 @@ bidi_copy_it (struct bidi_it *to, struct bidi_it *from)
 
 /* Caching the bidi iterator states.  */
 
-static struct bidi_it bidi_cache[1000]; /* FIXME: make this dynamically allocated! */
-static int bidi_cache_idx;
-static int bidi_cache_last_idx;
+#define BIDI_CACHE_CHUNK 200
+static struct bidi_it *bidi_cache;
+static size_t bidi_cache_size = 0;
+static size_t elsz = sizeof (struct bidi_it);
+static int bidi_cache_idx;     /* next unused cache slot */
+static int bidi_cache_last_idx;        /* slot of last cache hit */
 
 static INLINE void
 bidi_cache_reset (void)
@@ -553,6 +282,18 @@ bidi_cache_reset (void)
   bidi_cache_last_idx = -1;
 }
 
+static INLINE void
+bidi_cache_shrink (void)
+{
+  if (bidi_cache_size > BIDI_CACHE_CHUNK)
+    {
+      bidi_cache_size = BIDI_CACHE_CHUNK;
+      bidi_cache =
+       (struct bidi_it *) xrealloc (bidi_cache, bidi_cache_size * elsz);
+    }
+  bidi_cache_reset ();
+}
+
 static INLINE void
 bidi_cache_fetch_state (int idx, struct bidi_it *bidi_it)
 {
@@ -571,7 +312,7 @@ bidi_cache_fetch_state (int idx, struct bidi_it *bidi_it)
    resolved levels in cached states.  DIR, if non-zero, means search
    in that direction from the last cache hit.  */
 static INLINE int
-bidi_cache_search (int charpos, int level, int dir)
+bidi_cache_search (EMACS_INT charpos, int level, int dir)
 {
   int i, i_start;
 
@@ -674,9 +415,13 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
   if (idx < 0)
     {
       idx = bidi_cache_idx;
-      /* Don't overrun the cache limit.  */
-      if (idx > sizeof (bidi_cache) / sizeof (bidi_cache[0]) - 1)
-       abort ();
+      /* Enlarge the cache as needed.  */
+      if (idx >= bidi_cache_size)
+       {
+         bidi_cache_size += BIDI_CACHE_CHUNK;
+         bidi_cache =
+           (struct bidi_it *) xrealloc (bidi_cache, bidi_cache_size * elsz);
+       }
       /* Character positions should correspond to cache positions 1:1.
         If we are outside the range of cached positions, the cache is
         useless and must be reset.  */
@@ -690,7 +435,6 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
       bidi_copy_it (&bidi_cache[idx], bidi_it);
       if (!resolved)
        bidi_cache[idx].resolved_level = -1;
-      bidi_cache[idx].new_paragraph = 0;
     }
   else
     {
@@ -717,7 +461,7 @@ bidi_cache_iterator_state (struct bidi_it *bidi_it, int resolved)
 }
 
 static INLINE bidi_type_t
-bidi_cache_find (int charpos, int level, struct bidi_it *bidi_it)
+bidi_cache_find (EMACS_INT charpos, int level, struct bidi_it *bidi_it)
 {
   int i = bidi_cache_search (charpos, level, bidi_it->scan_dir);
 
@@ -752,16 +496,12 @@ bidi_peek_at_next_level (struct bidi_it *bidi_it)
 static EMACS_INT
 bidi_at_paragraph_end (EMACS_INT charpos, EMACS_INT bytepos)
 {
-  Lisp_Object sep_re = Fbuffer_local_value (Qparagraph_separate,
-                                           Fcurrent_buffer ());
-  Lisp_Object start_re = Fbuffer_local_value (Qparagraph_start,
-                                             Fcurrent_buffer ());
+  Lisp_Object sep_re;
+  Lisp_Object start_re;
   EMACS_INT val;
 
-  if (!STRINGP (sep_re))
-    sep_re = fallback_paragraph_separate_re;
-  if (!STRINGP (start_re))
-    start_re = fallback_paragraph_start_re;
+  sep_re = paragraph_separate_re;
+  start_re = paragraph_start_re;
 
   val = fast_looking_at (sep_re, charpos, bytepos, ZV, ZV_BYTE, Qnil);
   if (val < 0)
@@ -830,11 +570,9 @@ bidi_line_init (struct bidi_it *bidi_it)
 static EMACS_INT
 bidi_find_paragraph_start (EMACS_INT pos, EMACS_INT pos_byte)
 {
-  Lisp_Object re = Fbuffer_local_value (Qparagraph_start, Fcurrent_buffer ());
+  Lisp_Object re = paragraph_start_re;
   EMACS_INT limit = ZV, limit_byte = ZV_BYTE;
 
-  if (!STRINGP (re))
-    re = fallback_paragraph_start_re;
   while (pos_byte > BEGV_BYTE
         && fast_looking_at (re, pos, pos_byte, limit, limit_byte, Qnil) < 0)
     {
@@ -844,18 +582,26 @@ bidi_find_paragraph_start (EMACS_INT pos, EMACS_INT pos_byte)
   return pos_byte;
 }
 
-/* Determine the direction, a.k.a. base embedding level, of the
+/* Determine the base direction, a.k.a. base embedding level, of the
    paragraph we are about to iterate through.  If DIR is either L2R or
    R2L, just use that.  Otherwise, determine the paragraph direction
-   from the first strong character of the paragraph.
-
-   Note that this gives the paragraph separator the same direction as
-   the preceding paragraph, even though Emacs generally views the
-   separartor as not belonging to any paragraph.  */
+   from the first strong directional character of the paragraph.
+
+   NO_DEFAULT_P non-nil means don't default to L2R if the paragraph
+   has no strong directional characters and both DIR and
+   bidi_it->paragraph_dir are NEUTRAL_DIR.  In that case, search back
+   in the buffer until a paragraph is found with a strong character,
+   or until hitting BEGV.  In the latter case, fall back to L2R.  This
+   flag is used in current-bidi-paragraph-direction.
+
+   Note that this function gives the paragraph separator the same
+   direction as the preceding paragraph, even though Emacs generally
+   views the separartor as not belonging to any paragraph.  */
 void
-bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it)
+bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it, int no_default_p)
 {
   EMACS_INT bytepos = bidi_it->bytepos;
+  EMACS_INT pstartbyte;
 
   /* Special case for an empty buffer. */
   if (bytepos == BEGV_BYTE && bytepos == ZV_BYTE)
@@ -880,6 +626,9 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it)
       EMACS_INT pos;
       bidi_type_t type;
 
+      if (!bidi_initialized)
+       bidi_initialize ();
+
       /* If we are inside a paragraph separator, we are just waiting
         for the separator to be exhausted; use the previous paragraph
         direction.  But don't do that if we have been just reseated,
@@ -901,54 +650,75 @@ bidi_paragraph_init (bidi_dir_t dir, struct bidi_it *bidi_it)
 
       /* We are either at the beginning of a paragraph or in the
         middle of it.  Find where this paragraph starts.  */
-      bytepos = bidi_find_paragraph_start (pos, bytepos);
-
-      /* We should always be at the beginning of a new line at this
-        point.  */
-      if (!(bytepos == BEGV_BYTE || FETCH_CHAR (bytepos - 1) == '\n'))
-       abort ();
-
+      pstartbyte = bidi_find_paragraph_start (pos, bytepos);
       bidi_it->separator_limit = -1;
       bidi_it->new_paragraph = 0;
-      ch = FETCH_CHAR (bytepos);
-      ch_len = CHAR_BYTES (ch);
-      pos = BYTE_TO_CHAR (bytepos);
-      type = bidi_get_type (ch, NEUTRAL_DIR);
-
-      for (pos++, bytepos += ch_len;
-          /* NOTE: UAX#9 says to search only for L, AL, or R types of
-             characters, and ignore RLE, RLO, LRE, and LRO.  However,
-             I'm not sure it makes sense to omit those 4; should try
-             with and without that to see the effect.  */
-          (bidi_get_category (type) != STRONG)
-            || (bidi_ignore_explicit_marks_for_paragraph_level
-                && (type == RLE || type == RLO
-                    || type == LRE || type == LRO));
-          type = bidi_get_type (ch, NEUTRAL_DIR))
-       {
-         if (type == NEUTRAL_B && bidi_at_paragraph_end (pos, bytepos) >= -1)
-           break;
-         if (bytepos >= ZV_BYTE)
-           {
-             /* Pretend there's a paragraph separator at end of buffer.  */
-             type = NEUTRAL_B;
+
+      /* The following loop is run more than once only if NO_DEFAULT_P
+        is non-zero.  */
+      do {
+       bytepos = pstartbyte;
+       ch = FETCH_CHAR (bytepos);
+       ch_len = CHAR_BYTES (ch);
+       pos = BYTE_TO_CHAR (bytepos);
+       type = bidi_get_type (ch, NEUTRAL_DIR);
+
+       for (pos++, bytepos += ch_len;
+            /* NOTE: UAX#9 says to search only for L, AL, or R types
+               of characters, and ignore RLE, RLO, LRE, and LRO.
+               However, I'm not sure it makes sense to omit those 4;
+               should try with and without that to see the effect.  */
+            (bidi_get_category (type) != STRONG)
+              || (bidi_ignore_explicit_marks_for_paragraph_level
+                  && (type == RLE || type == RLO
+                      || type == LRE || type == LRO));
+            type = bidi_get_type (ch, NEUTRAL_DIR))
+         {
+           if (type == NEUTRAL_B && bidi_at_paragraph_end (pos, bytepos) >= -1)
              break;
-           }
-         FETCH_CHAR_ADVANCE (ch, pos, bytepos);
-       }
-      if (type == STRONG_R || type == STRONG_AL) /* P3 */
-       bidi_it->paragraph_dir = R2L;
-      else if (type == STRONG_L)
-       bidi_it->paragraph_dir = L2R;
+           if (bytepos >= ZV_BYTE)
+             {
+               /* Pretend there's a paragraph separator at end of
+                  buffer.  */
+               type = NEUTRAL_B;
+               break;
+             }
+           FETCH_CHAR_ADVANCE (ch, pos, bytepos);
+         }
+       if (type == STRONG_R || type == STRONG_AL) /* P3 */
+         bidi_it->paragraph_dir = R2L;
+       else if (type == STRONG_L)
+         bidi_it->paragraph_dir = L2R;
+       if (no_default_p && bidi_it->paragraph_dir == NEUTRAL_DIR)
+         {
+           /* If this paragraph is at BEGV, default to L2R.  */
+           if (pstartbyte == BEGV_BYTE)
+             bidi_it->paragraph_dir = L2R; /* P3 and HL1 */
+           else
+             {
+               EMACS_INT prevpbyte = pstartbyte;
+               EMACS_INT p = BYTE_TO_CHAR (pstartbyte), pbyte = pstartbyte;
+
+               /* Find the beginning of the previous paragraph, if any.  */
+               while (pbyte > BEGV_BYTE && prevpbyte >= pstartbyte)
+                 {
+                   p--;
+                   pbyte = CHAR_TO_BYTE (p);
+                   prevpbyte = bidi_find_paragraph_start (p, pbyte);
+                 }
+               pstartbyte = prevpbyte;
+             }
+         }
+      } while (no_default_p && bidi_it->paragraph_dir == NEUTRAL_DIR);
     }
   else
     abort ();
 
   /* Contrary to UAX#9 clause P3, we only default the paragraph
      direction to L2R if we have no previous usable paragraph
-     direction.  */
-  if (bidi_it->paragraph_dir == NEUTRAL_DIR)
-    bidi_it->paragraph_dir = L2R; /* P3 and ``higher protocols'' */
+     direction.  This is allowed by the HL1 clause.  */
+  if (bidi_it->paragraph_dir != L2R && bidi_it->paragraph_dir != R2L)
+    bidi_it->paragraph_dir = L2R; /* P3 and HL1 ``higher-level protocols'' */
   if (bidi_it->paragraph_dir == R2L)
     bidi_it->level_stack[0].level = 1;
   else
@@ -997,6 +767,7 @@ bidi_init_it (EMACS_INT charpos, EMACS_INT bytepos, struct bidi_it *bidi_it)
     bidi_it->prev_for_neutral.type_after_w1 =
     bidi_it->prev_for_neutral.orig_type = UNKNOWN_BT;
   bidi_it->sor = L2R;   /* FIXME: should it be user-selectable? */
+  bidi_cache_shrink ();
 }
 
 /* Push the current embedding level and override status; reset the
@@ -1795,7 +1566,7 @@ bidi_level_of_next_char (struct bidi_it *bidi_it)
 
         we want it to be displayed as
 
-            {RLO}STet{PDF}
+            {PDF}STet{RLO}
 
         not as
 
@@ -1883,7 +1654,7 @@ bidi_find_other_level_edge (struct bidi_it *bidi_it, int level, int end_flag)
 }
 
 void
-bidi_get_next_char_visually (struct bidi_it *bidi_it)
+bidi_move_to_visually_next (struct bidi_it *bidi_it)
 {
   int old_level, new_level, next_level;
   struct bidi_it sentinel;
@@ -1897,7 +1668,9 @@ bidi_get_next_char_visually (struct bidi_it *bidi_it)
   if (!bidi_it->first_elt && bidi_it->orig_type == NEUTRAL_B)
     bidi_line_init (bidi_it);
 
-  /* Prepare the sentinel iterator state.  */
+  /* Prepare the sentinel iterator state, and cache it.  When we bump
+     into it, scanning backwards, we'll know that the last non-base
+     level is exhausted.  */
   if (bidi_cache_idx == 0)
     {
       bidi_copy_it (&sentinel, bidi_it);
@@ -1908,6 +1681,7 @@ bidi_get_next_char_visually (struct bidi_it *bidi_it)
          sentinel.ch = '\n';   /* doesn't matter, but why not? */
          sentinel.ch_len = 1;
        }
+      bidi_cache_iterator_state (&sentinel, 1);
     }
 
   old_level = bidi_it->resolved_level;
@@ -1923,11 +1697,6 @@ bidi_get_next_char_visually (struct bidi_it *bidi_it)
       int incr = ascending ? 1 : -1;
       int expected_next_level = old_level + incr;
 
-      /* If we don't have anything cached yet, we need to cache the
-        sentinel state, since we'll need it to record where to jump
-        when the last non-base level is exhausted.  */
-      if (bidi_cache_idx == 0)
-       bidi_cache_iterator_state (&sentinel, 1);
       /* Jump (or walk) to the other edge of this level.  */
       bidi_find_other_level_edge (bidi_it, level_to_search, !ascending);
       /* Switch scan direction and peek at the next character in the
@@ -2037,6 +1806,6 @@ bidi_dump_cached_states (void)
   fputs ("\n", stderr);
   fputs ("pos ", stderr);
   for (i = 0; i < bidi_cache_idx; i++)
-    fprintf (stderr, "%*d", ndigits, bidi_cache[i].charpos);
+    fprintf (stderr, "%*ld", ndigits, (long)bidi_cache[i].charpos);
   fputs ("\n", stderr);
 }