feat(floppy-utils): add bash toolkit for retro floppy disk operations
Introduce floppy-utils: a CLI for creating, editing, archiving, and restoring floppy disk images on Linux with USB floppy drive support. Includes main floppy command, legacy wrappers, dependency checker, install script, and comprehensive documentation.
This commit is contained in:
parent
fddf498930
commit
34aeb7bc0d
657
README.md
657
README.md
@ -0,0 +1,657 @@
|
||||
# floppy-utils
|
||||
|
||||
Bash toolkit for retro floppy work on Linux: create and edit disk images, archive physical floppies to files, and write images back to a USB floppy drive—without memorizing `dd`, `losetup`, and mount options.
|
||||
|
||||
Designed for day-to-day use with a **USB floppy drive** (removable `/dev/sdX` that changes between machines). Loop mounts are kept separate from the physical drive so you can work on `.img` files offline and only touch hardware when reading or burning.
|
||||
|
||||
**Tested hardware:** Mitsumi SmartDisk USB UFDD (`03ee:6901`), appearing as `/dev/sda`, FAT12 volumes, automount via udisks at `/media/$USER/<LABEL>`.
|
||||
|
||||
---
|
||||
|
||||
## Table of contents
|
||||
|
||||
- [What it does](#what-it-does)
|
||||
- [Project layout](#project-layout)
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
- [Configuration](#configuration)
|
||||
- [Quick start](#quick-start)
|
||||
- [Command reference](#command-reference)
|
||||
- [Workflows](#workflows)
|
||||
- [Device selection](#device-selection)
|
||||
- [Physical drive vs disk images](#physical-drive-vs-disk-images)
|
||||
- [Safety](#safety)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Legacy wrappers](#legacy-wrappers)
|
||||
- [Limitations](#limitations)
|
||||
|
||||
---
|
||||
|
||||
## What it does
|
||||
|
||||
| Area | Capability |
|
||||
|------|------------|
|
||||
| **Images** | Create blank 360K / 720K / 1.44M `.img` files |
|
||||
| **Edit** | Loop-attach images, optional FAT format, mount under `$FLOPPY_MEDIADIR` |
|
||||
| **Archive** | Sector-accurate read from physical floppy → image file |
|
||||
| **Restore** | Burn image to physical floppy with confirmation |
|
||||
| **USB FDD** | Detect drive, refresh after disk swap, safe USB disconnect (`power-off`) |
|
||||
| **Visibility** | List devices, attached state, full status |
|
||||
|
||||
All operations go through a single CLI: **`floppy`** (with subcommands). Status messages go to **stderr** so command substitution (e.g. device paths) is not corrupted.
|
||||
|
||||
---
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
floppy-utils/
|
||||
├── README.md This file
|
||||
├── check-deps.sh Debian dependency check / apt install
|
||||
├── install.sh Install scripts to ~/.local/bin
|
||||
├── config.example Sample ~/.config/floppy-utils/config
|
||||
└── src/
|
||||
├── floppy Main CLI
|
||||
├── floppy-make Legacy → floppy make
|
||||
├── floppy-attach Legacy → floppy attach
|
||||
├── floppy-burn Legacy → floppy burn
|
||||
└── lib/
|
||||
└── common.sh Shared logic (sourced by floppy)
|
||||
```
|
||||
|
||||
There is no daemon. Scripts use `sudo` when not root for `dd`, `blockdev`, `losetup`, and mount operations.
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
### Supported platforms
|
||||
|
||||
- **Primary:** Debian-based distributions (Debian, Ubuntu, Raspberry Pi OS, Linux Mint, Pop!_OS, Kali, Devuan, etc.)
|
||||
- **Others:** Commands are checked on any Linux; `check-deps.sh --install` refuses non-Debian systems
|
||||
|
||||
### Required commands
|
||||
|
||||
| Command | Debian package | Used for |
|
||||
|---------|------------------|----------|
|
||||
| `lsblk` | util-linux | Device size, label, mount points |
|
||||
| `blockdev` | util-linux | Sector count for read/burn |
|
||||
| `losetup` | util-linux | Attach/detach loop devices |
|
||||
| `findmnt` | util-linux | Mount inspection |
|
||||
| `blkid` | util-linux | Filesystem / label detection |
|
||||
| `mount`, `umount` | mount | Loop and manual mounts |
|
||||
| `dd` | coreutils | Read, burn, create images |
|
||||
| `readlink` | coreutils | Resolve paths |
|
||||
| `mkfs.vfat` | dosfstools | Format new images on attach |
|
||||
|
||||
### Recommended commands
|
||||
|
||||
| Command | Debian package | Used for |
|
||||
|---------|------------------|----------|
|
||||
| `sudo` | sudo | Non-root operation |
|
||||
| `udisksctl` | udisks2 | Unmount/mount physical floppy (desktop integration) |
|
||||
| `udevadm` | udev | Settle after media change (`refresh`) |
|
||||
| `eject` | eject | Optional media-change hint (`refresh --eject`) |
|
||||
| `fdisk` | util-linux | Partition info saved on new formats |
|
||||
|
||||
### Check and install (Debian)
|
||||
|
||||
```bash
|
||||
cd /path/to/floppy-utils
|
||||
|
||||
./check-deps.sh # list OK / missing
|
||||
sudo ./check-deps.sh --install # apt install missing packages
|
||||
./check-deps.sh -q # quiet; used by install.sh
|
||||
```
|
||||
|
||||
`install.sh` runs the quiet check first and exits if required tools are missing.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Dependencies
|
||||
|
||||
```bash
|
||||
sudo ./check-deps.sh --install
|
||||
```
|
||||
|
||||
### 2. Scripts on PATH
|
||||
|
||||
**Option A — install to `~/.local/bin` (recommended)**
|
||||
|
||||
```bash
|
||||
./install.sh
|
||||
```
|
||||
|
||||
Ensure `~/.local/bin` is in your shell `PATH` (add to `~/.bashrc` or `~/.zshrc` if needed):
|
||||
|
||||
```bash
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
```
|
||||
|
||||
**Option B — run from source**
|
||||
|
||||
```bash
|
||||
export PATH="/path/to/floppy-utils/src:$PATH"
|
||||
floppy help
|
||||
```
|
||||
|
||||
### 3. Optional configuration
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/floppy-utils
|
||||
cp config.example ~/.config/floppy-utils/config
|
||||
# Edit: FLOPPY_DISKDIR, FLOPPY_DEVICE, etc.
|
||||
```
|
||||
|
||||
### Verify
|
||||
|
||||
```bash
|
||||
floppy help
|
||||
floppy devices -v
|
||||
floppy list
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
Settings load in this order:
|
||||
|
||||
1. Built-in defaults
|
||||
2. `~/.config/floppy-utils/config` (shell snippet, sourced if present)
|
||||
3. Environment variables (override config file)
|
||||
|
||||
### Variables
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `FLOPPY_DISKDIR` | `$HOME/Retro/BLANKS` | Directory for `.img` files (`make`, `read`, `attach` by name) |
|
||||
| `FLOPPY_MEDIADIR` | `/media/$USER` | Base directory for loop mounts (`attach`) |
|
||||
| `FLOPPY_DEVICE` | *(unset)* | Default block device for physical ops (`/dev/sda` on a dedicated retro box) |
|
||||
| `FLOPPY_DEVICE_MATCH` | `mitsumi\|ufdd\|fdd\|floppy` | Extended regex; boosts USB floppy scoring in `devices` / auto-pick |
|
||||
| `FLOPPY_DEFAULT_SIZE_KB` | `1440` | Default image size for `make` / `attach` |
|
||||
|
||||
### Example config file
|
||||
|
||||
```bash
|
||||
# ~/.config/floppy-utils/config
|
||||
FLOPPY_DISKDIR="$HOME/Retro/BLANKS"
|
||||
FLOPPY_MEDIADIR="/media/$USER"
|
||||
FLOPPY_DEVICE=/dev/sda
|
||||
FLOPPY_DEFAULT_SIZE_KB=1440
|
||||
```
|
||||
|
||||
### Example one-shot env
|
||||
|
||||
```bash
|
||||
FLOPPY_DEVICE=/dev/sda floppy read dosboot -y
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
# 1. USB floppy connected, disk inserted
|
||||
floppy refresh --mount
|
||||
floppy devices -v
|
||||
|
||||
# 2. Archive floppy to an image file
|
||||
floppy read -y # name from volume label or mount dir
|
||||
|
||||
# 3. Edit the image without hardware
|
||||
floppy attach disk
|
||||
# ... copy files under /media/$USER/disk ...
|
||||
floppy detach disk
|
||||
|
||||
# 4. Write image back to a floppy
|
||||
floppy refresh # after inserting blank or target disk
|
||||
floppy burn disk.img -d /dev/sda
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Command reference
|
||||
|
||||
Invoke as `floppy <command> [options] [args]`. Run `floppy help` for a short summary.
|
||||
|
||||
### `make` — create blank image
|
||||
|
||||
```bash
|
||||
floppy make [name] [-s 360|720|1440]
|
||||
```
|
||||
|
||||
- Creates `$FLOPPY_DISKDIR/<name>.img` filled with zeros
|
||||
- Omits `name` → random name `floppy<digits>`
|
||||
- Does not mount or format
|
||||
|
||||
**Examples**
|
||||
|
||||
```bash
|
||||
floppy make blank1440
|
||||
floppy make dos720 -s 720
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `attach` — loop-mount an image for editing
|
||||
|
||||
Aliases: `mount`
|
||||
|
||||
```bash
|
||||
floppy attach [name|path] [-s SIZE] [--format] [--no-format]
|
||||
```
|
||||
|
||||
- **No args:** new random 1.44M image, formatted vfat, mounted
|
||||
- **`name`:** use/create `$FLOPPY_DISKDIR/<name>.img`
|
||||
- **`path`:** absolute or relative path to an existing `.img`
|
||||
- New or raw images: formatted **vfat** unless `--no-format`
|
||||
- Mount point: `$FLOPPY_MEDIADIR/<name>` (not the FAT volume label path used by udisks for physical disks)
|
||||
- Uses `losetup --find --show` and mounts the **loop device** (not the file twice)
|
||||
|
||||
**Examples**
|
||||
|
||||
```bash
|
||||
floppy attach
|
||||
floppy attach mydisk -s 720
|
||||
floppy attach /archive/old.img
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `detach` — unmount and release
|
||||
|
||||
Aliases: `umount`
|
||||
|
||||
```bash
|
||||
floppy detach <name|mountpoint|loop|label>
|
||||
```
|
||||
|
||||
Accepts:
|
||||
|
||||
- Image name (`bootdisk` → `$FLOPPY_MEDIADIR/bootdisk` or loop for that image)
|
||||
- Full mount path (`/media/user/BOOTDISK`)
|
||||
- Loop device (`/dev/loop0`)
|
||||
- Physical volume label directory (`BOOTDISK` under `$FLOPPY_MEDIADIR`)
|
||||
|
||||
For physical `/dev/sdX` mounts, only **unmounts**; does not disconnect USB.
|
||||
|
||||
---
|
||||
|
||||
### `refresh` — re-probe after swapping floppies
|
||||
|
||||
Aliases: `rescan`, `reload`
|
||||
|
||||
```bash
|
||||
floppy refresh [-d DEV] [--mount] [--eject] [-w SEC]
|
||||
```
|
||||
|
||||
Run whenever you change disks **without** unplugging USB. For end of session, use **`floppy disconnect`** instead (see below).
|
||||
|
||||
1. Unmount stale filesystem (udisks or `umount`)
|
||||
2. `blockdev --flushbufs`
|
||||
3. Optional `eject` (`--eject`) for drives that need it
|
||||
4. `udevadm settle` and short wait (`-w`, default 2 seconds)
|
||||
5. Re-probe capacity; show before/after `lsblk` summary
|
||||
6. With `--mount`, mount new media via udisks if detected
|
||||
|
||||
**When to use:** `devices` still shows old label/size, automount behaves oddly, or `read` fails after a swap.
|
||||
|
||||
**Examples**
|
||||
|
||||
```bash
|
||||
floppy refresh
|
||||
floppy refresh --mount
|
||||
floppy refresh --eject -w 3
|
||||
floppy refresh -d /dev/sda
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `disconnect` — safe USB removal (end of session)
|
||||
|
||||
Aliases: `power-off`, `eject-usb`
|
||||
|
||||
```bash
|
||||
floppy disconnect [-d DEV]
|
||||
```
|
||||
|
||||
Use when you are **done with the USB drive** and want to unplug the cable. **Not** for swapping floppies (use `refresh`).
|
||||
|
||||
Steps performed:
|
||||
|
||||
1. Unmount any volume on the device (`udisks` or `umount`)
|
||||
2. `blockdev --flushbufs` and `sync`
|
||||
3. `udisksctl power-off -b DEV` (falls back to `eject` if power-off fails)
|
||||
|
||||
```bash
|
||||
floppy disconnect
|
||||
floppy disconnect -d /dev/sda
|
||||
```
|
||||
|
||||
On success you should see: **Safe to unplug the USB floppy drive now.**
|
||||
|
||||
---
|
||||
|
||||
### `read` — archive physical floppy to disk dir
|
||||
|
||||
Aliases: `archive`
|
||||
|
||||
```bash
|
||||
floppy read [name] [-d DEV] [-y] [--force] [--strict]
|
||||
```
|
||||
|
||||
- Reads **entire device** sector-by-sector into `$FLOPPY_DISKDIR/<name>.img`
|
||||
- Sector count from `blockdev --getsz` (2880 × 512 bytes for 1.44M)
|
||||
- Default `dd` conv: **`noerror,sync,fsync`** (tolerates bad sectors on old media)
|
||||
- Unmounts drive before read; 1 second settle
|
||||
- **`--strict`:** stop on first I/O error instead
|
||||
|
||||
**Default name** (if `name` omitted):
|
||||
|
||||
1. FAT volume label (sanitized, e.g. `DISK 2` → `disk_2`)
|
||||
2. Else basename of mount point (e.g. `disk` from `/media/user/disk`)
|
||||
3. Else random `floppy<digits>`
|
||||
|
||||
**Examples**
|
||||
|
||||
```bash
|
||||
floppy read -y
|
||||
floppy read bootdisk -d /dev/sda -y
|
||||
floppy read --strict -y # fail fast on errors
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `dump` — raw dump to any path
|
||||
|
||||
Aliases: `copy`
|
||||
|
||||
```bash
|
||||
floppy dump -o FILE [-d DEV] [-y] [--force] [--strict]
|
||||
```
|
||||
|
||||
Same read path as `read`, but **`-o`** is required (any file path, or `-` for stdout). Does not use `FLOPPY_DISKDIR` naming rules.
|
||||
|
||||
```bash
|
||||
floppy dump -o /tmp/floppy.raw -d /dev/sda -y
|
||||
floppy dump -o - -d /dev/sda | sha256sum
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `burn` — write image to physical drive
|
||||
|
||||
Aliases: `write`
|
||||
|
||||
```bash
|
||||
floppy burn <image> [-d DEV] [-y]
|
||||
```
|
||||
|
||||
- Resolves image: path, or name in `FLOPPY_DISKDIR`
|
||||
- **Never** hardcodes a device; uses `-d`, `FLOPPY_DEVICE`, or interactive pick
|
||||
- Confirms unless `-y`; warns if mounted, then unmounts
|
||||
- Writes with `dd bs=512 conv=fsync`
|
||||
|
||||
```bash
|
||||
floppy burn disk.img -d /dev/sda
|
||||
FLOPPY_DEVICE=/dev/sda floppy burn dosboot.img -y
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `devices` — list removable block devices
|
||||
|
||||
Aliases: `list-devices`
|
||||
|
||||
```bash
|
||||
floppy devices [-v]
|
||||
```
|
||||
|
||||
- Lists `/dev/sd*` and `/dev/fd*` disks with `RM=1`
|
||||
- **`-v`:** label, fstype, mount, heuristic **score** (USB FDD models score higher)
|
||||
|
||||
Use before `burn`/`read` when unsure of device node.
|
||||
|
||||
---
|
||||
|
||||
### `list` — what is attached now
|
||||
|
||||
Aliases: `attached`, `ls`
|
||||
|
||||
```bash
|
||||
floppy list [-v]
|
||||
```
|
||||
|
||||
| Section | Shows |
|
||||
|---------|--------|
|
||||
| Loop-attached | `.img` → loop device → mount point |
|
||||
| Physical media | Drive with disk inserted (mounted or not) |
|
||||
| **`-v` only** | All `.img` files in `FLOPPY_DISKDIR` |
|
||||
|
||||
Lighter than `status`; focused on “what can I edit or read right now?”
|
||||
|
||||
---
|
||||
|
||||
### `status` — full diagnostic snapshot
|
||||
|
||||
```bash
|
||||
floppy status
|
||||
```
|
||||
|
||||
Prints: config paths, loop attachments, mounts under `$FLOPPY_MEDIADIR`, verbose removable drive list.
|
||||
|
||||
---
|
||||
|
||||
## Workflows
|
||||
|
||||
### A. Swap disks in the USB drive (stay on same `/dev/sdX`)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
swap[Insert new floppy] --> refresh[floppy refresh --mount]
|
||||
refresh --> devices[floppy devices -v]
|
||||
devices --> next[read / burn / browse]
|
||||
```
|
||||
|
||||
```bash
|
||||
floppy refresh --mount
|
||||
floppy devices -v
|
||||
```
|
||||
|
||||
You do **not** need to replug USB between floppies.
|
||||
|
||||
---
|
||||
|
||||
### B. Archive a floppy (physical → file)
|
||||
|
||||
```bash
|
||||
floppy refresh --mount # optional: browse files first
|
||||
floppy devices -v # expect size 1.4M / 720K, not 0B
|
||||
floppy read -y # or: floppy read mylabel -y
|
||||
floppy list -v # confirm image in FLOPPY_DISKDIR
|
||||
```
|
||||
|
||||
Partial reads with warnings usually mean **damaged sectors**; image may still be useful. `floppy1982469877.img`-style random names meant the volume had no usable label—pass an explicit name: `floppy read disk -y`.
|
||||
|
||||
---
|
||||
|
||||
### C. Edit an image without hardware
|
||||
|
||||
```bash
|
||||
floppy attach myproject
|
||||
ls /media/$USER/myproject
|
||||
floppy detach myproject
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### D. Restore image to floppy
|
||||
|
||||
```bash
|
||||
floppy refresh # new disk in drive
|
||||
floppy burn myproject.img -d /dev/sda
|
||||
```
|
||||
|
||||
### G. Done for the day — unplug USB drive
|
||||
|
||||
```bash
|
||||
floppy disconnect # unmount + power off
|
||||
# unplug USB cable
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### E. Create a new blank floppy image for a retro project
|
||||
|
||||
```bash
|
||||
floppy make blank -s 1440
|
||||
floppy attach blank --format
|
||||
# copy files into mount point
|
||||
floppy detach blank
|
||||
floppy burn blank.img -d /dev/sda
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### F. Inspect current state
|
||||
|
||||
```bash
|
||||
floppy list -v # attached loops + physical + archive dir
|
||||
floppy status # everything including config
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Device selection
|
||||
|
||||
When `-d` and `FLOPPY_DEVICE` are unset, `floppy` picks a removable disk:
|
||||
|
||||
1. List removable `sd`/`fd` disks via `lsblk`
|
||||
2. Score each (higher = more likely USB floppy):
|
||||
- +100 if `MODEL` matches `FLOPPY_DEVICE_MATCH`
|
||||
- +40 if `TRAN=usb`
|
||||
- +30 if size is 360K / 720K / 1.4M
|
||||
- +10 if volume label present
|
||||
3. **Auto-select** if exactly one device, or exactly one with score ≥ 100
|
||||
4. Otherwise **interactive menu**
|
||||
|
||||
Always use `floppy devices -v` when multiple USB storage devices are attached (thumb drives, SD readers, etc.).
|
||||
|
||||
---
|
||||
|
||||
## Physical drive vs disk images
|
||||
|
||||
| | Physical USB floppy | Loop image (`attach`) |
|
||||
|--|---------------------|------------------------|
|
||||
| Device | `/dev/sda` | `/dev/loopN` |
|
||||
| Typical mount | `/media/$USER/<FAT_LABEL>` (udisks) | `/media/$USER/<image_name>` |
|
||||
| Create | `refresh`, insert disk | `make`, `attach` |
|
||||
| Archive | `read`, `dump` | copy `.img` file |
|
||||
| Write | `burn` | `burn` to physical only |
|
||||
| Release | `detach <label>`, `refresh` (swap disk) | `detach <name>` |
|
||||
| Unplug USB | `disconnect` | — |
|
||||
|
||||
---
|
||||
|
||||
## Safety
|
||||
|
||||
### Burn
|
||||
|
||||
- Prompts: `Write image to /dev/sdX? This will ERASE the floppy. [y/N]`
|
||||
- Skipped with `-y` only when you are certain
|
||||
- Targets **whole disk** (`/dev/sda`), not partitions (`/dev/sda1`)
|
||||
- Unmounts before write
|
||||
|
||||
### Read / dump
|
||||
|
||||
- Prompt unless `-y`
|
||||
- Refuses `0B` size (no disk inserted)
|
||||
- Does not overwrite existing output without `--force`
|
||||
|
||||
### Permissions
|
||||
|
||||
Non-root users need `sudo` (or polkit via `udisksctl` for mount/unmount). Configure passwordless sudo only if you accept the security tradeoff on a dedicated retro workstation.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `floppy: no media in /dev/sda` but disk is inserted
|
||||
|
||||
- Run `floppy refresh`, then `floppy devices -v` (size should be `1.4M`, not `0B`)
|
||||
- Re-seat the disk; try `floppy refresh --eject -w 3`
|
||||
|
||||
### Wrong label in `devices -v` (path shown as label)
|
||||
|
||||
Fixed in current code (per-field `lsblk`). Update if you see `label=/media/...`.
|
||||
|
||||
### `floppy read` uses random name `floppy123456789`
|
||||
|
||||
Volume has no FAT label; udisks mounted as `/media/user/disk`. Pass a name: `floppy read disk -y`, or set label on the FAT volume.
|
||||
|
||||
### `dd: Input/output error` partway through read
|
||||
|
||||
Common on aging floppies. Default `noerror,sync` completes with holes; warnings are printed. Use `--strict` only if you want fail-fast. Try `refresh` before retry; clean drive heads if errors persist.
|
||||
|
||||
### `floppy: command not found`
|
||||
|
||||
Run `./install.sh` or add `src` to `PATH`. Use `./floppy` from `src` when testing without install.
|
||||
|
||||
### `>>> using removable device` mixed into error text
|
||||
|
||||
Status must go to stderr (fixed). Update `lib/common.sh` if you see this on old copies.
|
||||
|
||||
### Automount fights `read` / `burn`
|
||||
|
||||
`read`, `burn`, and `dump` unmount first. Run `floppy refresh` after manual file-manager mounts.
|
||||
|
||||
### Leftover empty mount directory after `detach`
|
||||
|
||||
e.g. `/media/user/bootdisk (present, not mounted)`. Safe to remove: `rmdir` if empty.
|
||||
|
||||
### Device is `/dev/sda` on this machine but `/dev/sdb` elsewhere
|
||||
|
||||
Do not rely on fixed names; use `floppy devices` or set `FLOPPY_DEVICE` per host in config.
|
||||
|
||||
### Mitsumi drive shows `2880` during refresh
|
||||
|
||||
Internal `blockdev --getsz` probe (suppressed in output on current versions).
|
||||
|
||||
### `udisks power-off failed` on disconnect
|
||||
|
||||
Close file managers using the mount. Run `floppy detach <label>`, then `floppy disconnect` again. You can still unplug after a successful unmount if power-off fails—wait a few seconds for cache flush.
|
||||
|
||||
---
|
||||
|
||||
## Legacy wrappers
|
||||
|
||||
Thin scripts for muscle memory; all call `floppy`:
|
||||
|
||||
| Wrapper | Maps to |
|
||||
|---------|---------|
|
||||
| `floppy-make` | `floppy make "$@"` |
|
||||
| `floppy-attach` | `floppy attach` (supports old two-arg size: `floppy-attach name 1440`) |
|
||||
| `floppy-burn` | `floppy burn "$1"` |
|
||||
|
||||
---
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Linux only** (uses `losetup`, `lsblk`, udisks)
|
||||
- **Debian apt helper** only in `check-deps.sh`; other distros: install packages manually
|
||||
- **FAT-focused** for `attach` format (`mkfs.vfat`); raw/non-FAT images can be read/burned but not auto-mounted as vfat
|
||||
- **No write-protect detection** beyond mount errors
|
||||
- **No internal 5.25" / floppy controller** support beyond what the kernel exposes as `/dev/fd*`
|
||||
- **Sector size** assumed 512 bytes for read/burn
|
||||
- **Not a GUI**; pairs with desktop automount (udisks) when present
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
- `floppy help` — built-in usage text
|
||||
- `config.example` — starter configuration
|
||||
- `check-deps.sh --help` — dependency installer options
|
||||
226
check-deps.sh
Executable file
226
check-deps.sh
Executable file
@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verify dependencies for floppy-utils on Debian-based systems; optionally install via apt.
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL=0
|
||||
QUIET=0
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: check-deps.sh [OPTIONS]
|
||||
|
||||
Check that base tools required by floppy-utils are available.
|
||||
On Debian, Ubuntu, Raspberry Pi OS, and derivatives, missing packages can be
|
||||
installed with apt when --install is passed.
|
||||
|
||||
Options:
|
||||
--install, -i Install missing packages (requires apt and root/sudo)
|
||||
--quiet, -q Only print errors and the install command
|
||||
-h, --help Show this help
|
||||
|
||||
Examples:
|
||||
./check-deps.sh
|
||||
./check-deps.sh --install
|
||||
sudo ./check-deps.sh -i
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--install|-i) INSTALL=1; shift ;;
|
||||
--quiet|-q) QUIET=1; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "check-deps.sh: unknown option: $1" >&2; usage >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
log() {
|
||||
[[ "$QUIET" -eq 0 ]] && echo "$@"
|
||||
}
|
||||
|
||||
warn() {
|
||||
echo "check-deps.sh: $*" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
warn "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
is_debian_like() {
|
||||
if [[ -f /etc/debian_version ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source /etc/os-release
|
||||
case "${ID:-}:${ID_LIKE:-}" in
|
||||
debian:*|*debian*|ubuntu:*|*ubuntu*|raspbian:*|linuxmint:*|pop:*|elementary:*|kali:*|devuan:*)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
os_description() {
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source /etc/os-release
|
||||
printf '%s %s\n' "${NAME:-Linux}" "${VERSION_ID:-}"
|
||||
elif [[ -f /etc/debian_version ]]; then
|
||||
printf 'Debian %s\n' "$(cat /etc/debian_version)"
|
||||
else
|
||||
echo "Unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
# command -> Debian package providing it
|
||||
declare -A CMD_PKG=(
|
||||
[lsblk]=util-linux
|
||||
[blockdev]=util-linux
|
||||
[losetup]=util-linux
|
||||
[findmnt]=util-linux
|
||||
[fdisk]=util-linux
|
||||
[blkid]=util-linux
|
||||
[mount]=mount
|
||||
[umount]=mount
|
||||
[dd]=coreutils
|
||||
[readlink]=coreutils
|
||||
[mkfs.vfat]=dosfstools
|
||||
[sudo]=sudo
|
||||
[udisksctl]=udisks2
|
||||
[udevadm]=udev
|
||||
[eject]=eject
|
||||
)
|
||||
|
||||
REQUIRED_CMDS=(
|
||||
lsblk blockdev losetup findmnt blkid mount umount dd readlink mkfs.vfat
|
||||
)
|
||||
|
||||
RECOMMENDED_CMDS=(
|
||||
sudo udisksctl udevadm eject fdisk
|
||||
)
|
||||
|
||||
have_cmd() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
packages_for_cmds() {
|
||||
local -A seen=()
|
||||
local cmd pkg
|
||||
for cmd in "$@"; do
|
||||
pkg="${CMD_PKG[$cmd]:-}"
|
||||
[[ -n "$pkg" && -z "${seen[$pkg]:-}" ]] || continue
|
||||
seen[$pkg]=1
|
||||
printf '%s\n' "$pkg"
|
||||
done
|
||||
}
|
||||
|
||||
check_cmds() {
|
||||
local label="$1"
|
||||
local required="$2"
|
||||
shift 2
|
||||
local cmd
|
||||
local -a missing=()
|
||||
|
||||
log "$label"
|
||||
for cmd in "$@"; do
|
||||
if have_cmd "$cmd"; then
|
||||
log " OK $cmd"
|
||||
else
|
||||
if [[ "$required" -eq 1 ]]; then
|
||||
log " MISS $cmd (required)"
|
||||
else
|
||||
log " MISS $cmd (recommended)"
|
||||
fi
|
||||
missing+=("$cmd")
|
||||
fi
|
||||
done
|
||||
log ""
|
||||
|
||||
MISSING_RESULT=("${missing[@]}")
|
||||
}
|
||||
|
||||
run_apt_install() {
|
||||
local -a packages=("$@")
|
||||
[[ ${#packages[@]} -gt 0 ]] || return 0
|
||||
|
||||
if ! command -v apt-get >/dev/null 2>&1; then
|
||||
die "apt-get not found; install manually: ${packages[*]}"
|
||||
fi
|
||||
|
||||
local -a apt_cmd=(apt-get)
|
||||
if [[ "$(id -u)" -ne 0 ]]; then
|
||||
command -v sudo >/dev/null 2>&1 || die "root or sudo required to install: ${packages[*]}"
|
||||
apt_cmd=(sudo apt-get)
|
||||
fi
|
||||
|
||||
log "Installing: ${packages[*]}"
|
||||
"${apt_cmd[@]}" update -qq
|
||||
DEBIAN_FRONTEND=noninteractive "${apt_cmd[@]}" install -y --no-install-recommends "${packages[@]}"
|
||||
}
|
||||
|
||||
main() {
|
||||
local -a missing_required=() missing_recommended=() packages=()
|
||||
local debian=0
|
||||
|
||||
log "floppy-utils dependency check"
|
||||
log "OS: $(os_description)"
|
||||
|
||||
if is_debian_like; then
|
||||
debian=1
|
||||
log "Debian-based system detected."
|
||||
else
|
||||
warn "Not a Debian-based system — will check commands only (no apt install)."
|
||||
fi
|
||||
log ""
|
||||
|
||||
check_cmds "Required commands:" 1 "${REQUIRED_CMDS[@]}"
|
||||
missing_required=("${MISSING_RESULT[@]}")
|
||||
check_cmds "Recommended commands:" 0 "${RECOMMENDED_CMDS[@]}"
|
||||
missing_recommended=("${MISSING_RESULT[@]}")
|
||||
|
||||
if [[ ${#missing_required[@]} -eq 0 && ${#missing_recommended[@]} -eq 0 ]]; then
|
||||
log "All required and recommended commands are available."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ${#missing_required[@]} -gt 0 ]]; then
|
||||
log "Missing required: ${missing_required[*]}"
|
||||
fi
|
||||
if [[ ${#missing_recommended[@]} -gt 0 ]]; then
|
||||
log "Missing recommended: ${missing_recommended[*]}"
|
||||
fi
|
||||
|
||||
mapfile -t packages < <(packages_for_cmds "${missing_required[@]}" "${missing_recommended[@]}")
|
||||
if [[ ${#packages[@]} -gt 0 ]]; then
|
||||
log "Debian packages: ${packages[*]}"
|
||||
fi
|
||||
|
||||
if [[ "$INSTALL" -eq 1 ]]; then
|
||||
[[ "$debian" -eq 1 ]] || die "refusing --install on non-Debian system"
|
||||
run_apt_install "${packages[@]}"
|
||||
log ""
|
||||
log "Re-checking..."
|
||||
INSTALL=0
|
||||
main
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$debian" -eq 1 && ${#packages[@]} -gt 0 ]]; then
|
||||
log ""
|
||||
log "To install:"
|
||||
log " sudo ./check-deps.sh --install"
|
||||
log " # or: sudo apt-get install -y ${packages[*]}"
|
||||
fi
|
||||
|
||||
if [[ ${#missing_required[@]} -gt 0 ]]; then
|
||||
die "required dependencies missing"
|
||||
fi
|
||||
|
||||
warn "recommended tools missing — floppy-utils works with reduced functionality"
|
||||
exit 0
|
||||
}
|
||||
|
||||
main
|
||||
10
config.example
Normal file
10
config.example
Normal file
@ -0,0 +1,10 @@
|
||||
# Copy to ~/.config/floppy-utils/config and adjust paths.
|
||||
|
||||
# FLOPPY_DISKDIR="$HOME/Retro/BLANKS"
|
||||
# FLOPPY_MEDIADIR="/media/$USER"
|
||||
|
||||
# Mitsumi USB UFDD on this machine (when no other removable disks attached):
|
||||
# FLOPPY_DEVICE=/dev/sda
|
||||
|
||||
# FLOPPY_DEFAULT_SIZE_KB=1440
|
||||
# FLOPPY_DEVICE_MATCH='mitsumi|ufdd|fdd|floppy'
|
||||
23
install.sh
Executable file
23
install.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# Install floppy-utils scripts to ~/.local/bin
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
if [[ -x "$ROOT/check-deps.sh" ]]; then
|
||||
"$ROOT/check-deps.sh" -q || {
|
||||
echo "Run ./check-deps.sh --install to fix missing dependencies." >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
SRC="$ROOT/src"
|
||||
DEST="${HOME}/.local/bin"
|
||||
|
||||
mkdir -p "$DEST"
|
||||
for cmd in floppy floppy-make floppy-attach floppy-burn; do
|
||||
install -m 755 "$SRC/$cmd" "$DEST/$cmd"
|
||||
done
|
||||
|
||||
echo "Installed to $DEST"
|
||||
echo "Ensure \$HOME/.local/bin is on your PATH, then run: floppy help"
|
||||
309
src/floppy
Executable file
309
src/floppy
Executable file
@ -0,0 +1,309 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
FLOPPY_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib/common.sh
|
||||
source "$FLOPPY_SCRIPT_DIR/lib/common.sh"
|
||||
floppy_load_config
|
||||
|
||||
floppy_parse_size() {
|
||||
local size="${1:-$FLOPPY_DEFAULT_SIZE_KB}"
|
||||
case "$size" in
|
||||
360|720|1440) echo "$size" ;;
|
||||
360K|720K|1440K|1.44M|1.44m) echo "${size%K}" | sed 's/1.44M/1440/i' ;;
|
||||
*) floppy_die "invalid size: $size (use 360, 720, or 1440)" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
floppy_cmd_make() {
|
||||
local name="" size_kb="$FLOPPY_DEFAULT_SIZE_KB"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-s|--size)
|
||||
[[ $# -ge 2 ]] || floppy_die "$1 requires a value"
|
||||
size_kb="$(floppy_parse_size "$2")"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
echo "Usage: floppy make [name] [-s 360|720|1440]"
|
||||
return 0
|
||||
;;
|
||||
-*)
|
||||
floppy_die "unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
[[ -z "$name" ]] || floppy_die "unexpected argument: $1"
|
||||
name="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$name" ]]; then
|
||||
name="$(floppy_random_name)"
|
||||
fi
|
||||
|
||||
floppy_create_blank "$name" "$size_kb" >/dev/null
|
||||
floppy_info "created $(floppy_image_path_in_diskdir "$name")"
|
||||
}
|
||||
|
||||
floppy_cmd_devices() {
|
||||
local verbose=0
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-v|--verbose) verbose=1; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: floppy devices [-v]"
|
||||
return 0
|
||||
;;
|
||||
*) floppy_die "unknown option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
local out
|
||||
out="$(floppy_list_devices "$verbose")"
|
||||
if [[ -z "$out" ]]; then
|
||||
echo "No removable block devices found."
|
||||
echo "Connect a USB floppy drive and run again."
|
||||
return 0
|
||||
fi
|
||||
echo "Removable block devices (candidates for burn):"
|
||||
echo "$out"
|
||||
}
|
||||
|
||||
main() {
|
||||
local cmd="${1:-help}"
|
||||
shift || true
|
||||
|
||||
case "$cmd" in
|
||||
make)
|
||||
floppy_cmd_make "$@"
|
||||
;;
|
||||
attach|mount)
|
||||
local image_arg="" size_kb="$FLOPPY_DEFAULT_SIZE_KB" format="auto"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-s|--size)
|
||||
[[ $# -ge 2 ]] || floppy_die "$1 requires a value"
|
||||
size_kb="$(floppy_parse_size "$2")"
|
||||
shift 2
|
||||
;;
|
||||
--format) format=yes; shift ;;
|
||||
--no-format) format=no; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: floppy attach [name|path] [-s SIZE] [--format|--no-format]"
|
||||
return 0
|
||||
;;
|
||||
-*)
|
||||
floppy_die "unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
[[ -z "$image_arg" ]] || floppy_die "unexpected argument: $1"
|
||||
image_arg="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
floppy_cmd_attach "$image_arg" "$size_kb" "$format"
|
||||
;;
|
||||
detach|umount)
|
||||
[[ $# -ge 1 ]] || floppy_die "usage: floppy detach <name|mountpoint|loop>"
|
||||
floppy_cmd_detach "$1"
|
||||
;;
|
||||
read|archive)
|
||||
local name="" device="" yes=0 force=0 strict=0
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-d|--device)
|
||||
[[ $# -ge 2 ]] || floppy_die "$1 requires a value"
|
||||
device="$2"
|
||||
shift 2
|
||||
;;
|
||||
-y|--yes) yes=1; shift ;;
|
||||
--force) force=1; shift ;;
|
||||
--strict) strict=1; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: floppy read [name] [-d /dev/sdX] [-y] [--force] [--strict]"
|
||||
echo " Archives inserted floppy to FLOPPY_DISKDIR/<name>.img"
|
||||
echo " Default name: volume label, else mount dir name (e.g. disk), else random"
|
||||
echo " Uses dd conv=noerror,sync for bad sectors unless --strict"
|
||||
return 0
|
||||
;;
|
||||
-*)
|
||||
floppy_die "unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
[[ -z "$name" ]] || floppy_die "unexpected argument: $1"
|
||||
name="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
floppy_cmd_read "$name" "$device" "$yes" "$force" "$strict"
|
||||
;;
|
||||
dump|copy)
|
||||
local output="" device="" yes=0 force=0 strict=0
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-o|--output)
|
||||
[[ $# -ge 2 ]] || floppy_die "$1 requires a value"
|
||||
output="$2"
|
||||
shift 2
|
||||
;;
|
||||
-d|--device)
|
||||
[[ $# -ge 2 ]] || floppy_die "$1 requires a value"
|
||||
device="$2"
|
||||
shift 2
|
||||
;;
|
||||
-y|--yes) yes=1; shift ;;
|
||||
--force) force=1; shift ;;
|
||||
--strict) strict=1; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: floppy dump -o FILE [-d /dev/sdX] [-y] [--force] [--strict]"
|
||||
echo " Raw sector-for-sector copy; use -o - for stdout"
|
||||
return 0
|
||||
;;
|
||||
-*)
|
||||
floppy_die "unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
floppy_die "unexpected argument: $1 (use -o FILE)"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
[[ -n "$output" ]] || floppy_die "usage: floppy dump -o FILE [-d DEVICE]"
|
||||
floppy_cmd_dump "$output" "$device" "$yes" "$force" "$strict"
|
||||
;;
|
||||
burn|write)
|
||||
local image="" device="" yes=0
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-d|--device)
|
||||
[[ $# -ge 2 ]] || floppy_die "$1 requires a value"
|
||||
device="$2"
|
||||
shift 2
|
||||
;;
|
||||
-y|--yes) yes=1; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: floppy burn <image> [-d /dev/sdX] [-y]"
|
||||
return 0
|
||||
;;
|
||||
-*)
|
||||
floppy_die "unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
[[ -z "$image" ]] || floppy_die "unexpected argument: $1"
|
||||
image="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
[[ -n "$image" ]] || floppy_die "usage: floppy burn <image> [-d DEVICE] [-y]"
|
||||
floppy_cmd_burn "$image" "$device" "$yes"
|
||||
;;
|
||||
devices|list-devices)
|
||||
floppy_cmd_devices "$@"
|
||||
;;
|
||||
list|attached|ls)
|
||||
local verbose=0
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-v|--verbose) verbose=1; shift ;;
|
||||
-h|--help)
|
||||
echo "Usage: floppy list [-v]"
|
||||
echo " Loop-mounted .img files and physical floppy media in the drive"
|
||||
return 0
|
||||
;;
|
||||
*) floppy_die "unknown option: $1" ;;
|
||||
esac
|
||||
done
|
||||
floppy_cmd_list "$verbose"
|
||||
;;
|
||||
refresh|rescan|reload)
|
||||
local device="" do_eject=0 do_mount=0 wait_secs=2
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-d|--device)
|
||||
[[ $# -ge 2 ]] || floppy_die "$1 requires a value"
|
||||
device="$2"
|
||||
shift 2
|
||||
;;
|
||||
--eject) do_eject=1; shift ;;
|
||||
--mount) do_mount=1; shift ;;
|
||||
-w|--wait)
|
||||
[[ $# -ge 2 ]] || floppy_die "$1 requires a value"
|
||||
wait_secs="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
cat <<'EOF'
|
||||
Usage: floppy refresh [-d /dev/sdX] [--mount] [--eject] [-w SEC]
|
||||
|
||||
Run after swapping floppies in the same USB drive (without unplugging USB).
|
||||
Unmounts stale filesystem state, flushes buffers, and re-probes media size/label.
|
||||
|
||||
--mount Mount the new disk via udisks when media is detected
|
||||
--eject Call eject(1) first (some Mitsumi/USB FDD units need this)
|
||||
-w SEC Wait before re-probe (default 2)
|
||||
EOF
|
||||
return 0
|
||||
;;
|
||||
-*)
|
||||
floppy_die "unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
floppy_die "unexpected argument: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
floppy_cmd_refresh "$device" "$do_eject" "$do_mount" "$wait_secs"
|
||||
;;
|
||||
disconnect|power-off|eject-usb)
|
||||
local device=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-d|--device)
|
||||
[[ $# -ge 2 ]] || floppy_die "$1 requires a value"
|
||||
device="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
cat <<'EOF'
|
||||
Usage: floppy disconnect [-d /dev/sdX]
|
||||
|
||||
Safely finish work with the USB floppy drive:
|
||||
1. Unmount any mounted volume on the device
|
||||
2. Flush block device buffers
|
||||
3. Power off via udisks (or eject as fallback)
|
||||
|
||||
After success, unplug the USB cable. Use this when done for the day — not when
|
||||
swapping floppies (use "floppy refresh" instead).
|
||||
|
||||
Aliases: power-off, eject-usb
|
||||
EOF
|
||||
return 0
|
||||
;;
|
||||
-*)
|
||||
floppy_die "unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
floppy_die "unexpected argument: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
floppy_cmd_disconnect "$device"
|
||||
;;
|
||||
status)
|
||||
floppy_cmd_status
|
||||
;;
|
||||
help|--help|-h)
|
||||
floppy_usage
|
||||
;;
|
||||
*)
|
||||
floppy_die "unknown command: $cmd (try: floppy help)"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@ -1,59 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
MEDIADIR="/media/gmgauthier"
|
||||
format=0 # Don't format disk images that already exist
|
||||
|
||||
# get name of freshly created blank disk, or create one
|
||||
if [ $# -eq 0 ]; then
|
||||
echo ">>> NEW 1.44MB DISK IS BEING CREATED..."
|
||||
PREFIX="floppy"
|
||||
RANDO=$(od -An -N4 -i < /dev/urandom|sed 's/[ -]//g')
|
||||
DISK="$PREFIX$RANDO"
|
||||
dd if=/dev/zero of=$DISK.img bs=1k count=1440
|
||||
format=1
|
||||
# Legacy wrapper: positional args map to attach [name] -s SIZE
|
||||
set -euo pipefail
|
||||
dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [[ $# -eq 0 ]]; then
|
||||
exec "$dir/floppy" attach
|
||||
elif [[ $# -eq 1 ]]; then
|
||||
exec "$dir/floppy" attach "$1"
|
||||
elif [[ $# -eq 2 ]]; then
|
||||
exec "$dir/floppy" attach "$1" -s "$2"
|
||||
else
|
||||
DISK=$1
|
||||
if [ $# -eq 1 ]; then
|
||||
echo "Second argument should be disk size in kbytes: 360, 720, 1440"
|
||||
exit 1
|
||||
fi
|
||||
SIZE=$2
|
||||
if ! [ -f $DISK.img ]; then
|
||||
dd if=/dev/zero of=$DISK.img bs=1k count=$SIZE
|
||||
format=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Find the next available loop device on the system
|
||||
echo ">>> LOOP DEVICE IS BEING IDENTIFIED..."
|
||||
list=$(losetup -O name|sed 's/\/dev\/loop//g'|sed 's/NAME//g'|sort - --sort=human-numeric)
|
||||
### listarr=(${(@s: :)list}) # zsh version of the array generator
|
||||
listarr=($(echo $list | tr " " "\n")) #bash version of array generator
|
||||
lastdev=${listarr[-1]}
|
||||
lastdevnum=$((lastdev))
|
||||
nextdev=$((lastdevnum+1))
|
||||
|
||||
# allocate the next available loop device, format the disk, and mount it.
|
||||
nextdevname="/dev/loop$nextdev"
|
||||
echo ">>> LOOP DEVICE $nextdevname RESERVED FOR DISK: $DISK.img"
|
||||
|
||||
sudo losetup $nextdevname $DISK.img
|
||||
|
||||
if [ $format -eq 1 ]; then
|
||||
echo ">>> DISK IMAGE IS RAW AND WILL BE FORMATTED"
|
||||
sudo mkfs -t vfat $nextdevname
|
||||
fi
|
||||
|
||||
if ! [ -d $MEDIADIR/$DISK ]; then
|
||||
echo ">>> MOUNT DIRECTORY NOT FOUND. CREATING DIRECTORY NOW."
|
||||
sudo mkdir $MEDIADIR/$DISK
|
||||
fi
|
||||
|
||||
echo ">>> DISK $DISK.img MOUNTING AT LOCATION: $MEDIADIR/$DISK"
|
||||
sudo mount $DISK.img $MEDIADIR/$DISK
|
||||
|
||||
if [ $format -eq 1 ]; then
|
||||
echo ">>> DISK INFORMATION BEING COPIED TO $DISK.img"
|
||||
sudo fdisk -l $nextdevname >> $DISK-fdisk.txt
|
||||
sudo cp $DISK-fdisk.txt $MEDIADIR/$DISK/$DISK.txt && rm $DISK-fdisk.txt
|
||||
fi
|
||||
echo "Usage: floppy-attach [name] [size_kb]" >&2
|
||||
echo " or: floppy attach [name] [-s SIZE]" >&2
|
||||
exit 1
|
||||
fi
|
||||
@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "Supply the disk image filepath to burn to disk"
|
||||
exit 1
|
||||
# Legacy wrapper: single positional arg = image path
|
||||
set -euo pipefail
|
||||
dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [[ $# -eq 1 ]]; then
|
||||
exec "$dir/floppy" burn "$1"
|
||||
fi
|
||||
|
||||
sudo dd if="$1" of=/dev/sda bs=512 conv=sync ; sync
|
||||
echo "Usage: floppy-burn <image>" >&2
|
||||
echo " or: floppy burn <image> [-d DEVICE] [-y]" >&2
|
||||
exit 1
|
||||
@ -1,23 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
DISKDIR="/home/gmgauthier/Retro/BLANKS"
|
||||
|
||||
# get name of freshly created blank disk, or create one
|
||||
if [ $# -eq 0 ]; then
|
||||
echo ">>> NEW 1.44MB DISK IS BEING CREATED..."
|
||||
PREFIX="floppy"
|
||||
RANDO=$(od -An -N4 -i < /dev/urandom|sed 's/[ -]//g')
|
||||
DISK="$PREFIX$RANDO"
|
||||
dd if=/dev/zero of=$DISKDIR/$DISK.img bs=1k count=1440
|
||||
else
|
||||
DISK=$1
|
||||
if [ $# -eq 1 ]; then
|
||||
echo "Second argument should be disk size in kbytes: 360, 720, 1440"
|
||||
exit 1
|
||||
fi
|
||||
SIZE=$2
|
||||
if ! [ -f $DISK.img ]; then
|
||||
dd if=/dev/zero of=$DISKDIR/$DISK.img bs=1k count=$SIZE
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/floppy" make "$@"
|
||||
1019
src/lib/common.sh
Normal file
1019
src/lib/common.sh
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user