xref: /freebsd-src/contrib/llvm-project/libcxx/src/filesystem/path_parser.h (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
1*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
2*06c3fb27SDimitry Andric //
3*06c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*06c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*06c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*06c3fb27SDimitry Andric //
7*06c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
8*06c3fb27SDimitry Andric 
9*06c3fb27SDimitry Andric #ifndef PATH_PARSER_H
10*06c3fb27SDimitry Andric #define PATH_PARSER_H
11*06c3fb27SDimitry Andric 
12*06c3fb27SDimitry Andric #include <__config>
13*06c3fb27SDimitry Andric #include <__utility/unreachable.h>
14*06c3fb27SDimitry Andric #include <cstddef>
15*06c3fb27SDimitry Andric #include <filesystem>
16*06c3fb27SDimitry Andric #include <utility>
17*06c3fb27SDimitry Andric 
18*06c3fb27SDimitry Andric #include "format_string.h"
19*06c3fb27SDimitry Andric 
20*06c3fb27SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
21*06c3fb27SDimitry Andric 
22*06c3fb27SDimitry Andric inline bool isSeparator(path::value_type C) {
23*06c3fb27SDimitry Andric   if (C == '/')
24*06c3fb27SDimitry Andric     return true;
25*06c3fb27SDimitry Andric #if defined(_LIBCPP_WIN32API)
26*06c3fb27SDimitry Andric   if (C == '\\')
27*06c3fb27SDimitry Andric     return true;
28*06c3fb27SDimitry Andric #endif
29*06c3fb27SDimitry Andric   return false;
30*06c3fb27SDimitry Andric }
31*06c3fb27SDimitry Andric 
32*06c3fb27SDimitry Andric inline bool isDriveLetter(path::value_type C) {
33*06c3fb27SDimitry Andric   return (C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z');
34*06c3fb27SDimitry Andric }
35*06c3fb27SDimitry Andric 
36*06c3fb27SDimitry Andric namespace parser {
37*06c3fb27SDimitry Andric 
38*06c3fb27SDimitry Andric using string_view_t = path::__string_view;
39*06c3fb27SDimitry Andric using string_view_pair = pair<string_view_t, string_view_t>;
40*06c3fb27SDimitry Andric using PosPtr = path::value_type const*;
41*06c3fb27SDimitry Andric 
42*06c3fb27SDimitry Andric struct PathParser {
43*06c3fb27SDimitry Andric   enum ParserState : unsigned char {
44*06c3fb27SDimitry Andric     // Zero is a special sentinel value used by default constructed iterators.
45*06c3fb27SDimitry Andric     PS_BeforeBegin = path::iterator::_BeforeBegin,
46*06c3fb27SDimitry Andric     PS_InRootName = path::iterator::_InRootName,
47*06c3fb27SDimitry Andric     PS_InRootDir = path::iterator::_InRootDir,
48*06c3fb27SDimitry Andric     PS_InFilenames = path::iterator::_InFilenames,
49*06c3fb27SDimitry Andric     PS_InTrailingSep = path::iterator::_InTrailingSep,
50*06c3fb27SDimitry Andric     PS_AtEnd = path::iterator::_AtEnd
51*06c3fb27SDimitry Andric   };
52*06c3fb27SDimitry Andric 
53*06c3fb27SDimitry Andric   const string_view_t Path;
54*06c3fb27SDimitry Andric   string_view_t RawEntry;
55*06c3fb27SDimitry Andric   ParserState State;
56*06c3fb27SDimitry Andric 
57*06c3fb27SDimitry Andric private:
58*06c3fb27SDimitry Andric   PathParser(string_view_t P, ParserState State) noexcept : Path(P),
59*06c3fb27SDimitry Andric                                                             State(State) {}
60*06c3fb27SDimitry Andric 
61*06c3fb27SDimitry Andric public:
62*06c3fb27SDimitry Andric   PathParser(string_view_t P, string_view_t E, unsigned char S)
63*06c3fb27SDimitry Andric       : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
64*06c3fb27SDimitry Andric     // S cannot be '0' or PS_BeforeBegin.
65*06c3fb27SDimitry Andric   }
66*06c3fb27SDimitry Andric 
67*06c3fb27SDimitry Andric   static PathParser CreateBegin(string_view_t P) noexcept {
68*06c3fb27SDimitry Andric     PathParser PP(P, PS_BeforeBegin);
69*06c3fb27SDimitry Andric     PP.increment();
70*06c3fb27SDimitry Andric     return PP;
71*06c3fb27SDimitry Andric   }
72*06c3fb27SDimitry Andric 
73*06c3fb27SDimitry Andric   static PathParser CreateEnd(string_view_t P) noexcept {
74*06c3fb27SDimitry Andric     PathParser PP(P, PS_AtEnd);
75*06c3fb27SDimitry Andric     return PP;
76*06c3fb27SDimitry Andric   }
77*06c3fb27SDimitry Andric 
78*06c3fb27SDimitry Andric   PosPtr peek() const noexcept {
79*06c3fb27SDimitry Andric     auto TkEnd = getNextTokenStartPos();
80*06c3fb27SDimitry Andric     auto End = getAfterBack();
81*06c3fb27SDimitry Andric     return TkEnd == End ? nullptr : TkEnd;
82*06c3fb27SDimitry Andric   }
83*06c3fb27SDimitry Andric 
84*06c3fb27SDimitry Andric   void increment() noexcept {
85*06c3fb27SDimitry Andric     const PosPtr End = getAfterBack();
86*06c3fb27SDimitry Andric     const PosPtr Start = getNextTokenStartPos();
87*06c3fb27SDimitry Andric     if (Start == End)
88*06c3fb27SDimitry Andric       return makeState(PS_AtEnd);
89*06c3fb27SDimitry Andric 
90*06c3fb27SDimitry Andric     switch (State) {
91*06c3fb27SDimitry Andric     case PS_BeforeBegin: {
92*06c3fb27SDimitry Andric       PosPtr TkEnd = consumeRootName(Start, End);
93*06c3fb27SDimitry Andric       if (TkEnd)
94*06c3fb27SDimitry Andric         return makeState(PS_InRootName, Start, TkEnd);
95*06c3fb27SDimitry Andric     }
96*06c3fb27SDimitry Andric       _LIBCPP_FALLTHROUGH();
97*06c3fb27SDimitry Andric     case PS_InRootName: {
98*06c3fb27SDimitry Andric       PosPtr TkEnd = consumeAllSeparators(Start, End);
99*06c3fb27SDimitry Andric       if (TkEnd)
100*06c3fb27SDimitry Andric         return makeState(PS_InRootDir, Start, TkEnd);
101*06c3fb27SDimitry Andric       else
102*06c3fb27SDimitry Andric         return makeState(PS_InFilenames, Start, consumeName(Start, End));
103*06c3fb27SDimitry Andric     }
104*06c3fb27SDimitry Andric     case PS_InRootDir:
105*06c3fb27SDimitry Andric       return makeState(PS_InFilenames, Start, consumeName(Start, End));
106*06c3fb27SDimitry Andric 
107*06c3fb27SDimitry Andric     case PS_InFilenames: {
108*06c3fb27SDimitry Andric       PosPtr SepEnd = consumeAllSeparators(Start, End);
109*06c3fb27SDimitry Andric       if (SepEnd != End) {
110*06c3fb27SDimitry Andric         PosPtr TkEnd = consumeName(SepEnd, End);
111*06c3fb27SDimitry Andric         if (TkEnd)
112*06c3fb27SDimitry Andric           return makeState(PS_InFilenames, SepEnd, TkEnd);
113*06c3fb27SDimitry Andric       }
114*06c3fb27SDimitry Andric       return makeState(PS_InTrailingSep, Start, SepEnd);
115*06c3fb27SDimitry Andric     }
116*06c3fb27SDimitry Andric 
117*06c3fb27SDimitry Andric     case PS_InTrailingSep:
118*06c3fb27SDimitry Andric       return makeState(PS_AtEnd);
119*06c3fb27SDimitry Andric 
120*06c3fb27SDimitry Andric     case PS_AtEnd:
121*06c3fb27SDimitry Andric       __libcpp_unreachable();
122*06c3fb27SDimitry Andric     }
123*06c3fb27SDimitry Andric   }
124*06c3fb27SDimitry Andric 
125*06c3fb27SDimitry Andric   void decrement() noexcept {
126*06c3fb27SDimitry Andric     const PosPtr REnd = getBeforeFront();
127*06c3fb27SDimitry Andric     const PosPtr RStart = getCurrentTokenStartPos() - 1;
128*06c3fb27SDimitry Andric     if (RStart == REnd) // we're decrementing the begin
129*06c3fb27SDimitry Andric       return makeState(PS_BeforeBegin);
130*06c3fb27SDimitry Andric 
131*06c3fb27SDimitry Andric     switch (State) {
132*06c3fb27SDimitry Andric     case PS_AtEnd: {
133*06c3fb27SDimitry Andric       // Try to consume a trailing separator or root directory first.
134*06c3fb27SDimitry Andric       if (PosPtr SepEnd = consumeAllSeparators(RStart, REnd)) {
135*06c3fb27SDimitry Andric         if (SepEnd == REnd)
136*06c3fb27SDimitry Andric           return makeState(PS_InRootDir, Path.data(), RStart + 1);
137*06c3fb27SDimitry Andric         PosPtr TkStart = consumeRootName(SepEnd, REnd);
138*06c3fb27SDimitry Andric         if (TkStart == REnd)
139*06c3fb27SDimitry Andric           return makeState(PS_InRootDir, RStart, RStart + 1);
140*06c3fb27SDimitry Andric         return makeState(PS_InTrailingSep, SepEnd + 1, RStart + 1);
141*06c3fb27SDimitry Andric       } else {
142*06c3fb27SDimitry Andric         PosPtr TkStart = consumeRootName(RStart, REnd);
143*06c3fb27SDimitry Andric         if (TkStart == REnd)
144*06c3fb27SDimitry Andric           return makeState(PS_InRootName, TkStart + 1, RStart + 1);
145*06c3fb27SDimitry Andric         TkStart = consumeName(RStart, REnd);
146*06c3fb27SDimitry Andric         return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
147*06c3fb27SDimitry Andric       }
148*06c3fb27SDimitry Andric     }
149*06c3fb27SDimitry Andric     case PS_InTrailingSep:
150*06c3fb27SDimitry Andric       return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1,
151*06c3fb27SDimitry Andric                        RStart + 1);
152*06c3fb27SDimitry Andric     case PS_InFilenames: {
153*06c3fb27SDimitry Andric       PosPtr SepEnd = consumeAllSeparators(RStart, REnd);
154*06c3fb27SDimitry Andric       if (SepEnd == REnd)
155*06c3fb27SDimitry Andric         return makeState(PS_InRootDir, Path.data(), RStart + 1);
156*06c3fb27SDimitry Andric       PosPtr TkStart = consumeRootName(SepEnd ? SepEnd : RStart, REnd);
157*06c3fb27SDimitry Andric       if (TkStart == REnd) {
158*06c3fb27SDimitry Andric         if (SepEnd)
159*06c3fb27SDimitry Andric           return makeState(PS_InRootDir, SepEnd + 1, RStart + 1);
160*06c3fb27SDimitry Andric         return makeState(PS_InRootName, TkStart + 1, RStart + 1);
161*06c3fb27SDimitry Andric       }
162*06c3fb27SDimitry Andric       TkStart = consumeName(SepEnd, REnd);
163*06c3fb27SDimitry Andric       return makeState(PS_InFilenames, TkStart + 1, SepEnd + 1);
164*06c3fb27SDimitry Andric     }
165*06c3fb27SDimitry Andric     case PS_InRootDir:
166*06c3fb27SDimitry Andric       return makeState(PS_InRootName, Path.data(), RStart + 1);
167*06c3fb27SDimitry Andric     case PS_InRootName:
168*06c3fb27SDimitry Andric     case PS_BeforeBegin:
169*06c3fb27SDimitry Andric       __libcpp_unreachable();
170*06c3fb27SDimitry Andric     }
171*06c3fb27SDimitry Andric   }
172*06c3fb27SDimitry Andric 
173*06c3fb27SDimitry Andric   /// \brief Return a view with the "preferred representation" of the current
174*06c3fb27SDimitry Andric   ///   element. For example trailing separators are represented as a '.'
175*06c3fb27SDimitry Andric   string_view_t operator*() const noexcept {
176*06c3fb27SDimitry Andric     switch (State) {
177*06c3fb27SDimitry Andric     case PS_BeforeBegin:
178*06c3fb27SDimitry Andric     case PS_AtEnd:
179*06c3fb27SDimitry Andric       return PATHSTR("");
180*06c3fb27SDimitry Andric     case PS_InRootDir:
181*06c3fb27SDimitry Andric       if (RawEntry[0] == '\\')
182*06c3fb27SDimitry Andric         return PATHSTR("\\");
183*06c3fb27SDimitry Andric       else
184*06c3fb27SDimitry Andric         return PATHSTR("/");
185*06c3fb27SDimitry Andric     case PS_InTrailingSep:
186*06c3fb27SDimitry Andric       return PATHSTR("");
187*06c3fb27SDimitry Andric     case PS_InRootName:
188*06c3fb27SDimitry Andric     case PS_InFilenames:
189*06c3fb27SDimitry Andric       return RawEntry;
190*06c3fb27SDimitry Andric     }
191*06c3fb27SDimitry Andric     __libcpp_unreachable();
192*06c3fb27SDimitry Andric   }
193*06c3fb27SDimitry Andric 
194*06c3fb27SDimitry Andric   explicit operator bool() const noexcept {
195*06c3fb27SDimitry Andric     return State != PS_BeforeBegin && State != PS_AtEnd;
196*06c3fb27SDimitry Andric   }
197*06c3fb27SDimitry Andric 
198*06c3fb27SDimitry Andric   PathParser& operator++() noexcept {
199*06c3fb27SDimitry Andric     increment();
200*06c3fb27SDimitry Andric     return *this;
201*06c3fb27SDimitry Andric   }
202*06c3fb27SDimitry Andric 
203*06c3fb27SDimitry Andric   PathParser& operator--() noexcept {
204*06c3fb27SDimitry Andric     decrement();
205*06c3fb27SDimitry Andric     return *this;
206*06c3fb27SDimitry Andric   }
207*06c3fb27SDimitry Andric 
208*06c3fb27SDimitry Andric   bool atEnd() const noexcept {
209*06c3fb27SDimitry Andric     return State == PS_AtEnd;
210*06c3fb27SDimitry Andric   }
211*06c3fb27SDimitry Andric 
212*06c3fb27SDimitry Andric   bool inRootDir() const noexcept {
213*06c3fb27SDimitry Andric     return State == PS_InRootDir;
214*06c3fb27SDimitry Andric   }
215*06c3fb27SDimitry Andric 
216*06c3fb27SDimitry Andric   bool inRootName() const noexcept {
217*06c3fb27SDimitry Andric     return State == PS_InRootName;
218*06c3fb27SDimitry Andric   }
219*06c3fb27SDimitry Andric 
220*06c3fb27SDimitry Andric   bool inRootPath() const noexcept {
221*06c3fb27SDimitry Andric     return inRootName() || inRootDir();
222*06c3fb27SDimitry Andric   }
223*06c3fb27SDimitry Andric 
224*06c3fb27SDimitry Andric private:
225*06c3fb27SDimitry Andric   void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
226*06c3fb27SDimitry Andric     State = NewState;
227*06c3fb27SDimitry Andric     RawEntry = string_view_t(Start, End - Start);
228*06c3fb27SDimitry Andric   }
229*06c3fb27SDimitry Andric   void makeState(ParserState NewState) noexcept {
230*06c3fb27SDimitry Andric     State = NewState;
231*06c3fb27SDimitry Andric     RawEntry = {};
232*06c3fb27SDimitry Andric   }
233*06c3fb27SDimitry Andric 
234*06c3fb27SDimitry Andric   PosPtr getAfterBack() const noexcept { return Path.data() + Path.size(); }
235*06c3fb27SDimitry Andric 
236*06c3fb27SDimitry Andric   PosPtr getBeforeFront() const noexcept { return Path.data() - 1; }
237*06c3fb27SDimitry Andric 
238*06c3fb27SDimitry Andric   /// \brief Return a pointer to the first character after the currently
239*06c3fb27SDimitry Andric   ///   lexed element.
240*06c3fb27SDimitry Andric   PosPtr getNextTokenStartPos() const noexcept {
241*06c3fb27SDimitry Andric     switch (State) {
242*06c3fb27SDimitry Andric     case PS_BeforeBegin:
243*06c3fb27SDimitry Andric       return Path.data();
244*06c3fb27SDimitry Andric     case PS_InRootName:
245*06c3fb27SDimitry Andric     case PS_InRootDir:
246*06c3fb27SDimitry Andric     case PS_InFilenames:
247*06c3fb27SDimitry Andric       return &RawEntry.back() + 1;
248*06c3fb27SDimitry Andric     case PS_InTrailingSep:
249*06c3fb27SDimitry Andric     case PS_AtEnd:
250*06c3fb27SDimitry Andric       return getAfterBack();
251*06c3fb27SDimitry Andric     }
252*06c3fb27SDimitry Andric     __libcpp_unreachable();
253*06c3fb27SDimitry Andric   }
254*06c3fb27SDimitry Andric 
255*06c3fb27SDimitry Andric   /// \brief Return a pointer to the first character in the currently lexed
256*06c3fb27SDimitry Andric   ///   element.
257*06c3fb27SDimitry Andric   PosPtr getCurrentTokenStartPos() const noexcept {
258*06c3fb27SDimitry Andric     switch (State) {
259*06c3fb27SDimitry Andric     case PS_BeforeBegin:
260*06c3fb27SDimitry Andric     case PS_InRootName:
261*06c3fb27SDimitry Andric       return &Path.front();
262*06c3fb27SDimitry Andric     case PS_InRootDir:
263*06c3fb27SDimitry Andric     case PS_InFilenames:
264*06c3fb27SDimitry Andric     case PS_InTrailingSep:
265*06c3fb27SDimitry Andric       return &RawEntry.front();
266*06c3fb27SDimitry Andric     case PS_AtEnd:
267*06c3fb27SDimitry Andric       return &Path.back() + 1;
268*06c3fb27SDimitry Andric     }
269*06c3fb27SDimitry Andric     __libcpp_unreachable();
270*06c3fb27SDimitry Andric   }
271*06c3fb27SDimitry Andric 
272*06c3fb27SDimitry Andric   // Consume all consecutive separators.
273*06c3fb27SDimitry Andric   PosPtr consumeAllSeparators(PosPtr P, PosPtr End) const noexcept {
274*06c3fb27SDimitry Andric     if (P == nullptr || P == End || !isSeparator(*P))
275*06c3fb27SDimitry Andric       return nullptr;
276*06c3fb27SDimitry Andric     const int Inc = P < End ? 1 : -1;
277*06c3fb27SDimitry Andric     P += Inc;
278*06c3fb27SDimitry Andric     while (P != End && isSeparator(*P))
279*06c3fb27SDimitry Andric       P += Inc;
280*06c3fb27SDimitry Andric     return P;
281*06c3fb27SDimitry Andric   }
282*06c3fb27SDimitry Andric 
283*06c3fb27SDimitry Andric   // Consume exactly N separators, or return nullptr.
284*06c3fb27SDimitry Andric   PosPtr consumeNSeparators(PosPtr P, PosPtr End, int N) const noexcept {
285*06c3fb27SDimitry Andric     PosPtr Ret = consumeAllSeparators(P, End);
286*06c3fb27SDimitry Andric     if (Ret == nullptr)
287*06c3fb27SDimitry Andric       return nullptr;
288*06c3fb27SDimitry Andric     if (P < End) {
289*06c3fb27SDimitry Andric       if (Ret == P + N)
290*06c3fb27SDimitry Andric         return Ret;
291*06c3fb27SDimitry Andric     } else {
292*06c3fb27SDimitry Andric       if (Ret == P - N)
293*06c3fb27SDimitry Andric         return Ret;
294*06c3fb27SDimitry Andric     }
295*06c3fb27SDimitry Andric     return nullptr;
296*06c3fb27SDimitry Andric   }
297*06c3fb27SDimitry Andric 
298*06c3fb27SDimitry Andric   PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
299*06c3fb27SDimitry Andric     PosPtr Start = P;
300*06c3fb27SDimitry Andric     if (P == nullptr || P == End || isSeparator(*P))
301*06c3fb27SDimitry Andric       return nullptr;
302*06c3fb27SDimitry Andric     const int Inc = P < End ? 1 : -1;
303*06c3fb27SDimitry Andric     P += Inc;
304*06c3fb27SDimitry Andric     while (P != End && !isSeparator(*P))
305*06c3fb27SDimitry Andric       P += Inc;
306*06c3fb27SDimitry Andric     if (P == End && Inc < 0) {
307*06c3fb27SDimitry Andric       // Iterating backwards and consumed all the rest of the input.
308*06c3fb27SDimitry Andric       // Check if the start of the string would have been considered
309*06c3fb27SDimitry Andric       // a root name.
310*06c3fb27SDimitry Andric       PosPtr RootEnd = consumeRootName(End + 1, Start);
311*06c3fb27SDimitry Andric       if (RootEnd)
312*06c3fb27SDimitry Andric         return RootEnd - 1;
313*06c3fb27SDimitry Andric     }
314*06c3fb27SDimitry Andric     return P;
315*06c3fb27SDimitry Andric   }
316*06c3fb27SDimitry Andric 
317*06c3fb27SDimitry Andric   PosPtr consumeDriveLetter(PosPtr P, PosPtr End) const noexcept {
318*06c3fb27SDimitry Andric     if (P == End)
319*06c3fb27SDimitry Andric       return nullptr;
320*06c3fb27SDimitry Andric     if (P < End) {
321*06c3fb27SDimitry Andric       if (P + 1 == End || !isDriveLetter(P[0]) || P[1] != ':')
322*06c3fb27SDimitry Andric         return nullptr;
323*06c3fb27SDimitry Andric       return P + 2;
324*06c3fb27SDimitry Andric     } else {
325*06c3fb27SDimitry Andric       if (P - 1 == End || !isDriveLetter(P[-1]) || P[0] != ':')
326*06c3fb27SDimitry Andric         return nullptr;
327*06c3fb27SDimitry Andric       return P - 2;
328*06c3fb27SDimitry Andric     }
329*06c3fb27SDimitry Andric   }
330*06c3fb27SDimitry Andric 
331*06c3fb27SDimitry Andric   PosPtr consumeNetworkRoot(PosPtr P, PosPtr End) const noexcept {
332*06c3fb27SDimitry Andric     if (P == End)
333*06c3fb27SDimitry Andric       return nullptr;
334*06c3fb27SDimitry Andric     if (P < End)
335*06c3fb27SDimitry Andric       return consumeName(consumeNSeparators(P, End, 2), End);
336*06c3fb27SDimitry Andric     else
337*06c3fb27SDimitry Andric       return consumeNSeparators(consumeName(P, End), End, 2);
338*06c3fb27SDimitry Andric   }
339*06c3fb27SDimitry Andric 
340*06c3fb27SDimitry Andric   PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
341*06c3fb27SDimitry Andric #if defined(_LIBCPP_WIN32API)
342*06c3fb27SDimitry Andric     if (PosPtr Ret = consumeDriveLetter(P, End))
343*06c3fb27SDimitry Andric       return Ret;
344*06c3fb27SDimitry Andric     if (PosPtr Ret = consumeNetworkRoot(P, End))
345*06c3fb27SDimitry Andric       return Ret;
346*06c3fb27SDimitry Andric #endif
347*06c3fb27SDimitry Andric     return nullptr;
348*06c3fb27SDimitry Andric   }
349*06c3fb27SDimitry Andric };
350*06c3fb27SDimitry Andric 
351*06c3fb27SDimitry Andric inline string_view_pair separate_filename(string_view_t const& s) {
352*06c3fb27SDimitry Andric   if (s == PATHSTR(".") || s == PATHSTR("..") || s.empty())
353*06c3fb27SDimitry Andric     return string_view_pair{s, PATHSTR("")};
354*06c3fb27SDimitry Andric   auto pos = s.find_last_of('.');
355*06c3fb27SDimitry Andric   if (pos == string_view_t::npos || pos == 0)
356*06c3fb27SDimitry Andric     return string_view_pair{s, string_view_t{}};
357*06c3fb27SDimitry Andric   return string_view_pair{s.substr(0, pos), s.substr(pos)};
358*06c3fb27SDimitry Andric }
359*06c3fb27SDimitry Andric 
360*06c3fb27SDimitry Andric inline string_view_t createView(PosPtr S, PosPtr E) noexcept {
361*06c3fb27SDimitry Andric   return {S, static_cast<size_t>(E - S) + 1};
362*06c3fb27SDimitry Andric }
363*06c3fb27SDimitry Andric 
364*06c3fb27SDimitry Andric } // namespace parser
365*06c3fb27SDimitry Andric 
366*06c3fb27SDimitry Andric _LIBCPP_END_NAMESPACE_FILESYSTEM
367*06c3fb27SDimitry Andric 
368*06c3fb27SDimitry Andric #endif // PATH_PARSER_H
369