# HG changeset patch
# User K
# Date 1386335779 -7200
#      Fri Dec 06 15:16:19 2013 +0200
# Node ID da3b8749f162ff4849633e32bd19eabb4370a688
# Parent  fedf777c6b24651d4dd86310334521ace0b2139b
Win32: WCHAR support it ngx_read_dir() to enable Unicode in autoindex

Functions ngx_open_dir() and ngx_read_dir() are refactored to use WCHAR file names.
UTF-8 version of file name is cached in additional buffer.

diff -r fedf777c6b24 -r da3b8749f162 src/os/win32/ngx_files.c
--- a/src/os/win32/ngx_files.c	Fri Dec 06 13:16:56 2013 +0400
+++ b/src/os/win32/ngx_files.c	Fri Dec 06 15:16:19 2013 +0200
@@ -426,9 +426,21 @@
 ngx_int_t
 ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir)
 {
+    size_t      len;
+    u_short    *u;
+    u_short     utf16[NGX_UTF16_BUFLEN];
+
     ngx_cpystrn(name->data + name->len, NGX_DIR_MASK, NGX_DIR_MASK_LEN + 1);
 
-    dir->dir = FindFirstFile((const char *) name->data, &dir->finddata);
+    len = NGX_UTF16_BUFLEN;
+    u = ngx_utf8_to_utf16(utf16, name->data, &len);
+
+    if (u == NULL) {
+        return NGX_ERROR;
+    }
+    
+    dir->namelen = 0; // clear cached utf-8 buffer
+    dir->dir = FindFirstFileW(u, &dir->finddata);
 
     if (dir->dir == INVALID_HANDLE_VALUE) {
         return NGX_ERROR;
@@ -441,6 +453,8 @@
 }
 
 
+
+
 ngx_int_t
 ngx_read_dir(ngx_dir_t *dir)
 {
@@ -449,7 +463,8 @@
         return NGX_OK;
     }
 
-    if (FindNextFile(dir->dir, &dir->finddata) != 0) {
+    dir->namelen = 0; // clear cached utf-8 buffer
+    if (FindNextFileW(dir->dir, &dir->finddata) != 0) {
         dir->type = 1;
         return NGX_OK;
     }
@@ -574,6 +589,87 @@
     }
 }
 
+static u_char *
+ngx_utf8_encode_char(u_long ch, u_char *str) {
+    if (ch <= 0x7f) {
+        *str++ = (u_char)ch;
+    } else if (ch <= 0x0007ff) {
+        *str++ = (u_char)(0xc0 | (ch >>  6) & 0x1f);
+        *str++ = (u_char)(0x80 | (ch & 0x3f));
+    } else if (ch <= 0x00ffff) {
+        *str++ = (u_char)(0xe0 | (ch >> 12) & 0x0f);
+        *str++ = (u_char)(0x80 | (ch >>  6) & 0x3f);
+        *str++ = (u_char)(0x80 | (ch & 0x3f));
+    } else if (ch <= 0x10ffff) {
+        *str++ = (u_char)(0xf0 | (ch >> 18) & 0x07);
+        *str++ = (u_char)(0x80 | (ch >> 12) & 0x3f);
+        *str++ = (u_char)(0x80 | (ch >>  6) & 0x3f);
+        *str++ = (u_char)(0x80 | (ch & 0x3f));
+    }
+    return str;
+}
+
+static u_char *
+ngx_utf16_to_utf8(u_char *utf8, u_short *utf16, size_t *len)
+{
+    u_short   *u;
+    u_char    *p;
+    u_long    surrogate;
+
+    p = utf8;
+    u = utf16;
+
+    for ( ;; ) {
+
+        if ((*u >= 0xd800) && (*u <= 0xdbff)) {
+            surrogate = (*u - 0xd800) << 10;
+            if (!((*(u+1) >= 0xdc00) && (*(u+1) <= 0xdfff))) {
+                // Broken surrogate pair, out as is
+                p = ngx_utf8_encode_char(*u, p);
+            }
+        } else if ((*u >= 0xdc00) && (*u <= 0xdfff)) {
+            surrogate |= *u - 0xdc00;
+            p = ngx_utf8_encode_char(surrogate + 0x10000, p);
+        } else {
+            p = ngx_utf8_encode_char(*u, p);
+        }
+        
+        if (*u == 0) {
+            *len = p - utf8 - 1;
+            return utf8;
+        }
+        
+        u++;
+    }
+
+    /* unreachable */
+}
+
+
+u_char *
+ngx_de_name(ngx_dir_t *dir)
+{
+    size_t   len;
+    u_char  *u;
+    
+    if (!dir->namelen) {
+        len = NGX_UTF8_BUFLEN;
+        u = ngx_utf16_to_utf8(dir->name, dir->finddata.cFileName, &len);
+        dir->namelen = len;
+    }
+    return dir->name;
+}
+
+
+size_t 
+ngx_de_namelen(ngx_dir_t *dir)
+{
+    if (!dir->namelen) {
+        ngx_de_name(dir);
+    }
+    return dir->namelen;
+}
+
 
 ngx_int_t
 ngx_de_info(u_char *name, ngx_dir_t *dir)
@@ -801,12 +897,19 @@
 
         n = ngx_utf8_decode(&p, 4);
 
-        if (n > 0xffff) {
+        if (n > 0x10ffff) {
             ngx_set_errno(NGX_EILSEQ);
             return NULL;
         }
 
-        *u++ = (u_short) n;
+        if (n > 0xffff) {
+            // LE order
+            *u++ = (u_short) (0xd800 | ( ((n >> 16) & 0x1f) - 1 ) << 6 | (n & 0xffff) >> 10); // high surrogate
+            // ??? check buffer length ???
+            *u++ = (u_short) (0xdc00 | n & 0x3ff); // low surrogate
+        } else {
+            *u++ = (u_short) n;
+        }
     }
 
     /* the given buffer is not enough, allocate a new one */
@@ -838,13 +941,20 @@
 
         n = ngx_utf8_decode(&p, 4);
 
-        if (n > 0xffff) {
+        if (n > 0x10ffff) {
             free(utf16);
             ngx_set_errno(NGX_EILSEQ);
             return NULL;
         }
 
-        *u++ = (u_short) n;
+        if (n > 0xffff) {
+            // LE order
+            *u++ = (u_short) (0xd800 | ( ((n >> 16) & 0x1f) - 1 ) << 6 | (n & 0xffff) >> 10); // high surrogate
+            // ??? check buffer length ???
+            *u++ = (u_short) (0xdc00 | n & 0x3ff); // low surrogate
+        } else {
+            *u++ = (u_short) n;
+        }
     }
 
     /* unreachable */
diff -r fedf777c6b24 -r da3b8749f162 src/os/win32/ngx_files.h
--- a/src/os/win32/ngx_files.h	Fri Dec 06 13:16:56 2013 +0400
+++ b/src/os/win32/ngx_files.h	Fri Dec 06 15:16:19 2013 +0200
@@ -28,9 +28,15 @@
 } ngx_file_mapping_t;
 
 
+/* Windows defines MAX_PATH as 260 */
+#define NGX_UTF8_BUFLEN  ((MAX_PATH-1)*4+1) /* utf-8 char takes at most 4 bytes for U+10FFFF range */
+
+
 typedef struct {
     HANDLE                          dir;
-    WIN32_FIND_DATA                 finddata;
+    WIN32_FIND_DATAW                finddata;
+    u_char                          name[NGX_UTF8_BUFLEN];
+    size_t                          namelen;
 
     unsigned                        valid_info:1;
     unsigned                        type:1;
@@ -208,8 +214,8 @@
 #define ngx_dir_access(a)           (a)
 
 
-#define ngx_de_name(dir)            ((u_char *) (dir)->finddata.cFileName)
-#define ngx_de_namelen(dir)         ngx_strlen((dir)->finddata.cFileName)
+u_char *ngx_de_name(ngx_dir_t *dir);
+size_t ngx_de_namelen(ngx_dir_t *dir);
 
 ngx_int_t ngx_de_info(u_char *name, ngx_dir_t *dir);
 #define ngx_de_info_n               "dummy()"
