#!/bin/sh # termenv - independent native plugin environment for Terminal # This is a POSIX sh environment, not a Linux userland. Packages are # Terminal-compatible .deb files (scripts or iOS/Darwin arm64 binaries). ROOT="${TERM_ROOT:-$HOME/Documents/.terminal}" APPDIR="${APPDIR:-/Applications/Terminal.app}" PROFILE="$ROOT/etc/profile" SOURCES="$ROOT/etc/apt/sources.list" usage() { printf '%s\n' "Usage: termenv " printf '%s\n' "" printf '%s\n' " init Create the independent prefix and the local apt repository" printf '%s\n' " status Show prefix, PATH, apt/dpkg and installed packages" printf '%s\n' " enter Print the command that activates the environment" printf '%s\n' " run Run a command inside the environment" printf '%s\n' " on Add activation to ~/Documents/.profile" printf '%s\n' " off Remove activation from ~/Documents/.profile" printf '%s\n' " selftest init + apt update + install the sample \"hello\" package" printf '%s\n' " clean Remove apt caches and temporary files" printf '%s\n' "" printf '%s\n' "Environment:" printf '%s\n' " TERM_ROOT=~/Documents/.terminal" printf '%s\n' " PREFIX=$TERM_ROOT" } ensure_dirs() { mkdir -p "$ROOT/bin" "$ROOT/sbin" "$ROOT/lib" "$ROOT/include" \ "$ROOT/share/man" "$ROOT/etc/apt" \ "$ROOT/var/lib/dpkg/info" "$ROOT/var/lib/apt/lists" \ "$ROOT/var/cache/apt/archives" "$ROOT/var/log" \ "$ROOT/tmp" "$ROOT/repo/pool" "$ROOT/home" [ -f "$ROOT/var/lib/dpkg/status" ] || : > "$ROOT/var/lib/dpkg/status" } write_profile() { if [ ! -f "$PROFILE" ]; then cat > "$PROFILE" < "$SOURCES" return fi TMP="$ROOT/tmp/sources.$$" awk -v desired="$LOCAL_LINE" ' /^deb .*file:\/\/.*\/\.terminal\/repo/ { print desired; found=1; next } { print } END { if (!found) print desired } ' "$SOURCES" > "$TMP" mv "$TMP" "$SOURCES" if ! grep -qF "https://repo.procurs.us/" "$SOURCES"; then printf '%s\n' "$PROCURUS_LINE" >> "$SOURCES" fi if ! grep -qF "https://repo.havoc.app/" "$SOURCES"; then printf '%s\n' "$HAVOC_LINE" >> "$SOURCES" fi } make_sample() { if grep -q '^Package: hello$' "$ROOT/repo/Packages" 2>/dev/null; then return 0 fi SAMPLE="$ROOT/share/termenv/samples/hello" mkdir -p "$SAMPLE/bin" cat > "$SAMPLE/bin/hello" << 'EOF' #!/bin/sh echo "Hello from the Terminal plugin environment." echo "PREFIX=$PREFIX" echo "TERM_ROOT=$TERM_ROOT" if [ -d "$HOME/Documents/_sandbox" ]; then echo "Sandbox escape: available (see $HOME/Documents/_sandbox)" fi EOF chmod +x "$SAMPLE/bin/hello" if [ -f "$APPDIR/bin/mkdeb" ]; then sh "$APPDIR/bin/mkdeb" hello 1.0.0 "$SAMPLE" iphoneos-arm64 echo "termenv: mkdeb exit=$?" fi } do_init() { echo "termenv: creating directories..." ensure_dirs echo "termenv: writing profile/sources..." write_profile write_sources [ -f "$ROOT/repo/Packages" ] || : > "$ROOT/repo/Packages" echo "termenv: creating sample package..." make_sample echo "termenv: sample ready" echo "Prefix: $ROOT" echo "Profile: $PROFILE" echo "Run: source \"$PROFILE\" (or: termenv on)" } do_status() { ensure_dirs echo "Terminal native plugin environment" echo " prefix: $ROOT" echo " profile: $PROFILE" echo " apt: $APPDIR/bin/apt" echo " dpkg: $APPDIR/bin/dpkg" echo " sources: $SOURCES" if [ -f "$SOURCES" ]; then sed 's/^/ /' "$SOURCES" fi echo " packages:" if [ -s "$ROOT/var/lib/dpkg/status" ]; then awk '/^Package: /{printf " %s", substr($0,10)} /^Version: /{printf " %s", substr($0,10)} /^Status: /{status=substr($0,9)} /^$/{if(status ~ /installed/) print ""}' "$ROOT/var/lib/dpkg/status" else echo " (none)" fi echo " sandbox: $HOME/Documents/_sandbox" } do_run() { if [ $# -eq 0 ]; then echo "usage: termenv run " >&2 exit 2 fi if [ -f "$PROFILE" ]; then . "$PROFILE" fi "$@" } do_on() { do_init PROFILE_FILE="$HOME/Documents/.profile" MARK_BEGIN="# >>> Terminal plugin environment >>>" MARK_END="# <<< Terminal plugin environment <<<" if grep -qF "$MARK_BEGIN" "$PROFILE_FILE" 2>/dev/null; then echo "Activation already present in $PROFILE_FILE" return 0 fi { echo "$MARK_BEGIN" echo "source \"\$HOME/Documents/.terminal/etc/profile\"" echo "$MARK_END" } >> "$PROFILE_FILE" echo "Activation added to $PROFILE_FILE" } do_off() { PROFILE_FILE="$HOME/Documents/.profile" if [ ! -f "$PROFILE_FILE" ]; then echo "No $PROFILE_FILE" return 0 fi TMP="$ROOT/tmp/profile.$$" awk ' /# >>> Terminal plugin environment >>>/ {skip=1; next} /# <<< Terminal plugin environment << "$TMP" mv "$TMP" "$PROFILE_FILE" echo "Activation removed from $PROFILE_FILE" } do_clean() { rm -rf "$ROOT/tmp"/* rm -f "$ROOT/var/cache/apt/archives/"*.deb echo "Cleaned caches" } do_selftest() { do_init . "$PROFILE" echo "=== apt update ===" APT_SKIP_REMOTE=1 sh "$APPDIR/bin/apt" update echo "=== apt install hello ===" sh "$APPDIR/bin/apt" install hello echo "=== hello ===" hello echo "=== installed packages ===" sh "$APPDIR/bin/apt" list --installed echo "=== dpkg -l ===" sh "$APPDIR/bin/dpkg" -l } case "${1:-}" in init) do_init ;; status) do_status ;; enter) echo "source \"$PROFILE\"" ;; run) shift; do_run "$@" ;; on) do_on ;; off) do_off ;; clean) do_clean ;; selftest) do_selftest ;; ""|-h|--help|help) usage ;; *) usage; exit 2 ;; esac