At last! After much delay, much faffing back and forth, and much
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 22 May 2004 10:36:50 +0000 (10:36 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 22 May 2004 10:36:50 +0000 (10:36 +0000)
enhancement and fiddling, I have now massaged Arabeyes' first patch
into a form I'm happy to check in. Phew.

git-svn-id: svn://svn.tartarus.org/sgt/putty@4236 cda61777-01e9-0310-a592-d414129be87e

12 files changed:
Recipe
config.c
doc/config.but
ldisc.c
minibidi.c [new file with mode: 0644]
minibidi.h [new file with mode: 0644]
putty.h
settings.c
terminal.c
terminal.h
window.c
winhelp.h

diff --git a/Recipe b/Recipe
index 8cbc841..e2811cc 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -166,13 +166,16 @@ install-strip:
 # names. A line beginning `+' is assumed to continue the previous
 # line.
 
 # names. A line beginning `+' is assumed to continue the previous
 # line.
 
+# Terminal emulator and its (platform-independent) dependencies.
+TERMINAL = terminal wcwidth ldiscucs logging tree234 minibidi
+         + config dialog
+
 # GUI front end and terminal emulator (putty, puttytel).
 # GUI front end and terminal emulator (putty, puttytel).
-GUITERM  = window windlg winctrls terminal sizetip wcwidth unicode ldiscucs
-         + logging printing winutils dialog config wincfg tree234
+GUITERM  = TERMINAL window windlg winctrls sizetip unicode printing
+         + winutils wincfg
 
 # Same thing on Unix.
 
 # Same thing on Unix.
-UXTERM   = pterm config uxcfg dialog gtkdlg gtkcols gtkpanel tree234
-         + terminal wcwidth uxucs ldiscucs logging uxprint xkeysym
+UXTERM   = TERMINAL pterm uxcfg gtkdlg gtkcols gtkpanel uxucs uxprint xkeysym
 
 # Non-SSH back ends (putty, puttytel, plink).
 NONSSH   = telnet raw rlogin ldisc
 
 # Non-SSH back ends (putty, puttytel, plink).
 NONSSH   = telnet raw rlogin ldisc
index 68aaee7..de3a613 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1037,6 +1037,12 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
                  'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
                  I(offsetof(Config,no_remote_charset)));
     ctrl_checkbox(s, "Disable remote-controlled character set configuration",
                  'r', HELPCTX(features_charset), dlg_stdcheckbox_handler,
                  I(offsetof(Config,no_remote_charset)));
+    ctrl_checkbox(s, "Disable Arabic text shaping",
+                 'l', HELPCTX(no_help), dlg_stdcheckbox_handler,
+                 I(offsetof(Config, arabicshaping)));
+    ctrl_checkbox(s, "Disable bidirectional text display",
+                 'j', HELPCTX(no_help), dlg_stdcheckbox_handler,
+                 I(offsetof(Config, bidi)));
 
     /*
      * The Window panel.
 
     /*
      * The Window panel.
index 9dc8ae1..503f966 100644 (file)
@@ -1,4 +1,4 @@
-\versionid $Id: config.but,v 1.77 2004/04/24 20:05:03 jacob Exp $
+\versionid $Id: config.but,v 1.78 2004/05/22 10:36:50 simon Exp $
 
 \C{config} Configuring PuTTY
 
 
 \C{config} Configuring PuTTY
 
@@ -822,6 +822,47 @@ If you find that accented characters are not showing up the way you
 expect them to, particularly if you're running BitchX, you could try
 disabling the remote character set configuration commands.
 
 expect them to, particularly if you're running BitchX, you could try
 disabling the remote character set configuration commands.
 
+\S{config-features-shaping} Disabling Arabic text shaping
+
+\cfg{winhelp-topic}{features.arabicshaping}
+
+PuTTY supports shaping of Arabic text, which means that if your
+server sends text written in the basic Unicode Arabic alphabet then
+it will convert it to the correct display forms before printing it
+on the screen.
+
+If you are using full-screen software which was not expecting this
+to happen (especially if you are not an Arabic speaker and you
+unexpectedly find yourself dealing with Arabic text files in
+applications which are not Arabic-aware), you might find that the
+display becomes corrupted. By ticking this box, you can disable
+Arabic text shaping so that PuTTY displays precisely the characters
+it is told to display.
+
+You may also find you need to disable bidirectional text display;
+see \S{config-features-bidi}.
+
+\S{config-features-bidi} Disabling bidirectional text display
+
+\cfg{winhelp-topic}{features.bidi}
+
+PuTTY supports bidirectional text display, which means that if your
+server sends text written in a language which is usually displayed
+from right to left (such as Arabic or Hebrew) then PuTTY will
+automatically flip it round so that it is displayed in the right
+direction on the screen.
+
+If you are using full-screen software which was not expecting this
+to happen (especially if you are not an Arabic speaker and you
+unexpectedly find yourself dealing with Arabic text files in
+applications which are not Arabic-aware), you might find that the
+display becomes corrupted. By ticking this box, you can disable
+bidirectional text display, so that PuTTY displays text from left to
+right in all situations.
+
+You may also find you need to disable Arabic text shaping;
+see \S{config-features-arabicshaping}.
+
 \H{config-window} The Window panel
 
 The Window configuration panel allows you to control aspects of the
 \H{config-window} The Window panel
 
 The Window configuration panel allows you to control aspects of the
diff --git a/ldisc.c b/ldisc.c
index e572718..d57e6d1 100644 (file)
--- a/ldisc.c
+++ b/ldisc.c
@@ -32,13 +32,20 @@ static int plen(Ldisc ldisc, unsigned char c)
        return 1;
     else if (c < 128)
        return 2;                      /* ^x for some x */
        return 1;
     else if (c < 128)
        return 2;                      /* ^x for some x */
+    else if (in_utf(ldisc->term) && c >= 0xC0)
+       return 1;                      /* UTF-8 introducer character
+                                       * (FIXME: combining / wide chars) */
+    else if (in_utf(ldisc->term) && c >= 0x80 && c < 0xC0)
+       return 0;                      /* UTF-8 followup character */
     else
     else
-       return 4;                      /* <XY> for hex XY */
+       return 4;                      /* <XY> hex representation */
 }
 
 static void pwrite(Ldisc ldisc, unsigned char c)
 {
 }
 
 static void pwrite(Ldisc ldisc, unsigned char c)
 {
-    if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term))) {
+    if ((c >= 32 && c <= 126) ||
+       (!in_utf(ldisc->term) && c >= 0xA0) ||
+       (in_utf(ldisc->term) && c >= 0x80)) {
        c_write(ldisc, (char *)&c, 1);
     } else if (c < 128) {
        char cc[2];
        c_write(ldisc, (char *)&c, 1);
     } else if (c < 128) {
        char cc[2];
@@ -52,6 +59,14 @@ static void pwrite(Ldisc ldisc, unsigned char c)
     }
 }
 
     }
 }
 
+static int char_start(Ldisc ldisc, unsigned char c)
+{
+    if (in_utf(ldisc->term))
+       return (c < 0x80 || c >= 0xC0);
+    else
+       return 1;
+}
+
 static void bsb(Ldisc ldisc, int n)
 {
     while (n--)
 static void bsb(Ldisc ldisc, int n)
 {
     while (n--)
@@ -137,7 +152,9 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
                c += KCTRL('@');
            switch (ldisc->quotenext ? ' ' : c) {
                /*
                c += KCTRL('@');
            switch (ldisc->quotenext ? ' ' : c) {
                /*
-                * ^h/^?: delete one char and output one BSB
+                * ^h/^?: delete, and output BSBs, to return to
+                * last character boundary (in UTF-8 mode this may
+                * be more than one byte)
                 * ^w: delete, and output BSBs, to return to last
                 * space/nonspace boundary
                 * ^u: delete, and output BSBs, to return to BOL
                 * ^w: delete, and output BSBs, to return to last
                 * space/nonspace boundary
                 * ^u: delete, and output BSBs, to return to BOL
@@ -153,9 +170,11 @@ void ldisc_send(void *handle, char *buf, int len, int interactive)
              case KCTRL('H'):
              case KCTRL('?'):         /* backspace/delete */
                if (ldisc->buflen > 0) {
              case KCTRL('H'):
              case KCTRL('?'):         /* backspace/delete */
                if (ldisc->buflen > 0) {
-                   if (ECHOING)
-                       bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
-                   ldisc->buflen--;
+                   do {
+                       if (ECHOING)
+                           bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1]));
+                       ldisc->buflen--;
+                   } while (!char_start(ldisc, ldisc->buf[ldisc->buflen]));
                }
                break;
              case CTRL('W'):          /* delete word */
                }
                break;
              case CTRL('W'):          /* delete word */
diff --git a/minibidi.c b/minibidi.c
new file mode 100644 (file)
index 0000000..9943ae3
--- /dev/null
@@ -0,0 +1,1788 @@
+/************************************************************************
+ * $Id: minibidi.c,v 1.1 2004/05/22 10:36:50 simon Exp $
+ *
+ * ------------
+ * Description:
+ * ------------
+ * This is an implemention of Unicode's Bidirectional Algorithm
+ * (known as UAX #9).
+ *
+ *   http://www.unicode.org/reports/tr9/
+ * 
+ * Author: Ahmad Khalifa
+ *
+ * -----------------
+ * Revision Details:    (Updated by Revision Control System)
+ * -----------------
+ *  $Date: 2004/05/22 10:36:50 $
+ *  $Author: simon $
+ *  $Revision: 1.1 $
+ *  $Source: /u1/simon/svn-migration/cvs/putty/minibidi.c,v $
+ *
+ * (www.arabeyes.org - under MIT license)
+ *
+ ************************************************************************/
+
+/*
+ * TODO:
+ * =====
+ * - Explicit marks need to be handled (they are not 100% now)
+ * - Ligatures
+ */
+
+#include "minibidi.h"
+
+/*
+ * Flips the text buffer, according to max level, and
+ * all higher levels
+ * 
+ * Input:
+ * from: text buffer, on which to apply flipping
+ * level: resolved levels buffer
+ * max: the maximum level found in this line (should be unsigned char)
+ * count: line size in bidi_char
+ */
+void flipThisRun(bidi_char *from, unsigned char *level, int max, int count)
+{
+    int i, j, rcount, tlevel;
+    bidi_char temp;
+
+    j = i = 0;
+    while(i<count && j<count)
+    {
+
+       /* find the start of the run of level=max */
+       tlevel = max;
+       i = j = findIndexOfRun(level, i, count, max);
+       /* find the end of the run */
+       while(tlevel <= level[i] && i<count)
+       {
+           i++;
+       }
+       rcount = i-j;
+       for(; rcount>((i-j)/2); rcount--)
+       {
+           temp = from[j+rcount-1];
+           from[j+rcount-1] = from[i-rcount];
+           from[i-rcount] = temp;
+       }
+    }
+}
+
+/*
+ * Finds the index of a run with level equals tlevel
+ */
+int findIndexOfRun(unsigned char* level , int start, int count, int tlevel)
+{
+    int i;
+    for(i=start; i<count; i++)
+    {
+       if(tlevel == level[i])
+       {
+           return i;
+       }
+    }
+    return count;
+}
+
+/*
+ * Returns character type of ch, by calling RLE table lookup
+ * function
+ */
+unsigned char getType(wchar_t ch)
+{
+    return getRLE(ch);
+}
+
+/*
+ * The most significant 2 bits of each level are used to store
+ * Override status of each character
+ * This function sets the override bits of level according
+ * to the value in override, and reurns the new byte.
+ */
+unsigned char setOverrideBits(unsigned char level, unsigned char override)
+{
+    if(override == ON)
+       return level;
+    else if(override == R)
+       return level | OISR;
+    else if(override == L)
+       return level | OISL;
+    return level;
+}
+
+/* Dont remember what this was used for :-) */
+unsigned char getPreviousLevel(unsigned char* level, int from)
+{
+    unsigned char current;
+    from--;
+    current = level[from];
+    while(from>0 && level[from] == current)
+    {
+       from--;
+    }
+    return level[++from];
+}
+
+/*
+ * Returns the first odd value greater than x
+ */
+unsigned char leastGreaterOdd(unsigned char x)
+{
+    if((x % 2) == 0)
+       return x+1;
+    else
+       return x+2;
+}
+
+/*
+ * Returns the first even value greater than x
+ */
+unsigned char leastGreaterEven(unsigned char x)
+{
+    if((x % 2) == 0)
+       return x+2;
+    else
+       return x+1;
+}
+
+/*
+ * Loops over the RLE_table array looking for the
+ * type of ch
+ */
+unsigned char getRLE(wchar_t ch)
+{
+    int offset, i, freq;
+
+    freq = offset = 0;
+    for(i=0; i<0xFFFF; i++)
+    {
+       freq = ((RLENode*)RLE_table)[i].f;
+       offset += freq;
+       if(offset == ch)
+           return ((RLENode*)RLE_table)[i].d;
+       else if(offset > ch)
+           return ((RLENode*)RLE_table)[i-1].d;
+    }
+    /* this is here to stop compiler nagging */
+    return ON;
+}
+
+/* The Main shaping function, and the only one to be used
+ * by the outside world.
+ *
+ * line: buffer to apply shaping to. this must be passed by doBidi() first
+ * to: output buffer for the shaped data
+ * count: number of characters in line
+ */
+int do_shape(bidi_char *line, bidi_char *to, int count)
+{
+    int i, tempShape, ligFlag;
+
+    for(ligFlag=i=0; i<count; i++)
+    {
+       to[i] = line[i];
+       tempShape = STYPE(line[i].wc);
+       switch(tempShape )
+       {
+         case SC:
+           break;
+
+         case SU:
+           break;
+
+         case SR:
+           tempShape = STYPE(line[i+1].wc);
+           if((tempShape == SL) || (tempShape == SD) || (tempShape == SC))
+               to[i].wc = SFINAL((SISOLATED(line[i].wc)));
+           else
+               to[i].wc = SISOLATED(line[i].wc);
+           break;
+
+
+         case SD:
+           /* Make Ligatures */
+           tempShape = STYPE(line[i+1].wc);
+           if(line[i].wc == 0x644)
+           {
+               switch(line[i-1].wc)
+               {
+                 case 0x622:
+                   ligFlag = 1;
+                   if((tempShape == SL) || (tempShape == SD) || (tempShape == SC))
+                       to[i].wc = 0xFEF6;
+                   else
+                       to[i].wc = 0xFEF5;
+                   break;
+                 case 0x623:
+                   ligFlag = 1;
+                   if((tempShape == SL) || (tempShape == SD) || (tempShape == SC))
+                       to[i].wc = 0xFEF8;
+                   else
+                       to[i].wc = 0xFEF7;
+                   break;
+                 case 0x625:
+                   ligFlag = 1;
+                   if((tempShape == SL) || (tempShape == SD) || (tempShape == SC))
+                       to[i].wc = 0xFEFA;
+                   else
+                       to[i].wc = 0xFEF9;
+                   break;
+                 case 0x627:
+                   ligFlag = 1;
+                   if((tempShape == SL) || (tempShape == SD) || (tempShape == SC))
+                       to[i].wc = 0xFEFC;
+                   else
+                       to[i].wc = 0xFEFB;
+                   break;
+               }
+               if(ligFlag)
+               {
+                   to[i-1].wc = 0x20;
+                   ligFlag = 0;
+                   break;
+               }
+           }
+
+           if((tempShape == SL) || (tempShape == SD) || (tempShape == SC))
+           {
+               tempShape = STYPE(line[i-1].wc);
+               if((tempShape == SR) || (tempShape == SD) || (tempShape == SC))
+                   to[i].wc = SMEDIAL( (SISOLATED(line[i].wc)) );
+               else
+                   to[i].wc = SFINAL((SISOLATED(line[i].wc)));
+               break;
+           }
+
+           tempShape = STYPE(line[i-1].wc);
+           if((tempShape == SR) || (tempShape == SD) || (tempShape == SC))
+               to[i].wc = SINITIAL((SISOLATED(line[i].wc)));
+           else
+               to[i].wc = SISOLATED(line[i].wc);
+           break;
+
+
+       }
+    }
+    return 1;
+}
+
+/*
+ * The Main Bidi Function, and the only function that should
+ * be used by the outside world.
+ *
+ * line: a buffer of size count containing text to apply
+ * the Bidirectional algorithm to.
+ */
+
+int do_bidi(bidi_char *line, int count)
+{
+    unsigned char* types;
+    unsigned char* levels;
+    unsigned char paragraphLevel;
+    unsigned char currentEmbedding;
+    unsigned char currentOverride;
+    unsigned char tempType;
+    int i, j, imax, yes, bover;
+
+    /* Check the presence of R or AL types as optimization */
+    yes = 0;
+    for(i=0; i<count; i++)
+    {
+       if(getType(line[i].wc) == R || getType(line[i].wc) == AL)
+       {
+           yes = 1;
+           break;
+       }
+    }
+    if(yes == 0)
+       return L;
+
+    /* Initialize types, levels */
+    types = malloc(sizeof(unsigned char) * count);
+    levels = malloc(sizeof(unsigned char) * count);
+
+    /* Rule (P1)  NOT IMPLEMENTED
+     * P1. Split the text into separate paragraphs. A paragraph separator is
+     * kept with the previous paragraph. Within each paragraph, apply all the
+     * other rules of this algorithm.
+     */
+
+    /* Rule (P2), (P3)
+     * P2. In each paragraph, find the first character of type L, AL, or R.
+     * P3. If a character is found in P2 and it is of type AL or R, then set
+     * the paragraph embedding level to one; otherwise, set it to zero.
+     */
+    paragraphLevel = 0;
+    for( i=0; i<count ; i++)
+    {
+       if(getType(line[i].wc) == R || getType(line[i].wc) == AL)
+       {
+           paragraphLevel = 1;
+           break;
+       }
+       else if(getType(line[i].wc) == L)
+           break;
+    }
+
+    /* Rule (X1)
+     * X1. Begin by setting the current embedding level to the paragraph
+     * embedding level. Set the directional override status to neutral.
+     */
+    currentEmbedding = paragraphLevel;
+    currentOverride = ON;
+
+    /* Rule (X2), (X3), (X4), (X5), (X6), (X7), (X8)
+     * X2. With each RLE, compute the least greater odd embedding level.
+     * X3. With each LRE, compute the least greater even embedding level.
+     * X4. With each RLO, compute the least greater odd embedding level.
+     * X5. With each LRO, compute the least greater even embedding level.
+     * X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
+     *         a. Set the level of the current character to the current
+     *             embedding level.
+     *         b.  Whenever the directional override status is not neutral,
+     *               reset the current character type to the directional
+     *               override status.
+     * X7. With each PDF, determine the matching embedding or override code.
+     * If there was a valid matching code, restore (pop) the last
+     * remembered (pushed) embedding level and directional override.
+     * X8. All explicit directional embeddings and overrides are completely
+     * terminated at the end of each paragraph. Paragraph separators are not
+     * included in the embedding. (Useless here) NOT IMPLEMENTED
+     */
+    bover = 0;
+    for( i=0; i<count; i++)
+    {
+       tempType = getType(line[i].wc);
+       switch(tempType)
+       {
+         case RLE:
+           currentEmbedding = levels[i] = leastGreaterOdd(currentEmbedding);
+           levels[i] = setOverrideBits(levels[i], currentOverride);
+           currentOverride = ON;
+           break;
+
+         case LRE:
+           currentEmbedding = levels[i] = leastGreaterEven(currentEmbedding);
+           levels[i] = setOverrideBits(levels[i], currentOverride);
+           currentOverride = ON;
+           break;
+
+         case RLO:
+           currentEmbedding = levels[i] = leastGreaterOdd(currentEmbedding);
+           tempType = currentOverride = R;
+           bover = 1;
+           break;
+
+         case LRO:
+           currentEmbedding = levels[i] = leastGreaterEven(currentEmbedding);
+           tempType = currentOverride = L;
+           bover = 1;
+           break;
+
+         case PDF:
+           currentEmbedding = getPreviousLevel(levels, i);
+           currentOverride = currentEmbedding & OMASK;
+           currentEmbedding = currentEmbedding & ~OMASK;
+           levels[i] = currentEmbedding;
+           break;
+
+           /* Whitespace is treated as neutral for now */
+         case WS:
+         case S:
+           levels[i] = currentEmbedding;
+           tempType = ON;
+           if(currentOverride != ON)
+               tempType = currentOverride;
+           break;
+
+         default:
+           levels[i] = currentEmbedding;
+           if(currentOverride != ON)
+               tempType = currentOverride;
+           break;
+
+       }
+       types[i] = tempType;
+    }
+    /* this clears out all overrides, so we can use levels safely... */
+    /* checks bover first */
+    if(bover)
+       for( i=0; i<count; i++)
+           levels[i] = levels[i] & LMASK;
+
+    /* Rule (X9)
+     * X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
+     * Here, they're converted to BN.
+     */
+    for(i=0; i<count; i++)
+    {
+       switch(types[i])
+       {
+         case RLE:
+         case LRE:
+         case RLO:
+         case LRO:
+         case PDF:
+           types[i] = BN;
+           break;
+       }
+    }
+
+    /* Rule (W1)
+     * W1. Examine each non-spacing mark (NSM) in the level run, and change
+     * the type of the NSM to the type of the previous character. If the NSM
+     * is at the start of the level run, it will get the type of sor.
+     */
+    if(types[0] == NSM)
+       types[0] = paragraphLevel;
+
+    for(i=1; i<count; i++)
+    {
+       if(types[i] == NSM)
+           types[i] = types[i-1];
+       /* Is this a safe assumption?
+        * I assumed the previous, IS a character.
+        */
+    }
+
+    /* Rule (W2)
+     * W2. Search backwards from each instance of a European number until the
+     * first strong type (R, L, AL, or sor) is found.  If an AL is found,
+     * change the type of the European number to Arabic number.
+     */
+    for(i=0; i<count; i++)
+    {
+       if(types[i] == EN)
+       {
+           j=i;
+           while(j >= 0)
+           {
+               if(types[j] == AL)
+               {
+                   types[i] = AN;
+                   break;
+               }else if(types[j] == R || types[j] == L)
+                   {
+                       break;
+                   }
+               j--;
+           }
+       }
+    }
+
+    /* Rule (W3)
+     * W3. Change all ALs to R.
+     *
+     * Optimization: on Rule Xn, we might set a flag on AL type
+     * to prevent this loop in L R lines only...
+     */
+    for(i=0; i<count; i++)
+    {
+       if(types[i] == AL)
+           types[i] = R;
+    }
+
+    /* Rule (W4)
+     * W4. A single European separator between two European numbers changes
+     * to a European number. A single common separator between two numbers
+     * of the same type changes to that type.
+     */
+    for( i=0; i<(count-1); i++)
+    {
+       if(types[i] == ES)
+       {
+           if(types[i-1] == EN && types[i+1] == EN)
+               types[i] = EN;
+       }else if(types[i] == CS)
+           {
+               if(types[i-1] == EN && types[i+1] == EN)
+                   types[i] = EN;
+               else if(types[i-1] == AN && types[i+1] == AN)
+                   types[i] = AN;
+           }
+    }
+
+    /* Rule (W5)
+     * W5. A sequence of European terminators adjacent to European numbers
+     * changes to all European numbers.
+     *
+     * Optimization: lots here... else ifs need rearrangement
+     */
+    for(i=0; i<count; i++)
+    {
+       if(types[i] == ET)
+       {
+           if(types[i-1] == EN)
+           {
+               types[i] = EN;
+               continue;
+           }else if(types[i+1] == EN)
+               {
+                   types[i] = EN;
+                   continue;
+               }else if(types[i+1] == ET)
+                   {
+                       j=i;
+                       while(j <count && types[j] == ET)
+                       {
+                           j++;
+                       }
+                       if(types[j] == EN)
+                           types[i] = EN;
+                   }
+       }
+    }
+
+    /* Rule (W6)
+     * W6. Otherwise, separators and terminators change to Other Neutral:
+     */
+    for(i=0; i<count; i++)
+    {
+       switch(types[i])
+       {
+         case ES:
+         case ET:
+         case CS:
+           types[i] = ON;
+           break;
+       }
+    }
+
+    /* Rule (W7)
+     * W7. Search backwards from each instance of a European number until
+     * the first strong type (R, L, or sor) is found. If an L is found,
+     * then change the type of the European number to L.
+     */
+    for(i=0; i<count; i++)
+    {
+       if(types[i] == EN)
+       {
+           j=i;
+           while(j >= 0)
+           {
+               if(types[j] == L)
+               {
+                   types[i] = L;
+                   break;
+               }
+               else if(types[j] == R || types[j] == AL)
+               {
+                   break;
+               }
+               j--;
+           }
+       }
+    }
+
+    /* Rule (N1)
+     * N1. A sequence of neutrals takes the direction of the surrounding
+     * strong text if the text on both sides has the same direction. European
+     * and Arabic numbers are treated as though they were R.
+     */
+    if(types[0] == ON)
+    {
+       if((types[1] == R) || (types[1] == EN) || (types[1] == AN))
+           types[0] = R;
+       else if(types[1] == L)
+           types[0] = L;
+    }
+    for(i=1; i<(count-1); i++)
+    {
+       if(types[i] == ON)
+       {
+           if(types[i-1] == L)
+           {
+               j=i;
+               while(j<(count-1) && types[j] == ON)
+               {
+                   j++;
+               }
+               if(types[j] == L)
+               {
+                   while(i<j)
+                   {
+                       types[i] = L;
+                       i++;
+                   }
+               }
+
+           }else if((types[i-1] == R)  ||
+                    (types[i-1] == EN) ||
+                    (types[i-1] == AN))
+               {
+                   j=i;
+                   while(j<(count-1) && types[j] == ON)
+                   {
+                       j++;
+                   }
+                   if((types[j] == R)  ||
+                      (types[j] == EN) ||
+                      (types[j] == AN))
+                   {
+                       while(i<j)
+                       {
+                           types[i] = R;
+                           i++;
+                       }
+                   }
+               }
+       }
+    }
+    if(types[count-1] == ON)
+    {
+       if(types[count-2] == R || types[count-2] == EN || types[count-2] == AN)
+           types[count-1] = R;
+       else if(types[count-2] == L)
+           types[count-1] = L;
+    }
+
+    /* Rule (N2)
+     * N2. Any remaining neutrals take the embedding direction.
+     */
+    for(i=0; i<count; i++)
+    {
+       if(types[i] == ON)
+       {
+           if((levels[i] % 2) == 0)
+               types[i] = L;
+           else
+               types[i] = R;
+       }
+    }
+
+    /* Rule (I1)
+     * I1. For all characters with an even (left-to-right) embedding
+     * direction, those of type R go up one level and those of type AN or
+     * EN go up two levels.
+     */
+    for(i=0; i<count; i++)
+    {
+       if((levels[i] % 2) == 0)
+       {
+           if(types[i] == R)
+               levels[i] += 1;
+           else if(types[i] == AN || types[i] == EN)
+               levels[i] += 2;
+       }
+    }
+
+    /* Rule (I2)
+     * I2. For all characters with an odd (right-to-left) embedding direction,
+     * those of type L, EN or AN go up one level.
+     */
+    for(i=0; i<count; i++)
+    {
+       if((levels[i] % 2) == 1)
+       {
+           if(types[i] == L || types[i] == EN || types[i] == AN)
+               levels[i] += 1;
+       }
+    }
+
+    /* Rule (L1)
+     * L1. On each line, reset the embedding level of the following characters
+     * to the paragraph embedding level:
+     *         (1)segment separators, (2)paragraph separators,
+     *           (3)any sequence of whitespace characters preceding
+     *           a segment separator or paragraph separator,
+     *           (4)and any sequence of white space characters
+     *           at the end of the line.
+     * The types of characters used here are the original types, not those
+     * modified by the previous phase.
+     */
+    j=count-1;
+    while(j>0 && (getType(line[j].wc) == WS))
+    {
+       j--;
+    }
+    if(j < (count-1))
+    {
+       for(j++; j<count; j++)
+           levels[j] = paragraphLevel;
+    }
+    for(i=0; i<count; i++)
+    {
+       tempType = getType(line[i].wc);
+       if(tempType == WS)
+       {
+           j=i;
+           while(j<count && (getType(line[j].wc) == WS))
+           {
+               j++;
+           }
+           if(getType(line[j].wc) == B || getType(line[j].wc) == S)
+           {
+               for(j--; j>=i ; j--)
+               {
+                   levels[j] = paragraphLevel;
+               }
+           }
+       }else if(tempType == B || tempType == S)
+               levels[i] = paragraphLevel;
+    }
+
+    /* Rule (L4) NOT IMPLEMENTED
+     * L4. A character that possesses the mirrored property as specified by
+     * Section 4.7, Mirrored, must be depicted by a mirrored glyph if the
+     * resolved directionality of that character is R.
+     */
+    /* Note: this is implemented before L2 for efficiency */
+    for(i=0; i<count; i++)
+       if((levels[i] % 2) == 1)
+           doMirror(&line[i].wc);
+
+    /* Rule (L2)
+     * L2. From the highest level found in the text to the lowest odd level on
+     * each line, including intermediate levels not actually present in the
+     * text, reverse any contiguous sequence of characters that are at that
+     * level or higher
+     */
+    /* we flip the character string and leave the level array */
+    imax = 0;
+    i=0;
+    tempType = levels[0];
+    while(i < count)
+    {
+       if(levels[i] > tempType)
+       {
+           tempType = levels[i];
+           imax=i;
+       }
+       i++;
+    }
+    /* maximum level in tempType, its index in imax. */
+    while(tempType > 0)                /* loop from highest level to the least odd, */
+    {                          /* which i assume is 1 */
+       flipThisRun(line, levels, tempType, count);
+       tempType--;
+    }
+
+    /* Rule (L3) NOT IMPLEMENTED
+     * L3. Combining marks applied to a right-to-left base character will at
+     * this point precede their base character. If the rendering engine
+     * expects them to follow the base characters in the final display
+     * process, then the ordering of the marks and the base character must
+     * be reversed.
+     */
+    free(types);
+    free(levels);
+    return R;
+}
+
+
+/*
+ * Bad, Horrible funtion
+ * takes a pointer to a character that is checked for
+ * having a mirror glyph.
+ */
+void doMirror(wchar_t* ch)
+{
+    if((*ch & 0xFF00) == 0)
+    {
+       switch(*ch)
+       {
+         case 0x0028:
+           *ch = 0x0029;
+           break;
+         case 0x0029:
+           *ch = 0x0028;
+           break;
+         case 0x003C:
+           *ch = 0x003E;
+           break;
+         case 0x003E:
+           *ch = 0x003C;
+           break;
+         case 0x005B:
+           *ch = 0x005D;
+           break;
+         case 0x005D:
+           *ch = 0x005B;
+           break;
+         case 0x007B:
+           *ch = 0x007D;
+           break;
+         case 0x007D:
+           *ch = 0x007B;
+           break;
+         case 0x00AB:
+           *ch = 0x00BB;
+           break;
+         case 0x00BB:
+           *ch = 0x00AB;
+           break;
+       }
+    }
+    else if((*ch & 0xFF00) == 0x2000)
+    {
+       switch(*ch)
+       {
+         case 0x2039:
+           *ch = 0x203A;
+           break;
+         case 0x203A:
+           *ch = 0x2039;
+           break;
+         case 0x2045:
+           *ch = 0x2046;
+           break;
+         case 0x2046:
+           *ch = 0x2045;
+           break;
+         case 0x207D:
+           *ch = 0x207E;
+           break;
+         case 0x207E:
+           *ch = 0x207D;
+           break;
+         case 0x208D:
+           *ch = 0x208E;
+           break;
+         case 0x208E:
+           *ch = 0x208D;
+           break;
+       }
+    }
+    else if((*ch & 0xFF00) == 0x2200)
+    {
+       switch(*ch)
+       {
+         case 0x2208:
+           *ch = 0x220B;
+           break;
+         case 0x2209:
+           *ch = 0x220C;
+           break;
+         case 0x220A:
+           *ch = 0x220D;
+           break;
+         case 0x220B:
+           *ch = 0x2208;
+           break;
+         case 0x220C:
+           *ch = 0x2209;
+           break;
+         case 0x220D:
+           *ch = 0x220A;
+           break;
+         case 0x2215:
+           *ch = 0x29F5;
+           break;
+         case 0x223C:
+           *ch = 0x223D;
+           break;
+         case 0x223D:
+           *ch = 0x223C;
+           break;
+         case 0x2243:
+           *ch = 0x22CD;
+           break;
+         case 0x2252:
+           *ch = 0x2253;
+           break;
+         case 0x2253:
+           *ch = 0x2252;
+           break;
+         case 0x2254:
+           *ch = 0x2255;
+           break;
+         case 0x2255:
+           *ch = 0x2254;
+           break;
+         case 0x2264:
+           *ch = 0x2265;
+           break;
+         case 0x2265:
+           *ch = 0x2264;
+           break;
+         case 0x2266:
+           *ch = 0x2267;
+           break;
+         case 0x2267:
+           *ch = 0x2266;
+           break;
+         case 0x2268:
+           *ch = 0x2269;
+           break;
+         case 0x2269:
+           *ch = 0x2268;
+           break;
+         case 0x226A:
+           *ch = 0x226B;
+           break;
+         case 0x226B:
+           *ch = 0x226A;
+           break;
+         case 0x226E:
+           *ch = 0x226F;
+           break;
+         case 0x226F:
+           *ch = 0x226E;
+           break;
+         case 0x2270:
+           *ch = 0x2271;
+           break;
+         case 0x2271:
+           *ch = 0x2270;
+           break;
+         case 0x2272:
+           *ch = 0x2273;
+           break;
+         case 0x2273:
+           *ch = 0x2272;
+           break;
+         case 0x2274:
+           *ch = 0x2275;
+           break;
+         case 0x2275:
+           *ch = 0x2274;
+           break;
+         case 0x2276:
+           *ch = 0x2277;
+           break;
+         case 0x2277:
+           *ch = 0x2276;
+           break;
+         case 0x2278:
+           *ch = 0x2279;
+           break;
+         case 0x2279:
+           *ch = 0x2278;
+           break;
+         case 0x227A:
+           *ch = 0x227B;
+           break;
+         case 0x227B:
+           *ch = 0x227A;
+           break;
+         case 0x227C:
+           *ch = 0x227D;
+           break;
+         case 0x227D:
+           *ch = 0x227C;
+           break;
+         case 0x227E:
+           *ch = 0x227F;
+           break;
+         case 0x227F:
+           *ch = 0x227E;
+           break;
+         case 0x2280:
+           *ch = 0x2281;
+           break;
+         case 0x2281:
+           *ch = 0x2280;
+           break;
+         case 0x2282:
+           *ch = 0x2283;
+           break;
+         case 0x2283:
+           *ch = 0x2282;
+           break;
+         case 0x2284:
+           *ch = 0x2285;
+           break;
+         case 0x2285:
+           *ch = 0x2284;
+           break;
+         case 0x2286:
+           *ch = 0x2287;
+           break;
+         case 0x2287:
+           *ch = 0x2286;
+           break;
+         case 0x2288:
+           *ch = 0x2289;
+           break;
+         case 0x2289:
+           *ch = 0x2288;
+           break;
+         case 0x228A:
+           *ch = 0x228B;
+           break;
+         case 0x228B:
+           *ch = 0x228A;
+           break;
+         case 0x228F:
+           *ch = 0x2290;
+           break;
+         case 0x2290:
+           *ch = 0x228F;
+           break;
+         case 0x2291:
+           *ch = 0x2292;
+           break;
+         case 0x2292:
+           *ch = 0x2291;
+           break;
+         case 0x2298:
+           *ch = 0x29B8;
+           break;
+         case 0x22A2:
+           *ch = 0x22A3;
+           break;
+         case 0x22A3:
+           *ch = 0x22A2;
+           break;
+         case 0x22A6:
+           *ch = 0x2ADE;
+           break;
+         case 0x22A8:
+           *ch = 0x2AE4;
+           break;
+         case 0x22A9:
+           *ch = 0x2AE3;
+           break;
+         case 0x22AB:
+           *ch = 0x2AE5;
+           break;
+         case 0x22B0:
+           *ch = 0x22B1;
+           break;
+         case 0x22B1:
+           *ch = 0x22B0;
+           break;
+         case 0x22B2:
+           *ch = 0x22B3;
+           break;
+         case 0x22B3:
+           *ch = 0x22B2;
+           break;
+         case 0x22B4:
+           *ch = 0x22B5;
+           break;
+         case 0x22B5:
+           *ch = 0x22B4;
+           break;
+         case 0x22B6:
+           *ch = 0x22B7;
+           break;
+         case 0x22B7:
+           *ch = 0x22B6;
+           break;
+         case 0x22C9:
+           *ch = 0x22CA;
+           break;
+         case 0x22CA:
+           *ch = 0x22C9;
+           break;
+         case 0x22CB:
+           *ch = 0x22CC;
+           break;
+         case 0x22CC:
+           *ch = 0x22CB;
+           break;
+         case 0x22CD:
+           *ch = 0x2243;
+           break;
+         case 0x22D0:
+           *ch = 0x22D1;
+           break;
+         case 0x22D1:
+           *ch = 0x22D0;
+           break;
+         case 0x22D6:
+           *ch = 0x22D7;
+           break;
+         case 0x22D7:
+           *ch = 0x22D6;
+           break;
+         case 0x22D8:
+           *ch = 0x22D9;
+           break;
+         case 0x22D9:
+           *ch = 0x22D8;
+           break;
+         case 0x22DA:
+           *ch = 0x22DB;
+           break;
+         case 0x22DB:
+           *ch = 0x22DA;
+           break;
+         case 0x22DC:
+           *ch = 0x22DD;
+           break;
+         case 0x22DD:
+           *ch = 0x22DC;
+           break;
+         case 0x22DE:
+           *ch = 0x22DF;
+           break;
+         case 0x22DF:
+           *ch = 0x22DE;
+           break;
+         case 0x22E0:
+           *ch = 0x22E1;
+           break;
+         case 0x22E1:
+           *ch = 0x22E0;
+           break;
+         case 0x22E2:
+           *ch = 0x22E3;
+           break;
+         case 0x22E3:
+           *ch = 0x22E2;
+           break;
+         case 0x22E4:
+           *ch = 0x22E5;
+           break;
+         case 0x22E5:
+           *ch = 0x22E4;
+           break;
+         case 0x22E6:
+           *ch = 0x22E7;
+           break;
+         case 0x22E7:
+           *ch = 0x22E6;
+           break;
+         case 0x22E8:
+           *ch = 0x22E9;
+           break;
+         case 0x22E9:
+           *ch = 0x22E8;
+           break;
+         case 0x22EA:
+           *ch = 0x22EB;
+           break;
+         case 0x22EB:
+           *ch = 0x22EA;
+           break;
+         case 0x22EC:
+           *ch = 0x22ED;
+           break;
+         case 0x22ED:
+           *ch = 0x22EC;
+           break;
+         case 0x22F0:
+           *ch = 0x22F1;
+           break;
+         case 0x22F1:
+           *ch = 0x22F0;
+           break;
+         case 0x22F2:
+           *ch = 0x22FA;
+           break;
+         case 0x22F3:
+           *ch = 0x22FB;
+           break;
+         case 0x22F4:
+           *ch = 0x22FC;
+           break;
+         case 0x22F6:
+           *ch = 0x22FD;
+           break;
+         case 0x22F7:
+           *ch = 0x22FE;
+           break;
+         case 0x22FA:
+           *ch = 0x22F2;
+           break;
+         case 0x22FB:
+           *ch = 0x22F3;
+           break;
+         case 0x22FC:
+           *ch = 0x22F4;
+           break;
+         case 0x22FD:
+           *ch = 0x22F6;
+           break;
+         case 0x22FE:
+           *ch = 0x22F7;
+           break;
+       }
+    }else if((*ch & 0xFF00) == 0x2300)
+       {
+           switch(*ch)
+           {
+             case 0x2308:
+               *ch = 0x2309;
+               break;
+             case 0x2309:
+               *ch = 0x2308;
+               break;
+             case 0x230A:
+               *ch = 0x230B;
+               break;
+             case 0x230B:
+               *ch = 0x230A;
+               break;
+             case 0x2329:
+               *ch = 0x232A;
+               break;
+             case 0x232A:
+               *ch = 0x2329;
+               break;
+           }
+       }
+    else if((*ch & 0xFF00) == 0x2700)
+    {
+       switch(*ch)
+       {
+         case 0x2768:
+           *ch = 0x2769;
+           break;
+         case 0x2769:
+           *ch = 0x2768;
+           break;
+         case 0x276A:
+           *ch = 0x276B;
+           break;
+         case 0x276B:
+           *ch = 0x276A;
+           break;
+         case 0x276C:
+           *ch = 0x276D;
+           break;
+         case 0x276D:
+           *ch = 0x276C;
+           break;
+         case 0x276E:
+           *ch = 0x276F;
+           break;
+         case 0x276F:
+           *ch = 0x276E;
+           break;
+         case 0x2770:
+           *ch = 0x2771;
+           break;
+         case 0x2771:
+           *ch = 0x2770;
+           break;
+         case 0x2772:
+           *ch = 0x2773;
+           break;
+         case 0x2773:
+           *ch = 0x2772;
+           break;
+         case 0x2774:
+           *ch = 0x2775;
+           break;
+         case 0x2775:
+           *ch = 0x2774;
+           break;
+         case 0x27D5:
+           *ch = 0x27D6;
+           break;
+         case 0x27D6:
+           *ch = 0x27D5;
+           break;
+         case 0x27DD:
+           *ch = 0x27DE;
+           break;
+         case 0x27DE:
+           *ch = 0x27DD;
+           break;
+         case 0x27E2:
+           *ch = 0x27E3;
+           break;
+         case 0x27E3:
+           *ch = 0x27E2;
+           break;
+         case 0x27E4:
+           *ch = 0x27E5;
+           break;
+         case 0x27E5:
+           *ch = 0x27E4;
+           break;
+         case 0x27E6:
+           *ch = 0x27E7;
+           break;
+         case 0x27E7:
+           *ch = 0x27E6;
+           break;
+         case 0x27E8:
+           *ch = 0x27E9;
+           break;
+         case 0x27E9:
+           *ch = 0x27E8;
+           break;
+         case 0x27EA:
+           *ch = 0x27EB;
+           break;
+         case 0x27EB:
+           *ch = 0x27EA;
+           break;
+       }
+    }
+    else if((*ch & 0xFF00) == 0x2900)
+    {
+       switch(*ch)
+       {
+         case 0x2983:
+           *ch = 0x2984;
+           break;
+         case 0x2984:
+           *ch = 0x2983;
+           break;
+         case 0x2985:
+           *ch = 0x2986;
+           break;
+         case 0x2986:
+           *ch = 0x2985;
+           break;
+         case 0x2987:
+           *ch = 0x2988;
+           break;
+         case 0x2988:
+           *ch = 0x2987;
+           break;
+         case 0x2989:
+           *ch = 0x298A;
+           break;
+         case 0x298A:
+           *ch = 0x2989;
+           break;
+         case 0x298B:
+           *ch = 0x298C;
+           break;
+         case 0x298C:
+           *ch = 0x298B;
+           break;
+         case 0x298D:
+           *ch = 0x2990;
+           break;
+         case 0x298E:
+           *ch = 0x298F;
+           break;
+         case 0x298F:
+           *ch = 0x298E;
+           break;
+         case 0x2990:
+           *ch = 0x298D;
+           break;
+         case 0x2991:
+           *ch = 0x2992;
+           break;
+         case 0x2992:
+           *ch = 0x2991;
+           break;
+         case 0x2993:
+           *ch = 0x2994;
+           break;
+         case 0x2994:
+           *ch = 0x2993;
+           break;
+         case 0x2995:
+           *ch = 0x2996;
+           break;
+         case 0x2996:
+           *ch = 0x2995;
+           break;
+         case 0x2997:
+           *ch = 0x2998;
+           break;
+         case 0x2998:
+           *ch = 0x2997;
+           break;
+         case 0x29B8:
+           *ch = 0x2298;
+           break;
+         case 0x29C0:
+           *ch = 0x29C1;
+           break;
+         case 0x29C1:
+           *ch = 0x29C0;
+           break;
+         case 0x29C4:
+           *ch = 0x29C5;
+           break;
+         case 0x29C5:
+           *ch = 0x29C4;
+           break;
+         case 0x29CF:
+           *ch = 0x29D0;
+           break;
+         case 0x29D0:
+           *ch = 0x29CF;
+           break;
+         case 0x29D1:
+           *ch = 0x29D2;
+           break;
+         case 0x29D2:
+           *ch = 0x29D1;
+           break;
+         case 0x29D4:
+           *ch = 0x29D5;
+           break;
+         case 0x29D5:
+           *ch = 0x29D4;
+           break;
+         case 0x29D8:
+           *ch = 0x29D9;
+           break;
+         case 0x29D9:
+           *ch = 0x29D8;
+           break;
+         case 0x29DA:
+           *ch = 0x29DB;
+           break;
+         case 0x29DB:
+           *ch = 0x29DA;
+           break;
+         case 0x29F5:
+           *ch = 0x2215;
+           break;
+         case 0x29F8:
+           *ch = 0x29F9;
+           break;
+         case 0x29F9:
+           *ch = 0x29F8;
+           break;
+         case 0x29FC:
+           *ch = 0x29FD;
+           break;
+         case 0x29FD:
+           *ch = 0x29FC;
+           break;
+       }
+    }
+    else if((*ch & 0xFF00) == 0x2A00)
+    {
+       switch(*ch)
+       {
+         case 0x2A2B:
+           *ch = 0x2A2C;
+           break;
+         case 0x2A2C:
+           *ch = 0x2A2B;
+           break;
+         case 0x2A2D:
+           *ch = 0x2A2C;
+           break;
+         case 0x2A2E:
+           *ch = 0x2A2D;
+           break;
+         case 0x2A34:
+           *ch = 0x2A35;
+           break;
+         case 0x2A35:
+           *ch = 0x2A34;
+           break;
+         case 0x2A3C:
+           *ch = 0x2A3D;
+           break;
+         case 0x2A3D:
+           *ch = 0x2A3C;
+           break;
+         case 0x2A64:
+           *ch = 0x2A65;
+           break;
+         case 0x2A65:
+           *ch = 0x2A64;
+           break;
+         case 0x2A79:
+           *ch = 0x2A7A;
+           break;
+         case 0x2A7A:
+           *ch = 0x2A79;
+           break;
+         case 0x2A7D:
+           *ch = 0x2A7E;
+           break;
+         case 0x2A7E:
+           *ch = 0x2A7D;
+           break;
+         case 0x2A7F:
+           *ch = 0x2A80;
+           break;
+         case 0x2A80:
+           *ch = 0x2A7F;
+           break;
+         case 0x2A81:
+           *ch = 0x2A82;
+           break;
+         case 0x2A82:
+           *ch = 0x2A81;
+           break;
+         case 0x2A83:
+           *ch = 0x2A84;
+           break;
+         case 0x2A84:
+           *ch = 0x2A83;
+           break;
+         case 0x2A8B:
+           *ch = 0x2A8C;
+           break;
+         case 0x2A8C:
+           *ch = 0x2A8B;
+           break;
+         case 0x2A91:
+           *ch = 0x2A92;
+           break;
+         case 0x2A92:
+           *ch = 0x2A91;
+           break;
+         case 0x2A93:
+           *ch = 0x2A94;
+           break;
+         case 0x2A94:
+           *ch = 0x2A93;
+           break;
+         case 0x2A95:
+           *ch = 0x2A96;
+           break;
+         case 0x2A96:
+           *ch = 0x2A95;
+           break;
+         case 0x2A97:
+           *ch = 0x2A98;
+           break;
+         case 0x2A98:
+           *ch = 0x2A97;
+           break;
+         case 0x2A99:
+           *ch = 0x2A9A;
+           break;
+         case 0x2A9A:
+           *ch = 0x2A99;
+           break;
+         case 0x2A9B:
+           *ch = 0x2A9C;
+           break;
+         case 0x2A9C:
+           *ch = 0x2A9B;
+           break;
+         case 0x2AA1:
+           *ch = 0x2AA2;
+           break;
+         case 0x2AA2:
+           *ch = 0x2AA1;
+           break;
+         case 0x2AA6:
+           *ch = 0x2AA7;
+           break;
+         case 0x2AA7:
+           *ch = 0x2AA6;
+           break;
+         case 0x2AA8:
+           *ch = 0x2AA9;
+           break;
+         case 0x2AA9:
+           *ch = 0x2AA8;
+           break;
+         case 0x2AAA:
+           *ch = 0x2AAB;
+           break;
+         case 0x2AAB:
+           *ch = 0x2AAA;
+           break;
+         case 0x2AAC:
+           *ch = 0x2AAD;
+           break;
+         case 0x2AAD:
+           *ch = 0x2AAC;
+           break;
+         case 0x2AAF:
+           *ch = 0x2AB0;
+           break;
+         case 0x2AB0:
+           *ch = 0x2AAF;
+           break;
+         case 0x2AB3:
+           *ch = 0x2AB4;
+           break;
+         case 0x2AB4:
+           *ch = 0x2AB3;
+           break;
+         case 0x2ABB:
+           *ch = 0x2ABC;
+           break;
+         case 0x2ABC:
+           *ch = 0x2ABB;
+           break;
+         case 0x2ABD:
+           *ch = 0x2ABE;
+           break;
+         case 0x2ABE:
+           *ch = 0x2ABD;
+           break;
+         case 0x2ABF:
+           *ch = 0x2AC0;
+           break;
+         case 0x2AC0:
+           *ch = 0x2ABF;
+           break;
+         case 0x2AC1:
+           *ch = 0x2AC2;
+           break;
+         case 0x2AC2:
+           *ch = 0x2AC1;
+           break;
+         case 0x2AC3:
+           *ch = 0x2AC4;
+           break;
+         case 0x2AC4:
+           *ch = 0x2AC3;
+           break;
+         case 0x2AC5:
+           *ch = 0x2AC6;
+           break;
+         case 0x2AC6:
+           *ch = 0x2AC5;
+           break;
+         case 0x2ACD:
+           *ch = 0x2ACE;
+           break;
+         case 0x2ACE:
+           *ch = 0x2ACD;
+           break;
+         case 0x2ACF:
+           *ch = 0x2AD0;
+           break;
+         case 0x2AD0:
+           *ch = 0x2ACF;
+           break;
+         case 0x2AD1:
+           *ch = 0x2AD2;
+           break;
+         case 0x2AD2:
+           *ch = 0x2AD1;
+           break;
+         case 0x2AD3:
+           *ch = 0x2AD4;
+           break;
+         case 0x2AD4:
+           *ch = 0x2AD3;
+           break;
+         case 0x2AD5:
+           *ch = 0x2AD6;
+           break;
+         case 0x2AD6:
+           *ch = 0x2AD5;
+           break;
+         case 0x2ADE:
+           *ch = 0x22A6;
+           break;
+         case 0x2AE3:
+           *ch = 0x22A9;
+           break;
+         case 0x2AE4:
+           *ch = 0x22A8;
+           break;
+         case 0x2AE5:
+           *ch = 0x22AB;
+           break;
+         case 0x2AEC:
+           *ch = 0x2AED;
+           break;
+         case 0x2AED:
+           *ch = 0x2AEC;
+           break;
+         case 0x2AF7:
+           *ch = 0x2AF8;
+           break;
+         case 0x2AF8:
+           *ch = 0x2AF7;
+           break;
+         case 0x2AF9:
+           *ch = 0x2AFA;
+           break;
+         case 0x2AFA:
+           *ch = 0x2AF9;
+           break;
+       }
+    }
+    else if((*ch & 0xFF00) == 0x3000)
+    {
+       switch(*ch)
+       {
+         case 0x3008:
+           *ch = 0x3009;
+           break;
+         case 0x3009:
+           *ch = 0x3008;
+           break;
+         case 0x300A:
+           *ch = 0x300B;
+           break;
+         case 0x300B:
+           *ch = 0x300A;
+           break;
+         case 0x300C:
+           *ch = 0x300D;
+           break;
+         case 0x300D:
+           *ch = 0x300C;
+           break;
+         case 0x300E:
+           *ch = 0x300F;
+           break;
+         case 0x300F:
+           *ch = 0x300E;
+           break;
+         case 0x3010:
+           *ch = 0x3011;
+           break;
+         case 0x3011:
+           *ch = 0x3010;
+           break;
+         case 0x3014:
+           *ch = 0x3015;
+           break;
+         case 0x3015:
+           *ch = 0x3014;
+           break;
+         case 0x3016:
+           *ch = 0x3017;
+           break;
+         case 0x3017:
+           *ch = 0x3016;
+           break;
+         case 0x3018:
+           *ch = 0x3019;
+           break;
+         case 0x3019:
+           *ch = 0x3018;
+           break;
+         case 0x301A:
+           *ch = 0x301B;
+           break;
+         case 0x301B:
+           *ch = 0x301A;
+           break;
+       }
+    }
+    else if((*ch & 0xFF00) == 0xFF00)
+    {
+       switch(*ch)
+       {
+         case 0xFF08:
+           *ch = 0xFF09;
+           break;
+         case 0xFF09:
+           *ch = 0xFF08;
+           break;
+         case 0xFF1C:
+           *ch = 0xFF1E;
+           break;
+         case 0xFF1E:
+           *ch = 0xFF1C;
+           break;
+         case 0xFF3B:
+           *ch = 0xFF3D;
+           break;
+         case 0xFF3D:
+           *ch = 0xFF3B;
+           break;
+         case 0xFF5B:
+           *ch = 0xFF5D;
+           break;
+         case 0xFF5D:
+           *ch = 0xFF5B;
+           break;
+         case 0xFF5F:
+           *ch = 0xFF60;
+           break;
+         case 0xFF60:
+           *ch = 0xFF5F;
+           break;
+         case 0xFF62:
+           *ch = 0xFF63;
+           break;
+         case 0xFF63:
+           *ch = 0xFF62;
+           break;
+       }
+    }
+}
diff --git a/minibidi.h b/minibidi.h
new file mode 100644 (file)
index 0000000..91df54a
--- /dev/null
@@ -0,0 +1,456 @@
+/************************************************************************
+ * $Id: minibidi.h,v 1.1 2004/05/22 10:36:50 simon Exp $
+ *
+ * ------------
+ * Description:
+ * ------------
+ * This is an implemention of Unicode's Bidirectional Algorithm
+ * (known as UAX #9).
+ *
+ *   http://www.unicode.org/reports/tr9/
+ * 
+ * Author: Ahmad Khalifa
+ *
+ * -----------------
+ * Revision Details:    (Updated by Revision Control System)
+ * -----------------
+ *  $Date: 2004/05/22 10:36:50 $
+ *  $Author: simon $
+ *  $Revision: 1.1 $
+ *  $Source: /u1/simon/svn-migration/cvs/putty/minibidi.h,v $
+ *
+ * (www.arabeyes.org - under MIT license)
+ *
+ ************************************************************************/
+
+/*
+ * TODO:
+ * =====
+ * - work almost finished
+ * - Shaping Table to be expanded to include the whole range.
+ * - Ligature handling
+ */
+
+#include <stdlib.h>    /* definition of wchar_t*/
+
+#define LMASK  0x3F    /* Embedding Level mask */
+#define OMASK  0xC0    /* Override mask */
+#define OISL   0x80    /* Override is L */
+#define OISR   0x40    /* Override is R */
+
+/* Shaping Helpers */
+#define STYPE(xh) (((xh >= SHAPE_FIRST) && (xh <= SHAPE_LAST)) ? \
+shapetypes[xh-SHAPE_FIRST].type : SU) /*))*/
+#define SISOLATED(xh) (shapetypes[xh-SHAPE_FIRST].form_b)
+#define SFINAL(xh) xh+1
+#define SINITIAL(xh) xh+2
+#define SMEDIAL(ch) ch+3
+
+typedef struct bidi_char {
+    wchar_t origwc, wc;
+    unsigned short index;
+} bidi_char;
+
+/* function declarations */
+void flipThisRun(bidi_char *from, unsigned char* level, int max, int count);
+int findIndexOfRun(unsigned char* level , int start, int count, int tlevel);
+unsigned char getType(wchar_t ch);
+unsigned char setOverrideBits(unsigned char level, unsigned char override);
+unsigned char getPreviousLevel(unsigned char* level, int from);
+unsigned char leastGreaterOdd(unsigned char x);
+unsigned char leastGreaterEven(unsigned char x);
+unsigned char getRLE(wchar_t ch);
+int do_shape(bidi_char *line, bidi_char *to, int count);
+int do_bidi(bidi_char *line, int count);
+void doMirror(wchar_t* ch);
+
+/* character types */
+enum
+{
+   L,
+   LRE,
+   LRO,
+   R,
+   AL,
+   RLE,
+   RLO,
+   PDF,
+   EN,
+   ES,
+   ET,
+   AN,
+   CS,
+   NSM,
+   BN,
+   B,
+   S,
+   WS,
+   ON,
+};
+
+/* Shaping Types */
+enum
+{
+       SL, /* Left-Joining, doesnt exist in U+0600 - U+06FF */
+       SR, /* Right-Joining, ie has Isolated, Final */
+       SD, /* Dual-Joining, ie has Isolated, Final, Initial, Medial */
+       SU, /* Non-Joining */
+       SC  /* Join-Causing, like U+0640 (TATWEEL) */
+};
+
+typedef struct{
+       char type;
+       wchar_t form_b;
+} shape_node;
+
+/* Kept near the actual table, for verification. */
+#define SHAPE_FIRST 0x621
+#define SHAPE_LAST 0x64A
+
+const shape_node shapetypes[] = {
+/* index, Typ, Iso, Ligature Index*/
+/* 621 */ {SU, 0xFE80},
+/* 622 */ {SR, 0xFE81},
+/* 623 */ {SR, 0xFE83},
+/* 624 */ {SR, 0xFE85},
+/* 625 */ {SR, 0xFE87},
+/* 626 */ {SD, 0xFE89},
+/* 627 */ {SR, 0xFE8D},
+/* 628 */ {SD, 0xFE8F},
+/* 629 */ {SR, 0xFE93},
+/* 62A */ {SD, 0xFE95},
+/* 62B */ {SD, 0xFE99},
+/* 62C */ {SD, 0xFE9D},
+/* 62D */ {SD, 0xFEA1},
+/* 62E */ {SD, 0xFEA5},
+/* 62F */ {SR, 0xFEA9},
+/* 630 */ {SR, 0xFEAB},
+/* 631 */ {SR, 0xFEAD},
+/* 632 */ {SR, 0xFEAF},
+/* 633 */ {SD, 0xFEB1},
+/* 634 */ {SD, 0xFEB5},
+/* 635 */ {SD, 0xFEB9},
+/* 636 */ {SD, 0xFEBD},
+/* 637 */ {SD, 0xFEC1},
+/* 638 */ {SD, 0xFEC5},
+/* 639 */ {SD, 0xFEC9},
+/* 63A */ {SD, 0xFECD},
+/* 63B */ {SU, 0x0},
+/* 63C */ {SU, 0x0},
+/* 63D */ {SU, 0x0},
+/* 63E */ {SU, 0x0},
+/* 63F */ {SU, 0x0},
+/* 640 */ {SC, 0x0},
+/* 641 */ {SD, 0xFED1},
+/* 642 */ {SD, 0xFED5},
+/* 643 */ {SD, 0xFED9},
+/* 644 */ {SD, 0xFEDD},
+/* 645 */ {SD, 0xFEE1},
+/* 646 */ {SD, 0xFEE5},
+/* 647 */ {SD, 0xFEE9},
+/* 648 */ {SR, 0xFEED},
+/* 649 */ {SR, 0xFEEF}, /* SD */
+/* 64A */ {SD, 0xFEF1},
+};
+
+/*
+ * This describes the data byte and its frequency  
+ */
+typedef struct
+{
+   unsigned char f;
+   unsigned char d;
+}RLENode;
+
+
+/* This is an array of RLENodes, which is the
+ * Compressed unicode types table
+ */
+const unsigned char RLE_table[] =
+{
+   0x09, 0x10, 0x01, 0x0F, 0x01, 0x10, 0x01, 0x11,
+   0x01, 0x0F, 0x01, 0x0E, 0x0E, 0x0F, 0x03, 0x10,
+   0x01, 0x11, 0x01, 0x12, 0x02, 0x0A, 0x03, 0x12,
+   0x05, 0x0A, 0x01, 0x0C, 0x01, 0x0A, 0x01, 0x0C,
+   0x01, 0x09, 0x01, 0x08, 0x0A, 0x0C, 0x01, 0x12,
+   0x06, 0x00, 0x1A, 0x12, 0x06, 0x00, 0x1A, 0x12,
+   0x04, 0x0E, 0x06, 0x0F, 0x01, 0x0E, 0x1A, 0x0C,
+   0x01, 0x12, 0x01, 0x0A, 0x04, 0x12, 0x04, 0x00,
+   0x01, 0x12, 0x05, 0x0A, 0x02, 0x08, 0x02, 0x12,
+   0x01, 0x00, 0x01, 0x12, 0x03, 0x08, 0x01, 0x00,
+   0x01, 0x12, 0x05, 0x00, 0x17, 0x12, 0x01, 0x00,
+   0x1F, 0x12, 0x01, 0x00, 0xFF, 0x00, 0x2A, 0x12,
+   0x01, 0x00, 0x12, 0x12, 0x1C, 0x00, 0x5E, 0x12,
+   0x02, 0x00, 0x09, 0x12, 0x02, 0x00, 0x07, 0x12,
+   0x0E, 0x00, 0x02, 0x12, 0x0E, 0x00, 0x05, 0x12,
+   0x09, 0x00, 0x01, 0x12, 0x11, 0x0D, 0x50, 0x12,
+   0x10, 0x0D, 0x10, 0x12, 0x0A, 0x00, 0x01, 0x12,
+   0x0B, 0x00, 0x01, 0x12, 0x01, 0x00, 0x03, 0x12,
+   0x01, 0x00, 0x01, 0x12, 0x01, 0x00, 0x14, 0x12,
+   0x01, 0x00, 0x2C, 0x12, 0x01, 0x00, 0x26, 0x12,
+   0x0A, 0x00, 0x83, 0x0D, 0x04, 0x12, 0x01, 0x0D,
+   0x02, 0x00, 0x45, 0x12, 0x01, 0x00, 0x26, 0x12,
+   0x02, 0x00, 0x02, 0x12, 0x06, 0x00, 0x10, 0x12,
+   0x21, 0x00, 0x26, 0x12, 0x02, 0x00, 0x07, 0x12,
+   0x01, 0x00, 0x27, 0x12, 0x01, 0x00, 0x01, 0x12,
+   0x07, 0x0D, 0x11, 0x12, 0x01, 0x0D, 0x17, 0x12,
+   0x01, 0x0D, 0x03, 0x03, 0x01, 0x0D, 0x01, 0x03,
+   0x01, 0x0D, 0x02, 0x03, 0x01, 0x0D, 0x01, 0x12,
+   0x0B, 0x03, 0x1B, 0x12, 0x05, 0x03, 0x05, 0x12,
+   0x17, 0x0C, 0x01, 0x12, 0x0E, 0x04, 0x01, 0x12,
+   0x03, 0x04, 0x01, 0x12, 0x01, 0x04, 0x1A, 0x12,
+   0x05, 0x04, 0x0B, 0x0D, 0x0B, 0x12, 0x0A, 0x0B,
+   0x0A, 0x0A, 0x01, 0x0B, 0x02, 0x04, 0x03, 0x0D,
+   0x01, 0x04, 0x65, 0x0D, 0x07, 0x04, 0x01, 0x0D,
+   0x07, 0x04, 0x02, 0x0D, 0x02, 0x12, 0x01, 0x0D,
+   0x04, 0x12, 0x02, 0x08, 0x0A, 0x04, 0x05, 0x12,
+   0x01, 0x04, 0x0E, 0x12, 0x01, 0x0E, 0x01, 0x04,
+   0x01, 0x0D, 0x01, 0x04, 0x1B, 0x12, 0x03, 0x0D,
+   0x1B, 0x12, 0x35, 0x04, 0x26, 0x0D, 0x0B, 0x04,
+   0x01, 0x12, 0xFF, 0x12, 0x50, 0x0D, 0x02, 0x00,
+   0x01, 0x12, 0x01, 0x00, 0x35, 0x12, 0x02, 0x0D,
+   0x01, 0x00, 0x04, 0x0D, 0x08, 0x00, 0x04, 0x0D,
+   0x01, 0x12, 0x02, 0x00, 0x01, 0x0D, 0x04, 0x12,
+   0x03, 0x00, 0x0A, 0x0D, 0x02, 0x00, 0x0D, 0x12,
+   0x10, 0x0D, 0x01, 0x00, 0x02, 0x12, 0x01, 0x00,
+   0x08, 0x12, 0x02, 0x00, 0x02, 0x12, 0x02, 0x00,
+   0x16, 0x12, 0x01, 0x00, 0x07, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x03, 0x00, 0x04, 0x12, 0x02, 0x0D,
+   0x01, 0x12, 0x01, 0x00, 0x03, 0x0D, 0x04, 0x12,
+   0x02, 0x00, 0x02, 0x12, 0x02, 0x00, 0x02, 0x0D,
+   0x01, 0x12, 0x09, 0x00, 0x01, 0x12, 0x04, 0x00,
+   0x02, 0x12, 0x01, 0x00, 0x03, 0x0D, 0x02, 0x12,
+   0x02, 0x00, 0x0C, 0x0A, 0x02, 0x00, 0x07, 0x12,
+   0x07, 0x0D, 0x01, 0x12, 0x02, 0x00, 0x06, 0x12,
+   0x04, 0x00, 0x02, 0x12, 0x02, 0x00, 0x16, 0x12,
+   0x01, 0x00, 0x07, 0x12, 0x01, 0x00, 0x02, 0x12,
+   0x01, 0x00, 0x02, 0x12, 0x01, 0x00, 0x02, 0x12,
+   0x02, 0x0D, 0x01, 0x12, 0x01, 0x00, 0x03, 0x0D,
+   0x02, 0x12, 0x04, 0x0D, 0x02, 0x12, 0x02, 0x0D,
+   0x03, 0x12, 0x0B, 0x00, 0x04, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x07, 0x00, 0x0A, 0x0D, 0x02, 0x00,
+   0x03, 0x12, 0x0C, 0x0D, 0x02, 0x00, 0x01, 0x12,
+   0x01, 0x00, 0x07, 0x12, 0x01, 0x00, 0x01, 0x12,
+   0x01, 0x00, 0x03, 0x12, 0x01, 0x00, 0x16, 0x12,
+   0x01, 0x00, 0x07, 0x12, 0x01, 0x00, 0x02, 0x12,
+   0x01, 0x00, 0x05, 0x12, 0x02, 0x0D, 0x01, 0x00,
+   0x04, 0x0D, 0x05, 0x12, 0x01, 0x0D, 0x02, 0x00,
+   0x01, 0x12, 0x01, 0x00, 0x02, 0x0D, 0x01, 0x12,
+   0x02, 0x00, 0x01, 0x12, 0x0F, 0x00, 0x01, 0x12,
+   0x05, 0x00, 0x0A, 0x12, 0x11, 0x0D, 0x01, 0x00,
+   0x02, 0x12, 0x01, 0x00, 0x08, 0x12, 0x02, 0x00,
+   0x02, 0x12, 0x02, 0x00, 0x16, 0x12, 0x01, 0x00,
+   0x07, 0x12, 0x01, 0x00, 0x02, 0x12, 0x02, 0x00,
+   0x04, 0x12, 0x02, 0x0D, 0x01, 0x00, 0x02, 0x0D,
+   0x01, 0x00, 0x01, 0x0D, 0x03, 0x12, 0x03, 0x00,
+   0x02, 0x12, 0x02, 0x00, 0x02, 0x0D, 0x01, 0x12,
+   0x08, 0x0D, 0x01, 0x00, 0x01, 0x12, 0x04, 0x00,
+   0x02, 0x12, 0x01, 0x00, 0x03, 0x12, 0x04, 0x00,
+   0x0B, 0x12, 0x11, 0x0D, 0x01, 0x00, 0x01, 0x12,
+   0x01, 0x00, 0x06, 0x12, 0x03, 0x00, 0x03, 0x12,
+   0x01, 0x00, 0x04, 0x12, 0x03, 0x00, 0x02, 0x12,
+   0x01, 0x00, 0x01, 0x12, 0x01, 0x00, 0x02, 0x12,
+   0x03, 0x00, 0x02, 0x12, 0x03, 0x00, 0x03, 0x12,
+   0x03, 0x00, 0x08, 0x12, 0x01, 0x00, 0x03, 0x12,
+   0x04, 0x00, 0x02, 0x0D, 0x01, 0x00, 0x02, 0x12,
+   0x03, 0x00, 0x03, 0x12, 0x01, 0x00, 0x03, 0x0D,
+   0x01, 0x12, 0x09, 0x00, 0x01, 0x12, 0x0F, 0x00,
+   0x0C, 0x12, 0x0E, 0x00, 0x03, 0x12, 0x01, 0x00,
+   0x08, 0x12, 0x01, 0x00, 0x03, 0x12, 0x01, 0x00,
+   0x17, 0x12, 0x01, 0x00, 0x0A, 0x12, 0x01, 0x00,
+   0x05, 0x12, 0x04, 0x0D, 0x03, 0x00, 0x04, 0x12,
+   0x01, 0x0D, 0x03, 0x12, 0x01, 0x0D, 0x04, 0x12,
+   0x07, 0x0D, 0x02, 0x12, 0x09, 0x00, 0x02, 0x12,
+   0x04, 0x00, 0x0A, 0x12, 0x12, 0x00, 0x02, 0x12,
+   0x01, 0x00, 0x08, 0x12, 0x01, 0x00, 0x03, 0x12,
+   0x01, 0x00, 0x17, 0x12, 0x01, 0x00, 0x0A, 0x12,
+   0x01, 0x00, 0x05, 0x12, 0x04, 0x00, 0x01, 0x0D,
+   0x01, 0x00, 0x05, 0x12, 0x01, 0x0D, 0x01, 0x00,
+   0x02, 0x12, 0x01, 0x00, 0x02, 0x0D, 0x02, 0x12,
+   0x07, 0x00, 0x02, 0x12, 0x07, 0x00, 0x01, 0x12,
+   0x01, 0x00, 0x02, 0x12, 0x04, 0x00, 0x0A, 0x12,
+   0x12, 0x00, 0x02, 0x12, 0x01, 0x00, 0x08, 0x12,
+   0x01, 0x00, 0x03, 0x12, 0x01, 0x00, 0x17, 0x12,
+   0x01, 0x00, 0x10, 0x12, 0x04, 0x00, 0x03, 0x0D,
+   0x03, 0x12, 0x02, 0x00, 0x03, 0x12, 0x01, 0x00,
+   0x03, 0x0D, 0x01, 0x12, 0x09, 0x00, 0x01, 0x12,
+   0x08, 0x00, 0x02, 0x12, 0x04, 0x00, 0x0A, 0x12,
+   0x12, 0x00, 0x02, 0x12, 0x01, 0x00, 0x12, 0x12,
+   0x03, 0x00, 0x18, 0x12, 0x01, 0x00, 0x09, 0x12,
+   0x01, 0x00, 0x01, 0x12, 0x02, 0x00, 0x07, 0x12,
+   0x03, 0x0D, 0x01, 0x12, 0x04, 0x00, 0x03, 0x0D,
+   0x03, 0x12, 0x01, 0x0D, 0x01, 0x12, 0x01, 0x00,
+   0x08, 0x12, 0x12, 0x00, 0x03, 0x12, 0x0C, 0x00,
+   0x30, 0x0D, 0x01, 0x00, 0x02, 0x0D, 0x07, 0x12,
+   0x04, 0x0A, 0x01, 0x00, 0x07, 0x0D, 0x08, 0x00,
+   0x0D, 0x12, 0x25, 0x00, 0x02, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x02, 0x00, 0x02, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x02, 0x00, 0x01, 0x12, 0x06, 0x00,
+   0x04, 0x12, 0x01, 0x00, 0x07, 0x12, 0x01, 0x00,
+   0x03, 0x12, 0x01, 0x00, 0x01, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x02, 0x00, 0x02, 0x12, 0x01, 0x00,
+   0x04, 0x0D, 0x01, 0x00, 0x02, 0x0D, 0x06, 0x12,
+   0x01, 0x0D, 0x02, 0x00, 0x01, 0x12, 0x02, 0x00,
+   0x05, 0x12, 0x01, 0x00, 0x01, 0x12, 0x01, 0x0D,
+   0x06, 0x12, 0x02, 0x00, 0x0A, 0x12, 0x02, 0x00,
+   0x02, 0x12, 0x22, 0x00, 0x18, 0x0D, 0x02, 0x00,
+   0x1B, 0x0D, 0x01, 0x00, 0x01, 0x0D, 0x01, 0x00,
+   0x01, 0x0D, 0x01, 0x12, 0x04, 0x00, 0x0A, 0x12,
+   0x01, 0x00, 0x22, 0x12, 0x06, 0x0D, 0x0E, 0x00,
+   0x01, 0x0D, 0x05, 0x00, 0x01, 0x0D, 0x02, 0x00,
+   0x04, 0x12, 0x04, 0x0D, 0x08, 0x12, 0x01, 0x0D,
+   0x24, 0x12, 0x01, 0x00, 0x08, 0x0D, 0x01, 0x00,
+   0x06, 0x12, 0x02, 0x00, 0x01, 0x12, 0x30, 0x00,
+   0x22, 0x12, 0x01, 0x00, 0x05, 0x12, 0x01, 0x00,
+   0x02, 0x12, 0x01, 0x00, 0x01, 0x0D, 0x04, 0x00,
+   0x01, 0x0D, 0x01, 0x12, 0x03, 0x0D, 0x02, 0x00,
+   0x01, 0x0D, 0x01, 0x12, 0x06, 0x00, 0x18, 0x0D,
+   0x02, 0x12, 0x46, 0x00, 0x26, 0x12, 0x0A, 0x00,
+   0x29, 0x12, 0x02, 0x00, 0x01, 0x12, 0x04, 0x00,
+   0x5A, 0x12, 0x05, 0x00, 0x44, 0x12, 0x05, 0x00,
+   0x52, 0x12, 0x06, 0x00, 0x07, 0x12, 0x01, 0x00,
+   0x3F, 0x12, 0x01, 0x00, 0x01, 0x12, 0x01, 0x00,
+   0x04, 0x12, 0x02, 0x00, 0x07, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x01, 0x00, 0x04, 0x12, 0x02, 0x00,
+   0x27, 0x12, 0x01, 0x00, 0x01, 0x12, 0x01, 0x00,
+   0x04, 0x12, 0x02, 0x00, 0x1F, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x01, 0x00, 0x04, 0x12, 0x02, 0x00,
+   0x07, 0x12, 0x01, 0x00, 0x01, 0x12, 0x01, 0x00,
+   0x04, 0x12, 0x02, 0x00, 0x07, 0x12, 0x01, 0x00,
+   0x07, 0x12, 0x01, 0x00, 0x17, 0x12, 0x01, 0x00,
+   0x1F, 0x12, 0x01, 0x00, 0x01, 0x12, 0x01, 0x00,
+   0x04, 0x12, 0x02, 0x00, 0x07, 0x12, 0x01, 0x00,
+   0x27, 0x12, 0x01, 0x00, 0x13, 0x12, 0x06, 0x00,
+   0x1C, 0x12, 0x23, 0x00, 0x55, 0x12, 0x0C, 0x00,
+   0xFF, 0x00, 0xFF, 0x00, 0x78, 0x12, 0x09, 0x11,
+   0x01, 0x00, 0x1A, 0x12, 0x05, 0x00, 0x51, 0x12,
+   0x0F, 0x00, 0x0D, 0x12, 0x01, 0x00, 0x04, 0x0D,
+   0x03, 0x12, 0x0B, 0x00, 0x12, 0x0D, 0x03, 0x00,
+   0x02, 0x12, 0x09, 0x00, 0x12, 0x0D, 0x02, 0x12,
+   0x0C, 0x00, 0x0D, 0x12, 0x01, 0x00, 0x03, 0x12,
+   0x01, 0x0D, 0x02, 0x12, 0x0C, 0x00, 0x37, 0x0D,
+   0x07, 0x00, 0x08, 0x0D, 0x01, 0x00, 0x02, 0x0D,
+   0x0B, 0x00, 0x07, 0x0A, 0x01, 0x00, 0x01, 0x12,
+   0x03, 0x00, 0x0A, 0x12, 0x21, 0x0D, 0x03, 0x0E,
+   0x01, 0x12, 0x01, 0x00, 0x0A, 0x12, 0x06, 0x00,
+   0x58, 0x12, 0x08, 0x00, 0x29, 0x0D, 0x01, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0x5B, 0x00, 0x9C, 0x12, 0x04, 0x00,
+   0x5A, 0x12, 0x06, 0x00, 0x16, 0x12, 0x02, 0x00,
+   0x06, 0x12, 0x02, 0x00, 0x26, 0x12, 0x02, 0x00,
+   0x06, 0x12, 0x02, 0x00, 0x08, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x01, 0x00, 0x01, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x01, 0x00, 0x1F, 0x12, 0x02, 0x00,
+   0x35, 0x12, 0x01, 0x00, 0x07, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0x03, 0x00, 0x03, 0x12, 0x01, 0x00,
+   0x07, 0x12, 0x03, 0x00, 0x04, 0x12, 0x02, 0x00,
+   0x06, 0x12, 0x04, 0x00, 0x0D, 0x12, 0x05, 0x00,
+   0x03, 0x12, 0x01, 0x00, 0x07, 0x12, 0x03, 0x11,
+   0x0B, 0x0E, 0x03, 0x00, 0x01, 0x03, 0x01, 0x12,
+   0x18, 0x11, 0x01, 0x0F, 0x01, 0x01, 0x01, 0x05,
+   0x01, 0x07, 0x01, 0x02, 0x01, 0x06, 0x01, 0x11,
+   0x01, 0x0A, 0x05, 0x12, 0x2A, 0x11, 0x01, 0x0E,
+   0x04, 0x12, 0x06, 0x0E, 0x06, 0x08, 0x01, 0x00,
+   0x01, 0x12, 0x02, 0x08, 0x06, 0x0A, 0x02, 0x12,
+   0x03, 0x00, 0x01, 0x08, 0x0A, 0x0A, 0x02, 0x12,
+   0x14, 0x0A, 0x12, 0x12, 0x1E, 0x0D, 0x1B, 0x12,
+   0x17, 0x00, 0x01, 0x12, 0x04, 0x00, 0x01, 0x12,
+   0x02, 0x00, 0x0A, 0x12, 0x01, 0x00, 0x01, 0x12,
+   0x03, 0x00, 0x05, 0x12, 0x06, 0x00, 0x01, 0x12,
+   0x01, 0x00, 0x01, 0x12, 0x01, 0x00, 0x01, 0x12,
+   0x01, 0x00, 0x04, 0x0A, 0x01, 0x00, 0x03, 0x12,
+   0x01, 0x00, 0x07, 0x12, 0x03, 0x00, 0x03, 0x12,
+   0x05, 0x00, 0x05, 0x12, 0x16, 0x00, 0x24, 0x12,
+   0x8E, 0x0A, 0x02, 0x12, 0xFF, 0x12, 0x23, 0x00,
+   0x45, 0x12, 0x1A, 0x00, 0x01, 0x12, 0xCA, 0x08,
+   0x3C, 0x00, 0x4E, 0x08, 0x01, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0x20, 0x11, 0x01, 0x12,
+   0x04, 0x00, 0x03, 0x12, 0x19, 0x00, 0x09, 0x0D,
+   0x06, 0x12, 0x01, 0x00, 0x05, 0x12, 0x02, 0x00,
+   0x05, 0x12, 0x04, 0x00, 0x56, 0x12, 0x02, 0x0D,
+   0x02, 0x12, 0x02, 0x00, 0x03, 0x12, 0x01, 0x00,
+   0x5A, 0x12, 0x01, 0x00, 0x04, 0x12, 0x05, 0x00,
+   0x28, 0x12, 0x04, 0x00, 0x5E, 0x12, 0x01, 0x00,
+   0x28, 0x12, 0x38, 0x00, 0x2D, 0x12, 0x03, 0x00,
+   0x24, 0x12, 0x1C, 0x00, 0x1C, 0x12, 0x03, 0x00,
+   0x32, 0x12, 0x0F, 0x00, 0x0C, 0x12, 0x04, 0x00,
+   0x2F, 0x12, 0x01, 0x00, 0x77, 0x12, 0x04, 0x00,
+   0x63, 0x12, 0x02, 0x00, 0x1F, 0x12, 0x01, 0x00,
+   0x01, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xCD, 0x00, 0x01, 0x12,
+   0x4A, 0x00, 0x01, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xF5, 0x00,
+   0x01, 0x12, 0x5A, 0x00, 0xFF, 0x00, 0xFF, 0x00,
+   0xFF, 0x00, 0xFF, 0x00, 0x91, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0x7A, 0x00, 0x01, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xCD, 0x00,
+   0x01, 0x12, 0x5C, 0x00, 0x01, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0x81, 0x00, 0x02, 0x12,
+   0x7E, 0x00, 0x02, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0x02, 0x00, 0x02, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12, 0xFF, 0x12,
+   0xFF, 0x12, 0x17, 0x00, 0xFF, 0x00, 0x30, 0x12,
+   0x02, 0x00, 0x3B, 0x12, 0x95, 0x00, 0x07, 0x12,
+   0x0C, 0x00, 0x05, 0x12, 0x05, 0x03, 0x01, 0x0D,
+   0x01, 0x03, 0x0A, 0x0A, 0x01, 0x03, 0x0D, 0x12,
+   0x01, 0x03, 0x05, 0x12, 0x01, 0x03, 0x01, 0x12,
+   0x01, 0x03, 0x02, 0x12, 0x01, 0x03, 0x02, 0x12,
+   0x01, 0x03, 0x0A, 0x04, 0x62, 0x12, 0x21, 0x04,
+   0xFF, 0x04, 0x6C, 0x12, 0x12, 0x04, 0x40, 0x12,
+   0x02, 0x04, 0x36, 0x12, 0x28, 0x04, 0x0D, 0x12,
+   0x03, 0x0D, 0x10, 0x12, 0x10, 0x0D, 0x04, 0x12,
+   0x2C, 0x0C, 0x01, 0x12, 0x01, 0x0C, 0x01, 0x12,
+   0x02, 0x0C, 0x01, 0x12, 0x09, 0x0A, 0x01, 0x12,
+   0x02, 0x0A, 0x02, 0x12, 0x05, 0x0A, 0x02, 0x12,
+   0x05, 0x04, 0x05, 0x12, 0x01, 0x04, 0x87, 0x12,
+   0x02, 0x0E, 0x01, 0x12, 0x03, 0x0A, 0x03, 0x12,
+   0x05, 0x0A, 0x01, 0x0C, 0x01, 0x0A, 0x01, 0x0C,
+   0x01, 0x09, 0x01, 0x08, 0x0A, 0x0C, 0x01, 0x12,
+   0x06, 0x00, 0x1A, 0x12, 0x06, 0x00, 0x1A, 0x12,
+   0x0B, 0x00, 0x59, 0x12, 0x03, 0x00, 0x06, 0x12,
+   0x02, 0x00, 0x06, 0x12, 0x02, 0x00, 0x06, 0x12,
+   0x02, 0x00, 0x03, 0x12, 0x03, 0x0A, 0x02, 0x12,
+   0x03, 0x0A, 0x02, 0x12, 0x09, 0x00, 0x0E, 0x00,
+};
diff --git a/putty.h b/putty.h
index af3768d..723f1e6 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -428,6 +428,8 @@ struct config_tag {
     int window_border;
     char answerback[256];
     char printer[128];
     int window_border;
     char answerback[256];
     char printer[128];
+    int arabicshaping;
+    int bidi;
     /* Colour options */
     int system_colour;
     int try_palette;
     /* Colour options */
     int system_colour;
     int try_palette;
@@ -831,6 +833,16 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist,
                      int midsession, int protocol);
 
 /*
                      int midsession, int protocol);
 
 /*
+ * Exports from minibidi.c.
+ */
+typedef struct bidi_char {
+    wchar_t origwc, wc;
+    unsigned short index;
+} bidi_char;
+int do_bidi(bidi_char *line, int count);
+int do_shape(bidi_char *line, bidi_char *to, int count);
+
+/*
  * X11 auth mechanisms we know about.
  */
 enum {
  * X11 auth mechanisms we know about.
  */
 enum {
index 5abf15d..b9e6621 100644 (file)
@@ -274,6 +274,8 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
     write_setting_i(sesskey, "DECOriginMode", cfg->dec_om);
     write_setting_i(sesskey, "AutoWrapMode", cfg->wrap_mode);
     write_setting_i(sesskey, "LFImpliesCR", cfg->lfhascr);
     write_setting_i(sesskey, "DECOriginMode", cfg->dec_om);
     write_setting_i(sesskey, "AutoWrapMode", cfg->wrap_mode);
     write_setting_i(sesskey, "LFImpliesCR", cfg->lfhascr);
+    write_setting_i(sesskey, "DisableArabicShaping", cfg->arabicshaping);
+    write_setting_i(sesskey, "DisableBidi", cfg->bidi);
     write_setting_i(sesskey, "WinNameAlways", cfg->win_name_always);
     write_setting_s(sesskey, "WinTitle", cfg->wintitle);
     write_setting_i(sesskey, "TermWidth", cfg->width);
     write_setting_i(sesskey, "WinNameAlways", cfg->win_name_always);
     write_setting_s(sesskey, "WinTitle", cfg->wintitle);
     write_setting_i(sesskey, "TermWidth", cfg->width);
@@ -283,6 +285,7 @@ void save_open_settings(void *sesskey, int do_host, Config *cfg)
     write_setting_i(sesskey, "UseSystemColours", cfg->system_colour);
     write_setting_i(sesskey, "TryPalette", cfg->try_palette);
     write_setting_i(sesskey, "BoldAsColour", cfg->bold_colour);
     write_setting_i(sesskey, "UseSystemColours", cfg->system_colour);
     write_setting_i(sesskey, "TryPalette", cfg->try_palette);
     write_setting_i(sesskey, "BoldAsColour", cfg->bold_colour);
+
     for (i = 0; i < 22; i++) {
        char buf[20], buf2[30];
        sprintf(buf, "Colour%d", i);
     for (i = 0; i < 22; i++) {
        char buf[20], buf2[30];
        sprintf(buf, "Colour%d", i);
@@ -531,6 +534,8 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
     gppi(sesskey, "DECOriginMode", 0, &cfg->dec_om);
     gppi(sesskey, "AutoWrapMode", 1, &cfg->wrap_mode);
     gppi(sesskey, "LFImpliesCR", 0, &cfg->lfhascr);
     gppi(sesskey, "DECOriginMode", 0, &cfg->dec_om);
     gppi(sesskey, "AutoWrapMode", 1, &cfg->wrap_mode);
     gppi(sesskey, "LFImpliesCR", 0, &cfg->lfhascr);
+    gppi(sesskey, "DisableArabicShaping", 0, &cfg->arabicshaping);
+    gppi(sesskey, "DisableBidi", 0, &cfg->bidi);
     gppi(sesskey, "WinNameAlways", 1, &cfg->win_name_always);
     gpps(sesskey, "WinTitle", "", cfg->wintitle, sizeof(cfg->wintitle));
     gppi(sesskey, "TermWidth", 80, &cfg->width);
     gppi(sesskey, "WinNameAlways", 1, &cfg->win_name_always);
     gpps(sesskey, "WinTitle", "", cfg->wintitle, sizeof(cfg->wintitle));
     gppi(sesskey, "TermWidth", 80, &cfg->width);
@@ -540,6 +545,7 @@ void load_open_settings(void *sesskey, int do_host, Config *cfg)
     gppi(sesskey, "UseSystemColours", 0, &cfg->system_colour);
     gppi(sesskey, "TryPalette", 0, &cfg->try_palette);
     gppi(sesskey, "BoldAsColour", 1, &cfg->bold_colour);
     gppi(sesskey, "UseSystemColours", 0, &cfg->system_colour);
     gppi(sesskey, "TryPalette", 0, &cfg->try_palette);
     gppi(sesskey, "BoldAsColour", 1, &cfg->bold_colour);
+
     for (i = 0; i < 22; i++) {
        static const char *const defaults[] = {
            "187,187,187", "255,255,255", "0,0,0", "85,85,85", "0,0,0",
     for (i = 0; i < 22; i++) {
        static const char *const defaults[] = {
            "187,187,187", "255,255,255", "0,0,0", "85,85,85", "0,0,0",
index 083b0fb..3b14923 100644 (file)
@@ -237,6 +237,15 @@ void term_update(Terminal *term)
            term->seen_disp_event = 0;
            need_sbar_update = TRUE;
        }
            term->seen_disp_event = 0;
            need_sbar_update = TRUE;
        }
+
+       /* Allocate temporary buffers for Arabic shaping and bidi. */
+       if (!term->cfg.arabicshaping || !term->cfg.bidi)
+       {
+           term->wcFrom = sresize(term->wcFrom, term->cols, bidi_char);
+           term->ltemp = sresize(term->ltemp, term->cols+1, unsigned long);
+           term->wcTo = sresize(term->wcTo, term->cols, bidi_char);
+       }
+
        if (need_sbar_update)
            update_sbar(term);
        do_paint(term, ctx, TRUE);
        if (need_sbar_update)
            update_sbar(term);
        do_paint(term, ctx, TRUE);
@@ -428,6 +437,12 @@ Terminal *term_init(Config *mycfg, struct unicode_data *ucsdata,
     term->resize_fn = NULL;
     term->resize_ctx = NULL;
     term->in_term_out = FALSE;
     term->resize_fn = NULL;
     term->resize_ctx = NULL;
     term->in_term_out = FALSE;
+    term->ltemp = NULL;
+    term->wcFrom = NULL;
+    term->wcTo = NULL;
+
+    term->bidi_cache_size = 0;
+    term->pre_bidi_cache = term->post_bidi_cache = NULL;
 
     return term;
 }
 
     return term;
 }
@@ -436,6 +451,7 @@ void term_free(Terminal *term)
 {
     unsigned long *line;
     struct beeptime *beep;
 {
     unsigned long *line;
     struct beeptime *beep;
+    int i;
 
     while ((line = delpos234(term->scrollback, 0)) != NULL)
        sfree(line);
 
     while ((line = delpos234(term->scrollback, 0)) != NULL)
        sfree(line);
@@ -457,6 +473,17 @@ void term_free(Terminal *term)
        printer_finish_job(term->print_job);
     bufchain_clear(&term->printer_buf);
     sfree(term->paste_buffer);
        printer_finish_job(term->print_job);
     bufchain_clear(&term->printer_buf);
     sfree(term->paste_buffer);
+    sfree(term->ltemp);
+    sfree(term->wcFrom);
+    sfree(term->wcTo);
+
+    for (i = 0; i < term->bidi_cache_size; i++) {
+       sfree(term->pre_bidi_cache[i]);
+       sfree(term->post_bidi_cache[i]);
+    }
+    sfree(term->pre_bidi_cache);
+    sfree(term->post_bidi_cache);
+
     sfree(term);
 }
 
     sfree(term);
 }
 
@@ -3303,12 +3330,65 @@ static int linecmp(Terminal *term, unsigned long *a, unsigned long *b)
 #endif
 
 /*
 #endif
 
 /*
+ * To prevent having to run the reasonably tricky bidi algorithm
+ * too many times, we maintain a cache of the last lineful of data
+ * fed to the algorithm on each line of the display.
+ */
+static int term_bidi_cache_hit(Terminal *term, int line,
+                              unsigned long *lbefore, int width)
+{
+    if (!term->pre_bidi_cache)
+       return FALSE;                  /* cache doesn't even exist yet! */
+
+    if (line >= term->bidi_cache_size)
+       return FALSE;                  /* cache doesn't have this many lines */
+
+    if (!term->pre_bidi_cache[line])
+       return FALSE;                  /* cache doesn't contain _this_ line */
+
+    if (!memcmp(term->pre_bidi_cache[line], lbefore,
+               width * sizeof(unsigned long)))
+       return TRUE;                   /* aha! the line matches the cache */
+
+    return FALSE;                     /* it didn't match. */
+}
+
+static void term_bidi_cache_store(Terminal *term, int line,
+                                 unsigned long *lbefore,
+                                 unsigned long *lafter, int width)
+{
+    if (!term->pre_bidi_cache || term->bidi_cache_size <= line) {
+       int j = term->bidi_cache_size;
+       term->bidi_cache_size = line+1;
+       term->pre_bidi_cache = sresize(term->pre_bidi_cache,
+                                      term->bidi_cache_size,
+                                      unsigned long *);
+       term->post_bidi_cache = sresize(term->post_bidi_cache,
+                                       term->bidi_cache_size,
+                                       unsigned long *);
+       while (j < term->bidi_cache_size) {
+           term->pre_bidi_cache[j] = term->post_bidi_cache[j] = NULL;
+           j++;
+       }
+    }
+
+    sfree(term->pre_bidi_cache[line]);
+    sfree(term->post_bidi_cache[line]);
+
+    term->pre_bidi_cache[line] = snewn(width, unsigned long);
+    term->post_bidi_cache[line] = snewn(width, unsigned long);
+
+    memcpy(term->pre_bidi_cache[line], lbefore, width * sizeof(unsigned long));
+    memcpy(term->post_bidi_cache[line], lafter, width * sizeof(unsigned long));
+}
+
+/*
  * Given a context, update the window. Out of paranoia, we don't
  * allow WM_PAINT responses to do scrolling optimisations.
  */
 static void do_paint(Terminal *term, Context ctx, int may_optimise)
 {
  * Given a context, update the window. Out of paranoia, we don't
  * allow WM_PAINT responses to do scrolling optimisations.
  */
 static void do_paint(Terminal *term, Context ctx, int may_optimise)
 {
-    int i, j, our_curs_y, our_curs_x;
+    int i, it, j, our_curs_y, our_curs_x;
     unsigned long rv, cursor;
     pos scrpos;
     char ch[1024];
     unsigned long rv, cursor;
     pos scrpos;
     char ch[1024];
@@ -3324,8 +3404,8 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
     if (term->in_vbell) {
        ticks = GETTICKCOUNT();
        if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
     if (term->in_vbell) {
        ticks = GETTICKCOUNT();
        if (ticks - term->vbell_startpoint >= VBELL_TIMEOUT)
-           term->in_vbell = FALSE; 
-   }
+           term->in_vbell = FALSE;
+    }
 
     rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
 
 
     rv = (!term->rvideo ^ !term->in_vbell ? ATTR_REVERSE : 0);
 
@@ -3411,6 +3491,67 @@ static void do_paint(Terminal *term, Context ctx, int may_optimise)
                                  term->disptext[idx + term->cols]);
        term->disptext[idx + term->cols] = ldata[term->cols];
 
                                  term->disptext[idx + term->cols]);
        term->disptext[idx + term->cols] = ldata[term->cols];
 
+       /* Do Arabic shaping and bidi. */
+       if(!term->cfg.bidi || !term->cfg.arabicshaping) {
+
+           if (!term_bidi_cache_hit(term, i, ldata, term->cols)) {
+
+               for(it=0; it<term->cols ; it++)
+               {
+                   int uc = (ldata[it] & 0xFFFF);
+
+                   switch (uc & CSET_MASK) {
+                     case ATTR_LINEDRW:
+                       if (!term->cfg.rawcnp) {
+                           uc = term->ucsdata->unitab_xterm[uc & 0xFF];
+                           break;
+                       }
+                     case ATTR_ASCII:
+                       uc = term->ucsdata->unitab_line[uc & 0xFF];
+                       break;
+                     case ATTR_SCOACS:
+                       uc = term->ucsdata->unitab_scoacs[uc&0xFF];
+                       break;
+                   }
+                   switch (uc & CSET_MASK) {
+                     case ATTR_ACP:
+                       uc = term->ucsdata->unitab_font[uc & 0xFF];
+                       break;
+                     case ATTR_OEMCP:
+                       uc = term->ucsdata->unitab_oemcp[uc & 0xFF];
+                       break;
+                   }
+
+                   term->wcFrom[it].origwc = term->wcFrom[it].wc = uc;
+                   term->wcFrom[it].index = it;
+               }
+
+               if(!term->cfg.bidi)
+                   do_bidi(term->wcFrom, term->cols);
+
+               /* this is saved iff done from inside the shaping */
+               if(!term->cfg.bidi && term->cfg.arabicshaping)
+                   for(it=0; it<term->cols; it++)
+                       term->wcTo[it] = term->wcFrom[it];
+
+               if(!term->cfg.arabicshaping)
+                   do_shape(term->wcFrom, term->wcTo, term->cols);
+
+               for(it=0; it<term->cols ; it++)
+               {
+                   term->ltemp[it] = ldata[term->wcTo[it].index];
+
+                   if (term->wcTo[it].origwc != term->wcTo[it].wc)
+                       term->ltemp[it] = ((term->ltemp[it] & 0xFFFF0000) |
+                                          term->wcTo[it].wc);
+               }
+               term_bidi_cache_store(term, i, ldata, term->ltemp, term->cols);
+               ldata = term->ltemp;
+           } else {
+               ldata = term->post_bidi_cache[i];
+           }
+       }
+
        for (j = 0; j < term->cols; j++, idx++) {
            unsigned long tattr, tchar;
            unsigned long *d = ldata + j;
        for (j = 0; j < term->cols; j++, idx++) {
            unsigned long tattr, tchar;
            unsigned long *d = ldata + j;
index c3ee216..e2b06b1 100644 (file)
@@ -208,6 +208,14 @@ struct terminal_tag {
      * through.
      */
     int in_term_out;
      * through.
      */
     int in_term_out;
+
+    /*
+     * These are buffers used by the bidi and Arabic shaping code.
+     */
+    unsigned long *ltemp;
+    bidi_char *wcFrom, *wcTo;
+    unsigned long **pre_bidi_cache, **post_bidi_cache;
+    int bidi_cache_size;
 };
 
 #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8)
 };
 
 #define in_utf(term) ((term)->utf || (term)->ucsdata->line_codepage==CP_UTF8)
index fd977ed..71f30b4 100644 (file)
--- a/window.c
+++ b/window.c
@@ -1081,6 +1081,36 @@ static void init_palette(void)
 }
 
 /*
 }
 
 /*
+ * This is a wrapper to ExtTextOut() to force Windows to display
+ * the precise glyphs we give it. Otherwise it would do its own
+ * bidi and Arabic shaping, and we would end up uncertain which
+ * characters it had put where.
+ */
+static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc,
+                         unsigned short *lpString, UINT cbCount,
+                         CONST INT *lpDx)
+{
+
+    GCP_RESULTSW gcpr;
+    char *buffer = snewn(cbCount*2+2, char);
+    char *classbuffer = snewn(cbCount, char);
+    memset(&gcpr, 0, sizeof(gcpr));
+    memset(buffer, 0, cbCount*2+2);
+    memset(classbuffer, GCPCLASS_NEUTRAL, cbCount);
+
+    gcpr.lStructSize = sizeof(gcpr);
+    gcpr.lpGlyphs = (void *)buffer;
+    gcpr.lpClass = classbuffer;
+    gcpr.nGlyphs = cbCount;
+
+    GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr,
+                          FLI_MASK | GCP_CLASSIN);
+
+    ExtTextOut(hdc, x, y, ETO_GLYPH_INDEX | ETO_CLIPPED | ETO_OPAQUE, lprc,
+              buffer, cbCount, lpDx);
+}
+
+/*
  * Initialise all the fonts we will need initially. There may be as many as
  * three or as few as one.  The other (poentially) twentyone fonts are done
  * if/when they are needed.
  * Initialise all the fonts we will need initially. There may be as many as
  * three or as few as one.  The other (poentially) twentyone fonts are done
  * if/when they are needed.
@@ -2989,9 +3019,13 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        for (i = 0; i < len; i++)
            wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
 
        for (i = 0; i < len; i++)
            wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
 
-       ExtTextOutW(hdc, x,
+       /* print Glyphs as they are, without Windows' Shaping*/
+       exact_textout(hdc, x, y - font_height * (lattr == LATTR_BOT) + text_adjust,
+                     &line_box, wbuf, len, IpDx);
+/*     ExtTextOutW(hdc, x,
                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
                    ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
                    y - font_height * (lattr == LATTR_BOT) + text_adjust,
                    ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
+ */
 
        /* And the shadow bold hack. */
        if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
 
        /* And the shadow bold hack. */
        if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
index 3f72fce..bbde922 100644 (file)
--- a/winhelp.h
+++ b/winhelp.h
@@ -29,6 +29,8 @@
 #define WINHELP_CTX_features_qtitle "features.qtitle"
 #define WINHELP_CTX_features_dbackspace "features.dbackspace"
 #define WINHELP_CTX_features_charset "features.charset"
 #define WINHELP_CTX_features_qtitle "features.qtitle"
 #define WINHELP_CTX_features_dbackspace "features.dbackspace"
 #define WINHELP_CTX_features_charset "features.charset"
+#define WINHELP_CTX_features_arabicshaping "features.arabicshaping"
+#define WINHELP_CTX_features_bidi "features.bidi"
 #define WINHELP_CTX_terminal_autowrap "terminal.autowrap"
 #define WINHELP_CTX_terminal_decom "terminal.decom"
 #define WINHELP_CTX_terminal_lfhascr "terminal.lfhascr"
 #define WINHELP_CTX_terminal_autowrap "terminal.autowrap"
 #define WINHELP_CTX_terminal_decom "terminal.decom"
 #define WINHELP_CTX_terminal_lfhascr "terminal.lfhascr"