xref: /reactos/sdk/lib/ucrt/env/getpath.cpp (revision fe11f7a2)
1 /***
2 *getpath.c - extract a pathname from an environment variable
3 *
4 *       Copyright (c) Microsoft Corporation. All rights reserved.
5 *
6 *Purpose:
7 *       Extract pathnames from a string of semicolon delimited pathnames
8 *       (generally the value of an environment variable such as PATH).
9 *
10 *******************************************************************************/
11 
12 #include <corecrt_internal.h>
13 #include <stddef.h>
14 
15 /***
16 *_getpath() - extract a pathname from a semicolon-delimited list of pathnames
17 *
18 *Purpose:
19 *       To extract the next pathname from a semicolon-delimited list of
20 *       pathnames (usually the value on an environment variable) and copy
21 *       it to a caller-specified buffer. No check is done to see if the path
22 *       is valid. The maximum number of characters copied to the buffer is
23 *       maxlen - 1 (and then a '\0' is appended).
24 *
25 *ifdef _HPFS_
26 *       If we hit a quoted string, then allow any characters inside.
27 *       For example, to put a semi-colon in a path, the user could have
28 *       an environment variable that looks like:
29 *
30 *               PATH=C:\BIN;"D:\CRT\TOOLS;B1";C:\BINP
31 *endif
32 *
33 *       NOTE: Semi-colons in sequence are skipped over; pointers to 0-length
34 *       pathnames are NOT returned (this includes leading semi-colons).
35 *
36 *       NOTE: If this routine is made user-callable, the near attribute
37 *       must be replaced by _LOAD_DS and the prototype moved from INTERNAL.H
38 *       to STDLIB.H. The source files MISC\SEARCHEN.C and EXEC\SPAWNVPE.C
39 *       will need to be recompiled, but should not require any changes.
40 *
41 *Entry:
42 *       src    - Pointer to a string of 0 or more path specificiations,
43 *                delimited by semicolons (';'), and terminated by a null
44 *                character
45 *       dst    - Pointer to the buffer where the next path specification is to
46 *                be copied
47 *       maxlen - Maximum number of characters to be copied, counting the
48 *                terminating null character. Note that a value of 0 is treated
49 *                as UINT_MAX + 1.
50 *
51 *Exit:
52 *       If a pathname is successfully extracted and copied, a pointer to the
53 *       first character of next pathname is returned (intervening semicolons
54 *       are skipped over). If the pathname is too long, as much as possible
55 *       is copied to the user-specified buffer and nullptr is returned.
56 *
57 *       Note that the no check is made of the validity of the copied pathname.
58 *
59 *Exceptions:
60 *
61 *******************************************************************************/
62 template <typename Character>
63 _Success_(return != 0)
64 static Character* __cdecl common_getpath(
65     _In_z_                          Character const*    const   delimited_paths,
66     _Out_writes_z_(result_count)    Character*          const   result,
67                                     size_t              const   result_count
68     ) throw()
69 {
70     _VALIDATE_RETURN_NOEXC(result != nullptr, EINVAL, nullptr);
71 
72     if (result_count > 0)
73     {
74         result[0] = '\0';
75     }
76 
77     _VALIDATE_RETURN_NOEXC(result_count > 1,  EINVAL, nullptr);
78 
79     Character const* source_it = delimited_paths;
80 
81     // Skip past any leading semicolons:
82     while (*source_it == ';')
83     {
84         ++source_it;
85     }
86 
87     Character const* const source_first = source_it;
88 
89     Character*       result_it   = result;
90     Character* const result_last = result + result_count - 1; // Leave room for \0
91 
92 #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_NULLTERMINATED) // 26018 Prefast is confused.
93     while (*source_it != '\0' && *source_it != ';')
94     {
95         if (*source_it == '"')
96         {
97             // We found a quote; copy all characters until we reach the matching
98             // end quote or the end of the string:
99             ++source_it; // Advance past opening quote
100 
101             while (*source_it != '\0' && *source_it != '"')
102             {
103 #pragma warning(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY) // 26015 Prefast is confused.
104                 *result_it++ = *source_it++;
105                 if (result_it == result_last)
106                 {
107                     *result_it = '\0';
108                     errno = ERANGE;
109                     return nullptr;
110                 }
111             }
112 
113             if (*source_it != '\0')
114             {
115                 ++source_it; // Advance past closing quote
116             }
117         }
118         else
119         {
120             *result_it++ = *source_it++;
121             if (result_it == result_last)
122             {
123                 *result_it = '\0';
124                 errno = ERANGE;
125                 return nullptr;
126             }
127         }
128     }
129 
130     // If we copied something and stopped because we reached a semicolon, skip
131     // any semicolons before returning:
132     while (*source_it == ';')
133     {
134         ++source_it;
135     }
136 
137     *result_it = '\0';
138     return source_it == source_first
139         ? nullptr
140         : const_cast<Character*>(source_it);
141 }
142 
143 extern "C" char* __cdecl __acrt_getpath(
144     char const* const delimited_paths,
145     char*       const result,
146     size_t      const result_count
147     )
148 {
149     return common_getpath(delimited_paths, result, result_count);
150 }
151 
152 extern "C" wchar_t* __cdecl __acrt_wgetpath(
153     wchar_t const* const delimited_paths,
154     wchar_t*       const result,
155     size_t         const result_count
156     )
157 {
158     return common_getpath(delimited_paths, result, result_count);
159 }
160