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