@@ -295,6 +295,30 @@ _startsWithArgument(const wchar_t *x, int xLen, const wchar_t *y, int yLen)
295295}
296296
297297
298+ // Unlike regular startsWith, this function requires that the following
299+ // character is either NULL (that is, the entire string matches) or is one of
300+ // the characters in 'separators'.
301+ bool
302+ _startsWithSeparated (const wchar_t * x , int xLen , const wchar_t * y , int yLen , const wchar_t * separators )
303+ {
304+ if (!x || !y ) {
305+ return false;
306+ }
307+ yLen = yLen < 0 ? (int )wcsnlen_s (y , MAXLEN ) : yLen ;
308+ xLen = xLen < 0 ? (int )wcsnlen_s (x , MAXLEN ) : xLen ;
309+ if (xLen < yLen ) {
310+ return false;
311+ }
312+ if (xLen == yLen ) {
313+ return 0 == _compare (x , xLen , y , yLen );
314+ }
315+ return separators &&
316+ 0 == _compare (x , yLen , y , yLen ) &&
317+ wcschr (separators , x [yLen ]) != NULL ;
318+ }
319+
320+
321+
298322/******************************************************************************\
299323 *** HELP TEXT ***
300324\******************************************************************************/
@@ -409,6 +433,9 @@ typedef struct {
409433 bool listPaths ;
410434 // if true, display help message before contiuning
411435 bool help ;
436+ // if set, limits search to registry keys with the specified Company
437+ // This is intended for debugging and testing only
438+ const wchar_t * limitToCompany ;
412439 // dynamically allocated buffers to free later
413440 struct _SearchInfoBuffer * _buffer ;
414441} SearchInfo ;
@@ -489,6 +516,7 @@ dumpSearchInfo(SearchInfo *search)
489516 DEBUG_BOOL (list );
490517 DEBUG_BOOL (listPaths );
491518 DEBUG_BOOL (help );
519+ DEBUG (limitToCompany );
492520#undef DEBUG_BOOL
493521#undef DEBUG_2
494522#undef DEBUG
@@ -1606,6 +1634,10 @@ registrySearch(const SearchInfo *search, EnvironmentInfo **result, HKEY root, in
16061634 }
16071635 break ;
16081636 }
1637+ if (search -> limitToCompany && 0 != _compare (search -> limitToCompany , -1 , buffer , cchBuffer )) {
1638+ debug (L"# Skipping %s due to PYLAUNCHER_LIMIT_TO_COMPANY\n" , buffer );
1639+ continue ;
1640+ }
16091641 HKEY subkey ;
16101642 if (ERROR_SUCCESS == RegOpenKeyExW (root , buffer , 0 , KEY_READ , & subkey )) {
16111643 exitCode = _registrySearchTags (search , result , subkey , sortKey , buffer , fallbackArch );
@@ -1884,6 +1916,11 @@ collectEnvironments(const SearchInfo *search, EnvironmentInfo **result)
18841916 }
18851917 }
18861918
1919+ if (search -> limitToCompany ) {
1920+ debug (L"# Skipping APPX search due to PYLAUNCHER_LIMIT_TO_COMPANY\n" );
1921+ return 0 ;
1922+ }
1923+
18871924 for (struct AppxSearchInfo * info = APPX_SEARCH ; info -> familyName ; ++ info ) {
18881925 exitCode = appxSearch (search , result , info -> familyName , info -> tag , info -> sortKey );
18891926 if (exitCode && exitCode != RC_NO_PYTHON ) {
@@ -2053,12 +2090,15 @@ _companyMatches(const SearchInfo *search, const EnvironmentInfo *env)
20532090
20542091
20552092bool
2056- _tagMatches (const SearchInfo * search , const EnvironmentInfo * env )
2093+ _tagMatches (const SearchInfo * search , const EnvironmentInfo * env , int searchTagLength )
20572094{
2058- if (!search -> tag || !search -> tagLength ) {
2095+ if (searchTagLength < 0 ) {
2096+ searchTagLength = search -> tagLength ;
2097+ }
2098+ if (!search -> tag || !searchTagLength ) {
20592099 return true;
20602100 }
2061- return _startsWith (env -> tag , -1 , search -> tag , search -> tagLength );
2101+ return _startsWithSeparated (env -> tag , -1 , search -> tag , searchTagLength , L".-" );
20622102}
20632103
20642104
@@ -2095,7 +2135,7 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn
20952135 }
20962136
20972137 if (!search -> oldStyleTag ) {
2098- if (_companyMatches (search , env ) && _tagMatches (search , env )) {
2138+ if (_companyMatches (search , env ) && _tagMatches (search , env , -1 )) {
20992139 // Because of how our sort tree is set up, we will walk up the
21002140 // "prev" side and implicitly select the "best" best. By
21012141 // returning straight after a match, we skip the entire "next"
@@ -2120,7 +2160,7 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn
21202160 }
21212161 }
21222162
2123- if (_startsWith ( env -> tag , -1 , search -> tag , tagLength )) {
2163+ if (_tagMatches ( search , env , tagLength )) {
21242164 if (exclude32Bit && _is32Bit (env )) {
21252165 debug (L"# Excluding %s/%s because it looks like 32bit\n" , env -> company , env -> tag );
21262166 } else if (only32Bit && !_is32Bit (env )) {
@@ -2147,10 +2187,6 @@ selectEnvironment(const SearchInfo *search, EnvironmentInfo *root, EnvironmentIn
21472187 * best = NULL ;
21482188 return RC_NO_PYTHON_AT_ALL ;
21492189 }
2150- if (!root -> next && !root -> prev ) {
2151- * best = root ;
2152- return 0 ;
2153- }
21542190
21552191 EnvironmentInfo * result = NULL ;
21562192 int exitCode = _selectEnvironment (search , root , & result );
@@ -2560,6 +2596,17 @@ process(int argc, wchar_t ** argv)
25602596 debug (L"argv0: %s\nversion: %S\n" , argv [0 ], PY_VERSION );
25612597 }
25622598
2599+ DWORD len = GetEnvironmentVariableW (L"PYLAUNCHER_LIMIT_TO_COMPANY" , NULL , 0 );
2600+ if (len > 1 ) {
2601+ wchar_t * limitToCompany = allocSearchInfoBuffer (& search , len );
2602+ search .limitToCompany = limitToCompany ;
2603+ if (0 == GetEnvironmentVariableW (L"PYLAUNCHER_LIMIT_TO_COMPANY" , limitToCompany , len )) {
2604+ exitCode = RC_INTERNAL_ERROR ;
2605+ winerror (0 , L"Failed to read PYLAUNCHER_LIMIT_TO_COMPANY variable" );
2606+ goto abort ;
2607+ }
2608+ }
2609+
25632610 search .originalCmdLine = GetCommandLineW ();
25642611
25652612 exitCode = performSearch (& search , & envs );
0 commit comments