mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-07-04 02:40:49 +02:00
bash-5.0 distribution sources and documentation
This commit is contained in:
+31
-433
@@ -43,45 +43,29 @@
|
||||
<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/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>
|
||||
@@ -100,18 +84,6 @@
|
||||
<td>Front end to sync TERM changes to both stty(1) and readline 'bind'.</td>
|
||||
<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>
|
||||
@@ -121,10 +93,6 @@
|
||||
<td>Return zero if the argument is in the path and executable.</td>
|
||||
<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>
|
||||
@@ -133,18 +101,6 @@
|
||||
<td>./functions/isvalidip</td>
|
||||
<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>
|
||||
@@ -164,47 +120,14 @@
|
||||
<td>./functions/login</td>
|
||||
<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>
|
||||
@@ -237,10 +160,6 @@
|
||||
<td>A function to emulate the ancient ksh builtin.</td>
|
||||
<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>
|
||||
@@ -253,17 +172,6 @@
|
||||
<td>./functions/which</td>
|
||||
<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>
|
||||
@@ -278,27 +186,19 @@
|
||||
<td>cat(1) replacement with no options - the way cat was intended.</td>
|
||||
<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/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/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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>./loadables/head.c</td>
|
||||
<td>Copy first part of files.</td>
|
||||
@@ -323,10 +223,18 @@
|
||||
<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>
|
||||
@@ -355,14 +263,26 @@
|
||||
<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>
|
||||
@@ -430,221 +350,12 @@
|
||||
<td>./misc/README</td>
|
||||
<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>
|
||||
@@ -654,65 +365,15 @@
|
||||
<td>./scripts/center</td>
|
||||
<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>
|
||||
@@ -722,37 +383,6 @@
|
||||
<td>./scripts/spin.bash</td>
|
||||
<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>
|
||||
@@ -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>
|
||||
|
||||
+8
-104
@@ -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
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
Master source: https://github.com/scop/bash-completion
|
||||
|
||||
This is the latest version of the bash-completion package, which provides
|
||||
programmable completion specifications for a large number of commands.
|
||||
|
||||
If you are a vendor installing bash or preparing a package containing bash,
|
||||
please install the latest version of bash-completion when installing bash.
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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.
|
||||
#
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -100,10 +100,10 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \
|
||||
$(SHOBJ_CC) $(SHOBJ_CFLAGS) $(CCFLAGS) $(INC) -c -o $@ $<
|
||||
|
||||
|
||||
ALLPROG = print truefalse sleep finfo logname basename dirname \
|
||||
ALLPROG = print truefalse sleep finfo logname basename dirname fdflags \
|
||||
tty pathchk tee head mkdir rmdir printenv id whoami \
|
||||
uname sync push ln unlink realpath strftime mypid setpgid
|
||||
OTHERPROG = necho hello cat pushd
|
||||
uname sync push ln unlink realpath strftime mypid setpgid seq
|
||||
OTHERPROG = necho hello cat pushd stat rm
|
||||
|
||||
all: $(SHOBJ_STATUS)
|
||||
|
||||
@@ -142,6 +142,15 @@ 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)
|
||||
|
||||
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)
|
||||
|
||||
@@ -202,10 +211,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 +247,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 +258,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 +295,6 @@ mkdir.o: mkdir.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
|
||||
|
||||
@@ -32,28 +32,39 @@ 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.
|
||||
Makefile.inc.in Sample makefile to use for loadable builtin development.
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "builtins.h"
|
||||
#include "shell.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
int
|
||||
basename_builtin (list)
|
||||
@@ -46,6 +47,7 @@ basename_builtin (list)
|
||||
|
||||
if (no_options (list))
|
||||
return (EX_USAGE);
|
||||
list = loptend;
|
||||
|
||||
string = list->word->word;
|
||||
suffix = (char *)NULL;
|
||||
|
||||
@@ -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;
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "builtins.h"
|
||||
#include "shell.h"
|
||||
#include "common.h"
|
||||
#include "bashgetopt.h"
|
||||
|
||||
int
|
||||
dirname_builtin (list)
|
||||
@@ -38,15 +39,16 @@ dirname_builtin (list)
|
||||
int slen;
|
||||
char *string;
|
||||
|
||||
if (no_options (list))
|
||||
return (EX_USAGE);
|
||||
list = loptend;
|
||||
|
||||
if (list == 0 || list->next)
|
||||
{
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
}
|
||||
|
||||
if (no_options (list))
|
||||
return (EX_USAGE);
|
||||
|
||||
string = list->word->word;
|
||||
slen = strlen (string);
|
||||
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
@@ -336,9 +336,9 @@ int flags;
|
||||
} else if (flags & OPT_DEV)
|
||||
printf("%d\n", st->st_dev);
|
||||
else if (flags & OPT_INO)
|
||||
printf("%d\n", st->st_ino);
|
||||
printf("%lu\n", (unsigned long)st->st_ino);
|
||||
else if (flags & OPT_FID)
|
||||
printf("%d:%ld\n", st->st_dev, st->st_ino);
|
||||
printf("%d:%lu\n", st->st_dev, (unsigned long)st->st_ino);
|
||||
else if (flags & OPT_NLINK)
|
||||
printf("%d\n", st->st_nlink);
|
||||
else if (flags & OPT_LNKNAM) {
|
||||
|
||||
@@ -117,6 +117,7 @@ head_builtin (list)
|
||||
return (EX_USAGE);
|
||||
}
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -76,6 +76,7 @@ ln_builtin (list)
|
||||
case 'n':
|
||||
flags |= LN_NOFOLLOW;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
@@ -52,12 +52,12 @@ int
|
||||
mkdir_builtin (list)
|
||||
WORD_LIST *list;
|
||||
{
|
||||
int opt, pflag, omode, rval, nmode, parent_mode;
|
||||
int opt, pflag, mflag, omode, rval, nmode, parent_mode;
|
||||
char *mode;
|
||||
WORD_LIST *l;
|
||||
|
||||
reset_internal_getopt ();
|
||||
pflag = 0;
|
||||
pflag = mflag = 0;
|
||||
mode = (char *)NULL;
|
||||
while ((opt = internal_getopt(list, "m:p")) != -1)
|
||||
switch (opt)
|
||||
@@ -66,8 +66,10 @@ mkdir_builtin (list)
|
||||
pflag = 1;
|
||||
break;
|
||||
case 'm':
|
||||
mflag = 1;
|
||||
mode = list_optarg;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage();
|
||||
return (EX_USAGE);
|
||||
@@ -114,7 +116,7 @@ mkdir_builtin (list)
|
||||
|
||||
for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
|
||||
{
|
||||
if (pflag && make_path (l->word->word, nmode, parent_mode))
|
||||
if (pflag && make_path (l->word->word, mflag, nmode, parent_mode))
|
||||
{
|
||||
rval = EXECUTION_FAILURE;
|
||||
continue;
|
||||
@@ -132,8 +134,9 @@ mkdir_builtin (list)
|
||||
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, nmode, parent_mode)
|
||||
make_path (path, user_mode, nmode, parent_mode)
|
||||
char *path;
|
||||
int user_mode;
|
||||
int nmode, parent_mode;
|
||||
{
|
||||
int oumask;
|
||||
@@ -148,7 +151,7 @@ make_path (path, nmode, parent_mode)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (chmod (path, nmode))
|
||||
if (user_mode && chmod (path, nmode))
|
||||
{
|
||||
builtin_error ("%s: %s", path, strerror (errno));
|
||||
return 1;
|
||||
@@ -172,13 +175,20 @@ make_path (path, nmode, parent_mode)
|
||||
*p = '\0';
|
||||
if (stat (npath, &sb) != 0)
|
||||
{
|
||||
if (mkdir (npath, parent_mode))
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -122,6 +122,7 @@ print_builtin (list)
|
||||
case 'f':
|
||||
pfmt = list_optarg;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
@@ -46,6 +46,7 @@ printenv_builtin (list)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
@@ -51,6 +51,7 @@ push_builtin (list)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
@@ -0,0 +1,490 @@
|
||||
/* seq - print sequence of numbers to standard output.
|
||||
Copyright (C) 2018 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 (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 __P((const char *));
|
||||
static char *genformat __P((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; /* interation 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
|
||||
};
|
||||
@@ -48,13 +48,6 @@
|
||||
#include "builtins.h"
|
||||
#include "common.h"
|
||||
|
||||
#define RETURN(x) \
|
||||
do { \
|
||||
if (sp) *sp = sec; \
|
||||
if (usp) *usp = usec; \
|
||||
return (x); \
|
||||
} while (0)
|
||||
|
||||
int
|
||||
sleep_builtin (list)
|
||||
WORD_LIST *list;
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
@@ -84,6 +84,7 @@ tee_builtin (list)
|
||||
case 'i':
|
||||
nointr = 1;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
@@ -31,6 +31,7 @@ template_builtin (list)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
@@ -46,6 +46,7 @@ tty_builtin (list)
|
||||
case 's':
|
||||
sflag = 1;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
@@ -95,6 +95,7 @@ uname_builtin (list)
|
||||
case 'v':
|
||||
uname_flags |= FLAG_VERSION;
|
||||
break;
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
@@ -39,6 +39,7 @@ whoami_builtin (list)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
CASE_HELPOPT;
|
||||
default:
|
||||
builtin_usage ();
|
||||
return (EX_USAGE);
|
||||
|
||||
Reference in New Issue
Block a user