2 * Copyright 2002-2012, Haiku Inc.
3 * Distributed under the terms of the MIT License.
7 * Axel Dörfler, axeld@pinc-software.de
8 * Ingo Weinhold, bonefish@users.sf.net
16 #include <Directory.h>
18 #include <StorageDefs.h>
23 #include "storage_support.h"
29 // Creates an uninitialized BPath object.
38 // Creates a copy of the given BPath object.
39 BPath::BPath(const BPath
& path
)
48 // Creates a BPath object and initializes it to the filesystem entry
49 // specified by the passed in entry_ref struct.
50 BPath::BPath(const entry_ref
* ref
)
59 // Creates a BPath object and initializes it to the filesystem entry
60 // specified by the passed in BEntry object.
61 BPath::BPath(const BEntry
* entry
)
70 // Creates a BPath object and initializes it to the specified path or
71 // path and filename combination.
72 BPath::BPath(const char* dir
, const char* leaf
, bool normalize
)
77 SetTo(dir
, leaf
, normalize
);
81 // Creates a BPath object and initializes it to the specified directory
82 // and filename combination.
83 BPath::BPath(const BDirectory
* dir
, const char* leaf
, bool normalize
)
88 SetTo(dir
, leaf
, normalize
);
92 // Destroys the BPath object and frees any of its associated resources.
99 // Checks whether or not the object was properly initialized.
101 BPath::InitCheck() const
107 // Reinitializes the object to the filesystem entry specified by the
108 // passed in entry_ref struct.
110 BPath::SetTo(const entry_ref
* ref
)
114 return fCStatus
= B_BAD_VALUE
;
116 char path
[B_PATH_NAME_LENGTH
];
117 fCStatus
= _kern_entry_ref_to_path(ref
->device
, ref
->directory
,
118 ref
->name
, path
, sizeof(path
));
119 if (fCStatus
!= B_OK
)
122 fCStatus
= _SetPath(path
);
123 // the path is already normalized
128 // Reinitializes the object to the specified filesystem entry.
130 BPath::SetTo(const BEntry
* entry
)
137 fCStatus
= entry
->GetRef(&ref
);
138 if (fCStatus
== B_OK
)
139 fCStatus
= SetTo(&ref
);
145 // Reinitializes the object to the passed in path or path and
148 BPath::SetTo(const char* path
, const char* leaf
, bool normalize
)
150 status_t error
= (path
? B_OK
: B_BAD_VALUE
);
151 if (error
== B_OK
&& leaf
&& BPrivate::Storage::is_absolute_path(leaf
))
153 char newPath
[B_PATH_NAME_LENGTH
];
155 // we always normalize relative paths
156 normalize
|= !BPrivate::Storage::is_absolute_path(path
);
157 // build a new path from path and leaf
159 uint32 pathLen
= strlen(path
);
160 if (pathLen
>= sizeof(newPath
))
161 error
= B_NAME_TOO_LONG
;
163 strcpy(newPath
, path
);
164 // append leaf, if supplied
165 if (error
== B_OK
&& leaf
) {
166 bool needsSeparator
= (pathLen
> 0 && path
[pathLen
- 1] != '/');
167 uint32 wholeLen
= pathLen
+ (needsSeparator
? 1 : 0)
169 if (wholeLen
>= sizeof(newPath
))
170 error
= B_NAME_TOO_LONG
;
172 if (needsSeparator
) {
173 newPath
[pathLen
] = '/';
176 strcpy(newPath
+ pathLen
, leaf
);
179 // check, if necessary to normalize
180 if (error
== B_OK
&& !normalize
)
181 normalize
= normalize
|| _MustNormalize(newPath
, &error
);
183 // normalize the path, if necessary, otherwise just set it
186 // create a BEntry and initialize us with this entry
188 error
= entry
.SetTo(newPath
, false);
190 return SetTo(&entry
);
192 error
= _SetPath(newPath
);
195 // cleanup, if something went wrong
203 // Reinitializes the object to the passed in dir and relative path combination.
205 BPath::SetTo(const BDirectory
* dir
, const char* path
, bool normalize
)
207 status_t error
= (dir
&& dir
->InitCheck() == B_OK
? B_OK
: B_BAD_VALUE
);
208 // get the path of the BDirectory
211 error
= dir
->GetEntry(&entry
);
214 error
= dirPath
.SetTo(&entry
);
215 // let the other version do the work
217 error
= SetTo(dirPath
.Path(), path
, normalize
);
225 // Returns the object to an uninitialized state.
230 fCStatus
= B_NO_INIT
;
234 // Appends the passed in relative path to the end of the current path.
236 BPath::Append(const char* path
, bool normalize
)
238 status_t error
= (InitCheck() == B_OK
? B_OK
: B_BAD_VALUE
);
240 error
= SetTo(Path(), path
, normalize
);
248 // Gets the entire path of the object.
256 // Gets the leaf portion of the path.
260 if (InitCheck() != B_OK
)
263 const char* result
= fName
+ strlen(fName
);
264 // There should be no need for the second condition, since we deal
265 // with absolute paths only and those contain at least one '/'.
266 // However, it doesn't harm.
267 while (*result
!= '/' && result
> fName
)
275 // Initializes path with the parent directory of the BPath object.
277 BPath::GetParent(BPath
* path
) const
282 status_t error
= InitCheck();
286 int32 length
= strlen(fName
);
288 // handle "/" (path is supposed to be absolute)
289 return B_ENTRY_NOT_FOUND
;
292 char parentPath
[B_PATH_NAME_LENGTH
];
294 while (fName
[length
] != '/' && length
> 0)
300 memcpy(parentPath
, fName
, length
);
301 parentPath
[length
] = '\0';
303 return path
->SetTo(parentPath
);
307 // Gets whether or not the path is absolute or relative.
309 BPath::IsAbsolute() const
311 if (InitCheck() != B_OK
)
314 return fName
[0] == '/';
318 // Performs a simple (string-wise) comparison of paths for equality.
320 BPath::operator==(const BPath
& item
) const
322 return *this == item
.Path();
326 // Performs a simple (string-wise) comparison of paths for equality.
328 BPath::operator==(const char* path
) const
330 return (InitCheck() != B_OK
&& path
== NULL
)
331 || (fName
!= NULL
&& path
!= NULL
&& strcmp(fName
, path
) == 0);
335 // Performs a simple (string-wise) comparison of paths for inequality.
337 BPath::operator!=(const BPath
& item
) const
339 return !(*this == item
);
343 // Performs a simple (string-wise) comparison of paths for inequality.
345 BPath::operator!=(const char* path
) const
347 return !(*this == path
);
351 // Initializes the object as a copy of item.
353 BPath::operator=(const BPath
& item
)
361 // Initializes the object with the passed in path.
363 BPath::operator=(const char* path
)
373 // #pragma mark - BFlattenable functionality
376 // that's the layout of a flattened entry_ref
377 struct flattened_entry_ref
{
383 // base size of a flattened entry ref
384 static const size_t flattened_entry_ref_size
385 = sizeof(dev_t
) + sizeof(ino_t
);
388 // Overrides BFlattenable::IsFixedSize()
390 BPath::IsFixedSize() const
396 // Overrides BFlattenable::TypeCode()
398 BPath::TypeCode() const
404 // Gets the size of the flattened entry_ref struct that represents
405 // the path in bytes.
407 BPath::FlattenedSize() const
409 ssize_t size
= flattened_entry_ref_size
;
412 if (InitCheck() == B_OK
413 && entry
.SetTo(Path()) == B_OK
414 && entry
.GetRef(&ref
) == B_OK
) {
415 size
+= strlen(ref
.name
) + 1;
421 // Converts the path of the object to an entry_ref and writes it into buffer.
423 BPath::Flatten(void* buffer
, ssize_t size
) const
428 // ToDo: Reimplement for performance reasons: Don't call FlattenedSize().
429 ssize_t flattenedSize
= FlattenedSize();
430 if (flattenedSize
< 0)
431 return flattenedSize
;
432 if (size
< flattenedSize
)
434 status_t status
= InitCheck();
438 // convert the path to an entry_ref
441 status
= entry
.SetTo(Path());
443 status
= entry
.GetRef(&ref
);
447 // store the entry_ref in the buffer
448 flattened_entry_ref
& fref
= *(flattened_entry_ref
*)buffer
;
449 fref
.device
= ref
.device
;
450 fref
.directory
= ref
.directory
;
452 strcpy(fref
.name
, ref
.name
);
458 // Checks if type code is equal to B_REF_TYPE.
460 BPath::AllowsTypeCode(type_code code
) const
462 return code
== B_REF_TYPE
;
466 // Initializes the object with the flattened entry_ref data from the passed
469 BPath::Unflatten(type_code code
, const void* buffer
, ssize_t size
)
472 status_t error
= B_OK
;
474 if (!(code
== B_REF_TYPE
&& buffer
!= NULL
475 && size
>= (ssize_t
)flattened_entry_ref_size
)) {
479 if (size
== (ssize_t
)flattened_entry_ref_size
) {
482 // reconstruct the entry_ref from the buffer
483 const flattened_entry_ref
& fref
484 = *(const flattened_entry_ref
*)buffer
;
485 BString
name(fref
.name
, size
- flattened_entry_ref_size
);
486 entry_ref
ref(fref
.device
, fref
.directory
, name
.String());
496 void BPath::_WarPath1() {}
497 void BPath::_WarPath2() {}
498 void BPath::_WarPath3() {}
501 /*! Sets the supplied path.
503 The path is copied, if \a path is \c NULL the path of the object is set to
504 \c NULL as well. The old path is deleted.
506 \param path the path to be set
508 \returns A status code.
509 \retval B_OK Everything went fine.
510 \retval B_NO_MEMORY Insufficient memory.
513 BPath::_SetPath(const char* path
)
515 status_t error
= B_OK
;
516 const char* oldPath
= fName
;
519 fName
= new(nothrow
) char[strlen(path
) + 1];
527 // delete the old one
533 /*! Checks a path to see if normalization is required.
535 The following items require normalization:
536 - Relative pathnames (after concatenation; e.g. "boot/ltj")
537 - The presence of "." or ".." ("/boot/ltj/../ltj/./gwar")
538 - Redundant slashes ("/boot//ltj")
539 - A trailing slash ("/boot/ltj/")
541 \param _error A pointer to an error variable that will be set if the input
544 \return \c true if \a path requires normalization, \c false otherwise.
547 BPath::_MustNormalize(const char* path
, status_t
* _error
)
549 // Check for useless input
550 if (path
== NULL
|| path
[0] == 0) {
552 *_error
= B_BAD_VALUE
;
556 int len
= strlen(path
);
558 /* Look for anything in the string that forces us to normalize:
560 + any occurence of /./ or /../ or //, or a trailing /. or /..
564 return true; // not "/*"
567 else if (len
> 1 && path
[len
-1] == '/')
577 for (int i
= 0; path
[i
] != 0; i
++) {
581 state
= InitialSlash
;
586 return true; // "*//*"
596 return true; // "*/./*"
606 return true; // "*/../*"
613 // If we hit the end of the string while in either
614 // of these two states, there was a trailing /. or /..
615 if (state
== OneDot
|| state
== TwoDots
)