diff options
Diffstat (limited to 'examples')
190 files changed, 20862 insertions, 0 deletions
diff --git a/examples/bashdb/PERMISSION b/examples/bashdb/PERMISSION new file mode 100644 index 0000000..4e9460c --- /dev/null +++ b/examples/bashdb/PERMISSION @@ -0,0 +1,27 @@ +From mikel@ora.com Tue Aug 1 12:13:20 1995 +Flags: 10 +Return-Path: mikel@ora.com +Received: from ruby.ora.com (ruby.ora.com [198.112.208.25]) by odin.INS.CWRU.Edu with ESMTP (8.6.12+cwru/CWRU-2.1-ins) + id MAA01565; Tue, 1 Aug 1995 12:13:18 -0400 (from mikel@ora.com for <chet@odin.INS.CWRU.Edu>) +Received: (from fax@localhost) by ruby.ora.com (8.6.12/8.6.11) with UUCP id MAA23251; Tue, 1 Aug 1995 12:07:51 -0400 +Received: by los.ora.com (4.1/Spike-2.1) + id AA00672; Tue, 1 Aug 95 08:57:32 EDT +Date: Tue, 1 Aug 95 08:57:32 EDT +From: mikel@ora.com (Michael Loukides) +Message-Id: <9508011257.AA00672@los.ora.com> +Subject: Re: Ksh debugger from Rosenblatt's book [for bash] +To: Chet Ramey <chet@odin.INS.CWRU.Edu> +Cc: cmarie@ora.com, cam@iinet.com.au, brosenblatt@tm.com +In-Reply-To: Chet Ramey <chet@odin.INS.CWRU.Edu>, Mon, 31 Jul 1995 16:22:48 -0400 + + I've modified a (modified) version of Bill Rosenblatt's ksh debugger + to work with bash-2.0. Does ORA have any problem with me distributing + it with bash-2.0? + +That's great! + +Go ahead and circulate it; in fact, we should probably grab it and +stick it in our ftp archive, and put a reference to it in the book. +(Too late to actually discuss the thing, at least for this edition). +------- + diff --git a/examples/bashdb/README b/examples/bashdb/README new file mode 100644 index 0000000..2f643d1 --- /dev/null +++ b/examples/bashdb/README @@ -0,0 +1,3 @@ +This is a sample implementation of a bash debugger. It is not the same +as the project available from http://bashdb.sourceforge.net, and has been +deprecated in favor of that implementation. diff --git a/examples/bashdb/bashdb b/examples/bashdb/bashdb new file mode 100755 index 0000000..560cb7c --- /dev/null +++ b/examples/bashdb/bashdb @@ -0,0 +1,581 @@ +#! /bin/bash +# bashdb - Bash shell debugger +# +# Adapted from an idea in O'Reilly's `Learning the Korn Shell' +# Copyright (C) 1993-1994 O'Reilly and Associates, Inc. +# Copyright (C) 1998, 1999, 2001 Gary V. Vaughan <gvv@techie.com>> +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# NOTE: +# +# This program requires bash 2.x. +# If bash 2.x is installed as "bash2", you can invoke bashdb like this: +# +# DEBUG_SHELL=/bin/bash2 /bin/bash2 bashdb script.sh + +# TODO: +# +# break [regexp] +# cond [break] [condition] +# tbreak [regexp|+lines] +# restart +# Variable watchpoints +# Instrument `source' and `.' files in $_potbelliedpig +# be cleverer about lines we allow breakpoints to be set on +# break [function_name] + +echo 'Bash Debugger version 1.2.4' + +export _dbname=${0##*/} + +if test $# -lt 1; then + echo "$_dbname: Usage: $_dbname filename" >&2 + exit 1 +fi + +_guineapig=$1 + +if test ! -r $1; then + echo "$_dbname: Cannot read file '$_guineapig'." >&2 + exit 1 +fi + +shift + +__debug=${TMPDIR-/tmp}/bashdb.$$ +sed -e '/^# bashdb - Bash shell debugger/,/^# -- DO NOT DELETE THIS LINE -- /d' "$0" > $__debug +cat $_guineapig >> $__debug +exec ${DEBUG_SHELL-bash} $__debug $_guineapig "$@" + +exit 1 + +# -- DO NOT DELETE THIS LINE -- The program depends on it + +#bashdb preamble +# $1 name of the original guinea pig script + +__debug=$0 +_guineapig=$1 +__steptrap_calls=0 + +shift + +shopt -s extglob # turn on extglob so we can parse the debugger funcs + +function _steptrap +{ + local i=0 + + _curline=$1 + + if (( ++__steptrap_calls > 1 && $_curline == 1 )); then + return + fi + + if [ -n "$_disps" ]; then + while (( $i < ${#_disps[@]} )) + do + if [ -n "${_disps[$i]}" ]; then + _msg "${_disps[$i]}: \c" + eval _msg ${_disps[$i]} + fi + let i=$i+1 + done + fi + + if (( $_trace )); then + _showline $_curline + fi + + if (( $_steps >= 0 )); then + let _steps="$_steps - 1" + fi + + if _at_linenumbp ; then + _msg "Reached breakpoint at line $_curline" + _showline $_curline + _cmdloop + elif [ -n "$_brcond" ] && eval $_brcond; then + _msg "Break condition $_brcond true at line $_curline" + _showline $_curline + _cmdloop + elif (( $_steps == 0 )); then + # Assuming a real script will have the "#! /bin/sh" at line 1, + # assume that when $_curline == 1 we are inside backticks. + if (( ! $_trace )); then + _msg "Stopped at line $_curline" + _showline $_curline + fi + _cmdloop + fi +} + +function _setbp +{ + local i f line _x + + if [ -z "$1" ]; then + _listbp + return + fi + + eval "$_seteglob" + + if [[ $1 == *(\+)[1-9]*([0-9]) ]]; then + case $1 in + +*) + # normalize argument, then double it (+2 -> +2 + 2 = 4) + _x=${1##*([!1-9])} # cut off non-numeric prefix + _x=${x%%*([!0-9])} # cut off non-numeric suffix + f=$(( $1 + $_x )) + ;; + *) + f=$(( $1 )) + ;; + esac + + # find the next valid line + line="${_lines[$f]}" + while _invalidbreakp $f + do + (( f++ )) + line="${_lines[$f]}" + done + + if (( $f != $1 )) + then + _msg "Line $1 is not a valid breakpoint" + fi + + if [ -n "${_lines[$f]}" ]; then + _linebp[$1]=$1; + _msg "Breakpoint set at line $f" + else + _msg "Breakpoints can only be set on executable lines" + fi + else + _msg "Please specify a numeric line number" + fi + + eval "$_resteglob" +} + +function _listbp +{ + local i + + if [ -n "$_linebp" ]; then + _msg "Breakpoints:" + for i in ${_linebp[*]}; do + _showline $i + done + else + _msg "No breakpoints have been set" + fi +} + +function _clearbp +{ + local i + + if [ -z "$1" ]; then + read -e -p "Delete all breakpoints? " + case $REPLY in + [yY]*) + unset _linebp[*] + _msg "All breakpoints have been cleared" + ;; + esac + return 0 + fi + + eval "$_seteglob" + + if [[ $1 == [1-9]*([0-9]) ]]; then + unset _linebp[$1] + _msg "Breakpoint cleared at line $1" + else + _msg "Please specify a numeric line number" + fi + + eval "$_resteglob" +} + +function _setbc +{ + if (( $# > 0 )); then + _brcond=$@ + _msg "Break when true: $_brcond" + else + _brcond= + _msg "Break condition cleared" + fi +} + +function _setdisp +{ + if [ -z "$1" ]; then + _listdisp + else + _disps[${#_disps[@]}]="$1" + if (( ${#_disps[@]} < 10 )) + then + _msg " ${#_disps[@]}: $1" + else + _msg "${#_disps[@]}: $1" + fi + fi +} + +function _listdisp +{ + local i=0 j + + if [ -n "$_disps" ]; then + while (( $i < ${#_disps[@]} )) + do + let j=$i+1 + if (( ${#_disps[@]} < 10 )) + then + _msg " $j: ${_disps[$i]}" + else + _msg "$j: ${_disps[$i]}" + fi + let i=$j + done + else + _msg "No displays have been set" + fi +} + +function _cleardisp +{ + if (( $# < 1 )) ; then + read -e -p "Delete all display expressions? " + case $REPLY in + [Yy]*) + unset _disps[*] + _msg "All breakpoints have been cleared" + ;; + esac + return 0 + fi + + eval "$_seteglob" + + if [[ $1 == [1-9]*([0-9]) ]]; then + unset _disps[$1] + _msg "Display $i has been cleared" + else + _listdisp + _msg "Please specify a numeric display number" + fi + + eval "$_resteglob" +} + +# usage _ftrace -u funcname [funcname...] +function _ftrace +{ + local _opt=-t _tmsg="enabled" _func + if [[ $1 == -u ]]; then + _opt=+t + _tmsg="disabled" + shift + fi + for _func; do + declare -f $_opt $_func + _msg "Tracing $_tmsg for function $_func" + done +} + +function _cmdloop +{ + local cmd args + + while read -e -p "bashdb> " cmd args; do + test -n "$cmd" && history -s "$cmd $args" # save on history list + test -n "$cmd" || { set $_lastcmd; cmd=$1; shift; args=$*; } + if [ -n "$cmd" ] + then + case $cmd in + b|br|bre|brea|break) + _setbp $args + _lastcmd="break $args" + ;; + co|con) + _msg "ambiguous command: '$cmd', condition, continue?" + ;; + cond|condi|condit|conditi|conditio|condition) + _setbc $args + _lastcmd="condition $args" + ;; + c|cont|conti|contin|continu|continue) + _lastcmd="continue" + return + ;; + d) + _msg "ambiguous command: '$cmd', delete, display?" + ;; + de|del|dele|delet|delete) + _clearbp $args + _lastcmd="delete $args" + ;; + di|dis|disp|displ|displa|display) + _setdisp $args + _lastcmd="display $args" + ;; + f|ft|ftr|ftra|ftrace) + _ftrace $args + _lastcmd="ftrace $args" + ;; + \?|h|he|hel|help) + _menu + _lastcmd="help" + ;; + l|li|lis|list) + _displayscript $args + # _lastcmd is set in the _displayscript function + ;; + p|pr|pri|prin|print) + _examine $args + _lastcmd="print $args" + ;; + q|qu|qui|quit) + exit + ;; + s|st|ste|step|n|ne|nex|next) + let _steps=${args:-1} + _lastcmd="next $args" + return + ;; + t|tr|tra|trac|trace) + _xtrace + ;; + u|un|und|undi|undis|undisp|undispl|undispla|undisplay) + _cleardisp $args + _lastcmd="undisplay $args" + ;; + !*) + eval ${cmd#!} $args + _lastcmd="$cmd $args" + ;; + *) + _msg "Invalid command: '$cmd'" + ;; + esac + fi + done +} + +function _at_linenumbp +{ + [[ -n ${_linebp[$_curline]} ]] +} + +function _invalidbreakp +{ + local line=${_lines[$1]} + + # XXX - should use shell patterns + if test -z "$line" \ + || expr "$line" : '[ \t]*#.*' > /dev/null \ + || expr "$line" : '[ \t]*;;[ \t]*$' > /dev/null \ + || expr "$line" : '[ \t]*[^)]*)[ \t]*$' > /dev/null \ + || expr "$line" : '[ \t]*;;[ \t]*#.**$' > /dev/null \ + || expr "$line" : '[ \t]*[^)]*)[ \t]*;;[ \t]*$' > /dev/null \ + || expr "$line" : '[ \t]*[^)]*)[ \t]*;;*[ \t]*#.*$' > /dev/null + then + return 0 + fi + + return 1 +} + +function _examine +{ + if [ -n "$*" ]; then + _msg "$args: \c" + eval _msg $args + else + _msg "Nothing to print" + fi +} + +function _displayscript +{ + local i j start end bp cl + + if (( $# == 1 )); then # list 5 lines on either side of $1 + if [ $1 = "%" ]; then + let start=1 + let end=${#_lines[@]} + else + let start=$1-5 + let end=$1+5 + fi + elif (( $# > 1 )); then # list between start and end + if [ $1 = "^" ]; then + let start=1 + else + let start=$1 + fi + + if [ $2 = "\$" ]; then + let end=${#_lines[@]} + else + let end=$2 + fi + else # list 5 lines on either side of current line + let start=$_curline-5 + let end=$_curline+5 + fi + + # normalize start and end + if (( $start < 1 )); then + start=1 + fi + if (( $end > ${#_lines[@]} )); then + end=${#_lines[@]} + fi + + cl=$(( $end - $start )) + if (( $cl > ${LINES-24} )); then + pager=${PAGER-more} + else + pager=cat + fi + + i=$start + ( while (( $i <= $end )); do + _showline $i + let i=$i+1 + done ) 2>&1 | $pager + + # calculate the next block of lines + start=$(( $end + 1 )) + end=$(( $start + 11 )) + if (( $end > ${#_lines[@]} )) + then + end=${#_lines[@]} + fi + + _lastcmd="list $start $end" +} + +function _xtrace +{ + let _trace="! $_trace" + if (( $_trace )); then + _msg "Execution trace on" + else + _msg "Execution trace off" + fi +} + +function _msg +{ + echo -e "$@" >&2 +} + +function _showline +{ + local i=0 bp=' ' line=$1 cl=' ' + + if [[ -n ${_linebp[$line]} ]]; then + bp='*' + fi + + if (( $_curline == $line )); then + cl=">" + fi + + if (( $line < 100 )); then + _msg "${_guineapig/*\//}:$line $bp $cl${_lines[$line]}" + elif (( $line < 10 )); then + _msg "${_guineapig/*\//}:$line $bp $cl${_lines[$line]}" + elif (( $line > 0 )); then + _msg "${_guineapig/*\//}:$line $bp $cl${_lines[$line]}" + fi +} + +function _cleanup +{ + rm -f $__debug $_potbelliedpig 2> /dev/null +} + +function _menu +{ + _msg 'bashdb commands: + break N set breakpoint at line N + break list breakpoints & break condition + condition foo set break condition to foo + condition clear break condition + delete N clear breakpoint at line N + delete clear all breakpoints + display EXP evaluate and display EXP for each debug step + display show a list of display expressions + undisplay N remove display expression N + list N M display all lines of script between N and M + list N display 5 lines of script either side of line N + list display 5 lines if script either side of current line + continue continue execution upto next breakpoint + next [N] execute [N] statements (default 1) + print expr prints the value of an expression + trace toggle execution trace on/off + ftrace [-u] func make the debugger step into function FUNC + (-u turns off tracing FUNC) + help print this menu + ! string passes string to a shell + quit quit' +} + +shopt -u extglob + +HISTFILE=~/.bashdb_history +set -o history +set +H + +# strings to save and restore the setting of `extglob' in debugger functions +# that need it +_seteglob='local __eopt=-u ; shopt -q extglob && __eopt=-s ; shopt -s extglob' +_resteglob='shopt $__eopt extglob' + +_linebp=() +let _trace=0 +let _i=1 + +# Be careful about quoted newlines +_potbelliedpig=${TMPDIR-/tmp}/${_guineapig/*\//}.$$ +sed 's,\\$,\\\\,' $_guineapig > $_potbelliedpig + +_msg "Reading source from file: $_guineapig" +while read; do + _lines[$_i]=$REPLY + let _i=$_i+1 +done < $_potbelliedpig + +trap _cleanup EXIT +# Assuming a real script will have the "#! /bin/sh" at line 1, +# don't stop at line 1 on the first run +let _steps=1 +LINENO=-1 +trap '_steptrap $LINENO' DEBUG diff --git a/examples/bashdb/bashdb.el b/examples/bashdb/bashdb.el new file mode 100644 index 0000000..40584dd --- /dev/null +++ b/examples/bashdb/bashdb.el @@ -0,0 +1,177 @@ +;;; bashdb.el --- Grand Unified Debugger mode for running bashdb +;; Copyright (C) 2000, 2001 Masatake YAMATO + +;; Author: Masatake YAMATO <jet@gyve.org> + +;; 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 2 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, write to the Free Software Foundation, +;; Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +;; Commentary: +;; This program may run on Emacs 21.0.91 and XEmacs 21.1. +;; +;; Put +;; (autoload 'bashdb "bashdb" "Run bashdb" t nil) +;; to your .emacs. +;; M-x bashdb +;; Run bashdb (like this): bashdb target.sh +;; +;; About bashdb: +;; You can get bashdb from +;; http://www.oranda.demon.co.uk/development.html +;; +;; bashdb.el is based on perldb in gud.el in XEmacs 21.1. + +;; Revision: +;; $Revision: 1.6 $ +;; $Log: bashdb.el,v $ +;; Revision 1.6 2001/01/06 12:18:06 masata-y +;; Write note about XEmacs. +;; +;; + + +;;; Code: +(require 'gud) + +;; User customizable variable +(defcustom gud-bashdb-command-name "bashdb" + "File name for executing Bashdb." + :type 'string + :group 'gud) + +;; History of argument lists passed to bashdb. +(defvar gud-bashdb-history nil) + +(defun gud-bashdb-massage-args (file args) + (if xemacsp + (cons (file-name-nondirectory file) args) + args)) + +;; There's no guarantee that Emacs will hand the filter the entire +;; marker at once; it could be broken up across several strings. We +;; might even receive a big chunk with several markers in it. If we +;; receive a chunk of text which looks like it might contain the +;; beginning of a marker, we save it here between calls to the +;; filter. +(if xemacsp + (defvar gud-bashdb-marker-acc "")) +(defun gud-bashdb-marker-acc () + (if xemacsp + gud-bashdb-marker-acc + gud-marker-acc)) +(defun gud-bashdb-marker-acc-quote () + (if xemacsp + 'gud-bashdb-marker-acc + 'gud-marker-acc)) + +(defun gud-bashdb-marker-filter (string) + (save-match-data + (set (gud-bashdb-marker-acc-quote) + (concat (gud-bashdb-marker-acc) string)) + (let ((output "")) + ;; Process all the complete markers in this chunk. + (while (string-match "^\\([^:\n]+\\):\\([0-9]+\\)[ *]*>.*\n" + (gud-bashdb-marker-acc)) + (setq + ;; Extract the frame position from the marker. + gud-last-frame (cons + (substring (gud-bashdb-marker-acc) + (match-beginning 1) + (match-end 1)) + (string-to-int + (substring (gud-bashdb-marker-acc) + (match-beginning 2) + (match-end 2)))) + ;; Append any text before the marker to the output we're going + ;; to return - we don't include the marker in this text. + output (concat output + (substring (gud-bashdb-marker-acc) 0 (match-beginning 0)))) + ;; Set the accumulator to the remaining text. + (set + (gud-bashdb-marker-acc-quote) (substring + (gud-bashdb-marker-acc) (match-end 0)))) + + ;; Does the remaining text look like it might end with the + ;; beginning of another marker? If it does, then keep it in + ;; (gud-bashdb-marker-acc) until we receive the rest of it. Since we + ;; know the full marker regexp above failed, it's pretty simple to + ;; test for marker starts. + (if (string-match "^\\([^:\n]+\\):\\([0-9]+\\)[ *]*>" (gud-bashdb-marker-acc)) + (progn + ;; Everything before the potential marker start can be output. + (setq output (concat output (substring (gud-bashdb-marker-acc) + 0 (match-beginning 0)))) + ;; Everything after, we save, to combine with later input. + (set (gud-bashdb-marker-acc-quote) + (substring (gud-bashdb-marker-acc) (match-beginning 0)))) + + (setq output (concat output (gud-bashdb-marker-acc))) + (set (gud-bashdb-marker-acc-quote) "")) + + output))) + +(defun gud-bashdb-find-file (f) + (find-file-noselect f)) + +;;;###autoload +(defun bashdb (command-line) + "Run bashdb on program FILE in buffer *gud-FILE*. +The directory containing FILE becomes the initial working directory +and source-file directory for your debugger." + (interactive + (if xemacsp + (list (read-from-minibuffer "Run bashdb (like this): " + (if (consp gud-bashdb-history) + (car gud-bashdb-history) + (format "%s " gud-bashdb-command-name)) + nil nil + '(gud-bashdb-history . 1))) + (list (gud-query-cmdline 'bashdb)) + )) + + (if xemacsp + (progn + (gud-overload-functions '((gud-massage-args . gud-bashdb-massage-args) + (gud-marker-filter . gud-bashdb-marker-filter) + (gud-find-file . gud-bashdb-find-file))) + (gud-common-init command-line gud-bashdb-command-name)) + (gud-common-init command-line 'gud-bashdb-massage-args + 'gud-bashdb-marker-filter 'gud-bashdb-find-file) + (set (make-local-variable 'gud-minor-mode) 'bashdb)) + +;; Unsupported commands +;; condition foo set break condition to foo +;; condition clear break condition +;; display EXP evaluate and display EXP for each debug step +;; display show a list of display expressions +;; undisplay N remove display expression N +;; ! string passes string to a shell +;; quit quit + + (gud-def gud-break "break %l" "\C-b" "Set breakpoint at current line.") + (gud-def gud-list-break "break" "b" "List breakpoints & break condition.") + (gud-def gud-remove "delete %l" "\C-d" "Remove breakpoint at current line") + (gud-def gud-remove-all "delete" "d" "Clear all breakpoints") + (gud-def gud-cont "continue" "\C-r" "Continue with display.") + (gud-def gud-next "next" "\C-n" "Step one line (skip functions).") + (gud-def gud-print "print %e" "\C-p" "Evaluate bash expression at point.") + (gud-def gud-help "help" "h" "Show all commands.") + (gud-def gud-trace "trace" "t" "Toggle execution trace on/off") + + (setq comint-prompt-regexp "^bashdb> ") + (setq paragraph-start comint-prompt-regexp) + (run-hooks 'bashdb-mode-hook)) + +(provide 'bashdb) +;; bashdb.el ends here diff --git a/examples/complete/bashcc-1.0.1.tar.gz b/examples/complete/bashcc-1.0.1.tar.gz Binary files differnew file mode 100644 index 0000000..d680435 --- /dev/null +++ b/examples/complete/bashcc-1.0.1.tar.gz diff --git a/examples/complete/complete-examples b/examples/complete/complete-examples new file mode 100644 index 0000000..baa97e3 --- /dev/null +++ b/examples/complete/complete-examples @@ -0,0 +1,495 @@ +# +# Completion examples +# + +# +# This encapsulates the default bash completion code +# call with the word to be completed as $1 +# +# Since programmable completion does not use the bash default completions +# or the readline default of filename completion when the compspec does +# not generate any matches, this may be used as a `last resort' in a +# completion function to mimic the default bash completion behavior. +# +_bash_def_completion () +{ + local h t + COMPREPLY=() + + # command substitution + if [[ "$1" == \$\(* ]]; then + t=${1#??} + COMPREPLY=( $(compgen -c -P '$(' $t) ) + fi + # variables with a leading `${' + if [ ${#COMPREPLY[@]} -eq 0 ] && [[ "$1" == \$\{* ]]; then + t=${1#??} + COMPREPLY=( $(compgen -v -P '${' -S '}' $t) ) + fi + # variables with a leading `$' + if [ ${#COMPREPLY[@]} -eq 0 ] && [[ "$1" == \$* ]]; then + t=${1#?} + COMPREPLY=( $(compgen -v -P '$' $t ) ) + fi + # username expansion + if [ ${#COMPREPLY[@]} -eq 0 ] && [[ "$1" == ~* ]] && [[ "$1" != */* ]]; then + t=${1#?} + COMPREPLY=( $( compgen -u -P '~' $t ) ) + fi + # hostname + if [ ${#COMPREPLY[@]} -eq 0 ] && [[ "$1" == *@* ]]; then + h=${1%%@*} + t=${1#*@} + COMPREPLY=( $( compgen -A hostname -P "${h}@" $t ) ) + fi + # glob pattern + if [ ${#COMPREPLY[@]} -eq 0 ]; then + # sh-style glob pattern + if [[ $1 == *[*?[]* ]]; then + COMPREPLY=( $( compgen -G "$1" ) ) + # ksh-style extended glob pattern - must be complete + elif shopt -q extglob && [[ $1 == *[?*+\!@]\(*\)* ]]; then + COMPREPLY=( $( compgen -G "$1" ) ) + fi + fi + + # final default is filename completion + if [ ${#COMPREPLY[@]} -eq 0 ]; then + COMPREPLY=( $(compgen -f "$1" ) ) + fi +} + +# +# Return 1 if $1 appears to contain a redirection operator. Handles backslash +# quoting (barely). +# +_redir_op() +{ + case "$1" in + *\\'[\<\>]'*) return 1;; + *[\<\>]*) return 0;; + *) return 1;; + esac +} + + +# _redir_test tests the current word ($1) and the previous word ($2) for +# redirection operators and does filename completion on the current word +# if either one contains a redirection operator +_redir_test() +{ + if _redir_op "$1" ; then + COMPREPLY=( $( compgen -f "$1" ) ) + return 0 + elif _redir_op "$2" ; then + COMPREPLY=( $( compgen -f "$1" ) ) + return 0 + fi + return 1 +} + +# optional, but without this you can't use extended glob patterns +shopt -s extglob + +# +# Easy ones for the shell builtins +# +# nothing for: alias, break, continue, dirs, echo, eval, exit, getopts, +# let, logout, popd, printf, pwd, return, shift, suspend, test, times, +# umask +# + +complete -f -- . source +complete -A enabled builtin +complete -d cd + +# this isn't exactly right yet -- needs to skip shell functions and +# do $PATH lookup (or do compgen -c and filter out matches that also +# appear in compgen -A function) +complete -c command + +# could add -S '=', but that currently screws up because readline appends +# a space unconditionally + +complete -v export local readonly +complete -A helptopic help # currently same as builtins + +complete -d pushd + +complete -A shopt shopt + +complete -c type + +complete -a unalias +complete -v unset + +# +# Job control builtins: fg, bg, disown, kill, wait +# kill not done yet +# + +complete -A stopped -P '%' bg +complete -j -P '%' fg jobs disown + +# this is not quite right at this point + +_wait_func () +{ + local cur + cur=${COMP_WORDS[COMP_CWORD]} + + case "$cur" in + %*) COMPREPLY=( $(compgen -A running -P '%' ${cur#?} ) ) ;; + [0-9]*) COMPREPLY=( $(jobs -p | grep ^${cur}) ) ;; + *) COMPREPLY=( $(compgen -A running -P '%') $(jobs -p) ) + ;; + esac +} +complete -F _wait_func wait + +# +# more complicated things, several as yet unimplemented +# + +#complete -F _bind_func bind + +_declare_func() +{ + local cur prev nflag opts + + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + COMPREPLY=() + if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then + COMPREPLY=(-a -f -F -i -p -r -t -x) + return 0; + fi + if [[ $cur == '+' ]]; then + COMPREPLY=(+i +t +x) + return 0; + fi + if [[ $prev == '-p' ]]; then + COMPREPLY=( $(compgen -v $cur) ) + return 0; + fi + return 1 +} +complete -F _declare_func declare typeset + +_enable_func() +{ + local cur prev nflag opts + + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + COMPREPLY=() + if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then + COMPREPLY=(-a -d -f -n -p -s) + return 0; + fi + if [[ $prev == '-f' ]]; then + COMPREPLY=( $( compgen -f $cur ) ) + return 0; + fi + for opts in "${COMP_WORDS[@]}" ; do + if [[ $opts == -*n* ]]; then nflag=1; fi + done + + if [ -z "$nflag" ] ; then + COMPREPLY=( $( compgen -A enabled $cur ) ) + else + COMPREPLY=( $( compgen -A disabled $cur ) ) + fi + return 0; +} +complete -F _enable_func enable + +_exec_func() +{ + local cur prev + + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then + COMPREPLY=(-a -c -l) + return 0; + fi + if [[ $prev != -*a* ]]; then + COMPREPLY=( $( compgen -c $cur ) ) + return 0 + fi + return 1; +} +complete -F _exec_func exec + +_fc_func() +{ + local cur prev + + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then + COMPREPLY=(-e -n -l -r -s) + return 0; + fi + if [[ $prev == -*e ]]; then + COMPREPLY=( $(compgen -c $cur) ) + return 0 + fi + return 1 +} +complete -F _fc_func fc + +_hash_func() +{ + local cur prev + + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then + COMPREPLY=(-p -r -t) + return 0; + fi + + if [[ $prev == '-p' ]]; then + COMPREPLY=( $( compgen -f $cur ) ) + return 0; + fi + COMPREPLY=( $( compgen -c $cur ) ) + return 0 +} +complete -F _hash_func hash + +_history_func() +{ + local cur prev + + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + COMPREPLY=() + if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then + COMPREPLY=(-a -c -d -n -r -w -p -s) + return 0; + fi + if [[ $prev == -[anrw] ]]; then + COMPREPLY=( $( compgen -f $cur ) ) + fi + return 0 +} +complete -F _history_func history + +#complete -F _read_func read + +_set_func () +{ + local cur prev + + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + COMPREPLY=() + + _redir_test "$cur" "$prev" && return 0; + + if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then + COMPREPLY=(-a -b -e -f -k -m -n -o -p -t -u -v -x -B -C -H -P --) + return 0; + fi + if [[ $cur == '+' ]]; then + COMPREPLY=(+a +b +e +f +k +m +n +o +p +t +u +v +x +B +C +H +P) + return 0; + fi + if [[ $prev == [+-]o ]]; then + COMPREPLY=( $(compgen -A setopt $cur) ) + return 0; + fi + return 1; +} +complete -F _set_func set + +_trap_func () +{ + local cur + cur=${COMP_WORDS[COMP_CWORD]} + + if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then + COMPREPLY=(-l -p) + return 0; + fi + COMPREPLY=( $( compgen -A signal ${cur}) ) + return 0 +} +complete -F _trap_func trap + +# +# meta-completion (completion for complete/compgen) +# +_complete_meta_func() +{ + local cur prev cmd + COMPREPLY=() + + cmd=$1 + + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + _redir_test "$cur" "$prev" && return 0; + + if (( $COMP_CWORD <= 1 )) || [[ "$cur" == '-' ]]; then + case "$cmd" in + complete) COMPREPLY=(-a -b -c -d -e -f -j -k -s -v -u -r -p -A -G -W -P -S -X -F -C);; + compgen) COMPREPLY=(-a -b -c -d -e -f -j -k -s -v -u -A -G -W -P -S -X -F -C);; + esac + return 0 + fi + + if [[ $prev == -A ]]; then + COMPREPLY=(alias arrayvar binding builtin command directory \ +disabled enabled export file 'function' helptopic hostname job keyword \ +running service setopt shopt signal stopped variable) + return 0 + elif [[ $prev == -F ]]; then + COMPREPLY=( $( compgen -A function $cur ) ) + elif [[ $prev == -C ]]; then + COMPREPLY=( $( compgen -c $cur ) ) + else + COMPREPLY=( $( compgen -c $cur ) ) + fi + return 0 +} +complete -F _complete_meta_func complete compgen + +# +# some completions for shell reserved words +# +#complete -c -k time do if then else elif '{' + +# +# external commands +# + +complete -e printenv + +complete -c nohup exec nice eval trace truss strace sotruss gdb + +_make_targets () +{ + local mdef makef gcmd cur prev i + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + # if prev argument is -f, return possible filename completions. + # we could be a little smarter here and return matches against + # `makefile Makefile *.mk', whatever exists + case "$prev" in + -*f) COMPREPLY=( $(compgen -f $cur ) ); return 0;; + esac + + # if we want an option, return the possible posix options + case "$cur" in + -) COMPREPLY=(-e -f -i -k -n -p -q -r -S -s -t); return 0;; + esac + + # make reads `makefile' before `Makefile' + # GNU make reads `GNUmakefile' before all other makefiles, but we + # check that we're completing `gmake' before checking for it + if [ -f GNUmakefile ] && [ ${COMP_WORDS[0]} == gmake ]; then + mdef=GNUmakefile + elif [ -f makefile ]; then + mdef=makefile + elif [ -f Makefile ]; then + mdef=Makefile + else + mdef=*.mk # local convention + fi + + # before we scan for targets, see if a makefile name was specified + # with -f + for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do + if [[ ${COMP_WORDS[i]} == -*f ]]; then + eval makef=${COMP_WORDS[i+1]} # eval for tilde expansion + break + fi + done + + [ -z "$makef" ] && makef=$mdef + + # if we have a partial word to complete, restrict completions to + # matches of that word + if [ -n "$2" ]; then gcmd='grep "^$2"' ; else gcmd=cat ; fi + + # if we don't want to use *.mk, we can take out the cat and use + # test -f $makef and input redirection + COMPREPLY=( $(cat $makef 2>/dev/null | awk 'BEGIN {FS=":"} /^[^.# ][^=]*:/ {print $1}' | tr -s ' ' '\012' | sort -u | eval $gcmd ) ) +} +complete -F _make_targets -X '+($*|*.[cho])' make gmake pmake + +_umount_func () +{ + COMPREPLY=( $(mount | awk '{print $1}') ) +} +complete -F _umount_func umount + +_configure_func () +{ + case "$2" in + -*) ;; + *) return ;; + esac + + case "$1" in + \~*) eval cmd=$1 ;; + *) cmd="$1" ;; + esac + + COMPREPLY=( $("$cmd" --help | awk '{if ($1 ~ /--.*/) print $1}' | grep ^"$2" | sort -u) ) +} +complete -F _configure_func configure + +complete -W '"${GROUPS[@]}"' newgrp + +complete -f chown ln more cat +complete -d mkdir rmdir +complete -f strip + +complete -f -X '*.gz' gzip +complete -f -X '*.bz2' bzip2 +complete -f -X '*.Z' compress +complete -f -X '!*.+(gz|tgz|Gz)' gunzip gzcat zcat zmore +complete -f -X '!*.Z' uncompress zmore zcat +complete -f -X '!*.bz2' bunzip2 bzcat +complete -f -X '!*.zip' unzip +complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|JPEG|bmp)' xv + +complete -f -X '!*.pl' perl perl5 + +complete -A hostname rsh telnet rlogin ftp ping xping host traceroute nslookup +complete -A hostname rxterm rxterm3 rxvt2 + +complete -u su +complete -g newgrp groupdel groupmod + +complete -f -X '!*.+(ps|PS)' gs gv ghostview psselect pswrap +complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype catdvi +complete -f -X '!*.+(pdf|PDF)' acroread4 +complete -f -X '!*.texi*' makeinfo texi2dvi texi2html +complete -f -X '!*.+(tex|TEX)' tex latex slitex + +complete -f -X '!*.+(mp3|MP3)' mpg123 +complete -f -X '!*.+(htm|html)' links w3m lynx + +# +# other possibilities, left as exercises +# +#complete -F _find_func find +#complete -F _man_func man +#complete -F _stty_func stty diff --git a/examples/complete/complete.freebsd b/examples/complete/complete.freebsd new file mode 100644 index 0000000..7f6f4c2 --- /dev/null +++ b/examples/complete/complete.freebsd @@ -0,0 +1,31 @@ +#Date: Wed, 31 Jan 2001 12:53:56 -0800 +#From: Aaron Smith <aaron@mutex.org> +#To: freebsd-ports@freebsd.org +#Subject: useful bash completion function for pkg commands +#Message-ID: <20010131125356.G52003@gelatinous.com> + +#hi all. i just wanted to share this bash completion function i wrote that +#completes package names for pkg_info and pkg_delete. i find this a great +#help when dealing with port management. programmed completion requires +#bash-2.04. + +_pkg_func () +{ + local cur + + cur=${COMP_WORDS[COMP_CWORD]} + + if [[ $cur == '-' ]]; then + if [[ ${COMP_WORDS[0]} == 'pkg_info' ]]; then + COMPREPLY=(-a -c -d -D -i -k -r -R -p -L -q -I -m -v -e -l) + return 0; + elif [[ ${COMP_WORDS[0]} == 'pkg_delete' ]]; then + COMPREPLY=(-v -D -d -n -f -p) + return 0; + fi + fi + + COMPREPLY=( $(compgen -d /var/db/pkg/$cur | sed sN/var/db/pkg/NNg) ) + return 0 +} +complete -F _pkg_func pkg_delete pkg_info diff --git a/examples/complete/complete.gnu-longopt b/examples/complete/complete.gnu-longopt new file mode 100644 index 0000000..c55b436 --- /dev/null +++ b/examples/complete/complete.gnu-longopt @@ -0,0 +1,43 @@ +# +# Originally from: +# +#Message-ID: <3B13EC65.179451AE@wanadoo.fr> +#Date: Tue, 29 May 2001 20:37:25 +0200 +#From: Manu Rouat <emmanuel.rouat@wanadoo.fr> +#Subject: [bash] Universal command options completion? +# +# +#In the recent versions of bash (after 2.04) programmable +#completion is available. A useful completion function +#is , for a particular command, to enumerate all flags +#that can be used in the command. Now, most GNU unix +#commands have so-called 'long options' for example: +# +#ls --color=always --no-group --size +# +#and these are all listed when you issue a '--help' flag. +#So the idea is to use that, then parse the output of the +#'--help' and reinject this to compgen. The basis of the +#following 'universal' completion funtion was the _configure_func' +#written by Ian McDonnald (or is it Chet Ramey ?) +#A dedicated function will always be better, but this is quite +#convenient. I chose to use 'long options' because they are +#easy to parse and explicit too (it's the point I guess...) +#Lots of room for improvement ! + +_longopt_func () +{ + case "$2" in + -*) ;; + *) return ;; + esac + + case "$1" in + \~*) eval cmd=$1 ;; + *) cmd="$1" ;; + esac + COMPREPLY=( $("$cmd" --help | sed -e '/--/!d' -e 's/.*--\([^ ]*\).*/--\1/'| \ +grep ^"$2" |sort -u) ) +} + +complete -o default -F _longopt_func ldd wget bash id info # some examples that work diff --git a/examples/complete/complete.ianmac b/examples/complete/complete.ianmac new file mode 100644 index 0000000..2af9fc7 --- /dev/null +++ b/examples/complete/complete.ianmac @@ -0,0 +1,433 @@ +##### +#To: chet@po.cwru.edu, sarahmckenna@lucent.com +#Message-Id: <slrn8mqioc.msb.ian@lovelorn.linuxcare.com> +#Posted-To: comp.unix.shell, gnu.bash.bug +#Subject: bash 2.04 programmable completion examples +#Reply-To: ian@linuxcare.com, ian@caliban.org +#Summary: examples of programmable completion for bash 2.04 +#Date: Thu, 13 Jul 2000 00:52:33 -0400 (EDT) +#From: ianmacd@linuxcare.com (Ian Macdonald) +##### + +######################################################################### +# Turn on extended globbing +shopt -s extglob + +# A lot of the following one-liners were taken directly from the +# completion examples provided with the bash 2.04 source distribution + +# Make directory commands see only directories +complete -d cd mkdir rmdir pushd + +# Make file commands see only files +complete -f cat less more chown ln strip +complete -f -X '*.gz' gzip +complete -f -X '*.Z' compress +complete -f -X '!*.+(Z|gz|tgz|Gz)' gunzip zcat zmore +complete -f -X '!*.Z' uncompress zmore zcat +complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' ee xv +complete -f -X '!*.+(ps|PS|ps.gz)' gv +complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype +complete -f -X '!*.+(pdf|PDF)' acroread xpdf +complete -f -X '!*.texi*' makeinfo texi2dvi texi2html +complete -f -X '!*.+(tex|TEX)' tex latex slitex +complete -f -X '!*.+(mp3|MP3)' mpg123 + +# kill sees only signals +complete -A signal kill -P '%' + +# user commands see only users +complete -u finger su usermod userdel passwd + +# bg completes with stopped jobs +complete -A stopped -P '%' bg + +# other job commands +complete -j -P '%' fg jobs disown + +# network commands complete with hostname +complete -A hostname ssh rsh telnet rlogin ftp ping fping host traceroute \ + nslookup + +# export and others complete with shell variables +complete -v export local readonly unset + +# set completes with set options +complete -A setopt set + +# shopt completes with shopt options +complete -A shopt shopt + +# helptopics +complete -A helptopic help + +# unalias completes with aliases +complete -a unalias + +# various commands complete with commands +complete -c command type nohup exec nice eval strace gdb + +# bind completes with readline bindings (make this more intelligent) +complete -A binding bind + +# Now we get to the meat of the file, the functions themselves. Some +# of these are works in progress. Most assume GNU versions of the +# tools in question and may require modifications for use on vanilla +# UNIX systems. +# +# A couple of functions may have non-portable, Linux specific code in +# them, but this will be noted where applicable + + +# GNU chown(1) completion. This should be expanded to allow the use of +# ':' as well as '.' as the user.group separator. +# +_chown () +{ + local cur prev user group + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + # do not attempt completion if we're specifying an option + if [ "${cur:0:1}" = "-" ]; then return 0; fi + + # first parameter on line or first since an option? + if [ $COMP_CWORD -eq 1 ] || [ "${prev:0:1}" = "-" ]; then + case "$cur" in + [a-zA-Z]*.*) + user=${cur%.*} + group=${cur#*.} + COMPREPLY=( $( awk 'BEGIN {FS=":"} \ + {if ($1 ~ /^'$group'/) print $1}' \ + /etc/group ) ) + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + COMPREPLY[i]=$user.${COMPREPLY[i]} + done + return 0 + ;; + *) + COMPREPLY=( $( compgen -u $cur -S '.' ) ) + return 0 + ;; + esac + else + COMPREPLY=( $( compgen -f $cur ) ) + fi + + return 0 +} +complete -F _chown chown + +# umount(8) completion. This relies on the mount point being the third +# space-delimited field in the output of mount(8) +# +_umount () +{ + local cur + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + + # could rewrite the cut | grep to be a sed command, but this is + # clearer and doesn't result in much overhead + COMPREPLY=( $( mount | cut -d' ' -f 3 | grep ^$cur) ) + return 0 +} +complete -F _umount umount + +# GID completion. This will get a list of all valid group names from +# /etc/group and should work anywhere. +# +_gid_func () +{ + local cur + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($1 ~ /^'$cur'/) print $1}' \ + /etc/group ) ) + return 0 +} +complete -F _gid_func groupdel groupmod + +# mount(8) completion. This will pull a list of possible mounts out of +# /etc/fstab, unless the word being completed contains a ':', which +# would indicate the specification of an NFS server. In that case, we +# query the server for a list of all available exports and complete on +# that instead. +# +_mount () + +{ local cur + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + + case "$cur" in + *:*) + COMPREPLY=( $( /usr/sbin/showmount -e --no-headers ${cur%%:*} |\ + grep ^${cur#*:} | awk '{print $1}')) + return 0 + ;; + *) + COMPREPLY=( $( awk '{if ($2 ~ /\//) print $2}' /etc/fstab | \ + grep ^$cur )) + return 0 + ;; + esac +} +complete -F _mount mount + +# Linux rmmod(1) completion. This completes on a list of all currently +# installed kernel modules. +# +_rmmod () +{ + local cur + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + + COMPREPLY=($( lsmod | awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}')) + return 0 +} +complete -F _rmmod rmmod + +# Linux insmod(1) completion. This completes on a list of all +# available modules for the version of the kernel currently running. +# +_insmod () +{ + local cur modpath + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + modpath=/lib/modules/`uname -r` + + COMPREPLY=($( ls -R $modpath | sed -ne 's/^\('$cur'.*\)\.o$/\1/p')) + return 0 +} +complete -F _insmod insmod depmod modprobe + +# man(1) completion. This relies on the security enhanced version of +# GNU locate(1). UNIX variants having non-numeric man page sections +# other than l, m and n should add the appropriate sections to the +# first clause of the case statement. +# +# This is Linux specific, in that 'man <section> <page>' is the +# expected syntax. This allows one to do something like +# 'man 3 str<tab>' to obtain a list of all string handling syscalls on +# the system. +# +_man () +{ + local cur prev + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + [0-9lmn]) + COMPREPLY=($( slocate -ql 0 -r '/man/man'$prev'/'$cur | \ + sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' )) + return 0 + ;; + *) + COMPREPLY=($( slocate -ql 0 -r '/man/man./'$cur | \ + sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' )) + return 0 + ;; + esac +} +complete -F _man man + +# Linux killall(1) completion. This wouldn't be much use on, say, +# Solaris, where killall does exactly that: kills ALL processes. +# +# This could be improved. For example, it currently doesn't take +# command line options into account +# +_killall () +{ + local cur prev + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -[A-Z0-9]*) + # get a list of processes (the first sed evaluation + # takes care of swapped out processes, the second + # takes care of getting the basename of the process) + COMPREPLY=( $( ps ahx | awk '{if ($5 ~ /^'$cur'/) print $5}' | \ + sed -e 's#[]\[]##g' -e 's#^.*/##' )) + return 0 + ;; + esac + + # first parameter can be either a signal or a process + if [ $COMP_CWORD -eq 1 ]; then + # standard signal completion is rather braindead, so we need + # to hack around to get what we want here, which is to + # complete on a dash, followed by the signal name minus + # the SIG prefix + COMPREPLY=( $( compgen -A signal SIG${cur#-} )) + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + COMPREPLY[i]=-${COMPREPLY[i]#SIG} + done + fi + + # get processes, adding to signals if applicable + COMPREPLY=( ${COMPREPLY[*]} $( ps ahx | \ + awk '{if ($5 ~ /^'$cur'/) print $5}' | \ + sed -e 's#[]\[]##g' -e 's#^.*/##' )) + return 0 +} +complete -F _killall killall + +# GNU find(1) completion. This makes heavy use of ksh style extended +# globs and contains Linux specific code for completing the parameter +# to the -fstype option. +# +_find () +{ + local cur prev + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]#-} + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(max|min)depth) + COMPREPLY=( $( compgen -W '0 1 2 3 4 5 6 7 8 9' ) ) + return 0 + ;; + -?(a)newer|-fls|-fprint?(0|f)) + COMPREPLY=( $( compgen -f $cur ) ) + return 0 + ;; + -fstype) + # this is highly non-portable (the option to -d is a tab) + COMPREPLY=( $( cut -d' ' -f 2 /proc/filesystems | grep ^$cur ) ) + return 0 + ;; + -gid) + COMPREPLY=( $( awk 'BEGIN {FS=":"} \ + {if ($3 ~ /^'$cur'/) print $3}' /etc/group ) ) + return 0 + ;; + -group) + COMPREPLY=( $( awk 'BEGIN {FS=":"} \ + {if ($1 ~ /^'$cur'/) print $1}' /etc/group ) ) + return 0 + ;; + -?(x)type) + COMPREPLY=( $( compgen -W 'b c d p f l s' $cur ) ) + return 0 + ;; + -uid) + COMPREPLY=( $( awk 'BEGIN {FS=":"} \ + {if ($3 ~ /^'$cur'/) print $3}' /etc/passwd ) ) + return 0 + ;; + -user) + COMPREPLY=( $( compgen -u $cur ) ) + return 0 + ;; + -[acm]min|-[acm]time|-?(i)?(l)name|-inum|-?(i)path|-?(i)regex| \ + -links|-perm|-size|-used|-exec|-ok|-printf) + # do nothing, just wait for a parameter to be given + return 0 + ;; + esac + + # complete using basic options ($cur has had its dash removed here, + # as otherwise compgen will bomb out with an error, since it thinks + # the dash is an option to itself) + COMPREPLY=( $( compgen -W 'daystart depth follow help maxdepth \ + mindepth mount noleaf version xdev amin anewer atime \ + cmin cnewer ctime empty false fstype gid group ilname \ + iname inum ipath iregex links lname mmin mtime name \ + newer nouser nogroup perm regex size true type uid \ + used user xtype exec fls fprint fprint0 fprintf ok \ + print print0 printf prune ls' $cur ) ) + + # this removes any options from the list of completions that have + # already been specified somewhere on the command line. + COMPREPLY=( $( echo "${COMP_WORDS[@]}-" | \ + (while read -d '-' i; do + [ "$i" == "" ] && continue + # flatten array with spaces on either side, + # otherwise we cannot grep on word boundaries of + # first and last word + COMPREPLY=" ${COMPREPLY[@]} " + # remove word from list of completions + COMPREPLY=( ${COMPREPLY/ ${i%% *} / } ) + done + echo ${COMPREPLY[@]}) + ) ) + + # put dashes back + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + COMPREPLY[i]=-${COMPREPLY[i]} + done + + return 0 +} +complete -F _find find + +# Linux ifconfig(8) completion +# +_ifconfig () +{ + local cur + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + + case "${COMP_WORDS[1]}" in + -|*[0-9]*) + COMPREPLY=( $( compgen -W '-a up down arp promisc allmulti \ + metric mtu dstaddr netmask add del \ + tunnel irq io_addr mem_start media \ + broadcast pointopoint hw multicast \ + address txqueuelen' $cur )) + COMPREPLY=( $( echo " ${COMP_WORDS[@]}" | \ + (while read -d ' ' i; do + [ "$i" == "" ] && continue + # flatten array with spaces on either side, + # otherwise we cannot grep on word + # boundaries of first and last word + COMPREPLY=" ${COMPREPLY[@]} " + # remove word from list of completions + COMPREPLY=( ${COMPREPLY/ $i / } ) + done + echo ${COMPREPLY[@]}) + ) ) + return 0 + ;; + esac + + COMPREPLY=( $( ifconfig -a | sed -ne 's/^\('$cur'[^ ]*\).*$/\1/p' )) +} +complete -F _ifconfig ifconfig + +# Linux ipsec(8) completion (for FreeS/WAN). Very basic. +# +_ipsec () +{ + local cur + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + + COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look manual \ + pluto ranbits rsasigkey setup showdefaults \ + showhostkey spi spigrp tncfg whack' $cur )) +} +complete -F _ipsec ipsec +######################################################################### diff --git a/examples/complete/complete2.ianmac b/examples/complete/complete2.ianmac new file mode 100644 index 0000000..6fb0a96 --- /dev/null +++ b/examples/complete/complete2.ianmac @@ -0,0 +1,271 @@ +##### +#From: ian@linuxcare.com (Ian Macdonald) +#Newsgroups: comp.unix.shell +#Subject: More bash 2.04 completions +#Date: 12 Aug 2000 09:53:40 GMT +#Organization: Linuxcare, Inc. +#Lines: 274 +#Message-ID: <slrn8pa7l2.jgm.ian@lovelorn.linuxcare.com> +#Reply-To: ian@linuxcare.com +##### + +# Turn on extended globbing +shopt -s extglob + +# cvs(1) completion +# +_cvs () +{ + local cur prev + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [ $COMP_CWORD -eq 1 ] || [ "${prev:0:1}" = "-" ]; then + COMPREPLY=( $( compgen -W 'add admin checkout commit diff \ + export history import log rdiff release remove rtag status \ + tag update' $cur )) + else + COMPREPLY=( $( compgen -f $cur )) + fi + return 0 +} +complete -F _cvs cvs + +# rpm(8) completion. This isn't exhaustive yet, but still provides +# quite a lot of functionality. +# +_rpm() +{ + dashify() + { + local i + + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + if [ ${#COMPREPLY[i]} -le 2 ]; then + COMPREPLY[i]=-${COMPREPLY[i]} + else + COMPREPLY[i]=--${COMPREPLY[i]} + fi + done + } + + local cur cur_nodash prev + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + cur_nodash=${cur#-} + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [ $COMP_CWORD = 1 ]; then + # first parameter on line + case "$cur" in + -b*) + COMPREPLY=( $( compgen -W 'ba bb bc bi bl bp bs' \ + $cur_nodash ) ) + dashify + return 0 + ;; + -t*) + COMPREPLY=( $( compgen -W 'ta tb tc ti tl tp ts' \ + $cur_nodash ) ) + dashify + return 0 + ;; + --*) + COMPREPLY=( $( compgen -W 'help version initdb \ + checksig recompile rebuild resign addsign rebuilddb \ + showrc setperms setgids' ${cur_nodash#-} ) ) + dashify; + return 0 + ;; + *) + COMPREPLY=( $( compgen -W 'b e F i q t U V' \ + $cur_nodash ) ) + dashify + return 0 + ;; + esac + fi + + case "${COMP_WORDS[1]}" in + -[iFU]*) + # complete on list of relevant options + COMPREPLY=( $( compgen -W 'percent force test replacepkgs \ + replacefiles root excludedocs includedocs noscripts rcfile \ + ignorearch dbpath prefix ignoreos nodeps allfiles ftpproxy \ + ftpport justdb httpproxy httpport noorder relocate badreloc \ + notriggers excludepath ignoresize oldpackage' ${cur_nodash#-} )) + dashify; + # return if $cur is an option + [ "${cur:0:1}" = "-" ] && return 0 + # add a list of RPMS to possible completions + COMPREPLY=( ${COMPREPLY[@]} $( compgen -G $cur\*.rpm ) ) + return 0 + ;; + -qp*) + # complete on list of relevant options + COMPREPLY=( $( compgen -W 'scripts root rcfile whatprovides \ + whatrequires requires triggeredby ftpport ftpproxy httpproxy \ + httpport provides triggers dump changelog dbpath filesbypkg' \ + ${cur_nodash#-} ) ) + dashify; + # return if $cur is an option + [ "${cur:0:1}" = "-" ] && return 0 + # add a list of RPMS to possible completions + COMPREPLY=( ${COMPREPLY[@]} $( compgen -G $cur\*.rpm ) ) + return 0 + ;; + -*f) + # standard filename completion + COMPREPLY=( $( compgen -f $cur ) ) + return 0 + ;; + -e) + # complete on list of relevant options + COMPREPLY=( $( compgen -W 'allmatches noscripts notriggers \ + nodeps test' ${cur_nodash#-} ) ) + dashify; + # return if $cur is an option + [ "${cur:0:1}" = "-" ] && return 0 + # complete on basename of installed RPMs + COMPREPLY=( $( rpm -qa | \ + sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) ) + return 0 + ;; + -qa*) + # complete on list of relevant options + COMPREPLY=( $( compgen -W 'scripts root rcfile whatprovides \ + whatrequires requires triggeredby ftpport ftpproxy httpproxy \ + httpport provides triggers dump changelog dbpath specfile \ + querybynumber last filesbypkg' ${cur_nodash#-} ) ) + dashify; + return 0 + ;; + -q*) + # complete on list of relevant options + COMPREPLY=( $( compgen -W 'scripts root rcfile whatprovides \ + whatrequires requires triggeredby ftpport ftpproxy httpproxy \ + httpport provides triggers dump changelog dbpath specfile \ + querybynumber last filesbypkg' ${cur_nodash#-} ) ) + dashify; + # return if $cur is an option + [ "${cur:0:1}" = "-" ] && return 0 + # add a list of RPMS to possible completions + COMPREPLY=( ${COMPREPLY[@]} $( rpm -qa | \ + sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) ) + return 0 + ;; + -[Vy]*) + # complete on list of relevant options + COMPREPLY=( $( compgen -W 'root rcfile dbpath nodeps nofiles \ + noscripts nomd5 nopgp' ${cur_nodash#-} ) ) + dashify; + # return if $cur is an option + [ "${cur:0:1}" = "-" ] && return 0 + # add a list of RPMS to possible completions + COMPREPLY=( ${COMPREPLY[@]} $( rpm -qa | \ + sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) ) + return 0 + ;; + -b*) + # complete on list of relevant options + COMPREPLY=( $( compgen -W 'short-circuit timecheck clean \ + rmsource test sign buildroot target buildarch buildos' \ + ${cur_nodash#-} ) ) + dashify; + # return if $cur is an option + [ "${cur:0:1}" = "-" ] && return 0 + # complete on .spec files + COMPREPLY=( $( compgen -G $cur\*.spec ) ) + return 0 + ;; + -t*) + # complete on list of relevant options + COMPREPLY=( $( compgen -W 'short-circuit timecheck clean \ + rmsource test sign buildroot target buildarch buildos' \ + ${cur_nodash#-} ) ) + dashify; + # return if $cur is an option + [ "${cur:0:1}" = "-" ] && return 0 + # complete on .tar.gz files + COMPREPLY=( $( compgen -G $cur\*.tar.gz ) ) + return 0 + ;; + --re@(build|compile)) + # complete on source RPMs + COMPREPLY=( $( compgen -G $cur\*.src.rpm ) ) + return 0 + ;; + --@(checksig|@(re|add)sign)) + # complete on RPMs + COMPREPLY=( $( compgen -G $cur\*.rpm ) ) + return 0 + ;; + --set@(perms|gids)) + # complete on installed RPMs + COMPREPLY=( ${COMPREPLY[@]} $( rpm -qa | \ + sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) ) + return 0 + ;; + esac +} +complete -F _rpm rpm + +# chsh(1) completion +# +_chsh() +{ + local cur prev + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [ "$prev" = "-s" ]; then + COMPREPLY=( $( chsh -l | grep ^$cur ) ) + else + COMPREPLY=( $( compgen -u $cur ) ) + fi +} +complete -F _chsh chsh + +# chkconfig(8) completion +# +_chkconfig() +{ + local cur prev + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + cur_nodash=${cur#--} + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -W 'list add del level' $cur_nodash ) ) + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + COMPREPLY[i]=--${COMPREPLY[i]} + done + return 0 + fi + + if [ $COMP_CWORD -eq 4 ]; then + COMPREPLY=( $( compgen -W 'on off reset' $cur ) ) + return 0 + fi + + case "$prev" in + @([1-6]|--@(list|add|del))) + COMPREPLY=( $( compgen -W "`(cd /etc/rc.d/init.d; echo *)`" \ + $cur) ) + return 0 + ;; + --level) + COMPREPLY=( $( compgen -W '1 2 3 4 5 6' $cur ) ) + return 0 + ;; + esac +} +complete -F _chkconfig chkconfig +### diff --git a/examples/functions/array-stuff b/examples/functions/array-stuff new file mode 100644 index 0000000..97ed512 --- /dev/null +++ b/examples/functions/array-stuff @@ -0,0 +1,103 @@ +# usage: reverse arrayname +reverse() +{ + local -a R + local -i i + local rlen temp + + # make r a copy of the array whose name is passed as an arg + eval R=\( \"\$\{$1\[@\]\}\" \) + + # reverse R + rlen=${#R[@]} + + for ((i=0; i < rlen/2; i++ )) + do + temp=${R[i]} + R[i]=${R[rlen-i-1]} + R[rlen-i-1]=$temp + done + + # and assign R back to array whose name is passed as an arg + eval $1=\( \"\$\{R\[@\]\}\" \) +} + +A=(1 2 3 4 5 6 7) +echo "${A[@]}" +reverse A +echo "${A[@]}" +reverse A +echo "${A[@]}" + +# unset last element of A +alen=${#A[@]} +unset A[$alen-1] +echo "${A[@]}" + +# ashift -- like shift, but for arrays + +ashift() +{ + local -a R + local n + + case $# in + 1) n=1 ;; + 2) n=$2 ;; + *) echo "$FUNCNAME: usage: $FUNCNAME array [count]" >&2 + exit 2;; + esac + + # make r a copy of the array whose name is passed as an arg + eval R=\( \"\$\{$1\[@\]\}\" \) + + # shift R + R=( "${R[@]:$n}" ) + + # and assign R back to array whose name is passed as an arg + eval $1=\( \"\$\{R\[@\]\}\" \) +} + +ashift A 2 +echo "${A[@]}" + +ashift A +echo "${A[@]}" + +ashift A 7 +echo "${A[@]}" + +# Sort the members of the array whose name is passed as the first non-option +# arg. If -u is the first arg, remove duplicate array members. +array_sort() +{ + local -a R + local u + + case "$1" in + -u) u=-u ; shift ;; + esac + + if [ $# -eq 0 ]; then + echo "array_sort: argument expected" >&2 + return 1 + fi + + # make r a copy of the array whose name is passed as an arg + eval R=\( \"\$\{$1\[@\]\}\" \) + + # sort R + R=( $( printf "%s\n" "${A[@]}" | sort $u) ) + + # and assign R back to array whose name is passed as an arg + eval $1=\( \"\$\{R\[@\]\}\" \) + return 0 +} + +A=(3 1 4 1 5 9 2 6 5 3 2) +array_sort A +echo "${A[@]}" + +A=(3 1 4 1 5 9 2 6 5 3 2) +array_sort -u A +echo "${A[@]}" diff --git a/examples/functions/array-to-string b/examples/functions/array-to-string new file mode 100644 index 0000000..0d2fbe5 --- /dev/null +++ b/examples/functions/array-to-string @@ -0,0 +1,15 @@ +#! /bin/bash + +# Format: array_to_string vname_of_array vname_of_string separator +array_to_string() +{ + (( ($# < 2) || ($# > 3) )) && { + "$FUNCNAME: usage: $FUNCNAME arrayname stringname [separator]" + return 2 + } + + local array=$1 string=$2 + ((3==$#)) && [[ $3 = ? ]] && local IFS="${3}${IFS}" + eval $string="\"\${$array[*]}\"" + return 0 +} diff --git a/examples/functions/autoload b/examples/functions/autoload new file mode 100644 index 0000000..a563a77 --- /dev/null +++ b/examples/functions/autoload @@ -0,0 +1,111 @@ +# +# An almost ksh-compatible `autoload'. A function declared as `autoload' will +# be read in from a file the same name as the function found by searching the +# $FPATH (which works the same as $PATH), then that definition will be run. +# +# To do this without source support, we define a dummy function that, when +# executed, will load the file (thereby re-defining the function), then +# execute that newly-redefined function with the original arguments. +# +# It's not identical to ksh because ksh apparently does lazy evaluation +# and looks for the file to load from only when the function is referenced. +# This one requires that the file exist when the function is declared as +# `autoload'. +# +# usage: autoload func [func...] +# +# The first cut of this was by Bill Trost, trost@reed.bitnet +# +# Chet Ramey +# chet@ins.CWRU.Edu + +# +# Declare a function ($1) to be autoloaded from a file ($2) when it is first +# called. This defines a `temporary' function that will `.' the file +# containg the real function definition, then execute that new definition with +# the arguments given to this `fake' function. The autoload function defined +# by the file and the file itself *must* be named identically. +# + +aload() +{ + eval $1 '() { . '$2' ; '$1' "$@" ; return $? ; }' +} + +# +# Search $FPATH for a file the same name as the function given as $1, and +# autoload the function from that file. There is no default $FPATH. +# + +autoload() +{ + # + # Save the list of functions; we're going to blow away the arguments + # in a second. If any of the names contain white space, TFB. + # + + local args="$*" + + # + # This should, I think, list the functions marked as autoload and not + # yet defined, but we don't have enough information to do that here. + # + if [ $# -eq 0 ] ; then + echo "usage: autoload function [function...]" >&2 + return 1 + fi + + # + # If there is no $FPATH, there is no work to be done + # + + if [ -z "$FPATH" ] ; then + echo autoload: FPATH not set or null >&2 + return 1 + fi + + # + # This treats FPATH exactly like PATH: a null field anywhere in the + # FPATH is treated the same as the current directory. + # + # The path splitting command is taken from Kernighan and Pike + # + +# fp=$(echo $FPATH | sed 's/^:/.:/ +# s/::/:.:/g +# s/:$/:./ +# s/:/ /g') + + # replaced with builtin mechanisms 2001 Oct 10 + + fp=${FPATH/#:/.:} + fp=${fp//::/:.:} + fp=${fp/%:/:.} + fp=${fp//:/ } + + for FUNC in $args ; do + # + # We're blowing away the arguments to autoload here... + # We have to; there are no arrays (well, there are, but + # this doesn't use them yet). + # + set -- $fp + + while [ $# -ne 0 ] ; do + if [ -f $1/$FUNC ] ; then + break # found it! + fi + shift + done + + if [ $# -eq 0 ] ; then + echo "$FUNC: autoload function not found" >&2 + continue + fi + +# echo auto-loading $FUNC from $1/$FUNC + aload $FUNC $1/$FUNC + done + + return 0 +} diff --git a/examples/functions/autoload.v2 b/examples/functions/autoload.v2 new file mode 100644 index 0000000..e8f3433 --- /dev/null +++ b/examples/functions/autoload.v2 @@ -0,0 +1,192 @@ +# +# An almost ksh-compatible `autoload'. A function declared as `autoload' will +# be read in from a file the same name as the function found by searching the +# $FPATH (which works the same as $PATH), then that definition will be run. +# +# To do this without source support, we define a dummy function that, when +# executed, will load the file (thereby re-defining the function), then +# execute that newly-redefined function with the original arguments. +# +# It's not identical to ksh because ksh apparently does lazy evaluation +# and looks for the file to load from only when the function is referenced. +# This one requires that the file exist when the function is declared as +# `autoload'. +# +# usage: autoload [-pu] [func ...] +# +# options: +# -p print in a format that can be reused as input +# -u unset each function and remove it from the autoload list +# +# The first cut of this was by Bill Trost, trost@reed.edu +# +# Chet Ramey +# chet@ins.CWRU.Edu + +unset _AUTOLOADS +_aindex=0 + +# +# Declare a function ($1) to be autoloaded from a file ($2) when it is first +# called. This defines a `temporary' function that will `.' the file +# containg the real function definition, then execute that new definition with +# the arguments given to this `fake' function. The autoload function defined +# by the file and the file itself *must* be named identically. +# + +_aload() +{ + eval $1 '() { . '$2' ; '$1' "$@" ; return $? ; }' + _autoload_addlist "$1" +} + +_autoload_addlist() +{ + local i=0 + + while (( i < $_aindex )); do + case "${_AUTOLOADS[i]}" in + "$1") return 1 ;; + esac + (( i += 1 )) + done + _AUTOLOADS[_aindex]="$1" + (( _aindex += 1 )) + return 0 +} + +_autoload_dump() +{ + local func + + for func in ${_AUTOLOADS[@]}; do + [ -n "$1" ] && echo -n "autoload " + echo "$func" + done +} + +# Remove $1 from the list of autoloaded functions +_autoload_remove_one() +{ + local i=0 nnl=0 + local -a nlist + + while (( i < _aindex )); do + case "${_AUTOLOADS[i]}" in + "$1") ;; + *) nlist[nnl]="${_AUTOLOADS[i]}" ; (( nnl += 1 ));; + esac + (( i += 1 )) + done + unset _AUTOLOADS _aindex + eval _AUTOLOADS=( ${nlist[@]} ) + _aindex=$nnl +} + +# Remove all function arguments from the list of autoloaded functions +_autoload_remove() +{ + local func i es=0 + + # first unset the autoloaded functions + for func; do + i=0 + while (( i < _aindex )); do + case "${_AUTOLOADS[i]}" in + "$func") unset -f $func ; break ;; + esac + (( i += 1 )) + done + if (( i == _aindex )); then + echo "autoload: $func: not an autoloaded function" >&2 + es=1 + fi + done + + # then rebuild the list of autoloaded functions + for func ; do + _autoload_remove_one "$func" + done + + return $es +} + +# +# Search $FPATH for a file the same name as the function given as $1, and +# autoload the function from that file. There is no default $FPATH. +# + +autoload() +{ + local -a fp + local _autoload_unset nfp i + + if (( $# == 0 )) ; then + _autoload_dump + return 0 + fi + + OPTIND=1 + while getopts pu opt + do + case "$opt" in + p) _autoload_dump printable; return 0;; + u) _autoload_unset=y ;; + *) echo "autoload: usage: autoload [-pu] [function ...]" >&2 + return 1 ;; + esac + done + + shift $(( $OPTIND - 1 )) + + if [ -n "$_autoload_unset" ]; then + _autoload_remove "$@" + return $? + fi + + # + # If there is no $FPATH, there is no work to be done + # + + if [ -z "$FPATH" ] ; then + echo "autoload: FPATH not set or null" >&2 + return 1 + fi + + # + # This treats FPATH exactly like PATH: a null field anywhere in the + # FPATH is treated the same as the current directory. + # + # This turns $FPATH into an array, substituting `.' for `' + # + eval fp=( $( + IFS=':' + set -- ${FPATH} + for p in "$@" ; do echo -n "${p:-.} "; done + ) + ) + + nfp=${#fp[@]} + + for FUNC ; do + i=0; + while (( i < nfp )) ; do + if [ -f ${fp[i]}/$FUNC ] ; then + break # found it! + fi + (( i += 1 )) + done + + if (( i == nfp )) ; then + echo "autoload: $FUNC: autoload function not found" >&2 + es=1 + continue + fi + +# echo auto-loading $FUNC from ${fp[i]}/$FUNC + _aload $FUNC ${fp[i]}/$FUNC + es=0 + done + + return $es +} diff --git a/examples/functions/autoload.v3 b/examples/functions/autoload.v3 new file mode 100644 index 0000000..a82ffe9 --- /dev/null +++ b/examples/functions/autoload.v3 @@ -0,0 +1,125 @@ +#From: Mark Kennedy <mtk@ny.ubs.com> +#Message-ID: <35E2B899.63A02DF5@ny.ubs.com> +#Date: Tue, 25 Aug 1998 09:14:01 -0400 +#To: chet@nike.ins.cwru.edu +#Subject: a newer version of the ksh-style 'autoload' + +#enclosed you'll find 'autoload.v3', a version of the autoloader +#that emulates the ksh semantics of delaying the resolution (and loading) of the function +#until its first use. i took the liberty of simplifying the code a bit although it still uses the +#same functional breakdown. i recently went through the exercise of converting +#my ksh-based environment to bash (a very, very pleasant experience) +#and this popped out. + +# the psuedo-ksh autoloader. + +# The first cut of this was by Bill Trost, trost@reed.bitnet. +# The second cut came from Chet Ramey, chet@ins.CWRU.Edu +# The third cut came from Mark Kennedy, mtk@ny.ubs.com. 1998/08/25 + +unset _AUTOLOADS + +_aload() +{ + local func + for func; do + eval $func '() + { + local f=$(_autoload_resolve '$func') + if [[ $f ]]; then + . $f + '$func' "$@" + return $? + else + return 1 + fi + }' + _autoload_addlist $func + done +} + +_autoload_addlist() +{ + local func + + for func in ${_AUTOLOADS[@]}; do + [[ $func = "$1" ]] && return + done + + _AUTOLOADS[${#_AUTOLOADS[@]}]=$1 +} + +_autoload_dump() +{ + local func + + for func in ${_AUTOLOADS[@]}; do + [[ $1 ]] && echo -n "autoload " + echo $func + done +} + +_autoload_remove_one() +{ + local func + local -a NEW_AUTOLOADS + + for func in ${_AUTOLOADS[@]}; do + [[ $func != "$1" ]] && NEW_AUTOLOADS[${#NEW_AUTOLOADS[@]}]=$func + done + + _AUTOLOADS=( ${NEW_AUTOLOADS[@]} ) +} + +_autoload_remove() +{ + local victim func + + for victim; do + for func in ${_AUTOLOADS[@]}; do + [[ $victim = "$func" ]] && unset -f $func && continue 2 + done + echo "autoload: $func: not an autoloaded function" >&2 + done + + for func; do + _autoload_remove_one $func + done +} + +_autoload_resolve() +{ + if [[ ! "$FPATH" ]]; then + echo "autoload: FPATH not set or null" >&2 + return + fi + + local p + + for p in $( (IFS=':'; set -- ${FPATH}; echo "$@") ); do + p=${p:-.} + if [ -f $p/$1 ]; then echo $p/$1; return; fi + done + + echo "autoload: $1: function source file not found" >&2 +} + +autoload() +{ + if (( $# == 0 )) ; then _autoload_dump; return; fi + + local opt OPTIND + + while getopts pu opt + do + case $opt in + p) _autoload_dump printable; return;; + u) shift $((OPTIND-1)); _autoload_remove "$@"; return;; + *) echo "autoload: usage: autoload [-pu] [function ...]" >&2; return;; + esac + done + + shift $(($OPTIND-1)) + + _aload "$@" +} diff --git a/examples/functions/basename b/examples/functions/basename new file mode 100644 index 0000000..a541349 --- /dev/null +++ b/examples/functions/basename @@ -0,0 +1,23 @@ +# Date: Fri, 11 Oct 91 11:22:36 edt +# From: friedman@gnu.ai.mit.edu +# To: bfox@gnu.ai.mit.edu + +# A replacement for basename(1). Not all the systems I use have this +# program. Usage: basename [path] {extension} +function basename () +{ + local path="$1" + local suffix="$2" + local tpath="${path%/}" + + # Strip trailing '/' characters from path (unusual that this should + # ever occur, but basename(1) seems to deal with it.) + while [ "${tpath}" != "${path}" ]; do + tpath="${path}" + path="${tpath%/}" + done + + path="${path##*/}" # Strip off pathname + echo ${path%${suffix}} # Also strip off extension, if any. +} + diff --git a/examples/functions/basename2 b/examples/functions/basename2 new file mode 100644 index 0000000..a42231c --- /dev/null +++ b/examples/functions/basename2 @@ -0,0 +1,43 @@ +#From: "Grigoriy Strokin" <grg@philol.msu.ru> +#Newsgroups: comp.unix.shell +#Subject: fast basename and dirname functions for BASH/SH +#Date: Sat, 27 Dec 1997 21:18:40 +0300 +# +#Please send your comments to grg@philol.msu.ru + +function basename() +{ + local name="${1##*/}" + echo "${name%$2}" +} + +function dirname() +{ + local dir="${1%${1##*/}}" + [ "${dir:=./}" != "/" ] && dir="${dir%?}" + echo "$dir" +} + +# Two additional functions: +# 1) namename prints the basename without extension +# 2) ext prints extension of a file, including "." + +function namename() +{ + local name=${1##*/} + local name0="${name%.*}" + echo "${name0:-$name}" +} +function ext() +{ + local name=${1##*/} + local name0="${name%.*}" + local ext=${name0:+${name#$name0}} + echo "${ext:-.}" +} + + + + + + diff --git a/examples/functions/coproc.bash b/examples/functions/coproc.bash new file mode 100644 index 0000000..61dc8d7 --- /dev/null +++ b/examples/functions/coproc.bash @@ -0,0 +1,108 @@ +# coprocess.bash +# +# vi:set sts=2 sw=2 ai: +# + +coprocess_pid= + +# +# coprocess - Start, control, and end coprocesses. +# +function coprocess () +{ + while (( $# > 0 )) ; do + case "$1" in + # + # coprocess close + # + c|cl|clo|clos|close) + shift + exec 61>&- 62<&- + coprocess_pid= + if [ "$1" = "-SIGPIPE" ] ; then + # Only print message in an interactive shell + case "$-" in + *i*) + echo 'SIGPIPE' >&2 + ;; + esac + return 1 + fi + return 0 + ;; + + # + # coprocess open + # + o|op|ope|open) + shift + local fifo="/var/tmp/coprocess.$$.$RANDOM" + + local cmd="/bin/bash" + if (( $# > 0 )) ; then + cmd="$@" + fi + + mkfifo "$fifo.in" || return $? + mkfifo "$fifo.out" || { + ret=$? + rm -f "$fifo.in" + return $? + } + + ( "$@" <$fifo.in >$fifo.out ; rm -f "$fifo.in" "$fifo.out" ) & + coprocess_pid=$! + exec 61>$fifo.in 62<$fifo.out + return 0 + ;; + + # + # coprocess print - write to the coprocess + # + p|pr|pri|prin|print) + shift + local old_trap=$(trap -p SIGPIPE) + trap 'coprocess close -SIGPIPE' SIGPIPE + if [ $# -eq 1 ] && [ "$1" = "--stdin" ] ; then + cat >&61 + else + echo "$@" >&61 + fi + local ret=$? + eval "$old_trap" + return $ret + ;; + + # + # coprocess read - read from the coprocess + # + r|re|rea|read) + shift + local old_trap=$(trap -p SIGPIPE) + trap '_coprocess_close -SIGPIPE' SIGPIPE + builtin read "$@" <&62 + local ret=$? + eval "$old_trap" + return $ret + ;; + + s|st|sta|stat|statu|status) + if [ -z "$coprocess_pid" ] ; then + echo 'no active coprocess' + return 1 + else + echo " coprocess is active [$coprocess_pid]" + return 0 + fi + ;; + + *) + coprocess print "$@" + return $? + ;; + esac + shift + done + coprocess status + return $? +} diff --git a/examples/functions/coshell.README b/examples/functions/coshell.README new file mode 100644 index 0000000..9675cda --- /dev/null +++ b/examples/functions/coshell.README @@ -0,0 +1,53 @@ +Date: Fri, 21 Sep 2001 14:50:29 -0400 +From: "Jason M. Felice" <jfelice@cronosys.com> +To: bash-maintainers@gnu.org, chet@po.cwru.edu +Subject: Bash co-processes functions +Message-ID: <20010921145029.A6093@argo.eraserhead.net> +Mime-Version: 1.0 + +Attached to this message you will find coprocess.bash and coshell.bash. +Here's a brief synopsis of use: + +coprocess open telnet localhost +while coprocess read il ; do + echo "$il" + case "$il" in + *ogin:*) + coprocess print 'user' + ;; + *ord:*) + echo 'pass' |coprocess print --stdin + ;; + *$ *) + coprocess print 'exit' + break + ;; + esac +done +coprocess close + +And here's an example of the coshell function: + +coshell open ssh -l root otherbox +coshell eval hostname +coshell ls -l +if coshell test -d /tmp ; then echo 'otherbox has a /tmp!' ; fi + +coshell sendfile /var/lib/upgrade.rpm /tmp/test.rpm || exit $? +coshell eval rpm -ivh /tmp/test.rpm || exit $? +coshell eval rm -f /tmp/test.rpm || exit $? +coshell close +exit 0 + +There are a few minor issues that I'd like to work out, but it works well +enough for me ;-) The issues are: + +- Shell quoting issue with 'coshell eval' commands - need to somehow + re-quote words. +- Interactive commands hang 'coshell eval', tried redirecting in </dev/null + to executed command, but it caused strange shell exit problems. +- Some way to copy stdin from local coshell eval to remote shell. Probably + logically impossible, but would be wonderfully useful. + +I'm using it for writing scripts to publish websites and other scripts to +co-located servers. diff --git a/examples/functions/coshell.bash b/examples/functions/coshell.bash new file mode 100644 index 0000000..dc177b3 --- /dev/null +++ b/examples/functions/coshell.bash @@ -0,0 +1,127 @@ +# vi:set sts=2 sw=2 ai: +# +# coshell.bash - Control shell coprocesses (see coprocess.bash). +# + +function coshell () +{ + while (( $# > 0 )) ; do + case "$1" in + # + # coshell open + # + o|op|ope|open) + shift + coprocess open "$@" + local ret=$? + + # This should eat any ssh error messages or what not. + coshell eval : >/dev/null 2>&1 + return $ret + ;; + + # + # coshell close + # + c|cl|clo|close) + shift + coprocess close "$@" + return $? + ;; + + # + # coshell eval + # + e|ev|eva|eval) + shift + local cookie=$RANDOM + if (( $# == 0 )) ; then + echo "coshell eval: no argumentsl" >&2 + return 1 + fi + if [ x$coprocess_pid = x ] ; then + echo "coshell eval: no active coshell" >&2 + return 1 + fi + + coprocess print "$@" + coprocess print "coprocess_rc=\$?" + coprocess print "printf 'coprocess-$cookie----\n%d\n' \$coprocess_rc" + if [ x$coprocess_pid = x ] ; then + return 0 + fi + + local ol + while coprocess read ol ; do + case "$ol" in + *coprocess-$cookie----*) + ol="${ol%coprocess-$cookie----}" + echo -n "$ol" + break + ;; + esac + echo "$ol" + done + coprocess read ol + return $ol + ;; + + # + # coshell sendfile + # + s|se|sen|send|sendf|sendfi|sendfil|sendfile) + shift + if (( $# != 2 )) ; then + echo "coshell sendfile: syntax is 'coshell sendfile SRC TARGET'" >&2 + return 1 + fi + if [ x$coprocess_pid = x ] ; then + echo "coshell sendfile: no active coshell" >&2 + return 1 + fi + + local target=$2 + if coshell test -d "$target" ; then + target="$target/${1##*/}" + fi + + coprocess print "uudecode <<END_OF_FILE" + uuencode -m "$target" <$1 |coprocess print --stdin + coshell eval "END_OF_FILE" + return $? + ;; + + # + # coshell getfile + # + g|ge|get|getf|getfi|getfil|getfile) + shift + if (( $# != 2 )) ; then + echo "coshell getfile: syntax is 'coshell getfile SRC TARGET'" >&2 + return 1 + fi + if [ x$coprocess_pid = x ] ; then + echo "coshell getfile: no active coshell" >&2 + return 1 + fi + + local target=$2 + if test -d "$target" ; then + target="$target/${1##*/}" + fi + + coshell eval uuencode -m "$target" "<" "$1" |uudecode + return $? + ;; + + *) + coshell eval "$@" + return $? + ;; + esac + shift + done + coprocess status + return $? +} + diff --git a/examples/functions/csh-compat b/examples/functions/csh-compat new file mode 100644 index 0000000..b8dcf8f --- /dev/null +++ b/examples/functions/csh-compat @@ -0,0 +1,48 @@ +# C-shell compatabilty package. +# setenv VAR VALUE +function setenv () +{ + export $1="$2" +} + +function unsetenv () +{ + unset $1 +} + +# Can't write foreach yet. Need pattern matching, and a few extras. +function foreach () { +echo 'Can'\''t do `foreach'\'' yet. Type "help for".' +} + +# Make this work like csh's. Special case "term" and "path". +#set () { +#} + +chdir () +{ + builtin cd "$@" +} + +# alias - convert csh alias commands to bash functions +# from Mohit Aron <aron@cs.rice.edu> +# posted to usenet as <4i5p17$bnu@larry.rice.edu> +function alias () +{ + if [ "x$2" = "x" ] + then + declare -f $1 + else + case $2 in + *[#\!]*) + comm=$(echo $2 | sed 's/\\!\*/\"$\@\"/g + s/\\!:\([1-9]\)/\"$\1\"/g + s/#/\\#/g') + ;; + *) + comm="$2 \"\$@\"" ;; + esac + + eval function $1 \(\) "{" command "$comm" "; }" + fi +} diff --git a/examples/functions/dirfuncs b/examples/functions/dirfuncs new file mode 100644 index 0000000..3958bbe --- /dev/null +++ b/examples/functions/dirfuncs @@ -0,0 +1,142 @@ +# +# Directory manipulation functions from the book 'The Korn Shell' +# Modified for use with bash Mon Apr 18 08:37 1994 by +# Ken Konecki (kenk@wfg.com) +# +# Modified by Chet Ramey +# +# This could stand to have calls to `select' added back in +# + +alias integer="declare -i" + +integer _push_max=${CDSTACK-31} _push_top=${CDSTACK-31} + +unalias cd +# alias cd=_cd + +# Display directory stack -- $HOME display as ~ +dirs() +{ + dir="${PWD#$HOME/}" + case $dir in + $HOME) dir=\~ ;; + /*) ;; + *) dir=\~/$dir ;; + esac + + integer i=_push_top + integer n=1 + + echo "$n) $dir" + while let "i < $_push_max" + do + n=n+1 + eval "echo \$n\) \$_push_stack_$i" + i=i+1 + done +} + +# Change directory and put directory on front of stack +cd() +{ + typeset dir= + integer n=0 type=4 i + case $1 in + -|-1|2) # cd - + n=_push_top type=1 + ;; + -[1-9]|-[1-9][0-9]) # cd -n + n=_push_top+${1#-}-1 type=2 + ;; + + 1) # keep present directory + echo "$PWD" + return + ;; + + [2-9]|[1-9][0-9]) # cd n + n=_push_top+${1}-2 type=2 + ;; + + *) + if let "_push_top <= 0"; then + type=3 n=_push_max + fi + ;; + esac + + if let "type < 3"; then + if let "n >= _push_max"; then + echo cd: Directory stack not that deep + return 1 + else + eval dir=\${_push_stack_$n} + fi + fi + + case $dir in + ~*) dir=$HOME${dir#\~} ;; + esac + + cd2 ${dir:-$@} > /dev/null || return 1 + dir=${OLDPWD#$HOME/} + case $dir in + $HOME) dir=\~ ;; + /*) ;; + *) dir=\~/$dir ;; + esac + + case $type in + 1) # swap first two elements + eval _push_stack_$_push_top=\$dir ;; + + 2|3) # put $dir on top and shift down by one until top + i=_push_top + unset _dirlist + while let "i < $_push_max" ; do + eval _dirlist=\"\$_dirlist \$_push_stack_$i\" + i=i+1 + done + + i=_push_top + for dir in "$dir" ${_dirlist} ; do + let "i > n" && break + eval _push_stack_$i=\$dir + i=i+1 + done + ;; + 4) # push name + _push_top=_push_top-1; + eval _push_stack_$_push_top=\$dir + ;; + esac + + echo "$PWD" + +} + +# Menu-driven change directory command +function mcd +{ + dirs + echo -n "Select by number or enter a name: " + read + cd $REPLY +} + + +# Emulate ksh cd substitution +cd2() +{ + case "$#" in + 0) builtin cd "$HOME" ;; + 1) builtin cd "$1" ;; + 2) newDir=$(echo $PWD | sed -e "s:$1:$2:g") + case "$newDir" in + $PWD) echo "bash:: cd: bad substitution" >&2 ; return 1 ;; + *) builtin cd "$newDir" ;; + esac ;; + *) echo "bash: cd: wrong arg count" 1>&2 ; return 1 ;; + esac +} diff --git a/examples/functions/dirname b/examples/functions/dirname new file mode 100644 index 0000000..ccb8c84 --- /dev/null +++ b/examples/functions/dirname @@ -0,0 +1,21 @@ +# Date: Fri, 11 Oct 91 11:22:36 edt +# From: friedman@gnu.ai.mit.edu +# To: bfox@gnu.ai.mit.edu + +# A replacement for dirname(1). This one appears less often on some +# systems I use than basename(1), and I really depend on it for some +# things. Usage: dirname [path] +function dirname () +{ + local dir="$1" + local tdir="${dir%/}" + + # Strip trailing '/' characters from dir (unusual that this should + # ever occur, but dirname(1) seems to deal with it.) + while [ "${tdir}" != "${dir}" ]; do + tdir="${dir}" + dir="${tdir%/}" + done + + echo "${dir%/*}" +} diff --git a/examples/functions/emptydir b/examples/functions/emptydir new file mode 100644 index 0000000..412af5b --- /dev/null +++ b/examples/functions/emptydir @@ -0,0 +1,28 @@ +#! /bin/bash +# +#Derived from: +# +#From: damercer@mmm.com (Dan Mercer) +#Newsgroups: comp.unix.admin,comp.unix.shell,comp.unix.programmer,comp.sys.sun.admin +#Subject: Re: Command to find out if a directory is empty +#Date: 17 Aug 2000 14:35:56 GMT +#Message-ID: <8ngt8c$fmr$1@magnum.mmm.com> + +# usage: emptydir [dirname] ; default dirname is "." + +emptydir() +{ + typeset file dir=${1:-.} + [[ -d $dir ]] || { + echo "$FUNCNAME: $dir is not a directory" >&2 + return 2 + } + for file in $dir/.* $dir/* + do + case ${file#$dir/} in + .|..) ;; + \*) [[ -e $file ]];let $?;return;; + *) return 1;; + esac + done +} diff --git a/examples/functions/exitstat b/examples/functions/exitstat new file mode 100644 index 0000000..f49ebf5 --- /dev/null +++ b/examples/functions/exitstat @@ -0,0 +1,22 @@ +# Contributed by Noah Friedman and Roland McGrath. + +# To be run by the PROMPT_COMMAND variable, so that one can see what +# the exit status of processes are. + +function check_exit_status () +{ + local status="$?" + local signal="" + + if [ ${status} -ne 0 ] && [ ${status} != 128 ]; then + # If process exited by a signal, determine name of signal. + if [ ${status} -gt 128 ]; then + signal="$(builtin kill -l $((${status} - 128)) 2>/dev/null)" + if [ "$signal" ]; then signal="($signal)"; fi + fi + echo "[Exit ${status} ${signal}]" 1>&2 + fi + return 0 +} + +PROMPT_COMMAND=check_exit_status diff --git a/examples/functions/external b/examples/functions/external new file mode 100644 index 0000000..c2e52cd --- /dev/null +++ b/examples/functions/external @@ -0,0 +1,50 @@ +# Contributed by Noah Friedman. + +# To avoid using a function in bash, you can use the `builtin' or +# `command' builtins, but neither guarantees that you use an external +# program instead of a bash builtin if there's a builtin by that name. So +# this function can be used like `command' except that it guarantees the +# program is external by first disabling any builtin by that name. After +# the command is done executing, the state of the builtin is restored. +function external () +{ + local state="" + local exit_status + + if builtin_p "$1"; then + state="builtin" + enable -n "$1" + fi + + command "$@" + exit_status=$? + + if [ "$state" = "builtin" ]; then + enable "$1" + fi + + return ${exit_status} +} + +# What is does is tell you if a particular keyword is currently enabled as +# a shell builtin. It does NOT tell you if invoking that keyword will +# necessarily run the builtin. For that, do something like +# +# test "$(builtin type -type [keyword])" = "builtin" +# +# Note also, that disabling a builtin with "enable -n" will make builtin_p +# return false, since the builtin is no longer available. +function builtin_p () +{ + local word + + set $(builtin type -all -type "$1") + + for word in "$@" ; do + if [ "${word}" = "builtin" ]; then + return 0 + fi + done + + return 1 +} diff --git a/examples/functions/fact b/examples/functions/fact new file mode 100644 index 0000000..97efd49 --- /dev/null +++ b/examples/functions/fact @@ -0,0 +1,13 @@ +# Who said shells can't use recursion? Here is a factorial function. +# You call it with a number as an argument, and it returns the factorial +# of that number. + +fact () +{ + local num=$1; + if [ "$num" = 1 ] ; then + echo 1 + return ; + fi; + echo $(( $num * $(fact $(( $num - 1 )) ) )) +} diff --git a/examples/functions/fstty b/examples/functions/fstty new file mode 100644 index 0000000..a770d84 --- /dev/null +++ b/examples/functions/fstty @@ -0,0 +1,59 @@ +# +# A function that works as a front end for both stty and the `bind' +# builtin, so the tty driver and readline see the same changes +# + +# +# Convert between the stty ^H control character form and the readline \C-H +# form +# +cvt() +{ + echo "$@" | cat -v | sed 's/\^/\\C-/' +} + +# +# stty front-end. Parses the argument list and creates two command strings, +# one for stty, another for bind. +# +fstty() +{ + local cmd="" bargs="" + local e + + while [ $# -gt 0 ] + do + case "$1" in + -a) cmd="$cmd everything" + ;; + erase) shift; + e=$(cvt "$1") + cmd="$cmd erase $1" + bargs="$bargs '\"$e\": backward-delete-char'" + ;; + kill) shift + e=$(cvt "$1") + cmd="$cmd kill $1" + bargs="$bargs '\"$e\": unix-line-discard'" + ;; + werase) shift; + e=$(cvt "$1") + cmd="$cmd erase $1" + bargs="$bargs '\"$e\": backward-kill-word'" + ;; + lnext) shift; + e=$(cvt "$1") + cmd="$cmd erase $1" + bargs="$bargs '\"$e\": quoted-insert'" + ;; + *) cmd="$cmd $1" + ;; + esac + shift + done + + command stty $cmd + if [ -n "$bargs" ]; then + builtin bind $bargs + fi +} diff --git a/examples/functions/func b/examples/functions/func new file mode 100644 index 0000000..710f643 --- /dev/null +++ b/examples/functions/func @@ -0,0 +1,27 @@ +# +# func -- print out definitions for functions named by arguments +# +# usage: func name [name ...] +# +# Chet Ramey +# chet@ins.CWRU.Edu +func() +{ + local status=0 + + if [ $# -eq 0 ] ; then + echo "usage: func name [name...]" 1>&2 + return 1 + fi + + for f + do + if [ "$(builtin type -type $f)" != "function" ] ; then + echo "func: $f: not a function" 1>&2 + status=1 # one failed + continue + fi + builtin type $f | sed 1d + done + return $status +} diff --git a/examples/functions/gethtml b/examples/functions/gethtml new file mode 100644 index 0000000..2eec1d8 --- /dev/null +++ b/examples/functions/gethtml @@ -0,0 +1,35 @@ +# +# get_html -- get a web page from a remote server +# +# Original Author: Jeff Korn <jlk@cs.princeton.edu> +# Modified for bash by Chet Ramey <chet@po.cwru.edu> +# +# Example: get_html cnswww.cns.cwru.edu /~chet/ | more + +get_html() +{ + local host port + + (($# < 2)) && { + echo "usage: $FUNCNAME hostname path [port]" >&2 + return 1 + } + + host="$1" + port="${3:-80}" + + exec 3<> /dev/tcp/$host/$port || { + echo "$FUNCNAME: $host/$port: cannot connect" >&2 + exit 1 + } + + echo -e "GET $2 HTTP/1.0\n" >&3 + + cat <&3 + + exec 3<&- + + return 0 +} + +get_html "$@" diff --git a/examples/functions/getoptx.bash b/examples/functions/getoptx.bash new file mode 100644 index 0000000..d402c7d --- /dev/null +++ b/examples/functions/getoptx.bash @@ -0,0 +1,301 @@ +#From: "Grigoriy Strokin" <grg@philol.msu.ru> +#Newsgroups: comp.unix.shell +#Subject: BASH: getopt function that parses long-named options +#Date: Mon, 22 Dec 1997 20:35:18 +0300 + +#Hi, I have written a BASH function named getoptex, that is like bash builtin +#"getopts", but does parse long-named options and optional arguments. It only +#uses builtin bash commands, so it is very fast. In order to use it in your +#bash scripts, include a command ". getopt.sh" (<dot> getopt.sh) to the file +#containing your script, and that will define functions getopt, getoptex, and +#optlistex (the file getopt.sh with its detailed description is listed +#below). + +#*** file getopt.sh *** + +#! /bin/bash +# +# getopt.sh: +# functions like getopts but do long-named options parsing +# and support optional arguments +# +# Version 1.0 1997 by Grigoriy Strokin (grg@philol.msu.ru), Public Domain +# Date created: December 21, 1997 +# Date modified: December 21, 1997 +# +# IMPORTANT FEATURES +# +# 1) Parses both short and long-named options +# 2) Supports optional arguments +# 3) Only uses bash builtins, thus no calls to external +# utilities such as expr or sed is done. Therefore, +# parsing speed is high enough +# +# +# DESCRIPTION +# +# FUNCTION getopt +# Usage: getopt OPTLIST {"$@"|ALTERNATIVE_PARAMETERS} +# +# like getopts, but parse options with both required and optional arguments, +# Options with optional arguments must have "." instead of ":" after them. +# Furthemore, a variable name to place option name cannot be specified +# and is always placed in OPTOPT variable +# +# This function is provided for compatibility with getopts() +# OPTLIST style, and it actually calls getoptex (see bellow) +# +# NOTE that a list of parameters is required and must be either "$@", +# if processing command line arguments, or some alternative parameters. +# +# FUNCTION getoptex +# Usage: getoptex OPTION_LIST {"$@"|ALTERNATIVE_PARAMETERS} +# +# like getopts, but parse long-named options. +# +# Both getopt and getoptex return 0 if an option has been parsed, +# and 1 if all options are already parsed or an error occured +# +# Both getopt and getoptex set or test the following variables: +# +# OPTERR -- tested for whether error messages must be given for invalid +options +# +# OPTOPT -- set to the name of an option parsed, +# or to "?" if no more options or error +# OPTARG -- set to the option argument, if any; +# unset if ther is no argument; +# on error, set to the erroneous option name +# +# OPTIND -- Initialized to 1. +# Then set to the number of the next parameter to be parsed +# when getopt or getoptex will be called next time. +# When all options are parsed, contains a number of +# the first non-option argument. +# +# +# OPTOFS -- If a parameter number $OPTIND containg an option parsed +# does not contain any more options, OPTOFS is unset; +# otherwise, OPTOFS is set to such a number of "?" signs +# which is equal to the number of options parsed +# +# You might not set variables OPTIND and OPTOFS yourself +# unless you want to parse a list of parameters more than once. +# Otherwise, you whould unset OPTIND (or set it to 1) +# and unset OPTOFS each time you want to parse a new parameters +list +# +# Option list format is DIFFERENT from one for getopts or getopt. +getopts-style +# option list can be converted to getoptex-style using a function optlistex +# (see bellow) +# +# DESCRIPTION of option list used with getoptex: +# Option names are separated by whitespace. Options consiting of +# more than one character are treated as long-named (--option) +# +# Special characters can appear at the and of option names specifying +# whether an argument is required (default is ";"): +# ";" (default) -- no argument +# ":" -- required argument +# "," -- optional argument +# +# For example, an option list "a b c help version f: file: separator." +# defines the following options: +# -a, -b, -c, --help, --version -- no argument +# -f, --file -- argument required +# --separator -- optional argument +# +# FUNCTION optlistex +# Usage new_style_optlist=`optlistex OLD_STYLE_OPTLIST` +# +# Converts getopts-style option list in a format suitable for use with getoptex +# Namely, it inserts spaces after each option name. +# +# +# HOW TO USE +# +# In order o use in your bash scripts the functions described, +# include a command ". getopt.sh" to the file containing the script, +# which will define functions getopt, getoptex, and optlistex +# +# EXAMPLES +# +# See files 'getopt1' and 'getopt2' that contain sample scripts that use +# getopt and getoptex functions respectively +# +# +# Please send your comments to grg@philol.msu.ru + +function getoptex() +{ + let $# || return 1 + local optlist="${1#;}" + let OPTIND || OPTIND=1 + [ $OPTIND -lt $# ] || return 1 + shift $OPTIND + if [ "$1" != "-" ] && [ "$1" != "${1#-}" ] + then OPTIND=$[OPTIND+1]; if [ "$1" != "--" ] + then + local o + o="-${1#-$OPTOFS}" + for opt in ${optlist#;} + do + OPTOPT="${opt%[;.:]}" + unset OPTARG + local opttype="${opt##*[^;:.]}" + [ -z "$opttype" ] && opttype=";" + if [ ${#OPTOPT} -gt 1 ] + then # long-named option + case $o in + "--$OPTOPT") + if [ "$opttype" != ":" ]; then return 0; fi + OPTARG="$2" + if [ -z "$OPTARG" ]; + then # error: must have an agrument + let OPTERR && echo "$0: error: $OPTOPT must have an argument" >&2 + OPTARG="$OPTOPT"; + OPTOPT="?" + return 1; + fi + OPTIND=$[OPTIND+1] # skip option's argument + return 0 + ;; + "--$OPTOPT="*) + if [ "$opttype" = ";" ]; + then # error: must not have arguments + let OPTERR && echo "$0: error: $OPTOPT must not have arguments" >&2 + OPTARG="$OPTOPT" + OPTOPT="?" + return 1 + fi + OPTARG=${o#"--$OPTOPT="} + return 0 + ;; + esac + else # short-named option + case "$o" in + "-$OPTOPT") + unset OPTOFS + [ "$opttype" != ":" ] && return 0 + OPTARG="$2" + if [ -z "$OPTARG" ] + then + echo "$0: error: -$OPTOPT must have an argument" >&2 + OPTARG="$OPTOPT" + OPTOPT="?" + return 1 + fi + OPTIND=$[OPTIND+1] # skip option's argument + return 0 + ;; + "-$OPTOPT"*) + if [ $opttype = ";" ] + then # an option with no argument is in a chain of options + OPTOFS="$OPTOFS?" # move to the next option in the chain + OPTIND=$[OPTIND-1] # the chain still has other options + return 0 + else + unset OPTOFS + OPTARG="${o#-$OPTOPT}" + return 0 + fi + ;; + esac + fi + done + echo "$0: error: invalid option: $o" + fi; fi + OPTOPT="?" + unset OPTARG + return 1 +} +function optlistex +{ + local l="$1" + local m # mask + local r # to store result + while [ ${#m} -lt $[${#l}-1] ]; do m="$m?"; done # create a "???..." mask + while [ -n "$l" ] + do + r="${r:+"$r "}${l%$m}" # append the first character of $l to $r + l="${l#?}" # cut the first charecter from $l + m="${m#?}" # cut one "?" sign from m + if [ -n "${l%%[^:.;]*}" ] + then # a special character (";", ".", or ":") was found + r="$r${l%$m}" # append it to $r + l="${l#?}" # cut the special character from l + m="${m#?}" # cut one more "?" sign + fi + done + echo $r +} +function getopt() +{ + local optlist=`optlistex "$1"` + shift + getoptex "$optlist" "$@" + return $? +} + +#************************************** +# cut here +#************************************** +#*** (end of getopt.sh) *** + + +#*** file getopt1 *** + +#! /bin/bash +# getopt1: +# Sample script using the function getopt +# +# Type something like "getopt1 -ab -d 10 -e20 text1 text2" +# on the command line to see how it works +# +# See getopt.sh for more information +#. getopt.sh +#echo Using getopt to parse arguments: +#while getopt "abcd:e." "$@" +#do +# echo "Option <$OPTOPT> ${OPTARG:+has an arg <$OPTARG>}" +#done +#shift $[OPTIND-1] +#for arg in "$@" +#do +# echo "Non option argument <$arg>" +#done +# +#************************************** +# cut here +#************************************** +#*** (end of getopt1) *** +# +# +#*** file getopt2 *** +# +#! /bin/bash +# getopt2: +# Sample script using the function getoptex +# +# Type something like "getopt2 -ab -d 10 -e20 --opt1 --opt4=100 text1 text2" +# to see how it works +# +# See getopt.sh for more information +. getopt.sh +#echo Using getoptex to parse arguments: +#while getoptex "a; b; c; d: e. opt1 opt2 opt3 opt4: opt5." "$@" +#do +# echo "Option <$OPTOPT> ${OPTARG:+has an arg <$OPTARG>}" +#done +#shift $[OPTIND-1] +#for arg in "$@" +#do +# echo "Non option argument <$arg>" +#done +# +#************************************** +# cut here +#************************************** +#*** (end of getopt2) *** + diff --git a/examples/functions/inetaddr b/examples/functions/inetaddr new file mode 100644 index 0000000..f3e228f --- /dev/null +++ b/examples/functions/inetaddr @@ -0,0 +1,60 @@ +# +# inet2hex - Internet address conversion, dotted-decimal to hex +# +inet2hex () +{ + local IFS + + IFS=. + set -- $1 + + if (( $# != 4 )); then + echo "inet2hex: incorrect input format: $1" >&2 + echo "inet2hex: usage: inet2hex XX.XX.XX.XX" >&2 + return 2 + fi + + printf "0x%02x%02x%02x%02x\n" $1 $2 $3 $4 +} + +# +# hex2inet - Internet address conversion, hex to dotted-decimal +# +hex2inet () +{ + local x1 x2 x3 x4 + local rev + + OPTIND=1 + while getopts "r" o + do + case "$o" in + r) rev=true;; + *) echo "hex2inet: usage: hex2inet [-r] [0x]XXXXXXXX" >&2 ; exit 2;; + esac + done + shift $(( $OPTIND - 1 )) + + case "$1" in + 0x*) h=${1#??} ;; + *) h=$1 ;; + esac + + if (( ${#h} != 8 )); then + echo "hex2inet: $h not in inet format" >&2 + echo "hex2inet: usage: hex2inet [0x]XXXXXXXX" >&2 + return 2 + fi + + x1=$(( 0x${h:0:2} )) + x2=$(( 0x${h:2:2} )) + x3=$(( 0x${h:4:2} )) + x4=$(( 0x${h:6:2} )) + + if [ -z "$rev" ] ; then + printf "%d.%d.%d.%d\n" $x1 $x2 $x3 $x4 + else + printf "%d.%d.%d.%d\n" $x4 $x3 $x2 $x1 + fi + return 0 +} diff --git a/examples/functions/inpath b/examples/functions/inpath new file mode 100644 index 0000000..cb4c93d --- /dev/null +++ b/examples/functions/inpath @@ -0,0 +1,14 @@ +inpath() +{ + local PROG + path=$(echo $PATH | sed 's/^:/.:/ + s/::/:.:/g + s/:$/:./ + s/:/ /g') + + for x in $path + do + [ -x $x/$1 ] && { PROG=$x/$1; break; } + done + [ -n "$PROG" ] +} diff --git a/examples/functions/isnum.bash b/examples/functions/isnum.bash new file mode 100644 index 0000000..b733965 --- /dev/null +++ b/examples/functions/isnum.bash @@ -0,0 +1,52 @@ +#From: jrmartin@rainey.blueneptune.com (James R. Martin) +#Newsgroups: comp.unix.shell +#Subject: Re: testing user input on numeric or character value +#Date: 26 Nov 1997 01:28:43 GMT + +# isnum returns True if its argument is a valid number, +# and False (retval=1) if it is any other string. +# The first pattern requires a digit before the decimal +# point, and the second after the decimal point. + +# BASH NOTE: make sure you have executed `shopt -s extglob' before +# trying to use this function, or it will not work + +isnum() # string +{ + case $1 in + ?([-+])+([0-9])?(.)*([0-9])?([Ee]?([-+])+([0-9])) ) + return 0;; + ?([-+])*([0-9])?(.)+([0-9])?([Ee]?([-+])+([0-9])) ) + return 0;; + *) return 1;; + esac +} + +isnum2() # string +{ + case $1 in + ?([-+])+([[:digit:]])?(.)*([[:digit:]])?([Ee]?([-+])+([[:digit:]])) ) + return 0;; + ?([-+])*([[:digit:]])?(.)+([[:digit:]])?([Ee]?([-+])+([[:digit:]])) ) + return 0;; + *) return 1;; + esac +} + +isint() # string +{ + case $1 in + ?([-+])+([0-9]) ) + return 0;; + *) return 1;; + esac +} + +isint2() # string +{ + case $1 in + ?([-+])+([[:digit:]]) ) + return 0;; + *) return 1;; + esac +} diff --git a/examples/functions/isnum2 b/examples/functions/isnum2 new file mode 100644 index 0000000..e2e7a5f --- /dev/null +++ b/examples/functions/isnum2 @@ -0,0 +1,22 @@ +isnum2() +{ + case "$1" in + '[-+]' | '') return 1;; # empty or bare `-' or `+' + [-+]*[!0-9]*) return 1;; # non-digit with leading sign + [-+]*) return 0;; # OK + *[!0-9]*) return 1;; # non-digit + *) return 0;; # OK + esac +} + +# this one handles floating point +isnum3() +{ + case "$1" in + '') return 1;; # empty + *[!0-9.+-]*) return 1;; # non-digit, +, -, or . + *?[-+]*) return 1;; # sign as second or later char + *.*.*) return 1;; # multiple decimal points + *) return 0;; # OK + esac +} diff --git a/examples/functions/isvalidip b/examples/functions/isvalidip new file mode 100644 index 0000000..0b2dafe --- /dev/null +++ b/examples/functions/isvalidip @@ -0,0 +1,14 @@ +# Thanks to Chris F. A. Johnson <c.f.a.johnson@rogers.com> for this one +is_validip() +{ + case "$*" in + ""|*[!0-9.]*|*[!0-9]) return 1 ;; + esac + + local IFS=. + set -- $* + + [ $# -eq 4 ] && + [ ${1:-666} -le 255 ] && [ ${2:-666} -le 255 ] && + [ ${3:-666} -le 255 ] && [ ${4:-666} -le 254 ] +} diff --git a/examples/functions/jdate.bash b/examples/functions/jdate.bash new file mode 100644 index 0000000..9488ed9 --- /dev/null +++ b/examples/functions/jdate.bash @@ -0,0 +1,78 @@ +#From: damatex@CAM.ORG (Mario Boudreault) +#Newsgroups: comp.unix.shell +#Subject: JULIAN DATE CONVERSION SUB +#Date: 4 Aug 1995 10:23:28 -0400 +#Message-ID: <3vtah0$jb3@ocean.CAM.ORG> + +#For those using shells and who want to convert dates to a julian number +#here is a shell script (wihtout validation) that can be used as a base +#program for your shell scripts. + +#Special thanks to Ed Ferguson@ti.com who sent me the algorithm to compute +#that date. + +# +# MODIFIED BY CHET RAMEY TO CONVERT TO bash v2 SYNTAX +# + +# cnvdate - Conversion de dates en julienne et vice et versa... +# +# Par : Mario Boudreault Damatex Inc Montreal, Canada +# Date: 2 Aout 1995 +# Rev.: 2 Aout 1995 +# +# Usage: +# cvdate [-j] YYYMMDD pour convertir en nbre de jours +# cvdate -d {julian number} pour convertir en AAAAMMJJ +# + +jul_date() +{ + # + # Separe ANNEE, MOIS et JOUR... + # + YEAR=`echo $DATE | awk ' { print substr($0,1,4) } '` + MONTH=`echo $DATE | awk ' { print substr($0,5,2) } '` + DAY=`echo $DATE | awk ' { print substr($0,7,2) } '` + # + # Execute la formule magique... + # + A=$(( $DAY - 32075 + 1461 * ( $YEAR + 4800 - ( 14 - $MONTH ) / 12 ) \ + / 4 + 367 * ( $MONTH - 2 + ( 14 - $MONTH ) / 12 * 12 ) / 12 - \ + 3 * ( ( $YEAR + 4900 - ( 14 - $MONTH ) / 12 ) / 100 ) / 4 )) + echo $A +} + +day_date() +{ + TEMP1=$(( $DATE + 68569 )) + TEMP2=$(( 4 * $TEMP1 / 146097 )) + TEMP1=$(( $TEMP1 - ( 146097 * $TEMP2 + 3 ) / 4 )) + Y=$(( 4000 * ( $TEMP1 + 1 ) / 1461001 )) + TEMP1=$(( $TEMP1 - 1461 * $Y / 4 + 31 )) + M=$(( 80 * $TEMP1 / 2447 )) + D=$(( $TEMP1 - 2447 * $M / 80 )) + TEMP1=$(( $M / 11 )) + M=$(( $M + 2 - 12 * $TEMP1 )) + Y=$(( 100 * ( $TEMP2 - 49 ) + $Y + $TEMP1 )) + M=`echo $M | awk ' { M=$0 ; if ( length($0) == 1 ) M="0"$0 } END { print M } '` + D=`echo $D | awk ' { D=$0 ; if ( length($0) == 1 ) D="0"$0 } END { print D } '` + echo $Y$M$D +} + +# main() + +if [ $# -eq 1 ]; then + DATE=$1 + jul_date +elif [ "$1" = '-j' ]; then + DATE=$2 + jul_date +elif [ "$1" = '-d' ]; then + DATE=$2 + day_date +fi +# +# Termine +# +exit 0 diff --git a/examples/functions/jj.bash b/examples/functions/jj.bash new file mode 100644 index 0000000..212c9ce --- /dev/null +++ b/examples/functions/jj.bash @@ -0,0 +1,12 @@ +jj () +{ + p=$(jobs $1); + echo $p + + case "$p" in + [*) echo matches '[*' + ;; + *) echo not a match\? + ;; + esac +} diff --git a/examples/functions/keep b/examples/functions/keep new file mode 100644 index 0000000..4433b35 --- /dev/null +++ b/examples/functions/keep @@ -0,0 +1,62 @@ +# From: Seth Chaiklin <psykseth@aau.dk> +# To: chet@ins.CWRU.Edu +# Subject: bash functions (sorta) + +# +# keep: +# usage: keep program +# declare the a program should be "kept". i.e. try to fg a stopped one +# and only when that fails start a fresh program. +# + +keep() +{ + case $# in + 1|2) ;; + *) echo "usage: keep [alias] program" 1>&2 ; return 1;; + esac + + # progname + pn=${1##*/} + + # set up an alias for the kept program + if [ $# = 1 ]; then + alias "$pn=fg $1 2>/dev/null || $1" + else + alias "$1=fg $2 2>/dev/null || $2" + fi +} + +# +# unkeep: +# usage: unkeep program +# unset the alias set up by the keep function +# + +unkeep() +{ + if [ $# != 1 ]; then + echo "usage: unkeep program" + return 2 + fi + + # unset the alias for the kept program + unalias "${1##*/}" +} + +# +# kept: +# lists all kept programs in 'alias: program' form +# + +kept() +{ + alias | grep "fg.*2>" | sed "s/alias \(.*\)='fg.*||\(.*\)'$/\1:\2/" +} + + +# some things that should be kept +#keep /usr/local/bin/emacs +#keep e ${EDITOR:-/usr/local/bin/emacs} +#keep edit ${EDITOR:-/usr/local/bin/emacs} +#keep /usr/local/bin/emm diff --git a/examples/functions/ksh-cd b/examples/functions/ksh-cd new file mode 100644 index 0000000..801a490 --- /dev/null +++ b/examples/functions/ksh-cd @@ -0,0 +1,35 @@ +# +# ksh-like `cd': cd [-LP] [dir [change]] +# +cd() +{ + OPTIND=1 + while getopts "LP" opt + do + case $opt in + L|P) CDOPTS="$CDOPTS -$opt" ;; + *) echo "$FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2 + return 2;; + esac + done + + shift $(( $OPTIND - 1 )) + + case $# in + 0) builtin cd $CDOPTS "$HOME" ;; + 1) builtin cd $CDOPTS "$@" ;; + 2) old="$1" new="$2" + case "$PWD" in + *$old*) ;; + *) echo "${0##*/}: $FUNCNAME: bad substitution" >&2 ; return 1 ;; + esac + + dir=${PWD//$old/$new} + + builtin cd $CDOPTS "$dir" && echo "$PWD" + + ;; + *) echo "${0##*/}: $FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2 + return 2 ;; + esac +} diff --git a/examples/functions/ksh-compat-test b/examples/functions/ksh-compat-test new file mode 100644 index 0000000..feee965 --- /dev/null +++ b/examples/functions/ksh-compat-test @@ -0,0 +1,40 @@ +# +# replacements for test/[ that do arithmetic expansion on the operands to +# the arithmetic operators, like ksh. +# +function test() +{ + local -i n1 n3 + case "$#" in + 3) case "$2" in + -lt|-gt|-eq|-ne|-le|-ge) n1=$(( $1 )) + n3=$(( $3 )) + builtin test "$n1" $2 "$n3" + return $?;; + *) builtin test "$@" ;; + esac;; + *) builtin test "$@" ;; + esac +} + +function [() +{ + local -i n1 n3 + case "$#" in + 4) case "$2" in + -lt|-gt|-eq|-ne|-le|-ge) n1=$(( $1 )) + n3=$(( $3 )) + builtin [ "$n1" $2 "$n3" ] + return $?;; + *) builtin [ "$@" ;; + esac;; + *) builtin [ "$@" ;; + esac +} + +q=7 + +[ q -lt 10 ] +echo $? +[ $q -lt 10 ] +echo $? diff --git a/examples/functions/kshenv b/examples/functions/kshenv new file mode 100644 index 0000000..7594f2d --- /dev/null +++ b/examples/functions/kshenv @@ -0,0 +1,228 @@ +# +# .kshenv -- functions and aliases to provide the beginnings of a ksh +# environment for bash. +# +# Chet Ramey +# chet@ins.CWRU.Edu +# +# +# These are definitions for the ksh compiled-in `exported aliases'. There +# are others, but we already have substitutes for them: "history", "type", +# and "hash". +# +alias r="fc -s" +alias functions="typeset -f" +alias integer="typeset -i" +alias nohup="nohup " +alias command="command " +alias stop="kill -s STOP" +alias redirect="command exec" +alias hist="fc" + +# +# An almost-ksh compatible `whence' command. This is as hairy as it is +# because of the desire to exactly mimic ksh (whose behavior was determined +# empirically). +# +# This depends somewhat on knowing the format of the output of the bash +# `builtin type' command. +# + +whence() +{ + local vflag pflag fflag defarg c + local path + + vflag= aflag= pflag= fflag= + path= + if [ "$#" = "0" ] ; then + echo "whence: usage: whence [-afpv] name..." >&2 + return 2 + fi + + OPTIND=1 + while getopts "avfp" c + do + case "$c" in + a) defarg=-a ;; + f) fflag=1 ;; # no-op + p) pflag=1 ;; + v) vflag=1 ;; + ?) echo "whence: $1: unknown option" >&2 + echo "whence: usage: whence [-afpv] name..." >&2 + return 2 ;; + esac + done + + shift $(( $OPTIND - 1 )) + + if [ "$#" = "0" ] ; then + echo "whence: usage: whence [-afpv] name..." >&2 + return 2 + fi + + for cmd + do + if [ "$vflag" ] ; then + if [ -z "$defarg" ]; then + builtin type $cmd | sed 1q + else + if builtin type $defarg -t $cmd | grep 'function$' >/dev/null 2>&1; then + # HAIRY awk script to suppress + # printing of function body -- could + # do it with sed, but I don't have + # that kind of time + builtin type $defarg $cmd | awk ' +BEGIN {printit = 1;} +$1 == "'$cmd'" && $2 == "()" {printit=0; next; } +/^}$/ { if (printit == 0) printit=1 ; else print $0; next ; } +/.*/ { if (printit) print $0; }' + else + builtin type $defarg $cmd + fi + fi + else + path=$(builtin type $defarg -p $cmd) + if [ "$path" ] ; then + echo $path + else + case "$cmd" in + /*) echo "" ;; + *) case "$(builtin type -t $cmd)" in + "") echo "" ;; + *) echo "$cmd" ;; + esac + ;; + esac + fi + fi + done + return 0 +} + +# +# For real ksh homeboy fanatics, redefine the `type' builtin with a ksh +# version. +# +#type() +#{ +# whence -v "$*" +#} + +# +# ksh-like `cd': cd [-LP] [dir [change]] +# +cd() +{ + OPTIND=1 + while getopts "LP" opt + do + case $opt in + L|P) CDOPTS="$CDOPTS -$opt" ;; + *) echo "$FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2 + return 2;; + esac + done + + shift $(( $OPTIND - 1 )) + + case $# in + 0) builtin cd $CDOPTS "$HOME" ;; + 1) builtin cd $CDOPTS "$@" ;; + 2) old="$1" new="$2" + case "$PWD" in + *$old*) ;; + *) echo "${0##*/}: $FUNCNAME: bad substitution" >&2 ; return 1 ;; + esac + + dir=${PWD//$old/$new} + + builtin cd $CDOPTS "$dir" && echo "$PWD" + + ;; + *) echo "${0##*/}: $FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2 + return 2 ;; + esac +} + +# +# ksh print emulation +# +# print [-Rnprsu[n]] [-f format] [arg ...] +# +# - end of options +# -R BSD-style -- only accept -n, no escapes +# -n do not add trailing newline +# -p no-op (no coprocesses) +# -r no escapes +# -s print to the history file +# -u n redirect output to fd n +# -f format printf "$format" "$@" +# + +print() +{ + local eflag=-e + local nflag= fflag= c + local fd=1 + + OPTIND=1 + while getopts "fRnprsu:" c + do + case $c in + R) eflag= ;; + r) eflag= ;; + n) nflag=-n ;; + s) sflag=y ;; + f) fflag=y ;; + u) fd=$OPTARG ;; + p) ;; + esac + done + shift $(( $OPTIND - 1 )) + + if [ -n "$fflag" ]; then + builtin printf "$@" >&$fd + return + fi + + case "$sflag" in + y) builtin history -s "$*" ;; + *) builtin echo $eflag $nflag "$@" >&$fd + esac +} + +# substring function +# this function should be equivalent to the substring built-in which was +# eliminated after the 06/29/84 version +substring () +{ + local lpat flag str #local variables + set -f + case $1 in + -l|-L) + flag=$1 + lpat=$2 + shift 2 + ;; + esac + # test for too few or too many arguments + if [ x"$1" = x ] || [ $# -gt 2 ]; then + print -u2 'substring: bad argument count' + return 1 + fi + str=$1 + if [ x"$flag" = x-l ]; then #substring -l lpat + str=${str#$lpat} + elif [ x"$flag" = x-L ]; then + str=${str##$lpat} #substring -L lpat + fi + + if [ x"$2" != x ]; then + echo ${str%$2} + else + echo $str + fi + + return 0 +} diff --git a/examples/functions/login b/examples/functions/login new file mode 100644 index 0000000..3d59683 --- /dev/null +++ b/examples/functions/login @@ -0,0 +1,11 @@ +# replace the `login' and `newgrp' builtins in old bourne shells + +login() +{ + exec login "$@" +} + +newgrp() +{ + exec newgrp "$@" +} diff --git a/examples/functions/lowercase b/examples/functions/lowercase new file mode 100644 index 0000000..3cf6bde --- /dev/null +++ b/examples/functions/lowercase @@ -0,0 +1,27 @@ +#! /bin/bash +# +# original from +# @(#) lowercase.ksh 1.0 92/10/08 +# 92/10/08 john h. dubois iii (john@armory.com) +# +# conversion to bash v2 syntax done by Chet Ramey + +lowercase() +{ + for file; do + [ -f "$file" ] || continue + filename=${file##*/} + case "$file" in + */*) dirname=${file%/*} ;; + *) dirname=.;; + esac + nf=$(echo $filename | tr A-Z a-z) + newname="${dirname}/${nf}" + if [ "$nf" != "$filename" ]; then + mv "$file" "$newname" + echo "lowercase: $file -> $newname" + else + echo "lowercase: $file not changed." + fi + done +} diff --git a/examples/functions/manpage b/examples/functions/manpage new file mode 100644 index 0000000..60f9aed --- /dev/null +++ b/examples/functions/manpage @@ -0,0 +1,129 @@ +# Written from scratch by Tom Tromey (tromey@cns.caltech.edu) +# +# manpage -- find and print a manual page. +# usage: manpage section name [printing] +# +function manpage () +{ + local i h cmd zot sec + local num="$1" + local page="$2" + local printing="$3" + local mp + + mp="${MANPATH:-/usr/man}" + if [ "$#" -lt 2 ]; then return 1; fi # should print usage + if [ "$num" != "" ]; then + sec="${num%%[a-zA-Z]*}" + else + sec='[168234571lnpo]' + num="$sec" + fi + for i in $(echo "$mp" | tr : ' '); do + if [ ! -d "$i" ]; then continue; fi + file="$i"/man"$sec"/"$page"."$num"* + set $file + file="$1" + if [ -f "$file" ]; then + zot=$(sed 1q "$file") + cmd=${MANROFF:-"nroff -man - | col | cat -s"} + h=${zot##"'"'\"'} + if [ "$h" != "$zot" ]; then + while [ "$h" != "" ]; do + case "$h" in + *e) cmd="${MANEQN:-neqn} | $cmd";; + *r) cmd="refer | $cmd";; + *t) cmd="tbl | $cmd";; + *v) cmd="vgrind | $cmd";; + *) ;; # should print error + esac + h=${h%?} + done + fi + if [ "$printing" != "" ]; then + (cd "$i"; eval "$cmd") < "$file" | ${PAGER:-more} + else + (cd "$i"; eval "$cmd") < "$file" > /tmp/manpage-$$ + ${PAGER:-more} /tmp/manpage-$$ + rm -f /tmp/manpage-$$ + fi + break + fi + done +} + +function whatis_internal () +{ + local j + for j in $(echo "$MANPATH" | tr : ' '); do + if [ -f "$j/whatis" ]; then + eval $2 -i -e "$1" $j/whatis + fi + done +} + +function whatis () +{ + local name=$(basename "$1") + whatis_internal "$name" "grep -w" +} + +function apropos () +{ + whatis_internal "$1" "grep -F" +} + +# Note: "-" and "-t" together not supported. This man could be +# made a lot better, but it does everything I want. +function man () +{ + local PAGER printing mpath MANROFF num + mpath="${MANPATH:-/usr/man}" + while true; do + case "$1" in + -) PAGER=cat + printing= ;; + -t) + MANROFF=${TROFF:-"ptroff -man -t"} + PAGER="${TCAT:-lpr}" + printing=yes ;; + -M) + mpath="$2" + shift;; + *) break;; + esac + shift + done + local MANPATH="$mpath" + case "$1" in + -f | -k) + local g a + if [ "$1" = "-f" ]; then + g="grep -w" + a=$(basename "$2") + else + g="grep -F" + a="$2" + fi + whatis_internal "$a" "$g" + ;; + [0-9npol] | [0-9][a-z]* | new | public | old | local) + if [ "$1" = "new" ]; then + num=n + elif [ "$1" = "public" ]; then + num=p + elif [ "$1" = "old" ]; then + num=o + elif [ "$1" = "local" ]; then + num=l + else + num="$1" + fi + shift + manpage "$num" "$1" "$printing" + ;; + *) + manpage "$num" "$1" "$printing" + ;; + esac +} diff --git a/examples/functions/mhfold b/examples/functions/mhfold new file mode 100644 index 0000000..3c0c743 --- /dev/null +++ b/examples/functions/mhfold @@ -0,0 +1,16 @@ +# To: chet@ins.CWRU.Edu +# Subject: Bash functions +# From: Sandeep Mehta <sxm@philabs.Philips.Com> + +# print MH folders, useful only because folders(1) doesn't print +# mod date/times + +mhfold() +{ + list=`folders | awk '{if (1 < NR) print $1}'` + /bin/ls -lag ~/Mail > /tmp/fold$$ + for i in $list; do + grep $i /tmp/fold$$ + done + /bin/rm -f /tmp/fold$$ +} diff --git a/examples/functions/notify.bash b/examples/functions/notify.bash new file mode 100644 index 0000000..dafbac5 --- /dev/null +++ b/examples/functions/notify.bash @@ -0,0 +1,58 @@ +trap _notify CHLD +NOTIFY_ALL=false +unset NOTIFY_LIST +unalias false + +false() +{ + return 1 +} + +_notify () +{ + local i j + local newlist= + + if $NOTIFY_ALL + then + return # let bash take care of this itself + elif [ -z "$NOTIFY_LIST" ]; then + return + else + set -- $NOTIFY_LIST + for i in "$@" + do + j=$(jobs -n %$i) + if [ -n "$j" ]; then + echo "$j" + jobs -n %$i >/dev/null + else + newlist="newlist $i" + fi + done + NOTIFY_LIST="$newlist" + fi +} + +notify () +{ + local i j + + if [ $# -eq 0 ]; then + NOTIFY_ALL=: + set -b + return + else + for i in "$@" + do + # turn a valid job spec into a job number + j=$(jobs $i) + case "$j" in + [*) j=${j%%]*} + j=${j#[} + NOTIFY_LIST="$NOTIFY_LIST $j" + ;; + esac + done + fi +} diff --git a/examples/functions/pathfuncs b/examples/functions/pathfuncs new file mode 100644 index 0000000..56fdca3 --- /dev/null +++ b/examples/functions/pathfuncs @@ -0,0 +1,45 @@ +#From: "Simon J. Gerraty" <sjg@zen.void.oz.au> +#Message-Id: <199510091130.VAA01188@zen.void.oz.au> +#Subject: Re: a shell idea? +#Date: Mon, 09 Oct 1995 21:30:20 +1000 + + +# NAME: +# add_path.sh - add dir to path +# +# DESCRIPTION: +# These functions originated in /etc/profile and ksh.kshrc, but +# are more useful in a separate file. +# +# SEE ALSO: +# /etc/profile +# +# AUTHOR: +# Simon J. Gerraty <sjg@zen.void.oz.au> + +# @(#)Copyright (c) 1991 Simon J. Gerraty +# +# This file is provided in the hope that it will +# be of use. There is absolutely NO WARRANTY. +# Permission to copy, redistribute or otherwise +# use this file is hereby granted provided that +# the above copyright notice and this notice are +# left intact. + +# is $1 missing from $2 (or PATH) ? +no_path() { + eval "case :\$${2-PATH}: in *:$1:*) return 1;; *) return 0;; esac" +} +# if $1 exists and is not in path, append it +add_path () { + [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="\$${2:-PATH}:$1" +} +# if $1 exists and is not in path, prepend it +pre_path () { + [ -d ${1:-.} ] && no_path $* && eval ${2:-PATH}="$1:\$${2:-PATH}" +} +# if $1 is in path, remove it +del_path () { + no_path $* || eval ${2:-PATH}=`eval echo :'$'${2:-PATH}: | + sed -e "s;:$1:;:;g" -e "s;^:;;" -e "s;:\$;;"` +} diff --git a/examples/functions/recurse b/examples/functions/recurse new file mode 100644 index 0000000..f69cd50 --- /dev/null +++ b/examples/functions/recurse @@ -0,0 +1,63 @@ +#!/bin/bash + +#From: kaz@ashi.footprints.net (Kaz Kylheku) +#Newsgroups: comp.os.linux.misc +#Subject: Re: bash question: subdirectories +#Message-ID: <slrn8a0gu9.v5n.kaz@ashi.FootPrints.net> +#Date: Tue, 08 Feb 2000 16:24:35 GMT + +#Actually it can be made to. That is to say, it is possible to code a recursive +#descender function in the bash language. Here is an example. +# +#What is nice about this is that you can embed the function into your shell +#script. The function changes the current working directory as it descends. +#So it can handle arbitrarily deep paths. Whereas paths generated by the +#find command can cause a problem when they get too long; the kernel has a +#hard limit on the length of the string passed to the open() and other +#system calls. + +#There are races; what if the directory tree is blown away during the traversal? +#The function won't be able to crawl back up using the .. link and will just +#bail. + +# Recursive Directory Traverser +# Author: Kaz Kylheku +# Date: Feb 27, 1999 +# Copyright 1999 + +# Function parameter usage: +# $1 directory to search +# $2 pattern to search for +# $3 command to execute +# $4 secret argument for passing down path + +function recurse +{ + local file + local path + + if [ "$4" = "" ] ; then + path="${1%/}/" + else + path="$4$1/" + fi + + if cd "$1" ; then + for file in $2; do + if [ -f "$file" ] || [ -d "$file" ]; then + eval "$3" + fi + done + for file in .* * ; do + if [ "$file" = "." ] || [ "$file" = ".." ] ; then + continue + fi + if [ -d "$file" ] && [ ! -L "$file" ]; then + recurse "$file" "$2" "$3" "$path" + fi + done + cd .. + fi +} + +recurse "$1" "$2" 'echo "$path$file"' diff --git a/examples/functions/repeat2 b/examples/functions/repeat2 new file mode 100644 index 0000000..2e2dc7a --- /dev/null +++ b/examples/functions/repeat2 @@ -0,0 +1,43 @@ +# To: chet@ins.CWRU.Edu +# Subject: Bash functions +# From: Sandeep Mehta <sxm@philabs.Philips.Com> + +########################################## +# +# repeat - clone of C shell builtin `repeat' +# +# usage: repeat <count> <command> +# +# It has been tested inside other functions and in conditionals like +# if [ "`repeat <count> <command>`" ]; then COMMANDS [ else COMMANDS ] fi +# Please send me fixes/enhancements. +# +# Sandeep Mehta <sxm@philabs.Philips.Com> +########################################## +repeat() +{ + local rcount=$1 + + if [ $# -le 1 ] || [ -z "$rcount" ]; then + echo "usage: repeat <count> <command>" 1>&2 + return 2 + fi + + shift + + local acmd=("$@") + + if [ $rcount -le 0 ]; then + echo "count must be greater than 0" + echo "usage: repeat <count> <command>" 1>&2 + return 2 + fi + + st=0 + while [ $rcount -gt 0 ]; do + eval "${acmd[@]}" + st=$? + rcount=$((rcount - 1)) + done + return $st +} diff --git a/examples/functions/repeat3 b/examples/functions/repeat3 new file mode 100644 index 0000000..65048bf --- /dev/null +++ b/examples/functions/repeat3 @@ -0,0 +1,12 @@ +# From psamuels@jake.niar.twsu.edu (Peter Samuelson) +# posted to usenet, Message-ID: <6rtp8j$2a0$1@jake.niar.twsu.edu> + +repeat () +{ + local i max; # note that you can use \$i in the command string + max=$1; shift; + + i=1; while ((i <= max)); do + eval "$@"; ((i = i + 1)); + done; +} diff --git a/examples/functions/seq b/examples/functions/seq new file mode 100644 index 0000000..87c8a2c --- /dev/null +++ b/examples/functions/seq @@ -0,0 +1,29 @@ +# Generate a sequence from m to n, m defaults to 1. + +seq () +{ + declare -i lo hi i # makes local + local _SEQ + + case $# in + 1) seq 1 "$1" ; return $? ;; + 2) lo=$1 hi=$2 + i=$lo _SEQ="" + while let "i <= hi"; do + _SEQ="${_SEQ}$i " + let i+=1 + done + echo "${_SEQ# }" + return 0 ;; + *) echo seq: usage: seq [low] high 1>&2 ; return 2 ;; + esac +} + +# like the APL `iota' function (or at least how I remember it :-) +iota() +{ + case $# in + 1) seq 1 "$1"; return $?;; + *) echo "iota: usage: iota high" 1>&2; return 2;; + esac +} diff --git a/examples/functions/seq2 b/examples/functions/seq2 new file mode 100644 index 0000000..c3ad95c --- /dev/null +++ b/examples/functions/seq2 @@ -0,0 +1,37 @@ +# Generate a sequence from m to n, m defaults to 1. + +seq () +{ + declare -i lo hi i # makes local + local _SEQ INIT COMPARE STEP + + case "$1" in + -r) INIT='i=$hi _SEQ=""' COMPARE='let "i >= $lo"' STEP='let i-=1' ; shift ;; + *) INIT='i=$lo _SEQ=""' COMPARE='let "i <= $hi"' STEP='let i+=1' ;; + esac + + case $# in + 1) lo=1 hi="$1" ;; + 2) lo=$1 hi=$2 ;; + *) echo seq: usage: seq [-r] [low] high 1>&2 ; return 2 ;; + esac + + # equivalent to the as-yet-unimplemented + # for (( "$INIT" ; "$COMPARE" ; "$STEP" )); do _SEQ="${_SEQ}$i "; done + eval "$INIT" + while eval "$COMPARE"; do + _SEQ="${_SEQ}$i " + eval "$STEP" + done + echo "${_SEQ# }" + return 0 +} + +# like the APL `iota' function (or at least how I remember it :-) +iota() +{ + case $# in + 1) seq 1 "$1"; return $?;; + *) echo "iota: usage: iota high" 1>&2; return 2;; + esac +} diff --git a/examples/functions/shcat b/examples/functions/shcat new file mode 100644 index 0000000..c5d3d63 --- /dev/null +++ b/examples/functions/shcat @@ -0,0 +1,7 @@ +shcat() +{ + while read -r line + do + echo "$line" + done +} diff --git a/examples/functions/shcat2 b/examples/functions/shcat2 new file mode 100644 index 0000000..6fe90f4 --- /dev/null +++ b/examples/functions/shcat2 @@ -0,0 +1,19 @@ +shcat() +{ + while read -r line + do + echo "$line" + done +} + +shcat2() +{ + while [ $# -ge 1 ]; do + case "$1" in + -) shcat ;; + *) shcat < "$1" ;; + esac + shift + done + exit 0 +} diff --git a/examples/functions/sort-pos-params b/examples/functions/sort-pos-params new file mode 100644 index 0000000..0052b46 --- /dev/null +++ b/examples/functions/sort-pos-params @@ -0,0 +1,50 @@ +# Sort the positional paramters. +# Make sure the positional parameters are passed as arguments to the function. +# If -u is the first arg, remove duplicate array members. +sort_posparams() +{ + local -a R + local u + + case "$1" in + -u) u=-u ; shift ;; + esac + + # if you want the case of no positional parameters to return success, + # remove the error message and return 0 + if [ $# -eq 0 ]; then + echo "$FUNCNAME: argument expected" >&2 + return 1 + fi + + # make R a copy of the positional parameters + R=( "${@}" ) + + # sort R. + R=( $( printf "%s\n" "${R[@]}" | sort $u) ) + + printf "%s\n" "${R[@]}" + return 0 +} + +# will print everything on separate lines +set -- 3 1 4 1 5 9 2 6 5 3 2 +sort_posparams "$@" + +# sets without preserving quoted parameters +set -- $( sort_posparams "$@" ) +echo "$@" +echo $# + +# sets preserving quoted parameters, beware pos params with embedded newlines +set -- 'a b' 'a c' 'x z' + +oifs=$IFS +IFS=$'\n' +set -- $( sort_posparams "$@" ) +IFS="$oifs" + +echo "$@" +echo $# + +sort_posparams diff --git a/examples/functions/substr b/examples/functions/substr new file mode 100644 index 0000000..a80b3b4 --- /dev/null +++ b/examples/functions/substr @@ -0,0 +1,79 @@ +# +# substr -- a function to emulate the ancient ksh builtin +# + +# +# -l == shortest from left +# -L == longest from left +# -r == shortest from right (the default) +# -R == longest from right + +substr() +{ + local flag pat str + local usage="usage: substr -lLrR pat string or substr string pat" + + case "$1" in + -l | -L | -r | -R) + flag="$1" + pat="$2" + shift 2 + ;; + -*) + echo "substr: unknown option: $1" + echo "$usage" + return 1 + ;; + *) + flag="-r" + pat="$2" + ;; + esac + + if [ "$#" -eq 0 ] || [ "$#" -gt 2 ] ; then + echo "substr: bad argument count" + return 2 + fi + + str="$1" + + # + # We don't want -f, but we don't want to turn it back on if + # we didn't have it already + # + case "$-" in + "*f*") + ;; + *) + fng=1 + set -f + ;; + esac + + case "$flag" in + -l) + str="${str#$pat}" # substr -l pat string + ;; + -L) + str="${str##$pat}" # substr -L pat string + ;; + -r) + str="${str%$pat}" # substr -r pat string + ;; + -R) + str="${str%%$pat}" # substr -R pat string + ;; + *) + str="${str%$2}" # substr string pat + ;; + esac + + echo "$str" + + # + # If we had file name generation when we started, re-enable it + # + if [ "$fng" = "1" ] ; then + set +f + fi +} diff --git a/examples/functions/substr2 b/examples/functions/substr2 new file mode 100644 index 0000000..2bb8d36 --- /dev/null +++ b/examples/functions/substr2 @@ -0,0 +1,81 @@ +# +# substr -- a function to emulate the ancient ksh builtin +# + +# -l == remove shortest from left +# -L == remove longest from left +# -r == remove shortest from right (the default) +# -R == remove longest from right + +substr() +{ + local flag pat str + local usage="usage: substr -lLrR pat string or substr string pat" + local options="l:L:r:R:" + + OPTIND=1 + while getopts "$options" c + do + case "$c" in + l | L | r | R) + flag="-$c" + pat="$OPTARG" + ;; + '?') + echo "$usage" + return 1 + ;; + esac + done + + if [ "$OPTIND" -gt 1 ] ; then + shift $[ $OPTIND -1 ] + fi + + if [ "$#" -eq 0 ] || [ "$#" -gt 2 ] ; then + echo "substr: bad argument count" + return 2 + fi + + str="$1" + + # + # We don't want -f, but we don't want to turn it back on if + # we didn't have it already + # + case "$-" in + "*f*") + ;; + *) + fng=1 + set -f + ;; + esac + + case "$flag" in + -l) + str="${str#$pat}" # substr -l pat string + ;; + -L) + str="${str##$pat}" # substr -L pat string + ;; + -r) + str="${str%$pat}" # substr -r pat string + ;; + -R) + str="${str%%$pat}" # substr -R pat string + ;; + *) + str="${str%$2}" # substr string pat + ;; + esac + + echo "$str" + + # + # If we had file name generation when we started, re-enable it + # + if [ "$fng" = "1" ] ; then + set +f + fi +} diff --git a/examples/functions/term b/examples/functions/term new file mode 100644 index 0000000..fbe99f1 --- /dev/null +++ b/examples/functions/term @@ -0,0 +1,35 @@ +# +# term -- a shell function to set the terminal type interactively or not. +# + +term() +{ + local t + + if [ $# != 0 ] ; then + eval $(tset -sQ $1) + else # interactive + if [ -z "$TERM" ] ; then + TERM="unknown" + fi + + case "$TERM" in + network|dialup|unknown|lat) + TERM=unknown + ;; + *) + eval $(tset -sQ) + ;; + esac + + while [ "$TERM" = "unknown" ] ; do + echo -n "Terminal type: " + read t + if [ -n "$t" ] ; then + eval $(tset -sQ $t) + fi + done + fi +} + + diff --git a/examples/functions/whatis b/examples/functions/whatis new file mode 100644 index 0000000..56c5a58 --- /dev/null +++ b/examples/functions/whatis @@ -0,0 +1,52 @@ +# +# whatis -- and implementation of the 10th Edition Unix sh builtin `whatis' +# command. +# +# usage: whatis arg [...] +# +# For each argument, whatis prints the associated value as a parameter, +# builtin, function, alias, or executable file as appropriate. In each +# case, the value is printed in a form which would yield the same value +# if typed as input to the shell itself. +# + +whatis() +{ + local wusage='usage: whatis arg [arg...]' + local fail=0 + + if [ $# -eq 0 ] ; then + echo "$wusage" + return 1 + fi + + for arg + do + case $(builtin type -type $arg 2>/dev/null) in + "alias") + builtin alias "$arg" + ;; + "function") + builtin type "$arg" | sed 1d + ;; + "builtin") + echo builtin "$arg" + ;; + "file") + builtin type -path "$arg" + ;; + *) + # OK, we could have a variable, or we could have nada + if [ "$(eval echo \${$arg+set})" = "set" ] ; then + # It is a variable, and it is set + echo -n "$arg=" + eval echo '\"'\$$arg'\"' + else + echo whatis: $arg: not found + fail=1 + fi + ;; + esac + done + return $fail +} diff --git a/examples/functions/whence b/examples/functions/whence new file mode 100644 index 0000000..70b2322 --- /dev/null +++ b/examples/functions/whence @@ -0,0 +1,59 @@ +# +# An almost-ksh compatible `whence' command. This is as hairy as it is +# because of the desire to exactly mimic ksh. +# +# This depends somewhat on knowing the format of the output of the bash +# `builtin type' command. +# +# Chet Ramey +# chet@ins.CWRU.Edu +# +whence() +{ + local vflag= path= + + if [ "$#" = "0" ] ; then + echo "whence: argument expected" + return 1 + fi + case "$1" in + -v) vflag=1 + shift 1 + ;; + -*) echo "whence: bad option: $1" + return 1 + ;; + *) ;; + esac + + if [ "$#" = "0" ] ; then + echo "whence: bad argument count" + return 1 + fi + + for cmd + do + if [ "$vflag" ] ; then + echo $(builtin type $cmd | sed 1q) + else + path=$(builtin type -path $cmd) + if [ "$path" ] ; then + echo $path + else + case "$cmd" in + /*) if [ -x "$cmd" ]; then + echo "$cmd" + fi + ;; + *) case "$(builtin type -type $cmd)" in + "") ;; + *) echo "$cmd" + ;; + esac + ;; + esac + fi + fi + done + return 0 +} diff --git a/examples/functions/which b/examples/functions/which new file mode 100644 index 0000000..ca33703 --- /dev/null +++ b/examples/functions/which @@ -0,0 +1,44 @@ +# +# which - emulation of `which' as it appears in FreeBSD +# +# usage: which [-as] command [command...] +# + +which() +{ + local aflag sflag ES a opt + + OPTIND=1 + while builtin getopts as opt ; do + case "$opt" in + a) aflag=-a ;; + s) sflag=1 ;; + ?) echo "which: usage: which [-as] command [command ...]" >&2 + exit 2 ;; + esac + done + + (( $OPTIND > 1 )) && shift $(( $OPTIND - 1 )) + + # without command arguments, exit with status 1 + ES=1 + + # exit status is 0 if all commands are found, 1 if any are not found + for command; do + # if $command is a function, make sure we add -a so type + # will look in $PATH after finding the function + a=$aflag + case "$(builtin type -t $command)" in + "function") a=-a;; + esac + + if [ -n "$sflag" ]; then + builtin type -p $a $command >/dev/null 2>&1 + else + builtin type -p $a $command + fi + ES=$? + done + + return $ES +} diff --git a/examples/functions/xalias.bash b/examples/functions/xalias.bash new file mode 100644 index 0000000..88a00dc --- /dev/null +++ b/examples/functions/xalias.bash @@ -0,0 +1,22 @@ +# xalias - convert csh alias commands to bash functions +# from Mohit Aron <aron@cs.rice.edu> +# posted to usenet as <4i5p17$bnu@larry.rice.edu> +function xalias () +{ + if [ "x$2" = "x" ] + then + declare -f $1 + else + case $2 in + *[#\!]*) + comm=$(echo $2 | sed 's/\\!\*/\"$\@\"/g + s/\\!:\([1-9]\)/\"$\1\"/g + s/#/\\#/g') + ;; + *) + comm="$2 \"\$@\"" ;; + esac + + eval function $1 \(\) "{" command "$comm" "; }" + fi +} diff --git a/examples/functions/xfind.bash b/examples/functions/xfind.bash new file mode 100644 index 0000000..6d29038 --- /dev/null +++ b/examples/functions/xfind.bash @@ -0,0 +1,52 @@ +#! /bin/bash +#From: kaz@cafe.net (Kaz Kylheku) +#Newsgroups: comp.unix.shell +#Subject: Why not roll your own @#$% find! (was: splitting directory off from filename) +#Message-ID: <6n1117$tp1@espresso.cafe.net> +#Date: Fri, 26 Jun 1998 20:47:34 GMT + +# $1 = dirname, $2 = pattern, optional $3 = action +xfind() +{ + local x + local dir="$1" + + # descend into specified directory + + builtin cd -L "$1" || { + echo "${FUNCNAME}: cannot change dir to $1" >&2 + return 1 + } + + # + # default action is to print the filename + # + if [ -n "$3" ]; then + action="$3" + else + action='printf -- "%s\n"' + fi + + # process ordinary files that match pattern + + for x in $2 ; do + if [ -f "$x" ] ; then + eval "$action" "$x" + fi + done + + # now descend into subdirectories, avoiding symbolic links + # and directories that start with a period. + + for x in * ; do + if [ -d "$x" ] && [ ! -L "$x" ] ; then + $FUNCNAME "$x" "$2" "$action" + fi + done + + # finally, pop back up + + builtin cd -L .. +} + +#xfind "$@" diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in new file mode 100644 index 0000000..f6208f5 --- /dev/null +++ b/examples/loadables/Makefile.in @@ -0,0 +1,238 @@ +# +# Simple makefile for the sample loadable builtins +# +# Copyright (C) 1996 Free Software Foundation, Inc. + +# 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 2, 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + +# Include some boilerplate Gnu makefile definitions. +prefix = @prefix@ + +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +infodir = @infodir@ +includedir = @includedir@ + +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ +srcdir = @srcdir@ +VPATH = .:@srcdir@ + +@SET_MAKE@ +CC = @CC@ +RM = rm -f + +SHELL = @MAKE_SHELL@ + +host_os = @host_os@ +host_cpu = @host_cpu@ +host_vendor = @host_vendor@ + +CFLAGS = @CFLAGS@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ +DEFS = @DEFS@ +LOCAL_DEFS = @LOCAL_DEFS@ + +CPPFLAGS = @CPPFLAGS@ + +BASHINCDIR = ${topdir}/include + +LIBBUILD = ${BUILD_DIR}/lib + +INTL_LIBSRC = ${topdir}/lib/intl +INTL_BUILDDIR = ${LIBBUILD}/intl +INTL_INC = @INTL_INC@ +LIBINTL_H = @LIBINTL_H@ + +CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) $(CFLAGS) + +# +# These values are generated for configure by ${topdir}/support/shobj-conf. +# If your system is not supported by that script, but includes facilities for +# dynamic loading of shared objects, please update the script and send the +# changes to bash-maintainers@gnu.org. +# +SHOBJ_CC = @SHOBJ_CC@ +SHOBJ_CFLAGS = @SHOBJ_CFLAGS@ +SHOBJ_LD = @SHOBJ_LD@ +SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@ +SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@ +SHOBJ_LIBS = @SHOBJ_LIBS@ +SHOBJ_STATUS = @SHOBJ_STATUS@ + +INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins \ + -I$(BASHINCDIR) -I$(BUILD_DIR) -I$(LIBBUILD) \ + -I$(BUILD_DIR)/builtins $(INTL_INC) + +.c.o: + $(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CCFLAGS) $(INC) -c -o $@ $< + + +ALLPROG = print truefalse sleep pushd finfo logname basename dirname \ + tty pathchk tee head mkdir rmdir printenv id whoami \ + uname sync push ln unlink cut realpath getconf strftime +OTHERPROG = necho hello cat + +all: $(SHOBJ_STATUS) + +supported: $(ALLPROG) +others: $(OTHERPROG) + +unsupported: + @echo "Your system (${host_os}) is not supported by the" + @echo "${topdir}/support/shobj-conf script." + @echo "If your operating system provides facilities for dynamic" + @echo "loading of shared objects using the dlopen(3) interface," + @echo "please update the script and re-run configure. + @echo "Please send the changes you made to bash-maintainers@gnu.org" + @echo "for inclusion in future bash releases." + +everything: supported others + +print: print.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ print.o $(SHOBJ_LIBS) + +necho: necho.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ necho.o $(SHOBJ_LIBS) + +getconf: getconf.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ getconf.o $(SHOBJ_LIBS) + +hello: hello.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ hello.o $(SHOBJ_LIBS) + +truefalse: truefalse.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ truefalse.o $(SHOBJ_LIBS) + +sleep: sleep.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sleep.o $(SHOBJ_LIBS) + +finfo: finfo.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ finfo.o $(SHOBJ_LIBS) + +cat: cat.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cat.o $(SHOBJ_LIBS) + +logname: logname.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ logname.o $(SHOBJ_LIBS) + +basename: basename.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ basename.o $(SHOBJ_LIBS) + +dirname: dirname.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ dirname.o $(SHOBJ_LIBS) + +tty: tty.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tty.o $(SHOBJ_LIBS) + +pathchk: pathchk.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pathchk.o $(SHOBJ_LIBS) + +tee: tee.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tee.o $(SHOBJ_LIBS) + +mkdir: mkdir.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mkdir.o $(SHOBJ_LIBS) + +rmdir: rmdir.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rmdir.o $(SHOBJ_LIBS) + +head: head.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ head.o $(SHOBJ_LIBS) + +printenv: printenv.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ printenv.o $(SHOBJ_LIBS) + +id: id.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ id.o $(SHOBJ_LIBS) + +whoami: whoami.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ whoami.o $(SHOBJ_LIBS) + +uname: uname.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ uname.o $(SHOBJ_LIBS) + +sync: sync.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sync.o $(SHOBJ_LIBS) + +push: push.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ push.o $(SHOBJ_LIBS) + +ln: ln.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ ln.o $(SHOBJ_LIBS) + +unlink: unlink.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ unlink.o $(SHOBJ_LIBS) + +cut: cut.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cut.o $(SHOBJ_LIBS) + +realpath: realpath.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ realpath.o $(SHOBJ_LIBS) + +strftime: strftime.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ strftime.o $(SHOBJ_LIBS) + +# pushd is a special case. We use the same source that the builtin version +# uses, with special compilation options. +# +pushd.c: ${topdir}/builtins/pushd.def + $(RM) $@ + ${BUILD_DIR}/builtins/mkbuiltins -D ${topdir}/builtins ${topdir}/builtins/pushd.def + +pushd.o: pushd.c + $(RM) $@ + $(SHOBJ_CC) -DHAVE_CONFIG_H -DPUSHD_AND_POPD -DLOADABLE_BUILTIN $(SHOBJ_CFLAGS) $(CFLAGS) $(CPPFLAGS) $(INC) -c -o $@ $< + +pushd: pushd.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pushd.o $(SHOBJ_LIBS) + +clean: + $(RM) $(ALLPROG) $(OTHERPROG) *.o + -( cd perl && ${MAKE} ${MFLAGS} $@ ) + +mostlyclean: clean + -( cd perl && ${MAKE} ${MFLAGS} $@ ) + +distclean maintainer-clean: clean + $(RM) Makefile pushd.c + -( cd perl && ${MAKE} ${MFLAGS} $@ ) + +print.o: print.c +truefalse.o: truefalse.c +sleep.o: sleep.c +finfo.o: finfo.c +logname.o: logname.c +basename.o: basename.c +dirname.o: dirname.c +tty.o: tty.c +pathchk.o: pathchk.c +tee.o: tee.c +head.o: head.c +rmdir.o: rmdir.c +necho.o: necho.c +getconf.o: getconf.c +hello.o: hello.c +cat.o: cat.c +printenv.o: printenv.c +id.o: id.c +whoami.o: whoami.c +uname.o: uname.c +sync.o: sync.c +push.o: push.c +mkdir.o: mkdir.c +realpath.o: realpath.c +strftime.o: strftime.c diff --git a/examples/loadables/README b/examples/loadables/README new file mode 100644 index 0000000..db67860 --- /dev/null +++ b/examples/loadables/README @@ -0,0 +1,33 @@ +Some examples of ready-to-dynamic-load builtins. Most of the +examples given are reimplementations of standard commands whose +execution time is dominated by process startup time. The +exceptions are sleep, which allows you to sleep for fractions +of a second, finfo, which provides access to the rest of the +elements of the `stat' structure that `test' doesn't let you +see, and pushd/popd/dirs, which allows you to compile them out +of the shell. + +All of the new builtins in ksh93 that bash didn't already have +are included here, as is the ksh `print' builtin. + +The configure script in the top-level source directory uses the +support/shobj-conf script to set the right values in the Makefile, +so you should not need to change the Makefile. If your system +is not supported by support/shobj-conf, and it has the necessary +facilities for building shared objects and support for the +dlopen/dlsyn/dlclose/dlerror family of functions, please make +the necessary changes to support/shobj-conf and send the changes +to bash-maintainers@gnu.org. + +Loadable builtins are loaded into a running shell with + + enable -f filename builtin-name + +enable uses a simple reference-counting scheme to avoid unloading a +shared object that implements more than one loadable builtin before +all loadable builtins implemented in the object are removed. + +Many of the details needed by builtin writers are found in hello.c, +the canonical example. There is no real `builtin writers' programming +guide'. The file template.c provides a template to use for creating +new loadable builtins. diff --git a/examples/loadables/basename.c b/examples/loadables/basename.c new file mode 100644 index 0000000..7f254c7 --- /dev/null +++ b/examples/loadables/basename.c @@ -0,0 +1,108 @@ +/* basename - return nondirectory portion of pathname */ + +/* See Makefile for compilation details. */ + +#include "config.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include "builtins.h" +#include "shell.h" + +basename_builtin (list) + WORD_LIST *list; +{ + int slen, sufflen, off; + char *string, *suffix, *fn; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (no_options (list)) + return (EX_USAGE); + + string = list->word->word; + suffix = (char *)NULL; + if (list->next) + { + list = list->next; + suffix = list->word->word; + } + + if (list->next) + { + builtin_usage (); + return (EX_USAGE); + } + + slen = strlen (string); + + /* Strip trailing slashes */ + while (slen > 0 && string[slen - 1] == '/') + slen--; + + /* (2) If string consists entirely of slash characters, string shall be + set to a single slash character. In this case, skip steps (3) + through (5). */ + if (slen == 0) + { + fputs ("/\n", stdout); + return (EXECUTION_SUCCESS); + } + + /* (3) If there are any trailing slash characters in string, they + shall be removed. */ + string[slen] = '\0'; + + /* (4) If there are any slash characters remaining in string, the prefix + of string up to an including the last slash character in string + shall be removed. */ + while (--slen >= 0) + if (string[slen] == '/') + break; + + fn = string + slen + 1; + + /* (5) If the suffix operand is present, is not identical to the + characters remaining in string, and is identical to a suffix + of the characters remaining in string, the suffix suffix + shall be removed from string. Otherwise, string shall not be + modified by this step. */ + if (suffix) + { + sufflen = strlen (suffix); + slen = strlen (fn); + if (sufflen < slen) + { + off = slen - sufflen; + if (strcmp (fn + off, suffix) == 0) + fn[off] = '\0'; + } + } + printf ("%s\n", fn); + return (EXECUTION_SUCCESS); +} + +char *basename_doc[] = { + "The STRING is converted to a filename corresponding to the last", + "pathname component in STRING. If the suffix string SUFFIX is", + "supplied, it is removed.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin basename_struct = { + "basename", /* builtin name */ + basename_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + basename_doc, /* array of long documentation strings. */ + "basename string [suffix]", /* usage synopsis */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/cat.c b/examples/loadables/cat.c new file mode 100644 index 0000000..9dd1d1a --- /dev/null +++ b/examples/loadables/cat.c @@ -0,0 +1,100 @@ +/* + * cat replacement + * + * no options - the way cat was intended + */ + +#include <fcntl.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" + +#ifndef errno +extern int errno; +#endif + +extern char *strerror (); +extern char **make_builtin_argv (); + +static int +fcopy(fd) +int fd; +{ + char buf[1024], *s; + int n, w, e; + + while (n = read(fd, buf, sizeof (buf))) { + w = write(1, buf, n); + if (w != n) { + e = errno; + write(2, "cat: write error: ", 18); + s = strerror(e); + write(2, s, strlen(s)); + write(2, "\n", 1); + return 1; + } + } + return 0; +} + +cat_main (argc, argv) +int argc; +char **argv; +{ + int i, fd, r; + char *s; + + if (argc == 1) + return (fcopy(0)); + + for (i = r = 1; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] == '\0') + fd = 0; + else { + fd = open(argv[i], O_RDONLY, 0666); + if (fd < 0) { + s = strerror(errno); + write(2, "cat: cannot open ", 17); + write(2, argv[i], strlen(argv[i])); + write(2, ": ", 2); + write(2, s, strlen(s)); + write(2, "\n", 1); + continue; + } + } + r = fcopy(fd); + if (fd != 0) + close(fd); + } + return (r); +} + +cat_builtin(list) +WORD_LIST *list; +{ + char **v; + int c, r; + + v = make_builtin_argv(list, &c); + r = cat_main(c, v); + free(v); + + return r; +} + +char *cat_doc[] = { + "Read each FILE and display it on the standard output. If any", + "FILE is `-' or if no FILE argument is given, the standard input", + "is read.", + (char *)0 +}; + +struct builtin cat_struct = { + "cat", + cat_builtin, + BUILTIN_ENABLED, + cat_doc, + "cat [-] [file ...]", + 0 +}; diff --git a/examples/loadables/cut.c b/examples/loadables/cut.c new file mode 100644 index 0000000..d874034 --- /dev/null +++ b/examples/loadables/cut.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam S. Moskowitz of Menlo Consulting and Marciano Pitargue. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static const char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95"; +#endif /* not lint */ + +#include <config.h> + +#include <ctype.h> +#include <stdio.h> +#include <errno.h> + +#include "bashansi.h" + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +#if !defined (_POSIX2_LINE_MAX) +# define _POSIX2_LINE_MAX 2048 +#endif + +static int cflag; +static char dchar; +static int dflag; +static int fflag; +static int sflag; + +static int autostart, autostop, maxval; +static char positions[_POSIX2_LINE_MAX + 1]; + +static int c_cut __P((FILE *, char *)); +static int f_cut __P((FILE *, char *)); +static int get_list __P((char *)); +static char *_cut_strsep __P((char **, const char *)); + +int +cut_builtin(list) + WORD_LIST *list; +{ + FILE *fp; + int (*fcn) __P((FILE *, char *)) = NULL; + int ch; + + fcn = NULL; + dchar = '\t'; /* default delimiter is \t */ + + /* Since we don't support multi-byte characters, the -c and -b + options are equivalent, and the -n option is meaningless. */ + reset_internal_getopt (); + while ((ch = internal_getopt (list, "b:c:d:f:sn")) != -1) + switch(ch) { + case 'b': + case 'c': + fcn = c_cut; + if (get_list(list_optarg) < 0) + return (EXECUTION_FAILURE); + cflag = 1; + break; + case 'd': + dchar = *list_optarg; + dflag = 1; + break; + case 'f': + fcn = f_cut; + if (get_list(list_optarg) < 0) + return (EXECUTION_FAILURE); + fflag = 1; + break; + case 's': + sflag = 1; + break; + case 'n': + break; + case '?': + default: + builtin_usage(); + return (EX_USAGE); + } + + list = loptend; + + if (fflag) { + if (cflag) { + builtin_usage(); + return (EX_USAGE); + } + } else if (!cflag || dflag || sflag) { + builtin_usage(); + return (EX_USAGE); + } + + if (list) { + while (list) { + fp = fopen(list->word->word, "r"); + if (fp == 0) { + builtin_error("%s", list->word->word); + return (EXECUTION_FAILURE); + } + ch = (*fcn)(fp, list->word->word); + (void)fclose(fp); + if (ch < 0) + return (EXECUTION_FAILURE); + list = list->next; + } + } else { + ch = (*fcn)(stdin, "stdin"); + if (ch < 0) + return (EXECUTION_FAILURE); + } + + return (EXECUTION_SUCCESS); +} + +static int +get_list(list) + char *list; +{ + int setautostart, start, stop; + char *pos; + char *p; + + /* + * set a byte in the positions array to indicate if a field or + * column is to be selected; use +1, it's 1-based, not 0-based. + * This parser is less restrictive than the Draft 9 POSIX spec. + * POSIX doesn't allow lists that aren't in increasing order or + * overlapping lists. We also handle "-3-5" although there's no + * real reason too. + */ + for (; (p = _cut_strsep(&list, ", \t")) != NULL;) { + setautostart = start = stop = 0; + if (*p == '-') { + ++p; + setautostart = 1; + } + if (isdigit((unsigned char)*p)) { + start = stop = strtol(p, &p, 10); + if (setautostart && start > autostart) + autostart = start; + } + if (*p == '-') { + if (isdigit((unsigned char)p[1])) + stop = strtol(p + 1, &p, 10); + if (*p == '-') { + ++p; + if (!autostop || autostop > stop) + autostop = stop; + } + } + if (*p) { + builtin_error("[-cf] list: illegal list value"); + return -1; + } + if (!stop || !start) { + builtin_error("[-cf] list: values may not include zero"); + return -1; + } + if (stop > _POSIX2_LINE_MAX) { + builtin_error("[-cf] list: %d too large (max %d)", + stop, _POSIX2_LINE_MAX); + return -1; + } + if (maxval < stop) + maxval = stop; + for (pos = positions + start; start++ <= stop; *pos++ = 1); + } + + /* overlapping ranges */ + if (autostop && maxval > autostop) + maxval = autostop; + + /* set autostart */ + if (autostart) + memset(positions + 1, '1', autostart); + + return 0; +} + +/* ARGSUSED */ +static int +c_cut(fp, fname) + FILE *fp; + char *fname; +{ + int ch, col; + char *pos; + + ch = 0; + for (;;) { + pos = positions + 1; + for (col = maxval; col; --col) { + if ((ch = getc(fp)) == EOF) + return; + if (ch == '\n') + break; + if (*pos++) + (void)putchar(ch); + } + if (ch != '\n') { + if (autostop) + while ((ch = getc(fp)) != EOF && ch != '\n') + (void)putchar(ch); + else + while ((ch = getc(fp)) != EOF && ch != '\n'); + } + (void)putchar('\n'); + } + return (0); +} + +static int +f_cut(fp, fname) + FILE *fp; + char *fname; +{ + int ch, field, isdelim; + char *pos, *p, sep; + int output; + char lbuf[_POSIX2_LINE_MAX + 1]; + + for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) { + output = 0; + for (isdelim = 0, p = lbuf;; ++p) { + if (!(ch = *p)) { + builtin_error("%s: line too long.", fname); + return -1; + } + /* this should work if newline is delimiter */ + if (ch == sep) + isdelim = 1; + if (ch == '\n') { + if (!isdelim && !sflag) + (void)printf("%s", lbuf); + break; + } + } + if (!isdelim) + continue; + + pos = positions + 1; + for (field = maxval, p = lbuf; field; --field, ++pos) { + if (*pos) { + if (output++) + (void)putchar(sep); + while ((ch = *p++) != '\n' && ch != sep) + (void)putchar(ch); + } else { + while ((ch = *p++) != '\n' && ch != sep) + continue; + } + if (ch == '\n') + break; + } + if (ch != '\n') { + if (autostop) { + if (output) + (void)putchar(sep); + for (; (ch = *p) != '\n'; ++p) + (void)putchar(ch); + } else + for (; (ch = *p) != '\n'; ++p); + } + (void)putchar('\n'); + } + return (0); +} + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +static char * +_cut_strsep(stringp, delim) + register char **stringp; + register const char *delim; +{ + register char *s; + register const char *spanp; + register int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +static char *cut_doc[] = { + "Select portions of each line (as specified by LIST) from each FILE", + "(by default, the standard input), and write them to the standard output.", + "Items specified by LIST are either column positions or fields delimited", + "by a special character. Column numbering starts at 1.", + (char *)0 +}; + +struct builtin cut_struct = { + "cut", + cut_builtin, + BUILTIN_ENABLED, + cut_doc, + "cut -b list [-n] [file ...] OR cut -c list [file ...] OR cut -f list [-s] [-d delim] [file ...]", + 0 +}; diff --git a/examples/loadables/dirname.c b/examples/loadables/dirname.c new file mode 100644 index 0000000..6159560 --- /dev/null +++ b/examples/loadables/dirname.c @@ -0,0 +1,95 @@ +/* dirname - return directory portion of pathname */ + +/* See Makefile for compilation details. */ + +#include "config.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include "builtins.h" +#include "shell.h" + +dirname_builtin (list) + WORD_LIST *list; +{ + int slen; + char *string; + + if (list == 0 || list->next) + { + builtin_usage (); + return (EX_USAGE); + } + + if (no_options (list)) + return (EX_USAGE); + + string = list->word->word; + slen = strlen (string); + + /* Strip trailing slashes */ + while (slen > 0 && string[slen - 1] == '/') + slen--; + + /* (2) If string consists entirely of slash characters, string shall be + set to a single slash character. In this case, skip steps (3) + through (8). */ + if (slen == 0) + { + fputs ("/\n", stdout); + return (EXECUTION_SUCCESS); + } + + /* (3) If there are any trailing slash characters in string, they + shall be removed. */ + string[slen] = '\0'; + + /* (4) If there are no slash characters remaining in string, string + shall be set to a single period character. In this case, skip + steps (5) through (8). + + (5) If there are any trailing nonslash characters in string, + they shall be removed. */ + + while (--slen >= 0) + if (string[slen] == '/') + break; + + if (slen < 0) + { + fputs (".\n", stdout); + return (EXECUTION_SUCCESS); + } + + /* (7) If there are any trailing slash characters in string, they + shall be removed. */ + while (--slen >= 0) + if (string[slen] != '/') + break; + string[++slen] = '\0'; + + /* (8) If the remaining string is empty, string shall be set to a single + slash character. */ + printf ("%s\n", (slen == 0) ? "/" : string); + return (EXECUTION_SUCCESS); +} + +char *dirname_doc[] = { + "The STRING is converted to the name of the directory containing", + "the filename corresponding to the last pathname component in STRING.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin dirname_struct = { + "dirname", /* builtin name */ + dirname_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + dirname_doc, /* array of long documentation strings. */ + "dirname string", /* usage synopsis */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/finfo.c b/examples/loadables/finfo.c new file mode 100644 index 0000000..c1682ed --- /dev/null +++ b/examples/loadables/finfo.c @@ -0,0 +1,596 @@ +/* + * finfo - print file info + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#include "posixstat.h" +#include <stdio.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> + +#include "bashansi.h" +#include "shell.h" +#include "builtins.h" +#include "common.h" + +#ifndef errno +extern int errno; +#endif + +extern char **make_builtin_argv (); + +static int printst(); +static int printsome(); +static int printfinfo(); +static int finfo_main(); + +extern int sh_optind; +extern char *sh_optarg; +extern char *this_command_name; + +static char *prog; +static int pmask; + +#define OPT_UID 0x00001 +#define OPT_GID 0x00002 +#define OPT_DEV 0x00004 +#define OPT_INO 0x00008 +#define OPT_PERM 0x00010 +#define OPT_LNKNAM 0x00020 +#define OPT_FID 0x00040 +#define OPT_NLINK 0x00080 +#define OPT_RDEV 0x00100 +#define OPT_SIZE 0x00200 +#define OPT_ATIME 0x00400 +#define OPT_MTIME 0x00800 +#define OPT_CTIME 0x01000 +#define OPT_BLKSIZE 0x02000 +#define OPT_BLKS 0x04000 +#define OPT_FTYPE 0x08000 +#define OPT_PMASK 0x10000 +#define OPT_OPERM 0x20000 + +#define OPT_ASCII 0x1000000 + +#define OPTIONS "acdgiflmnopsuACGMP:U" + +static int +octal(s) +char *s; +{ + int r; + + r = *s - '0'; + while (*++s >= '0' && *s <= '7') + r = (r * 8) + (*s - '0'); + return r; +} + +static int +finfo_main(argc, argv) +int argc; +char **argv; +{ + register int i; + int mode, flags, opt; + + sh_optind = 0; /* XXX */ + prog = base_pathname(argv[0]); + if (argc == 1) { + builtin_usage(); + return(1); + } + flags = 0; + while ((opt = sh_getopt(argc, argv, OPTIONS)) != EOF) { + switch(opt) { + case 'a': flags |= OPT_ATIME; break; + case 'A': flags |= OPT_ATIME|OPT_ASCII; break; + case 'c': flags |= OPT_CTIME; break; + case 'C': flags |= OPT_CTIME|OPT_ASCII; break; + case 'd': flags |= OPT_DEV; break; + case 'i': flags |= OPT_INO; break; + case 'f': flags |= OPT_FID; break; + case 'g': flags |= OPT_GID; break; + case 'G': flags |= OPT_GID|OPT_ASCII; break; + case 'l': flags |= OPT_LNKNAM; break; + case 'm': flags |= OPT_MTIME; break; + case 'M': flags |= OPT_MTIME|OPT_ASCII; break; + case 'n': flags |= OPT_NLINK; break; + case 'o': flags |= OPT_OPERM; break; + case 'p': flags |= OPT_PERM; break; + case 'P': flags |= OPT_PMASK; pmask = octal(sh_optarg); break; + case 's': flags |= OPT_SIZE; break; + case 'u': flags |= OPT_UID; break; + case 'U': flags |= OPT_UID|OPT_ASCII; break; + default: builtin_usage (); return(1); + } + } + + argc -= sh_optind; + argv += sh_optind; + + if (argc == 0) { + builtin_usage(); + return(1); + } + + for (i = 0; i < argc; i++) + opt = flags ? printsome (argv[i], flags) : printfinfo(argv[i]); + + return(opt); +} + +static struct stat * +getstat(f) +char *f; +{ + static struct stat st; + int fd, r; + intmax_t lfd; + + if (strncmp(f, "/dev/fd/", 8) == 0) { + if ((legal_number(f + 8, &lfd) == 0) || (int)lfd != lfd) { + builtin_error("%s: invalid fd", f + 8); + return ((struct stat *)0); + } + fd = lfd; + r = fstat(fd, &st); + } else +#ifdef HAVE_LSTAT + r = lstat(f, &st); +#else + r = stat(f, &st); +#endif + if (r < 0) { + builtin_error("%s: cannot stat: %s", f, strerror(errno)); + return ((struct stat *)0); + } + return (&st); +} + +static int +printfinfo(f) +char *f; +{ + struct stat *st; + + st = getstat(f); + return (st ? printst(st) : 1); +} + +static int +getperm(m) +int m; +{ + return (m & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID)); +} + +static int +perms(m) +int m; +{ + char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */ + int i; + + i = 0; + if (m & S_IRUSR) + ubits[i++] = 'r'; + if (m & S_IWUSR) + ubits[i++] = 'w'; + if (m & S_IXUSR) + ubits[i++] = 'x'; + ubits[i] = '\0'; + + i = 0; + if (m & S_IRGRP) + gbits[i++] = 'r'; + if (m & S_IWGRP) + gbits[i++] = 'w'; + if (m & S_IXGRP) + gbits[i++] = 'x'; + gbits[i] = '\0'; + + i = 0; + if (m & S_IROTH) + obits[i++] = 'r'; + if (m & S_IWOTH) + obits[i++] = 'w'; + if (m & S_IXOTH) + obits[i++] = 'x'; + obits[i] = '\0'; + + if (m & S_ISUID) + ubits[2] = (m & S_IXUSR) ? 's' : 'S'; + if (m & S_ISGID) + gbits[2] = (m & S_IXGRP) ? 's' : 'S'; + if (m & S_ISVTX) + obits[2] = (m & S_IXOTH) ? 't' : 'T'; + + printf ("u=%s,g=%s,o=%s", ubits, gbits, obits); +} + +static int +printmode(mode) +int mode; +{ + if (S_ISBLK(mode)) + printf("S_IFBLK "); + if (S_ISCHR(mode)) + printf("S_IFCHR "); + if (S_ISDIR(mode)) + printf("S_IFDIR "); + if (S_ISREG(mode)) + printf("S_IFREG "); + if (S_ISFIFO(mode)) + printf("S_IFIFO "); + if (S_ISLNK(mode)) + printf("S_IFLNK "); + if (S_ISSOCK(mode)) + printf("S_IFSOCK "); +#ifdef S_ISWHT + if (S_ISWHT(mode)) + printf("S_ISWHT "); +#endif + perms(getperm(mode)); + printf("\n"); +} + +static int +printst(st) +struct stat *st; +{ + struct passwd *pw; + struct group *gr; + char *owner; + int ma, mi, d; + + ma = major (st->st_rdev); + mi = minor (st->st_rdev); +#if defined (makedev) + d = makedev (ma, mi); +#else + d = st->st_rdev & 0xFF; +#endif + printf("Device (major/minor): %d (%d/%d)\n", d, ma, mi); + + printf("Inode: %d\n", (int) st->st_ino); + printf("Mode: (%o) ", (int) st->st_mode); + printmode((int) st->st_mode); + printf("Link count: %d\n", (int) st->st_nlink); + pw = getpwuid(st->st_uid); + owner = pw ? pw->pw_name : "unknown"; + printf("Uid of owner: %d (%s)\n", (int) st->st_uid, owner); + gr = getgrgid(st->st_gid); + owner = gr ? gr->gr_name : "unknown"; + printf("Gid of owner: %d (%s)\n", (int) st->st_gid, owner); + printf("Device type: %d\n", (int) st->st_rdev); + printf("File size: %ld\n", (long) st->st_size); + printf("File last access time: %s", ctime (&st->st_atime)); + printf("File last modify time: %s", ctime (&st->st_mtime)); + printf("File last status change time: %s", ctime (&st->st_ctime)); + fflush(stdout); + return(0); +} + +static int +printsome(f, flags) +char *f; +int flags; +{ + struct stat *st; + struct passwd *pw; + struct group *gr; + int p; + char *b; + + st = getstat(f); + if (st == NULL) + return (1); + + /* Print requested info */ + if (flags & OPT_ATIME) { + if (flags & OPT_ASCII) + printf("%s", ctime(&st->st_atime)); + else + printf("%ld\n", st->st_atime); + } else if (flags & OPT_MTIME) { + if (flags & OPT_ASCII) + printf("%s", ctime(&st->st_mtime)); + else + printf("%ld\n", st->st_mtime); + } else if (flags & OPT_CTIME) { + if (flags & OPT_ASCII) + printf("%s", ctime(&st->st_ctime)); + else + printf("%ld\n", st->st_ctime); + } else if (flags & OPT_DEV) + printf("%d\n", st->st_dev); + else if (flags & OPT_INO) + printf("%d\n", st->st_ino); + else if (flags & OPT_FID) + printf("%d:%ld\n", st->st_dev, st->st_ino); + else if (flags & OPT_NLINK) + printf("%d\n", st->st_nlink); + else if (flags & OPT_LNKNAM) { +#ifdef S_ISLNK + b = xmalloc(4096); + p = readlink(f, b, 4096); + if (p >= 0 && p < 4096) + b[p] = '\0'; + else { + p = errno; + strcpy(b, prog); + strcat(b, ": "); + strcat(b, strerror(p)); + } + printf("%s\n", b); + free(b); +#else + printf("%s\n", f); +#endif + } else if (flags & OPT_PERM) { + perms(st->st_mode); + printf("\n"); + } else if (flags & OPT_OPERM) + printf("%o\n", getperm(st->st_mode)); + else if (flags & OPT_PMASK) + printf("%o\n", getperm(st->st_mode) & pmask); + else if (flags & OPT_UID) { + pw = getpwuid(st->st_uid); + if (flags & OPT_ASCII) + printf("%s\n", pw ? pw->pw_name : "unknown"); + else + printf("%d\n", st->st_uid); + } else if (flags & OPT_GID) { + gr = getgrgid(st->st_gid); + if (flags & OPT_ASCII) + printf("%s\n", gr ? gr->gr_name : "unknown"); + else + printf("%d\n", st->st_gid); + } else if (flags & OPT_SIZE) + printf("%ld\n", st->st_size); + + return (0); +} + +#ifndef NOBUILTIN +int +finfo_builtin(list) + WORD_LIST *list; +{ + int c, r; + char **v; + WORD_LIST *l; + + v = make_builtin_argv (list, &c); + r = finfo_main (c, v); + free (v); + + return r; +} + +static char *finfo_doc[] = { + "Display information about each FILE. Only single operators should", + "be supplied. If no options are supplied, a summary of the info", + "available about each FILE is printed. If FILE is of the form", + "/dev/fd/XX, file descriptor XX is described. Operators, if supplied,", + "have the following meanings:", + "", + " -a last file access time", + " -A last file access time in ctime format", + " -c last file status change time", + " -C last file status change time in ctime format", + " -m last file modification time", + " -M last file modification time in ctime format", + " -d device", + " -i inode", + " -f composite file identifier (device:inode)", + " -g gid of owner", + " -G group name of owner", + " -l name of file pointed to by symlink", + " -n link count", + " -o permissions in octal", + " -p permissions in ascii", + " -P mask permissions ANDed with MASK (like with umask)", + " -s file size in bytes", + " -u uid of owner", + " -U user name of owner", + (char *)0 +}; + +struct builtin finfo_struct = { + "finfo", + finfo_builtin, + BUILTIN_ENABLED, + finfo_doc, + "finfo [-acdgiflmnopsuACGMPU] file [file...]", + 0 +}; +#endif + +#ifdef NOBUILTIN +#if defined (PREFER_STDARG) +# include <stdarg.h> +#else +# if defined (PREFER_VARARGS) +# include <varargs.h> +# endif +#endif + +char *this_command_name; + +main(argc, argv) +int argc; +char **argv; +{ + this_command_name = argv[0]; + exit(finfo_main(argc, argv)); +} + +void +builtin_usage() +{ + fprintf(stderr, "%s: usage: %s [-%s] [file ...]\n", prog, OPTIONS); +} + +#ifndef HAVE_STRERROR +char * +strerror(e) +int e; +{ + static char ebuf[40]; + extern int sys_nerr; + extern char *sys_errlist[]; + + if (e < 0 || e > sys_nerr) { + sprintf(ebuf,"Unknown error code %d", e); + return (&ebuf[0]); + } + return (sys_errlist[e]); +} +#endif + +char * +xmalloc(s) +size_t s; +{ + char *ret; + extern char *malloc(); + + ret = malloc(s); + if (ret) + return (ret); + fprintf(stderr, "%s: cannot malloc %d bytes\n", prog, s); + exit(1); +} + +char * +base_pathname(p) +char *p; +{ + char *t; + + if (t = strrchr(p, '/')) + return(++t); + return(p); +} + +int +legal_number (string, result) + char *string; + long *result; +{ + int sign; + long value; + + sign = 1; + value = 0; + + if (result) + *result = 0; + + /* Skip leading whitespace characters. */ + while (whitespace (*string)) + string++; + + if (!*string) + return (0); + + /* We allow leading `-' or `+'. */ + if (*string == '-' || *string == '+') + { + if (!digit (string[1])) + return (0); + + if (*string == '-') + sign = -1; + + string++; + } + + while (digit (*string)) + { + if (result) + value = (value * 10) + digit_value (*string); + string++; + } + + /* Skip trailing whitespace, if any. */ + while (whitespace (*string)) + string++; + + /* Error if not at end of string. */ + if (*string) + return (0); + + if (result) + *result = value * sign; + + return (1); +} + +int sh_optind; +char *sh_optarg; +int sh_opterr; + +extern int optind; +extern char *optarg; + +int +sh_getopt(c, v, o) +int c; +char **v, *o; +{ + int r; + + r = getopt(c, v, o); + sh_optind = optind; + sh_optarg = optarg; + return r; +} + +#if defined (USE_VARARGS) +void +#if defined (PREFER_STDARG) +builtin_error (const char *format, ...) +#else +builtin_error (format, va_alist) + const char *format; + va_dcl +#endif +{ + va_list args; + + if (this_command_name && *this_command_name) + fprintf (stderr, "%s: ", this_command_name); + +#if defined (PREFER_STDARG) + va_start (args, format); +#else + va_start (args); +#endif + + vfprintf (stderr, format, args); + va_end (args); + fprintf (stderr, "\n"); +} +#else +void +builtin_error (format, arg1, arg2, arg3, arg4, arg5) + char *format, *arg1, *arg2, *arg3, *arg4, *arg5; +{ + if (this_command_name && *this_command_name) + fprintf (stderr, "%s: ", this_command_name); + + fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5); + fprintf (stderr, "\n"); + fflush (stderr); +} +#endif /* !USE_VARARGS */ + +#endif diff --git a/examples/loadables/getconf.c b/examples/loadables/getconf.c new file mode 100644 index 0000000..0bf2079 --- /dev/null +++ b/examples/loadables/getconf.c @@ -0,0 +1,1488 @@ +/* + * ORIGINAL COPYRIGHT STATEMENT: + * + * Copyright (c) 1994 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * POSIX.2 getconf utility + * + * Originally Written by: + * J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc. + * + * Heavily modified for inclusion in bash by + * Chet Ramey <chet@po.cwru.edu> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif + +#include <stdio.h> +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <errno.h> + +#include "typemax.h" + +#include "bashansi.h" +#include "shell.h" +#include "builtins.h" +#include "stdc.h" +#include "common.h" +#include "bashgetopt.h" + +#include "getconf.h" + +#ifndef errno +extern int errno; +#endif + +struct conf_variable +{ + const char *name; + enum { SYSCONF, CONFSTR, PATHCONF, CONSTANT, LLCONST, G_UNDEF } type; + long value; +}; + +#ifndef HAVE_CONFSTR +static size_t confstr __P((int, char *, size_t)); +#endif + +#ifndef HAVE_SYSCONF +static long sysconf __P((int)); +#endif + +#ifndef HAVE_PATHCONF +static long pathconf __P((const char *, int)); +#endif + +/* Hack to `encode' values wider than long into a conf_variable */ +#define VAL_LLONG_MIN -1000 +#define VAL_LLONG_MAX -1001 +#define VAL_ULLONG_MAX -1002 + +static const struct conf_variable conf_table[] = +{ + /* POSIX.2 Configurable Variable Values */ + { "PATH", CONFSTR, _CS_PATH }, + { "CS_PATH", CONFSTR, _CS_PATH }, + + /* POSIX.1 Configurable Variable Values (only Solaris?) */ +#if defined (_CS_LFS_CFLAGS) + { "LFS_CFLAGS", CONFSTR, _CS_LFS_CFLAGS }, + { "LFS_LDFLAGS", CONFSTR, _CS_LFS_LDFLAGS }, + { "LFS_LIBS", CONFSTR, _CS_LFS_LIBS }, + { "LFS_LINTFLAGS", CONFSTR, _CS_LFS_LINTFLAGS }, +#endif +#if defined (_CS_LFS64_CFLAGS) + { "LFS64_CFLAGS", CONFSTR, _CS_LFS64_CFLAGS }, + { "LFS64_LDFLAGS", CONFSTR, _CS_LFS64_LDFLAGS }, + { "LFS64_LIBS", CONFSTR, _CS_LFS64_LIBS }, + { "LFS64_LINTFLAGS", CONFSTR, _CS_LFS64_LINTFLAGS }, +#endif + + /* Single UNIX Specification version 2 Configurable Variable Values. The + SYSCONF variables say whether or not the appropriate CONFSTR variables + are available. */ +#if defined (_SC_XBS5_ILP32_OFF32) + { "XBS5_ILP32_OFF32", SYSCONF, _SC_XBS5_ILP32_OFF32 }, + { "_XBS5_ILP32_OFF32", SYSCONF, _SC_XBS5_ILP32_OFF32 }, +#endif +#if defined (_CS_XBS5_ILP32_OFF32_CFLAGS) + { "XBS5_ILP32_OFF32_CFLAGS", CONFSTR, _CS_XBS5_ILP32_OFF32_CFLAGS }, + { "XBS5_ILP32_OFF32_LDFLAGS", CONFSTR, _CS_XBS5_ILP32_OFF32_LDFLAGS }, + { "XBS5_ILP32_OFF32_LIBS", CONFSTR, _CS_XBS5_ILP32_OFF32_LIBS }, + { "XBS5_ILP32_OFF32_LINTFLAGS", CONFSTR, _CS_XBS5_ILP32_OFF32_LINTFLAGS }, +#endif +#if defined (_SC_XBS5_ILP32_OFFBIG) + { "XBS5_ILP32_OFFBIG", SYSCONF, _SC_XBS5_ILP32_OFFBIG }, + { "_XBS5_ILP32_OFFBIG", SYSCONF, _SC_XBS5_ILP32_OFFBIG }, +#endif +#if defined (_CS_XBS5_ILP32_OFFBIG_CFLAGS) + { "XBS5_ILP32_OFFBIG_CFLAGS", CONFSTR, _CS_XBS5_ILP32_OFFBIG_CFLAGS }, + { "XBS5_ILP32_OFFBIG_LDFLAGS", CONFSTR, _CS_XBS5_ILP32_OFFBIG_LDFLAGS }, + { "XBS5_ILP32_OFFBIG_LIBS", CONFSTR, _CS_XBS5_ILP32_OFFBIG_LIBS }, + { "XBS5_ILP32_OFFBIG_LINTFLAGS", CONFSTR, _CS_XBS5_ILP32_OFFBIG_LINTFLAGS }, +#endif +#if defined (_SC_XBS5_LP64_OFF64) + { "XBS5_LP64_OFF64", SYSCONF, _SC_XBS5_LP64_OFF64 }, + { "_XBS5_LP64_OFF64", SYSCONF, _SC_XBS5_LP64_OFF64 }, +#endif +#if defined (_CS_XBS5_LP64_OFF64_CFLAGS) + { "XBS5_LP64_OFF64_CFLAGS", CONFSTR, _CS_XBS5_LP64_OFF64_CFLAGS }, + { "XBS5_LP64_OFF64_LDFLAGS", CONFSTR, _CS_XBS5_LP64_OFF64_LDFLAGS }, + { "XBS5_LP64_OFF64_LIBS", CONFSTR, _CS_XBS5_LP64_OFF64_LIBS }, + { "XBS5_LP64_OFF64_LINTFLAGS", CONFSTR, _CS_XBS5_LP64_OFF64_LINTFLAGS }, +#endif +#if defined (_SC_XBS5_LPBIG_OFFBIG) + { "XBS5_LPBIG_OFFBIG", SYSCONF, _SC_XBS5_LPBIG_OFFBIG }, + { "_XBS5_LPBIG_OFFBIG", SYSCONF, _SC_XBS5_LPBIG_OFFBIG }, +#endif +#if defined (_CS_XBS5_LPBIG_OFFBIG_CFLAGS) + { "XBS5_LPBIG_OFFBIG_CFLAGS", CONFSTR, _CS_XBS5_LPBIG_OFFBIG_CFLAGS }, + { "XBS5_LPBIG_OFFBIG_LDFLAGS", CONFSTR, _CS_XBS5_LPBIG_OFFBIG_LDFLAGS }, + { "XBS5_LPBIG_OFFBIG_LIBS", CONFSTR, _CS_XBS5_LPBIG_OFFBIG_LIBS }, + { "XBS5_LPBIG_OFFBIG_LINTFLAGS", CONFSTR, _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS }, +#endif + + /* Single UNIX Specification version 3 (POSIX.1-200x) Configurable Variable + Values. The SYSCONF variables say whether or not the appropriate CONFSTR + variables are available. */ + +#if defined (_SC_POSIX_V6_ILP32_OFF32) + { "_POSIX_V6_ILP32_OFF32", SYSCONF, _SC_POSIX_V6_ILP32_OFF32 }, +#endif +#if defined (_CS_POSIX_V6_ILP32_OFF32_CFLAGS) + { "POSIX_V6_ILP32_OFF32_CFLAGS", CONFSTR, _CS_POSIX_V6_ILP32_OFF32_CFLAGS }, + { "POSIX_V6_ILP32_OFF32_LDFLAGS", CONFSTR, _CS_POSIX_V6_ILP32_OFF32_LDFLAGS }, + { "POSIX_V6_ILP32_OFF32_LIBS", CONFSTR, _CS_POSIX_V6_ILP32_OFF32_LIBS }, +#endif +#if defined (_CS_POSIX_V6_ILP32_OFF32_LINTFLAGS) + { "POSIX_V6_ILP32_OFF32_LINTFLAGS", CONFSTR, _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS }, +#endif +#if defined (_SC_POSIX_V6_ILP32_OFFBIG) + { "_POSIX_V6_ILP32_OFFBIG", SYSCONF, _SC_POSIX_V6_ILP32_OFFBIG }, +#endif +#if defined (_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS) + { "POSIX_V6_ILP32_OFFBIG_CFLAGS", CONFSTR, _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS }, + { "POSIX_V6_ILP32_OFFBIG_LDFLAGS", CONFSTR, _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS }, + { "POSIX_V6_ILP32_OFFBIG_LIBS", CONFSTR, _CS_POSIX_V6_ILP32_OFFBIG_LIBS }, +#endif +#if defined (_CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS) + { "POSIX_V6_ILP32_OFFBIG_LINTFLAGS", CONFSTR, _CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS }, +#endif +#if defined (_SC_POSIX_V6_LP64_OFF64) + { "_POSIX_V6_LP64_OFF64", SYSCONF, _SC_POSIX_V6_LP64_OFF64 }, +#endif +#if defined (_CS_POSIX_V6_LP64_OFF64_CFLAGS) + { "POSIX_V6_LP64_OFF64_CFLAGS", CONFSTR, _CS_POSIX_V6_LP64_OFF64_CFLAGS }, + { "POSIX_V6_LP64_OFF64_LDFLAGS", CONFSTR, _CS_POSIX_V6_LP64_OFF64_LDFLAGS }, + { "POSIX_V6_LP64_OFF64_LIBS", CONFSTR, _CS_POSIX_V6_LP64_OFF64_LIBS }, +#endif +#if defined (CS_POSIX_V6_LP64_OFF64_LINTFLAGS) + { "POSIX_V6_LP64_OFF64_LINTFLAGS", CONFSTR, _CS_POSIX_V6_LP64_OFF64_LINTFLAGS }, +#endif +#if defined (_SC_POSIX_V6_LPBIG_OFFBIG) + { "_POSIX_V6_LPBIG_OFFBIG", SYSCONF, _SC_POSIX_V6_LPBIG_OFFBIG }, +#endif +#if defined (_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS) + { "POSIX_V6_LPBIG_OFFBIG_CFLAGS", CONFSTR, _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS }, + { "POSIX_V6_LPBIG_OFFBIG_LDFLAGS", CONFSTR, _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS }, + { "POSIX_V6_LPBIG_OFFBIG_LIBS", CONFSTR, _CS_POSIX_V6_LPBIG_OFFBIG_LIBS }, +#endif +#if defined (_CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS) + { "POSIX_V6_LPBIG_OFFBIG_LINTFLAGS", CONFSTR, _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS }, +#endif + +#if defined (_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS) + { "POSIX_6_WIDTH_RESTRICTED_ENVS", CONFSTR, _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS }, +#endif + + /* POSIX.2 Utility Limit Minimum Values */ +#ifdef _POSIX2_BC_BASE_MAX + { "POSIX2_BC_BASE_MAX", CONSTANT, _POSIX2_BC_BASE_MAX }, +#else + { "POSIX2_BC_BASE_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_BC_DIM_MAX + { "POSIX2_BC_DIM_MAX", CONSTANT, _POSIX2_BC_DIM_MAX }, +#else + { "POSIX2_BC_DIM_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_BC_SCALE_MAX + { "POSIX2_BC_SCALE_MAX", CONSTANT, _POSIX2_BC_SCALE_MAX }, +#else + { "POSIX2_BC_SCALE_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_BC_STRING_MAX + { "POSIX2_BC_STRING_MAX", CONSTANT, _POSIX2_BC_STRING_MAX }, +#else + { "POSIX2_BC_STRING_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_CHARCLASS_NAME_MAX + { "POSIX2_CHARCLASS_NAME_MAX", CONSTANT, _POSIX2_CHARCLASS_NAME_MAX }, +#else + { "POSIX2_CHARCLASS_NAME_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_COLL_WEIGHTS_MAX + { "POSIX2_COLL_WEIGHTS_MAX", CONSTANT, _POSIX2_COLL_WEIGHTS_MAX }, +#else + { "POSIX2_COLL_WEIGHTS_MAX", G_UNDEF, -1 }, +#endif +#if defined (_POSIX2_EQUIV_CLASS_MAX) + { "POSIX2_EQUIV_CLASS_MAX", CONSTANT, _POSIX2_EQUIV_CLASS_MAX }, +#endif +#ifdef _POSIX2_EXPR_NEST_MAX + { "POSIX2_EXPR_NEST_MAX", CONSTANT, _POSIX2_EXPR_NEST_MAX }, +#else + { "POSIX2_EXPR_NEST_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_LINE_MAX + { "POSIX2_LINE_MAX", CONSTANT, _POSIX2_LINE_MAX }, +#else + { "POSIX2_LINE_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_RE_DUP_MAX + { "POSIX2_RE_DUP_MAX", CONSTANT, _POSIX2_RE_DUP_MAX }, +#else + { "POSIX2_RE_DUP_MAX", G_UNDEF, -1 }, +#endif +#if defined (_POSIX2_VERSION) + { "POSIX2_VERSION", CONSTANT, _POSIX2_VERSION }, +#else +# if !defined (_SC_2_VERSION) + { "POSIX2_VERSION", G_UNDEF, -1 }, +# endif +#endif + +#ifdef _POSIX2_BC_BASE_MAX + { "_POSIX2_BC_BASE_MAX", CONSTANT, _POSIX2_BC_BASE_MAX }, +#else + { "_POSIX2_BC_BASE_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_BC_DIM_MAX + { "_POSIX2_BC_DIM_MAX", CONSTANT, _POSIX2_BC_DIM_MAX }, +#else + { "_POSIX2_BC_DIM_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_BC_SCALE_MAX + { "_POSIX2_BC_SCALE_MAX", CONSTANT, _POSIX2_BC_SCALE_MAX }, +#else + { "_POSIX2_BC_SCALE_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_BC_STRING_MAX + { "_POSIX2_BC_STRING_MAX", CONSTANT, _POSIX2_BC_STRING_MAX }, +#else + { "_POSIX2_BC_STRING_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_CHARCLASS_NAME_MAX + { "_POSIX2_CHARCLASS_NAME_MAX", CONSTANT, _POSIX2_CHARCLASS_NAME_MAX }, +#else + { "_POSIX2_CHARCLASS_NAME_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_COLL_WEIGHTS_MAX + { "_POSIX2_COLL_WEIGHTS_MAX", CONSTANT, _POSIX2_COLL_WEIGHTS_MAX }, +#else + { "_POSIX2_COLL_WEIGHTS_MAX", G_UNDEF, -1 }, +#endif +#if defined (_POSIX2_EQUIV_CLASS_MAX) + { "POSIX2_EQUIV_CLASS_MAX", CONSTANT, _POSIX2_EQUIV_CLASS_MAX }, +#endif +#ifdef _POSIX2_EXPR_NEST_MAX + { "_POSIX2_EXPR_NEST_MAX", CONSTANT, _POSIX2_EXPR_NEST_MAX }, +#else + { "_POSIX2_EXPR_NEST_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_LINE_MAX + { "_POSIX2_LINE_MAX", CONSTANT, _POSIX2_LINE_MAX }, +#else + { "_POSIX2_LINE_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX2_RE_DUP_MAX + { "_POSIX2_RE_DUP_MAX", CONSTANT, _POSIX2_RE_DUP_MAX }, +#else + { "_POSIX2_RE_DUP_MAX", G_UNDEF, -1 }, +#endif + + /* X/OPEN Maxmimum Values */ +#ifdef _XOPEN_IOV_MAX + { "_XOPEN_IOV_MAX", CONSTANT, _XOPEN_IOV_MAX }, +#else + { "_XOPEN_IOV_MAX", G_UNDEF, -1 }, +#endif +#ifdef _XOPEN_NAME_MAX + { "_XOPEN_NAME_MAX", CONSTANT, _XOPEN_NAME_MAX }, +#else + { "_XOPEN_NAME_MAX", G_UNDEF, -1 }, +#endif +#ifdef _XOPEN_PATH_MAX + { "_XOPEN_PATH_MAX", CONSTANT, _XOPEN_PATH_MAX }, +#else + { "_XOPEN_PATH_MAX", G_UNDEF, -1 }, +#endif + + /* POSIX.1 Minimum Values */ +#ifdef _POSIX_AIO_LISTIO_MAX + { "_POSIX_AIO_LISTIO_MAX", CONSTANT, _POSIX_AIO_LISTIO_MAX }, +#else + { "_POSIX_AIO_LISTIO_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_AIO_MAX + { "_POSIX_AIO_MAX", CONSTANT, _POSIX_AIO_MAX }, +#else + { "_POSIX_AIO_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_ARG_MAX + { "_POSIX_ARG_MAX", CONSTANT, _POSIX_ARG_MAX }, +#else + { "_POSIX_ARG_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_CHILD_MAX + { "_POSIX_CHILD_MAX", CONSTANT, _POSIX_CHILD_MAX }, +#else + { "_POSIX_CHILD_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_DELAYTIMER_MAX + { "_POSIX_DELAYTIMER_MAX", CONSTANT, _POSIX_DELAYTIMER_MAX }, +#else + { "_POSIX_DELAYTIMER_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_HOST_NAME_MAX + { "_POSIX_HOST_NAME_MAX", CONSTANT, _POSIX_HOST_NAME_MAX }, +#else + { "_POSIX_HOST_NAME_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_LINK_MAX + { "_POSIX_LINK_MAX", CONSTANT, _POSIX_LINK_MAX }, +#else + { "_POSIX_LINK_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_LOGIN_NAME_MAX + { "_POSIX_LOGIN_NAME_MAX", CONSTANT, _POSIX_LOGIN_NAME_MAX }, +#else + { "_POSIX_LOGIN_NAME_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_MAX_CANON + { "_POSIX_MAX_CANON", CONSTANT, _POSIX_MAX_CANON }, +#else + { "_POSIX_MAX_CANON", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_MAX_INPUT + { "_POSIX_MAX_INPUT", CONSTANT, _POSIX_MAX_INPUT }, +#else + { "_POSIX_MAX_INPUT", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_MQ_OPEN_MAX + { "_POSIX_MQ_OPEN_MAX", CONSTANT, _POSIX_MQ_OPEN_MAX }, +#else + { "_POSIX_MQ_OPEN_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_MQ_PRIO_MAX + { "_POSIX_MQ_PRIO_MAX", CONSTANT, _POSIX_MQ_PRIO_MAX }, +#else + { "_POSIX_MQ_PRIO_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_NAME_MAX + { "_POSIX_NAME_MAX", CONSTANT, _POSIX_NAME_MAX }, +#else + { "_POSIX_NAME_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_NGROUPS_MAX + { "_POSIX_NGROUPS_MAX", CONSTANT, _POSIX_NGROUPS_MAX }, +#else + { "_POSIX_NGROUPS_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_OPEN_MAX + { "_POSIX_OPEN_MAX", CONSTANT, _POSIX_OPEN_MAX }, +#else + { "_POSIX_OPEN_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_PATH_MAX + { "_POSIX_PATH_MAX", CONSTANT, _POSIX_PATH_MAX }, +#else + { "_POSIX_PATH_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_PIPE_BUF + { "_POSIX_PIPE_BUF", CONSTANT, _POSIX_PIPE_BUF }, +#else + { "_POSIX_PIPE_BUF", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_RE_DUP_MAX + { "_POSIX_RE_DUP_MAX", CONSTANT, _POSIX_RE_DUP_MAX }, +#else + { "_POSIX_RE_DUP_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_RTSIG_MAX + { "_POSIX_RTSIG_MAX", CONSTANT, _POSIX_RTSIG_MAX }, +#else + { "_POSIX_RTSIG_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_SEM_NSEMS_MAX + { "_POSIX_SEM_NSEMS_MAX", CONSTANT, _POSIX_SEM_NSEMS_MAX }, +#else + { "_POSIX_SEM_NSEMS_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_SEM_VALUE_MAX + { "_POSIX_SEM_VALUE_MAX", CONSTANT, _POSIX_SEM_VALUE_MAX }, +#else + { "_POSIX_SEM_VALUE_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_SIGQUEUE_MAX + { "_POSIX_SIGQUEUE_MAX", CONSTANT, _POSIX_SIGQUEUE_MAX }, +#else + { "_POSIX_SIGQUEUE_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_SSIZE_MAX + { "_POSIX_SSIZE_MAX", CONSTANT, _POSIX_SSIZE_MAX }, +#else + { "_POSIX_SSIZE_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_SS_REPL_MAX + { "_POSIX_SS_REPL_MAX", CONSTANT, _POSIX_SS_REPL_MAX }, +#else + { "_POSIX_SS_REPL_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_STREAM_MAX + { "_POSIX_STREAM_MAX", CONSTANT, _POSIX_STREAM_MAX }, +#else + { "_POSIX_STREAM_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_SYMLINK_MAX + { "_POSIX_SYMLINK_MAX", CONSTANT, _POSIX_SYMLINK_MAX }, +#else + { "_POSIX_SYMLINK_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_SYMLOOP_MAX + { "_POSIX_SYMLOOP_MAX", CONSTANT, _POSIX_SYMLOOP_MAX }, +#else + { "_POSIX_SYMLOOP_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_THREAD_DESTRUCTOR_ITERATIONS + { "_POSIX_THREAD_DESTRUCTOR_ITERATIONS", CONSTANT, _POSIX_THREAD_DESTRUCTOR_ITERATIONS }, +#else + { "_POSIX_THREAD_DESTRUCTOR_ITERATIONS", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_THREAD_KEYS_MAX + { "_POSIX_THREAD_KEYS_MAX", CONSTANT, _POSIX_THREAD_KEYS_MAX }, +#else + { "_POSIX_THREAD_KEYS_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_THREAD_THREADS_MAX + { "_POSIX_THREAD_THREADS_MAX",CONSTANT, _POSIX_THREAD_THREADS_MAX }, +#else + { "_POSIX_THREAD_THREADS_MAX",G_UNDEF, -1 }, +#endif +#ifdef _POSIX_TIMER_MAX + { "_POSIX_TIMER_MAX", CONSTANT, _POSIX_TIMER_MAX }, +#else + { "_POSIX_TIMER_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_TRACE_EVENT_NAME_MAX + { "_POSIX_TRACE_EVENT_NAME_MAX", CONSTANT, _POSIX_TRACE_EVENT_NAME_MAX }, +#else + { "_POSIX_TRACE_EVENT_NAME_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_TRACE_NAME_MAX + { "_POSIX_TRACE_NAME_MAX", CONSTANT, _POSIX_TRACE_NAME_MAX }, +#else + { "_POSIX_TRACE_NAME_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_TRACE_SYS_MAX + { "_POSIX_TRACE_SYS_MAX", CONSTANT, _POSIX_TRACE_SYS_MAX }, +#else + { "_POSIX_TRACE_SYS_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_TRACE_USER_EVENT_MAX + { "_POSIX_TRACE_USER_EVENT_MAX", CONSTANT, _POSIX_TRACE_USER_EVENT_MAX }, +#else + { "_POSIX_TRACE_USER_EVENT_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_TTY_NAME_MAX + { "_POSIX_TTY_NAME_MAX", CONSTANT, _POSIX_TTY_NAME_MAX }, +#else + { "_POSIX_TTY_NAME_MAX", G_UNDEF, -1 }, +#endif +#ifdef _POSIX_TZNAME_MAX + { "_POSIX_TZNAME_MAX", CONSTANT, _POSIX_TZNAME_MAX }, +#else + { "_POSIX_TZNAME_MAX", G_UNDEF, -1 }, +#endif + + /* POSIX.1 Maximum Values */ +#ifdef _POSIX_CLOCKRES_MIN + { "_POSIX_CLOCKRES_MIN", CONSTANT, _POSIX_CLOCKRES_MIN }, +#else + { "_POSIX_CLOCKRES_MIN", G_UNDEF, -1 }, +#endif + + /* POSIX.1-2001/XPG6 (and later) Runtime Invariants from <limits.h> */ +#ifdef _SC_SS_REPL_MAX + { "SS_REPL_MAX", SYSCONF, _SC_SS_REPL_MAX }, +#endif +#ifdef _SC_TRACE_EVENT_NAME_MAX + { "TRACE_EVENT_NAME_MAX", SYSCONF, _SC_TRACE_EVENT_NAME_MAX }, +#endif +#ifdef _SC_TRACE_NAME_MAX + { "TRACE_NAME_MAX", SYSCONF, _SC_TRACE_NAME_MAX }, +#endif +#ifdef _SC_TRACE_SYS_MAX + { "TRACE_SYS_MAX", SYSCONF, _SC_TRACE_SYS_MAX }, +#endif +#ifdef _SC_TRACE_USER_EVENT_MAX + { "TRACE_USER_EVENT_MAX", SYSCONF, _SC_TRACE_USER_EVENT_MAX }, +#endif + + /* POSIX.2/XPG 4.2 (and later) Symbolic Utility Limits */ +#ifdef _SC_BC_BASE_MAX + { "BC_BASE_MAX", SYSCONF, _SC_BC_BASE_MAX }, +#endif +#ifdef _SC_BC_DIM_MAX + { "BC_DIM_MAX", SYSCONF, _SC_BC_DIM_MAX }, +#endif +#ifdef _SC_BC_SCALE_MAX + { "BC_SCALE_MAX", SYSCONF, _SC_BC_SCALE_MAX }, +#endif +#ifdef _SC_BC_STRING_MAX + { "BC_STRING_MAX", SYSCONF, _SC_BC_STRING_MAX }, +#endif +#ifdef CHARCLASS_NAME_MAX + { "CHARCLASS_NAME_MAX", CONSTANT, CHARCLASS_NAME_MAX }, +#endif +#ifdef _SC_COLL_WEIGHTS_MAX + { "COLL_WEIGHTS_MAX", SYSCONF, _SC_COLL_WEIGHTS_MAX }, +#endif +#ifdef _SC_EXPR_NEST_MAX + { "EXPR_NEST_MAX", SYSCONF, _SC_EXPR_NEST_MAX }, +#endif +#ifdef _SC_LINE_MAX + { "LINE_MAX", SYSCONF, _SC_LINE_MAX }, +#endif +# ifdef NL_ARGMAX + { "NL_ARGMAX", CONSTANT, NL_ARGMAX }, +#endif +#ifdef NL_LANGMAX + { "NL_LANGMAX", CONSTANT, NL_LANGMAX }, +#endif +#ifdef NL_MSGMAX + { "NL_MSGMAX", CONSTANT, NL_MSGMAX }, +#endif +#ifdef NL_NMAX + { "NL_NMAX", CONSTANT, NL_NMAX }, +#endif +#ifdef NL_SETMAX + { "NL_SETMAX", CONSTANT, NL_SETMAX }, +#endif +#ifdef NL_TEXTMAX + { "NL_TEXTMAX", CONSTANT, NL_TEXTMAX }, +#endif +#ifdef _SC_RAW_SOCKET + { "RAW_SOCKET", SYSCONF, _SC_RAW_SOCKET }, +#endif +#ifdef _SC_RE_DUP_MAX + { "RE_DUP_MAX", SYSCONF, _SC_RE_DUP_MAX }, +#endif + + /* POSIX.2 Optional Facility Configuration Values */ +#ifdef _SC_2_C_BIND + { "POSIX2_C_BIND", SYSCONF, _SC_2_C_BIND }, +#else + { "POSIX2_C_BIND", G_UNDEF, -1 }, +#endif +#ifdef _SC_2_C_DEV + { "POSIX2_C_DEV", SYSCONF, _SC_2_C_DEV }, +#else + { "POSIX2_C_DEV", G_UNDEF, -1 }, +#endif +#if defined (_SC_2_C_VERSION) + { "POSIX2_C_VERSION", SYSCONF, _SC_2_C_VERSION }, +#else + { "POSIX2_C_VERSION", G_UNDEF, -1 }, +#endif +#if defined (_SC_2_CHAR_TERM) + { "POSIX2_CHAR_TERM", SYSCONF, _SC_2_CHAR_TERM }, +#else + { "POSIX2_CHAR_TERM", G_UNDEF, -1 }, +#endif +#ifdef _SC_2_FORT_DEV + { "POSIX2_FORT_DEV", SYSCONF, _SC_2_FORT_DEV }, +#else + { "POSIX2_FORT_DEV", G_UNDEF, -1 }, +#endif +#ifdef _SC_2_FORT_RUN + { "POSIX2_FORT_RUN", SYSCONF, _SC_2_FORT_RUN }, +#else + { "POSIX2_FORT_RUN", G_UNDEF, -1 }, +#endif +#ifdef _SC_2_LOCALEDEF + { "POSIX2_LOCALEDEF", SYSCONF, _SC_2_LOCALEDEF }, +#else + { "POSIX2_LOCALEDEF", G_UNDEF, -1 }, +#endif +#ifdef _SC_2_SW_DEV + { "POSIX2_SW_DEV", SYSCONF, _SC_2_SW_DEV }, +#else + { "POSIX2_SW_DEV", G_UNDEF, -1 }, +#endif +#if defined (_SC2_UPE) + { "POSIX2_UPE", SYSCONF, _SC_2_UPE }, +#else + { "POSIX2_UPE", G_UNDEF, -1 }, +#endif +#if !defined (_POSIX2_VERSION) && defined (_SC_2_VERSION) + { "_POSIX2_VERSION", SYSCONF, _SC_2_VERSION }, +#else + { "_POSIX2_VERSION", G_UNDEF, -1 }, +#endif +#if defined (_SC_REGEX_VERSION) + { "REGEX_VERSION", SYSCONF, _SC_REGEX_VERSION }, + { "_REGEX_VERSION", SYSCONF, _SC_REGEX_VERSION }, +#else + { "REGEX_VERSION", G_UNDEF, -1 }, + { "_REGEX_VERSION", G_UNDEF, -1 }, +#endif + +#if defined (_SC_2_PBS) + { "_POSIX2_PBS", SYSCONF, _SC_2_PBS }, + { "_POSIX2_PBS_ACCOUNTING", SYSCONF, _SC_2_PBS_ACCOUNTING }, +# if defined (_SC_2_PBS_CHECKPOINT) + { "_POSIX2_PBS_CHECKPOINT", SYSCONF, _SC_2_PBS_CHECKPOINT }, +# endif + { "_POSIX2_PBS_LOCATE", SYSCONF, _SC_2_PBS_LOCATE }, + { "_POSIX2_PBS_MESSAGE", SYSCONF, _SC_2_PBS_MESSAGE }, + { "_POSIX2_PBS_TRACK", SYSCONF, _SC_2_PBS_TRACK }, +#endif + + /* POSIX.1 Configurable System Variables */ +#ifdef _SC_ARG_MAX + { "ARG_MAX", SYSCONF, _SC_ARG_MAX }, +#endif +#ifdef _SC_CHILD_MAX + { "CHILD_MAX", SYSCONF, _SC_CHILD_MAX }, +#endif +#ifdef _SC_CLK_TCK + { "CLK_TCK", SYSCONF, _SC_CLK_TCK }, +#endif +#ifdef _SC_DELAYTIMER_MAX + { "DELAYTIMER_MAX", SYSCONF, _SC_DELAYTIMER_MAX }, +#endif +#ifdef _SC_NGROUPS_MAX + { "NGROUPS_MAX", SYSCONF, _SC_NGROUPS_MAX }, +#endif +#ifdef NZERO + { "NZERO", CONSTANT, NZERO }, +#endif +#ifdef _SC_OPEN_MAX + { "OPEN_MAX", SYSCONF, _SC_OPEN_MAX }, +#endif +#ifdef PASS_MAX + { "PASS_MAX", CONSTANT, PASS_MAX }, +#endif +#ifdef _SC_STREAM_MAX + { "STREAM_MAX", SYSCONF, _SC_STREAM_MAX }, +#endif +#ifdef TMP_MAX + { "TMP_MAX", CONSTANT, TMP_MAX }, +#endif +#ifdef _SC_TZNAME_MAX + { "TZNAME_MAX", SYSCONF, _SC_TZNAME_MAX }, +#endif + + /* POSIX.1 Optional Facility Configuration Values */ +#if defined (_SC_ADVISORY_INFO) + { "_POSIX_ADVISORY_INFO", SYSCONF, _SC_ADVISORY_INFO }, +#endif +#if defined (_SC_ASYNCHRONOUS_IO) + { "_POSIX_ASYNCHRONOUS_IO", SYSCONF, _SC_ASYNCHRONOUS_IO }, +#endif +#if defined (_SC_BARRIERS) + { "_POSIX_BARRIERS", SYSCONF, _SC_BARRIERS }, +#endif +#if defined (_SC_BASE) + { "_POSIX_BASE", SYSCONF, _SC_BASE }, +#endif +#if defined (_SC_C_LANG_SUPPORT) + { "_POSIX_C_LANG_SUPPORT", SYSCONF, _SC_C_LANG_SUPPORT }, +#endif +#if defined (_SC_C_LANG_SUPPORT_R) + { "_POSIX_C_LANG_SUPPORT_R", SYSCONF, _SC_C_LANG_SUPPORT_R }, +#endif +#if defined (_SC_CLOCK_SELECTION) + { "_POSIX_CLOCK_SELECTION", SYSCONF, _SC_CLOCK_SELECTION }, +#endif +#if defined (_SC_CPUTIME) + { "_POSIX_CPUTIME", SYSCONF, _SC_CPUTIME }, +#endif +#if defined (_SC_DEVICE_IO) + { "_POSIX_DEVICE_IO", SYSCONF, _SC_DEVICE_IO }, +#endif +#if defined (_SC_DEVICE_SPECIFIC) + { "_POSIX_DEVICE_SPECIFIC", SYSCONF, _SC_DEVICE_SPECIFIC }, +#endif +#if defined (_SC_DEVICE_SPECIFIC_R) + { "_POSIX_DEVICE_SPECIFIC_R", SYSCONF, _SC_DEVICE_SPECIFIC_R }, +#endif +#if defined (_SC_FD_MGMT) + { "_POSIX_FD_MGMT", SYSCONF, _SC_FD_MGMT }, +#endif +#if defined (_SC_FIFO) + { "_POSIX_FIFO", SYSCONF, _SC_FIFO }, +#endif +#if defined (_SC_FILE_ATTRIBUTES) + { "_POSIX_FILE_ATTRIBUTES", SYSCONF, _SC_FILE_ATTRIBUTES }, +#endif +#if defined (_SC_FILE_LOCKING) + { "_POSIX_FILE_LOCKING", SYSCONF, _SC_FILE_LOCKING }, +#endif +#if defined (_SC_FILE_SYSTEM) + { "_POSIX_FILE_SYSTEM", SYSCONF, _SC_FILE_SYSTEM }, +#endif +#if defined (_SC_FSYNC) + { "_POSIX_FSYNC", SYSCONF, _SC_FSYNC }, +#endif +#if defined (_SC_IPV6) + { "_POSIX_IPV6", SYSCONF, _SC_IPV6 }, +#endif +#if defined (_SC_JOB_CONTROL) + { "_POSIX_JOB_CONTROL", SYSCONF, _SC_JOB_CONTROL }, +#endif +#if defined (_SC_MAPPED_FILES) + { "_POSIX_MAPPED_FILES", SYSCONF, _SC_MAPPED_FILES }, +#endif +#if defined (_SC_MEMLOCK) + { "_POSIX_MEMLOCK", SYSCONF, _SC_MEMLOCK }, +#endif +#if defined (_SC_MEMLOCK_RANGE) + { "_POSIX_MEMLOCK_RANGE", SYSCONF, _SC_MEMLOCK_RANGE }, +#endif +#if defined (_SC_MEMORY_PROTECTION) + { "_POSIX_MEMORY_PROTECTION", SYSCONF, _SC_MEMORY_PROTECTION }, +#endif +#if defined (_SC_MESSAGE_PASSING) + { "_POSIX_MESSAGE_PASSING", SYSCONF, _SC_MESSAGE_PASSING }, +#endif +#if defined (_SC_MONOTONIC_CLOCK) + { "_POSIX_MONOTONIC_CLOCK", SYSCONF, _SC_MONOTONIC_CLOCK }, +#endif +#if defined (_SC_MULTI_PROCESS) + { "_POSIX_MULTI_PROCESS", SYSCONF, _SC_MULTI_PROCESS }, +#endif +#if defined (_SC_NETWORKING) + { "_POSIX_NETWORKING", SYSCONF, _SC_NETWORKING }, +#endif +#if defined (_SC_PIPE) + { "_POSIX_PIPE", SYSCONF, _SC_PIPE }, +#endif +#if defined (SC_PRIORITIZED_IO) + { "_POSIX_PRIORITIZED_IO", SYSCONF, _SC_PRIORITIZED_IO }, +#endif +#if defined (_SC_PRIORITY_SCHEDULING) + { "_POSIX_PRIORITY_SCHEDULING", SYSCONF, _SC_PRIORITY_SCHEDULING }, +#endif +#if defined (_SC_READER_WRITER_LOCKS) + { "_POSIX_READER_WRITER_LOCKS", SYSCONF, _SC_READER_WRITER_LOCKS }, +#endif +#if defined (_SC_RAW_SOCKETS) + { "_POSIX_RAW_SOCKETS", SYSCONF, _SC_RAW_SOCKETS }, +#endif +#if defined (_SC_REALTIME_SIGNALS) + { "_POSIX_REALTIME_SIGNALS", SYSCONF, _SC_REALTIME_SIGNALS }, +#endif +#if defined (_SC_REGEXP) + { "_POSIX_REGEXP", SYSCONF, _SC_REGEXP }, +#endif +#if defined (_SC_SAVED_IDS) + { "_POSIX_SAVED_IDS", SYSCONF, _SC_SAVED_IDS }, +#endif +#if defined (_SC_SEMAPHORES) + { "_POSIX_SEMAPHORES", SYSCONF, _SC_SEMAPHORES }, +#endif +#if defined (_SC_SHARED_MEMORY_OBJECTS) + { "_POSIX_SHARED_MEMORY_OBJECTS", SYSCONF, _SC_SHARED_MEMORY_OBJECTS }, +#endif + { "_POSIX_SHELL", CONSTANT, 1 }, +#if defined (_SC_SIGNALS) + { "_POSIX_SIGNALS", SYSCONF, _SC_SIGNALS }, +#endif +#if defined (_SC_SINGLE_PROCESS) + { "_POSIX_SINGLE_PROCESS", SYSCONF, _SC_SINGLE_PROCESS }, +#endif +#if defined (_SC_SPAWN) + { "_POSIX_SPAWN", SYSCONF, _SC_SPAWN }, +#endif +#if defined (_SC_SPIN_LOCKS) + { "_POSIX_SPIN_LOCKS", SYSCONF, _SC_SPIN_LOCKS }, +#endif +#if defined (_SC_SPORADIC_SERVER) + { "_POSIX_SPORADIC_SERVER", SYSCONF, _SC_SPORADIC_SERVER }, +#endif +#if defined (_SC_SYMLOOP_MAX) + { "_POSIX_SYMLOOP_MAX", SYSCONF, _SC_SYMLOOP_MAX }, +#endif +#if defined (_SC_SYNCHRONIZED_IO) + { "_POSIX_SYNCHRONIZED_IO", SYSCONF, _SC_SYNCHRONIZED_IO }, +#endif +#if defined (_SC_SYSTEM_DATABASE) + { "_POSIX_SYSTEM_DATABASE", SYSCONF, _SC_SYSTEM_DATABASE }, +#endif +#if defined (_SC_SYSTEM_DATABASE_R) + { "_POSIX_SYSTEM_DATABASE_R", SYSCONF, _SC_SYSTEM_DATABASE_R }, +#endif +#if defined (_SC_THREAD_ATTR_STACKADDR) + { "_POSIX_THREAD_ATTR_STACKADDR", SYSCONF, _SC_THREAD_ATTR_STACKADDR }, +#endif +#if defined (_SC_THREAD_ATTR_STACKSIZE) + { "_POSIX_THREAD_ATTR_STACKSIZE", SYSCONF, _SC_THREAD_ATTR_STACKSIZE }, +#endif +#if defined (_SC_THREAD_CPUTIME) + { "_POSIX_THREAD_CPUTIME", SYSCONF, _SC_THREAD_CPUTIME }, +#endif +#if defined (_SC_THREAD_PRIO_INHERIT) + { "_POSIX_THREAD_PRIO_INHERIT", SYSCONF, _SC_THREAD_PRIO_INHERIT }, +#endif +#if defined (_SC_THREAD_PRIO_PROTECT) + { "_POSIX_THREAD_PRIO_PROTECT", SYSCONF, _SC_THREAD_PRIO_PROTECT }, +#endif +#if defined (_SC_THREAD_PRIORITY_SCHEDULING) + { "_POSIX_THREAD_PRIORITY_SCHEDULING", SYSCONF, _SC_THREAD_PRIORITY_SCHEDULING }, +#endif +#if defined (_SC_THREAD_PROCESS_SHARED) + { "_POSIX_THREAD_PROCESS_SHARED", SYSCONF, _SC_THREAD_PROCESS_SHARED }, +#endif +#if defined (_SC_THREAD_SAFE_FUNCTIONS) + { "_POSIX_THREAD_SAFE_FUNCTIONS", SYSCONF, _SC_THREAD_SAFE_FUNCTIONS }, +#endif +#if defined (_SC_THREAD_SPORADIC_SERVER) + { "_POSIX_THREAD_SPORADIC_SERVER", SYSCONF, _SC_THREAD_SPORADIC_SERVER }, +#endif +#if defined (_SC_THREADS) + { "_POSIX_THREADS", SYSCONF, _SC_THREADS }, +#endif +#if defined (_SC_TIMEOUTS) + { "_POSIX_TIMEOUTS", SYSCONF, _SC_TIMEOUTS }, +#endif +#if defined (_SC_TIMERS) + { "_POSIX_TIMERS", SYSCONF, _SC_TIMERS }, +#endif +#if defined (_SC_TRACE) + { "_POSIX_TRACE", SYSCONF, _SC_TRACE }, +#endif +#if defined (_SC_TRACE) + { "_POSIX_TRACE_EVENT_FILTER",SYSCONF, _SC_TRACE_EVENT_FILTER }, +#endif +#if defined (_SC_TRACE) + { "_POSIX_TRACE_INHERIT", SYSCONF, _SC_TRACE_INHERIT }, +#endif +#if defined (_SC_TRACE) + { "_POSIX_TRACE_LOG", SYSCONF, _SC_TRACE_LOG }, +#endif +#if defined (_SC_TYPED_MEMORY_OBJECTS) + { "_POSIX_TYPED_MEMORY_OBJECTS", SYSCONF, _SC_TYPED_MEMORY_OBJECTS }, +#endif +#if defined (_SC_VERSION) + { "_POSIX_VERSION", SYSCONF, _SC_VERSION }, +#endif + + /* XPG 4.2 Configurable System Variables. */ +#if defined (_SC_ATEXIT_MAX) + { "ATEXIT_MAX", SYSCONF, _SC_ATEXIT_MAX }, +#endif +#if defined (_SC_GETGR_R_SIZE_MAX) + { "GETGR_R_SIZE_MAX", SYSCONF, _SC_GETGR_R_SIZE_MAX }, +#endif +#if defined (_SC_GETPW_R_SIZE_MAX) + { "GETPW_R_SIZE_MAX", SYSCONF, _SC_GETPW_R_SIZE_MAX }, +#endif +#if defined (_SC_HOST_NAME_MAX) + { "HOST_NAME_MAX", SYSCONF, _SC_HOST_NAME_MAX }, +#endif +#if defined (_SC_IOV_MAX) + { "IOV_MAX", SYSCONF, _SC_IOV_MAX }, +#endif +#if defined (_SC_LOGIN_NAME_MAX) + { "LOGIN_NAME_MAX", SYSCONF, _SC_LOGIN_NAME_MAX }, +#endif +#if defined (_SC_LOGNAME_MAX) + { "LOGNAME_MAX", SYSCONF, _SC_LOGNAME_MAX }, +#endif +#if defined (_SC_PAGESIZE) + { "PAGESIZE", SYSCONF, _SC_PAGESIZE }, +#endif +#if defined (_SC_PAGE_SIZE) + { "PAGE_SIZE", SYSCONF, _SC_PAGE_SIZE }, +#endif +#if defined (_SC_SYMLOOP_MAX) + { "SYMLOOP_MAX", SYSCONF, _SC_SYMLOOP_MAX }, +#endif +#if defined (_SC_TTY_NAME_MAX) + { "TTY_NAME_MAX", SYSCONF, _SC_TTY_NAME_MAX }, +#endif +#if defined (_SC_USER_GROUPS) + { "_POSIX_USER_GROUPS", SYSCONF, _SC_USER_GROUPS }, +#endif +#if defined (_SC_USER_GROUPS_R) + { "_POSIX_USER_GROUPS_R", SYSCONF, _SC_USER_GROUPS_R }, +#endif + +#if defined (_SC_AIO_LISTIO_MAX) + { "AIO_LISTIO_MAX", SYSCONF, _SC_AIO_LISTIO_MAX }, +#endif +#if defined (_SC_AIO_MAX) + { "AIO_MAX", SYSCONF, _SC_AIO_MAX }, +#endif +#if defined (_SC_AIO_PRIO_DELTA_MAX) + { "AIO_PRIO_DELTA_MAX", SYSCONF, _SC_AIO_PRIO_DELTA_MAX }, +#endif +#if defined (_SC_MQ_OPEN_MAX) + { "MQ_OPEN_MAX", SYSCONF, _SC_MQ_OPEN_MAX }, +#endif +#if defined (_SC_MQ_PRIO_MAX) + { "MQ_PRIO_MAX", SYSCONF, _SC_MQ_PRIO_MAX }, +#endif +#if defined (_SC_RTSIG_MAX) + { "RTSIG_MAX", SYSCONF, _SC_RTSIG_MAX }, +#endif +#if defined (_SC_SEM_NSEMS_MAX) + { "SEM_NSEMS_MAX", SYSCONF, _SC_SEM_NSEMS_MAX }, +#endif +#if defined (_SC_SEM_VALUE_MAX) + { "SEM_VALUE_MAX", SYSCONF, _SC_SEM_VALUE_MAX }, +#endif +#if defined (_SC_SIGQUEUE_MAX) + { "SIGQUEUE_MAX", SYSCONF, _SC_SIGQUEUE_MAX }, +#endif +#if defined (_SC_TIMER_MAX) + { "TIMER_MAX", SYSCONF, _SC_TIMER_MAX }, +#endif + +#if defined (_SC_THREAD_DESTRUCTOR_ITERATIONS) + { "PTHREAD_DESTRUCTOR_ITERATIONS", SYSCONF, _SC_THREAD_DESTRUCTOR_ITERATIONS }, +#endif +#if defined (_SC_THREAD_KEYS_MAX) + { "PTHREAD_KEYS_MAX", SYSCONF, _SC_THREAD_KEYS_MAX }, +#endif +#if defined (_SC_THREAD_STACK_MIN) + { "PTHREAD_STACK_MIN", SYSCONF, _SC_THREAD_STACK_MIN }, +#endif +#if defined (_SC_THREAD_THREADS_MAX) + { "PTHREAD_THREADS_MAX", SYSCONF, _SC_THREAD_THREADS_MAX }, +#endif + + /* XPG 4.2 (and later) Optional Facility Configuration Values */ +#if defined (_SC_XOPEN_CRYPT) + { "_XOPEN_CRYPT", SYSCONF, _SC_XOPEN_CRYPT }, +#endif +#if defined (_SC_XOPEN_ENH_I18N) + { "_XOPEN_ENH_I18N", SYSCONF, _SC_XOPEN_ENH_I18N }, +#endif +#if defined (_SC_XOPEN_LEGACY) + { "_XOPEN_LEGACY", SYSCONF, _SC_XOPEN_LEGACY }, +#endif /* _SC_XOPEN_LEGACY */ +#if defined (_SC_XOPEN_REALTIME) + { "_XOPEN_REALTIME", SYSCONF, _SC_XOPEN_REALTIME }, +#endif +#if defined (_SC_XOPEN_REALTIME_THREADS) + { "_XOPEN_REALTIME_THREADS", SYSCONF, _SC_XOPEN_REALTIME_THREADS }, +#endif +#if defined (_SC_XOPEN_SHM) + { "_XOPEN_SHM", SYSCONF, _SC_XOPEN_SHM }, +#endif +#if defined (_SC_XOPEN_STREAMS) + { "_XOPEN_STREAMS", SYSCONF, _SC_XOPEN_STREAMS }, +#endif +#if defined (_SC_XOPEN_UNIX) + { "_XOPEN_UNIX", SYSCONF, _SC_XOPEN_UNIX }, +#endif +#if defined (_SC_XOPEN_VERSION) + { "_XOPEN_VERSION", SYSCONF, _SC_XOPEN_VERSION }, +#endif +#if defined (_SC_XOPEN_XCU_VERSION) + { "_XOPEN_XCU_VERSION", SYSCONF, _SC_XOPEN_XCU_VERSION }, +#endif +#if defined (_SC_XOPEN_XPG2) + { "_XOPEN_XPG2", SYSCONF, _SC_XOPEN_XPG2 }, +#endif +#if defined (_SC_XOPEN_XPG3) + { "_XOPEN_XPG3", SYSCONF, _SC_XOPEN_XPG3 }, +#endif +#if defined (_SC_XOPEN_XPG4) + { "_XOPEN_XPG4", SYSCONF, _SC_XOPEN_XPG4 }, +#endif +#if defined (_SC_XOPEN_XPG5) + { "_XOPEN_XPG5", SYSCONF, _SC_XOPEN_XPG5 }, +#endif + + /* POSIX.1 Configurable Pathname Values */ +#ifdef _PC_LINK_MAX + { "LINK_MAX", PATHCONF, _PC_LINK_MAX }, +#endif +#ifdef _PC_MAX_CANON + { "MAX_CANON", PATHCONF, _PC_MAX_CANON }, +#endif +#ifdef _PC_MAX_INPUT + { "MAX_INPUT", PATHCONF, _PC_MAX_INPUT }, +#endif +#ifdef _PC_NAME_MAX + { "NAME_MAX", PATHCONF, _PC_NAME_MAX }, +#endif +#ifdef _PC_PATH_MAX + { "PATH_MAX", PATHCONF, _PC_PATH_MAX }, +#endif +#ifdef _PC_PIPE_BUF + { "PIPE_BUF", PATHCONF, _PC_PIPE_BUF }, +#endif +#ifdef _PC_SYMLINK_MAX + { "SYMLINK_MAX", PATHCONF, _PC_SYMLINK_MAX }, +#endif +#ifdef _PC_CHOWN_RESTRICTED + { "_POSIX_CHOWN_RESTRICTED", PATHCONF, _PC_CHOWN_RESTRICTED }, +#endif +#ifdef _PC_NO_TRUNC + { "_POSIX_NO_TRUNC", PATHCONF, _PC_NO_TRUNC }, +#endif +#ifdef _PC_VDISABLE + { "_POSIX_VDISABLE", PATHCONF, _PC_VDISABLE }, +#endif + + /* XPG 4.2 Configurable Pathname Values */ +#if defined (_PC_FILESIZEBITS) + { "FILESIZEBITS", PATHCONF, _PC_FILESIZEBITS }, +#endif +#if defined (_PC_ASYNC_IO) + { "_POSIX_ASYNC_IO", PATHCONF, _PC_ASYNC_IO }, +#endif +#if defined (_PC_PRIO_IO) + { "_POSIX_PRIO_IO", PATHCONF, _PC_PRIO_IO }, +#endif +#if defined (_PC_SYNC_IO) + { "_POSIX_SYNC_IO", PATHCONF, _PC_SYNC_IO }, +#endif + + /* POSIX.1-200x configurable pathname values */ +#if defined (_PC_ALLOC_SIZE_MIN) + { "POSIX_ALLOC_SIZE_MIN", PATHCONF, _PC_ALLOC_SIZE_MIN }, + { "POSIX_REC_INCR_XFER_SIZE", PATHCONF, _PC_REC_INCR_XFER_SIZE }, + { "POSIX_REC_MAX_XFER_SIZE", PATHCONF, _PC_REC_MAX_XFER_SIZE }, + { "POSIX_REC_MIN_XFER_SIZE", PATHCONF, _PC_REC_MIN_XFER_SIZE }, + { "POSIX_REC_XFER_ALIGN", PATHCONF, _PC_REC_XFER_ALIGN }, +#endif + + /* ANSI/ISO C, POSIX.1-200x, XPG 4.2 (and later) C language type limits. */ + { "CHAR_BIT", CONSTANT, CHAR_BIT }, + { "CHAR_MAX", CONSTANT, CHAR_MAX }, + { "CHAR_MIN", CONSTANT, CHAR_MIN }, + { "INT_BIT", CONSTANT, INT_BIT }, + { "INT_MAX", CONSTANT, INT_MAX }, + { "INT_MIN", CONSTANT, INT_MIN }, +#ifdef LLONG_MAX + { "LLONG_MAX", LLCONST, VAL_LLONG_MAX }, + { "LLONG_MIN", LLCONST, VAL_LLONG_MIN }, +#endif + { "LONG_BIT", CONSTANT, LONG_BIT }, + { "LONG_MAX", CONSTANT, LONG_MAX }, + { "LONG_MIN", CONSTANT, LONG_MIN }, +#ifdef MB_LEN_MAX + { "MB_LEN_MAX", CONSTANT, MB_LEN_MAX }, +#endif + { "SCHAR_MAX", CONSTANT, SCHAR_MAX }, + { "SCHAR_MIN", CONSTANT, SCHAR_MIN }, + { "SHRT_MAX", CONSTANT, SHRT_MAX }, + { "SHRT_MIN", CONSTANT, SHRT_MIN }, + { "SIZE_MAX", CONSTANT, SIZE_MAX }, + { "SSIZE_MAX", CONSTANT, SSIZE_MAX }, + { "UCHAR_MAX", CONSTANT, UCHAR_MAX }, + { "UINT_MAX", CONSTANT, UINT_MAX }, +#ifdef ULLONG_MAX + { "ULLONG_MAX", LLCONST, VAL_ULLONG_MAX }, +#endif + { "ULONG_MAX", CONSTANT, ULONG_MAX }, + { "USHRT_MAX", CONSTANT, USHRT_MAX }, + { "WORD_BIT", CONSTANT, WORD_BIT }, + + { NULL } +}; + +static int num_getconf_variables = sizeof(conf_table) / sizeof(struct conf_variable) - 1; + +extern char *this_command_name; +extern char **make_builtin_argv (); + +static void getconf_help (); +static int getconf_print (); +static int getconf_one (); +static int getconf_all (); + +int +getconf_builtin (list) + WORD_LIST *list; +{ + int c, r, opt, aflag; + char **v; + + aflag = 0; + reset_internal_getopt(); + while ((opt = internal_getopt (list, "ahv:")) != -1) { + switch (opt) { + case 'a': + aflag = 1; + break; + case 'h': + getconf_help(); + return(EXECUTION_SUCCESS); + case 'v': + break; /* ignored */ + default: + builtin_usage(); + return(EX_USAGE); + } + } + + list = loptend; + if ((aflag == 0 && list == 0) || (aflag && list) || list_length(list) > 2) { + builtin_usage(); + return(EX_USAGE); + } + + r = aflag ? getconf_all() : getconf_one(list); + + return r; +} + +static void +getconf_help() +{ + const struct conf_variable *cp; + register int i, column; + + builtin_usage(); + printf("Acceptable variable names are:\n"); + for (cp = conf_table; cp->name != NULL; cp++) { + if (cp->type == PATHCONF) + printf("%s pathname\n", cp->name); + else + printf("%s\n", cp->name); + } +} + +static int +getconf_print(cp, vpath, all) +struct conf_variable *cp; +char *vpath; +int all; +{ + long val; + char *sval; + size_t slen; + + switch (cp->type) { + case G_UNDEF: + printf("undefined\n"); + break; + +#ifdef LLONG_MAX + case LLCONST: + switch (cp->value) { + default: + case VAL_LLONG_MIN: + printf ("%lld\n", LLONG_MIN); + break; + case VAL_LLONG_MAX: + printf ("%lld\n", LLONG_MAX); + break; +# if (ULLONG_MAX != LLONG_MAX) + case VAL_ULLONG_MAX: + printf ("%llu\n", ULLONG_MAX); + break; +# endif + } + break; +#endif + case CONSTANT: + switch (cp->value) { + case UCHAR_MAX: + case USHRT_MAX: + case UINT_MAX: +#if (ULONG_MAX != UINT_MAX) + case ULONG_MAX: +#endif +#if (SIZE_MAX != UINT_MAX) && (SIZE_MAX != ULONG_MAX) + case SIZE_MAX: +#endif + + printf("%lu\n", cp->value); + break; + default: + printf("%ld\n", cp->value); + break; + } + break; + + case CONFSTR: + errno = 0; + slen = confstr (cp->value, (char *) 0, (size_t) 0); + if (slen == 0) { + if (errno != 0) { + if (all) + printf ("getconf: %s\n", strerror(errno)); + else + builtin_error ("%s", strerror(errno)); + } else + printf ("undefined\n"); + return (EXECUTION_FAILURE); + } + sval = xmalloc(slen); + + confstr(cp->value, sval, slen); + printf("%s\n", sval); + free(sval); + break; + + case SYSCONF: + errno = 0; + if ((val = sysconf(cp->value)) == -1) { + if (errno != 0) { + if (all) + printf("getconf: %s\n", strerror (errno)); + else + builtin_error ("%s", strerror (errno)); + return (EXECUTION_FAILURE); + } + + printf ("undefined\n"); + } else { + printf("%ld\n", val); + } + break; + + case PATHCONF: + errno = 0; + if ((val = pathconf(vpath, cp->value)) == -1) { + if (errno != 0) { + if (all) + printf("getconf: %s: %s\n", vpath, strerror (errno)); + else + builtin_error ("%s: %s", vpath, strerror (errno)); + return (EXECUTION_FAILURE); + } + + printf ("undefined\n"); + } else { + printf ("%ld\n", val); + } + break; + } + + return (ferror(stdout) ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +static int +getconf_all() +{ + const struct conf_variable *cp; + int ret; + + ret = EXECUTION_SUCCESS; + for (cp = conf_table; cp->name != NULL; cp++) { + printf("%-35s", cp->name); + if (getconf_print(cp, "/", 1) == EXECUTION_FAILURE) + ret = EXECUTION_FAILURE; + } + return ret; +} + +static int +getconf_one(list) + WORD_LIST *list; +{ + const struct conf_variable *cp; + char *vname, *vpath; + + vname = list->word->word; + vpath = (list->next && list->next->word) ? list->next->word->word + : (char *)NULL; + + for (cp = conf_table; cp->name != NULL; cp++) { + if (strcmp(vname, cp->name) == 0) + break; + } + if (cp->name == NULL) { + builtin_error ("%s: unknown variable", vname); + return (EXECUTION_FAILURE); + } + + if (cp->type == PATHCONF) { + if (list->next == 0) { + builtin_usage(); + return(EX_USAGE); + } + } else { + if (list->next) { + builtin_usage(); + return(EX_USAGE); + } + } + + return (getconf_print(cp, vpath, 0)); +} + +static char *getconf_doc[] = { + "getconf writes the current value of a configurable system limit or", + "option variable to the standard output.", + (char *)NULL +}; + +struct builtin getconf_struct = { + "getconf", + getconf_builtin, + BUILTIN_ENABLED, + getconf_doc, + "getconf -[ah] or getconf [-v spec] sysvar or getconf [-v spec] pathvar pathname", + 0 +}; + +#ifndef HAVE_CONFSTR +static size_t +confstr (name, buf, len) + int name; + char *buf; + size_t len; +{ + switch (name) + { + case _CS_PATH: + if (len > 0 && buf) + { + strncpy (buf, STANDARD_UTILS_PATH, len - 1); + buf[len - 1] = '\0'; + } + return (sizeof (STANDARD_UTILS_PATH) + 1); + default: + errno = EINVAL; + return 0; + } +} +#endif + +#ifndef HAVE_SYSCONF +extern long get_clk_tck __P((void)); + +static long +sysconf (name) + int name; +{ +# if defined (_POSIX_VERSION) + switch (name) + { + case _SC_ARG_MAX: + return _POSIX_ARG_MAX; + case _SC_CHILD_MAX: + return _POSIX_CHILD_MAX; + case _SC_CLK_TCK: + return get_clk_tck(); + case _SC_NGROUPS_MAX: + return _POSIX_NGROUPS_MAX; + case _SC_OPEN_MAX: + return _POSIX_OPEN_MAX; + case _SC_JOB_CONTROL: + return _POSIX_JOB_CONTROL; + case _SC_SAVED_IDS: + return _POSIX_SAVED_IDS; + case _SC_VERSION: + return _POSIX_VERSION; + case _SC_BC_BASE_MAX: + return _POSIX2_BC_BASE_MAX; + case _SC_BC_DIM_MAX: + return _POSIX2_BC_DIM_MAX; + case _SC_BC_SCALE_MAX: + return _POSIX2_BC_SCALE_MAX; + case _SC_BC_STRING_MAX: + return _POSIX2_BC_STRING_MAX; + case _SC_COLL_WEIGHTS_MAX: + return -1; + case _SC_EXPR_NEST_MAX: + return _POSIX2_EXPR_NEST_MAX; + case _SC_LINE_MAX: + return _POSIX2_LINE_MAX; + case _SC_RE_DUP_MAX: + return _POSIX2_RE_DUP_MAX; + case _SC_STREAM_MAX: + return _POSIX_STREAM_MAX; + case _SC_TZNAME_MAX: + return _POSIX_TZNAME_MAX; + default: + errno = EINVAL; + return -1; + } +#else + errno = EINVAL; + return -1; +#endif +} +#endif + +#ifndef HAVE_PATHCONF +static long +pathconf (path, name) + const char *path; + int name; +{ +#if defined (_POSIX_VERSION) + switch (name) + { + case _PC_LINK_MAX: + return _POSIX_LINK_MAX; + case _PC_MAX_CANON: + return _POSIX_MAX_CANON; + case _PC_MAX_INPUT: + return _POSIX_MAX_INPUT; + case _PC_NAME_MAX: + return _POSIX_NAME_MAX; + case _PC_PATH_MAX: + return _POSIX_PATH_MAX; + case _PC_PIPE_BUF: + return _POSIX_PIPE_BUF; + case _PC_CHOWN_RESTRICTED: +#ifdef _POSIX_CHOWN_RESTRICTED + return _POSIX_CHOWN_RESTRICTED; +#else + return -1; +#endif + case _PC_NO_TRUNC: +#ifdef _POSIX_NO_TRUNC + return _POSIX_NO_TRUNC; +#else + return -1; +#endif + case _PC_VDISABLE: +#ifdef _POSIX_VDISABLE + return _POSIX_VDISABLE; +#else + return -1; +#endif + default: + errno = EINVAL; + return -1; + } +#else + errno = EINVAL; + return -1; +#endif +} +#endif diff --git a/examples/loadables/getconf.h b/examples/loadables/getconf.h new file mode 100644 index 0000000..c84062d --- /dev/null +++ b/examples/loadables/getconf.h @@ -0,0 +1,214 @@ +/* getconf.h -- replacement definitions for ones the system doesn't provide. */ + +#ifndef _GETCONF_H +#define _GETCONF_H + +/* Some systems do not define these; use POSIX.2 minimum recommended values. */ +#ifndef _POSIX2_COLL_WEIGHTS_MAX +# define _POSIX2_COLL_WEIGHTS_MAX 2 +#endif + +/* If we're on a posix system, but the system doesn't define the necessary + constants, use posix.1 minimum values. */ +#if defined (_POSIX_VERSION) + +#ifndef _POSIX_ARG_MAX +# define _POSIX_ARG_MAX 4096 +#endif +#ifndef _POSIX_CHILD_MAX +# define _POSIX_CHILD_MAX 6 +#endif +#ifndef _POSIX_LINK_MAX +# define _POSIX_LINK_MAX 8 +#endif +#ifndef _POSIX_MAX_CANON +# define _POSIX_MAX_CANON 255 +#endif +#ifndef _POSIX_MAX_INPUT +# define _POSIX_MAX_INPUT 255 +#endif +#ifndef _POSIX_NAME_MAX +# define _POSIX_NAME_MAX 14 +#endif +#ifndef _POSIX_NGROUPS_MAX +# define _POSIX_NGROUPS_MAX 0 +#endif +#ifndef _POSIX_OPEN_MAX +# define _POSIX_OPEN_MAX 16 +#endif +#ifndef _POSIX_PATH_MAX +# define _POSIX_PATH_MAX 255 +#endif +#ifndef _POSIX_PIPE_BUF +# define _POSIX_PIPE_BUF 512 +#endif +#ifndef _POSIX_SSIZE_MAX +# define _POSIX_SSIZE_MAX 32767 +#endif +#ifndef _POSIX_STREAM_MAX +# define _POSIX_STREAM_MAX 8 +#endif +#ifndef _POSIX_TZNAME_MAX +# define _POSIX_TZNAME_MAX 3 +#endif + +#ifndef _POSIX2_BC_BASE_MAX +# define _POSIX2_BC_BASE_MAX 99 +#endif +#ifndef _POSIX2_BC_DIM_MAX +# define _POSIX2_BC_DIM_MAX 2048 +#endif +#ifndef _POSIX2_BC_SCALE_MAX +# define _POSIX2_BC_SCALE_MAX 99 +#endif +#ifndef _POSIX2_BC_STRING_MAX +# define _POSIX2_BC_STRING_MAX 1000 +#endif +#ifndef _POSIX2_EQUIV_CLASS_MAX +# define _POSIX2_EQUIV_CLASS_MAX 2 +#endif +#ifndef _POSIX2_EXPR_NEST_MAX +# define _POSIX2_EXPR_NEST_MAX 32 +#endif +#ifndef _POSIX2_LINE_MAX +# define _POSIX2_LINE_MAX 2048 +#endif +#ifndef _POSIX2_RE_DUP_MAX +# define _POSIX2_RE_DUP_MAX 255 +#endif + +/* configurable system variables */ +#if !defined (HAVE_SYSCONF) + +#ifndef _SC_ARG_MAX +# define _SC_ARG_MAX 1 +# define _SC_CHILD_MAX 2 +# define _SC_CLK_TCK 3 +# define _SC_NGROUPS_MAX 4 +# define _SC_OPEN_MAX 5 +# define _SC_JOB_CONTROL 6 +# define _SC_SAVED_IDS 7 +# define _SC_VERSION 8 +# define _SC_BC_BASE_MAX 9 +# define _SC_BC_DIM_MAX 10 +# define _SC_BC_SCALE_MAX 11 +# define _SC_BC_STRING_MAX 12 +# define _SC_COLL_WEIGHTS_MAX 13 +# define _SC_EXPR_NEST_MAX 14 +# define _SC_LINE_MAX 15 +# define _SC_RE_DUP_MAX 16 +#if 0 +# define _SC_2_VERSION 17 +# define _SC_2_C_BIND 18 +# define _SC_2_C_DEV 19 +# define _SC_2_CHAR_TERM 20 +# define _SC_2_FORT_DEV 21 +# define _SC_2_FORT_RUN 22 +# define _SC_2_LOCALEDEF 23 +# define _SC_2_SW_DEV 24 +# define _SC_2_UPE 25 +#endif /* 0 */ + +# define _SC_STREAM_MAX 26 +# define _SC_TZNAME_MAX 27 +#endif /* !_SC_ARG_MAX */ + +#endif /* !HAVE_SYSCONF */ + +/* configurable pathname variables */ +#if !defined (HAVE_PATHCONF) + +#ifndef _PC_LINK_MAX +#define _PC_LINK_MAX 1 +#define _PC_MAX_CANON 2 +#define _PC_MAX_INPUT 3 +#define _PC_NAME_MAX 4 +#define _PC_PATH_MAX 5 +#define _PC_PIPE_BUF 6 +#define _PC_CHOWN_RESTRICTED 7 +#define _PC_NO_TRUNC 8 +#define _PC_VDISABLE 9 +#endif /* !_PC_LINK_MAX */ + +#endif /* !HAVE_PATHCONF */ + +#endif /* _POSIX_VERSION */ + +#ifndef _CS_PATH +# define _CS_PATH 1 +#endif + +/* ANSI/ISO C, POSIX.1-200x, XPG 4.2 (and later) C language type limits. + Defined only if the system include files don't. Assume a 32-bit + environment with signed 8-bit characters. */ + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif +#ifndef CHAR_MAX +# define CHAR_MAX 127 +#endif +#ifndef CHAR_MIN +# define CHAR_MIN -128 +#endif + +#ifndef INT_BIT +# define INT_BIT (sizeof (int) * CHAR_BIT) +#endif +#ifndef INT_MAX +# define INT_MAX 2147483647 +#endif +#ifndef INT_MIN +# define INT_MIN (-2147483647-1) +#endif + +#ifndef LONG_BIT +# define LONG_BIT (sizeof (long int) * CHAR_BIT) +#endif +#ifndef LONG_MAX +# define LONG_MAX 2147483647L +#endif +#ifndef LONG_MIN +# define LONG_MIN (-2147483647L-1L) +#endif + +#ifndef SCHAR_MAX +# define SCHAR_MAX CHAR_MAX +#endif +#ifndef SCHAR_MIN +# define SCHAR_MIN CHAR_MIN +#endif + +#ifndef SHRT_MAX +# define SHRT_MAX 32767 +#endif +#ifndef SHRT_MIN +# define SHRT_MIN (-32768) +#endif + +#ifndef UCHAR_MAX +# define UCHAR_MAX 255 +#endif +#ifndef UINT_MAX +# define UINT_MAX 4294967295U +#endif +#ifndef ULONG_MAX +# define ULONG_MAX 4294967295UL +#endif +#ifndef USHRT_MAX +# define UCHAR_MAX 65535 +#endif + +/* assume size_t is `unsigned int'; ssize_t is `int' */ +#ifndef SIZE_MAX +# define SIZE_MAX UINT_MAX +#endif +#ifndef SSIZE_MAX +# define SSIZE_MAX INT_MAX +#endif + +#ifndef WORD_BIT +# define WORD_BIT (sizeof (int) * CHAR_BIT) +#endif + +#endif /* _GETCONF_H */ diff --git a/examples/loadables/head.c b/examples/loadables/head.c new file mode 100644 index 0000000..9052689 --- /dev/null +++ b/examples/loadables/head.c @@ -0,0 +1,143 @@ +/* head - copy first part of files. */ + +/* See Makefile for compilation details. */ + +#include "config.h" + +#include "bashtypes.h" +#include "posixstat.h" +#include "filecntl.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" + +#include <stdio.h> +#include <errno.h> +#include "chartypes.h" + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +static void +munge_list (list) + WORD_LIST *list; +{ + WORD_LIST *l, *nl; + WORD_DESC *wd; + char *arg; + + for (l = list; l; l = l->next) + { + arg = l->word->word; + if (arg[0] != '-' || arg[1] == '-' || (DIGIT(arg[1]) == 0)) + return; + /* We have -[0-9]* */ + wd = make_bare_word (arg+1); + nl = make_word_list (wd, l->next); + l->word->word[1] = 'n'; + l->word->word[2] = '\0'; + l->next = nl; + l = nl; /* skip over new argument */ + } +} + +static int +file_head (fp, cnt) + FILE *fp; + int cnt; +{ + int ch; + + while (cnt--) + { + while ((ch = getc (fp)) != EOF) + { + if (putchar (ch) == EOF) + { + builtin_error ("write error: %s", strerror (errno)); + return EXECUTION_FAILURE; + } + if (ch == '\n') + break; + } + } +} + +head_builtin (list) + WORD_LIST *list; +{ + int nline, opt, rval; + WORD_LIST *l; + FILE *fp; + + char *t; + + munge_list (list); /* change -num into -n num */ + + reset_internal_getopt (); + nline = 10; + while ((opt = internal_getopt (list, "n:")) != -1) + { + switch (opt) + { + case 'n': + nline = atoi (list_optarg); + if (nline <= 0) + { + builtin_error ("bad line count: %s", list_optarg); + return (EX_USAGE); + } + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + return (file_head (stdin, nline)); + + for (rval = EXECUTION_SUCCESS, opt = 1, l = list; l; l = l->next) + { + fp = fopen (l->word->word, "r"); + if (fp == NULL) + { + builtin_error ("%s: %s", l->word->word, strerror (errno)); + continue; + } + if (list->next) /* more than one file */ + { + printf ("%s==> %s <==\n", opt ? "" : "\n", l->word->word); + opt = 0; + } + rval = file_head (fp, nline); + fclose (fp); + } + + return (rval); +} + +char *head_doc[] = { + "Copy the first N lines from the input files to the standard output.", + "N is supplied as an argument to the `-n' option. If N is not given,", + "the first ten lines are copied.", + (char *)NULL +}; + +struct builtin head_struct = { + "head", /* builtin name */ + head_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + head_doc, /* array of long documentation strings. */ + "head [-n num] [file ...]", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/hello.c b/examples/loadables/hello.c new file mode 100644 index 0000000..1d68c6f --- /dev/null +++ b/examples/loadables/hello.c @@ -0,0 +1,61 @@ +/* Sample builtin to be dynamically loaded with enable -f and create a new + builtin. */ + +/* See Makefile for compilation details. */ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +/* A builtin `xxx' is normally implemented with an `xxx_builtin' function. + If you're converting a command that uses the normal Unix argc/argv + calling convention, use argv = make_builtin_argv (list, &argc) and call + the original `main' something like `xxx_main'. Look at cat.c for an + example. + + Builtins should use internal_getopt to parse options. It is the same as + getopt(3), but it takes a WORD_LIST *. Look at print.c for an example + of its use. + + If the builtin takes no options, call no_options(list) before doing + anything else. If it returns a non-zero value, your builtin should + immediately return EX_USAGE. Look at logname.c for an example. + + A builtin command returns EXECUTION_SUCCESS for success and + EXECUTION_FAILURE to indicate failure. */ +int +hello_builtin (list) + WORD_LIST *list; +{ + printf("hello world\n"); + fflush (stdout); + return (EXECUTION_SUCCESS); +} + +/* An array of strings forming the `long' documentation for a builtin xxx, + which is printed by `help xxx'. It must end with a NULL. */ +char *hello_doc[] = { + "this is the long doc for the sample hello builtin", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. The flags must include BUILTIN_ENABLED so the + builtin can be used. */ +struct builtin hello_struct = { + "hello", /* builtin name */ + hello_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + hello_doc, /* array of long documentation strings. */ + "hello", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; + diff --git a/examples/loadables/id.c b/examples/loadables/id.c new file mode 100644 index 0000000..945190d --- /dev/null +++ b/examples/loadables/id.c @@ -0,0 +1,308 @@ +/* + * id - POSIX.2 user identity + * + * (INCOMPLETE -- supplementary groups for other users not yet done) + * + * usage: id [-Ggu] [-nr] [user] + * + * The default output format looks something like: + * uid=xxx(chet) gid=xx groups=aa(aname), bb(bname), cc(cname) + */ + +#include <config.h> +#include <stdio.h> +#include "bashtypes.h" +#include <pwd.h> +#include <grp.h> +#include "bashansi.h" + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#else +# include <sys/param.h> +#endif + +#if !defined (HAVE_GETPW_DECLS) +extern struct passwd *getpwuid (); +#endif +extern struct group *getgrgid (); + +#include "shell.h" +#include "builtins.h" +#include "stdc.h" +#include "common.h" +#include "bashgetopt.h" + +#define ID_ALLGROUPS 0x001 /* -G */ +#define ID_GIDONLY 0x002 /* -g */ +#define ID_USENAME 0x004 /* -n */ +#define ID_USEREAL 0x008 /* -r */ +#define ID_USERONLY 0x010 /* -u */ + +#define ID_FLAGSET(s) ((id_flags & (s)) != 0) + +static int id_flags; + +static uid_t ruid, euid; +static gid_t rgid, egid; + +static char *id_user; + +static int inituser (); + +static int id_pruser (); +static int id_prgrp (); +static int id_prgroups (); +static int id_prall (); + +int +id_builtin (list) + WORD_LIST *list; +{ + int opt; + char *user; + + id_flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "Ggnru")) != -1) + { + switch (opt) + { + case 'G': id_flags |= ID_ALLGROUPS; break; + case 'g': id_flags |= ID_GIDONLY; break; + case 'n': id_flags |= ID_USENAME; break; + case 'r': id_flags |= ID_USEREAL; break; + case 'u': id_flags |= ID_USERONLY; break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + user = list ? list->word->word : (char *)NULL; + + /* Check for some invalid option combinations */ + opt = ID_FLAGSET (ID_ALLGROUPS) + ID_FLAGSET (ID_GIDONLY) + ID_FLAGSET (ID_USERONLY); + if (opt > 1 || (opt == 0 && ((id_flags & (ID_USEREAL|ID_USENAME)) != 0))) + { + builtin_usage (); + return (EX_USAGE); + } + + if (list && list->next) + { + builtin_usage (); + return (EX_USAGE); + } + + if (inituser (user) < 0) + return (EXECUTION_FAILURE); + + opt = 0; + if (id_flags & ID_USERONLY) + opt += id_pruser ((id_flags & ID_USEREAL) ? ruid : euid); + else if (id_flags & ID_GIDONLY) + opt += id_prgrp ((id_flags & ID_USEREAL) ? rgid : egid); + else if (id_flags & ID_ALLGROUPS) + opt += id_prgroups (user); + else + opt += id_prall (user); + putchar ('\n'); + fflush (stdout); + + return (opt == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} + +static int +inituser (uname) + char *uname; +{ + struct passwd *pwd; + + if (uname) + { + pwd = getpwnam (uname); + if (pwd == 0) + { + builtin_error ("%s: no such user", uname); + return -1; + } + ruid = euid = pwd->pw_uid; + rgid = egid = pwd->pw_gid; + } + else + { + ruid = current_user.uid; + euid = current_user.euid; + rgid = current_user.gid; + egid = current_user.egid; + } + return 0; +} + +/* Print the name or value of user ID UID. */ +static int +id_pruser (uid) + int uid; +{ + struct passwd *pwd = NULL; + int r; + + r = 0; + if (id_flags & ID_USENAME) + { + pwd = getpwuid (uid); + if (pwd == NULL) + r = 1; + } + if (pwd) + printf ("%s", pwd->pw_name); + else + printf ("%u", (unsigned) uid); + + return r; +} + +/* Print the name or value of group ID GID. */ + +static int +id_prgrp (gid) + int gid; +{ + struct group *grp = NULL; + int r; + + r = 0; + if (id_flags & ID_USENAME) + { + grp = getgrgid (gid); + if (grp == NULL) + r = 1; + } + + if (grp) + printf ("%s", grp->gr_name); + else + printf ("%u", (unsigned) gid); + + return r; +} + +static int +id_prgroups (uname) + char *uname; +{ + int *glist, ng, i, r; + + r = 0; + id_prgrp (rgid); + if (egid != rgid) + { + putchar (' '); + id_prgrp (egid); + } + + if (uname) + { + builtin_error ("supplementary groups for other users not yet implemented"); + glist = (int *)NULL; + ng = 0; + r = 1; + } + else + glist = get_group_array (&ng); + + for (i = 0; i < ng; i++) + if (glist[i] != rgid && glist[i] != egid) + { + putchar (' '); + id_prgrp (glist[i]); + } + + return r; +} + +static int +id_prall (uname) + char *uname; +{ + int r, i, ng, *glist; + struct passwd *pwd; + struct group *grp; + + r = 0; + printf ("uid=%u", (unsigned) ruid); + pwd = getpwuid (ruid); + if (pwd == NULL) + r = 1; + else + printf ("(%s)", pwd->pw_name); + + printf (" gid=%u", (unsigned) rgid); + grp = getgrgid (rgid); + if (grp == NULL) + r = 1; + else + printf ("(%s)", grp->gr_name); + + if (euid != ruid) + { + printf (" euid=%u", (unsigned) euid); + pwd = getpwuid (euid); + if (pwd == NULL) + r = 1; + else + printf ("(%s)", pwd->pw_name); + } + + if (egid != rgid) + { + printf (" egid=%u", (unsigned) egid); + grp = getgrgid (egid); + if (grp == NULL) + r = 1; + else + printf ("(%s)", grp->gr_name); + } + + if (uname) + { + builtin_error ("supplementary groups for other users not yet implemented"); + glist = (int *)NULL; + ng = 0; + r = 1; + } + else + glist = get_group_array (&ng); + + if (ng > 0) + printf (" groups="); + for (i = 0; i < ng; i++) + { + if (i > 0) + printf (", "); + printf ("%u", (unsigned) glist[i]); + grp = getgrgid (glist[i]); + if (grp == NULL) + r = 1; + else + printf ("(%s)", grp->gr_name); + } + + return r; +} + +char *id_doc[] = { + "return information about user identity", + (char *)NULL +}; + +struct builtin id_struct = { + "id", + id_builtin, + BUILTIN_ENABLED, + id_doc, + "id [user]\n\tid -G [-n] [user]\n\tid -g [-nr] [user]\n\tid -u [-nr] [user]", + 0 +}; diff --git a/examples/loadables/ln.c b/examples/loadables/ln.c new file mode 100644 index 0000000..e3234e3 --- /dev/null +++ b/examples/loadables/ln.c @@ -0,0 +1,205 @@ +/* ln - make links */ + +/* See Makefile for compilation details. */ + +#include "config.h" + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "posixstat.h" + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +typedef int unix_link_syscall_t __P((const char *, const char *)); + +#define LN_SYMLINK 0x01 +#define LN_UNLINK 0x02 + +static unix_link_syscall_t *linkfn; +static int dolink (); + +ln_builtin (list) + WORD_LIST *list; +{ + int rval, opt, flags; + WORD_LIST *l; + char *sdir; + struct stat sb; + + flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "fs")) != -1) + { + switch (opt) + { + case 'f': + flags |= LN_UNLINK; + break; + case 's': + flags |= LN_SYMLINK; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + linkfn = (flags & LN_SYMLINK) ? symlink : link; + + if (list->next == 0) /* ln target, equivalent to ln target . */ + return (dolink (list->word->word, ".", flags)); + + if (list->next->next == 0) /* ln target source */ + return (dolink (list->word->word, list->next->word->word, flags)); + + /* ln target1 target2 ... directory */ + + /* find last argument: target directory, and make sure it's an existing + directory. */ + for (l = list; l->next; l = l->next) + ; + sdir = l->word->word; + + if (stat(sdir, &sb) < 0) + { + builtin_error ("%s", sdir); + return (EXECUTION_FAILURE); + } + + if (S_ISDIR (sb.st_mode) == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + for (rval = EXECUTION_SUCCESS; list != l; list = list->next) + rval += dolink (list->word->word, sdir, flags); + + return rval; +} + +static char * +mkdirpath (dir, file) + char *dir, *file; +{ + int dlen, flen; + char *ret; + + dlen = strlen (dir); + flen = strlen (file); + + ret = xmalloc (2 + dlen + flen); + + strcpy (ret, dir); + if (ret[dlen - 1] != '/') + ret[dlen++] = '/'; + strcpy (ret + dlen, file); + return ret; +} + +#if defined (HAVE_LSTAT) +# define LSTAT lstat +#else +# define LSTAT stat +#endif + +static int +dolink (src, dst, flags) + char *src, *dst; + int flags; +{ + struct stat ssb, dsb; + int exists; + char *dst_path, *p; + + /* If we're not doing symlinks, the source must exist and not be a + directory. */ + if ((flags & LN_SYMLINK) == 0) + { + if (stat (src, &ssb) != 0) + { + builtin_error ("%s: %s", src, strerror (errno)); + return (EXECUTION_FAILURE); + } + if (S_ISDIR (ssb.st_mode)) + { + errno = EISDIR; + builtin_error ("%s: %s", src, strerror (errno)); + return (EXECUTION_FAILURE); + } + } + + /* If the destination is a directory, create the final filename by appending + the basename of the source to the destination. */ + dst_path = 0; + if ((stat (dst, &dsb) == 0) && S_ISDIR (dsb.st_mode)) + { + if ((p = strrchr (src, '/')) == 0) + p = src; + else + p++; + + dst_path = mkdirpath (dst, p); + dst = dst_path; + } + + exists = LSTAT (dst, &dsb) == 0; + + /* If -f was specified, and the destination exists, unlink it. */ + if ((flags & LN_UNLINK) && exists && unlink (dst) != 0) + { + builtin_error ("%s: cannot unlink: %s", dst, strerror (errno)); + FREE (dst_path); + return (EXECUTION_FAILURE); + } + + /* Perform the link. */ + if ((*linkfn) (src, dst) != 0) + { + builtin_error ("cannot link %s to %s: %s", dst, src, strerror (errno)); + FREE (dst_path); + return (EXECUTION_FAILURE); + } + + FREE (dst_path); + return (EXECUTION_SUCCESS); +} + +char *ln_doc[] = { + "Create a new directory entry with the same modes as the original", + "file. The -f option means to unlink any existing file, permitting", + "the link to occur. The -s option means to create a symbolic link.", + "By default, ln makes hard links.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin ln_struct = { + "ln", /* builtin name */ + ln_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + ln_doc, /* array of long documentation strings. */ + "ln [-fs] file1 [file2] OR ln [-fs] file ... directory", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/logname.c b/examples/loadables/logname.c new file mode 100644 index 0000000..00cfd19 --- /dev/null +++ b/examples/loadables/logname.c @@ -0,0 +1,52 @@ +/* logname - print login name of current user */ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" + +#if !defined (errno) +extern int errno; +#endif + +logname_builtin (list) + WORD_LIST *list; +{ + char *np; + + if (no_options (list)) + return (EX_USAGE); + + np = getlogin (); + if (np == 0) + { + builtin_error ("cannot find username: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } + printf ("%s\n", np); + return (EXECUTION_SUCCESS); +} + +char *logname_doc[] = { + "write the current user's login name to the standard output", + "and exit. logname ignores the LOGNAME and USER variables.", + "logname ignores any non-option arguments.", + (char *)NULL +}; + +struct builtin logname_struct = { + "logname", + logname_builtin, + BUILTIN_ENABLED, + logname_doc, + "logname", + 0 +}; + diff --git a/examples/loadables/mkdir.c b/examples/loadables/mkdir.c new file mode 100644 index 0000000..cd6e5f9 --- /dev/null +++ b/examples/loadables/mkdir.c @@ -0,0 +1,216 @@ +/* mkdir - make directories */ + +/* See Makefile for compilation details. */ + +#include <config.h> + +#include "bashtypes.h" +#include "posixstat.h" +#include <errno.h> +#include <stdio.h> +#include "bashansi.h" +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +#define ISOCTAL(c) ((c) >= '0' && (c) <= '7') + +extern int parse_symbolic_mode (); + +static int make_path (); + +static int original_umask; + +int +mkdir_builtin (list) + WORD_LIST *list; +{ + int opt, pflag, omode, rval, octal, nmode, parent_mode, um; + char *mode; + WORD_LIST *l; + + reset_internal_getopt (); + pflag = 0; + mode = (char *)NULL; + while ((opt = internal_getopt(list, "m:p")) != -1) + switch (opt) + { + case 'p': + pflag = 1; + break; + case 'm': + mode = list_optarg; + break; + default: + builtin_usage(); + return (EX_USAGE); + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (mode == NULL) + omode = S_IRWXU | S_IRWXG | S_IRWXO; /* a=rwx */ + else if (ISOCTAL (*mode)) /* octal number */ + { + omode = read_octal (mode); + if (omode < 0) + { + builtin_error ("invalid file mode: %s", mode); + return (EXECUTION_FAILURE); + } + octal = 1; + } + else if (mode) + { + /* initial bits are a=rwx; the mode argument modifies them */ + omode = parse_symbolic_mode (mode, S_IRWXU | S_IRWXG | S_IRWXO); + if (omode < 0) + { + builtin_error ("invalid file mode: %s", mode); + return (EXECUTION_FAILURE); + } + octal = 0; + } + + /* Make the new mode */ + original_umask = umask (0); + umask (original_umask); + + nmode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~original_umask; + parent_mode = nmode | (S_IWRITE|S_IEXEC); /* u+wx */ + + /* Adjust new mode based on mode argument */ + nmode &= omode; + + for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next) + { + if (pflag && make_path (l->word->word, nmode, parent_mode)) + { + rval = EXECUTION_FAILURE; + continue; + } + else if (pflag == 0 && mkdir (l->word->word, nmode) < 0) + { + builtin_error ("cannot create directory `%s': %s", l->word->word, strerror (errno)); + rval = EXECUTION_FAILURE; + } + } + return rval; +} + +/* Make all the directories leading up to PATH, then create PATH. Note that + this changes the process's umask; make sure that all paths leading to a + return reset it to ORIGINAL_UMASK */ +static int +make_path (path, nmode, parent_mode) + char *path; + int nmode, parent_mode; +{ + int oumask; + struct stat sb; + char *p, *npath; + + if (stat (path, &sb) == 0) + { + if (S_ISDIR (sb.st_mode) == 0) + { + builtin_error ("`%s': file exists but is not a directory", path); + return 1; + } + + if (chmod (path, nmode)) + { + builtin_error ("%s: %s", path, strerror (errno)); + return 1; + } + + return 0; + } + + oumask = umask (0); + npath = savestring (path); /* So we can write to it. */ + + /* Check whether or not we need to do anything with intermediate dirs. */ + + /* Skip leading slashes. */ + p = npath; + while (*p == '/') + p++; + + while (p = strchr (p, '/')) + { + *p = '\0'; + if (stat (npath, &sb) != 0) + { + if (mkdir (npath, parent_mode)) + { + builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); + umask (original_umask); + free (npath); + return 1; + } + } + else if (S_ISDIR (sb.st_mode) == 0) + { + builtin_error ("`%s': file exists but is not a directory", npath); + umask (original_umask); + free (npath); + return 1; + } + + *p++ = '/'; /* restore slash */ + while (*p == '/') + p++; + } + + /* Create the final directory component. */ + if (stat (npath, &sb) && mkdir (npath, nmode)) + { + builtin_error ("cannot create directory `%s': %s", npath, strerror (errno)); + umask (original_umask); + free (npath); + return 1; + } + + umask (original_umask); + free (npath); + return 0; +} + +char *mkdir_doc[] = { + "Make directories. Create the directories named as arguments, in", + "the order specified, using mode rwxrwxrwx as modified by the current", + "umask (see `help umask'). The -m option causes the file permission", + "bits of the final directory to be MODE. The MODE argument may be", + "an octal number or a symbolic mode like that used by chmod(1). If", + "a symbolic mode is used, the operations are interpreted relative to", + "an initial mode of \"a=rwx\". The -p option causes any required", + "intermediate directories in PATH to be created. The directories", + "are created with permssion bits of rwxrwxrwx as modified by the current", + "umask, plus write and search permissions for the owner. mkdir", + "returns 0 if the directories are created successfully, and non-zero", + "if an error occurs.", + (char *)NULL +}; + +struct builtin mkdir_struct = { + "mkdir", + mkdir_builtin, + BUILTIN_ENABLED, + mkdir_doc, + "mkdir [-p] [-m mode] directory [directory ...]", + 0 +}; diff --git a/examples/loadables/necho.c b/examples/loadables/necho.c new file mode 100644 index 0000000..521ee2c --- /dev/null +++ b/examples/loadables/necho.c @@ -0,0 +1,33 @@ +/* necho - echo without options or argument interpretation */ + +/* Sample builtin to be dynamically loaded with enable -f and replace an + existing builtin. */ + +#include <stdio.h> +#include "builtins.h" +#include "shell.h" + +necho_builtin (list) +WORD_LIST *list; +{ + print_word_list (list, " "); + printf("\n"); + fflush (stdout); + return (EXECUTION_SUCCESS); +} + +char *necho_doc[] = { + "Print the arguments to the standard ouput separated", + "by space characters and terminated with a newline.", + (char *)NULL +}; + +struct builtin necho_struct = { + "echo", + necho_builtin, + BUILTIN_ENABLED, + necho_doc, + "echo [args]", + 0 +}; + diff --git a/examples/loadables/pathchk.c b/examples/loadables/pathchk.c new file mode 100644 index 0000000..2e36f8f --- /dev/null +++ b/examples/loadables/pathchk.c @@ -0,0 +1,358 @@ +/* pathchk - check pathnames for validity and portability */ + +/* Usage: pathchk [-p] path ... + + For each PATH, print a message if any of these conditions are false: + * all existing leading directories in PATH have search (execute) permission + * strlen (PATH) <= PATH_MAX + * strlen (each_directory_in_PATH) <= NAME_MAX + + Exit status: + 0 All PATH names passed all of the tests. + 1 An error occurred. + + Options: + -p Instead of performing length checks on the + underlying filesystem, test the length of the + pathname and its components against the POSIX.1 + minimum limits for portability, _POSIX_NAME_MAX + and _POSIX_PATH_MAX in 2.9.2. Also check that + the pathname contains no character not in the + portable filename character set. */ + +/* See Makefile for compilation details. */ + +#include <config.h> + +#include <sys/types.h> +#include "posixstat.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#if defined (HAVE_LIMITS_H) +# include <limits.h> +#endif + +#include "bashansi.h" + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "stdc.h" +#include "bashgetopt.h" +#include "maxpath.h" + +#if !defined (errno) +extern int errno; +#endif + +#if !defined (_POSIX_PATH_MAX) +# define _POSIX_PATH_MAX 255 +#endif +#if !defined (_POSIX_NAME_MAX) +# define _POSIX_NAME_MAX 14 +#endif + +/* How do we get PATH_MAX? */ +#if defined (_POSIX_VERSION) && !defined (PATH_MAX) +# define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX) +#endif + +/* How do we get NAME_MAX? */ +#if defined (_POSIX_VERSION) && !defined (NAME_MAX) +# define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX) +#endif + +#if !defined (PATH_MAX_FOR) +# define PATH_MAX_FOR(p) PATH_MAX +#endif + +#if !defined (NAME_MAX_FOR) +# define NAME_MAX_FOR(p) NAME_MAX +#endif + +extern char *strerror (); + +static int validate_path (); + +pathchk_builtin (list) + WORD_LIST *list; +{ + int retval, pflag, opt; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "p")) != -1) + { + switch (opt) + { + case 'p': + pflag = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + for (retval = 0; list; list = list->next) + retval |= validate_path (list->word->word, pflag); + + return (retval ? EXECUTION_FAILURE : EXECUTION_SUCCESS); +} + +char *pathchk_doc[] = { + "Check each pathname argument for validity (i.e., it may be used to", + "create or access a file without casuing syntax errors) and portability", + "(i.e., no filename truncation will result). If the `-p' option is", + "supplied, more extensive portability checks are performed.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin pathchk_struct = { + "pathchk", /* builtin name */ + pathchk_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + pathchk_doc, /* array of long documentation strings. */ + "pathchk [-p] pathname ...", /* usage synopsis */ + 0 /* reserved for internal use */ +}; + +/* The remainder of this file is stolen shamelessly from `pathchk.c' in + the sh-utils-1.12 distribution, by + + David MacKenzie <djm@gnu.ai.mit.edu> + and Jim Meyering <meyering@cs.utexas.edu> */ + +/* Each element is nonzero if the corresponding ASCII character is + in the POSIX portable character set, and zero if it is not. + In addition, the entry for `/' is nonzero to simplify checking. */ +static char const portable_chars[256] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* If PATH contains only portable characters, return 1, else 0. */ + +static int +portable_chars_only (path) + const char *path; +{ + const char *p; + + for (p = path; *p; ++p) + if (portable_chars[(const unsigned char) *p] == 0) + { + builtin_error ("path `%s' contains nonportable character `%c'", path, *p); + return 0; + } + return 1; +} + +/* On some systems, stat can return EINTR. */ + +#ifndef EINTR +# define SAFE_STAT(name, buf) stat (name, buf) +#else +# define SAFE_STAT(name, buf) safe_stat (name, buf) +static inline int +safe_stat (name, buf) + const char *name; + struct stat *buf; +{ + int ret; + + do + ret = stat (name, buf); + while (ret < 0 && errno == EINTR); + + return ret; +} +#endif + +/* Return 1 if PATH is a usable leading directory, 0 if not, + 2 if it doesn't exist. */ + +static int +dir_ok (path) + const char *path; +{ + struct stat stats; + + if (SAFE_STAT (path, &stats)) + return 2; + + if (!S_ISDIR (stats.st_mode)) + { + builtin_error ("`%s' is not a directory", path); + return 0; + } + + /* Use access to test for search permission because + testing permission bits of st_mode can lose with new + access control mechanisms. Of course, access loses if you're + running setuid. */ + if (access (path, X_OK) != 0) + { + if (errno == EACCES) + builtin_error ("directory `%s' is not searchable", path); + else + builtin_error ("%s: %s", path, strerror (errno)); + return 0; + } + + return 1; +} + +static char * +xstrdup (s) + char *s; +{ + return (savestring (s)); +} + +/* Make sure that + strlen (PATH) <= PATH_MAX + && strlen (each-existing-directory-in-PATH) <= NAME_MAX + + If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and + _POSIX_NAME_MAX instead, and make sure that PATH contains no + characters not in the POSIX portable filename character set, which + consists of A-Z, a-z, 0-9, ., _, -. + + Make sure that all leading directories along PATH that exist have + `x' permission. + + Return 0 if all of these tests are successful, 1 if any fail. */ + +static int +validate_path (path, portability) + char *path; + int portability; +{ + int path_max; + int last_elem; /* Nonzero if checking last element of path. */ + int exists; /* 2 if the path element exists. */ + char *slash; + char *parent; /* Last existing leading directory so far. */ + + if (portability && !portable_chars_only (path)) + return 1; + + if (*path == '\0') + return 0; + +#ifdef lint + /* Suppress `used before initialized' warning. */ + exists = 0; +#endif + + /* Figure out the parent of the first element in PATH. */ + parent = xstrdup (*path == '/' ? "/" : "."); + + slash = path; + last_elem = 0; + while (1) + { + int name_max; + int length; /* Length of partial path being checked. */ + char *start; /* Start of path element being checked. */ + + /* Find the end of this element of the path. + Then chop off the rest of the path after this element. */ + while (*slash == '/') + slash++; + start = slash; + slash = strchr (slash, '/'); + if (slash != NULL) + *slash = '\0'; + else + { + last_elem = 1; + slash = strchr (start, '\0'); + } + + if (!last_elem) + { + exists = dir_ok (path); + if (dir_ok == 0) + { + free (parent); + return 1; + } + } + + length = slash - start; + /* Since we know that `parent' is a directory, it's ok to call + pathconf with it as the argument. (If `parent' isn't a directory + or doesn't exist, the behavior of pathconf is undefined.) + But if `parent' is a directory and is on a remote file system, + it's likely that pathconf can't give us a reasonable value + and will return -1. (NFS and tempfs are not POSIX . . .) + In that case, we have no choice but to assume the pessimal + POSIX minimums. */ + name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent); + if (name_max < 0) + name_max = _POSIX_NAME_MAX; + if (length > name_max) + { + builtin_error ("name `%s' has length %d; exceeds limit of %d", + start, length, name_max); + free (parent); + return 1; + } + + if (last_elem) + break; + + if (exists == 1) + { + free (parent); + parent = xstrdup (path); + } + + *slash++ = '/'; + } + + /* `parent' is now the last existing leading directory in the whole path, + so it's ok to call pathconf with it as the argument. */ + path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent); + if (path_max < 0) + path_max = _POSIX_PATH_MAX; + free (parent); + if (strlen (path) > path_max) + { + builtin_error ("path `%s' has length %d; exceeds limit of %d", + path, strlen (path), path_max); + return 1; + } + + return 0; +} diff --git a/examples/loadables/perl/Makefile.in b/examples/loadables/perl/Makefile.in new file mode 100644 index 0000000..3af9b85 --- /dev/null +++ b/examples/loadables/perl/Makefile.in @@ -0,0 +1,97 @@ +# +# Makefile for builtin perl interpreter +# +# +# Copyright (C) 1998 Free Software Foundation, Inc. + +# 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 2, 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + +# Include some boilerplate Gnu makefile definitions. +prefix = @prefix@ + +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +infodir = @infodir@ +includedir = @includedir@ + +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ +srcdir = @srcdir@ +VPATH = .:@srcdir@ + +@SET_MAKE@ +CC = @CC@ +RM = rm -f + +SHELL = @MAKE_SHELL@ + +PERL5 = perl5 + +CFLAGS = @CFLAGS@ + +# +# These values are generated for configure by ${topdir}/support/shobj-conf. +# If your system is not supported by that script, but includes facilities for +# dynamic loading of shared objects, please update the script and send the +# changes to bash-maintainers@gnu.org. +# +SHOBJ_CC = @SHOBJ_CC@ +SHOBJ_CFLAGS = @SHOBJ_CFLAGS@ +SHOBJ_LD = @SHOBJ_LD@ +SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@ +SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@ +SHOBJ_LIBS = @SHOBJ_LIBS@ +SHOBJ_STATUS = @SHOBJ_STATUS@ + +# Values used for compiling the perl files +PERL_LDOPTS = `${PERL5} -MExtUtils::Embed -e ldopts` +PERL_CFLAGS = ${CCFLAGS} `${PERL5} -MExtUtils::Embed -e ccopts` + +SRC = bperl.c iperl.c perlxsi.c +OBJ = bperl.o iperl.o perlxsi.o + +BUILTIN = bperl5 + +INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins \ + -I$(topdir)/include -I$(BUILD_DIR) -I$(BUILD_DIR)/lib \ + -I$(BUILD_DIR)/builtins + + +${BUILTIN}: ${OBJ} + ${RM} $@ + ${SHOBJ_LD} ${SHOBJ_LDFLAGS} ${SHOBJ_XLDFLAGS} -o $@ ${OBJ} ${PERL_LDOPTS} ${SHOBJ_LIBS} + +bperl.o: bperl.c + ${RM} $@ + $(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CFLAGS) $(INC) -c -o $@ ${srcdir}/bperl.c + +iperl.o: iperl.c + ${RM} $@ + $(SHOBJ_CC) ${SHOBJ_CFLAGS} $(PERL_CFLAGS) -c -o $@ ${srcdir}/iperl.c + +perlxsi.c: + ${PERL5} -MExtUtils::Embed -e xsinit -- -o $@ + +perlxsi.o: perlxsi.c + ${RM} $@ + ${SHOBJ_CC} ${SHOBJ_CFLAGS} $(PERL_CFLAGS) -c -o $@ perlxsi.c + +clean mostlyclean: + ${RM} ${OBJ} + ${RM} ${BUILTIN} + +distclean maintainer-clean: clean + ${RM} perlxsi.c diff --git a/examples/loadables/perl/README b/examples/loadables/perl/README new file mode 100644 index 0000000..a70a99b --- /dev/null +++ b/examples/loadables/perl/README @@ -0,0 +1,6 @@ +This illustrates how to build a perl interpreter into bash. It's not +especially useful; more a proof of concept (it provides none of the +bash internals to the perl interpreter, for example). + +This *may* require adding "-rpath /path/to/perl/CORE" and -lperl options +when compiling bash itself. diff --git a/examples/loadables/perl/bperl.c b/examples/loadables/perl/bperl.c new file mode 100644 index 0000000..77e3f7c --- /dev/null +++ b/examples/loadables/perl/bperl.c @@ -0,0 +1,46 @@ +/* + * perl builtin + */ +#include <config.h> + +#include <fcntl.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" + +#ifndef errno +extern int errno; +#endif + +extern char **make_builtin_argv (); +extern char **export_env; + +extern int perl_main(); + +bperl_builtin(list) +WORD_LIST *list; +{ + char **v; + int c, r; + + v = make_builtin_argv(list, &c); + r = perl_main(c, v, export_env); + free(v); + + return r; +} + +char *bperl_doc[] = { + "An interface to a perl5 interpreter.", + (char *)0 +}; + +struct builtin bperl_struct = { + "bperl", + bperl_builtin, + BUILTIN_ENABLED, + bperl_doc, + "bperl [perl options] [file ...]", + 0 +}; diff --git a/examples/loadables/perl/iperl.c b/examples/loadables/perl/iperl.c new file mode 100644 index 0000000..92a6038 --- /dev/null +++ b/examples/loadables/perl/iperl.c @@ -0,0 +1,24 @@ +#include <EXTERN.h> /* from the Perl distribution */ +#include <perl.h> /* from the Perl distribution */ + +extern void xs_init _((void)); + +static PerlInterpreter *iperl; /*** The Perl interpreter ***/ + +int +perl_main(int argc, char **argv, char **env) +{ + int r; + + iperl = perl_alloc(); + perl_construct(iperl); + perl_parse(iperl, xs_init, argc, argv, (char **)NULL); + r = perl_run(iperl); + +PerlIO_flush(PerlIO_stdout()); +PerlIO_flush(PerlIO_stderr()); + + perl_destruct(iperl); + perl_free(iperl); + return (r); +} diff --git a/examples/loadables/print.c b/examples/loadables/print.c new file mode 100644 index 0000000..ad658a7 --- /dev/null +++ b/examples/loadables/print.c @@ -0,0 +1,169 @@ +/* + * print -- loadable ksh-93 style print builtin + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "bashtypes.h" + +#include <errno.h> +#include <limits.h> +#include <stdio.h> + +#include "bashansi.h" +#include "shell.h" +#include "builtins.h" +#include "stdc.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +int print_builtin (); +static int printargs (); + +static FILE *ofp; + +extern char *this_command_name; + +static char *print_doc[] = { + "Output the arguments. The -f option means to use the argument as a", + "format string as would be supplied to printf(1). The rest of the", + "options are as in ksh.", + (char *)NULL +}; + +struct builtin print_struct = { + "print", + print_builtin, + BUILTIN_ENABLED, + print_doc, + "print [-Rnprs] [-u unit] [-f format] [arguments]", + (char *)0 +}; + +#ifndef ISOPTION +#define ISOPTION(s, c) (s[0] == '-' && s[2] == '\0' && s[1] == c) +#endif + +int +print_builtin (list) + WORD_LIST *list; +{ + int c, r, nflag, raw, ofd, sflag; + intmax_t lfd; + char **v, *pfmt, *arg; + WORD_LIST *l; + + nflag = raw = sflag = 0; + ofd = 1; + pfmt = 0; + + reset_internal_getopt (); + while ((c = internal_getopt (list, "Rnprsu:f:")) != -1) + { + switch (c) + { + case 'R': + raw = 2; + loptend = lcurrent; + if (loptend && ISOPTION (loptend->word->word, 'n')) + { + loptend = loptend->next; + nflag = 1; + } + goto opt_end; + case 'r': + raw = 1; + break; + case 'n': + nflag = 1; + break; + case 's': + sflag = 1; + break; + case 'p': + break; /* NOP */ + case 'u': + if (all_digits (list_optarg) && legal_number (list_optarg, &lfd) && lfd == (int)lfd) + ofd = lfd; + else + { + for (l = list; l->next && l->next != lcurrent; l = l->next); + lcurrent = loptend = l; + goto opt_end; + } + break; + case 'f': + pfmt = list_optarg; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + +opt_end: + list = loptend; + + ofp = (ofd == 1) ? stdout : fdopen (dup (ofd), "w"); + + if (pfmt) + { + WORD_DESC *w; + WORD_LIST *nlist; + + w = make_word (pfmt); + nlist = make_word_list (w, list); + r = printf_builtin (nlist); + nlist->next = (WORD_LIST *)NULL; + dispose_words (nlist); + return (r); + } + + if (raw) + { + for (l = list; l; l = l->next) + { + fprintf (ofp, "%s", l->word->word); + if (l->next) + fprintf (ofp, " "); + } + if (nflag == 0) + fprintf (ofp, "\n"); + fflush (ofp); + return (0); + } + + r = printargs (list, ofp); + if (r && nflag == 0) + fprintf (ofp, "\n"); + if (ofd != 1) + fclose (ofp); + return 0; +} + +static int +printargs (list, ofp) + WORD_LIST *list; + FILE *ofp; +{ + WORD_LIST *l; + char *ostr; + int sawc; + + for (sawc = 0, l = list; l; l = l->next) + { + ostr = ansicstr (l->word->word, strlen (l->word->word), 0, &sawc, (int *)0); + fprintf (ofp, "%s", ostr); + free (ostr); + if (sawc) + return (0); + if (l->next) + fprintf (ofp, " "); + } + return (1); +} diff --git a/examples/loadables/printenv.c b/examples/loadables/printenv.c new file mode 100644 index 0000000..16f398f --- /dev/null +++ b/examples/loadables/printenv.c @@ -0,0 +1,71 @@ +/* + * printenv -- minimal builtin clone of BSD printenv(1). + * + * usage: printenv [varname] + * + */ + +#include <config.h> +#include <stdio.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +extern char **export_env; + +int +printenv_builtin (list) + WORD_LIST *list; +{ + register char **envp; + int opt; + SHELL_VAR *var; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + /* printenv */ + if (list == 0) + { + maybe_make_export_env (); /* this allows minimal code */ + for (envp = export_env; *envp; envp++) + printf ("%s\n", *envp); + return (EXECUTION_SUCCESS); + } + + /* printenv varname */ + var = find_variable (list->word->word); + if (var == 0 || (exported_p (var) == 0)) + return (EXECUTION_FAILURE); + + if (function_p (var)) + print_var_function (var); + else + print_var_value (var, 0); + + return (EXECUTION_SUCCESS); +} + +char *printenv_doc[] = { + "print values of environment variables", + (char *)NULL +}; + +struct builtin printenv_struct = { + "printenv", + printenv_builtin, + BUILTIN_ENABLED, + printenv_doc, + "printenv [varname]", + 0 +}; diff --git a/examples/loadables/push.c b/examples/loadables/push.c new file mode 100644 index 0000000..497ecd0 --- /dev/null +++ b/examples/loadables/push.c @@ -0,0 +1,95 @@ +/* + * push - anyone remember TOPS-20? + * + */ + +#include <config.h> +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "jobs.h" +#include "bashgetopt.h" + +#ifndef errno +extern int errno; +#endif + +extern int dollar_dollar_pid; +extern int last_command_exit_value; + +int +push_builtin (list) + WORD_LIST *list; +{ + pid_t pid; + int xstatus, opt; + + xstatus = EXECUTION_SUCCESS; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + pid = make_child (savestring ("push"), 0); + if (pid == -1) + { + builtin_error ("cannot fork: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } + else if (pid == 0) + { + /* Shell variable adjustments: $SHLVL, $$, $PPID, $! */ + adjust_shell_level (1); + dollar_dollar_pid = getpid (); + set_ppid (); + + /* Clean up job control stuff. */ + stop_making_children (); + cleanup_the_pipeline (); + delete_all_jobs (0); + + last_asynchronous_pid = NO_PID; + + /* Make sure the job control code has the right values for + the shell's process group and tty process group, and that + the signals are set correctly for job control. */ + initialize_job_control (0); + initialize_job_signals (); + + /* And read commands until exit. */ + reader_loop (); + exit_shell (last_command_exit_value); + } + else + { + stop_pipeline (0, (COMMAND *)NULL); + xstatus = wait_for (pid); + return (xstatus); + } +} + +char *push_doc[] = { + "Create a child that is an exact duplicate of the running shell", + "and wait for it to exit. The $SHLVL, $!, $$, and $PPID variables", + "are adjusted in the child. The return value is the exit status", + "of the child.", + (char *)NULL +}; + +struct builtin push_struct = { + "push", + push_builtin, + BUILTIN_ENABLED, + push_doc, + "push", + 0 +}; diff --git a/examples/loadables/realpath.c b/examples/loadables/realpath.c new file mode 100644 index 0000000..16478b7 --- /dev/null +++ b/examples/loadables/realpath.c @@ -0,0 +1,119 @@ +/* + * realpath -- canonicalize pathnames, resolving symlinks + * + * usage: realpath [-csv] pathname [pathname...] + * + * options: -c check whether or not each resolved path exists + * -s no output, exit status determines whether path is valid + * -v produce verbose output + * + * + * exit status: 0 if all pathnames resolved + * 1 if any of the pathname arguments could not be resolved + * + * + * Bash loadable builtin version + * + * Chet Ramey + * chet@po.cwru.edu + */ + +#include "config.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#include "bashansi.h" +#include <maxpath.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#ifndef errno +extern int errno; +#endif + +extern char *sh_realpath(); + +realpath_builtin(list) +WORD_LIST *list; +{ + int opt, cflag, vflag, sflag, es; + char *r, realbuf[PATH_MAX], *p; + struct stat sb; + + if (list == 0) { + builtin_usage(); + return (EX_USAGE); + } + + vflag = cflag = sflag = 0; + reset_internal_getopt(); + while ((opt = internal_getopt (list, "csv")) != -1) { + switch (opt) { + case 'c': + cflag = 1; + break; + case 's': + sflag = 1; + break; + case 'v': + vflag = 1; + break; + default: + usage(); + } + } + + list = loptend; + + if (list == 0) + usage(); + + for (es = EXECUTION_SUCCESS; list; list = list->next) { + p = list->word->word; + r = sh_realpath(p, realbuf); + if (r == 0) { + es = EXECUTION_FAILURE; + if (sflag == 0) + builtin_error("%s: cannot resolve: %s", p, strerror(errno)); + continue; + } + if (cflag && (stat(realbuf, &sb) < 0)) { + es = EXECUTION_FAILURE; + if (sflag == 0) + builtin_error("%s: %s", p, strerror(errno)); + continue; + } + if (sflag == 0) { + if (vflag) + printf ("%s -> ", p); + printf("%s\n", realbuf); + } + } + return es; +} + +char *realpath_doc[] = { + "Display the canonicalized version of each PATHNAME argument, resolving", + "symbolic links. The -c option checks whether or not each resolved name", + "exists. The -s option produces no output; the exit status determines the", + "valididty of each PATHNAME. The -v option produces verbose output. The", + "exit status is 0 if each PATHNAME was resolved; non-zero otherwise.", + (char *)NULL +}; + +struct builtin realpath_struct = { + "realpath", /* builtin name */ + realpath_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + realpath_doc, /* array of long documentation strings */ + "realpath [-csv] pathname [pathname...]", /* usage synopsis */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/rmdir.c b/examples/loadables/rmdir.c new file mode 100644 index 0000000..8d0f06a --- /dev/null +++ b/examples/loadables/rmdir.c @@ -0,0 +1,50 @@ +/* rmdir - remove directory */ + +/* See Makefile for compilation details. */ + +#include "config.h" + +#include <stdio.h> +#include <errno.h> +#include "builtins.h" +#include "shell.h" + +#if !defined (errno) +extern int errno; +#endif + +rmdir_builtin (list) + WORD_LIST *list; +{ + int rval; + WORD_LIST *l; + + if (no_options (list)) + return (EX_USAGE); + + for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next) + if (rmdir (l->word->word) < 0) + { + builtin_error ("%s: %s", l->word->word, strerror (errno)); + rval = EXECUTION_FAILURE; + } + + return rval; +} + +char *rmdir_doc[] = { + "rmdir removes the directory entry specified by each argument,", + "provided the directory is empty.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin rmdir_struct = { + "rmdir", /* builtin name */ + rmdir_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + rmdir_doc, /* array of long documentation strings. */ + "rmdir directory ...", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/sleep.c b/examples/loadables/sleep.c new file mode 100644 index 0000000..10a62cf --- /dev/null +++ b/examples/loadables/sleep.c @@ -0,0 +1,146 @@ +/* + * sleep -- sleep for fractions of a second + * + * usage: sleep seconds[.fraction] + */ +#include "config.h" + +#include "bashtypes.h" + +#if defined (TIME_WITH_SYS_TIME) +# include <sys/time.h> +# include <time.h> +#else +# if defined (HAVE_SYS_TIME_H) +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#include <stdio.h> +#include "chartypes.h" + +#include "shell.h" +#include "builtins.h" + +#define RETURN(x) \ + do { \ + if (sp) *sp = sec; \ + if (usp) *usp = usec; \ + return (x); \ + } while (0) + +#if defined (HAVE_TIMEVAL) && defined (HAVE_SELECT) +static int +fsleep(sec, usec) +long sec, usec; +{ + struct timeval tv; + + tv.tv_sec = sec; + tv.tv_usec = usec; + + return select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv); +} +#else /* !HAVE_TIMEVAL || !HAVE_SELECT */ +static int +fsleep(sec, usec) +long sec, usec; +{ + if (usec >= 500000) /* round */ + sec++; + return (sleep(sec)); +} +#endif /* !HAVE_TIMEVAL || !HAVE_SELECT */ + +/* + * An incredibly simplistic floating point converter. + */ +static int multiplier[7] = { 1, 100000, 10000, 1000, 100, 10, 1 }; + +static int +convert(s, sp, usp) +char *s; +long *sp, *usp; +{ + int n; + long sec, usec; + char *p; + + sec = usec = 0; + +#define DECIMAL '.' + + for (p = s; p && *p; p++) { + if (*p == DECIMAL) /* decimal point */ + break; + if (DIGIT(*p) == 0) + RETURN(0); + sec = (sec * 10) + (*p - '0'); + } + + if (*p == 0) + RETURN(1); + + if (*p == DECIMAL) + p++; + + /* Look for up to six digits past a decimal point. */ + for (n = 0; n < 6 && p[n]; n++) { + if (DIGIT(p[n]) == 0) + RETURN(0); + usec = (usec * 10) + (p[n] - '0'); + } + + /* Now convert to millionths */ + usec *= multiplier[n]; + + if (n == 6 && p[6] >= '5' && p[6] <= '9') + usec++; /* round up 1 */ + + RETURN(1); +} + +int +sleep_builtin (list) +WORD_LIST *list; +{ + long sec, usec; + + if (list == 0) { + builtin_usage(); + return(EX_USAGE); + } + + if (*list->word->word == '-' || list->next) { + builtin_usage (); + return (EX_USAGE); + } + + if (convert(list->word->word, &sec, &usec)) { + fsleep(sec, usec); + return(EXECUTION_SUCCESS); + } + + builtin_error("%s: bad sleep interval", list->word->word); + return (EXECUTION_FAILURE); +} + +static char *sleep_doc[] = { + "sleep suspends execution for a minimum of SECONDS[.FRACTION] seconds.", + (char *)NULL +}; + +struct builtin sleep_struct = { + "sleep", + sleep_builtin, + BUILTIN_ENABLED, + sleep_doc, + "sleep seconds[.fraction]", + 0 +}; diff --git a/examples/loadables/strftime.c b/examples/loadables/strftime.c new file mode 100644 index 0000000..92f489e --- /dev/null +++ b/examples/loadables/strftime.c @@ -0,0 +1,105 @@ +/* strftime - loadable builtin interface to strftime(3) */ + +/* See Makefile for compilation details. */ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashtypes.h" +#include "posixtime.h" + +#include <stdio.h> + +#include "builtins.h" +#include "shell.h" +#include "common.h" + +int +strftime_builtin (list) + WORD_LIST *list; +{ + char *format, *tbuf; + size_t tbsize, tsize; + time_t secs; + struct tm *t; + int n; + intmax_t i; + + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (no_options (list)) + return (EX_USAGE); + + format = list->word->word; + if (format == 0 || *format == 0) + { + printf ("\n"); + return (EXECUTION_SUCCESS); + } + + list = list->next; + + if (list && list->word->word) + { + n = legal_number (list->word->word, &i); + if (n == 0 || i < 0 || i != (time_t)i) + { + sh_invalidnum (list->word->word); + return (EXECUTION_FAILURE); + } + else + secs = i; + } + else + secs = NOW; + + t = localtime (&secs); + + tbsize = strlen (format) * 4; + tbuf = 0; + + /* Now try to figure out how big the buffer should really be. strftime(3) + will return the number of bytes placed in the buffer unless it's greater + than MAXSIZE, in which case it returns 0. */ + for (n = 1; n < 4; n++) + { + tbuf = xrealloc (tbuf, tbsize * n); + tsize = strftime (tbuf, tbsize * n, format, t); + if (tsize) + break; + } + + printf ("%s\n", tbuf); + free (tbuf); + + return (EXECUTION_SUCCESS); +} + +/* An array of strings forming the `long' documentation for a builtin xxx, + which is printed by `help xxx'. It must end with a NULL. */ +char *strftime_doc[] = { + "Converts date and time format to a string and displays it on the", + "standard output. If the optional second argument is supplied, it", + "is used as the number of seconds since the epoch to use in the", + "conversion, otherwise the current time is used.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. The flags must include BUILTIN_ENABLED so the + builtin can be used. */ +struct builtin strftime_struct = { + "strftime", /* builtin name */ + strftime_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + strftime_doc, /* array of long documentation strings. */ + "strftime format [seconds]", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/sync.c b/examples/loadables/sync.c new file mode 100644 index 0000000..44d4e09 --- /dev/null +++ b/examples/loadables/sync.c @@ -0,0 +1,32 @@ +/* sync - sync the disks by forcing pending filesystem writes to complete */ + +#include <config.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +sync_builtin (list) + WORD_LIST *list; +{ + sync(); + return (EXECUTION_SUCCESS); +} + +char *sync_doc[] = { + "force completion of pending disk writes", + (char *)NULL +}; + +struct builtin sync_struct = { + "sync", /* builtin name */ + sync_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + sync_doc, /* array of long documentation strings. */ + "sync", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/tee.c b/examples/loadables/tee.c new file mode 100644 index 0000000..934abda --- /dev/null +++ b/examples/loadables/tee.c @@ -0,0 +1,157 @@ +/* tee - duplicate standard input */ + +/* See Makefile for compilation details. */ + +#include "config.h" + +#include "bashtypes.h" +#include "posixstat.h" +#include "filecntl.h" + +#include <signal.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +typedef struct flist { + struct flist *next; + int fd; + char *fname; +} FLIST; + +static FLIST *tee_flist; + +#define TEE_BUFSIZE 8192 + +extern int interrupt_immediately; + +extern char *strerror (); + +tee_builtin (list) + WORD_LIST *list; +{ + int opt, append, nointr, rval, fd, fflags; + int n, nr, nw; + FLIST *fl; + char *buf, *bp; + + char *t; + + reset_internal_getopt (); + append = nointr = 0; + tee_flist = (FLIST *)NULL; + while ((opt = internal_getopt (list, "ai")) != -1) + { + switch (opt) + { + case 'a': + append = 1; + break; + case 'i': + nointr = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (nointr == 0) + interrupt_immediately++; + + buf = xmalloc (TEE_BUFSIZE); + + /* Initialize output file list. */ + fl = tee_flist = (FLIST *)xmalloc (sizeof(FLIST)); + tee_flist->fd = 1; + tee_flist->fname = "stdout"; + tee_flist->next = (FLIST *)NULL; + + /* Add file arguments to list of output files. */ + fflags = append ? O_WRONLY|O_CREAT|O_APPEND : O_WRONLY|O_CREAT|O_TRUNC; + for (rval = EXECUTION_SUCCESS; list; list = list->next) + { + fd = open (list->word->word, fflags, 0666); + if (fd < 0) + { + builtin_error ("%s: cannot open: %s", list->word->word, strerror (errno)); + rval = EXECUTION_FAILURE; + } + else + { + fl->next = (FLIST *)xmalloc (sizeof(FLIST)); + fl->next->fd = fd; + fl->next->fname = list->word->word; + fl = fl->next; + fl->next = (FLIST *)NULL; + } + } + + while ((nr = read(0, buf, TEE_BUFSIZE)) > 0) + for (fl = tee_flist; fl; fl = fl->next) + { + n = nr; + bp = buf; + do + { + if ((nw = write (fl->fd, bp, n)) == -1) + { + builtin_error ("%s: write error: %s", fl->fname, strerror (errno)); + rval = EXECUTION_FAILURE; + break; + } + bp += nw; + } + while (n -= nw); + } + if (nr < 0) + builtin_error ("read error: %s", strerror (errno)); + + /* Deallocate resources -- this is a builtin command. */ + tee_flist = tee_flist->next; /* skip bogus close of stdout */ + while (tee_flist) + { + fl = tee_flist; + if (close (fl->fd) < 0) + { + builtin_error ("%s: close_error: %s", fl->fname, strerror (errno)); + rval = EXECUTION_FAILURE; + } + tee_flist = tee_flist->next; + free (fl); + } + + return (rval); +} + +char *tee_doc[] = { + "Copy standard input to standard output, making a copy in each", + "filename argument. If the `-a' option is gived, the specified", + "files are appended to, otherwise they are overwritten. If the", + "`-i' option is supplied, tee ignores interrupts.", + (char *)NULL +}; + +struct builtin tee_struct = { + "tee", /* builtin name */ + tee_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + tee_doc, /* array of long documentation strings. */ + "tee [-ai] [file ...]", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/template.c b/examples/loadables/template.c new file mode 100644 index 0000000..7bb3f9f --- /dev/null +++ b/examples/loadables/template.c @@ -0,0 +1,56 @@ +/* template - example template for loadable builtin */ + +/* See Makefile for compilation details. */ + +#include <config.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif +#include "bashansi.h" +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#if !defined (errno) +extern int errno; +#endif + +extern char *strerror (); + +template_builtin (list) + WORD_LIST *list; +{ + int opt, rval; + + rval = EXECUTION_SUCCESS; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + return (rval); +} + +char *template_doc[] = { + (char *)NULL +}; + +struct builtin template_struct = { + "template", /* builtin name */ + template_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + template_doc, /* array of long documentation strings. */ + "template", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/truefalse.c b/examples/loadables/truefalse.c new file mode 100644 index 0000000..e77c74c --- /dev/null +++ b/examples/loadables/truefalse.c @@ -0,0 +1,45 @@ +/* true and false builtins */ + +#include "bashtypes.h" +#include "shell.h" +#include "builtins.h" + +true_builtin (list) + WORD_LIST *list; +{ + return EXECUTION_SUCCESS; +} + +false_builtin (list) + WORD_LIST *list; +{ + return EXECUTION_FAILURE; +} + +static char *true_doc[] = { + "Return a successful result.", + (char *)NULL +}; + +static char *false_doc[] = { + "Return an unsuccessful result.", + (char *)NULL +}; + +struct builtin true_struct = { + "true", + true_builtin, + BUILTIN_ENABLED, + true_doc, + "true", + 0 +}; + +struct builtin false_struct = { + "false", + false_builtin, + BUILTIN_ENABLED, + false_doc, + "false", + 0 +}; diff --git a/examples/loadables/tty.c b/examples/loadables/tty.c new file mode 100644 index 0000000..2183123 --- /dev/null +++ b/examples/loadables/tty.c @@ -0,0 +1,59 @@ +/* tty - return terminal name */ + +/* See Makefile for compilation details. */ + +#include "config.h" + +#include <stdio.h> +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +extern char *ttyname (); + +tty_builtin (list) + WORD_LIST *list; +{ + int opt, sflag; + char *t; + + reset_internal_getopt (); + sflag = 0; + while ((opt = internal_getopt (list, "s")) != -1) + { + switch (opt) + { + case 's': + sflag = 1; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + t = ttyname (0); + if (sflag == 0) + puts (t ? t : "not a tty"); + return (t ? EXECUTION_SUCCESS : EXECUTION_FAILURE); +} + +char *tty_doc[] = { + "tty writes the name of the terminal that is opened for standard", + "input to standard output. If the `-s' option is supplied, nothing", + "is written; the exit status determines whether or not the standard", + "input is connected to a tty.", + (char *)NULL +}; + +/* The standard structure describing a builtin command. bash keeps an array + of these structures. */ +struct builtin tty_struct = { + "tty", /* builtin name */ + tty_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + tty_doc, /* array of long documentation strings. */ + "tty [-s]", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/uname.c b/examples/loadables/uname.c new file mode 100644 index 0000000..9f450cd --- /dev/null +++ b/examples/loadables/uname.c @@ -0,0 +1,139 @@ +/* + * uname - print system information + * + * usage: uname [-amnrsv] + * + */ + +#include <config.h> +#include <stdio.h> + +#include "bashtypes.h" + +#if defined (HAVE_UNAME) +# include <sys/utsname.h> +#else +struct utsname { + char sysname[32]; + char nodename[32]; + char release[32]; + char version[32]; + char machine[32]; +}; +#endif + +#include <errno.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +#define FLAG_SYSNAME 0x01 /* -s */ +#define FLAG_NODENAME 0x02 /* -n */ +#define FLAG_RELEASE 0x04 /* -r */ +#define FLAG_VERSION 0x08 /* -v */ +#define FLAG_MACHINE 0x10 /* -m, -p */ + +#define FLAG_ALL 0x1f + +#ifndef errno +extern int errno; +#endif + +static void uprint(); + +static int uname_flags; + +uname_builtin (list) + WORD_LIST *list; +{ + int opt, r; + struct utsname uninfo; + + uname_flags = 0; + reset_internal_getopt (); + while ((opt = internal_getopt (list, "amnprsv")) != -1) + { + switch (opt) + { + case 'a': + uname_flags |= FLAG_ALL; + break; + case 'm': + case 'p': + uname_flags |= FLAG_MACHINE; + break; + case 'n': + uname_flags |= FLAG_NODENAME; + break; + case 'r': + uname_flags |= FLAG_RELEASE; + break; + case 's': + uname_flags |= FLAG_SYSNAME; + break; + case 'v': + uname_flags |= FLAG_VERSION; + break; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list) + { + builtin_usage (); + return (EX_USAGE); + } + + if (uname_flags == 0) + uname_flags = FLAG_SYSNAME; + + /* Only ancient systems will not have uname(2). */ +#ifdef HAVE_UNAME + if (uname (&uninfo) < 0) + { + builtin_error ("cannot get system name: %s", strerror (errno)); + return (EXECUTION_FAILURE); + } +#else + builtin_error ("cannot get system information: uname(2) not available"); + return (EXECUTION_FAILURE); +#endif + + uprint (FLAG_SYSNAME, uninfo.sysname); + uprint (FLAG_NODENAME, uninfo.nodename); + uprint (FLAG_RELEASE, uninfo.release); + uprint (FLAG_VERSION, uninfo.version); + uprint (FLAG_MACHINE, uninfo.machine); + + return (EXECUTION_SUCCESS); +} + +static void +uprint (flag, info) + int flag; + char *info; +{ + if (uname_flags & flag) + { + uname_flags &= ~flag; + printf ("%s%c", info, uname_flags ? ' ' : '\n'); + } +} + +char *uname_doc[] = { + "display information about the system", + (char *)NULL +}; + +struct builtin uname_struct = { + "uname", + uname_builtin, + BUILTIN_ENABLED, + uname_doc, + "uname [-amnrsv]", + 0 +}; diff --git a/examples/loadables/unlink.c b/examples/loadables/unlink.c new file mode 100644 index 0000000..8c81ad0 --- /dev/null +++ b/examples/loadables/unlink.c @@ -0,0 +1,52 @@ +/* unlink - remove a directory entry */ + +/* Should only be used to remove directories by a superuser prepared to let + fsck clean up the file system. */ + +#include <config.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <stdio.h> +#include <errno.h> + +#include "builtins.h" +#include "shell.h" + +#ifndef errno +extern int errno; +#endif + +unlink_builtin (list) + WORD_LIST *list; +{ + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + if (unlink (list->word->word) != 0) + { + builtin_error ("%s: cannot unlink: %s", list->word->word, strerror (errno)); + return (EXECUTION_FAILURE); + } + + return (EXECUTION_SUCCESS); +} + +char *unlink_doc[] = { + "Remove a directory entry.", + (char *)NULL +}; + +struct builtin unlink_struct = { + "unlink", /* builtin name */ + unlink_builtin, /* function implementing the builtin */ + BUILTIN_ENABLED, /* initial flags for builtin */ + unlink_doc, /* array of long documentation strings. */ + "unlink name", /* usage synopsis; becomes short_doc */ + 0 /* reserved for internal use */ +}; diff --git a/examples/loadables/whoami.c b/examples/loadables/whoami.c new file mode 100644 index 0000000..41fd5c4 --- /dev/null +++ b/examples/loadables/whoami.c @@ -0,0 +1,52 @@ +/* + * whoami - print out username of current user + */ + +#include <config.h> +#include <stdio.h> + +#include "builtins.h" +#include "shell.h" +#include "bashgetopt.h" + +whoami_builtin (list) + WORD_LIST *list; +{ + int opt; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "")) != -1) + { + switch (opt) + { + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + if (list) + { + builtin_usage (); + return (EX_USAGE); + } + + if (current_user.user_name == 0) + get_current_user_info (); + printf ("%s\n", current_user.user_name); + return (EXECUTION_SUCCESS); +} + +char *whoami_doc[] = { + "display name of current user", + (char *)NULL +}; + +struct builtin whoami_struct = { + "whoami", + whoami_builtin, + BUILTIN_ENABLED, + whoami_doc, + "whoami", + 0 +}; diff --git a/examples/misc/aliasconv.bash b/examples/misc/aliasconv.bash new file mode 100755 index 0000000..2245f06 --- /dev/null +++ b/examples/misc/aliasconv.bash @@ -0,0 +1,42 @@ +#! /bin/bash +# +# aliasconv.bash - convert csh aliases to bash aliases and functions +# +# usage: aliasconv.bash +# +# Chet Ramey +# chet@po.cwru.edu +# +trap 'rm -f /tmp/cb$$.?' 0 1 2 3 6 15 + +T=$'\t' + +cat << \EOF >/tmp/cb$$.1 +mkalias () +{ + case $2 in + '') echo alias ${1}="''" ;; + *[#\!]*) + comm=$(echo $2 | sed 's/\!\*/"$\@"/g + s/\!:\([1-9]\)/"$\1"/g + s/#/\#/g') + echo $1 \(\) "{" command "$comm" "; }" + ;; + *) echo alias ${1}=\'$(echo "${2}" | sed "s:':'\\\\'':g")\' ;; + esac +} +EOF + +# the first thing we want to do is to protect single quotes in the alias, +# since they whole thing is going to be surrounded by single quotes when +# passed to mkalias + +sed -e "s:':\\'\\\'\\':" -e "s/^\([a-zA-Z0-9_-]*\)$T\(.*\)$/mkalias \1 '\2'/" >>/tmp/cb$$.1 + +$BASH /tmp/cb$$.1 | sed -e 's/\$cwd/\$PWD/g' \ + -e 's/\$term/\$TERM/g' \ + -e 's/\$home/\$HOME/g' \ + -e 's/\$user/\$USER/g' \ + -e 's/\$prompt/\$PS1/g' + +exit 0 diff --git a/examples/misc/aliasconv.sh b/examples/misc/aliasconv.sh new file mode 100755 index 0000000..75db077 --- /dev/null +++ b/examples/misc/aliasconv.sh @@ -0,0 +1,42 @@ +#! /bin/bash +# +# aliasconv.sh - convert csh aliases to bash aliases and functions +# +# usage: aliasconv.sh +# +# Chet Ramey +# chet@po.cwru.edu +# +trap 'rm -f /tmp/cb$$.?' 0 1 2 3 6 15 + +T=' ' + +cat << \EOF >/tmp/cb$$.1 +mkalias () +{ + case $2 in + '') echo alias ${1}="''" ;; + *[#\!]*) + comm=`echo $2 | sed 's/\\!\*/"$\@"/g + s/\\!:\([1-9]\)/"$\1"/g + s/#/\#/g'` + echo $1 \(\) "{" command "$comm" "; }" + ;; + *) echo alias ${1}=\'`echo "${2}" | sed "s:':'\\\\\\\\'':"`\' ;; + esac +} +EOF + +# the first thing we want to do is to protect single quotes in the alias, +# since they whole thing is going to be surrounded by single quotes when +# passed to mkalias + +sed -e "s:':\\'\\\'\\':" -e "s/^\([a-zA-Z0-9_-]*\)$T\(.*\)$/mkalias \1 '\2'/" >>/tmp/cb$$.1 + +sh /tmp/cb$$.1 | sed -e 's/\$cwd/\$PWD/g' \ + -e 's/\$term/\$TERM/g' \ + -e 's/\$home/\$HOME/g' \ + -e 's/\$user/\$USER/g' \ + -e 's/\$prompt/\$PS1/g' + +exit 0 diff --git a/examples/misc/cshtobash b/examples/misc/cshtobash new file mode 100755 index 0000000..8e2b05c --- /dev/null +++ b/examples/misc/cshtobash @@ -0,0 +1,130 @@ +#! /bin/bash +# +# cshtobash - convert csh aliases, environment variables, and variables to +# bash equivalents +# +# usage: cshtobash [filename] +# +# If filename is given, that file is sourced. Note that csh always +# sources .cshrc. To recreate your csh login environment, run +# `cshtobash ~/.login'. +# +# Inspired by (and some borrowed from) a similar program distributed with +# zsh-3.0. +# +# Chet Ramey +# chet@po.cwru.edu +# +trap 'rm -f /tmp/cb$$.? cshout cshin' 0 1 2 3 6 15 + +T=$'\t' + +SOURCE="${1:+source $1}" + +cat << EOF >cshin +$SOURCE +alias >! /tmp/cb$$.a +setenv >! /tmp/cb$$.e +set >! /tmp/cb$$.v +EOF + +# give csh a minimal environment, similar to what login would provide +/usr/bin/env - USER=$USER HOME=$HOME PATH=/usr/bin:/bin:/usr/ucb:. TERM=$TERM SHELL=$SHELL /bin/csh -i < ./cshin > cshout 2>&1 + +# First convert aliases + +cat << \EOF >/tmp/cb$$.1 +mkalias () +{ + case $2 in + '') echo alias ${1}="''" ;; + *[#\!]*) + comm=$(echo $2 | sed 's/\!\*/"$\@"/g + s/\!:\([1-9]\)/"$\1"/g + s/#/\#/g') + echo $1 \(\) "{" command "$comm" "; }" + ;; + *) echo alias ${1}=\'$(echo "${2}" | sed "s:':'\\\\'':")\' ;; + esac +} +EOF + +sed "s/^\([a-zA-Z0-9_]*\)$T\(.*\)$/mkalias \1 '\2'/" < /tmp/cb$$.a >>/tmp/cb$$.1 + +echo '# csh aliases' +echo + +$BASH /tmp/cb$$.1 | sed -e 's/\$cwd/\$PWD/g' \ + -e 's/\$term/\$TERM/g' \ + -e 's/\$home/\$HOME/g' \ + -e 's/\$user/\$USER/g' \ + -e 's/\$prompt/\$PS1/g' + +# Next, convert environment variables +echo +echo '# csh environment variables' +echo + +# Would be nice to deal with embedded newlines, e.g. in TERMCAP, but ... +sed -e '/^SHLVL/d' \ + -e '/^PWD/d' \ + -e "s/'/'"\\\\"''"/g \ + -e "s/^\([A-Za-z0-9_]*=\)/export \1'/" \ + -e "s/$/'/" < /tmp/cb$$.e + +# Finally, convert local variables +echo +echo '# csh variables' +echo + +sed -e 's/'"$T"'/=/' \ + -e "s/'/'"\\\\"''"/g \ + -e '/^[A-Za-z0-9_]*=[^(]/{ + s/=/='"'/"' + s/$/'"'/"' + }' < /tmp/cb$$.v | +sed -e '/^argv=/d' -e '/^cwd=/d' -e '/^filec=/d' -e '/^status=/d' \ + -e '/^verbose=/d' \ + -e '/^term=/d' \ + -e '/^home=/d' \ + -e '/^path=/d' \ + -e '/^user=/d' \ + -e '/^shell=/d' \ + -e '/^cdpath=/d' \ + -e '/^mail=/d' \ + -e '/^home=/s//HOME=/' \ + -e '/^prompt=/s//PS1=/' \ + -e '/^histfile=/s//HISTFILE=/' \ + -e '/^history=/s//HISTSIZE=/' \ + -e '/^savehist=$/s//HISTFILESIZE=${HISTSIZE-500}/' \ + -e '/^savehist=/s//HISTFILESIZE=/' \ + -e '/^ignoreeof=$/s/^.*$/set -o ignoreeof # ignoreeof/' \ + -e '/^ignoreeof=/s//IGNOREEOF=/' \ + -e '/^noclobber=/s/^.*$/set -C # noclobber/' \ + -e '/^notify=/s/^.*$/set -b # notify/' \ + -e '/^noglob=/s/^.*$/set -f # noglob/' \ + + +# now some special csh variables converted to bash equivalents +echo +echo '# special csh variables converted to bash equivalents' +echo + +sed -e 's/'"$T"'/=/' < /tmp/cb$$.v | +grep "^cdpath=" | +sed 's/(// + s/ /:/g + s/)// + s/cdpath=/CDPATH=/' + + +sed -e 's/'"$T"'/=/' < /tmp/cb$$.v | +grep "^mail=" | +sed 's/(// + s/ /:/g + s/)// + s/mail=/MAILPATH=/' | +sed -e 's/MAILPATH=\([0-9][0-9][^:]*\)$/MAILCHECK=\1/' \ + -e 's/MAILPATH=\([0-9][0-9][^:]*\):\(.*\)/MAILCHECK=\1 MAILPATH=\2/' + +exit 0 diff --git a/examples/misc/suncmd.termcap b/examples/misc/suncmd.termcap new file mode 100644 index 0000000..c3422fb --- /dev/null +++ b/examples/misc/suncmd.termcap @@ -0,0 +1,30 @@ +#Posted-Date: Fri, 9 Mar 90 18:34:29 EST +#Date: Fri, 9 Mar 90 18:34:29 EST +#From: "Eirik Fuller" <wonton.tn.cornell.edu!eirik@ucsbcsl.UUCP> +#To: bfox@ai.mit.edu (Brian Fox) +#Subject: Patch to bash 1.05 for SunView +# +#I think this works: +# +Mu|sun-cmd:am:bs:km:pt:li#34:co#80:cl=^L:ce=\E[K:cd=\E[J:rs=\E[s: +# +#Another alternative is to send the ti string at startup time (and, I +#guess, the te string at exit time); that is how vi works in a cmdtool. +#The best reason to not do this is that this also disables scrolling +#which, as I understand it, is why anyone would use cmdtool in the +#first place. Sending the ti string at startup time would do strange +#things on other systems too; in xterm it would use the alternate +#screen. +# +#The problem with cmdtool, in case that is less than obvious, is that +#almost none of the capabilities advertised in /etc/termcap are enabled +#while scrolling is enabled. It has other problems too, like being +#part of an outdated proprietary windowing system, but there's probably +#no need to dwell on that. In a sense, though, the sun-cmd termcap +#entry doesn't lie about the capabilities; I think the termcap man page +#does warn about some terminals having cursor motion capabilities only +#in the "ti/te window". +# +#A general solution to this problem would require a termcap capability +#which somehow tells which features are available outside of the ti/te +#window. There is no such capability in termcap now, of course. diff --git a/examples/obashdb/PERMISSION b/examples/obashdb/PERMISSION new file mode 100644 index 0000000..4e9460c --- /dev/null +++ b/examples/obashdb/PERMISSION @@ -0,0 +1,27 @@ +From mikel@ora.com Tue Aug 1 12:13:20 1995 +Flags: 10 +Return-Path: mikel@ora.com +Received: from ruby.ora.com (ruby.ora.com [198.112.208.25]) by odin.INS.CWRU.Edu with ESMTP (8.6.12+cwru/CWRU-2.1-ins) + id MAA01565; Tue, 1 Aug 1995 12:13:18 -0400 (from mikel@ora.com for <chet@odin.INS.CWRU.Edu>) +Received: (from fax@localhost) by ruby.ora.com (8.6.12/8.6.11) with UUCP id MAA23251; Tue, 1 Aug 1995 12:07:51 -0400 +Received: by los.ora.com (4.1/Spike-2.1) + id AA00672; Tue, 1 Aug 95 08:57:32 EDT +Date: Tue, 1 Aug 95 08:57:32 EDT +From: mikel@ora.com (Michael Loukides) +Message-Id: <9508011257.AA00672@los.ora.com> +Subject: Re: Ksh debugger from Rosenblatt's book [for bash] +To: Chet Ramey <chet@odin.INS.CWRU.Edu> +Cc: cmarie@ora.com, cam@iinet.com.au, brosenblatt@tm.com +In-Reply-To: Chet Ramey <chet@odin.INS.CWRU.Edu>, Mon, 31 Jul 1995 16:22:48 -0400 + + I've modified a (modified) version of Bill Rosenblatt's ksh debugger + to work with bash-2.0. Does ORA have any problem with me distributing + it with bash-2.0? + +That's great! + +Go ahead and circulate it; in fact, we should probably grab it and +stick it in our ftp archive, and put a reference to it in the book. +(Too late to actually discuss the thing, at least for this edition). +------- + diff --git a/examples/obashdb/README b/examples/obashdb/README new file mode 100644 index 0000000..3373f5f --- /dev/null +++ b/examples/obashdb/README @@ -0,0 +1,12 @@ +This is a modified version of the Korn Shell debugger from Bill +Rosenblatt's `Learning the Korn Shell', published by O'Reilly +and Associates (ISBN 1-56592-054-6). + +The original `kshdb' is available for anonymous FTP with the URL + +http://examples.oreilly.com/korn/ksh.tar.Z + +A revised edition is available at: + +http://examples.oreilly.com/korn2/korn2_examples.tar.gz + diff --git a/examples/obashdb/bashdb b/examples/obashdb/bashdb new file mode 100644 index 0000000..97d287d --- /dev/null +++ b/examples/obashdb/bashdb @@ -0,0 +1,33 @@ +# kshdb - Korn Shell Debugger main file +# adapted from 'Learning the Korn Shell' by Bill Rosenblatt (O'Reilly) +# by Cigy Cyriac (cigy@felix.tulblr.unisys.com) +# Main driver: constructs full script (with preamble) and runs it + +echo 'Bourne-Again Shell Debugger version 0.1' + +_pname=${0##*/} + +[ $# -eq 0 ] && { + echo "${_pname}: usage: ${_pname} <script_file>" + exit 1 +} + +_guineapig=$1 + +[ -r $_guineapig ] || { + echo "${_pname}: cannot read $_guineapig." >&2 + exit 1 +} +shift + +_tmpdir=/tmp +_libdir=. +_dbgfile=$_tmpdir/bashdb$$ #temp file for script being debugged + +cat $_libdir/bashdb.pre $_guineapig > $_dbgfile +if [ -f "$BASH" ]; then + exec $BASH $_dbgfile $_guineapig $_tmpdir $_libdir "$@" +else + exec bash $_dbgfile $_guineapig $_tmpdir $_libdir "$@" +fi +# end of bashdb diff --git a/examples/obashdb/bashdb.fns b/examples/obashdb/bashdb.fns new file mode 100644 index 0000000..ac0612c --- /dev/null +++ b/examples/obashdb/bashdb.fns @@ -0,0 +1,237 @@ +# bashdb.fns - Bourne-Again Shell Debugger functions + +_BUFSIZ=100 + +# Here after each statement in script being debugged. +# Handle single-step and breakpoints. +_steptrap() { + let _curline=$1-1 # no. of line that just ran + let "$_curline < 1" && let _curline=1 + + let "$_curline > $_firstline+$_BUFSIZ" && _readin $_curline + + let " $_trace" && + _msg "$PS4, line $_curline: ${_lines[$(($_curline-$_firstline+1))]}" + + + # if in step mode, decrement counter + let " $_steps >= 0" && let _steps="$_steps - 1" + + # first check if line num or string brkpt. reached + if _at_linenumbp || _at_stringbp; then + _msg "Reached breakpoint at line $_curline" + _cmdloop # enter debugger + + # if not, check whether break condition exists and is true + elif [ -n "$_brcond" ] && eval $_brcond; then + _msg "Break condition $_brcond true at line $_curline" + _cmdloop # enter debugger + + # next, check if step mode and no. of steps is up + elif let "$_steps == 0"; then + _msg "Stopped at line $_curline" + _cmdloop # enter debugger + fi +} + + +# Debugger command loop. +# Here at start of debugger session, when brkpt. reached, or after single-step. +_cmdloop() { + local cmd args + +# added support for default command (last one entered) + + while read -e -p "bashdb> [$lastcmd $lastargs] " cmd args; do + if [ -z "$cmd" ]; then + cmd=$lastcmd + args=$lastargs + fi + + lastcmd="$cmd" + lastargs=$args + +# made commands to be debugger commands by default, no need for '*' prefix + + case $cmd in + bp ) _setbp $args ;; #set brkpt at line num or string + + bc ) _setbc $args ;; # set break condition + + cb ) _clearbp ;; # clear all brkpts. + + g ) return ;; # start/resume execution + + s ) let _steps=${args:-1} + return ;; # single-step N times(default 1) + + x ) _xtrace ;; # toggle execution trace + + pr ) _print $args ;; # print lines in file + + \? | h | help ) _menu ;; # print command menu + + hi ) history ;; # show command history + + q ) _cleanup; exit ;; # quit + + \! ) eval $args ;; # run shell command + + * ) _msg "Invalid command: $cmd" ; _menu ;; + esac + done +} + + +# see if next line no. is a brkpt. +_at_linenumbp() { + if [ -z "${_linebp}" ]; then + return 1 + fi + echo "${_curline}" | grep -E "(${_linebp%\|})" >/dev/null 2>&1 + return $? +} + + +# search string brkpts to see if next line in script matches. +_at_stringbp() { + local l; + + if [ -z "$_stringbp" ]; then + return 1; + fi + l=${_lines[$_curline-$_firstline+1]} + echo "${l}" | grep -E "\\*(${_stringbp%\|})\\*" >/dev/null 2>&1 + return $? +} + + +# print message to stderr +_msg() { + echo -e "$@" >&2 +} + + +# set brkpt(s) at given line numbers and/or strings +# by appending lines to brkpt file +_setbp() { + declare -i n + case "$1" in + "") _listbp ;; + [0-9]*) #number, set brkpt at that line + n=$1 + _linebp="${_linebp}$n|" + _msg "Breakpoint at line " $1 + ;; + *) #string, set brkpt at next line w/string + _stringbp="${_stringbp}$@|" + _msg "Breakpoint at next line containing $@." + ;; + esac +} + + +# list brkpts and break condition. +_listbp() { + _msg "Breakpoints at lines:" + _msg "${_linebp//\|/ }" + _msg "Breakpoints at strings:" + _msg "${_stringbp//\|/ }" + _msg "Break on condition:" + _msg "$_brcond" +} + + +# set or clear break condition +_setbc() { + if [ -n "$@" ] ; then + _brcond=$args + _msg "Break when true: $_brcond" + else + _brcond= + _msg "Break condition cleared" + fi +} + + +# clear all brkpts +_clearbp() { + _linebp= + _stringbp= + _msg "All breakpoints cleared" +} + + +# toggle execution trace feature +_xtrace() { + let _trace="! $_trace" + + _msg "Execution trace \c" + let " $_trace" && _msg "on." || _msg "off." +} + + +# print command menu +_menu() { + +# made commands to be debugger commands by default, no need for '*' prefix + + _msg 'bashdb commands: + bp N set breakpoint at line N + bp string set breakpoint at next line containing "string" + bp list breakpoints and break condition + bc string set break condition to "string" + bc clear break condition + cb clear all breakpoints + g start/resume execution + s [N] execute N statements (default 1) + x toggle execution trace on/off (default on) + pr [start|.] [cnt] print "cnt" lines from line no. "start" + ?, h, help print this menu + hi show command history + q quit + + ! cmd [args] execute command "cmd" with "args" + + default: last command (in "[ ]" at the prompt) + + Readline command line editing (emacs/vi mode) is available' +} + + +# erase temp files before exiting +_cleanup() { + rm $_dbgfile 2>/dev/null +} + + +# read $_BUFSIZ lines from $_guineapig into _lines array, starting from line $1 +# save number of first line read in _firstline +_readin() { + declare -i _i=1 + let _firstline=$1 + + SEDCMD="$_firstline,$(($_firstline+$_BUFSIZ))p" + + sed -n "$SEDCMD" $_guineapig > /tmp/_script.$$ + while read -r _lines[$_i]; do + _i=_i+1 + done < /tmp/_script.$$ + rm -f /tmp/_script.$$ 2>/dev/null +} + +_print() { + typeset _start _cnt + + if [ -z "$1" ] || [ "$1" = . ]; then + _start=$_curline + else + _start=$1 + fi + + _cnt=${2:-9} + + SEDCMD="$_start,$(($_start+$_cnt))p" + + pr -tn $_guineapig | sed -n "$SEDCMD" +} diff --git a/examples/obashdb/bashdb.pre b/examples/obashdb/bashdb.pre new file mode 100644 index 0000000..c9cdb72 --- /dev/null +++ b/examples/obashdb/bashdb.pre @@ -0,0 +1,37 @@ +# bashdb.pre - Bourne-Again Shell Debugger preamble file +# prepended to script being ddebugged +#arguments: +# $1 = name of original guineapig script +# $2 = dir where temp files are stored +# $3 = dir where bashdb.pre and bashdb.fns are stored + +# separate history file for bashdb +HISTFILE=~/.bashdb_history +set -o history +set +H + +# prompt for trace line +PS4="${1}" + +_dbgfile=$0 +_guineapig=$1 +_tmpdir=$2 +_libdir=$3 +shift 3 #move user's args into place + +. $_libdir/bashdb.fns #read in the debugger functions + +_linebp= +_stringbp= +let _trace=1 #init execution trace flag to on + +#read guineapig file into _lines array +_readin 1 + +trap _cleanup EXIT #erase files before exiting + +let _steps=1 #no. of statements to run after setting trap +#set LINENO, gets incremented to 1 +LINENO=0 +trap '_steptrap $LINENO' DEBUG +: 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 diff --git a/examples/scripts.v2/PERMISSION b/examples/scripts.v2/PERMISSION new file mode 100644 index 0000000..f65e848 --- /dev/null +++ b/examples/scripts.v2/PERMISSION @@ -0,0 +1,59 @@ +From spcecdt@armory.com Wed May 10 10:21:11 1995 +Flags: 10 +Return-Path: spcecdt@armory.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 KAA22876; Wed, 10 May 1995 10:21:10 -0400 (from spcecdt@armory.com for <chet@odin.INS.CWRU.Edu>) +Received: from deepthought.armory.com (mmdf@deepthought.armory.com [192.122.209.42]) by po.cwru.edu with SMTP (8.6.10+cwru/CWRU-2.3) + id BAA16354; Wed, 10 May 1995 01:33:22 -0400 (from spcecdt@armory.com for <chet@po.cwru.edu>) +From: John DuBois <spcecdt@armory.com> +Date: Tue, 9 May 1995 22:33:12 -0700 +In-Reply-To: Chet Ramey <chet@odin.ins.cwru.edu> + "ksh scripts" (May 9, 1:36pm) +X-Www: http://www.armory.com/~spcecdt/ +X-Mailer: Mail User's Shell (7.2.5 10/14/92) +To: chet@po.cwru.edu +Subject: Re: ksh scripts +Message-ID: <9505092233.aa13001@deepthought.armory.com> + + Sure. The canonical versions are available on ftp.armory.com; you might +want to pick up the latest versions before modifying them. + + John + +On May 9, 1:36pm, Chet Ramey wrote: +} Subject: ksh scripts +} From odin.ins.cwru.edu!chet Tue May 9 10:39:51 1995 +} Received: from odin.INS.CWRU.Edu by deepthought.armory.com id aa22336; +} 9 May 95 10:39 PDT +} Received: (chet@localhost) by odin.INS.CWRU.Edu (8.6.10+cwru/CWRU-2.1-ins) +} id NAA20487; Tue, 9 May 1995 13:39:24 -0400 (from chet) +} Date: Tue, 9 May 1995 13:36:54 -0400 +} From: Chet Ramey <chet@odin.ins.cwru.edu> +} To: john@armory.com +} Subject: ksh scripts +} Cc: chet@odin.ins.cwru.edu +} Reply-To: chet@po.cwru.edu +} Message-ID: <9505091736.AA20411.SM@odin.INS.CWRU.Edu> +} Read-Receipt-To: chet@po.CWRU.Edu +} MIME-Version: 1.0 +} Content-Type: text/plain; charset=us-ascii +} Status: OR +} +} Hi. I'm the maintainer of bash (the GNU `Bourne Again shell') for +} the FSF. +} +} I picked up a tar file of ksh scripts you wrote from an anon FTP site +} a while back. I'd like your permission to include modified versions +} of some of them in the next major bash distribution (with proper credit +} given, of course). Is it OK if I do that? +} +} Chet Ramey +} +} -- +} ``The lyf so short, the craft so long to lerne.'' - Chaucer +} +} Chet Ramey, Case Western Reserve University Internet: chet@po.CWRU.Edu +}-- End of excerpt from Chet Ramey + + + diff --git a/examples/scripts.v2/README b/examples/scripts.v2/README new file mode 100644 index 0000000..b3d0559 --- /dev/null +++ b/examples/scripts.v2/README @@ -0,0 +1,33 @@ +This collection of scripts was originally written for ksh-88 by +John DuBois <spcecdt@armory.com>. 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: + +arc2tarz Convert an "arc" archive to a compressed tar archive. +corename Tell what produced a core file. +fman Fast man replacement. +frcp Copy files using ftp but with rcp-type command line syntax. +lowercase Change filenames to lower case. +ncp A nicer front end for cp (has -i, etc.). +newext Change the extension of a group of files. +nmv A nicer front end for mv (has -i, etc.). +pages Print specified pages from files. +pf A pager front end that handles compressed files. +rename Change the names of files that match a pattern. +repeat Execute a command multiple times. +untar Unarchive a (possibly compressed) tarfile into a directory. +uudec Carefully uudecode multiple files. +uuenc uuencode multiple files. +vtree Print a visual display of a directory tree. +where Show where commands that match a pattern are. + +The following scripts were written or converted by Chet Ramey: + +bashrand Random number generator with upper and lower bounds and optional seed +cdhist cd replacement with a directory stack added +pmtop Poor man's `top' for SunOS 4.x and BSD/OS +shprof Line profiler for bash scripts diff --git a/examples/scripts.v2/arc2tarz b/examples/scripts.v2/arc2tarz new file mode 100644 index 0000000..285bede --- /dev/null +++ b/examples/scripts.v2/arc2tarz @@ -0,0 +1,85 @@ +#! /bin/bash +# +# original from: +# arc2tarz: convert arced file to tarred, compressed form. +# @(#) arc2tarz.ksh 1.0 92/02/16 +# 91/03/28 john h. dubois iii (john@armory.com) +# 92/02/16 added -h option for help +# +# conversion to bash v2 syntax by Chet Ramey + +unset ENV +Usage="Usage: $0 arcfile [-hcg] [ tarzfile ]" + +phelp() +{ +echo "$Usage +arcfile is the name of an arc file to convert to tarred, compressed form. +The file must have a .arc extension, but only the base name needs to be +given. If no output file name is given, it will be created in the current +directory with the name being the arcfile basename followed by .tar.EXT. +If the -c option is given, compress will be used, and EXT will be Z. +The default (also available with -g) is to use gzip, in which case EXT +is gz. If the basename is too long the extension may be truncated. All +uppercase letters in the names of files in the archive are moved to lowercase." +} + +compress=gzip +ext=gz + +while getopts "hcg" opt; do + case "$opt" in + h) phelp; exit 0;; + c) compress=compress; ext=Z;; + g) compress=gzip ; ext=gz ;; + *) echo "$Usage" 1>&2 ; exit 2;; + esac +done + +shift $((OPTIND - 1)) + +if [ $# = 0 ]; then + phelp + exit 0 +fi + +[ -z "$TMP" ] && tmpdir=/tmp/arc2tarz.$$ || tmpdir=$TMP/arc2tarz.$$ + +case "$1" in +*.arc) arcfile=$1 ;; +*) arcfile=$1.arc ;; +esac + +if [ ! -f $arcfile ] || [ ! -r $arcfile ]; then + echo "Could not open arc file \"$arcfile\"." + exit 1 +fi + +case "$arcfile" in +/*) ;; +*) arcfile=$PWD/$arcfile ;; +esac + +basename=${arcfile%.arc} +basename=${basename##*/} +[ $# -lt 2 ] && tarzname=$PWD/$basename.tar.$ext || tarzname=$2 + +trap 'rm -rf $tmpdir $tarzname' 1 2 3 6 15 + +mkdir $tmpdir +cd $tmpdir +echo "unarcing files..." +arc -ie $arcfile + +# lowercase +for f in *; do + new=$(echo $f | tr A-Z a-z) + if [ "$f" != "$new" ]; then + mv $f $new + fi +done + +echo "tarring/compressing files..." +tar cf - * | $compress > $tarzname +cd - +rm -rf $tmpdir diff --git a/examples/scripts.v2/bashrand b/examples/scripts.v2/bashrand new file mode 100644 index 0000000..54260c0 --- /dev/null +++ b/examples/scripts.v2/bashrand @@ -0,0 +1,76 @@ +#! /bin/bash +# bashrand - generate a random number in a specified range with an +# optionally specified ``seed'' value. +# +# Original Author: Peter Turnbull, May 1993 + +usage() +{ + echo "$PROG: usage: $PROG [-s seed] lower-limit upper-limit" >&2 +} + +PROG=${0##*/} + +SEED=$$ # Initialize random-number seed value with PID + +while getopts s: opt +do + case "$opt" in + s) SEED=$OPTARG ;; + *) usage ; exit 2 ;; + esac +done + +shift $((OPTIND - 1)) + +# Process command-line arguments: +case $# in + 2) Lower=$1; Upper=$2 ;; + *) usage ; exit 2 ;; +esac + +# Check that specified values are integers: +expr "$Lower" + 0 >/dev/null 2>&1 || { + echo "$PROG: lower ($Lower) not an integer" >&2 + exit 1 +} + +expr "$Upper" + 0 >/dev/null 2>&1 || { + echo "$PROG: upper ($Upper) not an integer" >&2 + exit 1 +} + +expr "$SEED" + 0 >/dev/null 2>&1 || { + echo "$PROG: seed ($SEED) not an integer" >&2 + exit 1 +} + +# Check that values are in the correct range: +(( $Lower < 0 )) || [ `expr "$Lower" : '.*'` -gt 5 ] && { + echo "$PROG: Lower limit ($Lower) out of range" >&2 + exit 1 +} + +(( $Upper > 32767 )) || [ `expr "$Upper" : '.*'` -gt 5 ] && { + echo "$PROG: Upper limit ($Upper) out of range" >&2; + exit 1 +} + +(( $SEED < 0 )) || (( $SEED > 32767 )) || [ `expr "$SEED" : '.*'` -gt 5 ] && { + echo "$PROG: Seed value ($SEED) out of range (0 to 32767)" >&2 + exit 1 +} + +(( $Upper <= $Lower )) && { + echo "$PROG: upper ($Upper) <= lower value ($Lower)" >&2 + exit 1 +} + +# Seed the random-number generator: +RANDOM=$SEED + +# Compute value, scaled within range: +let rand="$RANDOM % ($Upper - $Lower + 1) + $Lower" + +# Report result: +echo $rand diff --git a/examples/scripts.v2/cal2day.bash b/examples/scripts.v2/cal2day.bash new file mode 100644 index 0000000..f26128b --- /dev/null +++ b/examples/scripts.v2/cal2day.bash @@ -0,0 +1,49 @@ +#!/bin/bash +# cal2day - "parse" appropriate calendar output to match date number +# with day name. +# +# usage: cal2day month day [year] +# +# ORIGINAL *TAG:33239 3:Dec 9 1997:0755:sh.d/cal2day: +# +# Obtained from usenet +# +# Converted to bash v2 syntax by Chet Ramey <chet@po.cwru.edu> + +#1 PARSE OPTIONS +while getopts :dls _inst +do case $_inst in + (d) format='%1d%.0s\n' ;; # 0, 1, ..., 7 + (l) format='%0.s%-s\n' ;; # Sunday, Monday, ..., Saturday + (s) format='%0.s%-.3s\n' ;; # Sun, Mon, ..., Sat + esac +done +shift $((OPTIND-1)) + +#2 PARAMETER VALUES +((!$#)) && set -- $(date '+%m %d') +: ${format:='%0.s%-.3s\n'} +: ${1:?missing month parameter [1-12]} +: ${2:?missing day parameter [1-31]} + +#3 CALCULATE DAY-OF-WEEK FROM DATE +cal $1 ${3:-$(date +%Y)} | gawk -FX ' +BEGIN { day="Sunday Monday Tuesday WednesdayThursday Friday Saturday" + sub(/^0/, "", daynum) + dayre="(^| )" daynum "( |$)" + } +#NR==2 { print length($0) } +NR==1 || NR==2 \ + { next } +dayre { if (match($0, dayre)) + { #print RSTART, RLENGTH, substr($0, RSTART, RLENGTH) + if (daynum<=9 || RSTART==1) RSTART-=1 + exit + } + } +END { # 20/21 char width assumed + printf format, RSTART/3, substr(day, RSTART*3+1, 9) + } +' daynum=$2 format=$format - + +exit 0 diff --git a/examples/scripts.v2/cdhist.bash b/examples/scripts.v2/cdhist.bash new file mode 100644 index 0000000..df8aea7 --- /dev/null +++ b/examples/scripts.v2/cdhist.bash @@ -0,0 +1,176 @@ +#! /bin/bash +# +# cdhist - cd replacement with a directory stack like pushd/popd +# +# usage: cd [-l] [-n] [-] [dir] +# +# options: +# -l print the cd directory stack, one entry per line +# - equivalent to $OLDPWD +# -n cd to nth directory in cd directory stack +# -s cd to first directory in stack matching (substring) `s' +# +# arguments: +# dir cd to dir and push dir onto the cd directory stack +# +# If the new directory is a directory in the stack and the options selected +# it (-n, -s), the new working directory is printed +# +# If the variable CDHISTFILE is set, the cd directory stack is loaded from +# and written to $CDHISTFILE every time `cd' is executed. +# +# Note: I got this off the net somewhere; I don't know the original author +# +# Chet Ramey +# chet@po.cwru.edu + +_cd_print() +{ + echo -e "$@" +} + +cd() +{ + typeset -i cdlen i + typeset t + + if [ $# -eq 0 ] + then + set -- $HOME + fi + + if [ "$CDHISTFILE" ] && [ -r "$CDHISTFILE" ] # if directory history exists + then + typeset CDHIST + i=-1 + while read -r t # read directory history file + do + CDHIST[i=i+1]=$t + done <$CDHISTFILE + fi + + if [ "${CDHIST[0]}" != "$PWD" ] && [ -n "$PWD" ] + then + _cdins # insert $PWD into cd history + fi + + cdlen=${#CDHIST[*]} # number of elements in history + + case "$@" in + -) # cd to new dir + if [ "$OLDPWD" = "" ] && ((cdlen>1)) + then + '_cdprint' ${CDHIST[1]} + builtin cd ${CDHIST[1]} + pwd + else + builtin cd "$@" + # pwd + fi + ;; + -l) # _cdprint directory list + ((i=cdlen)) + while (((i=i-1)>=0)) + do + num=$i + '_cdprint' "$num ${CDHIST[i]}" + done + return + ;; + -[0-9]|-[0-9][0-9]) # cd to dir in list + if (((i=${1#-})<cdlen)) + then + '_cdprint' ${CDHIST[i]} + builtin cd ${CDHIST[i]} + pwd + else + builtin cd $@ + # pwd + fi + ;; + -*) # cd to matched dir in list + t=${1#-} + i=1 + while ((i<cdlen)) + do + case ${CDHIST[i]} in + *$t*) + '_cdprint' ${CDHIST[i]} + builtin cd ${CDHIST[i]} + pwd + break + ;; + esac + ((i=i+1)) + done + if ((i>=cdlen)) + then + builtin cd $@ + # pwd + fi + ;; + *) # cd to new dir + builtin cd $@ + # pwd + ;; + esac + + _cdins # insert $PWD into cd history + + if [ "$CDHISTFILE" ] + then + cdlen=${#CDHIST[*]} # number of elements in history + + i=0 + while ((i<cdlen)) + do + echo ${CDHIST[i]} # update directory history + ((i=i+1)) + done >$CDHISTFILE + fi +} + +_cdins() # insert $PWD into cd history +{ # meant to be called only by cd + typeset -i i + + i=0 + + while (( i < ${#CDHIST[*]} )) # see if dir is already in list + do + if [ "${CDHIST[$i]}" = "$PWD" ] + then + break + fi + ((i=i+1)) + done + + if (( i>22 )) # limit max size of list + then + i=22 + fi + + while (((i=i-1)>=0)) # bump old dirs in list + do + CDHIST[i+1]=${CDHIST[i]} + done + + CDHIST[0]=$PWD # insert new directory in list +} + +# examples +shopt -s expand_aliases + +# go to known place before doing anything +cd / + +echo CDHIST: "${CDHIST[@]}" +for dir in /tmp /bin - -2 -l +do + cd $dir + echo CDHIST: "${CDHIST[@]}" + echo PWD: $PWD + +done + +exit 0 diff --git a/examples/scripts.v2/corename b/examples/scripts.v2/corename new file mode 100644 index 0000000..2b51e5d --- /dev/null +++ b/examples/scripts.v2/corename @@ -0,0 +1,43 @@ +#! /bin/bash +# +# original from: +# @(#) corename.ksh 1.0 93/04/01 +# 92/11/11 john h. dubois iii (john@armory.com) +# 92/02/16 Added help option. +# 92/02/22 Added cd to origdir to fix prob w/multiple relative paths. +# 93/04/01 Added check for whether file exists. +# +# conversion to bash v2 syntax done by Chet Ramey + +# inspired by belal's equivalent utility + +if [ "$1" = -h ]; then + echo \ +"$0: print the names of executables that dumped core. +Usage: $0 [corename ...] +If no corename is given, \"core\" is assumed." + exit 0 +fi + +[ $# = 0 ] && set core +origdir=$PWD +for i; do + cd $origdir + file=${i##*/} + dir=${i%$file} + [ -z "$dir" ] && dir=$origdir/ + if [ ! -f $dir$file ]; then + echo "$dir$file: No such file." + continue + fi + if [ ! -r $dir$file ]; then + echo "$dir$file: Cannot open." + continue + fi + cd $dir + + # the adb output syntax is highly variable. this works on SunOS 4.x + set -- $(adb $file < /dev/null 2>&1 | sed 1q) + name=${7#??} + echo "$i: ${name%??}" +done diff --git a/examples/scripts.v2/fman b/examples/scripts.v2/fman new file mode 100644 index 0000000..1e94d21 --- /dev/null +++ b/examples/scripts.v2/fman @@ -0,0 +1,281 @@ +#! /bin/bash +# +# original from: +# fman: new man program +# @(#) fman.ksh 1.5 94/04/16 +# 91/07/03 john h. dubois iii (john@armory.com) +# 91/07/11 made it unpack man pages if neccessary +# 91/07/16 fixed test for whether man file pattern was expanded +# 92/01/21 made it read /etc/default/man to get section order, +# and only display the first section found. +# 92/02/06 changed name to fman +# 92/02/07 fixed bug in notfound +# 92/02/13 incorporated changes from DOS version +# 92/03/11 changed to use MANPATH from environment if set, +# and search all directories given in MANPATH +# 92/03/15 exec pager or man w/o forking +# 92/05/31 try using index if one exists +# 92/10/01 Added "See also <other sections>" +# 92/10/18 If PAGER is less, search for name of man page to make it easier +# to find information in man pages for multiple items +# 92/11/11 Make it work for compressed files not listed in index; +# deal with man pages listed in index that don't exist. +# 93/03/30 Fixed bug in MANPATH processing +# 93/06/17 Include paths in "See also:" message if they would be needed +# to get to a man page. Allow MANPATH spec on command line. +# 93/07/09 Added -h and -e options. +# 94/04/16 Added x option. +# +# conversion to bash v2 syntax done by Chet Ramey + +istrue() +{ + test 0 -ne "$1" +} + +isfalse() +{ + test 0 -eq "$1" +} + +# Finds all sections that man page $1 is in and puts them in the the +# global array Sections[]. +# The filename of each page is put in FileNames[] with the same index. +# Global vars used: +# patharr[] MANPATH directories. + +FindSectionsInIndex () +{ + typeset index indexes section mpath page=$1 + typeset -i i=0 NIndex=0 + + for mpath in "${patharr[@]}"; do + if [ -r $mpath/index ]; then + indexes="$indexes $mpath/index" + let NIndex+=1 + fi + done + [ -z "$indexes" ] && return + # Make grep give filename + [ NIndex -lt 2 ] && indexes="$indexes /dev/null" + # set positional parameters to + # indexfile:searchname pagename section ... + # e.g. + # /usr/man/index:FP_OFF Routines DOS + set -- `grep "^$page[ ]" $indexes` + while [ $# -gt 2 ]; do + FileNames[i]=${1%%index*}cat$3/$2.$3 + Sections[i]=$3 + shift 3 + let i+=1 + done +} + +# Finds all sections that man page $1 is in by searching each man directory +# in the order given in patharr[], +# and puts them in the the global array Sections[]. +# The filename of each page is put in FileNames[] with the same index. +# Global vars used: +# patharr[] MANPATH directories. +FindSectionsInDirs () +{ + local page=$1 mpath AllPaths Path + typeset -i i + + for mpath in "${patharr[@]}"; do + AllPaths="$AllPaths $mpath/cat[0-9]*/$page.* $mpath/man[0-9]*/$page.*" + done + + i=0 + for Path in $AllPaths; do + istrue $debug && echo Path = $Path + case "$Path" in + *\*) ;; + *) + # Remove compressed-file suffix to make FileNames be the same + # as it is when built by FindSectionsInIndex() + FileNames[i]=${Path%.[zZ]} + Path=${Path%/*} + Sections[i]=${Path##*/*.} + let i+=1 ;; + esac + done +} + +# FindSection: display man page. +# Uses ordarr[] (built from $ORDER) to display the version of the man +# page that occurs first in $ORDER. +# Sections[] gives the sections that a man page was found in. +# If the global variable "exist" is set to 1, nothing is displayed; +# the function instead returns zero if a page is found, nonzero if not. +# The filename of each page is in FileNames[] with the same index. +# Global vars used: +# Sections[], FileNames[], ordarr[] +FindSection () +{ + typeset -i NumPages i foundsec + local section OtherSec filename NPAGER=$PAGER POpt page=$1 Pat + local PageFile + + NumPages=${#Sections[*]} # Number of versions of man page found. + isfalse $NumPages && return 1 + case "$PAGER" in + *less) Popt="-p$page" ;; + esac + + # For each section in ORDER, determine if any man page was found in + # that section + for section in "${ordarr[@]}"; do + i=0 + foundsec=0 + while [ $i -lt $NumPages ]; do + if [ "${Sections[i]}" = $section ]; then + # Found a man page from this section of ORDER + filename=${FileNames[i]} + if [ -z "$PageFile" ]; then + PageFile=$filename + else + if istrue $foundsec; then + OtherSec="$OtherSec$page(${filename%/*/*} $section) " + else + OtherSec="$OtherSec$page($section) " + fi + fi + foundsec=1 + istrue $exist && return + fi + let i+=1 + done + done + # No pages with the specified section found. + [ -z "$PageFile" ] && return 1 + # Return if all we want to know is whether the man page exists. + [ "$exist" = 1 ] && return 0 + if [ -z "$OtherSec" ]; then + NPAGER="exec $PAGER" + fi + if [ -r $PageFile ]; then + $NPAGER $POpt $PageFile + elif [ -r $PageFile.z ]; then + pcat $PageFile.z | $NPAGER $POpt + elif [ -r $PageFile.Z ]; then + zcat $PageFile.Z | $NPAGER $POpt + elif [ -f $PageFile.gz ]; then + gzip -dc $PageFile.gz | $NPAGER $POpt + else + echo "$PageFile: cannot open." 1>&2 + OtherSec= + unset Sections[i] + let i+=1 + continue + fi + echo "See also $OtherSec" + exit 0 +} + +phelp() +{ +echo "$name: print man pages. +$name locates and prints the specified manual pages from the online UNIX +documentation. +$Usage +Options: +-e: Determine whether the specified man page exists. Nothing is printed; + $0 exits with a zero status if the page exists and a nonzero status if + it does not. +-h: Print this help." +} + +# main program + +typeset -i exist=0 debug=0 + +name=${0##*/} +Usage="Usage: $name [-eh] [[manpath] section] command-name" + +while getopts :hex opt; do + case $opt in + h) phelp; exit 0;; + e) exist=1 ;; + x) debug=1 ;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;; + ?) + echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2 ;; + esac +done + +# remove args that were options +shift $((OPTIND-1)) + +if [ $# -lt 1 ]; then + echo -e "$Usage\nUse -h for help." 1>&2 + exit +fi + +P=$PAGER +O=1:n:l:6:8:2:3:4:5:7:p:o +T=$TERM +M=${MANPATH:-/usr/local/man:/usr/man} +[ -f /etc/default/man ] && . /etc/default/man +[ -n "$P" ] && PAGER=$P +[ -n "$O" ] && ORDER=$O +[ -n "$T" ] && TERM=$T +[ -n "$M" ] && MANPATH=$M + +case $# in +0) echo "No man page specified." ; exit 1;; +1) page=$1;; +2) ORDER=$(echo $1 | tr a-z A-Z) ; page=$2;; +3) MANPATH=$1 + [ -n "$2" ] && ORDER=$(echo $2 | tr a-z A-Z) + page=$3;; +*) echo "Too many arguments."; exit 1;; +esac + +aargs=("$@") +[ ! -t 0 ] && PAGER=cat + +OIFS=$IFS +IFS=: +patharr=($MANPATH) +i=0 +for d in $MANPATH; do + for sec in $ORDER; do + ordarr[i]=$d/cat${sec} + let i+=1 + ordarr[i]=$d/man${sec} + let i+=1 + done +done +IFS=$OIFS + +istrue $debug && echo patharr = "${patharr[@]}" + +# if less or more is being used, remove multiple blank lines +export LESS="-s $LESS" +export MORE="-s $MORE" + +# Try using index +FindSectionsInIndex "$page" +# Exit 0 if a page was found and we're just testing for existence. +FindSection "$page" && exit 0 + +# Try searching directories +unset Sections[*] +FindSectionsInDirs "$page" +FindSection "$page" && exit 0 + +istrue $exist && exit 1 + +# Try using man +# If using more or less, make man run faster by letting more or less compress +# multiple blank lines instead of rmb +#case "$PAGER" in +#*more|*less) manopt=-b;; +#esac + +#cmd=(man $manopt -p$PAGER "${aargs[@]}") +export PAGER +cmd=(man $manopt "${aargs[@]}") +istrue $debug && echo "$name: running ${cmd[*]}" 1>&2 +exec "${cmd[@]}" diff --git a/examples/scripts.v2/frcp b/examples/scripts.v2/frcp new file mode 100755 index 0000000..572aa7b --- /dev/null +++ b/examples/scripts.v2/frcp @@ -0,0 +1,288 @@ +#! /bin/bash +# +# original from: +# +# @(#) frcp.ksh 2.2 93/11/14 +# 92/06/29 john h. dubois iii (john@armory.com) +# 92/10/14 Cleaned up, improved, added -d and -r options +# 92/11/11 Made work with a dest of '.' +# 93/07/09 Added -l and -n options, & login as anonymous if no .netrc entry +# 93/11/14 Use either passwd or password in .netrc, since ftp does. +# +# conversion to bash v2 syntax by Chet Ramey +# +# frcp: ftp front end with rcp-like syntax. +# Note: requires any machine names given to be listed with +# user and password in .netrc. If not, anonymous FTP is +# done. +# +# full path to ftp binary +if [ -x /usr/bin/ftp ]; then + FTP=/usr/bin/ftp; +elif [ -x /usr/ucb/ftp ]; then + FTP=/usr/ucb/ftp +else + FTP=ftp +fi + +istrue() +{ + test 0 -ne "$1" +} +isfalse() +{ + test 0 -eq "$1" +} + +# For each filename given, put the filename in filename[n] +# and the machine it is on in machine[n]. +function SplitNames { + typeset file + typeset -i i=1 + + unset filename[*] machine[*] + for file; do + case "$file" in + *:*) machine[i]=${file%%:*} ;; + *) machine[i]=$LocalMach ;; + esac + filename[i]=${file#*:} + let i+=1 + done +} + +function verboseprint { + echo "$@" + echo "$@" 1>&2 +} + +function MakeDir { + OFS=$IFS + local IFS=/ dir component + + case "$1" in + /*) ;; + *) dir=. + esac + set -- $1 + IFS=$OFS + for component; do + dir=$dir/$component + if [ ! -d "$dir" ]; then + if mkdir "$dir"; then :; else + echo "Could not make directory $dir." >&2 + return 1 + fi + fi + done + return 0 +} + +lastisdot () +{ + case "$1" in + */.|*/..) return 0;; + *) return 1;; + esac +} + +# CopyFiles: issue ftp(TC) commands to copy files. +# Usage: CopyFiles [sourcemachine:]sourcepath ... [destmachine:]destpath +# Global vars: +# Uses LocalMach (should be name of local machine) +# Sets global arrs machine[]/filename[] +function CopyFiles { + unset machine[*] filename[*] + + SplitNames "$@" # split names into filename[1..n] and machine[1..n] + + local DestMach=${machine[$#]} # Machine to copy files to + local DestPath=${filename[$#]} # Destination file/dir + + unset machine[$#] filename[$#] + + [ -z "$DestPath" ] && DestPath=. # dest was given as machine: + + # Try to determine if destination should be a directory + # so that it can be forced to be a directory. + + case "$DestPath" in + */) ;; # don't add / if trailing / already present + *) if [ $# -gt 2 ] || # if more than two args given, last must be a dir + # If dest in on local machine, check whether it is a directory + [ $DestMach = $LocalMach ] && [ -d "$DestPath" ] || + # If dest ends with . or .., it is a directory + lastisdot "$DestPath" + then + DestPath=$DestPath/ + fi ;; + esac + + # If one of the above tests made us think dest is a directory, + # but it isn't, complain + case "$DestPath" in + */) if [ "$DestMach" = "$LocalMach" ] && [ ! -d "$DestPath" ]; then + echo "Destination is not a directory." 1>&2 + exit 1 + fi ;; + esac + + DoCopy "$DestMach" "$DestPath" +} + +# Usage: OpenMachine machine-name +# Emits login sequence or doesn't, depending on .netrc file and global +# variables anon and noanon +OpenMachine () +{ + local machine=$1 netrc=$HOME/.netrc user= password= + + if isfalse $anon && [ -r $netrc ]; then + set -- $(gawk ' + /machine (.* )?'"$machine"'($| )/,/^ *$/ { + Fields[$1] = $2 + if ("passwd" in Fields) + Fields["password"] = Fields["passwd"] + if ("login" in Fields && "password" in Fields) { + print Fields["login"] " " Fields["password"] + exit + } + } + ' $netrc ) + user=$1 + password=$2 + fi + if [ -z "$password" ]; then + if istrue $noanon; then + echo "No .netrc entry for machine $machine" 1>&2 + exit 1 + fi + user=anonymous + password=$USER@$LocalMach + fi + verboseprint open $machine + echo user $user "*******" 1>&2 + echo user $user $password +} + +# Usage: DoCopy destination-machine destination-path +# Copies the files in global arrs machine[]/filename[] to the given dest +# Global vars: +# Uses machine[], filename[], LocalMach, check +DoCopy () +{ + local DestMach=$1 + local DestPath=$2 + local OpenMach # Machine that connection is currently open to + local OWD=$PWD SourceMach SourceFile + local FileName + typeset -i i=1 + + while [ $i -le ${#machine[*]} ]; do + istrue $check && verboseprint "runique" + + SourceMach=${machine[i]} + SourceFile=${filename[i]} + + DestFile=$DestPath + # if DestPath is a dir, + # add source filename to it without source path + case "$DestFile" in + */) DestFile=$DestFile${SourceFile##*/} ;; + esac + + if [ $SourceMach = $LocalMach ]; then + if [ $DestMach != "$OpenMach" ]; then + OpenMachine $DestMach + OpenMach=$DestMach + fi + verboseprint put $SourceFile $DestFile + elif [ $DestMach = $LocalMach ]; then + if istrue $check && [ -f "$DestFile" ]; then + echo "$DestFile already exists." 1>&2 + continue + fi + # If destination is on local machine, + # the dest will be a full dir/filename + if istrue $createdirs; then + MakeDir "${DestFile%/*}" || continue + fi + if [ $SourceMach != "$OpenMach" ]; then + OpenMachine $SourceMach + OpenMach=$SourceMach + fi + # If source filename has wildcards ([, ], *, ?) do an mget + case "$SourceFile" in + \[*\]|*\**|*\?*) + verboseprint lcd "$DestFile" + verboseprint mget "$SourceFile" + verboseprint lcd $OWD ;; + *) verboseprint get "$SourceFile" "$DestFile" ;; + esac + else + echo "Neither source machine \"$SourceMach\" "\ +"nor destination machine \"$DestMach\" is local." 1>&2 + fi + let i+=1 + done +} + +# Start of main program +name=${0##*/} + +if [ "$1" = -h ]; then + echo \ +"$name: do ftp transfers using rcp-style parameters. +Usage: $name <source> <destpath> or $name <source> [<source> ...] <destdir> +At least one of <source> and <destpath> must be the local system. +A remote filename is given as machinename:filename +If remote filenames contain wildcards, they will be globbed on the remote +machine. Make sure they are quoted when $name is invoked. +If the invoking user's .netrc file (see ftp(TC)) contains an entry for the +remote system with a login and password supplied, $name will log in using +the given login and password. If not, $name will login in as user +anonymous and with the user@localsystem as the password. +Options: +-c: check: do not overwrite files. +-d: create directories as needed. +-f: force: overwrite files (default). +-h: print this help. +-l: fail if there is no entry with login and password for the remote system, + instead of logging in as anonymous. +-n: log in as anonymous even if there is an entry for the remote system in + the user's .netrc file. +-r: read source/dest filename pairs from the standard input, + one pair per line, and copy files accordingly." + exit 0 +fi + +typeset -i check=0 createdirs=0 readinput=0 anon=0 noanon=0 + +while getopts :cdflnr Option +do + case "$Option" in + c) check=1;; + d) createdirs=1;; + f) check=0;; + l) noanon=1;; + n) anon=1;; + r) readinput=1;; + \?) echo "$OPTARG: invalid option."; exit 1;; + esac +done + +shift $((OPTIND-1)) + +LocalMach=`hostname` + +if istrue $readinput; then + while read line; do + CopyFiles $line + done | $FTP -nv +else + if [ $# -lt 2 ]; then + echo "$name: Not enough arguments. Use -h for help." 1>&2 + exit + fi + CopyFiles "$@" | $FTP -nv +fi diff --git a/examples/scripts.v2/lowercase b/examples/scripts.v2/lowercase new file mode 100644 index 0000000..fd2ec5d --- /dev/null +++ b/examples/scripts.v2/lowercase @@ -0,0 +1,44 @@ +#! /bin/bash +# +# original from +# @(#) lowercase.ksh 1.0 92/10/08 +# 92/10/08 john h. dubois iii (john@armory.com) +# +# conversion to bash v2 syntax done by Chet Ramey + +Usage="Usage: $name file ..." +phelp() +{ +echo "$name: change filenames to lower case. +$Usage +Each file is moved to a name with the same directory component, if any, +and with a filename component that is the same as the original but with +any upper case letters changed to lower case." +} + +name=${0##*/} + +while getopts "h" opt; do + case "$opt" in + h) phelp; exit 0;; + *) echo "$Usage" 1>&2; exit 2;; + esac +done + +shift $((OPTIND - 1)) + +for file; do + filename=${file##*/} + case "$file" in + */*) dirname=${file%/*} ;; + *) dirname=. ;; + esac + nf=$(echo $filename | tr A-Z a-z) + newname="${dirname}/${nf}" + if [ "$nf" != "$filename" ]; then + mv "$file" "$newname" + echo "$0: $file -> $newname" + else + echo "$0: $file not changed." + fi +done diff --git a/examples/scripts.v2/ncp b/examples/scripts.v2/ncp new file mode 100644 index 0000000..c91ba64 --- /dev/null +++ b/examples/scripts.v2/ncp @@ -0,0 +1,187 @@ +#! /bin/bash +# +# original from: +# @(#) ncp.ksh,nmv.ksh 1.1 94/07/23 +# 92/01/18 john h. dubois iii (john@armory.com) +# 92/01/31 added check for no args left after shifts +# 92/02/17 added help +# 92/02/25 remove path component from filename before tacking it onto dest. +# 92/03/15 exec mv or cp +# 93/07/13 Added -i +# 93/09/29 Made abort if file exists optional. +# 93/11/19 Exit before invoking mv if no files to move +# 94/01/03 Added o option +# 94/04/13 Added x option. +# Fixed appending of source filename, broken by earlier change. +# 94/07/23 Append only the filename part of the source path. +# +# conversion to bash v2 syntax done by Chet Ramey + +false() +{ + return 1 +} + +true() +{ + return 0 +} + +phelp() +{ +echo "$name: do a $cmd with extra checking and options. +$Usage +$name is used as a front end for $cmd to get the [icfo] options, and so +that a trailing / will force the last component of the path to be +interpreted as a directory, so that $name foo bar/ will fail if bar is +not an existing directory, instead of changing the name of foo to bar. +Effectively, $name foo bar/ is short for $name foo bar/foo +Options: +-h prints this help. +-c checks first for the existence of each file, and fails if it exists. +-i is like -c except that if the file exists and stdin and stdout are a + tty, a query is printed and a reply is read; a file is overwritten only + if the reply begins with 'y'. +-f unsets -c and -i (in case $cmd is aliased to $name). +-o (overwrite only) checks that the named file(s) exist and fails for any + that do not. It is the complement of the -c option. +Whichever of [cifo] comes later on the command line determines the behaviour. +Any of these options must come before any standard $cmd options." +} + +# interactive: Attempt to overwrite file should result in interactive +# query rather than automatic failure. +# noover: Do not overwrite files (if interactive is true, query, else fail) +# overwrite: Only overwriting is allowed, not creation of new files. +# debug: Print debugging info. +typeset interactive=false noover=false overwrite=false debug=false +name=${0##*/} + +case "$name" in +ncp|nmv) cmd=/bin/${name#?} ;; +*) echo "$name: Must be invoked as ncp or nmv." 1>&2 ; exit 2;; +esac + +Usage="Usage: $name [-cfhio] $cmd-cmd-line" + +while getopts :cfhiox opt; do + case $opt in + h) phelp; exit 0;; + x) debug=true ;; + c) noover=true ;; + i) noover=true ; interactive=true ;; + f) noover=false ; interactive=false ;; + o) overwrite=true ; noover=false ; interactive=false;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;; + ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2;; + esac +done + +# remove args that were options +shift $((OPTIND - 1)) + +if [ $# -lt 2 ]; then + echo -e "$Usage\nUse -h for help." + exit +fi + +Check() +{ + if [ ! -f "$1" ] && $overwrite; then + echo "$name: $1: File does not exist." 1>&2 + return 1 + elif [ -f "$1" ] && $noover; then + if [ $interactive = false ] || [ ! -t 0 ] || [ ! -t 1 ]; then + echo "$name: $1: File exists." 1>&2 + return 1 + else + while :; do + echo -n \ +"$name: $1: File exists. Overwrite? (y)es/(n)o/(a)bort/(Y)es for all: " 1>&2 + read reply + case "$reply" in + y*) + echo "$name: Overwriting $1." + return 0 + ;; + Y*) + echo "$name: Overwriting $1." + interactive=false + noover=false + return 0 + ;; + [nN]*) + echo "$name: Skipping $2." + return 1 + ;; + [aA]*) + echo "$name: Aborting." + exit 1 + ;; + *) + echo "$name: Invalid response." 1>&2 + ;; + esac + done + fi + else + return 0 + fi +} + +# i is the index of the filename being examined +# lastarg is the index of the last filename before the dest directory name +typeset -i i=0 lastarg=$(($#-1)) + +# Sets argv[0..$#-1] +argv=("$@") +$debug && echo argv = "${argv[@]}" 1>&2 +dest=${argv[lastarg]} + +if $debug; then + echo \ +"interactive=$interactive noover=$noover overwrite=$overwrite debug=$debug +lastarg=$lastarg dest=$dest name=$name cmd=$cmd +files=$*" 1>&2 +fi + +if $noover || $overwrite; then + $debug && echo "checking for existance of directories..." 1>&2 + # If the destination is not intended to be a directory... + if [ $# -eq 2 ] && [ ! -d "$dest" ]; then + Check "$dest" "$1" || exit 0 # No files to copy + else + while [ $i -lt $lastarg ]; do + Check "$dest/${argv[i]##*/}" "${argv[i]}" || unset argv[i] + let i+=1 + done + fi +fi + +[ ${#argv[@]} -lt 2 ] && exit 0 + +# If only 2 args are given, mv/cp will not insist that the destination +# be a directory, which we want if the destination ends in "/" or if +# the original number of args was >2. +# $# is still the original number of args. +# Tack the file name onto the destination to force this behaviour. + +lastisslash() +{ + case "$1" in + */) return 0;; + *) return 1;; + esac +} + +if [ ${#argv[@]} = 2 ] && { lastisslash "$2" || [ $# -gt 2 ]; }; then + $debug && echo "Appending filename." 1>&2 + # Don't know which element of argv[] holds the source filename, + # since may have started with more than 1 source file & had some unset. + # So, compact args to make it easy to find the set one. + argv=("${argv[@]}") + argv[1]="${argv[1]}/${argv[0]##*/}" +fi + +$debug && echo "Executing command: $cmd ${argv[@]}" 1>&2 +exec $cmd "${argv[@]}" diff --git a/examples/scripts.v2/newext b/examples/scripts.v2/newext new file mode 100644 index 0000000..37645bd --- /dev/null +++ b/examples/scripts.v2/newext @@ -0,0 +1,64 @@ +#! /bin/bash +# +# original from: +# newext: change filename extension +# @(#) newext.sh 1.1 93/04/13 +# 90/06/06 john h. dubois iii (john@armory.com) +# 90/11/14 changed ksh-specific code to hybrid: if running under Bourne, +# uses expr instead of ksh builtin ops. Removed SYSV specific code. +# 91/08/06 added -t option +# 92/11/06 made earlier code actually work! +# 93/04/13 If no filenames given, act on files in current dir +# +# conversion to bash v2 syntax by Chet Ramey + +usage="Usage: newext [-th] <oldext> <newext> [filename ...]" + +phelp() +{ +echo "$usage +Rename all given files that end in oldext with newext replacing oldext. +If no filenames are given, all files in the current directory that end +in oldext are acted on (no filename is equivalent to '*'). +Options: +-h: Print this help. +-t: Test: No action is taken except to print the mv commands that would +be executed if -t was not given." +} + +while getopts "th" opt; do + case "$opt" in + t) echo=echo;; + h) phelp; exit 0;; + *) echo "$usage" 1>&2; exit 2;; + esac +done + +shift $((OPTIND - 1)) + +oldext=$1 +newext=$2 + +case $# in +[01]) echo -e "$usage\nUse -h for help." 1>&2; exit 2;; +2) shift ; shift; set -- *;; +*) shift ; shift;; +esac + +found= + +for file +do + case "$file" in + *$oldext) + newname="${file%$oldext}$newext" + $echo mv "$file" "$newname" + found=true;; + esac +done + +if [ -z "$found" ]; then + echo "No files ending in \"$oldext\"." + exit 1 +fi +exit 0 diff --git a/examples/scripts.v2/nmv b/examples/scripts.v2/nmv new file mode 100644 index 0000000..c91ba64 --- /dev/null +++ b/examples/scripts.v2/nmv @@ -0,0 +1,187 @@ +#! /bin/bash +# +# original from: +# @(#) ncp.ksh,nmv.ksh 1.1 94/07/23 +# 92/01/18 john h. dubois iii (john@armory.com) +# 92/01/31 added check for no args left after shifts +# 92/02/17 added help +# 92/02/25 remove path component from filename before tacking it onto dest. +# 92/03/15 exec mv or cp +# 93/07/13 Added -i +# 93/09/29 Made abort if file exists optional. +# 93/11/19 Exit before invoking mv if no files to move +# 94/01/03 Added o option +# 94/04/13 Added x option. +# Fixed appending of source filename, broken by earlier change. +# 94/07/23 Append only the filename part of the source path. +# +# conversion to bash v2 syntax done by Chet Ramey + +false() +{ + return 1 +} + +true() +{ + return 0 +} + +phelp() +{ +echo "$name: do a $cmd with extra checking and options. +$Usage +$name is used as a front end for $cmd to get the [icfo] options, and so +that a trailing / will force the last component of the path to be +interpreted as a directory, so that $name foo bar/ will fail if bar is +not an existing directory, instead of changing the name of foo to bar. +Effectively, $name foo bar/ is short for $name foo bar/foo +Options: +-h prints this help. +-c checks first for the existence of each file, and fails if it exists. +-i is like -c except that if the file exists and stdin and stdout are a + tty, a query is printed and a reply is read; a file is overwritten only + if the reply begins with 'y'. +-f unsets -c and -i (in case $cmd is aliased to $name). +-o (overwrite only) checks that the named file(s) exist and fails for any + that do not. It is the complement of the -c option. +Whichever of [cifo] comes later on the command line determines the behaviour. +Any of these options must come before any standard $cmd options." +} + +# interactive: Attempt to overwrite file should result in interactive +# query rather than automatic failure. +# noover: Do not overwrite files (if interactive is true, query, else fail) +# overwrite: Only overwriting is allowed, not creation of new files. +# debug: Print debugging info. +typeset interactive=false noover=false overwrite=false debug=false +name=${0##*/} + +case "$name" in +ncp|nmv) cmd=/bin/${name#?} ;; +*) echo "$name: Must be invoked as ncp or nmv." 1>&2 ; exit 2;; +esac + +Usage="Usage: $name [-cfhio] $cmd-cmd-line" + +while getopts :cfhiox opt; do + case $opt in + h) phelp; exit 0;; + x) debug=true ;; + c) noover=true ;; + i) noover=true ; interactive=true ;; + f) noover=false ; interactive=false ;; + o) overwrite=true ; noover=false ; interactive=false;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;; + ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2;; + esac +done + +# remove args that were options +shift $((OPTIND - 1)) + +if [ $# -lt 2 ]; then + echo -e "$Usage\nUse -h for help." + exit +fi + +Check() +{ + if [ ! -f "$1" ] && $overwrite; then + echo "$name: $1: File does not exist." 1>&2 + return 1 + elif [ -f "$1" ] && $noover; then + if [ $interactive = false ] || [ ! -t 0 ] || [ ! -t 1 ]; then + echo "$name: $1: File exists." 1>&2 + return 1 + else + while :; do + echo -n \ +"$name: $1: File exists. Overwrite? (y)es/(n)o/(a)bort/(Y)es for all: " 1>&2 + read reply + case "$reply" in + y*) + echo "$name: Overwriting $1." + return 0 + ;; + Y*) + echo "$name: Overwriting $1." + interactive=false + noover=false + return 0 + ;; + [nN]*) + echo "$name: Skipping $2." + return 1 + ;; + [aA]*) + echo "$name: Aborting." + exit 1 + ;; + *) + echo "$name: Invalid response." 1>&2 + ;; + esac + done + fi + else + return 0 + fi +} + +# i is the index of the filename being examined +# lastarg is the index of the last filename before the dest directory name +typeset -i i=0 lastarg=$(($#-1)) + +# Sets argv[0..$#-1] +argv=("$@") +$debug && echo argv = "${argv[@]}" 1>&2 +dest=${argv[lastarg]} + +if $debug; then + echo \ +"interactive=$interactive noover=$noover overwrite=$overwrite debug=$debug +lastarg=$lastarg dest=$dest name=$name cmd=$cmd +files=$*" 1>&2 +fi + +if $noover || $overwrite; then + $debug && echo "checking for existance of directories..." 1>&2 + # If the destination is not intended to be a directory... + if [ $# -eq 2 ] && [ ! -d "$dest" ]; then + Check "$dest" "$1" || exit 0 # No files to copy + else + while [ $i -lt $lastarg ]; do + Check "$dest/${argv[i]##*/}" "${argv[i]}" || unset argv[i] + let i+=1 + done + fi +fi + +[ ${#argv[@]} -lt 2 ] && exit 0 + +# If only 2 args are given, mv/cp will not insist that the destination +# be a directory, which we want if the destination ends in "/" or if +# the original number of args was >2. +# $# is still the original number of args. +# Tack the file name onto the destination to force this behaviour. + +lastisslash() +{ + case "$1" in + */) return 0;; + *) return 1;; + esac +} + +if [ ${#argv[@]} = 2 ] && { lastisslash "$2" || [ $# -gt 2 ]; }; then + $debug && echo "Appending filename." 1>&2 + # Don't know which element of argv[] holds the source filename, + # since may have started with more than 1 source file & had some unset. + # So, compact args to make it easy to find the set one. + argv=("${argv[@]}") + argv[1]="${argv[1]}/${argv[0]##*/}" +fi + +$debug && echo "Executing command: $cmd ${argv[@]}" 1>&2 +exec $cmd "${argv[@]}" diff --git a/examples/scripts.v2/pages b/examples/scripts.v2/pages new file mode 100644 index 0000000..66ebc5f --- /dev/null +++ b/examples/scripts.v2/pages @@ -0,0 +1,187 @@ +#! /bin/bash +# +# original from: +# @(#) pages.sh 1.0 92/09/26 +# 92/09/05 John H. DuBois III (jhdiii@armory.com) +# 92/09/26 Added help +# +# conversion to bash v2 syntax by Chet Ramey + +Usage="$0 [-h] [-n lines/page] page-ranges [file ...]" + +usage() +{ + echo "$Usage" 1>&2 +} + +phelp() +{ +echo "$0: print selected pages. +Usage: $Usage + +If no file names are given, the standard input is read. + +The input is grouped into pages and a selected subset of them is printed. +Formfeeds are acted on correctly. + +If the output device does automatic line wrap, lines that longer than +the width of the output device will result in incorrect output. +The first non-option argument is a list of pages to print. + +Pages are given as a list of ranges separated by commas. +A range is either one number, two numbers separted by a dash, +or one number followed by a dash. A range consisting of one +number followed by a dash extends to the end of the document. + +Options: +-n sets the number of lines per page to n. The default is 66." +} + +while getopts "n:h" opt; do + case "$opt" in + n) LinesPerPage=$OPTARG;; + h) phelp; exit 0;; + *) usage; exit 2;; + esac +done + +shift $(($OPTIND - 1)) + +if [ $# -eq 0 ]; then + echo $0: no page ranges given. 1>&2 + usage + exit 1 +fi + +PageList=$1 +shift + +gawk " +BEGIN { + PageList = \"$PageList\"; LinesPerPage = \"$LinesPerPage\""' + if (LinesPerPage == "") + LinesPerPage = 66 + else + if (LinesPerPage !~ "[1-9][0-9]*") + ErrExit("Bad value for lines per page: " LinesPerPage) + LinesPerPage += 0 + NumRanges = split(PageList,Ranges,",") + for (i = 1; i <= NumRanges; i++) { + if ((StartRange = EndRange = Ranges[i]) !~ "^[0-9]+(-([0-9]+)?)?$") + ErrExit("Bad range \"" StartRange "\"") + sub("-.*","",StartRange) + sub(".*-","",EndRange) + if (EndRange == "") + EndRange = 2 ^ 30 + # Force StartRange and EndRange to be numeric values + if ((StartRange += 0) == 0 || (EndRange += 0) == 0) + ErrExit("Invalid page number \"0\" in range " Ranges[i]) + if (StartRange > EndRange) + ErrExit("Start page comes after end page in range " Ranges[i]) + TmpRangeStarts[i] = StartRange + TmpRangeEnds[i] = EndRange + } + + # Sort ranges + qsort(TmpRangeStarts,k) + RangeEnds[0] = 0 + for (i = 1; i <= NumRanges; i++) { + RangeEnds[i] = TmpRangeEnds[k[i]] + if ((RangeStarts[i] = TmpRangeStarts[k[i]]) <= RangeEnds[i - 1]) + ErrExit("Overlapping ranges: " Ranges[k[i]] "," Ranges[k[i - 1]]) + } + + RangeNum = LineNum = PageNum = 1 + InRange = In(PageNum,RangeStarts[RangeNum],RangeEnds[RangeNum]) + FS = "\014" +} + +{ + if (LineNum > LinesPerPage) + NewPage() + if (InRange) + printf "%s",$1 + # Deal with formfeeds + for (i = 2; i <= NF; i++) { + if (InRange) + printf "\014" + NewPage() + if (InRange) + printf "%s",$i + } + if (InRange) + print "" + LineNum++ +} + +function NewPage() { + PageNum++ + LineNum = 1 + # At the start of each page, check whether we are in a print range + WereInRange = InRange + InRange = In(PageNum,RangeStarts[RangeNum],RangeEnds[RangeNum]) + # If last page was in range and we no longer are, move to next range + if (WereInRange && !InRange && ++RangeNum > NumRanges) + exit +} + +function In(a,Min,Max) { + return (Min <= a && a <= Max) +} + +function ErrExit(S) { + print S > "/dev/stderr" + Err = 1 + exit 1 +} + +# Arr is an array of values with arbitrary indices. +# Array k is returned with numeric indices 1..n. +# The values in k are the indices of array arr, +# ordered so that if array arr is stepped through +# in the order arr[k[1]] .. arr[k[n]], it will be stepped +# through in order of the values of its elements. +# The return value is the number of elements in the array (n). +function qsort(arr,k, ArrInd,end) { + end = 0 + for (ArrInd in arr) + k[++end] = ArrInd; + qsortseg(arr,k,1,end); + return end +} + +function qsortseg(arr,k,start,end, left,right,sepval,tmp,tmpe,tmps) { + # handle two-element case explicitely for a tiny speedup + if ((end - start) == 1) { + if (arr[tmps = k[start]] > arr[tmpe = k[end]]) { + k[start] = tmpe + k[end] = tmps + } + return + } + left = start; + right = end; + sepval = arr[k[int((left + right) / 2)]] + # Make every element <= sepval be to the left of every element > sepval + while (left < right) { + while (arr[k[left]] < sepval) + left++ + while (arr[k[right]] > sepval) + right-- + if (left < right) { + tmp = k[left] + k[left++] = k[right] + k[right--] = tmp + } + } + if (left == right) + if (arr[k[left]] < sepval) + left++ + else + right-- + if (start < right) + qsortseg(arr,k,start,right) + if (left < end) + qsortseg(arr,k,left,end) +} +' "$@" diff --git a/examples/scripts.v2/pf b/examples/scripts.v2/pf new file mode 100644 index 0000000..ab6cd2c --- /dev/null +++ b/examples/scripts.v2/pf @@ -0,0 +1,127 @@ +#! /bin/bash +# +# original from: +# +# @(#) p.ksh 1.1 93/11/09 +# p: page compressed & plain files in the order given +# 92/01/23 john h. dubois iii (john@armory.com) +# 92/02/14 changed incorrect zpack to pcat +# 92/02/16 added help +# 92/10/11 search for file.Z and file.z if file not found +# 92/10/18 pass options to pager +# 93/11/09 Understand gzipped files too +# Wait after printing message about unreadable files +# Make less prompt include name of file being uncompressed +# +# conversion to bash v2 by Chet Ramey; renamed to pf +# +DefPager=/local/bin/less + +istrue() +{ + test 0 -ne "$1" +} + +warn() +{ + echo "$@" 1>&2 +} + +if [ "$1" = -h ]; then + echo \ +"$0: page a file. +Usage: $0 [pager-option ...] [filename ...] +Files are paged by the program specified in the user's PAGER +environment variable, or by $DefPager if PAGER is not set. +If no filename is given, text to page is read from the standard input. +If filenames are given, they are either paged directly, or unpacked/ +uncompressed and then paged. Files are assumed to be in packed, compressed, +or gzipped format if the filename ends in .Z, .z, or .gz respectively. +If a filename that does not end in .Z, .z, or .gz is not found, it is +searched for with one of those extensions attached. +Each group of plain files is paged by a single instance of the pager. +Each packed or compressed file is paged by a separate instance of the +pager. +Initial arguments beginning with + or - are taken to be pager options and +are passed to each instance of the pager. +If a pager option takes a value it should be given with the option as a +single argument (with no space between the option and the value)." + exit 0 +fi + +# Get pager options +while [ $# -gt 0 ]; do + case "$1" in + -*|+*) Opts="$Opts $1" ; shift;; + *) break;; + esac +done + +[ -z "$PAGER" ] && PAGER=$DefPager + +# Read from stdin +[ $# = 0 ] && exec $PAGER $Opts + +typeset -i filenum=0 badfile=0 + +for file; do + if [ ! -r "$file" ]; then + case "$file" in + *.[Zz]|*.gz) + # Check if user specified a compressed file without giving its extension + for ext in Z z gz; do + if [ -r "$file.$ext" ]; then + file="$file.$ext" + break + fi + done;; + esac + fi + if [ ! -r "$file" ]; then + warn "$file: cannot read." + badfile=1 + else + files[filenum]=$file + let filenum+=1 + fi +done + +if istrue $badfile && [ $filenum -gt 0 ]; then + echo -n "Press return to continue..." 1>&2 + read +fi + +unset plain + +for file in "${files[@]}"; do + case "$file" in + *.[zZ]|*.gz) + set -- Z zcat z pcat gz gzcat + # Find correct uncompression program + while [ $# -gt 0 ]; do + case "$file" in + *.$1) + # Page any uncompressed files so that they will be read + # in the correct order + [ ${#plain[@]} -gt 0 ] && $PAGER $Opts "${plain[@]}" + unset plain[*] + # If page is less, set the prompt to include the name of + # the file being uncompressed. Escape the . in the extension + # because less treats is specially in prompts (other dots + # in filenames will still be mucked with). + case "$PAGER" in + *less) Prompt="-P[${file%.$1}\\.$1] (%pb\\%)" ;; + *) unset Prompt ;; + esac + $2 "$file" | $PAGER "$Prompt" $Opts + break + esac + shift 2 + done + ;; + *) plain[${#plain[@]}]=$file;; + esac +done + +# Page any uncompressed files that haven't been paged yet +[ ${#plain[@]} -gt 0 ] && exec $PAGER $Opts "${plain[@]}" diff --git a/examples/scripts.v2/pmtop b/examples/scripts.v2/pmtop new file mode 100644 index 0000000..cc419ac --- /dev/null +++ b/examples/scripts.v2/pmtop @@ -0,0 +1,25 @@ +#! /bin/bash +# +# pmtop - poor man's `top' for SunOS 4.x +# + +CLEAR=clear # could also be 'tput clear' +HEADER="USER PID %CPU %MEM SZ RSS TT STAT START TIME COMMAND" + +if [ -n "$LINES" ]; then + SS=$(( $LINES - 2 )) +else + SS=20 +fi + +while : +do + $CLEAR + echo "$HEADER" + ps -aux | sort -nr -k 3 | sed ${SS}q + sleep 5 +done + +exit 0 + + diff --git a/examples/scripts.v2/ren b/examples/scripts.v2/ren new file mode 100644 index 0000000..da76026 --- /dev/null +++ b/examples/scripts.v2/ren @@ -0,0 +1,585 @@ +#!/bin/bash +#@ This program came from: ftp://ftp.armory.com/pub/scripts/ren +#@ Look there for the latest version. +#@ If you don't find it, look through http://www.armory.com/~ftp/ +# +# @(#) ren 2.1.1 2002-03-17 +# 1990-06-01 John H. DuBois III (john@armory.com) +# 1991-02-25 Improved help info +# 1992-06-07 Remove quotes from around shell pattern as required by new ksh +# 1994-05-10 Exit if no globbing chars given. +# 1995-01-23 Allow filename set to be given on command line. +# 1997-09-24 1.4 Let [] be used for globbing. Added x option. +# 1997-11-26 1.4.1 Notice if the sequences of globbing chars aren't the same. +# 1999-05-13 Changed name to ren to avoid conflict with /etc/rename +# 2000-01-01 1.4.2 Let input patterns that contain whitespace be used. +# 2001-02-14 1.5 Better test for whether old & new globbing seqs are identical. +# 2001-02-20 1.6 Added pP options. +# 2001-02-27 1.7 Added qf options. Improved interpretation of rename patterns. +# 2001-05-10 1.8 Allow multiple pP options. Added Qr options. +# 2001-07-25 2.0 Added mz options. +# 2001-11-25 2.1 Allow segment ranges to be given with -m. Work under ksh93. +# 2002-03-17 2.1.1 Fixed bug in test for legal expressions. + +# todo: It would be nice to be able to escape metacharacters with '\' +# todo: Should enhance patterns to make ] in a pair of brackets work ([]]) +# todo: Allow use of all ksh globbing patterns. +# todo: Allow use of extended regexps, with () to enumerate pieces and \num to +# todo: select them. +# +# Modifications for bash made by Chet Ramey <chet@po.cwru.edu> + +name=${0##*/} +Usage="Usage: +$name [-fhqtv] [-m<segstart[:segend]=operation>] [-z<len>] [-[pP]<pattern>] + oldpattern [newpattern [filename ...]] +or +$name -r [same options as above] oldpattern newpattern directory ..." +tell=false +verbose=false +warn=true +warnNoFiles=true +debug=false +recurse=false +inclPat= +exclPat= +declare -i inclCt=0 exclCt=0 +check=true +declare -i j op_end_seg + +# Begin bash additions +shopt -s extglob + +# +# ksh print emulation +# +# print [-Rnprsu[n]] [-f format] [arg ...] +# +# - end of options +# -R BSD-style -- only accept -n, no escapes +# -n do not add trailing newline +# -p no-op (no coprocesses) +# -r no escapes +# -s print to the history file +# -u n redirect output to fd n +# -f format printf "$format" "$@" +# + +print() +{ + local eflag=-e + local nflag= fflag= c + local fd=1 + + OPTIND=1 + while getopts "fRnprsu:" c + do + case $c in + R) eflag= ;; + r) eflag= ;; + n) nflag=-n ;; + s) sflag=y ;; + f) fflag=y ;; + u) fd=$OPTARG ;; + p) ;; + esac + done + shift $(( $OPTIND - 1 )) + + if [ -n "$fflag" ]; then + builtin printf "$@" >&$fd + return + fi + + case "$sflag" in + y) builtin history -s "$*" ;; + *) builtin echo $eflag $nflag "$@" >&$fd + esac +} + +# End bash additions + +while getopts :htvxp:P:fqQrm:z: opt; do + case $opt in + h) + print -r -- \ +"$name: rename files by changing parts of filenames that match a pattern. +$Usage +oldpattern and newpattern are subsets of sh filename patterns; the only +globbing operators (wildcards) allowed are ?, *, and []. All filenames that +match oldpattern will be renamed with the filename characters that match the +constant (non-globbing) characters of oldpattern changed to the corresponding +constant characters of newpattern. The characters of the filename that match +the globbing operators of oldpattern will be preserved. Globbing operators +in oldpattern must occur in the same order in newpattern; for every globbing +operators in newpattern there must be an identical globbing operators in +oldpattern in the same sequence. Both arguments should be quoted since +globbing operators are special to the shell. If filenames are given, only +those named are acted on; if not, all filenames that match oldpattern are acted +on. newpattern is required in all cases except when -m is given and no further +arguments are given. +If you are unsure whether a $name command will do what you intend, issue it +with the -t option first to be sure. +Examples: +$name \"/tmp/foo*.ba.?\" \"/tmp/new*x?\" + All filenames in /tmp that match foo*.ba.? will have the \"foo\" part + replaced by \"new\" and the \".ba.\" part replaced by \"x\". + For example, /tmp/fooblah.ba.baz would be renamed to /tmp/newblahxbaz. +$name \* \*- foo bar baz + foo, bar, and baz will be renamed to foo-, bar-, and baz-. +$name '????????' '????-??-??' + All filenames that are 8 characters long will be changed such that dashes + are inserted after the 4th and 6th characters. +Options: +-h: Print this help. +-r: Recursive operation. Filenames given on the command line after oldpattern + and newpattern are taken to be directories to traverse recursively. For + each subdirectory found, the specified renaming is applied to any matching + filenames. oldpattern and newpattern should not include any directory + components. +-p<pattern>, -P<pattern>: Act only on filenames that do (if -p is given) or do + not (if -P is given) match the sh-style filename globbing pattern + <pattern>. This further restricts the filenames that are acted on, beyond + the filename selection produced by oldpattern and the filename list (if + any). <pattern> must be quoted to prevent it from being interpreted by the + shell. Multiple instances of these options may be given. In this case, + filenames are acted on only if they match at least one of the patterns + given with -p and do not match any of the patterns given with -P. +-m<segstart[:segend]=operation>: For each file being renamed, perform a + mathematical operation on the string that results from concatenating + together the filename segments that matched globbing operator numbers + segstart through segend, where operators are numbered in order of + occurrence from the left. For example, in the pattern a?b*c[0-9]f, segment + 1 consists of the character that matched ?, segment 2 consists of the + character(s) that matched *, and segment 3 consists of the character that + matched [0-9]. The selected segments are replaced with the result of the + mathematical operation. + The concatenated string must consist of characters that can be interpreted + as a decimal integer; if it does not, the filename is not acted on. This + number is assigned to the variable 'i', which can be referenced by the + operation. The operations available are those understood by the ksh + interpreter, which includes most of the operators and syntax of the C + language. The original filename segment is replaced by the result of the + operation. If -m is used, newpattern may be an empty string or not given + at all (if no directory/file names are given). In this case, it is taken + to be the same as oldpattern. + If segend is given, any fixed text that occurs in the pattern between the + starting and ending globbing segments is discarded. If there are fewer + globbing segments than segend, no complaint is issued; the string is formed + from segment segstart through the last segment that does exist. + If segend is not given, the only segment acted on is startseg. + Examples: + $name -m3=i+6 '??*.ppm' + This is equivalent to: + $name -m3=i+6 '??*.ppm' '??*.ppm' + Since the old pattern and new pattern are identical, this would + normally be a no-op. But in this case, if a filename of ab079.ppm is + given, it is changed to ab85.ppm. + $name '-m1:2=i*2' 'foo??bar' + This will change a file named foo12bar to foo24bar + $name '-m1:2=i*2' 'foo?xyz?bar' + This will also change a file named foo1xyz2bar to foo24bar +-z<len>: Set the size of the number fields that result when -m is used. The + field is truncated to the trailing <len> digits or filled out to <len> + digits with leading zeroes. In the above example, if -z3 is given, the + output filename will be ab085.ppm. +-f: Force rename. By default, $name will not rename files if a file with the + new filename already exists. If -f is given, $name will carry out the + rename anyway. +-q: Quiet operation. By default, if -f is given, $name will still notify the + user if a rename results in replacement of an already-existing filename. + If -q is given, no notification is issued. +-Q: Suppress other warnings. By default, a warning is issued if no files are + selected for acting upon. If -Q is given, no warning is issued. +-v: Show the rename commands being executed. +-t: Show what rename commands would be done, but do not carry them out." + exit 0 + ;; + f) + check=false + ;; + q) + warn=false + ;; + Q) + warnNoFiles=false + ;; + r) + warnNoFiles=false + recurse=true + ;; + t) + tell=true + ;; + v) + verbose=true + ;; + x) + verbose=true + debug=true + ;; + p) + inclPats[inclCt]=$OPTARG + ((inclCt+=1)) + ;; + P) + exclPats[exclCt]=$OPTARG + ((exclCt+=1)) + ;; + m) + # Store operation for each segment number in ops[num] + # Store ending segment number in op_end_seg[num] + range=${OPTARG%%=*} + op=${OPTARG#*=} + start=${range%%:*} + end=${range#*:} + if [[ "$start" != +([0-9]) || "$start" -eq 0 ]]; then + print -ru2 -- "$name: Bad starting segment number given with -m: $start" + exit 1 + fi + if [[ "$end" != +([0-9]) || "$end" -eq 0 ]]; then + print -ru2 -- "$name: Bad ending segment number given with -m: $end" + exit 1 + fi + if [[ start -gt end ]]; then + print -ru2 -- "$name: Ending segment ($end) is less than starting segment ($start)" + exit 1 + fi + if [[ "$op" != @(|*[!_a-zA-Z0-9])i@(|[!_a-zA-Z0-9]*) ]]; then + print -ru2 -- \ + "$name: Operation given with -m does not reference 'i': $op" + exit 1 + fi + # Test whether operation is legal. let returns 1 both for error + # indication and when last expression evaluates to 0, so evaluate 1 + # after test expression. + i=1 + let "$op" 1 2>/dev/null || { + print -ru2 -- \ + "$name: Bad operation given with -m: $op" + exit 1 + } + ops[start]=$op + op_end_seg[start]=$end + ;; + z) + if [[ "$OPTARG" != +([0-9]) || "$OPTARG" -eq 0 ]]; then + print -ru2 -- "$name: Bad length given with -z: $OPTARG" + exit 1 + fi + typeset -Z$OPTARG j || exit 1 + ;; + +?) # no way to tell getopts to not treat +x as an option + print -r -u2 "$name: Do not prefix options with '+'." + exit 1 + ;; + :) + print -r -u2 \ +"$name: Option -$OPTARG requires a value. +$Usage +Use -h for help." + exit 1 + ;; + \?) + print -r -u2 \ +"$name: -$OPTARG: no such option. +$Usage +Use -h for help." + exit 1 + ;; + esac +done + +# remove args that were options +let OPTIND=OPTIND-1 +shift $OPTIND + +oldpat=$1 +newpat=$2 + +# If -m is given, a non-existant or null newpat should be set to oldpat +if [ ${#ops[*]} -gt 0 ]; then + case $# in + 0) + ;; + 1) + set -- "$oldpat" "$oldpat" + newpat=$oldpat + $debug && print -ru2 -- "Set new pattern to: $newpat" + ;; + *) + if [ -z "$newpat" ]; then + shift 2 + set -- "$oldpat" "$oldpat" "$@" + newpat=$oldpat + $debug && print -ru2 -- "Set new pattern to: $newpat" + fi + ;; + esac +fi + +# Make sure input patterns that contain whitespace can be expanded properly +IFS= + +origPat=$oldpat + +# Generate list of filenames to act on. +case $# in +[01]) + print -u2 "$Usage\nUse -h for help." + exit 1 + ;; +2) + if $recurse; then + print -r -u2 "$name: No directory names given with -r. Use -h for help." + exit 1 + fi + set -- $oldpat # Get list of all filenames that match 1st globbing pattern. + if [[ ! -a $1 ]]; then + $warnNoFiles && print -r -- "$name: No filenames match this pattern: $oldpat" + exit + fi + ;; +*) + shift 2 + ;; +esac + +integer patSegNum=1 numPatSegs + +# For old ksh +# while [[ "$oldpat" = *'[\*\?]'* ]]; do + +# Example oldpat: foo*.a +# Example newpat: bar*.b + +# Build list of non-pattern segments and globbing segments found in arguments. +# Note the patterns given are used to get the list of filenames to act on, +# to delimit constant segments, and to determine which parts of filenames are +# to be replaced. +# Examples given for first iteration (in the example, the only iteration) +# The || newpat is to ensure that new pattern does not have more globbing +# segments than old pattern +while [[ "$oldpat" = *@([\*\?]|\[+([!\]])\])* || + "$newpat" = *@([\*\?]|\[+([!\]])\])* ]]; do + ## Get leftmost globbing pattern in oldpat + + # Make r be oldpat with smallest left piece that includes a globbing + # pattern removed from it + r=${oldpat#*@([\*\?]|\[+([!\]])\])} # r=.a + # Make pat be oldpat with the above removed from it, leaving smallest + # left piece that includes a globbing pattern + pat=${oldpat%%"$r"} # pat=foo* + # Make l be pat with the globbing pattern removed from the right, + # leaving a constant string + l=${pat%@([\*\?]|\[+([!\]])\])} # l=foo + # Remove the constant part of pat from the left, leaving the globbing + # pattern + pat=${pat#"$l"} # pat=* + + # Do the same thing for newpat, solely to provide a reliable test that + # both oldpat & newpat contain exactly the same sequence of globbing + # patterns. + r=${newpat#*@([\*\?]|\[+([!\]])\])} # r=.b + npat=${newpat%%"$r"} # pat=bar* + l=${npat%@([\*\?]|\[+([!\]])\])} # l=bar + npat=${npat#"$l"} # npat=* + + if [[ "$pat" != "$npat" ]]; then + print -ru2 -- \ +"$name: Old-pattern and new-pattern do not have the same sequence of globbing chars. +Pattern segment $patSegNum: Old pattern: $pat New pattern: $npat" + exit 1 + fi + + ## Find parts before & after pattern + # oldpre[] stores the old constant part before the pattern, + # so that it can be removed and replaced with the new constant part. + oldpre[patSegNum]=${oldpat%%"$pat"*} # oldpre[1]=foo + # oldsuf stores the part that follows the globbing pattern, + # so that it too can be removed. + # After oldpre[] & oldsuf[] have been removed from a filename, what remains + # is the part matched by the globbing pattern, which is to be retained. + oldsuf[patSegNum]=${oldpat#*"$pat"} # oldsuf[1]=.a + # newpre[] stores the new constant part before the pattern, + # so that it can be used to replace the old constant part. + newpre[patSegNum]=${newpat%%"$pat"*} # newpre[1]=bar + # Get rid of processed part of patterns + oldpat=${oldpat#${oldpre[patSegNum]}"$pat"} # oldpat=.a + newpat=${newpat#${newpre[patSegNum]}"$pat"} # newpat=.b + # Store either * or ? in pats[], depending on whether this segment matches 1 + # or any number of characters. + [[ "$pat" = \[* ]] && pat=? + pats[patSegNum]=$pat + ((patSegNum+=1)) +done + +if [ patSegNum -eq 1 ]; then + print -u2 "No globbing chars in pattern." + exit 1 +fi + +oldpre[patSegNum]=${oldpat%%"$pat"*} # oldpre[2]=.a +oldsuf[patSegNum]=${oldpat#*"$pat"} # oldsuf[2]=.a +newpre[patSegNum]=${newpat%%"$pat"*} # newpre[2]=.b + +numPatSegs=patSegNum + +if $debug; then + patSegNum=1 + while [[ patSegNum -le numPatSegs ]]; do + print -ru2 -- \ +"Old prefix: <${oldpre[patSegNum]}> Old suffix: <${oldsuf[patSegNum]}> New prefix: <${newpre[patSegNum]}> Pattern: <${pats[patSegNum]}>" + ((patSegNum+=1)) + done +fi + +# Example filename: foox.a +# Example oldpat: foo*.a +# Example newpat: bar*.b + +integer numFiles=0 + +# Usage: renameFile filename [dirname] +# [dirname] is a directory name to prefix filenames with when they are printed +# for informational purposes. +# Uses globals: +# inclCt exclCt inclPats[] exclPats[] ops[] +# numPatSegs oldpre[] oldsuf[] newpre[] pats[] +# check warn tell verbose name +# Modifies globals: numFiles +function renameFile { + typeset file=$1 subdir=$2 + integer patSegNum patnum + typeset origname porigname newfile matchtext pnewfile matchsegs + integer startseg endseg + + origname=$file # origname=foox.a + porigname=$subdir$file + # Unfortunately, ksh88 does not do a good job of allowing for patterns + # stored in variables. Without the conditional expression being eval'ed, + # only sh patterns are recognized. If the expression is eval'ed, full + # ksh expressions can be used, but then expressions that contain whitespace + # break unless the user passed a pattern with the whitespace properly + # quoted, which is not intuititive. This is fixed in ksh93; full patterns + # work without being eval'ed. + if [ inclCt -gt 0 ]; then + patnum=0 + while [ patnum -lt inclCt ]; do + [[ "$file" = ${inclPats[patnum]} ]] && break + ((patnum+=1)) + done + if [ patnum -eq inclCt ]; then + $debug && print -ru2 -- "Skipping not-included filename '$porigname'" + return 1 + fi + fi + patnum=0 + while [ patnum -lt exclCt ]; do + if [[ "$file" = ${exclPats[patnum]} ]]; then + $debug && print -ru2 -- "Skipping excluded filename '$porigname'" + return 1 + fi + ((patnum+=1)) + done + # Extract matching segments from filename + ((numFiles+=1)) + patSegNum=1 + while [[ patSegNum -le numPatSegs ]]; do + # Remove a fixed prefix iteration: 1 2 + file=${file#${oldpre[patSegNum]}} # file=x.a file= + # Save the part of this suffix that is to be retained. To do this, we + # need to know what part of the suffix matched the current globbing + # segment. If the globbing segment is a *, this is done by removing + # the minimum part of the suffix that matches oldsuf (since * matches + # the longest segment possible). If the globbing segment is ? or [] + # (the latter has already been coverted to ?), it is done by taking the + # next character. + if [ "${pats[patSegNum]}" == \? ]; then + matchtext=${file#?} + matchtext=${file%$matchtext} + else + matchtext=${file%${oldsuf[patSegNum]}} # matchtext=x matchtext= + fi + $debug && print -ru2 -- "Matching segment $patSegNum: $matchtext" + file=${file#$matchtext} # file=.a file=.a + + matchsegs[patSegNum]=$matchtext + ((patSegNum+=1)) + done + + # Paste fixed and matching segments together to form new filename. + patSegNum=0 + newfile= + while [[ patSegNum -le numPatSegs ]]; do + matchtext=${matchsegs[patSegNum]} + startseg=patSegNum + if [ -n "${ops[startseg]}" ]; then + endseg=${op_end_seg[startseg]} + while [ patSegNum -lt endseg ]; do + ((patSegNum+=1)) + matchtext=$matchtext${matchsegs[patSegNum]} + done + if [[ "$matchtext" != +([-0-9]) ]]; then + print -ru2 -- \ +"Segment(s) $startseg - $endseg ($matchtext) of file '$porigname' do not form an integer; skipping this file." + return 2 + fi + i=$matchtext + let "j=${ops[startseg]}" || { + print -ru2 -- \ +"Operation failed on segment(s) $startseg - $endseg ($matchtext) of file '$file'; skipping this file." + return 2 + } + $debug && print -ru2 -- "Converted $matchtext to $j" + matchtext=$j + fi + newfile=$newfile${newpre[startseg]}$matchtext # newfile=barx newfile=barx.b + ((patSegNum+=1)) + done + + pnewfile=$subdir$newfile + if $check && [ -e "$newfile" ]; then + $warn && + print -ru2 -- "$name: Not renaming \"$porigname\"; destination filename \"$pnewfile\" already exists." + return 2 + fi + if $tell; then + print -n -r -- "Would move: $porigname -> $pnewfile" + $warn && [ -e "$newfile" ] && print -n -r " (destination filename already exists; would replace it)" + print "" + else + if $verbose; then + print -n -r -- "Moving: $porigname -> $pnewfile" + $warn && [ -e "$newfile" ] && print -n -r -- " (replacing old destination filename \"$pnewfile\")" + print "" + elif $warn && [ -e "$newfile" ]; then + print -r -- "$name: Note: Replacing old file \"$pnewfile\"" + fi + mv -f -- "$origname" "$newfile" + fi +} + +if $recurse; then + oPWD=$PWD + find "$@" -depth -type d ! -name '* +*' -print | while read dir; do + cd -- "$oPWD" + if cd -- "$dir"; then + for file in $origPat; do + renameFile "$file" "$dir/" + done + else + print -ru2 -- "$name: Could not access directory '$dir' - skipped." + fi + done +else + for file; do + renameFile "$file" + done +fi + +if [ numFiles -eq 0 ]; then + $warnNoFiles && print -ru2 -- \ + "$name: All filenames were excluded by patterns given with -p or -P." +fi diff --git a/examples/scripts.v2/rename b/examples/scripts.v2/rename new file mode 100644 index 0000000..96c46d6 --- /dev/null +++ b/examples/scripts.v2/rename @@ -0,0 +1,122 @@ +#! /bin/bash +# +# original from: +# @(#) rename.ksh 1.1 94/05/10 +# 90/06/01 John DuBois (spcecdt@armory.com) +# 91/02/25 Improved help info +# 92/06/07 remove quotes from around shell pattern as required by new ksh +# 94/05/10 Exit if no globbing chars given. +# +# conversion to bash v2 syntax by Chet Ramey + +phelp() +{ +echo "$usage +All files that match oldpattern will be renamed with the +filename components that match the constant parts of oldpattern +changed to the corresponding constant parts of newpattern. +The components of the filename that match variable parts of +oldpattern will be preserved. Variable parts in oldpattern +must occur in the same order in newpattern. Variables parts +can be '?' and '*'. +Example: +rename \"/tmp/foo*.ba.?\" \"/tmp/new*x?\" +All files in /tmp that match foo*.ba.? will have the \"foo\" part +replaced by \"new\" and the \".ba.\" part replaced by \"x\"." +} + +usage="usage: $name [-htv] oldpattern newpattern" +name=${0##/} + +while getopts "htv" opt; do + case "$opt" in + t) tell=true;; + v) verbose=true;; + h) phelp; exit 0;; + *) echo "$name: $usage" 1>&2; exit 2;; + esac +done +shift $((OPTIND - 1)) + +if [ $# -lt 2 ]; then + phelp + exit 2 +fi + +oldpat=$1 +newpat=$2 + +set -- $1 +if [ ! -e "$1" ]; then + echo "$name: no files match $oldpat." + exit 1 +fi + +typeset -i i=1 j + +# Example oldpat: foo*.a +# Example newpat: bar*.b + +# Examples given for first iteration (in the example, the only interation) +while :; do + case "$oldpat" in + *[\*\?]*) ;; + *) break;; + esac + + # Get leftmost globbing pattern in oldpat + pat=${oldpat#*[\*\?]} # pat=.a + pat=${oldpat%%"$pat"} # pat=foo* + pat=${pat##*[!\?\*]} # pat=* + # Find parts before & after pattern + oldpre[i]=${oldpat%%"$pat"*} # oldpre[1]=foo + oldsuf[i]=${oldpat#*"$pat"} # oldsuf[1]=.a + newpre[i]=${newpat%%"$pat"*} # newpre[1]=bar + # Get rid of processed part of patterns + oldpat=${oldpat#${oldpre[i]}"$pat"} # oldpat=.a + newpat=${newpat#${newpre[i]}"$pat"} # newpat=.b + let i=i+1 +done + +if [ $i -eq 1 ]; then + echo "No globbing chars in pattern." 1>&2 + exit 1 +fi + +oldpre[i]=${oldpat%%"$pat"*} # oldpre[2]=.a +oldsuf[i]=${oldpat#*"$pat"} # oldsuf[2]=.a +newpre[i]=${newpat%%"$pat"*} # newpre[2]=.b + +if [ -n "$verbose" ]; then + j=1 + while let "j < i"; do + echo \ +"Old prefix: ${oldpre[j]} Old suffix: ${oldsuf[j]} New prefix: ${newpre[j]}" + let j=j+1 + done +fi + +# Example file: foox.a + +for file; do + j=1 + origname=$file # origname=foox.a + newfile= + while let "j <= i"; do + # Peel off a prefix interation 1 2 + file=${file#${oldpre[j]}} # file=x.a file= + # Save the part of this prefix that is to be retained + const=${file%${oldsuf[j]}} # const=x const= + newfile=$newfile${newpre[j]}$const # newfile=barx newfile=barx.b + file=${file#$const} # file=.a file=.a + let j=j+1 + done + if [ -n "$tell" ]; then + echo "Would move \"$origname\" to \"$newfile\"." + else + if [ -n "$verbose" ]; then + echo "Moving \"$origname\" to \"$newfile\"." + fi + mv $origname $newfile + fi +done diff --git a/examples/scripts.v2/repeat b/examples/scripts.v2/repeat new file mode 100644 index 0000000..b6fccac --- /dev/null +++ b/examples/scripts.v2/repeat @@ -0,0 +1,121 @@ +#! /bin/bash +# +# original from: +# repeat: repeat a command. +# @(#) repeat.ksh 1.1 93/06/03 +# 90/05 john h. dubois iii (john@armory.com) +# 90/11 added help +# 93/06/03 Added s, h, p, and v options +# +# conversion to bash v2 syntax done by Chet Ramey + +istrue() +{ + test 0 -ne "$1" +} + +isfalse() +{ + test 0 -eq "$1" +} + +phelp() +{ +echo "$name: repeatedly execute a command line. +$Usage +commandline is executed once for each integer from startcount through endcount +inclusive. The default for startcount is 1 if a positive endcount or no +endcount is given, and -1 if a negative endcount is given. A count +parameter consisting of a single number is taken to be an endcount. If +only an endcount is given and it is positive, commandline is executed +endcount times. endcount may be less than startcount. If no endcount is +given (e.g. a count parameter of \"10-\"), commandline execution repeats +indefinitely with the iteration variable incrementing in a positive +direction. A count parameter of consisting of \"-\" will repeat +indefinitely starting with 1. + +Note that quoting and variables in commandline are interpreted twice, once +when it is passed to the repeat command, and once when it is actually executed. + +The iteration variable is \"count\". If \$count is used in commandline, make +sure it is quoted with ' or \. + +Options: +-h: Print this help. +-p: Print value of iteration variable on stderr before each iteration. +-s <sec>: sleep for <sec> seconds after each iteration except the last. +-v: Print start and end values before beginning." +} + +name=${0##*/} +Usage="Usage: repeat [-hpv] [-s <sec>] [[startcount]-][endcount] command [arg ...]" + +typeset -i count=1 forever=0 sleep=0 print=0 verbose=0 + +while getopts :0123456789hpvs: opt; do + case $opt in + h) phelp; exit 0;; + s) sleep=$OPTARG || exit 1;; + p) print=1;; + v)verbose=1;; + [0-9]) break;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2; exit 2;; + ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2; exit 2;; + esac +done + +# remove args that were options +shift $((OPTIND-1)) + +if [ $# -lt 2 ]; then + echo -e "$Usage\nUse -h for help." 1>&2 + exit 2 +fi + +case "$1" in +-[0-9]*-|[0-9]*-) + # Start value only + count=${1%-} + forever=1 + end="-1"; + ;; +-[0-9]*-[0-9]*|[0-9]*-[0-9]*) + # Start and end value + s=${1%-} + end=${s##[0-9]*-} + count=${s%-$end} + ;; +-[0-9]*|[0-9]*) + end=$1 + case "$end" in + -\*) count=-1;; + esac + ;; +-) + forever=1 + end="-1"; + ;; +*) + echo "$name: bad count parameter: $1" 1>&2 + exit 1 + ;; +esac + +shift + +[ -z "$end" ] && [ $count -le "$end" ] && increment=1 || increment=-1 + +istrue $verbose && echo "start=$count end=$end" 1>&2 + +# Need to do this here so that up to this point, -0 will keep the leading - +# and end will not be 0 if no value assigned +typeset -i end + +let end+=increment # make loop inclusive of original endcount + +while istrue $forever || [ $count -ne $end ]; do + istrue $print && echo $count 1>&2 + eval "$@" + istrue $sleep && sleep $sleep + let count+=increment +done diff --git a/examples/scripts.v2/shprof b/examples/scripts.v2/shprof new file mode 100644 index 0000000..73a1bb9 --- /dev/null +++ b/examples/scripts.v2/shprof @@ -0,0 +1,66 @@ +#! /bin/bash +# +# shprof - a line profiler for shell scripts +# +# adapted from a similar program included in `The New KornShell' by +# Bolsky and Korn and posted to usenet by bsh20858@challenger.fhda.edu +# +# converted to bash v2 syntax by Chet Ramey +# +TMPFILE=${TMP:-/tmp}/shprof$$ + +trap 'rm -f $TMPFILE' EXIT + +errexit() +{ + echo $0: "$@" >&2 + exit 1 +} + +# create script with profiling enabled +cat > $TMPFILE <<- \_EOF_ + declare -a _line + _profend() + { + case "$1" in + /*|./*) file="$1" ;; + *) file=$(type -path "$1") ;; + esac + + echo "*** line profile for $file ***" + i=1; + while read -r && [ $i -le $NLINE ]; do + count=${_line[$i]} + if [ "$count" -gt 0 ]; then + echo "[$count] $i: $REPLY" + fi + i=$((i + 1)) + done <$file +_EOF_ +# make the profiling script remove itself after printing line stats +echo "rm -f $TMPFILE" >> $TMPFILE +cat >> $TMPFILE <<- \_EOF_ + } + _command=$1 + shift + i=1 + NLINE=$(wc -l < "$_command") + while [ $i -le $NLINE ]; do + _line[$i]=0 + i=$((i + 1)) + done + unset i + trap "_profend ${_command}" EXIT + trap '_line[$LINENO]=$((${_line[$LINENO]} + 1))' DEBUG + LINENO=0 +_EOF_ + +case "$1" in +/*|./*) file=$1 ;; +*) file=$((type -path "$1")) ;; +esac + +cat "${file-$1}" >> $TMPFILE || errexit "${1}: cannot open" +chmod +x $TMPFILE + +exec -a "$file" $TMPFILE "$@" diff --git a/examples/scripts.v2/untar b/examples/scripts.v2/untar new file mode 100644 index 0000000..1ba6b6b --- /dev/null +++ b/examples/scripts.v2/untar @@ -0,0 +1,80 @@ +#! /bin/bash +# +# original from: +# @(#) untar.ksh 1.0 93/11/10 +# 92/10/08 john h. dubois iii (john@armory.com) +# 92/10/31 make it actually work if archive isn't in current dir! +# 93/11/10 Added pack and gzip archive support +# +# conversion to bash v2 syntax done by Chet Ramey + +phelp() +{ +echo \ +"$name: extract tar archives into directories, uncompressing if neccessary. +Usage: $name archive[.tar[.[Z|gz]]] .. +If an archive name given does not end in .tar, .tar.Z, or .tar.gz, it is +searched for first with .tar added, then .tar.Z, and then .tar.gz added. +The real filename must end in either .tar, .tar.Z, or .tar.gz. A +directory with the name of the archive is created in the current directory +(not necessarily the directory that the archive is in) if it does not +exist, and the the contents of the archive are extracted into it. +Absolute pathnames in tarfiles are suppressed." +} + +if [ $# -eq 0 ]; then + phelp + exit 1 +fi + +name=${0##/} +OWD=$PWD + +for file; do + cd $OWD + case "$file" in + *.tar.Z) ArchiveName=${file%%.tar.Z} zcat=zcat;; + *.tar.z) ArchiveName=${file%%.tar.z} zcat=pcat;; + *.tar.gz) ArchiveName=${file%%.tar.gz} zcat=gzcat;; + *) ArchiveName=$file + for ext in "" .Z .z .gz; do + if [ -f "$file.tar$ext" ]; then + file="$file.tar$ext" + break + fi + done + if [ ! -f "$file" ]; then + echo "$file: cannot find archive." 1>&2 + continue + fi + ;; + esac + if [ ! -r "$file" ]; then + echo "$file: cannot read." >&2 + continue + fi + DirName=${ArchiveName##*/} + [ -d "$DirName" ] || { + mkdir "$DirName" || { + echo "$DirName: could not make archive directory." 1>&2 + continue + } + } + + cd $DirName || { + echo "$name: cannot cd to $DirName" 1>&2 + continue + } + + case "$file" in + /*) ;; + *) file=$OWD/$file ;; + esac + + echo "Extracting archive $file into directory $DirName..." + case "$file" in + *.tar.Z|*.tar.z|*.tar.gz) $zcat $file | tar xvf -;; + *.tar) tar xvf $file;; + esac + echo "Done extracting archive $file into directory $DirName." +done diff --git a/examples/scripts.v2/uudec b/examples/scripts.v2/uudec new file mode 100644 index 0000000..7984058 --- /dev/null +++ b/examples/scripts.v2/uudec @@ -0,0 +1,45 @@ +: +# @(#) uudec.sh 1.0 93/11/22 +# 92/08/04 john@armory.com (John H. DuBois III) +# 93/11/22 Added help. + +isfalse() +{ + test 0 -eq "$1" +} + +phelp() +{ +"$name: process uuencoded files. +Usage: uudec [-h] filename ... +Options: +-h: Print this help." +} + +name=${0##*/} + +typeset -i force=0 + +while getopts "hf" opt; do + case "$opt" in + h) phelp; exit 0;; + f) force=1;; + *) echo "$Usage" 1>&2; exit 2;; + esac +done + +shift $((OPTIND - 1)) + +for file; do + echo "$file" + while read b mode filename && [ "$b" != begin ]; do :; done < "$file" + if [ "$b" = begin ]; then + if [ -f "$filename" ] && isfalse $force; then + echo "Output file \"$filename\" exists. Not written." + else + uudecode "$file" + fi + else + echo "No begin line." + fi +done diff --git a/examples/scripts.v2/uuenc b/examples/scripts.v2/uuenc new file mode 100644 index 0000000..480aa48 --- /dev/null +++ b/examples/scripts.v2/uuenc @@ -0,0 +1,69 @@ +#! /bin/bash +# +# original from: +# @(#) uuenc.ksh 1.0 93/09/18 +# 93/09/18 john h. dubois iii (john@armory.com) +# +# conversion to bash v2 syntax by Chet Ramey + +istrue() +{ + test 0 -ne "$1" +} + +isfalse() +{ + test 0 -eq "$1" +} + +phelp() +{ +echo "$name: uuencode files. +$Usage +For each filename given, $name uuencodes the file, using the final +component of the file's path as the stored filename in the uuencoded +archive and, with a .${SUF} appended, as the name to store the archive in. +Example: +$name /tmp/foo +The file /tmp/foo is uuencoded, with \"foo\" stored as the name to uudecode +the file into, and the output is stored in a file in the current directory +with the name \"foo.${SUF}\". +Options: +-f: Normally, if the file the output would be stored in already exists, + it is not overwritten and an error message is printed. If -f (force) + is given, it is silently overwritten. +-h: Print this help." +} + +name=${0##*/} +Usage="Usage: $name [-hf] <filename> ..." +typeset -i force=0 + +SUF=uu + +while getopts :hf opt; do + case $opt in + h) phelp; exit 0;; + f) force=1;; + +?) echo "$name: options should not be preceded by a '+'." 1>&2 ; exit 2;; + ?) echo "$name: $OPTARG: bad option. Use -h for help." 1>&2 ; exit 2;; + esac +done + +# remove args that were options +shift $((OPTIND - 1)) + +if [ $# -lt 1 ]; then + echo "$Usage\nUse -h for help." 1>&2 + exit +fi + +for file; do + tail=${file##*/} + out="$tail.${SUF}" + if isfalse $force && [ -a "$out" ]; then + echo "$name: $out: file exists. Use -f to overwrite." 1>&2 + else + uuencode $file $tail > $out + fi +done diff --git a/examples/scripts.v2/vtree b/examples/scripts.v2/vtree new file mode 100644 index 0000000..7523cc8 --- /dev/null +++ b/examples/scripts.v2/vtree @@ -0,0 +1,137 @@ +#! /bin/bash +# +# original from: +# vtree: visual directory tree +# @(#) vtree.sh 1.1 91/07/01 +# 90/04 john h. dubois iii (john@armory.com) +# 91/07/01 fixed bug that caused problems when dir given on command line, +# added some info to help, changed to 4-space indenting +# +# conversion to bash v2 syntax done by Chet Ramey +# +help=\ +"Syntax: vtree [startdir] [namelen=#] [linelen=#] +If startdir is not specified, tree will start at current dir. + +namelen specifies the minimum number of characters of a directory name that +are guaranteed to be printed. +This is a tradeoff between the number of tree levels that can fit on a +screen line and the number of chars of each dir name that can be printed. +In most cases it will be possible to print more than namelen characters of +the name (a name up to namelen+1 chars will always be printed in full), +but in some cases truncation down to namelen chars will occur. +If truncation occurs, a '>' is printed at the end of the name. +namelen=8 (the default) typically causes about 5 dirs/1000 to be truncated. +namelen=7 typically causes about 10 dirs/1000 to be truncated. +namelen=8 will allow 6 full length dirs to be printed in 79 columns. +namelen=7 will allow 7 full length dirs to be printed in 79 columns; + +linelen specifies the maximum number of characters to print on one screen +line. All characters beyond this are truncated. The default is 1024. +To avoid line wrap on an 80 column terminal with autowrap, use linelen=79. +" + +for i in "$@"; do + case $i in + -h) echo "$help"; exit;; + *=*) + vars="$vars $i" + ;; + *) + if [ ! -x $i ] || [ ! -d $i ]; then # arg must be a dir and executable + echo "$i: directory not accessible." + exit + fi + cd $i + ;; + esac + shift +done + +pwd # print path of root of tree + +# find all directories depth first; ignore permission errors +find . -type d -print 2> /dev/null | \ +gawk -F/ ' + +# Do this block for NR == 1 instead of BEGIN because command line var +# assignments are not done until after BEGIN block is executed. +NR == 1 { + if (namelen) + MaxLen = namelen; + else + MaxLen = 8; + if (!linelen) + linelen = 1024 + HSpace = substr(" ",1,MaxLen); # used to indent tree + n = 0; # number of dirs found on one major branch +} + +$0 != "." { # do for every line produced by find except tree root dir + if (NF == 2 && n > 0) # print major branch whenever a new one starts + list(); + Depth[n] = NF - 1; # record depth and name of dir + Name[n++] = $NF; +} + +END { + list() # print last major branch +} + +function list() { + Line = Name[0]; # initialize first line of branch to be branch base + for (i = 1; i < n; i++) { # for each name in major branch + if (Depth[i] == Depth[i-1] + 1) + AddHLink(); # if moving deeper into branch, use same line + else { + print substr(Line,1,linelen); # last line is done; print it + Line = ""; # start new line + # print indentation, vert links, and vert/horiz links + for (d = 1; d < Depth[i] - 1; d++) # for each level of indentation + # if a vert. link has been established for this level + if (VLink[d]) + Line = Line HSpace " | "; + else # print empty indentation + Line = Line HSpace " "; + # Print last part of vert. link + if (VLink[d] == i) { + VLink[d] = 0; # mark level for no vert link + Line = Line HSpace " \\--"; + } + else + Line = Line HSpace " |--"; + } + Line = Line Name[i]; # Add dir name to line + } + print substr(Line,1,linelen); # print last line of major branch + n = 0; # reset name counter +} + +function AddHLink() { + NDepth = Depth[i]; # Depth of this name + VLink[NDepth - 1] = 0; + # search until a name found at a level less than this one + for (j = i + 1; j < n && Depth[j] >= NDepth; j++) + # keep track of last name that VLink should connect to + if (Depth[j] == NDepth) + VLink[NDepth - 1] = j; + if (VLink[NDepth - 1]) { + NLine = substr(Line,1,(NDepth - 2) * (MaxLen + 4) + MaxLen + 1); + if (length(NLine) < length(Line)) + Line = substr(NLine,1,length(NLine) - 1) ">" + else + Line = NLine; + Line = Line substr("--------------+--", + 18 - ((NDepth - 1) * (MaxLen + 4) - length(Line))); + } + else { + NLine = substr(Line,1,(NDepth - 2) * (MaxLen + 4) + MaxLen + 3); + if (length(NLine) < length(Line)) + Line = substr(NLine,1,length(NLine) - 1) ">" + else + Line = NLine; + Line = Line substr("-----------------", + 1,(NDepth - 1) * (MaxLen + 4) - length(Line)); + } +} +' $vars diff --git a/examples/scripts.v2/where b/examples/scripts.v2/where new file mode 100644 index 0000000..7a1dbde --- /dev/null +++ b/examples/scripts.v2/where @@ -0,0 +1,111 @@ +#! /bin/bash +# +# original from: +# @(#) where.ksh 1.1 94/07/11 +# 91/01/12 john h. dubois iii (john@armory.com) +# 92/08/10 Only print executable *files*. +# 92/10/06 Print err msg if no match found. +# 92/11/27 Added implicit * +# 93/07/23 Print help only if -h is given. +# 94/01/01 Added -x option +# 94/07/11 Don't bother with eval +# +# conversion to bash v2 syntax done by Chet Ramey + +name=${0##*/} +Usage="Usage: $name [-hx] 'pattern' ..." +typeset -i exact=0 + +phelp() +{ +echo "$name: find executable files in PATH that match patterns. +$Usage +$name searches each directory specified in the PATH environment variable +for executable files that match the specified patterns. Patterns are +given as Korn shell filename patterns. They are surrounded by implicit +'*' characters, so that \"foo\" will match any executble file whose name +contains contains \"foo\". This can be overridden by using '^' and '$' to +force a match to start at the beginning and end at the end of a filename +respectively. Characters that are special to the shell must generally +be protected from the shell by surrounding them with quotes. +Examples: +$name foo +lists all executable files in PATH that contain foo. +$name '^b*sh$' +lists all executable files in PATH that start with b and end with sh. +An error message is printed if a no matching file is found for a pattern. +Options: +-h: Print this help. +-x: Find exact matches only; equivalent to putting ^ and $ at the start + and end of each pattern." +} + +istrue() +{ + test 0 -ne "$1" +} + +isfalse() +{ + test 0 -eq "$1" +} + +while getopts "xh" opt; do + case "$opt" in + x) exact=1;; + h) phelp ; exit 0;; + *) echo -e "$Usage\nUse -h for help." 1>&2; exit 2;; + esac +done + +shift $((OPTIND-1)) + +set +f # make sure filename globbing is on +Args=("$@") # save args + +OIFS=$IFS +IFS=: # Make PATH be split on : +Paths=($PATH) +IFS=$OIFS + +for arg in "${Args[@]}"; do + + # get rid of leading ^ + if istrue $exact; then + arg=${arg} + else + case "$arg" in + ^*) arg=${arg#?};; + *) arg="*$arg" ;; # Pattern is not anchored at start + esac + fi + + # get rid of trailing $ + if istrue $exact; then + arg="$arg" + else + case "$arg" in + *\$) arg=${arg%?} ;; + *) arg="$arg*" ;; + esac + fi + + found=0 # Pattern not found yet + Patterns= + # Make a pattern for each element of PATH + for PathElem in "${Paths[@]}"; do + [ -z "$PathElem" ] && PathElem=. + Patterns="$Patterns $PathElem/$arg" + done + + # Find all pattern matches that are executable regular files. + for file in $Patterns; do + if [ -x "$file" ] && [ -f "$file" ]; then + echo "$file" + found=1 + fi + done + if [ $found = 0 ]; then + echo "$arg: not found." 1>&2 + fi +done diff --git a/examples/scripts/adventure.sh b/examples/scripts/adventure.sh new file mode 100755 index 0000000..1694450 --- /dev/null +++ b/examples/scripts/adventure.sh @@ -0,0 +1,553 @@ +#!/bin/bash +# ash -- "Adventure shell" +# last edit: 86/04/21 D A Gwyn +# SCCS ID: @(#)ash.sh 1.4 + +OPATH=$PATH + +ask() +{ + echo -n "$@" '[y/n] ' + read ans + + case "$ans" in + y*|Y*) + return 0 + ;; + *) + return 1 + ;; + esac +} + +CAT=${PAGER:-more} + +ash_inst() +{ + cat <<- EOF + + Instructions for the Adventure shell + + Welcome to the Adventure shell! In this exploration of the UNIX file + system, I will act as your eyes and hands. As you move around, I will + describe whatever is visible and will carry out your commands. The + general form of a command is + Verb Object Extra_stuff. + Most commands pay no attention to the "Extra_stuff", and many do not + need an "Object". A typical command is + get all + which picks up all files in the current "room" (directory). You can + find out what you are carrying by typing the command + inventory + The command "help" results in a full description of all commands that I + understand. To quit the Adventure shell, type + quit + + There are UNIX monsters lurking in the background. These are also + known as "commands with arguments". + + Good luck! + EOF +} + +ash_help() +{ +echo "I understand the following commands (synonyms in parentheses):" +echo "" + +echo "change OBJECT to NEW_NAME changes the name of the object" +echo "clone OBJECT as NEW_NAME duplicates the object" +echo "drop OBJECTS leaves the objects in the room" +echo "enter (go) PASSAGE takes the labeled passage" +echo "examine OBJECTS describes the objects in detail" +echo "feed OBJECT to MONSTER stuffs the object into a UNIX monster" +echo "get (take) OBJECTS picks up the specified objects" +echo "gripe (bug) report a problem with the Adventure shell" +echo "help prints this summary" +echo "inventory (i) tells what you are carrying" +echo "kill (destroy) OBJECTS destroys the objects" +echo "look (l) describes the room, including hidden objects" +echo "open (read) OBJECT shows the contents of an object" +echo "quit (exit) leaves the Adventure shell" +echo "resurrect OBJECTS attempts to restore dead objects" +echo "steal OBJECT from MONSTER obtains the object from a UNIX monster" +echo "throw OBJECT at daemon feeds the object to the printer daemon" +echo "up takes the overhead passage" +echo "wake MONSTER awakens a UNIX monster" +echo "where (w) tells you where you are" +echo "xyzzy moves you to your home" +} + +MAINT=chet@ins.cwru.edu + +PATH=/usr/ucb:/bin:/usr/bin:/usr/local/bin:. +export PATH + +trap 'echo Ouch!' 2 3 +#trap '' 18 # disable Berkeley job control + +#ash_lk(){ echo " $1 " | fgrep " $2 " >&- 2>&-; } +ash_lk(){ echo " $1 " | fgrep -q " $2 " >/dev/null 2>&1 ; } +ash_pr(){ echo $* | tr ' ' '\012' | pr -5 -t -w75 -l$[ ( $# + 4 ) / 5 ]; } +ash_rm(){ echo " $1 " | sed -e "s/ $2 / /" -e 's/^ //' -e 's/ $//'; } + +# enable history, bang history expansion, and emacs editing +set -o history +set -o histexpand +set -o emacs + +cd +LIM=.limbo # $HOME/$LIM contains "destroyed" objects +mkdir $LIM || { + echo "ash: cannot mkdir $LIM: exiting" + exit 1 +} +KNAP=.knapsack # $HOME/$KNAP contains objects being "carried" +if [ ! -d $KNAP ] +then mkdir $KNAP >/dev/null 2>&1 + if [ $? = 0 ] + then echo 'You found a discarded empty knapsack.' + else echo 'You have no knapsack to carry things in.' + exit 1 + fi +else echo 'One moment while I peek in your old knapsack...' +fi + +kn=`echo \`ls -a $KNAP | sed -e '/^\.$/d' -e '/^\.\.$/d'\`` + +if ask 'Welcome to the Adventure shell! Do you need instructions?' +then + ash_inst + echo -n 'Type a newline to continue: ' + read +fi + +wiz=false +cha=false +prev=$LIM +while : +do room=`pwd` + if [ $room != $prev ] + then if [ $room = $HOME ] + then echo 'You are in your own home.' + else echo "You have entered $room." + fi + exs= + obs= + hexs= + hobs= + f=false + for i in `ls -a` + do case $i in + .|..) ;; + .*) if [ -f $i ] + then hobs="$hobs $i" + elif [ -d $i ] + then hexs="$hexs $i" + else f=true + fi + ;; + *) if [ -f $i ] + then obs="$obs $i" + elif [ -d $i ] + then exs="$exs $i" + else f=true + fi + ;; + esac + done + if [ "$obs" ] + then echo 'This room contains:' + ash_pr $obs + else echo 'The room looks empty.' + fi + if [ "$exs" ] + then echo 'There are exits labeled:' + ash_pr $exs + echo 'as well as a passage overhead.' + else echo 'There is a passage overhead.' + fi + if sh -c $f + then echo 'There are shadowy figures in the corner.' + fi + prev=$room + fi + + read -e -p '-advsh> ' verb obj x # prompt is '-advsh> ' + if [ $? != 0 ] + then verb=quit # EOF + fi + + case $verb in + change) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then set -- $x + case "$1" in + to) if [ "$2" ] + then if [ -f $2 ] + then echo "You must destroy $2 first." + set -- + fi + if [ "$2" ] + then if mv $obj $2 # >&- 2>&- + then echo "The $obj shimmers and turns into $2." + obs=`ash_rm "$2 $obs" "$obj"` + else echo "There is a cloud of smoke but the $obj is unchanged." + fi + fi + else echo 'To what?' + fi + ;; + *) echo "Change $obj to what?" + ;; + esac + else if ash_lk "$kn" "$obj" + then echo 'You must drop it first.' + else echo "I see no $obj here." + fi + fi + else echo 'Change what?' + fi + ;; + clone) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then if [ ! -r $obj ] + then echo "The $obj does not wish to be cloned." + else set -- $x + case "$1" in + as) if [ "$2" ] + then if [ -f $2 ] + then echo "You must destroy $2 first." + else if cp $obj $2 # >&- 2>&- + then echo "Poof! When the smoke clears, you see the new $2." + obs="$obs $2" + else echo 'You hear a dull thud but no clone appears.' + fi + fi + else echo 'As what?' + fi + ;; + *) echo "Clone $obj as what?" + ;; + esac + fi + else if ash_lk "$kn" "$obj" + then echo 'You must drop it first.' + else echo "I see no $obj here." + fi + fi + else echo 'Clone what?' + fi + ;; + drop) if [ "$obj" ] + then for it in $obj $x + do if ash_lk "$kn" "$it" + then if [ -w $it ] + then echo "You must destroy $it first." + else if mv $HOME/$KNAP/$it $it # >&- 2>&- + then echo "$it: dropped." + kn=`ash_rm "$kn" "$it"` + obs=`echo $it $obs` + else echo "The $it is caught in your knapsack." + fi + fi + else echo "You're not carrying the $it!" + fi + done + else echo 'Drop what?' + fi + ;; + enter|go) if [ "$obj" ] + then if [ $obj != up ] + then if ash_lk "$exs $hexs" "$obj" + then if [ -x $obj ] + then if cd $obj + then echo 'You squeeze through the passage.' + else echo "You can't go that direction." + fi + else echo 'An invisible force blocks your way.' + fi + else echo 'I see no such passage.' + fi + else if cd .. + then echo 'You struggle upwards.' + else echo "You can't reach that high." + fi + fi + else echo 'Which passage?' + fi + ;; + examine) if [ "$obj" ] + then if [ $obj = all ] + then $obj=`echo $obs $exs` + x= + fi + for it in $obj $x + do if ash_lk "$obs $hobs $exs $hexs" "$it" + then echo "Upon close inspection of the $it, you see:" + ls -ld $it 2>/dev/null + if [ $? != 0 ] + then echo "-- when you look directly at the $it, it vanishes." + fi + else if ash_lk "$kn" "$it" + then echo 'You must drop it first.' + else echo "I see no $it here." + fi + fi + done + else echo 'Examine what?' + fi + ;; + feed) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then set -- $x + case "$1" in + to) if [ "$2" ] + then shift + if PATH=$OPATH $* <$obj 2>/dev/null + then echo "The $1 monster devours your $obj." + if rm -f $obj # >&- 2>&- + then obs=`ash_rm "$obs" "$obj"` + else echo 'But he spits it back up.' + fi + else echo "The $1 monster holds his nose in disdain." + fi + else echo 'To what?' + fi + ;; + *) echo "Feed $obj to what?" + ;; + esac + else if ash_lk "$kn" "$obj" + then echo 'You must drop it first.' + else echo "I see no $obj here." + fi + fi + else echo 'Feed what?' + fi + ;; + get|take) if [ "$obj" ] + then if [ $obj = all ] + then obj="$obs" + x= + fi + for it in $obj $x + do if ash_lk "$obs $hobs" "$it" + then if ash_lk "$kn" "$it" + then echo 'You already have one.' + else if mv $it $HOME/$KNAP/$it # >&- 2>&- + then echo "$it: taken." + kn="$it $kn" + obs=`ash_rm "$obs" "$it"` + else echo "The $it is too heavy." + fi + fi + else echo "I see no $it here." + fi + done + else echo 'Get what?' + fi + ;; + gripe|bug) echo 'Please describe the problem and your situation at the time it failed.\nEnd the bug report with a line containing just a Ctrl-D.' + cat | mail $MAINT -s 'ash bug' + echo 'Thank you!' + ;; + help) ash_help + ;; + inventory|i) if [ "$kn" ] + then echo 'Your knapsack contains:' + ash_pr $kn + else echo 'You are poverty-stricken.' + fi + ;; + kill|destroy) if [ "$obj" ] + then if [ $obj = all ] + then x= + if ask "Do you really want to attempt to $verb them all?" + then obj=`echo $obs` + else echo 'Chicken!' + obj= + fi + fi + for it in $obj $x + do if ash_lk "$obs $hobs" "$it" + then if mv $it $HOME/$LIM # <&- >&- 2>&- + then if [ $verb = kill ] + then echo "The $it cannot defend himself; he dies." + else echo "You have destroyed the $it; it vanishes." + fi + obs=`ash_rm "$obs" "$it"` + else if [ $verb = kill ] + then echo "Your feeble blows are no match for the $it." + else echo "The $it is indestructible." + fi + fi + else if ash_lk "$kn" "$it" + then echo "You must drop the $it first." + found=false + else echo "I see no $it here." + fi + fi + done + else echo 'Kill what?' + fi + ;; + look|l) obs=`echo $obs $hobs` + hobs= + if [ "$obs" ] + then echo 'The room contains:' + ash_pr $obs + else echo 'The room is empty.' + fi + exs=`echo $exs $hexs` + hexs= + if [ "$exs" ] + then echo 'There are exits plainly labeled:' + ash_pr $exs + echo 'and a passage directly overhead.' + else echo 'The only exit is directly overhead.' + fi + ;; + magic) if [ "$obj" = mode ] + then if sh -c $cha + then echo 'You had your chance and you blew it.' + else if ask 'Are you a wizard?' + then echo -n 'Prove it! Say the magic word: ' + read obj + if [ "$obj" = armadillo ] + then echo 'Yes, master!!' + wiz=true + else echo "Homie says: I don't think so" + cha=true + fi + else echo "I didn't think so." + fi + fi + else echo 'Nice try.' + fi + ;; + open|read) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then if [ -r $obj ] + then if [ -s $obj ] + then echo "Opening the $obj reveals:" + $CAT < $obj + if [ $? != 0 ] + then echo '-- oops, you lost the contents!' + fi + else echo "There is nothing inside the $obj." + fi + else echo "You do not have the proper tools to open the $obj." + fi + else if ash_lk "$kn" "$obj" + then echo 'You must drop it first.' + found=false + else echo "I see no $obj here." + fi + fi + else echo 'Open what?' + fi + ;; + quit|exit) if ask 'Do you really want to quit now?' + then if [ "$kn" ] + then echo 'The contents of your knapsack will still be there next time.' + fi + rm -rf $HOME/$LIM + echo 'See you later!' + exit 0 + fi + ;; + resurrect) if [ "$obj" ] + then for it in $obj $x + do if ash_lk "$obs $hobs" "$it" + then echo "The $it is already alive and well." + else if mv $HOME/$LIM/$it $it # <&- >&- 2>&- + then echo "The $it staggers to his feet." + obs=`echo $it $obs` + else echo "There are sparks but no $it appears." + fi + fi + done + else echo 'Resurrect what?' + fi + ;; + steal) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then echo 'There is already one here.' + else set -- $x + case "$1" in + from) if [ "$2" ] + then shift + if PATH=$OPATH $* >$obj 2>/dev/null + then echo "The $1 monster drops the $obj." + obs=`echo $obj $obs` + else echo "The $1 monster runs away as you approach." + rm -f $obj # >&- 2>&- + fi + else echo 'From what?' + fi + ;; + *) echo "Steal $obj from what?" + ;; + esac + fi + else echo 'Steal what?' + fi + ;; + throw) if [ "$obj" ] + then if ash_lk "$obs $hobs" "$obj" + then set -- $x + case "$1" in + at) case "$2" in + daemon) if sh -c "lpr -r $obj" + then echo "The daemon catches the $obj, turns it into paper,\nand leaves it in the basket." + obs=`ash_rm "$obs" "$obj"` + else echo "The daemon is nowhere to be found." + fi + ;; + *) echo 'At what?' + ;; + esac + ;; + *) echo "Throw $obj at what?" + ;; + esac + else if ash_lk "$kn" "$obj" + then echo 'It is in your knapsack.' + found=false + else echo "I see no $obj here." + fi + fi + else echo 'Throw what?' + fi + ;; + u|up) if cd .. + then echo 'You pull yourself up a level.' + else echo "You can't reach that high." + fi + ;; + wake) if [ "$obj" ] + then echo "You awaken the $obj monster:" + PATH=$OPATH $obj $x + echo 'The monster slithers back into the darkness.' + else echo 'Wake what?' + fi + ;; + w|where) echo "You are in $room." + ;; + xyzzy) if cd + then echo 'A strange feeling comes over you.' + else echo 'Your spell fizzles out.' + fi + ;; + *) if [ "$verb" ] + then if sh -c $wiz + then PATH=$OPATH $verb $obj $x + else echo "I don't know how to \"$verb\"." + echo 'Type "help" for assistance.' + fi + else echo 'Say something!' + fi + ;; + esac +done diff --git a/examples/scripts/bcsh.sh b/examples/scripts/bcsh.sh new file mode 100755 index 0000000..b810cab --- /dev/null +++ b/examples/scripts/bcsh.sh @@ -0,0 +1,1254 @@ +# 1-Feb-86 09:37:35-MST,30567;000000000001 +# Return-Path: <unix-sources-request@BRL.ARPA> +# Received: from BRL-TGR.ARPA by SIMTEL20.ARPA with TCP; Sat 1 Feb 86 09:36:16-MST +# Received: from usenet by TGR.BRL.ARPA id a002623; 1 Feb 86 9:33 EST +# From: chris <chris@globetek.uucp> +# Newsgroups: net.sources +# Subject: Improved Bcsh (Bourne Shell Cshell-Emulator) +# Message-ID: <219@globetek.UUCP> +# Date: 30 Jan 86 17:34:26 GMT +# To: unix-sources@BRL-TGR.ARPA +# +# This is a new, improved version of my Bourne shell cshell-emulator. +# The code has been cleaned up quite a bit, and a couple of new features +# added (now supports 'noclobber' and 'iclobber' variables). A bug with +# 'eval' that caused "illegal I/O" error messages on vanilla V7 shells has +# also been fixed. + +# I have posted the program in its entirety because a context diff of the +# old and new versions was longer than the new version... + +# --Chris +# Bcsh -- A Simple Cshell-Like Command Pre-Processor For The Bourne Shell +# +# "Copyright (c) Chris Robertson, December 1985" +# +# This software may be used for any purpose provided the original +# copyright notice and this notice are affixed thereto. No warranties of +# any kind whatsoever are provided with this software, and it is hereby +# understood that the author is not liable for any damagages arising +# from the use of this software. +# +# Features Which the Cshell Does Not Have: +# ---------------------------------------- +# +# + command history persists across bcsh sessions +# + global last-command editing via 'g^string1^string2^' syntax +# + edit any command via $EDITOR or $VISUAL editors +# + history file name, .bcshrc file name, alias file name, and number +# of commands saved on termination can be set by environment variables +# + prompt may evaluate commands, such as `pwd`, `date`, etc. +# + the whole text of interactive 'for' and 'while' loops and 'if' +# statements goes into the history list and may be re-run or edited +# + multiple copies of commands and requests to see command history +# are not added to the history list +# + the history mechanism actually stores all commands entered in a +# current session, not just $history of them. This means that you +# can increase $history on the fly and at once have a larger history. +# +# +# Synonyms: +# --------- +# +# logout, exit, bye write out history file and exit +# h, history show current history list +# +# +# Aliases: +# -------- +# +# alias NAME CMND create an alias called NAME to run CMND +# unalias NAME remove the alias NAME +# +# There are no 'current-session only' aliases -- all alias and unalias +# commands are permanent, and stored in the $aliasfile. +# +# If an alias contains positional variables -- $1, $2, $*, etc. -- any +# arguments following the alias name are considered to be values for +# those variables, and the alias is turned into a command of the form +# 'set - arguments;alias'. Otherwise, a simple substitution is performed +# for the alias and the rest of the command preserved. The cshell +# convention of using '\!:n' in an alias to get bits of the current +# command is mercifully abandoned. +# +# Quotes are not necessary around the commands comprising an alias; +# in fact, any enclosing quotes are stripped when the alias is added +# to the file. +# +# A couple of typical aliases might be: +# +# goto cd $1;pwd +# l ls -F +# +# Note that aliasing something to "commands;logout" will not work -- if +# you want something to happen routinely on logout put it in the file +# specified by $logoutfile, default = $HOME/.blogout. +# +# +# Command Substitutions: +# ---------------------- +# +# !! substitute last command from history list +# !!:N substitute Nth element of last command from +# history list -- 0 = command name, 1 = 1st arg +# !!:$ substitute last element of last command from +# history list +# !!:* substitute all arguments to last command +# from history list +# !NUMBER substitute command NUMBER from the history list +# !NUMBER:N as above, but substitute Nth element, where +# 0 = command name, 1 = 1st arg, etc. +# !NUMBER:$ as above, but substitute last element +# !NUMBER:* as above, but substitute all arguments +# !-NUMBER substitute the command NUMBER lines from the +# end of the history list; 1 = last command +# !-NUMBER:N as above, but substitute Nth element, where +# 0 = command name, 1 = 1st arg, etc. +# !-NUMBER:$ as above, but substitute last element +# !-NUMBER:* as above, but substitute all arguments +# !?STRING substitute most-recent command from history list +# containing STRING -- STRING must be enclosed in +# braces if followed by any other characters +# !?STRING:N as above, but substitute Nth element, where +# 0 = command name, 1 = 1st arg, etc. +# !?STRING:$ as above, but substitute last element +# !?STRING:* as above, but substitute all arguments +# +# +# Command Editing: +# ---------------- +# +# CMND~e edit CMND using $EDITOR, where CMND may be found +# using a history substitution +# CMND~v edit CMND using $VISUAL, where CMND may be found +# using a history substitution +# " ^string1^string2^ substitute string2 for string1 in last command" +# command and run it +# " g^string1^string2^ globally substitute string2 for string1 in " +# last command and run it +# !NUMBER:s/string1/string2/ +# substitute string2 for string1 in +# command NUMBER and run it +# !NUMBER:gs/string1/string2/ +# globally substitute string2 for string1 in +# command NUMBER and run it +# !?STRING:s/string1/string2/ +# substitute string2 for string1 in last command +# containing STRING and run it +# !?STRING:gs/string1/string2/ +# globally substitute string2 for string1 in last +# command containing STRING and run it +# +# Any command which ends in the string ":p" is treated as a normal +# command until all substitutions have been completed. The trailing +# ":p" is then stripped, and the command is simply echoed and added to +# the history list instead of being executed. +# +# None of the other colon extensions of the cshell are supported. +# +# +# Shell Environment Variables: +# ---------------------------- +# +# EDITOR editor used by ~e command, default = "ed" +# VISUAL editor used by ~v command, default = "vi" +# MAIL your system mailbox +# PAGER paging program used by history command, default = "more" +# PS1 primary prompt +# PS2 secondary prompt +# history number of commands in history list, default = 22 +# histfile file history list is saved in, default = $HOME/.bhistory +# savehist number of commands remembered from last bcsh session +# aliasfile file of aliased commands, default = $HOME/.baliases +# logoutfile file of commands to be executed before termination +# inc_cmdno yes/no -- keep track of command numbers or not +# noclobber if set, existing files are not overwritten by '>' +# iclobber if both noclobber and iclobber are set, the user is +# prompted for confirmation before existing files are +# overwritten by '>' +# +# Note: if you are setting either noclobber or iclobber mid-session, +# set them to 'yes' +# +# +# Regular Shell Variables: +# ------------------------ +# +# Shell variables may be set via Bourne or cshell syntax, e.g., both +# "set foo=bar" and "foo=bar" set a variable called "foo" with the value +# "bar". However, all variables are automatically set as environment +# variables, so there is no need to export them. Conversely, there +# are NO local variables. Sorry, folks. +# +# A cshell-style "setenv" command is turned into a regular "set" command. +# +# +# The Prompt: +# ---------- +# +# You may, if you wish, have a command executed in your prompt. If +# the variable PS1 contains a dollar sign or a backquote, it is +# evaluated and the result used as the prompt, provided the evaluation +# did not produce a "not found" error message. The two special cases +# of PS1 consisting solely of "$" or "$ " are handled correctly. For +# example, to have the prompt contain the current directory followed +# by a space, enter: +# +# PS1=\'echo "`pwd` "\' +# +# You need the backslashed single quotes to prevent the command being +# evaluated by the variable-setting mechanism and the shell before it +# is assigned to PS1. +# +# To include the command number in your prompt, enter the command: +# +# PS1=\'echo "$cmdno "\' +# +# +# Shell Control-Flow Syntax: +# -------------------------- +# +# 'While', 'for', 'case', and 'if' commands entered in Bourne shell +# syntax are executed as normal. +# +# A valiant attempt is made to convert 'foreach' loops into 'for' loops, +# cshell-syntax 'while' loops into Bourne shell syntax, and 'switch' +# statements into 'case' statements. I cannot guarantee to always get it +# right. If you forget the 'do' in a 'while' or 'for' loop, or finish +# them with 'end' instead of 'done', this will be corrected. +# +# Note that cshell-to-Bourne control flow conversions do not take place +# if control is nested -- e.g., a 'foreach' inside a 'while' will fail. +# +# The simple-case cshell "if (condition) command" is turned into Bourne +# syntax. Other 'if' statements are left alone apart from making the +# 'then' a separate statement, because constructing a valid interactive +# cshell 'if' statement is essentially an exercise in frustration anyway. +# The cshell and Bourne shell have sufficiently different ideas about +# conditions that if is probably best to resign yourself to learning +# the Bourne shell conventions. +# +# Note that since most of the testing built-ins of the cshell are +# not available in the Bourne shell, a complex condition in a 'while' +# loop or an 'if' statement will probably fail. +# +# +# Bugs, Caveats, etc.: +# -------------------- +# +# This is not a super-speedy program. Be patient, especially on startup. +# +# To the best of my knowledge this program should work on ANY Bourne +# shell -- note that if your shell does not understand 'echo -n' you +# will have to re-set the values of '$n' and '$c'. +# +# This program may run out of stack space on a 16-bit machine where +# /bin/sh is not split-space. +# +# Mail checking is done every 10 commands if $MAIL is set in your +# environment. For anything fancier, you will have to hack the code. +# +# Because commands are stuffed in a file before sh is invoked on them, +# error messages from failed commands are ugly. +# +# Failed history substitutions either give nothing at all, or a +# "not found" style of error message. +# +# A command history is kept whether you want it or not. This may be +# perceived as a bug or a feature, depending on which side of bed you +# got out on. +# +# If you want a real backslash in a command, you will have to type two +# of them because the shell swallows the first backslash in the initial +# command pickup. This means that to include a non-history '!' in a +# command you need '\\!' -- a real wart, especially for net mail, +# but unavoidable. +# +# Commands containing an '@' will break all sorts of things. +# +# Very complex history substitutions may fail. +# +# File names containing numbers may break numeric history sustitutions. +# +# Commands containing bizzare sequences of characters may conflict +# with internal kludges. +# +# Aliasing something to "commands;logout" will not work -- if you +# want something to happen routinely on logout, put it in the file +# specified by $logoutfile, default = $HOME/.blogout. +# +# Please send all bug reports to ihnp4!utzoo!globetek!chris. +# Flames will be posted to net.general with 'Reply-to' set to your +# ' path... :-) ' +# +# +# +# ************* VERY IMPORTANT NOTICE ************* +# +# If your shell supports # comments, then REPLACE all the colon 'comments' +# with # comments. If it does not, then REMOVE all the 'comment' lines from the +# working copy of the file, as it will run MUCH faster -- the shell evaluates +# lines starting with a colon but does not actually execute them, so you will +# save the read-and-evaluate time by removing them. + +case "`echo -n foo`" in + -n*) + n= + c="\c" + ;; + foo) + n=-n + c= + ;; + *) + echo "Your 'echo' command is broken." + exit 1 + ;; +esac +history=${history-22} +savehist=${savehist-22} +histfile=${histfile-$HOME/.bhistory} +logoutfile=${logoutfile-$HOME/.blogout} +EDITOR=${EDITOR-ed} +VISUAL=${VISUAL-vi} +PAGER=${PAGER-more} + +aliasfile=${aliasfile-$HOME/.baliases} + +# the alias file may contain 1 blank line, so a test -s will not work + +case "`cat $aliasfile 2> /dev/null`" in + "") + doalias=no + ;; + *) + doalias=yes + ;; +esac + +if test -s "${sourcefile-$HOME/.bcshrc}" + then + . ${sourcefile-$HOME/.bcshrc} +fi + +if test -s "$histfile" + then + cmdno="`set - \`wc -l $histfile\`;echo $1`" + cmdno="`expr \"$cmdno\" + 1`" + lastcmd="`sed -n '$p' $histfile`" + copy=false + ohist=$histfile + while test ! -w "$histfile" + do + echo "Cannot write to history file '$histfile'." + echo $n "Please enter a new history filename: $c" + read histfile + copy=true + done + if $copy + then + cp $ohist $histfile + fi +else + cat /dev/null > $histfile + cmdno=1 + lastcmd= +fi + +# keep track of command number as the default + +inc_cmdno=${inc_cmdo-yes} + +# default prompts -- PS1 and PS2 may be SET but EMPTY, so '${PS1-% }' syntax +# is not used here + +case "$PS1" in + "") + PS1="% " + ;; +esac +case "$PS2" in + "") + PS2="> " + ;; +esac + +export histfile savehist history aliasfile EDITOR VISUAL PAGER cmdno PS1 PS2 + +case "$MAIL" in + "") + ;; + *) + if [ -f $MAIL ]; then + mailsize=`set - \`wc -c $MAIL\`;echo $1` + else + mailsize=0 + fi + ;; +esac + +trap ':' 2 +trap exit 3 +trap "tail -n $savehist $histfile>/tmp/hist$$;uniq /tmp/hist$$ > $histfile;\ +rm -f /tmp/*$$;exit 0" 15 + +getcmd=yes +mailcheck= +exclaim= +echoit= +mailprompt= + +while : +do + + run=yes + case "$mailprompt" in + "") + ;; + *) + echo "$mailprompt" + ;; + esac + case "$getcmd" in + yes) + : guess if the prompt should be evaluated or not + case "$PS1" in + \$|\$\ ) + echo $n "$PS1$c" + ;; + *\`*|*\$*) + tmp="`(eval $PS1) 2>&1`" + case "$tmp" in + *not\ found) + echo $n "$PS1$c" + ;; + *) + echo $n "$tmp$c" + ;; + esac + ;; + *) + echo $n "$PS1$c" + ;; + esac + + read cmd || cmd="exit" + ;; + *) ;; + esac + + case "$MAIL" in + "") + ;; + *) + : check for mail every 10 commands + case "$mailcheck" in + 1111111111) + mailcheck= + if [ -f $MAIL ]; then + newsize="`set - \`wc -c $MAIL\`;echo $1`" + else + newsize=0 + fi + if test "$newsize" -gt "$mailsize"; then + mailprompt="You have new mail" + else + mailprompt= + fi + mailsize=$newsize + ;; + *) + mailcheck=1$mailcheck + ;; + esac + ;; + esac + hist=no + + case "$cmd" in + "") + continue + ;; + sh) + sh + run=no + ;; + !!) + cmd=$lastcmd + echoit=yes + getcmd=no + continue + ;; + *:p) + cmd="`expr \"$cmd\" : '\(.*\):p'` +~+p" + getcmd=no + continue + ;; + foreach[\ \ ]*) + while test "$line" != "end"; do + echo $n "$PS2$c" + read line + cmd="${cmd};$line" + done + echo "$cmd" > /tmp/bcsh$$ + ed - /tmp/bcsh$$ << ++++ + s/end/done/ + s/foreach[ ]\(.*\)(/for \1 in / + s/)// + s/;/;do / + w +++++ + ;; + for[\ \ ]*|while[\ \ ]*) + # try to catch the most common cshell-to-Bourne-shell + # mistakes + + echo $n "$PS2$c" + read line + case "$line" in + *do) + line="do :" + ;; + *do*) + ;; + *) + line="do $line" + ;; + esac + + cmd="${cmd};$line" + while test "$line" != "done" && test "$line" != "end" + do + echo $n "$PS2$c" + read line + case "$line" in + end) + line=done + ;; + esac + cmd="${cmd};$line" + done + echo "$cmd" > /tmp/bcsh$$ + ;; + if[\ \ ]*) + while test "$line" != "fi" && test "$line" != "endif" + do + echo $n "$PS2$c" + read line + case "$line" in + *[a-z]*then) + line="`expr \"$line\" : '\(.*\)then'`;then" + ;; + endif) + line=fi + ;; + esac + cmd="${cmd};$line" + done + echo "$cmd" > /tmp/bcsh$$ + case "`grep then /tmp/bcsh$$`" in + "") + # fix 'if foo bar' cases + + ed - /tmp/bcsh$$ << ++++ + s/)/);then/ + s/.*/;fi/ + w +++++ + ;; + esac + ;; + case[\ \ ]*) + while test "$line" != "esac" + do + echo $n "$PS2$c" + read line + cmd="${cmd}@$line" + done + cmd="`echo \"$cmd\" | tr '@' ' '`" + echo "$cmd" > /tmp/bcsh$$ + ;; + switch[\ \ ]*) + while test "$line" != "endsw" + do + echo $n "$PS2$c" + read line + cmd="${cmd}@$line" + done + echo "$cmd" > /tmp/bcsh$$ + ed - /tmp/bcsh$$ << '++++' + 1,$s/@/\ +/g + g/switch.*(/s//case "/ + s/)/" in/ + 1,$s/case[ ]\(.*\):$/;;\ + \1)/ + 2d + 1,$s/endsw/;;\ +esac/ + g/breaksw/s/// + 1,$s/default.*/;;\ + *)/ + w +++++ + cmd="`cat /tmp/bcsh$$`" + ;; + *!*) + hist=yes + ;; + esac + + case "$hist" in + yes) + # deal with genuine exclamation marks, go back and parse again + + case "$cmd" in + *\>![\ \ ]*|*\\!*) + cmd="`echo \"$cmd\" | sed -e 's@\\!@REALEXCLAMATIONMARK@g'`" + exclaim=yes + getcmd=no + continue + ;; + esac + + # break command into elements, parse each one + + tmp= + for i in $cmd + do + # find element with !, peel off stuff up to ! + + case "$i" in + !) + # most likely a typo for !!, so fix it + front= + $i=!! + ;; + !!*) + front= + i="`expr \"$i\" : '.*\(!!.*\)'`" + ;; + *!!*) + front="`expr \"$i\" : '\(.*\)!!.*'`" + i="`expr \"$i\" : '.*\(!!.*\)'`" + ;; + !*) + front= + i="`expr \"$i\" : '.*!\(.*\)'`" + ;; + *) + tmp="$tmp$i " + continue + ;; + esac + case "$i" in + !!*) + # want last command + + rest="`expr \"$i\" : '!!\(.*\)'`" + i=$lastcmd + ;; + -*) + # we want to search back through the history list + + case "$i" in + -) + rest="`expr \"$i\" : '-\(.*\)'`" + i=$lastcmd + ;; + -[0-9]*) + wanted="`expr \"$i\" : '-\([0-9][0-9]*\).*'`" + rest="`expr \"$i\" : '-[0-9][0-9]*\(.*\)'`" + i="`tail -n $wanted $histfile | sed -e "1q"`" + ;; + esac + ;; + [0-9]*) + # find which number command is wanted + + wanted="`expr \"$i\" : '\([0-9][0-9]*\).*'`" + rest="`expr \"$i\" : '[0-9][0-9]*\(.*\)'`" + i="`grep -n . $histfile | grep \"^$wanted\"`" + i="`expr \"$i\" : \"${wanted}.\(.*\)\"`" + ;; + \?*) + + # find which 'command-contains' match is wanted + + case "$i" in + \?{*}*) + wanted="`expr \"$i\" : '?{\(.*\)}.*'`" + rest="`expr \"$i\" : '?.*}\(.*\)'`" + ;; + \?*:*) + wanted="`expr \"$i\" : '?\(.*\):.*'`" + rest="`expr \"$i\" : '?.*\(:.*\)'`" + ;; + \?*) + wanted="`expr \"$i\" : '?\(.*\)'`" + rest= + ;; + esac + i="`grep \"$wanted\" $histfile | sed -n '$p'`" + ;; + *) + # find which 'start-of-command' match is wanted + + case "$i" in + {*}*) + wanted="`expr \"$i\" : '{\(.*\)}.*'`" + rest="`expr \"$i\" : '.*}\(.*\)'`" + ;; + *:*) + wanted="`expr \"$i\" : '\(.*\):.*'`" + rest="`expr \"$i\" : '.*\(:.*\)'`" + ;; + *) + wanted="$i" + rest= + ;; + esac + i="`grep \"^$wanted\" $histfile | sed -n '$p'`" + ;; + esac + + # see if we actually found anything to substitute + + case "$i" in + "") + badsub="Event not found" + break + ;; + *) + badsub=no + ;; + esac + + case "$rest" in + "") + tmp="$front$tmp$i " + continue + ;; + :[0-9]*) + # find which element of $i is wanted + + number="`expr \"$rest\" : ':\([0-9][0-9]*\).*'`" + rest="`expr \"$rest\" : ':[0-9][0-9]*\(.*\)'`" + + # count through $i till we get to the + # right element + + counter=0 + for element in $i + do + case "$counter" in + $number) + break + ;; + *) + counter="`expr \"$counter\" + 1`" + # counter=$[ $counter + 1 ] + ;; + esac + done + case "$counter" in + $number) + badsub=no + ;; + *) + badsub="Bad command element" + break + ;; + esac + tmp="$tmp$front$element$rest " + continue + ;; + :\$*) + # spin through $i till we hit the last element + + rest="`expr \"$rest\" : ':\$\(.*\)'`" + for element in $i + do + : + done + tmp="$tmp$front$element$rest " + continue + ;; + :\**) + # we want all elements except the command itself + + rest="`expr \"$rest\" : ':\*\(.*\)'`" + save=$i + set - $i + shift + case "$*" in + "") + badsub="No arguments to command '$save'" + break + ;; + *) + badsub=no + ;; + esac + tmp="$tmp$front$*$rest " + continue + ;; + :s*|:gs*) + # we are doing a substitution + # put / on end if needed + + case "$rest" in + :s/*/*/*|:gs/*/*/*) + ;; + :s/*/*|:gs/*/*) + rest="${rest}/" + ;; + esac + + # find what substitution is wanted + + first="`expr \"$rest\" : ':*s\/\(.*\)\/.*\/.*'`" + second="`expr \"$i\" : ':*s/.*/\(.*\)/.*'`" + + # see if it is a global substitution + + case "$rest" in + :gs*) + global=g + ;; + :s*) + global= + ;; + esac + rest="`expr \"$rest\" : '.*/.*/.*/\(.*\)'`" + i="`echo \"$i\" | sed -e \"s@$first@$second@$global\"`" + + # see if subsitution worked + + case "$i" in + "") + badsub="Substiution failed" + break + ;; + *) + badsub=no + ;; + esac + tmp="$tmp$front$i$rest " + continue + ;; + *) + tmp="$tmp$front$i$rest " + ;; + esac + done + case "$badsub" in + no) + ;; + *) + echo "$badsub" + badsub=no + continue + ;; + esac + cmd="$tmp" + echoit=yes + getcmd=no + continue + ;; + *) + run=yes + ;; + esac + + case "$cmd" in + *\^*\^*\^*) + # see if the substitution is global + case "$cmd" in + g*) + global=g + ;; + *) + global= + ;; + esac + + # put a '^' on the end if necessary + case "$cmd" in + *\^) + ;; + *) + cmd="${cmd}^" + ;; + esac + + # find what substitution is wanted + + first="`expr \"$cmd\" : '*\^\(.*\)\^.*\^.*'`" + second="`expr \"$cmd\" : '*\^.*\^\(.*\)\^.*'`" + rest="`expr \"$cmd\" : '*\^.*\^.*\^\(.*\)'`" + cmd="`echo \"$lastcmd\" | sed -e \"s@$first@$second@$global\"`$rest" + + # see if the substitution worked + + case "$cmd" in + "") + echo "Substitution failed" + continue + ;; + esac + echoit=yes + getcmd=no + continue + ;; + *~e) + echo "$cmd" | sed -e "s@~e@@" > /tmp/bcsh$$ + $EDITOR /tmp/bcsh$$ + cmd="`cat /tmp/bcsh$$`" + getcmd=no + continue + ;; + *~v) + echo "$cmd" | sed -e "s@~v@@" > /tmp/bcsh$$ + echo "$lastcmd" > /tmp/bcsh$$ + $VISUAL /tmp/bcsh$$ + cmd="`cat /tmp/bcsh$$`" + getcmd=no + continue + ;; + exec[\ \ ]*) + tail -n $savehist $histfile>/tmp/hist$$ + uniq /tmp/hist$$ > $histfile + rm -f /tmp/*$$ + echo $cmd > /tmp/cmd$$ + . /tmp/cmd$$ + ;; + login[\ \ ]*|newgrp[\ \ ]*) + tail -n $savehist $histfile>/tmp/hist$$ + uniq /tmp/hist$$ > $histfile + rm -f /tmp/*$$ + echo $cmd > /tmp/cmd$$ + . /tmp/cmd$$ + ;; + logout|exit|bye) + if test -s "$logoutfile" + then + # sh $logoutfile + $SHELL $logoutfile + fi + tail -n $savehist $histfile > /tmp/hist$$ + uniq /tmp/hist$$ > $histfile + rm -f /tmp/*$$ + exit 0 + ;; + h|history) + grep -n . $histfile | tail -n $history | sed -e 's@:@ @' | $PAGER + continue + ;; + h[\ \ ]\|*|h[\ \ ]\>*|h\|*|h\>*) + cmd="`echo \"$cmd\" | sed -e \"s@h@grep -n . $histfile | tail -n $history | sed -e 's@:@ @'@\"`" + getcmd=no + continue + ;; + history[\ \ ]*\|*|history[\ \ ]*\>*) + cmd="`echo \"$cmd\" | sed -e \"s@history@grep -n . $histfile | tail -n $history | sed -e 's@:@ @'@\"`" + getcmd=no + continue + ;; + source[\ \ ]*) + set - $cmd + shift + echo . $* > /tmp/cmd$$ + . /tmp/cmd$$ + run=no + ;; + wait) + wait + run=no + ;; + .[\ \ ]*) + echo $cmd > /tmp/cmd$$ + . /tmp/cmd$$ + run=no + ;; + cd|cd[\ \ ]*) + # check if it will work first, or else this shell will terminate + # if the cd dies. If you have a built-in test, you might want + # to replace the try-it-and-see below with a couple of tests, + # but it is probably just as fast like this. + + echo $cmd > /tmp/cmd$$ + if ($SHELL /tmp/cmd$$) ; then + . /tmp/cmd$$ + fi + run=no + ;; + awk[\ \ ]*|dd[\ \ ]*|cc[\ \ ]*|make[\ \ ]*) + # these are the only commands I can think of whose syntax + # includes an equals sign. Add others as you find them. + + echo "$cmd" > /tmp/bcsh$$ + ;; + setenv*|*=*) + # handle setting shell variables, turning cshell syntax to Bourne + # syntax -- note all variables must be exported or they will not + # be usable in other commands + + echo "$cmd" > /tmp/cmd$$ + ed - /tmp/cmd$$ << ++++ + g/^setenv[ ]/s/[ ]/@/ + g/^setenv@/s/[ ]/=/ + g/^setenv@/s/// + g/^set/s/// + .t. + \$s/=.*// + s/^/export / + w +++++ + . /tmp/cmd$$ + rm -f /tmp/cmd$$ + run=no + ;; + unset[\ \ ]*|umask[\ \ ]*|export[\ \ ]*|set[\ \ ]*) + # handle commands which twiddle current environment + + $cmd + run=no + ;; + alias|alias[\ \ ]) + if [ -f $aliasfile ]; then + $PAGER $aliasfile + fi + lastcmd=$cmd + run=no + continue + ;; + alias[\ \ ]*) + case "$cmd" in + alias[\ \ ]\|*|alias[\ \ ]\>*) + cmd="`echo \"$cmd\" | sed -e \"s@alias@cat $aliasfile@\"`" + getcmd=no + continue + ;; + alias[\ \ ]*[\ \ ]*) + ;; + *) + echo "Syntax: alias name command" + cmd= + continue + ;; + esac + set - $cmd + shift + cmd="$*" + + # make sure there is always 1 blank line in file so + # unaliasing will always work -- ed normally refuses + # to write an empty file + echo "" >> $aliasfile + cat << ++++ >> $aliasfile +$cmd +++++ + +# ed - $aliasfile << '++++' +# g/alias[ ]/s/// +# g/^['"]\(.*\)['"]$/s//\1/ +# g/^/s//alias / +# w +#++++ + + sort -u -o $aliasfile $aliasfile + doalias=yes + cmd="alias $cmd" + run=no + ;; + unalias[\ \ ]*) + set - $cmd + case "$#" in + 2) + cmd=$2 + ;; + *) + echo "Syntax: unalias alias_name" + continue + ;; + esac + ed - $aliasfile << ++++ + /^$cmd[ ]/d + w +++++ + case "`set - \`wc -l $aliasfile\`;echo $1`" in + 1) + # just removed last alias + doalias=no + ;; + esac + run=no + ;; + *) + case "$doalias" in + yes) + set - $cmd + tmp="`grep \"^$1 \" $aliasfile`" + case "$tmp" in + $1[\ \ ]*) + shift + cmd=$* + set - $tmp + shift + tmp=$* + case "$tmp" in + *\$*) + # uses positional variables + + cmd="set - $cmd ; $tmp" + getcmd=no + continue + ;; + *) + cmd="$tmp $cmd" + getcmd=no + continue + ;; + esac + ;; + *) + echo "$cmd" > /tmp/bcsh$$ + ;; + esac + ;; + no) + echo "$cmd" > /tmp/bcsh$$ + ;; + esac + ;; + esac + + case "$cmd" in + *+~+p) + cmd="`expr \"$cmd\" : '\(.*\)+~+p'`" + echoit=yes + run=no + ;; + esac + + case "$cmd" in + "") + continue + ;; + *) + case "$exclaim" in + yes) + cmd="`echo \"$cmd\" | sed -e 's@REALEXCLAMATIONMARK@!@g'`" + echo "$cmd" > /tmp/bcsh$$ + ;; + esac + case "$echoit" in + yes) + echo $cmd + ;; + esac + case "$run" in + yes) + case "${noclobber+yes}" in + yes) + case "$cmd" in + *\>![\ \ ]*) + ed - /tmp/bcsh$$ << ++++ + g/>!/s//>/ + w +++++ + ;; + *\>\>*) + ;; + *\>*) + outfile="`expr \"$cmd\" : '.*>\(.*\)'`" + case "$outfile" in + \&*) + ;; + *) + set - $outfile + outfile="$1" + if test -s "$outfile" + then + case "${iclobber+yes}" in + yes) + echo $n "Overwrite ${outfile}? $c" + read answer + case "$answer" in + y*) + ;; + *) + echo ':' > /tmp/bcsh$$ + ;; + esac + ;; + *) + echo "${outfile}: file exists" + echo ':' > /tmp/bcsh$$ + ;; + esac + fi + ;; + esac + ;; + esac + ;; + *) + case "$cmd" in + *\>![\ \ ]*) + ed - /tmp/bcsh$$ << ++++ + g/>!/s//>/g + w +++++ + ;; + esac + ;; + esac + (trap 'exit 1' 2 3; $BASH /tmp/bcsh$$) + ;; + esac + case "$cmd" in + $lastcmd) + ;; + *) + case "$exclaim" in + yes) + cmd="`echo \"$cmd\" | sed -e 's@!@\\\\!@g'`" + ;; + esac + + cat << ++++ >> $histfile +$cmd +++++ + lastcmd=$cmd + + case "$inc_cmdno" in + yes) + cmdno="`expr \"$cmdno\" + 1`" + # cmdno=$[$cmdno + 1] + ;; + esac + ;; + esac + ;; + esac + + # The next commented-out line sets the prompt to include the command + # number -- you should only un-comment this if it is the ONLY thing + # you ever want as your prompt, because it will override attempts + # to set PS1 from the command level. If you want the command number + # in your prompt without sacrificing the ability to change the prompt + # later, replace the default setting for PS1 before the beginning of + # the main loop with the following: PS1='echo -n "${cmdno}% "' + # Doing it this way is, however, slower than the simple version below. + + PS1="${cmdno}% " + + getcmd=yes + echoit=no + exclaim=no +done +exit 0 + +# Christine Robertson {linus, ihnp4, decvax}!utzoo!globetek!chris diff --git a/examples/scripts/cat.sh b/examples/scripts/cat.sh new file mode 100644 index 0000000..78106b2 --- /dev/null +++ b/examples/scripts/cat.sh @@ -0,0 +1,12 @@ +shcat() +{ + while read -r ; do + echo "$REPLY" + done +} + +if [ -n "$1" ]; then + shcat < "$1" +else + shcat +fi diff --git a/examples/scripts/center b/examples/scripts/center new file mode 100644 index 0000000..8d367d3 --- /dev/null +++ b/examples/scripts/center @@ -0,0 +1,24 @@ +#! /bin/bash +# +# center - center a group of lines +# +# tabs in the lines might cause this to look a little bit off +# +# + +width=${COLUMNS:-80} + +if [[ $# == 0 ]] +then + set -- /dev/stdin +fi + +for file +do + while read -r + do + printf "%*s\n" $(( (width+${#REPLY})/2 )) "$REPLY" + done < $file +done + +exit 0 diff --git a/examples/scripts/dd-ex.sh b/examples/scripts/dd-ex.sh new file mode 100644 index 0000000..fafc83f --- /dev/null +++ b/examples/scripts/dd-ex.sh @@ -0,0 +1,476 @@ +#!/bin/sh + +# this is a line editor using only /bin/sh, /bin/dd and /bin/rm + +# /bin/rm is not really required, but it is nice to clean up temporary files + +PATH= +dd=/bin/dd +rm=/bin/rm + +# temporary files we might need +tmp=/tmp/silly.$$ +ed=/tmp/ed.$$ +trap "$rm -f $tmp $tmp.1 $tmp.2 $tmp.3 $tmp.4 $tmp.5 $tmp.6 $ed.a $ed.b $ed.c; exit" 0 1 2 3 + +# from now on, no more rm - the above trap is enough +unset rm + +# we do interesting things with IFS, but better save it... +saveIFS="$IFS" + +# in case "echo" is not a shell builtin... + +Echo () { +case "$1" in + -n) shift + $dd of=$tmp 2>/dev/null <<EOF +$@ +EOF + IFS="+" + set `$dd if=$tmp bs=1 of=/dev/null skip=1 2>&1` + IFS="$saveIFS" + $dd if=$tmp bs=1 count=$1 2>/dev/null + ;; + *) $dd 2>/dev/null <<EOF +$@ +EOF + ;; +esac +} + +# this is used to generate garbage files + +true () { + return 0 +} + +false () { + return 1 +} + +zero () { + ( trap 'go=false' 13 + go=true + while $go + do + $dd "if=$0" + case "$?" in + 0) ;; + *) go=false ;; + esac + done + ) 2>/dev/null +} + +# arithmetic using dd! + +# add variable n1 n2 n3... +# assigns n1+n2+n3+... to variable + +add () { + result="$1" + shift + $dd if=/dev/null of=$tmp bs=1 2>/dev/null + for n in "$@" + do + case "$n" in + 0) ;; + *) zero | $dd of=$tmp.1 bs=1 "count=$n" 2>/dev/null + ( $dd if=$tmp; $dd if=$tmp.1 ) 2>/dev/null | $dd of=$tmp.2 2>/dev/null + $dd if=$tmp.2 of=$tmp 2>/dev/null + ;; + esac + done + IFS="+" + set `$dd if=$tmp bs=1 of=/dev/null 2>&1` + IFS="$saveIFS" + eval $result='$1' +} + +# subtract variable n1 n2 +# subtracts n2 from n1, assigns result to variable + +subtract () { + result="$1" + zero | $dd of=$tmp bs=1 "count=$2" 2>/dev/null + IFS="+" + set `$dd if=$tmp bs=1 of=/dev/null "skip=$3" 2>&1` + IFS="$saveIFS" + case "$1" in + dd*) set 0 ;; + esac + eval $result='$1' +} + +# multiply variable n1 n2 +# variable = n1 * n2 + +multiply () { + result="$1" + zero | $dd "bs=$2" of=$tmp "count=$3" 2>/dev/null + IFS="+" + set `$dd if=$tmp bs=1 of=/dev/null 2>&1` + IFS="$saveIFS" + eval $result='$1' +} + +# divide variable n1 n2 +# variable = int( n1 / n2 ) + +divide () { + result="$1" + zero | $dd bs=1 of=$tmp "count=$2" 2>/dev/null + IFS="+" + set `$dd if=$tmp "bs=$3" of=/dev/null 2>&1` + IFS="$saveIFS" + eval $result='$1' +} + +# compare variable n1 n2 sets variable to lt if n1<n2, gt if n1>n2, eq if n1==n2 + +compare () { + res="$1" + n1="$2" + n2="$3" + subtract somename "$n1" "$n2" + case "$somename" in + 0) ;; + *) eval $res=gt; return; + esac + subtract somename "$n2" "$n1" + case "$somename" in + 0) ;; + *) eval $res=lt; return; + esac + eval $res=eq +} + +# lt n1 n2 returns true if n1 < n2 + +lt () { + n1="$1" + n2="$2" + subtract somename "$n2" "$n1" + case "$somename" in + 0) return 1 ;; + esac + return 0 +} + +# le n1 n2 returns true if n1 <= n2 + +le () { + n1="$1" + n2="$2" + subtract somename "$n1" "$n2" + case "$somename" in + 0) return 0 ;; + esac + return 1 +} + +# gt n1 n2 returns true if n1 > n2 + +gt () { + n1="$1" + n2="$2" + subtract somename "$n1" "$n2" + case "$somename" in + 0) return 1 ;; + esac + return 0 +} + +# ge n1 n2 returns true if n1 >= n2 + +ge () { + n1="$1" + n2="$2" + subtract somename "$n2" "$n1" + case "$somename" in + 0) return 0 ;; + esac + return 1 +} + +# useful functions for the line editor + +# open a file - copy it to the buffers + +open () { + file="$1" + set `$dd "if=$file" of=/dev/null 2>&1` + case "$1" in + dd*) return 1 + esac + # copy the first line to $ed.c + go=true + len=0 + while $go + do + case "`$dd "if=$file" bs=1 skip=$len count=1 2>/dev/null`" in + ?*) go=true ;; + *) go=false ;; + esac + add len 1 $len + done + # now $len is the length of the first line (including newline) + $dd "if=$file" bs=1 count=$len of=$ed.c 2>/dev/null + $dd "if=$file" bs=1 skip=$len of=$ed.b 2>/dev/null + $dd if=/dev/null of=$ed.a 2>/dev/null + lineno=1 +} + +# save a file - copy the buffers to the file + +save () { + # make a backup copy of the original + $dd "if=$1" "of=$1.bak" 2>/dev/null + # and save + ( $dd if=$ed.a; $dd if=$ed.c; $dd if=$ed.b ) > "$1" 2>/dev/null +} + +# replace n1 n2 bla replaces n2 chars of current line, starting n1-th + +replace () { + $dd if=$ed.c of=$tmp.1 bs=1 "count=$1" 2>/dev/null + ( $dd if=$ed.c "skip=$1" bs=1 | $dd of=$tmp.2 bs=1 "skip=$2" ) 2>/dev/null + shift + shift + ( $dd if=$tmp.1; Echo -n "$@"; $dd if=$tmp.2 ) > $tmp.3 2>/dev/null + $dd if=$tmp.3 of=$ed.c 2>/dev/null +} + +# rstring n s bla +# replace the n-th occurence of s with bla + +rstring () { + n="$1" + shift; + # first we have to find it - this is fun! + # we have $tmp.4 => text before string, $tmp.5 => text after + $dd if=/dev/null of=$tmp.4 2>/dev/null + $dd if=$ed.c of=$tmp.5 2>/dev/null + string="$1" + shift + $dd of=$tmp.6 2>/dev/null <<EOF +$@ +EOF + while : + do + case "`$dd if=$tmp.5 2>/dev/null`" in + $string*) + if lt $n 2 + then + # now we want to replace the string + Echo -n "$@" > $tmp.2 + Echo -n "$string" > $tmp.1 + IFS="+" + set `$dd bs=1 if=$tmp.1 of=/dev/null 2>&1` + IFS="$saveIFS" + slen=$1 + IFS="+" + ( $dd if=$tmp.4; $dd if=$tmp.2; $dd if=$tmp.5 bs=1 skip=$slen ) \ + 2>/dev/null > $tmp + $dd if=$tmp of=$ed.c 2>/dev/null + return 0 + else + subtract n $n 1 + ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null + $dd if=$tmp of=$tmp.4 2>/dev/null + # and remove it from $tmp.5 + $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null + $dd if=$tmp of=$tmp.5 2>/dev/null + fi + ;; + ?*) # add one more byte... + ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null + $dd if=$tmp of=$tmp.4 2>/dev/null + # and remove it from $tmp.5 + $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null + $dd if=$tmp of=$tmp.5 2>/dev/null + ;; + *) # not found + return 1 + ;; + esac + done +} + +# skip to next line +next () { + add l $lineno 1 + ( $dd if=$ed.a; $dd if=$ed.c ) 2>/dev/null > $tmp.3 + $dd if=$ed.b of=$tmp.4 2>/dev/null + open $tmp.4 + $dd if=$tmp.3 of=$ed.a 2>/dev/null + lineno=$l +} + +# delete current line +delete () { + l=$lineno + $dd if=$ed.a 2>/dev/null > $tmp.1 + $dd if=$ed.b of=$tmp.2 2>/dev/null + open $tmp.2 + $dd if=$tmp.1 of=$ed.a 2>/dev/null + lineno=$l +} + +# insert before current line (without changing current) +insert () { + ( $dd if=$ed.a; Echo "$@" ) 2>/dev/null > $tmp.1 + $dd if=$tmp.1 of=$ed.a 2>/dev/null + add lineno $lineno 1 +} + +# previous line +prev () { + case "$lineno" in + 1) ;; + *) subtract lineno $lineno 1 + # read last line of $ed.a + IFS='+' + set `$dd if=$ed.a of=/dev/null bs=1 2>&1` + IFS="$saveIFS" + size=$1 + # empty? + case "$size" in + 0) return ;; + esac + subtract size $size 1 + # skip final newline + case "$size" in + 0) ;; + *) subtract size1 $size 1 + case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in + ?*) ;; + *) size=$size1 ;; + esac + ;; + esac + go=true + while $go + do + case "$size" in + 0) go=false ;; + *) case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in + ?*) go=true; subtract size $size 1 ;; + *) go=false; add size $size 1 ;; + esac + ;; + esac + done + # now $size is the size of the first n-1 lines + # add $ed.c to $ed.b + ( $dd if=$ed.c; $dd if=$ed.b ) 2>/dev/null > $tmp.5 + $dd if=$tmp.5 of=$ed.b 2>/dev/null + # move line to ed.c + case "$size" in + 0) $dd if=$ed.a of=$ed.c 2>/dev/null + $dd if=/dev/null of=$tmp.5 2>/dev/null + ;; + *) $dd if=$ed.a of=$ed.c bs=1 skip=$size 2>/dev/null + $dd if=$ed.a of=$tmp.5 bs=1 count=$size 2>/dev/null + ;; + esac + # move rest to ed.a + $dd if=$tmp.5 of=$ed.a 2>/dev/null + ;; + esac +} + +# goes to a given line +goto () { + rl="$1" + compare bla "$rl" $lineno + case "$bla" in + eq) return + ;; + gt) while gt "$rl" $lineno + do + next + done + ;; + lt) while lt "$rl" $lineno + do + prev + done + ;; + esac +} + +lineout () { + Echo -n "$lineno: " + $dd if=$ed.c 2>/dev/null +} + +state=closed +name= +autoprint=true + +while true +do + Echo -n '> ' + read cmd arg + case "$cmd:$state" in + open:open) Echo "There is a file open already" ;; + open:*) if open "$arg" + then state=open; name="$arg"; $autoprint + else Echo "Cannot open $arg" + fi + ;; + new:open) Echo "There is a file open already" ;; + new:*) open "$arg" + state=open + name="$arg" + $autoprint + ;; + close:changed) Echo "Use 'discard' or 'save'" ;; + close:closed) Echo "Closed already" ;; + close:*) state=closed ;; + save:closed) Echo "There isn't a file to save" ;; + save:*) case "$arg" in + ?*) save "$arg" ;; + *) save "$name" ;; + esac + state=open + ;; + discard:changed) Echo "Your problem!"; state=closed ;; + discard:*) state=closed ;; + print:closed) Echo "No current file" ;; + print:*) lineout ;; + goto:closed) Echo "No current file" ;; + goto:*) goto "$arg"; $autoprint ;; + next:closed) Echo "No current file" ;; + next:*) next; $autoprint ;; + prev:closed) Echo "No current file" ;; + prev:*) prev; $autoprint ;; + name:closed) Echo "No current file" ;; + name:*) name="$arg" ;; + replace:closed) Echo "No current file" ;; + replace:*) if rstring 1 $arg + then state=changed; $autoprint + else Echo "Not found" + fi + ;; + nreplace:closed) Echo "No current file" ;; + nreplace:*) if rstring $arg + then state=changed; $autoprint + else Echo "Not found" + fi + ;; + delete:closed) Echo "No current file" ;; + delete:*) delete; state=changed; $autoprint ;; + insert:closed) Echo "No current file" ;; + insert:*) insert "$arg"; prev; state=changed; $autoprint ;; + quit:changed) Echo "Use 'save' or 'discard'" ;; + quit:*) Echo "bye"; exit;; + autoprint:*) autoprint="lineout" ;; + noprint:*) autoprint="" ;; + :*) ;; + *) Echo "Command not understood" ;; + esac +done + diff --git a/examples/scripts/fixfiles.bash b/examples/scripts/fixfiles.bash new file mode 100644 index 0000000..15f3ba8 --- /dev/null +++ b/examples/scripts/fixfiles.bash @@ -0,0 +1,92 @@ +#! /bin/bash +# +# From: c2a192@ugrad.cs.ubc.ca (Kazimir Kylheku) +# Newsgroups: comp.unix.shell,comp.os.linux.misc +# Subject: GNU Bash Script to fix filenames +# Date: 28 Mar 1996 14:54:43 -0800 +# Organization: Computer Science, University of B.C., Vancouver, B.C., Canada +# +#This is a script which takes a list of directories, descends through each one +#and ``corrects'' filenames that: +# +# - contain filename globbing characters: * ? [ ] +# - quote characters: ' " +# - control characters: 0-31 (127 is not dealt with---oops) +# - - or + as the first character +# +# The GNU version of 'tr' is required. Also requires 'sed'. +# +# Script to process a given list of directories recursively +# and rename each file to something that is reasonable. +# +# The rules are: +# +# 1. replace each space, [, ], *, ", and ' character in the name with a +# period. +# 2. replace each control character 1..31 with a printable character obtained +# by adding 64 to the ascii value. ^A becomes A, ^B becomes B and so on. +# 3. replace a - or + occuring at the beginning of the name with a # +# +# 4. if the resulting name has been changed in any way, then +# 5. if a file of the new name already exists, then +# 6. add a . to the new name and goto step 5. +# 7. rename the old name to the new name +# +# written by Kaz Kylheku <kaz@cafe.net> +# March 1996 +# Vancouver, Canada +# +# requires GNU 'bash', GNU 'tr', and some sort of 'sed' program. +# +# minimal conversion to bash v2 syntax done by Chet Ramey + +processfile() +{ + new_name="`echo -n $1 | tr '\173\175\052\077\042\047 ' '.......' | + tr '[\000-\037]' '[\100-\137]' | + sed -e 's/^-/#/' -e 's/+/#/'`" + if [ "$new_name" != "$1" ] ; then + while [ -e "$new_name" ] ; do + new_name="${new_name}." + done + echo changing \"$1\" to \"$new_name\" in `pwd` + mv -- "$1" "$new_name" + fi +} + +processdir() +{ + set -f + local savepwd="$PWD" + if cd "$1" ; then + set +f + for file in * ; do + set -f + if [ "$file" != "." ] && [ "$file" != ".." ] ; then + if [ -L "$file" ] ; then + echo "skipping symlink" $file in `pwd` + elif [ -d "$file" ] ; then + processdir "$file" + elif [ -f "$file" ] ; then + processfile "$file" + fi + fi + done + cd "$savepwd" + fi +} + +shopt -s nullglob dotglob + +if [ $# = 0 ] ; then + echo "$0: must specify a list of directories" >&2 + echo "$0: usage: $0 directory [directory ...]" >&2 + exit 2 +fi + +while [ $# != 0 ] ; do + processdir "$1" + shift +done + +exit 0 diff --git a/examples/scripts/hanoi.bash b/examples/scripts/hanoi.bash new file mode 100644 index 0000000..c308125 --- /dev/null +++ b/examples/scripts/hanoi.bash @@ -0,0 +1,21 @@ +# Towers of Hanoi in bash +# +# cribbed from the ksh93 book, example from exercises on page 85 +# +# Chet Ramey +# chet@po.cwru.edu + +hanoi() # n from to spare +{ + typeset -i nm1=$1-1 + ((nm1>0)) && hanoi $nm1 $2 $4 $3 + echo "Move disc $2 to $3" + ((nm1>0)) && hanoi $nm1 $4 $3 $2 +} + +case $1 in +[1-9]) + hanoi $1 1 2 3;; +*) echo "${0##*/}: Argument must be from 1 to 9" + exit 1;; +esac diff --git a/examples/scripts/inpath b/examples/scripts/inpath new file mode 100755 index 0000000..95f28bc --- /dev/null +++ b/examples/scripts/inpath @@ -0,0 +1,19 @@ +#! /bin/sh +# +# Search $PATH for a file the same name as $1; return TRUE if found. +# + +command=$1 +[ -n "$command" ] || exit 1 + +set `echo $PATH | sed 's/^:/.:/ + s/::/:.:/g + s/:$/:./ + s/:/ /g'` + +while [ $# -ne 0 ] ; do + [ -f $1/$command ] && exit 0 # test -x not universal + shift +done + +exit 1 diff --git a/examples/scripts/krand.bash b/examples/scripts/krand.bash new file mode 100755 index 0000000..dfdfd32 --- /dev/null +++ b/examples/scripts/krand.bash @@ -0,0 +1,74 @@ +# Originally +# +# From: bsh20858@news.fhda.edu (Brian S Hiles) +# Newsgroups: comp.unix.shell +# Subject: Re: getting random numbers +# Date: 23 Jan 1997 23:27:30 GMT +# Message-ID: <5c8s52$eif@tiptoe.fhda.edu> + +# @(#) krand Produces a random number within integer limits +# "krand" Korn shell script generates a random number in a +# specified range with an optionally specified ``seed'' value. +# Author: Peter Turnbull, May 1993 +# Modified by: Becca Thomas, January 1994 + +# changed the optional third argument to a -s option, converted to +# bash v2 syntax -- chet@po.cwru.edu + +PROGNAME=${0##*/} +USAGE="usage: $PROGNAME [-s seed] lower-limit upper-limit" + +Seed=$$ # Initialize random-number seed value with PID + +usage() +{ + echo ${PROGNAME}: "$USAGE" >&2 +} + +errexit() +{ + echo ${PROGNAME}: "$@" >&2 + exit 1 +} + +# Process command-line arguments: +while getopts "s:" opt; do + case "$opt" in + s) Seed=$OPTARG ;; + *) usage ; exit 2;; + esac +done + +shift $(($OPTIND - 1)) + +case $# in + 2) Lower=$1; Upper=$2 ;; + *) usage ; exit 2;; +esac + +# Check that specified values are integers: +expr "$Lower" + 0 >/dev/null 2>&1 +[ $? -eq 2 ] && { errexit "lower ($Lower) not an integer"; } +expr "$Upper" + 0 >/dev/null 2>&1 +[ $? -eq 2 ] && { errexit "upper ($Upper) not an integer"; } +expr "$Seed" + 0 >/dev/null 2>&1 +[ $? -eq 2 ] && { errexit "seed ($Seed) not an integer"; } + +# Check that values are in the correct range: +if (( "$Lower" < 0 )) || [ ${#Lower} -gt 5 ]; then + errexit "lower limit ($Lower) less than zero" +fi +if (( "$Upper" > 32767 )) || [ ${#Upper} -gt 5 ]; then + errexit "upper limit ($Upper) greater than 32767" +fi +if (( "$Seed" < 0 )) || (( "$Seed" > 32767 )) || [ ${#Seed} -gt 5 ]; then + errexit "seed value ($Seed) out of range (0 to 32767)" +fi +(( "$Upper" <= "$Lower" )) && errexit "upper limit ($Upper) <= lower limit ($Lower)" + +# Seed the random-number generator: +RANDOM=$Seed +# Compute value, scaled within range: +let rand="$RANDOM % ($Upper - $Lower + 1) + $Lower" +# Report result: +echo $rand diff --git a/examples/scripts/line-input.bash b/examples/scripts/line-input.bash new file mode 100644 index 0000000..3f2efae --- /dev/null +++ b/examples/scripts/line-input.bash @@ -0,0 +1,184 @@ +#! /bin/bash +# +#From: kaz@cafe.net (Kaz Kylheku) +#Newsgroups: comp.unix.shell +#Subject: Funky little bash script +#Message-ID: <6mspb9$ft2@espresso.cafe.net> +#Date: Thu, 25 Jun 1998 06:11:39 GMT + +#Here is something I wrote a few years ago when I was bored one day. +#Warning: this contains control characters. + +# Line input routine for GNU Bourne-Again Shell +# plus terminal-control primitives. +# +# by Kaz Kylheku +# June 1996, Vancouver, Canada + + +# +# Function to disable canonical input processing. +# Terminal modes are saved into variable "savetty" +# +# + +function raw +{ + savetty=$(stty -g) + stty -icanon -isig -echo -echok -echonl inlcr +} + +# +# Function to restore terminal settings from savetty variable +# + +function restore +{ + stty $savetty +} + +# +# Set terminal MIN and TIME values. +# If the input argument is a zero, set up terminal to wait for +# a keystroke indefinitely. If the argument is non-zero, set up +# an absolute timeout of that many tenths of a second. The inter-keystroke +# timer facility of the terminal driver is not exploited. +# + +function settimeout +# $1 = tenths of a second +{ + if [ "$1" = "0" ] ; then + min=1 + timeout=0 + else + min=0 + timeout="$1" + fi + + stty min $min time $timeout + + unset min timeout +} + +# +# Input a single key using 'dd' and echo it to standard output. +# Launching an external program to get a single keystroke is a bit +# of a pig, but it's the best you can do! Maybe we could convince the +# GNU guys to make 'dd' a bash builtin. +# + +function getkey +{ + eval $1="\"\$(dd bs=1 count=1 2> /dev/null)\"" +} + +# +# Input a line of text gracefully. +# The first argument is the name of a variable where the input line is +# to be stored. If this variable is not empty, its contents are printed +# and treated as though the user had entered them. +# The second argument gives the maximum length of the input line; if it +# is zero, the input is unlimited (bad idea). +# ^W is used to delete words +# ^R redraws the line at any time by backspacing over it and reprinting it +# ^U backspaces to the beginning +# ^H or ^? (backspace or del) delete a single character +# ^M (enter) terminates the input +# all other control keys are ignored and cause a beep when pressed +# +# + + +function getline +{ + settimeout 0 # No keystroke timeout. + save_IFS="$IFS" # Save word delimiter and set it to + IFS="" # to null so ${#line} works correctly. + eval line=\${$1} # Fetch line contents + echo -n "$line" # and print the existing line. + while [ 1 ] ; do + getkey key # fetch a single keystroke + case "$key" in + | ) # BS or DEL + if [ ${#line} != 0 ] ; then # if line not empty + echo -n " " # print destructive BS + line="${line%%?}" # chop last character + else # else if line empty + echo -n # beep the terminal + fi + ;; + ) # kill to line beg + while [ ${#line} != 0 ] ; do # while line not empty + echo -n " " # print BS, space, BS + line="${line%?}" # shorten line by 1 + done + ;; + ) # redraw line + linesave="$line" # save the contents + while [ ${#line} != 0 ] ; do # kill to line beg + echo -n " " + line="${line%?}" + done + echo -n "$linesave" # reprint, restore + line="$linesave" + unset linesave # forget temp var + ;; + ) + while [ "${line% }" != "$line" ] && [ ${#line} != 0 ] ; do + echo -n " " + line="${line%?}" + done + while [ "${line% }" = "$line" ] && [ ${#line} != 0 ] ; do + echo -n " " + line="${line%?}" + done + ;; + | | | | | | | | | | |
) + echo -n # ignore various control characters + ;; # with an annoying beep + | | | | | | | | | | | | ) + echo -n + ;; + ' ' | | | | | ) + echo -n + ;; + '' ) # Break out of loop on carriage return. + echo # Send a newline to the terminal. + break # (Also triggered by NUL char!). + ;; + * ) # Append character to the end of the line. + # If length is restricted, and the line is too + # long, then beep... + + if [ "$2" != 0 ] && [ $(( ${#line} >= $2 )) = 1 ] ; then + echo -n + else # Otherwise add + line="$line$key" # the character. + echo -n "$key" # And echo it. + fi + ;; + esac + done + eval $1=\"\$line\" + IFS="$save_IFS" + unset line save_IFS +} + +# uncomment the lines below to create a standalone test program +# +echo "Line input demo for the GNU Bourne-Again Shell." +echo "Hacked by Kaz Kylheku" +echo +echo "Use ^H/Backspace/Del to erase, ^W to kill words, ^U to kill the" +echo "whole line of input, ^R to redraw the line." +echo "Pass an argument to this program to prime the buffer contents" +raw +echo -n "go: " +if [ ${#1} != 0 ] ; then + LINE=$1 +fi +getline LINE 50 +restore + +echo "<$LINE>" diff --git a/examples/scripts/nohup.bash b/examples/scripts/nohup.bash new file mode 100644 index 0000000..3781293 --- /dev/null +++ b/examples/scripts/nohup.bash @@ -0,0 +1,51 @@ +# +# BASH VERSION OF nohup COMMAND +# +ctype() +{ + path=$(builtin type -p $cmd | sed 1q) + if [ -n "$path" ]; then + echo "$path" + return 0 + else + case "$cmd" in + */*) [ -x "$cmd ] && { echo "$cmd" ; return 0; } ;; + *) case "$(builtin type -t $cmd)" in + "") return 1;; + *) echo "$cmd" ; return 0;; + esac ;; + esac + fi + return 1 +} + +trap '' HUP # ignore hangup +command=$(ctype "$1") +oldmask=$(umask) +umask u=rw,og= # default mode for nohup.out +exec 0< /dev/null # disconnect input +if [ -t 1 ]; then # redirect output if necessary + if [ -w . ]; then + echo 'Sending output to nohup.out' + exec >> nohup.out + else echo "Sending output to $HOME/nohup.out" + exec >> $HOME/nohup.out + fi +fi + +umask "$oldmask" + +# direct unit 2 to a file +if [ -t 2 ]; then + exec 2>&1 +fi + +# run the command +case $command in +*/*) exec "$@" + ;; +time) eval "$@" + ;; +*) "$@" + ;; +esac diff --git a/examples/scripts/precedence b/examples/scripts/precedence new file mode 100755 index 0000000..9bbdb97 --- /dev/null +++ b/examples/scripts/precedence @@ -0,0 +1,75 @@ +# @(#)precedence_test 1.0 91/07/24 Maarten Litmaath +# test of relative precedences for `&&' and `||' operators + +echo "\`Say' echos its argument. Its return value is of no interest." +case `echo -n` in + '') Say () { echo -n "$*" ; } ;; + *) Say () { echo "$*\c" ; } ;; +esac + +echo "\`Truth' echos its argument and returns a TRUE result." +Truth () { + Say $1; + return 0; +} + +echo "\`False' echos its argument and returns a FALSE result." +False () { + Say $1; + return 1; +} + +echo "" + +cmd1='$open $test1 && $test2 $close || $test3' +cmd2='$test1 || $open $test2 && $test3 $close' + +grouping_sh= +grouping_C='( )' + +test3='Say 3' + +for i in 1 2 +do + eval proto=\$cmd$i + + for test1 in 'Truth 1' 'False 1' + do + for test2 in 'Truth 2' 'False 2' + do + for precedence in sh C + do + eval set x \$grouping_$precedence + shift + open=${1-' '} + close=${2-' '} + eval cmd=\""$proto"\" + Say "$cmd output=" + output=`eval "$cmd"` + Say "$output" + read correct || { echo 'Input fubar. Abort.' >&2; exit 1; } + test "X$output" = "X$correct" || echo " correct=$correct" + echo '' + done + + echo '' + done + done +done << EOF +12 +12 +123 +123 +13 +13 +13 +13 +13 +1 +13 +1 +123 +123 +12 +12 +EOF diff --git a/examples/scripts/randomcard.bash b/examples/scripts/randomcard.bash new file mode 100644 index 0000000..9cb6b50 --- /dev/null +++ b/examples/scripts/randomcard.bash @@ -0,0 +1,18 @@ +# The following prints a random card from a card deck. +# +# cribbed from the ksh93 book, example from page 70 +# +# chet@po.cwru.edu +# +declare -i i=0 + +# load the deck +for suit in clubs diamonds hearts spades; do + for n in ace 2 3 4 5 6 7 8 9 10 jack queen king; do + card[i]="$n of $suit" + i=i+1 # let is not required with integer variables + done +done + +# and print a random card +echo ${card[RANDOM%52]} diff --git a/examples/scripts/scrollbar b/examples/scripts/scrollbar new file mode 100755 index 0000000..388bea8 --- /dev/null +++ b/examples/scripts/scrollbar @@ -0,0 +1,25 @@ +#!/bin/bash +# +# scrollbar - display scrolling text +# +# usage: scrollbar args +# +# A cute hack originally from Heiner Steven <hs@bintec.de> +# +# converted from ksh syntax to bash v2 syntax by Chet Ramey + +WIDTH=${COLUMNS:-80} +WMINUS=$(( $WIDTH - 1 )) + +[ $# -lt 1 ] && set -- TESTING + +# use the bash-2.02 printf builtin +Text=$(printf "%-${WIDTH}s" "$*") +Text=${Text// /_} + +while : +do + printf "%-.${WIDTH}s\r" "$Text" + LastC=${Text:${WMINUS}:1} + Text="$LastC""${Text%?}" +done diff --git a/examples/scripts/scrollbar2 b/examples/scripts/scrollbar2 new file mode 100755 index 0000000..0e53634 --- /dev/null +++ b/examples/scripts/scrollbar2 @@ -0,0 +1,24 @@ +#!/bin/bash +# +# scrollbar - display scrolling text +# +# usage: scrollbar args +# +# A cute hack originally from Heiner Steven <hs@bintec.de> +# +# converted from ksh syntax to bash v2 syntax by Chet Ramey + +WIDTH=${COLUMNS:-80} +WMINUS=$(( $WIDTH - 1 )) + +[ $# -lt 1 ] && set -- TESTING + +# use the bash-2.02 printf builtin +Text=$(printf "%-${WIDTH}s" "$*") + +while : +do + printf "%-.${WIDTH}s\r" "$Text" + LastC=${Text:${WMINUS}:1} + Text="$LastC""${Text%?}" +done diff --git a/examples/scripts/self-repro b/examples/scripts/self-repro new file mode 100644 index 0000000..951d4e4 --- /dev/null +++ b/examples/scripts/self-repro @@ -0,0 +1,9 @@ +# self-reproducing script (except for these comment lines -- remove them) +# i got this from the ksh93 faq: +# http://www.kornshell.com/doc/faq.html +# +n=" +" q="'" x="cat <<-!" y=! z='n="$n" q="$q" x="$x" y=$y z=$q$z$q$n$x$n$z$n$y' +cat <<-! +n="$n" q="$q" x="$x" y=$y z=$q$z$q$n$x$n$z$n$yb +! diff --git a/examples/scripts/showperm.bash b/examples/scripts/showperm.bash new file mode 100644 index 0000000..2e06c0b --- /dev/null +++ b/examples/scripts/showperm.bash @@ -0,0 +1,53 @@ +#Newsgroups: comp.unix.shell +#From: gwc@root.co.uk (Geoff Clare) +#Subject: Re: Determining permissions on a file +#Message-ID: <Dr79nw.DtL@root.co.uk> +#Date: Fri, 10 May 1996 17:23:56 GMT + +#Here's a bit of Korn shell that converts the symbolic permissions produced +#by "ls -l" into octal, using only shell builtins. How to create a script +#combining this with an "ls -l" is left as an exercise... +# +# +# Converted to Bash v2 syntax by Chet Ramey <chet@po.cwru.edu> +# +# usage: showperm modestring +# +# example: showperm '-rwsr-x--x' +# + +[ -z "$1" ] && { + echo "showperm: usage: showperm modestring" >&2 + exit 2 +} + +tmode="$1" + +typeset -i omode sbits +typeset pmode + +# check for set-uid, etc. bits +sbits=0 +case $tmode in +???[sS]*) (( sbits += 8#4000 )) ;; # set-uid +??????[sSl]*) (( sbits += 8#2000 )) ;; # set-gid or mand. lock +?????????[tT]*) (( sbits += 8#1000 )) ;; # sticky +esac + +omode=0 +while : +do + tmode=${tmode#?} + case $tmode in + "") break ;; + [-STl]*) (( omode *= 2 )) ;; + [rwxst]*) (( omode = omode*2 + 1 )) ;; + *) echo "$0: first letter of \"$tmode\" is unrecognized" >&2 + (( omode *= 2 )) + ;; + esac +done + +(( omode += sbits )) + +printf "0%o\n" $omode diff --git a/examples/scripts/shprompt b/examples/scripts/shprompt new file mode 100755 index 0000000..ec8b997 --- /dev/null +++ b/examples/scripts/shprompt @@ -0,0 +1,137 @@ +# +# shprompt -- give a prompt and get an answer satisfying certain criteria +# +# shprompt [-dDfFsy] prompt +# s = prompt for string +# f = prompt for filename +# F = prompt for full pathname to a file or directory +# d = prompt for a directory name +# D = prompt for a full pathname to a directory +# y = prompt for y or n answer +# +# Chet Ramey +# chet@ins.CWRU.Edu + +type=file + +OPTS=dDfFsy + +succeed() +{ + echo "$1" + exit 0 +} + +while getopts "$OPTS" c +do + case "$c" in + s) type=string + ;; + f) type=file + ;; + F) type=path + ;; + d) type=dir + ;; + D) type=dirpath + ;; + y) type=yesno + ;; + ?) echo "usage: $0 [-$OPTS] prompt" 1>&2 + exit 2 + ;; + esac +done + +if [ "$OPTIND" -gt 1 ] ; then + shift $[$OPTIND - 1] +fi + +while : +do + case "$type" in + string) + echo -n "$1" 1>&2 + read ans || exit 1 + if [ -n "$ans" ] ; then + succeed "$ans" + fi + ;; + file|path) + echo -n "$1" 1>&2 + read ans || exit 1 + # + # use `fn' and eval so that bash will do tilde expansion for + # me + # + eval fn="$ans" + case "$fn" in + /*) if test -e "$fn" ; then + succeed "$fn" + else + echo "$0: '$fn' does not exist" 1>&2 + fi + ;; + *) if [ "$type" = "path" ] ; then + echo "$0: must give full pathname to file" 1>&2 + else + if test -e "$fn" ; then + succeed "$fn" + else + echo "$0: '$fn' does not exist" 1>&2 + fi + fi + ;; + esac + ;; + dir|dirpath) + echo -n "$1" 1>&2 + read ans || exit 1 + # + # use `fn' and eval so that bash will do tilde expansion for + # me + # + eval fn="$ans" + case "$fn" in + /*) if test -d "$fn" ; then + succeed "$fn" + elif test -e "$fn" ; then + echo "$0 '$fn' is not a directory" 1>&2 + else + echo "$0: '$fn' does not exist" 1>&2 + fi + ;; + *) if [ "$type" = "dirpath" ] ; then + echo "$0: must give full pathname to directory" 1>&2 + else + if test -d "$fn" ; then + succeed "$fn" + elif test -e "$fn" ; then + echo "$0 '$fn' is not a directory" 1>&2 + else + echo "$0: '$fn' does not exist" 1>&2 + fi + fi + ;; + esac + ;; + yesno) + echo -n "$1" 1>&2 + read ans || exit 1 + case "$ans" in + y|Y|[yY][eE][sS]) + succeed "yes" + ;; + n|N|[nN][oO]) + succeed "no" + exit 0 + ;; + *) + echo "$0: yes or no required" 1>&2 + ;; + esac + ;; + esac +done + +exit 1 diff --git a/examples/scripts/spin.bash b/examples/scripts/spin.bash new file mode 100644 index 0000000..dc6a66a --- /dev/null +++ b/examples/scripts/spin.bash @@ -0,0 +1,21 @@ +#!/bin/bash +# +# spin.bash -- provide a `spinning wheel' to show progress +# +# Chet Ramey +# chet@po.cwru.edu +# +bs=$'\b' + +chars="|${bs} \\${bs} -${bs} /${bs}" + +# Infinite loop for demo. purposes +while : +do + for letter in $chars + do + echo -n ${letter} + done +done + +exit 0 diff --git a/examples/scripts/timeout b/examples/scripts/timeout new file mode 100644 index 0000000..ac8d88f --- /dev/null +++ b/examples/scripts/timeout @@ -0,0 +1,53 @@ +#Newsgroups: comp.unix.admin,comp.unix.solaris,comp.unix.shell +#From: gwc@root.co.uk (Geoff Clare) +#Subject: Re: timeout -t <sec> <unix command> (Re: How to give rsh a shorter timeout?) +#Message-ID: <EoBxrs.223@root.co.uk> +#Date: Fri, 13 Feb 1998 18:23:52 GMT + +# +# Conversion to bash v2 syntax done by Chet Ramey <chet@po.cwru.edu +# UNTESTED +# + +prog=${0##*/} +usage="usage: $prog [-signal] [timeout] [:interval] [+delay] [--] <command>" + +SIG=-TERM # default signal sent to the process when the timer expires +timeout=60 # default timeout +interval=15 # default interval between checks if the process is still alive +delay=2 # default delay between posting the given signal and + # destroying the process (kill -KILL) + +while : +do + case $1 in + --) shift; break ;; + -*) SIG=$1 ;; + [0-9]*) timeout=$1 ;; + :*) EXPR='..\(.*\)' ; interval=`expr x"$1" : "$EXPR"` ;; + +*) EXPR='..\(.*\)' ; delay=`expr x"$1" : "$EXPR"` ;; + *) break ;; + esac + shift +done + +case $# in +0) echo "$prog: $usage" >&2 ; exit 2 ;; +esac + +( + for t in $timeout $delay + do + while (( $t > $interval )) + do + sleep $interval + kill -0 $$ || exit + t=$(( $t - $interval )) + done + sleep $t + kill $SIG $$ && kill -0 $$ || exit + SIG=-KILL + done +) 2> /dev/null & + +exec "$@" diff --git a/examples/scripts/vtree2 b/examples/scripts/vtree2 new file mode 100755 index 0000000..878cbab --- /dev/null +++ b/examples/scripts/vtree2 @@ -0,0 +1,43 @@ +#!/bin/bash +# +# vtree - make a tree printout of the specified directory, with disk usage +# in 1k blocks +# +# usage: vtree [-a] [dir] +# +# Original posted to Usenet sometime in February, 1996 +# I believe that the original author is Brian S. Hiles <bsh29256@atc.fhda.edu> +# +usage() +{ + echo "vtree: usage: vtree [-a] [dir]" >&2 +} + +while getopts a opt +do + case "$opt" in + a) andfiles=-a ;; + *) usage ; exit 2 ;; + esac +done + +shift $((OPTIND - 1)) + +export BLOCKSIZE=1k # 4.4 BSD systems need this + +[ $# -eq 0 ] && set . + +while [ $# -gt 0 ] +do + cd "$1" || { shift; [ $# -ge 1 ] && echo >&2; continue; } + echo -n "$PWD" + + du $andfiles | sort -k 2f | sed \ + -e 's/\([^ ]*\) \(.*\)/\2 (\1)/' \ + -e "s#^$1##" \ + -e 's#[^/]*/\([^/]*\)$#|____\1#' \ + -e 's#[^/]*/#| #g' + + [ $# -gt 1 ] && echo + shift +done diff --git a/examples/scripts/vtree3 b/examples/scripts/vtree3 new file mode 100644 index 0000000..2059b9f --- /dev/null +++ b/examples/scripts/vtree3 @@ -0,0 +1,99 @@ +#!/bin/ksh +# +# Name: dirtree +# Programmer: +# Hemant T. Shah +# Life Insurance Data Processing +# July 12 1994 +# +# Description: +# Print directory tree structure as follows: +# |___Mail +# |___scheduler +# |___cics_scripts +# |___tar_msdos +# |___awk +# |___attributes +# |___tmp +# |___News +# |___dosscsi +# |___FAQ_xterminal +# |___shell_history.Z +# |___FAQ_AIX +# |___aix_ftp_site +# |___hp_software +# |___dnload +# |___telnet.h +# |___msdos +# |___tnetd.tar.Z +# |___aix +# |___hp +# |___xkey.c +# +# Conversion to bash v2 syntax done by Chet Ramey +# - removed command substitutions calling `basename' +# + +ProgramName=${0##*/} +Path="." +ShowAll=1 +ShowDir=0 + + +ExpandDirectory() +{ +local object # Local variable + +cd "$1" + +for object in $PWD/.??* $PWD/* +do + if [ -d $object ]; # It is a directory + then + echo "${indent}|___${object##*/}/" + indent="${indent}! " # Add to indentation + if [ -x $object ]; + then + ExpandDirectory $object + fi + indent=${indent%????} # Remove from indentation + elif [ -e $object ]; then + if (( ShowAll == 1 )); + then + echo "${indent}|___${object##*/}" + fi + fi +done + +} + +usage() +{ + echo -e "Usage: $ProgramName [-h] [-f] [-d] [path] " + echo -e "\t-h ... display this help message." + echo -e "\t-f path ... shows all files and directories below path (default)." + echo -e "\t-d path ... shows all directories only below path." +} + +while getopts "fd" opt +do + case $opt in + f) ShowAll=1 ;; + d) ShowDir=1 ;; + *) usage ; exit 2;; + esac +done + +shift $(( $OPTIND - 1 )) + +Path=${1:-.} + +if [ ! -d "$Path" ]; then + echo "$0: error: specified path is not a directory." >&2 + exit 1 +fi + + + +echo "!$Path/" +ExpandDirectory $Path diff --git a/examples/scripts/vtree3a b/examples/scripts/vtree3a new file mode 100644 index 0000000..0678764 --- /dev/null +++ b/examples/scripts/vtree3a @@ -0,0 +1,100 @@ +#!/bin/bash +# +# Name: dirtree +# Programmer: +# Hemant T. Shah +# Life Insurance Data Processing +# July 12 1994 +# +# Description: +# Print directory tree structure as follows: +# |___Mail +# |___scheduler +# |___cics_scripts +# |___tar_msdos +# |___awk +# |___attributes +# |___tmp +# |___News +# |___dosscsi +# |___FAQ_xterminal +# |___shell_history.Z +# |___FAQ_AIX +# |___aix_ftp_site +# |___hp_software +# |___dnload +# |___telnet.h +# |___msdos +# |___tnetd.tar.Z +# |___aix +# |___hp +# |___xkey.c +# +# Conversion to bash v2 syntax done by Chet Ramey +# - removed command substitutions calling `basename' +# + +ProgramName=${0##*/} +Path="." +ShowAll=1 +ShowDir=0 + + +ExpandDirectory() +{ +local object # Local variable + +cd "$1" + +for object in $PWD/.??* $PWD/* +do + if [ -d $object ]; # It is a directory + then + echo "${indent}|___${object##*/}/" + indent="${indent}! " # Add to indentation + if [ -x $object ]; + then + ExpandDirectory $object + fi + indent=${indent%????} # Remove from indentation + elif [ -e $object ]; then + if (( ShowAll == 1 )); + then + echo "${indent}|___${object##*/}" + fi + fi +done + +} + +usage() +{ + echo -e "Usage: $ProgramName [-h] [-f] [-d] [path] " + echo -e "\t-h ... display this help message." + echo -e "\t-f path ... shows all files and directories below path (default)." + echo -e "\t-d path ... shows all directories only below path." +} + +while getopts "fd" opt +do + case $opt in + f) ShowAll=1 ;; + #d) ShowDir=1 ;; + d) ShowAll=0 ;; + *) usage ; exit 2;; + esac +done + +shift $(( $OPTIND - 1 )) + +Path=${1:-.} + +if [ ! -d "$Path" ]; then + echo "$0: error: specified path is not a directory." >&2 + exit 1 +fi + + + +echo "!$Path/" +ExpandDirectory $Path diff --git a/examples/scripts/websrv.sh b/examples/scripts/websrv.sh new file mode 100644 index 0000000..e07746b --- /dev/null +++ b/examples/scripts/websrv.sh @@ -0,0 +1,230 @@ +#!/bin/sh +#for instructions or updates go to: +#<A HREF="http://math.ucr.edu:8889/">This script's home page</A> +#email me questions or comments at: +#<A HREF="mailto:insom@math.ucr.edu">insom@math.ucr.edu</A> +#copyright chris ulrich; This software may be used or modified +#in any way so long as this notice remains intact. +# +# WWW server in sh +# Author: Chris Ulrich <chris@tinker.ucr.edu> +# + +INDEX=index.html +date=`date` +DOCHOME=/home/insom/web-docs +BINHOME=/home/insom/web-bin +LOGHOME=/home/insom/web-logs +LOGFILE=$LOGHOME/access_log +#verbose=: +verbose=echo +exec 2>> $LOGHOME/error_log + +hheader() { +echo "HTTP/1.0 200 OK +Server: WebSH/2.00 +Connection: close +Date: $date" +} + +header() { +echo "Content-type: $1 +" +} + +no_url() { + header "text/plain" + echo "No such url $1" +} + +send() { + #case "$#" in 2) ;; *) echo eep! | mailx insom@math.ucr.edu ; exit 3 ;; esac + if test -f "$DOCHOME/$2" + then + header "$1" + cat "$DOCHOME/$2" + else + no_url "$2" + fi +} + +LsToHTML() { + if test -f "$DOCHOME/$url/.title" + then + header "text/html; charset=US-ASCII" + echo "<pre>" + cat "$DOCHOME/$url/.title" + echo "</pre>" + elif test -f "$DOCHOME/$url/.title.html" + then + header "text/html; charset=US-ASCII" + cat "$DOCHOME/$url/.title.html" + else + header "text/html; charset=US-ASCII" + fi + + case "$url" in + /) ;; + *) url="$url/" + esac + + while read link + do + case $link in + *.cgi) ;; + *) + echo "<A HREF=\"$url$link\">$link</A> <BR>" + ;; + esac + done +} + +read method data + +$verbose " +$date access from ${TCPREMOTEINFO:=NO-IDENT}@${TCPREMOTEHOST:=$TCPREMOTEIP} + on local machine $TCPLOCALHOST + $method $data " >> $LOGFILE + +for hopeurl in $data +do + url="${url}${url:+ }$second" + second="$hopeurl" +done + +case "$second" in + *[1-9].*) + read inheader + while + case "$inheader" in + ?|'') false + ;; + *) + read inheader + ;; + esac + do + : + done + hheader + ;; +esac + +case "$url" in + *..*) + no_url "$url" + exit 1 + ;; + *.txt|*.[ch]) + send "text/plain; charset=US-ASCII" "$url" + ;; + *.html) + send "text/html; charset=US-ASCII" "$url" + ;; + *.cgi) + if test -x "$DOCHOME/$url" + then + read message + echo "$message" | "$DOCHOME/$url" + else + no_url "$url" + fi + ;; + *".cgi?"*) + oIFS="$IFS" + echo "$url" | { + IFS='?' read url QUERY_STRING + if test -x "$DOCHOME/$url" + then + IFS="$oIFS" + export QUERY_STRING + "$DOCHOME/$url" + else + no_url "$url" + fi + } + ;; + *.[Gg][Ii][Ff]) + send "image/gif" "$url" + ;; + *.[Jj][Pp][Gg]|*.[Jj][Pp][Ee][Gg]) + send "image/jpeg" "$url" + ;; + *.tbl) + header "text/html; charset=US-ASCII" + echo "<pre>" + test -f "$DOCHOME/$url" && + tbl < "$DOCHOME/$url" | nroff || + no_url "$url" + echo "</pre>" + ;; + *.nroff) + header "text/html; charset=US-ASCII" + echo "<pre>" + test -f "$DOCHOME/$url" && + nroff < "$DOCHOME/$url" || + no_url "$url" + echo "</pre>" + ;; + *mp[23]) + if test -f "$DOCHOME/$url" + then + header "application/mpstream" + echo "+$TCPLOCALIP:${MPSERVPORT:=9001}/$url" + else + no_url "$url" + fi + ;; + *.[0-9]|*.[0-9][a-z]) + header "text/html; charset=US-ASCII" + echo "<pre>" + if test -f "$DOCHOME/$url" + then + #nroff -man "$DOCHOME/$url" | $BINHOME/man2html + echo "perl at the moment is broken, so man2html doesn't work. Sorry." + echo "</pre>" + else + no_url "$url" + fi + ;; + *.???|*.??) + send "unknown/data" "$url" + ;; + */) + if test -d "$DOCHOME/$url" + then + ls "$DOCHOME/$url" | LsToHTML + fi + ;; + *) + if test -f "$DOCHOME/$url" + then + read filetype < "$DOCHOME/$url" + case "$filetype" in + \#!/*/*|\#!?/*/*) + header "text/plain; charset=US-ASCII" + cat "$DOCHOME/$url" + ;; + '<!*>') + header "text/html; charset=US-ASCII" + cat "$DOCHOME/$url" + ;; + *) + header "text/html; charset=US-ASCII" + echo "<pre>" + cat "$DOCHOME/$url" + echo "</pre>" + ;; + esac + elif test -f "$DOCHOME/$url/$INDEX" + then + header "text/html; charset=US-ASCII" + cat "$DOCHOME/$url/$INDEX" + elif test -d "$DOCHOME/$url" + then + ls "$DOCHOME/$url" | LsToHTML + else + no_url "$url" + fi + ;; +esac diff --git a/examples/scripts/xterm_title b/examples/scripts/xterm_title new file mode 100755 index 0000000..72ba099 --- /dev/null +++ b/examples/scripts/xterm_title @@ -0,0 +1,27 @@ +#! /bin/bash +# +# xterm_title - print the contents of the xterm title bar +# +# Derived from http://www.clark.net/pub/dickey/xterm/xterm.faq.html#how2_title +# +P=${0##*/} +[ -z "$DISPLAY" ] && { + echo "${P}: not running X" >&2 + exit 1 +} + +if [ -z "$TERM" ] || [ "$TERM" != "xterm" ]; then + echo "${P}: not running in an xterm" >&2 + exit 1 +fi + +exec </dev/tty +old=$(stty -g) +stty raw -echo min 0 time ${1-10} +echo -e "\033[21t\c" > /dev/tty +IFS='' read -r a +stty $old +b=${a#???} +echo "${b%??}" + +exit 0 diff --git a/examples/scripts/zprintf b/examples/scripts/zprintf new file mode 100755 index 0000000..5e2e3ad --- /dev/null +++ b/examples/scripts/zprintf @@ -0,0 +1,26 @@ +#! /bin/bash +# +# zprintf - function that calls gawk to do printf for those systems that +# don't have a printf executable +# +# The format and arguments can have trailing commas, just like gawk +# +# example: +# zprintf 'Eat %x %x and suck %x!\n' 57005 48879 64206 +# +# Chet Ramey +# chet@po.cwru.edu + +[ $# -lt 1 ] && { + echo "zprintf: usage: zprintf format [args ...]" >&2 + exit 2 +} + +fmt="${1%,}" +shift + +for a in "$@"; do + args="$args,\"${a%,}\"" +done + +gawk "BEGIN { printf \"$fmt\" $args }" diff --git a/examples/startup-files/Bash_aliases b/examples/startup-files/Bash_aliases new file mode 100644 index 0000000..bb9c01e --- /dev/null +++ b/examples/startup-files/Bash_aliases @@ -0,0 +1,63 @@ +# Some useful aliases. +alias texclean='rm -f *.toc *.aux *.log *.cp *.fn *.tp *.vr *.pg *.ky' +alias clean='echo -n "Really clean this directory?"; + read yorn; + if test "$yorn" = "y"; then + rm -f \#* *~ .*~ *.bak .*.bak *.tmp .*.tmp core a.out; + echo "Cleaned."; + else + echo "Not cleaned."; + fi' +alias h='history' +alias j="jobs -l" +alias l="ls -l " +alias ll="ls -l" +alias ls="ls -F" +alias pu="pushd" +alias po="popd" + +# +# Csh compatability: +# +alias unsetenv=unset +function setenv () { + export $1="$2" +} + +# Function which adds an alias to the current shell and to +# the ~/.bash_aliases file. +add-alias () +{ + local name=$1 value="$2" + echo alias $name=\'$value\' >>~/.bash_aliases + eval alias $name=\'$value\' + alias $name +} + +# "repeat" command. Like: +# +# repeat 10 echo foo +repeat () +{ + local count="$1" i; + shift; + for i in $(seq 1 "$count"); + do + eval "$@"; + done +} + +# Subfunction needed by `repeat'. +seq () +{ + local lower upper output; + lower=$1 upper=$2; + + if [ $lower -ge $upper ]; then return; fi + while [ $lower -le $upper ]; + do + echo -n "$lower " + lower=$(($lower + 1)) + done + echo "$lower" +} diff --git a/examples/startup-files/Bash_profile b/examples/startup-files/Bash_profile new file mode 100644 index 0000000..141e8df --- /dev/null +++ b/examples/startup-files/Bash_profile @@ -0,0 +1,18 @@ +# Startup file for bash login shells. +# +default_dir=/usr/local/lib/ + +if [ -n "$PS1" ]; then + PS1='\u@\h(\#)\$ ' + IGNOREEOF=3 +fi + +LOGIN_SHELL=true + +# If the user has her own init file, then use that one, else use the +# canonical one. +if [ -f ~/.bashrc ]; then + . ~/.bashrc +elif [ -f ${default_dir}Bashrc ]; then + . ${default_dir}Bashrc; +fi diff --git a/examples/startup-files/Bashrc.bfox b/examples/startup-files/Bashrc.bfox new file mode 100644 index 0000000..efe7d88 --- /dev/null +++ b/examples/startup-files/Bashrc.bfox @@ -0,0 +1,70 @@ +# Bourne Again SHell init file. +# +# Files you make look like rw-rw-r +umask 002 + +# Don't make useless coredump files. If you want a coredump, +# say "ulimit -c unlimited" and then cause a segmentation fault. +ulimit -c 0 + +# Sometimes, there are lots of places that one can find tex inputs. +export TEXINPUTS=.:$HOME/bin:/usr/lib/tex/inputs:/usr/local/lib/tex/inputs + +# Where's the Gnu stuff at? +GNU=/usr/gnu/bin +X11=/usr/bin/X11 + +UTIL_PATH=$GNU:$X11 +STANDARD_PATH=/usr/local/bin:/usr/ucb:/bin:/usr/bin:/usr/etc:/etc:/usr/games + +if [ -d $HOME/bin/$HOSTTYPE ]; then + MY_PATH=$HOME/bin/$HOSTTYPE +fi + +if [ -d $HOME/bin ]; then + MY_PATH=$MY_PATH:$HOME/bin +fi + +if [ -d /usr/hosts ]; then + STANDARD_PATH=$STANDARD_PATH:/usr/hosts +fi + +PATH=.:$MY_PATH:$UTIL_PATH:$STANDARD_PATH + +# If not running interactively, then return +if [ -z "$PS1" ]; then + return +fi + +# Set ignoreeof if you don't want EOF as the sole input to the shell to +# immediately signal a quit condition. This only happens at the start +# of a line if the line is empty, and you haven't just deleted a character +# with C-d. I turn this on in ~/.bash_profile so that only login shells +# have the right to be obnoxious. +# set -o ignoreeof + +# Set auto_resume if you want to resume on "emacs", as well as on +# "%emacs". +auto_resume=exact + +# Set notify if you want to be asynchronously notified about background +# job completion. +set -o notify + +# Make it so that failed `exec' commands don't flush this shell. +shopt -s execfail + +if [ -z "$LOGIN_SHELL" ]; then + PS1="\u@\h\$ " +fi + +HISTSIZE=256 +MAILCHECK=60 + +# A couple of default aliases. +alias j='jobs -l' +alias po=popd +alias pu=pushd +alias ls='ls -F' + +[ -f ~/.bash_aliases ] && . ~/.bash_aliases diff --git a/examples/startup-files/README b/examples/startup-files/README new file mode 100644 index 0000000..bdbfd90 --- /dev/null +++ b/examples/startup-files/README @@ -0,0 +1,5 @@ +Some sample startup files. The ones starting with capital letters +are originally from Brian Fox. The ones starting with lowercase +letters are from Chet Ramey. + +They will require changes for your environment. diff --git a/examples/startup-files/apple/README b/examples/startup-files/apple/README new file mode 100644 index 0000000..07005cb --- /dev/null +++ b/examples/startup-files/apple/README @@ -0,0 +1,24 @@ +This directory contains some useful bash files. + +In order to use this configuration: + + echo "source ~/.bashrc" > ~/.profile + echo "source /usr/share/init/bash/rc" > ~/.bashrc + echo "source /usr/share/init/bash/login" > ~/.login + +In order to customize this setup: + + mkdir ~/Library/init/bash + +and create the following files there as necessary: + + aliases.mine - shell aliases + completions.mine - completions + environment.mine - environment + rc.mine - run commands + path - command search path + +See the corresponding file in /usr/share/init/bash for more information about the role of each file. You can easily extend or override the configuration provided by the default file. For example, you can add more aliases by adding the appropriate commands in aliases.mine. + + -Fred + tritan@mit.edu diff --git a/examples/startup-files/apple/aliases b/examples/startup-files/apple/aliases new file mode 100644 index 0000000..23d3399 --- /dev/null +++ b/examples/startup-files/apple/aliases @@ -0,0 +1,34 @@ +## +# Bash aliases file +# +# Wilfredo Sanchez Jr. | tritan@mit.edu +## + +## +# Aliases +## + +alias .='cwd' +alias ..='cd ..' +alias cd..='cd ..' +alias cdwd='cd $(/bin/pwd)' +alias cwd='echo $PWD' +alias l='ls -lg' + +## +# Functions +## + +files () { find ${1} -type f -print ; } +ff () { find . -name ${1} -print ; } +ll () { ls -lag "$@" | more ; } +word () { fgrep -i "$*" /usr/dict/web2 ; } +wordcount () { cat "${1}" | tr -s ' .,;:?\!()[]"' '\012' | \ + awk 'END {print NR}' ; } + +## +# Read user's aliases +## +if [ -r ${bash_initdir}/aliases.mine ]; then + source ${bash_initdir}/aliases.mine +fi diff --git a/examples/startup-files/apple/bash.defaults b/examples/startup-files/apple/bash.defaults new file mode 100644 index 0000000..a80145b --- /dev/null +++ b/examples/startup-files/apple/bash.defaults @@ -0,0 +1,22 @@ +## +# Bash +# User preferences file +# Override these in rc.mine +# +# Wilfredo Sanchez Jr. | tritan@mit.edu +# July 09, 1992 +# +# MIT Project Athena +## + +if [ -n "$PS1" ]; then + + # Prompts + PS1='[\h:\w] \u\$ ' + PS2=' -> ' + #PS3= + #PS4= + + set -o emacs + +fi diff --git a/examples/startup-files/apple/environment b/examples/startup-files/apple/environment new file mode 100644 index 0000000..fee218b --- /dev/null +++ b/examples/startup-files/apple/environment @@ -0,0 +1,24 @@ +## +# Bourne Again Shell environment file +# Global environment setup +# +# Wilfredo Sanchez Jr. | tritan@mit.edu +# July 09, 1992 +# +# MIT Project Athena +# +# ORIGINAL SOURCES: /usr/athena/lib/init/cshrc (ATHENA REL 7.3P) +## + +export ENV_SET="YES" # avoid repeat + +# File creation mask +umask 022 # all files created are -rw-r--r-- + +## +# Load user environment +## + +if [ -f ${bash_initdir}/environment.mine ]; then + source ${bash_initdir}/environment.mine +fi diff --git a/examples/startup-files/apple/login b/examples/startup-files/apple/login new file mode 100644 index 0000000..b91f787 --- /dev/null +++ b/examples/startup-files/apple/login @@ -0,0 +1,15 @@ +## +# Set path +## + +export PATH="${HOME}/${MACHTYPE}/bin:${HOME}/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" + +export MANPATH="${HOME}/man:/usr/local/share/man:/usr/share/man" + +## +# Read user's login +## + +if (-r ${bash_initdir}/login.mine) then + source ${bash_initdir}/login.mine +fi diff --git a/examples/startup-files/apple/logout b/examples/startup-files/apple/logout new file mode 100644 index 0000000..812731c --- /dev/null +++ b/examples/startup-files/apple/logout @@ -0,0 +1,10 @@ +## +# Destroy credentials +## + +if [ -z "${TERM_PROGRAM}" ]; then + # Don't run these commands if the shell is launched by Terminal, + # even if it's a login shell. + + if klist -s; then kdestroy; fi +fi diff --git a/examples/startup-files/apple/rc b/examples/startup-files/apple/rc new file mode 100644 index 0000000..2451b9f --- /dev/null +++ b/examples/startup-files/apple/rc @@ -0,0 +1,63 @@ +## +# Bourne Again Shell config file +# +# Wilfredo Sanchez Jr. | tritan@mit.edu +# July 09, 1992 +# +# MIT Project Athena +# +# ORIGINAL SOURCES: /usr/athena/lib/init/cshrc (ATHENA REL 7.3P) +## + + default_initdir=/usr/share/init +default_bash_initdir=${default_initdir}/bash + user_initdir=~/Library/init + user_bash_initdir=${user_initdir}/bash + +if [ -r ${user_bash_initdir} ]; then + initdir=${user_initdir} + bash_initdir=${user_bash_initdir} +else + initdir=${default_initdir} + bash_initdir=${default_bash_initdir} +fi + +# SET UP HOST-DEPENDANT VARIABLES, ETC. + +host=$(echo $(hostname) | tr A-Z a-z) + +user=`whoami` + +export HOST=${host} +export USER=${user} + +# User ID +if [ -z "${uid}" ]; then uid=$(id | cut -d = -f 2 | cut -d \( -f 1); fi + +# SET COMMAND SEARCH PATH AND MAN PATH +if [ -f ${bash_initdir}/path ]; then source ${bash_initdir}/path; fi + +# ENVIRONMENT SETUP + +if [ -n "${PS1}" ]; then interactive="YES"; fi + +if [ -z "${ENV_SET}" ]; then + if [ -f ${default_bash_initdir}/environment ]; then + #echo "Initializing environment..." + source ${default_bash_initdir}/environment + fi +fi + +if [ -r ${default_bash_initdir}/bash.defaults ]; then + source ${default_bash_initdir}/bash.defaults +fi + +# DEFAULT LOGIN SOURCES +if [ -f ${bash_initdir}/rc.mine ]; then source ${bash_initdir}/rc.mine; fi + +if [ "${interactive}" = "YES" ]; then + # These aren't useful for non-interactive sessions + if [ -f ${default_bash_initdir}/aliases ]; then + source ${default_bash_initdir}/aliases + fi +fi diff --git a/examples/startup-files/bash-profile b/examples/startup-files/bash-profile new file mode 100644 index 0000000..e811df8 --- /dev/null +++ b/examples/startup-files/bash-profile @@ -0,0 +1,39 @@ +# This is the filename where your incoming mail arrives. +MAIL=~/mbox +MAILCHECK=30 + +HISTFILE=~/.history/history.$HOSTNAME + +PATH1=/usr/homes/chet/bin.$HOSTTYPE:/usr/local/bin/gnu: +PATH2=/usr/local/bin:/usr/ucb:/bin:/usr/bin/X11:. +PATH3=/usr/bin:/usr/new/bin:/usr/contrib/bin +PATH=$PATH1:$PATH2:$PATH3 + +EDITOR=/usr/local/bin/ce VISUAL=/usr/local/bin/ce FCEDIT=/usr/local/bin/ce + +SHELL=${SHELL:-${BASH:-/bin/bash}} + +PAGER=/usr/local/bin/less +LESS='-i -e -M -P%t?f%f :stdin .?pb%pb\%:?lbLine %lb:?bbByte %bb:-...' +# +# Bogus 1003.2 variables. This should really be in /etc/profile +# +LOGNAME=${USER-$(whoami)} +TZ=US/Eastern + +export HOME VISUAL EDITOR MAIL SHELL PATH TERM +export PAGER LESS TERMCAP HISTSIZE HISTFILE MAIL MAILCHECK LOGNAME TZ + +PS1="${HOSTNAME}\$ " +PS2='> ' +export PS1 PS2 + +umask 022 + +if [ -f /unix ] ; then + stty intr ^c # bogus +fi + +if [ -f ~/.bashrc ] ; then + . ~/.bashrc +fi diff --git a/examples/startup-files/bashrc b/examples/startup-files/bashrc new file mode 100644 index 0000000..069e8be --- /dev/null +++ b/examples/startup-files/bashrc @@ -0,0 +1,132 @@ +if [ -z "$PS1" ]; then + return +fi + +# bogus +if [ -f /unix ] ; then + alias ls='/bin/ls -CF' +else + alias ls='/bin/ls -F' +fi +alias ll='ls -l' +alias dir='ls -ba' + +alias ss="ps -aux" +alias dot='ls .[a-zA-Z0-9_]*' +alias news="xterm -g 80x45 -e trn -e -S1 -N &" + +alias c="clear" +alias m="more" +alias j="jobs" + +# common misspellings +alias mroe=more +alias pdw=pwd + +hash -p /usr/bin/mail mail + +if [ -z "$HOST" ] ; then + export HOST=${HOSTNAME} +fi + +HISTIGNORE="[ ]*:&:bg:fg" + +psgrep() +{ + ps -aux | grep $1 | grep -v grep +} + +# +# This is a little like `zap' from Kernighan and Pike +# + +pskill() +{ + local pid + + pid=$(ps -ax | grep $1 | grep -v grep | awk '{ print $1 }') + echo -n "killing $1 (process $pid)..." + kill -9 $pid + echo "slaughtered." +} + +term() +{ + TERM=$1 + export TERM + tset +} + +xtitle () +{ + echo -n -e "\033]0;$*\007" +} + +cd() +{ + builtin cd "$@" && xtitle $HOST: $PWD +} + +bold() +{ + tput smso +} + +unbold() +{ + tput rmso +} + +if [ -f /unix ] ; then +clear() +{ + tput clear +} +fi + +rot13() +{ + if [ $# = 0 ] ; then + tr "[a-m][n-z][A-M][N-Z]" "[n-z][a-m][N-Z][A-M]" + else + tr "[a-m][n-z][A-M][N-Z]" "[n-z][a-m][N-Z][A-M]" < $1 + fi +} + +watch() +{ + if [ $# -ne 1 ] ; then + tail -f nohup.out + else + tail -f $1 + fi +} + +# +# Remote login passing all 8 bits (so meta key will work) +# +rl() +{ + rlogin $* -8 +} + +function setenv() +{ + if [ $# -ne 2 ] ; then + echo "setenv: Too few arguments" + else + export $1="$2" + fi +} + +function chmog() +{ + if [ $# -ne 4 ] ; then + echo "usage: chmog mode owner group file" + return 1 + else + chmod $1 $4 + chown $2 $4 + chgrp $3 $4 + fi +} |