The BEntry class defines objects that represent "locations" in the file
system hierarchy. Each location (or entry) is given as a name within a
directory. For example, when you create a BEntry thus…
BEntryentry("/boot/home/fido");
…you're telling the BEntry object to represent the location of the file
called fido within the directory
/boot/home.
A BEntry doesn't care whether the entry you tell it to represent is a
plain file, a directory, or a symbolic link—it doesn't even care if
the entry even exists (but we'll get to that later in
"Abstract Entries"):
All the BEntry cares about is a name in a directory.
The most important implication of this is the object's attitude towards
data. BEntrys don't know how to operate on data.
You can't use a BEntry
to read or write a file's data or attributes. For data operations, you
have to turn your BEntry into a
BNode.
Nonetheless, it's often convenient to speak of a BEntry as having data;
for example, the phrase "the entry's data" really means "the data that
lies in the file that's located by the entry."
A properly initialized BEntry object (we'll get to the rules of
initialization later) knows the following:
Location info. A BEntry knows its own (leaf) name
(GetName()), its
full pathname (GetPath()),
and the identity of its parent directory
(GetParent()).
BStatable
info. As a descendant of
BStatable, a
BEntry can return
statistical information about the entry's data—its size, creation
date, owner, and so on.
entry_ref identifier. A BEntry can return
the entry_ref that globally
identifies the entry
(GetRef()).
A BEntry can do these things:
Perform hierarchical operations. A BEntry can change the name of its
entry (Rename()),
move it to another directory
(MoveTo()),
and remove it from the file hierarchy
(Remove()).
Initialize BNode
objects. The constructors and
SetTo() initializers
for BNode and its children
(BFile,
BDirectory, and
BSymLink) accept
BEntry arguments.
As mentioned above, the most important thing that a
BEntry can't do is access its own data: A
BEntry can't read or write data or attributes. To do
these things you need a BNode object.
(Actually, this isn't entirely true:
A BEntry can set the size of its data through the
BStatable::SetSize()
function. The function only works on plain files.)
To initialize a BEntry, you have to tell it which entry to represent; in
other words, you have to identify a directory and a name. You can
initialize a BEntry object directly…
during construction,
through the SetTo() function,
or through the assignment operator.
Or you can have some other object initialize your BEntry for you, by
passing the BEntry as an argument to…
BDirectory's
FindEntry() or
GetEntry() function,
BEntryList's
GetNextEntry() function (implemented by
BDirectory and
BQuery).
BEntry's
GetParent() function.
In all cases (except the assignment operator) you're asked if you want to "traverse" the entry during initialization. Traversal is used to "resolve" symbolic links:
If you traverse: The BEntry will point to the entry that the symbolic
link is linked to.
If you don't traverse: The BEntry will point to the symbolic link
itself.
For example, let's say
/boot/home/fidoLink is linked to /fido, to wit:
$ cd /boot/home $ ln -s ./fido fidoLink
Now let's make a traversed BEntry for fidoLink:
/* The second argument is the traversal bool. */BEntryentry("/boot/home/fidoLink",true);
If we ask for the entry's pathname…
BPathpath;entry.GetPath(&path);printf("Pathname: %sn",path.Path());
…we see
Pathname: /boot/home/fido
In other words, the BEntry refers to
fido, not fidoLink.
Traversal resolves nested links—it really wants to find a "real" file (or directory). If the entry that you're initializing to isn't a link, then the traversal flag is ignored.
When should you traverse, and when not? Here are a few rules of thumbs:
If somebody hands you a file reference—if your app gets a
RefsReceived()
message—then you probably want to traverse the entry.
If you're pawing over the contents of a directory (through
BDirectory's
GetNextEntry()),
then you probably don't want to traverse.
If you're looking at the result of a query (through
BQuery's
GetNextEntry()),
then you almost certainly don't want to traverse. The
query finds entries that satisfy certain criteria; if a symbolic link
is in the list, it's because the link itself was a winner. If the
linked-to file is also a winner, it will show up on its own.
Let's say you create a BEntry (to a symlink) without traversing, but then
you decide that you do want to resolve the link. Unfortunately, you can't
resolve in-place; instead, you have to initialize another BEntry using
info (entry_ref or pathname) that you get from the link entry:
BEntryentry1("/boot/home/fidoLink",false);BEntryentry2; entry_refref; /* First we check to see if it's a link. */ if (entry1.IsSymLink()) { /* Get the link's entry_ref... */entry1.GetRef(&ref); /* ...and use it to initialize the other BEntry. */entry2.SetTo(&ref,true); }
As we all should know by now, a BEntry identifies a name within a
specific directory. The directory that a BEntry identifies must exist,
but the entry that corresponds to the name doesn't have to. In other
words…
A BEntry can represent a file that doesn't exist. The entry is said
to be "abstract."
For example, the following construction creates a BEntry object based on
a BDirectory and a name:
BEntryentry(someDir, "myFile.h");
Let's assume that myFile.h doesn't exist. As long as the directory that's
referred to by someDir does exist, then the construction is legal. Some
of the BEntry functions (those inherited from
BStatable, for instance)
won't work, but the object itself is valid.
But validity doesn't equal existence:
SetTo() and
InitCheck()
do not tell you if a BEntry's entry actually
exists. Don't be confused; a return value of B_OK simply means the
object is valid.
If you want to know if a BEntry's entry actually exists, use the
Exists()
function.
To turn an abstract BEntry into a real entry (or, more accurately, a real
node), you have to specify the flavor of node that you want. There are
two methods for creating a node; the first is general, the second applies
to plain files only.
BDirectory's
CreateFile(),
CreateDirectory(),
CreateSymLink()
functions create nodes of the designated flavor. The functions don't take
BEntry arguments directly; instead, you invoke the functions on the
BEntry's directory, passing the entry's leaf name as an argument. Here we
turn an abstract entry (entry) into a directory:
BPathpath; charname[B_FILE_NAME_LENGTH]; /* A buffer for the name. */BDirectoryparent; /* The parent of our entry. */BDirectorytarget_dir; /* The product of the transformation. */ if (!entry.Exists()) {entry.GetParent(&path);entry.GetName(name);parent.SetTo(&path);parent.CreateDirectory(name, &dir); }
The following details understand you should, particularly if you want to participate in bedevtalk.
Although it's not intuitively obvious, a BEntry object does consume a
file descriptor. The file descriptor is opened on the entry's directory.
Your app has a limited number of file descriptors (currently 128, max),
so you may not want to cache BEntry objects as your primary means for
identifying an entry. If you're going to be dealing with a lot of entries
and you want to keep track of them all, it's better to cache entry_ref
structures or
BPath objects.
One more time: A BEntry identifies an entry as a name in a directory. As
described above, the directory is maintained internally as a file
descriptor; the name is simply a string. This means that…
The directory for a given BEntry is persistent. If you move the directory, the file descriptor, and so the BEntry, moves with it.
The name isn't persistent. If the user renames the leaf that a BEntry is pointing to, the BEntry will become abstract.
For example, take the following BEntry…
BEntryentry("/boot/home/lbj/footFetish.jpeg");
If the user moves the directory…
$ cd /boot/home $ mv lbj jfk
The BEntry (entry) "moves" with the directory. If you print the pathname
and ask if the BEntry's entry exists…
BPathpath;entry.GetPath(&path);printf("> Foot movie: %sn",path.Path());printf("> Exists? %sn",entry.Exists()?"Oui":"Non");
…you'll see this:
> Foot movie: /boot/home/jfk/footFetish.jpeg > Exists? Oui
The same isn't so for the name portion of a BEntry. If the user now moves
footFetish.jpeg…
$ cd /boot/home/jfk $ mv footFetish.jpeg hammerToe.jpeg
…your BEntry will not follow the file (it doesn't "follow the data").
The object will still represent the entry called footFetish.jpeg. The
BEntry will, in this case, become abstract.
Don't be confused: The BEntry only "loses track" of a renamed entry if
the name change is made behind the object's back. Manipulating the entry
name through the BEntry object's
Rename() function (for example), doesn't
baffle the object. For example:
BPathpath;BEntryentry("/boot/home/lbj/footFetish.jpeg");entry.Rename("hammerToe.jpeg");entry.GetPath(&path);printf("> Foot movie: %sn",path.Path());printf("> Exists? %sn",entry.Exists()?"Oui":"Non");
…and we see…
> Foot movie: /boot/home/lbj/hammerToe.jpeg > Exists? Oui
You can't lock an entry, but you can lock the entry's node (through
BNode's
Lock()
function). Initializing a BEntry to point to a locked node
is permitted, but the entry's directory must not be locked. If the
directory is locked, the BEntry constructor and
SetTo() function fail and set
InitCheck()
to B_BUSY.
Furthermore, the destination directories in BEntry's
Rename() and
MoveTo()
must be unlocked for the functions to succeed. And all
directories in the path to the entry must be unlocked for
GetPath() to
succeed.
If you get a B_BUSY error, you may want to try again—it's strongly
advised that locks be held as briefly as possible.