Skip to content
Snippets Groups Projects
dos_fs.c 80.7 KiB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
 * Return FALSE if one of the path components does not exist. The last path
Alexandre Julliard's avatar
Alexandre Julliard committed
 * component is only checked if 'check_last' is non-zero.
Alexandre Julliard's avatar
Alexandre Julliard committed
 * The buffers pointed to by 'long_buf' and 'short_buf' must be
 * at least MAX_PATHNAME_LEN long.
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
BOOL DOSFS_GetFullName( LPCWSTR name, BOOL check_last, DOS_FULL_NAME *full )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    UINT flags, codepage;
    char *p_l, *root;
    LPWSTR p_s;
    static const WCHAR driveA_rootW[] = {'A',':','\\',0};
    static const WCHAR dos_rootW[] = {'\\',0};
    TRACE("%s (last=%d)\n", debugstr_w(name), check_last );
    if ((!*name) || (*name=='\n'))
    { /* error code for Win98 */
        SetLastError(ERROR_BAD_PATHNAME);
        return FALSE;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
    if ((full->drive = DOSFS_GetPathDrive( &name )) == -1) return FALSE;
    flags = DRIVE_GetFlags( full->drive );
    codepage = DRIVE_GetCodepage(full->drive);
    lstrcpynA( full->long_name, DRIVE_GetRoot( full->drive ),
Alexandre Julliard's avatar
Alexandre Julliard committed
                 sizeof(full->long_name) );
    if (full->long_name[1]) root = full->long_name + strlen(full->long_name);
    else root = full->long_name;  /* root directory */
    strcpyW( full->short_name, driveA_rootW );
Alexandre Julliard's avatar
Alexandre Julliard committed
    full->short_name[0] += full->drive;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if ((*name == '\\') || (*name == '/'))  /* Absolute path */
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        while ((*name == '\\') || (*name == '/')) name++;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        lstrcpynA( root + 1, DRIVE_GetUnixCwd( full->drive ),
Alexandre Julliard's avatar
Alexandre Julliard committed
                     sizeof(full->long_name) - (root - full->long_name) - 1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
        if (root[1]) *root = '/';
        lstrcpynW( full->short_name + 3, DRIVE_GetDosCwd( full->drive ),
                   sizeof(full->short_name)/sizeof(full->short_name[0]) - 3 );
Alexandre Julliard's avatar
Alexandre Julliard committed
    p_l = full->long_name[1] ? full->long_name + strlen(full->long_name)
                             : full->long_name;
    p_s = full->short_name[3] ? full->short_name + strlenW(full->short_name)
Alexandre Julliard's avatar
Alexandre Julliard committed
                              : full->short_name + 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
    found = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
    while (*name && found)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
        /* Check for '.' and '..' */

        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++;
                while ((p_l > root) && (*p_l != '/')) p_l--;
                while ((p_s > full->short_name + 2) && (*p_s != '\\')) p_s--;
                *p_l = *p_s = '\0';  /* Remove trailing separator */
                continue;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
Alexandre Julliard's avatar
Alexandre Julliard committed

        /* Make sure buffers are large enough */

        if ((p_s >= full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 14) ||
Alexandre Julliard's avatar
Alexandre Julliard committed
            (p_l >= full->long_name + sizeof(full->long_name) - 1))
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
            SetLastError( ERROR_PATH_NOT_FOUND );
Alexandre Julliard's avatar
Alexandre Julliard committed
            return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
Alexandre Julliard's avatar
Alexandre Julliard committed

        /* Get the long and short name matching the file name */

        if ((found = DOSFS_FindUnixName( full, name, p_l + 1,
Alexandre Julliard's avatar
Alexandre Julliard committed
                         sizeof(full->long_name) - (p_l - full->long_name) - 1,
                         p_s + 1, !(flags & DRIVE_CASE_SENSITIVE) )))
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            *p_l++ = '/';
            p_l   += strlen(p_l);
            *p_s++ = '\\';
            p_s   += strlenW(p_s);
Alexandre Julliard's avatar
Alexandre Julliard committed
            while (!IS_END_OF_NAME(*name)) name++;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
        else if (!check_last)
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
            *p_l++ = '/';
            *p_s++ = '\\';
            while (!IS_END_OF_NAME(*name) &&
                   (p_s < full->short_name + sizeof(full->short_name)/sizeof(full->short_name[0]) - 1) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
                   (p_l < full->long_name + sizeof(full->long_name) - 1))
            {
                WCHAR wch;
                *p_s++ = tolowerW(*name);
Alexandre Julliard's avatar
Alexandre Julliard committed
                /* If the drive is case-sensitive we want to create new */
                /* files in lower-case otherwise we can't reopen them   */
                /* under the same short name. */
                if (flags & DRIVE_CASE_SENSITIVE) wch = tolowerW(*name);
                else wch = *name;
                p_l += WideCharToMultiByte(codepage, 0, &wch, 1, p_l, 2, NULL, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
                name++;
            }
	    /* Ignore trailing dots and spaces */
	    while(p_l[-1] == '.' || p_l[-1] == ' ') {
		--p_l;
		--p_s;
	    }
            *p_l = '\0';
            *p_s = '\0';
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
        while ((*name == '\\') || (*name == '/')) name++;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!found)
    {
        if (check_last)
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
            SetLastError( ERROR_FILE_NOT_FOUND );
Alexandre Julliard's avatar
Alexandre Julliard committed
            return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
        }
        if (*name)  /* Not last */
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
            SetLastError( ERROR_PATH_NOT_FOUND );
Alexandre Julliard's avatar
Alexandre Julliard committed
            return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!full->long_name[0]) strcpy( full->long_name, "/" );
    if (!full->short_name[2]) strcpyW( full->short_name + 2, dos_rootW );
    TRACE("returning %s = %s\n", full->long_name, debugstr_w(full->short_name) );
Alexandre Julliard's avatar
Alexandre Julliard committed
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           GetShortPathNameW   (KERNEL32.@)
 *
 * NOTES
 *  observed:
 *  longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
 *  longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
 * more observations ( with NT 3.51 (WinDD) ):
 * longpath <= 8.3 -> just copy longpath to shortpath
 * longpath > 8.3  ->
 *             a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
 *             b) file does exist     -> set the short filename.
 * - trailing slashes are reproduced in the short name, even if the
 *   file is not a directory
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 GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    DOS_FULL_NAME full_name;
    WCHAR tmpshortpath[MAX_PATHNAME_LEN];
    const WCHAR *p;
Peter Ganten's avatar
Peter Ganten committed
    DWORD sp = 0, lp = 0;
    int drive;
    DWORD tmplen;
Peter Ganten's avatar
Peter Ganten committed
    UINT flags;
    BOOL unixabsolute = *longpath == '/';
    TRACE("%s\n", debugstr_w(longpath));

    if (!longpath) {
      SetLastError(ERROR_INVALID_PARAMETER);
      return 0;
    }
    if (!longpath[0]) {
      SetLastError(ERROR_BAD_PATHNAME);
      return 0;
    /* check for drive letter */
    if (!unixabsolute && longpath[1] == ':' ) {
      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 );
    if (unixabsolute && drive != DRIVE_GetCurrentDrive()) {
      tmpshortpath[0] = drive + 'A';
      tmpshortpath[1] = ':';
      sp = 2;
    }
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++;
      tmplen = 0;
      for(p = longpath + lp; *p && *p != '/' && *p != '\\'; p++)
          tmplen++;
      lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
Peter Ganten's avatar
Peter Ganten committed
      /* 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 ) ) {
	strcpyW(tmpshortpath + sp, strrchrW(full_name.short_name, '\\') + 1);
	sp += strlenW(tmpshortpath + sp);
Peter Ganten's avatar
Peter Ganten committed
	lp += tmplen;
	continue;
      }

      TRACE("not found!\n" );
      SetLastError ( ERROR_FILE_NOT_FOUND );
      return 0;
    tmplen = strlenW(tmpshortpath) + 1;
    if (tmplen <= shortlen)
    {
        strcpyW(shortpath, tmpshortpath);
        TRACE("returning %s\n", debugstr_w(shortpath));
        tmplen--; /* length without 0 */
    }
Peter Ganten's avatar
Peter Ganten committed
    return tmplen;
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           GetShortPathNameA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    UNICODE_STRING longpathW;
    WCHAR shortpathW[MAX_PATH];
    DWORD ret, retW;

    if (!longpath)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }
    TRACE("%s\n", debugstr_a(longpath));

    if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return 0;
    }
    retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
    if (!retW)
        ret = 0;
    else if (retW > MAX_PATH)
    {
        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        ret = 0;
    }
    else
    {
        ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
        if (ret <= shortlen)
        {
            WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
            ret--; /* length without 0 */
        }
    }

    RtlFreeUnicodeString(&longpathW);
Alexandre Julliard's avatar
Alexandre Julliard committed
/***********************************************************************
 *           GetLongPathNameW   (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 GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    DOS_FULL_NAME full_name;
    const char *root;
    LPWSTR p;
    int drive;
    UINT codepage;
    DWORD ret, len = 0;

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

    TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);

    if(shortpath[0]=='\\' && shortpath[1]=='\\')
    {
        ERR("UNC pathname %s\n",debugstr_w(shortpath));
        lstrcpynW( longpath, full_name.short_name, longlen );
        return strlenW(longpath);
Alexandre Julliard's avatar
Alexandre Julliard committed
    if (!DOSFS_GetFullName( shortpath, TRUE, &full_name )) return 0;
    root = full_name.long_name;
    drive = DRIVE_FindDriveRoot(&root);
    codepage = DRIVE_GetCodepage(drive);

    ret = MultiByteToWideChar(codepage, 0, root, -1, NULL, 0);
    ret += 3; /* A:\ */
    /* reproduce terminating slash */
    if (ret > 4) /* if not drive root */
    {
        len = strlenW(shortpath);
        if (shortpath[len - 1] == '\\' || shortpath[len - 1] == '/')
            len = 1;
    }
    ret += len;
    if (ret <= longlen)
    {
        longpath[0] = 'A' + drive;
        longpath[1] = ':';
        MultiByteToWideChar(codepage, 0, root, -1, longpath + 2, longlen - 2);
        for (p = longpath; *p; p++) if (*p == '/') *p = '\\';
        if (len)
            longpath[ret - 2] = '\\';
            longpath[ret - 1] = 0;
        TRACE("returning %s\n", debugstr_w(longpath));
        ret--; /* length without 0 */
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           GetLongPathNameA   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    UNICODE_STRING shortpathW;
    WCHAR longpathW[MAX_PATH];
    DWORD ret, retW;

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

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

    if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return 0;
    }
    retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);

    if (!retW)
        ret = 0;
    else if (retW > MAX_PATH)
    {
        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        ret = 0;
    }
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
        if (ret <= longlen)
        {
            WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
            ret--; /* length without 0 */
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
    }

    RtlFreeUnicodeString(&shortpathW);
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
 *
 * A test for GetFullPathName with many pathological cases
Huw D. M. Davies's avatar
Huw D. M. Davies committed
 * now gives identical output for Wine and OSR2
Alexandre Julliard's avatar
Alexandre Julliard committed
 */
static DWORD DOSFS_DoGetFullPathName( LPCWSTR name, DWORD len, LPWSTR result )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    LPWSTR p, q;
    char *p_l;
    WCHAR drivecur[] = {'C',':','.',0};
    WCHAR driveletter=0;
    static const WCHAR bkslashW[] = {'\\',0};
    static const WCHAR dotW[] = {'.',0};
    static const WCHAR updir_slashW[] = {'\\','.','.','\\',0};
    static const WCHAR curdirW[] = {'\\','.','\\',0};
    static const WCHAR updirW[] = {'\\','.','.',0};
    if (!name[0])
    {
        SetLastError(ERROR_BAD_PATHNAME);
        return 0;
    }
    TRACE("passed %s\n", debugstr_w(name));
    if (name[1]==':')
      /*drive letter given */
    if ((name[1]==':') && ((name[2]=='\\') || (name[2]=='/')))
      /*absolute path given */
Uwe Bonnes's avatar
Uwe Bonnes committed
      {
        strncpyW(full_name.short_name, name, MAX_PATHNAME_LEN);
        full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
        drive = toupperW(name[0]) - 'A';
      }
    else
      {
	if (driveletter)
	  drivecur[0]=driveletter;
        else if ((name[0]=='\\') || (name[0]=='/'))
          strcpyW(drivecur, bkslashW);
	  strcpyW(drivecur, dotW);
	if (!DOSFS_GetFullName( drivecur, FALSE, &full_name ))
	  {
	    FIXME("internal: error getting drive/path\n");
	    return 0;
	  }
	/* find path that drive letter substitutes*/
	drive = toupperW(full_name.short_name[0]) - 'A';
	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_l = full_name.long_name;
	    p_l = full_name.long_name + strlen(root);
	/* append long name (= unix name) to drive */
	MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, p_l, -1,
                            full_name.short_name + 2, MAX_PATHNAME_LEN - 3);
	/* append name to treat */
	namelen= strlenW(full_name.short_name);
	p = (LPWSTR)name;
	  p += 2; /* skip drive name when appending */
	if (namelen + 2 + strlenW(p) > MAX_PATHNAME_LEN)
	  {
	    FIXME("internal error: buffer too small\n");
	     return 0;
	  }
	full_name.short_name[namelen++] ='\\';
	full_name.short_name[namelen] = 0;
	strncpyW(full_name.short_name + namelen, p, MAX_PATHNAME_LEN - namelen);
	full_name.short_name[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
      }
    /* reverse all slashes */
    for (p=full_name.short_name;
	 p < full_name.short_name + strlenW(full_name.short_name);
     /* Use memmove, as areas overlap */
    while ((p = strstrW(full_name.short_name, updir_slashW)))
      {
	if (p > full_name.short_name+2)
	  {
	    *p = 0;
            q = strrchrW(full_name.short_name, '\\');
            memmove(q+1, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
            memmove(full_name.short_name+3, p+4, (strlenW(p+4)+1) * sizeof(WCHAR));
	  }
      }
    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,
                  (strlenW(full_name.short_name+4)+1) * sizeof(WCHAR));
    while ((p = strstrW(full_name.short_name, curdirW)))
        memmove(p+1, p+3, (strlenW(p+3)+1) * sizeof(WCHAR));
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 = toupperW(*p);
    namelen = strlenW(full_name.short_name);
    if (!strcmpW(full_name.short_name+namelen-3, updirW))
	  /* one more strange case: "c:\test\test1\.."
	   return "c:\test" */
	  *(full_name.short_name+namelen-3)=0;
          q = strrchrW(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", debugstr_w(full_name.short_name));
    /* 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 = strlenW(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;
      }
        strncpyW( result, full_name.short_name, len );
        result[len - 1] = 0; /* ensure 0 termination */
    TRACE("returning %s\n", debugstr_w(full_name.short_name) );
Alexandre Julliard's avatar
Alexandre Julliard committed
}


/***********************************************************************
 *           GetFullPathNameA   (KERNEL32.@)
 *   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
{
    UNICODE_STRING nameW;
    WCHAR bufferW[MAX_PATH];
    DWORD ret, retW;

    if (!name)
Alexandre Julliard's avatar
Alexandre Julliard committed
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }
    if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return 0;
    }

    retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);

    if (!retW)
        ret = 0;
    else if (retW > MAX_PATH)
    {
        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        ret = 0;
    }
    else
    {
        ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
        if (ret <= len)
            WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
            ret--; /* length without 0 */

            if (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
    }

    RtlFreeUnicodeString(&nameW);
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
{
    DWORD ret = DOSFS_DoGetFullPathName( name, len, buffer );
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.
 * FIXME: convert dos file name to unicode
 */
BOOL WINAPI wine_get_unix_file_name( LPCSTR dos, LPSTR buffer, DWORD len )
{
    BOOL ret;
    DOS_FULL_NAME path;
    WCHAR dosW[MAX_PATHNAME_LEN];

    MultiByteToWideChar(CP_ACP, 0, dos, -1, dosW, MAX_PATHNAME_LEN);
    ret = DOSFS_GetFullName( dosW, FALSE, &path );
    if (ret && len)
    {
        strncpy( buffer, path.long_name, len );
        buffer[len - 1] = 0; /* ensure 0 termination */
    }
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_DATAW *entry )
Alexandre Julliard's avatar
Alexandre Julliard committed
{
    DWORD 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;
    LPCWSTR long_name, short_name;
Alexandre Julliard's avatar
Alexandre Julliard committed
    BY_HANDLE_FILE_INFORMATION fileinfo;
    WCHAR 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, (LARGE_INTEGER *)&entry->ftCreationTime );
        RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&entry->ftLastAccessTime );
        RtlSecondsSince1970ToTime( (time_t)0, (LARGE_INTEGER *)&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 );
        strcpyW( entry->cAlternateFileName, entry->cFileName );
Alexandre Julliard's avatar
Alexandre Julliard committed
        info->cur_pos++;
        TRACE("returning %s (%s) as label\n",
               debugstr_w(entry->cFileName), debugstr_w(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);

    while (DOSFS_ReadDir( info->u.dos_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 */

        if (info->long_mask && *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 */
        WideCharToMultiByte(DRIVE_GetCodepage(info->drive), 0, long_name, -1,
                            p, sizeof(buffer) - (int)(p - buffer), NULL, NULL);
        if (!FILE_Stat( buffer, &fileinfo, &is_symlink ))
Alexandre Julliard's avatar
Alexandre Julliard committed
        {
            WARN("can't stat %s\n", buffer);
Alexandre Julliard's avatar
Alexandre Julliard committed
            continue;
        }
        if (is_symlink && (fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
            static const WCHAR wineW[] = {'w','i','n','e',0};
            static const WCHAR ShowDirSymlinksW[] = {'S','h','o','w','D','i','r','S','y','m','l','i','n','k','s',0};
            static int show_dir_symlinks = -1;
            if (show_dir_symlinks == -1)
                show_dir_symlinks = PROFILE_GetWineIniBool(wineW, ShowDirSymlinksW, 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) );

        lstrcpynW( entry->cFileName, long_name, sizeof(entry->cFileName)/sizeof(entry->cFileName[0]) );
        if (!(flags & DRIVE_CASE_PRESERVING)) strlwrW( entry->cFileName );
        TRACE("returning %s (%s) %02lx %ld\n",
              debugstr_w(entry->cFileName), debugstr_w(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;
    LPCWSTR short_name, long_name;
Alexandre Julliard's avatar
Alexandre Julliard committed
    int count;
    UNICODE_STRING short_maskW, long_maskW;
    WIN32_FIND_DATAW entryW;
    TRACE("(%s, %s, %s, %x, %x, %x, %p)\n", debugstr_a(path),
          debugstr_a(short_mask), debugstr_a(long_mask), drive, attr, skip,
          entry);

    RtlCreateUnicodeStringFromAsciiz(&short_maskW, short_mask);
    RtlCreateUnicodeStringFromAsciiz(&long_maskW, long_mask);

Alexandre Julliard's avatar
Alexandre Julliard committed
    /* Check the cached directory */
    if (!(info.u.dos_dir && info.path == path && !strcmpW(info.short_mask, short_maskW.Buffer)
                   && !strcmpW(info.long_mask, long_maskW.Buffer) && info.drive == drive
Alexandre Julliard's avatar
Alexandre Julliard committed
                   && info.attr == attr && info.cur_pos <= skip))
Alexandre Julliard's avatar
Alexandre Julliard committed
        /* Not in the cache, open it anew */
        if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_dir );
Alexandre Julliard's avatar
Alexandre Julliard committed

        info.path = (LPSTR)path;
        RtlFreeHeap(GetProcessHeap(), 0, info.long_mask);
        RtlFreeHeap(GetProcessHeap(), 0, info.short_mask);
        info.long_mask = long_maskW.Buffer;
        info.short_mask = short_maskW.Buffer;
Alexandre Julliard's avatar
Alexandre Julliard committed
        info.attr = attr;
        info.drive = drive;
        info.cur_pos = 0;
        info.u.dos_dir = DOSFS_OpenDir( DRIVE_GetCodepage(drive), info.path );
    }
    else
    {
        RtlFreeUnicodeString(&short_maskW);
        RtlFreeUnicodeString(&long_maskW);
Alexandre Julliard's avatar
Alexandre Julliard committed
    }

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

    if (info.u.dos_dir && info.cur_pos == skip && DOSFS_FindNextEx( &info, &entryW ))
    {
        WideCharToMultiByte(CP_ACP, 0, entryW.cFileName, -1,
                            entry->cFileName, sizeof(entry->cFileName), NULL, NULL);
        WideCharToMultiByte(CP_ACP, 0, entryW.cAlternateFileName, -1,
                            entry->cAlternateFileName, sizeof(entry->cAlternateFileName), NULL, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
        count = info.cur_pos - skip;

        entry->dwFileAttributes = entryW.dwFileAttributes;
        entry->nFileSizeHigh    = entryW.nFileSizeHigh;
        entry->nFileSizeLow     = entryW.nFileSizeLow;
        entry->ftCreationTime   = entryW.ftCreationTime;
        entry->ftLastAccessTime = entryW.ftLastAccessTime;
        entry->ftLastWriteTime  = entryW.ftLastWriteTime;

Alexandre Julliard's avatar
Alexandre Julliard committed
    else
        count = 0;

    if (!count)
    {
        if (info.u.dos_dir) DOSFS_CloseDir( info.u.dos_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
/*************************************************************************
 *           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
{
Alexandre Julliard's avatar
Alexandre Julliard committed
    FIND_FIRST_INFO *info;
    if (!lpFileName)
    {
        SetLastError(ERROR_PATH_NOT_FOUND);
        return INVALID_HANDLE_VALUE;
    }

    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
    {
          WIN32_FIND_DATAW * data = (WIN32_FIND_DATAW *) lpFindFileData;
          char *p;
          INT long_mask_len;
          UINT codepage;

          data->dwReserved0 = data->dwReserved1 = 0x0;
          if (lpFileName[0] == '\\' && lpFileName[1] == '\\')
          {
              ERR("UNC path name\n");
              if (!(handle = GlobalAlloc(GMEM_MOVEABLE, sizeof(FIND_FIRST_INFO)))) break;

              info = (FIND_FIRST_INFO *)GlobalLock( handle );
              info->u.smb_dir = SMB_FindFirst(lpFileName);
              {
                 GlobalUnlock( handle );
                 GlobalFree(handle);
                 break;
              }

              info->drive = -1;

              GlobalUnlock( handle );
          }
          else
          {
            DOS_FULL_NAME full_name;

            if (lpFileName[0] && lpFileName[1] == ':')
            {
                /* don't allow root directories */
                if (!lpFileName[2] ||
                    ((lpFileName[2] == '/' || lpFileName[2] == '\\') && !lpFileName[3]))
                {
                    SetLastError(ERROR_FILE_NOT_FOUND);
                    return INVALID_HANDLE_VALUE;
                }
            }
            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 );

            codepage = DRIVE_GetCodepage(full_name.drive);
            p = strrchr( info->path, '/' );
            *p++ = '\0';
            long_mask_len = MultiByteToWideChar(codepage, 0, p, -1, NULL, 0);
            info->long_mask = HeapAlloc( GetProcessHeap(), 0, long_mask_len * sizeof(WCHAR) );
            MultiByteToWideChar(codepage, 0, p, -1, info->long_mask, long_mask_len);

            info->short_mask = NULL;
            info->attr = 0xff;
            info->drive = full_name.drive;
            info->cur_pos = 0;

            info->u.dos_dir = DOSFS_OpenDir( codepage, info->path );
            GlobalUnlock( handle );
          if (!FindNextFileW( handle, data ))
              SetLastError( ERROR_FILE_NOT_FOUND );