Skip to content

fix: route archive I/O through C++ streams for embedded mode compatib… - #14

Merged
pedroac merged 1 commit into
mainfrom
release
Jun 25, 2026
Merged

fix: route archive I/O through C++ streams for embedded mode compatib…#14
pedroac merged 1 commit into
mainfrom
release

Conversation

@pedroac

@pedroac pedroac commented Jun 25, 2026

Copy link
Copy Markdown
Owner

…ility

EmbeddedCli redirects std::cout/std::cin to stringstreams, but
libarchive's archive_write_open_FILE(stdout) and
archive_read_open_fd(STDIN_FILENO) operate on C-level handles that
bypass that redirection. On Windows GUI apps these handles are
disconnected, so spratpack's multi-atlas TAR output was silently lost
and _setmode calls on the invalid fd caused a fatal exit.

Replace direct FILE*/fd usage with archive_write_open/archive_read_open
callbacks that go through std::cout/std::cin, matching the approach
already used in spratunpack. Make _setmode failures non-fatal since
the C-level handles are irrelevant in embedded mode.

…ility

  EmbeddedCli redirects std::cout/std::cin to stringstreams, but
  libarchive's archive_write_open_FILE(stdout) and
  archive_read_open_fd(STDIN_FILENO) operate on C-level handles that
  bypass that redirection. On Windows GUI apps these handles are
  disconnected, so spratpack's multi-atlas TAR output was silently lost
  and _setmode calls on the invalid fd caused a fatal exit.

  Replace direct FILE*/fd usage with archive_write_open/archive_read_open
  callbacks that go through std::cout/std::cin, matching the approach
  already used in spratunpack. Make _setmode failures non-fatal since
  the C-level handles are irrelevant in embedded mode.
@what-the-diff

what-the-diff Bot commented Jun 25, 2026

Copy link
Copy Markdown

PR Summary

  • Version Update
    The application version has been updated from v0.11.7 to v0.11.8, assuring that users are accessing the latest, improved version of the software.

  • Code Refactoring in spratlayout_command.cpp
    We've made changes to the way extract_tar_from_stdin in spratlayout_command.cpp operates. More specifically, we've replaced how files are read, from direct file descriptor usage to a read callback implementation. This new approach improves compatibility when running the software in an embedded mode.

  • Enhancements to run_spratpack in spratpack_command.cpp
    The run_spratpack code block in spratpack_command.cpp has also been enhanced. We replaced the archive_write_open_FILE command with a write callback functionality that helps in writing output through std::cout. This also improves compatibility with embedded mode. Furthermore, we've added informational comments in the code, specifying that certain settings are non-fatal in an embedded mode of operation.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the spratlayout and spratpack commands to route archive I/O through std::cin and std::cout instead of OS-level file descriptors, ensuring compatibility with embedded environments where standard streams are redirected. Feedback on these changes suggests improving error handling in the custom read and write callbacks by explicitly calling archive_set_error and returning -1 when stream operations fail, rather than silently treating read errors as EOF or omitting error details on write failures.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +1088 to +1095
auto tar_read_cb = [](struct archive* /*unused*/, void* /*client_data*/,
const void** buffer) -> la_ssize_t {
static thread_local std::vector<char> buf(k_tar_read_buffer_size);
std::cin.read(buf.data(), static_cast<std::streamsize>(buf.size()));
auto n = std::cin.gcount();
*buffer = buf.data();
return static_cast<la_ssize_t>(n);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When a read error occurs on std::cin, the callback currently returns 0 (via std::cin.gcount()), which libarchive interprets as a clean End-Of-File (EOF). To prevent silent failures or truncated extractions on actual I/O errors, we should check std::cin.bad() or std::cin.fail() and report the error to libarchive using archive_set_error before returning -1.

    auto tar_read_cb = [](struct archive* a, void* /*client_data*/,
                          const void** buffer) -> la_ssize_t {
        static thread_local std::vector<char> buf(k_tar_read_buffer_size);
        std::cin.read(buf.data(), static_cast<std::streamsize>(buf.size()));
        if (std::cin.bad() || (std::cin.fail() && !std::cin.eof())) {
            archive_set_error(a, EIO, "std::cin read error");
            return -1;
        }
        auto n = std::cin.gcount();
        *buffer = buf.data();
        return static_cast<la_ssize_t>(n);
    };

Comment on lines +650 to +656
auto tar_write_cb = [](struct archive* /*unused*/, void* /*client_data*/,
const void* buffer, size_t length) -> la_ssize_t {
std::cout.write(static_cast<const char*>(buffer),
static_cast<std::streamsize>(length));
if (std::cout.fail()) return -1;
return static_cast<la_ssize_t>(length);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When a write error occurs on std::cout, the callback returns -1 but does not set an error on the archive object. To ensure that libarchive can report a meaningful error message (e.g., via archive_error_string), we should call archive_set_error with a proper error code (like EIO) before returning -1.

        auto tar_write_cb = [](struct archive* a, void* /*client_data*/,
                               const void* buffer, size_t length) -> la_ssize_t {
            std::cout.write(static_cast<const char*>(buffer),
                            static_cast<std::streamsize>(length));
            if (std::cout.fail()) {
                archive_set_error(a, EIO, "std::cout write error");
                return -1;
            }
            return static_cast<la_ssize_t>(length);
        };

@pedroac
pedroac merged commit 1a93c49 into main Jun 25, 2026
10 checks passed
@pedroac
pedroac deleted the release branch June 25, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants