@@ -295,6 +295,30 @@ _startsWithArgument(const wchar_t *x, int xLen, const wchar_t *y, int yLen)
295
295
}
296
296
297
297
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
+
298
322
/******************************************************************************\
299
323
*** HELP TEXT ***
300
324
\******************************************************************************/
@@ -409,6 +433,9 @@ typedef struct {
409
433
bool listPaths ;
410
434
// if true, display help message before contiuning
411
435
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 ;
412
439
// dynamically allocated buffers to free later
413
440
struct _SearchInfoBuffer * _buffer ;
414
441
} SearchInfo ;
@@ -489,6 +516,7 @@ dumpSearchInfo(SearchInfo *search)
489
516
DEBUG_BOOL (list );
490
517
DEBUG_BOOL (listPaths );
491
518
DEBUG_BOOL (help );
519
+ DEBUG (limitToCompany );
492
520
#undef DEBUG_BOOL
493
521
#undef DEBUG_2
494
522
#undef DEBUG
@@ -1606,6 +1634,10 @@ registrySearch(const SearchInfo *search, EnvironmentInfo **result, HKEY root, in
1606
1634
}
1607
1635
break ;
1608
1636
}
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
+ }
1609
1641
HKEY subkey ;
1610
1642
if (ERROR_SUCCESS == RegOpenKeyExW (root , buffer , 0 , KEY_READ , & subkey )) {
1611
1643
exitCode = _registrySearchTags (search , result , subkey , sortKey , buffer , fallbackArch );
@@ -1884,6 +1916,11 @@ collectEnvironments(const SearchInfo *search, EnvironmentInfo **result)
1884
1916
}
1885
1917
}
1886
1918
1919
+ if (search -> limitToCompany ) {
1920
+ debug (L"# Skipping APPX search due to PYLAUNCHER_LIMIT_TO_COMPANY\n" );
1921
+ return 0 ;
1922
+ }
1923
+
1887
1924
for (struct AppxSearchInfo * info = APPX_SEARCH ; info -> familyName ; ++ info ) {
1888
1925
exitCode = appxSearch (search , result , info -> familyName , info -> tag , info -> sortKey );
1889
1926
if (exitCode && exitCode != RC_NO_PYTHON ) {
@@ -2053,12 +2090,15 @@ _companyMatches(const SearchInfo *search, const EnvironmentInfo *env)
2053
2090
2054
2091
2055
2092
bool
2056
- _tagMatches (const SearchInfo * search , const EnvironmentInfo * env )
2093
+ _tagMatches (const SearchInfo * search , const EnvironmentInfo * env , int searchTagLength )
2057
2094
{
2058
- if (!search -> tag || !search -> tagLength ) {
2095
+ if (searchTagLength < 0 ) {
2096
+ searchTagLength = search -> tagLength ;
2097
+ }
2098
+ if (!search -> tag || !searchTagLength ) {
2059
2099
return true;
2060
2100
}
2061
- return _startsWith (env -> tag , -1 , search -> tag , search -> tagLength );
2101
+ return _startsWithSeparated (env -> tag , -1 , search -> tag , searchTagLength , L".-" );
2062
2102
}
2063
2103
2064
2104
@@ -2095,7 +2135,7 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn
2095
2135
}
2096
2136
2097
2137
if (!search -> oldStyleTag ) {
2098
- if (_companyMatches (search , env ) && _tagMatches (search , env )) {
2138
+ if (_companyMatches (search , env ) && _tagMatches (search , env , -1 )) {
2099
2139
// Because of how our sort tree is set up, we will walk up the
2100
2140
// "prev" side and implicitly select the "best" best. By
2101
2141
// returning straight after a match, we skip the entire "next"
@@ -2120,7 +2160,7 @@ _selectEnvironment(const SearchInfo *search, EnvironmentInfo *env, EnvironmentIn
2120
2160
}
2121
2161
}
2122
2162
2123
- if (_startsWith ( env -> tag , -1 , search -> tag , tagLength )) {
2163
+ if (_tagMatches ( search , env , tagLength )) {
2124
2164
if (exclude32Bit && _is32Bit (env )) {
2125
2165
debug (L"# Excluding %s/%s because it looks like 32bit\n" , env -> company , env -> tag );
2126
2166
} else if (only32Bit && !_is32Bit (env )) {
@@ -2147,10 +2187,6 @@ selectEnvironment(const SearchInfo *search, EnvironmentInfo *root, EnvironmentIn
2147
2187
* best = NULL ;
2148
2188
return RC_NO_PYTHON_AT_ALL ;
2149
2189
}
2150
- if (!root -> next && !root -> prev ) {
2151
- * best = root ;
2152
- return 0 ;
2153
- }
2154
2190
2155
2191
EnvironmentInfo * result = NULL ;
2156
2192
int exitCode = _selectEnvironment (search , root , & result );
@@ -2560,6 +2596,17 @@ process(int argc, wchar_t ** argv)
2560
2596
debug (L"argv0: %s\nversion: %S\n" , argv [0 ], PY_VERSION );
2561
2597
}
2562
2598
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
+
2563
2610
search .originalCmdLine = GetCommandLineW ();
2564
2611
2565
2612
exitCode = performSearch (& search , & envs );
0 commit comments