Skip to content
Snippets Groups Projects
dos_fs.c 71.3 KiB
Newer Older
Peter Ganten's avatar
Peter Ganten committed
 * - the absolute/relative path of the short name is reproduced like found
 *   in the long name
 * - longpath and shortpath may have the same address
 * Peter Ganten, 1999
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                  DWORD shortlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    DOS_FULL_NAME full_name;
    LPSTR tmpshortpath;
Peter Ganten's avatar
Peter Ganten committed
    DWORD sp = 0, lp = 0;
    int tmplen, drive;
    UINT flags;

    TRACE("%s\n", debugstr_a(longpath));

    if (!longpath) {
      SetLastError(ERROR_INVALID_PARAMETER);
      return 0;
    }
    if (!longpath[0]) {
      SetLastError(ERROR_BAD_PATHNAME);
      return 0;
Peter Ganten's avatar
Peter Ganten committed
    if ( ( tmpshortpath = HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) {
      SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
      return 0;
Peter Ganten's avatar
Peter Ganten committed
    /* check for drive letter */
    if ( longpath[1] == ':' ) {
Peter Ganten's avatar
Peter Ganten committed
      tmpshortpath[0] = longpath[0];
      tmpshortpath[1] = ':';
      sp = 2;
    }
Peter Ganten's avatar
Peter Ganten committed
    if ( ( drive = DOSFS_GetPathDrive ( &longpath )) == -1 ) return 0;
    flags = DRIVE_GetFlags ( drive );
Peter Ganten's avatar
Peter Ganten committed
    while ( longpath[lp] ) {

      /* check for path delimiters and reproduce them */
      if ( longpath[lp] == '\\' || longpath[lp] == '/' ) {
	if (!sp || tmpshortpath[sp-1]!= '\\') 
        {
	    /* strip double "\\" */
	    tmpshortpath[sp] = '\\';
	    sp++;
        }
        tmpshortpath[sp]=0;/*terminate string*/
Peter Ganten's avatar
Peter Ganten committed
	lp++;
Peter Ganten's avatar
Peter Ganten committed
      tmplen = strcspn ( longpath + lp, "\\/" ); 
      lstrcpynA ( tmpshortpath+sp, longpath + lp, tmplen+1 );
      
      /* Check, if the current element is a valid dos name */
      if ( DOSFS_ValidDOSName ( longpath + lp, !(flags & DRIVE_CASE_SENSITIVE) ) ) {
	sp += tmplen;
	lp += tmplen;
	continue;
Peter Ganten's avatar
Peter Ganten committed

      /* Check if the file exists and use the existing file name */
      if ( DOSFS_GetFullName ( tmpshortpath, TRUE, &full_name ) ) {
	strcpy( tmpshortpath+sp, strrchr ( full_name.short_name, '\\' ) + 1 );
Peter Ganten's avatar
Peter Ganten committed
	lp += tmplen;
	continue;
      }

      TRACE("not found!\n" );
      SetLastError ( ERROR_FILE_NOT_FOUND );
      return 0;
    lstrcpynA ( shortpath, tmpshortpath, shortlen );
    TRACE("returning %s\n", debugstr_a(shortpath) );
    HeapFree ( GetProcessHeap(), 0, tmpshortpath );
    
Peter Ganten's avatar
Peter Ganten committed
    return tmplen;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           GetShortPathNameW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath,
Alexandre Julliard's avatar
Alexandre Julliard committed
                                  DWORD shortlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    LPSTR longpathA, shortpathA;
Alexandre Julliard's avatar
Alexandre Julliard committed
    DWORD ret = 0;

    longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, longpath );
    shortpathA = HeapAlloc ( GetProcessHeap(), 0, shortlen );
    ret = GetShortPathNameA ( longpathA, shortpathA, shortlen );
    if (shortlen > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, shortpath, shortlen ))
        shortpath[shortlen-1] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
    HeapFree( GetProcessHeap(), 0, longpathA );
    HeapFree( GetProcessHeap(), 0, shortpathA );

    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           GetLongPathNameA   (KERNEL32.@)
 *
 * NOTES
 *  observed (Win2000):
 *  shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
 *  shortpath="":   LastError=ERROR_PATH_NOT_FOUND, ret=0
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;
Petr Tomasek's avatar
Petr Tomasek committed
    char *p, *r, *ll, *ss;

    if (!shortpath) {
      SetLastError(ERROR_INVALID_PARAMETER);
      return 0;
    }
    if (!shortpath[0]) {
      SetLastError(ERROR_PATH_NOT_FOUND);
      return 0;
    }

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
}


/***********************************************************************
 *           GetLongPathNameW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
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 );
        if (longlen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.long_name, -1,
                                                 longpath, longlen ))
            longpath[longlen-1] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    HeapFree( GetProcessHeap(), 0, shortpathA );
    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           DOSFS_DoGetFullPathName
Alexandre Julliard's avatar
Alexandre Julliard committed
 *
 * Implementation of GetFullPathNameA/W.
Uwe Bonnes's avatar
Uwe Bonnes committed
 *
Huw D. M. Davies's avatar
Huw D. M. Davies committed
 * A test for GetFullPathName with many pathological cases 
 * now gives identical output for Wine and OSR2
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
{
    DOS_FULL_NAME full_name;
    char *p,*q;
    const char * root;
    char drivecur[]="c:.";
    char driveletter=0;
    int namelen,drive=0;

    if ((strlen(name) >1)&& (name[1]==':'))
      /* drive letter given */
      {
	driveletter = name[0];
      }
    if ((strlen(name) >2)&& (name[1]==':') &&
	     ((name[2]=='\\') || (name[2]=='/')))
      /* absolute path given */
Uwe Bonnes's avatar
Uwe Bonnes committed
      {
	lstrcpynA(full_name.short_name,name,MAX_PATHNAME_LEN);
	drive = (int)FILE_toupper(name[0]) - 'A';
      }
    else
      {
	if (driveletter)
	  drivecur[0]=driveletter;
	else
	  strcpy(drivecur,".");
	if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
	  {
	    FIXME("internal: error getting drive/path\n");
	    return 0;
	  }
	/* find path that drive letter substitutes*/
	drive = (int)FILE_toupper(full_name.short_name[0]) -0x41;
	root= DRIVE_GetRoot(drive);
	if (!root)
	  {
	    FIXME("internal: error getting DOS Drive Root\n");
	    return 0;
	  }
	if (!strcmp(root,"/"))
	  {
	    /* we have just the last / and we need it. */
	    p= full_name.long_name;
	  }
	else
	  {
	    p= full_name.long_name +strlen(root);
	  }
	/* append long name (= unix name) to drive */
	lstrcpynA(full_name.short_name+2,p,MAX_PATHNAME_LEN-3);
	/* append name to treat */
	namelen= strlen(full_name.short_name);
	p = (char*)name;
	if (driveletter)
	  p += +2; /* skip drive name when appending */
	if (namelen +2  + strlen(p) > MAX_PATHNAME_LEN)
	  {
	    FIXME("internal error: buffer too small\n");
	     return 0;
	  }
	full_name.short_name[namelen++] ='\\';
	full_name.short_name[namelen] = 0;
	lstrcpynA(full_name.short_name +namelen,p,MAX_PATHNAME_LEN-namelen);
      }
    /* reverse all slashes */
    for (p=full_name.short_name;
	 p < full_name.short_name+strlen(full_name.short_name);
	 p++)
      {
	if ( *p == '/' )
	  *p = '\\';
      }
     /* Use memmove, as areas overlap */
     /* Delete .. */
    while ((p = strstr(full_name.short_name,"\\..\\")))
      {
	if (p > full_name.short_name+2)
	  {
	    *p = 0;
	    q = strrchr(full_name.short_name,'\\');
	    memmove(q+1,p+4,strlen(p+4)+1);
	  }
	else
	  {
	    memmove(full_name.short_name+3,p+4,strlen(p+4)+1);
	  }
      }
    if ((full_name.short_name[2]=='.')&&(full_name.short_name[3]=='.'))
	{
	  /* This case istn't treated yet : c:..\test */
	  memmove(full_name.short_name+2,full_name.short_name+4,
		  strlen(full_name.short_name+4)+1);
	}
     /* Delete . */
    while ((p = strstr(full_name.short_name,"\\.\\")))
      {
	*(p+1) = 0;
	memmove(p+1,p+3,strlen(p+3)+1);
Uwe Bonnes's avatar
Uwe Bonnes committed
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!(DRIVE_GetFlags(drive) & DRIVE_CASE_PRESERVING))
        for (p = full_name.short_name; *p; p++) *p = FILE_toupper(*p);
    namelen=strlen(full_name.short_name);
    if (!strcmp(full_name.short_name+namelen-3,"\\.."))
	{
	  /* one more strange case: "c:\test\test1\.." 
	   return "c:\test" */
	  *(full_name.short_name+namelen-3)=0;
	  q = strrchr(full_name.short_name,'\\');
	  *q =0;
	}
    if (full_name.short_name[namelen-1]=='.')
	full_name.short_name[(namelen--)-1] =0;
    if (!driveletter)
      if (full_name.short_name[namelen-1]=='\\')
	full_name.short_name[(namelen--)-1] =0;
    TRACE("got %s\n",full_name.short_name);

Uwe Bonnes's avatar
Uwe Bonnes committed
    /* 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 win95osr2, bon 001118)
Uwe Bonnes's avatar
Uwe Bonnes committed
    . */
    ret = strlen(full_name.short_name);
Uwe Bonnes's avatar
Uwe Bonnes committed
    if (ret >= len )
      {
	/* don't touch anything when the buffer is not large enough */
        SetLastError( ERROR_INSUFFICIENT_BUFFER );
	return ret+1;
      }
            MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, (LPWSTR)result, len );
	    lstrcpynA( result, full_name.short_name, len );
    TRACE("returning '%s'\n", full_name.short_name );
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           GetFullPathNameA   (KERNEL32.@)
 * 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;
}


/***********************************************************************
 *           GetFullPathNameW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
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 + strlenW(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;

/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
 *           wine_get_unix_file_name (KERNEL32.@) Not a Windows API
 *
 * Return the full Unix file name for a given path.
 */
BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
{
    BOOL ret;
    DOS_FULL_NAME path;
    if ((ret = DOSFS_GetFullName( dos, FALSE, &path ))) lstrcpynA( buffer, path.long_name, len );
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
{
    DWORD attr = info->attr | FA_UNUSED | FA_ARCHIVE | FA_RDONLY | FILE_ATTRIBUTE_SYMLINK;
    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;
        RtlSecondsSince1970ToTime( (time_t)0, &entry->ftCreationTime );
        RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastAccessTime );
        RtlSecondsSince1970ToTime( (time_t)0, &entry->ftLastWriteTime );
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++;
        TRACE("returning %s (%s) as label\n",
               entry->cFileName, entry->cAlternateFileName);
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;
        }
        if ((fileinfo.dwFileAttributes & FILE_ATTRIBUTE_SYMLINK) &&
            (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
        {
            static int show_dir_symlinks = -1;
            if (show_dir_symlinks == -1)
                show_dir_symlinks = PROFILE_GetWineIniBool("wine", "ShowDirSymlinks", 0);
            if (!show_dir_symlinks) 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)) _strlwr( 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;
Alexandre Julliard's avatar
Alexandre Julliard committed
    LPCSTR short_name, long_name;
    int count;

Alexandre Julliard's avatar
Alexandre Julliard committed

    /* 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) );
    }

Alexandre Julliard's avatar
Alexandre Julliard committed

    return count;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
/*************************************************************************
 *           FindFirstFileExA  (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
HANDLE WINAPI FindFirstFileExA(
	LPCSTR lpFileName,
	FINDEX_INFO_LEVELS fInfoLevelId,
	LPVOID lpFindFileData,
	FINDEX_SEARCH_OPS fSearchOp,
	LPVOID lpSearchFilter,
	DWORD dwAdditionalFlags)
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
    FIND_FIRST_INFO *info;
    
    if ((fSearchOp != FindExSearchNameMatch) || (dwAdditionalFlags != 0))
    {
        FIXME("options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags );
        return INVALID_HANDLE_VALUE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
      case FindExInfoStandard:
        {
          WIN32_FIND_DATAA * data = (WIN32_FIND_DATAA *) lpFindFileData;
          data->dwReserved0 = data->dwReserved1 = 0x0;
          if (!lpFileName) return 0;
          if (!DOSFS_GetFullName( lpFileName, FALSE, &full_name )) break;
          if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;
          info = (FIND_FIRST_INFO *)GlobalLock( handle );
          info->path = HeapAlloc( GetProcessHeap(), 0, strlen(full_name.long_name)+1 );
          strcpy( info->path, full_name.long_name );
          info->long_mask = strrchr( info->path, '/' );
          *(info->long_mask++) = '\0';
          info->short_mask = NULL;
          info->attr = 0xff;
          if (lpFileName[0] && (lpFileName[1] == ':'))
              info->drive = FILE_toupper(*lpFileName) - 'A';
          else info->drive = DRIVE_GetCurrentDrive();
          info->cur_pos = 0;

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

          GlobalUnlock( handle );
          if (!FindNextFileA( handle, data ))
          {
              FindClose( handle );
              SetLastError( ERROR_NO_MORE_FILES );
              break;
          }
          return handle;
        }
        break;
      default:
        FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    return INVALID_HANDLE_VALUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
}

/*************************************************************************
 *           FindFirstFileA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
HANDLE WINAPI FindFirstFileA(
	LPCSTR lpFileName,
	WIN32_FIND_DATAA *lpFindData )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindData,
                            FindExSearchNameMatch, NULL, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
}

/*************************************************************************
 *           FindFirstFileExW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
HANDLE WINAPI FindFirstFileExW(
	LPCWSTR lpFileName,
	FINDEX_INFO_LEVELS fInfoLevelId,
	LPVOID lpFindFileData,
	FINDEX_SEARCH_OPS fSearchOp,
	LPVOID lpSearchFilter,
	DWORD dwAdditionalFlags)
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    LPVOID _lpFindFileData;
    LPSTR pathA;

    switch(fInfoLevelId)
    {
      case FindExInfoStandard:
        {
          _lpFindFileData = &dataA;
        }
        break;
      default:
        FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
	return INVALID_HANDLE_VALUE;
    }

    pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName );
    handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
    HeapFree( GetProcessHeap(), 0, pathA );
    if (handle == INVALID_HANDLE_VALUE) return handle;
    
    switch(fInfoLevelId)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
      case FindExInfoStandard:
        {
          WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData;
          dataW->dwFileAttributes = dataA.dwFileAttributes;
          dataW->ftCreationTime   = dataA.ftCreationTime;
          dataW->ftLastAccessTime = dataA.ftLastAccessTime;
          dataW->ftLastWriteTime  = dataA.ftLastWriteTime;
          dataW->nFileSizeHigh    = dataA.nFileSizeHigh;
          dataW->nFileSizeLow     = dataA.nFileSizeLow;
          MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
                               dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) );
          MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
                               dataW->cAlternateFileName,
                               sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) );
        }
        break;
      default:
        FIXME("fInfoLevelId 0x%08x not implemented\n", fInfoLevelId );
        return INVALID_HANDLE_VALUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
    }
    return handle;
}

/*************************************************************************
 *           FindFirstFileW   (KERNEL32.@)
 */
HANDLE WINAPI FindFirstFileW( LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindData )
{
    return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindData,
                            FindExSearchNameMatch, NULL, 0);
}
Alexandre Julliard's avatar
Alexandre Julliard committed

/*************************************************************************
 *           FindNextFileA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
BOOL WINAPI FindNextFileA( HANDLE handle, WIN32_FIND_DATAA *data )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    FIND_FIRST_INFO *info;

    if ((handle == INVALID_HANDLE_VALUE) || 
       !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        SetLastError( ERROR_INVALID_HANDLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return FALSE;
    }
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;
}


/*************************************************************************
 *           FindNextFileW   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
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;
    MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1,
                         data->cFileName, sizeof(data->cFileName)/sizeof(WCHAR) );
    MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1,
                         data->cAlternateFileName,
                         sizeof(data->cAlternateFileName)/sizeof(WCHAR) );
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE;
}

/*************************************************************************
 *           FindClose   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
BOOL WINAPI FindClose( HANDLE handle )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    FIND_FIRST_INFO *info;

    if ((handle == INVALID_HANDLE_VALUE) ||
        !(info = (FIND_FIRST_INFO *)GlobalLock( handle )))
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        SetLastError( ERROR_INVALID_HANDLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
        return FALSE;
    }
    __TRY
    {
        if (info->dir) DOSFS_CloseDir( info->dir );
        if (info->path) HeapFree( GetProcessHeap(), 0, info->path );
    }
    __EXCEPT(page_fault)
    {
        WARN("Illegal handle %x\n", handle);
        SetLastError( ERROR_INVALID_HANDLE );
        return FALSE;
    }
    __ENDTRY
    GlobalUnlock( handle );
    GlobalFree( handle );
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE;
}

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. */