diff options
author | Case Duckworth | 2023-03-08 22:29:41 -0600 |
---|---|---|
committer | Case Duckworth | 2023-03-08 22:29:41 -0600 |
commit | 412ce1790ae88adb20057364a96d11cb92ce9f34 (patch) | |
tree | a6a3722c39f2a523a9b3f32abf0da9c26ea79779 | |
parent | Detect getline() errors (diff) | |
download | shin-412ce1790ae88adb20057364a96d11cb92ce9f34.tar.gz shin-412ce1790ae88adb20057364a96d11cb92ce9f34.zip |
Add shin.sh (sh library; acts a little differently to shin.awk/shin)
-rwxr-xr-x | shin.sh | 119 |
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 | |||
101 | shin() { | ||
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 | } | ||