#!/bin/sh # SHIN --- include files in shell scripts # Copyright (C) Case Duckworth _shin_check_SHINPATH() { printf "%s\n" "${SHINPATH:-.}" | tr : '\n' | while read -r path do if test -f "$path/$1" then printf '%s\n' "$path/$1" return 0 fi done return 1 } _shin_resolve_file() { f= case "$1" in ~/*) f="$HOME/${1#~/}" ;; /*) f="/${1#/}" ;; *) f="$(_shin_check_SHINPATH "$1")" ;; esac if test -f "$f" then printf '%s\n' "$f" elif test -f "$f.sh" then printf '%s\n' "$f.sh" else return 1 fi return 0 } _shin_include() { # _shin_include FILE ... for lib do lib="$(_shin_resolve_file "$lib")" test -f "$lib" && . "$lib" done } _shin_cleanup() { if "$_shin_rmtemp" && test -f "$_shin_temp" then rm -f "$_shin_temp" fi } _shin_build() ( # _shin_build FILE ... for file do file="$(_shin_resolve_file "$file")" # Determine output file if test -z "$_shin_output" then case "$file" in # file.shin -> file.sh *.shin) _shin_output="${file%in}" ;; # file.sh -> file *.sh) _shin_output="${file%.sh}" ;; # file.ext -> file.ext.sh *) _shin_output="$file.sh" ;; esac fi while read -r line do case "$line" in # skip lines where the user sources the library .*shin) ;; .*shin.sh) ;; # where the user invokes shin, replace with file shin*) # cursed eval \ for file in ${line#shin}\; \ do \ test -f \"\$file\" \&\& \ sed \"/^#!/d\" \"\$file\"\; \ done ;; # else, print the line *) printf '%s\n' "$line" ;; esac done < "$file" > "$_shin_temp" ec=$? if test "x$_shin_output" = x- then # already output to standard out cat "$_shin_temp" continue elif test $ec -ne 0 then # uh oh we messed up echo >&2 "shin: errors building." echo >&2 "shin: part-built file: $_shin_temp" _shin_rmtemp=false elif test -f "$_shin_output" && ! "$_shin_force" then # can't overwrite sumthin without -f echo >&2 "shin: file already exists: $_shin_output" echo >&2 "shin: part-built file: $_shin_temp" _shin_rmtemp=false else # move the temp file to the output if mv "$_shin_temp" "$_shin_output" then printf >&2 '%s\n' "$_shin_output" else echo >&2 "shin: errors moving $_shin_output" echo >&2 "shin: part-built file: $_shin_temp" _shin_rmtemp=false fi fi done ) shin() { _shin_func="${_shin_func:-_shin_include}" _shin_temp=/tmp/shinf _shin_rmtemp=true _shin_force=false _shin_debug=false trap _shin_cleanup INT EXIT QUIT while getopts dfcio: OPT do case "$OPT" in c) _shin_func=_shin_build ;; i) _shin_func=_shin_include ;; o) # output file implies build. _shin_func=_shin_build _shin_output="$OPTARG" echo "$_shin_output" ;; f) _shin_force=true ;; d) _shin_debug=true ;; *) exit 1 ;; esac done shift $((OPTIND - 1)) OPTIND=0 if "$_shin_debug" then cat >&2 <