aboutsummaryrefslogtreecommitdiffstats
path: root/examples/scripts.noah
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scripts.noah')
-rw-r--r--examples/scripts.noah/PERMISSION29
-rw-r--r--examples/scripts.noah/README26
-rw-r--r--examples/scripts.noah/aref.bash44
-rw-r--r--examples/scripts.noah/bash.sub.bash28
-rw-r--r--examples/scripts.noah/bash_version.bash42
-rw-r--r--examples/scripts.noah/meta.bash37
-rw-r--r--examples/scripts.noah/mktmp.bash66
-rw-r--r--examples/scripts.noah/number.bash185
-rw-r--r--examples/scripts.noah/prompt.bash40
-rw-r--r--examples/scripts.noah/remap_keys.bash71
-rw-r--r--examples/scripts.noah/require.bash182
-rw-r--r--examples/scripts.noah/send_mail.bash140
-rw-r--r--examples/scripts.noah/shcat.bash49
-rw-r--r--examples/scripts.noah/source.bash63
-rw-r--r--examples/scripts.noah/string.bash226
-rw-r--r--examples/scripts.noah/stty.bash64
-rw-r--r--examples/scripts.noah/y_or_n_p.bash78
17 files changed, 1370 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..95272d7
--- /dev/null
+++ b/examples/scripts.noah/README
@@ -0,0 +1,26 @@
+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.
+PERMISSION Permissions to use the scripts in this directory.
+prompt.bash A way to set PS1 to some predefined strings.
+README README
+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(1)'.
+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(1) 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