Back to index

lightning-sunbird  0.9+nobinonly
w95io.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Masayuki Nakano <masayuki@d-toybox.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 /* Windows 95 IO module
00040  *
00041  * Assumes synchronous I/O.
00042  *
00043  */
00044 
00045 #include "primpl.h"
00046 #include <direct.h>
00047 #include <mbstring.h>
00048 #ifdef MOZ_UNICODE
00049 #include <wchar.h>
00050 #endif /* MOZ_UNICODE */
00051 
00052 
00053 struct _MDLock               _pr_ioq_lock;
00054 
00055 /*
00056  * NSPR-to-NT access right mapping table for files.
00057  */
00058 static DWORD fileAccessTable[] = {
00059     FILE_GENERIC_READ,
00060     FILE_GENERIC_WRITE,
00061     FILE_GENERIC_EXECUTE
00062 };
00063 
00064 /*
00065  * NSPR-to-NT access right mapping table for directories.
00066  */
00067 static DWORD dirAccessTable[] = {
00068     FILE_GENERIC_READ,
00069     FILE_GENERIC_WRITE|FILE_DELETE_CHILD,
00070     FILE_GENERIC_EXECUTE
00071 };
00072 
00073 /*
00074  * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
00075  * We store the value in a PRTime variable for convenience.
00076  * This constant is used by _PR_FileTimeToPRTime().
00077  */
00078 #if defined(__MINGW32__)
00079 static const PRTime _pr_filetime_offset = 116444736000000000LL;
00080 #else
00081 static const PRTime _pr_filetime_offset = 116444736000000000i64;
00082 #endif
00083 
00084 static void InitUnicodeSupport(void);
00085 
00086 static PRBool IsPrevCharSlash(const char *str, const char *current);
00087 
00088 void
00089 _PR_MD_INIT_IO()
00090 {
00091     WORD WSAVersion = 0x0101;
00092     WSADATA WSAData;
00093     int err;
00094 
00095     err = WSAStartup( WSAVersion, &WSAData );
00096     PR_ASSERT(0 == err);
00097 
00098 #ifdef DEBUG
00099     /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
00100     {
00101         SYSTEMTIME systime;
00102         union {
00103            PRTime prt;
00104            FILETIME ft;
00105         } filetime;
00106         BOOL rv;
00107 
00108         systime.wYear = 1970;
00109         systime.wMonth = 1;
00110         /* wDayOfWeek is ignored */
00111         systime.wDay = 1;
00112         systime.wHour = 0;
00113         systime.wMinute = 0;
00114         systime.wSecond = 0;
00115         systime.wMilliseconds = 0;
00116 
00117         rv = SystemTimeToFileTime(&systime, &filetime.ft);
00118         PR_ASSERT(0 != rv);
00119         PR_ASSERT(filetime.prt == _pr_filetime_offset);
00120     }
00121 #endif /* DEBUG */
00122 
00123     _PR_NT_InitSids();
00124 
00125     InitUnicodeSupport();
00126 
00127     _PR_MD_InitSockets();
00128 }
00129 
00130 PRStatus
00131 _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
00132 {
00133     DWORD rv;
00134 
00135     PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
00136         INFINITE : PR_IntervalToMilliseconds(ticks);
00137     rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
00138     switch(rv) 
00139     {
00140         case WAIT_OBJECT_0:
00141             return PR_SUCCESS;
00142             break;
00143         case WAIT_TIMEOUT:
00144             _PR_THREAD_LOCK(thread);
00145             if (thread->state == _PR_IO_WAIT) {
00146                        ;
00147             } else {
00148                 if (thread->wait.cvar != NULL) {
00149                     thread->wait.cvar = NULL;
00150                     _PR_THREAD_UNLOCK(thread);
00151                 } else {
00152                     /* The CVAR was notified just as the timeout
00153                      * occurred.  This led to us being notified twice.
00154                      * call WaitForSingleObject() to clear the semaphore.
00155                      */
00156                     _PR_THREAD_UNLOCK(thread);
00157                     rv = WaitForSingleObject(thread->md.blocked_sema, 0);
00158                     PR_ASSERT(rv == WAIT_OBJECT_0);
00159                 }
00160             }
00161             return PR_SUCCESS;
00162             break;
00163         default:
00164             return PR_FAILURE;
00165             break;
00166     }
00167 }
00168 PRStatus
00169 _PR_MD_WAKEUP_WAITER(PRThread *thread)
00170 {
00171     if ( _PR_IS_NATIVE_THREAD(thread) ) 
00172     {
00173         if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE)
00174             return PR_FAILURE;
00175         else
00176                      return PR_SUCCESS;
00177        }
00178 }
00179 
00180 
00181 /* --- FILE IO ----------------------------------------------------------- */
00182 /*
00183  *  _PR_MD_OPEN() -- Open a file
00184  *
00185  *  returns: a fileHandle
00186  *
00187  *  The NSPR open flags (osflags) are translated into flags for Win95
00188  *
00189  *  Mode seems to be passed in as a unix style file permissions argument
00190  *  as in 0666, in the case of opening the logFile. 
00191  *
00192  */
00193 PRInt32
00194 _PR_MD_OPEN(const char *name, PRIntn osflags, int mode)
00195 {
00196     HANDLE file;
00197     PRInt32 access = 0;
00198     PRInt32 flags = 0;
00199     PRInt32 flag6 = 0;
00200     
00201     if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
00202  
00203     if (osflags & PR_RDONLY || osflags & PR_RDWR)
00204         access |= GENERIC_READ;
00205     if (osflags & PR_WRONLY || osflags & PR_RDWR)
00206         access |= GENERIC_WRITE;
00207 
00208     if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
00209         flags = CREATE_NEW;
00210     else if (osflags & PR_CREATE_FILE) {
00211         if (osflags & PR_TRUNCATE)
00212             flags = CREATE_ALWAYS;
00213         else
00214             flags = OPEN_ALWAYS;
00215     } else {
00216         if (osflags & PR_TRUNCATE)
00217             flags = TRUNCATE_EXISTING;
00218         else
00219             flags = OPEN_EXISTING;
00220     }
00221 
00222     file = CreateFile(name,
00223                       access,
00224                       FILE_SHARE_READ|FILE_SHARE_WRITE,
00225                       NULL,
00226                       flags,
00227                       flag6,
00228                       NULL);
00229     if (file == INVALID_HANDLE_VALUE) {
00230               _PR_MD_MAP_OPEN_ERROR(GetLastError());
00231         return -1; 
00232        }
00233 
00234     return (PRInt32)file;
00235 }
00236 
00237 PRInt32
00238 _PR_MD_OPEN_FILE(const char *name, PRIntn osflags, int mode)
00239 {
00240     HANDLE file;
00241     PRInt32 access = 0;
00242     PRInt32 flags = 0;
00243     PRInt32 flag6 = 0;
00244     SECURITY_ATTRIBUTES sa;
00245     LPSECURITY_ATTRIBUTES lpSA = NULL;
00246     PSECURITY_DESCRIPTOR pSD = NULL;
00247     PACL pACL = NULL;
00248 
00249     if (osflags & PR_CREATE_FILE) {
00250         if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable,
00251                 &pSD, &pACL) == PR_SUCCESS) {
00252             sa.nLength = sizeof(sa);
00253             sa.lpSecurityDescriptor = pSD;
00254             sa.bInheritHandle = FALSE;
00255             lpSA = &sa;
00256         }
00257     }
00258     
00259     if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
00260  
00261     if (osflags & PR_RDONLY || osflags & PR_RDWR)
00262         access |= GENERIC_READ;
00263     if (osflags & PR_WRONLY || osflags & PR_RDWR)
00264         access |= GENERIC_WRITE;
00265 
00266     if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
00267         flags = CREATE_NEW;
00268     else if (osflags & PR_CREATE_FILE) {
00269         if (osflags & PR_TRUNCATE)
00270             flags = CREATE_ALWAYS;
00271         else
00272             flags = OPEN_ALWAYS;
00273     } else {
00274         if (osflags & PR_TRUNCATE)
00275             flags = TRUNCATE_EXISTING;
00276         else
00277             flags = OPEN_EXISTING;
00278     }
00279 
00280     file = CreateFile(name,
00281                       access,
00282                       FILE_SHARE_READ|FILE_SHARE_WRITE,
00283                       lpSA,
00284                       flags,
00285                       flag6,
00286                       NULL);
00287     if (lpSA != NULL) {
00288         _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
00289     }
00290     if (file == INVALID_HANDLE_VALUE) {
00291               _PR_MD_MAP_OPEN_ERROR(GetLastError());
00292         return -1; 
00293        }
00294 
00295     return (PRInt32)file;
00296 }
00297 
00298 PRInt32
00299 _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
00300 {
00301     PRUint32 bytes;
00302     int rv, err;
00303 
00304     rv = ReadFile((HANDLE)fd->secret->md.osfd,
00305             (LPVOID)buf,
00306             len,
00307             &bytes,
00308             NULL);
00309     
00310     if (rv == 0) 
00311     {
00312         err = GetLastError();
00313         /* ERROR_HANDLE_EOF can only be returned by async io */
00314         PR_ASSERT(err != ERROR_HANDLE_EOF);
00315         if (err == ERROR_BROKEN_PIPE)
00316             return 0;
00317               else {
00318                      _PR_MD_MAP_READ_ERROR(err);
00319         return -1;
00320     }
00321     }
00322     return bytes;
00323 }
00324 
00325 PRInt32
00326 _PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
00327 {
00328     PRInt32 f = fd->secret->md.osfd;
00329     PRInt32 bytes;
00330     int rv;
00331     PRThread *me = _PR_MD_CURRENT_THREAD();
00332     
00333     rv = WriteFile((HANDLE)f,
00334             buf,
00335             len,
00336             &bytes,
00337             NULL );
00338             
00339     if (rv == 0) 
00340     {
00341               _PR_MD_MAP_WRITE_ERROR(GetLastError());
00342         return -1;
00343     }
00344     return bytes;
00345 } /* --- end _PR_MD_WRITE() --- */
00346 
00347 PROffset32
00348 _PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence)
00349 {
00350     DWORD moveMethod;
00351     PROffset32 rv;
00352 
00353     switch (whence) {
00354         case PR_SEEK_SET:
00355             moveMethod = FILE_BEGIN;
00356             break;
00357         case PR_SEEK_CUR:
00358             moveMethod = FILE_CURRENT;
00359             break;
00360         case PR_SEEK_END:
00361             moveMethod = FILE_END;
00362             break;
00363         default:
00364             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
00365             return -1;
00366     }
00367 
00368     rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod);
00369 
00370     /*
00371      * If the lpDistanceToMoveHigh argument (third argument) is
00372      * NULL, SetFilePointer returns 0xffffffff on failure.
00373      */
00374     if (-1 == rv) {
00375         _PR_MD_MAP_LSEEK_ERROR(GetLastError());
00376     }
00377     return rv;
00378 }
00379 
00380 PROffset64
00381 _PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence)
00382 {
00383     DWORD moveMethod;
00384     LARGE_INTEGER li;
00385     DWORD err;
00386 
00387     switch (whence) {
00388         case PR_SEEK_SET:
00389             moveMethod = FILE_BEGIN;
00390             break;
00391         case PR_SEEK_CUR:
00392             moveMethod = FILE_CURRENT;
00393             break;
00394         case PR_SEEK_END:
00395             moveMethod = FILE_END;
00396             break;
00397         default:
00398             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
00399             return -1;
00400     }
00401 
00402     li.QuadPart = offset;
00403     li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd,
00404             li.LowPart, &li.HighPart, moveMethod);
00405 
00406     if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) {
00407         _PR_MD_MAP_LSEEK_ERROR(err);
00408         li.QuadPart = -1;
00409     }
00410     return li.QuadPart;
00411 }
00412 
00413 /*
00414  * This is documented to succeed on read-only files, but Win32's
00415  * FlushFileBuffers functions fails with "access denied" in such a
00416  * case.  So we only signal an error if the error is *not* "access
00417  * denied".
00418  */
00419 PRInt32
00420 _PR_MD_FSYNC(PRFileDesc *fd)
00421 {
00422     /*
00423      * From the documentation:
00424      *
00425      *    On Windows NT, the function FlushFileBuffers fails if hFile
00426      *    is a handle to console output. That is because console
00427      *    output is not buffered. The function returns FALSE, and
00428      *    GetLastError returns ERROR_INVALID_HANDLE.
00429      *
00430      * On the other hand, on Win95, it returns without error.  I cannot
00431      * assume that 0, 1, and 2 are console, because if someone closes
00432      * System.out and then opens a file, they might get file descriptor
00433      * 1.  An error on *that* version of 1 should be reported, whereas
00434      * an error on System.out (which was the original 1) should be
00435      * ignored.  So I use isatty() to ensure that such an error was due
00436      * to this bogosity, and if it was, I ignore the error.
00437      */
00438 
00439     BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);
00440 
00441     if (!ok) {
00442        DWORD err = GetLastError();
00443        if (err != ERROR_ACCESS_DENIED) {  // from winerror.h
00444                      _PR_MD_MAP_FSYNC_ERROR(err);
00445            return -1;
00446        }
00447     }
00448     return 0;
00449 }
00450 
00451 PRInt32
00452 _MD_CloseFile(PRInt32 osfd)
00453 {
00454     PRInt32 rv;
00455     
00456     rv = (CloseHandle((HANDLE)osfd))?0:-1;
00457        if (rv == -1)
00458               _PR_MD_MAP_CLOSE_ERROR(GetLastError());
00459     return rv;
00460 }
00461 
00462 
00463 /* --- DIR IO ------------------------------------------------------------ */
00464 #define GetFileFromDIR(d)       (d)->d_entry.cFileName
00465 #define FileIsHidden(d)     ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
00466 
00467 void FlipSlashes(char *cp, int len)
00468 {
00469     while (--len >= 0) {
00470         if (cp[0] == '/') {
00471             cp[0] = PR_DIRECTORY_SEPARATOR;
00472         }
00473         cp = _mbsinc(cp);
00474     }
00475 } /* end FlipSlashes() */
00476 
00477 
00478 /*
00479 **
00480 ** Local implementations of standard Unix RTL functions which are not provided
00481 ** by the VC RTL.
00482 **
00483 */
00484 
00485 PRStatus
00486 _PR_MD_CLOSE_DIR(_MDDir *d)
00487 {
00488     if ( d ) {
00489         if (FindClose(d->d_hdl)) {
00490         d->magic = (PRUint32)-1;
00491         return PR_SUCCESS;
00492               } else {
00493                      _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
00494               return PR_FAILURE;
00495               }
00496     }
00497     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
00498     return PR_FAILURE;
00499 }
00500 
00501 
00502 PRStatus
00503 _PR_MD_OPEN_DIR(_MDDir *d, const char *name)
00504 {
00505     char filename[ MAX_PATH ];
00506     int len;
00507 
00508     len = strlen(name);
00509     /* Need 5 bytes for \*.* and the trailing null byte. */
00510     if (len + 5 > MAX_PATH) {
00511         PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
00512         return PR_FAILURE;
00513     }
00514     strcpy(filename, name);
00515 
00516     /*
00517      * If 'name' ends in a slash or backslash, do not append
00518      * another backslash.
00519      */
00520     if (IsPrevCharSlash(filename, filename + len)) {
00521         len--;
00522     }
00523     strcpy(&filename[len], "\\*.*");
00524     FlipSlashes( filename, strlen(filename) );
00525 
00526     d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
00527     if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
00528               _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
00529         return PR_FAILURE;
00530     }
00531     d->firstEntry = PR_TRUE;
00532     d->magic = _MD_MAGIC_DIR;
00533     return PR_SUCCESS;
00534 }
00535 
00536 char *
00537 _PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
00538 {
00539     PRInt32 err;
00540     BOOL rv;
00541     char *fileName;
00542 
00543     if ( d ) {
00544         while (1) {
00545             if (d->firstEntry) {
00546                 d->firstEntry = PR_FALSE;
00547                 rv = 1;
00548             } else {
00549                 rv = FindNextFile(d->d_hdl, &(d->d_entry));
00550             }
00551             if (rv == 0) {
00552                 break;
00553             }
00554             fileName = GetFileFromDIR(d);
00555             if ( (flags & PR_SKIP_DOT) &&
00556                  (fileName[0] == '.') && (fileName[1] == '\0'))
00557                  continue;
00558             if ( (flags & PR_SKIP_DOT_DOT) &&
00559                  (fileName[0] == '.') && (fileName[1] == '.') &&
00560                  (fileName[2] == '\0'))
00561                  continue;
00562             if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d))
00563                  continue;
00564             return fileName;
00565         }
00566         err = GetLastError();
00567         PR_ASSERT(NO_ERROR != err);
00568                      _PR_MD_MAP_READDIR_ERROR(err);
00569         return NULL;
00570               }
00571     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
00572     return NULL;
00573 }
00574 
00575 PRInt32
00576 _PR_MD_DELETE(const char *name)
00577 {
00578     if (DeleteFile(name)) {
00579         return 0;
00580     } else {
00581               _PR_MD_MAP_DELETE_ERROR(GetLastError());
00582         return -1;
00583     }
00584 }
00585 
00586 void
00587 _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
00588 {
00589     PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
00590     CopyMemory(prtm, filetime, sizeof(PRTime));
00591 #if defined(__MINGW32__)
00592     *prtm = (*prtm - _pr_filetime_offset) / 10LL;
00593 #else
00594     *prtm = (*prtm - _pr_filetime_offset) / 10i64;
00595 #endif
00596 
00597 #ifdef DEBUG
00598     /* Doublecheck our calculation. */
00599     {
00600         SYSTEMTIME systime;
00601         PRExplodedTime etm;
00602         PRTime cmp; /* for comparison */
00603         BOOL rv;
00604 
00605         rv = FileTimeToSystemTime(filetime, &systime);
00606         PR_ASSERT(0 != rv);
00607 
00608         /*
00609          * PR_ImplodeTime ignores wday and yday.
00610          */
00611         etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
00612         etm.tm_sec = systime.wSecond;
00613         etm.tm_min = systime.wMinute;
00614         etm.tm_hour = systime.wHour;
00615         etm.tm_mday = systime.wDay;
00616         etm.tm_month = systime.wMonth - 1;
00617         etm.tm_year = systime.wYear;
00618         /*
00619          * It is not well-documented what time zone the FILETIME's
00620          * are in.  WIN32_FIND_DATA is documented to be in UTC (GMT).
00621          * But BY_HANDLE_FILE_INFORMATION is unclear about this.
00622          * By our best judgement, we assume that FILETIME is in UTC.
00623          */
00624         etm.tm_params.tp_gmt_offset = 0;
00625         etm.tm_params.tp_dst_offset = 0;
00626         cmp = PR_ImplodeTime(&etm);
00627 
00628         /*
00629          * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
00630          * microseconds to milliseconds before doing the comparison.
00631          */
00632         PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
00633     }
00634 #endif /* DEBUG */
00635 }
00636 
00637 PRInt32
00638 _PR_MD_STAT(const char *fn, struct stat *info)
00639 {
00640     PRInt32 rv;
00641 
00642     rv = _stat(fn, (struct _stat *)info);
00643     if (-1 == rv) {
00644         /*
00645          * Check for MSVC runtime library _stat() bug.
00646          * (It's really a bug in FindFirstFile().)
00647          * If a pathname ends in a backslash or slash,
00648          * e.g., c:\temp\ or c:/temp/, _stat() will fail.
00649          * Note: a pathname ending in a slash (e.g., c:/temp/)
00650          * can be handled by _stat() on NT but not on Win95.
00651          *
00652          * We remove the backslash or slash at the end and
00653          * try again.
00654          */
00655 
00656         int len = strlen(fn);
00657         if (len > 0 && len <= _MAX_PATH
00658                 && IsPrevCharSlash(fn, fn + len)) {
00659             char newfn[_MAX_PATH + 1];
00660 
00661             strcpy(newfn, fn);
00662             newfn[len - 1] = '\0';
00663             rv = _stat(newfn, (struct _stat *)info);
00664         }
00665     }
00666 
00667     if (-1 == rv) {
00668         _PR_MD_MAP_STAT_ERROR(errno);
00669     }
00670     return rv;
00671 }
00672 
00673 #define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
00674 
00675 static PRBool
00676 IsPrevCharSlash(const char *str, const char *current)
00677 {
00678     const char *prev;
00679 
00680     if (str >= current)
00681         return PR_FALSE;
00682     prev = _mbsdec(str, current);
00683     return (prev == current - 1) && _PR_IS_SLASH(*prev);
00684 }
00685 
00686 /*
00687  * IsRootDirectory --
00688  *
00689  * Return PR_TRUE if the pathname 'fn' is a valid root directory,
00690  * else return PR_FALSE.  The char buffer pointed to by 'fn' must
00691  * be writable.  During the execution of this function, the contents
00692  * of the buffer pointed to by 'fn' may be modified, but on return
00693  * the original contents will be restored.  'buflen' is the size of
00694  * the buffer pointed to by 'fn'.
00695  *
00696  * Root directories come in three formats:
00697  * 1. / or \, meaning the root directory of the current drive.
00698  * 2. C:/ or C:\, where C is a drive letter.
00699  * 3. \<server name><share point name>\ or
00700  *    \<server name><share point name>, meaning the root directory
00701  *    of a UNC (Universal Naming Convention) name.
00702  */
00703 
00704 static PRBool
00705 IsRootDirectory(char *fn, size_t buflen)
00706 {
00707     char *p;
00708     PRBool slashAdded = PR_FALSE;
00709     PRBool rv = PR_FALSE;
00710 
00711     if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
00712         return PR_TRUE;
00713     }
00714 
00715     if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
00716             && fn[3] == '\0') {
00717         rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
00718         return rv;
00719     }
00720 
00721     /* The UNC root directory */
00722 
00723     if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
00724         /* The 'server' part should have at least one character. */
00725         p = &fn[2];
00726         if (*p == '\0' || _PR_IS_SLASH(*p)) {
00727             return PR_FALSE;
00728         }
00729 
00730         /* look for the next slash */
00731         do {
00732             p = _mbsinc(p);
00733         } while (*p != '\0' && !_PR_IS_SLASH(*p));
00734         if (*p == '\0') {
00735             return PR_FALSE;
00736         }
00737 
00738         /* The 'share' part should have at least one character. */
00739         p++;
00740         if (*p == '\0' || _PR_IS_SLASH(*p)) {
00741             return PR_FALSE;
00742         }
00743 
00744         /* look for the final slash */
00745         do {
00746             p = _mbsinc(p);
00747         } while (*p != '\0' && !_PR_IS_SLASH(*p));
00748         if (_PR_IS_SLASH(*p) && p[1] != '\0') {
00749             return PR_FALSE;
00750         }
00751         if (*p == '\0') {
00752             /*
00753              * GetDriveType() doesn't work correctly if the
00754              * path is of the form \\server\share, so we add
00755              * a final slash temporarily.
00756              */
00757             if ((p + 1) < (fn + buflen)) {
00758                 *p++ = '\\';
00759                 *p = '\0';
00760                 slashAdded = PR_TRUE;
00761             } else {
00762                 return PR_FALSE; /* name too long */
00763             }
00764         }
00765         rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
00766         /* restore the 'fn' buffer */
00767         if (slashAdded) {
00768             *--p = '\0';
00769         }
00770     }
00771     return rv;
00772 }
00773 
00774 PRInt32
00775 _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
00776 {
00777     HANDLE hFindFile;
00778     WIN32_FIND_DATA findFileData;
00779     char pathbuf[MAX_PATH + 1];
00780     
00781     if (NULL == fn || '\0' == *fn) {
00782         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
00783         return -1;
00784     }
00785 
00786     /*
00787      * FindFirstFile() expands wildcard characters.  So
00788      * we make sure the pathname contains no wildcard.
00789      */
00790     if (NULL != _mbspbrk(fn, "?*")) {
00791         PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
00792         return -1;
00793     }
00794 
00795     hFindFile = FindFirstFile(fn, &findFileData);
00796     if (INVALID_HANDLE_VALUE == hFindFile) {
00797         DWORD len;
00798         char *filePart;
00799 
00800         /*
00801          * FindFirstFile() does not work correctly on root directories.
00802          * It also doesn't work correctly on a pathname that ends in a
00803          * slash.  So we first check to see if the pathname specifies a
00804          * root directory.  If not, and if the pathname ends in a slash,
00805          * we remove the final slash and try again.
00806          */
00807 
00808         /*
00809          * If the pathname does not contain ., \, and /, it cannot be
00810          * a root directory or a pathname that ends in a slash.
00811          */
00812         if (NULL == _mbspbrk(fn, ".\\/")) {
00813             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
00814             return -1;
00815         } 
00816         len = GetFullPathName(fn, sizeof(pathbuf), pathbuf,
00817                 &filePart);
00818         if (0 == len) {
00819             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
00820             return -1;
00821         }
00822         if (len > sizeof(pathbuf)) {
00823             PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
00824             return -1;
00825         }
00826         if (IsRootDirectory(pathbuf, sizeof(pathbuf))) {
00827             info->type = PR_FILE_DIRECTORY;
00828             info->size = 0;
00829             /*
00830              * These timestamps don't make sense for root directories.
00831              */
00832             info->modifyTime = 0;
00833             info->creationTime = 0;
00834             return 0;
00835         }
00836         if (!IsPrevCharSlash(pathbuf, pathbuf + len)) {
00837             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
00838             return -1;
00839         } else {
00840             pathbuf[len - 1] = '\0';
00841             hFindFile = FindFirstFile(pathbuf, &findFileData);
00842             if (INVALID_HANDLE_VALUE == hFindFile) {
00843                 _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
00844                 return -1;
00845             }
00846         }
00847     }
00848 
00849     FindClose(hFindFile);
00850 
00851     if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
00852         info->type = PR_FILE_DIRECTORY;
00853     } else {
00854         info->type = PR_FILE_FILE;
00855     }
00856 
00857     info->size = findFileData.nFileSizeHigh;
00858     info->size = (info->size << 32) + findFileData.nFileSizeLow;
00859 
00860     _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
00861 
00862     if (0 == findFileData.ftCreationTime.dwLowDateTime &&
00863             0 == findFileData.ftCreationTime.dwHighDateTime) {
00864         info->creationTime = info->modifyTime;
00865     } else {
00866         _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
00867                 &info->creationTime);
00868     }
00869 
00870     return 0;
00871 }
00872 
00873 PRInt32
00874 _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
00875 {
00876     PRFileInfo64 info64;
00877     PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
00878     if (0 == rv)
00879     {
00880         info->type = info64.type;
00881         info->size = (PRUint32) info64.size;
00882         info->modifyTime = info64.modifyTime;
00883         info->creationTime = info64.creationTime;
00884     }
00885     return rv;
00886 }
00887 
00888 PRInt32
00889 _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
00890 {
00891     int rv;
00892 
00893     BY_HANDLE_FILE_INFORMATION hinfo;
00894 
00895     rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
00896     if (rv == FALSE) {
00897               _PR_MD_MAP_FSTAT_ERROR(GetLastError());
00898         return -1;
00899        }
00900 
00901     if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
00902         info->type = PR_FILE_DIRECTORY;
00903     else
00904         info->type = PR_FILE_FILE;
00905 
00906     info->size = hinfo.nFileSizeHigh;
00907     info->size = (info->size << 32) + hinfo.nFileSizeLow;
00908 
00909     _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
00910     _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
00911 
00912     return 0;
00913 }
00914 
00915 PRInt32
00916 _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
00917 {
00918     PRFileInfo64 info64;
00919     int rv = _PR_MD_GETOPENFILEINFO64(fd, &info64);
00920     if (0 == rv)
00921     {
00922         info->type = info64.type;
00923         info->modifyTime = info64.modifyTime;
00924         info->creationTime = info64.creationTime;
00925         LL_L2I(info->size, info64.size);
00926     }
00927     return rv;
00928 }
00929 
00930 PRStatus
00931 _PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable)
00932 {
00933     BOOL rv;
00934 
00935     /*
00936      * The SetHandleInformation function fails with the
00937      * ERROR_CALL_NOT_IMPLEMENTED error on Win95.
00938      */
00939     rv = SetHandleInformation(
00940             (HANDLE)fd->secret->md.osfd,
00941             HANDLE_FLAG_INHERIT,
00942             inheritable ? HANDLE_FLAG_INHERIT : 0);
00943     if (0 == rv) {
00944         _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
00945         return PR_FAILURE;
00946     }
00947     return PR_SUCCESS;
00948 } 
00949 
00950 void
00951 _PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported)
00952 {
00953     if (imported) {
00954         fd->secret->inheritable = _PR_TRI_UNKNOWN;
00955     } else {
00956         fd->secret->inheritable = _PR_TRI_FALSE;
00957     }
00958 }
00959 
00960 void
00961 _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd)
00962 {
00963     DWORD flags;
00964 
00965     PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
00966     if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) {
00967         if (flags & HANDLE_FLAG_INHERIT) {
00968             fd->secret->inheritable = _PR_TRI_TRUE;
00969         } else {
00970             fd->secret->inheritable = _PR_TRI_FALSE;
00971         }
00972     }
00973 }
00974 
00975 PRInt32
00976 _PR_MD_RENAME(const char *from, const char *to)
00977 {
00978     /* Does this work with dot-relative pathnames? */
00979     if (MoveFile(from, to)) {
00980         return 0;
00981     } else {
00982               _PR_MD_MAP_RENAME_ERROR(GetLastError());
00983         return -1;
00984     }
00985 }
00986 
00987 PRInt32
00988 _PR_MD_ACCESS(const char *name, PRAccessHow how)
00989 {
00990 PRInt32 rv;
00991     switch (how) {
00992       case PR_ACCESS_WRITE_OK:
00993         rv = _access(name, 02);
00994               break;
00995       case PR_ACCESS_READ_OK:
00996         rv = _access(name, 04);
00997               break;
00998       case PR_ACCESS_EXISTS:
00999         return _access(name, 00);
01000               break;
01001       default:
01002               PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
01003               return -1;
01004     }
01005        if (rv < 0)
01006               _PR_MD_MAP_ACCESS_ERROR(errno);
01007     return rv;
01008 }
01009 
01010 PRInt32
01011 _PR_MD_MKDIR(const char *name, PRIntn mode)
01012 {
01013     /* XXXMB - how to translate the "mode"??? */
01014     if (CreateDirectory(name, NULL)) {
01015         return 0;
01016     } else {
01017               _PR_MD_MAP_MKDIR_ERROR(GetLastError());
01018         return -1;
01019     }
01020 }
01021 
01022 PRInt32
01023 _PR_MD_MAKE_DIR(const char *name, PRIntn mode)
01024 {
01025     BOOL rv;
01026     SECURITY_ATTRIBUTES sa;
01027     LPSECURITY_ATTRIBUTES lpSA = NULL;
01028     PSECURITY_DESCRIPTOR pSD = NULL;
01029     PACL pACL = NULL;
01030 
01031     if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable,
01032             &pSD, &pACL) == PR_SUCCESS) {
01033         sa.nLength = sizeof(sa);
01034         sa.lpSecurityDescriptor = pSD;
01035         sa.bInheritHandle = FALSE;
01036         lpSA = &sa;
01037     }
01038     rv = CreateDirectory(name, lpSA);
01039     if (lpSA != NULL) {
01040         _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
01041     }
01042     if (rv) {
01043         return 0;
01044     } else {
01045         _PR_MD_MAP_MKDIR_ERROR(GetLastError());
01046         return -1;
01047     }
01048 }
01049 
01050 PRInt32
01051 _PR_MD_RMDIR(const char *name)
01052 {
01053     if (RemoveDirectory(name)) {
01054         return 0;
01055     } else {
01056               _PR_MD_MAP_RMDIR_ERROR(GetLastError());
01057         return -1;
01058     }
01059 }
01060 
01061 PRStatus
01062 _PR_MD_LOCKFILE(PRInt32 f)
01063 {
01064     PRStatus  rc = PR_SUCCESS;
01065        DWORD     rv;
01066 
01067        rv = LockFile( (HANDLE)f,
01068               0l, 0l,
01069               0x0l, 0xffffffffl ); 
01070        if ( rv == 0 ) {
01071         DWORD rc = GetLastError();
01072         PR_LOG( _pr_io_lm, PR_LOG_ERROR,
01073             ("_PR_MD_LOCKFILE() failed. Error: %d", rc ));
01074         rc = PR_FAILURE;
01075     }
01076 
01077     return rc;
01078 } /* end _PR_MD_LOCKFILE() */
01079 
01080 PRStatus
01081 _PR_MD_TLOCKFILE(PRInt32 f)
01082 {
01083     PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 );
01084     return PR_FAILURE;
01085 } /* end _PR_MD_TLOCKFILE() */
01086 
01087 
01088 PRStatus
01089 _PR_MD_UNLOCKFILE(PRInt32 f)
01090 {
01091        PRInt32   rv;
01092     
01093     rv = UnlockFile( (HANDLE) f,
01094               0l, 0l,
01095             0x0l, 0xffffffffl ); 
01096             
01097     if ( rv )
01098     {
01099        return PR_SUCCESS;
01100     }
01101     else
01102     {
01103               _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
01104               return PR_FAILURE;
01105     }
01106 } /* end _PR_MD_UNLOCKFILE() */
01107 
01108 PRInt32
01109 _PR_MD_PIPEAVAILABLE(PRFileDesc *fd)
01110 {
01111     if (NULL == fd)
01112               PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
01113        else
01114               PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
01115     return -1;
01116 }
01117 
01118 #ifdef MOZ_UNICODE
01119 
01120 typedef HANDLE (WINAPI *CreateFileWFn) (LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
01121 static CreateFileWFn createFileW = CreateFileW;
01122 typedef HANDLE (WINAPI *FindFirstFileWFn) (LPCWSTR, LPWIN32_FIND_DATAW);
01123 static FindFirstFileWFn findFirstFileW = FindFirstFileW;
01124 typedef BOOL (WINAPI *FindNextFileWFn) (HANDLE, LPWIN32_FIND_DATAW);
01125 static FindNextFileWFn findNextFileW = FindNextFileW;
01126 typedef DWORD (WINAPI *GetFullPathNameWFn) (LPCWSTR, DWORD, LPWSTR, LPWSTR *);
01127 static GetFullPathNameWFn getFullPathNameW = GetFullPathNameW;
01128 typedef UINT (WINAPI *GetDriveTypeWFn) (LPCWSTR);
01129 static GetDriveTypeWFn getDriveTypeW = GetDriveTypeW;
01130 
01131 #endif /* MOZ_UNICODE */
01132 
01133 PRBool _pr_useUnicode = PR_FALSE;
01134 
01135 static void InitUnicodeSupport(void)
01136 {
01137     /*
01138      * The W functions exist on Win9x as stubs that fail with the
01139      * ERROR_CALL_NOT_IMPLEMENTED error.  We plan to emulate the
01140      * MSLU W functions on Win9x in the future.
01141      */
01142 
01143     /* Find out if we are running on a Unicode enabled version of Windows */
01144     OSVERSIONINFOA osvi = {0};
01145 
01146     osvi.dwOSVersionInfoSize = sizeof(osvi);
01147     if (GetVersionExA(&osvi)) {
01148         _pr_useUnicode = (osvi.dwPlatformId >= VER_PLATFORM_WIN32_NT);
01149     } else {
01150         _pr_useUnicode = PR_FALSE;
01151     }
01152 #ifdef DEBUG
01153     /*
01154      * In debug builds, allow explicit use of ANSI methods to simulate
01155      * a Win9x environment for testing purposes.
01156      */
01157     if (getenv("WINAPI_USE_ANSI"))
01158         _pr_useUnicode = PR_FALSE;
01159 #endif
01160 }
01161 
01162 #ifdef MOZ_UNICODE
01163 
01164 /* ================ UTF16 Interfaces ================================ */
01165 void FlipSlashesW(PRUnichar *cp, int len)
01166 {
01167     while (--len >= 0) {
01168         if (cp[0] == L'/') {
01169             cp[0] = L'\\';
01170         }
01171         cp++;
01172     }
01173 } /* end FlipSlashesW() */
01174 
01175 PRInt32
01176 _PR_MD_OPEN_FILE_UTF16(const PRUnichar *name, PRIntn osflags, int mode)
01177 {
01178     HANDLE file;
01179     PRInt32 access = 0;
01180     PRInt32 flags = 0;
01181     PRInt32 flag6 = 0;
01182     SECURITY_ATTRIBUTES sa;
01183     LPSECURITY_ATTRIBUTES lpSA = NULL;
01184     PSECURITY_DESCRIPTOR pSD = NULL;
01185     PACL pACL = NULL;
01186 
01187     if (osflags & PR_CREATE_FILE) {
01188         if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable,
01189                 &pSD, &pACL) == PR_SUCCESS) {
01190             sa.nLength = sizeof(sa);
01191             sa.lpSecurityDescriptor = pSD;
01192             sa.bInheritHandle = FALSE;
01193             lpSA = &sa;
01194         }
01195     }
01196 
01197     if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
01198 
01199     if (osflags & PR_RDONLY || osflags & PR_RDWR)
01200         access |= GENERIC_READ;
01201     if (osflags & PR_WRONLY || osflags & PR_RDWR)
01202         access |= GENERIC_WRITE;
01203  
01204     if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
01205         flags = CREATE_NEW;
01206     else if (osflags & PR_CREATE_FILE) {
01207         if (osflags & PR_TRUNCATE)
01208             flags = CREATE_ALWAYS;
01209         else
01210             flags = OPEN_ALWAYS;
01211     } else {
01212         if (osflags & PR_TRUNCATE)
01213             flags = TRUNCATE_EXISTING;
01214         else
01215             flags = OPEN_EXISTING;
01216     }
01217 
01218     file = createFileW(name,
01219                        access,
01220                        FILE_SHARE_READ|FILE_SHARE_WRITE,
01221                        lpSA,
01222                        flags,
01223                        flag6,
01224                        NULL);
01225     if (lpSA != NULL) {
01226         _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
01227     }
01228     if (file == INVALID_HANDLE_VALUE) {
01229         _PR_MD_MAP_OPEN_ERROR(GetLastError());
01230         return -1;
01231     }
01232  
01233     return (PRInt32)file;
01234 }
01235  
01236 PRStatus
01237 _PR_MD_OPEN_DIR_UTF16(_MDDirUTF16 *d, const PRUnichar *name)
01238 {
01239     PRUnichar filename[ MAX_PATH ];
01240     int len;
01241 
01242     len = wcslen(name);
01243     /* Need 5 bytes for \*.* and the trailing null byte. */
01244     if (len + 5 > MAX_PATH) {
01245         PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
01246         return PR_FAILURE;
01247     }
01248     wcscpy(filename, name);
01249 
01250     /*
01251      * If 'name' ends in a slash or backslash, do not append
01252      * another backslash.
01253      */
01254     if (filename[len - 1] == L'/' || filename[len - 1] == L'\\') {
01255         len--;
01256     }
01257     wcscpy(&filename[len], L"\\*.*");
01258     FlipSlashesW( filename, wcslen(filename) );
01259 
01260     d->d_hdl = findFirstFileW( filename, &(d->d_entry) );
01261     if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
01262         _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
01263         return PR_FAILURE;
01264     }
01265     d->firstEntry = PR_TRUE;
01266     d->magic = _MD_MAGIC_DIR;
01267     return PR_SUCCESS;
01268 }
01269 
01270 PRUnichar *
01271 _PR_MD_READ_DIR_UTF16(_MDDirUTF16 *d, PRIntn flags)
01272 {
01273     PRInt32 err;
01274     BOOL rv;
01275     PRUnichar *fileName;
01276 
01277     if ( d ) {
01278         while (1) {
01279             if (d->firstEntry) {
01280                 d->firstEntry = PR_FALSE;
01281                 rv = 1;
01282             } else {
01283                 rv = findNextFileW(d->d_hdl, &(d->d_entry));
01284             }
01285             if (rv == 0) {
01286                 break;
01287             }
01288             fileName = GetFileFromDIR(d);
01289             if ( (flags & PR_SKIP_DOT) &&
01290                  (fileName[0] == L'.') && (fileName[1] == L'\0'))
01291                 continue;
01292             if ( (flags & PR_SKIP_DOT_DOT) &&
01293                  (fileName[0] == L'.') && (fileName[1] == L'.') &&
01294                  (fileName[2] == L'\0'))
01295                 continue;
01296             if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d))
01297                 continue;
01298             return fileName;
01299         }
01300         err = GetLastError();
01301         PR_ASSERT(NO_ERROR != err);
01302         _PR_MD_MAP_READDIR_ERROR(err);
01303         return NULL;
01304     }
01305     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
01306     return NULL;
01307 }
01308  
01309 PRStatus
01310 _PR_MD_CLOSE_DIR_UTF16(_MDDirUTF16 *d)
01311 {
01312     if ( d ) {
01313         if (FindClose(d->d_hdl)) {
01314             d->magic = (PRUint32)-1;
01315             return PR_SUCCESS;
01316         } else {
01317             _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
01318             return PR_FAILURE;
01319         }
01320     }
01321     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
01322     return PR_FAILURE;
01323 }
01324 
01325 #define _PR_IS_W_SLASH(ch) ((ch) == L'/' || (ch) == L'\\')
01326 
01327 /*
01328  * IsRootDirectoryW --
01329  *
01330  * Return PR_TRUE if the pathname 'fn' is a valid root directory,
01331  * else return PR_FALSE.  The PRUnichar buffer pointed to by 'fn' must
01332  * be writable.  During the execution of this function, the contents
01333  * of the buffer pointed to by 'fn' may be modified, but on return
01334  * the original contents will be restored.  'buflen' is the size of
01335  * the buffer pointed to by 'fn', in PRUnichars.
01336  *
01337  * Root directories come in three formats:
01338  * 1. / or \, meaning the root directory of the current drive.
01339  * 2. C:/ or C:\, where C is a drive letter.
01340  * 3. \<server name><share point name>\ or
01341  *    \<server name><share point name>, meaning the root directory
01342  *    of a UNC (Universal Naming Convention) name.
01343  */
01344 
01345 static PRBool
01346 IsRootDirectoryW(PRUnichar *fn, size_t buflen)
01347 {
01348     PRUnichar *p;
01349     PRBool slashAdded = PR_FALSE;
01350     PRBool rv = PR_FALSE;
01351 
01352     if (_PR_IS_W_SLASH(fn[0]) && fn[1] == L'\0') {
01353         return PR_TRUE;
01354     }
01355 
01356     if (iswalpha(fn[0]) && fn[1] == L':' && _PR_IS_W_SLASH(fn[2])
01357             && fn[3] == L'\0') {
01358         rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE;
01359         return rv;
01360     }
01361 
01362     /* The UNC root directory */
01363 
01364     if (_PR_IS_W_SLASH(fn[0]) && _PR_IS_W_SLASH(fn[1])) {
01365         /* The 'server' part should have at least one character. */
01366         p = &fn[2];
01367         if (*p == L'\0' || _PR_IS_W_SLASH(*p)) {
01368             return PR_FALSE;
01369         }
01370 
01371         /* look for the next slash */
01372         do {
01373             p++;
01374         } while (*p != L'\0' && !_PR_IS_W_SLASH(*p));
01375         if (*p == L'\0') {
01376             return PR_FALSE;
01377         }
01378 
01379         /* The 'share' part should have at least one character. */
01380         p++;
01381         if (*p == L'\0' || _PR_IS_W_SLASH(*p)) {
01382             return PR_FALSE;
01383         }
01384 
01385         /* look for the final slash */
01386         do {
01387             p++;
01388         } while (*p != L'\0' && !_PR_IS_W_SLASH(*p));
01389         if (_PR_IS_W_SLASH(*p) && p[1] != L'\0') {
01390             return PR_FALSE;
01391         }
01392         if (*p == L'\0') {
01393             /*
01394              * GetDriveType() doesn't work correctly if the
01395              * path is of the form \\server\share, so we add
01396              * a final slash temporarily.
01397              */
01398             if ((p + 1) < (fn + buflen)) {
01399                 *p++ = L'\\';
01400                 *p = L'\0';
01401                 slashAdded = PR_TRUE;
01402             } else {
01403                 return PR_FALSE; /* name too long */
01404             }
01405         }
01406         rv = getDriveTypeW(fn) > 1 ? PR_TRUE : PR_FALSE;
01407         /* restore the 'fn' buffer */
01408         if (slashAdded) {
01409             *--p = L'\0';
01410         }
01411     }
01412     return rv;
01413 }
01414 
01415 PRInt32
01416 _PR_MD_GETFILEINFO64_UTF16(const PRUnichar *fn, PRFileInfo64 *info)
01417 {
01418     HANDLE hFindFile;
01419     WIN32_FIND_DATAW findFileData;
01420     PRUnichar pathbuf[MAX_PATH + 1];
01421 
01422     if (NULL == fn || L'\0' == *fn) {
01423         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
01424         return -1;
01425     }
01426 
01427     /*
01428      * FindFirstFile() expands wildcard characters.  So
01429      * we make sure the pathname contains no wildcard.
01430      */
01431     if (NULL != wcspbrk(fn, L"?*")) {
01432         PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
01433         return -1;
01434     }
01435 
01436     hFindFile = findFirstFileW(fn, &findFileData);
01437     if (INVALID_HANDLE_VALUE == hFindFile) {
01438         DWORD len;
01439         PRUnichar *filePart;
01440 
01441         /*
01442          * FindFirstFile() does not work correctly on root directories.
01443          * It also doesn't work correctly on a pathname that ends in a
01444          * slash.  So we first check to see if the pathname specifies a
01445          * root directory.  If not, and if the pathname ends in a slash,
01446          * we remove the final slash and try again.
01447          */
01448 
01449         /*
01450          * If the pathname does not contain ., \, and /, it cannot be
01451          * a root directory or a pathname that ends in a slash.
01452          */
01453         if (NULL == wcspbrk(fn, L".\\/")) {
01454             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
01455             return -1;
01456         } 
01457         len = getFullPathNameW(fn, sizeof(pathbuf)/sizeof(pathbuf[0]), pathbuf,
01458                 &filePart);
01459         if (0 == len) {
01460             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
01461             return -1;
01462         }
01463         if (len > sizeof(pathbuf)/sizeof(pathbuf[0])) {
01464             PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
01465             return -1;
01466         }
01467         if (IsRootDirectoryW(pathbuf, sizeof(pathbuf)/sizeof(pathbuf[0]))) {
01468             info->type = PR_FILE_DIRECTORY;
01469             info->size = 0;
01470             /*
01471              * These timestamps don't make sense for root directories.
01472              */
01473             info->modifyTime = 0;
01474             info->creationTime = 0;
01475             return 0;
01476         }
01477         if (!_PR_IS_W_SLASH(pathbuf[len - 1])) {
01478             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
01479             return -1;
01480         } else {
01481             pathbuf[len - 1] = L'\0';
01482             hFindFile = findFirstFileW(pathbuf, &findFileData);
01483             if (INVALID_HANDLE_VALUE == hFindFile) {
01484                 _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
01485                 return -1;
01486             }
01487         }
01488     }
01489 
01490     FindClose(hFindFile);
01491 
01492     if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
01493         info->type = PR_FILE_DIRECTORY;
01494     } else {
01495         info->type = PR_FILE_FILE;
01496     }
01497 
01498     info->size = findFileData.nFileSizeHigh;
01499     info->size = (info->size << 32) + findFileData.nFileSizeLow;
01500 
01501     _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
01502 
01503     if (0 == findFileData.ftCreationTime.dwLowDateTime &&
01504             0 == findFileData.ftCreationTime.dwHighDateTime) {
01505         info->creationTime = info->modifyTime;
01506     } else {
01507         _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
01508                 &info->creationTime);
01509     }
01510 
01511     return 0;
01512 }
01513 /* ================ end of UTF16 Interfaces ================================ */
01514 #endif /* MOZ_UNICODE */