@@ -309,31 +309,86 @@ class ToolchainInstaller final : public Component
309309 // TODO: we do this a few times inline, move it into a helper function along with the xz decompression
310310 auto extractTar = [](const uint8_t * data, size_t size, const File& destRoot) -> bool {
311311 size_t offset = 0 ;
312+ std::string longLinkName; // For GNU tar @@LongLink entries
313+
312314 while (offset + 512 <= size) {
313315 const uint8_t * header = data + offset;
314316 if (header[0 ] == ' \0 ' ) break ; // End of archive
315-
316- // Get file name
317- std::string name (reinterpret_cast <const char *>(header), 100 );
317+
318+ // Get file name - handle both name and prefix fields for long paths
319+ std::string name;
320+
321+ if (!longLinkName.empty ()) {
322+ // Use the long name from previous @@LongLink entry
323+ name = longLinkName;
324+ longLinkName.clear ();
325+ } else {
326+ // Extract name from header (100 bytes at offset 0)
327+ char nameField[101 ] = {0 };
328+ std::memcpy (nameField, header, 100 );
329+ name = std::string (nameField);
330+
331+ // Check if there's a prefix field (155 bytes at offset 345)
332+ char prefixField[156 ] = {0 };
333+ std::memcpy (prefixField, header + 345 , 155 );
334+ std::string prefix (prefixField);
335+
336+ if (!prefix.empty ()) {
337+ name = prefix + " /" + name;
338+ }
339+ }
340+
318341 if (name.empty ()) break ;
319342
320- #if !JUCE_WINDOWS
343+ // Clean up the path
344+ name.erase (name.find_last_not_of (" \t\n\r\f\v\0 " ) + 1 );
345+
346+ #if !JUCE_WINDOWS
321347 mode_t mode = static_cast <mode_t >(
322348 std::strtoul (reinterpret_cast <const char *>(header + 100 ), nullptr , 8 )
323349 );
324350 bool executable = (mode & 0100 ) || (mode & 0010 ) || (mode & 0001 );
325- #endif
351+ #endif
352+
326353 // Get file size (octal)
327354 size_t fileSize = std::strtoull (reinterpret_cast <const char *>(header + 124 ), nullptr , 8 );
328- File outFile = destRoot.getChildFile (String (name));
329-
355+
330356 // Determine type
331357 char typeFlag = header[156 ];
358+
359+ // Handle GNU tar long link entries
360+ if (typeFlag == ' L' ) {
361+ // This is a @@LongLink entry - read the long filename
362+ size_t fileOffset = offset + 512 ;
363+ if (fileOffset + fileSize <= size) {
364+ longLinkName.assign (reinterpret_cast <const char *>(data + fileOffset), fileSize);
365+ // Remove null terminator if present
366+ if (!longLinkName.empty () && longLinkName.back () == ' \0 ' ) {
367+ longLinkName.pop_back ();
368+ }
369+ }
370+ // Skip this entry and continue
371+ size_t totalEntrySize = 512 + ((fileSize + 511 ) & ~511 );
372+ offset += totalEntrySize;
373+ continue ;
374+ }
375+
376+ // Handle PaxHeaders (POSIX extended headers)
377+ if (typeFlag == ' x' || name.find (" PaxHeaders." ) == 0 ) {
378+ // Skip extended header entries for now
379+ // (Full implementation would parse the extended attributes)
380+ size_t totalEntrySize = 512 + ((fileSize + 511 ) & ~511 );
381+ offset += totalEntrySize;
382+ continue ;
383+ }
384+
385+ File outFile = destRoot.getChildFile (String (name));
386+
332387 if (typeFlag == ' 5' ) {
333388 outFile.createDirectory ();
334- #if !JUCE_WINDOWS
389+ #if !JUCE_WINDOWS
335390 outFile.setExecutePermission (executable);
336- #endif
391+ #endif
337392 } else if (typeFlag == ' 0' || typeFlag == ' \0 ' ) {
338393 outFile.getParentDirectory ().createDirectory ();
339394 std::ofstream out (outFile.getFullPathName ().toRawUTF8 (), std::ios::binary);
@@ -346,18 +401,18 @@ class ToolchainInstaller final : public Component
346401 return false ;
347402 }
348403 out.close ();
349- #if !JUCE_WINDOWS
404+ #if !JUCE_WINDOWS
350405 outFile.setExecutePermission (executable);
351- #endif
406+ #endif
352407 }
353-
408+
354409 size_t totalEntrySize = 512 + ((fileSize + 511 ) & ~511 ); // pad to next 512
355410 offset += totalEntrySize;
356411 }
357412 return true ;
358413 };
359414
360- if (!extractTar (decompressedToolchain.data (), decompressedToolchain.size (), toolchainDir)) {
415+ if (!extractTar (decompressedToolchain.data (), decompressedToolchain.size (), toolchainDir. getParentDirectory () )) {
361416 failed = true ;
362417 }
363418
@@ -412,7 +467,7 @@ class ToolchainInstaller final : public Component
412467 float installProgress = 0 .0f ;
413468
414469 bool needsUpdate = false ;
415- int statusCode;
470+ int statusCode = 0 ;
416471
417472#if JUCE_WINDOWS
418473 String downloadSize = " 1.2 GB" ;
0 commit comments