summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/complete/complete-examples21
-rw-r--r--examples/complete/complete.freebsd31
-rw-r--r--examples/complete/complete.ianmac433
-rw-r--r--examples/complete/complete2.ianmac271
-rw-r--r--examples/functions/array-to-string15
-rw-r--r--examples/functions/emptydir28
-rw-r--r--examples/functions/fact2
-rw-r--r--examples/functions/gethtml35
-rw-r--r--examples/functions/ksh-cd35
-rw-r--r--examples/functions/kshenv44
-rw-r--r--examples/functions/lowercase2
-rw-r--r--examples/functions/recurse64
-rw-r--r--examples/functions/sort-pos-params50
-rw-r--r--examples/loadables/Makefile.in8
-rw-r--r--examples/loadables/cut.c376
-rw-r--r--examples/loadables/finfo.c6
-rw-r--r--examples/loadables/getconf.c75
-rw-r--r--examples/loadables/push.c95
-rw-r--r--examples/loadables/realpath.c119
-rwxr-xr-xexamples/scripts/adventure.sh2
-rw-r--r--examples/scripts/cat.sh12
-rw-r--r--examples/scripts/dd-ex.sh476
22 files changed, 2171 insertions, 29 deletions
diff --git a/examples/complete/complete-examples b/examples/complete/complete-examples
index 3b28dd2a..e1ad070a 100644
--- a/examples/complete/complete-examples
+++ b/examples/complete/complete-examples
@@ -352,7 +352,7 @@ _complete_meta_func()
if [[ $prev == -A ]]; then
COMPREPLY=(alias arrayvar binding builtin command directory \
-disabled enabled export file function helptopic hostname job keyword \
+disabled enabled export file 'function' helptopic hostname job keyword \
running setopt shopt signal stopped variable)
return 0
elif [[ $prev == -F ]]; then
@@ -400,7 +400,11 @@ _make_targets ()
esac
# make reads `makefile' before `Makefile'
- if [ -f makefile ]; then
+ # GNU make reads `GNUmakefile' before all other makefiles, but we
+ # check that we're completing `gmake' before checking for it
+ if [ -f GNUmakefile ] && [ ${COMP_WORDS[0]} == gmake ]; then
+ mdef=GNUmakefile
+ elif [ -f makefile ]; then
mdef=makefile
elif [ -f Makefile ]; then
mdef=Makefile
@@ -458,11 +462,13 @@ complete -d mkdir rmdir
complete -f strip
complete -f -X '*.gz' gzip
+complete -f -X '*.bz2' bzip2
complete -f -X '*.Z' compress
complete -f -X '!*.+(gz|tgz|Gz)' gunzip gzcat zcat zmore
complete -f -X '!*.Z' uncompress zmore zcat
-
-complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' xv
+complete -f -X '!*.bz2' bunzip2
+complete -f -X '!*.zip' unzip
+complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|JPEG|bmp)' xv
complete -f -X '!*.pl' perl perl5
@@ -472,11 +478,14 @@ complete -A hostname rxterm rxterm3 rxvt2
complete -u su
complete -f -X '!*.+(ps|PS)' gs gv ghostview psselect pswrap
-complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype
-complete -f -X '!*.+(pdf|PDF)' acroread
+complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype catdvi
+complete -f -X '!*.+(pdf|PDF)' acroread4
complete -f -X '!*.texi*' makeinfo texi2dvi texi2html
complete -f -X '!*.+(tex|TEX)' tex latex slitex
+complete -f -X '!*.+(mp3|MP3)' mpg123
+complete -f -X '!*.+(htm|html)' links w3m lynx
+
#
# other possibilities, left as exercises
#
diff --git a/examples/complete/complete.freebsd b/examples/complete/complete.freebsd
new file mode 100644
index 00000000..7f6f4c2a
--- /dev/null
+++ b/examples/complete/complete.freebsd
@@ -0,0 +1,31 @@
+#Date: Wed, 31 Jan 2001 12:53:56 -0800
+#From: Aaron Smith <aaron@mutex.org>
+#To: freebsd-ports@freebsd.org
+#Subject: useful bash completion function for pkg commands
+#Message-ID: <20010131125356.G52003@gelatinous.com>
+
+#hi all. i just wanted to share this bash completion function i wrote that
+#completes package names for pkg_info and pkg_delete. i find this a great
+#help when dealing with port management. programmed completion requires
+#bash-2.04.
+
+_pkg_func ()
+{
+ local cur
+
+ cur=${COMP_WORDS[COMP_CWORD]}
+
+ if [[ $cur == '-' ]]; then
+ if [[ ${COMP_WORDS[0]} == 'pkg_info' ]]; then
+ COMPREPLY=(-a -c -d -D -i -k -r -R -p -L -q -I -m -v -e -l)
+ return 0;
+ elif [[ ${COMP_WORDS[0]} == 'pkg_delete' ]]; then
+ COMPREPLY=(-v -D -d -n -f -p)
+ return 0;
+ fi
+ fi
+
+ COMPREPLY=( $(compgen -d /var/db/pkg/$cur | sed sN/var/db/pkg/NNg) )
+ return 0
+}
+complete -F _pkg_func pkg_delete pkg_info
diff --git a/examples/complete/complete.ianmac b/examples/complete/complete.ianmac
new file mode 100644
index 00000000..2af9fc74
--- /dev/null
+++ b/examples/complete/complete.ianmac
@@ -0,0 +1,433 @@
+#####
+#To: chet@po.cwru.edu, sarahmckenna@lucent.com
+#Message-Id: <slrn8mqioc.msb.ian@lovelorn.linuxcare.com>
+#Posted-To: comp.unix.shell, gnu.bash.bug
+#Subject: bash 2.04 programmable completion examples
+#Reply-To: ian@linuxcare.com, ian@caliban.org
+#Summary: examples of programmable completion for bash 2.04
+#Date: Thu, 13 Jul 2000 00:52:33 -0400 (EDT)
+#From: ianmacd@linuxcare.com (Ian Macdonald)
+#####
+
+#########################################################################
+# Turn on extended globbing
+shopt -s extglob
+
+# A lot of the following one-liners were taken directly from the
+# completion examples provided with the bash 2.04 source distribution
+
+# Make directory commands see only directories
+complete -d cd mkdir rmdir pushd
+
+# Make file commands see only files
+complete -f cat less more chown ln strip
+complete -f -X '*.gz' gzip
+complete -f -X '*.Z' compress
+complete -f -X '!*.+(Z|gz|tgz|Gz)' gunzip zcat zmore
+complete -f -X '!*.Z' uncompress zmore zcat
+complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' ee xv
+complete -f -X '!*.+(ps|PS|ps.gz)' gv
+complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype
+complete -f -X '!*.+(pdf|PDF)' acroread xpdf
+complete -f -X '!*.texi*' makeinfo texi2dvi texi2html
+complete -f -X '!*.+(tex|TEX)' tex latex slitex
+complete -f -X '!*.+(mp3|MP3)' mpg123
+
+# kill sees only signals
+complete -A signal kill -P '%'
+
+# user commands see only users
+complete -u finger su usermod userdel passwd
+
+# bg completes with stopped jobs
+complete -A stopped -P '%' bg
+
+# other job commands
+complete -j -P '%' fg jobs disown
+
+# network commands complete with hostname
+complete -A hostname ssh rsh telnet rlogin ftp ping fping host traceroute \
+ nslookup
+
+# export and others complete with shell variables
+complete -v export local readonly unset
+
+# set completes with set options
+complete -A setopt set
+
+# shopt completes with shopt options
+complete -A shopt shopt
+
+# helptopics
+complete -A helptopic help
+
+# unalias completes with aliases
+complete -a unalias
+
+# various commands complete with commands
+complete -c command type nohup exec nice eval strace gdb
+
+# bind completes with readline bindings (make this more intelligent)
+complete -A binding bind
+
+# Now we get to the meat of the file, the functions themselves. Some
+# of these are works in progress. Most assume GNU versions of the
+# tools in question and may require modifications for use on vanilla
+# UNIX systems.
+#
+# A couple of functions may have non-portable, Linux specific code in
+# them, but this will be noted where applicable
+
+
+# GNU chown(1) completion. This should be expanded to allow the use of
+# ':' as well as '.' as the user.group separator.
+#
+_chown ()
+{
+ local cur prev user group
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # do not attempt completion if we're specifying an option
+ if [ "${cur:0:1}" = "-" ]; then return 0; fi
+
+ # first parameter on line or first since an option?
+ if [ $COMP_CWORD -eq 1 ] || [ "${prev:0:1}" = "-" ]; then
+ case "$cur" in
+ [a-zA-Z]*.*)
+ user=${cur%.*}
+ group=${cur#*.}
+ COMPREPLY=( $( awk 'BEGIN {FS=":"} \
+ {if ($1 ~ /^'$group'/) print $1}' \
+ /etc/group ) )
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[i]=$user.${COMPREPLY[i]}
+ done
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $( compgen -u $cur -S '.' ) )
+ return 0
+ ;;
+ esac
+ else
+ COMPREPLY=( $( compgen -f $cur ) )
+ fi
+
+ return 0
+}
+complete -F _chown chown
+
+# umount(8) completion. This relies on the mount point being the third
+# space-delimited field in the output of mount(8)
+#
+_umount ()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+
+ # could rewrite the cut | grep to be a sed command, but this is
+ # clearer and doesn't result in much overhead
+ COMPREPLY=( $( mount | cut -d' ' -f 3 | grep ^$cur) )
+ return 0
+}
+complete -F _umount umount
+
+# GID completion. This will get a list of all valid group names from
+# /etc/group and should work anywhere.
+#
+_gid_func ()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($1 ~ /^'$cur'/) print $1}' \
+ /etc/group ) )
+ return 0
+}
+complete -F _gid_func groupdel groupmod
+
+# mount(8) completion. This will pull a list of possible mounts out of
+# /etc/fstab, unless the word being completed contains a ':', which
+# would indicate the specification of an NFS server. In that case, we
+# query the server for a list of all available exports and complete on
+# that instead.
+#
+_mount ()
+
+{ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+
+ case "$cur" in
+ *:*)
+ COMPREPLY=( $( /usr/sbin/showmount -e --no-headers ${cur%%:*} |\
+ grep ^${cur#*:} | awk '{print $1}'))
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $( awk '{if ($2 ~ /\//) print $2}' /etc/fstab | \
+ grep ^$cur ))
+ return 0
+ ;;
+ esac
+}
+complete -F _mount mount
+
+# Linux rmmod(1) completion. This completes on a list of all currently
+# installed kernel modules.
+#
+_rmmod ()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+
+ COMPREPLY=($( lsmod | awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}'))
+ return 0
+}
+complete -F _rmmod rmmod
+
+# Linux insmod(1) completion. This completes on a list of all
+# available modules for the version of the kernel currently running.
+#
+_insmod ()
+{
+ local cur modpath
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ modpath=/lib/modules/`uname -r`
+
+ COMPREPLY=($( ls -R $modpath | sed -ne 's/^\('$cur'.*\)\.o$/\1/p'))
+ return 0
+}
+complete -F _insmod insmod depmod modprobe
+
+# man(1) completion. This relies on the security enhanced version of
+# GNU locate(1). UNIX variants having non-numeric man page sections
+# other than l, m and n should add the appropriate sections to the
+# first clause of the case statement.
+#
+# This is Linux specific, in that 'man <section> <page>' is the
+# expected syntax. This allows one to do something like
+# 'man 3 str<tab>' to obtain a list of all string handling syscalls on
+# the system.
+#
+_man ()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ [0-9lmn])
+ COMPREPLY=($( slocate -ql 0 -r '/man/man'$prev'/'$cur | \
+ sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' ))
+ return 0
+ ;;
+ *)
+ COMPREPLY=($( slocate -ql 0 -r '/man/man./'$cur | \
+ sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' ))
+ return 0
+ ;;
+ esac
+}
+complete -F _man man
+
+# Linux killall(1) completion. This wouldn't be much use on, say,
+# Solaris, where killall does exactly that: kills ALL processes.
+#
+# This could be improved. For example, it currently doesn't take
+# command line options into account
+#
+_killall ()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -[A-Z0-9]*)
+ # get a list of processes (the first sed evaluation
+ # takes care of swapped out processes, the second
+ # takes care of getting the basename of the process)
+ COMPREPLY=( $( ps ahx | awk '{if ($5 ~ /^'$cur'/) print $5}' | \
+ sed -e 's#[]\[]##g' -e 's#^.*/##' ))
+ return 0
+ ;;
+ esac
+
+ # first parameter can be either a signal or a process
+ if [ $COMP_CWORD -eq 1 ]; then
+ # standard signal completion is rather braindead, so we need
+ # to hack around to get what we want here, which is to
+ # complete on a dash, followed by the signal name minus
+ # the SIG prefix
+ COMPREPLY=( $( compgen -A signal SIG${cur#-} ))
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[i]=-${COMPREPLY[i]#SIG}
+ done
+ fi
+
+ # get processes, adding to signals if applicable
+ COMPREPLY=( ${COMPREPLY[*]} $( ps ahx | \
+ awk '{if ($5 ~ /^'$cur'/) print $5}' | \
+ sed -e 's#[]\[]##g' -e 's#^.*/##' ))
+ return 0
+}
+complete -F _killall killall
+
+# GNU find(1) completion. This makes heavy use of ksh style extended
+# globs and contains Linux specific code for completing the parameter
+# to the -fstype option.
+#
+_find ()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]#-}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(max|min)depth)
+ COMPREPLY=( $( compgen -W '0 1 2 3 4 5 6 7 8 9' ) )
+ return 0
+ ;;
+ -?(a)newer|-fls|-fprint?(0|f))
+ COMPREPLY=( $( compgen -f $cur ) )
+ return 0
+ ;;
+ -fstype)
+ # this is highly non-portable (the option to -d is a tab)
+ COMPREPLY=( $( cut -d' ' -f 2 /proc/filesystems | grep ^$cur ) )
+ return 0
+ ;;
+ -gid)
+ COMPREPLY=( $( awk 'BEGIN {FS=":"} \
+ {if ($3 ~ /^'$cur'/) print $3}' /etc/group ) )
+ return 0
+ ;;
+ -group)
+ COMPREPLY=( $( awk 'BEGIN {FS=":"} \
+ {if ($1 ~ /^'$cur'/) print $1}' /etc/group ) )
+ return 0
+ ;;
+ -?(x)type)
+ COMPREPLY=( $( compgen -W 'b c d p f l s' $cur ) )
+ return 0
+ ;;
+ -uid)
+ COMPREPLY=( $( awk 'BEGIN {FS=":"} \
+ {if ($3 ~ /^'$cur'/) print $3}' /etc/passwd ) )
+ return 0
+ ;;
+ -user)
+ COMPREPLY=( $( compgen -u $cur ) )
+ return 0
+ ;;
+ -[acm]min|-[acm]time|-?(i)?(l)name|-inum|-?(i)path|-?(i)regex| \
+ -links|-perm|-size|-used|-exec|-ok|-printf)
+ # do nothing, just wait for a parameter to be given
+ return 0
+ ;;
+ esac
+
+ # complete using basic options ($cur has had its dash removed here,
+ # as otherwise compgen will bomb out with an error, since it thinks
+ # the dash is an option to itself)
+ COMPREPLY=( $( compgen -W 'daystart depth follow help maxdepth \
+ mindepth mount noleaf version xdev amin anewer atime \
+ cmin cnewer ctime empty false fstype gid group ilname \
+ iname inum ipath iregex links lname mmin mtime name \
+ newer nouser nogroup perm regex size true type uid \
+ used user xtype exec fls fprint fprint0 fprintf ok \
+ print print0 printf prune ls' $cur ) )
+
+ # this removes any options from the list of completions that have
+ # already been specified somewhere on the command line.
+ COMPREPLY=( $( echo "${COMP_WORDS[@]}-" | \
+ (while read -d '-' i; do
+ [ "$i" == "" ] && continue
+ # flatten array with spaces on either side,
+ # otherwise we cannot grep on word boundaries of
+ # first and last word
+ COMPREPLY=" ${COMPREPLY[@]} "
+ # remove word from list of completions
+ COMPREPLY=( ${COMPREPLY/ ${i%% *} / } )
+ done
+ echo ${COMPREPLY[@]})
+ ) )
+
+ # put dashes back
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[i]=-${COMPREPLY[i]}
+ done
+
+ return 0
+}
+complete -F _find find
+
+# Linux ifconfig(8) completion
+#
+_ifconfig ()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+
+ case "${COMP_WORDS[1]}" in
+ -|*[0-9]*)
+ COMPREPLY=( $( compgen -W '-a up down arp promisc allmulti \
+ metric mtu dstaddr netmask add del \
+ tunnel irq io_addr mem_start media \
+ broadcast pointopoint hw multicast \
+ address txqueuelen' $cur ))
+ COMPREPLY=( $( echo " ${COMP_WORDS[@]}" | \
+ (while read -d ' ' i; do
+ [ "$i" == "" ] && continue
+ # flatten array with spaces on either side,
+ # otherwise we cannot grep on word
+ # boundaries of first and last word
+ COMPREPLY=" ${COMPREPLY[@]} "
+ # remove word from list of completions
+ COMPREPLY=( ${COMPREPLY/ $i / } )
+ done
+ echo ${COMPREPLY[@]})
+ ) )
+ return 0
+ ;;
+ esac
+
+ COMPREPLY=( $( ifconfig -a | sed -ne 's/^\('$cur'[^ ]*\).*$/\1/p' ))
+}
+complete -F _ifconfig ifconfig
+
+# Linux ipsec(8) completion (for FreeS/WAN). Very basic.
+#
+_ipsec ()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+
+ COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look manual \
+ pluto ranbits rsasigkey setup showdefaults \
+ showhostkey spi spigrp tncfg whack' $cur ))
+}
+complete -F _ipsec ipsec
+#########################################################################
diff --git a/examples/complete/complete2.ianmac b/examples/complete/complete2.ianmac
new file mode 100644
index 00000000..6fb0a964
--- /dev/null
+++ b/examples/complete/complete2.ianmac
@@ -0,0 +1,271 @@
+#####
+#From: ian@linuxcare.com (Ian Macdonald)
+#Newsgroups: comp.unix.shell
+#Subject: More bash 2.04 completions
+#Date: 12 Aug 2000 09:53:40 GMT
+#Organization: Linuxcare, Inc.
+#Lines: 274
+#Message-ID: <slrn8pa7l2.jgm.ian@lovelorn.linuxcare.com>
+#Reply-To: ian@linuxcare.com
+#####
+
+# Turn on extended globbing
+shopt -s extglob
+
+# cvs(1) completion
+#
+_cvs ()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ $COMP_CWORD -eq 1 ] || [ "${prev:0:1}" = "-" ]; then
+ COMPREPLY=( $( compgen -W 'add admin checkout commit diff \
+ export history import log rdiff release remove rtag status \
+ tag update' $cur ))
+ else
+ COMPREPLY=( $( compgen -f $cur ))
+ fi
+ return 0
+}
+complete -F _cvs cvs
+
+# rpm(8) completion. This isn't exhaustive yet, but still provides
+# quite a lot of functionality.
+#
+_rpm()
+{
+ dashify()
+ {
+ local i
+
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ if [ ${#COMPREPLY[i]} -le 2 ]; then
+ COMPREPLY[i]=-${COMPREPLY[i]}
+ else
+ COMPREPLY[i]=--${COMPREPLY[i]}
+ fi
+ done
+ }
+
+ local cur cur_nodash prev
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ cur_nodash=${cur#-}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ $COMP_CWORD = 1 ]; then
+ # first parameter on line
+ case "$cur" in
+ -b*)
+ COMPREPLY=( $( compgen -W 'ba bb bc bi bl bp bs' \
+ $cur_nodash ) )
+ dashify
+ return 0
+ ;;
+ -t*)
+ COMPREPLY=( $( compgen -W 'ta tb tc ti tl tp ts' \
+ $cur_nodash ) )
+ dashify
+ return 0
+ ;;
+ --*)
+ COMPREPLY=( $( compgen -W 'help version initdb \
+ checksig recompile rebuild resign addsign rebuilddb \
+ showrc setperms setgids' ${cur_nodash#-} ) )
+ dashify;
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W 'b e F i q t U V' \
+ $cur_nodash ) )
+ dashify
+ return 0
+ ;;
+ esac
+ fi
+
+ case "${COMP_WORDS[1]}" in
+ -[iFU]*)
+ # complete on list of relevant options
+ COMPREPLY=( $( compgen -W 'percent force test replacepkgs \
+ replacefiles root excludedocs includedocs noscripts rcfile \
+ ignorearch dbpath prefix ignoreos nodeps allfiles ftpproxy \
+ ftpport justdb httpproxy httpport noorder relocate badreloc \
+ notriggers excludepath ignoresize oldpackage' ${cur_nodash#-} ))
+ dashify;
+ # return if $cur is an option
+ [ "${cur:0:1}" = "-" ] && return 0
+ # add a list of RPMS to possible completions
+ COMPREPLY=( ${COMPREPLY[@]} $( compgen -G $cur\*.rpm ) )
+ return 0
+ ;;
+ -qp*)
+ # complete on list of relevant options
+ COMPREPLY=( $( compgen -W 'scripts root rcfile whatprovides \
+ whatrequires requires triggeredby ftpport ftpproxy httpproxy \
+ httpport provides triggers dump changelog dbpath filesbypkg' \
+ ${cur_nodash#-} ) )
+ dashify;
+ # return if $cur is an option
+ [ "${cur:0:1}" = "-" ] && return 0
+ # add a list of RPMS to possible completions
+ COMPREPLY=( ${COMPREPLY[@]} $( compgen -G $cur\*.rpm ) )
+ return 0
+ ;;
+ -*f)
+ # standard filename completion
+ COMPREPLY=( $( compgen -f $cur ) )
+ return 0
+ ;;
+ -e)
+ # complete on list of relevant options
+ COMPREPLY=( $( compgen -W 'allmatches noscripts notriggers \
+ nodeps test' ${cur_nodash#-} ) )
+ dashify;
+ # return if $cur is an option
+ [ "${cur:0:1}" = "-" ] && return 0
+ # complete on basename of installed RPMs
+ COMPREPLY=( $( rpm -qa | \
+ sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) )
+ return 0
+ ;;
+ -qa*)
+ # complete on list of relevant options
+ COMPREPLY=( $( compgen -W 'scripts root rcfile whatprovides \
+ whatrequires requires triggeredby ftpport ftpproxy httpproxy \
+ httpport provides triggers dump changelog dbpath specfile \
+ querybynumber last filesbypkg' ${cur_nodash#-} ) )
+ dashify;
+ return 0
+ ;;
+ -q*)
+ # complete on list of relevant options
+ COMPREPLY=( $( compgen -W 'scripts root rcfile whatprovides \
+ whatrequires requires triggeredby ftpport ftpproxy httpproxy \
+ httpport provides triggers dump changelog dbpath specfile \
+ querybynumber last filesbypkg' ${cur_nodash#-} ) )
+ dashify;
+ # return if $cur is an option
+ [ "${cur:0:1}" = "-" ] && return 0
+ # add a list of RPMS to possible completions
+ COMPREPLY=( ${COMPREPLY[@]} $( rpm -qa | \
+ sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) )
+ return 0
+ ;;
+ -[Vy]*)
+ # complete on list of relevant options
+ COMPREPLY=( $( compgen -W 'root rcfile dbpath nodeps nofiles \
+ noscripts nomd5 nopgp' ${cur_nodash#-} ) )
+ dashify;
+ # return if $cur is an option
+ [ "${cur:0:1}" = "-" ] && return 0
+ # add a list of RPMS to possible completions
+ COMPREPLY=( ${COMPREPLY[@]} $( rpm -qa | \
+ sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) )
+ return 0
+ ;;
+ -b*)
+ # complete on list of relevant options
+ COMPREPLY=( $( compgen -W 'short-circuit timecheck clean \
+ rmsource test sign buildroot target buildarch buildos' \
+ ${cur_nodash#-} ) )
+ dashify;
+ # return if $cur is an option
+ [ "${cur:0:1}" = "-" ] && return 0
+ # complete on .spec files
+ COMPREPLY=( $( compgen -G $cur\*.spec ) )
+ return 0
+ ;;
+ -t*)
+ # complete on list of relevant options
+ COMPREPLY=( $( compgen -W 'short-circuit timecheck clean \
+ rmsource test sign buildroot target buildarch buildos' \
+ ${cur_nodash#-} ) )
+ dashify;
+ # return if $cur is an option
+ [ "${cur:0:1}" = "-" ] && return 0
+ # complete on .tar.gz files
+ COMPREPLY=( $( compgen -G $cur\*.tar.gz ) )
+ return 0
+ ;;
+ --re@(build|compile))
+ # complete on source RPMs
+ COMPREPLY=( $( compgen -G $cur\*.src.rpm ) )
+ return 0
+ ;;
+ --@(checksig|@(re|add)sign))
+ # complete on RPMs
+ COMPREPLY=( $( compgen -G $cur\*.rpm ) )
+ return 0
+ ;;
+ --set@(perms|gids))
+ # complete on installed RPMs
+ COMPREPLY=( ${COMPREPLY[@]} $( rpm -qa | \
+ sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) )
+ return 0
+ ;;
+ esac
+}
+complete -F _rpm rpm
+
+# chsh(1) completion
+#
+_chsh()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ "$prev" = "-s" ]; then
+ COMPREPLY=( $( chsh -l | grep ^$cur ) )
+ else
+ COMPREPLY=( $( compgen -u $cur ) )
+ fi
+}
+complete -F _chsh chsh
+
+# chkconfig(8) completion
+#
+_chkconfig()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]}
+ cur_nodash=${cur#--}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'list add del level' $cur_nodash ) )
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[i]=--${COMPREPLY[i]}
+ done
+ return 0
+ fi
+
+ if [ $COMP_CWORD -eq 4 ]; then
+ COMPREPLY=( $( compgen -W 'on off reset' $cur ) )
+ return 0
+ fi
+
+ case "$prev" in
+ @([1-6]|--@(list|add|del)))
+ COMPREPLY=( $( compgen -W "`(cd /etc/rc.d/init.d; echo *)`" \
+ $cur) )
+ return 0
+ ;;
+ --level)
+ COMPREPLY=( $( compgen -W '1 2 3 4 5 6' $cur ) )
+ return 0
+ ;;
+ esac
+}
+complete -F _chkconfig chkconfig
+###
diff --git a/examples/functions/array-to-string b/examples/functions/array-to-string
new file mode 100644
index 00000000..0d2fbe50
--- /dev/null
+++ b/examples/functions/array-to-string
@@ -0,0 +1,15 @@
+#! /bin/bash
+
+# Format: array_to_string vname_of_array vname_of_string separator
+array_to_string()
+{
+ (( ($# < 2) || ($# > 3) )) && {
+ "$FUNCNAME: usage: $FUNCNAME arrayname stringname [separator]"
+ return 2
+ }
+
+ local array=$1 string=$2
+ ((3==$#)) && [[ $3 = ? ]] && local IFS="${3}${IFS}"
+ eval $string="\"\${$array[*]}\""
+ return 0
+}
diff --git a/examples/functions/emptydir b/examples/functions/emptydir
new file mode 100644
index 00000000..412af5b1
--- /dev/null
+++ b/examples/functions/emptydir
@@ -0,0 +1,28 @@
+#! /bin/bash
+#
+#Derived from:
+#
+#From: damercer@mmm.com (Dan Mercer)
+#Newsgroups: comp.unix.admin,comp.unix.shell,comp.unix.programmer,comp.sys.sun.admin
+#Subject: Re: Command to find out if a directory is empty
+#Date: 17 Aug 2000 14:35:56 GMT
+#Message-ID: <8ngt8c$fmr$1@magnum.mmm.com>
+
+# usage: emptydir [dirname] ; default dirname is "."
+
+emptydir()
+{
+ typeset file dir=${1:-.}
+ [[ -d $dir ]] || {
+ echo "$FUNCNAME: $dir is not a directory" >&2
+ return 2
+ }
+ for file in $dir/.* $dir/*
+ do
+ case ${file#$dir/} in
+ .|..) ;;
+ \*) [[ -e $file ]];let $?;return;;
+ *) return 1;;
+ esac
+ done
+}
diff --git a/examples/functions/fact b/examples/functions/fact
index cd7bf467..97efd498 100644
--- a/examples/functions/fact
+++ b/examples/functions/fact
@@ -9,5 +9,5 @@ fact ()
echo 1
return ;
fi;
- echo $[ $num * $(fact $[ $num - 1 ])]
+ echo $(( $num * $(fact $(( $num - 1 )) ) ))
}
diff --git a/examples/functions/gethtml b/examples/functions/gethtml
new file mode 100644
index 00000000..2eec1d89
--- /dev/null
+++ b/examples/functions/gethtml
@@ -0,0 +1,35 @@
+#
+# get_html -- get a web page from a remote server
+#
+# Original Author: Jeff Korn <jlk@cs.princeton.edu>
+# Modified for bash by Chet Ramey <chet@po.cwru.edu>
+#
+# Example: get_html cnswww.cns.cwru.edu /~chet/ | more
+
+get_html()
+{
+ local host port
+
+ (($# < 2)) && {
+ echo "usage: $FUNCNAME hostname path [port]" >&2
+ return 1
+ }
+
+ host="$1"
+ port="${3:-80}"
+
+ exec 3<> /dev/tcp/$host/$port || {
+ echo "$FUNCNAME: $host/$port: cannot connect" >&2
+ exit 1
+ }
+
+ echo -e "GET $2 HTTP/1.0\n" >&3
+
+ cat <&3
+
+ exec 3<&-
+
+ return 0
+}
+
+get_html "$@"
diff --git a/examples/functions/ksh-cd b/examples/functions/ksh-cd
new file mode 100644
index 00000000..801a4909
--- /dev/null
+++ b/examples/functions/ksh-cd
@@ -0,0 +1,35 @@
+#
+# ksh-like `cd': cd [-LP] [dir [change]]
+#
+cd()
+{
+ OPTIND=1
+ while getopts "LP" opt
+ do
+ case $opt in
+ L|P) CDOPTS="$CDOPTS -$opt" ;;
+ *) echo "$FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2
+ return 2;;
+ esac
+ done
+
+ shift $(( $OPTIND - 1 ))
+
+ case $# in
+ 0) builtin cd $CDOPTS "$HOME" ;;
+ 1) builtin cd $CDOPTS "$@" ;;
+ 2) old="$1" new="$2"
+ case "$PWD" in
+ *$old*) ;;
+ *) echo "${0##*/}: $FUNCNAME: bad substitution" >&2 ; return 1 ;;
+ esac
+
+ dir=${PWD//$old/$new}
+
+ builtin cd $CDOPTS "$dir" && echo "$PWD"
+
+ ;;
+ *) echo "${0##*/}: $FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2
+ return 2 ;;
+ esac
+}
diff --git a/examples/functions/kshenv b/examples/functions/kshenv
index 35cc2124..2b9a6ebb 100644
--- a/examples/functions/kshenv
+++ b/examples/functions/kshenv
@@ -109,27 +109,39 @@ $1 == "'$cmd'" && $2 == "()" {printit=0; next; }
# whence -v "$*"
#}
+#
+# ksh-like `cd': cd [-LP] [dir [change]]
+#
cd()
{
+ OPTIND=1
+ while getopts "LP" opt
+ do
+ case $opt in
+ L|P) CDOPTS="$CDOPTS -$opt" ;;
+ *) echo "$FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2
+ return 2;;
+ esac
+ done
+
+ shift $(( $OPTIND - 1 ))
+
case $# in
- 0) builtin cd "$HOME" ;;
- 1) builtin cd "$@" ;;
- 2) old="$1"
- new="$2"
- # dir=$(echo "$PWD" | sed "s:$old:$new:g")
- dir=${PWD//$old/$new}
- case "$dir" in
- "$PWD") case "$PWD" in
- *$old*) ;;
- *) echo "$FUNCNAME: bad substitution" >&2 ; return 1 ;;
- esac ;;
- *) echo "$dir"
- builtin cd "$dir"
- ;;
+ 0) builtin cd $CDOPTS "$HOME" ;;
+ 1) builtin cd $CDOPTS "$@" ;;
+ 2) old="$1" new="$2"
+ case "$PWD" in
+ *$old*) ;;
+ *) echo "${0##*/}: $FUNCNAME: bad substitution" >&2 ; return 1 ;;
esac
+
+ dir=${PWD//$old/$new}
+
+ builtin cd $CDOPTS "$dir" && echo "$PWD"
+
;;
- *) echo "$FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2
- return 1 ;;
+ *) echo "${0##*/}: $FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2
+ return 2 ;;
esac
}
diff --git a/examples/functions/lowercase b/examples/functions/lowercase
index e3f866e0..3cf6bde2 100644
--- a/examples/functions/lowercase
+++ b/examples/functions/lowercase
@@ -11,7 +11,7 @@ lowercase()
for file; do
[ -f "$file" ] || continue
filename=${file##*/}
- case "$filename" in
+ case "$file" in
*/*) dirname=${file%/*} ;;
*) dirname=.;;
esac
diff --git a/examples/functions/recurse b/examples/functions/recurse
new file mode 100644
index 00000000..14c4b9e3
--- /dev/null
+++ b/examples/functions/recurse
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+#From: kaz@ashi.footprints.net (Kaz Kylheku)
+#Newsgroups: comp.os.linux.misc
+#Subject: Re: bash question: subdirectories
+#Message-ID: <slrn8a0gu9.v5n.kaz@ashi.FootPrints.net>
+#Date: Tue, 08 Feb 2000 16:24:35 GMT
+
+#Actually it can be made to. That is to say, it is possible to code a recursive
+#descender function in the bash language. Here is an example.
+#
+#What is nice about this is that you can embed the function into your shell
+#script. The function changes the current working directory as it descends.
+#So it can handle arbitrarily deep paths. Whereas paths generated by the
+#find command can cause a problem when they get too long; the kernel has a
+#hard limit on the length of the string passed to the open() and other
+#system calls.
+
+#There are races; what if the directory tree is blown away during the traversal?
+#The function won't be able to crawl back up using the .. link and will just
+#bail.
+
+# Recursive Directory Traverser
+# Author: Kaz Kylheku
+# Date: Feb 27, 1999
+# Copyright 1999
+
+# Function parameter usage:
+# $1 directory to search
+# $2 pattern to search for
+# $3 command to execute
+# $4 secret argument for passing down path
+
+function recurse
+{
+ local file
+ local path
+
+ if [ "$4" = "" ] ; then
+ path="${1%/}/"
+ else
+ path="$4$1/"
+ fi
+
+ if cd "$1" ; then
+ for file in $2; do
+ if [ -f "$file" -o -d "$file" ]; then
+ eval "$3"
+ fi
+ done
+ for file in .* * ; do
+ if [ "$file" = "." -o "$file" = ".." ] ; then
+ continue
+ fi
+ if [ -d "$file" -a ! -L "$file" ]; then
+ recurse "$file" "$2" "$3" "$path"
+ fi
+ done
+ cd ..
+ fi
+}
+
+recurse "$1" "$2" 'echo "$path$file"'
+
diff --git a/examples/functions/sort-pos-params b/examples/functions/sort-pos-params
new file mode 100644
index 00000000..0052b466
--- /dev/null
+++ b/examples/functions/sort-pos-params
@@ -0,0 +1,50 @@
+# Sort the positional paramters.
+# Make sure the positional parameters are passed as arguments to the function.
+# If -u is the first arg, remove duplicate array members.
+sort_posparams()
+{
+ local -a R
+ local u
+
+ case "$1" in
+ -u) u=-u ; shift ;;
+ esac
+
+ # if you want the case of no positional parameters to return success,
+ # remove the error message and return 0
+ if [ $# -eq 0 ]; then
+ echo "$FUNCNAME: argument expected" >&2
+ return 1
+ fi
+
+ # make R a copy of the positional parameters
+ R=( "${@}" )
+
+ # sort R.
+ R=( $( printf "%s\n" "${R[@]}" | sort $u) )
+
+ printf "%s\n" "${R[@]}"
+ return 0
+}
+
+# will print everything on separate lines
+set -- 3 1 4 1 5 9 2 6 5 3 2
+sort_posparams "$@"
+
+# sets without preserving quoted parameters
+set -- $( sort_posparams "$@" )
+echo "$@"
+echo $#
+
+# sets preserving quoted parameters, beware pos params with embedded newlines
+set -- 'a b' 'a c' 'x z'
+
+oifs=$IFS
+IFS=$'\n'
+set -- $( sort_posparams "$@" )
+IFS="$oifs"
+
+echo "$@"
+echo $#
+
+sort_posparams
diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in
index ab3c5d7e..95710890 100644
--- a/examples/loadables/Makefile.in
+++ b/examples/loadables/Makefile.in
@@ -67,7 +67,7 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins \
ALLPROG = print truefalse sleep pushd finfo logname basename dirname \
tty pathchk tee head mkdir rmdir sprintf printenv id whoami \
- uname sync push ln unlink
+ uname sync push ln unlink cut realpath
OTHERPROG = necho getconf hello cat
all: $(SHOBJ_STATUS)
@@ -164,6 +164,11 @@ ln: ln.o
unlink: unlink.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ unlink.o $(SHOBJ_LIBS)
+cut: cut.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cut.o $(SHOBJ_LIBS)
+
+realpath: realpath.o
+ $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ realpath.o $(SHOBJ_LIBS)
# pushd is a special case. We use the same source that the builtin version
# uses, with special compilation options.
@@ -214,3 +219,4 @@ uname.o: uname.c
sync.o: sync.c
push.o: push.c
mkdir.o: mkdir.c
+realpath.o: realpath.c
diff --git a/examples/loadables/cut.c b/examples/loadables/cut.c
new file mode 100644
index 00000000..d874034a
--- /dev/null
+++ b/examples/loadables/cut.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam S. Moskowitz of Menlo Consulting and Marciano Pitargue.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)cut.c 8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "bashansi.h"
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#if !defined (_POSIX2_LINE_MAX)
+# define _POSIX2_LINE_MAX 2048
+#endif
+
+static int cflag;
+static char dchar;
+static int dflag;
+static int fflag;
+static int sflag;
+
+static int autostart, autostop, maxval;
+static char positions[_POSIX2_LINE_MAX + 1];
+
+static int c_cut __P((FILE *, char *));
+static int f_cut __P((FILE *, char *));
+static int get_list __P((char *));
+static char *_cut_strsep __P((char **, const char *));
+
+int
+cut_builtin(list)
+ WORD_LIST *list;
+{
+ FILE *fp;
+ int (*fcn) __P((FILE *, char *)) = NULL;
+ int ch;
+
+ fcn = NULL;
+ dchar = '\t'; /* default delimiter is \t */
+
+ /* Since we don't support multi-byte characters, the -c and -b
+ options are equivalent, and the -n option is meaningless. */
+ reset_internal_getopt ();
+ while ((ch = internal_getopt (list, "b:c:d:f:sn")) != -1)
+ switch(ch) {
+ case 'b':
+ case 'c':
+ fcn = c_cut;
+ if (get_list(list_optarg) < 0)
+ return (EXECUTION_FAILURE);
+ cflag = 1;
+ break;
+ case 'd':
+ dchar = *list_optarg;
+ dflag = 1;
+ break;
+ case 'f':
+ fcn = f_cut;
+ if (get_list(list_optarg) < 0)
+ return (EXECUTION_FAILURE);
+ fflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'n':
+ break;
+ case '?':
+ default:
+ builtin_usage();
+ return (EX_USAGE);
+ }
+
+ list = loptend;
+
+ if (fflag) {
+ if (cflag) {
+ builtin_usage();
+ return (EX_USAGE);
+ }
+ } else if (!cflag || dflag || sflag) {
+ builtin_usage();
+ return (EX_USAGE);
+ }
+
+ if (list) {
+ while (list) {
+ fp = fopen(list->word->word, "r");
+ if (fp == 0) {
+ builtin_error("%s", list->word->word);
+ return (EXECUTION_FAILURE);
+ }
+ ch = (*fcn)(fp, list->word->word);
+ (void)fclose(fp);
+ if (ch < 0)
+ return (EXECUTION_FAILURE);
+ list = list->next;
+ }
+ } else {
+ ch = (*fcn)(stdin, "stdin");
+ if (ch < 0)
+ return (EXECUTION_FAILURE);
+ }
+
+ return (EXECUTION_SUCCESS);
+}
+
+static int
+get_list(list)
+ char *list;
+{
+ int setautostart, start, stop;
+ char *pos;
+ char *p;
+
+ /*
+ * set a byte in the positions array to indicate if a field or
+ * column is to be selected; use +1, it's 1-based, not 0-based.
+ * This parser is less restrictive than the Draft 9 POSIX spec.
+ * POSIX doesn't allow lists that aren't in increasing order or
+ * overlapping lists. We also handle "-3-5" although there's no
+ * real reason too.
+ */
+ for (; (p = _cut_strsep(&list, ", \t")) != NULL;) {
+ setautostart = start = stop = 0;
+ if (*p == '-') {
+ ++p;
+ setautostart = 1;
+ }
+ if (isdigit((unsigned char)*p)) {
+ start = stop = strtol(p, &p, 10);
+ if (setautostart && start > autostart)
+ autostart = start;
+ }
+ if (*p == '-') {
+ if (isdigit((unsigned char)p[1]))
+ stop = strtol(p + 1, &p, 10);
+ if (*p == '-') {
+ ++p;
+ if (!autostop || autostop > stop)
+ autostop = stop;
+ }
+ }
+ if (*p) {
+ builtin_error("[-cf] list: illegal list value");
+ return -1;
+ }
+ if (!stop || !start) {
+ builtin_error("[-cf] list: values may not include zero");
+ return -1;
+ }
+ if (stop > _POSIX2_LINE_MAX) {
+ builtin_error("[-cf] list: %d too large (max %d)",
+ stop, _POSIX2_LINE_MAX);
+ return -1;
+ }
+ if (maxval < stop)
+ maxval = stop;
+ for (pos = positions + start; start++ <= stop; *pos++ = 1);
+ }
+
+ /* overlapping ranges */
+ if (autostop && maxval > autostop)
+ maxval = autostop;
+
+ /* set autostart */
+ if (autostart)
+ memset(positions + 1, '1', autostart);
+
+ return 0;
+}
+
+/* ARGSUSED */
+static int
+c_cut(fp, fname)
+ FILE *fp;
+ char *fname;
+{
+ int ch, col;
+ char *pos;
+
+ ch = 0;
+ for (;;) {
+ pos = positions + 1;
+ for (col = maxval; col; --col) {
+ if ((ch = getc(fp)) == EOF)
+ return;
+ if (ch == '\n')
+ break;
+ if (*pos++)
+ (void)putchar(ch);
+ }
+ if (ch != '\n') {
+ if (autostop)
+ while ((ch = getc(fp)) != EOF && ch != '\n')
+ (void)putchar(ch);
+ else
+ while ((ch = getc(fp)) != EOF && ch != '\n');
+ }
+ (void)putchar('\n');
+ }
+ return (0);
+}
+
+static int
+f_cut(fp, fname)
+ FILE *fp;
+ char *fname;
+{
+ int ch, field, isdelim;
+ char *pos, *p, sep;
+ int output;
+ char lbuf[_POSIX2_LINE_MAX + 1];
+
+ for (sep = dchar; fgets(lbuf, sizeof(lbuf), fp);) {
+ output = 0;
+ for (isdelim = 0, p = lbuf;; ++p) {
+ if (!(ch = *p)) {
+ builtin_error("%s: line too long.", fname);
+ return -1;
+ }
+ /* this should work if newline is delimiter */
+ if (ch == sep)
+ isdelim = 1;
+ if (ch == '\n') {
+ if (!isdelim && !sflag)
+ (void)printf("%s", lbuf);
+ break;
+ }
+ }
+ if (!isdelim)
+ continue;
+
+ pos = positions + 1;
+ for (field = maxval, p = lbuf; field; --field, ++pos) {
+ if (*pos) {
+ if (output++)
+ (void)putchar(sep);
+ while ((ch = *p++) != '\n' && ch != sep)
+ (void)putchar(ch);
+ } else {
+ while ((ch = *p++) != '\n' && ch != sep)
+ continue;
+ }
+ if (ch == '\n')
+ break;
+ }
+ if (ch != '\n') {
+ if (autostop) {
+ if (output)
+ (void)putchar(sep);
+ for (; (ch = *p) != '\n'; ++p)
+ (void)putchar(ch);
+ } else
+ for (; (ch = *p) != '\n'; ++p);
+ }
+ (void)putchar('\n');
+ }
+ return (0);
+}
+
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+static char *
+_cut_strsep(stringp, delim)
+ register char **stringp;
+ register const char *delim;
+{
+ register char *s;
+ register const char *spanp;
+ register int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return (NULL);
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
+static char *cut_doc[] = {
+ "Select portions of each line (as specified by LIST) from each FILE",
+ "(by default, the standard input), and write them to the standard output.",
+ "Items specified by LIST are either column positions or fields delimited",
+ "by a special character. Column numbering starts at 1.",
+ (char *)0
+};
+
+struct builtin cut_struct = {
+ "cut",
+ cut_builtin,
+ BUILTIN_ENABLED,
+ cut_doc,
+ "cut -b list [-n] [file ...] OR cut -c list [file ...] OR cut -f list [-s] [-d delim] [file ...]",
+ 0
+};
diff --git a/examples/loadables/finfo.c b/examples/loadables/finfo.c
index ab3c9a41..5fdb6d43 100644
--- a/examples/loadables/finfo.c
+++ b/examples/loadables/finfo.c
@@ -9,6 +9,7 @@
#include <grp.h>
#include <errno.h>
+#include "bashansi.h"
#include "shell.h"
#include "builtins.h"
#include "common.h"
@@ -17,7 +18,6 @@
extern int errno;
#endif
-extern char *strrchr();
extern char **make_builtin_argv ();
static int printst();
@@ -137,7 +137,11 @@ char *f;
fd = lfd;
r = fstat(fd, &st);
} else
+#ifdef HAVE_LSTAT
+ r = lstat(f, &st);
+#else
r = stat(f, &st);
+#endif
if (r < 0) {
builtin_error("%s: cannot stat: %s", f, strerror(errno));
return ((struct stat *)0);
diff --git a/examples/loadables/getconf.c b/examples/loadables/getconf.c
index 64407cc9..cb8344ca 100644
--- a/examples/loadables/getconf.c
+++ b/examples/loadables/getconf.c
@@ -55,7 +55,7 @@
struct conf_variable
{
const char *name;
- enum { SYSCONF, CONFSTR, PATHCONF, CONSTANT } type;
+ enum { SYSCONF, CONFSTR, PATHCONF, CONSTANT, G_UNDEF } type;
long value;
};
@@ -105,19 +105,55 @@ static const struct conf_variable conf_table[] =
#endif /* _CS_XBS5_ILP32_OFF32_CFLAGS */
/* POSIX.2 Utility Limit Minimum Values */
+#ifdef _POSIX2_BC_BASE_MAX
{ "POSIX2_BC_BASE_MAX", CONSTANT, _POSIX2_BC_BASE_MAX },
+#else
+ { "POSIX2_BC_BASE_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_BC_DIM_MAX
{ "POSIX2_BC_DIM_MAX", CONSTANT, _POSIX2_BC_DIM_MAX },
+#else
+ { "POSIX2_BC_DIM_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_BC_SCALE_MAX
{ "POSIX2_BC_SCALE_MAX", CONSTANT, _POSIX2_BC_SCALE_MAX },
+#else
+ { "POSIX2_BC_SCALE_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_BC_STRING_MAX
{ "POSIX2_BC_STRING_MAX", CONSTANT, _POSIX2_BC_STRING_MAX },
+#else
+ { "POSIX2_BC_STRING_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_COLL_WEIGHTS_MAX
{ "POSIX2_COLL_WEIGHTS_MAX", CONSTANT, _POSIX2_COLL_WEIGHTS_MAX },
+#else
+ { "POSIX2_COLL_WEIGHTS_MAX", G_UNDEF, -1 },
+#endif
#if defined (_POSIX2_EQUIV_CLASS_MAX)
{ "POSIX2_EQUIV_CLASS_MAX", CONSTANT, _POSIX2_EQUIV_CLASS_MAX },
#endif
+#ifdef _POSIX2_EXPR_NEST_MAX
{ "POSIX2_EXPR_NEST_MAX", CONSTANT, _POSIX2_EXPR_NEST_MAX },
+#else
+ { "POSIX2_EXPR_NEST_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_LINE_MAX
{ "POSIX2_LINE_MAX", CONSTANT, _POSIX2_LINE_MAX },
+#else
+ { "POSIX2_LINE_MAX", G_UNDEF, -1 },
+#endif
+#ifdef _POSIX2_RE_DUP_MAX
{ "POSIX2_RE_DUP_MAX", CONSTANT, _POSIX2_RE_DUP_MAX },
+#else
+ { "POSIX2_RE_DUP_MAX", G_UNDEF, -1 },
+#endif
#if defined (_POSIX2_VERSION)
{ "POSIX2_VERSION", CONSTANT, _POSIX2_VERSION },
+#else
+# if !defined (_SC_2_VERSION)
+ { "POSIX2_VERSION", G_UNDEF, -1 },
+# endif
#endif
/* POSIX.1 Minimum Values */
@@ -146,20 +182,50 @@ static const struct conf_variable conf_table[] =
{ "RE_DUP_MAX", SYSCONF, _SC_RE_DUP_MAX },
/* POSIX.2 Optional Facility Configuration Values */
+#ifdef _SC_2_C_BIND
{ "POSIX2_C_BIND", SYSCONF, _SC_2_C_BIND },
+#else
+ { "POSIX2_C_BIND", G_UNDEF, -1 },
+#endif
+#ifdef _SC_2_C_DEV
{ "POSIX2_C_DEV", SYSCONF, _SC_2_C_DEV },
+#else
+ { "POSIX2_C_DEV", G_UNDEF, -1 },
+#endif
#if defined (_SC_2_C_VERSION)
{ "POSIX2_C_VERSION", SYSCONF, _SC_2_C_VERSION },
+#else
+ { "POSIX2_C_VERSION", G_UNDEF, -1 },
#endif
#if defined (_SC_2_CHAR_TERM)
{ "POSIX2_CHAR_TERM", SYSCONF, _SC_2_CHAR_TERM },
+#else
+ { "POSIX2_CHAR_TERM", G_UNDEF, -1 },
#endif
+#ifdef _SC_2_FORT_DEV
{ "POSIX2_FORT_DEV", SYSCONF, _SC_2_FORT_DEV },
+#else
+ { "POSIX2_FORT_DEV", G_UNDEF, -1 },
+#endif
+#ifdef _SC_2_FORT_RUN
{ "POSIX2_FORT_RUN", SYSCONF, _SC_2_FORT_RUN },
+#else
+ { "POSIX2_FORT_RUN", G_UNDEF, -1 },
+#endif
+#ifdef _SC_2_LOCALEDEF
{ "POSIX2_LOCALEDEF", SYSCONF, _SC_2_LOCALEDEF },
+#else
+ { "POSIX2_LOCALEDEF", G_UNDEF, -1 },
+#endif
+#ifdef _SC_2_SW_DEV
{ "POSIX2_SW_DEV", SYSCONF, _SC_2_SW_DEV },
+#else
+ { "POSIX2_SW_DEV", G_UNDEF, -1 },
+#endif
#if defined (_SC2_UPE)
{ "POSIX2_UPE", SYSCONF, _SC_2_UPE },
+#else
+ { "POSIX2_UPE", G_UNDEF, -1 },
#endif
#if !defined (_POSIX2_VERSION) && defined (_SC_2_VERSION)
{ "POSIX2_VERSION" SYSCONF, _SC_2_VERSION },
@@ -449,6 +515,10 @@ int all;
size_t slen;
switch (cp->type) {
+ case G_UNDEF:
+ printf("undefined\n");
+ break;
+
case CONSTANT:
printf("%ld\n", cp->value);
break;
@@ -508,7 +578,8 @@ int all;
break;
}
- return (ferror(stdout) ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+ return ((ferror(stdout) || cp->type == G_UNDEF) ? EXECUTION_FAILURE
+ : EXECUTION_SUCCESS);
}
static int
diff --git a/examples/loadables/push.c b/examples/loadables/push.c
new file mode 100644
index 00000000..497ecd0e
--- /dev/null
+++ b/examples/loadables/push.c
@@ -0,0 +1,95 @@
+/*
+ * push - anyone remember TOPS-20?
+ *
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "jobs.h"
+#include "bashgetopt.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern int dollar_dollar_pid;
+extern int last_command_exit_value;
+
+int
+push_builtin (list)
+ WORD_LIST *list;
+{
+ pid_t pid;
+ int xstatus, opt;
+
+ xstatus = EXECUTION_SUCCESS;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "")) != -1)
+ {
+ switch (opt)
+ {
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+ list = loptend;
+
+ pid = make_child (savestring ("push"), 0);
+ if (pid == -1)
+ {
+ builtin_error ("cannot fork: %s", strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
+ else if (pid == 0)
+ {
+ /* Shell variable adjustments: $SHLVL, $$, $PPID, $! */
+ adjust_shell_level (1);
+ dollar_dollar_pid = getpid ();
+ set_ppid ();
+
+ /* Clean up job control stuff. */
+ stop_making_children ();
+ cleanup_the_pipeline ();
+ delete_all_jobs (0);
+
+ last_asynchronous_pid = NO_PID;
+
+ /* Make sure the job control code has the right values for
+ the shell's process group and tty process group, and that
+ the signals are set correctly for job control. */
+ initialize_job_control (0);
+ initialize_job_signals ();
+
+ /* And read commands until exit. */
+ reader_loop ();
+ exit_shell (last_command_exit_value);
+ }
+ else
+ {
+ stop_pipeline (0, (COMMAND *)NULL);
+ xstatus = wait_for (pid);
+ return (xstatus);
+ }
+}
+
+char *push_doc[] = {
+ "Create a child that is an exact duplicate of the running shell",
+ "and wait for it to exit. The $SHLVL, $!, $$, and $PPID variables",
+ "are adjusted in the child. The return value is the exit status",
+ "of the child.",
+ (char *)NULL
+};
+
+struct builtin push_struct = {
+ "push",
+ push_builtin,
+ BUILTIN_ENABLED,
+ push_doc,
+ "push",
+ 0
+};
diff --git a/examples/loadables/realpath.c b/examples/loadables/realpath.c
new file mode 100644
index 00000000..16478b79
--- /dev/null
+++ b/examples/loadables/realpath.c
@@ -0,0 +1,119 @@
+/*
+ * realpath -- canonicalize pathnames, resolving symlinks
+ *
+ * usage: realpath [-csv] pathname [pathname...]
+ *
+ * options: -c check whether or not each resolved path exists
+ * -s no output, exit status determines whether path is valid
+ * -v produce verbose output
+ *
+ *
+ * exit status: 0 if all pathnames resolved
+ * 1 if any of the pathname arguments could not be resolved
+ *
+ *
+ * Bash loadable builtin version
+ *
+ * Chet Ramey
+ * chet@po.cwru.edu
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include "bashansi.h"
+#include <maxpath.h>
+#include <errno.h>
+
+#include "builtins.h"
+#include "shell.h"
+#include "bashgetopt.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+extern char *sh_realpath();
+
+realpath_builtin(list)
+WORD_LIST *list;
+{
+ int opt, cflag, vflag, sflag, es;
+ char *r, realbuf[PATH_MAX], *p;
+ struct stat sb;
+
+ if (list == 0) {
+ builtin_usage();
+ return (EX_USAGE);
+ }
+
+ vflag = cflag = sflag = 0;
+ reset_internal_getopt();
+ while ((opt = internal_getopt (list, "csv")) != -1) {
+ switch (opt) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ list = loptend;
+
+ if (list == 0)
+ usage();
+
+ for (es = EXECUTION_SUCCESS; list; list = list->next) {
+ p = list->word->word;
+ r = sh_realpath(p, realbuf);
+ if (r == 0) {
+ es = EXECUTION_FAILURE;
+ if (sflag == 0)
+ builtin_error("%s: cannot resolve: %s", p, strerror(errno));
+ continue;
+ }
+ if (cflag && (stat(realbuf, &sb) < 0)) {
+ es = EXECUTION_FAILURE;
+ if (sflag == 0)
+ builtin_error("%s: %s", p, strerror(errno));
+ continue;
+ }
+ if (sflag == 0) {
+ if (vflag)
+ printf ("%s -> ", p);
+ printf("%s\n", realbuf);
+ }
+ }
+ return es;
+}
+
+char *realpath_doc[] = {
+ "Display the canonicalized version of each PATHNAME argument, resolving",
+ "symbolic links. The -c option checks whether or not each resolved name",
+ "exists. The -s option produces no output; the exit status determines the",
+ "valididty of each PATHNAME. The -v option produces verbose output. The",
+ "exit status is 0 if each PATHNAME was resolved; non-zero otherwise.",
+ (char *)NULL
+};
+
+struct builtin realpath_struct = {
+ "realpath", /* builtin name */
+ realpath_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ realpath_doc, /* array of long documentation strings */
+ "realpath [-csv] pathname [pathname...]", /* usage synopsis */
+ 0 /* reserved for internal use */
+};
diff --git a/examples/scripts/adventure.sh b/examples/scripts/adventure.sh
index 846efc6e..16944501 100755
--- a/examples/scripts/adventure.sh
+++ b/examples/scripts/adventure.sh
@@ -99,7 +99,7 @@ set -o emacs
cd
LIM=.limbo # $HOME/$LIM contains "destroyed" objects
mkdir $LIM || {
- echo "ash: cannot mkdir $LIM: exiting
+ echo "ash: cannot mkdir $LIM: exiting"
exit 1
}
KNAP=.knapsack # $HOME/$KNAP contains objects being "carried"
diff --git a/examples/scripts/cat.sh b/examples/scripts/cat.sh
new file mode 100644
index 00000000..78106b21
--- /dev/null
+++ b/examples/scripts/cat.sh
@@ -0,0 +1,12 @@
+shcat()
+{
+ while read -r ; do
+ echo "$REPLY"
+ done
+}
+
+if [ -n "$1" ]; then
+ shcat < "$1"
+else
+ shcat
+fi
diff --git a/examples/scripts/dd-ex.sh b/examples/scripts/dd-ex.sh
new file mode 100644
index 00000000..fafc83f2
--- /dev/null
+++ b/examples/scripts/dd-ex.sh
@@ -0,0 +1,476 @@
+#!/bin/sh
+
+# this is a line editor using only /bin/sh, /bin/dd and /bin/rm
+
+# /bin/rm is not really required, but it is nice to clean up temporary files
+
+PATH=
+dd=/bin/dd
+rm=/bin/rm
+
+# temporary files we might need
+tmp=/tmp/silly.$$
+ed=/tmp/ed.$$
+trap "$rm -f $tmp $tmp.1 $tmp.2 $tmp.3 $tmp.4 $tmp.5 $tmp.6 $ed.a $ed.b $ed.c; exit" 0 1 2 3
+
+# from now on, no more rm - the above trap is enough
+unset rm
+
+# we do interesting things with IFS, but better save it...
+saveIFS="$IFS"
+
+# in case "echo" is not a shell builtin...
+
+Echo () {
+case "$1" in
+ -n) shift
+ $dd of=$tmp 2>/dev/null <<EOF
+$@
+EOF
+ IFS="+"
+ set `$dd if=$tmp bs=1 of=/dev/null skip=1 2>&1`
+ IFS="$saveIFS"
+ $dd if=$tmp bs=1 count=$1 2>/dev/null
+ ;;
+ *) $dd 2>/dev/null <<EOF
+$@
+EOF
+ ;;
+esac
+}
+
+# this is used to generate garbage files
+
+true () {
+ return 0
+}
+
+false () {
+ return 1
+}
+
+zero () {
+ ( trap 'go=false' 13
+ go=true
+ while $go
+ do
+ $dd "if=$0"
+ case "$?" in
+ 0) ;;
+ *) go=false ;;
+ esac
+ done
+ ) 2>/dev/null
+}
+
+# arithmetic using dd!
+
+# add variable n1 n2 n3...
+# assigns n1+n2+n3+... to variable
+
+add () {
+ result="$1"
+ shift
+ $dd if=/dev/null of=$tmp bs=1 2>/dev/null
+ for n in "$@"
+ do
+ case "$n" in
+ 0) ;;
+ *) zero | $dd of=$tmp.1 bs=1 "count=$n" 2>/dev/null
+ ( $dd if=$tmp; $dd if=$tmp.1 ) 2>/dev/null | $dd of=$tmp.2 2>/dev/null
+ $dd if=$tmp.2 of=$tmp 2>/dev/null
+ ;;
+ esac
+ done
+ IFS="+"
+ set `$dd if=$tmp bs=1 of=/dev/null 2>&1`
+ IFS="$saveIFS"
+ eval $result='$1'
+}
+
+# subtract variable n1 n2
+# subtracts n2 from n1, assigns result to variable
+
+subtract () {
+ result="$1"
+ zero | $dd of=$tmp bs=1 "count=$2" 2>/dev/null
+ IFS="+"
+ set `$dd if=$tmp bs=1 of=/dev/null "skip=$3" 2>&1`
+ IFS="$saveIFS"
+ case "$1" in
+ dd*) set 0 ;;
+ esac
+ eval $result='$1'
+}
+
+# multiply variable n1 n2
+# variable = n1 * n2
+
+multiply () {
+ result="$1"
+ zero | $dd "bs=$2" of=$tmp "count=$3" 2>/dev/null
+ IFS="+"
+ set `$dd if=$tmp bs=1 of=/dev/null 2>&1`
+ IFS="$saveIFS"
+ eval $result='$1'
+}
+
+# divide variable n1 n2
+# variable = int( n1 / n2 )
+
+divide () {
+ result="$1"
+ zero | $dd bs=1 of=$tmp "count=$2" 2>/dev/null
+ IFS="+"
+ set `$dd if=$tmp "bs=$3" of=/dev/null 2>&1`
+ IFS="$saveIFS"
+ eval $result='$1'
+}
+
+# compare variable n1 n2 sets variable to lt if n1<n2, gt if n1>n2, eq if n1==n2
+
+compare () {
+ res="$1"
+ n1="$2"
+ n2="$3"
+ subtract somename "$n1" "$n2"
+ case "$somename" in
+ 0) ;;
+ *) eval $res=gt; return;
+ esac
+ subtract somename "$n2" "$n1"
+ case "$somename" in
+ 0) ;;
+ *) eval $res=lt; return;
+ esac
+ eval $res=eq
+}
+
+# lt n1 n2 returns true if n1 < n2
+
+lt () {
+ n1="$1"
+ n2="$2"
+ subtract somename "$n2" "$n1"
+ case "$somename" in
+ 0) return 1 ;;
+ esac
+ return 0
+}
+
+# le n1 n2 returns true if n1 <= n2
+
+le () {
+ n1="$1"
+ n2="$2"
+ subtract somename "$n1" "$n2"
+ case "$somename" in
+ 0) return 0 ;;
+ esac
+ return 1
+}
+
+# gt n1 n2 returns true if n1 > n2
+
+gt () {
+ n1="$1"
+ n2="$2"
+ subtract somename "$n1" "$n2"
+ case "$somename" in
+ 0) return 1 ;;
+ esac
+ return 0
+}
+
+# ge n1 n2 returns true if n1 >= n2
+
+ge () {
+ n1="$1"
+ n2="$2"
+ subtract somename "$n2" "$n1"
+ case "$somename" in
+ 0) return 0 ;;
+ esac
+ return 1
+}
+
+# useful functions for the line editor
+
+# open a file - copy it to the buffers
+
+open () {
+ file="$1"
+ set `$dd "if=$file" of=/dev/null 2>&1`
+ case "$1" in
+ dd*) return 1
+ esac
+ # copy the first line to $ed.c
+ go=true
+ len=0
+ while $go
+ do
+ case "`$dd "if=$file" bs=1 skip=$len count=1 2>/dev/null`" in
+ ?*) go=true ;;
+ *) go=false ;;
+ esac
+ add len 1 $len
+ done
+ # now $len is the length of the first line (including newline)
+ $dd "if=$file" bs=1 count=$len of=$ed.c 2>/dev/null
+ $dd "if=$file" bs=1 skip=$len of=$ed.b 2>/dev/null
+ $dd if=/dev/null of=$ed.a 2>/dev/null
+ lineno=1
+}
+
+# save a file - copy the buffers to the file
+
+save () {
+ # make a backup copy of the original
+ $dd "if=$1" "of=$1.bak" 2>/dev/null
+ # and save
+ ( $dd if=$ed.a; $dd if=$ed.c; $dd if=$ed.b ) > "$1" 2>/dev/null
+}
+
+# replace n1 n2 bla replaces n2 chars of current line, starting n1-th
+
+replace () {
+ $dd if=$ed.c of=$tmp.1 bs=1 "count=$1" 2>/dev/null
+ ( $dd if=$ed.c "skip=$1" bs=1 | $dd of=$tmp.2 bs=1 "skip=$2" ) 2>/dev/null
+ shift
+ shift
+ ( $dd if=$tmp.1; Echo -n "$@"; $dd if=$tmp.2 ) > $tmp.3 2>/dev/null
+ $dd if=$tmp.3 of=$ed.c 2>/dev/null
+}
+
+# rstring n s bla
+# replace the n-th occurence of s with bla
+
+rstring () {
+ n="$1"
+ shift;
+ # first we have to find it - this is fun!
+ # we have $tmp.4 => text before string, $tmp.5 => text after
+ $dd if=/dev/null of=$tmp.4 2>/dev/null
+ $dd if=$ed.c of=$tmp.5 2>/dev/null
+ string="$1"
+ shift
+ $dd of=$tmp.6 2>/dev/null <<EOF
+$@
+EOF
+ while :
+ do
+ case "`$dd if=$tmp.5 2>/dev/null`" in
+ $string*)
+ if lt $n 2
+ then
+ # now we want to replace the string
+ Echo -n "$@" > $tmp.2
+ Echo -n "$string" > $tmp.1
+ IFS="+"
+ set `$dd bs=1 if=$tmp.1 of=/dev/null 2>&1`
+ IFS="$saveIFS"
+ slen=$1
+ IFS="+"
+ ( $dd if=$tmp.4; $dd if=$tmp.2; $dd if=$tmp.5 bs=1 skip=$slen ) \
+ 2>/dev/null > $tmp
+ $dd if=$tmp of=$ed.c 2>/dev/null
+ return 0
+ else
+ subtract n $n 1
+ ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null
+ $dd if=$tmp of=$tmp.4 2>/dev/null
+ # and remove it from $tmp.5
+ $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null
+ $dd if=$tmp of=$tmp.5 2>/dev/null
+ fi
+ ;;
+ ?*) # add one more byte...
+ ( $dd if=$tmp.4; $dd if=$tmp.5 bs=1 count=1 ) > $tmp 2>/dev/null
+ $dd if=$tmp of=$tmp.4 2>/dev/null
+ # and remove it from $tmp.5
+ $dd if=$tmp.5 of=$tmp bs=1 skip=1 2>/dev/null
+ $dd if=$tmp of=$tmp.5 2>/dev/null
+ ;;
+ *) # not found
+ return 1
+ ;;
+ esac
+ done
+}
+
+# skip to next line
+next () {
+ add l $lineno 1
+ ( $dd if=$ed.a; $dd if=$ed.c ) 2>/dev/null > $tmp.3
+ $dd if=$ed.b of=$tmp.4 2>/dev/null
+ open $tmp.4
+ $dd if=$tmp.3 of=$ed.a 2>/dev/null
+ lineno=$l
+}
+
+# delete current line
+delete () {
+ l=$lineno
+ $dd if=$ed.a 2>/dev/null > $tmp.1
+ $dd if=$ed.b of=$tmp.2 2>/dev/null
+ open $tmp.2
+ $dd if=$tmp.1 of=$ed.a 2>/dev/null
+ lineno=$l
+}
+
+# insert before current line (without changing current)
+insert () {
+ ( $dd if=$ed.a; Echo "$@" ) 2>/dev/null > $tmp.1
+ $dd if=$tmp.1 of=$ed.a 2>/dev/null
+ add lineno $lineno 1
+}
+
+# previous line
+prev () {
+ case "$lineno" in
+ 1) ;;
+ *) subtract lineno $lineno 1
+ # read last line of $ed.a
+ IFS='+'
+ set `$dd if=$ed.a of=/dev/null bs=1 2>&1`
+ IFS="$saveIFS"
+ size=$1
+ # empty?
+ case "$size" in
+ 0) return ;;
+ esac
+ subtract size $size 1
+ # skip final newline
+ case "$size" in
+ 0) ;;
+ *) subtract size1 $size 1
+ case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in
+ ?*) ;;
+ *) size=$size1 ;;
+ esac
+ ;;
+ esac
+ go=true
+ while $go
+ do
+ case "$size" in
+ 0) go=false ;;
+ *) case "`$dd if=$ed.a bs=1 skip=$size count=1 2>/dev/null`" in
+ ?*) go=true; subtract size $size 1 ;;
+ *) go=false; add size $size 1 ;;
+ esac
+ ;;
+ esac
+ done
+ # now $size is the size of the first n-1 lines
+ # add $ed.c to $ed.b
+ ( $dd if=$ed.c; $dd if=$ed.b ) 2>/dev/null > $tmp.5
+ $dd if=$tmp.5 of=$ed.b 2>/dev/null
+ # move line to ed.c
+ case "$size" in
+ 0) $dd if=$ed.a of=$ed.c 2>/dev/null
+ $dd if=/dev/null of=$tmp.5 2>/dev/null
+ ;;
+ *) $dd if=$ed.a of=$ed.c bs=1 skip=$size 2>/dev/null
+ $dd if=$ed.a of=$tmp.5 bs=1 count=$size 2>/dev/null
+ ;;
+ esac
+ # move rest to ed.a
+ $dd if=$tmp.5 of=$ed.a 2>/dev/null
+ ;;
+ esac
+}
+
+# goes to a given line
+goto () {
+ rl="$1"
+ compare bla "$rl" $lineno
+ case "$bla" in
+ eq) return
+ ;;
+ gt) while gt "$rl" $lineno
+ do
+ next
+ done
+ ;;
+ lt) while lt "$rl" $lineno
+ do
+ prev
+ done
+ ;;
+ esac
+}
+
+lineout () {
+ Echo -n "$lineno: "
+ $dd if=$ed.c 2>/dev/null
+}
+
+state=closed
+name=
+autoprint=true
+
+while true
+do
+ Echo -n '> '
+ read cmd arg
+ case "$cmd:$state" in
+ open:open) Echo "There is a file open already" ;;
+ open:*) if open "$arg"
+ then state=open; name="$arg"; $autoprint
+ else Echo "Cannot open $arg"
+ fi
+ ;;
+ new:open) Echo "There is a file open already" ;;
+ new:*) open "$arg"
+ state=open
+ name="$arg"
+ $autoprint
+ ;;
+ close:changed) Echo "Use 'discard' or 'save'" ;;
+ close:closed) Echo "Closed already" ;;
+ close:*) state=closed ;;
+ save:closed) Echo "There isn't a file to save" ;;
+ save:*) case "$arg" in
+ ?*) save "$arg" ;;
+ *) save "$name" ;;
+ esac
+ state=open
+ ;;
+ discard:changed) Echo "Your problem!"; state=closed ;;
+ discard:*) state=closed ;;
+ print:closed) Echo "No current file" ;;
+ print:*) lineout ;;
+ goto:closed) Echo "No current file" ;;
+ goto:*) goto "$arg"; $autoprint ;;
+ next:closed) Echo "No current file" ;;
+ next:*) next; $autoprint ;;
+ prev:closed) Echo "No current file" ;;
+ prev:*) prev; $autoprint ;;
+ name:closed) Echo "No current file" ;;
+ name:*) name="$arg" ;;
+ replace:closed) Echo "No current file" ;;
+ replace:*) if rstring 1 $arg
+ then state=changed; $autoprint
+ else Echo "Not found"
+ fi
+ ;;
+ nreplace:closed) Echo "No current file" ;;
+ nreplace:*) if rstring $arg
+ then state=changed; $autoprint
+ else Echo "Not found"
+ fi
+ ;;
+ delete:closed) Echo "No current file" ;;
+ delete:*) delete; state=changed; $autoprint ;;
+ insert:closed) Echo "No current file" ;;
+ insert:*) insert "$arg"; prev; state=changed; $autoprint ;;
+ quit:changed) Echo "Use 'save' or 'discard'" ;;
+ quit:*) Echo "bye"; exit;;
+ autoprint:*) autoprint="lineout" ;;
+ noprint:*) autoprint="" ;;
+ :*) ;;
+ *) Echo "Command not understood" ;;
+ esac
+done
+