diff options
Diffstat (limited to 'examples')
29 files changed, 1923 insertions, 547 deletions
diff --git a/examples/INDEX.html b/examples/INDEX.html index bd06b256..bcca1f9e 100644 --- a/examples/INDEX.html +++ b/examples/INDEX.html @@ -44,44 +44,28 @@ <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/basename2</td> - <td>Fast basename(1) and dirname(1) functions for BASH/SH.</td> - <td>basename, dirname</td> - </tr> - <tr> - <td>./functions/coproc.bash</td> - <td>Start, control, and end coprocesses.</td> - </tr> - <tr> - <td>./functions/coshell.bash</td> - <td>Control shell coprocesses (see coprocess.bash).</td> - </tr> - <tr> - <td>./functions/coshell.README</td> - <td>README for coshell and coproc.</td> - </tr> - <tr> <td>./functions/csh-compat</td> <td>A C-shell compatibility package.</td> <td>csh</td> </tr> <tr> - <td>./functions/dirfuncs</td> - <td>Directory manipulation functions from the book 'The Korn Shell'.</td> - </tr> - <tr> <td>./functions/dirname</td> <td>A replacement for dirname(1).</td> <td>dirname</td> </tr> <tr> - <td>./functions/emptydir</td> - <td>Find out if a directory is empty.</td> + <td>./functions/dirstack</td> + <td>Directory stack functions.</td> </tr> <tr> <td>./functions/exitstat</td> @@ -101,18 +85,6 @@ <td>stty.bash</td> </tr> <tr> - <td>./functions/func</td> - <td>Print out definitions for functions named by arguments.</td> - </tr> - <tr> - <td>./functions/gethtml</td> - <td>Get a web page from a remote server (wget(1) in bash!).</td> - </tr> - <tr> - <td>./functions/getoptx.bash</td> - <td>getopt function that parses long-named options.</td> - </tr> - <tr> <td>./functions/inetaddr</td> <td>Internet address conversion (inet2hex & hex2inet).</td> </tr> @@ -122,10 +94,6 @@ <td>inpath</td> </tr> <tr> - <td>./functions/isnum.bash</td> - <td>Test user input on numeric or character value.</td> - </tr> - <tr> <td>./functions/isnum2</td> <td>Test user input on numeric values, with floating point.</td> </tr> @@ -134,18 +102,6 @@ <td>Test user input for valid IP Addresses.</td> </tr> <tr> - <td>./functions/jdate.bash</td> - <td>Julian date conversion.</td> - </tr> - <tr> - <td>./functions/jj.bash</td> - <td>Look for running jobs.</td> - </tr> - <tr> - <td>./functions/keep</td> - <td>Try to keep some programs in the foreground and running.</td> - </tr> - <tr> <td>./functions/ksh-cd</td> <td>ksh-like 'cd': cd [-LP] [dir [change]].</td> <td>ksh</td> @@ -165,47 +121,14 @@ <td>Replace the 'login' and 'newgrp' builtins in old Bourne shells.</td> </tr> <tr> - <td>./functions/lowercase</td> - <td>Rename files to lower case.</td> - <td>rename lower</td> - </tr> - <tr> - <td>./functions/manpage</td> - <td>Find and print a manual page.</td> - <td>fman</td> - </tr> - <tr> - <td>./functions/mhfold</td> - <td>Print MH folders, useful only because folders(1) doesn't print mod date/times.</td> - </tr> - <tr> <td>./functions/notify.bash</td> <td>Notify when jobs change status.</td> </tr> <tr> - <td>./functions/pathfuncs</td> - <td>Path related functions (no_path, add_path, pre-path, del_path).</td> - <td>path</td> - </tr> - <tr> <td>./functions/README</td> <td>README</td> </tr> <tr> - <td>./functions/recurse</td> - <td>Recursive directory traverser.</td> - </tr> - <tr> - <td>./functions/repeat2</td> - <td>A clone of C shell builtin 'repeat'.</td> - <td>repeat, csh</td> - </tr> - <tr> - <td>./functions/repeat3</td> - <td>A clone of C shell builtin 'repeat'.</td> - <td>repeat, csh</td> - </tr> - <tr> <td>./functions/seq</td> <td>Generate a sequence from m to n, m defaults to 1.</td> </tr> @@ -238,10 +161,6 @@ <td>ksh</td> </tr> <tr> - <td>./functions/term</td> - <td>A shell function to set the terminal type interactively or not.</td> - </tr> - <tr> <td>./functions/whatis</td> <td>An implementation of the 10th Edition Unix sh builtin 'whatis(1)' command.</td> </tr> @@ -254,17 +173,6 @@ <td>An emulation of 'which(1)' as it appears in FreeBSD.</td> </tr> <tr> - <td>./functions/xalias.bash</td> - <td>Convert csh alias commands to bash functions.</td> - <td>csh, aliasconv</td> - </tr> - <tr> - <td>./functions/xfind.bash</td> - <td>A 'find(1)' clone.</td> - </tr> - <tr> - </tr> - <tr> <td>./loadables/</td> <td>Example loadable replacements</td> </tr> @@ -279,25 +187,17 @@ <td>cat, readline pager</td> </tr> <tr> - <td>./loadables/cut.c</td> - <td>cut(1) replacement.</td> - </tr> - <tr> <td>./loadables/dirname.c</td> <td>Return directory portion of pathname.</td> <td>dirname</td> </tr> <tr> - <td>./loadables/finfo.c</td> - <td>Print file info.</td> + <td>./loadables/fdflags.c</td> + <td>Display or modify file descriptor flags</td> </tr> <tr> - <td>./loadables/getconf.c</td> - <td>POSIX.2 getconf utility.</td> - </tr> - <tr> - <td>./loadables/getconf.h</td> - <td>Replacement definitions for ones the system doesn't provide.</td> + <td>./loadables/finfo.c</td> + <td>Print file info.</td> </tr> <tr> <td>./loadables/head.c</td> @@ -324,10 +224,18 @@ <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> @@ -356,14 +264,26 @@ <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> @@ -431,221 +351,12 @@ <td>README</td> </tr> <tr> - <td>./misc/suncmd.termcap</td> - <td>SunView TERMCAP string.</td> - </tr> - <tr> - </tr> - <tr> - <td>./scripts.noah</td> - <td>Noah Friedman's collection of scripts (updated to bash v2 syntax by Chet Ramey)</td> - </tr> - <tr> - <td>./scripts.noah/aref.bash</td> - <td>Pseudo-arrays and substring indexing examples.</td> - </tr> - <tr> - <td>./scripts.noah/bash.sub.bash</td> - <td>Library functions used by require.bash.</td> - </tr> - <tr> - <td>./scripts.noah/bash_version.bash</td> - <td>A function to slice up $BASH_VERSION.</td> - </tr> - <tr> - <td>./scripts.noah/meta.bash</td> - <td>Enable and disable eight-bit readline input.</td> - </tr> - <tr> - <td>./scripts.noah/mktmp.bash</td> - <td>Make a temporary file with a unique name.</td> - </tr> - <tr> - <td>./scripts.noah/number.bash</td> - <td>A fun hack to translate numerals into English.</td> - </tr> - <tr> - <td>./scripts.noah/PERMISSION</td> - <td>Permissions to use the scripts in this directory.</td> - </tr> - <tr> - <td>./scripts.noah/prompt.bash</td> - <td>A way to set PS1 to some predefined strings.</td> - </tr> - <tr> - <td>./scripts.noah/README</td> - <td>README</td> - </tr> - <tr> - <td>./scripts.noah/remap_keys.bash</td> - <td>A front end to 'bind' to redo readline bindings.</td> - </tr> - <tr> - <td>./scripts.noah/require.bash</td> - <td>Lisp-like require/provide library functions for bash.</td> - </tr> - <tr> - <td>./scripts.noah/send_mail.bash</td> - <td>Replacement SMTP client written in bash.</td> - </tr> - <tr> - <td>./scripts.noah/shcat.bash</td> - <td>Bash replacement for 'cat(1)'.</td> - <td>cat</td> - </tr> - <tr> - <td>./scripts.noah/source.bash</td> - <td>Replacement for source that uses current directory.</td> - </tr> - <tr> - <td>./scripts.noah/string.bash</td> - <td>The string(3) functions at the shell level.</td> - </tr> - <tr> - <td>./scripts.noah/stty.bash</td> - <td>Front-end to stty(1) that changes readline bindings too.</td> - <td>fstty</td> - </tr> - <tr> - <td>./scripts.noah/y_or_n_p.bash</td> - <td>Prompt for a yes/no/quit answer.</td> - <td>ask</td> - </tr> - <tr> - </tr> - <tr> - <td>./scripts.v2</td> - <td>John DuBois' ksh script collection (converted to bash v2 syntax by Chet Ramey).</td> - </tr> - <tr> - <td>./scripts.v2/arc2tarz</td> - <td>Convert an "arc" archive to a compressed tar archive.</td> - </tr> - <tr> - <td>./scripts.v2/bashrand</td> - <td>Random number generator with upper and lower bounds and optional seed.</td> - <td>random</td> - </tr> - <tr> - <td>./scripts.v2/cal2day.bash</td> - <td>Convert a day number to a name.</td> - </tr> - <tr> - <td>./scripts.v2/cdhist.bash</td> - <td>cd replacement with a directory stack added.</td> - </tr> - <tr> - <td>./scripts.v2/corename</td> - <td>Tell what produced a core file.</td> - </tr> - <tr> - <td>./scripts.v2/fman</td> - <td>Fast man(1) replacement.</td> - <td>manpage</td> - </tr> - <tr> - <td>./scripts.v2/frcp</td> - <td>Copy files using ftp(1) but with rcp-type command line syntax.</td> - </tr> - <tr> - <td>./scripts.v2/lowercase</td> - <td>Change filenames to lower case.</td> - <td>rename lower</td> - </tr> - <tr> - <td>./scripts.v2/ncp</td> - <td>A nicer front end for cp(1) (has -i, etc.).</td> - </tr> - <tr> - <td>./scripts.v2/newext</td> - <td>Change the extension of a group of files.</td> - <td>rename</td> - </tr> - <tr> - <td>./scripts.v2/nmv</td> - <td>A nicer front end for mv(1) (has -i, etc.).</td> - <td>rename</td> - </tr> - <tr> - <td>./scripts.v2/pages</td> - <td>Print specified pages from files.</td> - </tr> - <tr> - <td>./scripts.v2/PERMISSION</td> - <td>Permissions to use the scripts in this directory.</td> - </tr> - <tr> - <td>./scripts.v2/pf</td> - <td>A pager front end that handles compressed files.</td> - </tr> - <tr> - <td>./scripts.v2/pmtop</td> - <td>Poor man's 'top(1)' for SunOS 4.x and BSD/OS.</td> - </tr> - <tr> - <td>./scripts.v2/README</td> - <td>README</td> - </tr> - <tr> - <td>./scripts.v2/ren</td> - <td>Rename files by changing parts of filenames that match a pattern.</td> - <td>rename</td> - </tr> - <tr> - <td>./scripts.v2/rename</td> - <td>Change the names of files that match a pattern.</td> - <td>rename</td> - </tr> - <tr> - <td>./scripts.v2/repeat</td> - <td>Execute a command multiple times.</td> - <td>repeat</td> - </tr> - <tr> - <td>./scripts.v2/shprof</td> - <td>Line profiler for bash scripts.</td> - </tr> - <tr> - <td>./scripts.v2/untar</td> - <td>Unarchive a (possibly compressed) tarfile into a directory.</td> - </tr> - <tr> - <td>./scripts.v2/uudec</td> - <td>Carefully uudecode(1) multiple files.</td> - </tr> - <tr> - <td>./scripts.v2/uuenc</td> - <td>uuencode(1) multiple files.</td> - </tr> - <tr> - <td>./scripts.v2/vtree</td> - <td>Print a visual display of a directory tree.</td> - <td>tree</td> - </tr> - <tr> - <td>./scripts.v2/where</td> - <td>Show where commands that match a pattern are.</td> - </tr> - <tr> </tr> <tr> <td>./scripts</td> <td>Example scripts</td> </tr> <tr> - <td>./scripts/adventure.sh</td> - <td>Text adventure game in bash!</td> - </tr> - <tr> - <td>./scripts/bcsh.sh</td> - <td>Bourne shell cshell-emulator.</td> - <td>csh</td> - </tr> - <tr> - <td>./scripts/bash-hexdump.sh</td> - <td>hexdump(1) in bash</td> - <td>hexdump -C, hd</td> - <tr> <td>./scripts/cat.sh</td> <td>Readline-based pager.</td> <td>cat, readline pager</td> @@ -655,65 +366,15 @@ <td>Center - center a group of lines.</td> </tr> <tr> - <td>./scripts/dd-ex.sh</td> - <td>Line editor using only /bin/sh, /bin/dd and /bin/rm.</td> - </tr> - <tr> - <td>./scripts/fixfiles.bash</td> - <td>Recurse a tree and fix files containing various "bad" chars.</td> - </tr> - <tr> - <td>./scripts/hanoi.bash</td> - <td>The inevitable Towers of Hanoi in bash.</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/krand.bash</td> - <td>Produces a random number within integer limits.</td> - <td>random</td> - </tr> - <tr> - <td>./scripts/line-input.bash</td> - <td>Line input routine for GNU Bourne-Again Shell plus terminal-control primitives.</td> - </tr> - <tr> - <td>./scripts/nohup.bash</td> - <td>bash version of 'nohup' command.</td> - </tr> - <tr> - <td>./scripts/precedence</td> - <td>Test relative precedences for '&&' and '||' operators.</td> - </tr> - <tr> - <td>./scripts/randomcard.bash</td> - <td>Print a random card from a card deck.</td> - <td>random</td> - </tr> - <tr> <td>./scripts/README</td> <td>README</td> </tr> <tr> - <td>./scripts/scrollbar</td> - <td>Display scrolling text.</td> - </tr> - <tr> - <td>./scripts/scrollbar2</td> - <td>Display scrolling text.</td> - </tr> - <tr> - <td>./scripts/self-repro</td> - <td>A self-reproducing script (careful!)</td> - </tr> - <tr> - <td>./scripts/showperm.bash</td> - <td>Convert ls(1) symbolic permissions into octal mode.</td> - </tr> - <tr> <td>./scripts/shprompt</td> <td>Display a prompt and get an answer satisfying certain criteria.</td> <td>ask</td> @@ -723,37 +384,6 @@ <td>Display a 'spinning wheel' to show progress.</td> </tr> <tr> - <td>./scripts/timeout</td> - <td>Give rsh(1) a shorter timeout.</td> - </tr> - <tr> - <td>./scripts/timeout2</td> - <td>Execute a given command with a timeout.</td> - </tr> - <tr> - <td>./scripts/timeout3</td> - <td>Execute a given command with a timeout.</td> - </tr> - <tr> - <td>./scripts/vtree2</td> - <td>Display a tree printout of dir in 1k blocks.</td> - <td>tree</td> - </tr> - <tr> - <td>./scripts/vtree3</td> - <td>Display a graphical tree printout of dir.</td> - <td>tree</td> - </tr> - <tr> - <td>./scripts/vtree3a</td> - <td>Display a graphical tree printout of dir.</td> - <td>tree</td> - </tr> - <tr> - <td>./scripts/websrv.sh</td> - <td>A web server in bash!</td> - </tr> - <tr> <td>./scripts/xterm_title</td> <td>Print the contents of the xterm title bar.</td> </tr> @@ -793,36 +423,4 @@ </tr> <tr> </tr> - <tr> - <td>./startup-files/apple</td> - <td>Example Start-up files for Mac OS X.</td> - </tr> - <tr> - <td>./startup-files/apple/aliases</td> - <td>Sample aliases for Mac OS X.</td> - </tr> - <tr> - <td>./startup-files/apple/bash.defaults</td> - <td>Sample User preferences file.</td> - </tr> - <tr> - <td>./startup-files/apple/environment</td> - <td>Sample Bourne Again Shell environment file.</td> - </tr> - <tr> - <td>./startup-files/apple/login</td> - <td>Sample login wrapper.</td> - </tr> - <tr> - <td>./startup-files/apple/logout</td> - <td>Sample logout wrapper.</td> - </tr> - <tr> - <td>./startup-files/apple/rc</td> - <td>Sample Bourne Again Shell config file.</td> - </tr> - <tr> - <td>./startup-files/apple/README</td> - <td>README</td> - </tr> </table> diff --git a/examples/INDEX.txt b/examples/INDEX.txt index 4b5478ee..b47e2113 100644 --- a/examples/INDEX.txt +++ b/examples/INDEX.txt @@ -9,43 +9,25 @@ Path Description X-Ref ./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/basename2 Fast basename(1) and dirname(1) functions for BASH/SH. basename, dirname -./functions/coproc.bash Start, control, and end coprocesses. -./functions/coshell.bash Control shell coprocesses (see coprocess.bash). -./functions/coshell.README README for coshell and coproc. ./functions/csh-compat A C-shell compatibility package. csh -./functions/dirfuncs Directory manipulation functions from the book 'The Korn Shell'. ./functions/dirname A replacement for dirname(1). dirname -./functions/emptydir Find out if a directory is empty. +./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/func Print out definitions for functions named by arguments. -./functions/gethtml Get a web page from a remote server (wget(1) in bash!). -./functions/getoptx.bash getopt function that parses long-named options. ./functions/inetaddr Internet address conversion (inet2hex & hex2inet). ./functions/inpath Return zero if the argument is in the path and executable. inpath -./functions/isnum.bash Test user input on numeric or character value. ./functions/isnum2 Test user input on numeric values, with floating point. ./functions/isvalidip Test user input for valid IP Addresses. -./functions/jdate.bash Julian date conversion. -./functions/jj.bash Look for running jobs. -./functions/keep Try to keep some programs in the foreground and running. ./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/lowercase Rename files to lower case. rename lower -./functions/manpage Find and print a manual page. fman -./functions/mhfold Print MH folders, useful only because folders(1) doesn't print mod date/times. ./functions/notify.bash Notify when jobs change status. -./functions/pathfuncs Path related functions (no_path, add_path, pre-path, del_path). path ./functions/README README -./functions/recurse Recursive directory traverser. -./functions/repeat2 A clone of C shell builtin 'repeat'. repeat, csh -./functions/repeat3 A clone of C shell builtin 'repeat'. repeat, csh ./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 @@ -53,28 +35,25 @@ Path Description X-Ref ./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/term A shell function to set the terminal type interactively or not. ./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. -./functions/xalias.bash Convert csh alias commands to bash functions. csh, aliasconv -./functions/xfind.bash A 'find(1)' clone. ./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/cut.c cut(1) replacement. ./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/getconf.c POSIX.2 getconf utility. -./loadables/getconf.h Replacement definitions for ones the system doesn't provide. ./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. @@ -82,8 +61,11 @@ Path Description X-Ref ./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. @@ -101,83 +83,13 @@ Path Description X-Ref ./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 -./misc/suncmd.termcap SunView TERMCAP string. - -./scripts.noah Noah Friedman's collection of scripts (updated to bash v2 syntax by Chet Ramey) -./scripts.noah/aref.bash Pseudo-arrays and substring indexing examples. -./scripts.noah/bash.sub.bash Library functions used by require.bash. -./scripts.noah/bash_version.bash A function to slice up $BASH_VERSION. -./scripts.noah/meta.bash Enable and disable eight-bit readline input. -./scripts.noah/mktmp.bash Make a temporary file with a unique name. -./scripts.noah/number.bash A fun hack to translate numerals into English. -./scripts.noah/PERMISSION Permissions to use the scripts in this directory. -./scripts.noah/prompt.bash A way to set PS1 to some predefined strings. -./scripts.noah/README README -./scripts.noah/remap_keys.bash A front end to 'bind' to redo readline bindings. -./scripts.noah/require.bash Lisp-like require/provide library functions for bash. -./scripts.noah/send_mail.bash Replacement SMTP client written in bash. -./scripts.noah/shcat.bash Bash replacement for 'cat(1)'. cat -./scripts.noah/source.bash Replacement for source that uses current directory. -./scripts.noah/string.bash The string(3) functions at the shell level. -./scripts.noah/stty.bash Front-end to stty(1) that changes readline bindings too. fstty -./scripts.noah/y_or_n_p.bash Prompt for a yes/no/quit answer. ask - -./scripts.v2 John DuBois' ksh script collection (converted to bash v2 syntax by Chet Ramey). -./scripts.v2/arc2tarz Convert an "arc" archive to a compressed tar archive. -./scripts.v2/bashrand Random number generator with upper and lower bounds and optional seed. random -./scripts.v2/cal2day.bash Convert a day number to a name. -./scripts.v2/cdhist.bash cd replacement with a directory stack added. -./scripts.v2/corename Tell what produced a core file. -./scripts.v2/fman Fast man(1) replacement. manpage -./scripts.v2/frcp Copy files using ftp(1) but with rcp-type command line syntax. -./scripts.v2/lowercase Change filenames to lower case. rename lower -./scripts.v2/ncp A nicer front end for cp(1) (has -i, etc.). -./scripts.v2/newext Change the extension of a group of files. rename -./scripts.v2/nmv A nicer front end for mv(1) (has -i, etc.). rename -./scripts.v2/pages Print specified pages from files. -./scripts.v2/PERMISSION Permissions to use the scripts in this directory. -./scripts.v2/pf A pager front end that handles compressed files. -./scripts.v2/pmtop Poor man's 'top(1)' for SunOS 4.x and BSD/OS. -./scripts.v2/README README -./scripts.v2/ren Rename files by changing parts of filenames that match a pattern. rename -./scripts.v2/rename Change the names of files that match a pattern. rename -./scripts.v2/repeat Execute a command multiple times. repeat -./scripts.v2/shprof Line profiler for bash scripts. -./scripts.v2/untar Unarchive a (possibly compressed) tarfile into a directory. -./scripts.v2/uudec Carefully uudecode(1) multiple files. -./scripts.v2/uuenc uuencode(1) multiple files. -./scripts.v2/vtree Print a visual display of a directory tree. tree -./scripts.v2/where Show where commands that match a pattern are. ./scripts Example scripts -./scripts/adventure.sh Text adventure game in bash! -./scripts/bash-hexdump.sh hexdump(1) in bash -./scripts/bcsh.sh Bourne shell cshell-emulator. csh ./scripts/cat.sh Readline-based pager. cat, readline pager ./scripts/center Center - center a group of lines. -./scripts/dd-ex.sh Line editor using only /bin/sh, /bin/dd and /bin/rm. -./scripts/fixfiles.bash Recurse a tree and fix files containing various "bad" chars. -./scripts/hanoi.bash The inevitable Towers of Hanoi in bash. ./scripts/inpath Search $PATH for a file the same name as $1; return TRUE if found. inpath -./scripts/krand.bash Produces a random number within integer limits. random -./scripts/line-input.bash Line input routine for GNU Bourne-Again Shell plus terminal-control primitives. -./scripts/nohup.bash bash version of 'nohup' command. -./scripts/precedence Test relative precedences for '&&' and '||' operators. -./scripts/randomcard.bash Print a random card from a card deck. random -./scripts/README README -./scripts/scrollbar Display scrolling text. -./scripts/scrollbar2 Display scrolling text. -./scripts/self-repro A self-reproducing script (careful!) -./scripts/showperm.bash Convert ls(1) symbolic permissions into octal mode. ./scripts/shprompt Display a prompt and get an answer satisfying certain criteria. ask ./scripts/spin.bash Display a 'spinning wheel' to show progress. -./scripts/timeout Give rsh(1) a shorter timeout. -./scripts/timeout2 Execute a given command with a timeout. -./scripts/timeout3 Execute a given command with a timeout. -./scripts/vtree2 Display a tree printout of dir in 1k blocks. tree -./scripts/vtree3 Display a graphical tree printout of dir. tree -./scripts/vtree3a Display a graphical tree printout of dir. tree -./scripts/websrv.sh A web server in bash! ./scripts/xterm_title Print the contents of the xterm title bar. ./scripts/zprintf Emulate printf (obsolete since it's now a bash builtin). @@ -189,11 +101,3 @@ Path Description X-Ref ./startup-files/Bashrc.bfox Sample Bourne Again SHell init file (Fox). ./startup-files/README README -./startup-files/apple Example Start-up files for Mac OS X. -./startup-files/apple/aliases Sample aliases for Mac OS X. -./startup-files/apple/bash.defaults Sample User preferences file. -./startup-files/apple/environment Sample Bourne Again Shell environment file. -./startup-files/apple/login Sample login wrapper. -./startup-files/apple/logout Sample logout wrapper. -./startup-files/apple/rc Sample Bourne Again Shell config file. -./startup-files/apple/README README diff --git a/examples/bash-completion/README b/examples/bash-completion/README new file mode 100644 index 00000000..fb5e7654 --- /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 Binary files differnew file mode 100644 index 00000000..f5b90790 --- /dev/null +++ b/examples/bash-completion/bash-completion-2.5.tar.xz diff --git a/examples/functions/arrayops.bash b/examples/functions/arrayops.bash new file mode 100644 index 00000000..d34353ae --- /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 index a563a779..cb3a673e 100644 --- a/examples/functions/autoload +++ b/examples/functions/autoload @@ -22,7 +22,7 @@ # # Declare a function ($1) to be autoloaded from a file ($2) when it is first # called. This defines a `temporary' function that will `.' the file -# containg the real function definition, then execute that new definition with +# 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. # diff --git a/examples/functions/autoload.v4 b/examples/functions/autoload.v4 new file mode 100644 index 00000000..a172e612 --- /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 funcion 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 envirnoment, 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 + Programer. + + 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 consquence 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 extentions. + + At the time of this writing, git extentions 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 00000000..6d35d141 --- /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/loadables/Makefile.in b/examples/loadables/Makefile.in index ec305cda..0facc0ba 100644 --- a/examples/loadables/Makefile.in +++ b/examples/loadables/Makefile.in @@ -103,7 +103,7 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \ ALLPROG = print truefalse sleep finfo logname basename dirname \ tty pathchk tee head mkdir rmdir printenv id whoami \ uname sync push ln unlink realpath strftime mypid setpgid -OTHERPROG = necho hello cat pushd +OTHERPROG = necho hello cat pushd stat rm fdflags all: $(SHOBJ_STATUS) @@ -142,6 +142,12 @@ finfo: finfo.o 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) + logname: logname.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ logname.o $(SHOBJ_LIBS) @@ -202,10 +208,12 @@ strftime: strftime.o 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) + # pushd is a special case. We use the same source that the builtin version # uses, with special compilation options. # @@ -236,6 +244,7 @@ installdirs: install-dev: installdirs @$(INSTALL_DATA) Makefile.inc $(DESTDIR)$(loadablesdir)/Makefile.inc + @$(INSTALL_DATA) $(srcdir)/loadables.h $(DESTDIR)$(loadablesdir)/loadables.h @( cd $(BUILD_DIR) && ${MAKE} ${MFLAGS} DESTDIR="$(DESTDIR)" install-headers) install-supported: all installdirs install-dev @@ -246,7 +255,7 @@ install-supported: all installdirs install-dev done uninstall-dev: - -$(RM) $(DESTDIR)$(loadablesdir)/Makefile.inc + -$(RM) $(DESTDIR)$(loadablesdir)/Makefile.inc $(DESTDIR)$(loadablesdir)/loadables.h -( cd $(BUILD_DIR) && ${MAKE} ${MFLAGS} DESTDIR="$(DESTDIR)" uninstall-headers) uninstall-supported: uninstall-dev @@ -283,3 +292,4 @@ mkdir.o: mkdir.c realpath.o: realpath.c strftime.o: strftime.c setpgid.o: setpgid.c +stat.o: stat.c diff --git a/examples/loadables/README b/examples/loadables/README index 2eae9cc6..91febd48 100644 --- a/examples/loadables/README +++ b/examples/loadables/README @@ -32,28 +32,37 @@ 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. dirname.c Return directory portion of pathname. +fdflags.c Change the flag associated with one of bash's open file desriptors. 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 Start at a file loadable builtins can include for shell definitions +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. mkdir.c Make directories. -mypid.c Add $MYPID variable, demonstrate use of unload hook function +mypid.c Add $MYPID variable, demonstrate use of unload hook functio.n 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? -README README realpath.c Canonicalize pathnames, resolving symlinks. +rm.c Remove files and directories. rmdir.c Remove directory. +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. diff --git a/examples/loadables/cat.c b/examples/loadables/cat.c index 1ce2e2dc..be99c4cd 100644 --- a/examples/loadables/cat.c +++ b/examples/loadables/cat.c @@ -56,6 +56,7 @@ int fd; return 0; } +int cat_main (argc, argv) int argc; char **argv; @@ -88,6 +89,7 @@ char **argv; return (r); } +int cat_builtin(list) WORD_LIST *list; { diff --git a/examples/loadables/fdflags.c b/examples/loadables/fdflags.c new file mode 100644 index 00000000..f3094666 --- /dev/null +++ b/examples/loadables/fdflags.c @@ -0,0 +1,336 @@ +/* Loadable builtin to get and set file descriptor flags. */ + +/* See Makefile for compilation details. */ + +/* + Copyright (C) 2017 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" + +static const struct +{ + const char *name; + int value; +} file_flags[] = +{ +#ifdef O_APPEND + { "append", O_APPEND }, +#endif +#ifdef O_ASYNC + { "async", O_ASYNC }, +#endif +#ifdef O_SYNC + { "sync", O_SYNC }, +#endif +#ifdef O_NONBLOCK + { "nonblock", O_NONBLOCK }, +#endif +#ifdef O_FSYNC + { "fsync", O_FSYNC }, +#endif +#ifdef O_DSYNC + { "dsync", O_DSYNC }, +#endif +#ifdef O_RSYNC + { "rsync", O_RSYNC }, +#endif +#ifdef O_ALT_IO + { "altio", O_ALT_IO }, +#endif +#ifdef O_DIRECT + { "direct", O_DIRECT }, +#endif +#ifdef O_NOATIME + { "noatime", O_NOATIME }, +#endif +#ifdef O_NOSIGPIPE + { "nosigpipe", O_NOSIGPIPE }, +#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/head.c b/examples/loadables/head.c index 748bb83e..1edca6c5 100644 --- a/examples/loadables/head.c +++ b/examples/loadables/head.c @@ -117,6 +117,7 @@ head_builtin (list) return (EX_USAGE); } break; + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); diff --git a/examples/loadables/id.c b/examples/loadables/id.c index 87733494..f857b547 100644 --- a/examples/loadables/id.c +++ b/examples/loadables/id.c @@ -91,6 +91,7 @@ id_builtin (list) 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); diff --git a/examples/loadables/ln.c b/examples/loadables/ln.c index a853bc99..93764a35 100644 --- a/examples/loadables/ln.c +++ b/examples/loadables/ln.c @@ -76,6 +76,7 @@ ln_builtin (list) case 'n': flags |= LN_NOFOLLOW; break; + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); diff --git a/examples/loadables/mkdir.c b/examples/loadables/mkdir.c index 39ae07f9..767ad9eb 100644 --- a/examples/loadables/mkdir.c +++ b/examples/loadables/mkdir.c @@ -68,6 +68,7 @@ mkdir_builtin (list) case 'm': mode = list_optarg; break; + CASE_HELPOPT; default: builtin_usage(); return (EX_USAGE); diff --git a/examples/loadables/necho.c b/examples/loadables/necho.c index ee65f184..dd2092b0 100644 --- a/examples/loadables/necho.c +++ b/examples/loadables/necho.c @@ -38,7 +38,7 @@ WORD_LIST *list; char *necho_doc[] = { "Display arguments.", "", - "Print the arguments to the standard ouput separated", + "Print the arguments to the standard output separated", "by space characters and terminated with a newline.", (char *)NULL }; diff --git a/examples/loadables/pathchk.c b/examples/loadables/pathchk.c index 85e8a04c..c1151db4 100644 --- a/examples/loadables/pathchk.c +++ b/examples/loadables/pathchk.c @@ -112,6 +112,7 @@ pathchk_builtin (list) case 'p': pflag = 1; break; + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); @@ -135,7 +136,7 @@ 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 casuing syntax errors) and portability", + "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 diff --git a/examples/loadables/print.c b/examples/loadables/print.c index e17597b3..0120dbf4 100644 --- a/examples/loadables/print.c +++ b/examples/loadables/print.c @@ -122,6 +122,7 @@ print_builtin (list) case 'f': pfmt = list_optarg; break; + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); diff --git a/examples/loadables/printenv.c b/examples/loadables/printenv.c index 8d3a05df..8c7f7201 100644 --- a/examples/loadables/printenv.c +++ b/examples/loadables/printenv.c @@ -46,6 +46,7 @@ printenv_builtin (list) { switch (opt) { + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); diff --git a/examples/loadables/push.c b/examples/loadables/push.c index b0760733..9bcd5c32 100644 --- a/examples/loadables/push.c +++ b/examples/loadables/push.c @@ -51,6 +51,7 @@ push_builtin (list) { switch (opt) { + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); diff --git a/examples/loadables/realpath.c b/examples/loadables/realpath.c index b19b87fb..9892ddb8 100644 --- a/examples/loadables/realpath.c +++ b/examples/loadables/realpath.c @@ -86,15 +86,19 @@ WORD_LIST *list; case 'v': vflag = 1; break; + CASE_HELPOPT; default: builtin_usage(); + return (EX_USAGE); } } list = loptend; - if (list == 0) + if (list == 0) { builtin_usage(); + return (EX_USAGE); + } for (es = EXECUTION_SUCCESS; list; list = list->next) { p = list->word->word; diff --git a/examples/loadables/rm.c b/examples/loadables/rm.c new file mode 100644 index 00000000..adfbffdc --- /dev/null +++ b/examples/loadables/rm.c @@ -0,0 +1,177 @@ +/* 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 + + 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 + } + + 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; + + /* 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) + { + builtin_usage (); + return (EXECUTION_FAILURE); + } + + for (l = list; l; l = l->next) + { + 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/stat.c b/examples/loadables/stat.c new file mode 100644 index 00000000..52b9580f --- /dev/null +++ b/examples/loadables/stat.c @@ -0,0 +1,430 @@ +/* stat - load up an associative array with stat information about a file */ + +/* 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> + +#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 + }; + +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) + time_t t; +{ + char *tbuf, *ret; + size_t tlen; + + tbuf = ctime (&t); + tlen = strlen (tbuf); + ret = savestring (tbuf); + ret[tlen-1] = '\0'; + return ret; +} + +static char * +statval (which, fname, flags, sp) + int which; + char *fname; + int flags; + 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) : itos (sp->st_atime)); + case ST_MTIME: + return ((flags & 2) ? stattime (sp->st_mtime) : itos (sp->st_mtime)); + case ST_CTIME: + return ((flags & 2) ? stattime (sp->st_ctime) : 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, sp) + char *vname; + SHELL_VAR *var; + char *fname; + int flags; + 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, 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; + struct stat st; + SHELL_VAR *v; + + aname = "STAT"; + flags = 0; + + reset_internal_getopt (); + while ((opt = internal_getopt (list, "A: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_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + + list = loptend; + if (list == 0) + { + builtin_usage (); + return (EX_USAGE); + } + + fname = list->word->word; + + if (getstat (fname, flags, &st) < 0) + { + builtin_error ("%s: cannot stat: %s", fname, strerror (errno)); + return (EXECUTION_FAILURE); + } + + unbind_variable (aname); + v = make_new_assoc_variable (aname); + if (v == 0) + { + builtin_error ("%s: cannot create variable", aname); + return (EXECUTION_FAILURE); + } + if (loadstat (aname, v, fname, flags, &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. 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/tee.c b/examples/loadables/tee.c index 9462cda0..819d83ae 100644 --- a/examples/loadables/tee.c +++ b/examples/loadables/tee.c @@ -84,6 +84,7 @@ tee_builtin (list) case 'i': nointr = 1; break; + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); diff --git a/examples/loadables/template.c b/examples/loadables/template.c index 8cfd571d..094b80ce 100644 --- a/examples/loadables/template.c +++ b/examples/loadables/template.c @@ -31,6 +31,7 @@ template_builtin (list) { switch (opt) { + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); diff --git a/examples/loadables/tty.c b/examples/loadables/tty.c index 1adc5b59..febf518b 100644 --- a/examples/loadables/tty.c +++ b/examples/loadables/tty.c @@ -46,6 +46,7 @@ tty_builtin (list) case 's': sflag = 1; break; + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); diff --git a/examples/loadables/uname.c b/examples/loadables/uname.c index 339ec3dd..106a1c8d 100644 --- a/examples/loadables/uname.c +++ b/examples/loadables/uname.c @@ -95,6 +95,7 @@ uname_builtin (list) case 'v': uname_flags |= FLAG_VERSION; break; + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); diff --git a/examples/loadables/whoami.c b/examples/loadables/whoami.c index 5aa73828..3e7e36e4 100644 --- a/examples/loadables/whoami.c +++ b/examples/loadables/whoami.c @@ -39,6 +39,7 @@ whoami_builtin (list) { switch (opt) { + CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); |
