diff --git a/README.md b/README.md
index 99a3130..f36efcf 100644
--- a/README.md
+++ b/README.md
@@ -71,6 +71,16 @@ cargo install dcr
curl -fsSL https://dcr.dexoron.su/install.sh | bash
```
+
+
+
+| BSD (script) |
+
+
+```sh
+curl -fsSL https://raw.githubusercontent.com/dexoron/dcr/dev/install_bsd.sh | bash
+```
+
|
@@ -159,9 +169,9 @@ other in [docs/](docs/) and `dcr --help`
| Linux | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ |
| macOS | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Windows | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
-| FreeBSD | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ❌ | ❌ | ❌ |
-| OpenBSD | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ❌ | ❌ | ❌ |
-| NetBSD | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ❌ | ❌ | ❌ |
+| FreeBSD | ✅ | ⚠️ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
+| OpenBSD | ✅ | ⚠️ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
+| NetBSD | ✅ | ⚠️ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
✅ officially supported · ⚠️ community / best-effort · ❌ not supported
diff --git a/install_bsd.sh b/install_bsd.sh
new file mode 100755
index 0000000..46cb818
--- /dev/null
+++ b/install_bsd.sh
@@ -0,0 +1,224 @@
+#!/usr/bin/env bash
+set -Eeuo pipefail
+
+TMPDIR="/tmp/dcr-install"
+INSTALL_PATH="$HOME/.local/share/dcr"
+BINPATH="$HOME/.local/bin"
+LOGFILE="$HOME/.cache/dcr-install.log"
+REPO_URL="https://github.com/dexoron/dcr"
+GITHUB_API_LATEST="https://api.github.com/repos/dexoron/dcr/releases/latest"
+GITHUB_API_ALL="https://api.github.com/repos/dexoron/dcr/releases"
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m'
+
+mkdir -p "$(dirname "$LOGFILE")"
+exec > >(tee -a "$LOGFILE") 2>&1
+
+log() { echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"; }
+success() { echo -e "${GREEN}✔ $1${NC}"; }
+warn() { echo -e "${YELLOW}⚠ $1${NC}"; }
+error() { echo -e "${RED}✖ $1${NC}"; }
+
+trap 'error "Error on line $LINENO"; exit 1' ERR
+
+INSTALL_MODE=""
+CHANNEL=""
+
+check_os() {
+ case "$(uname -s)" in
+ FreeBSD|OpenBSD|NetBSD|DragonFly) ;;
+ *) error "Only BSD systems (FreeBSD, OpenBSD, NetBSD, DragonFly) are supported by this script"; exit 1 ;;
+ esac
+}
+
+detect_target() {
+ local os arch
+ os="$(uname -s)"
+ arch="$(uname -m)"
+
+ case "$os:$arch" in
+ FreeBSD:x86_64|FreeBSD:amd64) TARGET_TRIPLE="x86_64-unknown-freebsd" ;;
+ OpenBSD:x86_64|OpenBSD:amd64) TARGET_TRIPLE="x86_64-unknown-openbsd" ;;
+ NetBSD:x86_64|NetBSD:amd64) TARGET_TRIPLE="x86_64-unknown-netbsd" ;;
+ DragonFly:x86_64|DragonFly:amd64) TARGET_TRIPLE="x86_64-unknown-dragonfly" ;;
+ *) error "Unsupported BSD platform: $os/$arch"; exit 1 ;;
+ esac
+}
+
+check_common_dependencies() {
+ command -v curl >/dev/null 2>&1 || { error "curl is not installed"; exit 1; }
+ if [[ "$CHANNEL" == "dev" ]]; then
+ if ! command -v python3 >/dev/null 2>&1 && ! command -v jq >/dev/null 2>&1; then
+ error "For dev installations, either 'python3' or 'jq' must be installed to parse GitHub API response."
+ exit 1
+ fi
+ fi
+}
+
+check_build_dependencies() {
+ command -v git >/dev/null 2>&1 || { error "git is not installed"; exit 1; }
+ command -v cargo >/dev/null 2>&1 || { error "cargo is not installed"; exit 1; }
+}
+
+select_channel() {
+ echo "Choose channel:"
+ echo " 1) Latest stable release (default)"
+ echo " 2) Latest dev (pre-release)"
+ read -r -p "Enter 1 or 2 [1]: " choice
+
+ case "${choice:-1}" in
+ 1) CHANNEL="stable" ;;
+ 2) CHANNEL="dev" ;;
+ *) error "Unknown option"; exit 1 ;;
+ esac
+}
+
+select_install_mode() {
+ echo "Choose installation mode:"
+ echo " 1) Download prebuilt binary from GitHub Release (recommended)"
+ echo " 2) Build from git"
+ read -r -p "Enter 1 or 2 [1]: " choice
+
+ case "${choice:-1}" in
+ 1) INSTALL_MODE="release" ;;
+ 2) INSTALL_MODE="build" ;;
+ *) error "Unknown option"; exit 1 ;;
+ esac
+}
+
+fetch_release_json() {
+ if [[ "$CHANNEL" == "dev" ]]; then
+ log "Looking for latest dev (pre-release)..."
+ local json result
+ json="$(curl -fsSL "$GITHUB_API_ALL")"
+ if command -v jq >/dev/null 2>&1; then
+ result="$(printf '%s' "$json" | jq -e -c 'map(select(.prerelease == true))[0]')"
+ else
+ result="$(printf '%s' "$json" | python3 - <<'EOF'
+import sys, json
+releases = json.load(sys.stdin)
+pre = [r for r in releases if r.get("prerelease", False)]
+if not pre:
+ print("{}", end="")
+else:
+ print(json.dumps(pre[0]), end="")
+EOF
+)"
+ fi
+
+ if [[ -z "$result" || "$result" == "{}" || "$result" == "null" ]]; then
+ error "No dev (pre-release) found on GitHub"
+ exit 1
+ fi
+ printf '%s' "$result"
+ else
+ curl -fsSL "$GITHUB_API_LATEST"
+ fi
+}
+
+download_binary() {
+ local release_json tag version asset_name download_url
+
+ release_json="$(fetch_release_json)"
+
+ tag="$(printf '%s\n' "$release_json" | \
+ sed -n 's/.*"tag_name": "\([^"]*\)".*/\1/p' | head -n1)"
+ if [[ -z "$tag" ]]; then
+ error "Failed to determine release version"
+ exit 1
+ fi
+
+ version="${tag#v}"
+ asset_name="dcr-${TARGET_TRIPLE}-${version}"
+
+ log "Fetching release ${tag} (channel: ${CHANNEL})..."
+
+ download_url="$(printf '%s\n' "$release_json" | \
+ sed -n "s#.*\"browser_download_url\": \"\([^\"]*/${asset_name}\)\".*#\1#p" | head -n1)"
+
+ if [[ -z "$download_url" ]]; then
+ error "Asset ${asset_name} not found in release ${tag}"
+ exit 1
+ fi
+
+ mkdir -p "$INSTALL_PATH"
+ curl -fL "$download_url" -o "$INSTALL_PATH/dcr"
+ chmod +x "$INSTALL_PATH/dcr"
+ success "Binary ${asset_name} downloaded (${tag})"
+}
+
+prepare_sources() {
+ log "Fetching sources..."
+ rm -rf "$TMPDIR"
+ git clone --depth 1 "$REPO_URL" "$TMPDIR"
+ if [[ "$CHANNEL" == "dev" ]]; then
+ git clone --depth 1 --branch dev "$REPO_URL" "$TMPDIR" 2>/dev/null || \
+ git clone --depth 1 "$REPO_URL" "$TMPDIR"
+ fi
+ success "Sources fetched"
+}
+
+build_binary() {
+ log "Building release binary..."
+ (cd "$TMPDIR" && cargo build --release)
+ success "Build completed"
+}
+
+install_built_binary() {
+ mkdir -p "$INSTALL_PATH"
+ cp "$TMPDIR/target/release/dcr" "$INSTALL_PATH/dcr"
+ chmod +x "$INSTALL_PATH/dcr"
+ success "Binary installed from source"
+}
+
+install_link() {
+ log "Creating symlink..."
+ mkdir -p "$BINPATH"
+ ln -sf "$INSTALL_PATH/dcr" "$BINPATH/dcr"
+ success "Command 'dcr' added to $BINPATH"
+}
+
+check_path() {
+ if ! echo "$PATH" | grep -q "$BINPATH"; then
+ warn "Directory $BINPATH not found in PATH"
+ echo "Add this to ~/.bashrc, ~/.zshrc or ~/.profile:"
+ echo "export PATH=\"$BINPATH:\$PATH\""
+ fi
+}
+
+cleanup() {
+ rm -rf "$TMPDIR" 2>/dev/null || true
+}
+
+main() {
+ log "Starting DCR installation for BSD"
+
+ check_os
+ select_channel
+ check_common_dependencies
+ detect_target
+ select_install_mode
+ cleanup
+
+ if [[ "$INSTALL_MODE" == "build" ]]; then
+ check_build_dependencies
+ prepare_sources
+ build_binary
+ install_built_binary
+ else
+ download_binary
+ fi
+
+ install_link
+ check_path
+ cleanup
+
+ success "Installation completed successfully"
+ log "Log saved to $LOGFILE"
+}
+
+main "$@"