Fixed a lot of bugs in TEXT_TabbedTextOut. With the test cases used to
find them.

diff --git a/dlls/user/text.c b/dlls/user/text.c
index c05d008..2c3f265 100644
--- a/dlls/user/text.c
+++ b/dlls/user/text.c
@@ -28,6 +28,7 @@
 #include "wine/port.h"
 
 #include <stdarg.h>
+#include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 
@@ -1214,17 +1215,13 @@
 {
     INT defWidth;
     SIZE extent;
-    int i;
+    int i, j;
     int start = x;
-    BOOL first = TRUE;
-
-    extent.cx = 0;
-    extent.cy = 0;
 
     if (!lpTabPos)
         cTabStops=0;
 
-    if (cTabStops == 1 && *lpTabPos >= /* sic */ 0)
+    if (cTabStops == 1)
     {
         defWidth = *lpTabPos;
         cTabStops = 0;
@@ -1234,66 +1231,70 @@
         TEXTMETRICA tm;
         GetTextMetricsA( hdc, &tm );
         defWidth = 8 * tm.tmAveCharWidth;
-        if (cTabStops == 1)
-            cTabStops = 0; /* on negative *lpTabPos */
     }
 
     while (count > 0)
     {
-        /* tokenize string by tabs */
+        RECT r;
+        INT x0;
+        x0 = x;
+        r.left = x0;
+        /* chop the string into substrings of 0 or more <tabs> 
+         * possibly followed by 1 or more normal characters */
         for (i = 0; i < count; i++)
-            if (lpstr[i] == '\t') break;
-
-        GetTextExtentPointW( hdc, lpstr, i, &extent );
-
-        /* the first time round the loop we should use the value of x
-         * passed into the function.
-         * all other times, we calculate it here */
-        if (!first)
-        {
+            if (lpstr[i] != '\t') break;
+        for (j = i; j < count; j++)
+            if (lpstr[j] == '\t') break;
+        /* get the extent of the normal character part */
+        GetTextExtentPointW( hdc, lpstr + i, j - i , &extent );
+        /* and if there is a <tab>, calculate its position */
+        if( i) {
             /* get x coordinate for the drawing of this string */
-            for (; cTabStops > 0; lpTabPos++, cTabStops--)
+            for (; cTabStops > i; lpTabPos++, cTabStops--)
             {
-                if (*lpTabPos >= 0)
-                {
-                    if (nTabOrg + *lpTabPos >= x)
-                    {
-                        x = nTabOrg + *lpTabPos;
+                if( nTabOrg + abs( *lpTabPos) > x) {
+                    if( lpTabPos[ i - 1] >= 0) {
+                        /* a left aligned tab */
+                        x = nTabOrg + lpTabPos[ i-1] + extent.cx;
                         break;
                     }
-                }
-                else
-                {
-                    /* if tab pos is negative then text is right-aligned to tab
-                     * stop meaning that the string extends to the left, so we
-                     * must subtract the width of the string */
-                    if (nTabOrg + -*lpTabPos -extent.cx >= x)
+                    else
                     {
-                        x = nTabOrg + -*lpTabPos - extent.cx;
-                        break;
+                        /* if tab pos is negative then text is right-aligned
+                         * to tab stop meaning that the string extends to the
+                         * left, so we must subtract the width of the string */
+                        if (nTabOrg - lpTabPos[ i - 1] - extent.cx > x)
+                        {
+                            x = nTabOrg - lpTabPos[ i - 1];
+                            x0 = x - extent.cx;
+                            break;
+                        }
                     }
                 }
             }
             /* if we have run out of tab stops and we have a valid default tab
              * stop width then round x up to that width */
-            if ((cTabStops <= 0) && (defWidth > 0))
-                x = nTabOrg + ((x - nTabOrg) / defWidth + 1) * defWidth;
-        }
-        else first = FALSE;
-
+            if ((cTabStops <= i) && (defWidth > 0)) {
+                x0 = nTabOrg + ((x - nTabOrg) / defWidth + i) * defWidth;
+                x = x0 + extent.cx;
+            } else if ((cTabStops <= i) && (defWidth < 0)) {
+                x = nTabOrg + ((x - nTabOrg + extent.cx) / -defWidth + i)
+                    * -defWidth;
+                x0 = x - extent.cx;
+            }
+        } else
+            x += extent.cx;
+        
         if (fDisplayText)
         {
-            RECT r;
-            r.left   = x;
             r.top    = y;
-            r.right  = x + extent.cx;
+            r.right  = x;
             r.bottom = y + extent.cy;
-            ExtTextOutW( hdc, x, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
-                         &r, lpstr, i, NULL );
+            ExtTextOutW( hdc, x0, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0,
+                         &r, lpstr + i, j - i, NULL );
         }
-        x += extent.cx;
-        count -= i+1;
-        lpstr += i+1;
+        count -= j;
+        lpstr += j;
     }
     return MAKELONG(x - start, extent.cy);
 }