Index: src/file.c
==================================================================
--- src/file.c
+++ src/file.c
@@ -64,23 +64,24 @@
 ** Fill stat buf with information received from stat() or lstat().
 ** lstat() is called on Unix if isWd is TRUE and allow-symlinks setting is on.
 **
 */
 static int fossil_stat(const char *zFilename, struct stat *buf, int isWd){
+  int rc;
 #if !defined(_WIN32)
+  char *zMbcs = fossil_utf8_to_filename(zFilename);
   if( isWd && g.allowSymlinks ){
-    return lstat(zFilename, buf);
+    rc = lstat(zMbcs, buf);
   }else{
-    return stat(zFilename, buf);
+    rc = stat(zMbcs, buf);
   }
 #else
-  int rc = 0;
-  wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename);
+  wchar_t *zMbcs = fossil_utf8_to_filename(zFilename);
   rc = _wstati64(zMbcs, buf);
-  fossil_unicode_free(zMbcs);
+#endif
+  fossil_filename_free(zMbcs);
   return rc;
-#endif
 }
 
 /*
 ** Fill in the fileStat variable for the file named zFilename.
 ** If zFilename==0, then use the previous value of fileStat if
@@ -303,16 +304,17 @@
 /*
 ** Wrapper around the access() system call.
 */
 int file_access(const char *zFilename, int flags){
 #ifdef _WIN32
-  wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename);
+  wchar_t *zMbcs = fossil_utf8_to_filename(zFilename);
   int rc = _waccess(zMbcs, flags);
-  fossil_unicode_free(zMbcs);
 #else
-  int rc = access(zFilename, flags);
+  char *zMbcs = fossil_utf8_to_filename(zFilename);
+  int rc = access(zMbcs, flags);
 #endif
+  fossil_filename_free(zMbcs);
   return rc;
 }
 
 /*
 ** Find an unused filename similar to zBase with zSuffix appended.
@@ -402,19 +404,20 @@
 #if !defined(_WIN32)
   struct timeval tv[2];
   memset(tv, 0, sizeof(tv[0])*2);
   tv[0].tv_sec = newMTime;
   tv[1].tv_sec = newMTime;
-  utimes(zFilename, tv);
+  char *zMbcs = fossil_utf8_to_filename(zFilename);
+  utimes(zMbcs, tv);
 #else
   struct _utimbuf tb;
-  wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename);
+  wchar_t *zMbcs = fossil_utf8_to_filename(zFilename);
   tb.actime = newMTime;
   tb.modtime = newMTime;
   _wutime(zMbcs, &tb);
-  fossil_unicode_free(zMbcs);
 #endif
+  fossil_filename_free(zMbcs);
 }
 
 /*
 ** COMMAND: test-set-mtime
 **
@@ -441,16 +444,17 @@
 /*
 ** Delete a file.
 */
 void file_delete(const char *zFilename){
 #ifdef _WIN32
-  wchar_t *z = fossil_utf8_to_unicode(zFilename);
+  wchar_t *z = fossil_utf8_to_filename(zFilename);
   _wunlink(z);
-  fossil_unicode_free(z);
 #else
+  char *z = fossil_utf8_to_filename(zFilename);
   unlink(zFilename);
 #endif
+  fossil_filename_free(z);
 }
 
 /*
 ** Create the directory named in the argument, if it does not already
 ** exist.  If forceFlag is 1, delete any prior non-directory object
@@ -464,18 +468,18 @@
     if( !forceFlag ) return 1;
     file_delete(zName);
   }
   if( rc!=1 ){
 #if defined(_WIN32)
-    int rc;
-    wchar_t *zMbcs = fossil_utf8_to_unicode(zName);
+    wchar_t *zMbcs = fossil_utf8_to_filename(zName);
     rc = _wmkdir(zMbcs);
-    fossil_unicode_free(zMbcs);
-    return rc;
 #else
-    return mkdir(zName, 0755);
+    char *zMbcs = fossil_utf8_to_filename(zName);
+    rc = mkdir(zName, 0755);
 #endif
+    fossil_filename_free(zMbcs);
+    return rc;
   }
   return 0;
 }
 
 /*
@@ -483,11 +487,10 @@
 ** a file in a repository.  Valid filenames follow all of the
 ** following rules:
 **
 **     *  Does not begin with "/"
 **     *  Does not contain any path element named "." or ".."
-**     *  Does not contain any of these characters in the path: "\"
 **     *  Does not end with "/".
 **     *  Does not contain two or more "/" characters in a row.
 **     *  Contains at least one character
 **
 ** Invalid UTF8 characters result in a false return if bStrictUtf8 is
@@ -547,12 +550,10 @@
         if( (z[++i]&0xc0)!=0x80 ){
           /* Invalid second continuation byte */
           return 0;
         }
       }
-    }else if( bStrictUtf8 && (c=='\\') ){
-      return 0;
     }
     if( c=='/' ){
       if( z[i+1]=='/' ) return 0;
       if( z[i+1]=='.' ){
         if( z[i+2]=='/' || z[i+2]==0 ) return 0;
@@ -580,11 +581,11 @@
 }
 
 /*
 ** Simplify a filename by
 **
-**  * Convert all \ into / on windows
+**  * Convert all \ into / on windows and cygwin
 **  * removing any trailing and duplicate /
 **  * removing /./
 **  * removing /A/../
 **
 ** Changes are made in-place.  Return the new name length.
@@ -593,12 +594,12 @@
 */
 int file_simplify_name(char *z, int n, int slash){
   int i, j;
   if( n<0 ) n = strlen(z);
 
-  /* On windows convert all \ characters to / */
-#if defined(_WIN32)
+  /* On windows/cygwin convert all \ characters to / */
+#if defined(_WIN32) || defined(__CYGWIN__)
   for(i=0; i<n; i++){
     if( z[i]=='\\' ) z[i] = '/';
   }
 #endif
 
@@ -705,11 +706,13 @@
 ** Return true if zPath is an absolute pathname.  Return false
 ** if it is relative.
 */
 int file_is_absolute_path(const char *zPath){
   if( zPath[0]=='/'
-#if defined(_WIN32)
+#if defined(__CYGWIN__)
+      || zPath[0]=='\\'
+#elif defined(_WIN32)
       || zPath[0]=='\\'
       || (strlen(zPath)>3 && zPath[1]==':'
            && (zPath[2]=='\\' || zPath[2]=='/'))
 #endif
   ){
@@ -1137,14 +1140,14 @@
 ** Like fopen() but always takes a UTF8 argument.
 */
 FILE *fossil_fopen(const char *zName, const char *zMode){
 #ifdef _WIN32
   wchar_t *uMode = fossil_utf8_to_unicode(zMode);
-  wchar_t *uName = fossil_utf8_to_unicode(zName);
+  wchar_t *uName = fossil_utf8_to_filename(zName);
   FILE *f = _wfopen(uName, uMode);
-  fossil_unicode_free(uName);
+  fossil_filename_free(uName);
   fossil_unicode_free(uMode);
 #else
   FILE *f = fopen(zName, zMode);
 #endif
   return f;
 }

Index: src/rebuild.c
==================================================================
--- src/rebuild.c
+++ src/rebuild.c
@@ -841,11 +841,11 @@
   Blob aContent; /* content of the just read artifact */
   static int nFileRead = 0;
   void *zUnicodePath;
   char *zUtf8Name;
 
-  zUnicodePath = fossil_utf8_to_unicode(zPath);
+  zUnicodePath = fossil_utf8_to_filename(zPath);
   d = opendir(zUnicodePath);
   if( d ){
     while( (pEntry=readdir(d))!=0 ){
       Blob path;
       char *zSubpath;
@@ -875,11 +875,11 @@
     closedir(d);
   }else {
     fossil_panic("encountered error %d while trying to open \"%s\".",
                   errno, g.argv[3]);
   }
-  fossil_unicode_free(zUnicodePath);
+  fossil_filename_free(zUnicodePath);
 }
 
 /*
 ** COMMAND: reconstruct*
 **

Index: src/utf8.c
==================================================================
--- src/utf8.c
+++ src/utf8.c
@@ -111,19 +111,36 @@
 ** Translate text from the filename character set into
 ** to precomposed UTF8.  Return a pointer to the translated text.
 ** Call fossil_filename_free() to deallocate any memory used to store the
 ** returned pointer when done.
 */
-char *fossil_filename_to_utf8(const void *zFilename){
+char *fossil_filename_to_utf8(void *zFilename){
 #if defined(_WIN32)
-  int nByte = WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, 0, 0, 0, 0);
-  char *zUtf = sqlite3_malloc( nByte );
+  int nByte;
+  char *zUtf;
+  WCHAR *wUnicode = zFilename;
+  while( *wUnicode != 0 ){
+    if ( (*wUnicode & 0xFF80) == 0xF000 ){
+      WCHAR converted = (*wUnicode & 0x7F);
+      /* Only really convert it when the resulting char is in the given range*/
+      if ( (converted < 32) || wcschr(L"\"*<>?|:", converted) ){
+        *wUnicode = converted;
+      }
+    }
+    ++wUnicode;
+  }
+  nByte = WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, 0, 0, 0, 0);
+  zUtf = sqlite3_malloc( nByte );
   if( zUtf==0 ){
     return 0;
   }
   WideCharToMultiByte(CP_UTF8, 0, zFilename, -1, zUtf, nByte, 0, 0);
   return zUtf;
+#elif defined(__CYGWIN__)
+  char *zOut;
+  zOut = fossil_strdup(zFilename);
+  return zOut;
 #elif defined(__APPLE__) && !defined(WITHOUT_ICONV)
   char *zIn = (char*)zFilename;
   char *zOut;
   iconv_t cd;
   size_t n, x;
@@ -149,19 +166,71 @@
   return zOut;
 #else
   return (char *)zFilename;  /* No-op on non-mac unix */
 #endif
 }
+
+/*
+** Translate UTF8 to unicode for use in filename translations.
+** Return a pointer to the translated text..  Call fossil_filename_free()
+** to deallocate any memory used to store the returned pointer when done.
+**
+** On Windows, characters in the range U+0001 to U+0031 and the
+** characters '"', '*', ':', '<', '>', '?' and '|' are invalid
+** to be used. Therefore, translated those to characters in the
+** (private use area), in the range U+F001 - U+F07F, so those
+** characters never arrive in any Windows API. The filenames might
+** look strange in Windows explorer, but in the cygwin shell
+** everything looks as expected.
+**
+** See: <http://cygwin.com/cygwin-ug-net/using-specialnames.html>
+**
+*/
+void *fossil_utf8_to_filename(const char *zUtf8){
+#ifdef _WIN32
+  WCHAR *zUnicode = fossil_utf8_to_unicode(zUtf8);
+  WCHAR *wUnicode = zUnicode;
+  /* If path starts with "<drive>:/" or "<drive>:\", don't translate the ':' */
+  if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
+           && (zUtf8[2]=='\\' || zUtf8[2]=='/')) {
+    zUnicode[2] = '\\';
+    wUnicode += 3;
+  }
+  while( *wUnicode != '\0' ){
+    if ( (*wUnicode < 32) || wcschr(L"\"*<>?|:", *wUnicode) ){
+      *wUnicode |= 0xF000;
+    }else if( *wUnicode == '/' ){
+      *wUnicode = '\\';
+    }
+    ++wUnicode;
+  }
+
+  return zUnicode;
+#elif defined(__CYGWIN__)
+  char *zPath = fossil_strdup(zUtf8);
+  char *p = zPath;
+  while( (*p = *zUtf8++) != 0){
+    if (*p++ == '\\' ) {
+      p[-1] = '/';
+    }
+  }
+  return zPath;
+#elif defined(__APPLE__) && !defined(WITHOUT_ICONV)
+  return fossil_strdup(zUtf8);
+#else
+  return (void *)zUtf8;  /* No-op on unix */
+#endif
+}
 
 /*
 ** Deallocate any memory that was previously allocated by
-** fossil_filename_to_utf8().
+** fossil_filename_to_utf8() or fossil_utf8_to_filename().
 */
-void fossil_filename_free(char *pOld){
+void fossil_filename_free(void *pOld){
 #if defined(_WIN32)
   sqlite3_free(pOld);
-#elif defined(__APPLE__) && !defined(WITHOUT_ICONV)
+#elif (defined(__APPLE__) && !defined(WITHOUT_ICONV)) || defined(__CYGWIN__)
   fossil_free(pOld);
 #else
   /* No-op on all other unix */
 #endif
 }

Index: src/vfile.c
==================================================================
--- src/vfile.c
+++ src/vfile.c
@@ -459,11 +459,11 @@
     );
   }
   depth++;
 
   zDir = blob_str(pPath);
-  zNative = fossil_utf8_to_unicode(zDir);
+  zNative = fossil_utf8_to_filename(zDir);
   d = opendir(zNative);
   if( d ){
     while( (pEntry=readdir(d))!=0 ){
       char *zPath;
       char *zUtf8;
@@ -491,11 +491,11 @@
       fossil_filename_free(zUtf8);
       blob_resize(pPath, origSize);
     }
     closedir(d);
   }
-  fossil_unicode_free(zNative);
+  fossil_filename_free(zNative);
 
   depth--;
   if( depth==0 ){
     db_finalize(&ins);
   }