diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/complete/complete-examples | 21 | ||||
-rw-r--r-- | examples/complete/complete.freebsd | 31 | ||||
-rw-r--r-- | examples/complete/complete.ianmac | 433 | ||||
-rw-r--r-- | examples/complete/complete2.ianmac | 271 | ||||
-rw-r--r-- | examples/functions/array-to-string | 15 | ||||
-rw-r--r-- | examples/functions/emptydir | 28 | ||||
-rw-r--r-- | examples/functions/fact | 2 | ||||
-rw-r--r-- | examples/functions/gethtml | 35 | ||||
-rw-r--r-- | examples/functions/ksh-cd | 35 | ||||
-rw-r--r-- | examples/functions/kshenv | 44 | ||||
-rw-r--r-- | examples/functions/lowercase | 2 | ||||
-rw-r--r-- | examples/functions/recurse | 64 | ||||
-rw-r--r-- | examples/functions/sort-pos-params | 50 | ||||
-rw-r--r-- | examples/loadables/Makefile.in | 8 | ||||
-rw-r--r-- | examples/loadables/cut.c | 376 | ||||
-rw-r--r-- | examples/loadables/finfo.c | 6 | ||||
-rw-r--r-- | examples/loadables/getconf.c | 75 | ||||
-rw-r--r-- | examples/loadables/push.c | 95 | ||||
-rw-r--r-- | examples/loadables/realpath.c | 119 | ||||
-rwxr-xr-x | examples/scripts/adventure.sh | 2 | ||||
-rw-r--r-- | examples/scripts/cat.sh | 12 | ||||
-rw-r--r-- | examples/scripts/dd-ex.sh | 476 |
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 + |