Path handling

EVFS needs to be able to work with file and directory paths. The library provides functions to help you manipulate paths in an OS agnostic way. These are pure string manipulations and the referenced files and directories do not have to exist on any VFS. The operations allow you to extract portions of a path, join paths together, normalize them, and convert relative paths into absolute.

The representation of absolute paths can vary between systems. In Unix derived systems a bare ‘/’ represents the root of the filesystem tree. On DOS/Windows systems this can be more elaborate with optional drive letters and other variants. To make path processing more universal, these functions work in conjunction with a method from each VFS that identifies the root component of a path and whether it is absolute. This portion of a path is left largely untouched by the EVFS library. Paths are passed through and validated by the underlying filesystem. Because of this behavior you will need at least one filesystem registered with EVFS before you can operate on paths. If you register multiple filesystems with different conventions for the root component you must ensure that the right VFS is referenced with its matching paths.

String ranges

The EVFS path API functions frequently take a StringRange or AppendRange struct as input and output parameters. These are part of a small string utility in ‘src/util/range_strings.c’. These structs represent a substring pointing into another string. StringRange objects represent a substring that will not change bounds after a function call. AppendRange objects cover the empty space at the end of a string to append into. They have their start position advanced after an append operation. They have the same layout and only differ by the presence of const pointers in StringRange. These types can be casted back and forth as necessary.

The general procedure for using these types is to prepare a storage area for a string, either a local array or one produced by dynamic allocation. You then initialize the StringRange to cover the char array. This can be accomplished with a static initializer for true arrays or by calling range_init() with the start and size of the array when you have a pointer to allocated storage.

char buf[100];
StringRange buf_r = RANGE_FROM_ARRAY(buf); // Initializer can be used with array variables

char *buf2 = evfs_malloc(100);
StringRange buf2_r;
range_init(&buf_2_r, buf2, 100); // Initialize pointers

char *str; // Unknown length
StringRange str_r;
range_init(&str_r, str, strlen(str)+1); // When using strlen, add 1 to include the NUL

A substring pointed to by StringRange will not necessarily be NUL terminated. To print the substring using printf() formatted output, you use the “%.*s” specifier and the RANGE_FMT macro:

StringRange *str;

printf("Substring in range is: '%.*s'\n", RANGE_FMT(str));

Path API

All of the functions that generate a new string from joining, normalization, or absolute conversion have been designed to safely overwrite their input string if it is also the output. This allows you to reuse an existing range and minimize the need for temporary buffers. These functions also avoid use of evfs_malloc() whenever possible.

Most of these functions have an “_ex” suffix indicating that their last parameter is an optional VFS name. All of these forms have a macro defined without the suffix that uses the default VFS.

bool evfs_path_root_component_ex(const char *path, StringRange *root, const char *vfs_name)

Get the root portion of a path.

This is a virtual method that calls into a VFS specific implementation to handle different filesystem path formats. On POSIX style systems the root component of absolute paths is a leading sequence of one or more ‘/’ chars and nothing for relative paths. On DOS/Windows filesystems the root component may also have a colon separated volume letter or number on absolute or relative paths.

Parameters
  • path – Path to extract root from

  • root – Substring of path corresponding to root or empty if none found

  • vfs_name – VFS to work on. Use default VFS if NULL

Returns

true when the path is absolute

bool evfs_path_is_absolute_ex(const char *path, const char *vfs_name)

Detect if a path is absolute on the supplied VFS.

Parameters
  • path – Path to test

  • vfs_name – VFS to work on. Use default VFS if NULL

Returns

true if path is absolute

int evfs_path_basename(const char *path, StringRange *tail)

Get the file name portion of a path.

This copies the behavior of Python os.path.basename().

Parameters
  • path – Path to extract basename from

  • tail – Substring of path corresponding to the basename

Returns

EVFS_OK on success

int evfs_path_extname(const char *path, StringRange *ext)

Get the extension of a file.

This copies the behavior of Python os.path.splitext().

Parameters
  • path – Path to extract extension from

  • ext – Substring of path corresponding to the extension

Returns

EVFS_OK on success

int evfs_path_dirname_ex(const char *path, StringRange *head, const char *vfs_name)

Get the directory portion of a path.

This copies the behavior of Python os.path.dirname().

Parameters
  • path – Path to extract dirname from

  • head – Substring of path corresponding to the dirname

  • vfs_name – VFS to work on. Use default VFS if NULL

Returns

EVFS_OK on success

int evfs_path_join_ex(StringRange *head, StringRange *tail, StringRange *joined, const char *vfs_name)

Join two paths.

Parameters
  • head – Substring of left portion to join

  • tail – Substring of right portion to join

  • joined – Substring of output string. This can be the same as the head substring.

  • vfs_name – VFS to work on. Use default VFS if NULL

Returns

EVFS_OK on success

int evfs_path_join_str_ex(const char *head, const char *tail, StringRange *joined, const char *vfs_name)

Join two paths using char strings.

Parameters
  • head – Substring of left portion to join

  • tail – Substring of right portion to join

  • joined – Substring of output string. This can be the same as the head path.

  • vfs_name – VFS to work on. Use default VFS if NULL

Returns

EVFS_OK on success

int evfs_path_normalize_ex(const char *path, StringRange *normalized, const char *vfs_name)

Normalize a path.

This applies the following transformations:

  • Any root component is reduced to its minimal form.

  • Consecutive separators are merged into one

  • All separators after root component are converted to EVFS_DIR_SEP

  • “./” segments are removed

  • “../” segments are removed along with the preceeding segment

  • Trailing slashes are removed

Parameters
  • path – Path to be normalized

  • normalized – Normalized output path. This can be the same string as path

  • vfs_name – VFS to work on. Use default VFS if NULL

Returns

EVFS_OK on success

int evfs_path_absolute_ex(const char *path, StringRange *absolute, const char *vfs_name)

Convert a path to absolute form.

If the path is already absolute it is normalized. Otherwise it is joined to the current working directory and normalized.

Parameters
  • path – Path to become absolute

  • absolute – Absolute output path. This can be the same string as path

  • vfs_name – VFS to work on. Use default VFS if NULL

Returns

EVFS_OK on success

bool evfs_vfs_path_is_absolute(Evfs *vfs, const char *path)

Detect if a path is absolute on the supplied VFS.

This variant is for fs wrappers to use without a name lookup.

Parameters
  • vfs – The VFS for the path

  • path – Path to test

Returns

true if path is absolute