UXPath
UXPath is a filesystem path held as components instead of a string:
"/usr/local/bin" is three names plus the fact that it began with /.
All of its work is string work. Nothing here opens, stats or resolves anything on disk, so it is fully testable and safe to use on a path that does not exist yet.
#use <UXKit> // or #import "UXPath.xc"Overview
Section titled “Overview”UXPath* p = UXPath.parse((u8*)"/usr/local/share/fonts/system.fnt");
p.count(); // 5p.isAbsolute(); // truep.lastComponent(); // "system.fnt"p.pathExtension(); // "fnt"p.lastComponentWithoutExtension(); // "system"p.toString(); // back to "/usr/local/share/fonts/system.fnt"It has the shape of NSString’s path category, and it feeds the breadcrumb bar
(UXPathComp is one crumb) and file
navigation.
Parsing tidies as it goes
Section titled “Parsing tidies as it goes”Empty components are dropped, so doubled and trailing separators do not survive the round trip:
UXPath.parse((u8*)"//usr//local///bin/").toString(); // "/usr/local/bin"parse → toString is therefore a separator normalizer. Use it when
comparing two paths from different sources, such as one typed and one built by
concatenation.
The leading / is not a component; it is the separate absolute flag. An
absolute path and a relative one with the same names differ in a field, not in
the array, and count() never counts a phantom empty first element.
parse copies the bytes of each component, so the string you passed in can
go away afterwards.
Every mutator returns a new path
Section titled “Every mutator returns a new path”UXPath* dir = p.deletingLastComponent(); // /usr/local/share/fontsUXPath* next = dir.appendingComponent((u8*)"mono.fnt");// p is unchangedUXPath behaves as a value: nothing mutates the receiver. A caller you hand
a path to cannot alter it, and a chain of derivations needs no defensive copies.
Extensions split at the last dot
Section titled “Extensions split at the last dot”UXPath.parse((u8*)"backup.tar.gz").pathExtension(); // "gz"UXPath.parse((u8*)"backup.tar.gz").lastComponentWithoutExtension(); // "backup.tar"The last dot splits, not the first, so stem + "." + ext always rebuilds the
name. A double extension keeps its first half in the stem, which suits a Save
panel that replaces .gz with something else.
Normalizing is textual, and knows about the root
Section titled “Normalizing is textual, and knows about the root”UXPath.parse((u8*)"/a/b/./c/../../d").normalized(); // "/a/d". is dropped and .. pops the component before it. The two edge cases are at
the ends:
UXPath.parse((u8*)"/../../etc").normalized(); // "/etc"UXPath.parse((u8*)"../../etc/passwd").normalized(); // "../../etc/passwd"At the root, .. has nowhere to go and is discarded, as a real filesystem
treats /.. as /. In a relative path a leading .. is kept, because
../sibling has meaning and dropping it would change where the path points.
This asymmetry makes normalized safe to run on either kind.
Topics
Section titled “Topics”parse · count · component · lastComponent · isAbsolute · toString · copy · appendingComponent · deletingLastComponent · pathExtension · lastComponentWithoutExtension · normalized · addComp
static UXPath* parse(u8* s)Split a string. Empty components are dropped; a leading / sets
isAbsolute. Copies the bytes.
i32 count(void)How many components. 0 for both "/" and ""; isAbsolute
tells them apart.
component
Section titled “component”u8* component(i32 i)One component by index, without separators.
lastComponent
Section titled “lastComponent”u8* lastComponent(void)The filename. "" instead of null when there are no components, so it is
always safe to print.
isAbsolute
Section titled “isAbsolute”bool isAbsolute(void)Whether the path began with /. Every derivation preserves it.
toString
Section titled “toString”u8* toString(void)Rebuild, joining with / and restoring the leading one. Returns "/" for an
absolute empty path and "" for a relative one.
UXPath* copy(void)A new path with the same components and flag.
appendingComponent
Section titled “appendingComponent”UXPath* appendingComponent(u8* c)A new path with one more name on the end. Copies c, so a stack buffer is fine.
deletingLastComponent
Section titled “deletingLastComponent”UXPath* deletingLastComponent(void)The parent directory. An already-empty path stays empty; there is no error.
pathExtension
Section titled “pathExtension”u8* pathExtension(void)The text after the final dot in the last component, or "". See
above for dotfiles and trailing dots.
lastComponentWithoutExtension
Section titled “lastComponentWithoutExtension”u8* lastComponentWithoutExtension(void)The last component with its extension removed: the other half of the split.
normalized
Section titled “normalized”UXPath* normalized(void)Resolve . and .. by text. See
above.
addComp
Section titled “addComp”void addComp(u8* s)Append in place, taking the pointer instead of copying it. parse
is built on this. Prefer appendingComponent, which
copies and does not mutate.
Example
Section titled “Example”parsed: '/usr/local/share/fonts/system.fnt' count=5 abs=1last=system.fnt ext=fnt stem=systemmessy: '/usr/local/bin' count=3 abs=1dir: '/usr/local/share/fonts' count=4 abs=1sibling: '/usr/local/share/fonts/mono.fnt' count=5 abs=1original: '/usr/local/share/fonts/system.fnt' count=5 abs=1tar.gz: ext=gz stem=backup.tardotfile: ext='' stem=.profileabs before: '/a/b/./c/../../d' count=7 abs=1abs after: '/a/d' count=2 abs=1above root: '/etc' count=1 abs=1relative: '../../etc/passwd' count=4 abs=0rel mixed: '../b' count=2 abs=0The program is website/site/examples/uxkit/paths.xc. The doc-examples gate
compiles it, and the output above is what it prints. It boots no driver and
touches no disk.
Conforms to
Section titled “Conforms to”- A plain class (not an
Objectsubclass)
See also
Section titled “See also”UXPathComp: one componentUXURL: the same job for URLs;file:is the bridge between themUXFilePanel: where paths come from