Skip to content

Make cross-platform path/URI/charset handling in load/save more robust#3860

Merged
unp1 merged 5 commits into
mainfrom
bubel/xplatform-path-fixes
Jun 24, 2026
Merged

Make cross-platform path/URI/charset handling in load/save more robust#3860
unp1 merged 5 commits into
mainfrom
bubel/xplatform-path-fixes

Conversation

@unp1

@unp1 unp1 commented Jun 24, 2026

Copy link
Copy Markdown
Member

Intended Change

While auditing KeY for cross-platform (Linux/macOS/Windows) robustness of
loading and saving, several latent path/URI/charset issues surfaced. This PR
fixes them. Each issue is a separate commit.

  1. IOUtil.toURI corrupted + in file paths (all platforms).
    The central URL → File converter decoded the URL path with
    URLDecoder, whose application/x-www-form-urlencoded semantics turn a
    literal + into a space. URL paths never encode a space as + (they use
    %20), so any file or directory whose name contains + (e.g. C++Model/,
    a+b.key) was silently mangled. The + is now protected before decoding.

  2. Loader did not normalize backslashes — legacy Windows proofs failed on POSIX.
    KeYFile.readBootClassPath/readClassPath/readJavaPath parsed the stored
    path strings directly with Paths.get. Newer proofs are written with forward
    slashes, but proofs saved by older KeY versions on Windows contain
    backslashes; on POSIX a backslash is a valid file-name character, so the path
    was not split into segments and could not be resolved. The three readers now
    normalize separators, matching what IncludeFinder.addInclude already does
    for \include.

  3. UrlRuleSource reported the wrong file size on Windows / for spaced paths.
    The constructor used new File(url.getFile()).length(); url.getFile() is
    not percent-decoded and keeps the leading / of file:/C:/… on Windows, so
    for paths with spaces (%20) or on Windows it pointed at a non-existent file
    and returned 0. It now resolves via Paths.get(url.toURI()), matching the
    class's own file() method, with a stream-counting fallback.

  4. Filenames.makeFilenameRelative was not cross-platform safe.
    Path.relativize throws IllegalArgumentException when the two paths have
    different roots (e.g. different drive letters on Windows), and the result was
    returned with native separators. It now guards the differing-root case and
    normalizes output to forward slashes, mirroring IOUtil.safePathRelativeTo.

  5. Proof-independent settings JSON was written with the platform-default charset.
    The settings file is read as UTF-8 (CharStreams.fromPath) but was written
    with a plain FileWriter. On a JDK whose default charset is not UTF-8 (older
    JDKs on Windows), non-ASCII content (umlauts in bookmarked paths, user names)
    was written in one encoding and parsed back in another. The writer is now
    explicitly UTF-8.

Type of pull request

  • Bug fix (non-breaking change which fixes an issue)
  • Refactoring (behaviour should not change or only minimally change)
  • There are changes to the (Java) code

Ensuring quality

  • I made sure that introduced/changed code is well documented (javadoc and inline comments).
  • I have tested the feature as follows: The full test suites are left to CI as I do not have access to a local windows machine

Additional information and contact(s)

This PR has been done with AI tooling.

The contributions within this pull request are licensed under GPLv2 (only) for inclusion in KeY.

unp1 added 5 commits June 24, 2026 08:50
URLDecoder decodes the application/x-www-form-urlencoded form, in which a
literal '+' denotes a space. A URL path never encodes a space as '+'
(it uses %20), so a '+' in url.getPath() is part of the file name. The
previous decode turned it into a space, silently mangling any file or
directory whose name contains '+' (e.g. "C++Model/", "a+b.key"). Since
toURI is the central URL->File converter (toFile/toFileString route
through it), this affected all platforms.

Protect literal '+' as %2B before decoding the remaining percent-escapes.
readBootClassPath, readClassPath and readJavaPath parsed the stored
\bootclasspath / \classpath / \javaSource strings directly with
Paths.get. KeY now always writes forward slashes, but proofs saved by
older versions on Windows contain backslashes. On POSIX a backslash is a
valid file-name character, so such a path is not split into segments and
fails to resolve, making legacy Windows proofs unloadable on Linux/macOS.

IncludeFinder.addInclude already normalizes separators for \include;
this applies the same treatment to the three remaining path kinds via a
shared normalizeStoredPath helper. Converting '\' to '/' is safe on all
platforms (Windows accepts '/' as a separator).
The constructor counted bytes with new File(url.getFile()).length().
url.getFile() is not percent-decoded and retains the leading '/' of
"file:/C:/..." on Windows, so for file URLs containing spaces (%20) or on
Windows it pointed at a non-existent file and returned a length of 0.

Use Files.size(Paths.get(url.toURI())), matching the existing file()
method, and fall back to stream counting on failure. The byte count only
drives a progress indicator, but the previous behaviour was platform
dependent.
Two problems on Windows: Path.relativize throws IllegalArgumentException
when the two paths have different roots (e.g. proof and source on
different drives), and the result was returned with native separators
(backslashes), which are not portable when written into a proof file.

Guard the differing-root case by falling back to the absolute path and
normalize all output to forward slashes, mirroring the behaviour of
IOUtil.safePathRelativeTo.
The settings file is read as UTF-8 (ParsingFacade.readConfigurationFile
-> CharStreams.fromPath defaults to UTF-8) but was written with a plain
FileWriter, i.e. the platform-default charset. On a JDK whose default
charset is not UTF-8 (e.g. windows-1252 on Windows with older JDKs),
non-ASCII content such as umlauts in bookmarked paths or user names is
written in one encoding and parsed back in another, corrupting the
settings. Make the writer explicitly UTF-8 to match the reader.
@unp1 unp1 self-assigned this Jun 24, 2026
@unp1 unp1 added this to the v3.0.0 milestone Jun 24, 2026
@unp1
unp1 enabled auto-merge June 24, 2026 06:56
@unp1 unp1 changed the title Fix cross-platform path/URI/charset handling in load/save Make cross-platform path/URI/charset handling in load/save more robust Jun 24, 2026

@Drodt Drodt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

@unp1
unp1 added this pull request to the merge queue Jun 24, 2026
Merged via the queue into main with commit bcd629b Jun 24, 2026
62 of 70 checks passed
@unp1
unp1 deleted the bubel/xplatform-path-fixes branch June 24, 2026 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants