A BPath
object represents an absolute pathname, and provides some simple
path manipulation and querying functions. The primary features of the
class are:
It allocates storage for you. When you tell your BPath
object which
pathname you want it to represent, the object allocates storage for the
pathname automatically. When you delete the object, the storage is
freed.
It always represents an absolute path. The pathname strings that you
use to initialize a BPath
can be relative, and they can include
references to "." and "..". The BPath
"normalizes" the passed-in
strings to create an absolute pathname, as described in
"Initializing and Normalizing".
BPath
s are handy, but don't expect them to actually do very much: A BPath
is just a pathname. It identifies the location of a file, but it can't
manipulate the file, nor can it change the structure of the file system.
You can use your BPath
to initialize other, more powerful objects
(BEntry
,
BNode
and its kids). See
"Converting a BPath".
BPath
s can be passed through
BMessage
s.
To add a BPath
to a
BMessage
,
you have to flatten it first: BPath
implements
BFlattenable
for exactly
this reason. The receiver of the
BMessage
can resurrect the flattened
object as a BPath
object or as an entry_ref structure.
See "Passing a BPath in a BMessage".
BPath
objects are ideal for caching references to files. BPath
s don't
consume much in the way of system resources—they don't contain
file descriptors, for example. So they're great for keeping track of
the files that your application is interested in.
In the way that they're used, BPath
s and entry_refs are nearly identical.
In particular, entry_refs can do all three of the things listed here.
Whether you use BPath
s (pathnames in general) or entry_refs is largely a
matter of taste.
You initialize a BPath
—in other words, you establish the path that
the object represents—by passing a string (or two, or a
BDirectory
and a string) to the constructor or to the
SetTo()
function. Upon
initialization, the BPath
object concatenates the strings and then
"normalizes" the passed-in strings if it has to (this emphasis is
important, as we'll see in a moment). The following elements trigger
normalization:
a relative pathname (after concatenation; e.g. boot/lbj
)
the presence of "." or ".." (/boot/lbj/../lbj/./fido
)
redundant slashes (/boot//lbj
)
a trailing slash (/boot/lbj/
)
During normalization, BPath
conjures up an absolute pathname in the form
/dir1/dir2/.../dirN/leaf
It does this by applying the following rules:
relative pathnames are reckoned off of the current working directory
"." is ignored (at the head of a path, it's taken as the cwd).
".." bumps up one directory level
redundant slashes are coalesced
a trailing slash is removed.
(The one exception to this final rule is /
as a full pathname.)
There's a subtle side-effect that you get with normalization: When you
normalize a pathname, all the elements in the path up to but not
including the leaf must exist. In other words, a normalized BPath
object
gives you the same guarantee of existence as does an entry_ref structure.
The subtlety, here, is that an unnormalized BPath
needn't exist at all.
For example, here we create a BPath
for a pathname that contains a
non-existent directory:
/* We'll assume that "/abc/def/" doesn't exist. */BPath
path
("/abc/def/ghi.jkl"); /* Nonetheless, the BPath is successfully initialized. * The Path() function returns a pointer to the object's * pathname string. */printf
("Path: %sn".path
.Path()
);
On the command line we see:
$ Path: /abc/def/ghi.jkl
But if we tickle the normalization machine…
/* The redundant slash causes a normalization. */BPath
path
("/abc/def//ghi.jkl");
…the object is invalid:
$ Path: (null)
Both the constructor and the
SetTo()
function carry an optional argument
that lets you force the passed-in path to be normalized:
/* The trailing bool forces normalization. */BPath
path
("/abc/def/ghi.jkl",true
);printf
("Path: %sn",path
.Path()
);
In this case, the forced normalization nullifies the object:
$Path: (null)
Since forcing normalization makes BPath
's behaviour more consistent and
reliable, why not always normalize? Because normalization can be
expensive.
During normalization, the pathname is stat
'd and prodded rather heavily.
If you're planning on using your BPath
's pathname to initialize a
BEntry
or BNode
,
this prodding will happen again. Rather than incur the expense
twice, you may want to live with unnormalized BPath
objects, and take the
normalization hit during the subsequent initialization.
You can't force the BPath
constructor or
SetTo()
function to skip the normalization. If the path needs to be normalized, it will be
normalized.
BPath
doesn't let you ask if its pathname was normalized.
BPath
objects are passed back to you (by reference) by a number of
Storage Kit functions. However, you shouldn't find any functions that ask
for a BPath
object. This is a convention of usage:
If an API element returns a pathname to you, it does so in the form
of a BPath
. If it asks for a pathname from you (as an argument), it
asks for a const char*.
As an example of a function that returns a BPath
to you, recall
BEntry
's
GetPath()
function:
status_tBEntry
::GetPath
(BPath
*path
)
(As an aside, this is where the auto-allocation comes in
handy—because BPath
allocates the pathname storage for you, you
don't have to mess around with ugly buffer and length arguments.)
On the other hand, BEntry
's
SetTo()
takes a pathname as a const char*:
status_tBEntry
::SetTo
(const char *path
)
If you've got a BPath
loaded up with a pathname, you would call this
function thus:
entry
.SetTo
(path
.Path()
);
The constructors and SetTo()
functions in (most of) the Storage Kit
classes have const char* versions that can be called as shown here.
Let's say you've got a BPath
object that you want to send to some other
application. To do this, you have to add it to a
BMessage
object through
the latter's
AddFlat()
function. As an inheritor from
BFlattenable
the BPath
knows how to flatten itself for just this purpose.
BMessage
msg
;BPath
path
("/boot/lbj/fido"); /* The check here is important, as we'll describe * in a moment. */ if (msg
.AddFlat
("pathname", &path
) !=B_OK
) /* handle the error */
The receiver of the message can retrieve the pathname as a BPath
object
by calling FindFlat()
:
voidMyApp
::MessageReceived
(BMessage
*msg
) {BPath
path
; if (msg
->FindFlat
("pathname", &path
) !=B_OK
) /* handle the error */ ... }
Alternatively, the pathname can be retrieved as an entry_ref through
FindRef()
:
voidMyApp
::MessageReceived
(BMessage
*msg
) { entry_refref
; if (msg
->FindRef
("pathname", &ref
) !=B_OK
) /* handle the error */ ... }
If you want to skip all the conversion business and simply pass the
pathname as a string, use
AddString()
.
The receiver, of course, would have to call
FindString()
to retrieve your pathname string.
When you add a flattened BPath
to a
BMessage
, the object's pathname is
turned into an entry_ref. If the message receiver asks for a BPath
(through FindFlat()
),
the entry_ref is turned back into a BPath
object.
Therefore, it's more efficient to retrieve a flattened BPath
as an
entry_ref than it is to unflatten it as a BPath
object.
The BPath
to entry_ref conversion has another, more subtle implication:
Adding a BPath
through
AddFlat()
performs an implicit normalization on the data that's added to the
BMessage
.
If the normalization fails, the
AddFlat()
function returns an error and the data isn't added to the
BMessage
.
The original BPath
is untouched,
regardless of the result of the normalization.
As mentioned earlier, most of the Storage Kit classes have constructors
and SetTo()
functions that accept const char* arguments. If you want to
turn your BPath
into a
BFile
(for example), you would do this (including error checks):
status_terr
;BFile
file
(path
.Path()
);err
=InitCheck()
;
or
err
=file
.SetTo
(path
.Path()
);
To convert a BPath
to an entry_ref, pass the pathname to the
get_ref_for_path()
function:
entry_refref
; status_terr
;err
=get_ref_for_path
(path
.Path()
, &ref
);
For you Node Monitor users: You can't convert directly to a node_ref structure. The quickest way from here to there is:
node_refnref
; status_terr
; /* We'll skip InitCheck() and catch errors in GetNodeRef(). */BEntry
entry
(path
.Path()
);err
=entry
.GetNodeRef
(&nref
);
Remember, a BPath
represents a pathname, not a node. It isn't "updated"
when the file system changes:
A BPath
's pathname string never changes behind your back, even if the
entry that it originally pointed to is renamed, moved, or deleted.
For example:
BEntry
entry
;BPath
path
; /* Set a BPath, construct a BEntry from it, rename * the entry, and then print the BPath's pathname. */ if (path
.SetTo
("/boot/lbj/fido") ==B_OK
) if (entry
.SetTo
(&path
) ==B_OK
) if (entry
.Rename
("rover") ==B_OK
)printf
("Pathname: %sn",path
.Path()
);
We see…
$ Pathname: /boot/lbj/fido
...even though the entry that the BPath
was constructed to represent has
been renamed.