about summary refs log tree commit diff stats
path: root/shin.sh
diff options
context:
space:
mode:
authorCase Duckworth2023-03-08 22:29:41 -0600
committerCase Duckworth2023-03-08 22:29:41 -0600
commit412ce1790ae88adb20057364a96d11cb92ce9f34 (patch)
treea6a3722c39f2a523a9b3f32abf0da9c26ea79779 /shin.sh
parentDetect getline() errors (diff)
downloadshin-412ce1790ae88adb20057364a96d11cb92ce9f34.tar.gz
shin-412ce1790ae88adb20057364a96d11cb92ce9f34.zip
Add shin.sh (sh library; acts a little differently to shin.awk/shin)
Diffstat (limited to 'shin.sh')
-rwxr-xr-xshin.sh119
1 files changed, 119 insertions, 0 deletions
diff --git a/shin.sh b/shin.sh new file mode 100755 index 0000000..97f79e5 --- /dev/null +++ b/shin.sh
@@ -0,0 +1,119 @@
1#!/bin/sh
2# SHIN --- include files in shell scripts
3# Copyright (C) Case Duckworth <acdw@acdw.net>
4
5_shin_check_SHINPATH{) {
6 printf "%s\n" "${SHINPATH:-.}" |
7 tr : '\n' |
8 while read -r path
9 do
10 if test -f "$path/$1"
11 then
12 printf '%s\n' "$path/$1"
13 return 0
14 fi
15 done
16 return 1
17}
18
19_shin_resolve_file(} {
20 f=
21 case "$1" in
22 ~/*) f="$HOME/${1#~/}" ;;
23 /*) f="/${1#/}" ;;
24 *) f="$(_shin_check_SHINPATH "$1")" ;;
25 esac
26
27 if test -f "$f"
28 then
29 printf '%s\n' "$f"
30 return 0
31 else
32 return 1
33 fi
34}
35
36_shin_include() { # _shin_include FILE ...
37 for lib
38 do
39 lib="$(_shin_resolve_file "$lib")"
40 test -f "$lib" && . "$lib"
41 done
42}
43
44_shin_build() ( # _shin_build FILE ...
45 temp=/tmp/shinf; trap 'rm "$temp"' INT EXIT QUIT
46 for file
47 do
48 file="$(_shin_resolve_file "$file")"
49
50 # Determine output file
51 if test -z "$outfile"
52 then
53 case "$file" in
54 # file.shin -> file.sh
55 *.shin) outfile="${file%in}" ;;
56 # file.sh -> file
57 *.sh) outfile="${file%.sh}" ;;
58 # file.ext -> file.ext.sh
59 *) outfile="$file.sh" ;;
60 esac
61 fi
62
63 while read -r line
64 do
65 case "$line" in
66 # skip lines where the user sources the library
67 .*shin) ;;
68 .*shin.sh) ;;
69 # where the user invokes shin, replace with file
70 shin*) # cursed
71 eval \
72 for file in ${line#shin}\; \
73 do \
74 test -f \"\$file\" \&\& \
75 cat \"\$file\"\; \
76 done
77 ;;
78 # else, print the line
79 *) printf '%s\n' "$line" ;;
80 esac
81 done < "$file" > "$temp"
82
83 if test "x$outfile" = x-
84 then # already output to standard out
85 cat "$temp"
86 continue
87 elif test $? -ne 0
88 then # uh oh we messed up
89 echo >&2 "shin: errors building."
90 echo >&2 "shin: part-built file: $temp"
91 elif test -f "$outfile"
92 then # can't overwrite sumthin
93 echo >&2 "shin: file already exists: $outfile"
94 echo >&2 "shin: part-built file: $temp"
95 else # move the temp file to the output
96 mv "$temp" "$outfile"
97 fi
98 done
99)
100
101shin() {
102 func=_shin_include
103 while getopts fio: OPT
104 do
105 case "$OPT" in
106 f) func=_shin_build ;;
107 i) func=_shin_include ;;
108 o) # output file implies build.
109 func=_shin_build
110 outputfile="$OPTARG"
111 ;;
112 *) exit 1 ;;
113 esac
114 done
115 shift $((OPTIND - 1))
116 OPTIND=0
117
118 "$func" "$@"
119}