#!/usr/bin/env dub
/+ dub.sdl:
name "component_walk"
platforms "posix"
targetPath "build"
buildType "checked" {
buildOptions "optimize" "inline" "debugInfo"
}
+/
/**
* The portable fallback: resolving a relative path one component at a time
* with `openat(parent, name, O_NOFOLLOW | O_DIRECTORY)`.
*
* This is the algorithm every surveyed library falls back to when the kernel
* offers no `openat2` / `O_RESOLVE_BENEATH` / `O_NOFOLLOW_ANY`: reject `..`
* and absolute paths lexically, then open each intermediate directory relative
* to the previous one with `O_NOFOLLOW`, so a symlink anywhere in the path is
* refused instead of followed, and the leaf with `O_NOFOLLOW` too. Every step
* is atomic with respect to the *previous* handle, which is what a path string
* can never be. The demonstration plants a symlinked intermediate and a
* symlinked leaf and shows both refused, and a `..` refused before any syscall.
*
* One detail the output makes visible: a symlinked *leaf* is `ELOOP`, but a
* symlinked *intermediate* opened with `O_NOFOLLOW | O_DIRECTORY` is `ENOTDIR`
* on Linux — the directory check runs before the symlink check — so a fallback
* that wants to tell "a symlink" from "a regular file in the way" cannot read
* it off the errno; it has to `fstatat(AT_SYMLINK_NOFOLLOW)` the component.
*
* Companion to docs/research/safe-path-traversal/comparison.md
* § "The component walk".
*
* Run with: `dub run --single component-walk.d`
*/
module (module) component_walkThe portable fallback: resolving a relative path one component at a time
with openat(parent, name, O_NOFOLLOW | O_DIRECTORY).
This is the algorithm every surveyed library falls back to when the kernel
offers no openat2 / O_RESOLVE_BENEATH / O_NOFOLLOW_ANY: reject ..
and absolute paths lexically, then open each intermediate directory relative
to the previous one with O_NOFOLLOW, so a symlink anywhere in the path is
refused instead of followed, and the leaf with O_NOFOLLOW too. Every step
is atomic with respect to the previous handle, which is what a path string
can never be. The demonstration plants a symlinked intermediate and a
symlinked leaf and shows both refused, and a .. refused before any syscall.
One detail the output makes visible: a symlinked leaf is ELOOP, but a
symlinked intermediate opened with O_NOFOLLOW | O_DIRECTORY is ENOTDIR
on Linux — the directory check runs before the symlink check — so a fallback
that wants to tell "a symlink" from "a regular file in the way" cannot read
it off the errno; it has to fstatat(AT_SYMLINK_NOFOLLOW) the component.
Companion to docs/research/safe-path-traversal/comparison.md
§ "The component walk".
Run with: dub run --single component-walk.d
component_walk;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.errnoD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/errno.h.html, errno.h
Source
core/stdc/errno.d
errno : (alias) component_walk.errno = int core.stdc.errno.__errno_location() nothrow @nogc ref @trustederrno, (alias constant) component_walk.ELOOP = int core.stdc.errno.ELOOP = 40ELOOP, (alias constant) component_walk.ENOENT = int core.stdc.errno.ENOENT = 2ENOENT, (alias constant) component_walk.ENOTDIR = int core.stdc.errno.ENOTDIR = 20ENOTDIR;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.fcntlD header file for POSIX.
fcntl : (alias constant) component_walk.O_RDONLY = int core.sys.posix.fcntl.O_RDONLY = 0O_RDONLY, (alias constant) component_walk.O_NOFOLLOW = int core.sys.posix.fcntl.O_NOFOLLOW = 131072O_NOFOLLOW, open, openat;
// Darwin's druntime `core.sys.posix.fcntl` declares neither; the values are
// XNU's `bsd/sys/fcntl.h`.
version (OSXOSX)
{
enum O_DIRECTORY = 0x100000;
enum O_CLOEXEC = 0x1000000;
}
else
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.fcntlD header file for POSIX.
fcntl : (alias constant) component_walk.O_DIRECTORY = int core.sys.posix.fcntl.O_DIRECTORY = 65536O_DIRECTORY, (alias constant) component_walk.O_CLOEXEC = int core.sys.posix.fcntl.O_CLOEXEC = 524288O_CLOEXEC;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.unistdD header file for POSIX.
unistd : (alias) component_walk.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose;
import (package) stdstd.(package) std.algorithmalgorithm.(module) std.algorithm.iterationThis is a submodule of std.algorithm.
It contains generic iteration algorithms.
Function Name Description cache Eagerly evaluates and caches another range's front. lazyCache Lazily evaluates and caches another range's front, unlike cache. cacheBidirectional As above, but also provides back and popBack. chunkBy chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]]) returns a range containing 3 subranges: the first with just [1, 1]; the second with the elements [1, 2] and [2, 2]; and the third with just [2, 1]. cumulativeFold cumulativeFold!((a, b) => a + b)([1, 2, 3, 4]) returns a lazily-evaluated range containing the successive reduced values 1, 3, 6, 10. each each!writeln([1, 2, 3]) eagerly prints the numbers 1, 2 and 3 on their own lines. filter filter!(a => a > 0)([1, -1, 2, 0, -3]) iterates over elements 1 and 2. filterBidirectional Similar to filter, but also provides back and popBack at a small increase in cost. fold fold!((a, b) => a + b)([1, 2, 3, 4]) returns 10. group group([5, 2, 2, 3, 3]) returns a range containing the tuples tuple(5, 1), tuple(2, 2), and tuple(3, 2). joiner joiner(["hello", "world!"], "; ") returns a range that iterates over the characters "hello; world!". No new string is created - the existing inputs are iterated. map map!(a => a * 2)([1, 2, 3]) lazily returns a range with the numbers 2, 4, 6. mean Colloquially known as the average, mean([1, 2, 3]) returns 2. permutations Lazily computes all permutations using Heap's algorithm. reduce reduce!((a, b) => a + b)([1, 2, 3, 4]) returns 10. This is the old implementation of fold. splitWhen Lazily splits a range by comparing adjacent elements. splitter Lazily splits a range by a separator, element predicate or whitespace. substitute [1, 2].substitute(1, 0.1) returns [0.1, 2]. sum Same as fold, but specialized for accurate summation. uniq Iterates over the unique elements in a range, which is assumed sorted.
Source
std/algorithm/iteration.d
iteration : (alias template) component_walk.filter = std.algorithm.iteration.filter(alias predicate) if (is(typeof(unaryFun!predicate)))``filter!(predicate)(range) returns a new range containing only elements x in range for
which predicate(x) returns true.
The predicate is passed to unaryFun, and can be either a string, or
any callable that can be executed via pred(element).
filter, (alias template) component_walk.splitter = std.algorithm.iteration.splitter(alias pred = "a == b", Flag keepSeparators = No.keepSeparators, Range, Separator)(Range r, Separator s) if (is(typeof(binaryFun!pred(r.front, s)) : bool) && (hasSlicing!Range && hasLength!Range || isNarrowString!Range) && (is(ElementType!Range : Separator) || !(isForwardRange!Separator && (hasLength!Separator || isNarrowString!Separator))))Lazily splits a range using an element or range as a separator.
Separator ranges can be any narrow string type or sliceable range type.
Two adjacent separators are considered to surround an empty element in
the split range. Use filter!(a => !a.empty) on the result to compress
empty elements.
The predicate is passed to binaryFun and accepts
any callable function that can be executed via pred(element, s).
Note
If splitting a string on whitespace and token compression is desired,
consider using the ``splitter(r) overload.
Constraints
The predicate pred needs to accept an element of r and the
separator s.
splitter;
import (package) stdstd.(package) std.algorithmalgorithm.(module) std.algorithm.searchingThis is a submodule of std.algorithm.
It contains generic searching algorithms.
Function Name Description all all!"a > 0"([1, 2, 3, 4]) returns true because all elements are positive any any!"a > 0"([1, 2, -3, -4]) returns true because at least one element is positive balancedParens balancedParens("((1 + 1) / 2)", '(', ')') returns true because the string has balanced parentheses. boyerMooreFinder find("hello world", boyerMooreFinder("or")) returns "orld" using the Boyer-Moore algorithm. canFind canFind("hello world", "or") returns true. count Counts all elements or elements matching a predicate, specific element or sub-range.
count([1, 2, 1]) returns 3,
count([1, 2, 1], 1) returns 2 and
count!"a < 0"([1, -3, 0]) returns 1. |
| countUntil | countUntil(a, b) returns the number of steps taken in a to reach b; for example, countUntil("hello!", "o") returns 4. |
| commonPrefix | commonPrefix("parakeet", "parachute") returns "para". |
| endsWith | endsWith("rocks", "ks") returns true. |
| extrema | extrema([2, 1, 3, 5, 4]) returns [1, 5]. |
| find | find("hello world", "or") returns "orld" using linear search. (For binary search refer to SortedRange.) |
| findAdjacent | findAdjacent([1, 2, 3, 3, 4]) returns the subrange starting with two equal adjacent elements, i.e. [3, 3, 4]. |
| findAmong | findAmong("abcd", "qcx") returns "cd" because 'c' is among "qcx". |
| findSkip | If a = "abcde", then findSkip(a, "x") returns false and leaves a unchanged, whereas findSkip(a, "c") advances a to "de" and returns true. |
| findSplit | findSplit("abcdefg", "de") returns a tuple of three ranges "abc", "de", and "fg". |
| findSplitAfter | findSplitAfter("abcdefg", "de") returns a tuple of two ranges "abcde" and "fg". |
| findSplitBefore | findSplitBefore("abcdefg", "de") returns a tuple of two ranges "abc" and "defg". |
| minCount | minCount([2, 1, 1, 4, 1]) returns tuple(1, 3). |
| maxCount | maxCount([2, 4, 1, 4, 1]) returns tuple(4, 2). |
| minElement | Selects the minimal element of a range. minElement([3, 4, 1, 2]) returns 1. |
| maxElement | Selects the maximal element of a range. maxElement([3, 4, 1, 2]) returns 4. |
| minIndex | Index of the minimal element of a range. minIndex([3, 4, 1, 2]) returns 2. |
| maxIndex | Index of the maximal element of a range. maxIndex([3, 4, 1, 2]) returns 1. |
| minPos | minPos([2, 3, 1, 3, 4, 1]) returns the subrange [1, 3, 4, 1], i.e., positions the range at the first occurrence of its minimal element. |
| maxPos | maxPos([2, 3, 1, 3, 4, 1]) returns the subrange [4, 1], i.e., positions the range at the first occurrence of its maximal element. |
| skipOver | Assume a = "blah". Then skipOver(a, "bi") leaves a unchanged and returns false, whereas skipOver(a, "bl") advances a to refer to "ah" and returns true. |
| startsWith | startsWith("hello, world", "hello") returns true. |
| until | Lazily iterates a range until a specific value is found. |
Source
std/algorithm/searching.d
searching : (alias template) component_walk.canFind = std.algorithm.searching.canFind(alias pred = "a == b")Convenience function. Like find, but only returns whether or not the search
was successful.
For more information about pred see find.
canFind;
import (package) stdstd.(module) std.arrayFunctions and types that manipulate built-in arrays and associative arrays.
This module provides all kinds of functions to create, manipulate or convert arrays:
Function Name Description
| array |
Returns a copy of the input in a newly allocated dynamic array.
|
| appender |
Returns a new Appender or RefAppender initialized with a given array.
|
| assocArray |
Returns a newly allocated associative array from a range/ranges of keys and values.
|
| byPair |
Construct a range iterating over an associative array by key/value tuples.
|
| insertInPlace |
Inserts into an existing array at a given position.
|
| join |
Concatenates a range of ranges into one array.
|
| minimallyInitializedArray |
Returns a new array of type T.
|
| replace |
Returns a new array with all occurrences of a certain subrange replaced.
|
| replaceFirst |
Returns a new array with the first occurrence of a certain subrange replaced.
|
| replaceInPlace |
Replaces all occurrences of a certain subrange and puts the result into a given array.
|
| replaceInto |
Replaces all occurrences of a certain subrange and puts the result into an output range.
|
| replaceLast |
Returns a new array with the last occurrence of a certain subrange replaced.
|
| replaceSlice |
Returns a new array with a given slice replaced.
|
| replicate |
Creates a new array out of several copies of an input array or range.
|
| sameHead |
Checks if the initial segments of two arrays refer to the same
place in memory.
|
| sameTail |
Checks if the final segments of two arrays refer to the same place
in memory.
|
| split |
Eagerly split a range or string into an array.
|
| staticArray |
Creates a new static array from given data.
|
| uninitializedArray |
Returns a new array of type T without initializing its elements.
|
Source
std/array.d
array : (alias template) component_walk.array = std.array.array(Range)(Range r) if (isIterable!Range && !isAutodecodableString!Range && !isInfinite!Range)Allocates an array and initializes it with copies of the elements
of range r.
Narrow strings are handled as follows:
If autodecoding is turned on (default), then they are handled as a separate overload.
If autodecoding is turned off, then this is equivalent to duplicating the array.
array;
import (package) stdstd.(module) std.convA one-stop shop for converting values from one type to another.
Category Functions Generic asOriginalType castFrom parse to toChars bitCast Strings text wtext dtext writeText writeWText writeDText hexString Numeric octal roundTo signed unsigned Exceptions ConvException ConvOverflowException
Source
std/conv.d
conv : (alias template) component_walk.text = std.conv.text(T...)(T args) if (T.length > 0)Convenience functions for converting one or more arguments
of any type into text (the three character widths).
text;
import (package) stdstd.(module) std.fileUtilities for manipulating files and scanning directories. Functions
in this module handle files as a unit, e.g., read or write one file
at a time. For opening files and manipulating them via handles refer
to module std.stdio.
Category Functions General exists isDir isFile isSymlink rename thisExePath Directories chdir dirEntries getcwd mkdir mkdirRecurse rmdir rmdirRecurse tempDir Files append copy read readText remove slurp write Symlinks symlink readLink Attributes attrIsDir attrIsFile attrIsSymlink getAttributes getLinkAttributes getSize setAttributes Timestamp getTimes getTimesWin setTimes timeLastModified timeLastAccessed timeStatusChanged Other DirEntry FileException PreserveAttributes SpanMode getAvailableDiskSpace
Source
std/file.d
file : (alias template) component_walk.exists = std.file.exists(R)(R name) if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)Determine whether the given file (or directory) exists.
exists, (alias) component_walk.mkdirRecurse = void std.file.mkdirRecurse(scope const(char)[] pathname) @safeMake directory and all parent directories as needed.
Does nothing if the directory specified by
pathname already exists.
mkdirRecurse, (alias) component_walk.rmdirRecurse = void std.file.rmdirRecurse(scope const(char)[] pathname) @safeRemove directory and all of its content and subdirectories,
recursively.
rmdirRecurse, (alias template) component_walk.symlink = std.file.symlink(RO, RL)(RO original, RL link) if ((isSomeFiniteCharInputRange!RO || isConvertibleToString!RO) && (isSomeFiniteCharInputRange!RL || isConvertibleToString!RL))This function is POSIX-Only.
Creates a symbolic link (symlink).
symlink, (alias) component_walk.tempDir = string std.file.tempDir() @trustedReturns the path to a directory for temporary files.
On POSIX platforms, it searches through the following list of directories
and returns the first one which is found to exist:
The directory given by the TMPDIR environment variable.
The directory given by the TEMP environment variable.
The directory given by the TMP environment variable.
/tmp/
/var/tmp/
/usr/tmp/
On all platforms, tempDir returns the current working directory on failure.
The return value of the function is cached, so the procedures described
below will only be performed the first time the function is called. All
subsequent runs will return the same string, regardless of whether
environment variables and directory structures have changed in the
meantime.
The POSIX tempDir algorithm is inspired by Python's
tempfile.tempdir.
tempDir, (alias template) component_walk.write = std.file.write(R)(R name, const void[] buffer) if ((isSomeFiniteCharInputRange!R || isSomeString!R) && !isConvertibleToString!R)Write buffer to file name.
Creates the file if it does not already exist.
write;
import (package) stdstd.(module) std.pathThis module is used to manipulate path strings.
All functions, with the exception of expandTilde (and in some
cases absolutePath and relativePath), are pure
string manipulation functions; they don't depend on any state outside
the program, nor do they perform any actual file system actions.
This has the consequence that the module does not make any distinction
between a path that points to a directory and a path that points to a
file, and it does not know whether or not the object pointed to by the
path actually exists in the file system.
To differentiate between these cases, use isDir and
exists.
Note that on Windows, both the backslash (\) and the slash (/)
are in principle valid directory separators. This module treats them
both on equal footing, but in cases where a new separator is
added, a backslash will be used. Furthermore, the buildNormalizedPath
function will replace all slashes with backslashes on that platform.
In general, the functions in this module assume that the input paths
are well-formed. (That is, they should not contain invalid characters,
they should follow the file system's path format, etc.) The result
of calling a function on an ill-formed path is undefined. When there
is a chance that a path or a file name is invalid (for instance, when it
has been input by the user), it may sometimes be desirable to use the
isValidFilename and isValidPath functions to check
this.
Most functions do not perform any memory allocations, and if a string is
returned, it is usually a slice of an input string. If a function
allocates, this is explicitly mentioned in the documentation.
Category Functions Normalization absolutePath asAbsolutePath asNormalizedPath asRelativePath buildNormalizedPath buildPath chainPath expandTilde Partitioning baseName dirName dirSeparator driveName pathSeparator pathSplitter relativePath rootName stripDrive Validation isAbsolute isDirSeparator isRooted isValidFilename isValidPath Extension defaultExtension extension setExtension stripExtension withDefaultExtension withExtension Other filenameCharCmp filenameCmp globMatch CaseSensitive
Source
std/path.d
path : (alias template) component_walk.buildPath = std.path.buildPath(Range)(scope Range segments) if (isInputRange!Range && !isInfinite!Range && isSomeString!(ElementType!Range))Combines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
buildPath, isAbsolute;
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) component_walk.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, (alias template) component_walk.writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
import (package) stdstd.(module) std.stringString handling functions.
Category Functions Searching column indexOf indexOfAny indexOfNeither lastIndexOf lastIndexOfAny lastIndexOfNeither Comparison isNumeric Mutation capitalize Pruning and Filling center chomp chompPrefix chop detabber detab entab entabber leftJustify outdent rightJustify strip stripLeft stripRight wrap Substitution abbrev soundex soundexer succ tr translate Miscellaneous assumeUTF fromStringz lineSplitter representation splitLines toStringz Objects of types string, wstring, and dstring are value types and cannot be mutated element-by-element. For using mutation during building strings, use char[], wchar[], or dchar[]. The xxxstring types are preferable because they don't exhibit undesired aliasing, thus making code more robust.
The following functions are publicly imported:
Module Functions Publicly imported functions std.algorithm cmp, std,algorithm,comparison count, std,algorithm,searching endsWith, std,algorithm,searching startsWith, std,algorithm,searching std.array join, std,array replace, std,array replaceInPlace, std,array split, std,array empty, std,array std.format format, std,format sformat, std,format std.uni icmp, std,uni toLower, std,uni toLowerInPlace, std,uni toUpper, std,uni toUpperInPlace, std,uni There is a rich set of functions for string handling defined in other modules. Functions related to Unicode and ASCII are found in std.uni and std.ascii, respectively. Other functions that have a wider generality than just strings can be found in std.algorithm and std.range.
Source
std/string.d
string : (alias) component_walk.toStringz = immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedtoStringz;
/// The outcome of a walk: an fd (>= 0), or a negative errno, or `-1` with a
/// lexical reason (the walk never reached the kernel).
struct (struct) component_walk.WalkedThe outcome of a walk: an fd (>= 0), or a negative errno, or -1 with a
lexical reason (the walk never reached the kernel).
Walked
{
int (field) int component_walk.Walked.fdfd = -1;
(alias) object.string = stringstring (field) string component_walk.Walked.lexicalRefusallexicalRefusal;
int (field) int component_walk.Walked.errerr;
}
(struct) component_walk.WalkedThe outcome of a walk: an fd (>= 0), or a negative errno, or -1 with a
lexical reason (the walk never reached the kernel).
Walked component_walk.Walked component_walk.walk(int rootfd, string relative)walk(int (parameter) int rootfdrootfd, (alias) object.string = stringstring (parameter) string relativerelative)
{
(struct) component_walk.WalkedThe outcome of a walk: an fd (>= 0), or a negative errno, or -1 with a
lexical reason (the walk never reached the kernel).
Walked (local variable) component_walk.Walked ww;
if ((parameter) string relativerelative.isAbsolute)
{
(local variable) component_walk.Walked ww.(field) string component_walk.Walked.lexicalRefusallexicalRefusal = "absolute path";
return (local variable) component_walk.Walked ww;
}
auto (local variable) string[] partsparts = (parameter) string relativerelative.std.algorithm.iteration.splitter!("a == b", Flag.no, string, char).Result std.algorithm.iteration.splitter!("a == b", Flag.no, string, char)(string r, char s) pure nothrow @nogc @safeLazily splits a range using an element or range as a separator.
Separator ranges can be any narrow string type or sliceable range type.
Two adjacent separators are considered to surround an empty element in
the split range. Use filter!(a => !a.empty) on the result to compress
empty elements.
The predicate is passed to binaryFun and accepts
any callable function that can be executed via pred(element, s).
Note
If splitting a string on whitespace and token compression is desired,
consider using the ``splitter(r) overload.
Constraints
The predicate pred needs to accept an element of r and the
separator s.
Examples
Basic splitting with characters and numbers.
import std.algorithm.comparison : equal;
assert("a|bc|def".splitter('|').equal([ "a", "bc", "def" ]));
int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
int[][] w = [ [1], [2, 3], [4, 5, 6] ];
assert(a.splitter(0).equal(w));
Basic splitting with characters and numbers and keeping sentinels.
import std.algorithm.comparison : equal;
import std.typecons : Yes;
assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
.equal([ "a", "|", "bc", "|", "def" ]));
int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
int[][] w = [ [1], [0], [2, 3], [0], [4, 5, 6] ];
assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
Adjacent separators.
import std.algorithm.comparison : equal;
assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
assert("ab".splitter('|').equal([ "ab" ]));
assert("a|b||c".splitter('|').equal([ "a", "b", "", "c" ]));
assert("hello world".splitter(' ').equal([ "hello", "", "world" ]));
auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
auto w = [ [1, 2], [], [3], [4, 5], [] ];
assert(a.splitter(0).equal(w));
Adjacent separators and keeping sentinels.
import std.algorithm.comparison : equal;
import std.typecons : Yes;
assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
.equal([ "", "|", "ab", "|", "" ]));
assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
.equal([ "ab" ]));
assert("a|b||c".splitter!("a == b", Yes.keepSeparators)('|')
.equal([ "a", "|", "b", "|", "", "|", "c" ]));
assert("hello world".splitter!("a == b", Yes.keepSeparators)(' ')
.equal([ "hello", " ", "", " ", "world" ]));
auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
auto w = [ [1, 2], [0], [], [0], [3], [0], [4, 5], [0], [] ];
assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
Empty and separator-only ranges.
import std.algorithm.comparison : equal;
import std.range : empty;
assert("".splitter('|').empty);
assert("|".splitter('|').equal([ "", "" ]));
assert("||".splitter('|').equal([ "", "", "" ]));
Empty and separator-only ranges and keeping sentinels.
import std.algorithm.comparison : equal;
import std.typecons : Yes;
import std.range : empty;
assert("".splitter!("a == b", Yes.keepSeparators)('|').empty);
assert("|".splitter!("a == b", Yes.keepSeparators)('|')
.equal([ "", "|", "" ]));
assert("||".splitter!("a == b", Yes.keepSeparators)('|')
.equal([ "", "|", "", "|", "" ]));
Use a range for splitting
import std.algorithm.comparison : equal;
assert("a=>bc=>def".splitter("=>").equal([ "a", "bc", "def" ]));
assert("a|b||c".splitter("||").equal([ "a|b", "c" ]));
assert("hello world".splitter(" ").equal([ "hello", "world" ]));
int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
assert(a.splitter([0, 0]).equal(w));
a = [ 0, 0 ];
assert(a.splitter([0, 0]).equal([ (int[]).init, (int[]).init ]));
a = [ 0, 0, 1 ];
assert(a.splitter([0, 0]).equal([ [], [1] ]));
Use a range for splitting
import std.algorithm.comparison : equal;
import std.typecons : Yes;
assert("a=>bc=>def".splitter!("a == b", Yes.keepSeparators)("=>")
.equal([ "a", "=>", "bc", "=>", "def" ]));
assert("a|b||c".splitter!("a == b", Yes.keepSeparators)("||")
.equal([ "a|b", "||", "c" ]));
assert("hello world".splitter!("a == b", Yes.keepSeparators)(" ")
.equal([ "hello", " ", "world" ]));
int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
int[][] w = [ [1, 2], [0, 0], [3, 0, 4, 5, 0] ];
assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]).equal(w));
a = [ 0, 0 ];
assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
.equal([ (int[]).init, [0, 0], (int[]).init ]));
a = [ 0, 0, 1 ];
assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
.equal([ [], [0, 0], [1] ]));
Custom predicate functions.
import std.algorithm.comparison : equal;
import std.ascii : toLower;
assert("abXcdxef".splitter!"a.toLower == b"('x').equal(
[ "ab", "cd", "ef" ]));
auto w = [ [0], [1], [2] ];
assert(w.splitter!"a.front == b"(1).equal([ [[0]], [[2]] ]));
Custom predicate functions.
import std.algorithm.comparison : equal;
import std.typecons : Yes;
import std.ascii : toLower;
assert("abXcdxef".splitter!("a.toLower == b", Yes.keepSeparators)('x')
.equal([ "ab", "X", "cd", "x", "ef" ]));
auto w = [ [0], [1], [2] ];
assert(w.splitter!("a.front == b", Yes.keepSeparators)(1)
.equal([ [[0]], [[1]], [[2]] ]));
Leading separators, trailing separators, or no separators.
import std.algorithm.comparison : equal;
assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
assert("ab".splitter('|').equal([ "ab" ]));
Leading separators, trailing separators, or no separators.
import std.algorithm.comparison : equal;
import std.typecons : Yes;
assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
.equal([ "", "|", "ab", "|", "" ]));
assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
.equal([ "ab" ]));
Splitter returns bidirectional ranges if the delimiter is a single element
import std.algorithm.comparison : equal;
import std.range : retro;
assert("a|bc|def".splitter('|').retro.equal([ "def", "bc", "a" ]));
Splitter returns bidirectional ranges if the delimiter is a single element
import std.algorithm.comparison : equal;
import std.typecons : Yes;
import std.range : retro;
assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
.retro.equal([ "def", "|", "bc", "|", "a" ]));
splitter('/').component_walk.walk.FilterResult!(__lambda_L76_C49, Result) component_walk.walk.filter!(std.algorithm.iteration.splitter!("a == b", Flag.no, string, char).Result)(std.algorithm.iteration.splitter!("a == b", Flag.no, string, char).Result range) pure nothrow @nogc @safefilter`!(predicate)(`range`)` returns a new `range` containing only elements `x` in range`` for
which predicate(x) returns true.
The predicate is passed to unaryFun, and can be either a string, or
any callable that can be executed via pred(element).
Examples
import std.algorithm.comparison : equal;
import std.math.operations : isClose;
import std.range;
int[] arr = [ 1, 2, 3, 4, 5 ];
// Filter below 3
auto small = filter!(a => a < 3)(arr);
assert(equal(small, [ 1, 2 ]));
// Filter again, but with Uniform Function Call Syntax (UFCS)
auto sum = arr.filter!(a => a < 3);
assert(equal(sum, [ 1, 2 ]));
// In combination with chain() to span multiple ranges
int[] a = [ 3, -2, 400 ];
int[] b = [ 100, -101, 102 ];
auto r = chain(a, b).filter!(a => a > 0);
assert(equal(r, [ 3, 400, 100, 102 ]));
// Mixing convertible types is fair game, too
double[] c = [ 2.5, 3.0 ];
auto r1 = chain(c, a, b).filter!(a => cast(int) a != a);
assert(isClose(r1, [ 2.5 ]));
filter!(p => p.length && p != ".").string[] std.array.array!(component_walk.walk.FilterResult!(__lambda_L76_C49, Result))(component_walk.walk.FilterResult!(__lambda_L76_C49, Result) r) pure @safeAllocates an array and initializes it with copies of the elements
of range r.
Narrow strings are handled as follows:
If autodecoding is turned on (default), then they are handled as a separate overload.
If autodecoding is turned off, then this is equivalent to duplicating the array.
array;
if ((local variable) string[] partsparts.bool std.algorithm.searching.canFind!().canFind!(string[], string)(string[] haystack, scope string needle) pure nothrow @nogc @safeConvenience function. Like find, but only returns whether or not the search
was successful.
For more information about pred see find.
Examples
const arr = [0, 1, 2, 3];
assert(canFind(arr, 2));
assert(!canFind(arr, 4));
// find one of several needles
assert(arr.canFind(3, 2));
assert(arr.canFind(3, 2) == 2); // second needle found
assert(arr.canFind([1, 3], 2) == 2);
assert(canFind(arr, [1, 2], [2, 3]));
assert(canFind(arr, [1, 2], [2, 3]) == 1);
assert(canFind(arr, [1, 7], [2, 3]));
assert(canFind(arr, [1, 7], [2, 3]) == 2);
assert(!canFind(arr, [1, 3], [2, 4]));
assert(canFind(arr, [1, 3], [2, 4]) == 0);
Example using a custom predicate.
Note that the needle appears as the second argument of the predicate.
auto words = [
"apple",
"beeswax",
"cardboard"
];
assert(!canFind(words, "bees"));
assert( canFind!((string elem, string needle) => elem.startsWith(needle))(words, "bees"));
Search for multiple items in an array of items (search for needles in an array of haystacks)
string s1 = "aaa111aaa";
string s2 = "aaa222aaa";
string s3 = "aaa333aaa";
string s4 = "aaa444aaa";
const hay = [s1, s2, s3, s4];
assert(hay.canFind!(e => e.canFind("111", "222")));
canFind(".."))
{
(local variable) component_walk.Walked ww.(field) string component_walk.Walked.lexicalRefusallexicalRefusal = "`..` component";
return (local variable) component_walk.Walked ww;
}
int (local variable) int curcur = (parameter) int rootfdrootfd;
foreach ((parameter) ulong ii, (parameter) string partpart; (local variable) string[] partsparts)
{
const (local variable) const(bool) lastlast = (local variable) ulong ii + 1 == (local variable) string[] partsparts.(field) ulong string[].lengthlength;
const (local variable) const(int) flagsflags = (constant) int core.sys.posix.fcntl.O_RDONLY = 0O_RDONLY | (constant) int core.sys.posix.fcntl.O_NOFOLLOW = 131072O_NOFOLLOW | (constant) int core.sys.posix.fcntl.O_CLOEXEC = 524288O_CLOEXEC | ((local variable) const(bool) lastlast ? 0 : (constant) int core.sys.posix.fcntl.O_DIRECTORY = 65536O_DIRECTORY);
const (local variable) const(int) nextnext = openat(int core.sys.posix.fcntl.openat64(int, scope const(char*), int, ...) nothrow @nogccur, (local variable) string partpart.immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedExamples
import core.stdc.string : strlen;
import std.conv : to;
auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);
string test = "";
p = toStringz(test);
assert(*p == 0);
test = "\0";
p = toStringz(test);
assert(*p == 0);
test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);
const string test2 = "";
p = toStringz(test2);
assert(*p == 0);
assert(toStringz([]) is toStringz(""));
toStringz, (local variable) const(int) flagsflags);
const (local variable) const(int) ee = int core.stdc.errno.__errno_location() nothrow @nogc ref @trustederrno;
if ((local variable) int curcur != (parameter) int rootfdrootfd)
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int curcur);
if ((local variable) const(int) nextnext < 0)
{
(local variable) component_walk.Walked ww.(field) int component_walk.Walked.errerr = (local variable) const(int) ee;
return (local variable) component_walk.Walked ww;
}
(local variable) int curcur = (local variable) const(int) nextnext;
}
(local variable) component_walk.Walked ww.(field) int component_walk.Walked.fdfd = (local variable) int curcur;
return (local variable) component_walk.Walked ww;
}
(alias) object.string = stringstring string component_walk.errName(int e)errName(int (parameter) int ee)
{
switch ((parameter) int ee)
{
case (constant) int core.stdc.errno.ELOOP = 40ELOOP: return "ELOOP";
case (constant) int core.stdc.errno.ENOENT = 2ENOENT: return "ENOENT";
case (constant) int core.stdc.errno.ENOTDIR = 20ENOTDIR: return "ENOTDIR";
default: return string std.conv.text!(string, int)(string __param_0, int __param_1) pure nothrow @safeConvenience functions for converting one or more arguments
of any type into text (the three character widths).
text("errno ", (parameter) int ee);
}
}
void void component_walk.report(int rootfd, string path)report(int (parameter) int rootfdrootfd, (alias) object.string = stringstring (parameter) string pathpath)
{
auto (local variable) component_walk.Walked ww = component_walk.Walked component_walk.walk(int rootfd, string relative)walk((parameter) int rootfdrootfd, (parameter) string pathpath);
(alias) object.string = stringstring (local variable) string outcomeoutcome;
if ((local variable) component_walk.Walked ww.(field) string component_walk.Walked.lexicalRefusallexicalRefusal.(field) ulong string.lengthlength)
(local variable) string outcomeoutcome = "refused lexically: " ~ (local variable) component_walk.Walked ww.(field) string component_walk.Walked.lexicalRefusallexicalRefusal;
else if ((local variable) component_walk.Walked ww.(field) int component_walk.Walked.fdfd >= 0)
{
(local variable) string outcomeoutcome = "ok";
if ((local variable) component_walk.Walked ww.(field) int component_walk.Walked.fdfd != (parameter) int rootfdrootfd)
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) component_walk.Walked ww.(field) int component_walk.Walked.fdfd);
}
else
(local variable) string outcomeoutcome = string component_walk.errName(int e)errName((local variable) component_walk.Walked ww.(field) int component_walk.Walked.errerr);
void std.stdio.writefln!(char, string, string)(in char[] fmt, string __param_1, string __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-32s %s", (parameter) string pathpath, (local variable) string outcomeoutcome);
}
int int D main()main()
{
const (local variable) const(string) rootroot = string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath(string std.file.tempDir() @trustedReturns the path to a directory for temporary files.
On POSIX platforms, it searches through the following list of directories
and returns the first one which is found to exist:
The directory given by the TMPDIR environment variable.
The directory given by the TEMP environment variable.
The directory given by the TMP environment variable.
/tmp/
/var/tmp/
/usr/tmp/
On all platforms, tempDir returns the current working directory on failure.
The return value of the function is cached, so the procedures described
below will only be performed the first time the function is called. All
subsequent runs will return the same string, regardless of whether
environment variables and directory structures have changed in the
meantime.
The POSIX tempDir algorithm is inspired by Python's
tempfile.tempdir.
Examples
import std.ascii : letters;
import std.conv : to;
import std.path : buildPath;
import std.random : randomSample;
import std.utf : byCodeUnit;
// random id with 20 letters
auto id = letters.byCodeUnit.randomSample(20).to!string;
auto myFile = tempDir.buildPath(id ~ "my_tmp_file");
scope(exit) myFile.remove;
myFile.write("hello");
assert(myFile.readText == "hello");
tempDir, "component-walk-demo");
if ((local variable) const(string) rootroot.bool std.file.exists!string(string name) nothrow @nogc @safeDetermine whether the given file (or directory) exists.
exists)
void std.file.rmdirRecurse(scope const(char)[] pathname) @safeRemove directory and all of its content and subdirectories,
recursively.
rmdirRecurse((local variable) const(string) rootroot);
scope (exit) void std.file.rmdirRecurse(scope const(char)[] pathname) @safeRemove directory and all of its content and subdirectories,
recursively.
rmdirRecurse((local variable) const(string) rootroot);
void std.file.mkdirRecurse(scope const(char)[] pathname) @safeMake directory and all parent directories as needed.
Does nothing if the directory specified by
pathname already exists.
Examples
import std.path : buildPath;
auto dir = deleteme ~ "dir";
scope(exit) dir.rmdirRecurse;
dir.mkdir;
assert(dir.exists);
dir.mkdirRecurse; // does nothing
// creates all parent directories as needed
auto nested = dir.buildPath("a", "b", "c");
nested.mkdirRecurse;
assert(nested.exists);
import std.exception : assertThrown;
scope(exit) deleteme.remove;
deleteme.write("a");
// cannot make directory as it's already a file
assertThrown!FileException(deleteme.mkdirRecurse);
mkdirRecurse(string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath((local variable) const(string) rootroot, "a", "b"));
void std.file.write!string(string name, const(void[]) buffer) @safeWrite buffer to file name.
Creates the file if it does not already exist.
Examples
scope(exit)
{
assert(exists(deleteme));
remove(deleteme);
}
int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
write(deleteme, a); // deleteme is the name of a temporary file
const bytes = read(deleteme);
const fileInts = () @trusted { return cast(int[]) bytes; }();
assert(fileInts == a);
write(string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath((local variable) const(string) rootroot, "a", "b", "leaf.txt"), "x");
void std.file.symlink!(string, string)(string original, string link) @safeThis function is POSIX-Only.
Creates a symbolic link (symlink).
symlink("a", string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath((local variable) const(string) rootroot, "link-dir")); // a symlinked intermediate
void std.file.symlink!(string, string)(string original, string link) @safeThis function is POSIX-Only.
Creates a symbolic link (symlink).
symlink("b/leaf.txt", string std.path.buildPath!char(const(char)[][] paths...) pure nothrow @safeCombines one or more path segments.
This function takes a set of path segments, given as an input
range of string elements or as a set of string arguments,
and concatenates them with each other. Directory separators
are inserted between segments if necessary. If any of the
path segments are absolute (as defined by isAbsolute), the
preceding segments will be dropped.
On Windows, if one of the path segments are rooted, but not absolute
(e.g. \foo), all preceding path segments down to the previous
root will be dropped. (See below for an example.)
This function always allocates memory to hold the resulting path.
The variadic overload is guaranteed to only perform a single
allocation, as is the range version if paths is a forward
range.
Examples
version (Posix)
{
assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
assert(buildPath("/foo/", "bar/baz") == "/foo/bar/baz");
assert(buildPath("/foo", "/bar") == "/bar");
}
version (Windows)
{
assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
assert(buildPath("foo", `d:\bar`) == `d:\bar`);
assert(buildPath("foo", `\bar`) == `\bar`);
assert(buildPath(`c:\foo`, `\bar`) == `c:\bar`);
}
buildPath((local variable) const(string) rootroot, "a", "link-leaf")); // a symlinked leaf
const (local variable) const(int) rootfdrootfd = open(int core.sys.posix.fcntl.open64(scope const(char*), int, ...) nothrow @nogcroot.immutable(char)* std.string.toStringz(scope const(char)[] s) pure nothrow @trustedExamples
import core.stdc.string : strlen;
import std.conv : to;
auto p = toStringz("foo");
assert(strlen(p) == 3);
const(char)[] foo = "abbzxyzzy";
p = toStringz(foo[3 .. 5]);
assert(strlen(p) == 2);
string test = "";
p = toStringz(test);
assert(*p == 0);
test = "\0";
p = toStringz(test);
assert(*p == 0);
test = "foo\0";
p = toStringz(test);
assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);
const string test2 = "";
p = toStringz(test2);
assert(*p == 0);
assert(toStringz([]) is toStringz(""));
toStringz, (constant) int core.sys.posix.fcntl.O_RDONLY = 0O_RDONLY | (constant) int core.sys.posix.fcntl.O_DIRECTORY = 65536O_DIRECTORY | (constant) int core.sys.posix.fcntl.O_CLOEXEC = 524288O_CLOEXEC);
assert((local variable) const(int) rootfdrootfd >= 0);
scope (exit) int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) const(int) rootfdrootfd);
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln("component walk, O_NOFOLLOW on every step:");
void component_walk.report(int rootfd, string path)report((local variable) const(int) rootfdrootfd, "a/b/leaf.txt");
void component_walk.report(int rootfd, string path)report((local variable) const(int) rootfdrootfd, "./a//b/leaf.txt");
void component_walk.report(int rootfd, string path)report((local variable) const(int) rootfdrootfd, "link-dir/b/leaf.txt");
void component_walk.report(int rootfd, string path)report((local variable) const(int) rootfdrootfd, "a/link-leaf");
void component_walk.report(int rootfd, string path)report((local variable) const(int) rootfdrootfd, "a/../a/b/leaf.txt");
void component_walk.report(int rootfd, string path)report((local variable) const(int) rootfdrootfd, "/etc/hostname");
void component_walk.report(int rootfd, string path)report((local variable) const(int) rootfdrootfd, "a/b/leaf.txt/deeper");
void component_walk.report(int rootfd, string path)report((local variable) const(int) rootfdrootfd, "a/missing");
return 0;
}