\file storage_support.cpp
Implementations of miscellaneous internal Storage Kit support functions.
*/
#include <new>
#include <ctype.h>
#include <string.h>
#include <StorageDefs.h>
#include <SupportDefs.h>
#include <syscalls.h>
#include "storage_support.h"
using std::nothrow;
namespace BPrivate {
namespace Storage {
\return \c true, if \a path is not \c NULL and absolute, \c false otherwise
*/
bool
is_absolute_path(const char *path)
{
return (path && path[0] == '/');
}
part of the path and the length of its directory path part.
The value returned in \a fullPath is guaranteed to be > 0, i.e. the
function always returns a non-empty directory path part. The leaf name
part may be empty though (i.e. \code leafStart == leafEnd \endcode), which
will happen, if the supplied path consists only of one component.
\param fullPath The path to be parsed.
\param dirEnd Reference to a variable into which the end index of the
directory part shall be written. The index is exclusive.
\param leafStart Reference to a variable into which the start index of
the leaf name part shall be written. The index is inclusive.
\param leafEnd Reference to a variable into which the end index of
the leaf name part shall be written. The index is exclusive.
\return \c B_OK, if everything went fine, B_BAD_VALUE, if the supplied
path is invalid.
*/
status_t
parse_path(const char *fullPath, int &dirEnd, int &leafStart, int &leafEnd)
{
if (!fullPath)
return B_BAD_VALUE;
int pathLen = strlen(fullPath);
if (pathLen == 0)
return B_BAD_VALUE;
int i = pathLen - 1;
while (i >= 0 && fullPath[i] == '/')
i--;
leafEnd = i + 1;
if (leafEnd == 0) {
dirEnd = leafStart = leafEnd = 1;
return B_OK;
}
while (i >= 0 && fullPath[i] != '/')
i--;
leafStart = i + 1;
if (leafStart == 0) {
dirEnd = leafStart = leafEnd;
return B_OK;
}
while (i >= 0 && fullPath[i] == '/')
i--;
dirEnd = i + 1;
if (dirEnd == 0)
dirEnd = 1;
return B_OK;
}
and its directory path part.
The value returned in \a fullPath is guaranteed to be > 0, i.e. the
function always returns a non-empty directory path part. The leaf name
part may be empty though (i.e. \code leafStart == leafEnd \endcode), which
will happen, if the supplied path consists only of one component.
\param fullPath The path to be parsed.
\param dirPath Pointer to a character array of size \c B_PATH_NAME_LENGTH
or greater, into which the directory part shall be written.
May be \c NULL.
\param leaf Pointer to a character array of size \c B_FILE_NAME_LENGTH
or greater, into which the leaf name part shall be written.
May be \c NULL.
\return \c B_OK, if everything went fine, B_BAD_VALUE, if the supplied
path is invalid.
*/
status_t
parse_path(const char *fullPath, char *dirPath, char *leaf)
{
int leafStart, leafEnd, dirEnd;
status_t error = parse_path(fullPath, dirEnd, leafStart, leafEnd);
if (error != B_OK)
return error;
if (dirEnd >= B_PATH_NAME_LENGTH
|| leafEnd - leafStart >= B_FILE_NAME_LENGTH) {
return B_NAME_TOO_LONG;
}
if (dirPath)
strlcpy(dirPath, fullPath, dirEnd + 1);
if (leaf)
strlcpy(leaf, fullPath + leafStart, leafEnd - leafStart + 1);
return B_OK;
}
static
void
internal_parse_path(const char *fullPath, int &leafStart, int &leafEnd,
int &pathEnd)
{
if (fullPath == NULL)
return;
enum PathParserState { PPS_START, PPS_LEAF } state = PPS_START;
int len = strlen(fullPath);
leafStart = -1;
leafEnd = -1;
pathEnd = -2;
bool loop = true;
for (int pos = len-1; ; pos--) {
if (pos < 0)
break;
switch (state) {
case PPS_START:
if (fullPath[pos] != '/') {
leafEnd = pos;
state = PPS_LEAF;
}
break;
case PPS_LEAF:
if (fullPath[pos] == '/') {
leafStart = pos+1;
pathEnd = pos-1;
loop = false;
}
break;
}
if (!loop)
break;
}
}
and the leaf name.
\param fullPath the path name to be split
\param path a variable the directory path name pointer shall
be written into, may be NULL
\param leaf a variable the leaf name pointer shall be
written into, may be NULL
*/
status_t
split_path(const char *fullPath, char *&path, char *&leaf)
{
return split_path(fullPath, &path, &leaf);
}
and the leaf name.
\param fullPath the path name to be split
\param path a pointer to a variable the directory path name pointer shall
be written into, may be NULL
\param leaf a pointer to a variable the leaf name pointer shall be
written into, may be NULL
*/
status_t
split_path(const char *fullPath, char **path, char **leaf)
{
if (path)
*path = NULL;
if (leaf)
*leaf = NULL;
if (fullPath == NULL)
return B_BAD_VALUE;
int leafStart, leafEnd, pathEnd, len;
internal_parse_path(fullPath, leafStart, leafEnd, pathEnd);
try {
if (leafEnd == -1) {
if (fullPath[0] == '/') {
if (path) {
*path = new char[2];
(*path)[0] = '/';
(*path)[1] = 0;
}
if (leaf) {
*leaf = new char[2];
(*leaf)[0] = '.';
(*leaf)[1] = 0;
}
return B_OK;
} else if (fullPath[0] == 0) {
if (path) {
*path = new char[1];
(*path)[0] = 0;
}
if (leaf) {
*leaf = new char[2];
(*leaf)[0] = '.';
(*leaf)[1] = 0;
}
return B_OK;
}
} else if (leafStart == -1) {
leafStart = 0;
} else if (pathEnd == -1) {
pathEnd = 0;
}
if (path) {
if (pathEnd == -2) {
*path = new char[2];
(*path)[0] = '.';
(*path)[1] = 0;
} else {
len = pathEnd + 1;
*path = new char[len+1];
memcpy(*path, fullPath, len);
(*path)[len] = 0;
}
}
if (leaf) {
len = leafEnd - leafStart + 1;
*leaf = new char[len+1];
memcpy(*leaf, fullPath + leafStart, len);
(*leaf)[len] = 0;
}
} catch (std::bad_alloc& exception) {
if (path)
delete[] *path;
if (leaf)
delete[] *leaf;
return B_NO_MEMORY;
}
return B_OK;
}
which the next one starts. These values are only valid, if the function
returns \c B_OK.
\param path the path to be parsed
\param length the variable the length of the first component is written
into
\param nextComponent the variable the index of the next component is
written into. \c 0 is returned, if there is no next component.
\return \c B_OK, if \a path is not \c NULL, \c B_BAD_VALUE otherwise
*/
status_t
parse_first_path_component(const char *path, int32& length,
int32& nextComponent)
{
status_t error = (path ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
int32 i = 0;
for (; path[i] != '/' && path[i] != '\0'; i++);
if (i == 0 && path[i] != '\0')
i = 1;
length = i;
for (; path[i] == '/' && path[i] != '\0'; i++);
if (path[i] == '\0')
nextComponent = 0;
else
nextComponent = i;
}
return error;
}
which the next one starts. These values are only valid, if the function
returns \c B_OK.
\param path the path to be parsed
\param component the variable the pointer to the newly allocated string
containing the first path component is written into. The caller
is responsible for delete[]'ing the string.
\param nextComponent the variable the index of the next component is
written into. \c 0 is returned, if there is no next component.
\return \c B_OK, if \a path is not \c NULL, \c B_BAD_VALUE otherwise
*/
status_t
parse_first_path_component(const char *path, char *&component,
int32& nextComponent)
{
int32 length;
status_t error = parse_first_path_component(path, length, nextComponent);
if (error == B_OK) {
component = new(nothrow) char[length + 1];
if (component) {
strncpy(component, path, length);
component[length] = '\0';
} else
error = B_NO_MEMORY;
}
return error;
}
\c B_FILE_NAME_LENGTH (including the terminating null) and it doesn't
contain any \c "/".
\param entry the entry name
\return
- \c B_OK, if \a entry is valid,
- \c B_BAD_VALUE, if \a entry is \c NULL or contains a "/",
- \c B_NAME_TOO_LONG, if \a entry is too long
\note \c "" is considered a valid entry name.
*/
status_t
check_entry_name(const char *entry)
{
status_t error = (entry ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
if (strlen(entry) >= B_FILE_NAME_LENGTH)
error = B_NAME_TOO_LONG;
}
if (error == B_OK) {
for (int32 i = 0; error == B_OK && entry[i] != '\0'; i++) {
if (entry[i] == '/')
error = B_BAD_VALUE;
}
}
return error;
}
\c B_PATH_NAME_LENGTH (including the terminating null) and each of
its components is a valid entry name.
\param entry the entry name
\return
- \c B_OK, if \a path is valid,
- \c B_BAD_VALUE, if \a path is \c NULL,
- \c B_NAME_TOO_LONG, if \a path, or any of its components is too long
\note \c "" is considered a valid path name.
*/
status_t
check_path_name(const char *path)
{
status_t error = (path ? B_OK : B_BAD_VALUE);
if (error == B_BAD_VALUE)
return error;
const char *remainder = path;
int32 length, nextComponent;
do {
error = parse_first_path_component(remainder, length, nextComponent);
if (error == B_OK) {
if (length >= B_FILE_NAME_LENGTH)
error = B_NAME_TOO_LONG;
remainder += nextComponent;
}
} while (error == B_OK && nextComponent != 0);
if (error == B_OK && strlen(path) >= B_PATH_NAME_LENGTH)
error = B_NAME_TOO_LONG;
return error;
}
std::string
to_lower(const char *str)
{
std::string result;
to_lower(str, result);
return result;
}
void
to_lower(const char *str, std::string &result)
{
if (str) {
result = "";
for (int i = 0; i < (int)strlen(str); i++)
result += tolower(str[i]);
} else
result = "(null)";
}
void
to_lower(const char *str, char *result)
{
if (str && result) {
int i;
for (i = 0; i < (int)strlen(str); i++)
result[i] = tolower(str[i]);
result[i] = 0;
}
}
void
to_lower(char *str)
{
to_lower(str, str);
}
void escape_path(const char *str, char *result)
{
if (str && result) {
int32 len = strlen(str);
for (int32 i = 0; i < len; i++) {
char ch = str[i];
char escapeChar = 0;
switch (ch) {
case ' ':
case '\'':
case '"':
case '?':
case '\\':
case '(':
case ')':
case '[':
case ']':
case '*':
case '^':
escapeChar = ch;
break;
}
if (escapeChar) {
*(result++) = '\\';
*(result++) = escapeChar;
} else {
*(result++) = ch;
}
}
*result = 0;
}
}
void escape_path(char *str)
{
if (str) {
char *copy = new(nothrow) char[strlen(str)+1];
if (copy) {
strcpy(copy, str);
escape_path(copy, str);
}
delete [] copy;
}
}
bool
device_is_root_device(dev_t device)
{
return device == 1;
}
void
FDCloser::Close()
{
if (fFD >= 0)
_kern_close(fFD);
fFD = -1;
}
};
};