#!/bin/bash usage() { local self self="$(basename "$0")" warn "usage: $self [-f|--force] [+TEMPLATE] FILE" warn "usage: $self -l|--list" exit 2 } warn() { # # Print warning # echo "$@" >&2 } die() { # # Warn and exit prematurely # warn "$@" exit 3 } lstemplates() { # # Print list of valid templates # echo sed echo pya echo shellfu echo Bash echo Lua echo Perl echo py3 echo Python3 echo Python } #shellcheck disable=SC2016 mktemplate() { # # Print template $Template for $FilePath # case "$Template" in sh) echo "#!/bin/sh" echo '' echo '' ;; bash|Bash) echo "#!/bin/bash" echo '' echo '' ;; sed) echo "#!/bin/sed -f" echo '' echo '' ;; lua|Lua) echo "#!$(which lua)" echo '' ;; pl|Perl) echo "#!$(which perl)" echo '' echo 'use strict;' echo 'use warnings;' echo '' echo '' ;; py|Python) echo "#!$(which python)" echo '' echo "if __name__ == '__main__':" echo ' ' ;; py3|Python3) echo "#!/usr/bin/python3" echo '' echo 'import sys' echo '' echo '' echo 'class UsageError(ValueError):' echo ' pass' echo '' echo '' echo 'def usage():' echo ' self = sys.argv[0]' echo ' raise UsageError("usage: %s [-j|-t|-p] YAML_FILE.." % self)' echo '' echo '' echo 'def main(argv):' echo ' if len(argv) < 2:' echo ' usage()' echo '' echo '' echo 'if __name__ == "__main__":' echo ' main(sys.argv[1:])' ;; pya) echo '#!/usr/bin/python3' echo '"""' echo 'A simple application' echo '"""' echo '' echo 'import sys' echo 'import traceback' echo '' echo '' echo 'class UsageError(ValueError):' echo ' pass' echo '' echo '' echo 'class AppError(RuntimeError):' echo ' pass' echo '' echo '' echo 'class BaseApp:' echo ' """' echo ' Main application class' echo ' """' echo '' echo ' @classmethod' echo ' def main(cls, factory=None):' echo ' factory = factory if factory else cls.from_argv' echo ' try:' echo ' app = factory()' echo ' es = app.run()' echo ' except UsageError as e:' echo ' print(e, file=sys.stderr)' echo ' sys.exit(2)' echo ' except AppError as e:' echo ' print(e, file=sys.stderr)' echo ' sys.exit(3)' echo ' except Exception:' echo ' sys.stderr.write(traceback.format_exc())' echo ' sys.exit(4)' echo ' if type(es) is not int:' echo ' msg = (' echo ' "%s.run() did not return proper exit status: %r is not int"' echo ' % (app.__class__.__name__, es)' echo ' )' echo ' BaseApp.WARN(msg)' echo ' es = 4' echo ' sys.exit(es)' echo '' echo ' @classmethod' echo ' def from_argv(cls):' echo ' return cls(' echo ' name=sys.argv[0],' echo ' args=sys.argv[1:]' echo ' )' echo '' echo ' def __init__(self, name, args):' echo ' self.name = name' echo ' self.args = args' echo '' echo ' def _throw_usage(self, pattern=None):' echo ' """' echo ' Throw UsageError with pattern *pattern*.' echo ' """' echo ' parts = ["usage:", self.name]' echo ' if pattern:' echo ' parts.append(pattern)' echo ' raise UsageError(" ".join(parts))' echo '' echo ' @staticmethod' echo ' def WARN(msg):' echo ' """' echo ' Warn user with message *msg*. *msg* can be string or a list of lines.' echo ' """' echo ' if type(msg) is list:' echo ' lines = msg' echo ' else:' echo ' lines = [str(msg)]' echo ' for line in lines:' echo ' print(line, file=sys.stderr)' echo '' echo ' def _validate(self):' echo ' """' echo ' Validate *self.args*; throw UsageError if needed.' echo ' """' echo ' if not self.args:' echo ' self._throw_usage("ARG..")' echo '' echo ' def run(self):' echo ' """' echo ' Run the application; return integer exit status' echo ' """' echo ' self._validate()' echo ' if self.args[0] == "--fail":' echo ' raise AppError("you asked for it")' echo ' if self.args[0] == "--fail-unexpectedly":' echo ' raise RuntimeError("you asked for it ANyway")' echo ' if self.args[0] == "--warn":' echo ' BaseApp.WARN("you asked for a warning")' echo ' return 0' echo ' if self.args[0] == "--badret":' echo ' return object()' echo ' return 0' echo '' echo '' echo 'class App(BaseApp):' echo '' echo ' def _validate(self):' echo ' """' echo ' Validate *self.args*; throw UsageError as needed.' echo '' echo ' Prefer using self._throw_usage().' echo ' """' echo ' App.WARN("You need to rewrite App._validate()!!!")' echo ' super()._validate()' echo '' echo ' def run(self):' echo ' """' echo ' Run the application; return integer exit status' echo ' """' echo ' App.WARN("You need to rewrite App.run()")' echo ' super().run()' echo '' echo '' echo 'if __name__ == "__main__":' echo ' App.main()' ;; shellfu) echo "#!/bin/bash" echo "" echo '#shellcheck disable=SC1090' echo '. "$(sfpath)" || exit 3' echo '' echo 'shellfu import pretty' echo '' echo 'usage() {' echo ' mkusage "$@" "[-d] [-v] ARG..."' echo '}' echo '' echo 'main() {' echo ' while true; do case $1 in' echo ' -d) PRETTY_DEBUG=true; shift ;;' echo ' -v) PRETTY_VERBOSE=true; shift ;;' echo ' -*) usage -w "unknown argument: $1" ;;' echo ' *) break ;;' echo ' esac done' echo '}' echo '' echo 'main "$@"' ;; *) die "unknown template or language: $Template" ;; esac } guess_language() { # # Guess intended language from filepath $FilePath # local fname # file name fname="$(basename "$FilePath")" case "$fname" in *.*) echo "${fname##*.}" ;; *) echo "$MKX_DEFAULT_LANG" ;; esac } # # Default language # MKX_DEFAULT_LANG=${MKX_DEFAULT_LANG:-sh} main() { local FilePath # destination file path local Template # built-in template name local careful # ignore existing file careful=true while true; do case $1 in -f|--force) careful=false; shift ;; -l|--list) lstemplates; exit 0 ;; -*) usage ;; +*) Template="${1:1}"; shift ;; *) break ;; esac done FilePath="$1" test -n "$FilePath" || usage test -f "$FilePath" && $careful && die "file already exists: $FilePath" test -n "$Template" || Template=$(guess_language) mktemplate > "$FilePath" chmod +x "$FilePath" } main "$@"