Skip to content
Snippets Groups Projects
dos_fs.c 58.7 KiB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           GetLongPathName32A   (KERNEL32.xxx)
 */
DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                  DWORD longlen )
{
    DOS_FULL_NAME full_name;
Petr Tomasek's avatar
Petr Tomasek committed
    char *p, *r, *ll, *ss;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
    lstrcpynA( longpath, full_name.short_name, longlen );
Petr Tomasek's avatar
Petr Tomasek committed

    /* Do some hackery to get the long filename. */

    if (longpath) {
     ss=longpath+strlen(longpath);
     ll=full_name.long_name+strlen(full_name.long_name);
     p=NULL;
     while (ss>=longpath)
     {
       /* FIXME: aren't we more paranoid, than needed? */
       while ((ss[0]=='\\') && (ss>=longpath)) ss--;
       p=ss;
       while ((ss[0]!='\\') && (ss>=longpath)) ss--;
       if (ss>=longpath) 
         {
         /* FIXME: aren't we more paranoid, than needed? */
         while ((ll[0]=='/') && (ll>=full_name.long_name)) ll--;
         while ((ll[0]!='/') && (ll>=full_name.long_name)) ll--;
         if (ll<full_name.long_name) 
              { 
              ERR("Bad longname! (ss=%s ll=%s)\n This should never happen !\n"
		  ,ss ,ll ); 
              return 0;
              }
         }
     }

   /* FIXME: fix for names like "C:\\" (ie. with more '\'s) */
      if (p && p[2]) 
        {
	p+=1;
	if ((p-longpath)>0) longlen -= (p-longpath);
	lstrcpynA( p, ll , longlen);

        /* Now, change all '/' to '\' */
        for (r=p; r<(p+longlen); r++ ) 
          if (r[0]=='/') r[0]='\\';
        return strlen(longpath) - strlen(p) + longlen;
        }
Petr Tomasek's avatar
Petr Tomasek committed

    return strlen(longpath);
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.
Uwe Bonnes's avatar
Uwe Bonnes committed
 *
 * Known discrepancies to Win95 OSR2 bon 000118
 * "g:..\test should return G:\test
 * "..\..\..\..\test should return (Current drive):test
 *              even when test is not existant
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;
Uwe Bonnes's avatar
Uwe Bonnes committed
    char namelast;
    /* Address of the last byte in the buffer */
    char *endbuf = buffer + sizeof(buffer) - 1;
    TRACE("converting '%s'\n", name );
    if (!name || ((drive = DOSFS_GetPathDrive( &name )) == -1) )
    {
        SetLastError( ERROR_INVALID_PARAMETER );
Alexandre Julliard's avatar
Alexandre Julliard committed
    p = buffer;
    *p++ = 'A' + drive;
    *p++ = ':';
    *p++ = '\\';
    if ((*name!='/') && (*name!='\\'))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        /* Relative path or empty path */
        lstrcpynA( p, DRIVE_GetDosCwd(drive), sizeof(buffer) - 4 );
Alexandre Julliard's avatar
Alexandre Julliard committed
    }

    while (*name)
    {
        while ((*name == '\\') || (*name == '/'))
	    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++;
                continue;
            }
            else if ((name[1] == '.') && IS_END_OF_NAME(name[2]))
            {
		if (p == buffer + 3) {
                    /* no previous dir component */
                    SetLastError( ERROR_PATH_NOT_FOUND );
                    return 0;
                }
                /* skip previously added '\\' */
		p-=2;
		/* skip previous dir component */
Alexandre Julliard's avatar
Alexandre Julliard committed
                continue;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
        while (!IS_END_OF_NAME(*name) && (p<endbuf) )
            *p++ = *name++;
        if ((p<endbuf) && ((*name == '\\') || (*name == '/'))) {
            *p++='\\';
            name++;
        }
        if ( p==endbuf && *name )
        {
            SetLastError( ERROR_PATH_NOT_FOUND );
Alexandre Julliard's avatar
Alexandre Julliard committed
            return 0;
Uwe Bonnes's avatar
Uwe Bonnes committed
    /* Only return a trailing \\ if name does end in \\ // or :*/
    namelast= name[strlen(name)-1];
    if ( (namelast != '\\') && (namelast != '/')&& (namelast != ':') )
      {
	if(*(p-1) == '\\')
	  *(p-1) = '\0';
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
Uwe Bonnes's avatar
Uwe Bonnes committed
      CharUpperA( buffer );
    /* If the lpBuffer buffer is too small, the return value is the 
    size of the buffer, in characters, required to hold the path 
    plus the terminating \0 (tested against win95osr, bon 001118)
    . */
    ret = strlen(buffer);
    if (ret >= len )
      {
	/* don't touch anything when the buffer is not large enough */
        SetLastError( ERROR_INSUFFICIENT_BUFFER );
	return ret+1;
      }
    if (result)
    {
	if (unicode)
	    lstrcpynAtoW( (LPWSTR)result, buffer, len );
	else
	    lstrcpynA( result, buffer, len );
    }
    TRACE("returning '%s'\n", buffer );
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 );
Uwe Bonnes's avatar
Uwe Bonnes committed
    if (ret && (ret<=len) && buffer && lastpart)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        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 );
Uwe Bonnes's avatar
Uwe Bonnes committed
    if (ret && (ret<=len) && buffer && lastpart)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        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
        {
            WARN("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 );
        TRACE("returning %s (%s) %02lx %ld\n",
              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 );
    info->path = HEAP_strdupA( GetProcessHeap(), 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;
        HeapFree( GetProcessHeap(), 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 );
    if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
Alexandre Julliard's avatar
Alexandre Julliard committed
    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
       */

#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];

    TRACE("(%s,...)\n", devname ? devname : "<null>");
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!devname) {
	/* return known MSDOS devices */
	strcpy(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);
    }
    strcpy(buffer,"\\DEV\\");
    strcat(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; 
}
Patrik Stridvall's avatar
Patrik Stridvall committed
/***********************************************************************
 *           DefineDosDeviceA       (KERNEL32.182)
 */
BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath) {
	FIXME("(0x%08lx,%s,%s),stub!\n",flags,devname,targetpath);
	SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
	return FALSE;
}