Skip to content
Snippets Groups Projects
dos_fs.c 57.2 KiB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                  DWORD longlen )
{
    DOS_FULL_NAME full_name;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
    lstrcpynA( longpath, full_name.short_name, longlen );
    /* Do some hackery to get the long filename.
     * FIXME: Would be better if it returned the
     * long version of the directories too
     */
    longfilename = strrchr(full_name.long_name, '/')+1;
    if (longpath != NULL) {
      if ((p = strrchr( longpath, '\\' )) != NULL) {
	p++;
	longlen -= (p-longpath);
	lstrcpynA( p, longfilename , longlen);
      }
    }
    shortpathlen =
      ((strrchr( full_name.short_name, '\\' ) - full_name.short_name) + 1);
    return shortpathlen + strlen( longfilename );
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           GetLongPathName32W   (KERNEL32.269)
 */
DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                  DWORD longlen )
{
    DOS_FULL_NAME full_name;
    DWORD ret = 0;
    LPSTR shortpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, shortpath );

    /* FIXME: is it correct to always return a fully qualified short path? */
    if (DOSFS_GetFullName( shortpathA, TRUE, &full_name ))
    {
        ret = strlen( full_name.short_name );
        lstrcpynAtoW( longpath, full_name.long_name, longlen );
    }
    HeapFree( GetProcessHeap(), 0, shortpathA );
    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           DOSFS_DoGetFullPathName
Alexandre Julliard's avatar
Alexandre Julliard committed
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
 * Implementation of GetFullPathName32A/W.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
static DWORD DOSFS_DoGetFullPathName( LPCSTR name, DWORD len, LPSTR result,
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    char buffer[MAX_PATHNAME_LEN];
    int drive;
Alexandre Julliard's avatar
Alexandre Julliard committed
    char *p;
    DWORD ret;
    
    /* last possible position for a char != 0 */
    char *endchar = buffer + sizeof(buffer) - 2;
    *endchar = '\0';
    
    TRACE(dosfs, "converting '%s'\n", name );
    if (!name || !result || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
    {   SetLastError( ERROR_INVALID_PARAMETER );
        return 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    p = buffer;
    *p++ = 'A' + drive;
    *p++ = ':';
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (IS_END_OF_NAME(*name) && (*name))  /* Absolute path */
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        while (((*name == '\\') || (*name == '/')) && (!*endchar) )
	    *p++ = *name++;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    else  /* Relative path or empty path */
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        *p++ = '\\';
        lstrcpynA( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    *p = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed

    while (*name)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (*name == '.')
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (IS_END_OF_NAME(name[1]))
            {
                name++;
                while ((*name == '\\') || (*name == '/')) name++;
                continue;
            }
            else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
            {
                name += 2;
                while ((*name == '\\') || (*name == '/')) name++;

		if (p < buffer + 3) /* no previous dir component */
		    continue;
		p--; /* skip previously added '\\' */
		while ((*p == '\\') || (*p == '/')) p--;
		/* skip previous dir component */
                while ((*p != '\\') && (*p != '/')) p--;
                p++;
Alexandre Julliard's avatar
Alexandre Julliard committed
                continue;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
        {   SetLastError( ERROR_PATH_NOT_FOUND );
Alexandre Julliard's avatar
Alexandre Julliard committed
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
        while (!IS_END_OF_NAME(*name) && (!*endchar) )
Alexandre Julliard's avatar
Alexandre Julliard committed
            *p++ = *name++;
        while (((*name == '\\') || (*name == '/')) && (!*endchar) )
	    *p++ = *name++;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
Alexandre Julliard's avatar
Alexandre Julliard committed

    if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
       
    if (unicode)
        lstrcpynAtoW( (LPWSTR)result, buffer, len );
    else
        lstrcpynA( result, buffer, len );

    TRACE(dosfs, "returning '%s'\n", buffer );
    /* If the lpBuffer buffer is too small, the return value is the 
    size of the buffer, in characters, required to hold the path. */
    ret = strlen(buffer);

    if (ret >= len )
        SetLastError( ERROR_INSUFFICIENT_BUFFER );

    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           GetFullPathName32A   (KERNEL32.272)
 * NOTES
 *   if the path closed with '\', *lastpart is 0 
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                 LPSTR *lastpart )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer, FALSE );
    if (ret && lastpart)
    {
        LPSTR p = buffer + strlen(buffer);

	if (*p != '\\')
        {
	  while ((p > buffer + 2) && (*p != '\\')) p--;
          *lastpart = p + 1;
	}
	else *lastpart = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    return ret;
}


/***********************************************************************
 *           GetFullPathName32W   (KERNEL32.273)
 */
DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                 LPWSTR *lastpart )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
    DWORD ret = DOSFS_DoGetFullPathName( nameA, len, (LPSTR)buffer, TRUE );
    HeapFree( GetProcessHeap(), 0, nameA );
    if (ret && lastpart)
    {
        LPWSTR p = buffer + lstrlenW(buffer);
        if (*p != (WCHAR)'\\')
        {
            while ((p > buffer + 2) && (*p != (WCHAR)'\\')) p--;
            *lastpart = p + 1;
        }
        else *lastpart = NULL;  
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
}

/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
 *           DOSFS_FindNextEx
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
static int DOSFS_FindNextEx( FIND_FIRST_INFO *info, WIN32_FIND_DATAA *entry )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    BYTE attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY;
    UINT flags = DRIVE_GetFlags( info->drive );
Alexandre Julliard's avatar
Alexandre Julliard committed
    char *p, buffer[MAX_PATHNAME_LEN];
    const char *drive_path;
    int drive_root;
Alexandre Julliard's avatar
Alexandre Julliard committed
    LPCSTR long_name, short_name;
Alexandre Julliard's avatar
Alexandre Julliard committed
    BY_HANDLE_FILE_INFORMATION fileinfo;
    char dos_name[13];
Alexandre Julliard's avatar
Alexandre Julliard committed
    if ((info->attr & ~(FA_UNUSED | FA_ARCHIVE | FA_RDONLY)) == FA_LABEL)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (info->cur_pos) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
        entry->dwFileAttributes  = FILE_ATTRIBUTE_LABEL;
Alexandre Julliard's avatar
Alexandre Julliard committed
        DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftCreationTime, 0 );
        DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastAccessTime, 0 );
        DOSFS_UnixTimeToFileTime( (time_t)0, &entry->ftLastWriteTime, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
        entry->nFileSizeHigh     = 0;
        entry->nFileSizeLow      = 0;
        entry->dwReserved0       = 0;
        entry->dwReserved1       = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
        DOSFS_ToDosDTAFormat( DRIVE_GetLabel( info->drive ), entry->cFileName );
        strcpy( entry->cAlternateFileName, entry->cFileName ); 
        info->cur_pos++;
Alexandre Julliard's avatar
Alexandre Julliard committed
        return 1;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
    drive_path = info->path + strlen(DRIVE_GetRoot( info->drive ));
    while ((*drive_path == '/') || (*drive_path == '\\')) drive_path++;
    drive_root = !*drive_path;

    lstrcpynA( buffer, info->path, sizeof(buffer) - 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
    strcat( buffer, "/" );
    p = buffer + strlen(buffer);

Alexandre Julliard's avatar
Alexandre Julliard committed
    while (DOSFS_ReadDir( info->dir, &long_name, &short_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        info->cur_pos++;
Alexandre Julliard's avatar
Alexandre Julliard committed
        /* Don't return '.' and '..' in the root of the drive */
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (drive_root && (long_name[0] == '.') &&
            (!long_name[1] || ((long_name[1] == '.') && !long_name[2])))
            continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
        /* Check the long mask */

Alexandre Julliard's avatar
Alexandre Julliard committed
        if (info->long_mask)
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (!DOSFS_MatchLong( info->long_mask, long_name,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                  flags & DRIVE_CASE_SENSITIVE )) continue;
        }

        /* Check the short mask */

Alexandre Julliard's avatar
Alexandre Julliard committed
        if (info->short_mask)
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (!short_name)
Alexandre Julliard's avatar
Alexandre Julliard committed
            {
                DOSFS_Hash( long_name, dos_name, TRUE,
                            !(flags & DRIVE_CASE_SENSITIVE) );
                short_name = dos_name;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
            if (!DOSFS_MatchShort( info->short_mask, short_name )) continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }

        /* Check the file attributes */

        lstrcpynA( p, long_name, sizeof(buffer) - (int)(p - buffer) );
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (!FILE_Stat( buffer, &fileinfo ))
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            WARN(dosfs, "can't stat %s\n", buffer);
Alexandre Julliard's avatar
Alexandre Julliard committed
            continue;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (fileinfo.dwFileAttributes & ~attr) continue;
Alexandre Julliard's avatar
Alexandre Julliard committed

        /* We now have a matching entry; fill the result and return */

Alexandre Julliard's avatar
Alexandre Julliard committed
        entry->dwFileAttributes = fileinfo.dwFileAttributes;
        entry->ftCreationTime   = fileinfo.ftCreationTime;
        entry->ftLastAccessTime = fileinfo.ftLastAccessTime;
        entry->ftLastWriteTime  = fileinfo.ftLastWriteTime;
        entry->nFileSizeHigh    = fileinfo.nFileSizeHigh;
        entry->nFileSizeLow     = fileinfo.nFileSizeLow;
Alexandre Julliard's avatar
Alexandre Julliard committed

        if (short_name)
            DOSFS_ToDosDTAFormat( short_name, entry->cAlternateFileName );
        else
            DOSFS_Hash( long_name, entry->cAlternateFileName, FALSE,
                        !(flags & DRIVE_CASE_SENSITIVE) );

        lstrcpynA( entry->cFileName, long_name, sizeof(entry->cFileName) );
        if (!(flags & DRIVE_CASE_PRESERVING)) CharLowerA( entry->cFileName );
Alexandre Julliard's avatar
Alexandre Julliard committed
        TRACE(dosfs, "returning %s (%s) %02lx %ld\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
                       entry->cFileName, entry->cAlternateFileName,
                       entry->dwFileAttributes, entry->nFileSizeLow );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    return 0;  /* End of directory */
}
Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           DOSFS_FindNext
 *
 * Find the next matching file. Return the number of entries read to find
 * the matching one, or 0 if no more entries.
 * 'short_mask' is the 8.3 mask (in FCB format), 'long_mask' is the long
 * file name mask. Either or both can be NULL.
 *
 * NOTE: This is supposed to be only called by the int21 emulation
 *       routines. Thus, we should own the Win16Mutex anyway.
 *       Nevertheless, we explicitly enter it to ensure the static
 *       directory cache is protected.
 */
int DOSFS_FindNext( const char *path, const char *short_mask,
                    const char *long_mask, int drive, BYTE attr,
                    int skip, WIN32_FIND_DATAA *entry )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    static FIND_FIRST_INFO info = { NULL };
    LPCSTR short_name, long_name;
    int count;

    SYSLEVEL_EnterWin16Lock();

    /* Check the cached directory */
    if (!(info.dir && info.path == path && info.short_mask == short_mask
                   && info.long_mask == long_mask && info.drive == drive
                   && info.attr == attr && info.cur_pos <= skip))
    {  
        /* Not in the cache, open it anew */
        if (info.dir) DOSFS_CloseDir( info.dir );

        info.path = (LPSTR)path;
        info.long_mask = (LPSTR)long_mask;
        info.short_mask = (LPSTR)short_mask;
        info.attr = attr;
        info.drive = drive;
        info.cur_pos = 0;
        info.dir = DOSFS_OpenDir( info.path );
    }

    /* Skip to desired position */
    while (info.cur_pos < skip)
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (info.dir && DOSFS_ReadDir( info.dir, &long_name, &short_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
            info.cur_pos++;
        else
            break;

Alexandre Julliard's avatar
Alexandre Julliard committed
    if (info.dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, entry ))
Alexandre Julliard's avatar
Alexandre Julliard committed
        count = info.cur_pos - skip;
    else
        count = 0;

    if (!count)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (info.dir) DOSFS_CloseDir( info.dir );
Alexandre Julliard's avatar
Alexandre Julliard committed
        memset( &info, '\0', sizeof(info) );
    }

    SYSLEVEL_LeaveWin16Lock();

    return count;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
/*************************************************************************
 *           FindFirstFile16   (KERNEL.413)
 */
HANDLE16 WINAPI FindFirstFile16( LPCSTR path, WIN32_FIND_DATAA *data )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    DOS_FULL_NAME full_name;
Alexandre Julliard's avatar
Alexandre Julliard committed
    HGLOBAL16 handle;
    FIND_FIRST_INFO *info;

Andreas Mohr's avatar
Andreas Mohr committed
    data->dwReserved0 = data->dwReserved1 = 0x0;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!path) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!DOSFS_GetFullName( path, FALSE, &full_name ))
Alexandre Julliard's avatar
Alexandre Julliard committed
        return INVALID_HANDLE_VALUE16;
    if (!(handle = GlobalAlloc16( GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO) )))
        return INVALID_HANDLE_VALUE16;
    info = (FIND_FIRST_INFO *)GlobalLock16( handle );
Alexandre Julliard's avatar
Alexandre Julliard committed
    info->path = HEAP_strdupA( SystemHeap, 0, full_name.long_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
    info->long_mask = strrchr( info->path, '/' );
    *(info->long_mask++) = '\0';
    info->short_mask = NULL;
    info->attr = 0xff;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (path[0] && (path[1] == ':')) info->drive = toupper(*path) - 'A';
    else info->drive = DRIVE_GetCurrentDrive();
Alexandre Julliard's avatar
Alexandre Julliard committed
    info->cur_pos = 0;

    info->dir = DOSFS_OpenDir( info->path );

Alexandre Julliard's avatar
Alexandre Julliard committed
    GlobalUnlock16( handle );
    if (!FindNextFile16( handle, data ))
    {
        FindClose16( handle );
        SetLastError( ERROR_NO_MORE_FILES );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return INVALID_HANDLE_VALUE16;
    }
    return handle;
}


/*************************************************************************
 *           FindFirstFile32A   (KERNEL32.123)
 */
HANDLE WINAPI FindFirstFileA( LPCSTR path, WIN32_FIND_DATAA *data )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    HANDLE handle = FindFirstFile16( path, data );
    if (handle == INVALID_HANDLE_VALUE16) return INVALID_HANDLE_VALUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
    return handle;
}


/*************************************************************************
 *           FindFirstFile32W   (KERNEL32.124)
 */
HANDLE WINAPI FindFirstFileW( LPCWSTR path, WIN32_FIND_DATAW *data )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
    HANDLE handle = FindFirstFileA( pathA, &dataA );
Alexandre Julliard's avatar
Alexandre Julliard committed
    HeapFree( GetProcessHeap(), 0, pathA );
    if (handle != INVALID_HANDLE_VALUE)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        data->dwFileAttributes = dataA.dwFileAttributes;
        data->ftCreationTime   = dataA.ftCreationTime;
        data->ftLastAccessTime = dataA.ftLastAccessTime;
        data->ftLastWriteTime  = dataA.ftLastWriteTime;
        data->nFileSizeHigh    = dataA.nFileSizeHigh;
        data->nFileSizeLow     = dataA.nFileSizeLow;
        lstrcpyAtoW( data->cFileName, dataA.cFileName );
        lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
    }
    return handle;
}


/*************************************************************************
 *           FindNextFile16   (KERNEL.414)
 */
BOOL16 WINAPI FindNextFile16( HANDLE16 handle, WIN32_FIND_DATAA *data )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    FIND_FIRST_INFO *info;

    if (!(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
    {
        SetLastError( ERROR_INVALID_HANDLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return FALSE;
    }
    GlobalUnlock16( handle );
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!info->path || !info->dir)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        SetLastError( ERROR_NO_MORE_FILES );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!DOSFS_FindNextEx( info, data ))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        DOSFS_CloseDir( info->dir ); info->dir = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
        HeapFree( SystemHeap, 0, info->path );
Alexandre Julliard's avatar
Alexandre Julliard committed
        info->path = info->long_mask = NULL;
        SetLastError( ERROR_NO_MORE_FILES );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return FALSE;
    }
    return TRUE;
}


/*************************************************************************
 *           FindNextFile32A   (KERNEL32.126)
 */
BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    return FindNextFile16( handle, data );
}


/*************************************************************************
 *           FindNextFile32W   (KERNEL32.127)
 */
BOOL WINAPI FindNextFileW( HANDLE handle, WIN32_FIND_DATAW *data )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    WIN32_FIND_DATAA dataA;
    if (!FindNextFileA( handle, &dataA )) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
    data->dwFileAttributes = dataA.dwFileAttributes;
    data->ftCreationTime   = dataA.ftCreationTime;
    data->ftLastAccessTime = dataA.ftLastAccessTime;
    data->ftLastWriteTime  = dataA.ftLastWriteTime;
    data->nFileSizeHigh    = dataA.nFileSizeHigh;
    data->nFileSizeLow     = dataA.nFileSizeLow;
    lstrcpyAtoW( data->cFileName, dataA.cFileName );
    lstrcpyAtoW( data->cAlternateFileName, dataA.cAlternateFileName );
    return TRUE;
}


/*************************************************************************
 *           FindClose16   (KERNEL.415)
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
BOOL16 WINAPI FindClose16( HANDLE16 handle )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    FIND_FIRST_INFO *info;

    if ((handle == INVALID_HANDLE_VALUE16) ||
        !(info = (FIND_FIRST_INFO *)GlobalLock16( handle )))
    {
        SetLastError( ERROR_INVALID_HANDLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (info->dir) DOSFS_CloseDir( info->dir );
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (info->path) HeapFree( SystemHeap, 0, info->path );
    GlobalUnlock16( handle );
    GlobalFree16( handle );
    return TRUE;
}


/*************************************************************************
 *           FindClose32   (KERNEL32.119)
 */
BOOL WINAPI FindClose( HANDLE handle )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    return FindClose16( (HANDLE16)handle );
}


Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
 *           DOSFS_UnixTimeToFileTime
 *
 * Convert a Unix time to FILETIME format.
 * The FILETIME structure is a 64-bit value representing the number of
Alexandre Julliard's avatar
Alexandre Julliard committed
 * 100-nanosecond intervals since January 1, 1601, 0:00.
 * 'remainder' is the nonnegative number of 100-ns intervals
 * corresponding to the time fraction smaller than 1 second that
 * couldn't be stored in the time_t value.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
                               DWORD remainder )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    /* NOTES:

       CONSTANTS: 
       The time difference between 1 January 1601, 00:00:00 and
       1 January 1970, 00:00:00 is 369 years, plus the leap years
       from 1604 to 1968, excluding 1700, 1800, 1900.
       This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
       of 134774 days.

       Any day in that period had 24 * 60 * 60 = 86400 seconds.

       The time difference is 134774 * 86400 * 10000000, which can be written
       116444736000000000
       27111902 * 2^32 + 3577643008
       413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768

       If you find that these constants are buggy, please change them in all
       instances in both conversion functions.

       VERSIONS:
       There are two versions, one of them uses long long variables and
       is presumably faster but not ISO C. The other one uses standard C
       data types and operations but relies on the assumption that negative
       numbers are stored as 2's complement (-1 is 0xffff....). If this
       assumption is violated, dates before 1970 will not convert correctly.
       This should however work on any reasonable architecture where WINE
       will run.

       DETAILS:
       
       Take care not to remove the casts. I have tested these functions
       (in both versions) for a lot of numbers. I would be interested in
       results on other compilers than GCC.

       The operations have been designed to account for the possibility
       of 64-bit time_t in future UNICES. Even the versions without
       internal long long numbers will work if time_t only is 64 bit.
       A 32-bit shift, which was necessary for that operation, turned out
       not to work correctly in GCC, besides giving the warning. So I
       used a double 16-bit shift instead. Numbers are in the ISO version
       represented by three limbs, the most significant with 32 bit, the
       other two with 16 bit each.

       As the modulo-operator % is not well-defined for negative numbers,
       negative divisors have been avoided in DOSFS_FileTimeToUnixTime.

       There might be quicker ways to do this in C. Certainly so in
       assembler.

       Claus Fischer, fischer@iue.tuwien.ac.at
       */

Alexandre Julliard's avatar
Alexandre Julliard committed
#if (SIZEOF_LONG_LONG >= 8)
Alexandre Julliard's avatar
Alexandre Julliard committed
#  define USE_LONG_LONG 1
#else
#  define USE_LONG_LONG 0
#endif

#if USE_LONG_LONG		/* gcc supports long long type */

    long long int t = unix_time;
    t *= 10000000;
    t += 116444736000000000LL;
    t += remainder;
    filetime->dwLowDateTime  = (UINT)t;
    filetime->dwHighDateTime = (UINT)(t >> 32);
Alexandre Julliard's avatar
Alexandre Julliard committed

#else  /* ISO version */

    UINT a0;			/* 16 bit, low    bits */
    UINT a1;			/* 16 bit, medium bits */
    UINT a2;			/* 32 bit, high   bits */
Alexandre Julliard's avatar
Alexandre Julliard committed

    /* Copy the unix time to a2/a1/a0 */
    a0 =  unix_time & 0xffff;
    a1 = (unix_time >> 16) & 0xffff;
    /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
       Do not replace this by >> 32, it gives a compiler warning and it does
       not work. */
    a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
	  ~((~unix_time >> 16) >> 16));

    /* Multiply a by 10000000 (a = a2/a1/a0)
       Split the factor into 10000 * 1000 which are both less than 0xffff. */
    a0 *= 10000;
    a1 = a1 * 10000 + (a0 >> 16);
    a2 = a2 * 10000 + (a1 >> 16);
    a0 &= 0xffff;
    a1 &= 0xffff;

    a0 *= 1000;
    a1 = a1 * 1000 + (a0 >> 16);
    a2 = a2 * 1000 + (a1 >> 16);
    a0 &= 0xffff;
    a1 &= 0xffff;

    /* Add the time difference and the remainder */
    a0 += 32768 + (remainder & 0xffff);
    a1 += 54590 + (remainder >> 16   ) + (a0 >> 16);
    a2 += 27111902                     + (a1 >> 16);
    a0 &= 0xffff;
    a1 &= 0xffff;

    /* Set filetime */
    filetime->dwLowDateTime  = (a1 << 16) + a0;
    filetime->dwHighDateTime = a2;
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
}
Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
 *           DOSFS_FileTimeToUnixTime
 *
 * Convert a FILETIME format to Unix time.
Alexandre Julliard's avatar
Alexandre Julliard committed
 * If not NULL, 'remainder' contains the fractional part of the filetime,
 * in the range of [0..9999999] (even if time_t is negative).
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
#if USE_LONG_LONG

    long long int t = filetime->dwHighDateTime;
    t <<= 32;
    t += (UINT)filetime->dwLowDateTime;
Alexandre Julliard's avatar
Alexandre Julliard committed
    t -= 116444736000000000LL;
    if (t < 0)
    {
	if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
	return -1 - ((-t - 1) / 10000000);
    }
    else
    {
	if (remainder) *remainder = t % 10000000;
	return t / 10000000;
    }

#else  /* ISO version */

    UINT a0;			/* 16 bit, low    bits */
    UINT a1;			/* 16 bit, medium bits */
    UINT a2;			/* 32 bit, high   bits */
    UINT r;			/* remainder of division */
Alexandre Julliard's avatar
Alexandre Julliard committed
    unsigned int carry;		/* carry bit for subtraction */
    int negative;		/* whether a represents a negative value */

    /* Copy the time values to a2/a1/a0 */
    a2 =  (UINT)filetime->dwHighDateTime;
    a1 = ((UINT)filetime->dwLowDateTime ) >> 16;
    a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff;
Alexandre Julliard's avatar
Alexandre Julliard committed

    /* Subtract the time difference */
    if (a0 >= 32768           ) a0 -=             32768        , carry = 0;
    else                        a0 += (1 << 16) - 32768        , carry = 1;

    if (a1 >= 54590    + carry) a1 -=             54590 + carry, carry = 0;
    else                        a1 += (1 << 16) - 54590 - carry, carry = 1;

    a2 -= 27111902 + carry;
    
    /* If a is negative, replace a by (-1-a) */
    negative = (a2 >= ((UINT)1) << 31);
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (negative)
    {
	/* Set a to -a - 1 (a is a2/a1/a0) */
	a0 = 0xffff - a0;
	a1 = 0xffff - a1;
	a2 = ~a2;
    }

    /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
       Split the divisor into 10000 * 1000 which are both less than 0xffff. */
    a1 += (a2 % 10000) << 16;
    a2 /=       10000;
    a0 += (a1 % 10000) << 16;
    a1 /=       10000;
    r   =  a0 % 10000;
    a0 /=       10000;

    a1 += (a2 % 1000) << 16;
    a2 /=       1000;
    a0 += (a1 % 1000) << 16;
    a1 /=       1000;
    r  += (a0 % 1000) * 10000;
    a0 /=       1000;

    /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
    if (negative)
    {
	/* Set a to -a - 1 (a is a2/a1/a0) */
	a0 = 0xffff - a0;
	a1 = 0xffff - a1;
	a2 = ~a2;

        r  = 9999999 - r;
    }

    if (remainder) *remainder = r;

    /* Do not replace this by << 32, it gives a compiler warning and it does
       not work. */
    return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed

/***********************************************************************
 *           DosDateTimeToFileTime   (KERNEL32.76)
 */
BOOL WINAPI DosDateTimeToFileTime( WORD fatdate, WORD fattime, LPFILETIME ft)
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    struct tm newtm;

    newtm.tm_sec  = (fattime & 0x1f) * 2;
    newtm.tm_min  = (fattime >> 5) & 0x3f;
    newtm.tm_hour = (fattime >> 11);
    newtm.tm_mday = (fatdate & 0x1f);
    newtm.tm_mon  = ((fatdate >> 5) & 0x0f) - 1;
    newtm.tm_year = (fatdate >> 9) + 80;
    DOSFS_UnixTimeToFileTime( mktime( &newtm ), ft, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE;
}


/***********************************************************************
 *           FileTimeToDosDateTime   (KERNEL32.111)
 */
BOOL WINAPI FileTimeToDosDateTime( const FILETIME *ft, LPWORD fatdate,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                     LPWORD fattime )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    time_t unixtime = DOSFS_FileTimeToUnixTime( ft, NULL );
    struct tm *tm = localtime( &unixtime );
    if (fattime)
        *fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
    if (fatdate)
        *fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
                   + tm->tm_mday;
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE;
}


/***********************************************************************
 *           LocalFileTimeToFileTime   (KERNEL32.373)
 */
BOOL WINAPI LocalFileTimeToFileTime( const FILETIME *localft,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                       LPFILETIME utcft )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    struct tm *xtm;
Alexandre Julliard's avatar
Alexandre Julliard committed
    DWORD remainder;
Alexandre Julliard's avatar
Alexandre Julliard committed

    /* convert from local to UTC. Perhaps not correct. FIXME */
Alexandre Julliard's avatar
Alexandre Julliard committed
    time_t unixtime = DOSFS_FileTimeToUnixTime( localft, &remainder );
    xtm = gmtime( &unixtime );
    DOSFS_UnixTimeToFileTime( mktime(xtm), utcft, remainder );
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE; 
}


/***********************************************************************
 *           FileTimeToLocalFileTime   (KERNEL32.112)
 */
BOOL WINAPI FileTimeToLocalFileTime( const FILETIME *utcft,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                       LPFILETIME localft )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    DWORD remainder;
Alexandre Julliard's avatar
Alexandre Julliard committed
    /* convert from UTC to local. Perhaps not correct. FIXME */
Alexandre Julliard's avatar
Alexandre Julliard committed
    time_t unixtime = DOSFS_FileTimeToUnixTime( utcft, &remainder );
Alexandre Julliard's avatar
Alexandre Julliard committed
#ifdef HAVE_TIMEGM
    struct tm *xtm = localtime( &unixtime );
    time_t localtime;

    localtime = timegm(xtm);
    DOSFS_UnixTimeToFileTime( localtime, localft, remainder );

#else
    struct tm *xtm,*gtm;
    time_t time1,time2;

Alexandre Julliard's avatar
Alexandre Julliard committed
    xtm = localtime( &unixtime );
Alexandre Julliard's avatar
Alexandre Julliard committed
    gtm = gmtime( &unixtime );
    time1 = mktime(xtm);
    time2 = mktime(gtm);
    DOSFS_UnixTimeToFileTime( 2*time1-time2, localft, remainder );
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE; 
}


/***********************************************************************
 *           FileTimeToSystemTime   (KERNEL32.113)
 */
BOOL WINAPI FileTimeToSystemTime( const FILETIME *ft, LPSYSTEMTIME syst )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    struct tm *xtm;
Alexandre Julliard's avatar
Alexandre Julliard committed
    DWORD remainder;
    time_t xtime = DOSFS_FileTimeToUnixTime( ft, &remainder );
Alexandre Julliard's avatar
Alexandre Julliard committed
    xtm = gmtime(&xtime);
Alexandre Julliard's avatar
Alexandre Julliard committed
    syst->wYear         = xtm->tm_year+1900;
Alexandre Julliard's avatar
Alexandre Julliard committed
    syst->wMonth        = xtm->tm_mon + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
    syst->wDayOfWeek    = xtm->tm_wday;
    syst->wDay	        = xtm->tm_mday;
    syst->wHour	        = xtm->tm_hour;
    syst->wMinute       = xtm->tm_min;
    syst->wSecond       = xtm->tm_sec;
    syst->wMilliseconds	= remainder / 10000;
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE; 
}

Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           QueryDosDeviceA   (KERNEL32.413)
 *
 * returns array of strings terminated by \0, terminated by \0
 */
DWORD WINAPI QueryDosDeviceA(LPCSTR devname,LPSTR target,DWORD bufsize)
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    LPSTR s;
    char  buffer[200];

Alexandre Julliard's avatar
Alexandre Julliard committed
    TRACE(dosfs,"(%s,...)\n",devname?devname:"<null>");
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!devname) {
	/* return known MSDOS devices */
	lstrcpyA(buffer,"CON COM1 COM2 LPT1 NUL ");
Alexandre Julliard's avatar
Alexandre Julliard committed
	while ((s=strchr(buffer,' ')))
		*s='\0';

	lstrcpynA(target,buffer,bufsize);
Alexandre Julliard's avatar
Alexandre Julliard committed
	return strlen(buffer);
    }
    lstrcpyA(buffer,"\\DEV\\");
    lstrcatA(buffer,devname);
Alexandre Julliard's avatar
Alexandre Julliard committed
    if ((s=strchr(buffer,':'))) *s='\0';
    lstrcpynA(target,buffer,bufsize);
Alexandre Julliard's avatar
Alexandre Julliard committed
    return strlen(buffer);
}


/***********************************************************************
 *           QueryDosDeviceW   (KERNEL32.414)
 *
 * returns array of strings terminated by \0, terminated by \0
 */
DWORD WINAPI QueryDosDeviceW(LPCWSTR devname,LPWSTR target,DWORD bufsize)
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    LPSTR devnameA = devname?HEAP_strdupWtoA(GetProcessHeap(),0,devname):NULL;
    LPSTR targetA = (LPSTR)HEAP_xalloc(GetProcessHeap(),0,bufsize);
    DWORD ret = QueryDosDeviceA(devnameA,targetA,bufsize);
Alexandre Julliard's avatar
Alexandre Julliard committed

    lstrcpynAtoW(target,targetA,bufsize);
    if (devnameA) HeapFree(GetProcessHeap(),0,devnameA);
    if (targetA) HeapFree(GetProcessHeap(),0,targetA);
    return ret;
}

Alexandre Julliard's avatar
Alexandre Julliard committed

/***********************************************************************
 *           SystemTimeToFileTime   (KERNEL32.526)
 */
BOOL WINAPI SystemTimeToFileTime( const SYSTEMTIME *syst, LPFILETIME ft )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
Alexandre Julliard's avatar
Alexandre Julliard committed
#ifdef HAVE_TIMEGM
Alexandre Julliard's avatar
Alexandre Julliard committed
    struct tm xtm;
Alexandre Julliard's avatar
Alexandre Julliard committed
    time_t utctime;
#else
    struct tm xtm,*local_tm,*utc_tm;
    time_t localtim,utctime;
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
    xtm.tm_year	= syst->wYear-1900;
Alexandre Julliard's avatar
Alexandre Julliard committed
    xtm.tm_mon	= syst->wMonth - 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
    xtm.tm_wday	= syst->wDayOfWeek;
    xtm.tm_mday	= syst->wDay;
    xtm.tm_hour	= syst->wHour;
    xtm.tm_min	= syst->wMinute;
Alexandre Julliard's avatar
Alexandre Julliard committed
    xtm.tm_sec	= syst->wSecond; /* this is UTC */
    xtm.tm_isdst = -1;
#ifdef HAVE_TIMEGM
    utctime = timegm(&xtm);
    DOSFS_UnixTimeToFileTime( utctime, ft, 
			      syst->wMilliseconds * 10000 );
#else
    localtim = mktime(&xtm);    /* now we've got local time */
    local_tm = localtime(&localtim);
    utc_tm = gmtime(&localtim);
    utctime = mktime(utc_tm);
    DOSFS_UnixTimeToFileTime( 2*localtim -utctime, ft, 
			      syst->wMilliseconds * 10000 );
#endif
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE; 
}
BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
	FIXME(dosfs,"(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
	SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
	return FALSE;
}