#!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} manifest=${1:-linuxcnc-kinematics-source-files.txt} if [[ ! -d "$linuxcnc_root" ]]; then echo "missing LinuxCNC root: $linuxcnc_root" >&2 exit 1 fi if [[ ! -f "$manifest" ]]; then echo "missing kinematics manifest: $manifest" >&2 exit 1 fi linuxcnc_root=$(cd "$linuxcnc_root" && pwd) normalize_alias() { tr '[:upper:]' '[:lower:]' | tr -d '[:space:]' } trim_value() { sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//' } ini_value() { local file=$1 local key=$2 sed -n "s/^[[:space:]]*$key[[:space:]]*=[[:space:]]*//Ip" "$file" | head -n 1 | trim_value } ini_values() { local file=$1 local key=$2 sed -n "s/^[[:space:]]*$key[[:space:]]*=[[:space:]]*//Ip" "$file" | trim_value } remap_ngc_name() { local file=$1 local mcode=$2 sed -n "s/.*REMAP[[:space:]]*=[[:space:]]*M$mcode[[:space:]][^#;]*ngc[[:space:]]*=[[:space:]]*\\([^[:space:]#;]*\\).*/\\1/Ip" "$file" | head -n 1 } resolve_config_path() { local config_dir=$1 local path=$2 if [[ "$path" = /* ]]; then printf '%s\n' "$path" else printf '%s\n' "$config_dir/$path" fi } relative_linuxcnc_path() { local path=$1 path=$(cd "$(dirname "$path")" && pwd)/$(basename "$path") printf '%s\n' "${path#"$linuxcnc_root/"}" } remap_source_for_mcode() { local config=$1 local mcode=$2 local ngc_name ngc_name=$(remap_ngc_name "$config" "$mcode") if [[ -z "$ngc_name" ]]; then return 1 fi if [[ "$ngc_name" != *.ngc ]]; then ngc_name="$ngc_name.ngc" fi local config_dir config_dir=$(cd "$(dirname "$config")" && pwd) local subroutine_paths subroutine_paths=$(ini_values "$config" "SUBROUTINE_PATH") if [[ -z "$subroutine_paths" ]]; then subroutine_paths="." fi local subroutine_path while IFS= read -r subroutine_path; do [[ -n "$subroutine_path" ]] || continue local path_part IFS=: read -ra path_parts <<<"$subroutine_path" for path_part in "${path_parts[@]}"; do [[ -n "$path_part" ]] || continue local candidate_dir candidate_dir=$(resolve_config_path "$config_dir" "$path_part") if [[ -f "$candidate_dir/$ngc_name" ]]; then relative_linuxcnc_path "$candidate_dir/$ngc_name" return 0 fi done done <<<"$subroutine_paths" return 1 } kinstype_for_mcode() { local config=$1 local mcode=$2 local source if ! source=$(remap_source_for_mcode "$config" "$mcode"); then printf '%s\n' "-1" return 0 fi sed -n 's/^[[:space:]]*#[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' \ "$linuxcnc_root/$source" | head -n 1 } json_compact_string() { local value=$1 printf '%s' "$value" | normalize_alias | sed 's/\\/\\\\/g; s/"/\\"/g' } declare -A seen_aliases=() entries=() add_alias() { local alias=$1 local m428=$2 local m429=$3 local m430=$4 alias=$(json_compact_string "$alias") [[ -n "$alias" ]] || return 0 local types="$m428,$m429,$m430" if [[ -n "${seen_aliases[$alias]:-}" ]]; then if [[ "${seen_aliases[$alias]}" != "$types" ]]; then echo "ambiguous switchkins alias '$alias': ${seen_aliases[$alias]} and $types" >&2 exit 1 fi return 0 fi seen_aliases[$alias]=$types entries+=(" {\"$alias\", $m428, $m429, $m430},") } add_machine_aliases() { local machine=$1 local m428=$2 local m429=$3 local m430=$4 [[ -n "$machine" ]] || return 0 add_alias "$machine" "$m428" "$m429" "$m430" add_alias "${machine%%(*}" "$m428" "$m429" "$m430" } add_kinematics_aliases() { local kinematics=$1 local m428=$2 local m429=$3 local m430=$4 [[ -n "$kinematics" ]] || return 0 add_alias "$kinematics" "$m428" "$m429" "$m430" local first_word=${kinematics%%[[:space:]]*} add_alias "$first_word" "$m428" "$m429" "$m430" add_alias "${first_word%_kins}" "$m428" "$m429" "$m430" add_alias "${first_word%-kins}" "$m428" "$m429" "$m430" add_alias "$(printf '%s' "$first_word" | tr '_' '-')" "$m428" "$m429" "$m430" add_alias "$(printf '%s' "${first_word%_kins}" | tr '_' '-')" "$m428" "$m429" "$m430" add_alias "$(printf '%s' "${first_word%-kins}" | tr '_' '-')" "$m428" "$m429" "$m430" } add_halfile_aliases() { local halfile=$1 local m428=$2 local m429=$3 local m430=$4 [[ -n "$halfile" ]] || return 0 [[ "$halfile" == LIB:* ]] && return 0 add_alias "$halfile" "$m428" "$m429" "$m430" local hal_base=${halfile##*/} add_alias "$hal_base" "$m428" "$m429" "$m430" add_alias "${hal_base%.hal}" "$m428" "$m429" "$m430" } while IFS=: read -r group path _note; do [[ "$group" == "config" && "$path" == *.ini ]] || continue config="$linuxcnc_root/$path" [[ -f "$config" ]] || { echo "missing LinuxCNC config source: $path" >&2 exit 1 } if ! grep -Eiq 'REMAP[[:space:]]*=[[:space:]]*M(428|429|430)([^0-9]|$)' "$config"; then continue fi m428=$(kinstype_for_mcode "$config" 428) m429=$(kinstype_for_mcode "$config" 429) m430=$(kinstype_for_mcode "$config" 430) [[ -n "$m428" ]] || m428=-1 [[ -n "$m429" ]] || m429=-1 [[ -n "$m430" ]] || m430=-1 machine=$(ini_value "$config" "MACHINE") kinematics=$(ini_value "$config" "KINEMATICS") halfiles=$(ini_values "$config" "HALFILE") config_dir=${path%/*} config_base=${path##*/} config_stem=${config_base%.ini} add_alias "$path" "$m428" "$m429" "$m430" add_alias "$config_dir" "$m428" "$m429" "$m430" add_alias "${config_dir##*/}" "$m428" "$m429" "$m430" add_alias "$config_stem" "$m428" "$m429" "$m430" add_machine_aliases "$machine" "$m428" "$m429" "$m430" add_kinematics_aliases "$kinematics" "$m428" "$m429" "$m430" while IFS= read -r halfile; do add_halfile_aliases "$halfile" "$m428" "$m429" "$m430" done <<<"$halfiles" for mcode in 428 429 430; do if remap_source=$(remap_source_for_mcode "$config" "$mcode"); then remap_dir=${remap_source%/*} add_alias "$remap_dir" "$m428" "$m429" "$m430" add_alias "${remap_dir%/*}" "$m428" "$m429" "$m430" add_alias "${remap_dir%/remap_subs}" "$m428" "$m429" "$m430" remap_parent=${remap_dir%/remap_subs} add_alias "${remap_parent##*/}" "$m428" "$m429" "$m430" fi done done <"$manifest" cat <<'EOF' // Generated by ./generate-linuxcnc-switchkins-remap-table.sh. // Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/SUBROUTINE_PATH/REMAP entries and // adjacent remap_subs/{428,429,430}remap.ngc # assignments. EOF printf '%s\n' "${entries[@]}"