about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--COPYING9
-rw-r--r--Makefile31
-rw-r--r--README.md36
-rwxr-xr-xshin67
-rwxr-xr-xshin.awk60
5 files changed, 203 insertions, 0 deletions
diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..0520203 --- /dev/null +++ b/COPYING
@@ -0,0 +1,9 @@
1Copyright (C) 2022 Case Duckworth <acdw@acdw.net>
2
3Usage of the works is permitted provided that this instrument is
4retained with the works, so that any entity that uses the works is
5notified of this instrument.
6
7DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
8
9
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a9401a1 --- /dev/null +++ b/Makefile
@@ -0,0 +1,31 @@
1NAME = shin
2DESC = Include shell scripts in other shell scripts
3
4DESTDIR =
5PREFIX = /usr/local
6
7BIN = $(DESTDIR)$(PREFIX)/bin/$(NAME)
8
9.PHONY: help
10help:
11 @echo "$(NAME) : $(DESC)"
12 @echo "(C) 2022 Case Duckworth <acdw@acdw.net>"
13 @echo "Licensed under the Fair License; see COPYING for details."
14 @echo
15 @echo "TARGETS:"
16 @echo " install Install $(NAME) to $(BIN)."
17 @echo " link Install $(NAME) using symlinks."
18 @echo " Probably only useful for development."
19 @echo " uninstall Uninstall $(NAME)-related files."
20
21.PHONY: install
22install: $(NAME)
23 install -D $< $(BIN)
24
25.PHONY: link
26link:
27 ln -sf $(PWD)/$(NAME) $(BIN)
28
29.PHONY: uninstall
30uninstall:
31 rm $(BIN)
diff --git a/README.md b/README.md new file mode 100644 index 0000000..4b68c96 --- /dev/null +++ b/README.md
@@ -0,0 +1,36 @@
1# shin
2## include shell files
3
4built to scratch a personal itch. SHIN is an awk(1) script that plops include
5files in to .shin files. here's the usage text:
6
7```
8SHIN: include shell files in other shell files
9USAGE: shin FILE...
10
11FILEs named FILE.shin will be built to FILE.sh in the same directory.
12to include files in shin files, use the following comment syntax:
13
14#< INCLUDE
15
16shin will add INCLUDE below that comment, as well as a comment
17denoting the end of INCLUDE.
18```
19
20it's really that simple. and stupid.
21
22## configuration
23
24SHIN will look for include files in the current directory, unless you set the
25SHINPATH environment variable. $SHINPATH is a colon-separated list of
26directories like $PATH or $MANPATH.
27
28## extras
29
30`shin.awk` is the plain awk script. because awk is dumb about handling
31arguments, `shin` is a shell script that will do `shin.awk`'s work unless you
32pass `shin -h`, in which case it will print a helpful message.
33
34## license
35
36SHIN is licensed under the FAIR license. see COPYING for details.
diff --git a/shin b/shin new file mode 100755 index 0000000..b966091 --- /dev/null +++ b/shin
@@ -0,0 +1,67 @@
1#!/bin/sh
2# SHIN v. 0/1
3# Copyright (C) Case Duckworth <acdw@acdw.net>
4# Licensed under the Fair License. See COPYING for details.
5
6_shin() {
7 awk 'BEGIN {
8 if (ENVIRON["SHINPATH"]) split(ENVIRON["SHINPATH"], SHINPATH, ":")
9 else SHINPATH[1] = "."
10}
11FNR == 1 { outfile = FILENAME; sub(/in$/, "", outfile) }
12{ print($0) > outfile }
13/^#</ {
14 inclfile = shin_resolve(substr($0, 3))
15 while (getline l < inclfile) print(l) > outfile
16 close(inclfile)
17 sub(/</, ">", $0)
18 print > outfile
19}
20function shin_test(filename) {
21 if (! system("test -f \"" f "\"")) return filename
22 print("Cannot find \"" filename "\" in " sp) > (STDERR ? STDERR : "/dev/stderr")
23 exit 1
24}
25function shin_resolve(filename) {
26 if (match(filename, "^/")) return shin_test(filename)
27 if (match(filename, "^~")) return shin_test(ENVIRON["HOME"] "/" substr(filename, 2))
28 sub(/^[ \t]*/, "", filename)
29 sub(/[ \t]*$/, "", filename)
30 sp = ""
31 for (p in SHINPATH) {
32 sp = sp (sp ? ", " : "") "\"" SHINPATH[p] "\""
33 f = SHINPATH[p] "/" filename
34 gsub("//", "/", f)
35 return shin_test(f)
36 }
37}'
38}
39
40if [ "x$1" = -h ]; then
41 cat <<\EOF
42SHIN: include shell files in other shell files
43USAGE: shin [-h] FILE...
44
45FLAGS:
46 -h Show this help and exit.
47
48PARAMETERS:
49 FILE... Input files. FILEs named FILE.shin will be built to
50 FILE.sh in the same directory. To include files in
51 shin files, use the following comment syntax:
52
53 #< INCLUDE
54
55 If INCLUDE begins with / or ~, it's taken as a literal
56 file name; otherwise SHINPATH will be searched for the
57 file. SHINPATH is a colon-separated list of paths
58 like $PATH. If SHINPATH is unset, it defaults to the
59 current directory.
60
61 If INCLUDE is not found, shin will error and quit.
62 Otherwise, shin will add INCLUDE below that comment,
63 as well as a comment denoting the end of the INCLUDE.
64EOF
65else
66 _shin "$@"
67fi
diff --git a/shin.awk b/shin.awk new file mode 100755 index 0000000..80c76aa --- /dev/null +++ b/shin.awk
@@ -0,0 +1,60 @@
1#!/usr/bin/awk -f
2# SHIN: include files in shell scripts
3# by Case Duckworth <acdw@acdw.net>
4# usage: shin -- FILE.shin...
5# each FILE.shin will output to FILE.sh in the same directory
6BEGIN {
7 if (ENVIRON["SHINPATH"]) {
8 split(ENVIRON["SHINPATH"], SHINPATH, ":")
9 } else {
10 SHINPATH[1] = "."
11 }
12}
13
14FNR == 1 {
15 outfile = FILENAME
16 sub(/in$/, "", outfile)
17}
18
19{
20 print($0) > outfile
21}
22
23/^#</ {
24 inclfile = shin_resolve(substr($0, 3))
25 while (getline l < inclfile) {
26 print(l) > outfile
27 }
28 close(inclfile)
29 sub(/</, ">", $0)
30 print > outfile
31}
32
33
34function shin_resolve(filename)
35{
36 if (match(filename, "^/")) {
37 return shin_test(filename)
38 }
39 if (match(filename, "^~")) {
40 return shin_test(ENVIRON["HOME"] "/" substr(filename, 2))
41 }
42 sub(/^[ \t]*/, "", filename)
43 sub(/[ \t]*$/, "", filename)
44 sp = ""
45 for (p in SHINPATH) {
46 sp = sp (sp ? ", " : "") "\"" SHINPATH[p] "\""
47 f = SHINPATH[p] "/" filename
48 gsub("//", "/", f)
49 return shin_test(f)
50 }
51}
52
53function shin_test(filename)
54{
55 if (! system("test -f \"" f "\"")) {
56 return filename
57 }
58 print("Cannot find \"" filename "\" in " sp) > (STDERR ? STDERR : "/dev/stderr")
59 exit 1
60}