Skip to content

fix: read Molfile input from non-seekable files like /dev/stdin#238

Merged
fbaensch-beilstein merged 5 commits into
devfrom
fix/dev-stdin-named-pipe
Jul 8, 2026
Merged

fix: read Molfile input from non-seekable files like /dev/stdin#238
fbaensch-beilstein merged 5 commits into
devfrom
fix/dev-stdin-named-pipe

Conversation

@fbaensch-beilstein

Copy link
Copy Markdown
Collaborator

Summary

A follow-up to #236 / #237. While fixing the -STDIO pipe regression I found a separate, pre-existing bug: passing a non-seekable named input/dev/stdin, a FIFO, or a <(process substitution) — fails with:

Fatal Error 3 (aborted; Cannot interpret atom block line:   1 13  1  0  0  0  0) inp structure #1.

This is exactly the error @JervenBolleman hit when trying the /dev/stdin workaround in #236.

Root cause

DetectInputINChIFileType() (in ichiparm.c) auto-detects plain-InChI vs Molfile input by peeking the first 4 lines, then "rewinds" by closing and reopening the file by path:

fclose(*inp_file);
*inp_file = fopen(ip->path[0], fmode);

Reopening resets a regular file to the start, but a non-seekable stream cannot be rewound: the consumed Molfile header (name, comment, counts line) is gone, and reopening /dev/stdin just yields a fresh fd on the same, already-advanced pipe. The parser then begins mid-record and aborts. (This path only runs for a named input file, so real stdin — which skips detection — was unaffected.)

The old code also never checked the reopen for NULL, so a failed fopen would leave a dangling *inp_file.

Fix

  • Skip auto-detection when the stream is not seekable (fseek(*inp_file, 0, SEEK_SET) fails) — it can't be peeked non-destructively — and keep the declared MOLFILE/SDFILE type.
  • Rewind seekable input with fseek(0) instead of fclose()+fopen(), removing the latent NULL-dereference.

Behavior:

  • inchi-1 /dev/stdin …, <(…), FIFOs → now produce the correct InChI/InChIKey ✓
  • Regular files → unchanged; plain-InChI auto-detection still works ✓
  • Plain InChI from a pipe is no longer auto-detected (can't peek), but -InpAux forces it explicitly — verified working.

Tests

  • New POSIX-only test_named_pipe_input.py feeds the reported molecule via /dev/stdin. It fails against the pre-fix binary (reproduces the exact error) and passes against the fix.
  • Full test_executable suite green; the changed file is clean under -Wall -Wextra.

Related: #236, #237.

DetectInputINChIFileType() peeked the first lines of a named input file
to recognise a plain-InChI file, then "rewound" by closing and reopening
the file by path. Reopening only restores the read position for a regular
file; for a non-seekable input (pipe, FIFO, /dev/stdin, process
substitution) the consumed Molfile header was lost and the parser started
mid-record, aborting with "Cannot interpret atom block line".

Skip auto-detection when the stream is not seekable (it cannot be peeked
non-destructively) and keep the declared MOLFILE/SDFILE type; a plain
InChI can still be forced from a pipe with -InpAux. Rewind seekable input
with fseek() instead of fclose()+fopen(), which also removes a latent
NULL-dereference if the reopen ever failed.

Add a POSIX regression test feeding a Molfile via /dev/stdin.
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown

Unit Test Coverage Report

Coverage Regression Summary

Metric PR (%) Base (%) Difference (pp)
Lines 18.6 18.6 +0.00
Branches 12.3 12.3 +0.00

Find details on the base coverage at https://iupac-inchi.github.io/InChI/coverage/index.html

Find details on this PR's coverage by downloading coverage-reports-28949615220 and opening html/index.html

Copilot AI 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.

Pull request overview

This PR fixes a long-standing input-type auto-detection bug where DetectInputINChIFileType() destructively “peeked” non-seekable named inputs (e.g., /dev/stdin, FIFOs, process substitution) and then attempted to “rewind” by reopening via path, causing Molfile parsing to start mid-record.

Changes:

  • Skip plain-InChI auto-detection when the input stream is not seekable; keep the user-declared MOLFILE/SDFILE type.
  • Rewind seekable inputs via fseek(…, 0, SEEK_SET) instead of fclose() + fopen().
  • Add a POSIX-only regression test that exercises /dev/stdin as a named, non-seekable input.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
INCHI-1-SRC/INCHI_BASE/src/ichiparm.c Avoids destructive peeking on non-seekable named inputs and rewinds seekable streams safely with fseek.
INCHI-1-TEST/tests/test_executable/test_named_pipe_input.py Adds a regression test to ensure /dev/stdin works as a named input for Molfile.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread INCHI-1-TEST/tests/test_executable/test_named_pipe_input.py
Comment thread INCHI-1-TEST/tests/test_executable/test_named_pipe_input.py Outdated

@JanCBrammer JanCBrammer left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not much to add to the CoPilot review. Maybe this would be a good opportunity to add a Doxygen comment to DetectInputINChIFileType?

… test

Address review feedback on the regression test: also assert a zero exit
code (so it fails if the executable prints the key but still errors), and
store the molfile as a triple-quoted multi-line string to match the
existing test-suite convention instead of one long escaped line. Also add
a Doxygen comment to DetectInputINChIFileType describing the plain-InChI
detection and why it is skipped for non-seekable input.
@fbaensch-beilstein fbaensch-beilstein merged commit 4cffb2c into dev Jul 8, 2026
3 checks passed
@JanCBrammer JanCBrammer deleted the fix/dev-stdin-named-pipe branch July 9, 2026 11:31
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.

3 participants