Fix non-compliant -i parsing to prevent argument-shifting bugs in sh/dash
### Title: **Fix non-compliant `-i` parsing to prevent argument-shifting bugs in `sh`/`dash`** --- ### Description: **Summary** The current implementation of the `-i` option uses a "hack" to simulate optional arguments within `getopts`. By manually manipulating `OPTIND` and "peeking" at the next positional parameter, the script violates POSIX utility guidelines and causes desynchronization in minimalist shells like `dash`. This behavior is a known point of failure for automation tools and wrappers, notably documented in **nixos-anywhere [issue #615](https://github.com/nix-community/nixos-anywhere/issues/615)**. **The Problem** `getopts` is not designed to handle optional arguments. In `dash` (the default `/bin/sh` on many systems), manual modification of `OPTIND` during the loop often leads to: 1. The target `USER_HOST` being incorrectly shifted away. 2. Heuristics misidentifying a hostname as an identity file if a local file with a similar name exists. 3. Errors like `Too many arguments` or `Missing hostname` when the script is called programmatically. **Proposed Solution** To make the script fully POSIX-compliant and robust across all shells, I suggest removing the `-i` parsing from the `getopts` state machine and replacing the argument processing with a manual `while`/`case` loop. A manual loop allows for safe "lookahead" logic without interfering with the shell's internal `getopts` pointers. This ensures that: - `USER_HOST` is always correctly identified as the first non-option argument. - The script remains compatible with `dash`, `ksh`, and other strict `sh` implementations. - Automation wrappers can reliably pass arguments without fear of the script "swallowing" parameters. **Example of the more robust approach:** ```sh while [ $# -gt 0 ]; do case "$1" in -i) shift if [ -r "$1" ] || [ -r "$1.pub" ]; then use_id_file "$1" shift else use_id_file "$DEFAULT_PUB_ID_FILE" fi ;; -p) SSH_PORT="$2"; shift 2 ;; # ... handle other flags ... *) break ;; # First non-option is USER_HOST esac done ``` By moving to a standard manual parsing loop, we preserve the "user-friendly" optional argument of `-i` while gaining the reliability required for modern deployment workflows.
issue