1
0
Fork 0

Adding upstream version 5.2.37.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
This commit is contained in:
Daniel Baumann 2025-06-21 06:49:21 +02:00
parent cf91100bce
commit fa1b3d3922
Signed by: daniel.baumann
GPG key ID: BCC918A2ABD66424
1435 changed files with 757174 additions and 0 deletions

426
examples/INDEX.html Normal file
View file

@ -0,0 +1,426 @@
<table border=1>
<tr>
<th>Path</th>
<th>Description</th>
<th>X-Ref</th>
</tr>
<tr>
<td>./obashdb</td>
<td>Deprecated sample implementation of a bash debugger</td>
</tr>
<tr>
</tr>
<tr>
<td>./complete</td>
<td>Shell completion code</td>
</tr>
<tr>
</tr>
<tr>
<td>./functions</td>
<td>Example functions</td>
</tr>
<tr>
<td>./functions/array-stuff</td>
<td>Various array functions (ashift, array_sort, reverse).</td>
</tr>
<tr>
<td>./functions/array-to-string</td>
<td>Convert an array to a string.</td>
</tr>
<tr>
<td>./functions/autoload</td>
<td>An almost ksh-compatible 'autoload' (no lazy load).</td>
<td>ksh</td>
</tr>
<tr>
<td>./functions/autoload.v2</td>
<td>An almost ksh-compatible 'autoload' (no lazy load).</td>
<td>ksh</td>
</tr>
<tr>
<td>./functions/autoload.v3</td>
<td>A more ksh-compatible 'autoload' (with lazy load).</td>
<td>ksh</td>
</tr>
<tr>
<td>./functions/autoload.v3</td>
<td>An updated ksh-compatible 'autoload'.</td>
<td>ksh</td>
</tr>
<tr>
<td>./functions/basename</td>
<td>A replacement for basename(1).</td>
<td>basename</td>
</tr>
<tr>
<td>./functions/csh-compat</td>
<td>A C-shell compatibility package.</td>
<td>csh</td>
</tr>
<tr>
<td>./functions/dirname</td>
<td>A replacement for dirname(1).</td>
<td>dirname</td>
</tr>
<tr>
<td>./functions/dirstack</td>
<td>Directory stack functions.</td>
</tr>
<tr>
<td>./functions/exitstat</td>
<td>Display the exit status of processes.</td>
</tr>
<tr>
<td>./functions/external</td>
<td>Like 'command' but FORCES use of external command.</td>
</tr>
<tr>
<td>./functions/fact</td>
<td>Recursive factorial function.</td>
</tr>
<tr>
<td>./functions/fstty</td>
<td>Front end to sync TERM changes to both stty(1) and readline 'bind'.</td>
<td>stty.bash</td>
</tr>
<tr>
<td>./functions/inetaddr</td>
<td>Internet address conversion (inet2hex & hex2inet).</td>
</tr>
<tr>
<td>./functions/inpath</td>
<td>Return zero if the argument is in the path and executable.</td>
<td>inpath</td>
</tr>
<tr>
<td>./functions/isnum2</td>
<td>Test user input on numeric values, with floating point.</td>
</tr>
<tr>
<td>./functions/isvalidip</td>
<td>Test user input for valid IP Addresses.</td>
</tr>
<tr>
<td>./functions/ksh-cd</td>
<td>ksh-like 'cd': cd [-LP] [dir [change]].</td>
<td>ksh</td>
</tr>
<tr>
<td>./functions/ksh-compat-test</td>
<td>ksh-like arithmetic test replacements.</td>
<td>ksh</td>
</tr>
<tr>
<td>./functions/kshenv</td>
<td>Functions and aliases to provide the beginnings of a ksh environment for bash.</td>
<td>ksh</td>
</tr>
<tr>
<td>./functions/login</td>
<td>Replace the 'login' and 'newgrp' builtins in old Bourne shells.</td>
</tr>
<tr>
<td>./functions/notify.bash</td>
<td>Notify when jobs change status.</td>
</tr>
<tr>
<td>./functions/README</td>
<td>README</td>
</tr>
<tr>
<td>./functions/seq</td>
<td>Generate a sequence from m to n, m defaults to 1.</td>
</tr>
<tr>
<td>./functions/seq2</td>
<td>Generate a sequence from m to n, m defaults to 1.</td>
</tr>
<tr>
<td>./functions/shcat</td>
<td>Readline-based pager.</td>
<td>cat, readline pager</td>
</tr>
<tr>
<td>./functions/shcat2</td>
<td>Readline-based pagers.</td>
<td>cat, readline pager</td>
</tr>
<tr>
<td>./functions/sort-pos-params</td>
<td>Sort the positional parameters.</td>
</tr>
<tr>
<td>./functions/substr</td>
<td>A function to emulate the ancient ksh builtin.</td>
<td>ksh</td>
</tr>
<tr>
<td>./functions/substr2</td>
<td>A function to emulate the ancient ksh builtin.</td>
<td>ksh</td>
</tr>
<tr>
<td>./functions/whatis</td>
<td>An implementation of the 10th Edition Unix sh builtin 'whatis(1)' command.</td>
</tr>
<tr>
<td>./functions/whence</td>
<td>An almost-ksh compatible 'whence(1)' command.</td>
</tr>
<tr>
<td>./functions/which</td>
<td>An emulation of 'which(1)' as it appears in FreeBSD.</td>
</tr>
<tr>
<td>./loadables/</td>
<td>Example loadable replacements</td>
</tr>
<tr>
<td>./loadables/basename.c</td>
<td>Return non-directory portion of pathname.</td>
<td>basename</td>
</tr>
<tr>
<td>./loadables/cat.c</td>
<td>cat(1) replacement with no options - the way cat was intended.</td>
<td>cat, readline pager</td>
</tr>
<tr>
<td>./loadables/dirname.c</td>
<td>Return directory portion of pathname.</td>
<td>dirname</td>
</tr>
<tr>
<td>./loadables/fdflags.c</td>
<td>Display or modify file descriptor flags</td>
</tr>
<tr>
<td>./loadables/finfo.c</td>
<td>Print file info.</td>
</tr>
<tr>
<td>./loadables/head.c</td>
<td>Copy first part of files.</td>
</tr>
<tr>
<td>./loadables/hello.c</td>
<td>Obligatory "Hello World" / sample loadable.</td>
</tr>
<tr>
<td>./loadables/id.c</td>
<td>POSIX.2 user identity.</td>
</tr>
<tr>
<td>./loadables/ln.c</td>
<td>Make links.</td>
</tr>
<tr>
<td>./loadables/logname.c</td>
<td>Print login name of current user.</td>
</tr>
<tr>
<td>./loadables/Makefile.in</td>
<td>Simple makefile for the sample loadable builtins.</td>
</tr>
<tr>
<td>./loadables/Makefile.inc.in</td>
<td>Sample makefile to use for loadable builtin development.</td>
</tr>
<tr>
<td>./loadables/mkdir.c</td>
<td>Make directories.</td>
</tr>
<tr>
<td>./loadables/mypid.c</td>
<td>Demonstrate how a loadable builtin can create and delete shell variables.</td>
</tr>
<tr>
<td>./loadables/necho.c</td>
<td>echo without options or argument interpretation.</td>
</tr>
<tr>
<td>./loadables/pathchk.c</td>
<td>Check pathnames for validity and portability.</td>
</tr>
<tr>
<td>./loadables/print.c</td>
<td>Loadable ksh-93 style print builtin.</td>
</tr>
<tr>
<td>./loadables/printenv.c</td>
<td>Minimal builtin clone of BSD printenv(1).</td>
</tr>
<tr>
<td>./loadables/push.c</td>
<td>Anyone remember TOPS-20?</td>
</tr>
<tr>
<td>./loadables/README</td>
<td>README</td>
</tr>
<tr>
<td>./loadables/realpath.c</td>
<td>Canonicalize pathnames, resolving symlinks.</td>
</tr>
<tr>
<td>./loadables/rm.c</td>
<td>Remove file.</td>
</tr>
<tr>
<td>./loadables/rmdir.c</td>
<td>Remove directory.</td>
</tr>
<tr>
<td>./loadables/setpgid.c</td>
<td>Set a child process's process group.
</tr>
<tr>
<td>./loadables/sleep.c</td>
<td>sleep for fractions of a second.</td>
</tr>
<tr>
<td>./loadables/stat.c</td>
<td>Load an associative array with stat information about a file.</td>
</tr>
<tr>
<td>./loadables/strftime.c</td>
<td>Loadable builtin interface to strftime(3).</td>
</tr>
<tr>
<td>./loadables/sync.c</td>
<td>Sync the disks by forcing pending filesystem writes to complete.</td>
</tr>
<tr>
<td>./loadables/tee.c</td>
<td>Duplicate standard input.</td>
</tr>
<tr>
<td>./loadables/template.c</td>
<td>Example template for loadable builtin.</td>
</tr>
<tr>
<td>./loadables/truefalse.c</td>
<td>True and false builtins.</td>
</tr>
<tr>
<td>./loadables/tty.c</td>
<td>Return terminal name.</td>
</tr>
<tr>
<td>./loadables/uname.c</td>
<td>Print system information.</td>
</tr>
<tr>
<td>./loadables/unlink.c</td>
<td>Remove a directory entry.</td>
</tr>
<tr>
<td>./loadables/whoami.c</td>
<td>Print out username of current user.</td>
</tr>
<tr>
</tr>
<tr>
<td>./loadables/perl/</td>
<td>Illustrate how to build a Perl interpreter into bash.</td>
</tr>
<tr>
</tr>
<tr>
<td>./misc</td>
<td>Miscellaneous</td>
</tr>
<tr>
<td>./misc/aliasconv.bash</td>
<td>Convert csh aliases to bash aliases and functions.</td>
<td>csh, xalias</td>
</tr>
<tr>
<td>./misc/aliasconv.sh</td>
<td>Convert csh aliases to bash aliases and functions.</td>
<td>csh, xalias</td>
</tr>
<tr>
<td>./misc/cshtobash</td>
<td>Convert csh aliases, environment variables, and variables to bash equivalents.</td>
<td>csh, xalias</td>
</tr>
<tr>
<td>./misc/README</td>
<td>README</td>
</tr>
<tr>
</tr>
<tr>
<td>./scripts</td>
<td>Example scripts</td>
</tr>
<tr>
<td>./scripts/cat.sh</td>
<td>Readline-based pager.</td>
<td>cat, readline pager</td>
</tr>
<tr>
<td>./scripts/center</td>
<td>Center - center a group of lines.</td>
</tr>
<tr>
<td>./scripts/inpath</td>
<td>Search $PATH for a file the same name as $1; return TRUE if found.</td>
<td>inpath</td>
</tr>
<tr>
<td>./scripts/README</td>
<td>README</td>
</tr>
<tr>
<td>./scripts/shprompt</td>
<td>Display a prompt and get an answer satisfying certain criteria.</td>
<td>ask</td>
</tr>
<tr>
<td>./scripts/spin.bash</td>
<td>Display a 'spinning wheel' to show progress.</td>
</tr>
<tr>
<td>./scripts/xterm_title</td>
<td>Print the contents of the xterm title bar.</td>
</tr>
<tr>
<td>./scripts/zprintf</td>
<td>Emulate printf (obsolete since it's now a bash builtin).</td>
</tr>
<tr>
</tr>
<tr>
<td>./startup-files</td>
<td>Example Start-up files.</td>
</tr>
<tr>
<td>./startup-files/Bash_aliases</td>
<td>Some useful aliases (Fox).</td>
</tr>
<tr>
<td>./startup-files/Bash_profile</td>
<td>Sample startup file for bash login shells (Fox).</td>
</tr>
<tr>
<td>./startup-files/bash-profile</td>
<td>Sample startup file for bash login shells (Ramey).</td>
</tr>
<tr>
<td>./startup-files/bashrc</td>
<td>Sample Bourne Again SHell init file (Ramey).</td>
</tr>
<tr>
<td>./startup-files/Bashrc.bfox</td>
<td>Sample Bourne Again SHell init file (Fox).</td>
</tr>
<tr>
<td>./startup-files/README</td>
<td>README</td>
</tr>
<tr>
</tr>
</table>

103
examples/INDEX.txt Normal file
View file

@ -0,0 +1,103 @@
Path Description X-Ref
./obashdb Deprecated sample implementation of a bash debugger
./complete Shell completion code
./functions Example functions
./functions/array-stuff Various array functions (ashift, array_sort, reverse).
./functions/array-to-string Convert an array to a string.
./functions/autoload An almost ksh-compatible 'autoload' (no lazy load). ksh
./functions/autoload.v2 An almost ksh-compatible 'autoload' (no lazy load). ksh
./functions/autoload.v3 A more ksh-compatible 'autoload' (with lazy load). ksh
./functions/autoload.v4 An updated ksh-compatible 'autoload'. ksh
./functions/basename A replacement for basename(1). basename
./functions/csh-compat A C-shell compatibility package. csh
./functions/dirname A replacement for dirname(1). dirname
./functions/dirstack Directory stack functions.
./functions/exitstat Display the exit status of processes.
./functions/external Like 'command' but FORCES use of external command.
./functions/fact Recursive factorial function.
./functions/fstty Front end to sync TERM changes to both stty(1) and readline 'bind'. stty.bash
./functions/inetaddr Internet address conversion (inet2hex & hex2inet).
./functions/inpath Return zero if the argument is in the path and executable. inpath
./functions/isnum2 Test user input on numeric values, with floating point.
./functions/isvalidip Test user input for valid IP Addresses.
./functions/ksh-cd ksh-like 'cd': cd [-LP] [dir [change]]. ksh
./functions/ksh-compat-test ksh-like arithmetic test replacements. ksh
./functions/kshenv Functions and aliases to provide the beginnings of a ksh environment for bash. ksh
./functions/login Replace the 'login' and 'newgrp' builtins in old Bourne shells.
./functions/notify.bash Notify when jobs change status.
./functions/README README
./functions/seq Generate a sequence from m to n, m defaults to 1.
./functions/seq2 Generate a sequence from m to n, m defaults to 1.
./functions/shcat Readline-based pager. cat, readline pager
./functions/shcat2 Readline-based pagers. cat, readline pager
./functions/sort-pos-params Sort the positional parameters.
./functions/substr A function to emulate the ancient ksh builtin. ksh
./functions/substr2 A function to emulate the ancient ksh builtin. ksh
./functions/whatis An implementation of the 10th Edition Unix sh builtin 'whatis(1)' command.
./functions/whence An almost-ksh compatible 'whence(1)' command.
./functions/which An emulation of 'which(1)' as it appears in FreeBSD.
./loadables/ Example loadable replacements
./loadables/basename.c Return non-directory portion of pathname. basename
./loadables/cat.c cat(1) replacement with no options - the way cat was intended. cat, readline pager
./loadables/dirname.c Return directory portion of pathname. dirname
./loadables/fdflags.c Display or modify file descriptor flags
./loadables/finfo.c Print file info.
./loadables/head.c Copy first part of files.
./loadables/hello.c Obligatory "Hello World" / sample loadable.
./loadables/id.c POSIX.2 user identity.
./loadables/ln.c Make links.
./loadables/logname.c Print login name of current user.
./loadables/Makefile.in Simple makefile for the sample loadable builtins.
./loadables/Makefile.inc.in Sample makefile to use for loadable builtin development.
./loadables/mkdir.c Make directories.
./loadables/mypid.c Demonstrate how a loadable builtin can create and delete shell variables.
./loadables/necho.c echo without options or argument interpretation.
./loadables/pathchk.c Check pathnames for validity and portability.
./loadables/print.c Loadable ksh-93 style print builtin.
./loadables/printenv.c Minimal builtin clone of BSD printenv(1).
./loadables/push.c Anyone remember TOPS-20?
./loadables/README README
./loadables/realpath.c Canonicalize pathnames, resolving symlinks.
./loadables/rm.c Remove file.
./loadables/rmdir.c Remove directory.
./loadables/setpgid.c Set a child process's process group.
./loadables/sleep.c sleep for fractions of a second.
./loadables/stat.c Load an associative array with stat information about a file.
./loadables/strftime.c Loadable builtin interface to strftime(3).
./loadables/sync.c Sync the disks by forcing pending filesystem writes to complete.
./loadables/tee.c Duplicate standard input.
./loadables/template.c Example template for loadable builtin.
./loadables/truefalse.c True and false builtins.
./loadables/tty.c Return terminal name.
./loadables/uname.c Print system information.
./loadables/unlink.c Remove a directory entry.
./loadables/whoami.c Print out username of current user.
./loadables/perl/ Illustrate how to build a Perl interpreter into bash.
./misc Miscellaneous
./misc/aliasconv.bash Convert csh aliases to bash aliases and functions. csh, xalias
./misc/aliasconv.sh Convert csh aliases to bash aliases and functions. csh, xalias
./misc/cshtobash Convert csh aliases, environment variables, and variables to bash equivalents. csh, xalias
./misc/README README
./scripts Example scripts
./scripts/cat.sh Readline-based pager. cat, readline pager
./scripts/center Center - center a group of lines.
./scripts/inpath Search $PATH for a file the same name as $1; return TRUE if found. inpath
./scripts/shprompt Display a prompt and get an answer satisfying certain criteria. ask
./scripts/spin.bash Display a 'spinning wheel' to show progress.
./scripts/xterm_title Print the contents of the xterm title bar.
./scripts/zprintf Emulate printf (obsolete since it's now a bash builtin).
./startup-files Example Start-up files.
./startup-files/Bash_aliases Some useful aliases (Fox).
./startup-files/Bash_profile Sample startup file for bash login shells (Fox).
./startup-files/bash-profile Sample startup file for bash login shells (Ramey).
./startup-files/bashrc Sample Bourne Again SHell init file (Ramey).
./startup-files/Bashrc.bfox Sample Bourne Again SHell init file (Fox).
./startup-files/README README

View file

@ -0,0 +1,7 @@
Master source: https://github.com/scop/bash-completion
This is the latest version of the bash-completion package, which provides
programmable completion specifications for a large number of commands.
If you are a vendor installing bash or preparing a package containing bash,
please install the latest version of bash-completion when installing bash.

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

76
examples/complete/cdfunc Normal file
View file

@ -0,0 +1,76 @@
# cdfunc - example completion function for cd
#
# based on the cd completion function from the bash_completion package
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 2011 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
_comp_cd()
{
local IFS=$' \t\n' # normalize IFS
local cur _skipdot _cdpath
local i j k
# Tilde expansion, with side effect of expanding tilde to full pathname
case "$2" in
\~*) eval cur="$2" ;;
*) cur=$2 ;;
esac
# no cdpath or absolute pathname -- straight directory completion
if [[ -z "${CDPATH:-}" ]] || [[ "$cur" == @(./*|../*|/*) ]]; then
# compgen prints paths one per line; could also use while loop
IFS=$'\n'
COMPREPLY=( $(compgen -d -- "$cur") )
IFS=$' \t\n'
# CDPATH+directories in the current directory if not in CDPATH
else
IFS=$'\n'
_skipdot=false
# preprocess CDPATH to convert null directory names to .
_cdpath=${CDPATH/#:/.:}
_cdpath=${_cdpath//::/:.:}
_cdpath=${_cdpath/%:/:.}
for i in ${_cdpath//:/$'\n'}; do
if [[ $i -ef . ]]; then _skipdot=true; fi
k="${#COMPREPLY[@]}"
for j in $( compgen -d -- "$i/$cur" ); do
COMPREPLY[k++]=${j#$i/} # cut off directory
done
done
$_skipdot || COMPREPLY+=( $(compgen -d -- "$cur") )
IFS=$' \t\n'
fi
# variable names if appropriate shell option set and no completions
if shopt -q cdable_vars && [[ ${#COMPREPLY[@]} -eq 0 ]]; then
COMPREPLY=( $(compgen -v -- "$cur") )
fi
# append slash to passed directory name that is the only completion.
# readline will not do this if we complete from CDPATH
if [[ ${#COMPREPLY[@]} -eq 1 ]]; then
i=${COMPREPLY[0]} # shorthand
if [[ "$cur" == "$i" ]] && [[ "$i" != "*/" ]]; then
COMPREPLY[0]+=/
fi
fi
return 0
}
complete -o filenames -o nospace -o bashdefault -F _comp_cd cd

View file

@ -0,0 +1,512 @@
#
# Completion examples
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 2002 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# This encapsulates the default bash completion code
# call with the word to be completed as $1
#
# Since programmable completion does not use the bash default completions
# or the readline default of filename completion when the compspec does
# not generate any matches, this may be used as a `last resort' in a
# completion function to mimic the default bash completion behavior.
#
_bash_def_completion ()
{
local h t
COMPREPLY=()
# command substitution
if [[ "$1" == \$\(* ]]; then
t=${1#??}
COMPREPLY=( $(compgen -c -P '$(' $t) )
fi
# variables with a leading `${'
if [ ${#COMPREPLY[@]} -eq 0 ] && [[ "$1" == \$\{* ]]; then
t=${1#??}
COMPREPLY=( $(compgen -v -P '${' -S '}' $t) )
fi
# variables with a leading `$'
if [ ${#COMPREPLY[@]} -eq 0 ] && [[ "$1" == \$* ]]; then
t=${1#?}
COMPREPLY=( $(compgen -v -P '$' $t ) )
fi
# username expansion
if [ ${#COMPREPLY[@]} -eq 0 ] && [[ "$1" == ~* ]] && [[ "$1" != */* ]]; then
t=${1#?}
COMPREPLY=( $( compgen -u -P '~' $t ) )
fi
# hostname
if [ ${#COMPREPLY[@]} -eq 0 ] && [[ "$1" == *@* ]]; then
h=${1%%@*}
t=${1#*@}
COMPREPLY=( $( compgen -A hostname -P "${h}@" $t ) )
fi
# glob pattern
if [ ${#COMPREPLY[@]} -eq 0 ]; then
# sh-style glob pattern
if [[ $1 == *[*?[]* ]]; then
COMPREPLY=( $( compgen -G "$1" ) )
# ksh-style extended glob pattern - must be complete
elif shopt -q extglob && [[ $1 == *[?*+\!@]\(*\)* ]]; then
COMPREPLY=( $( compgen -G "$1" ) )
fi
fi
# final default is filename completion
if [ ${#COMPREPLY[@]} -eq 0 ]; then
COMPREPLY=( $(compgen -f "$1" ) )
fi
}
#
# Return 1 if $1 appears to contain a redirection operator. Handles backslash
# quoting (barely).
#
_redir_op()
{
case "$1" in
*\\'[\<\>]'*) return 1;;
*[\<\>]*) return 0;;
*) return 1;;
esac
}
# _redir_test tests the current word ($1) and the previous word ($2) for
# redirection operators and does filename completion on the current word
# if either one contains a redirection operator
_redir_test()
{
if _redir_op "$1" ; then
COMPREPLY=( $( compgen -f "$1" ) )
return 0
elif _redir_op "$2" ; then
COMPREPLY=( $( compgen -f "$1" ) )
return 0
fi
return 1
}
# optional, but without this you can't use extended glob patterns
shopt -s extglob
#
# Easy ones for the shell builtins
#
# nothing for: alias, break, continue, dirs, echo, eval, exit, getopts,
# let, logout, popd, printf, pwd, return, shift, suspend, test, times,
# umask
#
complete -f -- . source
complete -A enabled builtin
complete -d cd
# this isn't exactly right yet -- needs to skip shell functions and
# do $PATH lookup (or do compgen -c and filter out matches that also
# appear in compgen -A function)
complete -c command
# could add -S '=', but that currently screws up because readline appends
# a space unconditionally
complete -v export local readonly
complete -A helptopic help # currently same as builtins
complete -d pushd
complete -A shopt shopt
complete -c type
complete -a unalias
complete -v unset
#
# Job control builtins: fg, bg, disown, kill, wait
# kill not done yet
#
complete -A stopped -P '%' bg
complete -j -P '%' fg jobs disown
# this is not quite right at this point
_wait_func ()
{
local cur
cur=${COMP_WORDS[COMP_CWORD]}
case "$cur" in
%*) COMPREPLY=( $(compgen -A running -P '%' ${cur#?} ) ) ;;
[0-9]*) COMPREPLY=( $(jobs -p | grep ^${cur}) ) ;;
*) COMPREPLY=( $(compgen -A running -P '%') $(jobs -p) )
;;
esac
}
complete -F _wait_func wait
#
# more complicated things, several as yet unimplemented
#
#complete -F _bind_func bind
_declare_func()
{
local cur prev nflag opts
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
COMPREPLY=()
if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then
COMPREPLY=(-a -f -F -i -p -r -t -x)
return 0;
fi
if [[ $cur == '+' ]]; then
COMPREPLY=(+i +t +x)
return 0;
fi
if [[ $prev == '-p' ]]; then
COMPREPLY=( $(compgen -v $cur) )
return 0;
fi
return 1
}
complete -F _declare_func declare typeset
_enable_func()
{
local cur prev nflag opts
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
COMPREPLY=()
if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then
COMPREPLY=(-a -d -f -n -p -s)
return 0;
fi
if [[ $prev == '-f' ]]; then
COMPREPLY=( $( compgen -f $cur ) )
return 0;
fi
for opts in "${COMP_WORDS[@]}" ; do
if [[ $opts == -*n* ]]; then nflag=1; fi
done
if [ -z "$nflag" ] ; then
COMPREPLY=( $( compgen -A enabled $cur ) )
else
COMPREPLY=( $( compgen -A disabled $cur ) )
fi
return 0;
}
complete -F _enable_func enable
_exec_func()
{
local cur prev
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then
COMPREPLY=(-a -c -l)
return 0;
fi
if [[ $prev != -*a* ]]; then
COMPREPLY=( $( compgen -c $cur ) )
return 0
fi
return 1;
}
complete -F _exec_func exec
_fc_func()
{
local cur prev
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then
COMPREPLY=(-e -n -l -r -s)
return 0;
fi
if [[ $prev == -*e ]]; then
COMPREPLY=( $(compgen -c $cur) )
return 0
fi
return 1
}
complete -F _fc_func fc
_hash_func()
{
local cur prev
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then
COMPREPLY=(-p -r -t)
return 0;
fi
if [[ $prev == '-p' ]]; then
COMPREPLY=( $( compgen -f $cur ) )
return 0;
fi
COMPREPLY=( $( compgen -c $cur ) )
return 0
}
complete -F _hash_func hash
_history_func()
{
local cur prev
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
COMPREPLY=()
if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then
COMPREPLY=(-a -c -d -n -r -w -p -s)
return 0;
fi
if [[ $prev == -[anrw] ]]; then
COMPREPLY=( $( compgen -f $cur ) )
fi
return 0
}
complete -F _history_func history
#complete -F _read_func read
_set_func ()
{
local cur prev
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
COMPREPLY=()
_redir_test "$cur" "$prev" && return 0;
if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then
COMPREPLY=(-a -b -e -f -k -m -n -o -p -t -u -v -x -B -C -H -P --)
return 0;
fi
if [[ $cur == '+' ]]; then
COMPREPLY=(+a +b +e +f +k +m +n +o +p +t +u +v +x +B +C +H +P)
return 0;
fi
if [[ $prev == [+-]o ]]; then
COMPREPLY=( $(compgen -A setopt $cur) )
return 0;
fi
return 1;
}
complete -F _set_func set
_trap_func ()
{
local cur
cur=${COMP_WORDS[COMP_CWORD]}
if (( $COMP_CWORD <= 1 )) || [[ $cur == '-' ]]; then
COMPREPLY=(-l -p)
return 0;
fi
COMPREPLY=( $( compgen -A signal ${cur}) )
return 0
}
complete -F _trap_func trap
#
# meta-completion (completion for complete/compgen)
#
_complete_meta_func()
{
local cur prev cmd
COMPREPLY=()
cmd=$1
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
_redir_test "$cur" "$prev" && return 0;
if (( $COMP_CWORD <= 1 )) || [[ "$cur" == '-' ]]; then
case "$cmd" in
complete) COMPREPLY=(-a -b -c -d -e -f -j -k -s -v -u -r -p -A -G -W -P -S -X -F -C);;
compgen) COMPREPLY=(-a -b -c -d -e -f -j -k -s -v -u -A -G -W -P -S -X -F -C);;
esac
return 0
fi
if [[ $prev == -A ]]; then
COMPREPLY=(alias arrayvar binding builtin command directory \
disabled enabled export file 'function' helptopic hostname job keyword \
running service setopt shopt signal stopped variable)
return 0
elif [[ $prev == -F ]]; then
COMPREPLY=( $( compgen -A function $cur ) )
elif [[ $prev == -C ]]; then
COMPREPLY=( $( compgen -c $cur ) )
else
COMPREPLY=( $( compgen -c $cur ) )
fi
return 0
}
complete -F _complete_meta_func complete compgen
#
# some completions for shell reserved words
#
#complete -c -k time do if then else elif '{'
#
# external commands
#
complete -e printenv
complete -c nohup exec nice eval trace truss strace sotruss gdb
_make_targets ()
{
local mdef makef gcmd cur prev i
COMPREPLY=()
cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]}
# if prev argument is -f, return possible filename completions.
# we could be a little smarter here and return matches against
# `makefile Makefile *.mk', whatever exists
case "$prev" in
-*f) COMPREPLY=( $(compgen -f $cur ) ); return 0;;
esac
# if we want an option, return the possible posix options
case "$cur" in
-) COMPREPLY=(-e -f -i -k -n -p -q -r -S -s -t); return 0;;
esac
# make reads `makefile' before `Makefile'
# GNU make reads `GNUmakefile' before all other makefiles, but we
# check that we're completing `gmake' before checking for it
if [ -f GNUmakefile ] && [ ${COMP_WORDS[0]} == gmake ]; then
mdef=GNUmakefile
elif [ -f makefile ]; then
mdef=makefile
elif [ -f Makefile ]; then
mdef=Makefile
else
mdef=*.mk # local convention
fi
# before we scan for targets, see if a makefile name was specified
# with -f
for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do
if [[ ${COMP_WORDS[i]} == -*f ]]; then
eval makef=${COMP_WORDS[i+1]} # eval for tilde expansion
break
fi
done
[ -z "$makef" ] && makef=$mdef
# if we have a partial word to complete, restrict completions to
# matches of that word
if [ -n "$2" ]; then gcmd='grep "^$2"' ; else gcmd=cat ; fi
# if we don't want to use *.mk, we can take out the cat and use
# test -f $makef and input redirection
COMPREPLY=( $(cat $makef 2>/dev/null | awk 'BEGIN {FS=":"} /^[^.# ][^=]*:/ {print $1}' | tr -s ' ' '\012' | sort -u | eval $gcmd ) )
}
complete -F _make_targets -X '+($*|*.[cho])' make gmake pmake
_umount_func ()
{
COMPREPLY=( $(mount | awk '{print $1}') )
}
complete -F _umount_func umount
_configure_func ()
{
case "$2" in
-*) ;;
*) return ;;
esac
case "$1" in
\~*) eval cmd=$1 ;;
*) cmd="$1" ;;
esac
COMPREPLY=( $("$cmd" --help | awk '{if ($1 ~ /--.*/) print $1}' | grep ^"$2" | sort -u) )
}
complete -F _configure_func configure
complete -W '"${GROUPS[@]}"' newgrp
complete -f chown ln more cat
complete -d mkdir rmdir
complete -f strip
complete -f -X '*.gz' gzip
complete -f -X '*.bz2' bzip2
complete -f -X '*.Z' compress
complete -f -X '!*.+(gz|tgz|Gz)' gunzip gzcat zcat zmore
complete -f -X '!*.Z' uncompress zmore zcat
complete -f -X '!*.bz2' bunzip2 bzcat
complete -f -X '!*.zip' unzip
complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|JPEG|bmp)' xv
complete -f -X '!*.pl' perl perl5
complete -A hostname rsh telnet rlogin ftp ping xping host traceroute nslookup
complete -A hostname rxterm rxterm3 rxvt2
complete -u su
complete -g newgrp groupdel groupmod
complete -f -X '!*.+(ps|PS)' gs gv ghostview psselect pswrap
complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype catdvi
complete -f -X '!*.+(pdf|PDF)' acroread4
complete -f -X '!*.texi*' makeinfo texi2dvi texi2html
complete -f -X '!*.+(tex|TEX)' tex latex slitex
complete -f -X '!*.+(mp3|MP3)' mpg123
complete -f -X '!*.+(htm|html)' links w3m lynx
#
# other possibilities, left as exercises
#
#complete -F _find_func find
#complete -F _man_func man
#complete -F _stty_func stty

View file

@ -0,0 +1,122 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1999 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# usage: reverse arrayname
reverse()
{
local -a R
local -i i
local rlen temp
# make r a copy of the array whose name is passed as an arg
eval R=\( \"\$\{$1\[@\]\}\" \)
# reverse R
rlen=${#R[@]}
for ((i=0; i < rlen/2; i++ ))
do
temp=${R[i]}
R[i]=${R[rlen-i-1]}
R[rlen-i-1]=$temp
done
# and assign R back to array whose name is passed as an arg
eval $1=\( \"\$\{R\[@\]\}\" \)
}
A=(1 2 3 4 5 6 7)
echo "${A[@]}"
reverse A
echo "${A[@]}"
reverse A
echo "${A[@]}"
# unset last element of A
alen=${#A[@]}
unset A[$alen-1]
echo "${A[@]}"
# ashift -- like shift, but for arrays
ashift()
{
local -a R
local n
case $# in
1) n=1 ;;
2) n=$2 ;;
*) echo "$FUNCNAME: usage: $FUNCNAME array [count]" >&2
exit 2;;
esac
# make r a copy of the array whose name is passed as an arg
eval R=\( \"\$\{$1\[@\]\}\" \)
# shift R
R=( "${R[@]:$n}" )
# and assign R back to array whose name is passed as an arg
eval $1=\( \"\$\{R\[@\]\}\" \)
}
ashift A 2
echo "${A[@]}"
ashift A
echo "${A[@]}"
ashift A 7
echo "${A[@]}"
# Sort the members of the array whose name is passed as the first non-option
# arg. If -u is the first arg, remove duplicate array members.
array_sort()
{
local -a R
local u
case "$1" in
-u) u=-u ; shift ;;
esac
if [ $# -eq 0 ]; then
echo "array_sort: argument expected" >&2
return 1
fi
# make r a copy of the array whose name is passed as an arg
eval R=\( \"\$\{$1\[@\]\}\" \)
# sort R
R=( $( printf "%s\n" "${A[@]}" | sort $u) )
# and assign R back to array whose name is passed as an arg
eval $1=\( \"\$\{R\[@\]\}\" \)
return 0
}
A=(3 1 4 1 5 9 2 6 5 3 2)
array_sort A
echo "${A[@]}"
A=(3 1 4 1 5 9 2 6 5 3 2)
array_sort -u A
echo "${A[@]}"

View file

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

View file

@ -0,0 +1,146 @@
# arrayops.bash --- hide some of the nasty syntax for manipulating bash arrays
# Author: Noah Friedman <friedman@splode.com>
# Created: 2016-07-08
# Public domain
# $Id: arrayops.bash,v 1.3 2016/07/28 15:38:55 friedman Exp $
# Commentary:
# These functions try to tame the syntactic nightmare that is bash array
# syntax, which makes perl's almost look reasonable.
#
# For example the apush function below lets you write:
#
# apush arrayvar newval
#
# instead of
#
# ${arrayvar[${#arrayvar[@]}]}=newval
#
# Because seriously, you've got to be kidding me.
# These functions avoid the use of local variables as much as possible
# (especially wherever modification occurs) because those variable names
# might shadow the array name passed in. Dynamic scope!
# Code:
#:docstring apush:
# Usage: apush arrayname val1 {val2 {...}}
#
# Appends VAL1 and any remaining arguments to the end of the array
# ARRAYNAME as new elements.
#:end docstring:
apush()
{
eval "$1=(\"\${$1[@]}\" \"\${@:2}\")"
}
#:docstring apop:
# Usage: apop arrayname {n}
#
# Removes the last element from ARRAYNAME.
# Optional argument N means remove the last N elements.
#:end docstring:
apop()
{
eval "$1=(\"\${$1[@]:0:\${#$1[@]}-${2-1}}\")"
}
#:docstring aunshift:
# Usage: aunshift arrayname val1 {val2 {...}}
#
# Prepends VAL1 and any remaining arguments to the beginning of the array
# ARRAYNAME as new elements. The new elements will appear in the same order
# as given to this function, rather than inserting them one at a time.
#
# For example:
#
# foo=(a b c)
# aunshift foo 1 2 3
# => foo is now (1 2 3 a b c)
# but
#
# foo=(a b c)
# aunshift foo 1
# aunshift foo 2
# aunshift foo 3
# => foo is now (3 2 1 a b c)
#
#:end docstring:
aunshift()
{
eval "$1=(\"\${@:2}\" \"\${$1[@]}\")"
}
#:docstring ashift:
# Usage: ashift arrayname {n}
#
# Removes the first element from ARRAYNAME.
# Optional argument N means remove the first N elements.
#:end docstring:
ashift()
{
eval "$1=(\"\${$1[@]: -\${#$1[@]}+${2-1}}\")"
}
#:docstring aset:
# Usage: aset arrayname idx newval
#
# Assigns ARRAYNAME[IDX]=NEWVAL
#:end docstring:
aset()
{
eval "$1[\$2]=${@:3}"
}
#:docstring aref:
# Usage: aref arrayname idx {idx2 {...}}
#
# Echoes the value of ARRAYNAME at index IDX to stdout.
# If more than one IDX is specified, each one is echoed.
#
# Unfortunately bash functions cannot return arbitrary values in the usual way.
#:end docstring:
aref()
{
eval local "v=(\"\${$1[@]}\")"
local x
for x in ${@:2} ; do echo "${v[$x]}"; done
}
#:docstring aref:
# Usage: alen arrayname
#
# Echoes the length of the number of elements in ARRAYNAME.
#
# It also returns number as a numeric value, but return values are limited
# by a maximum of 255 so don't rely on this unless you know your arrays are
# relatively small.
#:end docstring:
alen()
{
eval echo "\${#$1[@]}"
eval return "\${#$1[@]}"
}
#:docstring anreverse:
# Usage: anreverse arrayname
#
# Reverse the order of the elements in ARRAYNAME.
# The array variable is altered by this operation.
#:end docstring:
anreverse()
{
eval set $1 "\"\${$1[@]}\""
eval unset $1
while [ $# -gt 1 ]; do
eval "$1=(\"$2\" \"\${$1[@]}\")"
set $1 "${@:3}"
done
}
#provide arrayops
# arrayops.bash ends here

111
examples/functions/autoload Normal file
View file

@ -0,0 +1,111 @@
#
# An almost ksh-compatible `autoload'. A function declared as `autoload' will
# be read in from a file the same name as the function found by searching the
# $FPATH (which works the same as $PATH), then that definition will be run.
#
# To do this without source support, we define a dummy function that, when
# executed, will load the file (thereby re-defining the function), then
# execute that newly-redefined function with the original arguments.
#
# It's not identical to ksh because ksh apparently does lazy evaluation
# and looks for the file to load from only when the function is referenced.
# This one requires that the file exist when the function is declared as
# `autoload'.
#
# usage: autoload func [func...]
#
# The first cut of this was by Bill Trost, trost@reed.bitnet
#
# Chet Ramey
# chet@ins.CWRU.Edu
#
# Declare a function ($1) to be autoloaded from a file ($2) when it is first
# called. This defines a `temporary' function that will `.' the file
# containing the real function definition, then execute that new definition with
# the arguments given to this `fake' function. The autoload function defined
# by the file and the file itself *must* be named identically.
#
aload()
{
eval $1 '() { . '$2' ; '$1' "$@" ; return $? ; }'
}
#
# Search $FPATH for a file the same name as the function given as $1, and
# autoload the function from that file. There is no default $FPATH.
#
autoload()
{
#
# Save the list of functions; we're going to blow away the arguments
# in a second. If any of the names contain white space, TFB.
#
local args="$*"
#
# This should, I think, list the functions marked as autoload and not
# yet defined, but we don't have enough information to do that here.
#
if [ $# -eq 0 ] ; then
echo "usage: autoload function [function...]" >&2
return 1
fi
#
# If there is no $FPATH, there is no work to be done
#
if [ -z "$FPATH" ] ; then
echo autoload: FPATH not set or null >&2
return 1
fi
#
# This treats FPATH exactly like PATH: a null field anywhere in the
# FPATH is treated the same as the current directory.
#
# The path splitting command is taken from Kernighan and Pike
#
# fp=$(echo $FPATH | sed 's/^:/.:/
# s/::/:.:/g
# s/:$/:./
# s/:/ /g')
# replaced with builtin mechanisms 2001 Oct 10
fp=${FPATH/#:/.:}
fp=${fp//::/:.:}
fp=${fp/%:/:.}
fp=${fp//:/ }
for FUNC in $args ; do
#
# We're blowing away the arguments to autoload here...
# We have to; there are no arrays (well, there are, but
# this doesn't use them yet).
#
set -- $fp
while [ $# -ne 0 ] ; do
if [ -f $1/$FUNC ] ; then
break # found it!
fi
shift
done
if [ $# -eq 0 ] ; then
echo "$FUNC: autoload function not found" >&2
continue
fi
# echo auto-loading $FUNC from $1/$FUNC
aload $FUNC $1/$FUNC
done
return 0
}

View file

@ -0,0 +1,192 @@
#
# An almost ksh-compatible `autoload'. A function declared as `autoload' will
# be read in from a file the same name as the function found by searching the
# $FPATH (which works the same as $PATH), then that definition will be run.
#
# To do this without source support, we define a dummy function that, when
# executed, will load the file (thereby re-defining the function), then
# execute that newly-redefined function with the original arguments.
#
# It's not identical to ksh because ksh apparently does lazy evaluation
# and looks for the file to load from only when the function is referenced.
# This one requires that the file exist when the function is declared as
# `autoload'.
#
# usage: autoload [-pu] [func ...]
#
# options:
# -p print in a format that can be reused as input
# -u unset each function and remove it from the autoload list
#
# The first cut of this was by Bill Trost, trost@reed.edu
#
# Chet Ramey
# chet@ins.CWRU.Edu
unset _AUTOLOADS
_aindex=0
#
# Declare a function ($1) to be autoloaded from a file ($2) when it is first
# called. This defines a `temporary' function that will `.' the file
# containing the real function definition, then execute that new definition with
# the arguments given to this `fake' function. The autoload function defined
# by the file and the file itself *must* be named identically.
#
_aload()
{
eval $1 '() { . '$2' ; '$1' "$@" ; return $? ; }'
_autoload_addlist "$1"
}
_autoload_addlist()
{
local i=0
while (( i < $_aindex )); do
case "${_AUTOLOADS[i]}" in
"$1") return 1 ;;
esac
(( i += 1 ))
done
_AUTOLOADS[_aindex]="$1"
(( _aindex += 1 ))
return 0
}
_autoload_dump()
{
local func
for func in ${_AUTOLOADS[@]}; do
[ -n "$1" ] && echo -n "autoload "
echo "$func"
done
}
# Remove $1 from the list of autoloaded functions
_autoload_remove_one()
{
local i=0 nnl=0
local -a nlist
while (( i < _aindex )); do
case "${_AUTOLOADS[i]}" in
"$1") ;;
*) nlist[nnl]="${_AUTOLOADS[i]}" ; (( nnl += 1 ));;
esac
(( i += 1 ))
done
unset _AUTOLOADS _aindex
eval _AUTOLOADS=( ${nlist[@]} )
_aindex=$nnl
}
# Remove all function arguments from the list of autoloaded functions
_autoload_remove()
{
local func i es=0
# first unset the autoloaded functions
for func; do
i=0
while (( i < _aindex )); do
case "${_AUTOLOADS[i]}" in
"$func") unset -f $func ; break ;;
esac
(( i += 1 ))
done
if (( i == _aindex )); then
echo "autoload: $func: not an autoloaded function" >&2
es=1
fi
done
# then rebuild the list of autoloaded functions
for func ; do
_autoload_remove_one "$func"
done
return $es
}
#
# Search $FPATH for a file the same name as the function given as $1, and
# autoload the function from that file. There is no default $FPATH.
#
autoload()
{
local -a fp
local _autoload_unset nfp i
if (( $# == 0 )) ; then
_autoload_dump
return 0
fi
OPTIND=1
while getopts pu opt
do
case "$opt" in
p) _autoload_dump printable; return 0;;
u) _autoload_unset=y ;;
*) echo "autoload: usage: autoload [-pu] [function ...]" >&2
return 1 ;;
esac
done
shift $(( $OPTIND - 1 ))
if [ -n "$_autoload_unset" ]; then
_autoload_remove "$@"
return $?
fi
#
# If there is no $FPATH, there is no work to be done
#
if [ -z "$FPATH" ] ; then
echo "autoload: FPATH not set or null" >&2
return 1
fi
#
# This treats FPATH exactly like PATH: a null field anywhere in the
# FPATH is treated the same as the current directory.
#
# This turns $FPATH into an array, substituting `.' for `'
#
eval fp=( $(
IFS=':'
set -- ${FPATH}
for p in "$@" ; do echo -n "${p:-.} "; done
)
)
nfp=${#fp[@]}
for FUNC ; do
i=0;
while (( i < nfp )) ; do
if [ -f ${fp[i]}/$FUNC ] ; then
break # found it!
fi
(( i += 1 ))
done
if (( i == nfp )) ; then
echo "autoload: $FUNC: autoload function not found" >&2
es=1
continue
fi
# echo auto-loading $FUNC from ${fp[i]}/$FUNC
_aload $FUNC ${fp[i]}/$FUNC
es=0
done
return $es
}

View file

@ -0,0 +1,125 @@
#From: Mark Kennedy <mark.t.kennedy@gmail.com> (<mtk@ny.ubs.com>)
#Message-ID: <35E2B899.63A02DF5@ny.ubs.com>
#Date: Tue, 25 Aug 1998 09:14:01 -0400
#To: chet@nike.ins.cwru.edu
#Subject: a newer version of the ksh-style 'autoload'
#enclosed you'll find 'autoload.v3', a version of the autoloader
#that emulates the ksh semantics of delaying the resolution (and loading) of the function
#until its first use. i took the liberty of simplifying the code a bit although it still uses the
#same functional breakdown. i recently went through the exercise of converting
#my ksh-based environment to bash (a very, very pleasant experience)
#and this popped out.
# the psuedo-ksh autoloader.
# The first cut of this was by Bill Trost, trost@reed.bitnet.
# The second cut came from Chet Ramey, chet@ins.CWRU.Edu
# The third cut came from Mark Kennedy, mtk@ny.ubs.com. 1998/08/25
unset _AUTOLOADS
_aload()
{
local func
for func; do
eval $func '()
{
local f=$(_autoload_resolve '$func')
if [[ $f ]]; then
. $f
'$func' "$@"
return $?
else
return 1
fi
}'
_autoload_addlist $func
done
}
_autoload_addlist()
{
local func
for func in ${_AUTOLOADS[@]}; do
[[ $func = "$1" ]] && return
done
_AUTOLOADS[${#_AUTOLOADS[@]}]=$1
}
_autoload_dump()
{
local func
for func in ${_AUTOLOADS[@]}; do
[[ $1 ]] && echo -n "autoload "
echo $func
done
}
_autoload_remove_one()
{
local func
local -a NEW_AUTOLOADS
for func in ${_AUTOLOADS[@]}; do
[[ $func != "$1" ]] && NEW_AUTOLOADS[${#NEW_AUTOLOADS[@]}]=$func
done
_AUTOLOADS=( ${NEW_AUTOLOADS[@]} )
}
_autoload_remove()
{
local victim func
for victim; do
for func in ${_AUTOLOADS[@]}; do
[[ $victim = "$func" ]] && unset -f $func && continue 2
done
echo "autoload: $func: not an autoloaded function" >&2
done
for func; do
_autoload_remove_one $func
done
}
_autoload_resolve()
{
if [[ ! "$FPATH" ]]; then
echo "autoload: FPATH not set or null" >&2
return
fi
local p
for p in $( (IFS=':'; set -- ${FPATH}; echo "$@") ); do
p=${p:-.}
if [ -f $p/$1 ]; then echo $p/$1; return; fi
done
echo "autoload: $1: function source file not found" >&2
}
autoload()
{
if (( $# == 0 )) ; then _autoload_dump; return; fi
local opt OPTIND
while getopts pu opt
do
case $opt in
p) _autoload_dump printable; return;;
u) shift $((OPTIND-1)); _autoload_remove "$@"; return;;
*) echo "autoload: usage: autoload [-pu] [function ...]" >&2; return;;
esac
done
shift $(($OPTIND-1))
_aload "$@"
}

View file

@ -0,0 +1,556 @@
## -*- sh -*-
# The psuedo-ksh autoloader.
# How to use:
# o One function per file.
# o File and function name match exactly.
# o File is located in a directory that is in FPATH.
# o This script (autoload) must be sourced in as early as possible. This
# implies that any code in this script should NOT rely on any library of local
# or self-defined functions having already been loaded.
# o autoload must be called for each function before the function can be used. If
# autoloads are in directories where there are nothing but autoloads, then
# 'autoload /path/to/files/*' suffices (but see options -a and -f).
# o The call must be made in the current environment, not a subshell.
# o The command line suffices as "current environment". If you have autoload
# calls in a script, that script must be dotted into the process.
# The first cut of this was by Bill Trost, trost@reed.bitnet.
# The second cut came from Chet Ramey, chet@ins.CWRU.Edu
# The third cut came from Mark Kennedy, mtk@ny.ubs.com. 1998/08/25
# The fourth cut came from Matthew Persico, matthew.persico@gmail.com 2017/August
autoload_calc_shimsize ()
{
echo $((AUTOLOAD_SHIM_OVERHEAD + 3 * ${#1}))
}
_autoload_split_fpath ()
{
(IFS=':'; set -- ${FPATH}; echo "$@")
}
_aload()
{
local opt OPTIND
local doexport=0
local doreload=0
local doverbose=0
local doevalshim=0
local loadthese
local optimize=0
local loaded=0
local exported=0
local optimized=0
local summary=0
local dofpath=0
while getopts xrvla:oyf opt; do
case $opt in
x) doexport=1;;
r) doreload=1;;
v) doverbose=1;;
l) doevalshim=1;;
a) loadthese=$(find $OPTARG -maxdepth 1 -type f -printf '%f ');;
o) optimize=1;;
y) summary=1;;
f) loadthese=$(find $(_autoload_split_fpath) -maxdepth 1 -type f -printf '%f ');;
*) echo "_aload: usage: _aload [-xrvlyf] [-a dir] [function ...]" >&2; return;;
esac
done
shift $(($OPTIND-1))
[ -z "$loadthese" ] && loadthese="$@"
local func
for func in $loadthese; do
local exists_fn
exists_fn=$(declare -F $func)
if [ -n "$exists_fn" ] && ((doreload==0)) && ((doevalshim==0))
then
if ((doverbose))
then
echo "autoload: function '$func' already exists"
fi
else
local andevaled=''
local andexported=''
local evalstat=0
local doshim=1
local funcfile
funcfile=$(_autoload_resolve $func)
if [[ $funcfile ]] ; then
## The file was found for $func. Process it.
if ((optimize)); then
## For the first function loaded, we will not know
## AUTOLOAD_SHIM_OVERHEAD. We can only calculate it after
## we have loaded one function.
if [[ $AUTOLOAD_SHIM_OVERHEAD ]]; then
local size=$(wc -c $funcfile| sed 's/ .*//')
local shimsize=$(autoload_calc_shimsize $func)
if (( size <= shimsize)); then
doshim=0
andevaled=', optimized'
((optimized+=1))
fi
fi
fi
if ((doevalshim)); then
doshim=0
andevaled=', evaled'
fi
## 'brand' as in branding a cow with a mark. We add a local
## variable to each function we autoload so that we can tell
## later on it is an autoloaded function without having to
## maintain some bash array or hash that cannot be passed to
## and used by subshells.
local brandtext
brandtext="eval \"\$(type $func | sed -e 1d -e 4ilocal\\ AUTOLOADED=\'$func\')\""
if ((doshim)); then
## Don't bother trying to save space by shoving all the
## eval text below onto one unreadable line; new lines will
## be added at your semicolons and any indentation below
## seems to be ignored anyway if you export the function;
## look at its BASH_FUNCTION representation.
eval $func '()
{
local IS_SHIM="$func"
local file=$(_autoload_resolve '$func')
if [[ $file ]]
then
. $file
'$brandtext'
'$func' "$@"
return $?
else
return 1;
fi
}'
else
. $funcfile
eval "$brandtext"
fi
evalstat=$?
if((evalstat==0))
then
((loaded+=1))
((doexport)) && export -f $func && andexported=', exported' && ((exported+=1))
((doverbose)) && echo "$func autoloaded${andexported}${andevaled}"
if [[ ! $AUTOLOAD_SHIM_OVERHEAD ]] && ((doshim)); then
## ...we have just loaded the first function shim into
## memory. Let's calc the AUTOLOAD_SHIM_OVERHEAD size
## to use going forward. In theory, we could check
## again here to see if we should optimize and source
## in this function, now that we now the
## AUTOLOAD_SHIM_OVERHEAD. In practice, it's not worth
## duping that code or creating a function to do so for
## one function.
AUTOLOAD_SHIM_OVERHEAD=$(type $func | grep -v -E "^$1 is a function" | sed "s/$func//g"| wc -c)
export AUTOLOAD_SHIM_OVERHEAD
fi
else
echo "$func failed to load" >&2
fi
fi
fi
done
((summary)) && echo "autoload: loaded:$loaded exported:$exported optimized:$optimized overhead:$AUTOLOAD_SHIM_OVERHEAD bytes"
}
_autoload_dump()
{
local opt OPTIND
local opt_p=''
local opt_s=''
while getopts ps opt
do
case $opt in
p ) opt_p=1;;
s ) opt_s=1;;
esac
done
shift $(($OPTIND-1))
local exported=''
local executed=''
local func
for func in $(declare | grep -E 'local\\{0,1} AUTOLOADED' | sed -e "s/.*AUTOLOADED=//" -e 's/\\//g' -e 's/[");]//g' -e "s/'//g")
do
if [ -n "$opt_p" ]; then echo -n "autoload "; fi
if [ -n "$opt_s" ]
then
exported=$(declare -F | grep -E "${func}$" | sed 's/declare -f\(x\{0,1\}\).*/\1/')
[ "$exported" = 'x' ] && exported=' exported' || exported=' not exported'
executed=$(type $func | grep 'local IS_SHIM')
[ -z "$executed" ] && executed=' executed' || executed=' not executed'
fi
echo "${func}${exported}${executed}"
done
}
_autoload_resolve()
{
if [[ ! "$FPATH" ]]; then
echo "autoload: FPATH not set or null" >&2
return
fi
local p # for 'path'. The $() commands in the for loop split the FPATH
# string into its constituents so that each one may be processed.
for p in $( _autoload_split_fpath ); do
p=${p:-.}
if [ -f $p/$1 ]; then echo $p/$1; return; fi
done
echo "autoload: $1: function source file not found" >&2
}
_autoload_edit()
{
[ -z "$EDITOR" ] && echo "Error: no EDITOR defined" && return 1
local toedit
local func
for func in "$@"
do
local file=$(_autoload_resolve $func)
if [[ $file ]]
then
toedit="$toedit $file"
else
echo "$funcname not found in FPATH funcfile. Skipping."
fi
done
[ -z "$toedit" ] && return 1
local timemarker=$(mktemp)
$EDITOR $toedit
local i
for i in $toedit
do
if [ $i -nt $timemarker ]
then
local f=$(basename $i)
echo Reloading $f
autoload -r $f
fi
done
}
_autoload_page()
{
[ -z "$PAGER" ] && echo "Error: no PAGER defined" && return 1
local topage
local func
for func in "$@"
do
local file=$(_autoload_resolve $func)
if [[ $file ]]
then
topage="$topage $file"
else
echo "$funcname not found in FPATH funcfile. Skipping."
fi
done
[ -z "$topage" ] && return 1
$PAGER $topage
}
_autoload_remove()
{
unset -f "$@"
}
_autoload_help()
{
cat <<EOH
NAME
autoload
SYNOPSIS
autoload [-ps]
autoload [-xuremloyv] [function ...]
autoload -a directory [-oyv]
autoload -f [-oyv]
autoload [-h]
autoreload [function ...]
DESCRIPTION
An implementation of the 'autoload' functionality built into other
shells, of which 'ksh' is the most prominent. It allows for a keeping
the process environment small by loading small 'shim' functions into
memory that will, on first call, load the full text of the given
function and run it. Subsequent calls to the function just run the
function.
'autoreload' is a synonym for 'autoload -r'. See below.
USAGE
o Each function to be autoloaded should be defined in a single file,
named exactly the same as the function.
o In order to avoid side effects, do NOT put code other than the
function definition in the file. Unless of course you want to do some
one-time initialization. But beware that if you reload the function
for any reason, you will rerun the initialization code. Make sure
your initialization is re-entrant. Or, better yet,
*** do NOT put code other than the function definition in the file ***
o These function definition files should be placed in a directory that
is in the FPATH environment variable. Subdirectories are NOT scanned.
o The autoload script should be sourced into the current process as
early as possible in process start up. See NOTES below for
suggestions.
o The calls to the autoload function must be made in the current
process. If your calls are in their own script, that script must be
sourced in. Command line invocations are also sufficient. (But see
'-l' below.)
o The first time the function is called, the shim function that was
created by the 'autoload' call is what is executed. This function
then goes and finds the appropriate file in FPATH, sources it in and
then calls the actual function with any arguments you just passed in
to the shim function. Subsequent calls just run the function.
OPTIONS
-a Autoload (a)ll the functions found in the given directory.
-f Autoload all the functions found in all the directories on the
FPATH.
-p Print all the autoloaded functions.
-s Print all the autoloaded functions and add their export status.
-x Export the specified functions to the environment for use in
subshells.
-u Unset the function, so it can be reloaded.
-r Reload the shims of the specified functions, even if the functions
have been already been executed. This will allow you to modify the
functions' source and have the new version executed next time the
function is called.
It would be very easy to modify a function's script, run the
function and scratch your head for a long time trying to figure out
why your changes are not being executed. That's why we provide the
'-e' flag described below for modifications.
Reloads, of course, only apply in the context of the current session
and any future subshell you start from the current session. Existing
sessions will need to have the same 'autoload -r' command run in
them.
-e Find the scripts in which the specified functions are defined and
start up \$EDITOR on those scripts. Reload the ones that were
modified when you exit \$EDITOR. (Note: If you use 'autoload -e foo'
to edit function 'foo', and then in your editor you separately load
up function 'bar', 'autoload' has no way of knowing that you edited
'bar' and will NOT reload 'bar' for you.)
Reloads, of course, only apply in the context of the current session
and any future subshell you start from the current session. Existing
sessions will need to have the same 'autoload -r' command run in
them.
-m Find the scripts in which the specified functions are defined and
run \$PAGER on them ('m' is for 'more', because 'p' (page) and 'l'
(load) are already used as options in 'autoload').
-l When autoloading a function, eval the shim immediately in order to
load the true function code. See "Using '-l'" in the NOTES below for
details.
-o Optimize. When autoloading, take the time to execute
'theCharCount=\$(wc -c \$theFuncFile)'
for each function and
if \$theCharCount < \$AUTOLOAD_SHIM_OVERHEAD
don't shim it, just eval directly.
-y Summar(y). Print the number of loaded, exported and optimized
functions.
-v Turns up the chattiness.
NOTES
o Calling 'autoload' on a function that already exists (either shimmed
or expanded) silently ignores the request to load the shim unless it
has been previously removed (-u) or you force the reload (-r).
o Changing and reloading a function that has been exported does not
require it be re-exported; the modifications will appear in
subsequent subshells.
o Using '-1'
If you are running under set -x and/or set -v, you may see that the
shim does not appear to "work"; instead of seeing the shim first and
the real code subsequently, you may see the shim evaluated multiple
times.
This may not be an error; review your code. What is most likely
happening is that you are calling the function in subshells via
backticks or $(), or in a script that is not being sourced into the
current environment. If you have not previously called the function
in question at your command line or in a script that was sourced into
the current environment, then the various subshells are going to
encounter the shim and replace with the real code before executing.
Remember, however, that environment modifications that occur in a
subshell are NOT propagated back to the calling shell or over to any
sibling shells. So, if you call an autoloaded function in a very
tight loop of very many subshells, you may want to make an 'autoload
-l' call before you start your loop. '-l' will instruct 'autoload' to
bypass the shim creation and just source in the function's file
directly. For a few calls, the overhead of repeatedly running the
shim is not expensive, but in a tight loop, it might be. Caveat
Programmer.
o Although the number of functions in the environment does not change
by using 'autoload', the amount of memory they take up can be greatly
reduced, depending on the size of your functions. If you have a lot
of small functions, then it is possible that the shim text will be
larger than your actual functions, rendering the memory savings moot.
'small' in this case can be determined by calling the function
'autoload_calc_shimsize' with the name of the function to determine
its shim size.
o In order to support the -p and -s options, we need a way to determine
if a function 'func' has been autoloaded or if it was loaded
diredctly. In order to do that, we modify the function's code by
adding the text
local AUTOLOADED='func';
to the shim and to the actual function text, just after the opening
brace. Then supporting -p and -s is just a matter of grepping through
all the function text in memory. Even though grepping through the
environment may not be the most efficient way to support this, it is
the simplest to implement for -p and -s operations that are not
heavily used.
As a consequence of this (and other reasons), the AUTOLOAD* namespace
is reserved for autoloading. Make sure you check any functions that
you bring under autoload for use of variables or functions that start
with AUTOLOAD and change them.
o The easiest way to load shims for all functions on the FPATH is to run
autoload -f -x
in the profile that gets run for login shells.
When called in the profile of a login shell where no definitions
exist, -f will load all functions it can find on FPATH and -x will
export all of those functions to be available in subshells when this
is called in a login shell. Using this option will relieve you of the
need to call 'autoload' after Every Single Function Definition, nor
will you need to call it in subshells.
The only thing left to do is to load up the autoload function itself
and its helper functions. That needs to happen in your profile:
export FPATH=~/functions # or wherever you stash them
if [ -z $(declare -F autoload) ]
then
. ~/bin/autoload # or wherever you've put it
fi
The 'if' statement is used to make sure we don't reload autoload
needlessly. Sourcing in the autoload script loads the 'autoload'
function and all of its support functions. Additionally, we export
all of these functions so that they are available in subshells; you
do not have to re-source the autoload file in '.bashrc'.
o Even with all of these shenanigans, you will find cases where no
matter how hard you try, your autoloaded functions will be
unavailable to you, even if you run 'autoload -x -f'. The typical
condition for this is starting up not a subshell, but a brand new
DIFFERENT shell. And the typical example of this is git extensions.
At the time of this writing, git extensions work by taking a command
'git foo' and looking for a file 'git-foo' on the path. 'git' then
executes 'git-foo' in a new shell - it executes your command in
/bin/sh. That's not a subshell of your process. It will not get your
exported shell functions. Ballgame over.
If you find that you want your functions to be available in such
circumstances, convert them back to plain old scripts, make sure they
are 'sh' compliant and take the read/parse hit every time they are
run.
EOH
}
autoload()
{
if (( $# == 0 )) ; then _autoload_dump; return; fi
local opt OPTIND OPTARG
local passthru
local dumpopt
while getopts psuema:yxrvlohf opt
do
case $opt in
p|s) dumpopt="$dumpopt -${opt}";;
u) shift $((OPTIND-1)); _autoload_remove "$@"; return;;
e) shift $((OPTIND-1)); _autoload_edit "$@"; return;;
m) shift $((OPTIND-1)); _autoload_page "$@"; return;;
x|r|v|l|y|f|o) passthru="$passthru -$opt";;
a) passthru="$passthru -$opt $OPTARG";;
h) _autoload_help; return;;
*) echo "autoload: usage: autoload [-puUx] [function ...]" >&2; return;;
esac
done
shift $(($OPTIND-1))
if [ -n "$dumpopt" ]
then
_autoload_dump $dumpopt
else
_aload $passthru "$@"
fi
}
autoreload ()
{
autoload -r "$@"
}
## When we source in autoload, we export (but NOT autoload) the autoload
## functions so that they are available in subshells and you don't have to
## source in the autoload file in subshells.
export -f _aload \
_autoload_dump \
_autoload_edit \
_autoload_help \
_autoload_page \
_autoload_resolve \
_autoload_split_fpath \
autoload \
autoload_calc_shimsize \
autoreload

View file

@ -0,0 +1,184 @@
#!/bin/bash
workdir=$(mktemp -d)
cp autoload $workdir
cd $workdir
pwd
. ./autoload
funclist='ALTEST_func1 ALTEST_funcexport ALTEST_funcu'
for funcname in $funclist; do
cat <<EOFFUNC > $funcname
$funcname ()
{
echo this is $funcname
}
EOFFUNC
done
export FPATH=$workdir
autoload ALTEST_func1 ALTEST_funcu
autoload -x ALTEST_funcexport
ok=0
failed=0
for funcname in $funclist; do
testname="$funcname loaded"
got=$(type $funcname 2>&1)
if [[ $got =~ "$funcname: not found" ]]; then
echo "## Failed $testname"
((failed+=1))
else
echo "ok - $testname"
((ok+=1))
testname="$funcname is a shim"
if [[ ! $got =~ "IS_SHIM" ]]; then
echo "## Failed $testname"
((failed+=1))
else
echo "ok - $testname"
((ok+=1))
testname="$funcname shim executed"
$funcname > /dev/null
got=$(type $funcname 2>&1)
if [[ $got =~ "IS_SHIM" ]]; then
echo "## Failed $testname"
((failed+=1))
else
echo "ok - $testname"
((ok+=1))
fi
fi
fi
done
funcname=ALTEST_func1
testname="$funcname shim reloaded"
autoload -r $funcname
got=$(type $funcname 2>&1)
if [[ ! $got =~ "IS_SHIM" ]]; then
echo "## Failed $testname"
((failed+=1))
else
echo "ok - $testname"
((ok+=1))
fi
funcname=ALTEST_funcu
testname="$funcname shim unloaded"
autoload -u $funcname
got=$(type $funcname 2>&1)
if [[ ! $got =~ "$funcname: not found" ]]; then
echo "## Failed $testname"
((failed+=1))
else
echo "ok - $testname"
((ok+=1))
fi
testname="autoload -p"
got=$(autoload -p | grep ALTEST)
if [[ ! $got =~ "autoload ALTEST_func1" ]] || \
[[ ! $got =~ "autoload ALTEST_funcexport" ]] ; then
echo "## Failed $testname"
((failed+=1))
else
echo "ok - $testname"
((ok+=1))
fi
testname="autoload -s"
echo "Executing $testname, could take a long time..."
got=$(autoload -s | grep ALTEST)
if [[ ! $got =~ "ALTEST_func1 not exported not executed" ]] || \
[[ ! $got =~ "ALTEST_funcexport exported executed" ]] ; then
echo "## Failed $testname"
echo "## got: $got"
((failed+=1))
else
echo "ok - $testname"
((ok+=1))
fi
testname="autoload -r -a $FPATH"
autoload -r -a $FPATH
localfailed=0
localok=0
for funcname in $funclist; do
got=$(type $funcname 2>&1)
if [[ $got =~ "$funcname: not found" ]]; then
echo "## Failed $testname - $funcname"
((localfailed+=1))
else
((localok+=1))
if [[ ! $got =~ "IS_SHIM" ]]; then
((localfailed+=1))
else
((localok+=1))
fi
fi
done
if ((localfailed==0)); then
echo "ok - $testname"
((ok+=1))
else
((failed+=1))
fi
testname="autoload -u $funclist"
autoload -u $funclist
localfailed=0
localok=0
for funcname in $funclist; do
got=$(type $funcname 2>&1)
if [[ ! $got =~ "$funcname: not found" ]]; then
echo "## Failed $testname - $funcname"
((localfailed+=1))
else
((localok+=1))
fi
done
if ((localfailed==0)); then
echo "ok - $testname"
((ok+=1))
else
((failed+=1))
fi
testname="autoload -r -f"
autoload -r -f
localfailed=0
localok=0
for funcname in $funclist; do
got=$(type $funcname 2>&1)
if [[ $got =~ "$funcname: not found" ]]; then
echo "## Failed $testname - $funcname"
((localfailed+=1))
else
((localok+=1))
if [[ ! $got =~ "IS_SHIM" ]]; then
((localfailed+=1))
else
((localok+=1))
fi
fi
done
if ((localfailed==0)); then
echo "ok - $testname"
((ok+=1))
else
((failed+=1))
fi
echo $ok passed, $failed failed
exit $failed

View file

@ -0,0 +1,23 @@
# Date: Fri, 11 Oct 91 11:22:36 edt
# From: friedman@gnu.ai.mit.edu
# To: bfox@gnu.ai.mit.edu
# A replacement for basename(1). Not all the systems I use have this
# program. Usage: basename [path] {extension}
function basename ()
{
local path="$1"
local suffix="$2"
local tpath="${path%/}"
# Strip trailing '/' characters from path (unusual that this should
# ever occur, but basename(1) seems to deal with it.)
while [ "${tpath}" != "${path}" ]; do
tpath="${path}"
path="${tpath%/}"
done
path="${path##*/}" # Strip off pathname
echo ${path%${suffix}} # Also strip off extension, if any.
}

View file

@ -0,0 +1,48 @@
# C-shell compatibility package.
# setenv VAR VALUE
function setenv ()
{
export $1="$2"
}
function unsetenv ()
{
unset $1
}
# Can't write foreach yet. Need pattern matching, and a few extras.
function foreach () {
echo 'Can'\''t do `foreach'\'' yet. Type "help for".'
}
# Make this work like csh's. Special case "term" and "path".
#set () {
#}
chdir ()
{
builtin cd "$@"
}
# alias - convert csh alias commands to bash functions
# from Mohit Aron <aron@cs.rice.edu>
# posted to usenet as <4i5p17$bnu@larry.rice.edu>
function alias ()
{
if [ "x$2" = "x" ]
then
declare -f $1
else
case $2 in
*[#\!]*)
comm=$(echo $2 | sed 's/\\!\*/\"$\@\"/g
s/\\!:\([1-9]\)/\"$\1\"/g
s/#/\\#/g')
;;
*)
comm="$2 \"\$@\"" ;;
esac
eval function $1 \(\) "{" command "$comm" "; }"
fi
}

View file

@ -0,0 +1,21 @@
# Date: Fri, 11 Oct 91 11:22:36 edt
# From: friedman@gnu.ai.mit.edu
# To: bfox@gnu.ai.mit.edu
# A replacement for dirname(1). This one appears less often on some
# systems I use than basename(1), and I really depend on it for some
# things. Usage: dirname [path]
function dirname ()
{
local dir="$1"
local tdir="${dir%/}"
# Strip trailing '/' characters from dir (unusual that this should
# ever occur, but dirname(1) seems to deal with it.)
while [ "${tdir}" != "${dir}" ]; do
tdir="${dir}"
dir="${tdir%/}"
done
echo "${dir%/*}"
}

160
examples/functions/dirstack Normal file
View file

@ -0,0 +1,160 @@
#!/bin/bash
# @(#) dirstack
###
# Another implementation of the directory manipulation functions
# published in the Bolsky & Korn book : "The new Korn shell" :
# cd, to change current directory
# d, to display the stack content
# Eric Sanchis (eric.sanchis@iut-rodez.fr), 2012
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###
shopt -s expand_aliases
shopt -s extglob
shopt -s cdable_vars
alias integer='declare -i'
integer MAX=32
integer INDMAX=MAX-1
integer INDTOP=0
unalias cd 2>/dev/null
alias cd=cdir
unset tab
tab[INDTOP]="$(pwd)"
function cdir
{
local -i ind
local dir
dir="${1:-$HOME}"
case "$dir" in
- ) # cd - => equivalent to : cd -1
ind=INDTOP-1
cd_by_number $ind
;;
-+([[:digit:]]) ) # cd -n
ind=$INDTOP-${dir#-}
cd_by_number $ind
;;
*) # cd ~ or cd dir_name
cd_by_name "$dir"
esac
}
function cd_by_number
{
local -i k=$1
local -i j
local dirtmp
if (( k < 0 ))
then
echo Impossible to change directory >&2
return 1
else
dirtmp="${tab[k]}"
j=k+1
while (( j <= INDTOP ))
do
tab[j-1]="${tab[j]}"
j=j+1
done
tab[INDTOP]="$dirtmp"
\cd "${tab[INDTOP]}"
fi
}
function cd_by_name
{
local -i i
local rep
rep=$( \cd "$1" &>/dev/null && pwd)
if [[ -z "$rep" ]]
then
echo cd : "$1" unknown >&2
return 1
fi
i=$INDTOP
while (( i >= 0 ))
do
if [[ "${tab[i]}" == "$rep" ]]
then break
fi
i=i-1
done
if (( i == INDTOP ))
then # cd -0 => we do nothing !
return 0
elif (( i == -1 ))
then # the directory isn't in the stack
if (( INDTOP == INDMAX ))
then # the stack is FULL
# the oldest directory is removed
local -i m
m=1
while (( m <= INDMAX ))
do
tab[m-1]="${tab[m]}"
m=m+1
done
else # the new directory is added to the top of the stack
INDTOP=INDTOP+1
fi
tab[INDTOP]="$rep"
\cd "${tab[INDTOP]}"
return 0
else # the directory is already in the stack
# $i gives its index
cd_by_number $i
fi
}
function d # display the directory stack
{
local -i i
local rep
i=0
while (( $i <= $INDTOP ))
do
rep="${tab[INDTOP-i]#$HOME/}"
case "$rep" in
$HOME) rep="~" ;;
/* ) : ;;
* ) rep="~/$rep"
esac
echo "$i ) $rep"
i=i+1
done
}

View file

@ -0,0 +1,22 @@
# Contributed by Noah Friedman and Roland McGrath.
# To be run by the PROMPT_COMMAND variable, so that one can see what
# the exit status of processes are.
function check_exit_status ()
{
local status="$?"
local signal=""
if [ ${status} -ne 0 ] && [ ${status} != 128 ]; then
# If process exited by a signal, determine name of signal.
if [ ${status} -gt 128 ]; then
signal="$(builtin kill -l $((${status} - 128)) 2>/dev/null)"
if [ "$signal" ]; then signal="($signal)"; fi
fi
echo "[Exit ${status} ${signal}]" 1>&2
fi
return 0
}
PROMPT_COMMAND=check_exit_status

View file

@ -0,0 +1,50 @@
# Contributed by Noah Friedman.
# To avoid using a function in bash, you can use the `builtin' or
# `command' builtins, but neither guarantees that you use an external
# program instead of a bash builtin if there's a builtin by that name. So
# this function can be used like `command' except that it guarantees the
# program is external by first disabling any builtin by that name. After
# the command is done executing, the state of the builtin is restored.
function external ()
{
local state=""
local exit_status
if builtin_p "$1"; then
state="builtin"
enable -n "$1"
fi
command "$@"
exit_status=$?
if [ "$state" = "builtin" ]; then
enable "$1"
fi
return ${exit_status}
}
# What is does is tell you if a particular keyword is currently enabled as
# a shell builtin. It does NOT tell you if invoking that keyword will
# necessarily run the builtin. For that, do something like
#
# test "$(builtin type -type [keyword])" = "builtin"
#
# Note also, that disabling a builtin with "enable -n" will make builtin_p
# return false, since the builtin is no longer available.
function builtin_p ()
{
local word
set $(builtin type -all -type "$1")
for word in "$@" ; do
if [ "${word}" = "builtin" ]; then
return 0
fi
done
return 1
}

13
examples/functions/fact Normal file
View file

@ -0,0 +1,13 @@
# Who said shells can't use recursion? Here is a factorial function.
# You call it with a number as an argument, and it returns the factorial
# of that number.
fact ()
{
local num=$1;
if [ "$num" = 1 ] ; then
echo 1
return ;
fi;
echo $(( $num * $(fact $(( $num - 1 )) ) ))
}

77
examples/functions/fstty Normal file
View file

@ -0,0 +1,77 @@
#
# A function that works as a front end for both stty and the `bind'
# builtin, so the tty driver and readline see the same changes
#
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 2011 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Convert between the stty ^H control character form and the readline \C-H
# form
#
cvt()
{
echo "$@" | cat -v | sed 's/\^/\\C-/'
}
#
# stty front-end. Parses the argument list and creates two command strings,
# one for stty, another for bind.
#
fstty()
{
local cmd="" bargs=""
local e
while [ $# -gt 0 ]
do
case "$1" in
-a) cmd="$cmd everything"
;;
erase) shift;
e=$(cvt "$1")
cmd="$cmd erase $1"
bargs="$bargs '\"$e\": backward-delete-char'"
;;
kill) shift
e=$(cvt "$1")
cmd="$cmd kill $1"
bargs="$bargs '\"$e\": unix-line-discard'"
;;
werase) shift;
e=$(cvt "$1")
cmd="$cmd erase $1"
bargs="$bargs '\"$e\": backward-kill-word'"
;;
lnext) shift;
e=$(cvt "$1")
cmd="$cmd erase $1"
bargs="$bargs '\"$e\": quoted-insert'"
;;
*) cmd="$cmd $1"
;;
esac
shift
done
command stty $cmd
if [ -n "$bargs" ]; then
builtin bind $bargs
fi
}

43
examples/functions/func Normal file
View file

@ -0,0 +1,43 @@
#
# func -- print out definitions for functions named by arguments
#
# usage: func name [name ...]
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1991 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
func()
{
local status=0
if [ $# -eq 0 ] ; then
echo "usage: func name [name...]" 1>&2
return 1
fi
for f
do
if [ "$(builtin type -type $f)" != "function" ] ; then
echo "func: $f: not a function" 1>&2
status=1 # one failed
continue
fi
builtin type $f | sed 1d
done
return $status
}

View file

@ -0,0 +1,79 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 2002 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# inet2hex - Internet address conversion, dotted-decimal to hex
#
inet2hex ()
{
local IFS
IFS=.
set -- $1
if (( $# != 4 )); then
echo "inet2hex: incorrect input format: $1" >&2
echo "inet2hex: usage: inet2hex XX.XX.XX.XX" >&2
return 2
fi
printf "0x%02x%02x%02x%02x\n" $1 $2 $3 $4
}
#
# hex2inet - Internet address conversion, hex to dotted-decimal
#
hex2inet ()
{
local x1 x2 x3 x4
local rev
OPTIND=1
while getopts "r" o
do
case "$o" in
r) rev=true;;
*) echo "hex2inet: usage: hex2inet [-r] [0x]XXXXXXXX" >&2 ; exit 2;;
esac
done
shift $(( $OPTIND - 1 ))
case "$1" in
0x*) h=${1#??} ;;
*) h=$1 ;;
esac
if (( ${#h} != 8 )); then
echo "hex2inet: $h not in inet format" >&2
echo "hex2inet: usage: hex2inet [0x]XXXXXXXX" >&2
return 2
fi
x1=$(( 0x${h:0:2} ))
x2=$(( 0x${h:2:2} ))
x3=$(( 0x${h:4:2} ))
x4=$(( 0x${h:6:2} ))
if [ -z "$rev" ] ; then
printf "%d.%d.%d.%d\n" $x1 $x2 $x3 $x4
else
printf "%d.%d.%d.%d\n" $x4 $x3 $x2 $x1
fi
return 0
}

14
examples/functions/inpath Normal file
View file

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

41
examples/functions/isnum2 Normal file
View file

@ -0,0 +1,41 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1998 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
isnum2()
{
case "$1" in
[-+] | '') return 1;; # empty or bare `-' or `+'
[-+]*[!0-9]*) return 1;; # non-digit with leading sign
[-+]*) return 0;; # OK
*[!0-9]*) return 1;; # non-digit
*) return 0;; # OK
esac
}
# this one handles floating point
isnum3()
{
case "$1" in
'') return 1;; # empty
*[!0-9.+-]*) return 1;; # non-digit, +, -, or .
*?[-+]*) return 1;; # sign as second or later char
*.*.*) return 1;; # multiple decimal points
*) return 0;; # OK
esac
}

View file

@ -0,0 +1,14 @@
# Thanks to Chris F. A. Johnson <c.f.a.johnson@rogers.com> for this one
is_validip()
{
case "$*" in
""|*[!0-9.]*|*[!0-9]) return 1 ;;
esac
local IFS=.
set -- $*
[ $# -eq 4 ] &&
[ ${1:-666} -le 255 ] && [ ${2:-666} -le 255 ] &&
[ ${3:-666} -le 255 ] && [ ${4:-666} -le 254 ]
}

54
examples/functions/ksh-cd Normal file
View file

@ -0,0 +1,54 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 2001 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# 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
}

View file

@ -0,0 +1,58 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1999 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# replacements for test/[ that do arithmetic expansion on the operands to
# the arithmetic operators, like ksh.
#
function test()
{
local -i n1 n3
case "$#" in
3) case "$2" in
-lt|-gt|-eq|-ne|-le|-ge) n1=$(( $1 ))
n3=$(( $3 ))
builtin test "$n1" $2 "$n3"
return $?;;
*) builtin test "$@" ;;
esac;;
*) builtin test "$@" ;;
esac
}
function [()
{
local -i n1 n3
case "$#" in
4) case "$2" in
-lt|-gt|-eq|-ne|-le|-ge) n1=$(( $1 ))
n3=$(( $3 ))
builtin [ "$n1" $2 "$n3" ]
return $?;;
*) builtin [ "$@" ;;
esac;;
*) builtin [ "$@" ;;
esac
}
q=7
[ q -lt 10 ]
echo $?
[ $q -lt 10 ]
echo $?

246
examples/functions/kshenv Normal file
View file

@ -0,0 +1,246 @@
#
# .kshenv -- functions and aliases to provide the beginnings of a ksh
# environment for bash.
#
# Chet Ramey
# chet@ins.CWRU.Edu
#
#
# Copyright 2002 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# These are definitions for the ksh compiled-in `exported aliases'. There
# are others, but we already have substitutes for them: "history", "type",
# and "hash".
#
alias r="fc -s"
alias functions="typeset -f"
alias integer="typeset -i"
alias nohup="nohup "
alias command="command "
alias stop="kill -s STOP"
alias redirect="command exec"
alias hist="fc"
#
# An almost-ksh compatible `whence' command. This is as hairy as it is
# because of the desire to exactly mimic ksh (whose behavior was determined
# empirically).
#
# This depends somewhat on knowing the format of the output of the bash
# `builtin type' command.
#
whence()
{
local vflag pflag fflag defarg c
local path
vflag= aflag= pflag= fflag=
path=
if [ "$#" = "0" ] ; then
echo "whence: usage: whence [-afpv] name..." >&2
return 2
fi
OPTIND=1
while getopts "avfp" c
do
case "$c" in
a) defarg=-a ;;
f) fflag=1 ;; # no-op
p) pflag=1 ;;
v) vflag=1 ;;
?) echo "whence: $1: unknown option" >&2
echo "whence: usage: whence [-afpv] name..." >&2
return 2 ;;
esac
done
shift $(( $OPTIND - 1 ))
if [ "$#" = "0" ] ; then
echo "whence: usage: whence [-afpv] name..." >&2
return 2
fi
for cmd
do
if [ "$vflag" ] ; then
if [ -z "$defarg" ]; then
builtin type $cmd | sed 1q
else
if builtin type $defarg -t $cmd | grep 'function$' >/dev/null 2>&1; then
# HAIRY awk script to suppress
# printing of function body -- could
# do it with sed, but I don't have
# that kind of time
builtin type $defarg $cmd | awk '
BEGIN {printit = 1;}
$1 == "'$cmd'" && $2 == "()" {printit=0; next; }
/^}$/ { if (printit == 0) printit=1 ; else print $0; next ; }
/.*/ { if (printit) print $0; }'
else
builtin type $defarg $cmd
fi
fi
else
path=$(builtin type $defarg -p $cmd)
if [ "$path" ] ; then
echo $path
else
case "$cmd" in
/*) echo "" ;;
*) case "$(builtin type -t $cmd)" in
"") echo "" ;;
*) echo "$cmd" ;;
esac
;;
esac
fi
fi
done
return 0
}
#
# For real ksh homeboy fanatics, redefine the `type' builtin with a ksh
# version.
#
#type()
#{
# whence -v "$*"
#}
#
# ksh-like `cd': cd [-LP] [dir [change]]
#
cd()
{
OPTIND=1
while getopts "LP" opt
do
case $opt in
L|P) CDOPTS="$CDOPTS -$opt" ;;
*) echo "$FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2
return 2;;
esac
done
shift $(( $OPTIND - 1 ))
case $# in
0) builtin cd $CDOPTS "$HOME" ;;
1) builtin cd $CDOPTS "$@" ;;
2) old="$1" new="$2"
case "$PWD" in
*$old*) ;;
*) echo "${0##*/}: $FUNCNAME: bad substitution" >&2 ; return 1 ;;
esac
dir=${PWD//$old/$new}
builtin cd $CDOPTS "$dir" && echo "$PWD"
;;
*) echo "${0##*/}: $FUNCNAME: usage: $FUNCNAME [-LP] [dir] [change]" >&2
return 2 ;;
esac
}
#
# ksh print emulation
#
# print [-Rnprsu[n]] [-f format] [arg ...]
#
# - end of options
# -R BSD-style -- only accept -n, no escapes
# -n do not add trailing newline
# -p no-op (no coprocesses)
# -r no escapes
# -s print to the history file
# -u n redirect output to fd n
# -f format printf "$format" "$@"
#
print()
{
local eflag=-e
local nflag= fflag= c
local fd=1
OPTIND=1
while getopts "fRnprsu:" c
do
case $c in
R) eflag= ;;
r) eflag= ;;
n) nflag=-n ;;
s) sflag=y ;;
f) fflag=y ;;
u) fd=$OPTARG ;;
p) ;;
esac
done
shift $(( $OPTIND - 1 ))
if [ -n "$fflag" ]; then
builtin printf "$@" >&$fd
return
fi
case "$sflag" in
y) builtin history -s "$*" ;;
*) builtin echo $eflag $nflag "$@" >&$fd
esac
}
# substring function
# this function should be equivalent to the substring built-in which was
# eliminated after the 06/29/84 version
substring ()
{
local lpat flag str #local variables
set -f
case $1 in
-l|-L)
flag=$1
lpat=$2
shift 2
;;
esac
# test for too few or too many arguments
if [ x"$1" = x ] || [ $# -gt 2 ]; then
print -u2 'substring: bad argument count'
return 1
fi
str=$1
if [ x"$flag" = x-l ]; then #substring -l lpat
str=${str#$lpat}
elif [ x"$flag" = x-L ]; then
str=${str##$lpat} #substring -L lpat
fi
if [ x"$2" != x ]; then
echo ${str%$2}
else
echo $str
fi
return 0
}

11
examples/functions/login Normal file
View file

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

View file

@ -0,0 +1,77 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1992 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
trap _notify CHLD
NOTIFY_ALL=false
unset NOTIFY_LIST
unalias false
false()
{
return 1
}
_notify ()
{
local i j
local newlist=
if $NOTIFY_ALL
then
return # let bash take care of this itself
elif [ -z "$NOTIFY_LIST" ]; then
return
else
set -- $NOTIFY_LIST
for i in "$@"
do
j=$(jobs -n %$i)
if [ -n "$j" ]; then
echo "$j"
jobs -n %$i >/dev/null
else
newlist="newlist $i"
fi
done
NOTIFY_LIST="$newlist"
fi
}
notify ()
{
local i j
if [ $# -eq 0 ]; then
NOTIFY_ALL=:
set -b
return
else
for i in "$@"
do
# turn a valid job spec into a job number
j=$(jobs $i)
case "$j" in
[*) j=${j%%]*}
j=${j#[}
NOTIFY_LIST="$NOTIFY_LIST $j"
;;
esac
done
fi
}

48
examples/functions/seq Normal file
View file

@ -0,0 +1,48 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1995 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Generate a sequence from m to n, m defaults to 1.
seq ()
{
declare -i lo hi i # makes local
local _SEQ
case $# in
1) seq 1 "$1" ; return $? ;;
2) lo=$1 hi=$2
i=$lo _SEQ=""
while let "i <= hi"; do
_SEQ="${_SEQ}$i "
let i+=1
done
echo "${_SEQ# }"
return 0 ;;
*) echo seq: usage: seq [low] high 1>&2 ; return 2 ;;
esac
}
# like the APL `iota' function (or at least how I remember it :-)
iota()
{
case $# in
1) seq 1 "$1"; return $?;;
*) echo "iota: usage: iota high" 1>&2; return 2;;
esac
}

56
examples/functions/seq2 Normal file
View file

@ -0,0 +1,56 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1998 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Generate a sequence from m to n, m defaults to 1.
seq ()
{
declare -i lo hi i # makes local
local _SEQ INIT COMPARE STEP
case "$1" in
-r) INIT='i=$hi _SEQ=""' COMPARE='let "i >= $lo"' STEP='let i-=1' ; shift ;;
*) INIT='i=$lo _SEQ=""' COMPARE='let "i <= $hi"' STEP='let i+=1' ;;
esac
case $# in
1) lo=1 hi="$1" ;;
2) lo=$1 hi=$2 ;;
*) echo seq: usage: seq [-r] [low] high 1>&2 ; return 2 ;;
esac
# equivalent to the as-yet-unimplemented
# for (( "$INIT" ; "$COMPARE" ; "$STEP" )); do _SEQ="${_SEQ}$i "; done
eval "$INIT"
while eval "$COMPARE"; do
_SEQ="${_SEQ}$i "
eval "$STEP"
done
echo "${_SEQ# }"
return 0
}
# like the APL `iota' function (or at least how I remember it :-)
iota()
{
case $# in
1) seq 1 "$1"; return $?;;
*) echo "iota: usage: iota high" 1>&2; return 2;;
esac
}

7
examples/functions/shcat Normal file
View file

@ -0,0 +1,7 @@
shcat()
{
while IFS= read -r line
do
echo "$line"
done
}

19
examples/functions/shcat2 Normal file
View file

@ -0,0 +1,19 @@
shcat()
{
while read -r
do
echo "$REPLY"
done
}
shcat2()
{
while [ $# -ge 1 ]; do
case "$1" in
-) shcat ;;
*) shcat < "$1" ;;
esac
shift
done
exit 0
}

View file

@ -0,0 +1,69 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 2001 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Sort the positional parameters.
# 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

97
examples/functions/substr Normal file
View file

@ -0,0 +1,97 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 2002 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# substr -- a function to emulate the ancient ksh builtin
#
#
# -l == shortest from left
# -L == longest from left
# -r == shortest from right (the default)
# -R == longest from right
substr()
{
local flag pat str
local usage="usage: substr -lLrR pat string or substr string pat"
case "$1" in
-l | -L | -r | -R)
flag="$1"
pat="$2"
shift 2
;;
-*)
echo "substr: unknown option: $1"
echo "$usage"
return 1
;;
*)
flag="-r"
pat="$2"
;;
esac
if [ "$#" -eq 0 ] || [ "$#" -gt 2 ] ; then
echo "substr: bad argument count"
return 2
fi
str="$1"
#
# We don't want -f, but we don't want to turn it back on if
# we didn't have it already
#
case "$-" in
"*f*")
;;
*)
fng=1
set -f
;;
esac
case "$flag" in
-l)
str="${str#$pat}" # substr -l pat string
;;
-L)
str="${str##$pat}" # substr -L pat string
;;
-r)
str="${str%$pat}" # substr -r pat string
;;
-R)
str="${str%%$pat}" # substr -R pat string
;;
*)
str="${str%$2}" # substr string pat
;;
esac
echo "$str"
#
# If we had file name generation when we started, re-enable it
#
if [ "$fng" = "1" ] ; then
set +f
fi
}

View file

@ -0,0 +1,99 @@
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 2002 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# substr -- a function to emulate the ancient ksh builtin
#
# -l == remove shortest from left
# -L == remove longest from left
# -r == remove shortest from right (the default)
# -R == remove longest from right
substr()
{
local flag pat str
local usage="usage: substr -lLrR pat string or substr string pat"
local options="l:L:r:R:"
OPTIND=1
while getopts "$options" c
do
case "$c" in
l | L | r | R)
flag="-$c"
pat="$OPTARG"
;;
'?')
echo "$usage"
return 1
;;
esac
done
if [ "$OPTIND" -gt 1 ] ; then
shift $(( $OPTIND -1 ))
fi
if [ "$#" -eq 0 ] || [ "$#" -gt 2 ] ; then
echo "substr: bad argument count"
return 2
fi
str="$1"
#
# We don't want -f, but we don't want to turn it back on if
# we didn't have it already
#
case "$-" in
"*f*")
;;
*)
fng=1
set -f
;;
esac
case "$flag" in
-l)
str="${str#$pat}" # substr -l pat string
;;
-L)
str="${str##$pat}" # substr -L pat string
;;
-r)
str="${str%$pat}" # substr -r pat string
;;
-R)
str="${str%%$pat}" # substr -R pat string
;;
*)
str="${str%$2}" # substr string pat
;;
esac
echo "$str"
#
# If we had file name generation when we started, re-enable it
#
if [ "$fng" = "1" ] ; then
set +f
fi
}

71
examples/functions/whatis Normal file
View file

@ -0,0 +1,71 @@
#
# whatis -- and implementation of the 10th Edition Unix sh builtin `whatis'
# command.
#
# usage: whatis arg [...]
#
# For each argument, whatis prints the associated value as a parameter,
# builtin, function, alias, or executable file as appropriate. In each
# case, the value is printed in a form which would yield the same value
# if typed as input to the shell itself.
#
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1994 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
whatis()
{
local wusage='usage: whatis arg [arg...]'
local fail=0
if [ $# -eq 0 ] ; then
echo "$wusage"
return 1
fi
for arg
do
case $(builtin type -type $arg 2>/dev/null) in
"alias")
builtin alias "$arg"
;;
"function")
builtin type "$arg" | sed 1d
;;
"builtin")
echo builtin "$arg"
;;
"file")
builtin type -path "$arg"
;;
*)
# OK, we could have a variable, or we could have nada
if [ "$(eval echo \${$arg+set})" = "set" ] ; then
# It is a variable, and it is set
echo -n "$arg="
eval echo '\"'\$$arg'\"'
else
echo whatis: $arg: not found
fail=1
fi
;;
esac
done
return $fail
}

78
examples/functions/whence Normal file
View file

@ -0,0 +1,78 @@
#
# An almost-ksh compatible `whence' command. This is as hairy as it is
# because of the desire to exactly mimic ksh.
#
# This depends somewhat on knowing the format of the output of the bash
# `builtin type' command.
#
# Chet Ramey
# chet@ins.CWRU.Edu
#
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1994 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
whence()
{
local vflag= path=
if [ "$#" = "0" ] ; then
echo "whence: argument expected"
return 1
fi
case "$1" in
-v) vflag=1
shift 1
;;
-*) echo "whence: bad option: $1"
return 1
;;
*) ;;
esac
if [ "$#" = "0" ] ; then
echo "whence: bad argument count"
return 1
fi
for cmd
do
if [ "$vflag" ] ; then
echo $(builtin type $cmd | sed 1q)
else
path=$(builtin type -path $cmd)
if [ "$path" ] ; then
echo $path
else
case "$cmd" in
/*) if [ -x "$cmd" ]; then
echo "$cmd"
fi
;;
*) case "$(builtin type -type $cmd)" in
"") ;;
*) echo "$cmd"
;;
esac
;;
esac
fi
fi
done
return 0
}

62
examples/functions/which Normal file
View file

@ -0,0 +1,62 @@
#
# which - emulation of `which' as it appears in FreeBSD
#
# usage: which [-as] command [command...]
#
#
# Chet Ramey <chet.ramey@case.edu>
#
# Copyright 1999 Chester Ramey
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# TThis program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
which()
{
local aflag sflag ES a opt
OPTIND=1
while builtin getopts as opt ; do
case "$opt" in
a) aflag=-a ;;
s) sflag=1 ;;
?) echo "which: usage: which [-as] command [command ...]" >&2
exit 2 ;;
esac
done
(( $OPTIND > 1 )) && shift $(( $OPTIND - 1 ))
# without command arguments, exit with status 1
ES=1
# exit status is 0 if all commands are found, 1 if any are not found
for command; do
# if $command is a function, make sure we add -a so type
# will look in $PATH after finding the function
a=$aflag
case "$(builtin type -t $command)" in
"function") a=-a;;
esac
if [ -n "$sflag" ]; then
builtin type -p $a $command >/dev/null 2>&1
else
builtin type -p $a $command
fi
ES=$?
done
return $ES
}

View file

@ -0,0 +1,335 @@
#
# Simple makefile for the sample loadable builtins
#
# Copyright (C) 1996-2022 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
PACKAGE = @PACKAGE_NAME@
VERSION = @PACKAGE_VERSION@
# Include some boilerplate Gnu makefile definitions.
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
infodir = @infodir@
includedir = @includedir@
datarootdir = @datarootdir@
loadablesdir = @loadablesdir@
headersdir = @headersdir@
topdir = @top_srcdir@
BUILD_DIR = @BUILD_DIR@
srcdir = @srcdir@
VPATH = @srcdir@
# Support an alternate destination root directory for package building
DESTDIR =
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_DATA = @INSTALL_DATA@
INSTALLMODE= -m 0755
@SET_MAKE@
CC = @CC@
RM = rm -f
SHELL = @MAKE_SHELL@
host_os = @host_os@
host_cpu = @host_cpu@
host_vendor = @host_vendor@
STYLE_CFLAGS = @STYLE_CFLAGS@
CFLAGS = @CFLAGS@
LOCAL_CFLAGS = @LOCAL_CFLAGS@
DEFS = @DEFS@
LOCAL_DEFS = @LOCAL_DEFS@
CPPFLAGS = @CPPFLAGS@
BASHINCDIR = ${topdir}/include
SUPPORT_SRC = $(topdir)/support/
LIBBUILD = ${BUILD_DIR}/lib
INTL_LIBSRC = ${topdir}/lib/intl
INTL_BUILDDIR = ${LIBBUILD}/intl
INTL_INC = @INTL_INC@
LIBINTL_H = @LIBINTL_H@
CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) $(CPPFLAGS) $(CFLAGS) $(STYLE_CFLAGS)
#
# These values are generated for configure by ${topdir}/support/shobj-conf.
# If your system is not supported by that script, but includes facilities for
# dynamic loading of shared objects, please update the script and send the
# changes to bash-maintainers@gnu.org.
#
SHOBJ_CC = @SHOBJ_CC@
SHOBJ_CFLAGS = @SHOBJ_CFLAGS@
SHOBJ_LD = @SHOBJ_LD@
SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@ @LDFLAGS@
SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@
SHOBJ_LIBS = @SHOBJ_LIBS@
SHOBJ_STATUS = @SHOBJ_STATUS@
INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \
-I$(BASHINCDIR) -I$(BUILD_DIR) -I$(LIBBUILD) \
-I$(BUILD_DIR)/builtins $(INTL_INC)
.c.o:
$(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CCFLAGS) $(INC) -c -o $@ $<
ALLPROG = print truefalse sleep finfo logname basename dirname fdflags \
tty pathchk tee head mkdir rmdir mkfifo mktemp printenv id whoami \
uname sync push ln unlink realpath strftime mypid setpgid seq rm \
accept csv dsv cut stat getconf
OTHERPROG = necho hello cat pushd asort
all: $(SHOBJ_STATUS)
supported: $(ALLPROG)
others: $(OTHERPROG)
unsupported:
@echo "Your system (${host_os}) is not supported by the"
@echo "${topdir}/support/shobj-conf script."
@echo "If your operating system provides facilities for dynamic"
@echo "loading of shared objects using the dlopen(3) interface,"
@echo "please update the script and re-run configure."
@echo "Please send the changes you made to bash-maintainers@gnu.org"
@echo "for inclusion in future bash releases."
everything: supported others
print: print.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ print.o $(SHOBJ_LIBS)
necho: necho.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ necho.o $(SHOBJ_LIBS)
hello: hello.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ hello.o $(SHOBJ_LIBS)
truefalse: truefalse.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ truefalse.o $(SHOBJ_LIBS)
accept: accept.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ accept.o $(SHOBJ_LIBS)
sleep: sleep.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sleep.o $(SHOBJ_LIBS)
finfo: finfo.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ finfo.o $(SHOBJ_LIBS)
cat: cat.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cat.o $(SHOBJ_LIBS)
rm: rm.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rm.o $(SHOBJ_LIBS)
fdflags: fdflags.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ fdflags.o $(SHOBJ_LIBS)
seq: seq.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ seq.o $(SHOBJ_LIBS)
logname: logname.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ logname.o $(SHOBJ_LIBS)
basename: basename.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ basename.o $(SHOBJ_LIBS)
dirname: dirname.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ dirname.o $(SHOBJ_LIBS)
tty: tty.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tty.o $(SHOBJ_LIBS)
pathchk: pathchk.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pathchk.o $(SHOBJ_LIBS)
tee: tee.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ tee.o $(SHOBJ_LIBS)
mkdir: mkdir.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mkdir.o $(SHOBJ_LIBS)
rmdir: rmdir.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ rmdir.o $(SHOBJ_LIBS)
mkfifo: mkfifo.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mkfifo.o $(SHOBJ_LIBS)
mktemp: mktemp.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mktemp.o $(SHOBJ_LIBS)
head: head.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ head.o $(SHOBJ_LIBS)
printenv: printenv.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ printenv.o $(SHOBJ_LIBS)
getconf: getconf.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ getconf.o $(SHOBJ_LIBS)
id: id.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ id.o $(SHOBJ_LIBS)
whoami: whoami.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ whoami.o $(SHOBJ_LIBS)
uname: uname.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ uname.o $(SHOBJ_LIBS)
sync: sync.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ sync.o $(SHOBJ_LIBS)
push: push.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ push.o $(SHOBJ_LIBS)
ln: ln.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ ln.o $(SHOBJ_LIBS)
unlink: unlink.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ unlink.o $(SHOBJ_LIBS)
realpath: realpath.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ realpath.o $(SHOBJ_LIBS)
csv: csv.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ csv.o $(SHOBJ_LIBS)
dsv: dsv.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ dsv.o $(SHOBJ_LIBS)
cut: cut.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ cut.o $(SHOBJ_LIBS)
strftime: strftime.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ strftime.o $(SHOBJ_LIBS)
mypid: mypid.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ mypid.o $(SHOBJ_LIBS)
setpgid: setpgid.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ setpgid.o $(SHOBJ_LIBS)
stat: stat.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ stat.o $(SHOBJ_LIBS)
asort: asort.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ asort.o $(SHOBJ_LIBS)
# pushd is a special case. We use the same source that the builtin version
# uses, with special compilation options.
#
pushd.c: ${topdir}/builtins/pushd.def
$(RM) $@
${BUILD_DIR}/builtins/mkbuiltins -D ${topdir}/builtins ${topdir}/builtins/pushd.def
pushd.o: pushd.c
$(RM) $@
$(SHOBJ_CC) -Wno-format-security -DHAVE_CONFIG_H -DPUSHD_AND_POPD -DLOADABLE_BUILTIN $(SHOBJ_CFLAGS) $(CFLAGS) $(CPPFLAGS) $(INC) -c -o $@ $<
pushd: pushd.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ pushd.o $(SHOBJ_LIBS)
clean:
$(RM) $(ALLPROG) $(OTHERPROG) *.o
-( cd perl && ${MAKE} ${MFLAGS} $@ )
mostlyclean: clean
-( cd perl && ${MAKE} ${MFLAGS} $@ )
distclean maintainer-clean: clean
$(RM) Makefile Makefile.inc Makefile.sample pushd.c
-( cd perl && ${MAKE} ${MFLAGS} $@ )
installdirs:
@${SHELL} $(SUPPORT_SRC)mkinstalldirs $(DESTDIR)$(loadablesdir)
install-dev: installdirs
@$(INSTALL_DATA) Makefile.inc $(DESTDIR)$(loadablesdir)/Makefile.inc
@$(INSTALL_DATA) Makefile.sample $(DESTDIR)$(loadablesdir)/Makefile.sample
@$(INSTALL_DATA) $(srcdir)/loadables.h $(DESTDIR)$(loadablesdir)/loadables.h
@( cd $(BUILD_DIR) && ${MAKE} ${MFLAGS} DESTDIR="$(DESTDIR)" install-headers)
install-supported: all installdirs install-dev
@echo installing example loadable builtins in $(DESTDIR)${loadablesdir}
@for prog in ${ALLPROG}; do \
echo $$prog ; \
$(INSTALL_PROGRAM) $(INSTALLMODE) $$prog $(DESTDIR)$(loadablesdir)/$$prog ;\
done
uninstall-dev:
-$(RM) $(DESTDIR)$(loadablesdir)/Makefile.inc $(DESTDIR)$(loadablesdir)/Makefile.sample
-$(RM) $(DESTDIR)$(loadablesdir)/loadables.h
-( cd $(BUILD_DIR) && ${MAKE} ${MFLAGS} DESTDIR="$(DESTDIR)" uninstall-headers)
uninstall-supported: uninstall-dev
-( cd $(DESTDIR)${loadablesdir} && $(RM) ${ALLPROG} )
install-unsupported:
uninstall-unsupported:
install: install-$(SHOBJ_STATUS)
uninstall: uninstall-$(SHOBJ_STATUS)
print.o: print.c
truefalse.o: truefalse.c
accept.o: accept.c
sleep.o: sleep.c
finfo.o: finfo.c
getconf.o: getconf.c getconf.h
logname.o: logname.c
basename.o: basename.c
dirname.o: dirname.c
tty.o: tty.c
pathchk.o: pathchk.c
tee.o: tee.c
head.o: head.c
rmdir.o: rmdir.c
necho.o: necho.c
hello.o: hello.c
cat.o: cat.c
csv.o: csv.c
dsv.o: dsv.c
cut.o: cut.c
printenv.o: printenv.c
id.o: id.c
whoami.o: whoami.c
uname.o: uname.c
sync.o: sync.c
push.o: push.c
mkdir.o: mkdir.c
mktemp.o: mktemp.c
realpath.o: realpath.c
strftime.o: strftime.c
setpgid.o: setpgid.c
stat.o: stat.c
fdflags.o: fdflags.c
seq.o: seq.c
asort.o: asort.c

View file

@ -0,0 +1,101 @@
#
# Sample makefile for bash loadable builtin development
#
# Copyright (C) 2015-2022 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
PACKAGE = @PACKAGE_NAME@
VERSION = @PACKAGE_VERSION@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
# Include some boilerplate Gnu makefile definitions.
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
infodir = @infodir@
includedir = @includedir@
datarootdir = @datarootdir@
loadablesdir = @loadablesdir@
headersdir = @headersdir@
topdir = @top_srcdir@
BUILD_DIR = @BUILD_DIR@
srcdir = @srcdir@
VPATH = @srcdir@
# Support an alternate destination root directory for package building
DESTDIR =
INSTALL = @INSTALL@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_DATA = @INSTALL_DATA@
INSTALLMODE= -m 0755
@SET_MAKE@
CC = @CC@
RM = rm -f
SHELL = @MAKE_SHELL@
host_os = @host_os@
host_cpu = @host_cpu@
host_vendor = @host_vendor@
CFLAGS = @CFLAGS@
LOCAL_CFLAGS = @LOCAL_CFLAGS@
DEFS = @DEFS@
LOCAL_DEFS = @LOCAL_DEFS@
CPPFLAGS = @CPPFLAGS@
BASHINCDIR = ${topdir}/include
SUPPORT_SRC = $(topdir)/support/
LIBBUILD = ${BUILD_DIR}/lib
INTL_LIBSRC = ${topdir}/lib/intl
INTL_BUILDDIR = ${LIBBUILD}/intl
INTL_INC = @INTL_INC@
LIBINTL_H = @LIBINTL_H@
CCFLAGS = $(DEFS) $(LOCAL_DEFS) $(LOCAL_CFLAGS) $(CFLAGS)
#
# These values are generated for configure by ${topdir}/support/shobj-conf.
# If your system is not supported by that script, but includes facilities for
# dynamic loading of shared objects, please update the script and send the
# changes to bash-maintainers@gnu.org.
#
SHOBJ_CC = @SHOBJ_CC@
SHOBJ_CFLAGS = @SHOBJ_CFLAGS@
SHOBJ_LD = @SHOBJ_LD@
SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@ @LDFLAGS@
SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@
SHOBJ_LIBS = @SHOBJ_LIBS@
SHOBJ_STATUS = @SHOBJ_STATUS@
INC = -I$(headersdir) -I$(headersdir)/include -I$(headersdir)/builtins
.c.o:
$(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CCFLAGS) $(INC) -c -o $@ $<

View file

@ -0,0 +1,44 @@
#
# Sample makefile for bash loadable builtin development
#
# Copyright (C) 2022 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# these should match the ones in Makefile.in (for the make install target)
prefix = @prefix@
exec_prefix = @exec_prefix@
libdir = @libdir@
# ${loadablesdir} is where the example loadable builtins and data files
# are installed (make install target in Makefile.in)
loadablesdir = @loadablesdir@
DESTDIR =
# include Makefile.inc for all boilerplate definitions
include $(DESTDIR)$(loadablesdir)/Makefile.inc
# here, `example' is the name of the shared object
# replace `example' with the appropriate filename
all: example
example: example.o
$(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ example.o $(SHOBJ_LIBS)
example.o: example.c

82
examples/loadables/README Normal file
View file

@ -0,0 +1,82 @@
Some examples of ready-to-dynamic-load builtins. Most of the
examples given are reimplementations of standard commands whose
execution time is dominated by process startup time. Some
exceptions are sleep, which allows you to sleep for fractions
of a second, finfo, which provides access to the rest of the
elements of the `stat' structure that `test' doesn't let you
see, csv, which allows you to manipulate data from comma-separated
values files, fdflags, which lets you change the flags associated
with one of the shell's file descriptors, and pushd/popd/dirs, which
allows you to compile them out of the shell.
All of the new builtins in ksh93 that bash didn't already have
are included here, as is the ksh `print' builtin.
The configure script in the top-level source directory uses the
support/shobj-conf script to set the right values in the Makefile,
so you should not need to change the Makefile. If your system
is not supported by support/shobj-conf, and it has the necessary
facilities for building shared objects and support for the
dlopen/dlsyn/dlclose/dlerror family of functions, please make
the necessary changes to support/shobj-conf and send the changes
to bash-maintainers@gnu.org.
Loadable builtins are loaded into a running shell with
enable -f filename builtin-name
enable uses a simple reference-counting scheme to avoid unloading a
shared object that implements more than one loadable builtin before
all loadable builtins implemented in the object are removed.
Many of the details needed by builtin writers are found in hello.c,
the canonical example. There is no real `builtin writers' programming
guide'. The file template.c provides a template to use for creating
new loadable builtins.
The file "Makefile.inc" is created using the same values that configure
writes into Makefile.in, and is installed in the same directory as the
rest of the example builtins. It's intended to be a start at something
that can be modified or included to help you build your own loadables
without having to search for the right CFLAGS and LDFLAGS.
basename.c Return non-directory portion of pathname.
cat.c cat(1) replacement with no options - the way cat was intended.
csv.c Process a line of csv data and store it in an indexed array.
cut.c Cut out selected portions of each line of a file.
dirname.c Return directory portion of pathname.
fdflags.c Change the flag associated with one of bash's open file descriptors.
finfo.c Print file info.
head.c Copy first part of files.
hello.c Obligatory "Hello World" / sample loadable.
id.c POSIX.2 user identity.
ln.c Make links.
loadables.h File loadable builtins can include for shell definitions.
logname.c Print login name of current user.
Makefile.in Simple makefile for the sample loadable builtins.
Makefile.inc.in Sample makefile to use for loadable builtin development.
mkdir.c Make directories.
mkfifo.c Create named pipes.
mktemp.c Make unique temporary file name.
mypid.c Add $MYPID variable, demonstrate use of unload hook function.
necho.c echo without options or argument interpretation.
pathchk.c Check pathnames for validity and portability.
print.c Loadable ksh-93 style print builtin.
printenv.c Minimal builtin clone of BSD printenv(1).
push.c Anyone remember TOPS-20?
realpath.c Canonicalize pathnames, resolving symlinks.
rm.c Remove files and directories.
rmdir.c Remove directory.
seq.c Print a sequence of decimal or floating point numbers.
setpgid.c Set a process's pgrp; example of how to wrap a system call.
sleep.c sleep for fractions of a second.
stat.c populate an associative array with information about a file
strftime.c Loadable builtin interface to strftime(3).
sync.c Sync the disks by forcing pending filesystem writes to complete.
tee.c Duplicate standard input.
template.c Example template for loadable builtin.
truefalse.c True and false builtins.
tty.c Return terminal name.
uname.c Print system information.
unlink.c Remove a directory entry.
whoami.c Print out username of current user.

245
examples/loadables/accept.c Normal file
View file

@ -0,0 +1,245 @@
/* accept - listen for and accept a remote network connection on a given port */
/*
Copyright (C) 2020 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "bashtypes.h"
#include <errno.h>
#include <time.h>
#include <limits.h>
#include "typemax.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "loadables.h"
static int accept_bind_variable (char *, int);
int
accept_builtin (list)
WORD_LIST *list;
{
SHELL_VAR *v;
intmax_t iport;
int opt;
char *tmoutarg, *fdvar, *rhostvar, *rhost, *bindaddr;
unsigned short uport;
int servsock, clisock;
struct sockaddr_in server, client;
socklen_t clientlen;
struct timeval timeval;
struct linger linger = { 0, 0 };
rhostvar = tmoutarg = fdvar = rhost = bindaddr = (char *)NULL;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "b:r:t:v:")) != -1)
{
switch (opt)
{
case 'b':
bindaddr = list_optarg;
break;
case 'r':
rhostvar = list_optarg;
break;
case 't':
tmoutarg = list_optarg;
break;
case 'v':
fdvar = list_optarg;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
/* Validate input and variables */
if (tmoutarg)
{
long ival, uval;
opt = uconvert (tmoutarg, &ival, &uval, (char **)0);
if (opt == 0 || ival < 0 || uval < 0)
{
builtin_error ("%s: invalid timeout specification", tmoutarg);
return (EXECUTION_FAILURE);
}
timeval.tv_sec = ival;
timeval.tv_usec = uval;
/* XXX - should we warn if ival == uval == 0 ? */
}
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
if (legal_number (list->word->word, &iport) == 0 || iport < 0 || iport > TYPE_MAXIMUM (unsigned short))
{
builtin_error ("%s: invalid port number", list->word->word);
return (EXECUTION_FAILURE);
}
uport = (unsigned short)iport;
if (fdvar == 0)
fdvar = "ACCEPT_FD";
unbind_variable (fdvar);
if (rhostvar)
unbind_variable (rhostvar);
if ((servsock = socket (AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
{
builtin_error ("cannot create socket: %s", strerror (errno));
return (EXECUTION_FAILURE);
}
memset ((char *)&server, 0, sizeof (server));
server.sin_family = AF_INET;
server.sin_port = htons(uport);
server.sin_addr.s_addr = bindaddr ? inet_addr (bindaddr) : htonl(INADDR_ANY);
if (server.sin_addr.s_addr == INADDR_NONE)
{
builtin_error ("invalid address: %s", strerror (errno));
return (EXECUTION_FAILURE);
}
opt = 1;
setsockopt (servsock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof (opt));
setsockopt (servsock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof (linger));
if (bind (servsock, (struct sockaddr *)&server, sizeof (server)) < 0)
{
builtin_error ("socket bind failure: %s", strerror (errno));
close (servsock);
return (EXECUTION_FAILURE);
}
if (listen (servsock, 1) < 0)
{
builtin_error ("listen failure: %s", strerror (errno));
close (servsock);
return (EXECUTION_FAILURE);
}
if (tmoutarg)
{
fd_set iofds;
FD_ZERO(&iofds);
FD_SET(servsock, &iofds);
opt = select (servsock+1, &iofds, 0, 0, &timeval);
if (opt < 0)
builtin_error ("select failure: %s", strerror (errno));
if (opt <= 0)
{
close (servsock);
return (EXECUTION_FAILURE);
}
}
clientlen = sizeof (client);
if ((clisock = accept (servsock, (struct sockaddr *)&client, &clientlen)) < 0)
{
builtin_error ("client accept failure: %s", strerror (errno));
close (servsock);
return (EXECUTION_FAILURE);
}
close (servsock);
accept_bind_variable (fdvar, clisock);
if (rhostvar)
{
rhost = inet_ntoa (client.sin_addr);
v = builtin_bind_variable (rhostvar, rhost, 0);
if (v == 0 || readonly_p (v) || noassign_p (v))
builtin_error ("%s: cannot set variable", rhostvar);
}
return (EXECUTION_SUCCESS);
}
static int
accept_bind_variable (varname, intval)
char *varname;
int intval;
{
SHELL_VAR *v;
char ibuf[INT_STRLEN_BOUND (int) + 1], *p;
p = fmtulong (intval, 10, ibuf, sizeof (ibuf), 0);
v = builtin_bind_variable (varname, p, 0); /* XXX */
if (v == 0 || readonly_p (v) || noassign_p (v))
builtin_error ("%s: cannot set variable", varname);
return (v != 0);
}
char *accept_doc[] = {
"Accept a network connection on a specified port.",
""
"This builtin allows a bash script to act as a TCP/IP server.",
"",
"Options, if supplied, have the following meanings:",
" -b address use ADDRESS as the IP address to listen on; the",
" default is INADDR_ANY",
" -t timeout wait TIMEOUT seconds for a connection. TIMEOUT may",
" be a decimal number including a fractional portion",
" -v varname store the numeric file descriptor of the connected",
" socket into VARNAME. The default VARNAME is ACCEPT_FD",
" -r rhost store the IP address of the remote host into the shell",
" variable RHOST, in dotted-decimal notation",
"",
"If successful, the shell variable ACCEPT_FD, or the variable named by the",
"-v option, will be set to the fd of the connected socket, suitable for",
"use as 'read -u$ACCEPT_FD'. RHOST, if supplied, will hold the IP address",
"of the remote client. The return status is 0.",
"",
"On failure, the return status is 1 and ACCEPT_FD (or VARNAME) and RHOST,",
"if supplied, will be unset.",
"",
"The server socket fd will be closed before accept returns.",
(char *) NULL
};
struct builtin accept_struct = {
"accept", /* builtin name */
accept_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
accept_doc, /* array of long documentation strings. */
"accept [-b address] [-t timeout] [-v varname] [-r addrvar ] port", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

279
examples/loadables/asort.c Normal file
View file

@ -0,0 +1,279 @@
/*
Copyright (C) 2020 Free Software Foundation, Inc.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "bashtypes.h"
#include "shell.h"
#include "builtins.h"
#include "common.h"
#include "xmalloc.h"
#include "bashgetopt.h"
typedef struct sort_element {
ARRAY_ELEMENT *v; // used when sorting array in-place
char *key; // used when sorting assoc array
char *value; // points to value of array element or assoc entry
double num; // used for numeric sort
} sort_element;
static int reverse_flag;
static int numeric_flag;
static int
compare(const void *p1, const void *p2) {
const sort_element e1 = *(sort_element *) p1;
const sort_element e2 = *(sort_element *) p2;
if (numeric_flag) {
if (reverse_flag)
return (e2.num > e1.num) ? 1 : (e2.num < e1.num) ? -1 : 0;
else
return (e1.num > e2.num) ? 1 : (e1.num < e2.num) ? -1 : 0;
}
else {
if (reverse_flag)
return strcoll(e2.value, e1.value);
else
return strcoll(e1.value, e2.value);
}
}
static int
sort_index(SHELL_VAR *dest, SHELL_VAR *source) {
HASH_TABLE *hash;
BUCKET_CONTENTS *bucket;
sort_element *sa;
ARRAY *array, *dest_array;
ARRAY_ELEMENT *ae;
size_t i, j, n;
char ibuf[INT_STRLEN_BOUND (intmax_t) + 1]; // used by fmtulong
char *key;
dest_array = array_cell(dest);
if (assoc_p(source)) {
hash = assoc_cell(source);
n = hash->nentries;
sa = xmalloc(n * sizeof(sort_element));
i = 0;
for ( j = 0; j < hash->nbuckets; ++j ) {
bucket = hash->bucket_array[j];
while ( bucket ) {
sa[i].v = NULL;
sa[i].key = bucket->key;
if ( numeric_flag )
sa[i].num = strtod(bucket->data, NULL);
else
sa[i].value = bucket->data;
i++;
bucket = bucket->next;
}
}
}
else {
array = array_cell(source);
n = array_num_elements(array);
sa = xmalloc(n * sizeof(sort_element));
i = 0;
for (ae = element_forw(array->head); ae != array->head; ae = element_forw(ae)) {
sa[i].v = ae;
if (numeric_flag)
sa[i].num = strtod(element_value(ae), NULL);
else
sa[i].value = element_value(ae);
i++;
}
}
// sanity check
if ( i != n ) {
builtin_error("%s: corrupt array", source->name);
return EXECUTION_FAILURE;
}
qsort(sa, n, sizeof(sort_element), compare);
array_flush(dest_array);
for ( i = 0; i < n; ++i ) {
if ( assoc_p(source) )
key = sa[i].key;
else
key = fmtulong((long unsigned)sa[i].v->ind, 10, ibuf, sizeof(ibuf), 0);
array_insert(dest_array, i, key);
}
return EXECUTION_SUCCESS;
}
static int
sort_inplace(SHELL_VAR *var) {
size_t i, n;
ARRAY *a;
ARRAY_ELEMENT *ae;
sort_element *sa = 0;
a = array_cell(var);
n = array_num_elements(a);
if ( n == 0 )
return EXECUTION_SUCCESS;
sa = xmalloc(n * sizeof(sort_element));
i = 0;
for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
sa[i].v = ae;
if (numeric_flag)
sa[i].num = strtod(element_value(ae), NULL);
else
sa[i].value = element_value(ae);
i++;
}
// sanity check
if ( i != n ) {
builtin_error("%s: corrupt array", var->name);
return EXECUTION_FAILURE;
}
qsort(sa, n, sizeof(sort_element), compare);
// for in-place sort, simply "rewire" the array elements
sa[0].v->prev = sa[n-1].v->next = a->head;
a->head->next = sa[0].v;
a->head->prev = sa[n-1].v;
a->max_index = n - 1;
for (i = 0; i < n; i++) {
sa[i].v->ind = i;
if (i > 0)
sa[i].v->prev = sa[i-1].v;
if (i < n - 1)
sa[i].v->next = sa[i+1].v;
}
xfree(sa);
return EXECUTION_SUCCESS;
}
int
asort_builtin(WORD_LIST *list) {
SHELL_VAR *var, *var2;
char *word;
int opt, ret;
int index_flag = 0;
numeric_flag = 0;
reverse_flag = 0;
reset_internal_getopt();
while ((opt = internal_getopt(list, "inr")) != -1) {
switch (opt) {
case 'i': index_flag = 1; break;
case 'n': numeric_flag = 1; break;
case 'r': reverse_flag = 1; break;
CASE_HELPOPT;
default:
builtin_usage();
return (EX_USAGE);
}
}
list = loptend;
if (list == 0) {
builtin_usage();
return EX_USAGE;
}
if (legal_identifier (list->word->word) == 0) {
sh_invalidid (list->word->word);
return EXECUTION_FAILURE;
}
if ( index_flag ) {
if ( list->next == 0 || list->next->next ) {
builtin_usage();
return EX_USAGE;
}
if (legal_identifier (list->next->word->word) == 0) {
sh_invalidid (list->next->word->word);
return EXECUTION_FAILURE;
}
var = find_or_make_array_variable(list->word->word, 1);
if (var == 0)
return EXECUTION_FAILURE;
var2 = find_variable(list->next->word->word);
if ( !var2 || ( !array_p(var2) && !assoc_p(var2) ) ) {
builtin_error("%s: Not an array", list->next->word->word);
return EXECUTION_FAILURE;
}
return sort_index(var, var2);
}
while (list) {
word = list->word->word;
var = find_variable(word);
list = list->next;
if (var == 0 || array_p(var) == 0) {
builtin_error("%s: Not an array", word);
continue;
}
if (readonly_p(var) || noassign_p(var)) {
if (readonly_p(var))
err_readonly(word);
continue;
}
if ( (ret = sort_inplace(var)) != EXECUTION_SUCCESS )
return ret;
}
return EXECUTION_SUCCESS;
}
char *asort_doc[] = {
"Sort arrays in-place.",
"",
"Options:",
" -n compare according to string numerical value",
" -r reverse the result of comparisons",
" -i sort using indices/keys",
"",
"If -i is supplied, SOURCE is not sorted in-place, but the indices (or keys",
"if associative) of SOURCE, after sorting it by its values, are placed as",
"values in the indexed array DEST",
"",
"Associative arrays may not be sorted in-place.",
"",
"Exit status:",
"Return value is zero unless an error happened (like invalid variable name",
"or readonly array).",
(char *)NULL
};
struct builtin asort_struct = {
"asort",
asort_builtin,
BUILTIN_ENABLED,
asort_doc,
"asort [-nr] array ... or asort [-nr] -i dest source",
0
};

View file

@ -0,0 +1,131 @@
/* basename - return nondirectory portion of pathname */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2020 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#include "bashgetopt.h"
int
basename_builtin (list)
WORD_LIST *list;
{
int slen, sufflen, off;
char *string, *suffix, *fn;
if (no_options (list))
return (EX_USAGE);
list = loptend;
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
string = list->word->word;
suffix = (char *)NULL;
if (list->next)
{
list = list->next;
suffix = list->word->word;
}
if (list->next)
{
builtin_usage ();
return (EX_USAGE);
}
slen = strlen (string);
/* Strip trailing slashes */
while (slen > 0 && string[slen - 1] == '/')
slen--;
/* (2) If string consists entirely of slash characters, string shall be
set to a single slash character. In this case, skip steps (3)
through (5). */
if (slen == 0)
{
fputs ("/\n", stdout);
return (EXECUTION_SUCCESS);
}
/* (3) If there are any trailing slash characters in string, they
shall be removed. */
string[slen] = '\0';
/* (4) If there are any slash characters remaining in string, the prefix
of string up to an including the last slash character in string
shall be removed. */
while (--slen >= 0)
if (string[slen] == '/')
break;
fn = string + slen + 1;
/* (5) If the suffix operand is present, is not identical to the
characters remaining in string, and is identical to a suffix
of the characters remaining in string, the suffix suffix
shall be removed from string. Otherwise, string shall not be
modified by this step. */
if (suffix)
{
sufflen = strlen (suffix);
slen = strlen (fn);
if (sufflen < slen)
{
off = slen - sufflen;
if (strcmp (fn + off, suffix) == 0)
fn[off] = '\0';
}
}
printf ("%s\n", fn);
return (EXECUTION_SUCCESS);
}
char *basename_doc[] = {
"Return non-directory portion of pathname.",
"",
"The STRING is converted to a filename corresponding to the last",
"pathname component in STRING. If the suffix string SUFFIX is",
"supplied, it is removed.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. */
struct builtin basename_struct = {
"basename", /* builtin name */
basename_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
basename_doc, /* array of long documentation strings. */
"basename string [suffix]", /* usage synopsis */
0 /* reserved for internal use */
};

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

@ -0,0 +1,138 @@
/*
* cat replacement
*
* no options - the way cat was intended
*/
/*
Copyright (C) 1999-2009,2022 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fcntl.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#ifndef errno
extern int errno;
#endif
extern char *strerror ();
extern char **make_builtin_argv ();
static int
fcopy(fd, fn)
int fd;
char *fn;
{
char buf[4096], *s;
int n, w, e;
while (n = read(fd, buf, sizeof (buf))) {
if (n < 0) {
e = errno;
write(2, "cat: read error: ", 18);
write(2, fn, strlen(fn));
write(2, ": ", 2);
s = strerror(e);
write(2, s, strlen(s));
write(2, "\n", 1);
return 1;
}
QUIT;
w = write(1, buf, n);
if (w != n) {
e = errno;
write(2, "cat: write error: ", 18);
s = strerror(e);
write(2, s, strlen(s));
write(2, "\n", 1);
return 1;
}
QUIT;
}
return 0;
}
int
cat_main (argc, argv)
int argc;
char **argv;
{
int i, fd, r;
char *s;
if (argc == 1)
return (fcopy(0, "standard input"));
for (i = r = 1; i < argc; i++) {
QUIT;
if (argv[i][0] == '-' && argv[i][1] == '\0')
fd = 0;
else {
fd = open(argv[i], O_RDONLY, 0666);
if (fd < 0) {
s = strerror(errno);
write(2, "cat: cannot open ", 17);
write(2, argv[i], strlen(argv[i]));
write(2, ": ", 2);
write(2, s, strlen(s));
write(2, "\n", 1);
continue;
}
}
r = fcopy(fd, argv[i]);
if (fd != 0)
close(fd);
}
QUIT;
return (r);
}
int
cat_builtin(list)
WORD_LIST *list;
{
char **v;
int c, r;
v = make_builtin_argv(list, &c);
QUIT;
r = cat_main(c, v);
free(v);
return r;
}
char *cat_doc[] = {
"Display files.",
"",
"Read each FILE and display it on the standard output. If any",
"FILE is `-' or if no FILE argument is given, the standard input",
"is read.",
(char *)0
};
struct builtin cat_struct = {
"cat",
cat_builtin,
BUILTIN_ENABLED,
cat_doc,
"cat [-] [file ...]",
0
};

206
examples/loadables/csv.c Normal file
View file

@ -0,0 +1,206 @@
/* csv - process a line of csv data and populate an indexed array with the
fields */
/*
Copyright (C) 2020 Free Software Foundation, Inc.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
/* See Makefile for compilation details. */
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include <stdio.h>
#include "loadables.h"
#define CSV_ARRAY_DEFAULT "CSV"
#define NQUOTE 0
#define DQUOTE 1
/* Split LINE into comma-separated fields, storing each field into a separate
element of array variable CSV, starting at index 0. The format of LINE is
as described in RFC 4180. */
static int
csvsplit (csv, line, dstring)
SHELL_VAR *csv;
char *line, *dstring;
{
arrayind_t ind;
char *field, *prev, *buf, *xbuf;
int delim, qstate;
int b, rval;
xbuf = 0;
ind = 0;
field = prev = line;
do
{
if (*prev == '"')
{
if (xbuf == 0)
xbuf = xmalloc (strlen (prev) + 1);
buf = xbuf;
b = 0;
qstate = DQUOTE;
for (field = ++prev; *field; field++)
{
if (qstate == DQUOTE && *field == '"' && field[1] == '"')
buf[b++] = *field++; /* skip double quote */
else if (qstate == DQUOTE && *field == '"')
qstate = NQUOTE;
else if (qstate == NQUOTE && *field == *dstring)
break;
else
/* This copies any text between a closing double quote and the
delimiter. If you want to change that, make sure to do the
copy only if qstate == DQUOTE. */
buf[b++] = *field;
}
buf[b] = '\0';
}
else
{
buf = prev;
field = prev + strcspn (prev, dstring);
}
delim = *field;
*field = '\0';
bind_array_element (csv, ind, buf, 0);
ind++;
*field = delim;
if (delim == *dstring)
prev = field + 1;
}
while (delim == *dstring);
if (xbuf)
free (xbuf);
return (rval = ind); /* number of fields */
}
int
csv_builtin (list)
WORD_LIST *list;
{
int opt, rval;
char *array_name, *csvstring;
SHELL_VAR *v;
array_name = 0;
rval = EXECUTION_SUCCESS;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "a:")) != -1)
{
switch (opt)
{
case 'a':
array_name = list_optarg;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (array_name == 0)
array_name = CSV_ARRAY_DEFAULT;
if (legal_identifier (array_name) == 0)
{
sh_invalidid (array_name);
return (EXECUTION_FAILURE);
}
if (list == 0)
{
builtin_error ("csv string argument required");
return (EX_USAGE);
}
v = find_or_make_array_variable (array_name, 1);
if (v == 0 || readonly_p (v) || noassign_p (v))
{
if (v && readonly_p (v))
err_readonly (array_name);
return (EXECUTION_FAILURE);
}
else if (array_p (v) == 0)
{
builtin_error ("%s: not an indexed array", array_name);
return (EXECUTION_FAILURE);
}
if (invisible_p (v))
VUNSETATTR (v, att_invisible);
array_flush (array_cell (v));
csvstring = list->word->word;
if (csvstring == 0 || *csvstring == 0)
return (EXECUTION_SUCCESS);
opt = csvsplit (v, csvstring, ",");
/* Maybe do something with OPT here, it's the number of fields */
return (rval);
}
/* Called when builtin is enabled and loaded from the shared object. If this
function returns 0, the load fails. */
int
csv_builtin_load (name)
char *name;
{
return (1);
}
/* Called when builtin is disabled. */
void
csv_builtin_unload (name)
char *name;
{
}
char *csv_doc[] = {
"Read comma-separated fields from a string.",
"",
"Parse STRING, a line of comma-separated values, into individual fields,",
"and store them into the indexed array ARRAYNAME starting at index 0.",
"If ARRAYNAME is not supplied, \"CSV\" is the default array name.",
(char *)NULL
};
struct builtin csv_struct = {
"csv", /* builtin name */
csv_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
csv_doc, /* array of long documentation strings. */
"csv [-a ARRAY] string", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

631
examples/loadables/cut.c Normal file
View file

@ -0,0 +1,631 @@
/* cut,lcut - extract specified fields from a line and assign them to an array
or print them to the standard output */
/*
Copyright (C) 2020 Free Software Foundation, Inc.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
/* See Makefile for compilation details. */
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include "loadables.h"
#include "shmbutil.h"
#define CUT_ARRAY_DEFAULT "CUTFIELDS"
#define NOPOS -2 /* sentinel for unset startpos/endpos */
#define BOL 0
#define EOL INT_MAX
#define NORANGE -1 /* just a position, no range */
#define BFLAG (1 << 0)
#define CFLAG (1 << 1)
#define DFLAG (1 << 2)
#define FFLAG (1 << 3)
#define SFLAG (1 << 4)
struct cutpos
{
int startpos, endpos; /* zero-based, correction done in getlist() */
};
struct cutop
{
int flags;
int delim;
int npos;
struct cutpos *poslist;
};
static int
poscmp (a, b)
void *a, *b;
{
struct cutpos *p1, *p2;
p1 = (struct cutpos *)a;
p2 = (struct cutpos *)b;
return (p1->startpos - p2->startpos);
}
static int
getlist (arg, opp)
char *arg;
struct cutpos **opp;
{
char *ntok, *ltok, *larg;
int s, e;
intmax_t num;
struct cutpos *poslist;
int npos, nsize;
poslist = 0;
nsize = npos = 0;
s = e = 0;
larg = arg;
while (ltok = strsep (&larg, ","))
{
if (*ltok == 0)
continue;
ntok = strsep (&ltok, "-");
if (*ntok == 0)
s = BOL;
else
{
if (legal_number (ntok, &num) == 0 || (int)num != num || num <= 0)
{
builtin_error ("%s: invalid list value", ntok);
*opp = poslist;
return -1;
}
s = num;
s--; /* fields are 1-based */
}
if (ltok == 0)
e = NORANGE;
else if (*ltok == 0)
e = EOL;
else
{
if (legal_number (ltok, &num) == 0 || (int)num != num || num <= 0)
{
builtin_error ("%s: invalid list value", ltok);
*opp = poslist;
return -1;
}
e = num;
e--;
if (e == s)
e = NORANGE;
}
if (npos == nsize)
{
nsize += 4;
poslist = (struct cutpos *)xrealloc (poslist, nsize * sizeof (struct cutpos));
}
poslist[npos].startpos = s;
poslist[npos].endpos = e;
npos++;
}
if (npos == 0)
{
builtin_error ("missing list of positions");
*opp = poslist;
return -1;
}
qsort (poslist, npos, sizeof(poslist[0]), poscmp);
*opp = poslist;
return npos;
}
static int
cutbytes (v, line, ops)
SHELL_VAR *v;
char *line;
struct cutop *ops;
{
arrayind_t ind;
char *buf, *bmap;
size_t llen;
int i, b, n, s, e;
llen = strlen (line);
buf = xmalloc (llen + 1);
bmap = xmalloc (llen + 1);
memset (bmap, 0, llen);
for (n = 0; n < ops->npos; n++)
{
s = ops->poslist[n].startpos; /* no translation needed yet */
e = ops->poslist[n].endpos;
if (e == NORANGE)
e = s;
else if (e == EOL || e >= llen)
e = llen - 1;
/* even if a column is specified multiple times, it will only be printed
once */
for (i = s; i <= e; i++)
bmap[i] = 1;
}
b = 0;
for (i = 0; i < llen; i++)
if (bmap[i])
buf[b++] = line[i];
buf[b] = 0;
if (v)
{
ind = 0;
bind_array_element (v, ind, buf, 0);
ind++;
}
else
printf ("%s\n", buf);
free (buf);
free (bmap);
return ind;
}
static int
cutchars (v, line, ops)
SHELL_VAR *v;
char *line;
struct cutop *ops;
{
arrayind_t ind;
char *buf, *bmap;
wchar_t *wbuf, *wb2;
size_t llen, wlen;
int i, b, n, s, e;
if (MB_CUR_MAX == 1)
return (cutbytes (v, line, ops));
if (locale_utf8locale && utf8_mbsmbchar (line) == 0)
return (cutbytes (v, line, ops));
llen = strlen (line);
wbuf = (wchar_t *)xmalloc ((llen + 1) * sizeof (wchar_t));
wlen = mbstowcs (wbuf, line, llen);
if (MB_INVALIDCH (wlen))
{
free (wbuf);
return (cutbytes (v, line, ops));
}
bmap = xmalloc (llen + 1);
memset (bmap, 0, llen);
for (n = 0; n < ops->npos; n++)
{
s = ops->poslist[n].startpos; /* no translation needed yet */
e = ops->poslist[n].endpos;
if (e == NORANGE)
e = s;
else if (e == EOL || e >= wlen)
e = wlen - 1;
/* even if a column is specified multiple times, it will only be printed
once */
for (i = s; i <= e; i++)
bmap[i] = 1;
}
wb2 = (wchar_t *)xmalloc ((wlen + 1) * sizeof (wchar_t));
b = 0;
for (i = 0; i < wlen; i++)
if (bmap[i])
wb2[b++] = wbuf[i];
wb2[b] = 0;
free (wbuf);
buf = bmap;
n = wcstombs (buf, wb2, llen);
if (v)
{
ind = 0;
bind_array_element (v, ind, buf, 0);
ind++;
}
else
printf ("%s\n", buf);
free (buf);
free (wb2);
return ind;
}
/* The basic strategy is to cut the line into fields using strsep, populate
an array of fields from 0..nf, then select those fields using the same
bitmap approach as cut{bytes,chars} and assign them to the array variable
V or print them on stdout. This function obeys SFLAG. */
static int
cutfields (v, line, ops)
SHELL_VAR *v;
char *line;
struct cutop *ops;
{
arrayind_t ind;
char *buf, *bmap, *field, **fields, delim[2];
size_t llen, fsize;
int i, b, n, s, e, nf;
ind = 0;
delim[0] = ops->delim;
delim[1] = '\0';
fields = 0;
nf = 0;
fsize = 0;
field = buf = line;
do
{
field = strsep (&buf, delim); /* destructive */
if (nf == fsize)
{
fsize += 8;
fields = xrealloc (fields, fsize * sizeof (char *));
}
fields[nf] = field;
if (field)
nf++;
}
while (field);
if (nf == 1)
{
free (fields);
if (ops->flags & SFLAG)
return ind;
if (v)
{
bind_array_element (v, ind, line, 0);
ind++;
}
else
printf ("%s\n", line);
return ind;
}
bmap = xmalloc (nf + 1);
memset (bmap, 0, nf);
for (n = 0; n < ops->npos; n++)
{
s = ops->poslist[n].startpos; /* no translation needed yet */
e = ops->poslist[n].endpos;
if (e == NORANGE)
e = s;
else if (e == EOL || e >= nf)
e = nf - 1;
/* even if a column is specified multiple times, it will only be printed
once */
for (i = s; i <= e; i++)
bmap[i] = 1;
}
for (i = 1, b = 0; b < nf; b++)
{
if (bmap[b] == 0)
continue;
if (v)
{
bind_array_element (v, ind, fields[b], 0);
ind++;
}
else
{
if (i == 0)
putchar (ops->delim);
printf ("%s", fields[b]);
}
i = 0;
}
if (v == 0)
putchar ('\n');
return nf;
}
static int
cutline (v, line, ops)
SHELL_VAR *v;
char *line;
struct cutop *ops;
{
int rval;
if (ops->flags & BFLAG)
rval = cutbytes (v, line, ops);
else if (ops->flags & CFLAG)
rval = cutchars (v, line, ops);
else
rval = cutfields (v, line, ops);
return (rval >= 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}
static int
cutfile (v, list, ops)
SHELL_VAR *v;
WORD_LIST *list;
struct cutop *ops;
{
int fd, unbuffered_read;
char *line, *b;
size_t llen;
WORD_LIST *l;
ssize_t n;
line = 0;
llen = 0;
l = list;
do
{
/* for each file */
if (l == 0 || (l->word->word[0] == '-' && l->word->word[1] == '\0'))
fd = 0;
else
fd = open (l->word->word, O_RDONLY);
if (fd < 0)
{
file_error (l->word->word);
return (EXECUTION_FAILURE);
}
#ifndef __CYGWIN__
unbuffered_read = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
#else
unbuffered_read = 1;
#endif
while ((n = zgetline (fd, &line, &llen, '\n', unbuffered_read)) != -1)
{
QUIT;
if (line[n] == '\n')
line[n] = '\0'; /* cutline expects no newline terminator */
cutline (v, line, ops); /* can modify line */
}
if (fd > 0)
close (fd);
QUIT;
if (l)
l = l->next;
}
while (l);
free (line);
return EXECUTION_SUCCESS;
}
#define OPTSET(x) ((cutflags & (x)) ? 1 : 0)
static int
cut_internal (which, list)
int which; /* not used yet */
WORD_LIST *list;
{
int opt, rval, cutflags, delim, npos;
char *array_name, *cutstring, *list_arg;
SHELL_VAR *v;
struct cutop op;
struct cutpos *poslist;
v = 0;
rval = EXECUTION_SUCCESS;
cutflags = 0;
array_name = 0;
list_arg = 0;
delim = '\t';
reset_internal_getopt ();
while ((opt = internal_getopt (list, "a:b:c:d:f:sn")) != -1)
{
switch (opt)
{
case 'a':
array_name = list_optarg;
break;
case 'b':
cutflags |= BFLAG;
list_arg = list_optarg;
break;
case 'c':
cutflags |= CFLAG;
list_arg = list_optarg;
break;
case 'd':
cutflags |= DFLAG;
delim = list_optarg[0];
if (delim == 0 || list_optarg[1])
{
builtin_error ("delimiter must be a single non-null character");
return (EX_USAGE);
}
break;
case 'f':
cutflags |= FFLAG;
list_arg = list_optarg;
break;
case 'n':
break;
case 's':
cutflags |= SFLAG;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (array_name && (legal_identifier (array_name) == 0))
{
sh_invalidid (array_name);
return (EXECUTION_FAILURE);
}
if (list == 0 && which == 0)
{
builtin_error ("string argument required");
return (EX_USAGE);
}
/* options are mutually exclusive and one is required */
if ((OPTSET (BFLAG) + OPTSET (CFLAG) + OPTSET (FFLAG)) != 1)
{
builtin_usage ();
return (EX_USAGE);
}
if ((npos = getlist (list_arg, &poslist)) < 0)
{
free (poslist);
return (EXECUTION_FAILURE);
}
if (array_name)
{
v = find_or_make_array_variable (array_name, 1);
if (v == 0 || readonly_p (v) || noassign_p (v))
{
if (v && readonly_p (v))
err_readonly (array_name);
return (EXECUTION_FAILURE);
}
else if (array_p (v) == 0)
{
builtin_error ("%s: not an indexed array", array_name);
return (EXECUTION_FAILURE);
}
if (invisible_p (v))
VUNSETATTR (v, att_invisible);
array_flush (array_cell (v));
}
op.flags = cutflags;
op.delim = delim;
op.npos = npos;
op.poslist = poslist;
/* we implement cut as a builtin with a cutfile() function that opens each
filename in LIST as a filename (or `-' for stdin) and runs cutline on
every line in the file. */
if (which == 0)
{
cutstring = list->word->word;
if (cutstring == 0 || *cutstring == 0)
{
free (poslist);
return (EXECUTION_SUCCESS);
}
rval = cutline (v, cutstring, &op);
}
else
rval = cutfile (v, list, &op);
return (rval);
}
int
lcut_builtin (list)
WORD_LIST *list;
{
return (cut_internal (0, list));
}
int
cut_builtin (list)
WORD_LIST *list;
{
return (cut_internal (1, list));
}
char *lcut_doc[] = {
"Extract selected fields from a string.",
"",
"Select portions of LINE (as specified by LIST) and assign them to",
"elements of the indexed array ARRAY starting at index 0, or write",
"them to the standard output if -a is not specified.",
"",
"Items specified by LIST are either column positions or fields delimited",
"by a special character, and are described more completely in cut(1).",
"",
"Columns correspond to bytes (-b), characters (-c), or fields (-f). The",
"field delimiter is specified by -d (default TAB). Column numbering",
"starts at 1.",
(char *)NULL
};
struct builtin lcut_struct = {
"lcut", /* builtin name */
lcut_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
lcut_doc, /* array of long documentation strings. */
"lcut [-a ARRAY] [-b LIST] [-c LIST] [-f LIST] [-d CHAR] [-sn] line", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};
char *cut_doc[] = {
"Extract selected fields from each line of a file.",
"",
"Select portions of each line (as specified by LIST) from each FILE",
"and write them to the standard output. cut reads from the standard",
"input if no FILE arguments are specified or if a FILE argument is a",
"single hyphen.",
"",
"Items specified by LIST are either column positions or fields delimited",
"by a special character, and are described more completely in cut(1).",
"",
"Columns correspond to bytes (-b), characters (-c), or fields (-f). The",
"field delimiter is specified by -d (default TAB). Column numbering",
"starts at 1.",
(char *)NULL
};
struct builtin cut_struct = {
"cut", /* builtin name */
cut_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
cut_doc, /* array of long documentation strings. */
"cut [-a ARRAY] [-b LIST] [-c LIST] [-f LIST] [-d CHAR] [-sn] [file ...]", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,119 @@
/* dirname - return directory portion of pathname */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#include "bashgetopt.h"
int
dirname_builtin (list)
WORD_LIST *list;
{
int slen;
char *string;
if (no_options (list))
return (EX_USAGE);
list = loptend;
if (list == 0 || list->next)
{
builtin_usage ();
return (EX_USAGE);
}
string = list->word->word;
slen = strlen (string);
/* Strip trailing slashes */
while (slen > 0 && string[slen - 1] == '/')
slen--;
/* (2) If string consists entirely of slash characters, string shall be
set to a single slash character. In this case, skip steps (3)
through (8). */
if (slen == 0)
{
fputs ("/\n", stdout);
return (EXECUTION_SUCCESS);
}
/* (3) If there are any trailing slash characters in string, they
shall be removed. */
string[slen] = '\0';
/* (4) If there are no slash characters remaining in string, string
shall be set to a single period character. In this case, skip
steps (5) through (8).
(5) If there are any trailing nonslash characters in string,
they shall be removed. */
while (--slen >= 0)
if (string[slen] == '/')
break;
if (slen < 0)
{
fputs (".\n", stdout);
return (EXECUTION_SUCCESS);
}
/* (7) If there are any trailing slash characters in string, they
shall be removed. */
while (--slen >= 0)
if (string[slen] != '/')
break;
string[++slen] = '\0';
/* (8) If the remaining string is empty, string shall be set to a single
slash character. */
printf ("%s\n", (slen == 0) ? "/" : string);
return (EXECUTION_SUCCESS);
}
char *dirname_doc[] = {
"Display directory portion of pathname.",
"",
"The STRING is converted to the name of the directory containing",
"the filename corresponding to the last pathname component in STRING.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. */
struct builtin dirname_struct = {
"dirname", /* builtin name */
dirname_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
dirname_doc, /* array of long documentation strings. */
"dirname string", /* usage synopsis */
0 /* reserved for internal use */
};

300
examples/loadables/dsv.c Normal file
View file

@ -0,0 +1,300 @@
/* dsv - process a line of delimiter-separated data and populate an indexed
array with the fields */
/*
Copyright (C) 2022 Free Software Foundation, Inc.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
/* See Makefile for compilation details. */
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include <stdio.h>
#include "loadables.h"
#define DSV_ARRAY_DEFAULT "DSV"
#define NQUOTE 0
#define DQUOTE 1
#define SQUOTE 2
#define F_SHELLQUOTE 0x01
#define F_GREEDY 0x02
#define F_PRESERVE 0x04
/* Split LINE into delimiter-separated fields, storing each field into a
separate element of array variable DSV, starting at index 0. The format
of LINE is delimiter-separated values. By default, this splits lines of
CSV data as described in RFC 4180. If *DSTRING is any other value than
',', this uses that character as a field delimiter. Pass F_SHELLQUOTE in
FLAGS to understand shell-like double-quoting and backslash-escaping in
double quotes instead of the "" CSV behavior, and shell-like single quotes.
Pass F_GREEDY in FLAGS to consume multiple leading and trailing instances
of *DSTRING and consecutive instances of *DSTRING in LINE without creating
null fields. If you want to preserve the quote characters in the generated
fields, pass F_PRESERVE; by default, this removes them. */
static int
dsvsplit (dsv, line, dstring, flags)
SHELL_VAR *dsv;
char *line, *dstring;
int flags;
{
arrayind_t ind;
char *field, *prev, *buf, *xbuf;
int delim, qstate;
int b, rval;
xbuf = 0;
ind = 0;
field = prev = line;
/* If we want a greedy split, consume leading instances of *DSTRING */
if (flags & F_GREEDY)
{
while (*prev == *dstring)
prev++;
field = prev;
}
do
{
if (*prev == '"')
{
if (xbuf == 0)
xbuf = xmalloc (strlen (prev) + 1);
buf = xbuf;
b = 0;
if (flags & F_PRESERVE)
buf[b++] = *prev;
qstate = DQUOTE;
for (field = ++prev; *field; field++)
{
if (qstate == DQUOTE && *field == '"' && field[1] == '"' && (flags & F_SHELLQUOTE) == 0)
buf[b++] = *field++; /* skip double quote */
else if (qstate == DQUOTE && (flags & F_SHELLQUOTE) && *field == '\\' && strchr (slashify_in_quotes, field[1]) != 0)
buf[b++] = *++field; /* backslash quoted double quote */
else if (qstate == DQUOTE && *field == '"')
{
qstate = NQUOTE;
if (flags & F_PRESERVE)
buf[b++] = *field;
}
else if (qstate == NQUOTE && *field == *dstring)
break;
else
/* This copies any text between a closing double quote and the
delimiter. If you want to change that, make sure to do the
copy only if qstate == DQUOTE. */
buf[b++] = *field;
}
buf[b] = '\0';
}
else if ((flags & F_SHELLQUOTE) && *prev == '\'')
{
if (xbuf == 0)
xbuf = xmalloc (strlen (prev) + 1);
buf = xbuf;
b = 0;
if (flags & F_PRESERVE)
buf[b++] = *prev;
qstate = SQUOTE;
for (field = ++prev; *field; field++)
{
if (qstate == SQUOTE && *field == '\'')
{
qstate = NQUOTE;
if (flags & F_PRESERVE)
buf[b++] = *field;
}
else if (qstate == NQUOTE && *field == *dstring)
break;
else
/* This copies any text between a closing single quote and the
delimiter. If you want to change that, make sure to do the
copy only if qstate == SQUOTE. */
buf[b++] = *field;
}
buf[b] = '\0';
}
else
{
buf = prev;
field = prev + strcspn (prev, dstring);
}
delim = *field;
*field = '\0';
if ((flags & F_GREEDY) == 0 || buf[0])
{
bind_array_element (dsv, ind, buf, 0);
ind++;
}
*field = delim;
if (delim == *dstring)
prev = field + 1;
}
while (delim == *dstring);
if (xbuf)
free (xbuf);
return (rval = ind); /* number of fields */
}
int
dsv_builtin (list)
WORD_LIST *list;
{
int opt, rval, flags;
char *array_name, *dsvstring, *delims;
SHELL_VAR *v;
array_name = 0;
rval = EXECUTION_SUCCESS;
delims = ",";
flags = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "a:d:Sgp")) != -1)
{
switch (opt)
{
case 'a':
array_name = list_optarg;
break;
case 'd':
delims = list_optarg;
break;
case 'S':
flags |= F_SHELLQUOTE;
break;
case 'g':
flags |= F_GREEDY;
break;
case 'p':
flags |= F_PRESERVE;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (array_name == 0)
array_name = DSV_ARRAY_DEFAULT;
if (legal_identifier (array_name) == 0)
{
sh_invalidid (array_name);
return (EXECUTION_FAILURE);
}
if (list == 0)
{
builtin_error ("dsv string argument required");
return (EX_USAGE);
}
v = find_or_make_array_variable (array_name, 1);
if (v == 0 || readonly_p (v) || noassign_p (v))
{
if (v && readonly_p (v))
err_readonly (array_name);
return (EXECUTION_FAILURE);
}
else if (array_p (v) == 0)
{
builtin_error ("%s: not an indexed array", array_name);
return (EXECUTION_FAILURE);
}
if (invisible_p (v))
VUNSETATTR (v, att_invisible);
array_flush (array_cell (v));
dsvstring = list->word->word;
if (dsvstring == 0 || *dsvstring == 0)
return (EXECUTION_SUCCESS);
opt = dsvsplit (v, dsvstring, delims, flags);
/* Maybe do something with OPT here, it's the number of fields */
return (rval);
}
/* Called when builtin is enabled and loaded from the shared object. If this
function returns 0, the load fails. */
int
dsv_builtin_load (name)
char *name;
{
return (1);
}
/* Called when builtin is disabled. */
void
dsv_builtin_unload (name)
char *name;
{
}
char *dsv_doc[] = {
"Read delimiter-separated fields from STRING.",
"",
"Parse STRING, a line of delimiter-separated values, into individual",
"fields, and store them into the indexed array ARRAYNAME starting at",
"index 0. The parsing understands and skips over double-quoted strings. ",
"If ARRAYNAME is not supplied, \"DSV\" is the default array name.",
"If the delimiter is a comma, the default, this parses comma-",
"separated values as specified in RFC 4180.",
"",
"The -d option specifies the delimiter. The delimiter is the first",
"character of the DELIMS argument. Specifying a DELIMS argument that",
"contains more than one character is not supported and will produce",
"unexpected results. The -S option enables shell-like quoting: double-",
"quoted strings can contain backslashes preceding special characters,",
"and the backslash will be removed; and single-quoted strings are",
"processed as the shell would process them. The -g option enables a",
"greedy split: sequences of the delimiter are skipped at the beginning",
"and end of STRING, and consecutive instances of the delimiter in STRING",
"do not generate empty fields. If the -p option is supplied, dsv leaves",
"quote characters as part of the generated field; otherwise they are",
"removed.",
"",
"The return value is 0 unless an invalid option is supplied or the ARRAYNAME",
"argument is invalid or readonly.",
(char *)NULL
};
struct builtin dsv_struct = {
"dsv", /* builtin name */
dsv_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
dsv_doc, /* array of long documentation strings. */
"dsv [-a ARRAYNAME] [-d DELIMS] [-Sgp] string", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,374 @@
/* Loadable builtin to get and set file descriptor flags. */
/* See Makefile for compilation details. */
/*
Copyright (C) 2017-2022 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <fcntl.h>
#include <errno.h>
#include "bashansi.h"
#include <stdio.h>
#include "loadables.h"
#ifndef FD_CLOEXEC
# define FD_CLOEXEC 1
#endif
static const struct
{
const char *name;
int value;
} file_flags[] =
{
#ifdef O_APPEND
{ "append", O_APPEND },
#else
# define O_APPEND 0
#endif
#ifdef O_ASYNC
{ "async", O_ASYNC },
#else
# define O_ASYNC 0
#endif
#ifdef O_SYNC
{ "sync", O_SYNC },
#else
# define O_SYNC 0
#endif
#ifdef O_NONBLOCK
{ "nonblock", O_NONBLOCK },
#else
# define O_NONBLOCK 0
#endif
#ifdef O_FSYNC
{ "fsync", O_FSYNC },
#else
# define O_FSYNC 0
#endif
#ifdef O_DSYNC
{ "dsync", O_DSYNC },
#else
# define O_DSYNC 0
#endif
#ifdef O_RSYNC
{ "rsync", O_RSYNC },
#else
# define O_RSYNC 0
#endif
#ifdef O_ALT_IO
{ "altio", O_ALT_IO },
#else
# define O_ALT_IO 0
#endif
#ifdef O_DIRECT
{ "direct", O_DIRECT },
#else
# define O_DIRECT 0
#endif
#ifdef O_NOATIME
{ "noatime", O_NOATIME },
#else
# define O_NOATIME 0
#endif
#ifdef O_NOSIGPIPE
{ "nosigpipe", O_NOSIGPIPE },
#else
# define O_NOSIGPIPE 0
#endif
#ifndef O_CLOEXEC
# define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_FSYNC|O_DSYNC|\
O_RSYNC|O_ALT_IO|O_DIRECT|O_NOATIME|O_NOSIGPIPE)
/* An unused bit in the file status flags word we can use to pass around the
state of close-on-exec. */
# define O_CLOEXEC ((~ALLFLAGS) ^ ((~ALLFLAGS) & ((~ALLFLAGS) - 1)))
#endif
#ifdef O_CLOEXEC
{ "cloexec", O_CLOEXEC },
#endif
};
#define N_FLAGS (sizeof (file_flags) / sizeof (file_flags[0]))
#ifndef errno
extern int errno;
#endif
/* FIX THIS */
static int
getallflags ()
{
int i, allflags;
for (i = allflags = 0; i < N_FLAGS; i++)
allflags |= file_flags[i].value;
return allflags;
}
static int
getflags(int fd, int p)
{
int c, f;
int allflags;
if ((c = fcntl(fd, F_GETFD)) == -1)
{
if (p)
builtin_error("can't get status for fd %d: %s", fd, strerror(errno));
return -1;
}
if ((f = fcntl(fd, F_GETFL)) == -1)
{
if (p)
builtin_error("Can't get flags for fd %d: %s", fd, strerror(errno));
return -1;
}
if (c)
f |= O_CLOEXEC;
return f & getallflags();
}
static void
printone(int fd, int p, int verbose)
{
int f;
size_t i;
if ((f = getflags(fd, p)) == -1)
return;
printf ("%d:", fd);
for (i = 0; i < N_FLAGS; i++)
{
if (f & file_flags[i].value)
{
printf ("%s%s", verbose ? "+" : "", file_flags[i].name);
f &= ~file_flags[i].value;
}
else if (verbose)
printf ( "-%s", file_flags[i].name);
else
continue;
if (f || (verbose && i != N_FLAGS - 1))
putchar (',');
}
printf ("\n");
}
static int
parseflags(char *s, int *p, int *n)
{
int f, *v;
size_t i;
f = 0;
*p = *n = 0;
for (s = strtok(s, ","); s; s = strtok(NULL, ","))
{
switch (*s)
{
case '+':
v = p;
s++;
break;
case '-':
v = n;
s++;
break;
default:
v = &f;
break;
}
for (i = 0; i < N_FLAGS; i++)
if (strcmp(s, file_flags[i].name) == 0)
{
*v |= file_flags[i].value;
break;
}
if (i == N_FLAGS)
builtin_error("invalid flag `%s'", s);
}
return f;
}
static void
setone(int fd, char *v, int verbose)
{
int f, n, pos, neg, cloexec;
f = getflags(fd, 1);
if (f == -1)
return;
parseflags(v, &pos, &neg);
cloexec = -1;
if ((pos & O_CLOEXEC) && (f & O_CLOEXEC) == 0)
cloexec = FD_CLOEXEC;
if ((neg & O_CLOEXEC) && (f & O_CLOEXEC))
cloexec = 0;
if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1)
builtin_error("can't set status for fd %d: %s", fd, strerror(errno));
pos &= ~O_CLOEXEC;
neg &= ~O_CLOEXEC;
f &= ~O_CLOEXEC;
n = f;
n |= pos;
n &= ~neg;
if (n != f && fcntl(fd, F_SETFL, n) == -1)
builtin_error("can't set flags for fd %d: %s", fd, strerror(errno));
}
static int
getmaxfd ()
{
int maxfd, ignore;
#ifdef F_MAXFD
maxfd = fcntl (0, F_MAXFD);
if (maxfd > 0)
return maxfd;
#endif
maxfd = getdtablesize ();
if (maxfd <= 0)
maxfd = HIGH_FD_MAX;
for (maxfd--; maxfd > 0; maxfd--)
if (fcntl (maxfd, F_GETFD, &ignore) != -1)
break;
return maxfd;
}
int
fdflags_builtin (WORD_LIST *list)
{
int opt, maxfd, i, num, verbose, setflag;
char *setspec;
WORD_LIST *l;
intmax_t inum;
setflag = verbose = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "s:v")) != -1)
{
switch (opt)
{
case 's':
setflag = 1;
setspec = list_optarg;
break;
case 'v':
verbose = 1;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
/* Maybe we could provide some default here, but we don't yet. */
if (list == 0 && setflag)
return (EXECUTION_SUCCESS);
if (list == 0)
{
maxfd = getmaxfd ();
if (maxfd < 0)
{
builtin_error ("can't get max fd: %s", strerror (errno));
return (EXECUTION_FAILURE);
}
for (i = 0; i < maxfd; i++)
printone (i, 0, verbose);
return (EXECUTION_SUCCESS);
}
opt = EXECUTION_SUCCESS;
for (l = list; l; l = l->next)
{
if (legal_number (l->word->word, &inum) == 0 || inum < 0)
{
builtin_error ("%s: invalid file descriptor", l->word->word);
opt = EXECUTION_FAILURE;
continue;
}
num = inum; /* truncate to int */
if (setflag)
setone (num, setspec, verbose);
else
printone (num, 1, verbose);
}
return (opt);
}
char *fdflags_doc[] =
{
"Display and modify file descriptor flags.",
"",
"Display or, if the -s option is supplied, set flags for each file",
"descriptor supplied as an argument. If the -v option is supplied,",
"the display is verbose, including each settable option name in the",
"form of a string such as that accepted by the -s option.",
"",
"The -s option accepts a string with a list of flag names, each preceded",
"by a `+' (set) or `-' (unset). Those changes are applied to each file",
"descriptor supplied as an argument.",
"",
"If no file descriptor arguments are supplied, the displayed information",
"consists of the status of flags for each of the shell's open files.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. The flags must include BUILTIN_ENABLED so the
builtin can be used. */
struct builtin fdflags_struct = {
"fdflags", /* builtin name */
fdflags_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
fdflags_doc, /* array of long documentation strings. */
"fdflags [-v] [-s flags_string] [fd ...]", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

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

@ -0,0 +1,629 @@
/*
* finfo - print file info
*
* Chet Ramey
* chet@po.cwru.edu
*/
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <sys/types.h>
#ifdef MAJOR_IN_MKDEV
# include <sys/mkdev.h>
#endif
#ifdef MAJOR_IN_SYSMACROS
# include <sys/sysmacros.h>
#endif
#include "posixstat.h"
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include "posixtime.h"
#include "bashansi.h"
#include "shell.h"
#include "builtins.h"
#include "common.h"
#include "getopt.h"
#ifndef errno
extern int errno;
#endif
extern char **make_builtin_argv ();
static void perms();
static int printst();
static int printsome();
static void printmode();
static int printfinfo();
static int finfo_main();
extern int sh_optind;
extern char *sh_optarg;
extern char *this_command_name;
static char *prog;
static int pmask;
#define OPT_UID 0x00001
#define OPT_GID 0x00002
#define OPT_DEV 0x00004
#define OPT_INO 0x00008
#define OPT_PERM 0x00010
#define OPT_LNKNAM 0x00020
#define OPT_FID 0x00040
#define OPT_NLINK 0x00080
#define OPT_RDEV 0x00100
#define OPT_SIZE 0x00200
#define OPT_ATIME 0x00400
#define OPT_MTIME 0x00800
#define OPT_CTIME 0x01000
#define OPT_BLKSIZE 0x02000
#define OPT_BLKS 0x04000
#define OPT_FTYPE 0x08000
#define OPT_PMASK 0x10000
#define OPT_OPERM 0x20000
#define OPT_ASCII 0x1000000
#define OPTIONS "acdgiflmnopsuACGMP:U"
static int
octal(s)
char *s;
{
int r;
r = *s - '0';
while (*++s >= '0' && *s <= '7')
r = (r * 8) + (*s - '0');
return r;
}
static int
finfo_main(argc, argv)
int argc;
char **argv;
{
register int i;
int mode, flags, opt;
sh_optind = 0; /* XXX */
prog = base_pathname(argv[0]);
if (argc == 1) {
builtin_usage();
return(1);
}
flags = 0;
while ((opt = sh_getopt(argc, argv, OPTIONS)) != EOF) {
switch(opt) {
case 'a': flags |= OPT_ATIME; break;
case 'A': flags |= OPT_ATIME|OPT_ASCII; break;
case 'c': flags |= OPT_CTIME; break;
case 'C': flags |= OPT_CTIME|OPT_ASCII; break;
case 'd': flags |= OPT_DEV; break;
case 'i': flags |= OPT_INO; break;
case 'f': flags |= OPT_FID; break;
case 'g': flags |= OPT_GID; break;
case 'G': flags |= OPT_GID|OPT_ASCII; break;
case 'l': flags |= OPT_LNKNAM; break;
case 'm': flags |= OPT_MTIME; break;
case 'M': flags |= OPT_MTIME|OPT_ASCII; break;
case 'n': flags |= OPT_NLINK; break;
case 'o': flags |= OPT_OPERM; break;
case 'p': flags |= OPT_PERM; break;
case 'P': flags |= OPT_PMASK; pmask = octal(sh_optarg); break;
case 's': flags |= OPT_SIZE; break;
case 'u': flags |= OPT_UID; break;
case 'U': flags |= OPT_UID|OPT_ASCII; break;
default: builtin_usage (); return(1);
}
}
argc -= sh_optind;
argv += sh_optind;
if (argc == 0) {
builtin_usage();
return(1);
}
for (i = 0; i < argc; i++)
opt = flags ? printsome (argv[i], flags) : printfinfo(argv[i]);
return(opt);
}
static struct stat *
getstat(f)
char *f;
{
static struct stat st;
int fd, r;
intmax_t lfd;
if (strncmp(f, "/dev/fd/", 8) == 0) {
if ((legal_number(f + 8, &lfd) == 0) || (int)lfd != lfd) {
builtin_error("%s: invalid fd", f + 8);
return ((struct stat *)0);
}
fd = lfd;
r = fstat(fd, &st);
} else
#ifdef HAVE_LSTAT
r = lstat(f, &st);
#else
r = stat(f, &st);
#endif
if (r < 0) {
builtin_error("%s: cannot stat: %s", f, strerror(errno));
return ((struct stat *)0);
}
return (&st);
}
static int
printfinfo(f)
char *f;
{
struct stat *st;
st = getstat(f);
return (st ? printst(st) : 1);
}
static int
getperm(m)
int m;
{
return (m & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID));
}
static void
perms(m)
int m;
{
char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */
int i;
i = 0;
if (m & S_IRUSR)
ubits[i++] = 'r';
if (m & S_IWUSR)
ubits[i++] = 'w';
if (m & S_IXUSR)
ubits[i++] = 'x';
ubits[i] = '\0';
i = 0;
if (m & S_IRGRP)
gbits[i++] = 'r';
if (m & S_IWGRP)
gbits[i++] = 'w';
if (m & S_IXGRP)
gbits[i++] = 'x';
gbits[i] = '\0';
i = 0;
if (m & S_IROTH)
obits[i++] = 'r';
if (m & S_IWOTH)
obits[i++] = 'w';
if (m & S_IXOTH)
obits[i++] = 'x';
obits[i] = '\0';
if (m & S_ISUID)
ubits[2] = (m & S_IXUSR) ? 's' : 'S';
if (m & S_ISGID)
gbits[2] = (m & S_IXGRP) ? 's' : 'S';
if (m & S_ISVTX)
obits[2] = (m & S_IXOTH) ? 't' : 'T';
printf ("u=%s,g=%s,o=%s", ubits, gbits, obits);
}
static void
printmode(mode)
int mode;
{
if (S_ISBLK(mode))
printf("S_IFBLK ");
if (S_ISCHR(mode))
printf("S_IFCHR ");
if (S_ISDIR(mode))
printf("S_IFDIR ");
if (S_ISREG(mode))
printf("S_IFREG ");
if (S_ISFIFO(mode))
printf("S_IFIFO ");
if (S_ISLNK(mode))
printf("S_IFLNK ");
if (S_ISSOCK(mode))
printf("S_IFSOCK ");
#ifdef S_ISWHT
if (S_ISWHT(mode))
printf("S_ISWHT ");
#endif
perms(getperm(mode));
printf("\n");
}
static int
printst(st)
struct stat *st;
{
struct passwd *pw;
struct group *gr;
char *owner;
int ma, mi, d;
ma = major (st->st_rdev);
mi = minor (st->st_rdev);
#if defined (makedev)
d = makedev (ma, mi);
#else
d = st->st_rdev & 0xFF;
#endif
printf("Device (major/minor): %d (%d/%d)\n", d, ma, mi);
printf("Inode: %d\n", (int) st->st_ino);
printf("Mode: (%o) ", (int) st->st_mode);
printmode((int) st->st_mode);
printf("Link count: %d\n", (int) st->st_nlink);
pw = getpwuid(st->st_uid);
owner = pw ? pw->pw_name : "unknown";
printf("Uid of owner: %d (%s)\n", (int) st->st_uid, owner);
gr = getgrgid(st->st_gid);
owner = gr ? gr->gr_name : "unknown";
printf("Gid of owner: %d (%s)\n", (int) st->st_gid, owner);
printf("Device type: %d\n", (int) st->st_rdev);
printf("File size: %ld\n", (long) st->st_size);
printf("File last access time: %s", ctime (&st->st_atime));
printf("File last modify time: %s", ctime (&st->st_mtime));
printf("File last status change time: %s", ctime (&st->st_ctime));
fflush(stdout);
return(0);
}
static int
printsome(f, flags)
char *f;
int flags;
{
struct stat *st;
struct passwd *pw;
struct group *gr;
int p;
char *b;
st = getstat(f);
if (st == NULL)
return (1);
/* Print requested info */
if (flags & OPT_ATIME) {
if (flags & OPT_ASCII)
printf("%s", ctime(&st->st_atime));
else
printf("%ld\n", st->st_atime);
} else if (flags & OPT_MTIME) {
if (flags & OPT_ASCII)
printf("%s", ctime(&st->st_mtime));
else
printf("%ld\n", st->st_mtime);
} else if (flags & OPT_CTIME) {
if (flags & OPT_ASCII)
printf("%s", ctime(&st->st_ctime));
else
printf("%ld\n", st->st_ctime);
} else if (flags & OPT_DEV)
printf("%lu\n", (unsigned long)st->st_dev);
else if (flags & OPT_INO)
printf("%lu\n", (unsigned long)st->st_ino);
else if (flags & OPT_FID)
printf("%lu:%lu\n", (unsigned long)st->st_dev, (unsigned long)st->st_ino);
else if (flags & OPT_NLINK)
printf("%lu\n", (unsigned long)st->st_nlink);
else if (flags & OPT_LNKNAM) {
#ifdef S_ISLNK
b = xmalloc(4096);
p = readlink(f, b, 4096);
if (p >= 0 && p < 4096)
b[p] = '\0';
else {
p = errno;
strcpy(b, prog);
strcat(b, ": ");
strcat(b, strerror(p));
}
printf("%s\n", b);
free(b);
#else
printf("%s\n", f);
#endif
} else if (flags & OPT_PERM) {
perms(st->st_mode);
printf("\n");
} else if (flags & OPT_OPERM)
printf("%o\n", getperm(st->st_mode));
else if (flags & OPT_PMASK)
printf("%o\n", getperm(st->st_mode) & pmask);
else if (flags & OPT_UID) {
pw = getpwuid(st->st_uid);
if (flags & OPT_ASCII)
printf("%s\n", pw ? pw->pw_name : "unknown");
else
printf("%d\n", st->st_uid);
} else if (flags & OPT_GID) {
gr = getgrgid(st->st_gid);
if (flags & OPT_ASCII)
printf("%s\n", gr ? gr->gr_name : "unknown");
else
printf("%d\n", st->st_gid);
} else if (flags & OPT_SIZE)
printf("%ld\n", (long) st->st_size);
return (0);
}
#ifndef NOBUILTIN
int
finfo_builtin(list)
WORD_LIST *list;
{
int c, r;
char **v;
WORD_LIST *l;
v = make_builtin_argv (list, &c);
r = finfo_main (c, v);
free (v);
return r;
}
static char *finfo_doc[] = {
"Display information about file attributes.",
"",
"Display information about each FILE. Only single operators should",
"be supplied. If no options are supplied, a summary of the info",
"available about each FILE is printed. If FILE is of the form",
"/dev/fd/XX, file descriptor XX is described. Operators, if supplied,",
"have the following meanings:",
"",
" -a last file access time",
" -A last file access time in ctime format",
" -c last file status change time",
" -C last file status change time in ctime format",
" -m last file modification time",
" -M last file modification time in ctime format",
" -d device",
" -i inode",
" -f composite file identifier (device:inode)",
" -g gid of owner",
" -G group name of owner",
" -l name of file pointed to by symlink",
" -n link count",
" -o permissions in octal",
" -p permissions in ascii",
" -P mask permissions ANDed with MASK (like with umask)",
" -s file size in bytes",
" -u uid of owner",
" -U user name of owner",
(char *)0
};
struct builtin finfo_struct = {
"finfo",
finfo_builtin,
BUILTIN_ENABLED,
finfo_doc,
"finfo [-acdgiflmnopsuACGMPU] file [file...]",
0
};
#endif
#ifdef NOBUILTIN
#if defined (PREFER_STDARG)
# include <stdarg.h>
#else
# if defined (PREFER_VARARGS)
# include <varargs.h>
# endif
#endif
char *this_command_name;
main(argc, argv)
int argc;
char **argv;
{
this_command_name = argv[0];
exit(finfo_main(argc, argv));
}
void
builtin_usage()
{
fprintf(stderr, "%s: usage: %s [-%s] [file ...]\n", prog, prog, OPTIONS);
}
#ifndef HAVE_STRERROR
char *
strerror(e)
int e;
{
static char ebuf[40];
extern int sys_nerr;
extern char *sys_errlist[];
if (e < 0 || e > sys_nerr) {
sprintf(ebuf,"Unknown error code %d", e);
return (&ebuf[0]);
}
return (sys_errlist[e]);
}
#endif
char *
xmalloc(s)
size_t s;
{
char *ret;
extern char *malloc();
ret = malloc(s);
if (ret)
return (ret);
fprintf(stderr, "%s: cannot malloc %d bytes\n", prog, s);
exit(1);
}
char *
base_pathname(p)
char *p;
{
char *t;
if (t = strrchr(p, '/'))
return(++t);
return(p);
}
int
legal_number (string, result)
char *string;
long *result;
{
int sign;
long value;
sign = 1;
value = 0;
if (result)
*result = 0;
/* Skip leading whitespace characters. */
while (whitespace (*string))
string++;
if (!*string)
return (0);
/* We allow leading `-' or `+'. */
if (*string == '-' || *string == '+')
{
if (!digit (string[1]))
return (0);
if (*string == '-')
sign = -1;
string++;
}
while (digit (*string))
{
if (result)
value = (value * 10) + digit_value (*string);
string++;
}
/* Skip trailing whitespace, if any. */
while (whitespace (*string))
string++;
/* Error if not at end of string. */
if (*string)
return (0);
if (result)
*result = value * sign;
return (1);
}
int sh_optind;
char *sh_optarg;
int sh_opterr;
extern int optind;
extern char *optarg;
int
sh_getopt(c, v, o)
int c;
char **v, *o;
{
int r;
r = getopt(c, v, o);
sh_optind = optind;
sh_optarg = optarg;
return r;
}
#if defined (USE_VARARGS)
void
#if defined (PREFER_STDARG)
builtin_error (const char *format, ...)
#else
builtin_error (format, va_alist)
const char *format;
va_dcl
#endif
{
va_list args;
if (this_command_name && *this_command_name)
fprintf (stderr, "%s: ", this_command_name);
#if defined (PREFER_STDARG)
va_start (args, format);
#else
va_start (args);
#endif
vfprintf (stderr, format, args);
va_end (args);
fprintf (stderr, "\n");
}
#else
void
builtin_error (format, arg1, arg2, arg3, arg4, arg5)
char *format, *arg1, *arg2, *arg3, *arg4, *arg5;
{
if (this_command_name && *this_command_name)
fprintf (stderr, "%s: ", this_command_name);
fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
fprintf (stderr, "\n");
fflush (stderr);
}
#endif /* !USE_VARARGS */
#endif

1163
examples/loadables/getconf.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,136 @@
/*
Copyright (C) 2021 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
/* getconf.h -- replacement definitions for ones the system doesn't provide
and don't appear in <typemax.h> */
#ifndef _GETCONF_H
#define _GETCONF_H
/* Some systems do not define these; use POSIX.2 minimum recommended values. */
#ifndef _POSIX2_COLL_WEIGHTS_MAX
# define _POSIX2_COLL_WEIGHTS_MAX 2
#endif
/* If we're on a posix system, but the system doesn't define the necessary
constants, use posix.1 minimum values. */
#if defined (_POSIX_VERSION)
#ifndef _POSIX_ARG_MAX
# define _POSIX_ARG_MAX 4096
#endif
#ifndef _POSIX_CHILD_MAX
# define _POSIX_CHILD_MAX 6
#endif
#ifndef _POSIX_LINK_MAX
# define _POSIX_LINK_MAX 8
#endif
#ifndef _POSIX_MAX_CANON
# define _POSIX_MAX_CANON 255
#endif
#ifndef _POSIX_MAX_INPUT
# define _POSIX_MAX_INPUT 255
#endif
#ifndef _POSIX_NAME_MAX
# define _POSIX_NAME_MAX 14
#endif
#ifndef _POSIX_NGROUPS_MAX
# define _POSIX_NGROUPS_MAX 0
#endif
#ifndef _POSIX_OPEN_MAX
# define _POSIX_OPEN_MAX 16
#endif
#ifndef _POSIX_PATH_MAX
# define _POSIX_PATH_MAX 255
#endif
#ifndef _POSIX_PIPE_BUF
# define _POSIX_PIPE_BUF 512
#endif
#ifndef _POSIX_SSIZE_MAX
# define _POSIX_SSIZE_MAX 32767
#endif
#ifndef _POSIX_STREAM_MAX
# define _POSIX_STREAM_MAX 8
#endif
#ifndef _POSIX_TZNAME_MAX
# define _POSIX_TZNAME_MAX 3
#endif
#ifndef _POSIX2_BC_BASE_MAX
# define _POSIX2_BC_BASE_MAX 99
#endif
#ifndef _POSIX2_BC_DIM_MAX
# define _POSIX2_BC_DIM_MAX 2048
#endif
#ifndef _POSIX2_BC_SCALE_MAX
# define _POSIX2_BC_SCALE_MAX 99
#endif
#ifndef _POSIX2_BC_STRING_MAX
# define _POSIX2_BC_STRING_MAX 1000
#endif
#ifndef _POSIX2_EQUIV_CLASS_MAX
# define _POSIX2_EQUIV_CLASS_MAX 2
#endif
#ifndef _POSIX2_EXPR_NEST_MAX
# define _POSIX2_EXPR_NEST_MAX 32
#endif
#ifndef _POSIX2_LINE_MAX
# define _POSIX2_LINE_MAX 2048
#endif
#ifndef _POSIX2_RE_DUP_MAX
# define _POSIX2_RE_DUP_MAX 255
#endif
#endif /* _POSIX_VERSION */
/* ANSI/ISO C, POSIX.1-200x, XPG 4.2, and C language type limits.
Defined only if the system include files and <typemax.h> don't. */
#ifndef CHAR_MAX
# define CHAR_MAX 127
#endif
#ifndef CHAR_MIN
# define CHAR_MIN -128
#endif
#ifndef SCHAR_MAX
# define SCHAR_MAX 127
#endif
#ifndef SCHAR_MIN
# define SCHAR_MIN -128
#endif
#ifndef INT_BIT
# define INT_BIT (sizeof (int) * CHAR_BIT)
#endif
#ifndef LONG_BIT
# define LONG_BIT (sizeof (long int) * CHAR_BIT)
#endif
#ifndef WORD_BIT
# define WORD_BIT (sizeof (int) * CHAR_BIT)
#endif
#if !defined (PRIdMAX)
# if HAVE_LONG_LONG
# define PRIdMAX "lld"
# else
# define PRIdMAX "ld"
# endif
#endif
#endif /* _GETCONF_H */

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

@ -0,0 +1,170 @@
/* head - copy first part of files. */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "bashtypes.h"
#include "posixstat.h"
#include "filecntl.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include <stdio.h>
#include <errno.h>
#include "chartypes.h"
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif
static void
munge_list (list)
WORD_LIST *list;
{
WORD_LIST *l, *nl;
WORD_DESC *wd;
char *arg;
for (l = list; l; l = l->next)
{
arg = l->word->word;
if (arg[0] != '-' || arg[1] == '-' || (DIGIT(arg[1]) == 0))
return;
/* We have -[0-9]* */
wd = make_bare_word (arg+1);
nl = make_word_list (wd, l->next);
l->word->word[1] = 'n';
l->word->word[2] = '\0';
l->next = nl;
l = nl; /* skip over new argument */
}
}
static int
file_head (fp, cnt)
FILE *fp;
int cnt;
{
int ch;
while (cnt--)
{
while ((ch = getc (fp)) != EOF)
{
QUIT;
if (putchar (ch) == EOF)
{
builtin_error ("write error: %s", strerror (errno));
return EXECUTION_FAILURE;
}
QUIT;
if (ch == '\n')
break;
}
}
return (EXECUTION_SUCCESS);
}
int
head_builtin (list)
WORD_LIST *list;
{
int nline, opt, rval;
WORD_LIST *l;
FILE *fp;
char *t;
munge_list (list); /* change -num into -n num */
reset_internal_getopt ();
nline = 10;
while ((opt = internal_getopt (list, "n:")) != -1)
{
switch (opt)
{
case 'n':
nline = atoi (list_optarg);
if (nline <= 0)
{
builtin_error ("bad line count: %s", list_optarg);
return (EX_USAGE);
}
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (list == 0)
return (file_head (stdin, nline));
for (rval = EXECUTION_SUCCESS, opt = 1, l = list; l; l = l->next)
{
fp = fopen (l->word->word, "r");
if (fp == NULL)
{
builtin_error ("%s: %s", l->word->word, strerror (errno));
continue;
}
if (list->next) /* more than one file */
{
printf ("%s==> %s <==\n", opt ? "" : "\n", l->word->word);
opt = 0;
}
QUIT;
rval = file_head (fp, nline);
fclose (fp);
}
return (rval);
}
char *head_doc[] = {
"Display lines from beginning of file.",
"",
"Copy the first N lines from the input files to the standard output.",
"N is supplied as an argument to the `-n' option. If N is not given,",
"the first ten lines are copied.",
(char *)NULL
};
struct builtin head_struct = {
"head", /* builtin name */
head_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
head_doc, /* array of long documentation strings. */
"head [-n num] [file ...]", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,96 @@
/* Sample builtin to be dynamically loaded with enable -f and create a new
builtin. */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
#include "loadables.h"
/* A builtin `xxx' is normally implemented with an `xxx_builtin' function.
If you're converting a command that uses the normal Unix argc/argv
calling convention, use argv = make_builtin_argv (list, &argc) and call
the original `main' something like `xxx_main'. Look at cat.c for an
example.
Builtins should use internal_getopt to parse options. It is the same as
getopt(3), but it takes a WORD_LIST *. Look at print.c for an example
of its use.
If the builtin takes no options, call no_options(list) before doing
anything else. If it returns a non-zero value, your builtin should
immediately return EX_USAGE. Look at logname.c for an example.
A builtin command returns EXECUTION_SUCCESS for success and
EXECUTION_FAILURE to indicate failure. */
int
hello_builtin (list)
WORD_LIST *list;
{
printf("hello world\n");
fflush (stdout);
return (EXECUTION_SUCCESS);
}
int
hello_builtin_load (s)
char *s;
{
printf ("hello builtin loaded\n");
fflush (stdout);
return (1);
}
void
hello_builtin_unload (s)
char *s;
{
printf ("hello builtin unloaded\n");
fflush (stdout);
}
/* An array of strings forming the `long' documentation for a builtin xxx,
which is printed by `help xxx'. It must end with a NULL. By convention,
the first line is a short description. */
char *hello_doc[] = {
"Sample builtin.",
"",
"this is the long doc for the sample hello builtin",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. The flags must include BUILTIN_ENABLED so the
builtin can be used. */
struct builtin hello_struct = {
"hello", /* builtin name */
hello_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
hello_doc, /* array of long documentation strings. */
"hello", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

329
examples/loadables/id.c Normal file
View file

@ -0,0 +1,329 @@
/*
* id - POSIX.2 user identity
*
* (INCOMPLETE -- supplementary groups for other users not yet done)
*
* usage: id [-Ggu] [-nr] [user]
*
* The default output format looks something like:
* uid=xxx(chet) gid=xx groups=aa(aname), bb(bname), cc(cname)
*/
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include "bashtypes.h"
#include <pwd.h>
#include <grp.h>
#include "bashansi.h"
#ifdef HAVE_LIMITS_H
# include <limits.h>
#else
# include <sys/param.h>
#endif
#if !defined (HAVE_GETPW_DECLS)
extern struct passwd *getpwuid ();
#endif
extern struct group *getgrgid ();
#include "shell.h"
#include "builtins.h"
#include "stdc.h"
#include "common.h"
#include "bashgetopt.h"
#define ID_ALLGROUPS 0x001 /* -G */
#define ID_GIDONLY 0x002 /* -g */
#define ID_USENAME 0x004 /* -n */
#define ID_USEREAL 0x008 /* -r */
#define ID_USERONLY 0x010 /* -u */
#define ID_FLAGSET(s) ((id_flags & (s)) != 0)
static int id_flags;
static uid_t ruid, euid;
static gid_t rgid, egid;
static char *id_user;
static int inituser ();
static int id_pruser ();
static int id_prgrp ();
static int id_prgroups ();
static int id_prall ();
int
id_builtin (list)
WORD_LIST *list;
{
int opt;
char *user;
id_flags = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "Ggnru")) != -1)
{
switch (opt)
{
case 'G': id_flags |= ID_ALLGROUPS; break;
case 'g': id_flags |= ID_GIDONLY; break;
case 'n': id_flags |= ID_USENAME; break;
case 'r': id_flags |= ID_USEREAL; break;
case 'u': id_flags |= ID_USERONLY; break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
user = list ? list->word->word : (char *)NULL;
/* Check for some invalid option combinations */
opt = ID_FLAGSET (ID_ALLGROUPS) + ID_FLAGSET (ID_GIDONLY) + ID_FLAGSET (ID_USERONLY);
if (opt > 1 || (opt == 0 && ((id_flags & (ID_USEREAL|ID_USENAME)) != 0)))
{
builtin_usage ();
return (EX_USAGE);
}
if (list && list->next)
{
builtin_usage ();
return (EX_USAGE);
}
if (inituser (user) < 0)
return (EXECUTION_FAILURE);
opt = 0;
if (id_flags & ID_USERONLY)
opt += id_pruser ((id_flags & ID_USEREAL) ? ruid : euid);
else if (id_flags & ID_GIDONLY)
opt += id_prgrp ((id_flags & ID_USEREAL) ? rgid : egid);
else if (id_flags & ID_ALLGROUPS)
opt += id_prgroups (user);
else
opt += id_prall (user);
putchar ('\n');
fflush (stdout);
return (opt == 0 ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}
static int
inituser (uname)
char *uname;
{
struct passwd *pwd;
if (uname)
{
pwd = getpwnam (uname);
if (pwd == 0)
{
builtin_error ("%s: no such user", uname);
return -1;
}
ruid = euid = pwd->pw_uid;
rgid = egid = pwd->pw_gid;
}
else
{
ruid = current_user.uid;
euid = current_user.euid;
rgid = current_user.gid;
egid = current_user.egid;
}
return 0;
}
/* Print the name or value of user ID UID. */
static int
id_pruser (uid)
int uid;
{
struct passwd *pwd = NULL;
int r;
r = 0;
if (id_flags & ID_USENAME)
{
pwd = getpwuid (uid);
if (pwd == NULL)
r = 1;
}
if (pwd)
printf ("%s", pwd->pw_name);
else
printf ("%u", (unsigned) uid);
return r;
}
/* Print the name or value of group ID GID. */
static int
id_prgrp (gid)
int gid;
{
struct group *grp = NULL;
int r;
r = 0;
if (id_flags & ID_USENAME)
{
grp = getgrgid (gid);
if (grp == NULL)
r = 1;
}
if (grp)
printf ("%s", grp->gr_name);
else
printf ("%u", (unsigned) gid);
return r;
}
static int
id_prgroups (uname)
char *uname;
{
int *glist, ng, i, r;
r = 0;
id_prgrp (rgid);
if (egid != rgid)
{
putchar (' ');
id_prgrp (egid);
}
if (uname)
{
builtin_error ("supplementary groups for other users not yet implemented");
glist = (int *)NULL;
ng = 0;
r = 1;
}
else
glist = get_group_array (&ng);
for (i = 0; i < ng; i++)
if (glist[i] != rgid && glist[i] != egid)
{
putchar (' ');
id_prgrp (glist[i]);
}
return r;
}
static int
id_prall (uname)
char *uname;
{
int r, i, ng, *glist;
struct passwd *pwd;
struct group *grp;
r = 0;
printf ("uid=%u", (unsigned) ruid);
pwd = getpwuid (ruid);
if (pwd == NULL)
r = 1;
else
printf ("(%s)", pwd->pw_name);
printf (" gid=%u", (unsigned) rgid);
grp = getgrgid (rgid);
if (grp == NULL)
r = 1;
else
printf ("(%s)", grp->gr_name);
if (euid != ruid)
{
printf (" euid=%u", (unsigned) euid);
pwd = getpwuid (euid);
if (pwd == NULL)
r = 1;
else
printf ("(%s)", pwd->pw_name);
}
if (egid != rgid)
{
printf (" egid=%u", (unsigned) egid);
grp = getgrgid (egid);
if (grp == NULL)
r = 1;
else
printf ("(%s)", grp->gr_name);
}
if (uname)
{
builtin_error ("supplementary groups for other users not yet implemented");
glist = (int *)NULL;
ng = 0;
r = 1;
}
else
glist = get_group_array (&ng);
if (ng > 0)
printf (" groups=");
for (i = 0; i < ng; i++)
{
if (i > 0)
printf (", ");
printf ("%u", (unsigned) glist[i]);
grp = getgrgid (glist[i]);
if (grp == NULL)
r = 1;
else
printf ("(%s)", grp->gr_name);
}
return r;
}
char *id_doc[] = {
"Display information about user."
"",
"Return information about user identity",
(char *)NULL
};
struct builtin id_struct = {
"id",
id_builtin,
BUILTIN_ENABLED,
id_doc,
"id [user]\n\tid -G [-n] [user]\n\tid -g [-nr] [user]\n\tid -u [-nr] [user]",
0
};

236
examples/loadables/ln.c Normal file
View file

@ -0,0 +1,236 @@
/* ln - make links */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2020 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "bashtypes.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "posixstat.h"
#include <stdio.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif
typedef int unix_link_syscall_t PARAMS((const char *, const char *));
#define LN_SYMLINK 0x01
#define LN_UNLINK 0x02
#define LN_NOFOLLOW 0x04
static unix_link_syscall_t *linkfn;
static int dolink ();
int
ln_builtin (list)
WORD_LIST *list;
{
int rval, opt, flags;
WORD_LIST *l;
char *sdir;
struct stat sb;
flags = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "fs")) != -1)
{
switch (opt)
{
case 'f':
flags |= LN_UNLINK;
break;
case 's':
flags |= LN_SYMLINK;
break;
case 'h':
case 'n':
flags |= LN_NOFOLLOW;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
linkfn = (flags & LN_SYMLINK) ? symlink : link;
if (list->next == 0) /* ln target, equivalent to ln target . */
return (dolink (list->word->word, ".", flags));
if (list->next->next == 0) /* ln target source */
return (dolink (list->word->word, list->next->word->word, flags));
/* ln target1 target2 ... directory */
/* find last argument: target directory, and make sure it's an existing
directory. */
for (l = list; l->next; l = l->next)
;
sdir = l->word->word;
if (stat(sdir, &sb) < 0)
{
builtin_error ("%s", sdir);
return (EXECUTION_FAILURE);
}
if (S_ISDIR (sb.st_mode) == 0)
{
builtin_usage ();
return (EX_USAGE);
}
for (rval = EXECUTION_SUCCESS; list != l; list = list->next)
rval += dolink (list->word->word, sdir, flags);
return rval;
}
static char *
mkdirpath (dir, file)
char *dir, *file;
{
int dlen, flen;
char *ret;
dlen = strlen (dir);
flen = strlen (file);
ret = xmalloc (2 + dlen + flen);
strcpy (ret, dir);
if (ret[dlen - 1] != '/')
ret[dlen++] = '/';
strcpy (ret + dlen, file);
return ret;
}
#if defined (HAVE_LSTAT)
# define LSTAT lstat
# define LSTAT_OR_STAT_IF(c, f, b) ((c) ? lstat((f), (b)) : stat((f), (b)))
#else
# define LSTAT stat
# define LSTAT_OR_STAT_IF(c, f, b) (stat((f), (b)))
#endif
static int
dolink (src, dst, flags)
char *src, *dst;
int flags;
{
struct stat ssb, dsb;
int exists;
char *dst_path, *p;
/* If we're not doing symlinks, the source must exist and not be a
directory. */
if ((flags & LN_SYMLINK) == 0)
{
if (stat (src, &ssb) != 0)
{
builtin_error ("%s: %s", src, strerror (errno));
return (EXECUTION_FAILURE);
}
if (S_ISDIR (ssb.st_mode))
{
errno = EISDIR;
builtin_error ("%s: %s", src, strerror (errno));
return (EXECUTION_FAILURE);
}
}
/* If the destination is a directory, create the final filename by appending
the basename of the source to the destination. */
dst_path = 0;
if ((LSTAT_OR_STAT_IF((flags & LN_NOFOLLOW), dst, &dsb) == 0) && S_ISDIR (dsb.st_mode))
{
if ((p = strrchr (src, '/')) == 0)
p = src;
else
p++;
dst_path = mkdirpath (dst, p);
dst = dst_path;
}
exists = LSTAT (dst, &dsb) == 0;
/* If -f was specified, and the destination exists, unlink it. */
if ((flags & LN_UNLINK) && exists && unlink (dst) != 0)
{
builtin_error ("%s: cannot unlink: %s", dst, strerror (errno));
FREE (dst_path);
return (EXECUTION_FAILURE);
}
/* Perform the link. */
if ((*linkfn) (src, dst) != 0)
{
builtin_error ("cannot link %s to %s: %s", dst, src, strerror (errno));
FREE (dst_path);
return (EXECUTION_FAILURE);
}
FREE (dst_path);
return (EXECUTION_SUCCESS);
}
char *ln_doc[] = {
"Link files.",
"",
"Create a new directory entry with the same modes as the original",
"file. The -f option means to unlink any existing file, permitting",
"the link to occur. The -s option means to create a symbolic link.",
"By default, ln makes hard links. Specifying -n or its synonym -h",
"causes ln to not resolve symlinks in the target file or directory.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. */
struct builtin ln_struct = {
"ln", /* builtin name */
ln_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
ln_doc, /* array of long documentation strings. */
"ln [-fhns] file1 [file2] OR ln [-fhns] file ... directory", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,34 @@
/* loadables.h -- Include files needed by all loadable builtins */
/* Copyright (C) 2015 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LOADABLES_H_
#define __LOADABLES_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
#endif

View file

@ -0,0 +1,74 @@
/* logname - print login name of current user */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif
int
logname_builtin (list)
WORD_LIST *list;
{
char *np;
if (no_options (list))
return (EX_USAGE);
np = getlogin ();
if (np == 0)
{
builtin_error ("cannot find username: %s", strerror (errno));
return (EXECUTION_FAILURE);
}
printf ("%s\n", np);
return (EXECUTION_SUCCESS);
}
char *logname_doc[] = {
"Display user login name.",
"",
"Write the current user's login name to the standard output",
"and exit. logname ignores the LOGNAME and USER variables.",
"logname ignores any non-option arguments.",
(char *)NULL
};
struct builtin logname_struct = {
"logname",
logname_builtin,
BUILTIN_ENABLED,
logname_doc,
"logname",
0
};

245
examples/loadables/mkdir.c Normal file
View file

@ -0,0 +1,245 @@
/* mkdir - make directories */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "bashtypes.h"
#include "posixstat.h"
#include <errno.h>
#include <stdio.h>
#include "bashansi.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif
#define ISOCTAL(c) ((c) >= '0' && (c) <= '7')
extern int parse_symbolic_mode ();
static int make_path ();
static int original_umask;
int
mkdir_builtin (list)
WORD_LIST *list;
{
int opt, pflag, mflag, omode, rval, nmode, parent_mode;
char *mode;
WORD_LIST *l;
reset_internal_getopt ();
pflag = mflag = 0;
mode = (char *)NULL;
while ((opt = internal_getopt(list, "m:p")) != -1)
switch (opt)
{
case 'p':
pflag = 1;
break;
case 'm':
mflag = 1;
mode = list_optarg;
break;
CASE_HELPOPT;
default:
builtin_usage();
return (EX_USAGE);
}
list = loptend;
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
if (mode == NULL)
omode = S_IRWXU | S_IRWXG | S_IRWXO; /* a=rwx */
else if (ISOCTAL (*mode)) /* octal number */
{
omode = read_octal (mode);
if (omode < 0)
{
builtin_error ("invalid file mode: %s", mode);
return (EXECUTION_FAILURE);
}
}
else /* symbolic mode */
{
/* initial bits are a=rwx; the mode argument modifies them */
omode = parse_symbolic_mode (mode, S_IRWXU | S_IRWXG | S_IRWXO);
if (omode < 0)
{
builtin_error ("invalid file mode: %s", mode);
return (EXECUTION_FAILURE);
}
}
/* Make the new mode */
original_umask = umask (0);
umask (original_umask);
nmode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~original_umask;
parent_mode = nmode | (S_IWUSR|S_IXUSR); /* u+wx */
/* Adjust new mode based on mode argument */
nmode &= omode;
for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
{
if (pflag && make_path (l->word->word, mflag, nmode, parent_mode))
{
rval = EXECUTION_FAILURE;
continue;
}
else if (pflag == 0 && mkdir (l->word->word, nmode) < 0)
{
builtin_error ("cannot create directory `%s': %s", l->word->word, strerror (errno));
rval = EXECUTION_FAILURE;
}
}
return rval;
}
/* Make all the directories leading up to PATH, then create PATH. Note that
this changes the process's umask; make sure that all paths leading to a
return reset it to ORIGINAL_UMASK */
static int
make_path (path, user_mode, nmode, parent_mode)
char *path;
int user_mode;
int nmode, parent_mode;
{
int oumask;
struct stat sb;
char *p, *npath;
if (stat (path, &sb) == 0)
{
if (S_ISDIR (sb.st_mode) == 0)
{
builtin_error ("`%s': file exists but is not a directory", path);
return 1;
}
if (user_mode && chmod (path, nmode))
{
builtin_error ("%s: %s", path, strerror (errno));
return 1;
}
return 0;
}
oumask = umask (0);
npath = savestring (path); /* So we can write to it. */
/* Check whether or not we need to do anything with intermediate dirs. */
/* Skip leading slashes. */
p = npath;
while (*p == '/')
p++;
while (p = strchr (p, '/'))
{
*p = '\0';
if (stat (npath, &sb) != 0)
{
if (mkdir (npath, 0))
{
builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
umask (original_umask);
free (npath);
return 1;
}
if (chmod (npath, parent_mode) != 0)
{
builtin_error ("cannot chmod directory `%s': %s", npath, strerror (errno));
umask (original_umask);
free (npath);
return 1;
}
}
else if (S_ISDIR (sb.st_mode) == 0)
{
builtin_error ("`%s': file exists but is not a directory", npath);
umask (original_umask);
free (npath);
return 1;
}
*p++ = '/'; /* restore slash */
while (*p == '/')
p++;
}
/* Create the final directory component. */
if (stat (npath, &sb) && mkdir (npath, nmode))
{
builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
umask (original_umask);
free (npath);
return 1;
}
umask (original_umask);
free (npath);
return 0;
}
char *mkdir_doc[] = {
"Create directories.",
"",
"Make directories. Create the directories named as arguments, in",
"the order specified, using mode rwxrwxrwx as modified by the current",
"umask (see `help umask'). The -m option causes the file permission",
"bits of the final directory to be MODE. The MODE argument may be",
"an octal number or a symbolic mode like that used by chmod(1). If",
"a symbolic mode is used, the operations are interpreted relative to",
"an initial mode of \"a=rwx\". The -p option causes any required",
"intermediate directories in PATH to be created. The directories",
"are created with permission bits of rwxrwxrwx as modified by the current",
"umask, plus write and search permissions for the owner. mkdir",
"returns 0 if the directories are created successfully, and non-zero",
"if an error occurs.",
(char *)NULL
};
struct builtin mkdir_struct = {
"mkdir",
mkdir_builtin,
BUILTIN_ENABLED,
mkdir_doc,
"mkdir [-p] [-m mode] directory [directory ...]",
0
};

146
examples/loadables/mkfifo.c Normal file
View file

@ -0,0 +1,146 @@
/* mkfifo - make FIFOs */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2020 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "bashtypes.h"
#include "posixstat.h"
#include <errno.h>
#include <stdio.h>
#include "bashansi.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif
#define ISOCTAL(c) ((c) >= '0' && (c) <= '7')
extern int parse_symbolic_mode ();
static int original_umask;
int
mkfifo_builtin (list)
WORD_LIST *list;
{
int opt, mflag, omode, rval, nmode, basemode;
char *mode;
WORD_LIST *l;
mflag = 0;
mode = (char *)NULL;
reset_internal_getopt ();
while ((opt = internal_getopt(list, "m:")) != -1)
switch (opt)
{
case 'm':
mflag = 1;
mode = list_optarg;
break;
CASE_HELPOPT;
default:
builtin_usage();
return (EX_USAGE);
}
list = loptend;
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
basemode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
if (mode == NULL)
omode = basemode;
else if (ISOCTAL (*mode)) /* octal number */
{
omode = read_octal (mode);
if (omode < 0)
{
builtin_error ("invalid file mode: %s", mode);
return (EXECUTION_FAILURE);
}
}
else /* symbolic mode */
{
/* initial bits are a=rwx; the mode argument modifies them */
omode = parse_symbolic_mode (mode, basemode);
if (omode < 0)
{
builtin_error ("invalid file mode: %s", mode);
return (EXECUTION_FAILURE);
}
}
/* Make the new mode */
original_umask = umask (0);
umask (original_umask);
nmode = basemode & ~original_umask;
/* Adjust new mode based on mode argument */
nmode &= omode;
for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
{
if (mkfifo (l->word->word, nmode) < 0)
{
builtin_error ("cannot create FIFO `%s': %s", l->word->word, strerror (errno));
rval = EXECUTION_FAILURE;
}
}
return rval;
}
char *mkfifo_doc[] = {
"Create FIFOs (named pipes).",
"",
"Make FIFOs. Create the FIFOs named as arguments, in",
"the order specified, using mode a=rw as modified by the current",
"umask (see `help umask'). The -m option causes the file permission",
"bits of the final FIFO to be MODE. The MODE argument may be",
"an octal number or a symbolic mode like that used by chmod(1). If",
"a symbolic mode is used, the operations are interpreted relative to",
"an initial mode of \"a=rw\". mkfifo returns 0 if the FIFOs are",
"umask, plus write and search permissions for the owner. mkdir",
"created successfully, and non-zero if an error occurs.",
(char *)NULL
};
struct builtin mkfifo_struct = {
"mkfifo",
mkfifo_builtin,
BUILTIN_ENABLED,
mkfifo_doc,
"mkfifo [-m mode] fifo_name [fifo_name ...]",
0
};

212
examples/loadables/mktemp.c Normal file
View file

@ -0,0 +1,212 @@
/* mktemp - create temporary file or directory */
/*
Copyright (C) 2019 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
#include "bashansi.h"
#include "loadables.h"
#define DEFAULT_PREFIX "shtmp"
int
mktemp_builtin (list)
WORD_LIST *list;
{
WORD_LIST *l;
int rval, opt, fd, mflags, base_mflags;
int dflag, qflag, tflag, uflag, onetime;
char *prefix, *varname, *filename, *template;
SHELL_VAR *v;
dflag = qflag = uflag = tflag = onetime = 0;
prefix = varname = 0;
rval = EXECUTION_SUCCESS;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "dqut:v:")) != -1)
{
switch (opt)
{
case 'd':
dflag = 1;
break;
case 'q':
qflag = 1;
break;
case 't':
tflag = 1;
prefix = list_optarg;
break;
case 'u':
uflag = 1;
break;
case 'v':
varname = list_optarg;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (varname) /* check for validity, not readonly */
{
if (legal_identifier (varname) == 0)
{
if (qflag == 0)
sh_invalidid (varname);
return (EXECUTION_FAILURE);
}
v = find_variable (varname);
if (v && readonly_p (v))
{
if (qflag == 0)
sh_readonly (varname);
return (EXECUTION_FAILURE);
}
}
onetime = (list == 0); /* once through the loop, $TMPDIR/prefix.XXXXXX */
if (prefix == 0)
prefix = DEFAULT_PREFIX;
base_mflags = MT_USETMPDIR|MT_USERANDOM; /* USERANDOM not strictly needed */
while (list || onetime)
{
mflags = base_mflags;
onetime = 0;
#if defined (USE_MKTEMP) && defined (USE_MKSTEMP)
if (list)
{
template = list->word->word;
mflags |= MT_TEMPLATE;
}
#else
/* This is sub-optimal. */
if (list)
{
/* Treat the basename as a prefix */
template = strrchr (list->word->word, '/');
if (template)
template++;
else
template = list->word->word;
}
#endif
else
template = prefix;
if (dflag)
{
filename = sh_mktmpdir (template, mflags);
if (filename == 0)
{
if (qflag == 0)
builtin_error ("%s: cannot create directory", template);
rval = EXECUTION_FAILURE;
}
else
{
if (uflag)
rmdir (filename);
printf ("%s\n", filename);
}
}
else /* filename */
{
fd = sh_mktmpfd (template, mflags, &filename);
if (fd < 0)
{
if (qflag == 0)
builtin_error ("%s: cannot create file", template);
rval = EXECUTION_FAILURE;
}
else
{
close (fd);
if (uflag)
unlink (filename);
printf ("%s\n", filename);
}
}
/* Assign variable if requested */
if (filename && varname)
{
v = builtin_bind_variable (varname, filename, 0);
if (v == 0 || readonly_p (v) || noassign_p (v))
{
builtin_error ("%s: cannot set variable", varname);
rval = EXECUTION_FAILURE;
}
}
FREE (filename);
if (list)
list = list->next;
}
return (rval);
}
char *mktemp_doc[] = {
"Make unique temporary file name",
"",
"Take each supplied filename template and overwrite a portion of it",
"to create a filename, which is unique and may be used by the calling",
"script. TEMPLATE is a string ending in some number of 'X's. If",
"TEMPLATE is not supplied, shtmp.XXXXXX is used and $TMPDIR is used as",
"the name of the containing directory. Files are created u+rw; directories",
"are created u+rwx.",
"",
"Options, if supplied, have the following meanings:",
"",
" -d Create a directory instead of a file",
" -q Do not print error messages about file creation failure",
" -t PREFIX Use PREFIX as the directory in which to create files",
" -u Do not create anything; simply print a name",
" -v VAR Store the generated name into shell variable VAR",
"",
"Any PREFIX supplied with -t is ignored if TEMPLATE is supplied.",
"",
"The return status is true if the file or directory was created successfully;",
"false if an error occurs or VAR is invalid or readonly.",
(char *)NULL
};
struct builtin mktemp_struct = {
"mktemp", /* builtin name */
mktemp_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
mktemp_doc, /* array of long documentation strings. */
"mktemp [-d] [-q] [-t prefix] [-u] [-v varname] [template] ...",
0 /* reserved for internal use */
};

View file

@ -0,0 +1,89 @@
/* This module should be dynamically loaded with enable -f
* which would create a new builtin named mypid. You'll need
* the source code for GNU bash to recompile this module.
*
* Then, from within bash, enable -f ./mypid enable_mypid, where ./mypid
* is the binary obtained from running make. Hereafter, `${MYPID}'
* is a shell builtin variable.
*
* This defines an unload hook function that is called when the builtin is
* deleted with enable -d that will unbind the MYPID variable so future
* references to it do not attempt to access memory that is no longer part
* of this process's address space.
*/
#include <config.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "builtins.h"
#include "shell.h"
#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
do \
{ SHELL_VAR *v = bind_variable (var, (val), 0); \
if (v) \
{ \
v->dynamic_value = gfunc; \
v->assign_func = afunc; \
} \
} \
while (0)
static SHELL_VAR *
assign_mypid (
SHELL_VAR *self,
char *value,
arrayind_t unused,
char *key )
{
return (self);
}
static SHELL_VAR *
get_mypid (SHELL_VAR *var)
{
int rv;
char *p;
rv = getpid();
p = itos (rv);
FREE (value_cell (var));
VSETATTR (var, att_integer);
var_setvalue (var, p);
return (var);
}
int
enable_mypid_builtin(WORD_LIST *list)
{
INIT_DYNAMIC_VAR ("MYPID", (char *)NULL, get_mypid, assign_mypid);
return 0;
}
void
enable_mypid_builtin_unload (char *s)
{
unbind_variable ("MYPID");
}
char const *enable_mypid_doc[] = {
"Enable $MYPID.",
"",
"Enables use of the ${MYPID} dynamic variable. ",
"It will yield the current pid of a subshell.",
(char *)0
};
struct builtin enable_mypid_struct = {
"enable_mypid",
enable_mypid_builtin,
BUILTIN_ENABLED,
(char**)(void*)enable_mypid_doc,
"enable_mypid N",
0
};

View file

@ -0,0 +1,54 @@
/* necho - echo without options or argument interpretation */
/* Sample builtin to be dynamically loaded with enable -f and replace an
existing builtin. */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "builtins.h"
#include "shell.h"
int
necho_builtin (list)
WORD_LIST *list;
{
print_word_list (list, " ");
printf("\n");
fflush (stdout);
return (EXECUTION_SUCCESS);
}
char *necho_doc[] = {
"Display arguments.",
"",
"Print the arguments to the standard output separated",
"by space characters and terminated with a newline.",
(char *)NULL
};
struct builtin necho_struct = {
"echo",
necho_builtin,
BUILTIN_ENABLED,
necho_doc,
"echo [args]",
0
};

View file

@ -0,0 +1,381 @@
/* pathchk - check pathnames for validity and portability */
/* Usage: pathchk [-p] path ...
For each PATH, print a message if any of these conditions are false:
* all existing leading directories in PATH have search (execute) permission
* strlen (PATH) <= PATH_MAX
* strlen (each_directory_in_PATH) <= NAME_MAX
Exit status:
0 All PATH names passed all of the tests.
1 An error occurred.
Options:
-p Instead of performing length checks on the
underlying filesystem, test the length of the
pathname and its components against the POSIX.1
minimum limits for portability, _POSIX_NAME_MAX
and _POSIX_PATH_MAX in 2.9.2. Also check that
the pathname contains no character not in the
portable filename character set. */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <sys/types.h>
#include "posixstat.h"
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#if defined (HAVE_LIMITS_H)
# include <limits.h>
#endif
#include "bashansi.h"
#include <stdio.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "stdc.h"
#include "bashgetopt.h"
#include "maxpath.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif
#if !defined (_POSIX_PATH_MAX)
# define _POSIX_PATH_MAX 255
#endif
#if !defined (_POSIX_NAME_MAX)
# define _POSIX_NAME_MAX 14
#endif
/* How do we get PATH_MAX? */
#if defined (_POSIX_VERSION) && !defined (PATH_MAX)
# define PATH_MAX_FOR(p) pathconf ((p), _PC_PATH_MAX)
#endif
/* How do we get NAME_MAX? */
#if defined (_POSIX_VERSION) && !defined (NAME_MAX)
# define NAME_MAX_FOR(p) pathconf ((p), _PC_NAME_MAX)
#endif
#if !defined (PATH_MAX_FOR)
# define PATH_MAX_FOR(p) PATH_MAX
#endif
#if !defined (NAME_MAX_FOR)
# define NAME_MAX_FOR(p) NAME_MAX
#endif
extern char *strerror ();
static int validate_path ();
int
pathchk_builtin (list)
WORD_LIST *list;
{
int retval, pflag, opt;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "p")) != -1)
{
switch (opt)
{
case 'p':
pflag = 1;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
for (retval = 0; list; list = list->next)
retval |= validate_path (list->word->word, pflag);
return (retval ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}
char *pathchk_doc[] = {
"Check pathnames for validity.",
"",
"Check each pathname argument for validity (i.e., it may be used to",
"create or access a file without causing syntax errors) and portability",
"(i.e., no filename truncation will result). If the `-p' option is",
"supplied, more extensive portability checks are performed.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. */
struct builtin pathchk_struct = {
"pathchk", /* builtin name */
pathchk_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
pathchk_doc, /* array of long documentation strings. */
"pathchk [-p] pathname ...", /* usage synopsis */
0 /* reserved for internal use */
};
/* The remainder of this file is stolen shamelessly from `pathchk.c' in
the sh-utils-1.12 distribution, by
David MacKenzie <djm@gnu.ai.mit.edu>
and Jim Meyering <meyering@cs.utexas.edu> */
/* Each element is nonzero if the corresponding ASCII character is
in the POSIX portable character set, and zero if it is not.
In addition, the entry for `/' is nonzero to simplify checking. */
static char const portable_chars[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/* If PATH contains only portable characters, return 1, else 0. */
static int
portable_chars_only (path)
const char *path;
{
const char *p;
for (p = path; *p; ++p)
if (portable_chars[(const unsigned char) *p] == 0)
{
builtin_error ("path `%s' contains nonportable character `%c'", path, *p);
return 0;
}
return 1;
}
/* On some systems, stat can return EINTR. */
#ifndef EINTR
# define SAFE_STAT(name, buf) stat (name, buf)
#else
# define SAFE_STAT(name, buf) safe_stat (name, buf)
static inline int
safe_stat (name, buf)
const char *name;
struct stat *buf;
{
int ret;
do
ret = stat (name, buf);
while (ret < 0 && errno == EINTR);
return ret;
}
#endif
/* Return 1 if PATH is a usable leading directory, 0 if not,
2 if it doesn't exist. */
static int
dir_ok (path)
const char *path;
{
struct stat stats;
if (SAFE_STAT (path, &stats))
return 2;
if (!S_ISDIR (stats.st_mode))
{
builtin_error ("`%s' is not a directory", path);
return 0;
}
/* Use access to test for search permission because
testing permission bits of st_mode can lose with new
access control mechanisms. Of course, access loses if you're
running setuid. */
if (access (path, X_OK) != 0)
{
if (errno == EACCES)
builtin_error ("directory `%s' is not searchable", path);
else
builtin_error ("%s: %s", path, strerror (errno));
return 0;
}
return 1;
}
static char *
xstrdup (s)
char *s;
{
return (savestring (s));
}
/* Make sure that
strlen (PATH) <= PATH_MAX
&& strlen (each-existing-directory-in-PATH) <= NAME_MAX
If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
_POSIX_NAME_MAX instead, and make sure that PATH contains no
characters not in the POSIX portable filename character set, which
consists of A-Z, a-z, 0-9, ., _, -.
Make sure that all leading directories along PATH that exist have
`x' permission.
Return 0 if all of these tests are successful, 1 if any fail. */
static int
validate_path (path, portability)
char *path;
int portability;
{
int path_max;
int last_elem; /* Nonzero if checking last element of path. */
int exists; /* 2 if the path element exists. */
char *slash;
char *parent; /* Last existing leading directory so far. */
if (portability && !portable_chars_only (path))
return 1;
if (*path == '\0')
return 0;
#ifdef lint
/* Suppress `used before initialized' warning. */
exists = 0;
#endif
/* Figure out the parent of the first element in PATH. */
parent = xstrdup (*path == '/' ? "/" : ".");
slash = path;
last_elem = 0;
while (1)
{
int name_max;
int length; /* Length of partial path being checked. */
char *start; /* Start of path element being checked. */
/* Find the end of this element of the path.
Then chop off the rest of the path after this element. */
while (*slash == '/')
slash++;
start = slash;
slash = strchr (slash, '/');
if (slash != NULL)
*slash = '\0';
else
{
last_elem = 1;
slash = strchr (start, '\0');
}
if (!last_elem)
{
exists = dir_ok (path);
if (exists == 0)
{
free (parent);
return 1;
}
}
length = slash - start;
/* Since we know that `parent' is a directory, it's ok to call
pathconf with it as the argument. (If `parent' isn't a directory
or doesn't exist, the behavior of pathconf is undefined.)
But if `parent' is a directory and is on a remote file system,
it's likely that pathconf can't give us a reasonable value
and will return -1. (NFS and tempfs are not POSIX . . .)
In that case, we have no choice but to assume the pessimal
POSIX minimums. */
name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
if (name_max < 0)
name_max = _POSIX_NAME_MAX;
if (length > name_max)
{
builtin_error ("name `%s' has length %d; exceeds limit of %d",
start, length, name_max);
free (parent);
return 1;
}
if (last_elem)
break;
if (exists == 1)
{
free (parent);
parent = xstrdup (path);
}
*slash++ = '/';
}
/* `parent' is now the last existing leading directory in the whole path,
so it's ok to call pathconf with it as the argument. */
path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
if (path_max < 0)
path_max = _POSIX_PATH_MAX;
free (parent);
if (strlen (path) > path_max)
{
builtin_error ("path `%s' has length %lu; exceeds limit of %d",
path, (unsigned long)strlen (path), path_max);
return 1;
}
return 0;
}

View file

@ -0,0 +1,99 @@
#
# Makefile for builtin perl interpreter
#
#
# Copyright (C) 1998 Free Software Foundation, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Include some boilerplate Gnu makefile definitions.
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
infodir = @infodir@
includedir = @includedir@
datarootdir = @datarootdir@
topdir = @top_srcdir@
BUILD_DIR = @BUILD_DIR@
srcdir = @srcdir@
VPATH = @srcdir@
@SET_MAKE@
CC = @CC@
RM = rm -f
SHELL = @MAKE_SHELL@
PERL5 = perl5
CFLAGS = @CFLAGS@
#
# These values are generated for configure by ${topdir}/support/shobj-conf.
# If your system is not supported by that script, but includes facilities for
# dynamic loading of shared objects, please update the script and send the
# changes to bash-maintainers@gnu.org.
#
SHOBJ_CC = @SHOBJ_CC@
SHOBJ_CFLAGS = @SHOBJ_CFLAGS@
SHOBJ_LD = @SHOBJ_LD@
SHOBJ_LDFLAGS = @SHOBJ_LDFLAGS@
SHOBJ_XLDFLAGS = @SHOBJ_XLDFLAGS@
SHOBJ_LIBS = @SHOBJ_LIBS@
SHOBJ_STATUS = @SHOBJ_STATUS@
# Values used for compiling the perl files
PERL_LDOPTS = `${PERL5} -MExtUtils::Embed -e ldopts`
PERL_CFLAGS = ${CCFLAGS} `${PERL5} -MExtUtils::Embed -e ccopts`
SRC = bperl.c iperl.c perlxsi.c
OBJ = bperl.o iperl.o perlxsi.o
BUILTIN = bperl5
INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins \
-I$(topdir)/include -I$(BUILD_DIR) -I$(BUILD_DIR)/lib \
-I$(BUILD_DIR)/builtins
${BUILTIN}: ${OBJ}
${RM} $@
${SHOBJ_LD} ${SHOBJ_LDFLAGS} ${SHOBJ_XLDFLAGS} -o $@ ${OBJ} ${PERL_LDOPTS} ${SHOBJ_LIBS}
bperl.o: bperl.c
${RM} $@
$(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CFLAGS) $(INC) -c -o $@ ${srcdir}/bperl.c
iperl.o: iperl.c
${RM} $@
$(SHOBJ_CC) ${SHOBJ_CFLAGS} $(PERL_CFLAGS) -c -o $@ ${srcdir}/iperl.c
perlxsi.c:
${PERL5} -MExtUtils::Embed -e xsinit -- -o $@
perlxsi.o: perlxsi.c
${RM} $@
${SHOBJ_CC} ${SHOBJ_CFLAGS} $(PERL_CFLAGS) -c -o $@ perlxsi.c
clean mostlyclean:
${RM} ${OBJ}
${RM} ${BUILTIN}
distclean maintainer-clean: clean
${RM} perlxsi.c

View file

@ -0,0 +1,6 @@
This illustrates how to build a perl interpreter into bash. It's not
especially useful; more a proof of concept (it provides none of the
bash internals to the perl interpreter, for example).
This *may* require adding "-rpath /path/to/perl/CORE" and -lperl options
when compiling bash itself.

View file

@ -0,0 +1,53 @@
/*
* perl builtin
*/
#include <config.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#ifndef errno
extern int errno;
#endif
extern char **make_builtin_argv (WORD_LIST *, int *);
extern char **export_env;
extern void perl_close(void);
extern int perl_main(int, char **, char **);
int
bperl_builtin(WORD_LIST *list)
{
char **v;
int c, r;
v = make_builtin_argv(list, &c);
r = perl_main(c, v, export_env);
free(v);
return r;
}
void
bperl_builtin_unload (char *s)
{
perl_close();
}
char *bperl_doc[] = {
"An interface to a perl5 interpreter.",
(char *)0
};
struct builtin bperl_struct = {
"bperl",
bperl_builtin,
BUILTIN_ENABLED,
bperl_doc,
"bperl [perl options] [file ...]",
0
};

View file

@ -0,0 +1,38 @@
#include <EXTERN.h> /* from the Perl distribution */
#include <perl.h> /* from the Perl distribution */
#define iperl my_perl /* I guess the name `my_perl' is required */
extern void xs_init (pTHX);
static PerlInterpreter *iperl; /*** The Perl interpreter ***/
static int first = 1;
void
perl_close (void)
{
PERL_SYS_TERM();
}
int
perl_main(int argc, char **argv, char **env)
{
int r;
if (first) {
first = 0;
PERL_SYS_INIT3(&argc, &argv, &env);
}
iperl = perl_alloc();
perl_construct(iperl);
perl_parse(iperl, xs_init, argc, argv, (char **)NULL);
r = perl_run(iperl);
PerlIO_flush(PerlIO_stdout());
PerlIO_flush(PerlIO_stderr());
perl_destruct(iperl);
perl_free(iperl);
return (r);
}

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

@ -0,0 +1,193 @@
/*
* print -- loadable ksh-93 style print builtin
*/
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "bashtypes.h"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include "bashansi.h"
#include "shell.h"
#include "builtins.h"
#include "stdc.h"
#include "bashgetopt.h"
#include "builtext.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif
int print_builtin ();
static int printargs ();
static FILE *ofp;
extern char *this_command_name;
static char *print_doc[] = {
"Display arguments.",
"",
"Output the arguments. The -f option means to use the argument as a",
"format string as would be supplied to printf(1). The rest of the",
"options are as in ksh.",
(char *)NULL
};
struct builtin print_struct = {
"print",
print_builtin,
BUILTIN_ENABLED,
print_doc,
"print [-Rnprs] [-u unit] [-f format] [arguments]",
(char *)0
};
#ifndef ISOPTION
#define ISOPTION(s, c) (s[0] == '-' && s[2] == '\0' && s[1] == c)
#endif
int
print_builtin (list)
WORD_LIST *list;
{
int c, r, nflag, raw, ofd, sflag;
intmax_t lfd;
char **v, *pfmt, *arg;
WORD_LIST *l;
nflag = raw = sflag = 0;
ofd = 1;
pfmt = 0;
reset_internal_getopt ();
while ((c = internal_getopt (list, "Rnprsu:f:")) != -1)
{
switch (c)
{
case 'R':
raw = 2;
loptend = lcurrent;
if (loptend && ISOPTION (loptend->word->word, 'n'))
{
loptend = loptend->next;
nflag = 1;
}
goto opt_end;
case 'r':
raw = 1;
break;
case 'n':
nflag = 1;
break;
case 's':
sflag = 1;
break;
case 'p':
break; /* NOP */
case 'u':
if (all_digits (list_optarg) && legal_number (list_optarg, &lfd) && lfd == (int)lfd)
ofd = lfd;
else
{
for (l = list; l->next && l->next != lcurrent; l = l->next);
lcurrent = loptend = l;
goto opt_end;
}
break;
case 'f':
pfmt = list_optarg;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
opt_end:
list = loptend;
ofp = (ofd == 1) ? stdout : fdopen (dup (ofd), "w");
if (pfmt)
{
WORD_DESC *w;
WORD_LIST *nlist;
w = make_word (pfmt);
nlist = make_word_list (w, list);
r = printf_builtin (nlist);
nlist->next = (WORD_LIST *)NULL;
dispose_words (nlist);
return (r);
}
if (raw)
{
for (l = list; l; l = l->next)
{
fprintf (ofp, "%s", l->word->word);
if (l->next)
fprintf (ofp, " ");
}
if (nflag == 0)
fprintf (ofp, "\n");
fflush (ofp);
return (0);
}
r = printargs (list, ofp);
if (r && nflag == 0)
fprintf (ofp, "\n");
if (ofd != 1)
fclose (ofp);
return 0;
}
static int
printargs (list, ofp)
WORD_LIST *list;
FILE *ofp;
{
WORD_LIST *l;
char *ostr;
int sawc;
for (sawc = 0, l = list; l; l = l->next)
{
ostr = ansicstr (l->word->word, strlen (l->word->word), 0, &sawc, (int *)0);
if (ostr)
fprintf (ofp, "%s", ostr);
free (ostr);
if (sawc)
return (0);
if (l->next)
fprintf (ofp, " ");
}
return (1);
}

View file

@ -0,0 +1,94 @@
/*
* printenv -- minimal builtin clone of BSD printenv(1).
*
* usage: printenv [varname]
*
*/
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
extern char **export_env;
int
printenv_builtin (list)
WORD_LIST *list;
{
register char **envp;
int opt;
SHELL_VAR *var;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "")) != -1)
{
switch (opt)
{
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
/* printenv */
if (list == 0)
{
maybe_make_export_env (); /* this allows minimal code */
for (envp = export_env; *envp; envp++)
printf ("%s\n", *envp);
return (EXECUTION_SUCCESS);
}
/* printenv varname */
var = find_variable (list->word->word);
if (var == 0 || (exported_p (var) == 0))
return (EXECUTION_FAILURE);
if (function_p (var))
print_var_function (var);
else
print_var_value (var, 0);
printf("\n");
return (EXECUTION_SUCCESS);
}
char *printenv_doc[] = {
"Display environment.",
"",
"Print names and values of environment variables",
(char *)NULL
};
struct builtin printenv_struct = {
"printenv",
printenv_builtin,
BUILTIN_ENABLED,
printenv_doc,
"printenv [varname]",
0
};

117
examples/loadables/push.c Normal file
View file

@ -0,0 +1,117 @@
/*
* push - anyone remember TOPS-20?
*
*/
/*
Copyright (C) 1999-2020 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "jobs.h"
#include "bashgetopt.h"
#include "common.h"
#ifndef errno
extern int errno;
#endif
extern pid_t 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)
{
CASE_HELPOPT;
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, 0);
return (xstatus);
}
}
char *push_doc[] = {
"Create child shell.",
"",
"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
};

View file

@ -0,0 +1,207 @@
/*
* realpath -- canonicalize pathnames, resolving symlinks
*
* usage: realpath [-cqsv] [-a name] pathname [pathname...]
*
* options: -a name assign each canonicalized pathname to indexed array
* variable NAME
* -c check whether or not each resolved path exists
* -q no output, exit status determines whether path is valid
* -s strip . and .. from the pathname only, no symlink resolution
* -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
*/
/*
Copyright (C) 1999-2009,2021,2022 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#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"
#include "common.h"
#ifndef errno
extern int errno;
#endif
extern char *sh_realpath();
int
realpath_builtin(WORD_LIST *list)
{
int opt, cflag, vflag, qflag, sflag, aflag, es;
char *r, realbuf[PATH_MAX], *p, *newpath;
struct stat sb;
#if defined (ARRAY_VARS)
arrayind_t ind;
char *aname;
SHELL_VAR *v;
#endif
if (list == 0) {
builtin_usage();
return (EX_USAGE);
}
vflag = cflag = qflag = aflag = sflag = 0;
#if defined (ARRAY_VARS)
aname = NULL;
v = NULL;
ind = 0;
#endif
reset_internal_getopt();
while ((opt = internal_getopt (list, "a:cqsv")) != -1) {
switch (opt) {
#if defined (ARRAY_VARS)
case 'a':
aflag = 1;
aname = list_optarg;
break;
#endif
case 'c':
cflag = 1;
break;
case 'q':
qflag = 1;
break;
case 's':
sflag = 1;
break;
case 'v':
vflag = 1;
break;
CASE_HELPOPT;
default:
builtin_usage();
return (EX_USAGE);
}
}
list = loptend;
if (list == 0) {
builtin_usage();
return (EX_USAGE);
}
#if defined (ARRAY_VARS)
if (aflag && legal_identifier (aname) == 0) {
sh_invalidid(aname);
return (EXECUTION_FAILURE);
}
if (aname && builtin_unbind_variable (aname) == -2)
return (EXECUTION_FAILURE);
if (aname) {
v = find_or_make_array_variable (aname, 1);
if (v == 0 || readonly_p (v) || noassign_p (v)) {
if (v && readonly_p (v))
err_readonly (aname);
return (EXECUTION_FAILURE);
} else if (array_p (v) == 0) {
builtin_error ("%s: not an indexed array", aname);
return (EXECUTION_FAILURE);
}
if (invisible_p (v))
VUNSETATTR (v, att_invisible);
array_flush (array_cell (v));
}
#endif
for (es = EXECUTION_SUCCESS; list; list = list->next) {
p = list->word->word;
if (sflag) {
/* sh_canonpath doesn't convert to absolute pathnames */
newpath = make_absolute(p, get_string_value("PWD"));
r = sh_canonpath(newpath, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
free(newpath);
} else
r = sh_realpath(p, realbuf);
if (r == 0) {
es = EXECUTION_FAILURE;
if (qflag == 0)
builtin_error("%s: cannot resolve: %s", p, strerror(errno));
continue;
}
if (cflag && (stat(r, &sb) < 0)) {
es = EXECUTION_FAILURE;
if (qflag == 0)
builtin_error("%s: %s", p, strerror(errno));
continue;
}
if (aflag) {
bind_array_element (v, ind, r, 0);
ind++;
}
if (qflag == 0) {
if (vflag)
printf ("%s -> ", p);
printf("%s\n", r);
}
if (sflag)
free (r);
}
return es;
}
char *realpath_doc[] = {
"Display pathname in canonical form.",
"",
"Display the canonicalized version of each PATHNAME argument, resolving",
"symbolic links.",
"The -a option stores each canonicalized PATHNAME argument into the indexed",
"array VARNAME.",
"The -c option checks whether or not each resolved name exists.",
"The -q option produces no output; the exit status determines the",
"validity of each PATHNAME, but any array assignment is still performed.",
"If the -s option is supplied, canonicalize . and .. pathname components",
"without resolving symbolic links.",
"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 [-a varname] [-cqsv] pathname [pathname...]", /* usage synopsis */
0 /* reserved for internal use */
};

185
examples/loadables/rm.c Normal file
View file

@ -0,0 +1,185 @@
/* rm - remove files and directories with -r */
/* See Makefile for compilation details. */
/*
Copyright (C) 2016 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <dirent.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#include "bashgetopt.h"
#if !defined (errno)
extern int errno;
#endif
static int rm_file(const char *fname);
static int force, recursive;
static int
_remove_directory(const char *dirname)
{
DIR *dir;
struct dirent *dp;
size_t dirlen;
int err;
dirlen = strlen (dirname);
err = 0;
if ((dir = opendir(dirname)))
{
while ((dp = readdir(dir)))
{
#ifdef __GNUC__
char fname[dirlen + 1 + strlen (dp->d_name) + 1];
#else
char *fname;
int fnsize;
#endif
QUIT;
if (*dp->d_name == '.' && (dp->d_name[1] == 0 || (dp->d_name[1] == '.' && dp->d_name[2] == 0)))
continue;
#ifdef __GNUC__
snprintf(fname, sizeof (fname), "%s/%s", dirname, dp->d_name);
#else
fnsize = dirlen + 1 + strlen (dp->d_name) + 1;
fname = xmalloc (fnsize);
snprintf(fname, fnsize, "%s/%s", dirname, dp->d_name);
#endif
if (rm_file (fname) && force == 0)
err = 1;
#ifndef __GNUC__
free (fname);
#endif
QUIT;
}
closedir(dir);
if (err == 0 && rmdir (dirname) && force == 0)
err = 1;
}
else if (force == 0)
err = 1;
if (err)
builtin_error ("%s: %s", dirname, strerror (errno));
return err;
}
static int
rm_file(const char *fname)
{
if (unlink (fname) == 0)
return 0;
QUIT;
/* If FNAME is a directory glibc returns EISDIR but correct POSIX value
would be EPERM. If we get that error and FNAME is a directory and -r
was supplied, recursively remove the directory and its contents */
if ((errno == EISDIR || errno == EPERM) && recursive && file_isdir (fname))
return _remove_directory(fname);
else if (force)
return 0;
builtin_error ("%s: %s", fname, strerror (errno));
return 1;
}
int
rm_builtin (list)
WORD_LIST *list;
{
const char *name;
WORD_LIST *l;
int rval, opt;
recursive = force = 0;
rval = EXECUTION_SUCCESS;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "Rrfi")) != -1)
{
switch (opt)
{
case 'R':
case 'r':
recursive = 1;
break;
case 'f':
force = 1;
break;
case 'i':
return (EX_DISKFALLBACK);
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (list == 0)
{
if (force == 0)
{
builtin_usage ();
return (EXECUTION_FAILURE);
}
return (EXECUTION_SUCCESS);
}
for (l = list; l; l = l->next)
{
QUIT;
if (rm_file(l->word->word) && force == 0)
rval = EXECUTION_FAILURE;
}
return rval;
}
char *rm_doc[] = {
"Remove files.",
"",
"rm removes the files specified as arguments.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. */
struct builtin rm_struct = {
"rm", /* builtin name */
rm_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
rm_doc, /* array of long documentation strings. */
"rm [-rf] file ...", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,72 @@
/* rmdir - remove directory */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif
int
rmdir_builtin (list)
WORD_LIST *list;
{
int rval;
WORD_LIST *l;
if (no_options (list))
return (EX_USAGE);
for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
if (rmdir (l->word->word) < 0)
{
builtin_error ("%s: %s", l->word->word, strerror (errno));
rval = EXECUTION_FAILURE;
}
return rval;
}
char *rmdir_doc[] = {
"Remove directory.",
"",
"rmdir removes the directory entry specified by each argument,",
"provided the directory is empty.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. */
struct builtin rmdir_struct = {
"rmdir", /* builtin name */
rmdir_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
rmdir_doc, /* array of long documentation strings. */
"rmdir directory ...", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

502
examples/loadables/seq.c Normal file
View file

@ -0,0 +1,502 @@
/* seq - print sequence of numbers to standard output.
Copyright (C) 2018-2020 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* Written as bash builtin by Chet Ramey. Portions from seq.c by Ulrich Drepper. */
#include <config.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <stdio.h>
#include <errno.h>
#include "bashansi.h"
#include "loadables.h"
#include "bashintl.h"
#ifndef errno
extern int errno;
#endif
#if defined (PRI_MACROS_BROKEN)
# undef PRIdMAX
#endif
#if !defined (PRIdMAX)
# if HAVE_LONG_LONG
# define PRIdMAX "lld"
# else
# define PRIdMAX "ld"
# endif
#endif
#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD && !defined(STRTOLD_BROKEN)
typedef long double floatmax_t;
# define FLOATMAX_CONV "L"
# define strtofltmax strtold
# define FLOATMAX_FMT "%Lg"
# define FLOATMAX_WFMT "%0.Lf"
# define USE_LONG_DOUBLE
#else
typedef double floatmax_t;
# define FLOATMAX_CONV ""
# define strtofltmax strtod
# define FLOATMAX_FMT "%g"
# define FLOATMAX_WFMT "%0.f"
#endif
static floatmax_t getfloatmax PARAMS((const char *));
static char *genformat PARAMS((floatmax_t, floatmax_t, floatmax_t));
#define MAX(a, b) (((a) < (b))? (b) : (a))
static int conversion_error = 0;
/* If true print all number with equal width. */
static int equal_width;
/* The string used to separate two numbers. */
static char const *separator;
/* The string output after all numbers have been output. */
static char const terminator[] = "\n";
static char decimal_point;
/* Pretty much the same as the version in builtins/printf.def */
static floatmax_t
getfloatmax (arg)
const char *arg;
{
floatmax_t ret;
char *ep;
errno = 0;
ret = strtofltmax (arg, &ep);
if (*ep)
{
sh_invalidnum ((char *)arg);
conversion_error = 1;
}
else if (errno == ERANGE)
{
builtin_error ("warning: %s: %s", arg, strerror(ERANGE));
conversion_error = 1;
}
if (ret == -0.0)
ret = 0.0;
return (ret);
}
/* If FORMAT is a valid printf format for a double argument, return
its long double equivalent, allocated from dynamic storage. This
was written by Ulrich Drepper, taken from coreutils:seq.c */
static char *
long_double_format (char const *fmt)
{
size_t i;
size_t length_modifier_offset;
int has_L;
for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
{
if (!fmt[i])
{
builtin_error ("format %s has no %% directive", fmt);
return 0;
}
}
i++;
i += strspn (fmt + i, "-+#0 '"); /* zero or more flags */
i += strspn (fmt + i, "0123456789"); /* optional minimum field width */
if (fmt[i] == '.') /* optional precision */
{
i++;
i += strspn (fmt + i, "0123456789");
}
length_modifier_offset = i; /* optional length modifier */
/* we could ignore an 'l' length modifier here */
has_L = (fmt[i] == 'L');
i += has_L;
switch (fmt[i])
{
case '\0':
builtin_error ("format %s ends in %%", fmt);
return 0;
case 'A':
case 'a':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
break;
default:
builtin_error ("format %s has unknown `%%%c' directive", fmt, fmt[i]);
return 0;
}
for (i++; ; i += (fmt[i] == '%') + 1)
if (fmt[i] == '%' && fmt[i + 1] != '%')
{
builtin_error ("format %s has too many %% directives", fmt);
return 0;
}
else if (fmt[i] == 0)
{
size_t format_size = i + 1;
char *ldfmt = xmalloc (format_size + 1);
memcpy (ldfmt, fmt, length_modifier_offset);
#ifdef USE_LONG_DOUBLE
ldfmt[length_modifier_offset] = 'L';
strcpy (ldfmt + length_modifier_offset + 1,
fmt + length_modifier_offset + has_L);
#else
strcpy (ldfmt + length_modifier_offset, fmt + length_modifier_offset);
#endif
return ldfmt;
}
}
/* Return the number of digits following the decimal point in NUMBUF */
static int
getprec (numbuf)
const char *numbuf;
{
int p;
char *dp;
if (dp = strchr (numbuf, decimal_point))
dp++; /* skip over decimal point */
for (p = 0; dp && *dp && ISDIGIT (*dp); dp++)
p++;
return p;
}
/* Return the default format given FIRST, INCR, and LAST. */
static char *
genformat (first, incr, last)
floatmax_t first, incr, last;
{
static char buf[6 + 2 * INT_STRLEN_BOUND (int)];
int wfirst, wlast, width;
int iprec, fprec, lprec, prec;
if (equal_width == 0)
return (FLOATMAX_FMT);
/* OK, we have to figure out the largest number of decimal places. This is
a little more expensive than using the original strings. */
snprintf (buf, sizeof (buf), FLOATMAX_FMT, incr);
iprec = getprec (buf);
wfirst = snprintf (buf, sizeof (buf), FLOATMAX_FMT, first);
fprec = getprec (buf);
prec = MAX (fprec, iprec);
wlast = snprintf (buf, sizeof (buf), FLOATMAX_FMT, last);
lprec = getprec (buf);
/* increase first width by any increased precision in increment */
wfirst += (prec - fprec);
/* adjust last width to use precision from first/incr */
wlast += (prec - lprec);
if (lprec && prec == 0)
wlast--; /* no decimal point */
if (lprec == 0 && prec)
wlast++; /* include decimal point */
if (fprec == 0 && prec)
wfirst++; /* include decimal point */
width = MAX (wfirst, wlast);
if (width)
sprintf (buf, "%%0%d.%d%sf", width, prec, FLOATMAX_CONV);
else
sprintf (buf, "%%.%d%sf", prec, FLOATMAX_CONV);
return buf;
}
int
print_fltseq (fmt, first, last, incr)
const char *fmt;
floatmax_t first, last, incr;
{
int n;
floatmax_t next;
const char *s;
n = 0; /* iteration counter */
s = "";
for (next = first; incr >= 0 ? (next <= last) : (next >= last); next = first + n * incr)
{
QUIT;
if (*s && fputs (s, stdout) == EOF)
return (sh_chkwrite (EXECUTION_FAILURE));
if (printf (fmt, next) < 0)
return (sh_chkwrite (EXECUTION_FAILURE));
s = separator;
n++;
}
if (n > 0 && fputs (terminator, stdout) == EOF)
return (sh_chkwrite (EXECUTION_FAILURE));
return (sh_chkwrite (EXECUTION_SUCCESS));
}
/* must be <= INT_STRLEN_BOUND(intmax_t) */
int
width_needed (num)
intmax_t num;
{
int ret;
ret = num < 0; /* sign */
if (ret)
num = -num;
do
ret++;
while (num /= 10);
return ret;
}
int
print_intseq (ifirst, ilast, iincr)
intmax_t ifirst, ilast, iincr;
{
char intwfmt[6 + INT_STRLEN_BOUND(int) + sizeof (PRIdMAX)];
const char *s;
intmax_t i, next;
/* compute integer format string */
if (equal_width) /* -w supplied */
{
int wfirst, wlast, width;
wfirst = width_needed (ifirst);
wlast = width_needed (ilast);
width = MAX(wfirst, wlast);
/* The leading %s is for the separator */
snprintf (intwfmt, sizeof (intwfmt), "%%s%%0%u" PRIdMAX, width);
}
/* We could use braces.c:mkseq here but that allocates lots of memory */
s = "";
for (i = ifirst; (ifirst <= ilast) ? (i <= ilast) : (i >= ilast); i = next)
{
QUIT;
/* The leading %s is for the separator */
if (printf (equal_width ? intwfmt : "%s%" PRIdMAX, s, i) < 0)
return (sh_chkwrite (EXECUTION_FAILURE));
s = separator;
next = i + iincr;
}
if (fputs (terminator, stdout) == EOF)
return (sh_chkwrite (EXECUTION_FAILURE));
return (sh_chkwrite (EXECUTION_SUCCESS));
}
int
seq_builtin (list)
WORD_LIST *list;
{
floatmax_t first, last, incr;
intmax_t ifirst, ilast, iincr;
WORD_LIST *l;
int opt, nargs, intseq, freefmt;
char *first_str, *incr_str, *last_str;
char const *fmtstr; /* The printf(3) format used for output. */
equal_width = 0;
separator = "\n";
fmtstr = NULL;
first = 1.0;
last = 0.0;
incr = 0.0; /* set later */
ifirst = ilast = iincr = 0;
first_str = incr_str = last_str = 0;
intseq = freefmt = 0;
opt = 0;
reset_internal_getopt ();
while (opt != -1)
{
l = lcurrent ? lcurrent : list;
if (l && l->word && l->word->word && l->word->word[0] == '-' &&
(l->word->word[1] == '.' || DIGIT (l->word->word[1])))
{
loptend = l;
break; /* negative number */
}
if ((opt = internal_getopt (list, "f:s:w")) == -1)
break;
switch (opt)
{
case 'f':
fmtstr = list_optarg;
break;
case 's':
separator = list_optarg;
break;
case 'w':
equal_width = 1;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (list == 0)
{
builtin_usage ();
return (EXECUTION_FAILURE);
}
for (nargs = 1, l = list; l->next; l = l->next)
nargs++;
if (nargs > 3)
{
builtin_usage ();
return (EXECUTION_FAILURE);
}
/* LAST */
conversion_error = 0;
last = getfloatmax (last_str = l->word->word);
if (conversion_error)
return (EXECUTION_FAILURE);
/* FIRST LAST */
if (nargs > 1)
{
conversion_error = 0;
first = getfloatmax (first_str = list->word->word);
if (conversion_error)
return (EXECUTION_FAILURE);
}
/* FIRST INCR LAST */
if (nargs > 2)
{
conversion_error = 0;
incr = getfloatmax (incr_str = list->next->word->word);
if (conversion_error)
return (EXECUTION_FAILURE);
if (incr == 0.0)
{
builtin_error ("zero %screment", (first < last) ? "in" : "de");
return (EXECUTION_FAILURE);
}
}
/* Sanitize arguments */
if (incr == 0.0)
incr = (first <= last) ? 1.0 : -1.0;
if ((incr < 0.0 && first < last) || (incr > 0 && first > last))
{
builtin_error ("incorrect %screment", (first < last) ? "in" : "de");
return (EXECUTION_FAILURE);
}
/* validate format here */
if (fmtstr)
{
fmtstr = long_double_format (fmtstr);
freefmt = 1;
if (fmtstr == 0)
return (EXECUTION_FAILURE);
}
if (fmtstr != NULL && equal_width)
{
builtin_warning ("-w ignored when the format string is specified");
equal_width = 0;
}
/* Placeholder for later additional conditions */
if (last_str && all_digits (last_str) &&
(first_str == 0 || all_digits (first_str)) &&
(incr_str == 0 || all_digits (incr_str)) &&
fmtstr == NULL)
intseq = 1;
if (intseq)
{
ifirst = (intmax_t)first; /* truncation */
ilast = (intmax_t)last;
iincr = (intmax_t)incr;
return (print_intseq (ifirst, ilast, iincr));
}
decimal_point = locale_decpoint ();
if (fmtstr == NULL)
fmtstr = genformat (first, incr, last);
print_fltseq (fmtstr, first, last, incr);
if (freefmt)
free ((void *)fmtstr);
return sh_chkwrite (EXECUTION_SUCCESS);
}
/* Taken largely from GNU seq. */
char *seq_doc[] = {
"Print numbers from FIRST to LAST, in steps of INCREMENT.",
"",
"-f FORMAT use printf style floating-point FORMAT",
"-s STRING use STRING to separate numbers (default: \\n)",
"-w equalize width by padding with leading zeroes",
"",
"If FIRST or INCREMENT is omitted, it defaults to 1. However, an",
"omitted INCREMENT defaults to -1 when LAST is smaller than FIRST.",
"The sequence of numbers ends when the sum of the current number and",
"INCREMENT would become greater than LAST.",
"FIRST, INCREMENT, and LAST are interpreted as floating point values.",
"",
"FORMAT must be suitable for printing one argument of type 'double';",
"it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point",
"decimal numbers with maximum precision PREC, and to %g otherwise.",
(char *)NULL
};
struct builtin seq_struct = {
"seq",
seq_builtin,
BUILTIN_ENABLED,
seq_doc,
"seq [-f format] [-s separator] [-w] [FIRST [INCR]] LAST",
0
};

View file

@ -0,0 +1,121 @@
/* setpgid.c: bash loadable wrapper for setpgid system call
An example of how to wrap a system call with a loadable builtin.
Originally contributed by Jason Vas Dias <jason.vas.dias@gmail.com>
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <errno.h>
#include <string.h>
#include "bashtypes.h"
#include "posixtime.h"
#include <stdio.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#include "bashgetopt.h"
#if !defined (_POSIX_VERSION)
# define setpgid(pid, pgrp) setpgrp (pid, pgrp)
#endif
int
setpgid_builtin (list)
WORD_LIST *list;
{
register WORD_LIST *wl;
intmax_t pid_arg, pgid_arg;
pid_t pid, pgid;
char *pidstr, *pgidstr;
wl = list;
pid = pgid = 0;
if (wl == 0 || wl->next == 0)
{
builtin_usage ();
return (EX_USAGE);
}
pidstr = wl->word ? wl->word->word : 0;
pgidstr = wl->next->word ? wl->next->word->word : 0;
if (pidstr == 0 || pgidstr == 0)
{
builtin_usage ();
return (EX_USAGE);
}
if (legal_number (pidstr, &pid_arg) == 0)
{
builtin_error ("%s: pid argument must be numeric", pidstr);
return (EXECUTION_FAILURE);
}
if (pid_arg < 0)
{
builtin_error("%s: negative pid values not allowed", pidstr);
return (EXECUTION_FAILURE);
}
pid = pid_arg;
if (legal_number (pgidstr, &pgid_arg) == 0)
{
builtin_error ("%s: pgrp argument must be numeric", pgidstr);
return (EXECUTION_FAILURE);
}
if (pgid_arg < 0)
{
builtin_error ("%s: negative pgrp values not allowed", pgidstr);
return (EXECUTION_FAILURE);
}
pgid = pgid_arg;
errno = 0;
if (setpgid(pid, pgid) < 0)
{
builtin_error("setpgid failed: %s", strerror (errno));
return (EXECUTION_FAILURE);
}
return (EXECUTION_SUCCESS);
}
const char *setpgid_doc[] = {
"invoke the setpgid(2) system call",
"",
"Arguments:",
" pid : numeric process identifier, >= 0",
" pgrpid: numeric process group identifier, >=0",
"See the setpgid(2) manual page.",
(const char *)NULL
};
struct builtin setpgid_struct = {
"setpgid",
setpgid_builtin,
BUILTIN_ENABLED,
(char **)setpgid_doc,
"setpgid pid pgrpid",
0
};

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

@ -0,0 +1,179 @@
/*
* sleep -- sleep for fractions of a second
*
* usage: sleep seconds[.fraction]
*
* as an extension, we support the GNU time interval format (2m20s)
*/
/*
Copyright (C) 1999-2021 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "bashtypes.h"
#if defined (TIME_WITH_SYS_TIME)
# include <sys/time.h>
# include <time.h>
#else
# if defined (HAVE_SYS_TIME_H)
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#if defined (HAVE_UNISTD_H)
#include <unistd.h>
#endif
#include <stdio.h>
#include "chartypes.h"
#include "loadables.h"
#define S_SEC 1
#define S_MIN (60*S_SEC)
#define S_HOUR (60*S_MIN)
#define S_DAY (24*S_HOUR)
static int
parse_gnutimefmt (char *string, long *sp, long *up)
{
int c, r;
char *s, *ep;
long tsec, tusec, accumsec, accumusec, t;
int mult;
tsec = tusec = 0;
accumsec = accumusec = 0;
mult = 1;
for (s = string; s && *s; s++) {
r = uconvert(s, &accumsec, &accumusec, &ep);
if (r == 0 && *ep == 0)
return r;
c = *ep;
mult = 1;
switch (c) {
case '\0':
case 's':
mult = S_SEC;
break;
case 'm':
mult = S_MIN;
break;
case 'h':
mult = S_HOUR;
break;
case 'd':
mult = S_DAY;
break;
default:
return 0;
}
/* multiply the accumulated value by the multiplier */
t = accumusec * mult;
accumsec = accumsec * mult + (t / 1000000);
accumusec = t % 1000000;
/* add to running total */
tsec += accumsec;
tusec += accumusec;
if (tusec >= 1000000) {
tsec++;
tusec -= 1000000;
}
/* reset and continue */
accumsec = accumusec = 0;
mult = 1;
if (c == 0)
break;
s = ep;
}
if (sp)
*sp = tsec;
if (up)
*up = tusec;
return 1;
}
int
sleep_builtin (WORD_LIST *list)
{
long sec, usec;
char *ep;
int r, mul;
time_t t;
if (list == 0) {
builtin_usage();
return(EX_USAGE);
}
/* Skip over `--' */
if (list->word && ISOPTION (list->word->word, '-'))
list = list->next;
if (*list->word->word == '-' || list->next) {
builtin_usage ();
return (EX_USAGE);
}
r = uconvert(list->word->word, &sec, &usec, &ep);
/*
* Maybe postprocess conversion failures here based on EP
*
* A heuristic: if the conversion failed, but the argument appears to
* contain a GNU-like interval specifier (e.g. "1m30s"), try to parse
* it. If we can't, return the right exit code to tell
* execute_builtin to try and execute a disk command instead.
*/
if (r == 0 && (strchr ("dhms", *ep) || strpbrk (list->word->word, "dhms")))
r = parse_gnutimefmt (list->word->word, &sec, &usec);
if (r) {
fsleep(sec, usec);
QUIT;
return(EXECUTION_SUCCESS);
}
builtin_error("%s: bad sleep interval", list->word->word);
return (EXECUTION_FAILURE);
}
static char *sleep_doc[] = {
"Suspend execution for specified period.",
""
"sleep suspends execution for a minimum of SECONDS[.FRACTION] seconds.",
"As an extension, sleep accepts GNU-style time intervals (e.g., 2m30s).",
(char *)NULL
};
struct builtin sleep_struct = {
"sleep",
sleep_builtin,
BUILTIN_ENABLED,
sleep_doc,
"sleep seconds[.fraction]",
0
};

464
examples/loadables/stat.c Normal file
View file

@ -0,0 +1,464 @@
/* stat - load up an associative array with stat information about a file */
/* See Makefile for compilation details. */
/*
Copyright (C) 2016,2022 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include <stdio.h>
#include <sys/types.h>
#include "posixstat.h"
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include "posixtime.h"
#include "bashansi.h"
#include "shell.h"
#include "builtins.h"
#include "common.h"
#include "bashgetopt.h"
#ifndef errno
extern int errno;
#endif
#define ST_NAME 0
#define ST_DEV 1
#define ST_INO 2
#define ST_MODE 3
#define ST_NLINK 4
#define ST_UID 5
#define ST_GID 6
#define ST_RDEV 7
#define ST_SIZE 8
#define ST_ATIME 9
#define ST_MTIME 10
#define ST_CTIME 11
#define ST_BLKSIZE 12
#define ST_BLOCKS 13
#define ST_CHASELINK 14
#define ST_PERMS 15
#define ST_END 16
static char *arraysubs[] =
{
"name", "device", "inode", "type", "nlink", "uid", "gid", "rdev",
"size", "atime", "mtime", "ctime", "blksize", "blocks", "link", "perms",
0
};
#define DEFTIMEFMT "%a %b %e %k:%M:%S %Z %Y"
#ifndef TIMELEN_MAX
# define TIMELEN_MAX 128
#endif
static char *stattime (time_t, const char *);
static int
getstat (fname, flags, sp)
const char *fname;
int flags;
struct stat *sp;
{
intmax_t lfd;
int fd, r;
if (strncmp (fname, "/dev/fd/", 8) == 0)
{
if ((legal_number(fname + 8, &lfd) == 0) || (int)lfd != lfd)
{
errno = EINVAL;
return -1;
}
fd = lfd;
r = fstat(fd, sp);
}
#ifdef HAVE_LSTAT
else if (flags & 1)
r = lstat(fname, sp);
#endif
else
r = stat(fname, sp);
return r;
}
static char *
statlink (fname, sp)
char *fname;
struct stat *sp;
{
#if defined (HAVE_READLINK)
char linkbuf[PATH_MAX];
int n;
if (fname && S_ISLNK (sp->st_mode) && (n = readlink (fname, linkbuf, PATH_MAX)) > 0)
{
linkbuf[n] = '\0';
return (savestring (linkbuf));
}
else
#endif
return (savestring (fname));
}
static char *
octalperms (m)
int m;
{
int operms;
char *ret;
operms = 0;
if (m & S_IRUSR)
operms |= 0400;
if (m & S_IWUSR)
operms |= 0200;
if (m & S_IXUSR)
operms |= 0100;
if (m & S_IRGRP)
operms |= 0040;
if (m & S_IWGRP)
operms |= 0020;
if (m & S_IXGRP)
operms |= 0010;
if (m & S_IROTH)
operms |= 0004;
if (m & S_IWOTH)
operms |= 0002;
if (m & S_IXOTH)
operms |= 0001;
if (m & S_ISUID)
operms |= 04000;
if (m & S_ISGID)
operms |= 02000;
if (m & S_ISVTX)
operms |= 01000;
ret = (char *)xmalloc (16);
snprintf (ret, 16, "%04o", operms);
return ret;
}
static char *
statperms (m)
int m;
{
char ubits[4], gbits[4], obits[4]; /* u=rwx,g=rwx,o=rwx */
int i;
char *ret;
i = 0;
if (m & S_IRUSR)
ubits[i++] = 'r';
if (m & S_IWUSR)
ubits[i++] = 'w';
if (m & S_IXUSR)
ubits[i++] = 'x';
ubits[i] = '\0';
i = 0;
if (m & S_IRGRP)
gbits[i++] = 'r';
if (m & S_IWGRP)
gbits[i++] = 'w';
if (m & S_IXGRP)
gbits[i++] = 'x';
gbits[i] = '\0';
i = 0;
if (m & S_IROTH)
obits[i++] = 'r';
if (m & S_IWOTH)
obits[i++] = 'w';
if (m & S_IXOTH)
obits[i++] = 'x';
obits[i] = '\0';
if (m & S_ISUID)
ubits[2] = (m & S_IXUSR) ? 's' : 'S';
if (m & S_ISGID)
gbits[2] = (m & S_IXGRP) ? 's' : 'S';
if (m & S_ISVTX)
obits[2] = (m & S_IXOTH) ? 't' : 'T';
ret = (char *)xmalloc (32);
snprintf (ret, 32, "u=%s,g=%s,o=%s", ubits, gbits, obits);
return ret;
}
static char *
statmode(mode)
int mode;
{
char *modestr, *m;
modestr = m = (char *)xmalloc (8);
if (S_ISBLK (mode))
*m++ = 'b';
if (S_ISCHR (mode))
*m++ = 'c';
if (S_ISDIR (mode))
*m++ = 'd';
if (S_ISREG(mode))
*m++ = '-';
if (S_ISFIFO(mode))
*m++ = 'p';
if (S_ISLNK(mode))
*m++ = 'l';
if (S_ISSOCK(mode))
*m++ = 's';
#ifdef S_ISDOOR
if (S_ISDOOR (mode))
*m++ = 'D';
#endif
#ifdef S_ISWHT
if (S_ISWHT(mode))
*m++ = 'W';
#endif
#ifdef S_ISNWK
if (S_ISNWK(mode))
*m++ = 'n';
#endif
#ifdef S_ISMPC
if (S_ISMPC (mode))
*m++ = 'm';
#endif
*m = '\0';
return (modestr);
}
static char *
stattime (t, timefmt)
time_t t;
const char *timefmt;
{
char *tbuf, *ret;
const char *fmt;
size_t tlen;
struct tm *tm;
fmt = timefmt ? timefmt : DEFTIMEFMT;
tm = localtime (&t);
ret = xmalloc (TIMELEN_MAX);
tlen = strftime (ret, TIMELEN_MAX, fmt, tm);
if (tlen == 0)
tlen = strftime (ret, TIMELEN_MAX, DEFTIMEFMT, tm);
return ret;
}
static char *
statval (which, fname, flags, fmt, sp)
int which;
char *fname;
int flags;
char *fmt;
struct stat *sp;
{
int temp;
switch (which)
{
case ST_NAME:
return savestring (fname);
case ST_DEV:
return itos (sp->st_dev);
case ST_INO:
return itos (sp->st_ino);
case ST_MODE:
return (statmode (sp->st_mode));
case ST_NLINK:
return itos (sp->st_nlink);
case ST_UID:
return itos (sp->st_uid);
case ST_GID:
return itos (sp->st_gid);
case ST_RDEV:
return itos (sp->st_rdev);
case ST_SIZE:
return itos (sp->st_size);
case ST_ATIME:
return ((flags & 2) ? stattime (sp->st_atime, fmt) : itos (sp->st_atime));
case ST_MTIME:
return ((flags & 2) ? stattime (sp->st_mtime, fmt) : itos (sp->st_mtime));
case ST_CTIME:
return ((flags & 2) ? stattime (sp->st_ctime, fmt) : itos (sp->st_ctime));
case ST_BLKSIZE:
return itos (sp->st_blksize);
case ST_BLOCKS:
return itos (sp->st_blocks);
case ST_CHASELINK:
return (statlink (fname, sp));
case ST_PERMS:
temp = sp->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO|S_ISUID|S_ISGID);
return (flags & 2) ? statperms (temp) : octalperms (temp);
default:
return savestring ("42");
}
}
static int
loadstat (vname, var, fname, flags, fmt, sp)
char *vname;
SHELL_VAR *var;
char *fname;
int flags;
char *fmt;
struct stat *sp;
{
int i;
char *key, *value;
SHELL_VAR *v;
for (i = 0; arraysubs[i]; i++)
{
key = savestring (arraysubs[i]);
value = statval (i, fname, flags, fmt, sp);
v = bind_assoc_variable (var, vname, key, value, ASS_FORCE);
}
return 0;
}
int
stat_builtin (list)
WORD_LIST *list;
{
int opt, flags;
char *aname, *fname, *timefmt;
struct stat st;
SHELL_VAR *v;
aname = "STAT";
flags = 0;
timefmt = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "A:F:Ll")) != -1)
{
switch (opt)
{
case 'A':
aname = list_optarg;
break;
case 'L':
flags |= 1; /* operate on links rather than resolving them */
break;
case 'l':
flags |= 2;
break;
case 'F':
timefmt = list_optarg;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
if (legal_identifier (aname) == 0)
{
sh_invalidid (aname);
return (EXECUTION_FAILURE);
}
list = loptend;
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
#if 0
unbind_variable (aname);
#endif
fname = list->word->word;
if (getstat (fname, flags, &st) < 0)
{
builtin_error ("%s: cannot stat: %s", fname, strerror (errno));
return (EXECUTION_FAILURE);
}
v = find_or_make_array_variable (aname, 3);
if (v == 0)
{
builtin_error ("%s: cannot create variable", aname);
return (EXECUTION_FAILURE);
}
if (loadstat (aname, v, fname, flags, timefmt, &st) < 0)
{
builtin_error ("%s: cannot assign file status information", aname);
unbind_variable (aname);
return (EXECUTION_FAILURE);
}
return (EXECUTION_SUCCESS);
}
/* An array of strings forming the `long' documentation for a builtin xxx,
which is printed by `help xxx'. It must end with a NULL. By convention,
the first line is a short description. */
char *stat_doc[] = {
"Load an associative array with file status information.",
"",
"Take a filename and load the status information returned by a",
"stat(2) call on that file into the associative array specified",
"by the -A option. The default array name is STAT.",
"",
"If the -L option is supplied, stat does not resolve symbolic links",
"and reports information about the link itself. The -l option results",
"in longer-form listings for some of the fields. When -l is used,",
"the -F option supplies a format string passed to strftime(3) to",
"display the file time information.",
"The exit status is 0 unless the stat fails or assigning the array",
"is unsuccessful.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. The flags must include BUILTIN_ENABLED so the
builtin can be used. */
struct builtin stat_struct = {
"stat", /* builtin name */
stat_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
stat_doc, /* array of long documentation strings. */
"stat [-lL] [-A aname] file", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,128 @@
/* strftime - loadable builtin interface to strftime(3) */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashtypes.h"
#include "posixtime.h"
#include <stdio.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#include "bashgetopt.h"
int
strftime_builtin (list)
WORD_LIST *list;
{
char *format, *tbuf;
size_t tbsize, tsize;
time_t secs;
struct tm *t;
int n;
intmax_t i;
if (no_options (list))
return (EX_USAGE);
list = loptend;
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
format = list->word->word;
if (format == 0 || *format == 0)
{
printf ("\n");
return (EXECUTION_SUCCESS);
}
list = list->next;
if (list && list->word->word)
{
n = legal_number (list->word->word, &i);
if (n == 0 || i < 0 || i != (time_t)i)
{
sh_invalidnum (list->word->word);
return (EXECUTION_FAILURE);
}
else
secs = i;
}
else
secs = NOW;
t = localtime (&secs);
tbsize = strlen (format) * 4;
tbuf = 0;
/* Now try to figure out how big the buffer should really be. strftime(3)
will return the number of bytes placed in the buffer unless it's greater
than MAXSIZE, in which case it returns 0. */
for (n = 1; n <= 8; n++)
{
tbuf = xrealloc (tbuf, tbsize * n);
tsize = strftime (tbuf, tbsize * n, format, t);
if (tsize)
break;
}
if (tsize)
printf ("%s\n", tbuf);
free (tbuf);
return (EXECUTION_SUCCESS);
}
/* An array of strings forming the `long' documentation for a builtin xxx,
which is printed by `help xxx'. It must end with a NULL. */
char *strftime_doc[] = {
"Display formatted time.",
"",
"Converts date and time format to a string and displays it on the",
"standard output. If the optional second argument is supplied, it",
"is used as the number of seconds since the epoch to use in the",
"conversion, otherwise the current time is used.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. The flags must include BUILTIN_ENABLED so the
builtin can be used. */
struct builtin strftime_struct = {
"strftime", /* builtin name */
strftime_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
strftime_doc, /* array of long documentation strings. */
"strftime format [seconds]", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

53
examples/loadables/sync.c Normal file
View file

@ -0,0 +1,53 @@
/* sync - sync the disks by forcing pending filesystem writes to complete */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
int
sync_builtin (list)
WORD_LIST *list;
{
sync();
return (EXECUTION_SUCCESS);
}
char *sync_doc[] = {
"Sync disks.",
""
"Force completion of pending disk writes",
(char *)NULL
};
struct builtin sync_struct = {
"sync", /* builtin name */
sync_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
sync_doc, /* array of long documentation strings. */
"sync", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

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

@ -0,0 +1,183 @@
/* tee - duplicate standard input */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2021 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "bashtypes.h"
#include "posixstat.h"
#include "filecntl.h"
#include <signal.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include <stdio.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
#if !defined (errno)
extern int errno;
#endif
typedef struct flist {
struct flist *next;
int fd;
char *fname;
} FLIST;
static FLIST *tee_flist;
#define TEE_BUFSIZE 8192
extern int interrupt_immediately;
extern char *strerror ();
int
tee_builtin (list)
WORD_LIST *list;
{
int opt, append, nointr, rval, fd, fflags;
int n, nr, nw;
FLIST *fl;
char *buf, *bp;
char *t;
reset_internal_getopt ();
append = nointr = 0;
tee_flist = (FLIST *)NULL;
while ((opt = internal_getopt (list, "ai")) != -1)
{
switch (opt)
{
case 'a':
append = 1;
break;
case 'i':
nointr = 1;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (nointr == 0)
interrupt_immediately++;
buf = xmalloc (TEE_BUFSIZE);
/* Initialize output file list. */
fl = tee_flist = (FLIST *)xmalloc (sizeof(FLIST));
tee_flist->fd = 1;
tee_flist->fname = "stdout";
tee_flist->next = (FLIST *)NULL;
/* Add file arguments to list of output files. */
fflags = append ? O_WRONLY|O_CREAT|O_APPEND : O_WRONLY|O_CREAT|O_TRUNC;
for (rval = EXECUTION_SUCCESS; list; list = list->next)
{
fd = open (list->word->word, fflags, 0666);
if (fd < 0)
{
builtin_error ("%s: cannot open: %s", list->word->word, strerror (errno));
rval = EXECUTION_FAILURE;
}
else
{
fl->next = (FLIST *)xmalloc (sizeof(FLIST));
fl->next->fd = fd;
fl->next->fname = list->word->word;
fl = fl->next;
fl->next = (FLIST *)NULL;
}
QUIT;
}
while ((nr = read(0, buf, TEE_BUFSIZE)) > 0)
for (fl = tee_flist; fl; fl = fl->next)
{
n = nr;
bp = buf;
do
{
if ((nw = write (fl->fd, bp, n)) == -1)
{
builtin_error ("%s: write error: %s", fl->fname, strerror (errno));
rval = EXECUTION_FAILURE;
break;
}
bp += nw;
QUIT;
}
while (n -= nw);
}
if (nr < 0)
builtin_error ("read error: %s", strerror (errno));
/* Deallocate resources -- this is a builtin command. */
tee_flist = tee_flist->next; /* skip bogus close of stdout */
while (tee_flist)
{
fl = tee_flist;
if (close (fl->fd) < 0)
{
builtin_error ("%s: close_error: %s", fl->fname, strerror (errno));
rval = EXECUTION_FAILURE;
}
tee_flist = tee_flist->next;
free (fl);
}
QUIT;
return (rval);
}
char *tee_doc[] = {
"Duplicate standard output.",
"",
"Copy standard input to standard output, making a copy in each",
"filename argument. If the `-a' option is given, the specified",
"files are appended to, otherwise they are overwritten. If the",
"`-i' option is supplied, tee ignores interrupts.",
(char *)NULL
};
struct builtin tee_struct = {
"tee", /* builtin name */
tee_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
tee_doc, /* array of long documentation strings. */
"tee [-ai] [file ...]", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,75 @@
/* template - example template for loadable builtin */
/* See Makefile for compilation details. */
#include <config.h>
#if defined (HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "bashansi.h"
#include <stdio.h>
#include <errno.h>
#include "loadables.h"
#if !defined (errno)
extern int errno;
#endif
extern char *strerror ();
int
template_builtin (list)
WORD_LIST *list;
{
int opt, rval;
rval = EXECUTION_SUCCESS;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "")) != -1)
{
switch (opt)
{
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
return (rval);
}
/* Called when `template' is enabled and loaded from the shared object. If this
function returns 0, the load fails. */
int
template_builtin_load (name)
char *name;
{
return (1);
}
/* Called when `template' is disabled. */
void
template_builtin_unload (name)
char *name;
{
}
char *template_doc[] = {
"Short description.",
""
"Longer description of builtin and usage.",
(char *)NULL
};
struct builtin template_struct = {
"template", /* builtin name */
template_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
template_doc, /* array of long documentation strings. */
"template", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,72 @@
/* true and false builtins */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "bashtypes.h"
#include "shell.h"
#include "builtins.h"
#include "common.h"
int
true_builtin (list)
WORD_LIST *list;
{
return EXECUTION_SUCCESS;
}
int
false_builtin (list)
WORD_LIST *list;
{
return EXECUTION_FAILURE;
}
static char *true_doc[] = {
"Exit successfully.",
"",
"Return a successful result.",
(char *)NULL
};
static char *false_doc[] = {
"Exit unsuccessfully.",
"",
"Return an unsuccessful result.",
(char *)NULL
};
struct builtin true_struct = {
"true",
true_builtin,
BUILTIN_ENABLED,
true_doc,
"true",
0
};
struct builtin false_struct = {
"false",
false_builtin,
BUILTIN_ENABLED,
false_doc,
"false",
0
};

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

@ -0,0 +1,83 @@
/* tty - return terminal name */
/* See Makefile for compilation details. */
/*
Copyright (C) 1999-2021 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdio.h>
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
extern char *ttyname ();
int
tty_builtin (list)
WORD_LIST *list;
{
int opt, sflag;
char *t;
reset_internal_getopt ();
sflag = 0;
while ((opt = internal_getopt (list, "s")) != -1)
{
switch (opt)
{
case 's':
sflag = 1;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
t = ttyname (0);
QUIT;
if (sflag == 0)
puts (t ? t : "not a tty");
return (t ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}
char *tty_doc[] = {
"Display terminal name.",
"",
"tty writes the name of the terminal that is opened for standard",
"input to standard output. If the `-s' option is supplied, nothing",
"is written; the exit status determines whether or not the standard",
"input is connected to a tty.",
(char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
of these structures. */
struct builtin tty_struct = {
"tty", /* builtin name */
tty_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
tty_doc, /* array of long documentation strings. */
"tty [-s]", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

162
examples/loadables/uname.c Normal file
View file

@ -0,0 +1,162 @@
/*
* uname - print system information
*
* usage: uname [-amnrsv]
*
*/
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include "bashtypes.h"
#if defined (HAVE_UNAME)
# include <sys/utsname.h>
#else
struct utsname {
char sysname[32];
char nodename[32];
char release[32];
char version[32];
char machine[32];
};
#endif
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
#define FLAG_SYSNAME 0x01 /* -s */
#define FLAG_NODENAME 0x02 /* -n */
#define FLAG_RELEASE 0x04 /* -r */
#define FLAG_VERSION 0x08 /* -v */
#define FLAG_MACHINE 0x10 /* -m, -p */
#define FLAG_ALL 0x1f
#ifndef errno
extern int errno;
#endif
static void uprint();
static int uname_flags;
int
uname_builtin (list)
WORD_LIST *list;
{
int opt, r;
struct utsname uninfo;
uname_flags = 0;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "amnprsv")) != -1)
{
switch (opt)
{
case 'a':
uname_flags |= FLAG_ALL;
break;
case 'm':
case 'p':
uname_flags |= FLAG_MACHINE;
break;
case 'n':
uname_flags |= FLAG_NODENAME;
break;
case 'r':
uname_flags |= FLAG_RELEASE;
break;
case 's':
uname_flags |= FLAG_SYSNAME;
break;
case 'v':
uname_flags |= FLAG_VERSION;
break;
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (list)
{
builtin_usage ();
return (EX_USAGE);
}
if (uname_flags == 0)
uname_flags = FLAG_SYSNAME;
/* Only ancient systems will not have uname(2). */
#ifdef HAVE_UNAME
if (uname (&uninfo) < 0)
{
builtin_error ("cannot get system name: %s", strerror (errno));
return (EXECUTION_FAILURE);
}
#else
builtin_error ("cannot get system information: uname(2) not available");
return (EXECUTION_FAILURE);
#endif
uprint (FLAG_SYSNAME, uninfo.sysname);
uprint (FLAG_NODENAME, uninfo.nodename);
uprint (FLAG_RELEASE, uninfo.release);
uprint (FLAG_VERSION, uninfo.version);
uprint (FLAG_MACHINE, uninfo.machine);
return (EXECUTION_SUCCESS);
}
static void
uprint (flag, info)
int flag;
char *info;
{
if (uname_flags & flag)
{
uname_flags &= ~flag;
printf ("%s%c", info, uname_flags ? ' ' : '\n');
}
}
char *uname_doc[] = {
"Display system information.",
"",
"Display information about the system hardware and OS.",
(char *)NULL
};
struct builtin uname_struct = {
"uname",
uname_builtin,
BUILTIN_ENABLED,
uname_doc,
"uname [-amnrsv]",
0
};

View file

@ -0,0 +1,74 @@
/* unlink - remove a directory entry */
/* Should only be used to remove directories by a superuser prepared to let
fsck clean up the file system. */
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <errno.h>
#include "builtins.h"
#include "shell.h"
#include "common.h"
#ifndef errno
extern int errno;
#endif
int
unlink_builtin (list)
WORD_LIST *list;
{
if (list == 0)
{
builtin_usage ();
return (EX_USAGE);
}
if (unlink (list->word->word) != 0)
{
builtin_error ("%s: cannot unlink: %s", list->word->word, strerror (errno));
return (EXECUTION_FAILURE);
}
return (EXECUTION_SUCCESS);
}
char *unlink_doc[] = {
"Remove a directory entry.",
"",
"Forcibly remove a directory entry, even if it's a directory.",
(char *)NULL
};
struct builtin unlink_struct = {
"unlink", /* builtin name */
unlink_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
unlink_doc, /* array of long documentation strings. */
"unlink name", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};

View file

@ -0,0 +1,75 @@
/*
* whoami - print out username of current user
*/
/*
Copyright (C) 1999-2009 Free Software Foundation, Inc.
This file is part of GNU Bash.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include "builtins.h"
#include "shell.h"
#include "bashgetopt.h"
#include "common.h"
int
whoami_builtin (list)
WORD_LIST *list;
{
int opt;
reset_internal_getopt ();
while ((opt = internal_getopt (list, "")) != -1)
{
switch (opt)
{
CASE_HELPOPT;
default:
builtin_usage ();
return (EX_USAGE);
}
}
list = loptend;
if (list)
{
builtin_usage ();
return (EX_USAGE);
}
if (current_user.user_name == 0)
get_current_user_info ();
printf ("%s\n", current_user.user_name);
return (EXECUTION_SUCCESS);
}
char *whoami_doc[] = {
"Print user name",
"",
"Display name of current user.",
(char *)NULL
};
struct builtin whoami_struct = {
"whoami",
whoami_builtin,
BUILTIN_ENABLED,
whoami_doc,
"whoami",
0
};

View file

@ -0,0 +1,44 @@
#! /bin/bash
#
# aliasconv.bash - convert csh aliases to bash aliases and functions
#
# usage: aliasconv.bash
#
# Chet Ramey
# chet@po.cwru.edu
#
trap 'rm -f $TMPFILE' 0 1 2 3 6 15
TMPFILE=$(mktemp -t cb.XXXXXX) || exit 1
T=$'\t'
cat << \EOF >$TMPFILE
mkalias ()
{
case $2 in
'') echo alias ${1}="''" ;;
*[#\!]*)
comm=$(echo $2 | sed 's/\!\*/"$\@"/g
s/\!:\([1-9]\)/"$\1"/g
s/#/\#/g')
echo $1 \(\) "{" command "$comm" "; }"
;;
*) echo alias ${1}=\'$(echo "${2}" | sed "s:':'\\\\'':g")\' ;;
esac
}
EOF
# the first thing we want to do is to protect single quotes in the alias,
# since they whole thing is going to be surrounded by single quotes when
# passed to mkalias
sed -e "s:':\\'\\\'\\':" -e "s/^\([a-zA-Z0-9_-]*\)$T\(.*\)$/mkalias \1 '\2'/" >>$TMPFILE
$BASH $TMPFILE | sed -e 's/\$cwd/\$PWD/g' \
-e 's/\$term/\$TERM/g' \
-e 's/\$home/\$HOME/g' \
-e 's/\$user/\$USER/g' \
-e 's/\$prompt/\$PS1/g'
exit 0

View file

@ -0,0 +1,42 @@
#! /bin/bash
#
# aliasconv.sh - convert csh aliases to bash aliases and functions
#
# usage: aliasconv.sh
#
# Chet Ramey
# chet@po.cwru.edu
#
trap 'rm -f $TMPFILE' 0 1 2 3 6 15
TMPFILE=$(mktemp -t cb.XXXXXX) || exit 1
T=' '
cat << \EOF >$TMPFILE
mkalias ()
{
case $2 in
'') echo alias ${1}="''" ;;
*[#\!]*)
comm=`echo $2 | sed 's/\\!\*/"$\@"/g
s/\\!:\([1-9]\)/"$\1"/g
s/#/\#/g'`
echo $1 \(\) "{" command "$comm" "; }"
;;
*) echo alias ${1}=\'`echo "${2}" | sed "s:':'\\\\\\\\'':"`\' ;;
esac
}
EOF
# the first thing we want to do is to protect single quotes in the alias,
# since they whole thing is going to be surrounded by single quotes when
# passed to mkalias
sed -e "s:':\\'\\\'\\':" -e "s/^\([a-zA-Z0-9_-]*\)$T\(.*\)$/mkalias \1 '\2'/" >>$TMPFILE
sh $TMPFILE | sed -e 's/\$cwd/\$PWD/g' \
-e 's/\$term/\$TERM/g' \
-e 's/\$home/\$HOME/g' \
-e 's/\$user/\$USER/g' \
-e 's/\$prompt/\$PS1/g'
exit 0

139
examples/misc/cshtobash Normal file
View file

@ -0,0 +1,139 @@
#! /bin/bash
#
# cshtobash - convert csh aliases, environment variables, and variables to
# bash equivalents
#
# usage: cshtobash [filename]
#
# If filename is given, that file is sourced. Note that csh always
# sources .cshrc. To recreate your csh login environment, run
# `cshtobash ~/.login'.
#
# Inspired by (and some borrowed from) a similar program distributed with
# zsh-3.0.
#
# Chet Ramey
# chet@po.cwru.edu
#
trap 'rm -f $TMPFILE1 $TMPFILEa $TMPFILEe $TMPFILEv $TMPFILEco $TMPFILEci' 0 1 2 3 6 15
{ TMPFILE1=$(mktemp -t cb.1.XXXXXX) &&
TMPFILEa=$(mktemp -t cb.a.XXXXXX) &&
TMPFILEe=$(mktemp -t cb.e.XXXXXX) &&
TMPFILEv=$(mktemp -t cb.v.XXXXXX) &&
TMPFILEco=$(mktemp -t cshout.XXXXXX) &&
TMPFILEci=$(mktemp -t cshin.XXXXXX)
} || exit 1
T=$'\t'
SOURCE="${1:+source $1}"
cat << EOF >$TMPFILEci
$SOURCE
alias >! $TMPFILEa
setenv >! $TMPFILEe
set >! $TMPFILEv
EOF
# give csh a minimal environment, similar to what login would provide
/usr/bin/env - USER=$USER HOME=$HOME PATH=/usr/bin:/bin:/usr/ucb:. TERM=$TERM SHELL=$SHELL /bin/csh -i < $TMPFILEci > $TMPFILEco 2>&1
# First convert aliases
cat << \EOF >$TMPFILE1
mkalias ()
{
case $2 in
'') echo alias ${1}="''" ;;
*[#\!]*)
comm=$(echo $2 | sed 's/\!\*/"$\@"/g
s/\!:\([1-9]\)/"$\1"/g
s/#/\#/g')
echo $1 \(\) "{" command "$comm" "; }"
;;
*) echo alias ${1}=\'$(echo "${2}" | sed "s:':'\\\\'':")\' ;;
esac
}
EOF
sed "s/^\([a-zA-Z0-9_]*\)$T\(.*\)$/mkalias \1 '\2'/" < $TMPFILEa >>$TMPFILE1
echo '# csh aliases'
echo
$BASH $TMPFILE1 | sed -e 's/\$cwd/\$PWD/g' \
-e 's/\$term/\$TERM/g' \
-e 's/\$home/\$HOME/g' \
-e 's/\$user/\$USER/g' \
-e 's/\$prompt/\$PS1/g'
# Next, convert environment variables
echo
echo '# csh environment variables'
echo
# Would be nice to deal with embedded newlines, e.g. in TERMCAP, but ...
sed -e '/^SHLVL/d' \
-e '/^PWD/d' \
-e "s/'/'"\\\\"''"/g \
-e "s/^\([A-Za-z0-9_]*=\)/export \1'/" \
-e "s/$/'/" < $TMPFILEe
# Finally, convert local variables
echo
echo '# csh variables'
echo
sed -e 's/'"$T"'/=/' \
-e "s/'/'"\\\\"''"/g \
-e '/^[A-Za-z0-9_]*=[^(]/{
s/=/='"'/"'
s/$/'"'/"'
}' < $TMPFILEv |
sed -e '/^argv=/d' -e '/^cwd=/d' -e '/^filec=/d' -e '/^status=/d' \
-e '/^verbose=/d' \
-e '/^term=/d' \
-e '/^home=/d' \
-e '/^path=/d' \
-e '/^user=/d' \
-e '/^shell=/d' \
-e '/^cdpath=/d' \
-e '/^mail=/d' \
-e '/^home=/s//HOME=/' \
-e '/^prompt=/s//PS1=/' \
-e '/^histfile=/s//HISTFILE=/' \
-e '/^history=/s//HISTSIZE=/' \
-e '/^savehist=$/s//HISTFILESIZE=${HISTSIZE-500}/' \
-e '/^savehist=/s//HISTFILESIZE=/' \
-e '/^ignoreeof=$/s/^.*$/set -o ignoreeof # ignoreeof/' \
-e '/^ignoreeof=/s//IGNOREEOF=/' \
-e '/^noclobber=/s/^.*$/set -C # noclobber/' \
-e '/^notify=/s/^.*$/set -b # notify/' \
-e '/^noglob=/s/^.*$/set -f # noglob/' \
# now some special csh variables converted to bash equivalents
echo
echo '# special csh variables converted to bash equivalents'
echo
sed -e 's/'"$T"'/=/' < $TMPFILEv |
grep "^cdpath=" |
sed 's/(//
s/ /:/g
s/)//
s/cdpath=/CDPATH=/'
sed -e 's/'"$T"'/=/' < $TMPFILEv |
grep "^mail=" |
sed 's/(//
s/ /:/g
s/)//
s/mail=/MAILPATH=/' |
sed -e 's/MAILPATH=\([0-9][0-9][^:]*\)$/MAILCHECK=\1/' \
-e 's/MAILPATH=\([0-9][0-9][^:]*\):\(.*\)/MAILCHECK=\1 MAILPATH=\2/'
exit 0

104
examples/scripts/bcalc Normal file
View file

@ -0,0 +1,104 @@
#! /bin/bash
#
# bcalc - a coproc example that uses bc to evaluate floating point expressions
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# If supplied command-line arguments, it uses them as the expression to have
# bc evaluate, and exits after reading the result. Otherwise, it enters an
# interactive mode, reading expressions and passing them to bc for evaluation,
# with line editing and history.
#
# You could even use this to write bc programs, but you'd have to rework the
# single-line REPL a little bit to do that (and get over the annoying timeout
# on the read)
#
# Chet Ramey
# chet.ramey@case.edu
# we force stderr to avoid synchronization issues on calculation errors, even
# with the read timeout
init()
{
coproc BC { bc -q 2>&1; }
# set scale
printf "scale = 10\n" >&${BC[1]}
# bash automatically sets BC_PID to the coproc pid; we store it so we
# can be sure to use it even after bash reaps the coproc and unsets
# the variables
coproc_pid=$BC_PID
}
# not strictly necessary; the pipes will be closed when the program exits
# but we can use it in reset() below
fini()
{
eval exec "${BC[1]}>&- ${BC[0]}<&-"
}
reset()
{
fini # close the old pipes
sleep 1
kill -1 $coproc_pid >/dev/null 2>&1 # make sure the coproc is dead
unset coproc_pid
init
}
# set a read timeout of a half second to avoid synchronization problems
calc()
{
printf "%s\n" "$1" >&${BC[1]}
read -t 0.5 ANSWER <&${BC[0]}
}
init
# if we have command line options, process them as a single expression and
# print the result. we could just run `bc <<<"scale = 10 ; $*"' and be done
# with it, but we init the coproc before this and run the calculation through
# the pipes in case we want to do something else with the answer
if [ $# -gt 0 ] ; then
calc "$*"
printf "%s\n" "$ANSWER"
fini
exit 0
fi
# we don't want to save the history anywhere
unset HISTFILE
while read -e -p 'equation: ' EQN
do
case "$EQN" in
'') continue ;;
exit|quit) break ;;
reset) reset ; continue ;;
esac
# save to the history list
history -s "$EQN"
# run it through bc
calc "$EQN"
if [ -n "$ANSWER" ] ; then
printf "%s\n" "$ANSWER"
fi
done
fini
exit 0

12
examples/scripts/cat.sh Normal file
View file

@ -0,0 +1,12 @@
shcat()
{
while read -r ; do
printf "%s\n" "$REPLY"
done
}
if [ -n "$1" ]; then
shcat < "$1"
else
shcat
fi

Some files were not shown because too many files have changed in this diff Show more