Skip to content

Decouple UI from CLI to reduce size of executable when only the CLI is needed#42

Open
philpem wants to merge 20 commits intogeraldholdsworth:mainfrom
philpem:feature/decouple-ui-from-cli
Open

Decouple UI from CLI to reduce size of executable when only the CLI is needed#42
philpem wants to merge 20 commits intogeraldholdsworth:mainfrom
philpem:feature/decouple-ui-from-cli

Conversation

@philpem
Copy link
Collaborator

@philpem philpem commented Feb 16, 2026

(My previous PR got deleted because I renamed the branch)

I put this together with Claude AI for a laugh and it turned out to work fairly well.
I've tested it locally and it can list and extract ADFS images from the CLI. The GUI seems to work OK too.

This fixes #39 by introducing a new "DiscImageManagerCLI" project which:

  • Moves the CLI parsing code out and removes the GUI dependencies.
  • Adds a new context unit (DiscImageContext) to break the dependency on the GUI components.
  • Adds {$IFNDEF NO_GUI} conditionals to Utils.pas to remove the GUI functions in non-GUI builds.
  • Adds a new Lazarus project to build it all.

It also removes the need for ICS by implementing HTTP URL encoding locally (which is the only thing ICS seems to be used for).

There are now "GUI" and "CLI" builds and never the twain shall meet, as they say.
The CLI code is still in the GUI build, so it's a bit icky. I have another PR to remove that -- we can decide whether or not we want to go that route. Having two executables and no console code in the GUI does mean there's two programs to maintain but it also makes a clear separation of responsibilities. At the moment the GUI mode detection code in the Linux build is broken and always tries to run in console mode (unless overridden with -g on the command line).

claude and others added 16 commits February 1, 2026 00:14
- Remove unused ExtCtrls and HTTPProtocol dependencies from DiscImage.pas
- Add conditional compilation to Utils.pas to exclude GUI code when NO_GUI defined
- Create DiscImageContext.pas: shared context for disc image operations
- Create CLICommands.pas: standalone CLI command processor without GUI dependencies
- Create DiscImageManagerCLI.lpr: CLI-only console application entry point
- Create DiscImageManagerCLI.lpi: Lazarus project file for CLI version
- Create UtilsCLI.pas: CLI-compatible subset of utility functions

This enables building a command-line version of the tool that does not
require an X server or GUI toolkits, suitable for headless server use.

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
Add -dNO_GUI custom compiler option to all Release build modes
(Linux 64-bit, Linux ARM, Windows 64-bit, macOS 64-bit, macOS ARM)
so that GUI-dependent code is excluded when building the CLI version.

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
TProgressProc is a private type inside TDiscImage class and not accessible
from other units. Removed the progress callback functionality from
TDiscImageContext as it's not needed for the CLI interface.

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
Implement URL percent-encoding/decoding functions locally to replace
the dependency on HTTPProtocol unit which was removed for CLI support.

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
Rename local variables Free/Used/Total to FreeBytes/UsedBytes/TotalBytes
to avoid conflict with the built-in TObject.Free method identifier.

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
Renamed local variable 'Params' to 'CmdParams' in DoRun method
to avoid shadowing the inherited Params property from TCustomApplication.

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
- Deleted UtilsCLI.pas as Utils.pas already has {$IFNDEF NO_GUI}
  conditionals that make it work for both GUI and CLI builds

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
Changed build mode names to match DiscImageManager.lpi naming:
- "Release Linux 64-bit" → "Release Linux 64 bit"
- "Release Linux ARM" → "Release Linux ARM 64 bit"
- "Release Windows 64-bit" → "Release Windows 64 bit"
- "Release macOS 64-bit" → "Release macOS 64 bit"

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
Changed UnitOutputDirectory from lib/ to lib-cli/ to prevent
compiled units from conflicting between GUI and CLI builds.

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
These are general-purpose string utilities that fit better alongside
other string functions in Utils.pas. DiscImage.pas now uses Utils.

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
Add the following commands that were present in the GUI's console mode:
- compact/defrag: Defragment disc image partitions
- dirtitle: Change current directory title
- exec/load/type: Change file addresses and filetypes
- find: Search host filesystem (distinct from image search)
- filetocsv: Batch CSV export of multiple images
- filetype: Translate between filetype names and numbers
- interleave: Change ADFS/AFS interleave method
- list: Display text/BASIC file contents
- report: Show detailed image report
- runscript: Execute commands from a script file
- savecsv: Save image catalogue as CSV
- stamp: Set current timestamp on files
- join/split: Placeholders (not implemented in GUI either)

Also updated:
- cat command output format to match GUI (shows boot option, timestamps,
  load/exec addresses, filetypes as appropriate for format)
- Help text expanded with all commands and format options

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
- Remove 'info' command (not in GUI console)
- Change 'ls' to behave like GUI: redirects to 'find *' (list host files)
  instead of being an alias for 'cat'
- Remove unused ShowImageInfo procedure
- Update help text accordingly

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
- CmdDefrag: Mark as not available in CLI mode (requires GUI infrastructure)
- CmdList: Convert for-loop to while-loop to fix illegal assignment to
  loop variable when skipping CR+LF pairs

https://claude.ai/code/session_01H1suvkNPi2MVsX1y9Qy86P
The "initial image file" detection used ParamStr(ParamCount) to grab the
last command-line argument and try loading it as a disc image. When using
`-s FOO.dim`, the script filename FOO.dim is the last argument, so it
was incorrectly loaded as a disc image — causing EReadError since a text
script file is not a valid disc image.

Replace the naive ParamStr(ParamCount) approach with a FindPositionalArg
helper that properly walks the argument list, skipping options and their
values (-s, -e and their arguments, -n, -h flags), and returns only a
true standalone positional argument intended as an image file path.

https://claude.ai/code/session_01CctTMMjYE63p4GMLjhrh61
@philpem
Copy link
Collaborator Author

philpem commented Feb 16, 2026

Hold off on this one for the moment. The extract command doesn't behave the same as the GUI version: it seems like the GUI recurses while this doesn't. The helpscreen is also wildly different to the original...

I'm going to go through and figure out how things differ and bring it back into alignment. We might have to consider this a proof of concept, and I'll go back and do it by hand!

- Replace hardcoded CLI help text with the same content from GUI's
  Help.Lines TMemo, using identical formatting (command names in
  red+bold, descriptions with leading space stripped, word wrapping)
- Add recursive directory extraction to CLI extract command via new
  DownloadFile/DownloadDirectory methods that mirror the GUI's
  TMainForm.DownLoadFile/DownLoadDirectory behavior
- Fix search and extract file accumulation: pass previous results
  to GetListOfFiles (via Image.FileSearch AddTo parameter) so
  multiple patterns accumulate rather than overwriting
- Add INF file creation support during extraction when CreateINF
  setting is enabled
- Add Global unit import for WrapText function

https://claude.ai/code/session_017Xs8rKBbGLsNaa8L8eNMLT
Dir command:
- Use ValidFile() instead of FileExists() directly so that
  relative directory names (e.g. 'Applicatio' while in '$')
  are resolved by also trying the current-directory-prefixed
  path (e.g. '$.Applicatio'), matching GUI behavior
- Add in-path parent specifier handling (e.g. '$.dir1.^.dir2')
  matching GUI's ParseCommand dir handler
- Properly track Ok state for success/failure reporting

Report command:
- Use Image.ImageReport(False) to generate the same detailed
  report as the GUI (disc record, free space map, etc.)
- Add file report section with broken directory and CRC error
  checking, matching GUI's btn_ShowReportClick
- Add footer with app title, version, and author info

https://claude.ai/code/session_017Xs8rKBbGLsNaa8L8eNMLT
@philpem
Copy link
Collaborator Author

philpem commented Feb 16, 2026

I spotted a bug in the original -- if you use the dir command with a relative path, it says the path is invalid but changes directory anyway. There's a fix on this branch, bb6836a, something similar should work on the GUI version's command-line interface.

c9a9992 fixes the bug I mentioned above where extract didn't recurse, which didn't match the GUI version.

I've not tested all the commands yet - I only use report, dir and extract for my imaging pipeline.

@philpem philpem marked this pull request as ready for review February 16, 2026 22:44
- delete: Add UEF/RFS special handling (entry-based deletion) and
  match GUI message format ("X deleted." / "Could not delete X.")
- new: Add AFS, DOS HDD, and Amiga HDD format support
- config: Full 43-option config array with actual get/set via registry,
  matching GUI's Configs array exactly
- status: Display all 43 config settings with current values from registry
- add: Support adding directories recursively (matching GUI's combined
  add/find handler with exclusion prefix support)
- filetocsv: Implement actual CSV batch output by loading each image
  and generating CSV using shared WriteCSVForImage helper
- savecsv: Rewrite to use registry-based CSV preferences (CSVParent,
  CSVFilename, CSVLoadAddr, etc.) matching GUI's SaveAsCSV output format
- create: Fix message wording to match GUI ("Create new directory")
- exec/load/type: Add hex validation for type command (matching GUI)
- defrag: Add TODO comment documenting GUI dependency for future work

https://claude.ai/code/session_017Xs8rKBbGLsNaa8L8eNMLT
@geraldholdsworth
Copy link
Owner

I think the best way to go, particularly for version 2, is to have two separate binaries - one for GUI and the other for console. Both can use the same TDiscImage base class, so therefore code sharing.

CreateDirectory takes var parameters, so function results can't be
passed directly. Assign GetParent() to a local variable first.

https://claude.ai/code/session_017Xs8rKBbGLsNaa8L8eNMLT
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.

Feature request - console-only Linux build (built executable currently requires GTK libraries)

3 participants