diff options
Diffstat (limited to 'examples/scripts.noah')
-rw-r--r-- | examples/scripts.noah/PERMISSION | 29 | ||||
-rw-r--r-- | examples/scripts.noah/README | 24 | ||||
-rw-r--r-- | examples/scripts.noah/aref.bash | 44 | ||||
-rw-r--r-- | examples/scripts.noah/bash.sub.bash | 28 | ||||
-rw-r--r-- | examples/scripts.noah/bash_version.bash | 42 | ||||
-rw-r--r-- | examples/scripts.noah/meta.bash | 37 | ||||
-rw-r--r-- | examples/scripts.noah/mktmp.bash | 66 | ||||
-rw-r--r-- | examples/scripts.noah/number.bash | 185 | ||||
-rw-r--r-- | examples/scripts.noah/prompt.bash | 40 | ||||
-rw-r--r-- | examples/scripts.noah/remap_keys.bash | 71 | ||||
-rw-r--r-- | examples/scripts.noah/require.bash | 182 | ||||
-rw-r--r-- | examples/scripts.noah/send_mail.bash | 140 | ||||
-rw-r--r-- | examples/scripts.noah/shcat.bash | 49 | ||||
-rw-r--r-- | examples/scripts.noah/source.bash | 63 | ||||
-rw-r--r-- | examples/scripts.noah/string.bash | 226 | ||||
-rw-r--r-- | examples/scripts.noah/stty.bash | 64 | ||||
-rw-r--r-- | examples/scripts.noah/y_or_n_p.bash | 78 |
17 files changed, 1368 insertions, 0 deletions
diff --git a/examples/scripts.noah/PERMISSION b/examples/scripts.noah/PERMISSION new file mode 100644 index 0000000..f415c48 --- /dev/null +++ b/examples/scripts.noah/PERMISSION @@ -0,0 +1,29 @@ +From friedman@cli.com Thu May 25 12:19:06 1995 +Flags: 10 +Return-Path: friedman@cli.com +Received: from po.cwru.edu (root@po.CWRU.Edu [129.22.4.2]) by odin.INS.CWRU.Edu with ESMTP (8.6.10+cwru/CWRU-2.1-ins) + id MAA08685; Thu, 25 May 1995 12:19:05 -0400 (from friedman@cli.com for <chet@odin.INS.CWRU.Edu>) +Received: from cli.com (cli.com [192.31.85.1]) by po.cwru.edu with SMTP (8.6.10+cwru/CWRU-2.3) + id MAA11299; Thu, 25 May 1995 12:19:00 -0400 (from friedman@cli.com for <chet@po.cwru.edu>) +Received: from tepui.cli.com by cli.com (4.1/SMI-4.1) + id AA27213; Thu, 25 May 95 11:18:25 CDT +Received: by tepui.cli.com (4.1) id AA16031; Thu, 25 May 95 11:18:23 CDT +Message-Id: <9505251618.AA16031@tepui.cli.com> +From: friedman@gnu.ai.mit.edu (Noah Friedman) +To: chet@po.cwru.edu +Subject: Bash scripts +Reply-To: friedman@gnu.ai.mit.edu +In-Reply-To: <chet@odin.ins.cwru.edu> Thu, 25 May 1995 11:19:59 -0400 +References: <9505251519.AA06424.SM@odin.INS.CWRU.Edu> +Date: Thu, 25 May 95 11:18:21 CST + +>Hi. I snagged some of your bash functions from your home directory on +>the FSF machines (naughty, I know), and I was wondering if you'd let +>me distribute them with bash-2.0. Thanks. + +Sure. I think there's a later copy in +~ftp/friedman/shell-inits/init-4.89.tar.gz. There are also some elisp and +es frobs in that file. + +It should serve as a pretty good example of how to get carried away. :-) + diff --git a/examples/scripts.noah/README b/examples/scripts.noah/README new file mode 100644 index 0000000..a33860b --- /dev/null +++ b/examples/scripts.noah/README @@ -0,0 +1,24 @@ +This collection of scripts was originally written for older versions +of bash by Noah Friedman (friedman@gnu.ai.mit.edu). The conversion +to bash v2 syntax was done by Chet Ramey. + +These scripts are as-is; there is no copyright associated with +any of them. They exist simply as examples of bash scripting. + +Here's a description of what's in this directory: + +aref.bash pseudo-arrays and substring indexing examples +bash.sub.bash library functions used by require.bash +bash_version.bash a function to slice up $BASH_VERSION +meta.bash enable and disable eight-bit readline input +mktmp.bash make a temporary file with a unique name +number.bash a fun hack to translate numerals into english +prompt.bash a way to set PS1 to some predefined strings +remap_keys.bash a front end to `bind' to redo readline bindings +require.bash lisp-like require/provide library functions for bash +send_mail.bash replacement smtp client written in bash +shcat.bash bash replacement for `cat' +source.bash replacement for source that uses current directory +string.bash the string(3) functions at the shell level +stty.bash front-end to stty that changes readline bindings too +y_or_n_p.bash prompt for a yes/no/quit answer diff --git a/examples/scripts.noah/aref.bash b/examples/scripts.noah/aref.bash new file mode 100644 index 0000000..9b221b8 --- /dev/null +++ b/examples/scripts.noah/aref.bash @@ -0,0 +1,44 @@ +# aref.bash --- pseudo-array manipulating routines +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created 1992-07-01 +# Last modified: 1993-02-03 +# Public domain + +# Conversion to bash v2 syntax done by Chet Ramey + +# Commentary: +# Code: + +#:docstring aref: +# Usage: aref NAME INDEX +# +# In array NAME, access element INDEX (0-origin) +#:end docstring: + +###;;;autoload +function aref () +{ + local name="$1" + local index="$2" + + set -- ${!name} + [ $index -ge 1 ] && shift $index + echo $1 +} + +#:docstring string_aref: +# Usage: aref STRING INDEX +# +# Echo the INDEXth character in STRING (0-origin) on stdout. +#:end docstring: + +###;;;autoload +function string_aref () +{ + local stuff=${1:$2} + echo ${stuff:0:1} +} + +provide aref + +# aref.bash ends here diff --git a/examples/scripts.noah/bash.sub.bash b/examples/scripts.noah/bash.sub.bash new file mode 100644 index 0000000..2504459 --- /dev/null +++ b/examples/scripts.noah/bash.sub.bash @@ -0,0 +1,28 @@ +# bash.sub.bash --- stub for standalone shell scripts using bash library +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-07-13 +# Last modified: 1993-09-29 +# Public domain + +#:docstring bash.sub: +# Standard subroutines for bash scripts wishing to use "require" to load +# libraries. +# +# Usage: In each directory where a bash script that uses this script +# exists, place a copy of this script. Then, at the top of such scripts, +# put the command +# +# source ${0%/*}/bash.sub || exit 1 +# +# Then you can use `require' to load packages. +# +#:end docstring: + +default_FPATH="~friedman/etc/init/bash/functions/lib" + +source "${default_FPATH}/feature" +REQUIRE_FAILURE_FATAL=t + +FPATH="${FPATH-${default_FPATH}}" + +# bash.sub.bash ends here diff --git a/examples/scripts.noah/bash_version.bash b/examples/scripts.noah/bash_version.bash new file mode 100644 index 0000000..4ea737b --- /dev/null +++ b/examples/scripts.noah/bash_version.bash @@ -0,0 +1,42 @@ +# bash_version.bash --- get major and minor components of bash version number +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1993-01-26 +# Last modified: 1993-01-26 +# Public domain + +# Converted to bash v2 syntax by Chet Ramey + +# Commentary: +# Code: + +#:docstring bash_version: +# Usage: bash_version {major|minor} +# +# Echo the major or minor number of this version of bash on stdout, or +# just echo $BASH_VERSION if no argument is given. +#:end docstring: + +###;;;autoload +function bash_version () +{ + local major minor + + case "$1" in + major) echo "${BASH_VERSION/.*/}" ;; + minor) major="${BASH_VERSION/.*/}" + minor="${BASH_VERSION#${major}.}" + echo "${minor%%.*}" ;; + patchlevel) minor="${BASH_VERSION#*.*.}" + echo "${minor%(*}" ;; + version) minor=${BASH_VERSION/#*.*./} + echo ${BASH_VERSION/%.$minor/} ;; + release) echo ${BASH_VERSION%(*} ;; + build) minor="${BASH_VERSION#*.*.*(}" + echo ${minor%)} ;; + *) echo "${BASH_VERSION}" ;; + esac +} + +provide bash_version + +# bash_version.bash ends here diff --git a/examples/scripts.noah/meta.bash b/examples/scripts.noah/meta.bash new file mode 100644 index 0000000..6121726 --- /dev/null +++ b/examples/scripts.noah/meta.bash @@ -0,0 +1,37 @@ +# meta.bash --- meta key frobnications +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-06-28 +# Last modified: 1993-01-26 +# Public domain + +# Commentary: +# Code: + +#:docstring meta: +# Usage: meta [on|off] +# +# An argument of "on" will make bash use the 8th bit of any input from +# a terminal as a "meta" bit, i.e bash will be able to use a real meta +# key. +# +# An argument of "off" causes bash to disregard the 8th bit, which is +# assumed to be used for parity instead. +#:end docstring: + +function meta () +{ + case "$1" in + on) bind 'set input-meta On' + bind 'set output-meta on' + bind 'set convert-meta off' ;; + off) bind 'set input-meta Off' + bind 'set output-meta off' + bind 'set convert-meta on' ;; + *) echo "Usage: meta [on|off]" 1>&2 ; return 1 ;; + esac + return 0 +} + +provide meta + +# meta.bash ends here diff --git a/examples/scripts.noah/mktmp.bash b/examples/scripts.noah/mktmp.bash new file mode 100644 index 0000000..3ea43ad --- /dev/null +++ b/examples/scripts.noah/mktmp.bash @@ -0,0 +1,66 @@ +# mktmp.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1993-02-03 +# Last modified: 1993-02-03 +# Public domain + +# Conversion to bash v2 syntax done by Chet Ramey + +# Commentary: +# Code: + +#:docstring mktmp: +# Usage: mktmp [template] {createp} +# +# Generate a unique filename from TEMPLATE by appending a random number to +# the end. +# +# If optional 2nd arg CREATEP is non-null, file will be created atomically +# before returning. This is to avoid the race condition that in between +# the time that the temporary name is returned and the caller uses it, +# someone else creates the file. +#:end docstring: + +###;;;autoload +function mktmp () +{ + local template="$1" + local tmpfile="${template}${RANDOM}" + local createp="$2" + local noclobber_status + + case "$-" in + *C*) noclobber_status=set;; + esac + + if [ "${createp:+set}" = "set" ]; then + # Version which creates file atomically through noclobber test. + set -o noclobber + (> "${tmpfile}") 2> /dev/null + while [ $? -ne 0 ] ; do + # Detect whether file really exists or creation lost because of + # some other permissions problem. If the latter, we don't want + # to loop forever. + if [ ! -e "${tmpfile}" ]; then + # Trying to create file again creates stderr message. + echo -n "mktmp: " 1>&2 + > "${tmpfile}" + return 1 + fi + tmpfile="${template}${RANDOM}" + (> "${tmpfile}") 2> /dev/null + done + test "${noclobber_status}" != "set" && set +o noclobber + else + # Doesn't create file, so it introduces race condition for caller. + while [ -e "${tmpfile}" ]; do + tmpfile="${template}${RANDOM}" + done + fi + + echo "${tmpfile}" +} + +provide mktmp + +# mktmp.bash ends here diff --git a/examples/scripts.noah/number.bash b/examples/scripts.noah/number.bash new file mode 100644 index 0000000..37b62b6 --- /dev/null +++ b/examples/scripts.noah/number.bash @@ -0,0 +1,185 @@ +# number.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1993-02-22 +# Last modified: 1993-04-01 +# Public domain + +# Conversion to bash v2 syntax done by Chet Ramey + +# Commentary: +# Code: + +#:docstring number: +# Usage: number [number] +# +# Converts decimal integers to english notation. Spaces and commas are +# optional. Numbers 67 digits and larger will overflow this script. +# +# E.g: number 99,000,000,000,000,454 +# => ninety-nine quadrillion four hundred fifty-four +# +#:end docstring: + +function number () +{ + local result + local val1 + local val2 + local val3 + local d1 + local d2 + local d3 + + case "$*" in + *[!0-9,.]* ) + echo "number: invalid character in argument." 1>&2 + return 1 + ;; + *.* ) + echo "number: fractions not supported (yet)." 1>&2 + return 1 + ;; + esac + + result='' + + eval set - "`echo ${1+\"$@\"} | sed -n -e ' + s/[, ]//g;s/^00*/0/g;s/\(.\)\(.\)\(.\)$/\"\1 \2 \3\"/; + :l + /[0-9][0-9][0-9]/{ + s/\([^\" ][^\" ]*\)\([^\" ]\)\([^\" ]\)\([^\" ]\)/\1\"\2 \3 \4\"/g; + t l + } + /^[0-9][0-9][0-9]/s/\([^\" ]\)\([^\" ]\)\([^\" ]\)/\"\1 \2 \3\"/; + /^[0-9][0-9]/s/\([^\" ]\)\([^\" ]\)/\"\1 \2\"/; + /^[0-9]/s/^\([^\" ][^\" ]*\)/\"\1\"/g;s/\"\"/\" \"/g;p;'`" + + while test $# -ne 0 ; do + eval `set - $1; + d3='' d2='' d1='' + case $# in + 1 ) d1=$1 ;; + 2 ) d2=$1 d1=$2 ;; + 3 ) d3=$1 d2=$2 d1=$3 ;; + esac + echo "d3=\"${d3}\" d2=\"${d2}\" d1=\"${d1}\""` + + val1='' val2='' val3='' + + case "${d3}" in + '1' ) val3='one' ;; + '2' ) val3='two' ;; + '3' ) val3='three' ;; + '4' ) val3='four' ;; + '5' ) val3='five' ;; + '6' ) val3='six' ;; + '7' ) val3='seven' ;; + '8' ) val3='eight' ;; + '9' ) val3='nine' ;; + esac + + case "${d2}" in + '1' ) val2='teen' ;; + '2' ) val2='twenty' ;; + '3' ) val2='thirty' ;; + '4' ) val2='forty' ;; + '5' ) val2='fifty' ;; + '6' ) val2='sixty' ;; + '7' ) val2='seventy' ;; + '8' ) val2='eighty' ;; + '9' ) val2='ninety' ;; + esac + + case "${val2}" in + 'teen') + val2='' + case "${d1}" in + '0') val1='ten' ;; + '1') val1='eleven' ;; + '2') val1='twelve' ;; + '3') val1='thirteen' ;; + '4') val1='fourteen' ;; + '5') val1='fifteen' ;; + '6') val1='sixteen' ;; + '7') val1='seventeen' ;; + '8') val1='eighteen' ;; + '9') val1='nineteen' ;; + esac + ;; + 0 ) : ;; + * ) + if test ".${val2}" != '.' && test ".${d1}" != '.0' ; then + val2="${val2}-" + fi + case "${d1}" in + '0') val2="${val2} " ;; + '1') val1='one' ;; + '2') val1='two' ;; + '3') val1='three' ;; + '4') val1='four' ;; + '5') val1='five' ;; + '6') val1='six' ;; + '7') val1='seven' ;; + '8') val1='eight' ;; + '9') val1='nine' ;; + esac + ;; + esac + + if test ".${val3}" != '.' ; then + result="${result}${val3} hundred " + fi + + if test ".${val2}" != '.' ; then + result="${result}${val2}" + fi + + if test ".${val1}" != '.' ; then + result="${result}${val1} " + fi + + if test ".${d1}${d2}${d3}" != '.000' ; then + case $# in + 0 | 1 ) ;; + 2 ) result="${result}thousand " ;; + 3 ) result="${result}million " ;; + 4 ) result="${result}billion " ;; + 5 ) result="${result}trillion " ;; + 6 ) result="${result}quadrillion " ;; + 7 ) result="${result}quintillion " ;; + 8 ) result="${result}sextillion " ;; + 9 ) result="${result}septillion " ;; + 10 ) result="${result}octillion " ;; + 11 ) result="${result}nonillion " ;; + 12 ) result="${result}decillion " ;; + 13 ) result="${result}undecillion " ;; + 14 ) result="${result}duodecillion " ;; + 15 ) result="${result}tredecillion " ;; + 16 ) result="${result}quattuordecillion " ;; + 17 ) result="${result}quindecillion " ;; + 18 ) result="${result}sexdecillion " ;; + 19 ) result="${result}septendecillion " ;; + 20 ) result="${result}octodecillion " ;; + 21 ) result="${result}novemdecillion " ;; + 22 ) result="${result}vigintillion " ;; + * ) + echo "Error: number too large (66 digits max)." 1>&2 + return 1 + ;; + esac + fi + + shift + done + + set - ${result} + case "$*" in + '') set - 'zero' ;; + esac + + echo ${1+"$@"} +} + +provide number + +# number.bash ends here diff --git a/examples/scripts.noah/prompt.bash b/examples/scripts.noah/prompt.bash new file mode 100644 index 0000000..3dc25a9 --- /dev/null +++ b/examples/scripts.noah/prompt.bash @@ -0,0 +1,40 @@ +# prompt.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-01-15 +# Public domain + +# $Id: prompt.bash,v 1.2 1994/10/18 16:34:35 friedman Exp $ + +# Commentary: +# Code: + +#:docstring prompt: +# Usage: prompt [chars] +# +# Various preformatted prompt strings selected by argument. For a +# list of available arguments and corresponding formats, do +# `type prompt'. +#:end docstring: + +###;;;autoload +function prompt () +{ + case "$1" in + d) PS1='$(dirs) \$ ' ;; + n) PS1='\$ ' ;; + hsw) PS1='\h[$SHLVL]: \w \$ ' ;; + hw) PS1='\h: \w \$ ' ;; + sh) PS1='[$SHLVL] \h\$ ' ;; + sw) PS1='[$SHLVL] \w \$ ' ;; + uh) PS1='\u@\h\$ ' ;; + uhsHw) PS1='\u@\h[$SHLVL]:\#: \w \$ ' ;; + uhsw) PS1='\u@\h[$SHLVL]: \w \$ ' ;; + uhw) PS1='\u@\h: \w \$ ' ;; + uw) PS1='(\u) \w \$ ' ;; + w) PS1='\w \$ ' ;; + esac +} + +provide prompt + +# prompt.bash ends here diff --git a/examples/scripts.noah/remap_keys.bash b/examples/scripts.noah/remap_keys.bash new file mode 100644 index 0000000..aa7c463 --- /dev/null +++ b/examples/scripts.noah/remap_keys.bash @@ -0,0 +1,71 @@ +# remap_keybindings.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-01-11 +# Last modified: 1993-02-03 +# Public domain + +# Conversion to bash v2 syntax done by Chet Ramey + +# Commentary: +# Code: + +#:docstring remap_keybindings: +# Usage: remap_keybindings old_function new_function +# +# Clear all readline keybindings associated with OLD_FUNCTION (a Readline +# function) rebinding them to NEW_FUNCTION (`self-insert' by default) +# +# This requires bash version 1.10 or newer, since previous versions did not +# implement the `bind' builtin. +#:end docstring: + +###;;;autoload +function remap_keybindings () +{ + local unbind_function="$1" + local bind_function="${2:-'self-insert'}" + local bind_output + local arg + + # If they're the same thing, the work has already been done. :-) + if [ "${unbind_function}" = "${bind_function}" ]; then + return 0 + fi + + while : ; do + bind_output="$(bind -q ${unbind_function} 2> /dev/null)" + + case "${bind_output}" in + "${unbind_function} can be invoked via"* ) ;; + "" ) return 1 ;; # probably bad argument to bind + *) return 0 ;; # unbound + esac + + # Format of bind_output is like: + # 'quoted-insert can be invoked via "\C-q", "\C-v".' + # 'self-insert can be invoked via " ", "!", """, "$", "%", ...' + set -- ${bind_output} + shift 5 + + for arg in "$@" ; do + # strip off trailing `.' or `,' + arg=${arg%.}; + arg=${arg%,}; + + case ${arg} in + ..) + # bind -q didn't provide whole list of key bindings; jump + # to top loop to get more + continue 2 ; + ;; + *) + bind "${arg}: ${bind_function}" + ;; + esac + done + done +} + +provide remap_keybindings + +# remap_keybindings.bash ends here diff --git a/examples/scripts.noah/require.bash b/examples/scripts.noah/require.bash new file mode 100644 index 0000000..f38040a --- /dev/null +++ b/examples/scripts.noah/require.bash @@ -0,0 +1,182 @@ +# require.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-07-08 +# Last modified: 1993-09-29 +# Public domain + +# Commentary: + +# These functions provide an interface based on the lisp implementation for +# loading libraries when they are needed and eliminating redundant loading. +# The basic idea is that each "package" (or set of routines, even if it is +# only one function) registers itself with a symbol that marks a "feature" +# as being "provided". If later you "require" a given feature, you save +# yourself the trouble of explicitly loading it again. +# +# At the bottom of each package, put a "provide foobar", so when another +# package has a "require foobar", it gets loaded and registered as a +# "feature" that won't need to get loaded again. (See warning below for +# reasons why provide should be put at the end.) +# +# The list of provided features are kept in the `FEATURES' variable, which +# is not exported. Care should be taken not to munge this in the shell. +# The search path comes from a colon-separated `FPATH' variable. It has no +# default value and must be set by the user. +# +# Require uses `fpath_search', which works by scanning all of FPATH for a +# file named the same as the required symbol but with a `.bash' appended to +# the name. If that is found, it is loaded. If it is not, FPATH is +# searched again for a file name the same as the feature (i.e. without any +# extension). Fpath_search may be useful for doing library filename +# lookups in other functions (such as a `load' or `autoload' function). +# +# Warning: Because require ultimately uses the builtin `source' command to +# read in files, it has no way of undoing the commands contained in the +# file if there is an error or if no provide statement appeared (this +# differs from the lisp implementation of require, which normally undoes +# most of the forms that were loaded if the require fails). Therefore, to +# minize the number of problems caused by requiring a faulty package (such +# as syntax errors in the source file) it is better to put the provide at +# the end of the file, rather than at the beginning. + +# Code: + +# Exporting this variable would cause considerable lossage, since none of +# the functions are exported (or at least, they're not guaranteed to be) +export -n FEATURES + +#:docstring : +# Null function. Provided only so that one can put page breaks in source +# files without any ill effects. +#:end docstring: +# +# (\\014 == C-l) +eval "function $(echo -e \\014) () { : }" + + +#:docstring featurep: +# Usage: featurep argument +# +# Returns 0 (true) if argument is a provided feature. Returns 1 (false) +# otherwise. +#:end docstring: + +###;;;autoload +function featurep () +{ + local feature="$1" + + case " ${FEATURES} " in + *" ${feature} "* ) return 0 ;; + esac + + return 1 +} + + +#:docstring provide: +# Usage: provide symbol ... +# +# Register a list of symbols as provided features +#:end docstring: + +###;;;autoload +function provide () +{ + local feature + + for feature in "$@" ; do + if ! featurep "${feature}" ; then + FEATURES="${FEATURES} ${feature}" + fi + done + + return 0 +} + + +#:docstring require: +# Usage: require feature {file} +# +# Load FEATURE if it is not already provided. Note that require does not +# call `provide' to register features. The loaded file must do that +# itself. If the package does not explicitly do a `provide' after being +# loaded, require will complain about the feature not being provided on +# stderr. +# +# Optional argument FILE means to try to load FEATURE from FILE. If no +# file argument is given, require searches through FPATH (see fpath_search) +# for the appropriate file. +# +# If the variable REQUIRE_FAILURE_FATAL is set, require will cause the +# current shell invocation to exit, rather than merely return. This may be +# useful for a shell script that vitally depends on a package. +# +#:end docstring: + +###;;;autoload +function require () +{ + local feature="$1" + local path="$2" + local file + + if ! featurep "${feature}" ; then + file=$(fpath_search "${feature}" "${path}") && source "${file}" + + if ! featurep "${feature}" ; then + echo "require: ${feature}: feature was not provided." 1>&2 + if [ "${REQUIRE_FAILURE_FATAL+set}" = "set" ]; then + exit 1 + fi + return 1 + fi + fi + + return 0 +} + +#:docstring fpath_search: +# Usage: fpath_search filename {path ...} +# +# Search $FPATH for `filename' or, if `path' (a list) is specified, search +# those directories instead of $FPATH. First the path is searched for an +# occurrence of `filename.bash, then a second search is made for just +# `filename'. +#:end docstring: + +###;;;autoload +function fpath_search () +{ + local name="$1" + local path="$2" + local suffix=".bash" + local file + + if [ -z "${path}" ]; then path="${FPATH}"; fi + + for file in "${name}${suffix}" "${name}" ; do + set -- $(IFS=':' + set -- ${path} + for p in "$@" ; do + echo -n "${p:-.} " + done) + + while [ $# -ne 0 ]; do + test -f "${1}/${file}" && { file="${1}/${file}"; break 2 } + shift + done + done + + if [ $# -eq 0 ]; then + echo "fpath_search: ${name}: file not found in fpath" 1>&2 + return 1 + fi + + echo "${file}" + return 0 +} + +provide require + +# require.bash ends here diff --git a/examples/scripts.noah/send_mail.bash b/examples/scripts.noah/send_mail.bash new file mode 100644 index 0000000..24a1220 --- /dev/null +++ b/examples/scripts.noah/send_mail.bash @@ -0,0 +1,140 @@ +# send_mail.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-07-02 +# Public domain + +# Commentary: + +# TODO: implement Fcc headers (see emacs manual) + +# Code: + +#:docstring send_mail: +# Usage: send_mail +# +# This function serves as a simple replacement for sendmail as a client +# interface on those systems where it is not available. It does assume +# that one can talk to an SMTP mailer on port 25 either on the local host +# or on the host specified by the MAILHOST environment variable. If you +# have access to sendmail, it's better to use 'sendmail -t' instead of this +# script (which probably isn't as robust). +# +# Message is read from stdin, and headers are parsed to determine +# recipients. +#:end docstring: + +###;;;autoload +function send_mail () +{ + # Need gawk, since several extensions are taken advantage of (like + # IGNORECASE for regexps). + local awk="${GAWK_LOCATION:-gawk}" + local DefaultFrom="${USER:-${LOGNAME}}" + local From + local To + local Cc + local Bcc + local tmpfile="/tmp/send_mail$$" + + while [ -e "${tmpfile}" ]; do + tmpfile="/tmp/send_mail${RANDOM}" + done + + # Lines consisting only of dots need one more dot appended. SMTP + # servers eat one of the dots (and if only 1 dot appears, it signifies + # the end of the message). + sed '/^\.\.*/s/^\(\.\.*\)$/\1./' > "${tmpfile}" + + # Parse mail headers in message to extract recipients list. + # This doesn't affect what the user sees---it's only used to generate + # the rcpt-to lines for SMTP. + eval $(${awk} -f - "${tmpfile}" <<- '__EOF__' + # Try to extract email address from amidst random data + function parse_address (data) + { + # From: "real name" <foobar@host> + # From: "" <foobar@host> + if (match(data, /^\"[^\"]*\"[ \t]*<.*>/)) { + data_idx = match(data, /^\"[^\"]*\"[ \t]*</) + data = substr(data, RSTART + RLENGTH); + if (data_idx = match(data, ">.*")) + data = substr(data, 1, RSTART - 1); + return data + } + # From: real name <foobar@host> + if (match(data, /<.*>/)) { + data_idx = match(data, /</) + data = substr(data, RSTART + RLENGTH); + if (data_idx = match(data, ">")) + data = substr(data, 1, RSTART - 1); + return data + } + # From: foobar@host (real name) + if (match(data, /\(.*\)/)) { + data_idx = match(data, /\(/); + data = substr(data, 1, RSTART - 1); + return data + } + # (hopefully) From: foobar@host + return data + } + + BEGIN { IGNORECASE = 1; } + + # Blank line signifies end of headers, so we can stop looking. + /^$/ { exit(0) } + + /^from:|^to:|^cc:|^bcc:/ { + header_idx = match($0, /^[^:]*:/) + if (header_idx) { + # Capitalize header name + header_firstchar = toupper(substr($0, RSTART, 1)); + header_rest = tolower(substr($0, RSTART + 1, RLENGTH - 2)); + header = header_firstchar header_rest + + $0 = substr($0, RSTART + RLENGTH + 1); + addresses = "" + # parse addresses + while ($0) { + # Strip leading whitespace + if (idx = match($0, /[ \t]*/)) + $0 = substr($0, RSTART + RLENGTH); + + # Find everything up to a nonquoted comma + # FIXME: doesnt handle quoting yet + if (idx = match($0, /,/)) { + data = substr($0, 1, RSTART); + $0 = substr($0, RSTART + 1); + } else { + data = $0 + $0 = "" + } + addresses = addresses " " parse_address(data) + } + + printf("%s='%s'\n", header, addresses); + } + } + __EOF__) + + # Not sure if an address is *required* after the HELO.. every sendmail + # I tried talking to didn't seem to care. Some sendmails don't care + # if there's a HELO at all. + cat <<- __EOF__ | telnet ${MAILHOST:-localhost} 25 > /dev/null 2>&1 + HELO + mail from: ${From:-${DefaultFrom}} + $(for name in ${To} ${Cc} ${Bcc} ; do + echo "rcpt to: ${name}" + done) + data + $(cat "${tmpfile}") + . + quit + __EOF__ + + rm -f "${tmpfile}" +} + +provide send_mail + +# send_mail.bash ends here diff --git a/examples/scripts.noah/shcat.bash b/examples/scripts.noah/shcat.bash new file mode 100644 index 0000000..5d9e96d --- /dev/null +++ b/examples/scripts.noah/shcat.bash @@ -0,0 +1,49 @@ +# shcat.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-07-17 +# Last modified: 1993-09-29 +# Public domain + +# Conversion to bash v2 syntax done by Chet Ramey + +# Commentary: +# Code: + +#:docstring shcat: +# Usage: shcat {file1} {file2} {...} +# +# Like `cat', only this is all inline bash. +#:end docstring: + +###;;;autoload +function shcat () +{ + local IFS="" + local line + local file + local exitstat=0 + + if [ $# -eq 0 ]; then + while read -r line; do + echo "${line}" + done + return 0 + else + for file in "$@" ; do + if [ -r "${file}" ]; then + while read -r line; do + echo "${line}" + done < "${file}" + else + # This will cause the error to be printed on stderr + < "${file}" + exitstat=1 + fi + done + return ${exitstat} + fi +} + +provide shcat + +# shcat.bash ends here diff --git a/examples/scripts.noah/source.bash b/examples/scripts.noah/source.bash new file mode 100644 index 0000000..2b36489 --- /dev/null +++ b/examples/scripts.noah/source.bash @@ -0,0 +1,63 @@ +# source.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-05-17 +# Last modified: 1993-09-29 +# Public domain + +# Commentary: +# Code: + +#:docstring source: +# Usage: source file ... +# +# Source forces file arguments to be considered in the current directory +# only, unless there is an absolute path starting with `/'. I think it's +# bad that the builtin "source" searches PATH, because PATH normally +# contains directories with binary files that aren't useful for bash to +# read and most people don't put "." first in their path. +# +# This "source" is capable of reading more than one file at a time. Return +# value is number of failed source attempts. +#:end docstring: + +# This function is not hygienic, but there's not much we can do about +# variable name conflicts here. + +###;;;autoload +function source () +{ + local -i _source_failure_count=0 + local _source_file + + for _source_file ; do + # Check first part of each filename. If it's not `/', `./', or + # `../' then prepend "./" to the path to force the builtin `source' + # not to go searching through PATH to find the file. + case "${_source_file}" in + /*|./*|../* ) ;; + * ) _source_file="./${_source_file}" ;; + esac + + builtin source "${_source_file}" || + _source_failure_count="_source_failure_count + 1" + + done + + return ${_source_failure_count} +} + +#:docstring .: +# See "source" +#:end docstring: + +# So that `.' will call function definition of `source' instead of builtin + +###;;;autoload +function . () +{ + source "$@" +} + +provide source + +# source.bash ends here diff --git a/examples/scripts.noah/string.bash b/examples/scripts.noah/string.bash new file mode 100644 index 0000000..d80ebe8 --- /dev/null +++ b/examples/scripts.noah/string.bash @@ -0,0 +1,226 @@ +# string.bash --- bash emulation of string(3) library routines +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-07-01 +# Last modified: 1993-09-29 +# Public domain + +# Conversion to bash v2 syntax done by Chet Ramey + +# Commentary: +# Code: + +#:docstring strcat: +# Usage: strcat s1 s2 +# +# Strcat appends the value of variable s2 to variable s1. +# +# Example: +# a="foo" +# b="bar" +# strcat a b +# echo $a +# => foobar +# +#:end docstring: + +###;;;autoload +function strcat () +{ + local s1_val s2_val + + s1_val=${!1} # indirect variable expansion + s2_val=${!2} + eval "$1"=\'"${s1_val}${s2_val}"\' +} + +#:docstring strncat: +# Usage: strncat s1 s2 $n +# +# Line strcat, but strncat appends a maximum of n characters from the value +# of variable s2. It copies fewer if the value of variabl s2 is shorter +# than n characters. Echoes result on stdout. +# +# Example: +# a=foo +# b=barbaz +# strncat a b 3 +# echo $a +# => foobar +# +#:end docstring: + +###;;;autoload +function strncat () +{ + local s1="$1" + local s2="$2" + local -i n="$3" + local s1_val s2_val + + s1_val=${!s1} # indirect variable expansion + s2_val=${!s2} + + if [ ${#s2_val} -gt ${n} ]; then + s2_val=${s2_val:0:$n} # substring extraction + fi + + eval "$s1"=\'"${s1_val}${s2_val}"\' +} + +#:docstring strcmp: +# Usage: strcmp $s1 $s2 +# +# Strcmp compares its arguments and returns an integer less than, equal to, +# or greater than zero, depending on whether string s1 is lexicographically +# less than, equal to, or greater than string s2. +#:end docstring: + +###;;;autoload +function strcmp () +{ + [ "$1" = "$2" ] && return 0 + + [ "${1}" '<' "${2}" ] > /dev/null && return -1 + + return 1 +} + +#:docstring strncmp: +# Usage: strncmp $s1 $s2 $n +# +# Like strcmp, but makes the comparison by examining a maximum of n +# characters (n less than or equal to zero yields equality). +#:end docstring: + +###;;;autoload +function strncmp () +{ + if [ -z "${3}" ] || [ "${3}" -le "0" ]; then + return 0 + fi + + if [ ${3} -ge ${#1} ] && [ ${3} -ge ${#2} ]; then + strcmp "$1" "$2" + return $? + else + s1=${1:0:$3} + s2=${2:0:$3} + strcmp $s1 $s2 + return $? + fi +} + +#:docstring strlen: +# Usage: strlen s +# +# Strlen returns the number of characters in string literal s. +#:end docstring: + +###;;;autoload +function strlen () +{ + eval echo "\${#${1}}" +} + +#:docstring strspn: +# Usage: strspn $s1 $s2 +# +# Strspn returns the length of the maximum initial segment of string s1, +# which consists entirely of characters from string s2. +#:end docstring: + +###;;;autoload +function strspn () +{ + # Unsetting IFS allows whitespace to be handled as normal chars. + local IFS= + local result="${1%%[!${2}]*}" + + echo ${#result} +} + +#:docstring strcspn: +# Usage: strcspn $s1 $s2 +# +# Strcspn returns the length of the maximum initial segment of string s1, +# which consists entirely of characters not from string s2. +#:end docstring: + +###;;;autoload +function strcspn () +{ + # Unsetting IFS allows whitspace to be handled as normal chars. + local IFS= + local result="${1%%[${2}]*}" + + echo ${#result} +} + +#:docstring strstr: +# Usage: strstr s1 s2 +# +# Strstr echoes a substring starting at the first occurrence of string s2 in +# string s1, or nothing if s2 does not occur in the string. If s2 points to +# a string of zero length, strstr echoes s1. +#:end docstring: + +###;;;autoload +function strstr () +{ + # if s2 points to a string of zero length, strstr echoes s1 + [ ${#2} -eq 0 ] && { echo "$1" ; return 0; } + + # strstr echoes nothing if s2 does not occur in s1 + case "$1" in + *$2*) ;; + *) return 1;; + esac + + # use the pattern matching code to strip off the match and everything + # following it + first=${1/$2*/} + + # then strip off the first unmatched portion of the string + echo "${1##$first}" +} + +#:docstring strtok: +# Usage: strtok s1 s2 +# +# Strtok considers the string s1 to consist of a sequence of zero or more +# text tokens separated by spans of one or more characters from the +# separator string s2. The first call (with a non-empty string s1 +# specified) echoes a string consisting of the first token on stdout. The +# function keeps track of its position in the string s1 between separate +# calls, so that subsequent calls made with the first argument an empty +# string will work through the string immediately following that token. In +# this way subsequent calls will work through the string s1 until no tokens +# remain. The separator string s2 may be different from call to call. +# When no token remains in s1, an empty value is echoed on stdout. +#:end docstring: + +###;;;autoload +function strtok () +{ + : +} + +#:docstring strtrunc: +# Usage: strtrunc $n $s1 {$s2} {$...} +# +# Used by many functions like strncmp to truncate arguments for comparison. +# Echoes the first n characters of each string s1 s2 ... on stdout. +#:end docstring: + +###;;;autoload +function strtrunc () +{ + n=$1 ; shift + for z; do + echo "${z:0:$n}" + done +} + +provide string + +# string.bash ends here diff --git a/examples/scripts.noah/stty.bash b/examples/scripts.noah/stty.bash new file mode 100644 index 0000000..611d970 --- /dev/null +++ b/examples/scripts.noah/stty.bash @@ -0,0 +1,64 @@ +# stty.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-01-11 +# Last modified: 1993-09-29 +# Public domain + +# Conversion to bash v2 syntax done by Chet Ramey + +# Commentary: +# Code: + +require remap_keybindings + +#:docstring stty: +# Track changes to certain keybindings with stty, and make those changes +# reflect in bash's readline bindings as well. +# +# This requires bash version 1.10 or newer, since previous versions did not +# implement the `bind' builtin. +#:end docstring: + +###;;;autoload +function stty () +{ + local erase="backward-delete-char" + local kill="unix-line-discard" + local werase="backward-kill-word" + local lnext="quoted-insert" + local readline_function="" + local key="" + local stty_command="" + + while [ $# -gt 0 ]; do + case "$1" in + erase | kill | werase | lnext ) + key=$(echo "${2}" | cat -v | sed 's/\^/\\C-/') + readline_function=$(eval echo \$${1}) + + # Get rid of any current bindings; the whole point of this + # function is to make the distinction between readline + # bindings and particular cbreak characters transparent; old + # readline keybindings shouldn't hang around. + # could use bind -r here instead of binding to self-insert + remap_keybindings "${readline_function}" "self-insert" + + # Bind new key to appropriate readline function + bind "\"${key}\": ${readline_function}" + + stty_command="${stty_command} ${1} ${2}" + shift 2 + ;; + *) + stty_command="${stty_command} ${1}" + shift + ;; + esac + done + + command stty ${stty_command} +} + +provide stty + +# stty.bash ends here diff --git a/examples/scripts.noah/y_or_n_p.bash b/examples/scripts.noah/y_or_n_p.bash new file mode 100644 index 0000000..2674a29 --- /dev/null +++ b/examples/scripts.noah/y_or_n_p.bash @@ -0,0 +1,78 @@ +# y_or_n_p.bash +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1992-06-18 +# Last modified: 1993-03-01 +# Public domain + +# Conversion to bash v2 syntax done by Chet Ramey + +# Commentary: +# Code: + +#:docstring y_or_n_p: +# Usage: y_or_n_p QUERY +# +# Print QUERY on stderr, then read stdin for a y-or-n response. Actually, +# user may type anything they like, but first character must be a `y', `n', +# `q', or `!', otherwise the question is repeated until such an answer is +# obtained. +# +# If user typed `y', y_or_n_p returns 0. +# +# If user typed `n', y_or_n_p returns 1. +# +# If user typed `!', y_or_n_p returns 2. This is an indication to the +# caller that no more queries should be made. Assume `y' for all the rest. +# +# If user typed `q', y_or_n_p returns 3. This is an indication to the +# caller that no more queries should be made. Assume `n' for all the rest. +# +#:end docstring: + +###;;;autoload +function y_or_n_p () +{ + local ans + + [ ! -t 0 ] && return 1 + + while read -p "$*" -e ans ; do + case "${ans}" in + y* | Y* ) return 0 ;; + n* | N* ) return 1 ;; + \! ) return 2 ;; + q* | Q* ) return 3 ;; + *) echo "Please answer one of \`y', \`n', \`q', or \`"\!"'" 1>&2 ;; + esac + done +} + +#:docstring yes_or_no_p: +# Usage: yes_or_no_p QUERY +# +# Like y_or_n_p, but require a full `yes', `no', `yes!', or `quit' response. +#:end docstring: + +###;;;autoload +function yes_or_no_p () +{ + local ans + + [ ! -t 0 ] && return 3 + + while read -p "$*" -e ans; do + ans="$(echo ${ans} | tr '[A-Z]' '[a-z]')" + + case "${ans}" in + yes ) return 0 ;; + no ) return 1 ;; + yes\! ) return 2 ;; + quit ) return 3 ;; + *) echo "Please answer \`yes', \`no', \`yes"\!"', or \`quit'" 1>&2 ;; + esac + done +} + +provide y_or_n_p + +# y_or_n_p.bash ends here |