summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:38:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:38:56 +0000
commit6c20c8ed2cb9ab69a1a57ccb2b9b79969a808321 (patch)
treef63ce19d57fad3ac4a15bc26dbfbfa2b834111b5 /examples
parentInitial commit. (diff)
downloadbash-6c20c8ed2cb9ab69a1a57ccb2b9b79969a808321.tar.xz
bash-6c20c8ed2cb9ab69a1a57ccb2b9b79969a808321.zip
Adding upstream version 5.2.15.upstream/5.2.15upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'examples')
-rw-r--r--examples/INDEX.html426
-rw-r--r--examples/INDEX.txt103
-rw-r--r--examples/bash-completion/README7
-rw-r--r--examples/bash-completion/bash-completion-2.5.tar.xzbin0 -> 276732 bytes
-rw-r--r--examples/complete/bash_completion9401
-rw-r--r--examples/complete/bashcc-1.0.1.tar.gzbin0 -> 4609 bytes
-rw-r--r--examples/complete/cdfunc76
-rw-r--r--examples/complete/complete-examples512
-rw-r--r--examples/functions/array-stuff122
-rw-r--r--examples/functions/array-to-string15
-rw-r--r--examples/functions/arrayops.bash146
-rw-r--r--examples/functions/autoload111
-rw-r--r--examples/functions/autoload.v2192
-rw-r--r--examples/functions/autoload.v3125
-rw-r--r--examples/functions/autoload.v4556
-rw-r--r--examples/functions/autoload.v4.t184
-rw-r--r--examples/functions/basename23
-rw-r--r--examples/functions/csh-compat48
-rw-r--r--examples/functions/dirname21
-rw-r--r--examples/functions/dirstack160
-rw-r--r--examples/functions/exitstat22
-rw-r--r--examples/functions/external50
-rw-r--r--examples/functions/fact13
-rw-r--r--examples/functions/fstty77
-rw-r--r--examples/functions/func43
-rw-r--r--examples/functions/inetaddr79
-rw-r--r--examples/functions/inpath14
-rw-r--r--examples/functions/isnum241
-rw-r--r--examples/functions/isvalidip14
-rw-r--r--examples/functions/ksh-cd54
-rw-r--r--examples/functions/ksh-compat-test58
-rw-r--r--examples/functions/kshenv246
-rw-r--r--examples/functions/login11
-rw-r--r--examples/functions/notify.bash77
-rw-r--r--examples/functions/seq48
-rw-r--r--examples/functions/seq256
-rw-r--r--examples/functions/shcat7
-rw-r--r--examples/functions/shcat219
-rw-r--r--examples/functions/sort-pos-params69
-rw-r--r--examples/functions/substr97
-rw-r--r--examples/functions/substr299
-rw-r--r--examples/functions/whatis71
-rw-r--r--examples/functions/whence78
-rw-r--r--examples/functions/which62
-rw-r--r--examples/loadables/Makefile.in335
-rw-r--r--examples/loadables/Makefile.inc.in101
-rw-r--r--examples/loadables/Makefile.sample.in44
-rw-r--r--examples/loadables/README82
-rw-r--r--examples/loadables/accept.c245
-rw-r--r--examples/loadables/asort.c279
-rw-r--r--examples/loadables/basename.c131
-rw-r--r--examples/loadables/cat.c138
-rw-r--r--examples/loadables/csv.c206
-rw-r--r--examples/loadables/cut.c631
-rw-r--r--examples/loadables/dirname.c119
-rw-r--r--examples/loadables/dsv.c300
-rw-r--r--examples/loadables/fdflags.c374
-rw-r--r--examples/loadables/finfo.c629
-rw-r--r--examples/loadables/getconf.c1163
-rw-r--r--examples/loadables/getconf.h136
-rw-r--r--examples/loadables/head.c170
-rw-r--r--examples/loadables/hello.c96
-rw-r--r--examples/loadables/id.c329
-rw-r--r--examples/loadables/ln.c236
-rw-r--r--examples/loadables/loadables.h34
-rw-r--r--examples/loadables/logname.c74
-rw-r--r--examples/loadables/mkdir.c245
-rw-r--r--examples/loadables/mkfifo.c146
-rw-r--r--examples/loadables/mktemp.c212
-rw-r--r--examples/loadables/mypid.c89
-rw-r--r--examples/loadables/necho.c54
-rw-r--r--examples/loadables/pathchk.c381
-rw-r--r--examples/loadables/perl/Makefile.in99
-rw-r--r--examples/loadables/perl/README6
-rw-r--r--examples/loadables/perl/bperl.c53
-rw-r--r--examples/loadables/perl/iperl.c38
-rw-r--r--examples/loadables/print.c193
-rw-r--r--examples/loadables/printenv.c94
-rw-r--r--examples/loadables/push.c117
-rw-r--r--examples/loadables/realpath.c207
-rw-r--r--examples/loadables/rm.c185
-rw-r--r--examples/loadables/rmdir.c72
-rw-r--r--examples/loadables/seq.c502
-rw-r--r--examples/loadables/setpgid.c121
-rw-r--r--examples/loadables/sleep.c179
-rw-r--r--examples/loadables/stat.c464
-rw-r--r--examples/loadables/strftime.c128
-rw-r--r--examples/loadables/sync.c53
-rw-r--r--examples/loadables/tee.c183
-rw-r--r--examples/loadables/template.c75
-rw-r--r--examples/loadables/truefalse.c72
-rw-r--r--examples/loadables/tty.c83
-rw-r--r--examples/loadables/uname.c162
-rw-r--r--examples/loadables/unlink.c74
-rw-r--r--examples/loadables/whoami.c75
-rw-r--r--examples/misc/aliasconv.bash44
-rw-r--r--examples/misc/aliasconv.sh42
-rw-r--r--examples/misc/cshtobash139
-rw-r--r--examples/scripts/bcalc104
-rw-r--r--examples/scripts/cat.sh12
-rw-r--r--examples/scripts/center41
-rw-r--r--examples/scripts/inpath19
-rw-r--r--examples/scripts/shprompt153
-rw-r--r--examples/scripts/spin.bash37
-rw-r--r--examples/scripts/xterm_title44
-rw-r--r--examples/scripts/zprintf43
-rw-r--r--examples/shellmath/LICENSE677
-rw-r--r--examples/shellmath/README.md166
-rw-r--r--examples/shellmath/assert.sh85
-rw-r--r--examples/shellmath/faster_e_demo.sh68
-rw-r--r--examples/shellmath/image.pngbin0 -> 48779 bytes
-rw-r--r--examples/shellmath/runTests.sh124
-rw-r--r--examples/shellmath/shellmath.sh1068
-rw-r--r--examples/shellmath/slower_e_demo.sh55
-rw-r--r--examples/shellmath/testCases.in142
-rw-r--r--examples/shellmath/timingData.txt42
-rw-r--r--examples/startup-files/Bash_aliases63
-rw-r--r--examples/startup-files/Bash_profile18
-rw-r--r--examples/startup-files/Bashrc.bfox70
-rw-r--r--examples/startup-files/README12
-rw-r--r--examples/startup-files/bash-profile39
-rw-r--r--examples/startup-files/bashrc133
122 files changed, 27287 insertions, 0 deletions
diff --git a/examples/INDEX.html b/examples/INDEX.html
new file mode 100644
index 0000000..bcca1f9
--- /dev/null
+++ b/examples/INDEX.html
@@ -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>
diff --git a/examples/INDEX.txt b/examples/INDEX.txt
new file mode 100644
index 0000000..b47e211
--- /dev/null
+++ b/examples/INDEX.txt
@@ -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
+
diff --git a/examples/bash-completion/README b/examples/bash-completion/README
new file mode 100644
index 0000000..fb5e765
--- /dev/null
+++ b/examples/bash-completion/README
@@ -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.
diff --git a/examples/bash-completion/bash-completion-2.5.tar.xz b/examples/bash-completion/bash-completion-2.5.tar.xz
new file mode 100644
index 0000000..f5b9079
--- /dev/null
+++ b/examples/bash-completion/bash-completion-2.5.tar.xz
Binary files differ
diff --git a/examples/complete/bash_completion b/examples/complete/bash_completion
new file mode 100644
index 0000000..b0cf4a8
--- /dev/null
+++ b/examples/complete/bash_completion
@@ -0,0 +1,9401 @@
+# bash_completion - programmable completion functions for bash 3.x
+# (backwards compatible with bash 2.05b)
+#
+# $Id: bash_completion,v 1.872 2006/03/01 16:20:18 ianmacd Exp $
+#
+# Copyright (C) Ian Macdonald <ian@caliban.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# The latest version of this software can be obtained here:
+#
+# http://www.caliban.org/bash/index.shtml#completion
+#
+# RELEASE: 20060301
+
+if [[ $- == *v* ]]; then
+ BASH_COMPLETION_ORIGINAL_V_VALUE="-v"
+else
+ BASH_COMPLETION_ORIGINAL_V_VALUE="+v"
+fi
+
+if [[ -n $BASH_COMPLETION_DEBUG ]]; then
+ set -v
+else
+ set +v
+fi
+
+# Alter the following to reflect the location of this file.
+#
+[ -n "$BASH_COMPLETION" ] || BASH_COMPLETION=/etc/bash_completion
+[ -n "$BASH_COMPLETION_DIR" ] || BASH_COMPLETION_DIR=/etc/bash_completion.d
+readonly BASH_COMPLETION BASH_COMPLETION_DIR
+
+# Set a couple of useful vars
+#
+UNAME=$( uname -s )
+# strip OS type and version under Cygwin (e.g. CYGWIN_NT-5.1 => Cygwin)
+UNAME=${UNAME/CYGWIN_*/Cygwin}
+RELEASE=$( uname -r )
+
+# features supported by bash 2.05 and higher
+if [ ${BASH_VERSINFO[0]} -eq 2 ] && [[ ${BASH_VERSINFO[1]} > 04 ]] ||
+ [ ${BASH_VERSINFO[0]} -gt 2 ]; then
+ declare -r bash205=$BASH_VERSION 2>/dev/null || :
+ default="-o default"
+ dirnames="-o dirnames"
+ filenames="-o filenames"
+fi
+# features supported by bash 2.05b and higher
+if [ ${BASH_VERSINFO[0]} -eq 2 ] && [[ ${BASH_VERSINFO[1]} = "05b" ]] ||
+ [ ${BASH_VERSINFO[0]} -gt 2 ]; then
+ declare -r bash205b=$BASH_VERSION 2>/dev/null || :
+ nospace="-o nospace"
+fi
+# features supported by bash 3.0 and higher
+if [ ${BASH_VERSINFO[0]} -gt 2 ]; then
+ declare -r bash3=$BASH_VERSION 2>/dev/null || :
+ bashdefault="-o bashdefault"
+ plusdirs="-o plusdirs"
+fi
+
+# Turn on extended globbing and programmable completion
+shopt -s extglob progcomp
+
+# A lot of the following one-liners were taken directly from the
+# completion examples provided with the bash 2.04 source distribution
+
+# Make directory commands see only directories
+complete -d pushd
+
+# The following section lists completions that are redefined later
+# Do NOT break these over multiple lines.
+#
+# START exclude -- do NOT remove this line
+complete -f -X '!*.?(t)bz?(2)' bunzip2 bzcat bzcmp bzdiff bzegrep bzfgrep bzgrep
+complete -f -X '!*.@(zip|ZIP|jar|JAR|exe|EXE|pk3|war|wsz|ear|zargo|xpi|sxw|ott)' unzip zipinfo
+complete -f -X '*.Z' compress znew
+complete -f -X '!*.@(Z|gz|tgz|Gz|dz)' gunzip zcmp zdiff zcat zegrep zfgrep zgrep zless zmore
+complete -f -X '!*.Z' uncompress
+complete -f -X '!*.@(gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX)' ee display
+complete -f -X '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|GIF|JPG|JP?(E)G|TIF?(F)|PNG|P[BGP]M|BMP|X[BP]M|RLE|RGB|PCX|FITS|PM)' xv qiv
+complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ|.bz2|.BZ2|.Z))' gv ggv kghostview
+complete -f -X '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' xdvi
+complete -f -X '!*.@(dvi|DVI)?(.@(gz|bz2))' kdvi
+complete -f -X '!*.@(dvi|DVI)' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx
+complete -f -X '!*.@(pdf|PDF)' acroread gpdf xpdf
+complete -f -X '!*.@(?(e)ps|?(E)PS|pdf|PDF)' kpdf
+complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ)|cb(r|z)|CB(R|Z)|djv?(u)|DJV?(U)||dvi|DVI|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX)' evince
+complete -f -X '!*.@(?(e)ps|?(E)PS)' ps2pdf
+complete -f -X '!*.texi*' makeinfo texi2html
+complete -f -X '!*.@(?(la)tex|?(LA)TEX|texi|TEXI|dtx|DTX|ins|INS)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi
+complete -f -X '!*.@(mp3|MP3)' mpg123 mpg321 madplay
+complete -f -X '!*.@(mp?(e)g|MP?(E)G|wma|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|viv|rm|ram|yuv|mov|MOV|qt|QT|wmv|mp3|MP3|m4v|M4V|ogg|OGG|ogm|OGM|mp4|MP4|wav|WAV|asx|ASX|mng|MNG|srt)' xine aaxine fbxine kaffeine
+complete -f -X '!*.@(avi|asf|wmv)' aviplay
+complete -f -X '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay
+complete -f -X '!*.@(mpg|mpeg|avi|mov|qt)' xanim
+complete -f -X '!*.@(ogg|OGG|m3u|flac|spx)' ogg123
+complete -f -X '!*.@(mp3|MP3|ogg|OGG|pls|m3u)' gqmpeg freeamp
+complete -f -X '!*.fig' xfig
+complete -f -X '!*.@(mid?(i)|MID?(I))' playmidi
+complete -f -X '!*.@(mid?(i)|MID?(I)|rmi|RMI|rcp|RCP|[gr]36|[GR]36|g18|G18|mod|MOD|xm|XM|it|IT|x3m|X3M)' timidity
+complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' vi vim gvim rvim view rview rgvim rgview gview
+complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' emacs
+complete -f -X '!*.@(exe|EXE|com|COM|scr|SCR|exe.so)' wine
+complete -f -X '!*.@(zip|ZIP|z|Z|gz|GZ|tgz|TGZ)' bzme
+complete -f -X '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx opera galeon curl dillo elinks amaya
+complete -f -X '!*.@(sxw|stw|sxg|sgl|doc|dot|rtf|txt|htm|html|odt|ott|odm)' oowriter
+complete -f -X '!*.@(sxi|sti|pps|ppt|pot|odp|otp)' ooimpress
+complete -f -X '!*.@(sxc|stc|xls|xlw|xlt|csv|ods|ots)' oocalc
+complete -f -X '!*.@(sxd|std|sda|sdd|odg|otg)' oodraw
+complete -f -X '!*.@(sxm|smf|mml|odf)' oomath
+complete -f -X '!*.odb' oobase
+complete -f -X '!*.rpm' rpm2cpio
+# FINISH exclude -- do not remove this line
+
+# start of section containing compspecs that can be handled within bash
+
+# user commands see only users
+complete -u su usermod userdel passwd chage write chfn groups slay w sux
+
+# group commands see only groups
+[ -n "$bash205" ] && complete -g groupmod groupdel newgrp 2>/dev/null
+
+# bg completes with stopped jobs
+complete -A stopped -P '%' bg
+
+# other job commands
+complete -j -P '%' fg jobs disown
+
+# readonly and unset complete with shell variables
+complete -v readonly unset
+
+# set completes with set options
+complete -A setopt set
+
+# shopt completes with shopt options
+complete -A shopt shopt
+
+# helptopics
+complete -A helptopic help
+
+# unalias completes with aliases
+complete -a unalias
+
+# bind completes with readline bindings (make this more intelligent)
+complete -A binding bind
+
+# type and which complete on commands
+complete -c command type which
+
+# builtin completes on builtins
+complete -b builtin
+
+# start of section containing completion functions called by other functions
+
+# This function checks whether we have a given program on the system.
+# No need for bulky functions in memory if we don't.
+#
+have()
+{
+ unset -v have
+ PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin type $1 &>/dev/null &&
+ have="yes"
+}
+
+# use GNU sed if we have it, since its extensions are still used in our code
+#
+[ $UNAME != Linux ] && have gsed && alias sed=gsed
+
+# This function checks whether a given readline variable
+# is `on'.
+#
+_rl_enabled()
+{
+ [[ "$( bind -v )" = *$1+([[:space:]])on* ]]
+}
+
+# This function shell-quotes the argument
+quote()
+{
+ echo \'${1//\'/\'\\\'\'}\' #'# Help vim syntax highlighting
+}
+
+# This function quotes the argument in a way so that readline dequoting
+# results in the original argument
+quote_readline()
+{
+ local t="${1//\\/\\\\}"
+ echo \'${t//\'/\'\\\'\'}\' #'# Help vim syntax highlighting
+}
+
+# This function shell-dequotes the argument
+dequote()
+{
+ eval echo "$1"
+}
+
+
+# Get the word to complete
+# This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases
+# where the user is completing in the middle of a word.
+# (For example, if the line is "ls foobar",
+# and the cursor is here --------> ^
+# it will complete just "foo", not "foobar", which is what the user wants.)
+_get_cword()
+{
+ if [[ "${#COMP_WORDS[COMP_CWORD]}" -eq 0 ]] || [[ "$COMP_POINT" == "${#COMP_LINE}" ]]; then
+ echo "${COMP_WORDS[COMP_CWORD]}"
+ else
+ local i
+ local cur="$COMP_LINE"
+ local index="$COMP_POINT"
+ for (( i = 0; i <= COMP_CWORD; ++i )); do
+ while [[ "${#cur}" -ge ${#COMP_WORDS[i]} ]] && [[ "${cur:0:${#COMP_WORDS[i]}}" != "${COMP_WORDS[i]}" ]]; do
+ cur="${cur:1}"
+ index="$(( index - 1 ))"
+ done
+ if [[ "$i" -lt "$COMP_CWORD" ]]; then
+ local old_size="${#cur}"
+ cur="${cur#${COMP_WORDS[i]}}"
+ local new_size="${#cur}"
+ index="$(( index - old_size + new_size ))"
+ fi
+ done
+
+ if [[ "${COMP_WORDS[COMP_CWORD]:0:${#cur}}" != "$cur" ]]; then
+ # We messed up! At least return the whole word so things keep working
+ echo "${COMP_WORDS[COMP_CWORD]}"
+ else
+ echo "${cur:0:$index}"
+ fi
+ fi
+}
+
+
+# This function performs file and directory completion. It's better than
+# simply using 'compgen -f', because it honours spaces in filenames.
+# If passed -d, it completes only on directories. If passed anything else,
+# it's assumed to be a file glob to complete on.
+#
+_filedir()
+{
+ local IFS=$'\t\n' xspec
+
+ _expand || return 0
+
+ local toks=( ) tmp
+ while read -r tmp; do
+ [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp
+ done < <( compgen -d -- "$(quote_readline "$cur")" )
+
+ if [[ "$1" != -d ]]; then
+ xspec=${1:+"!*.$1"}
+ while read -r tmp; do
+ [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp
+ done < <( compgen -f -X "$xspec" -- "$(quote_readline "$cur")" )
+ fi
+
+ COMPREPLY=( "${COMPREPLY[@]}" "${toks[@]}" )
+}
+
+# This function completes on signal names
+#
+_signals()
+{
+ local i
+
+ # standard signal completion is rather braindead, so we need
+ # to hack around to get what we want here, which is to
+ # complete on a dash, followed by the signal name minus
+ # the SIG prefix
+ COMPREPLY=( $( compgen -A signal SIG${cur#-} ))
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[i]=-${COMPREPLY[i]#SIG}
+ done
+}
+
+# This function completes on configured network interfaces
+#
+_configured_interfaces()
+{
+ if [ -f /etc/debian_version ]; then
+ # Debian system
+ COMPREPLY=( $( sed -ne 's|^iface \([^ ]\+\).*$|\1|p' \
+ /etc/network/interfaces ) )
+ elif [ -f /etc/SuSE-release ]; then
+ # SuSE system
+ COMPREPLY=( $( command ls \
+ /etc/sysconfig/network/ifcfg-* | \
+ sed -ne 's|.*ifcfg-\('$cur'.*\)|\1|p' ) )
+ elif [ -f /etc/pld-release ]; then
+ # PLD Linux
+ COMPREPLY=( $( command ls -B \
+ /etc/sysconfig/interfaces | \
+ sed -ne 's|.*ifcfg-\('$cur'.*\)|\1|p' ) )
+ else
+ # Assume Red Hat
+ COMPREPLY=( $( command ls \
+ /etc/sysconfig/network-scripts/ifcfg-* | \
+ sed -ne 's|.*ifcfg-\('$cur'.*\)|\1|p' ) )
+ fi
+}
+
+# This function completes on all available network interfaces
+# -a: restrict to active interfaces only
+# -w: restrict to wireless interfaces only
+#
+_available_interfaces()
+{
+ local cmd
+
+ if [ "${1:-}" = -w ]; then
+ cmd="iwconfig"
+ elif [ "${1:-}" = -a ]; then
+ cmd="ifconfig"
+ else
+ cmd="ifconfig -a"
+ fi
+
+ COMPREPLY=( $( eval $cmd 2>/dev/null | \
+ sed -ne 's|^\('$cur'[^[:space:][:punct:]]\{1,\}\).*$|\1|p') )
+}
+
+# This function expands tildes in pathnames
+#
+_expand()
+{
+ # FIXME: Why was this here?
+ # [ "$cur" != "${cur%\\}" ] && cur="$cur\\"
+
+ # expand ~username type directory specifications
+ if [[ "$cur" == \~*/* ]]; then
+ eval cur=$cur
+ elif [[ "$cur" == \~* ]]; then
+ cur=${cur#\~}
+ COMPREPLY=( $( compgen -P '~' -u $cur ) )
+ return ${#COMPREPLY[@]}
+ fi
+}
+
+# This function completes on process IDs.
+# AIX and Solaris ps prefers X/Open syntax.
+[ $UNAME = SunOS -o $UNAME = AIX ] &&
+_pids()
+{
+ COMPREPLY=( $( compgen -W '$( command ps -efo pid | sed 1d )' -- $cur ))
+} ||
+_pids()
+{
+ COMPREPLY=( $( compgen -W '$( command ps axo pid | sed 1d )' -- $cur ) )
+}
+
+# This function completes on process group IDs.
+# AIX and SunOS prefer X/Open, all else should be BSD.
+[ $UNAME = SunOS -o $UNAME = AIX ] &&
+_pgids()
+{
+ COMPREPLY=( $( compgen -W '$( command ps -efo pgid | sed 1d )' -- $cur ))
+} ||
+_pgids()
+{
+ COMPREPLY=( $( compgen -W '$( command ps axo pgid | sed 1d )' -- $cur ))
+}
+
+# This function completes on user IDs
+#
+_uids()
+{
+ if type getent &>/dev/null; then
+ COMPREPLY=( $( getent passwd | \
+ awk -F: '{if ($3 ~ /^'$cur'/) print $3}' ) )
+ elif type perl &>/dev/null; then
+ COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($uid) = (getpwent)[2]) { print $uid . "\n" }'"'"' )' -- $cur ) )
+ else
+ # make do with /etc/passwd
+ COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($3 ~ /^'$cur'/) print $3}'\
+ /etc/passwd ) )
+ fi
+}
+
+# This function completes on group IDs
+#
+_gids()
+{
+ if type getent &>/dev/null; then
+ COMPREPLY=( $( getent group | \
+ awk -F: '{if ($3 ~ /^'$cur'/) print $3}' ) )
+ elif type perl &>/dev/null; then
+ COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($gid) = (getgrent)[2]) { print $gid . "\n" }'"'"' )' -- $cur ) )
+ else
+ # make do with /etc/group
+ COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($3 ~ /^'$cur'/) print $3}'\
+ /etc/group ) )
+ fi
+}
+
+# This function completes on services
+#
+_services()
+{
+ local sysvdir famdir
+ [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d || sysvdir=/etc/init.d
+ famdir=/etc/xinetd.d
+ COMPREPLY=( $( builtin echo $sysvdir/!(*.rpmsave|*.rpmorig|*~|functions)) )
+
+ if [ -d $famdir ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" $( builtin echo $famdir/!(*.rpmsave|*.rpmorig|*~)) )
+ fi
+
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]#@($sysvdir|$famdir)/}' -- $cur ) )
+}
+
+# This function complete on modules
+#
+_modules()
+{
+ local modpath
+ modpath=/lib/modules/$1
+ COMPREPLY=( $( command ls -R $modpath | \
+ sed -ne 's/^\('$cur'.*\)\.k\?o\(\|.gz\)$/\1/p') )
+}
+
+# this function complete on user:group format
+#
+_usergroup()
+{
+ local IFS=$'\n'
+ cur=${cur//\\\\ / }
+ if [[ $cur = *@(\\:|.)* ]] && [ -n "$bash205" ]; then
+ user=${cur%%*([^:.])}
+ COMPREPLY=( $(compgen -P ${user/\\\\} -g -- ${cur##*[.:]}) )
+ elif [[ $cur = *:* ]] && [ -n "$bash205" ]; then
+ COMPREPLY=( $( compgen -g -- ${cur##*[.:]} ) )
+ else
+ COMPREPLY=( $( compgen -S : -u -- $cur ) )
+ fi
+}
+
+# this function count the number of mandatory args
+#
+_count_args()
+{
+ args=1
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" != -* ]]; then
+ args=$(($args+1))
+ fi
+ done
+}
+
+# start of section containing completion functions for bash built-ins
+
+# bash alias completion
+#
+_alias()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[$COMP_CWORD]}
+
+ case "$COMP_LINE" in
+ *[^=])
+ COMPREPLY=( $( compgen -A alias -S '=' -- $cur ) )
+ ;;
+ *=)
+ COMPREPLY=( "$( alias ${cur%=} 2>/dev/null | \
+ sed -e 's|^alias '$cur'\(.*\)$|\1|' )" )
+ ;;
+ esac
+}
+complete -F _alias $nospace alias
+
+# bash export completion
+#
+_export()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[$COMP_CWORD]}
+
+ case "$COMP_LINE" in
+ *=\$*)
+ COMPREPLY=( $( compgen -v -P '$' -- ${cur#*=\$} ) )
+ ;;
+ *[^=])
+ COMPREPLY=( $( compgen -v -S '=' -- $cur ) )
+ ;;
+ *=)
+ COMPREPLY=( "$( eval echo -n \"$`echo ${cur%=}`\" |
+ ( echo -n \'
+ sed -e 's/'\''/'\''\\\'\'''\''/g'
+ echo -n \' ) )" )
+ ;;
+ esac
+}
+complete -F _export $default $nospace export
+
+# bash shell function completion
+#
+_function()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $1 == @(declare|typeset) ]]; then
+ if [ "$prev" = -f ]; then
+ COMPREPLY=( $( compgen -A function -- $cur ) )
+ elif [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a -f -F -i -r -x -p' -- \
+ $cur ) )
+ fi
+ elif [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -A function -- $cur ) )
+ else
+ COMPREPLY=( "() $( type -- ${COMP_WORDS[1]} | sed -e 1,2d )" )
+ fi
+}
+complete -F _function function declare typeset
+
+# bash complete completion
+#
+_complete()
+{
+ local cur prev options
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -o)
+ options="default dirnames filenames"
+ [ -n "$bash205b" ] && options="$options nospace"
+ [ -n "$bash3" ] && options="$options bashdefault plusdirs"
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ return 0
+ ;;
+
+ -A)
+ COMPREPLY=( $( compgen -W 'alias arrayvar binding \
+ builtin command directory disabled enabled \
+ export file function group helptopic hostname \
+ job keyword running service setopt shopt \
+ signal stopped user variable' -- $cur ) )
+ return 0
+ ;;
+
+ -C)
+ COMPREPLY=( $( compgen -A command -- $cur ) )
+ return 0
+ ;;
+ -F)
+ COMPREPLY=( $( compgen -A function -- $cur ) )
+ return 0
+ ;;
+ -@(p|r))
+ COMPREPLY=( $( complete -p | sed -e 's|.* ||' | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ options="-a -b -c -d -e -f -g -j -k -s -v -u -A -G -W -P -S -X -F -C"
+ [ -n "$bash205" ] && options="$options -o"
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -A command -- $cur ) )
+ fi
+}
+complete -F _complete complete
+
+# start of section containing completion functions for external programs
+
+# a little help for FreeBSD ports users
+[ $UNAME = FreeBSD ] && complete -W 'index search fetch fetch-list \
+ extract patch configure build install reinstall \
+ deinstall clean clean-depends kernel buildworld' make
+
+# This completes on a list of all available service scripts for the
+# 'service' command and/or the SysV init.d directory, followed by
+# that script's available commands
+#
+{ have service || [ -d /etc/init.d/ ]; } &&
+_service()
+{
+ local cur sysvdir
+
+ COMPREPLY=()
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ cur=`_get_cword`
+
+ # don't complete for things like killall, ssh and mysql if it's
+ # the standalone command, rather than the init script
+ [[ ${COMP_WORDS[0]} != @(*init.d/!(functions|~)|service) ]] && return 0
+
+ # don't complete past 2nd token
+ [ $COMP_CWORD -gt 2 ] && return 0
+
+ [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \
+ || sysvdir=/etc/init.d
+
+ if [[ $COMP_CWORD -eq 1 ]] && [[ $prev == "service" ]]; then
+ _services
+ else
+ COMPREPLY=( $( compgen -W '`sed -ne "y/|/ /; \
+ s/^.*Usage.*{\(.*\)}.*$/\1/p" \
+ $sysvdir/${prev##*/} 2>/dev/null`' -- $cur ) )
+ fi
+
+ return 0
+} &&
+complete -F _service service
+[ -d /etc/init.d/ ] && complete -F _service $default \
+ $(for i in /etc/init.d/*; do echo ${i##*/}; done)
+
+# chown(1) completion
+#
+_chown()
+{
+ local cur
+ cur=`_get_cword`
+
+ # options completion
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes \
+ --dereference --no-dereference --from= --silent --quiet \
+ --reference= --recursive --verbose --help --version' -- $cur ) )
+ else
+ _count_args
+
+ case $args in
+ 1)
+ _usergroup
+ ;;
+ *)
+ _filedir
+ ;;
+ esac
+ fi
+}
+complete -F _chown $filenames chown
+
+# chgrp(1) completion
+#
+_chgrp()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ cur=${cur//\\\\/}
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # options completion
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes \
+ --dereference --no-dereference --silent --quiet \
+ --reference= --recursive --verbose --help --version' -- $cur ) )
+ return 0
+ fi
+
+ # first parameter on line or first since an option?
+ if [ $COMP_CWORD -eq 1 ] && [[ "$cur" != -* ]] || \
+ [[ "$prev" == -* ]] && [ -n "$bash205" ]; then
+ local IFS=$'\n'
+ COMPREPLY=( $( compgen -g $cur 2>/dev/null ) )
+ else
+ _filedir || return 0
+ fi
+
+ return 0
+}
+complete -F _chgrp $filenames chgrp
+
+# umount(8) completion. This relies on the mount point being the third
+# space-delimited field in the output of mount(8)
+#
+_umount()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ OLDIFS="$IFS"
+ IFS="\n"
+ COMPREPLY=( $( compgen -W '$( mount | cut -d" " -f 3 )' -- $cur ) )
+ IFS="$OLDIFS"
+
+ return 0
+}
+complete -F _umount $dirnames umount
+
+# mount(8) completion. This will pull a list of possible mounts out of
+# /etc/{,v}fstab, unless the word being completed contains a ':', which
+# would indicate the specification of an NFS server. In that case, we
+# query the server for a list of all available exports and complete on
+# that instead.
+#
+_mount()
+{ local cur i sm host
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ [[ "$cur" == \\ ]] && cur="/"
+
+ for i in {,/usr}/{,s}bin/showmount; do [ -x $i ] && sm=$i && break; done
+
+ if [ -n "$sm" ] && [[ "$cur" == *:* ]]; then
+ COMPREPLY=( $( $sm -e ${cur%%:*} | sed 1d | \
+ grep ^${cur#*:} | awk '{print $1}' ) )
+ elif [[ "$cur" == //* ]]; then
+ host=${cur#//}
+ host=${host%%/*}
+ if [ -n "$host" ]; then
+ COMPREPLY=( $( compgen -W "$( echo $( smbclient -d 0 -NL $host 2>/dev/null|
+ sed -ne '/^['"$'\t '"']*Sharename/,/^$/p' |
+ sed -ne '3,$s|^[^A-Za-z]*\([^'"$'\t '"']*\).*$|//'$host'/\1|p' ) )" -- "$cur" ) )
+ fi
+ elif [ -r /etc/vfstab ]; then
+ # Solaris
+ COMPREPLY=( $( awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' \
+ /etc/vfstab | grep "^$cur" ) )
+ elif [ ! -e /etc/fstab ]; then
+ # probably Cygwin
+ COMPREPLY=( $( mount | awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' \
+ | grep "^$cur" ) )
+ else
+ # probably Linux
+ COMPREPLY=( $( awk '! /^[ \t]*#/ {if ($2 ~ /\//) print $2}' \
+ /etc/fstab | grep "^$cur" ) )
+ fi
+
+ return 0
+}
+complete -F _mount $default $filenames mount
+
+# Linux rmmod(8) completion. This completes on a list of all currently
+# installed kernel modules.
+#
+have rmmod && {
+_rmmod()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( /sbin/lsmod | \
+ awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}' 2>/dev/null ))
+ return 0
+}
+complete -F _rmmod rmmod
+
+# Linux insmod(8), modprobe(8) and modinfo(8) completion. This completes on a
+# list of all available modules for the version of the kernel currently
+# running.
+#
+_insmod()
+{
+ local cur prev modpath
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # behave like lsmod for modprobe -r
+ if [ $1 = "modprobe" ] &&
+ [ "${COMP_WORDS[1]}" = "-r" ]; then
+ COMPREPLY=( $( /sbin/lsmod | \
+ awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}' ) )
+ return 0
+ fi
+
+ # do filename completion if we're giving a path to a module
+ if [[ "$cur" == */* ]]; then
+ _filedir '@(?(k)o?(.gz))'
+ return 0
+ fi
+
+ if [ $COMP_CWORD -gt 1 ] &&
+ [[ "${COMP_WORDS[COMP_CWORD-1]}" != -* ]]; then
+ # do module parameter completion
+ COMPREPLY=( $( /sbin/modinfo -p ${COMP_WORDS[1]} 2>/dev/null | \
+ awk '{if ($1 ~ /^parm:/ && $2 ~ /^'$cur'/) { print $2 } \
+ else if ($1 !~ /:/ && $1 ~ /^'$cur'/) { print $1 }}' ) )
+ else
+ _modules $(uname -r)
+ fi
+
+ return 0
+}
+complete -F _insmod $filenames insmod modprobe modinfo
+}
+
+# man(1) completion
+#
+[ $UNAME = GNU -o $UNAME = Linux -o $UNAME = Darwin \
+ -o $UNAME = FreeBSD -o $UNAME = SunOS -o $UNAME = Cygwin \
+ -o $UNAME = OpenBSD ] &&
+_man()
+{
+ local cur prev sect manpath UNAME
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ _expand || return 0
+
+ # default completion if parameter contains /
+ if [[ "$cur" == */* ]]; then
+ _filedir
+ return 0
+ fi
+
+ UNAME=$( uname -s )
+ # strip OS type and version under Cygwin
+ UNAME=${UNAME/CYGWIN_*/Cygwin}
+ if [ $UNAME = GNU -o $UNAME = Linux -o $UNAME = FreeBSD \
+ -o $UNAME = Cygwin ]; then
+ manpath=$( manpath 2>/dev/null || command man --path )
+ else
+ manpath=$MANPATH
+ fi
+
+ if [ -z "$manpath" ]; then
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ return 0
+ fi
+
+ # determine manual section to search
+ [[ "$prev" == [0-9ln] ]] && sect=$prev || sect='*'
+
+ manpath=$manpath:
+ if [ -n "$cur" ]; then
+ manpath="${manpath//://*man$sect/$cur* } ${manpath//://*cat$sect/$cur* }"
+ else
+ manpath="${manpath//://*man$sect/ } ${manpath//://*cat$sect/ }"
+ fi
+
+ # redirect stderr for when path doesn't exist
+ COMPREPLY=( $( eval command ls "$manpath" 2>/dev/null ) )
+ # weed out directory path names and paths to man pages
+ COMPREPLY=( ${COMPREPLY[@]##*/?(:)} )
+ # strip suffix from man pages
+ COMPREPLY=( ${COMPREPLY[@]%.@(gz|bz2)} )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]%.*}' -- "${cur//\\\\/}" ) )
+
+ [[ "$prev" != [0-9ln] ]] && _filedir '[0-9ln]'
+
+ return 0
+}
+[ $UNAME = GNU -o $UNAME = Linux -o $UNAME = Darwin \
+ -o $UNAME = FreeBSD -o $UNAME = SunOS -o $UNAME = Cygwin \
+ -o $UNAME = OpenBSD ] && \
+complete -F _man $filenames man apropos whatis
+
+# renice(8) completion
+#
+_renice()
+{
+ local command cur curopt i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ command=$1
+
+ i=0
+ # walk back through command line and find last option
+ while [ $i -le $COMP_CWORD -a ${#COMPREPLY[@]} -eq 0 ]; do
+ curopt=${COMP_WORDS[COMP_CWORD-$i]}
+ case "$curopt" in
+ -u)
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ ;;
+ -g)
+ _pgids
+ ;;
+ -p|$command)
+ _pids
+ ;;
+ esac
+ i=$(( ++i ))
+ done
+}
+complete -F _renice renice
+
+# kill(1) completion
+#
+_kill()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then
+ # return list of available signals
+ _signals
+ else
+ # return list of available PIDs
+ _pids
+ fi
+}
+complete -F _kill kill
+
+# Linux and FreeBSD killall(1) completion.
+#
+[ $UNAME = Linux -o $UNAME = FreeBSD ] &&
+_killall()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then
+ _signals
+ else
+ COMPREPLY=( $( compgen -W '$( command ps axo command | \
+ sed -ne "1d; s/^\[\?\([^-][^] ]*\).*$/\1/p" | \
+ sed -e "s/.*\///" )' -- $cur ) )
+ fi
+
+ return 0
+}
+[ $UNAME = Linux -o $UNAME = FreeBSD ] && complete -F _killall killall pkill
+
+# Linux and FreeBSD pgrep(1) completion.
+#
+[ $UNAME = Linux -o $UNAME = FreeBSD ] &&
+_pgrep()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -W '$( command ps axo command | \
+ sed -ne "1d; s/^\[\?\([^-][^] ]*\).*$/\1/p" | \
+ sed -e "s/.*\///" )' -- $cur ) )
+
+ return 0
+}
+[ $UNAME = Linux -o $UNAME = FreeBSD ] && complete -F _pgrep pgrep
+# Linux pidof(8) completion.
+[ $UNAME = Linux ] && complete -F _pgrep pidof
+
+# GNU find(1) completion. This makes heavy use of ksh style extended
+# globs and contains Linux specific code for completing the parameter
+# to the -fstype option.
+#
+_find()
+{
+ local cur prev i exprfound onlyonce
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(max|min)depth)
+ COMPREPLY=( $( compgen -W '0 1 2 3 4 5 6 7 8 9' -- $cur ) )
+ return 0
+ ;;
+ -?(a|c)newer|-fls|-fprint?(0|f)|-?(i)?(l)name|-?(i)wholename)
+ _filedir
+ return 0
+ ;;
+ -fstype)
+ # this is highly non-portable
+ [ -e /proc/filesystems ] &&
+ COMPREPLY=( $( cut -d$'\t' -f 2 /proc/filesystems | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+ -gid)
+ _gids
+ return 0
+ ;;
+ -group)
+ if [ -n "$bash205" ]; then
+ COMPREPLY=( $( compgen -g -- $cur 2>/dev/null) )
+ fi
+ return 0
+ ;;
+ -?(x)type)
+ COMPREPLY=( $( compgen -W 'b c d p f l s' -- $cur ) )
+ return 0
+ ;;
+ -uid)
+ _uids
+ return 0
+ ;;
+ -user)
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ return 0
+ ;;
+ -exec|-ok)
+ COMP_WORDS=(COMP_WORDS[0] $cur)
+ COMP_CWORD=1
+ _command
+ return 0
+ ;;
+ -[acm]min|-[acm]time|-?(i)?(l)name|-inum|-?(i)path|-?(i)regex| \
+ -links|-perm|-size|-used|-printf)
+ # do nothing, just wait for a parameter to be given
+ return 0
+ ;;
+ esac
+
+ _expand || return 0
+
+ # set exprfound to 1 if there is already an expression present
+ for i in ${COMP_WORDS[@]}; do
+ [[ "$i" = [-\(\),\!]* ]] && exprfound=1 && break
+ done
+
+ # handle case where first parameter is not a dash option
+ if [ "$exprfound" != 1 ] && [[ "$cur" != [-\(\),\!]* ]]; then
+ _filedir -d
+ return 0
+ fi
+
+ # complete using basic options
+ COMPREPLY=( $( compgen -W '-daystart -depth -follow -help -maxdepth \
+ -mindepth -mount -noleaf -version -xdev -amin -anewer \
+ -atime -cmin -cnewer -ctime -empty -false -fstype \
+ -gid -group -ilname -iname -inum -ipath -iregex \
+ -wholename \
+ -links -lname -mmin -mtime -name -newer -nouser \
+ -nogroup -perm -regex -size -true -type -uid -used \
+ -user -xtype -exec -fls -fprint -fprint0 -fprintf -ok \
+ -print -print0 -printf -prune -ls' -- $cur ) )
+
+ # this removes any options from the list of completions that have
+ # already been specified somewhere on the command line, as long as
+ # these options can only be used once (in a word, "options", in
+ # opposition to "tests" and "actions", as in the find(1) manpage).
+ onlyonce=' -daystart -depth -follow -help -maxdepth -mindepth -mount \
+ -noleaf -version -xdev '
+ COMPREPLY=( $( echo "${COMP_WORDS[@]}" | \
+ (while read -d ' ' i; do
+ [ "$i" == "" ] ||
+ [ "${onlyonce/ ${i%% *} / }" == "$onlyonce" ] &&
+ continue
+ # flatten array with spaces on either side,
+ # otherwise we cannot grep on word boundaries of
+ # first and last word
+ COMPREPLY=" ${COMPREPLY[@]} "
+ # remove word from list of completions
+ COMPREPLY=( ${COMPREPLY/ ${i%% *} / } )
+ done
+ echo "${COMPREPLY[@]}")
+ ) )
+
+ _filedir
+
+ return 0
+}
+complete -F _find $filenames find
+
+# Linux iwconfig(8) completion
+#
+[ $UNAME = Linux ] && have iwconfig &&
+_iwconfig()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ mode)
+ COMPREPLY=( $( compgen -W 'managed ad-hoc master \
+ repeater secondary monitor' -- $cur ) )
+ return 0
+ ;;
+ essid)
+ COMPREPLY=( $( compgen -W 'on off any' -- $cur ) )
+ if [ -n "${COMP_IWLIST_SCAN:-}" ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( iwlist ${COMP_WORDS[1]} scan | \
+ awk -F '"' '/ESSID/ {print $2}' | \
+ grep "^$cur" ))
+ fi
+ return 0
+ ;;
+ nwid)
+ COMPREPLY=( $( compgen -W 'on off' -- $cur ) )
+ return 0
+ ;;
+ channel)
+ COMPREPLY=( $( iwlist ${COMP_WORDS[1]} channel | \
+ awk '/^[[:space:]]*Channel/ {print $2}' | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+
+ freq)
+ COMPREPLY=( $( iwlist ${COMP_WORDS[1]} channel | \
+ awk '/^[[:space:]]*Channel/ {print $4"G"}' | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+ ap)
+ COMPREPLY=( $( compgen -W 'on off any' -- $cur ) )
+ if [ -n "${COMP_IWLIST_SCAN:-}" ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( iwlist ${COMP_WORDS[1]} scan | \
+ awk -F ': ' '/Address/ {print $2}' | \
+ grep "^$cur" ) )
+ fi
+ return 0
+ ;;
+ rate)
+ COMPREPLY=( $( compgen -W 'auto fixed' -- $cur ) )
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( iwlist ${COMP_WORDS[1]} rate | \
+ awk '/^[[:space:]]*[0-9]/ {print $1"M"}' | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+ rts)
+ COMPREPLY=( $( compgen -W 'auto fixed off' -- $cur ) )
+ return 0
+ ;;
+ frag)
+ COMPREPLY=( $( compgen -W 'auto fixed off' -- $cur ) )
+ return 0
+ ;;
+ key)
+ COMPREPLY=( $( compgen -W 'off on open restricted' -- $cur ) )
+ return 0
+ ;;
+ enc)
+ COMPREPLY=( $( compgen -W 'off on open restricted' -- $cur ) )
+ return 0
+ ;;
+ power)
+ COMPREPLY=( $( compgen -W 'period timeout off on' -- $cur ) )
+ return 0
+ ;;
+ txpower)
+ COMPREPLY=( $( compgen -W 'off on auto' -- $cur ) )
+ return 0
+ ;;
+ retry)
+ COMPREPLY=( $( compgen -W 'limit lifetime' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) )
+ else
+ _available_interfaces -w
+ fi
+ else
+ COMPREPLY=( $( compgen -W 'essid nwid mode freq channel sens mode \
+ ap nick rate rts frag enc key power txpower commit' -- $cur ) )
+ fi
+
+} &&
+complete -F _iwconfig iwconfig
+
+# Linux iwlist(8) completion
+#
+[ $UNAME = Linux ] && have iwlist &&
+_iwlist()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) )
+ else
+ _available_interfaces -w
+ fi
+ else
+ COMPREPLY=( $( compgen -W 'scan scanning freq frequency \
+ channel rate bit bitrate key enc encryption power \
+ txpower retry ap accesspoint peers event' -- $cur ) )
+ fi
+} &&
+complete -F _iwlist iwlist
+
+# Linux iwspy(8) completion
+#
+[ $UNAME = Linux ] && have iwspy &&
+_iwspy()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) )
+ else
+ _available_interfaces -w
+ fi
+ else
+ COMPREPLY=( $( compgen -W 'setthr getthr off' -- $cur ) )
+ fi
+} &&
+complete -F _iwspy iwspy
+
+# Linux iwpriv(8) completion
+#
+[ $UNAME = Linux ] && have iwpriv &&
+_iwpriv()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ roam)
+ COMPREPLY=( $( compgen -W 'on off' -- $cur ) )
+ return 0
+ ;;
+ port)
+ COMPREPLY=( $( compgen -W 'ad-hoc managed' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) )
+ else
+ _available_interfaces -w
+ fi
+ else
+ COMPREPLY=( $( compgen -W '--all roam port' -- $cur ) )
+ fi
+} &&
+complete -F _iwpriv iwpriv
+
+# RedHat & Debian GNU/Linux if{up,down} completion
+#
+[ $UNAME = Linux ] && { have ifup || have ifdown; } &&
+_ifupdown()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ _configured_interfaces
+ COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- "$cur") )
+ fi
+
+ return 0
+} &&
+complete -F _ifupdown ifup ifdown
+[ $UNAME = Linux ] && have ifstatus && complete -F _ifupdown ifstatus
+
+# Linux ipsec(8) completion (for FreeS/WAN)
+#
+[ $UNAME = Linux ] && have ipsec &&
+_ipsec()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look \
+ manual pluto ranbits rsasigkey \
+ setup showdefaults showhostkey spi \
+ spigrp tncfg whack' -- $cur ) )
+ return 0
+ fi
+
+ case ${COMP_WORDS[1]} in
+ auto)
+ COMPREPLY=( $( compgen -W '--asynchronous --up --add --delete \
+ --replace --down --route --unroute \
+ --ready --status --rereadsecrets' \
+ -- $cur ) )
+ ;;
+ manual)
+ COMPREPLY=( $( compgen -W '--up --down --route --unroute \
+ --union' -- $cur ) )
+ ;;
+ ranbits)
+ COMPREPLY=( $( compgen -W '--quick --continuous --bytes' \
+ -- $cur ) )
+ ;;
+ setup)
+ COMPREPLY=( $( compgen -W '--start --stop --restart' -- $cur ) )
+ ;;
+
+ *)
+ ;;
+ esac
+
+ return 0
+} &&
+complete -F _ipsec ipsec
+
+# Postfix completion.
+#
+have postfix && {
+# postfix(1)
+#
+_postfix()
+{
+ local cur prev
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-c -D -v)
+ return 0
+ fi
+ if [[ $prev == '-c' ]]; then
+ _filedir -d
+ return 0
+ fi
+ if [[ $prev == '-D' ]]; then
+ COMPREPLY=( $( compgen -W 'start' -- "`get_cword`" ) )
+ return 0
+ fi
+ COMPREPLY=( $( compgen -W 'start stop reload abort flush check' -- \
+ "`get_cword`" ) )
+}
+complete -F _postfix postfix
+
+# postalias(1) and postmap(1)
+#
+_postmap()
+{
+ local cur prev len idx
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-N -f -i -n -o -p -r -v -w -c -d -q)
+ return 0
+ fi
+ if [[ $prev == '-c' ]]; then
+ _filedir -d
+ return 0
+ fi
+ if [[ $prev == -[dq] ]]; then
+ return 0
+ fi
+
+ if [[ "$cur" == *:* ]]; then
+ COMPREPLY=( $( compgen -f -- ${cur#*:} ) )
+ else
+ len=${#cur}
+ idx=0
+ for pval in $( /usr/sbin/postconf -m ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]="$pval:"
+ idx=$(($idx+1))
+ fi
+ done
+ if [[ $idx -eq 0 ]]; then
+ COMPREPLY=( $( compgen -f -- "$cur" ) )
+ fi
+ fi
+ return 0
+}
+complete -F _postmap postmap postalias
+
+# postcat(1)
+#
+_postcat()
+{
+ local cur prev pval len idx qfile
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-c -q -v)
+ return 0
+ fi
+ if [[ $prev == '-c' ]]; then
+ _filedir -d
+ return 0
+ fi
+
+ qfile=0
+ for idx in "${COMP_WORDS[@]}"; do
+ [[ "$idx" = -q ]] && qfile=1 && break
+ done
+ if [[ $qfile == 1 ]]; then
+ len=${#cur}
+ idx=0
+ for pval in $( mailq | \
+ sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; s/[* !].*$//' ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]=$pval
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+ else
+ _filedir
+ return 0
+ fi
+}
+complete -F _postcat postcat
+
+# postconf(1)
+#
+_postconf()
+{
+ local cur prev pval len idx eqext
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-c -d -e -h -m -l -n -v)
+ return 0
+ fi
+ if [[ $prev == '-c' ]]; then
+ _filedir -d
+ return 0
+ fi
+ if [[ $prev == '-e' ]]; then
+ cur=${cur#[\"\']}
+ eqext='='
+ fi
+ len=${#cur}
+ idx=0
+ for pval in $( /usr/sbin/postconf | cut -d ' ' -f 1 ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]="$pval$eqext"
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+}
+complete -F _postconf postconf
+
+# postsuper(1)
+#
+_postsuper()
+{
+ local cur prev pval len idx
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ $cur == '-' ]]; then
+ COMPREPLY=(-c -d -h -H -p -r -s -v)
+ return 0
+ fi
+ case $prev in
+ -[dr])
+ len=${#cur}
+ idx=0
+ for pval in $( echo ALL; mailq | \
+ sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; s/[* !].*$//' ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]=$pval
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+ ;;
+ -h)
+ len=${#cur}
+ idx=0
+ for pval in $( echo ALL; mailq | \
+ sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; s/[* ].*$//; /!$/d' ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]=$pval
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+ ;;
+ -H)
+ len=${#cur}
+ idx=0
+ for pval in $( echo ALL; mailq | \
+ sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; /^[0-9A-Z]*[* ]/d; s/!.*$//' ); do
+ if [[ "$cur" == "${pval:0:$len}" ]]; then
+ COMPREPLY[$idx]=$pval
+ idx=$(($idx+1))
+ fi
+ done
+ return 0
+ ;;
+ esac
+ COMPREPLY=( $( compgen -W 'hold incoming active deferred' -- $cur ) )
+ return 0
+}
+complete -F _postsuper postsuper
+}
+
+# cvs(1) completion
+#
+have cvs && {
+set_prefix()
+{
+ [ -z ${prefix:-} ] || prefix=${cur%/*}/
+ [ -r ${prefix:-}CVS/Entries ] || prefix=""
+}
+
+get_entries()
+{
+ local IFS=$'\n'
+ [ -r ${prefix:-}CVS/Entries ] && \
+ entries=$(cut -d/ -f2 -s ${prefix:-}CVS/Entries)
+}
+
+get_modules()
+{
+ if [ -n "$prefix" ]; then
+ COMPREPLY=( $( command ls -d ${cvsroot}/${prefix}/!(CVSROOT) ) )
+ else
+ COMPREPLY=( $( command ls -d ${cvsroot}/!(CVSROOT) ) )
+ fi
+}
+
+_cvs()
+{
+ local cur count mode i cvsroot cvsroots pwd
+ local -a flags miss files entries changed newremoved
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ count=0
+ for i in "${COMP_WORDS[@]}"; do
+ [ $count -eq $COMP_CWORD ] && break
+ # Last parameter was the CVSROOT, now go back to mode selection
+ if [ "${COMP_WORDS[((count))]}" == "$cvsroot" -a "$mode" == "cvsroot" ]; then
+ mode=""
+ fi
+ if [ -z "$mode" ]; then
+ case $i in
+ -d)
+ mode=cvsroot
+ cvsroot=${COMP_WORDS[((count+1))]}
+ ;;
+ @(ad?(d)|new))
+ mode=add
+ ;;
+ @(adm?(in)|rcs))
+ mode=admin
+ ;;
+ ann?(notate))
+ mode=annotate
+ ;;
+ @(checkout|co|get))
+ mode=checkout
+ ;;
+ @(com?(mit)|ci))
+ mode=commit
+ ;;
+ di?(f?(f)))
+ mode=diff
+ ;;
+ ex?(p?(ort)))
+ mode=export
+ ;;
+ ?(un)edit)
+ mode=$i
+ ;;
+ hi?(s?(tory)))
+ mode=history
+ ;;
+ im?(p?(ort)))
+ mode=import
+ ;;
+ re?(l?(ease)))
+ mode=release
+ ;;
+ ?(r)log)
+ mode=log
+ ;;
+ @(rdiff|patch))
+ mode=rdiff
+ ;;
+ @(remove|rm|delete))
+ mode=remove
+ ;;
+ @(rtag|rfreeze))
+ mode=rtag
+ ;;
+ st?(at?(us)))
+ mode=status
+ ;;
+ @(tag|freeze))
+ mode=tag
+ ;;
+ up?(d?(ate)))
+ mode=update
+ ;;
+ *)
+ ;;
+ esac
+ elif [[ "$i" = -* ]]; then
+ flags=( "${flags[@]}" $i )
+ fi
+ count=$((++count))
+ done
+
+ case "$mode" in
+ add)
+ if [[ "$cur" != -* ]]; then
+ set_prefix
+ if [ $COMP_CWORD -gt 1 -a -r ${prefix:-}CVS/Entries ]; then
+ get_entries
+ [ -z "$cur" ] && \
+ files=$( command ls -Ad !(CVS) ) || \
+ files=$( command ls -d ${cur}* 2>/dev/null )
+ for i in "${entries[@]}"; do
+ files=( ${files[@]/#$i//} )
+ done
+ COMPREPLY=( $( compgen -W '${files[@]}' -- \
+ $cur ) )
+ fi
+ else
+ COMPREPLY=( $( compgen -W '-k -m' -- $cur ) )
+ fi
+ ;;
+ admin)
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '-i -a -A -e -b -c -k -l -u \
+ -L -U -m -M -n -N -o -q -I \
+ -s -t -t- -T -V -x -z' -- \
+ $cur ) )
+ fi
+ ;;
+ annotate)
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '-D -F -f -l -R -r' -- $cur ) )
+ else
+ get_entries
+ COMPREPLY=( $( compgen -W '${entries[@]}' -- $cur ) )
+ fi
+ ;;
+ checkout)
+ if [[ "$cur" != -* ]]; then
+ [ -z "$cvsroot" ] && cvsroot=$CVSROOT
+ COMPREPLY=( $( cvs -d "$cvsroot" co -c 2> /dev/null | \
+ awk '{print $1}' ) )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W '-A -N -P -R -c -f -l -n -p \
+ -s -r -D -d -k -j' -- $cur ) )
+ fi
+ ;;
+ commit)
+ set_prefix
+
+ if [[ "$cur" != -* ]] && [ -r ${prefix:-}CVS/Entries ]; then
+ # if $COMP_CVS_REMOTE is not null, 'cvs commit' will
+ # complete on remotely checked-out files (requires
+ # passwordless access to the remote repository
+ if [ -n "${COMP_CVS_REMOTE:-}" ]; then
+ # this is the least computationally intensive
+ # way found so far, but other changes
+ # (something other than changed/removed/new)
+ # may be missing
+ changed=( $( cvs -q diff --brief 2>&1 | \
+ sed -ne 's/^Files [^ ]* and \([^ ]*\) differ$/\1/p' ) )
+ newremoved=( $( cvs -q diff --brief 2>&1 | \
+ sed -ne 's/^cvs diff: \([^ ]*\) .*, no comparison available$/\1/p' ) )
+ COMPREPLY=( $( compgen -W '${changed[@]:-} \
+ ${newremoved[@]:-}' -- $cur ) )
+ else
+ _filedir
+ fi
+ else
+ COMPREPLY=( $( compgen -W '-n -R -l -f -F -m -r' -- \
+ $cur ) )
+ fi
+ ;;
+ cvsroot)
+ if [ -r ~/.cvspass ]; then
+ # Ugly escaping because of bash treating ':' specially
+ cvsroots=$( sed 's/^[^ ]* //; s/:/\\:/g' ~/.cvspass )
+ COMPREPLY=( $( compgen -W '$cvsroots' -- $cur ) )
+ fi
+ ;;
+ export)
+ if [[ "$cur" != -* ]]; then
+ [ -z "$cvsroot" ] && cvsroot=$CVSROOT
+ COMPREPLY=( $( cvs -d "$cvsroot" co -c | awk '{print $1}' ) )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W '-N -f -l -R -n \
+ -r -D -d -k' -- $cur ) )
+ fi
+ ;;
+ diff)
+ if [[ "$cur" == -* ]]; then
+ _longopt diff
+ else
+ get_entries
+ COMPREPLY=( $( compgen -W '${entries[@]:-}' -- $cur ) )
+ fi
+ ;;
+ remove)
+ if [[ "$cur" != -* ]]; then
+ set_prefix
+ if [ $COMP_CWORD -gt 1 -a -r ${prefix:-}CVS/Entries ]; then
+ get_entries
+ # find out what files are missing
+ for i in "${entries[@]}"; do
+ [ ! -r "$i" ] && miss=( "${miss[@]}" $i )
+ done
+ COMPREPLY=( $(compgen -W '${miss[@]:-}' -- $cur) )
+ fi
+ else
+ COMPREPLY=( $( compgen -W '-f -l -R' -- $cur ) )
+ fi
+ ;;
+ import)
+ if [[ "$cur" != -* ]]; then
+ # starts with same algorithm as checkout
+ [ -z "$cvsroot" ] && cvsroot=$CVSROOT
+ prefix=${cur%/*}
+ if [ -r ${cvsroot}/${prefix} ]; then
+ get_modules
+ COMPREPLY=( ${COMPREPLY[@]#$cvsroot} )
+ COMPREPLY=( ${COMPREPLY[@]#\/} )
+ fi
+ pwd=$( pwd )
+ pwd=${pwd##*/}
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]} $pwd' -- \
+ $cur ) )
+ else
+ COMPREPLY=( $( compgen -W '-d -k -I -b -m -W' -- $cur ))
+ fi
+ ;;
+ update)
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '-A -P -C -d -f -l -R -p \
+ -k -r -D -j -I -W' -- \
+ $cur ) )
+ fi
+ ;;
+ "")
+ COMPREPLY=( $( compgen -W 'add admin annotate checkout ci co \
+ commit diff delete edit export \
+ freeze get history import log new \
+ patch rcs rdiff release remove \
+ rfreeze rlog rm rtag stat status \
+ tag unedit up update -H -Q -q -b \
+ -d -e -f -l -n -t -r -v -w -x -z \
+ --help --version' -- $cur ) )
+ ;;
+ *)
+ ;;
+ esac
+
+ return 0
+}
+complete -F _cvs $default cvs
+}
+
+have rpm && {
+# helper functions for rpm completion
+#
+_rpm_installed_packages()
+{
+ local ver nodig nosig
+
+ if [ -r /var/log/rpmpkgs -a \
+ /var/log/rpmpkgs -nt /var/lib/rpm/Packages ]; then
+ # using RHL 7.2 or later - this is quicker than querying the DB
+ COMPREPLY=( $( sed -ne \
+ 's|^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9a-z.@]\+.*\.rpm$|\1|p' \
+ /var/log/rpmpkgs ) )
+ else
+ nodig=""
+ nosig=""
+ ver=$(rpm --version)
+ ver=${ver##* }
+
+ if [[ "$ver" > "4.0.4" ]]; then
+ nodig="--nodigest"
+ fi
+ if [[ "$ver" > "4.0.99" ]]; then
+ nosig="--nosignature"
+ fi
+
+ COMPREPLY=( $( rpm -qa $nodig $nosig | sed -ne \
+ 's|^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9a-z.@]\+$|\1|p' ) )
+ fi
+}
+
+_rpm_groups()
+{
+ local IFS=$'\t'
+ # remove trailing backslash, or grep will complain
+ cur=${cur%"\\"}
+ COMPREPLY=( $( rpm -qa $nodig $nosig --queryformat '%{group}\n' | \
+ grep "^$cur" ) )
+ # backslash escape spaces and translate newlines to tabs
+ COMPREPLY=( $( echo "${COMPREPLY[@]}" | sed 's/ /\\ /g' | tr '\n' '\t' ) )
+}
+
+# rpm(8) completion
+#
+_rpm()
+{
+ local cur prev ver nodig nosig
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ nodig=""
+ nosig=""
+ ver=$(rpm --version); ver=${ver##* }
+
+ if [[ "$ver" > "4.0.4" ]]; then
+ nodig="--nodigest"
+ fi
+ if [[ "$ver" > "4.0.99" ]]; then
+ nosig="--nosignature"
+ fi
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ # first parameter on line
+ case "$cur" in
+ -b*)
+ COMPREPLY=( $( compgen -W '-ba -bb -bc -bi -bl -bp -bs'\
+ -- $cur ) )
+ ;;
+ -t*)
+ COMPREPLY=( $( compgen -W '-ta -tb -tc -ti -tl -tp -ts'\
+ -- $cur ) )
+ ;;
+ --*)
+ COMPREPLY=( $( compgen -W '--help --version --initdb \
+ --checksig --recompile --rebuild --resign --addsign \
+ --rebuilddb --showrc --setperms --setugids --tarbuild \
+ --eval --install --upgrade --query --freshen --erase \
+ --verify --querytags --rmsource --rmspec --clean \
+ --import' -- $cur ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W '-b -e -F -i -q -t -U -V' \
+ -- $cur ) )
+ ;;
+ esac
+
+ return 0
+ fi
+
+ case "$prev" in
+ --@(@(db|exclude)path|prefix|relocate|root))
+ _filedir -d
+ return 0
+ ;;
+ --eval)
+ # get a list of macros
+ COMPREPLY=( $( sed -ne 's|^\(%'${cur#\%}'[^ '$'\t'']*\).*$|\1|p' \
+ /usr/lib/rpm/macros ) )
+ return 0
+ ;;
+ --pipe)
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ return 0
+ ;;
+ --rcfile)
+ _filedir
+ return 0
+ ;;
+ --specfile)
+ # complete on .spec files
+ _filedir spec
+ return 0
+ ;;
+ --whatprovides)
+ if [[ "$cur" == */* ]]; then
+ _filedir
+ else
+ # complete on capabilities
+ COMPREPLY=( $( rpm -qa $nodig $nosig --queryformat \
+ '%{providename}\n' | grep "^$cur" ) )
+ fi
+ return 0
+ ;;
+ --whatrequires)
+ # complete on capabilities
+ COMPREPLY=( $( rpm -qa $nodig $nosig --queryformat \
+ '%{requirename}\n' | grep "^$cur" ) )
+ return 0
+ ;;
+ esac
+
+ case "${COMP_WORDS[1]}" in
+ -@([iFU]*|-install|-freshen|-upgrade))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--percent --force --test \
+ --replacepkgs --replacefiles --root --excludedocs \
+ --includedocs --noscripts --rcfile --ignorearch \
+ --dbpath --prefix --ignoreos --nodeps --allfiles \
+ --ftpproxy --ftpport --justdb --httpproxy --httpport \
+ --noorder --relocate --badreloc --notriggers \
+ --excludepath --ignoresize --oldpackage --define \
+ --eval --pipe --queryformat --repackage --nosuggests \
+ --nodigest --nosignature' -- $cur ) )
+ else
+ _filedir 'rpm'
+ fi
+ ;;
+ -@(e|-erase))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--allmatches --noscripts \
+ --notriggers --nodeps --test --repackage' -- $cur ) )
+ else
+ _rpm_installed_packages
+ fi
+ ;;
+ -@(q*|-query))
+ # check whether we're doing file completion
+ if [ "${COMP_LINE#* -*([^ -])f}" != "$COMP_LINE" ]; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--scripts --root \
+ --rcfile --requires --ftpport --ftpproxy \
+ --httpproxy --httpport --provides --triggers \
+ --dump --changelog --dbpath \
+ --last --filesbypkg \
+ --info --list --state \
+ --docfiles --configfiles --queryformat \
+ --conflicts --obsoletes \
+ --nodigest --nosignature \
+ --triggerscripts' -- $cur ) )
+ else
+ _filedir
+ fi
+ elif [ "${COMP_LINE#* -*([^ -])g}" != "$COMP_LINE" ]; then
+ _rpm_groups
+ elif [ "${COMP_LINE#* -*([^ -])p}" != "$COMP_LINE" ]; then
+ # uninstalled package completion
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--scripts --root \
+ --rcfile --whatprovides --whatrequires \
+ --requires --triggeredby --ftpport --ftpproxy \
+ --httpproxy --httpport --provides --triggers \
+ --dump --changelog --dbpath --filesbypkg \
+ --define --eval --pipe --showrc --info --list \
+ --state --docfiles --configfiles --queryformat\
+ --conflicts --obsoletes --nodigest \
+ --nosignature' -- $cur ) )
+ else
+ _filedir 'rpm'
+ fi
+ else
+ # installed package completion
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--scripts --root \
+ --rcfile --whatprovides --whatrequires \
+ --requires --triggeredby --ftpport --ftpproxy \
+ --httpproxy --httpport --provides --triggers \
+ --dump --changelog --dbpath --specfile \
+ --querybynumber --last --filesbypkg --define \
+ --eval --pipe --showrc --info --list --state \
+ --docfiles --configfiles --queryformat \
+ --conflicts --obsoletes --pkgid --hdrid \
+ --fileid --tid --nodigest --nosignature \
+ --triggerscripts' -- $cur ) )
+ elif [ "${COMP_LINE#* -*([^ -])a}" == "$COMP_LINE" ]; then
+ _rpm_installed_packages
+ fi
+ fi
+ ;;
+ -@(K*|-checksig))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--nopgp --nogpg --nomd5 \
+ --nodigest --nosignature' -- $cur ) )
+ else
+ _filedir 'rpm'
+ fi
+ ;;
+ -@([Vy]*|-verify))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--root --rcfile --dbpath \
+ --nodeps --nogroup --nolinkto --nomode --nomtime \
+ --nordev --nouser --nofiles --noscripts --nomd5 \
+ --querytags --specfile --whatrequires --whatprovides \
+ --nodigest --nosignature' -- $cur ) )
+ # check whether we're doing file completion
+ elif [ "${COMP_LINE#* -*([^ -])f}" != "$COMP_LINE" ]; then
+ _filedir
+ elif [ "${COMP_LINE#* -*([^ -])g}" != "$COMP_LINE" ]; then
+ _rpm_groups
+ elif [ "${COMP_LINE#* -*([^ -])p}" != "$COMP_LINE" ]; then
+ _filedir 'rpm'
+ else
+ _rpm_installed_packages
+ fi
+ ;;
+ -[bt]*)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--short-circuit --timecheck \
+ --clean --rmsource --rmspec --test --sign --buildroot \
+ --target -- buildarch --buildos --nobuild --nodeps \
+ --nodirtokens' -- $cur ) )
+ elif [[ ${COMP_WORDS[1]} == -b* ]]; then
+ _filedir 'spec'
+ else
+ _filedir '@(tgz|tar.@(gz|bz2))'
+ fi
+ ;;
+ --re@(build|compile))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--nodeps --rmsource \
+ --rmspec --sign --nodirtokens --target' -- $cur ) )
+ else
+ _filedir '?(no)src.rpm'
+ fi
+ ;;
+ --tarbuild)
+ _filedir '@(tgz|tar.@(gz|bz2))'
+ ;;
+ --@(re|add)sign)
+ _filedir 'rpm'
+ ;;
+ --set@(perms|gids))
+ _rpm_installed_packages
+ ;;
+ --@(clean|rms@(ource|pec)))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--clean --rmsource \
+ --rmspec' -- $cur ) )
+ else
+ _filedir 'spec'
+ fi
+ ;;
+ --@(import|dbpath|root))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--import --dbpath --root' \
+ -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ esac
+
+ return 0
+}
+complete -F _rpm $filenames rpm rpmbuild
+}
+
+# Debian apt-get(8) completion.
+#
+have apt-get &&
+_apt_get()
+{
+ local cur prev special i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(install|remove|purge|source|build-dep) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ done
+
+ if [ -n "$special" ]; then
+ case $special in
+ remove|purge)
+ if [ -f /etc/debian_version ]; then
+ # Debian system
+ COMPREPLY=( $( _comp_dpkg_installed_packages \
+ $cur ) )
+ else
+ # assume RPM based
+ _rpm_installed_packages
+ fi
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) )
+ return 0
+ ;;
+
+ esac
+ fi
+
+ case "$prev" in
+ -@(c|-config-file))
+ _filedir
+ return 0
+ ;;
+
+ -@(t|-target-release|-default-release))
+ COMPREPLY=( $( apt-cache policy | \
+ grep "release.o=Debian,a=$cur" | \
+ sed -e "s/.*a=\(\w*\).*/\1/" | uniq 2> /dev/null) )
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$cur" == -* ]]; then
+
+ COMPREPLY=( $( compgen -W '-d -f -h -v -m -q -s -y \
+ -u -t -b -c -o --download-only --fix-broken \
+ --help --version --ignore-missing \
+ --fix-missing --no-download --quiet --simulate \
+ --just-print --dry-run --recon --no-act --yes \
+ --assume-yes --show-upgraded --only-source \
+ --compile --build --ignore-hold \
+ --target-release --no-upgrade --force-yes \
+ --print-uris --purge --reinstall \
+ --list-cleanup --default-release \
+ --trivial-only --no-remove --diff-only \
+ --tar-only --config-file --option --auto-remove' -- $cur ) )
+ else
+
+ COMPREPLY=( $( compgen -W 'update upgrade dselect-upgrade \
+ dist-upgrade install remove purge source \
+ build-dep check clean autoclean autoremove' \
+ -- $cur ) )
+
+ fi
+
+
+ return 0
+} &&
+complete -F _apt_get $filenames apt-get
+
+# Debian apt-cache(8) completion.
+#
+have apt-cache &&
+_apt_cache()
+{
+ local cur prev special i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+
+ if [ "$cur" != show ]; then
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(add|depends|dotty|policy|rdepends|madison|show?(pkg|src|)) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ done
+ fi
+
+
+ if [ -n "$special" ]; then
+ case $special in
+ add)
+ _filedir
+ return 0
+ ;;
+
+ *)
+ COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) )
+ return 0
+ ;;
+
+ esac
+ fi
+
+
+ case "$prev" in
+ -@(c|p|s|-config-file|-@(pkg|src)-cache))
+ _filedir
+ return 0
+ ;;
+ search)
+ if [[ "$cur" != -* ]]; then
+ return 0
+ fi
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+
+ COMPREPLY=( $( compgen -W '-h -v -p -s -q -i -f -a -g -c \
+ -o --help --version --pkg-cache --src-cache \
+ --quiet --important --full --all-versions \
+ --no-all-versions --generate --no-generate \
+ --names-only --all-names --recurse \
+ --config-file --option' -- $cur ) )
+ else
+
+ COMPREPLY=( $( compgen -W 'add gencaches show showpkg showsrc \
+ stats dump dumpavail unmet search search \
+ depends rdepends pkgnames dotty xvcg \
+ policy madison' -- $cur ) )
+
+ fi
+
+
+ return 0
+} &&
+complete -F _apt_cache $filenames apt-cache
+
+
+# Debian aptitude(1) completion
+#
+have aptitude && {
+have grep-status && {
+_comp_dpkg_hold_packages()
+{
+ grep-status -P -e "^$1" -a -FStatus 'hold' -n -s Package
+}
+} || {
+_comp_dpkg_hold_packages()
+{
+ grep -B 2 'hold' /var/lib/dpkg/status | grep "Package: $1" \
+ | cut -d\ -f2
+}
+}
+
+_aptitude()
+{
+ local cur dashoptions prev special i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+
+ dashoptions='-S -u -i -h --help --version -s --simulate -d \
+ --download-only -P --prompt -y --assume-yes -F \
+ --display-format -O --sort -w --width -f -r -g \
+ --with-recommends --with-suggests -R -G \
+ --without-recommends --without-suggests -t \
+ --target-release -V --show-versions -D --show-deps\
+ -Z -v --verbose --purge-unused'
+
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(install|reinstall|hold|unhold|markauto|unmarkauto|dist-upgrade|full-upgrade|download|show|forbid-version|purge|remove|changelog|why|why-not|keep|keep-all) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ #exclude some mutually exclusive options
+ [[ ${COMP_WORDS[i]} == '-u' ]] && dashoptions=${dashoptions/-i}
+ [[ ${COMP_WORDS[i]} == '-i' ]] && dashoptions=${dashoptions/-u}
+ done
+
+ if [[ -n "$special" ]]; then
+ case $special in
+ @(install|hold|markauto|unmarkauto|dist-upgrade|full-upgrade|download|show|changelog|why|why-not))
+ COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) )
+ return 0
+ ;;
+ @(purge|remove|reinstall|forbid-version))
+ COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) )
+ return 0
+ ;;
+ unhold)
+ COMPREPLY=( $( _comp_dpkg_hold_packages $cur ) )
+ return 0
+ ;;
+
+ esac
+ fi
+
+ case $prev in
+ # don't complete anything if these options are found
+ @(autoclean|clean|forget-new|search|upgrade|safe-upgrade|update|keep-all))
+ return 0
+ ;;
+
+ -S)
+ _filedir
+ return 0
+ ;;
+
+ -@(t|-target-release|-default-release))
+ COMPREPLY=( $( apt-cache policy | \
+ grep "release.o=Debian,a=$cur" | \
+ sed -e "s/.*a=\(\w*\).*/\1/" | uniq 2> /dev/null ) )
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W "$dashoptions" -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W 'update upgrade safe-upgrade forget-new clean \
+ autoclean install reinstall remove \
+ hold unhold purge markauto unmarkauto why why-not \
+ dist-upgrade full-upgrade download search show \
+ forbid-version changelog keep-all' -- $cur ) )
+ fi
+
+
+ return 0
+}
+complete -F _aptitude $default aptitude
+}
+
+# Debian apt-build(1) completion.
+#
+have apt-build &&
+_apt_build()
+{
+ local cur prev special i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(install|remove|source|info|clean) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ done
+
+ if [ -n "$special" ]; then
+ case $special in
+ @(install|source|info))
+ COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) )
+ return 0
+ ;;
+ remove)
+ COMPREPLY=( $( _comp_dpkg_installed_packages \
+ $cur ) )
+ return 0
+ ;;
+ *)
+ return 0
+ ;;
+ esac
+ fi
+
+ case "$prev" in
+
+ --@(patch|build-dir|repository-dir))
+ _filedir
+ return 0
+ ;;
+
+ -@(h|-help))
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --show-upgraded -u --build-dir \
+ --repository-dir --build-only \
+ --build-command --reinstall --rebuild \
+ --remove-builddep --no-wrapper --purge \
+ --patch --patch-strip -p --yes -y \
+ --version -v --no-source' -- $cur ) )
+
+ else
+ COMPREPLY=( $( compgen -W 'update upgrade install remove \
+ source dist-upgrade world clean info \
+ clean-build update-repository ' -- $cur ) )
+ fi
+
+
+ return 0
+} &&
+complete -F _apt_build $filenames apt-build
+
+# chsh(1) completion
+#
+_chsh()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ "$prev" = "-s" ]; then
+ if [ -f /etc/debian_version ]; then
+ COMPREPLY=( $( </etc/shells ) )
+ else
+ COMPREPLY=( $( chsh -l | grep "^$cur" ) )
+ fi
+ else
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ fi
+
+ return 0
+}
+complete -F _chsh chsh
+
+# chkconfig(8) completion
+#
+have chkconfig &&
+_chkconfig()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ @([1-6]|--@(list|add|del)))
+ _services
+ return 0
+ ;;
+ --level)
+ COMPREPLY=( $( compgen -W '1 2 3 4 5 6' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--list --add --del --level' -- $cur ) )
+ else
+ if [ $COMP_CWORD -eq 2 -o $COMP_CWORD -eq 4 ]; then
+ COMPREPLY=( $( compgen -W 'on off reset' -- $cur ) )
+ else
+ _services
+ fi
+ fi
+} &&
+complete -F _chkconfig chkconfig
+
+# This function provides simple user@host completion
+#
+_user_at_host() {
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ $cur == *@* ]]; then
+ _known_hosts
+ else
+ COMPREPLY=( $( compgen -u -- "$cur" ) )
+ fi
+
+ return 0
+}
+shopt -u hostcomplete && complete -F _user_at_host $nospace talk ytalk finger
+
+# This function performs host completion based on ssh's known_hosts files,
+# defaulting to standard host completion if they don't exist.
+#
+_known_hosts()
+{
+ local cur curd ocur user suffix aliases global_kh user_kh hosts i host
+ local -a kh khd config
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ ocur=$cur
+
+ [ "$1" = -a ] || [ "$2" = -a ] && aliases='yes'
+ [ "$1" = -c ] || [ "$2" = -c ] && suffix=':'
+ [[ $cur == *@* ]] && user=${cur%@*}@ && cur=${cur#*@}
+ kh=()
+
+ # ssh config files
+ [ -r /etc/ssh/ssh_config ] &&
+ config=( "${config[@]}" "/etc/ssh/ssh_config" )
+ [ -r "${HOME}/.ssh/config" ] &&
+ config=( "${config[@]}" "${HOME}/.ssh/config" )
+ [ -r "${HOME}/.ssh2/config" ] &&
+ config=( "${config[@]}" "${HOME}/.ssh2/config" )
+
+ if [ ${#config[@]} -gt 0 ]; then
+ # expand path (if present) to global known hosts file
+ global_kh=$( eval echo $( sed -ne 's/^[ \t]*[Gg][Ll][Oo][Bb][Aa][Ll][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['"$'\t '"']*\(.*\)$/\1/p' "${config[@]}" ) )
+ # expand path (if present) to user known hosts file
+ user_kh=$( eval echo $( sed -ne 's/^[ \t]*[Uu][Ss][Ee][Rr][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['"$'\t '"']*\(.*\)$/\1/p' "${config[@]}" ) )
+ fi
+
+ # Global known_hosts files
+ [ -r "$global_kh" ] &&
+ kh=( "${kh[@]}" "$global_kh" )
+ [ -r /etc/ssh/ssh_known_hosts ] &&
+ kh=( "${kh[@]}" /etc/ssh/ssh_known_hosts )
+ [ -r /etc/ssh/ssh_known_hosts2 ] &&
+ kh=( "${kh[@]}" /etc/ssh/ssh_known_hosts2 )
+ [ -r /etc/known_hosts ] &&
+ kh=( "${kh[@]}" /etc/known_hosts )
+ [ -r /etc/known_hosts2 ] &&
+ kh=( "${kh[@]}" /etc/known_hosts2 )
+ [ -d /etc/ssh2/knownhosts ] &&
+ khd=( "${khd[@]}" /etc/ssh2/knownhosts/*pub )
+
+ # User known_hosts files
+ [ -r "$user_kh" ] &&
+ kh=( "${kh[@]}" "$user_kh" )
+ [ -r ~/.ssh/known_hosts ] &&
+ kh=( "${kh[@]}" ~/.ssh/known_hosts )
+ [ -r ~/.ssh/known_hosts2 ] &&
+ kh=( "${kh[@]}" ~/.ssh/known_hosts2 )
+ [ -d ~/.ssh2/hostkeys ] &&
+ khd=( "${khd[@]}" ~/.ssh2/hostkeys/*pub )
+
+ # If we have known_hosts files to use
+ if [ ${#kh[@]} -gt 0 -o ${#khd[@]} -gt 0 ]; then
+ # Escape slashes and dots in paths for awk
+ cur=${cur//\//\\\/}
+ cur=${cur//\./\\\.}
+ curd=$cur
+
+ if [[ "$cur" == [0-9]*.* ]]; then
+ # Digits followed by a dot - just search for that
+ cur="^$cur.*"
+ elif [[ "$cur" == [0-9]* ]]; then
+ # Digits followed by no dot - search for digits followed
+ # by a dot
+ cur="^$cur.*\."
+ elif [ -z "$cur" ]; then
+ # A blank - search for a dot or an alpha character
+ cur="[a-z.]"
+ else
+ cur="^$cur"
+ fi
+
+ if [ ${#kh[@]} -gt 0 ]; then
+
+ # FS needs to look for a comma separated list
+ COMPREPLY=( $( awk 'BEGIN {FS=","}
+ /^[^|]/ {for (i=1; i<=2; ++i) { \
+ gsub(" .*$", "", $i); \
+ if ($i ~ /'$cur'/) {print $i} \
+ }}' "${kh[@]}" 2>/dev/null ) )
+ fi
+ if [ ${#khd[@]} -gt 0 ]; then
+ # Needs to look for files called
+ # .../.ssh2/key_22_<hostname>.pub
+ # dont fork any processes, because in a cluster environment,
+ # there can be hundreds of hostkeys
+ for i in "${khd[@]}" ; do
+ if [[ "$i" == *key_22_$curd*.pub ]] && [ -r "$i" ] ; then
+ host=${i/#*key_22_/}
+ host=${host/%.pub/}
+ COMPREPLY=( "${COMPREPLY[@]}" $host )
+ fi
+ done
+ fi
+
+ # append any available aliases from config files
+ if [ ${#config[@]} -gt 0 ] && [ -n "$aliases" ]; then
+ local host_aliases=$( sed -ne 's/^[Hh][Oo][Ss][Tt]\([Nn][Aa][Mm][Ee]\)\?['"$'\t '"']\+\([^*?]*\)$/\2/p' "${config[@]}" )
+ hosts=$( compgen -W "$host_aliases" -- $ocur )
+ COMPREPLY=( "${COMPREPLY[@]}" $hosts )
+ fi
+
+ # Now add results of normal hostname completion
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -A hostname -- $ocur ) )
+
+ # apply suffix
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[i]=$user${COMPREPLY[i]}$suffix
+ done
+ else
+ # Just do normal hostname completion
+ COMPREPLY=( $( compgen -A hostname -S "$suffix" -- $cur ) )
+ fi
+
+ return 0
+}
+complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 \
+ ping ping6 fping fping6 telnet host nslookup rsh rlogin ftp dig ssh-installkeys mtr
+
+# ssh(1) completion
+#
+have ssh && {
+_ssh()
+{
+ local cur prev
+ local -a config
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -*c)
+ COMPREPLY=( $( compgen -W 'blowfish 3des 3des-cbc blowfish-cbc \
+ arcfour cast128-cbc' -- $cur ) )
+ ;;
+ -*i)
+ _filedir
+ ;;
+ -*l)
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ ;;
+ *)
+ _known_hosts -a
+
+ [ $COMP_CWORD -eq 1 ] || \
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -c -- $cur ) )
+ esac
+
+ return 0
+}
+shopt -u hostcomplete && complete -F _ssh ssh slogin sftp xhost autossh
+
+# scp(1) completion
+#
+_scp()
+{
+ local cur userhost path
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _expand || return 0
+
+ if [[ "$cur" == *:* ]]; then
+ local IFS=$'\t\n'
+ # remove backslash escape from :
+ cur=${cur/\\:/:}
+ userhost=${cur%%?(\\):*}
+ path=${cur#*:}
+ # unescape spaces
+ path=${path//\\\\\\\\ / }
+ if [ -z "$path" ]; then
+ # default to home dir of specified user on remote host
+ path=$(ssh -o 'Batchmode yes' $userhost pwd 2>/dev/null)
+ fi
+ # escape spaces; remove executables, aliases, pipes and sockets;
+ # add space at end of file names
+ COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
+ command ls -aF1d "$path*" 2>/dev/null | \
+ sed -e "s/[][(){}<>\",:;^&!$&=?\`|\\ ']/\\\\\\\\\\\\&/g" \
+ -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) )
+ return 0
+ fi
+
+ [[ "$cur" == */* ]] || _known_hosts -c -a
+ local IFS=$'\t\n'
+ COMPREPLY=( "${COMPREPLY[@]}" $( command ls -aF1d $cur* \
+ 2>/dev/null | sed \
+ -e "s/[][(){}<>\",:;^&!$&=?\`|\\ ']/\\\\&/g" \
+ -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) )
+ return 0
+}
+complete -F _scp $nospace scp
+}
+
+# rsync(1) completion
+#
+have rsync &&
+_rsync()
+{
+ local cur prev shell i userhost path
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ _expand || return 0
+
+ case "$prev" in
+ --@(config|password-file|include-from|exclude-from))
+ _filedir
+ return 0
+ ;;
+ -@(T|-temp-dir|-compare-dest))
+ _filedir -d
+ return 0
+ ;;
+ -@(e|-rsh))
+ COMPREPLY=( $( compgen -W 'rsh ssh' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W '-v -q -c -a -r -R -b -u -l -L -H \
+ -p -o -g -D -t -S -n -W -x -B -e -C -I -T -P \
+ -z -h -4 -6 --verbose --quiet --checksum \
+ --archive --recursive --relative --backup \
+ --backup-dir --suffix= --update --links \
+ --copy-links --copy-unsafe-links --safe-links \
+ --hard-links --perms --owner --group --devices\
+ --times --sparse --dry-run --whole-file \
+ --no-whole-file --one-file-system \
+ --block-size= --rsh= --rsync-path= \
+ --cvs-exclude --existing --ignore-existing \
+ --delete --delete-excluded --delete-after \
+ --ignore-errors --max-delete= --partial \
+ --force --numeric-ids --timeout= \
+ --ignore-times --size-only --modify-window= \
+ --temp-dir= --compare-dest= --compress \
+ --exclude= --exclude-from= --include= \
+ --include-from= --version --daemon --no-detach\
+ --address= --config= --port= --blocking-io \
+ --no-blocking-io --stats --progress \
+ --log-format= --password-file= --bwlimit= \
+ --write-batch= --read-batch= --help' -- $cur ))
+ ;;
+ *:*)
+ # find which remote shell is used
+ shell=rsh
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -@(e|-rsh) ]]; then
+ shell=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ if [[ "$shell" == ssh ]]; then
+ # remove backslash escape from :
+ cur=${cur/\\:/:}
+ userhost=${cur%%?(\\):*}
+ path=${cur#*:}
+ # unescape spaces
+ path=${path//\\\\\\\\ / }
+ if [ -z "$path" ]; then
+ # default to home dir of specified
+ # user on remote host
+ path=$(ssh -o 'Batchmode yes' \
+ $userhost pwd 2>/dev/null)
+ fi
+ # escape spaces; remove executables, aliases, pipes
+ # and sockets; add space at end of file names
+ COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \
+ command ls -aF1d "$path*" 2>/dev/null | \
+ sed -e 's/ /\\\\\\\ /g' -e 's/[*@|=]$//g' \
+ -e 's/[^\/]$/& /g' ) )
+ fi
+ ;;
+ *)
+ _known_hosts -c -a
+ _filedir
+ ;;
+ esac
+
+ return 0
+} &&
+complete -F _rsync $nospace $filenames rsync
+
+# Linux route(8) completion
+#
+[ $UNAME = Linux ] &&
+_route()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ "$prev" = dev ]; then
+ COMPREPLY=( $( ifconfig -a | sed -ne 's|^\('$cur'[^ ]*\).*$|\1|p' ))
+ return 0
+ fi
+
+ COMPREPLY=( $( compgen -W 'add del -host -net netmask metric mss \
+ window irtt reject mod dyn reinstate dev \
+ default gw' -- $cur ) )
+
+ COMPREPLY=( $( echo " ${COMP_WORDS[@]}" | \
+ (while read -d ' ' i; do
+ [ "$i" == "" ] && continue
+ # flatten array with spaces on either side,
+ # otherwise we cannot grep on word
+ # boundaries of first and last word
+ COMPREPLY=" ${COMPREPLY[@]} "
+ # remove word from list of completions
+ COMPREPLY=( ${COMPREPLY/ $i / } )
+ done
+ echo "${COMPREPLY[@]}")
+ ) )
+ return 0
+}
+[ $UNAME = Linux ] && complete -F _route route
+
+# GNU make(1) completion
+#
+have make || have gmake || have gnumake || have pmake &&
+_make()
+{
+ local file makef makef_dir="." makef_inc cur prev i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case $prev in
+ -@(f|o|W))
+ _filedir
+ return 0
+ ;;
+ -@(I|C))
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --@(file|makefile))
+ _filedir
+ return 0
+ ;;
+ --@(directory|include-dir))
+ _filedir -d
+ return 0
+ ;;
+ esac
+ fi
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-b -m -B -C -d -e -f -h -i -I\
+ -j -l -k -n -o -p -q -r -R - s -S -t -v -w -W \
+ --always-make --directory= --debug \
+ --environment-overrides --file= --makefile= --help \
+ --ignore-errors --include-dir= --jobs --load-average \
+ --max-load --keep-going --just-print --dry-run \
+ --recon --old-file= --assume-old= --print-data-base \
+ --question --no-builtin-rules --no-builtin-variables \
+ --silent --quiet --no-keep-goind --stop --touch \
+ --version --print-directory --no-print-directory \
+ --what-if= --new-file= --assume-new= \
+ --warn-undefined-variables' -- $cur ) )
+ else
+ # before we check for makefiles, see if a path was specified
+ # with -C
+ for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do
+ if [[ ${COMP_WORDS[i]} == -C ]]; then
+ # eval for tilde expansion
+ eval makef_dir=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+
+ # make reads `GNUmakefile', then `makefile', then `Makefile'
+ if [ -f ${makef_dir}/GNUmakefile ]; then
+ makef=${makef_dir}/GNUmakefile
+ elif [ -f ${makef_dir}/makefile ]; then
+ makef=${makef_dir}/makefile
+ elif [ -f ${makef_dir}/Makefile ]; then
+ makef=${makef_dir}/Makefile
+ else
+ makef=${makef_dir}/*.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 for tilde expansion
+ eval makef=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+
+ [ ! -f $makef ] && return 0
+
+ # deal with included Makefiles
+ makef_inc=$( grep -E '^-?include' $makef | sed -e "s,^.* ,"$makef_dir"/," )
+
+ for file in $makef_inc; do
+ [ -f $file ] && makef="$makef $file"
+ done
+
+ COMPREPLY=( $( awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ \
+ {split($1,A,/ /);for(i in A)print A[i]}' \
+ $makef 2>/dev/null | command grep "^$cur" ))
+ fi
+} &&
+complete -f -F _make $filenames make gmake gnumake pmake
+
+# GNU tar(1) completion
+#
+_tar()
+{
+ local cur ext regex tar untar
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'c t x u r d A' -- $cur ) )
+ return 0
+ fi
+
+ case "${COMP_WORDS[1]}" in
+ ?(-)[cr]*f)
+ _filedir
+ return 0
+ ;;
+ +([^IZzjy])f)
+ ext='t@(ar?(.@(Z|gz|bz?(2)))|gz|bz?(2))'
+ regex='t\(ar\(\.\(Z\|gz\|bz2\?\)\)\?\|gz\|bz2\?\)'
+ ;;
+ *[Zz]*f)
+ ext='t?(ar.)@(gz|Z)'
+ regex='t\(ar\.\)\?\(gz\|Z\)'
+ ;;
+ *[Ijy]*f)
+ ext='t?(ar.)bz?(2)'
+ regex='t\(ar\.\)\?bz2\?'
+ ;;
+ *)
+ _filedir
+ return 0
+ ;;
+
+ esac
+
+ if [[ "$COMP_LINE" == *$ext' ' ]]; then
+ # complete on files in tar file
+ #
+ # get name of tar file from command line
+ tar=$( echo "$COMP_LINE" | \
+ sed -e 's/^.* \([^ ]*'$regex'\) .*$/\1/' )
+ # devise how to untar and list it
+ untar=t${COMP_WORDS[1]//[^Izjyf]/}
+
+ COMPREPLY=( $( compgen -W "$( echo $( tar $untar $tar \
+ 2>/dev/null ) )" -- "$cur" ) )
+ return 0
+ fi
+
+ # file completion on relevant files
+ _filedir "$ext"
+
+ return 0
+}
+[ -n "${COMP_TAR_INTERNAL_PATHS:-}" ] && complete -F _tar $dirnames tar ||
+ complete -F _tar $filenames tar
+
+# jar(1) completion
+#
+have jar &&
+_jar()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD = 1 ]; then
+ COMPREPLY=( $( compgen -W 'c t x u' -- $cur ) )
+ return 0
+ fi
+
+ case "${COMP_WORDS[1]}" in
+ *c*f)
+ _filedir
+ ;;
+ *f)
+ _filedir '?(e|j|w)ar'
+ ;;
+ *)
+ _filedir
+ ;;
+ esac
+} &&
+complete -F _jar $filenames jar
+
+# Linux iptables(8) completion
+#
+have iptables &&
+_iptables()
+{
+ local cur prev table chain
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ chain='s/^Chain \([^ ]\+\).*$/\1/p'
+
+ if [[ $COMP_LINE == *-t\ *filter* ]]; then
+ table="-t filter"
+ elif [[ $COMP_LINE == *-t\ *nat* ]]; then
+ table="-t nat"
+ elif [[ $COMP_LINE == *-t\ *mangle* ]]; then
+ table="-t mangle"
+ fi
+
+ case "$prev" in
+ -*[AIDRPFXLZ])
+ COMPREPLY=( $( compgen -W '`iptables $table -nL | \
+ sed -ne "s/^Chain \([^ ]\+\).*$/\1/p"`' -- $cur ) )
+ ;;
+ -*t)
+ COMPREPLY=( $( compgen -W 'nat filter mangle' -- $cur ) )
+ ;;
+ -j)
+ if [ "$table" = "-t filter" -o "$table" = "" ]; then
+ COMPREPLY=( $( compgen -W 'ACCEPT DROP LOG ULOG REJECT \
+ `iptables $table -nL | sed -ne "$chain" \
+ -e "s/INPUT|OUTPUT|FORWARD|PREROUTING|POSTROUTING//"`' -- \
+ $cur ) )
+ elif [ "$table" = "-t nat" ]; then
+ COMPREPLY=( $( compgen -W 'ACCEPT DROP LOG ULOG REJECT \
+ MIRROR SNAT DNAT MASQUERADE `iptables $table -nL | \
+ sed -ne "$chain" -e "s/OUTPUT|PREROUTING|POSTROUTING//"`' \
+ -- $cur ) )
+ elif [ "$table" = "-t mangle" ]; then
+ COMPREPLY=( $( compgen -W 'ACCEPT DROP LOG ULOG REJECT \
+ MARK TOS `iptables $table -nL | sed -ne "$chain" \
+ -e "s/INPUT|OUTPUT|FORWARD|PREROUTING|POSTROUTING//"`' -- \
+ $cur ) )
+ fi
+ ;;
+ *)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-i -o -s -d -p -f -m --append \
+ --delete --insert --replace --list --flush --zero --new \
+ --delete-chain --policy --rename-chain --proto --source \
+ --destination --in-interface --jump --match --numeric \
+ --out-interface --table --verbose --line-numbers --exact \
+ --fragment --modprobe= --set-counters --version' -- "$cur") )
+ fi
+ ;;
+ esac
+
+} &&
+complete -F _iptables iptables
+
+# tcpdump(8) completion
+#
+have tcpdump &&
+_tcpdump()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(r|w|F))
+ _filedir
+ return 0
+ ;;
+ -i)
+ _available_interfaces -a
+ return 0
+ ;;
+ esac
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a -d -e -f -l -n -N -O -p \
+ -q -R -S -t -u -v -x -C -F -i -m -r -s -T -w \
+ -E' -- $cur ) )
+ fi
+
+} &&
+complete -F _tcpdump tcpdump
+
+# autorpm(8) completion
+#
+have autorpm &&
+_autorpm()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -W '--notty --debug --help --version \
+ auto add fullinfo info help install list \
+ remove set' -- $cur ) )
+
+} &&
+complete -F _autorpm autorpm
+
+# This meta-cd function observes the CDPATH variable, so that cd additionally
+# completes on directories under those specified in CDPATH.
+#
+_cd()
+{
+ local IFS=$'\t\n' cur=`_get_cword` i j k
+
+ # try to allow variable completion
+ if [[ "$cur" == ?(\\)\$* ]]; then
+ COMPREPLY=( $( compgen -v -P '$' -- "${cur#?(\\)$}" ) )
+ return 0
+ fi
+
+ # Use standard dir completion if no CDPATH or parameter starts with /,
+ # ./ or ../
+ if [ -z "${CDPATH:-}" ] || [[ "$cur" == ?(.)?(.)/* ]]; then
+ _filedir -d
+ return 0
+ fi
+
+ local -r mark_dirs=$(_rl_enabled mark-directories && echo y)
+ local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y)
+
+ # we have a CDPATH, so loop on its contents
+ for i in ${CDPATH//:/$'\t'}; do
+ # create an array of matched subdirs
+ k="${#COMPREPLY[@]}"
+ for j in $( compgen -d $i/$cur ); do
+ if [[ ( $mark_symdirs && -h $j || $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then
+ j="${j}/"
+ fi
+ COMPREPLY[k++]=${j#$i/}
+ done
+ done
+
+ _filedir -d
+
+ if [[ ${#COMPREPLY[@]} -eq 1 ]]; then
+ i=${COMPREPLY[0]}
+ if [ "$i" == "$cur" ] && [[ $i != "*/" ]]; then
+ COMPREPLY[0]="${i}/"
+ fi
+ fi
+
+ return 0
+}
+if shopt -q cdable_vars; then
+ complete -v -F _cd $nospace $filenames cd
+else
+ complete -F _cd $nospace $filenames cd
+fi
+
+_remove_comp_word()
+{
+ if [[ COMP_CWORD -eq 0 ]]; then
+ return
+ elif [[ ${#COMP_WORDS[@]} -ge 2 ]]; then
+ local old_cw0="${COMP_WORDS[0]}"
+ local new_cw0="${COMP_WORDS[1]}"
+ local old_length="${#COMP_LINE}"
+ COMP_LINE=${COMP_LINE#${old_cw0}}
+ local head=${COMP_LINE:0:${#new_cw0}}
+ local i=1
+ while [[ $head != $new_cw0 ]]; do
+ COMP_LINE=${COMP_LINE:1}
+ head=${COMP_LINE:0:${#new_cw0}}
+ if (( ++i > 10 )); then
+ break
+ fi
+ done
+ local new_length="${#COMP_LINE}"
+ COMP_POINT=$(( COMP_POINT + new_length - old_length))
+
+ COMP_CWORD=$(( COMP_CWORD - 1 ))
+ for (( i=0; i < ${#COMP_WORDS[@]} - 1; ++i )); do
+ COMP_WORDS[i]="${COMP_WORDS[i+1]}"
+ done
+ unset COMP_WORDS[${#COMP_WORDS[@]}-1]
+ else
+ return
+ fi
+}
+
+# A meta-command completion function for commands like sudo(8), which need to
+# first complete on a command, then complete according to that command's own
+# completion definition - currently not quite foolproof (e.g. mount and umount
+# don't work properly), but still quite useful.
+#
+_command()
+{
+ local cur func cline cspec noglob cmd done i \
+ _COMMAND_FUNC _COMMAND_FUNC_ARGS
+
+ _remove_comp_word
+ COMPREPLY=()
+ cur=`_get_cword`
+ # If the the first arguments following our meta-command-invoker are
+ # switches, get rid of them. Most definitely not foolproof.
+ done=
+ while [ -z $done ] ; do
+ cmd=${COMP_WORDS[0]}
+ if [[ "$cmd" == -* ]] && [ $COMP_CWORD -ge 1 ]; then
+ _remove_comp_word
+ elif [[ "$cmd" == -* ]] && [[ $COMP_CWORD -eq 0 ]]; then
+ return
+ else
+ done=1
+ fi
+ done
+
+ if [ $COMP_CWORD -eq 0 ]; then
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ elif complete -p $cmd &>/dev/null; then
+ cspec=$( complete -p $cmd )
+ if [ "${cspec#* -F }" != "$cspec" ]; then
+ # COMP_CWORD and COMP_WORDS() are not read-only,
+ # so we can set them before handing off to regular
+ # completion routine
+
+ # get function name
+ func=${cspec#*-F }
+ func=${func%% *}
+
+ if [[ ${#COMP_WORDS[@]} -ge 2 ]]; then
+ $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}" "${COMP_WORDS[${#COMP_WORDS[@]}-2]}"
+ else
+ $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}"
+ fi
+
+ # remove any \: generated by a command that doesn't
+ # default to filenames or dirnames (e.g. sudo chown)
+ # FIXME: I'm pretty sure this does not work!
+ if [ "${cspec#*-o }" != "$cspec" ]; then
+ cspec=${cspec#*-o }
+ cspec=${cspec%% *}
+ if [[ "$cspec" != @(dir|file)names ]]; then
+ COMPREPLY=("${COMPREPLY[@]//\\\\:/:}")
+ fi
+ fi
+ elif [ -n "$cspec" ]; then
+ cspec=${cspec#complete};
+ cspec=${cspec%%$cmd};
+ COMPREPLY=( $( eval compgen "$cspec" -- "$cur" ) );
+ fi
+ fi
+
+ [ ${#COMPREPLY[@]} -eq 0 ] && _filedir
+}
+complete -F _command $filenames nohup exec nice eval strace time ltrace then \
+ else do vsound command xargs
+
+_root_command()
+{
+ PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin _command $1 $2 $3
+}
+complete -F _root_command $filenames sudo fakeroot really
+
+# ant(1) completion
+#
+have ant && {
+_ant()
+{
+ local cur prev buildfile i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -buildfile|-f)
+ _filedir 'xml'
+ return 0
+ ;;
+ -logfile)
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-help -projecthelp -version -quiet \
+ -verbose -debug -emacs -logfile -logger \
+ -listener -buildfile -f -D -find' -- $cur ) )
+ else
+ # available targets completion
+ # find which buildfile to use
+ buildfile=build.xml
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -buildfile ]]; then
+ buildfile=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ [ ! -f $buildfile ] && return 0
+
+ # parse buildfile for targets
+ COMPREPLY=( $( awk -F'"' '/<target name="/ {print $2}' \
+ $buildfile | grep "^$cur" )
+ $( awk -F"'" "/<target name='/ "'{print $2}' \
+ $buildfile | grep "^$cur" )
+ $( awk -F'"' '/<target [^n]/ {if ($1 ~ /name=/) { print $2 } else if ($3 ~ /name=/) {print $4} else if ($5 ~ /name=/) {print $6}}' \
+ $buildfile | grep "^$cur" ) )
+ fi
+}
+have complete-ant-cmd.pl && \
+ complete -C complete-ant-cmd.pl -F _ant $filenames ant || \
+ complete -F _ant $filenames ant
+}
+
+have nslookup &&
+_nslookup()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=${COMP_WORDS[COMP_CWORD]#-}
+
+ COMPREPLY=( $( compgen -P '-' -W 'all class= debug d2 domain= \
+ srchlist= defname search port= querytype= \
+ type= recurse retry root timeout vc \
+ ignoretc' -- $cur ) )
+} &&
+complete -F _nslookup nslookup
+
+# mysqladmin(1) completion
+#
+have mysqladmin &&
+_mysqladmin()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -u)
+ COMPREPLY=( $( compgen -u -- $cur ) )
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+
+ COMPREPLY=( $( compgen -W '-# -f -? -C -h -p -P -i -r -E -s -S -t -u \
+ -v -V -w' -- $cur ) )
+
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( compgen -W 'create drop extended-status flush-hosts \
+ flush-logs flush-status flush-tables \
+ flush-threads flush-privileges kill \
+ password ping processlist reload refresh \
+ shutdown status variables version' \
+ -- $cur ) )
+} &&
+complete -F _mysqladmin mysqladmin
+
+# gzip(1) completion
+#
+have gzip &&
+_gzip()
+{
+ local cur prev xspec IFS=$'\t\n'
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -d -f \
+ -h -l -L -n -N -q -r -S -t -v -V \
+ -1 -2 -3 -4 -5 -6 -7 -8 -9 \
+ --stdout --decompress --force --help --list \
+ --license --no-name --name --quiet --recursive \
+ --suffix --test --verbose --version --fast \
+ --best' -- $cur ) )
+ return 0
+ fi
+
+ xspec="*.?(t)gz"
+ if [[ "$prev" == --* ]]; then
+ [[ "$prev" == --decompress || \
+ "$prev" == --list || \
+ "$prev" == --test ]] && xspec="!"$xspec
+ [[ "$prev" == --force ]] && xspec=
+ elif [[ "$prev" == -* ]]; then
+ [[ "$prev" == -*[dlt]* ]] && xspec="!"$xspec
+ [[ "$prev" == -*f* ]] && xspec=
+ elif [ "$prev" = '>' ]; then
+ xspec=
+ elif [ "$prev" = '<' ]; then
+ xspec=
+ fi
+
+ _expand || return 0
+
+ COMPREPLY=( $( compgen -f -X "$xspec" -- $cur ) \
+ $( compgen -d -- $cur ) )
+} &&
+complete -F _gzip $filenames gzip
+
+# bzip2(1) completion
+#
+have bzip2 &&
+_bzip2()
+{
+ local cur prev xspec IFS=$'\t\n'
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -d -f -h -k -L -q -s \
+ -t -v -V -z -1 -2 -3 -4 -5 -6 -7 -8 -9 \
+ --help --decompress --compress --keep --force \
+ --test --stdout --quiet --verbose --license \
+ --version --small --fast --best' -- $cur ) )
+ return 0
+ fi
+
+ xspec="*.bz2"
+ if [[ "$prev" == --* ]]; then
+ [[ "$prev" == --decompress || \
+ "$prev" == --list || \
+ "$prev" == --test ]] && xspec="!"$xspec
+ [[ "$prev" == --compress ]] && xspec=
+ elif [[ "$prev" == -* ]]; then
+ [[ "$prev" == -*[dt]* ]] && xspec="!"$xspec
+ [[ "$prev" == -*z* ]] && xspec=
+ fi
+
+ _expand || return 0
+
+ COMPREPLY=( $( compgen -f -X "$xspec" -- $cur ) \
+ $( compgen -d -- $cur ) )
+} &&
+complete -F _bzip2 $filenames bzip2
+
+# openssl(1) completion
+#
+have openssl && {
+_openssl_sections()
+{
+ local config
+
+ config=/etc/ssl/openssl.cnf
+ [ ! -f $config ] && config=/usr/share/ssl/openssl.cnf
+ for (( i=2; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -config ]]; then
+ config=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ [ ! -f $config ] && return 0
+
+ COMPREPLY=( $( awk '/\[.*\]/ {print $2} ' $config | grep "^$cur" ) )
+}
+
+_openssl()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'asn1parse ca ciphers crl crl2pkcs7 \
+ dgst dh dhparam dsa dsaparam enc errstr gendh gendsa \
+ genrsa nseq passwd pkcs12 pkcs7 pkcs8 rand req rsa \
+ rsautl s_client s_server s_time sess_id smime speed \
+ spkac verify version x509 md2 md4 md5 mdc2 rmd160 sha \
+ sha1 base64 bf bf-cbc bf-cfb bf-ecb bf-ofb cast \
+ cast-cbc cast5-cbc cast5-cfb cast5-ecb cast5-ofb des \
+ des-cbc des-cfb des-ecb des-ede des-ede-cbc \
+ des-ede-cfb des-ede-ofb des-ede3 des-ede3-cbc \
+ des-ede3-cfb des-ede3-ofb des-ofb des3 desx rc2 \
+ rc2-40-cbc rc2-64-cbc rc2-cbc rc2-cfb rc2-ecb rc2-ofb \
+ rc4 rc4-40' -- $cur ) )
+ else
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ case ${COMP_WORDS[1]} in
+ asn1parse)
+ case $prev in
+ -inform)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|oid))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -in -out -noout -offset \
+ -length -i -oid -strparse' -- $cur ) )
+ fi
+ ;;
+ ca)
+ case $prev in
+ -@(config|revoke|cert|in|out|spkac|ss_cert))
+ _filedir
+ return 0
+ ;;
+ -outdir)
+ _filedir -d
+ return 0
+ ;;
+ -@(name|crlexts|extensions))
+ _openssl_sections
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-verbose -config -name \
+ -gencrl -revoke -crldays -crlhours -crlexts \
+ -startdate -enddate -days -md -policy -keyfile \
+ -key -passin -cert -in -out -notext -outdir \
+ -infiles -spkac -ss_cert -preserveDN -batch \
+ -msie_hack -extensions' -- $cur ) )
+ fi
+ ;;
+ ciphers)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-v -ssl2 -ssl3 -tls1' -- $cur ) )
+ fi
+ ;;
+ crl)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|CAfile))
+ _filedir
+ return 0
+ ;;
+ -CAPath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -text -in -out -noout \
+ -hash -issuer -lastupdate -nextupdate -CAfile -CApath' -- $cur ) )
+ fi
+ ;;
+ crl2pkcs7)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -print_certs' -- $cur ) )
+ fi
+ ;;
+ dgst)
+ case $prev in
+ -@(out|sign|verify|prvrify|signature))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-md5 -md4 -md2 -sha1 -sha -mdc2 -ripemd160 -dss1 \
+ -c -d -hex -binary -out -sign -verify -prverify -signature' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ dsa)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -passin -out -passout -des -des3 -idea -text -noout \
+ -modulus -pubin -pubout' -- $cur ) )
+ fi
+ ;;
+ dsaparam)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -noout \
+ -text -C -rand -genkey' -- $cur ) )
+ fi
+ ;;
+ enc)
+ case $prev in
+ -@(in|out|kfile))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-ciphername -in -out -pass \
+ -e -d -a -A -k -kfile -S -K -iv -p -P -bufsize -debug' -- $cur ) )
+ fi
+ ;;
+ dhparam)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -dsaparam -noout \
+ -text -C -2 -5 -rand' -- $cur ) )
+ fi
+ ;;
+ gendsa)
+ case $prev in
+ -@(out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-out -des -des3 -idea -rand' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ genrsa)
+ case $prev in
+ -@(out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-out -passout -des -des3 -idea -f4 -3 -rand' -- $cur ) )
+ fi
+ ;;
+ pkcs7)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -print_certs -text -noout' -- $cur ) )
+ fi
+ ;;
+ rand)
+ case $prev in
+ -@(out|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-out -rand -base64' -- $cur ) )
+ fi
+ ;;
+ req)
+ case "$prev" in
+ -@(in|out|key)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+
+ -@(in|out|rand|key|keyout|config))
+ _filedir
+ return 0
+ ;;
+ -extensions)
+ _openssl_sections
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in \
+ -passin -out -passout -text -noout -verify \
+ -modulus -new -rand -newkey -newkey -nodes \
+ -key -keyform -keyout -md5 -sha1 -md2 -mdc2 \
+ -config -x509 -days -asn1-kludge -newhdr \
+ -extensions -reqexts section' -- $cur ) )
+ fi
+ ;;
+ rsa)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER NET PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -passin -out -passout \
+ -sgckey -des -des3 -idea -text -noout -modulus -check -pubin \
+ -pubout -engine' -- $cur ) )
+ fi
+ ;;
+ rsautl)
+ case $prev in
+ -@(in|out|inkey))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-in -out -inkey -pubin -certin -sign -verify \
+ -encrypt -decrypt -pkcs -ssl -raw -hexdump -asn1parse' -- $cur ) )
+ fi
+ ;;
+ s_client)
+ case $prev in
+ -connect)
+ _known_hosts
+ return 0
+ ;;
+ -@(cert|key|CAfile|rand))
+ _filedir
+ return 0
+ ;;
+ -CApath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-connect -verify -cert -key -CApath -CAfile \
+ -reconnect -pause -showcerts -debug -msg -nbio_test -state -nbio \
+ -crlf -ign_eof -quiet -ssl2 -ssl3 -tls1 -no_ssl2 -no_ssl3 -no_tls1 \
+ -bugs -cipher -starttls -engine -rand' -- $cur ) )
+ fi
+ ;;
+ s_server)
+ case $prev in
+ -@(cert|key|dcert|dkey|dhparam|CAfile|rand))
+ _filedir
+ return 0
+ ;;
+ -CApath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-accept -context -verify -Verify -cert -key \
+ -dcert -dkey -dhparam -nbio -nbio_test -crlf -debug -msg -state -CApath \
+ -CAfile -nocert -cipher -quiet -no_tmp_rsa -ssl2 -ssl3 -tls1 -no_ssl2 \
+ -no_ssl3 -no_tls1 -no_dhe -bugs -hack -www -WWW -HTTP -engine -id_prefix \
+ -rand' -- $cur ) )
+ fi
+ ;;
+ s_time)
+ case $prev in
+ -connect)
+ _known_hosts
+ return 0
+ ;;
+ -@(cert|key|CAfile))
+ _filedir
+ return 0
+ ;;
+ -CApath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-connect -www -cert -key -CApath -CAfile -reuse \
+ -new -verify -nbio -time -ssl2 -ssl3 -bugs -cipher' -- $cur ) )
+ fi
+ ;;
+
+ sess_id)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out))
+ _filedir
+ return 0
+ ;;
+ esac
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform -in -out -text -noout \
+ -context ID' -- $cur ) )
+ fi
+ ;;
+ smime)
+ case $prev in
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'SMIME DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -@(in|out|certfile|signer|recip|inkey|content|rand))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-encrypt -decrypt -sign -verify -pk7out -des -des3 \
+ -rc2-40 -rc2-64 -rc2-128 -aes128 -aes192 -aes256 -in -certfile -signer \
+ -recip -inform -passin -inkey -out -outform -content -to -from -subject \
+ -text -rand' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ speed)
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-engine' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W 'md2 mdc2 md5 hmac sha1 rmd160 idea-cbc \
+ rc2-cbc rc5-cbc bf-cbc des-cbc des-ede3 rc4 rsa512 rsa1024 rsa2048 \
+ rsa4096 dsa512 dsa1024 dsa2048 idea rc2 des rsa blowfish' -- $cur ) )
+ fi
+ ;;
+ verify)
+ case $prev in
+ -@(CAfile|untrusted))
+ _filedir
+ return 0
+ ;;
+ -CApath)
+ _filedir -d
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-CApath -CAfile -purpose -untrusted -help -issuer_checks \
+ -verbose -certificates' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ x509)
+ case "$prev" in
+ -@(in|out|CA|CAkey|CAserial|extfile))
+ _filedir
+ return 0
+ ;;
+ -@(in|out)form)
+ COMPREPLY=( $( compgen -W 'DER PEM NET' -- $cur ) )
+ return 0
+ ;;
+ -@(key|CA|CAkey)form)
+ COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) )
+ return 0
+ ;;
+ -extensions)
+ _openssl_sections
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-inform -outform \
+ -keyform -CAform -CAkeyform -in -out \
+ -serial -hash -subject -issuer -nameopt \
+ -email -startdate -enddate -purpose \
+ -dates -modulus -fingerprint -alias \
+ -noout -trustout -clrtrust -clrreject \
+ -addtrust -addreject -setalias -days \
+ -set_serial -signkey -x509toreq -req \
+ -CA -CAkey -CAcreateserial -CAserial \
+ -text -C -md2 -md5 -sha1 -mdc2 -clrext \
+ -extfile -extensions -engine' -- $cur ) )
+ fi
+ ;;
+ @(md5|md4|md2|sha1|sha|mdc2|ripemd160))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c -d' -- $cur ) )
+ else
+ _filedir
+ fi
+ ;;
+ esac
+ fi
+
+ return 0
+}
+complete -F _openssl $default openssl
+}
+
+# screen(1) completion
+#
+have screen &&
+_screen()
+{
+ local cur prev preprev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ "$COMP_CWORD" -ge 2 ] && preprev=${COMP_WORDS[COMP_CWORD-2]}
+
+ if [ "$preprev" = "-d" -o "$preprev" = "-D" -a "$prev" = "-r" -o \
+ "$prev" = "-R" ]; then
+ # list all
+ COMPREPLY=( $( command screen -ls | \
+ sed -ne 's|^['$'\t'']\+\('$cur'[0-9]\+\.[^'$'\t'']\+\).*$|\1|p' ) )
+ else
+ case "$prev" in
+ -[rR])
+ # list detached
+ COMPREPLY=( $( command screen -ls | \
+ sed -ne 's|^['$'\t'']\+\('$cur'[0-9]\+\.[^'$'\t'']\+\).*Detached.*$|\1|p' ) )
+ ;;
+ -[dDx])
+ # list attached
+ COMPREPLY=( $( command screen -ls | \
+ sed -ne 's|^['$'\t'']\+\('$cur'[0-9]\+\.[^'$'\t'']\+\).*Attached.*$|\1|p' ) )
+ ;;
+ -s)
+ # shells
+ COMPREPLY=( $( grep ^${cur:-[^#]} /etc/shells ) )
+ ;;
+ *)
+ ;;
+ esac
+ fi
+
+ return 0
+} &&
+complete -F _screen $default screen
+
+# lftp(1) bookmark completion
+#
+have lftp &&
+_lftp()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ] && [ -f ~/.lftp/bookmarks ]; then
+ COMPREPLY=( $( compgen -W '$( sed -ne "s/^\(.*\)'$'\t''.*$/\1/p" \
+ ~/.lftp/bookmarks )' -- $cur ) )
+ fi
+
+ return 0
+} &&
+complete -F _lftp $default lftp
+
+# ncftp(1) bookmark completion
+#
+have ncftp &&
+_ncftp()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ] && [ -f ~/.ncftp/bookmarks ]; then
+ COMPREPLY=( $( compgen -W '$( sed -ne "s/^\([^,]\{1,\}\),.*$/\1/p" \
+ ~/.ncftp/bookmarks )' -- $cur ) )
+ fi
+
+ return 0
+} &&
+complete -F _ncftp $default ncftp
+
+# gdb(1) completion
+#
+have gdb &&
+_gdb()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ elif [ $COMP_CWORD -eq 2 ]; then
+ prev=${prev##*/}
+ COMPREPLY=( $( compgen -fW "$( command ps axo comm,pid | \
+ awk '{if ($1 ~ /^'"$prev"'/) print $2}' ) )" \
+ -- "$cur" ) )
+ fi
+} &&
+complete -F _gdb $filenames gdb
+
+# Postgresql completion
+#
+have psql && {
+_pg_databases()
+{
+ return
+ COMPREPLY=( $( psql -l 2>/dev/null | \
+ sed -e '1,/^-/d' -e '/^(/,$d' | \
+ awk '{print $1}' | grep "^$cur" ) )
+}
+
+_pg_users()
+{
+ #COMPREPLY=( $( psql -qtc 'select usename from pg_user' template1 2>/dev/null | \
+ # grep "^ $cur" ) )
+ #[ ${#COMPREPLY[@]} -eq 0 ] &&
+ COMPREPLY=( $( compgen -u -- $cur ) )
+}
+
+# createdb(1) completion
+#
+_createdb()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(h|-host=))
+ _known_hosts
+ return 0
+ ;;
+ -@(U|-username=))
+ _pg_users
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-D -T -E -h -p -U -W -e -q \
+ --location= --template= --encoding= --host= --port= \
+ --username= --password --echo --quiet --help' -- $cur ))
+ else
+ _pg_databases
+ fi
+}
+complete -F _createdb $default createdb
+
+# dropdb(1) completion
+#
+_dropdb()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(h|-host=))
+ _known_hosts
+ return 0
+ ;;
+ -@(U|-username=))
+ _pg_users
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-h -p -U -W -e -q \
+ --host= --port= --username= --password \
+ --interactive --echo --quiet --help' -- $cur ) )
+ else
+ _pg_databases
+ fi
+}
+complete -F _dropdb $default dropdb
+
+# psql(1) completion
+#
+_psql()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -h|--host)
+ _known_hosts
+ return 0
+ ;;
+ -U|--username)
+ _pg_users
+ return 0
+ ;;
+ -d|--dbname)
+ _pg_databases
+ return 0
+ ;;
+ -@(o|f)|--output|--file)
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # return list of available options
+ COMPREPLY=( $( compgen -W '-a --echo-all -A --no-align \
+ -c --command -d --dbname -e --echo-queries \
+ -E --echo-hidden -f --file -F --filed-separator \
+ -h --host -H --html -l --list -n -o --output \
+ -p --port -P --pset -q -R --record-separator \
+ -s --single-step -S --single-line -t --tuples-only \
+ -T --table-attr -U --username -v --variable \
+ -V --version -W --password -x --expanded -X --nopsqlrc \
+ -? --help ' -- $cur ) )
+ else
+ # return list of available databases
+ _pg_databases
+ fi
+}
+complete -F _psql $default psql
+}
+
+_longopt()
+{
+ local cur opt
+
+ cur=`_get_cword`
+
+ if [[ "$cur" == --*=* ]]; then
+ opt=${cur%%=*}
+ # cut backslash that gets inserted before '=' sign
+ opt=${opt%\\*}
+ cur=${cur#*=}
+ _filedir
+ COMPREPLY=( $( compgen -P "$opt=" -W '${COMPREPLY[@]}' -- $cur))
+ return 0
+ fi
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( $1 --help 2>&1 | sed -e '/--/!d' \
+ -e 's/.*\(--[-A-Za-z0-9]\+=\?\).*/\1/' | \
+ command grep "^$cur" | sort -u ) )
+ elif [[ "$1" == @(mk|rm)dir ]]; then
+ _filedir -d
+ else
+ _filedir
+ fi
+}
+# makeinfo and texi2dvi are defined elsewhere.
+for i in a2ps autoconf automake bc gprof ld nm objcopy objdump readelf strip \
+ bison cpio diff patch enscript cp df dir du ln ls mkfifo mknod mv rm \
+ touch vdir awk gperf grep grub indent less m4 sed shar date \
+ tee who texindex cat csplit cut expand fmt fold head \
+ md5sum nl od paste pr ptx sha1sum sort split tac tail tr unexpand \
+ uniq wc ldd bash id irb mkdir rmdir; do
+ have $i && complete -F _longopt $filenames $i
+done
+
+# These commands use filenames, so '-o filenames' is not needed.
+for i in env netstat seq uname units wget; do
+ have $i && complete -F _longopt $default $i
+done
+unset i
+
+# gcc(1) completion
+#
+# The only unusual feature is that we don't parse "gcc --help -v" output
+# directly, because that would include the options of all the other backend
+# tools (linker, assembler, preprocessor, etc) without any indication that
+# you cannot feed such options to the gcc driver directly. (For example, the
+# linker takes a -z option, but you must type -Wl,-z for gcc.) Instead, we
+# ask the driver ("g++") for the name of the compiler ("cc1"), and parse the
+# --help output of the compiler.
+#
+have gcc &&
+_gcc()
+{
+ local cur cc backend
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _expand || return 0
+
+ case "$1" in
+ gcj)
+ backend=jc1
+ ;;
+ gpc)
+ backend=gpc1
+ ;;
+ *77)
+ backend=f771
+ ;;
+ *)
+ backend=cc1 # (near-)universal backend
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ cc=$( $1 -print-prog-name=$backend )
+ # sink stderr:
+ # for C/C++/ObjectiveC it's useless
+ # for FORTRAN/Java it's an error
+ COMPREPLY=( $( $cc --help 2>/dev/null | tr '\t' ' ' | \
+ sed -e '/^ *-/!d' -e 's/ *-\([^ ]*\).*/-\1/' | \
+ command grep "^$cur" | sort -u ) )
+ else
+ _filedir
+ fi
+} &&
+complete $filenames -F _gcc gcc g++ c++ g77 gcj gpc
+[ $UNAME = GNU -o $UNAME = Linux -o $UNAME = Cygwin ] && \
+[ -n "${have:-}" ] && complete $filenames -F _gcc cc
+
+# Linux cardctl(8) completion
+#
+have cardctl &&
+_cardctl()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'status config ident suspend \
+ resume reset eject insert scheme' \
+ -- $cur ) )
+ fi
+} &&
+complete -F _cardctl cardctl
+
+# This function is required by _dpkg() and _dpkg-reconfigure()
+#
+have dpkg && {
+_comp_dpkg_installed_packages()
+{
+ grep -A 1 "Package: $1" /var/lib/dpkg/status | \
+ grep -B 1 -E "ok installed|half-installed|unpacked| \
+ half-configured|config-files" | \
+ grep "Package: $1" | cut -d\ -f2
+}
+
+# Debian dpkg(8) completion
+#
+_dpkg()
+{
+ local cur prev i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ i=$COMP_CWORD
+
+ _expand || return 0
+
+ # find the last option flag
+ if [[ $cur != -* ]]; then
+ while [[ $prev != -* && $i != 1 ]]; do
+ i=$((i-1))
+ prev=${COMP_WORDS[i-1]}
+ done
+ fi
+
+ case "$prev" in
+ -@(c|i|A|I|f|e|x|X|-@(install|unpack|record-avail|contents|info|fsys-tarfile|field|control|extract)))
+ _filedir '?(u)deb'
+ return 0
+ ;;
+ -@(b|-build))
+ _filedir -d
+ return 0
+ ;;
+ -@(s|p|l|-@(status|print-avail|list)))
+ COMPREPLY=( $( apt-cache pkgnames $cur 2>/dev/null ) )
+ return 0
+ ;;
+ -@(S|-search))
+ _filedir
+ return 0
+ ;;
+ -@(r|L|P|-@(remove|purge|listfiles)))
+ COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) )
+ return 0
+ ;;
+ *)
+
+ COMPREPLY=( $( compgen -W '-i --install --unpack -A --record-avail \
+ --configure -r --remove -P --purge --get-selections \
+ --set-selections --update-avail --merge-avail \
+ --clear-avail --command-fd --forget-old-unavail -s \
+ --status -p --print-avail -L --listfiles -l --list \
+ -S --search -C --audit --print-architecture \
+ --print-gnu-build-architecture \
+ --print-installation-architecture \
+ --compare-versions --help --version --force-help \
+ --force-all --force-auto-select --force-downgrade \
+ --force-configure-any --force-hold --force-bad-path \
+ --force-not-root --force-overwrite \
+ --force-overwrite-diverted --force-bad-verify \
+ --force-depends-version --force-depends \
+ --force-confnew --force-confold --force-confdef \
+ --force-confmiss --force-conflicts --force-architecture\
+ --force-overwrite-dir --force-remove-reinstreq \
+ --force-remove-essential -Dh \
+ --debug=help --licence --admindir= --root= --instdir= \
+ -O --selected-only -E --skip-same-version \
+ -G --refuse-downgrade -B --auto-deconfigure \
+ --no-debsig --no-act -D --debug= --status-fd \
+ -b --build -I --info -f --field -c --contents \
+ -x --extract -X --vextract --fsys-tarfile -e --control \
+ --ignore-depends= --abort-after' -- $cur ) )
+ ;;
+ esac
+
+
+}
+complete -F _dpkg $filenames dpkg dpkg-deb
+}
+
+# Debian GNU dpkg-reconfigure(8) completion
+#
+have dpkg-reconfigure &&
+_dpkg_reconfigure()
+{
+ local cur prev opt
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+
+ case "$prev" in
+ -@(f|-frontend))
+ opt=( $( echo /usr/share/perl5/Debconf/FrontEnd/* ) )
+ opt=( ${opt[@]##*/} )
+ opt=( ${opt[@]%.pm} )
+ COMPREPLY=( $( compgen -W '${opt[@]}' -- $cur ) )
+ return 0
+ ;;
+ -@(p|-priority))
+ COMPREPLY=( $( compgen -W 'low medium high critical' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-f --frontend -p --priority -a --all \
+ -u --unseen-only -h --help -s --showold \
+ --force --terse' -- $cur ) )
+ else
+ COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) )
+ fi
+} &&
+complete -F _dpkg_reconfigure $default dpkg-reconfigure
+
+# Debian dpkg-source completion
+#
+have dpkg-source &&
+_dpkg_source()
+{
+ local cur prev options work i action packopts unpackopts
+
+ packopts="-c -l -F -V -T -D -U -W -E -sa -i -I -sk -sp -su -sr -ss -sn -sA -sK -sP -sU -sR"
+ unpackopts="-sp -sn -su"
+ options=`echo "-x -b $packopts $unpackopts" | xargs echo | sort -u | xargs echo`
+
+ COMPREPLY=()
+ if [ "$1" != "dpkg-source" ]; then
+ exit 1
+ fi
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ action="options"
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[$i]} == "-x" ]]; then
+ action=unpack
+ elif [[ ${COMP_WORDS[$i]} == "-b" ]]; then
+ action=pack
+ elif [[ ${COMP_WORDS[$i]} == "-h" ]]; then
+ action=help
+ fi
+ done
+ # if currently seeing a complete option, return just itself.
+ for i in $options; do
+ if [ "$cur" = "$i" ]; then
+ COMPREPLY=( "$cur" )
+ return 0
+ fi
+ done
+ case "$action" in
+ "unpack")
+ if [ "$cur" = "-" -o "$cur" = "-s" ]; then
+ COMPREPLY=( $unpackots )
+ return 0
+ fi
+ case "$prev" in
+ "-x")
+ COMPREPLY=( $( compgen -d -- "$cur" ) \
+ $( compgen -f -X '!*.dsc' -- "$cur" ) )
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $unpackopts $(compgen -d -f -- "$cur" ) )
+ return 0
+ ;;
+ esac
+ return 0
+ ;;
+ "pack")
+ if [ "$cur" = "-" ]; then
+ COMPREPLY=( $packopts )
+ return 0
+ fi
+ if [ "$cur" = "-s" ]; then
+ COMPREPLY=( "-sa" "-sk" "-sp" "-su" "-sr" "-ss" "-sn" \
+ "-sA" "-sK" "-sP" "-sU" "-sR" )
+ return 0
+ fi
+ case "$prev" in
+ "-b")
+ COMPREPLY=( $( compgen -d -- "$cur" ) )
+ return 0
+ ;;
+ "-c"|"-l"|"-T"|"-i"|"-I")
+ # -c: get controlfile
+ # -l: get per-version info from this file
+ # -T: read variables here, not debian/substvars
+ # -i: <regexp> filter out files to ignore diffs of.
+ # -I: filter out files when building tarballs.
+ # return directory names and file names
+ COMPREPLY=( $( compgen -d -f ) )
+ return 0
+ ;;
+ "-F")
+ # -F: force change log format
+ COMPREPLY=( $( ( cd /usr/lib/dpkg/parsechangelog; compgen -f "$cur" ) ) )
+ return 0
+ ;;
+ "-V"|"-D")
+ # -V: set a substitution variable
+ # we don't know anything about possible variables or values
+ # so we don't try to suggest any completion.
+ COMPREPLY=()
+ return 0
+ ;;
+ "-D")
+ # -D: override or add a .dsc field and value
+ # if $cur doesn't contain a = yet, suggest variable names
+ if echo -- "$cur" | grep -q "="; then
+ # $cur contains a "="
+ COMPREPLY=()
+ return 0
+ else
+ COMPREPLY=( Format Source Version Binary Maintainer Uploader Architecture Standards-Version Build-Depends Files )
+ return 0
+ fi
+ ;;
+ "-U")
+ # -U: remove a field
+ # Suggest possible fieldnames
+ COMPREPLY=( Format Source Version Binary Maintainer Uploader Architecture Standards-Version Build-Depends Files )
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $packopts )
+ return 0
+ ;;
+ esac
+ return 0
+ ;;
+ *)
+ # if seeing a partial option, return possible completions.
+ if [ "$cur" = "-s" ]; then
+ COMPREPLY=( "-sa" "-sk" "-sp" "-su" "-sr" "-ss" "-sn" \
+ "-sA" "-sK" "-sP" "-sU" "-sR" )
+ return 0
+ fi
+ # else return all possible options.
+ COMPREPLY=( $options )
+ return 0
+ ;;
+ esac
+} &&
+complete -F _dpkg_source dpkg-source
+
+# Debian Linux dselect(8) completion.
+#
+have dselect &&
+_dselect()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --admindir)
+ _filedir -d
+ return 0
+ ;;
+
+ -@(D|debug))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--admindir --help --version --licence \
+ --license --expert --debug' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W 'access update select install config \
+ remove quit' -- $cur ) )
+ fi
+
+
+ return 0
+} &&
+complete -F _dselect $filenames dselect
+
+# Java completion
+#
+
+# available path elements completion
+have java && {
+_java_path()
+{
+ cur=${cur##*:}
+ _filedir '@(jar|zip)'
+}
+
+# exact classpath determination
+_java_find_classpath()
+{
+ local i
+
+ # search first in current options
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -@(cp|classpath) ]]; then
+ classpath=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+
+ # default to environment
+ [ -z "$classpath" ] && classpath=$CLASSPATH
+
+ # default to current directory
+ [ -z "$classpath" ] && classpath=.
+}
+
+# exact sourcepath determination
+_java_find_sourcepath()
+{
+ local i
+
+ # search first in current options
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == -sourcepath ]]; then
+ sourcepath=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+
+ # default to classpath
+ [ -z "$sourcepath" ] && _java_find_classpath
+ sourcepath=$classpath
+}
+
+# available classes completion
+_java_classes()
+{
+ local classpath i
+
+ # find which classpath to use
+ _java_find_classpath
+
+ # convert package syntax to path syntax
+ cur=${cur//.//}
+ # parse each classpath element for classes
+ for i in ${classpath//:/ }; do
+ if [ -r $i ] && [[ "$i" == *.@(jar|zip) ]]; then
+ if type zipinfo &> /dev/null; then
+ COMPREPLY=( "${COMPREPLY[@]}" $( zipinfo -1 \
+ "$i" | grep "^$cur" | grep '\.class$' | \
+ grep -v "\\$" ) )
+ else
+ COMPREPLY=( "${COMPREPLY[@]}" $( jar tf "$i" \
+ "$cur" | grep "\.class$" | grep -v "\\$" ) )
+ fi
+
+ elif [ -d $i ]; then
+ i=${i%/}
+ COMPREPLY=( "${COMPREPLY[@]}" $( find "$i" -type f \
+ -path "$i/$cur*.class" 2>/dev/null | \
+ grep -v "\\$" | sed -e "s|^$i/||" ) )
+ fi
+ done
+
+ # remove class extension
+ COMPREPLY=( ${COMPREPLY[@]%.class} )
+ # convert path syntax to package syntax
+ COMPREPLY=( ${COMPREPLY[@]//\//.} )
+}
+
+# available packages completion
+_java_packages()
+{
+ local sourcepath i
+
+ # find wich sourcepath to use
+ _java_find_sourcepath
+
+ # convert package syntax to path syntax
+ cur=${cur//.//}
+ # parse each sourcepath element for packages
+ for i in ${sourcepath//:/ }; do
+ if [ -d $i ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" $( command ls -F -d \
+ $i/$cur* 2>/dev/null | sed -e 's|^'$i'/||' ) )
+ fi
+ done
+ # keep only packages
+ COMPREPLY=( $( echo "${COMPREPLY[@]}" | tr " " "\n" | grep "/$" ) )
+ # remove packages extension
+ COMPREPLY=( ${COMPREPLY[@]%/} )
+ # convert path syntax to package syntax
+ cur=${COMPREPLY[@]//\//.}
+}
+
+# java completion
+#
+_java()
+{
+ local cur prev i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ for ((i=1; i < $COMP_CWORD; i++)); do
+ case ${COMP_WORDS[$i]} in
+ -cp|-classpath)
+ ((i++)) # skip the classpath string.
+ ;;
+ -*)
+ # this is an option, not a class/jarfile name.
+ ;;
+ *)
+ # once we've seen a class, just do filename completion
+ _filedir
+ return 0
+ ;;
+ esac
+ done
+
+ case $prev in
+ -@(cp|classpath))
+ _java_path
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-client -hotspot -server -classic \
+ -cp -classpath -D -verbose -verbose:class \
+ -verbose:gc -version:jni -version \
+ -showversion -? -help -X -jar \
+ -ea -enableassertions -da -disableassertions \
+ -esa -enablesystemassertions \
+ -dsa -disablesystemassertions ' -- $cur ) )
+ else
+ if [[ "$prev" == -jar ]]; then
+ # jar file completion
+ _filedir jar
+ else
+ # classes completion
+ _java_classes
+ fi
+ fi
+}
+complete -F _java $filenames java
+}
+
+# javadoc completion
+#
+have javadoc &&
+_javadoc()
+{
+ COMPREPLY=()
+ local cur prev
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -@(overview|helpfile|stylesheetfile))
+ _filedir
+ return 0
+ ;;
+ -d)
+ _filedir -d
+ return 0
+ ;;
+ -@(classpath|bootclasspath|docletpath|sourcepath|extdirs))
+ _java_path
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-overview -public -protected \
+ -package -private -help -doclet -docletpath \
+ -sourcepath -classpath -exclude -subpackages \
+ -breakiterator -bootclasspath -source -extdirs \
+ -verbose -locale -encoding -J -d -use -version \
+ -author -docfilessubdirs -splitindex \
+ -windowtitle -doctitle -header -footer -bottom \
+ -link -linkoffline -excludedocfilessubdir \
+ -group -nocomment -nodeprecated -noqualifier \
+ -nosince -nodeprecatedlist -notree -noindex \
+ -nohelp -nonavbar -quiet -serialwarn -tag \
+ -taglet -tagletpath -charset -helpfile \
+ -linksource -stylesheetfile -docencoding' -- \
+ $cur ) )
+ else
+ # source files completion
+ _filedir java
+ # packages completion
+ _java_packages
+ fi
+} &&
+complete -F _javadoc $filenames javadoc
+
+# javac completion
+#
+have javac &&
+_javac()
+{
+ COMPREPLY=()
+ local cur prev
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -d)
+ _filedir -d
+ return 0
+ ;;
+ -@(classpath|bootclasspath|sourcepath|extdirs))
+ _java_path
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-g -g:none -g:lines -g:vars\
+ -g:source -O -nowarn -verbose -deprecation -classpath\
+ -sourcepath -bootclasspath -extdirs -d -encoding -source\
+ -target -help' -- $cur ) )
+ else
+ # source files completion
+ _filedir java
+ fi
+} &&
+complete -F _javac $filenames javac
+
+# PINE address-book completion
+#
+have pine &&
+_pineaddr()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -W '$( awk "{print \$1}" ~/.addressbook 2>/dev/null)' \
+ -- $cur ) )
+} &&
+complete -F _pineaddr $default pine
+
+# mutt completion
+#
+# Mutt doesn't have an "addressbook" like Pine, but it has aliases and
+# a "query" function to retrieve addresses, so that's what we use here.
+have mutt || have muttng && {
+_muttaddr()
+{
+ _muttaliases
+ _muttquery
+ return 0
+}
+
+_muttconffiles()
+{
+ local file sofar
+ local -a newconffiles
+
+ sofar=" $1 "
+ shift
+ while [[ "$1" ]]; do
+ newconffiles=( $(sed -rn 's|^source[[:space:]]+([^[:space:]]+).*$|\1|p' $(eval echo $1) ) )
+ for file in "${newconffiles[@]}"; do
+ [[ ! "$file" ]] || [[ "${sofar/ ${file} / }" != "$sofar" ]] &&
+ continue
+ sofar="$sofar $file"
+ sofar=" $(eval _muttconffiles \"$sofar\" $file) "
+ done
+ shift
+ done
+ echo $sofar
+}
+
+_muttaliases()
+{
+ local cur muttrc
+ local -a conffiles aliases
+ cur=`_get_cword`
+
+ [ -f ~/.${muttcmd}/${muttcmd}rc ] && muttrc="~/.${muttcmd}/${muttcmd}rc"
+ [ -f ~/.${muttcmd}rc ] && muttrc="~/.${muttcmd}rc"
+ [ -z "$muttrc" ] && return 0
+
+ conffiles=( $(eval _muttconffiles $muttrc $muttrc) )
+ aliases=( $( sed -rn 's|^alias[[:space:]]+([^[:space:]]+).*$|\1|p' \
+ $(eval echo "${conffiles[@]}") ) )
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "${aliases[*]}" -- $cur ) )
+
+ return 0
+}
+
+_muttquery()
+{
+ local cur querycmd
+ local -a queryresults
+ cur=`_get_cword`
+
+ querycmd="$( $muttcmd -Q query_command | sed -r 's|^query_command=\"(.*)\"$|\1|; s|%s|'$cur'|' )"
+ if [ -z "$cur" -o -z "$querycmd" ]; then
+ queryresults=()
+ else
+ queryresults=( $( $querycmd | \
+ sed -nr '2,$s|^([^[:space:]]+).*|\1|p' ) )
+ fi
+
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "${queryresults[*]}" \
+ -- $cur ) )
+
+ return 0
+}
+
+_muttfiledir()
+{
+ local cur folder spoolfile
+ cur=`_get_cword`
+
+ # This is currently not working so well. Perhaps this function should
+ # just call _filedir() for the moment.
+ if [[ $cur == [=+]* ]]; then
+ folder="$( $muttcmd -Q folder | sed -r 's|^folder=\"(.*)\"$|\1|' )"
+ : folder:=~/Mail
+
+ # Match any file in $folder beginning with $cur
+ # (minus the leading '=' sign).
+ COMPREPLY=( $( compgen -f -- "$folder/${cur:1}" ) )
+ COMPREPLY=( ${COMPREPLY[@]#$folder/} )
+ return 0
+ elif [ "$cur" == !* ]; then
+ spoolfile="$( $muttcmd -Q spoolfile | sed -r 's|^spoolfile=\"(.*)\"$|\1|' )"
+ [ ! -z "$spoolfile" ] && eval cur="${cur/^!/$spoolfile}";
+ fi
+ _filedir
+
+ return 0
+}
+
+_mutt()
+{
+ local cur prev
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ COMPREPLY=()
+
+ [ ${COMP_WORDS[0]} == muttng ] && muttcmd="muttng" || muttcmd="mutt"
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W '-A -a -b -c -e -f -F -H -i -m -n \
+ -p -Q -R -s -v -x -y -z -Z -h' \
+ -- $cur ) )
+ return 0
+ ;;
+ *)
+ case "$prev" in
+ -@(a|f|F|H|i))
+ _muttfiledir
+ return 0
+ ;;
+ -A)
+ _muttaliases
+ return 0
+ ;;
+ -@(e|m|Q|s|h|p|R|v|y|z|Z))
+ return 0
+ ;;
+ *)
+ _muttaddr
+ return 0
+ ;;
+ esac
+ ;;
+ esac
+
+}
+complete -F _mutt $default $filenames mutt muttng
+}
+
+_configure_func()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ # if $COMP_CONFIGURE_HINTS is not null, then completions of the form
+ # --option=SETTING will include 'SETTING' as a contextual hint
+ [[ "$cur" != -* ]] && return 0
+
+ if [ -n "$COMP_CONFIGURE_HINTS" ]; then
+ COMPREPLY=( $( $1 --help | awk '/^ --[A-Za-z]/ { print $1; if ($2 ~ /--[A-Za-z]/) print $2 }' | sed -e 's/[[,].*//g' | grep ^$cur ) )
+
+ else
+ COMPREPLY=( $( $1 --help | awk '/^ --[A-Za-z]/ { print $1; if ($2 ~ /--[A-Za-z]/) print $2 }' | sed -e 's/[[,=].*//g' | grep ^$cur ) )
+ fi
+}
+complete -F _configure_func $default configure
+
+# Debian reportbug(1) completion
+#
+have reportbug &&
+_reportbug()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -f|--filename|-i|--include|--mta|-o|--output)
+ _filedir
+ return 0
+ ;;
+ -B|--bts)
+ COMPREPLY=( $( compgen -W "debian guug kde mandrake help" -- \
+ $cur ))
+ return 0
+ ;;
+ -e|--editor|--mua)
+ COMP_WORDS=(COMP_WORDS[0] $cur)
+ COMP_CWORD=1
+ _command
+ return 0
+ ;;
+ --mode)
+ COMPREPLY=( $( compgen -W "novice standard expert" -- $cur ) )
+ return 0
+ ;;
+ -S|--severity)
+ COMPREPLY=( $( compgen -W "grave serious important normal \
+ minor wishlist" -- $cur ) )
+ return 0
+ ;;
+ -u|--ui|--interface)
+ COMPREPLY=( $( compgen -W "newt text gnome" -- $cur ) )
+ return 0
+ ;;
+ -t|--type)
+ COMPREPLY=( $( compgen -W "gnats debbugs" -- $cur ) )
+ return 0
+ ;;
+ -T|--tags)
+ COMPREPLY=( $( compgen -W "none patch security upstream sid \
+ woody potato sarge fixed" -- $cur ))
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+
+ COMPREPLY=($( compgen -W '-h --help -v --version -a --af -b \
+ --no-query-bts --query-bts -B --bts -c --configure \
+ --no-config-files --check-available -d --debug \
+ --no-check-available -e --editor --email -f \
+ --filename -g --gnupg -H --header -i --include -j \
+ --justification -l --ldap --no-ldap -L --list-cc -m \
+ --maintonly --mode --mua --mta --mutt -n --mh --nmh \
+ -o --output -p --print -P --pgp --proxy --http_proxy\
+ -q --quiet -Q --query-only --realname --report-quiet \
+ --reply-to --replyto -s --subject -S --severity \
+ --smtphost -t --type -T --tags --template -V -x \
+ --no-cc --package-version -z --no-compress \
+ --ui --interface -u \
+ wnpp boot-floppies kernel-image' -- $cur ) \
+ $( apt-cache pkgnames -- $cur 2> /dev/null ) )
+ _filedir
+ return 0
+} &&
+complete -F _reportbug $filenames reportbug
+
+# Debian querybts(1) completion
+#
+have querybts &&
+_querybts()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -B|--bts)
+ COMPREPLY=( $( compgen -W "debian guug kde mandrake help" -- \
+ $cur ))
+ return 0
+ ;;
+ -u|--ui|--interface)
+ COMPREPLY=($( compgen -W "newt text gnome" -- $cur ))
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+
+ COMPREPLY=($( compgen -W '-h --help -v --version -A --archive \
+ -B --bts -l --ldap --no-ldap --proxy= --http_proxy= \
+ -s --source -w --web -u --ui --interface \
+ wnpp boot-floppies' -- $cur ) \
+ $( apt-cache pkgnames -- $cur 2> /dev/null ) )
+} &&
+complete -F _querybts $filenames querybts
+
+# update-alternatives completion
+#
+have update-alternatives && {
+installed_alternatives()
+{
+ local admindir
+ # find the admin dir
+ for i in alternatives dpkg/alternatives rpm/alternatives; do
+ [ -d /var/lib/$i ] && admindir=/var/lib/$i && break
+ done
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == --admindir ]]; then
+ admindir=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ COMPREPLY=( $( command ls $admindir | grep "^$cur" ) )
+}
+
+_update_alternatives()
+{
+ local cur prev mode args i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --@(altdir|admindir))
+ _filedir -d
+ return 0
+ ;;
+ --@(help|version))
+ return 0
+ ;;
+ esac
+
+ # find wich mode to use and how many real args used so far
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == --@(install|remove|auto|display|config|remove-all) ]]; then
+ mode=${COMP_WORDS[i]}
+ args=$(($COMP_CWORD - i))
+ break
+ fi
+ done
+
+ case $mode in
+ --install)
+ case $args in
+ 1)
+ _filedir
+ ;;
+ 2)
+ installed_alternatives
+ ;;
+ 3)
+ _filedir
+ ;;
+ esac
+ ;;
+ --remove)
+ case $args in
+ 1)
+ installed_alternatives
+ ;;
+ 2)
+ _filedir
+ ;;
+ esac
+ ;;
+ --auto)
+ installed_alternatives
+ ;;
+ --remove-all)
+ installed_alternatives
+ ;;
+ --display)
+ installed_alternatives
+ ;;
+ --config)
+ installed_alternatives
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W '--verbose --quiet --help --version \
+ --altdir --admindir' -- $cur ) \
+ $( compgen -W '--install --remove --auto --display \
+ --config' -- $cur ) )
+ esac
+}
+complete -F _update_alternatives update-alternatives
+}
+
+# Python completion
+#
+have python &&
+_python()
+{
+ local prev cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]##*/}
+
+ case "$prev" in
+ -Q)
+ COMPREPLY=( $( compgen -W "old new warn warnall" -- $cur ) )
+ return 0
+ ;;
+ -W)
+ COMPREPLY=( $( compgen -W "ignore default all module once error" -- $cur ) )
+ return 0
+ ;;
+ -c)
+ _filedir '@(py|pyc|pyo)'
+ return 0
+ ;;
+ !(python|-?))
+ [[ ${COMP_WORDS[COMP_CWORD-2]} != -@(Q|W) ]] && _filedir
+ ;;
+ esac
+
+
+ # if '-c' is already given, complete all kind of files.
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == -c ]]; then
+ _filedir
+ fi
+ done
+
+
+ if [[ "$cur" != -* ]]; then
+ _filedir '@(py|pyc|pyo)'
+ else
+ COMPREPLY=( $( compgen -W "- -d -E -h -i -O -Q -S -t -u \
+ -U -v -V -W -x -c" -- $cur ) )
+ fi
+
+
+
+ return 0
+} &&
+complete -F _python $filenames python
+
+# Perl completion
+#
+have perl &&
+{
+_perlmodules()
+{
+ COMPREPLY=( $( compgen -P "$prefix" -W "$( perl -e 'sub mods { my ($base,$dir)=@_; return if $base !~ /^\Q$ENV{cur}/; chdir($dir) or return; for (glob(q[*.pm])) {s/\.pm$//; print qq[$base$_\n]}; mods(/^(?:[.\d]+|$Config{archname}-$Config{osname}|auto)$/ ? undef : qq[${base}${_}\\\\:\\\\:],qq[$dir/$_]) for grep {-d} glob(q[*]); } mods(undef,$_) for @INC;' )" -- $cur ) )
+}
+
+_perl()
+{
+ local cur prev prefix temp
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ prefix=""
+
+ # completing an option (may or may not be separated by a space)
+ if [[ "$cur" == -?* ]]; then
+ temp=$cur
+ prev=${temp:0:2}
+ cur=${temp:2}
+ prefix=$prev
+ fi
+
+ # only handle module completion for now
+ case "$prev" in
+ -I|-x)
+ COMPREPLY=( $( compgen -d -P "$prev" -- "$cur" ) )
+ return 0
+ ;;
+ -m|-M)
+ _perlmodules
+ return 0
+ ;;
+ esac
+
+ # handle case where first parameter is not a dash option
+ if [[ "${COMP_WORDS[COMP_CWORD]}" != -* ]]; then
+ _filedir
+ return 0
+ fi
+
+ # complete using basic options
+ COMPREPLY=( $( compgen -W '-C -s -T -u -U -W -X -h -v -V -c -w -d -D -p \
+ -n -a -F -l -0 -I -m -M -P -S -x -i -e ' -- $cur ) )
+ return 0
+}
+complete -F _perl $filenames perl
+
+_perldoc()
+{
+ local cur prev prefix temp
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ prefix=""
+
+ # completing an option (may or may not be separated by a space)
+ if [[ "$cur" == -?* ]]; then
+ temp=$cur
+ prev=${temp:0:2}
+ cur=${temp:2}
+ prefix=$prev
+ fi
+
+ # complete builtin perl functions
+ case $prev in
+ -f)
+ COMPREPLY=( $( compgen -W 'chomp chop chr crypt hex index lc \
+ lcfirst length oct ord pack q qq reverse rindex sprintf \
+ substr tr uc ucfirst y m pos quotemeta s split study qr abs \
+ atan2 cos exp hex int log oct rand sin sqrt srand pop push \
+ shift splice unshift grep join map qw reverse sort unpack \
+ delete each exists keys values binmode close closedir \
+ dbmclose dbmopen die eof fileno flock format getc print \
+ printf read readdir rewinddir seek seekdir select syscall \
+ sysread sysseek syswrite tell telldir truncate warn write \
+ pack read syscall sysread syswrite unpack vec -X chdir chmod \
+ chown chroot fcntl glob ioctl link lstat mkdir open opendir \
+ readlink rename rmdir stat symlink umask unlink utime caller \
+ continue do dump eval exit goto last next redo return \
+ sub wantarray caller import local my our package use defined \
+ formline reset scalar undef \
+ alarm exec fork getpgrp getppid getpriority kill pipe qx \
+ setpgrp setpriority sleep system times wait waitpid \
+ import no package require use bless dbmclose dbmopen package \
+ ref tie tied untie use accept bind connect getpeername \
+ getsockname getsockopt listen recv send setsockopt shutdown \
+ socket socketpair msgctl msgget msgrcv msgsnd semctl semget \
+ semop shmctl shmget shmread shmwrite endgrent endhostent \
+ endnetent endpwent getgrent getgrgid getgrnam getlogin \
+ getpwent getpwnam getpwuid setgrent setpwent endprotoent \
+ endservent gethostbyaddr gethostbyname gethostent \
+ getnetbyaddr getnetbyname getnetent getprotobyname \
+ getprotobynumber getprotoent getservbyname getservbyport \
+ getservent sethostent setnetent setprotoent setservent \
+ gmtime localtime time times' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ case $cur in
+ -*)
+ COMPREPLY=( $( compgen -W '-h -v -t -u -m -l -F -X -f -q' -- $cur ))
+ return 0
+ ;;
+ */*)
+ return 0
+ ;;
+ *)
+ _perlmodules
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W '$( PAGER=cat man perl 2>/dev/null | sed -ne "/perl.*Perl overview/,/perlwin32/s/^[^a-z0-9]*\([a-z0-9]*\).*$/\1/p")' -- $cur ) )
+
+ return 0
+ ;;
+ esac
+}
+complete -F _perldoc $default perldoc
+}
+
+# rcs(1) completion
+#
+have rcs &&
+_rcs()
+{
+ local cur prev file dir i
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ file=${cur##*/}
+ dir=${cur%/*}
+
+ # deal with relative directory
+ [ "$file" = "$dir" ] && dir=.
+
+ COMPREPLY=( $( compgen -f "$dir/RCS/$file" ) )
+
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ file=${COMPREPLY[$i]##*/}
+ dir=${COMPREPLY[$i]%RCS/*}
+ COMPREPLY[$i]=$dir$file
+ done
+
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -G "$dir/$file*,v" ) )
+
+ for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do
+ COMPREPLY[$i]=${COMPREPLY[$i]%,v}
+ done
+
+ # default to files if nothing returned and we're checking in.
+ # otherwise, default to directories
+ [ ${#COMPREPLY[@]} -eq 0 -a $1 = ci ] && _filedir || _filedir -d
+} &&
+complete -F _rcs $filenames ci co rlog rcs rcsdiff
+
+# lilo(8) completion
+#
+have lilo && {
+_lilo_labels()
+{
+ COMPREPLY=( $( awk -F'=' '/label/ {print $2}' \
+ /etc/lilo.conf | sed -e 's/"//g' | grep "^$cur" ) )
+}
+
+_lilo()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -@(C|i|m|s|S))
+ _filedir
+ return 0
+ ;;
+ -r)
+ _filedir -d
+ return 0
+ ;;
+ -@(I|D|R))
+ # label completion
+ _lilo_labels
+ return 0
+ ;;
+ -@(A|b|M|u|U))
+ # device completion
+ cur=${cur:=/dev/}
+ _filedir
+ return 0
+ ;;
+ -T)
+ # topic completion
+ COMPREPLY=( $( compgen -W 'help ChRul EBDA geom geom= \
+ table= video' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ # relevant options completion
+ COMPREPLY=( $( compgen -W '-A -b -c -C -d -f -g -i -I -l -L -m \
+ -M -p -P -q -r -R -s -S -t -T -u -U -v -V -w -x -z' -- \
+ $cur ) )
+ fi
+}
+complete -F _lilo lilo
+}
+
+# links completion
+#
+have links &&
+_links()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ case "$cur" in
+ --*)
+ COMPREPLY=( $( compgen -W '--help' -- $cur ) )
+ ;;
+ -*)
+ COMPREPLY=( $( compgen -W '-async-dns -max-connections \
+ -max-connections-to-host -retries \
+ -receive-timeout -unrestartable-receive-timeout\
+ -format-cache-size -memory-cache-size \
+ -http-proxy -ftp-proxy -download-dir \
+ -assume-codepage -anonymous -dump -no-connect \
+ -source -version -help' -- $cur ) )
+ ;;
+ *)
+ if [ -r ~/.links/links.his ]; then
+ COMPREPLY=( $( compgen -W '$( < ~/.links/links.his )' \
+ -- $cur ) )
+ fi
+ _filedir '@(htm|html)'
+ return 0
+ ;;
+ esac
+
+ return 0
+} &&
+complete -F _links $filenames links
+
+[ $UNAME = FreeBSD ] && {
+# FreeBSD package management tool completion
+#
+_pkg_delete()
+{
+ local cur pkgdir prev
+
+ pkgdir=${PKG_DBDIR:-/var/db/pkg}/
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ "$prev" = "-o" -o "$prev" = "-p" -o "$prev" = "-W" ] && return 0
+
+ COMPREPLY=( $( compgen -d $pkgdir$cur ) )
+ COMPREPLY=( ${COMPREPLY[@]#$pkgdir} )
+
+ return 0
+}
+complete -F _pkg_delete $dirnames pkg_delete pkg_info
+have pkg_deinstall && complete -F _pkg_delete $dirnames pkg_deinstall
+
+# FreeBSD kernel module commands
+#
+_kldload()
+{
+ local cur moddir
+
+ moddir=/modules/
+ [ -d $moddir ] || moddir=/boot/kernel/
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -f $moddir$cur ) )
+ COMPREPLY=( ${COMPREPLY[@]#$moddir} )
+ COMPREPLY=( ${COMPREPLY[@]%.ko} )
+
+ return 0
+}
+complete -F _kldload $filenames kldload
+
+_kldunload()
+{
+ local cur
+ cur=`_get_cword`
+ COMPREPLY=( $(kldstat | sed -ne "s/^.*[ \t]\+\($cur[a-z_]\+\).ko$/\1/p") )
+}
+complete -F _kldunload $filenames kldunload
+}
+
+# FreeBSD portupgrade completion
+#
+have portupgrade &&
+_portupgrade()
+{
+ local cur pkgdir prev
+
+ pkgdir=${PKG_DBDIR:-/var/db/pkg}/
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ "$prev" = "-l" -o "$prev" = "-L" -o "$prev" = "-o" ] && return 0
+
+ COMPREPLY=( $( compgen -d $pkgdir$cur ) )
+ COMPREPLY=( ${COMPREPLY[@]#$pkgdir} )
+ COMPREPLY=( ${COMPREPLY[@]%-*} )
+
+ return 0
+} &&
+complete -F _portupgrade $dirnames portupgrade
+
+# FreeBSD portinstall completion
+#
+have portinstall &&
+_portinstall()
+{
+ local cur portsdir prev indexfile
+ local -a COMPREPLY2
+
+ portsdir=${PORTSDIR:-/usr/ports}/
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ # First try INDEX-5
+ indexfile=$portsdir/INDEX-5
+ # Then INDEX if INDEX-5 does not exist or system is not FreeBSD 5.x
+ [ "${OSTYPE%.*}" = "freebsd5" -a -f $indexfile ] ||
+ indexfile=$portsdir/INDEX
+
+ [ "$prev" = "-l" -o "$prev" = "-L" -o "$prev" = "-o" ] && return 0
+
+ COMPREPLY=( $( egrep "^$cur" < $indexfile | cut -d'|' -f1 ) )
+ COMPREPLY2=( $( egrep "^[^\|]+\|$portsdir$cur" < $indexfile | \
+ cut -d'|' -f2 ) )
+ COMPREPLY2=( ${COMPREPLY2[@]#$portsdir} )
+ COMPREPLY=( "${COMPREPLY[@]}" "${COMPREPLY2[@]}" )
+
+ return 0
+} &&
+complete -F _portinstall $dirnames portinstall
+
+# Slackware Linux removepkg completion
+#
+have removepkg && [ -f /etc/slackware-version ] &&
+_removepkg()
+{
+ local packages cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( (cd /var/log/packages; compgen -f -- "$cur") ) )
+} &&
+complete -F _removepkg $filenames removepkg &&
+ complete $dirnames -f -X '!*.tgz' installpkg upgradepkg explodepkg
+
+# look(1) completion
+#
+have look &&
+_look()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD = 1 ]; then
+ COMPREPLY=( $( compgen -W '$(look $cur)' ) )
+ fi
+} &&
+complete -F _look $default look
+
+# ypcat(1) and ypmatch(1) completion
+#
+have ypmatch &&
+_ypmatch()
+{
+ local cur map
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ [ $1 = ypcat ] && [ $COMP_CWORD -gt 1 ] && return 0
+ [ $1 = ypmatch ] && [ $COMP_CWORD -gt 2 ] && return 0
+
+ if [ $1 = ypmatch ] && [ $COMP_CWORD -eq 1 ] && \
+ [ ${#COMP_WORDS[@]} -eq 3 ]; then
+ map=${COMP_WORDS[2]}
+ COMPREPLY=( $( compgen -W '$( ypcat $map | \
+ cut -d':' -f 1 )' -- $cur) )
+ else
+ [ $1 = ypmatch ] && [ $COMP_CWORD -ne 2 ] && return 0
+ COMPREPLY=( $( compgen -W \
+ '$( echo $(ypcat -x | cut -d"\"" -f 2))' -- $cur))
+ fi
+
+ return 0
+} &&
+complete -F _ypmatch ypmatch ypcat
+
+# mplayer(1) completion
+#
+have mplayer && {
+_mplayer_options_list()
+{
+ cur=${cur%\\}
+ COMPREPLY=( $( $1 $2 help 2> /dev/null | \
+ sed -e '1,/^Available/d' | awk '{print $1}' | \
+ sed -e 's/:$//' -e 's/^'${2#-}'$//' -e 's/<.*//' | \
+ grep "^$cur" ) )
+}
+
+_mplayer()
+{
+ local cmd cur prev skinsdir IFS=$' \t\n' i j k=0
+
+ COMPREPLY=()
+ cmd=${COMP_WORDS[0]}
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(ac|afm|vc|vfm|ao|vo|vop|fstype))
+ _mplayer_options_list mplayer $prev
+ return 0
+ ;;
+ -@(oac|ovc|of))
+ _mplayer_options_list mencoder $prev
+ return 0
+ ;;
+ -audiofile)
+ _filedir '@(mp3|MP3|mpg|MPG|ogg|OGG|wav|WAV|mid|MID|flac|FLAC|mka|MKA)'
+ return 0
+ ;;
+ -font)
+ _filedir '@(desc|ttf)'
+ return 0
+ ;;
+ -sub)
+ _filedir '@(srt|SRT|sub|SUB|txt|TXT|utf|UTF|rar|RAR|mpsub|smi|js|ssa|SSA|aas|AAS)'
+ return 0
+ ;;
+ -vobsub)
+ _filedir '@(idx|IDX|ifo|IFO|sub|SUB)'
+ IFS=$'\t\n'
+ COMPREPLY=( $( for i in "${COMPREPLY[@]}"; do
+ if [ -f $i -a -r $i ]; then
+ echo ${i%.*}
+ else
+ echo $i
+ fi
+ done ) )
+ IFS=$' \t\n'
+ return 0
+ ;;
+ -ifo)
+ _filedir '@(ifo|IFO)'
+ return 0
+ ;;
+ -cuefile)
+ _filedir '@(bin|BIN|cue|CUE)'
+ return 0
+ ;;
+ -skin)
+ # if you don't have installed mplayer in /usr/local you
+ # may want to set the MPLAYER_SKINS_DIR global variable
+ if [ -n "$MPLAYER_SKINS_DIR" ]; then
+ skinsdir=$MPLAYER_SKINS_DIR
+ else
+ skinsdir=/usr/local/share/mplayer/Skin
+ fi
+
+ IFS=$'\t\n'
+ for i in ~/.mplayer/Skin $skinsdir; do
+ if [ -d $i -a -r $i ]; then
+ for j in $( compgen -d $i/$cur ); do
+ COMPREPLY[$k]=${j#$i/}
+ k=$((++k))
+ done
+ fi
+ done
+ IFS=$' \t\n'
+ return 0
+ ;;
+ -@(mixer|@(cdrom|dvd)-device|dvdauth|fb|zrdev))
+ cur=${cur:=/dev/}
+ _filedir
+ return 0
+ ;;
+ -@(edl?(out)|lircconf|menu-cfg|playlist|csslib|dumpfile)| \
+ -@(subfile|vobsub|aofile|fbmodeconfig|include|o|dvdkey)| \
+ -passlogfile)
+ _filedir
+ return 0
+ ;;
+ -@(auto@(q|sync)|loop|menu-root|speed|sstep|aid|alang)| \
+ -@(?(@(audio|sub)-)demuxer|bandwidth|cache|chapter)| \
+ -@(dvd?(angle)|fps|frames|mc|passwd|user|sb|srate|ss|vcd)| \
+ -@(vi?(d|vo)|ffactor|sid|slang|spu@(align|aa|gauss))| \
+ -@(vobsubid|delay|bpp|brightness|contrast|dfbopts|display)| \
+ -@(fbmode|geometry|guiwid|hue|icelayer|screen[wh]|wid)| \
+ -@(monitor@(aspect|-@(dotclock|[hv]freq))|panscan|saturation)| \
+ -@(xineramascreen|zr@(crop|norm|quality|[xy]doff|[vh]dec))| \
+ -@(aspect|pp|x|y|xy|z|stereo|audio-@(density|delay|preload))| \
+ -@(endpos|osdlevel|ffourcc|sws|channels|skiplimit|format)| \
+ -@(ofps|aa@(driver|@(osd|sub)color)|vobsubout?(i@(ndex|d)))| \
+ -sub@(-bg-@(alpha|color)|cp|delay|fps|pos|align|width)| \
+ -sub@(font-@(blur|outline|autoscale|encoding|@(osd|text)-scale)))
+ return 0
+ ;;
+ -lavdopts)
+ COMPREPLY=( $( compgen -W 'ec er= bug= idct= gray' \
+ -- $cur ) )
+ return 0
+ ;;
+ -lavcopts)
+ COMPREPLY=( $( compgen -W 'vcodec= vqmin= vqscale= \
+ vqmax= mbqmin= mbqmax= vqdiff= \
+ vmax_b_frames= vme= vhq v4mv \
+ keyint= vb_strategy= vpass= \
+ aspect= vbitrate= vratetol= \
+ vrc_maxrate= vrc_minrate= \
+ vrc_buf_size= vb_qfactor= vi_qfactor= \
+ vb_qoffset= vi_qoffset= vqblur= \
+ vqcomp= vrc_eq= vrc_override= \
+ vrc_init_cplx= vqsquish= vlelim= \
+ vcelim= vstrict= vdpart vpsize= gray \
+ vfdct= idct= lumi_mask= dark_mask= \
+ tcplx_mask= scplx_mask= naq ildct \
+ format= pred qpel precmp= cmp= \
+ subcmp= predia= dia= trell last_pred= \
+ preme= subq= psnr mpeg_quant aic umv' \
+ -- $cur ) )
+ return 0
+ ;;
+ -ssf)
+ COMPREPLY=( $( compgen -W 'lgb= cgb= ls= cs= chs= \
+ cvs=' -- $cur ) )
+ return 0
+ ;;
+ -jpeg)
+ COMPREPLY=( $( compgen -W 'noprogressive progressive \
+ nobaseline baseline optimize= \
+ smooth= quality= outdir=' -- $cur ) )
+ return 0
+ ;;
+ -xvidopts)
+ COMPREPLY=( $( compgen -W 'dr2 nodr2' -- $cur ) )
+ return 0
+ ;;
+ -xvidencopts)
+ COMPREPLY=( $( compgen -W 'pass= bitrate= \
+ fixed_quant= me_quality= 4mv \
+ rc_reaction_delay_factor= \
+ rc_averaging_period= rc_buffer= \
+ quant_range= min_key_interval= \
+ max_key_interval= mpeg_quant \
+ mod_quant lumi_mask hintedme \
+ hintfile debug keyframe_boost= \
+ kfthreshold= kfreduction=' -- $cur ) )
+ return 0
+ ;;
+ -divx4opts)
+ COMPREPLY=( $( compgen -W 'br= key= deinterlace q= \
+ min_quant= max_quant= rc_period= \
+ rc_reaction_period= crispness= \
+ rc_reaction_ratio= pass= vbrpass= \
+ help' -- $cur ) )
+ return 0
+ ;;
+ -info)
+ COMPREPLY=( $( compgen -W 'name= artist= genre= \
+ subject= copyright= srcform= \
+ comment= help' -- $cur ) )
+ return 0
+ ;;
+ -lameopts)
+ COMPREPLY=( $( compgen -W 'vbr= abr cbr br= q= aq= \
+ ratio= vol= mode= padding= fast \
+ preset= help' -- $cur ) )
+ return 0
+ ;;
+ -rawaudio)
+ COMPREPLY=( $( compgen -W 'on channels= rate= \
+ samplesize= format=' -- $cur ) )
+ return 0
+ ;;
+ -rawvideo)
+ COMPREPLY=( $( compgen -W 'on fps= sqcif qcif cif \
+ 4cif pal ntsc w= h= y420 yv12 yuy2 \
+ y8 format= size=' -- $cur ) )
+ return 0
+ ;;
+ -aop)
+ COMPREPLY=( $( compgen -W 'list= delay= format= fout= \
+ volume= mul= softclip' -- $cur ) )
+ return 0
+ ;;
+ -dxr2)
+ COMPREPLY=( $( compgen -W 'ar-mode= iec958-encoded \
+ iec958-decoded mute ucode= 75ire bw \
+ color interlaced macrovision= norm= \
+ square-pixel ccir601-pixel cr-left= \
+ cr-right= cr-top= cr-bot= ck-rmin= \
+ ck-gmin= ck-bmin= ck-rmax= ck-gmax= \
+ ck-bmax= ck-r= ck-g= ck-b= \
+ ignore-cache= ol-osd= olh-cor= \
+ olw-cor= olx-cor= oly-cor= overlay \
+ overlay-ratio= update-cache' -- $cur ))
+ return 0
+ ;;
+ -tv)
+ COMPREPLY=( $( compgen -W 'on noaudio driver= device= \
+ input= freq= outfmt= width= height= \
+ buffersize= norm= channel= chanlist= \
+ audiorate= forceaudio alsa amode= \
+ forcechan= adevice= audioid= volume= \
+ bass= treble= balance= fps= \
+ channels= immediatemode=' -- $cur ) )
+ return 0
+ ;;
+ -mf)
+ COMPREPLY=( $( compgen -W 'on w= h= fps= type=' \
+ -- $cur ) )
+ return 0
+ ;;
+ -cdda)
+ COMPREPLY=( $( compgen -W 'speed= paranoia= \
+ generic-dev= sector-size= overlap= \
+ toc-bias toc-offset= skip noskip' \
+ -- $cur ) )
+ return 0
+ ;;
+ -input)
+ COMPREPLY=( $( compgen -W 'conf= ar-delay ar-rate \
+ keylist cmdlist js-dev file' -- $cur ) )
+ return 0
+ ;;
+ -af)
+ COMPREPLY=( $( compgen -W 'resample resample= \
+ channels channels= format format= \
+ volume volume= delay delay= pan \
+ pan= sub sub= surround surround=' \
+ -- $cur ) )
+ return 0
+ ;;
+ -af-adv)
+ COMPREPLY=( $( compgen -W 'force= list=' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W '-aid -alang -audio-demuxer \
+ -audiofile -cdrom-device -cache -cdda \
+ -channels -chapter -csslib -demuxer \
+ -dvd -dvd-device -dvdangle -dvdauth \
+ -dvdkey -dvdnav -forceidx -fps -frames \
+ -hr-mp3-seek -idx -mc -mf -ni -nobps \
+ -passwd -rawaudio -rtsp-stream-over-tcp\
+ -skipopening -sb -srate -ss -tv -user \
+ -vcd -vid -vivo -ifo -ffactor -font \
+ -noautosub -nooverlapsub -sid -slang \
+ -sub -subcc -subcp -sub-demuxer \
+ -subdelay -subfont-autoscale \
+ -subfont-blur -subfont-encoding \
+ -subfont-osd-scale -subfont-outline \
+ -subfont-text-scale -subfps -subfile \
+ -subpos -unicode -utf8 -vobsub \
+ -vobsubid -ac -afm -aspect -flip \
+ -lavdopts -noaspect -nosound -pp -ssf \
+ -stereo -sws -vc -vfm -vop -xvidopts\
+ -xy -zoom -bandwidth -cuefile \
+ -noextbased -rawvideo -overlapsub \
+ -sub-bg-alpha -sub-bg-color -subalign \
+ -subwidth -sub-no-text-pp -spualign \
+ -spuaa -spugauss -pphelp -verbose -v \
+ -noni -noidx -nohr-mp3-seek -extbased \
+ -bps -oldpp -nozoom -noflip -nounicode \
+ -noutf8' -- $cur ) )
+ # add mplayer specific options
+ [[ "$cmd" == @(?(g)mplayer) ]] && COMPREPLY=( "${COMPREPLY[@]}" \
+ $(compgen -W '-autoq -autosync -benchmark \
+ -framedrop -h -help -hardframedrop \
+ -identify -input -lircconf -loop \
+ -nojoystick -nolirc -nortc -playlist \
+ -quiet -really-quiet -rnd -sdp -skin \
+ -slave -softsleep -speed -sstep \
+ -use-stdin -dumpaudio -dumpfile \
+ -dumpstream -dumpvideo -dumpmicrodvdsub\
+ -dumpmpsub -dumpsrtsub -dumpjacosub \
+ -dumpsami -dumpsub -osdlevel -af \
+ -af-adv -ao -aofile -aop -delay -mixer \
+ -nowaveheader -bpp -brightness \
+ -contrast -display -double -dr -dxr2 \
+ -fb -fbmode -fbmodeconfig -forcexv -fs \
+ -geometry -hue -icelayer -jpeg \
+ -monitor-dotclock -monitor-hfreq \
+ -monitor-vfreq -monitoraspect \
+ -nograbpointer -noslices -panscan \
+ -rootwin -saturation -screenw -screenh \
+ -stop-xscreensaver -vm -vo -vsync -wid \
+ -xineramascreen -z -zrbw -zrcrop \
+ -zrdev -zrfd -zrhelp -zrnorm -zrquality \
+ -zrvdec -zrhdec -zrxdoff -zrydoff -y \
+ -edl -edlout -enqueue -fixed-vo \
+ -menu -menu-root -menu-cfg -shuffle \
+ -format -aahelp -dfbopts -fstype \
+ -guiwid -nokeepaspect -x --help \
+ -aaosdcolor -aasubcolor -aadriver \
+ -aaextended -aaeight' -- $cur) )
+ # add mencoder specific options
+ [[ "$cmd" = mencoder ]] && COMPREPLY=( "${COMPREPLY[@]}" \
+ $(compgen -W '-audio-density -audio-delay \
+ -audio-preload -divx4opts -endpos \
+ -ffourcc -include -info -lameopts \
+ -lavcopts -noskip -o -oac -ofps -ovc \
+ -passlogfile -skiplimit -vobsubout \
+ -vobsuboutindex -vobsuboutid \
+ -xvidencopts -of --verbose' -- $cur) )
+ ;;
+ *)
+ _filedir '@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|ASF|vob|VOB|bin|BIN|dat|DAT|vcd|VCD|ps|PS|pes|PES|fli|FLI|flv|FLV|viv|VIV|rm?(j)|RM?(J)|ra?(m)|RA?(M)|yuv|YUV|mov|MOV|qt|QT|mp[34]|MP[34]|m4v|M4V|og[gm]|OG[GM]|wav|WAV|dump|DUMP|mk[av]|MK[AV]|m4a|M4A|aac|AAC|m2v|M2V|dv|DV|rmvb|RMVB|mid|MID|ts|TS|3gp|mpc|MPC|flac|FLAC|flv|FLV|divx|DIVX)'
+ ;;
+ esac
+
+ return 0
+}
+complete $filenames -F _mplayer mplayer mencoder gmplayer kplayer
+}
+
+# KDE dcop completion
+#
+have dcop &&
+_dcop()
+{
+ local cur compstr
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ if [ -z $cur ]; then
+ compstr=${COMP_WORDS[*]}
+ else
+ compstr=$( command echo ${COMP_WORDS[*]} | sed "s/ $cur$//" )
+ fi
+ COMPREPLY=( $( compgen -W '$( command $compstr | sed s/\(.*\)// )' -- $cur ) )
+} &&
+complete -F _dcop dcop
+
+# wvdial(1) completion
+#
+have wvdial &&
+_wvdial()
+{
+ local cur prev config i IFS=$'\t\n'
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ --config)
+ _filedir
+ return 0
+ ;;
+ esac
+
+ case $cur in
+ -*)
+ COMPREPLY=( $( compgen -W '--config --chat \
+ --remotename --help --version --no-syslog' \
+ -- $cur ) )
+ ;;
+ *)
+ # start with global and personal config files
+ config="/etc/wvdial.conf"$'\t'"$HOME/.wvdialrc"
+ # replace with command line config file if present
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" == "--config" ]]; then
+ config=${COMP_WORDS[i+1]}
+ break
+ fi
+ done
+ # parse config files for sections and
+ # remove default section
+ COMPREPLY=( $( sed -ne \
+ "s|^\[Dialer \($cur.*\)\]$|\1|p" \
+ $config 2>/dev/null |grep -v '^Defaults$'))
+ # escape spaces
+ COMPREPLY=${COMPREPLY// /\\ }
+ ;;
+ esac
+
+} &&
+complete -F _wvdial wvdial
+
+# gpg(1) completion
+#
+have gpg &&
+_gpg()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(s|-sign|-clearsign|-decrypt-files|-load-extension))
+ _filedir
+ return 0
+ ;;
+ --@(export|@(?(l|nr|nrl)sign|edit)-key))
+ # return list of public keys
+ COMPREPLY=( $( compgen -W "$( gpg --list-keys 2>/dev/null | sed -ne 's@^pub.*/\([^ ]*\).*\(<\([^>]*\)>\).*$@\1 \3@p')" -- "$cur" ))
+ return 0
+ ;;
+ -@(r|-recipient))
+ COMPREPLY=( $( compgen -W "$( gpg --list-keys 2>/dev/null | sed -ne 's@^pub.*<\([^>]*\)>.*$@\1@p')" -- "$cur" ))
+ if [ -e ~/.gnupg/gpg.conf ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$( sed -ne 's@^[ \t]*group[ \t][ \t]*\([^=]*\).*$@\1@p' ~/.gnupg/gpg.conf )" -- "$cur") )
+ fi
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-s -b -e -f -c -d -a -r -u -Z -o -v\
+ -q -n -N $(gpg --dump-options)' -- $cur ) )
+ fi
+
+} &&
+complete -F _gpg $default gpg
+
+# iconv(1) completion
+#
+have iconv &&
+_iconv()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(f|t|-@(from|to)-code))
+ COMPREPLY=( $( compgen -W \
+ '$( iconv --list | sed -e "s@//@@;" )' -- "$cur" ) )
+ return 0
+ ;;
+ esac
+
+
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '--from-code -f --to-code -t --list
+ --output -o --verbose' -- "$cur" ) )
+ return 0
+ fi
+} &&
+complete -F _iconv $default iconv
+
+# dict(1) completion
+#
+{ have dict || have rdict; } && {
+_dictdata()
+{
+ dict $host $port $1 2>/dev/null | sed -ne \
+ 's/^['$'\t '']['$'\t '']*\([^'$'\t '']*\).*$/\1/p'
+}
+
+_dict()
+{
+ local cur prev host port db dictfile
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ dictfile=/usr/share/dict/words
+
+ for (( i=1; i < COMP_CWORD; i++ )); do
+ case "${COMP_WORDS[i]}" in
+ -@(h|--host))
+ host=${COMP_WORDS[i+1]}
+ [ -n "$host" ] && host="-h $host"
+ i=$((++i))
+ ;;
+ -@(p|-port))
+ port=${COMP_WORDS[i+1]}
+ [ -n "$port" ] && port="-p $port"
+ i=$((++i))
+ ;;
+ -@(d|-database))
+ db=${COMP_WORDS[i+1]}
+ [ -n "$db" ] && host="-d $db"
+ i=$((++i))
+ ;;
+ *)
+ ;;
+ esac
+ done
+
+ if [[ "$cur" = -* ]]; then
+ COMPREPLY=( $( compgen -W '-h --host -p --port -d --database \
+ -m --match -s --strategy -c --config -C \
+ --nocorrect -D --dbs -S --strats -H \
+ --serverhelp -i --info -I --serverinfo \
+ -a --noauth -u --user -k --key -V --version \
+ -L --license --help -v --verbose -r --raw \
+ -P --pager --debug --html --pipesize --client' \
+ -- "$cur" ) )
+ return 0
+ fi
+
+ case "$prev" in
+ -@(d|-database|i|info))
+ COMPREPLY=( $( compgen -W '$( _dictdata -D )' -- "$cur" ) )
+ return 0
+ ;;
+ -@(s|-strategy))
+ COMPREPLY=( $( compgen -W '$( _dictdata -S )' -- "$cur" ) )
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+
+ [ -r $dictfile ] && \
+ COMPREPLY=( $( compgen -W '$( cat $dictfile )' -- "$cur" ) )
+}
+complete -F _dict $default dict rdict
+}
+
+# cdrecord(1) completion
+#
+have cdrecord &&
+_cdrecord()
+{
+ local cur prev i generic_options track_options track_mode
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # foo=bar style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ @(text|cue)file)
+ _filedir
+ return 0
+ ;;
+ blank)
+ COMPREPLY=( $( compgen -W 'help all fast \
+ track unreserve trtail unclose session' \
+ -- $cur ) )
+ return 0
+ ;;
+ driveropts)
+ COMPREPLY=( $( compgen -W 'burnfree noburnfree\
+ varirec= audiomaster forcespeed noforcespeed\
+ speedread nospeedread singlesession \
+ nosinglesession hidecdr nohidecdr tattooinfo\
+ tattoofile=' -- $cur ) )
+ return 0
+ ;;
+ esac
+ fi
+
+ generic_options=(-version -v -V -d -silent -s -force -immed -dummy \
+ -dao -raw -raw96r -raw96p -raw16 -multi -msinfo -toc \
+ -atip -fix -nofix -waiti -load -lock -eject -format \
+ -setdropts -checkdrive -prcap -inq -scanbus -reset \
+ -abort -overburn -ignsize -useinfo -packet -noclose \
+ -text debug= kdebug= kd= minbuf= speed= blank= fs= \
+ dev= gracetime= timeout= driver= driveropts= \
+ defpregap= pktsize= mcn= textfile= cuefile=)
+ track_options=(-audio -swab -data -mode2 -xa -xa1 -xa2 -xamix -cdi \
+ -isosize -pad padsize= -nopad -shorttrack -noshorttrack\
+ pregap= -preemp -nopreemp -copy -nocopy -scms tcsize= \
+ isrc= index=)
+ # look if previous was either a file or a track option
+ track_mode=0
+ if [ $COMP_CWORD -gt 1 ]; then
+ if [ -f "$prev" ]; then
+ track_mode=1
+ else
+ for (( i=0; i < ${#track_options[@]}; i++ )); do
+ if [[ "${track_options[i]}" == "$prev" ]]; then
+ track_mode=1
+ break
+ fi
+ done
+ fi
+ fi
+
+ # files are always eligible completion
+ _filedir
+ # track options are always available
+ COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W '${track_options[@]}' -- $cur ) )
+ # general options are no more available after file or track option
+ if [ $track_mode -eq 0 ]; then
+ COMPREPLY=( "${COMPREPLY[@]}" \
+ $( compgen -W '${generic_options[@]}' -- $cur ) )
+ fi
+
+} &&
+complete -F _cdrecord $filenames cdrecord
+
+# mkisofs(8) completion
+#
+have mkisofs &&
+_mkisofs()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(o|abstract|biblio|check-session|copyright|log-file|root-info|prep-boot|*-list))
+ _filedir
+ return 0
+ ;;
+ -*-charset)
+ COMPREPLY=( $( mkisofs -input-charset help 2>&1 | \
+ tail +3 | grep "^$cur") )
+ return 0
+ ;;
+ -uid)
+ _uids
+ return 0
+ ;;
+ -gid)
+ _gids
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-abstract -A -allow-lowercase \
+ -allow-multidot -biblio -cache-inodes \
+ -no-cache-inodes -b -eltorito-alt-boot -B -G \
+ -hard-disk-boot -no-emul-boot -no-boot \
+ -boot-load-seg -boot-load-size \
+ -boot-info-table -C -c -check-oldname \
+ -check-session -copyright -d -D -dir-mode \
+ -dvd-video -f -file-mode -gid -gui \
+ -graft-points -hide -hide-list -hidden \
+ -hidden-list -hide-joliet -hide-joliet-list \
+ -hide-joliet-trans-tbl -hide-rr-moved \
+ -input-charset -output-charset -iso-level -J \
+ -joliet-long -jcharset -l -L -log-file -m \
+ -exclude-list -max-iso9660-filenames -M -N \
+ -new-dir-mode -nobak -no-bak -force-rr -no-rr \
+ -no-split-symlink-components \
+ -no-split-symlink-fields -o -pad -no-pad \
+ -path-list -P -p -print-size -quiet -R -r \
+ -relaxed-filenames -sort -split-output \
+ -stream-media-size -stream-file-name -sysid -T\
+ -table-name -ucs-level -udf -uid \
+ -use-fileversion -U -no-iso-translate -V \
+ -volset -volset-size -volset-seqno -v -x -z \
+ -hfs -apple -map -magic -hfs-creator \
+ -hfs-type -probe -no-desktop -mac-name \
+ -boot-hfs-file -part -auto -cluster-size \
+ -hide-hfs -hide-hfs-list -hfs-volid \
+ -icon-position -root-info -prep-boot \
+ -input-hfs-charset -output-hfs-charset \
+ -hfs-unlock -hfs-bless -hfs-parms --cap \
+ --netatalk --double --ethershare --ushare \
+ --exchange --sgi --xinet --macbin --single \
+ --dave --sfm --osx-double --osx-hfs' -- $cur ))
+ else
+ _filedir
+ fi
+
+} &&
+complete -F _mkisofs $filenames mkisofs
+
+# mc(1) completion
+#
+have mc &&
+_mc()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # -name value style option
+ case "$prev" in
+ -@(e|v|l|P))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --@(edit|view|ftplog|printwd))
+ _filedir
+ return 0
+ ;;
+ esac
+ fi
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a --stickchars -b --nocolor -c \
+ --color -C --colors= -d --nomouse -e --edit= -f \
+ --datadir -k --resetsoft -l --ftplog= -P --printwd= \
+ -s --slow -t --termcap -u --nosubshell -U --subshell \
+ -v --view= -V --version -x --xterm -h --help' -- $cur ) )
+ else
+ _filedir -d
+ fi
+} &&
+complete -F _mc $filenames mc
+
+# yum(8) completion
+#
+have yum && {
+_yum()
+{
+ local cur prev special
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do
+ if [[ ${COMP_WORDS[i]} == @(install|update|upgrade|remove|erase|deplist) ]]; then
+ special=${COMP_WORDS[i]}
+ fi
+ done
+
+ if [ -n "$special" ]; then
+ case $special in
+ install|deplist)
+ COMPREPLY=( $( compgen -W '$( yum -C list | cut -d" " -f1 )' -- $cur ) )
+ return 0
+ ;;
+ *)
+ _rpm_installed_packages
+ return 0
+ ;;
+ esac
+ fi
+
+ case $cur in
+ --*)
+ COMPREPLY=( $( compgen -W '--installroot --version --help --enablerepo --disablerepo --exclude --obsoletes --noplugins' -- $cur ) )
+ return 0
+ ;;
+ -*)
+ COMPREPLY=( $( compgen -W '-c -e -d -y -t -R -C -h' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ case $prev in
+ list)
+ COMPREPLY=( $( compgen -W 'all available updates installed extras obsoletes recent' -- $cur ) )
+ ;;
+ clean)
+ COMPREPLY=( $( compgen -W 'packages headers metadata cache dbcache all' -- $cur ) )
+ ;;
+ localinstall)
+ _filedir rpm
+ ;;
+ -c)
+ _filedir
+ ;;
+ --installroot)
+ _filedir -d
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W 'install update check-update upgrade remove list \
+ search info provides clean groupinstall groupupdate \
+ grouplist deplist erase groupinfo groupremove \
+ localinstall localupdate makecache resolvedep \
+ shell whatprovides' -- $cur ) )
+ ;;
+ esac
+}
+complete -F _yum $filenames yum
+
+# yum-arch(8) completion
+#
+_yum_arch()
+{
+ local cur
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W '-d -v -vv -n -c -z -s -l -q' -- $cur ) )
+ ;;
+ *)
+ _filedir -d
+ ;;
+ esac
+
+ return 0
+
+}
+complete -F _yum_arch $filenames yum-arch
+}
+
+# ImageMagick completion
+#
+have convert && {
+_ImageMagick()
+{
+ local prev
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -channel)
+ COMPREPLY=( $( compgen -W 'Red Green Blue Opacity \
+ Matte Cyan Magenta Yellow Black' -- $cur ) )
+ return 0
+ ;;
+ -colormap)
+ COMPREPLY=( $( compgen -W 'shared private' -- $cur ) )
+ return 0
+ ;;
+ -colorspace)
+ COMPREPLY=( $( compgen -W 'GRAY OHTA RGB Transparent \
+ XYZ YCbCr YIQ YPbPr YUV CMYK' -- $cur ) )
+ return 0
+ ;;
+ -compose)
+ COMPREPLY=( $( compgen -W 'Over In Out Atop Xor Plus \
+ Minus Add Subtract Difference Multiply Bumpmap\
+ Copy CopyRed CopyGreen CopyBlue CopyOpacity' \
+ -- $cur ) )
+ return 0
+ ;;
+ -compress)
+ COMPREPLY=( $( compgen -W 'None BZip Fax Group4 JPEG \
+ Lossless LZW RLE Zip' -- $cur ) )
+ return 0
+ ;;
+ -dispose)
+ COMPREPLY=( $( compgen -W 'Undefined None Background \
+ Previous' -- $cur ) )
+ return 0
+ ;;
+ -encoding)
+ COMPREPLY=( $( compgen -W 'AdobeCustom AdobeExpert \
+ AdobeStandard AppleRoman BIG5 GB2312 Latin2 \
+ None SJIScode Symbol Unicode Wansung' -- $cur))
+ return 0
+ ;;
+ -endian)
+ COMPREPLY=( $( compgen -W 'MSB LSB' -- $cur ) )
+ return 0
+ ;;
+ -filter)
+ COMPREPLY=( $( compgen -W 'Point Box Triangle Hermite \
+ Hanning Hamming Blackman Gaussian Quadratic \
+ Cubic Catrom Mitchell Lanczos Bessel Sinc' \
+ -- $cur ) )
+ return 0
+ ;;
+ -format)
+ COMPREPLY=( $( convert -list format | \
+ awk '/ [r-][w-][+-] / {print $1}' | \
+ tr -d '*' | tr [:upper:] [:lower:] | \
+ grep "^$cur" ) )
+ return 0
+ ;;
+ -gravity)
+ COMPREPLY=( $( compgen -W 'Northwest North NorthEast \
+ West Center East SouthWest South SouthEast' \
+ -- $cur ) )
+ return 0
+ ;;
+ -intent)
+ COMPREPLY=( $( compgen -W 'Absolute Perceptual \
+ Relative Saturation' -- $cur ) )
+ return 0
+ ;;
+ -interlace)
+ COMPREPLY=( $( compgen -W 'None Line Plane Partition' \
+ -- $cur ) )
+ return 0
+ ;;
+ -limit)
+ COMPREPLY=( $( compgen -W 'Disk File Map Memory' \
+ -- $cur ) )
+ return 0
+ ;;
+ -list)
+ COMPREPLY=( $( compgen -W 'Delegate Format Magic \
+ Module Resource Type' -- $cur ) )
+ return 0
+ ;;
+ -map)
+ COMPREPLY=( $( compgen -W 'best default gray red \
+ green blue' -- $cur ) )
+ _filedir
+ return 0
+ ;;
+ -noise)
+ COMPREPLY=( $( compgen -W 'Uniform Gaussian \
+ Multiplicative \
+ Impulse Laplacian Poisson' -- $cur ) )
+ return 0
+ ;;
+ -preview)
+ COMPREPLY=( $( compgen -W 'Rotate Shear Roll Hue \
+ Saturation Brightness Gamma Spiff \
+ Dull Grayscale Quantize Despeckle \
+ ReduceNoise AddNoise Sharpen Blur \
+ Treshold EdgeDetect Spread Shade \
+ Raise Segment Solarize Swirl Implode \
+ Wave OilPaint CharcoalDrawing JPEG' \
+ -- $cur ) )
+ return 0
+ ;;
+ -@(mask|profile|texture|tile|write))
+ _filedir
+ return 0
+ ;;
+ -type)
+ COMPREPLY=( $( compgen -W 'Bilevel Grayscale Palette \
+ PaletteMatte TrueColor TrueColorMatte \
+ ColorSeparation ColorSeparationlMatte \
+ Optimize' -- $cur ) )
+ return 0
+ ;;
+ -units)
+ COMPREPLY=( $( compgen -W 'Undefined PixelsPerInch \
+ PixelsPerCentimeter' -- $cur ) )
+ return 0
+ ;;
+ -virtual-pixel)
+ COMPREPLY=( $( compgen -W 'Constant Edge mirror tile' \
+ -- $cur ) )
+ return 0
+ ;;
+ -visual)
+ COMPREPLY=( $( compgen -W 'StaticGray GrayScale \
+ StaticColor PseudoColor TrueColor \
+ DirectColor defaut visualid' -- $cur ))
+ return 0
+ ;;
+ esac
+}
+
+_convert()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-adjoin -affine -antialias -append \
+ -authenticate -average -background -black-threshold \
+ -blue-primary -blur -border -bordercolor -channel \
+ -charcoal -chop -clip -coalesce -colorize -colors \
+ -colorspace -comment -compress -contrast -convolve \
+ -crop -cycle -debug -deconstruct -delay -density \
+ -depth -despeckle -display -dispose -dither -draw \
+ -edge -emboss -encoding -endian -enhance -equalize \
+ -extract -fill -filter -flatten -flip -flop -font \
+ -frame -fuzz -gamma -gaussian -geometry \
+ -green-primary -gravity -help -implode -intent \
+ -interlace -label -lat -level -limit -list -log -loop \
+ -map -mask -matte -median -modulate -monochrome \
+ -morph -mosaic -negate -noop -noise -normalize \
+ -opaque -ordered-dither -page -paint -ping -pointsize \
+ -preview -profile -quality -raise -random-threshold \
+ -region -raise -red-primary -render -resize -resample \
+ -roll -rotate -sample -sampling-factor -scale -scene \
+ -seed -segment -shade -sharpen -shave -shear -size \
+ -solarize -spread -stroke -strokewidth -swirl \
+ -texture -threshold -thumbnail -tile -transform \
+ -transparent -treedepth -trim -type -undercolor \
+ -units -unsharp -verbose -version -view \
+ -virtual-pixel -wave -white-point -white-threshold \
+ -write' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+adjoin +append +compress \
+ +contrast +debug +dither +endian +gamma +label +map \
+ +mask +matte +negate +noise +page +raise +render \
+ +write' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _convert $filenames convert
+
+_mogrify()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-affine -antialias -authenticate \
+ -background -black-threshold -blue-primary -blur \
+ -border -bordercolor -channel -charcoal -chop \
+ -colorize -colors -colorspace -comment -compress \
+ -contrast -convolve -crop -cycle -debug -delay \
+ -density -depth -despeckle -display -dispose -dither \
+ -draw -edge -emboss -encoding -endian -enhance \
+ -equalize -extract -fill -filter -flip -flop -font \
+ -format -frame -fuzz -gamma -gaussian -geometry \
+ -green-primary -implode -interlace -help -label -lat \
+ -level -limit -list -log -loop -map -mask -matte \
+ -median -modulate -monochrome -negate -noop \
+ -normalize -opaque -page -paint -fill -ordered-dither \
+ -pointsize -profile -quality -raise -random-threshold \
+ -red-primary -region -resample -resize -roll -rotate \
+ -sample -sampling-factor -scale -scene -seed -segment \
+ -shade -sharpen -shear -size -solarize -spread \
+ -stroke -strokewidth -swirl -texture -threshold \
+ -thumbnail -tile -transform -transparent -treedepth \
+ -trim -type -undercolor -units -unsharp -verbose \
+ -version -view -virtual-pixel -wave -white-point \
+ -white-threshold' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+compress +contrast +debug +dither \
+ +endian +gamma +label +map +mask +matte +negate +page \
+ +raise' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _mogrify $filenames mogrify
+
+_display()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-authenticate -backdrop -border \
+ -colormap -colors -colorspace -comment -compress \
+ -contrast -crop -debug -delay -density -depth \
+ -despeckle -display -dispose -dither -edge -endian \
+ -enhance -extract -filter -flip -flop -frame -gamma \
+ -geometry -help -immutable -interlace -label -limit \
+ -log -map -matte -monochrome -negate -noop -page \
+ -quality -raise -remote -roll -rotate -sample \
+ -sampling-factor -scene -segment -sharpen -size \
+ -texture -treedepth -trim -update -verbose -version \
+ -virtual-pixel -window -window_group -write' -- $cur))
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+compress +contrast +debug +dither \
+ +endian +gamma +label +map +matte +negate +page \
+ +raise +write' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _display $filenames display
+
+_animate()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-authenticate -backdrop -colormap \
+ -colors -colorspace -crop -debug -delay -density \
+ -depth -display -dither -extract -gamma -geometry \
+ -help -interlace -limit -log -matte -map -monochrome \
+ -noop -pause -remote -rotate -sampling-factor -scene \
+ -size -treedepth -trim -verbose -version -visual \
+ -virtual-pixel -window' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+debug +dither +gamma +map +matte' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _animate $filenames animate
+
+_identify()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-authenticate -debug -density \
+ -depth -extract -format -help -interlace -limit -list \
+ -log -size -sampling-factor -verbose -version \
+ -virtual-pixel' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+debug ' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _identify $filenames identify
+
+_montage()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-adjoin -affine -authenticate \
+ -blue-primary -blur -colors -colorspace -comment \
+ -compose -compress -crop -debug -density -depth \
+ -display -dispose -dither -draw -encoding -endian \
+ -extract -fill -filter -flip -flop -frame -gamma \
+ -geometry -gravity -green-primary -interlace -help \
+ -label -limit -log -matte -mode -monochrome -noop \
+ -page -pointsize -quality -red-primary -resize \
+ -rotate -sampling-factor -scene -shadow -size \
+ -stroke -texture -thumbnail -tile -transform \
+ -transparent -treedepth -trim -type -verbose \
+ -version -virtual-pixel -white-point' -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+adjoin +compress +debug +dither \
+ +endian +gamma +label +matte +page' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _montage $filenames montage
+
+_composite()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _ImageMagick
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-affine -authenticate \
+ -blue-primary -colors -colorspace -comment -compose \
+ -compress -debug -density -depth -displace -display \
+ -dispose -dissolve -dither -encoding -endian -extract \
+ -filter -font -geometry -gravity -green-primary -help \
+ -interlace -label -limit -log -matte -monochrome \
+ -negate -page -profile -quality -red-primary -rotate \
+ -resize -sampling-factor -scene -sharpen -size \
+ -stegano -stereo -thumbnail -tile -transform \
+ -treedepth -type -units -unsharp -verbose -version \
+ -virtual-pixel -watermark -white-point -write' \
+ -- $cur ) )
+ elif [[ "$cur" == +* ]]; then
+ COMPREPLY=( $( compgen -W '+compress +debug +dither +endian +label \
+ +matte +negate +page +write' -- $cur ) )
+ else
+ _filedir
+ fi
+}
+complete -F _composite $filenames composite
+}
+
+# dd(1) completion
+#
+have dd &&
+_dd()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ case "$cur" in
+ if=*|of=*)
+ cur=${cur#*=}
+ _filedir
+ return 0
+ ;;
+ conv=*)
+ cur=${cur#*=}
+ COMPREPLY=( $( compgen -W 'ascii ebcdic ibm block unblock \
+ lcase notrunc ucase swab noerror sync' \
+ -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ _expand || return 0
+
+ COMPREPLY=( $( compgen -W '--help --version' -- $cur ) \
+ $( compgen -W 'bs cbs conv count ibs if obs of seek skip'\
+ -S '=' -- $cur ) )
+} &&
+complete -F _dd $nospace $filenames dd
+
+# CUPS cancel(1) completion
+#
+have cancel &&
+_cancel()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( lpstat | cut -d' ' -f1 | grep "^$cur" ) )
+} &&
+complete -F _cancel $filenames cancel
+
+# aspell(1) completion
+#
+have aspell && {
+_aspell_dictionary()
+{
+ local datadir
+ datadir=/usr/lib/aspell
+ COMPREPLY=( $( command ls $datadir/*.@(multi|alias) ) )
+ COMPREPLY=( ${COMPREPLY[@]%.@(multi|alias)} )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]#$datadir/}' -- $cur ) )
+}
+
+_aspell()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case "$prev" in
+ @(-c|-p|check))
+ _filedir
+ return 0
+ ;;
+ @(dump|create|merge))
+ COMPREPLY=( $( compgen -W 'master personal repl' -- $cur ) )
+ return 0
+ ;;
+ -d)
+ _aspell_dictionary
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --@(conf|personal|repl|per-conf))
+ _filedir
+ return 0
+ ;;
+ --@(conf-dir|data-dir|dict-dir|home-dir|local-data-dir|prefix))
+ _filedir -d
+ return 0
+ ;;
+ --master)
+ _aspell_dictionary
+ return 0
+ ;;
+ --mode)
+ COMPREPLY=( $( compgen -W 'none url email sgml tex' -- $cur ) )
+ return 0
+ ;;
+ --sug-mode)
+ COMPREPLY=( $( compgen -W 'ultra fast normal bad-speller' -- $cur ) )
+ return 0
+ ;;
+ --keymapping)
+ COMPREPLY=( $( compgen -W 'aspell ispell' -- $cur ) )
+ return 0
+ ;;
+ esac
+ fi
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--conf= --conf-dir= --data-dir= --dict-dir= \
+ --encoding= --add-filter= --rem-filter= --mode= -e \
+ -H -t --add-extra-dicts= --rem-extra-dicts= \
+ --home-dir= -W --ignore= --ignore-accents \
+ --dont-ignore-accents --ignore-case --dont-ignore-case \
+ --ignore-repl --dont-ignore-repl --jargon= --keyboard= \
+ --lang= --language-tag= --local-data-dir= -d --master= \
+ --module= --add-module-search-order= \
+ --rem-module-search-order= --per-conf= -p --personal= \
+ --prefix= --repl= -C -B --run-together --dont-run-together \
+ --run-together-limit= --run-together-min= --save-repl \
+ --dont-save-repl --set-prefix --dont-set-prefix --size= \
+ --spelling= --strip-accents --dont-strip-accents \
+ --sug-mode= --add-word-list-path= --rem-word-list-path= \
+ -b -x --backup -b|-x --dont-backup --reverse --dont-reverse \
+ --time --dont-time --keymapping= --add-email-quote= \
+ --rem-email-quote= --email-margin= --add-tex-command= \
+ --rem-tex-command= --tex-check-comments \
+ --dont-tex-check-comments --add-tex-extension= \
+ --rem-tex-extension= --add-sgml-check= --rem-sgml-check= \
+ --add-sgml-extension= --rem-sgml-extension=' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W '-? help -c check -a pipe -l list \
+ config config soundslike filter -v version dump \
+ create merge' -- $cur ) )
+ fi
+
+}
+complete -F _aspell $filenames aspell
+}
+
+# xmms(1) completion
+#
+have xmms &&
+_xmms()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-h --help -r --rew -p --play \
+ -u --pause -s --stop -t --play-pause -f --fwd -e \
+ --enqueue -m --show-main-window -i --sm-client-id \
+ -v --version' -- $cur ) )
+ else
+ _filedir '@(mp[23]|MP[23]|ogg|OGG|wav|WAV|pls|m3u|xm|mod|s[3t]m|it|mtm|ult|flac)'
+
+ fi
+
+} &&
+complete -F _xmms $filenames xmms
+
+# info(1) completion
+#
+have info &&
+_info()
+{
+ local cur infopath UNAME
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _expand || return 0
+
+ # default completion if parameter contains /
+ if [[ "$cur" == */* ]]; then
+ _filedir
+ return 0
+ fi
+
+ infopath='/usr/share/info'
+
+ if [ "${INFOPATH: -1:1}" == ':' ]; then
+ infopath=${INFOPATH}${infopath}
+ elif [ ${INFOPATH:+set} ]; then
+ infopath=$INFOPATH
+ fi
+
+ infopath=$infopath:
+ if [ -n "$cur" ]; then
+ infopath="${infopath//://$cur* }"
+ else
+ infopath="${infopath//:// }"
+ fi
+
+ # redirect stderr for when path doesn't exist
+ COMPREPLY=( $( eval command ls "$infopath" 2>/dev/null ) )
+ # weed out directory path names and paths to info pages
+ COMPREPLY=( ${COMPREPLY[@]##*/?(:)} )
+ # weed out info dir file
+ for (( i=0 ; i < ${#COMPREPLY[@]} ; ++i )); do
+ if [ "${COMPREPLY[$i]}" == 'dir' ]; then
+ unset COMPREPLY[$i];
+ fi;
+ done
+ # strip suffix from info pages
+ COMPREPLY=( ${COMPREPLY[@]%.@(gz|bz2)} )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]%.*}' -- "${cur//\\\\/}" ) )
+
+ return 0
+} &&
+complete -F _info $filenames info
+
+# dhclient(1) completion
+#
+have dhclient && _dhclient()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(cf|lf|pf|sf))
+ _filedir
+ return 0
+ ;;
+ -s)
+ _known_hosts
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-p -d -q -1 -r -lf -pf \
+ -cf -sf -s -g -n -nw -w' -- $cur ) )
+ else
+ _available_interfaces
+ fi
+} &&
+complete -F _dhclient dhclient
+
+# lvm(8) completion
+#
+have lvm && {
+_volumegroups()
+{
+ COMPREPLY=( $(compgen -W "$( vgscan 2>/dev/null | \
+ sed -n -e 's|.*Found.*"\(.*\)".*$|\1|p' )" -- $cur ) )
+}
+
+_physicalvolumes()
+{
+ COMPREPLY=( $(compgen -W "$( pvscan 2>/dev/null | \
+ sed -n -e 's|^.*PV \(.*\) VG.*$|\1|p' )" -- $cur ) )
+}
+
+_logicalvolumes()
+{
+ COMPREPLY=( $(compgen -W "$( lvscan 2>/dev/null | \
+ sed -n -e "s|^.*'\(.*\)'.*$|\1|p" )" -- $cur ) )
+}
+
+_units()
+{
+ COMPREPLY=( $( compgen -W 'h s b k m g t H K M G T' -- $cur ) )
+}
+
+_sizes()
+{
+ COMPREPLY=( $( compgen -W 'k K m M g G t T' -- $cur ) )
+}
+
+_args()
+{
+ args=0
+ if [[ "${COMP_WORDS[0]}" == lvm ]]; then
+ offset=2
+ else
+ offset=1
+ fi
+ for (( i=$offset; i < COMP_CWORD; i++ )); do
+ if [[ "${COMP_WORDS[i]}" != -* ]]; then
+ args=$(($args + 1))
+ fi
+ done
+}
+
+_lvmdiskscan()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h -? --help -l \
+ --lvmpartition -v --verbose --version' -- $cur ) )
+ fi
+}
+complete -F _lvmdiskscan lvmdiskscan
+
+_pvscan()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -e \
+ --exported -n --novolumegroup -h -? \
+ --help --ignorelockingfailure -P \
+ --partial -s --short -u --uuid -v \
+ --verbose --version' -- $cur ) )
+ fi
+}
+complete -F _pvscan pvscan
+
+_pvs()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(o|O|-options|-sort))
+ COMPREPLY=( $( compgen -W 'pv_fmt pv_uuid \
+ pv_size pv_free pv_used pv_name \
+ pv_attr pv_pe_count \
+ pv_pe_alloc_count' -- $cur ) )
+ return 0
+ ;;
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--aligned -a --all -d --debug \
+ -h -? --help --ignorelockingfailure --noheadings \
+ --nosuffix -o --options -O --sort \
+ --separator --unbuffered --units \
+ -v --verbose --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvs pvs
+
+_pvdisplay()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c --colon -C --columns --units \
+ -v --verbose -d --debug -h --help --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvdisplay pvdisplay
+
+_pvchange()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|x|-autobackup|--allocatable))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a --all -A --autobackup \
+ -d --debug -h --help -t --test -u --uuid -x \
+ --allocatable -v --verbose --addtag --deltag \
+ --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvchange pvchange
+
+_pvcreate()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --restorefile)
+ _filedir
+ return 0
+ ;;
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ --metadatacopies)
+ COMPREPLY=( $( compgen -W '0 1 2' -- $cur ) )
+ return 0
+ ;;
+ --@(metadatasize|setphysicalvolumesize))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--restorefile -d --debug -f \
+ --force -h -? --help --labelsector -M --metadatatype \
+ --metadatacopies --metadatasize \
+ --setphysicalvolumesize -t --test -u --uuid uuid -v \
+ --verbose -y --yes --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvcreate pvcreate
+
+_pvmove()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(n|-name))
+ _logicalvolumes
+ return 0
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--abort -A --autobackup \
+ -b --background -d --debug -f --force -h -? \
+ --help -i --interval -t --test -v --verbose \
+ --version -n --name' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvmove pvmove
+
+_pvremove()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -f --force -h -? \
+ --help -y --yes -t --test -v --verbose \
+ --version' -- $cur ) )
+ else
+ _physicalvolumes
+ fi
+}
+complete -F _pvremove pvremove
+
+_vgscan()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h --help \
+ --ignorelockingfailure --mknodes -P \
+ --partial -v --verbose --version' -- $cur ) )
+ fi
+}
+complete -F _vgscan vgscan
+
+_vgs()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(o|O|-options|-sort))
+ COMPREPLY=( $( compgen -W 'vg_fmt vg_uuid vg_name \
+ vg_attr vg_size vg_free vg_sysid \
+ vg_extent_size vg_extent_count vg_free_count \
+ max_lv max_pv pv_count lv_count snap_count \
+ vg_seqno' -- $cur ) )
+ return 0
+ ;;
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--aligned -d --debug \
+ -h --help --ignorelockingfailure --noheadings \
+ --nosuffix -o --options -O --sort -P --partial \
+ --separator --unbuffered --units \
+ -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgs vgs
+
+_vgdisplay()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c --colon -C --columns --units \
+ -P --partial -A --activevolumegroups -v --verbose \
+ -d --debug -h --help --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgdisplay vgdisplay
+
+_vgchange()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(a|A|x|-available|-autobackup|-resizeable))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --alloc -P \
+ --partial -d --debug -h --help --ignorelockingfailure \
+ -t --test -u --uuid -v --verbose --version -a \
+ --available -x --resizeable -l --logicalvolume \
+ --addtag --deltag' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgchange vgchange
+
+_vgcreate()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ -@(s|-physicalextentsize))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --addtag \
+ --alloc -d --debug -h --help -l --maxlogicalvolumes \
+ -M --metadatatype -p --maxphysicalvolumes -s \
+ --physicalextentsize -t --test -v --verbose \
+ --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _vgcreate vgcreate
+
+_vgremove()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h --help -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgremove vgremove
+
+_vgrename()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -h \
+ -? --help -t --test -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgrename vgrename
+
+_vgreduce()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a --all -A --autobackup -d \
+ --debug -h --help --removemissing -t --test -v \
+ --verbose --version' -- $cur ) )
+
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _vgreduce vgreduce
+
+_vgextend()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -h \
+ -? --help -t --test -v --verbose --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _vgextend vgextend
+
+_vgport()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a --all -d --debug -h \
+ -? --help -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgport vgimport vgexport
+
+_vgck()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h \
+ -? --help -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgck vgck
+
+_vgconvert()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ --metadatacopies)
+ COMPREPLY=( $( compgen -W '0 1 2' -- $cur ) )
+ return 0
+ ;;
+ --metadatasize)
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h --help --labelsector \
+ -M --metadatatype --metadatacopies --metadatasize \
+ -t --test -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgconvert vgconvert
+
+_vgcfgbackup()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(f|-file))
+ _filedir
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -f --file -h --help \
+ --ignorelockingfailure -P --partial -v --verbose \
+ --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgcfgbackup vgcfgbackup
+
+_vgcfgrestore()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(f|-file))
+ _filedir
+ return 0
+ ;;
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ -@(n|-name))
+ _volumegroups
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -f --file -l --list \
+ -h --help -M --Metadatatype -n --name -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgcfgrestore vgcfgrestore
+
+_vgmerge()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug \
+ -h --help -l --list -t --test -v --verbose \
+ --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgmerge vgmerge
+
+_vgsplit()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(M|-metadatatype))
+ COMPREPLY=( $( compgen -W '1 2' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug \
+ -h --help -l --list -M --metadatatype -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 -o $args -eq 1 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _vgsplit vgsplit
+
+_vgmknodes()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-d --debug -h --help -v --verbose \
+ --version' -- $cur ) )
+ else
+ _volumegroups
+ fi
+}
+complete -F _vgmknodes vgmknodes
+
+_lvscan()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-b --blockdevice -d --debug \
+ -h -? --help --ignorelockingfailure -P \
+ --partial -v --verbose --version' -- $cur ) )
+ fi
+}
+complete -F _lvscan lvscan
+
+_lvs()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(o|O|-options|-sort))
+ COMPREPLY=( $( compgen -W 'lv_uuid lv_name \
+ lv_attr lv_minor lv_size seg_count \
+ origin snap_percent segtype stripes \
+ stripesize chunksize seg_start \
+ seg_size' -- $cur ) )
+ return 0
+ ;;
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--aligned -d --debug \
+ -h --help --ignorelockingfailure --noheadings \
+ --nosuffix -o --options -O --sort -P --partial \
+ --segments --separator --unbuffered --units \
+ -v --verbose --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvs lvs
+
+_lvdisplay()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ --units)
+ _units
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-c --colon -C --columns --units \
+ -P --partial -m --maps -v --verbose -d --debug -h \
+ --help --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvdisplay lvdisplay
+
+_lvchange()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(a|A|C|M|-available|-autobackup|-continguous|-persistent))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(p|-permission))
+ COMPREPLY=( $( compgen -W 'r rw' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -a --available \
+ --addtag --alloc -C --contiguous -d --debug --deltag \
+ -f --force -h --help --ignorelockingfailure -M \
+ --persistent --major major --minor minor -P --partial \
+ -p --permission -r --readahead --refresh -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvchange lvchange
+
+_lvcreate()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|C|M|Z|-autobackup|-continguous|-persistent|-zero))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ -@(p|-permission))
+ COMPREPLY=( $( compgen -W 'r rw' -- $cur ) )
+ return 0
+ ;;
+ -@(n|-name))
+ _logicalvolumes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --addtag --alloc \
+ -C --contiguous -d --debug -h -? --help -i --stripes \
+ -I --stripesize -l --extents -L --size -M --persistent \
+ --major --minor -n --name -p --permission -r \
+ --readahead -t --test --type -v --verbose -Z --zero \
+ --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _volumegroups
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _lvcreate lvcreate
+
+_lvremove()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -f \
+ --force -h -? --help -t --test -v --verbose \
+ --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvremove lvremove
+
+_lvrename()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -h \
+ -? --help -t --test -v --verbose --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvrename lvrename
+
+_lvreduce()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup -d \
+ --debug -f --force -h --help -l --extents \
+ -L --size -n --nofsck -r --resizefs -t --test \
+ -v --verbose --version' -- $cur ) )
+ else
+ _logicalvolumes
+ fi
+}
+complete -F _lvreduce lvreduce
+
+_lvresize()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --alloc -d \
+ --debug -h --help -i --stripes -I --stripesize \
+ -l --extents -L --size -n --nofsck -r --resizefs \
+ -t --test --type -v --verbose --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _logicalvolumes
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _lvresize lvresize
+
+_lvextend()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -@(A|-autobackup))
+ COMPREPLY=( $( compgen -W 'y n' -- $cur ) )
+ return 0
+ ;;
+ -@(L|-size))
+ _sizes
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-A --autobackup --alloc -d \
+ --debug -h --help -i --stripes -I --stripesize \
+ -l --extents -L --size -n --nofsck -r --resizefs \
+ -t --test --type -v --verbose --version' -- $cur ) )
+ else
+ _args
+ if [ $args -eq 0 ]; then
+ _logicalvolumes
+ else
+ _physicalvolumes
+ fi
+ fi
+}
+complete -F _lvextend lvextend
+
+_lvm()
+{
+ local prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'dumpconfig help lvchange \
+ lvcreate lvdisplay lvextend lvmchange \
+ lvmdiskscan lvmsadc lvmsar lvreduce \
+ lvremove lvrename lvresize lvs lvscan \
+ pvchange pvcreate pvdata pvdisplay pvmove \
+ pvremove pvresize pvs pvscan vgcfgbackup \
+ vgcfgrestore vgchange vgck vgconvert \
+ vgcreate vgdisplay vgexport vgextend \
+ vgimport vgmerge vgmknodes vgreduce \
+ vgremove vgrename vgs vgscan vgsplit \
+ version' -- $cur ) )
+ else
+ case ${COMP_WORDS[1]} in
+ pvchange)
+ _pvchange
+ ;;
+ pvcreate)
+ _pvcreate
+ ;;
+ pvdisplay)
+ _pvdisplay
+ ;;
+ pvmove)
+ _pvmove
+ ;;
+ pvremove)
+ _pvremove
+ ;;
+ pvresize)
+ _pvresize
+ ;;
+ pvs)
+ _pvs
+ ;;
+ pvscan)
+ _pvscan
+ ;;
+ vgcfgbackup)
+ _vgcfgbackup
+ ;;
+ vgcfgrestore)
+ _vgcfgrestore
+ ;;
+ vgchange)
+ _vgchange
+ ;;
+ vgck)
+ _vgck
+ ;;
+ vgconvert)
+ _vgconvert
+ ;;
+ vgcreate)
+ _vgcreate
+ ;;
+ vgdisplay)
+ _vgdisplay
+ ;;
+ vgexport)
+ _vgexport
+ ;;
+ vgextend)
+ _vgextend
+ ;;
+ vgimport)
+ _vgimport
+ ;;
+ vgmerge)
+ _vgmerge
+ ;;
+ vgmknodes)
+ _vgmknodes
+ ;;
+ vgreduce)
+ _vgreduce
+ ;;
+ vgremove)
+ _vgremove
+ ;;
+ vgrename)
+ _vgrename
+ ;;
+ vgs)
+ _vgs
+ ;;
+ vgscan)
+ _vgscan
+ ;;
+ vgsplit)
+ _vgsplit
+ ;;
+ lvchange)
+ _lvchange
+ ;;
+ lvcreate)
+ _lvcreate
+ ;;
+ lvdisplay)
+ _lvdisplay
+ ;;
+ lvextend)
+ _lvextend
+ ;;
+ lvreduce)
+ _lvreduce
+ ;;
+ lvremove)
+ _lvremove
+ ;;
+ lvrename)
+ _lvrename
+ ;;
+ lvresize)
+ _lvresize
+ ;;
+ lvs)
+ _lvs
+ ;;
+ lvscan)
+ _lvscan
+ ;;
+ esac
+ fi
+}
+complete -F _lvm lvm
+}
+
+# mkinitrd(8) completion
+#
+have mkinitrd &&
+_mkinitrd()
+{
+ local cur args
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case "$prev" in
+ --preload)
+ _modules
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --@(with|builtin))
+ _modules
+ return 0
+ ;;
+ --@(fstab|dsdt))
+ _filedir
+ return 0
+ ;;
+ --tmpdir)
+ _filedir -d
+ return 0
+ ;;
+ esac
+ fi
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--version -v -f --preload \
+ --with= --omit-scsi-modules --omit-raid-modules \
+ --images-version --fstab= --nocompress --builtin= \
+ --nopivot --noudev --allow-missing --tmpdir= \
+ --initrdfs= --dsdt= --lvm-version= --froce-usb' \
+ -- $cur ) )
+ else
+ _count_args
+
+ case $args in
+ 1)
+ _filedir
+ ;;
+ 2)
+ COMPREPLY=( $( command ls /lib/modules | grep "^$cur" ) )
+ ;;
+ esac
+ fi
+
+} &&
+complete -F _mkinitrd mkinitrd
+
+# pkgconfig(1) completion
+#
+have pkg-config &&
+_pkg_config()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ # return list of available options
+ COMPREPLY=( $( compgen -W '-version --modversion \
+ --atleast-pkgconfig-version= --libs --libs-only-l \
+ --libs-only-other --libs-only-L --cflags \
+ --cflags-only-I --cflags-only-other --variable= \
+ --define-variable= --exists --uninstalled \
+ --atleast-version= --exact-version= --max-version= \
+ --list-all --debug --print-errors --silence-errors \
+ --errors-to-stdout -? --help --usage' -- $cur))
+ else
+ COMPREPLY=( $( pkg-config --list-all 2>/dev/null | \
+ awk '{print $1}' | grep "^$cur" ) )
+ fi
+} &&
+complete -F _pkg_config pkg-config
+
+
+# cpio(1) completion
+#
+have cpio && {
+_cpio_format()
+{
+ COMPREPLY=( $( compgen -W 'bin odc newc crc tar ustar hpbin hpodc' -- $cur ) )
+}
+
+_cpio()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case $prev in
+ -H)
+ _cpio_format
+ return 0
+ ;;
+ -@(E|F|I))
+ _filedir
+ return 0
+ ;;
+ -R)
+ _usergroup
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case $prev in
+ --format)
+ _cpio_format
+ return 0
+ ;;
+ --@(file|pattern-file))
+ _filedir
+ return 0
+ ;;
+ --owner)
+ _usergroup
+ return 0
+ ;;
+ --rsh-command)
+ COMPREPLY=( $( compgen -c -- $cur ) )
+ return 0
+ ;;
+ esac
+ fi
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W '-o --create -i --extract -p --pass-through' -- $cur) )
+ else
+ case ${COMP_WORDS[1]} in
+ -@(o|-create))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-0 -a -c -v -A -B\
+ -L -V -C -H -M -O -F --file= --format=\
+ --message= --null --reset-access-time\
+ --verbose --dot --append --block-size=\
+ --dereference --io-size= --quiet\
+ --force-local --rsh-command= --help\
+ --version' -- $cur ) )
+ fi
+ ;;
+ -@(i|-extract))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-b -c -d -f -m -n -r\
+ -t -s -u -v -B -S -V -C -E -H -M -R -I\
+ -F --file= --make-directories\
+ --nonmatching\
+ --preserve-modification-time\
+ --numeric-uid-gid --rename -t --list\
+ --swap-bytes --swap --dot\
+ --unconditional --verbose --block-size=\
+ --swap-halfwords --io-size=\
+ --pattern-file= --format= --owner=\
+ --no-preserve-owner --message=\
+ --force-local --no-absolute-filenames\
+ --sparse --only-verify-crc --quiet\
+ --rsh-command= --help\
+ --version' -- $cur ) )
+ fi
+ ;;
+ -@(p|-pass-through))
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-0 -a -d -l -m -u -v\
+ -L -V -R --null --reset-access-time\
+ --make-directories --link --quiet\
+ --preserve-modification-time\
+ --unconditional --verbose --dot\
+ --dereference --owner=\
+ --no-preserve-owner --sparse --help\
+ --version' -- $cur ) )
+ else
+ _filedir -d
+ fi
+ ;;
+ esac
+ fi
+}
+complete -F _cpio cpio
+}
+
+# id(1) completion
+#
+have id &&
+_id()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-a -g --group -G --groups -n --name\
+ -r --real -u --user --help --version' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -u $cur ) )
+ fi
+} &&
+complete -F _id id
+
+# getent(1) completion
+#
+have getent &&
+_getent()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ passwd)
+ COMPREPLY=( $( compgen -u $cur ) )
+ return 0
+ ;;
+ group)
+ COMPREPLY=( $( compgen -g $cur ) )
+ return 0
+ ;;
+ services)
+ COMPREPLY=( $( compgen -s $cur ) )
+ return 0
+ ;;
+ hosts)
+ COMPREPLY=( $( compgen -A hostname $cur ) )
+ return 0
+ ;;
+ protocols)
+ COMPREPLY=( $( getent protocols | awk '{print $1}' | grep "^$cur" ) )
+ return 0
+ ;;
+ networks)
+ COMPREPLY=( $( getent networks | awk '{print $1}' | grep "^$cur" ) )
+ return 0
+ ;;
+ esac
+
+
+ if [ $COMP_CWORD -eq 1 ]; then
+ COMPREPLY=( $( compgen -W 'passwd group hosts services protocols networks' -- $cur ) )
+ fi
+} &&
+complete -F _getent getent
+
+# ntpdate(1) completion
+#
+have ntpdate &&
+_ntpdate()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -k)
+ _filedir
+ return 0
+ ;;
+ -U)
+ COMPREPLY=( $( compgen -u $cur ) )
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-4 -6 -b -B -d -Q -q -s -u -v -a\
+ -e -k -p -o -r -t' -- $cur ) )
+ else
+ _known_hosts
+ fi
+} &&
+complete -F _ntpdate ntpdate
+
+# smartctl(8) completion
+#
+have smartctl && {
+_smartctl_quietmode()
+{
+ COMPREPLY=( $( compgen -W 'errorsonly silent' -- $cur ) )
+}
+_smartctl_device()
+{
+ COMPREPLY=( $( compgen -W 'ata scsi 3ware' -- $cur ) )
+}
+_smartctl_tolerance()
+{
+ COMPREPLY=( $( compgen -W 'warn exit ignore' -- $cur ) )
+}
+_smartctl_badsum()
+{
+ COMPREPLY=( $( compgen -W 'normal conservative permissive verypermissive' -- $cur ) )
+}
+_smartctl_report()
+{
+ COMPREPLY=( $( compgen -W 'ioctl ataioctl scsiioctl' -- $cur ) )
+}
+_smartctl_feature()
+{
+ COMPREPLY=( $( compgen -W 'on off' -- $cur ) )
+}
+_smartctl_log()
+{
+ COMPREPLY=( $( compgen -W 'error selftest selective directory' -- $cur ) )
+}
+_smartctl_vendorattribute()
+{
+ COMPREPLY=( $( compgen -W 'help 9,minutes 9,seconds 9,halfminutes \
+ 9,temp 192,emergencyretractcyclect 193,loadunload \
+ 194,10xCelsius 194,unknown 198,offlinescanuncsectorct \
+ 200,writeerrorcount 201,detectedtacount 220,temp' -- $cur ) )
+}
+_smartctl_firmwarebug()
+{
+ COMPREPLY=( $( compgen -W 'none samsung samsung2' -- $cur ) )
+}
+_smartctl_presets()
+{
+ COMPREPLY=( $( compgen -W 'use ignore show showall' -- $cur ) )
+}
+_smartctl_test()
+{
+ COMPREPLY=( $( compgen -W 'offline short long conveyance select afterselect,on afterselect,off pending' -- $cur ) )
+}
+
+_smartctl()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ # --name value style option
+ case "$prev" in
+ -q)
+ _smartctl_quietmode
+ ;;
+ -d)
+ _smartctl_device
+ return 0
+ ;;
+ -t)
+ _smartctl_tolerance
+ return 0
+ ;;
+ -b)
+ _smartctl_badsum
+ return 0
+ ;;
+ -r)
+ _smartctl_report
+ return 0
+ ;;
+ -s)
+ _smartctl_feature
+ return 0
+ ;;
+ -o)
+ _smartctl_feature
+ return 0
+ ;;
+ -S)
+ _smartctl_feature
+ return 0
+ ;;
+ -l)
+ _smartctl_log
+ return 0
+ ;;
+ -v)
+ _smartctl_vendorattribute
+ return 0
+ ;;
+ -F)
+ _smartctl_firmwarebug
+ return 0
+ ;;
+ -P)
+ _smartctl_presets
+ return 0
+ ;;
+ -t)
+ _smartctl_test
+ return 0
+ ;;
+ esac
+
+ # --name=value style option
+ if [[ "$cur" == *=* ]]; then
+ prev=${cur/=*/}
+ cur=${cur/*=/}
+ case "$prev" in
+ --quietmode)
+ _smartctl_quietmode
+ return 0
+ ;;
+ --device)
+ _smartctl_device
+ return 0
+ ;;
+ --tolerance)
+ _smartctl_tolerance
+ return 0
+ ;;
+ --badsum)
+ _smartctl_badsum
+ return 0
+ ;;
+ --report)
+ _smartctl_report
+ return 0
+ ;;
+ --smart)
+ _smartctl_feature
+ return 0
+ ;;
+ --offlineauto)
+ _smartctl_feature
+ return 0
+ ;;
+ --saveauto)
+ _smartctl_feature
+ return 0
+ ;;
+ --log)
+ _smartctl_log
+ return 0
+ ;;
+ --vendorattribute)
+ _smartctl_vendorattribute
+ return 0
+ ;;
+ --firmwarebug)
+ _smartctl_firmwarebug
+ return 0
+ ;;
+ --presets)
+ _smartctl_presets
+ return 0
+ ;;
+ --test)
+ _smartctl_test
+ return 0
+ ;;
+ esac
+ fi
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-h --help --usage -V --version \
+ --copyright --license-i --info -a --all -q \
+ --quietmode= -d --device= -T --tolerance= -b --badsum= \
+ -r --report= -s --smart= -o --offlineauto= -S \
+ --saveauto= -H --health -c --capabilities -A \
+ --attributes -l --log= -v --vendorattribute= -F \
+ --firmwarebug= -P --presets= -t --test= -C \
+ --captive -X --abort' -- $cur ) )
+ else
+ cur=${cur:=/dev/}
+ _filedir
+ fi
+}
+complete -F _smartctl smartctl
+}
+
+# vncviewer(1) completion
+#
+have vncviewer &&
+_vncviewer()
+{
+ local cur prev
+ local -a config
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case "$prev" in
+ -via)
+ _known_hosts -a
+ ;;
+ *)
+ # ssh into the the server, find and ping the broadcast address, then
+ # sort and show the results.
+ COMPREPLY=( $( ssh -o 'Batchmode yes' $prev \
+ "ping -bnc 4 255.255.255.255" 2>/dev/null | \
+ awk -F ' ' '{print $4}' | \
+ sort -n | uniq | egrep '[0-9]+\.[0-9]+\.' 2>/dev/null ) )
+ esac
+
+ return 0
+} &&
+complete -F _vncviewer vncviewer
+
+# sysctl(8) completion
+#
+have sysctl &&
+_sysctl()
+{
+ local cur
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ COMPREPLY=( $( compgen -W "$(sysctl -N -a 2>/dev/null)" -- $cur ) )
+
+ return 0
+} &&
+complete -F _sysctl sysctl
+
+# update-rc.d(8) completion
+#
+# Copyright (C) 2004 Servilio Afre Puentes <servilio@gmail.com>
+#
+have update-rc.d &&
+_update_rc_d()
+{
+ local cur prev sysvdir services options valid_options
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \
+ || sysvdir=/etc/init.d
+
+ services=( $(echo $sysvdir/!(README*|*.sh|*.dpkg*|*.rpm*)) )
+ services=( ${services[@]#$sysvdir/} )
+ options=( -f -n )
+
+ if [[ $COMP_CWORD -eq 1 || "$prev" == -* ]]; then
+ valid_options=( $( \
+ echo "${COMP_WORDS[@]} ${options[@]}" \
+ | tr " " "\n" \
+ | sed -ne "/$( echo "${options[@]}" | sed "s/ /\\|/g" )/p" \
+ | sort | uniq -u \
+ ) )
+ COMPREPLY=( $( compgen -W '${options[@]} ${services[@]}' \
+ -X '$( echo ${COMP_WORDS[@]} | tr " " "|" )' -- $cur ) )
+ elif [[ "$prev" == ?($( echo ${services[@]} | tr " " "|" )) ]]; then
+ COMPREPLY=( $( compgen -W 'remove defaults start stop' -- $cur ) )
+ elif [[ "$prev" == defaults && "$cur" == [0-9] ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 )
+ elif [[ "$prev" == defaults && "$cur" == [sk]?([0-9]) ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 )
+ elif [[ "$prev" == defaults && -z "$cur" ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 s k )
+ elif [[ "$prev" == ?(start|stop) ]]; then
+ if [[ "$cur" == [0-9] || -z "$cur" ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 )
+ elif [[ "$cur" == [0-9][0-9] ]]; then
+ COMPREPLY=( $cur )
+ else
+ COMPREPLY=()
+ fi
+ elif [[ "$prev" == ?([0-9][0-9]|[0-6S]) ]]; then
+ if [[ -z "$cur" ]]; then
+ if [[ $prev == [0-9][0-9] ]]; then
+ COMPREPLY=( 0 1 2 3 4 5 6 S )
+ else
+ COMPREPLY=( 0 1 2 3 4 5 6 S . )
+ fi
+ elif [[ "$cur" == [0-6S.] ]]; then
+ COMPREPLY=( $cur )
+ else
+ COMPREPLY=()
+ fi
+ elif [[ "$prev" == "." ]]; then
+ COMPREPLY=( $(compgen -W "start stop" -- $cur) )
+ else
+ COMPREPLY=()
+ fi
+
+ return 0
+} &&
+complete -F _update_rc_d update-rc.d
+
+# invoke-rc.d(8) completion
+#
+# Copyright (C) 2004 Servilio Afre Puentes <servilio@gmail.com>
+#
+have invoke-rc.d &&
+_invoke_rc_d()
+{
+ local cur prev sysvdir services options valid_options
+
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \
+ || sysvdir=/etc/init.d
+
+ services=( $(echo $sysvdir/!(README*|*.sh|*.dpkg*|*.rpm*)) )
+ services=( ${services[@]#$sysvdir/} )
+ options=( --help --quiet --force --try-anyway --disclose-deny --query --no-fallback )
+
+ if [[ ($COMP_CWORD -eq 1) || ("$prev" == --* ) ]]; then
+ valid_options=( $( \
+ echo ${COMP_WORDS[@]} ${options[@]} \
+ | tr " " "\n" \
+ | sed -ne "/$( echo ${options[@]} | sed "s/ /\\\\|/g" )/p" \
+ | sort | uniq -u \
+ ) )
+ COMPREPLY=( $( compgen -W '${valid_options[@]} ${services[@]}' -- \
+ $cur ) )
+ elif [ -x $sysvdir/$prev ]; then
+ COMPREPLY=( $( compgen -W '`sed -ne "y/|/ /; \
+ s/^.*Usage:[ ]*[^ ]*[ ]*{*\([^}\"]*\).*$/\1/p" \
+ $sysvdir/$prev`' -- \
+ $cur ) )
+ else
+ COMPREPLY=()
+ fi
+
+ return 0
+} &&
+complete -F _invoke_rc_d invoke-rc.d
+
+# minicom(1) completion
+#
+have minicom &&
+_minicom()
+{
+ local cur prev
+
+ COMPREPLY=()
+ cur=`_get_cword`
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+
+ case $prev in
+ -@(a|c))
+ COMPREPLY=( $( compgen -W 'on off' -- $cur ) )
+ return 0
+ ;;
+ -@(S|C))
+ _filedir
+ return 0
+ ;;
+ -P)
+ COMPREPLY=( $( command ls /dev/tty* ) )
+ COMPREPLY=( $( compgen -W '${COMPREPLY[@]} ${COMPREPLY[@]#/dev/}' -- $cur ) )
+ return 0
+ ;;
+ esac
+
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '-s -o -m -M -z -l -L -w -a -t \
+ -c -S -d -p -C -T -8' -- $cur ) )
+ else
+ COMPREPLY=( $( command ls /etc/minicom/minirc.* 2>/dev/null | sed -e 's|/etc/minicom/minirc.||' | grep "^$cur" ) )
+ fi
+} &&
+complete -F _minicom minicom
+
+# svn completion
+#
+have svn &&
+{
+_svn()
+{
+ local cur prev commands options command
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ commands='add blame praise annotate ann cat checkout co cleanup commit \
+ ci copy cp delete del remove rm diff di export help ? h import \
+ info list ls lock log merge mkdir move mv rename ren \
+ propdel pdel pd propedit pedit pe propget pget pg \
+ proplist plist pl propset pset ps resolved revert \
+ status stat st switch sw unlock update up'
+
+ if [[ $COMP_CWORD -eq 1 ]] ; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--version' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ fi
+ else
+
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ case $prev in
+ --config-dir)
+ _filedir -d
+ return 0;
+ ;;
+ -@(F|-file|-targets))
+ _filedir
+ return 0;
+ ;;
+ --encoding)
+ COMPREPLY=( $( compgen -W \
+ '$( iconv --list | sed -e "s@//@@;" )' \
+ -- "$cur" ) )
+ return 0;
+ ;;
+ --@(editor|diff|diff3)-cmd)
+ COMP_WORDS=(COMP_WORDS[0] $cur)
+ COMP_CWORD=1
+ _command
+ return 0;
+ ;;
+ esac
+
+ command=${COMP_WORDS[1]}
+
+ if [[ "$cur" == -* ]]; then
+ # possible options for the command
+ case $command in
+ add)
+ options='--auto-props --no-auto-props \
+ --force --targets --no-ignore \
+ --non-recursive -N -q --quiet'
+ ;;
+ @(blame|annotate|ann|praise))
+ options='-r --revisions --username \
+ --password --no-auth-cache \
+ --non-interactive -v \
+ --verbose --incremental --xml'
+ ;;
+ cat)
+ options='-r --revision --username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(checkout|co))
+ options='-r --revision -q --quiet -N \
+ --non-recursive --username \
+ --password --no-auth-cache \
+ --non-interactive \
+ --ignore-externals'
+ ;;
+ cleanup)
+ options='--diff3-cmd'
+ ;;
+ @(commit|ci))
+ options='-m --message -F --file \
+ --encoding --force-log -q \
+ --quiet --non-recursive -N \
+ --targets --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive --no-unlock'
+ ;;
+ @(copy|cp))
+ options='-m --message -F --file \
+ --encoding --force-log -r \
+ --revision -q --quiet \
+ --editor-cmd -username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(delete|del|remove|rm))
+ options='--force -m --message -F \
+ --file --encoding --force-log \
+ -q --quiet --targets \
+ --editor-cmd -username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(diff|di))
+ options='-r --revision -x --extensions \
+ --diff-cmd --no-diff-deleted \
+ -N --non-recursive --username \
+ --password --no-auth-cache \
+ --non-interactive --force \
+ --old --new --notice-ancestry'
+ ;;
+ export)
+ options='-r --revision -q --quiet \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive -N \
+ --non-recursive --force \
+ --native-eol --ignore-externals'
+ ;;
+ import)
+ options='--auto-props --no-auto-props \
+ -m --message -F --file \
+ --encoding --force-log -q \
+ --quiet --non-recursive \
+ --no-ignore --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ info)
+ options='--username --password \
+ --no-auth-cache \
+ --non-interactive -r \
+ --revision --xml --targets \
+ -R --recursive --incremental'
+ ;;
+ @(list|ls))
+ options='-r --revision -v --verbose -R \
+ --recursive --username \
+ --password --no-auth-cache \
+ --non-interactive \
+ --incremental --xml'
+ ;;
+ lock)
+ options='-m --message -F --file \
+ --encoding --force-log \
+ --targets --force --username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ log)
+ options='-r --revision -v --verbose \
+ --targets --username \
+ --password --no-auth-cache \
+ --non-interactive \
+ --stop-on-copy --incremental \
+ --xml -q --quiet --limit'
+ ;;
+ merge)
+ options='-r --revision -N \
+ --non-recursive -q --quiet \
+ --force --dry-run --diff3-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive \
+ --ignore-ancestry'
+ ;;
+ mkdir)
+ options='-m --message -F --file \
+ --encoding --force-log -q \
+ --quiet --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(move|mv|rename|ren))
+ options='-m --message -F --file \
+ --encoding --force-log -r \
+ --revision -q --quiet \
+ --force --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(propdel|pdel|pd))
+ options='-q --quiet -R --recursive -r \
+ --revision --revprop \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(propedit|pedit|pe))
+ options='-r --revision --revprop \
+ --encoding --editor-cmd \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive --force'
+ ;;
+ @(propget|pget|pg))
+ options='-R --recursive -r --revision \
+ --revprop --strict --username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(proplist|plist|pl))
+ options='-v --verbose -R --recursive \
+ -r --revision --revprop -q \
+ --quiet --username --password \
+ --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(propset|pset|ps))
+ options='-F --file -q --quiet \
+ --targets -R --recursive \
+ --revprop --encoding \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive -r \
+ --revision --force'
+ ;;
+ resolved)
+ options='--targets -R --recursive -q \
+ --quiet'
+ ;;
+ revert)
+ options='--targets -R --recursive -q \
+ --quiet'
+ ;;
+ @(status|stat|st))
+ options='-u --show-updates -v \
+ --verbose -N --non-recursive \
+ -q --quiet --username \
+ --password --no-auth-cache \
+ --non-interactive --no-ignore \
+ --ignore-externals \
+ --incremental --xml'
+ ;;
+ @(switch|sw))
+ options='--relocate -r --revision -N \
+ --non-recursive -q --quiet \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive --diff3-cmd'
+ ;;
+ unlock)
+ options='--targets --force --username \
+ --password --no-auth-cache \
+ --non-interactive'
+ ;;
+ @(update|up))
+ options='-r --revision -N \
+ --non-recursive -q --quiet \
+ --username --password \
+ --no-auth-cache \
+ --non-interactive \
+ --diff3-cmd --ignore-externals'
+ ;;
+ esac
+ options="$options --help -h --config-dir"
+
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ else
+ if [[ "$command" == @(help|h|\?) ]]; then
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ else
+ _filedir
+ fi
+ fi
+ fi
+
+ return 0
+}
+complete -F _svn $default svn
+
+_svnadmin()
+{
+ local cur prev commands options mode
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ commands='create deltify dump help ? hotcopy list-dblogs \
+ list-unused-dblogs load lslocks lstxns recover rmlocks \
+ rmtxns setlog verify'
+
+ if [[ $COMP_CWORD -eq 1 ]] ; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--version' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ fi
+ else
+ prev=${COMP_WORDS[COMP_CWORD-1]}
+ case $prev in
+ --config-dir)
+ _filedir -d
+ return 0;
+ ;;
+ --fs-type)
+ COMPREPLY=( $( compgen -W 'fsfs bdb' -- $cur ) )
+ return 0;
+ ;;
+ esac
+
+ command=${COMP_WORDS[1]}
+
+ if [[ "$cur" == -* ]]; then
+ # possible options for the command
+ case $command in
+ create)
+ options='--bdb-txn-nosync \
+ --bdb-log-keep --config-dir \
+ --fs-type'
+ ;;
+ deltify)
+ options='-r --revision -q --quiet'
+ ;;
+ dump)
+ options='-r --revision --incremental \
+ -q --quiet --deltas'
+ ;;
+ hotcopy)
+ options='--clean-logs'
+ ;;
+ load)
+ options='--ignore-uuid --force-uuid \
+ --parent-dir -q --quiet \
+ --use-pre-commit-hook \
+ --use-post-commit-hook'
+ ;;
+ rmtxns)
+ options='-q --quiet'
+ ;;
+ setlog)
+ options='-r --revision --bypass-hooks'
+ ;;
+ esac
+
+ options="$options --help -h"
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ else
+ if [[ "$command" == @(help|h|\?) ]]; then
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ else
+ _filedir
+ fi
+ fi
+ fi
+
+ return 0
+}
+complete -F _svnadmin $default svnadmin
+
+_svnlook()
+{
+ local cur prev commands options mode
+
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ commands='author cat changed date diff dirs-changed help ? h history \
+ info lock log propget pget pg proplist plist pl tree uuid \
+ youngest'
+
+ if [[ $COMP_CWORD -eq 1 ]] ; then
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--version' -- $cur ) )
+ else
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ fi
+ else
+ command=${COMP_WORDS[1]}
+
+ if [[ "$cur" == -* ]]; then
+ # possible options for the command
+ case $command in
+ @(author|cat|date|dirs-changed|info|log))
+ options='-r --revision -t \
+ --transaction'
+ ;;
+ changed)
+ options='-r --revision -t \
+ --transaction --copy-info'
+ ;;
+ diff)
+ options='-r --revision -t \
+ --transaction \
+ --no-diff-deleted \
+ --no-diff-added \
+ --diff-copy-from'
+ ;;
+ history)
+ options='-r --revision --show-ids'
+ ;;
+ prop@(get|list))
+ options='-r --revision -t \
+ --transaction --revprop'
+ ;;
+ tree)
+ options='-r --revision -t \
+ --transaction --show-ids \
+ --full-paths'
+ ;;
+ esac
+
+ options="$options --help -h"
+ COMPREPLY=( $( compgen -W "$options" -- $cur ) )
+ else
+ if [[ "$command" == @(help|h|\?) ]]; then
+ COMPREPLY=( $( compgen -W "$commands" -- $cur ) )
+ else
+ _filedir
+ fi
+ fi
+ fi
+
+ return 0
+}
+complete -F _svnlook $default svnlook
+}
+
+_filedir_xspec()
+{
+ local IFS cur xspec
+
+ IFS=$'\t\n'
+ COMPREPLY=()
+ cur=`_get_cword`
+
+ _expand || return 0
+
+ # get first exclusion compspec that matches this command
+ xspec=$( sed -ne $'/^complete .*[ \t]'${1##*/}$'\([ \t]\|$\)/{p;q;}' \
+ $BASH_COMPLETION )
+ # prune to leave nothing but the -X spec
+ xspec=${xspec#*-X }
+ xspec=${xspec%% *}
+
+ local toks=( ) tmp
+
+ while read -r tmp; do
+ [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp
+ done < <( compgen -d -- "$(quote_readline "$cur")" )
+
+ while read -r tmp; do
+ [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp
+ done < <( eval compgen -f -X $xspec -- "\$(quote_readline "\$cur")" )
+
+ COMPREPLY=( "${toks[@]}" )
+}
+list=( $( sed -ne '/^# START exclude/,/^# FINISH exclude/p' \
+ $BASH_COMPLETION | \
+ # read exclusion compspecs
+ (
+ while read line
+ do
+ # ignore compspecs that are commented out
+ if [ "${line#\#}" != "$line" ]; then continue; fi
+ line=${line%# START exclude*}
+ line=${line%# FINISH exclude*}
+ line=${line##*\'}
+ list=( "${list[@]}" $line )
+ done
+ echo "${list[@]}"
+ )
+ ) )
+# remove previous compspecs
+if [ ${#list[@]} -gt 0 ]; then
+ eval complete -r ${list[@]}
+ # install new compspecs
+ eval complete -F _filedir_xspec $filenames "${list[@]}"
+fi
+unset list
+
+# source completion directory definitions
+if [ -d $BASH_COMPLETION_DIR -a -r $BASH_COMPLETION_DIR -a \
+ -x $BASH_COMPLETION_DIR ]; then
+ for i in $BASH_COMPLETION_DIR/*; do
+ [[ ${i##*/} != @(*~|*.bak|*.swp|\#*\#|*.dpkg*|.rpm*) ]] &&
+ [ \( -f $i -o -h $i \) -a -r $i ] && . $i
+ done
+fi
+unset i
+
+# source user completion file
+[ $BASH_COMPLETION != ~/.bash_completion -a -r ~/.bash_completion ] \
+ && . ~/.bash_completion
+unset -f have
+unset UNAME RELEASE default dirnames filenames have nospace bashdefault \
+ plusdirs
+
+set $BASH_COMPLETION_ORIGINAL_V_VALUE
+unset BASH_COMPLETION_ORIGINAL_V_VALUE
+
+### Local Variables:
+### mode: shell-script
+### End:
diff --git a/examples/complete/bashcc-1.0.1.tar.gz b/examples/complete/bashcc-1.0.1.tar.gz
new file mode 100644
index 0000000..d680435
--- /dev/null
+++ b/examples/complete/bashcc-1.0.1.tar.gz
Binary files differ
diff --git a/examples/complete/cdfunc b/examples/complete/cdfunc
new file mode 100644
index 0000000..439edaf
--- /dev/null
+++ b/examples/complete/cdfunc
@@ -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
diff --git a/examples/complete/complete-examples b/examples/complete/complete-examples
new file mode 100644
index 0000000..8a0746d
--- /dev/null
+++ b/examples/complete/complete-examples
@@ -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
diff --git a/examples/functions/array-stuff b/examples/functions/array-stuff
new file mode 100644
index 0000000..e6316c7
--- /dev/null
+++ b/examples/functions/array-stuff
@@ -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[@]}"
diff --git a/examples/functions/array-to-string b/examples/functions/array-to-string
new file mode 100644
index 0000000..0d2fbe5
--- /dev/null
+++ b/examples/functions/array-to-string
@@ -0,0 +1,15 @@
+#! /bin/bash
+
+# Format: array_to_string vname_of_array vname_of_string separator
+array_to_string()
+{
+ (( ($# < 2) || ($# > 3) )) && {
+ "$FUNCNAME: usage: $FUNCNAME arrayname stringname [separator]"
+ return 2
+ }
+
+ local array=$1 string=$2
+ ((3==$#)) && [[ $3 = ? ]] && local IFS="${3}${IFS}"
+ eval $string="\"\${$array[*]}\""
+ return 0
+}
diff --git a/examples/functions/arrayops.bash b/examples/functions/arrayops.bash
new file mode 100644
index 0000000..d34353a
--- /dev/null
+++ b/examples/functions/arrayops.bash
@@ -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
diff --git a/examples/functions/autoload b/examples/functions/autoload
new file mode 100644
index 0000000..cb3a673
--- /dev/null
+++ b/examples/functions/autoload
@@ -0,0 +1,111 @@
+#
+# An almost ksh-compatible `autoload'. A function declared as `autoload' will
+# be read in from a file the same name as the function found by searching the
+# $FPATH (which works the same as $PATH), then that definition will be run.
+#
+# To do this without source support, we define a dummy function that, when
+# executed, will load the file (thereby re-defining the function), then
+# execute that newly-redefined function with the original arguments.
+#
+# It's not identical to ksh because ksh apparently does lazy evaluation
+# and looks for the file to load from only when the function is referenced.
+# This one requires that the file exist when the function is declared as
+# `autoload'.
+#
+# usage: autoload func [func...]
+#
+# The first cut of this was by Bill Trost, trost@reed.bitnet
+#
+# Chet Ramey
+# chet@ins.CWRU.Edu
+
+#
+# Declare a function ($1) to be autoloaded from a file ($2) when it is first
+# called. This defines a `temporary' function that will `.' the file
+# 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
+}
diff --git a/examples/functions/autoload.v2 b/examples/functions/autoload.v2
new file mode 100644
index 0000000..e29c695
--- /dev/null
+++ b/examples/functions/autoload.v2
@@ -0,0 +1,192 @@
+#
+# An almost ksh-compatible `autoload'. A function declared as `autoload' will
+# be read in from a file the same name as the function found by searching the
+# $FPATH (which works the same as $PATH), then that definition will be run.
+#
+# To do this without source support, we define a dummy function that, when
+# executed, will load the file (thereby re-defining the function), then
+# execute that newly-redefined function with the original arguments.
+#
+# It's not identical to ksh because ksh apparently does lazy evaluation
+# and looks for the file to load from only when the function is referenced.
+# This one requires that the file exist when the function is declared as
+# `autoload'.
+#
+# usage: autoload [-pu] [func ...]
+#
+# options:
+# -p print in a format that can be reused as input
+# -u unset each function and remove it from the autoload list
+#
+# The first cut of this was by Bill Trost, trost@reed.edu
+#
+# Chet Ramey
+# chet@ins.CWRU.Edu
+
+unset _AUTOLOADS
+_aindex=0
+
+#
+# Declare a function ($1) to be autoloaded from a file ($2) when it is first
+# called. This defines a `temporary' function that will `.' the file
+# 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
+}
diff --git a/examples/functions/autoload.v3 b/examples/functions/autoload.v3
new file mode 100644
index 0000000..b1e5dfe
--- /dev/null
+++ b/examples/functions/autoload.v3
@@ -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 "$@"
+}
diff --git a/examples/functions/autoload.v4 b/examples/functions/autoload.v4
new file mode 100644
index 0000000..850c614
--- /dev/null
+++ b/examples/functions/autoload.v4
@@ -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
diff --git a/examples/functions/autoload.v4.t b/examples/functions/autoload.v4.t
new file mode 100644
index 0000000..6d35d14
--- /dev/null
+++ b/examples/functions/autoload.v4.t
@@ -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
diff --git a/examples/functions/basename b/examples/functions/basename
new file mode 100644
index 0000000..a541349
--- /dev/null
+++ b/examples/functions/basename
@@ -0,0 +1,23 @@
+# Date: Fri, 11 Oct 91 11:22:36 edt
+# From: friedman@gnu.ai.mit.edu
+# To: bfox@gnu.ai.mit.edu
+
+# A replacement for basename(1). Not all the systems I use have this
+# program. Usage: basename [path] {extension}
+function basename ()
+{
+ local path="$1"
+ local suffix="$2"
+ local tpath="${path%/}"
+
+ # Strip trailing '/' characters from path (unusual that this should
+ # ever occur, but basename(1) seems to deal with it.)
+ while [ "${tpath}" != "${path}" ]; do
+ tpath="${path}"
+ path="${tpath%/}"
+ done
+
+ path="${path##*/}" # Strip off pathname
+ echo ${path%${suffix}} # Also strip off extension, if any.
+}
+
diff --git a/examples/functions/csh-compat b/examples/functions/csh-compat
new file mode 100644
index 0000000..6671ca8
--- /dev/null
+++ b/examples/functions/csh-compat
@@ -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
+}
diff --git a/examples/functions/dirname b/examples/functions/dirname
new file mode 100644
index 0000000..ccb8c84
--- /dev/null
+++ b/examples/functions/dirname
@@ -0,0 +1,21 @@
+# Date: Fri, 11 Oct 91 11:22:36 edt
+# From: friedman@gnu.ai.mit.edu
+# To: bfox@gnu.ai.mit.edu
+
+# A replacement for dirname(1). This one appears less often on some
+# systems I use than basename(1), and I really depend on it for some
+# things. Usage: dirname [path]
+function dirname ()
+{
+ local dir="$1"
+ local tdir="${dir%/}"
+
+ # Strip trailing '/' characters from dir (unusual that this should
+ # ever occur, but dirname(1) seems to deal with it.)
+ while [ "${tdir}" != "${dir}" ]; do
+ tdir="${dir}"
+ dir="${tdir%/}"
+ done
+
+ echo "${dir%/*}"
+}
diff --git a/examples/functions/dirstack b/examples/functions/dirstack
new file mode 100644
index 0000000..d68e619
--- /dev/null
+++ b/examples/functions/dirstack
@@ -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
+}
+
+
diff --git a/examples/functions/exitstat b/examples/functions/exitstat
new file mode 100644
index 0000000..f49ebf5
--- /dev/null
+++ b/examples/functions/exitstat
@@ -0,0 +1,22 @@
+# Contributed by Noah Friedman and Roland McGrath.
+
+# To be run by the PROMPT_COMMAND variable, so that one can see what
+# the exit status of processes are.
+
+function check_exit_status ()
+{
+ local status="$?"
+ local signal=""
+
+ if [ ${status} -ne 0 ] && [ ${status} != 128 ]; then
+ # If process exited by a signal, determine name of signal.
+ if [ ${status} -gt 128 ]; then
+ signal="$(builtin kill -l $((${status} - 128)) 2>/dev/null)"
+ if [ "$signal" ]; then signal="($signal)"; fi
+ fi
+ echo "[Exit ${status} ${signal}]" 1>&2
+ fi
+ return 0
+}
+
+PROMPT_COMMAND=check_exit_status
diff --git a/examples/functions/external b/examples/functions/external
new file mode 100644
index 0000000..c2e52cd
--- /dev/null
+++ b/examples/functions/external
@@ -0,0 +1,50 @@
+# Contributed by Noah Friedman.
+
+# To avoid using a function in bash, you can use the `builtin' or
+# `command' builtins, but neither guarantees that you use an external
+# program instead of a bash builtin if there's a builtin by that name. So
+# this function can be used like `command' except that it guarantees the
+# program is external by first disabling any builtin by that name. After
+# the command is done executing, the state of the builtin is restored.
+function external ()
+{
+ local state=""
+ local exit_status
+
+ if builtin_p "$1"; then
+ state="builtin"
+ enable -n "$1"
+ fi
+
+ command "$@"
+ exit_status=$?
+
+ if [ "$state" = "builtin" ]; then
+ enable "$1"
+ fi
+
+ return ${exit_status}
+}
+
+# What is does is tell you if a particular keyword is currently enabled as
+# a shell builtin. It does NOT tell you if invoking that keyword will
+# necessarily run the builtin. For that, do something like
+#
+# test "$(builtin type -type [keyword])" = "builtin"
+#
+# Note also, that disabling a builtin with "enable -n" will make builtin_p
+# return false, since the builtin is no longer available.
+function builtin_p ()
+{
+ local word
+
+ set $(builtin type -all -type "$1")
+
+ for word in "$@" ; do
+ if [ "${word}" = "builtin" ]; then
+ return 0
+ fi
+ done
+
+ return 1
+}
diff --git a/examples/functions/fact b/examples/functions/fact
new file mode 100644
index 0000000..97efd49
--- /dev/null
+++ b/examples/functions/fact
@@ -0,0 +1,13 @@
+# Who said shells can't use recursion? Here is a factorial function.
+# You call it with a number as an argument, and it returns the factorial
+# of that number.
+
+fact ()
+{
+ local num=$1;
+ if [ "$num" = 1 ] ; then
+ echo 1
+ return ;
+ fi;
+ echo $(( $num * $(fact $(( $num - 1 )) ) ))
+}
diff --git a/examples/functions/fstty b/examples/functions/fstty
new file mode 100644
index 0000000..5ed594f
--- /dev/null
+++ b/examples/functions/fstty
@@ -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
+}
diff --git a/examples/functions/func b/examples/functions/func
new file mode 100644
index 0000000..e7696f7
--- /dev/null
+++ b/examples/functions/func
@@ -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
+}
diff --git a/examples/functions/inetaddr b/examples/functions/inetaddr
new file mode 100644
index 0000000..9e72613
--- /dev/null
+++ b/examples/functions/inetaddr
@@ -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
+}
diff --git a/examples/functions/inpath b/examples/functions/inpath
new file mode 100644
index 0000000..cb4c93d
--- /dev/null
+++ b/examples/functions/inpath
@@ -0,0 +1,14 @@
+inpath()
+{
+ local PROG
+ path=$(echo $PATH | sed 's/^:/.:/
+ s/::/:.:/g
+ s/:$/:./
+ s/:/ /g')
+
+ for x in $path
+ do
+ [ -x $x/$1 ] && { PROG=$x/$1; break; }
+ done
+ [ -n "$PROG" ]
+}
diff --git a/examples/functions/isnum2 b/examples/functions/isnum2
new file mode 100644
index 0000000..b21974d
--- /dev/null
+++ b/examples/functions/isnum2
@@ -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
+}
diff --git a/examples/functions/isvalidip b/examples/functions/isvalidip
new file mode 100644
index 0000000..0b2dafe
--- /dev/null
+++ b/examples/functions/isvalidip
@@ -0,0 +1,14 @@
+# Thanks to Chris F. A. Johnson <c.f.a.johnson@rogers.com> for this one
+is_validip()
+{
+ case "$*" in
+ ""|*[!0-9.]*|*[!0-9]) return 1 ;;
+ esac
+
+ local IFS=.
+ set -- $*
+
+ [ $# -eq 4 ] &&
+ [ ${1:-666} -le 255 ] && [ ${2:-666} -le 255 ] &&
+ [ ${3:-666} -le 255 ] && [ ${4:-666} -le 254 ]
+}
diff --git a/examples/functions/ksh-cd b/examples/functions/ksh-cd
new file mode 100644
index 0000000..26b00a9
--- /dev/null
+++ b/examples/functions/ksh-cd
@@ -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
+}
diff --git a/examples/functions/ksh-compat-test b/examples/functions/ksh-compat-test
new file mode 100644
index 0000000..919d82b
--- /dev/null
+++ b/examples/functions/ksh-compat-test
@@ -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 $?
diff --git a/examples/functions/kshenv b/examples/functions/kshenv
new file mode 100644
index 0000000..9faba08
--- /dev/null
+++ b/examples/functions/kshenv
@@ -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
+}
diff --git a/examples/functions/login b/examples/functions/login
new file mode 100644
index 0000000..3d59683
--- /dev/null
+++ b/examples/functions/login
@@ -0,0 +1,11 @@
+# replace the `login' and `newgrp' builtins in old bourne shells
+
+login()
+{
+ exec login "$@"
+}
+
+newgrp()
+{
+ exec newgrp "$@"
+}
diff --git a/examples/functions/notify.bash b/examples/functions/notify.bash
new file mode 100644
index 0000000..ed4377c
--- /dev/null
+++ b/examples/functions/notify.bash
@@ -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
+}
diff --git a/examples/functions/seq b/examples/functions/seq
new file mode 100644
index 0000000..c1953ee
--- /dev/null
+++ b/examples/functions/seq
@@ -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
+}
diff --git a/examples/functions/seq2 b/examples/functions/seq2
new file mode 100644
index 0000000..4a54498
--- /dev/null
+++ b/examples/functions/seq2
@@ -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
+}
diff --git a/examples/functions/shcat b/examples/functions/shcat
new file mode 100644
index 0000000..84f0391
--- /dev/null
+++ b/examples/functions/shcat
@@ -0,0 +1,7 @@
+shcat()
+{
+ while IFS= read -r line
+ do
+ echo "$line"
+ done
+}
diff --git a/examples/functions/shcat2 b/examples/functions/shcat2
new file mode 100644
index 0000000..8ceff33
--- /dev/null
+++ b/examples/functions/shcat2
@@ -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
+}
diff --git a/examples/functions/sort-pos-params b/examples/functions/sort-pos-params
new file mode 100644
index 0000000..95acd85
--- /dev/null
+++ b/examples/functions/sort-pos-params
@@ -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
diff --git a/examples/functions/substr b/examples/functions/substr
new file mode 100644
index 0000000..f19f5d7
--- /dev/null
+++ b/examples/functions/substr
@@ -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
+}
diff --git a/examples/functions/substr2 b/examples/functions/substr2
new file mode 100644
index 0000000..fc21b98
--- /dev/null
+++ b/examples/functions/substr2
@@ -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
+}
diff --git a/examples/functions/whatis b/examples/functions/whatis
new file mode 100644
index 0000000..570c585
--- /dev/null
+++ b/examples/functions/whatis
@@ -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
+}
diff --git a/examples/functions/whence b/examples/functions/whence
new file mode 100644
index 0000000..ba27b00
--- /dev/null
+++ b/examples/functions/whence
@@ -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
+}
diff --git a/examples/functions/which b/examples/functions/which
new file mode 100644
index 0000000..f0db290
--- /dev/null
+++ b/examples/functions/which
@@ -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
+}
diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in
new file mode 100644
index 0000000..956f018
--- /dev/null
+++ b/examples/loadables/Makefile.in
@@ -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
diff --git a/examples/loadables/Makefile.inc.in b/examples/loadables/Makefile.inc.in
new file mode 100644
index 0000000..ee3c303
--- /dev/null
+++ b/examples/loadables/Makefile.inc.in
@@ -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 $@ $<
diff --git a/examples/loadables/Makefile.sample.in b/examples/loadables/Makefile.sample.in
new file mode 100644
index 0000000..fe9b06a
--- /dev/null
+++ b/examples/loadables/Makefile.sample.in
@@ -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
diff --git a/examples/loadables/README b/examples/loadables/README
new file mode 100644
index 0000000..c729a6a
--- /dev/null
+++ b/examples/loadables/README
@@ -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.
diff --git a/examples/loadables/accept.c b/examples/loadables/accept.c
new file mode 100644
index 0000000..ff98423
--- /dev/null
+++ b/examples/loadables/accept.c
@@ -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 */
+};
diff --git a/examples/loadables/asort.c b/examples/loadables/asort.c
new file mode 100644
index 0000000..e847ef8
--- /dev/null
+++ b/examples/loadables/asort.c
@@ -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
+};
diff --git a/examples/loadables/basename.c b/examples/loadables/basename.c
new file mode 100644
index 0000000..29dd1a6
--- /dev/null
+++ b/examples/loadables/basename.c
@@ -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 */
+};
diff --git a/examples/loadables/cat.c b/examples/loadables/cat.c
new file mode 100644
index 0000000..71685f3
--- /dev/null
+++ b/examples/loadables/cat.c
@@ -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
+};
diff --git a/examples/loadables/csv.c b/examples/loadables/csv.c
new file mode 100644
index 0000000..75b3772
--- /dev/null
+++ b/examples/loadables/csv.c
@@ -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 */
+};
diff --git a/examples/loadables/cut.c b/examples/loadables/cut.c
new file mode 100644
index 0000000..48f8004
--- /dev/null
+++ b/examples/loadables/cut.c
@@ -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 */
+};
diff --git a/examples/loadables/dirname.c b/examples/loadables/dirname.c
new file mode 100644
index 0000000..d802ca7
--- /dev/null
+++ b/examples/loadables/dirname.c
@@ -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 */
+};
diff --git a/examples/loadables/dsv.c b/examples/loadables/dsv.c
new file mode 100644
index 0000000..70e59cb
--- /dev/null
+++ b/examples/loadables/dsv.c
@@ -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 */
+};
diff --git a/examples/loadables/fdflags.c b/examples/loadables/fdflags.c
new file mode 100644
index 0000000..9f2d089
--- /dev/null
+++ b/examples/loadables/fdflags.c
@@ -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 */
+};
diff --git a/examples/loadables/finfo.c b/examples/loadables/finfo.c
new file mode 100644
index 0000000..8c278c3
--- /dev/null
+++ b/examples/loadables/finfo.c
@@ -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
diff --git a/examples/loadables/getconf.c b/examples/loadables/getconf.c
new file mode 100644
index 0000000..75a0a56
--- /dev/null
+++ b/examples/loadables/getconf.c
@@ -0,0 +1,1163 @@
+/* Copyright (C) 1991-2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ 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; version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
+
+/* Modified by Chet Ramey <chet.ramey@case.edu> for inclusion in bash. */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <error.h>
+#include <libintl.h>
+#include <locale.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "typemax.h"
+#include "loadables.h"
+#include "getconf.h"
+
+#ifndef errno
+extern int errno;
+#endif
+
+/* Hack to `encode' values wider than long into a conf_variable */
+#define VAL_LLONG_MIN -1000
+#define VAL_LLONG_MAX -1001
+#define VAL_ULLONG_MAX -1002
+#define VAL_ULONG_MAX -1003
+#define VAL_SSIZE_MAX -1004
+#define VAL_SIZE_MAX -1005
+
+struct conf
+ {
+ const char *name;
+ const long call_name; /* or value for CONSTANT */
+ const enum { SYSCONF, CONFSTR, PATHCONF, CONSTANT, UNDEFINED } call;
+ };
+
+static const struct conf vars[] =
+ {
+#ifdef _PC_LINK_MAX
+ { "LINK_MAX", _PC_LINK_MAX, PATHCONF },
+ { "_POSIX_LINK_MAX", _PC_LINK_MAX, PATHCONF },
+#else
+ { "LINK_MAX", _POSIX_LINK_MAX, CONSTANT },
+ { "_POSIX_LINK_MAX", _POSIX_LINK_MAX, CONSTANT },
+#endif
+#ifdef _PC_MAX_CANON
+ { "MAX_CANON", _PC_MAX_CANON, PATHCONF },
+ { "_POSIX_MAX_CANON", _PC_MAX_CANON, PATHCONF },
+#else
+ { "MAX_CANON", _POSIX_MAX_CANON, CONSTANT },
+ { "_POSIX_MAX_CANON", _POSIX_MAX_CANON, CONSTANT },
+#endif
+#ifdef _PC_MAX_INPUT
+ { "MAX_INPUT", _PC_MAX_INPUT, PATHCONF },
+ { "_POSIX_MAX_INPUT", _PC_MAX_INPUT, PATHCONF },
+#else
+ { "MAX_INPUT", _POSIX_MAX_INPUT, CONSTANT },
+ { "_POSIX_MAX_INPUT", _POSIX_MAX_INPUT, CONSTANT },
+#endif
+#ifdef _PC_NAME_MAX
+ { "NAME_MAX", _PC_NAME_MAX, PATHCONF },
+ { "_POSIX_NAME_MAX", _PC_NAME_MAX, PATHCONF },
+#else
+ { "NAME_MAX", _POSIX_NAME_MAX, CONSTANT },
+ { "_POSIX_NAME_MAX", _POSIX_NAME_MAX, CONSTANT },
+#endif
+#ifdef _PC_PATH_MAX
+ { "PATH_MAX", _PC_PATH_MAX, PATHCONF },
+ { "_POSIX_PATH_MAX", _PC_PATH_MAX, PATHCONF },
+#else
+ { "PATH_MAX", _POSIX_PATH_MAX, CONSTANT },
+ { "_POSIX_PATH_MAX", _POSIX_PATH_MAX, CONSTANT },
+#endif
+#ifdef _PC_PIPE_BUF
+ { "PIPE_BUF", _PC_PIPE_BUF, PATHCONF },
+ { "_POSIX_PIPE_BUF", _PC_PIPE_BUF, PATHCONF },
+#else
+ { "PIPE_BUF", _POSIX_PIPE_BUF, CONSTANT },
+ { "_POSIX_PIPE_BUF", _POSIX_PIPE_BUF, CONSTANT },
+#endif
+#ifdef _PC_SOCK_MAXBUF
+ { "SOCK_MAXBUF", _PC_SOCK_MAXBUF, PATHCONF },
+#endif
+#ifdef _PC_ASYNC_IO
+ { "_POSIX_ASYNC_IO", _PC_ASYNC_IO, PATHCONF },
+#endif
+ { "_POSIX_CHOWN_RESTRICTED", _PC_CHOWN_RESTRICTED, PATHCONF },
+ { "_POSIX_NO_TRUNC", _PC_NO_TRUNC, PATHCONF },
+#ifdef _PC_PRIO_IO
+ { "_POSIX_PRIO_IO", _PC_PRIO_IO, PATHCONF },
+#endif
+#ifdef _PC_SYNC_IO
+ { "_POSIX_SYNC_IO", _PC_SYNC_IO, PATHCONF },
+#endif
+ { "_POSIX_VDISABLE", _PC_VDISABLE, PATHCONF },
+
+ { "ARG_MAX", _SC_ARG_MAX, SYSCONF },
+ { "ATEXIT_MAX", _SC_ATEXIT_MAX, SYSCONF },
+#ifdef _SC_CHAR_BIT
+ { "CHAR_BIT", _SC_CHAR_BIT, SYSCONF },
+#else
+ { "CHAR_BIT", CHAR_BIT, CONSTANT },
+#endif
+#ifdef _SC_CHAR_MAX
+ { "CHAR_MAX", _SC_CHAR_MAX, SYSCONF },
+#else
+ { "CHAR_BIT", CHAR_MAX, CONSTANT },
+#endif
+#ifdef _SC_CHAR_MIN
+ { "CHAR_MIN", _SC_CHAR_MIN, SYSCONF },
+#else
+ { "CHAR_MIN", CHAR_MIN, CONSTANT },
+#endif
+ { "CHILD_MAX", _SC_CHILD_MAX, SYSCONF },
+ { "CLK_TCK", _SC_CLK_TCK, SYSCONF },
+#ifdef _SC_INT_BIT
+ { "INT_BIT", _SC_INT_BIT, SYSCONF },
+#else
+ { "INT_BIT", INT_BIT, CONSTANT },
+#endif
+#ifdef _SC_INT_MAX
+ { "INT_MAX", _SC_INT_MAX, SYSCONF },
+#else
+ { "INT_MAX", INT_MAX, CONSTANT },
+#endif
+#ifdef _SC_INT_MIN
+ { "INT_MIN", _SC_INT_MIN, SYSCONF },
+#else
+ { "INT_MIN", INT_MIN, CONSTANT },
+#endif
+#ifdef _SC_UIO_MAXIOV
+ { "IOV_MAX", _SC_UIO_MAXIOV, SYSCONF },
+#endif
+ { "LOGNAME_MAX", _SC_LOGIN_NAME_MAX, SYSCONF },
+#ifdef _SC_LONG_BIT
+ { "LONG_BIT", _SC_LONG_BIT, SYSCONF },
+#else
+ { "LONG_BIT", LONG_BIT, CONSTANT },
+#endif
+ { "LONG_MIN", LONG_MIN, CONSTANT },
+ { "LONG_MAX", LONG_MAX, CONSTANT },
+#if HAVE_LONG_LONG_INT
+ { "LLONG_MIN", VAL_LLONG_MIN, CONSTANT },
+ { "LLONG_MAX", VAL_LLONG_MAX, CONSTANT },
+#else
+ { "LLONG_MIN", LLONG_MIN, CONSTANT },
+ { "LLONG_MAX", LLONG_MAX, CONSTANT },
+#endif
+#ifdef _SC_MB_LEN_MAX
+ { "MB_LEN_MAX", _SC_MB_LEN_MAX, SYSCONF },
+#else
+ { "MB_LEN_MAX", MB_LEN_MAX, CONSTANT },
+#endif
+ { "NGROUPS_MAX", _SC_NGROUPS_MAX, SYSCONF },
+#ifdef _SC_NL_ARGMAX
+ { "NL_ARGMAX", _SC_NL_ARGMAX, SYSCONF },
+#endif
+#ifdef _SC_NL_LANGMAX
+ { "NL_LANGMAX", _SC_NL_LANGMAX, SYSCONF },
+#endif
+#ifdef _SC_NL_MSGMAX
+ { "NL_MSGMAX", _SC_NL_MSGMAX, SYSCONF },
+#endif
+#ifdef _SC_NL_NMAX
+ { "NL_NMAX", _SC_NL_NMAX, SYSCONF },
+#endif
+#ifdef _SC_NL_SETMAX
+ { "NL_SETMAX", _SC_NL_SETMAX, SYSCONF },
+#endif
+#ifdef _SC_NL_TEXTMAX
+ { "NL_TEXTMAX", _SC_NL_TEXTMAX, SYSCONF },
+#endif
+ { "NSS_BUFLEN_GROUP", _SC_GETGR_R_SIZE_MAX, SYSCONF },
+ { "NSS_BUFLEN_PASSWD", _SC_GETPW_R_SIZE_MAX, SYSCONF },
+#ifdef _SC_NZERO
+ { "NZERO", _SC_NZERO, SYSCONF },
+#endif
+ { "OPEN_MAX", _SC_OPEN_MAX, SYSCONF },
+ { "PAGESIZE", _SC_PAGESIZE, SYSCONF },
+ { "PAGE_SIZE", _SC_PAGESIZE, SYSCONF },
+#ifdef _SC_PASS_MAX
+ { "PASS_MAX", _SC_PASS_MAX, SYSCONF },
+#endif
+ { "PTHREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS, SYSCONF },
+ { "PTHREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX, SYSCONF },
+ { "PTHREAD_STACK_MIN", _SC_THREAD_STACK_MIN, SYSCONF },
+ { "PTHREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX, SYSCONF },
+#ifdef _SC_SCHAR_MAX
+ { "SCHAR_MAX", _SC_SCHAR_MAX, SYSCONF },
+#else
+ { "SCHAR_MAX", SCHAR_MAX, CONSTANT },
+#endif
+#ifdef _SC_SCHAR_MIN
+ { "SCHAR_MIN", _SC_SCHAR_MIN, SYSCONF },
+#else
+ { "SCHAR_MIN", SCHAR_MIN, CONSTANT },
+#endif
+#ifdef _SC_SHRT_MAX
+ { "SHRT_MAX", _SC_SHRT_MAX, SYSCONF },
+#else
+ { "SHRT_MAX", SHRT_MAX, CONSTANT },
+#endif
+#ifdef _SC_SHRT_MIN
+ { "SHRT_MIN", _SC_SHRT_MIN, SYSCONF },
+#else
+ { "SHRT_MIN", SHRT_MIN, CONSTANT },
+#endif
+#ifdef _SC_SIZE_MAX
+ { "SIZE_MAX", _SC_SIZE_MAX, SYSCONF },
+#else
+ { "SIZE_MAX", VAL_SIZE_MAX, CONSTANT },
+#endif
+#ifdef _SC_SSIZE_MAX
+ { "SSIZE_MAX", _SC_SSIZE_MAX, SYSCONF },
+#elif SIZEOF_SIZE_MAX == 8
+ { "SSIZE_MAX", VAL_SSIZE_MAX, CONSTANT },
+#else
+ { "SSIZE_MAX", VAL_SSIZE_MAX, CONSTANT },
+#endif
+ { "TTY_NAME_MAX", _SC_TTY_NAME_MAX, SYSCONF },
+ { "TZNAME_MAX", _SC_TZNAME_MAX, SYSCONF },
+#ifdef _SC_UCHAR_MAX
+ { "UCHAR_MAX", _SC_UCHAR_MAX, SYSCONF },
+#else
+ { "UCHAR_MAX", UCHAR_MAX, CONSTANT },
+#endif
+#ifdef _SC_UINT_MAX
+ { "UINT_MAX", _SC_UINT_MAX, SYSCONF },
+#else
+ { "UINT_MAX", UINT_MAX, CONSTANT },
+#endif
+#ifdef _SC_UIO_MAXIOV
+ { "UIO_MAXIOV", _SC_UIO_MAXIOV, SYSCONF },
+#endif
+#ifdef _SC_ULONG_MAX
+ { "ULONG_MAX", _SC_ULONG_MAX, SYSCONF },
+#else
+ { "ULONG_MAX", VAL_ULONG_MAX, CONSTANT },
+#endif
+#ifdef HAVE_LONG_LONG_INT
+ { "ULLONG_MAX", VAL_ULLONG_MAX, CONSTANT },
+#endif
+#ifdef _SC_USHRT_MAX
+ { "USHRT_MAX", _SC_USHRT_MAX, SYSCONF },
+#else
+ { "USHRT_MAX", USHRT_MAX, CONSTANT },
+#endif
+#ifdef _SC_WORD_BIT
+ { "WORD_BIT", _SC_WORD_BIT, SYSCONF },
+#else
+ { "WORD_BIT", WORD_BIT, CONSTANT },
+#endif
+#ifdef _SC_AVPHYS_PAGES
+ { "_AVPHYS_PAGES", _SC_AVPHYS_PAGES, SYSCONF },
+#endif
+ { "_NPROCESSORS_CONF", _SC_NPROCESSORS_CONF, SYSCONF },
+ { "_NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN, SYSCONF },
+ { "_PHYS_PAGES", _SC_PHYS_PAGES, SYSCONF },
+#ifdef _SC_ARG_MAX
+ { "_POSIX_ARG_MAX", _SC_ARG_MAX, SYSCONF },
+#else
+ { "_POSIX_ARG_MAX", _POSIX_ARG_MAX, CONSTANT },
+#endif
+ { "_POSIX_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO, SYSCONF },
+#ifdef _SC_CHILD_MAX
+ { "_POSIX_CHILD_MAX", _SC_CHILD_MAX, SYSCONF },
+#else
+ { "_POSIX_CHILD_MAX", _POSIX_CHILD_MAX, CONSTANT },
+#endif
+ { "_POSIX_FSYNC", _SC_FSYNC, SYSCONF },
+ { "_POSIX_JOB_CONTROL", _SC_JOB_CONTROL, SYSCONF },
+ { "_POSIX_MAPPED_FILES", _SC_MAPPED_FILES, SYSCONF },
+ { "_POSIX_MEMLOCK", _SC_MEMLOCK, SYSCONF },
+ { "_POSIX_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE, SYSCONF },
+ { "_POSIX_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION, SYSCONF },
+ { "_POSIX_MESSAGE_PASSING", _SC_MESSAGE_PASSING, SYSCONF },
+ { "_POSIX_NGROUPS_MAX", _SC_NGROUPS_MAX, SYSCONF },
+ { "_POSIX_OPEN_MAX", _SC_OPEN_MAX, SYSCONF },
+#ifdef _SC_PII
+ { "_POSIX_PII", _SC_PII, SYSCONF },
+#endif
+#ifdef _SC_PII_INTERNET
+ { "_POSIX_PII_INTERNET", _SC_PII_INTERNET, SYSCONF },
+#endif
+#ifdef _SC_PII_INTERNET_DGRAM
+ { "_POSIX_PII_INTERNET_DGRAM", _SC_PII_INTERNET_DGRAM, SYSCONF },
+#endif
+#ifdef _SC_PII_INTERNET_STREAM
+ { "_POSIX_PII_INTERNET_STREAM", _SC_PII_INTERNET_STREAM, SYSCONF },
+#endif
+#ifdef _SC_PII_OSI
+ { "_POSIX_PII_OSI", _SC_PII_OSI, SYSCONF },
+#endif
+#ifdef _SC_PII_OSI_CLTS
+ { "_POSIX_PII_OSI_CLTS", _SC_PII_OSI_CLTS, SYSCONF },
+#endif
+#ifdef _SC_PII_OSI_COTS
+ { "_POSIX_PII_OSI_COTS", _SC_PII_OSI_COTS, SYSCONF },
+#endif
+#ifdef _SC_PII_OSI_M
+ { "_POSIX_PII_OSI_M", _SC_PII_OSI_M, SYSCONF },
+#endif
+#ifdef _SC_PII_SOCKET
+ { "_POSIX_PII_SOCKET", _SC_PII_SOCKET, SYSCONF },
+#endif
+#ifdef _SC_PII_XTI
+ { "_POSIX_PII_XTI", _SC_PII_XTI, SYSCONF },
+#endif
+#ifdef _SC_POLL
+ { "_POSIX_POLL", _SC_POLL, SYSCONF },
+#endif
+#ifdef _SC_PRIORITIZED_IO
+ { "_POSIX_PRIORITIZED_IO", _SC_PRIORITIZED_IO, SYSCONF },
+#endif
+ { "_POSIX_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING, SYSCONF },
+ { "_POSIX_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS, SYSCONF },
+ { "_POSIX_SAVED_IDS", _SC_SAVED_IDS, SYSCONF },
+#ifdef _SC_SELECT
+ { "_POSIX_SELECT", _SC_SELECT, SYSCONF },
+#endif
+ { "_POSIX_SEMAPHORES", _SC_SEMAPHORES, SYSCONF },
+ { "_POSIX_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS, SYSCONF },
+#ifdef _SC_SSIZE_MAX
+ { "_POSIX_SSIZE_MAX", _SC_SSIZE_MAX, SYSCONF },
+#elif SIZEOF_SIZE_T == 8
+ { "_POSIX_SSIZE_MAX", VAL_SSIZE_MAX, CONSTANT },
+#else
+ { "_POSIX_SSIZE_MAX", VAL_SSIZE_MAX, CONSTANT },
+#endif
+#ifdef _SC_STREAM_MAX
+ { "_POSIX_STREAM_MAX", _SC_STREAM_MAX, SYSCONF },
+#else
+ { "_POSIX_STREAM_MAX", _POSIX_STREAM_MAX, CONSTANT },
+#endif
+ { "_POSIX_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO, SYSCONF },
+ { "_POSIX_THREADS", _SC_THREADS, SYSCONF },
+ { "_POSIX_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR, SYSCONF },
+ { "_POSIX_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE, SYSCONF },
+ { "_POSIX_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING, SYSCONF },
+ { "_POSIX_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT, SYSCONF },
+ { "_POSIX_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT, SYSCONF },
+#ifdef _SC_THREAD_ROBUST_PRIO_INHERIT
+ { "_POSIX_THREAD_ROBUST_PRIO_INHERIT", _SC_THREAD_ROBUST_PRIO_INHERIT,
+ SYSCONF },
+#endif
+#ifdef _SC_THREAD_ROBUST_PRIO_PROTECT
+ { "_POSIX_THREAD_ROBUST_PRIO_PROTECT", _SC_THREAD_ROBUST_PRIO_PROTECT,
+ SYSCONF },
+#endif
+ { "_POSIX_THREAD_PROCESS_SHARED", _SC_THREAD_PROCESS_SHARED, SYSCONF },
+ { "_POSIX_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS, SYSCONF },
+ { "_POSIX_TIMERS", _SC_TIMERS, SYSCONF },
+ { "TIMER_MAX", _SC_TIMER_MAX, SYSCONF },
+#ifdef _POSIX_TZNAME_MAX
+ { "_POSIX_TZNAME_MAX", _SC_TZNAME_MAX, SYSCONF },
+#else
+ { "_POSIX_TZNAME_MAX", _POSIX_TZNAME_MAX, CONSTANT },
+#endif
+ { "_POSIX_VERSION", _SC_VERSION, SYSCONF },
+#ifdef _SC_T_IOV_MAX
+ { "_T_IOV_MAX", _SC_T_IOV_MAX, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_CRYPT
+ { "_XOPEN_CRYPT", _SC_XOPEN_CRYPT, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_ENH_I18N
+ { "_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_LEGACY
+ { "_XOPEN_LEGACY", _SC_XOPEN_LEGACY, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_REALTIME
+ { "_XOPEN_REALTIME", _SC_XOPEN_REALTIME, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_REALTIME_THREADS
+ { "_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_SHM
+ { "_XOPEN_SHM", _SC_XOPEN_SHM, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_UNIX
+ { "_XOPEN_UNIX", _SC_XOPEN_UNIX, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_VERSION
+ { "_XOPEN_VERSION", _SC_XOPEN_VERSION, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_XCU_VERSION
+ { "_XOPEN_XCU_VERSION", _SC_XOPEN_XCU_VERSION, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_XPG2
+ { "_XOPEN_XPG2", _SC_XOPEN_XPG2, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_XPG3
+ { "_XOPEN_XPG3", _SC_XOPEN_XPG3, SYSCONF },
+#endif
+#ifdef _SC_XOPEN_XPG4
+ { "_XOPEN_XPG4", _SC_XOPEN_XPG4, SYSCONF },
+#endif
+ /* POSIX.2 */
+#ifdef _SC_BC_BASE_MAX
+ { "BC_BASE_MAX", _SC_BC_BASE_MAX, SYSCONF },
+ { "POSIX2_BC_BASE_MAX", _SC_BC_BASE_MAX, SYSCONF },
+#else
+ { "BC_BASE_MAX", _POSIX2_BC_BASE_MAX, CONSTANT },
+#endif
+#ifdef _SC_BC_BASE_MAX
+ { "BC_DIM_MAX", _SC_BC_DIM_MAX, SYSCONF },
+ { "POSIX2_BC_DIM_MAX", _SC_BC_DIM_MAX, SYSCONF },
+#else
+ { "BC_DIM_MAX", _POSIX2_BC_DIM_MAX, CONSTANT },
+#endif
+#ifdef _SC_BC_SCALE_MAX
+ { "BC_SCALE_MAX", _SC_BC_SCALE_MAX, SYSCONF },
+ { "POSIX2_BC_SCALE_MAX", _SC_BC_SCALE_MAX, SYSCONF },
+#else
+ { "BC_SCALE_MAX", _POSIX2_BC_SCALE_MAX, CONSTANT },
+#endif
+#ifdef _SC_BC_STRING_MAX
+ { "BC_STRING_MAX", _SC_BC_STRING_MAX, SYSCONF },
+ { "POSIX2_BC_STRING_MAX", _SC_BC_STRING_MAX, SYSCONF },
+#else
+ { "BC_STRING_MAX", _POSIX2_BC_STRING_MAX, CONSTANT },
+ { "POSIX2_BC_STRING_MAX", _POSIX2_BC_STRING_MAX, CONSTANT },
+#endif
+#ifdef _SC_CHARCLASS_NAME_MAX
+ { "CHARCLASS_NAME_MAX", _SC_CHARCLASS_NAME_MAX, SYSCONF },
+#endif
+#ifdef _SC_COLL_WEIGHTS_MAX
+ { "COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX, SYSCONF },
+ { "POSIX2_COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX, SYSCONF },
+#else
+ { "COLL_WEIGHTS_MAX", _POSIX2_COLL_WEIGHTS_MAX, CONSTANT },
+ { "POSIX2_COLL_WEIGHTS_MAX", _POSIX2_COLL_WEIGHTS_MAX, CONSTANT },
+#endif
+#ifdef _SC_EQUIV_CLASS_MAX
+ { "EQUIV_CLASS_MAX", _SC_EQUIV_CLASS_MAX, SYSCONF },
+#else
+ { "EQUIV_CLASS_MAX", _POSIX2_EQUIV_CLASS_MAX, CONSTANT },
+#endif
+#ifdef _SC_EXPR_NEST_MAX
+ { "EXPR_NEST_MAX", _SC_EXPR_NEST_MAX, SYSCONF },
+ { "POSIX2_EXPR_NEST_MAX", _SC_EXPR_NEST_MAX, SYSCONF },
+#else
+ { "EXPR_NEST_MAX", _POSIX2_EXPR_NEST_MAX, CONSTANT },
+ { "POSIX2_EXPR_NEST_MAX", _POSIX2_EXPR_NEST_MAX, CONSTANT },
+#endif
+#ifdef _SC_LINE_MAX
+ { "LINE_MAX", _SC_LINE_MAX, SYSCONF },
+ { "_POSIX2_LINE_MAX", _SC_LINE_MAX, SYSCONF },
+ { "POSIX2_LINE_MAX", _SC_LINE_MAX, SYSCONF },
+#else
+ { "LINE_MAX", _POSIX2_LINE_MAX, CONSTANT },
+ { "_POSIX2_LINE_MAX", _POSIX2_LINE_MAX, CONSTANT },
+ { "POSIX2_LINE_MAX", _POSIX2_LINE_MAX, CONSTANT },
+#endif
+#ifdef _SC_RE_DUP_MAX
+ { "POSIX2_RE_DUP_MAX", _SC_RE_DUP_MAX, SYSCONF },
+ { "RE_DUP_MAX", _SC_RE_DUP_MAX, SYSCONF },
+#else
+ { "POSIX2_RE_DUP_MAX", _POSIX2_RE_DUP_MAX, CONSTANT },
+ { "RE_DUP_MAX", _POSIX2_RE_DUP_MAX, CONSTANT },
+#endif
+ { "POSIX2_CHAR_TERM", _SC_2_CHAR_TERM, SYSCONF },
+ { "POSIX2_C_BIND", _SC_2_C_BIND, SYSCONF },
+ { "POSIX2_C_DEV", _SC_2_C_DEV, SYSCONF },
+#ifdef _SC_2_C_VERSION
+ { "POSIX2_C_VERSION", _SC_2_C_VERSION, SYSCONF },
+#endif
+ { "POSIX2_FORT_DEV", _SC_2_FORT_DEV, SYSCONF },
+ { "POSIX2_FORT_RUN", _SC_2_FORT_RUN, SYSCONF },
+ { "POSIX2_LOCALEDEF", _SC_2_LOCALEDEF, SYSCONF },
+ { "POSIX2_SW_DEV", _SC_2_SW_DEV, SYSCONF },
+ { "POSIX2_UPE", _SC_2_UPE, SYSCONF },
+ { "POSIX2_VERSION", _SC_2_VERSION, SYSCONF },
+
+ { "PATH", _CS_PATH, CONFSTR },
+ { "CS_PATH", _CS_PATH, CONFSTR },
+
+ /* LFS */
+#ifdef _CS_LFS_CFLAGS
+ { "LFS_CFLAGS", _CS_LFS_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_LFS_LDFLAGS
+ { "LFS_LDFLAGS", _CS_LFS_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_LFS_LIBS
+ { "LFS_LIBS", _CS_LFS_LIBS, CONFSTR },
+#endif
+#ifdef _CS_LFS_LINTFLAGS
+ { "LFS_LINTFLAGS", _CS_LFS_LINTFLAGS, CONFSTR },
+#endif
+#ifdef _CS_LFS64_CFLAGS
+ { "LFS64_CFLAGS", _CS_LFS64_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_LFS64_LDFLAGS
+ { "LFS64_LDFLAGS", _CS_LFS64_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_LFS64_LIBS
+ { "LFS64_LIBS", _CS_LFS64_LIBS, CONFSTR },
+#endif
+
+#ifdef _CS_LFS64_LINTFLAGS
+ { "LFS64_LINTFLAGS", _CS_LFS64_LINTFLAGS, CONFSTR },
+#endif
+
+ /* Programming environments. */
+#ifdef _CS_V5_WIDTH_RESTRICTED_ENVS
+ { "_XBS5_WIDTH_RESTRICTED_ENVS", _CS_V5_WIDTH_RESTRICTED_ENVS, CONFSTR },
+ { "XBS5_WIDTH_RESTRICTED_ENVS", _CS_V5_WIDTH_RESTRICTED_ENVS, CONFSTR },
+#endif
+
+#ifdef _SC_XBS5_ILP32_OFF32
+ { "_XBS5_ILP32_OFF32", _SC_XBS5_ILP32_OFF32, SYSCONF },
+#endif
+#ifdef _CS_XBS5_ILP32_OFF32_CFLAGS
+ { "XBS5_ILP32_OFF32_CFLAGS", _CS_XBS5_ILP32_OFF32_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_ILP32_OFF32_LDFLAGS
+ { "XBS5_ILP32_OFF32_LDFLAGS", _CS_XBS5_ILP32_OFF32_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_ILP32_OFF32_LIBS
+ { "XBS5_ILP32_OFF32_LIBS", _CS_XBS5_ILP32_OFF32_LIBS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_ILP32_OFF32_LINTFLAGS
+ { "XBS5_ILP32_OFF32_LINTFLAGS", _CS_XBS5_ILP32_OFF32_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_XBS5_ILP32_OFFBIG
+ { "_XBS5_ILP32_OFFBIG", _SC_XBS5_ILP32_OFFBIG, SYSCONF },
+#endif
+#ifdef _CS_XBS5_ILP32_OFFBIG_CFLAGS
+ { "XBS5_ILP32_OFFBIG_CFLAGS", _CS_XBS5_ILP32_OFFBIG_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_ILP32_OFFBIG_LDFLAGS
+ { "XBS5_ILP32_OFFBIG_LDFLAGS", _CS_XBS5_ILP32_OFFBIG_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_ILP32_OFFBIG_LIBS
+ { "XBS5_ILP32_OFFBIG_LIBS", _CS_XBS5_ILP32_OFFBIG_LIBS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_ILP32_OFFBIG_LINTFLAGS
+ { "XBS5_ILP32_OFFBIG_LINTFLAGS", _CS_XBS5_ILP32_OFFBIG_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_XBS5_LP64_OFF64
+ { "_XBS5_LP64_OFF64", _SC_XBS5_LP64_OFF64, SYSCONF },
+#endif
+#ifdef _CS_XBS5_LP64_OFF64_CFLAGS
+ { "XBS5_LP64_OFF64_CFLAGS", _CS_XBS5_LP64_OFF64_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_LP64_OFF64_LDFLAGS
+ { "XBS5_LP64_OFF64_LDFLAGS", _CS_XBS5_LP64_OFF64_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_LP64_OFF64_LIBS
+ { "XBS5_LP64_OFF64_LIBS", _CS_XBS5_LP64_OFF64_LIBS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_LP64_OFF64_LINTFLAGS
+ { "XBS5_LP64_OFF64_LINTFLAGS", _CS_XBS5_LP64_OFF64_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_XBS5_LPBIG_OFFBIG
+ { "_XBS5_LPBIG_OFFBIG", _SC_XBS5_LPBIG_OFFBIG, SYSCONF },
+#endif
+#ifdef _CS_XBS5_LPBIG_OFFBIG_CFLAGS
+ { "XBS5_LPBIG_OFFBIG_CFLAGS", _CS_XBS5_LPBIG_OFFBIG_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_LPBIG_OFFBIG_LDFLAGS
+ { "XBS5_LPBIG_OFFBIG_LDFLAGS", _CS_XBS5_LPBIG_OFFBIG_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_LPBIG_OFFBIG_LIBS
+ { "XBS5_LPBIG_OFFBIG_LIBS", _CS_XBS5_LPBIG_OFFBIG_LIBS, CONFSTR },
+#endif
+#ifdef _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS
+ { "XBS5_LPBIG_OFFBIG_LINTFLAGS", _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_V6_ILP32_OFF32
+ { "_POSIX_V6_ILP32_OFF32", _SC_V6_ILP32_OFF32, SYSCONF },
+#endif
+#ifdef _CS_POSIX_V6_ILP32_OFF32_CFLAGS
+ { "POSIX_V6_ILP32_OFF32_CFLAGS", _CS_POSIX_V6_ILP32_OFF32_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_ILP32_OFF32_LDFLAGS
+ { "POSIX_V6_ILP32_OFF32_LDFLAGS", _CS_POSIX_V6_ILP32_OFF32_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_ILP32_OFF32_LIBS
+ { "POSIX_V6_ILP32_OFF32_LIBS", _CS_POSIX_V6_ILP32_OFF32_LIBS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS
+ { "POSIX_V6_ILP32_OFF32_LINTFLAGS", _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _CS_V6_WIDTH_RESTRICTED_ENVS
+ { "_POSIX_V6_WIDTH_RESTRICTED_ENVS", _CS_V6_WIDTH_RESTRICTED_ENVS, CONFSTR },
+ { "POSIX_V6_WIDTH_RESTRICTED_ENVS", _CS_V6_WIDTH_RESTRICTED_ENVS, CONFSTR },
+#endif
+
+#ifdef _SC_V6_ILP32_OFFBIG
+ { "_POSIX_V6_ILP32_OFFBIG", _SC_V6_ILP32_OFFBIG, SYSCONF },
+#endif
+#ifdef _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS
+ { "POSIX_V6_ILP32_OFFBIG_CFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_ILP32_OFF32_LDFLAGS
+ { "POSIX_V6_ILP32_OFFBIG_LDFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_ILP32_OFF32_LIBS
+ { "POSIX_V6_ILP32_OFFBIG_LIBS", _CS_POSIX_V6_ILP32_OFFBIG_LIBS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS
+ { "POSIX_V6_ILP32_OFFBIG_LINTFLAGS", _CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_V6_LP64_OFF64
+ { "_POSIX_V6_LP64_OFF64", _SC_V6_LP64_OFF64, SYSCONF },
+#endif
+#ifdef _CS_POSIX_V6_LP64_OFF64_CFLAGS
+ { "POSIX_V6_LP64_OFF64_CFLAGS", _CS_POSIX_V6_LP64_OFF64_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_LP64_OFF64_LDFLAGS
+ { "POSIX_V6_LP64_OFF64_LDFLAGS", _CS_POSIX_V6_LP64_OFF64_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_LP64_OFF64_LIBS
+ { "POSIX_V6_LP64_OFF64_LIBS", _CS_POSIX_V6_LP64_OFF64_LIBS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_LP64_OFF64_LINTFLAGS
+ { "POSIX_V6_LP64_OFF64_LINTFLAGS", _CS_POSIX_V6_LP64_OFF64_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_V6_LPBIG_OFFBIG
+ { "_POSIX_V6_LPBIG_OFFBIG", _SC_V6_LPBIG_OFFBIG, SYSCONF },
+#endif
+#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS
+ { "POSIX_V6_LPBIG_OFFBIG_CFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS
+ { "POSIX_V6_LPBIG_OFFBIG_LDFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LIBS
+ { "POSIX_V6_LPBIG_OFFBIG_LIBS", _CS_POSIX_V6_LPBIG_OFFBIG_LIBS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS
+ { "POSIX_V6_LPBIG_OFFBIG_LINTFLAGS", _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_V7_ILP32_OFF32
+ { "_POSIX_V7_ILP32_OFF32", _SC_V7_ILP32_OFF32, SYSCONF },
+#endif
+#ifdef _CS_POSIX_V7_ILP32_OFF32_CFLAGS
+ { "POSIX_V7_ILP32_OFF32_CFLAGS", _CS_POSIX_V7_ILP32_OFF32_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_ILP32_OFF32_LDFLAGS
+ { "POSIX_V7_ILP32_OFF32_LDFLAGS", _CS_POSIX_V7_ILP32_OFF32_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_ILP32_OFF32_LIBS
+ { "POSIX_V7_ILP32_OFF32_LIBS", _CS_POSIX_V7_ILP32_OFF32_LIBS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_ILP32_OFF32_LINTFLAGS
+ { "POSIX_V7_ILP32_OFF32_LINTFLAGS", _CS_POSIX_V7_ILP32_OFF32_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _CS_V7_WIDTH_RESTRICTED_ENVS
+ { "_POSIX_V7_WIDTH_RESTRICTED_ENVS", _CS_V7_WIDTH_RESTRICTED_ENVS, CONFSTR },
+ { "POSIX_V7_WIDTH_RESTRICTED_ENVS", _CS_V7_WIDTH_RESTRICTED_ENVS, CONFSTR },
+#endif
+
+#ifdef _SC_V7_ILP32_OFFBIG
+ { "_POSIX_V7_ILP32_OFFBIG", _SC_V7_ILP32_OFFBIG, SYSCONF },
+#endif
+#ifdef _CS_POSIX_V7_ILP32_OFFBIG_CFLAGS
+ { "POSIX_V7_ILP32_OFFBIG_CFLAGS", _CS_POSIX_V7_ILP32_OFFBIG_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS
+ { "POSIX_V7_ILP32_OFFBIG_LDFLAGS", _CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_ILP32_OFFBIG_LIBS
+ { "POSIX_V7_ILP32_OFFBIG_LIBS", _CS_POSIX_V7_ILP32_OFFBIG_LIBS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS
+ { "POSIX_V7_ILP32_OFFBIG_LINTFLAGS", _CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_V7_LP64_OFF64
+ { "_POSIX_V7_LP64_OFF64", _SC_V7_LP64_OFF64, SYSCONF },
+#endif
+#ifdef _CS_POSIX_V7_LP64_OFF64_CFLAGS
+ { "POSIX_V7_LP64_OFF64_CFLAGS", _CS_POSIX_V7_LP64_OFF64_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_LP64_OFF64_LDFLAGS
+ { "POSIX_V7_LP64_OFF64_LDFLAGS", _CS_POSIX_V7_LP64_OFF64_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_LP64_OFF64_LIBS
+ { "POSIX_V7_LP64_OFF64_LIBS", _CS_POSIX_V7_LP64_OFF64_LIBS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_LP64_OFF64_LINTFLAGS
+ { "POSIX_V7_LP64_OFF64_LINTFLAGS", _CS_POSIX_V7_LP64_OFF64_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_V7_LPBIG_OFFBIG
+ { "_POSIX_V7_LPBIG_OFFBIG", _SC_V7_LPBIG_OFFBIG, SYSCONF },
+#endif
+#ifdef _CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS
+ { "POSIX_V7_LPBIG_OFFBIG_CFLAGS", _CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS
+ { "POSIX_V7_LPBIG_OFFBIG_LDFLAGS", _CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_LPBIG_OFFBIG_LIBS
+ { "POSIX_V7_LPBIG_OFFBIG_LIBS", _CS_POSIX_V7_LPBIG_OFFBIG_LIBS, CONFSTR },
+#endif
+#ifdef _CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS
+ { "POSIX_V7_LPBIG_OFFBIG_LINTFLAGS", _CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS, CONFSTR },
+#endif
+
+#ifdef _SC_ADVISORY_INFO
+ { "_POSIX_ADVISORY_INFO", _SC_ADVISORY_INFO, SYSCONF },
+#endif
+#ifdef _SC_BARRIERS
+ { "_POSIX_BARRIERS", _SC_BARRIERS, SYSCONF },
+#endif
+#ifdef _SC_BASE
+ { "_POSIX_BASE", _SC_BASE, SYSCONF },
+#endif
+#ifdef _SC_C_LANG_SUPPORT
+ { "_POSIX_C_LANG_SUPPORT", _SC_C_LANG_SUPPORT, SYSCONF },
+#endif
+#ifdef _SC_C_LANG_SUPPORT_R
+ { "_POSIX_C_LANG_SUPPORT_R", _SC_C_LANG_SUPPORT_R, SYSCONF },
+#endif
+ { "_POSIX_CLOCK_SELECTION", _SC_CLOCK_SELECTION, SYSCONF },
+ { "_POSIX_CPUTIME", _SC_CPUTIME, SYSCONF },
+ { "_POSIX_THREAD_CPUTIME", _SC_THREAD_CPUTIME, SYSCONF },
+#ifdef _SC_DEVICE_SPECIFIC
+ { "_POSIX_DEVICE_SPECIFIC", _SC_DEVICE_SPECIFIC, SYSCONF },
+#endif
+#ifdef _SC_DEVICE_SPECIFIC_R
+ { "_POSIX_DEVICE_SPECIFIC_R", _SC_DEVICE_SPECIFIC_R, SYSCONF },
+#endif
+#ifdef _SC_FD_MGMT
+ { "_POSIX_FD_MGMT", _SC_FD_MGMT, SYSCONF },
+#endif
+#ifdef _SC_FIFO
+ { "_POSIX_FIFO", _SC_FIFO, SYSCONF },
+#endif
+#ifdef _SC_PIPE
+ { "_POSIX_PIPE", _SC_PIPE, SYSCONF },
+#endif
+#ifdef _SC_FILE_ATTRIBUTES
+ { "_POSIX_FILE_ATTRIBUTES", _SC_FILE_ATTRIBUTES, SYSCONF },
+#endif
+#ifdef _SC_FILE_LOCKING
+ { "_POSIX_FILE_LOCKING", _SC_FILE_LOCKING, SYSCONF },
+#endif
+#ifdef _SC_FILE_SYSTEM
+ { "_POSIX_FILE_SYSTEM", _SC_FILE_SYSTEM, SYSCONF },
+#endif
+ { "_POSIX_MONOTONIC_CLOCK", _SC_MONOTONIC_CLOCK, SYSCONF },
+#ifdef _SC_MULTI_PROCESS
+ { "_POSIX_MULTI_PROCESS", _SC_MULTI_PROCESS, SYSCONF },
+#endif
+#ifdef _SC_SINGLE_PROCESS
+ { "_POSIX_SINGLE_PROCESS", _SC_SINGLE_PROCESS, SYSCONF },
+#endif
+#ifdef _SC_NETWORKING
+ { "_POSIX_NETWORKING", _SC_NETWORKING, SYSCONF },
+#endif
+ { "_POSIX_READER_WRITER_LOCKS", _SC_READER_WRITER_LOCKS, SYSCONF },
+ { "_POSIX_SPIN_LOCKS", _SC_SPIN_LOCKS, SYSCONF },
+ { "_POSIX_REGEXP", _SC_REGEXP, SYSCONF },
+#ifdef _SC_REGEX_VERSION
+ { "_REGEX_VERSION", _SC_REGEX_VERSION, SYSCONF },
+#endif
+ { "_POSIX_SHELL", _SC_SHELL, SYSCONF },
+#ifdef _SC_SIGNALS
+ { "_POSIX_SIGNALS", _SC_SIGNALS, SYSCONF },
+#endif
+ { "_POSIX_SPAWN", _SC_SPAWN, SYSCONF },
+#ifdef _SC_SPORADIC_SERVER
+ { "_POSIX_SPORADIC_SERVER", _SC_SPORADIC_SERVER, SYSCONF },
+#endif
+#ifdef _SC_THREAD_SPORADIC_SERVER
+ { "_POSIX_THREAD_SPORADIC_SERVER", _SC_THREAD_SPORADIC_SERVER, SYSCONF },
+#endif
+#ifdef _SC_SYSTEM_DATABASE
+ { "_POSIX_SYSTEM_DATABASE", _SC_SYSTEM_DATABASE, SYSCONF },
+#endif
+#ifdef _SC_SYSTEM_DATABASE_R
+ { "_POSIX_SYSTEM_DATABASE_R", _SC_SYSTEM_DATABASE_R, SYSCONF },
+#endif
+#ifdef _SC_TIMEOUTS
+ { "_POSIX_TIMEOUTS", _SC_TIMEOUTS, SYSCONF },
+#endif
+#ifdef _SC_TYPED_MEMORY_OBJECTS
+ { "_POSIX_TYPED_MEMORY_OBJECTS", _SC_TYPED_MEMORY_OBJECTS, SYSCONF },
+#endif
+#ifdef _SC_USER_GROUPS
+ { "_POSIX_USER_GROUPS", _SC_USER_GROUPS, SYSCONF },
+#endif
+#ifdef _SC_USER_GROUPS_R
+ { "_POSIX_USER_GROUPS_R", _SC_USER_GROUPS_R, SYSCONF },
+#endif
+ { "POSIX2_PBS", _SC_2_PBS, SYSCONF },
+ { "POSIX2_PBS_ACCOUNTING", _SC_2_PBS_ACCOUNTING, SYSCONF },
+ { "POSIX2_PBS_LOCATE", _SC_2_PBS_LOCATE, SYSCONF },
+ { "POSIX2_PBS_TRACK", _SC_2_PBS_TRACK, SYSCONF },
+ { "POSIX2_PBS_MESSAGE", _SC_2_PBS_MESSAGE, SYSCONF },
+ { "SYMLOOP_MAX", _SC_SYMLOOP_MAX, SYSCONF },
+ { "STREAM_MAX", _SC_STREAM_MAX, SYSCONF },
+ { "AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX, SYSCONF },
+ { "AIO_MAX", _SC_AIO_MAX, SYSCONF },
+#ifdef _SC_AIO_PRIO_DELTA_MAX
+ { "AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX, SYSCONF },
+#endif
+ { "DELAYTIMER_MAX", _SC_DELAYTIMER_MAX, SYSCONF },
+ { "HOST_NAME_MAX", _SC_HOST_NAME_MAX, SYSCONF },
+ { "LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX, SYSCONF },
+ { "MQ_OPEN_MAX", _SC_MQ_OPEN_MAX, SYSCONF },
+ { "MQ_PRIO_MAX", _SC_MQ_PRIO_MAX, SYSCONF },
+#ifdef _SC_DEVICE_IO
+ { "_POSIX_DEVICE_IO", _SC_DEVICE_IO, SYSCONF },
+#endif
+#ifdef _SC_TRACE
+ { "_POSIX_TRACE", _SC_TRACE, SYSCONF },
+#endif
+#ifdef _SC_TRACE_EVENT_FILTER
+ { "_POSIX_TRACE_EVENT_FILTER", _SC_TRACE_EVENT_FILTER, SYSCONF },
+#endif
+#ifdef _SC_TRACE_INHERIT
+ { "_POSIX_TRACE_INHERIT", _SC_TRACE_INHERIT, SYSCONF },
+#endif
+#ifdef _SC_TRACE_LOG
+ { "_POSIX_TRACE_LOG", _SC_TRACE_LOG, SYSCONF },
+#endif
+ { "RTSIG_MAX", _SC_RTSIG_MAX, SYSCONF },
+#ifdef _SC_SEM_NSEMS_MAX
+ { "SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX, SYSCONF },
+#endif
+#ifdef _SC_SEM_VALUE_MAX
+ { "SEM_VALUE_MAX", _SC_SEM_VALUE_MAX, SYSCONF },
+#endif
+ { "SIGQUEUE_MAX", _SC_SIGQUEUE_MAX, SYSCONF },
+ { "FILESIZEBITS", _PC_FILESIZEBITS, PATHCONF },
+#ifdef _PC_ALLOC_SIZE_MIN
+ { "POSIX_ALLOC_SIZE_MIN", _PC_ALLOC_SIZE_MIN, PATHCONF },
+#endif
+#ifdef _PC_REC_INCR_XFER_SIZE
+ { "POSIX_REC_INCR_XFER_SIZE", _PC_REC_INCR_XFER_SIZE, PATHCONF },
+#endif
+#ifdef _PC_REC_MAX_XFER_SIZE
+ { "POSIX_REC_MAX_XFER_SIZE", _PC_REC_MAX_XFER_SIZE, PATHCONF },
+#endif
+#ifdef _PC_REC_MIN_XFER_SIZE
+ { "POSIX_REC_MIN_XFER_SIZE", _PC_REC_MIN_XFER_SIZE, PATHCONF },
+#endif
+#ifdef _PC_REC_XFER_ALIGN
+ { "POSIX_REC_XFER_ALIGN", _PC_REC_XFER_ALIGN, PATHCONF },
+#endif
+ { "SYMLINK_MAX", _PC_SYMLINK_MAX, PATHCONF },
+#ifdef _PC_2_SYMLINKS
+ { "POSIX2_SYMLINKS", _PC_2_SYMLINKS, PATHCONF },
+#endif
+
+#ifdef _SC_LEVEL1_ICACHE_SIZE
+ { "LEVEL1_ICACHE_SIZE", _SC_LEVEL1_ICACHE_SIZE, SYSCONF },
+ { "LEVEL1_ICACHE_ASSOC", _SC_LEVEL1_ICACHE_ASSOC, SYSCONF },
+ { "LEVEL1_ICACHE_LINESIZE", _SC_LEVEL1_ICACHE_LINESIZE, SYSCONF },
+ { "LEVEL1_DCACHE_SIZE", _SC_LEVEL1_DCACHE_SIZE, SYSCONF },
+ { "LEVEL1_DCACHE_ASSOC", _SC_LEVEL1_DCACHE_ASSOC, SYSCONF },
+ { "LEVEL1_DCACHE_LINESIZE", _SC_LEVEL1_DCACHE_LINESIZE, SYSCONF },
+#endif
+#ifdef _SC_LEVEL2_CACHE_SIZE
+ { "LEVEL2_CACHE_SIZE", _SC_LEVEL2_CACHE_SIZE, SYSCONF },
+ { "LEVEL2_CACHE_ASSOC", _SC_LEVEL2_CACHE_ASSOC, SYSCONF },
+ { "LEVEL2_CACHE_LINESIZE", _SC_LEVEL2_CACHE_LINESIZE, SYSCONF },
+#endif
+#ifdef _SC_LEVEL3_CACHE_SIZE
+ { "LEVEL3_CACHE_SIZE", _SC_LEVEL3_CACHE_SIZE, SYSCONF },
+ { "LEVEL3_CACHE_ASSOC", _SC_LEVEL3_CACHE_ASSOC, SYSCONF },
+ { "LEVEL3_CACHE_LINESIZE", _SC_LEVEL3_CACHE_LINESIZE, SYSCONF },
+#endif
+#ifdef _SC_LEVEL4_CACHE_SIZE
+ { "LEVEL4_CACHE_SIZE", _SC_LEVEL4_CACHE_SIZE, SYSCONF },
+ { "LEVEL4_CACHE_ASSOC", _SC_LEVEL4_CACHE_ASSOC, SYSCONF },
+ { "LEVEL4_CACHE_LINESIZE", _SC_LEVEL4_CACHE_LINESIZE, SYSCONF },
+#endif
+
+#ifdef _SC_IPV6
+ { "IPV6", _SC_IPV6, SYSCONF },
+ { "_POSIX_IPV6", _SC_IPV6, SYSCONF },
+#endif
+#ifdef _SC_RAW_SOCKETS
+ { "RAW_SOCKETS", _SC_RAW_SOCKETS, SYSCONF },
+ { "_POSIX_RAW_SOCKETS", _SC_RAW_SOCKETS, SYSCONF },
+#endif
+
+ { NULL, 0, SYSCONF }
+ };
+
+static int getconf_print (const struct conf *, const char *, int);
+static int getconf_all (void);
+static int getconf_one (WORD_LIST *);
+static int getconf_internal (const struct conf *, int);
+
+static int
+getconf_internal (const struct conf *c, int all)
+{
+ long l, val;
+ intmax_t v;
+ uintmax_t uv;
+ unsigned long ul;
+#if HAVE_LONG_LONG_INT
+ unsigned long long ull;
+ long long ll;
+#endif
+ int r;
+
+ val = c->call_name;
+ r = EXECUTION_SUCCESS;
+ if (val == VAL_ULONG_MAX)
+ {
+ ul = ULONG_MAX;
+ printf ("%lu", ul);
+ }
+#if HAVE_LONG_LONG_INT
+ else if (val == VAL_ULLONG_MAX)
+ {
+ ull = ULLONG_MAX;
+ printf ("%llu", ull);
+ }
+ else if (val == VAL_LLONG_MIN || val == VAL_LLONG_MAX)
+ {
+ ll = (val == VAL_LLONG_MIN) ? LLONG_MIN : LLONG_MAX;
+ printf ("%lld", ll);
+ }
+#endif
+#if HAVE_LONG_LONG_INT
+ else if (val == VAL_SSIZE_MAX)
+ {
+ ll = SSIZE_MAX;
+ printf ("%lld", ll);
+ }
+ else if (val == VAL_SIZE_MAX)
+ {
+ ul = SIZE_MAX;
+ printf ("%lu", ul);
+ }
+#else
+ else if (val == VAL_SSIZE_MAX)
+ {
+ v = SSIZE_MAX;
+ printf ("%jd", v);
+ }
+ else if (val == VAL_SIZE_MAX)
+ {
+ uv = SIZE_MAX;
+ printf ("%ju", uv);
+ }
+#endif
+ else
+ printf ("%ld", val);
+
+ if (r == EXECUTION_SUCCESS)
+ printf ("\n");
+
+ return (r);
+}
+
+static int
+getconf_all (void)
+{
+ const struct conf *c;
+ char *path;
+ int r;
+
+ r = EXECUTION_SUCCESS;
+ for (c = vars; c->name != NULL; ++c)
+ {
+ printf("%-35s", c->name);
+ path = "/"; /* XXX for now */
+ if (getconf_print (c, path, 1) == EXECUTION_FAILURE)
+ r = EXECUTION_FAILURE;
+ }
+ return (r);
+}
+
+static int
+getconf_one (WORD_LIST *list)
+{
+ const struct conf *c;
+ char *vname, *vpath;
+
+ vname = list->word->word;
+ vpath = (list->next && list->next->word) ? list->next->word->word : 0;
+
+ for (c = vars; c->name != NULL; ++c)
+ {
+ if (strcmp (c->name, vname) == 0 || (strncmp (c->name, "_POSIX_", 7) == 0 &&
+ strcmp (c->name + 7, vname) == 0))
+ break;
+ }
+ if (c->name == NULL)
+ {
+ builtin_error ("%s: unknown variable", vname);
+ return (EXECUTION_FAILURE);
+ }
+
+ if (c->call_name == PATHCONF && list->next == 0)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ else if (c->call_name != PATHCONF && list->next)
+ {
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+
+ return (getconf_print (c, vpath, 0));
+}
+
+static int
+getconf_print (const struct conf *c, const char *vpath, int all)
+{
+ long value;
+ size_t clen;
+ char *cvalue;
+ int cn;
+
+ cn = c->call_name;
+ switch (c->call)
+ {
+ case PATHCONF:
+ errno = 0;
+ value = pathconf (vpath, cn);
+ if (value == -1L)
+ {
+ if (errno)
+ builtin_error ("pathconf %s: %s: %s", c->name, vpath, strerror (errno));
+ printf ("undefined\n");
+ return (EXECUTION_FAILURE);
+ }
+ else
+ printf ("%ld\n", value);
+ return (EXECUTION_SUCCESS);
+
+ case SYSCONF:
+ errno = 0;
+ value = sysconf (cn);
+ if (value == -1L)
+ {
+ if (errno)
+ builtin_error ("%s: %s", c->name, strerror (errno));
+ printf ("undefined\n");
+ return (EXECUTION_FAILURE);
+ }
+ else
+ printf ("%ld\n", value);
+ return (EXECUTION_SUCCESS);
+
+ case CONFSTR:
+ errno = 0;
+ clen = confstr (cn, (char *) NULL, 0);
+ cvalue = (char *) malloc (clen);
+ if (cvalue == NULL)
+ {
+ builtin_error ("memory allocation failure");
+ return (EXECUTION_FAILURE);
+ }
+ if (confstr (c->call_name, cvalue, clen) != clen)
+ {
+ if (errno != 0)
+ builtin_error ("%s: confstr: %s", c->name, strerror (errno));
+ printf ("undefined\n");
+ return (EXECUTION_FAILURE);
+ }
+ else
+ printf ("%.*s\n", (int) clen, cvalue);
+ free (cvalue);
+ return (EXECUTION_SUCCESS);
+
+ case CONSTANT:
+ return (getconf_internal (c, all));
+
+ case UNDEFINED:
+ builtin_error ("%s: undefined", c->name);
+ return (EXECUTION_FAILURE);
+ }
+
+ /* NOTREACHED */
+ return (EX_USAGE);
+}
+
+int
+getconf_builtin (WORD_LIST *list)
+{
+ const struct conf *c;
+ int r, opt, aflag, vflag;
+ char *varg;
+
+ aflag = vflag = 0;
+ varg = 0;
+ reset_internal_getopt ();
+ while ((opt = internal_getopt (list, "ahv:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'a':
+ aflag = 1;
+ break;
+ CASE_HELPOPT;
+ case 'h':
+ builtin_help ();
+ return (EX_USAGE);
+ case 'v':
+ return (EX_DISKFALLBACK);
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
+ }
+
+ list = loptend;
+ if ((aflag == 0 && list == 0) || (aflag && list) || list_length(list) > 2)
+ {
+ builtin_usage();
+ return (EX_USAGE);
+ }
+
+ r = aflag ? getconf_all () : getconf_one (list);
+ return r;
+}
+
+static char *getconf_doc[] = {
+ "Display values of system limits and options.",
+ "",
+ "getconf writes the current value of a configurable system limit or",
+ "option variable to the standard output.",
+ (char *)NULL
+};
+
+struct builtin getconf_struct = {
+ "getconf",
+ getconf_builtin,
+ BUILTIN_ENABLED,
+ getconf_doc,
+ "getconf -[ah] or getconf [-v spec] sysvar or getconf [-v spec] pathvar pathname",
+ 0
+};
diff --git a/examples/loadables/getconf.h b/examples/loadables/getconf.h
new file mode 100644
index 0000000..b8b9f10
--- /dev/null
+++ b/examples/loadables/getconf.h
@@ -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 */
diff --git a/examples/loadables/head.c b/examples/loadables/head.c
new file mode 100644
index 0000000..f9f022a
--- /dev/null
+++ b/examples/loadables/head.c
@@ -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 */
+};
diff --git a/examples/loadables/hello.c b/examples/loadables/hello.c
new file mode 100644
index 0000000..b09362b
--- /dev/null
+++ b/examples/loadables/hello.c
@@ -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 */
+};
diff --git a/examples/loadables/id.c b/examples/loadables/id.c
new file mode 100644
index 0000000..f857b54
--- /dev/null
+++ b/examples/loadables/id.c
@@ -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
+};
diff --git a/examples/loadables/ln.c b/examples/loadables/ln.c
new file mode 100644
index 0000000..874e9db
--- /dev/null
+++ b/examples/loadables/ln.c
@@ -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 */
+};
diff --git a/examples/loadables/loadables.h b/examples/loadables/loadables.h
new file mode 100644
index 0000000..c730357
--- /dev/null
+++ b/examples/loadables/loadables.h
@@ -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
diff --git a/examples/loadables/logname.c b/examples/loadables/logname.c
new file mode 100644
index 0000000..27e6591
--- /dev/null
+++ b/examples/loadables/logname.c
@@ -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
+};
+
diff --git a/examples/loadables/mkdir.c b/examples/loadables/mkdir.c
new file mode 100644
index 0000000..a5b4930
--- /dev/null
+++ b/examples/loadables/mkdir.c
@@ -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
+};
diff --git a/examples/loadables/mkfifo.c b/examples/loadables/mkfifo.c
new file mode 100644
index 0000000..2bc4e98
--- /dev/null
+++ b/examples/loadables/mkfifo.c
@@ -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
+};
diff --git a/examples/loadables/mktemp.c b/examples/loadables/mktemp.c
new file mode 100644
index 0000000..1f7c9ad
--- /dev/null
+++ b/examples/loadables/mktemp.c
@@ -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 */
+};
diff --git a/examples/loadables/mypid.c b/examples/loadables/mypid.c
new file mode 100644
index 0000000..fc1b267
--- /dev/null
+++ b/examples/loadables/mypid.c
@@ -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
+};
diff --git a/examples/loadables/necho.c b/examples/loadables/necho.c
new file mode 100644
index 0000000..dd2092b
--- /dev/null
+++ b/examples/loadables/necho.c
@@ -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
+};
+
diff --git a/examples/loadables/pathchk.c b/examples/loadables/pathchk.c
new file mode 100644
index 0000000..c1151db
--- /dev/null
+++ b/examples/loadables/pathchk.c
@@ -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;
+}
diff --git a/examples/loadables/perl/Makefile.in b/examples/loadables/perl/Makefile.in
new file mode 100644
index 0000000..59f39b6
--- /dev/null
+++ b/examples/loadables/perl/Makefile.in
@@ -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
diff --git a/examples/loadables/perl/README b/examples/loadables/perl/README
new file mode 100644
index 0000000..a70a99b
--- /dev/null
+++ b/examples/loadables/perl/README
@@ -0,0 +1,6 @@
+This illustrates how to build a perl interpreter into bash. It's not
+especially useful; more a proof of concept (it provides none of the
+bash internals to the perl interpreter, for example).
+
+This *may* require adding "-rpath /path/to/perl/CORE" and -lperl options
+when compiling bash itself.
diff --git a/examples/loadables/perl/bperl.c b/examples/loadables/perl/bperl.c
new file mode 100644
index 0000000..5b15258
--- /dev/null
+++ b/examples/loadables/perl/bperl.c
@@ -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
+};
diff --git a/examples/loadables/perl/iperl.c b/examples/loadables/perl/iperl.c
new file mode 100644
index 0000000..89438af
--- /dev/null
+++ b/examples/loadables/perl/iperl.c
@@ -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);
+}
diff --git a/examples/loadables/print.c b/examples/loadables/print.c
new file mode 100644
index 0000000..d6c5903
--- /dev/null
+++ b/examples/loadables/print.c
@@ -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);
+}
diff --git a/examples/loadables/printenv.c b/examples/loadables/printenv.c
new file mode 100644
index 0000000..8c7f720
--- /dev/null
+++ b/examples/loadables/printenv.c
@@ -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
+};
diff --git a/examples/loadables/push.c b/examples/loadables/push.c
new file mode 100644
index 0000000..b27455b
--- /dev/null
+++ b/examples/loadables/push.c
@@ -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
+};
diff --git a/examples/loadables/realpath.c b/examples/loadables/realpath.c
new file mode 100644
index 0000000..ef83671
--- /dev/null
+++ b/examples/loadables/realpath.c
@@ -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 */
+};
diff --git a/examples/loadables/rm.c b/examples/loadables/rm.c
new file mode 100644
index 0000000..d8d1522
--- /dev/null
+++ b/examples/loadables/rm.c
@@ -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 */
+};
diff --git a/examples/loadables/rmdir.c b/examples/loadables/rmdir.c
new file mode 100644
index 0000000..001c2bd
--- /dev/null
+++ b/examples/loadables/rmdir.c
@@ -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 */
+};
diff --git a/examples/loadables/seq.c b/examples/loadables/seq.c
new file mode 100644
index 0000000..f8eec5b
--- /dev/null
+++ b/examples/loadables/seq.c
@@ -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
+};
diff --git a/examples/loadables/setpgid.c b/examples/loadables/setpgid.c
new file mode 100644
index 0000000..e56fd51
--- /dev/null
+++ b/examples/loadables/setpgid.c
@@ -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
+};
diff --git a/examples/loadables/sleep.c b/examples/loadables/sleep.c
new file mode 100644
index 0000000..204601f
--- /dev/null
+++ b/examples/loadables/sleep.c
@@ -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
+};
diff --git a/examples/loadables/stat.c b/examples/loadables/stat.c
new file mode 100644
index 0000000..1e60e7b
--- /dev/null
+++ b/examples/loadables/stat.c
@@ -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 */
+};
diff --git a/examples/loadables/strftime.c b/examples/loadables/strftime.c
new file mode 100644
index 0000000..f4e194e
--- /dev/null
+++ b/examples/loadables/strftime.c
@@ -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 */
+};
diff --git a/examples/loadables/sync.c b/examples/loadables/sync.c
new file mode 100644
index 0000000..4fbeee1
--- /dev/null
+++ b/examples/loadables/sync.c
@@ -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 */
+};
diff --git a/examples/loadables/tee.c b/examples/loadables/tee.c
new file mode 100644
index 0000000..0365aa9
--- /dev/null
+++ b/examples/loadables/tee.c
@@ -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 */
+};
diff --git a/examples/loadables/template.c b/examples/loadables/template.c
new file mode 100644
index 0000000..094b80c
--- /dev/null
+++ b/examples/loadables/template.c
@@ -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 */
+};
diff --git a/examples/loadables/truefalse.c b/examples/loadables/truefalse.c
new file mode 100644
index 0000000..e011fa1
--- /dev/null
+++ b/examples/loadables/truefalse.c
@@ -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
+};
diff --git a/examples/loadables/tty.c b/examples/loadables/tty.c
new file mode 100644
index 0000000..381df25
--- /dev/null
+++ b/examples/loadables/tty.c
@@ -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 */
+};
diff --git a/examples/loadables/uname.c b/examples/loadables/uname.c
new file mode 100644
index 0000000..106a1c8
--- /dev/null
+++ b/examples/loadables/uname.c
@@ -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
+};
diff --git a/examples/loadables/unlink.c b/examples/loadables/unlink.c
new file mode 100644
index 0000000..ff2a78a
--- /dev/null
+++ b/examples/loadables/unlink.c
@@ -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 */
+};
diff --git a/examples/loadables/whoami.c b/examples/loadables/whoami.c
new file mode 100644
index 0000000..3e7e36e
--- /dev/null
+++ b/examples/loadables/whoami.c
@@ -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
+};
diff --git a/examples/misc/aliasconv.bash b/examples/misc/aliasconv.bash
new file mode 100644
index 0000000..22a0447
--- /dev/null
+++ b/examples/misc/aliasconv.bash
@@ -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
diff --git a/examples/misc/aliasconv.sh b/examples/misc/aliasconv.sh
new file mode 100644
index 0000000..fe975d6
--- /dev/null
+++ b/examples/misc/aliasconv.sh
@@ -0,0 +1,42 @@
+#! /bin/bash
+#
+# aliasconv.sh - convert csh aliases to bash aliases and functions
+#
+# usage: aliasconv.sh
+#
+# Chet Ramey
+# chet@po.cwru.edu
+#
+trap 'rm -f $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
diff --git a/examples/misc/cshtobash b/examples/misc/cshtobash
new file mode 100644
index 0000000..ce49bfc
--- /dev/null
+++ b/examples/misc/cshtobash
@@ -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
diff --git a/examples/scripts/bcalc b/examples/scripts/bcalc
new file mode 100644
index 0000000..bc7e2b4
--- /dev/null
+++ b/examples/scripts/bcalc
@@ -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
diff --git a/examples/scripts/cat.sh b/examples/scripts/cat.sh
new file mode 100644
index 0000000..3e65b3f
--- /dev/null
+++ b/examples/scripts/cat.sh
@@ -0,0 +1,12 @@
+shcat()
+{
+ while read -r ; do
+ printf "%s\n" "$REPLY"
+ done
+}
+
+if [ -n "$1" ]; then
+ shcat < "$1"
+else
+ shcat
+fi
diff --git a/examples/scripts/center b/examples/scripts/center
new file mode 100644
index 0000000..dbe6133
--- /dev/null
+++ b/examples/scripts/center
@@ -0,0 +1,41 @@
+#! /bin/bash
+#
+# center - center a group of lines
+#
+# tabs in the lines might cause this to look a little bit off
+#
+#
+# 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.
+
+width=${COLUMNS:-80}
+
+if [[ $# == 0 ]]
+then
+ set -- /dev/stdin
+fi
+
+for file
+do
+ while read -r
+ do
+ printf "%*s\n" $(( (width+${#REPLY})/2 )) "$REPLY"
+ done < $file
+done
+
+exit 0
diff --git a/examples/scripts/inpath b/examples/scripts/inpath
new file mode 100644
index 0000000..95f28bc
--- /dev/null
+++ b/examples/scripts/inpath
@@ -0,0 +1,19 @@
+#! /bin/sh
+#
+# Search $PATH for a file the same name as $1; return TRUE if found.
+#
+
+command=$1
+[ -n "$command" ] || exit 1
+
+set `echo $PATH | sed 's/^:/.:/
+ s/::/:.:/g
+ s/:$/:./
+ s/:/ /g'`
+
+while [ $# -ne 0 ] ; do
+ [ -f $1/$command ] && exit 0 # test -x not universal
+ shift
+done
+
+exit 1
diff --git a/examples/scripts/shprompt b/examples/scripts/shprompt
new file mode 100644
index 0000000..098c45f
--- /dev/null
+++ b/examples/scripts/shprompt
@@ -0,0 +1,153 @@
+#
+# shprompt -- give a prompt and get an answer satisfying certain criteria
+#
+# shprompt [-dDfFsy] prompt
+# s = prompt for string
+# f = prompt for filename
+# F = prompt for full pathname to a file or directory
+# d = prompt for a directory name
+# D = prompt for a full pathname to a directory
+# y = prompt for y or n answer
+#
+# Chet Ramey
+# chet@ins.CWRU.Edu
+#
+# 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.
+
+type=file
+
+OPTS=dDfFsy
+
+succeed()
+{
+ echo "$1"
+ exit 0
+}
+
+while getopts "$OPTS" c
+do
+ case "$c" in
+ s) type=string
+ ;;
+ f) type=file
+ ;;
+ F) type=path
+ ;;
+ d) type=dir
+ ;;
+ D) type=dirpath
+ ;;
+ y) type=yesno
+ ;;
+ ?) echo "usage: $0 [-$OPTS] prompt" 1>&2
+ exit 2
+ ;;
+ esac
+done
+
+if [ "$OPTIND" -gt 1 ] ; then
+ shift $(( $OPTIND - 1 ))
+fi
+
+while :
+do
+ case "$type" in
+ string)
+ echo -n "$1" 1>&2
+ read ans || exit 1
+ if [ -n "$ans" ] ; then
+ succeed "$ans"
+ fi
+ ;;
+ file|path)
+ echo -n "$1" 1>&2
+ read ans || exit 1
+ #
+ # use `fn' and eval so that bash will do tilde expansion for
+ # me
+ #
+ eval fn="$ans"
+ case "$fn" in
+ /*) if test -e "$fn" ; then
+ succeed "$fn"
+ else
+ echo "$0: '$fn' does not exist" 1>&2
+ fi
+ ;;
+ *) if [ "$type" = "path" ] ; then
+ echo "$0: must give full pathname to file" 1>&2
+ else
+ if test -e "$fn" ; then
+ succeed "$fn"
+ else
+ echo "$0: '$fn' does not exist" 1>&2
+ fi
+ fi
+ ;;
+ esac
+ ;;
+ dir|dirpath)
+ echo -n "$1" 1>&2
+ read ans || exit 1
+ #
+ # use `fn' and eval so that bash will do tilde expansion for
+ # me
+ #
+ eval fn="$ans"
+ case "$fn" in
+ /*) if test -d "$fn" ; then
+ succeed "$fn"
+ elif test -e "$fn" ; then
+ echo "$0 '$fn' is not a directory" 1>&2
+ else
+ echo "$0: '$fn' does not exist" 1>&2
+ fi
+ ;;
+ *) if [ "$type" = "dirpath" ] ; then
+ echo "$0: must give full pathname to directory" 1>&2
+ else
+ if test -d "$fn" ; then
+ succeed "$fn"
+ elif test -e "$fn" ; then
+ echo "$0 '$fn' is not a directory" 1>&2
+ else
+ echo "$0: '$fn' does not exist" 1>&2
+ fi
+ fi
+ ;;
+ esac
+ ;;
+ yesno)
+ echo -n "$1" 1>&2
+ read ans || exit 1
+ case "$ans" in
+ y|Y|[yY][eE][sS])
+ succeed "yes"
+ ;;
+ n|N|[nN][oO])
+ succeed "no"
+ exit 0
+ ;;
+ *)
+ echo "$0: yes or no required" 1>&2
+ ;;
+ esac
+ ;;
+ esac
+done
+
+exit 1
diff --git a/examples/scripts/spin.bash b/examples/scripts/spin.bash
new file mode 100644
index 0000000..9fa9125
--- /dev/null
+++ b/examples/scripts/spin.bash
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+# spin.bash -- provide a `spinning wheel' to show progress
+#
+# Chet Ramey
+# chet@po.cwru.edu
+#
+# Copyright 1997 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.
+
+bs=$'\b'
+
+chars="|${bs} \\${bs} -${bs} /${bs}"
+
+# Infinite loop for demo. purposes
+while :
+do
+ for letter in $chars
+ do
+ echo -n ${letter}
+ done
+done
+
+exit 0
diff --git a/examples/scripts/xterm_title b/examples/scripts/xterm_title
new file mode 100644
index 0000000..839003f
--- /dev/null
+++ b/examples/scripts/xterm_title
@@ -0,0 +1,44 @@
+#! /bin/bash
+#
+# xterm_title - print the contents of the xterm title bar
+#
+# Derived from http://www.clark.net/pub/dickey/xterm/xterm.faq.html#how2_title
+#
+
+# Copyright 1997 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.
+
+P=${0##*/}
+[ -z "$DISPLAY" ] && {
+ echo "${P}: not running X" >&2
+ exit 1
+}
+
+if [ -z "$TERM" ] || [ "$TERM" != "xterm" ]; then
+ echo "${P}: not running in an xterm" >&2
+ exit 1
+fi
+
+exec </dev/tty
+old=$(stty -g)
+stty raw -echo min 0 time ${1-10}
+echo -e "\033[21t\c" > /dev/tty
+IFS='' read -r a
+stty $old
+b=${a#???}
+echo "${b%??}"
+
+exit 0
diff --git a/examples/scripts/zprintf b/examples/scripts/zprintf
new file mode 100644
index 0000000..86f9e95
--- /dev/null
+++ b/examples/scripts/zprintf
@@ -0,0 +1,43 @@
+#! /bin/bash
+#
+# zprintf - function that calls gawk to do printf for those systems that
+# don't have a printf executable
+#
+# The format and arguments can have trailing commas, just like gawk
+#
+# example:
+# zprintf 'Eat %x %x and suck %x!\n' 57005 48879 64206
+#
+# Chet Ramey
+# chet@po.cwru.edu
+
+# Copyright 1996 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.
+
+
+[ $# -lt 1 ] && {
+ echo "zprintf: usage: zprintf format [args ...]" >&2
+ exit 2
+}
+
+fmt="${1%,}"
+shift
+
+for a in "$@"; do
+ args="$args,\"${a%,}\""
+done
+
+gawk "BEGIN { printf \"$fmt\" $args }"
diff --git a/examples/shellmath/LICENSE b/examples/shellmath/LICENSE
new file mode 100644
index 0000000..e3bf3bb
--- /dev/null
+++ b/examples/shellmath/LICENSE
@@ -0,0 +1,677 @@
+Shellfloat is copyright (c) 2020 by Michael Wood.
+================================================================================
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/examples/shellmath/README.md b/examples/shellmath/README.md
new file mode 100644
index 0000000..1b47256
--- /dev/null
+++ b/examples/shellmath/README.md
@@ -0,0 +1,166 @@
+# Shellmath
+Introducing decimal arithmetic libraries for the Bash shell, because
+they said it couldn't be done... and because:
+
+.
+
+![image info](./image.png)
+
+## Quick-start guide
+Download this project and source the file `shellmath.sh` into your shell script,
+then fire away at the shellmath API!
+
+The ___basic___ API looks like this:
+```
+ _shellmath_add arg1 arg2 [...] argN
+ _shellmath_subtract arg1 arg2 # means arg1 - arg2
+ _shellmath_multiply arg1 arg2 [...] argN
+ _shellmath_divide arg1 arg2 # means arg1 / arg2
+```
+
+The ___extended___ API introduces one more function:
+```
+ _shellmath_getReturnValue arg
+```
+
+This function optimizes away the need for ___$(___ subshelling ___)___ in order to capture `shellmath`'s output.
+To use this feature, just be sure to set `__shellmath_isOptimized=1` at the top
+of your script. (You can find an example in `faster_e_demo.sh`.)
+
+Operands to the _shellmath_ functions can be integers or decimal
+numbers presented in either standard or scientific notation:
+```
+ _shellmath_add 1.009 4.223e-2
+ _shellmath_getReturnValue sum
+ echo "The sum is $sum"
+```
+Addition and multiplication are of arbitrary arity; try this on for size:
+```
+ _shellmath_multiply 1 2 3 4 5 6
+ _shellmath_getReturnValue sixFactorial
+ echo "6 factorial is $sixFactorial"
+```
+Subtraction and division, OTOH, are exclusively binary operations.
+
+## The demos
+For a gentle introduction to `shellmath` run the demo `slower_e_demo.sh`
+with a small whole-number argument, say 15:
+```
+$ slower_e_demo.sh 15
+e = 2.7182818284589936
+```
+
+This script uses a few `shellmath` API calls to calculate *e*, the mathematical
+constant also known as [Euler's number](https://oeis.org/A001113). The argument
+*15* tells the script to evaluate the *15th-degree* Maclaurin polynomial for *e*.
+(That's the Taylor polynomial centered at 0.) Take a look inside the script to
+see how it uses the `shellmath` APIs.
+
+There is another demo script very much like this one but *different*, and the
+sensitive user can *feel* the difference. Try the following, but don't blink
+or you'll miss it ;)
+```
+$ faster_e_demo.sh 15
+e = 2.7182818284589936
+```
+
+Did you feel the difference? Try the `-t` option with both scripts; this will produce
+timing statistics. Here are my results
+when running from my minGW64 command prompt on Windows 10 with an Intel i3 Core CPU:
+```
+$ for n in {1..5}; do faster_e_demo.sh -t 15 2>&1; done | awk '/^real/ {print $2}'
+0m0.055s
+0m0.051s
+0m0.056s
+0m0.054s
+0m0.054s
+
+$ for n in {1..5}; do slower_e_demo.sh -t 15 2>&1; done | awk '/^real/ {print $2}'
+0m0.498s
+0m0.594s
+0m0.536s
+0m0.511s
+0m0.580s
+```
+
+(When sizing up these timings, do keep in mind that ___we are timing the
+calculation of e from its Maclaurin polynomial. Every invocation of either
+script is exercising the shellmath arithmetic subroutines 31 times.___)
+
+The comment header in `faster_e_demo.sh` explains the optimization and shows
+how to put this faster version to work for you.
+
+## Runtime efficiency competitive with awk and bc
+The file `timingData.txt` captures the results of some timing experiments that compare
+`shellmath` against the GNU versions of the calculators `awk` and `bc`. The experiments
+exercised each of the arithmetic operations and captured the results in a shell variable.
+The result summary below shows that `shellmath` is competitive with `awk` and runs faster
+than `bc` in these experiments. (One commenter noted that the differences in execution speed
+can be partially explained by the fact that `shellmath` and `awk` use finite precision
+whereas `bc` uses arbitrary precision. Another factor in these measurements is the need to
+subshell 'awk' and 'bc' to capture their results, whereas 'shellmath' writes directly to
+the shell's global memory.)
+
+Here are the run times of `shellmath` as a percentage of the `awk` and `bc` equivalents:
+```
+ versus awk versus bc
+ Addition: 82.2% 40.6%
+ Subtraction: 95.9% 50.5%
+ Multiplication: 135.9% 73.3%
+ Division: 80.3% 43.2%
+```
+
+Astute observers will note the experiments provide approximations to the sum, difference,
+product, and quotient of *pi* and *e*. Unfortunately I did not gain insight as to which
+of these values, if any, are
+[transcendental](https://en.wikipedia.org/wiki/Transcendental_number#Possible_transcendental_numbers).
+
+You can find a deeper discussion of shellmath's runtime efficiency
+[here](https://github.com/clarity20/shellmath/wiki/Shellmath-and-runtime-efficiency).
+
+## Background
+The Bash shell does not have built-in operators for decimal arithmetic, making it
+something of an oddity among well-known, widely-used programming languages. For the most part,
+practitioners in need of powerful computational building blocks have naturally opted
+for *other* languages and tools. Their widespread availability has diverted attention
+from the possibility of *implementing* decimal arithmetic in Bash and it's easy to assume
+that this ***cannot*** be done:
+
++ From the indispensable _Bash FAQ_ (on _Greg's Wiki_): [How can I calculate with floating point numbers?](http://mywiki.wooledge.org/BashFAQ/022)
+ *"For most operations... an external program must be used."*
++ From Mendel Cooper's wonderful and encyclopedic _Advanced Bash Scripting Guide_:
+ [Bash does not understand floating point arithmetic. Use bc instead.](https://tldp.org/LDP/abs/html/ops.html#NOFLOATINGPOINT)
++ From a community discussion on Stack Overflow, _How do I use floating point division in bash?_
+ The user's [preferred answer](https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash#12722107)
+ is a good example of _prevailing thought_ on this subject.
+
+Meanwhile,
+
++ Bash maintainer (BDFL?) Chet Ramey sounds a (brighter?) note in [The Bash Reference Guide, Section 6.5](https://tiswww.case.edu/php/chet/bash/bashref.html#Shell-Arithmetic)
+ by emphasizing what the built-in arithmetic operators ***can*** do.
+
+But finally, a glimmer of hope:
+
++ A [diamond-in-the-rough](http://stackoverflow.com/a/24431665/3776858) buried elsewhere
+ on Stack Overflow.
+ This down-and-dirty milestone computes the decimal quotient of two integer arguments. At a casual
+ glance, it seems to have drawn inspiration from the [Euclidean algorithm](https://mathworld.wolfram.com/EuclideanAlgorithm.html)
+ for computing GCDs, an entirely different approach than `shellmath`'s.
+
+Please try `shellmath` on for size and draw your own conclusions!
+
+## How it works
+`shellmath` splits decimal numbers into their integer and fractional parts,
+performs the appropriate integer operations on the parts, and recombines the results.
+(In the spirit of Bash, numerical overflow is silently ignored.)
+
+Because if we can get carrying, borrowing, place value, and the distributive
+law right, then the sky's the limit! As they say--erm, as they ___said___ in Rome,
+
+ Ad astra per aspera.
+
+## And now...
+You can run your floating-point calculations directly in Bash!
+
+## Please see also:
+[A short discussion on arbitrary precision and shellmath](https://github.com/clarity20/shellmath/wiki/Shellmath-and-arbitrary-precision-arithmetic)
diff --git a/examples/shellmath/assert.sh b/examples/shellmath/assert.sh
new file mode 100644
index 0000000..bc4122e
--- /dev/null
+++ b/examples/shellmath/assert.sh
@@ -0,0 +1,85 @@
+#!/bin/env bash
+###############################################################################
+# Internal test engine functions
+###############################################################################
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NO_COLOR='\033[0m'
+
+function _shellmath_assert_returnCode()
+{
+ _shellmath_assert_functionReturn -c "$@"
+ return $?
+}
+
+function _shellmath_assert_returnString()
+{
+ _shellmath_assert_functionReturn "$@"
+ return $?
+}
+
+function _shellmath_assert_functionReturn()
+{
+ if [[ $# -lt 2 ]]; then
+ echo "USAGE: ${FUNCNAME[0]} [-c] returnStringOrCode functionName [ functionArgs ... ]"
+ echo " By default, asserts against the string output by the function."
+ echo " Use -c to assert against the numeric return code instead."
+ return "${__shellmath_returnCodes[FAIL]}"
+ fi
+
+ if [[ "${1,,}" == '-c' ]]; then
+ mode=RETURN_CODE
+ shift
+ else
+ mode=RETURN_STRING
+ fi
+
+ expectedReturn="$1"
+ func="$2"
+ shift 2
+
+ args=("$@")
+
+ # Exercise the function in optimized mode; it will run faster by avoiding
+ # subshelling. This also suppresses dumping of function output to stdout.
+ __shellmath_isOptimized=${__shellmath_true}
+ "$func" "${args[@]}"
+ returnCode=$?
+ __shellmath_isOptimized=${__shellmath_false}
+
+ # Fetch the return value(s)
+ local numReturnValues
+ declare -a actualReturn
+ _shellmath_getReturnValueCount numReturnValues
+ if ((numReturnValues == 1)); then
+ _shellmath_getReturnValue actualReturn[0]
+ else
+ # Multiple returns? Join them into one string
+ local _i evalString="_shellmath_getReturnValues"
+ for ((_i=0; _i<numReturnValues; _i++)); do
+ evalString+=" actualReturn[$_i]"
+ done
+ eval "$evalString"
+ fi
+
+ if [[ $mode == RETURN_STRING ]]; then
+ if [[ "${actualReturn[*]}" == "$expectedReturn" ]]; then
+ _shellmath_setReturnValue "${GREEN}ok${NO_COLOR} "
+ return "$__shellmath_SUCCESS"
+ else
+ _shellmath_setReturnValue "${RED}FAIL${NO_COLOR} (${actualReturn[*]}) "
+ return "$__shellmath_FAIL"
+ fi
+ elif [[ $mode == RETURN_CODE ]]; then
+ if [[ "$returnCode" == "$expectedReturn" ]]; then
+ _shellmath_setReturnValue "${GREEN}ok${NO_COLOR} "
+ return "$__shellmath_SUCCESS"
+ else
+ _shellmath_setReturnValue "${RED}FAIL${NO_COLOR} ($returnCode) "
+ return "$__shellmath_FAIL"
+ fi
+ fi
+
+}
+
diff --git a/examples/shellmath/faster_e_demo.sh b/examples/shellmath/faster_e_demo.sh
new file mode 100644
index 0000000..84558a2
--- /dev/null
+++ b/examples/shellmath/faster_e_demo.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+
+###############################################################################
+# This script performs the same task as "slower_e_demo.sh" but with a major
+# performance optimization. The speedup is especially noticeable on GNU
+# emulation layers for Windows such as Cygwin and minGW, where the overhead
+# of subshelling is quite significant.
+#
+# The speedup uses global storage space to simulate pass-and-return by
+# reference so that you can capture the side effects of a function call without
+# writing to stdout and wrapping the call in a subshell. How to use:
+#
+# Turn on "__shellmath_isOptimized" as shown below.
+# Then instead of invoking "mySum = $(_shellmath_add $x $y)",
+# call "_shellmath_add $x $y; _shellmath_getReturnValue mySum".
+###############################################################################
+
+source shellmath.sh
+
+# Setting the '-t' flag will cause the script to time the algorithm
+if [[ "$1" == '-t' ]]; then
+ do_timing=${__shellmath_true}
+ shift
+fi
+
+if [[ $# -ne 1 ]]; then
+ echo "USAGE: ${BASH_SOURCE##*/} [-t] *N*"
+ echo " Approximates 'e' using the N-th order Maclaurin polynomial"
+ echo " (i.e. the Taylor polynomial centered at 0)."
+ echo " Specify the '-t' flag to time the main algorithm."
+ exit 0
+elif [[ ! "$1" =~ ^[0-9]+$ ]]; then
+ echo "Illegal argument. Whole numbers only, please."
+ exit 1
+fi
+
+__shellmath_isOptimized=${__shellmath_true}
+
+
+function run_algorithm()
+{
+ # Initialize
+ n=0; N=$1; zero_factorial=1
+
+ # Initialize "e" to its zeroth-order term
+ _shellmath_divide 1 $zero_factorial
+ _shellmath_getReturnValue term
+ e=$term
+
+ # Compute successive terms T(n) := T(n-1)/n and accumulate into e
+ for ((n=1; n<=N; n++)); do
+ _shellmath_divide "$term" "$n"
+ _shellmath_getReturnValue term
+ _shellmath_add "$e" "$term"
+ _shellmath_getReturnValue e
+ done
+
+ echo "e = $e"
+}
+
+if (( do_timing == __shellmath_true )); then
+ time run_algorithm "$1"
+else
+ run_algorithm "$1"
+fi
+
+exit 0
+
diff --git a/examples/shellmath/image.png b/examples/shellmath/image.png
new file mode 100644
index 0000000..4d3b898
--- /dev/null
+++ b/examples/shellmath/image.png
Binary files differ
diff --git a/examples/shellmath/runTests.sh b/examples/shellmath/runTests.sh
new file mode 100644
index 0000000..c9687e5
--- /dev/null
+++ b/examples/shellmath/runTests.sh
@@ -0,0 +1,124 @@
+#!/bin/env bash
+
+###############################################################################
+# runTests.sh
+#
+# Usage: runTests.sh [testFile]
+# where testFile defaults to testCases.in
+#
+# Processes a test file such as the testCases.in included with this package
+###############################################################################
+
+# Process one line from the test cases file. Invoked below through mapfile.
+function _shellmath_runTests()
+{
+ local lineNumber=$1
+ local text=$2
+
+ # Trim leading whitespace
+ [[ $text =~ ^[$' \t']*(.*) ]]
+ text=${BASH_REMATCH[1]}
+
+ # Skip comments and blank lines
+ [[ "$text" =~ ^# || -z $text ]] && return 0
+
+ # Check for line continuation
+ local len="${#text}"
+ if [[ ${text:$((len-1))} == '\' ]]; then
+
+ # Eat the continuation character and add to the buffer
+ __shellfloat_commandBuffer+="${text/%\\/ }"
+
+ # Defer processing
+ return
+
+ # No line continuation
+ else
+
+ # Assemble the command
+ local command=${__shellfloat_commandBuffer}${text}
+ __shellfloat_commandBuffer=""
+
+ words=($command)
+
+ # Expand first word to an assertion function
+ case ${words[0]} in
+
+ Code)
+ words[0]=_shellmath_assert_return${words[0]}
+
+ # Validate next word as a positive integer
+ if [[ ! "${words[1]}" =~ ^[0-9]+$ ]]; then
+ echo Line: "$lineNumber": Command "$command"
+ echo FAIL: \"Code\" requires integer return code
+ return 1
+ else
+ nextWord=2
+ fi
+ ;;
+
+ String)
+ words[0]=_shellmath_assert_return${words[0]}
+ # Allow multiword arguments if quoted
+ if [[ ${words[1]} =~ ^\" ]]; then
+ if [[ ${words[1]} =~ \"$ ]]; then
+ nextWord=2
+ else
+ for ((nextWord=2;;nextWord++)); do
+ if [[ ${words[nextWord]} =~ \"$ ]]; then
+ ((nextWord++))
+ break
+ fi
+ done
+ fi
+ else
+ nextWord=2
+ fi
+ ;;
+
+ Both)
+ ;;
+
+ *)
+ echo -e ${RED}FAIL${NO_COLOR} Line "$lineNumber": Command "$command": Code or String indicator required
+ return 2
+ ;;
+ esac
+
+ # Expand the next word to a shellmath function name
+ words[nextWord]=_shellmath_${words[nextWord]}
+ if ! type -t "${words[nextWord]}" >/dev/null; then
+ echo "${RED}FAIL${NO_COLOR} Line $lineNumber: Command "$command": Syntax error. Required: String|Code value operation args..."
+ return 3
+ fi
+
+ # Run the command, being respectful of shell metacharacters
+ fullCommand="${words[*]}"
+ eval "$fullCommand"
+ local returnString
+ _shellmath_getReturnValue returnString
+ echo -e "$returnString" Line "$lineNumber": "$command"
+
+ fi
+
+}
+
+
+function _main()
+{
+ source shellmath.sh
+ source assert.sh
+
+ # Initialize certain globals. As "public" functions, the arithmetic
+ # functions need to do this themselves, but there are some "private"
+ # functions that need this here when they are auto-tested.
+ _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true
+
+ # Process the test file line-by-line using the above runTests() function
+ mapfile -t -c 1 -C _shellmath_runTests -O 1 < "${1:-testCases.in}"
+
+ exit 0
+}
+
+_main "$@"
+
diff --git a/examples/shellmath/shellmath.sh b/examples/shellmath/shellmath.sh
new file mode 100644
index 0000000..5804ad2
--- /dev/null
+++ b/examples/shellmath/shellmath.sh
@@ -0,0 +1,1068 @@
+#!/bin/env bash
+################################################################################
+# shellmath.sh
+# Shell functions for floating-point arithmetic using only builtins
+#
+# Copyright (c) 2020 by Michael Wood. All rights reserved.
+#
+# Usage:
+#
+# source _thisPath_/_thisFileName_
+#
+# # Conventional method: call the APIs by subshelling
+# mySum=$( _shellmath_add 202.895 6.00311 )
+# echo $mySum
+#
+# # Optimized method: use hidden globals to simulate more flexible pass-and-return
+# _shellmath_isOptimized=1
+# _shellmath_add 44.2 -87
+# _shellmath_getReturnValue mySum
+# echo $mySum
+#
+################################################################################
+
+
+################################################################################
+# Program constants
+################################################################################
+declare -A -r __shellmath_numericTypes=(
+ [INTEGER]=0
+ [DECIMAL]=1
+)
+
+declare -A -r __shellmath_returnCodes=(
+ [SUCCESS]="0:Success"
+ [FAIL]="1:General failure"
+ [ILLEGAL_NUMBER]="2:Invalid argument; decimal number required: '%s'"
+ [DIVIDE_BY_ZERO]="3:Divide by zero error"
+)
+
+declare -r -i __shellmath_true=1
+declare -r -i __shellmath_false=0
+
+declare __shellmath_SUCCESS __shellmath_FAIL __shellmath_ILLEGAL_NUMBER
+
+################################################################################
+# Program state
+################################################################################
+declare __shellmath_isOptimized=${__shellmath_false}
+declare __shellmath_didPrecalc=${__shellmath_false}
+
+
+################################################################################
+# Error-handling utilities
+################################################################################
+function _shellmath_getReturnCode()
+{
+ local errorName=$1
+ return "${__shellmath_returnCodes[$errorName]%%:*}"
+}
+
+function _shellmath_warn()
+{
+ # Generate an error message and return control to the caller
+ _shellmath_handleError -r "$@"
+ return $?
+}
+
+function _shellmath_exit()
+{
+ # Generate an error message and EXIT THE SCRIPT / interpreter
+ _shellmath_handleError "$@"
+}
+
+function _shellmath_handleError()
+{
+ # Hidden option "-r" causes return instead of exit
+ local returnDontExit=$__shellmath_false
+ if [[ "$1" == "-r" ]]; then
+ returnDontExit=${__shellmath_true}
+ shift
+ fi
+
+ # Format of $1: returnCode:msgTemplate
+ [[ "$1" =~ ^([0-9]+):(.*) ]]
+ returnCode=${BASH_REMATCH[1]}
+ msgTemplate=${BASH_REMATCH[2]}
+ shift
+
+ # Display error msg, making parameter substitutions as needed
+ msgParameters="$*"
+ printf "$msgTemplate" "${msgParameters[@]}"
+
+ if ((returnDontExit)); then
+ return "$returnCode"
+ else
+ exit "$returnCode"
+ fi
+}
+
+
+################################################################################
+# precalc()
+#
+# Pre-calculates certain global data and by setting the global variable
+# "__shellmath_didPrecalc" records that this routine has been called. As an
+# optimization, the caller should check that global to prevent needless
+# invocations.
+################################################################################
+function _shellmath_precalc()
+{
+ # Set a few global constants
+ _shellmath_getReturnCode SUCCESS; __shellmath_SUCCESS=$?
+ _shellmath_getReturnCode FAIL; __shellmath_FAIL=$?
+ _shellmath_getReturnCode ILLEGAL_NUMBER; __shellmath_ILLEGAL_NUMBER=$?
+
+ # Determine the decimal precision to which we can accurately calculate.
+ # To do this we probe for the threshold at which integers overflow and
+ # take the integer floor of that number's base-10 logarithm.
+ # We check the 64-bit, 32-bit and 16-bit thresholds only.
+ if ((2**63 < 2**63-1)); then
+ __shellmath_precision=18
+ __shellmath_maxValue=$((2**63-1))
+ elif ((2**31 < 2**31-1)); then
+ __shellmath_precision=9
+ __shellmath_maxValue=$((2**31-1))
+ else ## ((2**15 < 2**15-1))
+ __shellmath_precision=4
+ __shellmath_maxValue=$((2**15-1))
+ fi
+
+ __shellmath_didPrecalc=$__shellmath_true
+}
+
+
+################################################################################
+# Simulate pass-and-return by reference using a secret global storage array
+################################################################################
+
+declare -a __shellmath_storage
+
+function _shellmath_setReturnValues()
+{
+ local -i _i
+
+ for ((_i=1; _i<=$#; _i++)); do
+ __shellmath_storage[_i]="${!_i}"
+ done
+
+ __shellmath_storage[0]=$#
+}
+
+function _shellmath_getReturnValues()
+{
+ local -i _i
+ local evalString
+
+ for ((_i=1; _i<=$#; _i++)); do
+ evalString+=${!_i}="${__shellmath_storage[_i]}"" "
+ done
+
+ eval "$evalString"
+}
+
+function _shellmath_setReturnValue() { __shellmath_storage=(1 "$1"); }
+function _shellmath_getReturnValue() { eval "$1"=\"${__shellmath_storage[1]}\"; }
+function _shellmath_getReturnValueCount() { eval "$1"=\"${__shellmath_storage[0]}\"; }
+
+################################################################################
+# validateAndParse(numericString)
+# Return Code: SUCCESS or ILLEGAL_NUMBER
+# Return Signature: integerPart fractionalPart isNegative numericType isScientific
+#
+# Validate and parse arguments to the main arithmetic routines
+################################################################################
+
+function _shellmath_validateAndParse()
+{
+ local n="$1"
+ local isNegative=${__shellmath_false}
+ local isScientific=${__shellmath_false}
+ local numericType returnCode
+
+ ((returnCode = __shellmath_SUCCESS))
+
+ # Accept decimals: leading digits (optional), decimal point, trailing digits
+ if [[ "$n" =~ ^[-]?([0-9]*)\.([0-9]+)$ ]]; then
+ local integerPart=${BASH_REMATCH[1]:-0}
+ local fractionalPart=${BASH_REMATCH[2]}
+
+ # Strip superfluous trailing zeros
+ if [[ "$fractionalPart" =~ ^(.*[^0])0*$ ]]; then
+ fractionalPart=${BASH_REMATCH[1]}
+ fi
+
+ numericType=${__shellmath_numericTypes[DECIMAL]}
+
+ # Factor out the negative sign if it is present
+ if [[ "$n" =~ ^- ]]; then
+ isNegative=${__shellmath_true}
+ n=${n:1}
+ fi
+
+ _shellmath_setReturnValues "$integerPart" "$fractionalPart" \
+ $isNegative "$numericType" $isScientific
+ return "$returnCode"
+
+ # Accept integers
+ elif [[ "$n" =~ ^[-]?[0-9]+$ ]]; then
+ numericType=${__shellmath_numericTypes[INTEGER]}
+
+ # Factor out the negative sign if it is present
+ if [[ "$n" =~ ^- ]]; then
+ isNegative=${__shellmath_true}
+ n=${n:1}
+ fi
+
+ _shellmath_setReturnValues "$n" 0 $isNegative "$numericType" $isScientific
+ return "$returnCode"
+
+ # Accept scientific notation: 1e5, 2.44E+10, etc.
+ elif [[ "$n" =~ (.*)[Ee](.*) ]]; then
+ local significand=${BASH_REMATCH[1]}
+ local exponent=${BASH_REMATCH[2]}
+
+ # Validate the significand: optional sign, integer part,
+ # optional decimal point and fractional part
+ if [[ "$significand" =~ ^[-]?([0-9]+)(\.([0-9]+))?$ ]]; then
+
+ isScientific=${__shellmath_true}
+
+ # Separate the integer and fractional parts
+ local sigInteger=${BASH_REMATCH[1]}
+ local sigIntLength=${#sigInteger}
+ local sigFraction=${BASH_REMATCH[3]}
+
+ # Strip superfluous trailing zeros
+ if [[ "$sigFraction" =~ ^(.*[^0])0*$ ]]; then
+ sigFraction=${BASH_REMATCH[1]}
+ fi
+
+ local sigFracLength=${#sigFraction}
+
+ if [[ "$n" =~ ^- ]]; then
+ isNegative=${__shellmath_true}
+ n=${n:1}
+ fi
+
+ # Rewrite the scientifically-notated number in ordinary decimal notation.
+ # IOW, realign the integer and fractional parts. Separate with a space
+ # so they can be returned as two separate values
+ if ((exponent > 0)); then
+ local zeroCount integer fraction
+ ((zeroCount = exponent - sigFracLength))
+ if ((zeroCount > 0)); then
+ printf -v zeros "%0*d" "$zeroCount" 0
+ n=${sigInteger}${sigFraction}${zeros}" 0"
+ numericType=${__shellmath_numericTypes[INTEGER]}
+ elif ((zeroCount < 0)); then
+ n=${sigInteger}${sigFraction:0:exponent}" "${sigFraction:exponent}
+ numericType=${__shellmath_numericTypes[DECIMAL]}
+ else
+ n=${sigInteger}${sigFraction}" 0"
+ numericType=${__shellmath_numericTypes[INTEGER]}
+ fi
+ integer=${n% *}; fraction=${n#* }
+ _shellmath_setReturnValues "$integer" "$fraction" $isNegative "$numericType" $isScientific
+ return "$returnCode"
+
+ elif ((exponent < 0)); then
+ local zeroCount integer fraction
+ ((zeroCount = -exponent - sigIntLength))
+ if ((zeroCount > 0)); then
+ printf -v zeros "%0*d" "$zeroCount" 0
+ n="0 "${zeros}${sigInteger}${sigFraction}
+ numericType=${__shellmath_numericTypes[DECIMAL]}
+ elif ((zeroCount < 0)); then
+ n=${sigInteger:0:-zeroCount}" "${sigInteger:(-zeroCount)}${sigFraction}
+ numericType=${__shellmath_numericTypes[DECIMAL]}
+ else
+ n="0 "${sigInteger}${sigFraction}
+ numericType=${__shellmath_numericTypes[DECIMAL]}
+ fi
+ integer=${n% *}; fraction=${n#* }
+ _shellmath_setReturnValues "$integer" "$fraction" $isNegative "$numericType" $isScientific
+ return "$returnCode"
+
+ else
+ # exponent == 0 means the number is already aligned as desired
+ numericType=${__shellmath_numericTypes[DECIMAL]}
+ _shellmath_setReturnValues "$sigInteger" "$sigFraction" $isNegative "$numericType" $isScientific
+ return "$returnCode"
+ fi
+
+ # Reject strings like xxx[Ee]yyy where xxx, yyy are not valid numbers
+ else
+ ((returnCode = __shellmath_ILLEGAL_NUMBER))
+ _shellmath_setReturnValues ""
+ return "$returnCode"
+ fi
+
+ # Reject everything else
+ else
+ ((returnCode = __shellmath_ILLEGAL_NUMBER))
+ _shellmath_setReturnValues ""
+ return "$returnCode"
+ fi
+}
+
+
+################################################################################
+# numToScientific (integerPart, fractionalPart)
+#
+# Format conversion utility function
+################################################################################
+function _shellmath_numToScientific()
+{
+ local integerPart=$1 fractionalPart=$2
+ local exponent head tail scientific
+
+ if ((integerPart > 0)); then
+ ((exponent = ${#integerPart}-1))
+ head=${integerPart:0:1}
+ tail=${integerPart:1}${fractionalPart}
+ elif ((integerPart < 0)); then
+ ((exponent = ${#integerPart}-2)) # skip "-" and first digit
+ head=${integerPart:0:2}
+ tail=${integerPart:2}${fractionalPart}
+ else
+ [[ "$fractionalPart" =~ ^[-]?(0*)([^0])(.*)$ ]]
+ exponent=$((-(${#BASH_REMATCH[1]} + 1)))
+ head=${BASH_REMATCH[2]}
+ tail=${BASH_REMATCH[3]}
+ fi
+
+ # Remove trailing zeros
+ [[ $tail =~ ^.*[^0] ]]; tail=${BASH_REMATCH[0]:-0}
+
+ printf -v scientific "%d.%de%d" "$head" "$tail" "$exponent"
+
+ _shellmath_setReturnValue "$scientific"
+}
+
+
+################################################################################
+# _shellmath_add (addend_1, addend_2)
+################################################################################
+function _shellmath_add()
+{
+ local n1="$1"
+ local n2="$2"
+
+ if ((! __shellmath_didPrecalc)); then
+ _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true
+ fi
+
+ local isVerbose=$(( __shellmath_isOptimized == __shellmath_false ))
+
+ # Is the caller itself an arithmetic function?
+ local isSubcall=${__shellmath_false}
+ local isMultiplication=${__shellmath_false}
+ if [[ "${FUNCNAME[1]}" =~ shellmath_(add|subtract|multiply|divide)$ ]]; then
+ isSubcall=${__shellmath_true}
+ if [[ "${BASH_REMATCH[1]}" == multiply ]]; then
+ isMultiplication=${__shellmath_true}
+ fi
+ fi
+
+ # Handle corner cases where argument count is not 2
+ local argCount=$#
+ if ((argCount == 0)); then
+ echo "Usage: ${FUNCNAME[0]} addend_1 addend_2"
+ return "$__shellmath_SUCCESS"
+ elif ((argCount == 1)); then
+ # Note the result as-is, print if running "normally", and return
+ _shellmath_setReturnValue "$n1"
+ (( isVerbose && ! isSubcall )) && echo "$n1"
+ return "$__shellmath_SUCCESS"
+ elif ((argCount > 2 && !isSubcall)); then
+ local recursiveReturn
+
+ # Use a binary recursion tree to add everything up
+ # 1) left branch
+ _shellmath_add "${@:1:$((argCount/2))}"; recursiveReturn=$?
+ _shellmath_getReturnValue n1
+ if (( recursiveReturn != __shellmath_SUCCESS )); then
+ _shellmath_setReturnValue "$n1"
+ return "$recursiveReturn"
+ fi
+ # 2) right branch
+ _shellmath_add "${@:$((argCount/2+1))}"; recursiveReturn=$?
+ _shellmath_getReturnValue n2
+ if (( recursiveReturn != __shellmath_SUCCESS )); then
+ _shellmath_setReturnValue "$n2"
+ return "$recursiveReturn"
+ fi
+ # 3) head node
+ local sum
+ _shellmath_add "$n1" "$n2"; recursiveReturn=$?
+ _shellmath_getReturnValue sum
+ _shellmath_setReturnValue "$sum"
+ if (( isVerbose && ! isSubcall )); then
+ echo "$sum"
+ fi
+ return "$recursiveReturn"
+ fi
+
+ local integerPart1 fractionalPart1 integerPart2 fractionalPart2
+ local isNegative1 type1 isScientific1 isNegative2 type2 isScientific2
+ local flags
+
+ if ((isMultiplication)); then
+ integerPart1="$1"
+ fractionalPart1="$2"
+ integerPart2="$3"
+ fractionalPart2="$4"
+
+ type1=${__shellmath_numericTypes[DECIMAL]}
+ type2=${__shellmath_numericTypes[DECIMAL]}
+ isNegative1=$__shellmath_false
+ isNegative2=$__shellmath_false
+ isScientific1=$__shellmath_false
+ isScientific2=$__shellmath_false
+ else
+ # Check and parse the arguments
+ _shellmath_validateAndParse "$n1"; flags=$?
+ _shellmath_getReturnValues integerPart1 fractionalPart1 isNegative1 type1 isScientific1
+ if ((flags == __shellmath_ILLEGAL_NUMBER)); then
+ _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n1"
+ return $?
+ fi
+ _shellmath_validateAndParse "$n2"; flags=$?
+ _shellmath_getReturnValues integerPart2 fractionalPart2 isNegative2 type2 isScientific2
+ if ((flags == __shellmath_ILLEGAL_NUMBER)); then
+ _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n2"
+ return $?
+ fi
+ fi
+
+ # Quick add & return for integer adds
+ if ((type1==type2 && type1==__shellmath_numericTypes[INTEGER])); then
+ ((isNegative1)) && ((integerPart1*=-1))
+ ((isNegative2)) && ((integerPart2*=-1))
+ local sum=$((integerPart1 + integerPart2))
+ if (( (!isSubcall) && (isScientific1 || isScientific2) )); then
+ _shellmath_numToScientific $sum ""
+ _shellmath_getReturnValue sum
+ fi
+ _shellmath_setReturnValue $sum
+ if (( isVerbose && ! isSubcall )); then
+ echo "$sum"
+ fi
+ return "$__shellmath_SUCCESS"
+ fi
+
+ # Right-pad both fractional parts with zeros to the same length
+ local fractionalLen1=${#fractionalPart1}
+ local fractionalLen2=${#fractionalPart2}
+ if ((fractionalLen1 > fractionalLen2)); then
+ # Use printf to zero-pad. This avoids mathematical side effects.
+ printf -v fractionalPart2 %-*s "$fractionalLen1" "$fractionalPart2"
+ fractionalPart2=${fractionalPart2// /0}
+ elif ((fractionalLen2 > fractionalLen1)); then
+ printf -v fractionalPart1 %-*s "$fractionalLen2" "$fractionalPart1"
+ fractionalPart1=${fractionalPart1// /0}
+ fi
+ local unsignedFracLength=${#fractionalPart1}
+
+ # Implement a sign convention that will enable us to detect carries by
+ # comparing string lengths of addends and sums: propagate the sign across
+ # both numeric parts (whether unsigned or zero).
+ if ((isNegative1)); then
+ fractionalPart1="-"$fractionalPart1
+ integerPart1="-"$integerPart1
+ fi
+ if ((isNegative2)); then
+ fractionalPart2="-"$fractionalPart2
+ integerPart2="-"$integerPart2
+ fi
+
+ local integerSum=0
+ local fractionalSum=0
+
+ ((integerSum = integerPart1+integerPart2))
+
+ # Summing the fractional parts is tricky: We need to override the shell's
+ # default interpretation of leading zeros, but the operator for doing this
+ # (the "10#" operator) cannot work directly with negative numbers. So we
+ # break it all down.
+ if ((isNegative1)); then
+ ((fractionalSum += (-1) * 10#${fractionalPart1:1}))
+ else
+ ((fractionalSum += 10#$fractionalPart1))
+ fi
+ if ((isNegative2)); then
+ ((fractionalSum += (-1) * 10#${fractionalPart2:1}))
+ else
+ ((fractionalSum += 10#$fractionalPart2))
+ fi
+
+ unsignedFracSumLength=${#fractionalSum}
+ if [[ "$fractionalSum" =~ ^[-] ]]; then
+ ((unsignedFracSumLength--))
+ fi
+
+ # Restore any leading zeroes that were lost when adding
+ if ((unsignedFracSumLength < unsignedFracLength)); then
+ local lengthDiff=$((unsignedFracLength - unsignedFracSumLength))
+ local zeroPrefix
+ printf -v zeroPrefix "%0*d" "$lengthDiff" 0
+ if ((fractionalSum < 0)); then
+ fractionalSum="-"${zeroPrefix}${fractionalSum:1}
+ else
+ fractionalSum=${zeroPrefix}${fractionalSum}
+ fi
+ fi
+
+ # Carry a digit from fraction to integer if required
+ if ((10#$fractionalSum!=0 && unsignedFracSumLength > unsignedFracLength)); then
+ local carryAmount
+ ((carryAmount = isNegative1?-1:1))
+ ((integerSum += carryAmount))
+ # Remove the leading 1-digit whether the fraction is + or -
+ fractionalSum=${fractionalSum/1/}
+ fi
+
+ # Transform the partial sums from additive to concatenative. Example: the
+ # pair (-2,3) is not -2.3 but rather (-2)+(0.3), i.e. -1.7 so we want to
+ # transform (-2,3) to (-1,7). This transformation is meaningful when
+ # the two parts have opposite signs, so that's what we look for.
+ if ((integerSum < 0 && 10#$fractionalSum > 0)); then
+ ((integerSum += 1))
+ ((fractionalSum = 10#$fractionalSum - 10**unsignedFracSumLength))
+ elif ((integerSum > 0 && 10#$fractionalSum < 0)); then
+ ((integerSum -= 1))
+ ((fractionalSum = 10**unsignedFracSumLength + 10#$fractionalSum))
+ fi
+ # This last case needs to function either as an "else" for the above,
+ # or as a coda to the "if" clause when integerSum is -1 initially.
+ if ((integerSum == 0 && 10#$fractionalSum < 0)); then
+ integerSum="-"$integerSum
+ ((fractionalSum *= -1))
+ fi
+
+ # Touch up the numbers for display
+ local sum
+ ((10#$fractionalSum < 0)) && fractionalSum=${fractionalSum:1}
+ if (( (!isSubcall) && (isScientific1 || isScientific2) )); then
+ _shellmath_numToScientific "$integerSum" "$fractionalSum"
+ _shellmath_getReturnValue sum
+ elif ((10#$fractionalSum)); then
+ printf -v sum "%s.%s" "$integerSum" "$fractionalSum"
+ else
+ sum=$integerSum
+ fi
+
+ # Note the result, print if running "normally", and return
+ _shellmath_setReturnValue $sum
+ if (( isVerbose && ! isSubcall )); then
+ echo "$sum"
+ fi
+
+ return "$__shellmath_SUCCESS"
+}
+
+
+################################################################################
+# subtract (subtrahend, minuend)
+################################################################################
+function _shellmath_subtract()
+{
+ local n1="$1"
+ local n2="$2"
+ local isVerbose=$(( __shellmath_isOptimized == __shellmath_false ))
+
+ if ((! __shellmath_didPrecalc)); then
+ _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true
+ fi
+
+ if (( $# == 0 || $# > 2 )); then
+ echo "Usage: ${FUNCNAME[0]} subtrahend minuend"
+ return "$__shellmath_SUCCESS"
+ elif (( $# == 1 )); then
+ # Note the value as-is and return
+ _shellmath_setReturnValue "$n1"
+ ((isVerbose)) && echo "$n1"
+ return "$__shellmath_SUCCESS"
+ fi
+
+ # Symbolically negate the second argument
+ if [[ "$n2" =~ ^- ]]; then
+ n2=${n2:1}
+ else
+ n2="-"$n2
+ fi
+
+ # Calculate, note the result, print if running "normally", and return
+ local difference
+ _shellmath_add "$n1" "$n2"
+ _shellmath_getReturnValue difference
+ if ((isVerbose)); then
+ echo "$difference"
+ fi
+
+ return $?
+}
+
+
+################################################################################
+# reduceOuterPairs (two integer parts [, two fractional parts])
+#
+# Examines the magnitudes of two numbers in advance of a multiplication
+# and either chops off their lowest-order digits or pushes them to the
+# corresponding lower-order parts in order to prevent overflow in the product.
+# The choice depends on whether 2 or 4 arguments are supplied.
+################################################################################
+function _shellmath_reduceOuterPairs()
+{
+ local value1="$1" value2="$2" subvalue1="$3" subvalue2="$4"
+
+ local digitExcess value1Len=${#value1} value2Len=${#value2}
+ ((digitExcess = value1Len + value2Len - __shellmath_precision))
+
+ # Be very precise about detecting overflow. The "digit excess" underestimates
+ # this: floor(log_10(longLongMax)). We don't want to needlessly lose precision
+ # when a product barely squeezes under the exact threshold.
+ if ((digitExcess>1 || (digitExcess==1 && value1 > __shellmath_maxValue/value2) )); then
+
+ # Identify the digit-tails to be pruned off and either discarded or
+ # pushed past the decimal point. In pruning the two values we want to
+ # retain as much "significance" as possible, so we try to equalize the
+ # lengths of the remaining digit sequences.
+ local tail1 tail2
+ local lengthDiff leftOver
+
+ # Which digit string is longer, and by how much?
+ ((lengthDiff = value1Len - value2Len))
+ if ((lengthDiff > 0)); then
+ if ((digitExcess <= lengthDiff)); then
+ # Chop digits from the longer string only
+ tail1=${value1:(-digitExcess)}
+ tail2="" # do not chop anything
+ else
+ # Chop more digits from the longer string so the two strings
+ # end up as nearly-equal in length as possible
+ ((leftOver = digitExcess - lengthDiff))
+ tail1=${value1:(-(lengthDiff + leftOver/2))}
+ tail2=${value2:(-((leftOver+1)/2))}
+ fi
+ else
+ ((lengthDiff *= -1))
+ # Mirror the above code block but swap 1 and 2
+ if ((digitExcess <= lengthDiff)); then
+ tail1=""
+ tail2=${value2:(-digitExcess)}
+ else
+ ((leftOver = digitExcess - lengthDiff))
+ tail1=${value1:(-((leftOver+1)/2))}
+ tail2=${value2:(-(lengthDiff + leftOver/2))}
+ fi
+ fi
+
+ # Discard the least-significant digits or move them past the decimal point
+ value1=${value1%${tail1}}
+ [[ -n "$subvalue1" ]] && subvalue1=${tail1}${subvalue1%0} # remove placeholder zero
+ value2=${value2%${tail2}}
+ [[ -n "$subvalue2" ]] && subvalue2=${tail2}${subvalue2%0}
+ else
+ # Signal the caller that no rescaling was actually done
+ ((digitExcess = 0))
+ fi
+
+ _shellmath_setReturnValues "$value1" "$value2" \
+ "$subvalue1" "$subvalue2" "$digitExcess"
+}
+
+################################################################################
+# rescaleValue(value, rescaleFactor)
+#
+# Upscales a decimal value by "factor" orders of magnitude: 3.14159 --> 3141.59
+################################################################################
+function _shellmath_rescaleValue()
+{
+ local value="$1" rescalingFactor="$2"
+ local head tail zeroCount zeroTail
+
+ [[ "$value" =~ ^(.*)\.(.*)$ ]]
+ head=${BASH_REMATCH[1]}
+ tail=${BASH_REMATCH[2]}
+ ((zeroCount = rescalingFactor - ${#tail}))
+ if ((zeroCount > 0)); then
+ printf -v zeroTail "%0*d" "$zeroCount" 0
+ value=${head}${tail}${zeroTail}
+ elif ((zeroCount < 0)); then
+ value=${head}${tail:0:rescalingFactor}"."${tail:rescalingFactor}
+ else
+ value=${head}${tail}
+ fi
+
+ _shellmath_setReturnValue "$value"
+}
+
+################################################################################
+# reduceCrossPairs (two integer parts, two fractional parts)
+#
+# Examines the precision of the inner products (of "multiplication by parts")
+# and if necessary truncates the fractional part(s) to prevent overflow
+################################################################################
+function _shellmath_reduceCrossPairs()
+{
+ local value1="$1" value2="$2" subvalue1="$3" subvalue2="$4"
+
+ local digitExcess value1Len=${#value1} value2Len=${#value2}
+ local subvalue1Len=${#subvalue1} subvalue2Len=${#subvalue2}
+
+ # Check BOTH cross-products
+ ((digitExcess = value1Len + subvalue2Len - __shellmath_precision))
+ if ((digitExcess > 1 || (digitExcess==1 && value1 > __shellmath_maxValue/subvalue2) )); then
+ subvalue2=${subvalue2:0:(-digitExcess)}
+ fi
+ ((digitExcess = value2Len + subvalue1Len - __shellmath_precision))
+ if ((digitExcess > 1 || (digitExcess==1 && value2 > __shellmath_maxValue/subvalue1) )); then
+ subvalue1=${subvalue1:0:(-digitExcess)}
+ fi
+
+ _shellmath_setReturnValues "$subvalue1" "$subvalue2"
+}
+
+
+function _shellmath_round()
+{
+ local number="$1" digitCount="$2"
+ local nextDigit=${number:digitCount:1}
+
+ number=${number:0:digitCount}
+ if ((nextDigit >= 5)); then
+ printf -v number "%0*d" "$digitCount" $((10#$number + 1))
+ fi
+
+ _shellmath_setReturnValue "$number"
+}
+
+################################################################################
+# multiply (multiplicand, multiplier)
+################################################################################
+function _shellmath_multiply()
+{
+ local n1="$1"
+ local n2="$2"
+
+ if ((! __shellmath_didPrecalc)); then
+ _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true
+ fi
+
+ local isVerbose=$(( __shellmath_isOptimized == __shellmath_false ))
+
+ # Is the caller itself an arithmetic function?
+ local isSubcall=${__shellmath_false}
+ if [[ "${FUNCNAME[1]}" =~ shellmath_(add|subtract|multiply|divide)$ ]]; then
+ isSubcall=${__shellmath_true}
+ fi
+
+ # Handle corner cases where argument count is not 2
+ local argCount=$#
+ if ((argCount == 0)); then
+ echo "Usage: ${FUNCNAME[0]} factor_1 factor_2"
+ return "$__shellmath_SUCCESS"
+ elif ((argCount == 1)); then
+ # Note the value as-is and return
+ _shellmath_setReturnValue "$n1"
+ (( isVerbose && ! isSubcall )) && echo "$n1"
+ return "$__shellmath_SUCCESS"
+ elif ((argCount > 2)); then
+ local recursiveReturn
+
+ # Use a binary recursion tree to multiply everything out
+ # 1) left branch
+ _shellmath_multiply "${@:1:$((argCount/2))}"; recursiveReturn=$?
+ _shellmath_getReturnValue n1
+ if (( recursiveReturn != __shellmath_SUCCESS )); then
+ _shellmath_setReturnValue "$n1"
+ return "$recursiveReturn"
+ fi
+ # 2) right branch
+ _shellmath_multiply "${@:$((argCount/2+1))}"; recursiveReturn=$?
+ _shellmath_getReturnValue n2
+ if (( recursiveReturn != __shellmath_SUCCESS )); then
+ _shellmath_setReturnValue "$n2"
+ return "$recursiveReturn"
+ fi
+ # 3) head node
+ local product
+ _shellmath_multiply "$n1" "$n2"; recursiveReturn=$?
+ _shellmath_getReturnValue product
+ _shellmath_setReturnValue "$product"
+ if (( isVerbose && ! isSubcall )); then
+ echo "$product"
+ fi
+ return "$recursiveReturn"
+ fi
+
+ local integerPart1 fractionalPart1 integerPart2 fractionalPart2
+ local isNegative1 type1 isScientific1 isNegative2 type2 isScientific2
+ local flags
+
+ # Check and parse the arguments
+ _shellmath_validateAndParse "$n1"; flags=$?
+ _shellmath_getReturnValues integerPart1 fractionalPart1 isNegative1 type1 isScientific1
+ if ((flags == __shellmath_ILLEGAL_NUMBER)); then
+ _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n1"
+ return $?
+ fi
+ _shellmath_validateAndParse "$n2"; flags=$?
+ _shellmath_getReturnValues integerPart2 fractionalPart2 isNegative2 type2 isScientific2
+ if ((flags == __shellmath_ILLEGAL_NUMBER)); then
+ _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n2"
+ return $?
+ fi
+
+ # Overflow / underflow detection and accommodation
+ local rescalingFactor=0
+ if ((${#integerPart1} + ${#integerPart2} + ${#fractionalPart1} + ${#fractionalPart2} >= ${__shellmath_precision})); then
+ _shellmath_reduceOuterPairs "$integerPart1" "$integerPart2" "$fractionalPart1" "$fractionalPart2"
+ _shellmath_getReturnValues integerPart1 integerPart2 fractionalPart1 fractionalPart2 rescalingFactor
+ if ((10#$fractionalPart1)); then type1=${__shellmath_numericTypes[DECIMAL]}; fi
+ if ((10#$fractionalPart2)); then type2=${__shellmath_numericTypes[DECIMAL]}; fi
+
+ _shellmath_reduceCrossPairs "$integerPart1" "$integerPart2" "$fractionalPart1" "$fractionalPart2"
+ _shellmath_getReturnValues fractionalPart1 fractionalPart2
+
+ _shellmath_reduceOuterPairs "$fractionalPart1" "$fractionalPart2"
+ _shellmath_getReturnValues fractionalPart1 fractionalPart2
+ fi
+
+ # Quick multiply & return for integer multiplies
+ if ((type1==type2 && type1==__shellmath_numericTypes[INTEGER])); then
+ ((isNegative1)) && ((integerPart1*=-1))
+ ((isNegative2)) && ((integerPart2*=-1))
+ local product=$((integerPart1 * integerPart2))
+ if ((rescalingFactor > 0)); then
+ _shellmath_rescaleValue "$product" "$rescalingFactor"
+ _shellmath_getReturnValue product
+ fi
+ if (( (!isSubcall) && (isScientific1 || isScientific2) )); then
+ _shellmath_numToScientific $product ""
+ _shellmath_getReturnValue product
+ fi
+ _shellmath_setReturnValue $product
+ if (( isVerbose && ! isSubcall )); then
+ echo "$product"
+ fi
+ return "$__shellmath_SUCCESS"
+ fi
+
+ # The product has four components per the distributive law
+ local intProduct floatProduct innerProduct1 innerProduct2
+ # Widths of the decimal parts
+ local floatWidth fractionalWidth1 fractionalWidth2
+
+ # Compute the integer and floating-point components
+ ((intProduct = integerPart1 * integerPart2))
+
+ fractionalWidth1=${#fractionalPart1}
+ fractionalWidth2=${#fractionalPart2}
+ ((floatWidth = fractionalWidth1 + fractionalWidth2))
+ ((floatProduct = 10#$fractionalPart1 * 10#$fractionalPart2))
+ if ((${#floatProduct} < floatWidth)); then
+ printf -v floatProduct "%0*d" "$floatWidth" "$floatProduct"
+ fi
+
+ # Compute the inner products: First integer-multiply, then rescale
+ ((innerProduct1 = integerPart1 * 10#$fractionalPart2))
+ ((innerProduct2 = integerPart2 * 10#$fractionalPart1))
+
+ # Rescale the inner products back to decimals so we can shellmath_add() them
+ if ((fractionalWidth2 <= ${#innerProduct1})); then
+ local innerInt1=${innerProduct1:0:(-$fractionalWidth2)}
+ local innerFloat1=${innerProduct1:(-$fractionalWidth2)}
+ integerPart1=${innerInt1}
+ fractionalPart1=${innerFloat1}
+ else
+ integerPart1=0
+ printf -v fractionalPart1 "%0*d" "$fractionalWidth2" "$innerProduct1"
+ fi
+ if ((fractionalWidth1 <= ${#innerProduct2})); then
+ local innerInt2=${innerProduct2:0:(-$fractionalWidth1)}
+ local innerFloat2=${innerProduct2:(-$fractionalWidth1)}
+ integerPart2=${innerInt2}
+ fractionalPart2=${innerFloat2}
+ else
+ integerPart2=0
+ printf -v fractionalPart2 "%0*d" "$fractionalWidth1" "$innerProduct2"
+ fi
+
+ # Combine the distributed parts
+ local innerSum product
+ # Add the inner products to get the inner sum
+ _shellmath_add "$integerPart1" "$fractionalPart1" "$integerPart2" "$fractionalPart2"
+ _shellmath_getReturnValue innerSum
+ [[ "$innerSum" =~ (.*)\.(.*) ]]
+ integerPart1=${BASH_REMATCH[1]}
+ fractionalPart1=${BASH_REMATCH[2]}
+ # Add inner sum + outer sum
+ _shellmath_add "$integerPart1" "$fractionalPart1" "$intProduct" "$floatProduct"
+ _shellmath_getReturnValue product
+
+ # Determine the sign of the product
+ if ((isNegative1 != isNegative2)); then
+ product="-"$product
+ fi
+
+ # When we pre-detect overflow in the integer part of the computation,
+ # we compensate by shrinking the inputs by some order of magnitude.
+ # Having now finished the computation, we revert to the original magnitude.
+ if ((rescalingFactor > 0)); then
+ _shellmath_rescaleValue "$product" "$rescalingFactor"
+ _shellmath_getReturnValue product
+ fi
+
+ # Convert to scientific notation if appropriate
+ if (( (!isSubcall) && (isScientific1 || isScientific2) )); then
+ _shellmath_numToScientific "${product%.*}" "${product#*.}"
+ _shellmath_getReturnValue product
+ fi
+
+ # Note the result, print if running "normally", and return
+ _shellmath_setReturnValue $product
+ if (( isVerbose && ! isSubcall )); then
+ echo "$product"
+ fi
+
+ return "$__shellmath_SUCCESS"
+}
+
+
+################################################################################
+# divide (dividend, divisor)
+################################################################################
+function _shellmath_divide()
+{
+ local n1="$1"
+ local n2="$2"
+ local integerPart1 fractionalPart1 integerPart2 fractionalPart2
+ local isNegative1 type1 isScientific1 isNegative2 type2 isScientific2
+
+ if ((! __shellmath_didPrecalc)); then
+ _shellmath_precalc; __shellmath_didPrecalc=$__shellmath_true
+ fi
+
+ local isVerbose=$(( __shellmath_isOptimized == __shellmath_false ))
+
+ local isTesting=${__shellmath_false}
+ if [[ "${FUNCNAME[1]}" == "_shellmath_assert_functionReturn" ]]; then
+ isTesting=${__shellmath_true}
+ fi
+
+ if [[ $# -eq 0 || $# -gt 2 ]]; then
+ echo "Usage: ${FUNCNAME[0]} dividend divisor"
+ return "$__shellmath_SUCCESS"
+ elif [[ $# -eq 1 ]]; then
+ # Note the value as-is and return
+ _shellmath_setReturnValue "$n1"
+ ((isVerbose)) && echo "$n1"
+ return "$__shellmath_SUCCESS"
+ fi
+
+ # Check and parse the arguments
+ local flags
+ _shellmath_validateAndParse "$n1"; flags=$?
+ _shellmath_getReturnValues integerPart1 fractionalPart1 isNegative1 type1 isScientific1
+ if ((flags == __shellmath_ILLEGAL_NUMBER)); then
+ _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n1"
+ return $?
+ fi
+ _shellmath_validateAndParse "$n2"; flags=$?
+ _shellmath_getReturnValues integerPart2 fractionalPart2 isNegative2 type2 isScientific2
+ if ((flags == __shellmath_ILLEGAL_NUMBER)); then
+ _shellmath_warn "${__shellmath_returnCodes[ILLEGAL_NUMBER]}" "$n2"
+ return $?
+ fi
+
+ # Throw error on divide by zero
+ if ((integerPart2 == 0 && 10#$fractionalPart2 == 0)); then
+ _shellmath_warn "${__shellmath_returnCodes[DIVIDE_BY_ZERO]}" "$n2"
+ return $?
+ fi
+
+ # Convert the division problem to an *integer* division problem by rescaling
+ # both inputs so as to lose their decimal points. To obtain maximal precision,
+ # we scale up the numerator further, padding with as many zeros as it can hold
+ local numerator denominator quotient
+ local rescaleFactor zeroCount zeroTail
+
+ if ((integerPart1 == 0)); then
+ integerPart1=""
+ fi
+ ((zeroCount = __shellmath_precision - ${#integerPart1} - ${#fractionalPart1}))
+ ((rescaleFactor = __shellmath_precision - ${#integerPart1} - ${#fractionalPart2}))
+ if ((zeroCount > 0)); then
+ printf -v zeroTail "%0*d" "$zeroCount" 0
+ fi
+
+ # Rescale and rewrite the fraction to be computed, and compute it
+ numerator=${integerPart1}${fractionalPart1}${zeroTail}
+ denominator=${integerPart2}${fractionalPart2}
+ ((quotient = 10#$numerator / 10#$denominator))
+
+ # For greater precision, re-divide by the remainder to get the next digits of the quotient
+ local remainder quotient_2
+ ((remainder = 10#$numerator % 10#$denominator)) # cannot exceed numerator or thus, maxValue
+ ((zeroCount = __shellmath_precision - ${#remainder}))
+ if ((zeroCount > 0)); then
+ printf -v zeroTail "%0*d" "$zeroCount" 0
+ else
+ zeroTail=""
+ fi
+ # Derive the new numerator from the remainder. Do not change the denominator.
+ numerator=${remainder}${zeroTail}
+ ((quotient_2 = 10#$numerator / 10#$denominator))
+ quotient=${quotient}${quotient_2}
+ ((rescaleFactor += ${#quotient_2}))
+
+ # Rescale back. For aesthetic reasons we also round off at the "precision"th decimal place
+ ((zeroCount = rescaleFactor - ${#quotient}))
+ if ((zeroCount >= 0)); then
+ local zeroPrefix="" fractionalPart
+ if ((zeroCount > 0)); then
+ printf -v zeroPrefix "%0*d" "$((rescaleFactor - ${#quotient}))" 0
+ fi
+ fractionalPart=${zeroPrefix}${quotient}
+ _shellmath_round "$fractionalPart" $__shellmath_precision
+ _shellmath_getReturnValue fractionalPart
+ quotient="0."${fractionalPart}
+ else
+ fractionalPart=${quotient:(-$rescaleFactor)}
+ _shellmath_round "$fractionalPart" $__shellmath_precision
+ _shellmath_getReturnValue fractionalPart
+ quotient=${quotient:0:(-$rescaleFactor)}"."${fractionalPart}
+ fi
+
+ # Determine the sign of the quotient
+ if ((isNegative1 != isNegative2)); then
+ quotient="-"$quotient
+ fi
+
+ if ((isTesting)); then
+ # Trim zeros. (Requires decimal point and zero tail.)
+ if [[ "$quotient" =~ [\.].*0$ ]]; then
+ # If the decimal point IMMEDIATELY precedes the 0s, remove that too
+ [[ $quotient =~ [\.]?0+$ ]]
+ quotient=${quotient%${BASH_REMATCH[0]}}
+ fi
+ fi
+
+ # Convert to scientific notation if appropriate
+ if ((isScientific1 || isScientific2)); then
+ _shellmath_numToScientific "${quotient%.*}" "${quotient#*.}"
+ _shellmath_getReturnValue quotient
+ fi
+
+ # Note the result, print if running "normally", and return
+ _shellmath_setReturnValue "$quotient"
+ if ((isVerbose)); then
+ echo "$quotient"
+ fi
+
+ return "$__shellmath_SUCCESS"
+}
+
diff --git a/examples/shellmath/slower_e_demo.sh b/examples/shellmath/slower_e_demo.sh
new file mode 100644
index 0000000..d8fc931
--- /dev/null
+++ b/examples/shellmath/slower_e_demo.sh
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+###############################################################################
+# This script illustrates the use of the shellmath APIs to perform
+# decimal calculations. Here we approximate the mathematical constant 'e'
+# using its Maclaurin polynomials (i.e. its Taylor polynomials centered at 0).
+###############################################################################
+
+source shellmath.sh
+
+# Setting the '-t' flag will cause the script to time the algorithm
+if [[ "$1" == '-t' ]]; then
+ do_timing=${__shellmath_true}
+ shift
+fi
+
+if [[ $# -ne 1 ]]; then
+ echo "USAGE: ${BASH_SOURCE##*/} [-t] *N*"
+ echo " Approximates 'e' using the N-th order Maclaurin polynomial"
+ echo " (i.e. the Taylor polynomial centered at 0)."
+ echo " Specify the '-t' flag to time the main algorithm."
+ exit 0
+elif [[ ! "$1" =~ ^[0-9]+$ ]]; then
+ echo "Illegal argument. Whole numbers only, please."
+ exit 1
+fi
+
+
+function run_algorithm()
+{
+ # Initialize
+ n=0; N=$1; zero_factorial=1
+
+ # Initialize e to the zeroth-order term
+ term=$(_shellmath_divide 1 $zero_factorial)
+ e=$term
+
+ # Compute successive terms T(n) := T(n-1)/n and accumulate into e
+ for ((n=1; n<=N; n++)); do
+ term=$(_shellmath_divide "$term" "$n")
+ e=$(_shellmath_add "$e" "$term")
+ done
+
+ echo "e = $e"
+}
+
+
+if (( do_timing == __shellmath_true )); then
+ time run_algorithm "$1"
+else
+ run_algorithm "$1"
+fi
+
+exit 0
+
diff --git a/examples/shellmath/testCases.in b/examples/shellmath/testCases.in
new file mode 100644
index 0000000..54e3a82
--- /dev/null
+++ b/examples/shellmath/testCases.in
@@ -0,0 +1,142 @@
+################################################################
+# The general testcase syntax is
+# assertionType expectedValue functionUnderTest [args ... ]
+#
+# where assertionType is either of:
+# Code to indicate the (bash-style) integer return code
+# String to indicate the string "printed" as a side effect
+#
+# and functionUnderTest is the function name
+# with the "_shellmath_" prefix removed.
+################################################################
+
+################################
+# Tests for SUPPORTING FUNCTIONS
+################################
+
+# Tests for getReturnCode()
+Code 0 getReturnCode SUCCESS
+Code 1 getReturnCode FAIL
+Code 2 getReturnCode ILLEGAL_NUMBER
+
+## Tests for validateAndParse():
+## Validate a number, determine its type and sign, split it into parts
+
+# Detect Invalid input
+Code 2 validateAndParse NaN
+String "" validateAndParse NaN
+# Positive integers
+String "4 0 0 0 0" validateAndParse 4
+# Negative integers
+String "9 0 1 0 0" validateAndParse -9
+# Decimals
+String "4 2 0 1 0" validateAndParse 4.2
+# Negative decimals
+String "4 2 1 1 0" validateAndParse -4.2
+# Scientific / exponential notation: Check all code branches
+String "340000 0 0 0 1" validateAndParse 3.4e5
+String "344 4 0 1 1" validateAndParse 3.444e2
+String "34567 0 0 0 1" validateAndParse 3.4567e4
+String "0 003456 0 1 1" validateAndParse 3.456e-3
+String "34 56 0 1 1" validateAndParse 345.6e-1
+String "0 23011 0 1 1" validateAndParse 23.011e-2
+String "23 011 0 1 1" validateAndParse 23.011e0
+
+####################
+# Tests for ADDITION
+####################
+String 4 add 4
+String 9 add 4 5
+
+# Same-length decimal tails with no leading zeros, no carry across decimal point
+String 2.214 add 1.105 1.109
+
+# Carry across decimal point
+String 3.8 add 1.9 1.9
+String -3.8 add -1.9 -1.9
+
+# Different-length decimals, one with leading zero
+String 2.195 add 1.105 1.09
+String -2.195 add -1.105 -1.09
+
+# Same-length tails having leading zeros
+String 2.014 add 1.005 1.009
+String -2.014 add -1.005 -1.009
+# Different-length tails with and without leading zeros
+String 3.31462 add 1.905 1.40962
+String 2.01462 add 1.005 1.00962
+
+# Subtraction
+String 2.5 subtract 5.2 2.7
+String -2.5 subtract 2.7 5.2
+String 2.5 add 5.2 -2.7
+
+# Integer part equal to 0
+String 1.5 add 0.6 0.9
+String 1.5 add .6 .9
+String -0.3 add 0.6 -0.9
+String -0.3 add .6 -.9
+
+# Recursive/multiple addition
+String 12 add 2 4 6
+String 6.6 add 1.1 2.2 3.3
+
+##########################
+# Tests for MULTIPLICATION
+##########################
+String 4 multiply 4
+String 20 multiply 4 5
+
+String 21.32 multiply 4.1 5.2
+String -21.32 multiply -4.1 5.2
+
+# Carry-heavy products
+String 98.901 multiply 9.9 9.99
+
+# Leading zeros after decimal point:
+# Track place value with zero-padding
+String 1.0201 multiply 1.01 1.01
+String 0.0001 multiply 0.01 0.01
+String 0.0001 add 0 0.0001
+
+# Staggered decimal precisions
+String 0.000001 multiply 0.01 0.0001
+
+# Interpret in base 10
+String 2.2781 multiply 1.09 2.09
+
+# Recursive multiplication
+String 35.1384 multiply 1.1 2.2 3.3 4.4
+
+####################
+# Tests for DIVISION
+####################
+String 4 divide 4
+String 4 divide 20 5
+
+String 0.5 divide 1 2
+String -0.5 divide -1 2
+
+# Mixed fractions
+String 34.54 divide 3454 100
+
+# Non-terminating decimals
+String 0.166666666666666667 divide 1 6
+
+# Decimal arguments
+String 0.25 divide 0.5 2
+String 0.04165 divide 0.1666 4
+
+###########################
+# Tests for scientific math
+###########################
+String 8.8e4 add 1.1e4 7.7e4
+String 4.239e1 add 1.224e1 3.015e1
+String -6.6e4 add 1.1e4 -7.7e4
+String -66000 add 11000 -77000
+String 1.23123e2 add 1.23e2 1.23e-1
+String 8.1403e7 multiply 2.03e5 4.01e2
+String 1.0e-7 multiply 1.0e-3 1.0e-4
+String 1.0e-7 multiply 1e-3 1e-4
+
+
diff --git a/examples/shellmath/timingData.txt b/examples/shellmath/timingData.txt
new file mode 100644
index 0000000..d1de887
--- /dev/null
+++ b/examples/shellmath/timingData.txt
@@ -0,0 +1,42 @@
+$ ######## Activate optimized mode as described in the README ########
+$ __shellmath_isOptimized=1
+
+$ ######## Addition ########
+$ time { for ((i=0; i<100; i++)); do _shellmath_add 3.1415926 2.7182818; _shellmath_getReturnValue sum; done; }
+real 0m0.196s
+user 0m0.195s
+sys 0m0.000s
+$ time { for ((i=0; i<100; i++)); do sum=$(bc <<< "3.1415926+2.7182818"); done; }
+real 0m0.488s
+user 0m0.092s
+sys 0m0.384s
+
+$ ######## Subtraction ########
+$ time { for ((i=0; i<100; i++)); do _shellmath_subtract 3.1415926 2.7182818; _shellmath_getReturnValue diff; done; }
+real 0m0.236s
+user 0m0.234s
+sys 0m0.001s
+$ time { for ((i=0; i<100; i++)); do diff=$(bc <<< "3.1415926-2.7182818"); done; }
+real 0m0.461s
+user 0m0.090s
+sys 0m0.388s
+
+$ ######## Multiplication ########
+$ time { for ((i=0; i<100; i++)); do _shellmath_multiply 3.1415926 2.7182818; _shellmath_getReturnValue prod; done; }
+real 0m0.340s
+user 0m0.333s
+sys 0m0.005s
+$ time { for ((i=0; i<100; i++)); do prod=$(bc <<< "3.1415926*2.7182818"); done; }
+real 0m0.465s
+user 0m0.105s
+sys 0m0.377s
+
+$ ######## Division ########
+$ time { for ((i=0; i<100; i++)); do _shellmath_divide 3.1415926/2.7182818; _shellmath_getReturnValue quot; done; }
+real 0m0.196s
+user 0m0.195s
+sys 0m0.000s
+$ time { for ((i=0; i<100; i++)); do quot=$(bc <<< "scale=8; 3.1415926/2.7182818"); done; }
+real 0m0.463s
+user 0m0.116s
+sys 0m0.364s
diff --git a/examples/startup-files/Bash_aliases b/examples/startup-files/Bash_aliases
new file mode 100644
index 0000000..2abb93e
--- /dev/null
+++ b/examples/startup-files/Bash_aliases
@@ -0,0 +1,63 @@
+# Some useful aliases.
+alias texclean='rm -f *.toc *.aux *.log *.cp *.fn *.tp *.vr *.pg *.ky'
+alias clean='echo -n "Really clean this directory?";
+ read yorn;
+ if test "$yorn" = "y"; then
+ rm -f \#* *~ .*~ *.bak .*.bak *.tmp .*.tmp core a.out;
+ echo "Cleaned.";
+ else
+ echo "Not cleaned.";
+ fi'
+alias h='history'
+alias j="jobs -l"
+alias l="ls -l "
+alias ll="ls -l"
+alias ls="ls -F"
+alias pu="pushd"
+alias po="popd"
+
+#
+# Csh compatibility:
+#
+alias unsetenv=unset
+function setenv () {
+ export $1="$2"
+}
+
+# Function which adds an alias to the current shell and to
+# the ~/.bash_aliases file.
+add-alias ()
+{
+ local name=$1 value="$2"
+ echo alias $name=\'$value\' >>~/.bash_aliases
+ eval alias $name=\'$value\'
+ alias $name
+}
+
+# "repeat" command. Like:
+#
+# repeat 10 echo foo
+repeat ()
+{
+ local count="$1" i;
+ shift;
+ for i in $(seq 1 "$count");
+ do
+ eval "$@";
+ done
+}
+
+# Subfunction needed by `repeat'.
+seq ()
+{
+ local lower upper output;
+ lower=$1 upper=$2;
+
+ if [ $lower -ge $upper ]; then return; fi
+ while [ $lower -le $upper ];
+ do
+ echo -n "$lower "
+ lower=$(($lower + 1))
+ done
+ echo "$lower"
+}
diff --git a/examples/startup-files/Bash_profile b/examples/startup-files/Bash_profile
new file mode 100644
index 0000000..141e8df
--- /dev/null
+++ b/examples/startup-files/Bash_profile
@@ -0,0 +1,18 @@
+# Startup file for bash login shells.
+#
+default_dir=/usr/local/lib/
+
+if [ -n "$PS1" ]; then
+ PS1='\u@\h(\#)\$ '
+ IGNOREEOF=3
+fi
+
+LOGIN_SHELL=true
+
+# If the user has her own init file, then use that one, else use the
+# canonical one.
+if [ -f ~/.bashrc ]; then
+ . ~/.bashrc
+elif [ -f ${default_dir}Bashrc ]; then
+ . ${default_dir}Bashrc;
+fi
diff --git a/examples/startup-files/Bashrc.bfox b/examples/startup-files/Bashrc.bfox
new file mode 100644
index 0000000..efe7d88
--- /dev/null
+++ b/examples/startup-files/Bashrc.bfox
@@ -0,0 +1,70 @@
+# Bourne Again SHell init file.
+#
+# Files you make look like rw-rw-r
+umask 002
+
+# Don't make useless coredump files. If you want a coredump,
+# say "ulimit -c unlimited" and then cause a segmentation fault.
+ulimit -c 0
+
+# Sometimes, there are lots of places that one can find tex inputs.
+export TEXINPUTS=.:$HOME/bin:/usr/lib/tex/inputs:/usr/local/lib/tex/inputs
+
+# Where's the Gnu stuff at?
+GNU=/usr/gnu/bin
+X11=/usr/bin/X11
+
+UTIL_PATH=$GNU:$X11
+STANDARD_PATH=/usr/local/bin:/usr/ucb:/bin:/usr/bin:/usr/etc:/etc:/usr/games
+
+if [ -d $HOME/bin/$HOSTTYPE ]; then
+ MY_PATH=$HOME/bin/$HOSTTYPE
+fi
+
+if [ -d $HOME/bin ]; then
+ MY_PATH=$MY_PATH:$HOME/bin
+fi
+
+if [ -d /usr/hosts ]; then
+ STANDARD_PATH=$STANDARD_PATH:/usr/hosts
+fi
+
+PATH=.:$MY_PATH:$UTIL_PATH:$STANDARD_PATH
+
+# If not running interactively, then return
+if [ -z "$PS1" ]; then
+ return
+fi
+
+# Set ignoreeof if you don't want EOF as the sole input to the shell to
+# immediately signal a quit condition. This only happens at the start
+# of a line if the line is empty, and you haven't just deleted a character
+# with C-d. I turn this on in ~/.bash_profile so that only login shells
+# have the right to be obnoxious.
+# set -o ignoreeof
+
+# Set auto_resume if you want to resume on "emacs", as well as on
+# "%emacs".
+auto_resume=exact
+
+# Set notify if you want to be asynchronously notified about background
+# job completion.
+set -o notify
+
+# Make it so that failed `exec' commands don't flush this shell.
+shopt -s execfail
+
+if [ -z "$LOGIN_SHELL" ]; then
+ PS1="\u@\h\$ "
+fi
+
+HISTSIZE=256
+MAILCHECK=60
+
+# A couple of default aliases.
+alias j='jobs -l'
+alias po=popd
+alias pu=pushd
+alias ls='ls -F'
+
+[ -f ~/.bash_aliases ] && . ~/.bash_aliases
diff --git a/examples/startup-files/README b/examples/startup-files/README
new file mode 100644
index 0000000..00df041
--- /dev/null
+++ b/examples/startup-files/README
@@ -0,0 +1,12 @@
+Some sample startup files. The ones starting with capital letters
+are originally from Brian Fox. The ones starting with lowercase
+letters are from Chet Ramey.
+
+They will require changes for your environment.
+
+Bash_aliases Some useful aliases (Fox).
+Bash_profile Sample startup file for bash login shells (Fox).
+bash-profile Sample startup file for bash login shells (Ramey).
+bashrc Sample Bourne Again SHell init file (Ramey).
+Bashrc.bfox Sample Bourne Again SHell init file (Fox).
+README README
diff --git a/examples/startup-files/bash-profile b/examples/startup-files/bash-profile
new file mode 100644
index 0000000..e811df8
--- /dev/null
+++ b/examples/startup-files/bash-profile
@@ -0,0 +1,39 @@
+# This is the filename where your incoming mail arrives.
+MAIL=~/mbox
+MAILCHECK=30
+
+HISTFILE=~/.history/history.$HOSTNAME
+
+PATH1=/usr/homes/chet/bin.$HOSTTYPE:/usr/local/bin/gnu:
+PATH2=/usr/local/bin:/usr/ucb:/bin:/usr/bin/X11:.
+PATH3=/usr/bin:/usr/new/bin:/usr/contrib/bin
+PATH=$PATH1:$PATH2:$PATH3
+
+EDITOR=/usr/local/bin/ce VISUAL=/usr/local/bin/ce FCEDIT=/usr/local/bin/ce
+
+SHELL=${SHELL:-${BASH:-/bin/bash}}
+
+PAGER=/usr/local/bin/less
+LESS='-i -e -M -P%t?f%f :stdin .?pb%pb\%:?lbLine %lb:?bbByte %bb:-...'
+#
+# Bogus 1003.2 variables. This should really be in /etc/profile
+#
+LOGNAME=${USER-$(whoami)}
+TZ=US/Eastern
+
+export HOME VISUAL EDITOR MAIL SHELL PATH TERM
+export PAGER LESS TERMCAP HISTSIZE HISTFILE MAIL MAILCHECK LOGNAME TZ
+
+PS1="${HOSTNAME}\$ "
+PS2='> '
+export PS1 PS2
+
+umask 022
+
+if [ -f /unix ] ; then
+ stty intr ^c # bogus
+fi
+
+if [ -f ~/.bashrc ] ; then
+ . ~/.bashrc
+fi
diff --git a/examples/startup-files/bashrc b/examples/startup-files/bashrc
new file mode 100644
index 0000000..2d8d37b
--- /dev/null
+++ b/examples/startup-files/bashrc
@@ -0,0 +1,133 @@
+case $- in
+*i*) ;;
+*) return ;;
+esac
+
+# bogus
+if [ -f /unix ] ; then
+ alias ls='/bin/ls -CF'
+else
+ alias ls='/bin/ls -F'
+fi
+alias ll='ls -l'
+alias dir='ls -ba'
+
+alias ss="ps -aux"
+alias dot='ls .[a-zA-Z0-9_]*'
+alias news="xterm -g 80x45 -e trn -e -S1 -N &"
+
+alias c="clear"
+alias m="more"
+alias j="jobs"
+
+# common misspellings
+alias mroe=more
+alias pdw=pwd
+
+hash -p /usr/bin/mail mail
+
+if [ -z "$HOST" ] ; then
+ export HOST=${HOSTNAME}
+fi
+
+HISTIGNORE="[ ]*:&:bg:fg"
+
+psgrep()
+{
+ ps -aux | grep $1 | grep -v grep
+}
+
+#
+# This is a little like `zap' from Kernighan and Pike
+#
+
+pskill()
+{
+ local pid
+
+ pid=$(ps -ax | grep $1 | grep -v grep | awk '{ print $1 }')
+ echo -n "killing $1 (process $pid)..."
+ kill -9 $pid
+ echo "slaughtered."
+}
+
+term()
+{
+ TERM=$1
+ export TERM
+ tset
+}
+
+xtitle ()
+{
+ echo -n -e "\033]0;$*\007"
+}
+
+cd()
+{
+ builtin cd "$@" && xtitle $HOST: $PWD
+}
+
+bold()
+{
+ tput smso
+}
+
+unbold()
+{
+ tput rmso
+}
+
+if [ -f /unix ] ; then
+clear()
+{
+ tput clear
+}
+fi
+
+rot13()
+{
+ if [ $# = 0 ] ; then
+ tr "[a-m][n-z][A-M][N-Z]" "[n-z][a-m][N-Z][A-M]"
+ else
+ tr "[a-m][n-z][A-M][N-Z]" "[n-z][a-m][N-Z][A-M]" < $1
+ fi
+}
+
+watch()
+{
+ if [ $# -ne 1 ] ; then
+ tail -f nohup.out
+ else
+ tail -f $1
+ fi
+}
+
+#
+# Remote login passing all 8 bits (so meta key will work)
+#
+rl()
+{
+ rlogin $* -8
+}
+
+function setenv()
+{
+ if [ $# -ne 2 ] ; then
+ echo "setenv: Too few arguments"
+ else
+ export $1="$2"
+ fi
+}
+
+function chmog()
+{
+ if [ $# -ne 4 ] ; then
+ echo "usage: chmog mode owner group file"
+ return 1
+ else
+ chmod $1 $4
+ chown $2 $4
+ chgrp $3 $4
+ fi
+}