Imported from ../bash-2.0.tar.gz.

This commit is contained in:
Jari Aalto 1996-12-23 17:02:34 +00:00
commit ccc6cda312
502 changed files with 91988 additions and 69123 deletions

View file

@ -1,22 +0,0 @@
#! /bin/sh
#
# Convert Csh aliases to Bash aliases. Adapted from a similar program
# supplied with zsh.
#
# This is a quick script to convert csh aliases to Bash aliases/functions.
# Pipe the output of csh's alias command through this; it will generate
# a series of alias/function definitions on stdout, suitable for
# processing by bash.
#
# This is not perfect, but it gets most common aliases; it should manage to
# cut down a lot of the busy work.
#
sed -e 's/ (\(.*\))/ \1/' >/tmp/cz$$.1
grep ! /tmp/cz$$.1 >/tmp/cz$$.2
grep -v ! /tmp/cz$$.1 >/tmp/cz$$.3
sed -e "s/'/'"\\\\"''"/g -e 's/^\([^ ]*\) \(.*\)$/alias \1='"'\2'/" \
/tmp/cz$$.3
sed -e 's/![:#]*/$/g' -e 's/^\([^ ]*\) \(.*\)$/\1 () { \2 }/' /tmp/cz$$.2
rm /tmp/cz$$.?
exit 0

View file

@ -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).
-------

8
examples/bashdb/README Normal file
View file

@ -0,0 +1,8 @@
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
ftp://ftp.uu.net/published/oreilly/nutshell/ksh/ksh.tar.Z

29
examples/bashdb/bashdb Normal file
View file

@ -0,0 +1,29 @@
# 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
exec bash $_dbgfile $_guineapig $_tmpdir $_libdir "$@"
# end of bashdb

235
examples/bashdb/bashdb.fns Normal file
View file

@ -0,0 +1,235 @@
# 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}" | egrep -s "(${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} | egrep -s "*(${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 "$(echo $_linebp | tr '|' ' ')"
_msg "Breakpoints at strings:"
_msg "$(echo $_stringbp | tr '|' ' ')"
_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"
}

View file

@ -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
:

View file

@ -29,7 +29,7 @@
aload()
{
eval $1 '() { . '$2' ; '$1' "$@" ; return $?; }'
eval $1 '() { . '$2' ; '$1' "$@" ; return $? ; }'
}
#

View file

@ -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.bitnet
#
# 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
}

View file

@ -1,27 +1,15 @@
# C-shell compatabilty package.
# setenv VAR VALUE
function setenv () {
function setenv ()
{
export $1="$2"
}
function unsetenv () {
function unsetenv ()
{
unset $1
}
function alias () {
local name=$1
shift
local value="$*"
if [ "$name" = "" ]; then
builtin alias
elif [ "$value" = "" ]; then
builtin alias $name
else
builtin alias $name="$value"
fi
}
# Can't write foreach yet. Need pattern matching, and a few extras.
function foreach () {
echo 'Can'\''t do `foreach'\'' yet. Type "help for".'
@ -31,6 +19,29 @@ echo 'Can'\''t do `foreach'\'' yet. Type "help for".'
#set () {
#}
chdir () {
builtin cd $*
}
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
echo $2 | egrep -s '(\!|#)' 2>/dev/null
if [ $? -eq 0 ]
then
comm=$(echo $2 | sed 's/\\!\*/\"$\@\"/g
s/\\!:\([1-9]\)/\"$\1\"/g
s/#/\\#/g')
else
comm="$2 \"\$@\""
fi
eval function $1 \(\) "{" command "$comm" "; }"
fi
}

View file

@ -11,7 +11,7 @@ function check_exit_status ()
if [ ${status} -ne 0 -a ${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)"
signal="$(builtin kill -l $((${status} - 128)) 2>/dev/null)"
if [ "$signal" ]; then signal="($signal)"; fi
fi
echo "[Exit ${status} ${signal}]" 1>&2

15
examples/functions/inpath Normal file
View file

@ -0,0 +1,15 @@
inpath()
{
path=$(echo $PATH | sed 's/^:/.:/
s/::/:.:/g
s/:$/:./
s/:/ /g')
for x in $path
do
[ -x $x/$1 ] && { PROG=$x/$1; break; }
done
[ -z "$PROG" ]
return
}

62
examples/functions/keep Normal file
View file

@ -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

View file

@ -39,13 +39,9 @@ whence()
return 1
fi
case "$1" in
-v) vflag=1
shift 1
;;
-*) echo "whence: bad option: $1"
return 1
;;
*) ;;
-v) vflag=1 ; shift 1 ;;
-*) echo "whence: bad option: $1" ; return 1 ;;
*) ;;
esac
if [ "$#" = "0" ] ; then
@ -63,15 +59,12 @@ whence()
echo $path
else
case "$cmd" in
/*) echo ""
;;
*) case "$(builtin type -type $cmd)" in
"") echo ""
;;
*) echo "$cmd"
;;
esac
;;
/*) echo "" ;;
*) case "$(builtin type -type $cmd)" in
"") echo "" ;;
*) echo "$cmd" ;;
esac
;;
esac
fi
fi
@ -117,7 +110,7 @@ cd()
# -n do not add trailing newline
# -p no-op (no coprocesses)
# -r no escapes
# -s no-op (print to the history file)
# -s print to the history file
# -u n redirect output to fd n
#
@ -131,20 +124,20 @@ print()
while getopts "Rnprsu:" c
do
case $c in
R) eflag=
;;
r) eflag=
;;
n) nflag=-n
;;
u) fd=$OPTARG
;;
p|s) ;;
R) eflag= ;;
r) eflag= ;;
n) nflag=-n ;;
s) sflag=y ;;
u) fd=$OPTARG ;;
p) ;;
esac
done
shift $[ $OPTIND - 1 ]
builtin echo $eflag $nflag "$@" >&$fd
case "$sflag" in
y) builtin history -s "$*" ;;
*) builtin echo $eflag $nflag "$@" >&$fd
esac
}
# substring function

11
examples/functions/login Normal file
View file

@ -0,0 +1,11 @@
# replace the `login' and `newgrp' builtins in old bourne shells
login()
{
exec login "$@"
}
newgrp()
{
exec newgrp "$@"
}

View file

@ -0,0 +1,26 @@
#! /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
filename=${file##*/}
case "$filename" 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
}

16
examples/functions/mhfold Normal file
View file

@ -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 | tail +2 | awk '{print $1}'`
/bin/ls -lag ~/Mail > /tmp/fold$$
for i in $list; do
grep $i /tmp/fold$$
done
/bin/rm -f /tmp/fold$$
}

View file

@ -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
}

29
examples/functions/seq Normal file
View file

@ -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
}

View file

@ -1,6 +1,6 @@
shcat()
{
while read line
while read -r line
do
echo "$line"
done

112
examples/loadables/Makefile Normal file
View file

@ -0,0 +1,112 @@
#
# Simple makefile for the sample loadable builtins
#
CC = cc
# SunOS 4
PICFLAG = -pic
# Some versions of gcc, esp. on NetBSD and FreeBSD
#PICFLAG = -fpic
# Linux -- could also be -fpic
#PICFLAG = -fPIC
# SunOS 5
#PICFLAG = -K pic
# SVR4, SVR4.2, Irix
#PICFLAG = -K PIC
# BSD/OS 2.1
#PICFLAG =
# AIX 4.2
#PICFLAG = -K
# SunOS 4, BSD/OS 2.1, SVR4.2, SVR4, Linux, AIX 4.2, etc.
LD = ld
# SunOS 5, Linux
#LD = cc
# SunOS 4
LDOPT = -assert pure-text
# OSF/1, Digital UNIX
#LDOPT = -shared -soname $@ -expect_unresolved '*'
# SunOS 5
#LDOPT = -dy -z text -G -i -h $@
# SVR4, SVR4.2
#LDOPT = -dy -z text -G -h $@
# NetBSD, FreeBSD -- might also need -r
#LDOPT = -x -Bshareable
# Linux
#LDOPT = -shared
# BSD/OS 2.1
#LDOPT = -r
# AIX 4.2
#LDOPT = -bdynamic -bnoentry -bexpall -G
# other libraries to link the shared object against
# BSD/OS 2.1
#LDLIBS = -lc_s.2.1.0
srcdir = ../..
INC= -I$(srcdir) -I$(srcdir)/builtins -I$(srcdir)/lib
.c.o:
$(CC) $(PICFLAG) $(CFLAGS) $(INC) -c -o $@ $<
all: printf print truefalse sleep pushd finfo logname basename dirname \
tty pathchk tee head rmdir sprintf
others: necho getconf hello cat
printf: printf.o
$(LD) $(LDOPT) -o $@ printf.o $(LDLIBS)
sprintf: sprintf.o
$(LD) $(LDOPT) -o $@ sprintf.o $(LDLIBS)
print: print.o
$(LD) $(LDOPT) -o $@ print.o $(LDLIBS)
necho: necho.o
$(LD) $(LDOPT) -o $@ necho.o $(LDLIBS)
getconf: getconf.o
$(LD) $(LDOPT) -o $@ getconf.o $(LDLIBS)
hello: hello.o
$(LD) $(LDOPT) -o $@ hello.o $(LDLIBS)
truefalse: truefalse.o
$(LD) $(LDOPT) -o $@ truefalse.o $(LDLIBS)
sleep: sleep.o
$(LD) $(LDOPT) -o $@ sleep.o $(LDLIBS)
pushd: pushd.o
$(LD) $(LDOPT) -o $@ pushd.o $(LDLIBS)
finfo: finfo.o
$(LD) $(LDOPT) -o $@ finfo.o $(LDLIBS)
cat: cat.o
$(LD) $(LDOPT) -o $@ cat.o $(LDLIBS)
logname: logname.o
$(LD) $(LDOPT) -o $@ logname.o $(LDLIBS)
basename: basename.o
$(LD) $(LDOPT) -o $@ basename.o $(LDLIBS)
dirname: dirname.o
$(LD) $(LDOPT) -o $@ dirname.o $(LDLIBS)
tty: tty.o
$(LD) $(LDOPT) -o $@ tty.o $(LDLIBS)
pathchk: pathchk.o
$(LD) $(LDOPT) -o $@ pathchk.o $(LDLIBS)
tee: tee.o
$(LD) $(LDOPT) -o $@ tee.o $(LDLIBS)
rmdir: rmdir.o
$(LD) $(LDOPT) -o $@ rmdir.o $(LDLIBS)
head: head.o
$(LD) $(LDOPT) -o $@ head.o $(LDLIBS)

27
examples/loadables/README Normal file
View file

@ -0,0 +1,27 @@
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.
Compile with cc and whatever pic options you need (look in the
Makefile for a few common settings)
load with ld and whatever shared object options you need (again,
look in the Makefile)
then 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'.

View file

@ -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 */
};

100
examples/loadables/cat.c Normal file
View file

@ -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
};

View file

@ -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 */
};

569
examples/loadables/finfo.c Normal file
View file

@ -0,0 +1,569 @@
/*
* finfo - print file info
*/
#include <sys/types.h>
#include "posixstat.h"
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include "shell.h"
#include "builtins.h"
#include "common.h"
#ifndef errno
extern int errno;
#endif
extern char *strrchr();
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;
long lfd;
if (strncmp(f, "/dev/fd/", 8) == 0) {
if (legal_number(f + 8, &lfd) == 0) {
builtin_error("%s: invalid fd", f + 8);
return ((struct stat *)0);
}
fd = lfd;
r = fstat(fd, &st);
} else
r = stat(f, &st);
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';
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 ");
perms(getperm(mode));
printf("\n");
}
static int
printst(st)
struct stat *st;
{
struct passwd *pw;
struct group *gr;
char *owner;
printf("Device (major/minor): %d (%d/%d)\n", (int) (st->st_dev & 0xFF),
(int) major (st->st_dev),
(int) minor (st->st_dev));
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
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

View file

@ -0,0 +1,272 @@
/*
* 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
*
* Written by:
* J.T. Conklin (jtc@wimsey.com), Winning Strategies, Inc.
*/
#ifndef lint
static char rcsid[] = "$Id: getconf.c,v 1.2 1994/05/10 00:04:12 jtc Exp $";
#endif /* not lint */
#include <stdio.h>
#include <limits.h>
#include <locale.h>
#include <unistd.h>
#include <errno.h>
#include "bashansi.h"
#include "shell.h"
#include "builtins.h"
#include "stdc.h"
struct conf_variable
{
const char *name;
enum { SYSCONF, CONFSTR, PATHCONF, CONSTANT } type;
long value;
};
/* BSD/OS does not define this; use Posix.2 recommended minimum value. */
#ifndef _POSIX2_COLL_WEIGHTS_MAX
#define _POSIX2_COLL_WEIGHTS_MAX 2
#endif
static const struct conf_variable conf_table[] =
{
{ "PATH", CONFSTR, _CS_PATH },
/* Utility Limit Minimum Values */
{ "POSIX2_BC_BASE_MAX", CONSTANT, _POSIX2_BC_BASE_MAX },
{ "POSIX2_BC_DIM_MAX", CONSTANT, _POSIX2_BC_DIM_MAX },
{ "POSIX2_BC_SCALE_MAX", CONSTANT, _POSIX2_BC_SCALE_MAX },
{ "POSIX2_BC_STRING_MAX", CONSTANT, _POSIX2_BC_STRING_MAX },
{ "POSIX2_COLL_WEIGHTS_MAX", CONSTANT, _POSIX2_COLL_WEIGHTS_MAX },
{ "POSIX2_EXPR_NEST_MAX", CONSTANT, _POSIX2_EXPR_NEST_MAX },
{ "POSIX2_LINE_MAX", CONSTANT, _POSIX2_LINE_MAX },
{ "POSIX2_RE_DUP_MAX", CONSTANT, _POSIX2_RE_DUP_MAX },
{ "POSIX2_VERSION", CONSTANT, _POSIX2_VERSION },
/* POSIX.1 Minimum Values */
{ "_POSIX_ARG_MAX", CONSTANT, _POSIX_ARG_MAX },
{ "_POSIX_CHILD_MAX", CONSTANT, _POSIX_CHILD_MAX },
{ "_POSIX_LINK_MAX", CONSTANT, _POSIX_LINK_MAX },
{ "_POSIX_MAX_CANON", CONSTANT, _POSIX_MAX_CANON },
{ "_POSIX_MAX_INPUT", CONSTANT, _POSIX_MAX_INPUT },
{ "_POSIX_NAME_MAX", CONSTANT, _POSIX_NAME_MAX },
{ "_POSIX_NGROUPS_MAX", CONSTANT, _POSIX_NGROUPS_MAX },
{ "_POSIX_OPEN_MAX", CONSTANT, _POSIX_OPEN_MAX },
{ "_POSIX_PATH_MAX", CONSTANT, _POSIX_PIPE_BUF },
{ "_POSIX_PIPE_BUF", CONSTANT, _POSIX_PIPE_BUF },
{ "_POSIX_SSIZE_MAX", CONSTANT, _POSIX_SSIZE_MAX },
{ "_POSIX_STREAM_MAX", CONSTANT, _POSIX_STREAM_MAX },
{ "_POSIX_TZNAME_MAX", CONSTANT, _POSIX_TZNAME_MAX },
/* Symbolic Utility Limits */
{ "BC_BASE_MAX", SYSCONF, _SC_BC_BASE_MAX },
{ "BC_DIM_MAX", SYSCONF, _SC_BC_DIM_MAX },
{ "BC_SCALE_MAX", SYSCONF, _SC_BC_SCALE_MAX },
{ "BC_STRING_MAX", SYSCONF, _SC_BC_STRING_MAX },
{ "COLL_WEIGHTS_MAX", SYSCONF, _SC_COLL_WEIGHTS_MAX },
{ "EXPR_NEST_MAX", SYSCONF, _SC_EXPR_NEST_MAX },
{ "LINE_MAX", SYSCONF, _SC_LINE_MAX },
{ "RE_DUP_MAX", SYSCONF, _SC_RE_DUP_MAX },
/* Optional Facility Configuration Values */
{ "POSIX2_C_BIND", SYSCONF, _SC_2_C_BIND },
{ "POSIX2_C_DEV", SYSCONF, _SC_2_C_DEV },
{ "POSIX2_CHAR_TERM", SYSCONF, _SC_2_CHAR_TERM },
{ "POSIX2_FORT_DEV", SYSCONF, _SC_2_FORT_DEV },
{ "POSIX2_FORT_RUN", SYSCONF, _SC_2_FORT_RUN },
{ "POSIX2_LOCALEDEF", SYSCONF, _SC_2_LOCALEDEF },
{ "POSIX2_SW_DEV", SYSCONF, _SC_2_SW_DEV },
{ "POSIX2_UPE", SYSCONF, _SC_2_UPE },
/* POSIX.1 Configurable System Variables */
{ "ARG_MAX", SYSCONF, _SC_ARG_MAX },
{ "CHILD_MAX", SYSCONF, _SC_CHILD_MAX },
{ "CLK_TCK", SYSCONF, _SC_CLK_TCK },
{ "NGROUPS_MAX", SYSCONF, _SC_NGROUPS_MAX },
{ "OPEN_MAX", SYSCONF, _SC_OPEN_MAX },
{ "STREAM_MAX", SYSCONF, _SC_STREAM_MAX },
{ "TZNAME_MAX", SYSCONF, _SC_TZNAME_MAX },
{ "_POSIX_JOB_CONTROL", SYSCONF, _SC_JOB_CONTROL },
{ "_POSIX_SAVED_IDS", SYSCONF, _SC_SAVED_IDS },
{ "_POSIX_VERSION", SYSCONF, _SC_VERSION },
{ "LINK_MAX", PATHCONF, _PC_LINK_MAX },
{ "MAX_CANON", PATHCONF, _PC_MAX_CANON },
{ "MAX_INPUT", PATHCONF, _PC_MAX_INPUT },
{ "NAME_MAX", PATHCONF, _PC_NAME_MAX },
{ "PATH_MAX", PATHCONF, _PC_PATH_MAX },
{ "PIPE_BUF", PATHCONF, _PC_PIPE_BUF },
{ "_POSIX_CHOWN_RESTRICTED", PATHCONF, _PC_CHOWN_RESTRICTED },
{ "_POSIX_NO_TRUNC", PATHCONF, _PC_NO_TRUNC },
{ "_POSIX_VDISABLE", PATHCONF, _PC_VDISABLE },
{ NULL }
};
extern char *this_command_name;
extern char *xmalloc ();
extern char **make_builtin_argv ();
static int getconf_main ();
int
getconf_builtin (list)
WORD_LIST *list;
{
int c, r;
char **v;
WORD_LIST *l;
v = make_builtin_argv (list, &c);
r = getconf_main (c, v);
free (v);
return r;
}
static int
getconf_main(argc, argv)
int argc;
char **argv;
{
int ch;
const struct conf_variable *cp;
long val;
size_t slen;
char *sval;
setlocale(LC_ALL, "");
while ((ch = getopt(argc, argv, "")) != -1) {
switch (ch) {
case '?':
default:
builtin_usage();
return(EX_USAGE);
/* NOTREACHED */
}
}
argc -= optind;
argv += optind;
if (argc < 1 || argc > 2) {
builtin_usage();
return(EX_USAGE);
/* NOTREACHED */
}
for (cp = conf_table; cp->name != NULL; cp++) {
if (strcmp(*argv, cp->name) == 0)
break;
}
if (cp->name == NULL) {
builtin_error ("%s: unknown variable", *argv);
return (EXECUTION_FAILURE);
}
if (cp->type == PATHCONF) {
if (argc != 2) {
builtin_usage();
return(EX_USAGE);
}
} else {
if (argc != 1) {
builtin_usage();
return(EX_USAGE);
}
}
switch (cp->type) {
case CONSTANT:
printf("%ld\n", cp->value);
break;
case CONFSTR:
slen = confstr (cp->value, (char *) 0, (size_t) 0);
sval = xmalloc(slen);
confstr(cp->value, sval, slen);
printf("%s\n", sval);
break;
case SYSCONF:
errno = 0;
if ((val = sysconf(cp->value)) == -1) {
if (errno != 0) {
builtin_error ("%s", strerror (errno));
return (EXECUTION_FAILURE);
}
printf ("undefined\n");
} else {
printf("%ld\n", val);
}
break;
case PATHCONF:
errno = 0;
if ((val = pathconf(argv[1], cp->value)) == -1) {
if (errno != 0) {
builtin_error ("%s: %s", argv[1], strerror (errno));
return (EXECUTION_FAILURE);
}
printf ("undefined\n");
} else {
printf ("%ld\n", val);
}
break;
}
return (ferror(stdout) ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}
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 sysvar or getconf pathvar pathname",
0
};

143
examples/loadables/head.c Normal file
View file

@ -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 <ctype.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] == '-' || (isdigit(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 */
};

View file

@ -0,0 +1,59 @@
/* 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"
/* 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 = word_list_to_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. */
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",
"which is a bare-bones echo",
(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 [args]", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -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
};

View file

@ -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 echo_struct = {
"echo",
necho_builtin,
BUILTIN_ENABLED,
necho_doc,
"echo [args]",
0
};

View file

@ -0,0 +1,359 @@
/* 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)
{
error (0, 0, "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))
{
error (0, 0, "`%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)
{
error (0, 0, "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)
{
error (0, 0, "path `%s' has length %d; exceeds limit of %d",
path, strlen (path), path_max);
return 1;
}
return 0;
}

553
examples/loadables/print.c Normal file
View file

@ -0,0 +1,553 @@
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. 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 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.
*/
#include <sys/types.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
#define PF(f, func) { \
if (fieldwidth) \
if (precision) \
(void)fprintf(ofp, f, fieldwidth, precision, func); \
else \
(void)fprintf(ofp, f, fieldwidth, func); \
else if (precision) \
(void)fprintf(ofp, f, precision, func); \
else \
(void)fprintf(ofp, f, func); \
}
static int asciicode __P((void));
static void escape __P((char *));
static int getchr __P((void));
static double getdouble __P((void));
static int getint __P((int *));
static int getlong __P((long *));
static char *getstr __P((void));
static char *mklong __P((char *, int));
static void usage __P((void));
static char **gargv;
int print_builtin ();
static int printf_main ();
static int printargs ();
static FILE *ofp;
extern char *ansicstr ();
extern char *single_quote ();
extern char **make_builtin_argv ();
extern char *this_command_name;
extern int optind;
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;
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))
ofd = atoi (list_optarg);
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)
{
v = word_list_to_argv (list, 0, 2, &c);
v[0] = this_command_name;
v[1] = pfmt;
r = printf_main (c, v);
free (v);
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), &sawc);
fprintf (ofp, "%s", ostr);
free (ostr);
if (sawc)
return (0);
if (l->next)
fprintf (ofp, " ");
}
return (1);
}
static int
printf_main(argc, argv)
int argc;
char *argv[];
{
static char *skip1, *skip2;
int ch, end, fieldwidth, precision;
char convch, nextch, *format, *fmt, *start;
while ((ch = getopt(argc, argv, "")) != EOF)
switch (ch) {
case '?':
default:
usage();
return (1);
}
argc -= optind;
argv += optind;
if (argc < 1) {
usage();
return (1);
}
/*
* Basic algorithm is to scan the format string for conversion
* specifications -- once one is found, find out if the field
* width or precision is a '*'; if it is, gather up value. Note,
* format strings are reused as necessary to use up the provided
* arguments, arguments of zero/null string are provided to use
* up the format string.
*/
skip1 = "#-+ 0";
skip2 = "*0123456789";
escape(fmt = format = *argv); /* backslash interpretation */
gargv = ++argv;
for (;;) {
end = 0;
/* find next format specification */
next: for (start = fmt;; ++fmt) {
if (!*fmt) {
/* avoid infinite loop */
if (end == 1) {
warnx("missing format character",
NULL, NULL);
return (1);
}
end = 1;
if (fmt > start)
(void)printf("%s", start);
if (!*gargv)
return (0);
fmt = format;
goto next;
}
/* %% prints a % */
if (*fmt == '%') {
if (*++fmt != '%')
break;
*fmt++ = '\0';
(void)printf("%s", start);
goto next;
}
}
/* skip to field width */
for (; strchr(skip1, *fmt); ++fmt);
if (*fmt == '*') {
if (getint(&fieldwidth))
return (1);
} else
fieldwidth = 0;
/* skip to possible '.', get following precision */
for (; strchr(skip2, *fmt); ++fmt);
if (*fmt == '.')
++fmt;
if (*fmt == '*') {
if (getint(&precision))
return (1);
} else
precision = 0;
/* skip to conversion char */
for (; strchr(skip2, *fmt); ++fmt);
if (!*fmt) {
warnx("missing format character", NULL, NULL);
return (1);
}
convch = *fmt;
nextch = *++fmt;
*fmt = '\0';
switch(convch) {
case 'c': {
char p;
p = getchr();
PF(start, p);
break;
}
case 's': {
char *p;
p = getstr();
PF(start, p);
break;
}
case 'b': { /* expand escapes in argument */
char *p;
p = getstr();
escape(p);
PF(start, p);
break;
}
case 'q': { /* print with shell single quoting */
char *p, *p2;
p = getstr();
p2 = single_quote(p);
PF(start, p2);
free(p2);
break;
}
case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
long p;
char *f;
if ((f = mklong(start, convch)) == NULL)
return (1);
if (getlong(&p))
return (1);
PF(f, p);
break;
}
case 'e': case 'E': case 'f': case 'g': case 'G': {
double p;
p = getdouble();
PF(start, p);
break;
}
default:
warnx("illegal format character", NULL, NULL);
return (1);
}
*fmt = nextch;
}
/* NOTREACHED */
}
static char *
mklong(str, ch)
char *str;
int ch;
{
static char copy[64];
int len;
len = strlen(str) + 2;
memmove(copy, str, len - 3);
copy[len - 3] = 'l';
copy[len - 2] = ch;
copy[len - 1] = '\0';
return (copy);
}
static void
escape(fmt)
register char *fmt;
{
register char *store;
register int value, c;
for (store = fmt; c = *fmt; ++fmt, ++store) {
if (c != '\\') {
*store = c;
continue;
}
switch (*++fmt) {
case '\0': /* EOS, user error */
*store = '\\';
*++store = '\0';
return;
case '\\': /* backslash */
case '\'': /* single quote */
*store = *fmt;
break;
case 'a': /* bell/alert */
*store = '\7';
break;
case 'b': /* backspace */
*store = '\b';
break;
case 'c':
return;
case 'e':
case 'E':
*store = '\033';
break;
case 'f': /* form-feed */
*store = '\f';
break;
case 'n': /* newline */
*store = '\n';
break;
case 'r': /* carriage-return */
*store = '\r';
break;
case 't': /* horizontal tab */
*store = '\t';
break;
case 'v': /* vertical tab */
*store = '\13';
break;
/* octal constant */
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
for (c = 3, value = 0;
c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
value <<= 3;
value += *fmt - '0';
}
--fmt;
*store = value;
break;
default:
*store = *fmt;
break;
}
}
*store = '\0';
}
static int
getchr()
{
if (!*gargv)
return ('\0');
return ((int)**gargv++);
}
static char *
getstr()
{
if (!*gargv)
return ("");
return (*gargv++);
}
static char *Number = "+-.0123456789";
static int
getint(ip)
int *ip;
{
long val;
if (getlong(&val))
return (1);
if (val > INT_MAX) {
warnx("%s: %s", *gargv, strerror(ERANGE));
return (1);
}
*ip = val;
return (0);
}
static int
getlong(lp)
long *lp;
{
long val;
char *ep;
if (!*gargv) {
*lp = 0;
return (0);
}
if (strchr(Number, **gargv)) {
errno = 0;
val = strtol(*gargv, &ep, 0);
if (*ep != '\0') {
warnx("%s: illegal number", *gargv, NULL);
return (1);
}
if (errno == ERANGE)
if (val == LONG_MAX) {
warnx("%s: %s", *gargv, strerror(ERANGE));
return (1);
}
if (val == LONG_MIN) {
warnx("%s: %s", *gargv, strerror(ERANGE));
return (1);
}
*lp = val;
++gargv;
return (0);
}
*lp = (long)asciicode();
return (0);
}
static double
getdouble()
{
if (!*gargv)
return ((double)0);
if (strchr(Number, **gargv))
return (atof(*gargv++));
return ((double)asciicode());
}
static int
asciicode()
{
register int ch;
ch = **gargv;
if (ch == '\'' || ch == '"')
ch = (*gargv)[1];
++gargv;
return (ch);
}
static void
usage()
{
(void)fprintf(stderr, "usage: print [-Rnprs] [-u unit] [-f format] [arg ...]\n");
}

460
examples/loadables/printf.c Normal file
View file

@ -0,0 +1,460 @@
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. 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 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.
*/
#if !defined(BUILTIN) && !defined(SHELL)
#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1989, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#endif
#ifndef lint
static char sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93";
#endif /* not lint */
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include "bashansi.h"
#include "shell.h"
#include "builtins.h"
#include "stdc.h"
#if !defined (errno)
extern int errno;
#endif
#define PF(f, func) { \
if (fieldwidth) \
if (precision) \
(void)printf(f, fieldwidth, precision, func); \
else \
(void)printf(f, fieldwidth, func); \
else if (precision) \
(void)printf(f, precision, func); \
else \
(void)printf(f, func); \
}
static int asciicode __P((void));
static void escape __P((char *));
static int getchr __P((void));
static double getdouble __P((void));
static int getint __P((int *));
static int getlong __P((long *));
static char *getstr __P((void));
static char *mklong __P((char *, int));
static void usage __P((void));
static char **gargv;
int printf_builtin ();
static int printf_main ();
extern char *this_command_name;
extern char *single_quote ();
extern char **make_builtin_argv ();
static char *printf_doc[] = {
"printf formats and prints its arguments, after the first, under control",
"of the format. The format is a character string which contains three",
"types of objects: plain characters, which are simply copied to standard",
"output, character escape sequences which are converted and copied to the",
"standard output, and format specifications, each of which causes printing",
"of the next successive argument. In addition to the standard printf(1)",
"formats, %%b means to expand escapes in the corresponding argument, and",
"%%q means to quote the argument in a way that can be reused as shell input.",
(char *)NULL
};
struct builtin printf_struct = {
"printf",
printf_builtin,
BUILTIN_ENABLED,
printf_doc,
"printf format [arguments]",
(char *)0
};
int
printf_builtin (list)
WORD_LIST *list;
{
int c, r;
char **v;
WORD_LIST *l;
v = make_builtin_argv (list, &c);
r = printf_main (c, v);
free (v);
fflush(stdout);
return r;
}
static int
printf_main(argc, argv)
int argc;
char *argv[];
{
extern int optind;
static char *skip1, *skip2;
int ch, end, fieldwidth, precision;
char convch, nextch, *format, *fmt, *start;
while ((ch = getopt(argc, argv, "")) != EOF)
switch (ch) {
case '?':
default:
usage();
return (1);
}
argc -= optind;
argv += optind;
if (argc < 1) {
usage();
return (1);
}
/*
* Basic algorithm is to scan the format string for conversion
* specifications -- once one is found, find out if the field
* width or precision is a '*'; if it is, gather up value. Note,
* format strings are reused as necessary to use up the provided
* arguments, arguments of zero/null string are provided to use
* up the format string.
*/
skip1 = "#-+ 0";
skip2 = "*0123456789";
escape(fmt = format = *argv); /* backslash interpretation */
gargv = ++argv;
for (;;) {
end = 0;
/* find next format specification */
next: for (start = fmt;; ++fmt) {
if (!*fmt) {
/* avoid infinite loop */
if (end == 1) {
warnx("missing format character",
NULL, NULL);
return (1);
}
end = 1;
if (fmt > start)
(void)printf("%s", start);
if (!*gargv)
return (0);
fmt = format;
goto next;
}
/* %% prints a % */
if (*fmt == '%') {
if (*++fmt != '%')
break;
*fmt++ = '\0';
(void)printf("%s", start);
goto next;
}
}
/* skip to field width */
for (; strchr(skip1, *fmt); ++fmt);
if (*fmt == '*') {
if (getint(&fieldwidth))
return (1);
} else
fieldwidth = 0;
/* skip to possible '.', get following precision */
for (; strchr(skip2, *fmt); ++fmt);
if (*fmt == '.')
++fmt;
if (*fmt == '*') {
if (getint(&precision))
return (1);
} else
precision = 0;
/* skip to conversion char */
for (; strchr(skip2, *fmt); ++fmt);
if (!*fmt) {
warnx("missing format character", NULL, NULL);
return (1);
}
convch = *fmt;
nextch = *++fmt;
*fmt = '\0';
switch(convch) {
case 'c': {
char p;
p = getchr();
PF(start, p);
break;
}
case 's': {
char *p;
p = getstr();
PF(start, p);
break;
}
case 'b': { /* expand escapes in argument */
char *p;
p = getstr();
escape(p);
PF("%s", p);
break;
}
case 'q': { /* print with shell single quoting */
char *p, *p2;
p = getstr();
p2 = single_quote(p);
PF("%s", p2);
free(p2);
break;
}
case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
long p;
char *f;
if ((f = mklong(start, convch)) == NULL)
return (1);
if (getlong(&p))
return (1);
PF(f, p);
break;
}
case 'e': case 'E': case 'f': case 'g': case 'G': {
double p;
p = getdouble();
PF(start, p);
break;
}
default:
warnx("illegal format character", NULL, NULL);
return (1);
}
*fmt = nextch;
}
/* NOTREACHED */
}
static char *
mklong(str, ch)
char *str;
int ch;
{
static char copy[64];
int len;
len = strlen(str) + 2;
memmove(copy, str, len - 3);
copy[len - 3] = 'l';
copy[len - 2] = ch;
copy[len - 1] = '\0';
return (copy);
}
static void
escape(fmt)
register char *fmt;
{
register char *store;
register int value, c;
for (store = fmt; c = *fmt; ++fmt, ++store) {
if (c != '\\') {
*store = c;
continue;
}
switch (*++fmt) {
case '\0': /* EOS, user error */
*store = '\\';
*++store = '\0';
return;
case '\\': /* backslash */
case '\'': /* single quote */
*store = *fmt;
break;
case 'a': /* bell/alert */
*store = '\7';
break;
case 'b': /* backspace */
*store = '\b';
break;
case 'c':
return;
case 'e':
case 'E':
*store = '\033';
break;
case 'f': /* form-feed */
*store = '\f';
break;
case 'n': /* newline */
*store = '\n';
break;
case 'r': /* carriage-return */
*store = '\r';
break;
case 't': /* horizontal tab */
*store = '\t';
break;
case 'v': /* vertical tab */
*store = '\13';
break;
/* octal constant */
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
for (c = 3, value = 0;
c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
value <<= 3;
value += *fmt - '0';
}
--fmt;
*store = value;
break;
default:
*store = *fmt;
break;
}
}
*store = '\0';
}
static int
getchr()
{
if (!*gargv)
return ('\0');
return ((int)**gargv++);
}
static char *
getstr()
{
if (!*gargv)
return ("");
return (*gargv++);
}
static char *Number = "+-.0123456789";
static int
getint(ip)
int *ip;
{
long val;
if (getlong(&val))
return (1);
if (val > INT_MAX) {
warnx("%s: %s", *gargv, strerror(ERANGE));
return (1);
}
*ip = val;
return (0);
}
static int
getlong(lp)
long *lp;
{
long val;
char *ep;
if (!*gargv) {
*lp = 0;
return (0);
}
if (strchr(Number, **gargv)) {
errno = 0;
val = strtol(*gargv, &ep, 0);
if (*ep != '\0') {
warnx("%s: illegal number", *gargv, NULL);
return (1);
}
if (errno == ERANGE)
if (val == LONG_MAX) {
warnx("%s: %s", *gargv, strerror(ERANGE));
return (1);
}
if (val == LONG_MIN) {
warnx("%s: %s", *gargv, strerror(ERANGE));
return (1);
}
*lp = val;
++gargv;
return (0);
}
*lp = (long)asciicode();
return (0);
}
static double
getdouble()
{
if (!*gargv)
return ((double)0);
if (strchr(Number, **gargv))
return (atof(*gargv++));
return ((double)asciicode());
}
static int
asciicode()
{
register int ch;
ch = **gargv;
if (ch == '\'' || ch == '"')
ch = (*gargv)[1];
++gargv;
return (ch);
}
static void
usage()
{
(void)fprintf(stderr, "usage: printf format [arg ...]\n");
}

601
examples/loadables/pushd.c Normal file
View file

@ -0,0 +1,601 @@
/* pushd.c, created from pushd.def. */
#include <config.h>
#include <stdio.h>
#include <sys/param.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include <errno.h>
#include <tilde/tilde.h>
#include "shell.h"
#include "builtins.h"
#include "maxpath.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif /* !errno */
static char *m_badarg = "%s: bad argument";
/* The list of remembered directories. */
static char **pushd_directory_list = (char **)NULL;
/* Number of existing slots in this list. */
static int directory_list_size;
/* Offset to the end of the list. */
static int directory_list_offset;
static void pushd_error ();
static void clear_directory_stack ();
static int cd_to_string ();
static int change_to_temp ();
static int get_dirstack_index ();
static void add_dirstack_element ();
#define NOCD 0x01
#define ROTATE 0x02
#define LONGFORM 0x04
#define CLEARSTAK 0x08
int
pushd_builtin (list)
WORD_LIST *list;
{
char *temp, *current_directory, *top;
int j, flags;
long num;
char direction;
/* If there is no argument list then switch current and
top of list. */
if (list == 0)
{
if (directory_list_offset == 0)
{
builtin_error ("no other directory");
return (EXECUTION_FAILURE);
}
current_directory = get_working_directory ("pushd");
if (current_directory == 0)
return (EXECUTION_FAILURE);
j = directory_list_offset - 1;
temp = pushd_directory_list[j];
pushd_directory_list[j] = current_directory;
j = change_to_temp (temp);
free (temp);
return j;
}
for (flags = 0; list; list = list->next)
{
if (ISOPTION (list->word->word, 'n'))
{
flags |= NOCD;
}
else if (ISOPTION (list->word->word, '-'))
{
list = list->next;
break;
}
else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
/* Let `pushd -' work like it used to. */
break;
else if (((direction = list->word->word[0]) == '+') || direction == '-')
{
if (legal_number (list->word->word + 1, &num) == 0)
{
builtin_error (m_badarg, list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
if (direction == '-')
num = directory_list_offset - num;
if (num > directory_list_offset || num < 0)
{
pushd_error (directory_list_offset, list->word->word);
return (EXECUTION_FAILURE);
}
flags |= ROTATE;
}
else if (*list->word->word == '-')
{
bad_option (list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
else
break;
}
if (flags & ROTATE)
{
/* Rotate the stack num times. Remember, the current
directory acts like it is part of the stack. */
temp = get_working_directory ("pushd");
if (num == 0)
{
j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
free (temp);
return j;
}
do
{
top = pushd_directory_list[directory_list_offset - 1];
for (j = directory_list_offset - 2; j > -1; j--)
pushd_directory_list[j + 1] = pushd_directory_list[j];
pushd_directory_list[j + 1] = temp;
temp = top;
num--;
}
while (num);
j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
free (temp);
return j;
}
if (list == 0)
return (EXECUTION_SUCCESS);
/* Change to the directory in list->word->word. Save the current
directory on the top of the stack. */
current_directory = get_working_directory ("pushd");
if (current_directory == 0)
return (EXECUTION_FAILURE);
j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
if (j == EXECUTION_SUCCESS)
{
add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
dirs_builtin ((WORD_LIST *)NULL);
return (EXECUTION_SUCCESS);
}
else
{
free (current_directory);
return (EXECUTION_FAILURE);
}
}
/* Pop the directory stack, and then change to the new top of the stack.
If LIST is non-null it should consist of a word +N or -N, which says
what element to delete from the stack. The default is the top one. */
int
popd_builtin (list)
WORD_LIST *list;
{
register int i;
long which;
int flags;
char direction;
for (flags = 0, which = 0L, direction = '+'; list; list = list->next)
{
if (ISOPTION (list->word->word, 'n'))
{
flags |= NOCD;
}
else if (ISOPTION (list->word->word, '-'))
{
list = list->next;
break;
}
else if (((direction = list->word->word[0]) == '+') || direction == '-')
{
if (legal_number (list->word->word + 1, &which) == 0)
{
builtin_error (m_badarg, list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
}
else if (*list->word->word == '-')
{
bad_option (list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
else
break;
}
if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
{
pushd_error (directory_list_offset, list ? list->word->word : "");
return (EXECUTION_FAILURE);
}
/* Handle case of no specification, or top of stack specification. */
if ((direction == '+' && which == 0) ||
(direction == '-' && which == directory_list_offset))
{
i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
: EXECUTION_SUCCESS;
if (i != EXECUTION_SUCCESS)
return (i);
free (pushd_directory_list[--directory_list_offset]);
}
else
{
/* Since an offset other than the top directory was specified,
remove that directory from the list and shift the remainder
of the list into place. */
i = (direction == '+') ? directory_list_offset - which : which;
free (pushd_directory_list[i]);
directory_list_offset--;
/* Shift the remainder of the list into place. */
for (; i < directory_list_offset; i++)
pushd_directory_list[i] = pushd_directory_list[i + 1];
}
dirs_builtin ((WORD_LIST *)NULL);
return (EXECUTION_SUCCESS);
}
/* Print the current list of directories on the directory stack. */
int
dirs_builtin (list)
WORD_LIST *list;
{
int flags, desired_index, index_flag, vflag;
long i;
char *temp, *w;
for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
{
if (ISOPTION (list->word->word, 'l'))
{
flags |= LONGFORM;
}
else if (ISOPTION (list->word->word, 'c'))
{
flags |= CLEARSTAK;
}
else if (ISOPTION (list->word->word, 'v'))
{
vflag |= 2;
}
else if (ISOPTION (list->word->word, 'p'))
{
vflag |= 1;
}
else if (ISOPTION (list->word->word, '-'))
{
list = list->next;
break;
}
else if (*list->word->word == '+' || *list->word->word == '-')
{
int sign;
if (legal_number (w = list->word->word + 1, &i) == 0)
{
builtin_error (m_badarg, list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
sign = (*list->word->word == '+') ? 1 : -1;
desired_index = get_dirstack_index (i, sign, &index_flag);
}
else
{
bad_option (list->word->word);
builtin_usage ();
return (EXECUTION_FAILURE);
}
}
if (flags & CLEARSTAK)
{
clear_directory_stack ();
return (EXECUTION_SUCCESS);
}
if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
{
pushd_error (directory_list_offset, w);
return (EXECUTION_FAILURE);
}
#define DIRSTACK_FORMAT(temp) \
(flags & LONGFORM) ? temp : polite_directory_format (temp)
/* The first directory printed is always the current working directory. */
if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
{
temp = get_working_directory ("dirs");
if (temp == 0)
temp = savestring ("<no current directory>");
if (vflag & 2)
printf ("%2d %s", 0, DIRSTACK_FORMAT (temp));
else
printf ("%s", DIRSTACK_FORMAT (temp));
free (temp);
if (index_flag)
{
putchar ('\n');
return EXECUTION_SUCCESS;
}
}
#define DIRSTACK_ENTRY(i) \
(flags & LONGFORM) ? pushd_directory_list[i] \
: polite_directory_format (pushd_directory_list[i])
/* Now print the requested directory stack entries. */
if (index_flag)
{
if (vflag & 2)
printf ("%2d %s", directory_list_offset - desired_index,
DIRSTACK_ENTRY (desired_index));
else
printf ("%s", DIRSTACK_ENTRY (desired_index));
}
else
for (i = directory_list_offset - 1; i >= 0; i--)
if (vflag >= 2)
printf ("\n%2d %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
else
printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));
putchar ('\n');
fflush (stdout);
return (EXECUTION_SUCCESS);
}
static void
pushd_error (offset, arg)
int offset;
char *arg;
{
if (offset == 0)
builtin_error ("directory stack empty");
else if (arg)
builtin_error ("%s: bad directory stack index", arg);
else
builtin_error ("bad directory stack index");
}
static void
clear_directory_stack ()
{
register int i;
for (i = 0; i < directory_list_offset; i++)
free (pushd_directory_list[i]);
directory_list_offset = 0;
}
/* Switch to the directory in NAME. This uses the cd_builtin to do the work,
so if the result is EXECUTION_FAILURE then an error message has already
been printed. */
static int
cd_to_string (name)
char *name;
{
WORD_LIST *tlist;
int result;
tlist = make_word_list (make_word (name), NULL);
result = cd_builtin (tlist);
dispose_words (tlist);
return (result);
}
static int
change_to_temp (temp)
char *temp;
{
int tt;
tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;
if (tt == EXECUTION_SUCCESS)
dirs_builtin ((WORD_LIST *)NULL);
return (tt);
}
static void
add_dirstack_element (dir)
char *dir;
{
int j;
if (directory_list_offset == directory_list_size)
{
j = (directory_list_size += 10) * sizeof (char *);
pushd_directory_list = (char **)xrealloc (pushd_directory_list, j);
}
pushd_directory_list[directory_list_offset++] = dir;
}
static int
get_dirstack_index (ind, sign, indexp)
int ind, sign, *indexp;
{
if (indexp)
*indexp = sign > 0 ? 1 : 2;
/* dirs +0 prints the current working directory. */
/* dirs -0 prints last element in directory stack */
if (ind == 0 && sign > 0)
return 0;
else if (ind == directory_list_offset)
{
if (indexp)
*indexp = sign > 0 ? 2 : 1;
return 0;
}
else
return (sign > 0 ? directory_list_offset - ind : ind);
}
char *
get_dirstack_element (ind, sign)
int ind, sign;
{
int i;
i = get_dirstack_index (ind, sign, (int *)NULL);
return (i < 0 || i > directory_list_offset) ? (char *)NULL
: pushd_directory_list[i];
}
void
set_dirstack_element (ind, sign, value)
int ind, sign;
char *value;
{
int i;
i = get_dirstack_index (ind, sign, (int *)NULL);
if (ind == 0 || i < 0 || i > directory_list_offset)
return;
free (pushd_directory_list[i]);
pushd_directory_list[i] = savestring (value);
}
WORD_LIST *
get_directory_stack ()
{
register int i;
WORD_LIST *ret;
char *d, *t;
for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
{
d = polite_directory_format (pushd_directory_list[i]);
ret = make_word_list (make_word (d), ret);
}
/* Now the current directory. */
d = get_working_directory ("dirstack");
i = 0; /* sentinel to decide whether or not to free d */
if (d == 0)
d = ".";
else
{
t = polite_directory_format (d);
/* polite_directory_format sometimes returns its argument unchanged.
If it does not, we can free d right away. If it does, we need to
mark d to be deleted later. */
if (t != d)
{
free (d);
d = t;
}
else /* t == d, so d is what we want */
i = 1;
}
ret = make_word_list (make_word (d), ret);
if (i)
free (d);
return ret; /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
}
static char *dirs_doc[] = {
"Display the list of currently remembered directories. Directories",
"find their way onto the list with the `pushd' command; you can get",
"back up through the list with the `popd' command.",
"",
"The -l flag specifies that `dirs' should not print shorthand versions",
"of directories which are relative to your home directory. This means",
"that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag",
"causes `dirs' to print the directory stack with one entry per line,",
"prepending the directory name with its position in the stack. The -p",
"flag does the same thing, but the stack position is not prepended.",
"The -c flag clears the directory stack by deleting all of the elements.",
"",
"+N displays the Nth entry counting from the left of the list shown by",
" dirs when invoked without options, starting with zero.",
"",
"-N displays the Nth entry counting from the right of the list shown by",
" dirs when invoked without options, starting with zero.",
(char *)NULL
};
static char *pushd_doc[] = {
"Adds a directory to the top of the directory stack, or rotates",
"the stack, making the new top of the stack the current working",
"directory. With no arguments, exchanges the top two directories.",
"",
"+N Rotates the stack so that the Nth directory (counting",
" from the left of the list shown by `dirs') is at the top.",
"",
"-N Rotates the stack so that the Nth directory (counting",
" from the right) is at the top.",
"",
"-n suppress the normal change of directory when adding directories",
" to the stack, so only the stack is manipulated.",
"",
"dir adds DIR to the directory stack at the top, making it the",
" new current working directory.",
"",
"You can see the directory stack with the `dirs' command.",
(char *)NULL
};
static char *popd_doc[] = {
"Removes entries from the directory stack. With no arguments,",
"removes the top directory from the stack, and cd's to the new",
"top directory.",
"",
"+N removes the Nth entry counting from the left of the list",
" shown by `dirs', starting with zero. For example: `popd +0'",
" removes the first directory, `popd +1' the second.",
"",
"-N removes the Nth entry counting from the right of the list",
" shown by `dirs', starting with zero. For example: `popd -0'",
" removes the last directory, `popd -1' the next to last.",
"",
"-n suppress the normal change of directory when removing directories",
" from the stack, so only the stack is manipulated.",
"",
"You can see the directory stack with the `dirs' command.",
(char *)NULL
};
struct builtin pushd_struct = {
"pushd",
pushd_builtin,
BUILTIN_ENABLED,
pushd_doc,
"pushd [+N | -N] [-n] [dir]",
0
};
struct builtin popd_struct = {
"popd",
popd_builtin,
BUILTIN_ENABLED,
popd_doc,
"popd [+N | -N] [-n]",
0
};
struct builtin dirs_struct = {
"dirs",
dirs_builtin,
BUILTIN_ENABLED,
dirs_doc,
"dirs [-clpv] [+N] [-N]",
0
};

View file

@ -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 */
};

150
examples/loadables/sleep.c Normal file
View file

@ -0,0 +1,150 @@
/*
* 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 "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
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 (isdigit(*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 (isdigit(p[n]) == 0)
RETURN(0);
usec = (usec * 10) + (p[n] - '0');
}
/* Now convert to millionths */
if (n == 1)
usec *= 100000;
else if (n == 2)
usec *= 10000;
else if (n == 3)
usec *= 1000;
else if (n == 4)
usec *= 100;
else if (n == 5)
usec *= 10;
else if (n == 6 && p[6] && isdigit(p[6]) && p[6] >= '5')
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
};

View file

@ -0,0 +1,514 @@
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. 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 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.
*/
#if !defined(BUILTIN) && !defined(SHELL)
#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1989, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#endif
#ifndef lint
static char sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93";
#endif /* not lint */
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include "bashansi.h"
#include "shell.h"
#include "builtins.h"
#include "stdc.h"
#if !defined (errno)
extern int errno;
#endif
static char sbuf[1024];
static int sblen;
/* Gee, I wish sprintf could be reliably counted upon to return the
number of characters written :-( */
#define PF(f, func) \
do { \
if (fieldwidth) \
if (precision) \
sprintf(sbuf, f, fieldwidth, precision, func); \
else \
sprintf(sbuf, f, fieldwidth, func); \
else if (precision) \
sprintf(sbuf, f, precision, func); \
else \
sprintf(sbuf, f, func); \
spaddstr (sbuf, strlen (sbuf)); \
} while (0)
static int asciicode __P((void));
static void escape __P((char *));
static int getchr __P((void));
static double getdouble __P((void));
static int getint __P((int *));
static int getlong __P((long *));
static char *getstr __P((void));
static char *mklong __P((char *, int));
static void usage __P((void));
static char **gargv;
static char *outstr;
static int outsize;
static int outind;
int sprintf_builtin ();
static int sprintf_main ();
static void spaddstr ();
extern char *this_command_name;
extern char *single_quote ();
extern char **make_builtin_argv ();
static char *sprintf_doc[] = {
"sprintf formats and outputs its arguments, after the second, under control",
"of the format and assigns the result to the variable named by its first",
"argument. The format is a character string which contains three types",
"of objects: plain characters, which are simply copied to the output string,",
"character escape sequences which are converted and copied to the output",
"string, and format specifications, each of which causes printing of the",
"next successive argument. In addition to the standard sprintf(3) formats,",
"%b means to expand escapes in the corresponding argument, and %q means",
"to quote the argument in a way that can be reused as shell input. Each",
"one of the format specifications must not expand to more than 1024",
"characters, though there is no limit on the total size of the output",
"string.",
(char *)NULL
};
struct builtin sprintf_struct = {
"sprintf",
sprintf_builtin,
BUILTIN_ENABLED,
sprintf_doc,
"sprintf var format [arguments]",
(char *)0
};
int
sprintf_builtin (list)
WORD_LIST *list;
{
int c, r;
char **v, *varname;
WORD_LIST *l;
SHELL_VAR *var;
if (list == 0)
{
builtin_usage ();
return (EXECUTION_FAILURE);
}
varname = list->word->word;
list = list->next;
if (legal_identifier (varname) == 0)
{
builtin_error ("%s: not a legal variable name", varname);
return (EXECUTION_FAILURE);
}
outind = 0;
if (outstr == 0)
outstr = xmalloc (outsize = 64);
outstr[0] = '\0';
v = make_builtin_argv (list, &c);
r = sprintf_main (c, v);
free (v);
var = bind_variable (varname, outstr);
if (readonly_p (var))
{
builtin_error ("%s: readonly variable", varname);
return (EXECUTION_FAILURE);
}
return r;
}
static void
spaddstr(str, len)
char *str;
int len;
{
RESIZE_MALLOCED_BUFFER (outstr, outind, len, outsize, 64);
strcpy (outstr + outind, str);
outind += len;
}
static int
sprintf_main(argc, argv)
int argc;
char *argv[];
{
extern int optind;
static char *skip1, *skip2;
int ch, end, fieldwidth, precision;
char convch, nextch, *format, *fmt, *start;
while ((ch = getopt(argc, argv, "")) != EOF)
switch (ch) {
case '?':
default:
usage();
return (1);
}
argc -= optind;
argv += optind;
if (argc < 1) {
usage();
return (1);
}
/*
* Basic algorithm is to scan the format string for conversion
* specifications -- once one is found, find out if the field
* width or precision is a '*'; if it is, gather up value. Note,
* format strings are reused as necessary to use up the provided
* arguments, arguments of zero/null string are provided to use
* up the format string.
*/
skip1 = "#-+ 0";
skip2 = "*0123456789";
escape(fmt = format = *argv); /* backslash interpretation */
gargv = ++argv;
for (;;) {
end = 0;
/* find next format specification */
next: for (start = fmt;; ++fmt) {
if (!*fmt) {
/* avoid infinite loop */
if (end == 1) {
warnx("missing format character",
NULL, NULL);
return (1);
}
end = 1;
if (fmt > start)
(void)printf("%s", start);
if (!*gargv)
return (0);
fmt = format;
goto next;
}
/* %% prints a % */
if (*fmt == '%') {
if (*++fmt != '%')
break;
*fmt++ = '\0';
(void)printf("%s", start);
goto next;
}
}
/* skip to field width */
for (; strchr(skip1, *fmt); ++fmt);
if (*fmt == '*') {
if (getint(&fieldwidth))
return (1);
} else
fieldwidth = 0;
/* skip to possible '.', get following precision */
for (; strchr(skip2, *fmt); ++fmt);
if (*fmt == '.')
++fmt;
if (*fmt == '*') {
if (getint(&precision))
return (1);
} else
precision = 0;
/* skip to conversion char */
for (; strchr(skip2, *fmt); ++fmt);
if (!*fmt) {
warnx("missing format character", NULL, NULL);
return (1);
}
convch = *fmt;
nextch = *++fmt;
*fmt = '\0';
switch(convch) {
case 'c': {
char p;
p = getchr();
PF(start, p);
break;
}
case 's': {
char *p;
p = getstr();
PF(start, p);
break;
}
case 'b': { /* expand escapes in argument */
char *p;
p = getstr();
escape(p);
PF("%s", p);
break;
}
case 'q': { /* print with shell single quoting */
char *p, *p2;
p = getstr();
p2 = single_quote(p);
PF("%s", p2);
free(p2);
break;
}
case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
long p;
char *f;
if ((f = mklong(start, convch)) == NULL)
return (1);
if (getlong(&p))
return (1);
PF(f, p);
break;
}
case 'e': case 'E': case 'f': case 'g': case 'G': {
double p;
p = getdouble();
PF(start, p);
break;
}
default:
warnx("illegal format character", NULL, NULL);
return (1);
}
*fmt = nextch;
}
/* NOTREACHED */
}
static char *
mklong(str, ch)
char *str;
int ch;
{
static char copy[64];
int len;
len = strlen(str) + 2;
memmove(copy, str, len - 3);
copy[len - 3] = 'l';
copy[len - 2] = ch;
copy[len - 1] = '\0';
return (copy);
}
static void
escape(fmt)
register char *fmt;
{
register char *store;
register int value, c;
for (store = fmt; c = *fmt; ++fmt, ++store) {
if (c != '\\') {
*store = c;
continue;
}
switch (*++fmt) {
case '\0': /* EOS, user error */
*store = '\\';
*++store = '\0';
return;
case '\\': /* backslash */
case '\'': /* single quote */
*store = *fmt;
break;
case 'a': /* bell/alert */
*store = '\7';
break;
case 'b': /* backspace */
*store = '\b';
break;
case 'c':
return;
case 'e':
case 'E':
*store = '\033';
break;
case 'f': /* form-feed */
*store = '\f';
break;
case 'n': /* newline */
*store = '\n';
break;
case 'r': /* carriage-return */
*store = '\r';
break;
case 't': /* horizontal tab */
*store = '\t';
break;
case 'v': /* vertical tab */
*store = '\13';
break;
/* octal constant */
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
for (c = 3, value = 0;
c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
value <<= 3;
value += *fmt - '0';
}
--fmt;
*store = value;
break;
default:
*store = *fmt;
break;
}
}
*store = '\0';
}
static int
getchr()
{
if (!*gargv)
return ('\0');
return ((int)**gargv++);
}
static char *
getstr()
{
if (!*gargv)
return ("");
return (*gargv++);
}
static char *Number = "+-.0123456789";
static int
getint(ip)
int *ip;
{
long val;
if (getlong(&val))
return (1);
if (val > INT_MAX) {
warnx("%s: %s", *gargv, strerror(ERANGE));
return (1);
}
*ip = val;
return (0);
}
static int
getlong(lp)
long *lp;
{
long val;
char *ep;
if (!*gargv) {
*lp = 0;
return (0);
}
if (strchr(Number, **gargv)) {
errno = 0;
val = strtol(*gargv, &ep, 0);
if (*ep != '\0') {
warnx("%s: illegal number", *gargv, NULL);
return (1);
}
if (errno == ERANGE)
if (val == LONG_MAX) {
warnx("%s: %s", *gargv, strerror(ERANGE));
return (1);
}
if (val == LONG_MIN) {
warnx("%s: %s", *gargv, strerror(ERANGE));
return (1);
}
*lp = val;
++gargv;
return (0);
}
*lp = (long)asciicode();
return (0);
}
static double
getdouble()
{
if (!*gargv)
return ((double)0);
if (strchr(Number, **gargv))
return (atof(*gargv++));
return ((double)asciicode());
}
static int
asciicode()
{
register int ch;
ch = **gargv;
if (ch == '\'' || ch == '"')
ch = (*gargv)[1];
++gargv;
return (ch);
}
static void
usage()
{
(void)fprintf(stderr, "usage: printf format [arg ...]\n");
}

157
examples/loadables/tee.c Normal file
View file

@ -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 */
};

View file

@ -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
};

59
examples/loadables/tty.c Normal file
View file

@ -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 */
};

38
examples/misc/alias-conv.bash Executable file
View file

@ -0,0 +1,38 @@
#! /bin/bash
#
# alias-conv.sh - convert csh aliases to bash aliases and functions
#
# usage: alias-conv.sh
#
# 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 ()
{
if [ "x$2" = "x" ]; then
echo alias ${1}="''"
elif echo "$2" | egrep -s '(\!|#)' >/dev/null 2>&1; then
comm=$(echo $2 | sed 's/\!\*/"$\@"/g
s/\!:\([1-9]\)/"$\1"/g
s/#/\#/g')
echo $1 \(\) "{" command "$comm" "; }"
else
echo alias ${1}=\'$(echo "${2}" | sed "s:':'\\\\'':")\'
fi
}
EOF
sed "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

38
examples/misc/alias-conv.sh Executable file
View file

@ -0,0 +1,38 @@
#! /bin/bash
#
# alias-conv.sh - convert csh aliases to bash aliases and functions
#
# usage: alias-conv.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 ()
{
if [ "x$2" = "x" ]; then
echo alias ${1}="''"
elif echo "$2" | egrep -s '(\!|#)' >/dev/null 2>&1; then
comm=`echo $2 | sed 's/\\!\*/"$\@"/g
s/\\!:\([1-9]\)/"$\1"/g
s/#/\#/g'`
echo $1 \(\) "{" command "$comm" "; }"
else
echo alias ${1}=\'`echo "${2}" | sed "s:':'\\\\\\\\'':"`\'
fi
}
EOF
sed "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

130
examples/misc/cshtobash Executable file
View file

@ -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 ()
{
if [ -z "$2" ]; then
echo alias ${1}="''"
elif echo "$2" | egrep -s '(\!|#)' >/dev/null 2>&1; then
comm=$(echo $2 | sed 's/\!\*/"$\@"/g
s/\!:\([1-9]\)/"$\1"/g
s/#/\#/g')
echo $1 \(\) "{" command "$comm" "; }"
else
echo alias ${1}=\'$(echo "${2}" | sed "s:':'\\\\'':")\'
fi
}
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

View file

@ -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. :-)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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}" != '.' -a ".${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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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}" -o "${3}" -le "0" ]; then
return 0
fi
if [ ${3} -ge ${#1} -a ${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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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" -a -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" -a "$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

View file

@ -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

281
examples/scripts.v2/fman Normal file
View file

@ -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 egrep 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 -- `egrep "^$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[@]}"

288
examples/scripts.v2/frcp Executable file
View file

@ -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 -a -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

View file

@ -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

187
examples/scripts.v2/ncp Normal file
View file

@ -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[@]}"

View file

@ -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

187
examples/scripts.v2/nmv Normal file
View file

@ -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[@]}"

187
examples/scripts.v2/pages Normal file
View file

@ -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)
}
' "$@"

127
examples/scripts.v2/pf Normal file
View file

@ -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[@]}"

25
examples/scripts.v2/pmtop Normal file
View file

@ -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 +2 | sed ${SS}q
sleep 5
done
exit 0

122
examples/scripts.v2/rename Normal file
View file

@ -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 [ ! -a "$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
print -u2 "No globbing chars in pattern."
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

119
examples/scripts.v2/repeat Normal file
View file

@ -0,0 +1,119 @@
#! /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
;;
-[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
;;
*)
echo "$name: bad count parameter: $1" 1>&2
exit 1
;;
esac
shift
[ -z "$end" -o $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

View file

@ -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 "$@"

80
examples/scripts.v2/untar Normal file
View file

@ -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

45
examples/scripts.v2/uudec Normal file
View file

@ -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

69
examples/scripts.v2/uuenc Normal file
View file

@ -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

137
examples/scripts.v2/vtree Normal file
View file

@ -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 -o ! -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

111
examples/scripts.v2/where Normal file
View file

@ -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

0
examples/scripts/bcsh.sh Normal file → Executable file
View file

19
examples/scripts/inpath Executable file
View file

@ -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

View file

@ -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

0
examples/scripts/precedence Normal file → Executable file
View file

25
examples/scripts/scrollbar Executable file
View file

@ -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}
[ $# -lt 1 ] && set -- TESTING
# Posix.2 compatible printf command or bash loadable builtin
# in examples/loadables/printf
Text=$(printf "%-${WIDTH}s" "$*")
Text=$(echo "$Text" | tr ' ' '_')
while :
do
printf "%-.${WIDTH}s\r" "$Text"
LastC=$(expr "$Text" : '.*\(.\)$')
Text=$(printf "%-.${WIDTH}s" "$LastC$Text")
done

42
examples/scripts/vtree2 Executable file
View file

@ -0,0 +1,42 @@
#!/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
#
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 +1f | sed \
's/\([^ ]*\) \(.*\)/\2 (\1)/
'"s#^$1##"'
s#[^/]*/\([^/]*\)$#|____\1#
s#[^/]*/#| #g'
[ $# -gt 1 ] && echo
shift
done

26
examples/scripts/zprintf Executable file
View file

@ -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 }"

View file

@ -13,7 +13,6 @@ alias j="jobs -l"
alias l="ls -l "
alias ll="ls -l"
alias ls="ls -F"
alias term='set noglob; eval `tset -Q -s `'
alias pu="pushd"
alias po="popd"
@ -53,11 +52,12 @@ seq ()
{
local lower upper output;
lower=$1 upper=$2;
if [ $lower -ge $upper ]; then return; fi
while [ $lower -le $upper ];
do
output="$output $lower";
lower=$[ $lower + 1 ];
done;
echo $output
echo -n "$lower "
lower=$(($lower + 1))
done
echo "$lower"
}

View file

@ -2,9 +2,9 @@
#
default_dir=/usr/local/lib/
if [ "$PS1" ]; then
PS1='\u@\h(\#)$ '
ignoreeof=3
if [ -n "$PS1" ]; then
PS1='\u@\h(\#)\$ '
IGNOREEOF=3
fi
LOGIN_SHELL=true
@ -12,9 +12,7 @@ LOGIN_SHELL=true
# If the user has her own init file, then use that one, else use the
# canonical one.
if [ -f ~/.bashrc ]; then
source ~/.bashrc
else if [ -f ${default_dir}Bashrc ]; then
source ${default_dir}Bashrc;
fi
. ~/.bashrc
elif [ -f ${default_dir}Bashrc ]; then
. ${default_dir}Bashrc;
fi

View file

@ -16,57 +16,55 @@ X11=/usr/bin/X11
UTIL_PATH=$GNU:$X11
STANDARD_PATH=/usr/local/bin:/usr/ucb:/bin:/usr/bin:/usr/etc:/etc:/usr/games
if [ "$HOSTTYPE" = "sony" ]; then STANDARD_PATH=STANDARD_PATH:/usr/sony/bin; fi
if [ -d $HOME/bin/$HOSTTYPE ]; then
MY_PATH=$HOME/bin/$HOSTTYPE
MY_PATH=$HOME/bin/$HOSTTYPE
fi
if [ -d $HOME/bin ]; then
MY_PATH=$MY_PATH:$HOME/bin
MY_PATH=$MY_PATH:$HOME/bin
fi
if [ -d /usr/hosts ]; then
STANDARD_PATH=$STANDARD_PATH:/usr/hosts
STANDARD_PATH=$STANDARD_PATH:/usr/hosts
fi
PATH=.:$MY_PATH:$UTIL_PATH:$STANDARD_PATH
# If running interactively, then:
if [ "$PS1" ]; then
# 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.
# ignoreeof=
# Set auto_resume if you want to resume on "emacs", as well as on
# "%emacs".
auto_resume=
# Set notify if you want to be asynchronously notified about background
# job completion.
notify=
# Make it so that failed `exec' commands don't flush this shell.
no_exit_on_failed_exec=
if [ ! "$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'
if [ -f ~/.bash_aliases ]; then
source ~/.bash_aliases
fi
# 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

View file

@ -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.

View file

@ -1,52 +1,37 @@
HOME=/usr/homes/chet
MAIL=/usr/homes/chet/mbox
# This is the filename where your incoming mail arrives.
MAIL=~/mbox
MAILCHECK=30
HISTFILE=/usr/homes/chet/.history
MACHINE=$(/usr/local/bin/machine)
HOST=$(hostname)
HISTFILE=~/.history/history.$HOSTNAME
PATH1=/usr/homes/chet/bin.$MACHINE:/usr/local/bin/gnu:
PATH1=/usr/homes/chet/bin.$HOSTTYPE:/usr/local/bin/gnu:
PATH2=/usr/local/bin:/usr/ucb:/bin:/usr/bin/X11:.
PATH3=/usr/andrew/bin:/usr/bin:/usr/ibm:/usr/local/bin/mh:/usr/new/bin:
PATH3=/usr/bin:/usr/new/bin:/usr/contrib/bin
PATH=$PATH1:$PATH2:$PATH3
EDITOR=/usr/homes/chet/bin.$MACHINE/ce
VISUAL=/usr/homes/chet/bin.$MACHINE/ce
FCEDIT=/usr/homes/chet/bin.$MACHINE/ce
EDITOR=/usr/local/bin/ce VISUAL=/usr/local/bin/ce FCEDIT=/usr/local/bin/ce
if [ "$BASH" ] ; then
SHELL=$BASH
else
SHELL=/bin/bash
fi
if [ "$MACHINE" = "ibm032" ] ; then
stty erase ^H
fi
PAGER=/usr/ucb/more
NNTPSERVER=kiwi
NS=/nfs/cwjcc/fs1/ns-engr/proj/netsrv/cwpub/proto/src
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=EST5EDT
TZ=US/Eastern
export HOME ENV VISUAL EDITOR MAIL SHELL PATH TERM
export PAGER LESS TERMCAP HISTSZIE HISTFILE
export MAIL MAILCHECK HOST HOSTNAME NNTPSERVER NS LOGNAME TZ
export HOME VISUAL EDITOR MAIL SHELL PATH TERM
export PAGER LESS TERMCAP HISTSIZE HISTFILE MAIL MAILCHECK LOGNAME TZ
PS1="${HOST}$ "
PS1="${HOSTNAME}\$ "
PS2='> '
export PS1 PS2
umask 022
if [ -f /unix ] ; then
stty intr ^c
stty intr ^c # bogus
fi
if [ -f ~/.bashrc ] ; then

View file

@ -1,35 +1,35 @@
if [ "$PS1" != "" ] ; then
if [ -f /unix ] ; then
alias ls='/bin/ls -CF'
alias ll='/bin/ls -lCF'
alias dir='/bin/ls -bCalF'
else
alias ls='/bin/ls -F'
alias ll='/bin/ls -lF'
alias dir='/bin/ls -balF'
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 mail=/usr/ucb/mail
alias dot='ls .[a-zA-Z0-9]*'
alias mroe=more
alias pwd='echo $PWD'
alias pdw='echo $PWD'
alias news="xterm -g 80x45 -e rn -e &"
alias back='cd $OLDPWD'
alias manroff="nroff /usr/lib/tmac/tmac.an.4.3"
alias laser="lpr -Palw2"
alias lw="lpr -Palw2"
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`
export HOST=${HOSTNAME}
fi
history_control=ignoredups
HISTIGNORE="[ ]*:&:bg:fg"
psgrep()
{
@ -57,10 +57,14 @@ term()
tset
}
xtitle ()
{
echo -n -e "\033]0;$*\007"
}
cd()
{
builtin cd $*
xtitle $HOST: $PWD
builtin cd "$@" && xtitle $HOST: $PWD
}
bold()
@ -126,14 +130,3 @@ function chmog()
chgrp $3 $4
fi
}
#
# Source kshenv for ksh-compatibility definitions
#
if [ -f ~/.kshenv ] ; then
. ~/.kshenv
fi
fi
#end of .bashrc