#!/bin/sh # # Shell command language linker # # Copyright (C) 2015 Patrick "P. J." McDermott # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . set -u VERSION='0.2.0' die() { local fmt="${1}" shift 1 printf "shld: ${fmt}\n" "${@}" >&2 exit 2 } link() { local make_executable="${1}" local interpreter="${2}" local entry_point="${3}" local output="${4}" shift 4 local input= # Open output file. if ! exec 3>"${output}~"; then die 'Cannot open file "%s"' "${output}~" fi # Write magic number and interpreter path. if ${make_executable}; then printf '#!%s\n' "${interpreter}" >&3 fi # Write __init() function. cat >&3 <<-'EOF' __init_funcs='' __init() { __init_funcs="${__init_funcs} ${1}" } EOF # Read input files. for input in "${@}"; do if ! cat "${input}" >&3; then die 'Cannot read file "%s"' "${input}" fi done # Add call to init functions. cat >&3 <<-'EOF' for __func in ${__init_funcs}; do ${__func} done EOF # Add call to entry point. if ${make_executable}; then printf '%s "${@}"\n' "${entry_point}" >&3 fi # Close output file, make it executable, and set its name. exec 3>&- if ${make_executable}; then if ! chmod a+x "${output}~"; then die 'Cannot set mode of file "%s"' "${output}~" fi fi if ! mv "${output}~" "${output}"; then die 'Cannot rename file to "%s"' "${output}" fi } usage() { printf 'Usage: %s [option ...] ...\n' "${0}" } help() { usage cat < Make an executable and use as the interpreter for your program, instead of the default of "/bin/sh" -e Make an executable and use as the function for beginning execution of your program, instead of the default of "main" -o Use as the name of the program produced by shld, instead of the default of "out.sh" EOF } version() { cat <. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. EOF } main() { local opt= local make_executable=false local interpreter='/bin/sh' local entry_point='main' local output='out.sh' while getopts 'hVI:e:o:' opt; do case "${opt}" in 'h') help exit ;; 'V') version exit ;; 'I') interpreter="${OPTARG}" make_executable=true ;; 'e') entry_point="${OPTARG}" make_executable=true ;; 'o') output="${OPTARG}" ;; esac done shift $(($OPTIND - 1)) if [ ${#} -lt 1 ]; then usage >&2 exit 1 fi link "${make_executable}" "${interpreter}" "${entry_point}" \ "${output}" "${@}" } main "${@}"