dir.cpp revision 6f82db701c69ae6925eb7e8bf1dfe25c9f951bdd
/* $Id$ */
/** @file
* IPRT - Directory Manipulation, Part 1.
*/
/*
* Copyright (C) 2006-2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP RTLOGGROUP_DIR
#define RTDIR_AGNOSTIC
static bool rtDirFilterWinNtMatchDosStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
static bool rtDirFilterWinNtMatchStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter);
{
/*
* Resolve the path.
*/
char szAbsPath[RTPATH_MAX];
if (RT_FAILURE(rc))
return rc;
/*
* Iterate the path components making sure each of them exists.
*/
/* skip volume name */
/* skip the root slash if any */
if ( psz[0] == '/'
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
|| psz[0] == '\\'
#endif
)
psz++;
/* iterate over path components. */
do
{
/* the next component is NULL, stop iterating */
if (!*psz)
break;
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
#else
#endif
if (psz)
*psz = '\0';
/*
* ASSUME that RTDirCreate will return VERR_ALREADY_EXISTS and not VERR_ACCESS_DENIED in those cases
* where the directory exists but we don't have write access to the parent directory.
*/
if (rc == VERR_ALREADY_EXISTS)
rc = VINF_SUCCESS;
if (!psz)
break;
*psz++ = RTPATH_DELIMITER;
} while (RT_SUCCESS(rc));
return rc;
}
/**
* Filter a the filename in the against a filter.
*
* @returns true if the name matches the filter.
* @returns false if the name doesn't match filter.
* @param pDir The directory handle.
* @param pszName The path to match to the filter.
*/
{
/*
* Walk the string and compare.
*/
do
{
AssertRCReturn(rc, false);
return false;
} while (uc);
return true;
}
/**
* Matches end of name.
*/
{
|| ucFilter == '<'
|| ucFilter == '*'
|| ucFilter == '"')
puszFilter++;
return !ucFilter;
}
/**
* Recursive star matching.
* Practically the same as normal star, except that the dos star stops
* when hitting the last dot.
*
* @returns true on match.
* @returns false on miss.
*/
static bool rtDirFilterWinNtMatchDosStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter)
{
/*
* If there is no dos star, we should work just like the NT star.
* Since that's generally faster algorithms, we jump down to there if we can.
*/
if (!pszDosDot)
/*
* Inspect the next filter char(s) until we find something to work on.
*/
switch (ucFilter)
{
/*
* The star expression is the last in the pattern.
* We're fine if the name ends with a dot.
*/
case '\0':
return !pszDosDot[1];
/*
* Simplified by brute force.
*/
case '>': /* dos question mark */
case '?':
case '*':
case '<': /* dos star */
case '"': /* dos dot */
{
puszFilter--;
do
{
return true;
/* backtrack and do the current char. */
}
/*
* Ok, we've got zero or more characters.
* We'll try match starting at each occurrence of this character.
*/
default:
{
return true;
do
{
return true;
return false;
}
}
/* won't ever get here! */
}
/**
* Recursive star matching.
*
* @returns true on match.
* @returns false on miss.
*/
static bool rtDirFilterWinNtMatchStar(unsigned iDepth, RTUNICP uc, const char *pszNext, PCRTUNICP puszFilter)
{
/*
* Inspect the next filter char(s) until we find something to work on.
*/
for (;;)
{
switch (ucFilter)
{
/*
* The star expression is the last in the pattern.
* Cool, that means we're done!
*/
case '\0':
return true;
/*
* Just in case (doubt we ever get here), just merge it with the current one.
*/
case '*':
break;
/*
* Skip a fixed number of chars.
* Figure out how many by walking the filter ignoring '*'s.
*/
case '?':
{
unsigned cQms = 1;
{
puszFilter++;
}
do
{
if (!uc)
return false;
} while (--cQms > 0);
/* done? */
if (!ucFilter)
return true;
break;
}
/*
* The simple way is to try char by char and match the remaining
* expression. If it's trailing we're done.
*/
case '>': /* dos question mark */
{
return true;
do
{
return true;
} while (uc);
/* backtrack and do the current char. */
}
/*
* This bugger is interesting.
* Time for brute force. Iterate the name char by char.
*/
case '<':
{
do
{
return true;
} while (uc);
return false;
}
/*
* This guy matches a '.' or the end of the name.
* It's very simple if the rest of the filter expression also matches eon.
*/
case '"':
return true;
ucFilter = '.';
/* fall thru */
/*
* Ok, we've got zero or more characters.
* We'll try match starting at each occurrence of this character.
*/
default:
{
do
{
return true;
} while (uc);
return false;
}
}
} /* for (;;) */
/* won't ever get here! */
}
/**
* Filter a the filename in the against a filter.
*
* The rules are as follows:
* '?' Matches exactly one char.
* '*' Matches zero or more chars.
* '<' The dos star, matches zero or more chars except the DOS dot.
* '>' The dos question mark, matches one char, but dots and end-of-name eats them.
* '"' The dos dot, matches a dot or end-of-name.
*
* @returns true if the name matches the filter.
* @returns false if the name doesn't match filter.
* @param iDepth The recursion depth.
* @param pszName The path to match to the filter.
* @param puszFilter The filter string.
*/
{
/*
* Walk the string and match it up char by char.
*/
do
{
switch (ucFilter)
{
/* Exactly one char. */
case '?':
if (!uc)
return false;
break;
/* One char, but the dos dot and end-of-name eats '>' and '<'. */
case '>': /* dos ? */
if (!uc)
return rtDirFilterWinNtMatchEon(puszFilter);
if (uc == '.')
{
puszFilter++;
++puszFilter;
else /* the does question mark doesn't match '.'s, so backtrack. */
}
break;
/* Match a dot or the end-of-name. */
case '"': /* dos '.' */
if (uc != '.')
{
if (uc)
return false;
return rtDirFilterWinNtMatchEon(puszFilter);
}
break;
/* zero or more */
case '*':
case '<': /* dos '*' */
/* uppercased match */
default:
{
return false;
break;
}
}
} while (uc);
return true;
}
/**
* Filter a the filename in the against a filter.
*
* @returns true if the name matches the filter.
* @returns false if the name doesn't match filter.
* @param pDir The directory handle.
* @param pszName The path to match to the filter.
*/
{
}
/**
* Initializes a WinNt like wildcard filter.
*
* @returns Pointer to the filter function.
* @returns NULL if the filter doesn't filter out anything.
* @param pDir The directory handle (not yet opened).
*/
{
/*
* Check for the usual * and <"< (*.* in DOS language) patterns.
*/
)
return NULL;
/*
* Uppercase the expression, also do a little optimizations when possible.
*/
bool fHaveWildcards = false;
unsigned iRead = 0;
unsigned iWrite = 0;
{
if (uc == '*')
{
fHaveWildcards = true;
/* remove extra stars. */
iRead++;
}
fHaveWildcards = true;
else
}
return fHaveWildcards
}
/**
* Common worker for opening a directory.
*
* @returns IPRT status code.
* @param ppDir Where to store the directory handle.
* @param pszPath The specified path.
* @param pszFilter Pointer to where the filter start in the path. NULL if no filter.
* @param enmFilter The type of filter to apply.
*/
static int rtDirOpenCommon(PRTDIR *ppDir, const char *pszPath, const char *pszFilter, RTDIRFILTER enmFilter)
{
/*
* Expand the path.
*
* The purpose of this exercise to have the abs path around
* for querying extra information about the objects we list.
* As a sideeffect we also validate the path here.
*/
int rc;
if (!pszFilter)
{
cbFilter = cucFilter0 = 0;
}
else
{
{
/* yea, I'm lazy. sue me. */
if (!pszTmp)
return VERR_NO_MEMORY;
}
else
}
if (RT_FAILURE(rc))
return rc;
/* add trailing '/' if missing. */
{
}
/*
* Allocate and initialize the directory handle.
*
* The posix definition of Data.d_name allows it to be < NAME_MAX + 1,
* thus the horrible ugliness here. Solaris uses d_name[1] for instance.
*/
+ cucFilter0 * sizeof(RTUNICP)
+ cbFilter
if (!pDir)
return VERR_NO_MEMORY;
/* initialize it */
if (cbFilter)
{
}
else
{
}
switch (enmFilter)
{
default:
case RTDIRFILTER_NONE:
break;
case RTDIRFILTER_WINNT:
break;
case RTDIRFILTER_UNIX:
break;
case RTDIRFILTER_UNIX_UPCASED:
break;
}
pDir->fDataUnread = false;
/*
* Hand it over to the native part.
*/
if (RT_SUCCESS(rc))
else
return rc;
}
{
/*
* Validate input.
*/
/*
* Take common cause with RTDirOpenFiltered().
*/
return rc;
}
RTDECL(int) RTDirOpenFiltered(PRTDIR *ppDir, const char *pszPath, RTDIRFILTER enmFilter, uint32_t fOpen)
{
/*
* Validate input.
*/
switch (enmFilter)
{
case RTDIRFILTER_UNIX:
case RTDIRFILTER_UNIX_UPCASED:
return VERR_NOT_IMPLEMENTED;
case RTDIRFILTER_NONE:
case RTDIRFILTER_WINNT:
break;
default:
}
/*
* Find the last component, i.e. where the filter criteria starts and the dir name ends.
*/
const char *pszFilter;
if (enmFilter == RTDIRFILTER_NONE)
else
{
if (!pszFilter) /* trailing slash => directory to read => no filter. */
}
/*
* Call worker common with RTDirOpen which will verify the path, allocate
* and initialize the handle, and finally call the backend.
*/
LogFlow(("RTDirOpenFiltered(%p:{%p}, %p:{%s}, %d): return %Rrc\n",
return rc;
}
{
char szPath[RTPATH_MAX];
if (RT_SUCCESS(rc))
{
}
return rc;
}
{
if (RT_FAILURE(rc))
return rc;
else
return VINF_SUCCESS;
}
RTDECL(int) RTDirQueryUnknownType(const char *pszComposedName, bool fFollowSymlinks, RTDIRENTRYTYPE *penmType)
{
if ( *penmType != RTDIRENTRYTYPE_UNKNOWN
&& ( !fFollowSymlinks
|| *penmType != RTDIRENTRYTYPE_SYMLINK))
return VINF_SUCCESS;
}
{
return false;
return true;
return false;
}
{
return false;
return true;
return false;
}