2222using Azure . Identity ;
2323using System . Text . RegularExpressions ;
2424using System . Threading ;
25+ using System . Text . Json ;
2526using System . Threading . Tasks ;
2627using System . Xml ;
2728
@@ -1157,6 +1158,42 @@ private static string GetHomeOrCreateTempHome()
11571158
11581159 private readonly static Version PSVersion6 = new Version ( 6 , 0 ) ;
11591160
1161+ /// <summary>
1162+ /// Gets the user content directory path based on PSContentPath experimental feature settings.
1163+ /// Checks if PSContentPath is enabled and returns the appropriate path (custom, default, or legacy).
1164+ /// </summary>
1165+ private static string GetUserContentPath ( PSCmdlet psCmdlet , string defaultPSContentPath , string legacyPath )
1166+ {
1167+ bool usePSContentPath = IsExperimentalFeatureEnabled ( psCmdlet , "PSContentPath" ) ;
1168+
1169+ if ( usePSContentPath )
1170+ {
1171+ psCmdlet . WriteVerbose ( "PSContentPath experimental feature is enabled" ) ;
1172+
1173+ // Check environment variable and config file for custom PSUserContentPath
1174+ string customPSUserContentPath = GetPSUserContentPath ( psCmdlet ) ;
1175+
1176+ if ( ! string . IsNullOrEmpty ( customPSUserContentPath ) && Directory . Exists ( customPSUserContentPath ) )
1177+ {
1178+ // Use custom configured path
1179+ psCmdlet . WriteVerbose ( $ "Using custom PSUserContentPath: { customPSUserContentPath } ") ;
1180+ return customPSUserContentPath ;
1181+ }
1182+ else
1183+ {
1184+ // Use default PSContentPath location when feature is enabled
1185+ psCmdlet . WriteVerbose ( $ "Using default PSContentPath location: { defaultPSContentPath } ") ;
1186+ return defaultPSContentPath ;
1187+ }
1188+ }
1189+ else
1190+ {
1191+ // PSContentPath not enabled, use legacy location
1192+ psCmdlet . WriteVerbose ( $ "Using legacy location: { legacyPath } ") ;
1193+ return legacyPath ;
1194+ }
1195+ }
1196+
11601197 /// <summary>
11611198 /// Checks if a PowerShell experimental feature is enabled by reading the PowerShell configuration file.
11621199 /// Returns false if the configuration file doesn't exist or if the feature is not enabled.
@@ -1179,18 +1216,19 @@ private static bool IsExperimentalFeatureEnabled(PSCmdlet psCmdlet, string featu
11791216 }
11801217
11811218 string jsonContent = File . ReadAllText ( configPath ) ;
1182- var config = Newtonsoft . Json . Linq . JObject . Parse ( jsonContent ) ;
1183-
1184- // Look for "ExperimentalFeatures": ["FeatureName"] in the config
1185- var experimentalFeatures = config [ "ExperimentalFeatures" ] as Newtonsoft . Json . Linq . JArray ;
1186- if ( experimentalFeatures != null )
1219+ using ( var jsonDoc = JsonDocument . Parse ( jsonContent ) )
11871220 {
1188- foreach ( var feature in experimentalFeatures )
1221+ // Look for "ExperimentalFeatures": ["FeatureName"] in the config
1222+ if ( jsonDoc . RootElement . TryGetProperty ( "ExperimentalFeatures" , out var experimentalFeatures ) &&
1223+ experimentalFeatures . ValueKind == JsonValueKind . Array )
11891224 {
1190- if ( string . Equals ( feature . ToString ( ) , featureName , StringComparison . OrdinalIgnoreCase ) )
1225+ foreach ( var feature in experimentalFeatures . EnumerateArray ( ) )
11911226 {
1192- psCmdlet . WriteVerbose ( string . Format ( "Experimental feature '{0}' found in configuration file" , featureName ) ) ;
1193- return true ;
1227+ if ( string . Equals ( feature . GetString ( ) , featureName , StringComparison . OrdinalIgnoreCase ) )
1228+ {
1229+ psCmdlet . WriteVerbose ( string . Format ( "Experimental feature '{0}' found in configuration file" , featureName ) ) ;
1230+ return true ;
1231+ }
11941232 }
11951233 }
11961234 }
@@ -1236,14 +1274,18 @@ private static string GetPSUserContentPath(PSCmdlet psCmdlet)
12361274 }
12371275
12381276 string jsonContent = File . ReadAllText ( configPath ) ;
1239- var config = Newtonsoft . Json . Linq . JObject . Parse ( jsonContent ) ;
1240-
1241- // Look for PSUserContentPath in the config
1242- var psUserContentPath = config [ "PSUserContentPath" ] ? . ToString ( ) ;
1243- if ( ! string . IsNullOrEmpty ( psUserContentPath ) )
1277+ using ( var jsonDoc = JsonDocument . Parse ( jsonContent ) )
12441278 {
1245- psCmdlet . WriteVerbose ( string . Format ( "Found PSUserContentPath in config file: {0}" , psUserContentPath ) ) ;
1246- return psUserContentPath ;
1279+ // Look for PSUserContentPath in the config
1280+ if ( jsonDoc . RootElement . TryGetProperty ( "PSUserContentPath" , out var pathElement ) )
1281+ {
1282+ string psUserContentPath = pathElement . GetString ( ) ;
1283+ if ( ! string . IsNullOrEmpty ( psUserContentPath ) )
1284+ {
1285+ psCmdlet . WriteVerbose ( string . Format ( "Found PSUserContentPath in config file: {0}" , psUserContentPath ) ) ;
1286+ return psUserContentPath ;
1287+ }
1288+ }
12471289 }
12481290
12491291 psCmdlet . WriteVerbose ( "PSUserContentPath not configured in PowerShell configuration file or environment variable" ) ;
@@ -1265,52 +1307,50 @@ private static void GetStandardPlatformPaths(
12651307 {
12661308 string powerShellType = ( psCmdlet . Host . Version >= PSVersion6 ) ? "PowerShell" : "WindowsPowerShell" ;
12671309
1268- // Check if PSContentPath experimental feature is enabled
1269- bool usePSContentPath = IsExperimentalFeatureEnabled ( psCmdlet , "PSContentPath" ) ;
1270-
1271- if ( usePSContentPath )
1310+ // Windows PowerShell doesn't support experimental features or PSContentPath
1311+ if ( powerShellType == "WindowsPowerShell" )
12721312 {
1273- psCmdlet . WriteVerbose ( "PSContentPath experimental feature is enabled" ) ;
1274-
1275- // Check environment variable and config file for custom PSUserContentPath
1276- string customPSUserContentPath = GetPSUserContentPath ( psCmdlet ) ;
1277-
1278- if ( ! string . IsNullOrEmpty ( customPSUserContentPath ) && Directory . Exists ( customPSUserContentPath ) )
1279- {
1280- // Use custom configured path
1281- localUserDir = customPSUserContentPath ;
1282- psCmdlet . WriteVerbose ( $ "Using custom PSUserContentPath: { localUserDir } ") ;
1283- }
1284- else
1285- {
1286- // Use default LocalApplicationData location when PSContentPath is enabled
1287- localUserDir = Path . Combine (
1288- Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) ,
1289- powerShellType
1290- ) ;
1291- psCmdlet . WriteVerbose ( $ "Using default PSContentPath location: { localUserDir } ") ;
1292- }
1313+ // Use legacy Documents folder for Windows PowerShell
1314+ localUserDir = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . MyDocuments ) , powerShellType ) ;
1315+ psCmdlet . WriteVerbose ( $ "Using Windows PowerShell Documents folder: { localUserDir } ") ;
12931316 }
12941317 else
12951318 {
1296- // PSContentPath not enabled, use legacy Documents folder
1297- localUserDir = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . MyDocuments ) , powerShellType ) ;
1298- psCmdlet . WriteVerbose ( $ "Using legacy Documents folder: { localUserDir } ") ;
1319+ string defaultPSContentPath = Path . Combine (
1320+ Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) ,
1321+ powerShellType
1322+ ) ;
1323+ string legacyPath = Path . Combine (
1324+ Environment . GetFolderPath ( Environment . SpecialFolder . MyDocuments ) ,
1325+ powerShellType
1326+ ) ;
1327+
1328+ localUserDir = GetUserContentPath ( psCmdlet , defaultPSContentPath , legacyPath ) ;
12991329 }
13001330
13011331 allUsersDir = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , powerShellType ) ;
13021332 }
13031333 else
13041334 {
13051335 // paths are the same for both Linux and macOS
1306- localUserDir = Path . Combine ( GetHomeOrCreateTempHome ( ) , ".local" , "share" , "powershell" ) ;
1307- // Create the default data directory if it doesn't exist.
1336+ string xdgDataHome = Environment . GetEnvironmentVariable ( "XDG_DATA_HOME" ) ;
1337+ if ( string . IsNullOrEmpty ( xdgDataHome ) )
1338+ {
1339+ xdgDataHome = Path . Combine ( GetHomeOrCreateTempHome ( ) , ".local" , "share" ) ;
1340+ }
1341+
1342+ string defaultPSContentPath = Path . Combine ( xdgDataHome , "powershell" ) ;
1343+ string legacyPath = Path . Combine ( GetHomeOrCreateTempHome ( ) , ".local" , "share" , "powershell" ) ;
1344+
1345+ localUserDir = GetUserContentPath ( psCmdlet , defaultPSContentPath , legacyPath ) ;
1346+
1347+ // Create the default data directory if it doesn't exist
13081348 if ( ! Directory . Exists ( localUserDir ) )
13091349 {
13101350 Directory . CreateDirectory ( localUserDir ) ;
13111351 }
13121352
1313- allUsersDir = System . IO . Path . Combine ( "/usr" , "local" , "share" , "powershell" ) ;
1353+ allUsersDir = Path . Combine ( "/usr" , "local" , "share" , "powershell" ) ;
13141354 }
13151355 }
13161356
0 commit comments