add wide versions of win32 system calls (first step in
Gurusamy Sarathy [Fri, 28 May 1999 21:22:23 +0000 (21:22 +0000)]
globalization); delayload winsock for performance if compiling
with VC 6.0

p4raw-id: //depot/perl@3501

win32/Makefile
win32/dl_win32.xs
win32/makefile.mk
win32/win32.c
win32/win32.h

index 086da4c..3d8570e 100644 (file)
@@ -42,9 +42,11 @@ INST_VER     = \5.00557
 #USE_MULTI     = define
 
 #
-# uncomment next line if you are using Visual C++ 2.x
+# uncomment one of the following lines if you are using either
+# Visual C++ 2.x or Visual C++ 6.x (aka Visual Studio 98)
 #
 #CCTYPE                = MSVC20
+#CCTYPE                = MSVC60
 
 #
 # uncomment next line if you want to use the perl object
@@ -58,15 +60,6 @@ INST_VER     = \5.00557
 #CFG           = Debug
 
 #
-# uncomment next option if you want to use the VC++ compiler optimization.
-# Warning: This is known to produce incorrect code for compiler versions
-# earlier than VC++ 98 (Visual Studio 6.0). VC++ 98 generates code that
-# successfully passes the Perl regression test suite. It hasn't yet been
-# widely tested with real applications though.
-#
-#CFG           = Optimize
-
-#
 # uncomment to enable use of PerlCRT.DLL when using the Visual C compiler.
 # Highly recommended.  It has patches that fix known bugs in MSVCRT.DLL.
 # This currently requires VC 5.0 with Service Pack 3.
@@ -187,6 +180,20 @@ ARCHNAME   = MSWin32-$(PROCESSOR_ARCHITECTURE)
 !ENDIF
 !ENDIF
 
+# Visual Studio 98 specific
+!IF "$(CCTYPE)" == "MSVC60"
+
+# VC 6.0 can load the socket dll on demand.  Makes the test suite
+# run in about 10% less time.
+DELAYLOAD      = -DELAYLOAD:wsock32.dll delayimp.lib 
+
+# VC 6.0 seems capable of compiling perl correctly with optimizations
+# enabled.  Anything earlier fails tests.
+!IF "$(CFG)" == ""
+CFG            = Optimize
+!ENDIF
+!ENDIF
+
 ARCHDIR                = ..\lib\$(ARCHNAME)
 COREDIR                = ..\lib\CORE
 AUTODIR                = ..\lib\auto
@@ -251,7 +258,8 @@ LINK_DBG    = -release
 OPTIMIZE       = $(OPTIMIZE) $(CXX_FLAG)
 !ENDIF
 
-LIBBASEFILES   = $(CRYPT_LIB) oldnames.lib kernel32.lib user32.lib gdi32.lib \
+LIBBASEFILES   = $(DELAYLOAD) $(CRYPT_LIB) \
+               oldnames.lib kernel32.lib user32.lib gdi32.lib \
                winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib \
                oleaut32.lib netapi32.lib uuid.lib wsock32.lib mpr.lib winmm.lib \
                version.lib odbc32.lib odbccp32.lib
index 3473520..8a91973 100644 (file)
@@ -102,9 +102,17 @@ dl_load_file(filename,flags=0)
     int                        flags
     PREINIT:
     CODE:
+    WCHAR wfilename[MAX_PATH];
     DLDEBUG(1,PerlIO_printf(PerlIO_stderr(),"dl_load_file(%s):\n", filename));
-    if (dl_static_linked(filename) == 0)
-       RETVAL = (void*) LoadLibraryEx(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ) ;
+    if (dl_static_linked(filename) == 0) {
+       if (USING_WIDE()) {
+           A2WHELPER(filename, wfilename, sizeof(wfilename), GETINTERPMODE());
+           RETVAL = (void*) LoadLibraryExW(wfilename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+       }
+       else {
+           RETVAL = (void*) LoadLibraryExA(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+       }
+    }
     else
        RETVAL = (void*) GetModuleHandle(NULL);
     DLDEBUG(2,PerlIO_printf(PerlIO_stderr()," libref=%x\n", RETVAL));
index d50408a..c6605ec 100644 (file)
@@ -46,11 +46,17 @@ INST_VER    *= \5.00557
 #USE_MULTI     *= define
 
 #
-# uncomment one
-#
+# uncomment exactly one of the following
+# 
+# Visual C++ 2.x
 #CCTYPE                *= MSVC20
+# Visual C++ > 2.x and < 6.x
 #CCTYPE                *= MSVC
+# Visual C++ >= 6.x
+#CCTYPE                *= MSVC60
+# Borland 5.02 or later
 CCTYPE         *= BORLAND
+# mingw32/egcs or mingw32/gcc
 #CCTYPE                *= GCC
 
 #
@@ -62,25 +68,15 @@ CCTYPE              *= BORLAND
 
 #
 # uncomment next line if you want debug version of perl (big,slow)
+# If not enabled, we automatically try to use maximum optimization
+# with all compilers that are known to have a working optimizer.
 #
 #CFG           *= Debug
 
 #
-# uncomment next option if you want to use the VC++ compiler optimization.
-# This option is only relevant for the Microsoft compiler; we automatically
-# use maximum optimization with the other compilers (unless you specify a
-# DEBUGGING build).
-# Warning: This is known to produce incorrect code for compiler versions
-# earlier than VC++ 98 (Visual Studio 6.0). VC++ 98 generates code that
-# successfully passes the Perl regression test suite. It hasn't yet been
-# widely tested with real applications though.
-#
-#CFG           *= Optimize
-
-#
 # uncomment to enable use of PerlCRT.DLL when using the Visual C compiler.
 # Highly recommended.  It has patches that fix known bugs in MSVCRT.DLL.
-# This currently requires VC 5.0 with Service Pack 3.
+# This currently requires VC 5.0 with Service Pack 3 or later.
 # Get it from CPAN at http://www.perl.com/CPAN/authors/id/D/DO/DOUGL/
 # and follow the directions in the package to install.
 #
@@ -196,6 +192,18 @@ ARCHNAME   = MSWin32-$(PROCESSOR_ARCHITECTURE)-thread
 ARCHNAME       = MSWin32-$(PROCESSOR_ARCHITECTURE)
 .ENDIF
 
+# Visual Studio 98 specific
+.IF "$(CCTYPE)" == "MSVC60"
+
+# VC 6.0 can load the socket dll on demand.  Makes the test suite
+# run in about 10% less time.
+DELAYLOAD      *= -DELAYLOAD:wsock32.dll delayimp.lib 
+
+# VC 6.0 seems capable of compiling perl correctly with optimizations
+# enabled.  Anything earlier fails tests.
+CFG            *= Optimize
+.ENDIF
+
 ARCHDIR                = ..\lib\$(ARCHNAME)
 COREDIR                = ..\lib\CORE
 AUTODIR                = ..\lib\auto
@@ -334,7 +342,8 @@ OPTIMIZE    = -Od $(RUNTIME) -DNDEBUG
 LINK_DBG       = -release
 .ENDIF
 
-LIBBASEFILES   = $(CRYPT_LIB) oldnames.lib kernel32.lib user32.lib gdi32.lib \
+LIBBASEFILES   = $(DELAYLOAD) $(CRYPT_LIB) \
+               oldnames.lib kernel32.lib user32.lib gdi32.lib \
                winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib \
                oleaut32.lib netapi32.lib uuid.lib wsock32.lib mpr.lib winmm.lib \
                version.lib odbc32.lib odbccp32.lib
index 9361191..20aa4ee 100644 (file)
@@ -669,8 +669,12 @@ win32_opendir(char *filename)
     long               idx;
     char               scanname[MAX_PATH+3];
     struct stat                sbuf;
-    WIN32_FIND_DATA    FindData;
+    WIN32_FIND_DATAA   aFindData;
+    WIN32_FIND_DATAW   wFindData;
     HANDLE             fh;
+    char               buffer[MAX_PATH*2];
+    WCHAR              wbuffer[MAX_PATH];
+    char*              ptr;            
 
     len = strlen(filename);
     if (len > MAX_PATH)
@@ -700,7 +704,13 @@ win32_opendir(char *filename)
     scanname[len] = '\0';
 
     /* do the FindFirstFile call */
-    fh = FindFirstFile(scanname, &FindData);
+    if (USING_WIDE()) {
+       A2WHELPER(scanname, wbuffer, sizeof(wbuffer), GETINTERPMODE());
+       fh = FindFirstFileW(wbuffer, &wFindData);
+    }
+    else {
+       fh = FindFirstFileA(scanname, &aFindData);
+    }
     if (fh == INVALID_HANDLE_VALUE) {
        /* FindFirstFile() fails on empty drives! */
        if (GetLastError() == ERROR_FILE_NOT_FOUND)
@@ -712,11 +722,18 @@ win32_opendir(char *filename)
     /* now allocate the first part of the string table for
      * the filenames that we find.
      */
-    idx = strlen(FindData.cFileName)+1;
+    if (USING_WIDE()) {
+       W2AHELPER(wFindData.cFileName, buffer, sizeof(buffer), GETINTERPMODE());
+       ptr = buffer;
+    }
+    else {
+       ptr = aFindData.cFileName;
+    }
+    idx = strlen(ptr)+1;
     New(1304, p->start, idx, char);
     if (p->start == NULL)
        croak("opendir: malloc failed!\n");
-    strcpy(p->start, FindData.cFileName);
+    strcpy(p->start, ptr);
     p->nfiles++;
 
     /* loop finding all the files that match the wildcard
@@ -724,15 +741,21 @@ win32_opendir(char *filename)
      * the variable idx should point one past the null terminator
      * of the previous string found.
      */
-    while (FindNextFile(fh, &FindData)) {
-       len = strlen(FindData.cFileName);
+    while (USING_WIDE()
+           ? FindNextFileW(fh, &wFindData)
+           : FindNextFileA(fh, &aFindData)) {
+       if (USING_WIDE()) {
+           W2AHELPER(wFindData.cFileName, buffer, sizeof(buffer), GETINTERPMODE());
+       }
+       /* ptr is set above to the correct area */
+       len = strlen(ptr);
        /* bump the string table size by enough for the
         * new name and it's null terminator
         */
        Renew(p->start, idx+len+1, char);
        if (p->start == NULL)
            croak("opendir: malloc failed!\n");
-       strcpy(&p->start[idx], FindData.cFileName);
+       strcpy(&p->start[idx], ptr);
        p->nfiles++;
        idx += len+1;
     }
@@ -930,6 +953,7 @@ win32_stat(const char *path, struct stat *buffer)
     char       t[MAX_PATH+1]; 
     int                l = strlen(path);
     int                res;
+    WCHAR      wbuffer[MAX_PATH];
 
     if (l > 1) {
        switch(path[l - 1]) {
@@ -951,13 +975,25 @@ win32_stat(const char *path, struct stat *buffer)
            break;
        }
     }
-    res = stat(path,buffer);
+    if (USING_WIDE()) {
+       A2WHELPER(path, wbuffer, sizeof(wbuffer), GETINTERPMODE());
+       res = _wstat(wbuffer, (struct _stat *)buffer);
+    }
+    else {
+       res = stat(path, buffer);
+    }
     if (res < 0) {
        /* CRT is buggy on sharenames, so make sure it really isn't.
         * XXX using GetFileAttributesEx() will enable us to set
         * buffer->st_*time (but note that's not available on the
         * Windows of 1995) */
-       DWORD r = GetFileAttributes(path);
+       DWORD r;
+       if (USING_WIDE()) {
+           r = GetFileAttributesW(wbuffer);
+       }
+       else {
+           r = GetFileAttributesA(path);
+       }
        if (r != 0xffffffff && (r & FILE_ATTRIBUTE_DIRECTORY)) {
            /* buffer may still contain old garbage since stat() failed */
            Zero(buffer, 1, struct stat);
@@ -973,7 +1009,9 @@ win32_stat(const char *path, struct stat *buffer)
            && (path[2] == '\\' || path[2] == '/'))
        {
            /* The drive can be inaccessible, some _stat()s are buggy */
-           if (!GetVolumeInformation(path,NULL,0,NULL,NULL,NULL,NULL,0)) {
+           if (USING_WIDE()
+               ? !GetVolumeInformationW(wbuffer,NULL,0,NULL,NULL,NULL,NULL,0)
+               : !GetVolumeInformationA(path,NULL,0,NULL,NULL,NULL,NULL,0)) {
                errno = ENOENT;
                return -1;
            }
@@ -1083,19 +1121,46 @@ DllExport char *
 win32_getenv(const char *name)
 {
     static char *curitem = Nullch;     /* XXX threadead */
-    static DWORD curlen = 0;           /* XXX threadead */
+    static WCHAR *wCuritem = (WCHAR*)Nullch;   /* XXX threadead */
+    static DWORD curlen = 0, wCurlen = 0;/* XXX threadead */
+    WCHAR wBuffer[MAX_PATH];
     DWORD needlen;
+    if (USING_WIDE()) {
+       if (!wCuritem) {
+           wCurlen = 512;
+           New(1306,wCuritem,wCurlen,WCHAR);
+       }
+    }
     if (!curitem) {
        curlen = 512;
        New(1305,curitem,curlen,char);
     }
 
-    needlen = GetEnvironmentVariable(name,curitem,curlen);
+    if (USING_WIDE()) {
+       A2WHELPER(name, wBuffer, sizeof(wBuffer), GETINTERPMODE());
+       needlen = GetEnvironmentVariableW(wBuffer,wCuritem,wCurlen);
+    }
+    else
+       needlen = GetEnvironmentVariableA(name,curitem,curlen);
     if (needlen != 0) {
-       while (needlen > curlen) {
-           Renew(curitem,needlen,char);
-           curlen = needlen;
-           needlen = GetEnvironmentVariable(name,curitem,curlen);
+       if (USING_WIDE()) {
+           while (needlen > wCurlen) {
+               Renew(wCuritem,needlen,WCHAR);
+               wCurlen = needlen;
+               needlen = GetEnvironmentVariableW(wBuffer,wCuritem,wCurlen);
+           }
+           if (needlen > curlen) {
+               Renew(curitem,needlen,char);
+               curlen = needlen;
+           }
+           W2AHELPER(wCuritem, curitem, curlen, GETINTERPMODE());
+       }
+       else {
+           while (needlen > curlen) {
+               Renew(curitem,needlen,char);
+               curlen = needlen;
+               needlen = GetEnvironmentVariableA(name,curitem,curlen);
+           }
        }
     }
     else {
@@ -1124,31 +1189,47 @@ win32_putenv(const char *name)
 {
     char* curitem;
     char* val;
-    int relval = -1;
+    WCHAR* wCuritem;
+    WCHAR* wVal;
+    int length, relval = -1;
     if(name) {
-       New(1309,curitem,strlen(name)+1,char);
-       strcpy(curitem, name);
-       val = strchr(curitem, '=');
-       if(val) {
-           /* The sane way to deal with the environment.
-            * Has these advantages over putenv() & co.:
-            *  * enables us to store a truly empty value in the
-            *    environment (like in UNIX).
-            *  * we don't have to deal with RTL globals, bugs and leaks.
-            *  * Much faster.
-            * Why you may want to enable USE_WIN32_RTL_ENV:
-            *  * environ[] and RTL functions will not reflect changes,
-            *    which might be an issue if extensions want to access
-            *    the env. via RTL.  This cuts both ways, since RTL will
-            *    not see changes made by extensions that call the Win32
-            *    functions directly, either.
-            * GSAR 97-06-07
-            */
-           *val++ = '\0';
-           if(SetEnvironmentVariable(curitem, *val ? val : NULL))
-               relval = 0;
+       if (USING_WIDE()) {
+           length = strlen(name)+1;
+           New(1309,wCuritem,length,WCHAR);
+           A2WHELPER(name, wCuritem, length*2, GETINTERPMODE());
+           wVal = wcschr(wCuritem, '=');
+           if(wVal) {
+               *wVal++ = '\0';
+               if(SetEnvironmentVariableW(wCuritem, *wVal ? wVal : NULL))
+                   relval = 0;
+           }
+           Safefree(wCuritem);
+       }
+       else {
+           New(1309,curitem,strlen(name)+1,char);
+           strcpy(curitem, name);
+           val = strchr(curitem, '=');
+           if(val) {
+               /* The sane way to deal with the environment.
+                * Has these advantages over putenv() & co.:
+                *  * enables us to store a truly empty value in the
+                *    environment (like in UNIX).
+                *  * we don't have to deal with RTL globals, bugs and leaks.
+                *  * Much faster.
+                * Why you may want to enable USE_WIN32_RTL_ENV:
+                *  * environ[] and RTL functions will not reflect changes,
+                *    which might be an issue if extensions want to access
+                *    the env. via RTL.  This cuts both ways, since RTL will
+                *    not see changes made by extensions that call the Win32
+                *    functions directly, either.
+                * GSAR 97-06-07
+                */
+               *val++ = '\0';
+               if(SetEnvironmentVariableA(curitem, *val ? val : NULL))
+                   relval = 0;
+           }
+           Safefree(curitem);
        }
-       Safefree(curitem);
     }
     return relval;
 }
@@ -1220,8 +1301,16 @@ win32_utime(const char *filename, struct utimbuf *times)
     FILETIME ftAccess;
     FILETIME ftWrite;
     struct utimbuf TimeBuffer;
+    WCHAR wbuffer[MAX_PATH];
 
-    int rc = utime(filename,times);
+    int rc;
+    if (USING_WIDE()) {
+       A2WHELPER(filename, wbuffer, sizeof(wbuffer), GETINTERPMODE());
+       rc = _wutime(wbuffer, (struct _utimbuf*)times);
+    }
+    else {
+       rc = utime(filename, times);
+    }
     /* EACCES: path specifies directory or readonly file */
     if (rc == 0 || errno != EACCES /* || !IsWinNT() */)
        return rc;
@@ -1233,9 +1322,16 @@ win32_utime(const char *filename, struct utimbuf *times)
     }
 
     /* This will (and should) still fail on readonly files */
-    handle = CreateFile(filename, GENERIC_READ | GENERIC_WRITE,
-                       FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
-                       OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    if (USING_WIDE()) {
+       handle = CreateFileW(wbuffer, GENERIC_READ | GENERIC_WRITE,
+                           FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
+                           OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    }
+    else {
+       handle = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
+                           FILE_SHARE_READ | FILE_SHARE_DELETE, NULL,
+                           OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    }
     if (handle == INVALID_HANDLE_VALUE)
        return rc;
 
@@ -1770,11 +1866,20 @@ win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
     return fwrite(buf, size, count, fp);
 }
 
+#define MODE_SIZE 10
+
 DllExport FILE *
 win32_fopen(const char *filename, const char *mode)
 {
+    WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH];
     if (stricmp(filename, "/dev/null")==0)
-       return fopen("NUL", mode);
+       filename = "NUL";
+
+    if (USING_WIDE()) {
+       A2WHELPER(mode, wMode, sizeof(wMode), GETINTERPMODE());
+        A2WHELPER(filename, wBuffer, sizeof(wBuffer), GETINTERPMODE());
+       return _wfopen(wBuffer, wMode);
+    }
     return fopen(filename, mode);
 }
 
@@ -1784,16 +1889,28 @@ win32_fopen(const char *filename, const char *mode)
 #endif
 
 DllExport FILE *
-win32_fdopen( int handle, const char *mode)
+win32_fdopen(int handle, const char *mode)
 {
+    WCHAR wMode[MODE_SIZE];
+    if (USING_WIDE()) {
+       A2WHELPER(mode, wMode, sizeof(wMode), GETINTERPMODE());
+       return _wfdopen(handle, wMode);
+    }
     return fdopen(handle, (char *) mode);
 }
 
 DllExport FILE *
-win32_freopen( const char *path, const char *mode, FILE *stream)
+win32_freopen(const char *path, const char *mode, FILE *stream)
 {
+    WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH];
     if (stricmp(path, "/dev/null")==0)
-       return freopen("NUL", mode, stream);
+       path = "NUL";
+
+    if (USING_WIDE()) {
+       A2WHELPER(mode, wMode, sizeof(wMode), GETINTERPMODE());
+       A2WHELPER(path, wBuffer, sizeof(wBuffer), GETINTERPMODE());
+       return _wfreopen(wBuffer, wMode, stream);
+    }
     return freopen(path, mode, stream);
 }
 
@@ -2026,12 +2143,24 @@ win32_pclose(FILE *pf)
 DllExport int
 win32_rename(const char *oname, const char *newname)
 {
+    WCHAR wOldName[MAX_PATH];
+    WCHAR wNewName[MAX_PATH];
+    BOOL bResult;
     /* XXX despite what the documentation says about MoveFileEx(),
      * it doesn't work under Windows95!
      */
     if (IsWinNT()) {
-       if (!MoveFileEx(oname,newname,
-                       MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING)) {
+       if (USING_WIDE()) {
+           A2WHELPER(oname, wOldName, sizeof(wOldName), GETINTERPMODE());
+           A2WHELPER(newname, wNewName, sizeof(wNewName), GETINTERPMODE());
+           bResult = MoveFileExW(wOldName,wNewName,
+                       MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING);
+       }
+       else {
+           bResult = MoveFileExA(oname,newname,
+                       MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING);
+       }
+       if (!bResult) {
            DWORD err = GetLastError();
            switch (err) {
            case ERROR_BAD_NET_NAME:
@@ -2152,8 +2281,14 @@ win32_open(const char *path, int flag, ...)
     pmode = va_arg(ap, int);
     va_end(ap);
 
+    WCHAR wBuffer[MAX_PATH];
     if (stricmp(path, "/dev/null")==0)
-       return open("NUL", flag, pmode);
+       path = "NUL";
+
+    if (USING_WIDE()) {
+        A2WHELPER(path, wBuffer, sizeof(wBuffer), GETINTERPMODE());
+       return _wopen(wBuffer, flag, pmode);
+    }
     return open(path,flag,pmode);
 }
 
index bfca44a..a539a16 100644 (file)
@@ -392,5 +392,19 @@ struct thread_intern {
 #  endif /* !USE_DECLSPEC_THREAD */
 #endif /* USE_THREADS */
 
+/* UNICODE<>ANSI translation helpers */
+/* Use CP_ACP when mode is ANSI */
+/* Use CP_UTF8 when mode is UTF8 */
+
+#define A2WHELPER(lpa, lpw, nChars, acp)\
+    lpw[0] = 0, MultiByteToWideChar(acp, 0, lpa, -1, lpw, nChars)
+
+#define W2AHELPER(lpw, lpa, nChars, acp)\
+    lpa[0] = '\0', WideCharToMultiByte(acp, 0, lpw, -1, lpa, nChars, NULL, NULL)
+
+/* place holders for now */
+#define USING_WIDE() 0
+#define GETINTERPMODE() CP_ACP
+
 #endif /* _INC_WIN32_PERL5 */