223 lines
6.3 KiB
Bash
Executable File
223 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
#
|
||
# StarForth — Steady-State Virtual Machine Runtime
|
||
#
|
||
# Copyright (c) 2023–2025 Robert A. James
|
||
# All rights reserved.
|
||
#
|
||
# This file is part of the StarForth project.
|
||
#
|
||
# Licensed under the StarForth License, Version 1.0 (the "License");
|
||
# you may not use this file except in compliance with the License.
|
||
#
|
||
# You may obtain a copy of the License at:
|
||
# https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
#
|
||
# This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
# express or implied, including but not limited to the warranties of
|
||
# merchantability, fitness for a particular purpose, and noninfringement.
|
||
#
|
||
# See the License for the specific language governing permissions and
|
||
# limitations under the License.
|
||
#
|
||
# StarForth — Steady-State Virtual Machine Runtime
|
||
# Copyright (c) 2023–2025 Robert A. James
|
||
# All rights reserved.
|
||
#
|
||
# This file is part of the StarForth project.
|
||
#
|
||
# Licensed under the StarForth License, Version 1.0 (the "License");
|
||
# you may not use this file except in compliance with the License.
|
||
#
|
||
# You may obtain a copy of the License at:
|
||
# https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
#
|
||
# This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
# express or implied, including but not limited to the warranties of
|
||
# merchantability, fitness for a particular purpose, and noninfringement.
|
||
#
|
||
# See the License for the specific language governing permissions and
|
||
# limitations under the License.
|
||
#
|
||
#
|
||
|
||
# scripts/apply-init.sh — interactive wrapper for tools/apply-init
|
||
# Reads ./conf/init-4.4th and writes its Block N sections into a selected disk image.
|
||
|
||
set -euo pipefail
|
||
|
||
# --- Layout ---
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||
TOOLS_DIR="${REPO_ROOT}/tools"
|
||
DISKS_DIR="${REPO_ROOT}/disks"
|
||
CONF_DIR="${REPO_ROOT}/conf"
|
||
|
||
SRC_C="${TOOLS_DIR}/apply_init.c"
|
||
BIN="${TOOLS_DIR}/apply-init"
|
||
|
||
# Defaults
|
||
IN_DEFAULT="${CONF_DIR}/init.4th"
|
||
FBS_DEFAULT="1024"
|
||
START_GUARD_DEFAULT="0" # bump to 1024 if you want to default-guard boot area
|
||
END_GUARD_DEFAULT="-1" # -1 = no upper guard
|
||
|
||
BOLD="$(printf '\033[1m')"
|
||
DIM="$(printf '\033[2m')"
|
||
RESET="$(printf '\033[0m')"
|
||
|
||
die(){ echo "ERROR: $*" >&2; exit 1; }
|
||
have_cmd(){ command -v "$1" >/dev/null 2>&1; }
|
||
|
||
pick_cc(){
|
||
if have_cmd gcc; then echo gcc
|
||
elif have_cmd cc; then echo cc
|
||
else die "No C compiler found (need gcc or cc)."
|
||
fi
|
||
}
|
||
|
||
stat_mtime(){
|
||
stat -c '%Y' "$1" 2>/dev/null || stat -f '%m' "$1"
|
||
}
|
||
|
||
need_rebuild(){
|
||
local src="$1" bin="$2"
|
||
[[ ! -f "$bin" ]] && return 0
|
||
[[ ! -f "$src" ]] && return 1
|
||
local s b; s="$(stat_mtime "$src")"; b="$(stat_mtime "$bin")"
|
||
[[ "$s" -gt "$b" ]] && return 0 || return 1
|
||
}
|
||
|
||
build_apply(){
|
||
[[ -f "$SRC_C" ]] || die "Missing source: $SRC_C"
|
||
mkdir -p "$TOOLS_DIR"
|
||
local CC FLAGS
|
||
CC="$(pick_cc)"
|
||
FLAGS="-std=gnu99 -D_FILE_OFFSET_BITS=64 -O2 -Wall -Wextra -Werror"
|
||
echo "${DIM}Building: $CC $FLAGS -o $BIN $SRC_C${RESET}"
|
||
"$CC" $FLAGS -o "$BIN" "$SRC_C"
|
||
echo "${DIM}Built $BIN${RESET}"
|
||
}
|
||
|
||
ensure_layout(){
|
||
[[ -d "$DISKS_DIR" ]] || die "Missing disks directory: $DISKS_DIR"
|
||
[[ -d "$CONF_DIR" ]] || mkdir -p "$CONF_DIR"
|
||
if need_rebuild "$SRC_C" "$BIN"; then build_apply; fi
|
||
[[ -x "$BIN" ]] || die "apply-init not executable: $BIN"
|
||
}
|
||
|
||
ask_yes_no(){
|
||
local prompt="$1" default="${2:-y}" ans def_disp="Y/n"
|
||
[[ "$default" == "n" ]] && def_disp="y/N"
|
||
read -r -p "$prompt [$def_disp] " ans || true
|
||
ans="${ans:-$default}"
|
||
case "$ans" in y|Y|yes|YES) return 0 ;; n|N|no|NO) return 1 ;; *) echo "y or n, please." >&2; ask_yes_no "$prompt" "$default" ;; esac
|
||
}
|
||
|
||
list_disks(){
|
||
find "$DISKS_DIR" -maxdepth 1 -type f -printf "%f\n" | sort
|
||
}
|
||
|
||
pick_disk(){
|
||
echo "${BOLD}Select a disk image from ${DISKS_DIR}:${RESET}"
|
||
mapfile -t DISKS < <(list_disks)
|
||
[[ "${#DISKS[@]}" -gt 0 ]] || die "No disk images found in $DISKS_DIR"
|
||
local i=1
|
||
for d in "${DISKS[@]}"; do printf " %2d) %s\n" "$i" "$d"; i=$((i+1)); done
|
||
local choice
|
||
while :; do
|
||
read -r -p "Enter choice [1-${#DISKS[@]}]: " choice || true
|
||
[[ "$choice" =~ ^[0-9]+$ ]] || { echo "Not a number."; continue; }
|
||
(( choice>=1 && choice<=${#DISKS[@]} )) || { echo "Out of range."; continue; }
|
||
DISK_CHOSEN="${DISKS_DIR}/${DISKS[$((choice-1))]}"
|
||
break
|
||
done
|
||
echo "Selected: ${BOLD}${DISK_CHOSEN}${RESET}"
|
||
}
|
||
|
||
ask_int(){
|
||
local prompt="$1" default="$2" __out="$3" val
|
||
read -r -p "$prompt (default ${default}): " val || true
|
||
val="${val:-$default}"
|
||
[[ "$val" =~ ^-?[0-9]+$ ]] || { echo "Not a valid integer: $val"; ask_int "$prompt" "$default" "$__out"; return; }
|
||
printf -v "$__out" "%s" "$val"
|
||
}
|
||
|
||
collect_params(){
|
||
echo
|
||
read -r -p "Path to init.4th [default ${IN_DEFAULT}]: " IN_PATH || true
|
||
IN_PATH="${IN_PATH:-$IN_DEFAULT}"
|
||
[[ -f "$IN_PATH" ]] || die "Input file not found: $IN_PATH"
|
||
|
||
echo
|
||
ask_int "Forth block size (FBS)" "$FBS_DEFAULT" FBS
|
||
FBS_FLAG="--fbs=${FBS}"
|
||
|
||
echo
|
||
ask_int "Guard: minimum block to allow (start)" "$START_GUARD_DEFAULT" START_GUARD
|
||
ask_int "Guard: maximum block to allow (end, -1 for none)" "$END_GUARD_DEFAULT" END_GUARD
|
||
|
||
echo
|
||
if ask_yes_no "Truncate block text if longer than FBS? (--clip)" "n"; then
|
||
CLIP_FLAG="--clip"
|
||
else
|
||
CLIP_FLAG=""
|
||
fi
|
||
|
||
if ask_yes_no "Dry run (no writes)?" "n"; then
|
||
DRY_FLAG="--dry-run"
|
||
else
|
||
DRY_FLAG=""
|
||
fi
|
||
|
||
if ask_yes_no "Verify after write (read-back & compare)?" "y"; then
|
||
VERIFY_FLAG="--verify"
|
||
else
|
||
VERIFY_FLAG=""
|
||
fi
|
||
|
||
if ask_yes_no "Verbose logging?" "n"; then
|
||
VERBOSE_FLAG="--verbose"
|
||
else
|
||
VERBOSE_FLAG=""
|
||
fi
|
||
}
|
||
|
||
build_command(){
|
||
CMD=( "$BIN"
|
||
"--img=${DISK_CHOSEN}"
|
||
"--in=${IN_PATH}"
|
||
"$FBS_FLAG"
|
||
)
|
||
# Guards
|
||
[[ "$START_GUARD" != "0" ]] && CMD+=( "--start=${START_GUARD}" ) || CMD+=( "--start=0" )
|
||
[[ "$END_GUARD" != "-1" ]] && CMD+=( "--end=${END_GUARD}" )
|
||
# Toggles
|
||
[[ -n "$CLIP_FLAG" ]] && CMD+=( "$CLIP_FLAG" )
|
||
[[ -n "$DRY_FLAG" ]] && CMD+=( "$DRY_FLAG" )
|
||
[[ -n "$VERIFY_FLAG" ]] && CMD+=( "$VERIFY_FLAG" )
|
||
[[ -n "$VERBOSE_FLAG" ]] && CMD+=( "$VERBOSE_FLAG" )
|
||
}
|
||
|
||
main(){
|
||
ensure_layout
|
||
pick_disk
|
||
collect_params
|
||
build_command
|
||
|
||
echo
|
||
echo "${BOLD}About to run:${RESET}"
|
||
printf " %q " "${CMD[@]}"; echo
|
||
echo
|
||
if ask_yes_no "Proceed?" "y"; then
|
||
"${CMD[@]}"
|
||
echo
|
||
echo "${BOLD}Done.${RESET} Applied init.4th → ${DISK_CHOSEN}"
|
||
else
|
||
echo "Aborted."
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
main "$@"
|