# The Storage Kit: BPath

Derived from: BFlattenable

Declared in: be/storage/Path.h

Library: libbe.so

## Overview

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.

BPaths 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.

So what do you use BPaths for?

- You can use your BPaths to initialize other, more powerful objects (BEntry, BNode and its kids). See Converting a BPath .

- BPaths can be passed through BMessages. 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. BPaths 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, BPaths and `entry_ref`s are nearly identical. In particular, `entry_ref`s can do all three of the things listed here. Whether you use BPaths (pathnames in general) or `entry_ref`s is largely a matter of taste.

### Initializing and Normalizing

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:  %s\\n". 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)

#### Forcing Initialization

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:  %s\\n", path.Path());

In this case, the forced normalization nullifies the object:

       $Path:  (null)

#### Normalization by Default?

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.

#### Other Normalization Details

- 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.

### The BPath Calling Convention

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_t BEntry::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_t BEntry::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.

### Passing a BPath in a BMessage

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_NO_ERROR)
          /* handle the error */

The receiver of the message can retrieve the pathname as a BPath object by calling `FindFlat()`:

       void MyApp::MessageReceived(BMessage *msg)
       {
          BPath path;

          if (msg->FindFlat("pathname", &path) != B_NO_ERROR)
             /* handle the error */
          ...
       }

Alternatively, the pathname can be retrieved as an `entry_ref` through `FindRef()`:

       void MyApp::MessageReceived(BMessage *msg)
       {
          entry_ref ref;

          if (msg->FindRef("pathname", &ref) != B_NO_ERROR)
             /* 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.

#### What's Really Going On

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.

### Converting a BPath

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_t err;

       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_ref ref;
       status_t err;

       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_ref nref;
       status_t err;

       /* We'll skip InitCheck() and catch errors in GetNodeRef(). */
       BEntry entry(path.Path());
       err = entry.GetNodeRef(&nref);

### Immutability

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_NO_ERROR)
          if (entry.SetTo(&path) == B_NO_ERROR)
             if (entry.Rename("rover") == B_NO_ERROR)
                printf("Pathname: %s\\n", path.Path());

We see...

       $ Pathname:  /boot/lbj/fido

...even though the entry that the BPath was constructed to represent has been renamed.

## Constructor and Destructor

### BPath()

          BPath(const char *path, const char *leaf = NULL, bool normalize = false)
          BPath(const BDirectory *dir, const char *leaf = NULL, bool normalize = false)

          BPath(void)
          BPath(const BPath &path)

Creates a new BPath object that represents the path that's created from the arguments. See the analogous `SetTo()` functions for descriptions of the flavorful constructors.

- The default constructor does nothing; it should be followed by a call to `SetTo()`.
- The copy constructor makes a copy of the argument's pathname.

The constructor automatically allocates memory for the object's stored pathname. The memory is freed when the object is deleted.

To check to see if an initialization was successful, call `InitCheck()`.

### ~BPath

          virtual ~BPath()

Frees the object's pathname storage and extinguishes the object.

## Member Functions

### Append()

          status_t Append(const char *path, bool normalize = false)

Appends the pathname given by path to the object's current pathname. path must be relative. If normalize is `true`, the new pathname is normalized; otherwise, it's normalized only if necessary.

Note that this...

        Append("subdir/file")

...is the same as (and is implemented as):

       path.SetTo(path.Path(), "subdir/file");

The `Append()` return value is picked up from the `SetTo()` call.

RETURN CODES

- `B_NO_ERROR`. Success.
- `B_BAD_VALUE`. path contained a leading "/", or `this` is uninitialized.
- See `SetTo()` for other return values.

### GetParent()

          status_t GetParent(BPath *path) const

Initializes the argument with the pathname to the parent directory of `this`. Destructive parenting is acceptable (sociologically, it's a given):

       BPath path("/boot/lbj/fido");

       path.GetParent(&path);

Other details...

- `GetParent()` makes a call to `SetTo()`, but it's guaranteed not to tickle the normalization machine.

- You can't get the parent of "/".

RETURN CODES

- `B_NO_ERROR`. Hello, mother.
- `B_ENTRY_NOT_FOUND`. You tried to get the parent of "/".
- `B_BAD_VALUE`. path is `NULL`.
- `B_NO_MEMORY`. Couldn't allocate storage for the pathname.

If the initialization isn't successful, the argument's `InitCheck()` is set to `B_NO_INIT`.

### InitCheck()

          status_t InitCheck(void) const

Returns the status of the most recent construction or `SetTo()` call.

RETURN CODES

- `B_NO_ERROR`. The initialization was successful.
- `B_NO_INIT`. The object is uninitialized (this includes `Unset()`).
- See `SetTo()` for other errors.

### Path(), Leaf()

          const char *Path(void) const
          const char *Leaf(void) const

These functions return the object's full path and leaf name, respectively. For example:

       BPath path("/boot/lbj/fido");
       printf("Path:  %s\\n", path.Path());
       printf("Leaf:  %s\\n", path.Leaf());

Produces...

       $ Path:  /boot/lbj/fido
       $ Leaf:  fido

In both cases, the returned pointers belong to the BPath object. When the BPath is deleted, the pointers go with it.

If the BPath isn't initialized, the functions return pointers to `NULL`.

### SetTo(), Unset()

          status_t SetTo(const char *path, const char *leaf = NULL, bool normalize = false)
          status_t SetTo(const BDirectory *dir, const char *leaf = NULL,
             bool normalize = false)

          void Unset(void)

The `SetTo()` function frees the pathname that the object currently holds, and re-initializes the object according to the arguments:

- The first version concatenates the path and leaf strings (interposing a "/" if necessary). If path is relative, the concatenated pathname is appended to the current working directory. Note that you don't have to split your pathname into two parts to call this constructor; the optional leaf argument is provided simply as a convenience.

- The second version performs a similar operation using the path of the BDirectory as the initial part of the pathname.

Regarding the leaf argument:

- The leaf string can contain directories--it needn't be just a leaf name.
- However, leaf must be a relative pathname (it can't start with "/").

If set to `true`, the normalize argument tells the object to normalize the new pathname. By default (`false`), the pathname is normalized only if necessary. Note that the default doesn't mean that the object absolutely won't normalize, it just won't do it if it doesn't think it's necessary. See Initializing and Normalizing for the full story on normalizing a pathname, including the conditions that trigger default normalization.

Storage for the pathname is allocated by the BPath object and is freed when the object is deleted (or when you re-initialize through `SetTo()`). The path and leaf arguments are copied into the allocated storage.

Other details...

- Destructive setting is safe:

       /* This works... */
       path.SetTo(path.Path(), ...);

- Currently, `SetTo()` only checks pathname and filename length if it has to normalize.

`Unset()` frees the object's pathname storage and sets the `InitCheck()` value to `B_NO_INIT`.

RETURN CODES

- `B_NO_ERROR`. Successful initialization.

- `B_BAD_VALUE`. path is `NULL`, leaf isn't relative (it starts with a "/"), or dir is uninitialized.

- `B_BAD_VALUE`. A directory in the path doesn't exist (normalization only).

- `B_NAME_TOO_LONG`. A pathname element is too long (normalization only).

- `B_NO_MEMORY`. Couldn't allocate storage for the pathname.

The return value is also recorded in `InitCheck()`.

## B Flattenable Functions

The following functions are implemented in accordance with the rules set down by the BFlattenable class. You never need to invoke these functions directly; they're implemented so a BPath can added to a BMessage (see Passing a BPath in a BMessage ). But in case you're interested...

### AllowsTypeCode()

          virtual bool AllowsTypeCode(type_code code) const

Returns `true` if code is `B_REF_TYPE`, and `false` otherwise.

### Flatten()

          virtual status_t Flatten(void *buffer, ssize_t size) const

Converts the object's pathname to an `entry_ref` and writes it into buffer. Currently, size is ignored.

RETURN CODES

- `B_NO_ERROR`. Peachy.
- `B_NAME_TOO_LONG`. The pathname is too long (> 1024 characters).
- `B_ENTRY_NOT_FOUND`. A directory in the path doesn't exist.

### FlattenedSize()

          virtual ssize_t FlattenedSize() const

Returns the size of the `entry_ref` that represents the flattened pathname.

### IsFixedSize()

          virtual bool IsFixedSize() const

Returns `false`.

### TypeCode()

          virtual type_code TypeCode() const

Returns `B_REF_TYPE`.

### Unflatten()

          virtual status_t Unflatten(type_code code,
             const void *buffer,
             ssize_t size)

Initializes the BPath with the flattened `entry_ref` data that's found in buffer. The type code must be `B_REF_TYPE`.

RETURN CODES

- `B_NO_ERROR`. Success.
- `B_BAD_VALUE`. Wrong type code (not `B_REF_TYPE`).
- `B_ENTRY_NOT_FOUND`. A directory in the `entry_ref` data doesn't exist.

The `Unflatten()` return value is recorded in `InitCheck()`.

## Operators

### = (assignment)

          BPath& operator=(const BPath &path)
          BPath& operator=(const char *string)

Initializes `this` with a copy of the pathname that's gotten from the argument. Also sets `InitCheck()`.

### ==, != (comparison)

             bool operator==(const BPath &path) const
             bool operator==(const char *string) const

             bool operator!=(const BPath &path) const
             bool operator!=(const char *string) const

Compares `this`'s pathname with the pathname taken from the argument. The comparison is a simple `strcmp()`; neither path is normalized or otherwise altered before the comparison is made. For example:

       BPath path("/boot/lbj/fido");

       chdir("/boot");
       printf("Are they equal?  %d\\n", path == "lbj/fido");

Displays:

       $ Are they equal? 0

The Be Book, in lovely HTML, for BeOS Release 3.

Copyright © 1998 Be, Inc. All rights reserved.

Last modified March 26, 1998.