diff options
author | April Chin <April.Chin@Sun.COM> | 2008-12-27 14:59:38 -0800 |
---|---|---|
committer | April Chin <April.Chin@Sun.COM> | 2008-12-27 14:59:38 -0800 |
commit | 7c2fbfb345896881c631598ee3852ce9ce33fb07 (patch) | |
tree | 4b173b5657508562dfc0aa05f7d056d1e9add505 /usr/src/lib/libshell/common | |
parent | 6071ac1de68fed78e1e10052045bbb5f1732a263 (diff) | |
download | illumos-joyent-7c2fbfb345896881c631598ee3852ce9ce33fb07.tar.gz |
PSARC/2008/094 ksh93 Update 1
PSARC/2008/344 ksh93 Integration Update 1 Amendments 1
PSARC/2008/589 Remove /usr/bin/printf from PSARC case 2008 094
6619428 *ksh93* RFE: Update ksh93 in Solaris to ast-ksh.2008-11-04
6788659 RFE: Update libpp in Solaris to ast-open.2008-07-25
6561901 RFE: Add "shcomp" (shell script compiler) + kernel module to exec binary sh code
6599668 RFE: Move consumers of alias.sh over to ksh93
6595183 *ksh93* RFE: Update ksh93-integration demo code
6775901 *ksh93* no C message catalogs are generated for ksh93
6451262 *sleep* RFE: /usr/bin/sleep should support floating-point values
6687139 *ksh93* command substitution, exec, and stdout redirection cause allocation loop
6703761 *ksh93* crashes in script containing uncommon output redirections
6715496 *ksh93* SEGVs on array reinitialization
6713682 *ksh93* Creating a compound variable in a subshell "bleeds through" to the calling subshell
6672350 *ksh93* causes parent shell to die when child shell is suspended
6745015 *ksh93* VARIABLE=`command substitution` assignment is not reliable on OpenSolaris
6710205 *ksh93* problem with command substitution (within back quotes) containing \$'
6737600 *ksh93* exits debugger when user presses ctrl-c
6748645 *ksh93* fc -l -e - is mis-parsed, outputs wrong error message "-e - requires single argument"
6754020 *ksh93* does weird '[' expansion
6753538 *ksh93* umask modification leaks out of a ksh93 subshell
6766246 *ksh93* bug in pattern matching
6763594 *ksh93* executes command after "command" builtin twice on failure
6762665 *ksh93* Difficult-to-reproduce SIGSEGV in ksh93
Diffstat (limited to 'usr/src/lib/libshell/common')
196 files changed, 25619 insertions, 6938 deletions
diff --git a/usr/src/lib/libshell/common/COMPATIBILITY b/usr/src/lib/libshell/common/COMPATIBILITY index 8f20d44374..d4d645a99a 100644 --- a/usr/src/lib/libshell/common/COMPATIBILITY +++ b/usr/src/lib/libshell/common/COMPATIBILITY @@ -125,5 +125,10 @@ omitted features that are completely upward compatible. the stty lnext character is set to control-v or is unset. The sequence escape control-v will display the shell version. +29. In ksh88, DEBUG traps were executed. after each command. In ksh93 + DEBUG traps are exeucted before each command. + +30. In ksh88, a redirection to a file name given by an empty string was + ignored. In ksh93, this is an error. I am interested in expanding this list so please let me know if you uncover any others. diff --git a/usr/src/lib/libshell/common/DESIGN b/usr/src/lib/libshell/common/DESIGN index 8e35e3fc55..c11c0aff1e 100644 --- a/usr/src/lib/libshell/common/DESIGN +++ b/usr/src/lib/libshell/common/DESIGN @@ -8,6 +8,11 @@ Directory layout: The remainder are private. The directory data contains readonly data files for ksh93. + The man pages for built-ins are in builtins.c rather + than included as statics with the implementations in the + bltins directory because some systems don't make static const + data readonly and we want these to be shared by all running + shells. The directory edit contains the code for command line editing and history. @@ -59,7 +64,7 @@ Include directory: 4. edit.h contains definitions that are common to both vi and emacs edit modes. 5. env.h contains interfaces for creating and modifying - environment variables. + environment variables. 6. fault.h contains prototypes for signal related functions and trap and fault handling. 7. fcin.h contains macro and function definitions for @@ -134,30 +139,32 @@ sh directory: 18. nvdisc.c contains code related to name-value pair disciplines. 19. nvtree.c contains code for compound variables and for walking the namespace. - 20. parse.c contains the code for the shell parser. - 21. path.c contains the code for pathname lookup and + 20. nvtype.c contains most of the code related to types that + are created with typeset -T. + 21. parse.c contains the code for the shell parser. + 22. path.c contains the code for pathname lookup and some path functions. It also contains the code that executes commands and scripts. - 22. pmain.c is just a calls sh_main() so that all of the + 23. pmain.c is just a calls sh_main() so that all of the rest of the shell can be in a shared library. - 23. shcomp.c contains the main program to the shell + 24. shcomp.c contains the main program to the shell compiler. This program parses a script and creates a file that the shell can read containing the parse tree. - 24. streval.c is an C arithmetic evaluator. - 25. string.c contains some string related functions. - 26. subshell.c contains the code to save and restore + 25. streval.c is an C arithmetic evaluator. + 26. string.c contains some string related functions. + 27. subshell.c contains the code to save and restore environments so that subshells can run without creating a new process. - 27. suid_exec.c contains the program from running execute + 28. suid_exec.c contains the program from running execute only and/or setuid/setgid scripts. - 28. tdump.c contains the code to dump a parse tree into + 29. tdump.c contains the code to dump a parse tree into a file. - 29. timers.c contains code for multiple event timeouts. - 30. trestore contians the code for restoring the parse + 30. timers.c contains code for multiple event timeouts. + 31. trestore contians the code for restoring the parse tree from the file created by tdump. - 31. userinit.c contains a dummy userinit() function. + 32. userinit.c contains a dummy userinit() function. This is now obsolete with the new version of sh_main(). - 32. waitevent.c contains the sh_waitnotify function so + 33. waitevent.c contains the sh_waitnotify function so that builtins can handle processing events when the shell is waiting for input or for process completion. - 33. xec.c is the main shell executuion loop. + 34. xec.c is the main shell executuion loop. diff --git a/usr/src/lib/libshell/common/PROMO.mm b/usr/src/lib/libshell/common/PROMO.mm index 332ff3a855..def4552243 100644 --- a/usr/src/lib/libshell/common/PROMO.mm +++ b/usr/src/lib/libshell/common/PROMO.mm @@ -110,7 +110,7 @@ it to intercept assignments and references. .LI Improved debugging: KSH-93 can generate line numbers on execution traces. Also, I/O redirections are now traced. -There is a DEBUG trap that gets evaluated after each command +There is a DEBUG trap that gets evaluated before each command so that errors can be localized. .LI Job Control: On systems that support job control, including diff --git a/usr/src/lib/libshell/common/README b/usr/src/lib/libshell/common/README index 3c3899fda2..1feeec8a90 100644 --- a/usr/src/lib/libshell/common/README +++ b/usr/src/lib/libshell/common/README @@ -27,7 +27,9 @@ and changing their value will cause all modules that could be affected by this change to be recompiled. The options have the following defaults and meanings: ACCT off Shell accounting. - ACCTFILE off Enable per user accounting info + ACCTFILE off Enable per user accounting info. + AUDIT off For auditing specific users + AUDITFILE "/etc/ksh_audit" APPEND on Allows var+=val string and array append. BASH off Bash compatibility mode. It is not fully implemented and is experimental. @@ -69,10 +71,12 @@ The options have the following defaults and meanings: SEVENBIT off Strip the eigth bit from characters. SPAWN off Use spawn as combined fork/exec. May improve speed on some systems. + STATS on Add .sh.stats compound variable. SUID_EXEC on Execute /etc/suid_exec for setuid, setgid script. TIMEOUT off Set this to the number of seconds for timing out and exiting the shell when you don't enter a command. If non-zero, TMOUT can not be set larger than this value. + TYPEDEF on Enable typeset type definitions. VSH on Compile with vi command line editing. The original vi line editor code was provided by Pat Sullivan at CB. @@ -125,7 +129,8 @@ equal. The tests sub-directory contains a number of regression tests for ksh. To run all these tests with the shell you just built, go to the tests directory and run the command - SHELL=../ksh shtests + SHELL=$dir/ksh $dir/ksh shtests +where dir is the directory of the ksh you want to test. The file PROMO.mm is an advertisement that extolls the virtues of ksh. The file sh.1 contains the troff (man) description of this Shell. diff --git a/usr/src/lib/libshell/common/RELEASE b/usr/src/lib/libshell/common/RELEASE index 034ca7033d..f368dd1531 100644 --- a/usr/src/lib/libshell/common/RELEASE +++ b/usr/src/lib/libshell/common/RELEASE @@ -1,4 +1,439 @@ -07-04-18 --- Release ksh93s+ --- +08-10-31 --- Release ksh93t --- +08-10-31 Variable scoping/initialization bugs that could dump core were fixed. +08-10-24 The lexer now accepts all RE characters for patterns prefixed + with a ksh ~(...) option expression. +08-10-24 For ${var/pat/sub} \0 in sub expands to the text matched by pat. +08-10-18 A bug in array scoping that could dump core has been fixed. +08-10-10 read -n and -N fixed to count characters in multibyte locales. +08-10-10 A bug that mishandled _.array[] type references has been fixed. +08-10-09 ${.sh.version} now contains a catenation of the following (after + 'Version') denoting compile time features: + A SHOPT_AUDIT + B SHOPT_BASH + L SHOPT_ACCT + M SHOPT_MULTIBYTE +08-10-09 A bug that caused subshell command substitution with redirection + to hang has been fixed. +08-10-08 Output errors, other than to stderr, now result in a diagnostic. +08-10-08 ksh93 now supports types that contain arrays of other types as + members. Earlier versions core dumped in this case. +08-10-05 A bug which caused the shell to emit a syntax error for an arithmetic + statement of the form (( var.name[sub] = value)) has been fixed. +08-10-01 A bug that caused subshell command substitution to hang has + been fixed. +08-09-29 When the -p export option of typeset is used with other options, + only those variables matching the specified options are displayed. +08-09-29 When the shell reads the environment and finds variables that are + not valid shell assignments, it now passes these on to subsequent + commands rather than deleting them. +08-09-29 A bug in the display of compound variables containing an indexed + array of compound variables has been fixed. +08-09-29 A bug in the display of compound variables containing an associative + array with a subscript containing a . in the name has been fixed. +08-09-26 A core dump in the subshell environment restore has been fixed. +08-09-24 $(...) has been fixed to properly set the exit status in $?. +08-09-23 $(<...) with IFS=$'\n\n' has been fixed to retain all but the last + of multiple trailing newlines. +08-09-23 The -p option to typeset when used with other attributes, restricts + the output to variables with the specified attributes. +08-09-22 A bug that sometimes lost the exit status of a job has been fixed. +08-09-21 A bug that retained trailing command substitution newlines in + cases where the command caused the shell to fork has been fixed. +08-09-19 type, whence -v, and command -v were fixed to comply with POSIX + by writing 'not found' diagnostics to the standard error. +08-09-18 test and [...] were fixed to comply with POSIX in the case + of test '(' binop ')' where binop is a valid binary test operator. +08-09-16 If a method discipline named create is specified when defining a + type, this function will be called when an instance is created. +08-09-15 The variable _ is now set as a reference to the compound variable + when defining a compound variable or a type. +08-09-10 The shell now prints an error message when the type name specified + for an indexed array subscript is not an enumeration type. +08-09-10 A bug in which a subshell that spawned a background process could + loose output that was produced after the foreground completed + has been fixed. +08-09-10 A timing bug on some systems that could cause coprocesses started by a + subshell to not clean up and prevent other coprocesses has been fixed. +08-09-09 The typeset -m option is now able to rename array elements from + the same array. +08-09-09 The exit status of 2 from the DEBUG trap causes the next command + to be skipped. An exit value of 255 from a DEBUG trap called from + a function causes the function to return. +08-09-08 A bug in which a coprocess created in a subshell that did not + complete when the subshell terminated could prevent a coprocess + from being created in the parent shell has been fixed. +08-09-05 An assignment of the form name1=name2 where name1 and name2 + are both compound variables causes name1 to get a copy of name2. + name1+=name2 causes name2 sub-variables to be appended to name1. +08-09-05 A bug in which unsetting a compound variable did not unset all + the sub-variables has been fixed. +08-09-01 A bug in the subshell cleanup code that could cause SIGSEGV has + been fixed. +06-08-26 The SHLVL variable which is an environment variable used by bash + and zsh that gets incremented when the shell starts. +08-08-25 For an indexed array, a negative subscript now refers to offsets + from the end so that -1 refers to the last element. +08-08-24 An alignment error for shorts on 64 bit architectures has been fixed. +08-08-22 If oldvar is a compound variable, typeset -C newvar=oldvar creates + newvar as a copy of oldvar. +08-08-19 The ALRM signal no longer cause the sleep builtin to terminate. +08-08-13 When used in an arithmetic expression, the .sh.version variable + now produces a number that will be increasing for each release. +08-08-11 A bug in which type instantiation with a compound assignment in + a dot script in which the type is defined has been fixed. +08-08-07 The -m option has been added to typeset to move or rename a + variable. Not documented yet. +08-08-06 A bug in read when used in a loop when a prompt was specified + when reading from a terminal has been fixed. +08-08-01 A bug with the pipefail option in which a nested pipeline could + cause an asynchronous command to block has been fixed. +08-08-01 A for loop optimizer bug that treats .sh.lineno as an invariant + has been fixed. +08-07-30 A bug in which expanding compound variable that had a get discipline + from with a here document could cause a syntax error has been fixed. +08-07-18 A bug in which a nameref caused a local variable to be created + rather than binding to an existing variable in the global scope + has been fixed. +08-07-17 A bug which occurred when a nameref was created from within a + function that was part of a pipeline has been fixed. +08-07-14 The compile option SHOPT_STATS was added. With this option the + compound variable .sh.stats keeps usage statistics that could help + with performance tuning. +08-07-10 The output of set now always uses a single line for each variable. + For array variables, the complete set of values is now displayed. +08-07-09 The typeset -C option can be used with arrays to indicate that + each element should default to a compound variable. +08-07-08 The %B format now outputs compound variables and arrays. The + alternate flag # can be used to cause output into a single line. +08-07-03 When the window change signal, WINCH, is received, the current + edit line is redrawn in place. +08-07-01 A bug in the handling of shared variables when inside an embedded + type has been fixed. +08-06-29 A bug in multiline edit mode which occurred when the prompt length + was three characters or less has been fixed. +08-06-23 A bug in which the SIGCLD was not be triggered when background + jobs completed has been fixed. +08-06-23 KSH_VERSION added as a name reference to .sh.version. +08-06-20 type now outputs 'special builtin' for special builtins. +08-06-19 A couple of bugs in multi-dimensional arrays have been fxied. +08-06-19 A bug in which a syntax error in a dot script could generated + a syntax error in the next subsequent command has been fixed. +08-06-17 Reduced the maximum function call depth to 2048 to avoid exceptions + on some architectures. +08-06-16 A bug in which printf "%B" could generate an exception when the + specified variable was not set has been fixed. +08-06-16 When typeset -p is followed by variable names, it now displays + the attributes names and values for the specific names. +08-06-14 A bug that could effect the drawing of the screen from multiline + emacs or gmacs mode when walking up the history file has been fixed. +08-06-13 A bug in which a compound variable defined in a subshell could + have side effects into the parent shell has been fixed. +08-06-13 A number of bugs related to using .sh.level to set the stack from + for DEBUG traps have been fixed. +08-06-13 The .sh.lineno variable has been added. When .sh.level is changed + inside a DEBUG trap, the .sh.lineno contains the calling line number + for the specified stack frame. +08-06-13 The .sh.level variable has been documented and now works. +08-06-11 The -C option has been added to read for reading compound command + definitions from a file. +08-06-11 The . command is now permitted inside a compound command definition. + The dot script can contain declaration commands and dot commands. +08-06-09 Add -C option to typeset so that typeset -C foo, is equivalent + to foo=(). +08-06-09 Added -n warning message for typeset option orderings that are valid + with ksh88 but not valid with ksh93, for example Lx5. +08-06-09 A bug in which the return value for an assignment command containing + a command substitution with that failed was zero when the assignment + contained redirections has been fixed. +08-06-09 A bug in the quoting of $ inside a ERE pattern ~(E)(pattern) + has been fixed. +08-06-06 A bug when processing `` command substitution with the character + sequence \$' has been fixed. +08-06-02 When defining a type, the typeset -r attribute causes this field + to be required to be specified for each instance of the type and + does not allow a default value. +08-06-02 Several bugs in which compound variables were modified by + subshells have been fixed. +08-05-22 The ceil function has been added to the math functions. +08-05-21 A bug in which a name reference defined in a function and passed + as an argument to another function could cause an incorrect binding. +08-05-21 A bug in freeing compound variables that are local to functions + has been fixed. +08-05-19 The array expansions ${array[sub1..sub2]} and ${!array[sub1..sub2]} + to expand to the value (or subscripts) for array between sub1 and + sub2 inclusive. For associative arrays, the range is based on + location in the POSIX locale. The .. must be explicit and cannot + result from an expansion. +08-05-15 The trap on SIGCLD is no longer triggered by the completion of + the foreground job as with ksh88. +08-05-14 A bug in the implementation of the editing feature added on + 07-09-19 in emacs mode has been fixed. +08-05-12 A bug in processing the test built-in with parenthesis has been + fixed. +08-05-12 The unset built-in now returns non-zero when deleting an array + subscript that is not set. +08-05-08 Changing the value of HISTFILE or HISTSIZE will cause the old + history file to be close and reopened with the new name or size. +08-05-08 When FPATH is changed functions that were found via a path search + will be searched for again. +08-05-08 A parser bug in which reserved words and labels were recognized + inside compound indexed array assignment after a new-line has + been fixed. +08-05-07 A bug in getopts when handling numerical option arguments has + been fixed. +08-05-07 The typeset -S option was added for variables outside type + definitions to provide a storage class similar to C static + inside a function defined with function name. When outside + type definitions and outside a function, the -S option cause + the specified variable so be unset before the assignment and + before the remaining attributes are supplied. +08-05-07 A bug that affected the cursor movement in multiline mode when + a character was deleted from near the beginning of the any + line other than the first. +08-05-01 In multiline edit mode, the refresh operation will now clear + the remaining portion of the last line. +08-05-01 A bug in computing prompt widths for the edit modes for prompts + with multibyte characters has been fixed. +08-05-01 A bug in the multiline edit mode which could cause the current + line to be displayed incorrectly when moving backwards from third + or higher line to the previous line has been fixed. +08-05-01 A bug in which options set in functions declared with the function + name syntax were carried across into functions invoked by these + functions has been fixed. +08-04-30 A bug which could cause a coprocess to hang when the read end + is a builtin command has been fixed. +08-04-30 The emacs and vi editors have been modified to handle window + change commands as soon as they happen rather than waiting for + the next command. +08-04-28 A bug in which ${!x} did not expand to x when x was unset has been + fixed. +08-04-27 A bug in which the assignment x=(typeset -a foo=([0]=abc)) created + x.foo as an associative array has been fixed. +08-04-25 A bug in which $# did not report correctly when there were more + than 32K positional parameters has been fixed. +08-04-04 Choose the name _ as the sub-variable that holds type or instance + specific data used by discipline functions. +08-03-27 A bug in which the terminal group was not given back to the parent + shell when the last part of a pipeline was handled by the parent shell + and the other parts of the pipeline complete has been fixed. + The symptom was that the pipeline became uninterruptable. +08-03-25 A bug in restricted mode introduced in ksh93s that caused scripts + that did not use #! to executed in restricted mode has been fixed. +08-03-25 A bug in which the pipefail option did not work for a pipeline + within a pipeline has been fixed. +08-03-24 A bug in which OPTIND was not set correctly in subshells has + been fixed. +08-03-24 A bug which could cause a memory exception when a compound variable + containing an indexed array with only element 0 defined was expanded. +08-03-20 A bug in which ${!var[sub].*} was treated as an error has been fixed. +08-03-20 Associative array assignments of the form ([name]=value ...) + now allow ; as well as space tab and new line to separate elements. +08-03-18 A buffering problem in which standard error was sometimes + not flushed before sleep has been fixed. +08-03-17 A bug in which a signal sent to $$ while in a subshell would be + sent to the subshell rather than the parent has been fixed. +08-03-17 --default option added to set(1) to handle set +o POSIX semantics. + set --state added as a long name alias for set +o. +08-03-14 A bug in which using monitor mode from within a script could + cause the terminal group to change has been fixed. +08-03-10 The new ${...} command substitution will treat the trailing } + as a reserved word even if it is not at the beginning of a command, + for example, ${ date }. +08-03-10 If the name of the ENV begins with /./ or ././ then the + /etc/ksh.kshrc file will not be executed on systems that support + this interactive initialization file. +08-03-07 A bug in which ksh -i did not run the ENV file has been fixed. +08-03-07 A bug in which ulimit did not always produce the same output as + ulimit -fS has been fixed. +08-03-04 A bug in multiline mode in emacs and vi mode which could cause the + cursor to be on the wrong line when interrupt was hit has been fixed. +08-03-03 The change made in ksh93s+ on 07-06-18 in which braces became + optional for ${a[i]} inside [[...]] was restored in the case + where the argument can be a pattern. +08-03-03 A bug in which creating a name reference to an associative array + instance would fail when the subscript contained characters [ or + ] has been fixed. +08-02-29 The redirection operator >; has been added which for non-special + files, generates the output in a temporary file and writes the + specified file only of the command has completed successfully. +08-02-15 A bug in ${var/pattern/string} for patterns of the form ?(*) and +(*) + has bee fixed. +08-02-07 A bug in which test \( ! -e \) produced an error has been fixed. +08-02-14 The typeset -a option can now optionally be followed by the name + of an enumeration type which allows subscripts to be enumerations. +08-02-14 The enum builtin which creates enumeration types has been added. +08-02-12 The backoff logic when there are no more processes has been fixed. +08-02-07 The -X option has been added to typeset. The -X option creates + a double precision number that gets displayed using the C99 %a + format. It can be used along with -l for long double. +08-01-31 The -T option to typeset has been added for creating typed + variables. Also the -h and -S options have been added to + typeset that are only applicable when defining a type. +08-01-31 The prefix expansion operator @ has been added. ${@name} + expands to the type of name or yields the attributes. +07-11-15 A bug in the macro expander for multibyte characters in which + part of the character contains a file pattern byte has been fixed. +07-10-03 A bug in which : was not allowed as part of an alias name has been + fixed. +07-09-26 A bug in which appending a compound variable to a compound variable + or to an index array didn't work has been fixed. +07-09-19 In both emacs and vi edit mode, the escape sequence \E[A (usually + cursor up, when the cursor is at the end of the line will fetch + the most recent line starting with the current line. +07-09-18 The value of ${!var} was correct when var was a reference to an + array instance. +07-09-18 The value of ${!var[sub]} was not expanding to var[sub] and this + was fixed. It also fixed ${name} where name is a name reference + to var[sub]. +07-09-18 It is now legal to create a name reference without an initialization. + It will be bound to a variable on the first assignment. +07-08-30 A discipline function can be invoked as ${x.foo} and is equivalent + to ${ x.foo;} and can be invoked as x.foo inside ((...)). +07-07-09 A bug in which typeset -a did not list indexed arrays has been + fixed. +07-07-03 The command substitution ${ command;} has been added. It behaves + like $(command) except that command is executed in the current + shell environment. The ${ must be followed by a blank or an + operator. + +08-04-17 --- Release ksh93s+ --- +08-04-17 A bug in which umask was not being restored correctly after a + subshell has been fixed. +08-04-15 A bug in which sending a STOP signal to a job control shell started + from within a shell function caused cause the invoking shell to + terminate has been fixed. +08-04-11 A bug which caused $(exec > /dev/null) to go into an infinite loop + has been fixed. +08-03-27 A bug in which typeset -LZ was being treated as -RZ has been fixed. +08-03-06 A bug with ksh -P on systems that support the the profile shell, + in which it would exit after running a non-builtin has been fixed. +08-01-31 A bug in which command substitution inside ((...)) could cause + syntax errors or lead to core dumps has been fixed. +08-01-17 A bug in which discipline functions could be deleted when invoked + from a subshell has been fixed. +08-01-17 A bug in which a command substitution consisting only of + assignments was treated as a noop has been fixed. +08-01-17 A bug in which discipline functions invoked from withing a + compound assignment could fail has been fixed. +08-01-16 Incomplete arithmetic assignments, for example ((x += )), now + generate an error message. +08-01-16 A bug in which a set discipline defined for a variable before + an array assignment could cause a core dump has been fixed. +08-01-03 A bug in on some systems in which exit status 0 is incorrectly + returned by a process that catches the SIGCONT signal is stopped + and then continued. +07-12-13 A race condition in which a program that has been stopped and then + continued could loose the exit status has been fixed. +07-12-12 Code to check for file system out of space write errors for all + writes has been added. +07-12-11 A bug in the macro expander for multibyte characters in which + part of the character contains a file pattern byte has been fixed. +07-12-06 A bug in the emacs edit mode when multiline was set that output + a backspace before the newline to the screen has been fixed. +07-12-04 A bug in which using <n>TAB after a variable name listing expansion + in the edit modes would cause the $ to disappear has been fixed. +07-11-28 A bug in which setting IFS to readonly could cause a subsequent + command substitution to fail has been fixed. +07-11-27 A work around for a gcc 4.* C99 "feature" that could cause a job + control shell to go into an infinite loop by adding the volatile + attribute to some auto vars in functions that call setjmp(). +07-11-27 A bug in which the shell could read ahead on a pipe causing the + standard input to be incorrectly positioned has been fixed. +07-11-27 A bug in which compound variable UTF-8 multibyte values were not + expanded or traced properly has been fixed. +07-11-21 A bug where an unbalanced '[' in a command argument was not treated + properly has been fixed. +07-11-15 A bug in which compatibility mode (no long option names) getopts(1) + incorrectly set the value of OPTARG for flag options has been fixed. +07-11-15 A bug in which "hash -- name" treated "--" as an invalid name operand + has been fixed. +07-11-15 typeset now handles "-t -- [-r] [--]" for s5r4 hash(1) compatibility. +07-11-15 A bug in which the umask builtin mis-handled symbolic mode operands + has been fixed. +07-11-15 Bugs in which shell arithmetic and the printf builtin mis-handled the + signs of { -NaN -Inf -0.0 } have been fixed. +07-11-15 The full { SIGRTMIN SIGRTMIN+1 ... SIGRTMAX-1 SIGRTMAX } range + of signals, determined at runtime, are now supported. +07-11-15 A bug in which creating an index array with only subscript 0 created + only a simple variable has been fixed. +07-11-14 A bug in which appending to an indexed array using the form + name+=([sub]=value) could cause the array to become an associative + array has been fixed. +07-11-14 A bug in which typeset without arguments could coredump if a + variable is declared as in indexed array and has no elements has + been fixed. +07-11-14 A bug in which creating a local SECONDS variable with typeset in + a function could corrupt memory has been fixed. +07-11-14 A bug which could cause a core dump when a script invoked by name + from a function used compound variables has been fixed. +07-11-05 A bug in which printf %d "'AB" did not diagnose unconverted characters + has been fixed. +07-11-05 printf %g "'A" support added for all floating point formats. +07-11-01 A bug in which typeset -f fun did not display the function definition + when invoked in a subshell has been fixed. +07-10-29 The sleep builtin was fixed so that all floating point constants + are valid operands. +07-10-10 A bug in which the locale was not being restored after + LANG=value command has been fixed. +07-09-20 A bug in which a nameref to a compound variable that was local + to the calling function would not expand correctly when displaying + is value has been fixed. +07-09-19 A bug which cause cause a core dump if .sh.edchar returned + 80 characters or more from a keyboard trap has been fixed. +07-09-14 A bug in which could cause a core dump when more than 8 file + descriptors were in use has been fixed. +07-09-10 A bug in which creating a name reference to an instance of + an array when the array name is itself a reference has been fixed. +07-09-10 The file completion code has been modified so that after an = in + any word, each : will be considered a path delimiter. +07-09-06 A bug in which subprocess cleanup could corrupt the malloc() heap + has been fixed. +07-08-26 A bug in which a name reference to an associative array instance + could cause the subscript to be evaluated as an arithmetic expression + has been fixed. +07-08-22 A bug in which the value of an array instance was of a compound + variable was not expanded correctly has been fixed. +07-08-14 A bug which could cause a core dump when a compound assignment was + made to a compound variable element with a typeset -a attribute + has been fixed. +07-08-08 A bug in which a trap ignored in a subshell caused it to be + ignored by the parent has been fixed. +07-08-07 A bug in which the set command would generated erroneous output + for a variable with the -RZ attribute if the variable name had been + passed to a function has been fixed. +07-08-02 A bug in which read x[1] could core dump has been fixed. +07-08-02 A second bug in which after read x[sub] into an associative array + of an element that hasn't been assigned could lead to a core dump + has been fixed. +07-07-31 A bug in which a pipeline that completed correctly could have + an exit status of 127 when pipefail was enabled has been fixed. +07-07-09 The SHOPT_AUDIT compile option has been added for keyboard logging. +07-06-25 In vi insert mode, ksh no longer emits a backspace character + before the carriage return when the newline is entered. +07-06-25 A bug in which pipefail would cause a command to return 0 + when the pipeline was the last command and the failure happened + on a component other than the last has been fixed. +07-06-25 A bug in the expansion of ${var/pattern/rep} when pattern or rep + contained a left parenthesis in single quotes has been fixed. +07-06-18 The braces for a subscripted variable with ${var[sub]} are now + optional when inside [[...]], ((...)) or as a subscript. +07-05-28 A bug in brace expansion in which single and double quotes did + not treat the comma as a literal character has been fixed. +07-05-24 The -p option of whence now disables -v. +07-05-23 Several bug fixes in compound variables and arrays of arrays + have been made. +07-05-15 A bug in which the %B format of printf was affected by the + locale has been fixed. +07-05-14 A bug in which \ was not removed in the replacement pattern with + ${var/pattern/rep} when it was not followed by \ or a digit has + been fixed. +07-05-10 A bug in which ksh -R file core dumped if no script was specified + has been fixed. it not displays an error message. +07-05-07 Added additional Solaris signals to signal table. +07-04-30 A bug in which a pipeline with command substitution inside a + function could cause a pipeline that invokes this function to + hang when the pipefail option is on has been fixed. +07-04-30 Added -q to whence. 07-04-18 A small memory leak with each redirection of a non-builtin has been fixed. 07-03-08 A bug in which set +o output command line options has been fixed. @@ -619,7 +1054,7 @@ 02-06-17 --- Release ksh93n- --- 02-06-17 A bug in which user defined macros could cause a core dump in - with MULTIBYE mode has been fixed. + with MULTIBYTE mode has been fixed. 02-06-17 A bug in which printf format specifiers of the form %2$s were causing a core dump has been fixed. 02-06-17 A bug in which setting stty to noecho mode did not prevent the @@ -1326,7 +1761,7 @@ positioned for the last command of a script has been fixed. 95-04-31 A bug in the edit modes which allowed walking back in the history file for more than HISTSIZE commands has - beed fixed. + been fixed. 95-04-31 A bug which could cause a core dump if variable TMPDIR was changed between two command substitutions has been fixed. 95-04-31. A bug which prevented a trap on EXIT from being cleared diff --git a/usr/src/lib/libshell/common/TYPES b/usr/src/lib/libshell/common/TYPES new file mode 100644 index 0000000000..6eb6f41b5e --- /dev/null +++ b/usr/src/lib/libshell/common/TYPES @@ -0,0 +1,182 @@ + +The ability for users to define types has been added to ksh93t. +Here is a quick summary of how types are defined and used in ksh93t. +This is still a work in progress so some changes and additions +are likely. + +A type can be defined either by a shared library or by using the new +typeset -T option to the shell. The method for defining types via +a shared library is not described here. However, the source file +bltins/enum.c is an example of a builtin that creates enumeration types. + +By convention, typenames begin with a capitol letter and end in _t. +To define a type, use + typeset -T Type_t=( + definition + ) +where definition contains assignment commands, declaration commands, +and function definitions. A declaration command (for example typeset, +readonly, and export), is a built-in that differs from other builtins in +that tilde substitution is performed on arguments after an =, assignments +do not have to precede the command name, and field splitting and pathname +expansion is not performed on the arguments. +For example, + typeset -T Pt_t=( + float -h 'length in inches' x=1 + float -h 'width in inches' y=0 + integer -S count=0 + len() + { + print -r $((sqrt(_.x*_.x + _.y*_.y))) + } + set() + { + (( _.count++)) + } + ) + +defines a type Pt_t that has three variables x, y, and count defined as well +as the discipline functions len and set. The variable x has an initial value +of 1 and the variable y has an initial value of 0. The new -h option argument, +is used for documentations purposes as described later and is ignored outside +of a type definition. + + +The variable count has the new -S attribute which means that it is shared +between all instances of the type. The -S option to typeset is ignored +outside of a type definition. Note the variable named _ that is used inside +the function definition for len and set. It will be a reference to the +instance of Pt_t that invoked the function. The functions len and set +could also have been defined with function len and function set, but +since there are no local variables, the len() and set() form are more +efficient since they don't need to set up a context for local variables +and for saving and restoring traps. + +If the discipline function named create is defined it will be +invoked when creating each instance for that type. A function named +create cannot be defined by any instance. + +When a type is defined, a declaration built-in command by this name +is added to ksh. As with other shell builtins, you can get the man page +for this newly added command by invoking Pt_t --man. The information from +the -h options will be embedded in this man page. Any functions that +use getopts to process arguments will be cross referenced on the generated +man page. + +Since Pt_t is now a declaration command it can be used in the definition +of other types, for example + typeset -T Rect_t=( Pt_t ur ll) + +Because a type definition is a command, it can be loaded on first reference +by putting the definition into a file that is found on FPATH. +Thus, if this definition is in a file named Pt_t on FPATH, then +a program can create instances of Pt_t without first including +the definition. + +A type definition is readonly and cannot be unset. Unsetting non-shared +elements of a type restores them to their default value. Unsetting a +shared element has no effect. + +The Pt_t command is used to create an instance of Pt_t. + Pt_t p1 +creates an instance named p1 with the initial value for p1.x set to 1 +and the initial value of p1.y set to 0. + Pt_t p2=(x=3 y=4) +creates an instance with the specified initial values. The len function +gives the distance of the point to the origin. Thus, p1.len will output +1 and p2.len will output 5. + +ksh93t also introduces a more efficient command substitution mechanism. +Instead of $(command), the new command substitution ${ command;} +can be used. Unlike (and ) which are always special, the { and } are +reserved words and require the space after { and a newline or ; before }. +Unlike $(), the ${ ;} command substitution executes the command in +the current shell context saving the need to save and restore +changes, therefore also allowing side effects. + +When trying to expand an element of a type, if the element does not exist, +ksh will look for a discipline function with that name and treat this as if +it were the ${ ;} command substitution. Thus, ${p1.len} is equivalent to +${ p1.len;} and within an arithmetic expression, p1.len will be expanded +via the new command substitution method. + +The type of any variable can be obtained from the new prefix +operator @. Thus, ${@p1} will output Pt_t. + +By default, each instance inherits all the discipline functions defined +by the type definition other than create. However, each instance can define +a function by the same name that will override this definition. +However, only discipline functions with the same name as those defined +by the type or the standard get, set, append, and unset disciplines +can be defined by each instance. + +Each instance of the type Pt_t behaves like a compound variable except +that only the variables defined by the type can be referenced or set. +Thus, p2.x=9 is valid, but p2.z=9 is not. Unless a set discipline function +does otherwise, the value of $p1 will be expanded to the form of a compound +variable that can be used for reinput into ksh. + +If the variables var1 and var2 are of the same type, then the assignment + var2=var1 +will create a copy of the variable var1 into var2. This is equivalent to + eval var2="$var1" +but is faster since the variable does not need to get expanded or reparsed. + +The type Pt_t can be referenced as if it were a variable using the name +.sh.type.Pt_t. To change the default point location for subsequent +instances of Pt_t, you can do + .sh.type.Pt_t=(x=5 y=12) +so that + Pt_t p3 + p3.len +would be 13. + +Types can be defined for simple variables as well as for compound +objects such as Pt_t. In this case, the variable named . inside +the definition refers to the real value for the variable. For example, +the type definition + typeset -T Time_t=( + integer .=0 + _='%H:%M:%S' + get() + { + .sh.value=$(printf "%(${_._})T" "#$((_))" ) + } + set() + { + .sh.value=$(printf "%(%#)T" "${.sh.value}") + + } + ) + +The sub-variable name _ is reserved for data used by discipline functions +and will not be included with data written with the %B option to printf. +In this case it is used to specify a date format. + +In this case + Time_t t1 t2=now +will define t1 as the time at the beginning of the epoch and t2 +as the current time. Unlike the previous case, $t2 will output +the current time in the date format specified by the value t2._. +However, the value of ${t2.} will expand the instance to a form +that can be used as input to the shell. + +Finally, types can be derived from an existing type. If the first +element in a type definition is named _, then the new type +consists of all the elements and discipline functions from the +type of _ extended by elements and discipline functions defined +by new type definition. For example, + + typeset -T Pq_t=( + Pt_t _ + float z=0. + len() + { + print -r $((sqrt(_.x*_.x + _.y*_.y + _.z*_.z))) + } + ) + +defines a new type Pq_t which is based on Pq_t and contains an additional +field z and a different len discipline function. It is also possible +to create a new type Pt_t based on the original Pt_t. In this case +the original Pt_t is no longer accessible. diff --git a/usr/src/lib/libshell/common/bltins/alarm.c b/usr/src/lib/libshell/common/bltins/alarm.c index 6f4aa2f644..ff3369d293 100644 --- a/usr/src/lib/libshell/common/bltins/alarm.c +++ b/usr/src/lib/libshell/common/bltins/alarm.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -235,7 +235,7 @@ int b_alarm(int argc,char *argv[],void *extra) register int n,rflag=0; register Namval_t *np; register struct tevent *tp; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; while (n = optget(argv, sh_optalarm)) switch (n) { case 'r': @@ -262,7 +262,7 @@ int b_alarm(int argc,char *argv[],void *extra) np = nv_open(argv[0],shp->var_tree,NV_NOARRAY|NV_VARNAME|NV_NOASSIGN); if(!nv_isnull(np)) nv_unset(np); - nv_setattr(np, NV_INTEGER|NV_DOUBLE); + nv_setattr(np, NV_DOUBLE); if(!(tp = newof(NIL(struct tevent*),struct tevent,1,0))) errormsg(SH_DICT,ERROR_exit(1),e_nospace); tp->fun.disc = &alarmdisc; diff --git a/usr/src/lib/libshell/common/bltins/cd_pwd.c b/usr/src/lib/libshell/common/bltins/cd_pwd.c index f57f7c977e..ac9d3f426f 100644 --- a/usr/src/lib/libshell/common/bltins/cd_pwd.c +++ b/usr/src/lib/libshell/common/bltins/cd_pwd.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -39,7 +39,6 @@ #include <ls.h> #include <ctype.h> -#ifdef PATH_BFPATH /* * Invalidate path name bindings to relative paths */ @@ -50,18 +49,13 @@ static void rehash(register Namval_t *np,void *data) if(pp && *pp->name!='/') nv_unset(np); } -#endif int b_cd(int argc, char *argv[],void *extra) { -#ifdef PATH_BFPATH register char *dir; Pathcomp_t *cdpath = 0; -#else - register char *dir, *cdpath=""; -#endif register const char *dp; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; int saverrno=0; int rval,flag=0; char *oldpwd; @@ -105,7 +99,6 @@ int b_cd(int argc, char *argv[],void *extra) if(*dir != '/') #endif /* _WINIX */ { -#ifdef PATH_BFPATH if(!(cdpath = (Pathcomp_t*)shp->cdpathlist) && (dp=(CDPNOD)->nvalue.cp)) { if(cdpath=path_addpath((Pathcomp_t*)0,dp,PATH_CDPATH)) @@ -114,38 +107,22 @@ int b_cd(int argc, char *argv[],void *extra) cdpath->shp = shp; } } -#else - cdpath = nv_getval(nv_scoped(CDPNOD)); -#endif if(!oldpwd) oldpwd = path_pwd(1); } -#ifndef PATH_BFPATH - if(!cdpath) - cdpath = ""; -#endif if(*dir=='.') { /* test for pathname . ./ .. or ../ */ if(*(dp=dir+1) == '.') dp++; if(*dp==0 || *dp=='/') -#ifdef PATH_BFPATH cdpath = 0; -#else - cdpath = ""; -#endif } rval = -1; do { -#ifdef PATH_BFPATH dp = cdpath?cdpath->name:""; cdpath = path_nextcomp(cdpath,dir,0); -#else - dp = cdpath; - cdpath=path_join(cdpath,dir); -#endif #if _WINIX if(*stakptr(PATH_OFFSET+1)==':' && isalpha(*stakptr(PATH_OFFSET))) { @@ -210,11 +187,7 @@ success: stakseek(dir-stakptr(0)); } dir = (char*)stakfreeze(1)+PATH_OFFSET; -#ifdef PATH_BFPATH if(*dp && (*dp!='.'||dp[1]) && strchr(dir,'/')) -#else - if(*dp && *dp!= ':' && strchr(dir,'/')) -#endif sfputr(sfstdout,dir,'\n'); if(*dir != '/') return(0); @@ -228,11 +201,9 @@ success: nv_putval(pwdnod,dir,NV_RDONLY); nv_onattr(pwdnod,NV_NOFREE|NV_EXPORT); shp->pwd = pwdnod->nvalue.cp; -#ifdef PATH_BFPATH nv_scan(shp->track_tree,rehash,(void*)0,NV_TAGGED,NV_TAGGED); path_newdir(shp->pathlist); path_newdir(shp->cdpathlist); -#endif return(0); } @@ -240,7 +211,7 @@ int b_pwd(int argc, char *argv[],void *extra) { register int n, flag = 0; register char *cp; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; NOT_USED(argc); while((n = optget(argv,sh_optpwd))) switch(n) { diff --git a/usr/src/lib/libshell/common/bltins/cflow.c b/usr/src/lib/libshell/common/bltins/cflow.c index 3f32f4386d..6afe9d584f 100644 --- a/usr/src/lib/libshell/common/bltins/cflow.c +++ b/usr/src/lib/libshell/common/bltins/cflow.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -47,7 +47,7 @@ int b_return(register int n, register char *argv[],void *extra) { register char *arg; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; struct checkpt *pp = (struct checkpt*)shp->jmplist; const char *options = (**argv=='r'?sh_optreturn:sh_optexit); while((n = optget(argv,options))) switch(n) @@ -85,7 +85,7 @@ int b_break(register int n, register char *argv[],void *extra) { char *arg; register int cont= **argv=='c'; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; while((n = optget(argv,cont?sh_optcont:sh_optbreak))) switch(n) { case ':': diff --git a/usr/src/lib/libshell/common/bltins/enum.c b/usr/src/lib/libshell/common/bltins/enum.c new file mode 100644 index 0000000000..c4d8bf03e9 --- /dev/null +++ b/usr/src/lib/libshell/common/bltins/enum.c @@ -0,0 +1,283 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2008 AT&T Intellectual Property * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +#include <shell.h> + +static const char enum_usage[] = +"[-?@(#)$Id: enum (AT&T Research) 2008-01-08 $\n]" +USAGE_LICENSE +"[+NAME?enum - create an enumeration type]" +"[+DESCRIPTION?\benum\b is a declaration command that creates an enumeration " + "type \atypename\a that can only store any one of the values in the indexed " + "array variable \atypename\a.]" +"[+?If the list of \avalue\as is ommitted, then \atypename\a must name an " + "indexed array variable with at least two elements.]" +"[i:ignorecase?The values are case insensitive.]" +"\n" +"\n\atypename\a[\b=(\b \avalue\a ... \b)\b]\n" +"\n" +"[+EXIT STATUS]" + "{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" + "}" +"[+SEE ALSO?\bksh\b(1), \btypeset\b(1).]" +; + +static const char enum_type[] = +"[-1c?\n@(#)$Id: type (AT&T Labs Research) 2008-01-08 $\n]" +USAGE_LICENSE +"[+NAME?\f?\f - create an instance of type \b\f?\f\b]" +"[+DESCRIPTION?\b\f?\f\b creates a variable for each \aname\a with " + "enumeration type \b\f?\f\b where \b\f?\f\b is a type that has been " + "created with the \benum\b(1) command.]" +"[+?The variable can have one of the following values\fvalues\f. " + "The the values are \fcase\fcase sensitive.]" +"[+?If \b=\b\avalue\a is omitted, the default is \fdefault\f.]" +"[+?If no \aname\as are specified then the names and values of all " + "variables of this type are written to standard output.]" +"[+?\b\f?\f\b is built-in to the shell as a declaration command so that " + "field splitting and pathname expansion are not performed on " + "the arguments. Tilde expansion occurs on \avalue\a.]" +"[r?Enables readonly. Once enabled, the value cannot be changed or unset.]" +"[a?index array. Each \aname\a will converted to an index " + "array of type \b\f?\f\b. If a variable already exists, the current " + "value will become index \b0\b.]" +"[A?Associative array. Each \aname\a will converted to an associate " + "array of type \b\f?\f\b. If a variable already exists, the current " + "value will become subscript \b0\b.]" +"[h]:[string?Used within a type definition to provide a help string " + "for variable \aname\a. Otherwise, it is ignored.]" +"[S?Used with a type definition to indicate that the variable is shared by " + "each instance of the type. When used inside a function defined " + "with the \bfunction\b reserved word, the specified variables " + "will have function static scope. Otherwise, the variable is " + "unset prior to processing the assignment list.]" +#if 0 +"[p?Causes the output to be in a form of \b\f?\f\b commands that can be " + "used as input to the shell to recreate the current type of " + "these variables.]" +#endif +"\n" +"\n[name[=value]...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" +"}" + +"[+SEE ALSO?\benum\b(1), \btypeset\b(1)]" +; + +struct Enum +{ + Namfun_t hdr; + short nelem; + short iflag; + const char *values[1]; +}; + +static int enuminfo(Opt_t* op, Sfio_t *out, const char *str, Optdisc_t *fp) +{ + Namval_t *np; + struct Enum *ep; + int n=0; + const char *v; + np = *(Namval_t**)(fp+1); + ep = (struct Enum*)np->nvfun; + if(strcmp(str,"default")==0) +#if 0 + sfprintf(out,"\b%s\b%c",ep->values[0],0); +#else + sfprintf(out,"\b%s\b",ep->values[0]); +#endif + else if(strcmp(str,"case")==0) + { + if(ep->iflag) + sfprintf(out,"not "); + } + else while(v=ep->values[n++]) + { + sfprintf(out,", \b%s\b",v); + } + return(0); +} + +static Namfun_t *clone_enum(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) +{ + struct Enum *ep, *pp=(struct Enum*)fp; + ep = newof(0,struct Enum,1,pp->nelem*sizeof(char*)); + memcpy((void*)ep,(void*)pp,sizeof(struct Enum)+pp->nelem*sizeof(char*)); + return(&ep->hdr); +} + +static void put_enum(Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + struct Enum *ep = (struct Enum*)fp; + register const char *v; + unsigned short i=0, n; + if(!val) + { + nv_disc(np,&ep->hdr,NV_POP); + if(!ep->hdr.nofree) + free((void*)ep); + nv_putv(np, val, flags,fp); + return; + } + if(flags&NV_INTEGER) + { + nv_putv(np,val,flags,fp); + return; + } + while(v=ep->values[i]) + { + if(ep->iflag) + n = strcasecmp(v,val); + else + n = strcmp(v,val); + if(n==0) + { + nv_putv(np, (char*)&i, NV_UINT16, fp); + return; + } + i++; + } + error(ERROR_exit(1), "%s: invalid value %s",nv_name(np),val); +} + +static char* get_enum(register Namval_t* np, Namfun_t *fp) +{ + static char buff[6]; + struct Enum *ep = (struct Enum*)fp; + long n = nv_getn(np,fp); + if(n < ep->nelem) + return((char*)ep->values[n]); + sfsprintf(buff,sizeof(buff),"%u%c",n,0); + return(buff); +} + +static Sfdouble_t get_nenum(register Namval_t* np, Namfun_t *fp) +{ + return(nv_getn(np,fp)); +} + +const Namdisc_t ENUM_disc = { 0, put_enum, get_enum, get_nenum, 0,0,clone_enum }; + +#ifdef STANDALONE +static int enum_create(int argc, char** argv, void* context) +#else +int b_enum(int argc, char** argv, void* context) +#endif +{ + int sz,i,n,iflag = 0; + Namval_t *np, *tp; + Namarr_t *ap; + char *cp,*sp; + struct Enum *ep; + Shell_t *shp = ((Shbltin_t*)context)->shp; + struct { + Optdisc_t opt; + Namval_t *np; + } optdisc; + + cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY); + for (;;) + { + switch (optget(argv, enum_usage)) + { + case 'i': + iflag = 'i'; + continue; + case '?': + error(ERROR_USAGE|4, "%s", opt_info.arg); + break; + case ':': + error(2, "%s", opt_info.arg); + break; + } + break; + } + argv += opt_info.index; + if (error_info.errors || !*argv || *(argv + 1)) + { + error(ERROR_USAGE|2, "%s", optusage(NiL)); + return 1; + } + while(cp = *argv++) + { + if(!(np = nv_open(cp, (void*)0, NV_VARNAME|NV_NOADD)) || !(ap=nv_arrayptr(np)) || ap->fun || (sz=ap->nelem&(((1L<<ARRAY_BITS)-1))) < 2) + error(ERROR_exit(1), "%s must name an array containing at least two elements",cp); + n = staktell(); + sfprintf(stkstd,"%s.%s%c",NV_CLASS,np->nvname,0); + tp = nv_open(stakptr(n), shp->var_tree, NV_VARNAME); + stakseek(n); + n = sz; + i = 0; + nv_onattr(tp, NV_UINT16); + nv_putval(tp, (char*)&i, NV_INTEGER); + nv_putsub(np, (char*)0, ARRAY_SCAN); + do + { + sz += strlen(nv_getval(np)); + } + while(nv_nextsub(np)); + sz += n*sizeof(char*); + if(!(ep = newof(0,struct Enum,1,sz))) + error(ERROR_system(1), "out of space"); + ep->iflag = iflag; + ep->nelem = n; + cp = (char*)&ep->values[n+1]; + nv_putsub(np, (char*)0, ARRAY_SCAN); + ep->values[n] = 0; + i = 0; + do + { + ep->values[i++] = cp; + sp = nv_getval(np); + n = strlen(sp); + memcpy(cp,sp,n+1); + cp += n+1; + } + while(nv_nextsub(np)); + ep->hdr.dsize = sizeof(struct Enum)+sz; + ep->hdr.disc = &ENUM_disc; + ep->hdr.type = tp; + nv_onattr(tp, NV_RDONLY); + nv_disc(tp, &ep->hdr,NV_FIRST); + memset(&optdisc,0,sizeof(optdisc)); + optdisc.opt.infof = enuminfo; + optdisc.np = tp; + nv_addtype(tp, enum_type, &optdisc.opt, sizeof(optdisc)); + } + return error_info.errors != 0; +} + +#ifdef STANDALONE +void lib_init(int flag, void* context) +{ + Shell_t *shp = ((Shbltin_t*)context)->shp; + Namval_t *mp,*bp; + if(flag) + return; + bp = sh_addbuiltin("Enum", enum_create, (void*)0); + mp = nv_search("typeset",shp->bltin_tree,0); + nv_onattr(bp,nv_isattr(mp,NV_PUBLIC)); +} +#endif diff --git a/usr/src/lib/libshell/common/bltins/getopts.c b/usr/src/lib/libshell/common/bltins/getopts.c index 65057da8d2..be48cc8886 100644 --- a/usr/src/lib/libshell/common/bltins/getopts.c +++ b/usr/src/lib/libshell/common/bltins/getopts.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -35,16 +35,17 @@ static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp) { + Shell_t *shp = *(Shell_t**)(dp+1); + Stk_t *stkp = shp->stk; if(nv_search(s,sh.fun_tree,0)) { - int savtop = staktell(); - char *savptr = stakfreeze(0); - stakputc('$'); - stakputc('('); - stakputs(s); - stakputc(')'); - sfputr(sp,sh_mactry(stakfreeze(1)),-1); - stakset(savptr,savtop); + int savtop = stktell(stkp); + char *savptr = stkfreeze(stkp,0); + sfputc(stkp,'$'); + sfputc(stkp,'('); + sfputr(stkp,s,')'); + sfputr(sp,sh_mactry(shp,stkfreeze(stkp,1)),-1); + stkset(stkp,savptr,savtop); } return(1); } @@ -54,14 +55,18 @@ int b_getopts(int argc,char *argv[],void *extra) register char *options=error_info.context->id; register Namval_t *np; register int flag, mode, r=0; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; char value[2], key[2]; - int jmpval; + int jmpval,extended; struct checkpt buff, *pp; - Optdisc_t disc; + struct { + Optdisc_t hdr; + Shell_t *sh; + } disc; memset(&disc, 0, sizeof(disc)); - disc.version = OPT_VERSION; - disc.infof = infof; + disc.hdr.version = OPT_VERSION; + disc.hdr.infof = infof; + disc.sh = shp; value[1] = 0; key[1] = 0; while((flag = optget(argv,sh_optgetopts))) switch(flag) @@ -98,6 +103,7 @@ int b_getopts(int argc,char *argv[],void *extra) opt_info.offset = shp->st.optchar; if(mode= (*options==':')) options++; + extended = *options=='\n' && *(options+1)=='[' || *options=='[' && *(options+1)=='-'; sh_pushcontext(&buff,1); jmpval = sigsetjmp(buff.buff,0); if(jmpval) @@ -107,7 +113,7 @@ int b_getopts(int argc,char *argv[],void *extra) pp->mode = SH_JMPERREXIT; sh_exit(2); } - opt_info.disc = &disc; + opt_info.disc = &disc.hdr; switch(opt_info.index>=0 && opt_info.index<=argc?(opt_info.num= LONG_MIN,flag=optget(argv,options)):0) { case '?': @@ -165,18 +171,20 @@ int b_getopts(int argc,char *argv[],void *extra) np = nv_open(nv_name(OPTARGNOD),shp->var_tree,NV_NOSCOPE); if(opt_info.num == LONG_MIN) nv_putval(np, opt_info.arg, NV_RDONLY); - else if (opt_info.num > 0 && opt_info.arg && opt_info.arg[0] == (char)opt_info.num) + else if (opt_info.arg && opt_info.num > 0 && isalpha((char)opt_info.num) && !isdigit(opt_info.arg[0]) && opt_info.arg[0] != '-' && opt_info.arg[0] != '+') { key[0] = (char)opt_info.num; key[1] = 0; nv_putval(np, key, NV_RDONLY); } - else + else if(extended) { Sfdouble_t d; d = opt_info.number; nv_putval(np, (char*)&d, NV_LDOUBLE|NV_RDONLY); } + else + nv_putval(np, opt_info.arg, NV_RDONLY); nv_close(np); sh_popcontext(&buff); opt_info.disc = 0; diff --git a/usr/src/lib/libshell/common/bltins/hist.c b/usr/src/lib/libshell/common/bltins/hist.c index fcb81958e4..b248515a06 100644 --- a/usr/src/lib/libshell/common/bltins/hist.c +++ b/usr/src/lib/libshell/common/bltins/hist.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -45,7 +45,7 @@ int b_hist(int argc,char *argv[], void *extra) register History_t *hp; register char *arg; register int flag,fdo; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; Sfio_t *outfile; char *fname; int range[2], incr, index2, indx= -1; @@ -57,7 +57,7 @@ int b_hist(int argc,char *argv[], void *extra) #endif Histloc_t location; NOT_USED(argc); - if(!sh_histinit()) + if(!sh_histinit((void*)shp)) errormsg(SH_DICT,ERROR_system(1),e_histopen); hp = shp->hist_ptr; while((flag = optget(argv,sh_opthist))) switch(flag) @@ -169,7 +169,7 @@ int b_hist(int argc,char *argv[], void *extra) range[0] = index2; if(flag==0) /* set default termination range */ - range[1] = (lflag?hist_max(hp)-1:range[0]); + range[1] = ((lflag && !edit)?hist_max(hp)-1:range[0]); if(range[1]>=(flag=(hist_max(hp) - !lflag))) range[1] = flag; /* check for valid ranges */ @@ -215,7 +215,7 @@ int b_hist(int argc,char *argv[], void *extra) sfclose(outfile); hist_eof(hp); arg = edit; - if(!arg && !(arg=nv_getval(nv_scoped(HISTEDIT))) && !(arg=nv_getval(nv_scoped(FCEDNOD)))) + if(!arg && !(arg=nv_getval(sh_scoped(shp,HISTEDIT))) && !(arg=nv_getval(sh_scoped(shp,FCEDNOD)))) arg = (char*)e_defedit; #ifdef apollo /* diff --git a/usr/src/lib/libshell/common/bltins/misc.c b/usr/src/lib/libshell/common/bltins/misc.c index 974c3de037..22a1410fc3 100644 --- a/usr/src/lib/libshell/common/bltins/misc.c +++ b/usr/src/lib/libshell/common/bltins/misc.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -63,7 +63,7 @@ int b_exec(int argc,char *argv[], void *extra) register int n; logdata.clear = 0; logdata.arg0 = 0; - logdata.sh = (Shell_t*)extra; + logdata.sh = ((Shbltin_t*)extra)->shp; logdata.sh->st.ioset = 0; while (n = optget(argv, sh_optexec)) switch (n) { @@ -102,7 +102,7 @@ int B_login(int argc,char *argv[],void *extra) register Shell_t *shp; const char *pname; if(argc) - shp = (Shell_t*)extra; + shp = ((Shbltin_t*)extra)->shp; else { logp = (struct login*)extra; @@ -143,15 +143,15 @@ int B_login(int argc,char *argv[],void *extra) if(logp && logp->arg0) argv[0] = logp->arg0; #ifdef JOBS - if(job_close() < 0) + if(job_close(shp) < 0) return(1); #endif /* JOBS */ /* force bad exec to terminate shell */ pp->mode = SH_JMPEXIT; sh_sigreset(2); - sh_freeup(); + sh_freeup(shp); path_exec(pname,argv,NIL(struct argnod*)); - sh_done(0); + sh_done(shp,0); } return(1); } @@ -182,7 +182,7 @@ int b_let(int argc,char *argv[],void *extra) int b_eval(int argc,char *argv[], void *extra) { register int r; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; NOT_USED(argc); while (r = optget(argv,sh_opteval)) switch (r) { @@ -209,14 +209,14 @@ int b_dot_cmd(register int n,char *argv[],void* extra) register char *script; register Namval_t *np; register int jmpval; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; struct sh_scoped savst, *prevscope = shp->st.self; char *filename=0; int fd; struct dolnod *argsave=0, *saveargfor; struct checkpt buff; Sfio_t *iop=0; - NOT_USED(extra); + short level; while (n = optget(argv,sh_optdot)) switch (n) { case ':': @@ -230,9 +230,8 @@ int b_dot_cmd(register int n,char *argv[],void* extra) script = *argv; if(error_info.errors || !script) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); - if(shp->dot_depth++ > DOTMAX) + if(shp->dot_depth+1 > DOTMAX) errormsg(SH_DICT,ERROR_exit(1),e_toodeep,script); - shp->st.lineno = error_info.line; if(!(np=shp->posix_fun)) { /* check for KornShell style function first */ @@ -241,11 +240,7 @@ int b_dot_cmd(register int n,char *argv[],void* extra) { if(!np->nvalue.ip) { -#ifdef PATH_BFPATH - path_search(script,NIL(Pathcomp_t*),0); -#else - path_search(script,NIL(char*),0); -#endif + path_search(script,NIL(Pathcomp_t**),0); if(np->nvalue.ip) { if(nv_isattr(np,NV_FPOSIX)) @@ -261,12 +256,19 @@ int b_dot_cmd(register int n,char *argv[],void* extra) { if((fd=path_open(script,path_get(script))) < 0) errormsg(SH_DICT,ERROR_system(1),e_open,script); - filename = path_fullname(stakptr(PATH_OFFSET)); + filename = path_fullname(stkptr(shp->stk,PATH_OFFSET)); } } *prevscope = shp->st; + shp->st.lineno = np?((struct functnod*)nv_funtree(np))->functline:1; + shp->st.var_local = shp->st.save_tree = shp->var_tree; if(filename) + { shp->st.filename = filename; + shp->st.lineno = 1; + } + level = shp->fn_depth+shp->dot_depth+1; + nv_putval(SH_LEVELNOD,(char*)&level,NV_INT16); shp->st.prevst = prevscope; shp->st.self = &savst; shp->topscope = (Shscope_t*)shp->st.self; @@ -277,11 +279,12 @@ int b_dot_cmd(register int n,char *argv[],void* extra) nv_putval(SH_PATHNAMENOD, shp->st.filename ,NV_NOFREE); shp->posix_fun = 0; if(np || argv[1]) - argsave = sh_argnew(argv,&saveargfor); + argsave = sh_argnew(shp,argv,&saveargfor); sh_pushcontext(&buff,SH_JMPDOT); jmpval = sigsetjmp(buff.buff,0); if(jmpval == 0) { + shp->dot_depth++; if(np) sh_exec((Shnode_t*)(nv_funtree(np)),sh_isstate(SH_ERREXIT)); else @@ -296,7 +299,7 @@ int b_dot_cmd(register int n,char *argv[],void* extra) free((void*)shp->st.filename); shp->dot_depth--; if((np || argv[1]) && jmpval!=SH_JMPSCRIPT) - sh_argreset(argsave,saveargfor); + sh_argreset(shp,argsave,saveargfor); else { prevscope->dolc = shp->st.dolc; @@ -340,7 +343,7 @@ int b_false(int argc,register char *argv[], void *extra) int b_shift(register int n, register char *argv[], void *extra) { register char *arg; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; while((n = optget(argv,sh_optshift))) switch(n) { case ':': @@ -366,7 +369,7 @@ int b_shift(register int n, register char *argv[], void *extra) int b_wait(int n,register char *argv[],void *extra) { - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; while((n = optget(argv,sh_optwait))) switch(n) { case ':': @@ -392,7 +395,7 @@ int b_wait(int n,register char *argv[],void *extra) int b_bg(register int n,register char *argv[],void *extra) { register int flag = **argv; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; register const char *optstr = sh_optbg; if(*argv[0]=='f') optstr = sh_optfg; @@ -426,7 +429,7 @@ int b_bg(register int n,register char *argv[],void *extra) int b_jobs(register int n,char *argv[],void *extra) { register int flag = 0; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; while((n = optget(argv,sh_optjobs))) switch(n) { case 'l': @@ -506,7 +509,7 @@ int b_universe(int argc, char *argv[],void *extra) register int flag, n; register const char *optstr; register char *vend; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; if(argv[0][1]=='p') { optstr = sh_optvpath; @@ -539,7 +542,7 @@ int b_universe(int argc, char *argv[],void *extra) flag |= FS3D_GET; if((n = mount(*argv,(char*)0,flag,0)) >= 0) { - vend = stakalloc(++n); + vend = stkalloc(shp->stk,++n); n = mount(*argv,vend,flag|FS3D_SIZE(n),0); } if(n < 0) diff --git a/usr/src/lib/libshell/common/bltins/mkservice.c b/usr/src/lib/libshell/common/bltins/mkservice.c index bba532b79d..be16f45162 100644 --- a/usr/src/lib/libshell/common/bltins/mkservice.c +++ b/usr/src/lib/libshell/common/bltins/mkservice.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/bltins/print.c b/usr/src/lib/libshell/common/bltins/print.c index 5363c6f3ab..513d46fb0a 100644 --- a/usr/src/lib/libshell/common/bltins/print.c +++ b/usr/src/lib/libshell/common/bltins/print.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -60,6 +60,7 @@ struct printf int argsize; int intvar; char **nextarg; + char *lastarg; char cescape; char err; Shell_t *sh; @@ -69,6 +70,7 @@ static int extend(Sfio_t*,void*, Sffmt_t*); static const char preformat[] = ""; static char *genformat(char*); static int fmtvecho(const char*, struct printf*); +static ssize_t fmtbase64(Sfio_t*, char*, int); struct print { @@ -80,24 +82,6 @@ struct print static char* nullarg[] = { 0, 0 }; -/* - * Need to handle write failures to avoid locking output pool - */ -static int outexceptf(Sfio_t* iop, int mode, void* data, Sfdisc_t* dp) -{ - if(mode==SF_DPOP || mode==SF_FINAL) - free((void*)dp); - else if(mode==SF_WRITE && (errno!= EINTR || sh.trapnote)) - { - int save = errno; - sfpurge(iop); - sfpool(iop,NIL(Sfio_t*),SF_WRITE); - errno = save; - errormsg(SH_DICT,ERROR_system(1),e_badwrite,sffileno(iop)); - } - return(0); -} - #if !SHOPT_ECHOPRINT int B_echo(int argc, char *argv[],void *extra) { @@ -105,7 +89,7 @@ static int outexceptf(Sfio_t* iop, int mode, void* data, Sfdisc_t* dp) struct print prdata; prdata.options = sh_optecho+5; prdata.raw = prdata.echon = 0; - prdata.sh = (Shell_t*)extra; + prdata.sh = ((Shbltin_t*)extra)->shp; NOT_USED(argc); /* This mess is because /bin/echo on BSD is different */ if(!prdata.sh->universe) @@ -145,7 +129,7 @@ int b_printf(int argc, char *argv[],void *extra) struct print prdata; NOT_USED(argc); memset(&prdata,0,sizeof(prdata)); - prdata.sh = (Shell_t*)extra; + prdata.sh = ((Shbltin_t*)extra)->shp; prdata.options = sh_optprintf; return(b_print(-1,argv,&prdata)); } @@ -159,10 +143,10 @@ int b_print(int argc, char *argv[], void *extra) { register Sfio_t *outfile; register int exitval=0,n, fd = 1; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; const char *options, *msg = e_file+4; char *format = 0; - int sflag = 0, nflag=0, rflag=0; + int sflag = 0, nflag=0, rflag=0, vflag=0; if(argc>0) { options = sh_optprint; @@ -196,7 +180,7 @@ int b_print(int argc, char *argv[], void *extra) break; case 's': /* print to history file */ - if(!sh_histinit()) + if(!sh_histinit((void*)shp)) errormsg(SH_DICT,ERROR_system(1),e_history); fd = sffileno(shp->hist_ptr->histfp); sh_onstate(SH_HISTORY); @@ -218,6 +202,9 @@ int b_print(int argc, char *argv[], void *extra) fd = -1; break; + case 'v': + vflag=1; + break; case ':': /* The following is for backward compatibility */ #if OPT_VERSION >= 19990123 @@ -252,6 +239,8 @@ int b_print(int argc, char *argv[], void *extra) argv += opt_info.index; if(error_info.errors || (argc<0 && !(format = *argv++))) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); + if(vflag && format) + errormsg(SH_DICT,ERROR_usage(2),"-v and -f are mutually exclusive"); skip: if(format) format = genformat(format); @@ -265,7 +254,7 @@ skip2: n = 0; } else if(!(n=shp->fdstatus[fd])) - n = sh_iocheckfd(fd); + n = sh_iocheckfd(shp,fd); if(!(n&IOWRITE)) { /* don't print error message for stdout for compatibility */ @@ -275,20 +264,11 @@ skip2: } if(!(outfile=shp->sftable[fd])) { - Sfdisc_t *dp; sh_onstate(SH_NOTRACK); n = SF_WRITE|((n&IOREAD)?SF_READ:0); shp->sftable[fd] = outfile = sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fd,n); sh_offstate(SH_NOTRACK); sfpool(outfile,shp->outpool,SF_WRITE); - if(dp = new_of(Sfdisc_t,0)) - { - dp->exceptf = outexceptf; - dp->seekf = 0; - dp->writef = 0; - dp->readf = 0; - sfdisc(outfile,dp); - } } /* turn off share to guarantee atomic writes for printf */ n = sfset(outfile,SF_SHARE|SF_PUBLIC,0); @@ -316,10 +296,17 @@ skip2: sfpool(sfstderr,pool,SF_WRITE); exitval = pdata.err; } + else if(vflag) + { + while(*argv) + fmtbase64(outfile,*argv++,0); + } else { /* echo style print */ - if(sh_echolist(outfile,rflag,argv) && !nflag) + if(nflag && !argv[0]) + sfsync((Sfio_t*)0); + else if(sh_echolist(outfile,rflag,argv) && !nflag) sfputc(outfile,'\n'); } if(sflag) @@ -454,15 +441,23 @@ static char *fmthtml(const char *string) return(stakptr(offset)); } -static void *fmtbase64(char *string, ssize_t *sz) +#if 1 +static ssize_t fmtbase64(Sfio_t *iop, char *string, int alt) +#else +static void *fmtbase64(char *string, ssize_t *sz, int alt) +#endif { char *cp; Sfdouble_t d; - size_t size; + ssize_t size; Namval_t *np = nv_open(string, NiL, NV_VARNAME|NV_NOASSIGN|NV_NOADD); static union types_t number; - if(!np) - return(""); + if(!np || nv_isnull(np)) + { + if(sh_isoption(SH_NOUNSET)) + errormsg(SH_DICT,ERROR_exit(1),e_notset,string); + return(0); + } if(nv_isattr(np,NV_INTEGER)) { d = nv_getnum(np); @@ -502,11 +497,51 @@ static void *fmtbase64(char *string, ssize_t *sz) number.i = (int)d; } } +#if 1 + return(sfwrite(iop, (void*)&number, size)); +#else if(sz) *sz = size; return((void*)&number); +#endif } if(nv_isattr(np,NV_BINARY)) +#if 1 + { + Namfun_t *fp; + for(fp=np->nvfun; fp;fp=fp->next) + { + if(fp->disc && fp->disc->writef) + break; + } + if(fp) + return (*fp->disc->writef)(np, iop, 0, fp); + else + { + int n = nv_size(np); + cp = (char*)np->nvalue.cp; + if((size = n)==0) + size = strlen(cp); + size = sfwrite(iop, cp, size); + return(n?n:size); + } + } + else if(nv_isarray(np) && nv_arrayptr(np)) + { + nv_outnode(np,iop,(alt?-1:0),0); + sfputc(iop,')'); + return(sftell(iop)); + } + else + { + if(alt && nv_isvtree(np)) + nv_onattr(np,NV_EXPORT); + if(!(cp = nv_getval(np))) + return(0); + size = strlen(cp); + return(sfwrite(iop,cp,size)); + } +#else nv_onattr(np,NV_RAW); cp = nv_getval(np); if(nv_isattr(np,NV_BINARY)) @@ -516,6 +551,33 @@ static void *fmtbase64(char *string, ssize_t *sz) if(sz) *sz = size; return((void*)cp); +#endif +} + +static int varname(const char *str, int n) +{ + register int c,dot=1,len=1; + if(n < 0) + { + if(*str=='.') + str++; + n = strlen(str); + } + for(;n > 0; n-=len) + { +#ifdef SHOPT_MULTIBYTE + len = mbsize(str); + c = mbchar(str); +#else + c = *(unsigned char*)str++; +#endif + if(dot && !(isalpha(c)||c=='_')) + break; + else if(dot==0 && !(isalnum(c) || c=='_' || c == '.')) + break; + dot = (c=='.'); + } + return(n==0); } static int extend(Sfio_t* sp, void* v, Sffmt_t* fe) @@ -532,6 +594,20 @@ static int extend(Sfio_t* sp, void* v, Sffmt_t* fe) struct printf* pp = (struct printf*)fe; register char* argp = *pp->nextarg; + if(fe->n_str>0 && varname(fe->t_str,fe->n_str) && (!argp || varname(argp,-1))) + { + if(argp) + pp->lastarg = argp; + else + argp = pp->lastarg; + if(argp) + { + sfprintf(pp->sh->strbuf,"%s.%.*s%c",argp,fe->n_str,fe->t_str,0); + argp = sfstruse(pp->sh->strbuf); + } + } + else + pp->lastarg = 0; fe->flags |= SFFMT_VALUE; if(!argp || format=='Z') { @@ -662,6 +738,11 @@ static int extend(Sfio_t* sp, void* v, Sffmt_t* fe) case '\'': case '"': value->ll = ((unsigned char*)argp)[1]; + if(argp[2] && (argp[2] != argp[0] || argp[3])) + { + errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp); + pp->err = 1; + } break; default: d = sh_strnum(argp,&lastchar,0); @@ -698,6 +779,21 @@ static int extend(Sfio_t* sp, void* v, Sffmt_t* fe) case 'F': case 'G': d = sh_strnum(*pp->nextarg,&lastchar,0); + switch(*argp) + { + case '\'': + case '"': + d = ((unsigned char*)argp)[1]; + if(argp[2] && (argp[2] != argp[0] || argp[3])) + { + errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp); + pp->err = 1; + } + break; + default: + d = sh_strnum(*pp->nextarg,&lastchar,0); + break; + } if(SFFMT_LDOUBLE) { value->ld = d; @@ -750,7 +846,11 @@ static int extend(Sfio_t* sp, void* v, Sffmt_t* fe) } break; case 'B': - value->s = (char*)fmtbase64(value->s, &fe->size); + if(!sh.strbuf2) + sh.strbuf2 = sfstropen(); + fe->size = fmtbase64(sh.strbuf2,value->s, fe->flags&SFFMT_ALTER); + value->s = sfstruse(sh.strbuf2); + fe->flags |= SFFMT_SHORT; break; case 'H': value->s = fmthtml(value->s); diff --git a/usr/src/lib/libshell/common/bltins/read.c b/usr/src/lib/libshell/common/bltins/read.c index 930470275c..874b575986 100644 --- a/usr/src/lib/libshell/common/bltins/read.c +++ b/usr/src/lib/libshell/common/bltins/read.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -19,7 +19,7 @@ ***********************************************************************/ #pragma prototyped /* - * read [-Aprs] [-d delim] [-u filenum] [-t timeout] [-n n] [-N n] [name...] + * read [-ACprs] [-d delim] [-u filenum] [-t timeout] [-n n] [-N n] [name...] * * David Korn * AT&T Labs @@ -45,23 +45,49 @@ #define N_FLAG 8 /* fixed size read at most */ #define NN_FLAG 0x10 /* fixed size read exact */ #define V_FLAG 0x20 /* use default value */ +#define C_FLAG 0x40 /* read into compound variable */ #define D_FLAG 8 /* must be number of bits for all flags */ +struct read_save +{ + char **argv; + char *prompt; + short fd; + short plen; + int flags; + long timeout; +}; + int b_read(int argc,char *argv[], void *extra) { Sfdouble_t sec; register char *name; register int r, flags=0, fd=0; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; long timeout = 1000*shp->st.tmout; - int save_prompt; + int save_prompt, fixargs=((Shbltin_t*)extra)->invariant; + struct read_save *rp; static char default_prompt[3] = {ESC,ESC}; - NOT_USED(argc); + if(argc==0) + return(0); + if(rp = (struct read_save*)(((Shbltin_t*)extra)->data)) + { + flags = rp->flags; + timeout = rp->timeout; + fd = rp->fd; + argv = rp->argv; + name = rp->prompt; + r = rp->plen; + goto bypass; + } while((r = optget(argv,sh_optread))) switch(r) { case 'A': flags |= A_FLAG; break; + case 'C': + flags |= C_FLAG; + break; case 't': sec = sh_strnum(opt_info.arg, (char**)0,1); timeout = sec ? 1000*sec : 1; @@ -79,11 +105,11 @@ int b_read(int argc,char *argv[], void *extra) errormsg(SH_DICT,ERROR_exit(1),e_query); break; case 'n': case 'N': - flags &= ~((1<<D_FLAG)-1); + flags &= ((1<<D_FLAG)-1); flags |= (r=='n'?N_FLAG:NN_FLAG); r = (int)opt_info.num; if((unsigned)r > (1<<((8*sizeof(int))-D_FLAG))-1) - errormsg(SH_DICT,ERROR_exit(1),e_overlimit,"n"); + errormsg(SH_DICT,ERROR_exit(1),e_overlimit,opt_info.name); flags |= (r<< D_FLAG); break; case 'r': @@ -112,19 +138,30 @@ int b_read(int argc,char *argv[], void *extra) if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2), "%s", optusage((char*)0)); if(!((r=shp->fdstatus[fd])&IOREAD) || !(r&(IOSEEK|IONOSEEK))) - r = sh_iocheckfd(fd); + r = sh_iocheckfd(shp,fd); if(fd<0 || !(r&IOREAD)) errormsg(SH_DICT,ERROR_system(1),e_file+4); /* look for prompt */ shp->prompt = default_prompt; if((name = *argv) && (name=strchr(name,'?')) && (r&IOTTY)) + r = strlen(name++); + else + r = 0; + if(argc==fixargs && (rp=newof(NIL(struct read_save*),struct read_save,1,0))) { - r = strlen(++name)+1; - if(shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR)) - { - memcpy(shp->prompt,name,r); - sfwrite(sfstderr,shp->prompt,r-1); - } + ((Shbltin_t*)extra)->data = (void*)rp; + rp->fd = fd; + rp->flags = flags; + rp->timeout = timeout; + rp->argv = argv; + rp->prompt = name; + rp->plen = r; + } +bypass: + if(r && (shp->prompt=(char*)sfreserve(sfstderr,r,SF_LOCKR))) + { + memcpy(shp->prompt,name,r); + sfwrite(sfstderr,shp->prompt,r-1); } shp->timeout = 0; save_prompt = shp->nextprompt; @@ -166,28 +203,31 @@ int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeo register unsigned char *cp; register Namval_t *np; register char *name, *val; - register Sfio_t *iop; + register Sfio_t *iop; + Namfun_t *nfp; char *ifs; unsigned char *cpmax; unsigned char *del; char was_escape = 0; char use_stak = 0; - char was_write = 0; - char was_share = 1; + volatile char was_write = 0; + volatile char was_share = 1; int rel, wrd; long array_index = 0; void *timeslot=0; int delim = '\n'; int jmpval=0; int size = 0; + int binary; struct checkpt buff; - if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(fd))) + if(!(iop=shp->sftable[fd]) && !(iop=sh_iostream(shp,fd))) return(1); + sh_stats(STAT_READS); if(names && (name = *names)) { if(val= strchr(name,'?')) *val = 0; - np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME|NV_ARRAY); + np = nv_open(name,shp->var_tree,NV_NOASSIGN|NV_VARNAME); if((flags&V_FLAG) && shp->ed_context) ((struct edit*)shp->ed_context)->e_default = np; if(flags&A_FLAG) @@ -197,6 +237,12 @@ int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeo nv_unset(np); nv_putsub(np,NIL(char*),0L); } + else if(flags&C_FLAG) + { + delim = -1; + nv_unset(np); + nv_setvtree(np); + } else name = *++names; if(val) @@ -219,11 +265,12 @@ int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeo if(shp->fdstatus[fd]&IOTTY) tty_raw(fd,1); } - if(!(flags&(N_FLAG|NN_FLAG))) + binary = nv_isattr(np,NV_BINARY); + if(!binary && !(flags&(N_FLAG|NN_FLAG))) { Namval_t *mp; /* set up state table based on IFS */ - ifs = nv_getval(mp=nv_scoped(IFSNOD)); + ifs = nv_getval(mp=sh_scoped(shp,IFSNOD)); if((flags&R_FLAG) && shp->ifstable['\\']==S_ESC) shp->ifstable['\\'] = 0; else if(!(flags&R_FLAG) && shp->ifstable['\\']==0) @@ -237,8 +284,19 @@ int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeo shp->ifstable[0] = S_EOF; } sfclrerr(iop); - if(np->nvfun && np->nvfun->disc->readf) - return((* np->nvfun->disc->readf)(np,iop,delim,np->nvfun)); + for(nfp=np->nvfun; nfp; nfp = nfp->next) + { + if(nfp->disc && nfp->disc->readf) + { + if((c=(*nfp->disc->readf)(np,iop,delim,nfp))>=0) + return(c); + } + } + if(binary && !(flags&(N_FLAG|NN_FLAG))) + { + flags |= NN_FLAG; + size = nv_size(np); + } was_write = (sfset(iop,SF_WRITE,0)&SF_WRITE)!=0; if(fd==0) was_share = (sfset(iop,SF_SHARE,1)&SF_SHARE)!=0; @@ -253,13 +311,17 @@ int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeo } if(flags&(N_FLAG|NN_FLAG)) { - char buf[64],*var=buf; + char buf[64],*var=buf,*cur,*end,*up,*v; /* reserved buffer */ if((c=size)>=sizeof(buf)) { if(!(var = (char*)malloc(c+1))) sh_exit(1); + end = var + c; } + else + end = var + sizeof(buf) - 1; + up = cur = var; if((sfset(iop,SF_SHARE,1)&SF_SHARE) && fd!=0) was_share = 1; if(size==0) @@ -269,38 +331,93 @@ int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeo } else { - c= (shp->fdstatus[fd]&(IOTTY|IONOSEEK))?1:-1; - if(flags&NN_FLAG) - c = size; - if(cp = sfreserve(iop,c,!(flags&NN_FLAG))) - c = sfvalue(iop); - else - c = 0; - if(c>size) - c = size; - if(c>0) + int f,m; + for (;;) { - memcpy((void*)var,cp,c); - if(flags&N_FLAG) - sfread(iop,cp,c); + c = (flags&NN_FLAG) ? -size : -1; + cp = sfreserve(iop,c,SF_LOCKR); + f = 1; + if((m = sfvalue(iop)) > 0) + { + if(!cp) + { + m = (cp = sfreserve(iop,size,0)) ? sfvalue(iop) : 0; + f = 0; + } + if(m>0 && (flags&N_FLAG) && !binary && (v=memchr(cp,'\n',m))) + m = v-(char*)cp; + } + if((c=m)>size) + c = size; + if(c>0) + { + if(c > (end-cur)) + { + int cx = cur - var, ux = up - var; + if (var == buf) + { + m = (end - var) + (c - (end - cur)); + v = (char*)malloc(m+1); + memcpy(v, var, cur - var); + } + else + v = newof(var, char, m, 1); + end = v + m; + cur = v + cx; + up = v + ux; + } + memcpy((void*)cur,cp,c); + if(f) + sfread(iop,cp,c); + cur += c; +#if SHOPT_MULTIBYTE + if(!binary && mbwide()) + { + int x; + int z; + int y = cur - up; + + mbinit(); + *cur = 0; + x = z = 0; + while (up < cur && (z = mbsize(up)) > 0) + { + up += z; + x++; + } + if((size -= x) > 0 && (up >= cur || z < 0) && ((flags & NN_FLAG) || z < 0 || m > c)) + continue; + } +#endif + } +#if SHOPT_MULTIBYTE + if(!binary && mbwide() && (up == var || (flags & NN_FLAG) && size)) + cur = var; +#endif + *cur = 0; + if(c>=size) + sfclrerr(iop); + break; } - var[c] = 0; - if(c>=size) - sfclrerr(iop); } if(timeslot) timerdel(timeslot); - if(nv_isattr(np,NV_BINARY)) + if(binary) { - if(c<sizeof(buf)) - var = memdup(var,c); - nv_putval(np,var, NV_RAW); - nv_setsize(np,c); + if(c==nv_size(np)) + memcpy((char*)np->nvalue.cp,var,c); + else + { + if(var==buf) + var = memdup(var,c); + nv_putval(np,var,NV_RAW); + nv_setsize(np,c); + } } else { nv_putval(np,var,0); - if(c>=sizeof(buf)) + if(var!=buf) free((void*)var); } goto done; @@ -313,7 +430,7 @@ int sh_readline(register Shell_t *shp,char **names, int fd, int flags,long timeo timerdel(timeslot); if((flags&S_FLAG) && !shp->hist_ptr) { - sh_histinit(); + sh_histinit((void*)shp); if(!shp->hist_ptr) flags &= ~S_FLAG; } diff --git a/usr/src/lib/libshell/common/bltins/shiocmd_solaris.c b/usr/src/lib/libshell/common/bltins/shiocmd_solaris.c index 25bced42a2..84b875f4cb 100644 --- a/usr/src/lib/libshell/common/bltins/shiocmd_solaris.c +++ b/usr/src/lib/libshell/common/bltins/shiocmd_solaris.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2007 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -21,6 +21,7 @@ #include <shell.h> #include <stdio.h> +#include <stdbool.h> #include <option.h> #include <stk.h> #include <tm.h> @@ -31,6 +32,9 @@ #endif #include <poll.h> +#define sh_contexttoshb(context) ((Shbltin_t*)(context)) +#define sh_contexttoshell(context) ((context)?(sh_contexttoshb(context)->shp):(NULL)) + /* * time formatting related */ @@ -487,7 +491,7 @@ extern int b_open(int argc, char *argv[], void *extra) { register Namval_t *np; register int n,oflag=0; - Shell_t *shp = (Shell_t*)extra; + Shell_t *shp = sh_contexttoshell(extra); struct filedata *fdp; mode_t mode = 0666; long flags = 0; @@ -658,18 +662,18 @@ extern int b_tmpfile(int argc, char *argv[], void *extra) { register Namval_t *np; register int n; - Shell_t *shp = (Shell_t*)extra; + Shell_t *shp = sh_contexttoshell(extra); struct filedata *fdp; - int inherit = 0; + bool inherit = false; FILE *file = NULL; int ffd, fd = -1; while (n = optget(argv, sh_opttmpfile)) switch (n) { case 'i': - inherit = 1; + inherit = true; break; case 'I': - inherit = 0; + inherit = false; break; case ':': errormsg(SH_DICT, 2, "%s", opt_info.arg); @@ -734,17 +738,17 @@ extern int b_dup(int argc, char *argv[], void *extra) { register Namval_t *np; register int n; - Shell_t *shp = (Shell_t*)extra; + Shell_t *shp = sh_contexttoshell(extra); struct filedata *fdp; - int inherit = 0; + bool inherit = false; int ffd, fd = -1; while (n = optget(argv, sh_optdup)) switch (n) { case 'i': - inherit = 1; + inherit = true; break; case 'I': - inherit = 0; + inherit = false; break; case ':': errormsg(SH_DICT, 2, "%s", opt_info.arg); @@ -809,7 +813,7 @@ extern int b_stat(int argc, char *argv[], void *extra) { register Namval_t *np; register int n; - Shell_t *shp = (Shell_t*)extra; + Shell_t *shp = sh_contexttoshell(extra); struct filedata *fdp; long flags = 0; struct stat statb; @@ -854,7 +858,7 @@ extern int b_stat(int argc, char *argv[], void *extra) } static const char sh_optpoll[] = -"[-?\n@(#)$Id: poll (AT&T Labs Research) 2007-05-07 $\n]" +"[-?\n@(#)$Id: poll (AT&T Labs Research) 2007-12-20 $\n]" "[-author?Roland Mainz <roland.mainz@nrubsig.org]" "[-license?http://www.opensource.org/licenses/cpl1.0.txt]" "[+NAME? poll - input/output multiplexing]" @@ -957,7 +961,17 @@ static const char sh_optpoll[] = "[+?Regular files always poll TRUE for reading and writing.]" -"[t:timeout]:[milliseconds?Timeout in milliseconds. If the value timeout is 0, " +"[c:fdcount]:[fdcount?Upon successful completion, a non-negative value is " + "returned. A positive value indicates the total number of " + "file descriptors that has been selected (that is, file " + "descriptors for which the revents member is non-zero). A " + "value of 0 indicates that the call timed out and no file " + "descriptors have been selected. Upon failure, -1 is returned.]" +"[t:timeout]:[seconds?Timeout in seconds. If the value timeout is 0, " + "poll returns immediately. If the value of timeout is -1, poll " + "blocks until a requested event occurs or until the call is " + "interrupted.]" +"[T:mtimeout]:[milliseconds?Timeout in milliseconds. If the value timeout is 0, " "poll returns immediately. If the value of timeout is -1, poll " "blocks until a requested event occurs or until the call is " "interrupted.]" @@ -1041,32 +1055,48 @@ void poll_eventstostr(char *s, int events) if(*s=='|') *s='\0'; } + +#undef getconf +#define getconf(x) strtol(astconf(x,NiL,NiL),NiL,0) extern int b_poll(int argc, char *argv[], void *extra) { register Namval_t *np; register int n; - Shell_t *shp = (Shell_t*)extra; + Shell_t *shp = sh_contexttoshell(extra); char *varname; int fd; -/* |BPOLL_MAX| needs to be larger than |OPEN_MAX| to make sure we - * can listen to different sets of events per fd. - */ -#define BPOLL_MAX 512 - struct pollfd pollfd[BPOLL_MAX]; unsigned int numpollfd = 0; int i; char *s; - long timeout = -1; + double timeout = -1.; char buff[256]; - + char *pollfdcountvarname = NULL; + long open_max, + bpoll_max; + + if ((open_max = getconf("OPEN_MAX")) <= 0) + open_max = OPEN_MAX; + /* |bpoll_max| needs to be larger than |OPEN_MAX| to make sure we + * can listen to different sets of events per fd. + */ + bpoll_max = open_max*2L; + while (n = optget(argv, sh_optpoll)) switch (n) { case 't': + case 'T': errno = 0; - timeout = strtol(opt_info.arg, (char **)NULL, 0); + timeout = strtod(opt_info.arg, (char **)NULL); if (errno != 0) errormsg(SH_DICT, ERROR_system(1), "%s: invalid timeout", opt_info.arg); + + /* -t uses seconds, -T milliseconds */ + if (n == 't') + timeout *= 1000.; + break; + case 'c': + pollfdcountvarname = opt_info.arg; break; case ':': errormsg(SH_DICT, 2, "%s", opt_info.arg); @@ -1081,25 +1111,27 @@ extern int b_poll(int argc, char *argv[], void *extra) errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0)); varname = argv[0]; + + struct pollfd pollfd[bpoll_max]; - for(i=0 ; i < BPOLL_MAX ; i++) + for(i=0 ; i < bpoll_max ; i++) { - np = nv_open_fmt(shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN|NV_NOFAIL|NV_NOADD, "%s[%d].fd", varname, i); + np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].fd", varname, i); if (!np) break; fd = (int)nv_getnum(np); if (fd < 0 || fd > OPEN_MAX) - errormsg(SH_DICT, ERROR_system(1), "poll: invalid pollfd fd"); + errormsg(SH_DICT, ERROR_system(1), "invalid pollfd fd"); nv_close(np); pollfd[i].fd = fd; - np = nv_open_fmt(shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN|NV_NOFAIL|NV_NOADD, "%s[%d].events", varname, i); - if (!s) - errormsg(SH_DICT, ERROR_system(1), "poll: missing pollfd events"); + np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL|NV_NOADD, "%s[%d].events", varname, i); + if (!np) + errormsg(SH_DICT, ERROR_system(1), "missing pollfd events"); s = nv_getval(np); if (!s) - errormsg(SH_DICT, ERROR_system(1), "poll: missing pollfd events value"); + errormsg(SH_DICT, ERROR_system(1), "missing pollfd events value"); pollfd[i].events = poll_strtoevents(s); nv_close(np); @@ -1108,19 +1140,30 @@ extern int b_poll(int argc, char *argv[], void *extra) numpollfd++; } - if (i == BPOLL_MAX) - errormsg(SH_DICT, ERROR_system(1), "poll: cannot handle more than %d entries.", BPOLL_MAX); + if (i == bpoll_max) + errormsg(SH_DICT, ERROR_system(1), "cannot handle more than %d entries.", bpoll_max); n = poll(pollfd, numpollfd, timeout); /* FixMe: EGAIN and EINTR may require extra handling */ if (n < 0) - errormsg(SH_DICT, ERROR_system(1), "poll: failure"); + errormsg(SH_DICT, ERROR_system(1), "failure"); + + if (pollfdcountvarname) + { + int32_t v = n; + + np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s", pollfdcountvarname); + if (!np) + errormsg(SH_DICT, ERROR_system(1), "couldn't create poll count variable %s", pollfdcountvarname); + nv_putval(np, (char *)&v, NV_INTEGER); + nv_close(np); + } for(i=0 ; i < numpollfd ; i++) { - np = nv_open_fmt(shp->var_tree, NV_ARRAY|NV_VARNAME|NV_NOASSIGN|NV_NOFAIL, "%s[%d].revents", varname, i); + np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_NOFAIL, "%s[%d].revents", varname, i); if (!np) - errormsg(SH_DICT, ERROR_system(1), "poll: couldn't create pollfd %s[%d].revents", varname, i); + errormsg(SH_DICT, ERROR_system(1), "couldn't create pollfd %s[%d].revents", varname, i); poll_eventstostr(buff, pollfd[i].revents); @@ -1150,7 +1193,7 @@ static const char sh_optrewind[] = extern int b_rewind(int argc, char *argv[], void *extra) { - Shell_t *shp = (Shell_t*)extra; + Shell_t *shp = sh_contexttoshell(extra); int fd = -1; register int n; while (n = optget(argv, sh_optrewind)) switch (n) @@ -1177,4 +1220,3 @@ extern int b_rewind(int argc, char *argv[], void *extra) return(0); } - diff --git a/usr/src/lib/libshell/common/bltins/shopen.c b/usr/src/lib/libshell/common/bltins/shopen.c deleted file mode 100644 index 6a7a85a990..0000000000 --- a/usr/src/lib/libshell/common/bltins/shopen.c +++ /dev/null @@ -1,533 +0,0 @@ -/*********************************************************************** -* * -* This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * -* and is licensed under the * -* Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * -* * -* A copy of the License is available at * -* http://www.opensource.org/licenses/cpl1.0.txt * -* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * -* * -* Information and Software Systems Research * -* AT&T Research * -* Florham Park NJ * -* * -* David Korn <dgk@research.att.com> * -* * -***********************************************************************/ -#pragma prototyped - -static const char id[] = "\n@(#)$Id: open (AT&T Research) 1998-07-07 $\0\n"; - -#include <shell.h> -#include <option.h> -#include <stk.h> -#include <tm.h> -#ifndef SH_DICT -# define SH_DICT "libshell" -#endif - -/* - * time formatting related -*/ -struct dctime -{ - Namfun_t fun; - Namval_t *format; -}; - -static char *get_time(Namval_t* np, Namfun_t* nfp) -{ - static char buff[256]; - struct dctime *dp = (struct dctime*)nfp; - time_t t = nv_getn(np,nfp); - char *format = nv_getval(dp->format); - tmfmt(buff,sizeof(buff),format,(time_t*)0); - return(buff); -} - -static void put_time(Namval_t* np, const char* val, int flag, Namfun_t* nfp) -{ - struct dctime *dp = (struct dctime*)nfp; - char *last; - if(val) - { - int32_t t; - if(flag&NV_INTEGER) - { - if(flag&NV_LONG) - t = *(Sfdouble_t*)val; - else - t = *(double*)val; - } - else - { - t = tmdate(val, &last, (time_t*)0); - if(*last) - errormsg(SH_DICT,ERROR_exit(1),"%s: invalid date/time string",val); - } - nv_putv(np,(char*)&t,NV_INTEGER,nfp); - } - else - { - nv_unset(dp->format); - free((void*)dp->format); - nv_putv(np,val,flag,nfp); - } -} - -static Namval_t *create_time(Namval_t *np, const char *name, int flags, Namfun_t *nfp) -{ - struct dctime *dp = (struct dctime*)nfp; - if(strcmp(name,"format")) - return((Namval_t*)0); - return(dp->format); -} - -static const Namdisc_t timedisc = -{ - sizeof(struct dctime), - put_time, - get_time, - 0, - 0, - create_time, -}; - - -static Namval_t *make_time(Namval_t* np) -{ - int offset = stktell(stkstd); - char *name = nv_name(np); - struct dctime *dp = newof(NULL,struct dctime,1,0); - if(!dp) - return((Namval_t*)0); - sfprintf(stkstd,"%s.format\0",name); - sfputc(stkstd,0); - dp->format = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD); - dp->fun.disc = &timedisc; - nv_stack(np,&dp->fun); - return(np); -} - -/* - * mode formatting related -*/ -static char *get_mode(Namval_t* np, Namfun_t* nfp) -{ - mode_t mode = nv_getn(np,nfp); - return(fmtperm(mode)); -} - -static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp) -{ - if(val) - { - int32_t mode; - char *last; - if(flag&NV_INTEGER) - { - if(flag&NV_LONG) - mode = *(Sfdouble_t*)val; - else - mode = *(double*)val; - } - else - { - mode = strperm(val, &last,0); - if(*last) - errormsg(SH_DICT,ERROR_exit(1),"%s: invalid mode string",val); - } - nv_putv(np,(char*)&mode,NV_INTEGER,nfp); - } - else - nv_putv(np,val,flag,nfp); -} - -static const Namdisc_t modedisc = -{ - 0, - put_mode, - get_mode, -}; - -static Namval_t *make_mode(Namval_t* np) -{ - char *name = nv_name(np); - Namfun_t *nfp = newof(NULL,Namfun_t,1,0); - if(!nfp) - return((Namval_t*)0); - nfp->disc = &modedisc; - nv_stack(np,nfp); - return(np); -} - -/* - * field related typese and functions - */ -typedef struct _field_ -{ - char *name; /* field name */ - int flags; /* flags */ - short offset; /* offset of field into data */ - short size; /* size of field */ - Namval_t *(*make)(Namval_t*); /* discipline constructor */ -} Shfield_t; - -/* - * lookup field in field table - */ -static Shfield_t *sh_findfield(Shfield_t *ftable, int nelem, const char *name) -{ - Shfield_t *fp = ftable; - register int i,n; - register const char *cp; - for(cp=name; *cp; cp++) - { - if(*cp=='.') - break; - } - n = cp-name; - for(i=0; i < nelem; i++,fp++) - { - if(memcmp(fp->name,name,n)==0 && fp->name[n]==0) - return(fp); - } - return(0); -} - -/* - * class types and functions - */ - -typedef struct _class_ -{ - int nelem; /* number of elements */ - int dsize; /* size for data structure */ - Shfield_t *fields; /* field description table */ -} Shclass_t; - -struct dcclass -{ - Namfun_t fun; - Shclass_t sclass; -}; - -static Namval_t *sh_newnode(register Shfield_t *fp, Namval_t *np) -{ - char *val = np->nvalue + fp->offset; - char *name = nv_name(np); - register Namval_t *nq; - int offset = stktell(stkstd); - sfprintf(stkstd,"%s.%s\0",name,fp->name); - sfputc(stkstd,0); - nq = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD); - if(fp->size<0) - val = *(char**)val; - nv_putval(nq,val,fp->flags|NV_NOFREE); - if(fp->make) - (*fp->make)(nq); - return(nq); -} - -static Namval_t *fieldcreate(Namval_t *np, const char *name, int flags, Namfun_t *nfp) -{ - struct dcclass *dcp = (struct dcclass*)nfp; - Shclass_t *sp = &dcp->sclass; - Shfield_t *fp = sh_findfield(sp->fields,sp->nelem,name); - Namval_t *nq,**nodes = (Namval_t**)(dcp+1); - int n = fp-sp->fields; - int len = strlen(fp->name); - void *data = (void*)np->nvalue; - if(!(nq=nodes[n])) - { - nodes[n] = nq = sh_newnode(fp,np); - nfp->last = ""; - } - if(name[len]==0) - return(nq); - return(nq); -} - -static void genvalue(Sfio_t *out, Shclass_t *sp, int indent, Namval_t *npar) -{ - Shfield_t *fp = sp->fields; - Namval_t *np, **nodes= (Namval_t**)(sp+1); - register int i,isarray; - if(out) - { - sfwrite(out,"(\n",2); - indent++; - } - for(i=0; i < sp->nelem; i++,fp++) - { -#if 0 - /* handle recursive case */ -#endif - if(!(np=nodes[i]) && out) - np = sh_newnode(fp,npar); - if(np) - { - isarray=0; - if(nv_isattr(np,NV_ARRAY)) - { - isarray=1; - if(array_elem(nv_arrayptr(np))==0) - isarray=2; - else - nv_putsub(np,(char*)0,ARRAY_SCAN); - } - sfnputc(out,'\t',indent); - sfputr(out,fp->name,(isarray==2?'\n':'=')); - if(isarray) - { - if(isarray==2) - continue; - sfwrite(out,"(\n",2); - sfnputc(out,'\t',++indent); - } - while(1) - { - char *fmtq; - if(isarray) - { - sfprintf(out,"[%s]",sh_fmtq(nv_getsub(np))); - sfputc(out,'='); - } - if(!(fmtq=nv_getval(np)) || !(fmtq=sh_fmtq(fmtq))) - fmtq = ""; - sfputr(out,fmtq,'\n'); - if(!nv_nextsub(np)) - break; - sfnputc(out,'\t',indent); - } - if(isarray) - { - sfnputc(out,'\t',--indent); - sfwrite(out,")\n",2); - } - } - } - if(out) - { - if(indent>1) - sfnputc(out,'\t',indent-1); - sfputc(out,')'); - } -} - -static char *walk_class(register Namval_t *np, int dlete, struct dcclass *dcp) -{ - static Sfio_t *out; - Sfio_t *outfile; - int savtop = stktell(stkstd); - char *savptr = stkfreeze(stkstd,0); - if(dlete) - outfile = 0; - else if(!(outfile=out)) - outfile = out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING); - else - sfseek(outfile,0L,SEEK_SET); - genvalue(outfile,&dcp->sclass,0,np); - stkset(stkstd,savptr,savtop); - if(!outfile) - return((char*)0); - sfputc(out,0); - return((char*)out->_data); -} - -static char *get_classval(Namval_t* np, Namfun_t* nfp) -{ - return(walk_class(np,0,(struct dcclass *)nfp)); -} - -static void put_classval(Namval_t* np, const char* val, int flag, Namfun_t* nfp) -{ - walk_class(np,1,(struct dcclass *)nfp); - if(nfp = nv_stack(np,(Namfun_t*)0)) - { - free((void*)nfp); - if(np->nvalue && !nv_isattr(np,NV_NOFREE)) - free((void*)np->nvalue); - } - if(val) - nv_putval(np,val,flag); -} - -static const Namdisc_t classdisc = -{ - sizeof(struct dcclass), - put_classval, - get_classval, - 0, - 0, - fieldcreate -}; - -static int mkclass(Namval_t *np, Shclass_t *sp) -{ - struct dcclass *tcp = newof(NULL,struct dcclass,1,sp->nelem*sizeof(Namval_t*)); - if(!tcp) - return(0); - memset((void*)(tcp+1),0,sp->nelem*sizeof(Namval_t*)); - tcp->fun.disc = &classdisc; - tcp->sclass = *sp; - np->nvalue = (char*)calloc(sp->dsize,1); - nv_stack(np,&tcp->fun); - return(1); -} - -/* - * ====================from here down is file class specific - */ -static struct stat *Sp; - -struct filedata -{ - struct stat statb; - int fd; - char *name; -}; - -static Shfield_t filefield[] = -{ - { "atime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_atime), sizeof(Sp->st_atime), make_time}, - { "ctime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ctime), sizeof(Sp->st_ctime), make_time}, - { "dev", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_dev),sizeof(Sp->st_dev)}, - { "fd", NV_INTEGER|NV_RDONLY, offsetof(struct filedata,fd), sizeof(int)}, - { "gid", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_gid), sizeof(Sp->st_gid)}, - { "ino", NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_ino), sizeof(Sp->st_ino)}, - { "mode", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mode), sizeof(Sp->st_mode), make_mode}, - { "mtime", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_mtime), sizeof(Sp->st_mtime), make_time}, - { "name", NV_RDONLY, offsetof(struct filedata,name), -1 }, - { "nlink", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_nlink), sizeof(Sp->st_nlink)}, - { "size", NV_LONG|NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_size), sizeof(Sp->st_size)}, - { "uid", NV_INTEGER|NV_RDONLY, offsetof(struct stat,st_uid), sizeof(Sp->st_uid)} -}; - -static Shclass_t Fileclass = -{ - sizeof(filefield)/sizeof(*filefield), - sizeof(struct filedata), - filefield -}; - - -#define letterbit(bit) (1<<((bit)-'a')) - -static const char sh_optopen[] = -"[-?\n@(#)$Id: open (AT&T Labs Research) 2007-03-11 $\n]" -"[-author?David Korn <dgk@research.att.com>]" -"[-license?http://www.opensource.org/licenses/cpl1.0.txt]" -"[+NAME? open - create a shell variable correspnding to a file]" -"[+DESCRIPTION?\bopen\b creates the compound variable \avar\a correspinding " - "to the file given by the pathname \afile\a. The elements of \avar\a " - "are the names of elements in the \astat\a structure with the \bst_\b " - "prefix removed.]" -"[+?If the \b-r\b and/or \b-w\b mode is specified, then \afile\a is opened and " - "the variable \avar\a\b.fd\b is the file descriptor.]" -"[a:append?Open for append.]" -"[b:binary?Open in binary mode.]" -"[c:create?Open for create.]" -"[i:inherit?Open without the close-on-exec bit set.]" -"[r:read?Open with read access.]" -"[w:write?Open with write access.]" -"[m:mode]:[mode:=rwrwrw?Open with access mode \amode\a.]" -"[x:exclusive?Open exclusive.]" -"\n" -"\nvar file\n" -"\n" -"[+EXIT STATUS?]{" - "[+0?Success.]" - "[+>0?An error occurred.]" -"}" -"[+SEE ALSO?\bstat\b(2)]" -; - - -extern int b_open(int argc, char *argv[], void *extra) -{ - register Namval_t *np; - register int n,oflag=0; - Shell_t *shp = (Shell_t*)extra; - struct filedata *fdp; - struct stat statb; - mode_t mode = 0666; - long flags = 0; - int fd = -1; - while (n = optget(argv, sh_optopen)) switch (n) - { - case 'r': - case 'i': - case 'w': - flags |= letterbit(n); - break; - case 'b': -#ifdef O_BINARY - oflag |= O_BINARY; -#endif - break; - case 't': -#ifdef O_TEXT - oflag |= O_TEXT; -#endif - break; - case 'x': - oflag |= O_EXCL; - break; - case 'c': - oflag |= O_CREAT; - break; - case 'a': - oflag |= O_APPEND; - break; - case 'm': - break; - case ':': - errormsg(SH_DICT,2, "%s", opt_info.arg); - break; - case '?': - errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg); - break; - } - argc -= opt_info.index; - argv += opt_info.index; - if(argc!=2) - errormsg(SH_DICT,ERROR_usage(2),optusage((char*)0)); - if(!(flags&(letterbit('r')|letterbit('w')))) - { - if(stat(argv[1],&statb)<0) - errormsg(SH_DICT,ERROR_system(1),"%s: open failed",argv[1]); - } - else - { - if(flags&letterbit('r')) - { - if(flags&letterbit('w')) - oflag |= O_RDWR; - else - oflag |= O_RDONLY; - } - else if(flags&letterbit('w')) - oflag |= O_WRONLY; - fd = open(argv[1],oflag,mode); - if(fd<0) - errormsg(SH_DICT,ERROR_system(1),"%s: open failed",argv[1]); - } - if(!(flags&letterbit('i'))) - fcntl(fd,F_SETFL,0); - np = nv_open(argv[0],shp->var_tree,NV_ARRAY|NV_VARNAME|NV_NOASSIGN); - if(!nv_isnull(np)) - nv_unset(np); - mkclass(np,&Fileclass); - fdp = (struct filedata*)np->nvalue; - if(!(flags&(letterbit('r')|letterbit('w')))) - fdp->statb = statb; - else - fstat(fd,&fdp->statb); - fdp->fd = fd; - fdp->name = strdup(argv[1]); - return(0); -} diff --git a/usr/src/lib/libshell/common/bltins/sleep.c b/usr/src/lib/libshell/common/bltins/sleep.c index 2f52ca651c..48af7fe807 100644 --- a/usr/src/lib/libshell/common/bltins/sleep.c +++ b/usr/src/lib/libshell/common/bltins/sleep.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -23,7 +23,6 @@ * * David Korn * AT&T Labs - * research!dgk * */ @@ -46,8 +45,11 @@ int b_sleep(register int argc,char *argv[],void *extra) { register char *cp; register double d; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; time_t tloc = 0; + char *last; + if(!(shp->sigflag[SIGALRM]&(SH_SIGFAULT|SH_SIGOFF))) + sh_sigtrap(SIGALRM); while((argc = optget(argv,sh_optsleep))) switch(argc) { case ':': @@ -58,11 +60,10 @@ int b_sleep(register int argc,char *argv[],void *extra) break; } argv += opt_info.index; - if(error_info.errors || !(cp= *argv) || !(strmatch(cp,e_numeric))) + if(error_info.errors || !(cp= *argv) || ((d=strtod(cp, (char**)&last)),*last)) errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0)); - if((d=strtod(cp, (char**)0)) > .10) + if(d > .10) { - sfsync(shp->outpool); time(&tloc); tloc += (time_t)(d+.5); } @@ -92,22 +93,23 @@ static void completed(void * handle) unsigned int sleep(unsigned int sec) { + Shell_t *shp = &sh; pid_t newpid, curpid=getpid(); void *tp; char expired = 0; - sh.lastsig = 0; + shp->lastsig = 0; tp = (void*)sh_timeradd(1000*sec, 0, completed, (void*)&expired); do { - if(!sh.waitevent || (*sh.waitevent)(-1,-1L,0)==0) + if(!shp->waitevent || (*shp->waitevent)(-1,-1L,0)==0) pause(); - if(sh.sigflag[SIGALRM]&SH_SIGTRAP) + if(shp->sigflag[SIGALRM]&SH_SIGTRAP) sh_timetraps(); if((newpid=getpid()) != curpid) { curpid = newpid; - sh.lastsig = 0; - sh.trapnote &= ~SH_SIGSET; + shp->lastsig = 0; + shp->trapnote &= ~SH_SIGSET; if(expired) expired = 0; else @@ -115,7 +117,7 @@ unsigned int sleep(unsigned int sec) tp = (void*)sh_timeradd(1000*sec, 0, completed, (void*)&expired); } } - while(!expired && sh.lastsig==0); + while(!expired && shp->lastsig==0); if(!expired) timerdel(tp); sh_sigcheck(); @@ -129,6 +131,7 @@ unsigned int sleep(unsigned int sec) void sh_delay(double t) { register int n = (int)t; + Shell_t *shp = &sh; #ifdef _lib_poll struct pollfd fd; if(t<=0) @@ -140,7 +143,7 @@ void sh_delay(double t) } if(n=(int)(1000*t)) { - if(!sh.waitevent || (*sh.waitevent)(-1,(long)n,0)==0) + if(!shp->waitevent || (*shp->waitevent)(-1,(long)n,0)==0) poll(&fd,0,n); } #else @@ -148,7 +151,7 @@ void sh_delay(double t) struct timeval timeloc; if(t<=0) return; - if(n=(int)(1000*t) && sh.waitevent && (*sh.waitevent)(-1,(long)n,0)) + if(n=(int)(1000*t) && shp->waitevent && (*shp->waitevent)(-1,(long)n,0)) return; n = (int)t; timeloc.tv_sec = n; @@ -166,7 +169,7 @@ void sh_delay(double t) } if(n=(int)(1000*t)) { - if(!sh.waitevent || (*sh.waitevent)(-1,(long)n,0)==0) + if(!shp->waitevent || (*shp->waitevent)(-1,(long)n,0)==0) select(0,(fd_set*)0,(fd_set*)0,n); } # else @@ -180,7 +183,7 @@ void sh_delay(double t) clock_t begin = times(&tt); if(begin==0) return; - t *= sh.lim.clk_tck; + t *= shp->lim.clk_tck; n += (t+.5); while((times(&tt)-begin) < n); } diff --git a/usr/src/lib/libshell/common/bltins/test.c b/usr/src/lib/libshell/common/bltins/test.c index f21db8a75c..644f54eb1c 100644 --- a/usr/src/lib/libshell/common/bltins/test.c +++ b/usr/src/lib/libshell/common/bltins/test.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -113,7 +113,7 @@ int b_test(int argc, char *argv[],void *extra) struct test tdata; register char *cp = argv[0]; register int not; - tdata.sh = (Shell_t*)extra; + tdata.sh = ((Shbltin_t*)extra)->shp; tdata.av = argv; tdata.ap = 1; if(c_eq(cp,'[')) @@ -125,6 +125,15 @@ int b_test(int argc, char *argv[],void *extra) if(argc <= 1) return(1); cp = argv[1]; + if(c_eq(cp,'(') && argc<=6 && c_eq(argv[argc-1],')')) + { + /* special case ( binop ) to conform with standard */ + if(!(argc==4 && (not=sh_lookup(cp=argv[2],shtab_testops)))) + { + cp = (++argv)[1]; + argc -= 2; + } + } not = c_eq(cp,'!'); /* posix portion for test */ switch(argc) @@ -173,8 +182,6 @@ int b_test(int argc, char *argv[],void *extra) case 2: return(*cp==0); } - if(argc==5) - argv--; tdata.ac = argc; return(!expr(&tdata,0)); } diff --git a/usr/src/lib/libshell/common/bltins/trap.c b/usr/src/lib/libshell/common/bltins/trap.c index 7197144d5e..a298a35742 100644 --- a/usr/src/lib/libshell/common/bltins/trap.c +++ b/usr/src/lib/libshell/common/bltins/trap.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -45,8 +45,8 @@ static void sig_list(Shell_t*,int); int b_trap(int argc,char *argv[],void *extra) { register char *arg = argv[1]; - register int sig, pflag = 0; - register Shell_t *shp = (Shell_t*)extra; + register int sig, clear = 0, dflag = 0, pflag = 0; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; NOT_USED(argc); while (sig = optget(argv, sh_opttrap)) switch (sig) { @@ -66,9 +66,8 @@ int b_trap(int argc,char *argv[],void *extra) errormsg(SH_DICT,ERROR_usage(2),"%s", optusage((char*)0)); if(arg = *argv) { - register int clear; char *action = arg; - if(!pflag) + if(!dflag && !pflag) { /* first argument all digits or - means clear */ while(isdigit(*arg)) @@ -79,8 +78,18 @@ int b_trap(int argc,char *argv[],void *extra) ++argv; if(*action=='-' && action[1]==0) clear++; + /* + * NOTE: 2007-11-26: workaround for tests/signal.sh + * if function semantics can be worked out then it + * may merit a -d,--default option + */ + else if(*action=='+' && action[1]==0 && sh.st.self == &sh.global) + { + clear++; + dflag++; + } } - while(!argv[0]) + if(!argv[0]) errormsg(SH_DICT,ERROR_exit(1),e_condition); } while(arg = *argv++) @@ -132,7 +141,11 @@ int b_trap(int argc,char *argv[],void *extra) sfputr(sfstdout,arg,'\n'); } else if(clear) + { sh_sigclear(sig); + if(dflag) + signal(sig,SIG_DFL); + } else { if(sig >= shp->st.trapmax) @@ -153,7 +166,7 @@ int b_kill(int argc,char *argv[],void *extra) { register char *signame; register int sig=SIGTERM, flag=0, n; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; NOT_USED(argc); while((n = optget(argv,sh_optkill))) switch(n) { @@ -222,8 +235,8 @@ endopts: static int sig_number(const char *string) { const Shtable_t *tp; - register int n,sig=0; - char *last; + register int n,o,sig=0; + char *last, *name; if(isdigit(*string)) { n = strtol(string,&last,10); @@ -233,7 +246,7 @@ static int sig_number(const char *string) else { register int c; - n = staktell(); + o = staktell(); do { c = *string++; @@ -242,30 +255,92 @@ static int sig_number(const char *string) stakputc(c); } while(c); - stakseek(n); - if(memcmp(stakptr(n),"SIG",3)==0) + stakseek(o); + if(memcmp(stakptr(o),"SIG",3)==0) { sig = 1; - n += 3; + o += 3; } - tp = sh_locate(stakptr(n),(const Shtable_t*)shtab_signals,sizeof(*shtab_signals)); + tp = sh_locate(stakptr(o),(const Shtable_t*)shtab_signals,sizeof(*shtab_signals)); n = tp->sh_number; if(sig==1 && (n>=(SH_TRAP-1) && n < (1<<SH_SIGBITS))) { /* sig prefix cannot match internal traps */ n = 0; tp = (Shtable_t*)((char*)tp + sizeof(*shtab_signals)); - if(strcmp(stakptr(n),tp->sh_name)==0) + if(strcmp(stakptr(o),tp->sh_name)==0) n = tp->sh_number; } - n &= (1<<SH_SIGBITS)-1; - if(n < SH_TRAP) - n--; + if((n>>SH_SIGBITS)&SH_SIGRUNTIME) + n = sh.sigruntime[(n&((1<<SH_SIGBITS)-1))-1]; + else + { + n &= (1<<SH_SIGBITS)-1; + if(n < SH_TRAP) + n--; + } + if(n<0 && (name=stakptr(o)) && *name++=='R' && *name++=='T') + { + if(name[0]=='M' && name[1]=='I' && name[2]=='N' && name[3]=='+') + { + if((sig=(int)strtol(name+4,&name,10)) >= 0 && !*name) + n = sh.sigruntime[SH_SIGRTMIN] + sig; + } + else if(name[0]=='M' && name[1]=='A' && name[2]=='X' && name[3]=='-') + { + if((sig=(int)strtol(name+4,&name,10)) >= 0 && !*name) + n = sh.sigruntime[SH_SIGRTMAX] - sig; + } + else if((sig=(int)strtol(name,&name,10)) > 0 && !*name) + n = sh.sigruntime[SH_SIGRTMIN] + sig - 1; + if(n<sh.sigruntime[SH_SIGRTMIN] || n>sh.sigruntime[SH_SIGRTMAX]) + n = -1; + } } return(n); } /* + * synthesize signal name for sig in buf + * pfx!=0 prepends SIG to default signal number + */ +static char* sig_name(int sig, char* buf, int pfx) +{ + register int i; + + i = 0; + if(sig>sh.sigruntime[SH_SIGRTMIN] && sig<sh.sigruntime[SH_SIGRTMAX]) + { + buf[i++] = 'R'; + buf[i++] = 'T'; + buf[i++] = 'M'; + if(sig>sh.sigruntime[SH_SIGRTMIN]+(sh.sigruntime[SH_SIGRTMAX]-sh.sigruntime[SH_SIGRTMIN])/2) + { + buf[i++] = 'A'; + buf[i++] = 'X'; + buf[i++] = '-'; + sig = sh.sigruntime[SH_SIGRTMAX]-sig; + } + else + { + buf[i++] = 'I'; + buf[i++] = 'N'; + buf[i++] = '+'; + sig = sig-sh.sigruntime[SH_SIGRTMIN]; + } + } + else if(pfx) + { + buf[i++] = 'S'; + buf[i++] = 'I'; + buf[i++] = 'G'; + } + i += sfsprintf(buf+i, 8, "%d", sig); + buf[i] = 0; + return buf; +} + +/* * if <flag> is positive, then print signal name corresponding to <flag> * if <flag> is zero, then print all signal names * if <flag> is negative, then print all traps @@ -274,10 +349,12 @@ static void sig_list(register Shell_t *shp,register int flag) { register const struct shtable2 *tp; register int sig = shp->sigmax+1; + register char *sname; + char name[10]; const char *names[SH_TRAP]; const char *traps[SH_DEBUGTRAP+1]; tp=shtab_signals; - if(flag==0) + if(flag<=0) { /* not all signals may be defined, so initialize */ while(--sig >= 0) @@ -287,8 +364,9 @@ static void sig_list(register Shell_t *shp,register int flag) } while(*tp->sh_name) { - sig = tp->sh_number; - sig &= ((1<<SH_SIGBITS)-1); + sig = tp->sh_number&((1<<SH_SIGBITS)-1); + if ((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME) + sig = sh.sigruntime[sig-1]+1; if(sig==flag) { sfprintf(sfstdout,"%s\n",tp->sh_name); @@ -301,12 +379,11 @@ static void sig_list(register Shell_t *shp,register int flag) tp++; } if(flag > 0) - sfprintf(sfstdout,"%d\n",flag-1); + sfputr(sfstdout, sig_name(flag-1,name,0), '\n'); else if(flag<0) { /* print the traps */ - register char *trap,*sname,**trapcom; - char name[6]; + register char *trap,**trapcom; sig = shp->st.trapmax; /* use parent traps if otrapcom is set (for $(trap) */ trapcom = (shp->st.otrapcom?shp->st.otrapcom:shp->st.trapcom); @@ -315,14 +392,7 @@ static void sig_list(register Shell_t *shp,register int flag) if(!(trap=trapcom[sig])) continue; if(!(sname=(char*)names[sig+1])) - { - sname = name; - sname[0] = 'S'; - sname[1] = 'I'; - sname[2] = 'G'; - sname[3] = (sig/10)+'0'; - sname[4] = (sig%10)+'0'; - } + sname = sig_name(sig,name,1); sfprintf(sfstdout,trapfmt,sh_fmtq(trap),sname); } for(sig=SH_DEBUGTRAP; sig>=0; sig--) @@ -337,11 +407,9 @@ static void sig_list(register Shell_t *shp,register int flag) /* print all the signal names */ for(sig=2; sig <= shp->sigmax; sig++) { - if(names[sig]) - sfputr(sfstdout,names[sig],'\n'); - else - sfprintf(sfstdout,"SIG%d\n",sig-1); + if(!(sname=(char*)names[sig+1])) + sname = sig_name(sig,name,1); + sfputr(sfstdout,sname,'\n'); } } } - diff --git a/usr/src/lib/libshell/common/bltins/typeset.c b/usr/src/lib/libshell/common/bltins/typeset.c index 14b02a6ad3..734651fd3a 100644 --- a/usr/src/lib/libshell/common/bltins/typeset.c +++ b/usr/src/lib/libshell/common/bltins/typeset.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -40,7 +40,7 @@ #include "history.h" #include "builtins.h" #include "variables.h" -#include <dlldefs.h> +#include "FEATURE/dynamic" struct tdata { @@ -48,7 +48,10 @@ struct tdata Namval_t *tp; Sfio_t *outfile; char *prefix; - int aflag; + char *tname; + char *help; + short aflag; + short pflag; int argnum; int scanmask; Dt_t *scanroot; @@ -59,7 +62,7 @@ struct tdata static int print_namval(Sfio_t*, Namval_t*, int, struct tdata*); static void print_attribute(Namval_t*,void*); static void print_all(Sfio_t*, Dt_t*, struct tdata*); -static void print_scan(Sfio_t*, int, Dt_t*, int, struct tdata*t); +static void print_scan(Sfio_t*, int, Dt_t*, int, struct tdata*); static int b_unall(int, char**, Dt_t*, Shell_t*); static int b_common(char**, int, Dt_t*, struct tdata*); static void pushname(Namval_t*,void*); @@ -85,7 +88,7 @@ int b_readonly(int argc,char *argv[],void *extra) struct tdata tdata; NOT_USED(argc); memset((void*)&tdata,0,sizeof(tdata)); - tdata.sh = (Shell_t*)extra; + tdata.sh = ((Shbltin_t*)extra)->shp; tdata.aflag = '-'; while((flag = optget(argv,*command=='e'?sh_optexport:sh_optreadonly))) switch(flag) { @@ -120,8 +123,8 @@ int b_readonly(int argc,char *argv[],void *extra) else { flag = (NV_ASSIGN|NV_EXPORT|NV_IDENT); - if(!sh.prefix) - sh.prefix = ""; + if(!tdata.sh->prefix) + tdata.sh->prefix = ""; } return(b_common(argv,flag,tdata.sh->var_tree, &tdata)); } @@ -135,7 +138,7 @@ int b_alias(int argc,register char *argv[],void *extra) struct tdata tdata; NOT_USED(argc); memset((void*)&tdata,0,sizeof(tdata)); - tdata.sh = (Shell_t*)extra; + tdata.sh = ((Shbltin_t*)extra)->shp; troot = tdata.sh->alias_tree; if(*argv[0]=='h') flag = NV_TAGGED; @@ -169,11 +172,23 @@ int b_alias(int argc,register char *argv[],void *extra) argv += (opt_info.index-1); if(flag&NV_TAGGED) { - if(argv[1] && strcmp(argv[1],"-r")==0) + /* hacks to handle hash -r | -- */ + if(argv[1] && argv[1][0]=='-') { - /* hack to handle hash -r */ - nv_putval(PATHNOD,nv_getval(PATHNOD),NV_RDONLY); - return(0); + if(argv[1][1]=='r' && argv[1][2]==0) + { + nv_putval(PATHNOD,nv_getval(PATHNOD),NV_RDONLY); + argv++; + if(!argv[1]) + return(0); + } + if(argv[1][0]=='-') + { + if(argv[1][1]=='-' && argv[1][2]==0) + argv++; + else + errormsg(SH_DICT, ERROR_exit(1), e_option, argv[1]); + } } troot = tdata.sh->track_tree; } @@ -188,28 +203,41 @@ int b_alias(int argc,register char *argv[],void *extra) #endif int b_typeset(int argc,register char *argv[],void *extra) { - register int flag = NV_VARNAME|NV_ASSIGN; - register int n; - struct tdata tdata; - Namtype_t *ntp = (Namtype_t*)extra; - Dt_t *troot; - int isfloat=0, shortint=0; + register int n, flag = NV_VARNAME|NV_ASSIGN; + struct tdata tdata; + const char *optstring = sh_opttypeset; + Namdecl_t *ntp = (Namdecl_t*)((Shbltin_t*)extra)->ptr; + Dt_t *troot; + int isfloat=0, shortint=0, sflag=0; NOT_USED(argc); memset((void*)&tdata,0,sizeof(tdata)); - tdata.sh = ntp->shp; - tdata.tp = ntp->np; + tdata.sh = ((Shbltin_t*)extra)->shp; + if(ntp) + { + tdata.tp = ntp->tp; + opt_info.disc = (Optdisc_t*)ntp->optinfof; + optstring = ntp->optstring; + } troot = tdata.sh->var_tree; - opt_info.disc = (Optdisc_t*)ntp->optinfof; - while((n = optget(argv,ntp->optstring))) + while((n = optget(argv,optstring))) { switch(n) { case 'a': flag |= NV_IARRAY; + if(opt_info.arg && *opt_info.arg!='[') + { + opt_info.index--; + goto endargs; + } + tdata.tname = opt_info.arg; break; case 'A': flag |= NV_ARRAY; break; + case 'C': + flag |= NV_COMVAR; + break; case 'E': /* The following is for ksh88 compatibility */ if(opt_info.offset && !strchr(argv[opt_info.index],'E')) @@ -218,15 +246,27 @@ int b_typeset(int argc,register char *argv[],void *extra) break; } case 'F': + case 'X': if(!opt_info.arg || (tdata.argnum = opt_info.num) <0) tdata.argnum = 10; isfloat = 1; if(n=='E') + { + flag &= ~NV_HEXFLOAT; flag |= NV_EXPNOTE; + } + else if(n=='X') + { + flag &= ~NV_EXPNOTE; + flag |= NV_HEXFLOAT; + } break; case 'b': flag |= NV_BINARY; break; + case 'm': + flag |= NV_MOVE; + break; case 'n': flag &= ~NV_VARNAME; flag |= (NV_REF|NV_IDENT); @@ -238,24 +278,18 @@ int b_typeset(int argc,register char *argv[],void *extra) flag |= NV_TYPE; tdata.prefix = opt_info.arg; break; - case 'L': - if(tdata.argnum==0) - tdata.argnum = (int)opt_info.num; - if(tdata.argnum < 0) - errormsg(SH_DICT,ERROR_exit(1), e_badfield, tdata.argnum); - flag &= ~NV_RJUST; - flag |= NV_LJUST; - break; - case 'Z': - flag |= NV_ZFILL; - /* FALL THRU*/ - case 'R': + case 'L': case 'Z': case 'R': if(tdata.argnum==0) tdata.argnum = (int)opt_info.num; if(tdata.argnum < 0) errormsg(SH_DICT,ERROR_exit(1), e_badfield, tdata.argnum); - flag &= ~NV_LJUST; - flag |= NV_RJUST; + if(n=='Z') + flag |= NV_ZFILL; + else + { + flag &= ~(NV_LJUST|NV_RJUST); + flag |= (n=='L'?NV_LJUST:NV_RJUST); + } break; case 'f': flag &= ~(NV_VARNAME|NV_ASSIGN); @@ -271,10 +305,19 @@ int b_typeset(int argc,register char *argv[],void *extra) break; case 'p': tdata.prefix = argv[0]; - continue; + tdata.pflag = 1; + break; case 'r': flag |= NV_RDONLY; break; +#ifdef SHOPT_TYPEDEF + case 'S': + sflag=1; + break; + case 'h': + tdata.help = opt_info.arg; + break; +#endif /*SHOPT_TYPEDEF*/ case 's': shortint=1; break; @@ -299,6 +342,7 @@ int b_typeset(int argc,register char *argv[],void *extra) if(tdata.aflag==0) tdata.aflag = *opt_info.option; } +endargs: argv += opt_info.index; opt_info.disc = 0; /* handle argument of + and - specially */ @@ -306,51 +350,95 @@ int b_typeset(int argc,register char *argv[],void *extra) tdata.aflag = *argv[0]; else argv--; + if((flag&NV_ZFILL) && !(flag&NV_LJUST)) + flag |= NV_RJUST; if((flag&NV_INTEGER) && (flag&(NV_LJUST|NV_RJUST|NV_ZFILL))) error_info.errors++; if((flag&NV_BINARY) && (flag&(NV_LJUST|NV_UTOL|NV_LTOU))) error_info.errors++; + if((flag&NV_MOVE) && (flag&~(NV_MOVE|NV_VARNAME|NV_ASSIGN))) + error_info.errors++; + if((flag&NV_REF) && (flag&~(NV_REF|NV_IDENT|NV_ASSIGN))) + error_info.errors++; if(troot==tdata.sh->fun_tree && ((isfloat || flag&~(NV_FUNCT|NV_TAGGED|NV_EXPORT|NV_LTOU)))) error_info.errors++; if(error_info.errors) errormsg(SH_DICT,ERROR_usage(2),"%s", optusage(NIL(char*))); if(isfloat) - flag |= NV_INTEGER|NV_DOUBLE; + flag |= NV_DOUBLE; if(shortint) flag |= NV_SHORT|NV_INTEGER; - if(tdata.sh->fn_depth) + if(sflag) + { + if(tdata.sh->mktype) + flag |= NV_REF|NV_TAGGED; + else if(!tdata.sh->typeinit) + flag |= NV_STATIC|NV_IDENT; + } + if(tdata.sh->fn_depth && !tdata.pflag) flag |= NV_NOSCOPE; if(flag&NV_TYPE) { - int offset = staktell(); - stakputs(NV_CLASS); + Stk_t *stkp = tdata.sh->stk; + int offset = stktell(stkp); + sfputr(stkp,NV_CLASS,-1); if(NV_CLASS[sizeof(NV_CLASS)-2]!='.') - stakputc('.'); - stakputs(tdata.prefix); - stakputc(0); - tdata.tp = nv_open(stakptr(offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN); - stakseek(offset); + sfputc(stkp,'.'); + sfputr(stkp,tdata.prefix,0); + tdata.tp = nv_open(stkptr(stkp,offset),tdata.sh->var_tree,NV_VARNAME|NV_NOARRAY|NV_NOASSIGN); + stkseek(stkp,offset); if(!tdata.tp) errormsg(SH_DICT,ERROR_exit(1),"%s: unknown type",tdata.prefix); + tdata.tp->nvenv = tdata.help; flag &= ~NV_TYPE; } - else if(tdata.aflag==0 && ntp->np) + else if(tdata.aflag==0 && ntp && ntp->tp) tdata.aflag = '-'; + if(!tdata.sh->mktype) + tdata.help = 0; return(b_common(argv,flag,troot,&tdata)); } +static void print_value(Sfio_t *iop, Namval_t *np, struct tdata *tp) +{ + char *name; + if(nv_isnull(np)) + return; + sfputr(iop,nv_name(np),tp->aflag=='+'?'\n':'='); + if(tp->aflag=='+') + return; + if(nv_isarray(np) && nv_arrayptr(np)) + { + nv_outnode(np,iop,-1,0); + sfwrite(iop,")\n",2); + } + else + { + if(nv_isvtree(np)) + nv_onattr(np,NV_EXPORT); + if(!(name = nv_getval(np))) + name = Empty; + if(!nv_isvtree(np)) + name = sh_fmtq(name); + sfputr(iop,name,'\n'); + } +} + static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata *tp) { register char *name; char *last = 0; - int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN)); - int r=0, ref=0; + int nvflags=(flag&(NV_ARRAY|NV_NOARRAY|NV_VARNAME|NV_IDENT|NV_ASSIGN|NV_STATIC|NV_MOVE)); + int r=0, ref=0, comvar=(flag&NV_COMVAR),iarray=(flag&NV_IARRAY); Shell_t *shp =tp->sh; - if(!sh.prefix) - nvflags |= NV_NOSCOPE; - else if(*sh.prefix==0) - sh.prefix = 0; - flag &= ~(NV_NOARRAY|NV_NOSCOPE|NV_VARNAME|NV_IDENT); + if(!shp->prefix) + { + if(!tp->pflag) + nvflags |= NV_NOSCOPE; + } + else if(*shp->prefix==0) + shp->prefix = 0; + flag &= ~(NV_NOARRAY|NV_NOSCOPE|NV_VARNAME|NV_IDENT|NV_STATIC|NV_COMVAR|NV_IARRAY); if(argv[1]) { if(flag&NV_REF) @@ -360,6 +448,8 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * if(tp->aflag!='-') nvflags |= NV_NOREF; } + if(tp->pflag) + nvflags |= NV_NOREF; while(name = *++argv) { register unsigned newflag; @@ -377,10 +467,10 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * /* Function names cannot be special builtin */ if((np=nv_search(name,shp->bltin_tree,0)) && nv_isattr(np,BLT_SPC)) errormsg(SH_DICT,ERROR_exit(1),e_badfun,name); - np = nv_open(name,shp->fun_tree,NV_NOARRAY|NV_IDENT|NV_NOSCOPE); + np = nv_open(name,sh_subfuntree(1),NV_NOARRAY|NV_IDENT|NV_NOSCOPE); } - else - np = nv_search(name,shp->fun_tree,HASH_NOSCOPE); + else if((np=nv_search(name,troot,0)) && !is_afunction(np)) + np = 0; if(np && ((flag&NV_LTOU) || !nv_isnull(np) || nv_isattr(np,NV_LTOU))) { if(flag==0) @@ -397,18 +487,29 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * } else r++; + if(tp->help) + { + int offset = stktell(shp->stk); + sfputr(shp->stk,shp->prefix,'.'); + sfputr(shp->stk,name,0); + if((np=nv_search(stkptr(shp->stk,offset),troot,0)) && np->nvalue.cp) + np->nvalue.rp->help = tp->help; + stkseek(shp->stk,offset); + } continue; } - np = nv_open(name,troot,nvflags); /* tracked alias */ if(troot==shp->track_tree && tp->aflag=='-') { -#ifdef PATH_BFPATH + np = nv_search(name,troot,NV_ADD); path_alias(np,path_absolute(nv_name(np),NIL(Pathcomp_t*))); -#else - nv_onattr(np,NV_NOALIAS); - path_alias(np,path_absolute(nv_name(np),NIL(char*))); -#endif + continue; + } + np = nv_open(name,troot,nvflags|NV_ARRAY); + if(tp->pflag) + { + nv_attribute(np,sfstdout,tp->prefix,1); + print_value(sfstdout,np,tp); continue; } if(flag==NV_ASSIGN && !ref && tp->aflag!='-' && !strchr(name,'=')) @@ -418,6 +519,38 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * sfprintf(sfstderr,sh_translate(e_noalias),name); r++; } + if(!comvar && !iarray) + continue; + } + if(troot==shp->var_tree && ((tp->tp && !nv_isarray(np)) || !shp->st.real_fun && (nvflags&NV_STATIC)) && !strchr(name,'=') && !(shp->envlist && nv_onlist(shp->envlist,name))) + _nv_unset(np,0); + if(troot==shp->var_tree) + { + if(iarray) + { + if(tp->tname) + nv_atypeindex(np,tp->tname+1); + else if(nv_isnull(np)) + nv_onattr(np,NV_ARRAY|(comvar?NV_NOFREE:0)); + else + nv_putsub(np, (char*)0, 0); + } + else if(nvflags&NV_ARRAY) + { + if(comvar) + { + _nv_unset(np,NV_RDONLY); + nv_onattr(np,NV_NOFREE); + } + nv_setarray(np,nv_associative); + } + else if(comvar && !nv_rename(np,flag|NV_COMVAR)) + nv_setvtree(np); + } + if(flag&NV_MOVE) + { + nv_rename(np, flag); + nv_close(np); continue; } if(tp->tp) @@ -425,16 +558,6 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * nv_settype(np,tp->tp,tp->aflag=='-'?0:NV_APPEND); flag = (np->nvflag&NV_NOCHANGE); } - if(troot==shp->var_tree && (flag&NV_IARRAY)) - { - flag &= ~NV_IARRAY; - if(nv_isnull(np)) - nv_onattr(np,NV_ARRAY); - else - nv_putsub(np, (char*)0, 0); - } - if(troot==shp->var_tree && (nvflags&NV_ARRAY)) - nv_setarray(np,nv_associative); curflag = np->nvflag; flag &= ~NV_ASSIGN; if(last=strchr(name,'=')) @@ -459,10 +582,13 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * else if(!(flag&NV_LJUST)) newflag &= ~NV_LJUST; } - if (flag & NV_UTOL) - newflag &= ~NV_LTOU; - else if (flag & NV_LTOU) - newflag &= ~NV_UTOL; + if(!(flag&NV_INTEGER)) + { + if (flag & NV_UTOL) + newflag &= ~NV_LTOU; + else if (flag & NV_LTOU) + newflag &= ~NV_UTOL; + } } else { @@ -479,6 +605,7 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * else { char *oldname=0; + int len=strlen(name); if(tp->argnum==1 && newflag==NV_INTEGER && nv_isattr(np,NV_INTEGER)) tp->argnum = 10; /* use reference name for export */ @@ -487,11 +614,18 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * oldname = np->nvname; np->nvname = name; } + if(np->nvfun && !nv_isarray(np) && name[len-1]=='.') + newflag |= NV_NODISC; nv_newattr (np, newflag&~NV_ASSIGN,tp->argnum); if(oldname) np->nvname = oldname; } } + if(tp->help && !nv_isattr(np,NV_MINIMAL|NV_EXPORT)) + { + np->nvenv = tp->help; + nv_onattr(np,NV_EXPORT); + } if(last) *last = '='; /* set or unset references */ @@ -505,7 +639,10 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * if(!(hp=(Dt_t*)shp->st.prevst->save_tree)) hp = dtvnext(shp->var_tree); } - nv_setref(np,hp,NV_VARNAME); + if(tp->sh->mktype) + nv_onattr(np,NV_REF|NV_FUNCT); + else + nv_setref(np,hp,NV_VARNAME); } else nv_unref(np); @@ -513,8 +650,10 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * nv_close(np); } } - else if(!sh.envlist) + else if(!tp->sh->envlist) { + if(shp->prefix) + errormsg(SH_DICT,2, "%s: compound assignment requires sub-variable name",shp->prefix); if(tp->aflag) { if(troot==shp->fun_tree) @@ -523,7 +662,11 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * tp->prefix = 0; } else if(troot==shp->var_tree) + { flag |= (nvflags&NV_ARRAY); + if(flag&NV_IARRAY) + flag |= NV_ARRAY; + } print_scan(sfstdout,flag,troot,tp->aflag=='+',tp); } else if(troot==shp->alias_tree) @@ -535,14 +678,15 @@ static int b_common(char **argv,register int flag,Dt_t *troot,struct tdata * return(r); } -typedef void (*Iptr_t)(int); +typedef void (*Iptr_t)(int,void*); typedef int (*Fptr_t)(int, char*[], void*); #define GROWLIB 4 -static void** liblist; -static int nlib; -static int maxlib; +static void **liblist; +static unsigned short *libattr; +static int nlib; +static int maxlib; /* * This allows external routines to load from the same library */ @@ -557,35 +701,54 @@ void **sh_getliblist(void) * always move to head of search list * return: 0: already loaded 1: first load */ +#if SHOPT_DYNAMIC int sh_addlib(void* library) { register int n; register int r; Iptr_t initfn; + Shbltin_t *sp = &sh.bltindata; + sp->nosfio = 0; for (n = r = 0; n < nlib; n++) { if (r) + { liblist[n-1] = liblist[n]; + libattr[n-1] = libattr[n]; + } else if (liblist[n] == library) r++; } if (r) nlib--; else if ((initfn = (Iptr_t)dlllook(library, "lib_init"))) - (*initfn)(0); + (*initfn)(0,sp); if (nlib >= maxlib) { maxlib += GROWLIB; if (liblist) + { liblist = (void**)realloc((void*)liblist, (maxlib+1)*sizeof(void**)); + libattr = (unsigned short*)realloc((void*)liblist, (maxlib+1)*sizeof(unsigned short*)); + } else + { liblist = (void**)malloc((maxlib+1)*sizeof(void**)); + libattr = (unsigned short*)malloc((maxlib+1)*sizeof(unsigned short*)); + } } + libattr[nlib] = (sp->nosfio?BLT_NOSFIO:0); liblist[nlib++] = library; liblist[nlib] = 0; return !r; } +#else +int sh_addlib(void* library) +{ + return 0; +} +#endif /* SHOPT_DYNAMIC */ /* * add change or list built-ins @@ -599,10 +762,13 @@ int b_builtin(int argc,char *argv[],void *extra) long dlete=0; struct tdata tdata; Fptr_t addr; + Stk_t *stkp; void *library=0; char *errmsg; NOT_USED(argc); - tdata.sh = (Shell_t*)extra; + memset(&tdata,0,sizeof(tdata)); + tdata.sh = ((Shbltin_t*)extra)->shp; + stkp = tdata.sh->stk; while (n = optget(argv,sh_optbuiltin)) switch (n) { case 's': @@ -638,46 +804,51 @@ int b_builtin(int argc,char *argv[],void *extra) if(tdata.sh->subshell) sh_subfork(); } +#if SHOPT_DYNAMIC if(arg) { -#ifdef _hdr_dlldefs #if (_AST_VERSION>=20040404) if(!(library = dllplug(SH_ID,arg,NIL(char*),RTLD_LAZY,NIL(char*),0))) #else if(!(library = dllfind(arg,NIL(char*),RTLD_LAZY,NIL(char*),0))) #endif -#else - if(!(library = dlopen(arg,DL_MODE))) -#endif { errormsg(SH_DICT,ERROR_exit(0),"%s: %s",arg,dlerror()); return(1); } sh_addlib(library); } - else if(*argv==0 && !dlete) + else +#endif /* SHOPT_DYNAMIC */ + if(*argv==0 && !dlete) { print_scan(sfstdout, flag, tdata.sh->bltin_tree, 1, &tdata); return(0); } r = 0; - flag = staktell(); + flag = stktell(stkp); while(arg = *argv) { name = path_basename(arg); - stakputs("b_"); - stakputs(name); + sfwrite(stkp,"b_",2); + sfputr(stkp,name,0); errmsg = 0; addr = 0; for(n=(nlib?nlib:dlete); --n>=0;) { /* (char*) added for some sgi-mips compilers */ - if(dlete || (addr = (Fptr_t)dlllook(liblist[n],stakptr(flag)))) +#if SHOPT_DYNAMIC + if(dlete || (addr = (Fptr_t)dlllook(liblist[n],stkptr(stkp,flag)))) +#else + if(dlete) +#endif /* SHOPT_DYNAMIC */ { if(np = sh_addbuiltin(arg, addr,pointerof(dlete))) { if(dlete || nv_isattr(np,BLT_SPC)) errmsg = "restricted name"; + else + nv_onattr(np,libattr[n]); } break; } @@ -695,7 +866,7 @@ int b_builtin(int argc,char *argv[],void *extra) errormsg(SH_DICT,ERROR_exit(0),"%s: %s",*argv,errmsg); r = 1; } - stakseek(flag); + stkseek(stkp,flag); argv++; } return(r); @@ -705,11 +876,11 @@ int b_set(int argc,register char *argv[],void *extra) { struct tdata tdata; memset(&tdata,0,sizeof(tdata)); - tdata.sh = (Shell_t*)extra; + tdata.sh = ((Shbltin_t*)extra)->shp; tdata.prefix=0; if(argv[1]) { - if(sh_argopts(argc,argv) < 0) + if(sh_argopts(argc,argv,tdata.sh) < 0) return(2); if(sh_isoption(SH_VERBOSE)) sh_onstate(SH_VERBOSE); @@ -735,13 +906,13 @@ int b_set(int argc,register char *argv[],void *extra) int b_unalias(int argc,register char *argv[],void *extra) { - Shell_t *shp = (Shell_t*)extra; + Shell_t *shp = ((Shbltin_t*)extra)->shp; return(b_unall(argc,argv,shp->alias_tree,shp)); } int b_unset(int argc,register char *argv[],void *extra) { - Shell_t *shp = (Shell_t*)extra; + Shell_t *shp = ((Shbltin_t*)extra)->shp; return(b_unall(argc,argv,shp->var_tree,shp)); } @@ -750,6 +921,7 @@ static int b_unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp) register Namval_t *np; register const char *name; register int r; + Dt_t *dp; int nflag=0,all=0,isfun; NOT_USED(argc); if(troot==shp->alias_tree) @@ -763,7 +935,7 @@ static int b_unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp) while(r = optget(argv,name)) switch(r) { case 'f': - troot = sh_subfuntree(0); + troot = sh_subfuntree(1); break; case 'a': all=1; @@ -794,7 +966,7 @@ static int b_unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp) dtclear(troot); else while(name = *argv++) { - if(np=nv_open(name,troot,NV_NOADD|nflag)) + if(np=nv_open(name,troot,NV_NOADD|NV_NOFAIL|nflag)) { if(is_abuiltin(np)) { @@ -802,12 +974,23 @@ static int b_unall(int argc, char **argv, register Dt_t *troot, Shell_t* shp) continue; } isfun = is_afunction(np); - if(shp->subshell && troot==shp->var_tree) - np=sh_assignok(np,0); + if(troot==shp->var_tree) + { + if(nv_isarray(np) && name[strlen(name)-1]==']' && !nv_getsub(np)) + { + r=1; + continue; + } + + if(shp->subshell) + np=sh_assignok(np,0); + } nv_unset(np); nv_close(np); - if(isfun) - dtdelete(troot,np); + if(troot==shp->var_tree && shp->st.real_fun && (dp=shp->var_tree->walk) && dp==shp->st.real_fun->sdict) + nv_delete(np,dp,NV_NOFREE); + else if(isfun) + nv_delete(np,troot,NV_NOFREE); } else r = 1; @@ -825,14 +1008,19 @@ static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, st sh_sigcheck(); if(flag) flag = '\n'; - if(nv_isattr(np,NV_NOPRINT)==NV_NOPRINT) + if(nv_isattr(np,NV_NOPRINT|NV_INTEGER)==NV_NOPRINT) { if(is_abuiltin(np)) sfputr(file,nv_name(np),'\n'); return(0); } if(tp->prefix) - sfputr(file,tp->prefix,' '); + { + if(*tp->prefix=='t') + nv_attribute(np,tp->outfile,tp->prefix,tp->aflag); + else + sfputr(file,tp->prefix,' '); + } if(is_afunction(np)) { Sfio_t *iop=0; @@ -850,7 +1038,7 @@ static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, st flag = '\n'; if(flag) { - if(np->nvalue.ip && np->nvalue.rp->hoffset>=0) + if(tp->pflag && np->nvalue.ip && np->nvalue.rp->hoffset>=0) sfprintf(file," #line %d %s\n",np->nvalue.rp->lineno,fname?sh_fmtq(fname):""); else sfputc(file, '\n'); @@ -875,15 +1063,18 @@ static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, st } return(nv_size(np)+1); } + if(nv_arrayptr(np)) + { + print_value(file,np,tp); + return(0); + } + if(nv_isvtree(np)) + nv_onattr(np,NV_EXPORT); if(cp=nv_getval(np)) { sfputr(file,nv_name(np),-1); if(!flag) - { flag = '='; - if(nv_arrayptr(np)) - sfprintf(file,"[%s]", sh_fmtq(nv_getsub(np))); - } sfputc(file,flag); if(flag != '\n') { @@ -893,7 +1084,11 @@ static int print_namval(Sfio_t *file,register Namval_t *np,register int flag, st sfprintf(file,"[%s]\n", sh_fmtq(nv_refsub(np))); } else +#if SHOPT_TYPEDEF + sfputr(file,nv_isvtree(np)?cp:sh_fmtq(cp),'\n'); +#else sfputr(file,sh_fmtq(cp),'\n'); +#endif /* SHOPT_TYPEDEF */ } return(1); } @@ -931,16 +1126,20 @@ static void print_scan(Sfio_t *file, int flag, Dt_t *root, int option,struct tda register Namval_t *np; register int namec; Namval_t *onp = 0; - sh.last_table=0; + tp->sh->last_table=0; flag &= ~NV_ASSIGN; tp->scanmask = flag&~NV_NOSCOPE; tp->scanroot = root; tp->outfile = file; +#if SHOPT_TYPEDEF + if(!tp->prefix && tp->tp) + tp->prefix = nv_name(tp->tp); +#endif /* SHOPT_TYPEDEF */ if(flag&NV_INTEGER) tp->scanmask |= (NV_DOUBLE|NV_EXPNOTE); namec = nv_scan(root,nullscan,(void*)tp,tp->scanmask,flag); - argv = tp->argnam = (char**)stakalloc((namec+1)*sizeof(char*)); - namec = nv_scan(root, pushname, (void*)tp, tp->scanmask, flag); + argv = tp->argnam = (char**)stkalloc(tp->sh->stk,(namec+1)*sizeof(char*)); + namec = nv_scan(root, pushname, (void*)tp, tp->scanmask, flag&~NV_IARRAY); if(mbcoll()) strsort(argv,namec,strcoll); while(namec--) @@ -948,21 +1147,18 @@ static void print_scan(Sfio_t *file, int flag, Dt_t *root, int option,struct tda if((np=nv_search(*argv++,root,0)) && np!=onp && (!nv_isnull(np) || np->nvfun || nv_isattr(np,~NV_NOFREE))) { onp = np; - if((flag&NV_ARRAY) && nv_aindex(np)>=0) - continue; - if(!flag && nv_isattr(np,NV_ARRAY)) + if(flag&NV_ARRAY) { - if(array_elem(nv_arrayptr(np))==0) - continue; - nv_putsub(np,NIL(char*),ARRAY_SCAN); - do + if(nv_aindex(np)>=0) { - print_namval(file,np,option,tp); + if(!(flag&NV_IARRAY)) + continue; } - while(!option && nv_nextsub(np)); + else if((flag&NV_IARRAY)) + continue; + } - else - print_namval(file,np,option,tp); + print_namval(file,np,option,tp); } } } diff --git a/usr/src/lib/libshell/common/bltins/ulimit.c b/usr/src/lib/libshell/common/bltins/ulimit.c index 4f7a2fc844..53fd29def5 100644 --- a/usr/src/lib/libshell/common/bltins/ulimit.c +++ b/usr/src/lib/libshell/common/bltins/ulimit.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -70,13 +70,13 @@ int b_ulimit(int argc,char *argv[],void *extra) register char *limit; register int mode=0, n; register unsigned long hit = 0; - Shell_t *shp = (Shell_t*)extra; + Shell_t *shp = ((Shbltin_t*)extra)->shp; #ifdef _lib_getrlimit struct rlimit rlp; #endif /* _lib_getrlimit */ const Limit_t* tp; char* conf; - int label, unit, noargs, nosupport; + int label, unit, nosupport; rlim_t i; char tmp[32]; Optdisc_t disc; @@ -109,9 +109,9 @@ int b_ulimit(int argc,char *argv[],void *extra) break; } opt_info.disc = 0; - limit = argv[opt_info.index]; /* default to -f */ - if(noargs=(hit==0)) + limit = argv[opt_info.index]; + if(hit==0) for(n=0; shtab_limits[n].option; n++) if(shtab_limits[n].index == RLIMIT_FSIZE) { @@ -196,10 +196,9 @@ int b_ulimit(int argc,char *argv[],void *extra) conf = (char*)e_nosupport; sfputr(sfstdout,conf,'\n'); } - else if(i!=INFINITY || noargs) + else if(i!=INFINITY) { - if(!noargs) - i += (unit-1); + i += (unit-1); sfprintf(sfstdout,"%I*d\n",sizeof(i),i/unit); } else diff --git a/usr/src/lib/libshell/common/bltins/umask.c b/usr/src/lib/libshell/common/bltins/umask.c index a3f0072363..fc5a684016 100644 --- a/usr/src/lib/libshell/common/bltins/umask.c +++ b/usr/src/lib/libshell/common/bltins/umask.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -75,7 +75,7 @@ int b_umask(int argc,char *argv[],void *extra) { char *cp = mask; flag = umask(0); - c = strperm(cp,&cp,~flag); + c = strperm(cp,&cp,~flag&0777); if(*cp) { umask(flag); diff --git a/usr/src/lib/libshell/common/bltins/whence.c b/usr/src/lib/libshell/common/bltins/whence.c index 7d05a741d6..39366f0e68 100644 --- a/usr/src/lib/libshell/common/bltins/whence.c +++ b/usr/src/lib/libshell/common/bltins/whence.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -40,6 +40,7 @@ #define A_FLAG 4 #define F_FLAG 010 #define X_FLAG 020 +#define Q_FLAG 040 static int whence(Shell_t *,char**, int); @@ -51,7 +52,7 @@ static int whence(Shell_t *,char**, int); int b_command(register int argc,char *argv[],void *extra) { register int n, flags=0; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; opt_info.index = opt_info.offset = 0; while((n = optget(argv,sh_optcommand))) switch(n) { @@ -94,7 +95,7 @@ int b_command(register int argc,char *argv[],void *extra) int b_whence(int argc,char *argv[],void *extra) { register int flags=0, n; - register Shell_t *shp = (Shell_t*)extra; + register Shell_t *shp = ((Shbltin_t*)extra)->shp; NOT_USED(argc); if(*argv[0]=='t') flags = V_FLAG; @@ -111,6 +112,10 @@ int b_whence(int argc,char *argv[],void *extra) break; case 'p': flags |= P_FLAG; + flags &= ~V_FLAG; + break; + case 'q': + flags |= Q_FLAG; break; case ':': errormsg(SH_DICT,2, "%s", opt_info.arg); @@ -136,21 +141,20 @@ static int whence(Shell_t *shp,char **argv, register int flags) Dt_t *root; Namval_t *nq; char *notused; -#ifdef PATH_BFPATH - Pathcomp_t *pp; -#endif + Pathcomp_t *pp=0; int notrack = 1; + if(flags&Q_FLAG) + flags &= ~A_FLAG; while(name= *argv++) { tofree=0; aflag = ((flags&A_FLAG)!=0); cp = 0; np = 0; -#ifdef PATH_BFPATH - pp = 0; -#endif if(flags&P_FLAG) goto search; + if(flags&Q_FLAG) + goto bltins; /* reserved words first */ if(sh_lookup(name,shtab_reserved)) { @@ -179,6 +183,7 @@ static int whence(Shell_t *shp,char **argv, register int flags) aflag++; } /* built-ins and functions next */ + bltins: root = (flags&F_FLAG)?shp->bltin_tree:shp->fun_tree; if(np= nv_bfsearch(name, root, &nq, ¬used)) { @@ -190,10 +195,17 @@ static int whence(Shell_t *shp,char **argv, register int flags) if(nv_isnull(np)) cp = sh_translate(is_ufunction); else if(is_abuiltin(np)) - cp = sh_translate(is_builtin); + { + if(nv_isattr(np,BLT_SPC)) + cp = sh_translate(is_spcbuiltin); + else + cp = sh_translate(is_builtin); + } else cp = sh_translate(is_function); } + if(flags&Q_FLAG) + continue; sfprintf(sfstdout,"%s%s\n",name,cp); if(!aflag) continue; @@ -206,67 +218,66 @@ static int whence(Shell_t *shp,char **argv, register int flags) cp=0; notrack=1; } -#ifdef PATH_BFPATH - if(path_search(name,pp,2)) - cp = name; - else + do { - cp = stakptr(PATH_OFFSET); - if(*cp==0) - cp = 0; - else if(*cp!='/') + if(path_search(name,&pp,2+(aflag>1))) + cp = name; + else { - cp = path_fullname(cp); - tofree=1; + cp = stakptr(PATH_OFFSET); + if(*cp==0) + cp = 0; + else if(*cp!='/') + { + cp = path_fullname(cp); + tofree=1; + } } - } -#else - if(path_search(name,cp,2)) - cp = name; - else - cp = shp->lastpath; - shp->lastpath = 0; -#endif - if(cp) - { - if(flags&V_FLAG) + if(flags&Q_FLAG) + r |= !cp; + else if(cp) { - if(*cp!= '/') + if(flags&V_FLAG) + { + if(*cp!= '/') + { + if(!np && (np=nv_search(name,shp->track_tree,0))) + sfprintf(sfstdout,"%s %s %s/%s\n",name,sh_translate(is_talias),path_pwd(0),cp); + else if(!np || nv_isnull(np)) + sfprintf(sfstdout,"%s%s\n",name,sh_translate(is_ufunction)); + continue; + } + sfputr(sfstdout,sh_fmtq(name),' '); + /* built-in version of program */ + if(*cp=='/' && (np=nv_search(cp,shp->bltin_tree,0))) + msg = sh_translate(is_builtver); + /* tracked aliases next */ + else if(aflag>1 || !notrack || strchr(name,'/')) + msg = sh_translate("is"); + else + msg = sh_translate(is_talias); + sfputr(sfstdout,msg,' '); + } + sfputr(sfstdout,sh_fmtq(cp),'\n'); + if(aflag) { -#ifdef PATH_BFPATH - if(!np && (np=nv_search(name,shp->track_tree,0))) - sfprintf(sfstdout,"%s %s %s/%s\n",name,sh_translate(is_talias),path_pwd(0),cp); - else if(!np || nv_isnull(np)) -#else - if(!np || nv_isnull(np)) -#endif - sfprintf(sfstdout,"%s%s\n",name,sh_translate(is_ufunction)); - continue; + if(aflag<=1) + aflag++; + if (pp) + pp = pp->next; } - sfputr(sfstdout,sh_fmtq(name),' '); - /* built-in version of program */ - if(*cp=='/' && (np=nv_search(cp,shp->bltin_tree,0))) - msg = sh_translate(is_builtver); - /* tracked aliases next */ - else if(!notrack || strchr(name,'/')) - msg = sh_translate("is"); else - msg = sh_translate(is_talias); - sfputr(sfstdout,msg,' '); + pp = 0; + if(tofree) + free((char*)cp); } - sfputr(sfstdout,sh_fmtq(cp),'\n'); - if(tofree) - free((char*)cp); - } - else if(aflag<=1) - { - r |= 1; - if(flags&V_FLAG) + else if(aflag<=1) { - sfprintf(sfstdout,sh_translate(e_found),sh_fmtq(name)); - sfputc(sfstdout,'\n'); + r |= 1; + if(flags&V_FLAG) + errormsg(SH_DICT,ERROR_exit(0),e_found,sh_fmtq(name)); } - } + } while(pp); } return(r); } diff --git a/usr/src/lib/libshell/common/data/aliases.c b/usr/src/lib/libshell/common/data/aliases.c index 577f098f1d..8a76dc7e82 100644 --- a/usr/src/lib/libshell/common/data/aliases.c +++ b/usr/src/lib/libshell/common/data/aliases.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/data/bash_pre_rc.sh b/usr/src/lib/libshell/common/data/bash_pre_rc.sh index 98e6aba833..e8f51f428b 100644 --- a/usr/src/lib/libshell/common/data/bash_pre_rc.sh +++ b/usr/src/lib/libshell/common/data/bash_pre_rc.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -102,15 +102,16 @@ function PS1.set var+="$prefix" case ${remaining:1:1} in t) var+="\$(printf '%(%H:%M:%S)T')";; - d) var+="\$(printf '%(%a %b:%d)T')";; + d) var+="\$(printf '%(%a %b:%e)T')";; n) var+=$'\n';; s) var+=ksh;; w) var+="\$(pwd)";; W) var+="\$(basename \"\$(pwd)\")";; u) var+=$USER;; - h) var+=$(hostname);; + h) var+=$(hostname -s);; '#') var+=!;; !) var+=!;; + @) var+="\$(printf '%(%I:%M%p)T')";; '$') if (( $(id -u) == 0 )) then var+='#' else var+='$' @@ -168,15 +169,47 @@ alias enable=builtin function help { - man=--man - [[ $1 == -s ]] && man=--short && shift + typeset b cmd usage try_cmd man + function has_help_option + { + [[ $1 == @(''|/*|:|echo|false|true|login|test|'[') ]] && return 1 + return 0 + } + typeset -A short_use=( + [echo]='Usage: echo [ options ] [arg]...' + [:]='Usage: : ...' + [true]='Usage: true ...' + [false]='Usage: false ...' + [login]='Usage: login [-p] [name]' + ['[']='Usage: [ EXPRESSION ] | [ OPTION' + [test]='Usage: test EXPRESSION | test' + ) b=$(builtin) - for i - do - for j in $b - do - [[ $i == $j ]] && $j $man + if (( $# == 0)) + then print 'The following is the current list of built-in commands:' + print -r $'Type help *name* for more information about name\n' + for cmd in $b + do if has_help_option $cmd + then usage=$($cmd --short 2>&1) + print -r -- "${usage:7}" + else print -r -- ${short_use[$cmd]:7} + fi done + return + fi + b=${b/'['/} + man=--man + [[ $1 == -s ]] && man=--short && shift + for try_cmd + do if has_help_option $try_cmd + then if [[ $try_cmd == @(${b//$'\n'/'|'}) ]] + then $try_cmd $man + else man $try_cmd + fi + elif [[ $man == '--short' ]] + then print -r -- ${short_use[$try_cmd]} + else man $try_cmd + fi done } @@ -219,3 +252,4 @@ function cd typeset BASH=$0 ! shopt -qo posix && HISTFILE=~/.bash_history HOSTNAME=$(hostname) +nameref BASH_SUBSHELL=.sh.subshell diff --git a/usr/src/lib/libshell/common/data/builtins.c b/usr/src/lib/libshell/common/data/builtins.c index ddbe5c74ea..dad24caf31 100644 --- a/usr/src/lib/libshell/common/data/builtins.c +++ b/usr/src/lib/libshell/common/data/builtins.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -37,14 +37,11 @@ # define bltin(x) 0 #endif -#ifndef SH_CMDLIB_DIR -# define SH_CMDLIB_DIR "/opt/ast/bin" -#endif #if defined(SHOPT_CMDLIB_DIR) && !defined(SHOPT_CMDLIB_HDR) # define SHOPT_CMDLIB_HDR <cmdlist.h> #endif #define Q(f) #f /* libpp cpp workaround -- fixed 2005-04-11 */ -#define CMDLIST(f) SH_CMDLIB_DIR "/" Q(f), NV_BLTIN|NV_NOFREE, bltin(f), +#define CMDLIST(f) SH_CMDLIB_DIR "/" Q(f), NV_BLTIN|NV_BLTINOPT|NV_NOFREE, bltin(f), #undef basename #undef dirname @@ -64,25 +61,26 @@ const struct shtable3 shtab_builtins[] = "break", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(break), "continue", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(break), "typeset", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_DCL,bltin(typeset), - "test", NV_BLTIN|BLT_ENV|NV_NOFREE, bltin(test), + "test", NV_BLTIN|BLT_ENV, bltin(test), "[", NV_BLTIN|BLT_ENV, bltin(test), "let", NV_BLTIN|BLT_ENV, bltin(let), - "export", NV_BLTIN|BLT_SPC|BLT_DCL, bltin(readonly), + "export", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_DCL,bltin(readonly), + ".", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(dot_cmd), + "return", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(return), #if SHOPT_BASH "local", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_DCL,bltin(typeset), #endif #if _bin_newgrp || _usr_bin_newgrp "newgrp", NV_BLTIN|BLT_ENV|BLT_SPC, Bltin(login), #endif /* _bin_newgrp || _usr_bin_newgrp */ - ".", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(dot_cmd), "alias", NV_BLTIN|BLT_SPC|BLT_DCL, bltin(alias), "hash", NV_BLTIN|BLT_SPC|BLT_DCL, bltin(alias), - "exit", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(return), + "enum", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_DCL,bltin(enum), "eval", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_EXIT,bltin(eval), + "exit", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(return), "fc", NV_BLTIN|BLT_ENV|BLT_EXIT, bltin(hist), "hist", NV_BLTIN|BLT_ENV|BLT_EXIT, bltin(hist), "readonly", NV_BLTIN|BLT_ENV|BLT_SPC|BLT_DCL,bltin(readonly), - "return", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(return), "shift", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(shift), "trap", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(trap), "unalias", NV_BLTIN|BLT_ENV|BLT_SPC, bltin(unalias), @@ -98,20 +96,19 @@ const struct shtable3 shtab_builtins[] = "bg", NV_BLTIN|BLT_ENV, bltin(bg), "fg", NV_BLTIN|BLT_ENV|BLT_EXIT, bltin(bg), "disown", NV_BLTIN|BLT_ENV, bltin(bg), - "kill", NV_BLTIN|BLT_ENV|NV_NOFREE, bltin(kill), + "kill", NV_BLTIN|BLT_ENV, bltin(kill), # else - "/bin/kill", NV_BLTIN|BLT_ENV|NV_NOFREE, bltin(kill), + "/bin/kill", NV_BLTIN|BLT_ENV, bltin(kill), # endif /* SIGTSTP */ "jobs", NV_BLTIN|BLT_ENV, bltin(jobs), #endif /* JOBS */ "false", NV_BLTIN|BLT_ENV, bltin(false), -SH_CMDLIB_DIR "/getconf",NV_BLTIN|BLT_ENV, bltin(getconf), "getopts", NV_BLTIN|BLT_ENV, bltin(getopts), "print", NV_BLTIN|BLT_ENV, bltin(print), - "printf", NV_BLTIN|NV_NOFREE, bltin(printf), - "pwd", NV_BLTIN|NV_NOFREE, bltin(pwd), + "printf", NV_BLTIN|BLT_ENV, bltin(printf), + "pwd", NV_BLTIN, bltin(pwd), "read", NV_BLTIN|BLT_ENV, bltin(read), - "sleep", NV_BLTIN|NV_NOFREE, bltin(sleep), + "sleep", NV_BLTIN, bltin(sleep), "alarm", NV_BLTIN, bltin(alarm), "ulimit", NV_BLTIN|BLT_ENV, bltin(ulimit), "umask", NV_BLTIN|BLT_ENV, bltin(umask), @@ -131,6 +128,7 @@ SH_CMDLIB_DIR "/getconf",NV_BLTIN|BLT_ENV, bltin(getconf), CMDLIST(basename) CMDLIST(chmod) CMDLIST(dirname) + CMDLIST(getconf) CMDLIST(head) CMDLIST(mkdir) CMDLIST(logname) @@ -159,7 +157,7 @@ const char sh_set[] = "}" "[f?Pathname expansion is disabled.]" "[h?Obsolete. Causes each command whose name has the syntax of an " - "alias to become a tracked aliase when it is first encountered.]" + "alias to become a tracked alias when it is first encountered.]" "[k?This is obsolete. All arguments of the form \aname\a\b=\b\avalue\a " "are removed and placed in the variable assignment list for " "the command. Ordinarily, variable assignments must precede " @@ -662,7 +660,7 @@ USAGE_LICENSE "Text between two \\b (backspace) characters indicates " "that the text should be emboldened when displayed. " "Text between two \\a (bell) characters indicates that the text should " - "be emphasised or italicized when displayed. " + "be emphasized or italicized when displayed. " "Text between two \\v (vertical tab) characters indicates " "that the text should displayed in a fixed width font. " "Text between two \\f (formfeed) characters will be replaced by the " @@ -684,6 +682,8 @@ USAGE_LICENSE "[+i?Ignore this \aoptstring\a when generating help. Used when " "combining \aoptstring\a values from multiple passes.]" "[+l?Display only \alongname\a options in help messages.]" + "[+n?Associate -\anumber\a and +\anumber\a options with the first " + "option with numeric arguments.]" "[+o?The \b-\b option character prefix is optional (supports " "obsolete \bps\b(1) option syntax.)]" "[+p?\anumber\a specifies the number of \b-\b characters that must " @@ -865,7 +865,7 @@ USAGE_LICENSE "\n[job ...]\n" "\n" "[+EXIT STATUS?]{" - "[+0?If all jobs are sucessfully disowned.]" + "[+0?If all jobs are successfully disowned.]" "[+>0?If one more \ajob\as does not exist.]" "}" @@ -912,7 +912,7 @@ USAGE_LICENSE ; const char sh_opthist[] = -"[-1c?@(#)$Id: hist (AT&T Research) 2000-04-02 $\n]" +"[-1cn?@(#)$Id: hist (AT&T Research) 2000-04-02 $\n]" USAGE_LICENSE "[+NAME?\f?\f - process command history list]" "[+DESCRIPTION?\b\f?\f\b lists, edits, or re-executes, commands " @@ -1065,7 +1065,7 @@ USAGE_LICENSE "[+NAME?print - write arguments to standard output]" "[+DESCRIPTION?By default, \bprint\b writes each \astring\a operand to " "standard output and appends a newline character.]" -"[+?Unless, the \b-r\b or \b-f\b option is specifed, each \b\\\b " +"[+?Unless, the \b-r\b or \b-f\b option is specified, each \b\\\b " "character in each \astring\a operand is processed specially as " "follows:]{" "[+\\a?Alert character.]" @@ -1100,6 +1100,8 @@ USAGE_LICENSE "[s?Write the output as an entry in the shell history file instead of " "standard output.]" "[u]:[fd:=1?Write to file descriptor number \afd\a instead of standard output.]" +"[v?Treat each \astring\a as a variable name and write the value in \b%B\b " + "format. Cannot be used with \b-f\b.]" "\n" "\n[string ...]\n" "\n" @@ -1240,7 +1242,7 @@ USAGE_LICENSE "[+?If there are more variables than fields, the remaining variables are " "set to empty strings. If there are fewer variables than fields, " "the leftover fields and their intervening separators are assigned " - "to the last variable. If no \avar\a is specifed then the variable " + "to the last variable. If no \avar\a is specified then the variable " "\bREPLY\b is used.]" "[+?When \avar\a has the binary attribute and \b-n\b or \b-N\b is specified, " "the bytes that are read are stored directly into \bvar\b.]" @@ -1249,6 +1251,7 @@ USAGE_LICENSE "is a terminal or pipe.]" "[A?Unset \avar\a and then create an indexed array containing each field in " "the line starting at index 0.]" +"[C?Unset \avar\a and read \avar\a as a compound variable.]" "[d]:[delim?Read until delimiter \adelim\a instead of to the end of line.]" "[p?Read from the current co-process instead of standard input. An end of " "file causes \bread\b to disconnect the co-process so that another " @@ -1275,7 +1278,7 @@ USAGE_LICENSE ; const char sh_optreadonly[] = -"[-1c?\n@(#)$Id: readonly (AT&T Research) 1999-07-07 $\n]" +"[-1c?\n@(#)$Id: readonly (AT&T Research) 2008-06-16 $\n]" USAGE_LICENSE "[+NAME?readonly - set readonly attribute on variables]" "[+DESCRIPTION?\breadonly\b sets the readonly attribute on each of " @@ -1283,6 +1286,9 @@ USAGE_LICENSE "values from being changed. If \b=\b\avalue\a is specified, " "the variable \aname\a is set to \avalue\a before the variable " "is made readonly.]" +"[+?Within a type definition, if the value is not specified, then a " + "value must be specified when creating each instance of the type " + "and the value is readonly for each instance.]" "[+?If no \aname\as are specified then the names and values of all " "readonly variables are written to standard output.]" "[+?\breadonly\b is built-in to the shell as a declaration command so that " @@ -1368,14 +1374,19 @@ USAGE_LICENSE "[D\f:dump-strings\f?Do not execute the script, but output the set of double " "quoted strings preceded by a \b$\b. These strings are needed for " "localization of the script to different locales.]" -"[E?Reads the file \b${ENV-$HOME/.kshrc}\b, if it exists, as a profile. " +"[E?Reads the file " +#if SHOPT_SYSRC + "\b/etc/ksh.kshrc\b, if it exists, as a profile, followed by " +#endif + "\b${ENV-$HOME/.kshrc}\b, if it exists, as a profile. " "On by default for interactive shells; use \b+E\b to disable.]" #if SHOPT_PFSH "[P?Invoke the shell as a profile shell. See \bpfexec\b(1).]" #endif #if SHOPT_KIA "[R]:[file?Do not execute the script, but create a cross reference database " - "in \afile\a that can be used a separate shell script browser.]" + "in \afile\a that can be used a separate shell script browser. The " + "-R option requires a script to be specified as the first operand.]" #endif /* SHOPT_KIA */ #if SHOPT_BASH "\fbash2\f" @@ -1424,6 +1435,9 @@ USAGE_LICENSE "[A]:[name?Assign the arguments sequentially to the array named by \aname\a " "starting at subscript 0 rather than to the positional parameters.]" "\fabc\f" +"[06:default?Restore all non-command line options to the default settings.]" +"[07:state?List the current option state in the form of a \bset\b command " + "that can be executed to restore the state.]" "\n" "\n[arg ...]\n" "\n" @@ -1466,7 +1480,7 @@ USAGE_LICENSE "[+NAME?sleep - suspend execution for an interval]" "[+DESCRIPTION?\bsleep\b suspends execution for at least the time specified " "by \aseconds\a or until a \bSIGALRM\b signal is received. " - "\aseconds\a can be specifed as a floating point number but the " + "\aseconds\a can be specified as a floating point number but the " "actual granularity depends on the underlying system, normally " "around 1 millisecond.]" "\n" @@ -1534,7 +1548,7 @@ USAGE_LICENSE ; const char sh_opttypeset[] = -"+[-1c?\n@(#)$Id: typeset (AT&T Research) 2003-01-15 $\n]" +"+[-1c?\n@(#)$Id: typeset (AT&T Research) 2008-08-04 $\n]" USAGE_LICENSE "[+NAME?\f?\f - declare or display variables with attributes]" "[+DESCRIPTION?Without the \b-f\b option, \b\f?\f\b sets, unsets, " @@ -1571,10 +1585,12 @@ USAGE_LICENSE "[+?\b\f?\f\b is built-in to the shell as a declaration command so that " "field splitting and pathname expansion are not performed on " "the arguments. Tilde expansion occurs on \avalue\a.]" -#if SHOPT_BASH -"[a?Ignored, used for bash compatibility.]" -#endif +#if 1 +"[a]:?[type?Indexed array. This is the default. If \b[\b\atype\a\b]]\b is " + "specified, each subscript is interpreted as a value of type \atype\a.]" +#else "[a?Indexed array. this is the default.]" +#endif "[b?Each \aname\a may contain binary data. Its value is the mime " "base64 encoding of the data. It can be used with \b-Z\b, " "to specify fixed sized fields.]" @@ -1583,8 +1599,12 @@ USAGE_LICENSE "from 2 to 64.]" "[l?Convert uppercase character to lowercase. Unsets \b-u\b attribute. When " "used with \b-i\b, \b-E\b, or \b-F\b indicates long variant.]" +"[m?Move. The value is the name of a variable whose value will be " + "moved to \aname\a. The orignal variable will be unset. Cannot be " + "used with any other options.]" "[n?Name reference. The value is the name of a variable that \aname\a " - "references. \aname\a cannot contain a \b.\b.]" + "references. \aname\a cannot contain a \b.\b. Cannot be use with " + "any other options.]" "[p?Causes the output to be in a format that can be used as input to the " "shell to recreate the attributes for variables.]" "[r?Enables readonly. Once enabled it cannot be disabled. See " @@ -1602,6 +1622,9 @@ USAGE_LICENSE "[A?Associative array. Each \aname\a will converted to an associate " "array. If a variable already exists, the current value will " "become index \b0\b.]" +"[C?Compound variable. Each \aname\a will be a compound variable. If " + "\avalue\a names a compound variable it will be copied to \aname\a. " + "Otherwise if the variable already exists, it will first be unset.]" "[E]#?[n:=10?Floating point number represented in scientific notation. " "\an\a specifies the number of significant figures when the " "value is expanded.]" @@ -1617,6 +1640,19 @@ USAGE_LICENSE "[R]#?[n?Right justify. If \an\a is given it represents the field width. If " "the \b-Z\b attribute is also specified, then zeros will " "be used as the fill character. Otherwise, spaces are used.]" +"[X]#?[n:=10?Floating point number represented in hexadecimal notation. " + "\an\a specifies the number of significant figures when the " + "value is expanded.]" + +#ifdef SHOPT_TYPEDEF +"[h]:[string?Used within a type definition to provide a help string " + "for variable \aname\a. Otherwise, it is ignored.]" +"[S?Used with a type definition to indicate that the variable is shared by " + "each instance of the type. When used inside a function defined " + "with the \bfunction\b reserved word, the specified variables " + "will have function static scope. Otherwise, the variable is " + "unset prior to processing the assignment list.]" +#endif "[T]:[tname?\atname\a is the name of a type name given to each \aname\a.]" "[Z]#?[n?Zero fill. If \an\a is given it represents the field width.]" "\n" @@ -1784,7 +1820,7 @@ USAGE_LICENSE #endif /* SHOPT_FS_3D */ const char sh_optwhence[] = -"[-1c?\n@(#)$Id: whence (AT&T Research) 1999-07-07 $\n]" +"[-1c?\n@(#)$Id: whence (AT&T Research) 2007-04-24 $\n]" USAGE_LICENSE "[+NAME?whence - locate a command and describe its type]" "[+DESCRIPTION?Without \b-v\b, \bwhence\b writes on standard output an " @@ -1792,12 +1828,14 @@ USAGE_LICENSE "on the complete search order that the shell uses. If \aname\a " "is not found, then no output is produced.]" "[+?If \b-v\b is specified, the output will also contain information " - "that indicates how the given \aname\a would be interpretted by " + "that indicates how the given \aname\a would be interpreted by " "the shell in the current execution environment.]" "[a?Displays all uses for each \aname\a rather than the first.]" "[f?Do not check for functions.]" "[p?Do not check to see if \aname\a is a reserved word, a built-in, " - "an alias, or a function.]" + "an alias, or a function. This turns off the \b-v\b option.]" +"[q?Quiet mode. Returns 0 if all arguments are built-ins, functions, or are " + "programs found on the path.]" "[v?For each name you specify, the shell displays a line that indicates " "if that name is one of the following:]{" "[+?Reserved word]" @@ -1807,7 +1845,6 @@ USAGE_LICENSE "[+?Function]" "[+?Tracked alias]" "[+?Program]" - "[+?Not found]" "}" "\n" "\nname ...\n" @@ -1828,6 +1865,5 @@ const char e_baddisc[] = "%s: invalid discipline function"; const char e_nospace[] = "out of memory"; const char e_nofork[] = "cannot fork"; const char e_nosignal[] = "%s: unknown signal name"; -const char e_numeric[] = "*([0-9])?(.)*([0-9])"; const char e_condition[] = "condition(s) required"; const char e_cneedsarg[] = "-c requires argument"; diff --git a/usr/src/lib/libshell/common/data/keywords.c b/usr/src/lib/libshell/common/data/keywords.c index 66b3c3cea2..95e4bee1f6 100644 --- a/usr/src/lib/libshell/common/data/keywords.c +++ b/usr/src/lib/libshell/common/data/keywords.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/data/lexstates.c b/usr/src/lib/libshell/common/data/lexstates.c index 0cedf9a4dc..438d58f568 100644 --- a/usr/src/lib/libshell/common/data/lexstates.c +++ b/usr/src/lib/libshell/common/data/lexstates.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -56,7 +56,7 @@ static const char sh_lexstate0[256] = S_NAME, S_RES, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, S_NAME, #endif /* SHOPT_NAMESPACE */ S_NAME, S_NAME, S_NAME, S_RES, S_RES, S_RES, S_NAME, S_RES, - S_NAME, S_NAME, S_NAME, S_REG, S_OP, S_REG, S_TILDE,S_REG, + S_NAME, S_NAME, S_NAME, S_BRACE,S_OP, S_BRACE,S_TILDE,S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, S_REG, @@ -360,8 +360,8 @@ static const char sh_lexstate9[256] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, S_QUOTE,0, S_DOL, 0, S_PAT, S_LIT, - S_PAT, S_PAT, S_PAT, 0, 0, 0, 0, S_SLASH, - 0, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, + S_PAT, S_PAT, S_PAT, 0, S_COM, 0, S_DOT, S_SLASH, + S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_DIG, S_COLON,0, 0, S_EQ, 0, S_PAT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -409,3 +409,4 @@ const char e_lexfuture[] = "line %d: \\ in front of %c reserved for future use"; const char e_lexlongquote[] = "line %d: %c quote may be missing"; const char e_lexzerobyte[] = "zero byte"; const char e_lexemptyfor[] = "line %d: empty for list"; +const char e_lextypeset[] = "line %d: %s invalid typeset option order"; diff --git a/usr/src/lib/libshell/common/data/limits.c b/usr/src/lib/libshell/common/data/limits.c index 9f0a87b3e8..4c743f7050 100644 --- a/usr/src/lib/libshell/common/data/limits.c +++ b/usr/src/lib/libshell/common/data/limits.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/data/math.tab b/usr/src/lib/libshell/common/data/math.tab index cecfc12a34..281389f105 100644 --- a/usr/src/lib/libshell/common/data/math.tab +++ b/usr/src/lib/libshell/common/data/math.tab @@ -1,6 +1,6 @@ # <return type: i:integer f:floating-point> <#floating-point-args> <function-name> [<alias> ...] # <function-name>l variants are handled by features/math.sh -# @(#)math.tab (AT&T Research) 2006-10-18 +# @(#)math.tab (AT&T Research) 2008-05-22 f 1 acos f 1 acosh f 1 asin @@ -9,6 +9,7 @@ f 1 atan f 2 atan2 f 1 atanh f 1 cbrt +f 1 ceil f 2 copysign f 1 cos f 1 cosh diff --git a/usr/src/lib/libshell/common/data/msg.c b/usr/src/lib/libshell/common/data/msg.c index 498f6558d0..3a06ed3f8d 100644 --- a/usr/src/lib/libshell/common/data/msg.c +++ b/usr/src/lib/libshell/common/data/msg.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -96,14 +96,19 @@ const char e_aliname[] = "%s: invalid alias name"; const char e_badexport[] = "%s: invalid export name"; const char e_badref[] = "%s: reference variable cannot be an array"; const char e_noarray[] = "%s: cannot be an array"; +const char e_badappend[] = "%s: invalid append to associative array"; const char e_noref[] = "%s: no reference name"; const char e_selfref[] = "%s: invalid self reference"; const char e_noalias[] = "%s: alias not found\n"; const char e_format[] = "%s: bad format"; const char e_redef[] = "%s: type cannot be redefined"; +const char e_required[] = "%s: is a required element of %s"; const char e_badtformat[] = "%c: bad format character in time format"; const char e_nolabels[] = "%s: label not implemented"; const char e_notimp[] = "%s: not implemented"; +const char e_notelem[] = "%.*s: is not an element of %s"; +const char e_notenum[] = "%s: not an enumeration type"; +const char e_unknowntype[] = "%.*s: unknown type"; const char e_nosupport[] = "not supported"; const char e_badrange[] = "%d-%d: invalid range"; const char e_eneedsarg[] = "-e - requires single argument"; @@ -116,6 +121,7 @@ const char e_on [] = "on"; const char e_off[] = "off"; const char is_reserved[] = " is a keyword"; const char is_builtin[] = " is a shell builtin"; +const char is_spcbuiltin[] = " is a special shell builtin"; const char is_builtver[] = "is a shell builtin version of"; const char is_alias[] = "%s is an alias for "; const char is_xalias[] = "%s is an exported alias for "; diff --git a/usr/src/lib/libshell/common/data/options.c b/usr/src/lib/libshell/common/data/options.c index 82ffd82d6c..cd49f6555c 100644 --- a/usr/src/lib/libshell/common/data/options.c +++ b/usr/src/lib/libshell/common/data/options.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -25,7 +25,7 @@ #include "shtable.h" #if SHOPT_BASH -# define bashopt(a,b) a, b|SH_BASHOPT, +# define bashopt(a,b) a, b|SH_BASHOPT, # define bashextra(a,b) a, b|SH_BASHEXTRA, #else # define bashopt(a,b) @@ -92,8 +92,15 @@ const Shtable_t shtab_options[] = bashextra("physical", SH_PHYSICAL) bashextra("posix", SH_POSIX) "privileged", SH_PRIVILEGED, -#if SHOPT_PFSH +#if SHOPT_BASH + "profile", SH_LOGIN_SHELL|SH_COMMANDLINE, +# if SHOPT_PFSH + "pfsh", SH_PFSH|SH_COMMANDLINE, +# endif +#else +# if SHOPT_PFSH "profile", SH_PFSH|SH_COMMANDLINE, +# endif #endif bashopt("progcomp", SH_PROGCOMP) bashopt("promptvars", SH_PROMPTVARS) @@ -115,13 +122,15 @@ const Shtable_t shtab_options[] = const Shtable_t shtab_attributes[] = { + {"-Sshared", NV_REF|NV_TAGGED}, {"-nnameref", NV_REF}, {"-xexport", NV_EXPORT}, {"-rreadonly", NV_RDONLY}, {"-ttagged", NV_TAGGED}, - {"-llong", (NV_INTEGER|NV_DOUBLE|NV_LONG)}, - {"-Eexponential",(NV_INTEGER|NV_DOUBLE|NV_EXPNOTE)}, - {"-Ffloat", (NV_INTEGER|NV_DOUBLE)}, + {"-llong", (NV_DOUBLE|NV_LONG)}, + {"-Eexponential",(NV_DOUBLE|NV_EXPNOTE)}, + {"-Xhexfloat", (NV_DOUBLE|NV_HEXFLOAT)}, + {"-Ffloat", NV_DOUBLE}, {"-llong", (NV_INTEGER|NV_LONG)}, {"-sshort", (NV_INTEGER|NV_SHORT)}, {"-uunsigned", (NV_INTEGER|NV_UNSIGN)}, diff --git a/usr/src/lib/libshell/common/data/signals.c b/usr/src/lib/libshell/common/data/signals.c index cb29d32a1a..7c3df373f9 100644 --- a/usr/src/lib/libshell/common/data/signals.c +++ b/usr/src/lib/libshell/common/data/signals.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -25,7 +25,7 @@ # define SIGCHLD SIGCLD #endif -#define VAL(sig,mode) ((sig+1)|(mode)<<SH_SIGBITS) +#define VAL(sig,mode) ((sig+1)|((mode)<<SH_SIGBITS)) #define TRAP(n) (((n)|SH_TRAP)-1) #ifndef ERROR_dictionary @@ -55,6 +55,9 @@ const struct shtable2 shtab_signals[] = #ifdef SIGBUS "BUS", VAL(SIGBUS,SH_SIGDONE), S("Bus error"), #endif /* SIGBUS */ +#ifdef SIGCANCEL + "CANCEL", VAL(SIGCANCEL,SH_SIGIGNORE), S("Thread cancellation"), +#endif /*SIGCANCEL */ #ifdef SIGCHLD "CHLD", VAL(SIGCHLD,SH_SIGFAULT), S("Death of Child"), # ifdef SIGCLD @@ -71,6 +74,9 @@ const struct shtable2 shtab_signals[] = "CONT", VAL(SIGCONT,SH_SIGIGNORE), S("Stopped process continued"), #endif /* SIGCONT */ "DEBUG", VAL(TRAP(SH_DEBUGTRAP),0), "", +#ifdef SIGDANGER + "DANGER", VAL(SIGDANGER,0), S("System crash soon"), +#endif /* SIGDANGER */ #ifdef SIGDIL "DIL", VAL(SIGDIL,0), S("DIL signal"), #endif /* SIGDIL */ @@ -99,6 +105,12 @@ const struct shtable2 shtab_signals[] = #ifdef SIGIOT "IOT", VAL(SIGIOT,SH_SIGDONE), S("Abort"), #endif /* SIGIOT */ +#ifdef SIGJVM1 + "JVM1", VAL(SIGJVM1,SH_SIGIGNORE), S("Special signal used by Java Virtual Machine"), +#endif /*SIGJVM1 */ +#ifdef SIGJVM2 + "JVM2", VAL(SIGJVM2,SH_SIGIGNORE), S("Special signal used by Java Virtual Machine"), +#endif /*SIGJVM2 */ "KEYBD", VAL(TRAP(SH_KEYTRAP),0), "", #ifdef SIGKILL "KILL", VAL(SIGKILL,0), S("Killed"), @@ -135,31 +147,13 @@ const struct shtable2 shtab_signals[] = #endif /* SIGPWR */ #ifdef SIGQUIT "QUIT", VAL(SIGQUIT,SH_SIGDONE|SH_SIGINTERACTIVE), S("Quit"), -#ifdef __SIGRTMIN -#undef SIGRTMIN -#define SIGRTMIN __SIGRTMIN -#else -#ifdef _SIGRTMIN -#undef SIGRTMIN -#define SIGRTMIN _SIGRTMIN -#endif -#endif +#endif /* SIGQUIT */ #ifdef SIGRTMIN - "RTMIN", VAL(SIGRTMIN,0), S("Lowest priority realtime signal"), + "RTMIN", VAL(SH_SIGRTMIN,SH_SIGRUNTIME), S("Lowest priority realtime signal"), #endif /* SIGRTMIN */ -#ifdef __SIGRTMAX -#undef SIGRTMAX -#define SIGRTMAX __SIGRTMAX -#else -#ifdef _SIGRTMAX -#undef SIGRTMAX -#define SIGRTMAX _SIGRTMAX -#endif -#endif #ifdef SIGRTMAX - "RTMAX", VAL(SIGRTMAX,0), S("Highest priority realtime signal"), + "RTMAX", VAL(SH_SIGRTMAX,SH_SIGRUNTIME), S("Highest priority realtime signal"), #endif /* SIGRTMAX */ -#endif /* SIGQUIT */ "SEGV", VAL(SIGSEGV,0), S("Memory fault"), #ifdef SIGSTOP "STOP", VAL(SIGSTOP,0), S("Stopped (SIGSTOP)"), @@ -205,9 +199,6 @@ const struct shtable2 shtab_signals[] = #ifdef SIGMIGRATE "MIGRATE", VAL(SIGMIGRATE,0), S("Migrate process"), #endif /* SIGMIGRATE */ -#ifdef SIGDANGER - "DANGER", VAL(SIGDANGER,0), S("System crash soon"), -#endif /* SIGDANGER */ #ifdef SIGSOUND "SOUND", VAL(SIGSOUND,0), S("Sound completed"), #endif /* SIGSOUND */ @@ -223,5 +214,8 @@ const struct shtable2 shtab_signals[] = #ifdef SIGXFSZ "XFSZ", VAL(SIGXFSZ,SH_SIGDONE|SH_SIGINTERACTIVE), S("Exceeded file size limit"), #endif /* SIGXFSZ */ +#ifdef SIGXRES + "XRES", VAL(SIGXRES,SH_SIGDONE|SH_SIGINTERACTIVE), S("Exceeded resource control"), +#endif /* SIGRES */ "", 0, 0 }; diff --git a/usr/src/lib/libshell/common/data/solaris_cmdlist.h b/usr/src/lib/libshell/common/data/solaris_cmdlist.h index d12e2c0ddb..29c75aaa7d 100644 --- a/usr/src/lib/libshell/common/data/solaris_cmdlist.h +++ b/usr/src/lib/libshell/common/data/solaris_cmdlist.h @@ -20,14 +20,12 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#ifndef _SOLARIS_CMDLIST_H -#define _SOLARIS_CMDLIST_H - -#pragma ident "%Z%%M% %I% %E% SMI" +#ifndef _SOLARIS_KSH_CMDLIST_H +#define _SOLARIS_KSH_CMDLIST_H #ifdef __cplusplus extern "C" { @@ -42,19 +40,32 @@ extern "C" { /* POSIX compatible commands */ #ifdef _NOT_YET -#define XPG6CMDLIST(f) { "/usr/xpg6/bin/" #f, NV_BLTIN|NV_NOFREE, bltin(f) }, -#define XPG4CMDLIST(f) { "/usr/xpg4/bin/" #f, NV_BLTIN|NV_NOFREE, bltin(f) }, +#define XPG6CMDLIST(f) \ + { "/usr/xpg6/bin/" #f, NV_BLTIN|NV_BLTINOPT|NV_NOFREE, bltin(f) }, +#define XPG4CMDLIST(f) \ + { "/usr/xpg4/bin/" #f, NV_BLTIN|NV_BLTINOPT|NV_NOFREE, bltin(f) }, #else #define XPG6CMDLIST(f) #define XPG4CMDLIST(f) #endif /* NOT_YET */ /* * Commands which are 100% compatible with native Solaris versions (/bin is - * a softlink to ./usr/bin so both need to be listed here) + * a softlink to ./usr/bin, ksh93 takes care about the lookup) + */ +#define BINCMDLIST(f) \ + { "/bin/" #f, NV_BLTIN|NV_BLTINOPT|NV_NOFREE, bltin(f) }, +#define USRBINCMDLIST(f) \ + { "/usr/bin/" #f, NV_BLTIN|NV_BLTINOPT|NV_NOFREE, bltin(f) }, +#define SBINCMDLIST(f) \ + { "/sbin/" #f, NV_BLTIN|NV_BLTINOPT|NV_NOFREE, bltin(f) }, +#define SUSRBINCMDLIST(f) \ + { "/usr/sbin/" #f, NV_BLTIN|NV_BLTINOPT|NV_NOFREE, bltin(f) }, +/* + * Make all ksh93 builtins accessible when /usr/ast/bin was added to + * /usr/xpg6/bin:/usr/xpg4/bin:/usr/ccs/bin:/usr/bin:/bin:/opt/SUNWspro/bin */ -#define BINCMDLIST(f) { "/bin/" #f, NV_BLTIN|NV_NOFREE, bltin(f) }, -/* Make all ksh93 builtins accessible when /usr/ast/bin was added to ${PATH} */ -#define ASTCMDLIST(f) { "/usr/ast/bin/" #f, NV_BLTIN|NV_NOFREE, bltin(f) }, +#define ASTCMDLIST(f) \ + { "/usr/ast/bin/" #f, NV_BLTIN|NV_BLTINOPT|NV_NOFREE, bltin(f) }, /* undo ast_map.h #defines to avoid collision */ #undef basename @@ -91,14 +102,17 @@ ASTCMDLIST(id) ASTCMDLIST(join) XPG4CMDLIST(ln) ASTCMDLIST(ln) +BINCMDLIST(logname) ASTCMDLIST(logname) BINCMDLIST(mkdir) ASTCMDLIST(mkdir) +BINCMDLIST(mkfifo) ASTCMDLIST(mkfifo) XPG4CMDLIST(mv) ASTCMDLIST(mv) ASTCMDLIST(paste) ASTCMDLIST(pathchk) +BINCMDLIST(rev) ASTCMDLIST(rev) XPG4CMDLIST(rm) ASTCMDLIST(rm) @@ -106,17 +120,23 @@ BINCMDLIST(rmdir) ASTCMDLIST(rmdir) XPG4CMDLIST(stty) ASTCMDLIST(stty) +BINCMDLIST(sum) +ASTCMDLIST(sum) +SUSRBINCMDLIST(sync) +SBINCMDLIST(sync) +BINCMDLIST(sync) +ASTCMDLIST(sync) XPG4CMDLIST(tail) ASTCMDLIST(tail) BINCMDLIST(tee) ASTCMDLIST(tee) +BINCMDLIST(tty) ASTCMDLIST(tty) ASTCMDLIST(uname) BINCMDLIST(uniq) ASTCMDLIST(uniq) BINCMDLIST(wc) ASTCMDLIST(wc) -/* End-of-generated-data. */ /* Mandatory for ksh93 test suite and AST scripts */ BINCMDLIST(getconf) @@ -125,4 +145,4 @@ BINCMDLIST(getconf) } #endif -#endif /* _SOLARIS_CMDLIST_H */ +#endif /* !_SOLARIS_KSH_CMDLIST_H */ diff --git a/usr/src/lib/libshell/common/data/strdata.c b/usr/src/lib/libshell/common/data/strdata.c index 57689b68a3..f980c834f3 100644 --- a/usr/src/lib/libshell/common/data/strdata.c +++ b/usr/src/lib/libshell/common/data/strdata.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/data/testops.c b/usr/src/lib/libshell/common/data/testops.c index 449ca99efb..e00e4857c9 100644 --- a/usr/src/lib/libshell/common/data/testops.c +++ b/usr/src/lib/libshell/common/data/testops.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/data/variables.c b/usr/src/lib/libshell/common/data/variables.c index 9042e235b0..65b1985d29 100644 --- a/usr/src/lib/libshell/common/data/variables.c +++ b/usr/src/lib/libshell/common/data/variables.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -26,6 +26,7 @@ #include "shtable.h" #include "name.h" #include "defs.h" +#include "variables.h" /* * This is the list of built-in shell variables and default values @@ -75,6 +76,7 @@ const struct shtable2 shtab_variables[] = "LC_MESSAGES", 0, (char*)0, "LC_NUMERIC", 0, (char*)0, "FIGNORE", 0, (char*)0, + "KSH_VERSION", 0, (char*)0, ".sh", NV_TABLE|NV_RDONLY|NV_NOFREE|NV_NOPRINT,(char*)0, ".sh.edchar", 0, (char*)0, ".sh.edcol", 0, (char*)0, @@ -91,6 +93,9 @@ const struct shtable2 shtab_variables[] = ".sh.fun", 0, (char*)0, ".sh.subshell", NV_INTEGER|NV_SHORT|NV_NOFREE, (char*)0, ".sh.level", 0, (char*)0, + ".sh.lineno", NV_INTEGER|NV_RDONLY, (char*)0, + ".sh.stats", NV_RDONLY, (char*)0, + "SHLVL", NV_INTEGER|NV_NOFREE|NV_EXPORT, (char*)0, #if SHOPT_FS_3D "VPATH", 0, (char*)0, #endif /* SHOPT_FS_3D */ @@ -103,3 +108,25 @@ const struct shtable2 shtab_variables[] = "", 0, (char*)0 }; +const char *nv_discnames[] = { "get", "set", "append", "unset", 0 }; + +#ifdef SHOPT_STATS +const Shtable_t shtab_stats[] = +{ + "arg_cachehits", STAT_ARGHITS, + "arg_expands", STAT_ARGEXPAND, + "comsubs", STAT_COMSUB, + "forks", STAT_FORKS, + "funcalls", STAT_FUNCT, + "globs", STAT_GLOBS, + "linesread", STAT_READS, + "nv_cachehit", STAT_NVHITS, + "nv_opens", STAT_NVOPEN, + "pathsearch", STAT_PATHS, + "posixfuncall", STAT_SVFUNCT, + "simplecmds", STAT_SCMDS, + "spawns", STAT_SPAWN, + "subshell", STAT_SUBSHELL +}; +#endif /* SHOPT_STATS */ + diff --git a/usr/src/lib/libshell/common/edit/completion.c b/usr/src/lib/libshell/common/edit/completion.c index 050019fbe5..54b4212470 100644 --- a/usr/src/lib/libshell/common/edit/completion.c +++ b/usr/src/lib/libshell/common/edit/completion.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -68,7 +68,8 @@ static char *overlaid(register char *str,register const char *newstr,int nocase) static char *find_begin(char outbuff[], char *last, int endchar, int *type) { register char *cp=outbuff, *bp, *xp; - register int c,inquote = 0; + register int c,inquote = 0, inassign=0; + int mode=*type; bp = outbuff; *type = 0; while(cp < last) @@ -94,7 +95,7 @@ static char *find_begin(char outbuff[], char *last, int endchar, int *type) if(inquote == '\'') break; c = *(unsigned char*)cp; - if(isaletter(c) || c=='{') + if(mode!='*' && (isaletter(c) || c=='{')) { int dot = '.'; if(c=='{') @@ -112,7 +113,7 @@ static char *find_begin(char outbuff[], char *last, int endchar, int *type) if((c= mbchar(cp)) , c!=dot && !isaname(c)) break; } - if(cp>=last) + if(cp>=last && c!= '}') { *type='$'; return(++xp); @@ -120,6 +121,7 @@ static char *find_begin(char outbuff[], char *last, int endchar, int *type) } else if(c=='(') { + *type = mode; xp = find_begin(cp,last,')',type); if(*(cp=xp)!=')') bp = xp; @@ -129,6 +131,13 @@ static char *find_begin(char outbuff[], char *last, int endchar, int *type) break; case '=': if(!inquote) + { + bp = cp; + inassign = 1; + } + break; + case ':': + if(!inquote && inassign) bp = cp; break; case '~': @@ -139,7 +148,10 @@ static char *find_begin(char outbuff[], char *last, int endchar, int *type) if(c && c==endchar) return(xp); if(!inquote && ismeta(c)) + { bp = cp; + inassign = 0; + } break; } } @@ -172,7 +184,7 @@ int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) { if(count> ep->e_nlist) return(-1); - mode = '*'; + mode = '?'; av[0] = ep->e_clist[count-1]; av[1] = 0; } @@ -207,6 +219,7 @@ int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) register int c; char *last = out; c = *(unsigned char*)out; + var = mode; begin = out = find_begin(outbuff,last,0,&var); /* addstar set to zero if * should not be added */ if(var=='$') @@ -234,6 +247,8 @@ int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) out++; } } + if(mode=='?') + mode = '*'; if(var!='$' && mode=='\\' && out[-1]!='*') addstar = '*'; if(*begin=='~' && !strchr(begin,'/')) @@ -264,7 +279,7 @@ int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) } else { - com = sh_argbuild(&narg,comptr,0); + com = sh_argbuild(ep->sh,&narg,comptr,0); /* special handling for leading quotes */ if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\'')) begin--; @@ -373,14 +388,9 @@ int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count) { Namval_t *np; /* add as tracked alias */ -#ifdef PATH_BFPATH Pathcomp_t *pp; if(*cp=='/' && (pp=path_dirfind(sh.pathlist,cp,'/')) && (np=nv_search(begin,sh.track_tree,NV_ADD))) path_alias(np,pp); -#else - if(*cp=='/' && (np=nv_search(begin,sh.track_tree,NV_ADD))) - path_alias(np,cp); -#endif out = strcopy(begin,cp); } /* add quotes if necessary */ diff --git a/usr/src/lib/libshell/common/edit/edit.c b/usr/src/lib/libshell/common/edit/edit.c index a832a4a965..fbee33ca57 100644 --- a/usr/src/lib/libshell/common/edit/edit.c +++ b/usr/src/lib/libshell/common/edit/edit.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -53,6 +53,8 @@ static char CURSOR_UP[20] = { ESC, '[', 'A', 0 }; + + #if SHOPT_MULTIBYTE # define is_cntrl(c) ((c<=STRIP) && iscntrl(c)) # define is_print(c) ((c&~STRIP) || isprint(c)) @@ -583,34 +585,39 @@ void ed_crlf(register Edit_t *ep) void ed_setup(register Edit_t *ep, int fd, int reedit) { + Shell_t *shp = ep->sh; register char *pp; - register char *last; + register char *last, *prev; char *ppmax; int myquote = 0, n; - register int qlen = 1; + register int qlen = 1, qwid; char inquote = 0; ep->e_fd = fd; ep->e_multiline = sh_isoption(SH_MULTILINE)!=0; #ifdef SIGWINCH - if(!(sh.sigflag[SIGWINCH]&SH_SIGFAULT)) + if(!(shp->sigflag[SIGWINCH]&SH_SIGFAULT)) { signal(SIGWINCH,sh_fault); - sh.sigflag[SIGWINCH] |= SH_SIGFAULT; + shp->sigflag[SIGWINCH] |= SH_SIGFAULT; } + pp = shp->st.trapcom[SIGWINCH]; + shp->st.trapcom[SIGWINCH] = 0; sh_fault(SIGWINCH); + shp->st.trapcom[SIGWINCH] = pp; + ep->sh->winch = 0; #endif #if KSHELL ep->e_stkptr = stakptr(0); ep->e_stkoff = staktell(); - if(!(last = sh.prompt)) + if(!(last = shp->prompt)) last = ""; - sh.prompt = 0; + shp->prompt = 0; #else last = ep->e_prbuff; #endif /* KSHELL */ - if(sh.hist_ptr) + if(shp->hist_ptr) { - register History_t *hp = sh.hist_ptr; + register History_t *hp = shp->hist_ptr; ep->e_hismax = hist_max(hp); ep->e_hismin = hist_min(hp); } @@ -631,7 +638,7 @@ void ed_setup(register Edit_t *ep, int fd, int reedit) *pp++ = '\r'; { register int c; - while(c= *last++) switch(c) + while(prev = last, c = mbchar(last)) switch(c) { case ESC: { @@ -642,7 +649,7 @@ void ed_setup(register Edit_t *ep, int fd, int reedit) { if(pp < ppmax) *pp++ = c; - if(c=='\a') + if(c=='\a' || c==ESC || c=='\r') break; if(skip || (c>='0' && c<='9')) continue; @@ -651,6 +658,8 @@ void ed_setup(register Edit_t *ep, int fd, int reedit) else if(n>2 || (c!= '[' && c!= ']')) break; } + if(c==0 || c==ESC || c=='\r') + last--; qlen += (n+1); break; } @@ -693,17 +702,22 @@ void ed_setup(register Edit_t *ep, int fd, int reedit) } if(pp < ppmax) { - qlen += inquote; - *pp++ = c; - if(!inquote && !is_print(c)) + if(inquote) + qlen++; + else if(!is_print(c)) ep->e_crlf = 0; + if((qwid = last - prev) > 1) + qlen += qwid - mbwidth(c); + while(prev < last && pp < ppmax) + *pp++ = *prev++; } + break; } } if(pp-ep->e_prompt > qlen) ep->e_plen = pp - ep->e_prompt - qlen; *pp = 0; - if((ep->e_wsize -= ep->e_plen) < 7) + if(!ep->e_multiline && (ep->e_wsize -= ep->e_plen) < 7) { register int shift = 7-ep->e_wsize; ep->e_wsize = 7; @@ -736,7 +750,7 @@ void ed_setup(register Edit_t *ep, int fd, int reedit) #ifdef _cmd_tput char *term; if(!ep->e_term) - ep->e_term = nv_search("TERM",sh.var_tree,0); + ep->e_term = nv_search("TERM",shp->var_tree,0); if(ep->e_term && (term=nv_getval(ep->e_term)) && strlen(term)<sizeof(ep->e_termname) && strcmp(term,ep->e_termname)) { sh_trap(".sh.subscript=$(tput cuu1 2>/dev/null)",0); @@ -746,7 +760,7 @@ void ed_setup(register Edit_t *ep, int fd, int reedit) strcpy(ep->e_termname,term); } #endif - ep->e_wsize = MAXLINE - (ep->e_plen-2); + ep->e_wsize = MAXLINE - (ep->e_plen+1); } if(ep->e_default && (pp = nv_getval(ep->e_default))) { @@ -760,6 +774,19 @@ void ed_setup(register Edit_t *ep, int fd, int reedit) } } +static void ed_putstring(register Edit_t *ep, const char *str) +{ + register int c; + while(c = *str++) + ed_putchar(ep,c); +} + +static void ed_nputchar(register Edit_t *ep, int n, int c) +{ + while(n-->0) + ed_putchar(ep,c); +} + /* * Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set * Use sfpkrd() to poll() or select() to wait for input if possible @@ -774,8 +801,9 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit) register Edit_t *ep = (Edit_t*)context; register int rv= -1; register int delim = (ep->e_raw==RAWMODE?'\r':'\n'); + Shell_t *shp = ep->sh; int mode = -1; - int (*waitevent)(int,long,int) = sh.waitevent; + int (*waitevent)(int,long,int) = shp->waitevent; if(ep->e_raw==ALTMODE) mode = 1; if(size < 0) @@ -785,11 +813,53 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit) } sh_onstate(SH_TTYWAIT); errno = EINTR; - sh.waitevent = 0; + shp->waitevent = 0; while(rv<0 && errno==EINTR) { - if(sh.trapnote&(SH_SIGSET|SH_SIGTRAP)) + if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP)) goto done; + if(ep->sh->winch) + { + Edpos_t lastpos; + int n, rows, newsize; + /* move cursor to start of first line */ + ed_putchar(ep,'\r'); + ed_flush(ep); + astwinsize(2,&rows,&newsize); + n = (ep->e_plen+ep->e_cur)/++ep->e_winsz; + while(n--) + ed_putstring(ep,CURSOR_UP); + if(ep->e_multiline && newsize>ep->e_winsz && (lastpos.line=(ep->e_plen+ep->e_peol)/ep->e_winsz)) + { + /* clear the current command line */ + n = lastpos.line; + while(lastpos.line--) + { + ed_nputchar(ep,ep->e_winsz,' '); + ed_putchar(ep,'\n'); + } + ed_nputchar(ep,ep->e_winsz,' '); + while(n--) + ed_putstring(ep,CURSOR_UP); + } + ep->sh->winch = 0; + ed_flush(ep); + sh_delay(.05); + astwinsize(2,&rows,&newsize); + ep->e_winsz = newsize-1; + if(!ep->e_multiline && ep->e_wsize < MAXLINE) + ep->e_wsize = ep->e_winsz-2; + ep->e_nocrnl=1; + if(*ep->e_vi_insert) + { + buff[0] = ESC; + buff[1] = cntl('L'); + buff[2] = 'a'; + return(3); + } + buff[0] = cntl('L'); + return(1); + } /* an interrupt that should be ignored */ errno = 0; if(!waitevent || (rv=(*waitevent)(fd,-1L,0))>=0) @@ -824,7 +894,7 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit) rv = read(fd,buff,size); if(rv>=0 || errno!=EINTR) break; - if(sh.trapnote&(SH_SIGSET|SH_SIGTRAP)) + if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP)) goto done; /* an interrupt that should be ignored */ fixtime(); @@ -833,7 +903,7 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit) else if(rv>=0 && mode>0) rv = read(fd,buff,rv>0?rv:1); done: - sh.waitevent = waitevent; + shp->waitevent = waitevent; sh_offstate(SH_TTYWAIT); return(rv); } @@ -952,15 +1022,17 @@ int ed_getchar(register Edit_t *ep,int mode) ed_flush(ep); ep->e_inmacro = 0; /* The while is necessary for reads of partial multbyte chars */ + *ep->e_vi_insert = (mode==-2); if((n=ed_read(ep,ep->e_fd,readin,-LOOKAHEAD,0)) > 0) n = putstack(ep,readin,n,1); + *ep->e_vi_insert = 0; } if(ep->e_lookahead) { /* check for possible key mapping */ if((c = ep->e_lbuf[--ep->e_lookahead]) < 0) { - if(mode<=0 && sh.st.trap[SH_KEYTRAP]) + if(mode<=0 && ep->sh->st.trap[SH_KEYTRAP]) { n=1; if((readin[0]= -c) == ESC) @@ -1025,6 +1097,8 @@ void ed_putchar(register Edit_t *ep,register int c) char buf[8]; register char *dp = ep->e_outptr; register int i,size=1; + if(!dp) + return; buf[0] = c; #if SHOPT_MULTIBYTE /* check for place holder */ @@ -1075,7 +1149,14 @@ Edpos_t ed_curpos(Edit_t *ep,genchar *phys, int off, int cur, Edpos_t curpos) col = pos.col; } else + { pos.line = 0; + while(col > ep->e_winsz) + { + pos.line++; + col -= (ep->e_winsz+1); + } + } while(off-->0) { if(c) @@ -1097,60 +1178,69 @@ Edpos_t ed_curpos(Edit_t *ep,genchar *phys, int off, int cur, Edpos_t curpos) return(pos); } -static void ed_putstring(register Edit_t *ep, const char *str) -{ - register int c; - while(c = *str++) - ed_putchar(ep,c); -} - int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register int new,int first) { static int oldline; register int delta; + int clear = 0; Edpos_t newpos; delta = new - old; - if( delta == 0 ) + if(first < 0) + { + first = 0; + clear = 1; + } + if( delta == 0 && !clear) return(new); if(ep->e_multiline) { ep->e_curpos = ed_curpos(ep, physical, old,0,ep->e_curpos); + if(clear && old>=ep->e_peol && (clear=ep->e_winsz-ep->e_curpos.col)>0) + { + ed_nputchar(ep,clear,' '); + ed_nputchar(ep,clear,'\b'); + return(new); + } newpos = ed_curpos(ep, physical, new,old,ep->e_curpos); if(ep->e_curpos.col==0 && ep->e_curpos.line>0 && oldline<ep->e_curpos.line && delta<0) ed_putstring(ep,"\r\n"); oldline = newpos.line; if(ep->e_curpos.line > newpos.line) { - int n; + int n,pline,plen=ep->e_plen; for(;ep->e_curpos.line > newpos.line; ep->e_curpos.line--) ed_putstring(ep,CURSOR_UP); - if(newpos.line==0 && (n=ep->e_plen- ep->e_curpos.col)>0) + pline = plen/(ep->e_winsz+1); + if(newpos.line <= pline) + plen -= pline*(ep->e_winsz+1); + else + plen = 0; + if((n=plen- ep->e_curpos.col)>0) { ep->e_curpos.col += n; ed_putchar(ep,'\r'); - if(!ep->e_crlf) + if(!ep->e_crlf && pline==0) ed_putstring(ep,ep->e_prompt); else { - int m = ep->e_winsz+1-ep->e_plen; + int m = ep->e_winsz+1-plen; ed_putchar(ep,'\n'); - n = ep->e_plen; + n = plen; if(m < ed_genlen(physical)) { while(physical[m] && n-->0) ed_putchar(ep,physical[m++]); } - while(n-->0) - ed_putchar(ep,' '); + ed_nputchar(ep,n,' '); ed_putstring(ep,CURSOR_UP); } } } else if(ep->e_curpos.line < newpos.line) { - for(;ep->e_curpos.line < newpos.line;ep->e_curpos.line++) - ed_putchar(ep,'\n'); + ed_nputchar(ep, newpos.line-ep->e_curpos.line,'\n'); + ep->e_curpos.line = newpos.line; ed_putchar(ep,'\r'); ep->e_curpos.col = 0; } @@ -1161,18 +1251,24 @@ int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register newpos.line=0; if(delta<0) { + int bs= newpos.line && ep->e_plen>ep->e_winsz; /*** move to left ***/ delta = -delta; /*** attempt to optimize cursor movement ***/ - if(!ep->e_crlf || (2*delta <= ((old-first)+(newpos.line?0:ep->e_plen))) ) + if(!ep->e_crlf || bs || (2*delta <= ((old-first)+(newpos.line?0:ep->e_plen))) ) { - for( ; delta; delta-- ) - ed_putchar(ep,'\b'); + ed_nputchar(ep,delta,'\b'); + delta = 0; } else { if(newpos.line==0) ed_putstring(ep,ep->e_prompt); + else + { + first = 1+(newpos.line*ep->e_winsz - ep->e_plen); + ed_putchar(ep,'\r'); + } old = first; delta = new-first; } @@ -1244,6 +1340,7 @@ int ed_virt_to_phys(Edit_t *ep,genchar *virt,genchar *phys,int cur,int voff,int break; } *dp = 0; + ep->e_peol = dp-phys; return(r); } @@ -1375,8 +1472,7 @@ static int compare(register const char *a,register const char *b,register int n) * This version will use termios when possible, otherwise termio */ - -tcgetattr(int fd, struct termios *tt) +int tcgetattr(int fd, struct termios *tt) { register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); register int r,i; @@ -1398,7 +1494,7 @@ tcgetattr(int fd, struct termios *tt) return(r); } -tcsetattr(int fd,int mode,struct termios *tt) +int tcsetattr(int fd,int mode,struct termios *tt) { register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context); register int r; @@ -1446,6 +1542,7 @@ static int keytrap(Edit_t *ep,char *inbuff,register int insize, int bufsize, int { register char *cp; int savexit; + Shell_t *shp = ep->sh; #if SHOPT_MULTIBYTE char buff[MAXLINE]; ed_external(ep->e_inbuf,cp=buff); @@ -1465,16 +1562,19 @@ static int keytrap(Edit_t *ep,char *inbuff,register int insize, int bufsize, int nv_putval(ED_COLNOD,(char*)&ep->e_col,NV_NOFREE|NV_INTEGER); nv_putval(ED_TXTNOD,(char*)cp,NV_NOFREE); nv_putval(ED_MODENOD,ep->e_vi_insert,NV_NOFREE); - savexit = sh.savexit; - sh_trap(sh.st.trap[SH_KEYTRAP],0); - sh.savexit = savexit; + savexit = shp->savexit; + sh_trap(shp->st.trap[SH_KEYTRAP],0); + shp->savexit = savexit; if((cp = nv_getval(ED_CHRNOD)) == inbuff) nv_unset(ED_CHRNOD); - else + else if(bufsize>0) { strncpy(inbuff,cp,bufsize); + inbuff[bufsize-1]='\0'; insize = strlen(inbuff); } + else + insize = 0; nv_unset(ED_TXTNOD); return(insize); } diff --git a/usr/src/lib/libshell/common/edit/emacs.c b/usr/src/lib/libshell/common/edit/emacs.c index 0882693cc9..02842994ab 100644 --- a/usr/src/lib/libshell/common/edit/emacs.c +++ b/usr/src/lib/libshell/common/edit/emacs.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -108,6 +108,7 @@ typedef struct _emacs_ char CntrlO; char overflow; /* Screen overflow flag set */ char scvalid; /* Screen is up to date */ + char lastdraw; /* last update type */ int offset; /* Screen offset */ enum { @@ -195,6 +196,7 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit) } Prompt = prompt; ep->screen = Screen; + ep->lastdraw = FINAL; if(tty_raw(ERRIO,0) < 0) { return(reedit?reedit:ed_read(context, fd,buff,scend,0)); @@ -206,7 +208,6 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit) out = (genchar*)buff; #if SHOPT_MULTIBYTE out = (genchar*)roundof((char*)out-(char*)0,sizeof(genchar)); - ed_internal(buff,out); #endif /* SHOPT_MULTIBYTE */ if(!kstack) { @@ -231,6 +232,12 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit) i = sigsetjmp(env,0); if (i !=0) { + if(ep->ed->e_multiline) + { + cur = eol; + draw(ep,FINAL); + ed_flush(ep->ed); + } tty_cooked(ERRIO); if (i == UEOF) { @@ -588,8 +595,10 @@ update: } continue; case cntl('L'): - ed_crlf(ep->ed); + if(!ep->ed->e_nocrnl) + ed_crlf(ep->ed); draw(ep,REFRESH); + ep->ed->e_nocrnl = 0; continue; case cntl('[') : do_escape: @@ -649,6 +658,8 @@ update: location.hist_command = hline; /* save current position */ location.hist_line = hloff; #endif + cur = 0; + draw(ep,UPDATE); hist_copy((char*)out,MAXLINE, hline,hloff); #if SHOPT_MULTIBYTE ed_internal((char*)(out),out); @@ -1000,6 +1011,26 @@ static int escape(register Emacs_t* ep,register genchar *out,int count) switch(i=ed_getchar(ep->ed,1)) { case 'A': + if(cur>0 && eol==cur && (cur<(SEARCHSIZE-2) || ep->prevdirection == -2)) + { + if(ep->lastdraw==APPEND && ep->prevdirection != -2) + { + out[cur] = 0; + gencpy(&((genchar*)lstring)[1],out); +#if SHOPT_MULTIBYTE + ed_external(&((genchar*)lstring)[1],lstring+1); +#endif /* SHOPT_MULTIBYTE */ + *lstring = '^'; + ep->prevdirection = -2; + } + if(*lstring) + { + ed_ungetchar(ep->ed,'\r'); + ed_ungetchar(ep->ed,cntl('R')); + return(-1); + } + } + *lstring = 0; ed_ungetchar(ep->ed,cntl('P')); return(-1); case 'B': @@ -1189,6 +1220,8 @@ static void search(Emacs_t* ep,genchar *out,int direction) } i = genlen(string); + if(ep->prevdirection == -2 && i!=2 || direction!=1) + ep->prevdirection = -1; if (direction < 1) { ep->prevdirection = -ep->prevdirection; @@ -1264,6 +1297,7 @@ static void draw(register Emacs_t *ep,Draw_t option) sptr = drawbuff; logcursor = sptr + cur; longline = NORMAL; + ep->lastdraw = option; if (option == FIRST || option == REFRESH) { @@ -1377,6 +1411,9 @@ static void draw(register Emacs_t *ep,Draw_t option) } #endif /* SHOPT_MULTIBYTE */ } + if(ep->ed->e_multiline && option == REFRESH && ep->ed->e_nocrnl==0) + ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1); + /****************** @@ -1407,7 +1444,7 @@ static void draw(register Emacs_t *ep,Draw_t option) i = (ncursor-nscreen) - ep->offset; setcursor(ep,i,0); if(option==FINAL && ep->ed->e_multiline) - setcursor(ep,nscend-nscreen,0); + setcursor(ep,nscend+1-nscreen,0); ep->scvalid = 1; return; } diff --git a/usr/src/lib/libshell/common/edit/hexpand.c b/usr/src/lib/libshell/common/edit/hexpand.c index e9dbac5bcb..49444ca464 100644 --- a/usr/src/lib/libshell/common/edit/hexpand.c +++ b/usr/src/lib/libshell/common/edit/hexpand.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/edit/history.c b/usr/src/lib/libshell/common/edit/history.c index f98140fc0f..da875d70c4 100644 --- a/usr/src/lib/libshell/common/edit/history.c +++ b/usr/src/lib/libshell/common/edit/history.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -49,13 +49,23 @@ #define HIST_BSIZE 4096 /* size of history file buffer */ #define HIST_DFLT 512 /* default size of history list */ +#if SHOPT_AUDIT +# define _HIST_AUDIT Sfio_t *auditfp; \ + char *tty; \ + int auditmask; +#else +# define _HIST_AUDIT +#endif + #define _HIST_PRIVATE \ + void *histshell; \ off_t histcnt; /* offset into history file */\ off_t histmarker; /* offset of last command marker */ \ int histflush; /* set if flushed outside of hflush() */\ int histmask; /* power of two mask for histcnt */ \ char histbuff[HIST_BSIZE+1]; /* history file buffer */ \ int histwfail; \ + _HIST_AUDIT \ off_t histcmds[2]; /* offset for recent commands, must be last */ #define hist_ind(hp,c) ((int)((c)&(hp)->histmask)) @@ -96,7 +106,7 @@ int _Hist = 0; static void hist_marker(char*,long); -static void hist_trim(History_t*, int); +static History_t* hist_trim(History_t*, int); static int hist_nearend(History_t*,Sfio_t*, off_t); static int hist_check(int); static int hist_clean(int); @@ -119,10 +129,10 @@ static History_t *hist_ptr; static char *logname; # include <pwd.h> - int acctinit(void) + static int acctinit(History_t *hp) { register char *cp, *acctfile; - Namval_t *np = nv_search("ACCTFILE",sh.var_tree,0); + Namval_t *np = nv_search("ACCTFILE",((Shell_t*)hp->histshell)->var_tree,0); if(!np || !(acctfile=nv_getval(np))) return(0); @@ -135,7 +145,6 @@ static History_t *hist_ptr; cp = "unknown"; } logname = strdup(cp); - if((acctfd=sh_open(acctfile, O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && (unsigned)acctfd < 10) @@ -164,6 +173,42 @@ static History_t *hist_ptr; } #endif /* SHOPT_ACCTFILE */ +#if SHOPT_AUDIT +static int sh_checkaudit(History_t *hp, const char *name, char *logbuf, size_t len) +{ + Shell_t *shp = (Shell_t*)hp->histshell; + char *buff, *cp, *last; + int id1, id2, r=0, n, fd; + if((fd=open(name, O_RDONLY)) < 0) + return(0); + if((n = read(fd, logbuf,len-1)) < 0) + goto done; + while(logbuf[n-1]=='\n') + n--; + logbuf[n] = 0; + if(!(cp=strchr(logbuf,';')) && !(cp=strchr(logbuf,' '))) + goto done; + *cp = 0; + do + { + cp++; + id1 = id2 = strtol(cp,&last,10); + if(*last=='-') + id1 = strtol(last+1,&last,10); + if(shp->euserid >=id1 && shp->euserid <= id2) + r |= 1; + if(shp->userid >=id1 && shp->userid <= id2) + r |= 2; + cp = last; + } + while(*cp==';' || *cp==' '); +done: + close(fd); + return(r); + +} +#endif /*SHOPT_AUDIT*/ + static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION }; static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL}; @@ -179,8 +224,9 @@ static void hist_touch(void *handle) * cleaned up. * hist_open() returns 1, if history file is open */ -int sh_histinit(void) +int sh_histinit(void *sh_context) { + Shell_t *shp = (Shell_t*)sh_context; register int fd; register History_t *hp; register char *histname; @@ -189,7 +235,7 @@ int sh_histinit(void) register char *cp; register off_t hsize = 0; - if(sh.hist_ptr=hist_ptr) + if(shp->hist_ptr=hist_ptr) return(1); if(!(histname = nv_getval(HISTFILE))) { @@ -206,7 +252,7 @@ int sh_histinit(void) { /* reuse history file if same name */ wasopen = 0; - sh.hist_ptr = hist_ptr = hp; + shp->hist_ptr = hist_ptr = hp; if(strcmp(histname,hp->histname)==0) return(1); else @@ -243,7 +289,7 @@ retry: { #if KSHELL /* don't allow root a history_file in /tmp */ - if(sh.userid) + if(shp->userid) #endif /* KSHELL */ { if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*)))) @@ -265,7 +311,8 @@ retry: close(fd); return(0); } - sh.hist_ptr = hist_ptr = hp; + shp->hist_ptr = hist_ptr = hp; + hp->histshell = (void*)shp; hp->histsize = maxlines; hp->histmask = histmask; hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE); @@ -318,7 +365,7 @@ retry: sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); sfsync(sfstderr); #endif /* DEBUG */ - hist_trim(hp,(int)hp->histind-maxlines); + hp = hist_trim(hp,(int)hp->histind-maxlines); } sfdisc(hp->histfp,&hp->histdisc); #if KSHELL @@ -327,8 +374,31 @@ retry: sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); #if SHOPT_ACCTFILE if(sh_isstate(SH_INTERACTIVE)) - acctinit(); + acctinit(hp); #endif /* SHOPT_ACCTFILE */ +#if SHOPT_AUDIT + { + char buff[SF_BUFSIZE]; + hp->auditfp = 0; + if(sh_isstate(SH_INTERACTIVE) && (hp->auditmask=sh_checkaudit(hp,SHOPT_AUDITFILE, buff, sizeof(buff)))) + { + if((fd=sh_open(buff,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && fd < 10) + { + int n; + if((n = sh_fcntl(fd,F_DUPFD, 10)) >= 0) + { + sh_close(fd); + fd = n; + } + } + if(fd>=0) + { + hp->tty = strdup(ttyname(2)); + hp->auditfp = sfnew((Sfio_t*)0,NULL,-1,fd,SF_WRITE); + } + } + } +#endif return(1); } @@ -338,10 +408,19 @@ retry: void hist_close(register History_t *hp) { + Shell_t *shp = (Shell_t*)hp->histshell; sfclose(hp->histfp); +#if SHOPT_AUDIT + if(hp->auditfp) + { + if(hp->tty) + free((void*)hp->tty); + sfclose(hp->auditfp); + } +#endif /* SHOPT_AUDIT */ free((char*)hp); hist_ptr = 0; - sh.hist_ptr = 0; + shp->hist_ptr = 0; #if SHOPT_ACCTFILE if(acctfd) { @@ -376,7 +455,7 @@ static int hist_clean(int fd) * Copy the last <n> commands to a new file and make this the history file */ -static void hist_trim(History_t *hp, int n) +static History_t* hist_trim(History_t *hp, int n) { register char *cp; register int incmd=1, c=0; @@ -407,17 +486,16 @@ static void hist_trim(History_t *hp, int n) if(tmpname==name) tmpname = 0; } - hp = hist_ptr = 0; + hist_ptr = 0; if(fstat(sffileno(hist_old->histfp),&statb)>=0) { histinit = 1; histmode = statb.st_mode; } - if(!sh_histinit()) + if(!sh_histinit(hp->histshell)) { /* use the old history file */ - hist_ptr = hist_old; - return; + return hist_ptr = hist_old; } hist_new = hist_ptr; hist_ptr = hist_old; @@ -459,8 +537,7 @@ static void hist_trim(History_t *hp, int n) hist_new->histcnt += c; sfwrite(hist_new->histfp,buff,c); } - hist_ptr = hist_new; - hist_cancel(hist_ptr); + hist_cancel(hist_new); sfclose(hist_old->histfp); if(tmpname) { @@ -468,6 +545,7 @@ static void hist_trim(History_t *hp, int n) free(tmpname); } free((char*)hist_old); + return hist_ptr = hist_new; } /* @@ -668,7 +746,7 @@ void hist_flush(register History_t *hp) if(sfsync(hp->histfp)<0) { hist_close(hp); - if(!sh_histinit()) + if(!sh_histinit(hp->histshell)) sh_offoption(SH_HISTORY); } hp->histflush = 0; @@ -718,6 +796,15 @@ static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* *bufptr++ = '\n'; *bufptr++ = 0; size = bufptr - (char*)buff; +#if SHOPT_AUDIT + if(hp->auditfp) + { + Shell_t *shp = (Shell_t*)hp->histshell; + time_t t=time((time_t*)0); + sfprintf(hp->auditfp,"%u;%u;%s;%*s%c",sh_isoption(SH_PRIVILEGED)?shp->euserid:shp->userid,t,hp->tty,size,buff,0); + sfsync(hp->auditfp); + } +#endif /* SHOPT_AUDIT */ #if SHOPT_ACCTFILE if(acctfd) { @@ -876,7 +963,7 @@ Histloc_t hist_find(register History_t*hp,char *string,register int index1,int f } #if KSHELL /* allow a search to be aborted */ - if(sh.trapnote&SH_SIGSET) + if(((Shell_t*)hp->histshell)->trapnote&SH_SIGSET) break; #endif /* KSHELL */ } @@ -986,7 +1073,7 @@ char *hist_word(char *string,int size,int word) if(!hp) #if KSHELL { - strncpy(string,sh.lastarg,size); + strncpy(string,((Shell_t*)hp->histshell)->lastarg,size); return(string); } #else diff --git a/usr/src/lib/libshell/common/edit/vi.c b/usr/src/lib/libshell/common/edit/vi.c index 5d6fa8e56f..391999b5e9 100644 --- a/usr/src/lib/libshell/common/edit/vi.c +++ b/usr/src/lib/libshell/common/edit/vi.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -556,6 +556,11 @@ int ed_viread(void *context, int fd, register char *shbuf, int nchar, int reedit i = sigsetjmp(editb.e_env,0); if( i != 0 ) { + if(vp->ed->e_multiline) + { + cur_virt = last_virt; + sync_cursor(vp); + } virtual[0] = '\0'; tty_cooked(ERRIO); @@ -795,7 +800,7 @@ static int cntlmode(Vi_t *vp) case cntl('L'): /** Redraw line **/ /*** print the prompt and ***/ /* force a total refresh */ - if(vp->nonewline==0) + if(vp->nonewline==0 && !vp->ed->e_nocrnl) putchar('\n'); vp->nonewline = 0; pr_string(vp,Prompt); @@ -1472,6 +1477,7 @@ static void getline(register Vi_t* vp,register int mode) if( mode != SEARCH ) save_last(vp); refresh(vp,INPUT); + last_phys++; return; case '\t': /** command completion **/ @@ -1557,7 +1563,21 @@ static int mvcursor(register Vi_t* vp,register int motion) switch(motion=getcount(vp,ed_getchar(vp->ed,-1))) { case 'A': - ed_ungetchar(vp->ed,'k'); + if(cur_virt>=0 && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt) + { + virtual[last_virt + 1] = '\0'; + gencpy(&((genchar*)lsearch)[1], virtual); +#if SHOPT_MULTIBYTE + ed_external(&((genchar*)lsearch)[1],lsearch+1); +#endif /* SHOPT_MULTIBYTE */ + *lsearch = '^'; + vp->direction = -2; + ed_ungetchar(vp->ed,'n'); + } + else if(cur_virt==0 && vp->direction == -2) + ed_ungetchar(vp->ed,'n'); + else + ed_ungetchar(vp->ed,'k'); return(1); case 'B': ed_ungetchar(vp->ed,'j'); @@ -1927,6 +1947,9 @@ static void refresh(register Vi_t* vp, int mode) vp->long_line = vp->long_char; } + if(vp->ed->e_multiline && vp->ofirst_wind==INVALID && !vp->ed->e_nocrnl) + ed_setcursor(vp->ed, physical, last_phys+1, last_phys+1, -1); + vp->ed->e_nocrnl = 0; vp->ocur_phys = ncur_phys; vp->ocur_virt = cur_virt; vp->ofirst_wind = first_w; @@ -2103,6 +2126,8 @@ static int search(register Vi_t* vp,register int mode) register int i; Histloc_t location; + if( vp->direction == -2 && mode != 'n') + vp->direction = -1; if( mode == '/' || mode == '?') { /*** new search expression ***/ diff --git a/usr/src/lib/libshell/common/features/dynamic b/usr/src/lib/libshell/common/features/dynamic index 1835d41282..33f2d138e9 100644 --- a/usr/src/lib/libshell/common/features/dynamic +++ b/usr/src/lib/libshell/common/features/dynamic @@ -1,8 +1,8 @@ -hdr,sys dlfcn,dl,ldr,dll -hdr dlldefs -lib dlopen,shl_load,loadbind,dllload,dllfind cat{ - #if !defined(SHOPT_FS_3D) && ( _lib_dllfind || _lib_dlopen || _lib_shl_load || _lib_loadbind ) + #if SHOPT_DYNAMIC + #include <dlldefs.h> + #endif + #if !defined(SHOPT_FS_3D) && SHOPT_DYNAMIC # define SHOPT_FS_3D 1 #endif /* !SHOPT_FS_3D */ #if SHOPT_FS_3D diff --git a/usr/src/lib/libshell/common/features/externs b/usr/src/lib/libshell/common/features/externs index 58701306be..7d5e48d7c7 100644 --- a/usr/src/lib/libshell/common/features/externs +++ b/usr/src/lib/libshell/common/features/externs @@ -1,9 +1,9 @@ set prototyped hdr nc,exec_attr mem exception.name,_exception.name math.h -lib setreuid,setregid,nice,sigflag,fork,spawnveg +lib setreuid,setregid,nice,sigflag,fork,spawnveg,fchdir lib pathnative,pathposix,uwin_path,uwin_unpath,fts_notify -lib fchdir +lib memcntl sys/mman.h reference unistd.h diff --git a/usr/src/lib/libshell/common/features/math.sh b/usr/src/lib/libshell/common/features/math.sh index 1e407e57f7..4db6d7b3e3 100644 --- a/usr/src/lib/libshell/common/features/math.sh +++ b/usr/src/lib/libshell/common/features/math.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -20,7 +20,7 @@ : generate the ksh math builtin table : include math.tab -# @(#)math.sh (AT&T Research) 2007-02-02 +# @(#)math.sh (AT&T Research) 2008-08-29 command=$0 iffeflags="-n -v -F ast_standards.h" @@ -35,13 +35,20 @@ table=$1 names= tests= +: check long double + +eval `iffe $iffeflags -c "$cc" - typ long.double 2>&$stderr` + : read the table exec < $table while read type args name aka comment do case $type in [fi]) names="$names $name" - tests="$tests,$name,${name}l" + tests="$tests,$name" + case $_typ_long_double in + 1) tests="$tests,${name}l" ;; + esac eval TYPE_$name=$type ARGS_$name=$args AKA_$name=$aka ;; esac @@ -49,7 +56,7 @@ done : check the math library -eval `iffe $iffeflags -c "$cc" - typ long.double : lib $tests $iffehdrs $iffelibs 2>&$stderr` +eval `iffe $iffeflags -c "$cc" - lib $tests $iffehdrs $iffelibs 2>&$stderr` lib= for name in $names do eval x='$'_lib_${name}l y='$'_lib_${name} @@ -123,7 +130,7 @@ do eval x='$'_lib_${name}l y='$'_lib_${name} r='$'TYPE_${name} a='$'ARGS_${name} ;; esac case $local:$m:$n:$d in - 1:*:*:*|*:1:*:*|*:*::) + 1:*:*:*|*:1:*:*|*:*:1:) args= code="static $L local_$f(" sep= diff --git a/usr/src/lib/libshell/common/features/options b/usr/src/lib/libshell/common/features/options index 9c713d150e..c3f48b64d4 100644 --- a/usr/src/lib/libshell/common/features/options +++ b/usr/src/lib/libshell/common/features/options @@ -24,8 +24,9 @@ tst cross{ esac } - test -d /dev/fd + ls /dev/fd/9 9<&0 >/dev/null 2>&1 option DEVFD $? + exec 9<&- case `echo a | tr a '\012' | wc -l` in *1*) option MULTIBYTE 0 ;; esac diff --git a/usr/src/lib/libshell/common/features/poll b/usr/src/lib/libshell/common/features/poll index 7211a47806..6fa8fb308c 100644 --- a/usr/src/lib/libshell/common/features/poll +++ b/usr/src/lib/libshell/common/features/poll @@ -8,6 +8,12 @@ tst pipe_socketpair note{ use socketpair() for peekable pipe() }end execute{ #include <signal.h> #include <sys/types.h> #include <sys/socket.h> + #ifndef SHUT_RD + #define SHUT_RD 0 + #endif + #ifndef SHUT_WR + #define SHUT_WR 1 + #endif static void handler(sig) int sig; { @@ -24,8 +30,8 @@ tst pipe_socketpair note{ use socketpair() for peekable pipe() }end execute{ close(0); if (pipe(pfd) < 0 || socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0 || - shutdown(sfd[0], 1) < 0 || - shutdown(sfd[1], 0) < 0) + shutdown(sfd[1], SHUT_RD) < 0 || + shutdown(sfd[0], SHUT_WR) < 0) return(1); if ((pid = fork()) < 0) return(1); @@ -50,8 +56,8 @@ tst pipe_socketpair note{ use socketpair() for peekable pipe() }end execute{ close(sfd[0]); signal(SIGPIPE, handler); if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0 || - shutdown(sfd[0], 1) < 0 || - shutdown(sfd[1], 0) < 0) + shutdown(sfd[1], SHUT_RD) < 0 || + shutdown(sfd[0], SHUT_WR) < 0) return(1); close(sfd[0]); write(sfd[1], msg, sizeof(msg) - 1); diff --git a/usr/src/lib/libshell/common/features/sigfeatures b/usr/src/lib/libshell/common/features/sigfeatures index 1999dc11bb..58beaf8daa 100644 --- a/usr/src/lib/libshell/common/features/sigfeatures +++ b/usr/src/lib/libshell/common/features/sigfeatures @@ -7,8 +7,8 @@ cat{ #endif #ifdef _lib_sigprocmask # define sh_sigaction(s,action) do { sigset_t ss;\ - sigemptyset(&ss);\ - sigaddset(&ss,(s));\ + sigemptyset(&ss); \ + if(s) sigaddset(&ss,(s)); \ sigprocmask(action,&ss,0); \ }while(0) # define sigrelease(s) sh_sigaction(s,SIG_UNBLOCK) diff --git a/usr/src/lib/libshell/common/fun/mandelbrotset1 b/usr/src/lib/libshell/common/fun/mandelbrotset1 deleted file mode 100644 index fd39f387ec..0000000000 --- a/usr/src/lib/libshell/common/fun/mandelbrotset1 +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/ksh93 - -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" -# - -# -# mandelbrotset1 - a simple mandelbrot set generation and -# parallel execution demo -# - -# Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not POSIX-conformant -export PATH=/usr/xpg4/bin:/bin:/usr/bin:/usr/X11/bin:/usr/X11R6/bin:/usr/openwin/bin - -function printmsg -{ - print -u 2 "$@" -} - -function fatal_error -{ - print -u 2 "${progname}: $@" - exit 1 -} - -function print_color -{ - print -n "${symbollist:${1}:1}" -} - -function mandelbrot -{ - float x=$1 - float y=$2 - float xx - float yy - float x1=$3 - float y1=$4 - integer iteration=$5 - integer max_iteration=$6 - float mag - - for (( mag=0 ; mag < max_mag && iteration < max_iteration ; iteration++ )) ; do - (( xx=x*x )) - (( yy=y*y )) - (( mag=xx+yy )) - - (( y=x*y*2+y1 )) - (( x=xx-yy+x1 )) - done - - print ${iteration} - - return 0 -} - -function loop_serial -{ - for (( y=y_min ; y < y_max ; y+=stepwidth )) ; do - for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do - print_color $(mandelbrot ${x} ${y} ${x} ${y} 1 ${symbollistlen}) - done - - print - done -} - -function loop_parallel -{ - integer numjobs=0 - # the following calculation suffers from rounding errors - integer lines_per_job=$(( ((m_height+(numcpus-1)) / numcpus) )) - - printmsg "# lines_per_job=${lines_per_job}" - printmsg "# numcpus=${numcpus}" - - # "renice" worker jobs - set -o bgnice - - if [ "${TMPDIR}" = "" ] ; then - TMPDIR="/tmp" - fi - - # try to generate a job identifer prefix which is unique across multiple hosts - jobident="job_host_$(uname -n)pid_$$_ppid${PPID}" - - printmsg $"## prepare..." - for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do - rm -f "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" - - let numjobs++ - done - - printmsg $"## running ${numjobs} children..." - for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do - ( - for (( ; y < y_max && lines_per_job-- > 0 ; y+=stepwidth )) ; do - for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do - print_color $(mandelbrot ${x} ${y} ${x} ${y} 1 ${symbollistlen}) - done - - print - done >"${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" - ) & - done - - printmsg $"## waiting for ${numjobs} children..." - wait - - printmsg $"## output:" - for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do - print "$(cat "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput")" - rm "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" - done -} - -function usage -{ - OPTIND=0 - getopts -a "${progname}" "${USAGE}" OPT '-?' - exit 2 -} - -# main -builtin printf -builtin cat -builtin rm -builtin sleep -builtin uname # loop_parallel needs the ksh93 builtin version to generate unique job file names - -float x_max -float x_min -float y_max -float y_min -float m_width -float m_height -float max_mag -float stepwidth -integer numcpus - -# make sure ${COLUMN} and ${LINES} are set -eval $(resize -u) - -symbollist=' .:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%#' -symbollistlen=$(( ${#symbollist} - 1)) -mode="parallel" -progname="${0}" -max_mag=400 -stepwidth=0.1 -numcpus=16 - -let m_width=COLUMNS-1 m_height=LINES-2 - -progname="${0}" - -USAGE=$' -[-? -@(#)\$Id: mandelbrotset1 (Roland Mainz) 2007-06-05 \$ -] -[+NAME?mandelbrotset1 - generate mandelbrot set fractals with ksh93] -[+DESCRIPTION?\bmandelbrotset1\b mandelbrot set fractal generator - which runs either in serial or parallel mode (using multiple worker jobs).] -[w:width?Width of fractal.]:[width] -[h:height?Height of fractal.]:[height] -[s:symbols?Symbols to build the fractal from.]:[symbolstring] -[m:mag?Magnification level.]:[magnificationlevel] -[p:stepwidth?Width per step.]:[widthperstep] -[S:serial?Run in serial mode.] -[P:parallel?Run in parallel mode.] -[M:mode?Execution mode.]:[mode] -[C:numcpus?Number of processors used for parallel execution.]:[numcpus] -[+SEE ALSO?\bjuliaset1\b(1), \bksh93\b(1)] -' - -while getopts -a "${progname}" "${USAGE}" OPT ; do -# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" - case ${OPT} in - w) m_width="${OPTARG}" ;; - h) m_height="${OPTARG}" ;; - s) symbollist="${OPTARG}" ;; - m) max_mag="${OPTARG}" ;; - p) stepwidth="${OPTARG}" ;; - S) mode="serial" ;; - P) mode="parallel" ;; - M) mode="${OPTARG}" ;; - C) numcpus="${OPTARG}" ;; - *) usage ;; - esac -done -shift ${OPTIND}-1 - -printmsg "# width=${m_width}" -printmsg "# height=${m_height}" -printmsg "# max_mag=${max_mag}" -printmsg "# stepwidth=${stepwidth}" -printmsg "# symbollist='${symbollist}'" -printmsg "# mode=${mode}" - -symbollistlen=$(( ${#symbollist} - 1)) - -let x_max=m_width*stepwidth/2. x_min=-x_max -let y_max=m_height*stepwidth/2. y_min=-y_max - -case "${mode}" in - parallel) loop_parallel ;; - serial) loop_serial ;; - *) fatal_error $"Unknown mode \"${mode}\"." -esac - -# EOF. diff --git a/usr/src/lib/libshell/common/fun/rssread b/usr/src/lib/libshell/common/fun/rssread deleted file mode 100644 index e561cb3587..0000000000 --- a/usr/src/lib/libshell/common/fun/rssread +++ /dev/null @@ -1,414 +0,0 @@ -#!/bin/ksh93 - -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" -# - -# -# rssread - a simple RSS2.0 reader with RSS to XHTML to -# plaintext conversion. -# - -# Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not POSIX-conformant -export PATH=/usr/xpg4/bin:/bin:/usr/bin - -function printmsg -{ - print -u 2 "$@" -} - -function debugmsg -{ -# printmsg "$@" -true -} - -function fatal_error -{ - print -u 2 "${progname}: $@" - exit 1 -} - -function cat_http -{ -( - protocol="${1%://*}" - path1="${1#*://}" # "http://foo.bat.net/x/y.html" ----> "foo.bat.net/x/y.html" - - host="${path1%%/*}" - path="${path1#*/}" - port="${host##*:}" - - # If URL did not contain a port number in the host part then look at the - # protocol to get the port number - if [ "${port}" = "${host}" ] ; then - case "${protocol}" in - "http") port=80 ;; - *) port="$(getent services "${protocol}" | sed 's/[^0-9]*//;s/\/.*//')" ;; - esac - else - host="${host%:*}" - fi - - printmsg "protocol=${protocol} port=${port} host=${host} path=${path}" - - # prechecks - [ "${protocol}" = "" ] && fatal_error "protocol not set." - [ "${port}" = "" ] && fatal_error "port not set." - [ "${host}" = "" ] && fatal_error "host not set." - [ "${path}" = "" ] && fatal_error "path not set." - - # open TCP channel - exec 3<>"/dev/tcp/${host}/${port}" - - # send HTTP request - request="GET /${path} HTTP/1.0\n" - request+="Host: ${host}\n" - request+="User-Agent: ksh93/rssread (2007-01-16; $(uname -s -r -p))\n" - print "${request}\n" >&3 - - # collect response and send it to stdout - cat <&3 -) -} - -function html_entity_to_ascii -{ - typeset -A entity_cache - - # Todo: Add more HTML/MathML entities here - entity_cache["nbsp"]=" " - entity_cache["lt"]="<" - entity_cache["gt"]=">" - entity_cache["amp"]="&" - entity_cache["quot"]="\"" - entity_cache["apos"]="'" - - buf="" - while read -r -N 1 c ; do - if [ "$c" != "&" ] ; then - printf "%s" "${c}" - continue - fi - - entity="" - while read -r -N 1 c ; do - case "$c" in - ";") - break - ;; - ~(Eilr)[a-z0-9#]) - entity+="$c" - continue - ;; - *) - debugmsg "error &${entity}${c}#" - - print -n "${entity}${c}" - entity="" - continue 2 - ;; - esac - done - - value="" - if [ "${entity_cache["${entity}"]}" != "" ] ; then - debugmsg "match #${entity}# = #${entity_cache["${entity}"]}#" - value="${entity_cache["${entity}"]}" - else - if [ "${entity:0:1}" = "#" ] ; then - # decimal literal - value="$(printf "\u[$(printf "%x" "${entity:1:8}")]")" - elif [[ "${entity:0:7}" = ~(Eilr)[0-9a-f]* ]] ; then - # hexadecimal literal - value="$(printf "\u[${entity:0:7}]")" - else - # unknown literal - pass-through - value="<ENT=${entity}>" - fi - - entity_cache["${entity}"]="${value}" - - debugmsg "lookup #${entity}# = #${entity_cache["${entity}"]}#" - fi - - printf "%s" "$value" - done -} - -# dumb xhtml handler - no CSS, tables, images, iframes or nested -# structures are supported (and we assume that the input is correct -# xhtml). The code was written in a trial&&error manner and should be -# rewritten to parse xhtml correctly. -function handle_html -{ - # we can't use global variables here when multiple callbacks use the same - # callback function - but we can use the callback associative array for - # variable storage instead - nameref callbacks=${1} - tag_type="$2" - tag_value="$3" - - case "${tag_type}" in - tag_begin) - case "${tag_value}" in - br*) printf "\n" ;; - hr*) printf "\n-------------------------------------\n" ;; - pre*) callbacks["html_pre"]=1 ;; - p*) printf "\n" ;; - esac - ;; - - tag_end) - case "${tag_value}" in - pre*) callbacks["html_pre"]=0 ;; - esac - ;; - - tag_text) - if [ ${callbacks["html_pre"]} -eq 1 ] ; then - printf "%s" "${tag_value}" - else - # compress spaces/newlines/tabs/etc. - printf "%s" "${tag_value/+([\n\r\t\v[:space:][:blank:]])/ }" - fi - ;; - - document_start) - callbacks["html_pre"]=0 - ;; - document_end) ;; - esac -} - -function handle_rss -{ - # we can't use global variables here when multiple callbacks use the same - # callback function - but we can use the callback associative array for - # variable storage instead - nameref callbacks=${1} - tag_type="$2" - tag_value="$3" - - case "${tag_type}" in - tag_begin) - case "${tag_value}" in - item*) - item["title"]="" - item["link"]="" - item["tag"]="" - item["description"]="" - ;; - esac - callbacks["textbuf"]="" - ;; - tag_end) - case "${tag_value}" in - item*) - # note that each RSS item needs to be converted seperately from RSS to HTML to plain text - # to make sure that the state of one RSS item doesn't affect others - ( - printf $"<br />#### RSS item: title: %s ####" "${item["title"]}" - printf $"<br />## author: %s" "${item["author"]}" - printf $"<br />## link: %s" "${item["link"]}" - printf $"<br />## date: %s" "${item["pubDate"]}" - printf $"<br />## begin description:" - printf $"<br />%s<br />" "${item["description"]}" - printf $"<br />## end description<br />" - print # extra newline to make sure the sed pipeline gets flushed - ) | - html_entity_to_ascii | # convert XML entities (e.g. decode RSS content to HTML code) - xml_tok "xhtmltok_cb" | # convert HTML to plain text - html_entity_to_ascii # convert HTML entities - ;; - title*) item["title"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; - link*) item["link"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; - dc:creator* | author*) item["author"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; - dc:date* | pubDate*) item["pubDate"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; - description*) item["description"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; - esac - callbacks["textbuf"]="" - ;; - tag_text) - callbacks["textbuf"]+="${tag_value}" - ;; - document_start) ;; - document_end) ;; - esac -} - -function xml_tok -{ - typeset buf="" - typeset c="" - - nameref callbacks=${1} - - [ ! -z "${callbacks["document_start"]}" ] && ${callbacks["document_start"]} "${1}" "document_start" - - while read -N 1 c -d '\0'; do - isendtag=false - - if [ "$c" = "<" ] ; then - if [ "$buf" != "" ] ; then - [ ! -z "${callbacks["tag_text"]}" ] && ${callbacks["tag_text"]} "${1}" "tag_text" "$buf" - buf="" - fi - - read -N 1 c -d '\0' - if [ "$c" = "/" ] ; then - isendtag=true - else - buf="$c" - fi - read -d '>' c - buf+="$c" - - if ${isendtag} ; then - [ ! -z "${callbacks["tag_end"]}" ] && ${callbacks["tag_end"]} "${1}" "tag_end" "$buf" - else - [ ! -z "${callbacks["tag_begin"]}" ] && ${callbacks["tag_begin"]} "${1}" "tag_begin" "$buf" - - # handle tags like <br/> (which are start- and end-tag in one piece) - if [[ "${buf}" = ~(Er).*/ ]] ; then - [ ! -z "${callbacks["tag_end"]}" ] && ${callbacks["tag_end"]} "${1}" "tag_end" "$buf" - fi - fi - buf="" - else - buf+="$c" - fi - done - - [ ! -z "${callbacks["document_end"]}" ] && ${callbacks["document_start"]} "${1}" "document_end" "exit_success" - - print # final newline to make filters like "sed" happy -} - -# return the value of LC_MESSAGES needed for subprocesses which -# want to run in a different locale/encoding -function get_lc_messages -{ - [ "${LC_ALL}" != "" ] && { print "${LC_ALL}" ; return 0 ; } - [ "${LC_MESSAGES}" != "" ] && { print "${LC_MESSAGES}" ; return 0 ; } - [ "${LANG}" != "" ] && { print "${LANG}" ; return 0 ; } - print "C" ; return 0 -} - -function usage -{ - OPTIND=0 - getopts -a "${progname}" "${USAGE}" OPT '-?' - exit 2 -} - -# make sure we use the ksh93 builtin versions -builtin cat -builtin printf - -typeset -A rsstok_cb # callbacks for xml_tok -rsstok_cb["tag_begin"]="handle_rss" -rsstok_cb["tag_end"]="handle_rss" -rsstok_cb["tag_text"]="handle_rss" -rsstok_cb["textbuf"]="" - -typeset -A xhtmltok_cb # callbacks for xml_tok -xhtmltok_cb["tag_begin"]="handle_html" -xhtmltok_cb["tag_end"]="handle_html" -xhtmltok_cb["tag_text"]="handle_html" -xhtmltok_cb["textbuf"]="" -xhtmltok_cb["html_pre"]=0 - -typeset -A item - -typeset -A bookmark_urls - -# "ramdom" urls for testing -bookmark_urls=( - ["google_blogs_ksh"]="http://blogsearch.google.com/blogsearch_feeds?hl=en&scoring=d&q=ksh&ie=utf-8&num=100&output=rss" - ["ksh93_integration"]="http://www.opensolaris.org/rss/os/project/ksh93-integration/announcements/rss2.xml" - ["blogs_sun_com"]="http://blogs.sun.com/main/feed/entries/rss" - ["jmcp"]="http://www.jmcp.homeunix.com/roller/rss/jmcp" - ["katakai"]="http://blogs.sun.com/katakai/feed/entries/rss" - ["planetsun"]="http://www.planetsun.org/rss20.xml" - ["planetsolaris"]="http://www.planetsolaris.org/rss20.xml" - ["planetopensolaris"]="http://planet.opensolaris.org/rss20.xml" -) - -progname="${0}" - -USAGE=$' -[-? -@(#)\$Id: rssread (Roland Mainz) 2007-06-05 \$ -] -[+NAME?rssread - fetch RSS messages and convert them to plain text] -[+DESCRIPTION?\brssread\b RSS to plain text converter - which fetches RSS streams via HTTP and converts them from RSS to HTML to plain UTF-8 text.] - -[ url ] - -[+SEE ALSO?\bksh93\b(1)] -' - -while getopts -a "${progname}" "${USAGE}" OPT ; do -# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" - case ${OPT} in - *) usage ;; - esac -done -shift ${OPTIND}-1 - -url="$1" - -if [ "$url" = "" ] ; then - fatal_error $"No url given." -fi - -if [ "${bookmark_urls[${url}]}" != "" ] ; then - printmsg $"Using bookmark ${url} = ${bookmark_urls[${url}]}" - url="${bookmark_urls[${url}]}" -fi - -( - # set unicode locale since RSS is encoded in UTF-8 - # (and make sure $LC_MESSAGES is set to the parent - # process's locale that all error messages are using - # the callers locale/encoding) - export \ - LC_MESSAGES="$(get_lc_messages)" \ - LC_MONETARY="en_US.UTF-8" \ - LC_NUMERIC="en_US.UTF-8" \ - LC_COLLATE="en_US.UTF-8" \ - LC_CTYPE="en_US.UTF-8" \ - LC_TIME="en_US.UTF-8" \ - LANG="en_US.UTF-8" - - cat_http "$url" | - xml_tok "rsstok_cb" -) # | iconv -f "UTF-8" - - - -#EOF. diff --git a/usr/src/lib/libshell/common/fun/termclock b/usr/src/lib/libshell/common/fun/termclock deleted file mode 100644 index 9bf0cf6d85..0000000000 --- a/usr/src/lib/libshell/common/fun/termclock +++ /dev/null @@ -1,267 +0,0 @@ -#!/bin/ksh93 - -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" -# - -# -# termclock - a simple analog clock for terminals -# - -# Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not POSIX-conformant -export PATH=/usr/xpg4/bin:/bin:/usr/bin - -function fatal_error -{ - print -u 2 "${progname}: $@" - exit 1 -} - -# cache tput values (to avoid |fork()|'ing a "tput" child every second) -function tput_cup -{ - integer y="$1" x="$2" - nameref c=tput_cup_cache["${y}_${x}"] - - if [ "$c" == "" ] ; then - # fast path for known terminal types - if [[ ${TERM} = ~(Elr)(vt100|vt220|xterm|xterm-color|dtterm) ]] ; then - c="$(printf "\E[%d;%dH" $((y+1)) $((x+1)))" - else - c="$(tput cup $y $x)" - fi - fi - - print -n "$c" -} - -function draw_clock -{ - float angle a - float x y - - for(( angle=0.0 ; angle < 360. ; angle+=6 )) ; do - (( a=angle/360.*(2*M_PI) )) - - (( x=clock.len_x*cos(a) )) - (( y=clock.len_y*sin(a) )) - tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x )) - - # add "mark" every 30 degrees - if (( int(angle)%30 == 0 )) ; then - print -n "0" - else - print -n "x" - fi - done -} - -function draw_hand -{ - float angle="$1" a - typeset ch="$2" - float length="$3" - float x y - - (( a=angle/360.*(2*M_PI) )) - - for(( s=0.0 ; s < 10. ; s+=0.5 )) ; do - (( x=(clock.len_x*(s/10.)*(length/100.))*cos(a) )) - (( y=(clock.len_y*(s/10.)*(length/100.))*sin(a) )) - - tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x )) - print -n "${ch}" - done -} - -function draw_clock_hand -{ - nameref hand=$1 - - draw_hand $(( 360.*(hand.val/hand.scale)-90. )) "${hand.ch}" ${hand.length} -} - -function clear_clock_hand -{ - nameref hand=$1 - - draw_hand $(( 360.*(hand.val/hand.scale)-90. )) " " ${hand.length} -} - -function main_loop -{ - typeset c - - # note: we can't use subshells when writing to the double-buffer file because this - # will render the tput value cache useless - while true ; do - if ${init_screen} ; then - init_screen="false" - - # "resize" is needed because older versions of ksh93 may have - # trouble with getting the right terminal size at startup - [ -x "/usr/X11/bin/resize" ] && eval "$(/usr/X11/bin/resize -u)" || - [ -x "/usr/X11R6/bin/resize" ] && eval "$(/usr/X11R6/bin/resize -u)" || - [ -x "/usr/openwin/bin/resize" ] && eval "$(/usr/openwin/bin/resize -u)" || - fatal_error "resize not found." - - (( clock.middle_x=COLUMNS/2.-.5 )) - (( clock.middle_y=LINES/2.-.5 )) - (( clock.len_x=COLUMNS/2-2 )) - (( clock.len_y=LINES/2-2 )) - - { - clear - draw_clock - } >&6 - fi - - { - (( $(date +"hours.val=%H , minutes.val=%M , seconds.val=%S") )) - - # small trick to get a smooth "analog" flair - (( hours.val+=minutes.val/60. )) - (( minutes.val+=seconds.val/60. )) - - draw_clock_hand seconds - draw_clock_hand minutes - draw_clock_hand hours - - # move cursor to home position - tput_cup 0 0 - } >&6 - - 6<#((0)) - cat <&6 - - 6<&- ; rm -f "${scratchfile}" ; exec 6<>"${scratchfile}" - - c="" ; read -t ${update_interval} -n 1 c - if [ "$c" != "" ] ; then - case "$c" in - ~(Ei)q | $'\E') return 0 ;; - esac - fi - - { - clear_clock_hand hours - clear_clock_hand minutes - clear_clock_hand seconds - } >&6 - done -} - -function usage -{ - OPTIND=0 - getopts -a "${progname}" "${USAGE}" OPT '-?' - exit 2 -} - -# program start -progname="${0}" - -builtin date -builtin rm -builtin printf - -typeset -A tput_cup_cache - -float -r M_PI=3.14159265358979323846 - -clock=( - float middle_x - float middle_y - integer len_x - integer len_y -) - -typeset init_screen="true" - -# set clock properties -seconds=( float val - typeset ch - float scale - integer length ) -minutes=( float val - typeset ch - float scale - integer length ) -hours=( float val - typeset ch - float scale - integer length ) - -seconds.length=90 seconds.scale=60 seconds.ch="s" -minutes.length=75 minutes.scale=60 minutes.ch="m" -hours.length=50 hours.scale=12 hours.ch="h" - -float update_interval=0.9 - -USAGE=$' -[-? -@(#)\$Id: termclock (Roland Mainz) 2007-06-05 \$ -] -[+NAME?termclock - analog clock for terminals] -[+DESCRIPTION?\btermclock\b is an analog clock for terminals. - The termclock program displays the time in analog or digital - form. The time is continuously updated at a frequency which - may be specified by the user.] -[u:update?Update interval (defaults to 0.9 seconds).]:[interval] -[+SEE ALSO?\bksh93\b(1), \bxclock\b(1)] -' - -while getopts -a "${progname}" "${USAGE}" OPT ; do -# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" - case ${OPT} in - u) update_interval=${OPTARG} ;; - *) usage ;; - esac -done -shift ${OPTIND}-1 - -# prechecks -which tput >/dev/null || fatal_error "tput not found." -which mktemp >/dev/null || fatal_error "mktemp not found." -(( update_interval < 0. || update_interval > 7200. )) && fatal_error "invalid update_interval value." - -# create temporary file for double-buffering and register an EXIT trap -# to remove this file when the shell interpreter exits -scratchfile="$(mktemp /tmp/termclock.pid$$.XXXXXX)" -if [ -z "${scratchfile}" ]; then exit 1; fi -trap 'rm -f "${scratchfile}"' EXIT -rm -f "${scratchfile}" ; exec 6<>"${scratchfile}" - -# regiter trap to handle window size changes -trap 'init_screen="true"' WINCH - -main_loop - -# exiting - put cursor below clock -tput_cup $((LINES-2)) 0 - -# EOF. diff --git a/usr/src/lib/libshell/common/include/argnod.h b/usr/src/lib/libshell/common/include/argnod.h index fd93e5b215..7d59fb41f1 100644 --- a/usr/src/lib/libshell/common/include/argnod.h +++ b/usr/src/lib/libshell/common/include/argnod.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -69,10 +69,10 @@ struct slnod /* struct for link list of stacks */ struct dolnod { - short dolrefcnt; /* reference count */ - short dolmax; /* size of dolval array */ - short dolnum; /* number of elements */ - short dolbot; /* current first element */ + int dolrefcnt; /* reference count */ + int dolmax; /* size of dolval array */ + int dolnum; /* number of elements */ + int dolbot; /* current first element */ struct dolnod *dolnxt; /* used when list are chained */ char *dolval[1]; /* array of value pointers */ }; @@ -123,16 +123,12 @@ struct argnod #define ARG_OPTIMIZE 0x200 /* try to optimize */ #define ARG_NOGLOB 0x400 /* no file name expansion */ #define ARG_LET 0x800 /* processing let command arguments */ +#define ARG_ARRAYOK 0x1000 /* $x[sub] ==> ${x[sub]} */ -extern char **sh_argbuild(int*,const struct comnod*,int); extern struct dolnod *sh_argcreate(char*[]); -extern char *sh_argdolminus(void); -extern struct dolnod *sh_argfree(struct dolnod*,int); -extern struct dolnod *sh_argnew(char*[],struct dolnod**); -extern int sh_argopts(int,char*[]); -extern void sh_argreset(struct dolnod*,struct dolnod*); -extern void sh_argset(char*[]); -extern struct dolnod *sh_arguse(void); +extern char *sh_argdolminus(void*); +extern int sh_argopts(int,char*[],void*); + extern const char e_heading[]; extern const char e_off[]; diff --git a/usr/src/lib/libshell/common/include/builtins.h b/usr/src/lib/libshell/common/include/builtins.h index 8c79075fce..9fc32de97d 100644 --- a/usr/src/lib/libshell/common/include/builtins.h +++ b/usr/src/lib/libshell/common/include/builtins.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -39,8 +39,10 @@ #define SYSBRACKET (sh.bltin_cmds+11) #define SYSLET (sh.bltin_cmds+12) #define SYSEXPORT (sh.bltin_cmds+13) +#define SYSDOT (sh.bltin_cmds+14) +#define SYSRETURN (sh.bltin_cmds+15) #if SHOPT_BASH -# define SYSLOCAL (sh.bltin_cmds+14) +# define SYSLOCAL (sh.bltin_cmds+16) #else # define SYSLOCAL 0 #endif @@ -54,6 +56,7 @@ extern int b_alias(int, char*[],void*); extern int b_break(int, char*[],void*); extern int b_dot_cmd(int, char*[],void*); +extern int b_enum(int, char*[],void*); extern int b_exec(int, char*[],void*); extern int b_eval(int, char*[],void*); extern int b_return(int, char*[],void*); @@ -123,7 +126,6 @@ extern const char e_overlimit[]; extern const char e_eneedsarg[]; extern const char e_toodeep[]; extern const char e_badname[]; -extern const char e_badwrite[]; extern const char e_badsyntax[]; #ifdef _cmd_universe extern const char e_nouniverse[]; @@ -131,7 +133,6 @@ extern const char e_badsyntax[]; extern const char e_histopen[]; extern const char e_condition[]; extern const char e_badrange[]; -extern const char e_numeric[]; extern const char e_trap[]; extern const char e_direct[]; extern const char e_defedit[]; diff --git a/usr/src/lib/libshell/common/include/defs.h b/usr/src/lib/libshell/common/include/defs.h index 91a3d2c3e1..78263fefb8 100644 --- a/usr/src/lib/libshell/common/include/defs.h +++ b/usr/src/lib/libshell/common/include/defs.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -25,6 +25,8 @@ * Shell interface private definitions * */ +#ifndef defs_h_defined +#define defs_h_defined #include <ast.h> #include <sfio.h> @@ -34,11 +36,17 @@ #include <history.h> #include "fault.h" #include "argnod.h" +#include "name.h" +#define _SH_PRIVATE +#include <shcmd.h> +#undef _SH_PRIVATE #ifndef pointerof #define pointerof(x) ((void*)((char*)0+(x))) #endif +#define Empty ((char*)(e_sptbnl+3)) + #define env_change() (++ast.env_serial) #if SHOPT_ENV # include <env.h> @@ -52,6 +60,8 @@ * note that the first few fields have to be the same as for * Shscoped_t in <shell.h> */ + + struct sh_scoped { struct sh_scoped *prevst; /* pointer to previous state */ @@ -59,6 +69,7 @@ struct sh_scoped char **dolv; char *cmdname; char *filename; + char *funname; int lineno; Dt_t *save_tree; /* var_tree for calling function */ struct sh_scoped *self; /* pointer to copy of this scope*/ @@ -80,6 +91,7 @@ struct sh_scoped char **trapcom; char **otrapcom; void *timetrap; + struct Ufunction *real_fun; /* current 'function name' function */ }; struct limits @@ -97,6 +109,7 @@ struct limits #define _SH_PRIVATE \ struct sh_scoped st; /* scoped information */ \ struct limits lim; /* run time limits */ \ + Stk_t *stk; /* stack poiter */ \ Sfio_t *heredocs; /* current here-doc temp file */ \ Sfio_t *funlog; /* for logging function definitions */ \ int **fdptrs; /* pointer to file numbers */ \ @@ -105,18 +118,20 @@ struct limits char *lastpath; /* last alsolute path found */ \ int path_err; /* last error on path search */ \ Dt_t *track_tree; /* for tracked aliases*/ \ - Namval_t *bltin_nodes; /* pointer to built-in variables */ \ Dt_t *var_base; /* global level variables */ \ Namval_t *namespace; /* current active namespace*/ \ Namval_t *last_table; /* last table used in last nv_open */ \ + Namval_t *prev_table; /* previous table used in nv_open */ \ Sfio_t *outpool; /* ouput stream pool */ \ long timeout; /* read timeout */ \ short curenv; /* current subshell number */ \ short jobenv; /* subshell number for jobs */ \ + int infd; /* input file descriptor */ \ int nextprompt; /* next prompt is PS<nextprompt> */ \ + int bltin_nnodes; /* number of bltins nodes */ \ + Namval_t *bltin_nodes; /* pointer to built-in variables */ \ Namval_t *bltin_cmds; /* pointer to built-in commands */ \ Namval_t *posix_fun; /* points to last name() function */ \ - int infd; /* input file descriptor */ \ char *outbuff; /* pointer to output buffer */ \ char *errbuff; /* pointer to stderr buffer */ \ char *prompt; /* pointer to prompt string */ \ @@ -133,6 +148,7 @@ struct limits pid_t pid; /* process id of shell */ \ pid_t bckpid; /* background process id */ \ pid_t cpid; \ + pid_t spid; /* subshell process id */ \ int32_t ppid; /* parent process id of shell */ \ int topfd; \ int sigmax; /* maximum number of signals */ \ @@ -144,7 +160,11 @@ struct limits char forked; \ char binscript; \ char deftype; \ + char funload; \ char used_pos; /* used postional parameter */\ + char universe; \ + char winch; \ + char indebug; /* set when in debug trap */ \ unsigned char lastsig; /* last signal received */ \ char *readscript; /* set before reading a script */ \ int *inpipe; /* input pipe pointer */ \ @@ -155,11 +175,13 @@ struct limits struct argnod *envlist; \ struct dolnod *arglist; \ int fn_depth; \ + int fn_reset; \ int dot_depth; \ int hist_depth; \ int xargmin; \ int xargmax; \ int xargexit; \ + int nenv; \ mode_t mask; \ long nforks; \ Env_t *env; \ @@ -174,27 +196,36 @@ struct limits void *cdpathlist; \ char **argaddr; \ void *optlist; \ - int optcount ; \ struct sh_scoped global; \ struct checkpt checkbase; \ Shinit_f userinit; \ Shbltin_f bltinfun; \ + Shbltin_t bltindata; \ Shwait_f waitevent; \ char *cur_line; \ char *rcfile; \ char **login_files; \ - short offsets[10]; \ + int offsets[10]; \ Sfio_t **sftable; \ unsigned char *fdstatus; \ const char *pwd; \ History_t *hist_ptr; \ - char universe; \ void *jmpbuffer; \ void *mktype; \ Sfio_t *strbuf; \ + Sfio_t *strbuf2; \ Dt_t *last_root; \ + Dt_t *prev_root; \ + Dt_t *fpathdict; \ + Dt_t *typedict; \ char ifstable[256]; \ - Shopt_t offoptions; + unsigned char sigruntime[2]; \ + unsigned long test; \ + Shopt_t offoptions; \ + Shopt_t glob_options; \ + Namval_t *typeinit; \ + int *stats; \ + Namfun_t nvfun; #include <shell.h> @@ -302,13 +333,22 @@ struct limits #define MATCH_MAX 64 +#define SH_READEVAL 0x4000 /* for sh_eval */ + +extern Shell_t *nv_shell(Namval_t*); extern int sh_addlib(void*); +extern void sh_applyopts(Shell_t*,Shopt_t); +extern char **sh_argbuild(Shell_t*,int*,const struct comnod*,int); +extern struct dolnod *sh_argfree(Shell_t *, struct dolnod*,int); +extern struct dolnod *sh_argnew(Shell_t*,char*[],struct dolnod**); extern void *sh_argopen(Shell_t*); +extern void sh_argreset(Shell_t*,struct dolnod*,struct dolnod*); extern Namval_t *sh_assignok(Namval_t*,int); +extern struct dolnod *sh_arguse(Shell_t*); extern char *sh_checkid(char*,char*); -extern int sh_debug(const char*,const char*,const char*,char *const[],int); +extern int sh_debug(Shell_t *shp,const char*,const char*,const char*,char *const[],int); extern int sh_echolist(Sfio_t*, int, char**); -extern struct argnod *sh_endword(int); +extern struct argnod *sh_endword(Shell_t*,int); extern char **sh_envgen(void); #if SHOPT_ENV extern void sh_envput(Env_t*, Namval_t*); @@ -318,25 +358,30 @@ extern Sfdouble_t sh_arith(const char*); extern void *sh_arithcomp(char*); extern pid_t sh_fork(int,int*); extern pid_t _sh_fork(pid_t, int ,int*); -extern char *sh_mactrim(char*,int); -extern int sh_macexpand(struct argnod*,struct argnod**,int); -extern void sh_machere(Sfio_t*, Sfio_t*, char*); +extern char *sh_mactrim(Shell_t*,char*,int); +extern int sh_macexpand(Shell_t*,struct argnod*,struct argnod**,int); +extern int sh_macfun(Shell_t*,const char*,int); +extern void sh_machere(Shell_t*,Sfio_t*, Sfio_t*, char*); extern void *sh_macopen(Shell_t*); -extern char *sh_macpat(struct argnod*,int); -extern char *sh_mactry(char*); +extern char *sh_macpat(Shell_t*,struct argnod*,int); +extern char *sh_mactry(Shell_t*,char*); extern void sh_printopts(Shopt_t,int,Shopt_t*); extern int sh_readline(Shell_t*,char**,int,int,long); extern Sfio_t *sh_sfeval(char*[]); extern void sh_setmatch(const char*,int,int,int[]); extern Dt_t *sh_subaliastree(int); +extern void sh_scope(Shell_t*, struct argnod*, int); +extern Namval_t *sh_scoped(Shell_t*, Namval_t*); extern Dt_t *sh_subfuntree(int); +extern void sh_subjobcheck(pid_t); extern int sh_subsavefd(int); -extern void sh_subtmpfile(void); +extern void sh_subtmpfile(int); extern char *sh_substitute(const char*,const char*,char*); extern const char *_sh_translate(const char*); extern int sh_trace(char*[],int); extern void sh_trim(char*); extern int sh_type(const char*); +extern void sh_unscope(Shell_t*); extern void sh_utol(const char*, char*); extern int sh_whence(char**,int); @@ -371,7 +416,32 @@ extern const char e_dict[]; /* sh_printopts() mode flags -- set --[no]option by default */ #define PRINT_VERBOSE 0x01 /* option on|off list */ -#define PRINT_ALL 0x02 /* list unset iptions too */ +#define PRINT_ALL 0x02 /* list unset options too */ #define PRINT_NO_HEADER 0x04 /* omit listing header */ #define PRINT_SHOPT 0x08 /* shopt -s|-u */ #define PRINT_TABLE 0x10 /* table of all options */ + +#ifdef SHOPT_STATS + /* performance statistics */ +# define STAT_ARGHITS 0 +# define STAT_ARGEXPAND 1 +# define STAT_COMSUB 2 +# define STAT_FORKS 3 +# define STAT_FUNCT 4 +# define STAT_GLOBS 5 +# define STAT_READS 6 +# define STAT_NVHITS 7 +# define STAT_NVOPEN 8 +# define STAT_PATHS 9 +# define STAT_SVFUNCT 10 +# define STAT_SCMDS 11 +# define STAT_SPAWN 12 +# define STAT_SUBSHELL 13 + extern const Shtable_t shtab_stats[]; +# define sh_stats(x) (sh.stats[(x)]++) +#else +# define sh_stats(x) +#endif /* SHOPT_STATS */ + + +#endif diff --git a/usr/src/lib/libshell/common/include/edit.h b/usr/src/lib/libshell/common/include/edit.h index ddbd45c24b..c477130ed1 100644 --- a/usr/src/lib/libshell/common/include/edit.h +++ b/usr/src/lib/libshell/common/include/edit.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -78,7 +78,8 @@ typedef struct edit int e_lnext; int e_fchar; int e_plen; /* length of prompt string */ - int e_crlf; /* zero if cannot return to beginning of line */ + char e_crlf; /* zero if cannot return to beginning of line */ + char e_nocrnl; /* don't put a new-line with ^L */ int e_llimit; /* line length limit */ int e_hline; /* current history line number */ int e_hloff; /* line number offset for command */ diff --git a/usr/src/lib/libshell/common/include/env.h b/usr/src/lib/libshell/common/include/env.h index f888913c34..93db7f5c27 100644 --- a/usr/src/lib/libshell/common/include/env.h +++ b/usr/src/lib/libshell/common/include/env.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/include/fault.h b/usr/src/lib/libshell/common/include/fault.h index 107b337f68..e754ea56eb 100644 --- a/usr/src/lib/libshell/common/include/fault.h +++ b/usr/src/lib/libshell/common/include/fault.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -33,6 +33,7 @@ #include "FEATURE/setjmp" #include "FEATURE/sigfeatures" + #ifndef SIGWINCH # ifdef SIGWIND # define SIGWINCH SIGWIND @@ -63,6 +64,10 @@ typedef void (*SH_SIGTYPE)(int,void(*)(int)); #define SH_SIGTSTP 0200 /* tstp signal received */ #define SH_SIGALRM 0200 /* timer alarm received */ #define SH_SIGTERM SH_SIGOFF /* term signal received */ +#define SH_SIGRUNTIME 0400 /* runtime value */ + +#define SH_SIGRTMIN 0 /* sh.sigruntime[] index */ +#define SH_SIGRTMAX 1 /* sh.sigruntime[] index */ /* * These are longjmp values @@ -108,11 +113,11 @@ struct checkpt #define sh_popcontext(bp) (sh.jmplist=(bp)->prev, errorpop(&((bp)->err))) extern void sh_fault(int); -extern void sh_done(int); +extern void sh_done(void*,int); extern void sh_chktrap(void); extern void sh_sigclear(int); extern void sh_sigdone(void); -extern void sh_siginit(void); +extern void sh_siginit(void*); extern void sh_sigtrap(int); extern void sh_sigreset(int); extern void sh_timetraps(void); diff --git a/usr/src/lib/libshell/common/include/fcin.h b/usr/src/lib/libshell/common/include/fcin.h index c147a8fdef..b498efa7f2 100644 --- a/usr/src/lib/libshell/common/include/fcin.h +++ b/usr/src/lib/libshell/common/include/fcin.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -36,7 +36,8 @@ typedef struct _fcin unsigned char *fclast; /* pointer to end of input buffer */ unsigned char *fcptr; /* pointer to next input char */ unsigned char fcchar; /* saved character */ - void (*fcfun)(Sfio_t*,const char*,int); /* advance function */ + void (*fcfun)(Sfio_t*,const char*,int,void*); /* advance function */ + void *context; /* context pointer */ int fcleft; /* for multibyte boundary */ Sfoff_t fcoff; /* offset for last read */ } Fcin_t; @@ -54,7 +55,7 @@ typedef struct _fcin extern int fcfill(void); extern int fcfopen(Sfio_t*); extern int fcclose(void); -void fcnotify(void(*)(Sfio_t*,const char*,int)); +void fcnotify(void(*)(Sfio_t*,const char*,int,void*),void*); extern int fcmbstate(const char*,int*,int*); extern Fcin_t _Fcin; /* used by macros */ diff --git a/usr/src/lib/libshell/common/include/history.h b/usr/src/lib/libshell/common/include/history.h index f97cd8b4a4..53e2b24964 100644 --- a/usr/src/lib/libshell/common/include/history.h +++ b/usr/src/lib/libshell/common/include/history.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -56,7 +56,7 @@ extern int _Hist; #define hist_min(hp) ((_Hist=((int)((hp)->histind-(hp)->histsize)))>=0?_Hist:0) #define hist_max(hp) ((int)((hp)->histind)) /* these are the history interface routines */ -extern int sh_histinit(void); +extern int sh_histinit(void *); extern void hist_cancel(History_t*); extern void hist_close(History_t*); extern int hist_copy(char*, int, int, int); diff --git a/usr/src/lib/libshell/common/include/io.h b/usr/src/lib/libshell/common/include/io.h index b8881bee84..4f3d280262 100644 --- a/usr/src/lib/libshell/common/include/io.h +++ b/usr/src/lib/libshell/common/include/io.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -65,19 +65,19 @@ #define sh_inuse(f2) (sh.fdptrs[f2]) -extern int sh_iocheckfd(int); -extern void sh_ioinit(void); +extern int sh_iocheckfd(Shell_t*,int); +extern void sh_ioinit(Shell_t*); extern int sh_iomovefd(int); -extern int sh_iorenumber(int,int); +extern int sh_iorenumber(Shell_t*,int,int); extern void sh_pclose(int[]); -extern void sh_iorestore(int,int); +extern void sh_iorestore(Shell_t*,int,int); #if defined(__EXPORT__) && defined(_BLD_DLL) && defined(_BLD_shell) __EXPORT__ #endif -extern Sfio_t *sh_iostream(int); -extern int sh_redirect(struct ionod*,int); -extern void sh_iosave(int,int); -extern void sh_iounsave(void); +extern Sfio_t *sh_iostream(Shell_t*,int); +extern int sh_redirect(Shell_t*,struct ionod*,int); +extern void sh_iosave(Shell_t *, int,int,char*); +extern void sh_iounsave(Shell_t*); extern int sh_chkopen(const char*); extern int sh_ioaccess(int,int); extern int sh_devtofd(const char*); @@ -98,6 +98,7 @@ extern const char e_open[]; extern const char e_notseek[]; extern const char e_noread[]; extern const char e_badseek[]; +extern const char e_badwrite[]; extern const char e_badpattern[]; extern const char e_toomany[]; extern const char e_pipe[]; diff --git a/usr/src/lib/libshell/common/include/jobs.h b/usr/src/lib/libshell/common/include/jobs.h index c4998eabca..068f0a1abe 100644 --- a/usr/src/lib/libshell/common/include/jobs.h +++ b/usr/src/lib/libshell/common/include/jobs.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -64,6 +64,7 @@ struct process pid_t p_fgrp; /* process group when stopped */ short p_job; /* job number of process */ unsigned short p_exit; /* exit value or signal number */ + unsigned short p_exitmin; /* minimum exit value for xargs */ unsigned short p_flag; /* flags - see below */ int p_env; /* subshell environment number */ #ifdef JOBS @@ -105,8 +106,19 @@ extern struct jobs job; #ifdef JOBS +#if !_std_malloc +#include <vmalloc.h> +#if VMALLOC_VERSION >= 20070911L +#define vmbusy() (vmstat(0,0)!=0) +#endif +#endif +#ifndef vmbusy +#define vmbusy() 0 +#endif + + #define job_lock() (job.in_critical++) -#define job_unlock() do{if(!--job.in_critical&&job.savesig)job_reap(job.savesig);}while(0) +#define job_unlock() do{if(!--job.in_critical&&job.savesig&&!vmbusy())job_reap(job.savesig);}while(0) extern const char e_jobusage[]; extern const char e_done[]; @@ -137,21 +149,21 @@ extern void job_clear(void); extern void job_bwait(char**); extern int job_walk(Sfio_t*,int(*)(struct process*,int),int,char*[]); extern int job_kill(struct process*,int); -extern void job_wait(pid_t); +extern int job_wait(pid_t); extern int job_post(pid_t,pid_t); extern void *job_subsave(void); extern void job_subrestore(void*); #ifdef JOBS - extern void job_init(int); - extern int job_close(void); + extern void job_init(Shell_t*,int); + extern int job_close(Shell_t*); extern int job_list(struct process*,int); extern int job_terminate(struct process*,int); extern int job_switch(struct process*,int); extern void job_fork(pid_t); extern int job_reap(int); #else -# define job_init(flag) -# define job_close() (0) +# define job_init(s,flag) +# define job_close(s) (0) # define job_fork(p) #endif /* JOBS */ diff --git a/usr/src/lib/libshell/common/include/lexstates.h b/usr/src/lib/libshell/common/include/lexstates.h index 8e3cdbfef6..77f351c88d 100644 --- a/usr/src/lib/libshell/common/include/lexstates.h +++ b/usr/src/lib/libshell/common/include/lexstates.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -148,4 +148,5 @@ extern const char e_lexlongquote[]; extern const char e_lexfuture[]; extern const char e_lexzerobyte[]; extern const char e_lexemptyfor[]; +extern const char e_lextypeset[]; #endif diff --git a/usr/src/lib/libshell/common/include/name.h b/usr/src/lib/libshell/common/include/name.h index 04ed23e7d0..60821ee706 100644 --- a/usr/src/lib/libshell/common/include/name.h +++ b/usr/src/lib/libshell/common/include/name.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -43,6 +43,7 @@ union Value int32_t *lp; Sflong_t *llp; /* for long long arithmetic */ int16_t s; + int16_t *sp; double *dp; /* for floating point arithmetic */ Sfdouble_t *ldp; /* for long floating point arithmetic */ struct Namarray *array; /* for array node */ @@ -67,6 +68,8 @@ union Value #define ARRAY_NOCLONE (16L<<ARRAY_BITS) /* do not clone array disc */ #define ARRAY_NOCHILD (32L<<ARRAY_BITS) /* skip compound arrays */ #define ARRAY_SETSUB (64L<<ARRAY_BITS) /* set subscript */ +#define ARRAY_NOSCOPE (128L<<ARRAY_BITS) /* top level scope only */ +#define ARRAY_TREE (256L<<ARRAY_BITS) /* arrays of compound vars */ #define NV_ASETSUB 8 /* set subscript */ /* These flags are used as options to array_get() */ @@ -86,11 +89,15 @@ struct Namref /* This describes a user shell function node */ struct Ufunction { - int *ptree; /* address of parse tree */ - int lineno; /* line number of function start */ - off_t hoffset; /* offset into source or history file */ - Namval_t *nspace; /* pointer to name space */ - char *fname; /* file name where function defined */ + int *ptree; /* address of parse tree */ + int lineno; /* line number of function start */ + off_t hoffset; /* offset into source or history file */ + Namval_t *nspace; /* pointer to name space */ + char *fname; /* file name where function defined */ + char *help; /* help string */ + Dt_t *sdict; /* dictionary for statics */ + Dt_t *fdict; /* dictionary node belongs to */ + Namval_t *np; /* function node pointer */ }; #ifndef ARG_RAW @@ -101,11 +108,13 @@ struct Ufunction /* The following attributes are for internal use */ #define NV_NOCHANGE (NV_EXPORT|NV_IMPORT|NV_RDONLY|NV_TAGGED|NV_NOFREE) -#define NV_ATTRIBUTES (~(NV_NOSCOPE|NV_ARRAY|NV_NOARRAY|NV_IDENT|NV_ASSIGN|NV_REF|NV_VARNAME)) +#define NV_ATTRIBUTES (~(NV_NOSCOPE|NV_ARRAY|NV_NOARRAY|NV_IDENT|NV_ASSIGN|NV_REF|NV_VARNAME|NV_STATIC)) #define NV_PARAM NV_NODISC /* expansion use positional params */ /* This following are for use with nodes which are not name-values */ #define NV_TYPE 0x1000000 +#define NV_STATIC 0x2000000 +#define NV_COMVAR 0x4000000 #define NV_FUNCTION (NV_RJUST|NV_FUNCT) /* value is shell function */ #define NV_FPOSIX NV_LJUST /* posix function semantics */ #define NV_FTMP NV_ZFILL /* function source in tmpfile */ @@ -119,10 +128,12 @@ struct Ufunction #define BLT_SPC (NV_LJUST) /* special built-ins */ #define BLT_EXIT (NV_RJUST) /* exit value can be > 255 */ #define BLT_DCL (NV_TAGGED) /* declaration command */ -#define nv_isref(n) (nv_isattr((n),NV_REF)==NV_REF) -#define nv_istable(n) (nv_isattr((n),NV_TABLE|NV_LJUST|NV_RJUST)==NV_TABLE) -#define is_abuiltin(n) (nv_isattr(n,NV_BLTIN)==NV_BLTIN) -#define is_afunction(n) (nv_isattr(n,NV_FUNCTION)==NV_FUNCTION) +#define BLT_NOSFIO (NV_IMPORT) /* doesn't use sfio */ +#define NV_OPTGET (NV_BINARY) /* function calls getopts */ +#define nv_isref(n) (nv_isattr((n),NV_REF|NV_TAGGED|NV_FUNCT)==NV_REF) +#define nv_istable(n) (nv_isattr((n),NV_TABLE|NV_LJUST|NV_RJUST|NV_INTEGER)==NV_TABLE) +#define is_abuiltin(n) (nv_isattr(n,NV_BLTIN|NV_INTEGER)==NV_BLTIN) +#define is_afunction(n) (nv_isattr(n,NV_FUNCTION|NV_REF)==NV_FUNCTION) #define nv_funtree(n) ((n)->nvalue.rp->ptree) #define funptr(n) ((n)->nvalue.bfp) @@ -138,16 +149,13 @@ struct Ufunction #define nv_reftree(n) ((n)->nvalue.nrp->root) #define nv_reftable(n) ((n)->nvalue.nrp->table) #define nv_refsub(n) ((n)->nvalue.nrp->sub) -#if SHOPT_OO -# define nv_class(np) (nv_isattr(np,NV_REF|NV_IMPORT)?0:(Namval_t*)((np)->nvenv)) -#endif /* SHOPT_OO */ /* ... etc */ #define nv_setsize(n,s) ((n)->nvsize = (s)) #undef nv_size #define nv_size(np) ((np)->nvsize) -#define nv_isnull(np) (!(np)->nvalue.cp && !(np)->nvfun && !nv_isattr(np,NV_SHORT)) +#define nv_isnull(np) (!(np)->nvalue.cp && !((np)->nvfun && (np)->nvfun->disc) && nv_isattr(np,NV_SHORT|NV_INTEGER)!=(NV_SHORT|NV_INTEGER)) /* ... for arrays */ @@ -158,16 +166,21 @@ extern int array_maxindex(Namval_t*); extern char *nv_endsubscript(Namval_t*, char*, int); extern Namfun_t *nv_cover(Namval_t*); extern Namarr_t *nv_arrayptr(Namval_t*); +extern int nv_arraysettype(Namval_t*, Namval_t*,const char*,int); +extern int nv_aimax(Namval_t*); +extern int nv_atypeindex(Namval_t*, const char*); extern int nv_setnotify(Namval_t*,char **); extern int nv_unsetnotify(Namval_t*,char **); extern void nv_setlist(struct argnod*, int); +extern struct argnod* nv_onlist(struct argnod*, const char*); extern void nv_optimize(Namval_t*); extern void nv_outname(Sfio_t*,char*, int); -extern void nv_scope(struct argnod*); extern void nv_unref(Namval_t*); extern void _nv_unset(Namval_t*,int); extern int nv_clone(Namval_t*, Namval_t*, int); -extern void *nv_diropen(const char*); +void clone_all_disc(Namval_t*, Namval_t*, int); +extern Namfun_t *nv_clone_disc(Namfun_t*, int); +extern void *nv_diropen(Namval_t*, const char*); extern char *nv_dirnext(void*); extern void nv_dirclose(void*); extern char *nv_getvtree(Namval_t*, Namfun_t*); @@ -175,20 +188,26 @@ extern void nv_attribute(Namval_t*, Sfio_t*, char*, int); extern Namval_t *nv_bfsearch(const char*, Dt_t*, Namval_t**, char**); extern Namval_t *nv_mkclone(Namval_t*); extern Namval_t *nv_mktype(Namval_t**, int); -extern void nv_addnode(Namval_t*, int); +extern Namval_t *nv_addnode(Namval_t*, int); extern Namval_t *nv_parent(Namval_t*); extern char *nv_getbuf(size_t); extern Namval_t *nv_mount(Namval_t*, const char *name, Dt_t*); extern Namval_t *nv_arraychild(Namval_t*, Namval_t*, int); extern int nv_compare(Dt_t*, Void_t*, Void_t*, Dtdisc_t*); +extern void nv_outnode(Namval_t*,Sfio_t*, int, int); +extern int nv_subsaved(Namval_t*); +extern void nv_typename(Namval_t*, Sfio_t*); extern const Namdisc_t RESTRICTED_disc; +extern const Namdisc_t ENUM_disc; extern char nv_local; extern Dtdisc_t _Nvdisc; +extern const char *nv_discnames[]; extern const char e_subscript[]; extern const char e_nullset[]; extern const char e_notset[]; extern const char e_noparent[]; +extern const char e_notelem[]; extern const char e_readonly[]; extern const char e_badfield[]; extern const char e_restricted[]; @@ -196,6 +215,7 @@ extern const char e_ident[]; extern const char e_varname[]; extern const char e_noalias[]; extern const char e_noarray[]; +extern const char e_notenum[]; extern const char e_aliname[]; extern const char e_badexport[]; extern const char e_badref[]; @@ -205,4 +225,7 @@ extern const char e_envmarker[]; extern const char e_badlocale[]; extern const char e_loop[]; extern const char e_redef[]; +extern const char e_required[]; +extern const char e_badappend[]; +extern const char e_unknowntype[]; #endif /* _NV_PRIVATE */ diff --git a/usr/src/lib/libshell/common/include/national.h b/usr/src/lib/libshell/common/include/national.h index 837015011a..542bb583d6 100644 --- a/usr/src/lib/libshell/common/include/national.h +++ b/usr/src/lib/libshell/common/include/national.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/include/nval.h b/usr/src/lib/libshell/common/include/nval.h index 6d02dc0a3b..def26ebb18 100644 --- a/usr/src/lib/libshell/common/include/nval.h +++ b/usr/src/lib/libshell/common/include/nval.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -31,6 +31,7 @@ #include <ast.h> #include <cdt.h> +#include <option.h> /* for compatibility with old hash library */ #define Hashtab_t Dt_t @@ -44,8 +45,7 @@ typedef struct Namfun Namfun_t; typedef struct Namdisc Namdisc_t; typedef struct Nambfun Nambfun_t; typedef struct Namarray Namarr_t; -typedef struct Nambltin Nambltin_t; -typedef struct Namtype Namtype_t; +typedef struct Namdecl Namdecl_t; /* * This defines the template for nodes that have their own assignment @@ -64,13 +64,14 @@ struct Namdisc Namval_t *(*nextf)(Namval_t*, Dt_t*, Namfun_t*); Namval_t *(*typef)(Namval_t*, Namfun_t*); int (*readf)(Namval_t*, Sfio_t*, int, Namfun_t*); + int (*writef)(Namval_t*, Sfio_t*, int, Namfun_t*); }; struct Namfun { const Namdisc_t *disc; char nofree; - char funs; + unsigned char subshell; unsigned short dsize; Namfun_t *next; char *last; @@ -92,22 +93,14 @@ struct Namarray long nelem; /* number of elements */ void *(*fun)(Namval_t*,const char*,int); /* associative arrays */ Namval_t *parent; /* for multi-dimensional */ + Dt_t *table; /* for subscripts */ + void *scope; /* non-zerp when scoped */ }; -/* Passed as third argument to a builtin when NV_BLTINOPT is set on node */ -struct Nambltin +/* The context pointer for declaration command */ +struct Namdecl { - void *shp; - Namval_t *np; - void *ptr; - void *data; - int flags; -}; - -struct Namtype -{ - void *shp; - Namval_t *np; + Namval_t *tp; /* point to type */ const char *optstring; void *optinfof; }; @@ -132,6 +125,7 @@ struct Namval }; #define NV_CLASS ".sh.type" +#define NV_DATA "_" /* special class or instance variable */ #define NV_MINSZ (sizeof(struct Namval)-sizeof(Dtlink_t)-sizeof(char*)) #define nv_namptr(p,n) ((Namval_t*)((char*)(p)+(n)*NV_MINSZ-sizeof(Dtlink_t))) @@ -163,13 +157,14 @@ struct Namval #define NV_SHORT (NV_RJUST) /* when integers are not long */ #define NV_LONG (NV_UTOL) /* for long long and long double */ #define NV_UNSIGN (NV_LTOU) /* for unsigned quantities */ -#define NV_DOUBLE (NV_ZFILL) /* for floating point */ +#define NV_DOUBLE (NV_INTEGER|NV_ZFILL) /* for floating point */ #define NV_EXPNOTE (NV_LJUST) /* for scientific notation */ +#define NV_HEXFLOAT (NV_LTOU) /* for C99 base16 float notation */ /* options for nv_open */ #define NV_APPEND 0x10000 /* append value */ -#define NV_MOVE 0x20000 /* for use with nv_clone */ +#define NV_MOVE 0x8000000 /* for use with nv_clone */ #define NV_ADD 8 /* add node if not found */ #define NV_ASSIGN NV_NOFREE /* assignment is possible */ @@ -185,19 +180,21 @@ struct Namval #define NV_NODISC NV_IDENT /* ignore disciplines */ #define NV_FUNCT NV_IDENT /* option for nv_create */ -#define NV_BLTINOPT NV_ZFILL /* save state for optimization*/ +#define NV_BLTINOPT NV_ZFILL /* mark builtins in libcmd */ #define NV_PUBLIC (~(NV_NOSCOPE|NV_ASSIGN|NV_IDENT|NV_VARNAME|NV_NOADD)) /* numeric types */ +#define NV_INT16P (NV_LJUST|NV_SHORT|NV_INTEGER) #define NV_INT16 (NV_SHORT|NV_INTEGER) #define NV_UINT16 (NV_UNSIGN|NV_SHORT|NV_INTEGER) +#define NV_UINT16P (NV_LJUSTNV_UNSIGN|NV_SHORT|NV_INTEGER) #define NV_INT32 (NV_INTEGER) #define NV_UNT32 (NV_UNSIGN|NV_INTEGER) #define NV_INT64 (NV_LONG|NV_INTEGER) #define NV_UINT64 (NV_UNSIGN|NV_LONG|NV_INTEGER) -#define NV_FLOAT (NV_SHORT|NV_DOUBLE|NV_INTEGER) -#define NV_LDOUBLE (NV_LONG|NV_DOUBLE|NV_INTEGER) +#define NV_FLOAT (NV_SHORT|NV_DOUBLE) +#define NV_LDOUBLE (NV_LONG|NV_DOUBLE) /* name-value pair macros */ #define nv_isattr(np,f) ((np)->nvflag & (f)) @@ -213,6 +210,7 @@ struct Namval #define NV_ADELETE 5 /* delete current subscript */ #define NV_AADD 6 /* add subscript if not found */ #define NV_ACURRENT 7 /* return current subscript Namval_t* */ +#define NV_ASETSUB 8 /* set current subscript */ /* The following are for nv_disc */ #define NV_FIRST 1 @@ -221,7 +219,7 @@ struct Namval #define NV_CLONE 4 /* The following are operations for nv_putsub() */ -#define ARRAY_BITS 24 +#define ARRAY_BITS 22 #define ARRAY_ADD (1L<<ARRAY_BITS) /* add subscript if not found */ #define ARRAY_SCAN (2L<<ARRAY_BITS) /* For ${array[@]} */ #define ARRAY_UNDEF (4L<<ARRAY_BITS) /* For ${array} */ @@ -239,7 +237,9 @@ struct Namval # endif /* _BLD_shell */ #endif /* _DLL */ /* prototype for array interface*/ +extern Namarr_t *nv_arrayptr(Namval_t*); extern Namarr_t *nv_setarray(Namval_t*,void*(*)(Namval_t*,const char*,int)); +extern int nv_arraynsub(Namarr_t*); extern void *nv_associative(Namval_t*,const char*,int); extern int nv_aindex(Namval_t*); extern int nv_nextsub(Namval_t*); @@ -253,6 +253,7 @@ extern int nv_clone(Namval_t*, Namval_t*, int); extern void nv_close(Namval_t*); extern void *nv_context(Namval_t*); extern Namval_t *nv_create(const char*, Dt_t*, int,Namfun_t*); +extern void nv_delete(Namval_t*, Dt_t*, int); extern Dt_t *nv_dict(Namval_t*); extern Sfdouble_t nv_getn(Namval_t*, Namfun_t*); extern Sfdouble_t nv_getnum(Namval_t*); @@ -260,13 +261,15 @@ extern char *nv_getv(Namval_t*, Namfun_t*); extern char *nv_getval(Namval_t*); extern Namfun_t *nv_hasdisc(Namval_t*, const Namdisc_t*); extern int nv_isnull(Namval_t*); +extern Namfun_t *nv_isvtree(Namval_t*); extern Namval_t *nv_lastdict(void); +extern Namval_t *nv_mkinttype(char*, size_t, int, const char*, Namdisc_t*); extern void nv_newattr(Namval_t*,unsigned,int); extern Namval_t *nv_open(const char*,Dt_t*,int); extern void nv_putval(Namval_t*,const char*,int); extern void nv_putv(Namval_t*,const char*,int,Namfun_t*); +extern int nv_rename(Namval_t*,int); extern int nv_scan(Dt_t*,void(*)(Namval_t*,void*),void*,int,int); -extern Namval_t *nv_scoped(Namval_t*); extern char *nv_setdisc(Namval_t*,const char*,Namval_t*,Namfun_t*); extern void nv_setref(Namval_t*, Dt_t*,int); extern int nv_settype(Namval_t*, Namval_t*, int); @@ -274,17 +277,19 @@ extern void nv_setvec(Namval_t*,int,int,char*[]); extern void nv_setvtree(Namval_t*); extern int nv_setsize(Namval_t*,int); extern Namfun_t *nv_disc(Namval_t*,Namfun_t*,int); -extern void nv_unset(Namval_t*); +extern void nv_unset(Namval_t*); /*obsolete */ +extern void _nv_unset(Namval_t*,int); extern Namval_t *nv_search(const char *, Dt_t*, int); -extern void nv_unscope(void); extern char *nv_name(Namval_t*); extern Namval_t *nv_type(Namval_t*); +extern void nv_addtype(Namval_t*,const char*, Optdisc_t*, size_t); extern const Namdisc_t *nv_discfun(int); #ifdef _DLL # undef extern #endif /* _DLL */ +#define nv_unset(np) _nv_unset(np,0) #define nv_size(np) nv_setsize((np),-1) #define nv_stack(np,nf) nv_disc(np,nf,0) @@ -296,7 +301,7 @@ extern const Namdisc_t *nv_discfun(int); # define nv_istype(np) nv_isattr(np) # define nv_newtype(np) nv_newattr(np) # define nv_namset(np,a,b) nv_open(np,a,b) -# define nv_free(np) nv_unset(np) +# define nv_free(np) nv_unset(np,0) # define nv_settype(np,a,b,c) nv_setdisc(np,a,b,c) # define nv_search(np,a,b) nv_open(np,a,((b)?0:NV_NOADD)) # define settype setdisc diff --git a/usr/src/lib/libshell/common/include/path.h b/usr/src/lib/libshell/common/include/path.h index 2460be3cbf..15c4e9694b 100644 --- a/usr/src/lib/libshell/common/include/path.h +++ b/usr/src/lib/libshell/common/include/path.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -44,7 +44,7 @@ #define PATH_STD_DIR 0100 /* directory is on $(getconf PATH) */ #define PATH_OFFSET 2 /* path offset for path_join */ -#define MAXDEPTH (sizeof(char*)==2?64:4096) /* maximum recursion depth*/ +#define MAXDEPTH (sizeof(char*)==2?64:2048) /* maximum recursion depth*/ /* * path component structure for path searching @@ -55,6 +55,7 @@ typedef struct pathcomp int refcount; dev_t dev; ino_t ino; + time_t mtime; char *name; char *lib; char *blib; @@ -90,7 +91,7 @@ extern Pathcomp_t *path_get(const char*); #undef extern extern char *path_pwd(int); extern Pathcomp_t *path_nextcomp(Pathcomp_t*,const char*,Pathcomp_t*); -extern int path_search(const char*,Pathcomp_t*,int); +extern int path_search(const char*,Pathcomp_t**,int); extern char *path_relative(const char*); extern int path_complete(const char*, const char*,struct argnod**); #if SHOPT_BRACEPAT @@ -115,6 +116,7 @@ extern const char e_crondir[]; #endif /* SHOPT_SUID_EXEC */ extern const char is_alias[]; extern const char is_builtin[]; +extern const char is_spcbuiltin[]; extern const char is_builtver[]; extern const char is_reserved[]; extern const char is_talias[]; diff --git a/usr/src/lib/libshell/common/include/shell.h b/usr/src/lib/libshell/common/include/shell.h index 66ccd20436..b82e582cfc 100644 --- a/usr/src/lib/libshell/common/include/shell.h +++ b/usr/src/lib/libshell/common/include/shell.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -27,7 +27,7 @@ * */ -#include <cmd.h> +#include <ast.h> #include <cdt.h> #ifdef _SH_PRIVATE # include "name.h" @@ -35,7 +35,7 @@ # include <nval.h> #endif /* _SH_PRIVATE */ -#define SH_VERSION 20060510 +#define SH_VERSION 20071012 #undef NOT_USED #define NOT_USED(x) (&x,1) @@ -47,8 +47,9 @@ typedef struct } Shopt_t; -typedef void (*Shinit_f)(int); -typedef int (*Shbltin_f)(int, char*[], void*); +typedef struct Shell_s Shell_t; + +typedef void (*Shinit_f)(Shell_t*, int); typedef int (*Shwait_f)(int, long, int); union Shnode_u; @@ -115,6 +116,7 @@ typedef struct sh_scope char **argv; char *cmdname; char *filename; + char *funname; int lineno; Dt_t *var_tree; struct sh_scope *self; @@ -124,7 +126,7 @@ typedef struct sh_scope * Saves the state of the shell */ -typedef struct sh_static +struct Shell_s { Shopt_t options; /* set -o options */ Dt_t *var_tree; /* for shell variables */ @@ -136,10 +138,11 @@ typedef struct sh_static int exitval; /* most recent exit value */ unsigned char trapnote; /* set when trap/signal is pending */ char subshell; /* set for virtual subshell */ + char shcomp; /* set when runing shcomp */ #ifdef _SH_PRIVATE _SH_PRIVATE #endif /* _SH_PRIVATE */ -} Shell_t; +}; /* flags for sh_parse */ #define SH_NL 1 /* Treat new-lines as ; */ @@ -149,9 +152,13 @@ typedef struct sh_static #define SH_IOCOPROCESS (-2) #define SH_IOHISTFILE (-3) +#include <cmd.h> + /* symbolic value for sh_fdnotify */ #define SH_FDCLOSE (-1) +#undef getenv /* -lshell provides its own */ + #if defined(__EXPORT__) && defined(_DLL) # ifdef _BLD_shell # define extern __EXPORT__ @@ -169,7 +176,8 @@ extern int sh_trap(const char*,int); extern int sh_fun(Namval_t*,Namval_t*, char*[]); extern int sh_funscope(int,char*[],int(*)(void*),void*,int); extern Sfio_t *sh_iogetiop(int,int); -extern int sh_main(int, char*[], void(*)(int)); +extern int sh_main(int, char*[], Shinit_f); +extern int sh_run(int, char*[]); extern void sh_menu(Sfio_t*, int, char*[]); extern Namval_t *sh_addbuiltin(const char*, int(*)(int, char*[],void*), void*); extern char *sh_fmtq(const char*); diff --git a/usr/src/lib/libshell/common/include/shlex.h b/usr/src/lib/libshell/common/include/shlex.h index 56de279d6c..8f5277c246 100644 --- a/usr/src/lib/libshell/common/include/shlex.h +++ b/usr/src/lib/libshell/common/include/shlex.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -31,7 +31,8 @@ #include "shtable.h" #include "lexstates.h" -struct shlex_t + +typedef struct _shlex_ { Shell_t *sh; /* pointer to the interpreter */ struct argnod *arg; /* current word */ @@ -42,9 +43,12 @@ struct shlex_t int digits; /* numerical value with word token */ char aliasok; /* on when alias is legal */ char assignok; /* on when name=value is legal */ + char inexec; /* on when processing exec */ + char intypeset; /* on when processing typeset */ + char comp_assign; /* in compound assignment */ + char comsub; /* parsing command substitution */ int inlineno; /* saved value of sh.inlineno */ int firstline; /* saved value of sh.st.firstline */ - int comsub; /* parsing command substitution */ #if SHOPT_KIA Sfio_t *kiafile; /* kia output file */ Sfio_t *kiatmp; /* kia reference file */ @@ -56,7 +60,10 @@ struct shlex_t char *scriptname; /* name of script file */ Dt_t *entity_tree; /* for entity ids */ #endif /* SHOPT_KIA */ -}; +#ifdef _SHLEX_PRIVATE + _SHLEX_PRIVATE +#endif +} Lex_t; /* symbols for parsing */ #define NL '\n' @@ -122,6 +129,7 @@ struct shlex_t #define SH_COMPASSIGN 010 /* allow compound assignments only */ +#if 0 typedef struct _shlex_ { struct shlex_t _shlex; @@ -131,6 +139,7 @@ typedef struct _shlex_ } Lex_t; #define shlex (((Lex_t*)(sh.lex_context))->_shlex) +#endif extern const char e_unexpected[]; extern const char e_unmatched[]; extern const char e_endoffile[]; @@ -144,9 +153,15 @@ extern const char e_newline[]; #define LBRACT '[' #define RBRACT ']' -extern int sh_lex(); +extern int sh_lex(Lex_t*); +extern Shnode_t *sh_dolparen(Lex_t*); extern Lex_t *sh_lexopen(Lex_t*, Shell_t*, int); -extern void sh_lexskip(int,int,int); -extern void sh_syntax(void); +extern void sh_lexskip(Lex_t*,int,int,int); +extern void sh_syntax(Lex_t*); +#if SHOPT_KIA + extern int kiaclose(Lex_t *); + extern unsigned long kiaentity(Lex_t*, const char*,int,int,int,int,unsigned long,int,int,const char*); +#endif /* SHOPT_KIA */ + #endif /* !NOTSYM */ diff --git a/usr/src/lib/libshell/common/include/shnodes.h b/usr/src/lib/libshell/common/include/shnodes.h index b688edb4c5..e55cfc42eb 100644 --- a/usr/src/lib/libshell/common/include/shnodes.h +++ b/usr/src/lib/libshell/common/include/shnodes.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -40,6 +40,7 @@ #define FSHOWME (0400<<COMBITS) /* set for showme commands */ #define FPOSIX (02<<COMBITS) /* posix semantics function */ #define FLINENO (04<<COMBITS) /* for/case has line number */ +#define FOPTGET (010<<COMBITS) /* function calls getopts */ #define TNEGATE (01<<COMBITS) /* ! inside [[...]] */ #define TBINARY (02<<COMBITS) /* binary operator in [[...]] */ @@ -169,21 +170,22 @@ struct arithnod /* types of ionodes stored in iofile */ -#define IOUFD 0x3f /* file descriptor number mask */ -#define IOPUT 0x40 /* > redirection operator */ -#define IOAPP 0x80 /* >> redirection operator */ -#define IODOC 0x100 /* << redirection operator */ -#define IOMOV 0x200 /* <& or >& operators */ -#define IOCLOB 0x400 /* noclobber bit */ -#define IORDW 0x800 /* <> redirection operator */ -#define IORAW 0x1000 /* no expansion needed for filename */ -#define IOSTRG 0x2000 /* here-document stored as incore string */ -#define IOSTRIP 0x4000 /* strip leading tabs for here-document */ -#define IOQUOTE 0x8000 /* here-document delimiter was quoted */ -#define IOVNM 0x10000 /* iovname field is non-zero */ -#define IOLSEEK 0x20000 /* seek operators <# or ># */ -#define IOARITH 0x40000 /* arithmetic seek <# ((expr)) */ -#define IOCOPY IOCLOB /* copy skipped lines onto standard output */ +#define IOUFD 0x3f /* file descriptor number mask */ +#define IOPUT 0x40 /* > redirection operator */ +#define IOAPP 0x80 /* >> redirection operator */ +#define IODOC 0x100 /* << redirection operator */ +#define IOMOV 0x200 /* <& or >& operators */ +#define IOCLOB 0x400 /* noclobber bit */ +#define IORDW 0x800 /* <> redirection operator */ +#define IORAW 0x1000 /* no expansion needed for filename */ +#define IOSTRG 0x2000 /* here-document stored as incore string */ +#define IOSTRIP 0x4000 /* strip leading tabs for here-document */ +#define IOQUOTE 0x8000 /* here-document delimiter was quoted */ +#define IOVNM 0x10000 /* iovname field is non-zero */ +#define IOLSEEK 0x20000 /* seek operators <# or ># */ +#define IOARITH 0x40000 /* arithmetic seek <# ((expr)) */ +#define IOREWRITE 0x80000 /* arithmetic seek <# ((expr)) */ +#define IOCOPY IOCLOB /* copy skipped lines onto standard output */ union Shnode_u { @@ -205,18 +207,13 @@ union Shnode_u struct arithnod ar; }; -extern void sh_freeup(void); +extern void sh_freeup(Shell_t*); extern void sh_funstaks(struct slnod*,int); extern Sfio_t *sh_subshell(Shnode_t*, int, int); #if defined(__EXPORT__) && defined(_BLD_DLL) && defined(_BLD_shell) __EXPORT__ #endif extern int sh_tdump(Sfio_t*, const Shnode_t*); -extern Shnode_t *sh_dolparen(void); -extern Shnode_t *sh_trestore(Sfio_t*); -#if SHOPT_KIA - extern int kiaclose(void); - extern unsigned long kiaentity(const char*,int,int,int,int,unsigned long,int,int,const char*); -#endif /* SHOPT_KIA */ +extern Shnode_t *sh_trestore(Shell_t*, Sfio_t*); #endif /* _SHNODES_H */ diff --git a/usr/src/lib/libshell/common/include/shtable.h b/usr/src/lib/libshell/common/include/shtable.h index fc4e8e471f..041372b431 100644 --- a/usr/src/lib/libshell/common/include/shtable.h +++ b/usr/src/lib/libshell/common/include/shtable.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -33,20 +33,20 @@ typedef struct shtable1 { const char *sh_name; - unsigned sh_number; + const unsigned sh_number; } Shtable_t; struct shtable2 { const char *sh_name; - unsigned sh_number; + const unsigned sh_number; const char *sh_value; }; struct shtable3 { const char *sh_name; - unsigned sh_number; + const unsigned sh_number; int (*sh_value)(int, char*[], void*); }; diff --git a/usr/src/lib/libshell/common/include/streval.h b/usr/src/lib/libshell/common/include/streval.h index bbf51bd9d7..4ec3181f15 100644 --- a/usr/src/lib/libshell/common/include/streval.h +++ b/usr/src/lib/libshell/common/include/streval.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/include/terminal.h b/usr/src/lib/libshell/common/include/terminal.h index 1712c12bab..a0e813c935 100644 --- a/usr/src/lib/libshell/common/include/terminal.h +++ b/usr/src/lib/libshell/common/include/terminal.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/include/test.h b/usr/src/lib/libshell/common/include/test.h index b1ece6fcfa..593bd5852b 100644 --- a/usr/src/lib/libshell/common/include/test.h +++ b/usr/src/lib/libshell/common/include/test.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/include/timeout.h b/usr/src/lib/libshell/common/include/timeout.h index 3248b562c1..803c6b09d2 100644 --- a/usr/src/lib/libshell/common/include/timeout.h +++ b/usr/src/lib/libshell/common/include/timeout.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/include/ulimit.h b/usr/src/lib/libshell/common/include/ulimit.h index b31d12b494..d5f3b21391 100644 --- a/usr/src/lib/libshell/common/include/ulimit.h +++ b/usr/src/lib/libshell/common/include/ulimit.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/include/variables.h b/usr/src/lib/libshell/common/include/variables.h index 2ceefcf9f7..ac9c5a536b 100644 --- a/usr/src/lib/libshell/common/include/variables.h +++ b/usr/src/lib/libshell/common/include/variables.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -68,37 +68,41 @@ #define LCMSGNOD (sh.bltin_nodes+38) #define LCNUMNOD (sh.bltin_nodes+39) #define FIGNORENOD (sh.bltin_nodes+40) -#define DOTSHNOD (sh.bltin_nodes+41) -#define ED_CHRNOD (sh.bltin_nodes+42) -#define ED_COLNOD (sh.bltin_nodes+43) -#define ED_TXTNOD (sh.bltin_nodes+44) -#define ED_MODENOD (sh.bltin_nodes+45) -#define SH_NAMENOD (sh.bltin_nodes+46) -#define SH_SUBSCRNOD (sh.bltin_nodes+47) -#define SH_VALNOD (sh.bltin_nodes+48) -#define SH_VERSIONNOD (sh.bltin_nodes+49) -#define SH_DOLLARNOD (sh.bltin_nodes+50) -#define SH_MATCHNOD (sh.bltin_nodes+51) -#define SH_COMMANDNOD (sh.bltin_nodes+52) -#define SH_PATHNAMENOD (sh.bltin_nodes+53) -#define SH_FUNNAMENOD (sh.bltin_nodes+54) -#define SH_SUBSHELLNOD (sh.bltin_nodes+55) -#define SH_LEVELNOD (sh.bltin_nodes+56) +#define VERSIONNOD (sh.bltin_nodes+41) +#define DOTSHNOD (sh.bltin_nodes+42) +#define ED_CHRNOD (sh.bltin_nodes+43) +#define ED_COLNOD (sh.bltin_nodes+44) +#define ED_TXTNOD (sh.bltin_nodes+45) +#define ED_MODENOD (sh.bltin_nodes+46) +#define SH_NAMENOD (sh.bltin_nodes+47) +#define SH_SUBSCRNOD (sh.bltin_nodes+48) +#define SH_VALNOD (sh.bltin_nodes+49) +#define SH_VERSIONNOD (sh.bltin_nodes+50) +#define SH_DOLLARNOD (sh.bltin_nodes+51) +#define SH_MATCHNOD (sh.bltin_nodes+52) +#define SH_COMMANDNOD (sh.bltin_nodes+53) +#define SH_PATHNAMENOD (sh.bltin_nodes+54) +#define SH_FUNNAMENOD (sh.bltin_nodes+55) +#define SH_SUBSHELLNOD (sh.bltin_nodes+56) +#define SH_LEVELNOD (sh.bltin_nodes+57) +#define SH_LINENO (sh.bltin_nodes+58) +#define SH_STATS (sh.bltin_nodes+59) +#define SHLVL (sh.bltin_nodes+60) #if SHOPT_FS_3D -# define VPATHNOD (sh.bltin_nodes+57) +# define VPATHNOD (sh.bltin_nodes+61) # define NFS_3D 1 #else # define NFS_3D 0 #endif /* SHOPT_FS_3D */ #if SHOPT_VPIX -# define DOSPATHNOD (sh.bltin_nodes+57+NFS_3D) -# define VPIXNOD (sh.bltin_nodes+58+NFS_3D) +# define DOSPATHNOD (sh.bltin_nodes+61+NFS_3D) +# define VPIXNOD (sh.bltin_nodes+62+NFS_3D) # define NVPIX (NFS_3D+2) #else # define NVPIX NFS_3D #endif /* SHOPT_VPIX */ #ifdef apollo -# define SYSTYPENOD (sh.bltin_nodes+57+NVPIX) +# define SYSTYPENOD (sh.bltin_nodes+61+NVPIX) #endif /* apollo */ #endif /* SH_VALNOD */ diff --git a/usr/src/lib/libshell/common/include/version.h b/usr/src/lib/libshell/common/include/version.h index dafdfcbd68..8f025169ff 100644 --- a/usr/src/lib/libshell/common/include/version.h +++ b/usr/src/lib/libshell/common/include/version.h @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -17,4 +17,4 @@ * David Korn <dgk@research.att.com> * * * ***********************************************************************/ -#define SH_RELEASE "1993-12-28 s+" +#define SH_RELEASE "93t 2008-11-04" diff --git a/usr/src/lib/libshell/common/llib-lshell b/usr/src/lib/libshell/common/llib-lshell index 49bb9889e7..a65fe10ee2 100644 --- a/usr/src/lib/libshell/common/llib-lshell +++ b/usr/src/lib/libshell/common/llib-lshell @@ -19,120 +19,15 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * lib/libshell/common/llib-lshell * */ -#pragma ident "%Z%%M% %I% %E% SMI" - /*LINTLIBRARY*/ /*PROTOLIB1*/ #include <shell.h> #include <nval.h> - -/* automatically generated data start here */ -extern const char e_defpath[]; -extern const char e_found[]; -extern const char e_nospace[]; -extern const char e_format[]; -extern const char e_number[]; -extern const char e_restricted[]; -extern const char e_recursive[]; -extern char e_version[]; -extern Dt_t *sh_bltin_tree (void); -extern void sh_subfork (void); -extern Shell_t *sh_init (int,char*[],Shinit_f); -extern int sh_reinit (char*[]); -extern int sh_eval (Sfio_t*,int); -extern void sh_delay (double); -extern void *sh_parse (Shell_t*, Sfio_t*,int); -extern int sh_trap (const char*,int); -extern int sh_fun (Namval_t*,Namval_t*, char*[]); -extern int sh_funscope (int,char*[],int(*)(void*),void*,int); -extern Sfio_t *sh_iogetiop (int,int); -extern int sh_main (int, char*[], void(*)(int)); -extern void sh_menu (Sfio_t*, int, char*[]); -extern Namval_t *sh_addbuiltin (const char*, int(*)(int, char*[],void*), void*); -extern char *sh_fmtq (const char*); -extern char *sh_fmtqf (const char*, int, int); -extern Sfdouble_t sh_strnum (const char*, char**, int); -extern int sh_access (const char*,int); -extern int sh_close (int); -extern int sh_dup (int); -extern void sh_exit (int); -extern int sh_fcntl (int, int, ...); -extern Sfio_t *sh_fd2sfio (int); -extern Shell_t *sh_getinterp (void); -extern int sh_open (const char*, int, ...); -extern int sh_openmax (void); -extern Sfio_t *sh_pathopen (const char*); -extern ssize_t sh_read (int, void*, size_t); -extern ssize_t sh_write (int, const void*, size_t); -extern off_t sh_seek (int, off_t, int); -extern int sh_pipe (int[]); -extern mode_t sh_umask (mode_t); -extern void *sh_waitnotify (Shwait_f); -extern Shscope_t *sh_getscope (int,int); -extern Shscope_t *sh_setscope (Shscope_t*); -extern void sh_sigcheck (void); -extern unsigned long sh_isoption (int); -extern unsigned long sh_onoption (int); -extern unsigned long sh_offoption (int); -extern int sh_waitsafe (void); -extern int sh_exec (const Shnode_t*,int); -extern int sh_waitsafe(void); -extern int sh_exec(const Shnode_t*,int); -extern void **sh_getliblist(void); -extern Shell_t sh; -extern Namarr_t *nv_setarray (Namval_t*,void*(*)(Namval_t*,const char*,int)); -extern void *nv_associative (Namval_t*,const char*,int); -extern int nv_aindex (Namval_t*); -extern int nv_nextsub (Namval_t*); -extern char *nv_getsub (Namval_t*); -extern Namval_t *nv_putsub (Namval_t*, char*, long); -extern Namval_t *nv_opensub (Namval_t*); -extern int nv_adddisc (Namval_t*, const char**, Namval_t**); -extern int nv_clone (Namval_t*, Namval_t*, int); -extern void nv_close (Namval_t*); -extern void *nv_context (Namval_t*); -extern Namval_t *nv_create (const char*, Dt_t*, int,Namfun_t*); -extern Dt_t *nv_dict (Namval_t*); -extern Sfdouble_t nv_getn (Namval_t*, Namfun_t*); -extern Sfdouble_t nv_getnum (Namval_t*); -extern char *nv_getv (Namval_t*, Namfun_t*); -extern char *nv_getval (Namval_t*); -extern Namfun_t *nv_hasdisc (Namval_t*, const Namdisc_t*); -extern int nv_isnull (Namval_t*); -extern Namval_t *nv_lastdict (void); -extern void nv_newattr (Namval_t*,unsigned,int); -extern Namval_t *nv_open (const char*,Dt_t*,int); -extern void nv_putval (Namval_t*,const char*,int); -extern void nv_putv (Namval_t*,const char*,int,Namfun_t*); -extern int nv_scan (Dt_t*,void(*)(Namval_t*,void*),void*,int,int); -extern Namval_t *nv_scoped (Namval_t*); -extern char *nv_setdisc (Namval_t*,const char*,Namval_t*,Namfun_t*); -extern void nv_setref (Namval_t*, Dt_t*,int); -extern int nv_settype (Namval_t*, Namval_t*, int); -extern void nv_setvec (Namval_t*,int,int,char*[]); -extern void nv_setvtree (Namval_t*); -extern int nv_setsize (Namval_t*,int); -extern Namfun_t *nv_disc (Namval_t*,Namfun_t*,int); -extern void nv_unset (Namval_t*); -extern Namval_t *nv_search (const char *, Dt_t*, int); -extern void nv_unscope (void); -extern char *nv_name (Namval_t*); -extern Namval_t *nv_type (Namval_t*); -extern const Namdisc_t *nv_discfun (int); -/* end of automatically generated data */ - -/* Manually added based on libshell/common/include/builtins.h */ -extern int b_printf(int, char*[],void*); -extern int B_echo(int, char*[],void*); -extern int b_print(int, char*[],void*); -extern int b_pwd(int, char*[],void*); -extern int b_sleep(int, char*[],void*); -extern int b_test(int, char*[],void*); diff --git a/usr/src/lib/libshell/common/nval.3 b/usr/src/lib/libshell/common/nval.3 index 4acfc70b64..3926ef5b88 100644 --- a/usr/src/lib/libshell/common/nval.3 +++ b/usr/src/lib/libshell/common/nval.3 @@ -28,7 +28,9 @@ Namdisc_t; .ft 5 Namval_t *nv_open(const char *\fIname\fP, Dt_t *\fIdict\fP, int \fIflags\fP); Namval_t *nv_create(const char *\fIname\fP, Dt_t *\fIdict\fP, int \fIflags\fP, Namfun_t *\fIfp\fP); +Namval_t *nv_namptr(void *\fIptr\fP, int \fIindx\fP); void nv_close(Namval_t *\fInp\fP); +void nv_delete(Namval_t *\fInp\fP, Dt_t *\fIdict\fP, int \fInofree\fP); .ft R .fi .SS "GETTING AND SETTING VALUES" @@ -38,7 +40,7 @@ char *nv_getval(Namval_t *\fInp\fP); Sfdouble_t nv_getnum(Namval_t *\fInp\fP); char *nv_name(Namval_t *\fInp\fP); void nv_putval(Namval_t *\fInp\fP, const char *\fIval\fP, int \fIflags\fP); -void nv_unset(Namval_t *\fInp\fP); +void nv_unset(Namval_t *\fInp\fP, int \fIflags\fP); int nv_clone(Namval_t *\fIsrc\fP, Namval_t *\fIdest\fP, int \fIflags\fP); .ft R .fi @@ -49,6 +51,7 @@ int nv_isnull(Namval_t *\fInp\fP); int nv_setsize(Namval_t *\fInp\fP, int \fIsize\fP); int nv_size(Namval_t *\fInp\fP); unsigned nv_isattr(Namval_t *\fInp\fP, unsigned \fIflags\fP); +Namfun_t *nv_isvtree(Namval_t *\fInp\fP); unsigned nv_onattr(Namval_t *\fInp\fP, unsigned \fIflags\fP); unsigned nv_offattr(Namval_t *\fInp\fP, unsigned \fIflags\fP); void nv_newattr(Namval_t *\fInp\fP, unsigned \fIflags\fP, int \fIsize\fP); @@ -60,6 +63,7 @@ void nv_newattr(Namval_t *\fInp\fP, unsigned \fIflags\fP, int \fIsize\fP); .ft 5 unsigned nv_isarray(Namval_t *\fInp\fP); Namarr_t *nv_setarray(Namval_t *\fInp\fP,void*(*\fIfun\fP)(Namval_t*,const char*,int)); +Namarr_t *nv_arrayptr(Namval_t *\fInp\fP); Namval_t *nv_putsub(Namval_t *\fInp\fP, char *\fIname\fP, long \fImode\fP); Namval_t *nv_opensub(Namval_t *\fInp\fP); void nv_setvec(Namval_t *\fInp\fP, int \fIappend\fP, int \fIargc\fP, char *\fIargv\fP[]); @@ -81,11 +85,20 @@ char *nv_adddisc(Namval_t *\fInp\fP, const char **\fInames\fP); const Namdisc_t *nv_discfun(int \fIwhich\fP); .ft R .fi +.SS "TYPES" +.nf +.ft 5 +Namval_t *nv_type(Namval_t *\fInp\fP); +int *nv_settype(Namval_t *\fInp\fP, Namval_t *\fItp\fP, int \fIflags\fP); +Namval_t *nv_mkinttype(char *\fIname\fP, size_t \fIsz\fP, int \fIus\fP, const char *\fIstr\fP, Namdisc_t *\fIdp\fP); +void nv_addtype(Namval_t *\fInp\fP, const char *\fIstr\fP, Optdisc_t* *\fIop\fP, size_t \fIsz\fP); +.ft R +.fi .SS "MISCELLANEOUS FUNCTIONS" .nf .ft 5 int nv_scan(Dt_t *\fIdict\fP, void(*\fIfn\fP)(Namval_t*,void*), void *\fIdata\fP, int \fImask\fP, int \fIflags\fP); -Dt_t nv_dict(Namval_t *\fInp\fP); +Dt_t *nv_dict(Namval_t *\fInp\fP); void nv_setvtree(Namval_t *\fInp\fP); void nv_setref(Namval_t *\fInp\fP, Dt_t *\fIdp\fP, int \fIflags\fP); Namval_t *nv_lastdict(void); @@ -179,11 +192,25 @@ The \f5nv_lastdict()\fP function returns a pointer the the name-value pair that contains the last dictionary searched on the previous \f5nv_open()\fP. .PP +Name-value pairs can also be allocated without belonging to +a dictionary. They will typically be looked by a a \fIcreate\fP +discipline associated with a parent node. In this case the +node size will by \f5NV_MINSZ\fP and \fIn\fP nodes can be allocated +vial \f5calloc(5NV_MINSZ,\fIn\fP)\fP(3). +The \f5nv_namptr\fP function can be used on the pointer returned by +\f5calloc\fP along with the element number to return the +corresponding node. +Each of these nodes must be given the \f5NV_MINIMAL\fP attributes. +.PP The \f5nv_close()\fP indicates that the pointer returned by \f5nv_open()\fP or \f5nv_opensub()\fP will not be referenced again. If the name-value pair is unset, and not referenced elsewhere, the name-value pair may be freed. .PP +The \f5nv_delete()\fP function will remove the node \fInp\fP from +the dictionary \fIdict\fP. Unless \fInofree\fP is non-zero, the +node \fInp\fP will also be freed. +.PP The \f5nv_name()\fP function returns the name of the given name-value pair \fInp\fP. The \f5nv_setsize()\fP function returns the size of the field for @@ -228,6 +255,8 @@ as the value for \fInp\fP. The \f5nv_unset()\fP function clears out the value and attributes of the given name-value function but does not free the name-value pair. +If called from the \f5putval\fP discipline function, use the \fIflags\fP +argument as the \fIflags\fP to \f5nv_unset()\fP. Otherwise, use 0. .PP The following attributes can be associated with a name-value pair: .IP @@ -282,6 +311,11 @@ expanded as a string. This attribute has an associated number that defines the the precision of the mantissa. .IP +\f5NV_HEXFLOAT\fP: +Used in conjunction with \f5NV_INTEGER\fP and \f5NV_DOUBLE\fP to +cause the value to be represented in C99 %a format when expanded as +a string. +.IP \f5NV_BINARY\fP: The name-value pair contains a buffer of binary data and \f5nv_size()\fP is the number of bytes for this data. By default the value @@ -292,6 +326,10 @@ size to be fixed and data either truncated or filled with \f50\fP bytes. \f5NV_REF\fP: The name-value pair is a name reference variable. .IP +\f5NV_MINIMAL\fP: +The name-value pair node is not embedded in a dictionary +and is minimal size, \f5NV_MINSZ\fP. +.IP \f5NV_NODISC\fP: All discipline functions are ignored when performing assignments and lookups. @@ -311,6 +349,10 @@ to agree with the new attributes. For an array variable, the values for each of the subscripts will be changed. .PP +The \f5nv_isvtree()\fP function returns a pointer to the compound +variable discipline if the node \fInp\fP is a compound variable +or \f5NULL\fP otherwise. +.PP The \f5nv_isarray()\fP function returns a non-zero value if the specified name-value pair is an array. .PP @@ -558,6 +600,15 @@ The name of the current subscript must be returned. Returns a pointer to a name-value pair corresponding to the current subscript, or \f5NULL\fP if this array type doesn't create represent each element as a name-value pair. +.IP +\f5NV_ASETSUB\fP: +Set the current subscript to the name-value pair passed in +as the second argument. +.PP +If \fInp\fP refers to an array, +\f5nv_arrayptr()\fP returns a pointer to +the array discipline structure \f5Namarr_t\fP. +Otherwise \f5nv_arrayptr()\fP returns \f5NULL\fP. .PP If \fInp\fP refers to an array, the \f5nv_getsub()\fP returns a pointer to @@ -612,8 +663,31 @@ will return a string containing all the names and values of children nodes in a format that can be used in a shell compound assignment. .PP +The \f5nv_type()\fP function returns a name_value pair pointer +that contains the type definition for the specified name-value pair. +The \fInvname\fP field contains the name for the type. +.PP +The \f5nv_settype()\fP function converts the name-value pair +given by \fInp\fP into the type given by \fItp\fP. +.PP +The \f5nv_addtype()\fP function adds the name of the type given by +\fInp\fP to the list of declaration built-ins. The \fIstr\fP +argument contains the string used by \f5optget\fP(3) to generate +the man page and process the options. The \fIop\fP argument +specifies the callback discipline used by \f5optget\fP(3) and +\fIsz\fP specifies the size of the callback information so +that the discipline \fBoptget\fP(3) can be extended with private +data used by the callback function. +.P +The \f5nv_mkinttype()\fP function creates named integer types +of the specified \fIname\fP. The \fIsize\fP parameter is the size +in bytes of the integer variable and \fIus\fP is non-zero +for unsigned integer types. If \fIdp\fP is specified then integer +variables of this type will all use this discipline. .SH SEE ALSO +calloc(3) cdt(3) shell(3) +optget(3) .SH AUTHOR David G. Korn (dgk@research.att.com). diff --git a/usr/src/lib/libshell/common/scripts/crawlsrccomments.sh b/usr/src/lib/libshell/common/scripts/crawlsrccomments.sh new file mode 100644 index 0000000000..520d48f118 --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/crawlsrccomments.sh @@ -0,0 +1,1203 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +# constants values for tokenizer/parser stuff +typeset -r ch=( + newline=$'\n' + tab=$'\t' + formfeed=$'\f' +) + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +function printmsg +{ + print -u2 "$*" +} + + +function attrstrtoattrarray +{ +#set -o xtrace + typeset s="$1" + nameref aa=$2 # attribute array + integer aa_count=0 + integer aa_count=0 + typeset nextattr + integer currattrlen=0 + typeset tagstr + typeset tagval + + while (( ${#s} > 0 )) ; do + # skip whitespaces + while [[ "${s:currattrlen:1}" == ~(E)[[:blank:][:space:]] ]] ; do + (( currattrlen++ )) + done + s="${s:currattrlen:${#s}}" + + # anything left ? + (( ${#s} == 0 )) && break + + # Pattern tests: + #x="foo=bar huz=123" ; print "${x##~(E)[[:alnum:]_-:]*=[^[:blank:]\"]*}" + #x='foo="ba=r o" huz=123' ; print "${x##~(E)[[:alnum:]_-:]*=\"[^\"]*\"}" + #x="foo='ba=r o' huz=123" ; print "${x##~(E)[[:alnum:]_-:]*=\'[^\"]*\'}" + #x="foox huz=123" ; print "${x##~(E)[[:alnum:]_-:]*}" + # All pattern combined via eregex (w|x|y|z): + #x='foo="bar=o" huz=123' ; print "${x##~(E)([[:alnum:]_-:]*=[^[:blank:]\"]*|[[:alnum:]_-:]*=\"[^\"]*\"|[[:alnum:]_-:]*=\'[^\"]*\')}" + nextattr="${s##~(E)([[:alnum:]_-:]*=[^[:blank:]\"]*|[[:alnum:]_-:]*=\"[^\"]*\"|[[:alnum:]_-:]*=\'[^\"]*\'|[[:alnum:]_-:]*)}" + currattrlen=$(( ${#s} - ${#nextattr})) + + # add entry + tagstr="${s:0:currattrlen}" + if [[ "${tagstr}" == *=* ]] ; then + # normal case: attribute with value + + tagval="${tagstr#*=}" + + # strip quotes ('' or "") + if [[ "${tagval}" == ~(Elr)(\'.*\'|\".*\") ]] ; then + tagval="${tagval:1:${#tagval}-2}" + fi + + aa[${aa_count}]=( name="${tagstr%%=*}" value="${tagval}" ) + else + # special case for HTML where you have something like <foo baz> + aa[${aa_count}]=( name="${tagstr}" ) + fi + (( aa_count++ )) + (( aa_count > 1000 )) && fatal_error "$0: aa_count too large" # assert + done +} + +# XML document handler +function handle_xml_document +{ +#set -o xtrace + nameref callbacks=${1} + typeset tag_type="${2}" + typeset tag_value="${3}" + typeset tag_attributes="${4}" + nameref doc=${callbacks["arg_tree"]} + nameref nodepath="${stack.items[stack.pos]}" + nameref nodesnum="${stack.items[stack.pos]}num" + + case "${tag_type}" in + tag_comment) + nodepath[${nodesnum}]+=( + typeset tagtype="comment" + typeset tagvalue="${tag_value}" + ) + (( nodesnum++ )) + ;; + esac + +# print "xmltok: '${tag_type}' = '${tag_value}'" +} + +function xml_tok +{ + typeset buf="" + typeset namebuf="" + typeset attrbuf="" + typeset c="" + typeset isendtag # bool: true/false + typeset issingletag # bool: true/false (used for tags like "<br />") + nameref callbacks=${1} + + [[ ! -z "${callbacks["document_start"]}" ]] && ${callbacks["document_start"]} "${1}" "document_start" + + while IFS='' read -r -N 1 c ; do + isendtag=false + + if [[ "$c" == "<" ]] ; then + # flush any text content + if [[ "$buf" != "" ]] ; then + [[ ! -z "${callbacks["tag_text"]}" ]] && ${callbacks["tag_text"]} "${1}" "tag_text" "$buf" + buf="" + fi + + IFS='' read -r -N 1 c + if [[ "$c" == "/" ]] ; then + isendtag=true + else + buf="$c" + fi + IFS='' read -r -d '>' c + buf+="$c" + + # handle comments + if [[ "$buf" == ~(El)!-- ]] ; then + # did we read the comment completely ? + if [[ "$buf" != ~(Elr)!--.*-- ]] ; then + buf+=">" + while [[ "$buf" != ~(Elr)!--.*-- ]] ; do + IFS='' read -r -N 1 c || break + buf+="$c" + done + fi + + [[ ! -z "${callbacks["tag_comment"]}" ]] && ${callbacks["tag_comment"]} "${1}" "tag_comment" "${buf:3:${#buf}-5}" + buf="" + continue + fi + + # check if the tag starts and ends at the same time (like "<br />") + if [[ "${buf}" == ~(Er).*/ ]] ; then + issingletag=true + buf="${buf%*/}" + else + issingletag=false + fi + + # check if the tag has attributes (e.g. space after name) + if [[ "$buf" == ~(E)[[:space:][:blank:]] ]] ; then + namebuf="${buf%%~(E)[[:space:][:blank:]].*}" + attrbuf="${buf#~(E).*[[:space:][:blank:]]}" + else + namebuf="$buf" + attrbuf="" + fi + + if ${isendtag} ; then + [[ ! -z "${callbacks["tag_end"]}" ]] && ${callbacks["tag_end"]} "${1}" "tag_end" "$namebuf" + else + [[ ! -z "${callbacks["tag_begin"]}" ]] && ${callbacks["tag_begin"]} "${1}" "tag_begin" "$namebuf" "$attrbuf" + + # handle tags like <br/> (which are start- and end-tag in one piece) + if ${issingletag} ; then + [[ ! -z "${callbacks["tag_end"]}" ]] && ${callbacks["tag_end"]} "${1}" "tag_end" "$namebuf" + fi + fi + buf="" + else + buf+="$c" + fi + done + + [[ ! -z "${callbacks["document_end"]}" ]] && ${callbacks["document_end"]} "${1}" "document_end" "exit_success" + + print # final newline to make filters like "sed" happy +} + +# enumerate comments in a shell (or shell-like) script +function enumerate_comments_shell +{ + set -o errexit + + typeset input_file="$1" + nameref comment_array="$2" + integer max_num_comments="$3" + integer ca=0 # index in "comment_array" + + integer res=0 + + typeset comment="" + + while (( res == 0 )) ; do + IFS='' read -r line + (( res=$? )) + + if [[ "${line}" == ~(El)#.* ]] ; then + comment+="${line#\#}${ch.newline}" + else + if [[ "$comment" != "" ]] ; then + comment_array[ca++]="${comment}" + comment="" + + if (( ca > max_num_comments )) ; then + break + fi + fi + fi + done <"${input_file}" + + return 0 +} + + +# enumerate comments in a troff document +function enumerate_comments_troff +{ + set -o errexit + + typeset input_file="$1" + nameref comment_array="$2" + integer max_num_comments="$3" + integer ca=0 # index in "comment_array" + + integer res=0 + + typeset comment="" + + while (( res == 0 )) ; do + IFS='' read -r line + (( res=$? )) + + if [[ "${line}" == ~(El)\.*\\\" ]] ; then + comment+="${line#~(El)\.*\\\"}${ch.newline}" + else + if [[ "$comment" != "" ]] ; then + comment_array[ca++]="${comment}" + comment="" + + if (( ca > max_num_comments )) ; then + break + fi + fi + fi + done <"${input_file}" + + return 0 +} + + +# enumerate comments in files which are preprocessed by +# CPP (e.g. C, C++, Imakefile etc.) +function enumerate_comments_cpp +{ + set -o errexit +# set -o nounset + + integer err=0 + + typeset input_file="$1" + nameref comment_array="$2" + integer max_num_comments="$3" + integer max_filesize_for_scan="$4" + integer ca=0 # index in "comment_array" + + typeset content + integer content_length + + integer file_pos # file position + typeset line_pos=( + integer x=0 # X position in line + integer y=0 # Y position in line (line number) + ) + typeset c c2 + + typeset comment + + typeset state=( + # C comment state + typeset in_c_comment=false + # C++ comment state + typeset cxx=( + typeset in_comment=false + typeset comment_continued=false + # position of current //-pos + typeset comment_pos=( + integer x=-1 + integer y=-1 + ) + # position of previous //-pos + typeset comment_prev_pos=( + integer x=-1 + integer y=-1 + ) + ) + # literal state + typeset in_sq_literal=false # single-quote literal + typeset in_dq_literal=false # double-quote literal + ) + + content="$(< "${input_file}")" + + # Truncate file to "max_filesize_for_scan" charatcters. + # This was originally added to work around a performance problem with + # the ${str:offset:chunksize} operator which scales badly in ksh93 + # version 's' with the number of characters + if (( ${#content} > max_filesize_for_scan )) ; then + print -u2 -f "## WARNING: File '%s' truncated to %d characters\n" \ + "${input_file}" \ + max_filesize_for_scan + content="${content:0:max_filesize_for_scan}" + fi + content_length=${#content} + + # Iterate through the source code. The last character + # (when file_pos == content_length) will be empty to indicate + # EOF (this is needed for cases like when + # a C++ comment is not terminated by a newline... ;-/) + for (( file_pos=0 ; file_pos <= content_length ; file_pos++ )) ; do + c2="${content:file_pos:2}" + c="${c2:0:1}" + + if [[ "$c" == "${ch.newline}" ]] ; then + (( line_pos.x=0, line_pos.y++ )) + else + (( line_pos.x++ )) + fi + + if ${state.in_c_comment} ; then + if [[ "$c2" == "*/" ]] ; then + (( file_pos++, line_pos.x++ )) + state.in_c_comment=false + + # flush comment text + comment_array[ca++]="${comment}" + comment="" + + if (( ca > max_num_comments )) ; then + break + fi + else + comment+="$c" + fi + elif ${state.cxx.in_comment} ; then + if [[ "$c" == "${ch.newline}" || "$c" == "" ]] ; then + state.cxx.in_comment=false + + # flush comment text + if ${state.cxx.comment_continued} ; then + comment_array[ca-1]+="${ch.newline}${comment}" + (( state.cxx.comment_prev_pos.x=state.cxx.comment_pos.x , + state.cxx.comment_prev_pos.y=state.cxx.comment_pos.y )) + else + comment_array[ca++]="${comment}" + (( state.cxx.comment_prev_pos.x=state.cxx.comment_pos.x , + state.cxx.comment_prev_pos.y=state.cxx.comment_pos.y )) + fi + comment="" + + if (( ca > max_num_comments )) ; then + break + fi + else + comment+="$c" + fi + elif ${state.in_sq_literal} ; then + if [[ "$c" == "'" && "${content:file_pos-1:1}" != '\' ]] ; then + state.in_sq_literal=false + fi + elif ${state.in_dq_literal} ; then + if [[ "$c" == '"' && "${content:file_pos-1:1}" != '\' ]] ; then + state.in_dq_literal=false + fi + else + if [[ "$c2" == "/*" ]] ; then + (( file_pos++, line_pos.x++ )) + state.in_c_comment=true + comment="" + elif [[ "$c2" == "//" ]] ; then + (( file_pos++, line_pos.x++ )) + if (( state.cxx.comment_prev_pos.x == line_pos.x && \ + state.cxx.comment_prev_pos.y == (line_pos.y-1) )) ; then + state.cxx.comment_continued=true + else + state.cxx.comment_continued=false + fi + (( state.cxx.comment_pos.x=line_pos.x , state.cxx.comment_pos.y=line_pos.y )) + state.cxx.in_comment=true + comment="" + elif [[ "$c" == "'" && "${content:file_pos-1:1}" != '\' ]] ; then + state.in_sq_literal=true + elif [[ "$c" == '"' && "${content:file_pos-1:1}" != '\' ]] ; then + state.in_dq_literal=true + fi + fi + done + + if [[ "$comment" != "" ]] ; then + print -u2 "## ERROR: Comment text buffer not empty at EOF." + err=1 + fi + + if ${state.in_c_comment} ; then + print -u2 "## ERROR: C comment did not close before EOF." + err=1 + fi + + if ${state.cxx.in_comment} ; then + print -u2 "## ERROR: C++ comment did not close before EOF." + err=1 + fi + + if ${state.in_dq_literal} ; then + print -u2 "## ERROR: Double-quoted literal did not close before EOF." + err=1 + fi + + # We treat this one only as warning since things like "foo.html.cpp" may + # trigger this condition accidently + if ${state.in_sq_literal} ; then + print -u2 "## WARNING: Single-quoted literal did not close before EOF." + fi + + return $err +} + +# determine file type +function get_file_format +{ + set -o errexit + + typeset filename="$1" + nameref file_format="$2" + + typeset fileeval # evaluation result of /usr/bin/file + + # check whether "filename" is a plain, readable file + [[ ! -f "$filename" ]] && return 1 + [[ ! -r "$filename" ]] && return 1 + + # In theory this code would exclusively look at the contents of + # the file to figure out it's file format - unfortunately + # /usr/bin/file is virtually useless (the heuristics, matching + # and output unreliable) for many file formats and therefore + # we have to do a multi-stage approach which looks + # at the file's content if possible and at the filename + # otherwise. Fun... ;-( + + # pass one: Find matches for file formats where /usr/bin/file + # is known to be unreliable: + case "$filename" in + *.[ch] | *.cpp | *.cc | *.cxx | *.hxx) + file_format="c_source" + return 0 + ;; + *Imakefile) + file_format="imakefile" + return 0 + ;; + *Makefile) + file_format="makefile" + return 0 + ;; + esac + + # pass two: match by file content via /usr/bin/file + fileeval="$(LC_ALL=C /usr/bin/file "$filename")" + case "$fileeval" in + ~(E)roff) + file_format="troff" + return 0 + ;; + ~(E)html\ document) + file_format="html" + return 0 + ;; + ~(E)sgml\ document) + file_format="sgml" + return 0 + ;; + ~(E)executable.*(shell|(/|/r|/pf)(sh|ksh|ksh93|rksh93|dtksh|tksh|bash))\ script) + file_format="shell" + return 0 + ;; + ~(E)executable.*/perl\ script) + file_format="perl" + return 0 + ;; + esac + + # pass three: fallhack to filename matching + case "$filename" in + *.man) + file_format="troff" + return 0 + ;; + *.html) + file_format="html" + return 0 + ;; + *.sgml) + file_format="sgml" + return 0 + ;; + *.xml) + file_format="xml" + return 0 + ;; + *.png) + file_format="image_png" + return 0 + ;; + *.xcf) + file_format="image_xcf" + return 0 + ;; + *.shar) + file_format="archive_shell" + return 0 + ;; + *.sh) + file_format="shell" + return 0 + ;; + *.pcf) + file_format="font_pcf" + return 0 + ;; + *.bdf) + file_format="font_bdf" + return 0 + ;; + *.pmf) + file_format="font_pmf" + return 0 + ;; + *.ttf | *.otf) + file_format="font_ttf" + return 0 + ;; + *.pfa | *.pfb) + file_format="font_postscript" + return 0 + ;; + esac + + return 1 +} + +function extract_comments +{ + set -o errexit + + nameref records="$1" + typeset filename="$2" + integer max_num_comments="$3" + integer max_filesize_for_scan="$4" + + typeset datatype="" + + records[${filename}]=( + typeset filename="$filename" + + typeset fileformat_found="false" # "true" or "false" + typeset file_format="" + + typeset -A hashsum + + typeset comments_parsed="false" # "true" or "false" + typeset -a comments + ) + + records[${filename}].hashsum["md5"]="$(sum -x md5 < "$filename")" + records[${filename}].hashsum["sha1"]="$(sum -x sha1 < "$filename")" + + if get_file_format "$filename" datatype ; then + records[${filename}].fileformat_found="true" + records[${filename}].file_format="$datatype" + else + return 1 + fi + + case "$datatype" in + c_source|imakefile) + enumerate_comments_cpp "${filename}" "records[${filename}].comments" ${max_num_comments} ${max_filesize_for_scan} && \ + records[${filename}].comments_parsed=true + ;; + shell|makefile) + enumerate_comments_shell "${filename}" "records[${filename}].comments" ${max_num_comments} ${max_filesize_for_scan} && \ + records[${filename}].comments_parsed=true + ;; + troff) + enumerate_comments_troff "${filename}" "records[${filename}].comments" ${max_num_comments} ${max_filesize_for_scan} && \ + records[${filename}].comments_parsed=true + ;; + # NOTE: Disabled for now + #xml|html|sgml) + # enumerate_comments_xml "${filename}" "records[${filename}].comments" ${max_num_comments} ${max_filesize_for_scan} && \ + # records[${filename}].comments_parsed=true + # ;; + esac + + return 0 +} + +# parse HTTP return code, cookies etc. +function parse_http_response +{ + nameref response="$1" + typeset h statuscode statusmsg i + + # we use '\r' as additional IFS to filter the final '\r' + IFS=$' \t\r' read -r h statuscode statusmsg # read HTTP/1.[01] <code> + [[ "$h" != ~(Eil)HTTP/.* ]] && { print -u2 -f $"%s: HTTP/ header missing\n" "$0" ; return 1 ; } + [[ "$statuscode" != ~(Elr)[0-9]* ]] && { print -u2 -f $"%s: invalid status code\n" "$0" ; return 1 ; } + response.statuscode="$statuscode" + response.statusmsg="$statusmsg" + + # skip remaining headers + while IFS='' read -r i ; do + [[ "$i" == $'\r' ]] && break + + # strip '\r' at the end + i="${i/~(Er)$'\r'/}" + + case "$i" in + ~(Eli)Content-Type:.*) + response.content_type="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Content-Length:[[:blank:]]*[0-9]*) + integer response.content_length="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Transfer-Encoding:.*) + response.transfer_encoding="${i/~(El).*:[[:blank:]]*/}" + ;; + esac + done + + return 0 +} + +function cat_http_body +{ + typeset emode="$1" + typeset hexchunksize="0" + integer chunksize=0 + + if [[ "${emode}" == "chunked" ]] ; then + while IFS=$'\r' read hexchunksize && + [[ "${hexchunksize}" == ~(Elri)[0-9abcdef]* ]] && + (( chunksize=16#${hexchunksize} )) && (( chunksize > 0 )) ; do + dd bs=1 count="${chunksize}" 2>/dev/null + done + else + cat + fi + + return 0 +} + +function cat_http +{ + typeset protocol="${1%://*}" + typeset path1="${1#*://}" # "http://foo.bat.net/x/y.html" ----> "foo.bat.net/x/y.html" + + typeset host="${path1%%/*}" + typeset path="${path1#*/}" + typeset port="${host##*:}" + + integer netfd + typeset -C httpresponse # http response + + # If URL did not contain a port number in the host part then look at the + # protocol to get the port number + if [[ "${port}" == "${host}" ]] ; then + case "${protocol}" in + "http") port=80 ;; + *) port="$(getent services "${protocol}" | sed 's/[^0-9]*//;s/\/.*//')" ;; + esac + else + host="${host%:*}" + fi + + printmsg "protocol=${protocol} port=${port} host=${host} path=${path}" + + # prechecks + [[ "${protocol}" == "" ]] && { print -u2 -f "%s: protocol not set.\n" "$0" ; return 1 ; } + [[ "${port}" == "" ]] && { print -u2 -f "%s: port not set.\n" "$0" ; return 1 ; } + [[ "${host}" == "" ]] && { print -u2 -f "%s: host not set.\n" "$0" ; return 1 ; } + [[ "${path}" == "" ]] && { print -u2 -f "%s: path not set.\n" "$0" ; return 1 ; } + + # open TCP channel + redirect {netfd}<>"/dev/tcp/${host}/${port}" + (( $? != 0 )) && { print -u2 -f "%s: Couldn't open %s\n" "$0" "${1}" ; return 1 ; } + + # send HTTP request + request="GET /${path} HTTP/1.1\r\n" + request+="Host: ${host}\r\n" + request+="User-Agent: crawlsrccomments/ksh93 (2008-06-14; $(uname -s -r -p))\r\n" + request+="Connection: close\r\n" + print -n -- "${request}\r\n" >&${netfd} + + # collect response and send it to stdout + parse_http_response httpresponse <&${netfd} + cat_http_body "${httpresponse.transfer_encoding}" <&${netfd} + + # close connection + redirect {netfd}<&- + + return 0 +} + +function print_stats +{ + set -o errexit + + # gather some statistics + typeset stats=( + integer files_with_comments=0 + integer files_without_comments=0 + + integer files_without_known_format=0 + + integer files_with_license_info=0 + integer files_without_license_info=0 + + integer total_num_files=0 + ) + + for i in $(printf "%s\n" "${!records[@]}" | sort) ; do + if "${records[$i].comments_parsed}" ; then + (( stats.files_with_comments++ )) + else + (( stats.files_without_comments++ )) + fi + + if ! "${records[$i].fileformat_found}" ; then + (( stats.files_without_known_format++ )) + fi + + if "${records[$i].license_info_found}" ; then + (( stats.files_with_license_info++ )) + else + (( stats.files_without_license_info++ )) + fi + + (( stats.total_num_files++ )) + done + + printf "%B\n" stats + return 0 +} + + +function print_comments_plain +{ + set -o errexit + + nameref records=$1 + nameref options=$2 + typeset i j + + for i in $(printf "%s\n" "${!records[@]}" | sort) ; do + nameref node=records[$i] + + if [[ "${options.filepattern.accept}" != "" ]] && \ + [[ "${node.filename}" != ${options.filepattern.accept} ]] ; then + continue + fi + if [[ "${options.filepattern.reject}" != "" ]] && \ + [[ "${node.filename}" == ${options.filepattern.reject} ]] ; then + continue + fi + + node.license_info_found=false + + if ! "${node.comments_parsed}" ; then + continue + fi + + for j in "${!node.comments[@]}" ; do + typeset s="${node.comments[$j]}" + typeset match=false + + if [[ "${options.commentpattern.accept}" != "" ]] && \ + [[ "$s" == ${options.commentpattern.accept} ]] ; then + match=true + fi + if [[ "${options.commentpattern.reject}" != "" ]] && \ + [[ "$s" == ${options.commentpattern.reject} ]] ; then + match=false + fi + + if "${match}" ; then + printf "\f#### filename='%s',\tcomment=%s\n" "${node.filename}" "$j" + printf "%s\n" "$s" + node.license_info_found=true + fi + done + + if ! "${node.license_info_found}" ; then + printf "## no match found in '%s'," "${node.filename}" + printf "comments_parsed=%s, fileformat_found=%s, file_format=%s\n" \ + "${node.comments_parsed}" \ + "${node.fileformat_found}" \ + "${node.file_format}" + fi + done + + return 0 +} + +function print_comments_duplicates_compressed +{ + set -o errexit + + nameref records=$1 + nameref options=$2 + typeset i j + typeset -A hashed_comments + integer num_hashed_comments + + for i in $(printf "%s\n" "${!records[@]}" | sort) ; do + nameref node=records[$i] + + if [[ "${options.filepattern.accept}" != "" ]] && \ + [[ "${node.filename}" != ${options.filepattern.accept} ]] ; then + continue + fi + if [[ "${options.filepattern.reject}" != "" ]] && \ + [[ "${node.filename}" == ${options.filepattern.reject} ]] ; then + continue + fi + + node.license_info_found=false + + if ! "${node.comments_parsed}" ; then + continue + fi + + for j in "${!node.comments[@]}" ; do + typeset s="${node.comments[$j]}" + typeset match=false + + if [[ "${options.commentpattern.accept}" != "" ]] && \ + [[ "$s" == ${options.commentpattern.accept} ]] ; then + match=true + fi + if [[ "${options.commentpattern.reject}" != "" ]] && \ + [[ "$s" == ${options.commentpattern.reject} ]] ; then + match=false + fi + + + if "${match}" ; then + typeset -l hashstring # lowercase + + # compress the comment (e.g. convert whiteapces and '.,:;()"' to newline characters) ... + hashstring="${s//+([\n\r\t\v*#.,:;\(\)\"[:space:][:blank:]])/${ch.newline}}" + # ... and then create a MD5 hash from this string + hash="$(sum -x md5 <<<"${hashstring}")" + + nameref hc_node=hashed_comments[${hash}] + + if [[ "${hc_node}" == "" ]] ; then + # build node if there isn't one yet + typeset -a hc_node.fileids + typeset hc_node.comment="$s" + fi + + hc_node.fileids+=( "$(printf "%s (md5='%s', sha1='%s')\n" "${node.filename}" "${node.hashsum["md5"]}" "${node.hashsum["sha1"]}")" ) + + node.license_info_found=true + fi + done + + if ! "${node.license_info_found}" ; then + printf "## no match found in " + printf "%s (md5='%s', sha1='%s'), " "${node.filename}" "${node.hashsum["md5"]}" "${node.hashsum["sha1"]}" + printf "comments_parsed=%s, fileformat_found=%s, file_format=%s\n" \ + "${node.comments_parsed}" \ + "${node.fileformat_found}" \ + "${node.file_format}" + fi + done + + # print comments and all fileids (filename+hash sums) which include this comment + for i in "${!hashed_comments[@]}" ; do + printf "\f## The comment (ID=%s) ..." "${i}" + printf "\n-- snip --" + printf "\n%s" "${hashed_comments[${i}].comment}" + printf "\n-- snip --" + printf "\n... applies to the following files:\n" + printf "\t%s\n" "${hashed_comments[${i}].fileids[@]}" # printf repeats the format string for each array memeber + done + + return 0 +} + +function do_crawl +{ + set -o errexit + + typeset options=( + integer max_filesize_for_scan=$((256*1024)) + integer max_num_comments=$((2**62)) # FIXME: This should be "+Inf" (=Infinite) + ) + + shift + while getopts -a "${progname}" "${do_crawl_usage}" OPT "$@" ; do + printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + S) options.max_filesize_for_scan="${OPTARG}" ;; + N) options.max_num_comments="${OPTARG}" ;; + *) usage do_crawl_usage ;; + esac + done + shift $((OPTIND-1)) + + typeset scan=( + typeset -A records + ) + + # read filenames from stdin + while read i ; do + printf "## scanning %s ...\n" "$i" + extract_comments scan.records "$i" ${options.max_num_comments} ${options.max_filesize_for_scan} || true + done + + # print compound variable array (we strip the "typeset -A records" for now) + printf "%B\n" scan | + sed $'s/^#.*$//;s/^\(//;s/^\)//;s/^\ttypeset -A records=\(//;s/^\t\)//' >"crawlsrccomments_extracted_comments.cpv" + + print "# Wrote results to crawlsrccomments_extracted_comments.cpv" + + return 0 +} + +function do_getcomments +{ + set -o errexit + + # vars + typeset scan=( + typeset -A records + ) + typeset database + typeset tmp + + typeset options=( + typeset database="crawlsrccomments_extracted_comments.cpv" + + typeset print_stats=false + typeset zapduplicates=false + typeset filepattern=( + typeset accept="*" + typeset reject="" + ) + typeset commentpattern=( + typeset accept="~(Ei)(license|copyright)" + typeset reject="" + ) + ) + + shift + while getopts -a "${progname}" "${do_getcomments_usage}" OPT "$@" ; do + # printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + c) options.commentpattern.accept="${OPTARG}" ;; + C) options.commentpattern.reject="${OPTARG}" ;; + D) options.database="${OPTARG}" ;; + l) options.filepattern.accept="${OPTARG}" ;; + L) options.filepattern.reject="${OPTARG}" ;; + S) options.print_stats=true ;; + +S) options.print_stats=false ;; + Z) options.zapduplicates=true ;; + +Z) options.zapduplicates=false ;; + *) usage do_getcomments_usage ;; + esac + done + shift $((OPTIND-1)) + + # array of temporary files which should be cleaned-up upon exit + typeset -a tmpfiles + trap 'set -o errexit ; print -u2 "# Cleaning up..." ; ((${#tmpfiles[@]} > 0)) && rm -- "${tmpfiles[@]}" ; print -u2 "# Done."' EXIT + + # Support for HTTP URLs + if [[ "${options.database}" == ~(El)http://.* ]] ; then + database="/tmp/extract_license_cat_http_${PPID}_$$.tmp" + tmpfiles+=( "${database}" ) + print -u2 "# Loading URL..." + cat_http "${options.database}" >"${database}" + print -u2 "# Loading URL done." + else + database="${options.database}" + fi + + if [[ ! -r "${database}" ]] ; then + fatal_error "Can't read ${database}." + fi + + # Support for compressed database files + case "$(LC_ALL=C /usr/bin/file "${database}")" in + *bzip2*) + tmp="/tmp/extract_license_bzcat_${PPID}_$$.tmp" + tmpfiles+=( "${tmp}" ) + print -u2 "# Uncompressing data (bzip2) ..." + bzcat <"${database}" >"${tmp}" + print -u2 "# Uncompression done." + database="${tmp}" + ;; + *gzip*) + tmp="/tmp/extract_license_bzcat_${PPID}_$$.tmp" + tmpfiles+=( "${tmp}" ) + print -u2 "# Uncompressing data (gzip) ..." + gunzip -c <"${database}" >"${tmp}" + print -u2 "# Uncompression done." + database="${tmp}" + ;; + esac + + # Read compound variable which contain all recorded comments + print -u2 "# reading records..." + { + printf "(" + cat "${database}" + printf ")\n" + } | read -C scan.records || fatal_error 'Error reading data.' + print -u2 -f "# reading %d records done.\n" "${#scan.records[@]}" + + # print comments + print -u2 "# processing data..." + print "## comments start:" + if "${options.zapduplicates}" ; then + print_comments_duplicates_compressed scan.records options + else + print_comments_plain scan.records options + fi + print "## comments end" + print -u2 "# processing data done." + + if "${options.print_stats}" ; then + print_stats + fi + + return 0 +} + +function usage +{ + nameref usagemsg=$1 + OPTIND=0 + getopts -a "${progname}" "${usagemsg}" OPT '-?' + exit 2 +} + +typeset -r do_getcomments_usage=$'+ +[-?\n@(#)\$Id: getcomments (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@sun.com>] +[+NAME?getcomments - extract license information from source files] +[+DESCRIPTION?\bgetcomments\b is a small utilty script which extracts + license information from the "\bgetcomments\b"-database + file created by \bcrawl\b. The script allows various + filters (see options below) to be applied on the database] +[+?The license extraction is done in two steps - first a crawler script + called \bcrawl\b will scan all source files, extract + the comments and stores this information in a "database" file called + "crawlsrccomments_extracted_comments.cpv" and then \bextract_license\b allows + queries on this database.] +[D:database?Database file for input (either file or http://-URL).]:[database] +[l:acceptfilepattern?Process only files which match pattern.]:[pattern] +[L:rejectfilepattern?Process only files which do not match pattern.]:[pattern] +[c:acceptcommentpattern?Match comments which match pattern. Defaults to ~(Ei)(license|copyright)]:[pattern] +[C:rejectcommentpattern?Discard comments which match pattern. Defaults to ""]:[pattern] +[S:stats?Print statistics.] +[Z:zapsimilar?Combine similar/duplicate comments in the report.] +[+SEE ALSO?\bksh93\b(1), \bsvcprop\b(1)] +' + +typeset -r do_crawl_usage=$'+ +[-?\n@(#)\$Id: crawl (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@sun.com>] +[+NAME?crawl - crawl comment information from source files] +[+DESCRIPTION?\bcrawl\b is a small utilty script which reads + a list of source code files from stdin, determinates the type of + syntax used by these files and then extracts + comments from the source code and stores this information into a + "database"-like file called "crawlsrccomments_extracted_comments.cpv" which can then + be processed by \bextract_license\b or similar processing tools.] +[S:scanmaxcharacters?Scan a maximum number of numchars characters for comments. + Defaults to 256K characters.]:[numchars] +[N:maxnumcomments?Maximum numbers of comments to crawl. Defaults to "+Infinite"]:[numcomments] +[+SEE ALSO?\bksh93\b(1), \bsvcprop\b(1)] +' + +typeset -r crawlsrccomments_usage=$'+ +[-?\n@(#)\$Id: crawlsrccomments (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@sun.com>] +[+NAME?crawlsrccomments - extract and filter comment information from source files] +[+DESCRIPTION?\bcrawlsrccomments\b is a small utilty script which reads + a list of source code files from stdin, determinates the type of + syntax used by these files and then extracts + comments from the source code and stores this information into a + "database"-like file called "crawlsrccomments_extracted_comments.cpv" which can then + be processed by \bextract_license\b or similar processing tools.] + +[crawl|getcomments] options + +[+SEE ALSO?\bksh93\b(1), \bsvcprop\b(1)] +' + + +# program start +builtin basename +builtin cat +builtin date +builtin uname +builtin rm +builtin sum || fatal_error "sum builtin not found." + +# exit at the first error we hit +set -o errexit + +typeset progname="${ basename "${0}" ; }" + +while getopts -a "${progname}" "${crawlsrccomments_usage}" OPT ; do + # printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage crawlsrccomments_usage ;; + esac +done +shift $((OPTIND-1)) + +typeset cmd="$1" + +case "$cmd" in + "crawl") + progname+=" ${cmd}" + do_crawl "$@" + exit $? + ;; + "getcomments") + progname+=" ${cmd}" + do_getcomments "$@" + exit $? + ;; + *) + usage crawlsrccomments_usage + ;; +esac + +fatal_error "not reached." +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/filemutexdemo1.sh b/usr/src/lib/libshell/common/scripts/filemutexdemo1.sh new file mode 100644 index 0000000000..1de54a92fc --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/filemutexdemo1.sh @@ -0,0 +1,267 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# filemutexdemo1 - a simple locking demo which supports read/write +# locks and critical sections (like JAVA's "syncronized" keyword) +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +# Definition for a mutex which uses the filesystem for locking +typeset -T filemutex_t=( + typeset name + + typeset lock_dirname + + typeset locked_exclusive="false" + typeset locked_shared="false" + + # keep track of subshell level. The problem is that we do not know a + # way to figure out whether someone calls "unlock" in a subshell and then + # leaves the subshell and calls "unlock" again + integer subshell=-1 + + typeset lock_dirname + + # create a filemutex instance (including lock directory) + function create + { + # make sure we return an error if the init didn't work + set -o errexit + + [[ "$1" == "" ]] && return 1 + + _.name="$1" + _.lock_dirname="/tmp/filemutex_t_${_.name}.lock" + + mkdir "${_.lock_dirname}" + + # last entry, used to mark the mutex as initalised+valid + (( _.subshell=.sh.subshell )) + return 0 + } + + # use a filemutex instance (same as "create" but without creating + # the lock directory) + function create_child + { + # make sure we return an error if the init didn't work + set -o errexit + + [[ "$1" == "" ]] && return 1 + + _.name="$1" + _.lock_dirname="/tmp/filemutex_t_${_.name}.lock" + + # last entry, used to mark the mutex as initalised+valid + (( _.subshell=.sh.subshell )) + return 0 + } + + function check_subshell + { + (( _.subshell == .sh.subshell )) && return 0 + print -u2 -f "filemutex_t.%s(%s): Wrong subshell level\n" "$1" "${_.name}" + return 1 + } + + function try_lock_shared + { + _.check_subshell "try_lock_shared" || return 1 + + mkdir "${_.lock_dirname}/shared_${PPID}_$$" 2>/dev/null || return 1 + _.locked_shared="true" + return 0 + } + + function lock_shared + { + float interval=0.2 + + _.check_subshell "lock_shared" || return 1 + + while ! _.try_lock_shared ; do sleep ${interval} ; (( interval+=interval/10. )) ; done + return 0 + } + + function try_lock_exclusive + { + _.check_subshell "try_lock_exclusive" || return 1 + + rmdir "${_.lock_dirname}" 2>/dev/null || return 1 + _.locked_exclusive="true" + return 0 + } + + function lock_exclusive + { + float interval=0.2 + + _.check_subshell "lock_exclusive" || return 1 + + while ! _.try_lock_exclusive ; do sleep ${interval} ; (( interval+=interval/10. )) ; done + return 0 + } + + # critical section support (like java's "synchronized" keyword) + function synchronized + { + integer retcode + + _.check_subshell "synchronized" || return 1 + + _.lock_exclusive + + "$@" + (( retcode=$? )) + + _.unlock + + return ${retcode} + } + + # critical section support with shared lock + function synchronized_shared + { + integer retcode + + _.check_subshell "synchronized_shared" || return 1 + + _.lock_shared + + "$@" + (( retcode=$? )) + + _.unlock + + return ${retcode} + } + + function unlock + { + # return an error if rmdir/mkdir/check_subshell fail... + set -o errexit + + _.check_subshell "unlock" + + if ${_.locked_shared} ; then + rmdir "${_.lock_dirname}/shared_${PPID}_$$" + _.locked_shared="false" + return 0 + elif ${_.locked_exclusive} ; then + mkdir "${_.lock_dirname}" + _.locked_exclusive="false" + return 0 + fi + + print -u2 -f "filemutex_t.unlock(%s): mutex '%s' not locked." "$1" "${_.name}" + return 1 + } + + # destroy mutex if noone is using it anymore (not the same as "unset" !!)) + function destroy + { + _.check_subshell "destroy" || return 1 + + (${_.locked_exclusive} || ${_.locked_shared}) && _.unlock + rmdir "${_.lock_dirname}" + return 0 + } +) + +# main +builtin mkdir +builtin rmdir + +print "## Start." + +typeset -r mymutexname="hello_world" + +filemutex_t fs + +fs.create "${mymutexname}" || print -u2 "Mutex init failed." + +print "# Starting child which keeps an exclusive lock for 10 seconds..." +( + filemutex_t child_fs + + child_fs.create_child "${mymutexname}" + + child_fs.lock_exclusive + sleep 10 + child_fs.unlock +) & + +sleep 1 + +printf "%T: # Waiting to obtain a shared lock...\n" +fs.lock_shared +printf "%T: # Obtained shared lock\n" + +printf "fs.locked_exclusive=%s, fs.locked_shared=%s\n" "${fs.locked_exclusive}" "${fs.locked_shared}" + +ls -lad /tmp/filemutex*/* + +printf "%T: # Executing child which runs printf '|%%s|\\\n' 'hello' 'world' inside a synchronized section\n" +( + filemutex_t child_fs + + child_fs.create_child "${mymutexname}" + + child_fs.synchronized printf '|%s|\n' 'hello' 'world' +) & + +printf "%T: # Sleeping 5 secs while holding the shared lock...\n" +sleep 5. + +printf "%T: # Releasing shared lock...\n" +fs.unlock + +sleep 5. +print "# Destroying lock..." +fs.destroy + +print "## Done." + +exit 0 diff --git a/usr/src/lib/libshell/common/scripts/filetree1.sh b/usr/src/lib/libshell/common/scripts/filetree1.sh new file mode 100644 index 0000000000..6946fca4fb --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/filetree1.sh @@ -0,0 +1,229 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + + +function do_directory +{ + nameref tree=$1 + typeset basedir="$2" + + typeset basename + typeset dirname + typeset i + typeset dummy + + typeset -A tree.files + typeset -A tree.dirs + + find "${basedir}"/* -prune 2>/dev/null | while read i ; do + dirname="$(dirname "$i")" + basename="$(basename "$i")" + + # define "node" + if [[ -d "$i" ]] ; then + typeset -C tree.dirs["${basename}"] + nameref node=tree.dirs["${basename}"] + typeset -C node.flags + node.flags.dir="true" + node.flags.file="false" + else + typeset -C tree.files["${basename}"] + nameref node=tree.files["${basename}"] + typeset -C node.flags + + node.flags.dir="false" + node.flags.file="true" + fi + + # basic attributes + typeset -C node.paths=( + dirname="${dirname}" + basename="${basename}" + path="${i}" + ) + + nameref nflags=node.flags + [[ -r "$i" ]] && nflags.readable="true" || nflags.readable="false" + [[ -w "$i" ]] && nflags.writeable="true" || nflags.writeable="false" + [[ -x "$i" ]] && nflags.executable="true" || nflags.executable="false" + + [[ -b "$i" ]] && nflags.blockdevice="true" || nflags.blockdevice="false" + [[ -c "$i" ]] && nflags.characterdevice="true" || nflags.characterdevice="false" + [[ -S "$i" ]] && nflags.socket="true" || nflags.socket="false" + + [[ -L "$i" ]] && nflags.symlink="true" || nflags.symlink="false" + + integer node.size + integer node.links + typeset -C node.owner + ( [[ -x /usr/bin/runat ]] && ls -@ade "$i" || ls -lade "$i" ) | + IFS=' ' read \ + node.mask \ + node.links \ + node.owner.uid \ + node.owner.gid \ + node.size \ + dummy + + typeset -C node.extended_attributes + if [[ ${node.mask} == ~(Er)@ ]] ; then + node.extended_attributes.hasattrs="true" + typeset -a attrlist=( + $( runat "$i" "ls -1" ) + ) + else + node.extended_attributes.hasattrs="false" + fi + + if ${nflags.readable} ; then + # note that /usr/xpg4/bin/file does not use $'\t' as seperator - we + # have to use ':' instead. + file -h "$i" | IFS=' ' read dummy node.filetype + fi + + if ${nflags.dir} ; then + do_directory "${!node}" "$i" + fi + done + + # remove empty lists + (( ${#tree.files[@]} == 0 )) && unset tree.files + (( ${#tree.dirs[@]} == 0 )) && unset tree.dirs + + return 0 +} + + +function pathtovartree +{ + nameref tree=$1 + typeset basedir="$2" + + do_directory tree "${basedir}" + + return 0 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${filetree1_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename +builtin cat +builtin dirname +builtin date +builtin uname + +typeset progname="${ basename "${0}" ; }" + +typeset -r filetree1_usage=$'+ +[-?\n@(#)\$Id: filetree1 (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@sun.com>] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?filetree1 - file tree demo] +[+DESCRIPTION?\bfiletree1\b is a small ksh93 compound variable demo + which accepts a directory name as input, and then builds tree + nodes for all files+directories and stores all file attributes + in these notes and then outputs the tree in the format + specified by viewmode (either "list", "namelist" or "tree")..] + +viewmode dirs + +[+SEE ALSO?\bksh93\b(1), \bfile\b(1)] +' + +while getopts -a "${progname}" "${filetree1_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +typeset viewmode="$1" +shift + +if [[ "${viewmode}" != ~(Elr)(list|namelist|tree) ]] ; then + fatal_error $"Invalid view mode \"${viewmode}\"." +fi + +typeset -C myfiletree + +while (( $# > 0 )) ; do + print -u2 -f "# Scanning %s ...\n" "${1}" + pathtovartree myfiletree "${1}" + shift +done +print -u2 $"#parsing completed." + +case "${viewmode}" in + list) + set | egrep "^myfiletree\[" | fgrep -v ']=$' + ;; + namelist) + typeset + | egrep "^myfiletree\[" + ;; + tree) + printf "%B\n" myfiletree + ;; + *) + fatal_error $"Invalid view mode \"${viewmode}\"." + ;; +esac + +print -u2 $"#done." + +exit 0 +# EOF. diff --git a/usr/src/lib/libshell/common/fun/gnaw b/usr/src/lib/libshell/common/scripts/gnaw.sh index 159d420207..880e18118b 100644 --- a/usr/src/lib/libshell/common/fun/gnaw +++ b/usr/src/lib/libshell/common/scripts/gnaw.sh @@ -1,4 +1,4 @@ -#!/bin/ksh93 +#!/usr/bin/ksh93 # # CDDL HEADER START @@ -22,39 +22,65 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # # gnaw - a simple ksh93 technology demo # # Note that this script has been written with the main idea to show # many of ksh93's new features (comparing to ksh88/bash) and not -# as an example of efficient&&clean script code. +# as an example of efficient&&clean script code (much of the code +# could be done more efficient using compound variables, this script +# focus is the usage of associative arrays). # -# Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not POSIX-conformant -export PATH=/usr/xpg4/bin:/bin:/usr/bin +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C function print_setcursorpos { - print -n "${vtcode[cup_${1}_${2}]}" + print -n -- "${vtcode[cup_${1}_${2}]}" } function beep { - ${quiet} || print -n "${vtcode["bel"]}" + ${quiet} || print -n -- "${vtcode["bel"]}" } function fatal_error { - print -u 2 "${progname}: $@" + print -u2 "${progname}: $*" exit 1 } +# Get terminal size and put values into a compound variable with the integer +# members "columns" and "lines" +function get_term_size +{ + nameref rect=$1 + + rect.columns=${ tput cols ; } || return 1 + rect.lines=${ tput lines ; } || return 1 + + return 0 +} + function print_levelmap { integer screen_y_offset=$1 @@ -62,7 +88,7 @@ function print_levelmap integer max_numlines=$3 # maximum lines we're allowed to render integer x integer y - line="" + typeset line="" print_setcursorpos 0 ${screen_y_offset} @@ -72,21 +98,24 @@ function print_levelmap line+="${levelmap["${x}_${y}"]}" done - print "${line} " + print -- "${line} " done # print lines filled with spaces for each line not filled # by the level map line="${vtcode["spaceline"]:0:${levelmap["max_x"]}}" for (( ; (y-start_y_pos) < max_numlines ; y++ )) ; do - print "${line} " + print -- "${line} " done + return 0 } function level_completed { - render_buffer="$( - print -n "${vtcode["clear"]}" + integer i + typeset dummy + typeset render_buffer="$( + print -n -- "${vtcode["clear"]}" cat <<ENDOFTEXT # ###### # # ###### # @@ -111,22 +140,24 @@ ENDOFTEXT printf " SCORE: --> %s <--\n" "${player["score"]}" printf " LIVES: --> %s <--\n" "${player["lives"]}" )" - print "${render_buffer}" + print -- "${render_buffer}${end_of_frame}" # wait five seconds and swallow any user input for (( i=0 ; i < 50 ; i++ )) ; do - read -t 0.1 -n 1 dummy + read -r -t 0.1 -n 1 dummy done - print "Press any key to continue..." + print "Press any key to continue...${end_of_frame}" # wait five secs or for a key - read -t 5 -n 1 dummy + read -r -t 5 -n 1 dummy + return 0 } function game_over { - render_buffer="$( - print -n "${vtcode["clear"]}" + typeset dummy + typeset render_buffer="$( + print -n -- "${vtcode["clear"]}" cat <<ENDOFTEXT #### ## # # ###### @@ -149,21 +180,22 @@ ENDOFTEXT printf "\n SCORE: --> %s <--\n" "${player["score"]}" )" - print "${render_buffer}" + print -r -- "${render_buffer}${end_of_frame}" # wait five seconds and swallow any user input for (( i=0 ; i < 50 ; i++ )) ; do - read -t 0.1 -n 1 dummy + read -r -t 0.1 -n 1 dummy done - print "Press any key to continue..." + print "Press any key to continue...${end_of_frame}" # wait five secs or for a key - read -t 5 -n 1 dummy + read -r -t 5 -n 1 dummy + return 0 } function run_logo { - render_buffer="$( + typeset render_buffer="$( cat <<ENDOFTEXT ##### # # # # # ### @@ -175,16 +207,17 @@ function run_logo ##### # # # # ## ## ### ENDOFTEXT )" - print "${vtcode["clear"]}${render_buffer}" + print -- "${vtcode["clear"]}${render_buffer}" # wait two seconds and swallow any user input for (( i=0 ; i < 20 ; i++ )) ; do - read -t 0.1 -n 1 dummy + read -r -t 0.1 -n 1 dummy done print "\n (The KornShell 93 maze game)" attract_mode + return 0 } function attract_mode @@ -193,11 +226,13 @@ function attract_mode # Now present some info, line-by-line in an endless loop # until the user presses a key (we turn the "magic" return # code for that) - magic_return_code=69 - IFS="|" ; # Make sure we do not swallow whitespaces + integer -r magic_return_code=69 + typeset line + IFS='' ; # Make sure we do not swallow whitespaces + while true ; do ( - exec 5<&0 + redirect 5<&0 (cat <<ENDOFTEXT @@ -270,15 +305,15 @@ function attract_mode ENDOFTEXT # clear screen, line-by-line - for (( i=0 ; i < LINES ; i++ )) ; do print "" ; done - ) | (while read line ; do - read -t 0.3 -n 1 c <&5 - [ "$c" != "" ] && exit ${magic_return_code} - print "${line}" + for (( i=0 ; i < termsize.lines ; i++ )) ; do print "" ; done + ) | (while read -r line ; do + read -r -t 0.3 -n 1 c <&5 + [[ "$c" != "" ]] && exit ${magic_return_code} + print -- "${line}" done) - [ $? -eq ${magic_return_code} ] && exit ${magic_return_code} + (( $? == magic_return_code )) && exit ${magic_return_code} ) - [ $? -eq ${magic_return_code} ] && return 0 + (( $? == magic_return_code )) && return 0 sleep 2 done @@ -289,22 +324,23 @@ function run_menu { integer numlevels=0 integer selected_level=0 + typeset l # built list of available levels based on the "function levelmap_.*" # built into this script typeset -f | egrep "^function.*levelmap_.*" | sed 's/^function //' | - while read l ; do + while read -r l ; do levellist[numlevels]="$l" numlevels+=1 done # swallow any queued user input (e.g. drain stdin) - read -t 0.1 -n 100 dummy + read -r -t 0.1 -n 100 dummy while true ; do # menu loop with timeout (which switches to "attract mode") while true ; do - print -n "${vtcode["clear"]}" + print -n -- "${vtcode["clear"]}" cat <<ENDOFTEXT >======================================\ @@ -321,50 +357,51 @@ ENDOFTEXT print "\t - [L]evels:" for (( i=0 ; i < numlevels ; i++ )) ; do - printf "\t %s %s \n" "$([ $i -eq $selected_level ] && print -n "*" || print -n " ")" "${levellist[i]##levelmap_}" + printf "\t %s %s \n" "$( (( i == selected_level )) && print -n "*" || print -n " ")" "${levellist[i]##levelmap_}" done print "\t - Rendering options:" - printf "\t [%s] Use [U]nicode\n" "$([ $game_use_unicode -eq 1 ] && print -n "x" || print -n "_")" - printf "\t [%s] Use [C]olors\n" "$([ $game_use_colors -eq 1 ] && print -n "x" || print -n "_")" + printf "\t [%s] Use [U]nicode\n" "$( (( game_use_unicode == 1 )) && print -n "x" || print -n "_" )" + printf "\t [%s] Use [C]olors\n" "$( (( game_use_colors == 1 )) && print -n "x" || print -n "_" )" print "\t - [S]tart - [Q]uit" # wait 30 secs (before we switch to "attract mode") - c="" ; read -t 30 -n 1 c + c="" ; read -r -t 30 -n 1 c case "$c" in - 'l') selected_level=$(((selected_level+numlevels+1) % numlevels)) ;; - 'L') selected_level=$(((selected_level+numlevels-1) % numlevels)) ;; - ~(Ei)s) - [ ${game_use_colors} -eq 1 ] && print "${vtcode["bg_black"]}" + 'l') (( selected_level=(selected_level+numlevels+1) % numlevels )) ;; + 'L') (( selected_level=(selected_level+numlevels-1) % numlevels )) ;; + ~(Fi)s) + (( game_use_colors == 1 )) && print -- "${vtcode["bg_black"]}" case "${game_use_colors}${game_use_unicode}" in "00") main_loop "${levellist[selected_level]}" ;; "01") main_loop "${levellist[selected_level]}" | map_filter 0 1 ;; "10") main_loop "${levellist[selected_level]}" | map_filter 1 0 ;; "11") main_loop "${levellist[selected_level]}" | map_filter 1 1 ;; esac - print "${vtcode["vtreset"]}" + print -- "${vtcode["vtreset"]}" ;; - ~(Ei)q|$'\E') + ~(Fi)q | $'\E') # make sure we do not exit on a cursor key (e.g. <esc>[A,B,C,D) - read -t 0.01 -n 1 c - if [ "$c" = "[" ] ; then + read -r -t 0.01 -n 1 c + if [[ "$c" == "[" ]] ; then # this was a cursor key sequence, just eat the 3rd charcater - read -t 0.01 -n 1 c + read -r -t 0.01 -n 1 c else exit 0 fi ;; - ~(Ei)u) game_use_unicode=$(((game_use_unicode+2+1) % 2)) ;; - ~(Ei)c) game_use_colors=$(((game_use_colors+2+1) % 2)) ;; + ~(Fi)u) (( game_use_unicode=(game_use_unicode+2+1) % 2)) ;; + ~(Fi)c) (( game_use_colors=(game_use_colors+2+1) % 2)) ;; "") break ;; # timeout, switch to attract mode *) beep ;; esac done - print -n "${vtcode["clear"]}" + print -n -- "${vtcode["clear"]}" attract_mode done + return 0 } function levelmap_stripes @@ -388,6 +425,7 @@ cat <<ENDOFLEVEL # # ################################### ENDOFLEVEL + return 0 } function levelmap_livad @@ -417,6 +455,7 @@ cat <<ENDOFLEVEL # # ##################################################### ENDOFLEVEL + return 0 } function levelmap_classic1 @@ -454,6 +493,7 @@ cat <<ENDOFLEVEL #.......................# ######################### ENDOFLEVEL + return 0 } function levelmap_classic2 @@ -481,6 +521,7 @@ cat <<ENDOFLEVEL #.....................# ####################### ENDOFLEVEL + return 0 } function levelmap_easy @@ -499,6 +540,7 @@ cat <<ENDOFLEVEL # .......... P # ################## ENDOFLEVEL + return 0 } function levelmap_sunsolaristext @@ -520,31 +562,34 @@ cat <<ENDOFLEVEL # #### #### ###### . # ....# # ####. # ################################################ ENDOFLEVEL + return 0 } function read_levelmap { - map="$( $1 )" + typeset map="$( $1 )" integer y=0 integer x=0 integer maxx=0 integer numdots=0 + typeset line + typeset c - print "$map" | - while read line ; do - x=0 - while (( x < ${#line} )) ; do + while read -r line ; do + for (( x=0 ; x < ${#line} ; x++ )) ; do c="${line:x:1}" case $c in ".") numdots+=1 ;; "M") + # log start position of monsters levelmap["monsterstartpos_x"]="$x" levelmap["monsterstartpos_y"]="$y" c=" " ;; "P") + # log start position of player levelmap["playerstartpos_x"]="$x" levelmap["playerstartpos_y"]="$y" c=" " @@ -552,20 +597,19 @@ function read_levelmap esac levelmap["${x}_${y}"]="$c" - let x++ done - maxx=$x - let y++ - done + (( maxx=x , y++ )) + done <<<"${map}" levelmap["max_x"]=${maxx} levelmap["max_y"]=${y} levelmap["numdots"]=${numdots} - if [ "${levelmap["monsterstartpos_x"]}" = "" ] ; then + # consistency checks + if [[ "${levelmap["monsterstartpos_x"]}" == "" ]] ; then fatal_error "read_levelmap: monsterstartpos_x is empty." fi - if [ "${levelmap["playerstartpos_x"]}" = "" ] ; then + if [[ "${levelmap["playerstartpos_x"]}" == "" ]] ; then fatal_error "read_levelmap: playerstartpos_x is empty." fi @@ -576,26 +620,27 @@ function player.set { case "${.sh.subscript}" in pos_y) - if [ "${levelmap["${player["pos_x"]}_${.sh.value}"]}" = "#" ] ; then + if [[ "${levelmap["${player["pos_x"]}_${.sh.value}"]}" == "#" ]] ; then .sh.value=${player["pos_y"]} beep fi ;; pos_x) - if [ "${levelmap["${.sh.value}_${player["pos_y"]}"]}" = "#" ] ; then + if [[ "${levelmap["${.sh.value}_${player["pos_y"]}"]}" == "#" ]] ; then .sh.value=${player["pos_x"]} beep fi ;; esac + return 0 } function monster.set { case "${.sh.subscript}" in *_pos_y) - if [ "${levelmap["${monster[${currmonster}_"pos_x"]}_${.sh.value}"]}" = "#" ] ; then + if [[ "${levelmap["${monster[${currmonster}_"pos_x"]}_${.sh.value}"]}" == "#" ]] ; then .sh.value=${monster[${currmonster}_"pos_y"]} # turn homing off when the monster hit a wall monster[${currmonster}_"homing"]=0 @@ -603,13 +648,14 @@ function monster.set ;; *_pos_x) - if [ "${levelmap["${.sh.value}_${monster[${currmonster}_"pos_y"]}"]}" = "#" ] ; then + if [[ "${levelmap["${.sh.value}_${monster[${currmonster}_"pos_y"]}"]}" == "#" ]] ; then .sh.value=${monster[${currmonster}_"pos_x"]} # turn homing off when the monster hit a wall monster[${currmonster}_"homing"]=0 fi ;; esac + return 0 } function render_game @@ -617,17 +663,17 @@ function render_game # render_buffer is some kind of "background buffer" to "double buffer" # all output and combine it in one write to reduce flickering in the # terminal - render_buffer="$( - screen_y_offset=1 - start_y_pos=0 - render_num_lines=${levelmap["max_y"]} - - if (( (LINES-3) < levelmap["max_y"] )) ; then - start_y_pos=$((player["pos_y"] / 2)) - render_num_lines=$((LINES-5)) + typeset render_buffer="$( + integer screen_y_offset=1 + integer start_y_pos=0 + integer render_num_lines=${levelmap["max_y"]} + + if (( (termsize.lines-3) < levelmap["max_y"] )) ; then + (( start_y_pos=player["pos_y"] / 2)) + (( render_num_lines=termsize.lines-5)) fi - #print -n "${vtcode["clear"]}" + #print -n -- "${vtcode["clear"]}" print_setcursorpos 0 0 # print score (note the " " around "%d" are neccesary to clean up cruft @@ -641,8 +687,8 @@ function render_game # render monsters for currmonster in ${monsterlist} ; do - let m_pos_x=monster[${currmonster}_"pos_x"] - let m_pos_y=monster[${currmonster}_"pos_y"]+screen_y_offset-start_y_pos + (( m_pos_x=monster[${currmonster}_"pos_x"] )) + (( m_pos_y=monster[${currmonster}_"pos_y"]+screen_y_offset-start_y_pos )) if (( m_pos_y >= screen_y_offset && m_pos_y < render_num_lines )) ; then print_setcursorpos ${m_pos_x} ${m_pos_y} @@ -655,8 +701,9 @@ function render_game emptyline=" " print -n " >> ${player["message"]} <<${emptyline:0:${#emptyline}-${#player["message"]}}" )" - print "${render_buffer}" -# print "renderbuffersize=$(print "${render_buffer}" | wc -c) " + print -r -- "${render_buffer}${end_of_frame}" +# print "renderbuffersize=$(print "${render_buffer}" | wc -c) ${end_of_frame}" + return 0 } function main_loop @@ -666,7 +713,7 @@ function main_loop integer num_cycles=0 float rs - print -n "${vtcode["clear"]}" + print -n -- "${vtcode["clear"]}" read_levelmap "$1" @@ -692,22 +739,22 @@ function main_loop while true ; do num_cycles+=1 seconds_before_read=${SECONDS} - c="" ; read -t ${sleep_per_cycle} -n 1 c + c="" ; read -r -t ${sleep_per_cycle} -n 1 c - if [ "$c" != "" ] ; then + if [[ "$c" != "" ]] ; then # special case handling for cursor keys which are usually composed # of three characters (e.g. "<ESC>[D"). If only <ESC> is hit we # quicky exit - if [ "$c" = $'\E' ] ; then - read -t 0.1 -n 1 c - if [ "$c" != "[" ] ; then + if [[ "$c" == $'\E' ]] ; then + read -r -t 0.1 -n 1 c + if [[ "$c" != "[" ]] ; then return 0 fi # we assume the user is using the cursor keys, this |read| # should fetch the 3rd byte of the three-character sequence # for the cursor keys - read -t 0.1 -n 1 c + read -r -t 0.1 -n 1 c fi # if the user hit a key the "read" above was interrupted @@ -715,28 +762,28 @@ function main_loop # We wait here some moments (|rs|="remaining seconds") to # avoid that the game gets "faster" when more user input # is given. - rs=$((sleep_per_cycle-(SECONDS-seconds_before_read))) + (( rs=sleep_per_cycle-(SECONDS-seconds_before_read) )) (( rs > 0.001 )) && sleep ${rs} player["message"]="" case "$c" in - j|D|4) let player["pos_x"]-=1 ;; - k|C|6) let player["pos_x"]+=1 ;; - i|A|8) let player["pos_y"]-=1 ;; - m|B|2) let player["pos_y"]+=1 ;; + j|D|4) (( player["pos_x"]-=1 )) ;; + k|C|6) (( player["pos_x"]+=1 )) ;; + i|A|8) (( player["pos_y"]-=1 )) ;; + m|B|2) (( player["pos_y"]+=1 )) ;; q) return 0 ;; esac - if [ "${levelmap["${player["pos_x"]}_${player["pos_y"]}"]}" = "." ] ; then + if [[ "${levelmap["${player["pos_x"]}_${player["pos_y"]}"]}" == "." ]] ; then levelmap["${player["pos_x"]}_${player["pos_y"]}"]=" " - let levelmap["numdots"]-=1 + (( levelmap["numdots"]-=1 )) - let player["score"]+=10 + (( player["score"]+=10 )) player["message"]='GNAW!!' - if [ ${levelmap["numdots"]} -le 0 ] ; then + if (( levelmap["numdots"] <= 0 )) ; then level_completed return 0 fi @@ -744,10 +791,10 @@ function main_loop fi # generic player status change - if [ ${player["invulnerable"]} -gt 0 ] ; then - let player["invulnerable"]-=1 + if (( player["invulnerable"] > 0 )) ; then + (( player["invulnerable"]-=1 )) fi - if [ ${player["lives"]} -le 0 ] ; then + if (( player["lives"] <= 0 )) ; then game_over return 0 fi @@ -755,98 +802,71 @@ function main_loop # move monsters for currmonster in ${monsterlist} ; do # make monster as half as slow then the others when it is following the user - if [ ${monster[${currmonster}_"homing"]} -gt 0 ] ; then - [ $((num_cycles % 2)) -gt 0 ] && continue + if (( monster[${currmonster}_"homing"] > 0 )) ; then + (( (num_cycles%2) > 0 )) && continue fi - if [ ${monster[${currmonster}_"pos_x"]} = ${player["pos_x"]} ] ; then - if [ $((monster[${currmonster}_"pos_y"]-player["pos_y"])) -gt 0 ] ; then - let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=-1 + if [[ ${monster[${currmonster}_"pos_x"]} == ${player["pos_x"]} ]] ; then + if (( (monster[${currmonster}_"pos_y"]-player["pos_y"]) > 0 )) ; then + (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=-1 )) else - let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=+1 + (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=+1 )) fi monster[${currmonster}_"homing"]=1 - if [ ${player["invulnerable"]} -le 0 ] ; then + if (( player["invulnerable"] <= 0 )) ; then player["message"]="Attention: ${currmonster} is chasing you" fi - elif [ ${monster[${currmonster}_"pos_y"]} = ${player["pos_y"]} ] ; then - if [ $((monster[${currmonster}_"pos_x"]-player["pos_x"])) -gt 0 ] ; then - let monster[${currmonster}_"xstep"]=-1 monster[${currmonster}_"ystep"]=-0 + elif (( monster[${currmonster}_"pos_y"] == player["pos_y"] )) ; then + if (( (monster[${currmonster}_"pos_x"]-player["pos_x"]) > 0 )) ; then + (( monster[${currmonster}_"xstep"]=-1 , monster[${currmonster}_"ystep"]=-0 )) else - let monster[${currmonster}_"xstep"]=+1 monster[${currmonster}_"ystep"]=+0 + (( monster[${currmonster}_"xstep"]=+1 , monster[${currmonster}_"ystep"]=+0 )) fi monster[${currmonster}_"homing"]=1 - if [ ${player["invulnerable"]} -le 0 ] ; then + if (( player["invulnerable"] <= 0 )) ; then player["message"]="Attention: ${currmonster} is chasing you" fi else - if [ ${monster[${currmonster}_"homing"]} -eq 0 ] ; then + if (( monster[${currmonster}_"homing"] == 0 )) ; then case $((SECONDS % 6 + RANDOM % 4)) in - 0) let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=+0 ;; - 2) let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=+1 ;; - 3) let monster[${currmonster}_"xstep"]=+1 monster[${currmonster}_"ystep"]=+0 ;; - 5) let monster[${currmonster}_"xstep"]=+0 monster[${currmonster}_"ystep"]=-1 ;; - 6) let monster[${currmonster}_"xstep"]=-1 monster[${currmonster}_"ystep"]=+0 ;; + 0) (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=+0 )) ;; + 2) (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=+1 )) ;; + 3) (( monster[${currmonster}_"xstep"]=+1 , monster[${currmonster}_"ystep"]=+0 )) ;; + 5) (( monster[${currmonster}_"xstep"]=+0 , monster[${currmonster}_"ystep"]=-1 )) ;; + 6) (( monster[${currmonster}_"xstep"]=-1 , monster[${currmonster}_"ystep"]=+0 )) ;; esac fi fi - let monster[${currmonster}_"pos_x"]=monster[${currmonster}_"pos_x"]+monster[${currmonster}_"xstep"] - let monster[${currmonster}_"pos_y"]=monster[${currmonster}_"pos_y"]+monster[${currmonster}_"ystep"] + (( monster[${currmonster}_"pos_x"]=monster[${currmonster}_"pos_x"]+monster[${currmonster}_"xstep"] )) + (( monster[${currmonster}_"pos_y"]=monster[${currmonster}_"pos_y"]+monster[${currmonster}_"ystep"] )) # check if a monster hit the player - if [ ${player["invulnerable"]} -le 0 ] ; then - if [ ${monster[${currmonster}_"pos_x"]} -eq ${player["pos_x"]} -a \ - ${monster[${currmonster}_"pos_y"]} -eq ${player["pos_y"]} ] ; then + if (( player["invulnerable"] <= 0 )) ; then + if (( monster[${currmonster}_"pos_x"] == player["pos_x"] && \ + monster[${currmonster}_"pos_y"] == player["pos_y"] )) ; then # if player was hit by a monster take one life and # make him invulnerable for 10 cycles to avoid that # the next cycle steals more lives player["message"]="Ouuuchhhh" player["invulnerable"]=10 - let player["lives"]-=1 + (( player["lives"]-=1 )) - beep ; beep ; sleep 0.3 ; beep ; beep + beep ; beep ; sleep 0.2 ; beep ; beep fi fi done render_game done + return 0 } -# program start function map_filter { -# Choose between the old "sed"-based codepath and the new ksh93-native one -# The old codepath no longer used except for the unicode mode because -# we do not have control over the point where "sed" flushes it's buffer -# which completely defeats the doube-buffering code. Unfortunately the new -# codepath has problems in UTF-8 mode (bug in ksh93 ?) which forces us to -# use the old codepath in this case. -if [ $2 -eq 1 ] ; then -( - filter1="" - filter2="" - - # should we add the color map ? - if [ $1 -eq 1 ] ; then - filter1="s/#/${vtcode["fg_blue"]}#/g;\ - s/x/${vtcode["fg_red"]}x/g;\ - s/@/${vtcode["fg_yellow"]}@/g;\ - s/ /${vtcode["fg_grey"]} /g;\ - s/\./${vtcode["fg_lightred"]}./g;" - fi + typeset ch_player ch_monster ch_wall var - # should we add the unicode map ? - if [ $2 -eq 1 ] ; then - filter2="s/@/$(printf '\u[24d2]')/g;s/x/$(printf '\u[2605]')/g;s/#/$(printf '\u[25a6]')/g" - fi - - sed -e "${filter1}" -e "${filter2}" -) -else -( - if [ $1 -eq 1 ] ; then + if (( $1 == 1 )) ; then ch_player="${vtcode["fg_yellow"]}" ch_monster="${vtcode["fg_red"]}" ch_wall="${vtcode["fg_blue"]}" @@ -856,7 +876,7 @@ else ch_wall="" fi - if [ $2 -eq 1 ] ; then + if (( $2 == 1 )) ; then # unicode map ch_player+="$(printf '\u[24d2]')" ch_monster+="$(printf '\u[2605]')" @@ -868,46 +888,52 @@ else ch_wall+="#" fi - IFS="|" # make sure we don't swallow spaces/tabs - while read var ; do + # note that this filter currently defeats the "double-buffering" + while IFS='' read -r -d "${end_of_frame}" var ; do var="${var// /${vtcode["fg_grey"]} }" var="${var//\./${vtcode["fg_lightred"]}.}" var="${var//@/${ch_player}}" var="${var//x/${ch_monster}}" var="${var//#/${ch_wall}}" - print "${var}" + print -r -- "${var}" done -) -fi + return 0 } function exit_trap { # restore stty settings - stty ${SAVED_STTY} + stty ${saved_stty} print "bye." + return 0 } function usage { OPTIND=0 - getopts -a "${progname}" "${USAGE}" OPT '-?' + getopts -a "${progname}" "${gnaw_usage}" OPT '-?' exit 2 } # program start -progname="${0}" -quiet=false - # make sure we use the ksh93 "cat" builtin which supports the "-u" option +builtin basename builtin cat builtin wc -builtin printf # we need this for positional parameters ('printf "%2\$s %1\$s" hello world' = "world hello") -builtin sleep + +typeset progname="${ basename "${0}" ; }" + +# terminal size rect +typeset -C termsize=( + integer columns=-1 + integer lines=-1 +) # global variables +typeset quiet=false + typeset -A levelmap typeset -A player typeset -A monster @@ -915,33 +941,33 @@ typeset -A monster integer game_use_colors=0 integer game_use_unicode=0 -USAGE=$' -[-? -@(#)\$Id: gnaw (Roland Mainz) 2007-06-05 \$ -] +typeset -r gnaw_usage=$'+ +[-?\n@(#)\$Id: gnaw (Roland Mainz) 2008-11-04 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] [+NAME?gnaw - maze game written in ksh93] [+DESCRIPTION?\bgnaw\b is a maze game. - The player maneuvers a yellow '@' sign to navigate a maze while eating + The player maneuvers a yellow "@" sign to navigate a maze while eating small dots. A level is finished when all the dots are eaten. Five monsters (maw, claw, jitterbug, tentacle and grendel) also wander the maze in an attempt - to catch the '@'. Each level begins with all ghosts in their home, and '@' near + to catch the "@". Each level begins with all ghosts in their home, and "@" near the bottom of the maze. The monsters are released from the home one by one at the start of each level and start their rentless hunt after the player.] [q:quiet?Disable use of terminal bell.] [+SEE ALSO?\bksh93\b(1)] ' -while getopts -a "${progname}" "${USAGE}" OPT ; do +while getopts -a "${progname}" "${gnaw_usage}" OPT ; do # printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" case ${OPT} in - q) quiet=true ;; + q) quiet=true ;; + +q) quiet=false ;; *) usage ;; esac done -shift ${OPTIND}-1 +shift $((OPTIND-1)) # save stty values and register the exit trap which restores these values on exit -SAVED_STTY="$(stty -g)" +saved_stty="$(stty -g)" trap exit_trap EXIT print "Loading..." @@ -950,25 +976,10 @@ print "Loading..." # "-echo" turns the terminal echo off stty -icanon min 1 time 0 -inpck -echo -# "resize" cannot fetch the terminal width/height for some terminals -case ${TERM} in - sun | sun-color) - export COLUMNS=80 LINES=25 - ;; - vt52) - export COLUMNS=80 LINES=24 - ;; - *) - # get width/height from current terminal - [ -x "/usr/X11/bin/resize" ] && eval "$(/usr/X11/bin/resize -u)" || - [ -x "/usr/X11R6/bin/resize" ] && eval "$(/usr/X11R6/bin/resize -u)" || - [ -x "/usr/openwin/bin/resize" ] && eval "$(/usr/openwin/bin/resize -u)" || - fatal_error "resize not found." - ;; -esac +get_term_size termsize || fatal_error "Could not get terminal size." # prechecks -(( COLUMNS < 60 )) && fatal_error "Terminal width must be larger than 60 columns (currently ${COLUMNS})." +(( termsize.columns < 60 )) && fatal_error "Terminal width must be larger than 60 columns (currently ${termsize.columns})." typeset -A vtcode # color values taken from http://frexx.de/xterm-256-notes/, other @@ -991,9 +1002,14 @@ vtcode=( ["vtreset"]="$(tput reset)" ["clear"]="$(tput clear)" ["bel"]="$(tput bel)" - ["spaceline"]="$(for (( i=0 ; i < COLUMNS ; i++ )) ; do print -n " " ; done)" + ["spaceline"]="$(for (( i=0 ; i < termsize.columns ; i++ )) ; do print -n " " ; done)" ) +# character used to as marker that a single frame ends at this point - this +# is used by the "double buffering" code to make sure the "read" builtin +# can read a whole "frame" instead of reading stuff line-by-line +typeset -r end_of_frame=$'\t' + # get terminal sequence to move cursor to position x,y # (see http://vt100.net/docs/vt100-ug/chapter3.html#CPR) case ${TERM} in @@ -1004,26 +1020,26 @@ case ${TERM} in -e 's/%[%id]*p1[%id]*/%2\\\$d/g' \ -e 's/%[%id]*p2[%id]*/%1\\\$d/g' \ -e 's/,$//')" - for (( x=0 ; x < COLUMNS ; x++ )) ; do - for (( y=0 ; y < LINES ; y++ )) ; do + for (( x=0 ; x < termsize.columns ; x++ )) ; do + for (( y=0 ; y < termsize.lines ; y++ )) ; do vtcode[cup_${x}_${y}]="$(printf "${cup}" $((x + 1)) $((y + 1)) )" done done ;; *) - printf "# Unrecognised terminal type '%s', fetching %dx%d items from terminfo database, please wait...\n" "${TERM}" "${COLUMNS}" "${LINES}" - for (( x=0 ; x < COLUMNS ; x++ )) ; do - for (( y=0 ; y < LINES ; y++ )) ; do + printf "# Unrecognised terminal type '%s', fetching %dx%d items from terminfo database, please wait...\n" "${TERM}" "${termsize.columns}" "${termsize.lines}" + for (( x=0 ; x < termsize.columns ; x++ )) ; do + for (( y=0 ; y < termsize.lines ; y++ )) ; do vtcode[cup_${x}_${y}]="$(tput cup ${y} ${x})" done done ;; esac -print "${vtcode["vtreset"]}" +print -- "${vtcode["vtreset"]}" run_logo run_menu +exit 0 # EOF. - diff --git a/usr/src/lib/libshell/common/scripts/mandelbrotset1.sh b/usr/src/lib/libshell/common/scripts/mandelbrotset1.sh new file mode 100644 index 0000000000..2b01d33f4e --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/mandelbrotset1.sh @@ -0,0 +1,281 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# mandelbrotset1 - a simple mandelbrot set generation and +# parallel execution demo +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function printmsg +{ + print -u2 "$*" +} + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +# Get terminal size and put values into a compound variable with the integer +# members "columns" and "lines" +function get_term_size +{ + nameref rect=$1 + + rect.columns=${ tput cols ; } || return 1 + rect.lines=${ tput lines ; } || return 1 + + return 0 +} + +function print_color +{ + print -r -n -- "${symbollist:${1}:1}" + return 0 +} + +function mandelbrot +{ + nameref result=$1 + float x=$2 + float y=$3 + float xx + float yy + float x1=$4 + float y1=$5 + integer iteration=$6 + integer max_iteration=$7 + float mag + + for (( mag=0 ; mag < max_mag && iteration < max_iteration ; iteration++ )) ; do + (( + xx=x*x , + yy=y*y , + mag=xx+yy , + y=x*y*2+y1 , + x=xx-yy+x1 + )) + done + + (( result=iteration )) + + return 0 +} + +# build mandelbrot image serially +function loop_serial +{ + integer value + + for (( y=y_min ; y < y_max ; y+=stepwidth )) ; do + for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do + mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen} + print_color ${value} + done + + print + done + + return 0 +} + +# build mandelbrot image using parallel worker jobs +function loop_parallel +{ + integer numjobs=0 + # the following calculation suffers from rounding errors + integer lines_per_job=$(( ((m_height+(numcpus-1)) / numcpus) )) + + printmsg $"# lines_per_job=${lines_per_job}" + printmsg $"# numcpus=${numcpus}" + + # "renice" worker jobs + set -o bgnice + + if [[ "${TMPDIR}" == "" ]] ; then + TMPDIR="/tmp" + fi + + # try to generate a job identifer prefix which is unique across multiple hosts + jobident="job_host_$(uname -n)pid_$$_ppid${PPID}" + + printmsg $"## prepare..." + for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do + rm -f "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" + + (( numjobs++ )) + done + + printmsg $"## running ${numjobs} children..." + for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do + ( + integer value + + for (( ; y < y_max && lines_per_job-- > 0 ; y+=stepwidth )) ; do + for (( x=x_min ; x < x_max ; x+=stepwidth )) ; do + mandelbrot value ${x} ${y} ${x} ${y} 1 ${symbollistlen} + print_color ${value} + done + + print + done >"${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" + ) & + done + + printmsg $"## waiting for ${numjobs} children..." + wait + + printmsg $"## output:" + for (( y=y_min ; y < y_max ; y+=(stepwidth*lines_per_job) )) ; do + print -- "$( < "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput")" + rm "${TMPDIR}/mandelbrot_${jobident}_child_$y.joboutput" + done + + return 0 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${mandelbrotset1_usage}" OPT '-?' + exit 2 +} + +# main +builtin basename +builtin cat +builtin rm +builtin uname # loop_parallel needs the ksh93 builtin version to generate unique job file names + +typeset progname="${ basename "${0}" ; }" + +float x_max +float x_min +float y_max +float y_min +float m_width +float m_height +float max_mag +float stepwidth +integer numcpus + +# terminal size rect +typeset -C termsize=( + integer columns=-1 + integer lines=-1 +) + +get_term_size termsize || fatal_error $"Could not get terminal size." + +typeset symbollist=' .:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%#' +typeset symbollistlen=$(( ${#symbollist} - 1)) +typeset mode="parallel" + +max_mag=400 +stepwidth=0.1 +numcpus=16 + +(( m_width=termsize.columns-1 , m_height=termsize.lines-2 )) + +typeset -r mandelbrotset1_usage=$'+ +[-?\n@(#)\$Id: mandelbrotset1 (Roland Mainz) 2008-11-04 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?mandelbrotset1 - generate mandelbrot set fractals with ksh93] +[+DESCRIPTION?\bmandelbrotset1\b mandelbrot set fractal generator + which runs either in serial or parallel mode (using multiple worker jobs).] +[w:width?Width of fractal.]:[width] +[h:height?Height of fractal.]:[height] +[s:symbols?Symbols to build the fractal from.]:[symbolstring] +[m:mag?Magnification level.]:[magnificationlevel] +[p:stepwidth?Width per step.]:[widthperstep] +[S:serial?Run in serial mode.] +[P:parallel?Run in parallel mode.] +[M:mode?Execution mode.]:[mode] +[C:numcpus?Number of processors used for parallel execution.]:[numcpus] +[+SEE ALSO?\bjuliaset1\b(1), \bksh93\b(1)] +' + +while getopts -a "${progname}" "${mandelbrotset1_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + w) m_width="${OPTARG}" ;; + h) m_height="${OPTARG}" ;; + s) symbollist="${OPTARG}" ;; + m) max_mag="${OPTARG}" ;; + p) stepwidth="${OPTARG}" ;; + S) mode="serial" ;; + P) mode="parallel" ;; + M) mode="${OPTARG}" ;; + C) numcpus="${OPTARG}" ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +printmsg "# width=${m_width}" +printmsg "# height=${m_height}" +printmsg "# max_mag=${max_mag}" +printmsg "# stepwidth=${stepwidth}" +printmsg "# symbollist='${symbollist}'" +printmsg "# mode=${mode}" + +(( symbollistlen=${#symbollist}-1 )) + +(( + x_max=m_width*stepwidth/2. , + x_min=-x_max , + y_max=m_height*stepwidth/2. , + y_min=-y_max +)) + +case "${mode}" in + parallel) loop_parallel ; exit $? ;; + serial) loop_serial ; exit $? ;; + *) fatal_error $"Unknown mode \"${mode}\"." ;; +esac + +fatal_error "not reached." +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/multifollow.sh b/usr/src/lib/libshell/common/scripts/multifollow.sh new file mode 100644 index 0000000000..d0323ef915 --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/multifollow.sh @@ -0,0 +1,148 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u 2 "${progname}: $@" + exit 1 +} + + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${multifollow_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename +builtin cat + +typeset progname="$(basename "${0}")" + +typeset -r multifollow_usage=$'+ +[-?\n@(#)\$Id: multifollow (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?multifollow - use tail -f on multiple files] +[+DESCRIPTION?\bmultifollow\b is a small utilty which can "follow" multiple + files similar to tail -f.] + +[ file ... ] + +[+SEE ALSO?\bksh93\b(1), \btail\b(1)] +' + +while getopts -a "${progname}" "${multifollow_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# expecting at least one more arguments +(($# >= 1)) || usage + +builtin -f libshell.so.1 poll || fatal_error "poll builtin not found." + +typeset -a files +integer numfiles=0 +integer i + +# register trap to cleanup child processes +trap 'for ((i=0 ; i < numfiles ; i++ )) ; do kill -TERM ${files[i].childpid} ; done' EXIT + +# setup "tail -f" childs, FIFOs and information for the "poll" builtin +for (( ; $# > 0 ; numfiles++ )) ; do + typeset files[${numfiles}]=( + typeset name="$1" + typeset pipename="/tmp/multifollow_pipe_${PPID}_$$_${numfiles}" + integer childpid=-1 + + # poll(1) information + integer fd="-1" + typeset events="POLLIN" + typeset revents="" + ) + + mkfifo "${files[${numfiles}].pipename}" + redirect {files[numfiles].fd}<>"${files[numfiles].pipename}" + + tail -f "${files[${numfiles}].name}" >"${files[${numfiles}].pipename}" & + files[${numfiles}].childpid=$! + + rm "${files[${numfiles}].pipename}" + + shift +done + +typeset do_poll=true + +# event loop +while true ; do + if ${do_poll} ; then + for ((i=0 ; i < numfiles ; i++ )) ; do + files[i].revents="" + done + poll files + fi + do_poll=true + + for ((i=0 ; i < numfiles ; i++ )) ; do + if [[ "${files[i].revents}" != "" ]] ; then + # todo: investigate why we have to use "do_poll" at all - AFAIK it + # should be sufficient to call "poll" and get "revents" set if there + # are any remaining data... + if read -t0 -u${files[i].fd} line ; then + print -- "#${i}: ${line}" + do_poll=false + fi + fi + done +done + +fatal_error "not reached." +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/primenumbers1.sh b/usr/src/lib/libshell/common/scripts/primenumbers1.sh new file mode 100644 index 0000000000..aba6f5aeac --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/primenumbers1.sh @@ -0,0 +1,115 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# primenumbers1 - a simple prime number generator +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + + +# check whether arg1 is a prime number via comparing it against the "pn" array +function is_prime +{ + integer i + integer num=$1 + float max_pn + + (( max_pn=sqrt(num)+1. )) + + for (( i=0 ; i < num_pn && pn[i] < max_pn ; i++)) ; do + (( num % pn[i] == 0 )) && return 1; + done + return 0 +} + +# main +set -o errexit + +# get arguments +integer max_prime=$1 # maximum prime number +typeset outputformat=$2 + +# variables +integer -a pn # integer array for the prime numbers +integer num_pn=1 # number of prime numbers +integer n # current number which should be tested +pn[0]=2 # start value + +# prechecks +(( max_prime > 1 )) || { print -u2 -f "%s: requires a positive integer as first input.\n" "$0" ; exit 1 ; } + +# calculate prime numbers +printf $"# %s: Calculating prime numbes from 1 to %i\n" "${ date '+%T' ; }" max_prime 1>&2 + +for (( n=3 ; n < max_prime ; n+=2 )) ; do + if is_prime $n ; then + (( pn[num_pn++]=n )) + fi +done + +# print results +printf $"# %s: Calculation done, printing results:\n" "${ date '+%T' ; }" 1>&2 + +for (( n=0 ; n < num_pn ; n++ )) ; do + # print prime number + case ${outputformat} in + block) + printf $"%i$( (( n % 8 == 0 )) && print -r '\n' || print -r ',\t')" pn[n] + ;; + line) + printf $"%i\n" pn[n] + ;; + *) + printf $"prime %i:\t%i\n" n pn[n] + ;; + esac +done + +if [[ ${outputformat} == "block" ]] && (( n % 8 != 1 )); then + print +fi + +printf $"# %s: Done.\n" "${ date '+%T' ; }" 1>&2 + +#EOF. diff --git a/usr/src/lib/libshell/common/scripts/rssread.sh b/usr/src/lib/libshell/common/scripts/rssread.sh new file mode 100644 index 0000000000..fea8627178 --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/rssread.sh @@ -0,0 +1,554 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# rssread - a simple RSS2.0 reader with RSS to XHTML to +# plaintext conversion. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +function printmsg +{ + print -u2 "$*" +} + +function debugmsg +{ +# printmsg "$*" +true +} + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +# parse HTTP return code, cookies etc. +function parse_http_response +{ + nameref response="$1" + typeset h statuscode statusmsg i + + # we use '\r' as additional IFS to filter the final '\r' + IFS=$' \t\r' read -r h statuscode statusmsg # read HTTP/1.[01] <code> + [[ "$h" != ~(Eil)HTTP/.* ]] && { print -u2 -f $"%s: HTTP/ header missing\n" "$0" ; return 1 ; } + [[ "$statuscode" != ~(Elr)[0-9]* ]] && { print -u2 -f $"%s: invalid status code\n" "$0" ; return 1 ; } + response.statuscode="$statuscode" + response.statusmsg="$statusmsg" + + # skip remaining headers + while IFS='' read -r i ; do + [[ "$i" == $'\r' ]] && break + + # strip '\r' at the end + i="${i/~(Er)$'\r'/}" + + case "$i" in + ~(Eli)Content-Type:.*) + response.content_type="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Content-Length:[[:blank:]]*[0-9]*) + integer response.content_length="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Transfer-Encoding:.*) + response.transfer_encoding="${i/~(El).*:[[:blank:]]*/}" + ;; + esac + done + + return 0 +} + +function cat_http_body +{ + typeset emode="$1" + typeset hexchunksize="0" + integer chunksize=0 + + if [[ "${emode}" == "chunked" ]] ; then + while IFS=$'\r' read hexchunksize && + [[ "${hexchunksize}" == ~(Elri)[0-9abcdef]* ]] && + (( chunksize=16#${hexchunksize} )) && (( chunksize > 0 )) ; do + dd bs=1 count="${chunksize}" 2>/dev/null + done + else + cat + fi + + return 0 +} + +function cat_http +{ + typeset protocol="${1%://*}" + typeset path1="${1#*://}" # "http://foo.bat.net/x/y.html" ----> "foo.bat.net/x/y.html" + + typeset host="${path1%%/*}" + typeset path="${path1#*/}" + typeset port="${host##*:}" + + integer netfd + typeset -C httpresponse # http response + + # If URL did not contain a port number in the host part then look at the + # protocol to get the port number + if [[ "${port}" == "${host}" ]] ; then + case "${protocol}" in + "http") port=80 ;; + *) port="$(getent services "${protocol}" | sed 's/[^0-9]*//;s/\/.*//')" ;; + esac + else + host="${host%:*}" + fi + + printmsg "protocol=${protocol} port=${port} host=${host} path=${path}" + + # prechecks + [[ "${protocol}" == "" ]] && { print -u2 -f "%s: protocol not set.\n" "$0" ; return 1 ; } + [[ "${port}" == "" ]] && { print -u2 -f "%s: port not set.\n" "$0" ; return 1 ; } + [[ "${host}" == "" ]] && { print -u2 -f "%s: host not set.\n" "$0" ; return 1 ; } + [[ "${path}" == "" ]] && { print -u2 -f "%s: path not set.\n" "$0" ; return 1 ; } + + # open TCP channel + redirect {netfd}<>"/dev/tcp/${host}/${port}" + (( $? != 0 )) && { print -u2 -f "%s: Couldn't open %s\n" "$0" "${1}" ; return 1 ; } + + # send HTTP request + request="GET /${path} HTTP/1.1\r\n" + request+="Host: ${host}\r\n" + request+="User-Agent: rssread/ksh93 (2008-10-14; $(uname -s -r -p))\r\n" + request+="Connection: close\r\n" + print -n -- "${request}\r\n" >&${netfd} + + # collect response and send it to stdout + parse_http_response httpresponse <&${netfd} + cat_http_body "${httpresponse.transfer_encoding}" <&${netfd} + + # close connection + redirect {netfd}<&- + + return 0 +} + +function html_entity_to_ascii +{ + typeset buf + typeset entity + typeset c + typeset value + + # Todo: Add more HTML/MathML entities here + # Note we use a static variable (typeset -S) here to make sure we + # don't loose the cache data between calls + typeset -S -A entity_cache=( + # entity to ascii (fixme: add UTF-8 transliterations) + ["nbsp"]=' ' + ["lt"]='<' + ["le"]='<=' + ["gt"]='>' + ["ge"]='>=' + ["amp"]='&' + ["quot"]='"' + ["apos"]="'" + ) + + buf="" + while IFS='' read -r -N 1 c ; do + if [[ "$c" != "&" ]] ; then + print -n -r -- "${c}" + continue + fi + + entity="" + while IFS='' read -r -N 1 c ; do + case "$c" in + ";") + break + ;; + ~(Eilr)[a-z0-9#]) + entity+="$c" + continue + ;; + *) +# debugmsg "error &${entity}${c}#" + + print -n -r -- "${entity}${c}" + entity="" + continue 2 + ;; + esac + done + + value="" + if [[ "${entity_cache["${entity}"]}" != "" ]] ; then +# debugmsg "match #${entity}# = #${entity_cache["${entity}"]}#" + value="${entity_cache["${entity}"]}" + else + if [[ "${entity:0:1}" == "#" ]] ; then + # decimal literal + value="${ printf "\u[${ printf "%x" "${entity:1:8}" ; }]" ; }" + elif [[ "${entity:0:7}" == ~(Eilr)[0-9a-f]* ]] ; then + # hexadecimal literal + value="${ printf "\u[${entity:0:7}]" ; }" + else + # unknown literal - pass-through + value="ENT=|${entity}|" + fi + + entity_cache["${entity}"]="${value}" + +# debugmsg "lookup #${entity}# = #${entity_cache["${entity}"]}#" + fi + + printf "%s" "${value}" + done + + return 0 +} + +# dumb xhtml handler - no CSS, tables, images, iframes or nested +# structures are supported (and we assume that the input is correct +# xhtml). The code was written in a trial&&error manner and should be +# rewritten to parse xhtml correctly. +function handle_html +{ + # we can't use global variables here when multiple callbacks use the same + # callback function - but we can use the callback associative array for + # variable storage instead + nameref callbacks=${1} + typeset tag_type="$2" + typeset tag_value="$3" + + case "${tag_type}" in + tag_begin) + case "${tag_value}" in + br) printf "\n" ;; + hr) printf "\n-------------------------------------\n" ;; + pre) callbacks["html_pre"]='true' ;; + p) printf "\n" ;; + esac + ;; + + tag_end) + case "${tag_value}" in + pre) callbacks["html_pre"]='false' ;; + esac + ;; + + tag_text) + if ${callbacks["html_pre"]} ; then + printf "%s" "${tag_value}" + else + # compress spaces/newlines/tabs/etc. + printf "%s" "${tag_value//+([\n\r\t\v[:space:][:blank:]])/ }" + fi + ;; + + document_start) + callbacks["html_pre"]='false' + ;; + document_end) ;; + esac + + return 0 +} + +function handle_rss +{ + # we can't use global variables here when multiple callbacks use the same + # callback function - but we can use the callback associative array for + # variable storage instead + nameref callbacks=${1} + typeset tag_type="$2" + typeset tag_value="$3" + + case "${tag_type}" in + tag_begin) + case "${tag_value}" in + item) + item["title"]="" + item["link"]="" + item["tag"]="" + item["description"]="" + ;; + esac + callbacks["textbuf"]="" + ;; + tag_end) + case "${tag_value}" in + item) + # note that each RSS item needs to be converted seperately from RSS to HTML to plain text + # to make sure that the state of one RSS item doesn't affect others + ( + printf $"<br />#### RSS item: title: %s ####" "${item["title"]}" + printf $"<br />## author: %s" "${item["author"]}" + printf $"<br />## link: %s" "${item["link"]}" + printf $"<br />## date: %s" "${item["pubDate"]}" + printf $"<br />## begin description:" + printf $"<br />%s<br />" "${item["description"]}" + printf $"<br />## end description<br />" + print # extra newline to make sure the sed pipeline gets flushed + ) | + html_entity_to_ascii | # convert XML entities (e.g. decode RSS content to HTML code) + xml_tok "xhtmltok_cb" | # convert HTML to plain text + html_entity_to_ascii # convert HTML entities + ;; + title) item["title"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + link) item["link"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + dc:creator | author) item["author"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + dc:date | pubDate) item["pubDate"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + description) item["description"]="${callbacks["textbuf"]}" ; callbacks["textbuf"]="" ;; + esac + callbacks["textbuf"]="" + ;; + tag_text) + callbacks["textbuf"]+="${tag_value}" + ;; + document_start) ;; + document_end) ;; + esac + return 0 +} + +function xml_tok +{ + typeset buf="" + typeset namebuf="" + typeset attrbuf="" + typeset c="" + typeset isendtag # bool: true/false + typeset issingletag # bool: true/false (used for tags like "<br />") + nameref callbacks=${1} + + [[ ! -z "${callbacks["document_start"]}" ]] && ${callbacks["document_start"]} "${1}" "document_start" + + while IFS='' read -r -N 1 c ; do + isendtag=false + + if [[ "$c" == "<" ]] ; then + # flush any text content + if [[ "$buf" != "" ]] ; then + [[ ! -z "${callbacks["tag_text"]}" ]] && ${callbacks["tag_text"]} "${1}" "tag_text" "$buf" + buf="" + fi + + IFS='' read -r -N 1 c + if [[ "$c" == "/" ]] ; then + isendtag=true + else + buf="$c" + fi + IFS='' read -r -d '>' c + buf+="$c" + + # handle comments + if [[ "$buf" == ~(El)!-- ]] ; then + # did we read the comment completely ? + if [[ "$buf" != ~(Elr)!--.*-- ]] ; then + buf+=">" + while [[ "$buf" != ~(Elr)!--.*-- ]] ; do + IFS='' read -r -N 1 c || break + buf+="$c" + done + fi + + [[ ! -z "${callbacks["tag_comment"]}" ]] && ${callbacks["tag_comment"]} "${1}" "tag_comment" "${buf:3:${#buf}-5}" + buf="" + continue + fi + + # check if the tag starts and ends at the same time (like "<br />") + if [[ "${buf}" == ~(Er).*/ ]] ; then + issingletag=true + buf="${buf%*/}" + else + issingletag=false + fi + + # check if the tag has attributes (e.g. space after name) + if [[ "$buf" == ~(E)[[:space:][:blank:]] ]] ; then + namebuf="${buf%%~(E)[[:space:][:blank:]].*}" + attrbuf="${buf#~(E).*[[:space:][:blank:]]}" + else + namebuf="$buf" + attrbuf="" + fi + + if ${isendtag} ; then + [[ ! -z "${callbacks["tag_end"]}" ]] && ${callbacks["tag_end"]} "${1}" "tag_end" "$namebuf" + else + [[ ! -z "${callbacks["tag_begin"]}" ]] && ${callbacks["tag_begin"]} "${1}" "tag_begin" "$namebuf" "$attrbuf" + + # handle tags like <br/> (which are start- and end-tag in one piece) + if ${issingletag} ; then + [[ ! -z "${callbacks["tag_end"]}" ]] && ${callbacks["tag_end"]} "${1}" "tag_end" "$namebuf" + fi + fi + buf="" + else + buf+="$c" + fi + done + + [[ ! -z "${callbacks["document_end"]}" ]] && ${callbacks["document_end"]} "${1}" "document_end" "exit_success" + + print # final newline to make filters like "sed" happy +} + +# return the value of LC_MESSAGES needed for subprocesses which +# want to run in a different locale/encoding +function get_lc_messages +{ + [[ "${LC_ALL}" != "" ]] && { print "${LC_ALL}" ; return 0 ; } + [[ "${LC_MESSAGES}" != "" ]] && { print "${LC_MESSAGES}" ; return 0 ; } + [[ "${LANG}" != "" ]] && { print "${LANG}" ; return 0 ; } + print "C" ; return 0 +} + +function do_rssread +{ + # set unicode locale since RSS is encoded in UTF-8 + # (and make sure $LC_MESSAGES is set to the parent + # process's locale that all error messages are using + # the callers locale/encoding) + export \ + LC_MESSAGES="${ get_lc_messages ; }" \ + LC_MONETARY="en_US.UTF-8" \ + LC_NUMERIC="en_US.UTF-8" \ + LC_COLLATE="en_US.UTF-8" \ + LC_CTYPE="en_US.UTF-8" \ + LC_TIME="en_US.UTF-8" \ + LANG="en_US.UTF-8" + + # need extra newline after cat_http to terminate line with $'\n' + # to make "xml_tok" happy + { cat_http "$1" ; print ; } | + xml_tok "rsstok_cb" + return 0 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${rssread_usage}" OPT '-?' + exit 2 +} + +# make sure we use the ksh93 builtin versions +builtin basename +builtin cat + +typeset -A rsstok_cb # callbacks for xml_tok +rsstok_cb["tag_begin"]="handle_rss" +rsstok_cb["tag_end"]="handle_rss" +rsstok_cb["tag_text"]="handle_rss" +rsstok_cb["textbuf"]="" + +typeset -A xhtmltok_cb # callbacks for xml_tok +xhtmltok_cb["tag_begin"]="handle_html" +xhtmltok_cb["tag_end"]="handle_html" +xhtmltok_cb["tag_text"]="handle_html" +xhtmltok_cb["textbuf"]="" +xhtmltok_cb["html_pre"]='false' + +typeset -A item + +typeset -A bookmark_urls + +# "ramdom" urls for testing +bookmark_urls=( + ["google_blogs_ksh"]="http://blogsearch.google.com/blogsearch_feeds?hl=en&scoring=d&q=(%22ksh93%22%7C%22ksh+93%22+%7C+%22korn93%22+%7C+%22korn+93%22)&ie=utf-8&num=100&output=rss" + # OpenSolaris.org sites + ["ksh93_integration"]="http://www.opensolaris.org/rss/os/project/ksh93-integration/announcements/rss2.xml" + ["shell"]="http://www.opensolaris.org/rss/os/project/shell/announcements/rss2.xml" + ["systemz"]="http://www.opensolaris.org/rss/os/project/systemz/announcements/rss2.xml" + # some Sun staff/sites + ["blogs_sun_com"]="http://blogs.sun.com/main/feed/entries/rss" + ["bigadmin"]="http://www.sun.com/bigadmin/content/rss/motd.xml" + ["jmcp"]="http://www.jmcp.homeunix.com/roller/jmcp/feed/entries/rss" + ["katakai"]="http://blogs.sun.com/katakai/feed/entries/rss" + ["alanc"]="http://blogs.sun.com/alanc/feed/entries/rss" + ["planetsun"]="http://www.planetsun.org/rss20.xml" + ["planetsolaris"]="http://www.planetsolaris.org/rss20.xml" + ["planetopensolaris"]="http://planet.opensolaris.org/rss20.xml" + ["theregister_uk"]="http://www.theregister.co.uk/headlines.rss" + ["heise"]="http://www.heise.de/newsticker/heise.rdf" + ["slashdot"]="http://rss.slashdot.org/Slashdot/slashdot" +) + +typeset progname="${ basename "${0}" ; }" + +typeset -r rssread_usage=$'+ +[-?\n@(#)\$Id: rssread (Roland Mainz) 2008-11-10 \$\n] +[-author?Roland Mainz <roland.mainz@sun.com>] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?rssread - fetch RSS messages and convert them to plain text] +[+DESCRIPTION?\brssread\b RSS to plain text converter + which fetches RSS streams via HTTP and converts them from + RSS to HTML to plain text in the current locale/encoding.] +[I:noiconv?Do not convert data from UTF-8 to current locale/encoding.] + +[ url ] + +[+SEE ALSO?\bksh93\b(1), \bshnote\b(1)] +' + +typeset noiconv=false + +while getopts -a "${progname}" "${rssread_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + I) noiconv=true ;; + +I) noiconv=false ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +typeset url="$1" + +if [[ "${url}" == "" ]] ; then + fatal_error $"No url given." +fi + +if [[ "${bookmark_urls[${url}]}" != "" ]] ; then + printmsg $"Using bookmark ${url} = ${bookmark_urls[${url}]}" + url="${bookmark_urls[${url}]}" +fi + +if ${noiconv} ; then + do_rssread "${url}" +else + do_rssread "${url}" | iconv -f "UTF-8" - - +fi + +exit 0 +#EOF. diff --git a/usr/src/lib/libshell/common/scripts/shcalc.sh b/usr/src/lib/libshell/common/scripts/shcalc.sh new file mode 100644 index 0000000000..db7149a0bd --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/shcalc.sh @@ -0,0 +1,151 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# shcalc - small shell-based calculator +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +function do_calculate +{ + typeset calcline="$1" + float x=0.0 + + printf "(( x=( %s ) ))\n" "${calcline}" | source /dev/stdin + if (( $? != 0 )) ; then + print -f $"%s: Syntax error in %s\n" "${progname}" "${calcline}" + return 1 + fi + + printf "%s == %.40g\n" "${calcline}" x + + return 0 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${shcalc_usage}" OPT '-?' + exit 2 +} + +# program start +# (be carefull with builtins here - they are unconditionally available +# in the shell's "restricted" mode) +builtin basename +builtin sum + +typeset progname="${ basename "${0}" ; }" + +typeset -r shcalc_usage=$'+ +[-?\n@(#)\$Id: shcalc (Roland Mainz) 2008-11-03 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?shcalc - simple shell calculator] +[+DESCRIPTION?\bsshcalc\b is a small calculator application which + prints the results of ISO C99 math expressions read from either + arguments or stdin if no arguments are given.] +[+SEE ALSO?\bksh93\b(1),\bceil\b(3M), \bcopysign\b(3M), \bcos\b(3M), + \bcosh\b(3M), \berf\b(3M), \berfc\b(3M), \bexp\b(3M), + \bexp2\b(3M), \bexpm1\b(3M), \bfabs abs\b(3M), \bfdim\b(3M), + \bfinite\b(3M), \bfloor int\b(3M), \bfma\b(3M), \bfmax\b(3M), \bfmin\b(3M), + \bfmod\b(3M), \bfpclassify\b(3M), \bhypot\b(3M), \bilogb\b(3M), + \bisfinite\b(3M), \bisgreater\b(3M), \bisgreaterequal\b(3M), \bisinf\b(3M), + \bisless\b(3M), \bislessequal\b(3M), \bislessgreater\b(3M), \bisnan\b(3M), + \bisnormal\b(3M), \bissubnormal\b(3M), \bisunordered\b(3M), \biszero\b(3M), + \blgamma\b(3M), \blog\b(3M), \blog1p\b(3M), \blog2\b(3M), + \blogb\b(3M), \bnearbyint\b(3M), \bnextafter\b(3M), \bnexttoward\b(3M), + \bpow\b(3M), \bremainder\b(3M), \brint\b(3M), \bround\b(3M), + \bscalb\b(3M), \bscalbn\b(3M), \bsignbit\b(3M), \bsin\b(3M), + \bsinh\b(3M), \bsqrt\b(3M), \btan\b(3M), \btanh\b(3M), + \btgamma\b(3M), \btrunc\b(3M)] +' +while getopts -a "${progname}" "${shcalc_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +integer res + +if (( $# == 0 )) ; then + # No arguments ? Switch to interactive mode... + + # make sure "read" below uses "gmacs"-like editor keys and "multiline" mode + + set -o gmacs + set -o multiline + + while read "calcline?calc> " ; do + # quit ? + [[ "${calcline}" == ~(Elri)(exit|quit|eof) ]] && break + + # empty line ? + [[ "${calcline}" == ~(Elri)([[:space:]]*) ]] && continue + + do_calculate "$calcline" + (( res=$? )) + done + + exit ${res} +else + while (( $# > 0 )) ; do + do_calculate "$1" + (( res=$? )) + shift + done + + exit ${res} +fi + +# not reached + +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/shircbot.sh b/usr/src/lib/libshell/common/scripts/shircbot.sh new file mode 100644 index 0000000000..2ce067ad6c --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/shircbot.sh @@ -0,0 +1,429 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# shircbot - a simple IRC client/bot demo +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +# Definition for a IRC session class +typeset -T ircsession_t=( + typeset -C server=( + typeset name + integer port + ) + + typeset nick="ksh93irc" + + typeset running=true + + integer fd=-1 + + function createsession + { + set -o xtrace + + _.server.name=$1 + _.server.port=$2 + _.nick=$3 + + redirect {_.fd}<>"/dev/tcp/${_.server.name}/${_.server.port}" + (( $? == 0 )) || { print -n2 $"Could not open server connection." ; return 1 ; } + + printf "fd=%d\n" _.fd + + return 0 + } + + function login + { + { + printf "USER %s %s %s %s\n" "${_.nick}" "${_.nick}" "${_.nick}" "${_.nick}" + printf "NICK %s\n" "${_.nick}" + } >&${_.fd} + + return 0 + } + + function join_channel + { + printf "JOIN %s\n" "$1" >&${_.fd} + + return 0 + } + + function mainloop + { + typeset line + float -S last_tick=0 + # We use the linebuf_t class here since network traffic + # isn't guranteed to fit a single $'\n'-terminated line + # into one TCP package. linebuf_t buffers characters + # until it has one complete line. This avoids the need for + # async I/O normally used by IRC clients + linebuf_t serverbuf + linebuf_t clientbuf + integer fd=${_.fd} + + set -o xtrace + + _.login + + while ${_.running} ; do + while serverbuf.readbuf line <&${fd} ; do + _.dispatch_serverevent "$line" + done + + while clientbuf.readbuf line </dev/stdin ; do + printf "client: %q\n" "${line}" + printf "%s\n" "${line}" >&${fd} + done + + # call mainloop_tick function in intervals to handle + # async events (e.g. automatic /join etc.) + if (( (SECONDS-last_tick) > 5. )) ; then + (( last_tick=SECONDS )) + _.mainloop_tick + fi + done + + return 0 + } + + function mainloop_tick + { + return 0 + } + + function dispatch_serverevent + { + typeset line="$1" + + case "${line}" in + ~(El)PING) + typeset -C ping_args=( + line="$line" + ) + _.serverevent_ping "ping_args" + ;; + ~(El):.*\ PRIVMSG) + typeset -C privmsg_args=( + typeset line="$line" + typeset msguser="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\1}" + typeset msgchannel="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\3}" + typeset msg="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\4}" + ) + _.serverevent_privmsg "privmsg_args" + ;; + ~(El):.*\ INVITE) + typeset -C invite_args=( + typeset line="$line" + typeset inviteuser="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\1}" + typeset invitenick="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\3}" + typeset invitechannel="${line/~(Elr)([^ ]+) ([^ ]+) ([^ ]+) (.*)/\4}" + ) + _.serverevent_invite "invite_args" + ;; + *) + printf "server: %q\n" "${line}" + ;; + esac + + return 0 + } + + function serverevent_privmsg + { + nameref args=$1 + typeset msguser="${args.msguser}" + typeset msgchannel="${args.msgchannel}" + typeset msg="${args.msg}" + + printf "#privms: user=%q, channel=%q, msg=%q\n" "$msguser" "$msgchannel" "$msg" + + return 0 + } + + function serverevent_invite + { + nameref args=$1 + + printf "JOIN %s\n" "${args.invitechannel/:/}" >&${_.fd} + + return 0 + } + + function send_privmsg + { + typeset channel="$1" + typeset msg="$2" + + # Do we have to escape any characters in "msg" ? + printf "PRIVMSG %s :%s\n" "${channel}" "${msg}" >&${_.fd} + + return 0 + } + + function serverevent_ping + { + nameref args=$1 + + printf "PONG %s\n" "${args.line/~(Elr)([^ ]+) ([^ ]+).*/\2}" >&${_.fd} + + return 0 + } +) + +# line buffer class +# The buffer class tries to read characters from the given <fd> until +# it has read a whole line. +typeset -T linebuf_t=( + typeset buf + + function reset + { + _.buf="" + return 0 + } + + function readbuf + { + nameref var=$1 + typeset ch + + while IFS='' read -t 0.2 -N 1 ch ; do + [[ "$ch" == $'\r' ]] && continue + + if [[ "$ch" == $'\n' ]] ; then + var="${_.buf}" + _.reset + return 0 + fi + + _.buf+="$ch" + done + + return 1 + } +) + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${shircbot_usage}" OPT '-?' + exit 2 +} + +# program start +# (be carefull with builtins here - they are unconditionally available +# in the shell's "restricted" mode) +builtin basename +builtin sum + +typeset progname="${ basename "${0}" ; }" + +typeset -r shircbot_usage=$'+ +[-?\n@(#)\$Id: shircbot (Roland Mainz) 2008-10-31 \$\n] +[-author?Roland Mainz <roland.mainz@sun.com>] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?shircbot - simple IRC bot demo] +[+DESCRIPTION?\bshircbot\b is a small demo IRC bot which provides + a simple IRC bot with several subcommands.] +[n:nickname?IRC nickname for this bot.]:[nick] +[s:ircserver?IRC servername.]:[servername] +[j:joinchannel?IRC servername.]:[channelname] +[+SEE ALSO?\bksh93\b(1)] +' + +typeset -C config=( + typeset nickname="${LOGNAME}bot" + typeset servername="irc.freenode.net" + integer port=6667 + typeset -a join_channels +) + +while getopts -a "${progname}" "${shircbot_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + n) config.nickname="${OPTARG}" ;; + s) config.servername="${OPTARG}" ;; + j) config.join_channels+=( "${OPTARG}" ) ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# if no channel was provided we join a predefined set of channels +if (( ${#config.join_channels[@]} == 0 )) ; then + if [[ "${config.servername}" == "irc.freenode.net" ]] ; then + config.join_channels+=( "#opensolaris" ) + config.join_channels+=( "#opensolaris-dev" ) + config.join_channels+=( "#opensolaris-arc" ) + config.join_channels+=( "#ksh" ) + elif [[ "${config.servername}" == ~(E)irc.(sfbay|sweden) ]] ; then + config.join_channels+=( "#onnv" ) + fi +fi + +print "## Start." + +ircsession_t mybot + +# override ircsession_t::serverevent_privmsg with a new method for our bot +function mybot.serverevent_privmsg +{ + nameref args=$1 + typeset msguser="${args.msguser}" + typeset msgchannel="${args.msgchannel}" + typeset msg="${args.msg}" + + printf "#message: user=%q, channel=%q, msg=%q\n" "$msguser" "$msgchannel" "$msg" + + # Check if we get a private message + if [[ "${msgchannel}" == "${_.nick}" ]] ; then + # ${msgchannel} point to our own nick if we got a private message, + # we need to extract the sender's nickname from ${msguser} and put + # it into msgchannel + msgchannel="${msguser/~(El):(.*)!.*/\1}" + else + # check if this is a command for this bot + [[ "$msg" != ~(Eli):${_.nick}:[[:space:]] ]] && return 0 + fi + + # strip beginning (e.g. ":<nick>:" or ":") plus extra spaces + msg="${msg/~(Eli)(:${_.nick})*:[[:space:]]*/}" + + printf "botmsg=%q\n" "$msg" + + case "$msg" in + ~(Eli)date) + _.send_privmsg "$msgchannel" "$( + ( printf "%(%Y-%m-%d, %Th/%Z)T\n" ) + )" + ;; + ~(Eli)echo) + _.send_privmsg "$msgchannel" "${msg#*echo}" + ;; + ~(Eli)exitbot) + typeset exitkey="$(print "$msguser" | sum -x sha1)" # this is unwise&&insecure + if [[ "$msg" == *${exitkey}* ]] ; then + _.running=false + fi + ;; + ~(Eli)help) + _.send_privmsg "$msgchannel" "$( + printf "Hello, this is shircbot, written in ksh93 (%s). " "${.sh.version}" + printf "Subcommands are 'say hello', 'math <math-expr>', 'stocks', 'uuid', 'date' and 'echo'." + )" + ;; + ~(Eli)math) + if [[ "${msg}" == ~(E)[\`\$] ]] ; then + # "restricted" shell mode would prevent any damage but we try to be carefull... + _.send_privmsg "$msgchannel" "Syntax error." + else + typeset mathexpr="${msg#*math}" + + printf "Calculating '%s'\n" "${mathexpr}" + _.send_privmsg "$msgchannel" "$( + ( printf 'export PATH=/usr/$RANDOM/foo ; set -o restricted ; printf "%%s = %%.40g\n" "%s" $(( %s ))\n' "${mathexpr}" "${mathexpr}" | source /dev/stdin 2>&1 ) + )" + fi + ;; + ~(Eli)say\ hello) + _.send_privmsg "$msgchannel" "Hello, this is a bot." + ;; + ~(Eli)stocks) + typeset stockmsg tickersymbol + for tickersymbol in "JAVA" "IBM" "AAPL" "HPQ" ; do + stockmsg="$( /usr/sfw/bin/wget -q -O /dev/stdout "http://quote.yahoo.com/d/quotes.csv?f=sl1d1t1c1ohgv&e=.csv&s=${tickersymbol}" 2>&1 )" + _.send_privmsg "$msgchannel" "${tickersymbol}: ${stockmsg//,/ }" + done + ;; + ~(Eli)uuid) + _.send_privmsg "$msgchannel" "$( + ( print "%(%Y%M%D%S%N)T$((RANDOM))%s\n" "${msguser}" | sum -x sha256 ) + )" + ;; + esac + + return 0 +} + +# Automatically join the list of channels listed in |config.join_channels| +# after the client is connected to the server for some time +function mybot.mainloop_tick +{ + integer -S autojoin_done=2 + integer i + + if (( autojoin_done-- == 0 && ${#config.join_channels[@]} > 0 )) ; then + print "# Autojoin channels..." + + for ((i=0 ; i < ${#config.join_channels[@]} ; i++ )) ; do + mybot.join_channel "${config.join_channels[i]}" + done + fi + + return 0 +} + +mybot.createsession "${config.servername}" ${config.port} "${config.nickname}" + +# This is a network-facing application - once we've set eveything up +# we set PATH to a random value and switch to the shell's restricted +# mode to make sure noone can escape the jail. +#export PATH=/usr/$RANDOM/foo +#set -o restricted + +mybot.mainloop + +print "## End." + +exit 0 diff --git a/usr/src/lib/libshell/common/scripts/shlint.sh b/usr/src/lib/libshell/common/scripts/shlint.sh new file mode 100644 index 0000000000..640fb14837 --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/shlint.sh @@ -0,0 +1,94 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# shlint - a simple lint wrapper around "shcomp" +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${shlint_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename + +typeset progname="${ basename "${0}" ; }" + +typeset -r shlint_usage=$'+ +[-?\n@(#)\$Id: shlint (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@sun.com>] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?shlint - lint for POSIX shell scripts] +[+DESCRIPTION?\bshlint\b is a lint for POSIX shell scripts.] +[+SEE ALSO?\bshcomp\b(1), \bksh93\b(1)] +' + +while getopts -a "${progname}" "${shlint_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +file="$1" +[[ ! -f "$file" ]] && fatal_error $"File ${file} not found." +[[ ! -r "$file" ]] && fatal_error $"File ${file} not readable." + +x="$( /usr/bin/ksh93 -n "${file}" 2>&1 1>/dev/null )" + +printf "%s" "$x" + +[[ "$x" != "" ]] && exit 1 || exit 0 +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/shman.sh b/usr/src/lib/libshell/common/scripts/shman.sh new file mode 100644 index 0000000000..57e57cec23 --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/shman.sh @@ -0,0 +1,388 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +function debug_print +{ + # don't use "--" here to allow "-f" for formatting +# print -u2 "$@" + return 0 +} + +# Build a list of compound variables calculated from MANPATH and +# locale which contain... +# "manpath_element" - the MANPATH element this entry belongs to +# "dir" - physical directory of "manpath_element" +# "sect" - section (if "manpath_element" is something like /usr/share/man,1b) +# ... and put the result in the array named by argv[1] +function enumerate_mandirs +{ + nameref md=$1 + typeset manpath_element dir sect manlang + integer i=0 + + if [[ "${LC_MESSAGES}" != "" ]] ; then + manlang="${LC_MESSAGES}" + else + manlang="${LANG}" + fi + + print -r -- "${MANPATH//:/$'\n'}" | while read manpath_element ; do + # strip section from manpath elements like "/usr/share/man,1b" + dir="${manpath_element/~(E)(.*),(.*)/\1}" + sect="${manpath_element/~(E)(.*),(.*)/\2}" + [[ "${sect}" == "${dir}" ]] && sect="" + + if [[ "${manlang}" != "" && -d "${dir}/${manlang}" ]] ; then + md+=( + manpath_element="${manpath_element}" + dir="${dir}/${manlang}" + sect="${sect}" + ) + fi + if [[ -d "${dir}" ]] ; then + md+=( + manpath_element="${manpath_element}" + dir="${dir}" + sect="${sect}" + ) + fi + done + + return 0 +} + +function enumerate_mansects +{ + nameref ms=$1 + nameref mandir_node=$2 + typeset mancf="${mandir_node.dir}/man.cf" + typeset x s l + + if [[ "${mandir_node.sect}" != "" ]] ; then + x="${mandir_node.sect}" + elif [[ "${MANSECTS}" != "" ]] ; then + x="${MANSECTS//,/$'\n'}" + elif [[ -f "${mancf}" && -r "${mancf}" ]] ; then + x="$(egrep -v '^#|^[[:space:]]*$' <"${mancf}" | egrep '^MANSECTS=')" + x="${x/MANSECTS=}/" + x="${x//,/$'\n'}" + else + x="$(cd "${mandir_node.dir}" ; \ + ls -1d ~(El)(sman|man).*/ | \ + while read s ; do \ + s="${s/~(El)(sman|man)/}" ; \ + s="${s/~(Er)\//}" ; \ + print -r -- "$s" ; \ + done)" + fi + + while read l ; do + [[ "${l}" != ~(Elr)[[:blank:]]* ]] && ms+=( "${l}" ) +# print -- "sect=$l" + done <<<"${x}" + +# printf "enumerate_mansects: found %d entries.\n" ${#ms[@]} + + return 0 +} + +# wrapper around more/less +function browse_manpage +{ + typeset tmpdirname + typeset doc_filename="$1" + typeset doc_title="$2" + + # squish characters in filename which are not allowed in a filesystem + # (currently '/') + doc_title="${doc_title//\//}" + + # check if we have "less" installed, if not fall back to /usr/xpg4/bin/more + if which less >/dev/null 2>&1 ; then + # use "cat" here to avoid that "less" may try funny things + cat <"${doc_filename}" | less -I -M $"--prompt=MManual\ page\ ${doc_title}\ ?ltline\ %lt?L/%L.:" + else + tmpdirname="$(mktemp -d "/tmp/shman_${PPID}_$$_XXXXXX")" + + mkdir -p "${tmpdirname}" || { print -u2 -f $"Couldn't create tmp. dir %s\n" "${tmpdirname}" ; return 1 ; } + + ( + cd "${tmpdirname}" + + # note: we need to support /dev/stdin + cat <"${doc_filename}" >"./${doc_title}" + + /usr/xpg4/bin/more "${doc_title}" + + rm -f "${doc_title}" + ) + + rmdir "${tmpdirname}" + fi + + return 0 +} + +# /usr/bin/man <keyword> +function show_manpage +{ + typeset -a -C mandirs + integer i + integer j + + enumerate_mandirs mandirs +# debug_print -- "${mandirs[@]}" + + integer num_mandirs=${#mandirs[@]} + + for ((i=0 ; i < num_mandirs ; i++ )) ; do + typeset mandir="${mandirs[i].dir}" + + typeset -a mansects + enumerate_mansects mansects "mandirs[$i]" + + integer num_mansects="${#mansects[@]}" +# debug_print -- "mansects=${mansects[@]}" + + for ((j=0 ; j < num_mansects ; j++ )) ; do + typeset mansect="${mansects[j]}" + + # try 1: SGML manpage + typeset match="${mandir}/sman${mansect}/${manname}.${mansect}" + if [[ -r "${match}" ]] ; then + typeset note nlink + + # follow SGML links if needed (needs rework, including protection against link loops) + while true ; do + debug_print -f "match: %s\n" "${match}" + + tmp="$(cd "${mandir}" ; LC_MESSAGES=C /usr/lib/sgml/sgml2roff "${match}")" + read note nlink <<<"${tmp}" + + if [[ "${note}" == ".so" ]] ; then + match="${nlink}" + else + break + fi + done + + tbl <<<"${tmp}" | eqn | nroff -u0 -Tlp -man - | col -x | browse_manpage /dev/stdin "${manname}(${mansect})" + return 0 + fi + + # try 2: troff manpage + match="${mandir}/man${mansect}/${manname}.${mansect}" + if [[ -r "${match}" ]] ; then + debug_print -f "match: %s\n" "${match}" + tbl <"${match}" | eqn | nroff -u0 -Tlp -man - | col -x | browse_manpage /dev/stdin "${manname}(${mansect})" + return 0 + fi + done + unset mansects num_mansects + done + + printf $"No manual entry for %s.\n" "${manname}" + return 0 +} + +# /usr/bin/man -l <keyword> +function list_manpages +{ + typeset -a -C mandirs + + enumerate_mandirs mandirs + #debug_print -- "${mandirs[@]}" + + integer num_mandirs=${#mandirs[@]} + + for ((i=0 ; i < num_mandirs ; i++ )) ; do + typeset mandir="${mandirs[i].dir}" + + typeset -a mansects + enumerate_mansects mansects "mandirs[$i]" + + integer num_mansects="${#mansects[@]}" +# debug_print -- "mansects=${mansects[@]}" + + for ((j=0 ; j < num_mansects ; j++ )) ; do + mansect="${mansects[j]}" + + # try 1: SGML manpage + match="${mandir}/sman${mansect}/${manname}.${mansect}" + if [[ -r "${match}" ]] ; then + printf "%s (%s)\t-M %s\n" "${manname}" "${mansect}" "${mandir}" + continue + fi + + # try 2: troff manpage + match="${mandir}/man${mansect}/${manname}.${mansect}" + if [[ -r "${match}" ]] ; then + printf "%s (%s)\t-M %s\n" "${manname}" "${mansect}" "${mandir}" + continue + fi + done + unset mansects num_mansects + done + + return 0 +} + +# /usr/bin/appropos +function list_keywords +{ + typeset -a mandirs + typeset name namesec title + + enumerate_mandirs mandirs + #debug_print -- "${mandirs[@]}" + + integer num_mandirs=${#mandirs[@]} + + for ((i=0 ; i < num_mandirs ; i++ )) ; do + typeset mandir="${mandirs[i].dir}" + typeset windexfile="${mandir}/windex" + + if [[ ! -r "${windexfile}" ]] ; then + print -u2 -f $"%s: Can't open %s.\n" "${progname}" "${windexfile}" + continue + fi + + while IFS=$'\t' read name namesec title ; do + if [[ "${name}${namesec}${title}" == ~(Fi)${manname} ]] ; then + printf "%s\t%s\t%s\n" "${name}" "${namesec}" "${title}" + fi + done <"${windexfile}" + done + + return 0 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${man_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename +builtin cat +builtin date + +typeset progname="$(basename "${0}")" + +typeset -r man_usage=$'+ +[-?\n@(#)\$Id: shman (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[-author?Roland Mainz <roland.mainz@sun.com>] +[+NAME?man - find and display reference manual pages] +[+DESCRIPTION?The man command displays information from the reference + manuals. It displays complete manual pages that you select + by name, or one-line summaries selected either by keyword + (-k), or by the name of an associated file (-f). If no + manual page is located, man prints an error message.] +[+?write me.] +[k:keyword?Prints out one-line summaries from the windex database (table of contents) that + contain any of the given keywords. The windex database is created using + catman(1M).] +[l:list?Lists all manual pages found matching name within the search path.] +[M:mpath?Specifies an alternate search path for manual pages. path is a colon-separated + list of directories that contain manual page directory subtrees. For example, if + path is /usr/share/man:/usr/local/man, man searches for name in the standard + location, and then /usr/local/man. When used with the -k or -f options, the -M + option must appear first. Each directory in the path is assumed to contain subdirectories of the form man* or sman* , + one for each section. This option overrides the MANPATH environment variable.]:[path] +[s:section?Specifies sections of the manual for man to search. The directories searched for + name are limited to those specified by section. section can be a numerical + digit, perhaps followed by one or more letters to match the desired section of + the manual, for example, "3libucb". Also, section can be a word, for example, + local, new, old, public. section can also be a letter. + To specify multiple sections, separate each section with + a comma. This option overrides the MANPATH environment variable and the man.cf + file. + See Search Path below for an explanation of how man conducts its search.]:[section] + +name + +[+SEE ALSO?\bksh93\b(1), \bman\b(1)] +' + +typeset do_list=false +typeset do_keyword=false + +while getopts -a "${progname}" "${man_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + M) MANPATH="${OPTARG}" ;; + l) do_list=true ;; + k) do_keyword=true ;; + s) MANSECTS="${OPTARG}" ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# cd /usr/man; LC_MESSAGES=C /usr/lib/sgml/sgml2roff /usr/man/sman1as/asadmin-list-timers.1as | tbl | eqn | nroff -u0 -Tlp -man - | col -x > /tmp/mpLQaqac + +typeset manname="$1" +debug_print -f "# searching for %s ...\n" "${manname}" + +if ${do_keyword} ; then + list_keywords +elif ${do_list} ; then + list_manpages +else + show_manpage +fi + +# todo: better exit codes +exit 0 +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/shnote.sh b/usr/src/lib/libshell/common/scripts/shnote.sh new file mode 100644 index 0000000000..1361feb8a7 --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/shnote.sh @@ -0,0 +1,414 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +function encode_multipart_form_data +{ + nameref formdata="$1" + nameref content="formdata.content" + integer numformelements=${#formdata.form[*]} + integer i + typeset tmp + + content="" + + # todo: add support to upload files + for (( i=0 ; i < numformelements ; i++ )) ; do + nameref element="formdata.form[${i}]" + + content+="--${formdata.boundary}\n" + content+="Content-Disposition: form-data; name=\"${element.name}\"\n" + content+="\n" + # make sure we quote the '\' properly since we pass these data to one instance of + # "print" when putting the content on the wire. + content+="${element.data//\\/\\\\}\n" # fixme: may need encoding for non-ASCII data + done + + # we have to de-quote the content before we can count the real numer of bytes in the payload + tmp="$(print -- "${content}")" + formdata.content_length=${#tmp} + + # add content tail (which MUST not be added to the content length) + content+="--${formdata.boundary}--\n" + + return 0 +} + +# parse HTTP return code, cookies etc. +function parse_http_response +{ + nameref response="$1" + typeset h statuscode statusmsg i + + # we use '\r' as additional IFS to filter the final '\r' + IFS=$' \t\r' read -r h statuscode statusmsg # read HTTP/1.[01] <code> + [[ "$h" != ~(Eil)HTTP/.* ]] && { print -u2 -f $"%s: HTTP/ header missing\n" "$0" ; return 1 ; } + [[ "$statuscode" != ~(Elr)[0-9]* ]] && { print -u2 -f $"%s: invalid status code\n" "$0" ; return 1 ; } + response.statuscode="$statuscode" + response.statusmsg="$statusmsg" + + # skip remaining headers + while IFS='' read -r i ; do + [[ "$i" == $'\r' ]] && break + + # strip '\r' at the end + i="${i/~(Er)$'\r'/}" + + case "$i" in + ~(Eli)Content-Type:.*) + response.content_type="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Content-Length:[[:blank:]]*[0-9]*) + integer response.content_length="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Transfer-Encoding:.*) + response.transfer_encoding="${i/~(El).*:[[:blank:]]*/}" + ;; + esac + done + + return 0 +} + +function cat_http_body +{ + typeset emode="$1" + typeset hexchunksize="0" + integer chunksize=0 + + if [[ "${emode}" == "chunked" ]] ; then + while IFS=$'\r' read hexchunksize && + [[ "${hexchunksize}" == ~(Elri)[0-9abcdef]* ]] && + (( chunksize=16#${hexchunksize} )) && (( chunksize > 0 )) ; do + dd bs=1 count="${chunksize}" 2>/dev/null + done + else + cat + fi + + return 0 +} + +function history_write_record +{ + # rec: history record: + # rec.title + # rec.description + # rec.provider + # rec.providertoken + # rec.url + nameref rec="$1" + integer histfd + + mkdir -p "${HOME}/.shnote" + + { + # write a single-line record which can be read + # as a compound variable back into the shell + printf "title=%q description=%q date=%q provider=%q providertoken=%q url=%q\n" \ + "${rec.title}" \ + "${rec.description}" \ + "$(date)" \ + "${rec.provider}" \ + "${rec.providertoken}" \ + "${rec.url}" + } >>"${history_file}" + + return $? +} + +function print_history +{ + integer histfd # http stream number + typeset line + + (( $# != 0 && $# != 1 )) && { print -u2 -f $"%s: Wrong number of arguments.\n" "$0" ; return 1 ; } + + # default output format is: + # <access url>/<title> <date> <access url> + [[ "$1" == "-l" ]] || printf "# %s\t\t\t\t\t%s\t%s\n" "<url>" "<title>" "<date>" + + # no history file ? + if [[ ! -f "${history_file}" ]] ; then + return 0 + fi + + # open history file + redirect {histfd}<>"${history_file}" + (( $? != 0 )) && { print -u2 "Couldn't open history file." ; return 1 ; } + + while read -u${histfd} line ; do + typeset -C rec + + printf "( %s )\n" "${line}" | read -C rec + + if [[ "$1" == "-l" ]] ; then + print -- "${rec}" + else + printf "%q\t%q\t%q\n" "${rec.url}" "${rec.title}" "${rec.date}" + fi + + unset rec + done + + # close history file + redirect {histfd}<&- + + return 0 +} + +function put_note_pastebin_ca +{ + # key to autheticate this script against pastebin.ca + typeset -r pastebin_ca_key="9CFXFyeNC3iga/vthok75kTBu5kSSLPD" + # site setup + typeset url_host="opensolaris.pastebin.ca" + typeset url_path="/quiet-paste.php?api=${pastebin_ca_key}" + typeset url="http://${url_host}${url_path}" + integer netfd # http stream number + typeset -C httpresponse + + (( $# != 1 )) && { print -u2 -f $"%s: Wrong number of arguments.\n" "$0" ; return 1 ; } + (( ${#1} == 0 )) && { print -u2 -f $"%s: No data.\n" "$0" ; return 1 ; } + + # argument for "encode_multipart_form_data" + typeset mimeform=( + # input + typeset boundary + typeset -a form + # output + typeset content + integer content_length + ) + + typeset request="" + typeset content="" + + typeset -r boundary="--------shnote_${RANDOM}_Xfish_${RANDOM}_Yeats_${RANDOM}_Zchicken_${RANDOM}monster_--------" + + mimeform.boundary="${boundary}" + mimeform.form=( # we use explicit index numbers since we rely on them below when filling the history + [0]=( name="name" data="${LOGNAME}" ) + [1]=( name="expiry" data="Never" ) + [2]=( name="type" data="1" ) + [3]=( name="description" data="logname=${LOGNAME};hostname=$(hostname);date=$(date)" ) + [4]=( name="content" data="$1" ) + ) + encode_multipart_form_data mimeform + + content="${mimeform.content}" + + request="POST ${url_path} HTTP/1.1\r\n" + request+="Host: ${url_host}\r\n" + request+="User-Agent: ${http_user_agent}\r\n" + request+="Connection: close\r\n" + request+="Content-Type: multipart/form-data; boundary=${boundary}\r\n" + request+="Content-Length: $(( mimeform.content_length ))\r\n" + + redirect {netfd}<>"/dev/tcp/${url_host}/80" + (( $? != 0 )) && { print -u2 -f $"$0: Couldn't open connection to %s.\n" "$0" "${url_host}" ; return 1 ; } + + # send http post + { + print -n -- "${request}\r\n" + print -n -- "${content}\r\n" + } >&${netfd} + + # process reply + parse_http_response httpresponse <&${netfd} + response="$(cat_http_body "${httpresponse.transfer_encoding}" <&${netfd})" + + # close connection + redirect {netfd}<&- + + if [[ "${response}" == ~(E).*SUCCESS.* ]] ; then + typeset response_token="${response/~(E).*SUCCESS:/}" + + printf "SUCCESS: http://opensolaris.pastebin.ca/%s\n" "${response_token}" + + # write history entry + typeset histrec=( + title="${mimeform.form[0].data}" + description="${mimeform.form[3].data}" + providertoken="${response_token}" + provider="opensolaris.pastebin.ca" + url="http://opensolaris.pastebin.ca/${response_token}" + ) + + history_write_record histrec + return 0 + else + printf "ERROR: %s\n" "${response}" + return 1 + fi + + # not reached +} + +function get_note_pastebin_ca +{ + typeset recordname="$1" + integer netfd # http stream number + + (( $# != 1 )) && { print -u2 -f $"%s: No key or key URL.\n" "$0" ; return 1 ; } + + case "${recordname}" in + ~(Elr)[0-9][0-9]*) + # pass-through + ;; + ~(Elr)http://opensolaris.pastebin.ca/raw/[0-9]*) + recordname="${recordname/~(El)http:\/\/opensolaris.pastebin.ca\/raw\//}" + ;; + ~(Elr)http://opensolaris.pastebin.ca/[0-9]*) + recordname="${recordname/~(El)http:\/\/opensolaris.pastebin.ca\//}" + ;; + *) + fatal_error $"Unsupported record name ${recordname}." + esac + + print -u2 -f "# Record name is '%s'\n" "${recordname}" + + typeset url_host="opensolaris.pastebin.ca" + typeset url_path="/raw/${recordname}" + typeset url="http://${url_host}${url_path}" + # I hereby curse Solaris for not having an entry for "http" in /etc/services + + # open TCP channel + redirect {netfd}<>"/dev/tcp/${url_host}/80" + (( $? != 0 )) && { print -u2 -f $"%s: Couldn't open connection to %s.\n" "$0" "${url_host}" ; return 1 ; } + + # send HTTP request + request="GET ${url_path} HTTP/1.1\r\n" + request+="Host: ${url_host}\r\n" + request+="User-Agent: ${http_user_agent}\r\n" + request+="Connection: close\r\n" + print -u${netfd} -- "${request}\r\n" + + # collect response and send it to stdout + parse_http_response httpresponse <&${netfd} + cat_http_body "${httpresponse.transfer_encoding}" <&${netfd} + + # close connection + redirect {netfd}<&- + + print # add newline + + return 0 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${USAGE}" OPT '-?' + exit 2 +} + +# program start +builtin basename +builtin cat +builtin date +builtin uname + +typeset progname="${ basename "${0}" ; }" + +# HTTP protocol client identifer +typeset -r http_user_agent="shnote/ksh93 (2008-10-14; $(uname -s -r -p))" + +# name of history log (the number after "history" is some kind of version +# counter to handle incompatible changes to the history file format) +typeset -r history_file="${HOME}/.shnote/history0.txt" + +typeset -r shnote_usage=$'+ +[-?\n@(#)\$Id: shnote (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?shnote - read/write text data to internet clipboards] +[+DESCRIPTION?\bshnote\b is a small utilty which can read and write text + data to internet "clipboards" such as opensolaris.pastebin.ca.] +[+?The first arg \bmethod\b describes one of the methods, "put" saves a string + to the internet clipboard, returning an identifer and the full URL + where the data are stored. The method "get" retrives the raw + information using the identifer from the previous "put" action. + The method "hist" prints a history of transactions created with the + "put" method and the keys to retrive them again using the "get" method.] +[+?The second arg \bstring\b contains either the string data which should be + stored on the clipboard using the "put" method, the "get" method uses + this information as identifer to retrive the raw data from the + clipboard.] + +method [ string ] + +[+SEE ALSO?\bksh93\b(1), \brssread\b(1), \bshtwitter\b(1), \bshtinyurl\b(1), http://opensolaris.pastebin.ca] +' + +while getopts -a "${progname}" "${shnote_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# expecting at least one more argument, the single method below will do +# the checks for more arguments if needed ("put" and "get" methods need +# at least one extra argument, "hist" none). +(($# >= 1)) || usage + +typeset method="$1" +shift + +case "${method}" in + put) put_note_pastebin_ca "$@" ; exit $? ;; + get) get_note_pastebin_ca "$@" ; exit $? ;; + hist) print_history "$@" ; exit $? ;; + *) usage ;; +esac + +fatal_error $"not reached." +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/shpiano.sh b/usr/src/lib/libshell/common/scripts/shpiano.sh new file mode 100644 index 0000000000..4690fee5ed --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/shpiano.sh @@ -0,0 +1,1386 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +function beep +{ + tput bel + return 0 +} + +# array which holds frequency and sample data +# (the data are created on demand, "sample_set" indicates whether the "sample" variable +# needs to be filled or not) +typeset -A tones=( + ["C3"]=( float freq=261.63 ; typeset sample_set="false" ; typeset -b sample ) + ["C#3"]=( float freq=277.18 ; typeset sample_set="false" ; typeset -b sample ) + ["D3"]=( float freq=293.66 ; typeset sample_set="false" ; typeset -b sample ) + ["D#3"]=( float freq=311.13 ; typeset sample_set="false" ; typeset -b sample ) + ["E3"]=( float freq=329.63 ; typeset sample_set="false" ; typeset -b sample ) + ["F3"]=( float freq=349.23 ; typeset sample_set="false" ; typeset -b sample ) + ["F#3"]=( float freq=369.99 ; typeset sample_set="false" ; typeset -b sample ) + ["G3"]=( float freq=391.99 ; typeset sample_set="false" ; typeset -b sample ) + ["G#3"]=( float freq=415.31 ; typeset sample_set="false" ; typeset -b sample ) + ["A3"]=( float freq=440.00 ; typeset sample_set="false" ; typeset -b sample ) + ["A#3"]=( float freq=466.16 ; typeset sample_set="false" ; typeset -b sample ) + ["B3"]=( float freq=493.88 ; typeset sample_set="false" ; typeset -b sample ) + ["C4"]=( float freq=523.25 ; typeset sample_set="false" ; typeset -b sample ) + ["C#4"]=( float freq=554.37 ; typeset sample_set="false" ; typeset -b sample ) + ["D4"]=( float freq=587.33 ; typeset sample_set="false" ; typeset -b sample ) + ["D#4"]=( float freq=622.25 ; typeset sample_set="false" ; typeset -b sample ) + ["E4"]=( float freq=659.26 ; typeset sample_set="false" ; typeset -b sample ) + ["F4"]=( float freq=698.46 ; typeset sample_set="false" ; typeset -b sample ) + ["F#4"]=( float freq=739.99 ; typeset sample_set="false" ; typeset -b sample ) + ["G4"]=( float freq=783.99 ; typeset sample_set="false" ; typeset -b sample ) + ["G#4"]=( float freq=830.61 ; typeset sample_set="false" ; typeset -b sample ) + ["A4"]=( float freq=880.00 ; typeset sample_set="false" ; typeset -b sample ) + ["A#4"]=( float freq=932.32 ; typeset sample_set="false" ; typeset -b sample ) + ["B4"]=( float freq=987.77 ; typeset sample_set="false" ; typeset -b sample ) + ["C5"]=( float freq=1046.5 ; typeset sample_set="false" ; typeset -b sample ) + + # dummy entry for pause + ["p"]=( float freq=NaN ; typeset sample_set="false" ; typeset -b sample ) +) + +# alias table which translates the various names of "notes" to the matching entry +# in the "tones" table +typeset -r -A notes=( + ["C3"]=( val=tones["C3"] ) ["key_d"]=( val=tones["C3"] ) + ["C#3"]=( val=tones["C#3"] ) ["key_r"]=( val=tones["C#3"] ) + ["D3"]=( val=tones["D3"] ) ["key_f"]=( val=tones["D3"] ) + ["D#3"]=( val=tones["D#3"] ) ["key_t"]=( val=tones["D#3"] ) + ["E3"]=( val=tones["E3"] ) ["key_g"]=( val=tones["E3"] ) + ["F3"]=( val=tones["F3"] ) ["key_h"]=( val=tones["F3"] ) + ["F#3"]=( val=tones["F#3"] ) ["key_u"]=( val=tones["F#3"] ) + ["G3"]=( val=tones["G3"] ) ["key_j"]=( val=tones["G3"] ) + ["G#3"]=( val=tones["G#3"] ) ["key_i"]=( val=tones["G#3"] ) + ["A3"]=( val=tones["A3"] ) ["key_k"]=( val=tones["A3"] ) + ["A#3"]=( val=tones["A#3"] ) ["key_o"]=( val=tones["A#3"] ) + ["B3"]=( val=tones["B3"] ) ["key_l"]=( val=tones["B3"] ) + ["C4"]=( val=tones["C4"] ) ["key_D"]=( val=tones["C4"] ) + ["C#4"]=( val=tones["C#4"] ) ["key_R"]=( val=tones["C#4"] ) + ["D4"]=( val=tones["D4"] ) ["key_F"]=( val=tones["D4"] ) + ["D#4"]=( val=tones["D#4"] ) ["key_T"]=( val=tones["D#4"] ) + ["E4"]=( val=tones["E4"] ) ["key_G"]=( val=tones["E4"] ) + ["F4"]=( val=tones["F4"] ) ["key_H"]=( val=tones["F4"] ) + ["F#4"]=( val=tones["F#4"] ) ["key_U"]=( val=tones["F#4"] ) + ["G4"]=( val=tones["G4"] ) ["key_J"]=( val=tones["G4"] ) + ["G#4"]=( val=tones["G#4"] ) ["key_I"]=( val=tones["G#4"] ) + ["A4"]=( val=tones["A4"] ) ["key_K"]=( val=tones["A4"] ) + ["A#4"]=( val=tones["A#4"] ) ["key_O"]=( val=tones["A#4"] ) + ["B4"]=( val=tones["B4"] ) ["key_L"]=( val=tones["B4"] ) + ["C5"]=( val=tones["C5"] ) +) + +# array used to convert a 14-bit unsigned PCM value to +# inverted 8-bit u-law +# (values were "stolen" from usr/src/cmd/audio/utilities/g711.c +integer -r -a audio_pcmulinear14bittoulaw8bit=( + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 + 0x01 0x01 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02 + 0x02 0x02 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 + 0x03 0x03 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 0x04 + 0x04 0x04 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 0x05 + 0x05 0x05 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 0x06 + 0x06 0x06 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 0x07 + 0x07 0x07 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 + 0x08 0x08 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 0x09 + 0x09 0x09 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a 0x0a + 0x0a 0x0a 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b 0x0b + 0x0b 0x0b 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c 0x0c + 0x0c 0x0c 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d 0x0d + 0x0d 0x0d 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e 0x0e + 0x0e 0x0e 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f 0x0f + 0x0f 0x0f 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 + 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 + 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 + 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 + 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 + 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 + 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 + 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 + 0x10 0x10 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 + 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 + 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 + 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 + 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 + 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 + 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 + 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 + 0x11 0x11 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 + 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 + 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 + 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 + 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 + 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 + 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 + 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 + 0x12 0x12 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 + 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 + 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 + 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 + 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 + 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 + 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 + 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 + 0x13 0x13 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 + 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 + 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 + 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 + 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 + 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 + 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 + 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 + 0x14 0x14 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 + 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 + 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 + 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 + 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 + 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 + 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 + 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 + 0x15 0x15 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 + 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 + 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 + 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 + 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 + 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 + 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 + 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 + 0x16 0x16 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 + 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 + 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 + 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 + 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 + 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 + 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 + 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x17 + 0x17 0x17 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 + 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 + 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 + 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 + 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 + 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 + 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 + 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 0x18 + 0x18 0x18 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 + 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 + 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 + 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 + 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 + 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 + 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 + 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 0x19 + 0x19 0x19 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a + 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a + 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a + 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a + 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a + 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a + 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a + 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a 0x1a + 0x1a 0x1a 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b + 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b + 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b + 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b + 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b + 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b + 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b + 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b 0x1b + 0x1b 0x1b 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c + 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c + 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c + 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c + 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c + 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c + 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c + 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c 0x1c + 0x1c 0x1c 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d + 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d + 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d + 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d + 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d + 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d + 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d + 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d 0x1d + 0x1d 0x1d 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e + 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e + 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e + 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e + 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e + 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e + 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e + 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e 0x1e + 0x1e 0x1e 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f + 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f + 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f + 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f + 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f + 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f + 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f + 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f 0x1f + 0x1f 0x1f 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 + 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 + 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 + 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 + 0x20 0x20 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 + 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 + 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 + 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 0x21 + 0x21 0x21 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 + 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 + 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 + 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 0x22 + 0x22 0x22 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 + 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 + 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 + 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 0x23 + 0x23 0x23 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 + 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 + 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 + 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 0x24 + 0x24 0x24 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 + 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 + 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 + 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 0x25 + 0x25 0x25 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 + 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 + 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 + 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 0x26 + 0x26 0x26 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 + 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 + 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 + 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 0x27 + 0x27 0x27 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 + 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 + 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 + 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 0x28 + 0x28 0x28 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 + 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 + 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 + 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 0x29 + 0x29 0x29 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a + 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a + 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a + 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a 0x2a + 0x2a 0x2a 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b + 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b + 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b + 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b 0x2b + 0x2b 0x2b 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c + 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c + 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c + 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c 0x2c + 0x2c 0x2c 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d + 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d + 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d + 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d 0x2d + 0x2d 0x2d 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e + 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e + 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e + 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e 0x2e + 0x2e 0x2e 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f + 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f + 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f + 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f 0x2f + 0x2f 0x2f 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 + 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 + 0x30 0x30 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 + 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 0x31 + 0x31 0x31 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 + 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 0x32 + 0x32 0x32 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 + 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 0x33 + 0x33 0x33 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 + 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 0x34 + 0x34 0x34 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 + 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 0x35 + 0x35 0x35 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 + 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 0x36 + 0x36 0x36 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 + 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 0x37 + 0x37 0x37 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 + 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 0x38 + 0x38 0x38 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 + 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 0x39 + 0x39 0x39 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a + 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a 0x3a + 0x3a 0x3a 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b + 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b 0x3b + 0x3b 0x3b 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c + 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c 0x3c + 0x3c 0x3c 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d + 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d 0x3d + 0x3d 0x3d 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e + 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e 0x3e + 0x3e 0x3e 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f + 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f 0x3f + 0x3f 0x3f 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 0x40 + 0x40 0x40 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 + 0x41 0x41 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x42 + 0x42 0x42 0x43 0x43 0x43 0x43 0x43 0x43 0x43 0x43 0x43 0x43 0x43 0x43 0x43 0x43 + 0x43 0x43 0x44 0x44 0x44 0x44 0x44 0x44 0x44 0x44 0x44 0x44 0x44 0x44 0x44 0x44 + 0x44 0x44 0x45 0x45 0x45 0x45 0x45 0x45 0x45 0x45 0x45 0x45 0x45 0x45 0x45 0x45 + 0x45 0x45 0x46 0x46 0x46 0x46 0x46 0x46 0x46 0x46 0x46 0x46 0x46 0x46 0x46 0x46 + 0x46 0x46 0x47 0x47 0x47 0x47 0x47 0x47 0x47 0x47 0x47 0x47 0x47 0x47 0x47 0x47 + 0x47 0x47 0x48 0x48 0x48 0x48 0x48 0x48 0x48 0x48 0x48 0x48 0x48 0x48 0x48 0x48 + 0x48 0x48 0x49 0x49 0x49 0x49 0x49 0x49 0x49 0x49 0x49 0x49 0x49 0x49 0x49 0x49 + 0x49 0x49 0x4a 0x4a 0x4a 0x4a 0x4a 0x4a 0x4a 0x4a 0x4a 0x4a 0x4a 0x4a 0x4a 0x4a + 0x4a 0x4a 0x4b 0x4b 0x4b 0x4b 0x4b 0x4b 0x4b 0x4b 0x4b 0x4b 0x4b 0x4b 0x4b 0x4b + 0x4b 0x4b 0x4c 0x4c 0x4c 0x4c 0x4c 0x4c 0x4c 0x4c 0x4c 0x4c 0x4c 0x4c 0x4c 0x4c + 0x4c 0x4c 0x4d 0x4d 0x4d 0x4d 0x4d 0x4d 0x4d 0x4d 0x4d 0x4d 0x4d 0x4d 0x4d 0x4d + 0x4d 0x4d 0x4e 0x4e 0x4e 0x4e 0x4e 0x4e 0x4e 0x4e 0x4e 0x4e 0x4e 0x4e 0x4e 0x4e + 0x4e 0x4e 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f 0x4f + 0x4f 0x4f 0x50 0x50 0x50 0x50 0x50 0x50 0x50 0x50 0x51 0x51 0x51 0x51 0x51 0x51 + 0x51 0x51 0x52 0x52 0x52 0x52 0x52 0x52 0x52 0x52 0x53 0x53 0x53 0x53 0x53 0x53 + 0x53 0x53 0x54 0x54 0x54 0x54 0x54 0x54 0x54 0x54 0x55 0x55 0x55 0x55 0x55 0x55 + 0x55 0x55 0x56 0x56 0x56 0x56 0x56 0x56 0x56 0x56 0x57 0x57 0x57 0x57 0x57 0x57 + 0x57 0x57 0x58 0x58 0x58 0x58 0x58 0x58 0x58 0x58 0x59 0x59 0x59 0x59 0x59 0x59 + 0x59 0x59 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5b 0x5b 0x5b 0x5b 0x5b 0x5b + 0x5b 0x5b 0x5c 0x5c 0x5c 0x5c 0x5c 0x5c 0x5c 0x5c 0x5d 0x5d 0x5d 0x5d 0x5d 0x5d + 0x5d 0x5d 0x5e 0x5e 0x5e 0x5e 0x5e 0x5e 0x5e 0x5e 0x5f 0x5f 0x5f 0x5f 0x5f 0x5f + 0x5f 0x5f 0x60 0x60 0x60 0x60 0x61 0x61 0x61 0x61 0x62 0x62 0x62 0x62 0x63 0x63 + 0x63 0x63 0x64 0x64 0x64 0x64 0x65 0x65 0x65 0x65 0x66 0x66 0x66 0x66 0x67 0x67 + 0x67 0x67 0x68 0x68 0x68 0x68 0x69 0x69 0x69 0x69 0x6a 0x6a 0x6a 0x6a 0x6b 0x6b + 0x6b 0x6b 0x6c 0x6c 0x6c 0x6c 0x6d 0x6d 0x6d 0x6d 0x6e 0x6e 0x6e 0x6e 0x6f 0x6f + 0x6f 0x6f 0x70 0x70 0x71 0x71 0x72 0x72 0x73 0x73 0x74 0x74 0x75 0x75 0x76 0x76 + 0x77 0x77 0x78 0x78 0x79 0x79 0x7a 0x7a 0x7b 0x7b 0x7c 0x7c 0x7d 0x7d 0x7e 0x7e + 0xff 0xfe 0xfe 0xfd 0xfd 0xfc 0xfc 0xfb 0xfb 0xfa 0xfa 0xf9 0xf9 0xf8 0xf8 0xf7 + 0xf7 0xf6 0xf6 0xf5 0xf5 0xf4 0xf4 0xf3 0xf3 0xf2 0xf2 0xf1 0xf1 0xf0 0xf0 0xef + 0xef 0xef 0xef 0xee 0xee 0xee 0xee 0xed 0xed 0xed 0xed 0xec 0xec 0xec 0xec 0xeb + 0xeb 0xeb 0xeb 0xea 0xea 0xea 0xea 0xe9 0xe9 0xe9 0xe9 0xe8 0xe8 0xe8 0xe8 0xe7 + 0xe7 0xe7 0xe7 0xe6 0xe6 0xe6 0xe6 0xe5 0xe5 0xe5 0xe5 0xe4 0xe4 0xe4 0xe4 0xe3 + 0xe3 0xe3 0xe3 0xe2 0xe2 0xe2 0xe2 0xe1 0xe1 0xe1 0xe1 0xe0 0xe0 0xe0 0xe0 0xdf + 0xdf 0xdf 0xdf 0xdf 0xdf 0xdf 0xdf 0xde 0xde 0xde 0xde 0xde 0xde 0xde 0xde 0xdd + 0xdd 0xdd 0xdd 0xdd 0xdd 0xdd 0xdd 0xdc 0xdc 0xdc 0xdc 0xdc 0xdc 0xdc 0xdc 0xdb + 0xdb 0xdb 0xdb 0xdb 0xdb 0xdb 0xdb 0xda 0xda 0xda 0xda 0xda 0xda 0xda 0xda 0xd9 + 0xd9 0xd9 0xd9 0xd9 0xd9 0xd9 0xd9 0xd8 0xd8 0xd8 0xd8 0xd8 0xd8 0xd8 0xd8 0xd7 + 0xd7 0xd7 0xd7 0xd7 0xd7 0xd7 0xd7 0xd6 0xd6 0xd6 0xd6 0xd6 0xd6 0xd6 0xd6 0xd5 + 0xd5 0xd5 0xd5 0xd5 0xd5 0xd5 0xd5 0xd4 0xd4 0xd4 0xd4 0xd4 0xd4 0xd4 0xd4 0xd3 + 0xd3 0xd3 0xd3 0xd3 0xd3 0xd3 0xd3 0xd2 0xd2 0xd2 0xd2 0xd2 0xd2 0xd2 0xd2 0xd1 + 0xd1 0xd1 0xd1 0xd1 0xd1 0xd1 0xd1 0xd0 0xd0 0xd0 0xd0 0xd0 0xd0 0xd0 0xd0 0xcf + 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xcf 0xce + 0xce 0xce 0xce 0xce 0xce 0xce 0xce 0xce 0xce 0xce 0xce 0xce 0xce 0xce 0xce 0xcd + 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcd 0xcc + 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcc 0xcb + 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xcb 0xca + 0xca 0xca 0xca 0xca 0xca 0xca 0xca 0xca 0xca 0xca 0xca 0xca 0xca 0xca 0xca 0xc9 + 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc9 0xc8 + 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc8 0xc7 + 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc7 0xc6 + 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc6 0xc5 + 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc5 0xc4 + 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc4 0xc3 + 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc3 0xc2 + 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc2 0xc1 + 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc1 0xc0 + 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xc0 0xbf + 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf + 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbf 0xbe + 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe + 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbe 0xbd + 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd + 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbd 0xbc + 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc + 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbc 0xbb + 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb + 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xbb 0xba + 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba + 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xba 0xb9 + 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 + 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb9 0xb8 + 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 + 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb8 0xb7 + 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 + 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb7 0xb6 + 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 + 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb6 0xb5 + 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 + 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb5 0xb4 + 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 + 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb4 0xb3 + 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 + 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb3 0xb2 + 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 + 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb2 0xb1 + 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 + 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb1 0xb0 + 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 + 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xb0 0xaf + 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf + 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf + 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf + 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xaf 0xae + 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae + 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae + 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae + 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xae 0xad + 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad + 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad + 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad + 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xad 0xac + 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac + 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac + 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac + 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xac 0xab + 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab + 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab + 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab + 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xab 0xaa + 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa + 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa + 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa + 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xaa 0xa9 + 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 + 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 + 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 + 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa9 0xa8 + 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 + 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 + 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 + 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa8 0xa7 + 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 + 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 + 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 + 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa7 0xa6 + 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 + 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 + 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 + 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa6 0xa5 + 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 + 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 + 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 + 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa5 0xa4 + 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 + 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 + 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 + 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa4 0xa3 + 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 + 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 + 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 + 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa3 0xa2 + 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 + 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 + 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 + 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa2 0xa1 + 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 + 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 + 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 + 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa1 0xa0 + 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 + 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 + 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 + 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0xa0 0x9f + 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f + 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f + 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f + 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f + 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f + 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f + 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f + 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9f 0x9e + 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e + 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e + 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e + 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e + 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e + 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e + 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e + 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9e 0x9d + 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d + 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d + 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d + 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d + 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d + 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d + 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d + 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9d 0x9c + 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c + 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c + 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c + 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c + 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c + 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c + 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c + 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9c 0x9b + 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b + 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b + 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b + 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b + 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b + 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b + 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b + 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9b 0x9a + 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a + 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a + 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a + 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a + 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a + 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a + 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a + 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x9a 0x99 + 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 + 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 + 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 + 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 + 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 + 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 + 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 + 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x99 0x98 + 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 + 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 + 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 + 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 + 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 + 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 + 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 + 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x98 0x97 + 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 + 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 + 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 + 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 + 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 + 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 + 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 + 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x97 0x96 + 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 + 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 + 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 + 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 + 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 + 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 + 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 + 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x95 + 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 + 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 + 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 + 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 + 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 + 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 + 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 + 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x95 0x94 + 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 + 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 + 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 + 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 + 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 + 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 + 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 + 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x94 0x93 + 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 + 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 + 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 + 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 + 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 + 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 + 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 + 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x93 0x92 + 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 + 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 + 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 + 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 + 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 + 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 + 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 + 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x92 0x91 + 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 + 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 + 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 + 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 + 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 + 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 + 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 + 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x91 0x90 + 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 + 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 + 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 + 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 + 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 + 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 + 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 + 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f + 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8f 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e + 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8e 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d + 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8d 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c + 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8c 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b + 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8b 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a + 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x8a 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 + 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x89 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 + 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x88 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 + 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x87 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 + 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x86 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 + 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x85 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 + 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x84 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 + 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x83 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 + 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x82 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 + 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x81 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 + 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80 +) + +# base64 handling stuff +typeset -r base64chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + +function bytearraytobase64 +{ + nameref bytearray=$1 + typeset out="" + integer len + integer i i0 i1 i2 + + (( len=${#bytearray[*]}-1 )) + for (( i=0 ; i < len ; i+=3 )) ; do + (( i0=bytearray[i+0] , i1=bytearray[i+1] , i2=bytearray[i+2] )) + + out+="${base64chars:$(( i0 >> 2 )):1}" + out+="${base64chars:$(( ((i0 & 0x03) << 4) | ((i1 & 0xf0) >> 4) )):1}" + (( (i+1) < len )) && { out+="${base64chars:$(( ((i1 & 0x0f) << 2) | ((i2 & 0xc0) >> 6) )):1}" ; } || out+="=" + (( (i+2) < len )) && { out+="${base64chars:$(( i2 & 0x3f )):1}" ; } || out+="=" + done + + printf "%s" "${out}" + return 0 +} + + +# stack layout: +# stack.currpos = current position +# stack.data = nameref to integer array +function stack_init +{ + nameref stk=$1 + stk.currpos=0 + return 0 +} + +function stack_put_byte +{ + nameref stk=$1 + stk.data[stk.currpos++]=$(( $2 & 0xFF )) + return 0 +} + +function stack_put_uint16 +{ + integer val=$2 + stack_put_byte $1 $(( (val >> 8) & 0xFF )) + stack_put_byte $1 $(( val & 0xFF )) + return 0 +} + +# put an au(4) header on a stack variable +function audio_put_au_header +{ + nameref au=$1 + + # au_magic: magic number + stack_put_byte au $(('.')) + stack_put_byte au $(('s')) + stack_put_byte au $(('n')) + stack_put_byte au $(('d')) + # au_offset + stack_put_byte au 0x00 + stack_put_byte au 0x00 + stack_put_byte au 0x00 + stack_put_byte au 0x1C + # au_data_size (0xFFFFFFFF = AUDIO_AU_UNKNOWN_SIZE ((unsigned)(~0))) + stack_put_byte au 0xFF + stack_put_byte au 0xFF + stack_put_byte au 0xFF + stack_put_byte au 0xFF + # au_encoding + stack_put_byte au 0x00 + stack_put_byte au 0x00 + stack_put_byte au 0x00 + stack_put_byte au 1 + # au_sample_rate + stack_put_byte au 0x00 + stack_put_byte au 0x00 + stack_put_byte au 0x1f + stack_put_byte au 0x40 + # au_channels + stack_put_byte au 0x00 + stack_put_byte au 0x00 + stack_put_byte au 0x00 + stack_put_byte au 0x01 + # dummy + stack_put_byte au 0 + stack_put_byte au 0 + stack_put_byte au 0 + stack_put_byte au 0 + return 0 +} + +function print_piano_layout +{ + cat <<ENDOFTEXT +---------------------------------------------------- + | ##### ##### | ##### ##### ##### | + | ##### ##### | ##### ##### ##### | + | ##### ##### | ##### ##### ##### | + | #cis# #dis# | #fis# #gis# #ais# | + | #des# # es# | #ges# # as# # b # | + | \###/ \###/ | \###/ \###/ \###/ | + | | | | | | | | + | | | | | | | | + | c | d | e | f | g | a | h | + | | | | | | | | +/\-----/\-----/\-----/\-----/\-----/\-----/\-----/\- + +Keys: + R T U I O + D F G H J K L +ENDOFTEXT + return 0 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${shpiano_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename + +typeset progname="${ basename "${0}" ; }" + +typeset -r shpiano_usage=$'+ +[-?\n@(#)\$Id: shpiano (Roland Mainz) 2008-11-03 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?shpiano - simple audio demo] +[+DESCRIPTION?\bshpiano\b is a small demo application which converts + keyboard input into 8bit Mu-law audio samples which are + send to /dev/audio.] +[+SEE ALSO?\bksh93\b(1), \bau\b(4), \baudio\b(7i)] +' + +while getopts -a "${progname}" "${shpiano_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +float -r M_PI=3.14159265358979323846 + +float sample_rate=8000. +float duration=0.10 +float freq +float w # temporary "wave" value +integer i +integer audiofd # audio device file descriptor +typeset key +typeset audio=( typeset -i currpos=0 ; typeset -a -i data=( [0]=0 ) ) # stack object + +clear +print_piano_layout + +if [[ "${AUDIODEV}" == "" ]] ; then + AUDIODEV="/dev/audio" +fi +print -u2 -f $"Playing sound to device\n" "${AUDIODEV}" + +# open channel to audio device +redirect {audiofd}<>"${AUDIODEV}" +(( $? != 0 )) && fatal_error $"Couldn't open audio device." + +# build pause sample +stack_init audio +for ((i=0 ; i < ((sample_rate*duration)/2.) ; i++)) ; do + stack_put_byte audio 0 +done +typeset -b pause_sample=${ bytearraytobase64 audio.data ; } + +stack_init audio +audio_put_au_header audio +typeset -b au_header=${ bytearraytobase64 audio.data ; } + +# begin playing +printf "%B" au_header >&${audiofd} + +# warning: the math used here is so wrong that your head may +# explode when you continue reading this +while read -r -N 1 key?$'\r > ' ; do + if [[ ${key} == ~(E)($'\E'|'q'|'Q') ]] ; then + break # quit + fi + + printf "\r" + if [[ -z "${notes[key_${key}]}" ]] ; then + nameref curr_note=tones["p"] + (( freq=1.*(1./duration) )) + else + nameref curr_note="${notes[key_${key}].val}" + (( freq=curr_note.freq )) + fi + +# printf "note=%s sample_rate=%f, freq=%f\n" "${!curr_note}" sample_rate freq >&2 + + # Create sample data if they didn't exist yet and + # store them in the "tones" array + if ! ${curr_note.sample_set} ; then + stack_init audio + + for ((i=0 ; i < (sample_rate*duration) ; i++)) ; do + # first create the sinus wave... + (( w=sin( ((i*freq)/sample_rate) * (2.*M_PI)) )) + # ...scale it to 14bit signed PCM linear and... + (( w=8192.+w*8191. )) + # ...then convert it to 8bit ulaw + # ("audio_pcmulinear14bittoulaw8bit" is unsigned but we use + # "8192" above as starting point to do the "signed to unsigned" + # conversion) ... + stack_put_byte audio $(( audio_pcmulinear14bittoulaw8bit[int(w)] )) + done + + curr_note.sample=${ bytearraytobase64 audio.data ; } + curr_note.sample_set="true" + fi + + # output sample + { + printf "%B" curr_note.sample + printf "%B" pause_sample + } >&${audiofd} +done + +# close audio device +redirect {audiofd}<&- + +print -u2 $"# done." +exit 0 +#EOF. diff --git a/usr/src/lib/libshell/common/scripts/shtinyurl.sh b/usr/src/lib/libshell/common/scripts/shtinyurl.sh new file mode 100644 index 0000000000..1d3e70bf67 --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/shtinyurl.sh @@ -0,0 +1,202 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +# parse HTTP return code, cookies etc. +function parse_http_response +{ + nameref response="$1" + typeset h statuscode statusmsg i + + # we use '\r' as additional IFS to filter the final '\r' + IFS=$' \t\r' read -r h statuscode statusmsg # read HTTP/1.[01] <code> + [[ "$h" != ~(Eil)HTTP/.* ]] && { print -u2 -f $"%s: HTTP/ header missing\n" "$0" ; return 1 ; } + [[ "$statuscode" != ~(Elr)[0-9]* ]] && { print -u2 -f $"%s: invalid status code\n" "$0" ; return 1 ; } + response.statuscode="$statuscode" + response.statusmsg="$statusmsg" + + # skip remaining headers + while IFS='' read -r i ; do + [[ "$i" == $'\r' ]] && break + + # strip '\r' at the end + i="${i/~(Er)$'\r'/}" + + case "$i" in + ~(Eli)Content-Type:.*) + response.content_type="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Content-Length:[[:blank:]]*[0-9]*) + integer response.content_length="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Transfer-Encoding:.*) + response.transfer_encoding="${i/~(El).*:[[:blank:]]*/}" + ;; + esac + done + + return 0 +} + +function cat_http_body +{ + typeset emode="$1" + typeset hexchunksize="0" + integer chunksize=0 + + if [[ "${emode}" == "chunked" ]] ; then + while IFS=$'\r' read hexchunksize && + [[ "${hexchunksize}" == ~(Elri)[0-9abcdef]* ]] && + (( chunksize=16#${hexchunksize} )) && (( chunksize > 0 )) ; do + dd bs=1 count="${chunksize}" 2>/dev/null + done + else + cat + fi + + return 0 +} + +function request_tinyurl +{ + # site setup + typeset url_host="tinyurl.com" + typeset url_path="/api-create.php" + typeset url="http://${url_host}${url_path}" + integer netfd # http stream number + typeset inputurl="$1" + typeset -C httpresponse # http response + typeset request="" + + # we assume "inputurl" is a correctly encoded URL which doesn't + # require any further mangling + url_path+="?url=${inputurl}" + + request="GET ${url_path} HTTP/1.1\r\n" + request+="Host: ${url_host}\r\n" + request+="User-Agent: ${http_user_agent}\r\n" + request+="Connection: close\r\n" + + redirect {netfd}<>"/dev/tcp/${url_host}/80" + (( $? != 0 )) && { print -u2 -f $"%s: Couldn't open connection to %s.\n" "$0" "${url_host}" ; return 1 ; } + + # send http post + { + print -n -- "${request}\r\n" + } >&${netfd} + + # process reply + parse_http_response httpresponse <&${netfd} + response="${ cat_http_body "${httpresponse.transfer_encoding}" <&${netfd} ; }" + + # close connection + redirect {netfd}<&- + + if (( httpresponse.statuscode >= 200 && httpresponse.statuscode <= 299 )) ; then + print -r -- "${response}" + return 0 + else + print -u2 -f $"tinyurl response was (%s,%s):\n%s\n" "${httpresponse.statuscode}" "${httpresponse.statusmsg}" "${response}" + return 1 + fi + + # not reached +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${shtinyurl_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename +builtin cat +builtin date +builtin uname + +typeset progname="${ basename "${0}" ; }" + +# HTTP protocol client identifer +typeset -r http_user_agent="shtinyurl/ksh93 (2008-10-14; ${ uname -s -r -p ; })" + +typeset -r shtinyurl_usage=$'+ +[-?\n@(#)\$Id: shtinyurl (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?shtinyurl - create short tinyurl.com alias URL from long URL] +[+DESCRIPTION?\bshtinyurl\b is a small utility which passes a given URL + to the tinyurl.com service which creates short aliases in the + form of http://tinyurl.com/XXXXXXXX to redirect long URLs.] +[+?The first arg \burl\b describes a long URL which is transformed into + a tinyurl.com short alias.] + +url + +[+SEE ALSO?\bksh93\b(1), \brssread\b(1), \bshtwitter\b(1), http://www.tinyurl.com] +' + +while getopts -a "${progname}" "${shtinyurl_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# expecting at least one more argument +(($# >= 1)) || usage + +typeset url="$1" +shift + +request_tinyurl "${url}" +exit $? +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/shtwitter.sh b/usr/src/lib/libshell/common/scripts/shtwitter.sh new file mode 100644 index 0000000000..9eca33bcbd --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/shtwitter.sh @@ -0,0 +1,358 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +function encode_x_www_form_urlencoded +{ + nameref formdata=$1 + nameref content="formdata.content" + integer numformelements=${#formdata.form[*]} + integer i j + + content="" + + for (( i=0 ; i < numformelements ; i++ )) ; do + nameref element="formdata.form[${i}]" + typeset data="${element.data}" + integer datalen="${#data}" + typeset c + + [[ "$content" != "" ]] && content+="&" + + content+="${element.name}=" + + for ((j=0 ; j < datalen ; j++)) ; do + c="${data:j:1}" + case "$c" in + ' ') c="+" ;; + '!') c="%21" ;; + '*') c="%2A" ;; + "'") c="%27" ;; + '(') c="%28" ;; + ')') c="%29" ;; + ';') c="%3B" ;; + ':') c="%3A" ;; + '@') c="%40" ;; + '&') c="%26" ;; + '=') c="%3D" ;; + '+') c="%2B" ;; + '$') c="%24" ;; + ',') c="%2C" ;; + '/') c="%2F" ;; + '?') c="%3F" ;; + '%') c="%25" ;; + '#') c="%23" ;; + '[') c="%5B" ;; + '\') c="%5C" ;; # we need this to avoid the '\'-quoting hell + ']') c="%5D" ;; + *) ;; + esac + content+="$c" + done + done + + formdata.content_length=${#content} + + return 0 +} + +# parse HTTP return code, cookies etc. +function parse_http_response +{ + nameref response="$1" + typeset h statuscode statusmsg i + + # we use '\r' as additional IFS to filter the final '\r' + IFS=$' \t\r' read -r h statuscode statusmsg # read HTTP/1.[01] <code> + [[ "$h" != ~(Eil)HTTP/.* ]] && { print -u2 -f $"%s: HTTP/ header missing\n" "$0" ; return 1 ; } + [[ "$statuscode" != ~(Elr)[0-9]* ]] && { print -u2 -f $"%s: invalid status code\n" "$0" ; return 1 ; } + response.statuscode="$statuscode" + response.statusmsg="$statusmsg" + + # skip remaining headers + while IFS='' read -r i ; do + [[ "$i" == $'\r' ]] && break + + # strip '\r' at the end + i="${i/~(Er)$'\r'/}" + + case "$i" in + ~(Eli)Content-Type:.*) + response.content_type="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Content-Length:[[:blank:]]*[0-9]*) + integer response.content_length="${i/~(El).*:[[:blank:]]*/}" + ;; + ~(Eli)Transfer-Encoding:.*) + response.transfer_encoding="${i/~(El).*:[[:blank:]]*/}" + ;; + esac + done + + return 0 +} + +function cat_http_body +{ + typeset emode="$1" + typeset hexchunksize="0" + integer chunksize=0 + + if [[ "${emode}" == "chunked" ]] ; then + while IFS=$'\r' read hexchunksize && + [[ "${hexchunksize}" == ~(Elri)[0-9abcdef]* ]] && + (( chunksize=16#${hexchunksize} )) && (( chunksize > 0 )) ; do + dd bs=1 count="${chunksize}" 2>/dev/null + done + else + cat + fi + + return 0 +} + +function encode_http_basic_auth +{ + typeset user="$1" + typeset passwd="$2" + typeset s + integer s_len + typeset -b base64var + + # ksh93 binary variables use base64 encoding, the same as the + # HTTP basic authentification. We only have to read the + # plaintext user:passwd string into the binary variable "base64var" + # and then print this variable as ASCII. + s="${user}:${passwd}" + s_len="${#s}" + print -n "${s}" | read -N${s_len} base64var + + print -- "${base64var}" # print ASCII (base64) representation of binary var + + return 0 +} + +function put_twitter_message +{ + [[ "$SHTWITTER_USER" == "" ]] && { print -u2 -f $"%s: SHTWITTER_USER not set.\n" "$0" ; return 1 ; } + [[ "$SHTWITTER_PASSWD" == "" ]] && { print -u2 -f $"%s: SHTWITTER_PASSWD not set.\n" "$0" ; return 1 ; } + + (( $# != 1 )) && { print -u2 -f $"%s: Wrong number of arguments.\n" "$0" ; return 1 ; } + + # site setup + typeset url_host="twitter.com" + typeset url_path="/statuses/update.xml" + typeset url="http://${url_host}${url_path}" + integer netfd # http stream number + typeset msgtext="$1" + typeset -C httpresponse # http response + + # argument for "encode_x_www_form_urlencoded" + typeset urlform=( + # input + typeset -a form + # output + typeset content + integer content_length + ) + + typeset request="" + typeset content="" + + urlform.form=( + ( name="status" data="${msgtext}" ) + ) + + encode_x_www_form_urlencoded urlform + + content="${urlform.content}" + + request="POST ${url_path} HTTP/1.1\r\n" + request+="Host: ${url_host}\r\n" + request+="Authorization: Basic ${ encode_http_basic_auth "${SHTWITTER_USER}" "${SHTWITTER_PASSWD}" ; }\r\n" + request+="User-Agent: ${http_user_agent}\r\n" + request+="Connection: close\r\n" + request+="Content-Type: application/x-www-form-urlencoded\r\n" + request+="Content-Length: $(( urlform.content_length ))\r\n" + + redirect {netfd}<>"/dev/tcp/${url_host}/80" + (( $? != 0 )) && { print -u2 -f "%s: Could not open connection to %s\n." "$0" "${url_host}" ; return 1 ; } + + # send http post + { + print -n -- "${request}\r\n" + print -n -- "${content}\r\n" + } >&${netfd} + + # process reply + parse_http_response httpresponse <&${netfd} + response="${ cat_http_body "${httpresponse.transfer_encoding}" <&${netfd} ; }" + + # close connection + redirect {netfd}<&- + + printf $"twitter response was (%s,%s): %s\n" "${httpresponse.statuscode}" "${httpresponse.statusmsg}" "${response}" + + if (( httpresponse.statuscode >= 200 && httpresponse.statuscode <= 299 )) ; then + return 0 + else + return 1 + fi + + # not reached +} + +function verify_twitter_credentials +{ + [[ "$SHTWITTER_USER" == "" ]] && { print -u2 -f $"%s: SHTWITTER_USER not set.\n" "$0" ; return 1 ; } + [[ "$SHTWITTER_PASSWD" == "" ]] && { print -u2 -f $"%s: SHTWITTER_PASSWD not set.\n" "$0" ; return 1 ; } + + (( $# != 0 )) && { print -u2 -f $"%s: Wrong number of arguments.\n" "$0" ; return 1 ; } + + # site setup + typeset url_host="twitter.com" + typeset url_path="/account/verify_credentials.xml" + typeset url="http://${url_host}${url_path}" + integer netfd # http stream number + typeset -C httpresponse # http response + + typeset request="" + + request="POST ${url_path} HTTP/1.1\r\n" + request+="Host: ${url_host}\r\n" + request+="Authorization: Basic ${ encode_http_basic_auth "${SHTWITTER_USER}" "${SHTWITTER_PASSWD}" ; }\r\n" + request+="User-Agent: ${http_user_agent}\r\n" + request+="Connection: close\r\n" + request+="Content-Type: application/x-www-form-urlencoded\r\n" + request+="Content-Length: 0\r\n" # dummy + + redirect {netfd}<>"/dev/tcp/${url_host}/80" + (( $? != 0 )) && { print -u2 -f $"%s: Could not open connection to %s.\n" "$0" "${url_host}" ; return 1 ; } + + # send http post + { + print -n -- "${request}\r\n" + } >&${netfd} + + # process reply + parse_http_response httpresponse <&${netfd} + response="${ cat_http_body "${httpresponse.transfer_encoding}" <&${netfd} ; }" + + # close connection + redirect {netfd}<&- + + printf $"twitter response was (%s,%s): %s\n" "${httpresponse.statuscode}" "${httpresponse.statusmsg}" "${response}" + + if (( httpresponse.statuscode >= 200 && httpresponse.statuscode <= 299 )) ; then + return 0 + else + return 1 + fi + + # not reached +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${shtwitter_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename +builtin cat +builtin date +builtin uname + +typeset progname="${ basename "${0}" ; }" + +# HTTP protocol client identifer +typeset -r http_user_agent="shtwitter/ksh93 (2008-10-14; ${ uname -s -r -p ; })" + +typeset -r shtwitter_usage=$'+ +[-?\n@(#)\$Id: shtwitter (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?shtwitter - read/write text data to internet clipboards] +[+DESCRIPTION?\bshtwitter\b is a small utility which can read and write text + to the twitter.com microblogging site.] +[+?The first arg \bmethod\b describes one of the methods, "update" posts a + text message to the users twitter blog, returning the raw response + message from the twitter server.] +[+?The second arg \bstring\b contains the string data which should be + stored on twitter.com.] + +method [ string ] + +[+SEE ALSO?\bksh93\b(1), \brssread\b(1), \bshtinyurl\b(1), http://www.twitter.com] +' + +while getopts -a "${progname}" "${shtwitter_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# expecting at least one more argument +(($# >= 1)) || usage + +typeset method="$1" +shift + +case "${method}" in + update|blog) put_twitter_message "$@" ; exit $? ;; + verify_credentials) verify_twitter_credentials "$@" ; exit $? ;; + *) usage ;; +esac + +fatal_error $"not reached." +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/svcproptree1.sh b/usr/src/lib/libshell/common/scripts/svcproptree1.sh new file mode 100644 index 0000000000..e196fb413a --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/svcproptree1.sh @@ -0,0 +1,172 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + + +function svcproptovartree +{ + nameref tree=$1 + + typeset name + typeset servicename + typeset propname + + typeset datatype + + typeset -a fields + integer num_fields + integer i + + while IFS=' ' read -A fields ; do + num_fields=${#fields[*]} + + name="${fields[0]}" + datatype="${fields[1]}" + # parse service/property name + servicename="${name%~(Er):properties/.*}" + servicename="${servicename/~(El)svc:\//}" # strip "svc:/" + propname="${name#~(El).*:properties/}" + + if [[ "${tree["${servicename}"].properties[*]}" == "" ]] ; then + typeset -A tree["${servicename}"].properties=( ) + fi + + nameref node=tree["${servicename}"].properties["${propname}"] + + node=( + typeset datatype="${datatype}" + typeset valuelist="true" + typeset -a values + ) + + for (( i=2 ; i < num_fields ; i++ )) ; do + node.values+=( "${fields[i]}" ) + done + done + + return 0 +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${svcproptree1_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename +builtin cat +builtin date +builtin uname + +typeset progname="${ basename "${0}" ; }" + +typeset -r svcproptree1_usage=$'+ +[-?\n@(#)\$Id: svcproptree1 (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?svcproptree1 - SMF tree demo] +[+DESCRIPTION?\bsvcproptree1\b is a small ksh93 compound variable demo + which reads accepts a SMF service pattern name input file, + reads the matching service properties and converts them into an internal + variable tree representation and outputs it in the format + specified by viewmode (either "list", "namelist" or "tree")..] + +pattern viewmode + +[+SEE ALSO?\bksh93\b(1), \bsvcprop\b(1)] +' + +while getopts -a "${progname}" "${svcproptree1_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +typeset svcpattern="$1" +typeset viewmode="$2" + +if [[ "${viewmode}" != ~(Elr)(list|namelist|tree) ]] ; then + fatal_error $"Invalid view mode \"${viewmode}\"." +fi + +typeset svc=( + typeset -A proptree +) + +typeset s + +s="$(/usr/bin/svcprop -f "${svcpattern}")" || fatal_error $"svcprop failed with exit code $?." +print -u2 $"#loading completed." + +print -r -- "$s" | svcproptovartree svc.proptree +print -u2 $"#parsing completed." + +case "${viewmode}" in + list) + set | egrep "^svc.proptree\[" | fgrep -v ']=$' + ;; + namelist) + typeset + | egrep "^svc.proptree\[" + ;; + tree) + printf "%B\n" svc + ;; + *) + fatal_error $"Invalid view mode \"${viewmode}\"." + ;; +esac + +print -u2 $"#done." + +exit 0 +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/termclock.sh b/usr/src/lib/libshell/common/scripts/termclock.sh new file mode 100644 index 0000000000..edcadd4e10 --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/termclock.sh @@ -0,0 +1,312 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# termclock - a simple analog clock for terminals +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +# Make sure all math stuff runs in the "C" locale to avoid problems +# with alternative # radix point representations (e.g. ',' instead of +# '.' in de_DE.*-locales). This needs to be set _before_ any +# floating-point constants are defined in this script). +if [[ "${LC_ALL}" != "" ]] ; then + export \ + LC_MONETARY="${LC_ALL}" \ + LC_MESSAGES="${LC_ALL}" \ + LC_COLLATE="${LC_ALL}" \ + LC_CTYPE="${LC_ALL}" + unset LC_ALL +fi +export LC_NUMERIC=C + +function fatal_error +{ + print -u2 "${progname}: $*" + exit 1 +} + +# cache tput values (to avoid |fork()|'ing a "tput" child every second) +function tput_cup +{ + # static variable as cache for "tput_cup" + typeset -S -A tput_cup_cache + + integer y="$1" x="$2" + nameref c="tput_cup_cache[\"${y}_${x}\"]" + + if [[ "$c" == "" ]] ; then + # fast path for known terminal types + if [[ ${TERM} == ~(Elr)(vt100|vt220|xterm|xterm-color|dtterm) ]] ; then + c="${ printf "\E[%d;%dH" y+1 x+1 ; }" + else + c="${ tput cup $y $x ; }" + fi + fi + + print -r -n -- "$c" + return 0 +} + +# Get terminal size and put values into a compound variable with the integer +# members "columns" and "lines" +function get_term_size +{ + nameref rect=$1 + + rect.columns=${ tput cols ; } || return 1 + rect.lines=${ tput lines ; } || return 1 + + return 0 +} + +function draw_clock +{ + float angle a + float x y + integer i=1 + + for(( angle=0.0 ; angle < 360. ; angle+=6 )) ; do + (( + a=angle/360.*(2*M_PI) , + + x=clock.len_x*cos(a) , + y=clock.len_y*sin(a) + )) + + tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x )) + + # add "mark" every 30 degrees + if (( int(angle)%30 == 0 )) ; then + print -r -n "$(((++i)%12+1))" + else + print -r -n "x" + fi + done + return 0 +} + +function draw_hand +{ + float angle="$1" a + typeset ch="$2" + float length="$3" + float x y + + (( a=angle/360.*(2*M_PI) )) + + for (( s=0.0 ; s < 10. ; s+=0.5 )) ; do + (( + x=(clock.len_x*(s/10.)*(length/100.))*cos(a) , + y=(clock.len_y*(s/10.)*(length/100.))*sin(a) + )) + + tput_cup $(( y+clock.middle_y )) $(( x+clock.middle_x )) + print -r -n -- "${ch}" + done + return 0 +} + +function draw_clock_hand +{ + nameref hand=$1 + draw_hand $(( 360.*(hand.val/hand.scale)-90. )) "${hand.ch}" ${hand.length} + return 0 +} + +function clear_clock_hand +{ + nameref hand=$1 + draw_hand $(( 360.*(hand.val/hand.scale)-90. )) " " ${hand.length} + return 0 +} + +function main_loop +{ + typeset c + + # note: we can't use subshells when writing to the double-buffer file because this + # will render the tput value cache useless + while true ; do + if ${init_screen} ; then + init_screen="false" + + get_term_size termsize || fatal_error $"Couldn't get terminal size." + + (( + clock.middle_x=termsize.columns/2.-.5 , + clock.middle_y=termsize.lines/2.-.5 , + clock.len_x=termsize.columns/2-2 , + clock.len_y=termsize.lines/2-2 , + )) + + { + clear + draw_clock + } >&6 + fi + + { + (( ${ date +"hours.val=%H , minutes.val=%M , seconds.val=%S" ; } )) + + # small trick to get a smooth "analog" flair + (( + hours.val+=minutes.val/60. , + minutes.val+=seconds.val/60. + )) + + draw_clock_hand seconds + draw_clock_hand minutes + draw_clock_hand hours + + # move cursor to home position + tput_cup 0 0 + } >&6 + + 6<#((0)) + cat <&6 + + redirect 6<&- ; rm -f "${scratchfile}" ; redirect 6<>"${scratchfile}" + + c="" ; read -r -t ${update_interval} -N 1 c + if [[ "$c" != "" ]] ; then + case "$c" in + ~(Fi)q | $'\E') return 0 ;; + esac + fi + + { + clear_clock_hand hours + clear_clock_hand minutes + clear_clock_hand seconds + } >&6 + done +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${termclock_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename +builtin cat +builtin date +builtin rm + +typeset progname="${ basename "${0}" ; }" + +float -r M_PI=3.14159265358979323846 + +# terminal size rect +typeset -C termsize=( + integer columns=-1 + integer lines=-1 +) + +typeset init_screen="true" + +typeset -C clock=( + float middle_x + float middle_y + integer len_x + integer len_y +) + + +# set clock properties +typeset -C seconds=( + float val + typeset ch + float scale + integer length ) +typeset -C minutes=( + float val + typeset ch + float scale + integer length ) +typeset -C hours=( + float val + typeset ch + float scale + integer length ) + +seconds.length=90 seconds.scale=60 seconds.ch=$"s" +minutes.length=75 minutes.scale=60 minutes.ch=$"m" +hours.length=50 hours.scale=12 hours.ch=$"h" + +float update_interval=0.9 + +typeset -r termclock_usage=$'+ +[-?\n@(#)\$Id: termclock (Roland Mainz) 2008-11-04 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[-author?David Korn <dgk@research.att.com>] +[+NAME?termclock - analog clock for terminals] +[+DESCRIPTION?\btermclock\b is an analog clock for terminals. + The termclock program displays the time in analog or digital + form. The time is continuously updated at a frequency which + may be specified by the user.] +[u:update?Update interval (defaults to 0.9 seconds).]:[interval] +[+SEE ALSO?\bksh93\b(1), \bxclock\b(1)] +' + +while getopts -a "${progname}" "${termclock_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + u) update_interval=${OPTARG} ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +# prechecks +which tput >/dev/null || fatal_error $"tput not found." +which mktemp >/dev/null || fatal_error $"mktemp not found." +(( update_interval >= 0. && update_interval <= 7200. )) || fatal_error $"invalid update_interval value." + +# create temporary file for double-buffering and register an EXIT trap +# to remove this file when the shell interpreter exits +scratchfile="${ mktemp "/tmp/termclock.ppid${PPID}_pid$$.XXXXXX" ; }" +[[ "${scratchfile}" != "" ]] || fatal_error $"Could not create temporary file name." +trap 'rm -f "${scratchfile}"' EXIT +rm -f "${scratchfile}" ; redirect 6<>"${scratchfile}" || fatal_error $"Could not create temporary file." + +# register trap to handle window size changes +trap 'init_screen="true"' WINCH + +main_loop + +# exiting - put cursor below clock +tput_cup $((termsize.lines-2)) 0 + +exit 0 +# EOF. diff --git a/usr/src/lib/libshell/common/scripts/test_net_sctp.sh b/usr/src/lib/libshell/common/scripts/test_net_sctp.sh new file mode 100644 index 0000000000..a17aaba62f --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/test_net_sctp.sh @@ -0,0 +1,65 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# test_net_sctp - a simple ksh93 SCTP demo +# + +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +set -o xtrace +set -o errexit + +# declare variables +integer netfd +typeset request + +# print intro +print "# testing SCTP support" +print "# (via fetching the main page of http://www.sctp.org/ via SCTP)" + +# open sctp stream and print it's number +redirect {netfd}<>/dev/sctp/www.sctp.org/80 +print "sctp fd=${netfd}" + +# send HTTP request +request="GET / HTTP/1.1\r\n" +request+="Host: www.sctp.org\r\n" +request+="User-Agent: ksh93/test_net_sctp (2008-10-14; $(uname -s -r -p))\r\n" +request+="Connection: close\r\n" +print -u${netfd} -n -- "${request}\r\n" + +# print response to stdout +cat <&${netfd} + +# close connection +redirect {netfd}<&- + +print "#done" + +#EOF. diff --git a/usr/src/lib/libshell/common/scripts/xmldocumenttree1.sh b/usr/src/lib/libshell/common/scripts/xmldocumenttree1.sh new file mode 100644 index 0000000000..ab6517f372 --- /dev/null +++ b/usr/src/lib/libshell/common/scripts/xmldocumenttree1.sh @@ -0,0 +1,356 @@ +#!/usr/bin/ksh93 + +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant +export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin + +function fatal_error +{ + print -u 2 "${progname}: $*" + exit 1 +} + +function attrstrtoattrarray +{ +#set -o xtrace + typeset s="$1" + nameref aa=$2 # attribute array + integer aa_count=0 + integer aa_count=0 + typeset nextattr + integer currattrlen=0 + typeset tagstr + typeset tagval + + while (( ${#s} > 0 )) ; do + # skip whitespaces + while [[ "${s:currattrlen:1}" == ~(E)[[:blank:][:space:]] ]] ; do + (( currattrlen++ )) + done + s="${s:currattrlen:${#s}}" + + # anything left ? + (( ${#s} == 0 )) && break + + # Pattern tests: + #x="foo=bar huz=123" ; print "${x##~(E)[[:alnum:]_-:]*=[^[:blank:]\"]*}" + #x='foo="ba=r o" huz=123' ; print "${x##~(E)[[:alnum:]_-:]*=\"[^\"]*\"}" + #x="foo='ba=r o' huz=123" ; print "${x##~(E)[[:alnum:]_-:]*=\'[^\"]*\'}" + #x="foox huz=123" ; print "${x##~(E)[[:alnum:]_-:]*}" + # All pattern combined via eregex (w|x|y|z): + #x='foo="bar=o" huz=123' ; print "${x##~(E)([[:alnum:]_-:]*=[^[:blank:]\"]*|[[:alnum:]_-:]*=\"[^\"]*\"|[[:alnum:]_-:]*=\'[^\"]*\')}" + nextattr="${s##~(E)([[:alnum:]_-:]*=[^[:blank:]\"]*|[[:alnum:]_-:]*=\"[^\"]*\"|[[:alnum:]_-:]*=\'[^\"]*\'|[[:alnum:]_-:]*)}" + currattrlen=$(( ${#s} - ${#nextattr})) + + # add entry + tagstr="${s:0:currattrlen}" + if [[ "${tagstr}" == *=* ]] ; then + # normal case: attribute with value + + tagval="${tagstr#*=}" + + # strip quotes ('' or "") + if [[ "${tagval}" == ~(Elr)(\'.*\'|\".*\") ]] ; then + tagval="${tagval:1:${#tagval}-2}" + fi + + aa[${aa_count}]=( name="${tagstr%%=*}" value="${tagval}" ) + else + # special case for HTML where you have something like <foo baz> + aa[${aa_count}]=( name="${tagstr}" ) + fi + (( aa_count++ )) + (( aa_count > 1000 )) && fatal_error "$0: aa_count too large" # assert + done +} + + +function handle_document +{ +#set -o xtrace + nameref callbacks=${1} + typeset tag_type="${2}" + typeset tag_value="${3}" + typeset tag_attributes="${4}" + nameref doc=${callbacks["arg_tree"]} + nameref nodepath="${stack.items[stack.pos]}" + nameref nodesnum="${stack.items[stack.pos]}num" + + case "${tag_type}" in + tag_begin) + nodepath[${nodesnum}]+=( + typeset tagtype="element" + typeset tagname="${tag_value}" + typeset -A tagattributes=( ) + typeset -A nodes=( ) + integer nodesnum=0 + ) + + # fill attributes + if [[ "${tag_attributes}" != "" ]] ; then + attrstrtoattrarray "${tag_attributes}" "nodepath[${nodesnum}].tagattributes" + fi + + (( stack.pos++ )) + stack.items[stack.pos]="${stack.items[stack.pos-1]}[${nodesnum}].nodes" + (( nodesnum++ )) + ;; + tag_end) + (( stack.pos-- )) + ;; + tag_text) + nodepath[${nodesnum}]+=( + typeset tagtype="text" + typeset tagvalue="${tag_value}" + ) + (( nodesnum++ )) + ;; + tag_comment) + nodepath[${nodesnum}]+=( + typeset tagtype="comment" + typeset tagvalue="${tag_value}" + ) + (( nodesnum++ )) + ;; + document_start) + ;; + document_end) + ;; + esac + +# print "xmltok: '${tag_type}' = '${tag_value}'" +} + +function xml_tok +{ + typeset buf="" + typeset namebuf="" + typeset attrbuf="" + typeset c="" + typeset isendtag # bool: true/false + typeset issingletag # bool: true/false (used for tags like "<br />") + nameref callbacks=${1} + + [[ ! -z "${callbacks["document_start"]}" ]] && ${callbacks["document_start"]} "${1}" "document_start" + + while IFS='' read -r -N 1 c ; do + isendtag=false + + if [[ "$c" == "<" ]] ; then + # flush any text content + if [[ "$buf" != "" ]] ; then + [[ ! -z "${callbacks["tag_text"]}" ]] && ${callbacks["tag_text"]} "${1}" "tag_text" "$buf" + buf="" + fi + + IFS='' read -r -N 1 c + if [[ "$c" == "/" ]] ; then + isendtag=true + else + buf="$c" + fi + IFS='' read -r -d '>' c + buf+="$c" + + # handle comments + if [[ "$buf" == ~(El)!-- ]] ; then + # did we read the comment completely ? + if [[ "$buf" != ~(Elr)!--.*-- ]] ; then + buf+=">" + while [[ "$buf" != ~(Elr)!--.*-- ]] ; do + IFS='' read -r -N 1 c || break + buf+="$c" + done + fi + + [[ ! -z "${callbacks["tag_comment"]}" ]] && ${callbacks["tag_comment"]} "${1}" "tag_comment" "${buf:3:${#buf}-5}" + buf="" + continue + fi + + # check if the tag starts and ends at the same time (like "<br />") + if [[ "${buf}" == ~(Er).*/ ]] ; then + issingletag=true + buf="${buf%*/}" + else + issingletag=false + fi + + # check if the tag has attributes (e.g. space after name) + if [[ "$buf" == ~(E)[[:space:][:blank:]] ]] ; then + namebuf="${buf%%~(E)[[:space:][:blank:]].*}" + attrbuf="${buf#~(E).*[[:space:][:blank:]]}" + else + namebuf="$buf" + attrbuf="" + fi + + if ${isendtag} ; then + [[ ! -z "${callbacks["tag_end"]}" ]] && ${callbacks["tag_end"]} "${1}" "tag_end" "$namebuf" + else + [[ ! -z "${callbacks["tag_begin"]}" ]] && ${callbacks["tag_begin"]} "${1}" "tag_begin" "$namebuf" "$attrbuf" + + # handle tags like <br/> (which are start- and end-tag in one piece) + if ${issingletag} ; then + [[ ! -z "${callbacks["tag_end"]}" ]] && ${callbacks["tag_end"]} "${1}" "tag_end" "$namebuf" + fi + fi + buf="" + else + buf+="$c" + fi + done + + [[ ! -z "${callbacks["document_end"]}" ]] && ${callbacks["document_end"]} "${1}" "document_end" "exit_success" + + print # final newline to make filters like "sed" happy +} + +function print_sample1_xml +{ +cat <<EOF +<br /> +<score-partwise instrument="flute1"> + <identification> + <kaiman>nocrocodile</kaiman> + </identification> + <!-- a comment --> + <partlist> + <foo>myfootext</foo> + <bar>mybartext</bar> + <snap /> + <!-- another + comment --> + <ttt>myttttext</ttt> + </partlist> +</score-partwise> +EOF +} + +function usage +{ + OPTIND=0 + getopts -a "${progname}" "${xmldocumenttree1_usage}" OPT '-?' + exit 2 +} + +# program start +builtin basename +builtin cat +builtin date +builtin uname + +typeset progname="${ basename "${0}" ; }" + +typeset -r xmldocumenttree1_usage=$'+ +[-?\n@(#)\$Id: xmldocumenttree1 (Roland Mainz) 2008-10-14 \$\n] +[-author?Roland Mainz <roland.mainz@nrubsig.org>] +[+NAME?xmldocumenttree1 - XML tree demo] +[+DESCRIPTION?\bxmldocumenttree\b is a small ksh93 compound variable demo + which reads a XML input file, converts it into an internal + variable tree representation and outputs it in the format + specified by viewmode (either "list", "namelist" or "tree").] + +file viewmode + +[+SEE ALSO?\bksh93\b(1)] +' + +while getopts -a "${progname}" "${xmldocumenttree1_usage}" OPT ; do +# printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|" + case ${OPT} in + *) usage ;; + esac +done +shift $((OPTIND-1)) + +typeset xmlfile="$1" +typeset viewmode="$2" + +if [[ "${xmlfile}" == "" ]] ; then + fatal_error $"No file given." +fi + +if [[ "${viewmode}" != ~(Elr)(list|namelist|tree) ]] ; then + fatal_error $"Invalid view mode \"${viewmode}\"." +fi + +typeset -C xdoc +typeset -A xdoc.nodes +integer xdoc.nodesnum=0 + +typeset -C stack +typeset -a stack.items=( [0]="doc.nodes" ) +integer stack.pos=0 + +# setup callbacks for xml_tok +typeset -A document_cb # callbacks for xml_tok +document_cb["document_start"]="handle_document" +document_cb["document_end"]="handle_document" +document_cb["tag_begin"]="handle_document" +document_cb["tag_end"]="handle_document" +document_cb["tag_text"]="handle_document" +document_cb["tag_comment"]="handle_document" +# argument for "handle_document" +document_cb["arg_tree"]="xdoc" + + +if [[ "${xmlfile}" == "#sample1" ]] ; then + print_sample1_xml | xml_tok document_cb +elif [[ "${xmlfile}" == "#sample2" ]] ; then + /usr/sfw/bin/wget \ + --user-agent='ksh93_xmldocumenttree' \ + --output-document=- \ + 'http://www.google.com/custom?q=gummi+bears' | + /usr/bin/iconv -f "ISO8859-1" | + xml_tok document_cb +else + cat "${xmlfile}" | xml_tok document_cb +fi + +print -u2 "#parsing completed." + +case "${viewmode}" in + list) + set | egrep "xdoc.*(tagname|tagtype|tagval|tagattributes)" | fgrep -v ']=$' + ;; + namelist) + typeset + | egrep "xdoc.*(tagname|tagtype|tagval|tagattributes)" + ;; + tree) + print -- "${xdoc}" + ;; + *) + fatal_error $"Invalid view mode \"${viewmode}\"." + ;; +esac + +print -u2 "#done." + +exit 0 +# EOF. diff --git a/usr/src/lib/libshell/common/sh.1 b/usr/src/lib/libshell/common/sh.1 index 61393046b6..4ebbb41a31 100644 --- a/usr/src/lib/libshell/common/sh.1 +++ b/usr/src/lib/libshell/common/sh.1 @@ -23,8 +23,8 @@ sh, rsh, pfsh \- shell, the .\} .if \nZ=1 \{\ -.\} ksh, rksh, pfksh \- KornShell, a +.\} .if \nZ=2 \{\ ksh93, rksh93, pfksh93 \- KornShell, a .\} @@ -32,8 +32,8 @@ standard/restricted command and programming language .SH SYNOPSIS .if \nZ=0 \{\ .B sh -.if \nZ=1 \{\ .\} +.if \nZ=1 \{\ .B ksh .\} .if \nZ=2 \{\ @@ -296,13 +296,13 @@ of the first of a .I list\^ that is a simple command not beginning -with a redirection, and not occuring within a +with a redirection, and not occurring within a .BR while , .BR until , or .B if .IR list , -can be prededed by a semicolon. +can be preceded by a semicolon. This semicolon is ignored unless the .B showme @@ -628,10 +628,12 @@ and are not quoted: One or more variable assignments can start a simple command or can be arguments to the .BR typeset , +.BR enum , .BR export , or .B readonly -special built-in commands. +special built-in commands as well as +to other declaration commands created as types. The syntax for an \f2assignment\^\fP is of the form: .TP .PD 0 @@ -672,9 +674,19 @@ a compound variable allowing subsequence child elements to be defined. Nested variable assignment. Multiple assignments can be specified by separating each of them with a \f3;\fP. The previous value is unset before the assignment. +Other declaration commands such as +.BR readonly, +.BR enum , +and +other declaration commands can be used in place of +.BR typeset . +.TP +\f3\|.\fP \f2filename\^\fP +Include the assignment commands contained in +.IR filename . .PD .RE -.P +.PP In addition, a \f3+=\fP can be used in place of the \f3=\fP to signify adding to or appending to the previous value. When \f3+=\fP is applied to an arithmetic type, \f2word\^\fP @@ -683,6 +695,12 @@ When applied to a string variable, the value defined by \f2word\^\fP is appended to the value. For compound assignments, the previous value is not unset and the new values are appended to the current ones provided that the types are compatible. +.PP +The right hand side of a variable assignment undergoes all the expansion +list below except word splitting, brace expansion, and file name generation. +When the left hand side is an assignment is a compound variable and +the right hand is the name of a compound variable, the coumpound variable +on the right will be copied or appended to the compound variable on the left. .SS Comments. .PD 0 A word beginning with @@ -839,15 +857,20 @@ also terminates a .B \(ap login name. .SS Command Substitution. -The standard output from a command enclosed in +The standard output from a command list enclosed in parentheses preceded by a dollar sign ( -.B $(\|) -) -or a pair of grave accents (\^\f3\*`\^\*`\fP\^) +\f3$(\fP\f2list\^\fP\f3)\fP +), +or in a brace group preceded by a dollar sign ( +\f3${ \fP\f2list\^\fP\f3;}\fP +), or in a pair of grave accents (\^\f3\*`\^\*`\fP\^) may be used as part or all of a word; trailing new-lines are removed. -In the second (obsolete) form, the string between the quotes is processed +In the second case, the \f3{\fP and \f3}\fP are treated as a reserved words +so that \f3{\fP must be followed by a \f2blank\^\fP and \f3}\fP must +appear at the beginning of the line or follow a \f3;\fP. +In the third (obsolete) form, the string between the quotes is processed for special quoting characters before the command is executed (see .I Quoting\^ below). @@ -859,6 +882,11 @@ The command substitution \^\f3$(\^\fP\f2n\^\fP\f3<#\^)\fP will expand to the current byte offset for file descriptor .IR n . +Except for the second form, the command list is run in a subshell so that no +side effects are possible. +For the second form, the final +.B } +will be recognized as a reserved word after any token. .SS Arithmetic Substitution. An arithmetic expression enclosed in double parentheses preceded by a dollar sign ( @@ -978,11 +1006,18 @@ between a and a .BR ] . To assign values to an indexed array, use +\f2vname\fP\f3=(\fP\f2value\fP .\|.\|.\f3)\fP or \f3set \-A\fP \f2vname\fP \f2value\fP .\|.\|. . -The value of all +The value of all non-negative subscripts must be in the range of -0 through 1,048,575. +0 through 4,194,303. +A negative subscript is treated as an offset from the maximum +current index +1 so that -1 refers to the last element. +Indexed arrays can be declared with the +.B \-a +option to +.BR typeset. Indexed arrays need not be declared. Any reference to a variable with a valid subscript is @@ -1065,10 +1100,11 @@ to be references and assignments to the variable whose name has been passed to the function. .sp .5 .PP -If either of the floating point attributes, +If any of the floating point attributes, .BR \-E , -or .BR \-F , +or +.BR \-X , or the integer attribute, .BR \-i , is set for @@ -1107,8 +1143,10 @@ The braces are required when .I parameter\^ is followed by a letter, digit, or underscore that is not to be interpreted as part of its name, -when the variable name contains a \fB\s+2.\s-2\fP, -or when a variable is subscripted. +when the variable name contains a \fB\s+2.\s-2\fP. +The braces are also required when a variable is subscripted +unless it is part of an Arithmetic Expression +or a Conditional Expression. If .I parameter\^ is one or more digits then it is a positional parameter. @@ -1129,12 +1167,22 @@ If an array .I vname\^ with subscript .B \(** -or -.B @ +.BR @ , +or of the form +.I sub1\^ +.B .. +.IR sub2 . is used, then the value for each of the -elements +elements between +.I sub1\^ +and +.I sub2\^ +inclusive (or all elments for +.B \(** +and +.BR @ ) is substituted, separated by the first character of @@ -1163,6 +1211,13 @@ The number of elements in the array .I vname\^ is substituted. .TP +.PD 0 +\f3${@\fP\f2vname\^\fP\f3}\fP +Expands to the type name (See +.I "Type Variables"\^ +below) or attributes of the variable referred to by +.IR vname . +.TP \f3${!\fP\f2vname\^\fP\f3}\fP Expands to the name of the variable referred to by .IR vname . @@ -1176,9 +1231,12 @@ is a name reference. Expands to name of the subscript unless .I subscript\^ is -.B * -or +.BR * , .BR @ . +or of the form +.I sub1\^ +.B .. +.IR sub2 . When .I subscript\^ is @@ -1194,6 +1252,19 @@ is same as above, except that when used in double quotes, each array subscript yields a separate argument. +When +.I subscript\^ +is of the form +.I sub1\^ +.B .. +.I sub2\^ +it expands +to the list of subscripts between +.I sub1\^ +and +.I sub2\^ +inclusive using the same quoting rules as +.BR @ . .TP \f3${!\fP\f2prefix\^\fP\f3*}\fP Expands to the names of the variables whose names begin with @@ -1449,6 +1520,19 @@ This parameter is also used to hold the name of the matching .B .SM MAIL file when checking for mail. +While defining a compound variable or a type, +.B _ +is initialized as a reference to the compound variable or type. +When a discipline function is invoked, +.B _ +is initialized as a reference to the variable associated with +the call to this function. +Finally when +.B _ +is used as the name of the first variable of a type definition, +the new type is derived from the type of the first variable (See +.I "Type Variables"\^ +below.). .TP .B ! The process number of the last background command invoked or @@ -1516,6 +1600,15 @@ The pathname of the file than contains the current command. .B .sh.fun The name of the current function that is being executed. .TP +.B .sh.level +Set to the current function depth. This can be changed +inside a DEBUG trap and will set the context to the specified +level. +.TP +.B .sh.lineno +Set during a DEBUG trap to the line number for the caller of +each function. +.TP .B .sh.match An indexed array which stores the most recent match and sub-pattern matches after conditional pattern matches that match and after @@ -1559,6 +1652,11 @@ discipline function is invoked. Set to a value that identifies the version of this shell. .TP .B +.SM KSH_VERSION +A name reference to +.BR .sh.version . +.TP +.B .SM LINENO The current line number within the script or function being executed. @@ -1616,6 +1714,16 @@ seconds since shell invocation is returned. If this variable is assigned a value, then the value returned upon reference will be the value that was assigned plus the number of seconds since the assignment. +.TP +.SM +.B SHLVL +An integer variable the is incremented each time the shell +is invoked and is exported. +If +.SM +.B SHLVL +is not in the environment when the shell is invoked, it is set +to 1. .PD .RE .PP @@ -1663,7 +1771,7 @@ are performed on the value to generate the pathname of the script that will be executed when the shell -is invoked +is invoked interactively (see .I Invocation\^ below). @@ -1673,6 +1781,15 @@ and .B function definitions. The default value is \fB$HOME/.kshrc\fP. +On systems that support a system wide \fB/etc/ksh.kshrc\fP initialization file, +if the filename generated by the expansion of +.SM +.B ENV +begins with +.B /./ +or +.B .\^/.\^/ +the system wide initialization file will not be executed. .TP .B .SM FCEDIT @@ -1706,7 +1823,7 @@ Unlike .SM .BR PATH , the current directory must be represented -explictily by +explicitly by .B . rather than by adjacent .B : @@ -2010,7 +2127,7 @@ The CPU percentage, computed as (U + S) / R. .PD .RE .IP -The braces denote optional portions. +The brackets denote optional portions. The optional \fIp\fP is a digit specifying the \fIprecision\fP, the number of fractional digits after a decimal point. A value of 0 causes no decimal point or fraction to be output. @@ -2313,7 +2430,7 @@ will match all files and zero or more directories and subdirectories. If followed by a .B / -than only directories and subdirectories will match. +then only directories and subdirectories will match. .TP .B ? Matches any single character. @@ -2417,7 +2534,7 @@ compound patterns a \f3\-\fP can be inserted in front of the \f3(\fP to cause the shortest match to the specified \f2pattern-list\^\fP to be used. .PP -When \f2pattern-list\^\fP is contained within parenthesis, +When \f2pattern-list\^\fP is contained within parentheses, the backslash character \f3\e\fP is treated specially even when inside a character class. All ANSI-C character escapes are recognized and match the specified character. In addition @@ -2511,9 +2628,9 @@ the \f2n\fP-th. sub-pattern, matches the same string as the sub-pattern itself. .PP Finally a pattern can contain sub-patterns of the form -\f3\(ap(\fP\f2options\^\fP\f3:\fP\f2pattern-list\^\fP\f3)\fP. +\f3\(ap(\fP\f2options\^\fP\f3:\fP\f2pattern-list\^\fP\f3)\fP, where either \f2options\^\fP or \f3:\fP\f2pattern-list\^\fP -can be omitted. Unlike, the other compound patterns, +can be omitted. Unlike the other compound patterns, these sub-patterns are not counted in the numbered sub-patterns. If \f2options\^\fP is present, it can consist of one or more of the following: @@ -2581,26 +2698,6 @@ listed earlier (see .I Definitions\^ above) has a special meaning to the shell -.TP -.B i -Treat the match as case insensitive. -.TP -.B g -File the longest match (greedy). This is the default. -.PD -.RE -If both \f2options\^\fP and \f3:\fP\f2pattern-list\^\fP -are specified, then the options apply only to \f2pattern-list\^\fP. -Otherwise, these options remain in effect until they are disabled -by a subsequent \f3\(ap(\fP\f2...\^\fP\f3)\fP or at the end of -the sub-pattern containing \f3\(ap(\fP\f2...\^\fP\f3)\fP. -.SS Quoting. -Each of the -.I metacharacters\^ -listed earlier (see -.I Definitions\^ -above) -has a special meaning to the shell and causes termination of a word unless quoted. A character may be .I quoted\^ @@ -2745,7 +2842,7 @@ that apply to floating point quantities can be used. In addition, the operator .B ** can be used for exponentiation. -It has higher precedence than multiplication as is left associative. +It has higher precedence than multiplication and is left associative. In addition, when the value of an arithmetic variable or sub-expression can be represented as a long integer, all C language integer arithmetic operations can be performed. @@ -2760,15 +2857,16 @@ can be used within an arithmetic expression: .if t .RS .B .if n abs acos acosh asin asinh atan atan2 atanh cbrt copysign cos cosh erf erfc exp exp2 expm1 fabs fdim finite floor fma fmax fmod hypot ilogb int isinf isnan lgamma log log2 logb nearbyint nextafter nexttoward pow remainder rint round sin sinh sqrt tan tanh tgamma trunc -.if t abs acos acosh asin asinh atan atan2 atanh cbrt copysign cos cosh erf erfc exp exp2 expm1 fabs fdim finite floor fma fmax fmod hypot ilogb int isinf isnan lgamma log log2 logb nearbyint nextafter nextroward pow rint round sin sinh sqrt tan tanh tgamma trunc +.if t abs acos acosh asin asinh atan atan2 atanh cbrt copysign cos cosh erf erfc exp exp2 expm1 fabs fdim finite floor fma fmax fmod hypot ilogb int isinf isnan lgamma log log2 logb nearbyint nextafter nexttoward pow rint round sin sinh sqrt tan tanh tgamma trunc .if t .RE .PP An internal representation of a .I variable\^ as a double precision floating point can be specified with the -\f3\-E\fP \*(OK\f2n\^\fP\*(CK +\f3\-E\fP \*(OK\f2n\^\fP\*(CK, +\f3\-F\fP \*(OK\f2n\^\fP\*(CK, or -\f3\-F\fP \*(OK\f2n\^\fP\*(CK +\f3\-X\fP \*(OK\f2n\^\fP\*(CK option of the .B typeset special built-in command. @@ -2783,9 +2881,14 @@ The .B \-F option causes the expansion to be represented as a floating decimal number when it is expanded. +The +.B \-X +option cause the expansion to be represented using the +.B %a +format defined by ISO C-99. The optional option argument .I n -defines the number of places after the decimal point in this case. +defines the number of places after the decimal (or radix) point in this case. .PP An internal integer representation of a .I variable\^ @@ -2804,6 +2907,7 @@ Arithmetic evaluation is performed on the value of each assignment to a variable with the .BR \-E , .BR \-F , +.BR \-X , or .B \-i attribute. @@ -3028,7 +3132,7 @@ True, if .I string\^ does not match .IR pattern . -With the +When the .I string\^ matches the .I pattern\^ @@ -3256,7 +3360,7 @@ then leading spaces and tabs will be stripped off the first line of the document and up to an equivalent indentation will be stripped from the remaining lines and from .IR word . -A tab stop is assumend to occur at every 8 columns for the +A tab stop is assumed to occur at every 8 columns for the purposes of determining the indentation. .TP \f3<<<\fP\f2word\fP @@ -3330,7 +3434,7 @@ If one of the above, other than and the .B ># and -.B ># +.B <# forms, is preceded by .BI { varname } @@ -3344,7 +3448,7 @@ If or the any of the .B ># and -.B ># +.B <# forms is preceded by .BI { varname } @@ -3534,7 +3638,7 @@ special built-in command used within a function defines local variables whose scope includes the current function. They can be passed to functions that they call in the -variable assignment list the precedes the call or as arguments +variable assignment list that precedes the call or as arguments passed as name references. Errors within functions return control to the caller. .PP @@ -3591,7 +3695,8 @@ file. Each variable can have zero or more discipline functions associated with it. The shell initially understands the discipline names \f3get\fP, -\f3set\fP, \f3append\fP, and \f3unset\fP but on most systems +\f3set\fP, \f3append\fP, and \f3unset\fP but can be added +when defining new types. On most systems others can be added at run time via the C programming interface extension provided by the .B builtin @@ -3623,12 +3728,113 @@ contains the name of the variable for which the discipline function is called, is the subscript of the variable, and .B .sh.value will contain the value being assigned inside the -.B .set +.B set discipline function. +The variable +.B _ +is a reference to the variable including the subscript if any. For the \f3set\fP discipline, changing .B .sh.value will change the value that gets assigned. +Finally, the expansion \f3${\fP\f2var\^\fP\f3.\fP\f2name\^\fP\f3}\fP, +when \f2name\^\fP is the name of a discipline, and there is +no variable of this name, is equivalent to the command substitution +\f3${ \fP\f2var\^\fP\f3.\fP\f2name\^\fP\f3;}\fP. + +.SS Type Variables. +Typed variables provide a way to create data structure and objects. +A type can be defined either by a shared library, by the +.B enum +built-in command described below, or by using the new +.B \-T +option of the +.B typeset +built-in command. +With the +.B \-T +option of +.BR typeset , +the type name, specified as an option argument to +.BR \-T , +is set with a compound variable assignment that defines the type. +Function definitions can appear inside the compound variable +assignment and these become discipline functions for this type and +can be invoked or redefined by each instance of the type. +The function name +.B create +is treated specially. It is invoked for each instance of +the type that is created but is not inherited and cannot be +redefined for each instance. +.PP +When a type is defined a special built-in command of that name +is added. These built-ins are declaration commands and follow the +same expansion rules as all the special built-in commands defined +below that are preceded by \(dg\(dg. These commands can subsequently +be used inside further type definitions. The man page for these commands can +be generated by using the +.B \-\-man +option or any of the other +.B \-\- +options described with +.BR getopts . +The +.BR \-r , +.BR \-a , +.BR \-A , +.BR \-h , +and +.B \-S +options of +.B typeset +are permitted with each of these new built-ins. +.PP +An instance of a type is created by invoking the type name +followed by one or more instance names. +Each instance of the type is initialized with a copy of the sub-variables +except for sub-variables that are defined with the +.B \-S +option. Variables defined with the +.B \-S +are shared by all instances of the type. +Each instance can change the value of any sub-variable and can also +define new discipline functions of the same names +as those defined by the type definition as well as any +standard discipline names. +No additional sub-variables can be defined for any instance. +.PP +When defining a type, +if the value of a sub-variable is not set and the +.B \-r +attribute is specified, it causes the sub-variable +to be a required sub-variable. +Whenever an instance of a type is created, all required sub-variables +must be specified. +These sub-variables become readonly in each instance. +.PP +When +.B unset +is invoked on a sub-variable within a type, +and the +.B \-r +attribute has not been specified for this field, +the value is reset to the default value associative with +the type. +Invoking +.B unset +on a type instance not contained within another type deletes +all sub-variables and the variable itself. +.PP +A type definition can be derived from another type definition +by defining the first sub-variable name as +.B _ +and defining its type as the base type. +Any remaining definitions will be additions and modifications +that apply to the new type. +If the new type name is the same is that of the base type, +the type will be replaced and the original type will +no longer be accessible. + .SS Jobs. .PP If the @@ -3829,7 +4035,7 @@ and a function of the given name is executed as described above. If not found, and the file .B .paths -is found, and the this file contains a line of the form +is found, and this file contains a line of the form .BI FPATH= path where .I path\^ @@ -4244,7 +4450,7 @@ Delete previous character. (User defined literal next character as defined by the .IR stty (1) -command. +command, or .B ^V if not defined.) @@ -4375,7 +4581,13 @@ Moves back one line when not on the first line of a multi-line command. .PP .TP 10 .BI M-[A -Equivalent to +If the cursor is at the end of the line, it is equivalent to +.B ^R +with +.I string\^ +set to the contents of the current line. +Otherwise, it is +equivalent to .BR ^P. .PP .TP 10 @@ -4471,7 +4683,7 @@ alias by the name .BI _\&_ letter and if an alias of this name is defined, its value will be inserted on the input queue. -The can be used to program functions keys on many terminals. +This can be used to program function keys on many terminals. .PP .TP 10 .B M-. @@ -4769,7 +4981,12 @@ Equivalent to .BR k . .TP 10 [\f2count\fP]\f3[A\fP -Equivalent to +If cursor is at the end of the line it is equivalent to +.B / +with +.I string^\ +set to the contents of the current line. +Otherwise, it is equivalent to .BR k . .TP 10 [\f2count\fP]\f3j\fP @@ -5070,6 +5287,9 @@ tilde substitution is performed after the .B = sign and field splitting and file name generation are not performed. +These are called +.I declaration\^ +built-ins. .PD .TP \(dg \f3:\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK @@ -5425,6 +5645,16 @@ See .IR echo (1) for usage and description. .TP +\(dg\(dg \f3enum\fP \*(OK \f3\-i\fP \*(CK \f2type\^\fP\*(OK=(\f2value\^\fP .\|.\|.) \*(CK +Creates a declaration command named \f2type\^\fP that is an +integer type that allows one of the specifed \f2value\fPs as +enumeration names. If \f3=(\fP\f2value\^\ .\|.\|.\|\fP\f3)\fP is +omitted, then \f2type\^\fP must be an indexed array variable with at +least two elements and the values are taken from this array variable. +If +.B -i +is specified the values are case insensitive. +.TP \(dg \f3eval\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK The arguments are read as input to the shell @@ -5891,7 +6121,7 @@ other than .BR \-n . The .B \-e -causes the above escape conventions to be applied +causes the above escape conventions to be applied. This is the default behavior. It reverses the effect of an earlier .BR \-r . @@ -5961,7 +6191,7 @@ format can be used instead of to cause characters in .I arg\^ that are special in HTML and XML -to be output to be output as their entity name. +to be output as their entity name. .LI A .B %P @@ -5978,7 +6208,7 @@ format can be used instead of .B %s to cause .I arg\^ -interpreted as a shell pattern +to be interpreted as a shell pattern and to be printed as an extended regular expression. .LI A @@ -6008,7 +6238,7 @@ format can be followed by a and the output base. In this case, the .B # -flag character caues +flag character causes .IB base # to be prepended. .LI @@ -6051,7 +6281,7 @@ or on the command line determines which method is used. .TP -\f3read\fP \*(OK \f3\-Aprs\^\fP \*(CK \*(OK \f3\-d\fP \f2delim\^\fP\*(CK \*(OK \f3\-n\fP \f2n\^\fP\*(CK \*(OK \*(OK \f3\-N\fP \f2n\^\fP\*(CK \*(OK \*(OK \f3\-t\fP \f2timeout\^\fP\*(CK \*(OK \f3\-u\fP \f2unit\^\fP\*(CK \*(OK \f2vname\f3?\f2prompt\^\f1 \*(CK \*(OK \f2vname\^\fP .\|.\|. \*(CK +\f3read\fP \*(OK \f3\-ACprsv\^\fP \*(CK \*(OK \f3\-d\fP \f2delim\^\fP\*(CK \*(OK \f3\-n\fP \f2n\^\fP\*(CK \*(OK \*(OK \f3\-N\fP \f2n\^\fP\*(CK \*(OK \*(OK \f3\-t\fP \f2timeout\^\fP\*(CK \*(OK \f3\-u\fP \f2unit\^\fP\*(CK \*(OK \f2vname\f3?\f2prompt\^\f1 \*(CK \*(OK \f2vname\^\fP .\|.\|. \*(CK The shell input mechanism. One line is read and is broken up into fields using the characters in @@ -6117,6 +6347,12 @@ to be unset and each field that is read to be stored in successive elements of the indexed array .IR vname. The +.B \-C +option causes the variable +.I vname\^ +to be read as a compound variable. Blanks will be ignored when +finding the beginning open parenthesis. +The .B \-p option causes the input line to be taken from the input pipe @@ -6184,6 +6420,8 @@ are marked readonly and these names cannot be changed by subsequent assignment. +When defining a type, if the value of a readonly sub-variable is not defined +the value is required when creating each instance. .TP \(dg \f3return\fP \*(OK \f2n\^\fP \*(CK Causes a shell @@ -6208,7 +6446,7 @@ script, then it behaves the same as .BR exit . .TP -\(dg \f3set\fP \*(OK \f3\(+-CGabefhkmnoprstuvx\fP \*(CK \*(OK \f3\(+-o\fP \*(OK \f2option\^\fP \*(CK \*(CK .\|.\|. \*(OK \f3\(+-A\fP \f2vname\^\fP \*(CK \*(OK \f2arg\^\fP .\|.\|. \*(CK +\(dg \f3set\fP \*(OK \f3\(+-BCGabefhkmnoprstuvx\fP \*(CK \*(OK \f3\(+-o\fP \*(OK \f2option\^\fP \*(CK \*(CK .\|.\|. \*(OK \f3\(+-A\fP \f2vname\^\fP \*(CK \*(OK \f2arg\^\fP .\|.\|. \*(CK The options for this command have meaning as follows: .RS .PD 0 @@ -6230,6 +6468,9 @@ is not unset first. Enable brace pattern field generation. This is the default behavior. .TP 8 +.B \-B +Enable brace group expansion. On by default. +.TP 8 .B \-C Prevents redirection .B > @@ -6256,7 +6497,18 @@ Prints job completion messages as soon as a background job changes state rather than waiting for the next prompt. .TP 8 .B \-e -If a command has a non-zero exit status, +Unless contained in a +.B \(bv\(bv +or +.B && +command, or the command following an +.B if +.B while +or +.B until +command or in the pipeline following +.BR ! , +if a command has a non-zero exit status, execute the .SM .B ERR @@ -6303,8 +6555,8 @@ Same as All background jobs are run at a lower priority. This is the default mode. .TP 8 -.B bracexpand -Sans as +.B braceexpand +Same as .BR \-B . .TP 8 .B emacs @@ -6372,10 +6624,10 @@ Same as A pipeline will not complete until all components of the pipeline have completed, and the return value will be the value of the last non-zero command -to fail or zero of no command has failed. +to fail or zero if no command has failed. .TP 8 .B showme -When enabled, simple commands or pipelines preceded by a a semicolon +When enabled, simple commands or pipelines preceded by a semicolon .RB ( ; ) will be displayed as if the .B xtrace @@ -6572,6 +6824,13 @@ will contain the contents of the current command line when .I action\^ is running. +If the exit status of the trap is +.B 2 +the command will not be executed. +If the exit status of the trap is +.B 255 +and inside a function or a dot script, +the function or dot script will return. If .I sig\^ is @@ -6628,7 +6887,7 @@ Does nothing, and exits 0. Used with .B while for infinite loops. .TP -\(dg\(dg \f3typeset\fP \*(OK \f3\(+-AHflabnprtux\^\fP \*(CK \*(OK \f3\(+-EFLRZi\*(OK\f2n\^\fP\*(CK \*(CK \*(OK \f2vname\^\fP\*(OK\f3=\fP\f2value\^\fP \*(CK \^ \*(CK .\|.\|. +\(dg\(dg \f3typeset\fP \*(OK \f3\(+-ACHSflbnprtux\^\fP \*(CK \*(OK \f3\(+-EFLRXZi\*(OK\f2n\^\fP\*(CK \*(CK \*(OK \f3\-T \f2tname\fP=(\f2assign_list\fP) \*(CK \*(OK \f3\-h \f2str\fP \*(CK \*(OK \f3\-a\fP \*(OK\f2type\fP\*(CK \*(CK \*(OK \f2vname\^\fP\*(OK\f3=\fP\f2value\^\fP \*(CK \^ \*(CK .\|.\|. Sets attributes and values for shell variables and functions. When invoked inside a function defined with the .B function @@ -6650,11 +6909,27 @@ to be an associative array. Subscripts are strings rather than arithmetic expressions. .TP +.B \-C +causes each +.I vname\^ +to be a compound variable. +.I value\^ +names a compound variable it is copied into +.IR vname . +Otherwise, it unsets each +.IR vname . +.TP .B \-a Declares .I vname\^ to be an indexed array. -This is optional unless except for compound variable assignments. +If +.I type\^ +is specified, it must be the name of an enumeration +type created with the +.B enum +command and it allows enumeration constants to be used +as subscripts. .TP .B \-E Declares @@ -6713,6 +6988,38 @@ The .B \-L option is turned off. .TP +.B \-S +When used within the +.I assign_list\^ +of a type definition, it causes the specified sub-variable +to be shared by all instances of the type. +When used inside a function defined with the +.B function +reserved word, the specified variables will have +.I "function static\^" +scope. +Otherwise, the variable is unset prior to processing the assignment list. +.TP +.B \-T +Creates a type named by \fItname\^\fP using the compound +assignment +.I assign_list\^ +to \f2tname\fP. +.TP +.B \-X +Declares +.I vname\^ +to be a double precision floating point number +and expands using the +.B %a +format of ISO-C99. +If +.I n\^ +is non-zero, it defines the number of hex digits after +the radix point that is used when expanding +.IR vname . +The default is 10. +.TP .B \-Z Right justify and fill with leading zeros if the first non-blank character is a digit and the @@ -6778,6 +7085,15 @@ format can be used to output the actual data in this buffer instead of the base64 encoding of the data. .TP +.B \-h +Used within type definitions to add information when generating +information about the sub-variable on the man page. +It is ignored when used outside of a type definition. +When used with +.B \-f +the information is associated with the corresponding discipline +function. +.TP .B \-i Declares .I vname\^ @@ -6805,6 +7121,15 @@ defined by the value of variable This is usually used to reference a variable inside a function whose name has been passed as an argument. .TP +.B \-p +The name, attributes and values for the give +.IR vname s +are written on standard output in a form that can be +used as shell input. +If +.B +p +is specified, then the values are not displayed. +.TP .B \-r The given .IR vname s @@ -6913,7 +7238,7 @@ If neither the .B H nor .B S -options is specified, the limit applies to both. +option is specified, the limit applies to both. The current resource limit is printed when .I limit\^ is omitted. @@ -7001,7 +7326,10 @@ The variables given by the list of .IR vname s are unassigned, i.e., +except for sub-variables within a type, their values and attributes are erased. +For sub-variables of a type, the values are reset to the +default value from the type definition. Readonly variables cannot be unset. If the .B \-f @@ -7078,7 +7406,7 @@ option produces a more verbose report. The .B \-f -options skips the search for functions. +option skips the search for functions. The .B \-p option @@ -7086,6 +7414,11 @@ does a path search for .I name\^ even if name is an alias, a function, or a reserved word. The +.B \-p +option turns off the +.B \-v +option. +The .B \-a option is similar to the @@ -7123,7 +7456,7 @@ option is not present and .I arg\^ and a file by the name of .I arg\^ -exits, then it reads and executes this script. +exists, then it reads and executes this script. Otherwise, if the first .I arg\^ does not contain a @@ -7146,6 +7479,19 @@ the following options are interpreted by the shell when it is invoked: .PP .PD 0 +.TP 8 +.B \-D +Do not execute the script, but output the set of double quoted strings +preceded by a +.BR $ . +These strings are needed for localization of the script to different locales. +.TP 8 +.B \-E +Reads the file named by the +.B ENV +variable or by +\s-1$HOME\s+1/\f3.\fPkshrc +if not defined after the profiles. .TP 10 .BI \-c If the @@ -7451,3 +7797,4 @@ won't be executed until the foreground job terminates. It is a good idea to leave a space after the comma operator in arithmetic expressions to prevent the comma from being interpreted as the decimal point character in certain locales. + diff --git a/usr/src/lib/libshell/common/sh/args.c b/usr/src/lib/libshell/common/sh/args.c index ad1bf46a40..06ae7dbd38 100644 --- a/usr/src/lib/libshell/common/sh/args.c +++ b/usr/src/lib/libshell/common/sh/args.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -43,7 +43,7 @@ # define PFSHOPT #endif #if SHOPT_BASH -# define BASHOPT "\375\374\373" +# define BASHOPT "\374" #else # define BASHOPT #endif @@ -56,10 +56,6 @@ #define SORT 1 #define PRINT 2 -void sh_applyopts(Shopt_t); - -static int arg_expand(struct argnod*,struct argnod**,int); - static char *null; /* The following order is determined by sh_optset */ @@ -70,7 +66,7 @@ static const int flagval[] = SH_PFSH, #endif #if SHOPT_BASH - SH_NOPROFILE, SH_RC, SH_POSIX, + SH_POSIX, #endif SH_DICTIONARY, SH_INTERACTIVE, SH_RESTRICTED, SH_CFLAG, SH_ALLEXPORT, SH_NOTIFY, SH_ERREXIT, SH_NOGLOB, SH_TRACKALL, @@ -87,7 +83,7 @@ static const int flagval[] = typedef struct _arg_ { - Shell_t *shp; + Shell_t *sh; struct dolnod *argfor; /* linked list of blocks to be cleaned up */ struct dolnod *dolh; char flagadr[NUM_OPTS+1]; @@ -96,6 +92,9 @@ typedef struct _arg_ #endif /* SHOPT_KIA */ } Arg_t; +static int arg_expand(Shell_t*,struct argnod*,struct argnod**,int); +static void sh_argset(Arg_t*, char *[]); + /* ======== option handling ======== */ @@ -103,7 +102,7 @@ void *sh_argopen(Shell_t *shp) { void *addr = newof(0,Arg_t,1,0); Arg_t *ap = (Arg_t*)addr; - ap->shp = shp; + ap->sh = shp; return(addr); } @@ -136,17 +135,19 @@ static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp) * The -o option is used to set option by name * This routine returns the number of non-option arguments */ -int sh_argopts(int argc,register char *argv[]) +int sh_argopts(int argc,register char *argv[], void *context) { - register int n,o; - register Arg_t *ap = (Arg_t*)sh.arg_context; - Shopt_t newflags; + Shell_t *shp = (Shell_t*)context; + register int n,o; + register Arg_t *ap = (Arg_t*)(shp->arg_context); + Lex_t *lp = (Lex_t*)(shp->lex_context); + Shopt_t newflags; int setflag=0, action=0, trace=(int)sh_isoption(SH_XTRACE); Namval_t *np = NIL(Namval_t*); const char *cp; int verbose,f; Optdisc_t disc; - newflags=sh.options; + newflags=ap->sh->options; memset(&disc, 0, sizeof(disc)); disc.version = OPT_VERSION; disc.infof = infof; @@ -159,11 +160,11 @@ int sh_argopts(int argc,register char *argv[]) while((n = optget(argv,setflag?sh_optset:sh_optksh))) { o=0; - f=*opt_info.option=='-'; + f=*opt_info.option=='-' && (opt_info.num || opt_info.arg); switch(n) { case 'A': - np = nv_open(opt_info.arg,sh.var_tree,NV_NOASSIGN|NV_ARRAY|NV_VARNAME); + np = nv_open(opt_info.arg,ap->sh->var_tree,NV_NOASSIGN|NV_ARRAY|NV_VARNAME); if(f) nv_unset(np); continue; @@ -202,34 +203,51 @@ int sh_argopts(int argc,register char *argv[]) break; #if SHOPT_BASH case -1: /* --rcfile */ - sh.rcfile = opt_info.arg; + ap->sh->rcfile = opt_info.arg; continue; - case -6: /* --version */ - sfputr(sfstdout, "ksh bash emulation, version ",-1); - np = nv_open("BASH_VERSION",sh.var_tree,0); - sfputr(sfstdout, nv_getval(np),-1); - np = nv_open("MACHTYPE",sh.var_tree,0); - sfprintf(sfstdout, " (%s)\n", nv_getval(np)); - sh_exit(0); - case -2: /* --noediting */ - off_option(&newflags,SH_VI); - off_option(&newflags,SH_EMACS); - off_option(&newflags,SH_GMACS); + if (!f) + { + off_option(&newflags,SH_VI); + off_option(&newflags,SH_EMACS); + off_option(&newflags,SH_GMACS); + } continue; - case -3: /* --profile */ - f = !f; - /*FALLTHROUGH*/ - case -4: /* --rc */ - case -5: /* --posix */ + n = 'l'; + goto skip; + case -4: /* --posix */ /* mask lower 8 bits to find char in optksh string */ n&=0xff; goto skip; + case -5: /* --version */ + sfputr(sfstdout, "ksh bash emulation, version ",-1); + np = nv_open("BASH_VERSION",ap->sh->var_tree,0); + sfputr(sfstdout, nv_getval(np),-1); + np = nv_open("MACHTYPE",ap->sh->var_tree,0); + sfprintf(sfstdout, " (%s)\n", nv_getval(np)); + sh_exit(0); #endif + case -6: /* --default */ + { + register const Shtable_t *tp; + for(tp=shtab_options; o = tp->sh_number; tp++) + if(!(o&SH_COMMANDLINE) && is_option(&newflags,o&0xff)) + off_option(&newflags,o&0xff); + } + continue; + case -7: + f = 0; + goto byname; case 'D': on_option(&newflags,SH_NOEXEC); goto skip; + case 'T': + if (opt_info.num) + ap->sh->test |= opt_info.num; + else + ap->sh->test = 0; + continue; case 's': if(setflag) { @@ -275,7 +293,7 @@ int sh_argopts(int argc,register char *argv[]) off_option(&newflags,SH_GMACS); } on_option(&newflags,o); - off_option(&sh.offoptions,o); + off_option(&ap->sh->offoptions,o); } else { @@ -283,7 +301,7 @@ int sh_argopts(int argc,register char *argv[]) trace = 0; off_option(&newflags,o); if(setflag==0) - on_option(&sh.offoptions,o); + on_option(&ap->sh->offoptions,o); } } if(error_info.errors) @@ -310,7 +328,7 @@ int sh_argopts(int argc,register char *argv[]) if(argc>0) strsort(argv,argc,strcoll); else - strsort(sh.st.dolv+1,sh.st.dolc,strcoll); + strsort(ap->sh->st.dolv+1,ap->sh->st.dolc,strcoll); } if(np) { @@ -318,11 +336,11 @@ int sh_argopts(int argc,register char *argv[]) nv_close(np); } else if(argc>0 || ((cp=argv[-1]) && strcmp(cp,"--")==0)) - sh_argset(argv-1); + sh_argset(ap,argv-1); } else if(is_option(&newflags,SH_CFLAG)) { - if(!(sh.comdiv = *argv++)) + if(!(ap->sh->comdiv = *argv++)) { errormsg(SH_DICT,2,e_cneedsarg); errormsg(SH_DICT,ERROR_usage(2),optusage(NIL(char*))); @@ -332,23 +350,25 @@ int sh_argopts(int argc,register char *argv[]) /* handling SH_INTERACTIVE and SH_PRIVILEGED has been moved to * sh_applyopts(), so that the code can be reused from b_shopt(), too */ - sh_applyopts(newflags); + sh_applyopts(ap->sh,newflags); #if SHOPT_KIA if(ap->kiafile) { - if(!(shlex.kiafile=sfopen(NIL(Sfio_t*),ap->kiafile,"w+"))) + if(!argv[0]) + errormsg(SH_DICT,ERROR_usage(2),"-R requires scriptname"); + if(!(lp->kiafile=sfopen(NIL(Sfio_t*),ap->kiafile,"w+"))) errormsg(SH_DICT,ERROR_system(3),e_create,ap->kiafile); - if(!(shlex.kiatmp=sftmp(2*SF_BUFSIZE))) + if(!(lp->kiatmp=sftmp(2*SF_BUFSIZE))) errormsg(SH_DICT,ERROR_system(3),e_tmpcreate); - sfputr(shlex.kiafile,";vdb;CIAO/ksh",'\n'); - shlex.kiabegin = sftell(shlex.kiafile); - shlex.entity_tree = dtopen(&_Nvdisc,Dtbag); - shlex.scriptname = strdup(sh_fmtq(argv[0])); - shlex.script=kiaentity(shlex.scriptname,-1,'p',-1,0,0,'s',0,""); - shlex.fscript=kiaentity(shlex.scriptname,-1,'f',-1,0,0,'s',0,""); - shlex.unknown=kiaentity("<unknown>",-1,'p',-1,0,0,'0',0,""); - kiaentity("<unknown>",-1,'p',0,0,shlex.unknown,'0',0,""); - shlex.current = shlex.script; + sfputr(lp->kiafile,";vdb;CIAO/ksh",'\n'); + lp->kiabegin = sftell(lp->kiafile); + lp->entity_tree = dtopen(&_Nvdisc,Dtbag); + lp->scriptname = strdup(sh_fmtq(argv[0])); + lp->script=kiaentity(lp,lp->scriptname,-1,'p',-1,0,0,'s',0,""); + lp->fscript=kiaentity(lp,lp->scriptname,-1,'f',-1,0,0,'s',0,""); + lp->unknown=kiaentity(lp,"<unknown>",-1,'p',-1,0,0,'0',0,""); + kiaentity(lp,"<unknown>",-1,'p',0,0,lp->unknown,'0',0,""); + lp->current = lp->script; ap->kiafile = 0; } #endif /* SHOPT_KIA */ @@ -357,7 +377,7 @@ int sh_argopts(int argc,register char *argv[]) /* apply new options */ -void sh_applyopts(Shopt_t newflags) +void sh_applyopts(Shell_t* shp,Shopt_t newflags) { /* cannot set -n for interactive shells since there is no way out */ if(sh_isoption(SH_INTERACTIVE)) @@ -368,17 +388,17 @@ void sh_applyopts(Shopt_t newflags) { if(sh_isoption(SH_PRIVILEGED)) { - setuid(sh.userid); - setgid(sh.groupid); - if(sh.euserid==0) + setuid(shp->userid); + setgid(shp->groupid); + if(shp->euserid==0) { - sh.euserid = sh.userid; - sh.egroupid = sh.groupid; + shp->euserid = shp->userid; + shp->egroupid = shp->groupid; } } - else if((sh.userid!=sh.euserid && setuid(sh.euserid)<0) || - (sh.groupid!=sh.egroupid && setgid(sh.egroupid)<0) || - (sh.userid==sh.euserid && sh.groupid==sh.egroupid)) + else if((shp->userid!=shp->euserid && setuid(shp->euserid)<0) || + (shp->groupid!=shp->egroupid && setgid(shp->egroupid)<0) || + (shp->userid==shp->euserid && shp->groupid==shp->egroupid)) off_option(&newflags,SH_PRIVILEGED); } #if SHOPT_BASH @@ -410,15 +430,16 @@ void sh_applyopts(Shopt_t newflags) sh_offoption(SH_HISTORY); } #endif - sh.options = newflags; + shp->options = newflags; } + /* * returns the value of $- */ -char *sh_argdolminus(void) +char *sh_argdolminus(void* context) { + register Arg_t *ap = (Arg_t*)context; register const char *cp=optksh; - register Arg_t *ap = (Arg_t*)sh.arg_context; register char *flagp=ap->flagadr; while(cp< &optksh[NUM_OPTS]) { @@ -434,16 +455,15 @@ char *sh_argdolminus(void) /* * set up positional parameters */ -void sh_argset(char *argv[]) +static void sh_argset(Arg_t *ap,char *argv[]) { - register Arg_t *ap = (Arg_t*)sh.arg_context; - sh_argfree(ap->dolh,0); + sh_argfree(ap->sh,ap->dolh,0); ap->dolh = sh_argcreate(argv); /* link into chain */ ap->dolh->dolnxt = ap->argfor; ap->argfor = ap->dolh; - sh.st.dolc = ap->dolh->dolnum-1; - sh.st.dolv = ap->dolh->dolval; + ap->sh->st.dolc = ap->dolh->dolnum-1; + ap->sh->st.dolv = ap->dolh->dolval; } /* @@ -453,11 +473,11 @@ void sh_argset(char *argv[]) * Delete the blk from the argfor chain * If flag is set, then the block dolh is not freed */ -struct dolnod *sh_argfree(struct dolnod *blk,int flag) +struct dolnod *sh_argfree(Shell_t *shp, struct dolnod *blk,int flag) { register struct dolnod* argr=blk; register struct dolnod* argblk; - register Arg_t *ap = (Arg_t*)sh.arg_context; + register Arg_t *ap = (Arg_t*)shp->arg_context; if(argblk=argr) { if((--argblk->dolrefcnt)==0) @@ -518,39 +538,39 @@ struct dolnod *sh_argcreate(register char *argv[]) /* * used to set new arguments for functions */ -struct dolnod *sh_argnew(char *argi[], struct dolnod **savargfor) +struct dolnod *sh_argnew(Shell_t *shp,char *argi[], struct dolnod **savargfor) { - register Arg_t *ap = (Arg_t*)sh.arg_context; + register Arg_t *ap = (Arg_t*)shp->arg_context; register struct dolnod *olddolh = ap->dolh; *savargfor = ap->argfor; ap->dolh = 0; ap->argfor = 0; - sh_argset(argi); + sh_argset(ap,argi); return(olddolh); } /* * reset arguments as they were before function */ -void sh_argreset(struct dolnod *blk, struct dolnod *afor) +void sh_argreset(Shell_t *shp,struct dolnod *blk, struct dolnod *afor) { - register Arg_t *ap = (Arg_t*)sh.arg_context; - while(ap->argfor=sh_argfree(ap->argfor,0)); + register Arg_t *ap = (Arg_t*)shp->arg_context; + while(ap->argfor=sh_argfree(shp,ap->argfor,0)); ap->argfor = afor; if(ap->dolh = blk) { - sh.st.dolc = ap->dolh->dolnum-1; - sh.st.dolv = ap->dolh->dolval; + shp->st.dolc = ap->dolh->dolnum-1; + shp->st.dolv = ap->dolh->dolval; } } /* * increase the use count so that an sh_argset will not make it go away */ -struct dolnod *sh_arguse(void) +struct dolnod *sh_arguse(Shell_t* shp) { register struct dolnod *dh; - register Arg_t *ap = (Arg_t*)sh.arg_context; + register Arg_t *ap = (Arg_t*)shp->arg_context; if(dh=ap->dolh) dh->dolrefcnt++; return(dh); @@ -624,7 +644,7 @@ void sh_printopts(Shopt_t oflags,register int mode, Shopt_t *mask) if(mode&PRINT_SHOPT) sfwrite(sfstdout,"shopt -s",3); else - sfwrite(sfstdout,"set",3); + sfwrite(sfstdout,"set --default",13); } for(tp=shtab_options; value=tp->sh_number; tp++) { @@ -671,11 +691,11 @@ void sh_printopts(Shopt_t oflags,register int mode, Shopt_t *mask) /* * build an argument list */ -char **sh_argbuild(int *nargs, const struct comnod *comptr,int flag) +char **sh_argbuild(Shell_t *shp,int *nargs, const struct comnod *comptr,int flag) { register struct argnod *argp; struct argnod *arghead=0; - sh.xargmin = 0; + shp->xargmin = 0; { register const struct comnod *ac = comptr; register int n; @@ -689,10 +709,9 @@ char **sh_argbuild(int *nargs, const struct comnod *comptr,int flag) { register struct dolnod *ap = (struct dolnod*)ac->comarg; *nargs = ap->dolnum; - ((struct comnod*)ac)->comtyp |= COMFIXED; return(ap->dolval+ap->dolbot); } - sh.lastpath = 0; + shp->lastpath = 0; *nargs = 0; if(ac) { @@ -701,12 +720,12 @@ char **sh_argbuild(int *nargs, const struct comnod *comptr,int flag) argp = ac->comarg; while(argp) { - n = arg_expand(argp,&arghead,flag); + n = arg_expand(shp,argp,&arghead,flag); if(n>1) { - if(sh.xargmin==0) - sh.xargmin = *nargs; - sh.xargmax = *nargs+n; + if(shp->xargmin==0) + shp->xargmin = *nargs; + shp->xargmax = *nargs+n; } *nargs += n; argp = argp->argnxt.ap; @@ -718,12 +737,11 @@ char **sh_argbuild(int *nargs, const struct comnod *comptr,int flag) register char **comargn; register int argn; register char **comargm; - int argfixed = COMFIXED; argn = *nargs; /* allow room to prepend args */ argn += 1; - comargn=(char**)stakalloc((unsigned)(argn+1)*sizeof(char*)); + comargn=(char**)stkalloc(shp->stk,(unsigned)(argn+1)*sizeof(char*)); comargm = comargn += argn; *comargn = NIL(char*); if(!argp) @@ -737,8 +755,6 @@ char **sh_argbuild(int *nargs, const struct comnod *comptr,int flag) struct argnod *nextarg = argp->argchn.ap; argp->argchn.ap = 0; *--comargn = argp->argval; - if(!(argp->argflag&ARG_RAW) || (argp->argflag&ARG_EXP)) - argfixed = 0; if(!(argp->argflag&ARG_RAW)) sh_trim(*comargn); if(!(argp=nextarg) || (argp->argflag&ARG_MAKE)) @@ -748,7 +764,7 @@ char **sh_argbuild(int *nargs, const struct comnod *comptr,int flag) comargm = comargn; } } - ((struct comnod*)comptr)->comtyp |= argfixed; + shp->last_table = 0; return(comargn); } } @@ -774,7 +790,7 @@ static int arg_pipe(register int pv[]) #endif /* Argument expansion */ -static int arg_expand(register struct argnod *argp, struct argnod **argchain,int flag) +static int arg_expand(Shell_t *shp,register struct argnod *argp, struct argnod **argchain,int flag) { register int count = 0; argp->argflag &= ~ARG_MAKE; @@ -784,34 +800,34 @@ static int arg_expand(register struct argnod *argp, struct argnod **argchain,int /* argument of the form (cmd) */ register struct argnod *ap; int monitor, fd, pv[2]; - ap = (struct argnod*)stakseek(ARGVAL); + ap = (struct argnod*)stkseek(shp->stk,ARGVAL); ap->argflag |= ARG_MAKE; ap->argflag &= ~ARG_RAW; ap->argchn.ap = *argchain; *argchain = ap; count++; - stakwrite(e_devfdNN,8); + sfwrite(shp->stk,e_devfdNN,8); sh_pipe(pv); fd = argp->argflag&ARG_RAW; - stakputs(fmtbase((long)pv[fd],10,0)); - ap = (struct argnod*)stakfreeze(1); - sh.inpipe = sh.outpipe = 0; + sfputr(shp->stk,fmtbase((long)pv[fd],10,0),0); + ap = (struct argnod*)stkfreeze(shp->stk,0); + shp->inpipe = shp->outpipe = 0; if(monitor = (sh_isstate(SH_MONITOR)!=0)) sh_offstate(SH_MONITOR); if(fd) { - sh.inpipe = pv; + shp->inpipe = pv; sh_exec((Shnode_t*)argp->argchn.ap,(int)sh_isstate(SH_ERREXIT)); } else { - sh.outpipe = pv; + shp->outpipe = pv; sh_exec((Shnode_t*)argp->argchn.ap,(int)sh_isstate(SH_ERREXIT)); } if(monitor) sh_onstate(SH_MONITOR); close(pv[1-fd]); - sh_iosave(-pv[fd], sh.topfd); + sh_iosave(shp,-pv[fd], shp->topfd, (char*)0); } else #endif /* SHOPT_DEVFD */ @@ -819,11 +835,12 @@ static int arg_expand(register struct argnod *argp, struct argnod **argchain,int { #if SHOPT_OPTIMIZE struct argnod *ap; + sh_stats(STAT_ARGEXPAND); if(flag&ARG_OPTIMIZE) argp->argchn.ap=0; if(ap=argp->argchn.ap) { - sh.optcount++; + sh_stats(STAT_ARGHITS); count = 1; ap->argchn.ap = *argchain; ap->argflag |= ARG_RAW; @@ -832,7 +849,7 @@ static int arg_expand(register struct argnod *argp, struct argnod **argchain,int } else #endif /* SHOPT_OPTIMIZE */ - count = sh_macexpand(argp,argchain,flag); + count = sh_macexpand(shp,argp,argchain,flag); } else { diff --git a/usr/src/lib/libshell/common/sh/arith.c b/usr/src/lib/libshell/common/sh/arith.c index b81e798fae..f21b79ad6f 100644 --- a/usr/src/lib/libshell/common/sh/arith.c +++ b/usr/src/lib/libshell/common/sh/arith.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -35,7 +35,7 @@ #define LLONG_MAX LONG_MAX #endif -static Sfdouble_t Zero, NaN, Inf; +static Sfdouble_t NaN, Inf, Fun; static Namval_t Infnod = { { 0 }, @@ -50,36 +50,51 @@ static Namval_t NaNnod = NV_NOFREE|NV_LDOUBLE,NV_RDONLY }; -static Namval_t *scope(register Namval_t *np,register struct lval *lvalue,int assign) +static Namval_t FunNode = +{ + { 0 }, + "?", + NV_NOFREE|NV_LDOUBLE,NV_RDONLY +}; + +static Namval_t *scope(Shell_t *shp,register Namval_t *np,register struct lval *lvalue,int assign) { register Namarr_t *ap; register int flag = lvalue->flag; - register char *sub=0; - if(lvalue->emode&ARITH_COMP) + register char *sub=0, *cp=(char*)np; + register Namval_t *mp; + int flags = HASH_NOSCOPE|HASH_SCOPE|HASH_BUCKET; + Dt_t *sdict = (shp->st.real_fun? shp->st.real_fun->sdict:0); + assign = assign?NV_ASSIGN:NV_NOASSIGN; + if(cp>=lvalue->expr && cp < lvalue->expr+lvalue->elen) { - char *cp = (char*)np; - register Namval_t *mp; - if(cp>=lvalue->expr && cp < lvalue->expr+lvalue->elen) + int offset; + /* do binding to node now */ + int c = cp[flag]; + cp[flag] = 0; + if((!(np = nv_open(cp,shp->var_tree,assign|NV_VARNAME|NV_NOADD|NV_NOFAIL)) || nv_isnull(np)) && sh_macfun(shp,cp, offset = staktell())) { - /* do bindiing to node now */ - int c = cp[flag]; - cp[flag] = 0; - np = nv_open(cp,sh.var_tree,NV_NOASSIGN|NV_VARNAME); + Fun = sh_arith(sub=stakptr(offset)); + FunNode.nvalue.ldp = &Fun; cp[flag] = c; - if(cp[flag+1]=='[') - flag++; - else - flag = 0; + return(&FunNode); } - else if(dtvnext(sh.var_tree) && (mp=nv_search((char*)np,sh.var_tree,HASH_NOSCOPE|HASH_SCOPE|HASH_BUCKET))) + np = nv_open(cp,shp->var_tree,assign|NV_VARNAME); + cp[flag] = c; + if(cp[flag+1]=='[') + flag++; + else + flag = 0; + cp = (char*)np; + } + if((lvalue->emode&ARITH_COMP) && dtvnext(shp->var_tree) && ((mp=nv_search(cp,shp->var_tree,flags))||(sdict && (mp=nv_search(cp,sdict,flags))))) + { + while(nv_isref(mp)) { - while(nv_isref(mp)) - { - sub = nv_refsub(mp); - mp = nv_refnode(mp); - } - np = mp; + sub = nv_refsub(mp); + mp = nv_refnode(mp); } + np = mp; } if(flag || sub) { @@ -95,14 +110,16 @@ static Namval_t *scope(register Namval_t *np,register struct lval *lvalue,int as static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdouble_t n) { + Shell_t *shp = &sh; register Sfdouble_t r= 0; char *str = (char*)*ptr; + register char *cp; switch(type) { case ASSIGN: { register Namval_t *np = (Namval_t*)(lvalue->value); - np = scope(np,lvalue,1); + np = scope(shp,np,lvalue,1); nv_putval(np, (char*)&n, NV_LDOUBLE); r=nv_getnum(np); break; @@ -119,14 +136,18 @@ static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdoubl { register Namval_t *np; int dot=0; - char *cp; while(1) { while(xp=str, c=mbchar(str), isaname(c)); str = xp; + if(c=='[' && dot==NV_NOADD) + { + str = nv_endsubscript((Namval_t*)0,str,0); + c = *str; + } if(c!='.') break; - dot=1; + dot=NV_NOADD; if((c = *++str) !='[') continue; str = nv_endsubscript((Namval_t*)0,cp=str,NV_SUBQUOTE)-1; @@ -168,7 +189,7 @@ static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdoubl { int offset = staktell(); char *saveptr = stakfreeze(0); - Dt_t *root = (lvalue->emode&ARITH_COMP)?sh.var_base:sh.var_tree; + Dt_t *root = (lvalue->emode&ARITH_COMP)?shp->var_base:shp->var_tree; *str = c; while(c=='[' || c=='.') { @@ -184,32 +205,39 @@ static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdoubl } else { + dot = NV_NOADD|NV_NOFAIL; str++; while(xp=str, c=mbchar(str), isaname(c)); str = xp; } } *str = 0; - if(strcasecmp(*ptr,"Inf")==0) + cp = (char*)*ptr; + if ((cp[0] == 'i' || cp[0] == 'I') && (cp[1] == 'n' || cp[1] == 'N') && (cp[2] == 'f' || cp[2] == 'F') && cp[3] == 0) { - Inf = 1.0/Zero; + Inf = strtold("Inf", NiL); Infnod.nvalue.ldp = &Inf; np = &Infnod; } - else if(strcasecmp(*ptr,"NaN")==0) + else if ((cp[0] == 'n' || cp[0] == 'N') && (cp[1] == 'a' || cp[1] == 'A') && (cp[2] == 'n' || cp[2] == 'N') && cp[3] == 0) { - NaN = 0.0/Zero; + NaN = strtold("NaN", NiL); NaNnod.nvalue.ldp = &NaN; np = &NaNnod; } - else - np = nv_open(*ptr,root,NV_NOASSIGN|NV_VARNAME); + else if(!(np = nv_open(*ptr,root,NV_NOASSIGN|NV_VARNAME|dot))) + { + lvalue->value = (char*)*ptr; + lvalue->flag = str-lvalue->value; + } if(saveptr != stakptr(0)) stakset(saveptr,offset); else stakseek(offset); } *str = c; + if(!np && lvalue->value) + break; lvalue->value = (char*)np; if((lvalue->emode&ARITH_COMP) || (nv_isarray(np) && nv_aindex(np)<0)) { @@ -230,9 +258,7 @@ static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdoubl str = nv_endsubscript(np,str,NV_ADD|NV_SUBQUOTE); while((c=*str)=='['); } - else if(nv_isarray(np)) - nv_putsub(np,NIL(char*),ARRAY_UNDEF); - if(nv_isattr(np,NV_INTEGER|NV_DOUBLE)==(NV_INTEGER|NV_DOUBLE)) + if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE) lvalue->isfloat=1; lvalue->flag = nv_aindex(np); } @@ -260,7 +286,8 @@ static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdoubl c='e'; else c = *str; - if(c==GETDECIMAL(0) || c=='e' || c == 'E') + if(c==GETDECIMAL(0) || c=='e' || c == 'E' || lastbase == + 16 && (c == 'p' || c == 'P')) { lvalue->isfloat=1; r = strtold(val,&str); @@ -289,7 +316,7 @@ static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdoubl register Namval_t *np = (Namval_t*)(lvalue->value); if(sh_isoption(SH_NOEXEC)) return(0); - np = scope(np,lvalue,0); + np = scope(shp,np,lvalue,0); if(((lvalue->emode&2) || lvalue->level>1 || sh_isoption(SH_NOUNSET)) && nv_isnull(np) && !nv_isattr(np,NV_INTEGER)) { *ptr = nv_name(np); @@ -300,7 +327,7 @@ static Sfdouble_t arith(const char **ptr, struct lval *lvalue, int type, Sfdoubl r = nv_getnum(np); if(nv_isattr(np,NV_INTEGER|NV_BINARY)==(NV_INTEGER|NV_BINARY)) lvalue->isfloat= (r!=(Sflong_t)r); - else if(nv_isattr(np,NV_INTEGER|NV_DOUBLE)==(NV_INTEGER|NV_DOUBLE)) + else if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE) lvalue->isfloat=1; return(r); } @@ -338,10 +365,13 @@ Sfdouble_t sh_strnum(register const char *str, char** ptr, int mode) d = strtonll(str,&last,&base,-1); if(*last || errno) { - d = strval(str,&last,arith,mode); + if(!last || *last!='.' || last[1]!='.') + d = strval(str,&last,arith,mode); if(!ptr && *last && mode>0) errormsg(SH_DICT,ERROR_exit(1),e_lexbadchar,*last,str); } + else if (!d && *str=='-') + d = -0.0; if(ptr) *ptr = last; return(d); diff --git a/usr/src/lib/libshell/common/sh/array.c b/usr/src/lib/libshell/common/sh/array.c index 5b33f20e5a..ed46e84738 100644 --- a/usr/src/lib/libshell/common/sh/array.c +++ b/usr/src/lib/libshell/common/sh/array.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -33,16 +33,17 @@ #define NUMSIZE (4+(ARRAY_MAX>999)+(ARRAY_MAX>9999)+(ARRAY_MAX>99999)) #define is_associative(ap) array_assoc((Namarr_t*)(ap)) -#define array_setbit(cp, n) (cp[(n)/CHAR_BIT] |= 1<<(((n)&(CHAR_BIT-1)))) -#define array_clrbit(cp, n) (cp[(n)/CHAR_BIT] &= ~(1<<(((n)&(CHAR_BIT-1))))) -#define array_isbit(cp, n) (cp[(n)/CHAR_BIT] & 1<<(((n)&(CHAR_BIT-1)))) +#define array_setbit(cp, n, b) (cp[n] |= (b)) +#define array_clrbit(cp, n, b) (cp[n] &= ~(b)) +#define array_isbit(cp, n, b) (cp[n] & (b)) #define NV_CHILD NV_EXPORT - -static char Empty[] = ""; +#define ARRAY_CHILD 1 +#define ARRAY_NOFREE 2 struct index_array { Namarr_t header; + void *xp; /* if set, subscripts will be converted */ int cur; /* index of current element */ int maxi; /* maximum index for array */ unsigned char *bits; /* bit array for child subscripts */ @@ -52,12 +53,62 @@ struct index_array struct assoc_array { Namarr_t header; - Dt_t *table; Namval_t *pos; Namval_t *nextpos; Namval_t *cur; }; +static Namarr_t *array_scope(Namval_t *np, Namarr_t *ap, int flags) +{ + Namarr_t *aq; + struct index_array *ar; + size_t size = ap->hdr.dsize; + if(size==0) + size = ap->hdr.disc->dsize; + if(!(aq=newof(NIL(Namarr_t*),Namarr_t,1,size-sizeof(Namarr_t)))) + return(0); + memcpy(aq,ap,size); + aq->hdr.nofree &= ~1; + aq->hdr.nofree |= (flags&NV_RDONLY)?1:0; + if(is_associative(aq)) + { + aq->scope = (void*)dtopen(&_Nvdisc,Dtoset); + dtview((Dt_t*)aq->scope,aq->table); + aq->table = (Dt_t*)aq->scope; + return(aq); + } + aq->scope = (void*)ap; + ar = (struct index_array*)aq; + memset(ar->val, 0, ar->maxi*sizeof(char*)); + return(aq); +} + +static int array_unscope(Namval_t *np,Namarr_t *ap) +{ + Namfun_t *fp; + if(!ap->scope) + return(0); + if(is_associative(ap)) + (*ap->fun)(np, NIL(char*), NV_AFREE); + if((fp = nv_disc(np,(Namfun_t*)ap,NV_POP)) && !(fp->nofree&1)) + free((void*)fp); + nv_delete(np,(Dt_t*)0,0); + return(1); +} + +static void array_syncsub(Namarr_t *ap, Namarr_t *aq) +{ + ((struct index_array*)ap)->cur = ((struct index_array*)aq)->cur; +} + +static int array_covered(Namval_t *np, struct index_array *ap) +{ + struct index_array *aq = (struct index_array*)ap->header.scope; + if(!ap->header.fun && aq) + return ((ap->cur<aq->maxi) && aq->val[ap->cur].cp); + return(0); +} + /* * replace discipline with new one */ @@ -81,10 +132,12 @@ static void array_setptr(register Namval_t *np, struct index_array *old, struct * but <= ARRAY_MAX) is returned. * */ -static int arsize(register int maxi) +static int arsize(struct index_array *ap, register int maxi) { - register int i = roundof(maxi,ARRAY_INCR); - return (i>ARRAY_MAX?ARRAY_MAX:i); + if(ap && maxi < 2*ap->maxi) + maxi = 2*ap->maxi; + maxi = roundof(maxi,ARRAY_INCR); + return (maxi>ARRAY_MAX?ARRAY_MAX:maxi); } static struct index_array *array_grow(Namval_t*, struct index_array*,int); @@ -100,19 +153,38 @@ int array_maxindex(Namval_t *np) return(i+1); } -static union Value *array_getup(Namval_t *np, Namarr_t *arp) +static union Value *array_getup(Namval_t *np, Namarr_t *arp, int update) { register struct index_array *ap = (struct index_array*)arp; register union Value *up; - if(!nv_isarray(np)) + int nofree; + if(!arp) return(&np->nvalue); if(is_associative(ap)) - up = (union Value*)((*arp->fun)(np,NIL(char*),0)); + { + Namval_t *mp; + mp = (Namval_t*)((*arp->fun)(np,NIL(char*),NV_ACURRENT)); + if(mp) + { + nofree = nv_isattr(mp,NV_NOFREE); + up = &mp->nvalue; + } + else + return((union Value*)((*arp->fun)(np,NIL(char*),0))); + } else { if(ap->cur >= ap->maxi) errormsg(SH_DICT,ERROR_exit(1),e_subscript,nv_name(np)); up = &(ap->val[ap->cur]); + nofree = array_isbit(ap->bits,ap->cur,ARRAY_NOFREE); + } + if(update) + { + if(nofree) + nv_onattr(np,NV_NOFREE); + else + nv_offattr(np,NV_NOFREE); } return(up); } @@ -128,13 +200,17 @@ static Namval_t *array_find(Namval_t *np,Namarr_t *arp, int flag) register union Value *up; Namval_t *mp; int wasundef; + if(flag&ARRAY_LOOKUP) + ap->header.nelem &= ~ARRAY_NOSCOPE; + else + ap->header.nelem |= ARRAY_NOSCOPE; if(wasundef = ap->header.nelem&ARRAY_UNDEF) { ap->header.nelem &= ~ARRAY_UNDEF; /* delete array is the same as delete array[@] */ if(flag&ARRAY_DELETE) { - nv_putsub(np, NIL(char*), ARRAY_SCAN); + nv_putsub(np, NIL(char*), ARRAY_SCAN|ARRAY_NOSCOPE); ap->header.nelem |= ARRAY_SCAN; } else /* same as array[0] */ @@ -150,14 +226,25 @@ static Namval_t *array_find(Namval_t *np,Namarr_t *arp, int flag) mp = (Namval_t*)((*arp->fun)(np,NIL(char*),NV_ACURRENT)); if(!mp) up = (union Value*)∓ - else if(nv_isattr(mp,NV_CHILD)) + else if(nv_isarray(mp)) { - if(wasundef && nv_isarray(mp->nvalue.np)) - nv_putsub(mp->nvalue.np,NIL(char*),ARRAY_UNDEF); - return(mp->nvalue.np); + if(wasundef) + nv_putsub(mp,NIL(char*),ARRAY_UNDEF); + return(mp); } else + { up = &mp->nvalue; + if(nv_isvtree(mp)) + { + if(!up->cp && flag==ARRAY_ASSIGN) + { + nv_arraychild(np,mp,0); + ap->header.nelem++; + } + return(mp); + } + } } else { @@ -166,7 +253,18 @@ static Namval_t *array_find(Namval_t *np,Namarr_t *arp, int flag) if(ap->cur>=ap->maxi) errormsg(SH_DICT,ERROR_exit(1),e_subscript,nv_name(np)); up = &(ap->val[ap->cur]); - if(up->np && array_isbit(ap->bits,ap->cur)) + if((!up->cp||up->cp==Empty) && nv_type(np) && nv_isvtree(np)) + { + char *cp; + if(!ap->header.table) + ap->header.table = dtopen(&_Nvdisc,Dtoset); + sfprintf(sh.strbuf,"%d",ap->cur); + cp = sfstruse(sh.strbuf); + mp = nv_search(cp, ap->header.table, NV_ADD); + mp->nvenv = (char*)np; + nv_arraychild(np,mp,0); + } + if(up->np && array_isbit(ap->bits,ap->cur,ARRAY_CHILD)) { if(wasundef && nv_isarray(up->np)) nv_putsub(up->np,NIL(char*),ARRAY_UNDEF); @@ -178,97 +276,201 @@ static Namval_t *array_find(Namval_t *np,Namarr_t *arp, int flag) { if(flag!=ARRAY_ASSIGN) return(0); - ap->header.nelem++; + if(!array_covered(np,ap)) + ap->header.nelem++; } return(np); } +#if SHOPT_TYPEDEF +int nv_arraysettype(Namval_t *np, Namval_t *tp, const char *sub, int flags) +{ + Namval_t *nq; + char *av[2]; + int rdonly = nv_isattr(np,NV_RDONLY); + int xtrace = sh_isoption(SH_XTRACE); + Namarr_t *ap = nv_arrayptr(np); + av[1] = 0; + sh.last_table = 0; + if(!ap->table) + ap->table = dtopen(&_Nvdisc,Dtoset); + if(nq = nv_search(sub, ap->table, NV_ADD)) + { + if(!nq->nvfun && nq->nvalue.cp && *nq->nvalue.cp==0) + _nv_unset(nq,NV_RDONLY); + nv_arraychild(np,nq,0); + if(!nv_isattr(tp,NV_BINARY)) + { + sfprintf(sh.strbuf,"%s=%s",nv_name(nq),nv_getval(np)); + av[0] = strdup(sfstruse(sh.strbuf)); + } + if(!nv_clone(tp,nq,flags|NV_NOFREE)) + return(0); + ap->nelem |= ARRAY_SCAN; + if(!rdonly) + nv_offattr(nq,NV_RDONLY); + if(!nv_isattr(tp,NV_BINARY)) + { + if(xtrace) + sh_offoption(SH_XTRACE); + ap->nelem &= ~ARRAY_SCAN; + sh_eval(sh_sfeval(av),0); + ap->nelem |= ARRAY_SCAN; + free((void*)av[0]); + if(xtrace) + sh_onoption(SH_XTRACE); + } + return(1); + } + return(0); +} +#endif /* SHOPT_TYPEDEF */ + + static Namfun_t *array_clone(Namval_t *np, Namval_t *mp, int flags, Namfun_t *fp) { Namarr_t *ap = (Namarr_t*)fp; Namval_t *nq, *mq; char *name, *sub=0; - int nelem = ap->nelem,offset=staktell(); - struct index_array *aq, *ar; + int nelem, skipped=0; + Dt_t *otable=ap->table; + struct index_array *aq = (struct index_array*)ap, *ar; + Shell_t *shp = sh_getinterp(); + if(flags&NV_MOVE) + { + if((flags&NV_COMVAR) && nv_putsub(np,NIL(char*),ARRAY_SCAN)) + { + do + { + if(nq=nv_opensub(np)) + nq->nvenv = (void*)mp; + } + while(nv_nextsub(np)); + } + return(fp); + } + nelem = ap->nelem; if(nelem&ARRAY_NOCLONE) return(0); - if(array_assoc(ap)) - nv_setarray(mp,ap->fun); - else + if((flags&NV_TYPE) && !ap->scope) { - nv_putsub(mp,NIL(char*),ap->nelem); - if(aq=(struct index_array*)nv_arrayptr(mp)) - aq->bits = (unsigned char*)&aq->val[aq->maxi]; + ap = array_scope(np,ap,flags); + return(&ap->hdr); } + ap = (Namarr_t*)nv_clone_disc(fp,0); + if(flags&NV_COMVAR) + { + ap->scope = 0; + ap->nelem = 0; + sh.prev_table = sh.last_table; + sh.prev_root = sh.last_root; + } + if(ap->table) + { + ap->table = dtopen(&_Nvdisc,Dtoset); + if(ap->scope && !(flags&NV_COMVAR)) + { + ap->scope = ap->table; + dtview(ap->table, otable->view); + } + } + mp->nvfun = (Namfun_t*)ap; + mp->nvflag &= NV_MINIMAL; + mp->nvflag |= (np->nvflag&~(NV_MINIMAL|NV_NOFREE)); if(!(nelem&(ARRAY_SCAN|ARRAY_UNDEF)) && (sub=nv_getsub(np))) sub = strdup(sub); ar = (struct index_array*)ap; - nv_onattr(mp,nv_isattr(np,NV_INTEGER|NV_UTOL|NV_LTOU|NV_LJUST|NV_RJUST|NV_ZFILL|NV_BINARY)); - nv_putsub(np,NIL(char*),ARRAY_SCAN); + if(!is_associative(ap)) + ar->bits = (unsigned char*)&ar->val[ar->maxi]; + if(!nv_putsub(np,NIL(char*),ARRAY_SCAN|((flags&NV_COMVAR)?0:ARRAY_NOSCOPE))) + { + if(ap->fun) + (*ap->fun)(np,(char*)np,0); + skipped=1; + goto skip; + } do { - if(array_assoc(ap)) - name = (char*)((*ap->fun)(np,NIL(char*),NV_ANAME)); - else - name = nv_getsub(np); - nv_putsub(mp,name,ARRAY_ADD); - if((!array_assoc(ap) && array_isbit(ar->bits,ar->cur) && (nq=np)) || - (array_assoc(ap) && (nq = (Namval_t*)((*ap->fun)(np,NIL(char*),NV_ACURRENT))) && nv_isattr(nq, NV_CHILD))) - { - sfprintf(stkstd,"%s[%s]",nv_name(mp),name); - stakputc(0); - mq = nv_search(stakptr(offset), sh.var_tree, NV_ADD); - stakseek(offset); - if(mq) + name = nv_getsub(np); + nv_putsub(mp,name,ARRAY_ADD|ARRAY_NOSCOPE); + mq = 0; + if(nq=nv_opensub(np)) + mq = nv_search(name,ap->table,NV_ADD); + if(nq && (flags&NV_COMVAR) && nv_isvtree(nq)) + { + mq->nvalue.cp = 0; + if(!is_associative(ap)) + ar->val[ar->cur].np = mq; + nv_clone(nq,mq,flags); + } + else if(flags&NV_ARRAY) + { + if((flags&NV_NOFREE) && !is_associative(ap)) + array_setbit(aq->bits,aq->cur,ARRAY_NOFREE); + else if(nq && (flags&NV_NOFREE)) { - nv_clone(nq->nvalue.np,mq,0); - if(array_assoc(ap)) - { - nq = (Namval_t*)((*ap->fun)(mp,NIL(char*),NV_ACURRENT)); - nq->nvalue.np = mp; - nv_onattr(nq,NV_CHILD); - } - else if(aq) - { - array_setbit(aq->bits,aq->cur); - aq->val[aq->cur].np = mq; - } + mq->nvalue = nq->nvalue; + nv_onattr(nq,NV_NOFREE); } } else if(nv_isattr(np,NV_INTEGER)) { Sfdouble_t d= nv_getnum(np); + if(!is_associative(ap)) + ar->val[ar->cur].cp = 0; nv_putval(mp,(char*)&d,NV_LDOUBLE); } else + { + if(!is_associative(ap)) + ar->val[ar->cur].cp = 0; nv_putval(mp,nv_getval(np),NV_RDONLY); + } + aq->header.nelem |= ARRAY_NOSCOPE; } while(nv_nextsub(np)); +skip: if(sub) { - nv_putsub(np,sub,0L); + if(!skipped) + nv_putsub(np,sub,0L); free((void*)sub); } - ap->nelem = nelem; - ((Namarr_t*)mp->nvfun)->nelem = nelem; - return(nv_stack(mp,(Namfun_t*)0)); + aq->header.nelem = ap->nelem = nelem; + return(&ap->hdr); } static char *array_getval(Namval_t *np, Namfun_t *disc) { - register Namarr_t *ap = (Namarr_t*)disc; + register Namarr_t *aq,*ap = (Namarr_t*)disc; register Namval_t *mp; if((mp=array_find(np,ap,ARRAY_LOOKUP))!=np) + { + if(!mp && !is_associative(ap) && (aq=(Namarr_t*)ap->scope)) + { + array_syncsub(aq,ap); + if((mp=array_find(np,aq,ARRAY_LOOKUP))==np) + return(nv_getv(np,&aq->hdr)); + } return(mp?nv_getval(mp):0); + } return(nv_getv(np,&ap->hdr)); } static Sfdouble_t array_getnum(Namval_t *np, Namfun_t *disc) { - register Namarr_t *ap = (Namarr_t*)disc; + register Namarr_t *aq,*ap = (Namarr_t*)disc; register Namval_t *mp; if((mp=array_find(np,ap,ARRAY_LOOKUP))!=np) + { + if(!mp && !is_associative(ap) && (aq=(Namarr_t*)ap->scope)) + { + array_syncsub(aq,ap); + if((mp=array_find(np,aq,ARRAY_LOOKUP))==np) + return(nv_getn(np,&aq->hdr)); + } return(mp?nv_getnum(mp):0); + } return(nv_getn(np,&ap->hdr)); } @@ -278,52 +480,113 @@ static void array_putval(Namval_t *np, const char *string, int flags, Namfun_t * register union Value *up; register Namval_t *mp; register struct index_array *aq = (struct index_array*)ap; + int scan,nofree = nv_isattr(np,NV_NOFREE); do { mp = array_find(np,ap,string?ARRAY_ASSIGN:ARRAY_DELETE); + scan = ap->nelem&ARRAY_SCAN; if(mp && mp!=np) + { + if(!is_associative(ap) && string && !nv_type(np) && nv_isvtree(mp)) + { + if(!nv_isattr(np,NV_NOFREE)) + _nv_unset(mp,flags&NV_RDONLY); + array_clrbit(aq->bits,aq->cur,ARRAY_CHILD); + aq->val[aq->cur].cp = 0; + if(!nv_isattr(mp,NV_NOFREE)) + nv_delete(mp,ap->table,0); + goto skip; + } nv_putval(mp, string, flags); + if(string) + { +#if SHOPT_TYPEDEF + if(ap->hdr.type && ap->hdr.type!=nv_type(mp)) + nv_arraysettype(np,ap->hdr.type,nv_getsub(np),0); +#endif /* SHOPT_TYPEDEF */ + continue; + } + ap->nelem |= scan; + } if(!string) { if(mp) { - if(mp!=np) - { - dtdelete(sh.var_tree,(void*)mp); - free((void*)mp); - } if(is_associative(ap)) + { (*ap->fun)(np,NIL(char*),NV_ADELETE); - else if(mp!=np) + np->nvalue.cp = 0; + } + else { - array_clrbit(aq->bits,aq->cur); - aq->val[aq->cur].cp = 0; + if(mp!=np) + { + array_clrbit(aq->bits,aq->cur,ARRAY_CHILD); + aq->val[aq->cur].cp = 0; + nv_delete(mp,ap->table,0); + } + if(!array_covered(np,(struct index_array*)ap)) + ap->nelem--; } - ap->nelem--; } if(array_elem(ap)==0 && ((ap->nelem&ARRAY_SCAN) || !is_associative(ap))) { if(is_associative(ap)) (*ap->fun)(np, NIL(char*), NV_AFREE); + else if(ap->table) + dtclose(ap->table); nv_offattr(np,NV_ARRAY); } - if(!mp || mp!=np) + if(!mp || mp!=np || is_associative(ap)) continue; } + skip: /* prevent empty string from being deleted */ - if(np->nvalue.cp == Empty) - np->nvalue.cp = 0; + up = array_getup(np,ap,!nofree); + if(up->cp == Empty) + up->cp = 0; + if(nv_isarray(np)) + np->nvalue.up = up; nv_putv(np,string,flags,&ap->hdr); - up = array_getup(np,ap); - up->cp = np->nvalue.cp; + if(!is_associative(ap)) + { + if(string) + array_clrbit(aq->bits,aq->cur,ARRAY_NOFREE); + else if(mp==np) + aq->val[aq->cur].cp = 0; + } +#if SHOPT_TYPEDEF + if(string && ap->hdr.type && nv_isvtree(np)) + nv_arraysettype(np,ap->hdr.type,nv_getsub(np),0); +#endif /* SHOPT_TYPEDEF */ } while(!string && nv_nextsub(np)); + if(ap) + ap->nelem &= ~ARRAY_NOSCOPE; + if(nofree) + nv_onattr(np,NV_NOFREE); + else + nv_offattr(np,NV_NOFREE); if(!string && !nv_isattr(np,NV_ARRAY)) { Namfun_t *nfp; - if(nfp = nv_disc(np,(Namfun_t*)ap,NV_POP)) + if(!is_associative(ap) && aq->xp) + { + _nv_unset(nv_namptr(aq->xp,0),NV_RDONLY); + free((void*)aq->xp); + } + if((nfp = nv_disc(np,(Namfun_t*)ap,NV_POP)) && !(nfp->nofree&1)) free((void*)nfp); + if(!nv_isnull(np)) + { + nv_onattr(np,NV_NOFREE); + _nv_unset(np,flags); + } + if(np->nvalue.cp==Empty) + np->nvalue.cp = 0; } + if(!string && (flags&NV_TYPE)) + array_unscope(np,ap); } static const Namdisc_t array_disc = @@ -337,6 +600,21 @@ static const Namdisc_t array_disc = array_clone }; +static void array_copytree(Namval_t *np, Namval_t *mp) +{ + char *val; + Namfun_t *fp = nv_disc(np,NULL,NV_POP); + nv_offattr(np,NV_ARRAY); + nv_clone(np,mp,0); + np->nvalue.up = &mp->nvalue; + val = sfstruse(sh.strbuf); + fp->nofree &= ~1; + nv_disc(np,(Namfun_t*)fp, NV_FIRST); + fp->nofree |= 1; + nv_onattr(np,NV_ARRAY); + mp->nvenv = (char*)np; +} + /* * Increase the size of the indexed array of elements in <arp> * so that <maxi> is a legal index. If <arp> is 0, an array @@ -347,61 +625,105 @@ static const Namdisc_t array_disc = static struct index_array *array_grow(Namval_t *np, register struct index_array *arp,int maxi) { register struct index_array *ap; - register int i=0; - register int newsize = arsize(maxi+1); + register int i; + register int newsize = arsize(arp,maxi+1); if (maxi >= ARRAY_MAX) errormsg(SH_DICT,ERROR_exit(1),e_subscript, fmtbase((long)maxi,10,0)); - ap = new_of(struct index_array,(newsize-1)*sizeof(union Value*)+newsize/CHAR_BIT); - memset((void*)ap,0,sizeof(*ap)); + i = (newsize-1)*sizeof(union Value*)+newsize; + ap = new_of(struct index_array,i); + memset((void*)ap,0,sizeof(*ap)+i); ap->maxi = newsize; ap->cur = maxi; ap->bits = (unsigned char*)&ap->val[newsize]; - memset(ap->bits, 0, newsize/CHAR_BIT); + memset(ap->bits, 0, newsize); if(arp) { ap->header = arp->header; - for(;i < arp->maxi;i++) + ap->header.hdr.dsize = sizeof(*ap) + i; + for(i=0;i < arp->maxi;i++) ap->val[i].cp = arp->val[i].cp; - memcpy(ap->bits, arp->bits, (arp->maxi/CHAR_BIT)); + memcpy(ap->bits, arp->bits, arp->maxi); array_setptr(np,arp,ap); free((void*)arp); } else { + Namval_t *mp=0; + ap->header.hdr.dsize = sizeof(*ap) + i; + i = 0; ap->header.fun = 0; - if((ap->val[0].cp=np->nvalue.cp)) - i++; - else if(nv_hasdisc(np,&array_disc)) - { - Namval_t *mp; - int offset = staktell(); - sfprintf(stkstd,"%s[0]",nv_name(np)); - stakputc(0); - mp = nv_search(stakptr(offset), sh.var_tree, NV_ADD); - stakseek(offset); + if(nv_isnull(np) && nv_isattr(np,NV_NOFREE)) + { + i = ARRAY_TREE; + nv_offattr(np,NV_NOFREE); + } + if(np->nvalue.cp==Empty) + np->nvalue.cp=0; + if(nv_hasdisc(np,&array_disc) || nv_isvtree(np)) + { + ap->header.table = dtopen(&_Nvdisc,Dtoset); + mp = nv_search("0", ap->header.table, 0); + if(mp && nv_isnull(mp)) { - nv_clone(np,mp,0); + Namfun_t *fp; ap->val[0].np = mp; - array_setbit(ap->bits,0); + array_setbit(ap->bits,0,ARRAY_CHILD); + for(fp=np->nvfun; fp && !fp->disc->readf; fp=fp->next); + if(fp) + (*fp->disc->readf)(mp,(Sfio_t*)0,0,fp); + i++; } - i++; } + else if((ap->val[0].cp=np->nvalue.cp)) + i++; else if(nv_isattr(np,NV_INTEGER)) { Sfdouble_t d= nv_getnum(np); i++; } ap->header.nelem = i; - ap->header.hdr.nofree = 1; ap->header.hdr.disc = &array_disc; - nv_disc(np,(Namfun_t*)ap, NV_LAST); + nv_disc(np,(Namfun_t*)ap, NV_FIRST); + nv_onattr(np,NV_ARRAY); + if(mp) + { + array_copytree(np,mp); + ap->header.hdr.nofree &= ~1; + } } for(;i < newsize;i++) ap->val[i].cp = 0; return(ap); } +int nv_atypeindex(Namval_t *np, const char *tname) +{ + Namval_t *tp; + int offset = staktell(); + int n = strlen(tname)-1; + sfprintf(stkstd,"%s.%.*s%c",NV_CLASS,n,tname,0); + tp = nv_open(stakptr(offset), sh.var_tree, NV_NOADD|NV_VARNAME); + stakseek(offset); + if(tp) + { + struct index_array *ap = (struct index_array*)nv_arrayptr(np); + if(!nv_hasdisc(tp,&ENUM_disc)) + errormsg(SH_DICT,ERROR_exit(1),e_notenum,tp->nvname); + if(!ap) + ap = array_grow(np,ap,1); + ap->xp = calloc(NV_MINSZ,1); + np = nv_namptr(ap->xp,0); + np->nvname = tp->nvname; + nv_onattr(np,NV_MINIMAL); + nv_clone(tp,np,NV_NOFREE); + nv_offattr(np,NV_RDONLY); + return(1); + } + errormsg(SH_DICT,ERROR_exit(1),e_unknowntype, n,tname); + return(0); +} + Namarr_t *nv_arrayptr(register Namval_t *np) { if(nv_isattr(np,NV_ARRAY)) @@ -446,7 +768,6 @@ static Namarr_t *nv_changearray(Namval_t *np, void *(*fun)(Namval_t*,const char* } nv_putsub(np, string_index, ARRAY_ADD); up = (union Value*)((*ap->fun)(np,NIL(char*),0)); - ap->nelem++; up->cp = save_ap->val[dot].cp; save_ap->val[dot].cp = 0; } @@ -463,7 +784,9 @@ static Namarr_t *nv_changearray(Namval_t *np, void *(*fun)(Namval_t*,const char* Namarr_t *nv_setarray(Namval_t *np, void *(*fun)(Namval_t*,const char*,int)) { register Namarr_t *ap; - char *value; + char *value=0; + Namfun_t *fp; + int nelem = 0; if(fun && (ap = nv_arrayptr(np))) { /* @@ -474,17 +797,29 @@ Namarr_t *nv_setarray(Namval_t *np, void *(*fun)(Namval_t*,const char*,int)) ap = nv_changearray(np, fun); return(ap); } - value = nv_getval(np); + if(nv_isnull(np) && nv_isattr(np,NV_NOFREE)) + { + nelem = ARRAY_TREE; + nv_offattr(np,NV_NOFREE); + } + if(!(fp=nv_isvtree(np))) + value = nv_getval(np); if(fun && !ap && (ap = (Namarr_t*)((*fun)(np, NIL(char*), NV_AINIT)))) { /* check for preexisting initialization and save */ - ap->nelem = 0; + ap->nelem = nelem; ap->fun = fun; nv_onattr(np,NV_ARRAY); - if(value) + if(fp || value) { nv_putsub(np, "0", ARRAY_ADD); - nv_putval(np, value, 0); + if(value) + nv_putval(np, value, 0); + else + { + Namval_t *mp = (Namval_t*)((*fun)(np,NIL(char*),NV_ACURRENT)); + array_copytree(np,mp); + } } return(ap); } @@ -496,27 +831,38 @@ Namarr_t *nv_setarray(Namval_t *np, void *(*fun)(Namval_t*,const char*,int)) */ Namval_t *nv_arraychild(Namval_t *np, Namval_t *nq, int c) { - register Namarr_t *ap = nv_arrayptr(np); - union Value *up; - if(!(up = array_getup(np,ap))) - return((Namval_t*)0); + Namfun_t *fp; + register Namarr_t *ap = nv_arrayptr(np); + union Value *up; + Namval_t *tp; if(!nq) - return(array_find(np,ap, ARRAY_LOOKUP)); + return(ap?array_find(np,ap, ARRAY_LOOKUP):0); + if(!ap) + { + nv_putsub(np, NIL(char*), ARRAY_FILL); + ap = nv_arrayptr(np); + } + if(!(up = array_getup(np,ap,0))) + return((Namval_t*)0); np->nvalue.cp = up->cp; - ap->nelem |= ARRAY_NOCLONE; - nv_clone(np, nq, NV_NODISC); - nv_offattr(nq,NV_ARRAY); - ap->nelem &= ~ARRAY_NOCLONE; - if(ap->fun) + if((tp=nv_type(np)) || c) { - up->np = (Namval_t*)((*ap->fun)(np,NIL(char*),NV_ACURRENT)); - nv_onattr(up->np, NV_CHILD); - (up->np)->nvalue.np = nq; + ap->nelem |= ARRAY_NOCLONE; + nq->nvenv = (char*)np; + if(c=='t') + nv_clone(tp,nq, 0); + else + nv_clone(np, nq, NV_NODISC); + nv_offattr(nq,NV_ARRAY); + ap->nelem &= ~ARRAY_NOCLONE; } - else + nq->nvenv = (char*)np; + if((fp=nq->nvfun) && fp->disc && fp->disc->setdisc && (fp = nv_disc(nq,fp,NV_POP))) + free((void*)fp); + if(!ap->fun) { struct index_array *aq = (struct index_array*)ap; - array_setbit(aq->bits,aq->cur); + array_setbit(aq->bits,aq->cur,ARRAY_CHILD); up->np = nq; } if(c=='.') @@ -531,33 +877,42 @@ Namval_t *nv_arraychild(Namval_t *np, Namval_t *nq, int c) */ int nv_nextsub(Namval_t *np) { - register struct index_array *ap = (struct index_array*)nv_arrayptr(np); - register unsigned dot; + register struct index_array *ap = (struct index_array*)nv_arrayptr(np); + register unsigned dot; + struct index_array *aq=0, *ar=0; if(!ap || !(ap->header.nelem&ARRAY_SCAN)) return(0); if(is_associative(ap)) { - struct assoc_array *aq; - if(aq=(*ap->header.fun)(np,NIL(char*),NV_ANEXT)) + Namval_t *nq; + if(nq=(*ap->header.fun)(np,NIL(char*),NV_ANEXT)) { - if(nv_isattr(aq->cur,NV_CHILD)) - nv_putsub(aq->cur->nvalue.np,NIL(char*),ARRAY_UNDEF); + if(nv_isattr(nq,NV_CHILD)) + nv_putsub(nq->nvalue.np,NIL(char*),ARRAY_UNDEF); return(1); } ap->header.nelem &= ~(ARRAY_SCAN|ARRAY_NOCHILD); return(0); } + if(!(ap->header.nelem&ARRAY_NOSCOPE)) + ar = (struct index_array*)ap->header.scope; for(dot=ap->cur+1; dot < (unsigned)ap->maxi; dot++) { - if(ap->val[dot].cp) + aq = ap; + if(!ap->val[dot].cp && !(ap->header.nelem&ARRAY_NOSCOPE)) + { + if(!(aq=ar) || dot>=(unsigned)aq->maxi) + continue; + } + if(aq->val[dot].cp) { ap->cur = dot; - if(array_isbit(ap->bits, dot)) + if(array_isbit(aq->bits, dot,ARRAY_CHILD)) { - - if(ap->header.nelem&ARRAY_NOCHILD) + Namval_t *mp = aq->val[dot].np; + if((aq->header.nelem&ARRAY_NOCHILD) && nv_isvtree(mp)) continue; - nv_putsub(ap->val[dot].np,NIL(char*),ARRAY_UNDEF); + nv_putsub(mp,NIL(char*),ARRAY_UNDEF); } return(1); } @@ -585,7 +940,18 @@ Namval_t *nv_putsub(Namval_t *np,register char *sp,register long mode) if(!ap || !ap->header.fun) { if(sp) - size = (int)sh_arith((char*)sp); + { + if(ap && ap->xp && !strmatch(sp,"+([0-9])")) + { + Namval_t *mp = nv_namptr(ap->xp,0); + nv_putval(mp, sp,0); + size = nv_getnum(mp); + } + else + size = (int)sh_arith((char*)sp); + } + if(size <0 && ap) + size += array_maxindex(np); if(size >= ARRAY_MAX || (size < 0)) { errormsg(SH_DICT,ERROR_exit(1),e_subscript, nv_name(np)); @@ -598,24 +964,42 @@ Namval_t *nv_putsub(Namval_t *np,register char *sp,register long mode) if(sh.subshell) np = sh_assignok(np,1); ap = array_grow(np, ap,size); - nv_onattr(np,NV_ARRAY); } ap->header.nelem &= ~ARRAY_UNDEF; - ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF)); + ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF|ARRAY_NOSCOPE)); +#if 0 + if(array_isbit(ap->bits,oldsize,ARRAY_CHILD)) + mp = ap->val[oldsize].np; + if(size != oldsize && mp->nvalue.cp) + { + Namfun_t *nfp; + for(nfp=np->nvfun; nfp; nfp=nfp->next) + { + if(nfp->disc && nfp->disc->readf) + { + (*nfp->disc->readf)(mp,(Sfio_t*)0,0,nfp); + break; + } + } + } +#endif ap->cur = size; - if((mode&ARRAY_SCAN) && !ap->val[size].cp && !nv_nextsub(np)) + if((mode&ARRAY_SCAN) && (ap->cur--,!nv_nextsub(np))) np = 0; - if(mode&ARRAY_FILL) + if(mode&(ARRAY_FILL|ARRAY_ADD)) { if(!(mode&ARRAY_ADD)) { int n; - for(n=0; n < size; n++) + for(n=0; n <= size; n++) { if(!ap->val[n].cp) + { ap->val[n].cp = Empty; + if(!array_covered(np,ap)) + ap->header.nelem++; + } } - ap->header.nelem = n|(ap->header.nelem&(ARRAY_SCAN|ARRAY_UNDEF)); if(n=ap->maxi-ap->maxi) memset(&ap->val[size],0,n*sizeof(union Value)); } @@ -624,37 +1008,34 @@ Namval_t *nv_putsub(Namval_t *np,register char *sp,register long mode) if(sh.subshell) np = sh_assignok(np,1); ap->val[size].cp = Empty; - ap->header.nelem++; + if(!array_covered(np,ap)) + ap->header.nelem++; } } else if(!(mode&ARRAY_SCAN)) { ap->header.nelem &= ~ARRAY_SCAN; - if(array_isbit(ap->bits,size)) + if(array_isbit(ap->bits,size,ARRAY_CHILD)) nv_putsub(ap->val[size].np,NIL(char*),ARRAY_UNDEF); + if(sp && !(mode&ARRAY_ADD) && !ap->val[size].cp) + np = 0; } return((Namval_t*)np); } ap->header.nelem &= ~ARRAY_UNDEF; if(!(mode&ARRAY_FILL)) ap->header.nelem &= ~ARRAY_SCAN; - ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF)); + ap->header.nelem |= (mode&(ARRAY_SCAN|ARRAY_NOCHILD|ARRAY_UNDEF|ARRAY_NOSCOPE)); if(sp) { - union Value *up; if(mode&ARRAY_SETSUB) { (*ap->header.fun)(np, sp, NV_ASETSUB); return(np); } - up = (union Value*)(*ap->header.fun)(np, sp, (mode&ARRAY_ADD)?NV_AADD:0); - if(up && !up->cp && (mode&ARRAY_ADD) && (mode&ARRAY_FILL)) - { - if(sh.subshell) - np = sh_assignok(np,1); - up->cp = Empty; - ap->header.nelem++; - } + (*ap->header.fun)(np, sp, (mode&ARRAY_ADD)?NV_AADD:0); + if(!(mode&(ARRAY_SCAN|ARRAY_ADD)) && !(*ap->header.fun)(np,NIL(char*),NV_ACURRENT)) + np = 0; } else if(mode&ARRAY_SCAN) (*ap->header.fun)(np,(char*)np,0); @@ -695,7 +1076,11 @@ char *nv_endsubscript(Namval_t *np, register char *cp, int mode) sh_trim(sp=stakptr(count)); } if(mode && np) - nv_putsub(np, sp, ARRAY_ADD|(cp[1]?ARRAY_FILL:mode&ARRAY_FILL)); + { + if((mode&NV_ASSIGN) && (cp[1]=='=' || cp[1]=='+')) + mode |= NV_ADD; + nv_putsub(np, sp, ((mode&NV_ADD)?ARRAY_ADD:0)|(cp[1]&&(mode&NV_ADD)?ARRAY_FILL:mode&ARRAY_FILL)); + } if(quoted) stakseek(count); *cp++ = c; @@ -706,8 +1091,13 @@ char *nv_endsubscript(Namval_t *np, register char *cp, int mode) Namval_t *nv_opensub(Namval_t* np) { register struct index_array *ap = (struct index_array*)nv_arrayptr(np); - if(ap && is_associative(ap)) - return((Namval_t*)((*ap->header.fun)(np,NIL(char*),NV_ACURRENT))); + if(ap) + { + if(is_associative(ap)) + return((Namval_t*)((*ap->header.fun)(np,NIL(char*),NV_ACURRENT))); + else if(array_isbit(ap->bits,ap->cur,ARRAY_CHILD)) + return(ap->val[ap->cur].np); + } return(NIL(Namval_t*)); } @@ -721,6 +1111,12 @@ char *nv_getsub(Namval_t* np) return(NIL(char*)); if(is_associative(ap)) return((char*)((*ap->header.fun)(np,NIL(char*),NV_ANAME))); + if(ap->xp) + { + np = nv_namptr(ap->xp,0); + np->nvalue.s = ap->cur; + return(nv_getval(np)); + } if((dot = ap->cur)==0) *--cp = '0'; else while(n=dot) @@ -738,14 +1134,31 @@ char *nv_getsub(Namval_t* np) int nv_aindex(register Namval_t* np) { Namarr_t *ap = nv_arrayptr(np); - if(!ap || is_associative(ap)) + if(!ap) + return(0); + else if(is_associative(ap)) return(-1); return(((struct index_array*)(ap))->cur&ARRAY_MASK); } +int nv_arraynsub(register Namarr_t* ap) +{ + return(array_elem(ap)); +} + +int nv_aimax(register Namval_t* np) +{ + struct index_array *ap = (struct index_array*)nv_arrayptr(np); + int sub = -1; + if(!ap || is_associative(&ap->header)) + return(-1); + sub = ap->maxi; + while(--sub>0 && ap->val[sub].cp==0); + return(sub); +} /* - * This is the default implementation for associate arrays + * This is the default implementation for associative arrays */ void *nv_associative(register Namval_t *np,const char *sp,int mode) { @@ -756,42 +1169,52 @@ void *nv_associative(register Namval_t *np,const char *sp,int mode) case NV_AINIT: if(ap = (struct assoc_array*)calloc(1,sizeof(struct assoc_array))) { - ap->table = dtopen(&_Nvdisc,Dtbag); + ap->header.table = dtopen(&_Nvdisc,Dtoset); ap->cur = 0; ap->pos = 0; ap->header.hdr.disc = &array_disc; - ap->header.hdr.nofree = 1; - nv_disc(np,(Namfun_t*)ap, NV_LAST); + nv_disc(np,(Namfun_t*)ap, NV_FIRST); + ap->header.hdr.dsize = sizeof(struct assoc_array); + ap->header.hdr.nofree &= ~1; } return((void*)ap); case NV_ADELETE: if(ap->cur) { - if(nv_isattr(ap->cur,NV_NOFREE)) - nv_offattr(ap->cur,NV_NOFREE); - else - { - dtdelete(ap->table,(void*)ap->cur); - free((void*)ap->cur); - ap->cur = 0; - } + if(!ap->header.scope || (Dt_t*)ap->header.scope==ap->header.table || !nv_search(ap->cur->nvname,(Dt_t*)ap->header.scope,0)) + ap->header.nelem--; + _nv_unset(ap->cur,NV_RDONLY); + nv_delete(ap->cur,ap->header.table,0); + ap->cur = 0; } return((void*)ap); case NV_AFREE: ap->pos = 0; - dtclose(ap->table); + if(ap->header.scope) + { + ap->header.table = dtview(ap->header.table,(Dt_t*)0); + dtclose(ap->header.scope); + ap->header.scope = 0; + } + else + dtclose(ap->header.table); return((void*)ap); case NV_ANEXT: if(!ap->pos) { + if((ap->header.nelem&ARRAY_NOSCOPE) && ap->header.scope && dtvnext(ap->header.table)) + { + ap->header.scope = dtvnext(ap->header.table); + ap->header.table->view = 0; + } if(!(ap->pos=ap->cur)) - ap->pos = (Namval_t*)dtfirst(ap->table); + ap->pos = (Namval_t*)dtfirst(ap->header.table); } else ap->pos = ap->nextpos; for(;ap->cur=ap->pos; ap->pos=ap->nextpos) { - ap->nextpos = (Namval_t*)dtnext(ap->table,ap->pos); + ap->nextpos = (Namval_t*)dtnext(ap->header.table,ap->pos); if(ap->cur->nvalue.cp) { if((ap->header.nelem&ARRAY_NOCHILD) && nv_isattr(ap->cur,NV_CHILD)) @@ -799,29 +1222,66 @@ void *nv_associative(register Namval_t *np,const char *sp,int mode) return((void*)ap); } } + if((ap->header.nelem&ARRAY_NOSCOPE) && ap->header.scope && !dtvnext(ap->header.table)) + { + ap->header.table->view = (Dt_t*)ap->header.scope; + ap->header.scope = ap->header.table; + } return(NIL(void*)); case NV_ASETSUB: ap->cur = (Namval_t*)sp; - /* FALL THROUGH*/ + return((void*)ap->cur); case NV_ACURRENT: + if(ap->cur) + ap->cur->nvenv = (char*)np; return((void*)ap->cur); case NV_ANAME: if(ap->cur) - return((void*)nv_name(ap->cur)); + return((void*)ap->cur->nvname); return(NIL(void*)); default: if(sp) { + Namval_t *mp=0; + ap->cur = 0; if(sp==(char*)np) - { - ap->cur = 0; return(0); + type = nv_isattr(np,NV_PUBLIC&~(NV_ARRAY|NV_CHILD|NV_MINIMAL)); + if(mode) + mode = NV_ADD|HASH_NOSCOPE; + else if(ap->header.nelem&ARRAY_NOSCOPE) + mode = HASH_NOSCOPE; + if(*sp==0 && (mode&NV_ADD)) + sfprintf(sfstderr,"adding empty subscript\n"); + if(sh.subshell && (mp=nv_search(sp,ap->header.table,0)) && nv_isnull(mp)) + ap->cur = mp; + if((mp || (mp=nv_search(sp,ap->header.table,mode))) && nv_isnull(mp) && (mode&NV_ADD)) + { + nv_onattr(mp,type); + mp->nvenv = (char*)np; + if((mode&NV_ADD) && nv_type(np)) + nv_arraychild(np,mp,0); + if(sh.subshell) + np = sh_assignok(np,1); + if(!ap->header.scope || !nv_search(sp,dtvnext(ap->header.table),0)) + ap->header.nelem++; + if(nv_isnull(mp)) + { + if(ap->header.nelem&ARRAY_TREE) + nv_setvtree(mp); + mp->nvalue.cp = Empty; + } + } + else if(ap->header.nelem&ARRAY_SCAN) + { + Namval_t fake; + fake.nvname = (char*)sp; + ap->pos = mp = (Namval_t*)dtprev(ap->header.table,&fake); + ap->nextpos = (Namval_t*)dtnext(ap->header.table,mp); } - else if(!(ap->header.nelem&ARRAY_SCAN)) + np = mp; + if(ap->pos != np && !(ap->header.nelem&ARRAY_SCAN)) ap->pos = 0; - type = nv_isattr(np,NV_PUBLIC&~(NV_ARRAY|NV_CHILD)); - if((np=nv_search(sp,ap->table,mode?NV_ADD:0)) && nv_isnull(np)) - nv_onattr(np,type); ap->cur = np; } if(ap->cur) @@ -837,19 +1297,21 @@ void *nv_associative(register Namval_t *np,const char *sp,int mode) void nv_setvec(register Namval_t *np,int append,register int argc,register char *argv[]) { int arg0=0; - struct index_array *ap=0; + struct index_array *ap=0,*aq; if(nv_isarray(np)) { ap = (struct index_array*)nv_arrayptr(np); if(ap && is_associative(ap)) - errormsg(SH_DICT,ERROR_exit(1),"cannot append index array to associate array %s",nv_name(np)); + errormsg(SH_DICT,ERROR_exit(1),"cannot append index array to associative array %s",nv_name(np)); } if(append) { if(ap) { + if(!(aq = (struct index_array*)ap->header.scope)) + aq = ap; arg0 = ap->maxi; - while(--arg0>0 && ap->val[arg0].cp==0); + while(--arg0>0 && ap->val[arg0].cp==0 && aq->val[arg0].cp==0); arg0++; } else if(!nv_isnull(np)) @@ -857,8 +1319,7 @@ void nv_setvec(register Namval_t *np,int append,register int argc,register char } while(--argc >= 0) { - if((argc+arg0)>0 || nv_isattr(np,NV_ARRAY)) - nv_putsub(np,NIL(char*),(long)argc+arg0); + nv_putsub(np,NIL(char*),(long)argc+arg0|ARRAY_FILL|ARRAY_ADD); nv_putval(np,argv[argc],0); } } diff --git a/usr/src/lib/libshell/common/sh/bash.c b/usr/src/lib/libshell/common/sh/bash.c index ecb10472b8..4b2def3b22 100644 --- a/usr/src/lib/libshell/common/sh/bash.c +++ b/usr/src/lib/libshell/common/sh/bash.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -38,8 +38,6 @@ #define BASH_VERSION BASH_MAJOR "." BASH_MINOR "." BASH_PATCH "(" BASH_BUILD ")-" BASH_RELEASE -void sh_applyopts(Shopt_t); - extern const char bash_pre_rc[]; static char *login_files[4]; @@ -50,8 +48,6 @@ const char sh_bash1[] = "[P?Do not follow symbolic links, use physical directory structure " "instead. Only available in bash compatibility mode.]"; const char sh_bash2[] = -"[l:login?Make the shell act as if it had been invoked as a login shell. " -"Only available if invoked as \bbash\b.]" "[O]:?[shopt_option?\ashopt_option\a is one of the shell options accepted by " "the \bshopt\b builtin. If \ashopt_option\a is present, \b-O\b sets " "the value of that option; \b+O\b unsets it. If \ashopt_option\a is " @@ -66,12 +62,9 @@ const char sh_bash2[] = "[03:profile?Read either the system-wide startup file or any of the " "personal initialization files. On by default for interactive " "shells. Only available if invoked as \bbash\b.]" -"[04:rc?Read and execute the personal initialization file " - "\b$HOME/.bashrc\b. On by default for interactive shells. Only " - "available if invoked as \bbash\b.]" -"[05:posix?If invoked as \bbash\b, turn on POSIX compatibility. \bBash\b in " +"[04:posix?If invoked as \bbash\b, turn on POSIX compatibility. \bBash\b in " "POSIX mode is not the same as \bksh\b.]" -"[06:version?Print version number and exit.]"; +"[05:version?Print version number and exit.]"; const char sh_optshopt[] = "+[-1c?\n@(#)$Id: shopt (AT&T Research) 2003-02-13 $\n]" @@ -293,7 +286,7 @@ int b_shopt(int argc,register char *argv[],void *extra) else if(setflag&SET_UNSET) for(n=0;n<4;n++) newflags.v[n] &= ~opt.v[n]; - sh_applyopts(newflags); + sh_applyopts(shp,newflags); shp->options = newflags; if(is_option(&newflags,SH_XTRACE)) sh_trace(argv,1); @@ -317,6 +310,7 @@ int b_shopt(int argc,register char *argv[],void *extra) void bash_init(int mode) { + Shell_t *shp = &sh; Sfio_t *iop; Namval_t *np; int n=0,xtrace,verbose; @@ -326,7 +320,7 @@ void bash_init(int mode) { /* termination code */ if(sh_isoption(SH_LOGIN_SHELL) && !sh_isoption(SH_POSIX)) - sh_source(&sh, NiL, sh_mactry((char*)e_bash_logout)); + sh_source(shp, NiL, sh_mactry(shp,(char*)e_bash_logout)); return; } @@ -341,7 +335,7 @@ void bash_init(int mode) sh_onoption(SH_CMDHIST); sh_onoption(SH_LITHIST); sh_onoption(SH_NOEMPTYCMDCOMPL); - if(sh.login_sh==2) + if(shp->login_sh==2) sh_onoption(SH_LOGIN_SHELL); if(strcmp(astconf("CONFORMANCE",0,0),"standard")==0) sh_onoption(SH_POSIX); @@ -360,13 +354,13 @@ void bash_init(int mode) /* set up some variables needed for --version * needs to go here because --version option is parsed before the init script. */ - if(np=nv_open("HOSTTYPE",sh.var_tree,0)) + if(np=nv_open("HOSTTYPE",shp->var_tree,0)) nv_putval(np, BASH_HOSTTYPE, NV_NOFREE); - if(np=nv_open("MACHTYPE",sh.var_tree,0)) + if(np=nv_open("MACHTYPE",shp->var_tree,0)) nv_putval(np, BASH_MACHTYPE, NV_NOFREE); - if(np=nv_open("BASH_VERSION",sh.var_tree,0)) + if(np=nv_open("BASH_VERSION",shp->var_tree,0)) nv_putval(np, BASH_VERSION, NV_NOFREE); - if(np=nv_open("BASH_VERSINFO",sh.var_tree,0)) + if(np=nv_open("BASH_VERSINFO",shp->var_tree,0)) { char *argv[7]; argv[0] = BASH_MAJOR; @@ -385,7 +379,7 @@ void bash_init(int mode) /* rest of init stage */ /* restrict BASH_ENV */ - if(np=nv_open("BASH_ENV",sh.var_tree,0)) + if(np=nv_open("BASH_ENV",shp->var_tree,0)) { const Namdisc_t *dp = nv_discfun(NV_DCRESTRICT); Namfun_t *fp = calloc(dp->dsize,1); @@ -394,7 +388,7 @@ void bash_init(int mode) } /* open GLOBIGNORE node */ - if(np=nv_open("GLOBIGNORE",sh.var_tree,0)) + if(np=nv_open("GLOBIGNORE",shp->var_tree,0)) { const Namdisc_t *dp = &SH_GLOBIGNORE_disc; Namfun_t *fp = calloc(dp->dsize,1); @@ -404,7 +398,7 @@ void bash_init(int mode) /* set startup files */ n=0; - if(!sh_isoption(SH_NOPROFILE)) + if(sh_isoption(SH_LOGIN_SHELL)) { if(!sh_isoption(SH_POSIX)) { @@ -413,13 +407,13 @@ void bash_init(int mode) } login_files[n++] = (char*)e_profile; } - sh.login_files = login_files; + shp->login_files = login_files; reinit: xtrace = sh_isoption(SH_XTRACE); sh_offoption(SH_XTRACE); verbose = sh_isoption(SH_VERBOSE); sh_offoption(SH_VERBOSE); - if(np = nv_open("SHELLOPTS", sh.var_tree, NV_NOADD)) + if(np = nv_open("SHELLOPTS", shp->var_tree, NV_NOADD)) nv_offattr(np,NV_RDONLY); iop = sfopen(NULL, bash_pre_rc, "s"); sh_eval(iop,0); diff --git a/usr/src/lib/libshell/common/sh/defs.c b/usr/src/lib/libshell/common/sh/defs.c index 8b11839eb4..8b8a6aa323 100644 --- a/usr/src/lib/libshell/common/sh/defs.c +++ b/usr/src/lib/libshell/common/sh/defs.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -29,9 +29,9 @@ #include "edit.h" #include "timeout.h" -struct sh_static sh = {0}; +Shell_t sh = {0}; #ifdef __IMPORT__ - struct sh_static *_imp__sh = &sh; + Shell_t *_imp__sh = &sh; #endif Dtdisc_t _Nvdisc = diff --git a/usr/src/lib/libshell/common/sh/deparse.c b/usr/src/lib/libshell/common/sh/deparse.c index 74457d1092..c5704d2cf9 100644 --- a/usr/src/lib/libshell/common/sh/deparse.c +++ b/usr/src/lib/libshell/common/sh/deparse.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/sh/env.c b/usr/src/lib/libshell/common/sh/env.c index d7f7b0b6d7..d1f9361975 100644 --- a/usr/src/lib/libshell/common/sh/env.c +++ b/usr/src/lib/libshell/common/sh/env.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/sh/expand.c b/usr/src/lib/libshell/common/sh/expand.c index 097ed0da66..15004a09be 100644 --- a/usr/src/lib/libshell/common/sh/expand.c +++ b/usr/src/lib/libshell/common/sh/expand.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -90,6 +90,7 @@ static char *nextdir(glob_t *gp, char *dir) int path_expand(const char *pattern, struct argnod **arghead) { + Shell_t *shp = &sh; glob_t gdata; register struct argnod *ap; register glob_t *gp= &gdata; @@ -98,6 +99,7 @@ int path_expand(const char *pattern, struct argnod **arghead) register int off; register char *sp, *cp, *cp2; #endif + sh_stats(STAT_GLOBS); memset(gp,0,sizeof(gdata)); flags = GLOB_AUGMENTED|GLOB_NOCHECK|GLOB_NOSORT|GLOB_STACK|GLOB_LIST|GLOB_DISC; if(sh_isoption(SH_MARKDIRS)) @@ -117,8 +119,8 @@ int path_expand(const char *pattern, struct argnod **arghead) if(sh_isstate(SH_COMPLETE)) { #if KSHELL - extra += scantree(sh.alias_tree,pattern,arghead); - extra += scantree(sh.fun_tree,pattern,arghead); + extra += scantree(shp->alias_tree,pattern,arghead); + extra += scantree(shp->fun_tree,pattern,arghead); # if GLOB_VERSION >= 20010916L gp->gl_nextdir = nextdir; # endif @@ -139,13 +141,13 @@ int path_expand(const char *pattern, struct argnod **arghead) * Generate shell patterns out of those here. */ if(sh_isstate(SH_FCOMPLETE)) - cp=nv_getval(nv_scoped(FIGNORENOD)); + cp=nv_getval(sh_scoped(shp,FIGNORENOD)); else { static Namval_t *GLOBIGNORENOD; if(!GLOBIGNORENOD) - GLOBIGNORENOD = nv_open("GLOBIGNORE",sh.var_tree,0); - cp=nv_getval(nv_scoped(GLOBIGNORENOD)); + GLOBIGNORENOD = nv_open("GLOBIGNORE",shp->var_tree,0); + cp=nv_getval(sh_scoped(shp,GLOBIGNORENOD)); } if(cp) { @@ -182,10 +184,10 @@ int path_expand(const char *pattern, struct argnod **arghead) } else #endif - gp->gl_fignore = nv_getval(nv_scoped(FIGNORENOD)); + gp->gl_fignore = nv_getval(sh_scoped(shp,FIGNORENOD)); if(suflen) gp->gl_suffix = sufstr; - gp->gl_intr = &sh.trapnote; + gp->gl_intr = &shp->trapnote; suflen = 0; if(memcmp(pattern,"~(N",3)==0) flags &= ~GLOB_NOCHECK; diff --git a/usr/src/lib/libshell/common/sh/fault.c b/usr/src/lib/libshell/common/sh/fault.c index 2d71736187..60af63972d 100644 --- a/usr/src/lib/libshell/common/sh/fault.c +++ b/usr/src/lib/libshell/common/sh/fault.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -30,10 +30,11 @@ #include <fcin.h> #include "io.h" #include "history.h" -#include "shnodes.h" +#include "shlex.h" #include "variables.h" #include "jobs.h" #include "path.h" +#include "builtins.h" #define abortsig(sig) (sig==SIGABRT || sig==SIGBUS || sig==SIGILL || sig==SIGSEGV) @@ -59,9 +60,10 @@ static char indone; */ void sh_fault(register int sig) { - register int flag=0; - register char *trap; - register struct checkpt *pp = (struct checkpt*)sh.jmplist; + register Shell_t *shp = sh_getinterp(); + register int flag=0; + register char *trap; + register struct checkpt *pp = (struct checkpt*)shp->jmplist; int action=0; /* reset handler */ if(!(sig&SH_TRAP)) @@ -74,37 +76,56 @@ void sh_fault(register int sig) int32_t v; astwinsize(2,&rows,&cols); if(v = cols) - nv_putval(COLUMNS, (char*)&v, NV_INT32); + nv_putval(COLUMNS, (char*)&v, NV_INT32|NV_RDONLY); if(v = rows) - nv_putval(LINES, (char*)&v, NV_INT32); + nv_putval(LINES, (char*)&v, NV_INT32|NV_RDONLY); + shp->winch++; } #endif /* SIGWINCH */ - if(sh.savesig) + if(shp->savesig) { /* critical region, save and process later */ - sh.savesig = sig; + shp->savesig = sig; + return; + } + trap = shp->st.trapcom[sig]; + if(sig==SIGALRM && shp->bltinfun==b_sleep) + { + if(trap && *trap) + { + shp->trapnote |= SH_SIGTRAP; + shp->sigflag[sig] |= SH_SIGTRAP; + } + return; + } + if(shp->subshell && sig!=SIGINT && sig!=SIGQUIT && sig!=SIGWINCH) + { + shp->exitval = SH_EXITSIG|sig; + sh_subfork(); + shp->exitval = 0; return; } - /* handle ignored signals */ - if((trap=sh.st.trapcom[sig]) && *trap==0) + if(trap && *trap==0) return; - flag = sh.sigflag[sig]&~SH_SIGOFF; + flag = shp->sigflag[sig]&~SH_SIGOFF; if(!trap) { + if(sig==SIGINT && (shp->trapnote&SH_SIGIGNORE)) + return; if(flag&SH_SIGIGNORE) return; if(flag&SH_SIGDONE) { void *ptr=0; - if((flag&SH_SIGINTERACTIVE) && sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_FORKED) && ! sh.subshell) + if((flag&SH_SIGINTERACTIVE) && sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_FORKED) && ! shp->subshell) { /* check for TERM signal between fork/exec */ if(sig==SIGTERM && job.in_critical) - sh.trapnote |= SH_SIGTERM; + shp->trapnote |= SH_SIGTERM; return; } - sh.lastsig = sig; + shp->lastsig = sig; sigrelease(sig); if(pp->mode < SH_JMPFUN) pp->mode = SH_JMPFUN; @@ -114,14 +135,14 @@ void sh_fault(register int sig) { if(ptr) free(ptr); - if(!sh.subshell) - sh_done(sig); + if(!shp->subshell) + sh_done(shp,sig); sh_exit(SH_EXITSIG); } /* mark signal and continue */ - sh.trapnote |= SH_SIGSET; - if(sig < sh.sigmax) - sh.sigflag[sig] |= SH_SIGSET; + shp->trapnote |= SH_SIGSET; + if(sig < shp->sigmax) + shp->sigflag[sig] |= SH_SIGSET; #if defined(VMFL) && (VMALLOC_VERSION>=20031205L) if(abortsig(sig)) { @@ -137,7 +158,7 @@ void sh_fault(register int sig) } errno = 0; if(pp->mode==SH_JMPCMD) - sh.lastsig = sig; + shp->lastsig = sig; if(trap) { /* @@ -149,12 +170,12 @@ void sh_fault(register int sig) } else { - sh.lastsig = sig; + shp->lastsig = sig; flag = SH_SIGSET; #ifdef SIGTSTP if(sig==SIGTSTP) { - sh.trapnote |= SH_SIGTSTP; + shp->trapnote |= SH_SIGTSTP; if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK)) { sigrelease(sig); @@ -165,14 +186,20 @@ void sh_fault(register int sig) #endif /* SIGTSTP */ } #ifdef ERROR_NOTIFY - if((error_info.flags&ERROR_NOTIFY) && sh.bltinfun) - action = (*sh.bltinfun)(-sig,(char**)0,(void*)0); -#endif + /* This is obsolete */ + if((error_info.flags&ERROR_NOTIFY) && shp->bltinfun) + action = (*shp->bltinfun)(-sig,(char**)0,(void*)0); if(action>0) return; - sh.trapnote |= flag; - if(sig < sh.sigmax) - sh.sigflag[sig] |= flag; +#endif + if(shp->bltinfun && shp->bltindata.notify) + { + shp->bltindata.sigset = 1; + return; + } + shp->trapnote |= flag; + if(sig < shp->sigmax) + shp->sigflag[sig] |= flag; if(pp->mode==SH_JMPCMD && sh_isstate(SH_STOPOK)) { if(action<0) @@ -185,45 +212,45 @@ void sh_fault(register int sig) /* * initialize signal handling */ -void sh_siginit(void) +void sh_siginit(void *ptr) { + Shell_t *shp = (Shell_t*)ptr; register int sig, n=SIGTERM+1; register const struct shtable2 *tp = shtab_signals; sig_begin(); /* find the largest signal number in the table */ +#ifdef SIGRTMIN + shp->sigruntime[SH_SIGRTMIN] = SIGRTMIN; +#endif /* SIGRTMIN */ +#ifdef SIGRTMAX + shp->sigruntime[SH_SIGRTMAX] = SIGRTMAX; +#endif /* SIGRTMAX */ while(*tp->sh_name) { - if((sig=tp->sh_number&((1<<SH_SIGBITS)-1))>n && sig<SH_TRAP) + sig = tp->sh_number&((1<<SH_SIGBITS)-1); + if ((tp->sh_number>>SH_SIGBITS) & SH_SIGRUNTIME) + sig = shp->sigruntime[sig-1]; + if(sig>n && sig<SH_TRAP) n = sig; tp++; } -#if defined(_SC_SIGRT_MAX) && defined(_SIGRTMAX) - if((sig=SIGRTMAX+1)>n && sig<SH_TRAP) - n = sig; -#endif - sh.sigmax = n; - sh.st.trapcom = (char**)calloc(n,sizeof(char*)); - sh.sigflag = (unsigned char*)calloc(n,1); - sh.sigmsg = (char**)calloc(n,sizeof(char*)); + shp->sigmax = n++; + shp->st.trapcom = (char**)calloc(n,sizeof(char*)); + shp->sigflag = (unsigned char*)calloc(n,1); + shp->sigmsg = (char**)calloc(n,sizeof(char*)); for(tp=shtab_signals; sig=tp->sh_number; tp++) { n = (sig>>SH_SIGBITS); - if((sig &= ((1<<SH_SIGBITS)-1)) > sh.sigmax) + if((sig &= ((1<<SH_SIGBITS)-1)) > shp->sigmax) continue; sig--; -#if defined(_SC_SIGRT_MIN) && defined(_SIGRTMIN) - if(sig==_SIGRTMIN) - sig = SIGRTMIN; -#endif -#if defined(_SC_SIGRT_MAX) && defined(_SIGRTMAX) - if(sig==_SIGRTMAX) - sig = SIGRTMAX; -#endif + if(n&SH_SIGRUNTIME) + sig = shp->sigruntime[sig]; if(sig>=0) { - sh.sigflag[sig] = n; + shp->sigflag[sig] = n; if(*tp->sh_name) - sh.sigmsg[sig] = (char*)tp->sh_value; + shp->sigmsg[sig] = (char*)tp->sh_value; } } } @@ -301,7 +328,7 @@ void sh_sigreset(register int mode) sh.sigflag[sig] = flag; } } - for(sig=SH_DEBUGTRAP;sig>=0;sig--) + for(sig=SH_DEBUGTRAP-1;sig>=0;sig--) { if(trap=sh.st.trap[sig]) { @@ -344,7 +371,7 @@ void sh_chktrap(void) { register int sig=sh.st.trapmax; register char *trap; - if(!sh.trapnote) + if(!(sh.trapnote&~SH_SIGIGNORE)) sig=0; sh.trapnote &= ~SH_SIGTRAP; /* execute errexit trap first */ @@ -353,7 +380,12 @@ void sh_chktrap(void) int sav_trapnote = sh.trapnote; sh.trapnote &= ~SH_SIGSET; if(sh.st.trap[SH_ERRTRAP]) - sh_trap(sh.st.trap[SH_ERRTRAP],0); + { + trap = sh.st.trap[SH_ERRTRAP]; + sh.st.trap[SH_ERRTRAP] = 0; + sh_trap(trap,0); + sh.st.trap[SH_ERRTRAP] = trap; + } sh.trapnote = sav_trapnote; if(sh_isoption(SH_ERREXIT)) { @@ -382,7 +414,8 @@ void sh_chktrap(void) */ int sh_trap(const char *trap, int mode) { - int jmpval, savxit = sh.exitval; + Shell_t *shp = sh_getinterp(); + int jmpval, savxit = shp->exitval; int was_history = sh_isstate(SH_HISTORY); int was_verbose = sh_isstate(SH_VERBOSE); int staktop = staktell(); @@ -392,7 +425,7 @@ int sh_trap(const char *trap, int mode) fcsave(&savefc); sh_offstate(SH_HISTORY); sh_offstate(SH_VERBOSE); - sh.intrap++; + shp->intrap++; sh_pushcontext(&buff,SH_JMPTRAP); jmpval = sigsetjmp(buff.buff,0); if(jmpval == 0) @@ -416,15 +449,15 @@ int sh_trap(const char *trap, int mode) else { if(jmpval==SH_JMPEXIT) - savxit = sh.exitval; + savxit = shp->exitval; jmpval=SH_JMPTRAP; } } sh_popcontext(&buff); - sh.intrap--; - sfsync(sh.outpool); - if(jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN) - sh.exitval=savxit; + shp->intrap--; + sfsync(shp->outpool); + if(!shp->indebug && jmpval!=SH_JMPEXIT && jmpval!=SH_JMPFUN) + shp->exitval=savxit; stakset(savptr,staktop); fcrestore(&savefc); if(was_history) @@ -433,8 +466,8 @@ int sh_trap(const char *trap, int mode) sh_onstate(SH_VERBOSE); exitset(); if(jmpval>SH_JMPTRAP) - siglongjmp(*sh.jmplist,jmpval); - return(sh.exitval); + siglongjmp(*shp->jmplist,jmpval); + return(shp->exitval); } /* @@ -442,95 +475,109 @@ int sh_trap(const char *trap, int mode) */ void sh_exit(register int xno) { - register struct checkpt *pp = (struct checkpt*)sh.jmplist; + Shell_t *shp = &sh; + register struct checkpt *pp = (struct checkpt*)shp->jmplist; register int sig=0; register Sfio_t* pool; - sh.exitval=xno; + shp->exitval=xno; if(xno==SH_EXITSIG) - sh.exitval |= (sig=sh.lastsig); + shp->exitval |= (sig=shp->lastsig); #ifdef SIGTSTP - if(sh.trapnote&SH_SIGTSTP) + if(shp->trapnote&SH_SIGTSTP) { /* ^Z detected by the shell */ - sh.trapnote = 0; - sh.sigflag[SIGTSTP] = 0; - if(!sh.subshell && sh_isstate(SH_MONITOR) && !sh_isstate(SH_STOPOK)) + shp->trapnote = 0; + shp->sigflag[SIGTSTP] = 0; + if(!shp->subshell && sh_isstate(SH_MONITOR) && !sh_isstate(SH_STOPOK)) return; if(sh_isstate(SH_TIMING)) return; /* Handles ^Z for shell builtins, subshells, and functs */ - sh.lastsig = 0; + shp->lastsig = 0; sh_onstate(SH_MONITOR); sh_offstate(SH_STOPOK); - sh.trapnote = 0; - if(!sh.subshell && (sig=sh_fork(0,NIL(int*)))) + shp->trapnote = 0; + if(!shp->subshell && (sig=sh_fork(0,NIL(int*)))) { job.curpgid = 0; job.parent = (pid_t)-1; job_wait(sig); job.parent = 0; - sh.sigflag[SIGTSTP] = 0; + shp->sigflag[SIGTSTP] = 0; /* wait for child to stop */ - sh.exitval = (SH_EXITSIG|SIGTSTP); + shp->exitval = (SH_EXITSIG|SIGTSTP); /* return to prompt mode */ pp->mode = SH_JMPERREXIT; } else { - if(sh.subshell) + if(shp->subshell) sh_subfork(); /* child process, put to sleep */ sh_offstate(SH_STOPOK); sh_offstate(SH_MONITOR); - sh.sigflag[SIGTSTP] = 0; + shp->sigflag[SIGTSTP] = 0; /* stop child job */ killpg(job.curpgid,SIGTSTP); /* child resumes */ job_clear(); - sh.forked = 1; - sh.exitval = (xno&SH_EXITMASK); + shp->forked = 1; + shp->exitval = (xno&SH_EXITMASK); return; } } #endif /* SIGTSTP */ /* unlock output pool */ sh_offstate(SH_NOTRACK); - if(!(pool=sfpool(NIL(Sfio_t*),sh.outpool,SF_WRITE))) - pool = sh.outpool; /* can't happen? */ + if(!(pool=sfpool(NIL(Sfio_t*),shp->outpool,SF_WRITE))) + pool = shp->outpool; /* can't happen? */ sfclrlock(pool); #ifdef SIGPIPE - if(sh.lastsig==SIGPIPE) + if(shp->lastsig==SIGPIPE) sfpurge(pool); #endif /* SIGPIPE */ sfclrlock(sfstdin); if(!pp) - sh_done(sig); - sh.prefix = 0; + sh_done(shp,sig); + shp->prefix = 0; +#if SHOPT_TYPEDEF + shp->mktype = 0; +#endif /* SHOPT_TYPEDEF*/ if(pp->mode == SH_JMPSCRIPT && !pp->prev) - sh_done(sig); - siglongjmp(pp->buff,pp->mode); + sh_done(shp,sig); + if(pp->mode) + siglongjmp(pp->buff,pp->mode); +} + +static void array_notify(Namval_t *np, void *data) +{ + Namarr_t *ap = nv_arrayptr(np); + NOT_USED(data); + if(ap && ap->fun) + (*ap->fun)(np, 0, NV_AFREE); } /* * This is the exit routine for the shell */ -void sh_done(register int sig) +void sh_done(void *ptr, register int sig) { + Shell_t *shp = (Shell_t*)ptr; register char *t; - register int savxit = sh.exitval; - sh.trapnote = 0; + register int savxit = shp->exitval; + shp->trapnote = 0; indone=1; if(sig==0) - sig = sh.lastsig; - if(sh.userinit) - (*sh.userinit)(-1); - if(t=sh.st.trapcom[0]) + sig = shp->lastsig; + if(shp->userinit) + (*shp->userinit)(shp, -1); + if(t=shp->st.trapcom[0]) { - sh.st.trapcom[0]=0; /*should free but not long */ - sh.oldexit = savxit; + shp->st.trapcom[0]=0; /*should free but not long */ + shp->oldexit = savxit; sh_trap(t,0); - savxit = sh.exitval; + savxit = shp->exitval; } else { @@ -538,7 +585,8 @@ void sh_done(register int sig) sh_offstate(SH_ERREXIT); sh_chktrap(); } - sh_freeup(); + nv_scan(shp->var_tree,array_notify,(void*)0,NV_ARRAY,NV_ARRAY); + sh_freeup(shp); #if SHOPT_ACCT sh_accend(); #endif /* SHOPT_ACCT */ @@ -547,14 +595,14 @@ void sh_done(register int sig) tty_cooked(-1); #endif #ifdef JOBS - if((sh_isoption(SH_INTERACTIVE) && sh.login_sh) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP))) + if((sh_isoption(SH_INTERACTIVE) && shp->login_sh) || (!sh_isoption(SH_INTERACTIVE) && (sig==SIGHUP))) job_walk(sfstderr,job_terminate,SIGHUP,NIL(char**)); #endif /* JOBS */ - job_close(); - if(nv_search("VMTRACE", sh.var_tree,0)) + job_close(shp); + if(nv_search("VMTRACE", shp->var_tree,0)) strmatch((char*)0,(char*)0); sfsync((Sfio_t*)sfstdin); - sfsync((Sfio_t*)sh.outpool); + sfsync((Sfio_t*)shp->outpool); sfsync((Sfio_t*)sfstdout); if(sig) { @@ -566,7 +614,7 @@ void sh_done(register int sig) } #if SHOPT_KIA if(sh_isoption(SH_NOEXEC)) - kiaclose(); + kiaclose((Lex_t*)shp->lex_context); #endif /* SHOPT_KIA */ exit(savxit&SH_EXITMASK); } diff --git a/usr/src/lib/libshell/common/sh/fcin.c b/usr/src/lib/libshell/common/sh/fcin.c index aea98364f5..9618bb9ac0 100644 --- a/usr/src/lib/libshell/common/sh/fcin.c +++ b/usr/src/lib/libshell/common/sh/fcin.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -97,7 +97,7 @@ int fcfill(void) _Fcin.fcptr = ptr = last; } if((n = ptr-_Fcin.fcbuff) && _Fcin.fcfun) - (*_Fcin.fcfun)(f,(const char*)_Fcin.fcbuff,n); + (*_Fcin.fcfun)(f,(const char*)_Fcin.fcbuff,n,_Fcin.context); sfread(f, (char*)_Fcin.fcbuff, n); _Fcin.fcoff +=n; _Fcin._fcfile = 0; @@ -128,9 +128,10 @@ int fcclose(void) /* * Set the notify function that is called for each fcfill() */ -void fcnotify(void (*fun)(Sfio_t*,const char*,int)) +void fcnotify(void (*fun)(Sfio_t*,const char*,int,void*),void* context) { _Fcin.fcfun = fun; + _Fcin.context = context; } #ifdef __EXPORT__ diff --git a/usr/src/lib/libshell/common/sh/init.c b/usr/src/lib/libshell/common/sh/init.c index fbb6e5c6bc..0bd2f92b79 100644 --- a/usr/src/lib/libshell/common/sh/init.c +++ b/usr/src/lib/libshell/common/sh/init.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -32,6 +32,7 @@ #include <ctype.h> #include <ccode.h> #include <pwd.h> +#include <tmx.h> #include "variables.h" #include "path.h" #include "fault.h" @@ -46,17 +47,44 @@ #include "lexstates.h" #include "version.h" +char e_version[] = "\n@(#)$Id: Version " +#if SHOPT_AUDIT +#define ATTRS 1 + "A" +#endif +#if SHOPT_BASH +#define ATTRS 1 + "B" +#endif +#if SHOPT_ACCT +#define ATTRS 1 + "L" +#endif #if SHOPT_MULTIBYTE - char e_version[] = "\n@(#)$Id: Version M "SH_RELEASE" $\0\n"; -#else - char e_version[] = "\n@(#)$Id: Version "SH_RELEASE" $\0\n"; -#endif /* SHOPT_MULTIBYTE */ +#define ATTRS 1 + "M" +#endif +#if SHOPT_PFSH && _hdr_exec_attr +#define ATTRS 1 + "P" +#endif +#if ATTRS + " " +#endif + SH_RELEASE " $\0\n"; #if SHOPT_BASH - extern void bash_init(int); + extern void bash_init(Shell_t*,int); #endif #define RANDMASK 0x7fff + +#ifndef ARG_MAX +# define ARG_MAX (1*1024*1024) +#endif +#ifndef CHILD_MAX +# define CHILD_MAX (1*1024) +#endif #ifndef CLK_TCK # define CLK_TCK 60 #endif /* CLK_TCK */ @@ -77,23 +105,15 @@ struct seconds struct rand { Namfun_t hdr; - Shell_t *sh; int32_t rand_last; }; struct ifs { Namfun_t hdr; - Shell_t *sh; Namval_t *ifsnp; }; -struct shell -{ - Namfun_t hdr; - Shell_t *sh; -}; - struct match { Namfun_t hdr; @@ -112,31 +132,33 @@ typedef struct _init_ Namfun_t VPATH_init; #endif /* SHOPT_FS_3D */ struct ifs IFS_init; - struct shell PATH_init; -#ifdef PATH_BFPATH - struct shell FPATH_init; - struct shell CDPATH_init; -#endif - struct shell SHELL_init; - struct shell ENV_init; - struct shell VISUAL_init; - struct shell EDITOR_init; - struct shell OPTINDEX_init; + Namfun_t PATH_init; + Namfun_t FPATH_init; + Namfun_t CDPATH_init; + Namfun_t SHELL_init; + Namfun_t ENV_init; + Namfun_t VISUAL_init; + Namfun_t EDITOR_init; + Namfun_t HISTFILE_init; + Namfun_t HISTSIZE_init; + Namfun_t OPTINDEX_init; struct seconds SECONDS_init; struct rand RAND_init; - struct shell LINENO_init; - struct shell L_ARG_init; + Namfun_t LINENO_init; + Namfun_t L_ARG_init; + Namfun_t SH_VERSION_init; struct match SH_MATCH_init; #ifdef _hdr_locale - struct shell LC_TYPE_init; - struct shell LC_NUM_init; - struct shell LC_COLL_init; - struct shell LC_MSG_init; - struct shell LC_ALL_init; - struct shell LANG_init; + Namfun_t LC_TYPE_init; + Namfun_t LC_NUM_init; + Namfun_t LC_COLL_init; + Namfun_t LC_MSG_init; + Namfun_t LC_ALL_init; + Namfun_t LANG_init; #endif /* _hdr_locale */ } Init_t; +static int nbltins; static void env_init(Shell_t*); static Init_t *nv_init(Shell_t*); static Dt_t *inittree(Shell_t*,const struct shtable2*); @@ -173,12 +195,13 @@ static char *nospace(int unused) static void put_ed(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { register const char *cp, *name=nv_name(np); - if(*name=='E' && nv_getval(nv_scoped(VISINOD))) + Shell_t *shp = nv_shell(np); + if(*name=='E' && nv_getval(sh_scoped(shp,VISINOD))) goto done; sh_offoption(SH_VI); sh_offoption(SH_EMACS); sh_offoption(SH_GMACS); - if(!(cp=val) && (*name=='E' || !(cp=nv_getval(nv_scoped(EDITNOD))))) + if(!(cp=val) && (*name=='E' || !(cp=nv_getval(sh_scoped(shp,EDITNOD))))) goto done; /* turn on vi or emacs option if editor name is either*/ cp = path_basename(cp); @@ -192,12 +215,37 @@ done: nv_putv(np, val, flags, fp); } +/* Trap for HISTFILE and HISTSIZE variables */ +static void put_history(register Namval_t* np,const char *val,int flags,Namfun_t *fp) +{ + Shell_t *shp = nv_shell(np); + void *histopen = shp->hist_ptr; + if(val && histopen) + { + if(np==HISTFILE && strcmp(val,nv_getval(HISTFILE))==0) + return; + if(np==HISTSIZE && sh_arith(val)==nv_getnum(HISTSIZE)) + return; + hist_close(shp->hist_ptr); + } + nv_putv(np, val, flags, fp); + if(histopen) + { + if(val) + sh_histinit(shp); + else + hist_close(histopen); + } +} + /* Trap for OPTINDEX */ static void put_optindex(Namval_t* np,const char *val,int flags,Namfun_t *fp) { - Shell_t *shp = ((struct shell*)fp)->sh; + Shell_t *shp = nv_shell(np); shp->st.opterror = shp->st.optchar = 0; nv_putv(np, val, flags, fp); + if(!val) + nv_disc(np,fp,NV_POP); } static Sfdouble_t nget_optindex(register Namval_t* np, Namfun_t *fp) @@ -205,34 +253,37 @@ static Sfdouble_t nget_optindex(register Namval_t* np, Namfun_t *fp) return((Sfdouble_t)*np->nvalue.lp); } +static Namfun_t *clone_optindex(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) +{ + Namfun_t *dp = (Namfun_t*)malloc(sizeof(Namfun_t)); + memcpy((void*)dp,(void*)fp,sizeof(Namfun_t)); + mp->nvalue.lp = np->nvalue.lp; + dp->nofree = 0; + return(dp); +} + + /* Trap for restricted variables FPATH, PATH, SHELL, ENV */ static void put_restricted(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { - Shell_t *shp = ((struct shell*)fp)->sh; - int path_scoped = 0; -#ifdef PATH_BFPATH + Shell_t *shp = nv_shell(np); + int path_scoped = 0; Pathcomp_t *pp; char *name = nv_name(np); -#endif if(!(flags&NV_RDONLY) && sh_isoption(SH_RESTRICTED)) errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np)); if(np==PATHNOD || (path_scoped=(strcmp(name,PATHNOD->nvname)==0))) { -#ifndef PATH_BFPATH - shp->lastpath = 0; -#endif nv_scan(shp->track_tree,rehash,(void*)0,NV_TAGGED,NV_TAGGED); if(path_scoped && !val) val = PATHNOD->nvalue.cp; } if(val && !(flags&NV_RDONLY) && np->nvalue.cp && strcmp(val,np->nvalue.cp)==0) return; -#ifdef PATH_BFPATH - if(shp->pathlist && np==FPATHNOD) + if(np==FPATHNOD) shp->pathlist = (void*)path_unsetfpath((Pathcomp_t*)shp->pathlist); -#endif nv_putv(np, val, flags, fp); -#ifdef PATH_BFPATH + shp->universe = 0; if(shp->pathlist) { val = np->nvalue.cp; @@ -255,14 +306,12 @@ sfprintf(sfstderr,"%d: name=%s val=%s\n",getpid(),name,val); path_dump((Pathcomp_t*)shp->pathlist); #endif } -#endif } -#ifdef PATH_BFPATH static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t *fp) { Pathcomp_t *pp; - Shell_t *shp = ((struct shell*)fp)->sh; + Shell_t *shp = nv_shell(np); nv_putv(np, val, flags, fp); if(!shp->cdpathlist) return; @@ -271,7 +320,6 @@ static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t if(shp->cdpathlist = (void*)pp) pp->shp = shp; } -#endif #ifdef _hdr_locale /* @@ -295,6 +343,7 @@ static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t /* Trap for LC_ALL, LC_TYPE, LC_MESSAGES, LC_COLLATE and LANG */ static void put_lang(Namval_t* np,const char *val,int flags,Namfun_t *fp) { + Shell_t *shp = nv_shell(np); int type; char *lc_all = nv_getval(LCALLNOD); char *name = nv_name(np); @@ -314,11 +363,11 @@ static void put_cdpath(register Namval_t* np,const char *val,int flags,Namfun_t type= -1; if(sh_isstate(SH_INIT) && type>=0 && type!=LC_ALL && lc_all && *lc_all) type= -1; - if(type>=0) + if(type>=0 || type==LC_ALL) { if(!setlocale(type,val?val:"")) { - if(!sh_isstate(SH_INIT) || sh.login_sh==0) + if(!sh_isstate(SH_INIT) || shp->login_sh==0) errormsg(SH_DICT,0,e_badlocale,val); return; } @@ -397,7 +446,7 @@ static char* get_ifs(register Namval_t* np, Namfun_t *fp) register struct ifs *ip = (struct ifs*)fp; register char *cp, *value; register int c,n; - register Shell_t *shp = ip->sh; + register Shell_t *shp = nv_shell(np); value = nv_getv(np,fp); if(np!=ip->ifsnp) { @@ -463,6 +512,7 @@ static void put_seconds(register Namval_t* np,const char *val,int flags,Namfun_t if(!np->nvalue.dp) { nv_setsize(np,3); + nv_onattr(np,NV_DOUBLE); np->nvalue.dp = new_of(double,0); } nv_putv(np, val, flags, fp); @@ -473,14 +523,15 @@ static void put_seconds(register Namval_t* np,const char *val,int flags,Namfun_t static char* get_seconds(register Namval_t* np, Namfun_t *fp) { + Shell_t *shp = nv_shell(np); register int places = nv_size(np); struct tms tp; double d, offset = (np->nvalue.dp?*np->nvalue.dp:0); NOT_USED(fp); timeofday(&tp); d = dtime(&tp)- offset; - sfprintf(sh.strbuf,"%.*f",places,d); - return(sfstruse(sh.strbuf)); + sfprintf(shp->strbuf,"%.*f",places,d); + return(sfstruse(shp->strbuf)); } static Sfdouble_t nget_seconds(register Namval_t* np, Namfun_t *fp) @@ -554,7 +605,7 @@ static Sfdouble_t nget_lineno(Namval_t* np, Namfun_t *fp) static void put_lineno(Namval_t* np,const char *val,int flags,Namfun_t *fp) { register long n; - Shell_t *shp = ((struct shell*)fp)->sh; + Shell_t *shp = nv_shell(np); if(!val) { nv_stack(np, NIL(Namfun_t*)); @@ -576,25 +627,27 @@ static char* get_lineno(register Namval_t* np, Namfun_t *fp) static char* get_lastarg(Namval_t* np, Namfun_t *fp) { + Shell_t *shp = nv_shell(np); NOT_USED(np); - return(sh.lastarg); + return(shp->lastarg); } static void put_lastarg(Namval_t* np,const char *val,int flags,Namfun_t *fp) { + Shell_t *shp = nv_shell(np); if(flags&NV_INTEGER) { - sfprintf(sh.strbuf,"%.*g",12,*((double*)val)); - val = sfstruse(sh.strbuf); + sfprintf(shp->strbuf,"%.*g",12,*((double*)val)); + val = sfstruse(shp->strbuf); } - if(sh.lastarg && !nv_isattr(np,NV_NOFREE)) - free((void*)sh.lastarg); + if(shp->lastarg && !nv_isattr(np,NV_NOFREE)) + free((void*)shp->lastarg); else nv_offattr(np,NV_NOFREE); if(val) - sh.lastarg = strdup(val); + shp->lastarg = strdup(val); else - sh.lastarg = 0; + shp->lastarg = 0; } static int hasgetdisc(register Namfun_t *fp) @@ -635,7 +688,7 @@ void sh_setmatch(const char *v, int vsize, int nmatch, int match[]) } memcpy(mp->val,v,vsize); mp->val[vsize] = 0; - nv_putsub(SH_MATCHNOD, NIL(char*), nmatch|ARRAY_FILL); + nv_putsub(SH_MATCHNOD, NIL(char*), (nmatch-1)|ARRAY_FILL); mp->lastsub = -1; } } @@ -670,7 +723,30 @@ static char* get_match(register Namval_t* np, Namfun_t *fp) return(mp->rval); } -static const Namdisc_t SH_MATCH_disc = { sizeof(struct match), 0, get_match }; +static const Namdisc_t SH_MATCH_disc = { sizeof(struct match), 0, get_match }; + +static char* get_version(register Namval_t* np, Namfun_t *fp) +{ + return(nv_getv(np,fp)); +} + +static Sfdouble_t nget_version(register Namval_t* np, Namfun_t *fp) +{ + register const char *cp = e_version + strlen(e_version)-10; + register int c; + Sflong_t t = 0; + NOT_USED(fp); + + while (c = *cp++) + if (c >= '0' && c <= '9') + { + t *= 10; + t += c - '0'; + } + return((Sfdouble_t)t); +} + +static const Namdisc_t SH_VERSION_disc = { 0, 0, get_version, nget_version }; #if SHOPT_FS_3D /* @@ -709,16 +785,15 @@ static const Namdisc_t SH_MATCH_disc = { sizeof(struct match), 0, get_match }; static const Namdisc_t IFS_disc = { sizeof(struct ifs), put_ifs, get_ifs }; -const Namdisc_t RESTRICTED_disc = { sizeof(struct shell), put_restricted }; -#ifdef PATH_BFPATH -static const Namdisc_t CDPATH_disc = { sizeof(struct shell), put_cdpath }; -#endif -static const Namdisc_t EDITOR_disc = { sizeof(struct shell), put_ed }; -static const Namdisc_t OPTINDEX_disc = { sizeof(struct shell), put_optindex, 0, nget_optindex }; +const Namdisc_t RESTRICTED_disc = { sizeof(Namfun_t), put_restricted }; +static const Namdisc_t CDPATH_disc = { sizeof(Namfun_t), put_cdpath }; +static const Namdisc_t EDITOR_disc = { sizeof(Namfun_t), put_ed }; +static const Namdisc_t HISTFILE_disc = { sizeof(Namfun_t), put_history }; +static const Namdisc_t OPTINDEX_disc = { sizeof(Namfun_t), put_optindex, 0, nget_optindex, 0, 0, clone_optindex }; static const Namdisc_t SECONDS_disc = { sizeof(struct seconds), put_seconds, get_seconds, nget_seconds }; static const Namdisc_t RAND_disc = { sizeof(struct rand), put_rand, get_rand, nget_rand }; -static const Namdisc_t LINENO_disc = { sizeof(struct shell), put_lineno, get_lineno, nget_lineno }; -static const Namdisc_t L_ARG_disc = { sizeof(struct shell), put_lastarg, get_lastarg }; +static const Namdisc_t LINENO_disc = { sizeof(Namfun_t), put_lineno, get_lineno, nget_lineno }; +static const Namdisc_t L_ARG_disc = { sizeof(Namfun_t), put_lastarg, get_lastarg }; #if SHOPT_NAMESPACE static char* get_nspace(Namval_t* np, Namfun_t *fp) @@ -732,7 +807,7 @@ static const Namdisc_t L_ARG_disc = { sizeof(struct shell), put_lastarg, get_la #endif /* SHOPT_NAMESPACE */ #ifdef _hdr_locale - static const Namdisc_t LC_disc = { sizeof(struct shell), put_lang }; + static const Namdisc_t LC_disc = { sizeof(Namfun_t), put_lang }; #endif /* _hdr_locale */ /* @@ -841,25 +916,66 @@ int sh_type(register const char *path) } break; } - if (*s++ != 's' || *s++ != 'h') - return 0; - t |= SH_TYPE_SH; - if ((t & SH_TYPE_KSH) && *s == '9' && *(s+1) == '3') - s += 2; + if (*s++ == 's' && (*s == 'h' || *s == 'u')) + { + s++; + t |= SH_TYPE_SH; + if ((t & SH_TYPE_KSH) && *s == '9' && *(s+1) == '3') + s += 2; #if _WINIX - if (*s == '.' && *(s+1) == 'e' && *(s+2) == 'x' && *(s+3) == 'e') - s += 4; + if (*s == '.' && *(s+1) == 'e' && *(s+2) == 'x' && *(s+3) == 'e') + s += 4; #endif - if (*s) - t &= ~(SH_TYPE_PROFILE|SH_TYPE_RESTRICTED); - return t; + if (!isalnum(*s)) + return t; + } + return t & ~(SH_TYPE_BASH|SH_TYPE_KSH|SH_TYPE_PROFILE|SH_TYPE_RESTRICTED); +} + + +static char *get_mode(Namval_t* np, Namfun_t* nfp) +{ + mode_t mode = nv_getn(np,nfp); + return(fmtperm(mode)); +} + +static void put_mode(Namval_t* np, const char* val, int flag, Namfun_t* nfp) +{ + if(val) + { + mode_t mode; + char *last; + if(flag&NV_INTEGER) + { + if(flag&NV_LONG) + mode = *(Sfdouble_t*)val; + else + mode = *(double*)val; + } + else + mode = strperm(val, &last,0); + if(*last) + errormsg(SH_DICT,ERROR_exit(1),"%s: invalid mode string",val); + nv_putv(np,(char*)&mode,NV_INTEGER,nfp); + } + else + nv_putv(np,val,flag,nfp); } +static const Namdisc_t modedisc = +{ + 0, + put_mode, + get_mode, +}; + + /* * initialize the shell */ -Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) +Shell_t *sh_init(register int argc,register char *argv[], Shinit_f userinit) { + Shell_t *shp = &sh; register int n; int type; static char *login_files[3]; @@ -871,25 +987,26 @@ Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) #else init_ebcdic(); #endif - umask(umask(0)); - sh.mac_context = sh_macopen(&sh); - sh.arg_context = sh_argopen(&sh); - sh.lex_context = (void*)sh_lexopen(0,&sh,1); - sh.ed_context = (void*)ed_open(&sh); - sh.strbuf = sfstropen(); - sfsetbuf(sh.strbuf,(char*)0,64); + umask(shp->mask=umask(0)); + shp->mac_context = sh_macopen(shp); + shp->arg_context = sh_argopen(shp); + shp->lex_context = (void*)sh_lexopen(0,shp,1); + shp->ed_context = (void*)ed_open(shp); + shp->strbuf = sfstropen(); + shp->stk = stkstd; + sfsetbuf(shp->strbuf,(char*)0,64); sh_onstate(SH_INIT); error_info.exit = sh_exit; error_info.id = path_basename(argv[0]); #if ERROR_VERSION >= 20000102L error_info.catalog = e_dict; #endif - sh.cpipe[0] = -1; - sh.coutpipe = -1; - sh.userid=getuid(); - sh.euserid=geteuid(); - sh.groupid=getgid(); - sh.egroupid=getegid(); + shp->cpipe[0] = -1; + shp->coutpipe = -1; + shp->userid=getuid(); + shp->euserid=geteuid(); + shp->groupid=getgid(); + shp->egroupid=getegid(); for(n=0;n < 10; n++) { /* don't use lower bits when rand() generates large numbers */ @@ -899,41 +1016,42 @@ Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) break; } } - sh.lim.clk_tck = getconf("CLK_TCK"); - sh.lim.arg_max = getconf("ARG_MAX"); - sh.lim.open_max = getconf("OPEN_MAX"); - sh.lim.child_max = getconf("CHILD_MAX"); - sh.lim.ngroups_max = getconf("NGROUPS_MAX"); - sh.lim.posix_version = getconf("VERSION"); - sh.lim.posix_jobcontrol = getconf("JOB_CONTROL"); - if(sh.lim.arg_max <=0) - sh.lim.arg_max = ARG_MAX; - if(sh.lim.child_max <=0) - sh.lim.child_max = CHILD_MAX; - if(sh.lim.open_max <0) - sh.lim.open_max = OPEN_MAX; - if(sh.lim.open_max > (SHRT_MAX-2)) - sh.lim.open_max = SHRT_MAX-2; - if(sh.lim.clk_tck <=0) - sh.lim.clk_tck = CLK_TCK; + shp->lim.clk_tck = getconf("CLK_TCK"); + shp->lim.arg_max = getconf("ARG_MAX"); + shp->lim.open_max = getconf("OPEN_MAX"); + shp->lim.child_max = getconf("CHILD_MAX"); + shp->lim.ngroups_max = getconf("NGROUPS_MAX"); + shp->lim.posix_version = getconf("VERSION"); + shp->lim.posix_jobcontrol = getconf("JOB_CONTROL"); + if(shp->lim.arg_max <=0) + shp->lim.arg_max = ARG_MAX; + if(shp->lim.child_max <=0) + shp->lim.child_max = CHILD_MAX; + if(shp->lim.open_max <0) + shp->lim.open_max = OPEN_MAX; + if(shp->lim.open_max > (SHRT_MAX-2)) + shp->lim.open_max = SHRT_MAX-2; + if(shp->lim.clk_tck <=0) + shp->lim.clk_tck = CLK_TCK; #if SHOPT_FS_3D if(fs3d(FS3D_TEST)) - sh.lim.fs3d = 1; + shp->lim.fs3d = 1; #endif /* SHOPT_FS_3D */ - sh_ioinit(); + sh_ioinit(shp); /* initialize signal handling */ - sh_siginit(); + sh_siginit(shp); stakinstall(NIL(Stak_t*),nospace); /* set up memory for name-value pairs */ - sh.init_context = nv_init(&sh); + shp->init_context = nv_init(shp); /* read the environment */ if(argc>0) { type = sh_type(*argv); if(type&SH_TYPE_LOGIN) - sh.login_sh = 2; + shp->login_sh = 2; } - env_init(&sh); + env_init(shp); + *SHLVL->nvalue.ip +=1; #if SHOPT_SPAWN { /* @@ -942,17 +1060,17 @@ Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) */ char *last, *cp=nv_getval(L_ARGNOD); char buff[PATH_MAX+1]; - sh.shpath = 0; - sfprintf(sh.strbuf,"/proc/%d/exe",getpid()); - if((n=readlink(sfstruse(sh.strbuf),buff,sizeof(buff)-1))>0) + shp->shpath = 0; + sfprintf(shp->strbuf,"/proc/%d/exe",getpid()); + if((n=readlink(sfstruse(shp->strbuf),buff,sizeof(buff)-1))>0) { buff[n] = 0; - sh.shpath = strdup(buff); + shp->shpath = strdup(buff); } else if((cp && (sh_type(cp)&SH_TYPE_SH)) || (argc>0 && strchr(cp= *argv,'/'))) { if(*cp=='/') - sh.shpath = strdup(cp); + shp->shpath = strdup(cp); else if(cp = nv_getval(PWDNOD)) { int offset = staktell(); @@ -960,7 +1078,7 @@ Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) stakputc('/'); stakputs(argv[0]); pathcanon(stakptr(offset),PATH_DOTDOT); - sh.shpath = strdup(stakptr(offset)); + shp->shpath = strdup(stakptr(offset)); stakseek(offset); } } @@ -972,7 +1090,7 @@ Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) #endif /* SHOPT_FS_3D */ astconfdisc(newconf); #if SHOPT_TIMEOUT - sh.st.tmout = SHOPT_TIMEOUT; + shp->st.tmout = SHOPT_TIMEOUT; #endif /* SHOPT_TIMEOUT */ /* initialize jobs table */ job_clear(); @@ -990,33 +1108,33 @@ Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) /* check for invocation as bash */ if(type&SH_TYPE_BASH) { - sh.userinit = userinit = bash_init; + shp->userinit = userinit = bash_init; sh_onoption(SH_BASH); sh_onstate(SH_PREINIT); - (*userinit)(0); + (*userinit)(shp, 0); sh_offstate(SH_PREINIT); } #endif /* look for options */ - /* sh.st.dolc is $# */ - if((sh.st.dolc = sh_argopts(-argc,argv)) < 0) + /* shp->st.dolc is $# */ + if((shp->st.dolc = sh_argopts(-argc,argv,shp)) < 0) { - sh.exitval = 2; - sh_done(0); + shp->exitval = 2; + sh_done(shp,0); } opt_info.disc = 0; - sh.st.dolv=argv+(argc-1)-sh.st.dolc; - sh.st.dolv[0] = argv[0]; - if(sh.st.dolc < 1) + shp->st.dolv=argv+(argc-1)-shp->st.dolc; + shp->st.dolv[0] = argv[0]; + if(shp->st.dolc < 1) sh_onoption(SH_SFLAG); if(!sh_isoption(SH_SFLAG)) { - sh.st.dolc--; - sh.st.dolv++; + shp->st.dolc--; + shp->st.dolv++; #if _WINIX { char* name; - name = sh.st.dolv[0]; + name = shp->st.dolv[0]; if(name[1]==':' && (name[2]=='/' || name[2]=='\\')) { #if _lib_pathposix @@ -1041,21 +1159,21 @@ Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) #if SHOPT_PFSH if (sh_isoption(SH_PFSH)) { - struct passwd *pw = getpwuid(sh.userid); + struct passwd *pw = getpwuid(shp->userid); if(pw) - sh.user = strdup(pw->pw_name); + shp->user = strdup(pw->pw_name); } #endif /* set[ug]id scripts require the -p flag */ - if(sh.userid!=sh.euserid || sh.groupid!=sh.egroupid) + if(shp->userid!=shp->euserid || shp->groupid!=shp->egroupid) { #if SHOPT_P_SUID /* require sh -p to run setuid and/or setgid */ - if(!sh_isoption(SH_PRIVILEGED) && sh.euserid < SHOPT_P_SUID) + if(!sh_isoption(SH_PRIVILEGED) && shp->euserid < SHOPT_P_SUID) { - setuid(sh.euserid=sh.userid); - setgid(sh.egroupid=sh.groupid); + setuid(shp->euserid=shp->userid); + setgid(shp->egroupid=shp->groupid); } else #else @@ -1063,7 +1181,7 @@ Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) #endif /* SHOPT_P_SUID */ #ifdef SHELLMAGIC /* careful of #! setuid scripts with name beginning with - */ - if(sh.login_sh && argv[1] && strcmp(argv[0],argv[1])==0) + if(shp->login_sh && argv[1] && strcmp(argv[0],argv[1])==0) errormsg(SH_DICT,ERROR_exit(1),e_prohibited); #endif /*SHELLMAGIC*/ } @@ -1071,25 +1189,52 @@ Shell_t *sh_init(register int argc,register char *argv[], void(*userinit)(int)) sh_offoption(SH_PRIVILEGED); /* shname for $0 in profiles and . scripts */ if(strmatch(argv[1],e_devfdNN)) - sh.shname = strdup(argv[0]); + shp->shname = strdup(argv[0]); else - sh.shname = strdup(sh.st.dolv[0]); + shp->shname = strdup(shp->st.dolv[0]); /* * return here for shell script execution * but not for parenthesis subshells */ - error_info.id = strdup(sh.st.dolv[0]); /* error_info.id is $0 */ - sh.jmpbuffer = (void*)&sh.checkbase; - sh_pushcontext(&sh.checkbase,SH_JMPSCRIPT); - sh.st.self = &sh.global; - sh.topscope = (Shscope_t*)sh.st.self; + error_info.id = strdup(shp->st.dolv[0]); /* error_info.id is $0 */ + shp->jmpbuffer = (void*)&shp->checkbase; + sh_pushcontext(&shp->checkbase,SH_JMPSCRIPT); + shp->st.self = &shp->global; + shp->topscope = (Shscope_t*)shp->st.self; sh_offstate(SH_INIT); login_files[0] = (char*)e_profile; login_files[1] = ".profile"; - sh.login_files = login_files; - if(sh.userinit=userinit) - (*userinit)(0); - return(&sh); + shp->login_files = login_files; + shp->bltindata.version = SH_VERSION; + shp->bltindata.shp = shp; + shp->bltindata.shrun = sh_run; + shp->bltindata.shtrap = sh_trap; + shp->bltindata.shexit = sh_exit; + shp->bltindata.shbltin = sh_addbuiltin; +#if _AST_VERSION >= 20080617L + shp->bltindata.shgetenv = getenv; + shp->bltindata.shsetenv = setenviron; + astintercept(&shp->bltindata,1); +#endif +#if 0 +#define NV_MKINTTYPE(x,y,z) nv_mkinttype(#x,sizeof(x),(x)-1<0,(y),(Namdisc_t*)z); + NV_MKINTTYPE(pid_t,"process id",0); + NV_MKINTTYPE(gid_t,"group id",0); + NV_MKINTTYPE(uid_t,"user id",0); + NV_MKINTTYPE(size_t,(const char*)0,0); + NV_MKINTTYPE(ssize_t,(const char*)0,0); + NV_MKINTTYPE(off_t,"offset in bytes",0); + NV_MKINTTYPE(ino_t,"\ai-\anode number",0); + NV_MKINTTYPE(mode_t,(const char*)0,&modedisc); + NV_MKINTTYPE(dev_t,"device id",0); + NV_MKINTTYPE(nlink_t,"hard link count",0); + NV_MKINTTYPE(blkcnt_t,"block count",0); + NV_MKINTTYPE(time_t,"seconds since the epoch",0); + nv_mkstat(); +#endif + if(shp->userinit=userinit) + (*userinit)(shp, 0); + return(shp); } Shell_t *sh_getinterp(void) @@ -1102,25 +1247,41 @@ Shell_t *sh_getinterp(void) */ int sh_reinit(char *argv[]) { + Shell_t *shp = &sh; Shopt_t opt; - dtclear(sh.fun_tree); - dtclose(sh.alias_tree); - sh.alias_tree = inittree(&sh,shtab_aliases); - sh.namespace = 0; - sh.inuse_bits = 0; - if(sh.userinit) - (*sh.userinit)(1); - if(sh.heredocs) + Namval_t *np,*npnext; + Dt_t *dp; + for(np=dtfirst(shp->fun_tree);np;np=npnext) { - sfclose(sh.heredocs); - sh.heredocs = 0; + if((dp=shp->fun_tree)->walk) + dp = dp->walk; + npnext = (Namval_t*)dtnext(shp->fun_tree,np); + if(np>= shp->bltin_cmds && np < &shp->bltin_cmds[nbltins]) + continue; + if(is_abuiltin(np) && nv_isattr(np,NV_EXPORT)) + continue; + if(*np->nvname=='/') + continue; + nv_delete(np,dp,NV_NOFREE); + } + dtclose(shp->alias_tree); + shp->alias_tree = inittree(shp,shtab_aliases); + shp->last_root = shp->var_tree; + shp->namespace = 0; + shp->inuse_bits = 0; + if(shp->userinit) + (*shp->userinit)(shp, 1); + if(shp->heredocs) + { + sfclose(shp->heredocs); + shp->heredocs = 0; } /* remove locals */ sh_onstate(SH_INIT); - nv_scan(sh.var_tree,sh_envnolocal,(void*)0,NV_EXPORT,0); - nv_scan(sh.var_tree,sh_envnolocal,(void*)0,NV_ARRAY,NV_ARRAY); + nv_scan(shp->var_tree,sh_envnolocal,(void*)0,NV_EXPORT,0); + nv_scan(shp->var_tree,sh_envnolocal,(void*)0,NV_ARRAY,NV_ARRAY); sh_offstate(SH_INIT); - memset(sh.st.trapcom,0,(sh.st.trapmax+1)*sizeof(char*)); + memset(shp->st.trapcom,0,(shp->st.trapmax+1)*sizeof(char*)); memset((void*)&opt,0,sizeof(opt)); if(sh_isoption(SH_TRACKALL)) on_option(&opt,SH_TRACKALL); @@ -1132,18 +1293,19 @@ int sh_reinit(char *argv[]) on_option(&opt,SH_VI); if(sh_isoption(SH_VIRAW)) on_option(&opt,SH_VIRAW); - sh.options = opt; + shp->options = opt; /* set up new args */ if(argv) - sh.arglist = sh_argcreate(argv); - if(sh.arglist) - sh_argreset(sh.arglist,NIL(struct dolnod*)); - sh.envlist=0; - sh.curenv = 0; - sh.shname = error_info.id = strdup(sh.st.dolv[0]); + shp->arglist = sh_argcreate(argv); + if(shp->arglist) + sh_argreset(shp,shp->arglist,NIL(struct dolnod*)); + shp->envlist=0; + shp->curenv = 0; + shp->shname = error_info.id = strdup(shp->st.dolv[0]); sh_offstate(SH_FORKED); - sh.fn_depth = sh.dot_depth = 0; + shp->fn_depth = shp->dot_depth = 0; sh_sigreset(0); + *SHLVL->nvalue.ip +=1; return(1); } @@ -1152,11 +1314,7 @@ int sh_reinit(char *argv[]) */ Namfun_t *nv_cover(register Namval_t *np) { -#ifdef PATH_BFPATH if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==FPATHNOD || np==CDPNOD || np==SECONDS) -#else - if(np==IFSNOD || np==PATHNOD || np==SHELLNOD || np==SECONDS) -#endif return(np->nvfun); #ifdef _hdr_locale if(np==LCALLNOD || np==LCTYPENOD || np==LCMSGNOD || np==LCCOLLNOD || np==LCNUMNOD || np==LANGNOD) @@ -1165,110 +1323,205 @@ Namfun_t *nv_cover(register Namval_t *np) return(0); } -static Namtype_t typeset; static const char *shdiscnames[] = { "tilde", 0}; +#ifdef SHOPT_STATS +struct Stats +{ + Namfun_t hdr; + Shell_t *sh; + char *nodes; + int numnodes; + int current; +}; + +static Namval_t *next_stat(register Namval_t* np, Dt_t *root,Namfun_t *fp) +{ + struct Stats *sp = (struct Stats*)fp; + if(!root) + sp->current = 0; + else if(++sp->current>=sp->numnodes) + return(0); + return(nv_namptr(sp->nodes,sp->current)); +} + +static Namval_t *create_stat(Namval_t *np,const char *name,int flag,Namfun_t *fp) +{ + struct Stats *sp = (struct Stats*)fp; + register const char *cp=name; + register int i=0,n; + Namval_t *nq=0; + Shell_t *shp = sp->sh; + if(!name) + return(SH_STATS); + while((i=*cp++) && i != '=' && i != '+' && i!='['); + n = (cp-1) -name; + for(i=0; i < sp->numnodes; i++) + { + nq = nv_namptr(sp->nodes,i); + if((n==0||memcmp(name,nq->nvname,n)==0) && nq->nvname[n]==0) + goto found; + } + nq = 0; +found: + if(nq) + { + fp->last = (char*)&name[n]; + shp->last_table = SH_STATS; + } + else + errormsg(SH_DICT,ERROR_exit(1),e_notelem,n,name,nv_name(np)); + return(nq); +} + +static const Namdisc_t stat_disc = +{ + 0, 0, 0, 0, 0, + create_stat, + 0, 0, + next_stat +}; + +static char *name_stat(Namval_t *np, Namfun_t *fp) +{ + Shell_t *shp = sh_getinterp(); + sfprintf(shp->strbuf,".sh.stats.%s",np->nvname); + return(sfstruse(shp->strbuf)); +} + +static const Namdisc_t stat_child_disc = +{ + 0,0,0,0,0,0,0, + name_stat +}; + +static Namfun_t stat_child_fun = +{ + &stat_child_disc, 1, 0, sizeof(Namfun_t) +}; + +static void stat_init(Shell_t *shp) +{ + int i,nstat = STAT_SUBSHELL+1; + struct Stats *sp = newof(0,struct Stats,1,nstat*NV_MINSZ); + Namval_t *np; + sp->numnodes = nstat; + sp->nodes = (char*)(sp+1); + shp->stats = (int*)calloc(sizeof(int*),nstat); + sp->sh = shp; + for(i=0; i < nstat; i++) + { + np = nv_namptr(sp->nodes,i); + np->nvfun = &stat_child_fun; + np->nvname = (char*)shtab_stats[i].sh_name; + nv_onattr(np,NV_RDONLY|NV_MINIMAL|NV_NOFREE|NV_INTEGER); + nv_setsize(np,10); + np->nvalue.ip = &shp->stats[i]; + } + sp->hdr.dsize = sizeof(struct Stats) + nstat*(sizeof(int)+NV_MINSZ); + sp->hdr.disc = &stat_disc; + nv_stack(SH_STATS,&sp->hdr); + sp->hdr.nofree = 1; + nv_setvtree(SH_STATS); +} +#else +# define stat_init(x) +#endif /* SHOPT_STATS */ + /* * Initialize the shell name and alias table */ static Init_t *nv_init(Shell_t *shp) { + static int shlvl=0; Namval_t *np; register Init_t *ip; double d=0; ip = newof(0,Init_t,1,0); if(!ip) return(0); + shp->nvfun.last = (char*)shp; + shp->nvfun.nofree = 1; ip->sh = shp; shp->var_base = shp->var_tree = inittree(shp,shtab_variables); + SHLVL->nvalue.ip = &shlvl; ip->IFS_init.hdr.disc = &IFS_disc; ip->IFS_init.hdr.nofree = 1; - ip->IFS_init.sh = shp; - ip->PATH_init.hdr.disc = &RESTRICTED_disc; - ip->PATH_init.hdr.nofree = 1; - ip->PATH_init.sh = shp; -#ifdef PATH_BFPATH - ip->FPATH_init.hdr.disc = &RESTRICTED_disc; - ip->FPATH_init.hdr.nofree = 1; - ip->FPATH_init.sh = shp; - ip->CDPATH_init.hdr.disc = &CDPATH_disc; - ip->CDPATH_init.hdr.nofree = 1; - ip->CDPATH_init.sh = shp; -#endif - ip->SHELL_init.hdr.disc = &RESTRICTED_disc; - ip->SHELL_init.sh = shp; - ip->SHELL_init.hdr.nofree = 1; - ip->ENV_init.hdr.disc = &RESTRICTED_disc; - ip->ENV_init.hdr.nofree = 1; - ip->ENV_init.sh = shp; - ip->VISUAL_init.hdr.disc = &EDITOR_disc; - ip->VISUAL_init.hdr.nofree = 1; - ip->VISUAL_init.sh = shp; - ip->EDITOR_init.hdr.disc = &EDITOR_disc; - ip->EDITOR_init.hdr.nofree = 1; - ip->EDITOR_init.sh = shp; - ip->OPTINDEX_init.hdr.disc = &OPTINDEX_disc; - ip->OPTINDEX_init.hdr.nofree = 1; - ip->OPTINDEX_init.sh = shp; + ip->PATH_init.disc = &RESTRICTED_disc; + ip->PATH_init.nofree = 1; + ip->FPATH_init.disc = &RESTRICTED_disc; + ip->FPATH_init.nofree = 1; + ip->CDPATH_init.disc = &CDPATH_disc; + ip->CDPATH_init.nofree = 1; + ip->SHELL_init.disc = &RESTRICTED_disc; + ip->SHELL_init.nofree = 1; + ip->ENV_init.disc = &RESTRICTED_disc; + ip->ENV_init.nofree = 1; + ip->VISUAL_init.disc = &EDITOR_disc; + ip->VISUAL_init.nofree = 1; + ip->EDITOR_init.disc = &EDITOR_disc; + ip->EDITOR_init.nofree = 1; + ip->HISTFILE_init.disc = &HISTFILE_disc; + ip->HISTFILE_init.nofree = 1; + ip->HISTSIZE_init.disc = &HISTFILE_disc; + ip->HISTSIZE_init.nofree = 1; + ip->OPTINDEX_init.disc = &OPTINDEX_disc; + ip->OPTINDEX_init.nofree = 1; ip->SECONDS_init.hdr.disc = &SECONDS_disc; ip->SECONDS_init.hdr.nofree = 1; - ip->SECONDS_init.sh = shp; ip->RAND_init.hdr.disc = &RAND_disc; ip->RAND_init.hdr.nofree = 1; ip->SH_MATCH_init.hdr.disc = &SH_MATCH_disc; ip->SH_MATCH_init.hdr.nofree = 1; - ip->LINENO_init.hdr.disc = &LINENO_disc; - ip->LINENO_init.hdr.nofree = 1; - ip->LINENO_init.sh = shp; - ip->L_ARG_init.hdr.disc = &L_ARG_disc; - ip->L_ARG_init.hdr.nofree = 1; + ip->SH_VERSION_init.disc = &SH_VERSION_disc; + ip->SH_VERSION_init.nofree = 1; + ip->LINENO_init.disc = &LINENO_disc; + ip->LINENO_init.nofree = 1; + ip->L_ARG_init.disc = &L_ARG_disc; + ip->L_ARG_init.nofree = 1; #ifdef _hdr_locale - ip->LC_TYPE_init.hdr.disc = &LC_disc; - ip->LC_TYPE_init.hdr.nofree = 1; - ip->LC_NUM_init.hdr.disc = &LC_disc; - ip->LC_NUM_init.hdr.nofree = 1; - ip->LC_COLL_init.hdr.disc = &LC_disc; - ip->LC_COLL_init.hdr.nofree = 1; - ip->LC_MSG_init.hdr.disc = &LC_disc; - ip->LC_MSG_init.hdr.nofree = 1; - ip->LC_ALL_init.hdr.disc = &LC_disc; - ip->LC_ALL_init.hdr.nofree = 1; - ip->LANG_init.hdr.disc = &LC_disc; - ip->LANG_init.hdr.nofree = 1; - ip->LC_TYPE_init.sh = shp; - ip->LC_NUM_init.sh = shp; - ip->LC_COLL_init.sh = shp; - ip->LC_MSG_init.sh = shp; - ip->LANG_init.sh = shp; + ip->LC_TYPE_init.disc = &LC_disc; + ip->LC_TYPE_init.nofree = 1; + ip->LC_NUM_init.disc = &LC_disc; + ip->LC_NUM_init.nofree = 1; + ip->LC_COLL_init.disc = &LC_disc; + ip->LC_COLL_init.nofree = 1; + ip->LC_MSG_init.disc = &LC_disc; + ip->LC_MSG_init.nofree = 1; + ip->LC_ALL_init.disc = &LC_disc; + ip->LC_ALL_init.nofree = 1; + ip->LANG_init.disc = &LC_disc; + ip->LANG_init.nofree = 1; #endif /* _hdr_locale */ nv_stack(IFSNOD, &ip->IFS_init.hdr); - nv_stack(PATHNOD, &ip->PATH_init.hdr); -#ifdef PATH_BFPATH - nv_stack(FPATHNOD, &ip->FPATH_init.hdr); - nv_stack(CDPNOD, &ip->CDPATH_init.hdr); -#endif - nv_stack(SHELLNOD, &ip->SHELL_init.hdr); - nv_stack(ENVNOD, &ip->ENV_init.hdr); - nv_stack(VISINOD, &ip->VISUAL_init.hdr); - nv_stack(EDITNOD, &ip->EDITOR_init.hdr); - nv_stack(OPTINDNOD, &ip->OPTINDEX_init.hdr); + nv_stack(PATHNOD, &ip->PATH_init); + nv_stack(FPATHNOD, &ip->FPATH_init); + nv_stack(CDPNOD, &ip->CDPATH_init); + nv_stack(SHELLNOD, &ip->SHELL_init); + nv_stack(ENVNOD, &ip->ENV_init); + nv_stack(VISINOD, &ip->VISUAL_init); + nv_stack(EDITNOD, &ip->EDITOR_init); + nv_stack(HISTFILE, &ip->HISTFILE_init); + nv_stack(HISTSIZE, &ip->HISTSIZE_init); + nv_stack(OPTINDNOD, &ip->OPTINDEX_init); nv_stack(SECONDS, &ip->SECONDS_init.hdr); - nv_stack(L_ARGNOD, &ip->L_ARG_init.hdr); - nv_putval(SECONDS, (char*)&d, NV_INTEGER|NV_DOUBLE); + nv_stack(L_ARGNOD, &ip->L_ARG_init); + nv_putval(SECONDS, (char*)&d, NV_DOUBLE); nv_stack(RANDNOD, &ip->RAND_init.hdr); d = (shp->pid&RANDMASK); - nv_putval(RANDNOD, (char*)&d, NV_INTEGER|NV_DOUBLE); - nv_stack(LINENO, &ip->LINENO_init.hdr); + nv_putval(RANDNOD, (char*)&d, NV_DOUBLE); + nv_stack(LINENO, &ip->LINENO_init); nv_putsub(SH_MATCHNOD,(char*)0,10); nv_onattr(SH_MATCHNOD,NV_RDONLY); nv_stack(SH_MATCHNOD, &ip->SH_MATCH_init.hdr); + nv_stack(SH_VERSIONNOD, &ip->SH_VERSION_init); #ifdef _hdr_locale - nv_stack(LCTYPENOD, &ip->LC_TYPE_init.hdr); - nv_stack(LCALLNOD, &ip->LC_ALL_init.hdr); - nv_stack(LCMSGNOD, &ip->LC_MSG_init.hdr); - nv_stack(LCCOLLNOD, &ip->LC_COLL_init.hdr); - nv_stack(LCNUMNOD, &ip->LC_NUM_init.hdr); - nv_stack(LANGNOD, &ip->LANG_init.hdr); + nv_stack(LCTYPENOD, &ip->LC_TYPE_init); + nv_stack(LCALLNOD, &ip->LC_ALL_init); + nv_stack(LCMSGNOD, &ip->LC_MSG_init); + nv_stack(LCCOLLNOD, &ip->LC_COLL_init); + nv_stack(LCNUMNOD, &ip->LC_NUM_init); + nv_stack(LANGNOD, &ip->LANG_init); #endif /* _hdr_locale */ (PPIDNOD)->nvalue.lp = (&shp->ppid); (TMOUTNOD)->nvalue.lp = (&shp->st.tmout); @@ -1278,12 +1531,6 @@ static Init_t *nv_init(Shell_t *shp) shp->alias_tree = inittree(shp,shtab_aliases); shp->track_tree = dtopen(&_Nvdisc,Dtset); shp->bltin_tree = inittree(shp,(const struct shtable2*)shtab_builtins); - typeset.shp = shp; - typeset.optstring = sh_opttypeset; - nv_search("typeset",shp->bltin_tree,0)->nvfun = (void*)&typeset; -#if SHOPT_BASH - nv_search("local",shp->bltin_tree,0)->nvfun = (void*)&typeset; -#endif shp->fun_tree = dtopen(&_Nvdisc,Dtoset); dtview(shp->fun_tree,shp->bltin_tree); #if SHOPT_NAMESPACE @@ -1293,8 +1540,15 @@ static Init_t *nv_init(Shell_t *shp) nv_putval(np,".sh.global",NV_RDONLY|NV_NOFREE); nv_stack(np, &NSPACE_init); #endif /* SHOPT_NAMESPACE */ - np = nv_mount(DOTSHNOD, "type", dtopen(&_Nvdisc,Dtoset)); + np = nv_mount(DOTSHNOD, "type", shp->typedict=dtopen(&_Nvdisc,Dtoset)); nv_adddisc(DOTSHNOD, shdiscnames, (Namval_t**)0); + SH_LINENO->nvalue.ip = &shp->st.lineno; + VERSIONNOD->nvalue.nrp = newof(0,struct Namref,1,0); + VERSIONNOD->nvalue.nrp->np = SH_VERSIONNOD; + VERSIONNOD->nvalue.nrp->root = nv_dict(DOTSHNOD); + VERSIONNOD->nvalue.nrp->table = DOTSHNOD; + nv_onattr(VERSIONNOD,NV_RDONLY|NV_REF); + stat_init(shp); return(ip); } @@ -1313,10 +1567,17 @@ static Dt_t *inittree(Shell_t *shp,const struct shtable2 *name_vals) n++; np = (Namval_t*)calloc(n,sizeof(Namval_t)); if(!shp->bltin_nodes) + { shp->bltin_nodes = np; + shp->bltin_nnodes = n; + } else if(name_vals==(const struct shtable2*)shtab_builtins) + { shp->bltin_cmds = np; + nbltins = n; + } base_treep = treep = dtopen(&_Nvdisc,Dtoset); + treep->user = (void*)shp; for(tp=name_vals;*tp->sh_name;tp++,np++) { if((np->nvname = strrchr(tp->sh_name,'.')) && np->nvname!=((char*)tp->sh_name)) @@ -1330,7 +1591,11 @@ static Dt_t *inittree(Shell_t *shp,const struct shtable2 *name_vals) if(name_vals==(const struct shtable2*)shtab_builtins) np->nvalue.bfp = ((struct shtable3*)tp)->sh_value; else + { + if(name_vals == shtab_variables) + np->nvfun = &sh.nvfun; np->nvalue.cp = (char*)tp->sh_value; + } nv_setattr(np,tp->sh_number); if(nv_istable(np)) nv_mount(np,(const char*)0,dict=dtopen(&_Nvdisc,Dtoset)); @@ -1372,6 +1637,11 @@ static void env_init(Shell_t *shp) np->nvenv = cp; nv_close(np); } + else /* swap with fron */ + { + ep[-1] = environ[shp->nenv]; + environ[shp->nenv++] = cp; + } } while(cp=next) { @@ -1412,7 +1682,7 @@ static void env_init(Shell_t *shp) } } #ifdef _ENV_H - env_delete(sh.env,e_envmarker); + env_delete(shp->env,e_envmarker); #endif if(nv_isnull(PWDNOD) || nv_isattr(PWDNOD,NV_TAGGED)) { diff --git a/usr/src/lib/libshell/common/sh/io.c b/usr/src/lib/libshell/common/sh/io.c index a14a941942..50fa35561f 100644 --- a/usr/src/lib/libshell/common/sh/io.c +++ b/usr/src/lib/libshell/common/sh/io.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -79,10 +79,16 @@ static int (*fdnotify)(int,int); # define htonl(x) (x) # endif # if _pipe_socketpair +# ifndef SHUT_RD +# define SHUT_RD 0 +# endif +# ifndef SHUT_WR +# define SHUT_WR 1 +# endif # if _socketpair_shutdown_mode -# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[0],1)<0||fchmod((v)[0],S_IRUSR)<0||shutdown((v)[1],0)<0||fchmod((v)[1],S_IWUSR)<0)?(-1):0) +# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[1],SHUT_RD)<0||fchmod((v)[1],S_IWUSR)<0||shutdown((v)[0],SHUT_WR)<0||fchmod((v)[0],S_IRUSR)<0)?(-1):0) # else -# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[0],1)<0||shutdown((v)[1],0)<0)?(-1):0) +# define pipe(v) ((socketpair(AF_UNIX,SOCK_STREAM,0,v)<0||shutdown((v)[1],SHUT_RD)<0||shutdown((v)[0],SHUT_WR)<0)?(-1):0) # endif # endif @@ -308,6 +314,7 @@ struct fdsave int orig_fd; /* original file descriptor */ int save_fd; /* saved file descriptor */ int subshell; /* saved for subshell */ + char *tname; /* name used with >; */ }; static int subexcept(Sfio_t*, int, void*, Sfdisc_t*); @@ -319,11 +326,11 @@ static ssize_t slowread(Sfio_t*, void*, size_t, Sfdisc_t*); static ssize_t subread(Sfio_t*, void*, size_t, Sfdisc_t*); static ssize_t tee_write(Sfio_t*,const void*,size_t,Sfdisc_t*); static int io_prompt(Sfio_t*,int); -static int io_heredoc(register struct ionod*, const char*, int); -static void sftrack(Sfio_t*,int,int); +static int io_heredoc(Shell_t*,register struct ionod*, const char*, int); +static void sftrack(Sfio_t*,int,void*); static const Sfdisc_t eval_disc = { NULL, NULL, NULL, eval_exceptf, NULL}; static Sfdisc_t tee_disc = {NULL,tee_write,NULL,NULL,NULL}; -static Sfio_t *subopen(Sfio_t*, off_t, long); +static Sfio_t *subopen(Shell_t *,Sfio_t*, off_t, long); static const Sfdisc_t sub_disc = { subread, 0, 0, subexcept, 0 }; struct subfile @@ -376,36 +383,80 @@ static short filemapsize; /* ======== input output and file copying ======== */ -void sh_ioinit(void) +void sh_ioinit(Shell_t *shp) { register int n; filemapsize = 8; - filemap = (struct fdsave*)malloc(8*sizeof(struct fdsave)); + filemap = (struct fdsave*)malloc(filemapsize*sizeof(struct fdsave)); #if SHOPT_FASTPIPE - n = sh.lim.open_max+2; + n = shp->lim.open_max+2; #else - n = sh.lim.open_max; + n = shp->lim.open_max; #endif /* SHOPT_FASTPIPE */ - sh.fdstatus = (unsigned char*)malloc((unsigned)n); - memset((char*)sh.fdstatus,0,n); - sh.fdptrs = (int**)malloc(n*sizeof(int*)); - memset((char*)sh.fdptrs,0,n*sizeof(int*)); - sh.sftable = (Sfio_t**)malloc(n*sizeof(Sfio_t*)); - memset((char*)sh.sftable,0,n*sizeof(Sfio_t*)); - sh.sftable[0] = sfstdin; - sh.sftable[1] = sfstdout; - sh.sftable[2] = sfstderr; + shp->fdstatus = (unsigned char*)malloc((unsigned)n); + memset((char*)shp->fdstatus,0,n); + shp->fdptrs = (int**)malloc(n*sizeof(int*)); + memset((char*)shp->fdptrs,0,n*sizeof(int*)); + shp->sftable = (Sfio_t**)malloc(n*sizeof(Sfio_t*)); + memset((char*)shp->sftable,0,n*sizeof(Sfio_t*)); + shp->sftable[0] = sfstdin; + shp->sftable[1] = sfstdout; + shp->sftable[2] = sfstderr; sfnotify(sftrack); - sh_iostream(0); + sh_iostream(shp,0); /* all write steams are in the same pool and share outbuff */ - sh.outpool = sfopen(NIL(Sfio_t*),NIL(char*),"sw"); /* pool identifier */ - sh.outbuff = (char*)malloc(IOBSIZE); - sh.errbuff = (char*)malloc(IOBSIZE/4); - sfsetbuf(sfstderr,sh.errbuff,IOBSIZE/4); - sfsetbuf(sfstdout,sh.outbuff,IOBSIZE); - sfpool(sfstdout,sh.outpool,SF_WRITE); - sfpool(sfstderr,sh.outpool,SF_WRITE); + shp->outpool = sfopen(NIL(Sfio_t*),NIL(char*),"sw"); /* pool identifier */ + shp->outbuff = (char*)malloc(IOBSIZE); + shp->errbuff = (char*)malloc(IOBSIZE/4); + sfsetbuf(sfstderr,shp->errbuff,IOBSIZE/4); + sfsetbuf(sfstdout,shp->outbuff,IOBSIZE); + sfpool(sfstdout,shp->outpool,SF_WRITE); + sfpool(sfstderr,shp->outpool,SF_WRITE); sfset(sfstdout,SF_LINE,0); + sfset(sfstderr,SF_LINE,0); + sfset(sfstdin,SF_SHARE|SF_PUBLIC,1); +} + +/* + * Handle output stream exceptions + */ +static int outexcept(register Sfio_t *iop,int type,void *data,Sfdisc_t *handle) +{ + static int active = 0; + + NOT_USED(handle); + if(type==SF_DPOP || type==SF_FINAL) + free((void*)handle); + else if(type==SF_WRITE && (*(ssize_t*)data)<0 && sffileno(iop)!=2) + switch (errno) + { + case EINTR: + case EPIPE: +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ESHUTDOWN + case ESHUTDOWN: +#endif + break; + default: + if(!active) + { + int mode = ((struct checkpt*)sh.jmplist)->mode; + int save = errno; + active = 1; + ((struct checkpt*)sh.jmplist)->mode = 0; + sfpurge(iop); + sfpool(iop,NIL(Sfio_t*),SF_WRITE); + errno = save; + errormsg(SH_DICT,ERROR_system(1),e_badwrite,sffileno(iop)); + active = 0; + ((struct checkpt*)sh.jmplist)->mode = mode; + sh_exit(1); + } + return(-1); + } + return(0); } /* @@ -415,15 +466,16 @@ void sh_ioinit(void) * For output streams, the buffer is set to sh.output and put into * the sh.outpool synchronization pool */ -Sfio_t *sh_iostream(register int fd) +Sfio_t *sh_iostream(Shell_t *shp, register int fd) { register Sfio_t *iop; - register int status = sh_iocheckfd(fd); + register int status = sh_iocheckfd(shp,fd); register int flags = SF_WRITE; char *bp; + Sfdisc_t *dp; #if SHOPT_FASTPIPE - if(fd>=sh.lim.open_max) - return(sh.sftable[fd]); + if(fd>=shp->lim.open_max) + return(shp->sftable[fd]); #endif /* SHOPT_FASTPIPE */ if(status==IOCLOSE) { @@ -447,69 +499,71 @@ Sfio_t *sh_iostream(register int fd) flags &= ~SF_WRITE; } else - bp = sh.outbuff; + bp = shp->outbuff; if(status&IODUP) flags |= SF_SHARE|SF_PUBLIC; - if((iop = sh.sftable[fd]) && sffileno(iop)>=0) + if((iop = shp->sftable[fd]) && sffileno(iop)>=0) sfsetbuf(iop, bp, IOBSIZE); else if(!(iop=sfnew((fd<=2?iop:0),bp,IOBSIZE,fd,flags))) return(NIL(Sfio_t*)); + dp = newof(0,Sfdisc_t,1,0); if(status&IOREAD) { - Sfdisc_t *dp; sfset(iop,SF_MALLOC,1); + if(!(status&IOWRITE)) + sfset(iop,SF_IOCHECK,1); + dp->exceptf = slowexcept; + if(status&IOTTY) + dp->readf = slowread; + else if(status&IONOSEEK) { - dp = newof(0,Sfdisc_t,1,0); - dp->exceptf = slowexcept; - if(status&IOTTY) - dp->readf = slowread; - else if(status&IONOSEEK) - { - dp->readf = piperead; - sfset(iop, SF_IOINTR,1); - } - else - dp->readf = 0; - dp->seekf = 0; - dp->writef = 0; - sfdisc(iop,dp); + dp->readf = piperead; + sfset(iop, SF_IOINTR,1); } + else + dp->readf = 0; + dp->seekf = 0; + dp->writef = 0; } else - sfpool(iop,sh.outpool,SF_WRITE); - sh.sftable[fd] = iop; + { + dp->exceptf = outexcept; + sfpool(iop,shp->outpool,SF_WRITE); + } + sfdisc(iop,dp); + shp->sftable[fd] = iop; return(iop); } /* * preserve the file descriptor or stream by moving it */ -static void io_preserve(register Sfio_t *sp, register int f2) +static void io_preserve(Shell_t* shp, register Sfio_t *sp, register int f2) { register int fd; if(sp) fd = sfsetfd(sp,10); else fd = sh_fcntl(f2,F_DUPFD,10); - if(f2==sh.infd) - sh.infd = fd; + if(f2==shp->infd) + shp->infd = fd; if(fd<0) errormsg(SH_DICT,ERROR_system(1),e_toomany); - if(sh.fdptrs[fd]=sh.fdptrs[f2]) + if(shp->fdptrs[fd]=shp->fdptrs[f2]) { if(f2==job.fd) job.fd=fd; - *sh.fdptrs[fd] = fd; - sh.fdptrs[f2] = 0; + *shp->fdptrs[fd] = fd; + shp->fdptrs[f2] = 0; } - sh.sftable[fd] = sp; - sh.fdstatus[fd] = sh.fdstatus[f2]; + shp->sftable[fd] = sp; + shp->fdstatus[fd] = shp->fdstatus[f2]; if(fcntl(f2,F_GETFD,0)&1) { fcntl(fd,F_SETFD,FD_CLOEXEC); - sh.fdstatus[fd] |= IOCLEX; + shp->fdstatus[fd] |= IOCLEX; } - sh.sftable[f2] = 0; + shp->sftable[f2] = 0; } /* @@ -518,39 +572,39 @@ static void io_preserve(register Sfio_t *sp, register int f2) * The original stream <f1> is closed. * The new file descriptor <f2> is returned; */ -int sh_iorenumber(register int f1,register int f2) +int sh_iorenumber(Shell_t *shp, register int f1,register int f2) { - register Sfio_t *sp = sh.sftable[f2]; + register Sfio_t *sp = shp->sftable[f2]; if(f1!=f2) { /* see whether file descriptor is in use */ if(sh_inuse(f2) || (f2>2 && sp)) { - if(!(sh.inuse_bits&(1<<f2))) - io_preserve(sp,f2); + if(!(shp->inuse_bits&(1<<f2))) + io_preserve(shp,sp,f2); sp = 0; } else if(f2==0) - sh.st.ioset = 1; + shp->st.ioset = 1; sh_close(f2); if(f2<=2 && sp) { - register Sfio_t *spnew = sh_iostream(f1); - sh.fdstatus[f2] = (sh.fdstatus[f1]&~IOCLEX); + register Sfio_t *spnew = sh_iostream(shp,f1); + shp->fdstatus[f2] = (shp->fdstatus[f1]&~IOCLEX); sfsetfd(spnew,f2); sfswap(spnew,sp); sfset(sp,SF_SHARE|SF_PUBLIC,1); } else { - sh.fdstatus[f2] = (sh.fdstatus[f1]&~IOCLEX); + shp->fdstatus[f2] = (shp->fdstatus[f1]&~IOCLEX); if((f2 = sh_fcntl(f1,F_DUPFD, f2)) < 0) errormsg(SH_DICT,ERROR_system(1),e_file+4); else if(f2 <= 2) - sh_iostream(f2); + sh_iostream(shp,f2); } if(sp) - sh.sftable[f1] = 0; + shp->sftable[f1] = 0; sh_close(f1); } return(f2); @@ -582,6 +636,8 @@ int sh_close(register int fd) return(r); } +#ifdef O_SERVICE + static int onintr(struct addrinfo* addr, void* handle) { @@ -598,11 +654,14 @@ onintr(struct addrinfo* addr, void* handle) return 0; } +#endif + /* * Mimic open(2) with checks for pseudo /dev/ files. */ int sh_open(register const char *path, int flags, ...) { + Shell_t *shp = &sh; register int fd = -1; mode_t mode; char *e; @@ -658,7 +717,7 @@ int sh_open(register const char *path, int flags, ...) } if (fd >= 0) { - if((mode=sh_iocheckfd(fd))==IOCLOSE) + if((mode=sh_iocheckfd(shp,fd))==IOCLOSE) return(-1); flags &= O_ACCMODE; if(!(mode&IOWRITE) && ((flags==O_WRONLY) || (flags==O_RDWR))) @@ -748,13 +807,13 @@ static int pat_line(const regex_t* rp, const char *buff, register size_t n) return(cp-buff); } -static int io_patseek(regex_t *rp, Sfio_t* sp, int flags) +static int io_patseek(Shell_t *shp, regex_t *rp, Sfio_t* sp, int flags) { char *cp, *match; - int r, fd=sffileno(sp), close_exec = sh.fdstatus[fd]&IOCLEX; + int r, fd=sffileno(sp), close_exec = shp->fdstatus[fd]&IOCLEX; int was_share,s=(PIPE_BUF>SF_BUFSIZE?SF_BUFSIZE:PIPE_BUF); size_t n,m; - sh.fdstatus[sffileno(sp)] |= IOCLEX; + shp->fdstatus[sffileno(sp)] |= IOCLEX; if(fd==0) was_share = sfset(sp,SF_SHARE,1); while((cp=sfreserve(sp, -s, SF_LOCKR)) || (cp=sfreserve(sp,SF_UNBOUND, SF_LOCKR))) @@ -779,20 +838,20 @@ static int io_patseek(regex_t *rp, Sfio_t* sp, int flags) break; } if(!close_exec) - sh.fdstatus[sffileno(sp)] &= ~IOCLEX; + shp->fdstatus[sffileno(sp)] &= ~IOCLEX; if(fd==0 && !(was_share&SF_SHARE)) sfset(sp, SF_SHARE,0); return(0); } -static Sfoff_t file_offset(int fn, char *fname) +static Sfoff_t file_offset(Shell_t *shp, int fn, char *fname) { - Sfio_t *sp = sh.sftable[fn]; + Sfio_t *sp = shp->sftable[fn]; char *cp; Sfoff_t off; struct Eof endf; - Namval_t *mp = nv_open("EOF",sh.var_tree,0); - Namval_t *pp = nv_open("CUR",sh.var_tree,0); + Namval_t *mp = nv_open("EOF",shp->var_tree,0); + Namval_t *pp = nv_open("CUR",shp->var_tree,0); memset(&endf,0,sizeof(struct Eof)); endf.fd = fn; endf.hdr.disc = &EOF_disc; @@ -823,6 +882,48 @@ void sh_pclose(register int pv[]) pv[0] = pv[1] = -1; } +static char *io_usename(char *name, int *perm, int mode) +{ + struct stat statb; + char *tname, *sp, *ep; + int fd,len,n=0; + if(mode==0) + { + if((fd = sh_open(name,O_RDONLY,0)) > 0) + { + if(fstat(fd,&statb) < 0) + return(0); + if(!S_ISREG(statb.st_mode)) + return(0); + *perm = statb.st_mode&(RW_ALL|(S_IXUSR|S_IXGRP|S_IXOTH)); + } + else if(fd < 0 && errno!=ENOENT) + return(0); + } + tname = sp = (char*)stakalloc((len=strlen(name)) + 5); + if(ep = strrchr(name,'/')) + { + memcpy(sp,name,n=++ep-name); + len -=n; + sp += n; + } + else + ep = name; + *sp++ = '.'; + memcpy(sp,ep,len); + strcpy(sp+len,".tmp"); + switch(mode) + { + case 1: + rename(tname,name); + break; + case 2: + unlink(tname); + break; + } + return(tname); +} + /* * I/O redirection * flag = 0 if files are to be restored @@ -830,7 +931,7 @@ void sh_pclose(register int pv[]) * flag = 3 when called from $( < ...), just open file and return * flag = SH_SHOWME for trace only */ -int sh_redirect(struct ionod *iop, int flag) +int sh_redirect(Shell_t *shp,struct ionod *iop, int flag) { Sfoff_t off; register char *fname; @@ -839,8 +940,8 @@ int sh_redirect(struct ionod *iop, int flag) int o_mode; /* mode flag for open */ static char io_op[7]; /* used for -x trace info */ int clexec=0, fn, traceon; - int r, indx = sh.topfd; - char *after="", *trace = sh.st.trap[SH_DEBUGTRAP]; + int r, indx = shp->topfd, perm= -1; + char *tname=0, *after="", *trace = shp->st.trap[SH_DEBUGTRAP]; Namval_t *np=0; if(flag==2) clexec = 1; @@ -850,6 +951,8 @@ int sh_redirect(struct ionod *iop, int flag) { iof=iop->iofile; fn = (iof&IOUFD); + if(fn==1 && shp->subshell && (flag==2 || (sfset(sfstdout,0,0)&SF_STRING))) + sh_subfork(); io_op[0] = '0'+(iof&IOUFD); if(iof&IOPUT) { @@ -873,19 +976,20 @@ int sh_redirect(struct ionod *iop, int flag) memset(ap, 0, ARGVAL); ap->argflag = ARG_MAC; strcpy(ap->argval,iop->ioname); - fname=sh_macpat(ap,(iof&IOARITH)?ARG_ARITH:ARG_EXP); + fname=sh_macpat(shp,ap,(iof&IOARITH)?ARG_ARITH:ARG_EXP); } else - fname=sh_mactrim(fname,(!sh_isoption(SH_NOGLOB)&&sh_isoption(SH_INTERACTIVE))?2:0); + fname=sh_mactrim(shp,fname,(!sh_isoption(SH_NOGLOB)&&sh_isoption(SH_INTERACTIVE))?2:0); } errno=0; + np = 0; if(iop->iovname) { - np = nv_open(iop->iovname,sh.var_tree,NV_NOASSIGN|NV_VARNAME); + np = nv_open(iop->iovname,shp->var_tree,NV_NOASSIGN|NV_VARNAME); if(nv_isattr(np,NV_RDONLY)) errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np)); io_op[0] = '}'; - if((iof&IOMOV) && *fname=='-') + if((iof&IOLSEEK) || ((iof&IOMOV) && *fname=='-')) fn = nv_getnum(np); } if(iof&IOLSEEK) @@ -906,7 +1010,7 @@ int sh_redirect(struct ionod *iop, int flag) { if(traceon) sfputr(sfstderr,io_op,'<'); - fd = io_heredoc(iop,fname,traceon); + fd = io_heredoc(shp,iop,fname,traceon); if(traceon && (flag==SH_SHOWME)) sh_close(fd); fname = 0; @@ -929,13 +1033,13 @@ int sh_redirect(struct ionod *iop, int flag) message = e_file; goto fail; } - if(sh.subshell && dupfd==1) + if(shp->subshell && dupfd==1) { - sh_subtmpfile(); + sh_subtmpfile(0); dupfd = sffileno(sfstdout); } - else if(sh.sftable[dupfd]) - sfsync(sh.sftable[dupfd]); + else if(shp->sftable[dupfd]) + sfsync(shp->sftable[dupfd]); } else if(fd=='-' && fname[1]==0) { @@ -945,9 +1049,9 @@ int sh_redirect(struct ionod *iop, int flag) else if(fd=='p' && fname[1]==0) { if(iof&IOPUT) - dupfd = sh.coutpipe; + dupfd = shp->coutpipe; else - dupfd = sh.cpipe[0]; + dupfd = shp->cpipe[0]; if(flag) toclose = dupfd; } @@ -960,16 +1064,16 @@ int sh_redirect(struct ionod *iop, int flag) goto traceit; if((fd=sh_fcntl(dupfd,F_DUPFD,3))<0) goto fail; - sh_iocheckfd(dupfd); - sh.fdstatus[fd] = (sh.fdstatus[dupfd]&~IOCLEX); - if(toclose<0 && sh.fdstatus[fd]&IOREAD) - sh.fdstatus[fd] |= IODUP; - else if(dupfd==sh.cpipe[0]) - sh_pclose(sh.cpipe); + sh_iocheckfd(shp,dupfd); + shp->fdstatus[fd] = (shp->fdstatus[dupfd]&~IOCLEX); + if(toclose<0 && shp->fdstatus[fd]&IOREAD) + shp->fdstatus[fd] |= IODUP; + else if(dupfd==shp->cpipe[0]) + sh_pclose(shp->cpipe); else if(toclose>=0) { if(flag==0) - sh_iosave(toclose,indx); /* save file descriptor */ + sh_iosave(shp,toclose,indx,(char*)0); /* save file descriptor */ sh_close(toclose); } } @@ -996,6 +1100,12 @@ int sh_redirect(struct ionod *iop, int flag) io_op[2] = '>'; o_mode |= O_APPEND; } + else if((iof&IOREWRITE) && (flag==0 || flag==1 || sh_subsavefd(fn))) + { + io_op[2] = ';'; + o_mode |= O_TRUNC; + tname = io_usename(fname,&perm,0); + } else { o_mode |= O_TRUNC; @@ -1008,7 +1118,7 @@ int sh_redirect(struct ionod *iop, int flag) { #if SHOPT_FS_3D if(S_ISREG(sb.st_mode)&& - (!sh.lim.fs3d || iview(&sb)==0)) + (!shp->lim.fs3d || iview(&sb)==0)) #else if(S_ISREG(sb.st_mode)) #endif /* SHOPT_FS_3D */ @@ -1024,8 +1134,14 @@ int sh_redirect(struct ionod *iop, int flag) openit: if(flag!=SH_SHOWME) { - if((fd=sh_open(fname,o_mode,RW_ALL)) <0) + if((fd=sh_open(tname?tname:fname,o_mode,RW_ALL)) <0) errormsg(SH_DICT,ERROR_system(1),((o_mode&O_CREAT)?e_create:e_open),fname); + if(perm>0) +#if _lib_fchmod + fchmod(fd,perm); +#else + chmod(tname,perm); +#endif } } traceit: @@ -1054,14 +1170,14 @@ int sh_redirect(struct ionod *iop, int flag) } else av +=3; - sh_debug(trace,(char*)0,(char*)0,av,ARG_NOGLOB); + sh_debug(shp,trace,(char*)0,(char*)0,av,ARG_NOGLOB); } if(iof&IOLSEEK) { - Sfio_t *sp = sh.sftable[fn]; - r = sh.fdstatus[fn]; + Sfio_t *sp = shp->sftable[fn]; + r = shp->fdstatus[fn]; if(!(r&(IOSEEK|IONOSEEK))) - r = sh_iocheckfd(fn); + r = sh_iocheckfd(shp,fn); sfsprintf(io_op,sizeof(io_op),"%d\0",fn); if(r==IOCLOSE) { @@ -1078,12 +1194,14 @@ int sh_redirect(struct ionod *iop, int flag) goto fail; } message = e_badseek; - if((off = file_offset(fn,fname))<0) + if((off = file_offset(shp,fn,fname))<0) goto fail; if(sp) - r=sfseek(sp, off, SEEK_SET); + off=sfseek(sp, off, SEEK_SET); else - r=lseek(fn, off, SEEK_SET); + off=lseek(fn, off, SEEK_SET); + if(off<0) + r = -1; } else { @@ -1100,8 +1218,8 @@ int sh_redirect(struct ionod *iop, int flag) goto fail; } if(!sp) - sp = sh_iostream(fn); - r=io_patseek(rp,sp,iof); + sp = sh_iostream(shp,fn); + r=io_patseek(shp,rp,sp,iof); if(sp && flag==3) { /* close stream but not fn */ @@ -1117,7 +1235,7 @@ int sh_redirect(struct ionod *iop, int flag) } if(!np) { - if(flag==0) + if(flag==0 || tname) { if(fd==fn) { @@ -1127,17 +1245,17 @@ int sh_redirect(struct ionod *iop, int flag) sh_close(fn); } } - sh_iosave(fn,indx); + sh_iosave(shp,fn,indx,tname?fname:0); } else if(sh_subsavefd(fn)) - sh_iosave(fn,indx|IOSUBSHELL); + sh_iosave(shp,fn,indx|IOSUBSHELL,tname?fname:0); } if(fd<0) { - if(sh_inuse(fn) || fn==sh.infd) + if(sh_inuse(fn) || fn==shp->infd) { - if(fn>9 || !(sh.inuse_bits&(1<<fn))) - io_preserve(sh.sftable[fn],fn); + if(fn>9 || !(shp->inuse_bits&(1<<fn))) + io_preserve(shp,shp->sftable[fn],fn); } sh_close(fn); } @@ -1153,7 +1271,7 @@ int sh_redirect(struct ionod *iop, int flag) { if((fn=fcntl(fd,F_DUPFD,10)) < 0) goto fail; - sh.fdstatus[fn] = sh.fdstatus[fd]; + shp->fdstatus[fn] = shp->fdstatus[fd]; sh_close(fd); fd = fn; } @@ -1161,19 +1279,19 @@ int sh_redirect(struct ionod *iop, int flag) nv_onattr(np,NV_INT32); v = fn; nv_putval(np,(char*)&v, NV_INT32); - sh_iocheckfd(fd); + sh_iocheckfd(shp,fd); } else { - fd = sh_iorenumber(sh_iomovefd(fd),fn); + fd = sh_iorenumber(shp,sh_iomovefd(fd),fn); if(fn>2 && fn<10) - sh.inuse_bits |= (1<<fn); + shp->inuse_bits |= (1<<fn); } } if(fd >2 && clexec) { fcntl(fd,F_SETFD,FD_CLOEXEC); - sh.fdstatus[fd] |= IOCLEX; + shp->fdstatus[fd] |= IOCLEX; } } else @@ -1188,11 +1306,11 @@ fail: /* * Create a tmp file for the here-document */ -static int io_heredoc(register struct ionod *iop, const char *name, int traceon) +static int io_heredoc(Shell_t *shp,register struct ionod *iop, const char *name, int traceon) { register Sfio_t *infile = 0, *outfile; register int fd; - if(!(iop->iofile&IOSTRG) && (!sh.heredocs || iop->iosize==0)) + if(!(iop->iofile&IOSTRG) && (!shp->heredocs || iop->iosize==0)) return(sh_open(e_devnull,O_RDONLY)); /* create an unnamed temporary file */ if(!(outfile=sftmp(0))) @@ -1205,7 +1323,7 @@ static int io_heredoc(register struct ionod *iop, const char *name, int traceon) } else { - infile = subopen(sh.heredocs,iop->iooffset,iop->iosize); + infile = subopen(shp,shp->heredocs,iop->iooffset,iop->iosize); if(traceon) { char *cp = sh_fmtq(iop->iodelim); @@ -1221,9 +1339,9 @@ static int io_heredoc(register struct ionod *iop, const char *name, int traceon) } else { - char *lastpath = sh.lastpath; - sh_machere(infile,outfile,iop->ioname); - sh.lastpath = lastpath; + char *lastpath = shp->lastpath; + sh_machere(shp,infile,outfile,iop->ioname); + shp->lastpath = lastpath; if(infile) sfclose(infile); } @@ -1235,7 +1353,7 @@ static int io_heredoc(register struct ionod *iop, const char *name, int traceon) if(traceon && !(iop->iofile&IOSTRG)) sfputr(sfstderr,iop->ioname,'\n'); lseek(fd,(off_t)0,SEEK_SET); - sh.fdstatus[fd] = IOREAD; + shp->fdstatus[fd] = IOREAD; return(fd); } @@ -1256,28 +1374,43 @@ static ssize_t tee_write(Sfio_t *iop,const void *buff,size_t n,Sfdisc_t *unused) * if <origfd> < 0, then -origfd is saved, but not duped so that it * will be closed with sh_iorestore. */ -void sh_iosave(register int origfd, int oldtop) +void sh_iosave(Shell_t *shp, register int origfd, int oldtop, char *name) { /*@ - assume oldtop>=0 && oldtop<sh.lim.open_max; + assume oldtop>=0 && oldtop<shp->lim.open_max; @*/ register int savefd; int flag = (oldtop&IOSUBSHELL); oldtop &= ~IOSUBSHELL; /* see if already saved, only save once */ - for(savefd=sh.topfd; --savefd>=oldtop; ) + for(savefd=shp->topfd; --savefd>=oldtop; ) { if(filemap[savefd].orig_fd == origfd) return; } /* make sure table is large enough */ - if(sh.topfd >= filemapsize) + if(shp->topfd >= filemapsize) { + char *cp, *oldptr = (char*)filemap; + char *oldend = (char*)&filemap[filemapsize]; + long moved; filemapsize += 8; if(!(filemap = (struct fdsave*)realloc(filemap,filemapsize*sizeof(struct fdsave)))) errormsg(SH_DICT,ERROR_exit(4),e_nospace); - + if(moved = (char*)filemap - oldptr) + { +#if SHOPT_FASTPIPE + for(savefd=shp->lim.open_max+2; --savefd>=0; ) +#else + for(savefd=shp->lim.open_max; --savefd>=0; ) +#endif /* SHOPT_FASTPIPE */ + { + cp = (char*)shp->fdptrs[savefd]; + if(cp >= oldptr && cp < oldend) + shp->fdptrs[savefd] = (int*)(oldptr+moved); + } + } } #if SHOPT_DEVFD if(origfd <0) @@ -1291,60 +1424,61 @@ void sh_iosave(register int origfd, int oldtop) if((savefd = sh_fcntl(origfd, F_DUPFD, 10)) < 0 && errno!=EBADF) errormsg(SH_DICT,ERROR_system(1),e_toomany); } - filemap[sh.topfd].subshell = flag; - filemap[sh.topfd].orig_fd = origfd; - filemap[sh.topfd++].save_fd = savefd; + filemap[shp->topfd].tname = name; + filemap[shp->topfd].subshell = flag; + filemap[shp->topfd].orig_fd = origfd; + filemap[shp->topfd++].save_fd = savefd; if(savefd >=0) { - register Sfio_t* sp = sh.sftable[origfd]; + register Sfio_t* sp = shp->sftable[origfd]; /* make saved file close-on-exec */ sh_fcntl(savefd,F_SETFD,FD_CLOEXEC); if(origfd==job.fd) job.fd = savefd; - sh.fdstatus[savefd] = sh.fdstatus[origfd]; - sh.fdptrs[savefd] = &filemap[sh.topfd-1].save_fd; - if(!(sh.sftable[savefd]=sp)) + shp->fdstatus[savefd] = shp->fdstatus[origfd]; + shp->fdptrs[savefd] = &filemap[shp->topfd-1].save_fd; + if(!(shp->sftable[savefd]=sp)) return; sfsync(sp); if(origfd <=2) { /* copy standard stream to new stream */ sp = sfswap(sp,NIL(Sfio_t*)); - sh.sftable[savefd] = sp; + shp->sftable[savefd] = sp; } else - sh.sftable[origfd] = 0; + shp->sftable[origfd] = 0; } } /* * close all saved file descriptors */ -void sh_iounsave(void) +void sh_iounsave(Shell_t* shp) { register int fd, savefd, newfd; - for(newfd=fd=0; fd < sh.topfd; fd++) + for(newfd=fd=0; fd < shp->topfd; fd++) { if((savefd = filemap[fd].save_fd)< 0) filemap[newfd++] = filemap[fd]; else { - sh.sftable[savefd] = 0; + shp->sftable[savefd] = 0; sh_close(savefd); } } - sh.topfd = newfd; + shp->topfd = newfd; } /* * restore saved file descriptors from <last> on */ -void sh_iorestore(int last, int jmpval) +void sh_iorestore(Shell_t *shp, int last, int jmpval) { register int origfd, savefd, fd; int flag = (last&IOSUBSHELL); last &= ~IOSUBSHELL; - for (fd = sh.topfd - 1; fd >= last; fd--) + for (fd = shp->topfd - 1; fd >= last; fd--) { if(!flag && filemap[fd].subshell) continue; @@ -1352,47 +1486,49 @@ void sh_iorestore(int last, int jmpval) { if ((savefd = filemap[fd].save_fd) >= 0) { - sh.sftable[savefd] = 0; + shp->sftable[savefd] = 0; sh_close(savefd); } continue; } origfd = filemap[fd].orig_fd; + if(filemap[fd].tname) + io_usename(filemap[fd].tname,(int*)0,shp->exitval?2:1); sh_close(origfd); if ((savefd = filemap[fd].save_fd) >= 0) { sh_fcntl(savefd, F_DUPFD, origfd); if(savefd==job.fd) job.fd=origfd; - sh.fdstatus[origfd] = sh.fdstatus[savefd]; + shp->fdstatus[origfd] = shp->fdstatus[savefd]; /* turn off close-on-exec if flag if necessary */ - if(sh.fdstatus[origfd]&IOCLEX) + if(shp->fdstatus[origfd]&IOCLEX) fcntl(origfd,F_SETFD,FD_CLOEXEC); if(origfd<=2) { - sfswap(sh.sftable[savefd],sh.sftable[origfd]); + sfswap(shp->sftable[savefd],shp->sftable[origfd]); if(origfd==0) - sh.st.ioset = 0; + shp->st.ioset = 0; } else - sh.sftable[origfd] = sh.sftable[savefd]; - sh.sftable[savefd] = 0; + shp->sftable[origfd] = shp->sftable[savefd]; + shp->sftable[savefd] = 0; sh_close(savefd); } else - sh.fdstatus[origfd] = IOCLOSE; + shp->fdstatus[origfd] = IOCLOSE; } if(!flag) { /* keep file descriptors for subshell restore */ - for (fd = last ; fd < sh.topfd; fd++) + for (fd = last ; fd < shp->topfd; fd++) { if(filemap[fd].subshell) filemap[last++] = filemap[fd]; } } - if(last < sh.topfd) - sh.topfd = last; + if(last < shp->topfd) + shp->topfd = last; } /* @@ -1402,10 +1538,11 @@ void sh_iorestore(int last, int jmpval) */ int sh_ioaccess(int fd,register int mode) { + Shell_t *shp = &sh; register int flags; if(mode==X_OK) return(-1); - if((flags=sh_iocheckfd(fd))!=IOCLOSE) + if((flags=sh_iocheckfd(shp,fd))!=IOCLOSE) { if(mode==F_OK) return(0); @@ -1455,7 +1592,12 @@ static int slowexcept(register Sfio_t *iop,int type,void *data,Sfdisc_t *handle) if(errno!=EINTR) return(0); n=1; + sh_onstate(SH_TTYWAIT); } + else + n = 0; + if(sh.bltinfun && sh.bltindata.sigset) + return(-1); errno = 0; if(sh.trapnote&SH_SIGSET) { @@ -1495,6 +1637,8 @@ static ssize_t piperead(Sfio_t *iop,void *buff,register size_t size,Sfdisc_t *ha { int fd = sffileno(iop); NOT_USED(handle); + if(job.waitsafe && job.savesig) + job_reap(job.savesig); if(sh.trapnote) { errno = EINTR; @@ -1502,10 +1646,12 @@ static ssize_t piperead(Sfio_t *iop,void *buff,register size_t size,Sfdisc_t *ha } if(sh_isstate(SH_INTERACTIVE) && io_prompt(iop,sh.nextprompt)<0 && errno==EIO) return(0); + sh_onstate(SH_TTYWAIT); if(!(sh.fdstatus[sffileno(iop)]&IOCLEX) && (sfset(iop,0,0)&SF_SHARE)) size = ed_read(sh.ed_context, fd, (char*)buff, size,0); else size = sfrd(iop,buff,size,handle); + sh_offstate(SH_TTYWAIT); return(size); } /* @@ -1594,7 +1740,7 @@ static ssize_t slowread(Sfio_t *iop,void *buff,register size_t size,Sfdisc_t *ha * check and return the attributes for a file descriptor */ -int sh_iocheckfd(register int fd) +int sh_iocheckfd(Shell_t *shp, register int fd) { register int flags, n; if((n=sh.fdstatus[fd])&IOCLOSE) @@ -1661,6 +1807,7 @@ int sh_iocheckfd(register int fd) static int io_prompt(Sfio_t *iop,register int flag) { + Shell_t *shp = &sh; register char *cp; char buff[1]; char *endprompt; @@ -1691,7 +1838,7 @@ static int io_prompt(Sfio_t *iop,register int flag) ioctl(sffileno(sfstderr),TIOCLBIC,&mode); } #endif /* TIOCLBIC */ - cp = sh_mactry(nv_getval(nv_scoped(PS1NOD))); + cp = sh_mactry(shp,nv_getval(sh_scoped(shp,PS1NOD))); for(;c= *cp;cp++) { if(c==HIST_CHAR) @@ -1711,10 +1858,10 @@ static int io_prompt(Sfio_t *iop,register int flag) goto done; } case 2: - cp = nv_getval(nv_scoped(PS2NOD)); + cp = nv_getval(sh_scoped(shp,PS2NOD)); break; case 3: - cp = nv_getval(nv_scoped(PS3NOD)); + cp = nv_getval(sh_scoped(shp,PS3NOD)); break; default: goto done; @@ -1745,11 +1892,13 @@ static int pipeexcept(Sfio_t* iop, int mode, void *data, Sfdisc_t* handle) /* * keep track of each stream that is opened and closed */ -static void sftrack(Sfio_t* sp,int flag, int newfd) +static void sftrack(Sfio_t* sp, int flag, void* data) { + Shell_t *shp = &sh; register int fd = sffileno(sp); register struct checkpt *pp; register int mode; + int newfd = integralof(data); if(flag==SF_SETFD || flag==SF_CLOSING) { if(newfd<0) @@ -1772,12 +1921,12 @@ static void sftrack(Sfio_t* sp,int flag, int newfd) return; } #endif - if((unsigned)fd >= sh.lim.open_max) + if((unsigned)fd >= shp->lim.open_max) return; if(sh_isstate(SH_NOTRACK)) return; mode = sfset(sp,0,0); - if(sp==sh.heredocs && fd < 10 && flag==SF_NEW) + if(sp==shp->heredocs && fd < 10 && flag==SF_NEW) { fd = sfsetfd(sp,10); fcntl(fd,F_SETFD,FD_CLOEXEC); @@ -1786,23 +1935,23 @@ static void sftrack(Sfio_t* sp,int flag, int newfd) return; if(flag==SF_NEW) { - if(!sh.sftable[fd] && sh.fdstatus[fd]==IOCLOSE) + if(!shp->sftable[fd] && shp->fdstatus[fd]==IOCLOSE) { - sh.sftable[fd] = sp; + shp->sftable[fd] = sp; flag = (mode&SF_WRITE)?IOWRITE:0; if(mode&SF_READ) flag |= IOREAD; - sh.fdstatus[fd] = flag; + shp->fdstatus[fd] = flag; #if 0 if(flag==IOWRITE) - sfpool(sp,sh.outpool,SF_WRITE); + sfpool(sp,shp->outpool,SF_WRITE); else #else if(flag!=IOWRITE) #endif - sh_iostream(fd); + sh_iostream(shp,fd); } - if((pp=(struct checkpt*)sh.jmplist) && pp->mode==SH_JMPCMD) + if((pp=(struct checkpt*)shp->jmplist) && pp->mode==SH_JMPCMD) { struct openlist *item; /* @@ -1820,9 +1969,9 @@ static void sftrack(Sfio_t* sp,int flag, int newfd) } else if(flag==SF_CLOSING || (flag==SF_SETFD && newfd<=2)) { - sh.sftable[fd] = 0; - sh.fdstatus[fd]=IOCLOSE; - if(pp=(struct checkpt*)sh.jmplist) + shp->sftable[fd] = 0; + shp->fdstatus[fd]=IOCLOSE; + if(pp=(struct checkpt*)shp->jmplist) { struct openlist *item; for(item=pp->olist; item; item=item->next) @@ -1916,7 +2065,7 @@ static int eval_exceptf(Sfio_t *iop,int type, void *data, Sfdisc_t *handle) * The stream can be read with the normal stream operations */ -static Sfio_t *subopen(Sfio_t* sp, off_t offset, long size) +static Sfio_t *subopen(Shell_t *shp,Sfio_t* sp, off_t offset, long size) { register struct subfile *disp; if(sfseek(sp,offset,SEEK_SET) <0) @@ -1927,7 +2076,7 @@ static Sfio_t *subopen(Sfio_t* sp, off_t offset, long size) disp->oldsp = sp; disp->offset = offset; disp->size = disp->left = size; - sp = sfnew(NIL(Sfio_t*),(char*)(disp+1),IOBSIZE,sh.lim.open_max,SF_READ); + sp = sfnew(NIL(Sfio_t*),(char*)(disp+1),IOBSIZE,shp->lim.open_max,SF_READ); sfdisc(sp,&disp->disc); return(sp); } @@ -1982,11 +2131,12 @@ static int subexcept(Sfio_t* sp,register int mode, void *data, Sfdisc_t* handle) */ void sh_menu(Sfio_t *outfile,int argn,char *argv[]) { + Shell_t *shp = &sh; register int i,j; register char **arg; int nrow, ncol=1, ndigits=1; int fldsize, wsize = ed_window(); - char *cp = nv_getval(nv_scoped(LINES)); + char *cp = nv_getval(sh_scoped(shp,LINES)); nrow = (cp?1+2*((int)strtol(cp, (char**)0, 10)/3):NROW); for(i=argn;i >= 10;i /= 10) ndigits++; @@ -2132,6 +2282,7 @@ mode_t sh_umask(mode_t m) Sfio_t *sh_iogetiop(int fd, int mode) { + Shell_t *shp = &sh; int n; Sfio_t *iop=0; if(mode!=SF_READ && mode!=SF_WRITE) @@ -2142,18 +2293,18 @@ Sfio_t *sh_iogetiop(int fd, int mode) switch(fd) { case SH_IOHISTFILE: - if(!sh_histinit()) + if(!sh_histinit((void*)shp)) return(iop); - fd = sffileno(sh.hist_ptr->histfp); + fd = sffileno(shp->hist_ptr->histfp); break; case SH_IOCOPROCESS: if(mode==SF_WRITE) - fd = sh.coutpipe; + fd = shp->coutpipe; else - fd = sh.cpipe[0]; + fd = shp->cpipe[0]; break; default: - if(fd<0 || fd >= sh.lim.open_max) + if(fd<0 || fd >= shp->lim.open_max) fd = -1; } if(fd<0) @@ -2161,14 +2312,14 @@ Sfio_t *sh_iogetiop(int fd, int mode) errno = EBADF; return(iop); } - if(!(n=sh.fdstatus[fd])) - n = sh_iocheckfd(fd); + if(!(n=shp->fdstatus[fd])) + n = sh_iocheckfd(shp,fd); if(mode==SF_WRITE && !(n&IOWRITE)) return(iop); if(mode==SF_READ && !(n&IOREAD)) return(iop); - if(!(iop = sh.sftable[fd])) - iop=sh_iostream(fd); + if(!(iop = shp->sftable[fd])) + iop=sh_iostream(shp,fd); return(iop); } @@ -2184,9 +2335,10 @@ Notify_f sh_fdnotify(Notify_f notify) Sfio_t *sh_fd2sfio(int fd) { + Shell_t *shp = &sh; register int status; Sfio_t *sp = sh.sftable[fd]; - if(!sp && (status = sh_iocheckfd(fd))!=IOCLOSE) + if(!sp && (status = sh_iocheckfd(shp,fd))!=IOCLOSE) { register int flags=0; if(status&IOREAD) @@ -2201,6 +2353,7 @@ Sfio_t *sh_fd2sfio(int fd) Sfio_t *sh_pathopen(const char *cp) { + Shell_t *shp = &sh; int n; #ifdef PATH_BFPATH if((n=path_open(cp,path_get(cp))) < 0) @@ -2211,5 +2364,5 @@ Sfio_t *sh_pathopen(const char *cp) #endif if(n < 0) errormsg(SH_DICT,ERROR_system(1),e_open,cp); - return(sh_iostream(n)); + return(sh_iostream(shp,n)); } diff --git a/usr/src/lib/libshell/common/sh/jobs.c b/usr/src/lib/libshell/common/sh/jobs.c index d397810f3d..0c9724ce0f 100644 --- a/usr/src/lib/libshell/common/sh/jobs.c +++ b/usr/src/lib/libshell/common/sh/jobs.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -63,6 +63,7 @@ struct jobsave static struct jobsave *job_savelist; static int njob_savelist; +static struct process *pwfg; static void init_savelist(void) { @@ -204,11 +205,17 @@ int job_reap(register int sig) register struct process *pw; struct process *px; register int flags; - struct process dummy; struct jobsave *jp; + struct back_save *bp; int nochild=0, oerrno, wstat; Waitevent_f waitevent = sh.waitevent; static int wcontinued = WCONTINUED; + if (vmbusy()) + { + errormsg(SH_DICT,ERROR_warn(0),"vmbusy() inside job_reap() -- should not happen"); + if (getenv("_AST_KSH_VMBUSY_ABORT")) + abort(); + } #ifdef DEBUG if(sfprintf(sfstderr,"ksh: job line %4d: reap pid=%d critical=%d signal=%d\n",__LINE__,getpid(),job.in_critical,sig) <=0) write(2,"waitsafe\n",9); @@ -223,12 +230,14 @@ int job_reap(register int sig) oerrno = errno; while(1) { - if(!(flags&WNOHANG) && !sh.intrap && waitevent && job.pwlist) + if(!(flags&WNOHANG) && !sh.intrap && job.pwlist) { - if((*waitevent)(-1,-1L,0)) + sh_onstate(SH_TTYWAIT); + if(waitevent && (*waitevent)(-1,-1L,0)) flags |= WNOHANG; } pid = waitpid((pid_t)-1,&wstat,flags); + sh_offstate(SH_TTYWAIT); /* * some systems (linux 2.6) may return EINVAL @@ -245,17 +254,21 @@ int job_reap(register int sig) flags |= WNOHANG; job.waitsafe++; jp = 0; + lastpid = pid; if(!(pw=job_bypid(pid))) { #ifdef DEBUG sfprintf(sfstderr,"ksh: job line %4d: reap pid=%d critical=%d unknown job pid=%d pw=%x\n",__LINE__,getpid(),job.in_critical,pid,pw); #endif /* DEBUG */ + if (WIFCONTINUED(wstat) && wcontinued) + continue; pw = &dummy; pw->p_exit = 0; pw->p_pgrp = 0; + pw->p_exitmin = 0; if(job.toclear) job_clear(); - if(bck.count++ > sh.lim.child_max) + if(++bck.count > sh.lim.child_max) job_chksave(0); if(jp = jobsave_create(pid)) { @@ -284,17 +297,14 @@ int job_reap(register int sig) px->p_nxtjob = job.pwlist; job.pwlist = px; } - pw->p_exit = WSTOPSIG(wstat); pw->p_flag |= (P_NOTIFY|P_SIGNALLED|P_STOPPED); + pw->p_exit = WSTOPSIG(wstat); if(pw->p_pgrp && pw->p_pgrp==job.curpgid && sh_isstate(SH_STOPOK)) sh_fault(pw->p_exit); continue; } else if (WIFCONTINUED(wstat) && wcontinued) - { pw->p_flag &= ~(P_NOTIFY|P_SIGNALLED|P_STOPPED); - pw->p_exit = 0; - } else #endif /* SIGTSTP */ { @@ -306,9 +316,12 @@ int job_reap(register int sig) sh.cpipe[1] = -1; sh.coutpipe = -1; } + else if(sh.subshell) + sh_subjobcheck(pid); + + pw->p_flag &= ~(P_STOPPED|P_SIGNALLED); if (WIFSIGNALED(wstat)) { - pw->p_flag &= ~P_STOPPED; pw->p_flag |= (P_DONE|P_NOTIFY|P_SIGNALLED); if (WTERMCORE(wstat)) pw->p_flag |= P_COREDUMP; @@ -327,7 +340,8 @@ int job_reap(register int sig) else { pw->p_flag |= (P_DONE|P_NOTIFY); - if(WEXITSTATUS(wstat) > pw->p_exit) + pw->p_exit = pw->p_exitmin; + if(WEXITSTATUS(wstat) > pw->p_exitmin) pw->p_exit = WEXITSTATUS(wstat); } if(pw->p_pgrp==0) @@ -346,6 +360,18 @@ int job_reap(register int sig) /* only top-level process in job should have notify set */ if(px && pw != px) pw->p_flag &= ~P_NOTIFY; + if(pid==pw->p_fgrp && pid==tcgetpgrp(JOBTTY)) + { + px = job_byjid((int)pw->p_job); + for(; px && (px->p_flag&P_DONE); px=px->p_nxtproc); + if(!px) + tcsetpgrp(JOBTTY,job.mypid); + } + if(!sh.intrap && sh.st.trapcom[SIGCHLD] && pid>0 && (pwfg!=job_bypid(pid))) + { + sh.sigflag[SIGCHLD] |= SH_SIGTRAP; + sh.trapnote |= SH_SIGTRAP; + } } if(errno==ECHILD) { @@ -353,11 +379,6 @@ int job_reap(register int sig) nochild = 1; } sh.waitevent = waitevent; - if(!sh.intrap && sh.st.trapcom[SIGCHLD]) - { - sh.sigflag[SIGCHLD] |= SH_SIGTRAP; - sh.trapnote |= SH_SIGTRAP; - } if(sh_isoption(SH_NOTIFY) && sh_isstate(SH_TTYWAIT)) { outfile = sfstderr; @@ -375,7 +396,7 @@ int job_reap(register int sig) */ static void job_waitsafe(int sig) { - if(job.in_critical) + if(job.in_critical || vmbusy()) { job.savesig = sig; job.waitsafe++; @@ -388,9 +409,9 @@ static void job_waitsafe(int sig) * initialize job control if possible * if lflag is set the switching driver message will not print */ -void job_init(int lflag) +void job_init(Shell_t *shp, int lflag) { - register int i,ntry=0; + register int ntry=0; job.fd = JOBTTY; signal(SIGCHLD,job_waitsafe); # if defined(SIGCLD) && (SIGCLD!=SIGCHLD) @@ -429,7 +450,7 @@ void job_init(int lflag) register int fd; register char *ttynam; #ifndef SIGTSTP - setpgid(0,sh.pid); + setpgid(0,shp->pid); #endif /*SIGTSTP */ if(job.mypgid<0 || !(ttynam=ttyname(JOBTTY))) return; @@ -437,11 +458,11 @@ void job_init(int lflag) if((fd = open(ttynam,O_RDWR)) <0) return; if(fd!=JOBTTY) - sh_iorenumber(fd,JOBTTY); - job.mypgid = sh.pid; + sh_iorenumber(shp,fd,JOBTTY); + job.mypgid = shp->pid; #ifdef SIGTSTP - tcsetpgrp(JOBTTY,sh.pid); - setpgid(0,sh.pid); + tcsetpgrp(JOBTTY,shp->pid); + setpgid(0,shp->pid); #endif /* SIGTSTP */ } #ifdef SIGTSTP @@ -454,7 +475,7 @@ void job_init(int lflag) return; /* Stop this shell until continued */ signal(SIGTTIN,SIG_DFL); - kill(sh.pid,SIGTTIN); + kill(shp->pid,SIGTTIN); /* resumes here after continue tries again */ if(ntry++ > IOMAXTRY) { @@ -495,7 +516,7 @@ void job_init(int lflag) #ifdef SIGTSTP /* make sure that we are a process group leader */ - setpgid(0,sh.pid); + setpgid(0,shp->pid); # if defined(SA_NOCLDWAIT) && defined(_lib_sigflag) sigflag(SIGCHLD, SA_NOCLDSTOP|SA_NOCLDWAIT, 0); # endif /* SA_NOCLDWAIT */ @@ -503,7 +524,7 @@ void job_init(int lflag) signal(SIGTTOU,SIG_IGN); /* The shell now handles ^Z */ signal(SIGTSTP,sh_fault); - tcsetpgrp(JOBTTY,sh.pid); + tcsetpgrp(JOBTTY,shp->pid); # ifdef CNSUSP /* set the switch character */ tty_get(JOBTTY,&my_stty); @@ -516,7 +537,7 @@ void job_init(int lflag) # endif /* CNSUSP */ sh_onoption(SH_MONITOR); job.jobcontrol++; - job.mypid = sh.pid; + job.mypid = shp->pid; #endif /* SIGTSTP */ return; } @@ -526,7 +547,7 @@ void job_init(int lflag) * see if there are any stopped jobs * restore tty driver and pgrp */ -int job_close(void) +int job_close(Shell_t* shp) { register struct process *pw; register int count = 0, running = 0; @@ -558,7 +579,7 @@ int job_close(void) errormsg(SH_DICT,0,e_terminate); return(-1); } - else if(running && sh.login_sh) + else if(running && shp->login_sh) { errormsg(SH_DICT,0,e_jobsrunning); return(-1); @@ -625,7 +646,7 @@ static void job_reset(register struct process *pw) /* save the terminal state for current job */ #ifdef SIGTSTP job_fgrp(pw,tcgetpgrp(job.fd)); - if(tcsetpgrp(job.fd,sh.pid) !=0) + if(tcsetpgrp(job.fd,job.mypid) !=0) return; #endif /* SIGTSTP */ /* force the following tty_get() to do a tcgetattr() unless fg */ @@ -1043,6 +1064,7 @@ int job_post(pid_t pid, pid_t join) { register struct process *pw; register History_t *hp = sh.hist_ptr; + int val; sh.jobenv = sh.curenv; if(njob_savelist < NJOB_SAVELIST) init_savelist(); @@ -1088,8 +1110,8 @@ int job_post(pid_t pid, pid_t join) pw->p_env = sh.curenv; pw->p_pid = pid; pw->p_flag = P_EXITSAVE; - pw->p_exit = sh.xargexit; - sh.xargexit = 0; + pw->p_exitmin = sh.xargexit; + pw->p_exit = 0; if(sh_isstate(SH_MONITOR)) { if(killpg(job.curpgid,0)<0 && errno==ESRCH) @@ -1110,10 +1132,9 @@ int job_post(pid_t pid, pid_t join) else pw->p_name = -1; #endif /* JOBS */ - if(pid==lastpid) + if ((val = job_chksave(pid)) >= 0) { - int val = job_chksave(pid); - pw->p_exit = val>0?val:0; + pw->p_exit = val; if(pw->p_exit==SH_STOPSIG) { pw->p_flag |= (P_SIGNALLED|P_STOPPED); @@ -1187,11 +1208,11 @@ static void job_prmsg(register struct process *pw) * pid=-1 to wait for all runing processes */ -void job_wait(register pid_t pid) +int job_wait(register pid_t pid) { register struct process *pw=0,*px; register int jobid = 0; - int nochild; + int nochild = 1; char intr = 0; if(pid <= 0) { @@ -1203,6 +1224,8 @@ void job_wait(register pid_t pid) job_lock(); if(pid > 1) { + if(pid==sh.spid) + sh.spid = 0; if(!(pw=job_bypid(pid))) { /* check to see whether job status has been saved */ @@ -1210,13 +1233,13 @@ void job_wait(register pid_t pid) sh.exitval = ERROR_NOENT; exitset(); job_unlock(); - return; + return(nochild); } else if(intr && pw->p_env!=sh.curenv) { sh.exitval = ERROR_NOENT; job_unlock(); - return; + return(nochild); } jobid = pw->p_job; if(!intr) @@ -1224,12 +1247,19 @@ void job_wait(register pid_t pid) if(pw->p_pgrp && job.parent!= (pid_t)-1) job_set(job_byjid(jobid)); } + pwfg = pw; #ifdef DEBUG sfprintf(sfstderr,"ksh: job line %4d: wait pid=%d critical=%d job=%d pid=%d\n",__LINE__,getpid(),job.in_critical,jobid,pid); if(pw) sfprintf(sfstderr,"ksh: job line %4d: wait pid=%d critical=%d flags=%o\n",__LINE__,getpid(),job.in_critical,pw->p_flag); #endif /* DEBUG*/ errno = 0; + if(sh.coutpipe>=0 && sh.cpid==lastpid) + { + sh_close(sh.coutpipe); + sh_close(sh.cpipe[1]); + sh.cpipe[1] = sh.coutpipe = -1; + } while(1) { if(job.waitsafe) @@ -1328,9 +1358,10 @@ void job_wait(register pid_t pid) if((intr && sh.trapnote) || (pid==1 && !intr)) break; } + pwfg = 0; job_unlock(); if(pid==1) - return; + return(nochild); exitset(); if(pw->p_pgrp) { @@ -1347,10 +1378,18 @@ void job_wait(register pid_t pid) #endif /* SIGTSTP */ } else + { + if(pw->p_pid == tcgetpgrp(JOBTTY)) + { + if(pw->p_pgrp==0) + pw->p_pgrp = pw->p_pid; + job_reset(pw); + } tty_set(-1, 0, NIL(struct termios*)); + } done: if(!job.waitall && sh_isoption(SH_PIPEFAIL)) - return; + return(nochild); if(!sh.intrap) { job_lock(); @@ -1361,6 +1400,7 @@ done: } job_unlock(); } + return(nochild); } /* @@ -1483,7 +1523,7 @@ static struct process *job_unpost(register struct process *pwtop,int notify) for(pw=pwtop; pw; pw=pw->p_nxtproc) { /* save the exit status for background jobs */ - if(pw->p_flag&P_EXITSAVE) + if((pw->p_flag&P_EXITSAVE) || pw->p_pid==sh.spid) { struct jobsave *jp; /* save status for future wait */ @@ -1593,10 +1633,13 @@ static char *job_sigmsg(int sig) if(sig<sh.sigmax && sh.sigmsg[sig]) return(sh.sigmsg[sig]); #if defined(SIGRTMIN) && defined(SIGRTMAX) - if(sig>=SIGRTMIN && sig<=SIGRTMAX) + if(sig>=sh.sigruntime[SH_SIGRTMIN] && sig<=sh.sigruntime[SH_SIGRTMAX]) { static char sigrt[20]; - sfsprintf(sigrt,sizeof(sigrt),"SIGRTMIN+%d",sig-SIGRTMIN); + if(sig>sh.sigruntime[SH_SIGRTMIN]+(sh.sigruntime[SH_SIGRTMAX]-sig<=sh.sigruntime[SH_SIGRTMIN])/2) + sfsprintf(sigrt,sizeof(sigrt),"SIGRTMAX-%d",sh.sigruntime[SH_SIGRTMAX]-sig); + else + sfsprintf(sigrt,sizeof(sigrt),"SIGRTMIN+%d",sig-sh.sigruntime[SH_SIGRTMIN]); return(sigrt); } #endif @@ -1657,10 +1700,22 @@ void *job_subsave(void) void job_subrestore(void* ptr) { - register struct jobsave *jp,*jpnext; + register struct jobsave *jp; register struct back_save *bp = (struct back_save*)ptr; register struct process *pw, *px, *pwnext; + struct jobsave *jpnext; job_lock(); + for(jp=bck.list; jp; jp=jpnext) + { + jpnext = jp->next; + if(jp->pid==sh.spid) + { + jp->next = bp->list; + bp->list = jp; + } + else + job_chksave(jp->pid); + } for(pw=job.pwlist; pw; pw=pwnext) { pwnext = pw->p_nxtjob; @@ -1670,12 +1725,13 @@ void job_subrestore(void* ptr) px->p_flag |= P_DONE; job_unpost(pw,0); } - for(jp=bck.list,bck= *bp; jp; jp=jpnext) - { - jpnext = jp->next; - free((void*)jp); - } - free(ptr); + + /* + * queue up old lists for disposal by job_reap() + */ + + bck = *bp; + free((void*)bp); job_unlock(); } diff --git a/usr/src/lib/libshell/common/sh/lex.c b/usr/src/lib/libshell/common/sh/lex.c index c8502e3b7b..9753ea8074 100644 --- a/usr/src/lib/libshell/common/sh/lex.c +++ b/usr/src/lib/libshell/common/sh/lex.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -79,7 +79,6 @@ struct lexstate char reservok; /* >0 for reserved word legal */ char skipword; /* next word can't be reserved */ char last_quote; /* last multi-line quote character */ - char comp_assign; /* inside compound assignment */ }; struct lexdata @@ -107,24 +106,19 @@ struct lexdata }; #define _SHLEX_PRIVATE \ - struct lexdata _lexd; \ - struct lexstate _lex; + struct lexdata lexd; \ + struct lexstate lex; #include "shlex.h" -#define lexd lp->_lexd -#define lex lp->_lex -#undef shlex -#define shlex lp->_shlex - -#define pushlevel(c,s) ((lexd.level>=lexd.lex_max?stack_grow(lp):1) &&\ - ((lexd.lex_match[lexd.level++]=lexd.lastc),\ - lexd.lastc=(((s)<<CHAR_BIT)|(c)))) -#define oldmode() (lexd.lastc>>CHAR_BIT) -#define endchar() (lexd.lastc&0xff) -#define setchar(c) (lexd.lastc = ((lexd.lastc&~0xff)|(c))) -#define poplevel() (lexd.lastc=lexd.lex_match[--lexd.level]) +#define pushlevel(lp,c,s) ((lp->lexd.level>=lp->lexd.lex_max?stack_grow(lp):1) &&\ + ((lp->lexd.lex_match[lp->lexd.level++]=lp->lexd.lastc),\ + lp->lexd.lastc=(((s)<<CHAR_BIT)|(c)))) +#define oldmode(lp) (lp->lexd.lastc>>CHAR_BIT) +#define endchar(lp) (lp->lexd.lastc&0xff) +#define setchar(lp,c) (lp->lexd.lastc = ((lp->lexd.lastc&~0xff)|(c))) +#define poplevel(lp) (lp->lexd.lastc=lp->lexd.lex_match[--lp->lexd.level]) static char *fmttoken(Lex_t*, int, char*); #ifdef SF_BUFCONST @@ -133,7 +127,7 @@ static char *fmttoken(Lex_t*, int, char*); static int alias_exceptf(Sfio_t*, int, Sfdisc_t*); #endif static void setupalias(Lex_t*,const char*, Namval_t*); -static int comsub(Lex_t*); +static int comsub(Lex_t*,int); static void nested_here(Lex_t*); static int here_copy(Lex_t*, struct ionod*); static int stack_grow(Lex_t*); @@ -141,41 +135,41 @@ static const Sfdisc_t alias_disc = { NULL, NULL, NULL, alias_exceptf, NULL }; #if SHOPT_KIA -static void refvar(int type) +static void refvar(Lex_t *lp, int type) { - register Shell_t *shp = sh_getinterp(); - register Lex_t *lp = (Lex_t*)shp->lex_context; - off_t off = (fcseek(0)-(type+1))-(lexd.first?lexd.first:fcfirst()); + register Shell_t *shp = lp->sh; + register Stk_t *stkp = shp->stk; + off_t off = (fcseek(0)-(type+1))-(lp->lexd.first?lp->lexd.first:fcfirst()); unsigned long r; - if(lexd.first) + if(lp->lexd.first) { - off = (fcseek(0)-(type+1)) - lexd.first; - r=kiaentity(lexd.first+lexd.kiaoff+type,off-lexd.kiaoff,'v',-1,-1,shlex.current,'v',0,""); + off = (fcseek(0)-(type+1)) - lp->lexd.first; + r=kiaentity(lp,lp->lexd.first+lp->lexd.kiaoff+type,off-lp->lexd.kiaoff,'v',-1,-1,lp->current,'v',0,""); } else { - int n,offset = staktell(); + int n,offset = stktell(stkp); char *savptr,*begin; off = offset + (fcseek(0)-(type+1)) - fcfirst(); - if(lexd.kiaoff < offset) + if(lp->lexd.kiaoff < offset) { /* variable starts on stak, copy remainder */ if(off>offset) - stakwrite(fcfirst()+type,off-offset); - n = staktell()-lexd.kiaoff; - begin = stakptr(lexd.kiaoff); + sfwrite(stkp,fcfirst()+type,off-offset); + n = stktell(stkp)-lp->lexd.kiaoff; + begin = stkptr(stkp,lp->lexd.kiaoff); } else { /* variable in data buffer */ - begin = fcfirst()+(type+lexd.kiaoff-offset); - n = off-lexd.kiaoff; + begin = fcfirst()+(type+lp->lexd.kiaoff-offset); + n = off-lp->lexd.kiaoff; } - savptr = stakfreeze(0); - r=kiaentity(begin,n,'v',-1,-1,shlex.current,'v',0,""); - stakset(savptr,offset); + savptr = stkfreeze(stkp,0); + r=kiaentity(lp,begin,n,'v',-1,-1,lp->current,'v',0,""); + stkset(stkp,savptr,offset); } - sfprintf(shlex.kiatmp,"p;%..64d;v;%..64d;%d;%d;r;\n",shlex.current,r,shp->inlineno,shp->inlineno); + sfprintf(lp->kiatmp,"p;%..64d;v;%..64d;%d;%d;r;\n",lp->current,r,shp->inlineno,shp->inlineno); } #endif /* SHOPT_KIA */ @@ -183,11 +177,12 @@ static void refvar(int type) * This routine gets called when reading across a buffer boundary * If lexd.nocopy is off, then current token is saved on the stack */ -static void lex_advance(Sfio_t *iop, const char *buff, register int size) +static void lex_advance(Sfio_t *iop, const char *buff, register int size, void *context) { - register Shell_t *shp = sh_getinterp(); - register Lex_t *lp = (Lex_t*)shp->lex_context; - register Sfio_t *log= shp->funlog; + register Lex_t *lp = (Lex_t*)context; + register Shell_t *shp = lp->sh; + register Sfio_t *log= shp->funlog; + Stk_t *stkp = shp->stk; #if KSHELL /* write to history file and to stderr if necessary */ if(iop && !sfstacked(iop)) @@ -199,22 +194,22 @@ static void lex_advance(Sfio_t *iop, const char *buff, register int size) sfwrite(sfstderr, buff, size); } #endif - if(lexd.nocopy) + if(lp->lexd.nocopy) return; - if(lexd.first) + if(lp->lexd.first) { - size -= (lexd.first-(char*)buff); - buff = lexd.first; - if(!lexd.noarg) - shlex.arg = (struct argnod*)stakseek(ARGVAL); + size -= (lp->lexd.first-(char*)buff); + buff = lp->lexd.first; + if(!lp->lexd.noarg) + lp->arg = (struct argnod*)stkseek(stkp,ARGVAL); #if SHOPT_KIA - lexd.kiaoff += ARGVAL; + lp->lexd.kiaoff += ARGVAL; #endif /* SHOPT_KIA */ } - if(size>0 && (shlex.arg||lexd.noarg)) + if(size>0 && (lp->arg||lp->lexd.noarg)) { - stakwrite(buff,size); - lexd.first = 0; + sfwrite(stkp,buff,size); + lp->lexd.first = 0; } } @@ -222,31 +217,26 @@ static void lex_advance(Sfio_t *iop, const char *buff, register int size) * fill up another input buffer * preserves lexical state */ -static int lexfill(void) +static int lexfill(Lex_t *lp) { - Shell_t *shp = sh_getinterp(); register int c; - register Lex_t *lp = (Lex_t*)shp->lex_context; - struct shlex_t savelex; - struct lexdata savedata; - struct lexstate savestate; + Lex_t savelex; struct argnod *ap; int aok; - savelex = shlex; - savedata = lexd; - savestate = lex; - ap = shlex.arg; + savelex = *lp; + ap = lp->arg; c = fcfill(); if(ap) - shlex.arg = ap; - lex = savestate; - lexd = savedata; - lexd.first = 0; - aok= shlex.aliasok; - ap = shlex.arg; - shlex = savelex; - shlex.arg = ap; - shlex.aliasok = aok; + lp->arg = ap; + lp->lex = savelex.lex; + lp->lexd = savelex.lexd; + if(fcfile() || c) + lp->lexd.first = 0; + aok= lp->aliasok; + ap = lp->arg; + memcpy(lp, &savelex, offsetof(Lex_t,lexd)); + lp->arg = ap; + lp->aliasok = aok; return(c); } @@ -255,38 +245,37 @@ static int lexfill(void) */ Lex_t *sh_lexopen(Lex_t *lp, Shell_t *sp, int mode) { - fcnotify(lex_advance); if(!lp) { lp = (Lex_t*)newof(0,Lex_t,1,0); - lp->_shlex.sh = sp; + lp->sh = sp; } - lex.intest = lex.incase = lex.skipword = lexd.warn = 0; - lex.comp_assign = 0; - lex.reservok = 1; + fcnotify(lex_advance,lp); + lp->lex.intest = lp->lex.incase = lp->lex.skipword = lp->lexd.warn = 0; + lp->comp_assign = 0; + lp->lex.reservok = 1; if(!sh_isoption(SH_DICTIONARY) && sh_isoption(SH_NOEXEC)) - lexd.warn=1; + lp->lexd.warn=1; if(!mode) { - lexd.noarg = lexd.level= lexd.dolparen = 0; - lexd.nocopy = lexd.docword = lexd.nest = lexd.paren = 0; + lp->lexd.noarg = lp->lexd.level= lp->lexd.dolparen = lp->lexd.balance = 0; + lp->lexd.nocopy = lp->lexd.docword = lp->lexd.nest = lp->lexd.paren = 0; } - shlex.comsub = 0; + lp->comsub = 0; return(lp); } #ifdef DBUG -extern int lextoken(void); -int sh_lex(void) +extern int lextoken(Lex_t*); +int sh_lex(Lex_t *lp) { - Shell_t *shp = sh_getinterp(); - register Lex_t *lp = (Lex_t*)shp->lex_context; + Shell_t *shp = lp->sh; register int flag; char *quoted, *macro, *split, *expand; char tokstr[3]; register int tok = lextoken(); quoted = macro = split = expand = ""; - if(tok==0 && (flag=shlex.arg->argflag)) + if(tok==0 && (flag=lp->arg->argflag)) { if(flag&ARG_MAC) macro = "macro:"; @@ -304,62 +293,62 @@ int sh_lex(void) /* * Get the next word and put it on the top of the stak - * A pointer to the current word is stored in shlex.arg + * A pointer to the current word is stored in lp->arg * Returns the token type */ -int sh_lex(void) +int sh_lex(Lex_t* lp) { - register Shell_t *shp = sh_getinterp(); + register Shell_t *shp = lp->sh; register const char *state; - register int n, c, mode=ST_BEGIN, wordflags=0; - register Lex_t *lp = (Lex_t*)shp->lex_context; - int inlevel=lexd.level, assignment=0, ingrave=0; + register int n, c, mode=ST_BEGIN, wordflags=0; + Stk_t *stkp = shp->stk; + int inlevel=lp->lexd.level, assignment=0, ingrave=0; Sfio_t *sp; #if SHOPT_MULTIBYTE LEN=1; #endif /* SHOPT_MULTIBYTE */ - if(lexd.paren) + if(lp->lexd.paren) { - lexd.paren = 0; - return(shlex.token=LPAREN); + lp->lexd.paren = 0; + return(lp->token=LPAREN); } - if(lex.incase) - shlex.assignok = 0; + if(lp->lex.incase) + lp->assignok = 0; else - shlex.assignok |= lex.reservok; - if(lex.comp_assign==2) - lex.comp_assign = lex.reservok = 0; - lexd.arith = (lexd.nest==1); - if(lexd.nest) + lp->assignok |= lp->lex.reservok; + if(lp->comp_assign==2) + lp->comp_assign = lp->lex.reservok = 0; + lp->lexd.arith = (lp->lexd.nest==1); + if(lp->lexd.nest) { - pushlevel(lexd.nest,ST_NONE); - lexd.nest = 0; - mode = lexd.lex_state; + pushlevel(lp,lp->lexd.nest,ST_NONE); + lp->lexd.nest = 0; + mode = lp->lexd.lex_state; } - else if(lexd.docword) + else if(lp->lexd.docword) { if(fcgetc(c)=='-' || c=='#') { - lexd.docword++; - shlex.digits=(c=='#'?3:1); + lp->lexd.docword++; + lp->digits=(c=='#'?3:1); } else if(c=='<') { - shlex.digits=2; - lexd.docword=0; + lp->digits=2; + lp->lexd.docword=0; } else if(c>0) fcseek(-1); } - if(!lexd.dolparen) + if(!lp->lexd.dolparen) { - shlex.arg = 0; + lp->arg = 0; if(mode!=ST_BEGIN) - lexd.first = fcseek(0); + lp->lexd.first = fcseek(0); else - lexd.first = 0; + lp->lexd.first = 0; } - shlex.lastline = sh.inlineno; + lp->lastline = lp->sh->inlineno; while(1) { /* skip over characters in the current state */ @@ -372,7 +361,7 @@ int sh_lex(void) goto breakloop; case S_EOF: sp = fcfile(); - if((n=lexfill()) > 0) + if((n=lexfill(lp)) > 0) { fcseek(-1); continue; @@ -389,16 +378,16 @@ int sh_lex(void) } else { - shlex.token = -1; - sh_syntax(); + lp->token = -1; + sh_syntax(lp); } } /* end-of-file */ if(mode==ST_BEGIN) - return(shlex.token=EOFSYM); - if(mode >ST_NORM && lexd.level>0) + return(lp->token=EOFSYM); + if(mode >ST_NORM && lp->lexd.level>0) { - switch(c=endchar()) + switch(c=endchar(lp)) { case '$': if(mode==ST_LIT) @@ -406,8 +395,8 @@ int sh_lex(void) c = '\''; break; } - mode = oldmode(); - poplevel(); + mode = oldmode(lp); + poplevel(lp); continue; case RBRACT: c = LBRACT; @@ -420,27 +409,27 @@ int sh_lex(void) c = LBRACE; break; case '"': case '`': case '\'': - lexd.balance = c; + lp->lexd.balance = c; break; } if(sp && !(sfset(sp,0,0)&SF_STRING)) { - shlex.lasttok = c; - shlex.token = EOFSYM; - sh_syntax(); + lp->lasttok = c; + lp->token = EOFSYM; + sh_syntax(lp); } - lexd.balance = c; + lp->lexd.balance = c; } goto breakloop; case S_COM: /* skip one or more comment line(s) */ - lex.reservok = !lex.intest; - if((n=lexd.nocopy) && lexd.dolparen) - lexd.nocopy--; + lp->lex.reservok = !lp->lex.intest; + if((n=lp->lexd.nocopy) && lp->lexd.dolparen) + lp->lexd.nocopy--; do { while(fcgetc(c)>0 && c!='\n'); - if(c<=0 || shlex.heredoc) + if(c<=0 || lp->heredoc) break; while(shp->inlineno++,fcpeek(0)=='\n') fcseek(1); @@ -448,50 +437,51 @@ int sh_lex(void) fcseek(1); } while(c=='#'); - lexd.nocopy = n; + lp->lexd.nocopy = n; if(c<0) - return(shlex.token=EOFSYM); + return(lp->token=EOFSYM); n = S_NLTOK; shp->inlineno--; /* FALL THRU */ case S_NLTOK: /* check for here-document */ - if(shlex.heredoc) + if(lp->heredoc) { - if(!lexd.dolparen) - lexd.nocopy++; + if(!lp->lexd.dolparen) + lp->lexd.nocopy++; c = shp->inlineno; - if(here_copy(lp,shlex.heredoc)<=0 && shlex.lasttok) + if(here_copy(lp,lp->heredoc)<=0 && lp->lasttok) { - shlex.lasttok = IODOCSYM; - shlex.token = EOFSYM; - shlex.lastline = c; - sh_syntax(); + lp->lasttok = IODOCSYM; + lp->token = EOFSYM; + lp->lastline = c; + sh_syntax(lp); } - if(!lexd.dolparen) - lexd.nocopy--; - shlex.heredoc = 0; + if(!lp->lexd.dolparen) + lp->lexd.nocopy--; + lp->heredoc = 0; } - lex.reservok = !lex.intest; - lex.skipword = 0; + lp->lex.reservok = !lp->lex.intest; + lp->lex.skipword = 0; /* FALL THRU */ case S_NL: /* skip over new-lines */ - lex.last_quote = 0; + lp->lex.last_quote = 0; while(shp->inlineno++,fcget()=='\n'); fcseek(-1); if(n==S_NLTOK) { - lex.comp_assign = 0; - return(shlex.token='\n'); + lp->comp_assign = 0; + return(lp->token='\n'); } case S_BLNK: - if(lex.incase<=TEST_RE) + if(lp->lex.incase<=TEST_RE) continue; /* implicit RPAREN for =~ test operator */ - if(inlevel+1==lexd.level) + if(inlevel+1==lp->lexd.level) { - fcseek(-1); + if(lp->lex.intest) + fcseek(-1); c = RPAREN; goto do_pop; } @@ -500,26 +490,26 @@ int sh_lex(void) /* return operator token */ if(c=='<' || c=='>') { - if(lex.testop2) - lex.testop2 = 0; + if(lp->lex.testop2) + lp->lex.testop2 = 0; else { - shlex.digits = (c=='>'); - lex.skipword = 1; - shlex.aliasok = lex.reservok; - lex.reservok = 0; + lp->digits = (c=='>'); + lp->lex.skipword = 1; + lp->aliasok = lp->lex.reservok; + lp->lex.reservok = 0; } } else { - lex.reservok = !lex.intest; + lp->lex.reservok = !lp->lex.intest; if(c==RPAREN) { - if(!lexd.dolparen) - lex.incase = 0; - return(shlex.token=c); + if(!lp->lexd.dolparen) + lp->lex.incase = 0; + return(lp->token=c); } - lex.testop1 = lex.intest; + lp->lex.testop1 = lp->lex.intest; } if(fcgetc(n)>0) fcseek(-1); @@ -528,25 +518,25 @@ int sh_lex(void) if(n==c) { if(c=='<') - lexd.docword=1; + lp->lexd.docword=1; else if(n==LPAREN) { - lexd.nest=1; - shlex.lastline = shp->inlineno; - lexd.lex_state = ST_NESTED; + lp->lexd.nest=1; + lp->lastline = shp->inlineno; + lp->lexd.lex_state = ST_NESTED; fcseek(1); - return(sh_lex()); + return(sh_lex(lp)); } c |= SYMREP; } else if(c=='(' || c==')') - return(shlex.token=c); + return(lp->token=c); else if(c=='&') { #if SHOPT_BASH if(!sh_isoption(SH_POSIX) && n=='>') { - shlex.digits = -1; + lp->digits = -1; c = '>'; } else @@ -560,8 +550,8 @@ int sh_lex(void) else if(n==LPAREN) { c |= SYMLPAR; - lex.reservok = 1; - lex.skipword = 0; + lp->lex.reservok = 1; + lp->lex.skipword = 0; } else if(n=='|') c |= SYMPIPE; @@ -569,24 +559,33 @@ int sh_lex(void) c = IORDWRSYM; else if(n=='#' && (c=='<'||c=='>')) c |= SYMSHARP; + else if(n==';' && c=='>') + { + c |= SYMSEMI; + if(lp->inexec) + { + lp->token = c; + sh_syntax(lp); + } + } else n = 0; if(n) { fcseek(1); - lex.incase = (c==BREAKCASESYM || c==FALLTHRUSYM); + lp->lex.incase = (c==BREAKCASESYM || c==FALLTHRUSYM); } else { - if((n=fcpeek(0))!=RPAREN && n!=LPAREN && lexd.warn) + if((n=fcpeek(0))!=RPAREN && n!=LPAREN && lp->lexd.warn) errormsg(SH_DICT,ERROR_warn(0),e_lexspace,shp->inlineno,c,n); } } - if(c==LPAREN && lex.comp_assign && !lex.intest && !lex.incase) - lex.comp_assign = 2; + if(c==LPAREN && lp->comp_assign && !lp->lex.intest && !lp->lex.incase) + lp->comp_assign = 2; else - lex.comp_assign = 0; - return(shlex.token=c); + lp->comp_assign = 0; + return(lp->token=c); case S_ESC: /* check for \<new-line> */ fcgetc(n); @@ -612,19 +611,19 @@ int sh_lex(void) if(!(sp=fcfile())) state=fcseek(0); fcclose(); - ap = shlex.arg; + ap = lp->arg; if(sp) fcfopen(sp); else fcsopen((char*)state); /* remove \new-line */ - n = staktell()-c; - stakseek(n); - shlex.arg = ap; + n = stktell(stkp)-c; + stkseek(stkp,n); + lp->arg = ap; if(n<=ARGVAL) { mode = 0; - lexd.first = 0; + lp->lexd.first = 0; } continue; } @@ -632,23 +631,23 @@ int sh_lex(void) if(mode==ST_DOL) goto err; #ifndef STR_MAXIMAL - else if(mode==ST_NESTED && lexd.warn && - endchar()==RBRACE && + else if(mode==ST_NESTED && lp->lexd.warn && + endchar(lp)==RBRACE && sh_lexstates[ST_DOL][n]==S_DIG ) errormsg(SH_DICT,ERROR_warn(0),e_lexfuture,shp->inlineno,n); #endif /* STR_MAXIMAL */ break; case S_NAME: - if(!lex.skipword) - lex.reservok *= 2; + if(!lp->lex.skipword) + lp->lex.reservok *= 2; /* FALL THRU */ case S_TILDE: case S_RES: - if(!lexd.dolparen) - lexd.first = fcseek(0)-LEN; - else if(lexd.docword) - lexd.docend = fcseek(0)-LEN; + if(!lp->lexd.dolparen) + lp->lexd.first = fcseek(0)-LEN; + else if(lp->lexd.docword) + lp->lexd.docend = fcseek(0)-LEN; mode = ST_NAME; if(c=='.') fcseek(-1); @@ -656,7 +655,11 @@ int sh_lex(void) continue; fcgetc(n); if(n>0) + { + if(c=='~' && n==LPAREN && lp->lex.incase) + lp->lex.incase = TEST_RE; fcseek(-1); + } if(n==LPAREN) goto epat; wordflags = ARG_MAC; @@ -665,6 +668,7 @@ int sh_lex(void) case S_REG: if(mode==ST_BEGIN) { + do_reg: /* skip new-line joining */ if(c=='\\' && fcpeek(0)=='\n') { @@ -673,11 +677,11 @@ int sh_lex(void) continue; } fcseek(-1); - if(!lexd.dolparen) - lexd.first = fcseek(0); - else if(lexd.docword) - lexd.docend = fcseek(0); - if(c=='[' && shlex.assignok>=SH_ASSIGN) + if(!lp->lexd.dolparen) + lp->lexd.first = fcseek(0); + else if(lp->lexd.docword) + lp->lexd.docend = fcseek(0); + if(c=='[' && lp->assignok>=SH_ASSIGN) { mode = ST_NAME; continue; @@ -686,7 +690,7 @@ int sh_lex(void) mode = ST_NORM; continue; case S_LIT: - if(oldmode()==ST_NONE) /* in ((...)) */ + if(oldmode(lp)==ST_NONE && !lp->lexd.noarg) /* in ((...)) */ { if((c=fcpeek(0))==LPAREN || c==RPAREN || c=='$' || c==LBRACE || c==RBRACE || c=='[' || c==']') { @@ -698,35 +702,35 @@ int sh_lex(void) wordflags |= ARG_QUOTED; if(mode==ST_DOL) { - if(endchar()!='$') + if(endchar(lp)!='$') goto err; - if(oldmode()==ST_QUOTE) /* $' within "" or `` */ + if(oldmode(lp)==ST_QUOTE) /* $' within "" or `` */ { - if(lexd.warn) + if(lp->lexd.warn) errormsg(SH_DICT,ERROR_warn(0),e_lexslash,shp->inlineno); mode = ST_LIT; } } if(mode!=ST_LIT) { - if(lexd.warn && lex.last_quote && shp->inlineno > shlex.lastline) - errormsg(SH_DICT,ERROR_warn(0),e_lexlongquote,shlex.lastline,lex.last_quote); - lex.last_quote = 0; - shlex.lastline = shp->inlineno; + if(lp->lexd.warn && lp->lex.last_quote && shp->inlineno > lp->lastline) + errormsg(SH_DICT,ERROR_warn(0),e_lexlongquote,lp->lastline,lp->lex.last_quote); + lp->lex.last_quote = 0; + lp->lastline = shp->inlineno; if(mode!=ST_DOL) - pushlevel('\'',mode); + pushlevel(lp,'\'',mode); mode = ST_LIT; continue; } /* check for multi-line single-quoted string */ - else if(shp->inlineno > shlex.lastline) - lex.last_quote = '\''; - mode = oldmode(); - poplevel(); + else if(shp->inlineno > lp->lastline) + lp->lex.last_quote = '\''; + mode = oldmode(lp); + poplevel(lp); break; case S_ESC2: /* \ inside '' */ - if(endchar()=='$') + if(endchar(lp)=='$') { fcgetc(n); if(n=='\n') @@ -734,37 +738,40 @@ int sh_lex(void) } continue; case S_GRAVE: - if(lexd.warn && (mode!=ST_QUOTE || endchar()!='`')) + if(lp->lexd.warn && (mode!=ST_QUOTE || endchar(lp)!='`')) errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete1,shp->inlineno); wordflags |=(ARG_MAC|ARG_EXP); if(mode==ST_QUOTE) ingrave = !ingrave; /* FALL THRU */ case S_QUOTE: - if(oldmode()==ST_NONE && lexd.arith) /* in ((...)) */ - continue; + if(oldmode(lp)==ST_NONE && lp->lexd.arith) /* in ((...)) */ + { + if(n!=S_GRAVE || fcpeek(0)=='\'') + continue; + } if(n==S_QUOTE) wordflags |=ARG_QUOTED; if(mode!=ST_QUOTE) { if(c!='"' || mode!=ST_QNEST) { - if(lexd.warn && lex.last_quote && shp->inlineno > shlex.lastline) - errormsg(SH_DICT,ERROR_warn(0),e_lexlongquote,shlex.lastline,lex.last_quote); - lex.last_quote=0; - shlex.lastline = shp->inlineno; - pushlevel(c,mode); + if(lp->lexd.warn && lp->lex.last_quote && shp->inlineno > lp->lastline) + errormsg(SH_DICT,ERROR_warn(0),e_lexlongquote,lp->lastline,lp->lex.last_quote); + lp->lex.last_quote=0; + lp->lastline = shp->inlineno; + pushlevel(lp,c,mode); } ingrave = (c=='`'); mode = ST_QUOTE; continue; } - else if((n=endchar())==c) + else if((n=endchar(lp))==c) { - if(shp->inlineno > shlex.lastline) - lex.last_quote = c; - mode = oldmode(); - poplevel(); + if(shp->inlineno > lp->lastline) + lp->lex.last_quote = c; + mode = oldmode(lp); + poplevel(lp); } else if(c=='"' && n==RBRACE) mode = ST_QNEST; @@ -774,46 +781,52 @@ int sh_lex(void) if(mode==ST_QUOTE && ingrave) continue; #if SHOPT_KIA - if(lexd.first) - lexd.kiaoff = fcseek(0)-lexd.first; + if(lp->lexd.first) + lp->lexd.kiaoff = fcseek(0)-lp->lexd.first; else - lexd.kiaoff = staktell()+fcseek(0)-fcfirst(); + lp->lexd.kiaoff = stktell(stkp)+fcseek(0)-fcfirst(); #endif /* SHOPT_KIA */ - pushlevel('$',mode); + pushlevel(lp,'$',mode); mode = ST_DOL; continue; case S_PAR: + do_comsub: wordflags |= ARG_MAC; - mode = oldmode(); - poplevel(); + mode = oldmode(lp); + poplevel(lp); fcseek(-1); - wordflags |= comsub(lp); + wordflags |= comsub(lp,c); continue; case S_RBRA: - if((n=endchar()) == '$') + if((n=endchar(lp)) == '$') goto err; if(mode!=ST_QUOTE || n==RBRACE) { - mode = oldmode(); - poplevel(); + mode = oldmode(lp); + poplevel(lp); } break; case S_EDOL: /* end $identifier */ #if SHOPT_KIA - if(shlex.kiafile) - refvar(0); + if(lp->kiafile) + refvar(lp,0); #endif /* SHOPT_KIA */ - if(lexd.warn && c==LBRACT) + if(lp->lexd.warn && c==LBRACT && !lp->lex.intest && !lp->lexd.arith && oldmode(lp)!= ST_NESTED) errormsg(SH_DICT,ERROR_warn(0),e_lexusebrace,shp->inlineno); fcseek(-1); - mode = oldmode(); - poplevel(); + mode = oldmode(lp); + poplevel(lp); break; case S_DOT: /* make sure next character is alpha */ if(fcgetc(n)>0) - fcseek(-1); + { + if(n=='.') + fcgetc(n); + if(n>0) + fcseek(-1); + } if(isaletter(n) || n==LBRACT) continue; if(mode==ST_NAME) @@ -829,27 +842,27 @@ int sh_lex(void) goto err; case S_SPC1: wordflags |= ARG_MAC; - if(endchar()==RBRACE) + if(endchar(lp)==RBRACE) { - setchar(c); + setchar(lp,c); continue; } /* FALL THRU */ case S_ALP: - if(c=='.' && endchar()=='$') + if(c=='.' && endchar(lp)=='$') goto err; case S_SPC2: case S_DIG: wordflags |= ARG_MAC; - switch(endchar()) + switch(endchar(lp)) { case '$': if(n==S_ALP) /* $identifier */ mode = ST_DOLNAME; else { - mode = oldmode(); - poplevel(); + mode = oldmode(lp); + poplevel(lp); } break; #if SHOPT_TYPEDEF @@ -862,7 +875,7 @@ int sh_lex(void) case RBRACE: if(n==S_ALP) { - setchar(RBRACE); + setchar(lp,RBRACE); if(c=='.') fcseek(-1); mode = ST_BRACE; @@ -874,9 +887,9 @@ int sh_lex(void) if(state[c]==S_ALP) goto err; if(n==S_DIG) - setchar('0'); + setchar(lp,'0'); else - setchar('!'); + setchar(lp,'!'); } break; case '0': @@ -888,23 +901,23 @@ int sh_lex(void) break; dolerr: case S_ERR: - if((n=endchar()) == '$') + if((n=endchar(lp)) == '$') goto err; if(c=='*' || (n=sh_lexstates[ST_BRACE][c])!=S_MOD1 && n!=S_MOD2) { /* see whether inside `...` */ - mode = oldmode(); - poplevel(); - if((n = endchar()) != '`') + mode = oldmode(lp); + poplevel(lp); + if((n = endchar(lp)) != '`') goto err; - pushlevel(RBRACE,mode); + pushlevel(lp,RBRACE,mode); } else - setchar(RBRACE); + setchar(lp,RBRACE); mode = ST_NESTED; continue; case S_MOD1: - if(oldmode()==ST_QUOTE || oldmode()==ST_NONE) + if(oldmode(lp)==ST_QUOTE || oldmode(lp)==ST_NONE) { /* allow ' inside "${...}" */ if(c==':' && fcgetc(n)>0) @@ -921,8 +934,8 @@ int sh_lex(void) /* FALL THRU */ case S_MOD2: #if SHOPT_KIA - if(shlex.kiafile) - refvar(1); + if(lp->kiafile) + refvar(lp,1); #endif /* SHOPT_KIA */ if(c!=':' && fcgetc(n)>0) { @@ -935,10 +948,10 @@ int sh_lex(void) { if(c!='%') { - shlex.token = n; - sh_syntax(); + lp->token = n; + sh_syntax(lp); } - else if(lexd.warn) + else if(lp->lexd.warn) errormsg(SH_DICT,ERROR_warn(0),e_lexquote,shp->inlineno,'%'); } } @@ -946,26 +959,31 @@ int sh_lex(void) mode = ST_NESTED; continue; case S_LBRA: - if((c=endchar()) == '$') + if((c=endchar(lp)) == '$') { - setchar(RBRACE); if(fcgetc(c)>0) fcseek(-1); + setchar(lp,RBRACE); if(state[c]!=S_ERR && c!=RBRACE) continue; + if((n=sh_lexstates[ST_BEGIN][c])==0 || n==S_OP || n==S_NLTOK) + { + c = LBRACE; + goto do_comsub; + } } err: - n = endchar(); - mode = oldmode(); - poplevel(); + n = endchar(lp); + mode = oldmode(lp); + poplevel(lp); if(n!='$') { - shlex.token = c; - sh_syntax(); + lp->token = c; + sh_syntax(lp); } else { - if(lexd.warn && c!='/' && sh_lexstates[ST_NORM][c]!=S_BREAK && (c!='"' || mode==ST_QUOTE)) + if(lp->lexd.warn && c!='/' && sh_lexstates[ST_NORM][c]!=S_BREAK && (c!='"' || mode==ST_QUOTE)) errormsg(SH_DICT,ERROR_warn(0),e_lexslash,shp->inlineno); else if(c=='"' && mode!=ST_QUOTE) wordflags |= ARG_MESSAGE; @@ -973,18 +991,23 @@ int sh_lex(void) } continue; case S_META: - if(lexd.warn && endchar()==RBRACE) + if(lp->lexd.warn && endchar(lp)==RBRACE) errormsg(SH_DICT,ERROR_warn(0),e_lexusequote,shp->inlineno,c); continue; case S_PUSH: - pushlevel(RPAREN,mode); + pushlevel(lp,RPAREN,mode); mode = ST_NESTED; continue; case S_POP: do_pop: - if(lexd.level <= inlevel) + if(lp->lexd.level <= inlevel) break; - n = endchar(); + if(lp->lexd.level==inlevel+1 && lp->lex.incase>=TEST_RE && !lp->lex.intest) + { + fcseek(-1); + goto breakloop; + } + n = endchar(lp); if(c==RBRACT && !(n==RBRACT || n==RPAREN)) continue; if((c==RBRACE||c==RPAREN) && n==RPAREN) @@ -1001,18 +1024,18 @@ int sh_lex(void) } if(c==';' && n!=';') { - if(lexd.warn && n==RBRACE) + if(lp->lexd.warn && n==RBRACE) errormsg(SH_DICT,ERROR_warn(0),e_lexusequote,shp->inlineno,c); continue; } if(mode==ST_QNEST) { - if(lexd.warn) + if(lp->lexd.warn) errormsg(SH_DICT,ERROR_warn(0),e_lexescape,shp->inlineno,c); continue; } - mode = oldmode(); - poplevel(); + mode = oldmode(lp); + poplevel(lp); /* quotes in subscript need expansion */ if(mode==ST_NAME && (wordflags&ARG_QUOTED)) wordflags |= ARG_MAC; @@ -1021,43 +1044,43 @@ int sh_lex(void) { if(fcgetc(n)==RPAREN) { - if(mode==ST_NONE && !lexd.dolparen) + if(mode==ST_NONE && !lp->lexd.dolparen) goto breakloop; - lex.reservok = 1; - lex.skipword = 0; - return(shlex.token=EXPRSYM); + lp->lex.reservok = 1; + lp->lex.skipword = 0; + return(lp->token=EXPRSYM); } /* backward compatibility */ - if(lexd.dolparen) + if(lp->lexd.dolparen) fcseek(-1); else { - if(lexd.warn) + if(lp->lexd.warn) errormsg(SH_DICT,ERROR_warn(0),e_lexnested,shp->inlineno); - if(!(state=lexd.first)) + if(!(state=lp->lexd.first)) state = fcfirst(); fcseek(state-fcseek(0)); - if(shlex.arg) + if(lp->arg) { - shlex.arg = (struct argnod*)stakfreeze(1); - setupalias(lp,shlex.arg->argval,NIL(Namval_t*)); + lp->arg = (struct argnod*)stkfreeze(stkp,1); + setupalias(lp,lp->arg->argval,NIL(Namval_t*)); } - lexd.paren = 1; + lp->lexd.paren = 1; } - return(shlex.token=LPAREN); + return(lp->token=LPAREN); } if(mode==ST_NONE) return(0); if(c!=n) { - shlex.token = c; - sh_syntax(); + lp->token = c; + sh_syntax(lp); } if(c==RBRACE && (mode==ST_NAME||mode==ST_NORM)) goto epat; continue; case S_EQ: - assignment = shlex.assignok; + assignment = lp->assignok; /* FALL THRU */ case S_COLON: if(assignment) @@ -1070,7 +1093,7 @@ int sh_lex(void) } break; case S_LABEL: - if(lex.reservok && !lex.incase) + if(lp->lex.reservok && !lp->lex.incase) { c = fcget(); fcseek(-1); @@ -1083,12 +1106,12 @@ int sh_lex(void) break; case S_BRACT: /* check for possible subscript */ - if((n=endchar())==RBRACT || n==RPAREN || + if((n=endchar(lp))==RBRACT || n==RPAREN || (mode==ST_BRACE) || - (oldmode()==ST_NONE) || - (mode==ST_NAME && (shlex.assignok||lexd.level))) + (oldmode(lp)==ST_NONE) || + (mode==ST_NAME && (lp->assignok||lp->lexd.level))) { - pushlevel(RBRACT,mode); + pushlevel(lp,RBRACT,mode); wordflags |= ARG_QUOTED; mode = ST_NESTED; continue; @@ -1098,23 +1121,41 @@ int sh_lex(void) case S_BRACE: { int isfirst; - if(lexd.dolparen) + if(lp->lexd.dolparen) + { + if(mode==ST_BEGIN && (lp->lex.reservok||lp->comsub)) + { + fcgetc(n); + if(n>0) + fcseek(-1); + else + n = '\n'; + if(n==RBRACT || sh_lexstates[ST_NORM][n]) + return(lp->token=c); + } break; - isfirst = (lexd.first&&fcseek(0)==lexd.first+1); + } + else if(mode==ST_BEGIN) + { + if(lp->comsub && c==RBRACE) + return(lp->token=c); + goto do_reg; + } + isfirst = (lp->lexd.first&&fcseek(0)==lp->lexd.first+1); fcgetc(n); /* check for {} */ if(c==LBRACE && n==RBRACE) break; if(n>0) fcseek(-1); - else if(lex.reservok) + else if(lp->lex.reservok) break; /* check for reserved word { or } */ - if(lex.reservok && state[n]==S_BREAK && isfirst) + if(lp->lex.reservok && state[n]==S_BREAK && isfirst) break; if(sh_isoption(SH_BRACEEXPAND) && c==LBRACE && !assignment && state[n]!=S_BREAK - && !lex.incase && !lex.intest - && !lex.skipword) + && !lp->lex.incase && !lp->lex.intest + && !lp->lex.skipword) { wordflags |= ARG_EXP; } @@ -1129,14 +1170,14 @@ int sh_lex(void) epat: if(fcgetc(n)==LPAREN) { - if(lex.incase==TEST_RE) + if(lp->lex.incase==TEST_RE) { - lex.incase++; - pushlevel(RPAREN,ST_NORM); + lp->lex.incase++; + pushlevel(lp,RPAREN,ST_NORM); mode = ST_NESTED; } wordflags |= ARG_EXP; - pushlevel(RPAREN,mode); + pushlevel(lp,RPAREN,mode); mode = ST_NESTED; continue; } @@ -1146,64 +1187,71 @@ int sh_lex(void) continue; break; } - lex.comp_assign = 0; + lp->comp_assign = 0; if(mode==ST_NAME) mode = ST_NORM; else if(mode==ST_NONE) return(0); } breakloop: - if(lexd.dolparen) + if(lp->lexd.nocopy) + { + lp->lexd.balance = 0; + return(0); + } + if(lp->lexd.dolparen) { - lexd.balance = 0; - if(lexd.docword) + lp->lexd.balance = 0; + if(lp->lexd.docword) nested_here(lp); - lexd.message = (wordflags&ARG_MESSAGE); - return(shlex.token=0); + lp->lexd.message = (wordflags&ARG_MESSAGE); + return(lp->token=0); } - if(!(state=lexd.first)) + if(!(state=lp->lexd.first)) state = fcfirst(); n = fcseek(0)-(char*)state; - if(!shlex.arg) - shlex.arg = (struct argnod*)stakseek(ARGVAL); + if(!lp->arg) + lp->arg = (struct argnod*)stkseek(stkp,ARGVAL); if(n>0) - stakwrite(state,n); + sfwrite(stkp,state,n); /* add balancing character if necessary */ - if(lexd.balance) + if(lp->lexd.balance) { - stakputc(lexd.balance); - lexd.balance = 0; + sfputc(stkp,lp->lexd.balance); + lp->lexd.balance = 0; } - stakputc(0); - stakseek(staktell()-1); - state = stakptr(ARGVAL); - n = staktell()-ARGVAL; - lexd.first=0; + sfputc(stkp,0); + stkseek(stkp,stktell(stkp)-1); + state = stkptr(stkp,ARGVAL); + n = stktell(stkp)-ARGVAL; + lp->lexd.first=0; if(n==1) { /* check for numbered redirection */ n = state[0]; if((c=='<' || c=='>') && isadigit(n)) { - c = sh_lex(); - shlex.digits = (n-'0'); + c = sh_lex(lp); + lp->digits = (n-'0'); return(c); } if(n==LBRACT) c = 0; + else if(n==RBRACE && lp->comsub) + return(lp->token=n); else if(n=='~') c = ARG_MAC; else c = (wordflags&ARG_EXP); n = 1; } - else if(n>2 && state[0]=='{' && state[n-1]=='}' && !lex.intest && !lex.incase && (c=='<' || c== '>') && sh_isoption(SH_BRACEEXPAND)) + else if(n>2 && state[0]=='{' && state[n-1]=='}' && !lp->lex.intest && !lp->lex.incase && (c=='<' || c== '>') && sh_isoption(SH_BRACEEXPAND)) { if(!strchr(state,',')) { - stakseek(staktell()-1); - shlex.arg = (struct argnod*)stakfreeze(1); - return(shlex.token=IOVNAME); + stkseek(stkp,stktell(stkp)-1); + lp->arg = (struct argnod*)stkfreeze(stkp,1); + return(lp->token=IOVNAME); } c = wordflags; } @@ -1211,151 +1259,151 @@ breakloop: c = wordflags; if(assignment<0) { - stakseek(staktell()-1); - shlex.arg = (struct argnod*)stakfreeze(1); - lex.reservok = 1; - return(shlex.token=LABLSYM); + stkseek(stkp,stktell(stkp)-1); + lp->arg = (struct argnod*)stkfreeze(stkp,1); + lp->lex.reservok = 1; + return(lp->token=LABLSYM); } - if(assignment || (lex.intest&&!lex.incase) || mode==ST_NONE) + if(assignment || (lp->lex.intest&&!lp->lex.incase) || mode==ST_NONE) c &= ~ARG_EXP; if((c&ARG_EXP) && (c&ARG_QUOTED)) c |= ARG_MAC; if(mode==ST_NONE) { /* eliminate trailing )) */ - stakseek(staktell()-2); + stkseek(stkp,stktell(stkp)-2); } if(c&ARG_MESSAGE) { if(sh_isoption(SH_DICTIONARY)) - shlex.arg = sh_endword(2); + lp->arg = sh_endword(shp,2); if(!sh_isoption(SH_NOEXEC)) { - shlex.arg = sh_endword(1); + lp->arg = sh_endword(shp,1); c &= ~ARG_MESSAGE; } } - if(c==0 || (c&(ARG_MAC|ARG_EXP)) || (lexd.warn && !lexd.docword)) + if(c==0 || (c&(ARG_MAC|ARG_EXP)) || (lp->lexd.warn && !lp->lexd.docword)) { - shlex.arg = (struct argnod*)stakfreeze(1); - shlex.arg->argflag = (c?c:ARG_RAW); + lp->arg = (struct argnod*)stkfreeze(stkp,1); + lp->arg->argflag = (c?c:ARG_RAW); } else if(mode==ST_NONE) - shlex.arg = sh_endword(-1); + lp->arg = sh_endword(shp,-1); else - shlex.arg = sh_endword(0); - state = shlex.arg->argval; - lex.comp_assign = assignment; + lp->arg = sh_endword(shp,0); + state = lp->arg->argval; + lp->comp_assign = assignment; if(assignment) - shlex.arg->argflag |= ARG_ASSIGN; - else if(!lex.skipword) - shlex.assignok = 0; - shlex.arg->argchn.cp = 0; - shlex.arg->argnxt.ap = 0; + lp->arg->argflag |= ARG_ASSIGN; + else if(!lp->lex.skipword) + lp->assignok = 0; + lp->arg->argchn.cp = 0; + lp->arg->argnxt.ap = 0; if(mode==ST_NONE) - return(shlex.token=EXPRSYM); - if(lex.intest) + return(lp->token=EXPRSYM); + if(lp->lex.intest) { - if(lex.testop1) + if(lp->lex.testop1) { - lex.testop1 = 0; + lp->lex.testop1 = 0; if(n==2 && state[0]=='-' && state[2]==0 && strchr(test_opchars,state[1])) { - if(lexd.warn && state[1]=='a') + if(lp->lexd.warn && state[1]=='a') errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete2,shp->inlineno); - shlex.digits = state[1]; - shlex.token = TESTUNOP; + lp->digits = state[1]; + lp->token = TESTUNOP; } else if(n==1 && state[0]=='!' && state[1]==0) { - lex.testop1 = 1; - shlex.token = '!'; + lp->lex.testop1 = 1; + lp->token = '!'; } else { - lex.testop2 = 1; - shlex.token = 0; + lp->lex.testop2 = 1; + lp->token = 0; } - return(shlex.token); + return(lp->token); } - lex.incase = 0; + lp->lex.incase = 0; c = sh_lookup(state,shtab_testops); switch(c) { case TEST_END: - lex.testop2 = lex.intest = 0; - lex.reservok = 1; - shlex.token = ETESTSYM; - return(shlex.token); + lp->lex.testop2 = lp->lex.intest = 0; + lp->lex.reservok = 1; + lp->token = ETESTSYM; + return(lp->token); case TEST_SEQ: - if(lexd.warn && state[1]==0) + if(lp->lexd.warn && state[1]==0) errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete3,shp->inlineno); /* FALL THRU */ default: - if(lex.testop2) + if(lp->lex.testop2) { - if(lexd.warn && (c&TEST_ARITH)) + if(lp->lexd.warn && (c&TEST_ARITH)) errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete4,shp->inlineno,state); if(c&TEST_PATTERN) - lex.incase = 1; + lp->lex.incase = 1; else if(c==TEST_REP) - lex.incase = TEST_RE; - lex.testop2 = 0; - shlex.digits = c; - shlex.token = TESTBINOP; - return(shlex.token); + lp->lex.incase = TEST_RE; + lp->lex.testop2 = 0; + lp->digits = c; + lp->token = TESTBINOP; + return(lp->token); } case TEST_OR: case TEST_AND: case 0: - return(shlex.token=0); + return(lp->token=0); } } - if(lex.reservok /* && !lex.incase*/ && n<=2) + if(lp->lex.reservok /* && !lp->lex.incase*/ && n<=2) { /* check for {, }, ! */ c = state[0]; if(n==1 && (c=='{' || c=='}' || c=='!')) { - if(lexd.warn && c=='{' && lex.incase==2) + if(lp->lexd.warn && c=='{' && lp->lex.incase==2) errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete6,shp->inlineno); - if(lex.incase==1 && c==RBRACE) - lex.incase = 0; - return(shlex.token=c); + if(lp->lex.incase==1 && c==RBRACE) + lp->lex.incase = 0; + return(lp->token=c); } - else if(!lex.incase && c==LBRACT && state[1]==LBRACT) + else if(!lp->lex.incase && c==LBRACT && state[1]==LBRACT) { - lex.intest = lex.testop1 = 1; - lex.testop2 = lex.reservok = 0; - return(shlex.token=BTESTSYM); + lp->lex.intest = lp->lex.testop1 = 1; + lp->lex.testop2 = lp->lex.reservok = 0; + return(lp->token=BTESTSYM); } } c = 0; - if(!lex.skipword) + if(!lp->lex.skipword) { - if(n>1 && lex.reservok==1 && mode==ST_NAME && + if(n>1 && lp->lex.reservok==1 && mode==ST_NAME && (c=sh_lookup(state,shtab_reserved))) { - if(lex.incase) + if(lp->lex.incase) { - if(lex.incase >1) - lex.incase = 1; + if(lp->lex.incase >1) + lp->lex.incase = 1; else if(c==ESACSYM) - lex.incase = 0; + lp->lex.incase = 0; else c = 0; } else if(c==FORSYM || c==CASESYM || c==SELECTSYM || c==FUNCTSYM || c==NSPACESYM) { - lex.skipword = 1; - lex.incase = 2*(c==CASESYM); + lp->lex.skipword = 1; + lp->lex.incase = 2*(c==CASESYM); } else - lex.skipword = 0; + lp->lex.skipword = 0; if(c==INSYM) - lex.reservok = 0; + lp->lex.reservok = 0; else if(c==TIMESYM) { /* yech - POSIX requires time -p */ @@ -1365,13 +1413,13 @@ breakloop: if(n=='-') c=0; } - return(shlex.token=c); + return(lp->token=c); } - if(!(wordflags&ARG_QUOTED) && (lex.reservok||shlex.aliasok)) + if(!(wordflags&ARG_QUOTED) && (lp->lex.reservok||lp->aliasok)) { /* check for aliases */ Namval_t* np; - if(!lex.incase && !assignment && fcpeek(0)!=LPAREN && + if(!lp->lex.incase && !assignment && fcpeek(0)!=LPAREN && (np=nv_search(state,shp->alias_tree,HASH_SCOPE)) && !nv_isattr(np,NV_NOEXPAND) #if KSHELL @@ -1381,33 +1429,35 @@ breakloop: { setupalias(lp,state,np); nv_onattr(np,NV_NOEXPAND); - lex.reservok = 1; - shlex.assignok |= lex.reservok; - return(sh_lex()); + lp->lex.reservok = 1; + lp->assignok |= lp->lex.reservok; + return(sh_lex(lp)); } } - lex.reservok = 0; + lp->lex.reservok = 0; } - lex.skipword = lexd.docword = 0; - return(shlex.token=c); + lp->lex.skipword = lp->lexd.docword = 0; + return(lp->token=c); } /* * read to end of command substitution */ -static int comsub(register Lex_t *lp) +static int comsub(register Lex_t *lp, int endtok) { register int n,c,count=1; - register int line=shlex.sh->inlineno; + register int line=lp->sh->inlineno; char word[5]; - int messages=0, assignok=shlex.assignok; + int messages=0, assignok=lp->assignok, csub; struct lexstate save; - save = lex; - sh_lexopen(lp,shlex.sh,1); - lexd.dolparen++; - lex.incase=0; - pushlevel(0,0); - if(sh_lex()==LPAREN) + save = lp->lex; + csub = lp->comsub; + sh_lexopen(lp,lp->sh,1); + lp->lexd.dolparen++; + lp->lex.incase=0; + pushlevel(lp,0,0); + lp->comsub = (endtok==LBRACE); + if(sh_lex(lp)==endtok) { while(1) { @@ -1428,49 +1478,75 @@ static int comsub(register Lex_t *lp) if(sh_lexstates[ST_NAME][c]==S_BREAK) { if(memcmp(word,"case",4)==0) - lex.incase=1; + lp->lex.incase=1; else if(memcmp(word,"esac",4)==0) - lex.incase=0; + lp->lex.incase=0; } skip: if(c && (c!='#' || n==0)) fcseek(-1); - if(c==RBRACE && lex.incase) - lex.incase=0; - switch(sh_lex()) + if(c==RBRACE && lp->lex.incase) + lp->lex.incase=0; + switch(c=sh_lex(lp)) { - case LPAREN: case IPROCSYM: case OPROCSYM: - if(!lex.incase) + case LBRACE: + if(endtok==LBRACE && !lp->lex.incase) + { + lp->comsub = 0; + count++; + } + break; + case RBRACE: + rbrace: + if(endtok==LBRACE && --count<=0) + goto done; + lp->comsub = (count==1); + break; + case IPROCSYM: case OPROCSYM: + case LPAREN: + if(endtok==LPAREN && !lp->lex.incase) count++; break; case RPAREN: - if(lex.incase) - lex.incase=0; - else if(--count<=0) + if(lp->lex.incase) + lp->lex.incase=0; + else if(endtok==LPAREN && --count<=0) goto done; break; case EOFSYM: - shlex.lastline = line; - shlex.lasttok = LPAREN; - sh_syntax(); + lp->lastline = line; + lp->lasttok = endtok; + sh_syntax(lp); case IOSEEKSYM: if(fcgetc(c)!='#' && c>0) fcseek(-1); break; case IODOCSYM: - sh_lex(); + sh_lex(lp); break; case 0: - messages |= lexd.message; + lp->lex.reservok = 0; + messages |= lp->lexd.message; + break; + case ';': + fcgetc(c); + if(c==RBRACE && endtok==LBRACE) + goto rbrace; + if(c>0) + fcseek(-1); + /* fall through*/ + default: + lp->lex.reservok = 1; } } } done: - poplevel(); - shlex.lastline = line; - lexd.dolparen--; - lex = save; - shlex.assignok = (endchar()==RBRACT?assignok:0); + poplevel(lp); + lp->comsub = csub; + lp->lastline = line; + lp->lexd.dolparen--; + lp->lex = save; + lp->assignok = (endchar(lp)==RBRACT?assignok:0); return(messages); } @@ -1480,30 +1556,31 @@ done: */ static void nested_here(register Lex_t *lp) { - register struct ionod *iop; - register int n,offset; - struct argnod *arg = shlex.arg; - char *base; - if(offset=staktell()) - base = stakfreeze(0); - n = fcseek(0)-lexd.docend; + register struct ionod *iop; + register int n,offset; + struct argnod *arg = lp->arg; + Stk_t *stkp = lp->sh->stk; + char *base; + if(offset=stktell(stkp)) + base = stkfreeze(stkp,0); + n = fcseek(0)-lp->lexd.docend; iop = newof(0,struct ionod,1,n+ARGVAL); - iop->iolst = shlex.heredoc; - stakseek(ARGVAL); - stakwrite(lexd.docend,n); - shlex.arg = sh_endword(0); + iop->iolst = lp->heredoc; + stkseek(stkp,ARGVAL); + sfwrite(stkp,lp->lexd.docend,n); + lp->arg = sh_endword(lp->sh,0); iop->ioname = (char*)(iop+1); - strcpy(iop->ioname,shlex.arg->argval); + strcpy(iop->ioname,lp->arg->argval); iop->iofile = (IODOC|IORAW); - if(lexd.docword>1) + if(lp->lexd.docword>1) iop->iofile |= IOSTRIP; - shlex.heredoc = iop; - shlex.arg = arg; - lexd.docword = 0; + lp->heredoc = iop; + lp->arg = arg; + lp->lexd.docword = 0; if(offset) - stakset(base,offset); + stkset(stkp,base,offset); else - stakseek(0); + stkseek(stkp,0); } /* @@ -1511,29 +1588,28 @@ static void nested_here(register Lex_t *lp) * if <copy> is non,zero, then the characters are copied to the stack * <state> is the initial lexical state */ -void sh_lexskip(int close, register int copy, int state) +void sh_lexskip(Lex_t *lp,int close, register int copy, int state) { - register Lex_t *lp = (Lex_t*)sh.lex_context; register char *cp; - lexd.nest = close; - lexd.lex_state = state; - lexd.noarg = 1; + lp->lexd.nest = close; + lp->lexd.lex_state = state; + lp->lexd.noarg = 1; if(copy) - fcnotify(lex_advance); + fcnotify(lex_advance,lp); else - lexd.nocopy++; - sh_lex(); - lexd.noarg = 0; + lp->lexd.nocopy++; + sh_lex(lp); + lp->lexd.noarg = 0; if(copy) { - fcnotify(0); - if(!(cp=lexd.first)) + fcnotify(0,lp); + if(!(cp=lp->lexd.first)) cp = fcfirst(); if((copy = fcseek(0)-cp) > 0) - stakwrite(cp,copy); + sfwrite(lp->sh->stk,cp,copy); } else - lexd.nocopy--; + lp->lexd.nocopy--; } #if SHOPT_CRNL @@ -1571,13 +1647,13 @@ static int here_copy(Lex_t *lp,register struct ionod *iop) register const char *state; register int c,n; register char *bufp,*cp; - register Sfio_t *sp=shlex.sh->heredocs, *funlog; + register Sfio_t *sp=lp->sh->heredocs, *funlog; int stripcol=0,stripflg, nsave, special=0; - if(funlog=shlex.sh->funlog) + if(funlog=lp->sh->funlog) { if(fcfill()>0) fcseek(-1); - shlex.sh->funlog = 0; + lp->sh->funlog = 0; } if(iop->iolst) here_copy(lp,iop->iolst); @@ -1621,14 +1697,14 @@ static int here_copy(Lex_t *lp,register struct ionod *iop) } if(n==S_EOF || !(c=fcget())) { - if(!lexd.dolparen && (c=(fcseek(0)-1)-bufp)) + if(!lp->lexd.dolparen && (c=(fcseek(0)-1)-bufp)) { if(n==S_ESC) c--; if((c=sfwrite(sp,bufp,c))>0) iop->iosize += c; } - if((c=lexfill())<=0) + if((c=lexfill(lp))<=0) break; if(n==S_ESC) { @@ -1648,10 +1724,10 @@ static int here_copy(Lex_t *lp,register struct ionod *iop) switch(n) { case S_NL: - shlex.sh->inlineno++; + lp->sh->inlineno++; if((stripcol && c==' ') || (stripflg && c=='\t')) { - if(!lexd.dolparen) + if(!lp->lexd.dolparen) { /* write out line */ n = fcseek(0)-bufp; @@ -1688,13 +1764,13 @@ static int here_copy(Lex_t *lp,register struct ionod *iop) { if(!(c=fcget())) { - if(!lexd.dolparen && (c=cp-bufp)) + if(!lp->lexd.dolparen && (c=cp-bufp)) { if((c=sfwrite(sp,cp=bufp,c))>0) iop->iosize+=c; } nsave = n; - if((c=lexfill())<=0) + if((c=lexfill(lp))<=0) { c = iop->iodelim[n]==0; goto done; @@ -1709,15 +1785,15 @@ static int here_copy(Lex_t *lp,register struct ionod *iop) } #endif /* SHOPT_CRNL */ if(c==NL) - shlex.sh->inlineno++; + lp->sh->inlineno++; if(iop->iodelim[n]==0 && (c==NL||c==RPAREN)) { - if(!lexd.dolparen && (n=cp-bufp)) + if(!lp->lexd.dolparen && (n=cp-bufp)) { if((n=sfwrite(sp,bufp,n))>0) iop->iosize += n; } - shlex.sh->inlineno--; + lp->sh->inlineno--; if(c==RPAREN) fcseek(-1); goto done; @@ -1730,7 +1806,7 @@ static int here_copy(Lex_t *lp,register struct ionod *iop) * was crossed while checking the * delimiter */ - if(!lexd.dolparen && nsave>0) + if(!lp->lexd.dolparen && nsave>0) { if((n=sfwrite(sp,bufp,nsave))>0) iop->iosize += n; @@ -1762,8 +1838,8 @@ static int here_copy(Lex_t *lp,register struct ionod *iop) if(c==NL) { /* new-line joining */ - shlex.sh->inlineno++; - if(!lexd.dolparen && (n=(fcseek(0)-bufp)-n)>0) + lp->sh->inlineno++; + if(!lp->lexd.dolparen && (n=(fcseek(0)-bufp)-n)>0) { if((n=sfwrite(sp,bufp,n))>0) iop->iosize += n; @@ -1783,8 +1859,8 @@ static int here_copy(Lex_t *lp,register struct ionod *iop) n=0; } done: - shlex.sh->funlog = funlog; - if(lexd.dolparen) + lp->sh->funlog = funlog; + if(lp->lexd.dolparen) free((void*)iop); else if(!special) iop->iofile |= IOQUOTE; @@ -1799,9 +1875,9 @@ static char *fmttoken(Lex_t *lp, register int sym, char *tok) if(sym < 0) return((char*)sh_translate(e_lexzerobyte)); if(sym==0) - return(shlex.arg?shlex.arg->argval:"?"); - if(lex.intest && shlex.arg && *shlex.arg->argval) - return(shlex.arg->argval); + return(lp->arg?lp->arg->argval:"?"); + if(lp->lex.intest && lp->arg && *lp->arg->argval) + return(lp->arg->argval); if(sym&SYMRES) { register const Shtable_t *tp=shtab_reserved; @@ -1835,6 +1911,9 @@ static char *fmttoken(Lex_t *lp, register int sym, char *tok) case SYMSHARP: sym = '#'; break; + case SYMSEMI: + sym = ';'; + break; default: sym = 0; } @@ -1848,22 +1927,21 @@ static char *fmttoken(Lex_t *lp, register int sym, char *tok) * print a bad syntax message */ -void sh_syntax(void) +void sh_syntax(Lex_t *lp) { - register Shell_t *shp = sh_getinterp(); + register Shell_t *shp = lp->sh; register const char *cp = sh_translate(e_unexpected); register char *tokstr; - register Lex_t *lp = (Lex_t*)shp->lex_context; - register int tok = shlex.token; + register int tok = lp->token; char tokbuf[3]; Sfio_t *sp; - if((tok==EOFSYM) && shlex.lasttok) + if((tok==EOFSYM) && lp->lasttok) { - tok = shlex.lasttok; + tok = lp->lasttok; cp = sh_translate(e_unmatched); } else - shlex.lastline = shp->inlineno; + lp->lastline = shp->inlineno; tokstr = fmttoken(lp,tok,tokbuf); if((sp=fcfile()) || (shp->infd>=0 && (sp=shp->sftable[shp->infd]))) { @@ -1876,27 +1954,27 @@ void sh_syntax(void) } else fcclose(); - shp->inlineno = shlex.inlineno; - shp->st.firstline = shlex.firstline; + shp->inlineno = lp->inlineno; + shp->st.firstline = lp->firstline; #if KSHELL if(!sh_isstate(SH_INTERACTIVE) && !sh_isstate(SH_PROFILE)) #else if(shp->inlineno!=1) #endif - errormsg(SH_DICT,ERROR_exit(SYNBAD),e_lexsyntax1,shlex.lastline,tokstr,cp); + errormsg(SH_DICT,ERROR_exit(SYNBAD),e_lexsyntax1,lp->lastline,tokstr,cp); else errormsg(SH_DICT,ERROR_exit(SYNBAD),e_lexsyntax2,tokstr,cp); } -static char *stack_shift(register char *sp,char *dp) +static char *stack_shift(Stk_t *stkp, register char *sp,char *dp) { register char *ep; - register int offset = staktell(); - register int left = offset-(sp-stakptr(0)); + register int offset = stktell(stkp); + register int left = offset-(sp-stkptr(stkp,0)); register int shift = (dp+1-sp); offset += shift; - stakseek(offset); - sp = stakptr(offset); + stkseek(stkp,offset); + sp = stkptr(stkp,offset); ep = sp - shift; while(left--) *--sp = *--ep; @@ -1911,7 +1989,7 @@ static char *stack_shift(register char *sp,char *dp) * The result is left on the stak * If mode==2, the each $"" string is printed on standard output */ -struct argnod *sh_endword(int mode) +struct argnod *sh_endword(Shell_t *shp,int mode) { register const char *state = sh_lexstates[ST_NESTED]; register int n; @@ -1920,8 +1998,9 @@ struct argnod *sh_endword(int mode) struct argnod* argp=0; char *ep=0, *xp=0; int bracket=0; - stakputc(0); - sp = stakptr(ARGVAL); + Stk_t *stkp=shp->stk; + sfputc(stkp,0); + sp = stkptr(stkp,ARGVAL); #if SHOPT_MULTIBYTE if(mbwide()) { @@ -1961,10 +2040,10 @@ struct argnod *sh_endword(int mode) switch(n) { case S_EOF: - stakseek(dp-stakptr(0)); + stkseek(stkp,dp-stkptr(stkp,0)); if(mode<=0) { - argp = (struct argnod*)stakfreeze(0); + argp = (struct argnod*)stkfreeze(stkp,0); argp->argflag = ARG_RAW|ARG_QUOTED; } return(argp); @@ -2015,7 +2094,7 @@ struct argnod *sh_endword(int mode) dp = ep+n; if(sp-dp <= 1) { - sp = stack_shift(sp,dp); + sp = stack_shift(stkp,sp,dp); dp = sp-1; ep = dp-n; } @@ -2064,7 +2143,7 @@ struct argnod *sh_endword(int mode) { if(dp>=sp) { - sp = stack_shift(sp,dp+1); + sp = stack_shift(stkp,sp,dp+1); dp = sp-2; } *dp++ = '\\'; @@ -2104,7 +2183,7 @@ struct argnod *sh_endword(int mode) dp[-1] = '\\'; if(dp>=sp) { - sp = stack_shift(sp,dp); + sp = stack_shift(stkp,sp,dp); dp = sp-1; } *dp++ = ']'; @@ -2120,7 +2199,7 @@ struct argnod *sh_endword(int mode) dp[-1] = '\\'; if(dp>=sp) { - sp = stack_shift(sp,dp); + sp = stack_shift(stkp,sp,dp); dp = sp-1; } *dp++ = '['; @@ -2215,7 +2294,7 @@ static int alias_exceptf(Sfio_t *iop,int type,Sfdisc_t *handle) /* if last character is a blank, then next work can be alias */ register int c = fcpeek(-1); if(isblank(c)) - shlex.aliasok = 1; + lp->aliasok = 1; *ap->buf = ap->nextc; ap->nextc = 0; sfsetbuf(iop,ap->buf,1); @@ -2238,11 +2317,11 @@ static void setupalias(Lex_t *lp, const char *string,Namval_t *np) if(ap->np = np) { #if SHOPT_KIA - if(shlex.kiafile) + if(lp->kiafile) { unsigned long r; - r=kiaentity(nv_name(np),-1,'p',0,0,shlex.current,'a',0,""); - sfprintf(shlex.kiatmp,"p;%..64d;p;%..64d;%d;%d;e;\n",shlex.current,r,shlex.sh->inlineno,shlex.sh->inlineno); + r=kiaentity(lp,nv_name(np),-1,'p',0,0,lp->current,'a',0,""); + sfprintf(lp->kiatmp,"p;%..64d;p;%..64d;%d;%d;e;\n",lp->current,r,lp->sh->inlineno,lp->sh->inlineno); } #endif /* SHOPT_KIA */ if((ap->nextc=fcget())==0) @@ -2252,13 +2331,13 @@ static void setupalias(Lex_t *lp, const char *string,Namval_t *np) ap->nextc = 0; iop = sfopen(NIL(Sfio_t*),(char*)string,"s"); sfdisc(iop, &ap->disc); - lexd.nocopy++; + lp->lexd.nocopy++; if(!(base=fcfile())) base = sfopen(NIL(Sfio_t*),fcseek(0),"s"); fcclose(); sfstack(base,iop); fcfopen(base); - lexd.nocopy--; + lp->lexd.nocopy--; } /* @@ -2266,11 +2345,11 @@ static void setupalias(Lex_t *lp, const char *string,Namval_t *np) */ static int stack_grow(Lex_t *lp) { - lexd.lex_max += STACK_ARRAY; - if(lexd.lex_match) - lexd.lex_match = (int*)realloc((char*)lexd.lex_match,sizeof(int)*lexd.lex_max); + lp->lexd.lex_max += STACK_ARRAY; + if(lp->lexd.lex_match) + lp->lexd.lex_match = (int*)realloc((char*)lp->lexd.lex_match,sizeof(int)*lp->lexd.lex_max); else - lexd.lex_match = (int*)malloc(sizeof(int)*STACK_ARRAY); - return(lexd.lex_match!=0); + lp->lexd.lex_match = (int*)malloc(sizeof(int)*STACK_ARRAY); + return(lp->lexd.lex_match!=0); } diff --git a/usr/src/lib/libshell/common/sh/macro.c b/usr/src/lib/libshell/common/sh/macro.c index 8108050b14..7b1ab77b48 100644 --- a/usr/src/lib/libshell/common/sh/macro.c +++ b/usr/src/lib/libshell/common/sh/macro.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -71,11 +71,12 @@ typedef struct _mac_ char arith; /* set for ((...)) */ char let; /* set when expanding let arguments */ char zeros; /* strip leading zeros when set */ + char arrayok; /* $x[] ok for arrays */ + char subcopy; /* set when copying subscript */ + int dotdot; /* set for .. in subscript */ void *nvwalk; /* for name space walking*/ } Mac_t; -#define mac (*((Mac_t*)(sh.mac_context))) - #undef ESCAPE #define ESCAPE '\\' #define isescchar(s) ((s)>S_QUOTE) @@ -95,12 +96,12 @@ typedef struct _mac_ static int substring(const char*, const char*, int[], int); static void copyto(Mac_t*, int, int); -static void comsubst(Mac_t*,int); +static void comsubst(Mac_t*, Shnode_t*, int); static int varsub(Mac_t*); static void mac_copy(Mac_t*,const char*, int); -static void tilde_expand2(int); -static char *sh_tilde(const char*); -static char *special(int); +static void tilde_expand2(Shell_t*,int); +static char *sh_tilde(Shell_t*,const char*); +static char *special(Shell_t *,int); static void endfield(Mac_t*,int); static void mac_error(Namval_t*); static char *mac_getstring(char*); @@ -120,19 +121,19 @@ void *sh_macopen(Shell_t *shp) /* * perform only parameter substitution and catch failures */ -char *sh_mactry(register char *string) +char *sh_mactry(Shell_t *shp,register char *string) { if(string) { int jmp_val; - int savexit = sh.savexit; + int savexit = shp->savexit; struct checkpt buff; sh_pushcontext(&buff,SH_JMPSUB); jmp_val = sigsetjmp(buff.buff,0); if(jmp_val == 0) - string = sh_mactrim(string,0); + string = sh_mactrim(shp,string,0); sh_popcontext(&buff); - sh.savexit = savexit; + shp->savexit = savexit; return(string); } return(""); @@ -145,28 +146,31 @@ char *sh_mactry(register char *string) * yields a single pathname. * If <mode> negative, than expansion rules for assignment are applied. */ -char *sh_mactrim(char *str, register int mode) +char *sh_mactrim(Shell_t *shp, char *str, register int mode) { - register Mac_t *mp = (Mac_t*)sh.mac_context; - Mac_t savemac; + register Mac_t *mp = (Mac_t*)shp->mac_context; + Stk_t *stkp = shp->stk; + Mac_t savemac; savemac = *mp; - stakseek(0); + stkseek(stkp,0); mp->arith = (mode==3); mp->let = 0; - sh.argaddr = 0; + shp->argaddr = 0; mp->pattern = (mode==1||mode==2); mp->patfound = 0; - mp->assign = (mode<0); + mp->assign = 0; + if(mode<0) + mp->assign = -mode; mp->quoted = mp->lit = mp->split = mp->quote = 0; mp->sp = 0; - if(mp->ifsp=nv_getval(nv_scoped(IFSNOD))) + if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD))) mp->ifs = *mp->ifsp; else mp->ifs = ' '; - stakseek(0); + stkseek(stkp,0); fcsopen(str); copyto(mp,0,mp->arith); - str = stakfreeze(1); + str = stkfreeze(stkp,1); if(mode==2) { /* expand only if unique */ @@ -184,23 +188,24 @@ char *sh_mactrim(char *str, register int mode) /* * Perform all the expansions on the argument <argp> */ -int sh_macexpand(register struct argnod *argp, struct argnod **arghead,int flag) +int sh_macexpand(Shell_t* shp, register struct argnod *argp, struct argnod **arghead,int flag) { - register int flags = argp->argflag; - register char *str = argp->argval; - register Mac_t *mp = (Mac_t*)sh.mac_context; - char **saveargaddr = sh.argaddr; - Mac_t savemac; + register int flags = argp->argflag; + register char *str = argp->argval; + register Mac_t *mp = (Mac_t*)shp->mac_context; + char **saveargaddr = shp->argaddr; + Mac_t savemac; + Stk_t *stkp = shp->stk; savemac = *mp; mp->sp = 0; - if(mp->ifsp=nv_getval(nv_scoped(IFSNOD))) + if(mp->ifsp=nv_getval(sh_scoped(shp,IFSNOD))) mp->ifs = *mp->ifsp; else mp->ifs = ' '; - if(flag&ARG_OPTIMIZE) - sh.argaddr = (char**)&argp->argchn.ap; + if((flag&ARG_OPTIMIZE) && !shp->indebug) + shp->argaddr = (char**)&argp->argchn.ap; else - sh.argaddr = 0; + shp->argaddr = 0; mp->arghead = arghead; mp->quoted = mp->lit = mp->quote = 0; mp->arith = ((flag&ARG_ARITH)!=0); @@ -208,6 +213,7 @@ int sh_macexpand(register struct argnod *argp, struct argnod **arghead,int flag) mp->split = !(flag&ARG_ASSIGN); mp->assign = !mp->split; mp->pattern = mp->split && !(flag&ARG_NOGLOB) && !sh_isoption(SH_NOGLOB); + mp->arrayok = mp->arith || (flag&ARG_ARRAYOK); str = argp->argval; fcsopen(str); mp->fields = 0; @@ -215,29 +221,31 @@ int sh_macexpand(register struct argnod *argp, struct argnod **arghead,int flag) { mp->split = 0; mp->pattern = ((flag&ARG_EXP)!=0); - stakseek(0); + stkseek(stkp,0); } else { - stakseek(ARGVAL); - *stakptr(ARGVAL-1) = 0; + stkseek(stkp,ARGVAL); + *stkptr(stkp,ARGVAL-1) = 0; } mp->patfound = 0; + if(mp->pattern) + mp->arrayok = 0; copyto(mp,0,mp->arith); if(!arghead) { - argp->argchn.cp = stakfreeze(1); - if(sh.argaddr) + argp->argchn.cp = stkfreeze(stkp,1); + if(shp->argaddr) argp->argflag |= ARG_MAKE; } else { endfield(mp,mp->quoted); flags = mp->fields; - if(flags==1 && sh.argaddr) + if(flags==1 && shp->argaddr) argp->argchn.ap = *arghead; } - sh.argaddr = saveargaddr; + shp->argaddr = saveargaddr; *mp = savemac; return(flags); } @@ -246,28 +254,30 @@ int sh_macexpand(register struct argnod *argp, struct argnod **arghead,int flag) * Expand here document which is stored in <infile> or <string> * The result is written to <outfile> */ -void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string) +void sh_machere(Shell_t *shp,Sfio_t *infile, Sfio_t *outfile, char *string) { register int c,n; register const char *state = sh_lexstates[ST_QUOTE]; register char *cp; - register Mac_t *mp = (Mac_t*)sh.mac_context; + register Mac_t *mp = (Mac_t*)shp->mac_context; + Lex_t *lp = (Lex_t*)mp->shp->lex_context; Fcin_t save; Mac_t savemac; + Stk_t *stkp = shp->stk; savemac = *mp; - stakseek(0); - sh.argaddr = 0; + stkseek(stkp,0); + shp->argaddr = 0; mp->sp = outfile; mp->split = mp->assign = mp->pattern = mp->patfound = mp->lit = mp->arith = mp->let = 0; mp->quote = 1; - mp->ifsp = nv_getval(nv_scoped(IFSNOD)); + mp->ifsp = nv_getval(sh_scoped(shp,IFSNOD)); mp->ifs = ' '; fcsave(&save); if(infile) fcfopen(infile); else fcsopen(string); - fcnotify(0); + fcnotify(0,lp); cp = fcseek(0); while(1) { @@ -285,7 +295,7 @@ void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string) n=state[*(unsigned char*)cp++]; break; default: - /* use state of alpah character */ + /* use state of alpha character */ n=state['a']; cp += len; } @@ -323,7 +333,7 @@ void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string) sfputc(outfile,ESCAPE); continue; case S_GRAVE: - comsubst(mp,0); + comsubst(mp,(Shnode_t*)0,0); break; case S_DOL: c = fcget(); @@ -336,30 +346,39 @@ void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string) case S_DIG: case S_LBRA: { Fcin_t save2; - int offset = staktell(); + int offset = stktell(stkp); int offset2; - stakputc(c); + sfputc(stkp,c); if(n==S_LBRA) - sh_lexskip(RBRACE,1,ST_BRACE); + { + c = fcget(); + fcseek(-1); + if(sh_lexstates[ST_NORM][c]==S_BREAK) + { + comsubst(mp,(Shnode_t*)0,2); + break; + } + sh_lexskip(lp,RBRACE,1,ST_BRACE); + } else if(n==S_ALP) { while(fcgetc(c),isaname(c)) - stakputc(c); + sfputc(stkp,c); fcseek(-1); } - stakputc(0); - offset2 = staktell(); + sfputc(stkp,0); + offset2 = stktell(stkp); fcsave(&save2); - fcsopen(stakptr(offset)); + fcsopen(stkptr(stkp,offset)); varsub(mp); - if(c=staktell()-offset2) - sfwrite(outfile,(char*)stakptr(offset2),c); + if(c=stktell(stkp)-offset2) + sfwrite(outfile,(char*)stkptr(stkp,offset2),c); fcrestore(&save2); - stakseek(offset); + stkseek(stkp,offset); break; } case S_PAR: - comsubst(mp,1); + comsubst(mp,(Shnode_t*)0,1); break; case S_EOF: if((c=fcfill()) > 0) @@ -379,23 +398,24 @@ void sh_machere(Sfio_t *infile, Sfio_t *outfile, char *string) /* * expand argument but do not trim pattern characters */ -char *sh_macpat(register struct argnod *arg, int flags) +char *sh_macpat(Shell_t *shp,register struct argnod *arg, int flags) { register char *sp = arg->argval; if((arg->argflag&ARG_RAW)) return(sp); + sh_stats(STAT_ARGEXPAND); if(flags&ARG_OPTIMIZE) arg->argchn.ap=0; if(!(sp=arg->argchn.cp)) { - sh_macexpand(arg,NIL(struct argnod**),flags); + sh_macexpand(shp,arg,NIL(struct argnod**),flags|ARG_ARRAYOK); sp = arg->argchn.cp; if(!(flags&ARG_OPTIMIZE) || !(arg->argflag&ARG_MAKE)) arg->argchn.cp = 0; arg->argflag &= ~ARG_MAKE; } else - sh.optcount++; + sh_stats(STAT_ARGHITS); return(sp); } @@ -407,6 +427,7 @@ static void copyto(register Mac_t *mp,int endch, int newquote) register int c,n; register const char *state = sh_lexstates[ST_MACRO]; register char *cp,*first; + Lex_t *lp = (Lex_t*)mp->shp->lex_context; int tilde = -1; int oldquote = mp->quote; int ansi_c = 0; @@ -414,11 +435,12 @@ static void copyto(register Mac_t *mp,int endch, int newquote) int ere = 0; int brace = 0; Sfio_t *sp = mp->sp; + Stk_t *stkp = mp->shp->stk; mp->sp = NIL(Sfio_t*); mp->quote = newquote; first = cp = fcseek(0); if(!mp->quote && *cp=='~') - tilde = staktell(); + tilde = stktell(stkp); /* handle // operator specially */ if(mp->pattern==2 && *cp=='/') cp++; @@ -461,7 +483,7 @@ static void copyto(register Mac_t *mp,int endch, int newquote) /* process ANSI-C escape character */ char *addr= --cp; if(c) - stakwrite(first,c); + sfwrite(stkp,first,c); c = chresc(cp,&addr); cp = addr; first = fcseek(cp-first); @@ -473,13 +495,13 @@ static void copyto(register Mac_t *mp,int endch, int newquote) n = wctomb((char*)mb, c); for(i=0;i<n;i++) - stakputc(mb[i]); + sfputc(stkp,mb[i]); } else #endif /* SHOPT_MULTIBYTE */ - stakputc(c); + sfputc(stkp,c); if(c==ESCAPE && mp->pattern) - stakputc(ESCAPE); + sfputc(stkp,ESCAPE); break; } else if(sh_isoption(SH_BRACEEXPAND) && mp->pattern==4 && (*cp==',' || *cp==LBRACE || *cp==RBRACE || *cp=='.')) @@ -491,9 +513,9 @@ static void copyto(register Mac_t *mp,int endch, int newquote) cp = fcseek(c+2); if(c= cp[-1]) { - stakputc(c); + sfputc(stkp,c); if(c==ESCAPE) - stakputc(ESCAPE); + sfputc(stkp,ESCAPE); } else cp--; @@ -514,12 +536,18 @@ static void copyto(register Mac_t *mp,int endch, int newquote) (n==S_PAT||n==S_ENDCH||n==S_SLASH||n==S_BRACT||*cp=='-')))) { cp += (n!=S_EOF); + if(ere && n==S_ESC && *cp =='\\' && cp[1]=='$') + { + /* convert \\\$ into \$' */ + sfwrite(stkp,first,c+1); + cp = first = fcseek(c+3); + } break; } - if(mp->lit || (mp->quote && !isqescchar(n) && n!=S_ENDCH)) + if(!(ere && *cp=='$') && (mp->lit || (mp->quote && !isqescchar(n) && n!=S_ENDCH))) { /* add \ for file expansion */ - stakwrite(first,c+1); + sfwrite(stkp,first,c+1); first = fcseek(c); break; } @@ -530,7 +558,7 @@ static void copyto(register Mac_t *mp,int endch, int newquote) { /* eliminate \ */ if(c) - stakwrite(first,c); + sfwrite(stkp,first,c); /* check new-line joining */ first = fcseek(c+1); } @@ -544,18 +572,18 @@ static void copyto(register Mac_t *mp,int endch, int newquote) if(mp->split && !mp->quote && endch) mac_copy(mp,first,c); else - stakwrite(first,c); + sfwrite(stkp,first,c); } first = fcseek(c+1); c = mp->pattern; if(n==S_GRAVE) - comsubst(mp,0); + comsubst(mp,(Shnode_t*)0,0); else if((n= *cp)==0 || !varsub(mp)) { if(n=='\'' && !mp->quote) ansi_c = 1; else if(mp->quote || n!='"') - stakputc('$'); + sfputc(stkp,'$'); } cp = first = fcseek(0); if(*cp) @@ -572,12 +600,12 @@ static void copyto(register Mac_t *mp,int endch, int newquote) if(mp->split && !mp->quote && !mp->lit && endch) mac_copy(mp,first,c); else - stakwrite(first,c); + sfwrite(stkp,first,c); } c += (n!=S_EOF); first = fcseek(c); if(tilde>=0) - tilde_expand2(tilde); + tilde_expand2(mp->shp,tilde); goto done; case S_QUOTE: if(mp->lit || mp->arith) @@ -596,7 +624,7 @@ static void copyto(register Mac_t *mp,int endch, int newquote) if(mp->split && endch && !mp->quote && !mp->lit) mac_copy(mp,first,c); else - stakwrite(first,c); + sfwrite(stkp,first,c); } first = fcseek(c+1); if(n==S_LIT) @@ -613,26 +641,28 @@ static void copyto(register Mac_t *mp,int endch, int newquote) mp->quoted++; break; case S_BRACT: - if(mp->arith || ((mp->assign==1 || endch==RBRACT) && + if(mp->arith || (((mp->assign&1) || endch==RBRACT) && !(mp->quote || mp->lit))) { int offset=0,oldpat = mp->pattern; - int oldarith = mp->arith; - stakwrite(first,++c); - if(mp->assign==1 && first[c-2]=='.') - offset = staktell(); + int oldarith = mp->arith, oldsub=mp->subcopy; + sfwrite(stkp,first,++c); + if((mp->assign&1) && first[c-2]=='.') + offset = stktell(stkp); first = fcseek(c); mp->pattern = 4; mp->arith = 0; + mp->subcopy = 0; copyto(mp,RBRACT,0); + mp->subcopy = oldsub; mp->arith = oldarith; mp->pattern = oldpat; - stakputc(RBRACT); + sfputc(stkp,RBRACT); if(offset) { - cp = stakptr(staktell()); - if(sh_checkid(stakptr(offset),cp)!=cp) - stakseek(staktell()-2); + cp = stkptr(stkp,stktell(stkp)); + if(sh_checkid(stkptr(stkp,offset),cp)!=cp) + stkseek(stkp,stktell(stkp)-2); } cp = first = fcseek(0); break; @@ -655,6 +685,17 @@ static void copyto(register Mac_t *mp,int endch, int newquote) --paren; } goto pattern; + case S_COM: + if(mp->pattern==4 && (mp->quote || mp->lit)) + { + if(c) + { + sfwrite(stkp,first,c); + first = fcseek(c); + } + sfputc(stkp,ESCAPE); + } + break; case S_BRACE: if(!(mp->quote || mp->lit)) { @@ -674,15 +715,15 @@ static void copyto(register Mac_t *mp,int endch, int newquote) if(mp->pattern==3) break; if(c) - stakwrite(first,c); + sfwrite(stkp,first,c); first = fcseek(c); - stakputc(ESCAPE); + sfputc(stkp,ESCAPE); break; case S_EQ: if(mp->assign==1) { if(*cp=='~' && !endch && !mp->quote && !mp->lit) - tilde = staktell()+(c+1); + tilde = stktell(stkp)+(c+1); mp->assign = 2; } break; @@ -691,14 +732,14 @@ static void copyto(register Mac_t *mp,int endch, int newquote) if(tilde >=0) { if(c) - stakwrite(first,c); + sfwrite(stkp,first,c); first = fcseek(c); - tilde_expand2(tilde); + tilde_expand2(mp->shp,tilde); tilde = -1; c=0; } if(n==S_COLON && mp->assign==2 && *cp=='~' && endch==0 && !mp->quote &&!mp->lit) - tilde = staktell()+(c+1); + tilde = stktell(stkp)+(c+1); else if(n==S_SLASH && mp->pattern==2) #if 0 goto pattern; @@ -706,17 +747,26 @@ static void copyto(register Mac_t *mp,int endch, int newquote) { if(mp->quote || mp->lit) goto pattern; - stakwrite(first,c+1); + sfwrite(stkp,first,c+1); first = fcseek(c+1); - c = staktell(); - sh_lexskip(RBRACE,0,ST_NESTED); - stakseek(c); + c = stktell(stkp); + sh_lexskip(lp,RBRACE,0,ST_NESTED); + stkseek(stkp,c); cp = fcseek(-1); - stakwrite(first,cp-first); + sfwrite(stkp,first,cp-first); first=cp; } #endif break; + case S_DOT: + if(*cp=='.' && mp->subcopy==1) + { + sfwrite(stkp,first,c); + sfputc(stkp,0); + mp->dotdot = stktell(stkp); + cp = first = fcseek(c+2); + } + break; } } done: @@ -729,26 +779,23 @@ done: */ static void mac_substitute(Mac_t *mp, register char *cp,char *str,register int subexp[],int subsize) { - register int c,n; -#if 0 - register char *first=cp; -#else + register int c,n; register char *first=fcseek(0); - char *ptr; - Mac_t savemac; - n = staktell(); + char *ptr; + Mac_t savemac; + Stk_t *stkp = mp->shp->stk; + n = stktell(stkp); savemac = *mp; mp->pattern = 3; mp->split = 0; fcsopen(cp); copyto(mp,0,0); - stakputc(0); - ptr = cp = strdup(stakptr(n)); - stakseek(n); + sfputc(stkp,0); + ptr = cp = strdup(stkptr(stkp,n)); + stkseek(stkp,n); *mp = savemac; fcsopen(first); first = cp; -#endif while(1) { while((c= *cp++) && c!=ESCAPE); @@ -776,9 +823,7 @@ static void mac_substitute(Mac_t *mp, register char *cp,char *str,register int s } if(n=cp-first-1) mac_copy(mp,first,n); -#if 1 free(ptr); -#endif } #if SHOPT_FILESCAN @@ -844,14 +889,14 @@ static char *getdolarg(Shell_t *shp, int n, int *size) /* * get the prefix after name reference resolution */ -static char *prefix(char *id) +static char *prefix(Shell_t *shp, char *id) { Namval_t *np; register char *cp = strchr(id,'.'); if(cp) { *cp = 0; - np = nv_search(id, sh.var_tree,0); + np = nv_search(id, shp->var_tree,0); *cp = '.'; if(isastchar(cp[1])) cp[1] = 0; @@ -859,7 +904,7 @@ static char *prefix(char *id) { int n; char *sp; - sh.argaddr = 0; + shp->argaddr = 0; while(nv_isref(np)) np = nv_refnode(np); id = (char*)malloc(strlen(cp)+1+(n=strlen(sp=nv_name(np)))+1); @@ -878,22 +923,58 @@ static int subcopy(Mac_t *mp, int flag) { int split = mp->split; int xpattern = mp->pattern; - int loc = staktell(); + int loc = stktell(mp->shp->stk); int xarith = mp->arith; + int arrayok = mp->arrayok; mp->split = 0; mp->arith = 0; mp->pattern = flag?4:0; + mp->arrayok=1; + mp->subcopy++; + mp->dotdot = 0; copyto(mp,RBRACT,0); + mp->subcopy = 0; mp->pattern = xpattern; mp->split = split; mp->arith = xarith; + mp->arrayok = arrayok; return(loc); } +/* + * if name is a discipline function, run the function and put the results + * on the stack so that ${x.foo} behaves like ${ x.foo;} + */ +int sh_macfun(Shell_t *shp, const char *name, int offset) +{ + Namval_t *np, *nq; + np = nv_bfsearch(name,shp->fun_tree,&nq,(char**)0); + if(np) + { + /* treat ${x.foo} as ${x.foo;} */ + Shnode_t *tp; + char buff[sizeof(struct dolnod)+sizeof(char*)]; + struct comnod node; + struct dolnod *dp = (struct dolnod*)buff; + memset(&node,0,sizeof(node)); + memset(&buff,0,sizeof(buff)); + tp = (Shnode_t*)&node; + tp->com.comarg = (struct argnod*)dp; + tp->com.comline = shp->inlineno; + dp->dolnum = 2; + dp->dolval[0] = strdup(name); + stkseek(shp->stk,offset); + comsubst((Mac_t*)shp->mac_context,tp,2); + free(dp->dolval[0]); + return(1); + } + return(0); +} + static int namecount(Mac_t *mp,const char *prefix) { int count = 0; - mp->nvwalk = nv_diropen(prefix); + mp->nvwalk = nv_diropen((Namval_t*)0,prefix); while(nv_dirnext(mp->nvwalk)) count++; nv_dirclose(mp->nvwalk); @@ -905,7 +986,7 @@ static char *nextname(Mac_t *mp,const char *prefix, int len) char *cp; if(len==0) { - mp->nvwalk = nv_diropen(prefix); + mp->nvwalk = nv_diropen((Namval_t*)0,prefix); return((char*)mp->nvwalk); } if(!(cp=nv_dirnext(mp->nvwalk))) @@ -924,10 +1005,12 @@ static int varsub(Mac_t *mp) register char *v,*argp=0; register Namval_t *np = NIL(Namval_t*); register int dolg=0, mode=0; + Lex_t *lp = (Lex_t*)mp->shp->lex_context; Namarr_t *ap=0; int dolmax=0, vsize= -1, offset= -1, nulflg, replen=0, bysub=0; - char idbuff[3], *id = idbuff, *pattern=0, *repstr; - int oldpat=mp->pattern,idnum=0,flag=0,d; + char idbuff[3], *id = idbuff, *pattern=0, *repstr, *arrmax=0; + int addsub=0,oldpat=mp->pattern,idnum=0,flag=0,d; + Stk_t *stkp = mp->shp->stk; retry1: mp->zeros = 0; idbuff[0] = 0; @@ -971,19 +1054,19 @@ retry1: /* FALL THRU */ case S_SPC2: *id = c; - v = special(c); + v = special(mp->shp,c); if(isastchar(c)) { mode = c; #if SHOPT_FILESCAN - if(sh.cur_line) + if(mp->shp->cur_line) { v = getdolarg(&sh,1,(int*)0); dolmax = MAX_ARGN; } else #endif /* SHOPT_FILESCAN */ - dolmax = sh.st.dolc+1; + dolmax = mp->shp->st.dolc+1; dolg = (v!=0); } break; @@ -995,11 +1078,11 @@ retry1: case S_PAR: if(type) goto nosub; - comsubst(mp,1); + comsubst(mp,(Shnode_t*)0,1); return(1); case S_DIG: c -= '0'; - sh.argaddr = 0; + mp->shp->argaddr = 0; if(type) { register int d; @@ -1009,18 +1092,18 @@ retry1: } idnum = c; if(c==0) - v = special(c); + v = special(mp->shp,c); #if SHOPT_FILESCAN - else if(sh.cur_line) + else if(mp->shp->cur_line) { - sh.used_pos = 1; + mp->shp->used_pos = 1; v = getdolarg(&sh,c,&vsize); } #endif /* SHOPT_FILESCAN */ - else if(c <= sh.st.dolc) + else if(c <= mp->shp->st.dolc) { - sh.used_pos = 1; - v = sh.st.dolv[c]; + mp->shp->used_pos = 1; + v = mp->shp->st.dolv[c]; } else v = 0; @@ -1028,16 +1111,16 @@ retry1: case S_ALP: if(c=='.' && type==0) goto nosub; - offset = staktell(); + offset = stktell(stkp); do { np = 0; do - stakputc(c); + sfputc(stkp,c); while(((c=fcget()),(c>0x7f||isaname(c)))||type && c=='.'); - while(c==LBRACT && type) + while(c==LBRACT && (type||mp->arrayok)) { - sh.argaddr=0; + mp->shp->argaddr=0; if((c=fcget(),isastchar(c)) && fcpeek(0)==RBRACT) { if(type==M_VNAME) @@ -1047,9 +1130,9 @@ retry1: c = fcget(); if(c=='.' || c==LBRACT) { - stakputc(LBRACT); - stakputc(mode); - stakputc(RBRACT); + sfputc(stkp,LBRACT); + sfputc(stkp,mode); + sfputc(stkp,RBRACT); } else flag = NV_ARRAY; @@ -1058,23 +1141,43 @@ retry1: else { fcseek(-1); - if(type==M_VNAME) - type = M_SUBNAME; - stakputc(LBRACT); - v = stakptr(subcopy(mp,1)); - stakputc(RBRACT); + c = stktell(stkp); + sfputc(stkp,LBRACT); + v = stkptr(stkp,subcopy(mp,1)); + if(type && mp->dotdot) + { + mode = '@'; + v[-1] = 0; + if(type==M_VNAME) + type = M_SUBNAME; + else if(type==M_SIZE) + goto nosub; + } + else + sfputc(stkp,RBRACT); c = fcget(); + if(c==0 && type==M_VNAME) + type = M_SUBNAME; } } } while(type && c=='.'); if(c==RBRACE && type && fcpeek(-2)=='.') { - stakseek(staktell()-1); - type = M_TREE; + /* ${x.} or ${x..} */ + if(fcpeek(-3) == '.') + { + stkseek(stkp,stktell(stkp)-2); + nv_local = 1; + } + else + { + stkseek(stkp,stktell(stkp)-1); + type = M_TREE; + } } - stakputc(0); - id=stakptr(offset); + sfputc(stkp,0); + id=stkptr(stkp,offset); if(isastchar(c) && type) { if(type==M_VNAME || type==M_SIZE) @@ -1094,20 +1197,48 @@ retry1: if(c=='=' || c=='?' || (c==':' && ((d=fcpeek(0))=='=' || d=='?'))) flag &= ~NV_NOADD; #if SHOPT_FILESCAN - if(sh.cur_line && *id=='R' && strcmp(id,"REPLY")==0) + if(mp->shp->cur_line && *id=='R' && strcmp(id,"REPLY")==0) { - sh.argaddr=0; + mp->shp->argaddr=0; np = REPLYNOD; } else #endif /* SHOPT_FILESCAN */ - if(sh.argaddr) + if(mp->shp->argaddr) flag &= ~NV_NOADD; - np = nv_open(id,sh.var_tree,flag|NV_NOFAIL); + np = nv_open(id,mp->shp->var_tree,flag|NV_NOFAIL); + if((!np || nv_isnull(np)) && type==M_BRACE && c==RBRACE && !(flag&NV_ARRAY)) + { + if(sh_macfun(mp->shp,id,offset)) + { + fcget(); + return(1); + } + } ap = np?nv_arrayptr(np):0; if(type) { - if(ap && isastchar(mode) && !(ap->nelem&ARRAY_SCAN)) + if(mp->dotdot) + { + if(ap) + { + nv_putsub(np,v,ARRAY_SCAN); + v = stkptr(stkp,mp->dotdot); + dolmax =1; + if(array_assoc(ap)) + arrmax = strdup(v); + else if((dolmax = (int)sh_arith(v))<0) + dolmax += array_maxindex(np); + if(type==M_SUBNAME) + bysub = 1; + } + else + { + if((int)sh_arith(v)) + np = 0; + } + } + else if(ap && (isastchar(mode)||type==M_TREE) && !(ap->nelem&ARRAY_SCAN) && type!=M_SIZE) nv_putsub(np,NIL(char*),ARRAY_SCAN); if(!isbracechar(c)) goto nosub; @@ -1116,37 +1247,53 @@ retry1: } else fcseek(-1); - if((type==M_VNAME||type==M_SUBNAME) && sh.argaddr && strcmp(nv_name(np),id)) - sh.argaddr = 0; + if(type<=1 && np && nv_isvtree(np) && mp->pattern==1 && !mp->split) + { + int peek=1,cc=fcget(); + if(type && cc=='}') + { + cc = fcget(); + peek = 2; + } + if(mp->quote && cc=='"') + { + cc = fcget(); + peek++; + } + fcseek(-peek); + if(cc==0) + mp->assign = 1; + } + if((type==M_VNAME||type==M_SUBNAME) && mp->shp->argaddr && strcmp(nv_name(np),id)) + mp->shp->argaddr = 0; c = (type>M_BRACE && isastchar(mode)); - if(np && (!c || !ap)) + if(np && (type==M_TREE || !c || !ap)) { - if(type==M_VNAME) + char *savptr; + c = *((unsigned char*)stkptr(stkp,offset-1)); + savptr = stkfreeze(stkp,0); + if(type==M_VNAME || (type==M_SUBNAME && ap)) { type = M_BRACE; v = nv_name(np); + if(ap && !mp->dotdot && !(ap->nelem&ARRAY_UNDEF)) + addsub = 1; } #ifdef SHOPT_TYPEDEF else if(type==M_TYPE) { -#if 0 Namval_t *nq = nv_type(np); -#else - Namval_t *nq = 0; -#endif type = M_BRACE; if(nq) - v = nv_name(nq); + nv_typename(nq,mp->shp->strbuf); else - { - nv_attribute(np,sh.strbuf,"typeset",1); - v = sfstruse(sh.strbuf); - } + nv_attribute(np,mp->shp->strbuf,"typeset",1); + v = sfstruse(mp->shp->strbuf); } #endif /* SHOPT_TYPEDEF */ #if SHOPT_FILESCAN - else if(sh.cur_line && np==REPLYNOD) - v = sh.cur_line; + else if(mp->shp->cur_line && np==REPLYNOD) + v = mp->shp->cur_line; #endif /* SHOPT_FILESCAN */ else if(type==M_TREE) v = nv_getvtree(np,(Namfun_t*)0); @@ -1154,17 +1301,30 @@ retry1: { v = nv_getval(np); /* special case --- ignore leading zeros */ - if( (mp->arith||mp->let) && (np->nvfun || nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL))) && (offset==0 || !isalnum(*((unsigned char*)stakptr(offset-1))))) + if( (mp->arith||mp->let) && (np->nvfun || nv_isattr(np,(NV_LJUST|NV_RJUST|NV_ZFILL))) && (offset==0 || !isalnum(c))) mp->zeros = 1; } + if(savptr==stakptr(0)) + stkseek(stkp,offset); + else + stkset(stkp,savptr,offset); } else + { v = 0; - stakseek(offset); + if(type==M_VNAME) + { + v = id; + type = M_BRACE; + } + else if(type==M_TYPE) + type = M_BRACE; + } + stkseek(stkp,offset); if(ap) { #if SHOPT_OPTIMIZE - if(sh.argaddr) + if(mp->shp->argaddr) nv_optimize(np); #endif if(isastchar(mode) && array_elem(ap)> !c) @@ -1185,8 +1345,8 @@ retry1: mac_error(np); if(type==M_NAMESCAN || type==M_NAMECOUNT) { - id = prefix(id); - stakseek(offset); + id = prefix(mp->shp,id); + stkseek(stkp,offset); if(type==M_NAMECOUNT) { c = namecount(mp,id); @@ -1222,14 +1382,14 @@ retry1: else if(dolg>0) { #if SHOPT_FILESCAN - if(sh.cur_line) + if(mp->shp->cur_line) { getdolarg(&sh,MAX_ARGN,(int*)0); - c = sh.offsets[0]; + c = mp->shp->offsets[0]; } else #endif /* SHOPT_FILESCAN */ - c = sh.st.dolc; + c = mp->shp->st.dolc; } else if(dolg<0) c = array_elem(ap); @@ -1264,7 +1424,7 @@ retry1: if(c!=RBRACE) { int newops = (c=='#' || c == '%' || c=='/'); - offset = staktell(); + offset = stktell(stkp); if(c=='/' ||c==':' || ((!v || (nulflg && *v==0)) ^ (c=='+'||c=='#'||c=='%'))) { int newquote = mp->quote; @@ -1300,15 +1460,15 @@ retry1: mp->arith = arith; mp->zeros = zeros; /* add null byte */ - stakputc(0); - stakseek(staktell()-1); + sfputc(stkp,0); + stkseek(stkp,stktell(stkp)-1); } else { - sh_lexskip(RBRACE,0,(!newops&&mp->quote)?ST_QUOTE:ST_NESTED); - stakseek(offset); + sh_lexskip(lp,RBRACE,0,(!newops&&mp->quote)?ST_QUOTE:ST_NESTED); + stkseek(stkp,offset); } - argp=stakptr(offset); + argp=stkptr(stkp,offset); } } else @@ -1327,9 +1487,9 @@ retry1: if(type<0 && (type+= dolmax)<0) type = 0; if(type==0) - v = special(dolg=0); + v = special(mp->shp,dolg=0); #if SHOPT_FILESCAN - else if(sh.cur_line) + else if(mp->shp->cur_line) { v = getdolarg(&sh,dolg=type,&vsize); if(!v) @@ -1337,7 +1497,7 @@ retry1: } #endif /* SHOPT_FILESCAN */ else if(type < dolmax) - v = sh.st.dolv[dolg=type]; + v = mp->shp->st.dolv[dolg=type]; else v = 0; } @@ -1426,7 +1586,7 @@ retry1: } if(*ptr) mac_error(np); - stakseek(offset); + stkseek(stkp,offset); argp = 0; } /* check for substring operations */ @@ -1454,7 +1614,7 @@ retry1: if((type=='/' || c=='/') && (repstr = mac_getstring(pattern))) replen = strlen(repstr); if(v || c=='/' && offset>=0) - stakseek(offset); + stkseek(stkp,offset); } /* check for quoted @ */ if(mode=='@' && mp->quote && !v && c!='-') @@ -1463,7 +1623,7 @@ retry2: if(v && (!nulflg || *v ) && c!='+') { register int d = (mode=='@'?' ':mp->ifs); - int match[2*(MATCH_MAX+1)], nmatch, vsize_last; + int match[2*(MATCH_MAX+1)], nmatch, nmatch_prev, vsize_last; char *vlast; while(1) { @@ -1471,12 +1631,14 @@ retry2: v= ""; if(c=='/' || c=='#' || c== '%') { - flag = (type || c=='/')?STR_GROUP|STR_MAXIMAL:STR_GROUP; + flag = (type || c=='/')?(STR_GROUP|STR_MAXIMAL):STR_GROUP; if(c!='/') flag |= STR_LEFT; + nmatch = 0; while(1) { vsize = strlen(v); + nmatch_prev = nmatch; if(c=='%') nmatch=substring(v,pattern,match,flag&STR_MAXIMAL); else @@ -1493,7 +1655,7 @@ retry2: vsize = 0; if(vsize) mac_copy(mp,v,vsize); - if(nmatch && replen>0) + if(nmatch && replen>0 && (match[1] || !nmatch_prev)) mac_substitute(mp,repstr,v,match,nmatch); if(nmatch==0) v += vsize; @@ -1503,7 +1665,11 @@ retry2: { /* avoid infinite loop */ if(nmatch && match[1]==0) + { + nmatch = 0; + mac_copy(mp,v,1); v++; + } continue; } vsize = -1; @@ -1514,14 +1680,39 @@ retry2: } if(vsize) mac_copy(mp,v,vsize>0?vsize:strlen(v)); + if(addsub) + { + sfprintf(mp->shp->strbuf,"[%s]",nv_getsub(np)); + v = sfstruse(mp->shp->strbuf); + mac_copy(mp, v, strlen(v)); + } if(dolg==0 && dolmax==0) break; - if(dolg>=0) + if(mp->dotdot) + { + if(nv_nextsub(np) == 0) + break; + if(bysub) + v = nv_getsub(np); + else + v = nv_getval(np); + if(array_assoc(ap)) + { + if(strcmp(bysub?v:nv_getsub(np),arrmax)>0) + break; + } + else + { + if(nv_aindex(np) > dolmax) + break; + } + } + else if(dolg>=0) { if(++dolg >= dolmax) break; #if SHOPT_FILESCAN - if(sh.cur_line) + if(mp->shp->cur_line) { if(dolmax==MAX_ARGN && isastchar(mode)) break; @@ -1533,7 +1724,7 @@ retry2: } else #endif /* SHOPT_FILESCAN */ - v = sh.st.dolv[dolg]; + v = mp->shp->st.dolv[dolg]; } else if(!np) { @@ -1547,6 +1738,8 @@ retry2: nv_putsub(np,NIL(char*),ARRAY_UNDEF); break; } + if(ap) + ap->nelem |= ARRAY_SCAN; if(nv_nextsub(np) == 0) break; if(bysub) @@ -1566,9 +1759,11 @@ retry2: if(mp->sp) sfputc(mp->sp,d); else - stakputc(d); + sfputc(stkp,d); } } + if(arrmax) + free((void*)arrmax); if(pattern) free((void*)pattern); } @@ -1584,7 +1779,7 @@ retry2: id = ltos(idnum); if(*argp) { - stakputc(0); + sfputc(stkp,0); errormsg(SH_DICT,ERROR_exit(1),"%s: %s",id,argp); } else if(v) @@ -1596,12 +1791,12 @@ retry2: { if(np) { - if(sh.subshell) + if(mp->shp->subshell) np = sh_assignok(np,1); nv_putval(np,argp,0); v = nv_getval(np); nulflg = 0; - stakseek(offset); + stkseek(stkp,offset); goto retry2; } else @@ -1614,9 +1809,8 @@ retry2: { if(nv_isarray(np)) { - sfprintf(sh.strbuf,"%s[%s]\0",nv_name(np),nv_getsub(np)); - id = nv_getsub(np); - id = sfstruse(sh.strbuf); + sfprintf(mp->shp->strbuf,"%s[%s]\0",nv_name(np),nv_getsub(np)); + id = sfstruse(mp->shp->strbuf); } else id = nv_name(np); @@ -1628,6 +1822,12 @@ retry2: nv_close(np); return(1); nosub: + if(type==M_BRACE && sh_lexstates[ST_NORM][c]==S_BREAK) + { + fcseek(-1); + comsubst(mp,(Shnode_t*)0,2); + return(1); + } if(type) mac_error(np); fcseek(-1); @@ -1639,47 +1839,51 @@ nosub: * This routine handles command substitution * <type> is 0 for older `...` version */ -static void comsubst(Mac_t *mp,int type) +static void comsubst(Mac_t *mp,register Shnode_t* t, int type) { Sfdouble_t num; register int c; register char *str; Sfio_t *sp; + Stk_t *stkp = mp->shp->stk; Fcin_t save; - struct slnod *saveslp = sh.st.staklist; + struct slnod *saveslp = mp->shp->st.staklist; struct _mac_ savemac; - int savtop = staktell(); - char lastc, *savptr = stakfreeze(0); + int savtop = stktell(stkp); + char lastc, *savptr = stkfreeze(stkp,0); int was_history = sh_isstate(SH_HISTORY); int was_verbose = sh_isstate(SH_VERBOSE); - int newlines,bufsize; - register Shnode_t *t; + int was_interactive = sh_isstate(SH_INTERACTIVE); + int newlines,bufsize,nextnewlines; Namval_t *np; - sh.argaddr = 0; + mp->shp->argaddr = 0; savemac = *mp; - sh.st.staklist=0; + mp->shp->st.staklist=0; if(type) { sp = 0; fcseek(-1); - t = sh_dolparen(); + if(!t) + t = sh_dolparen((Lex_t*)mp->shp->lex_context); if(t && t->tre.tretyp==TARITH) { - str = t->ar.arexpr->argval; fcsave(&save); - if(!(t->ar.arexpr->argflag&ARG_RAW)) - str = sh_mactrim(str,3); - num = sh_arith(str); + if((t->ar.arexpr->argflag&ARG_RAW)) + num = arith_exec(t->ar.arcomp); + else + num = sh_arith(sh_mactrim(mp->shp,t->ar.arexpr->argval,3)); out_offset: - stakset(savptr,savtop); + stkset(stkp,savptr,savtop); *mp = savemac; - if((Sflong_t)num==num) - sfprintf(sh.strbuf,"%lld",(Sflong_t)num); + if((Sflong_t)num!=num) + sfprintf(mp->shp->strbuf,"%.*Lg",LDBL_DIG,num); + else if(num) + sfprintf(mp->shp->strbuf,"%lld",(Sflong_t)num); else - sfprintf(sh.strbuf,"%.*Lg",LDBL_DIG,num); - str = sfstruse(sh.strbuf); + sfprintf(mp->shp->strbuf,"%Lg",num); + str = sfstruse(mp->shp->strbuf); mac_copy(mp,str,strlen(str)); - sh.st.staklist = saveslp; + mp->shp->st.staklist = saveslp; fcrestore(&save); return; } @@ -1692,30 +1896,31 @@ static void comsubst(Mac_t *mp,int type) { fcgetc(c); if(!(isescchar(sh_lexstates[ST_QUOTE][c]) || - (c=='"' && mp->quote)) || (c=='$' && fcpeek(0)=='\'')) - stakputc(ESCAPE); + (c=='"' && mp->quote))) + sfputc(stkp,ESCAPE); } - stakputc(c); + sfputc(stkp,c); } - c = staktell(); - str=stakfreeze(1); + c = stktell(stkp); + str=stkfreeze(stkp,1); /* disable verbose and don't save in history file */ sh_offstate(SH_HISTORY); sh_offstate(SH_VERBOSE); if(mp->sp) sfsync(mp->sp); /* flush before executing command */ sp = sfnew(NIL(Sfio_t*),str,c,-1,SF_STRING|SF_READ); - c = sh.inlineno; - sh.inlineno = error_info.line+sh.st.firstline; + c = mp->shp->inlineno; + mp->shp->inlineno = error_info.line+mp->shp->st.firstline; t = (Shnode_t*)sh_parse(mp->shp, sp,SH_EOF|SH_NL); - sh.inlineno = c; + mp->shp->inlineno = c; + type = 1; } #if KSHELL if(t) { fcsave(&save); sfclose(sp); - if(t->tre.tretyp==0 && !t->com.comarg) + if(t->tre.tretyp==0 && !t->com.comarg && !t->com.comset) { /* special case $(<file) and $(<#file) */ register int fd; @@ -1726,28 +1931,29 @@ static void comsubst(Mac_t *mp,int type) if((ip=t->tre.treio) && ((ip->iofile&IOLSEEK) || !(ip->iofile&IOUFD)) && (r=sigsetjmp(buff.buff,0))==0) - fd = sh_redirect(ip,3); + fd = sh_redirect(mp->shp,ip,3); else fd = sh_chkopen(e_devnull); sh_popcontext(&buff); if(r==0 && ip && (ip->iofile&IOLSEEK)) { - if(sp=sh.sftable[fd]) + if(sp=mp->shp->sftable[fd]) num = sftell(sp); else num = lseek(fd, (off_t)0, SEEK_CUR); goto out_offset; } sp = sfnew(NIL(Sfio_t*),(char*)malloc(IOBSIZE+1),IOBSIZE,fd,SF_READ|SF_MALLOC); + type = 3; } else - sp = sh_subshell(t,sh_isstate(SH_ERREXIT),1); + sp = sh_subshell(t,sh_isstate(SH_ERREXIT),type); fcrestore(&save); } else sp = sfopen(NIL(Sfio_t*),"","sr"); - sh_freeup(); - sh.st.staklist = saveslp; + sh_freeup(mp->shp); + mp->shp->st.staklist = saveslp; if(was_history) sh_onstate(SH_HISTORY); if(was_verbose) @@ -1756,16 +1962,17 @@ static void comsubst(Mac_t *mp,int type) sp = sfpopen(NIL(Sfio_t*),str,"r"); #endif *mp = savemac; - np = nv_scoped(IFSNOD); - nv_putval(np,mp->ifsp,0); + np = sh_scoped(mp->shp,IFSNOD); + nv_putval(np,mp->ifsp,NV_RDONLY); mp->ifsp = nv_getval(np); - stakset(savptr,savtop); + stkset(stkp,savptr,savtop); newlines = 0; lastc = 0; sfsetbuf(sp,(void*)sp,0); bufsize = sfvalue(sp); /* read command substitution output and put on stack or here-doc */ sfpool(sp, NIL(Sfio_t*), SF_WRITE); + sh_offstate(SH_INTERACTIVE); while((str=(char*)sfreserve(sp,SF_UNBOUND,0)) && (c = sfvalue(sp))>0) { #if SHOPT_CRNL @@ -1790,28 +1997,31 @@ static void comsubst(Mac_t *mp,int type) } if(c) *dp++ = *str++; - *dp = 0; str = buff; c = dp-str; #endif /* SHOPT_CRNL */ + /* delay appending trailing new-lines */ + for(nextnewlines=0; c-->0 && str[c]=='\n'; nextnewlines++); + if(c < 0) + { + newlines += nextnewlines; + continue; + } if(newlines >0) { if(mp->sp) sfnputc(mp->sp,'\n',newlines); - else if(!mp->quote && mp->split && sh.ifstable['\n']) + else if(!mp->quote && mp->split && mp->shp->ifstable['\n']) endfield(mp,0); - else while(newlines--) - stakputc('\n'); - newlines = 0; + else + sfnputc(stkp,'\n',newlines); } else if(lastc) { mac_copy(mp,&lastc,1); lastc = 0; } - /* delay appending trailing new-lines */ - while(str[--c]=='\n') - newlines++; + newlines = nextnewlines; if(++c < bufsize) str[c] = 0; else @@ -1822,14 +2032,19 @@ static void comsubst(Mac_t *mp,int type) } mac_copy(mp,str,c); } - if(--newlines>0 && sh.ifstable['\n']==S_DELIM) + if(was_interactive) + sh_onstate(SH_INTERACTIVE); + if(mp->shp->spid) + job_wait(mp->shp->spid); + if(--newlines>0 && mp->shp->ifstable['\n']==S_DELIM) { if(mp->sp) sfnputc(mp->sp,'\n',newlines); - else if(!mp->quote && mp->split && sh.ifstable['\n']) - endfield(mp,0); - else while(newlines--) - stakputc('\n'); + else if(!mp->quote && mp->split) + while(newlines--) + endfield(mp,1); + else + sfnputc(stkp,'\n',newlines); } if(lastc) mac_copy(mp,&lastc,1); @@ -1844,7 +2059,8 @@ static void mac_copy(register Mac_t *mp,register const char *str, register int s { register char *state; register const char *cp=str; - register int c,n,nopat; + register int c,n,nopat,len; + Stk_t *stkp=mp->shp->stk; nopat = (mp->quote||mp->assign==1||mp->arith); if(mp->zeros) { @@ -1862,6 +2078,14 @@ static void mac_copy(register Mac_t *mp,register const char *str, register int s /* insert \ before file expansion characters */ while(size-->0) { +#if SHOPT_MULTIBYTE + if(mbwide() && (len=mbsize(cp))>1) + { + cp += len; + size -= (len-1); + continue; + } +#endif c = state[n= *(unsigned char*)cp++]; if(nopat&&(c==S_PAT||c==S_ESC||c==S_BRACT||c==S_ENDCH) && mp->pattern!=3) c=1; @@ -1879,18 +2103,18 @@ static void mac_copy(register Mac_t *mp,register const char *str, register int s if(c) { if(c = (cp-1) - str) - stakwrite(str,c); - stakputc(ESCAPE); + sfwrite(stkp,str,c); + sfputc(stkp,ESCAPE); str = cp-1; } } if(c = cp-str) - stakwrite(str,c); + sfwrite(stkp,str,c); } else if(!mp->quote && mp->split && (mp->ifs||mp->pattern)) { /* split words at ifs characters */ - state = sh.ifstable; + state = mp->shp->ifstable; if(mp->pattern) { char *sp = "&|()"; @@ -1910,11 +2134,21 @@ static void mac_copy(register Mac_t *mp,register const char *str, register int s } while(size-->0) { - if((n=state[c= *(unsigned char*)cp++])==S_ESC || n==S_EPAT) + n=state[c= *(unsigned char*)cp++]; +#if SHOPT_MULTIBYTE + if(mbwide() && n!=S_MBYTE && (len=mbsize(cp-1))>1) + { + sfwrite(stkp,cp-1, len); + cp += --len; + size -= len; + continue; + } +#endif + if(n==S_ESC || n==S_EPAT) { /* don't allow extended patterns in this case */ mp->patfound = mp->pattern; - stakputc(ESCAPE); + sfputc(stkp,ESCAPE); } else if(n==S_PAT) mp->patfound = mp->pattern; @@ -1963,7 +2197,7 @@ static void mac_copy(register Mac_t *mp,register const char *str, register int s continue; } - stakputc(c); + sfputc(stkp,c); } if(mp->pattern) { @@ -1979,12 +2213,12 @@ static void mac_copy(register Mac_t *mp,register const char *str, register int s if(state[c]==S_PAT) state[c] = 0; } - if(sh.ifstable[ESCAPE]==S_ESC) - sh.ifstable[ESCAPE] = 0; + if(mp->shp->ifstable[ESCAPE]==S_ESC) + mp->shp->ifstable[ESCAPE] = 0; } } else - stakwrite(str,size); + sfwrite(stkp,str,size); } /* @@ -1994,16 +2228,17 @@ static void mac_copy(register Mac_t *mp,register const char *str, register int s */ static void endfield(register Mac_t *mp,int split) { - register struct argnod *argp; - register int count=0; - if(staktell() > ARGVAL || split) + register struct argnod *argp; + register int count=0; + Stk_t *stkp = mp->shp->stk; + if(stktell(stkp) > ARGVAL || split) { - argp = (struct argnod*)stakfreeze(1); + argp = (struct argnod*)stkfreeze(stkp,1); argp->argnxt.cp = 0; argp->argflag = 0; if(mp->patfound) { - sh.argaddr = 0; + mp->shp->argaddr = 0; #if SHOPT_BRACEPAT count = path_generate(argp,mp->arghead); #else @@ -2028,7 +2263,7 @@ static void endfield(register Mac_t *mp,int split) if(mp->assign || sh_isoption(SH_NOGLOB)) argp->argflag |= ARG_RAW|ARG_EXP; } - stakseek(ARGVAL); + stkseek(stkp,ARGVAL); } mp->quoted = mp->quote; } @@ -2129,9 +2364,9 @@ static int charlen(const char *string,int len) */ static int sh_btilde(int argc, char *argv[], void *context) { - char *cp = sh_tilde(argv[1]); + Shell_t *shp = ((Shbltin_t*)context)->shp; + char *cp = sh_tilde(shp,argv[1]); NOT_USED(argc); - NOT_USED(context); if(!cp) cp = argv[1]; sfputr(sfstdout, cp, '\n'); @@ -2141,18 +2376,19 @@ static int sh_btilde(int argc, char *argv[], void *context) /* * <offset> is byte offset for beginning of tilde string */ -static void tilde_expand2(register int offset) +static void tilde_expand2(Shell_t *shp, register int offset) { - char shtilde[10], *av[3], *ptr=stakfreeze(1); + char shtilde[10], *av[3], *ptr=stkfreeze(shp->stk,1); Sfio_t *iop, *save=sfstdout; Namval_t *np; static int beenhere=0; strcpy(shtilde,".sh.tilde"); - np = nv_open(shtilde,sh.fun_tree, NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOFAIL); + np = nv_open(shtilde,shp->fun_tree, NV_VARNAME|NV_NOARRAY|NV_NOASSIGN|NV_NOFAIL); if(np && !beenhere) { beenhere = 1; sh_addbuiltin(shtilde,sh_btilde,0); + nv_onattr(np,NV_EXPORT); } av[0] = ".sh.tilde"; av[1] = &ptr[offset]; @@ -2165,7 +2401,7 @@ static void tilde_expand2(register int offset) else sh_btilde(2, av, &sh); sfstdout = save; - stakset(ptr, offset); + stkset(shp->stk,ptr, offset); sfseek(iop,(Sfoff_t)0,SEEK_SET); sfset(iop,SF_READ,1); if(ptr = sfreserve(iop, SF_UNBOUND, -1)) @@ -2176,10 +2412,10 @@ static void tilde_expand2(register int offset) if(n==1 && fcpeek(0)=='/' && ptr[n-1]) n--; if(n) - stakwrite(ptr,n); + sfwrite(shp->stk,ptr,n); } else - stakputs(av[1]); + sfputr(shp->stk,av[1],0); sfclose(iop); } @@ -2192,7 +2428,7 @@ static void tilde_expand2(register int offset) * If string doesn't start with ~ or ~... not found then 0 returned. */ -static char *sh_tilde(register const char *string) +static char *sh_tilde(Shell_t *shp,register const char *string) { register char *cp; register int c; @@ -2203,16 +2439,16 @@ static char *sh_tilde(register const char *string) return(NIL(char*)); if((c = *string)==0) { - if(!(cp=nv_getval(nv_scoped(HOME)))) + if(!(cp=nv_getval(sh_scoped(shp,HOME)))) cp = getlogin(); return(cp); } if((c=='-' || c=='+') && string[1]==0) { if(c=='+') - cp = nv_getval(nv_scoped(PWDNOD)); + cp = nv_getval(sh_scoped(shp,PWDNOD)); else - cp = nv_getval(nv_scoped(OLDPWDNOD)); + cp = nv_getval(sh_scoped(shp,OLDPWDNOD)); return(cp); } if(logins_tree && (np=nv_search(string,logins_tree,0))) @@ -2229,40 +2465,40 @@ static char *sh_tilde(register const char *string) /* * return values for special macros */ -static char *special(register int c) +static char *special(Shell_t *shp,register int c) { register Namval_t *np; if(c!='$') - sh.argaddr = 0; + shp->argaddr = 0; switch(c) { case '@': case '*': - return(sh.st.dolc>0?sh.st.dolv[1]:NIL(char*)); + return(shp->st.dolc>0?shp->st.dolv[1]:NIL(char*)); case '#': #if SHOPT_FILESCAN - if(sh.cur_line) + if(shp->cur_line) { - getdolarg(&sh,MAX_ARGN,(int*)0); - return(ltos(sh.offsets[0])); + getdolarg(shp,MAX_ARGN,(int*)0); + return(ltos(shp->offsets[0])); } #endif /* SHOPT_FILESCAN */ - return(ltos(sh.st.dolc)); + return(ltos(shp->st.dolc)); case '!': - if(sh.bckpid) - return(ltos(sh.bckpid)); + if(shp->bckpid) + return(ltos(shp->bckpid)); break; case '$': if(nv_isnull(SH_DOLLARNOD)) - return(ltos(sh.pid)); + return(ltos(shp->pid)); return(nv_getval(SH_DOLLARNOD)); case '-': - return(sh_argdolminus()); + return(sh_argdolminus(shp->arg_context)); case '?': - return(ltos(sh.savexit)); + return(ltos(shp->savexit)); case 0: - if(sh_isstate(SH_PROFILE) || !error_info.id || ((np=nv_search(error_info.id,sh.bltin_tree,0)) && nv_isattr(np,BLT_SPC))) - return(sh.shname); + if(sh_isstate(SH_PROFILE) || !error_info.id || ((np=nv_search(error_info.id,shp->bltin_tree,0)) && nv_isattr(np,BLT_SPC))) + return(shp->shname); else return(error_info.id); } @@ -2281,21 +2517,29 @@ static void mac_error(Namval_t *np) /* * Given pattern/string, replace / with 0 and return pointer to string - * \ characters are stripped from string. + * \ characters are stripped from string. The \ are stripped in the + * replacement string unless followed by a digit or \. */ static char *mac_getstring(char *pattern) { - register char *cp = pattern; - register int c; + register char *cp=pattern, *rep=0, *dp; + register int c; while(c = *cp++) { - if(c==ESCAPE) - cp++; - else if(c=='/') + if(c==ESCAPE && (!rep || (*cp && strchr("&|()[]*?",*cp)))) + { + c = *cp++; + } + else if(!rep && c=='/') { cp[-1] = 0; - return(cp); + rep = dp = cp; + continue; } + if(rep) + *dp++ = c; } - return(NIL(char*)); + if(rep) + *dp = 0; + return(rep); } diff --git a/usr/src/lib/libshell/common/sh/main.c b/usr/src/lib/libshell/common/sh/main.c index 0b2f4ac875..793e978b56 100644 --- a/usr/src/lib/libshell/common/sh/main.c +++ b/usr/src/lib/libshell/common/sh/main.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -130,7 +130,7 @@ int sh_source(Shell_t *shp, Sfio_t *iop, const char *file) #define REMOTE(m) !(m) #endif -int sh_main(int ac, char *av[], void (*userinit)(int)) +int sh_main(int ac, char *av[], Shinit_f userinit) { register char *name; register int fdin; @@ -140,7 +140,7 @@ int sh_main(int ac, char *av[], void (*userinit)(int)) int i, rshflag; /* set for restricted shell */ char *command; #ifdef _lib_sigvec - /* This is to clear mask that my be left on by rlogin */ + /* This is to clear mask that may be left on by rlogin */ clearsigmask(SIGALRM); clearsigmask(SIGHUP); clearsigmask(SIGCHLD); @@ -160,8 +160,6 @@ int sh_main(int ac, char *av[], void (*userinit)(int)) { /* begin script execution here */ sh_reinit((char**)0); - if(rshflag) - sh_onoption(SH_RESTRICTED); } shp->fn_depth = shp->dot_depth = 0; command = error_info.id; @@ -184,10 +182,11 @@ int sh_main(int ac, char *av[], void (*userinit)(int)) if(shp->login_sh >= 2) sh_onoption(SH_LOGIN_SHELL); /* decide whether shell is interactive */ - if(!sh_isoption(SH_TFLAG) && !sh_isoption(SH_CFLAG) && sh_isoption(SH_SFLAG) && - tty_check(0) && tty_check(ERRIO)) - { + if(!sh_isoption(SH_INTERACTIVE) && !sh_isoption(SH_TFLAG) && !sh_isoption(SH_CFLAG) && + sh_isoption(SH_SFLAG) && tty_check(0) && tty_check(ERRIO)) sh_onoption(SH_INTERACTIVE); + if(sh_isoption(SH_INTERACTIVE)) + { sh_onoption(SH_BGNICE); sh_onoption(SH_RC); } @@ -197,8 +196,8 @@ int sh_main(int ac, char *av[], void (*userinit)(int)) #endif )) sh_onoption(SH_RC); - for(i=0; i<elementsof(sh.offoptions.v); i++) - sh.options.v[i] &= ~sh.offoptions.v[i]; + for(i=0; i<elementsof(shp->offoptions.v); i++) + shp->options.v[i] &= ~shp->offoptions.v[i]; if(sh_isoption(SH_INTERACTIVE)) { #ifdef SIGXCPU @@ -209,15 +208,15 @@ int sh_main(int ac, char *av[], void (*userinit)(int)) #endif /* SIGXFSZ */ sh_onoption(SH_MONITOR); } - job_init(sh_isoption(SH_LOGIN_SHELL)); - if(sh_isoption(SH_LOGIN_SHELL) && !sh_isoption(SH_NOPROFILE)) + job_init(shp,sh_isoption(SH_LOGIN_SHELL)); + if(sh_isoption(SH_LOGIN_SHELL)) { /* system profile */ sh_source(shp, iop, e_sysprofile); if(!sh_isoption(SH_NOUSRPROFILE) && !sh_isoption(SH_PRIVILEGED)) { char **files = shp->login_files; - while ((name = *files++) && !sh_source(shp, iop, sh_mactry(name))); + while ((name = *files++) && !sh_source(shp, iop, sh_mactry(shp,name))); } } /* make sure PWD is set up correctly */ @@ -232,15 +231,22 @@ int sh_main(int ac, char *av[], void (*userinit)(int)) #if SHOPT_SYSRC sh_source(shp, iop, e_bash_sysrc); #endif - sh_source(shp, iop, shp->rcfile ? shp->rcfile : sh_mactry((char*)e_bash_rc)); + sh_source(shp, iop, shp->rcfile ? shp->rcfile : sh_mactry(shp,(char*)e_bash_rc)); } else #endif { + if(name = sh_mactry(shp,nv_getval(ENVNOD))) + name = *name ? strdup(name) : (char*)0; #if SHOPT_SYSRC - sh_source(shp, iop, e_sysrc); + if(!strmatch(name, "?(.)/./*")) + sh_source(shp, iop, e_sysrc); #endif - sh_source(shp, iop, sh_mactry(nv_getval(ENVNOD))); + if(name) + { + sh_source(shp, iop, name); + free(name); + } } } else if(sh_isoption(SH_INTERACTIVE) && sh_isoption(SH_PRIVILEGED)) @@ -272,7 +278,7 @@ int sh_main(int ac, char *av[], void (*userinit)(int)) int type; fdin = (int)strtol(name+8, (char**)0, 10); if(fstat(fdin,&statb)<0) - errormsg(SH_DICT,ERROR_system(1),e_open,error_info.id); + errormsg(SH_DICT,ERROR_system(1),e_open,name); #if !_WINIX /* * try to undo effect of solaris 2.5+ @@ -356,7 +362,7 @@ int sh_main(int ac, char *av[], void (*userinit)(int)) sh_onstate(SH_INTERACTIVE); nv_putval(IFSNOD,(char*)e_sptbnl,NV_RDONLY); exfile(shp,iop,fdin); - sh_done(0); + sh_done(shp,0); /* NOTREACHED */ return(0); } @@ -389,7 +395,7 @@ static void exfile(register Shell_t *shp, register Sfio_t *iop,register int fno) } fcntl(fno,F_SETFD,FD_CLOEXEC); shp->fdstatus[fno] |= IOCLEX; - iop = sh_iostream(fno); + iop = sh_iostream((void*)shp,fno); } else iop = sfstdin; @@ -402,7 +408,7 @@ static void exfile(register Shell_t *shp, register Sfio_t *iop,register int fno) if(nv_isnull(PS1NOD)) nv_putval(PS1NOD,(shp->euserid?e_stdprompt:e_supprompt),NV_RDONLY); sh_sigdone(); - if(sh_histinit()) + if(sh_histinit((void*)shp)) sh_onoption(SH_HISTORY); } else @@ -423,14 +429,14 @@ static void exfile(register Shell_t *shp, register Sfio_t *iop,register int fno) if(jmpval) { Sfio_t *top; - sh_iorestore(0,jmpval); + sh_iorestore((void*)shp,0,jmpval); hist_flush(shp->hist_ptr); sfsync(shp->outpool); shp->st.execbrk = shp->st.breakcnt = 0; /* check for return from profile or env file */ if(sh_isstate(SH_PROFILE) && (jmpval==SH_JMPFUN || jmpval==SH_JMPEXIT)) goto done; - if(!sh_isoption(SH_INTERACTIVE) || sh_isstate(SH_FORKED) || (jmpval > SH_JMPERREXIT && job_close() >=0)) + if(!sh_isoption(SH_INTERACTIVE) || sh_isstate(SH_FORKED) || (jmpval > SH_JMPERREXIT && job_close(shp) >=0)) { sh_offstate(SH_INTERACTIVE); sh_offstate(SH_MONITOR); @@ -466,7 +472,7 @@ static void exfile(register Shell_t *shp, register Sfio_t *iop,register int fno) while(1) { shp->nextprompt = 1; - sh_freeup(); + sh_freeup(shp); stakset(NIL(char*),0); exitset(); sh_offstate(SH_STOPOK); @@ -540,7 +546,7 @@ static void exfile(register Shell_t *shp, register Sfio_t *iop,register int fno) errormsg(SH_DICT,0,e_logout); continue; } - else if(job_close()<0) + else if(job_close(shp)<0) continue; } if(errno==0 && sferror(iop) && --maxtry>0) @@ -596,12 +602,12 @@ done: if(sh_isstate(SH_INTERACTIVE)) { sfputc(sfstderr,'\n'); - job_close(); + job_close(shp); } if(jmpval == SH_JMPSCRIPT) siglongjmp(*shp->jmplist,jmpval); else if(jmpval == SH_JMPEXIT) - sh_done(0); + sh_done(shp,0); if(fno>0) sh_close(fno); if(shp->st.filename) @@ -662,7 +668,7 @@ static void chkmail(Shell_t *shp, char *files) /* save and restore $_ */ char *save = shp->lastarg; shp->lastarg = cp; - errormsg(SH_DICT,0,sh_mactry(qp?qp+1:(char*)e_mailmsg)); + errormsg(SH_DICT,0,sh_mactry(shp,qp?qp+1:(char*)e_mailmsg)); shp->lastarg = save; } lastmail = statb; diff --git a/usr/src/lib/libshell/common/sh/name.c b/usr/src/lib/libshell/common/sh/name.c index b9d5cbe9eb..bbdeeb83dd 100644 --- a/usr/src/lib/libshell/common/sh/name.c +++ b/usr/src/lib/libshell/common/sh/name.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -34,6 +34,8 @@ #include "FEATURE/externs" #include "streval.h" +#define NVCACHE 8 /* must be a power of 2 */ +#define Empty ((char*)(e_sptbnl+3)) static char *savesub = 0; #if !_lib_pathnative && _lib_uwin_path @@ -55,16 +57,50 @@ static void attstore(Namval_t*,void*); static void pushnam(Namval_t*,void*); static char *staknam(Namval_t*, char*); #endif -static void ltou(const char*,char*); +static void ltou(char*); +static void utol(char*); static void rightjust(char*, int, int); +static char *lastdot(char*, int); struct adata { - char **argnam; - int attsize; - char *attval; + Shell_t *sh; + Namval_t *tp; + char **argnam; + int attsize; + char *attval; }; +#if SHOPT_TYPEDEF + struct sh_type + { + void *previous; + Namval_t **nodes; + Namval_t *rp; + short numnodes; + short maxnodes; + }; +#endif /*SHOPT_TYPEDEF */ + +#if NVCACHE + struct Namcache + { + struct Cache_entry + { + Dt_t *root; + char *name; + Namval_t *np; + Namval_t *last_table; + int flags; + short size; + short len; + } entries[NVCACHE]; + short index; + short ok; + }; + static struct Namcache nvcache; +#endif + char nv_local = 0; #ifndef _ENV_H static void(*nullscan)(Namval_t*,void*); @@ -134,7 +170,7 @@ void nv_outname(Sfio_t *out, char *name, int len) break; sfwrite(out,cp,++sp-cp); stakseek(offset); - for(; c= *sp; sp++) + while(c= *sp++) { if(c==']') break; @@ -152,7 +188,7 @@ void nv_outname(Sfio_t *out, char *name, int len) sfputc(out,']'); return; } - cp = sp; + cp = sp-1; } if(*cp) { @@ -164,56 +200,156 @@ void nv_outname(Sfio_t *out, char *name, int len) stakseek(offset); } +#if SHOPT_TYPEDEF +Namval_t *nv_addnode(Namval_t* np, int remove) +{ + register struct sh_type *sp = (struct sh_type*)sh.mktype; + register int i; + register char *name=0; + if(sp->numnodes==0 && !nv_isnull(np) && sh.last_table) + { + /* could be an redefine */ + Dt_t *root = nv_dict(sh.last_table); + sp->rp = np; + nv_delete(np,root,NV_NOFREE); + np = nv_search(sp->rp->nvname,root,NV_ADD); + } + if(sp->numnodes && memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1)) + { + name = (sp->nodes[0])->nvname; + i = strlen(name); + if(memcmp(np->nvname,name,i)) + return(np); + } + if(sp->rp && sp->numnodes) + { + /* check for a redefine */ + if(name && np->nvname[i]=='.' && np->nvname[i+1]=='_' && np->nvname[i+2]==0) + sp->rp = 0; + else + { + Dt_t *root = nv_dict(sh.last_table); + nv_delete(sp->nodes[0],root,NV_NOFREE); + dtinsert(root,sp->rp); + errormsg(SH_DICT,ERROR_exit(1),e_redef,sp->nodes[0]->nvname); + } + } + for(i=0; i < sp->numnodes; i++) + { + if(np == sp->nodes[i]) + { + if(remove) + { + while(++i < sp->numnodes) + sp->nodes[i-1] = sp->nodes[i]; + sp->numnodes--; + } + return(np); + } + } + if(remove) + return(np); + if(sp->numnodes==sp->maxnodes) + { + sp->maxnodes += 20; + sp->nodes = (Namval_t**)realloc(sp->nodes,sizeof(Namval_t*)*sp->maxnodes); + } + sp->nodes[sp->numnodes++] = np; + return(np); +} +#endif /* SHOPT_TYPEDEF */ + +/* + * given a list of assignments, determine <name> is on the list + returns a pointer to the argnod on the list or NULL + */ +struct argnod *nv_onlist(struct argnod *arg, const char *name) +{ + char *cp; + int len = strlen(name); + for(;arg; arg=arg->argnxt.ap) + { + if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE))) + cp = ((struct fornod*)arg->argchn.ap)->fornam; + else + cp = arg->argval; + if(memcmp(cp,name,len)==0 && (cp[len]==0 || cp[len]=='=')) + return(arg); + } + return(0); +} + /* * Perform parameter assignment for a linked list of parameters * <flags> contains attributes for the parameters */ void nv_setlist(register struct argnod *arg,register int flags) { + Shell_t *shp = &sh; register char *cp; - register Namval_t *np; - char *trap=sh.st.trap[SH_DEBUGTRAP]; + register Namval_t *np, *mp; + char *trap=shp->st.trap[SH_DEBUGTRAP]; + char *prefix = shp->prefix; int traceon = (sh_isoption(SH_XTRACE)!=0); int array = (flags&(NV_ARRAY|NV_IARRAY)); - flags &= ~(NV_TYPE|NV_ARRAY); + Namarr_t *ap; + Namval_t node; + struct Namref nr; +#if SHOPT_TYPEDEF + int maketype = flags&NV_TYPE; + struct sh_type shtp; + if(maketype) + { + shtp.previous = shp->mktype; + shp->mktype=(void*)&shtp; + shtp.numnodes=0; + shtp.maxnodes = 20; + shtp.rp = 0; + shtp.nodes =(Namval_t**)malloc(shtp.maxnodes*sizeof(Namval_t*)); + } +#endif /* SHOPT_TYPEDEF*/ + flags &= ~(NV_TYPE|NV_ARRAY|NV_IARRAY); if(sh_isoption(SH_ALLEXPORT)) flags |= NV_EXPORT; - if(sh.prefix) + if(shp->prefix) { flags &= ~(NV_IDENT|NV_EXPORT); flags |= NV_VARNAME; } for(;arg; arg=arg->argnxt.ap) { - sh.used_pos = 0; + shp->used_pos = 0; if(arg->argflag&ARG_MAC) - cp = sh_mactrim(arg->argval,-1); + { + shp->prefix = 0; + cp = sh_mactrim(shp,arg->argval,(flags&NV_NOREF)?-3:-1); + shp->prefix = prefix; + } else { - Namval_t *mp; stakseek(0); - if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED))) + if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE))) { int flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN); + int sub=0; struct fornod *fp=(struct fornod*)arg->argchn.ap; register Shnode_t *tp=fp->fortre; - char *prefix = sh.prefix; - flag |= (flags&NV_NOSCOPE); + flag |= (flags&(NV_NOSCOPE|NV_STATIC)); if(arg->argflag&ARG_QUOTED) - cp = sh_mactrim(fp->fornam,-1); + cp = sh_mactrim(shp,fp->fornam,-1); else cp = fp->fornam; - error_info.line = fp->fortyp-sh.st.firstline; - if(sh.fn_depth && (Namval_t*)tp->com.comnamp==SYSTYPESET) + error_info.line = fp->fortyp-shp->st.firstline; + if(shp->fn_depth && (Namval_t*)tp->com.comnamp==SYSTYPESET) flag |= NV_NOSCOPE; if(prefix && tp->com.comset && *cp=='[') { - sh.prefix = 0; - np = nv_open(prefix,sh.var_tree,flag); - sh.prefix = prefix; + shp->prefix = 0; + np = nv_open(prefix,shp->var_tree,flag); + shp->prefix = prefix; if(np) { - if(!nv_isarray(np)) + if(nv_isvtree(np) && !nv_isarray(np)) { stakputc('.'); stakputs(cp); @@ -222,10 +358,16 @@ void nv_setlist(register struct argnod *arg,register int flags) nv_close(np); } } - np = nv_open(cp,sh.var_tree,flag); + np = nv_open(cp,shp->var_tree,flag|NV_ASSIGN); + if((flags&NV_STATIC) && !nv_isnull(np)) +#if SHOPT_TYPEDEF + goto check_type; +#else + continue; +#endif /* SHOPT_TYPEDEF */ if(array) { - if(!(flags&NV_APPEND)) + if(!(arg->argflag&ARG_APPEND)) nv_unset(np); if(array&NV_ARRAY) { @@ -235,15 +377,31 @@ void nv_setlist(register struct argnod *arg,register int flags) { nv_onattr(np,NV_ARRAY); } + if(tp->tre.tretyp!=TLST && !tp->com.comset && !tp->com.comarg) +#if SHOPT_TYPEDEF + goto check_type; +#else + continue; +#endif /* SHOPT_TYPEDEF */ } /* check for array assignment */ if(tp->tre.tretyp!=TLST && tp->com.comarg && !tp->com.comset && !((mp=tp->com.comnamp) && nv_isattr(mp,BLT_DCL))) { int argc; - char **argv = sh_argbuild(&argc,&tp->com,0); + Dt_t *last_root = shp->last_root; + char **argv = sh_argbuild(shp,&argc,&tp->com,0); + shp->last_root = last_root; +#if SHOPT_TYPEDEF + if(shp->mktype && shp->dot_depth==0 && np==((struct sh_type*)shp->mktype)->nodes[0]) + { + shp->mktype = 0; + errormsg(SH_DICT,ERROR_exit(1),"%s: not a known type name",argv[0]); + } +#endif /* SHOPT_TYPEDEF */ if(!(arg->argflag&ARG_APPEND)) { - nv_unset(np); + if(!nv_isarray(np) || ((ap=nv_arrayptr(np)) && (ap->nelem&ARRAY_MASK))) + nv_unset(np); } nv_setvec(np,(arg->argflag&ARG_APPEND),argc,argv); if(traceon || trap) @@ -253,7 +411,7 @@ void nv_setlist(register struct argnod *arg,register int flags) if(arg->argflag&ARG_APPEND) n = '+'; if(trap) - sh_debug(trap,name,(char*)0,argv,(arg->argflag&ARG_APPEND)|ARG_ASSIGN); + sh_debug(shp,trap,name,(char*)0,argv,(arg->argflag&ARG_APPEND)|ARG_ASSIGN); if(traceon) { sh_trace(NIL(char**),0); @@ -264,51 +422,122 @@ void nv_setlist(register struct argnod *arg,register int flags) sfwrite(sfstderr,")\n",2); } } +#if SHOPT_TYPEDEF + goto check_type; +#else continue; +#endif /* SHOPT_TYPEDEF */ } + if((tp->tre.tretyp&COMMSK)==TFUN) + goto skip; if(tp->tre.tretyp==TLST || !tp->com.comset || tp->com.comset->argval[0]!='[') { + if(tp->tre.tretyp!=TLST && !tp->com.comnamp && tp->com.comset && tp->com.comset->argval[0]==0 && tp->com.comset->argchn.ap) + { + if(prefix) + cp = stakcopy(nv_name(np)); + shp->prefix = cp; + if(tp->com.comset->argval[1]=='[') + { + if((arg->argflag&ARG_APPEND) && (!nv_isarray(np) || (nv_aindex(np)>=0))) + nv_unset(np); + if(!(array&NV_IARRAY) && !(tp->com.comset->argflag&ARG_MESSAGE)) + nv_setarray(np,nv_associative); + } + nv_setlist(tp->com.comset,flags); + shp->prefix = prefix; + if(tp->com.comset->argval[1]!='[') + nv_setvtree(np); + nv_close(np); +#if SHOPT_TYPEDEF + goto check_type; +#else + continue; +#endif /* SHOPT_TYPEDEF */ + } if(*cp!='.' && *cp!='[' && strchr(cp,'[')) { nv_close(np); - np = nv_open(cp,sh.var_tree,flag); + np = nv_open(cp,shp->var_tree,flag); } - if((arg->argflag&ARG_APPEND) && !nv_isarray(np)) - nv_unset(np); - } - else - { - if(sh_isoption(SH_BASH) || (array&NV_IARRAY)) + if(arg->argflag&ARG_APPEND) { - if(!(arg->argflag&ARG_APPEND)) - nv_unset(np); + if(nv_isarray(np)) + { + if((sub=nv_aimax(np)) < 0 && nv_arrayptr(np)) + errormsg(SH_DICT,ERROR_exit(1),e_badappend,nv_name(np)); + if(sub>=0) + sub++; + } + if(!nv_isnull(np) && np->nvalue.cp!=Empty && !nv_isvtree(np)) + sub=1; } - else if((arg->argflag&ARG_APPEND) && (!nv_isarray(np) || (nv_aindex(np)>=0))) + else if(np->nvalue.cp && np->nvalue.cp!=Empty && !nv_type(np)) { - nv_unset(np); - nv_setarray(np,nv_associative); + _nv_unset(np,NV_EXPORT); } - else + } + else + { + if(!(arg->argflag&ARG_APPEND)) + _nv_unset(np,NV_EXPORT); + if(!sh_isoption(SH_BASH) && !(array&NV_IARRAY) && !nv_isarray(np)) nv_setarray(np,nv_associative); } - if(prefix) - cp = stakcopy(nv_name(np)); - sh.prefix = cp; + skip: + if(sub>0) + { + sfprintf(stkstd,"%s[%d]",prefix?nv_name(np):cp,sub); + shp->prefix = stakfreeze(1); + nv_putsub(np,(char*)0,ARRAY_ADD|ARRAY_FILL|sub); + } + else if(prefix) + shp->prefix = stakcopy(nv_name(np)); + else + shp->prefix = cp; + shp->last_table = 0; + memset(&nr,0,sizeof(nr)); + memcpy(&node,L_ARGNOD,sizeof(node)); + L_ARGNOD->nvalue.nrp = &nr; + nr.np = np; + nr.root = shp->last_root; + nr.table = shp->last_table; + L_ARGNOD->nvflag = NV_REF|NV_NOFREE; + L_ARGNOD->nvfun = 0; sh_exec(tp,sh_isstate(SH_ERREXIT)); - sh.prefix = prefix; +#if SHOPT_TYPEDEF + if(!maketype) +#endif + { + L_ARGNOD->nvalue.nrp = node.nvalue.nrp; + L_ARGNOD->nvflag = node.nvflag; + L_ARGNOD->nvfun = node.nvfun; + } + shp->prefix = prefix; if(nv_isarray(np) && (mp=nv_opensub(np))) np = mp; - nv_setvtree(np); + while(tp->tre.tretyp==TLST) + { + if(!tp->lst.lstlef || !tp->lst.lstlef->tre.tretyp==TCOM || tp->lst.lstlef->com.comarg || tp->lst.lstlef->com.comset && tp->lst.lstlef->com.comset->argval[0]!='[') + break; + tp = tp->lst.lstrit; + + } + if(!nv_isarray(np) && (tp->com.comarg || !tp->com.comset || tp->com.comset->argval[0]!='[')) + nv_setvtree(np); +#if SHOPT_TYPEDEF + goto check_type; +#else continue; +#endif /* SHOPT_TYPEDEF */ } cp = arg->argval; + mp = 0; } - if(sh.prefix && *cp=='.' && cp[1]=='=') - cp++; - np = nv_open(cp,sh.var_tree,flags); - if(!np->nvfun) + np = nv_open(cp,shp->var_tree,flags); + if(!np->nvfun && (flags&NV_NOREF)) { - if(sh.used_pos) + if(shp->used_pos) nv_onattr(np,NV_PARAM); else nv_offattr(np,NV_PARAM); @@ -321,7 +550,7 @@ void nv_setlist(register struct argnod *arg,register int flags) int append = 0; if(nv_isarray(np)) sub = savesub; - if(cp=strchr(sp,'=')) + if(cp=lastdot(sp,'=')) { if(cp[-1]=='+') append = ARG_APPEND; @@ -345,9 +574,27 @@ void nv_setlist(register struct argnod *arg,register int flags) char *av[2]; av[0] = cp; av[1] = 0; - sh_debug(trap,name,sub,av,append); + sh_debug(shp,trap,name,sub,av,append); } } +#if SHOPT_TYPEDEF + check_type: + if(maketype) + { + nv_open(shtp.nodes[0]->nvname,shp->var_tree,NV_ASSIGN|NV_VARNAME|NV_NOADD|NV_NOFAIL); + np = nv_mktype(shtp.nodes,shtp.numnodes); + free((void*)shtp.nodes); + shp->mktype = shtp.previous; + maketype = 0; + shp->prefix = 0; + if(nr.np == np) + { + L_ARGNOD->nvalue.nrp = node.nvalue.nrp; + L_ARGNOD->nvflag = node.nvflag; + L_ARGNOD->nvfun = node.nvfun; + } + } +#endif /* SHOPT_TYPEDEF */ } } @@ -380,7 +627,9 @@ static char *copystack(const char *prefix, register const char *name, const char stakseek(staktell()-1); if(*name=='.' && name[1]=='[') last = staktell()+2; - if(*name!='[' && *name!='.' && *name!='=' && *name!='+') + if(*name!='[' && *name!='.' && *name!='=' && *name!='+') + stakputc('.'); + if(*name=='.' && (name[1]=='=' || name[1]==0)) stakputc('.'); } if(last) @@ -415,15 +664,16 @@ static char *stack_extend(const char *cname, char *cp, int n) return((char*)name); } -Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) +Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) { + Shell_t *shp = &sh; char *cp=(char*)name, *sp, *xp; register int c; register Namval_t *np=0, *nq=0; Namfun_t *fp=0; long mode, add=0; int copy=1,isref,top=0,noscope=(flags&NV_NOSCOPE); - if(root==sh.var_tree) + if(root==shp->var_tree) { if(dtvnext(root)) top = 1; @@ -431,7 +681,7 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) flags &= ~NV_NOSCOPE; } if(!dp->disc) - copy = dp->nofree; + copy = dp->nofree&1; if(*cp=='.') cp++; while(1) @@ -451,13 +701,13 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) case '.': if(flags&NV_IDENT) return(0); - if(root==sh.var_tree) + if(root==shp->var_tree) flags &= ~NV_EXPORT; if(!copy && !(flags&NV_NOREF)) { c = sp-name; copy = cp-name; - dp->nofree = 1; + dp->nofree |= 1; name = copystack((const char*)0, name,(const char*)0); cp = (char*)name+copy; sp = (char*)name+c; @@ -471,11 +721,56 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) isref = 0; dp->last = cp; mode = (c=='.' || (flags&NV_NOADD))?add:NV_ADD; - if(flags&NV_NOSCOPE) + if((flags&NV_NOSCOPE) && c!='.') mode |= HASH_NOSCOPE; + np=0; if(top) - nq = nv_search(name,sh.var_base,0); - if(np = nv_search(name,root,mode)) + { + struct Ufunction *rp; + if((rp=shp->st.real_fun) && !rp->sdict && (flags&NV_STATIC)) + { + Dt_t *dp = dtview(shp->var_tree,(Dt_t*)0); + rp->sdict = dtopen(&_Nvdisc,Dtoset); + dtview(rp->sdict,shp->var_base); + dtview(shp->var_tree,rp->sdict); + } + if(np = nv_search(name,shp->var_tree,0)) + { + if(shp->var_tree->walk == shp->var_base) + { + nq = np; + if(flags&NV_NOSCOPE) + { + if(mode==0) + root = shp->var_base; + else + { + nv_delete(np,(Dt_t*)0,0); + np = 0; + } + } + } + else + { + root = shp->var_tree->walk; + flags |= NV_NOSCOPE; + noscope = 1; + } + } + if(rp && rp->sdict && (flags&NV_STATIC)) + { + root = rp->sdict; + if(np && shp->var_tree->walk==shp->var_tree) + { + _nv_unset(np,0); + nv_delete(np,shp->var_tree,0); + np = 0; + } + if(!np || shp->var_tree->walk!=root) + np = nv_search(name,root,HASH_NOSCOPE|NV_ADD); + } + } + if(np || (np = nv_search(name,root,mode))) { isref = nv_isref(np); if(top) @@ -489,7 +784,7 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) flags |= NV_NOSCOPE; } } - else if(add && nv_isnull(np) && c=='.') + else if(add && nv_isnull(np) && c=='.' && cp[1]!='.') nv_setvtree(np); } if(c) @@ -498,24 +793,31 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) if(isref) { char *sub=0; +#if NVCACHE + nvcache.ok = 0; +#endif if(c=='.') /* don't optimize */ - sh.argaddr = 0; - else if(flags&NV_NOREF) + shp->argaddr = 0; + else if((flags&NV_NOREF) && (c!='[' || *cp!='.')) { - if(c) + if(c && !(flags&NV_NOADD)) nv_unref(np); return(np); } - while(nv_isref(np)) + while(nv_isref(np) && np->nvalue.cp) { root = nv_reftree(np); - sh.last_table = nv_reftable(np); + shp->last_root = root; + shp->last_table = nv_reftable(np); sub = nv_refsub(np); np = nv_refnode(np); if(sub && c!='.') nv_putsub(np,sub,0L); flags |= NV_NOSCOPE; + noscope = 1; } + if(nv_isref(np) && (c=='[' || c=='.' || !(flags&NV_ASSIGN))) + errormsg(SH_DICT,ERROR_exit(1),e_noref,nv_name(np)); if(sub && c==0) return(np); if(np==nq) @@ -524,7 +826,7 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) { c = (cp-sp); copy = strlen(cp=nv_name(np)); - dp->nofree = 1; + dp->nofree |= 1; name = copystack(cp,sp,sub); sp = (char*)name + copy; cp = sp+c; @@ -534,12 +836,21 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) } flags |= NV_NOREF; } - sh.last_root = root; + shp->last_root = root; + if(cp[1]=='.') + cp++; + if(c=='.' && (cp[1]==0 || cp[1]=='=' || cp[1]=='+')) + { + nv_local = 1; + return(np); + } + if(cp[-1]=='.') + cp--; do { if(!np) { - if(*sp=='[' && *cp==0 && cp[-1]==']') + if(!nq && *sp=='[' && *cp==0 && cp[-1]==']') { /* * for backward compatibility @@ -554,6 +865,7 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) } if(c=='[' || (c=='.' && nv_isarray(np))) { + char *sub=0; int n = 0; if(c=='[') { @@ -564,24 +876,30 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) dp->last = cp; return(np); } - if(n&&(flags&NV_ARRAY)) + if((n&NV_ADD)&&(flags&NV_ARRAY)) n |= ARRAY_FILL; - cp = nv_endsubscript(np,sp,n); + if(flags&NV_ASSIGN) + n |= NV_ADD; + cp = nv_endsubscript(np,sp,n|(flags&NV_ASSIGN)); } else cp = sp; - if((c = *cp)=='.' || c=='[' || (n&ARRAY_FILL)) + if((c = *cp)=='.' || (c=='[' && nv_isarray(np)) || (n&ARRAY_FILL) || (flags&NV_ARRAY)) { int m = cp-sp; - char *sub = m?nv_getsub(np):0; + sub = m?nv_getsub(np):0; if(!sub) + { + if(m && !(n&NV_ADD)) + return(0); sub = "0"; + } n = strlen(sub)+2; if(!copy) { copy = cp-name; - dp->nofree = 1; + dp->nofree |= 1; name = copystack((const char*)0, name,(const char*)0); cp = (char*)name+copy; sp = cp-m; @@ -610,30 +928,66 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) } } else if(c==0 && mode && (n=nv_aindex(np))>0) - nv_putsub(np,(char*)0,n|ARRAY_FILL); - else if(n==0 && c==0) + nv_putsub(np,(char*)0,n); + else if(n==0 && (c==0 || (c=='[' && !nv_isarray(np)))) { /* subscript must be 0*/ cp[-1] = 0; - c = sh_arith(sp+1); + n = sh_arith(sp+1); cp[-1] = ']'; - if(c) + if(n) return(0); + if(c) + sp = cp; } dp->last = cp; if(nv_isarray(np) && (c=='[' || c=='.' || (flags&NV_ARRAY))) { - *(sp=cp) = 0; - nq = nv_search(name,root,mode); - *sp = c; - if(nq && nv_isnull(nq)) - nq = nv_arraychild(np,nq,c); - if(!(np=nq)) - return(np); + sp = cp; + if(!(nq = nv_opensub(np))) + { + Namarr_t *ap = nv_arrayptr(np); + if(!sub && (flags&NV_NOADD)) + return(0); + n = mode|((flags&NV_NOADD)?0:NV_ADD); + if(!ap && (n&NV_ADD)) + { + nv_putsub(np,sub,ARRAY_FILL); + ap = nv_arrayptr(np); + } + if(n && ap && !ap->table) + ap->table = dtopen(&_Nvdisc,Dtoset); + if(ap && ap->table && (nq=nv_search(sub,ap->table,n))) + nq->nvenv = (char*)np; + if(nq && nv_isnull(nq)) + nq = nv_arraychild(np,nq,c); + } + if(nq) + { + if(c=='.' && !nv_isvtree(nq)) + { + if(flags&NV_NOADD) + return(0); + nv_setvtree(nq); + } + np = nq; + } + else if(memcmp(cp,"[0]",3)) + return(nq); + else + { + /* ignore [0] */ + dp->last = cp += 3; + c = *cp; + } } } else if(nv_isarray(np)) + { + if(c==0 && (flags&NV_MOVE)) + return(np); nv_putsub(np,NIL(char*),ARRAY_UNDEF); + } if(c=='.' && (fp=np->nvfun)) { for(; fp; fp=fp->next) @@ -648,13 +1002,20 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) add = NV_ADD; break; } - else if((np=nq) && (c = *(cp=dp->last=fp->last))==0) - return(np); + else if(np=nq) + { + if((c = *(sp=cp=dp->last=fp->last))==0) + { + if(nv_isarray(np) && sp[-1]!=']') + nv_putsub(np,NIL(char*),ARRAY_UNDEF); + return(np); + } + } } } } while(c=='['); - if(c!='.') + if(c!='.' || cp[1]=='.') return(np); cp++; break; @@ -670,6 +1031,39 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) } /* + * delete the node <np> from the dictionary <root> and clear from the cache + * if <root> is NULL, only the cache is cleared + * if flags does not contain NV_NOFREE, the node is freed + */ +void nv_delete(Namval_t* np, Dt_t *root, int flags) +{ +#if NVCACHE + register int c; + struct Cache_entry *xp; + for(c=0,xp=nvcache.entries ; c < NVCACHE; xp= &nvcache.entries[++c]) + { + if(xp->np==np) + xp->root = 0; + } +#endif + if(root) + { + if(dtdelete(root,np)) + { + if(!(flags&NV_NOFREE) && ((flags&NV_FUNCTION) || !nv_subsaved(np))) + free((void*)np); + } +#if 0 + else + { + sfprintf(sfstderr,"%s not deleted\n",nv_name(np)); + sfsync(sfstderr); + } +#endif + } +} + +/* * Put <arg> into associative memory. * If <flags> & NV_ARRAY then follow array to next subscript * If <flags> & NV_NOARRAY then subscript is not allowed @@ -680,10 +1074,12 @@ Namval_t *nv_create(const char *name, Dt_t *root, int flags, Namfun_t *dp) * If <flags> & NV_NOADD then node will not be added if not found * If <flags> & NV_NOREF then don't follow reference * If <flags> & NV_NOFAIL then don't generate an error message on failure + * If <flags> & NV_STATIC then unset before an assignment * SH_INIT is only set while initializing the environment */ Namval_t *nv_open(const char *name, Dt_t *root, int flags) { + Shell_t *shp = &sh; register char *cp=(char*)name; register int c; register Namval_t *np; @@ -693,25 +1089,29 @@ Namval_t *nv_open(const char *name, Dt_t *root, int flags) char *fname = 0; int offset = staktell(); Dt_t *funroot; - +#if NVCACHE + struct Cache_entry *xp; +#endif + + sh_stats(STAT_NVOPEN); memset(&fun,0,sizeof(fun)); - sh.last_table = sh.namespace; + shp->last_table = shp->namespace; if(!root) - root = sh.var_tree; - sh.last_root = root; - if(root==sh_subfuntree(1)) + root = shp->var_tree; + shp->last_root = root; + if(root==shp->fun_tree) { flags |= NV_NOREF; msg = e_badfun; - if((np=sh.namespace) || strchr(name,'.')) + if((np=shp->namespace) || strchr(name,'.')) { name = cp = copystack(np?nv_name(np):0,name,(const char*)0); fname = strrchr(cp,'.'); *fname = 0; - fun.nofree = 1; + fun.nofree |= 1; flags &= ~NV_IDENT; funroot = root; - root = sh.var_tree; + root = shp->var_tree; } } else if(!(flags&(NV_IDENT|NV_VARNAME|NV_ASSIGN))) @@ -724,24 +1124,24 @@ Namval_t *nv_open(const char *name, Dt_t *root, int flags) { while(nv_isref(np)) { - sh.last_table = nv_reftable(np); + shp->last_table = nv_reftable(np); np = nv_refnode(np); } } return(np); } - else if(sh.prefix && /**name!='.' &&*/ (flags&NV_ASSIGN)) + else if(shp->prefix && (flags&NV_ASSIGN)) { - name = cp = copystack(sh.prefix,name,(const char*)0); - fun.nofree = 1; + name = cp = copystack(shp->prefix,name,(const char*)0); + fun.nofree |= 1; } c = *(unsigned char*)cp; - if(root==sh.alias_tree) + if(root==shp->alias_tree) { msg = e_aliname; while((c= *(unsigned char*)cp++) && (c!='=') && (c!='/') && - (c>=0x200 || !(c=sh_lexstates[ST_NORM][c]) || c==S_EPAT)); - if(sh.subshell && c=='=') + (c>=0x200 || !(c=sh_lexstates[ST_NORM][c]) || c==S_EPAT || c==S_COLON)); + if(shp->subshell && c=='=') root = sh_subaliastree(1); if(c= *--cp) *cp = 0; @@ -756,14 +1156,65 @@ Namval_t *nv_open(const char *name, Dt_t *root, int flags) { c = *++cp; flags |= NV_NOREF; - if(root==sh.var_tree) - root = sh.var_base; - sh.last_table = 0; + if(root==shp->var_tree) + root = shp->var_base; + shp->last_table = 0; } if(c= !isaletter(c)) goto skip; +#if NVCACHE + for(c=0,xp=nvcache.entries ; c < NVCACHE; xp= &nvcache.entries[++c]) + { + if(xp->root!=root) + continue; + if(*name==*xp->name && (flags&(NV_ARRAY|NV_NOSCOPE))==xp->flags && memcmp(xp->name,name,xp->len)==0 && (name[xp->len]==0 || name[xp->len]=='=' || name[xp->len]=='+')) + { + sh_stats(STAT_NVHITS); + np = xp->np; + cp = (char*)name+xp->len; + if(nv_isarray(np)) + nv_putsub(np,NIL(char*),ARRAY_UNDEF); + shp->last_table = xp->last_table; + goto nocache; + } + } + nvcache.ok = 1; +#endif np = nv_create(name, root, flags, &fun); cp = fun.last; +#if NVCACHE + if(np && nvcache.ok && cp[-1]!=']') + { + xp = &nvcache.entries[nvcache.index]; + if(*cp) + { + char *sp = strchr(name,*cp); + if(!sp) + goto nocache; + xp->len = sp-name; + } + else + xp->len = strlen(name); + c = roundof(xp->len+1,32); + if(c > xp->size) + { + if(xp->size==0) + xp->name = malloc(c); + else + xp->name = realloc(xp->name,c); + xp->size = c; + } + memcpy(xp->name,name,xp->len); + xp->name[xp->len] = 0; + xp->root = root; + xp->np = np; + xp->last_table = shp->last_table; + xp->flags = (flags&(NV_ARRAY|NV_NOSCOPE)); + nvcache.index = (nvcache.index+1)&(NVCACHE-1); + } +nocache: + nvcache.ok = 0; +#endif if(fname) { c = ((flags&NV_NOSCOPE)?HASH_NOSCOPE:0)|((flags&NV_NOADD)?0:NV_ADD); @@ -771,13 +1222,26 @@ Namval_t *nv_open(const char *name, Dt_t *root, int flags) np = nv_search(name, funroot, c); *fname = 0; } - else if(*cp=='+' && cp[1]=='=') + else { - append=NV_APPEND; - cp++; + if(*cp=='.' && cp[1]=='.') + { + append |= NV_NODISC; + cp+=2; + } + if(*cp=='+' && cp[1]=='=') + { + append |= NV_APPEND; + cp++; + } } c = *cp; skip: +#if SHOPT_TYPEDEF + if(np && shp->mktype) + np = nv_addnode(np,0); +#endif /* SHOPT_TYPEDEF */ + if(c=='=' && np && (flags&NV_ASSIGN)) { cp++; @@ -789,12 +1253,25 @@ skip: } else { - char *sub=0; + char *sub=0, *prefix= shp->prefix; + int isref; + shp->prefix = 0; + if((flags&NV_STATIC) && !shp->mktype) + { + if(!nv_isnull(np)) + return(np); + } + isref = nv_isref(np); if(sh_isoption(SH_XTRACE) && nv_isarray(np)) sub = nv_getsub(np); c = msg==e_aliname? 0: (append | (flags&NV_EXPORT)); + if(isref) + nv_offattr(np,NV_REF); nv_putval(np, cp, c); + if(isref) + nv_setref(np,(Dt_t*)0,NV_VARNAME); savesub = sub; + shp->prefix = prefix; } nv_onattr(np, flags&NV_ATTRIBUTES); } @@ -808,7 +1285,7 @@ skip: msg = e_noarray; errormsg(SH_DICT,ERROR_exit(1),msg,name); } - if(fun.nofree) + if(fun.nofree&1) stakseek(offset); return(np); } @@ -836,7 +1313,10 @@ void nv_putval(register Namval_t *np, const char *string, int flags) register char *cp; register int size = 0; register int dot; +#ifdef _ENV_H int was_local = nv_local; +#endif + union Value u; if(!(flags&NV_RDONLY) && nv_isattr (np, NV_RDONLY)) errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np)); /* The following could cause the shell to fork if assignment @@ -845,40 +1325,49 @@ void nv_putval(register Namval_t *np, const char *string, int flags) sh.argaddr = 0; if(sh.subshell && !nv_local) np = sh_assignok(np,1); - if(np->nvfun && !nv_isattr(np,NV_REF)) + if(np->nvfun && np->nvfun->disc && !(flags&NV_NODISC) && !nv_isattr(np,NV_REF)) { /* This function contains disc */ if(!nv_local) { nv_local=1; nv_putv(np,sp,flags,np->nvfun); +#ifdef _ENV_H if(sp && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT))) sh_envput(sh.env,np); +#endif return; } /* called from disc, assign the actual value */ } flags &= ~NV_NODISC; + nv_local=0; if(flags&(NV_NOREF|NV_NOFREE)) { - if(!nv_isnull(np) && np->nvalue.cp!=sp) - nv_unset(np); - nv_local=0; + if(np->nvalue.cp && np->nvalue.cp!=sp && !nv_isattr(np,NV_NOFREE)) + free((void*)np->nvalue.cp); np->nvalue.cp = (char*)sp; nv_setattr(np,(flags&~NV_RDONLY)|NV_NOFREE); return; } - nv_local=0; up= &np->nvalue; -#if !SHOPT_BSH + if(nv_isattr(np,NV_INT16P) == NV_INT16) + { + if(!np->nvalue.up || !nv_isarray(np)) + { + up = &u; + up->up = &np->nvalue; + } + } + else if(np->nvalue.up && nv_isarray(np) && nv_arrayptr(np)) + up = np->nvalue.up; + if(up && up->cp==Empty) + up->cp = 0; if(nv_isattr(np,NV_EXPORT)) nv_offattr(np,NV_IMPORT); - else if(!nv_isattr(np,NV_MINIMAL)) - np->nvenv = 0; -#endif /* SHOPT_BSH */ if(nv_isattr (np, NV_INTEGER)) { - if(nv_isattr(np, NV_DOUBLE)) + if(nv_isattr(np, NV_DOUBLE) == NV_DOUBLE) { if(nv_isattr(np, NV_LONG) && sizeof(double)<sizeof(Sfdouble_t)) { @@ -928,7 +1417,7 @@ void nv_putval(register Namval_t *np, const char *string, int flags) Sflong_t ll=0,oll=0; if(flags&NV_INTEGER) { - if(flags&NV_DOUBLE) + if((flags&NV_DOUBLE) == NV_DOUBLE) { if(flags&NV_LONG) ll = *((Sfdouble_t*)sp); @@ -969,7 +1458,7 @@ void nv_putval(register Namval_t *np, const char *string, int flags) int32_t l=0,ol=0; if(flags&NV_INTEGER) { - if(flags&NV_DOUBLE) + if((flags&NV_DOUBLE) == NV_DOUBLE) { Sflong_t ll; if(flags&NV_LONG) @@ -1013,8 +1502,8 @@ void nv_putval(register Namval_t *np, const char *string, int flags) { int16_t s=0; if(flags&NV_APPEND) - s = up->s; - up->s = s+(int16_t)l; + s = *up->sp; + *(up->sp) = s+(int16_t)l; nv_onattr(np,NV_NOFREE); } else @@ -1037,20 +1526,30 @@ void nv_putval(register Namval_t *np, const char *string, int flags) #endif /* _lib_pathnative */ if(flags&NV_INTEGER) { - if(flags&NV_DOUBLE) + if((flags&NV_DOUBLE)==NV_DOUBLE) { if(flags&NV_LONG) sfprintf(sh.strbuf,"%.*Lg",LDBL_DIG,*((Sfdouble_t*)sp)); else sfprintf(sh.strbuf,"%.*g",DBL_DIG,*((double*)sp)); } - else if(flags&NV_LONG) - sfprintf(sh.strbuf,"%lld\0",*((Sflong_t*)sp)); + else if(flags&NV_UNSIGN) + { + if(flags&NV_LONG) + sfprintf(sh.strbuf,"%I*lu",sizeof(Sfulong_t),*((Sfulong_t*)sp)); + else + sfprintf(sh.strbuf,"%lu",(unsigned long)((flags&NV_SHORT)?*((uint16_t*)sp):*((uint32_t*)sp))); + } else - sfprintf(sh.strbuf,"%ld\0",*((int32_t*)sp)); + { + if(flags&NV_LONG) + sfprintf(sh.strbuf,"%I*ld",sizeof(Sflong_t),*((Sflong_t*)sp)); + else + sfprintf(sh.strbuf,"%ld",(long)((flags&NV_SHORT)?*((int16_t*)sp):*((int32_t*)sp))); + } sp = sfstruse(sh.strbuf); } - if(nv_isattr(np, NV_HOST)==NV_HOST && sp) + if(nv_isattr(np, NV_HOST|NV_INTEGER)==NV_HOST && sp) { #ifdef _lib_pathnative /* @@ -1094,7 +1593,10 @@ void nv_putval(register Namval_t *np, const char *string, int flags) /* delay free in case <sp> points into free region */ tofree = up->cp; } - nv_offattr(np,NV_NOFREE); + if(nv_isattr(np,NV_BINARY) && !(flags&NV_RAW)) + tofree = 0; + if(nv_isattr(np,NV_LJUST|NV_RJUST)) + tofree = 0; if (sp) { dot = strlen(sp); @@ -1105,7 +1607,10 @@ void nv_putval(register Namval_t *np, const char *string, int flags) if(flags&NV_RAW) { if(tofree) + { free((void*)tofree); + nv_offattr(np,NV_NOFREE); + } up->cp = sp; return; } @@ -1115,6 +1620,7 @@ void nv_putval(register Namval_t *np, const char *string, int flags) if(size==0) size = oldsize + (3*dot/4); cp = (char*)malloc(size+1); + nv_offattr(np,NV_NOFREE); if(oldsize) memcpy((void*)cp,(void*)up->cp,oldsize); up->cp = cp; @@ -1134,19 +1640,29 @@ void nv_putval(register Namval_t *np, const char *string, int flags) nv_setsize(np,size=dot); else if(size > dot) dot = size; - cp = (char*)malloc(((unsigned)dot+1)); + else if(nv_isattr(np,NV_LJUST) && dot>size) + dot = size; + if(size==0 || tofree || !(cp=(char*)up->cp)) + { + cp = (char*)malloc(((unsigned)dot+1)); + cp[dot] = 0; + nv_offattr(np,NV_NOFREE); + } + } else cp = 0; up->cp = cp; if(sp) { + int c = cp[dot]; + memcpy(cp,sp,dot); + cp[dot]=0; if(nv_isattr(np, NV_LTOU)) - ltou(sp,cp); + ltou(cp); else if(nv_isattr (np, NV_UTOL)) - sh_utol(sp,cp); - else - strcpy(cp, sp); + utol(cp); + cp[dot] = c; if(nv_isattr(np, NV_RJUST) && nv_isattr(np, NV_ZFILL)) rightjust(cp,size,'0'); else if(nv_isattr(np, NV_RJUST)) @@ -1155,7 +1671,7 @@ void nv_putval(register Namval_t *np, const char *string, int flags) { register char *dp; dp = strlen (cp) + cp; - *(cp = (cp + size)) = 0; + cp = cp+size; for (; dp < cp; *dp++ = ' '); } #if SHOPT_MULTIBYTE @@ -1166,11 +1682,13 @@ void nv_putval(register Namval_t *np, const char *string, int flags) } if(flags&NV_APPEND) stakseek(offset); - if(tofree) + if(tofree && tofree!=Empty) free((void*)tofree); } +#ifdef _ENV_H if(!was_local && ((flags&NV_EXPORT) || nv_isattr(np,NV_EXPORT))) sh_envput(sh.env,np); +#endif return; } @@ -1308,10 +1826,10 @@ static void attstore(register Namval_t *np, void *data) return; flag = nv_isattr(np,NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER); stakputc('='); - if((flag&NV_DOUBLE) && (flag&NV_INTEGER)) + if((flag&NV_DOUBLE) == NV_DOUBLE) { /* export doubles as integers for ksh88 compatibility */ - stakputc(c+(flag&~(NV_DOUBLE|NV_EXPNOTE))); + stakputc(c+NV_INTEGER|(flag&~(NV_DOUBLE|NV_EXPNOTE))); } else { @@ -1327,14 +1845,16 @@ static void attstore(register Namval_t *np, void *data) { register int flag = np->nvflag; register struct adata *ap = (struct adata*)data; + ap->sh = &sh; + ap->tp = 0; if(!(flag&NV_EXPORT) || (flag&NV_FUNCT)) return; flag &= (NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER); *ap->attval++ = '='; - if((flag&NV_DOUBLE) && (flag&NV_INTEGER)) + if((flag&NV_DOUBLE) == NV_DOUBLE) { /* export doubles as integers for ksh88 compatibility */ - *ap->attval++ = ' '+(flag&~(NV_DOUBLE|NV_EXPNOTE)); + *ap->attval++ = ' '+ NV_INTEGER|(flag&~(NV_DOUBLE|NV_EXPNOTE)); *ap->attval = ' '; } else @@ -1354,6 +1874,8 @@ static void pushnam(Namval_t *np, void *data) { register char *value; register struct adata *ap = (struct adata*)data; + ap->sh = &sh; + ap->tp = 0; if(nv_isattr(np,NV_IMPORT)) { if(np->nvenv) @@ -1394,16 +1916,22 @@ char **sh_envgen(void) register int namec; register char *cp; struct adata data; + Shell_t *shp = sh_getinterp(); + data.sh = shp; + data.tp = 0; /* L_ARGNOD gets generated automatically as full path name of command */ nv_offattr(L_ARGNOD,NV_EXPORT); data.attsize = 6; - namec = nv_scan(sh.var_tree,nullscan,(void*)0,NV_EXPORT,NV_EXPORT); + namec = nv_scan(shp->var_tree,nullscan,(void*)0,NV_EXPORT,NV_EXPORT); + namec += shp->nenv; er = (char**)stakalloc((namec+4)*sizeof(char*)); - data.argnam = (er+=2); - nv_scan(sh.var_tree, pushnam,&data,NV_EXPORT, NV_EXPORT); + data.argnam = (er+=2) + shp->nenv; + if(shp->nenv) + memcpy((void*)er,environ,shp->nenv*sizeof(char*)); + nv_scan(shp->var_tree, pushnam,&data,NV_EXPORT, NV_EXPORT); *data.argnam = (char*)stakalloc(data.attsize); cp = data.attval = strcopy(*data.argnam,e_envmarker); - nv_scan(sh.var_tree, attstore,&data,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER)); + nv_scan(shp->var_tree, attstore,&data,0,(NV_RDONLY|NV_UTOL|NV_LTOU|NV_RJUST|NV_LJUST|NV_ZFILL|NV_INTEGER)); *data.attval = 0; if(cp!=data.attval) data.argnam++; @@ -1421,16 +1949,20 @@ struct scan void *scandata; }; - static int scanfilter(Dt_t *dict, void *arg, void *data) { register Namval_t *np = (Namval_t*)arg; register int k=np->nvflag; register struct scan *sp = (struct scan*)data; + register struct adata *tp = (struct adata*)sp->scandata; NOT_USED(dict); +#if SHOPT_TYPEDEF + if(tp && tp->tp && nv_type(np)!=tp->tp) + return(0); +#endif /*SHOPT_TYPEDEF */ if(sp->scanmask?(k&sp->scanmask)==sp->scanflags:(!sp->scanflags || (k&sp->scanflags))) { - if(!np->nvalue.cp && !nv_isattr(np,~NV_DEFAULT)) + if(!np->nvalue.cp && !np->nvfun && !nv_isattr(np,~NV_DEFAULT)) return(0); if(sp->scanfn) { @@ -1473,14 +2005,28 @@ int nv_scan(Dt_t *root, void (*fn)(Namval_t*,void*), void *data,int mask, int fl /* * create a new environment scope */ -void nv_scope(struct argnod *envlist) +void sh_scope(Shell_t *shp, struct argnod *envlist, int fun) { - register Dt_t *newscope; + register Dt_t *newscope, *newroot=shp->var_base; + struct Ufunction *rp; newscope = dtopen(&_Nvdisc,Dtoset); - dtview(newscope,(Dt_t*)sh.var_tree); - sh.var_tree = (Dt_t*)newscope; if(envlist) + { + dtview(newscope,(Dt_t*)shp->var_tree); + shp->var_tree = newscope; nv_setlist(envlist,NV_EXPORT|NV_NOSCOPE|NV_IDENT|NV_ASSIGN); + if(!fun) + return; + shp->var_tree = dtview(newscope,0); + } + if((rp=shp->st.real_fun) && rp->sdict) + { + dtview(rp->sdict,newroot); + newroot = rp->sdict; + + } + dtview(newscope,(Dt_t*)newroot); + shp->var_tree = newscope; } /* @@ -1530,17 +2076,50 @@ void nv_close(Namval_t *np) NOT_USED(np); } -static void table_unset(register Dt_t *root, int flags, Dt_t *oroot) +static void table_unset(Shell_t *shp, register Dt_t *root, int flags, Dt_t *oroot) { - register Namval_t *np,*nq; - for(np=(Namval_t*)dtfirst(root);np;np=nq) + register Namval_t *np,*nq, *npnext; + for(np=(Namval_t*)dtfirst(root);np;np=npnext) { + if(nv_isref(np)) + nv_unref(np); + if(nq=dtsearch(oroot,np)) + { + if(nv_cover(nq)) + { + int subshell = shp->subshell; + shp->subshell = 0; + if(nv_isattr(nq, NV_INTEGER)) + { + Sfdouble_t d = nv_getnum(nq); + nv_putval(nq,(char*)&d,NV_LDOUBLE); + } + else + nv_putval(nq, nv_getval(nq), NV_RDONLY); + shp->subshell = subshell; + np->nvfun = 0; + } +#ifdef _ENV_H + if(nv_isattr(nq,NV_EXPORT)) + sh_envput(shp->env,nq); +#endif + } + npnext = (Namval_t*)dtnext(root,np); + shp->last_root = root; + shp->last_table = 0; + if(nv_isvtree(np)) + { + int len = strlen(np->nvname); + while((nq=npnext) && memcmp(np->nvname,nq->nvname,len)==0 && nq->nvname[len]=='.') + + { + npnext = (Namval_t*)dtnext(root,nq); + _nv_unset(nq,flags); + nv_delete(nq,root,0); + } + } _nv_unset(np,flags); - if(oroot && (nq=nv_search(nv_name(np),oroot,0)) && nv_isattr(nq,NV_EXPORT)) - sh_envput(sh.env,nq); - nq = (Namval_t*)dtnext(root,np); - dtdelete(root,np); - free((void*)np); + nv_delete(np,root,0); } } @@ -1552,9 +2131,11 @@ static void table_unset(register Dt_t *root, int flags, Dt_t *oroot) * will retain its attributes. * <flags> can contain NV_RDONLY to override the readonly attribute * being cleared. + * <flags> can contain NV_EXPORT to override preserve nvenv */ void _nv_unset(register Namval_t *np,int flags) { + Shell_t *shp = &sh; register union Value *up; if(!(flags&NV_RDONLY) && nv_isattr (np,NV_RDONLY)) errormsg(SH_DICT,ERROR_exit(1),e_readonly, nv_name(np)); @@ -1563,24 +2144,47 @@ void _nv_unset(register Namval_t *np,int flags) register struct slnod *slp = (struct slnod*)(np->nvenv); if(slp && !nv_isattr(np,NV_NOFREE)) { + struct Ufunction *rq,*rp = np->nvalue.rp; /* free function definition */ register char *name=nv_name(np),*cp= strrchr(name,'.'); if(cp) { Namval_t *npv; *cp = 0; - npv = nv_open(name,sh.var_tree,NV_NOARRAY|NV_VARNAME|NV_NOADD); + npv = nv_open(name,shp->var_tree,NV_NOARRAY|NV_VARNAME|NV_NOADD); *cp++ = '.'; if(npv) nv_setdisc(npv,cp,NIL(Namval_t*),(Namfun_t*)npv); } + if(rp->fname && shp->fpathdict && (rq = (struct Ufunction*)nv_search(rp->fname,shp->fpathdict,0))) + { + do + { + if(rq->np != np) + continue; + dtdelete(shp->fpathdict,rq); + break; + } + while(rq = (struct Ufunction*)dtnext(shp->fpathdict,rq)); + } + if(rp->sdict) + { + Namval_t *mp, *nq; + for(mp=(Namval_t*)dtfirst(rp->sdict);mp;mp=nq) + { + nq = dtnext(rp->sdict,mp); + _nv_unset(mp,NV_RDONLY); + nv_delete(mp,rp->sdict,0); + } + dtclose(rp->sdict); + } stakdelete(slp->slptr); free((void*)np->nvalue.ip); np->nvalue.ip = 0; } goto done; } - if(sh.subshell && !nv_isnull(np)) + if(shp->subshell && !nv_isnull(np)) np = sh_assignok(np,0); nv_offattr(np,NV_NODISC); if(np->nvfun && !nv_isref(np)) @@ -1590,49 +2194,52 @@ void _nv_unset(register Namval_t *np,int flags) { nv_local=1; nv_putv(np,NIL(char*),flags,np->nvfun); + nv_local=0; return; } /* called from disc, assign the actual value */ nv_local=0; } - up = &np->nvalue; - if(up->cp) + if(nv_isarray(np) && np->nvalue.cp!=Empty && np->nvfun) + up = np->nvalue.up; + else + up = &np->nvalue; + if(up && up->cp) { - if(!nv_isattr (np, NV_NOFREE)) + if(up->cp!=Empty && !nv_isattr(np, NV_NOFREE)) free((void*)up->cp); up->cp = 0; } done: if(!nv_isarray(np) || !nv_arrayptr(np)) { - if(nv_isref(np)) + if(nv_isref(np) && !nv_isattr(np,NV_EXPORT)) free((void*)np->nvalue.nrp); nv_setsize(np,0); if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT)) { if(nv_isattr(np,NV_EXPORT) && !strchr(np->nvname,'[')) - env_delete(sh.env,nv_name(np)); - np->nvenv = 0; + env_delete(shp->env,nv_name(np)); + if(!(flags&NV_EXPORT) || nv_isattr(np,NV_IMPORT|NV_EXPORT)==(NV_IMPORT|NV_EXPORT)) + np->nvenv = 0; nv_setattr(np,0); } else + { nv_setattr(np,NV_MINIMAL); + nv_delete(np,(Dt_t*)0,0); + } } } -void nv_unset(register Namval_t *np) -{ - _nv_unset(np,0); -} - /* * return the node pointer in the highest level scope */ -Namval_t *nv_scoped(register Namval_t *np) +Namval_t *sh_scoped(Shell_t *shp, register Namval_t *np) { - if(!dtvnext(sh.var_tree)) + if(!dtvnext(shp->var_tree)) return(np); - return(dtsearch(sh.var_tree,np)); + return(dtsearch(shp->var_tree,np)); } #if 1 @@ -1671,6 +2278,7 @@ static char *tableval(Dt_t *root) struct optimize { Namfun_t hdr; + Shell_t *sh; char **ptr; struct optimize *next; Namval_t *np; @@ -1699,7 +2307,12 @@ static void put_optimize(Namval_t* np,const char *val,int flags,Namfun_t *fp) optimize_clear(np,fp); } -static const Namdisc_t optimize_disc = {sizeof(struct optimize),put_optimize}; +static Namfun_t *clone_optimize(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) +{ + return((Namfun_t*)0); +} + +static const Namdisc_t optimize_disc = {sizeof(struct optimize),put_optimize,0,0,0,0,clone_optimize}; void nv_optimize(Namval_t *np) { @@ -1707,9 +2320,14 @@ void nv_optimize(Namval_t *np) register struct optimize *op, *xp; if(sh.argaddr) { + if(np==SH_LINENO) + { + sh.argaddr = 0; + return; + } for(fp=np->nvfun; fp; fp = fp->next) { - if(fp->disc->getnum || fp->disc->getval) + if(fp->disc && (fp->disc->getnum || fp->disc->getval)) { sh.argaddr = 0; return; @@ -1781,14 +2399,16 @@ char *nv_getval(register Namval_t *np) if(!nv_local && sh.argaddr) nv_optimize(np); #endif /* SHOPT_OPTIMIZE */ - if(!np->nvfun && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF|NV_TABLE)) + if((!np->nvfun || !np->nvfun->disc) && !nv_isattr(np,NV_ARRAY|NV_INTEGER|NV_FUNCT|NV_REF|NV_TABLE)) goto done; if(nv_isref(np)) { + if(!np->nvalue.cp) + return(0); sh.last_table = nv_reftable(np); return(nv_name(nv_refnode(np))); } - if(np->nvfun) + if(np->nvfun && np->nvfun->disc) { if(!nv_local) { @@ -1803,7 +2423,7 @@ char *nv_getval(register Namval_t *np) Sflong_t ll; if(!up->cp) return("0"); - if(nv_isattr (np,NV_DOUBLE)) + if(nv_isattr (np,NV_DOUBLE)==NV_DOUBLE) { Sfdouble_t ld; double d; @@ -1813,6 +2433,8 @@ char *nv_getval(register Namval_t *np) ld = *up->ldp; if(nv_isattr (np,NV_EXPNOTE)) format = "%.*Lg"; + else if(nv_isattr (np,NV_HEXFLOAT)) + format = "%.*La"; else format = "%.*Lf"; sfprintf(sh.strbuf,format,nv_size(np),ld); @@ -1822,6 +2444,8 @@ char *nv_getval(register Namval_t *np) d = *up->dp; if(nv_isattr (np,NV_EXPNOTE)) format = "%.*g"; + else if(nv_isattr (np,NV_HEXFLOAT)) + format = "%.*a"; else format = "%.*f"; sfprintf(sh.strbuf,format,nv_size(np),d); @@ -1833,14 +2457,24 @@ char *nv_getval(register Namval_t *np) if(nv_isattr (np,NV_LONG)) ll = *(Sfulong_t*)up->llp; else if(nv_isattr (np,NV_SHORT)) - ll = (uint16_t)up->s; + { + if(nv_isattr(np,NV_INT16P)==NV_INT16P) + ll = *(uint16_t*)(up->sp); + else + ll = (uint16_t)up->s; + } else ll = *(uint32_t*)(up->lp); } else if(nv_isattr (np,NV_LONG)) ll = *up->llp; else if(nv_isattr (np,NV_SHORT)) - ll = up->s; + { + if(nv_isattr(np,NV_INT16P)==NV_INT16P) + ll = *up->sp; + else + ll = up->s; + } else ll = *(up->lp); if((numeric=nv_size(np))==10) @@ -1889,7 +2523,7 @@ Sfdouble_t nv_getnum(register Namval_t *np) #endif /* SHOPT_OPTIMIZE */ if(nv_istable(np)) errormsg(SH_DICT,ERROR_exit(1),e_number,nv_name(np)); - if(np->nvfun) + if(np->nvfun && np->nvfun->disc) { if(!nv_local) { @@ -1898,12 +2532,19 @@ Sfdouble_t nv_getnum(register Namval_t *np) } nv_local=0; } + if(nv_isref(np)) + { + str = nv_refsub(np); + np = nv_refnode(np); + if(str) + nv_putsub(np,str,0L); + } if(nv_isattr (np, NV_INTEGER)) { up= &np->nvalue; - if(!up->lp) + if(!up->lp || up->cp==Empty) r = 0; - else if(nv_isattr(np, NV_DOUBLE)) + else if(nv_isattr(np, NV_DOUBLE)==NV_DOUBLE) { if(nv_isattr(np, NV_LONG)) r = *up->ldp; @@ -1915,7 +2556,12 @@ Sfdouble_t nv_getnum(register Namval_t *np) if(nv_isattr(np, NV_LONG)) r = (Sflong_t)*((Sfulong_t*)up->llp); else if(nv_isattr(np, NV_SHORT)) - r = (Sflong_t)((uint16_t)up->s); + { + if(nv_isattr(np,NV_INT16P)==NV_INT16P) + r = (Sflong_t)(*(uint16_t*)up->sp); + else + r = (Sflong_t)((uint16_t)up->s); + } else r = *((uint32_t*)up->lp); } @@ -1924,7 +2570,12 @@ Sfdouble_t nv_getnum(register Namval_t *np) if(nv_isattr(np, NV_LONG)) r = *up->llp; else if(nv_isattr(np, NV_SHORT)) - r = up->s; + { + if(nv_isattr(np,NV_INT16P)==NV_INT16P) + r = *up->sp; + else + r = up->s; + } else r = *up->lp; } @@ -1952,16 +2603,17 @@ void nv_newattr (register Namval_t *np, unsigned newatts, int size) register unsigned int n; Namarr_t *ap = 0; int oldsize,oldatts; + Namfun_t *fp= (newatts&NV_NODISC)?np->nvfun:0; + newatts &= ~NV_NODISC; /* check for restrictions */ if(sh_isoption(SH_RESTRICTED) && ((sp=nv_name(np))==nv_name(PATHNOD) || sp==nv_name(SHELLNOD) || sp==nv_name(ENVNOD) || sp==nv_name(FPATHNOD))) errormsg(SH_DICT,ERROR_exit(1),e_restricted,nv_name(np)); /* handle attributes that do not change data separately */ n = np->nvflag; -#if SHOPT_BSH if(newatts&NV_EXPORT) nv_offattr(np,NV_IMPORT); -#endif /* SHOPT_BSH */ +#ifdef _ENV_H if(((n^newatts)&NV_EXPORT)) { /* record changes to the environment */ @@ -1970,6 +2622,7 @@ void nv_newattr (register Namval_t *np, unsigned newatts, int size) else sh_envput(sh.env,np); } +#endif if((size==0||(n&NV_INTEGER)) && ((n^newatts)&~NV_NOCHANGE)==0) { if(size) @@ -1983,6 +2636,8 @@ void nv_newattr (register Namval_t *np, unsigned newatts, int size) nv_putsub(np,NIL(char*),ARRAY_SCAN); oldsize = nv_size(np); oldatts = np->nvflag; + if(fp) + np->nvfun = 0; if(ap) /* add element to prevent array deletion */ ap->nelem++; do @@ -2000,11 +2655,16 @@ void nv_newattr (register Namval_t *np, unsigned newatts, int size) Namval_t *mp; ap->nelem &= ~ARRAY_SCAN; if(mp=nv_opensub(np)) - nv_onattr(mp,NV_NOFREE); - } - nv_unset(np); - if(ap) + { + nv_unset(mp); + mp->nvalue.cp = Empty; + } + else + nv_unset(np); ap->nelem |= ARRAY_SCAN; + } + else + nv_unset(np); if(size==0 && (newatts&(NV_LJUST|NV_RJUST|NV_ZFILL))) size = n; } @@ -2020,6 +2680,8 @@ void nv_newattr (register Namval_t *np, unsigned newatts, int size) } } while(ap && nv_nextsub(np)); + if(fp) + np->nvfun = fp; if(ap) ap->nelem--; return; @@ -2100,55 +2762,127 @@ char* setenviron(const char *name) } /* - * copy <str1> to <str2> changing lower case to upper case - * <str2> must be big enough to hold <str1> - * <str1> and <str2> may point to the same place. + * convert <str> to upper case */ - -static void ltou(register char const *str1,register char *str2) -/*@ - assume str1!=0 && str2!=0; - return x satisfying strlen(in str1)==strlen(in str2); -@*/ +static void ltou(register char *str) { register int c; - for(; c= *((unsigned char*)str1); str1++,str2++) + for(; c= *((unsigned char*)str); str++) { if(islower(c)) - *str2 = toupper(c); - else - *str2 = c; + *str = toupper(c); + } +} + +/* + * convert <str> to lower case + */ +static void utol(register char *str) +{ + register int c; + for(; c= *((unsigned char*)str); str++) + { + if(isupper(c)) + *str = tolower(c); } - *str2 = 0; } /* * normalize <cp> and return pointer to subscript if any + * if <eq> is specified, return pointer to first = not in a subscript */ -static char *lastdot(register char *cp) +static char *lastdot(register char *cp, int eq) { - register char *dp=cp, *ep=0; + register char *ep=0; register int c; + if(eq) + cp++; while(c= *cp++) { - *dp++ = c; if(c=='[') - ep = cp; + cp = nv_endsubscript((Namval_t*)0,ep=cp,0); else if(c=='.') { if(*cp=='[') - { - ep = nv_endsubscript((Namval_t*)0,cp,0); - c = ep-cp; - memcpy(dp,cp,c); - dp = sh_checkid(dp+1,dp+c); - cp = ep; - } + cp = nv_endsubscript((Namval_t*)0,cp,0); ep = 0; } + else if(eq && c == '=') + return(cp-1); } - *dp = 0; - return(ep); + return(eq?0:ep); +} + +int nv_rename(register Namval_t *np, int flags) +{ + Shell_t *shp = &sh; + register Namval_t *mp=0,*nr=0; + register char *cp; + int index= -1; + Namval_t *last_table = shp->last_table; + Dt_t *last_root = shp->last_root; + Dt_t *hp = 0; + if(nv_isattr(np,NV_PARAM) && shp->st.prevst) + { + if(!(hp=(Dt_t*)shp->st.prevst->save_tree)) + hp = dtvnext(shp->var_tree); + } + if(!(cp=nv_getval(np))) + { + if(flags&NV_MOVE) + errormsg(SH_DICT,ERROR_exit(1),e_varname,""); + return(0); + } + if(lastdot(cp,0) && nv_isattr(np,NV_MINIMAL)) + errormsg(SH_DICT,ERROR_exit(1),e_varname,nv_name(np)); + if(nv_isarray(np) && !(mp=nv_opensub(np))) + index=nv_aindex(np); + if(!hp) + hp = shp->var_tree; + if(!(nr = nv_open(cp, hp, flags|NV_ARRAY|NV_NOREF|NV_NOSCOPE|NV_NOADD|NV_NOFAIL))) + hp = shp->var_base; + else if(shp->last_root) + hp = shp->last_root; + if(!nr) + nr= nv_open(cp, hp, flags|NV_NOREF|((flags&NV_MOVE)?0:NV_NOFAIL)); + if(!nr) + { + if(!nv_isvtree(np)) + _nv_unset(np,0); + return(0); + } + if(!mp && index>=0 && nv_isvtree(nr)) + { + sfprintf(shp->strbuf,"%s[%d]%c",nv_name(np),index,0); + /* create a virtual node */ + if(mp = nv_open(sfstruse(shp->strbuf),shp->var_tree,NV_VARNAME|NV_ADD|NV_ARRAY)) + mp->nvenv = (void*)np; + } + if(mp) + np = mp; + if(nr==np) + { + if(index<0) + return(0); + if(cp = nv_getval(np)) + cp = strdup(cp); + } + _nv_unset(np,0); + if(nr==np) + { + nv_putsub(np,(char*)0, index); + nv_putval(np,cp,0); + free((void*)cp); + return(1); + } + shp->prev_table = shp->last_table; + shp->prev_root = shp->last_root; + shp->last_table = last_table; + shp->last_root = last_root; + nv_clone(nr,np,(flags&NV_MOVE)|NV_COMVAR); + if(flags&NV_MOVE) + nv_delete(nr,(Dt_t*)0,NV_NOFREE); + return(1); } /* @@ -2156,28 +2890,34 @@ static char *lastdot(register char *cp) */ void nv_setref(register Namval_t *np, Dt_t *hp, int flags) { - register Namval_t *nq, *nr; + Shell_t *shp = &sh; + register Namval_t *nq, *nr=0; register char *ep,*cp; if(nv_isref(np)) return; if(nv_isarray(np)) errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np)); if(!(cp=nv_getval(np))) - errormsg(SH_DICT,ERROR_exit(1),e_noref,nv_name(np)); - if((ep = lastdot(cp)) && nv_isattr(np,NV_MINIMAL)) - errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np)); - if(!hp) - hp = sh.var_tree; - nr= nq = nv_open(cp, hp, flags|NV_NOREF); - while(nv_isref(nr)) { - sh.last_table = nv_reftable(nr); - hp = nv_reftree(nr); - nr = nv_refnode(nr); + nv_unset(np); + nv_onattr(np,NV_REF); + return; } + if((ep = lastdot(cp,0)) && nv_isattr(np,NV_MINIMAL)) + errormsg(SH_DICT,ERROR_exit(1),e_badref,nv_name(np)); + if(!hp) + hp = shp->var_tree; + if(!(nr = nq = nv_open(cp, hp, flags|NV_NOSCOPE|NV_NOADD|NV_NOFAIL))) + hp = shp->var_base; + else if(shp->last_root) + hp = shp->last_root; + if(nq && ep && nv_isarray(nq) && !nv_getsub(nq)) + nv_endsubscript(nq,ep-1,NV_ADD); + if(!nr) + nr= nq = nv_open(cp, hp, flags); if(nr==np) { - if(sh.namespace && nv_dict(sh.namespace)==hp) + if(shp->namespace && nv_dict(shp->namespace)==hp) errormsg(SH_DICT,ERROR_exit(1),e_selfref,nv_name(np)); /* bind to earlier scope, or add to global scope */ if(!(hp=dtvnext(hp)) || (nq=nv_search((char*)np,hp,NV_ADD|HASH_BUCKET))==np) @@ -2186,18 +2926,27 @@ void nv_setref(register Namval_t *np, Dt_t *hp, int flags) if(ep) { /* cause subscript evaluation and return result */ -#if 0 - nv_endsubscript(nq,ep,NV_ADD); -#endif - ep = nv_getsub(nq); + if(nv_isarray(nq)) + ep = nv_getsub(nq); + else + { + ep[strlen(ep)-1] = 0; + nv_putsub(nr, ep, 0); + ep[strlen(ep)-1] = ']'; + if(nq = nv_opensub(nr)) + ep = 0; + else + nq = nr; + } } nv_unset(np); + nv_delete(np,(Dt_t*)0,0); np->nvalue.nrp = newof(0,struct Namref,1,0); np->nvalue.nrp->np = nq; np->nvalue.nrp->root = hp; if(ep) np->nvalue.nrp->sub = strdup(ep); - np->nvalue.nrp->table = sh.last_table; + np->nvalue.nrp->table = shp->last_table; nv_onattr(np,NV_REF|NV_NOFREE); } @@ -2241,15 +2990,22 @@ Shscope_t *sh_setscope(Shscope_t *scope) *sh.st.self = sh.st; sh.st = *((struct sh_scoped*)scope); sh.var_tree = scope->var_tree; + SH_PATHNAMENOD->nvalue.cp = sh.st.filename; + SH_FUNNAMENOD->nvalue.cp = sh.st.funname; return(old); } -void nv_unscope(void) +void sh_unscope(Shell_t *shp) { - register Dt_t *root = sh.var_tree; + register Dt_t *root = shp->var_tree; register Dt_t *dp = dtview(root,(Dt_t*)0); - table_unset(root,NV_RDONLY|NV_NOSCOPE,dp); - sh.var_tree=dp; + table_unset(shp,root,NV_RDONLY|NV_NOSCOPE,dp); + if(shp->st.real_fun && dp==shp->st.real_fun->sdict) + { + dp = dtview(dp,(Dt_t*)0); + shp->st.real_fun->sdict->view = dp; + } + shp->var_tree=dp; dtclose(root); } @@ -2328,6 +3084,18 @@ char *nv_name(register Namval_t *np) char *cp; if(is_abuiltin(np) || is_afunction(np)) return(np->nvname); + if(!nv_isattr(np,NV_MINIMAL|NV_EXPORT) && np->nvenv) + { + Namval_t *nq= sh.last_table, *mp= (Namval_t*)np->nvenv; + if(np==sh.last_table) + sh.last_table = 0; + if(nv_isarray(mp)) + sfprintf(sh.strbuf,"%s[%s]",nv_name(mp),np->nvname); + else + sfprintf(sh.strbuf,"%s.%s",nv_name(mp),np->nvname); + sh.last_table = nq; + return(sfstruse(sh.strbuf)); + } if(nv_istable(np)) #if 1 sh.last_table = nv_parent(np); @@ -2380,3 +3148,22 @@ int nv_setsize(register Namval_t *np, int size) np->nvsize = size; return(oldsize); } + +Shell_t *nv_shell(Namval_t *np) +{ + Namfun_t *fp; + for(fp=np->nvfun;fp;fp=fp->next) + { + if(!fp->disc) + return((Shell_t*)fp->last); + } + return(0); +} + +#undef nv_unset + +void nv_unset(register Namval_t *np) +{ + _nv_unset(np,0); + return; +} diff --git a/usr/src/lib/libshell/common/sh/nvdisc.c b/usr/src/lib/libshell/common/sh/nvdisc.c index 533f5f7f5c..bcf987b9a6 100644 --- a/usr/src/lib/libshell/common/sh/nvdisc.c +++ b/usr/src/lib/libshell/common/sh/nvdisc.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -28,8 +28,6 @@ #include "builtins.h" #include "path.h" -static const char *discnames[] = { "get", "set", "append", "unset", 0 }; - int nv_compare(Dt_t* dict, Void_t *sp, Void_t *dp, Dtdisc_t *disc) { if(sp==dp) @@ -82,14 +80,14 @@ Sfdouble_t nv_getn(Namval_t *np, register Namfun_t *nfp) nv_local=0; for(; fp; fp=fp->next) { - if(!fp->disc->getnum && !fp->disc->getval) + if(!fp->disc || (!fp->disc->getnum && !fp->disc->getval)) continue; if(!fp->disc->getnum && nv_isattr(np,NV_INTEGER)) continue; if(!nv_isattr(np,NV_NODISC) || fp==(Namfun_t*)nv_arrayptr(np)) break; } - if(fp && fp->disc->getnum) + if(fp && fp->disc && fp->disc->getnum) d = (*fp->disc->getnum)(np,fp); else if(nv_isattr(np,NV_INTEGER)) { @@ -98,7 +96,7 @@ Sfdouble_t nv_getn(Namval_t *np, register Namfun_t *nfp) } else { - if(fp && fp->disc->getval) + if(fp && fp->disc && fp->disc->getval) str = (*fp->disc->getval)(np,fp); else str = nv_getv(np,fp?fp:nfp); @@ -121,15 +119,18 @@ void nv_putv(Namval_t *np, const char *value, int flags, register Namfun_t *nfp) if((fp=nfp) != NIL(Namfun_t*) && !nv_local) fp = nfp = nfp->next; nv_local=0; + if(flags&NV_NODISC) + fp = 0; for(; fp; fp=fpnext) { fpnext = fp->next; - if(!fp->disc->putval) + if(!fp->disc || !fp->disc->putval) { if(!value) { - nv_disc(np,fp,NV_POP); - if(!fp->nofree) + if(fp->disc || !(fp->nofree&1)) + nv_disc(np,fp,NV_POP); + if(!(fp->nofree&1)) free((void*)fp); } continue; @@ -145,25 +146,10 @@ void nv_putv(Namval_t *np, const char *value, int flags, register Namfun_t *nfp) if(value) nv_putval(np, value, flags); else - _nv_unset(np, flags&NV_RDONLY); + _nv_unset(np, flags&(NV_RDONLY|NV_EXPORT)); } } -#if 0 -/* - * node creation discipline - */ -Namval_t *nv_create(register Namval_t* np,const char *name,int flag,register Namfun_t *fp) -{ - fp = fp?fp->next:np->nvfun; - while(fp && fp->disc && !fp->disc->createf) - fp = fp->next; - if(fp && fp->disc->createf) - return((*fp->disc->createf)(np,name,flag,fp)); - return(NIL(Namval_t*)); -} -#endif - #define LOOKUP 0 #define ASSIGN 1 #define APPEND 2 @@ -240,7 +226,7 @@ static void chktfree(register Namval_t *np, register struct vardisc *vp) { /* no disc left so pop */ Namfun_t *fp; - if((fp=nv_stack(np, NIL(Namfun_t*))) && !fp->nofree) + if((fp=nv_stack(np, NIL(Namfun_t*))) && !(fp->nofree&1)) free((void*)fp); } } @@ -255,6 +241,23 @@ static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle) register Namval_t *nq = vp->disc[type]; struct blocked block, *bp = block_info(np, &block); Namval_t node; + union Value *up = np->nvalue.up; +#if SHOPT_TYPEDEF + Namval_t *tp, *nr; + if(val && (tp=nv_type(np)) && (nr=nv_open(val,sh.var_tree,NV_VARNAME|NV_ARRAY|NV_NOADD|NV_NOFAIL)) && tp==nv_type(nr)) + { + char *sub = nv_getsub(np); + nv_unset(np); + if(sub) + { + nv_putsub(np, sub, ARRAY_ADD); + nv_putval(np,nv_getval(nr), 0); + } + else + nv_clone(nr,np,0); + goto done; + } +#endif /* SHOPT_TYPEDEF */ if(val || isblocked(bp,type)) { if(!nq || isblocked(bp,type)) @@ -269,7 +272,7 @@ static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle) nv_unset(SH_VALNOD); } if(flags&NV_INTEGER) - nv_onattr(SH_VALNOD,(flags&(NV_INTEGER|NV_LONG|NV_DOUBLE|NV_EXPNOTE|NV_SHORT))); + nv_onattr(SH_VALNOD,(flags&(NV_LONG|NV_DOUBLE|NV_EXPNOTE|NV_HEXFLOAT|NV_SHORT))); nv_putval(SH_VALNOD, val, (flags&NV_INTEGER)?flags:NV_NOFREE); } else @@ -287,6 +290,8 @@ static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle) if(!vp->disc[type]) chktfree(np,vp); } + if(nv_isarray(np)) + np->nvalue.up = up; if(val) { register char *cp; @@ -320,6 +325,8 @@ static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle) Namarr_t *ap; block(bp,type); nv_putv(np, val, flags, handle); + if(sh.subshell) + goto done; if(nv_isarray(np) && (ap=nv_arrayptr(np)) && ap->nelem>0) goto done; for(n=0; n < sizeof(vp->disc)/sizeof(*vp->disc); n++) @@ -332,7 +339,7 @@ static void assign(Namval_t *np,const char* val,int flags,Namfun_t *handle) } unblock(bp,type); nv_disc(np,handle,NV_POP); - if(!handle->nofree) + if(!(handle->nofree&1)) free(handle); } done: @@ -351,6 +358,7 @@ static char* lookup(Namval_t *np, Namfun_t *handle) register Namval_t *nq = vp->disc[LOOKUP]; register char *cp=0; Namval_t node; + union Value *up = np->nvalue.up; if(nq && !isblocked(bp,LOOKUP)) { node = *SH_VALNOD; @@ -364,15 +372,19 @@ static char* lookup(Namval_t *np, Namfun_t *handle) unblock(bp,LOOKUP); if(!vp->disc[LOOKUP]) chktfree(np,vp); - cp = nv_getval(SH_VALNOD); + if(cp = nv_getval(SH_VALNOD)) + { + cp = stkcopy(stkstd,cp); + _nv_unset(SH_VALNOD,NV_RDONLY); + } if(!nv_isnull(&node)) { - if(cp) - cp = strdup(cp); /* restore everything but the nvlink field */ memcpy(&SH_VALNOD->nvname, &node.nvname, sizeof(node)-sizeof(node.nvlink)); } } + if(nv_isarray(np)) + np->nvalue.up = up; if(!cp) cp = nv_getv(np,handle); if(bp== &block) @@ -380,6 +392,7 @@ static char* lookup(Namval_t *np, Namfun_t *handle) return(cp); } + static const Namdisc_t shdisc = { sizeof(struct vardisc), @@ -399,6 +412,8 @@ char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *acti register struct vardisc *vp = (struct vardisc*)np->nvfun; register int type; char *empty = ""; + if(vp && !vp->fun.disc) + vp = 0; if(np == (Namval_t*)fp) { register const char *name; @@ -407,11 +422,11 @@ char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *acti if(!event) { if(!action) - return((char*)discnames[0]); + return((char*)nv_discnames[0]); getname=1; event = (char*)action; } - for(type=0; name=discnames[type]; type++) + for(type=0; name=nv_discnames[type]; type++) { if(strcmp(event,name)==0) break; @@ -419,13 +434,16 @@ char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *acti if(getname) { event = 0; - if(name && !(name = discnames[++type])) + if(name && !(name = nv_discnames[++type])) action = 0; } if(!name) { - if((fp=(Namfun_t*)vp) && fp->disc->setdisc) - return((*fp->disc->setdisc)(np,event,action,fp)); + for(fp=(Namfun_t*)vp; fp; fp=fp->next) + { + if(fp->disc && fp->disc->setdisc) + return((*fp->disc->setdisc)(np,event,action,fp)); + } } else if(getname) return((char*)name); @@ -437,7 +455,7 @@ char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *acti /* not the top level */ while(fp = fp->next) { - if(fp->disc->setdisc) + if(fp->disc && fp->disc->setdisc) return((*fp->disc->setdisc)(np,event,action,fp)); } return(NIL(char*)); @@ -472,7 +490,6 @@ char *nv_setdisc(register Namval_t* np,register const char *event,Namval_t *acti return(action?(char*)action:empty); } - /* * Set disc on given <event> to <action> * If action==np, the current disc is returned @@ -525,7 +542,7 @@ static char *setdisc(register Namval_t* np,register const char *event,Namval_t * static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp) { nv_putv(np,val,flag,fp); - if(!val) + if(!val && !(flag&NV_NOFREE)) { register Nambfun_t *vp = (Nambfun_t*)fp; register int i; @@ -544,7 +561,7 @@ static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp) } } nv_disc(np,fp,NV_POP); - if(!fp->nofree) + if(!(fp->nofree&1)) free((void*)fp); } @@ -552,7 +569,7 @@ static void putdisc(Namval_t* np, const char* val, int flag, Namfun_t* fp) static const Namdisc_t Nv_bdisc = { 0, putdisc, 0, 0, setdisc }; -static Namfun_t *nv_clone_disc(register Namfun_t *fp) +Namfun_t *nv_clone_disc(register Namfun_t *fp, int flags) { register Namfun_t *nfp; register int size; @@ -561,7 +578,9 @@ static Namfun_t *nv_clone_disc(register Namfun_t *fp) if(!(nfp=newof(NIL(Namfun_t*),Namfun_t,1,size-sizeof(Namfun_t)))) return(0); memcpy(nfp,fp,size); - nfp->nofree = 0; + if(flags&NV_COMVAR) + nfp->nofree &= ~1; + nfp->nofree |= (flags&NV_RDONLY)?1:0; return(nfp); } @@ -578,7 +597,8 @@ int nv_adddisc(Namval_t *np, const char **names, Namval_t **funs) if(!(vp = newof(NIL(Nambfun_t*),Nambfun_t,1,n*sizeof(Namval_t*)))) return(0); vp->fun.dsize = sizeof(Nambfun_t)+n*sizeof(Namval_t*); - vp->fun.funs = 1; + vp->fun.nofree |= 2; + vp->num = n; if(funs) memcpy((void*)vp->bltins, (void*)funs,n*sizeof(Namval_t*)); else while(n>=0) @@ -590,7 +610,7 @@ int nv_adddisc(Namval_t *np, const char **names, Namval_t **funs) } /* - * push, pop, clone, or reorder disciplines onto node <np> + * push, pop, clne, or reorder disciplines onto node <np> * mode can be one of * NV_FIRST: Move or push <fp> to top of the stack or delete top * NV_LAST: Move or push <fp> to bottom of stack or delete last @@ -606,11 +626,12 @@ Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode) return(0); if(fp) { + fp->subshell = sh.subshell; if((lp=np->nvfun)==fp) { if(mode==NV_CLONE) { - lp = nv_clone_disc(fp); + lp = nv_clone_disc(fp,0); return(np->nvfun=lp); } if(mode==NV_FIRST || mode==0) @@ -618,18 +639,22 @@ Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode) np->nvfun = lp->next; if(mode==NV_POP) return(fp); + if(mode==NV_LAST && (lp->next==0 || lp->next->disc==0)) + return(fp); } /* see if <fp> is on the list already */ lpp = &np->nvfun; if(lp) { - while(lp->next) + while(lp->next && lp->next->disc) { if(lp->next==fp) { + if(mode==NV_LAST && fp->next==0) + return(fp); if(mode==NV_CLONE) { - fp = nv_clone_disc(fp); + fp = nv_clone_disc(fp,0); lp->next = fp; return(fp); } @@ -652,8 +677,8 @@ Namfun_t *nv_disc(register Namval_t *np, register Namfun_t* fp, int mode) fp->next = 0; else { - if(fp->nofree && *lpp) - fp = nv_clone_disc(fp); + if((fp->nofree&1) && *lpp) + fp = nv_clone_disc(fp,0); fp->next = *lpp; } *lpp = fp; @@ -698,7 +723,7 @@ static void put_notify(Namval_t* np,const char *val,int flags,Namfun_t *fp) nv_stack(np,fp); nv_stack(np,(Namfun_t*)0); *pp->ptr = 0; - if(!fp->nofree) + if(!(fp->nofree&1)) free((void*)fp); } @@ -713,7 +738,7 @@ int nv_unsetnotify(Namval_t *np, char **addr) { nv_stack(np,fp); nv_stack(np,(Namfun_t*)0); - if(!fp->nofree) + if(!(fp->nofree&1)) free((void*)fp); return(1); } @@ -754,7 +779,7 @@ static void *num_clone(register Namval_t *np, void *val) void *nval; if(!val) return(0); - if(nv_isattr(np,NV_DOUBLE)) + if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE) { if(nv_isattr(np,NV_LONG)) size = sizeof(Sfdouble_t); @@ -768,7 +793,12 @@ static void *num_clone(register Namval_t *np, void *val) if(nv_isattr(np,NV_LONG)) size = sizeof(Sflong_t); else if(nv_isattr(np,NV_SHORT)) - size = sizeof(int16_t); + { + if(nv_isattr(np,NV_INT16P)==NV_INT16P) + size = sizeof(short); + else + return((void*)np->nvalue.ip); + } else size = sizeof(int32_t); } @@ -778,17 +808,22 @@ static void *num_clone(register Namval_t *np, void *val) return(nval); } -static void clone_all_disc( Namval_t *np, Namval_t *mp, int flags) +void clone_all_disc( Namval_t *np, Namval_t *mp, int flags) { - register Namfun_t *fp, **mfp = &mp->nvfun, *nfp; - for(fp=np->nvfun; fp;fp=fp->next) + register Namfun_t *fp, **mfp = &mp->nvfun, *nfp, *fpnext; + for(fp=np->nvfun; fp;fp=fpnext) { - if(fp->funs && (flags&NV_NODISC)) + fpnext = fp->next; + if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef) + return; + if((fp->nofree&2) && (flags&NV_NODISC)) nfp = 0; if(fp->disc && fp->disc->clonef) nfp = (*fp->disc->clonef)(np,mp,flags,fp); + else if(flags&NV_MOVE) + nfp = fp; else - nfp = nv_clone_disc(fp); + nfp = nv_clone_disc(fp,flags); if(!nfp) continue; nfp->next = 0; @@ -803,38 +838,69 @@ static void clone_all_disc( Namval_t *np, Namval_t *mp, int flags) * NV_MOVE - move <np> to <mp> * NV_NOFREE - mark the new node as nofree * NV_NODISC - discplines with funs non-zero will not be copied + * NV_COMVAR - cloning a compound variable */ int nv_clone(Namval_t *np, Namval_t *mp, int flags) { - Namfun_t *fp; + Namfun_t *fp, *fpnext; + const char *val = mp->nvalue.cp; + unsigned short flag = mp->nvflag; + unsigned short size = mp->nvsize; + for(fp=mp->nvfun; fp; fp=fpnext) + { + fpnext = fp->next; + if(!fpnext && (flags&NV_COMVAR) && fp->disc && fp->disc->namef) + break; + if(!(fp->nofree&1)) + free((void*)fp); + } + mp->nvfun = fp; if(fp=np->nvfun) { - if(flags&NV_MOVE) + if(nv_isattr(mp,NV_EXPORT|NV_MINIMAL) == (NV_EXPORT|NV_MINIMAL)) { - mp->nvfun = fp; - goto skip; + mp->nvenv = 0; + nv_offattr(mp,NV_MINIMAL); } + if(!(flags&NV_COMVAR) && !nv_isattr(np,NV_MINIMAL) && np->nvenv && !(nv_isattr(mp,NV_MINIMAL))) + mp->nvenv = np->nvenv; + mp->nvflag &= NV_MINIMAL; + mp->nvflag |= np->nvflag&~(NV_ARRAY|NV_MINIMAL|NV_NOFREE); + flag = mp->nvflag; clone_all_disc(np, mp, flags); } if(flags&NV_APPEND) return(1); -skip: - nv_setsize(mp,nv_size(np)); - if(!nv_isattr(mp,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT)) - mp->nvenv = (!nv_isattr(np,NV_MINIMAL)||nv_isattr(np,NV_EXPORT))?np->nvenv:0; - mp->nvalue.cp = np->nvalue.cp; - mp->nvflag = np->nvflag; + if(mp->nvsize == size) + nv_setsize(mp,nv_size(np)); + if(mp->nvflag == flag) + mp->nvflag = (np->nvflag&~(NV_MINIMAL))|(mp->nvflag&NV_MINIMAL); + if(mp->nvalue.cp==val && !nv_isattr(np,NV_INTEGER)) + { + if(np->nvalue.cp && np->nvalue.cp!=Empty && (flags&NV_COMVAR) && !(flags&NV_MOVE)) + { + if(size) + mp->nvalue.cp = (char*)memdup(np->nvalue.cp,size); + else + mp->nvalue.cp = strdup(np->nvalue.cp); + nv_offattr(mp,NV_NOFREE); + } + else if(!(mp->nvalue.cp = np->nvalue.cp)) + nv_offattr(mp,NV_NOFREE); + } if(flags&NV_MOVE) { + if(nv_isattr(np,NV_INTEGER)) + mp->nvalue.ip = np->nvalue.ip; np->nvfun = 0; np->nvalue.cp = 0; if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(mp,NV_EXPORT)) np->nvenv = 0; - np->nvflag = 0; + np->nvflag &= NV_MINIMAL; nv_setsize(np,0); return(1); } - if(nv_isattr(np,NV_INTEGER)) + if(nv_isattr(np,NV_INTEGER) && mp->nvalue.ip!=np->nvalue.ip) mp->nvalue.ip = (int*)num_clone(np,(void*)np->nvalue.ip); else if(flags&NV_NOFREE) nv_onattr(np,NV_NOFREE); @@ -912,7 +978,6 @@ Namval_t *nv_search(const char *name, Dt_t *root, int mode) } if(!np && (mode&NV_ADD)) { - if(sh.namespace && !(mode&HASH_NOSCOPE) && root==sh.var_tree) root = nv_dict(sh.namespace); else if(!dp && !(mode&HASH_NOSCOPE)) @@ -939,9 +1004,10 @@ Namval_t *nv_search(const char *name, Dt_t *root, int mode) */ Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last) { - int offset = staktell(); + int c,offset = staktell(); register char *sp, *cp=0; Namval_t *np, *nq; + char *dname=0; if(var) *var = 0; /* check for . in the name before = */ @@ -949,19 +1015,42 @@ Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last) { if(*sp=='=') return(0); - if(*sp=='.') + if(*sp=='[') + { + if(sp[-1]!='.') + dname = sp; + while(*sp=='[') + { + sp = nv_endsubscript((Namval_t*)0,(char*)sp,0); + if(sp[-1]!=']') + return(0); + } + if(*sp==0) + break; + if(*sp!='.') + return(0); + if(dname) + { + cp = dname; + dname = sp+1; + } + } + else if(*sp=='.') cp = sp; } if(!cp) return(var?nv_search(name,root,0):0); stakputs(name); stakputc(0); + if(!dname) + dname = cp+1; cp = stakptr(offset) + (cp-name); if(last) *last = cp; + c = *cp; *cp = 0; - nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_NOASSIGN|NV_NOADD|NV_NOFAIL); - *cp = '.'; + nq=nv_open(stakptr(offset),0,NV_VARNAME|NV_ARRAY|NV_NOASSIGN|NV_NOADD|NV_NOFAIL); + *cp = c; if(!nq) { np = 0; @@ -973,14 +1062,16 @@ Namval_t *nv_bfsearch(const char *name, Dt_t *root, Namval_t **var, char **last) goto done; } *var = nq; - return((Namval_t*)nv_setdisc(nq,cp+1,nq,(Namfun_t*)nq)); + if(c=='[') + nv_endsubscript(nq, cp,NV_NOADD); + return((Namval_t*)nv_setdisc(nq,dname,nq,(Namfun_t*)nq)); done: stakseek(offset); return(np); } /* - * add or replace built-in version of command commresponding to <path> + * add or replace built-in version of command corresponding to <path> * The <bltin> argument is a pointer to the built-in * if <extra>==1, the built-in will be deleted * Special builtins cannot be added or deleted return failure @@ -990,10 +1081,10 @@ done: */ Namval_t *sh_addbuiltin(const char *path, int (*bltin)(int, char*[],void*),void *extra) { - register const char *name = path_basename(path); - char *cp; - register Namval_t *np, *nq=0; - int offset = staktell(); + register const char *name = path_basename(path); + char *cp; + register Namval_t *np, *nq=0; + int offset=staktell(); if(name==path && (nq=nv_bfsearch(name,sh.bltin_tree,(Namval_t**)0,&cp))) path = name = stakptr(offset); if(np = nv_search(path,sh.bltin_tree,0)) @@ -1031,10 +1122,13 @@ Namval_t *sh_addbuiltin(const char *path, int (*bltin)(int, char*[],void*),void if(!np && !(np = nv_search(path,sh.bltin_tree,bltin?NV_ADD:0))) return(0); if(nv_isattr(np,BLT_SPC)) + { + if(extra) + np->nvfun = (Namfun_t*)extra; return(np); + } np->nvenv = 0; np->nvfun = 0; - nv_setattr(np,0); if(bltin) { np->nvalue.bfp = bltin; @@ -1067,13 +1161,6 @@ struct table Dt_t *dict; }; -Namval_t *nv_parent(Namval_t *np) -{ - if(!nv_istable(np)) - return(0); - return(((struct table*)(np->nvfun))->parent); -} - static Namval_t *next_table(register Namval_t* np, Dt_t *root,Namfun_t *fp) { struct table *tp = (struct table *)fp; @@ -1093,7 +1180,7 @@ static Namval_t *create_table(Namval_t *np,const char *name,int flags,Namfun_t * static Namfun_t *clone_table(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) { struct table *tp = (struct table*)fp; - struct table *ntp = (struct table*)nv_clone_disc(fp); + struct table *ntp = (struct table*)nv_clone_disc(fp,0); Dt_t *oroot=tp->dict,*nroot=dtopen(&_Nvdisc,Dtoset); if(!nroot) return(0); @@ -1126,7 +1213,7 @@ static void put_table(register Namval_t* np, const char* val, int flags, Namfun_ free((void*)mp); } dtclose(root); - if(!fp->nofree) + if(!(fp->nofree&1)) free((void*)fp); } @@ -1173,6 +1260,14 @@ static const Namdisc_t table_disc = next_table, }; +Namval_t *nv_parent(Namval_t *np) +{ + struct table *tp = (struct table *)nv_hasdisc(np,&table_disc); + if(tp) + return(tp->parent); + return(0); +} + Dt_t *nv_dict(Namval_t* np) { struct table *tp = (struct table*)nv_hasdisc(np,&table_disc); @@ -1185,6 +1280,8 @@ Dt_t *nv_dict(Namval_t* np) return(tp->dict); #if 0 np = nv_create(np,(const char*)0, NV_FIRST, (Namfun_t*)0); +#else + break; #endif } return(sh.var_tree); @@ -1220,7 +1317,7 @@ Namval_t *nv_mount(Namval_t *np, const char *name, Dt_t *dict) tp->parent = pp; tp->fun.disc = &table_disc; nv_onattr(mp,NV_TABLE); - nv_disc(mp, &tp->fun, NV_LAST); + nv_disc(mp, &tp->fun, NV_FIRST); return(mp); } @@ -1236,28 +1333,3 @@ const Namdisc_t *nv_discfun(int which) return(0); } -/* - * This function turns variable <np> to the type <tp> - */ -int nv_settype(Namval_t* np, Namval_t *tp, int flags) -{ - int isnull = nv_isnull(np); - char *val=0; - if(isnull) - flags &= ~NV_APPEND; - else - { - val = strdup(nv_getval(np)); - if(!(flags&NV_APPEND)) - _nv_unset(np, NV_RDONLY); - } - if(!nv_clone(tp,np,flags|NV_NOFREE)) - return(0); - if(val) - { - nv_putval(np,val,NV_RDONLY); - free((void*)val); - } - return(0); -} - diff --git a/usr/src/lib/libshell/common/sh/nvtree.c b/usr/src/lib/libshell/common/sh/nvtree.c index 82657e3a0c..d493b5d602 100644 --- a/usr/src/lib/libshell/common/sh/nvtree.c +++ b/usr/src/lib/libshell/common/sh/nvtree.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -30,22 +30,41 @@ #include "defs.h" #include "name.h" #include "argnod.h" +#include "lexstates.h" struct nvdir { Dt_t *root; Namval_t *hp; Namval_t *table; + Namval_t *otable; Namval_t *(*nextnode)(Namval_t*,Dt_t*,Namfun_t*); Namfun_t *fun; struct nvdir *prev; int len; - int offset; char data[1]; }; char *nv_getvtree(Namval_t*, Namfun_t *); static void put_tree(Namval_t*, const char*, int,Namfun_t*); +static char *walk_tree(Namval_t*, Namval_t*, int); + +static int read_tree(Namval_t* np, Sfio_t *iop, int n, Namfun_t *dp) +{ + Sfio_t *sp; + char *cp; + int c; + if(n>=0) + return(-1); + while((c = sfgetc(iop)) && isblank(c)); + sfungetc(iop,c); + sfprintf(sh.strbuf,"%s=%c",nv_name(np),0); + cp = sfstruse(sh.strbuf); + sp = sfopen((Sfio_t*)0,cp,"s"); + sfstack(iop,sp); + c=sh_eval(iop,SH_READEVAL); + return(c); +} static Namval_t *create_tree(Namval_t *np,const char *name,int flag,Namfun_t *dp) { @@ -62,6 +81,20 @@ static Namval_t *create_tree(Namval_t *np,const char *name,int flag,Namfun_t *dp return((flag&NV_NOADD)?0:np); } +static Namfun_t *clone_tree(Namval_t *np, Namval_t *mp, int flags, Namfun_t *fp){ + Namfun_t *dp; + if ((flags&NV_MOVE) && nv_type(np)) + return(fp); + dp = nv_clone_disc(fp,flags); + if((flags&NV_COMVAR) && !(flags&NV_RAW)) + { + walk_tree(np,mp,flags); + if((flags&NV_MOVE) && !(fp->nofree&1)) + free((void*)fp); + } + return(dp); +} + static const Namdisc_t treedisc = { 0, @@ -69,21 +102,29 @@ static const Namdisc_t treedisc = nv_getvtree, 0, 0, - create_tree + create_tree, + clone_tree + ,0,0,0, + read_tree }; static char *nextdot(const char *str) { register char *cp; + register int c; if(*str=='.') str++; - if(*str =='[') + for(cp=(char*)str;c= *cp; cp++) { - cp = nv_endsubscript((Namval_t*)0,(char*)str,0); - return(*cp=='.'?cp:0); + if(c=='[') + { + cp = nv_endsubscript((Namval_t*)0,(char*)cp,0); + return(*cp=='.'?cp:0); + } + if(c=='.') + return(cp); } - else - return(strchr(str,'.')); + return(0); } static Namfun_t *nextdisc(Namval_t *np) @@ -99,38 +140,90 @@ static Namfun_t *nextdisc(Namval_t *np) return(0); } -void *nv_diropen(const char *name) +void *nv_diropen(Namval_t *np,const char *name) { char *next,*last; int c,len=strlen(name); struct nvdir *save, *dp = new_of(struct nvdir,len); - Namval_t *np, fake; - Namfun_t *nfp; + Namval_t *nq=0,fake; + Namfun_t *nfp=0; if(!dp) return(0); memset((void*)dp, 0, sizeof(*dp)); - last=dp->data; if(name[len-1]=='*' || name[len-1]=='@') len -= 1; - name = memcpy(last,name,len); - last[len] = 0; + name = memcpy(dp->data,name,len); + dp->data[len] = 0; dp->len = len; - dp->root = sh.var_tree; + dp->root = sh.last_root?sh.last_root:sh.var_tree; +#if 1 + while(1) + { + dp->table = sh.last_table; + sh.last_table = 0; + if(*(last=(char*)name)==0) + break; + if(!(next=nextdot(last))) + break; + *next = 0; + np = nv_open(name, dp->root, NV_NOFAIL); + *next = '.'; + if(!np || !nv_istable(np)) + break; + dp->root = nv_dict(np); + name = next+1; + } +#else dp->table = sh.last_table; + sh.last_table = 0; + last = dp->data; +#endif if(*name) { fake.nvname = (char*)name; - dp->hp = (Namval_t*)dtprev(dp->root,&fake); - dp->hp = (Namval_t*)dtnext(dp->root,dp->hp); + if(dp->hp = (Namval_t*)dtprev(dp->root,&fake)) + { + char *cp = nv_name(dp->hp); + c = strlen(cp); + if(memcmp(name,cp,c) || name[c]!='[') + dp->hp = (Namval_t*)dtnext(dp->root,dp->hp); + else + { + np = dp->hp; + last = 0; + } + } } else dp->hp = (Namval_t*)dtfirst(dp->root); - while(next= nextdot(last)) + while(1) { - c = *next; - *next = 0; - np = nv_search(last,dp->root,0); - *next = c; + if(!last) + next = 0; + else if(next= nextdot(last)) + { + c = *next; + *next = 0; + } + if(!np) + { + if(nfp && nfp->disc && nfp->disc->createf) + { + np = (*nfp->disc->createf)(nq,last,0,nfp); + if(*nfp->last == '[') + { + nv_endsubscript(np,nfp->last,NV_NOADD); + if(nq = nv_opensub(np)) + np = nq; + } + } + else + np = nv_search(last,dp->root,0); + } + if(next) + *next = c; + if(np==dp->hp && !next) + dp->hp = (Namval_t*)dtnext(dp->root,dp->hp); if(np && ((nfp=nextdisc(np)) || nv_istable(np))) { if(!(save = new_of(struct nvdir,0))) @@ -140,16 +233,12 @@ void *nv_diropen(const char *name) if(nv_istable(np)) dp->root = nv_dict(np); else - dp->root = (Dt_t*)dp; - dp->offset = last-(char*)name; - if(dp->offset<len) - dp->len = len-dp->offset; - else - dp->len = 0; + dp->root = (Dt_t*)np; if(nfp) { dp->nextnode = nfp->disc->nextf; dp->table = np; + dp->otable = sh.last_table; dp->fun = nfp; dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp); } @@ -158,7 +247,11 @@ void *nv_diropen(const char *name) } else break; + if(!next || next[1]==0) + break; last = next+1; + nq = np; + np = 0; } return((void*)dp); } @@ -179,26 +272,51 @@ char *nv_dirnext(void *dir) register Namval_t *np, *last_table; register char *cp; Namfun_t *nfp; + Namval_t *nq; while(1) { while(np=dp->hp) { +#if 0 + char *sptr; +#endif + if(nv_isarray(np)) + nv_putsub(np,(char*)0, ARRAY_UNDEF); dp->hp = nextnode(dp); - if(nv_isnull(np)) + if(nv_isnull(np) && !nv_isarray(np)) continue; last_table = sh.last_table; +#if 0 + if(dp->table && dp->otable && !nv_isattr(dp->table,NV_MINIMAL)) + { + sptr = dp->table->nvenv; + dp->table->nvenv = (char*)dp->otable; + } +#endif sh.last_table = dp->table; cp = nv_name(np); +#if 0 + if(dp->table && dp->otable && !nv_isattr(dp->table,NV_MINIMAL)) + dp->table->nvenv = sptr; +#endif + if(dp->nextnode && !dp->hp && (nq = (Namval_t*)dp->table)) + { + Namarr_t *ap = nv_arrayptr(nq); + if(ap && (ap->nelem&ARRAY_SCAN) && nv_nextsub(nq)) + dp->hp = (*dp->nextnode)(np,(Dt_t*)0,dp->fun); + } sh.last_table = last_table; - if(!dp->len || memcmp(cp+dp->offset,dp->data,dp->len)==0) + if(!dp->len || memcmp(cp,dp->data,dp->len)==0) { - if((nfp=nextdisc(np)) || nv_istable(np)) + if((nfp=nextdisc(np)) && (nfp->disc->getval||nfp->disc->getnum) && nv_isvtree(np) && strcmp(cp,dp->data)) + nfp = 0; + if(nfp || nv_istable(np)) { Dt_t *root; if(nv_istable(np)) root = nv_dict(np); else - root = (Dt_t*)dp; + root = (Dt_t*)np; /* check for recursive walk */ for(save=dp; save; save=save->prev) { @@ -206,7 +324,7 @@ char *nv_dirnext(void *dir) break; } if(save) - continue; + return(cp); if(!(save = new_of(struct nvdir,0))) return(0); *save = *dp; @@ -215,7 +333,13 @@ char *nv_dirnext(void *dir) dp->len = 0; if(nfp && np->nvfun) { +#if 0 + Namarr_t *ap = nv_arrayptr(np); + if(ap && (ap->nelem&ARRAY_UNDEF)) + nv_putsub(np,(char*)0,ARRAY_SCAN); +#endif dp->nextnode = nfp->disc->nextf; + dp->otable = dp->table; dp->table = np; dp->fun = nfp; dp->hp = (*dp->nextnode)(np,(Dt_t*)0,nfp); @@ -228,9 +352,6 @@ char *nv_dirnext(void *dir) } if(!(save=dp->prev)) break; -#if 0 - sh.last_table = dp->table; -#endif *dp = *save; free((void*)save); } @@ -264,7 +385,14 @@ static void outtype(Namval_t *np, Namfun_t *fp, Sfio_t* out, const char *prefix) else if(!prefix) type = "type"; if(type) - sfprintf(out,"%s %s ",type,tp->nvname); + { + char *cp=tp->nvname; + if(cp=strrchr(cp,'.')) + cp++; + else + cp = tp->nvname; + sfprintf(out,"%s %s ",type,cp); + } } /* @@ -274,33 +402,51 @@ void nv_attribute(register Namval_t *np,Sfio_t *out,char *prefix,int noname) { register const Shtable_t *tp; register char *cp; - register unsigned val; - register unsigned mask; - register unsigned attr; + register unsigned val,mask,attr; + char *ip=0; Namfun_t *fp=0; + Namval_t *typep=0; for(fp=np->nvfun;fp;fp=fp->next) { - if(fp->type || (fp->disc && fp->disc->typef &&(*fp->disc->typef)(np,fp))) + if((typep=fp->type) || (fp->disc && fp->disc->typef && (typep=(*fp->disc->typef)(np,fp)))) break; } -#if 0 - if(!fp && !nv_isattr(np,~NV_ARRAY)) + if(!fp && !nv_isattr(np,~(NV_MINIMAL|NV_NOFREE))) { - if(!nv_isattr(np,NV_ARRAY) || nv_aindex(np)>=0) - return; - } -#else - if(!fp && !nv_isattr(np,~NV_MINIMAL)) + if(prefix && *prefix) + { + if(nv_isvtree(np)) + sfprintf(out,"%s -C ",prefix); + else if(!np->nvalue.cp && nv_isattr(np,~NV_NOFREE)==NV_MINIMAL && strcmp(np->nvname,"_")) + sfputr(out,prefix,' '); + } return; -#endif + } if ((attr=nv_isattr(np,~NV_NOFREE)) || fp) { - if((attr&NV_NOPRINT)==NV_NOPRINT) + if((attr&NV_NOPRINT|NV_INTEGER)==NV_NOPRINT) attr &= ~NV_NOPRINT; if(!attr && !fp) return; - if(prefix) + if(fp) + { + prefix = Empty; + attr &= NV_RDONLY|NV_ARRAY; + if(nv_isattr(np,NV_REF|NV_TAGGED)==(NV_REF|NV_TAGGED)) + attr |= (NV_REF|NV_TAGGED); + if(typep) + { + char *cp = typep->nvname; + if(cp = strrchr(cp,'.')) + cp++; + else + cp = typep->nvname; + sfputr(out,cp,' '); + fp = 0; + } + } + else if(prefix && *prefix) sfputr(out,prefix,' '); for(tp = shtab_attributes; *tp->sh_name;tp++) { @@ -313,7 +459,7 @@ void nv_attribute(register Namval_t *np,Sfio_t *out,char *prefix,int noname) * with E attribute from being given the F * attribute as well */ - if(val==(NV_INTEGER|NV_DOUBLE) && (attr&NV_EXPNOTE)) + if(val==NV_DOUBLE && (attr&(NV_EXPNOTE|NV_HEXFLOAT))) continue; if(val&NV_INTEGER) mask |= NV_DOUBLE; @@ -324,38 +470,44 @@ void nv_attribute(register Namval_t *np,Sfio_t *out,char *prefix,int noname) if(val==NV_ARRAY) { Namarr_t *ap = nv_arrayptr(np); - if(array_assoc(ap)) + char **xp=0; + if(ap && array_assoc(ap)) { if(tp->sh_name[1]!='A') continue; } else if(tp->sh_name[1]=='A') continue; -#if 0 - cp = "associative"; - else - cp = "indexed"; - if(!prefix) - sfputr(out,cp,' '); - else if(*cp=='i') - tp++; -#endif + if(ap && (ap->nelem&ARRAY_TREE)) + { + if(prefix && *prefix) + sfwrite(out,"-C ",3); + } + if(ap && !array_assoc(ap) && (xp=(char**)(ap+1)) && *xp) + ip = nv_namptr(*xp,0)->nvname; } if(prefix) { if(*tp->sh_name=='-') sfprintf(out,"%.2s ",tp->sh_name); + if(ip) + { + sfprintf(out,"[%s] ",ip); + ip = 0; + } } else sfputr(out,tp->sh_name+2,' '); if ((val&(NV_LJUST|NV_RJUST|NV_ZFILL)) && !(val&NV_INTEGER) && val!=NV_HOST) sfprintf(out,"%d ",nv_size(np)); + if(val==(NV_REF|NV_TAGGED)) + attr &= ~(NV_REF|NV_TAGGED); } if(val==NV_INTEGER && nv_isattr(np,NV_INTEGER)) { if(nv_size(np) != 10) { - if(nv_isattr(np, NV_DOUBLE)) + if(nv_isattr(np, NV_DOUBLE)== NV_DOUBLE) cp = "precision"; else cp = "base"; @@ -380,34 +532,160 @@ struct Walk Dt_t *root; int noscope; int indent; + int nofollow; + int array; + int flags; }; +void nv_outnode(Namval_t *np, Sfio_t* out, int indent, int special) +{ + char *fmtq,*ep,*xp; + Namval_t *mp; + Namarr_t *ap = nv_arrayptr(np); + int tabs=0,c,more,associative = 0; + if(ap) + { + if(!(ap->nelem&ARRAY_SCAN)) + nv_putsub(np,NIL(char*),ARRAY_SCAN); + sfputc(out,'('); + if(indent>=0) + { + sfputc(out,'\n'); + tabs=1; + } + if(!(associative =(array_assoc(ap)!=0))) + { + if(array_elem(ap) < nv_aimax(np)+1) + associative=1; + } + } + mp = nv_opensub(np); + while(1) + { + if(mp && special && nv_isvtree(mp)) + { + if(!nv_nextsub(np)) + break; + mp = nv_opensub(np); + continue; + } + if(tabs) + sfnputc(out,'\t',++indent); + tabs=0; + if(associative||special) + { + if(!(fmtq = nv_getsub(np))) + break; + sfprintf(out,"[%s]",sh_fmtq(fmtq)); + sfputc(out,'='); + } + if(mp && nv_isarray(mp)) + { + nv_outnode(mp, out, indent+(indent>=0),0); + if(indent>0) + sfnputc(out,'\t',indent); + sfputc(out,')'); + sfputc(out,indent>=0?'\n':' '); + more = nv_nextsub(np); + goto skip; + } + if(mp && nv_isvtree(mp)) + nv_onattr(mp,NV_EXPORT); + ep = nv_getval(mp?mp:np); + xp = 0; + if(!ap && nv_isattr(np,NV_INTEGER|NV_LJUST)==NV_LJUST) + { + xp = ep+nv_size(np); + while(--xp>ep && *xp==' '); + if(xp>ep || *xp!=' ') + xp++; + if(xp < (ep+nv_size(np))) + *xp = 0; + else + xp = 0; + } + if(mp && nv_isvtree(mp)) + fmtq = ep; + else if(!(fmtq = sh_fmtq(ep))) + fmtq = ""; + else if(!associative && (ep=strchr(fmtq,'='))) + { + char *qp = strchr(fmtq,'\''); + if(!qp || qp>ep) + { + sfwrite(out,fmtq,ep-fmtq); + sfputc(out,'\\'); + fmtq = ep; + } + } + more = nv_nextsub(np); + c = '\n'; + if(indent<0) + { + c = ';'; + if(ap) + c = more?' ':-1; + } + sfputr(out,fmtq,c); + if(xp) + *xp = ' '; + skip: + if(!more) + return; + mp = nv_opensub(np); + if(indent>0 && !(mp && special && nv_isvtree(mp))) + sfnputc(out,'\t',indent); + } +} + static void outval(char *name, const char *vname, struct Walk *wp) { register Namval_t *np, *nq; register Namfun_t *fp; - int isarray=0, associative=0, special=0; - if(!(np=nv_open(vname,wp->root,NV_ARRAY|NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope))) + int isarray=0, special=0,mode=0; + if(*name!='.' || vname[strlen(vname)-1]==']') + mode = NV_ARRAY; + if(!(np=nv_open(vname,wp->root,mode|NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL|wp->noscope))) return; - if(nv_isarray(np) && *name=='.') - special = 1; - if(!special && (fp=nv_hasdisc(np,&treedisc))) + fp = nv_hasdisc(np,&treedisc); + if(*name=='.') { + if(nv_isattr(np,NV_BINARY)) + return; + if(fp && np->nvalue.cp && np->nvalue.cp!=Empty) + { + nv_local = 1; + fp = 0; + } + if(fp) + return; + if(nv_isarray(np)) + return; + } + if(!special && fp && !nv_isarray(np)) + { + Namfun_t *xp; if(!wp->out) { fp = nv_stack(np,fp); if(fp = nv_stack(np,NIL(Namfun_t*))) free((void*)fp); np->nvfun = 0; + return; } - return; + for(xp=fp->next; xp; xp = xp->next) + { + if(xp->disc && (xp->disc->getval || xp->disc->getnum)) + break; + } + if(!xp) + return; } - if(nv_isnull(np)) + if((nv_isnull(np) || np->nvalue.cp==Empty) && !nv_isarray(np)) return; - if(special || nv_isarray(np)) + if(special || (nv_isarray(np) && nv_arrayptr(np))) { isarray=1; - associative= nv_aindex(np)<0; if(array_elem(nv_arrayptr(np))==0) isarray=2; else @@ -417,63 +695,49 @@ static void outval(char *name, const char *vname, struct Walk *wp) { _nv_unset(np,NV_RDONLY); nv_close(np); +#if 0 + if(sh.subshell==0 && !(wp->flags&NV_RDONLY) && !nv_isattr(np,NV_MINIMAL|NV_NOFREE)) + nv_delete(np,wp->root,0); +#endif return; } if(isarray==1 && !nq) - return; - if(special) { - associative = 1; - sfnputc(wp->out,'\t',wp->indent); + sfputc(wp->out,'('); + if(wp->indent>=0) + sfputc(wp->out,'\n'); + return; } - else - { + if(isarray==0 && nv_isarray(np) && nv_isnull(np)) /* empty array */ + isarray = 2; + special |= wp->nofollow; + if(!wp->array && wp->indent>0) sfnputc(wp->out,'\t',wp->indent); - nv_attribute(np,wp->out,"typeset",'='); + if(!special) + { + if(*name!='.') + nv_attribute(np,wp->out,"typeset",'='); nv_outname(wp->out,name,-1); - sfputc(wp->out,(isarray==2?'\n':'=')); - if(isarray) - { - if(isarray==2) - return; - sfwrite(wp->out,"(\n",2); - sfnputc(wp->out,'\t',++wp->indent); - } + if(np->nvalue.cp || nv_isattr(np,~(NV_MINIMAL|NV_NOFREE)) || nv_isvtree(np)) + sfputc(wp->out,(isarray==2?'\n':'=')); + if(isarray==2) + return; } - while(1) + fp = np->nvfun; + if(*name=='.' && !isarray) + np->nvfun = 0; + nv_outnode(np, wp->out, wp->indent, special); + if(*name=='.' && !isarray) + np->nvfun = fp; + if(isarray && !special) { - char *fmtq,*ep; - if(isarray && associative) + if(wp->indent>0) { - if(!(fmtq = nv_getsub(np))) - break; - sfprintf(wp->out,"[%s]",sh_fmtq(fmtq)); - sfputc(wp->out,'='); + sfnputc(wp->out,'\t',wp->indent); + sfwrite(wp->out,")\n",2); } - if(!(fmtq = sh_fmtq(nv_getval(np)))) - fmtq = ""; - else if(!associative && (ep=strchr(fmtq,'='))) - { - char *qp = strchr(fmtq,'\''); - if(!qp || qp>ep) - { - sfwrite(wp->out,fmtq,ep-fmtq); - sfputc(wp->out,'\\'); - fmtq = ep; - } - } - if(*name=='[' && !isarray) - sfprintf(wp->out,"(%s)\n",fmtq); else - sfputr(wp->out,fmtq,'\n'); - if(!nv_nextsub(np)) - break; - sfnputc(wp->out,'\t',wp->indent); - } - if(isarray && !special) - { - sfnputc(wp->out,'\t',--wp->indent); - sfwrite(wp->out,")\n",2); + sfwrite(wp->out,");",2); } } @@ -483,8 +747,8 @@ static void outval(char *name, const char *vname, struct Walk *wp) static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp) { register char *cp,*nextcp,*arg; - register int m,r; register Sfio_t *outfile = wp->out; + register int m,r,l; if(n==0) m = strlen(prefix); else if(cp=nextdot(prefix)) @@ -492,10 +756,14 @@ static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp) else m = strlen(prefix)-1; m++; - if(outfile) + if(outfile && !wp->array) { - sfwrite(outfile,"(\n",2); - wp->indent++; + sfputc(outfile,'('); + if(wp->indent>=0) + { + wp->indent++; + sfputc(outfile,'\n'); + } } for(; arg= *argv; argv++) { @@ -514,42 +782,82 @@ static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp) { if(outfile) { - sfnputc(outfile,'\t',wp->indent); + Namval_t *np,*tp; + *nextcp = 0; + np=nv_open(arg,wp->root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL|wp->noscope); + if(!np || (nv_isarray(np) && (!(tp=nv_opensub(np)) || !nv_isvtree(tp)))) + { + *nextcp = '.'; + continue; + } + if(wp->indent>=0) + sfnputc(outfile,'\t',wp->indent); + if(*cp!='[' && (tp = nv_type(np))) + { + char *sp; + if(sp = strrchr(tp->nvname,'.')) + sp++; + else + sp = tp->nvname; + sfputr(outfile,sp,' '); + } nv_outname(outfile,cp,nextcp-cp); sfputc(outfile,'='); + *nextcp = '.'; + } + else + { + outval(cp,arg,wp); + continue; } argv = genvalue(argv,cp,n+m+r,wp); - if(outfile) + if(wp->indent>=0) sfputc(outfile,'\n'); if(*argv) continue; break; } - else if(outfile && argv[1] && memcmp(arg,argv[1],r=strlen(arg))==0 && argv[1][r]=='[') + else if(outfile && !wp->nofollow && argv[1] && memcmp(arg,argv[1],l=strlen(arg))==0 && argv[1][l]=='[') { Namval_t *np = nv_open(arg,wp->root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|wp->noscope); if(!np) continue; - sfnputc(outfile,'\t',wp->indent); + wp->array = nv_isarray(np); + if(wp->indent>0) + sfnputc(outfile,'\t',wp->indent); nv_attribute(np,outfile,"typeset",1); nv_close(np); - sfputr(outfile,arg+m+(n?n+1:0),'='); - argv = genvalue(++argv,cp,cp-arg ,wp); - sfputc(outfile,'\n'); + sfputr(outfile,arg+m+r+(n?n:0),'='); + wp->nofollow=1; + argv = genvalue(argv,cp,cp-arg ,wp); + sfputc(outfile,wp->indent<0?';':'\n'); } else if(outfile && *cp=='[') { - sfnputc(outfile,'\t',wp->indent); + if(wp->indent) + sfnputc(outfile,'\t',wp->indent); sfputr(outfile,cp,'='); argv = genvalue(++argv,cp,cp-arg ,wp); sfputc(outfile,'\n'); } else + { outval(cp,arg,wp); + if(wp->array) + { + if(wp->indent>=0) + wp->indent++; + else + sfputc(outfile,' '); + wp->array = 0; + } + } } else break; + wp->nofollow = 0; } + wp->array = 0; if(outfile) { int c = prefix[m-1]; @@ -559,7 +867,8 @@ static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp) outval(".",prefix-n,wp); if(c=='.') cp[m-1] = c; - sfnputc(outfile,'\t',wp->indent-1); + if(wp->indent>0) + sfnputc(outfile,'\t',--wp->indent); sfputc(outfile,')'); } return(--argv); @@ -568,33 +877,79 @@ static char **genvalue(char **argv, const char *prefix, int n, struct Walk *wp) /* * walk the virtual tree and print or delete name-value pairs */ -static char *walk_tree(register Namval_t *np, int dlete) +static char *walk_tree(register Namval_t *np, Namval_t *xp, int flags) { static Sfio_t *out; struct Walk walk; Sfio_t *outfile; - int savtop = staktell(); + int len, savtop = staktell(); char *savptr = stakfreeze(0); register struct argnod *ap=0; struct argnod *arglist=0; char *name,*cp, **argv; char *subscript=0; void *dir; - int n=0, noscope=(dlete&NV_NOSCOPE); + int n=0, noscope=(flags&NV_NOSCOPE); + Namarr_t *arp = nv_arrayptr(np); + Dt_t *save_tree = sh.var_tree; + Namval_t *mp=0; + Shell_t *shp = sh_getinterp(); + char *xpname = xp?stakcopy(nv_name(xp)):0; + if(xp) + { + shp->last_root = shp->prev_root; + shp->last_table = shp->prev_table; + } + if(shp->last_table) + shp->last_root = nv_dict(shp->last_table); + if(shp->last_root) + shp->var_tree = shp->last_root; stakputs(nv_name(np)); - if(subscript = nv_getsub(np)) + if(arp && !(arp->nelem&ARRAY_SCAN) && (subscript = nv_getsub(np))) { + mp = nv_opensub(np); stakputc('['); stakputs(subscript); stakputc(']'); stakputc('.'); } + else if(*stakptr(staktell()-1) == ']') + mp = np; name = stakfreeze(1); - dir = nv_diropen(name); + len = strlen(name); + shp->last_root = 0; + dir = nv_diropen(mp,name); + walk.root = shp->last_root?shp->last_root:shp->var_tree; if(subscript) name[strlen(name)-1] = 0; while(cp = nv_dirnext(dir)) { + if(cp[len]!='.') + continue; + if(xp) + { + Dt_t *dp = shp->var_tree; + Namval_t *nq, *mq; + if(strlen(cp)<=len) + continue; + nq = nv_open(cp,walk.root,NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL); + if(!nq && (flags&NV_MOVE)) + nq = nv_search(cp,walk.root,NV_NOADD); + stakseek(0); + stakputs(xpname); + stakputs(cp+len); + stakputc(0); + shp->var_tree = save_tree; + mq = nv_open(stakptr(0),save_tree,NV_VARNAME|NV_NOASSIGN|NV_NOFAIL); + shp->var_tree = dp; + if(nq && mq) + { + nv_clone(nq,mq,flags|NV_RAW); + if(flags&NV_MOVE) + nv_delete(nq,walk.root,0); + } + continue; + } stakseek(ARGVAL); stakputs(cp); ap = (struct argnod*)stakfreeze(1); @@ -603,41 +958,63 @@ static char *walk_tree(register Namval_t *np, int dlete) n++; arglist = ap; } + nv_dirclose(dir); + if(xp) + { + shp->var_tree = save_tree; + return((char*)0); + } argv = (char**)stakalloc((n+1)*sizeof(char*)); argv += n; *argv = 0; for(; ap; ap=ap->argchn.ap) *--argv = ap->argval; - nv_dirclose(dir); - if(dlete&1) + if(flags&1) outfile = 0; else if(!(outfile=out)) outfile = out = sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING); else sfseek(outfile,0L,SEEK_SET); walk.out = outfile; - walk.root = sh.last_root; - walk.indent = 0; + walk.indent = (flags&NV_EXPORT)?-1:0; + walk.nofollow = 0; walk.noscope = noscope; + walk.array = 0; + walk.flags = flags; genvalue(argv,name,0,&walk); stakset(savptr,savtop); + shp->var_tree = save_tree; if(!outfile) return((char*)0); sfputc(out,0); return((char*)out->_data); } +Namfun_t *nv_isvtree(Namval_t *np) +{ + if(np) + return(nv_hasdisc(np,&treedisc)); + return(0); +} + /* * get discipline for compound initializations */ char *nv_getvtree(register Namval_t *np, Namfun_t *fp) { - NOT_USED(fp); - if(nv_isattr(np,NV_BINARY) && nv_isattr(np,NV_RAW)) + int flags=0; + for(; fp && fp->next; fp=fp->next) + { + if(fp->next->disc && (fp->next->disc->getnum || fp->next->disc->getval)) + return(nv_getv(np,fp)); + } + if(nv_isattr(np,NV_BINARY) && !nv_isattr(np,NV_RAW)) return(nv_getv(np,fp)); - if(nv_isattr(np,NV_ARRAY) && nv_arraychild(np,(Namval_t*)0,0)==np) + if(nv_isattr(np,NV_ARRAY) && !nv_type(np) && nv_arraychild(np,(Namval_t*)0,0)==np) return(nv_getv(np,fp)); - return(walk_tree(np,0)); + if(flags = nv_isattr(np,NV_EXPORT)) + nv_offattr(np,NV_EXPORT); + return(walk_tree(np,(Namval_t*)0,flags)); } /* @@ -647,10 +1024,29 @@ static void put_tree(register Namval_t *np, const char *val, int flags,Namfun_t { struct Namarray *ap; int nleft = 0; - if(!nv_isattr(np,NV_INTEGER)) - walk_tree(np,(flags&NV_NOSCOPE)|1); + if(!val && !fp->next && nv_isattr(np,NV_NOFREE)) + return; + if(!nv_isattr(np,(NV_INTEGER|NV_BINARY))) + { + Shell_t *shp = sh_getinterp(); + Namval_t *last_table = shp->last_table; + Dt_t *last_root = shp->last_root; + Namval_t *mp = val?nv_open(val,shp->var_tree,NV_VARNAME|NV_NOADD|NV_NOASSIGN|NV_NOFAIL):0; + if(mp && nv_isvtree(mp)) + { + shp->prev_table = shp->last_table; + shp->prev_root = shp->last_root; + shp->last_table = last_table; + shp->last_root = last_root; + if(!(flags&NV_APPEND)) + walk_tree(np,(Namval_t*)0,(flags&NV_NOSCOPE)|1); + nv_clone(mp,np,NV_COMVAR); + return; + } + walk_tree(np,(Namval_t*)0,(flags&NV_NOSCOPE)|1); + } nv_putv(np, val, flags,fp); - if(nv_isattr(np,NV_INTEGER)) + if(val && nv_isattr(np,(NV_INTEGER|NV_BINARY))) return; if(ap= nv_arrayptr(np)) nleft = array_elem(ap); @@ -658,9 +1054,7 @@ static void put_tree(register Namval_t *np, const char *val, int flags,Namfun_t { fp = nv_stack(np,fp); if(fp = nv_stack(np,NIL(Namfun_t*))) - { free((void*)fp); - } } } @@ -670,6 +1064,8 @@ static void put_tree(register Namval_t *np, const char *val, int flags,Namfun_t void nv_setvtree(register Namval_t *np) { register Namfun_t *nfp; + if(sh.subshell) + sh_assignok(np,1); if(nv_hasdisc(np, &treedisc)) return; nfp = newof(NIL(void*),Namfun_t,1,0); diff --git a/usr/src/lib/libshell/common/sh/nvtype.c b/usr/src/lib/libshell/common/sh/nvtype.c new file mode 100644 index 0000000000..a17bc99fda --- /dev/null +++ b/usr/src/lib/libshell/common/sh/nvtype.c @@ -0,0 +1,1533 @@ +/*********************************************************************** +* * +* This software is part of the ast package * +* Copyright (c) 1982-2008 AT&T Intellectual Property * +* and is licensed under the * +* Common Public License, Version 1.0 * +* by AT&T Intellectual Property * +* * +* A copy of the License is available at * +* http://www.opensource.org/licenses/cpl1.0.txt * +* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * +* * +* Information and Software Systems Research * +* AT&T Research * +* Florham Park NJ * +* * +* David Korn <dgk@research.att.com> * +* * +***********************************************************************/ +#pragma prototyped +/* + * David Korn + * AT&T Labs + * + */ + +#include "defs.h" + +static const char sh_opttype[] = +"[-1c?\n@(#)$Id: type (AT&T Labs Research) 2008-07-01 $\n]" +USAGE_LICENSE +"[+NAME?\f?\f - set the type of variables to \b\f?\f\b]" +"[+DESCRIPTION?\b\f?\f\b sets the type on each of the variables specified " + "by \aname\a to \b\f?\f\b. If \b=\b\avalue\a is specified, " + "the variable \aname\a is set to \avalue\a before the variable " + "is converted to \b\f?\f\b.]" +"[+?If no \aname\as are specified then the names and values of all " + "variables of this type are written to standard output.]" +"[+?\b\f?\f\b is built-in to the shell as a declaration command so that " + "field splitting and pathname expansion are not performed on " + "the arguments. Tilde expansion occurs on \avalue\a.]" +"[r?Enables readonly. Once enabled, the value cannot be changed or unset.]" +"[a]:?[type?Indexed array. Each \aname\a will converted to an index " + "array of type \b\f?\f\b. If a variable already exists, the current " + "value will become index \b0\b. If \b[\b\atype\a\b]]\b is " + "specified, each subscript is interpreted as a value of enumeration " + "type \atype\a.]" +"[A?Associative array. Each \aname\a will converted to an associate " + "array of type \b\f?\f\b. If a variable already exists, the current " + "value will become subscript \b0\b.]" +"[h]:[string?Used within a type definition to provide a help string " + "for variable \aname\a. Otherwise, it is ignored.]" +"[S?Used with a type definition to indicate that the variable is shared by " + "each instance of the type. When used inside a function defined " + "with the \bfunction\b reserved word, the specified variables " + "will have function static scope. Otherwise, the variable is " + "unset prior to processing the assignment list.]" +"[+DETAILS]\ftypes\f" +"\n" +"\n[name[=value]...]\n" +"\n" +"[+EXIT STATUS?]{" + "[+0?Successful completion.]" + "[+>0?An error occurred.]" +"}" + +"[+SEE ALSO?\fother\f \breadonly\b(1), \btypeset\b(1)]" +; + +typedef struct Namtype Namtype_t; +typedef struct Namchld +{ + Namfun_t fun; + Namtype_t *ptype; + Namtype_t *ttype; +} Namchld_t; + +struct Namtype +{ + Namfun_t fun; + Shell_t *sh; + Namval_t *np; + Namval_t *parent; + Namval_t *bp; + Namval_t *cp; + char *nodes; + char *data; + Namchld_t childfun; + int numnodes; + char **names; + size_t dsize; + short strsize; + unsigned short ndisc; + unsigned short current; + unsigned short nref; +}; + +#if 0 +struct type +{ + Namtype_t hdr; + unsigned short ndisc; + unsigned short current; + unsigned short nref; +}; +#endif + +typedef struct +{ + char _cSfdouble_t; + Sfdouble_t _dSfdouble_t; + char _cdouble; + double _ddouble; + char _cfloat; + float _dfloat; + char _cSflong_t; + Sflong_t _dSflong_t; + char _clong; + long _dlong; + char _cshort; + short _dshort; + char _cpointer; + char *_dpointer; +} _Align_; + +#define alignof(t) ((char*)&((_Align_*)0)->_d##t-(char*)&((_Align_*)0)->_c##t) + +static void put_type(Namval_t*, const char*, int, Namfun_t*); +static Namval_t* create_type(Namval_t*, const char*, int, Namfun_t*); +static Namfun_t* clone_type(Namval_t*, Namval_t*, int, Namfun_t*); +static Namval_t* next_type(Namval_t*, Dt_t*, Namfun_t*); + +static const Namdisc_t type_disc = +{ + sizeof(Namtype_t), + put_type, + 0, + 0, + 0, + create_type, + clone_type, + 0, + next_type, + 0, +#if 0 + read_type +#endif +}; + +static size_t datasize(Namval_t *np, size_t *offset) +{ + size_t s=0, a=0; + Namarr_t *ap; + if(nv_isattr(np,NV_INTEGER)) + { + if(nv_isattr(np,NV_DOUBLE)==NV_DOUBLE) + { + if(nv_isattr(np, NV_LONG)) + { + a = alignof(Sfdouble_t); + s = sizeof(Sfdouble_t); + } + else if(nv_isattr(np, NV_SHORT)) + { + a = alignof(float); + s = sizeof(float); + } + else + { + a = alignof(double); + s = sizeof(double); + } + } + else + { + if(nv_isattr(np, NV_LONG)) + { + a = alignof(Sflong_t); + s = sizeof(Sflong_t); + } + else if(nv_isattr(np, NV_SHORT)) + { + a = alignof(short); + s = sizeof(short); + } + else + { + a = alignof(long); + s = sizeof(long); + } + } + } + else if(nv_isattr(np, NV_BINARY) || nv_isattr(np,NV_LJUST|NV_RJUST|NV_ZFILL)) + s = nv_size(np); + else + { + a = alignof(pointer); + s = nv_size(np); + } + if(a>1 && offset) + *offset = a*((*offset +a-1)/a); + if(nv_isarray(np) && (ap = nv_arrayptr(np))) + s *= array_elem(ap); + return(s); +} + +static char *name_chtype(Namval_t *np, Namfun_t *fp) +{ + Namchld_t *pp = (Namchld_t*)fp; + char *cp, *sub; + Namval_t *tp = sh.last_table; + Namval_t *nq = pp->ptype->np; + Namarr_t *ap; + if(nv_isattr(np,NV_REF|NV_TAGGED)==(NV_REF|NV_TAGGED)) + sh.last_table = 0; + cp = nv_name(nq); + if((ap = nv_arrayptr(nq)) && !(ap->nelem&ARRAY_UNDEF) && (sub= nv_getsub(nq))) + sfprintf(sh.strbuf,"%s[%s].%s",cp,sub,np->nvname); + else + sfprintf(sh.strbuf,"%s.%s",cp,np->nvname); + sh.last_table = tp; + return(sfstruse(sh.strbuf)); +} + +static void put_chtype(Namval_t* np, const char* val, int flag, Namfun_t* fp) +{ + if(!val && nv_isattr(np,NV_REF)) + return; + nv_putv(np,val,flag,fp); + if(!val) + { + Namchld_t *pp = (Namchld_t*)fp; + size_t dsize=0,offset = (char*)np-(char*)pp->ptype; + Namval_t *mp = (Namval_t*)((char*)pp->ttype+offset); + dsize = datasize(mp,&dsize); + if(mp->nvalue.cp >= pp->ttype->data && mp->nvalue.cp < (char*)pp+pp->ttype->fun.dsize) + { + np->nvalue.cp = pp->ptype->data + (mp->nvalue.cp-pp->ptype->data); + memcpy((char*)np->nvalue.cp,mp->nvalue.cp,dsize); + } + else if(!nv_isarray(mp) && mp->nvalue.cp) + { + np->nvalue.cp = mp->nvalue.cp; + nv_onattr(np,NV_NOFREE); + } + np->nvsize = mp->nvsize; + np->nvflag = mp->nvflag&~NV_RDONLY; + } +} + +static const Namdisc_t chtype_disc = +{ + sizeof(Namchld_t), + put_chtype, + 0, + 0, + 0, + 0, + 0, + name_chtype +}; + +static Namval_t *findref(void *nodes, int n) +{ + Namval_t *tp,*np = nv_namptr(nodes,n); + char *name = np->nvname; + int i=n, len= strrchr(name,'.')-name; + Namtype_t *pp; + while(--i>0) + { + np = nv_namptr(nodes,i); + if(np->nvname[len]==0) + { + tp = nv_type(np); + pp = (Namtype_t*)nv_hasdisc(tp,&type_disc); + return(nv_namptr(pp->nodes,n-i-1)); + } + } + return(0); +} + +static int fixnode(Namtype_t *dp, Namtype_t *pp, int i, struct Namref *nrp,int flag) +{ + Namval_t *nq = nv_namptr(dp->nodes,i); + Namfun_t *fp; + if(fp=nv_hasdisc(nq,&chtype_disc)) + nv_disc(nq, fp, NV_POP); + if(nv_isattr(nq,NV_REF)) + { + nq->nvalue.nrp = nrp++; + nv_setsize(nq,0); + if(strchr(nq->nvname,'.')) + nq->nvalue.nrp->np = findref(dp->nodes,i); + else + nq->nvalue.nrp->np = nv_namptr(pp->childfun.ttype->nodes,i); + nq->nvalue.nrp->root = sh.last_root; + nq->nvalue.nrp->table = pp->np; + nq ->nvflag = NV_REF|NV_NOFREE|NV_MINIMAL; + return(1); + } + if(nq->nvalue.cp || nq->nvfun) + { + const char *data = nq->nvalue.cp; + if(nq->nvfun) + { + Namval_t *np = nv_namptr(pp->nodes,i); + if(nv_isarray(nq)) + nq->nvalue.cp = 0; + nq->nvfun = 0; + if(nv_isarray(nq) && nv_type(np)) + clone_all_disc(np,nq,flag&~NV_TYPE); + else + clone_all_disc(np,nq,flag); + if(fp) + nv_disc(np, fp, NV_LAST); + } +#if 0 + if(nq->nvalue.cp >= pp->data && nq->nvalue.cp < (char*)pp +pp->fun.dsize) + nq->nvalue.cp = dp->data + (nq->nvalue.cp-pp->data); +#else + if(data >= pp->data && data < (char*)pp +pp->fun.dsize) + nq->nvalue.cp = dp->data + (data-pp->data); +#endif + else if(!nq->nvfun && pp->childfun.ttype!=pp->childfun.ptype) + { + Namval_t *nr = nv_namptr( pp->childfun.ttype->nodes,i); + if(nr->nvalue.cp!=nq->nvalue.cp) + { + if(i=nv_size(nq)) + { + const char *cp = nq->nvalue.cp; + nq->nvalue.cp = (char*)malloc(i); + memcpy((char*)nq->nvalue.cp,cp,i); + } + else + nq->nvalue.cp = strdup(nq->nvalue.cp); + nv_offattr(nq,NV_NOFREE); + } + } + + } + if(fp) + nv_disc(nq, &dp->childfun.fun, NV_LAST); + return(0); +} + +static Namfun_t *clone_type(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) +{ + Namtype_t *dp, *pp=(Namtype_t*)fp; + register int i; + register Namval_t *nq, *nr; + size_t size = fp->dsize; + int save, offset=staktell(); + char *cp; + Dt_t *root = sh.last_root; + Namval_t *last_table = sh.last_table; + struct Namref *nrp = 0; + Namarr_t *ap; + if(flags&NV_MOVE) + { + pp->np = mp; + pp->childfun.ptype = pp; + return(fp); + } + if(flags&NV_TYPE) + return(nv_clone_disc(fp,flags)); + if(size==0 && (!fp->disc || (size=fp->disc->dsize)==0)) + size = sizeof(Namfun_t); + dp = (Namtype_t*)malloc(size+pp->nref*sizeof(struct Namref)); + if(pp->nref) + { + nrp = (struct Namref*)((char*)dp + size); + memset((void*)nrp,0,pp->nref*sizeof(struct Namref)); + } + memcpy((void*)dp,(void*)pp,size); +#if 0 + dp->parent = nv_lastdict(); +#else + dp->parent = mp; +#endif + dp->fun.nofree = (flags&NV_RDONLY?1:0); + dp->np = mp; + dp->childfun.ptype = dp; +#if 0 + dp->childfun.ttype = (Namtype_t*)nv_hasdisc(dp->fun.type,&type_disc); +#endif + dp->nodes = (char*)(dp+1); + dp->data = (char*)dp + (pp->data - (char*)pp); + for(i=dp->numnodes; --i >= 0; ) + { + nq = nv_namptr(dp->nodes,i); + if(fixnode(dp,pp,i,nrp,NV_TYPE)) + { + nrp++; + nq = nq->nvalue.nrp->np; + } + if(nq->nvalue.cp || nv_isarray(nq) || nv_isattr(nq,NV_RDONLY)) + { + /* see if default value has been overwritten */ + if(!mp->nvname) + continue; + sh.last_table = last_table; + if(pp->strsize<0) + cp = nv_name(np); + else + cp = nv_name(mp); + stakputs(cp); + stakputc('.'); + stakputs(nq->nvname); + stakputc(0); + root = nv_dict(mp); + save = fp->nofree; + fp->nofree = 1; + nr = nv_create(stakptr(offset),root,NV_VARNAME|NV_NOADD,fp); + fp->nofree = save; + stakseek(offset); + if(nr) + { + if(nv_isattr(nq,NV_RDONLY) && (nq->nvalue.cp || nv_isattr(nq,NV_INTEGER))) + errormsg(SH_DICT,ERROR_exit(1),e_readonly, nq->nvname); + if(nv_isref(nq)) + nq = nv_refnode(nq); + if((size = datasize(nr,(size_t*)0)) && size==datasize(nq,(size_t*)0)) + memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,size); + else if(ap=nv_arrayptr(nr)) + { + nv_putsub(nr,NIL(char*),ARRAY_SCAN|ARRAY_NOSCOPE); + do + { + if(array_assoc(ap)) + cp = (char*)((*ap->fun)(nr,NIL(char*),NV_ANAME)); + else + cp = nv_getsub(nr); + nv_putsub(nq,cp,ARRAY_ADD|ARRAY_NOSCOPE); + if(array_assoc(ap)) + { + Namval_t *mp = (Namval_t*)((*ap->fun)(nr,NIL(char*),NV_ACURRENT)); + Namval_t *mq = (Namval_t*)((*ap->fun)(nq,NIL(char*),NV_ACURRENT)); + nv_clone(mp,mq,NV_MOVE); + ap->nelem--; + nv_delete(mp,ap->table,0); + } + else + { + cp = nv_getval(nr); + nv_putval(nq,cp,0); + } + } + while(nv_nextsub(nr)); + } + else + nv_putval(nq,nv_getval(nr),NV_RDONLY); +#if SHOPT_TYPEDEF + if(sh.mktype) + nv_addnode(nr,1); +#endif /* SHOPT_TYPEDEF */ + if(pp->strsize<0) + continue; + _nv_unset(nr,0); + if(!nv_isattr(nr,NV_MINIMAL)) + nv_delete(nr,sh.last_root,0); + } + else if(nv_isattr(nq,NV_RDONLY) && !nq->nvalue.cp && !nv_isattr(nq,NV_INTEGER)) + errormsg(SH_DICT,ERROR_exit(1),e_required,nq->nvname,nv_name(mp)); + } + } + if(nv_isattr(mp,NV_BINARY)) + mp->nvalue.cp = dp->data; + if(pp->strsize<0) + dp->strsize = -pp->strsize; + return(&dp->fun); +} + + +/* + * return Namval_t* corresponding to child <name> in <np> + */ +static Namval_t *create_type(Namval_t *np,const char *name,int flag,Namfun_t *fp) +{ + Namtype_t *dp = (Namtype_t*)fp; + register const char *cp=name; + register int i=0,n; + Namval_t *nq=0; + if(!name) + return(dp->parent); + while((n=*cp++) && n != '=' && n != '+' && n!='['); + n = (cp-1) -name; + if(dp->numnodes && dp->strsize<0) + { + char *base = (char*)np-sizeof(Dtlink_t); + int m=strlen(np->nvname); + while((nq=nv_namptr(base,++i)) && memcmp(nq->nvname,np->nvname,m)==0) + { + if(nq->nvname[m]=='.' && memcmp(name,&nq->nvname[m+1],n)==0 && nq->nvname[m+n+1]==0) + goto found; + } + nq = 0; + } + else for(i=0; i < dp->numnodes; i++) + { + nq = nv_namptr(dp->nodes,i); + if((n==0||memcmp(name,nq->nvname,n)==0) && nq->nvname[n]==0) + { + while(nv_isref(nq)) + nq = nq->nvalue.nrp->np; + goto found; + } + } + nq = 0; +found: + if(nq) + { + fp->last = (char*)&name[n]; + sh.last_table = dp->parent; + } + else + { + if(name[n]!='=') for(i=0; i < dp->ndisc; i++) + { + if((memcmp(name,dp->names[i],n)==0) && dp->names[i][n]==0) + return(nq); + } + errormsg(SH_DICT,ERROR_exit(1),e_notelem,n,name,nv_name(np)); + } + return(nq); +} + +static void put_type(Namval_t* np, const char* val, int flag, Namfun_t* fp) +{ + Namval_t *nq; + if(val && (nq=nv_open(val,sh.var_tree,NV_VARNAME|NV_ARRAY|NV_NOADD|NV_NOFAIL))) + { + Namfun_t *pp; + if((pp=nv_hasdisc(nq,fp->disc)) && pp->type==fp->type) + + { + _nv_unset(np, flag); + nv_clone(nq,np,0); + return; + } + } + nv_putv(np,val,flag,fp); + if(!val) + { + Namtype_t *dp = (Namtype_t*)fp; + Namval_t *nq; + Namarr_t *ap; + int i; + if(nv_isarray(np) && (ap=nv_arrayptr(np)) && ap->nelem>0) + return; + for(i=0; i < dp->numnodes; i++) + { + nq = nv_namptr(dp->nodes,i); + if(ap=nv_arrayptr(nq)) + ap->nelem |= ARRAY_UNDEF; + if(!nv_hasdisc(nq,&type_disc)) + _nv_unset(nq,flag|NV_TYPE|nv_isattr(nq,NV_RDONLY)); + } + nv_disc(np,fp,NV_POP); + if(!(fp->nofree&1)) + free((void*)fp); + } +} + +static Namval_t *next_type(register Namval_t* np, Dt_t *root,Namfun_t *fp) +{ + Namtype_t *dp = (Namtype_t*)fp; + if(!root) + { + Namarr_t *ap = nv_arrayptr(np); + if(ap && (ap->nelem&ARRAY_UNDEF)) + nv_putsub(np,(char*)0,ARRAY_SCAN); + dp->current = 0; + } + else if(++dp->current>=dp->numnodes) + return(0); + return(nv_namptr(dp->nodes,dp->current)); +} + +static Namfun_t *clone_inttype(Namval_t* np, Namval_t *mp, int flags, Namfun_t *fp) +{ + Namfun_t *pp= (Namfun_t*)malloc(fp->dsize); + memcpy((void*)pp, (void*)fp, fp->dsize); + fp->nofree &= ~1; + if(nv_isattr(mp,NV_NOFREE) && mp->nvalue.cp) + memcpy((void*)mp->nvalue.cp,np->nvalue.cp, fp->dsize-sizeof(*fp)); + else + mp->nvalue.cp = (char*)(fp+1); + if(!nv_isattr(mp,NV_MINIMAL)) + mp->nvenv = 0; + nv_offattr(mp,NV_RDONLY); + return(pp); +} + +static int typeinfo(Opt_t* op, Sfio_t *out, const char *str, Optdisc_t *fp) +{ + char *cp,**help,buffer[256]; + Namtype_t *dp; + Namval_t *np,*nq,*tp; + int n, i, offset=staktell(); + Sfio_t *sp; + + np = *(Namval_t**)(fp+1); + stakputs(NV_CLASS); + stakputc('.'); + stakputs(np->nvname); + stakputc(0); + np = nv_open(stakptr(offset), sh.var_tree, NV_NOADD|NV_VARNAME); + stakseek(offset); + if(!np) + { + sfprintf(sfstderr,"%s: no such variable\n",np->nvname); + return(-1); + } + if(!(dp=(Namtype_t*)nv_hasdisc(np,&type_disc))) + { + Namfun_t *fp; + for(fp=np->nvfun;fp;fp=fp->next) + { + if(fp->disc && fp->disc->clonef==clone_inttype) + break; + } + if(!fp) + { + sfprintf(sfstderr,"%s: not a type\n",np->nvname); + return(-1); + } + if(strcmp(str,"other")==0) + return(0); + tp = fp->type; + nv_offattr(np,NV_RDONLY); + fp->type = 0; + if(np->nvenv) + sfprintf(out,"[+?\b%s\b is a %s.]\n", tp->nvname, np->nvenv); + cp = (char*)out->_next; + sfprintf(out,"[+?\b%s\b is a %n ", tp->nvname, &i); + nv_attribute(np,out,(char*)0, 1); + if(cp[i+1]=='i') + cp[i-1]='n'; + fp->type = tp; + nv_onattr(np,NV_RDONLY); + sfprintf(out," with default value \b%s\b.]",nv_getval(np)); + return(0); + } + if(strcmp(str,"other")==0) + { + Nambfun_t *bp; + if(bp=(Nambfun_t*)nv_hasdisc(np,nv_discfun(NV_DCADD))) + { + for(i=0; i < bp->num; i++) + { + if(nv_isattr(bp->bltins[i],NV_OPTGET)) + sfprintf(out,"\b%s.%s\b(3), ",np->nvname,bp->bnames[i]); + } + } + return(0); + } + help = &dp->names[dp->ndisc]; + sp = sfnew((Sfio_t*)0,buffer,sizeof(buffer),-1,SF_STRING|SF_WRITE); + sfprintf(out,"[+?\b%s\b defines the following fields:]{\n",np->nvname); + for(i=0; i < dp->numnodes; i++) + { + nq = nv_namptr(dp->nodes,i); + if(tp=nv_type(nq)) + { + Namfun_t *pp = nv_hasdisc(nq,&type_disc); + sfprintf(out,"\t[+%s?%s.\n",nq->nvname,tp->nvname); + n = strlen(nq->nvname); + while((cp=nv_namptr(dp->nodes,i+1)->nvname) && memcmp(cp,nq->nvname,n)==0 && cp[n]=='.') + i++; + } + else + { + sfseek(sp,(Sfoff_t)0, SEEK_SET); + nv_attribute(nq,sp,(char*)0,1); + cp = 0; + if(!nv_isattr(nq,NV_REF)) + cp = sh_fmtq(nv_getval(nq)); + sfputc(sp,0); + for(n=strlen(buffer); n>0 && buffer[n-1]==' '; n--); + buffer[n] = 0; + if(cp) + sfprintf(out,"\t[+%s?%s, default value is %s.\n",nq->nvname,*buffer?buffer:"string",cp); + else + sfprintf(out,"\t[+%s?%s.\n",nq->nvname,*buffer?buffer:"string"); + } + if(help[i]) + sfprintf(out," %s.",help[i]); + sfputc(out,']'); + } + sfprintf(out,"}\n"); + if(dp->ndisc>0) + { + stakseek(offset); + stakputs(NV_CLASS); + stakputc('.'); + stakputs(np->nvname); + stakputc('.'); + n = staktell(); + sfprintf(out,"[+?\b%s\b defines the following discipline functions:]{\n",np->nvname); + for(i=0; i < dp->ndisc; i++) + { + stakputs(dp->names[i]); + stakputc(0); + cp = 0; + if((nq = nv_search(stakptr(offset),sh.fun_tree,0)) && nq->nvalue.cp) + cp = nq->nvalue.rp->help; + sfprintf(out,"\t[+%s?%s]\n",dp->names[i],cp?cp:Empty); + if(cp) + sfputc(out,'.'); + stakseek(n); + } + sfprintf(out,"}\n"); + } + stakseek(offset); + sfclose(sp); + return(0); +} + +static int std_disc(Namval_t *mp, Namtype_t *pp) +{ + register const char *sp, *cp = strrchr(mp->nvname,'.'); + register const char **argv; + register int i; + Namval_t *np=0,*nq; + if(cp) + cp++; + else + cp = mp->nvname; + if(strcmp(cp,"create")==0) + { + if(pp) + pp->cp = mp; + return(0); + } + for(argv=nv_discnames; sp=*argv; argv++) + { + if(strcmp(cp,sp)==0) + { + if(!pp) + return(1); + goto found; + } + } + return(0); +found: + if(memcmp(sp=mp->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0) + sp += sizeof(NV_CLASS); + sp += strlen(pp->fun.type->nvname)+1; + if(sp == cp) + np = pp->fun.type; + else for(i=1; i < pp->numnodes; i++) + { + nq = nv_namptr(pp->nodes,i); + if(memcmp(nq->nvname, sp, cp-sp-1)==0) + { + np = nq; + break; + } + } + if(np) + { + nv_onattr(mp,NV_NOFREE); + if(!nv_setdisc(np,cp, mp, (Namfun_t*)np)) + sfprintf(sfstderr," nvsetdisc failed name=%s sp=%s cp=%s\n",np->nvname,sp,cp); + } + else + sfprintf(sfstderr,"can't set discipline %s cp=%s \n",sp,cp); + return(1); +} + + +void nv_addtype(Namval_t *np, const char *optstr, Optdisc_t *op, size_t optsz) +{ + Namdecl_t *cp = newof((Namdecl_t*)0,Namdecl_t,1,optsz); + Optdisc_t *dp = (Optdisc_t*)(cp+1); + Shell_t *shp = sh_getinterp(); + Namval_t *mp,*bp; + char *name; + if(optstr) + cp->optstring = optstr; + else + cp->optstring = sh_opttype; + memcpy((void*)dp,(void*)op, optsz); + cp->optinfof = (void*)dp; + cp->tp = np; + mp = nv_search("typeset",shp->bltin_tree,0); + if(name=strrchr(np->nvname,'.')) + name++; + else + name = np->nvname; + if((bp=nv_search(name,shp->fun_tree,NV_NOSCOPE)) && !bp->nvalue.ip) + nv_delete(bp,shp->fun_tree,0); + bp = sh_addbuiltin(name, mp->nvalue.bfp, (void*)cp); + nv_onattr(bp,nv_isattr(mp,NV_PUBLIC)); + nv_onattr(np, NV_RDONLY); +} + +static void addtype(Namval_t *mp) +{ + struct { + Optdisc_t opt; + Namval_t *np; + } optdisc; + memset(&optdisc,0,sizeof(optdisc)); + optdisc.opt.infof = typeinfo; + optdisc.np = mp; + nv_addtype(mp,sh_opttype, &optdisc.opt, sizeof(optdisc)); +} + +/* + * This function creates a type out of the <numnodes> nodes in the + * array <nodes>. The first node is the name for the type + */ +Namval_t *nv_mktype(Namval_t **nodes, int numnodes) +{ + Namval_t *mp=nodes[0], *bp=0, *np, *nq, **mnodes=nodes; + int i,j,k,m,n,nd=0,nref=0,iref=0,inherit=0; + int size=sizeof(NV_DATA), dsize=0, nnodes; + size_t offset=0; + char *name=0, *cp, *sp, **help; + Namtype_t *pp,*qp=0,*dp,*tp; + Dt_t *root = nv_dict(mp); + struct Namref *nrp = 0; + Namfun_t *fp; + m = strlen(mp->nvname)+1; + for(nnodes=1,i=1; i <numnodes; i++) + { + np=nodes[i]; + if(is_afunction(np)) + { + if(!std_disc(np, (Namtype_t*)0)) + { + size += strlen(np->nvname+m)+1; + if(memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0) + size -= sizeof(NV_CLASS); + nd++; + } + continue; + } + if(nv_isattr(np,NV_REF)) + iref++; + if(np->nvenv) + size += strlen((char*)np->nvenv)+1; + if(strcmp(&np->nvname[m],NV_DATA)==0 && !nv_type(np)) + continue; + if(qp) + { /* delete duplicates */ + for(j=0; j < qp->numnodes;j++) + { + nq = nv_namptr(qp->nodes,j); + if(strcmp(nq->nvname,&np->nvname[m])==0) + break; + } + if(j < qp->numnodes) + continue; + } + nnodes++; + if(name && memcmp(&name[m],&np->nvname[m],n)==0 && np->nvname[m+n]=='.') + offset -= sizeof(char*); + dsize = datasize(np,&offset); + if(!nv_isarray(np) && (dp=(Namtype_t*)nv_hasdisc(np, &type_disc))) + { + nnodes += dp->numnodes; + if((n=dp->strsize)<0) + n = -n; + iref = nref += dp->nref; + if(np->nvname[m]=='_' && np->nvname[m+1]==0 && (bp=nv_type(np))) + { + qp = dp; + nd = dp->ndisc; + nnodes = dp->numnodes; + offset = 0; + dsize = nv_size(np); + size += n; + } + else + size += n + dp->numnodes*(strlen(&np->nvname[m])+1); + n = strlen(np->nvname); + while((i+1) < numnodes && (cp=nodes[i+1]->nvname) && memcmp(cp,np->nvname,n)==0 && cp[n]=='.') + i++; + } + else if(nv_isattr(np,NV_REF)) + nref++; + offset += (dsize?dsize:4); + size += (n=strlen(name=np->nvname)-m+1); + } + offset = roundof(offset,sizeof(char*)); + nv_setsize(mp,offset); + if(nd) + nd++; + k = roundof(sizeof(Namtype_t),sizeof(Sfdouble_t)) - sizeof(Namtype_t); + pp = newof(NiL, Namtype_t, 1, nnodes*NV_MINSZ + offset + size + (nnodes+nd)*sizeof(char*) + iref*sizeof(struct Namref)+k); + pp->fun.dsize = sizeof(Namtype_t)+nnodes*NV_MINSZ +offset+k; + pp->fun.type = mp; + pp->parent = nv_lastdict(); + pp->np = mp; + pp->bp = bp; + pp->childfun.fun.disc = &chtype_disc; + pp->childfun.fun.nofree = 1; + pp->childfun.ttype = pp; + pp->childfun.ptype = pp; + pp->fun.disc = &type_disc; + pp->nodes = (char*)(pp+1); + pp->numnodes = nnodes; + pp->data = pp->nodes + nnodes*NV_MINSZ +k; + pp->dsize = offset; + nrp = (struct Namref*)(pp->data+offset); + pp->names = (char**)(nrp+iref); + help = &pp->names[nd]; + pp->strsize = size; + cp = (char*)&pp->names[nd+nnodes]; + if(qp) + mnodes = newof(NiL, Namval_t*, nd+1, 0); + nd = 0; + nq = nv_namptr(pp->nodes,0); + nq->nvname = cp; + nv_onattr(nq,NV_MINIMAL); + cp = strcopy(cp,NV_DATA); + *cp++ = 0; + for(name=0, offset=0, k=i=1; i < numnodes; i++) + { + np=nodes[i]; + if(is_afunction(np)) + { + sp = np->nvname+m; + if(memcmp(np->nvname,NV_CLASS,sizeof(NV_CLASS)-1)==0) + sp += sizeof(NV_CLASS); + if(!std_disc(np, pp)) + { + /* see if discipline already defined */ + for(j=0; j< nd; j++) + { + if(strcmp(sp,pp->names[j])==0) + { + mnodes[j] = nodes[i]; + break; + } + } + if(j>=nd) + { + pp->names[nd] = cp; + mnodes[nd++] = nodes[i]; + cp = strcopy(cp,sp); + *cp++ = 0; + } + nv_onattr(mnodes[j],NV_NOFREE); + } + continue; + } + if(inherit) + { + for(j=0; j < k ; j++) + { + nq = nv_namptr(pp->nodes,j); + if(strcmp(nq->nvname,&np->nvname[m])==0) + break; + } + if(j < k) + { + sp = nv_getval(np); + if(nv_isvtree(np)) + sfprintf(sfstderr,"initialization not implemented\n"); + else if(sp) + nv_putval(nq,sp,0); + goto skip; + } + } + if(strcmp(&np->nvname[m],NV_DATA)==0 && !nv_type(np)) + { + char *val=nv_getval(np); + nq = nv_namptr(pp->nodes,0); + nq->nvfun = 0; + nv_putval(nq,(val?val:0),nv_isattr(np,~(NV_IMPORT|NV_EXPORT|NV_ARRAY))); + nq->nvflag = np->nvflag|NV_NOFREE|NV_MINIMAL; + goto skip; + } + if(qp) + { + Nambfun_t *bp; + dp = (Namtype_t*)nv_hasdisc(nv_type(np), &type_disc); + memcpy(pp->nodes,dp->nodes,dp->numnodes*NV_MINSZ); + offset = nv_size(np); + memcpy(pp->data,dp->data,offset); + for(k=0;k < dp->numnodes; k++) + { + Namval_t *nr = nv_namptr(qp->nodes,k); + nq = nv_namptr(pp->nodes,k); + if(fixnode(pp,dp,k,nrp,0)) + { + nrp++; + nq = nq->nvalue.nrp->np; + } + if(!nv_isattr(nr,NV_REF) && !nv_hasdisc(nr,&type_disc)) + { + if(nr->nvsize) + memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,size=datasize(nr,(size_t*)0)); + else + { + nq->nvalue.cp = nr->nvalue.cp; + nv_onattr(nq,NV_NOFREE); + } + } + } + if(bp=(Nambfun_t*)nv_hasdisc(np,nv_discfun(NV_DCADD))) + { + for(j=0; j < bp->num; j++) + { + pp->names[nd++] = (char*)bp->bnames[j]; + mnodes[j] = bp->bltins[j]; + } + } + qp = 0; + inherit=1; + goto skip; + } + nq = nv_namptr(pp->nodes,k); + if(np->nvenv) + { + /* need to save the string pointer */ + nv_offattr(np,NV_EXPORT); + help[k] = cp; + cp = strcopy(cp,np->nvenv); + j = *help[k]; + if(islower(j)) + *help[k] = toupper(j); + *cp++ = 0; + np->nvenv = 0; + } + nq->nvname = cp; + if(name && memcmp(name,&np->nvname[m],n)==0 && np->nvname[m+n]=='.') + offset -= sizeof(char*); + dsize = datasize(np,&offset); + cp = strcopy(name=cp, &np->nvname[m]); + n = cp-name; + *cp++ = 0; + nq->nvsize = np->nvsize; + nq->nvflag = (np->nvflag&~(NV_IMPORT|NV_EXPORT))|NV_NOFREE|NV_MINIMAL; + if(dp = (Namtype_t*)nv_hasdisc(np, &type_disc)) + { + int r,kfirst=k; + char *cname = &np->nvname[m]; + /* + * If field is a type, mark the type by setting + * strsize<0. This changes create_type() + */ + clone_all_disc(np,nq,NV_RDONLY); + if(nv_isarray(np)) + { + nv_disc(nq, &pp->childfun.fun, NV_LAST); + k++; + goto skip; + } + if(fp=nv_hasdisc(nq,&chtype_disc)) + nv_disc(nq, &pp->childfun.fun, NV_LAST); + if(tp = (Namtype_t*)nv_hasdisc(nq, &type_disc)) + tp->strsize = -tp->strsize; +else sfprintf(sfstderr,"tp==NULL\n"); + for(r=0; r < dp->numnodes; r++) + { + Namval_t *nr = nv_namptr(dp->nodes,r); + nq = nv_namptr(pp->nodes,++k); + nq->nvname = cp; + dsize = datasize(nr,&offset); + nq->nvflag = nr->nvflag; + if(nr->nvalue.cp) + { + Namchld_t *xp = (Namchld_t*)nv_hasdisc(nr,&chtype_disc); + if(xp && nr->nvalue.cp >= xp->ptype->data && nr->nvalue.cp < xp->ptype->data+xp->ptype->fun.dsize) + { + nq->nvalue.cp = pp->data+offset; + memcpy((char*)nq->nvalue.cp,nr->nvalue.cp,dsize); + nv_onattr(nq,NV_NOFREE); + } + else + nq->nvalue.cp = strdup(nr->nvalue.cp); + nv_disc(nq, &pp->childfun.fun, NV_LAST); + } + nq->nvsize = nr->nvsize; + offset += dsize; + if(*cname!='_' || cname[1]) + { + cp = strcopy(cp,cname); + *cp++ = '.'; + } + cp = strcopy(cp,nr->nvname); + *cp++ = 0; + } + while((i+1) < numnodes && (cname=&nodes[i+1]->nvname[m]) && memcmp(cname,&np->nvname[m],n)==0 && cname[n]=='.') + { + int j=kfirst; + nv_unset(np); + nv_delete(np,root,0); + np = nodes[++i]; + while(j < k) + { + nq = nv_namptr(pp->nodes,++j); + if(strcmp(nq->nvname,cname)==0) + { + sfprintf(sfstderr,"%s found at k=%d\n",nq->nvname,k); + if(nq->nvalue.cp>=pp->data && nq->nvalue.cp< (char*)pp->names) + memcpy((char*)nq->nvalue.cp,np->nvalue.cp,datasize(np,0)); + break; + } + } + } + } + else + { + j = nv_isattr(np,NV_NOFREE); + nq->nvfun = np->nvfun; + np->nvfun = 0; + if(nv_isarray(nq) && !nq->nvfun) + { + nv_putsub(nq, (char*)0, ARRAY_FILL); + ((Namarr_t*)nq->nvfun)->nelem--; + + } + nv_disc(nq, &pp->childfun.fun, NV_LAST); + if(nq->nvfun) + { + for(fp=nq->nvfun; fp; fp = fp->next) + fp->nofree |= 1; + } + nq->nvalue.cp = np->nvalue.cp; + if(dsize) + { + nq->nvalue.cp = pp->data+offset; + sp = (char*)np->nvalue.cp; + if(nv_isattr(np,NV_INT16P) ==NV_INT16) + { + sp= (char*)&np->nvalue; + nv_onattr(nq,NV_INT16P); + } + if(sp) + memcpy((char*)nq->nvalue.cp,sp,dsize); + else if(nv_isattr(np,NV_LJUST|NV_RJUST)) + memset((char*)nq->nvalue.cp,' ',dsize); + if(!j) + free((void*)np->nvalue.cp); + } + np->nvalue.cp = 0; +#if 0 + offset += dsize; +#else + offset += (dsize?dsize:4); +#endif + } + k++; + skip: + if(!nv_isnull(np)) + _nv_unset(np,0); + nv_delete(np,root,0); + } + pp->ndisc = nd; + pp->nref = nref; + if(k>1) + { + nv_setsize(mp,offset); + mp->nvalue.cp = pp->data; + nv_onattr(mp,NV_NOFREE|NV_BINARY|NV_RAW); + } + else if(!mp->nvalue.cp) + mp->nvalue.cp = Empty; + nv_disc(mp, &pp->fun, NV_LAST); + if(nd>0) + { + pp->names[nd] = 0; + nv_adddisc(mp, (const char**)pp->names, mnodes); + } + if(mnodes!=nodes) + free((void*)mnodes); + addtype(mp); + return(mp); +} + +Namval_t *nv_mkinttype(char *name, size_t size, int sign, const char *help, Namdisc_t *ep) +{ + Namval_t *mp; + Namfun_t *fp; + Namdisc_t *dp; + int offset=staktell(); + stakputs(NV_CLASS); + stakputc('.'); + stakputs(name); + stakputc(0); + mp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME); + stakseek(offset); + offset = size + sizeof(Namdisc_t); + fp = newof(NiL, Namfun_t, 1, offset); + fp->type = mp; + fp->nofree |= 1; + fp->dsize = sizeof(Namfun_t)+size; + dp = (Namdisc_t*)(fp+1); + if(ep) + *dp = *ep; + dp->clonef = clone_inttype; + fp->disc = dp; + mp->nvalue.cp = (char*)(fp+1) + sizeof(Namdisc_t); + nv_setsize(mp,10); + mp->nvenv = (char*)help; + nv_onattr(mp,NV_NOFREE|NV_RDONLY|NV_INTEGER|NV_EXPORT); + if(size==16) + nv_onattr(mp,NV_INT16P); + else if(size==64) + nv_onattr(mp,NV_INT64); + if(!sign) + nv_onattr(mp,NV_UNSIGN); + nv_disc(mp, fp, NV_LAST); + addtype(mp); + return(mp); +} + +void nv_typename(Namval_t *tp, Sfio_t *out) +{ + char *v,*cp; + Namtype_t *dp; + cp = nv_name(tp); + if(v=strrchr(cp,'.')) + cp = v+1; + if((dp = (Namtype_t*)nv_hasdisc(tp,&type_disc)) && dp->bp) + { + nv_typename(dp->bp,out); + sfprintf(out,"%s.%s",sfstruse(out),cp); + } + else + sfputr(out,cp,-1); +} + +Namval_t *nv_type(Namval_t *np) +{ + Namfun_t *fp; + for(fp=np->nvfun; fp; fp=fp->next) + { + if(fp->type) + return(fp->type); + if(fp->disc && fp->disc->typef && (np= (*fp->disc->typef)(np,fp))) + return(np); + } + return(0); +} + +/* + * call any and all create functions + */ +static void type_init(Namval_t *np) +{ + int i; + Namtype_t *dp, *pp=(Namtype_t*)nv_hasdisc(np,&type_disc); + Namval_t *nq; + if(!pp) + return; + for(i=0; i < pp->numnodes; i++) + { + nq = nv_namptr(pp->nodes,i); + if((dp=(Namtype_t*)nv_hasdisc(nq,&type_disc)) && dp->cp) + sh_fun(dp->cp,nq, (char**)0); + } + if(pp->cp) + sh_fun(pp->cp, np, (char**)0); +} + +/* + * This function turns variable <np> to the type <tp> + */ +int nv_settype(Namval_t* np, Namval_t *tp, int flags) +{ + int isnull = nv_isnull(np); + int rdonly = nv_isattr(np,NV_RDONLY); + char *val=0; + Namarr_t *ap=0; + int nelem=0; +#if SHOPT_TYPEDEF + Namval_t *tq; + if(nv_type(np)==tp) + return(0); + if(nv_isarray(np) && (tq=nv_type(np))) + { + if(tp==tq) + return(0); + errormsg(SH_DICT,ERROR_exit(1),e_redef,nv_name(np)); + } + if((ap=nv_arrayptr(np)) && ap->nelem>0) + { + nv_putsub(np,NIL(char*),ARRAY_SCAN); + ap->hdr.type = tp; + do + { + nv_arraysettype(np, tp, nv_getsub(np),flags); + } + while(nv_nextsub(np)); + } + else if(ap || nv_isarray(np)) + { + flags &= ~NV_APPEND; + if(!ap) + { + nv_putsub(np,"0",ARRAY_FILL); + ap = nv_arrayptr(np); + nelem = 1; + + } + } + else +#endif /*SHOPT_TYPEDEF */ + { + if(isnull) + flags &= ~NV_APPEND; + else if(!nv_isvtree(np)) + { + val = strdup(nv_getval(np)); + if(!(flags&NV_APPEND)) + _nv_unset(np, NV_RDONLY); + } + if(!nv_clone(tp,np,flags|NV_NOFREE)) + return(0); + } + if(ap) + { + int nofree; + nv_disc(np,&ap->hdr,NV_POP); + np->nvalue.up = 0; + nv_clone(tp,np,flags|NV_NOFREE); + if(np->nvalue.cp && !nv_isattr(np,NV_NOFREE)) + free((void*)np->nvalue.cp); + np->nvalue.up = 0; + nofree = ap->hdr.nofree; + ap->hdr.nofree = 0; + nv_disc(np, &ap->hdr, NV_FIRST); + ap->hdr.nofree = nofree; + nv_onattr(np,NV_ARRAY); + if(nelem) + { + ap->nelem++; + nv_putsub(np,"0",0); + _nv_unset(np,NV_RDONLY); + ap->nelem--; + } + } + type_init(np); + if(!rdonly) + nv_offattr(np,NV_RDONLY); + if(val) + { + nv_putval(np,val,NV_RDONLY); + free((void*)val); + } + return(0); +} + +#define S(x) #x +#define FIELD(x,y) { S(y##x), S(x##_t), offsetof(struct stat,st_##y##x) } +typedef struct _field_ +{ + char *name; + char *type; + int offset; +} Fields_t; + +Fields_t foo[]= +{ + FIELD(dev,), + FIELD(ino,), + FIELD(nlink,), + FIELD(mode,), + FIELD(uid,), + FIELD(gid,), + FIELD(size,), + FIELD(time,a), + FIELD(time,m), + FIELD(time,c), +#if 0 + FIELD(blksize,), + FIELD(blocks,), +#endif + 0 +}; + + +Namval_t *nv_mkstruct(const char *name, int rsize, Fields_t *fields) +{ + Namval_t *mp, *nq, *nr, *tp; + Fields_t *fp; + Namtype_t *dp, *pp; + char *cp, *sp; + int nnodes=0, offset=staktell(), n, r, i, j; + size_t m, size=0; + stakputs(NV_CLASS); + stakputc('.'); + r = staktell(); + stakputs(name); + stakputc(0); + mp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME); + stakseek(r); + + for(fp=fields; fp->name; fp++) + { + m = strlen(fp->name)+1; + size += m; + nnodes++; + if(memcmp(fp->type,"typeset",7)) + { + stakputs(fp->type); + stakputc(0); + tp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME|NV_NOADD|NV_NOFAIL); + stakseek(r); + if(!tp) + errormsg(SH_DICT,ERROR_exit(1),e_unknowntype,strlen(fp->type),fp->type); + if(dp = (Namtype_t*)nv_hasdisc(tp,&type_disc)) + { + nnodes += dp->numnodes; + if((i=dp->strsize) < 0) + i = -i; + size += i + dp->numnodes*m; + } + } + } + pp = newof(NiL,Namtype_t, 1, nnodes*NV_MINSZ + rsize + size); + pp->fun.dsize = sizeof(Namtype_t)+nnodes*NV_MINSZ +rsize; + pp->fun.type = mp; + pp->np = mp; + pp->childfun.fun.disc = &chtype_disc; + pp->childfun.fun.nofree = 1; + pp->childfun.ttype = pp; + pp->childfun.ptype = pp; + pp->fun.disc = &type_disc; + pp->nodes = (char*)(pp+1); + pp->numnodes = nnodes; + pp->strsize = size; + pp->data = pp->nodes + nnodes*NV_MINSZ; + cp = pp->data + rsize; + for(i=0,fp=fields; fp->name; fp++) + { + nq = nv_namptr(pp->nodes,i++); + nq->nvname = cp; + nq->nvalue.cp = pp->data + fp->offset; + nv_onattr(nq,NV_MINIMAL|NV_NOFREE); + m = strlen(fp->name)+1; + memcpy(cp, fp->name, m); + cp += m; + if(memcmp(fp->type,"typeset",7)) + { + stakputs(fp->type); + stakputc(0); + tp = nv_open(stakptr(offset), sh.var_tree, NV_VARNAME); + stakseek(r); + clone_all_disc(tp,nq,NV_RDONLY); + nq->nvflag = tp->nvflag|NV_MINIMAL|NV_NOFREE; + nq->nvsize = tp->nvsize; + if(dp = (Namtype_t*)nv_hasdisc(nq,&type_disc)) + dp->strsize = -dp->strsize; + if(dp = (Namtype_t*)nv_hasdisc(tp,&type_disc)) + { + if(nv_hasdisc(nq,&chtype_disc)) + nv_disc(nq, &pp->childfun.fun, NV_LAST); + sp = (char*)nq->nvalue.cp; + memcpy(sp, dp->data, nv_size(tp)); + for(j=0; j < dp->numnodes; j++) + { + nr = nv_namptr(dp->nodes,j); + nq = nv_namptr(pp->nodes,i++); + nq->nvname = cp; + memcpy(cp,fp->name,m); + cp[m-1] = '.'; + cp += m; + n = strlen(nr->nvname)+1; + memcpy(cp,nr->nvname,n); + cp += n; + if(nr->nvalue.cp>=dp->data && nr->nvalue.cp < (char*)pp + pp->fun.dsize) + { + nq->nvalue.cp = sp + (nr->nvalue.cp-dp->data); + } + nq->nvflag = nr->nvflag; + nq->nvsize = nr->nvsize; + } + } + } + else if(strmatch(fp->type+7,"*-*i*")==0) + { + nv_onattr(nq,NV_NOFREE|NV_RDONLY|NV_INTEGER); + if(strmatch(fp->type+7,"*-*s*")==0) + nv_onattr(nq,NV_INT16P); + else if(strmatch(fp->type+7,"*-*l*")==0) + nv_onattr(nq,NV_INT64); + if(strmatch(fp->type+7,"*-*u*")==0) + nv_onattr(nq,NV_UNSIGN); + } + + } + stakseek(offset); + nv_onattr(mp,NV_RDONLY|NV_NOFREE|NV_BINARY); + nv_setsize(mp,rsize); + nv_disc(mp, &pp->fun, NV_LAST); + mp->nvalue.cp = pp->data; + addtype(mp); + return(mp); +} + +static void put_stat(Namval_t* np, const char* val, int flag, Namfun_t* nfp) +{ + if(val) + { + if(stat(val,(struct stat*)np->nvalue.cp)<0) + sfprintf(sfstderr,"stat of %s failed\n",val); + return; + } + nv_putv(np,val,flag,nfp); + nv_disc(np,nfp,NV_POP); + if(!(nfp->nofree&1)) + free((void*)nfp); +} + +static const Namdisc_t stat_disc = +{ + 0, + put_stat +}; + + +void nv_mkstat(void) +{ + Namval_t *tp; + Namfun_t *fp; + tp = nv_mkstruct("stat_t", sizeof(struct stat), foo); + nv_offattr(tp,NV_RDONLY); + nv_setvtree(tp); + fp = newof(NiL,Namfun_t,1,0); + fp->type = tp; + fp->disc = &stat_disc; + nv_disc(tp,fp,NV_FIRST); + nv_putval(tp,"/dev/null",0); + nv_onattr(tp,NV_RDONLY); +} diff --git a/usr/src/lib/libshell/common/sh/parse.c b/usr/src/lib/libshell/common/sh/parse.c index 1cacd79e57..bccbd49d50 100644 --- a/usr/src/lib/libshell/common/sh/parse.c +++ b/usr/src/lib/libshell/common/sh/parse.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -48,23 +48,23 @@ /* These routines are local to this module */ -static Shnode_t *makeparent(int, Shnode_t*); -static Shnode_t *makelist(int, Shnode_t*, Shnode_t*); +static Shnode_t *makeparent(Lex_t*, int, Shnode_t*); +static Shnode_t *makelist(Lex_t*, int, Shnode_t*, Shnode_t*); static struct argnod *qscan(struct comnod*, int); -static struct ionod *inout(struct ionod*, int); -static Shnode_t *sh_cmd(int,int); -static Shnode_t *term(int); -static Shnode_t *list(int); -static struct regnod *syncase(int); -static Shnode_t *item(int); -static Shnode_t *simple(int, struct ionod*); -static int skipnl(int); -static Shnode_t *test_expr(int); -static Shnode_t *test_and(void); -static Shnode_t *test_or(void); -static Shnode_t *test_primary(void); +static struct ionod *inout(Lex_t*,struct ionod*, int); +static Shnode_t *sh_cmd(Lex_t*,int,int); +static Shnode_t *term(Lex_t*,int); +static Shnode_t *list(Lex_t*,int); +static struct regnod *syncase(Lex_t*,int); +static Shnode_t *item(Lex_t*,int); +static Shnode_t *simple(Lex_t*,int, struct ionod*); +static int skipnl(Lex_t*,int); +static Shnode_t *test_expr(Lex_t*,int); +static Shnode_t *test_and(Lex_t*); +static Shnode_t *test_or(Lex_t*); +static Shnode_t *test_primary(Lex_t*); -#define sh_getlineno() (shlex.lastline) +#define sh_getlineno(lp) (lp->lastline) #ifndef NIL # define NIL(type) ((type)0) @@ -80,6 +80,7 @@ static struct stdata } st; #endif +static int opt_get; static int loop_level; static struct argnod *label_list; static struct argnod *label_last; @@ -92,7 +93,7 @@ static struct argnod *label_last; * write out entities for each item in the list * type=='V' for variable assignment lists * Otherwise type is determined by the command */ -static unsigned long writedefs(struct argnod *arglist, int line, int type, struct argnod *cmd) +static unsigned long writedefs(Lex_t *lexp,struct argnod *arglist, int line, int type, struct argnod *cmd) { register struct argnod *argp = arglist; register char *cp; @@ -102,10 +103,10 @@ static unsigned long writedefs(struct argnod *arglist, int line, int type, struc static char atbuff[20]; int justify=0; char *attribute = atbuff; - unsigned long parent=shlex.script; + unsigned long parent=lexp->script; if(type==0) { - parent = shlex.current; + parent = lexp->current; type = 'v'; switch(*argp->argval) { @@ -140,7 +141,7 @@ static unsigned long writedefs(struct argnod *arglist, int line, int type, struc } } else if(cmd) - parent=kiaentity(sh_argstr(cmd),-1,'p',-1,-1,shlex.unknown,'b',0,""); + parent=kiaentity(lexp,sh_argstr(cmd),-1,'p',-1,-1,lexp->unknown,'b',0,""); *attribute = 0; while(argp) { @@ -148,32 +149,109 @@ static unsigned long writedefs(struct argnod *arglist, int line, int type, struc n = cp-argp->argval; else n = strlen(argp->argval); - eline = sh.inlineno-(shlex.token==NL); - r=kiaentity(argp->argval,n,type,line,eline,parent,justify,width,atbuff); - sfprintf(shlex.kiatmp,"p;%..64d;v;%..64d;%d;%d;s;\n",shlex.current,r,line,eline); + eline = lexp->sh->inlineno-(lexp->token==NL); + r=kiaentity(lexp,argp->argval,n,type,line,eline,parent,justify,width,atbuff); + sfprintf(lexp->kiatmp,"p;%..64d;v;%..64d;%d;%d;s;\n",lexp->current,r,line,eline); argp = argp->argnxt.ap; } return(r); } #endif /* SHOPT_KIA */ + +static void typeset_order(const char *str,int line) +{ + register int c,n=0; + unsigned const char *cp=(unsigned char*)str; + static unsigned char *table; + if(*cp!='+' && *cp!='-') + return; + if(!table) + { + table = calloc(1,256); + for(cp=(unsigned char*)"bflmnprstuxACHS";c = *cp; cp++) + table[c] = 1; + for(cp=(unsigned char*)"aiEFLRXhTZ";c = *cp; cp++) + table[c] = 2; + for(c='0'; c <='9'; c++) + table[c] = 3; + } + for(cp=(unsigned char*)str; c= *cp++; n=table[c]) + { + if(table[c] < n) + errormsg(SH_DICT,ERROR_warn(0),e_lextypeset,line,str); + } +} + +/* + * add type definitions when compiling with -n + */ +static void check_typedef(struct comnod *tp) +{ + char *cp=0; + if(tp->comtyp&COMSCAN) + { + struct argnod *ap = tp->comarg; + while(ap = ap->argnxt.ap) + { + if(!(ap->argflag&ARG_RAW) || memcmp(ap->argval,"--",2)) + break; + if(sh_isoption(SH_NOEXEC)) + typeset_order(ap->argval,tp->comline); + if(memcmp(ap->argval,"-T",2)==0) + { + if(ap->argval[2]) + cp = ap->argval+2; + else if((ap->argnxt.ap)->argflag&ARG_RAW) + cp = (ap->argnxt.ap)->argval; + if(cp) + break; + } + } + } + else + { + struct dolnod *dp = (struct dolnod*)tp->comarg; + char **argv = dp->dolval + dp->dolbot+1; + while((cp= *argv++) && memcmp(cp,"--",2)) + { + if(sh_isoption(SH_NOEXEC)) + typeset_order(cp,tp->comline); + if(memcmp(cp,"-T",2)==0) + { + if(cp[2]) + cp = cp+2; + else + cp = *argv; + break; + } + } + } + if(cp) + { + Namval_t *mp=(Namval_t*)tp->comnamp ,*bp; + bp = sh_addbuiltin(cp,mp->nvalue.bfp, (void*)0); + nv_onattr(bp,nv_isattr(mp,NV_PUBLIC)); + } +} + /* * Make a parent node for fork() or io-redirection */ -static Shnode_t *makeparent(int flag, Shnode_t *child) +static Shnode_t *makeparent(Lex_t *lp, int flag, Shnode_t *child) { register Shnode_t *par = getnode(forknod); par->fork.forktyp = flag; par->fork.forktre = child; par->fork.forkio = 0; - par->fork.forkline = sh_getlineno()-1; + par->fork.forkline = sh_getlineno(lp)-1; return(par); } -static Shnode_t *getanode(struct argnod *ap) +static Shnode_t *getanode(Lex_t *lp, struct argnod *ap) { register Shnode_t *t = getnode(arithnod); t->ar.artyp = TARITH; - t->ar.arline = sh_getlineno(); + t->ar.arline = sh_getlineno(lp); t->ar.arexpr = ap; if(ap->argflag&ARG_RAW) t->ar.arcomp = sh_arithcomp(ap->argval); @@ -185,11 +263,11 @@ static Shnode_t *getanode(struct argnod *ap) /* * Make a node corresponding to a command list */ -static Shnode_t *makelist(int type, Shnode_t *l, Shnode_t *r) +static Shnode_t *makelist(Lex_t *lexp, int type, Shnode_t *l, Shnode_t *r) { register Shnode_t *t; if(!l || !r) - sh_syntax(); + sh_syntax(lexp); else { if((type&COMMSK) == TTST) @@ -211,16 +289,17 @@ static Shnode_t *makelist(int type, Shnode_t *l, Shnode_t *r) void *sh_parse(Shell_t *shp, Sfio_t *iop, int flag) { register Shnode_t *t; + Lex_t *lexp = (Lex_t*)shp->lex_context; Fcin_t sav_input; - struct argnod *sav_arg = shlex.arg; + struct argnod *sav_arg = lexp->arg; int sav_prompt = shp->nextprompt; if(shp->binscript && sffileno(iop)==shp->infd) - return((void*)sh_trestore(iop)); + return((void*)sh_trestore(shp,iop)); fcsave(&sav_input); shp->st.staklist = 0; - shlex.heredoc = 0; - shlex.inlineno = shp->inlineno; - shlex.firstline = shp->st.firstline; + lexp->heredoc = 0; + lexp->inlineno = shp->inlineno; + lexp->firstline = shp->st.firstline; shp->nextprompt = 1; loop_level = 0; label_list = label_last = 0; @@ -228,7 +307,7 @@ void *sh_parse(Shell_t *shp, Sfio_t *iop, int flag) sh_onstate(SH_INTERACTIVE); if(sh_isoption(SH_VERBOSE)) sh_onstate(SH_VERBOSE); - sh_lexopen((Lex_t*)shp->lex_context,shp,0); + sh_lexopen(lexp,shp,0); if(fcfopen(iop) < 0) return(NIL(void*)); if(fcfile()) @@ -241,13 +320,13 @@ void *sh_parse(Shell_t *shp, Sfio_t *iop, int flag) fcgetc(version); fcclose(); fcrestore(&sav_input); - shlex.arg = sav_arg; + lexp->arg = sav_arg; if(version > 3) errormsg(SH_DICT,ERROR_exit(1),e_lexversion); if(sffileno(iop)==shp->infd) shp->binscript = 1; sfgetc(iop); - return((void*)sh_trestore(iop)); + return((void*)sh_trestore(shp,iop)); } } if((flag&SH_NL) && (shp->inlineno=error_info.line+shp->st.firstline)==0) @@ -255,10 +334,10 @@ void *sh_parse(Shell_t *shp, Sfio_t *iop, int flag) #if KSHELL shp->nextprompt = 2; #endif - t = sh_cmd((flag&SH_EOF)?EOFSYM:'\n',SH_SEMI|SH_EMPTY|(flag&SH_NL)); + t = sh_cmd(lexp,(flag&SH_EOF)?EOFSYM:'\n',SH_SEMI|SH_EMPTY|(flag&SH_NL)); fcclose(); fcrestore(&sav_input); - shlex.arg = sav_arg; + lexp->arg = sav_arg; /* unstack any completed alias expansions */ if((sfset(iop,0,0)&SF_STRING) && !sfreserve(iop,0,-1)) { @@ -269,10 +348,10 @@ void *sh_parse(Shell_t *shp, Sfio_t *iop, int flag) shp->nextprompt = sav_prompt; if(flag&SH_NL) { - shp->st.firstline = shlex.firstline; - shp->inlineno = shlex.inlineno; + shp->st.firstline = lexp->firstline; + shp->inlineno = lexp->inlineno; } - stakseek(0); + stkseek(shp->stk,0); return((void*)t); } @@ -280,26 +359,28 @@ void *sh_parse(Shell_t *shp, Sfio_t *iop, int flag) * This routine parses up the matching right parenthesis and returns * the parse tree */ -Shnode_t *sh_dolparen(void) +Shnode_t *sh_dolparen(Lex_t* lp) { register Shnode_t *t=0; - register Lex_t *lp = (Lex_t*)sh.lex_context; Sfio_t *sp = fcfile(); - int line = sh.inlineno; - sh.inlineno = error_info.line+sh.st.firstline; - sh_lexopen(lp,&sh,1); - shlex.comsub = 1; - switch(sh_lex()) + int line = lp->sh->inlineno; + lp->sh->inlineno = error_info.line+lp->sh->st.firstline; + sh_lexopen(lp,lp->sh,1); + lp->comsub = 1; + switch(sh_lex(lp)) { /* ((...)) arithmetic expression */ case EXPRSYM: - t = getanode(shlex.arg); + t = getanode(lp,lp->arg); break; case LPAREN: - t = sh_cmd(RPAREN,SH_NL|SH_EMPTY); + t = sh_cmd(lp,RPAREN,SH_NL|SH_EMPTY); + break; + case LBRACE: + t = sh_cmd(lp,RBRACE,SH_NL|SH_EMPTY); break; } - shlex.comsub = 0; + lp->comsub = 0; if(!sp && (sp=fcfile())) { /* @@ -315,7 +396,7 @@ Shnode_t *sh_dolparen(void) fcsopen(cp); sfclose(sp); } - sh.inlineno = line; + lp->sh->inlineno = line; return(t); } @@ -323,11 +404,11 @@ Shnode_t *sh_dolparen(void) * remove temporary files and stacks */ -void sh_freeup(void) +void sh_freeup(Shell_t *shp) { - if(sh.st.staklist) - sh_funstaks(sh.st.staklist,-1); - sh.st.staklist = 0; + if(shp->st.staklist) + sh_funstaks(shp->st.staklist,-1); + shp->st.staklist = 0; } /* @@ -358,21 +439,21 @@ void sh_funstaks(register struct slnod *slp,int flag) * list [ ; cmd ] */ -static Shnode_t *sh_cmd(register int sym, int flag) +static Shnode_t *sh_cmd(Lex_t *lexp, register int sym, int flag) { register Shnode_t *left, *right; register int type = FINT|FAMP; if(sym==NL) - shlex.lasttok = 0; - left = list(flag); - if(shlex.token==NL) + lexp->lasttok = 0; + left = list(lexp,flag); + if(lexp->token==NL) { if(flag&SH_NL) - shlex.token=';'; + lexp->token=';'; } else if(!left && !(flag&SH_EMPTY)) - sh_syntax(); - switch(shlex.token) + sh_syntax(lexp); + switch(lexp->token) { case COOPSYM: /* set up a cooperating process */ type |= (FPIN|FPOU|FPCL|FCOOP); @@ -383,23 +464,23 @@ static Shnode_t *sh_cmd(register int sym, int flag) /* (...)& -> {...;} & */ if(left->tre.tretyp==TPAR) left = left->par.partre; - left = makeparent(TFORK|type, left); + left = makeparent(lexp,TFORK|type, left); } /* FALL THRU */ case ';': if(!left) - sh_syntax(); - if(right=sh_cmd(sym,flag|SH_EMPTY)) - left=makelist(TLST, left, right); + sh_syntax(lexp); + if(right=sh_cmd(lexp,sym,flag|SH_EMPTY)) + left=makelist(lexp,TLST, left, right); break; case EOFSYM: if(sym==NL) break; default: - if(sym && sym!=shlex.token) + if(sym && sym!=lexp->token) { - if(sym!=ELSESYM || (shlex.token!=ELIFSYM && shlex.token!=FISYM)) - sh_syntax(); + if(sym!=ELSESYM || (lexp->token!=ELIFSYM && lexp->token!=FISYM)) + sh_syntax(lexp); } } return(left); @@ -412,12 +493,12 @@ static Shnode_t *sh_cmd(register int sym, int flag) * list || term * unfortunately, these are equal precedence */ -static Shnode_t *list(register int flag) +static Shnode_t *list(Lex_t *lexp, register int flag) { - register Shnode_t *t = term(flag); + register Shnode_t *t = term(lexp,flag); register int token; - while(t && ((token=shlex.token)==ANDFSYM || token==ORFSYM)) - t = makelist((token==ANDFSYM?TAND:TORF), t, term(SH_NL|SH_SEMI)); + while(t && ((token=lexp->token)==ANDFSYM || token==ORFSYM)) + t = makelist(lexp,(token==ANDFSYM?TAND:TORF), t, term(lexp,SH_NL|SH_SEMI)); return(t); } @@ -426,29 +507,29 @@ static Shnode_t *list(register int flag) * item * item | term */ -static Shnode_t *term(register int flag) +static Shnode_t *term(Lex_t *lexp,register int flag) { register Shnode_t *t; register int token; if(flag&SH_NL) - token = skipnl(flag); + token = skipnl(lexp,flag); else - token = sh_lex(); + token = sh_lex(lexp); /* check to see if pipeline is to be timed */ if(token==TIMESYM || token==NOTSYM) { t = getnode(parnod); t->par.partyp=TTIME; - if(shlex.token==NOTSYM) + if(lexp->token==NOTSYM) t->par.partyp |= COMSCAN; - t->par.partre = term(0); + t->par.partre = term(lexp,0); } - else if((t=item(SH_NL|SH_EMPTY|(flag&SH_SEMI))) && shlex.token=='|') + else if((t=item(lexp,SH_NL|SH_EMPTY|(flag&SH_SEMI))) && lexp->token=='|') { register Shnode_t *tt; int showme = t->tre.tretyp&FSHOWME; - t = makeparent(TFORK|FPOU,t); - if(tt=term(SH_NL)) + t = makeparent(lexp,TFORK|FPOU,t); + if(tt=term(lexp,SH_NL)) { switch(tt->tre.tretyp&COMMSK) { @@ -459,13 +540,13 @@ static Shnode_t *term(register int flag) tt->lst.lstlef->tre.tretyp |= FPIN|FPCL; break; default: - tt= makeparent(TSETIO|FPIN|FPCL,tt); + tt= makeparent(lexp,TSETIO|FPIN|FPCL,tt); } - t=makelist(TFIL,t,tt); + t=makelist(lexp,TFIL,t,tt); t->tre.tretyp |= showme; } - else if(shlex.token) - sh_syntax(); + else if(lexp->token) + sh_syntax(lexp); } return(t); } @@ -473,9 +554,9 @@ static Shnode_t *term(register int flag) /* * case statement */ -static struct regnod* syncase(register int esym) +static struct regnod* syncase(Lex_t *lexp,register int esym) { - register int tok = skipnl(0); + register int tok = skipnl(lexp,0); register struct regnod *r; if(tok==esym) return(NIL(struct regnod*)); @@ -483,35 +564,35 @@ static struct regnod* syncase(register int esym) r->regptr=0; r->regflag=0; if(tok==LPAREN) - skipnl(0); + skipnl(lexp,0); while(1) { - if(!shlex.arg) - sh_syntax(); - shlex.arg->argnxt.ap=r->regptr; - r->regptr = shlex.arg; - if((tok=sh_lex())==RPAREN) + if(!lexp->arg) + sh_syntax(lexp); + lexp->arg->argnxt.ap=r->regptr; + r->regptr = lexp->arg; + if((tok=sh_lex(lexp))==RPAREN) break; else if(tok=='|') - sh_lex(); + sh_lex(lexp); else - sh_syntax(); + sh_syntax(lexp); } - r->regcom=sh_cmd(0,SH_NL|SH_EMPTY|SH_SEMI); - if((tok=shlex.token)==BREAKCASESYM) - r->regnxt=syncase(esym); + r->regcom=sh_cmd(lexp,0,SH_NL|SH_EMPTY|SH_SEMI); + if((tok=lexp->token)==BREAKCASESYM) + r->regnxt=syncase(lexp,esym); else if(tok==FALLTHRUSYM) { r->regflag++; - r->regnxt=syncase(esym); + r->regnxt=syncase(lexp,esym); } else { if(tok!=esym && tok!=EOFSYM) - sh_syntax(); + sh_syntax(lexp); r->regnxt=0; } - if(shlex.token==EOFSYM) + if(lexp->token==EOFSYM) return(NIL(struct regnod*)); return(r); } @@ -523,46 +604,47 @@ static struct regnod* syncase(register int esym) * Otherise a list containing an arithmetic command and a while * is returned. */ -static Shnode_t *arithfor(register Shnode_t *tf) +static Shnode_t *arithfor(Lex_t *lexp,register Shnode_t *tf) { register Shnode_t *t, *tw = tf; register int offset; register struct argnod *argp; register int n; - int argflag = shlex.arg->argflag; + Stk_t *stkp = lexp->sh->stk; + int argflag = lexp->arg->argflag; /* save current input */ Fcin_t sav_input; fcsave(&sav_input); - fcsopen(shlex.arg->argval); + fcsopen(lexp->arg->argval); /* split ((...)) into three expressions */ for(n=0; ; n++) { register int c; - argp = (struct argnod*)stakseek(ARGVAL); + argp = (struct argnod*)stkseek(stkp,ARGVAL); argp->argnxt.ap = 0; argp->argchn.cp = 0; argp->argflag = argflag; if(n==2) break; /* copy up to ; onto the stack */ - sh_lexskip(';',1,ST_NESTED); - offset = staktell()-1; + sh_lexskip(lexp,';',1,ST_NESTED); + offset = stktell(stkp)-1; if((c=fcpeek(-1))!=';') break; /* remove trailing white space */ - while(offset>ARGVAL && ((c= *stakptr(offset-1)),isspace(c))) + while(offset>ARGVAL && ((c= *stkptr(stkp,offset-1)),isspace(c))) offset--; /* check for empty initialization expression */ if(offset==ARGVAL && n==0) continue; - stakseek(offset); + stkseek(stkp,offset); /* check for empty condition and treat as while((1)) */ if(offset==ARGVAL) - stakputc('1'); - argp = (struct argnod*)stakfreeze(1); - t = getanode(argp); + sfputc(stkp,'1'); + argp = (struct argnod*)stkfreeze(stkp,1); + t = getanode(lexp,argp); if(n==0) - tf = makelist(TLST,t,tw); + tf = makelist(lexp,TLST,t,tw); else tw->wh.whtre = t; } @@ -573,53 +655,58 @@ static Shnode_t *arithfor(register Shnode_t *tf) fcrestore(&sav_input); if(n<2) { - shlex.token = RPAREN|SYMREP; - sh_syntax(); + lexp->token = RPAREN|SYMREP; + sh_syntax(lexp); } /* check whether the increment is present */ if(*argp->argval) { - t = getanode(argp); + t = getanode(lexp,argp); tw->wh.whinc = (struct arithnod*)t; } else tw->wh.whinc = 0; - sh_lexopen((Lex_t*)sh.lex_context, &sh,1); - if((n=sh_lex())==NL) - n = skipnl(0); + sh_lexopen(lexp, lexp->sh,1); + if((n=sh_lex(lexp))==NL) + n = skipnl(lexp,0); else if(n==';') - n = sh_lex(); + n = sh_lex(lexp); if(n!=DOSYM && n!=LBRACE) - sh_syntax(); - tw->wh.dotre = sh_cmd(n==DOSYM?DONESYM:RBRACE,SH_NL); + sh_syntax(lexp); + tw->wh.dotre = sh_cmd(lexp,n==DOSYM?DONESYM:RBRACE,SH_NL); tw->wh.whtyp = TWH; return(tf); } -static Shnode_t *funct(void) +static Shnode_t *funct(Lex_t *lexp) { + Shell_t *shp = lexp->sh; register Shnode_t *t; register int flag; struct slnod *volatile slp=0; Stak_t *savstak; Sfoff_t first, last; - struct functnod *fp; + struct functnod *volatile fp; Sfio_t *iop; #if SHOPT_KIA - unsigned long current = shlex.current; + unsigned long current = lexp->current; #endif /* SHOPT_KIA */ int jmpval, saveloop=loop_level; struct argnod *savelabel = label_last; struct checkpt buff; + int save_optget = opt_get; + void *in_mktype = shp->mktype; + shp->mktype = 0; + opt_get = 0; t = getnode(functnod); - t->funct.functline = sh.inlineno; + t->funct.functline = shp->inlineno; t->funct.functtyp=TFUN; t->funct.functargs = 0; - if(!(flag = (shlex.token==FUNCTSYM))) + if(!(flag = (lexp->token==FUNCTSYM))) t->funct.functtyp |= FPOSIX; - else if(sh_lex()) - sh_syntax(); + else if(sh_lex(lexp)) + sh_syntax(lexp); if(!(iop=fcfile())) { iop = sfopen(NIL(Sfio_t*),fcseek(0),"s"); @@ -627,53 +714,53 @@ static Shnode_t *funct(void) fcfopen(iop); } t->funct.functloc = first = fctell(); - if(!sh.st.filename || sffileno(iop)<0) + if(!shp->st.filename || sffileno(iop)<0) { if(fcfill() >= 0) fcseek(-1); if(sh_isstate(SH_HISTORY)) - t->funct.functloc = sfseek(sh.hist_ptr->histfp,(off_t)0,SEEK_CUR); + t->funct.functloc = sfseek(shp->hist_ptr->histfp,(off_t)0,SEEK_CUR); else { /* copy source to temporary file */ t->funct.functloc = 0; - if(shlex.sh->heredocs) - t->funct.functloc = sfseek(shlex.sh->heredocs,(Sfoff_t)0, SEEK_END); + if(lexp->sh->heredocs) + t->funct.functloc = sfseek(lexp->sh->heredocs,(Sfoff_t)0, SEEK_END); else - shlex.sh->heredocs = sftmp(HERE_MEM); - shlex.sh->funlog = shlex.sh->heredocs; + lexp->sh->heredocs = sftmp(HERE_MEM); + lexp->sh->funlog = lexp->sh->heredocs; t->funct.functtyp |= FPIN; } } - t->funct.functnam= (char*)shlex.arg->argval; + t->funct.functnam= (char*)lexp->arg->argval; #if SHOPT_KIA - if(shlex.kiafile) - shlex.current = kiaentity(t->funct.functnam,-1,'p',-1,-1,shlex.script,'p',0,""); + if(lexp->kiafile) + lexp->current = kiaentity(lexp,t->funct.functnam,-1,'p',-1,-1,lexp->script,'p',0,""); #endif /* SHOPT_KIA */ if(flag) { - shlex.token = sh_lex(); + lexp->token = sh_lex(lexp); #if SHOPT_BASH - if(shlex.token == LPAREN) + if(lexp->token == LPAREN) { - if((shlex.token = sh_lex()) == RPAREN) + if((lexp->token = sh_lex(lexp)) == RPAREN) t->funct.functtyp |= FPOSIX; else - sh_syntax(); + sh_syntax(lexp); } #endif } if(t->funct.functtyp&FPOSIX) - skipnl(0); + skipnl(lexp,0); else { - if(shlex.token==0) - t->funct.functargs = (struct comnod*)simple(SH_NOIO|SH_FUNDEF,NIL(struct ionod*)); - while(shlex.token==NL) - shlex.token = sh_lex(); + if(lexp->token==0) + t->funct.functargs = (struct comnod*)simple(lexp,SH_NOIO|SH_FUNDEF,NIL(struct ionod*)); + while(lexp->token==NL) + lexp->token = sh_lex(lexp); } - if((flag && shlex.token!=LBRACE) || shlex.token==EOFSYM) - sh_syntax(); + if((flag && lexp->token!=LBRACE) || lexp->token==EOFSYM) + sh_syntax(lexp); sh_pushcontext(&buff,1); jmpval = sigsetjmp(buff.buff,0); if(jmpval == 0) @@ -683,8 +770,8 @@ static Shnode_t *funct(void) savstak = stakinstall(savstak, 0); slp = (struct slnod*)stakalloc(sizeof(struct slnod)+sizeof(struct functnod)); slp->slchild = 0; - slp->slnext = sh.st.staklist; - sh.st.staklist = 0; + slp->slnext = shp->st.staklist; + shp->st.staklist = 0; t->funct.functstak = (struct slnod*)slp; /* * store the pathname of function definition file on stack @@ -694,20 +781,20 @@ static Shnode_t *funct(void) fp->functtyp = TFUN|FAMP; fp->functnam = 0; fp->functline = t->funct.functline; - if(sh.st.filename) - fp->functnam = stakcopy(sh.st.filename); + if(shp->st.filename) + fp->functnam = stakcopy(shp->st.filename); loop_level = 0; label_last = label_list; - if(!flag && shlex.token==0) + if(!flag && lexp->token==0) { /* copy current word token to current stak frame */ struct argnod *ap; - flag = ARGVAL + strlen(shlex.arg->argval); + flag = ARGVAL + strlen(lexp->arg->argval); ap = (struct argnod*)stakalloc(flag); - memcpy(ap,shlex.arg,flag); - shlex.arg = ap; + memcpy(ap,lexp->arg,flag); + lexp->arg = ap; } - t->funct.functtre = item(SH_NOIO); + t->funct.functtre = item(lexp,SH_NOIO); } sh_popcontext(&buff); loop_level = saveloop; @@ -716,50 +803,54 @@ static Shnode_t *funct(void) if(slp) { slp->slptr = stakinstall(savstak,0); - slp->slchild = sh.st.staklist; + slp->slchild = shp->st.staklist; } #if SHOPT_KIA - shlex.current = current; + lexp->current = current; #endif /* SHOPT_KIA */ if(jmpval) { if(slp && slp->slptr) { - sh.st.staklist = slp->slnext; + shp->st.staklist = slp->slnext; stakdelete(slp->slptr); } - siglongjmp(*sh.jmplist,jmpval); + siglongjmp(*shp->jmplist,jmpval); } - sh.st.staklist = (struct slnod*)slp; + shp->st.staklist = (struct slnod*)slp; last = fctell(); fp->functline = (last-first); fp->functtre = t; - if(shlex.sh->funlog) + shp->mktype = in_mktype; + if(lexp->sh->funlog) { if(fcfill()>0) fcseek(-1); - shlex.sh->funlog = 0; + lexp->sh->funlog = 0; } #if SHOPT_KIA - if(shlex.kiafile) - kiaentity(t->funct.functnam,-1,'p',t->funct.functline,sh.inlineno-1,shlex.current,'p',0,""); + if(lexp->kiafile) + kiaentity(lexp,t->funct.functnam,-1,'p',t->funct.functline,shp->inlineno-1,lexp->current,'p',0,""); #endif /* SHOPT_KIA */ + t->funct.functtyp |= opt_get; + opt_get = save_optget; return(t); } /* * Compound assignment */ -static struct argnod *assign(register struct argnod *ap) +static struct argnod *assign(Lex_t *lexp, register struct argnod *ap, int tdef) { register int n; register Shnode_t *t, **tp; register struct comnod *ac; + Stk_t *stkp = lexp->sh->stk; int array=0; Namval_t *np; n = strlen(ap->argval)-1; if(ap->argval[n]!='=') - sh_syntax(); + sh_syntax(lexp); if(ap->argval[n-1]=='+') { ap->argval[n--]=0; @@ -774,81 +865,110 @@ static struct argnod *assign(register struct argnod *ap) *ap->argval=0; t = getnode(fornod); t->for_.fornam = (char*)(ap->argval+1); - t->for_.fortyp = sh_getlineno(); + t->for_.fortyp = sh_getlineno(lexp); tp = &t->for_.fortre; ap->argchn.ap = (struct argnod*)t; ap->argflag &= ARG_QUOTED; ap->argflag |= array; - shlex.assignok = SH_ASSIGN; + lexp->assignok = SH_ASSIGN; + lexp->aliasok = 1; array=0; - if((n=skipnl(0))==RPAREN || n==LPAREN) + if((n=skipnl(lexp,0))==RPAREN || n==LPAREN) { int index= 0; struct argnod **settail; ac = (struct comnod*)getnode(comnod); settail= &ac->comset; memset((void*)ac,0,sizeof(*ac)); - ac->comline = sh_getlineno(); + ac->comline = sh_getlineno(lexp); while(n==LPAREN) { struct argnod *ap; - ap = (struct argnod*)stakseek(ARGVAL); + ap = (struct argnod*)stkseek(stkp,ARGVAL); ap->argflag= ARG_ASSIGN; - sfprintf(stkstd,"[%d]=",index++); - ap = (struct argnod*)stakfreeze(1); + sfprintf(stkp,"[%d]=",index++); + ap = (struct argnod*)stkfreeze(stkp,1); ap->argnxt.ap = 0; - ap = assign(ap); + ap = assign(lexp,ap,0); ap->argflag |= ARG_MESSAGE; *settail = ap; settail = &(ap->argnxt.ap); - n = skipnl(0); + while((n = skipnl(lexp,0))==0) + { + ap = (struct argnod*)stkseek(stkp,ARGVAL); + ap->argflag= ARG_ASSIGN; + sfprintf(stkp,"[%d]=",index++); + stakputs(lexp->arg->argval); + ap = (struct argnod*)stkfreeze(stkp,1); + ap->argnxt.ap = 0; + ap->argflag = lexp->arg->argflag; + *settail = ap; + settail = &(ap->argnxt.ap); + } } } - else if(n) - sh_syntax(); - else if(!(shlex.arg->argflag&ARG_ASSIGN) && !((np=nv_search(shlex.arg->argval,sh.fun_tree,0)) && nv_isattr(np,BLT_DCL))) + else if(n && n!=FUNCTSYM) + sh_syntax(lexp); + else if(n!=FUNCTSYM && !(lexp->arg->argflag&ARG_ASSIGN) && !((np=nv_search(lexp->arg->argval,lexp->sh->fun_tree,0)) && (nv_isattr(np,BLT_DCL)|| np==SYSDOT))) + { array=SH_ARRAY; + if(fcgetc(n)==LPAREN) + { + int c; + if(fcgetc(c)==RPAREN) + { + lexp->token = SYMRES; + array = 0; + } + else + fcseek(-2); + } + else if(n>0) + fcseek(-1); + if(array && tdef) + sh_syntax(lexp); + } while(1) { - if((n=shlex.token)==RPAREN) + if((n=lexp->token)==RPAREN) break; if(n==FUNCTSYM || n==SYMRES) - ac = (struct comnod*)funct(); + ac = (struct comnod*)funct(lexp); else - ac = (struct comnod*)simple(SH_NOIO|SH_ASSIGN|array,NIL(struct ionod*)); - if((n=shlex.token)==RPAREN) + ac = (struct comnod*)simple(lexp,SH_NOIO|SH_ASSIGN|array,NIL(struct ionod*)); + if((n=lexp->token)==RPAREN) break; if(n!=NL && n!=';') - sh_syntax(); - shlex.assignok = SH_ASSIGN; - if((n=skipnl(0)) || array) + sh_syntax(lexp); + lexp->assignok = SH_ASSIGN; + if((n=skipnl(lexp,0)) || array) { if(n==RPAREN) break; if(array || n!=FUNCTSYM) - sh_syntax(); + sh_syntax(lexp); } - if((n!=FUNCTSYM) && !(shlex.arg->argflag&ARG_ASSIGN) && !((np=nv_search(shlex.arg->argval,sh.fun_tree,0)) && nv_isattr(np,BLT_DCL))) + if((n!=FUNCTSYM) && !(lexp->arg->argflag&ARG_ASSIGN) && !((np=nv_search(lexp->arg->argval,lexp->sh->fun_tree,0)) && (nv_isattr(np,BLT_DCL)||np==SYSDOT))) { - struct argnod *arg = shlex.arg; + struct argnod *arg = lexp->arg; if(n!=0) - sh_syntax(); + sh_syntax(lexp); /* check for sys5 style function */ - if(sh_lex()!=LPAREN || sh_lex()!=RPAREN) + if(sh_lex(lexp)!=LPAREN || sh_lex(lexp)!=RPAREN) { - shlex.arg = arg; - shlex.token = 0; - sh_syntax(); + lexp->arg = arg; + lexp->token = 0; + sh_syntax(lexp); } - shlex.arg = arg; - shlex.token = SYMRES; + lexp->arg = arg; + lexp->token = SYMRES; } - t = makelist(TLST,(Shnode_t*)ac,t); + t = makelist(lexp,TLST,(Shnode_t*)ac,t); *tp = t; tp = &t->lst.lstrit; } *tp = (Shnode_t*)ac; - shlex.assignok = 0; + lexp->assignok = 0; return(ap); } @@ -863,56 +983,56 @@ static struct argnod *assign(register struct argnod *ap) * begin ... end */ -static Shnode_t *item(int flag) +static Shnode_t *item(Lex_t *lexp,int flag) { register Shnode_t *t; register struct ionod *io; - register int tok = (shlex.token&0xff); - int savwdval = shlex.lasttok; - int savline = shlex.lastline; - int showme=0; - if(!(flag&SH_NOIO) && (tok=='<' || tok=='>')) - io=inout(NIL(struct ionod*),1); + register int tok = (lexp->token&0xff); + int savwdval = lexp->lasttok; + int savline = lexp->lastline; + int showme=0, comsub; + if(!(flag&SH_NOIO) && (tok=='<' || tok=='>' || lexp->token==IOVNAME)) + io=inout(lexp,NIL(struct ionod*),1); else io=0; - if((tok=shlex.token) && tok!=EOFSYM && tok!=FUNCTSYM) + if((tok=lexp->token) && tok!=EOFSYM && tok!=FUNCTSYM) { - shlex.lastline = sh_getlineno(); - shlex.lasttok = shlex.token; + lexp->lastline = sh_getlineno(lexp); + lexp->lasttok = lexp->token; } switch(tok) { /* [[ ... ]] test expression */ case BTESTSYM: - t = test_expr(ETESTSYM); + t = test_expr(lexp,ETESTSYM); t->tre.tretyp &= ~TTEST; break; /* ((...)) arithmetic expression */ case EXPRSYM: - t = getanode(shlex.arg); - sh_lex(); + t = getanode(lexp,lexp->arg); + sh_lex(lexp); goto done; /* case statement */ case CASESYM: { - int savetok = shlex.lasttok; - int saveline = shlex.lastline; + int savetok = lexp->lasttok; + int saveline = lexp->lastline; t = getnode(swnod); - if(sh_lex()) - sh_syntax(); - t->sw.swarg=shlex.arg; + if(sh_lex(lexp)) + sh_syntax(lexp); + t->sw.swarg=lexp->arg; t->sw.swtyp=TSW; t->sw.swio = 0; t->sw.swtyp |= FLINENO; - t->sw.swline = sh.inlineno; - if((tok=skipnl(0))!=INSYM && tok!=LBRACE) - sh_syntax(); - if(!(t->sw.swlst=syncase(tok==INSYM?ESACSYM:RBRACE)) && shlex.token==EOFSYM) + t->sw.swline = lexp->sh->inlineno; + if((tok=skipnl(lexp,0))!=INSYM && tok!=LBRACE) + sh_syntax(lexp); + if(!(t->sw.swlst=syncase(lexp,tok==INSYM?ESACSYM:RBRACE)) && lexp->token==EOFSYM) { - shlex.lasttok = savetok; - shlex.lastline = saveline; - sh_syntax(); + lexp->lasttok = savetok; + lexp->lastline = saveline; + sh_syntax(lexp); } break; } @@ -923,11 +1043,11 @@ static Shnode_t *item(int flag) register Shnode_t *tt; t = getnode(ifnod); t->if_.iftyp=TIF; - t->if_.iftre=sh_cmd(THENSYM,SH_NL); - t->if_.thtre=sh_cmd(ELSESYM,SH_NL|SH_SEMI); - tok = shlex.token; - t->if_.eltre=(tok==ELSESYM?sh_cmd(FISYM,SH_NL|SH_SEMI): - (tok==ELIFSYM?(shlex.token=IFSYM, tt=item(SH_NOIO)):0)); + t->if_.iftre=sh_cmd(lexp,THENSYM,SH_NL); + t->if_.thtre=sh_cmd(lexp,ELSESYM,SH_NL|SH_SEMI); + tok = lexp->token; + t->if_.eltre=(tok==ELSESYM?sh_cmd(lexp,FISYM,SH_NL|SH_SEMI): + (tok==ELIFSYM?(lexp->token=IFSYM, tt=item(lexp,SH_NOIO)):0)); if(tok==ELIFSYM) { if(!tt || tt->tre.tretyp!=TSETIO) @@ -945,33 +1065,33 @@ static Shnode_t *item(int flag) case SELECTSYM: { t = getnode(fornod); - t->for_.fortyp=(shlex.token==FORSYM?TFOR:TSELECT); + t->for_.fortyp=(lexp->token==FORSYM?TFOR:TSELECT); t->for_.forlst=0; - t->for_.forline = sh.inlineno; - if(sh_lex()) + t->for_.forline = lexp->sh->inlineno; + if(sh_lex(lexp)) { - if(shlex.token!=EXPRSYM || t->for_.fortyp!=TFOR) - sh_syntax(); + if(lexp->token!=EXPRSYM || t->for_.fortyp!=TFOR) + sh_syntax(lexp); /* arithmetic for */ - t = arithfor(t); + t = arithfor(lexp,t); break; } - t->for_.fornam=(char*) shlex.arg->argval; + t->for_.fornam=(char*) lexp->arg->argval; t->for_.fortyp |= FLINENO; #if SHOPT_KIA - if(shlex.kiafile) - writedefs(shlex.arg,sh.inlineno,'v',NIL(struct argnod*)); + if(lexp->kiafile) + writedefs(lexp,lexp->arg,lexp->sh->inlineno,'v',NIL(struct argnod*)); #endif /* SHOPT_KIA */ - while((tok=sh_lex())==NL); + while((tok=sh_lex(lexp))==NL); if(tok==INSYM) { - if(sh_lex()) + if(sh_lex(lexp)) { - if(shlex.token != NL && shlex.token !=';') - sh_syntax(); + if(lexp->token != NL && lexp->token !=';') + sh_syntax(lexp); /* some Linux scripts assume this */ if(sh_isoption(SH_NOEXEC)) - errormsg(SH_DICT,ERROR_warn(0),e_lexemptyfor,sh.inlineno-(shlex.token=='\n')); + errormsg(SH_DICT,ERROR_warn(0),e_lexemptyfor,lexp->sh->inlineno-(lexp->token=='\n')); t->for_.forlst = (struct comnod*)getnode(comnod); (t->for_.forlst)->comarg = 0; (t->for_.forlst)->comset = 0; @@ -982,18 +1102,18 @@ static Shnode_t *item(int flag) (t->for_.forlst)->comtyp = 0; } else - t->for_.forlst=(struct comnod*)simple(SH_NOIO,NIL(struct ionod*)); - if(shlex.token != NL && shlex.token !=';') - sh_syntax(); - tok = skipnl(0); + t->for_.forlst=(struct comnod*)simple(lexp,SH_NOIO,NIL(struct ionod*)); + if(lexp->token != NL && lexp->token !=';') + sh_syntax(lexp); + tok = skipnl(lexp,0); } /* 'for i;do cmd' is valid syntax */ else if(tok==';') - tok=sh_lex(); + tok=sh_lex(lexp); if(tok!=DOSYM && tok!=LBRACE) - sh_syntax(); + sh_syntax(lexp); loop_level++; - t->for_.fortre=sh_cmd(tok==DOSYM?DONESYM:RBRACE,SH_NL|SH_SEMI); + t->for_.fortre=sh_cmd(lexp,tok==DOSYM?DONESYM:RBRACE,SH_NL|SH_SEMI); if(--loop_level==0) label_last = label_list; break; @@ -1001,20 +1121,20 @@ static Shnode_t *item(int flag) /* This is the code for parsing function definitions */ case FUNCTSYM: - return(funct()); + return(funct(lexp)); #if SHOPT_NAMESPACE case NSPACESYM: t = getnode(fornod); t->for_.fortyp=TNSPACE; t->for_.forlst=0; - if(sh_lex()) - sh_syntax(); - t->for_.fornam=(char*) shlex.arg->argval; - while((tok=sh_lex())==NL); + if(sh_lex(lexp)) + sh_syntax(lexp); + t->for_.fornam=(char*) lexp->arg->argval; + while((tok=sh_lex(lexp))==NL); if(tok!=LBRACE) - sh_syntax(); - t->for_.fortre = sh_cmd(RBRACE,SH_NL); + sh_syntax(lexp); + t->for_.fortre = sh_cmd(lexp,RBRACE,SH_NL); break; #endif /* SHOPT_NAMESPACE */ @@ -1022,10 +1142,10 @@ static Shnode_t *item(int flag) case WHILESYM: case UNTILSYM: t = getnode(whnod); - t->wh.whtyp=(shlex.token==WHILESYM ? TWH : TUN); + t->wh.whtyp=(lexp->token==WHILESYM ? TWH : TUN); loop_level++; - t->wh.whtre = sh_cmd(DOSYM,SH_NL); - t->wh.dotre = sh_cmd(DONESYM,SH_NL|SH_SEMI); + t->wh.whtre = sh_cmd(lexp,DOSYM,SH_NL); + t->wh.dotre = sh_cmd(lexp,DONESYM,SH_NL|SH_SEMI); if(--loop_level==0) label_last = label_list; t->wh.whinc = 0; @@ -1036,17 +1156,17 @@ static Shnode_t *item(int flag) register struct argnod *argp = label_list; while(argp) { - if(strcmp(argp->argval,shlex.arg->argval)==0) - errormsg(SH_DICT,ERROR_exit(3),e_lexsyntax3,sh.inlineno,argp->argval); + if(strcmp(argp->argval,lexp->arg->argval)==0) + errormsg(SH_DICT,ERROR_exit(3),e_lexsyntax3,lexp->sh->inlineno,argp->argval); argp = argp->argnxt.ap; } - shlex.arg->argnxt.ap = label_list; - label_list = shlex.arg; - label_list->argchn.len = sh_getlineno(); + lexp->arg->argnxt.ap = label_list; + label_list = lexp->arg; + label_list->argchn.len = sh_getlineno(lexp); label_list->argflag = loop_level; - skipnl(flag); - if(!(t = item(SH_NL))) - sh_syntax(); + skipnl(lexp,flag); + if(!(t = item(lexp,SH_NL))) + sh_syntax(lexp); tok = (t->tre.tretyp&(COMSCAN|COMSCAN-1)); if(sh_isoption(SH_NOEXEC) && tok!=TWH && tok!=TUN && tok!=TFOR && tok!=TSELECT) errormsg(SH_DICT,ERROR_warn(0),e_lexlabignore,label_list->argchn.len,label_list->argval); @@ -1055,12 +1175,15 @@ static Shnode_t *item(int flag) /* command group with {...} */ case LBRACE: - t = sh_cmd(RBRACE,SH_NL); + comsub = lexp->comsub; + lexp->comsub = 0; + t = sh_cmd(lexp,RBRACE,SH_NL); + lexp->comsub = comsub; break; case LPAREN: t = getnode(parnod); - t->par.partre=sh_cmd(RPAREN,SH_NL); + t->par.partre=sh_cmd(lexp,RPAREN,SH_NL); t->par.partyp=TPAR; break; @@ -1073,45 +1196,51 @@ static Shnode_t *item(int flag) { if(!(flag&SH_SEMI)) return(0); - if(sh_lex()==';') - sh_syntax(); + if(sh_lex(lexp)==';') + sh_syntax(lexp); showme = FSHOWME; } /* simple command */ case 0: - t = (Shnode_t*)simple(flag,io); + t = (Shnode_t*)simple(lexp,flag,io); + if(t->com.comarg && lexp->intypeset && (lexp->sh->shcomp || sh_isoption(SH_NOEXEC) || sh.dot_depth)) + check_typedef(&t->com); + lexp->intypeset = 0; + lexp->inexec = 0; t->tre.tretyp |= showme; return(t); } - sh_lex(); - if(io=inout(io,0)) + sh_lex(lexp); + if(io=inout(lexp,io,0)) { if((tok=t->tre.tretyp&COMMSK) != TFORK) tok = TSETIO; - t=makeparent(tok,t); + t=makeparent(lexp,tok,t); t->tre.treio=io; } done: - shlex.lasttok = savwdval; - shlex.lastline = savline; + lexp->lasttok = savwdval; + lexp->lastline = savline; return(t); } /* * This is for a simple command, for list, or compound assignment */ -static Shnode_t *simple(int flag, struct ionod *io) +static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io) { register struct comnod *t; register struct argnod *argp; register int tok; + Stk_t *stkp = lexp->sh->stk; struct argnod **argtail; struct argnod **settail; - int argno = 0; + int cmdarg=0; + int argno = 0, argmax=0; int assignment = 0; int key_on = (!(flag&SH_NOIO) && sh_isoption(SH_KEYWORD)); int associative=0; - if((argp=shlex.arg) && (argp->argflag&ARG_ASSIGN) && argp->argval[0]=='[') + if((argp=lexp->arg) && (argp->argflag&ARG_ASSIGN) && argp->argval[0]=='[') { flag |= SH_ARRAY; associative = 1; @@ -1119,29 +1248,29 @@ static Shnode_t *simple(int flag, struct ionod *io) t = (struct comnod*)getnode(comnod); t->comio=io; /*initial io chain*/ /* set command line number for error messages */ - t->comline = sh_getlineno(); + t->comline = sh_getlineno(lexp); argtail = &(t->comarg); t->comset = 0; t->comnamp = 0; t->comnamq = 0; t->comstate = 0; settail = &(t->comset); - while(shlex.token==0) + while(lexp->token==0) { - argp = shlex.arg; + argp = lexp->arg; if(*argp->argval==LBRACE && (flag&SH_FUNDEF) && argp->argval[1]==0) { - shlex.token = LBRACE; + lexp->token = LBRACE; break; } if(associative && argp->argval[0]!='[') - sh_syntax(); + sh_syntax(lexp); /* check for assignment argument */ if((argp->argflag&ARG_ASSIGN) && assignment!=2) { *settail = argp; settail = &(argp->argnxt.ap); - shlex.assignok = (flag&SH_ASSIGN)?SH_ASSIGN:1; + lexp->assignok = (flag&SH_ASSIGN)?SH_ASSIGN:1; if(assignment) { struct argnod *ap=argp; @@ -1151,9 +1280,9 @@ static Shnode_t *simple(int flag, struct ionod *io) last = strchr(argp->argval,'='); if((cp=strchr(argp->argval,'[')) && (cp < last)) last = cp; - stakseek(ARGVAL); - stakwrite(argp->argval,last-argp->argval); - ap=(struct argnod*)stakfreeze(1); + stkseek(stkp,ARGVAL); + sfwrite(stkp,argp->argval,last-argp->argval); + ap=(struct argnod*)stkfreeze(stkp,1); ap->argflag = ARG_RAW; ap->argchn.ap = 0; } @@ -1163,43 +1292,62 @@ static Shnode_t *simple(int flag, struct ionod *io) argno++; } else /* alias substitutions allowed */ - shlex.aliasok = 1; + lexp->aliasok = 1; } else { if(!(argp->argflag&ARG_RAW)) + { + if(argno>0) + argmax = argno; argno = -1; - if(argno>=0 && argno++==0 && !(flag&SH_ARRAY) && *argp->argval!='/') + } + if(argno>=0 && argno++==cmdarg && !(flag&SH_ARRAY) && *argp->argval!='/') { /* check for builtin command */ - Namval_t *np=nv_bfsearch(argp->argval,sh.fun_tree, (Namval_t**)&t->comnamq,(char**)0); - if((t->comnamp=(void*)np) && is_abuiltin(np) && - nv_isattr(np,BLT_DCL)) + Namval_t *np=nv_bfsearch(argp->argval,lexp->sh->fun_tree, (Namval_t**)&t->comnamq,(char**)0); + if(cmdarg==0) + t->comnamp = (void*)np; + if(np && is_abuiltin(np)) { - assignment = 1+(*argp->argval=='a'); - key_on = 1; + if(nv_isattr(np,BLT_DCL)) + { + assignment = 1+(*argp->argval=='a'); + if(np==SYSTYPESET) + lexp->intypeset = 1; + key_on = 1; + } + else if(np==SYSCOMMAND) + cmdarg++; + else if(np==SYSEXEC) + lexp->inexec = 1; + else if(np->nvalue.bfp==b_getopts) + opt_get |= FOPTGET; } } *argtail = argp; argtail = &(argp->argnxt.ap); - if(!(shlex.assignok=key_on) && !(flag&SH_NOIO)) - shlex.assignok = SH_COMPASSIGN; - shlex.aliasok = 0; + if(!(lexp->assignok=key_on) && !(flag&SH_NOIO) && sh_isoption(SH_NOEXEC)) + lexp->assignok = SH_COMPASSIGN; + lexp->aliasok = 0; } retry: - tok = sh_lex(); + tok = sh_lex(lexp); + if(tok==LABLSYM && (flag&SH_ASSIGN)) + lexp->token = tok = 0; #if SHOPT_DEVFD if((tok==IPROCSYM || tok==OPROCSYM)) { Shnode_t *t; int mode = (tok==OPROCSYM); - t = sh_cmd(RPAREN,SH_NL); - argp = (struct argnod*)stakalloc(sizeof(struct argnod)); + t = sh_cmd(lexp,RPAREN,SH_NL); + argp = (struct argnod*)stkalloc(stkp,sizeof(struct argnod)); *argp->argval = 0; + argmax = 0; argno = -1; *argtail = argp; argtail = &(argp->argnxt.ap); - argp->argchn.ap = (struct argnod*)makeparent(mode?TFORK|FPIN|FAMP|FPCL:TFORK|FPOU,t); + argp->argchn.ap = (struct argnod*)makeparent(lexp,mode?TFORK|FPIN|FAMP|FPCL:TFORK|FPOU,t); argp->argflag = (ARG_EXP|mode); goto retry; } @@ -1208,20 +1356,26 @@ static Shnode_t *simple(int flag, struct ionod *io) { if(argp->argflag&ARG_ASSIGN) { - argp = assign(argp); + int intypeset = lexp->intypeset; + int tdef = 0; + lexp->intypeset = 0; + if(t->comnamp==SYSTYPESET && t->comarg->argnxt.ap && strcmp(t->comarg->argnxt.ap->argval,"-T")==0) + tdef = 1; + argp = assign(lexp,argp,tdef); + lexp->intypeset = intypeset; if(associative) - shlex.assignok |= SH_ASSIGN; + lexp->assignok |= SH_ASSIGN; goto retry; } else if(argno==1 && !t->comset) { /* SVR2 style function */ - if(sh_lex() == RPAREN) + if(sh_lex(lexp) == RPAREN) { - shlex.arg = argp; - return(funct()); + lexp->arg = argp; + return(funct(lexp)); } - shlex.token = LPAREN; + lexp->token = LPAREN; } } else if(flag&SH_ASSIGN) @@ -1229,7 +1383,11 @@ static Shnode_t *simple(int flag, struct ionod *io) if(tok==RPAREN) break; else if(tok==NL && (flag&SH_ARRAY)) + { + lexp->comp_assign = 2; goto retry; + } + } if(!(flag&SH_NOIO)) { @@ -1237,41 +1395,43 @@ static Shnode_t *simple(int flag, struct ionod *io) { while(io->ionxt) io = io->ionxt; - io->ionxt = inout((struct ionod*)0,0); + io->ionxt = inout(lexp,(struct ionod*)0,0); } else - t->comio = io = inout((struct ionod*)0,0); + t->comio = io = inout(lexp,(struct ionod*)0,0); } } *argtail = 0; - t->comtyp = TCOM; + if(argno>0) + argmax = argno; + t->comtyp = TCOM | (argmax<<(COMBITS+2)); #if SHOPT_KIA - if(shlex.kiafile && !(flag&SH_NOIO)) + if(lexp->kiafile && !(flag&SH_NOIO)) { register Namval_t *np=(Namval_t*)t->comnamp; unsigned long r=0; int line = t->comline; argp = t->comarg; if(np) - r = kiaentity(nv_name(np),-1,'p',-1,0,shlex.unknown,'b',0,""); + r = kiaentity(lexp,nv_name(np),-1,'p',-1,0,lexp->unknown,'b',0,""); else if(argp) - r = kiaentity(sh_argstr(argp),-1,'p',-1,0,shlex.unknown,'c',0,""); + r = kiaentity(lexp,sh_argstr(argp),-1,'p',-1,0,lexp->unknown,'c',0,""); if(r>0) - sfprintf(shlex.kiatmp,"p;%..64d;p;%..64d;%d;%d;c;\n",shlex.current,r,line,line); + sfprintf(lexp->kiatmp,"p;%..64d;p;%..64d;%d;%d;c;\n",lexp->current,r,line,line); if(t->comset && argno==0) - writedefs(t->comset,line,'v',t->comarg); + writedefs(lexp,t->comset,line,'v',t->comarg); else if(np && nv_isattr(np,BLT_DCL)) - writedefs(argp,line,0,NIL(struct argnod*)); + writedefs(lexp,argp,line,0,NIL(struct argnod*)); else if(argp && strcmp(argp->argval,"read")==0) - writedefs(argp,line,0,NIL(struct argnod*)); + writedefs(lexp,argp,line,0,NIL(struct argnod*)); #if 0 else if(argp && strcmp(argp->argval,"unset")==0) - writedefs(argp,line,'u',NIL(struct argnod*)); + writedefs(lexp,argp,line,'u',NIL(struct argnod*)); #endif else if(argp && *argp->argval=='.' && argp->argval[1]==0 && (argp=argp->argnxt.ap)) { - r = kiaentity(sh_argstr(argp),-1,'p',0,0,shlex.script,'d',0,""); - sfprintf(shlex.kiatmp,"p;%..64d;p;%..64d;%d;%d;d;\n",shlex.current,r,line,line); + r = kiaentity(lexp,sh_argstr(argp),-1,'p',0,0,lexp->script,'d',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;p;%..64d;%d;%d;d;\n",lexp->current,r,line,line); } } #endif /* SHOPT_KIA */ @@ -1304,30 +1464,30 @@ static Shnode_t *simple(int flag, struct ionod *io) break; } if(sh_isoption(SH_NOEXEC) && tok==0) - errormsg(SH_DICT,ERROR_warn(0),e_lexlabunknown,sh.inlineno-(shlex.token=='\n'),cp); + errormsg(SH_DICT,ERROR_warn(0),e_lexlabunknown,lexp->sh->inlineno-(lexp->token=='\n'),cp); } else if(sh_isoption(SH_NOEXEC) && np==SYSSET && ((tok= *argp->argval)=='-'||tok=='+') && (argp->argval[1]==0||strchr(argp->argval,'k'))) - errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete5,sh.inlineno-(shlex.token=='\n'),argp->argval); + errormsg(SH_DICT,ERROR_warn(0),e_lexobsolete5,lexp->sh->inlineno-(lexp->token=='\n'),argp->argval); } /* expand argument list if possible */ if(argno>0) t->comarg = qscan(t,argno); else if(t->comarg) t->comtyp |= COMSCAN; - shlex.aliasok = 0; + lexp->aliasok = 0; return((Shnode_t*)t); } /* * skip past newlines but issue prompt if interactive */ -static int skipnl(int flag) +static int skipnl(Lex_t *lexp,int flag) { register int token; - while((token=sh_lex())==NL); + while((token=sh_lex(lexp))==NL); if(token==';' && !(flag&SH_SEMI)) - sh_syntax(); + sh_syntax(lexp); return(token); } @@ -1336,18 +1496,19 @@ static int skipnl(int flag) * if flag>0 then an alias can be in the next word * if flag<0 only one redirection will be processed */ -static struct ionod *inout(struct ionod *lastio,int flag) +static struct ionod *inout(Lex_t *lexp,struct ionod *lastio,int flag) { - register int iof = shlex.digits, token=shlex.token; + register int iof = lexp->digits, token=lexp->token; register struct ionod *iop; + Stk_t *stkp = lexp->sh->stk; char *iovname=0; #if SHOPT_BASH register int errout=0; #endif if(token==IOVNAME) { - iovname=shlex.arg->argval+1; - token= sh_lex(); + iovname=lexp->arg->argval+1; + token= sh_lex(lexp); iof = 0; } switch(token&0xff) @@ -1387,83 +1548,85 @@ static struct ionod *inout(struct ionod *lastio,int flag) iof |= IOCLOB; else if((token&SYMSHARP) == SYMSHARP) iof |= IOLSEEK; + else if((token&SYMSEMI) == SYMSEMI) + iof |= IOREWRITE; break; default: return(lastio); } - shlex.digits=0; - iop=(struct ionod*) stakalloc(sizeof(struct ionod)); + lexp->digits=0; + iop=(struct ionod*) stkalloc(stkp,sizeof(struct ionod)); iop->iodelim = 0; - if(token=sh_lex()) + if(token=sh_lex(lexp)) { - if(token==RPAREN && (iof&IOLSEEK) && shlex.comsub) + if(token==RPAREN && (iof&IOLSEEK) && lexp->comsub) { - shlex.arg = (struct argnod*)stakalloc(sizeof(struct argnod)+3); - strcpy(shlex.arg->argval,"CUR"); - shlex.arg->argflag = ARG_RAW; + lexp->arg = (struct argnod*)stkalloc(stkp,sizeof(struct argnod)+3); + strcpy(lexp->arg->argval,"CUR"); + lexp->arg->argflag = ARG_RAW; iof |= IOARITH; fcseek(-1); } else if(token==EXPRSYM && (iof&IOLSEEK)) iof |= IOARITH; else - sh_syntax(); + sh_syntax(lexp); } - iop->ioname=shlex.arg->argval; + iop->ioname=lexp->arg->argval; iop->iovname = iovname; if(iof&IODOC) { - if(shlex.digits==2) + if(lexp->digits==2) { iof |= IOSTRG; - if(!(shlex.arg->argflag&ARG_RAW)) + if(!(lexp->arg->argflag&ARG_RAW)) iof &= ~IORAW; } else { - if(!shlex.sh->heredocs) - shlex.sh->heredocs = sftmp(HERE_MEM); - iop->iolst=shlex.heredoc; - shlex.heredoc=iop; - if(shlex.arg->argflag&ARG_QUOTED) + if(!lexp->sh->heredocs) + lexp->sh->heredocs = sftmp(HERE_MEM); + iop->iolst=lexp->heredoc; + lexp->heredoc=iop; + if(lexp->arg->argflag&ARG_QUOTED) iof |= IOQUOTE; - if(shlex.digits==3) + if(lexp->digits==3) iof |= IOLSEEK; - if(shlex.digits) + if(lexp->digits) iof |= IOSTRIP; } } else { iop->iolst = 0; - if(shlex.arg->argflag&ARG_RAW) + if(lexp->arg->argflag&ARG_RAW) iof |= IORAW; } iop->iofile=iof; if(flag>0) /* allow alias substitutions and parameter assignments */ - shlex.aliasok = shlex.assignok = 1; + lexp->aliasok = lexp->assignok = 1; #if SHOPT_KIA - if(shlex.kiafile) + if(lexp->kiafile) { - int n = sh.inlineno-(shlex.token=='\n'); + int n = lexp->sh->inlineno-(lexp->token=='\n'); if(!(iof&IOMOV)) { - unsigned long r=kiaentity((iof&IORAW)?sh_fmtq(iop->ioname):iop->ioname,-1,'f',0,0,shlex.script,'f',0,""); - sfprintf(shlex.kiatmp,"p;%..64d;f;%..64d;%d;%d;%c;%d\n",shlex.current,r,n,n,(iof&IOPUT)?((iof&IOAPP)?'a':'w'):((iof&IODOC)?'h':'r'),iof&IOUFD); + unsigned long r=kiaentity(lexp,(iof&IORAW)?sh_fmtq(iop->ioname):iop->ioname,-1,'f',0,0,lexp->script,'f',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;%c;%d\n",lexp->current,r,n,n,(iof&IOPUT)?((iof&IOAPP)?'a':'w'):((iof&IODOC)?'h':'r'),iof&IOUFD); } } #endif /* SHOPT_KIA */ if(flag>=0) { struct ionod *ioq=iop; - sh_lex(); + sh_lex(lexp); #if SHOPT_BASH if(errout) { /* redirect standard output to standard error */ - ioq = (struct ionod*)stakalloc(sizeof(struct ionod)); + ioq = (struct ionod*)stkalloc(stkp,sizeof(struct ionod)); ioq->ioname = "1"; ioq->iolst = 0; ioq->iodelim = 0; @@ -1471,7 +1634,7 @@ static struct ionod *inout(struct ionod *lastio,int flag) iop->ionxt=ioq; } #endif - ioq->ionxt=inout(lastio,flag); + ioq->ionxt=inout(lexp,lastio,flag); } else iop->ionxt=0; @@ -1540,27 +1703,27 @@ static struct argnod *qscan(struct comnod *ac,int argn) return((struct argnod*)dp); } -static Shnode_t *test_expr(int sym) +static Shnode_t *test_expr(Lex_t *lp,int sym) { - register Shnode_t *t = test_or(); - if(shlex.token!=sym) - sh_syntax(); + register Shnode_t *t = test_or(lp); + if(lp->token!=sym) + sh_syntax(lp); return(t); } -static Shnode_t *test_or(void) +static Shnode_t *test_or(Lex_t *lp) { - register Shnode_t *t = test_and(); - while(shlex.token==ORFSYM) - t = makelist(TORF|TTEST,t,test_and()); + register Shnode_t *t = test_and(lp); + while(lp->token==ORFSYM) + t = makelist(lp,TORF|TTEST,t,test_and(lp)); return(t); } -static Shnode_t *test_and(void) +static Shnode_t *test_and(Lex_t *lp) { - register Shnode_t *t = test_primary(); - while(shlex.token==ANDFSYM) - t = makelist(TAND|TTEST,t,test_primary()); + register Shnode_t *t = test_primary(lp); + while(lp->token==ANDFSYM) + t = makelist(lp,TAND|TTEST,t,test_primary(lp)); return(t); } @@ -1581,46 +1744,46 @@ static void ere_match(void) fcfopen(base); } -static Shnode_t *test_primary(void) +static Shnode_t *test_primary(Lex_t *lexp) { register struct argnod *arg; register Shnode_t *t; register int num,token; - token = skipnl(0); - num = shlex.digits; + token = skipnl(lexp,0); + num = lexp->digits; switch(token) { case '(': - t = test_expr(')'); - t = makelist(TTST|TTEST|TPAREN ,t, (Shnode_t*)pointerof(sh.inlineno)); + t = test_expr(lexp,')'); + t = makelist(lexp,TTST|TTEST|TPAREN ,t, (Shnode_t*)pointerof(lexp->sh->inlineno)); break; case '!': - if(!(t = test_primary())) - sh_syntax(); + if(!(t = test_primary(lexp))) + sh_syntax(lexp); t->tre.tretyp |= TNEGATE; return(t); case TESTUNOP: - if(sh_lex()) - sh_syntax(); + if(sh_lex(lexp)) + sh_syntax(lexp); #if SHOPT_KIA - if(shlex.kiafile && !strchr("sntzoOG",num)) + if(lexp->kiafile && !strchr("sntzoOG",num)) { - int line = sh.inlineno- (shlex.token==NL); + int line = lexp->sh->inlineno- (lexp->token==NL); unsigned long r; - r=kiaentity(sh_argstr(shlex.arg),-1,'f',0,0,shlex.script,'t',0,""); - sfprintf(shlex.kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",shlex.current,r,line,line); + r=kiaentity(lexp,sh_argstr(lexp->arg),-1,'f',0,0,lexp->script,'t',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",lexp->current,r,line,line); } #endif /* SHOPT_KIA */ - t = makelist(TTST|TTEST|TUNARY|(num<<TSHIFT), - (Shnode_t*)shlex.arg,(Shnode_t*)shlex.arg); - t->tst.tstline = sh.inlineno; + t = makelist(lexp,TTST|TTEST|TUNARY|(num<<TSHIFT), + (Shnode_t*)lexp->arg,(Shnode_t*)lexp->arg); + t->tst.tstline = lexp->sh->inlineno; break; /* binary test operators */ case 0: - arg = shlex.arg; - if((token=sh_lex())==TESTBINOP) + arg = lexp->arg; + if((token=sh_lex(lexp))==TESTBINOP) { - num = shlex.digits; + num = lexp->digits; if(num==TEST_REP) { ere_match(); @@ -1633,48 +1796,48 @@ static Shnode_t *test_primary(void) num = TEST_SGT; else if(token==ANDFSYM||token==ORFSYM||token==ETESTSYM||token==RPAREN) { - t = makelist(TTST|TTEST|TUNARY|('n'<<TSHIFT), + t = makelist(lexp,TTST|TTEST|TUNARY|('n'<<TSHIFT), (Shnode_t*)arg,(Shnode_t*)arg); - t->tst.tstline = sh.inlineno; + t->tst.tstline = lexp->sh->inlineno; return(t); } else - sh_syntax(); + sh_syntax(lexp); #if SHOPT_KIA - if(shlex.kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT)) + if(lexp->kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT)) { - int line = sh.inlineno- (shlex.token==NL); + int line = lexp->sh->inlineno- (lexp->token==NL); unsigned long r; - r=kiaentity(sh_argstr(shlex.arg),-1,'f',0,0,shlex.current,'t',0,""); - sfprintf(shlex.kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",shlex.current,r,line,line); + r=kiaentity(lexp,sh_argstr(lexp->arg),-1,'f',0,0,lexp->current,'t',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",lexp->current,r,line,line); } #endif /* SHOPT_KIA */ - if(sh_lex()) - sh_syntax(); + if(sh_lex(lexp)) + sh_syntax(lexp); if(num&TEST_PATTERN) { - if(shlex.arg->argflag&(ARG_EXP|ARG_MAC)) + if(lexp->arg->argflag&(ARG_EXP|ARG_MAC)) num &= ~TEST_PATTERN; } t = getnode(tstnod); t->lst.lsttyp = TTST|TTEST|TBINARY|(num<<TSHIFT); t->lst.lstlef = (Shnode_t*)arg; - t->lst.lstrit = (Shnode_t*)shlex.arg; - t->tst.tstline = sh.inlineno; + t->lst.lstrit = (Shnode_t*)lexp->arg; + t->tst.tstline = lexp->sh->inlineno; #if SHOPT_KIA - if(shlex.kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT)) + if(lexp->kiafile && (num==TEST_EF||num==TEST_NT||num==TEST_OT)) { - int line = sh.inlineno-(shlex.token==NL); + int line = lexp->sh->inlineno-(lexp->token==NL); unsigned long r; - r=kiaentity(sh_argstr(shlex.arg),-1,'f',0,0,shlex.current,'t',0,""); - sfprintf(shlex.kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",shlex.current,r,line,line); + r=kiaentity(lexp,sh_argstr(lexp->arg),-1,'f',0,0,lexp->current,'t',0,""); + sfprintf(lexp->kiatmp,"p;%..64d;f;%..64d;%d;%d;t;\n",lexp->current,r,line,line); } #endif /* SHOPT_KIA */ break; default: return(0); } - skipnl(0); + skipnl(lexp,0); return(t); } @@ -1683,23 +1846,23 @@ static Shnode_t *test_primary(void) * return an entity checksum * The entity is created if it doesn't exist */ -unsigned long kiaentity(const char *name,int len,int type,int first,int last,unsigned long parent, int pkind, int width, const char *attr) +unsigned long kiaentity(Lex_t *lexp,const char *name,int len,int type,int first,int last,unsigned long parent, int pkind, int width, const char *attr) { + Stk_t *stkp = lexp->sh->stk; Namval_t *np; - long offset = staktell(); - stakputc(type); + long offset = stktell(stkp); + sfputc(stkp,type); if(len>0) - stakwrite(name,len); + sfwrite(stkp,name,len); else { if(type=='p') - stakputs(path_basename(name)); + sfputr(stkp,path_basename(name),0); else - stakputs(name); + sfputr(stkp,name,0); } - stakputc(0); - np = nv_search(stakptr(offset),shlex.entity_tree,NV_ADD); - stakseek(offset); + np = nv_search(stakptr(offset),lexp->entity_tree,NV_ADD); + stkseek(stkp,offset); np->nvalue.i = pkind; nv_setsize(np,width); if(!nv_isattr(np,NV_TAGGED) && first>=0) @@ -1708,9 +1871,9 @@ unsigned long kiaentity(const char *name,int len,int type,int first,int last,uns if(!pkind) pkind = '0'; if(len>0) - sfprintf(shlex.kiafile,"%..64d;%c;%.*s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,len,name,first,last,parent,shlex.fscript,pkind,width,attr); + sfprintf(lexp->kiafile,"%..64d;%c;%.*s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,len,name,first,last,parent,lexp->fscript,pkind,width,attr); else - sfprintf(shlex.kiafile,"%..64d;%c;%s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,name,first,last,parent,shlex.fscript,pkind,width,attr); + sfprintf(lexp->kiafile,"%..64d;%c;%s;%d;%d;%..64d;%..64d;%c;%d;%s\n",np->hash,type,name,first,last,parent,lexp->fscript,pkind,width,attr); } return(np->hash); } @@ -1718,40 +1881,41 @@ unsigned long kiaentity(const char *name,int len,int type,int first,int last,uns static void kia_add(register Namval_t *np, void *data) { char *name = nv_name(np); + Lex_t *lp = (Lex_t*)data; NOT_USED(data); - kiaentity(name+1,-1,*name,0,-1,(*name=='p'?shlex.unknown:shlex.script),np->nvalue.i,nv_size(np),""); + kiaentity(lp,name+1,-1,*name,0,-1,(*name=='p'?lp->unknown:lp->script),np->nvalue.i,nv_size(np),""); } -int kiaclose(void) +int kiaclose(Lex_t *lexp) { register off_t off1,off2; register int n; - if(shlex.kiafile) + if(lexp->kiafile) { - unsigned long r = kiaentity(shlex.scriptname,-1,'p',-1,sh.inlineno-1,0,'s',0,""); - kiaentity(shlex.scriptname,-1,'p',1,sh.inlineno-1,r,'s',0,""); - kiaentity(shlex.scriptname,-1,'f',1,sh.inlineno-1,r,'s',0,""); - nv_scan(shlex.entity_tree,kia_add,(void*)0,NV_TAGGED,0); - off1 = sfseek(shlex.kiafile,(off_t)0,SEEK_END); - sfseek(shlex.kiatmp,(off_t)0,SEEK_SET); - sfmove(shlex.kiatmp,shlex.kiafile,SF_UNBOUND,-1); - off2 = sfseek(shlex.kiafile,(off_t)0,SEEK_END); + unsigned long r = kiaentity(lexp,lexp->scriptname,-1,'p',-1,lexp->sh->inlineno-1,0,'s',0,""); + kiaentity(lexp,lexp->scriptname,-1,'p',1,lexp->sh->inlineno-1,r,'s',0,""); + kiaentity(lexp,lexp->scriptname,-1,'f',1,lexp->sh->inlineno-1,r,'s',0,""); + nv_scan(lexp->entity_tree,kia_add,(void*)lexp,NV_TAGGED,0); + off1 = sfseek(lexp->kiafile,(off_t)0,SEEK_END); + sfseek(lexp->kiatmp,(off_t)0,SEEK_SET); + sfmove(lexp->kiatmp,lexp->kiafile,SF_UNBOUND,-1); + off2 = sfseek(lexp->kiafile,(off_t)0,SEEK_END); #ifdef SF_BUFCONST if(off2==off1) - n= sfprintf(shlex.kiafile,"DIRECTORY\nENTITY;%lld;%d\nDIRECTORY;",(Sflong_t)shlex.kiabegin,(size_t)(off1-shlex.kiabegin)); + n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%lld;%d\nDIRECTORY;",(Sflong_t)lexp->kiabegin,(size_t)(off1-lexp->kiabegin)); else - n= sfprintf(shlex.kiafile,"DIRECTORY\nENTITY;%lld;%d\nRELATIONSHIP;%lld;%d\nDIRECTORY;",(Sflong_t)shlex.kiabegin,(size_t)(off1-shlex.kiabegin),(Sflong_t)off1,(size_t)(off2-off1)); + n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%lld;%d\nRELATIONSHIP;%lld;%d\nDIRECTORY;",(Sflong_t)lexp->kiabegin,(size_t)(off1-lexp->kiabegin),(Sflong_t)off1,(size_t)(off2-off1)); if(off2 >= INT_MAX) off2 = -(n+12); - sfprintf(shlex.kiafile,"%010.10lld;%010d\n",(Sflong_t)off2+10, n+12); + sfprintf(lexp->kiafile,"%010.10lld;%010d\n",(Sflong_t)off2+10, n+12); #else if(off2==off1) - n= sfprintf(shlex.kiafile,"DIRECTORY\nENTITY;%d;%d\nDIRECTORY;",shlex.kiabegin,off1-shlex.kiabegin); + n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%d;%d\nDIRECTORY;",lexp->kiabegin,off1-lexp->kiabegin); else - n= sfprintf(shlex.kiafile,"DIRECTORY\nENTITY;%d;%d\nRELATIONSHIP;%d;%d\nDIRECTORY;",shlex.kiabegin,off1-shlex.kiabegin,off1,off2-off1); - sfprintf(shlex.kiafile,"%010d;%010d\n",off2+10, n+12); + n= sfprintf(lexp->kiafile,"DIRECTORY\nENTITY;%d;%d\nRELATIONSHIP;%d;%d\nDIRECTORY;",lexp->kiabegin,off1-lexp->kiabegin,off1,off2-off1); + sfprintf(lexp->kiafile,"%010d;%010d\n",off2+10, n+12); #endif } - return(sfclose(shlex.kiafile)); + return(sfclose(lexp->kiafile)); } #endif /* SHOPT_KIA */ diff --git a/usr/src/lib/libshell/common/sh/path.c b/usr/src/lib/libshell/common/sh/path.c index a07a9d6e00..fc91fb34e4 100644 --- a/usr/src/lib/libshell/common/sh/path.c +++ b/usr/src/lib/libshell/common/sh/path.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -28,13 +28,13 @@ #include <fcin.h> #include <ls.h> #include <nval.h> -#include <dlldefs.h> #include "variables.h" #include "path.h" #include "io.h" #include "jobs.h" #include "history.h" #include "test.h" +#include "FEATURE/dynamic" #include "FEATURE/externs" #if SHOPT_PFSH # ifdef _hdr_exec_attr @@ -42,6 +42,11 @@ # else # undef SHOPT_PFSH # endif +# if _lib_vfork +# include <ast_vfork.h> +# else +# define vfork() fork() +# endif #endif #define RW_ALL (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH) @@ -69,10 +74,18 @@ static int onstdpath(const char *name) return(0); } -static int path_pfexecve(const char *path, char *argv[],char *const envp[]) +static pid_t path_pfexecve(const char *path, char *argv[],char *const envp[],int spawn) { #if SHOPT_PFSH + pid_t pid; char resolvedpath[PATH_MAX + 1]; + if(spawn) + { + while((pid = vfork()) < 0) + _sh_fork(pid, 0, (int*)0); + if(pid) + return(pid); + } if(!sh_isoption(SH_PFSH)) return(execve(path, argv, envp)); /* Solaris implements realpath(3C) using the resolvepath(2) */ @@ -111,11 +124,19 @@ static int path_pfexecve(const char *path, char *argv[],char *const envp[]) } -static pid_t _spawnveg(const char *path, char* const argv[], char* const envp[], pid_t pid) +static pid_t _spawnveg(const char *path, char* const argv[], char* const envp[], pid_t pgid) { int waitsafe = job.waitsafe; + pid_t pid; job_lock(); - pid = spawnveg(path,argv,envp,pid); + while(1) + { + sh_stats(STAT_SPAWN); + pid = spawnveg(path,argv,envp,pgid); + if(pid>=0 || errno!=EAGAIN) + break; + _sh_fork(pid, 0, (int*)0); + } job.waitsafe = waitsafe; job_unlock(); return(pid); @@ -189,7 +210,7 @@ static pid_t path_xargs(const char *path, char *argv[],char *const envp[], int s return(_spawnveg(path,argv,envp,spawn>>1)); } else - return((pid_t)path_pfexecve(path,argv,envp)); + return(path_pfexecve(path,argv,envp,spawn)); } if(!spawn) exit(exitval); @@ -273,7 +294,7 @@ static void free_bltin(Namval_t *np,void *data) return; } if((void*)np->nvenv==pp->bltin_lib) - dtdelete(sh_bltin_tree(),np); + nv_delete(np,sh_bltin_tree(),NV_NOFREE); } /* @@ -294,8 +315,10 @@ void path_delete(Pathcomp_t *first) if(pp->bltin_lib || (pp->flags&PATH_STD_DIR)) { nv_scan(sh_bltin_tree(),free_bltin,pp,0,0); +#if SHOPT_DYNAMIC if(pp->bltin_lib) dlclose(pp->bltin_lib); +#endif /* SHOPT_DYNAMIC */ } free((void*)pp); if(old) @@ -329,7 +352,7 @@ static char *path_lib(Pathcomp_t *pp, char *path) char save[8]; for( ;pp; pp=pp->next) { - if(pp->ino==statb.st_ino && pp->dev==statb.st_dev) + if(pp->ino==statb.st_ino && pp->dev==statb.st_dev && pp->mtime==statb.st_mtime) return(pp->lib); } pcomp.len = 0; @@ -414,7 +437,7 @@ static void path_init(Shell_t *shp) Pathcomp_t *pp; if(!std_path && !(std_path=astconf("PATH",NIL(char*),NIL(char*)))) std_path = e_defpath; - if(val=nv_scoped((PATHNOD))->nvalue.cp) + if(val=sh_scoped(shp,(PATHNOD))->nvalue.cp) { pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_PATH); if(shp->pathlist = (void*)pp) @@ -426,7 +449,7 @@ static void path_init(Shell_t *shp) pp = defpath_init(shp); shp->pathlist = (void*)path_dup(pp); } - if(val=nv_scoped((FPATHNOD))->nvalue.cp) + if(val=sh_scoped(shp,(FPATHNOD))->nvalue.cp) { pp = (void*)path_addpath((Pathcomp_t*)shp->pathlist,val,PATH_FPATH); if(shp->pathlist = (void*)pp) @@ -551,16 +574,37 @@ char *path_fullname(const char *name) */ static void funload(Shell_t *shp,int fno, const char *name) { - char *oldname=shp->st.filename, buff[IOBSIZE+1]; - int savestates = sh_getstate(); + char *pname,*oldname=shp->st.filename, buff[IOBSIZE+1]; + Namval_t *np; + struct Ufunction *rp; + int savestates = sh_getstate(), oldload=shp->funload; + pname = path_fullname(stakptr(PATH_OFFSET)); + if(shp->fpathdict && (rp = dtmatch(shp->fpathdict,(void*)pname))) + { + do + { + if((np = dtsearch(shp->fun_tree,rp->np)) && is_afunction(np)) + { + if(np->nvalue.rp) + np->nvalue.rp->fdict = 0; + nv_delete(np,shp->fun_tree,NV_NOFREE); + } + dtinsert(shp->fun_tree,rp->np); + rp->fdict = shp->fun_tree; + } + while((rp=dtnext(shp->fpathdict,rp)) && strcmp(pname,rp->fname)==0); + return; + } sh_onstate(SH_NOLOG); sh_onstate(SH_NOALIAS); shp->readscript = (char*)name; - shp->st.filename = path_fullname(stakptr(PATH_OFFSET)); + shp->st.filename = pname; + shp->funload = 1; error_info.line = 0; sh_eval(sfnew(NIL(Sfio_t*),buff,IOBSIZE,fno,SF_READ),0); shp->readscript = 0; free((void*)shp->st.filename); + shp->funload = oldload; shp->st.filename = oldname; sh_setstate(savestates); } @@ -568,12 +612,14 @@ static void funload(Shell_t *shp,int fno, const char *name) /* * do a path search and track alias if requested * if flag is 0, or if name not found, then try autoloading function - * if flag==2, returns 1 if name found on FPATH + * if flag==2 or 3, returns 1 if name found on FPATH + * if flag==3 no tracked alias will be set * returns 1, if function was autoloaded. - * If endpath!=NULL, Path search ends when path matches endpath. + * If oldpp is not NULL, it will contain a pointer to the path component + * where it was found. */ -int path_search(register const char *name,Pathcomp_t *endpath, int flag) +int path_search(register const char *name,Pathcomp_t **oldpp, int flag) { register Namval_t *np; register int fno; @@ -606,8 +652,9 @@ int path_search(register const char *name,Pathcomp_t *endpath, int flag) path_init(shp); if(flag) { - if(!(pp=path_absolute(name,endpath)) && endpath) - pp = path_absolute(name,NIL(Pathcomp_t*)); + pp = path_absolute(name,oldpp?*oldpp:NIL(Pathcomp_t*)); + if(oldpp) + *oldpp = pp; if(!pp && (np=nv_search(name,sh.fun_tree,HASH_NOSCOPE))&&np->nvalue.ip) return(1); if(!pp) @@ -630,7 +677,7 @@ int path_search(register const char *name,Pathcomp_t *endpath, int flag) *stakptr(PATH_OFFSET) = 0; return(0); } - else if(pp && !sh_isstate(SH_DEFPATH) && *name!='/') + else if(pp && !sh_isstate(SH_DEFPATH) && *name!='/' && flag<3) { if(np=nv_search(name,shp->track_tree,NV_ADD)) path_alias(np,pp); @@ -641,18 +688,17 @@ int path_search(register const char *name,Pathcomp_t *endpath, int flag) /* * do a path search and find the full pathname of file name - * end search of path matches endpath without checking execute permission */ -Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *endpath) +Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *pp) { register int f,isfun; int noexec=0; - Pathcomp_t *pp,*oldpp; + Pathcomp_t *oldpp; Shell_t *shp = &sh; Namval_t *np; shp->path_err = ENOENT; - if(!(pp=path_get(""))) + if(!pp && !(pp=path_get(""))) return(0); shp->path_err = 0; while(1) @@ -661,12 +707,11 @@ Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *endpath) isfun = (pp->flags&PATH_FPATH); if(oldpp=pp) pp = path_nextcomp(pp,name,0); - if(endpath) - return(endpath); if(!isfun && !sh_isoption(SH_RESTRICTED)) { - if(nv_search(stakptr(PATH_OFFSET),sh.bltin_tree,0)) + if(*stakptr(PATH_OFFSET)=='/' && nv_search(stakptr(PATH_OFFSET),sh.bltin_tree,0)) return(oldpp); +#if SHOPT_DYNAMIC if(oldpp->blib) { typedef int (*Fptr_t)(int, char*[], void*); @@ -684,9 +729,8 @@ Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *endpath) cp = oldpp->blib; if(strcmp(cp,LIBCMD)==0 && (addr=(Fptr_t)dlllook((void*)0,stakptr(n)))) { - np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL); - np->nvfun = (Namfun_t*)np->nvname; - return(oldpp); + if((np = sh_addbuiltin(stakptr(PATH_OFFSET),addr,NiL)) && nv_isattr(np,NV_BLTINOPT)) + return(oldpp); } #if (_AST_VERSION>=20040404) if (oldpp->bltin_lib = dllplug(SH_ID, oldpp->blib, NiL, RTLD_LAZY, NiL, 0)) @@ -703,7 +747,9 @@ Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *endpath) return(oldpp); } } +#endif /* SHOPT_DYNAMIC */ } + sh_stats(STAT_PATHS); f = canexecute(stakptr(PATH_OFFSET),isfun); if(isfun && f>=0) { @@ -714,16 +760,17 @@ Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *endpath) } else if(f>=0 && (oldpp->flags & PATH_STD_DIR)) { - int offset = staktell(); + int n = staktell(); stakputs("/bin/"); stakputs(name); stakputc(0); - np = nv_search(stakptr(offset),sh.bltin_tree,0); - stakseek(offset); + np = nv_search(stakptr(n),sh.bltin_tree,0); + stakseek(n); if(np) { - np = sh_addbuiltin(stakptr(PATH_OFFSET),np->nvalue.bfp,NiL); - np->nvfun = (Namfun_t*)np->nvname; + n = np->nvflag; + np = sh_addbuiltin(stakptr(PATH_OFFSET),np->nvalue.bfp,nv_context(np)); + np->nvflag = n; } } if(!pp || f>=0) @@ -733,8 +780,7 @@ Pathcomp_t *path_absolute(register const char *name, Pathcomp_t *endpath) } if(f<0) { - if(!endpath) - shp->path_err = (noexec?noexec:ENOENT); + shp->path_err = (noexec?noexec:ENOENT); return(0); } stakputc(0); @@ -947,7 +993,7 @@ pid_t path_spawn(const char *opath,register char **argv, char **envp, Pathcomp_t } v = stakfreeze(1); r = 1; - xp = envp + 2; + xp = envp + 1; while (s = *xp++) { if (strneq(s, v, n) && s[n] == '=') @@ -986,28 +1032,10 @@ pid_t path_spawn(const char *opath,register char **argv, char **envp, Pathcomp_t path = sp; } #endif /* SHELLMAGIC */ - if(sh_isoption(SH_RESTRICTED)) - { - int fd; - if((fd = sh_open(opath,O_RDONLY,0)) >= 0) - { - char buff[PATH_MAX]; - n = read(fd,buff,sizeof(buff)); - close(fd); - if(n>2 && buff[0]=='#' && buff[1]=='!') - { - for(s=buff; n>0 && *s!='\n'; n--,s++) - { - if(*s=='/') - errormsg(SH_DICT,ERROR_exit(1),e_restricted,opath); - } - } - } - } if(spawn && !sh_isoption(SH_PFSH)) pid = _spawnveg(opath, &argv[0],envp, spawn>>1); else - path_pfexecve(opath, &argv[0] ,envp); + pid = path_pfexecve(opath, &argv[0] ,envp,spawn); if(xp) *xp = xval; #ifdef SHELLMAGIC @@ -1048,6 +1076,7 @@ retry: return(pid); } while(_sh_fork(pid,0,(int*)0) < 0); + ((struct checkpt*)shp->jmplist)->mode = SH_JMPEXIT; #else return(-1); #endif @@ -1159,20 +1188,21 @@ static void exscript(Shell_t *shp,register char *path,register char *argv[],char } savet = *--argv; *argv = path; - path_pfexecve(e_suidexec,argv,envp); + path_pfexecve(e_suidexec,argv,envp,0); fail: /* * The following code is just for compatibility */ if((n=open(path,O_RDONLY,0)) < 0) - errormsg(SH_DICT,ERROR_system(1),e_open,path); + errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path); if(savet) *argv++ = savet; openok: shp->infd = n; } #else - shp->infd = sh_chkopen(path); + if((shp->infd = sh_open(path,O_RDONLY,0)) < 0) + errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_exec,path); #endif shp->infd = sh_iomovefd(shp->infd); #if SHOPT_ACCT @@ -1210,7 +1240,7 @@ static void exscript(Shell_t *shp,register char *path,register char *argv[],char SHACCT = getenv("SHACCT"); } /* - * suspend accounting unitl turned on by sh_accbegin() + * suspend accounting until turned on by sh_accbegin() */ void sh_accsusp(void) { @@ -1318,6 +1348,7 @@ static Pathcomp_t *path_addcomp(Pathcomp_t *first, Pathcomp_t *old,const char *n { statb.st_ino = old->ino; statb.st_dev = old->dev; + statb.st_mtime = old->mtime; if(old->ino==0 && old->dev==0) flag |= PATH_SKIP; } @@ -1335,12 +1366,13 @@ static Pathcomp_t *path_addcomp(Pathcomp_t *first, Pathcomp_t *old,const char *n statb.st_dev = 0; } statb.st_ino = 0; + statb.st_mtime = 0; } if(*name=='/' && onstdpath(name)) flag |= PATH_STD_DIR; for(pp=first, oldpp=0; pp; oldpp=pp, pp=pp->next) { - if(pp->ino==statb.st_ino && pp->dev==statb.st_dev) + if(pp->ino==statb.st_ino && pp->dev==statb.st_dev && pp->mtime==statb.st_mtime) { /* if both absolute paths, eliminate second */ pp->flags |= flag; @@ -1357,6 +1389,7 @@ static Pathcomp_t *path_addcomp(Pathcomp_t *first, Pathcomp_t *old,const char *n pp->len = len; pp->dev = statb.st_dev; pp->ino = statb.st_ino; + pp->mtime = statb.st_mtime; if(oldpp) oldpp->next = pp; else @@ -1568,6 +1601,7 @@ void path_newdir(Pathcomp_t *first) } pp->dev = statb.st_dev; pp->ino = statb.st_ino; + pp->mtime = statb.st_mtime; for(pq=first;pq!=pp;pq=pq->next) { if(pp->ino==pq->ino && pp->dev==pq->dev) @@ -1600,6 +1634,18 @@ void path_newdir(Pathcomp_t *first) Pathcomp_t *path_unsetfpath(Pathcomp_t *first) { register Pathcomp_t *pp=first, *old=0; + Shell_t *shp = &sh; + if(shp->fpathdict) + { + struct Ufunction *rp, *rpnext; + for(rp=(struct Ufunction*)dtfirst(shp->fpathdict);rp;rp=rpnext) + { + rpnext = (struct Ufunction*)dtnext(shp->fpathdict,rp); + if(rp->fdict) + nv_delete(rp->np,rp->fdict,NV_NOFREE); + rp->fdict = 0; + } + } while(pp) { if((pp->flags&PATH_FPATH) && !(pp->flags&PATH_BFPATH)) diff --git a/usr/src/lib/libshell/common/sh/pmain.c b/usr/src/lib/libshell/common/sh/pmain.c index 9cf8a886f9..5c61ce4107 100644 --- a/usr/src/lib/libshell/common/sh/pmain.c +++ b/usr/src/lib/libshell/common/sh/pmain.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -21,10 +21,27 @@ #include <shell.h> +#include "FEATURE/externs" + +#if defined(__sun) && _sys_mman && _lib_memcntl && defined(MHA_MAPSIZE_STACK) && defined(MC_HAT_ADVISE) +# undef VM_FLAGS /* solaris vs vmalloc.h symbol clash */ +# include <sys/mman.h> +#else +# undef _lib_memcntl +#endif + typedef int (*Shnote_f)(int, long, int); int main(int argc, char *argv[]) { +#if _lib_memcntl + /* advise larger stack size */ + struct memcntl_mha mha; + mha.mha_cmd = MHA_MAPSIZE_STACK; + mha.mha_flags = 0; + mha.mha_pagesize = 64 * 1024; + (void)memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mha, 0, 0); +#endif sh_waitnotify((Shnote_f)0); - return(sh_main(argc, argv, 0)); + return(sh_main(argc, argv, (Shinit_f)0)); } diff --git a/usr/src/lib/libshell/common/sh/shcomp.c b/usr/src/lib/libshell/common/sh/shcomp.c index 454ac3e5be..a1b717f3e7 100644 --- a/usr/src/lib/libshell/common/sh/shcomp.c +++ b/usr/src/lib/libshell/common/sh/shcomp.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -59,6 +59,7 @@ USAGE_LICENSE ; #include <shell.h> +#include "defs.h" #include "shnodes.h" #include "sys/stat.h" @@ -94,6 +95,7 @@ int main(int argc, char *argv[]) break; } shp = sh_init(argc,argv,(Shinit_f)0); + shp->shcomp = 1; argv += opt_info.index; argc -= opt_info.index; if(error_info.errors || argc>2) @@ -127,12 +129,15 @@ int main(int argc, char *argv[]) if(!dflag) sfwrite(out,header,sizeof(header)); shp->inlineno = 1; +#if SHOPT_BRACEPAT + sh_onoption(SH_BRACEEXPAND); +#endif while(1) { stakset((char*)0,0); if(t = (Shnode_t*)sh_parse(shp,in,0)) { - if(t->tre.tretyp==0 && t->com.comnamp && strcmp(nv_name((Namval_t*)t->com.comnamp),"alias")==0) + if((t->tre.tretyp&(COMMSK|COMSCAN))==0 && t->com.comnamp && strcmp(nv_name((Namval_t*)t->com.comnamp),"alias")==0) sh_exec(t,0); if(!dflag && sh_tdump(out,t) < 0) errormsg(SH_DICT,ERROR_exit(1),"dump failed"); diff --git a/usr/src/lib/libshell/common/sh/streval.c b/usr/src/lib/libshell/common/sh/streval.c index fd74d47cd6..f39cc24e9b 100644 --- a/usr/src/lib/libshell/common/sh/streval.c +++ b/usr/src/lib/libshell/common/sh/streval.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -486,7 +486,7 @@ again: case A_PLUS: goto again; case A_EOF: - if(precedence>5) + if(precedence>2) ERROR(vp,e_moretokens); return(1); case A_MINUS: diff --git a/usr/src/lib/libshell/common/sh/string.c b/usr/src/lib/libshell/common/sh/string.c index 2d5fcaaac3..b7c9c7d63c 100644 --- a/usr/src/lib/libshell/common/sh/string.c +++ b/usr/src/lib/libshell/common/sh/string.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -256,6 +256,7 @@ void sh_trim(register char *sp) int len; if(mbwide() && (len=mbsize(sp))>1) { + memmove(dp, sp, len); dp += len; sp += len; continue; @@ -300,7 +301,7 @@ void sh_utol(register char const *str1,register char *str2) */ char *sh_fmtq(const char *string) { - register const char *cp = string; + register const char *cp = string, *op; register int c, state; int offset; if(!cp) @@ -343,14 +344,12 @@ char *sh_fmtq(const char *string) #endif { #if SHOPT_MULTIBYTE - if(c>=0x200) - continue; if(c=='\'' || !iswprint(c)) #else if(c=='\'' || !isprint(c)) #endif /* SHOPT_MULTIBYTE */ state = 2; - else if(c==']' || (c!=':' && (c=sh_lexstates[ST_NORM][c]) && c!=S_EPAT)) + else if(c==']' || (c!=':' && c<=0xff && (c=sh_lexstates[ST_NORM][c]) && c!=S_EPAT)) state |=1; } if(state<2) @@ -367,9 +366,9 @@ char *sh_fmtq(const char *string) stakwrite("$'",2); cp = string; #if SHOPT_MULTIBYTE - while(c= mbchar(cp)) + while(op = cp, c= mbchar(cp)) #else - while(c= *(unsigned char*)cp++) + while(op = cp, c= *(unsigned char*)cp++) #endif { state=1; @@ -401,19 +400,28 @@ char *sh_fmtq(const char *string) default: #if SHOPT_MULTIBYTE if(!iswprint(c)) + { + while(op<cp) + sfprintf(staksp,"\\%.3o",*(unsigned char*)op++); + continue; + } #else if(!isprint(c)) -#endif { sfprintf(staksp,"\\%.3o",c); continue; } +#endif state=0; break; } if(state) + { stakputc('\\'); - stakputc(c); + stakputc(c); + } + else + stakwrite(op, cp-op); } stakputc('\''); } diff --git a/usr/src/lib/libshell/common/sh/subshell.c b/usr/src/lib/libshell/common/sh/subshell.c index 550e299d42..75dfc1f97d 100644 --- a/usr/src/lib/libshell/common/sh/subshell.c +++ b/usr/src/lib/libshell/common/sh/subshell.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -47,6 +47,8 @@ struct Link { struct Link *next; + Namval_t *child; + Dt_t *dict; Namval_t *node; }; @@ -55,15 +57,14 @@ struct Link */ static struct subshell { + Shell_t *shp; /* shell interpreter */ struct subshell *prev; /* previous subshell data */ struct subshell *pipe; /* subshell where output goes to pipe on fork */ Dt_t *var; /* variable table at time of subshell */ struct Link *svar; /* save shell variable table */ Dt_t *sfun; /* function scope for subshell */ Dt_t *salias;/* alias scope for subshell */ -#ifdef PATH_BFPATH Pathcomp_t *pathlist; /* for PATH variable */ -#endif #if (ERROR_VERSION >= 20030214L) struct Error_context_s *errcontext; #else @@ -82,65 +83,73 @@ static struct subshell char monitor; unsigned char fdstatus; int fdsaved; /* bit make for saved files */ - int bckpid; + int sig; /* signal for $$ */ + pid_t bckpid; + pid_t cpid; + int coutpipe; + int cpipe; + int nofork; } *subshell_data; static int subenv; /* * This routine will turn the sftmp() file into a real /tmp file or pipe - * if the /tmp file create fails */ -void sh_subtmpfile(void) +void sh_subtmpfile(int pflag) { + Shell_t *shp = &sh; + int fds[2]; + Sfoff_t off; if(sfset(sfstdout,0,0)&SF_STRING) { register int fd; - register struct checkpt *pp = (struct checkpt*)sh.jmplist; + register struct checkpt *pp = (struct checkpt*)shp->jmplist; register struct subshell *sp = subshell_data->pipe; /* save file descriptor 1 if open */ if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0) { fcntl(fd,F_SETFD,FD_CLOEXEC); - sh.fdstatus[fd] = sh.fdstatus[1]|IOCLEX; + shp->fdstatus[fd] = shp->fdstatus[1]|IOCLEX; close(1); } else if(errno!=EBADF) errormsg(SH_DICT,ERROR_system(1),e_toomany); - /* popping a discipline forces a /tmp file create */ - sfdisc(sfstdout,SF_POPDISC); - if((fd=sffileno(sfstdout))<0) - { - /* unable to create the /tmp file so use a pipe */ - int fds[2]; - Sfoff_t off; - sh_pipe(fds); - sp->pipefd = fds[0]; - sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC); - /* write the data to the pipe */ - if(off = sftell(sfstdout)) - write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off); - sfclose(sfstdout); - if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1) - errormsg(SH_DICT,ERROR_system(1),e_file+4); - sh_close(fds[1]); - } - else + if(!pflag) { - sh.fdstatus[fd] = IOREAD|IOWRITE; - sfsync(sfstdout); - if(fd==1) - fcntl(1,F_SETFD,0); - else + sfdisc(sfstdout,SF_POPDISC); + if((fd=sffileno(sfstdout))>=0) { - sfsetfd(sfstdout,1); - sh.fdstatus[1] = sh.fdstatus[fd]; - sh.fdstatus[fd] = IOCLOSE; + sh.fdstatus[fd] = IOREAD|IOWRITE; + sfsync(sfstdout); + if(fd==1) + fcntl(1,F_SETFD,0); + else + { + sfsetfd(sfstdout,1); + sh.fdstatus[1] = sh.fdstatus[fd]; + sh.fdstatus[fd] = IOCLOSE; + } + goto skip; } } - sh_iostream(1); + sh_pipe(fds); + sp->pipefd = fds[0]; + sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC); + /* write the data to the pipe */ + if(off = sftell(sfstdout)) + { + write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off); + sfpurge(sfstdout); + } + sfclose(sfstdout); + if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1) + errormsg(SH_DICT,ERROR_system(1),e_file+4); + sh_close(fds[1]); + skip: + sh_iostream(shp,1); sfset(sfstdout,SF_SHARE|SF_PUBLIC,1); - sfpool(sfstdout,sh.outpool,SF_WRITE); + sfpool(sfstdout,shp->outpool,SF_WRITE); if(pp && pp->olist && pp->olist->strm == sfstdout) pp->olist->strm = 0; } @@ -154,32 +163,50 @@ void sh_subtmpfile(void) void sh_subfork(void) { register struct subshell *sp = subshell_data; + Shell_t *shp = sp->shp; + int curenv = shp->curenv; pid_t pid; /* see whether inside $(...) */ if(sp->pipe) - sh_subtmpfile(); + sh_subtmpfile(1); + shp->curenv = 0; if(pid = sh_fork(0,NIL(int*))) { + shp->curenv = curenv; /* this is the parent part of the fork */ if(sp->subpid==0) sp->subpid = pid; - siglongjmp(*sh.jmplist,SH_JMPSUB); + siglongjmp(*shp->jmplist,SH_JMPSUB); } else { - int16_t subshell; /* this is the child part of the fork */ /* setting subpid to 1 causes subshell to exit when reached */ sh_onstate(SH_FORKED); sh_onstate(SH_NOLOG); sh_offstate(SH_MONITOR); subshell_data = 0; - subshell = sh.subshell = 0; - nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16); + shp->subshell = 0; + SH_SUBSHELLNOD->nvalue.s = 0; sp->subpid=0; } } +int nv_subsaved(register Namval_t *np) +{ + register struct subshell *sp; + register struct Link *lp; + for(sp = (struct subshell*)subshell_data; sp; sp=sp->prev) + { + for(lp=sp->svar; lp; lp = lp->next) + { + if(lp->node==np) + return(1); + } + } + return(0); +} + /* * This routine will make a copy of the given node in the * layer created by the most recent subshell_fork if the @@ -187,30 +214,74 @@ void sh_subfork(void) */ Namval_t *sh_assignok(register Namval_t *np,int add) { - register Namval_t *mp; - register struct Link *lp; + register Namval_t *mp; + register struct Link *lp; register struct subshell *sp = (struct subshell*)subshell_data; - int save; + struct Ufunction *rp; + Shell_t *shp = sp->shp; + Dt_t *dp; + Namval_t *mpnext; + Namarr_t *ap; + int save; /* don't bother with this */ if(!sp->shpwd || (nv_isnull(np) && !add)) return(np); /* don't bother to save if in newer scope */ - if(nv_search((char*)np,sp->var,HASH_BUCKET)!=np) - return(np); + if(!(rp=shp->st.real_fun) || !(dp=rp->sdict)) + dp = sp->var; + if(np->nvenv && !nv_isattr(np,NV_MINIMAL|NV_EXPORT) && shp->last_root) + dp = shp->last_root; + if((mp=nv_search((char*)np,dp,HASH_BUCKET))!=np) + { + if(mp || !np->nvfun || np->nvfun->subshell>=sh.subshell) + return(np); + } + if((ap=nv_arrayptr(np)) && (mp=nv_opensub(np))) + { + shp->last_root = ap->table; + sh_assignok(mp,add); + if(!add || array_assoc(ap)) + return(np); + } for(lp=subshell_data->svar; lp; lp = lp->next) { if(lp->node==np) return(np); } - mp = newof(0,Namval_t,1,0); - lp = (struct Link*)mp; + /* first two pointers use linkage from np */ + lp = (struct Link*)malloc(sizeof(*np)+2*sizeof(void*)); + memset(lp,0, sizeof(*mp)+2*sizeof(void*)); lp->node = np; + if(!add && nv_isvtree(np)) + { + Namval_t fake; + Dt_t *walk, *root=shp->var_tree; + char *name = nv_name(np); + int len = strlen(name); + fake.nvname = name; + mpnext = dtnext(root,&fake); + dp = root->walk?root->walk:root; + while(mp=mpnext) + { + walk = root->walk?root->walk:root; + mpnext = dtnext(root,mp); + if(memcmp(name,mp->nvname,len) || mp->nvname[len]!='.') + break; + nv_delete(mp,walk,NV_NOFREE); + *((Namval_t**)mp) = lp->child; + lp->child = mp; + + } + } + lp->dict = dp; + mp = (Namval_t*)&lp->dict; lp->next = subshell_data->svar; subshell_data->svar = lp; - save = sh.subshell; - sh.subshell = 0;; - nv_clone(np,mp,NV_NOFREE); - sh.subshell = save; + save = shp->subshell; + shp->subshell = 0; + mp->nvname = np->nvname; + nv_clone(np,mp,(add?(nv_isnull(np)?0:NV_NOFREE)|NV_ARRAY:NV_MOVE)); + shp->subshell = save; return(np); } @@ -222,21 +293,29 @@ static void nv_restore(struct subshell *sp) register struct Link *lp, *lq; register Namval_t *mp, *np; const char *save = sp->shpwd; + Namval_t *mpnext; sp->shpwd = 0; /* make sure sh_assignok doesn't save with nv_unset() */ for(lp=sp->svar; lp; lp=lq) { - np = (Namval_t*)lp; - mp = lp->node; + np = (Namval_t*)&lp->dict; lq = lp->next; + mp = lp->node; + if(!mp->nvname) + continue; if(nv_isarray(mp)) nv_putsub(mp,NIL(char*),ARRAY_SCAN); _nv_unset(mp,NV_RDONLY); + if(nv_isarray(np)) + { + nv_clone(np,mp,NV_MOVE); + goto skip; + } nv_setsize(mp,nv_size(np)); if(!nv_isattr(np,NV_MINIMAL) || nv_isattr(np,NV_EXPORT)) mp->nvenv = np->nvenv; mp->nvfun = np->nvfun; mp->nvflag = np->nvflag; - if((mp==nv_scoped(PATHNOD)) || (mp==nv_scoped(IFSNOD))) + if(nv_cover(mp)) nv_putval(mp, np->nvalue.cp,0); else mp->nvalue.cp = np->nvalue.cp; @@ -250,7 +329,14 @@ static void nv_restore(struct subshell *sp) } else if(nv_isattr(np,NV_EXPORT)) env_delete(sh.env,nv_name(mp)); - free((void*)np); + skip: + for(mp=lp->child; mp; mp=mpnext) + { + mpnext = *((Namval_t**)mp); + dtinsert(lp->dict,mp); + } + free((void*)lp); + sp->svar = lq; } sp->shpwd=save; } @@ -288,18 +374,25 @@ Dt_t *sh_subfuntree(int create) dtview(sp->sfun,sh.fun_tree); sh.fun_tree = sp->sfun; } - return(sp->sfun); + return(sh.fun_tree); } -static void table_unset(register Dt_t *root) +static void table_unset(register Dt_t *root,int fun) { register Namval_t *np,*nq; + int flag; for(np=(Namval_t*)dtfirst(root);np;np=nq) { - _nv_unset(np,NV_RDONLY); nq = (Namval_t*)dtnext(root,np); - dtdelete(root,np); - free((void*)np); + flag=0; + if(fun && np->nvalue.rp->fname && *np->nvalue.rp->fname=='/') + { + np->nvalue.rp->fdict = 0; + flag = NV_NOFREE; + } + else + _nv_unset(np,NV_RDONLY); + nv_delete(np,root,flag|NV_FUNCTION); } } @@ -315,6 +408,22 @@ int sh_subsavefd(register int fd) return(old); } +void sh_subjobcheck(pid_t pid) +{ + register struct subshell *sp = subshell_data; + while(sp) + { + if(sp->cpid==pid) + { + sh_close(sp->coutpipe); + sh_close(sp->cpipe); + sp->coutpipe = sp->cpipe = -1; + return; + } + sp = sp->prev; + } +} + /* * Run command tree <t> in a virtual sub-shell * If comsub is not null, then output will be placed in temp file (or buffer) @@ -327,8 +436,9 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) Shell_t *shp = &sh; struct subshell sub_data; register struct subshell *sp = &sub_data; - int jmpval,nsig; + int jmpval,nsig=0; int savecurenv = shp->curenv; + int savejobpgid = job.curpgid; int16_t subshell; char *savsig; Sfio_t *iop=0; @@ -337,38 +447,42 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) struct dolnod *argsav=0; memset((char*)sp, 0, sizeof(*sp)); sfsync(shp->outpool); - argsav = sh_arguse(); + argsav = sh_arguse(shp); if(shp->curenv==0) { subshell_data=0; subenv = 0; } shp->curenv = ++subenv; + job.curpgid = 0; savst = shp->st; sh_pushcontext(&buff,SH_JMPSUB); subshell = shp->subshell+1; - nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16); + SH_SUBSHELLNOD->nvalue.s = subshell; shp->subshell = subshell; sp->prev = subshell_data; + sp->shp = shp; + sp->sig = 0; subshell_data = sp; sp->errcontext = &buff.err; sp->var = shp->var_tree; sp->options = shp->options; sp->jobs = job_subsave(); -#ifdef PATH_BFPATH /* make sure initialization has occurred */ if(!shp->pathlist) path_get("."); sp->pathlist = path_dup((Pathcomp_t*)shp->pathlist); -#endif if(!shp->pwd) path_pwd(0); sp->bckpid = shp->bckpid; - if(!comsub || !sh_isoption(SH_SUBSHARE)) + if(comsub) + sh_stats(STAT_COMSUB); + if(!comsub || (comsub==1 && !sh_isoption(SH_SUBSHARE))) { sp->shpwd = shp->pwd; sp->pwd = (shp->pwd?strdup(shp->pwd):0); sp->mask = shp->mask; + sh_stats(STAT_SUBSHELL); /* save trap table */ shp->st.otrapcom = 0; if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0]) @@ -378,6 +492,11 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) /* this nonsense needed for $(trap) */ shp->st.otrapcom = (char**)savsig; } + sp->cpid = shp->cpid; + sp->coutpipe = shp->coutpipe; + sp->cpipe = shp->cpipe[1]; + shp->coutpipe = shp->cpipe[1] = -1; + shp->cpid = 0; sh_sigreset(0); } jmpval = sigsetjmp(buff.buff,0); @@ -386,6 +505,7 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) if(comsub) { /* disable job control */ + shp->spid = 0; sp->jobcontrol = job.jobcontrol; sp->monitor = (sh_isstate(SH_MONITOR)!=0); job.jobcontrol=0; @@ -405,6 +525,9 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) sfswap(iop,sfstdout); sfset(sfstdout,SF_READ,0); shp->fdstatus[1] = IOWRITE; + if(!(sp->nofork = sh_state(SH_NOFORK))) + sh_onstate(SH_NOFORK); + flags |= sh_state(SH_NOFORK); } else if(sp->prev) { @@ -413,7 +536,7 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) } sh_exec(t,flags); } - if(jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell) + if(comsub!=2 && jmpval!=SH_JMPSUB && shp->st.trapcom[0] && shp->subshell) { /* trap on EXIT not handled by child */ char *trap=shp->st.trapcom[0]; @@ -428,19 +551,20 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) subshell_data = sp->prev; if(jmpval==SH_JMPSCRIPT) siglongjmp(*shp->jmplist,jmpval); - sh_done(0); + sh_done(shp,0); } if(comsub) { /* re-enable job control */ + if(!sp->nofork) + sh_offstate(SH_NOFORK); job.jobcontrol = sp->jobcontrol; if(sp->monitor) sh_onstate(SH_MONITOR); if(sp->pipefd>=0) { /* sftmp() file has been returned into pipe */ - iop = sh_iostream(sp->pipefd); - sfdisc(iop,SF_POPDISC); + iop = sh_iostream(shp,sp->pipefd); sfclose(sfstdout); } else @@ -476,37 +600,44 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) shp->fdstatus[1] = sp->fdstatus; } if(sp->subpid) - job_wait(sp->subpid); - if(comsub && iop) + { + if(shp->exitval > SH_EXITSIG) + sp->sig = (shp->exitval&SH_EXITMASK); + shp->exitval = 0; + if(comsub) + shp->spid = sp->subpid; + else + job_wait(sp->subpid); + } + if(comsub && iop && sp->pipefd<0) sfseek(iop,(off_t)0,SEEK_SET); - if(shp->subshell) - shp->subshell--; - subshell = shp->subshell; - nv_putval(SH_SUBSHELLNOD, (char*)&subshell, NV_INT16); -#ifdef PATH_BFPATH path_delete((Pathcomp_t*)shp->pathlist); shp->pathlist = (void*)sp->pathlist; -#endif job_subrestore(sp->jobs); shp->jobenv = savecurenv; + job.curpgid = savejobpgid; shp->bckpid = sp->bckpid; if(sp->shpwd) /* restore environment if saved */ { + int n; shp->options = sp->options; nv_restore(sp); if(sp->salias) { shp->alias_tree = dtview(sp->salias,0); - table_unset(sp->salias); + table_unset(sp->salias,0); dtclose(sp->salias); } if(sp->sfun) { shp->fun_tree = dtview(sp->sfun,0); - table_unset(sp->sfun); + table_unset(sp->sfun,1); dtclose(sp->sfun); } + n = shp->st.trapmax-savst.trapmax; sh_sigreset(1); + if(n>0) + memset(&shp->st.trapcom[savst.trapmax],0,n*sizeof(char*)); shp->st = savst; shp->curenv = savecurenv; if(nsig) @@ -518,13 +649,11 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) if(!shp->pwd || strcmp(sp->pwd,shp->pwd)) { /* restore PWDNOD */ - Namval_t *pwdnod = nv_scoped(PWDNOD); + Namval_t *pwdnod = sh_scoped(shp,PWDNOD); if(shp->pwd) { chdir(shp->pwd=sp->pwd); -#ifdef PATH_BFPATH path_newdir(shp->pathlist); -#endif } if(nv_isattr(pwdnod,NV_NOFREE)) pwdnod->nvalue.cp = (const char*)sp->pwd; @@ -538,13 +667,34 @@ Sfio_t *sh_subshell(Shnode_t *t, int flags, int comsub) else free((void*)sp->pwd); if(sp->mask!=shp->mask) - umask(shp->mask); + umask(shp->mask=sp->mask); + if(shp->coutpipe>=0) + { + sh_close(shp->coutpipe); + sh_close(shp->cpipe[1]); + } + shp->cpid = sp->cpid; + shp->cpipe[1] = sp->cpipe; + shp->coutpipe = sp->coutpipe; } + if(shp->subshell) + SH_SUBSHELLNOD->nvalue.s = --shp->subshell; + if(sp->sig) + { + if(sp->prev) + sp->prev->sig = sp->sig; + else + { + sh_fault(sp->sig); + sh_chktrap(); + } + } + subshell = shp->subshell; subshell_data = sp->prev; - sh_argfree(argsav,0); + sh_argfree(shp,argsav,0); shp->trapnote = 0; if(shp->topfd != buff.topfd) - sh_iorestore(buff.topfd|IOSUBSHELL,jmpval); + sh_iorestore(shp,buff.topfd|IOSUBSHELL,jmpval); if(shp->exitval > SH_EXITSIG) { int sig = shp->exitval&SH_EXITMASK; diff --git a/usr/src/lib/libshell/common/sh/suid_exec.c b/usr/src/lib/libshell/common/sh/suid_exec.c index 0495d29890..461dc7879a 100644 --- a/usr/src/lib/libshell/common/sh/suid_exec.c +++ b/usr/src/lib/libshell/common/sh/suid_exec.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/sh/tdump.c b/usr/src/lib/libshell/common/sh/tdump.c index 80ef55a9aa..c5d6b25c99 100644 --- a/usr/src/lib/libshell/common/sh/tdump.c +++ b/usr/src/lib/libshell/common/sh/tdump.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -160,7 +160,7 @@ static int p_arg(register const struct argnod *arg) struct fornod *fp; while(arg) { - if((n = strlen(arg->argval)) || (arg->argflag&~ARG_APPEND)) + if((n = strlen(arg->argval)) || (arg->argflag&~(ARG_APPEND|ARG_MESSAGE|ARG_QUOTED))) fp=0; else { diff --git a/usr/src/lib/libshell/common/sh/timers.c b/usr/src/lib/libshell/common/sh/timers.c index ae57a3336f..796b43cf5a 100644 --- a/usr/src/lib/libshell/common/sh/timers.c +++ b/usr/src/lib/libshell/common/sh/timers.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/sh/trestore.c b/usr/src/lib/libshell/common/sh/trestore.c index d866ca712f..b9fc63d82f 100644 --- a/usr/src/lib/libshell/common/sh/trestore.c +++ b/usr/src/lib/libshell/common/sh/trestore.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -32,29 +32,29 @@ #include "path.h" #include "io.h" -static struct dolnod *r_comlist(void); -static struct argnod *r_arg(void); -static struct ionod *r_redirect(void); -static struct regnod *r_switch(void); -static Shnode_t *r_tree(void); -static char *r_string(void); -static void r_comarg(struct comnod*); +static struct dolnod *r_comlist(Shell_t*); +static struct argnod *r_arg(Shell_t*); +static struct ionod *r_redirect(Shell_t*); +static struct regnod *r_switch(Shell_t*); +static Shnode_t *r_tree(Shell_t*); +static char *r_string(Stk_t*); +static void r_comarg(Shell_t*,struct comnod*); static Sfio_t *infile; -#define getnode(type) ((Shnode_t*)stakalloc(sizeof(struct type))) +#define getnode(s,type) ((Shnode_t*)stkalloc((s),sizeof(struct type))) -Shnode_t *sh_trestore(Sfio_t *in) +Shnode_t *sh_trestore(Shell_t *shp,Sfio_t *in) { Shnode_t *t; infile = in; - t = r_tree(); + t = r_tree(shp); return(t); } /* * read in a shell tree */ -static Shnode_t *r_tree() +static Shnode_t *r_tree(Shell_t *shp) { long l = sfgetl(infile); register int type; @@ -66,101 +66,101 @@ static Shnode_t *r_tree() { case TTIME: case TPAR: - t = getnode(parnod); - t->par.partre = r_tree(); + t = getnode(shp->stk,parnod); + t->par.partre = r_tree(shp); break; case TCOM: - t = getnode(comnod); + t = getnode(shp->stk,comnod); t->tre.tretyp = type; - r_comarg((struct comnod*)t); + r_comarg(shp,(struct comnod*)t); break; case TSETIO: case TFORK: - t = getnode(forknod); + t = getnode(shp->stk,forknod); t->fork.forkline = sfgetu(infile); - t->fork.forktre = r_tree(); - t->fork.forkio = r_redirect(); + t->fork.forktre = r_tree(shp); + t->fork.forkio = r_redirect(shp); break; case TIF: - t = getnode(ifnod); - t->if_.iftre = r_tree(); - t->if_.thtre = r_tree(); - t->if_.eltre = r_tree(); + t = getnode(shp->stk,ifnod); + t->if_.iftre = r_tree(shp); + t->if_.thtre = r_tree(shp); + t->if_.eltre = r_tree(shp); break; case TWH: - t = getnode(whnod); - t->wh.whinc = (struct arithnod*)r_tree(); - t->wh.whtre = r_tree(); - t->wh.dotre = r_tree(); + t = getnode(shp->stk,whnod); + t->wh.whinc = (struct arithnod*)r_tree(shp); + t->wh.whtre = r_tree(shp); + t->wh.dotre = r_tree(shp); break; case TLST: case TAND: case TORF: case TFIL: - t = getnode(lstnod); - t->lst.lstlef = r_tree(); - t->lst.lstrit = r_tree(); + t = getnode(shp->stk,lstnod); + t->lst.lstlef = r_tree(shp); + t->lst.lstrit = r_tree(shp); break; case TARITH: - t = getnode(arithnod); + t = getnode(shp->stk,arithnod); t->ar.arline = sfgetu(infile); - t->ar.arexpr = r_arg(); + t->ar.arexpr = r_arg(shp); t->ar.arcomp = 0; if((t->ar.arexpr)->argflag&ARG_RAW) t->ar.arcomp = sh_arithcomp((t->ar.arexpr)->argval); break; case TFOR: - t = getnode(fornod); + t = getnode(shp->stk,fornod); t->for_.forline = 0; if(type&FLINENO) t->for_.forline = sfgetu(infile); - t->for_.fortre = r_tree(); - t->for_.fornam = r_string(); - t->for_.forlst = (struct comnod*)r_tree(); + t->for_.fortre = r_tree(shp); + t->for_.fornam = r_string(shp->stk); + t->for_.forlst = (struct comnod*)r_tree(shp); break; case TSW: - t = getnode(swnod); + t = getnode(shp->stk,swnod); t->sw.swline = 0; if(type&FLINENO) t->sw.swline = sfgetu(infile); - t->sw.swarg = r_arg(); + t->sw.swarg = r_arg(shp); if(type&COMSCAN) - t->sw.swio = r_redirect(); + t->sw.swio = r_redirect(shp); else t->sw.swio = 0; - t->sw.swlst = r_switch(); + t->sw.swlst = r_switch(shp); break; case TFUN: { Stak_t *savstak; struct slnod *slp; - t = getnode(functnod); + t = getnode(shp->stk,functnod); t->funct.functloc = -1; t->funct.functline = sfgetu(infile); - t->funct.functnam = r_string(); + t->funct.functnam = r_string(shp->stk); savstak = stakcreate(STAK_SMALL); savstak = stakinstall(savstak, 0); - slp = (struct slnod*)stakalloc(sizeof(struct slnod)); + slp = (struct slnod*)stkalloc(shp->stk,sizeof(struct slnod)); slp->slchild = 0; - slp->slnext = sh.st.staklist; - sh.st.staklist = 0; - t->funct.functtre = r_tree(); + slp->slnext = shp->st.staklist; + shp->st.staklist = 0; + t->funct.functtre = r_tree(shp); t->funct.functstak = slp; slp->slptr = stakinstall(savstak,0); - slp->slchild = sh.st.staklist; - t->funct.functargs = (struct comnod*)r_tree(); + slp->slchild = shp->st.staklist; + t->funct.functargs = (struct comnod*)r_tree(shp); break; } case TTST: - t = getnode(tstnod); + t = getnode(shp->stk,tstnod); t->tst.tstline = sfgetu(infile); if((type&TPAREN)==TPAREN) - t->lst.lstlef = r_tree(); + t->lst.lstlef = r_tree(shp); else { - t->lst.lstlef = (Shnode_t*)r_arg(); + t->lst.lstlef = (Shnode_t*)r_arg(shp); if((type&TBINARY)) - t->lst.lstrit = (Shnode_t*)r_arg(); + t->lst.lstrit = (Shnode_t*)r_arg(shp); } } if(t) @@ -168,13 +168,14 @@ static Shnode_t *r_tree() return(t); } -static struct argnod *r_arg(void) +static struct argnod *r_arg(Shell_t *shp) { register struct argnod *ap=0, *apold, *aptop=0; register long l; + Stk_t *stkp=shp->stk; while((l=sfgetu(infile))>0) { - ap = (struct argnod*)stakseek((unsigned)l+ARGVAL); + ap = (struct argnod*)stkseek(stkp,(unsigned)l+ARGVAL); if(!aptop) aptop = ap; else @@ -187,27 +188,27 @@ static struct argnod *r_arg(void) ap->argval[l] = 0; ap->argchn.cp = 0; ap->argflag = sfgetc(infile); - if(ap->argflag&ARG_MESSAGE) + if((ap->argflag&ARG_MESSAGE) && *ap->argval) { /* replace international messages */ - ap = sh_endword(1); + ap = sh_endword(shp,1); ap->argflag &= ~ARG_MESSAGE; if(!(ap->argflag&(ARG_MAC|ARG_EXP))) - ap = sh_endword(0); + ap = sh_endword(shp,0); else { - ap = (struct argnod*)stakfreeze(0); + ap = (struct argnod*)stkfreeze(stkp,0); if(ap->argflag==0) ap->argflag = ARG_RAW; } } else - ap = (struct argnod*)stakfreeze(0); - if(*ap->argval==0 && (ap->argflag&~ARG_APPEND)==0) + ap = (struct argnod*)stkfreeze(stkp,0); + if(*ap->argval==0 && (ap->argflag&~(ARG_APPEND|ARG_MESSAGE|ARG_QUOTED))==0) { - struct fornod *fp = (struct fornod*)getnode(fornod); + struct fornod *fp = (struct fornod*)getnode(shp->stk,fornod); fp->fortyp = sfgetu(infile); - fp->fortre = r_tree(); + fp->fortre = r_tree(shp); fp->fornam = ap->argval+1; ap->argchn.ap = (struct argnod*)fp; } @@ -218,34 +219,34 @@ static struct argnod *r_arg(void) return(aptop); } -static struct ionod *r_redirect(void) +static struct ionod *r_redirect(Shell_t* shp) { register long l; register struct ionod *iop=0, *iopold, *ioptop=0; while((l=sfgetl(infile))>=0) { - iop = (struct ionod*)getnode(ionod); + iop = (struct ionod*)getnode(shp->stk,ionod); if(!ioptop) ioptop = iop; else iopold->ionxt = iop; iop->iofile = l; - iop->ioname = r_string(); - if(iop->iodelim = r_string()) + iop->ioname = r_string(shp->stk); + if(iop->iodelim = r_string(shp->stk)) { iop->iosize = sfgetl(infile); - if(sh.heredocs) - iop->iooffset = sfseek(sh.heredocs,(off_t)0,SEEK_END); + if(shp->heredocs) + iop->iooffset = sfseek(shp->heredocs,(off_t)0,SEEK_END); else { - sh.heredocs = sftmp(512); + shp->heredocs = sftmp(512); iop->iooffset = 0; } - sfmove(infile,sh.heredocs, iop->iosize, -1); + sfmove(infile,shp->heredocs, iop->iosize, -1); } iopold = iop; if(iop->iofile&IOVNM) - iop->iovname = r_string(); + iop->iovname = r_string(shp->stk); else iop->iovname = 0; iop->iofile &= ~IOVNM; @@ -255,30 +256,30 @@ static struct ionod *r_redirect(void) return(ioptop); } -static void r_comarg(struct comnod *com) +static void r_comarg(Shell_t *shp,struct comnod *com) { char *cmdname=0; - com->comio = r_redirect(); - com->comset = r_arg(); + com->comio = r_redirect(shp); + com->comset = r_arg(shp); com->comstate = 0; if(com->comtyp&COMSCAN) { - com->comarg = r_arg(); + com->comarg = r_arg(shp); if(com->comarg->argflag==ARG_RAW) cmdname = com->comarg->argval; } - else if(com->comarg = (struct argnod*)r_comlist()) + else if(com->comarg = (struct argnod*)r_comlist(shp)) cmdname = ((struct dolnod*)(com->comarg))->dolval[ARG_SPARE]; com->comline = sfgetu(infile); com->comnamq = 0; if(cmdname) { char *cp; - com->comnamp = (void*)nv_search(cmdname,sh.fun_tree,0); + com->comnamp = (void*)nv_search(cmdname,shp->fun_tree,0); if(com->comnamp && (cp =strrchr(cmdname+1,'.'))) { *cp = 0; - com->comnamp = (void*)nv_open(cmdname,sh.var_tree,NV_VARNAME|NV_NOADD|NV_NOARRAY); + com->comnamp = (void*)nv_open(cmdname,shp->var_tree,NV_VARNAME|NV_NOADD|NV_NOARRAY); *cp = '.'; } } @@ -286,36 +287,36 @@ static void r_comarg(struct comnod *com) com->comnamp = 0; } -static struct dolnod *r_comlist(void) +static struct dolnod *r_comlist(Shell_t *shp) { register struct dolnod *dol=0; register long l; register char **argv; if((l=sfgetl(infile))>0) { - dol = (struct dolnod*)stakalloc(sizeof(struct dolnod) + sizeof(char*)*(l+ARG_SPARE)); + dol = (struct dolnod*)stkalloc(shp->stk,sizeof(struct dolnod) + sizeof(char*)*(l+ARG_SPARE)); dol->dolnum = l; dol->dolbot = ARG_SPARE; argv = dol->dolval+ARG_SPARE; - while(*argv++ = r_string()); + while(*argv++ = r_string(shp->stk)); } return(dol); } -static struct regnod *r_switch(void) +static struct regnod *r_switch(Shell_t *shp) { register long l; struct regnod *reg=0,*regold,*regtop=0; while((l=sfgetl(infile))>=0) { - reg = (struct regnod*)getnode(regnod); + reg = (struct regnod*)getnode(shp->stk,regnod); if(!regtop) regtop = reg; else regold->regnxt = reg; reg->regflag = l; - reg->regptr = r_arg(); - reg->regcom = r_tree(); + reg->regptr = r_arg(shp); + reg->regcom = r_tree(shp); regold = reg; } if(reg) @@ -323,14 +324,14 @@ static struct regnod *r_switch(void) return(regtop); } -static char *r_string(void) +static char *r_string(Stk_t *stkp) { register Sfio_t *in = infile; register unsigned long l = sfgetu(in); register char *ptr; if(l == 0) return(NIL(char*)); - ptr = stakalloc((unsigned)l); + ptr = stkalloc(stkp,(unsigned)l); if(--l > 0) { if(sfread(in,ptr,(size_t)l)!=(size_t)l) diff --git a/usr/src/lib/libshell/common/sh/waitevent.c b/usr/src/lib/libshell/common/sh/waitevent.c index fb7329f471..f187a3085d 100644 --- a/usr/src/lib/libshell/common/sh/waitevent.c +++ b/usr/src/lib/libshell/common/sh/waitevent.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * diff --git a/usr/src/lib/libshell/common/sh/xec.c b/usr/src/lib/libshell/common/sh/xec.c index 378492256e..a930c18460 100644 --- a/usr/src/lib/libshell/common/sh/xec.c +++ b/usr/src/lib/libshell/common/sh/xec.c @@ -1,10 +1,10 @@ /*********************************************************************** * * * This software is part of the ast package * -* Copyright (c) 1982-2007 AT&T Knowledge Ventures * +* Copyright (c) 1982-2008 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * -* by AT&T Knowledge Ventures * +* by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * @@ -45,6 +45,12 @@ # include <vmalloc.h> #endif +#if _lib_vfork +# include <ast_vfork.h> +#else +# define vfork() fork() +#endif + #define SH_NTFORK SH_TIMING #if _lib_nice @@ -54,12 +60,12 @@ # define spawnveg(a,b,c,d) spawnve(a,b,c) #endif /* !_lib_spawnveg */ #if SHOPT_SPAWN - static pid_t sh_ntfork(const Shnode_t*,char*[],int*,int); + static pid_t sh_ntfork(Shell_t*,const Shnode_t*,char*[],int*,int); #endif /* SHOPT_SPAWN */ -static void sh_funct(Namval_t*, int, char*[], struct argnod*,int); +static void sh_funct(Shell_t *,Namval_t*, int, char*[], struct argnod*,int); static int trim_eq(const char*, const char*); -static void coproc_init(int pipes[]); +static void coproc_init(Shell_t*, int pipes[]); static void *timeout; static char pipejob; @@ -96,16 +102,17 @@ static void l_time(Sfio_t *outfile,register clock_t t,int p) sfprintf(outfile,"%dm%ds",min,sec); } -static int p_time(Sfio_t *out, const char *format, clock_t *tm) +static int p_time(Shell_t *shp, Sfio_t *out, const char *format, clock_t *tm) { - int c,p,l,n,offset = staktell(); - const char *first; - double d; + int c,p,l,n,offset = staktell(); + const char *first; + double d; + Stk_t *stkp = shp->stk; for(first=format ; c= *format; format++) { if(c!='%') continue; - stakwrite(first, format-first); + sfwrite(stkp, first, format-first); n = l = 0; p = 3; if((c= *++format) == '%') @@ -136,24 +143,24 @@ static int p_time(Sfio_t *out, const char *format, clock_t *tm) n = 2; else if(c!='R') { - stakseek(offset); + stkseek(stkp,offset); errormsg(SH_DICT,ERROR_exit(0),e_badtformat,c); return(0); } d = (double)tm[n]/sh.lim.clk_tck; skip: if(l) - l_time(stkstd, tm[n], p); + l_time(stkp, tm[n], p); else - sfprintf(stkstd,"%.*f",p, d); + sfprintf(stkp,"%.*f",p, d); first = format+1; } if(format>first) - stakwrite(first, format-first); - stakputc('\n'); - n = staktell()-offset; - sfwrite(out,stakptr(offset),n); - stakseek(offset); + sfwrite(stkp,first, format-first); + sfputc(stkp,'\n'); + n = stktell(stkp)-offset; + sfwrite(out,stkptr(stkp,offset),n); + stkseek(stkp,offset); return(n); } @@ -172,13 +179,17 @@ static int p_comarg(register struct comnod *com) if(com->comstate && np) { /* call builtin to cleanup state */ - Nambltin_t bdata; - bdata.shp = &sh; - bdata.np = com->comnamq; - bdata.ptr =nv_context(np); - bdata.data = com->comstate; - bdata.flags = SH_END_OPTIM; - (*funptr(np))(0,(char**)0, &bdata); + Shbltin_t *bp = &sh.bltindata; + void *save_ptr = bp->ptr; + void *save_data = bp->data; + bp->bnode = np; + bp->vnode = com->comnamq; + bp->ptr = nv_context(np); + bp->data = com->comstate; + bp->flags = SH_END_OPTIM; + (*funptr(np))(0,(char**)0, bp); + bp->ptr = save_ptr; + bp->data = save_data; } com->comstate = 0; if(com->comarg && !np) @@ -312,7 +323,7 @@ static void out_string(Sfio_t *iop, register const char *cp, int c, int quoted) { if(quoted) { - int n = staktell(); + int n = stktell(stkstd); cp = sh_fmtq(cp); if(iop==stkstd && cp==stkptr(stkstd,n)) { @@ -340,10 +351,12 @@ static void put_level(Namval_t* np,const char *val,int flags,Namfun_t *fp) struct Level *lp = (struct Level*)fp; int16_t level, oldlevel = (int16_t)nv_getnum(np); nv_putv(np,val,flags,fp); + if(!val) + return; level = nv_getnum(np); if(level<0 || level > lp->maxlevel) { - nv_putv(np, (char*)&oldlevel, flags, fp); + nv_putv(np, (char*)&oldlevel, NV_INT16, fp); /* perhaps this should be an error */ return; } @@ -351,28 +364,42 @@ static void put_level(Namval_t* np,const char *val,int flags,Namfun_t *fp) return; if(sp = sh_getscope(level,SEEK_SET)) { - sh_setscope(sp); - error_info.line = sp->lineno; + sh_setscope(sp); + error_info.id = sp->cmdname; + } - nv_putval(SH_PATHNAMENOD, sh.st.filename ,NV_NOFREE); } -static const Namdisc_t level_disc = { 0, put_level }; +static const Namdisc_t level_disc = { sizeof(struct Level), put_level }; + +static struct Level *init_level(int level) +{ + struct Level *lp = newof(NiL,struct Level,1,0); + lp->maxlevel = level; + _nv_unset(SH_LEVELNOD,0); + nv_onattr(SH_LEVELNOD,NV_INT16|NV_NOFREE); + nv_putval(SH_LEVELNOD,(char*)&lp->maxlevel,NV_INT16); + lp->hdr.disc = &level_disc; + nv_disc(SH_LEVELNOD,&lp->hdr,NV_FIRST); + return(lp); +} /* * write the current common on the stack and make it available as .sh.command */ -int sh_debug(const char *trap, const char *name, const char *subscript, char *const argv[], int flags) +int sh_debug(Shell_t *shp, const char *trap, const char *name, const char *subscript, char *const argv[], int flags) { + Stk_t *stkp=shp->stk; struct sh_scoped savst; - Shscope_t *sp, *topmost; Namval_t *np = SH_COMMANDNOD; - struct Level lev; - char *sav = stakptr(0); - int n=4, offset=staktell(); + char *sav = stkptr(stkp,0); + int n=4, offset=stktell(stkp); const char *cp = "+=( "; Sfio_t *iop = stkstd; - int16_t level; + short level; + if(shp->indebug) + return(0); + shp->indebug = 1; if(name) { sfputr(iop,name,-1); @@ -400,33 +427,29 @@ int sh_debug(const char *trap, const char *name, const char *subscript, char *co if(flags&ARG_ASSIGN) sfputc(iop,')'); else if(iop==stkstd) - *stakptr(staktell()-1) = 0; - np->nvalue.cp = stakfreeze(1); - sh.st.lineno = error_info.line; + *stkptr(stkp,stktell(stkp)-1) = 0; + np->nvalue.cp = stkfreeze(stkp,1); /* now setup .sh.level variable */ - topmost = sh_getscope(0,SEEK_END); - for(level=0, sp=topmost; sp; sp=sp->par_scope) - level++; - memset((void*)&lev,0,sizeof(lev)); - lev.hdr.disc = &level_disc; - lev.maxlevel = --level; - nv_unset(SH_LEVELNOD); - nv_onattr(SH_LEVELNOD,NV_INT16|NV_NOFREE); - nv_putval(SH_LEVELNOD,(char*)&level,NV_INT16); - nv_disc(SH_LEVELNOD,&lev.hdr,NV_FIRST); - savst = sh.st; - sh.st.trap[SH_DEBUGTRAP] = 0; + shp->st.lineno = error_info.line; + level = shp->fn_depth+shp->dot_depth; + if(!SH_LEVELNOD->nvfun || !SH_LEVELNOD->nvfun->disc || nv_isattr(SH_LEVELNOD,NV_INT16|NV_NOFREE)!=(NV_INT16|NV_NOFREE)) + init_level(level); + else + nv_putval(SH_LEVELNOD,(char*)&level,NV_INT16); + savst = shp->st; + shp->st.trap[SH_DEBUGTRAP] = 0; n = sh_trap(trap,0); np->nvalue.cp = 0; - nv_putval(SH_LEVELNOD,(char*)&level,NV_INT16); - nv_disc(SH_LEVELNOD,&lev.hdr,NV_POP); - nv_unset(SH_LEVELNOD); - nv_putval(SH_PATHNAMENOD, sh.st.filename ,NV_NOFREE); - sh.st = savst; - if(sav != stakptr(0)) - stakset(sav,0); + shp->indebug = 0; + if(shp->st.cmdname) + error_info.id = shp->st.cmdname; + nv_putval(SH_PATHNAMENOD,shp->st.filename,NV_NOFREE); + nv_putval(SH_FUNNAMENOD,shp->st.funname,NV_NOFREE); + shp->st = savst; + if(sav != stkptr(stkp,0)) + stkset(stkp,sav,0); else - stakseek(offset); + stkseek(stkp,offset); return(n); } @@ -442,14 +465,30 @@ int sh_eval(register Sfio_t *iop, int mode) struct checkpt *pp = (struct checkpt*)shp->jmplist; struct checkpt buff; static Sfio_t *io_save; + volatile int traceon=0, lineno=0; io_save = iop; /* preserve correct value across longjmp */ +#define SH_TOPFUN 0x8000 /* this is a temporary tksh hack */ + if (mode & SH_TOPFUN) + { + mode ^= SH_TOPFUN; + shp->fn_reset = 1; + } sh_pushcontext(&buff,SH_JMPEVAL); buff.olist = pp->olist; jmpval = sigsetjmp(buff.buff,0); if(jmpval==0) { - t = (Shnode_t*)sh_parse(shp,iop,SH_NL); - sfclose(iop); + if(mode&SH_READEVAL) + { + lineno = shp->inlineno; + if(traceon=sh_isoption(SH_XTRACE)) + sh_offoption(SH_XTRACE); + } + t = (Shnode_t*)sh_parse(shp,iop,(mode&SH_READEVAL)?0:SH_NL); + if(mode&SH_READEVAL) + mode &= SH_READEVAL; + else + sfclose(iop); io_save = 0; if(!sh_isoption(SH_VERBOSE)) sh_offstate(SH_VERBOSE); @@ -461,57 +500,63 @@ int sh_eval(register Sfio_t *iop, int mode) sh_exec(t,sh_isstate(SH_ERREXIT)|mode); } sh_popcontext(&buff); + if(traceon) + sh_onoption(SH_XTRACE); + if(lineno) + shp->inlineno = lineno; if(io_save) sfclose(io_save); - sh_freeup(); + sh_freeup(shp); shp->st.staklist = saveslp; + shp->fn_reset = 0; if(jmpval>SH_JMPEVAL) siglongjmp(*shp->jmplist,jmpval); - return(sh.exitval); + return(shp->exitval); } #if SHOPT_FASTPIPE -static int pipe_exec(int pv[], Shnode_t *t, int errorflg) +static int pipe_exec(Shell_t* shp,int pv[], Shnode_t *t, int errorflg) { struct checkpt buff; register Shnode_t *tchild = t->fork.forktre; Namval_t *np; - Sfio_t *iop; - int jmpval,r; + int jmpval; + volatile Sfio_t *iop; + volatile int r; if((tchild->tre.tretyp&COMMSK)!=TCOM || !(np=(Namval_t*)(tchild->com.comnamp))) { sh_pipe(pv); return(sh_exec(t,errorflg)); } - pv[0] = sh.lim.open_max; - sh.fdstatus[pv[0]] = IOREAD|IODUP|IOSEEK; - pv[1] = sh.lim.open_max+1; - sh.fdstatus[pv[1]] = IOWRITE|IOSEEK; + pv[0] = shp->lim.open_max; + shp->fdstatus[pv[0]] = IOREAD|IODUP|IOSEEK; + pv[1] = shp->lim.open_max+1; + shp->fdstatus[pv[1]] = IOWRITE|IOSEEK; iop = sftmp(IOBSIZE+1); - sh.sftable[sh.lim.open_max+1] = iop; + shp->sftable[shp->lim.open_max+1] = iop; sh_pushcontext(&buff,SH_JMPIO); if(t->tre.tretyp&FPIN) - sh_iosave(0,sh.topfd); - sh_iosave(1,sh.topfd); + sh_iosave(shp,0,shp->topfd,(char*)0); + sh_iosave(shp,1,shp->topfd,(char*)0); jmpval = sigsetjmp(buff.buff,0); if(jmpval==0) { if(t->tre.tretyp&FPIN) - sh_iorenumber(sh.inpipe[0],0); - sh_iorenumber(sh.lim.open_max+1,1); + sh_iorenumber(shp,shp->inpipe[0],0); + sh_iorenumber(shp,shp->lim.open_max+1,1); r = sh_exec(tchild,errorflg); if(sffileno(sfstdout)>=0) pv[0] = sfsetfd(sfstdout,10); iop = sfswap(sfstdout,0); } sh_popcontext(&buff); - sh.sftable[pv[0]] = iop; - sh.fdstatus[pv[0]] = IOREAD|IODUP|IOSEEK; + shp->sftable[pv[0]] = iop; + shp->fdstatus[pv[0]] = IOREAD|IODUP|IOSEEK; sfset(iop,SF_WRITE,0); sfseek(iop,0L,SEEK_SET); - sh_iorestore(buff.topfd,jmpval); + sh_iorestore(shp,buff.topfd,jmpval); if(jmpval>SH_JMPIO) - siglongjmp(*sh.jmplist,jmpval); + siglongjmp(*shp->jmplist,jmpval); return(r); } #endif /* SHOPT_FASTPIPE */ @@ -528,8 +573,10 @@ static int checkopt(char *argv[], int c) continue; if(*cp!='-' || cp[1]=='-') break; - if(strchr(cp,c)) + if(strchr(++cp,c)) return(1); + if(*cp=='h' && cp[1]==0 && *++argv==0) + break; } return(0); } @@ -544,23 +591,66 @@ static void free_list(struct openlist *olist) } } +/* + * set ${.sh.name} and ${.sh.subscript} + * set _ to reference for ${.sh.name}[$.sh.subscript] + */ +static int set_instance(Namval_t *nq, Namval_t *node, struct Namref *nr) +{ + char *cp = nv_name(nq); + Namarr_t *ap; + memset(nr,0,sizeof(*nr)); + nr->np = nq; + nr->root = sh.var_tree; + nr->table = sh.last_table; + if(sh.var_tree!=sh.var_base && !nv_open(cp,nr->root,NV_VARNAME|NV_NOREF|NV_NOSCOPE|NV_NOADD|NV_NOFAIL)) + nr->root = sh.var_base; + nv_putval(SH_NAMENOD, cp, NV_NOFREE); + memcpy(node,L_ARGNOD,sizeof(*node)); + L_ARGNOD->nvalue.nrp = nr; + L_ARGNOD->nvflag = NV_REF|NV_NOFREE; + L_ARGNOD->nvfun = 0; + L_ARGNOD->nvenv = 0; + if((ap=nv_arrayptr(nq)) && (cp = nv_getsub(nq)) && (cp = strdup(cp))) + { + nv_putval(SH_SUBSCRNOD,nr->sub=cp,NV_NOFREE); + return(ap->nelem&ARRAY_SCAN); + } + return(0); +} + +static void unset_instance(Namval_t *nq, Namval_t *node, struct Namref *nr,long mode) +{ + L_ARGNOD->nvalue.nrp = node->nvalue.nrp; + L_ARGNOD->nvflag = node->nvflag; + L_ARGNOD->nvfun = node->nvfun; + if(nr->sub) + { + nv_putsub(nq, nr->sub, mode); + free((void*)nr->sub); + } + nv_unset(SH_NAMENOD); + nv_unset(SH_SUBSCRNOD); +} int sh_exec(register const Shnode_t *t, int flags) { + register Shell_t *shp = &sh; + Stk_t *stkp = shp->stk; sh_sigcheck(); - if(t && !sh.st.execbrk && !sh_isoption(SH_NOEXEC)) + if(t && !shp->st.execbrk && !sh_isoption(SH_NOEXEC)) { register int type = flags; register char *com0 = 0; int errorflg = (type&sh_state(SH_ERREXIT))|OPTIMIZE; int execflg = (type&sh_state(SH_NOFORK)); int mainloop = (type&sh_state(SH_INTERACTIVE)); -#if SHOPT_SPAWN +#if SHOPT_AMP || SHOPT_SPAWN int ntflag = (type&sh_state(SH_NTFORK)); #endif - int topfd = sh.topfd; - char *sav=stakptr(0); - char *cp=0, **com=0; + int topfd = shp->topfd; + char *sav=stkptr(stkp,0); + char *cp=0, **com=0, *comn; int argn; int skipexitset = 0; int was_interactive = 0; @@ -580,11 +670,11 @@ int sh_exec(register const Shnode_t *t, int flags) if(was_monitor&flags) sh_onstate(SH_MONITOR); type = t->tre.tretyp; - if(!sh.intrap) - sh.oldexit=sh.exitval; - sh.exitval=0; - sh.lastsig = 0; - sh.lastpath = 0; + if(!shp->intrap) + shp->oldexit=shp->exitval; + shp->exitval=0; + shp->lastsig = 0; + shp->lastpath = 0; switch(type&COMMSK) { case TCOM: @@ -593,9 +683,12 @@ int sh_exec(register const Shnode_t *t, int flags) char *trap; Namval_t *np, *nq, *last_table; struct ionod *io; - int command=0; - error_info.line = t->com.comline-sh.st.firstline; - com = sh_argbuild(&argn,&(t->com),OPTIMIZE); + int command=0, flgs=NV_ASSIGN; + shp->bltindata.invariant = type>>(COMBITS+2); + type &= (COMMSK|COMSCAN); + sh_stats(STAT_SCMDS); + error_info.line = t->com.comline-shp->st.firstline; + com = sh_argbuild(shp,&argn,&(t->com),OPTIMIZE); echeck = 1; if(t->tre.tretyp&COMSCAN) { @@ -606,157 +699,196 @@ int sh_exec(register const Shnode_t *t, int flags) np = (Namval_t*)(t->com.comnamp); nq = (Namval_t*)(t->com.comnamq); com0 = com[0]; - sh.xargexit = 0; + shp->xargexit = 0; while(np==SYSCOMMAND) { - register int n = b_command(0,com,&sh); + register int n = b_command(0,com,&shp->bltindata); if(n==0) break; command += n; np = 0; if(!(com0= *(com+=n))) break; - np = nv_bfsearch(com0, sh.bltin_tree, &nq, &cp); + np = nv_bfsearch(com0, shp->bltin_tree, &nq, &cp); } - if(sh.xargexit) + if(shp->xargexit) { - sh.xargmin -= command; - sh.xargmax -= command; + shp->xargmin -= command; + shp->xargmax -= command; } else - sh.xargmin = 0; + shp->xargmin = 0; argn -= command; if(!command && np && is_abuiltin(np)) - np = dtsearch(sh.fun_tree,np); - if(com0 && !np && !strchr(com0,'/')) + np = dtsearch(shp->fun_tree,np); + if(com0) { - Dt_t *root = command?sh.bltin_tree:sh.fun_tree; - np = nv_bfsearch(com0, root, &nq, &cp); -#if SHOPT_NAMESPACE - if(sh.namespace && !nq && !cp) + if(!np && !strchr(com0,'/')) { - int offset = staktell(); - stakputs(nv_name(sh.namespace)); - stakputc('.'); - stakputs(com0); - stakseek(offset); - np = nv_bfsearch(stakptr(offset), root, &nq, &cp); - } + Dt_t *root = command?shp->bltin_tree:shp->fun_tree; + np = nv_bfsearch(com0, root, &nq, &cp); +#if SHOPT_NAMESPACE + if(shp->namespace && !nq && !cp) + { + int offset = stktell(stkp); + sfputr(stkp,nv_name(shp->namespace),-1); + sfputc(stkp,'.'); + sfputr(stkp,com0,0); + stkseek(stkp,offset); + np = nv_bfsearch(stkptr(stkp,offset), root, &nq, &cp); + } #endif /* SHOPT_NAMESPACE */ + } + comn = com[argn-1]; } io = t->tre.treio; - if(sh.envlist = argp = t->com.comset) + if(shp->envlist = argp = t->com.comset) { - if(argn==0 || (np && !command && nv_isattr(np,BLT_SPC))) + if(argn==0 || (np && nv_isattr(np,BLT_SPC))) { - register int flgs=NV_VARNAME|NV_ASSIGN; + if(argn) + { + if(checkopt(com,'A')) + flgs |= NV_ARRAY; + else if(checkopt(com,'a')) + flgs |= NV_IARRAY; + } #if SHOPT_BASH if(np==SYSLOCAL) { if(!nv_getval(SH_FUNNAMENOD)) errormsg(SH_DICT,ERROR_exit(1),"%s: can only be used in a function",com0); - if(!sh.st.var_local) + if(!shp->st.var_local) { - nv_scope((struct argnod*)0); - sh.st.var_local = sh.var_tree; + sh_scope(shp,(struct argnod*)0,0); + shp->st.var_local = shp->var_tree; } } if(np==SYSTYPESET || np==SYSLOCAL) #else - if(np==SYSTYPESET) + if(np==SYSTYPESET || (np && np->nvalue.bfp==SYSTYPESET->nvalue.bfp)) #endif { + if(np!=SYSTYPESET) + shp->typeinit = np; + if(checkopt(com,'C')) + flgs |= NV_COMVAR; + if(checkopt(com,'S')) + flgs |= NV_STATIC; if(checkopt(com,'n')) flgs |= NV_NOREF; #if SHOPT_TYPEDEF - else if(checkopt(com,'T')) + else if(argn>=3 && checkopt(com,'T')) { - sh.prefix = NV_CLASS; + shp->prefix = NV_CLASS; flgs |= NV_TYPE; } #endif /* SHOPT_TYPEDEF */ - if(checkopt(com,'A')) - flgs |= NV_ARRAY; - else if(checkopt(com,'a')) - flgs |= NV_IARRAY; - if((sh.fn_depth && !sh.prefix) || np==SYSLOCAL) + if((shp->fn_depth && !shp->prefix) || np==SYSLOCAL) flgs |= NV_NOSCOPE; } else if(np==SYSEXPORT) flgs |= NV_EXPORT; - else if(np) - flgs = NV_IDENT|NV_ASSIGN; + if(flgs&(NV_EXPORT|NV_NOREF)) + flgs |= NV_IDENT; + else + flgs |= NV_VARNAME; #if 0 if(OPTIMIZE) flgs |= NV_TAGGED; #endif nv_setlist(argp,flgs); + if(np==shp->typeinit) + shp->typeinit = 0; + shp->envlist = argp; argp = NULL; } } - last_table = sh.last_table; - sh.last_table = 0; + last_table = shp->last_table; + shp->last_table = 0; if((io||argn)) { + Shbltin_t *bp=0; static char *argv[1]; + int tflags = 1; + if(np && nv_isattr(np,BLT_DCL)) + tflags |= 2; if(argn==0) { /* fake 'true' built-in */ - argn=1; np = SYSTRUE; *argv = nv_name(np); com = argv; } /* set +x doesn't echo */ else if((np!=SYSSET) && sh_isoption(SH_XTRACE)) - sh_trace(com-command,1); + sh_trace(com-command,tflags); else if((t->tre.tretyp&FSHOWME) && sh_isoption(SH_SHOWME)) { int ison = sh_isoption(SH_XTRACE); if(!ison) sh_onoption(SH_XTRACE); - sh_trace(com-command,1); + sh_trace(com-command,tflags); if(io) - sh_redirect(io,SH_SHOWME); + sh_redirect(shp,io,SH_SHOWME); if(!ison) sh_offoption(SH_XTRACE); break; } - if(trap=sh.st.trap[SH_DEBUGTRAP]) - sh_debug(trap,(char*)0, (char*)0, com, ARG_RAW); + if(trap=shp->st.trap[SH_DEBUGTRAP]) + { + int n = sh_debug(shp,trap,(char*)0,(char*)0, com, ARG_RAW); + if(n==255 && shp->fn_depth+shp->dot_depth) + { + np = SYSRETURN; + argn = 1; + com[0] = np->nvname; + com[1] = 0; + io = 0; + argp = 0; + } + else if(n==2) + break; + } if(io) - sfsync(sh.outpool); - sh.lastpath = 0; + sfsync(shp->outpool); + shp->lastpath = 0; if(!np && !strchr(com0,'/')) { -#ifdef PATH_BFPATH - if(path_search(com0,NIL(Pathcomp_t*),1)) - np=nv_search(com0,sh.fun_tree,0); + if(path_search(com0,NIL(Pathcomp_t**),1)) + { + error_info.line = t->com.comline-shp->st.firstline; + if((np=nv_search(com0,shp->fun_tree,0)) && !np->nvalue.ip) + { + Namval_t *mp=nv_search(com0,shp->bltin_tree,0); + if(mp) + np = mp; + } + } else { - if((np=nv_search(com0,sh.track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && np->nvalue.cp) - np=nv_search(nv_getval(np),sh.bltin_tree,0); + if((np=nv_search(com0,shp->track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && np->nvalue.cp) + np=nv_search(nv_getval(np),shp->bltin_tree,0); else np = 0; } -#else - if(path_search(com0,NIL(char*),1)) - np=nv_search(com0,sh.fun_tree,0); - if(sh.lastpath) - np=nv_search(sh.lastpath,sh.bltin_tree,0); -#endif } /* check for builtins */ if(np && is_abuiltin(np)) { - Nambltin_t bdata; - void *context; - int scope=0, jmpval, save_prompt,share; + volatile int scope=0, share=0; + volatile void *save_ptr; + volatile void *save_data; + int jmpval, save_prompt; struct checkpt buff; unsigned long was_vi=0, was_emacs=0, was_gmacs=0; struct stat statb; + bp = &shp->bltindata; + save_ptr = bp->ptr; + save_data = bp->data; + memset(&statb, 0, sizeof(struct stat)); if(strchr(nv_name(np),'/')) { /* @@ -785,61 +917,65 @@ int sh_exec(register const Shnode_t *t, int flags) else if(np==SYSEXEC) type=1+!com[1]; else - type = (execflg && !sh.subshell && !sh.st.trapcom[0]); - sh_redirect(io,type); + type = (execflg && !shp->subshell && !shp->st.trapcom[0]); + sh_redirect(shp,io,type); for(item=buff.olist;item;item=item->next) item->strm=0; } if(!(nv_isattr(np,BLT_ENV))) { - if(!sh.pwd) - path_pwd(0); - if(sh.pwd) - stat(".",&statb); + if(bp->nosfio) + { + if(!shp->pwd) + path_pwd(0); + if(shp->pwd) + stat(".",&statb); + } + sfsync(NULL); share = sfset(sfstdin,SF_SHARE,0); sh_onstate(SH_STOPOK); sfpool(sfstderr,NIL(Sfio_t*),SF_WRITE); sfset(sfstderr,SF_LINE,1); - save_prompt = sh.nextprompt; - sh.nextprompt = 0; + save_prompt = shp->nextprompt; + shp->nextprompt = 0; } if(argp) { scope++; - nv_scope(argp); + sh_scope(shp,argp,0); } opt_info.index = opt_info.offset = 0; opt_info.disc = 0; error_info.id = *com; - sh.exitval = 0; - if(!(context=nv_context(np))) - context = (void*)&sh; - sh.bltinfun = funptr(np); - if(nv_isattr(np,NV_BLTINOPT)) - { - bdata.shp = &sh; - bdata.np = nq; - bdata.ptr = context; - bdata.data = t->com.comstate; - bdata.flags = (OPTIMIZE!=0); - context = (void*)&bdata; - } - if(execflg && !sh.subshell && - !sh.st.trapcom[0] && !sh.st.trap[SH_ERRTRAP] && sh.fn_depth==0 && !nv_isattr(np,BLT_ENV)) + if(argn) + shp->exitval = 0; + shp->bltinfun = funptr(np); + bp->bnode = np; + bp->vnode = nq; + bp->ptr = nv_context(np); + bp->data = t->com.comstate; + bp->sigset = 0; + bp->notify = 0; + bp->flags = (OPTIMIZE!=0); + if(shp->subshell && nv_isattr(np,BLT_NOSFIO)) + sh_subtmpfile(0); + if(execflg && !shp->subshell && + !shp->st.trapcom[0] && !shp->st.trap[SH_ERRTRAP] && shp->fn_depth==0 && !nv_isattr(np,BLT_ENV)) { /* do close-on-exec */ int fd; - for(fd=0; fd < sh.lim.open_max; fd++) - if((sh.fdstatus[fd]&IOCLEX)&&fd!=sh.infd) + for(fd=0; fd < shp->lim.open_max; fd++) + if((shp->fdstatus[fd]&IOCLEX)&&fd!=shp->infd) sh_close(fd); } - sh.exitval = (*sh.bltinfun)(argn,com,context); + if(argn) + shp->exitval = (*shp->bltinfun)(argn,com,(void*)bp); if(error_info.flags&ERROR_INTERACTIVE) tty_check(ERRIO); - if(nv_isattr(np,NV_BLTINOPT)) - ((Shnode_t*)t)->com.comstate = bdata.data; - if(!nv_isattr(np,BLT_EXIT) && sh.exitval!=SH_RUNPROG) - sh.exitval &= SH_EXITMASK; + ((Shnode_t*)t)->com.comstate = shp->bltindata.data; + bp->data = (void*)save_data; + if(!nv_isattr(np,BLT_EXIT) && shp->exitval!=SH_RUNPROG) + shp->exitval &= SH_EXITMASK; } else { @@ -849,38 +985,42 @@ int sh_exec(register const Shnode_t *t, int flags) if(item->strm) { sfclrlock(item->strm); - if(sh.hist_ptr && item->strm == sh.hist_ptr->histfp) - hist_close(sh.hist_ptr); + if(shp->hist_ptr && item->strm == shp->hist_ptr->histfp) + hist_close(shp->hist_ptr); else sfclose(item->strm); } } + if(shp->bltinfun && (error_info.flags&ERROR_NOTIFY)) + (*shp->bltinfun)(-2,com,(void*)bp); /* failure on special built-ins fatal */ if(jmpval<=SH_JMPCMD && (!nv_isattr(np,BLT_SPC) || command)) jmpval=0; } + if(bp && bp->ptr!= nv_context(np)) + np->nvfun = (Namfun_t*)bp->ptr; if(!(nv_isattr(np,BLT_ENV))) { - if(sh.pwd) + if(bp->nosfio && shp->pwd) { struct stat stata; stat(".",&stata); /* restore directory changed */ if(statb.st_ino!=stata.st_ino || statb.st_dev!=stata.st_dev) - chdir(sh.pwd); + chdir(shp->pwd); } sh_offstate(SH_STOPOK); if(share&SF_SHARE) sfset(sfstdin,SF_PUBLIC|SF_SHARE,1); sfset(sfstderr,SF_LINE,0); - sfpool(sfstderr,sh.outpool,SF_WRITE); + sfpool(sfstderr,shp->outpool,SF_WRITE); sfpool(sfstdin,NIL(Sfio_t*),SF_WRITE); - sh.nextprompt = save_prompt; + shp->nextprompt = save_prompt; } sh_popcontext(&buff); errorpop(&buff.err); error_info.flags &= ~ERROR_SILENT; - sh.bltinfun = 0; + shp->bltinfun = 0; if(buff.olist) free_list(buff.olist); if(was_vi) @@ -890,13 +1030,19 @@ int sh_exec(register const Shnode_t *t, int flags) else if(was_gmacs) sh_onoption(SH_GMACS); if(scope) - nv_unscope(); + sh_unscope(shp); + bp->ptr = (void*)save_ptr; + bp->data = (void*)save_data; /* don't restore for subshell exec */ - if((sh.topfd>topfd) && !(sh.subshell && np==SYSEXEC)) - sh_iorestore(topfd,jmpval); + if((shp->topfd>topfd) && !(shp->subshell && np==SYSEXEC)) + sh_iorestore(shp,topfd,jmpval); if(jmpval) - siglongjmp(*sh.jmplist,jmpval); - if(sh.exitval >=0) + siglongjmp(*shp->jmplist,jmpval); +#if 0 + if(flgs&NV_STATIC) + ((Shnode_t*)t)->com.comset = 0; +#endif + if(shp->exitval >=0) goto setexit; np = 0; type=0; @@ -904,30 +1050,30 @@ int sh_exec(register const Shnode_t *t, int flags) /* check for functions */ if(!command && np && nv_isattr(np,NV_FUNCTION)) { - int indx,jmpval=0; + volatile int indx; + int jmpval=0; struct checkpt buff; Namval_t node; + struct Namref nr; + long mode; register struct slnod *slp; if(!np->nvalue.ip) { -#ifdef PATH_BFPATH - indx = path_search(com0,NIL(Pathcomp_t*),0); -#else - indx = path_search(com0,NIL(char*),0); -#endif + indx = path_search(com0,NIL(Pathcomp_t**),0); if(indx==1) - np = nv_search(com0,sh.fun_tree,HASH_NOSCOPE); + np = nv_search(com0,shp->fun_tree,HASH_NOSCOPE); + if(!np->nvalue.ip) { if(indx==1) { errormsg(SH_DICT,ERROR_exit(0),e_defined,com0); - sh.exitval = ERROR_NOEXEC; + shp->exitval = ERROR_NOEXEC; } else { errormsg(SH_DICT,ERROR_exit(0),e_found,"function"); - sh.exitval = ERROR_NOENT; + shp->exitval = ERROR_NOENT; } goto setexit; } @@ -938,54 +1084,34 @@ int sh_exec(register const Shnode_t *t, int flags) staklink(slp->slptr); if(nq) { - struct Namref nr; - sh.last_table = last_table; - memset(&nr,0,sizeof(nr)); - nr.np = nq; - nv_putval(SH_NAMENOD, nv_name(nq), NV_NOFREE); - memcpy(&node,L_ARGNOD,sizeof(node)); - L_ARGNOD->nvalue.nrp = &nr; - L_ARGNOD->nvenv = 0; - L_ARGNOD->nvfun = (Namfun_t*)sh.last_table; - L_ARGNOD->nvflag = NV_REF|NV_NOFREE; - if(nv_arrayptr(nq)) - { - nv_putval(SH_SUBSCRNOD,nv_getsub(nq),NV_NOFREE); - L_ARGNOD->nvenv = (char*)SH_SUBSCRNOD->nvalue.cp; - } + shp->last_table = last_table; + mode = set_instance(nq,&node,&nr); } if(io) { - indx = sh.topfd; + indx = shp->topfd; sh_pushcontext(&buff,SH_JMPCMD); jmpval = sigsetjmp(buff.buff,0); } if(jmpval == 0) { if(io) - indx = sh_redirect(io,execflg); - sh_funct(np,argn,com,t->com.comset,(flags&~OPTIMIZE_FLAG)); + indx = sh_redirect(shp,io,execflg); + sh_funct(shp,np,argn,com,t->com.comset,(flags&~OPTIMIZE_FLAG)); } if(io) { if(buff.olist) free_list(buff.olist); sh_popcontext(&buff); - sh_iorestore(indx,jmpval); + sh_iorestore(shp,indx,jmpval); } if(nq) - { - L_ARGNOD->nvalue.np = node.nvalue.np; - L_ARGNOD->nvenv = node.nvenv; - L_ARGNOD->nvflag = node.nvflag; - L_ARGNOD->nvfun = node.nvfun; - nv_unset(SH_NAMENOD); - nv_unset(SH_SUBSCRNOD); - } + unset_instance(nq,&node,&nr,mode); sh_funstaks(slp->slchild,-1); stakdelete(slp->slptr); if(jmpval > SH_JMPFUN) - siglongjmp(*sh.jmplist,jmpval); + siglongjmp(*shp->jmplist,jmpval); goto setexit; } } @@ -1002,11 +1128,15 @@ int sh_exec(register const Shnode_t *t, int flags) int no_fork,jobid; int pipes[2]; no_fork = (execflg && !(type&(FAMP|FPOU)) && - !sh.subshell && !sh.st.trapcom[0] && - !sh.st.trap[SH_ERRTRAP] && sh.fn_depth==0); - if(sh.subshell) - sh_subtmpfile(); - if(sh_isstate(SH_PROFILE) || sh.dot_depth) +#if SHOPT_AMP || SHOPT_SPAWN + !ntflag && +#endif + !shp->subshell && !shp->st.trapcom[0] && + !shp->st.trap[SH_ERRTRAP] && shp->fn_depth==0 && + !(pipejob && sh_isoption(SH_PIPEFAIL))); + if(shp->subshell) + sh_subtmpfile(1); + if(sh_isstate(SH_PROFILE) || shp->dot_depth) { /* disable foreground job monitor */ if(!(type&FAMP)) @@ -1021,11 +1151,11 @@ int sh_exec(register const Shnode_t *t, int flags) else { if(type&FCOOP) - coproc_init(pipes); + coproc_init(shp,pipes); nv_getval(RANDNOD); #if SHOPT_AMP if((type&(FAMP|FINT)) == (FAMP|FINT)) - parent = sh_ntfork(t,com,&jobid,ntflag); + parent = sh_ntfork(shp,t,com,&jobid,ntflag); else parent = sh_fork(type,&jobid); if(parent<0) @@ -1034,11 +1164,11 @@ int sh_exec(register const Shnode_t *t, int flags) #if SHOPT_SPAWN # ifdef _lib_fork if(com) - parent = sh_ntfork(t,com,&jobid,ntflag); + parent = sh_ntfork(shp,t,com,&jobid,ntflag); else parent = sh_fork(type,&jobid); # else - if((parent = sh_ntfork(t,com,&jobid,ntflag))<=0) + if((parent = sh_ntfork(shp,t,com,&jobid,ntflag))<=0) break; # endif /* _lib_fork */ if(parent<0) @@ -1054,14 +1184,32 @@ int sh_exec(register const Shnode_t *t, int flags) */ { if(type&FPCL) - sh_close(sh.inpipe[0]); + sh_close(shp->inpipe[0]); if(type&(FCOOP|FAMP)) - sh.bckpid = parent; - if(!(type&(FAMP|FPOU))) + shp->bckpid = parent; + else if(!(type&(FAMP|FPOU))) { - if(sh.topfd > topfd) - sh_iorestore(topfd,0); - job_wait(parent); + if(shp->topfd > topfd) + sh_iorestore(shp,topfd,0); + if(!sh_isoption(SH_MONITOR)) + { + if(!(shp->sigflag[SIGINT]&(SH_SIGFAULT|SH_SIGOFF))) + sh_sigtrap(SIGINT); + shp->trapnote |= SH_SIGIGNORE; + } + if(execflg && shp->subshell) + { + shp->spid = parent; + job.pwlist->p_env--; + } + else + job_wait(parent); + if(!sh_isoption(SH_MONITOR)) + { + shp->trapnote &= ~SH_SIGIGNORE; + if(shp->exitval == (SH_EXITSIG|SIGINT)) + sh_fault(SIGINT); + } } if(type&FAMP) { @@ -1082,7 +1230,7 @@ int sh_exec(register const Shnode_t *t, int flags) * this is the FORKED branch (child) of execute */ { - int jmpval; + volatile int jmpval; struct checkpt buff; if(no_fork) sh_sigreset(2); @@ -1095,7 +1243,7 @@ int sh_exec(register const Shnode_t *t, int flags) /* default std input for & */ signal(SIGINT,SIG_IGN); signal(SIGQUIT,SIG_IGN); - if(!sh.st.ioset) + if(!shp->st.ioset) { if(sh_close(0)>=0) sh_chkopen(e_devnull); @@ -1109,20 +1257,35 @@ int sh_exec(register const Shnode_t *t, int flags) #endif /* _lib_nice */ if(type&FPIN) { - sh_iorenumber(sh.inpipe[0],0); + sh_iorenumber(shp,shp->inpipe[0],0); if(!(type&FPOU) || (type&FCOOP)) - sh_close(sh.inpipe[1]); + sh_close(shp->inpipe[1]); } if(type&FPOU) { - sh_iorenumber(sh.outpipe[1],1); - sh_pclose(sh.outpipe); + sh_iorenumber(shp,shp->outpipe[1],1); + sh_pclose(shp->outpipe); } if((type&COMMSK)!=TCOM) - error_info.line = t->fork.forkline-sh.st.firstline; - sh_redirect(t->tre.treio,1); - if(sh.topfd) - sh_iounsave(); + error_info.line = t->fork.forkline-shp->st.firstline; + if(shp->topfd) + sh_iounsave(shp); + topfd = shp->topfd; + sh_redirect(shp,t->tre.treio,1); + if(shp->topfd > topfd) + { + while((parent = vfork()) < 0) + _sh_fork(parent, 0, (int*)0); + if(parent) + { + job_clear(); + job_post(parent,0); + job_wait(parent); + sh_iorestore(shp,topfd,SH_JMPCMD); + sh_done(shp,(shp->exitval&SH_EXITSIG)?(shp->exitval&SH_EXITMASK):0); + + } + } if((type&COMMSK)!=TCOM) { /* don't clear job table for out @@ -1136,14 +1299,14 @@ int sh_exec(register const Shnode_t *t, int flags) else if(com0) { sh_offoption(SH_ERREXIT); - sh_freeup(); + sh_freeup(shp); path_exec(com0,com,t->com.comset); } done: sh_popcontext(&buff); if(jmpval>SH_JMPEXIT) - siglongjmp(*sh.jmplist,jmpval); - sh_done(0); + siglongjmp(*shp->jmplist,jmpval); + sh_done(shp,0); } } @@ -1156,19 +1319,16 @@ int sh_exec(register const Shnode_t *t, int flags) pid_t pid; int jmpval, waitall; struct checkpt buff; - if(sh.subshell) - { - flags &= ~sh_state(SH_NOFORK); + if(shp->subshell) execflg = 0; - } sh_pushcontext(&buff,SH_JMPIO); if(type&FPIN) { was_interactive = sh_isstate(SH_INTERACTIVE); sh_offstate(SH_INTERACTIVE); if(!execflg) - sh_iosave(0,sh.topfd); - sh_iorenumber(sh.inpipe[0],0); + sh_iosave(shp,0,shp->topfd,(char*)0); + sh_iorenumber(shp,shp->inpipe[0],0); /* * if read end of pipe is a simple command * treat as non-sharable to improve performance @@ -1180,40 +1340,42 @@ int sh_exec(register const Shnode_t *t, int flags) pid = job.parent; } else - error_info.line = t->fork.forkline-sh.st.firstline; + error_info.line = t->fork.forkline-shp->st.firstline; jmpval = sigsetjmp(buff.buff,0); if(jmpval==0) { - sh_redirect(t->fork.forkio,execflg); + sh_redirect(shp,t->fork.forkio,execflg); (t->fork.forktre)->tre.tretyp |= t->tre.tretyp&FSHOWME; sh_exec(t->fork.forktre,flags); } + else + sfsync(shp->outpool); sh_popcontext(&buff); - sh_iorestore(buff.topfd,jmpval); + sh_iorestore(shp,buff.topfd,jmpval); if(buff.olist) free_list(buff.olist); if(type&FPIN) { job.waitall = waitall; - type = sh.exitval; + type = shp->exitval; if(!(type&SH_EXITSIG)) { /* wait for remainder of pipline */ job_wait(waitall?pid:0); if(type || !sh_isoption(SH_PIPEFAIL)) - sh.exitval = type; + shp->exitval = type; } - sh.st.ioset = 0; + shp->st.ioset = 0; } if(jmpval>SH_JMPIO) - siglongjmp(*sh.jmplist,jmpval); + siglongjmp(*shp->jmplist,jmpval); break; } case TPAR: echeck = 1; flags &= ~OPTIMIZE_FLAG; - if(!sh.subshell && !sh.st.trapcom[0] && !sh.st.trap[SH_ERRTRAP] && (flags&sh_state(SH_NOFORK))) + if(!shp->subshell && !shp->st.trapcom[0] && !shp->st.trap[SH_ERRTRAP] && (flags&sh_state(SH_NOFORK))) { int jmpval; struct checkpt buff; @@ -1223,8 +1385,8 @@ int sh_exec(register const Shnode_t *t, int flags) sh_exec(t->par.partre,flags); sh_popcontext(&buff); if(jmpval > SH_JMPEXIT) - siglongjmp(*sh.jmplist,jmpval); - sh_done(0); + siglongjmp(*shp->jmplist,jmpval); + sh_done(shp,0); } else sh_subshell(t->par.partre,flags,0); @@ -1242,10 +1404,11 @@ int sh_exec(register const Shnode_t *t, int flags) int savepipe = pipejob; int showme = t->tre.tretyp&FSHOWME; pid_t savepgid = job.curpgid; - if(sh.subshell) - sh_subtmpfile(); - sh.inpipe = pvo; - sh.outpipe = pvn; + job.curpgid = 0; + if(shp->subshell) + sh_subtmpfile(1); + shp->inpipe = pvo; + shp->outpipe = pvn; pvo[1] = -1; if(sh_isoption(SH_PIPEFAIL)) job.waitall = 1; @@ -1254,7 +1417,7 @@ int sh_exec(register const Shnode_t *t, int flags) do { #if SHOPT_FASTPIPE - type = pipe_exec(pvn,t->lst.lstlef, errorflg); + type = pipe_exec(shp,pvn,t->lst.lstlef, errorflg); #else /* create the pipe */ sh_pipe(pvn); @@ -1272,8 +1435,8 @@ int sh_exec(register const Shnode_t *t, int flags) } /* repeat until end of pipeline */ while(!type && t->tre.tretyp==TFIL); - sh.inpipe = pvn; - sh.outpipe = 0; + shp->inpipe = pvn; + shp->outpipe = 0; if(type == 0) { /* @@ -1289,7 +1452,7 @@ int sh_exec(register const Shnode_t *t, int flags) pipejob = savepipe; #ifdef SIGTSTP if(!pipejob && sh_isstate(SH_MONITOR)) - tcsetpgrp(JOBTTY,sh.pid); + tcsetpgrp(JOBTTY,shp->pid); #endif /*SIGTSTP */ job.curpgid = savepgid; break; @@ -1332,35 +1495,35 @@ int sh_exec(register const Shnode_t *t, int flags) struct comnod *tp; char *cp, *trap, *nullptr = 0; int nameref, refresh=1; - static char *av[5] = { "for", 0, "in" }; + char *av[5]; #if SHOPT_OPTIMIZE - int jmpval = ((struct checkpt*)sh.jmplist)->mode; + int jmpval = ((struct checkpt*)shp->jmplist)->mode; struct checkpt buff; - void *optlist = sh.optlist; - sh.optlist = 0; + void *optlist = shp->optlist; + shp->optlist = 0; sh_tclear(t->for_.fortre); sh_pushcontext(&buff,jmpval); jmpval = sigsetjmp(buff.buff,0); if(jmpval) goto endfor; #endif /* SHOPT_OPTIMIZE */ - error_info.line = t->for_.forline-sh.st.firstline; + error_info.line = t->for_.forline-shp->st.firstline; if(!(tp=t->for_.forlst)) { - args=sh.st.dolv+1; - nargs = sh.st.dolc; - argsav=sh_arguse(); + args=shp->st.dolv+1; + nargs = shp->st.dolc; + argsav=sh_arguse(shp); } else { - args=sh_argbuild(&argn,tp,0); + args=sh_argbuild(shp,&argn,tp,0); nargs = argn; } - np = nv_open(t->for_.fornam, sh.var_tree,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME|NV_NOREF); + np = nv_open(t->for_.fornam, shp->var_tree,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME|NV_NOREF); nameref = nv_isref(np)!=0; - sh.st.loopcnt++; + shp->st.loopcnt++; cp = *args; - while(cp && sh.st.execbrk==0) + while(cp && shp->st.execbrk==0) { if(t->tre.tretyp&COMSCAN) { @@ -1372,17 +1535,17 @@ int sh_exec(register const Shnode_t *t, int flags) sh_menu(sfstderr,nargs,args); refresh = 0; } - save_prompt = sh.nextprompt; - sh.nextprompt = 3; - sh.timeout = 0; - sh.exitval=sh_readline(&sh,&nullptr,0,1,1000*sh.st.tmout); - sh.nextprompt = save_prompt; - if(sh.exitval||sfeof(sfstdin)||sferror(sfstdin)) + save_prompt = shp->nextprompt; + shp->nextprompt = 3; + shp->timeout = 0; + shp->exitval=sh_readline(shp,&nullptr,0,1,1000*shp->st.tmout); + shp->nextprompt = save_prompt; + if(shp->exitval||sfeof(sfstdin)||sferror(sfstdin)) { - sh.exitval = 1; + shp->exitval = 1; break; } - if(!(val=nv_getval(nv_scoped(REPLYNOD)))) + if(!(val=nv_getval(sh_scoped(shp,REPLYNOD)))) continue; else { @@ -1411,45 +1574,47 @@ int sh_exec(register const Shnode_t *t, int flags) nv_putval(np,cp,0); if(nameref) nv_setref(np,(Dt_t*)0,NV_VARNAME); - if(trap=sh.st.trap[SH_DEBUGTRAP]) + if(trap=shp->st.trap[SH_DEBUGTRAP]) { av[0] = (t->tre.tretyp&COMSCAN)?"select":"for"; av[1] = t->for_.fornam; + av[2] = "in"; av[3] = cp; - sh_debug(trap,(char*)0,(char*)0,av,0); + av[4] = 0; + sh_debug(shp,trap,(char*)0,(char*)0,av,0); } sh_exec(t->for_.fortre,flag); flag &= ~OPTIMIZE_FLAG; if(t->tre.tretyp&COMSCAN) { - if((cp=nv_getval(nv_scoped(REPLYNOD))) && *cp==0) + if((cp=nv_getval(sh_scoped(shp,REPLYNOD))) && *cp==0) refresh++; } else cp = *++args; check: - if(sh.st.breakcnt<0) - sh.st.execbrk = (++sh.st.breakcnt !=0); + if(shp->st.breakcnt<0) + shp->st.execbrk = (++shp->st.breakcnt !=0); } #if SHOPT_OPTIMIZE endfor: sh_popcontext(&buff); sh_tclear(t->for_.fortre); - sh_optclear(&sh,optlist); + sh_optclear(shp,optlist); if(jmpval) - siglongjmp(*sh.jmplist,jmpval); + siglongjmp(*shp->jmplist,jmpval); #endif /*SHOPT_OPTIMIZE */ - if(sh.st.breakcnt>0) - sh.st.execbrk = (--sh.st.breakcnt !=0); - sh.st.loopcnt--; - sh_argfree(argsav,0); + if(shp->st.breakcnt>0) + shp->st.execbrk = (--shp->st.breakcnt !=0); + shp->st.loopcnt--; + sh_argfree(shp,argsav,0); nv_close(np); break; } case TWH: /* while and until */ { - register int r=0; + volatile int r=0; int first = OPTIMIZE_FLAG; Shnode_t *tt = t->wh.whtre; #if SHOPT_FILESCAN @@ -1457,10 +1622,10 @@ int sh_exec(register const Shnode_t *t, int flags) int savein,fd; #endif /*SHOPT_FILESCAN*/ #if SHOPT_OPTIMIZE - int jmpval = ((struct checkpt*)sh.jmplist)->mode; + int jmpval = ((struct checkpt*)shp->jmplist)->mode; struct checkpt buff; - void *optlist = sh.optlist; - sh.optlist = 0; + void *optlist = shp->optlist; + shp->optlist = 0; sh_tclear(t->wh.whtre); sh_tclear(t->wh.dotre); sh_pushcontext(&buff,jmpval); @@ -1471,26 +1636,26 @@ int sh_exec(register const Shnode_t *t, int flags) #if SHOPT_FILESCAN if(type==TWH && tt->tre.tretyp==TCOM && !tt->com.comarg && tt->com.comio) { - fd = sh_redirect(tt->com.comio,3); + fd = sh_redirect(shp,tt->com.comio,3); savein = dup(0); if(fd==0) fd = savein; iop = sfnew(NULL,NULL,SF_UNBOUND,fd,SF_READ); close(0); open("/dev/null",O_RDONLY); - sh.offsets[0] = -1; - sh.offsets[1] = 0; + shp->offsets[0] = -1; + shp->offsets[1] = 0; if(tt->com.comset) nv_setlist(tt->com.comset,NV_IDENT|NV_ASSIGN); } #endif /*SHOPT_FILESCAN */ - sh.st.loopcnt++; - while(sh.st.execbrk==0) + shp->st.loopcnt++; + while(shp->st.execbrk==0) { #if SHOPT_FILESCAN if(iop) { - if(!(sh.cur_line=sfgetr(iop,'\n',SF_STRING))) + if(!(shp->cur_line=sfgetr(iop,'\n',SF_STRING))) break; } else @@ -1498,16 +1663,16 @@ int sh_exec(register const Shnode_t *t, int flags) if((sh_exec(tt,first)==0)!=(type==TWH)) break; r = sh_exec(t->wh.dotre,first|errorflg); - if(sh.st.breakcnt<0) - sh.st.execbrk = (++sh.st.breakcnt !=0); + if(shp->st.breakcnt<0) + shp->st.execbrk = (++shp->st.breakcnt !=0); /* This is for the arithmetic for */ - if(sh.st.execbrk==0 && t->wh.whinc) + if(shp->st.execbrk==0 && t->wh.whinc) sh_exec((Shnode_t*)t->wh.whinc,first); first = 0; errorflg &= ~OPTIMIZE_FLAG; #if SHOPT_FILESCAN - sh.offsets[0] = -1; - sh.offsets[1] = 0; + shp->offsets[0] = -1; + shp->offsets[1] = 0; #endif /*SHOPT_FILESCAN */ } #if SHOPT_OPTIMIZE @@ -1515,21 +1680,21 @@ int sh_exec(register const Shnode_t *t, int flags) sh_popcontext(&buff); sh_tclear(t->wh.whtre); sh_tclear(t->wh.dotre); - sh_optclear(&sh,optlist); + sh_optclear(shp,optlist); if(jmpval) - siglongjmp(*sh.jmplist,jmpval); + siglongjmp(*shp->jmplist,jmpval); #endif /*SHOPT_OPTIMIZE */ - if(sh.st.breakcnt>0) - sh.st.execbrk = (--sh.st.breakcnt !=0); - sh.st.loopcnt--; - sh.exitval= r; + if(shp->st.breakcnt>0) + shp->st.execbrk = (--shp->st.breakcnt !=0); + shp->st.loopcnt--; + shp->exitval= r; #if SHOPT_FILESCAN if(iop) { sfclose(iop); close(0); dup(savein); - sh.cur_line = 0; + shp->cur_line = 0; } #endif /*SHOPT_FILESCAN */ break; @@ -1537,23 +1702,26 @@ int sh_exec(register const Shnode_t *t, int flags) case TARITH: /* (( expression )) */ { register char *trap; - static char *arg[4]= {"((", 0, "))"}; - error_info.line = t->ar.arline-sh.st.firstline; + char *arg[4]; + error_info.line = t->ar.arline-shp->st.firstline; + arg[0] = "(("; if(!(t->ar.arexpr->argflag&ARG_RAW)) - arg[1] = sh_macpat(t->ar.arexpr,OPTIMIZE|ARG_ARITH); + arg[1] = sh_macpat(shp,t->ar.arexpr,OPTIMIZE|ARG_ARITH); else arg[1] = t->ar.arexpr->argval; - if(trap=sh.st.trap[SH_DEBUGTRAP]) - sh_debug(trap,(char*)0, (char*)0, arg, ARG_ARITH); + arg[2] = "))"; + arg[3] = 0; + if(trap=shp->st.trap[SH_DEBUGTRAP]) + sh_debug(shp,trap,(char*)0, (char*)0, arg, ARG_ARITH); if(sh_isoption(SH_XTRACE)) { sh_trace(NIL(char**),0); sfprintf(sfstderr,"((%s))\n",arg[1]); } if(t->ar.arcomp) - sh.exitval = !arith_exec((Arith_t*)t->ar.arcomp); + shp->exitval = !arith_exec((Arith_t*)t->ar.arcomp); else - sh.exitval = !sh_arith(arg[1]); + shp->exitval = !sh_arith(arg[1]); break; } @@ -1563,20 +1731,23 @@ int sh_exec(register const Shnode_t *t, int flags) else if(t->if_.eltre) sh_exec(t->if_.eltre, flags); else - sh.exitval=0; /* force zero exit for if-then-fi */ + shp->exitval=0; /* force zero exit for if-then-fi */ break; case TSW: { Shnode_t *tt = (Shnode_t*)t; - char *trap, *r = sh_macpat(tt->sw.swarg,OPTIMIZE); - error_info.line = t->sw.swline-sh.st.firstline; + char *trap, *r = sh_macpat(shp,tt->sw.swarg,OPTIMIZE); + error_info.line = t->sw.swline-shp->st.firstline; t= (Shnode_t*)(tt->sw.swlst); - if(trap=sh.st.trap[SH_DEBUGTRAP]) + if(trap=shp->st.trap[SH_DEBUGTRAP]) { - static char *av[4] = {"case", 0, "in" }; + char *av[4]; + av[0] = "case"; av[1] = r; - sh_debug(trap, (char*)0, (char*)0, av, 0); + av[2] = "in"; + av[3] = 0; + sh_debug(shp,trap, (char*)0, (char*)0, av, 0); } while(t) { @@ -1586,7 +1757,7 @@ int sh_exec(register const Shnode_t *t, int flags) register char *s; if(rex->argflag&ARG_MAC) { - s = sh_macpat(rex,OPTIMIZE|ARG_EXP); + s = sh_macpat(shp,rex,OPTIMIZE|ARG_EXP); while(*s=='\\' && s[1]==0) s+=2; } @@ -1626,7 +1797,7 @@ int sh_exec(register const Shnode_t *t, int flags) if(type!=TTIME) { sh_exec(t->par.partre,OPTIMIZE); - sh.exitval = !sh.exitval; + shp->exitval = !shp->exitval; break; } if(t->par.partre) @@ -1657,15 +1828,15 @@ int sh_exec(register const Shnode_t *t, int flags) #ifdef timeofday times(&after); timeofday(&ta); - at = sh.lim.clk_tck*(ta.tv_sec-tb.tv_sec); - at += ((sh.lim.clk_tck*(((1000000L/2)/sh.lim.clk_tck)+(ta.tv_usec-tb.tv_usec)))/1000000L); + at = shp->lim.clk_tck*(ta.tv_sec-tb.tv_sec); + at += ((shp->lim.clk_tck*(((1000000L/2)/shp->lim.clk_tck)+(ta.tv_usec-tb.tv_usec)))/1000000L); #else at = times(&after) - bt; #endif /* timeofday */ tm[0] = at; if(t->par.partre) { - Namval_t *np = nv_open("TIMEFORMAT",sh.var_tree,NV_NOADD); + Namval_t *np = nv_open("TIMEFORMAT",shp->var_tree,NV_NOADD); if(np) { format = nv_getval(np); @@ -1675,19 +1846,13 @@ int sh_exec(register const Shnode_t *t, int flags) format = e_timeformat; } else - { format = strchr(format+1,'\n')+1; -#if 0 - if(sh.optcount) - sfprintf(sfstderr,"%d optimizations\n",sh.optcount); -#endif - } tm[1] = after.tms_utime - before.tms_utime; tm[1] += after.tms_cutime - before.tms_cutime; tm[2] = after.tms_stime - before.tms_stime; tm[2] += after.tms_cstime - before.tms_cstime; if(format && *format) - p_time(sfstderr,sh_translate(format),tm); + p_time(shp,sfstderr,sh_translate(format),tm); break; } case TFUN: @@ -1701,90 +1866,94 @@ int sh_exec(register const Shnode_t *t, int flags) if(t->tre.tretyp==TNSPACE) { Dt_t *root,*oldroot, *top=0; - Namval_t *oldnspace = sh.namespace; - int offset = staktell(); - long optindex = sh.st.optindex; + Namval_t *oldnspace = shp->namespace; + int offset = stktell(stkp); + long optindex = shp->st.optindex; if(cp) errormsg(SH_DICT,ERROR_exit(1),e_ident,fname); - stakputc('.'); - stakputs(fname); - stakputc(0); - np = nv_open(stakptr(offset),sh.var_base,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME); - offset = staktell(); - sh.namespace = np; + sfputc(stkp,'.'); + sfputr(stkp,fname,0); + np = nv_open(stkptr(stkp,offset),shp->var_base,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME); + offset = stktell(stkp); + shp->namespace = np; if(!(root=nv_dict(np))) { root = dtopen(&_Nvdisc,Dtoset); nv_putval(np,(char*)root,NV_TABLE|NV_NOFREE); - sh.st.optindex = 1; + shp->st.optindex = 1; } - if(oldnspace && dtvnext(dtvnext(sh.var_tree))) - top = dtview(sh.var_tree,0); - else if(dtvnext(sh.var_tree)) - top = dtview(sh.var_tree,0); - oldroot = sh.var_tree; - dtview(root,sh.var_base); - sh.var_tree = root; + if(oldnspace && dtvnext(dtvnext(shp->var_tree))) + top = dtview(shp->var_tree,0); + else if(dtvnext(shp->var_tree)) + top = dtview(shp->var_tree,0); + oldroot = shp->var_tree; + dtview(root,shp->var_base); + shp->var_tree = root; if(top) - dtview(sh.var_tree,top); + dtview(shp->var_tree,top); sh_exec(t->for_.fortre,flags); - if(dtvnext(sh.var_tree)) - top = dtview(sh.var_tree,0); - sh.var_tree = oldroot; + if(dtvnext(shp->var_tree)) + top = dtview(shp->var_tree,0); + shp->var_tree = oldroot; if(top) - dtview(top,sh.var_tree); - sh.namespace = oldnspace; - sh.st.optindex = optindex; + dtview(top,shp->var_tree); + shp->namespace = oldnspace; + shp->st.optindex = optindex; break; } #endif /* SHOPT_NAMESPACE */ /* look for discipline functions */ - error_info.line = t->funct.functline-sh.st.firstline; + error_info.line = t->funct.functline-shp->st.firstline; /* Function names cannot be special builtin */ - if(cp || sh.prefix) + if(cp || shp->prefix) { - int offset = staktell(); - if(sh.prefix) + int offset = stktell(stkp); + if(shp->prefix) { - cp = sh.prefix; - sh.prefix = 0; - npv = nv_open(cp,sh.var_tree,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME); - sh.prefix = cp; + cp = shp->prefix; + shp->prefix = 0; + npv = nv_open(cp,shp->var_tree,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME); + shp->prefix = cp; cp = fname; } else { - stakwrite(fname,cp-fname); - stakputc(0); - npv = nv_open(stakptr(offset),sh.var_tree,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME); + sfwrite(stkp,fname,cp++-fname); + sfputc(stkp,0); + npv = nv_open(stkptr(stkp,offset),shp->var_tree,NV_NOASSIGN|NV_NOARRAY|NV_VARNAME); } - offset = staktell(); - stakputs(nv_name(npv)); - if(*cp!='.') - stakputc('.'); - stakputs(cp); - stakputc(0); - fname = stakptr(offset); + offset = stktell(stkp); + sfprintf(stkp,"%s.%s%c",nv_name(npv),cp,0); + fname = stkptr(stkp,offset); } - else if((np=nv_search(fname,sh.bltin_tree,0)) && nv_isattr(np,BLT_SPC)) + else if((np=nv_search(fname,shp->bltin_tree,0)) && nv_isattr(np,BLT_SPC)) errormsg(SH_DICT,ERROR_exit(1),e_badfun,fname); #if SHOPT_NAMESPACE - else if(sh.namespace) + else if(shp->namespace) { - int offset = staktell(); - stakputs(nv_name(sh.namespace)); - stakputc('.'); - stakputs(fname); - stakputc(0); - fname = stakptr(offset); + int offset = stktell(stkp); + sfputr(stkp,nv_name(shp->namespace),-1); + sfputc(stkp,'.'); + sfputr(stkp,fname,0); + fname = stkptr(stkp,offset); } #endif /* SHOPT_NAMESPACE */ np = nv_open(fname,sh_subfuntree(1),NV_NOASSIGN|NV_NOARRAY|NV_VARNAME|NV_NOSCOPE); if(npv) { - if(!sh.mktype) - cp = nv_setdisc(npv,cp+1,np,(Namfun_t*)npv); - nv_close(npv); + Namval_t *tp = npv; + if(!shp->mktype) + { + if(shp->typeinit) + { + if(tp=nv_open(shp->typeinit->nvname,shp->typedict,NV_IDENT|NV_NOFAIL)) + nv_close(npv); + else + tp = npv; + } + cp = nv_setdisc(tp,cp,np,(Namfun_t*)tp); + } + nv_close(tp); if(!cp) errormsg(SH_DICT,ERROR_exit(1),e_baddisc,fname); } @@ -1793,11 +1962,24 @@ int sh_exec(register const Shnode_t *t, int flags) slp = (struct slnod*)np->nvenv; sh_funstaks(slp->slchild,-1); stakdelete(slp->slptr); + if(shp->funload) + { + free((void*)np->nvalue.rp); + np->nvalue.rp = 0; + } + + } + if(!np->nvalue.rp) + { + np->nvalue.rp = new_of(struct Ufunction,shp->funload?sizeof(Dtlink_t):0); + memset((void*)np->nvalue.rp,0,sizeof(struct Ufunction)); } - else - np->nvalue.rp = new_of(struct Ufunction,0); if(t->funct.functstak) { + static Dtdisc_t _Rpdisc = + { + offsetof(struct Ufunction,fname), -1, sizeof(struct Ufunction) + }; struct functnod *fp; slp = t->funct.functstak; sh_funstaks(slp->slchild,1); @@ -1806,13 +1988,23 @@ int sh_exec(register const Shnode_t *t, int flags) nv_funtree(np) = (int*)(t->funct.functtre); np->nvalue.rp->hoffset = t->funct.functloc; np->nvalue.rp->lineno = t->funct.functline; - np->nvalue.rp->nspace = sh.namespace; + np->nvalue.rp->nspace = shp->namespace; np->nvalue.rp->fname = 0; + np->nvalue.rp->fdict = shp->fun_tree; fp = (struct functnod*)(slp+1); if(fp->functtyp==(TFUN|FAMP)) np->nvalue.rp->fname = fp->functnam; nv_setsize(np,fp->functline); nv_offattr(np,NV_FPOSIX); + if(shp->funload) + { + struct Ufunction *rp = np->nvalue.rp; + rp->np = np; + if(!shp->fpathdict) + shp->fpathdict = dtopen(&_Rpdisc,Dtbag); + if(shp->fpathdict) + dtinsert(shp->fpathdict,rp); + } } else nv_unset(np); @@ -1822,6 +2014,8 @@ int sh_exec(register const Shnode_t *t, int flags) nv_onattr(np,NV_FUNCTION); if(type&FPIN) nv_onattr(np,NV_FTMP); + if(type&FOPTGET) + nv_onattr(np,NV_OPTGET); break; } @@ -1833,12 +2027,12 @@ int sh_exec(register const Shnode_t *t, int flags) int negate = (type&TNEGATE)!=0; if(type&TTEST) skipexitset++; - error_info.line = t->tst.tstline-sh.st.firstline; + error_info.line = t->tst.tstline-shp->st.firstline; echeck = 1; if((type&TPAREN)==TPAREN) { sh_exec(t->lst.lstlef,OPTIMIZE); - n = !sh.exitval; + n = !shp->exitval; } else { @@ -1847,10 +2041,10 @@ int sh_exec(register const Shnode_t *t, int flags) register char *trap; char *argv[6]; n = type>>TSHIFT; - left = sh_macpat(&(t->lst.lstlef->arg),OPTIMIZE); + left = sh_macpat(shp,&(t->lst.lstlef->arg),OPTIMIZE); if(type&TBINARY) - right = sh_macpat(&(t->lst.lstrit->arg),((n==TEST_PEQ||n==TEST_PNE)?ARG_EXP:0)|OPTIMIZE); - if(trap=sh.st.trap[SH_DEBUGTRAP]) + right = sh_macpat(shp,&(t->lst.lstrit->arg),((n==TEST_PEQ||n==TEST_PNE)?ARG_EXP:0)|OPTIMIZE); + if(trap=shp->st.trap[SH_DEBUGTRAP]) argv[0] = (type&TNEGATE)?((char*)e_tstbegin):"[["; if(sh_isoption(SH_XTRACE)) { @@ -1871,7 +2065,7 @@ int sh_exec(register const Shnode_t *t, int flags) argv[2] = left; argv[3] = "]]"; argv[4] = 0; - sh_debug(trap,(char*)0,(char*)0,argv, 0); + sh_debug(shp,trap,(char*)0,(char*)0,argv, 0); } n = test_unop(n,left); } @@ -1891,7 +2085,7 @@ int sh_exec(register const Shnode_t *t, int flags) argv[3] = right; argv[4] = "]]"; argv[5] = 0; - sh_debug(trap,(char*)0,(char*)0,argv, pattern); + sh_debug(shp,trap,(char*)0,(char*)0,argv, pattern); } n = test_binop(n,left,right); if(traceon) @@ -1906,13 +2100,13 @@ int sh_exec(register const Shnode_t *t, int flags) if(traceon) sfwrite(sfstderr,e_tstend,4); } - sh.exitval = ((!n)^negate); + shp->exitval = ((!n)^negate); if(!skipexitset) exitset(); break; } } - if(sh.trapnote || (sh.exitval && sh_isstate(SH_ERREXIT)) && + if(shp->trapnote || (shp->exitval && sh_isstate(SH_ERREXIT)) && t && echeck) sh_chktrap(); /* set $_ */ @@ -1921,31 +2115,31 @@ int sh_exec(register const Shnode_t *t, int flags) /* store last argument here if it fits */ static char lastarg[32]; if(sh_isstate(SH_FORKED)) - sh_done(0); - if(sh.lastarg!= lastarg && sh.lastarg) - free(sh.lastarg); - if(strlen(com[argn-1]) < sizeof(lastarg)) + sh_done(shp,0); + if(shp->lastarg!= lastarg && shp->lastarg) + free(shp->lastarg); + if(strlen(comn) < sizeof(lastarg)) { nv_onattr(L_ARGNOD,NV_NOFREE); - sh.lastarg = strcpy(lastarg,com[argn-1]); + shp->lastarg = strcpy(lastarg,comn); } else { nv_offattr(L_ARGNOD,NV_NOFREE); - sh.lastarg = strdup(com[argn-1]); + shp->lastarg = strdup(comn); } } if(!skipexitset) exitset(); if(!(OPTIMIZE)) { - if(sav != stakptr(0)) - stakset(sav,0); - else if(staktell()) - stakseek(0); + if(sav != stkptr(stkp,0)) + stkset(stkp,sav,0); + else if(stktell(stkp)) + stkseek(stkp,0); } - if(sh.trapnote&SH_SIGSET) - sh_exit(SH_EXITSIG|sh.lastsig); + if(shp->trapnote&SH_SIGSET) + sh_exit(SH_EXITSIG|shp->lastsig); if(was_interactive) sh_onstate(SH_INTERACTIVE); if(was_monitor && sh_isoption(SH_MONITOR)) @@ -1953,7 +2147,35 @@ int sh_exec(register const Shnode_t *t, int flags) if(was_errexit) sh_onstate(SH_ERREXIT); } - return(sh.exitval); + return(shp->exitval); +} + +int sh_run(int argn, char *argv[]) +{ + register struct dolnod *dp; + register struct comnod *t = (struct comnod*)stakalloc(sizeof(struct comnod)); + int savtop = staktell(); + char *savptr = stakfreeze(0); + Opt_t *op, *np = optctx(0, 0); + Shbltin_t bltindata; + bltindata = sh.bltindata; + op = optctx(np, 0); + memset(t, 0, sizeof(struct comnod)); + dp = (struct dolnod*)stakalloc((unsigned)sizeof(struct dolnod) + ARG_SPARE*sizeof(char*) + argn*sizeof(char*)); + dp->dolnum = argn; + dp->dolbot = ARG_SPARE; + memcpy(dp->dolval+ARG_SPARE, argv, (argn+1)*sizeof(char*)); + t->comarg = (struct argnod*)dp; + if(!strchr(argv[0],'/')) + t->comnamp = (void*)nv_bfsearch(argv[0],sh.fun_tree,(Namval_t**)&t->comnamq,(char**)0); + argn=sh_exec((Shnode_t*)t,sh_isstate(SH_ERREXIT)); + optctx(op,np); + sh.bltindata = bltindata; + if(savptr!=stakptr(0)) + stakset(savptr,savtop); + else + stakseek(savtop); + return(argn); } /* @@ -1980,18 +2202,21 @@ static int trim_eq(register const char *r,register const char *s) int sh_trace(register char *argv[], register int nl) { + Shell_t *shp = &sh; register char *cp; register int bracket = 0; + int decl = (nl&2); + nl &= ~2; if(sh_isoption(SH_XTRACE)) { /* make this trace atomic */ sfset(sfstderr,SF_SHARE|SF_PUBLIC,0); - if(!(cp=nv_getval(nv_scoped(PS4NOD)))) + if(!(cp=nv_getval(sh_scoped(shp,PS4NOD)))) cp = "+ "; else { sh_offoption(SH_XTRACE); - cp = sh_mactry(cp); + cp = sh_mactry(shp,cp); sh_onoption(SH_XTRACE); } if(*cp) @@ -2010,12 +2235,12 @@ int sh_trace(register char *argv[], register int nl) { if(bracket==0 || *argv || *cp!=']') cp = sh_fmtq(cp); - if(sh.prefix && cp!=argv0 && *cp!='-') + if(decl && shp->prefix && cp!=argv0 && *cp!='-') { if(*cp=='.' && cp[1]==0) - cp = sh.prefix; + cp = shp->prefix; else - sfputr(sfstderr,sh.prefix,'.'); + sfputr(sfstderr,shp->prefix,'.'); } sfputr(sfstderr,cp,*argv?' ':nl); } @@ -2052,33 +2277,39 @@ static void timed_out(void *handle) pid_t _sh_fork(register pid_t parent,int flags,int *jobid) { static long forkcnt = 1000L; + Shell_t *shp = &sh; pid_t curpgid = job.curpgid; pid_t postid = (flags&FAMP)?0:curpgid; - int sig; + int sig,nochild; if(parent<0) { + sh_sigcheck(); if((forkcnt *= 2) > 1000L*SH_FORKLIM) { forkcnt=1000L; errormsg(SH_DICT,ERROR_system(ERROR_NOEXEC),e_nofork); } - sh_sigcheck(); timeout = (void*)sh_timeradd(forkcnt, 0, timed_out, NIL(void*)); - job_wait((pid_t)1); + nochild = job_wait((pid_t)1); if(timeout) { + if(nochild) + pause(); + else if(forkcnt>1000L) + forkcnt /= 2; timerdel(timeout); - forkcnt /= 2; + timeout = 0; } return(-1); } - forkcnt=1000L; + forkcnt = 1000L; if(parent) { - int myjob; - sh.nforks++; + int myjob,waitall=job.waitall; + shp->nforks++; if(job.toclear) job_clear(); + job.waitall = waitall; #ifdef JOBS /* first process defines process group */ if(sh_isstate(SH_MONITOR)) @@ -2099,7 +2330,7 @@ pid_t _sh_fork(register pid_t parent,int flags,int *jobid) if(!sh_isstate(SH_MONITOR) && job.waitall && postid==0) job.curpgid = parent; if(flags&FCOOP) - sh.cpid = parent; + shp->cpid = parent; myjob = job_post(parent,postid); if(flags&FAMP) job.curpgid = curpgid; @@ -2111,9 +2342,9 @@ pid_t _sh_fork(register pid_t parent,int flags,int *jobid) vmtrace(-1); #endif /* This is the child process */ - if(sh.trapnote&SH_SIGTERM) + if(shp->trapnote&SH_SIGTERM) sh_exit(SH_EXITSIG|SIGTERM); - sh.nforks=0; + shp->nforks=0; timerdel(NIL(void*)); #ifdef JOBS if(!job.jobcontrol && !(flags&FAMP)) @@ -2141,22 +2372,23 @@ pid_t _sh_fork(register pid_t parent,int flags,int *jobid) job.jobcontrol = 0; #endif /* JOBS */ job.toclear = 1; - sh.login_sh = 0; + shp->login_sh = 0; sh_offoption(SH_LOGIN_SHELL); sh_onstate(SH_FORKED); sh_onstate(SH_NOLOG); - sh.fn_depth = 0; + if (shp->fn_reset) + shp->fn_depth = shp->fn_reset = 0; #if SHOPT_ACCT sh_accsusp(); #endif /* SHOPT_ACCT */ /* Reset remaining signals to parent */ /* except for those `lost' by trap */ sh_sigreset(2); - sh.subshell = 0; - if((flags&FAMP) && sh.coutpipe>1) - sh_close(sh.coutpipe); - sig = sh.savesig; - sh.savesig = 0; + shp->subshell = 0; + if((flags&FAMP) && shp->coutpipe>1) + sh_close(shp->coutpipe); + sig = shp->savesig; + shp->savesig = 0; if(sig>0) sh_fault(sig); sh_sigcheck(); @@ -2185,6 +2417,7 @@ pid_t sh_fork(int flags, int *jobid) job_fork(-1); sh.savesig = -1; while(_sh_fork(parent=fork(),flags,jobid) < 0); + sh_stats(STAT_FORKS); sig = sh.savesig; sh.savesig = 0; if(sig>0) @@ -2213,43 +2446,47 @@ static void local_exports(register Namval_t *np, void *data) */ int sh_funscope(int argn, char *argv[],int(*fun)(void*),void *arg,int execflg) { - register char *trap; - register int nsig; - struct dolnod *argsav=0,*saveargfor; - struct sh_scoped savst, *prevscope = sh.st.self; - struct argnod *envlist=0; - Shopt_t savopt; - int jmpval; - int r = 0; - char *savstak; - struct funenv *fp; - struct checkpt buff; - Namval_t *nspace = sh.namespace; - savopt = sh.options; - sh.st.lineno = error_info.line; - *prevscope = sh.st; + register char *trap; + register int nsig; + register Shell_t *shp = &sh; + struct dolnod *argsav=0,*saveargfor; + struct sh_scoped savst, *prevscope = shp->st.self; + struct argnod *envlist=0; + int jmpval; + volatile int r = 0; + char *savstak; + struct funenv *fp; + struct checkpt buff; + Namval_t *nspace = shp->namespace; + if(shp->fn_depth==0) + shp->glob_options = shp->options; + else + shp->options = shp->glob_options; +#if 0 + shp->st.lineno = error_info.line; +#endif + *prevscope = shp->st; sh_offoption(SH_ERREXIT); - sh.st.prevst = prevscope; - sh.st.self = &savst; - sh.topscope = (Shscope_t*)sh.st.self; - sh.st.opterror = sh.st.optchar = 0; - sh.st.optindex = 1; - sh.st.loopcnt = 0; + shp->st.prevst = prevscope; + shp->st.self = &savst; + shp->topscope = (Shscope_t*)shp->st.self; + shp->st.opterror = shp->st.optchar = 0; + shp->st.optindex = 1; + shp->st.loopcnt = 0; if(!fun) { fp = (struct funenv*)arg; + shp->st.real_fun = (fp->node)->nvalue.rp; envlist = fp->env; } - prevscope->save_tree = sh.var_tree; - nv_scope(envlist); - if(dtvnext(prevscope->save_tree)!= (sh.namespace?sh.var_base:0)) + prevscope->save_tree = shp->var_tree; + sh_scope(shp,envlist,1); + if(dtvnext(prevscope->save_tree)!= (shp->namespace?shp->var_base:0)) { /* eliminate parent scope */ - Dt_t *dt = dtview(sh.var_tree,0); - dtview(sh.var_tree,dtvnext(prevscope->save_tree)); nv_scan(prevscope->save_tree, local_exports,(void*)0, NV_EXPORT, NV_EXPORT|NV_NOSCOPE); } - sh.st.save_tree = sh.var_tree; + shp->st.save_tree = shp->var_tree; if(!fun) { Namval_t *np; @@ -2258,104 +2495,117 @@ int sh_funscope(int argn, char *argv[],int(*fun)(void*),void *arg,int execflg) else sh_offoption(SH_XTRACE); #if SHOPT_NAMESPACE - if((np=(fp->node)->nvalue.rp->nspace) && np!=sh.namespace) + if((np=(fp->node)->nvalue.rp->nspace) && np!=shp->namespace) { - Dt_t *dt = sh.var_tree; + Dt_t *dt = shp->var_tree; dtview(dt,0); dtview(dt,nv_dict(np)); - sh.var_tree = nv_dict(np); - sh.namespace = np; + shp->var_tree = nv_dict(np); + shp->namespace = np; } #endif /* SHOPT_NAMESPACE */ } - sh.st.cmdname = argv[0]; + shp->st.cmdname = argv[0]; /* save trap table */ - if((nsig=sh.st.trapmax*sizeof(char*))>0 || sh.st.trapcom[0]) + if((nsig=shp->st.trapmax*sizeof(char*))>0 || shp->st.trapcom[0]) { nsig += sizeof(char*); - memcpy(savstak=stakalloc(nsig),(char*)&sh.st.trapcom[0],nsig); + memcpy(savstak=stakalloc(nsig),(char*)&shp->st.trapcom[0],nsig); } sh_sigreset(0); - argsav = sh_argnew(argv,&saveargfor); + argsav = sh_argnew(shp,argv,&saveargfor); sh_pushcontext(&buff,SH_JMPFUN); errorpush(&buff.err,0); error_info.id = argv[0]; - sh.st.var_local = sh.var_tree; + shp->st.var_local = shp->var_tree; jmpval = sigsetjmp(buff.buff,0); if(!fun) { - sh.st.filename = fp->node->nvalue.rp->fname; - nv_putval(SH_PATHNAMENOD, sh.st.filename ,NV_NOFREE); - nv_putval(SH_FUNNAMENOD,nv_name(fp->node),NV_NOFREE); + shp->st.filename = fp->node->nvalue.rp->fname; + shp->st.funname = nv_name(fp->node); + nv_putval(SH_PATHNAMENOD,shp->st.filename,NV_NOFREE); + nv_putval(SH_FUNNAMENOD,shp->st.funname,NV_NOFREE); } if(jmpval == 0) { - if(sh.fn_depth++ > MAXDEPTH) - siglongjmp(*sh.jmplist,SH_JMPERRFN); + if(shp->fn_depth++ > MAXDEPTH) + siglongjmp(*shp->jmplist,SH_JMPERRFN); else if(fun) r= (*fun)(arg); else { sh_exec((Shnode_t*)(nv_funtree((fp->node))),execflg|SH_ERREXIT); - r = sh.exitval; + r = shp->exitval; } } - if(--sh.fn_depth==1 && jmpval==SH_JMPERRFN) + if(--shp->fn_depth==1 && jmpval==SH_JMPERRFN) errormsg(SH_DICT,ERROR_exit(1),e_toodeep,argv[0]); sh_popcontext(&buff); - if (sh.st.self != &savst) - sh.var_tree = (Dt_t*)savst.save_tree; - nv_unscope(); - sh.namespace = nspace; - sh.var_tree = (Dt_t*)prevscope->save_tree; - sh_argreset(argsav,saveargfor); - trap = sh.st.trapcom[0]; - sh.st.trapcom[0] = 0; + if (shp->st.self != &savst) + shp->var_tree = (Dt_t*)savst.save_tree; + sh_unscope(shp); + shp->namespace = nspace; + shp->var_tree = (Dt_t*)prevscope->save_tree; + if(shp->topscope != (Shscope_t*)shp->st.self) + sh_setscope(shp->topscope); + sh_argreset(shp,argsav,saveargfor); + trap = shp->st.trapcom[0]; + shp->st.trapcom[0] = 0; sh_sigreset(1); - if (sh.st.self != &savst) - *sh.st.self = sh.st; - sh.st = *prevscope; - sh.topscope = (Shscope_t*)prevscope; - nv_getval(nv_scoped(IFSNOD)); + if (shp->st.self != &savst) + *shp->st.self = shp->st; + shp->st = *prevscope; + shp->topscope = (Shscope_t*)prevscope; + nv_getval(sh_scoped(shp,IFSNOD)); if(nsig) - memcpy((char*)&sh.st.trapcom[0],savstak,nsig); - sh.trapnote=0; + memcpy((char*)&shp->st.trapcom[0],savstak,nsig); + shp->trapnote=0; if(nsig) stakset(savstak,0); - sh.options = savopt; + shp->options = shp->glob_options; if(trap) { sh_trap(trap,0); free(trap); } - if(sh.exitval > SH_EXITSIG) - sh_fault(sh.exitval&SH_EXITMASK); + if(shp->exitval > SH_EXITSIG) + sh_fault(shp->exitval&SH_EXITMASK); if(jmpval > SH_JMPFUN) { sh_chktrap(); - siglongjmp(*sh.jmplist,jmpval); + siglongjmp(*shp->jmplist,jmpval); } return(r); } - -static void sh_funct(Namval_t *np,int argn, char *argv[],struct argnod *envlist,int execflg) +static void sh_funct(Shell_t *shp,Namval_t *np,int argn, char *argv[],struct argnod *envlist,int execflg) { struct funenv fun; char *fname = nv_getval(SH_FUNNAMENOD); + struct Level *lp =(struct Level*)(SH_LEVELNOD->nvfun); + int level; + sh_stats(STAT_FUNCT); + if(!lp->hdr.disc) + lp = init_level(0); + if((struct sh_scoped*)shp->topscope != shp->st.self) + sh_setscope(shp->topscope); + level = lp->maxlevel = shp->dot_depth + shp->fn_depth+1; + SH_LEVELNOD->nvalue.s = lp->maxlevel; + shp->st.lineno = error_info.line; if(nv_isattr(np,NV_FPOSIX)) { char *save; - int loopcnt = sh.st.loopcnt; - sh.posix_fun = np; - opt_info.index = opt_info.offset = 0; - error_info.errors = 0; + int loopcnt = shp->st.loopcnt; + shp->posix_fun = np; save = argv[-1]; argv[-1] = 0; + shp->st.funname = nv_name(np); nv_putval(SH_FUNNAMENOD, nv_name(np),NV_NOFREE); - sh.st.loopcnt = 0; - b_dot_cmd(argn+1,argv-1,&sh); - sh.st.loopcnt = loopcnt; + opt_info.index = opt_info.offset = 0; + error_info.errors = 0; + shp->st.loopcnt = 0; + b_dot_cmd(argn+1,argv-1,&shp->bltindata); + shp->st.loopcnt = loopcnt; argv[-1] = save; } else @@ -2364,8 +2614,19 @@ static void sh_funct(Namval_t *np,int argn, char *argv[],struct argnod *envlist, fun.node = np; sh_funscope(argn,argv,0,&fun,execflg); } + if(level-- != nv_getnum(SH_LEVELNOD)) + { + Shscope_t *sp = sh_getscope(0,SEEK_END); + sh_setscope(sp); + } + lp->maxlevel = level; + SH_LEVELNOD->nvalue.s = lp->maxlevel; +#if 0 + nv_putval(SH_FUNNAMENOD,shp->st.funname,NV_NOFREE); +#else nv_putval(SH_FUNNAMENOD,fname,NV_NOFREE); - nv_putval(SH_PATHNAMENOD, sh.st.filename ,0); +#endif + nv_putval(SH_PATHNAMENOD,shp->st.filename,NV_NOFREE); } /* @@ -2375,15 +2636,20 @@ static void sh_funct(Namval_t *np,int argn, char *argv[],struct argnod *envlist, */ int sh_fun(Namval_t *np, Namval_t *nq, char *argv[]) { + Shell_t *shp = &sh; register int offset; register char *base; Namval_t node; + struct Namref nr; + long mode; + char *prefix = shp->prefix; int n=0; char *av[2]; Fcin_t save; fcsave(&save); if((offset=staktell())>0) base=stakfreeze(0); + shp->prefix = 0; if(!argv) { argv = av; @@ -2393,62 +2659,37 @@ int sh_fun(Namval_t *np, Namval_t *nq, char *argv[]) while(argv[n]) n++; if(nq) - { - /* - * set ${.sh.name} and ${.sh.subscript} - * set _ to reference for ${.sh.name}[$.sh.subscript] - */ - struct Namref nr; - memset(&nr,0,sizeof(nr)); - nr.np = nq; - nv_putval(SH_NAMENOD, nv_name(nq), NV_NOFREE); - memcpy(&node,L_ARGNOD,sizeof(node)); - L_ARGNOD->nvalue.nrp = &nr; - L_ARGNOD->nvenv = 0; - L_ARGNOD->nvfun = (Namfun_t*)sh.last_table; - L_ARGNOD->nvflag = NV_REF|NV_NOFREE; - if(nv_arrayptr(nq)) - { - nv_putval(SH_SUBSCRNOD,nv_getsub(nq),NV_NOFREE); - L_ARGNOD->nvenv = (char*)SH_SUBSCRNOD->nvalue.cp; - } - } + mode = set_instance(nq,&node, &nr); if(is_abuiltin(np)) { int jmpval; struct checkpt buff; + Shbltin_t *bp = &sh.bltindata; sh_pushcontext(&buff,SH_JMPCMD); jmpval = sigsetjmp(buff.buff,1); if(jmpval == 0) { - void *context = nv_context(np); + bp->bnode = np; + bp->ptr = nv_context(np); errorpush(&buff.err,0); error_info.id = argv[0]; opt_info.index = opt_info.offset = 0; opt_info.disc = 0; sh.exitval = 0; - if(!context) - context = (void*)&sh; - sh.exitval = (*funptr(np))(n,argv,context); + sh.exitval = (*funptr(np))(n,argv,(void*)bp); } sh_popcontext(&buff); if(jmpval>SH_JMPCMD) siglongjmp(*sh.jmplist,jmpval); } else - sh_funct(np,n,argv,(struct argnod*)0,sh_isstate(SH_ERREXIT)); + sh_funct(shp,np,n,argv,(struct argnod*)0,sh_isstate(SH_ERREXIT)); if(nq) - { - L_ARGNOD->nvalue.np = node.nvalue.np; - L_ARGNOD->nvenv = node.nvenv; - L_ARGNOD->nvflag = node.nvflag; - L_ARGNOD->nvfun = node.nvfun; - nv_unset(SH_NAMENOD); - nv_unset(SH_SUBSCRNOD); - } + unset_instance(nq, &node, &nr, mode); fcrestore(&save); if(offset>0) stakset(base,offset); + shp->prefix = prefix; return(sh.exitval); } @@ -2469,41 +2710,41 @@ int cmdrecurse(int argc, char* argv[], int ac, char* av[]) /* * set up pipe for cooperating process */ -static void coproc_init(int pipes[]) +static void coproc_init(Shell_t *shp, int pipes[]) { int outfd; - if(sh.coutpipe>=0 && sh.cpid) + if(shp->coutpipe>=0 && shp->cpid) errormsg(SH_DICT,ERROR_exit(1),e_pexists); - sh.cpid = 0; - if(sh.cpipe[0]<=0 || sh.cpipe[1]<=0) + shp->cpid = 0; + if(shp->cpipe[0]<=0 || shp->cpipe[1]<=0) { /* first co-process */ - sh_pclose(sh.cpipe); - sh_pipe(sh.cpipe); - if((outfd=sh.cpipe[1]) < 10) + sh_pclose(shp->cpipe); + sh_pipe(shp->cpipe); + if((outfd=shp->cpipe[1]) < 10) { - int fd=fcntl(sh.cpipe[1],F_DUPFD,10); + int fd=fcntl(shp->cpipe[1],F_DUPFD,10); if(fd>=10) { - sh.fdstatus[fd] = (sh.fdstatus[outfd]&~IOCLEX); + shp->fdstatus[fd] = (shp->fdstatus[outfd]&~IOCLEX); close(outfd); - sh.fdstatus[outfd] = IOCLOSE; - sh.cpipe[1] = fd; + shp->fdstatus[outfd] = IOCLOSE; + shp->cpipe[1] = fd; } } - if(fcntl(*sh.cpipe,F_SETFD,FD_CLOEXEC)>=0) - sh.fdstatus[sh.cpipe[0]] |= IOCLEX; - sh.fdptrs[sh.cpipe[0]] = sh.cpipe; + if(fcntl(*shp->cpipe,F_SETFD,FD_CLOEXEC)>=0) + shp->fdstatus[shp->cpipe[0]] |= IOCLEX; + shp->fdptrs[shp->cpipe[0]] = shp->cpipe; - if(fcntl(sh.cpipe[1],F_SETFD,FD_CLOEXEC) >=0) - sh.fdstatus[sh.cpipe[1]] |= IOCLEX; + if(fcntl(shp->cpipe[1],F_SETFD,FD_CLOEXEC) >=0) + shp->fdstatus[shp->cpipe[1]] |= IOCLEX; } - sh.outpipe = sh.cpipe; - sh_pipe(sh.inpipe=pipes); - sh.coutpipe = sh.inpipe[1]; - sh.fdptrs[sh.coutpipe] = &sh.coutpipe; - if(fcntl(sh.outpipe[0],F_SETFD,FD_CLOEXEC)>=0) - sh.fdstatus[sh.outpipe[0]] |= IOCLEX; + shp->outpipe = shp->cpipe; + sh_pipe(shp->inpipe=pipes); + shp->coutpipe = shp->inpipe[1]; + shp->fdptrs[shp->coutpipe] = &shp->coutpipe; + if(fcntl(shp->outpipe[0],F_SETFD,FD_CLOEXEC)>=0) + shp->fdstatus[shp->outpipe[0]] |= IOCLEX; } #if SHOPT_SPAWN @@ -2533,7 +2774,7 @@ static void print_fun(register Namval_t* np, void *data) */ static int run_subshell(const Shnode_t *t,pid_t grp) { - static char prolog[] = "(print $(typeset +A);set; typeset -p; print .sh.dollar=$$;set +o)"; + static const char prolog[] = "(print $(typeset +A);set; typeset -p; print .sh.dollar=$$;set +o)"; register int i, fd, trace = sh_isoption(SH_XTRACE); int pin,pout; pid_t pid; @@ -2618,18 +2859,17 @@ static void sigreset(int mode) /* * A combined fork/exec for systems with slow or non-existent fork() */ -static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag) +static pid_t sh_ntfork(Shell_t *shp,const Shnode_t *t,char *argv[],int *jobid,int flag) { static pid_t spawnpid; static int savetype; static int savejobid; - Shell_t *shp = sh_getinterp(); - struct checkpt buff; - int otype=0, scope=0, jmpval; - int jobwasset=0, sigwasset=0; - char **arge, *path; - pid_t grp = 0; - Pathcomp_t *pp; + struct checkpt buff; + int otype=0, jmpval; + volatile int jobwasset=0, scope=0, sigwasset=0; + char **arge, *path; + volatile pid_t grp = 0; + Pathcomp_t *pp; if(flag) { otype = savetype; @@ -2679,28 +2919,28 @@ static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag) signal(SIGINT,SIG_IGN); if(!shp->st.ioset) { - sh_iosave(0,buff.topfd); - sh_iorenumber(sh_chkopen(e_devnull),0); + sh_iosave(shp,0,buff.topfd,(char*)0); + sh_iorenumber(shp,sh_chkopen(e_devnull),0); } } if(otype&FPIN) { int fd = shp->inpipe[1]; - sh_iosave(0,buff.topfd); - sh_iorenumber(shp->inpipe[0],0); + sh_iosave(shp,0,buff.topfd,(char*)0); + sh_iorenumber(shp,shp->inpipe[0],0); if(fd>=0 && (!(otype&FPOU) || (otype&FCOOP)) && fcntl(fd,F_SETFD,FD_CLOEXEC)>=0) shp->fdstatus[fd] |= IOCLEX; } if(otype&FPOU) { - sh_iosave(1,buff.topfd); - sh_iorenumber(sh_dup(shp->outpipe[1]),1); + sh_iosave(shp,1,buff.topfd,(char*)0); + sh_iorenumber(shp,sh_dup(shp->outpipe[1]),1); if(fcntl(shp->outpipe[0],F_SETFD,FD_CLOEXEC)>=0) shp->fdstatus[shp->outpipe[0]] |= IOCLEX; } if(t->fork.forkio) - sh_redirect(t->fork.forkio,0); + sh_redirect(shp,t->fork.forkio,0); if(optimize==0) { #ifdef SIGTSTP @@ -2737,7 +2977,7 @@ static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag) if((otype&FPIN) && (!(otype&FPOU) || (otype&FCOOP)) && fcntl(shp->inpipe[1],F_SETFD,FD_CLOEXEC)>=0) shp->fdstatus[shp->inpipe[1]] &= ~IOCLEX; if(t->fork.forkio || otype) - sh_iorestore(buff.topfd,jmpval); + sh_iorestore(shp,buff.topfd,jmpval); if(optimize==0) { #ifdef SIGTSTP @@ -2781,49 +3021,45 @@ static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag) } spawnpid = -1; if(t->com.comio) - sh_redirect(t->com.comio,0); + sh_redirect(shp,t->com.comio,0); error_info.id = *argv; if(t->com.comset) { scope++; - nv_scope(t->com.comset); + sh_scope(shp,t->com.comset,0); } if(!strchr(path=argv[0],'/')) { -#ifdef PATH_BFPATH Namval_t *np; if((np=nv_search(path,shp->track_tree,0)) && !nv_isattr(np,NV_NOALIAS) && np->nvalue.cp) path = nv_getval(np); else if(path_absolute(path,NIL(Pathcomp_t*))) { - path = stakptr(PATH_OFFSET); - stakfreeze(0); - } - else - { - pp=path_get(path); - while(pp) - { - if(pp->len==1 && *pp->name=='.') - break; - pp = pp->next; - } - if(!pp) - path = 0; - } -#else - path = shp->lastpath; -#endif + path = stkptr(shp->stk,PATH_OFFSET); + stkfreeze(shp->stk,0); } - else if(sh_isoption(SH_RESTRICTED)) - errormsg(SH_DICT,ERROR_exit(1),e_restricted,path); - if(!path) + else { - spawnpid = -1; - goto fail; + pp=path_get(path); + while(pp) + { + if(pp->len==1 && *pp->name=='.') + break; + pp = pp->next; + } + if(!pp) + path = 0; } - arge = sh_envgen(); - shp->exitval = 0; + } + else if(sh_isoption(SH_RESTRICTED)) + errormsg(SH_DICT,ERROR_exit(1),e_restricted,path); + if(!path) + { + spawnpid = -1; + goto fail; + } + arge = sh_envgen(); + shp->exitval = 0; #ifdef SIGTSTP if(job.jobcontrol) { @@ -2893,12 +3129,12 @@ static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag) sigreset(1); /* restore ignored signals */ if(scope) { - nv_unscope(); + sh_unscope(shp); if(jmpval==SH_JMPSCRIPT) nv_setlist(t->com.comset,NV_EXPORT|NV_IDENT|NV_ASSIGN); } if(t->com.comio) - sh_iorestore(buff.topfd,jmpval); + sh_iorestore(shp,buff.topfd,jmpval); if(jmpval>SH_JMPCMD) siglongjmp(*shp->jmplist,jmpval); if(spawnpid>0) @@ -2933,15 +3169,3 @@ static pid_t sh_ntfork(const Shnode_t *t,char *argv[],int *jobid,int flag) } # endif /* _lib_fork */ #endif /* SHOPT_SPAWN */ - -/* - * override procrun() since it is used in libcmd - */ -#include <proc.h> -int procrun(const char *path, char *argv[]) -{ - if(sh.subshell) - sh_subtmpfile(); - return(procclose(procopen(path, argv, NiL, NiL, PROC_FOREGROUND|PROC_GID -|PROC_UID))); -} diff --git a/usr/src/lib/libshell/common/shell.3 b/usr/src/lib/libshell/common/shell.3 index e6fbbf026e..7d7394bbc4 100644 --- a/usr/src/lib/libshell/common/shell.3 +++ b/usr/src/lib/libshell/common/shell.3 @@ -20,6 +20,7 @@ libshell.a -lshell Shell_t; Shopt_t; Shscope_t; +Shbltin_t; Shbltin_f; Shinit_f; Shwait_f; @@ -29,7 +30,7 @@ Shwait_f; .nf .ft 5 int sh_main(int \fIargc\fP, char *\fIargv\fP[], Sh_init \fIfn\fP); -Shell_t *sh_init(int \fIargc\fP, char *\fIargv\fP); +Shell_t *sh_init(int \fIargc\fP, char *\fIargv\fP[]); Shell_t *sh_getinterp(void); Namval_t *sh_addbuiltin(const char *\fIname\fP,Sh_bltin_f \fIfn\fP,void *\fIarg\fP); @@ -40,6 +41,7 @@ unsigned int sh_offoption(int \fIoption\fP); void *sh_parse(Shell_t *\fIshp\fP, Sfio_t *\fIsp\fP, int \fIflags\fP); int sh_trap(const char *\fIstring\fP, int \fImode\fP); +int sh_run(int \fIargc\fP, char *\fIargv\fP[]); int sh_eval(Sfio_t *\fIsp\fP,int \fImode\fP); int sh_fun(Namval_t *\fIfunnode\fP, Namval_t *\fIvarnode\fP, char *\fIargv\fP[]); int sh_funscope(int \fIargc\fP,char *\fIargv\fP[],int(*\fIfn\fP)(void*),void *\fIarg\fP,int \fIflags\fP); @@ -103,7 +105,10 @@ into the shell by loading dynamic libraries at run time using the \f5builtin\fP(1) command. In this case the shell will look for a function named \f5lib_init\fP in your library and, if found, will execute this function with -argument \f50\fP when the library is loaded. +two arguments. The first +argument will be an \f5int\P with value \f50\fP when the library is loaded. +The second argument will contain a pointer to a structure of type +\f5Shbltin_t\fP. In addition, for each argument named on the \f5builtin\fP command line, it will look for a function named \f5b_\fP\fIname\fP\f5()\fP in your library and will \fIname\fP as a built-in. @@ -157,17 +162,20 @@ All built-in commands to the shell are invoked with three arguments. The first two arguments give the number of arguments and the argument list and uses the same conventions as the \f5main()\fP function -of a program. The third argument is a pointer that +of a program. The third argument is a pointer to a structure +of type \f5Shbltin_t\fP. This structure contains \f5shp\P which is a pointer +to the shell interpreter, and \f5ptr\fP which is a pointer that can be associated with each built-in. The \f5sh_addbuiltin()\fP function is used to add, replace or delete built-in commands. It takes the name of the built-in, \fIname\fP, a pointer to the function that implements the built-in, \fIfn\fP, and -a pointer that will be passed to the function when +a pointer that will be passed to the function in the \f5ptr\fP field when it is invoked. If, \fIfn\fP is non-\f5NULL\fP the built-in command -is added or replaced. Otherwise, the given -built-in command will be deleted. +is added or replaced. Otherwise, \f5sh_addbuiltin()\fP will +return a pointer to the built-in if it exists or \f5NULL\fP otherwise. +If \fIarg\fP is \f5(void*)1\fP the built-in will be deleted. The \fIname\fP argument can be in the format of a pathname. It cannot be the name of any of the special built-in commands. If \fIname\fP contains a \f5/\fP, the built-in is the basename of @@ -268,6 +276,12 @@ is compiled and then executed so that aliases defined within the string or file will not take effect until the next command is executed. .PP +The \f5sh_run()\fP function will run the command given by +by the argument list \fIargv\fP containing \fIargc\fP elements. +If \fIargv\fP\f5[0]\fP does not contain a \f5/\fP, it will +be checked to see if it is a built-in or function before +performing a path search. +.PP The \f5sh_eval()\fP function executes a string or file stream \fIsp\fP. If \fImode\fP is non-zero and the history file has diff --git a/usr/src/lib/libshell/common/tests/alias.sh b/usr/src/lib/libshell/common/tests/alias.sh index bf7f17f5f6..e001668ef7 100644 --- a/usr/src/lib/libshell/common/tests/alias.sh +++ b/usr/src/lib/libshell/common/tests/alias.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -78,6 +78,20 @@ builtin -d rm 2> /dev/null if whence rm > /dev/null then [[ ! $(alias -t | grep rm= ) ]] && err_exit 'tracked alias not set' PATH=$PATH - [[ $(alias -t | grep rm= ) ]] && err_exit 'tracked alias not cleared' + [[ $(alias -t | grep rm= ) ]] && err_exit 'tracked alias not cleared' fi +if hash -r 2>/dev/null && [[ ! $(hash) ]] +then mkdir /tmp/ksh$$ || err_exit "mkdir /tmp/ksh$$ failed" + trap "cd /; rm -rf /tmp/ksh$$" EXIT + PATH=/tmp/ksh$$:/bin:/usr/bin + for i in foo -foo -- + do print ':' > /tmp/ksh$$/$i + chmod +x /tmp/ksh$$/$i + hash -r -- $i 2>/dev/null || err_exit "hash -r -- $i failed" + [[ $(hash) == $i=/tmp/ksh$$/$i ]] || err_exit "hash -r -- $i failed, expected $i=/tmp/ksh$$/$i, got $(hash)" + done +else err_exit 'hash -r failed' +fi +( alias :pr=print) 2> /dev/null || err_exit 'alias beginning with : fails' +( alias p:r=print) 2> /dev/null || err_exit 'alias with : in name fails' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/append.sh b/usr/src/lib/libshell/common/tests/append.sh index 7f3cad6021..00c432520a 100644 --- a/usr/src/lib/libshell/common/tests/append.sh +++ b/usr/src/lib/libshell/common/tests/append.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -63,10 +63,18 @@ point+=( y=3 z=4) if [[ ${point.y} != 3 ]] then err_exit 'compound append fails' fi +if [[ ${point.x} != 1 ]] +then err_exit 'compound append to compound variable unsets existing variables' +fi unset foo foo=one foo+=(two) if [[ ${foo[@]} != 'one two' ]] then err_exit 'array append to non array variable fails' fi +unset foo +foo[0]=(x=3) +foo+=(x=4) +[[ ${foo[1].x} == 4 ]] || err_exit 'compound append to index array not working' +[[ ${foo[0].x} == 3 ]] || err_exit 'compound append to index array unsets existing variables' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/arith.sh b/usr/src/lib/libshell/common/tests/arith.sh index ec3ada5df1..c330abce90 100644 --- a/usr/src/lib/libshell/common/tests/arith.sh +++ b/usr/src/lib/libshell/common/tests/arith.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -308,7 +308,6 @@ if [[ $x != 42#18 ]] then err_exit 'display of unsigned integers in non-decimal bases wrong' fi $SHELL -c 'i=0;(( ofiles[i] != -1 && (ofiles[i] < mins || mins == -1) ));exit 0' 2> /dev/null || err_exit 'lexical error with arithemtic expression' -rm -f core $SHELL -c '(( +1 == 1))' 2> /dev/null || err_exit 'unary + not working' typeset -E20 val=123.01234567890 [[ $val == 123.0123456789 ]] || err_exit "rounding error val=$val" @@ -328,7 +327,7 @@ do let "x = $x+1" done (( x == n )) || err_exit 'let with zero filled fields not working' (( y == n )) || err_exit '((...)) with zero filled fields not working' -typeset -LZ3 x=10 +typeset -RZ3 x=10 [[ $(($x)) == 10 && $((1$x)) == 1010 ]] || err_exit 'zero filled fields not preserving leading zeros' unset y [[ $(let y=$x;print $y) == 10 && $(let y=1$x;print $y) == 1010 ]] || err_exit 'zero filled fields not preserving leading zeros with let' @@ -357,7 +356,7 @@ typeset -i i=x (( ${x:0:1} == 0 )) || err_exit 'leading zero should not be stripped for x:a:b' c010=3 (( c$x == 3 )) || err_exit 'leading zero with variable should not be stripped' -[[ $( ($SHELL -c '((++1))' 2>&1)2>/dev/null ) == *lvalue* ]] || err_exit "((--1)) not generating error message" +[[ $( ($SHELL -c '((++1))' 2>&1)2>/dev/null ) == *lvalue* ]] || err_exit "((++1)) not generating error message" i=2 (( "22" == 22 )) || err_exit "double quoted constants fail" (( "2$i" == 22 )) || err_exit "double quoted variables fail" @@ -442,25 +441,46 @@ unset x [[ $x == "$((x))" ]] || err_exit '$x !- $((x)) when x is pi' $SHELL -c "[[ ${x//./} == {14,100}(\d) ]]" 2> /dev/null || err_exit 'pi has less than 14 significant places' if (( Inf+1 == Inf )) -then [[ $(printf "%g\n" $((Inf))) == inf ]] || err_exit 'printf "%g\n" $((Inf)) fails' -# [[ $(printf "%g\n" $((Nan))) == inf ]] || err_exit 'printf "%g\n" $((Nan)) fails' - [[ $(printf "%g\n" Inf) == inf ]] || err_exit 'printf "%g\n" Inf fails' - [[ $(printf "%g\n" NaN) == nan ]] || err_exit 'printf "%g\n" NaN fails' - [[ $(print -- $((Inf))) == inf ]] || err_exit 'print -- $((Inf)) fails' +then set \ + Inf inf \ + -Inf -inf \ + Nan nan \ + -Nan -nan \ + 1.0/0.0 inf + while (( $# >= 2 )) + do x=$(printf "%g\n" $(($1))) + [[ $x == $2 ]] || err_exit "printf '%g\\n' \$(($1)) failed -- expected $2, got $x" + x=$(printf "%g\n" $1) + [[ $x == $2 ]] || err_exit "printf '%g\\n' $1 failed -- expected $2, got $x" + x=$(printf -- $(($1))) + [[ $x == $2 ]] || err_exit "print -- \$(($1)) failed -- expected $2, got $x" + shift 2 + done (( 1.0/0.0 == Inf )) || err_exit '1.0/0.0 != Inf' - [[ $(print -- $((0.0/0.0))) == nan ]] || err_exit '0.0/0.0 != NaN' + [[ $(print -- $((0.0/0.0))) == ?(-)nan ]] || err_exit '0.0/0.0 != NaN' (( Inf*Inf == Inf )) || err_exit 'Inf*Inf != Inf' (( NaN != NaN )) || err_exit 'NaN == NaN' (( -5*Inf == -Inf )) || err_exit '-5*Inf != -Inf' - [[ $(print -- $((sqrt(-1.0)))) == nan ]]|| err_exit 'sqrt(-1.0) != NaN' + [[ $(print -- $((sqrt(-1.0)))) == ?(-)nan ]]|| err_exit 'sqrt(-1.0) != NaN' (( pow(1.0,Inf) == 1.0 )) || err_exit 'pow(1.0,Inf) != 1.0' (( pow(Inf,0.0) == 1.0 )) || err_exit 'pow(Inf,0.0) != 1.0' - [[ $(print -- $((NaN/Inf))) == nan ]] || err_exit 'NaN/Inf != NaN' + [[ $(print -- $((NaN/Inf))) == ?(-)nan ]] || err_exit 'NaN/Inf != NaN' (( 4.0/Inf == 0.0 )) || err_exit '4.0/Inf != 0.0' else err_exit 'Inf and NaN not working' fi -unset x y -float x=14.555 y +unset x y n r +n=14.555 +float x=$n y y=$(printf "%a" x) -(( x == y )) || err_exit "output of printf %a not self preserving -- expected $x, got $y" +r=$y +[[ $r == $n ]] || err_exit "output of printf %a not self preserving -- expected $x, got $y" +unset x y r +x=-0 +y=$(printf "%g %g %g %g %g %g\n" -0. -0 $((-0)) x $x $((x))) +r="-0 -0 -0 -0 -0 -0" +[[ $y == "$r" ]] || err_exit "-0 vs -0.0 inconsistency -- expected '$r', got '$y'" +$SHELL -c '(( x=));:' 2> /dev/null && err_exit '((x=)) should be an error' +$SHELL -c '(( x+=));:' 2> /dev/null && err_exit '((x+=)) should be an error' +$SHELL -c '(( x=+));:' 2> /dev/null && err_exit '((x=+)) should be an error' +$SHELL -c 'x=();x.arr[0]=(z=3); ((x.arr[0].z=2))' 2> /dev/null || err_exit '(((x.arr[0].z=2)) should not be an error' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/arrays.sh b/usr/src/lib/libshell/common/tests/arrays.sh index 7dd09fca0f..14504edc00 100644 --- a/usr/src/lib/libshell/common/tests/arrays.sh +++ b/usr/src/lib/libshell/common/tests/arrays.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -155,7 +155,6 @@ fi if (( s[$y] != 4 )) then err_exit '(( s[$y] != 4 ))' fi -unset y set -A y 2 4 6 typeset -i y z=${y[@]} @@ -378,4 +377,91 @@ set -- "${foo[@]:1}" unset bar : ${_foo[bar=4]} (( bar == 4 )) || err_exit 'subscript of unset variable not evaluated' +unset foo bar +foo[5]=4 +bar[4]=3 +bar[0]=foo +foo[0]=bam +foo[4]=5 +[[ ${!foo[2+2]} == 'foo[4]' ]] || err_exit '${!var[sub]} should be var[sub]' +[[ ${bar[${foo[5]}]} == 3 ]] || err_exit 'array subscript cannot be an array instance' +[[ $bar[4] == 3 ]] || err_exit '$bar[x] != ${bar[x]} inside [[ ]]' +(( $bar[4] == 3 )) || err_exit '$bar[x] != ${bar[x]} inside (( ))' +[[ $bar[$foo[5]] == 3 ]] || err_exit '$bar[foo[x]] != ${bar[foo[x]]} inside [[ ]]' +(( $bar[$foo[5]] == 3 )) || err_exit '$bar[foo[x]] != ${bar[foo[x]]} inside (( ))' +x=$bar[4] +[[ $x == 4 ]] && err_exit '$bar[4] should not be an array in an assignment' +x=${bar[$foo[5]]} +(( $x == 3 )) || err_exit '${bar[$foo[sub]]} not working' +[[ $($SHELL <<- \++EOF+++ + typeset -i test_variable=0 + typeset -A test_array + test_array[1]=100 + read test_array[2] <<-! + 2 + ! + read test_array[3] <<-! + 3 + ! + test_array[3]=4 + print "val=${test_array[3]}" +++EOF+++ +) == val=4 ]] 2> /dev/null || err_exit 'after reading array[j] and assign array[j] fails' +[[ $($SHELL <<- \+++EOF+++ + pastebin=( typeset -a form) + pastebin.form+=( name="name" data="clueless" ) + print -r -- ${pastebin.form[0].name} ++++EOF+++ +) == name ]] 2> /dev/null || err_exit 'indexed array in compound variable not working' +unset foo bar +: ${foo[bar=2]} +[[ $bar == 2 ]] || err_exit 'subscript not evaluated for unset variable' +unset foo bar +bar=1 +typeset -a foo=([1]=ok [2]=no) +[[ $foo[bar] == ok ]] || err_exit 'typeset -a not working for simple assignment' +unset foo +typeset -a foo=([1]=(x=ok) [2]=(x=no)) +[[ $(typeset | grep 'foo$') == *index* ]] || err_exit 'typeset -a not creating an indexed array' +foo+=([5]=good) +[[ $(typeset | grep 'foo$') == *index* ]] || err_exit 'append to indexed array not preserving array type' +unset foo +typeset -A foo=([1]=ok [2]=no) +[[ $foo[bar] == ok ]] && err_exit 'typeset -A not working for simple assignment' +unset foo +typeset -A foo=([1]=(x=ok) [2]=(x=no)) +[[ ${foo[bar].x} == ok ]] && err_exit 'typeset -A not working for compound assignment' +[[ $($SHELL -c 'typeset -a foo;typeset | grep "foo$"' 2> /dev/null) == *index* ]] || err_exit 'typeset fails for indexed array with no elements' +xxxxx=(one) +[[ $(typeset | grep xxxxx$) == *'indexed array'* ]] || err_exit 'array of one element not an indexed array' +unset foo +foo[1]=(x=3 y=4) +{ [[ ${!foo[1].*} == 'foo[1].x foo[1].y' ]] ;} 2> /dev/null || err_exit '${!foo[sub].*} not expanding correctly' +unset x +x=( typeset -a foo=( [0]="a" [1]="b" [2]="c" )) +[[ ${@x.foo} == 'typeset -a'* ]] || err_exit 'x.foo is not an indexed array' +x=( typeset -A foo=( [0]="a" [1]="b" [2]="c" )) +[[ ${@x.foo} == 'typeset -A'* ]] || err_exit 'x.foo is not an associative array' +$SHELL -c $'x=(foo\n\tbar\nbam\n)' 2> /dev/null || err_exit 'compound array assignment with new-lines not working' +$SHELL -c $'x=(foo\n\tbar:\nbam\n)' 2> /dev/null || err_exit 'compound array assignment with labels not working' +$SHELL -c $'x=(foo\n\tdone\nbam\n)' 2> /dev/null || err_exit 'compound array assignment with reserved words not working' +[[ $($SHELL -c 'typeset -A A; print $(( A[foo].bar ))' 2> /dev/null) == 0 ]] || err_exit 'unset variable not evaluating to 0' +unset a +typeset -A a +a[a].z=1 +a[z].z=2 +unset a[a] +[[ ${!a[@]} == z ]] || err_exit '"unset a[a]" unsets entire array' +unset a +a=([x]=1 [y]=2 [z]=(foo=3 bar=4)) +eval "b=$(printf "%B\n" a)" +eval "c=$(printf "%#B\n" a)" +[[ ${a[*]} == "${b[*]}" ]] || err_exit 'printf %B not preserving values for arrays' +[[ ${a[*]} == "${c[*]}" ]] || err_exit 'printf %#B not preserving values for arrays' +unset a +a=(zero one two three four) +a[6]=six +[[ ${a[-1]} == six ]] || err_exit 'a[-1] should be six' +[[ ${a[-3]} == four ]] || err_exit 'a[-3] should be four' +[[ ${a[-3..-1]} == 'four six' ]] || err_exit "a[-3,-1] should be 'four six'" exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/arrays2.sh b/usr/src/lib/libshell/common/tests/arrays2.sh new file mode 100644 index 0000000000..56bce796ae --- /dev/null +++ b/usr/src/lib/libshell/common/tests/arrays2.sh @@ -0,0 +1,123 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 +for ((i=0; i < 4; i++ )) +do for ((j=0; j < 5; j++ )) + do a[i][j]=$i$j + done +done +for ((i=0; i < 4; i++ )) +do for ((j=0; j < 5; j++ )) + do [[ ${a[i][j]} == "$i$j" ]] || err_exit "\${a[$i][$j]} != $i$j" + done +done +for ((i=0; i < 4; i++ )) +do j=0;for k in ${a[i][@]} + do [[ $k == "$i$j" ]] || err_exit "\${a[i][@]} != $i$j" + (( j++ )) + done +done +unset a +a=( + ( 00 01 02 03 04 ) + ( 10 11 12 13 14 15) + ( 20 21 22 23 24 ) + ( 30 31 32 33 34 ) +) + +function check +{ + nameref a=$1 + nameref b=a[2] + typeset c=$1 + integer i j + for ((i=0; i < 4; i++ )) + do for ((j=0; j < 5; j++ )) + do [[ ${a[$i][$j]} == "$i$j" ]] || err_exit "\${$c[$i][$j]} != $i$j" + done + done + (( ${#a[@]} == 4 )) || err_exit "\${#$c[@]} not 4" + (( ${#a[0][@]} == 5 )) || err_exit "\${#$c[0][@]} not 5" + (( ${#a[1][@]} == 6 )) || err_exit "\${#$c[1][@]} not 6" + set -s -- ${!a[@]} + [[ ${@} == '0 1 2 3' ]] || err_exit "\${!$c[@]} not 0 1 2 3" + set -s -- ${!a[0][@]} + [[ ${@} == '0 1 2 3 4' ]] || err_exit "\${!$c[0][@]} not 0 1 2 3 4" + set -s -- ${!a[1][@]} + [[ ${@} == '0 1 2 3 4 5' ]] || err_exit "\${!$c[1][@]} not 0 1 2 3 4 5" + [[ $a == 00 ]] || err_exit "\$$c is not 00" + [[ ${a[0]} == 00 ]] || err_exit "\${$a[0]} is not 00" + [[ ${a[0][0]} == 00 ]] || err_exit "${a[0][0]} is not 00" + [[ ${a[0][0][0]} == 00 ]] || err_exit "\${$c[0][0][0]} is not 00" + [[ ${a[0][0][1]} == '' ]] || err_exit "\${$c[0][0][1]} is not empty" + [[ ${b[3]} == 23 ]] || err_exit "${!b}[3] not = 23" +} + +check a + +unset a +typeset -A a +for ((i=0; i < 4; i++ )) +do for ((j=0; j < 5; j++ )) + do a[$i][j]=$i$j + done +done +for ((i=0; i < 4; i++ )) +do for ((j=0; j < 5; j++ )) + do [[ ${a[$i][j]} == "$i$j" ]] || err_exit "\${a[$i][$j]} == $i$j" + done +done +a[1][5]=15 +b=( + [0]=( 00 01 02 03 04 ) + [1]=( 10 11 12 13 14 15) + [2]=( 20 21 22 23 24 ) + [3]=( 30 31 32 33 34 ) +) +check b +[[ ${a[1][@]} == "${b[1][@]}" ]] || err_exit "a[1] not equal to b[1]" +c=( + [0]=( [0]=00 [1]=01 [2]=02 [3]=03 [4]=04 ) + [1]=( [0]=10 [1]=11 [2]=12 [3]=13 [4]=14 [5]=15) + [2]=( [0]=20 [1]=21 [2]=22 [3]=23 [4]=24 ) + [3]=( [0]=30 [1]=31 [2]=32 [3]=33 [4]=34 ) +) +check c +typeset -A d +d[0]=( [0]=00 [1]=01 [2]=02 [3]=03 [4]=04 ) +d[1]=( [0]=10 [1]=11 [2]=12 [3]=13 [4]=14 [5]=15) +d[2]=( [0]=20 [1]=21 [2]=22 [3]=23 [4]=24 ) +d[3]=( [0]=30 [1]=31 [2]=32 [3]=33 [4]=34 ) +check d +unset a b c d +[[ ${a-set} ]] || err_exit "a is set after unset" +[[ ${b-set} ]] || err_exit "b is set after unset" +[[ ${c-set} ]] || err_exit "c is set after unset" +[[ ${d-set} ]] || err_exit "c is set after unset" +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/attributes.sh b/usr/src/lib/libshell/common/tests/attributes.sh index 8be16f1d42..2d4f1e3d21 100644 --- a/usr/src/lib/libshell/common/tests/attributes.sh +++ b/usr/src/lib/libshell/common/tests/attributes.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -123,6 +123,10 @@ typeset -Z2 m if [[ $(/tmp/ksh$$) != 13 ]] then err_exit 'attributes not cleared for script execution' fi +print 'print VAR=$VAR' > /tmp/ksh$$ +typeset -L70 VAR=var +/tmp/ksh$$ > /tmp/ksh$$.1 +[[ $(< /tmp/ksh$$.1) == VAR= ]] || err_exit 'typeset -L should not be inherited' typeset -Z LAST=00 unset -f foo function foo @@ -140,7 +144,8 @@ foo if (( ${#LAST} != 2 )) then err_exit 'LAST!=2' fi -rm -rf /tmp/ksh$$ +[[ $(set | grep LAST) == LAST=02 ]] || err_exit "LAST not correct in set list" +rm -rf /tmp/ksh$$* set -a unset foo foo=bar @@ -190,7 +195,7 @@ hello worldhello worldhello world ! [[ $v1 == "$b1" ]] || err_exit "v1=$v1 should be $b1" [[ $v2 == "$x" ]] || err_exit "v1=$v2 should be $x" -[[ $(env '!=1' $SHELL -c 'echo ok' 2>/dev/null) == ok ]] || err_exit 'malformed environment terminates shell' +[[ $(env - '!=1' $SHELL -c 'echo ok' 2>/dev/null) == ok ]] || err_exit 'malformed environment terminates shell' unset var typeset -b var printf '12%Z34' | read -r -N 5 var @@ -213,4 +218,66 @@ function fun fun [[ $(export | grep foo) == 'foo=hello' ]] || err_exit 'export not working in functions' [[ $(export | grep bar) ]] && err_exit 'typeset -x not local' +[[ $($SHELL -c 'typeset -r IFS=;print -r $(pwd)' 2> /dev/null) == "$(pwd)" ]] || err_exit 'readonly IFS causes command substitution to fail' +fred[66]=88 +[[ $(typeset -pa) == *fred* ]] || err_exit 'typeset -pa not working' +unset x y z +typeset -LZ3 x=abcd y z=00abcd +y=03 +[[ $y == "3 " ]] || err_exit '-LZ3 not working for value 03' +[[ $x == "abc" ]] || err_exit '-LZ3 not working for value abcd' +[[ $x == "abc" ]] || err_exit '-LZ3 not working for value 00abcd' +unset x z +set +a +[[ $(typeset -p z) ]] && err_exit "typeset -p for z undefined failed" +unset z +x='typeset -i z=45' +eval "$x" +[[ $(typeset -p z) == "$x" ]] || err_exit "typeset -p for '$x' failed" +[[ $(typeset +p z) == "${x%=*}" ]] || err_exit "typeset +p for '$x' failed" +unset z +x='typeset -a z=(a b c)' +eval "$x" +[[ $(typeset -p z) == "$x" ]] || err_exit "typeset -p for '$x' failed" +[[ $(typeset +p z) == "${x%=*}" ]] || err_exit "typeset +p for '$x' failed" +unset z +x='typeset -C z=( + foo=bar + xxx=bam +)' +eval "$x" +x=${x//$'\t'} +x=${x//$'(\n'/'('} +x=${x//$'\n'/';'} +[[ $(typeset -p z) == "$x" ]] || err_exit "typeset -p for '$x' failed" +[[ $(typeset +p z) == "${x%%=*}" ]] || err_exit "typeset +p for '$x' failed" +unset z +x='typeset -A z=([bar]=bam [xyz]=bar)' +eval "$x" +[[ $(typeset -p z) == "$x" ]] || err_exit "typeset -p for '$x' failed" +[[ $(typeset +p z) == "${x%%=*}" ]] || err_exit "typeset +p for '$x' failed" +unset z +foo=abc +x='typeset -n z=foo' +eval "$x" +[[ $(typeset -p z) == "$x" ]] || err_exit "typeset -p for '$x' failed" +[[ $(typeset +p z) == "${x%%=*}" ]] || err_exit "typeset +p for '$x' failed" +typeset +n z +unset foo z +typeset -T Pt_t=( + float x=1 y=2 +) +Pt_t z +x=${z//$'\t'} +x=${x//$'(\n'/'('} +x=${x//$'\n'/';'} +[[ $(typeset -p z) == "Pt_t z=$x" ]] || err_exit "typeset -p for type failed" +[[ $(typeset +p z) == "Pt_t z" ]] || err_exit "typeset +p for type failed" +unset z +function foo +{ + typeset -p bar +} +bar=xxx +[[ $(foo) == bar=xxx ]] || err_exit 'typeset -p not working inside a function' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/basic.sh b/usr/src/lib/libshell/common/tests/basic.sh index 28755da226..3911f9c472 100644 --- a/usr/src/lib/libshell/common/tests/basic.sh +++ b/usr/src/lib/libshell/common/tests/basic.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -28,6 +28,27 @@ alias err_exit='err_exit $LINENO' # test basic file operations like redirection, pipes, file expansion Command=${0##*/} integer Errors=0 +set -- \ + go+r 0000 \ + go-r 0044 \ + ug=r 0330 \ + go+w 0000 \ + go-w 0022 \ + ug=w 0550 \ + go+x 0000 \ + go-x 0011 \ + ug=x 0660 \ + go-rx 0055 \ + uo-wx 0303 \ + ug-rw 0660 \ + o= 0007 +while (( $# >= 2 )) +do umask 0 + umask $1 + g=$(umask) + [[ $g == $2 ]] || err_exit "umask 0; umask $1 failed -- expected $2, got $g" + shift 2 +done umask u=rwx,go=rx || err_exit "umask u=rws,go=rx failed" if [[ $(umask -S) != u=rwx,g=rx,o=rx ]] then err_exit 'umask -S incorrect' @@ -37,6 +58,13 @@ trap "cd /; rm -rf /tmp/ksh$$" EXIT pwd=$PWD [[ $SHELL != /* ]] && SHELL=$pwd/$SHELL cd /tmp/ksh$$ || err_exit "cd /tmp/ksh$$ failed" +um=$(umask -S) +( umask 0777; > foobar ) +rm -f foobar +> foobar +[[ -r foobar ]] || err_exit 'umask not being restored after subshell' +umask "$um" +rm -f foobar # optimizer bug test > foobar for i in 1 2 @@ -272,20 +300,24 @@ optbug() return 1 } optbug || err_exit 'array size optimzation bug' -wait # not running --pipefile which would interfere with subsequent tests +wait # not running --pipefail which would interfere with subsequent tests : $(jobs -p) # required to clear jobs for next jobs -p (interactive side effect) sleep 20 & +pids=$! if [[ $(jobs -p) != $! ]] then err_exit 'jobs -p not reporting a background job' fi sleep 20 & +pids="$pids $!" foo() { set -- $(jobs -p) (( $# == 2 )) || err_exit "$# jobs not reported -- 2 expected" } foo -[[ $( (trap 'print alarm' ALRM; sleep 4) & sleep 2; kill -ALRM $!) == alarm ]] || err_exit 'ALRM signal not working' +kill $pids + +[[ $( (trap 'print alarm' ALRM; sleep 4) & sleep 2; kill -ALRM $!; sleep 2; wait) == alarm ]] || err_exit 'ALRM signal not working' [[ $($SHELL -c 'trap "" HUP; $SHELL -c "(sleep 2;kill -HUP $$)& sleep 4;print done"') != done ]] && err_exit 'ignored traps not being ignored' [[ $($SHELL -c 'o=foobar; for x in foo bar; do (o=save);print $o;done' 2> /dev/null ) == $'foobar\nfoobar' ]] || err_exit 'for loop optimization subshell bug' command exec 3<> /dev/null @@ -319,6 +351,7 @@ chmod +x /tmp/ksh$$x [[ $($SHELL -c "print foo | /tmp/ksh$$x ;:" 2> /dev/null ) == foo ]] || err_exit 'piping into script fails' [[ $($SHELL -c 'X=1;print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 1 ]] || err_exit 'x=1;${x:=$(..."...")} failure' [[ $($SHELL -c 'print -r -- ${X:=$(expr "a(0)" : '"'a*(\([^)]\))')}'" 2> /dev/null) == 0 ]] || err_exit '${x:=$(..."...")} failure' +exec 3<&- if [[ -d /dev/fd && -w /dev/fd/3 ]] then [[ $(cat <(print hello) ) == hello ]] || err_exit "process substitution not working outside for or while loop" $SHELL -c '[[ $(for i in 1;do cat <(print hello);done ) == hello ]]' 2> /dev/null|| err_exit "process substitution not working in for or while loop" @@ -330,7 +363,68 @@ print "#! $SHELL" > /tmp/ksh$$x print 'print -- $0' >> /tmp/ksh$$x chmod +x /tmp/ksh$$x [[ $(/tmp/ksh$$x) == /tmp/ksh$$x ]] || err_exit "\$0 is $0 instead of /tmp/ksh$$x" +cat > /tmp/ksh$$x <<- \EOF + myfilter() { x=$(print ok | cat); print -r -- $SECONDS;} + set -o pipefail + sleep 3 | myfilter +EOF +(( $($SHELL /tmp/ksh$$x) > 2.0 )) && err_exit 'command substitution causes pipefail option to hang' rm -f /tmp/ksh$$x exec 3<&- ( typeset -r foo=bar) 2> /dev/null || err_exit 'readonly variables set in a subshell cannot unset' +$SHELL -c 'x=${ print hello;}; [[ $x == hello ]]' 2> /dev/null || err_exit '${ command;} not supported' +$SHELL 2> /dev/null <<- \EOF || err_exit 'multiline ${...} command substitution not supported' + x=${ + print hello + } + [[ $x == hello ]] +EOF +$SHELL 2> /dev/null <<- \EOF || err_exit '${...} command substitution with side effects not supported ' + y=bye + x=${ + y=hello + print hello + } + [[ $y == $x ]] +EOF +$SHELL 2> /dev/null <<- \EOF || err_exit 'nested ${...} command substitution not supported' + x=${ + print ${ print hello;} $(print world) + } + [[ $x == 'hello world' ]] +EOF +$SHELL 2> /dev/null <<- \EOF || err_exit 'terminating } is not a reserved word with ${ command }' + x=${ { print -n } ; print -n hello ; } ; print ' world' } + [[ $x == '}hello world' ]] +EOF +$SHELL 2> /dev/null <<- \EOF || err_exit '${ command;}xxx not working' + f() + { + print foo + } + [[ ${ f;}bar == foobar ]] +EOF + +unset foo +function foo +{ + print bar +} +[[ ${foo} == bar ]] || err_exit '${foo} is not command substitution when foo unset' +[[ ! ${foo[@]} ]] || err_exit '${foo[@]} is not empty when foo is unset' +[[ ! ${foo[3]} ]] || err_exit '${foo[3]} is not empty when foo is unset' +[[ $(print "[${ print foo }]") == '[foo]' ]] || err_exit '${...} not working when } is followed by ]' +[[ $(print "${ print "[${ print foo }]" }") == '[foo]' ]] || err_exit 'nested ${...} not working when } is followed by ]' +unset foo +foo=$(false) > /dev/null && err_exit 'failed command substitution with redirection not returning false' +expected=foreback +got=$(print -n fore;(sleep 2;print back)&) +[[ $got == $expected ]] || err_exit "command substitution background process output error -- got '$got', expected '$expected'" + +for false in false $(whence -p false) +do x=$($false) && err_exit "x=\$($false) should fail" + $($false) && err_exit "\$($false) should fail" + $($false) > /dev/null && err_exit "\$($false) > /dev/null should fail" +done +[[ $(env 'x-a=y' $SHELL -c 'env | grep x-a') == *x-a=y* ]] || err_exit 'invalid environment variables not preserved' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/bracket.sh b/usr/src/lib/libshell/common/tests/bracket.sh index 25daf72c32..0b36806fbf 100644 --- a/usr/src/lib/libshell/common/tests/bracket.sh +++ b/usr/src/lib/libshell/common/tests/bracket.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -31,11 +31,12 @@ null='' if [[ ! -z $null ]] then err_exit "-z: null string should be of zero length" fi -file=/tmp/regress$$ +file=/tmp/regresso$$ +newer_file=/tmp/regressn$$ if [[ -z $file ]] then err_exit "-z: $file string should not be of zero length" fi -trap "rm -f $file" EXIT +trap "rm -f $file $newer_file" EXIT rm -f $file if [[ -a $file ]] then err_exit "-a: $file shouldn't exist" @@ -111,11 +112,12 @@ if [[ ! -w /dev/fd/2 ]] then err_exit "/dev/fd/2 not open for writing" fi sleep 1 -if [[ ! . -ot $file ]] -then err_exit ". should be older than $file" +> $newer_file +if [[ ! $file -ot $newer_file ]] +then err_exit "$file should be older than $newer_file" fi -if [[ /bin -nt $file ]] -then err_exit "$file should be newer than /bin" +if [[ $file -nt $newer_file ]] +then err_exit "$newer_file should be newer than $file" fi if [[ $file != /tmp/* ]] then err_exit "$file should match /tmp/*" @@ -176,6 +178,8 @@ test -d . -a '(' ! -f . ')' || err_exit 'test not working' if [[ '!' != ! ]] then err_exit 'quoting unary operator not working' fi +test \( -n x \) -o \( -n y \) 2> /dev/null || err_exit 'test ( -n x ) -o ( -n y) not working' +test \( -n x \) -o -n y 2> /dev/null || err_exit 'test ( -n x ) -o -n y not working' chmod 600 $file exec 4> $file print -u4 foobar @@ -230,4 +234,12 @@ $SHELL -c '[[ abc =~ a(b)c ]]' 2> /dev/null || err_exit '[[ abc =~ a(b)c ]] fail $SHELL -xc '[[ abc =~ \babc\b ]]' 2> /dev/null || err_exit '[[ abc =~ \babc\b ]] fails' [[ abc == ~(E)\babc\b ]] || err_exit '\b not preserved for ere when not in ()' [[ abc == ~(iEi)\babc\b ]] || err_exit '\b not preserved for ~(iEi) when not in ()' + +e=$($SHELL -c '[ -z "" -a -z "" ]' 2>&1) +[[ $e ]] && err_exit "[ ... ] compatibility check failed -- $e" +i=hell +[[ hell0 == $i[0] ]] || err_exit 'pattern $i[0] interpreded as array ref' +test '(' = ')' && err_exit '"test ( = )" should not be true' +[[ $($SHELL -c 'case F in ~(Eilr)[a-z0-9#]) print ok;;esac' 2> /dev/null) == ok ]] || err_exit '~(Eilr) not working in case command' +[[ $($SHELL -c "case Q in ~(Fi)q | \$'\E') print ok;;esac" 2> /dev/null) == ok ]] || err_exit '~(Fi)q | \E not working in case command' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/builtins.sh b/usr/src/lib/libshell/common/tests/builtins.sh index 1d2dfe9157..08dac83a4e 100644 --- a/usr/src/lib/libshell/common/tests/builtins.sh +++ b/usr/src/lib/libshell/common/tests/builtins.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -43,9 +43,9 @@ if [[ $var != : || $OPTARG != r ]] then err_exit "'getopts :r:s var -r' not working" fi OPTIND=1 -getopts :d#u var -d 100 -if [[ $var != d || $OPTARG != 100 ]] -then err_exit "'getopts :d#u var -d 100' not working var=$var" +getopts :d#u OPT -d 16177 +if [[ $OPT != d || $OPTARG != 16177 ]] +then err_exit "'getopts :d#u OPT=d OPTARG=16177' failed -- OPT=$OPT OPTARG=$OPTARG" fi OPTIND=1 while getopts 'ab' option -a -b @@ -128,6 +128,8 @@ x=$0 if [[ $(eval 'print $0') != $x ]] then err_exit '$0 not correct for eval' fi +$SHELL -c 'read x <<< hello' 2> /dev/null || err_exit 'syntax <<< not recognized' +($SHELL -c 'read x[1] <<< hello') 2> /dev/null || err_exit 'read x[1] not working' unset x readonly x set -- $(readonly) @@ -159,7 +161,7 @@ done if [[ $(print -f "%b" "\a\n\v\b\r\f\E\03\\oo") != $'\a\n\v\b\r\f\E\03\\oo' ]] then err_exit 'print -f "%b" not working' fi -if [[ $(print -f "%P" "[^x].*b$") != '*[!x]*b' ]] +if [[ $(print -f "%P" "[^x].*b\$") != '*[!x]*b' ]] then err_exit 'print -f "%P" not working' fi if [[ $(abc: for i in foo bar;do print $i;break abc;done) != foo ]] @@ -184,10 +186,9 @@ fi if [[ $(trap -p HUP) != 'print HUP' ]] then err_exit '$(trap -p HUP) not working' fi -[[ $($SHELL -c 'trap "print ok" SIGTERM; kill -s SIGTERM $$' 2> /dev/null) == ok - ]] || err_exit 'SIGTERM not recognized' -[[ $($SHELL -c 'trap "print ok" sigterm; kill -s sigterm $$' 2> /dev/null) == ok - ]] || err_exit 'SIGTERM not recognized' +[[ $($SHELL -c 'trap "print ok" SIGTERM; kill -s SIGTERM $$' 2> /dev/null) == ok ]] || err_exit 'SIGTERM not recognized' +[[ $($SHELL -c 'trap "print ok" sigterm; kill -s sigterm $$' 2> /dev/null) == ok ]] || err_exit 'SIGTERM not recognized' +[[ $($SHELL -c '( trap "" TERM);kill $$;print bad' == bad) ]] 2> /dev/null && err_exit 'trap ignored in subshell causes it to be ignored by parent' ${SHELL} -c 'kill -1 -$$' 2> /dev/null [[ $(kill -l $?) == HUP ]] || err_exit 'kill -1 -pid not working' ${SHELL} -c 'kill -1 -$$' 2> /dev/null @@ -297,12 +298,6 @@ print $'line1\nline2' | behead if [[ $left != line2 ]] then err_exit "read reading ahead on a pipe" fi -read -n1 y <<! -abc -! -if [[ $y != a ]] -then err_exit 'read -n1 not working' -fi print -n $'{ read -r line;print $line;}\nhello' > /tmp/ksh$$ chmod 755 /tmp/ksh$$ trap 'rm -rf /tmp/ksh$$' EXIT @@ -349,6 +344,34 @@ getopts 'n#num' opt -n 3 if [[ $($SHELL -c $'printf \'%2$s %1$s\n\' world hello') != 'hello world' ]] then err_exit 'printf %2$s %1$s not working' fi +val=$(( 'C' )) +set -- \ + "'C" $val 0 \ + "'C'" $val 0 \ + '"C' $val 0 \ + '"C"' $val 0 \ + "'CX" $val 1 \ + "'CX'" $val 1 \ + "'C'X" $val 1 \ + '"CX' $val 1 \ + '"CX"' $val 1 \ + '"C"X' $val 1 +while (( $# >= 3 )) +do arg=$1 val=$2 code=$3 + shift 3 + for fmt in '%d' '%g' + do out=$(printf "$fmt" "$arg" 2>/dev/null) + err=$(printf "$fmt" "$arg" 2>&1 >/dev/null) + printf "$fmt" "$arg" >/dev/null 2>&1 + ret=$? + [[ $out == $val ]] || err_exit "printf $fmt $arg failed -- expected $val, got $out" + if (( $code )) + then [[ $err ]] || err_exit "printf $fmt $arg failed, error message expected" + else [[ $err ]] && err_exit "$err: printf $fmt $arg failed, error message not expected -- got '$err'" + fi + (( $ret == $code )) || err_exit "printf $fmt $arg failed -- expected exit code $code, got $ret" + done +done ((n=0)) ((n++)); ARGC[$n]=1 ARGV[$n]="" ((n++)); ARGC[$n]=2 ARGV[$n]="-a" @@ -362,43 +385,19 @@ do set -- ${ARGV[$i]} do : done if [[ $OPTIND != ${ARGC[$i]} ]] - then err_exit "\$OPTIND after getopts loop incorrect -- got $OPTIND, expected ${ARGC[$i]}" + then err_exit "\$OPTIND after getopts loop incorrect -- expected ${ARGC[$i]}, got $OPTIND" fi done -unset a -{ read -N3 a; read -N1 b;} <<! -abcdefg -! -[[ $a == abc ]] || err_exit 'read -N3 here-document not working' -[[ $b == d ]] || err_exit 'read -N1 here-document not working' -read -n3 a <<! -abcdefg -! -[[ $a == abc ]] || err_exit 'read -n3 here-document not working' -(print -n a;sleep 1; print -n bcde) | { read -N3 a; read -N1 b;} -[[ $a == abc ]] || err_exit 'read -N3 from pipe not working' -[[ $b == d ]] || err_exit 'read -N1 from pipe not working' -(print -n a;sleep 1; print -n bcde) |read -n3 a -[[ $a == a ]] || err_exit 'read -n3 from pipe not working' -rm -f /tmp/fifo$$ -if mkfifo /tmp/fifo$$ 2> /dev/null -then (print -n a; sleep 1;print -n bcde) > /tmp/fifo$$ & - { - read -u5 -n3 -t2 a || err_exit 'read -n3 from fifo timedout' - read -u5 -n1 -t2 b || err_exit 'read -n1 from fifo timedout' - } 5< /tmp/fifo$$ - [[ $a == a ]] || err_exit 'read -n3 from fifo not working' - rm -f /tmp/fifo$$ - mkfifo /tmp/fifo$$ 2> /dev/null - (print -n a; sleep 1;print -n bcde) > /tmp/fifo$$ & - { - read -u5 -N3 -t2 a || err_exit 'read -N3 from fifo timed out' - read -u5 -N1 -t2 b || err_exit 'read -N1 from fifo timedout' - } 5< /tmp/fifo$$ - [[ $a == abc ]] || err_exit 'read -N3 from fifo not working' - [[ $b == d ]] || err_exit 'read -N1 from fifo not working' -fi -rm -f /tmp/fifo$$ +options=ab:c +optarg=foo +set -- -a -b $optarg -c bar +while getopts $options opt +do case $opt in + a|c) [[ $OPTARG ]] && err_exit "getopts $options \$OPTARG for flag $opt failed, expected \"\", got \"$OPTARG\"" ;; + b) [[ $OPTARG == $optarg ]] || err_exit "getopts $options \$OPTARG failed -- \"$optarg\" expected, got \"$OPTARG\"" ;; + *) err_exit "getopts $options failed -- got flag $opt" ;; + esac +done function longline { integer i @@ -448,4 +447,23 @@ then err_exit "read -t in pipe taking $total_t secs - $(( reps * delay )) minimu elif (( total_t < reps * delay )) then err_exit "read -t in pipe taking $total_t secs - $(( reps * delay )) minimum - too fast" fi +$SHELL -c 'sleep $(printf "%a" .95)' 2> /dev/null || err_exit "sleep doesn't except %a format constants" +$SHELL -c 'test \( ! -e \)' 2> /dev/null ; [[ $? == 1 ]] || err_exit 'test \( ! -e \) not working' +[[ $(ulimit) == "$(ulimit -fS)" ]] || err_exit 'ulimit is not the same as ulimit -fS' +tmpfile=${TMP-/tmp}/ksh$$.2 +trap 'rm -f /tmp/ksh$$ "$tmpfile"' EXIT +print $'\nprint -r -- "${.sh.file} ${LINENO} ${.sh.lineno}"' > $tmpfile +[[ $( . "$tmpfile") == "$tmpfile 2 1" ]] || err_exit 'dot command not working' +print -r -- "'xxx" > $tmpfile +[[ $($SHELL -c ". $tmpfile"$'\n print ok' 2> /dev/null) == ok ]] || err_exit 'syntax error in dot command affects next command' + +float sec=$SECONDS del=4 +exec 3>&2 2>/dev/null +$SHELL -c "( sleep 1; kill -ALRM \$\$ ) & sleep $del" 2> /dev/null +exitval=$? +(( sec = SECONDS - sec )) +exec 2>&3- +(( exitval )) && err_exit "sleep doesn't exit 0 with ALRM interupt" +(( sec > (del - 1) )) || err_exit "ALRM signal causes sleep to terminate prematurely -- expected 3 sec, got $sec" + exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/case.sh b/usr/src/lib/libshell/common/tests/case.sh index 4090d30103..cca84686bd 100644 --- a/usr/src/lib/libshell/common/tests/case.sh +++ b/usr/src/lib/libshell/common/tests/case.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # diff --git a/usr/src/lib/libshell/common/tests/comvar.sh b/usr/src/lib/libshell/common/tests/comvar.sh index 5dc5c59a1c..dfc9d9acfd 100644 --- a/usr/src/lib/libshell/common/tests/comvar.sh +++ b/usr/src/lib/libshell/common/tests/comvar.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -73,6 +73,7 @@ if [[ ${!x.@} != foo.x ]] then err_exit 'name references not expanded on prefix matching' fi unset x +unset -n x ( x=() x.foo.bar=7 @@ -191,7 +192,235 @@ point=(integer x=6 y=8) localvar (( (point.x*point.x + point.y*point.y) == 100 )) || err_exit "global compound variable not preserved" [[ $($SHELL -c 'foo=();foo.[x]=(y z); print ${foo.x[@]}') == 'y z' ]] 2> /dev/null || err_exit 'foo=( [x]=(y z) not working' +function staticvar +{ + if [[ $1 ]] + then print -r -- "$point" + return + fi + typeset -S point=(typeset -i x=3 y=4) + (( (point.x*point.x + point.y*point.y) == 25 )) || err_exit "local compound variable not working" + point.y=5 + point.z=foobar +} +staticvar + (( (point.x*point.x + point.y*point.y) == 100 )) || err_exit "global compound variable not preserved" +[[ $(staticvar x) == $'(\n\ttypeset -i x=3\n\ttypeset -i y=5\n\tz=foobar\n)' ]] || err_exit 'static variables in function not working' +integer x=3 +( typeset -S x=+++)2> /dev/null || err_exit "typeset -S doesn't unset first" + unset z ( [[ ${z.foo.bar:-abc} == abc ]] 2> /dev/null) || err_exit ':- not working with compound variables' -exit $((Errors)) +stack=() +typeset -a stack.items=([0]=foo [1]=bar) +[[ ${stack.items[0]} == foo ]] || err_exit 'typeset -a variable not expanding correctly' +$SHELL -c 'typeset -a info=( [1]=( passwd=( since=2005-07-20) ))' || err_exit 'problem with embedded index array in compound variable' +x=(foo=([1]=(y=([2]=(z=4))))) +[[ $x == *'.y'=* ]] && err_exit 'expansion with bogus leading . in name' +unset z +z=1 +function foo +{ + z=3 + [[ ${a.z} == 3 ]] && err_exit "\${a.z} should not be 3" + print hi +} +a=( b=$(foo) ) +[[ ${a.z} == 3 ]] && err_exit 'a.z should not be set to 3' +function a.b.get +{ + .sh.value=foo +} +{ b=( b1=${a.b} ) ;} 2> /dev/null +[[ ${b.b1} == foo ]] || err_exit '${b.b1} should be foo' +function dcl1 +{ + eval 'a=1 + function a.set + { print ${.sh.name}=${.sh.value}; }' +} +function dcl2 +{ + eval 'b=(typeset x=0; typeset y=0 ) + function b.x.set + { print ${.sh.name}=${.sh.value}; }' +} +dcl1 +[[ ${ a=123;} == 'a=123' ]] || err_exit 'should be a=123' +dcl2 +[[ ${ b.x=456;} == 'b.x=456' ]] || err_exit 'should be b.x=456' +eval 'b=(typeset x=0; typeset y=0 ) +function b.x.set +{ print ${.sh.name}=${.sh.value}; }' > /dev/null +[[ ${ b.x=789;} == 'b.x=789' ]] || err_exit 'should be b.x=789' +unset a b +function func +{ + typeset X + X=( bar=2 ) +} + +X=( foo=1 ) +func +[[ $X == $'(\n\tfoo=1\n)' ]] || err_exit 'scoping problem with compound variables' +unset foo +typeset -A foo=([a]=aa;[b]=bb;[c]=cc) +[[ ${foo[c]} == cc ]] || err_exit 'associative array assignment with; not working' +[[ $({ $SHELL -c 'x=(); typeset -a x.foo; x.foo=bar; print -r -- "$x"' ;} 2> /dev/null) == $'(\n\ttypeset -a foo=bar\n)' ]] || err_exit 'indexed array in compound variable with only element 0 defined fails' +unset foo +foo=(typeset -a bar) +[[ $foo == *'typeset -a bar'* ]] || err_exit 'array attribute -a not preserved in compound variable' +unset s +typeset -A s=( [foo]=(y=2 z=3) [bar]=(y=4 z=5)) +[[ ${s[@]} == *z=*z=* ]] || err_exit 'missing elements in compound associative array' +unset nodes +typeset -A nodes +nodes[0]+=( integer x=5) +[[ ${nodes[0].x} == 5 ]] || err_exit '${nodes[0].x} should be 5' +unset foo +typeset -C foo +foo.bar=abc +[[ $foo == $'(\n\tbar=abc\n)' ]] || err_exit 'typeset -C not working for foo' +typeset -C foo=(bar=def) +[[ $foo == $'(\n\tbar=def\n)' ]] || err_exit 'typeset -C not working when initialized' +foo=( + hello=ok + yes=( bam=2 yes=4) + typeset -A array=([one]=one [two]=2) + last=me +) +eval foo2="$foo" +foo2.hello=notok foo2.yes.yex=no foo2.extra=yes. +typeset -C bar bam +{ + read -Cu3 bar + read -Cu3 bam + read -ru3 +} 3<<- ++++ + "$foo" + "$foo2" + last line +++++ +[[ $? == 0 ]] || err_exit ' read -C failed' +[[ $bar == "$foo" ]] || err_exit '$foo != $bar' +[[ $bam == "$foo2" ]] || err_exit '$foo2 != $bmr' +[[ $REPLY == 'last line' ]] || err_exit "\$REPLY=$REPLY should be 'last line" +typeset x=( typeset -a foo=( [1][3]=hello [9][2]="world" ) ) +eval y="(typeset -a foo=$(printf "%B\n" x.foo) )" +[[ $x == "$y" ]] || err_exit '$x.foo != $y.foo with %B' +eval y="(typeset -a foo=$(printf "%#B\n" x.foo) )" +[[ $x == "$y" ]] || err_exit '$x.foo != $y.foo with %#B' +eval y="$(printf "%B\n" x)" +[[ $x == "$y" ]] || err_exit '$x != $y with %B' +eval y="$(printf "%#B\n" x)" +[[ $x == "$y" ]] || err_exit '$x != $y with %#B' +y=$(set | grep ^x=) 2> /dev/null +eval "${y/#x/y}" +[[ $x == "$y" ]] || err_exit '$x != $y with set | grep' +unset x y z +x=( float x=0 y=1; z=([foo]=abc [bar]=def)) +typeset -C y=x +[[ $x == "$y" ]] || err_exit '$x != $y with typeset -C' +unset y +y=() +y=x +[[ $x == "$y" ]] || err_exit '$x != $y when x=y and x and y are -C ' +function foobar +{ + typeset -C z + z=x + [[ $x == "$z" ]] || err_exit '$x != $z when x=z and x and z are -C ' + y=z +} +[[ $x == "$y" ]] || err_exit '$x != $y when x=y -C copied in a function ' +z=(foo=abc) +y+=z +[[ $y == *foo=abc* ]] || err_exit 'z not appended to y' +unset y.foo +[[ $x == "$y" ]] || err_exit '$x != $y when y.foo deleted' +unset x y +x=( foo=(z=abc d=ghi) bar=abc; typeset -A r=([x]=3 [y]=4)) +unset x +x=() +[[ $x == $'(\n)' ]] || err_exit 'unset compound variable is not empty' +unset z +z=() +z.foo=( [one]=hello [two]=(x=3 y=4) [three]=hi) +z.bar[0]=hello +z.bar[2]=world +z.bar[1]=(x=4 y=5) +exp='( + typeset -a bar=( + [0]=hello + [2]=world + [1]=( + x=4 + y=5 + ) + ) + typeset -A foo=( + [one]=hello + [three]=hi + [two]=( + x=3 + y=4 + ) + ) +)' +got=$z +[[ $got == "$exp" ]] || { + exp=$(printf %q "$exp") + got=$(printf %q "$got") + err_exit "compound indexed array pretty print failed -- expected $exp, got $got" +} + +typeset -A record +record[a]=( + typeset -a x=( + [1]=( + X=1 + ) + ) +) +exp=$'(\n\ttypeset -a x=(\n\t\t[1]=(\n\t\t\tX=1\n\t\t)\n\t)\n)' +got=${record[a]} +[[ $got == "$exp" ]] || { + exp=$(printf %q "$exp") + got=$(printf %q "$got") + err_exit "compound indexed array pretty print failed -- expected $exp, got $got" +} + +unset r +r=( + typeset -a x=( + [1]=( + X=1 + ) + ) +) +exp=$'(\n\ttypeset -a x=(\n\t\t[1]=(\n\t\t\tX=1\n\t\t)\n\t)\n)' +got=$r +[[ $got == "$exp" ]] || { + exp=$(printf %q "$exp") + got=$(printf %q "$got") + err_exit "compound indexed array pretty print failed -- expected $exp, got $got" +} + +# array of compund variables +typeset -C data=( + typeset -a samples +) +data.samples+=( + type1="greeting1" + timestamp1="now1" + command1="grrrr1" +) +data.samples+=( + type2="greeting2" + timestamp2="now2" + command2="grrrr2" +) + +[[ $data == %(()) ]] || err_exit "unbalanced parenthesis with compound variable containing array of compound variables" +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/coprocess.sh b/usr/src/lib/libshell/common/tests/coprocess.sh index 5bbbdf759a..feff0bace2 100644 --- a/usr/src/lib/libshell/common/tests/coprocess.sh +++ b/usr/src/lib/libshell/common/tests/coprocess.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -37,7 +37,7 @@ fi function ping # id { integer x=0 - while ((x < 5)) + while ((x++ < 5)) do read -r print -r "$1 $REPLY" done @@ -52,6 +52,7 @@ print -u5 'hello again' || err_exit 'write on u5 fails' read -u6 line [[ $line == 'hello again' ]] || err_exit 'coprocess after moving fds fails' exec 5<&- 6<&- +wait $! ping three |& exec 3>&p @@ -66,25 +67,25 @@ do case $i in four) to=-u4;; pipe) to=-p;; esac - count=count+1 - print $to $i $count + (( count++ )) + print $to $i $count done while ((count > 0)) -do count=count-1 +do (( count-- )) read -p -# print -r - "$REPLY" set -- $REPLY if [[ $1 != $2 ]] - then err_exit "$1 does not match 2" + then err_exit "$1 does not match $2" fi case $1 in - three);; - four) ;; - pipe) ;; - *) err_exit "unknown message +|$REPLY|+" + three) ;; + four) ;; + pipe) ;; + *) err_exit "unknown message +|$REPLY|+" ;; esac done +kill $(jobs -p) 2>/dev/null file=/tmp/regress$$ trap "rm -f $file" EXIT @@ -92,60 +93,72 @@ cat > $file <<\! /bin/cat |& ! chmod +x $file -$file 2> /dev/null || err_exit "parent coprocess prevents script coprocess" +sleep 10 |& +$file 2> /dev/null || err_exit "parent coprocess prevents script coprocess" exec 5<&p 6>&p exec 5<&- 6>&- +kill $(jobs -p) 2>/dev/null + ${SHELL-ksh} |& print -p $'print hello | cat\nprint Done' read -t 5 -p read -t 5 -p if [[ $REPLY != Done ]] -then err_exit "${SHELL-ksh} coprocess not working" +then err_exit "${SHELL-ksh} coprocess not working" fi exec 5<&p 6>&p exec 5<&- 6>&- -count=0 +wait $! + { echo line1 | grep 'line2' echo line2 | grep 'line1' } |& -SECONDS=0 -while - read -p -t 10 line -do - ((count = count + 1)) - echo "Line $count: $line" +SECONDS=0 count=0 +while read -p -t 10 line +do ((count++)) done if (( SECONDS > 8 )) -then err_exit 'read -p hanging' +then err_exit "read -p hanging (SECONDS=$SECONDS count=$count)" fi +wait $! + ( sleep 3 |& sleep 1 && kill $!; sleep 1; sleep 3 |& sleep 1 && kill $! ) || err_exit "coprocess cleanup not working correctly" -unset line +{ : |& } 2>/dev/null || + err_exit "subshell coprocess lingers in parent" +wait $! + +unset N r e +integer N=5 +e=12345 ( - integer n=0 - while read line - do echo $line |& - if cat <&p - then ((n++)) - wait $! - fi - done > /dev/null 2>&1 <<- ! - line1 - line2 - line3 - line4 - line5 - line6 - line7 - ! - (( n==7 )) && print ok -) | read -t 10 line -if [[ $line != ok ]] -then err_exit 'coprocess timing bug' -fi + integer i + for ((i = 1; i <= N; i++)) + do print $i |& + read -p r + print -n $r + wait $! + done + print +) 2>/dev/null | read -t 10 r +[[ $r == $e ]] || err_exit "coprocess timing bug -- expected $e, got '$r'" +r= +( + integer i + for ((i = 1; i <= N; i++)) + do print $i |& + sleep 0.01 + r=$r$(cat <&p) + wait $! + done + print $r +) 2>/dev/null | read -t 10 r +[[ $r == $e ]] || err_exit "coprocess command substitution bug -- expected $e, got '$r'" + ( /bin/cat |& + sleep 0.01 exec 6>&p print -u6 ok exec 6>&- @@ -154,7 +167,7 @@ fi ) && err_exit 'coprocess with subshell would hang' for sig in IOT ABRT do if ( trap - $sig ) 2> /dev/null - then if [[ $( + then if [[ $( { sig=$sig $SHELL 2> /dev/null <<- '++EOF++' cat |& pid=$! trap "print TRAP" $sig @@ -164,9 +177,12 @@ do if ( trap - $sig ) 2> /dev/null sleep 2 kill -$sig $$ kill $pid - ) 2> /dev/null & + sleep 2 + kill $$ + ) & read -p - ) != $'TRAP\nTRAP' ]] + ++EOF++ + } ) != $'TRAP\nTRAP' ]] 2> /dev/null then err_exit 'traps when reading from coprocess not working' fi break diff --git a/usr/src/lib/libshell/common/tests/cubetype.sh b/usr/src/lib/libshell/common/tests/cubetype.sh new file mode 100644 index 0000000000..f3597d258d --- /dev/null +++ b/usr/src/lib/libshell/common/tests/cubetype.sh @@ -0,0 +1,210 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 +integer n=2 + +typeset -T Box_t=( + float -h 'height in inches' x=2 + float -h 'width in inches' y=4 + comvar=(top=8 bottom=9) + integer -S count=0 + items=(foo bar) + colors=([wall]=blue [floor]=red) + typeset name=unknown + typeset -L6 status=INIT + len() + { + print -r $((sqrt(_.x*_.x + _.y*_.y))) + (( _.count++)) + } + typeset -fh 'distance from the origin' len + depth() + { + print 0 + } + float x=3 +) + +for ((i=0; i < n; i++)) +do +Box_t b=(name=box1) +exp=3 got=${b.x} +[[ "$got" == "$exp" ]] || err_exit "\${b.x} incorrect for iteration $i -- expected $exp, got '$got'" +exp=5 got=$(( b.len )) +(( got == exp )) || err_exit "b.len incorrect for iteration $i -- expected $exp, got '$got = sqrt(${b.x}*${b.x}+${b.y}*${b.y})'" +exp=5 got=${b.len} +[[ "$got" == "$exp" ]] || err_exit "\${b.len} incorrect for iteration $i -- expected $exp, got '$got = sqrt(${b.x}*${b.x}+${b.y}*${b.y})'" +exp=box1 got=${b.name} +[[ "$got" == "${exp}" ]] || err_exit "\${b.name} incorrect for iteration $i -- expected $exp, got '$got'" +exp=2 got=$(( b.count )) +(( got == exp )) || err_exit "b.count incorrect for iteration $i -- expected $exp, got '$got'" +exp=2 got=${b.count} +[[ "$got" == "$exp" ]] || err_exit "\${b.ccount} incorrect for iteration $i -- expected $exp, got '$got'" +b.colors[wall]=green +b.colors[door]=white +exp=3 got=${#b.colors[@]} +[[ "$got" == "$exp" ]] || err_exit "\${#b.colors[@]} incorrect for iteration $i -- expected $exp, got '$got'" +b.comvar.bottom=11 +b.items[1]=bam +b.items[2]=extra +exp=3 got=${#b.items[@]} +[[ ${#b.items[@]} == 3 ]] || err_exit "\${#b.items[@]} incorrect for iteration $i -- expected $exp, got '$got'" +Box_t bb=b +bb.colors[desk]=orange +exp=4 got=${#b.colors[@]} +[[ ${#bb.colors[@]} == 4 ]] || err_exit "\${#bb.colors[@]} incorrect for iteration $i -- expected $exp, got '$got'" +unset b.colors +exp=2 got=${#b.colors[@]} +[[ ${#b.colors[@]} == 2 ]] || err_exit "\${#b.colors[@]} incorrect for iteration $i -- expected $exp, got '$got'" +unset b.items +exp=2 got=${#b.items[@]} +[[ ${#b.items[@]} == 2 ]] || err_exit "\${#b.items[@]} incorrect for iteration $i -- expected $exp, got '$got'" +unset bb.colors +exp=2 got=${#bb.colors[@]} +[[ ${#bb.colors[@]} == 2 ]] || err_exit "\${#bb.colors[@]} incorrect for iteration $i -- expected $exp, got '$got'" +unset bb.items +exp=2 got=${#bb.items[@]} +[[ ${#bb.items[@]} == 2 ]] || err_exit "\${#bb.items[@]} incorrect for iteration $i -- expected $exp, got '$got'" +[[ $b == "$bb" ]] || err_exit "\$b='$b' != \$bb='$bb'" +b.count=0 +false +unset b bb +done + +typeset -T Cube_t=( + Box_t _=(y=5) + float z=1 + depth() + { + print -r -- $((_.z)) + } + len() + { + print -r $((sqrt(_.x*_.x + _.y*_.y + _.z*_.z))) + (( _.count++)) + } + float x=8 + fun() + { + print 'hello world' + } +) + + +for ((i=0; i < n; i++)) +do +Box_t b=(name=box2) +[[ ${b.name} == box2 ]] || err_exit "\${b.name} incorrect -- expected box2, got '${b.name}'" +(( b.len == 5 )) || err_exit "b.len incorrect for box2 -- expected 5, got '$(( b.len ))'" +(( b.count == 1 )) || err_exit "b.count incorrect -- expected 1, got '$(( b.count ))'" +Cube_t c=(name=cube1) +[[ $c == $'(\n\ttypeset -l -E x=8\n\ttypeset -l -E y=5\n\tcomvar=(\n\t\ttop=8\n\t\tbottom=9\n\t)\n\ttypeset -S -l -i count=1\n\ttypeset -a items=(\n\t\tfoo\n\t\tbar\n\t)\n\ttypeset -A colors=(\n\t\t[floor]=red\n\t\t[wall]=blue\n\t)\n\tname=cube1\n\ttypeset -L 6 status=INIT\n\ttypeset -l -E z=1\n)' ]] || err_exit '$c not correct' +[[ ${c.x} == 8 ]] || err_exit '${c.x} != 8' +[[ ${c.depth} == 1 ]] || err_exit '${c.depth} != 1' +[[ ${c.name} == cube1 ]] || err_exit '${c.name} != cube1 ' +[[ $(c.fun) == 'hello world' ]] || err_exit '$(c.fun) != "hello world"' +[[ ${c.fun} == 'hello world' ]] || err_exit '${c.fun} != "hello world"' +(( abs(c.len - sqrt(90)) < 1e-10 )) || err_exit 'c.len != sqrt(90)' +(( c.count == 2 )) || err_exit 'c.count != 2' +(( c.count == b.count )) || err_exit 'c.count != b.count' +c.count=0 +Cube_t d=c +[[ $d == "$c" ]] || err_exit '$d != $c' +eval "Cube_t zzz=$c" +[[ $zzz == "$c" ]] || err_exit '$zzz != $c' +Cube_t zzz=c +[[ $zzz == "$c" ]] || err_exit '$zzz != $c without eval' +xxx=$(typeset -p c) +eval "${xxx/c=/ccc=}" +[[ $ccc == "$c" ]] || err_exit '$ccc != $c' +unset b c d zzz xxx ccc +done +for ((i=0; i < n; i++)) +do +Cube_t cc +cc[2]=(x=2 y=3 name=two colors+=([table]=white) items+=(pencil) z=6) +[[ ${cc[2].y} == 3 ]] || err_exit '${cc[2].y} != 3' +(( cc[2].y == 3 )) || err_exit '(( cc[2].y != 3))' +[[ ${cc[2].colors[table]} == white ]] || err_exit '${cc[2].colors[table]} != white' +[[ ${cc[2].items[2]} == pencil ]] || err_exit '${cc[2].items[2]} != pencil' +(( cc[2].len == 7 )) || err_exit '(( cc[2].len != 7 ))' +[[ $(cc[2].len) == 7 ]] || err_exit '$(cc[2].len) != 7 ))' +[[ ${cc[2].len} == 7 ]] || err_exit '${cc[2].len} != 7 ))' +(( cc[2].count == 2 )) || err_exit 'cc[2].count != 2' +unset cc[2].x cc[2].y cc[2].z +(( cc[2].len == cc[0].len )) || err_exit 'cc[2].len != cc[0].len' +(( cc[2].len == cc.len )) || err_exit 'cc[2].len != cc.len' +(( cc[2].count == 6 )) || err_exit 'cc[2].count != 6' +unset cc[2].name cc[2].colors cc[2].items +[[ $cc == "${cc[2]}" ]] || err_exit '$cc != ${cc[2]}' +cc.count=0 +unset cc +Cube_t -A cc +cc[two]=(x=2 y=3 name=two colors+=([table]=white) items+=(pencil) z=6) +Cube_t cc[one] +[[ ${#cc[@]} == 2 ]] || err_exit '${#cc[@]} != 2' +[[ ${cc[two].y} == 3 ]] || err_exit '${cc[two].y} != 3' +(( cc[two].y == 3 )) || err_exit '(( cc[two].y != 3))' +[[ ${cc[two].colors[table]} == white ]] || err_exit '${cc[two].colors[table]} != white' +[[ ${cc[two].items[2]} == pencil ]] || err_exit '${cc[two].items[2]} != pencil' +(( cc[two].len == 7 )) || err_exit '(( cc[two].len != 7 ))' +[[ $(cc[two].len) == 7 ]] || err_exit '$(cc[two].len) != 7 ))' +[[ ${cc[two].len} == 7 ]] || err_exit '${cc[two].len} != 7 ))' +(( cc[two].count == 2 )) || err_exit 'cc[two].count != 2' +unset cc[two].x cc[two].y cc[two].z +(( cc[two].len == cc[one].len )) || err_exit 'cc[two].len != cc[one].len' +(( cc[two].count == 4 )) || err_exit 'cc[two].count != 4' +unset cc[two].name unset cc[two].colors cc[two].items +[[ ${cc[one]} == "${cc[two]}" ]] || err_exit '${cc[one]} != ${cc[two]}' +cc[two].count=0 +unset cc +Cube_t cc=( + [one]= + [two]=(x=2 y=3 name=two colors+=([table]=white) z=6) +) +[[ ${#cc[@]} == 2 ]] || err_exit '${#cc[@]} != 2' +[[ ${cc[two].y} == 3 ]] || err_exit '${cc[two].y} != 3' +(( cc[two].y == 3 )) || err_exit '(( cc[two].y != 3))' +[[ ${cc[two].colors[table]} == white ]] || err_exit '${cc[two].colors[table]} != white' +(( cc[two].len == 7 )) || err_exit '(( cc[two].len != 7 ))' +[[ $(cc[two].len) == 7 ]] || err_exit '$(cc[two].len) != 7 ))' +[[ ${cc[two].len} == 7 ]] || err_exit '${cc[two].len} != 7 ))' +(( cc[two].count == 2 )) || err_exit 'cc[two].count != 2' +unset cc[two].x cc[two].y cc[two].z +(( cc[two].len == cc[one].len )) || err_exit 'cc[two].len != cc[one].len' +(( cc[two].count == 4 )) || err_exit 'cc[two].count != 4' +cc[three]=cc[two] +[[ ${cc[two]} == "${cc[three]}" ]] || err_exit ' ${cc[two]} != ${cc[three]}' +[[ $cc[two] == "${cc[three]}" ]] || err_exit ' $cc[two] != $cc[three]' +[[ ${#cc[@]} == 3 ]] || err_exit '${#cc[@]} != 3' +unset cc[two].name unset cc[two].colors +cc[two].count=0 +unset cc +done +exit $Errors diff --git a/usr/src/lib/libshell/common/tests/enum.sh b/usr/src/lib/libshell/common/tests/enum.sh new file mode 100644 index 0000000000..35d2a27952 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/enum.sh @@ -0,0 +1,67 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 +enum Color_t=(red green blue orange yellow) +enum -i Sex_t=(Male Female) +for ((i=0; i < 1000; i++)) +do +Color_t x +[[ $x == red ]] || err_exit 'Color_t does not default to red' +x=orange +[[ $x == orange ]] || err_exit '$x should be orange' +( x=violet) 2> /dev/null && err_exit 'x=violet should fail' +x[2]=green +[[ ${x[2]} == green ]] || err_exit '${x[2]} should be green' +(( x[2] == 1 )) || err_exit '((x[2]!=1))' +[[ $((x[2])) == 1 ]] || err_exit '$((x[2]))!=1' +[[ $x == orange ]] || err_exit '$x is no longer orange' +Color_t -A y +y[foo]=yellow +[[ ${y[foo]} == yellow ]] || err_exit '${y[foo]} != yellow' +(( y[foo] == 4 )) || err_exit '(( y[foo] != 4))' +unset y +typeset -a [Color_t] z +z[green]=xyz +[[ ${z[green]} == xyz ]] || err_exit '${z[green]} should be xyz' +[[ ${z[1]} == xyz ]] || err_exit '${z[1]} should be xyz' +z[orange]=bam +[[ ${!z[@]} == 'green orange' ]] || err_exit '${!z[@]} == "green orange"' +unset x +Sex_t x +[[ $x == Male ]] || err_exit 'Sex_t not defaulting to Male' +x=female +[[ $x == Female ]] || err_exit 'Sex_t not case sensitive' +unset x y z +done +( +typeset -T X_t=( typeset name=aha ) +typeset -a[X_t] arr +) 2> /dev/null +[[ $? == 1 ]] || err_exit 'typeset -a[X_t] should generate an error message when X-t is not an enumeriation type' +exit $Errors diff --git a/usr/src/lib/libshell/common/tests/exit.sh b/usr/src/lib/libshell/common/tests/exit.sh index b099ccfecc..01c407bf18 100644 --- a/usr/src/lib/libshell/common/tests/exit.sh +++ b/usr/src/lib/libshell/common/tests/exit.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -78,4 +78,5 @@ then err_exit 'subshell trap on exit overwrites parent trap' fi cd ~- || err_exit "cd back failed" rm -r /tmp/ksh$$ || err_exit "rm -r /tmp/ksh$$ failed" +$SHELL -c 'builtin -f cmd getconf; getconf --"?-version"; exit 0' >/dev/null 2>&1 || err_exit 'ksh plugin exit failed -- was ksh built with CCFLAGS+=$(CC.EXPORT.DYNAMIC)?' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/expand.sh b/usr/src/lib/libshell/common/tests/expand.sh index 70018b1feb..5dd46bdd8f 100644 --- a/usr/src/lib/libshell/common/tests/expand.sh +++ b/usr/src/lib/libshell/common/tests/expand.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -37,6 +37,8 @@ set -- \ 'f{d,e,f}g' 'fdg feg ffg' \ '{l,n,m}xyz' 'lxyz nxyz mxyz' \ '{abc\,def}' '{abc,def}' \ + '{"abc,def"}' '{abc,def}' \ + "{'abc,def'}" '{abc,def}' \ '{abc}' '{abc}' \ '\{a,b,c,d,e}' '{a,b,c,d,e}' \ '{x,y,\{a,b,c}}' 'x} y} {a} b} c}' \ diff --git a/usr/src/lib/libshell/common/tests/functions.sh b/usr/src/lib/libshell/common/tests/functions.sh index fd45cd1767..0eaa17c688 100644 --- a/usr/src/lib/libshell/common/tests/functions.sh +++ b/usr/src/lib/libshell/common/tests/functions.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -27,6 +27,14 @@ alias err_exit='err_exit $LINENO' integer Errors=0 Command=${0##*/} + +tmp=/tmp/kshtf$$ +function cleanup +{ + rm -rf $tmp +} +mkdir $tmp || err_exit "mkdir $tmp failed" + integer foo=33 bar=bye # check for global variables and $0 @@ -81,11 +89,11 @@ function foobar { (return 0) } -> /tmp/shtests$$.1 +> $tmp/shtests$$.1 { foobar -if [ -r /tmp/shtests$$.1 ] -then rm -r /tmp/shtests$$.1 +if [ -r $tmp/shtests$$.1 ] +then rm -r $tmp/shtests$$.1 else err_exit 'return within subshell inside function error' fi } @@ -111,7 +119,7 @@ function foo x=2 ( x=3 - cd /tmp + cd $tmp print bar ) if [[ $x != 2 ]] @@ -130,19 +138,19 @@ fun() /bin/echo hello if [[ $(fun) != hello ]] then err_exit one line functions not working fi -trap 'rm -f /tmp/script$$ /tmp/data$$.[12]' EXIT -cat > /tmp/script$$ <<-\! +trap cleanup EXIT +cat > $tmp/script$$ <<-\! print -r -- "$1" ! -chmod +x /tmp/script$$ +chmod +x $tmp/script$$ function passargs { - /tmp/script$$ "$@" + $tmp/script$$ "$@" } if [[ $(passargs one) != one ]] then err_exit 'passing args from functions to scripts not working' fi -cat > /tmp/script$$ <<-\! +cat > $tmp/script$$ <<-\! trap 'exit 0' EXIT function foo { @@ -150,17 +158,17 @@ cat > /tmp/script$$ <<-\! } foo ! -if ! /tmp/script$$ +if ! $tmp/script$$ then err_exit 'exit trap incorrectly triggered' fi -if ! $SHELL -c /tmp/script$$ +if ! $SHELL -c $tmp/script$$ then err_exit 'exit trap incorrectly triggered when invoked with -c' fi -$SHELL -c "trap 'rm /tmp/script$$' EXIT" -if [[ -f /tmp/script$$ ]] +$SHELL -c "trap 'rm $tmp/script$$' EXIT" +if [[ -f $tmp/script$$ ]] then err_exit 'exit trap not triggered when invoked with -c' fi -cat > /tmp/script$$ <<- \EOF +cat > $tmp/script$$ <<- \EOF foobar() { return @@ -169,8 +177,8 @@ cat > /tmp/script$$ <<- \EOF foobar print -r -- "$1" EOF -chmod +x /tmp/script$$ -if [[ $( $SHELL /tmp/script$$ arg1 arg2) != arg2 ]] +chmod +x $tmp/script$$ +if [[ $( $SHELL $tmp/script$$ arg1 arg2) != arg2 ]] then err_exit 'arguments not restored by posix functions' fi function foo @@ -205,15 +213,15 @@ if [[ $(foo) != 3 ]] then err_exit 'variable assignment list not using parent scope' fi unset -f foo$$ -trap "rm -f /tmp/foo$$" EXIT INT -cat > /tmp/foo$$ <<! +#trap "rm -f $tmp/foo$$" EXIT INT +cat > $tmp/foo$$ <<! function foo$$ { print foo } ! -chmod +x /tmp/foo$$ -FPATH=/tmp +chmod +x $tmp/foo$$ +FPATH=$tmp autoload foo$$ if [[ $(foo$$ 2>/dev/null) != foo ]] then err_exit 'autoload not working' @@ -286,6 +294,8 @@ bad if [[ $val != false ]] then err_exit 'set -e not inherited for posix functions' fi +trap - ERR + function myexport { nameref var=$1 @@ -302,34 +312,40 @@ function myexport } export dgk=base -if [[ $(myexport dgk fun) != fun ]] -then err_exit 'export inside function not working' +val=$(myexport dgk fun) +if [[ $val != fun ]] +then err_exit "export inside function not working -- expected 'fun', got '$val'" fi -val=$(export | grep "^dgk=") -if [[ ${val#dgk=} != base ]] -then err_exit 'export not restored after function call' +val=$(export | sed -e '/^dgk=/!d' -e 's/^dgk=//') +if [[ $val != base ]] +then err_exit "export not restored after function call -- expected 'base', got '$val'" fi -if [[ $(myexport dgk fun fun2) != fun2 ]] -then err_exit 'export inside function not working with recursive function' +val=$(myexport dgk fun fun2) +if [[ $val != fun2 ]] +then err_exit "export inside function not working with recursive function -- expected 'fun2', got '$val'" fi -val=$(export | grep "^dgk=") -if [[ ${val#dgk=} != base ]] -then err_exit 'export not restored after recursive function call' +val=$(export | sed -e '/^dgk=/!d' -e 's/^dgk=//') +if [[ $val != base ]] +then err_exit "export not restored after recursive function call -- expected 'base', got '$val'" fi -if [[ $(dgk=try3 myexport dgk) != try3 ]] -then err_exit 'name=value not added to export list with function call' +val=$(dgk=try3 myexport dgk) +if [[ $val != try3 ]] +then err_exit "name=value not added to export list with function call -- expected 'try3', got '$val'" fi -val=$(export | grep "^dgk=") -if [[ ${val#dgk=} != base ]] -then err_exit 'export not restored name=value function call' +val=$(export | sed -e '/^dgk=/!d' -e 's/^dgk=//') +if [[ $val != base ]] +then err_exit "export not restored name=value function call -- expected 'base', got '$val'" fi unset zzz -if [[ $(myexport zzz fun) != fun ]] -then err_exit 'export inside function not working for zzz' +val=$(myexport zzz fun) +if [[ $val != fun ]] +then err_exit "export inside function not working -- expected 'fun', got '$val'" fi -if [[ $(export | grep "zzz=") ]] -then err_exit 'zzz exported after function call' +val=$(export | sed -e '/^zzz=/!d' -e 's/^zzz=//') +if [[ $val ]] +then err_exit "unset varaible exported after function call -- expected '', got '$val'" fi + unset zzz typeset -u zzz function foo @@ -368,8 +384,8 @@ function closure return $r } closure 0 || err_exit -u2 'for loop function optimization bug2' -mkdir /tmp/ksh$$ || err_exit "mkdir /tmp/ksh$$ failed" -cd /tmp/ksh$$ || err_exit "cd /tmp/ksh$$ failed" +mkdir $tmp/ksh$$ || err_exit "mkdir $tmp/ksh$$ failed" +cd $tmp/ksh$$ || err_exit "cd $tmp/ksh$$ failed" print 'false' > try chmod +x try cat > tst <<- EOF @@ -384,9 +400,9 @@ EOF if [[ $($SHELL < tst) == error ]] then err_exit 'ERR trap not cleared' fi -FPATH=/tmp/ksh$$ -print ': This does nothing' > /tmp/ksh$$/foobar -chmod +x /tmp/ksh$$/foobar +FPATH=$tmp/ksh$$ +print ': This does nothing' > $tmp/ksh$$/foobar +chmod +x $tmp/ksh$$/foobar unset -f foobar { foobar;} 2> /dev/null if [[ $? != 126 ]] @@ -395,7 +411,7 @@ fi print 'set a b c' > dotscript [[ $(PATH=$PATH: $SHELL -c '. dotscript;print $#') == 3 ]] || err_exit 'positional parameters not preserved with . script without arguments' cd ~- || err_exit "cd back failed" -cd /; rm -r /tmp/ksh$$ || err_exit "rm -r /tmp/ksh$$ failed" +cd /; rm -r $tmp/ksh$$ || err_exit "rm -r $tmp/ksh$$ failed" function errcheck { trap 'print ERR; return 1' ERR @@ -423,7 +439,7 @@ a() b() { : ;} [[ $(a) == a ]] || err_exit '.sh.fun not set correctly in a function' print $'a(){\ndate\n}' | $SHELL 2> /dev/null || err_exit 'parser error in a(){;date;}' -cat > /tmp/data$$.1 << '++EOF' +cat > $tmp/data$$.1 << '++EOF' 1 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 3 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -445,7 +461,7 @@ cat > /tmp/data$$.1 << '++EOF' 19 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 20 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ++EOF -cat > /tmp/script$$ << '++EOF' +cat > $tmp/script$$ << '++EOF' # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -711,11 +727,10 @@ f() { cat <<\M ++EOF -cat /tmp/data$$.1 >> /tmp/script$$ -printf 'M\n}\n\nf\n\n' >> /tmp/script$$ -$SHELL -c /tmp/script$$ > /tmp/data$$.2 -cmp -s /tmp/data$$.[12] || err_exit 'error with long functions' -rm -f /tmp/script$$ /tmp/data$$.[12] +cat $tmp/data$$.1 >> $tmp/script$$ +printf 'M\n}\n\nf\n\n' >> $tmp/script$$ +$SHELL -c $tmp/script$$ > $tmp/data$$.2 +cmp -s $tmp/data$$.[12] || err_exit 'error with long functions' v=1 function f { @@ -755,4 +770,212 @@ function foo done } [[ $(foo 'NUMBERED RECORDSIZE') == ok ]] || err_exit 'optimization error with undefined variable' +unset x +x=$( + set -e + integer count=0 + function err_f + { + if ((count++==3)) + then print failed + else false + fi + } + trap 'err_f' ERR + false +) +[[ $x == failed ]] && err_exit 'ERR trap executed multiple times' +trap cleanup EXIT +export environment +typeset global +function f +{ + typeset i t local + + for i + do case $i in + [-+]*) set "$@" + continue + ;; + local) local=f + t=$(typeset +f $local) + ;; + global) global=f + t=$(typeset +f $global) + ;; + environment) + environment=f + t=$(typeset +f $environment) + ;; + literal)t=$(typeset +f f) + ;; + positional) + set -- f + t=$(typeset +f $1) + ;; + esac + [[ $t ]] || err_exit "typeset +f \$$i failed" + done +} +f local global environment literal positional +$SHELL -c ' + print exit 0 > '$tmp'/script$$ + chmod +x '$tmp'/script$$ + unset var + var=( ident=1 ) + function fun + { + PATH='$tmp' script$$ + } + fun +' || err_exit "compound variable cleanup before script exec failed" +( $SHELL << \++EOF++ +function main +{ + typeset key + typeset -A entry + entry[a]=( value=aaa ) +} +main +++EOF++ +) 2> /dev/null || err_exit 'function main fails' +optind=$OPTIND +sub() +{ + ( + OPTIND=1 + while getopts :abc OPTION "$@" + do print OPTIND=$OPTIND + done + ) +} +[[ $(sub -a) == OPTIND=2 ]] || err_exit 'OPTIND should be 2' +[[ $(sub -a) == OPTIND=2 ]] || err_exit 'OPTIND should be 2 again' +[[ $OPTIND == "$optind" ]] || err_exit 'OPTIND should be 1' + +function bar +{ + [[ -o nounset ]] && err_exit 'nounset option should not be inherited' +} +function foo +{ + set -o nounset + bar +} +set +o nounset +foo +function red +{ + integer -S d=0 + printf 'red_one %d\n' d + (( d++ )) + return 0 +} +[[ ${ red } != 'red_one 0' ]] && err_exit 'expected red_one 0' +[[ ${ red } != 'red_one 1' ]] && err_exit 'expected red_one 1' +xyz=$0 +function traceback +{ + integer .level=.sh.level + while((--.level>=0)) + do + ((.sh.level = .level)) + [[ $xyz == "$0" ]] || err_exit "\$xyz=$xyz does not match $0 on level ${.level}" + [[ ${.sh.lineno} == "$1" ]] || err_exit "\${.sh.lineno}=${.sh.lineno} does not match $1 on level ${.level}" + done +} + +function foo +{ + typeset xyz=foo + set -- $((LINENO+1)) + bar $LINENO "$1" +} + +function bar +{ + typeset xyz=bar + set -- $((LINENO+2)) + trap 'traceback $LINENO' DEBUG + : $LINENO "$1" +} + +set -- $((LINENO+1)) +foo $LINENO +function .sh.fun.set +{ + print -r -- "${.sh.value}" +} +function abc +{ + : +} +def() +{ + : +} +[[ $(abc) == abc ]] || err_exit '.sh.fun.set not capturing function name' +[[ $(def) == def ]] || err_exit '.sh.fun.set not capturing name()' +unset -f .sh.fun.set + +# tests for debug functions +basefile=${.sh.file} +integer baseline +cleanup +trap 'rm $tmp' EXIT +tmp=${TMPDIR:-/tmp}/ksh$$.1 +cat > $tmp << \+++ + : line 1 + + : line 3 ++++ +# Print one line in a call stack +function _Dbg_print_frame +{ + typeset -i pos=$1 + typeset fn=$2 + typeset filename="$3" + typeset -i line=$4 + typeset arg=$5 + shift 5 + if ((pos==0)) + then [[ $filename == "$basefile" ]] || err_exit "filename for level 0 is $filename not $basename" + [[ $arg == DEBUG ]] && ((baseline++)) + [[ $line == "$baseline" ]] || err_exit "line number for level 0 is $line not $baseline" + elif ((pos==1)) + then [[ $filename == "$tmp" ]] || err_exit "filename for level 1 is $filename not $tmp" + [[ $* == 'foo bar' ]] || err_exit "args are '$*', not 'foo bar'" + [[ $line == $arg ]] || err_exit "line number for level 1 is $line not $arg" + else err_exit "level should be 0 or 1 but is $pos" + fi +} + +function _Dbg_debug_trap_handler +{ + + integer .level=.sh.level .max=.sh.level-1 + while((--.level>=0)) + do + ((.sh.level = .level)) + _Dbg_print_frame "${.level}" "$0" "${.sh.file}" "${.sh.lineno}" "${.sh.command##* }" "$@" + done +} + +((baseline=LINENO+2)) +trap '_Dbg_debug_trap_handler' DEBUG +. $tmp foo bar +trap '' DEBUG + +caller() { + integer .level=.sh.level .max=.sh.level-1 + while((--.level>=0)) + do + ((.sh.level = .level)) + print -r -- "${.sh.lineno}" + done +} +bar() { caller;} +set -- $(bar) +[[ $1 == $2 ]] && err_exit ".sh.inline optimization bug" +( $SHELL -c ' function foo { typeset x=$1;print $1;};z=();z=($(foo bar)) ') 2> /dev/null || err_exit 'using a function to set an array in a command sub fails' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/glob.sh b/usr/src/lib/libshell/common/tests/glob.sh index 4d5fa0b519..2c3ccd09d3 100644 --- a/usr/src/lib/libshell/common/tests/glob.sh +++ b/usr/src/lib/libshell/common/tests/glob.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -19,8 +19,7 @@ ######################################################################## function err_exit { - print -u2 -n "\t" - print -u2 -r ${Command}[$1]: "${@:2}" + print -u2 -r $'\t'"${Command}[$1] ${@:2}" ((errors++)) } alias err_exit='err_exit $LINENO' @@ -30,6 +29,8 @@ integer aware=0 contrary=0 ignorant=0 function test_glob { typeset lineno expected drop arg got sep op val add del + lineno=$1 + shift if [[ $1 == --* ]] then del=${1#--} shift @@ -38,8 +39,8 @@ function test_glob then add=${1#++} shift fi - lineno=$1 expected=$2 - shift 2 + expected=$1 + shift if (( contrary )) then if [[ $expected == "<Beware> "* ]] then expected=${expected#"<Beware> "} @@ -62,9 +63,10 @@ function test_glob fi fi if [[ $got != "$expected" ]] - then err_exit $lineno "glob: got '$got' expected '$expected'" + then 'err_exit' $lineno "glob -- expected '$expected', got '$got'" fi } +alias test_glob='test_glob $LINENO' function test_case { @@ -77,9 +79,10 @@ function test_case esac " if [[ $got != "$expected" ]] - then err_exit $lineno "case $subject in $pattern) got '$got' expected '$expected'" + then 'err_exit' $lineno "case $subject in $pattern) -- expected '$expected', got '$got'" fi } +alias test_case='test_case $LINENO' Command=${0##*/} tmp=/tmp/ksh$$ @@ -88,9 +91,9 @@ unset undefined export LC_COLLATE=C -mkdir $tmp || err_exit $LINENO "mkdir $tmp failed" +mkdir $tmp || err_exit "mkdir $tmp failed" trap "cd /; rm -rf $tmp" EXIT -cd $tmp || err_exit $LINENO "cd $tmp failed" +cd $tmp || err_exit "cd $tmp failed" rm -rf * touch B b @@ -108,14 +111,14 @@ rm -rf * touch a b c d abc abd abe bb bcd ca cb dd de Beware mkdir bdir -test_glob $LINENO '<a> <abc> <abd> <abe> <X*>' a* X* -test_glob $LINENO '<a> <abc> <abd> <abe>' \a* +test_glob '<a> <abc> <abd> <abe> <X*>' a* X* +test_glob '<a> <abc> <abd> <abe>' \a* if ( set --nullglob ) 2>/dev/null then set --nullglob - test_glob $LINENO '<a> <abc> <abd> <abe>' a* X* + test_glob '<a> <abc> <abd> <abe>' a* X* set --nonullglob fi @@ -126,54 +129,54 @@ then mkdir tmp touch tmp/l1 tmp/l2 tmp/l3 - test_glob $LINENO '' tmp/l[12] tmp/*4 tmp/*3 - test_glob $LINENO '' tmp/l[12] tmp/*4 tmp/*3 + test_glob '' tmp/l[12] tmp/*4 tmp/*3 + test_glob '' tmp/l[12] tmp/*4 tmp/*3 rm -r tmp set --nofailglob fi -test_glob $LINENO '<bdir/>' b*/ -test_glob $LINENO '<*>' \* -test_glob $LINENO '<a*>' 'a*' -test_glob $LINENO '<a*>' a\* -test_glob $LINENO '<c> <ca> <cb> <a*> <*q*>' c* a\* *q* -test_glob $LINENO '<**>' "*"* -test_glob $LINENO '<**>' \** -test_glob $LINENO '<\.\./*/>' "\.\./*/" -test_glob $LINENO '<s/\..*//>' 's/\..*//' -test_glob $LINENO '</^root:/{s/^[!:]*:[!:]*:\([!:]*\).*$/\1/>' "/^root:/{s/^[!:]*:[!:]*:\([!:]*\).*"'$'"/\1/" -test_glob $LINENO '<abc> <abd> <abe> <bb> <cb>' [a-c]b* -test_glob ++Beware $LINENO '<abd> <abe> <bb> <bcd> <bdir> <ca> <cb> <dd> <de>' [a-y]*[!c] -test_glob $LINENO '<abd> <abe>' a*[!c] +test_glob '<bdir/>' b*/ +test_glob '<*>' \* +test_glob '<a*>' 'a*' +test_glob '<a*>' a\* +test_glob '<c> <ca> <cb> <a*> <*q*>' c* a\* *q* +test_glob '<**>' "*"* +test_glob '<**>' \** +test_glob '<\.\./*/>' "\.\./*/" +test_glob '<s/\..*//>' 's/\..*//' +test_glob '</^root:/{s/^[!:]*:[!:]*:\([!:]*\).*$/\1/>' "/^root:/{s/^[!:]*:[!:]*:\([!:]*\).*"'$'"/\1/" +test_glob '<abc> <abd> <abe> <bb> <cb>' [a-c]b* +test_glob ++Beware '<abd> <abe> <bb> <bcd> <bdir> <ca> <cb> <dd> <de>' [a-y]*[!c] +test_glob '<abd> <abe>' a*[!c] touch a-b aXb -test_glob $LINENO '<a-b> <aXb>' a[X-]b +test_glob '<a-b> <aXb>' a[X-]b touch .x .y -test_glob --Beware $LINENO '<Beware> <d> <dd> <de>' [!a-c]* +test_glob --Beware '<Beware> <d> <dd> <de>' [!a-c]* if mkdir a\*b 2>/dev/null then touch a\*b/ooo - test_glob $LINENO '<a*b/ooo>' a\*b/* - test_glob $LINENO '<a*b/ooo>' a\*?/* - test_case $LINENO '<match>' '!7' '*\!*' - test_case $LINENO '<match>' 'r.*' '*.\*' - test_glob $LINENO '<abc>' a[b]c - test_glob $LINENO '<abc>' a["b"]c - test_glob $LINENO '<abc>' a[\b]c - test_glob $LINENO '<abc>' a?c - test_case $LINENO '<match>' 'abc' 'a"b"c' - test_case $LINENO '<match>' 'abc' 'a*c' - test_case $LINENO '<nomatch>' 'abc' '"a?c"' - test_case $LINENO '<nomatch>' 'abc' 'a\*c' - test_case $LINENO '<nomatch>' 'abc' 'a\[b]c' - test_case $LINENO '<match>' '"$undefined"' '""' - test_case $LINENO '<match>' 'abc' 'a["\b"]c' + test_glob '<a*b/ooo>' a\*b/* + test_glob '<a*b/ooo>' a\*?/* + test_case '<match>' '!7' '*\!*' + test_case '<match>' 'r.*' '*.\*' + test_glob '<abc>' a[b]c + test_glob '<abc>' a["b"]c + test_glob '<abc>' a[\b]c + test_glob '<abc>' a?c + test_case '<match>' 'abc' 'a"b"c' + test_case '<match>' 'abc' 'a*c' + test_case '<nomatch>' 'abc' '"a?c"' + test_case '<nomatch>' 'abc' 'a\*c' + test_case '<nomatch>' 'abc' 'a\[b]c' + test_case '<match>' '"$undefined"' '""' + test_case '<match>' 'abc' 'a["\b"]c' rm -rf mkdir a\*b fi @@ -182,68 +185,68 @@ mkdir man mkdir man/man1 touch man/man1/sh.1 -test_glob $LINENO '<man/man1/sh.1>' */man*/sh.* -test_glob $LINENO '<man/man1/sh.1>' $(echo */man*/sh.*) -test_glob $LINENO '<man/man1/sh.1>' "$(echo */man*/sh.*)" - -test_case $LINENO '<match>' 'abc' 'a***c' -test_case $LINENO '<match>' 'abc' 'a*****?c' -test_case $LINENO '<match>' 'abc' '?*****??' -test_case $LINENO '<match>' 'abc' '*****??' -test_case $LINENO '<match>' 'abc' '*****??c' -test_case $LINENO '<match>' 'abc' '?*****?c' -test_case $LINENO '<match>' 'abc' '?***?****c' -test_case $LINENO '<match>' 'abc' '?***?****?' -test_case $LINENO '<match>' 'abc' '?***?****' -test_case $LINENO '<match>' 'abc' '*******c' -test_case $LINENO '<match>' 'abc' '*******?' -test_case $LINENO '<match>' 'abcdecdhjk' 'a*cd**?**??k' -test_case $LINENO '<match>' 'abcdecdhjk' 'a**?**cd**?**??k' -test_case $LINENO '<match>' 'abcdecdhjk' 'a**?**cd**?**??k***' -test_case $LINENO '<match>' 'abcdecdhjk' 'a**?**cd**?**??***k' -test_case $LINENO '<match>' 'abcdecdhjk' 'a**?**cd**?**??***k**' -test_case $LINENO '<match>' 'abcdecdhjk' 'a****c**?**??*****' -test_case $LINENO '<match>' "'-'" '[-abc]' -test_case $LINENO '<match>' "'-'" '[abc-]' -test_case $LINENO '<match>' "'\\'" '\\' -test_case $LINENO '<match>' "'\\'" '[\\]' -test_case $LINENO '<match>' "'\\'" "'\\'" -test_case $LINENO '<match>' "'['" '[[]' -test_case $LINENO '<match>' '[' '[[]' -test_case $LINENO '<match>' "'['" '[' -test_case $LINENO '<match>' '[' '[' -test_case $LINENO '<match>' "'[abc'" "'['*" -test_case $LINENO '<nomatch>' "'[abc'" '[*' -test_case $LINENO '<match>' '[abc' "'['*" -test_case $LINENO '<nomatch>' '[abc' '[*' -test_case $LINENO '<match>' 'abd' "a[b/c]d" -test_case $LINENO '<match>' 'a/d' "a[b/c]d" -test_case $LINENO '<match>' 'acd' "a[b/c]d" -test_case $LINENO '<match>' "']'" '[]]' -test_case $LINENO '<match>' "'-'" '[]-]' -test_case $LINENO '<match>' 'p' '[a-\z]' -test_case $LINENO '<match>' '"/tmp"' '[/\\]*' -test_case $LINENO '<nomatch>' 'abc' '??**********?****?' -test_case $LINENO '<nomatch>' 'abc' '??**********?****c' -test_case $LINENO '<nomatch>' 'abc' '?************c****?****' -test_case $LINENO '<nomatch>' 'abc' '*c*?**' -test_case $LINENO '<nomatch>' 'abc' 'a*****c*?**' -test_case $LINENO '<nomatch>' 'abc' 'a********???*******' -test_case $LINENO '<nomatch>' "'a'" '[]' -test_case $LINENO '<nomatch>' 'a' '[]' -test_case $LINENO '<nomatch>' "'['" '[abc' -test_case $LINENO '<nomatch>' '[' '[abc' - -test_glob ++Beware $LINENO '<b> <bb> <bcd> <bdir>' b* -test_glob $LINENO '<Beware> <b> <bb> <bcd> <bdir>' [bB]* +test_glob '<man/man1/sh.1>' */man*/sh.* +test_glob '<man/man1/sh.1>' $(echo */man*/sh.*) +test_glob '<man/man1/sh.1>' "$(echo */man*/sh.*)" + +test_case '<match>' 'abc' 'a***c' +test_case '<match>' 'abc' 'a*****?c' +test_case '<match>' 'abc' '?*****??' +test_case '<match>' 'abc' '*****??' +test_case '<match>' 'abc' '*****??c' +test_case '<match>' 'abc' '?*****?c' +test_case '<match>' 'abc' '?***?****c' +test_case '<match>' 'abc' '?***?****?' +test_case '<match>' 'abc' '?***?****' +test_case '<match>' 'abc' '*******c' +test_case '<match>' 'abc' '*******?' +test_case '<match>' 'abcdecdhjk' 'a*cd**?**??k' +test_case '<match>' 'abcdecdhjk' 'a**?**cd**?**??k' +test_case '<match>' 'abcdecdhjk' 'a**?**cd**?**??k***' +test_case '<match>' 'abcdecdhjk' 'a**?**cd**?**??***k' +test_case '<match>' 'abcdecdhjk' 'a**?**cd**?**??***k**' +test_case '<match>' 'abcdecdhjk' 'a****c**?**??*****' +test_case '<match>' "'-'" '[-abc]' +test_case '<match>' "'-'" '[abc-]' +test_case '<match>' "'\\'" '\\' +test_case '<match>' "'\\'" '[\\]' +test_case '<match>' "'\\'" "'\\'" +test_case '<match>' "'['" '[[]' +test_case '<match>' '[' '[[]' +test_case '<match>' "'['" '[' +test_case '<match>' '[' '[' +test_case '<match>' "'[abc'" "'['*" +test_case '<nomatch>' "'[abc'" '[*' +test_case '<match>' '[abc' "'['*" +test_case '<nomatch>' '[abc' '[*' +test_case '<match>' 'abd' "a[b/c]d" +test_case '<match>' 'a/d' "a[b/c]d" +test_case '<match>' 'acd' "a[b/c]d" +test_case '<match>' "']'" '[]]' +test_case '<match>' "'-'" '[]-]' +test_case '<match>' 'p' '[a-\z]' +test_case '<match>' '"/tmp"' '[/\\]*' +test_case '<nomatch>' 'abc' '??**********?****?' +test_case '<nomatch>' 'abc' '??**********?****c' +test_case '<nomatch>' 'abc' '?************c****?****' +test_case '<nomatch>' 'abc' '*c*?**' +test_case '<nomatch>' 'abc' 'a*****c*?**' +test_case '<nomatch>' 'abc' 'a********???*******' +test_case '<nomatch>' "'a'" '[]' +test_case '<nomatch>' 'a' '[]' +test_case '<nomatch>' "'['" '[abc' +test_case '<nomatch>' '[' '[abc' + +test_glob ++Beware '<b> <bb> <bcd> <bdir>' b* +test_glob '<Beware> <b> <bb> <bcd> <bdir>' [bB]* if ( set --nocaseglob ) 2>/dev/null then set --nocaseglob - test_glob $LINENO '<Beware> <b> <bb> <bcd> <bdir>' b* - test_glob $LINENO '<Beware> <b> <bb> <bcd> <bdir>' [b]* - test_glob $LINENO '<Beware> <b> <bb> <bcd> <bdir>' [bB]* + test_glob '<Beware> <b> <bb> <bcd> <bdir>' b* + test_glob '<Beware> <b> <bb> <bcd> <bdir>' [b]* + test_glob '<Beware> <b> <bb> <bcd> <bdir>' [bB]* set --nonocaseglob fi @@ -252,7 +255,7 @@ if ( set -f ) 2>/dev/null then set -f - test_glob $LINENO '<*>' * + test_glob '<*>' * set +f fi @@ -261,43 +264,114 @@ if ( set --noglob ) 2>/dev/null then set --noglob - test_glob $LINENO '<*>' * + test_glob '<*>' * set --glob fi FIGNORE='.*|*' -test_glob $LINENO '<*>' * +test_glob '<*>' * FIGNORE='.*|*c|*e|?' -test_glob $LINENO '<a-b> <aXb> <abd> <bb> <bcd> <bdir> <ca> <cb> <dd> <man>' * +test_glob '<a-b> <aXb> <abd> <bb> <bcd> <bdir> <ca> <cb> <dd> <man>' * FIGNORE='.*|*b|*d|?' -test_glob $LINENO '<Beware> <abc> <abe> <bdir> <ca> <de> <man>' * +test_glob '<Beware> <abc> <abe> <bdir> <ca> <de> <man>' * FIGNORE= -test_glob $LINENO '<man/man1/sh.1>' */man*/sh.* +test_glob '<man/man1/sh.1>' */man*/sh.* unset FIGNORE -test_glob $LINENO '<bb> <ca> <cb> <dd> <de>' ?? -test_glob $LINENO '<man/man1/sh.1>' */man*/sh.* +test_glob '<bb> <ca> <cb> <dd> <de>' ?? +test_glob '<man/man1/sh.1>' */man*/sh.* GLOBIGNORE='.*:*' set -- * if [[ $1 == '*' ]] then GLOBIGNORE='.*:*c:*e:?' - test_glob $LINENO '<>' * + test_glob '<>' * GLOBIGNORE='.*:*b:*d:?' - test_glob $LINENO '<>' * + test_glob '<>' * unset GLOBIGNORE - test_glob $LINENO '<>' * - test_glob $LINENO '<man/man1/sh.1>' */man*/sh.* + test_glob '<>' * + test_glob '<man/man1/sh.1>' */man*/sh.* GLOBIGNORE= - test_glob $LINENO '<man/man1/sh.1>' */man*/sh.* + test_glob '<man/man1/sh.1>' */man*/sh.* fi +unset GLOBIGNORE + +function test_sub +{ + x='${subject'$2'}' + eval g=$x + if [[ "$g" != "$3" ]] + then 'err_exit' $1 subject="'$subject' $x failed, expected '$3', got '$g'" + fi +} +alias test_sub='test_sub $LINENO' + +set --noglob --nobraceexpand + +subject='A regular expressions test' + +test_sub '/e/#' 'A r#gular expressions test' +test_sub '//e/#' 'A r#gular #xpr#ssions t#st' +test_sub '/[^e]/#' '# regular expressions test' +test_sub '//[^e]/#' '###e######e###e########e##' +test_sub '/+(e)/#' 'A r#gular expressions test' +test_sub '//+(e)/#' 'A r#gular #xpr#ssions t#st' +test_sub '/@-(e)/#' 'A r#gular expressions test' +test_sub '//@-(e)/#' 'A r#gular #xpr#ssions t#st' +test_sub '/?(e)/#' '#A regular expressions test' +test_sub '//?(e)/#' '#A# #r#g#u#l#a#r# #x#p#r#s#s#i#o#n#s# #t#s#t#' +test_sub '/*(e)/#' '#A regular expressions test' +test_sub '//*(e)/#' '#A# #r#g#u#l#a#r# #x#p#r#s#s#i#o#n#s# #t#s#t#' +test_sub '//@(e)/[\1]' 'A r[e]gular [e]xpr[e]ssions t[e]st' +test_sub '//@-(e)/[\1]' 'A r[e]gular [e]xpr[e]ssions t[e]st' +test_sub '//+(e)/[\1]' 'A r[e]gular [e]xpr[e]ssions t[e]st' +test_sub '//+-(e)/[\1]' 'A r[e]gular [e]xpr[e]ssions t[e]st' +test_sub '//@(+(e))/[\1]' 'A r[e]gular [e]xpr[e]ssions t[e]st' +test_sub '//@(+-(e))/[\1]' 'A r[e]gular [e]xpr[e]ssions t[e]st' +test_sub '//-(e)/#' 'A regular expressions test' +test_sub '//--(e)/#' 'A regular expressions test' +test_sub '//?(e)/[\1]' '[]A[] []r[e]g[]u[]l[]a[]r[] [e]x[]p[]r[e]s[]s[]i[]o[]n[]s[] []t[e]s[]t[]' +test_sub '//{0,1}(e)/[\1]' '[]A[] []r[e]g[]u[]l[]a[]r[] [e]x[]p[]r[e]s[]s[]i[]o[]n[]s[] []t[e]s[]t[]' +test_sub '//*(e)/[\1]' '[]A[] []r[e]g[]u[]l[]a[]r[] [e]x[]p[]r[e]s[]s[]i[]o[]n[]s[] []t[e]s[]t[]' +test_sub '//{0,}(e)/[\1]' '[]A[] []r[e]g[]u[]l[]a[]r[] [e]x[]p[]r[e]s[]s[]i[]o[]n[]s[] []t[e]s[]t[]' +test_sub '//@(?(e))/[\1]' '[]A[] []r[e]g[]u[]l[]a[]r[] [e]x[]p[]r[e]s[]s[]i[]o[]n[]s[] []t[e]s[]t[]' +test_sub '//@({0,1}(e))/[\1]' '[]A[] []r[e]g[]u[]l[]a[]r[] [e]x[]p[]r[e]s[]s[]i[]o[]n[]s[] []t[e]s[]t[]' +test_sub '//@(*(e))/[\1]' '[]A[] []r[e]g[]u[]l[]a[]r[] [e]x[]p[]r[e]s[]s[]i[]o[]n[]s[] []t[e]s[]t[]' +test_sub '//@({0,}(e))/[\1]' '[]A[] []r[e]g[]u[]l[]a[]r[] [e]x[]p[]r[e]s[]s[]i[]o[]n[]s[] []t[e]s[]t[]' +test_sub '/?-(e)/#' '#A regular expressions test' +test_sub '/@(?-(e))/[\1]' '[]A regular expressions test' +test_sub '/!(e)/#' '#' +test_sub '//!(e)/#' '#' +test_sub '/@(!(e))/[\1]' '[A regular expressions test]' +test_sub '//@(!(e))/[\1]' '[A regular expressions test]' + +subject='e' + +test_sub '/!(e)/#' '#e' +test_sub '//!(e)/#' '#e#' +test_sub '/!(e)/[\1]' '[]e' +test_sub '//!(e)/[\1]' '[]e[]' +test_sub '/@(!(e))/[\1]' '[]e' +test_sub '//@(!(e))/[\1]' '[]e[]' + +subject='a' + +test_sub '/@(!(a))/[\1]' '[]a' +test_sub '//@(!(a))/[\1]' '[]a[]' + +subject='aha' + +test_sub '/@(!(a))/[\1]' '[aha]' +test_sub '//@(!(a))/[\1]' '[aha]' +test_sub '/@(!(aha))/[\1]' '[ah]a' +test_sub '//@(!(aha))/[\1]' '[ah][a]' exit $errors diff --git a/usr/src/lib/libshell/common/tests/grep.sh b/usr/src/lib/libshell/common/tests/grep.sh index db20206dbf..82c0e13cac 100644 --- a/usr/src/lib/libshell/common/tests/grep.sh +++ b/usr/src/lib/libshell/common/tests/grep.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # diff --git a/usr/src/lib/libshell/common/tests/heredoc.sh b/usr/src/lib/libshell/common/tests/heredoc.sh index 53835f2007..aadc9cdeec 100644 --- a/usr/src/lib/libshell/common/tests/heredoc.sh +++ b/usr/src/lib/libshell/common/tests/heredoc.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -210,4 +210,20 @@ printf $'cat <<# \\!!!\n\thello\n\t\tworld\n!!!' > $f [[ $($SHELL "$f") == $'hello\n\tworld' ]] || err_exit "<<# not working for quoted here documents" printf $'w=world;cat <<# !!!\n\thello\n\t\t$w\n!!!' > $f [[ $($SHELL "$f") == $'hello\n\tworld' ]] || err_exit "<<# not working for non-quoted here documents" +[[ $( $SHELL <<- \++++ + S=( typeset a ) + function S.a.get + { + .sh.value=$__a + } + __a=1234 + cat <<-EOF + ${S.a} + EOF +++++ +) == 1234 ]] 2> /dev/null || err_exit 'here document with get discipline failed' +[[ $($SHELL -c 'g(){ print ok;}; cat <<- EOF + ${ g;} + EOF + ' 2> /dev/null) == ok ]] || err_exit '${ command;} not working in heredoc' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/io.sh b/usr/src/lib/libshell/common/tests/io.sh index 649520482a..a8b1b8a030 100644 --- a/usr/src/lib/libshell/common/tests/io.sh +++ b/usr/src/lib/libshell/common/tests/io.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -27,7 +27,9 @@ alias err_exit='err_exit $LINENO' Command=${0##*/} integer Errors=0 -# cut here + +unset HISTFILE + function fun { while command exec 3>&1 @@ -227,8 +229,20 @@ then (( $(3<#) == 0 )) || err_exit "not at position 0" read -u3 && err_exit "not found pattern not positioning at eof" cat /tmp/seek$$ | read -r <# *WWW* [[ $REPLY == *WWWWW* ]] || err_exit '<# not working for pipes' + { < /tmp/seek$$ <# ((2358336120)) ;} 2> /dev/null || err_exit 'long seek not working' else err_exit "/tmp/seek$$: cannot open for reading" fi +command exec 3<&- || 'cannot close 3' +for ((i=0; i < 62; i++)) +do printf "%.39c\n" ${x:i:1} +done > /tmp/seek$$ +if command exec {n}<> /tmp/seek$$ +then { command exec {n}<#((EOF)) ;} 2> /dev/null || err_exit '{n}<# not working' + if $SHELL -c '{n}</dev/null' 2> /dev/null + then (( $({n}<#) == 40*62)) || err_exit '$({n}<#) not working' + else err_exit 'not able to parse {n}</dev/null' + fi +fi trap "" EXIT rm -f /tmp/seek$$ $SHELL -ic ' @@ -248,4 +262,112 @@ trap 'rm -rf /tmp/io.sh$$*' EXIT $SHELL -c "{ > /tmp/io.sh$$.1 ; date;} >&- 2> /dev/null" > /tmp/io.sh$$.2 [[ -s /tmp/io.sh$$.1 || -s /tmp/io.sh$$.2 ]] && err_exit 'commands with standard output closed produce output' $SHELL -c "$SHELL -c ': 3>&1' 1>&- 2>/dev/null" && err_exit 'closed standard output not passed to subshell' +[[ $(cat <<- \EOF | $SHELL + do_it_all() + { + dd 2>/dev/null # not a ksh93 buildin + return $? + } + do_it_all ; exit $? + hello world +EOF) == 'hello world' ]] || err_exit 'invalid readahead on stdin' +$SHELL -c 'exec 3>; /dev/null' 2> /dev/null && err_exit '>; with exec should be an error' +$SHELL -c ': 3>; /dev/null' 2> /dev/null || err_exit '>; not working with at all' +print hello > /tmp/io.sh$$.1 +if ! $SHELL -c "false >; /tmp/io.sh$$.1" 2> /dev/null +then [[ $(</tmp/io.sh$$.1) == hello ]] || err_exit '>; not preserving file on failure' +fi +if ! $SHELL -c "sed -e 's/hello/hello world/' /tmp/io.sh$$.1" >; /tmp/io.sh$$.1 2> /dev/null +then [[ $(</tmp/io.sh$$.1) == 'hello world' ]] || err_exit '>; not updating file on success' +fi + +unset y +read -n1 y <<! +abc +! +if [[ $y != a ]] +then err_exit 'read -n1 not working' +fi +unset a +{ read -N3 a; read -N1 b;} <<! +abcdefg +! +[[ $a == abc ]] || err_exit 'read -N3 here-document not working' +[[ $b == d ]] || err_exit 'read -N1 here-document not working' +read -n3 a <<! +abcdefg +! +[[ $a == abc ]] || err_exit 'read -n3 here-document not working' +(print -n a;sleep 1; print -n bcde) | { read -N3 a; read -N1 b;} +[[ $a == abc ]] || err_exit 'read -N3 from pipe not working' +[[ $b == d ]] || err_exit 'read -N1 from pipe not working' +(print -n a;sleep 1; print -n bcde) |read -n3 a +[[ $a == a ]] || err_exit 'read -n3 from pipe not working' +rm -f /tmp/fifo$$ +if mkfifo /tmp/fifo$$ 2> /dev/null +then (print -n a; sleep 1;print -n bcde) > /tmp/fifo$$ & + { + read -u5 -n3 -t2 a || err_exit 'read -n3 from fifo timedout' + read -u5 -n1 -t2 b || err_exit 'read -n1 from fifo timedout' + } 5< /tmp/fifo$$ + [[ $a == a ]] || err_exit 'read -n3 from fifo not working' + rm -f /tmp/fifo$$ + mkfifo /tmp/fifo$$ 2> /dev/null + (print -n a; sleep 1;print -n bcde) > /tmp/fifo$$ & + { + read -u5 -N3 -t2 a || err_exit 'read -N3 from fifo timed out' + read -u5 -N1 -t2 b || err_exit 'read -N1 from fifo timedout' + } 5< /tmp/fifo$$ + [[ $a == abc ]] || err_exit 'read -N3 from fifo not working' + [[ $b == d ]] || err_exit 'read -N1 from fifo not working' +fi +rm -f /tmp/fifo$$ + +if $SHELL -c "export LC_ALL=en_US.UTF-8; c=$'\342\202\254'; [[ \${#c} == 1 ]]" 2>/dev/null +then lc_utf8=en_US.UTF-8 +else lc_utf8='' +fi + +typeset -a e o=(-n2 -N2) +integer i +set -- \ + 'a' 'bcd' 'a bcd' 'ab cd' \ + 'ab' 'cd' 'ab cd' 'ab cd' \ + 'abc' 'd' 'ab cd' 'ab cd' \ + 'abcd' '' 'ab cd' 'ab cd' +while (( $# >= 3 )) +do a=$1 + b=$2 + e[0]=$3 + e[1]=$4 + shift 4 + for ((i = 0; i < 2; i++)) + do for lc_all in C $lc_utf8 + do g=$(LC_ALL=$lc_all $SHELL -c "{ print -n '$a'; sleep 0.2; print -n '$b'; sleep 0.2; } | { read ${o[i]} a; print -n \$a; read a; print -n \ \$a; }") + [[ $g == "${e[i]}" ]] || err_exit "LC_ALL=$lc_all read ${o[i]} from pipe '$a $b' failed -- expected '${e[i]}', got '$g'" + done + done +done + +if [[ $lc_utf8 ]] +then export LC_ALL=en_US.UTF-8 + typeset -a c=( '' 'A' $'\303\274' $'\342\202\254' ) + integer i w + typeset o + if (( ${#c[2]} == 1 && ${#c[3]} == 1 )) + then for i in 1 2 3 + do for o in n N + do for w in 1 2 3 + do print -nr "${c[w]}" | read -${o}${i} g + if [[ $o == N ]] && (( i > 1 )) + then e='' + else e=${c[w]} + fi + [[ $g == "$e" ]] || err_exit "read -${o}${i} failed for '${c[w]}' -- expected '$e', got '$g'" + done + done + done + fi +fi + exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/locale.sh b/usr/src/lib/libshell/common/tests/locale.sh new file mode 100644 index 0000000000..ab94722e2d --- /dev/null +++ b/usr/src/lib/libshell/common/tests/locale.sh @@ -0,0 +1,104 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 + +# LC_ALL=debug is an ast specific debug/test locale + +if [[ "$(LC_ALL=debug $SHELL <<- \+EOF+ + x=a<1z>b<2yx>c + print ${#x} + +EOF+)" != 5 + ]] +then err_exit '${#x} not working with multibyte locales' +fi + +export LC_ALL=C +if (( $($SHELL -c $'export LC_ALL=en_US.UTF-8; print -r "\342\202\254\342\202\254\342\202\254\342\202\254w\342\202\254\342\202\254\342\202\254\342\202\254" | wc -m' 2>/dev/null) == 10 )) +then LC_ALL=en_US.UTF-8 $SHELL -c b1=$'"\342\202\254\342\202\254\342\202\254\342\202\254w\342\202\254\342\202\254\342\202\254\342\202\254"; [[ ${b1:4:1} == w ]]' || err_exit 'Multibyte ${var:offset:len} not working correctly' +fi + +export LC_ALL=C +a=$($SHELL -c '/' 2>&1 | sed -e "s,.*: *,," -e "s, *\[.*,,") +b=$($SHELL -c '(LC_ALL=debug / 2>/dev/null); /' 2>&1 | sed -e "s,.*: *,," -e "s, *\[.*,,") +[[ "$b" == "$a" ]] || err_exit "locale not restored after subshell -- expected '$a', got '$b'" +b=$($SHELL -c '(LC_ALL=debug; / 2>/dev/null); /' 2>&1 | sed -e "s,.*: *,," -e "s, *\[.*,,") +[[ "$b" == "$a" ]] || err_exit "locale not restored after subshell -- expected '$a', got '$b'" + +# test shift-jis \x81\x40 ... \x81\x7E encodings +# (shift char followed by 7 bit ascii) + +typeset -i16 chr +for lc_all in $(PATH=/bin:/usr/bin locale -a 2>/dev/null | grep -i jis) +do export LC_ALL=$lc_all + for ((chr=0x40; chr<=0x7E; chr++)) + do c=${chr#16#} + for s in \\x81\\x$c \\x$c + do b="$(printf "$s")" + eval n=\$\'$s\' + [[ $b == "$n" ]] || err_exit "LC_ALL=$lc_all printf difference for \"$s\" -- expected '$n', got '$b'" + u=$(print -- $b) + q=$(print -- "$b") + [[ $u == "$q" ]] || err_exit "LC_ALL=$lc_all quoted print difference for \"$s\" -- $b => '$u' vs \"$b\" => '$q'" + done + done +done + +# test multibyte value/trace format -- $'\303\274' is UTF-8 u-umlaut + +LC_ALL=C +lc_all=de_DE.UTF-8 +c=$(LC_ALL=C $SHELL -c "printf $':%2s:\n' $'\303\274'") +u=$(LC_ALL=$lc_all $SHELL -c "printf $':%2s:\n' $'\303\274'" 2>/dev/null) +if [[ "$c" != "$u" ]] +then LC_ALL=$lc_all + x=$'+2+ typeset item.text\ ++3+ item.text=\303\274\ ++4+ print -- \303\274\ +\303\274\ ++5+ eval $\'arr[0]=(\\n\\ttext=\\303\\274\\n)\' ++2+ arr[0].text=ü\ ++6+ print -- \303\274\ +ü\ ++7+ eval txt=$\'(\\n\\ttext=\\303\\274\\n)\' ++2+ txt.text=\303\274\ ++8+ print -- \'(\' text=$\'\\303\\274\' \')\'\ +( text=\303\274 )' + u=$(LC_ALL=$lc_all PS4='+$LINENO+ ' $SHELL -x -c " + item=(typeset text) + item.text=$'\303\274' + print -- \"\${item.text}\" + eval \"arr[0]=\$item\" + print -- \"\${arr[0].text}\" + eval \"txt=\${arr[0]}\" + print -- \$txt + " 2>&1) + [[ "$u" == "$x" ]] || err_exit LC_ALL=$lc_all multibyte value/trace format failed +fi + +exit $Errors diff --git a/usr/src/lib/libshell/common/tests/nameref.sh b/usr/src/lib/libshell/common/tests/nameref.sh index a6c52ddaa8..21a672db14 100644 --- a/usr/src/lib/libshell/common/tests/nameref.sh +++ b/usr/src/lib/libshell/common/tests/nameref.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -226,4 +226,75 @@ function fun } i=foo [[ $(fun $i) == hi ]] || err_exit 'nameref for compound variable with in function name of caller fails' +unset -n foo bar +typeset -A foo +foo[x.y]=(x=3 y=4) +nameref bar=foo[x.y] +[[ ${bar.x} == 3 ]] || err_exit 'nameref to subscript containing . fails' +[[ ${!bar} == 'foo[x.y]' ]] || err_exit '${!var} not correct for nameref to an array instance' +typeset +n bar +nameref bar=foo +[[ ${!bar} == foo ]] || err_exit '${!var} not correct for nameref to array variable' +$SHELL -c 'function bar { nameref x=foo[++];};typeset -A foo;bar' 2> /dev/null ||err_exit 'nameref of associative array tries to evaluate subscript' +i=$($SHELL -c 'nameref foo=bar; bar[2]=(x=3 y=4); nameref x=foo[2].y;print -r -- $x' 2> /dev/null) +[[ $i == 4 ]] || err_exit 'creating reference from subscripted variable whose name is a reference failed' +[[ $($SHELL 2> /dev/null <<- '+++EOF' + function bar + { + nameref x=$1 + print -r -- "$x" + } + function foo + { + typeset var=( foo=hello) + bar var + } + foo ++++EOF +) == *foo=hello* ]] || err_exit 'unable to display compound variable from name reference of local variable' +#set -x +for c in '=' '[' ']' '\' "'" '"' '<' '=' '(' +do [[ $($SHELL 2> /dev/null <<- ++EOF++ + x;i=\\$c;typeset -A a; a[\$i]=foo;typeset -n x=a[\$i]; print "\$x" + ++EOF++ +) != foo ]] && err_exit 'nameref x=[$c] '"not working for c=$c" +done +unset -n foo x +unset foo x +typeset -A foo +nameref x=foo[xyz] +foo[xyz]=ok +[[ $x == ok ]] || err_exit 'nameref to unset subscript not working' +function function2 +{ + nameref v=$1 + v.x=19 v.y=20 +} +function function1 +{ + typeset compound_var=() + function2 compound_var + printf "x=%d, y=%d\n" compound_var.x compound_var.y +} +x="$(function1)" +[[ "$x" != 'x=19, y=20' ]] && err_exit "expected 'x=19, y=20', got '${x}'" +typeset +n bar +unset foo bar +[[ $(function a +{ + for i in foo bar + do typeset -n v=$i + print $v + done | cat +} +foo=1 bar=2;a) == $'1\n2' ]] 2> /dev/null || err_exit 'nameref in pipeline broken' +function a +{ + typeset -n v=vars.data._1 + print "${v.a} ${v.b}" +} +vars=(data=()) +vars.data._1.a=a.1 +vars.data._1.b=b.1 +[[ $(a) == 'a.1 b.1' ]] || err_exit 'nameref choosing wrong scope -- ' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/options.sh b/usr/src/lib/libshell/common/tests/options.sh index 2520cb2a2e..07608efa1e 100644 --- a/usr/src/lib/libshell/common/tests/options.sh +++ b/usr/src/lib/libshell/common/tests/options.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -27,6 +27,9 @@ alias err_exit='err_exit $LINENO' Command=${0##*/} integer Errors=0 + +unset HISTFILE + if [[ $( ${SHELL-ksh} -s hello<<-\! print $1 ! @@ -46,83 +49,85 @@ fi tmp=/tmp/ksh$$ mkdir $tmp rc=$tmp/.kshrc -print $'function env_hit\n{\n\tprint OK\n}' > $rc +print $'PS1=""\nfunction env_hit\n{\n\tprint OK\n}' > $rc -export ENV=$rc +export ENV='${nosysrc}'$rc if [[ -o privileged ]] then - [[ $(print env_hit | $SHELL 2>&1) == "OK" ]] && + [[ $(print env_hit | $SHELL 2>/dev/null) == "OK" ]] && err_exit 'privileged nointeractive shell reads $ENV file' - [[ $(print env_hit | $SHELL -E 2>&1) == "OK" ]] && + [[ $(print env_hit | $SHELL -E 2>/dev/null) == "OK" ]] && err_exit 'privileged -E reads $ENV file' - [[ $(print env_hit | $SHELL +E 2>&1) == "OK" ]] && + [[ $(print env_hit | $SHELL +E 2>/dev/null) == "OK" ]] && err_exit 'privileged +E reads $ENV file' - [[ $(print env_hit | $SHELL --rc 2>&1) == "OK" ]] && + [[ $(print env_hit | $SHELL --rc 2>/dev/null) == "OK" ]] && err_exit 'privileged --rc reads $ENV file' - [[ $(print env_hit | $SHELL --norc 2>&1) == "OK" ]] && + [[ $(print env_hit | $SHELL --norc 2>/dev/null) == "OK" ]] && err_exit 'privileged --norc reads $ENV file' else - [[ $(print env_hit | $SHELL 2>&1) == "OK" ]] && + [[ $(print env_hit | $SHELL 2>/dev/null) == "OK" ]] && err_exit 'nointeractive shell reads $ENV file' - [[ $(print env_hit | $SHELL -E 2>&1) == "OK" ]] || + [[ $(print env_hit | $SHELL -E 2>/dev/null) == "OK" ]] || err_exit '-E ignores $ENV file' - [[ $(print env_hit | $SHELL +E 2>&1) == "OK" ]] && + [[ $(print env_hit | $SHELL +E 2>/dev/null) == "OK" ]] && err_exit '+E reads $ENV file' - [[ $(print env_hit | $SHELL --rc 2>&1) == "OK" ]] || + [[ $(print env_hit | $SHELL --rc 2>/dev/null) == "OK" ]] || err_exit '--rc ignores $ENV file' - [[ $(print env_hit | $SHELL --norc 2>&1) == "OK" ]] && + [[ $(print env_hit | $SHELL --norc 2>/dev/null) == "OK" ]] && err_exit '--norc reads $ENV file' + [[ $(print env_hit | $SHELL -i 2>/dev/null) == "OK" ]] || + err_exit '-i ignores $ENV file' fi export ENV= if [[ -o privileged ]] then - [[ $(print env_hit | HOME=$tmp $SHELL 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL 2>/dev/null) == "OK" ]] && err_exit 'privileged nointeractive shell reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL -E 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL -E 2>/dev/null) == "OK" ]] && err_exit 'privileged -E ignores empty $ENV' - [[ $(print env_hit | HOME=$tmp $SHELL +E 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL +E 2>/dev/null) == "OK" ]] && err_exit 'privileged +E reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>/dev/null) == "OK" ]] && err_exit 'privileged --rc ignores empty $ENV' - [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>/dev/null) == "OK" ]] && err_exit 'privileged --norc reads $HOME/.kshrc file' else - [[ $(print env_hit | HOME=$tmp $SHELL 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL 2>/dev/null) == "OK" ]] && err_exit 'nointeractive shell reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL -E 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL -E 2>/dev/null) == "OK" ]] && err_exit '-E ignores empty $ENV' - [[ $(print env_hit | HOME=$tmp $SHELL +E 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL +E 2>/dev/null) == "OK" ]] && err_exit '+E reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>/dev/null) == "OK" ]] && err_exit '--rc ignores empty $ENV' - [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>/dev/null) == "OK" ]] && err_exit '--norc reads $HOME/.kshrc file' fi unset ENV if [[ -o privileged ]] then - [[ $(print env_hit | HOME=$tmp $SHELL 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL 2>/dev/null) == "OK" ]] && err_exit 'privileged nointeractive shell reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL -E 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL -E 2>/dev/null) == "OK" ]] && err_exit 'privileged -E reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL +E 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL +E 2>/dev/null) == "OK" ]] && err_exit 'privileged +E reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>/dev/null) == "OK" ]] && err_exit 'privileged --rc reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>/dev/null) == "OK" ]] && err_exit 'privileged --norc reads $HOME/.kshrc file' else - [[ $(print env_hit | HOME=$tmp $SHELL 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL 2>/dev/null) == "OK" ]] && err_exit 'nointeractive shell reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL -E 2>&1) == "OK" ]] || + [[ $(print env_hit | HOME=$tmp $SHELL -E 2>/dev/null) == "OK" ]] || err_exit '-E ignores $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL +E 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL +E 2>/dev/null) == "OK" ]] && err_exit '+E reads $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>&1) == "OK" ]] || + [[ $(print env_hit | HOME=$tmp $SHELL --rc 2>/dev/null) == "OK" ]] || err_exit '--rc ignores $HOME/.kshrc file' - [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>&1) == "OK" ]] && + [[ $(print env_hit | HOME=$tmp $SHELL --norc 2>/dev/null) == "OK" ]] && err_exit '--norc reads $HOME/.kshrc file' fi @@ -162,34 +167,34 @@ echo "echo '$t'" > .profile cp $SHELL ./-ksh if [[ -o privileged ]] then - [[ $(HOME=$PWD $SHELL -l </dev/null 2>&1) == *$t* ]] && + [[ $(HOME=$PWD $SHELL -l </dev/null 2>/dev/null) == *$t* ]] && err_exit 'privileged -l reads .profile' - [[ $(HOME=$PWD $SHELL --login </dev/null 2>&1) == *$t* ]] && + [[ $(HOME=$PWD $SHELL --login </dev/null 2>/dev/null) == *$t* ]] && err_exit 'privileged --login reads .profile' - [[ $(HOME=$PWD $SHELL --login-shell </dev/null 2>&1) == *$t* ]] && + [[ $(HOME=$PWD $SHELL --login-shell </dev/null 2>/dev/null) == *$t* ]] && err_exit 'privileged --login-shell reads .profile' - [[ $(HOME=$PWD $SHELL --login_shell </dev/null 2>&1) == *$t* ]] && + [[ $(HOME=$PWD $SHELL --login_shell </dev/null 2>/dev/null) == *$t* ]] && err_exit 'privileged --login_shell reads .profile' - [[ $(HOME=$PWD exec -a -ksh $SHELL </dev/null 2>&1) == *$t* ]] && + [[ $(HOME=$PWD exec -a -ksh $SHELL </dev/null 2>/dev/null) == *$t* ]] && err_exit 'privileged exec -a -ksh ksh reads .profile' - [[ $(HOME=$PWD ./-ksh -i </dev/null 2>&1) == *$t* ]] && + [[ $(HOME=$PWD ./-ksh -i </dev/null 2>/dev/null) == *$t* ]] && err_exit 'privileged ./-ksh reads .profile' - [[ $(HOME=$PWD ./-ksh -ip </dev/null 2>&1) == *$t* ]] && + [[ $(HOME=$PWD ./-ksh -ip </dev/null 2>/dev/null) == *$t* ]] && err_exit 'privileged ./-ksh -p reads .profile' else - [[ $(HOME=$PWD $SHELL -l </dev/null 2>&1) == *$t* ]] || + [[ $(HOME=$PWD $SHELL -l </dev/null 2>/dev/null) == *$t* ]] || err_exit '-l ignores .profile' - [[ $(HOME=$PWD $SHELL --login </dev/null 2>&1) == *$t* ]] || + [[ $(HOME=$PWD $SHELL --login </dev/null 2>/dev/null) == *$t* ]] || err_exit '--login ignores .profile' - [[ $(HOME=$PWD $SHELL --login-shell </dev/null 2>&1) == *$t* ]] || + [[ $(HOME=$PWD $SHELL --login-shell </dev/null 2>/dev/null) == *$t* ]] || err_exit '--login-shell ignores .profile' - [[ $(HOME=$PWD $SHELL --login_shell </dev/null 2>&1) == *$t* ]] || + [[ $(HOME=$PWD $SHELL --login_shell </dev/null 2>/dev/null) == *$t* ]] || err_exit '--login_shell ignores .profile' - [[ $(HOME=$PWD exec -a -ksh $SHELL </dev/null 2>&1) == *$t* ]] || + [[ $(HOME=$PWD exec -a -ksh $SHELL </dev/null 2>/dev/null) == *$t* ]] || err_exit 'exec -a -ksh ksh ignores .profile' - [[ $(HOME=$PWD ./-ksh -i </dev/null 2>&1) == *$t* ]] || + [[ $(HOME=$PWD ./-ksh -i </dev/null 2>/dev/null) == *$t* ]] || err_exit './-ksh ignores .profile' - [[ $(HOME=$PWD ./-ksh -ip </dev/null 2>&1) == *$t* ]] && + [[ $(HOME=$PWD ./-ksh -ip </dev/null 2>/dev/null) == *$t* ]] && err_exit './-ksh -p does not ignore .profile' fi cd ~- @@ -304,10 +309,68 @@ do if [[ -o ?$opt ]] then err_exit "[[ -o ?no$opt ]] should fail" fi done + +[[ $(set +o) == $(set --state) ]] || err_exit "set --state different from set +o" +set -- $(set --state) +[[ $1 == set && $2 == --default ]] || err_exit "set --state failed -- expected 'set --default *', got '$1 $2 *'" +shift +restore=$* +shift +off= +for opt +do case $opt in + --not*) opt=${opt/--/--no} ;; + --no*) opt=${opt/--no/--} ;; + --*) opt=${opt/--/--no} ;; + esac + off="$off $opt" +done +set $off +state=$(set --state) +default=$(set --default --state) +[[ $state == $default ]] || err_exit "set --state for default options failed: expected '$default', got '$state'" +set $restore +state=$(set --state) +[[ $state == "set $restore" ]] || err_exit "set --state after restore failed: expected 'set $restore', got '$state'" + false | true | true || err_exit 'pipe not exiting exit value of last element' true | true | false && err_exit 'pipe not exiting false' set -o pipefail false | true | true && err_exit 'pipe with first not failing with pipefail' true | false | true && err_exit 'pipe middle not failing with pipefail' true | true | false && err_exit 'pipe last not failing with pipefail' +print hi | (sleep 1;/bin/cat) > /dev/null || err_exit 'pipeline fails with pipefail' +( + set -o pipefail + false | true + (( $? )) || err_exit 'pipe not failing in subshell with pipefail' +) | wc >/dev/null +$SHELL -c 'set -o pipefail; false | $(whence -p true);' && err_exit 'pipefail not returning failure with sh -c' +[[ $( set -o pipefail + pipe() { date | cat > /dev/null ;} + print $'1\n2' | + while read i + do if pipe /tmp + then { print enter $i; sleep 2; print leave $i; } & + fi + done + wait) == $'enter 1\nenter 2'* ]] || err_exit '& job delayed by pipefail' +$SHELL -c '[[ $- == *c* ]]' || err_exit 'option c not in $-' +trap 'rm -f /tmp/.profile' EXIT +> /tmp/.profile +for i in i l r s D E a b e f h k n r t u v x B C G H +do HOME=/tmp ENV= $SHELL -$i 2> /dev/null <<- ++EOF++ || err_exit "option $i not in \$-" + [[ \$- == *$i* ]] || exit 1 + ++EOF++ +done +letters=ilrabefhknuvxBCGE +integer j=0 +for i in interactive login restricted allexport notify errexit \ + noglob trackall keyword noexec nounset verbose xtrace braceexpand \ + noclobber globstar rc +do HOME=/tmp ENV= $SHELL -o $i 2> /dev/null <<- ++EOF++ || err_exit "option $i not equivalent to ${letters:j:1}" + [[ \$- == *${letters:j:1}* ]] || exit 1 + ++EOF++ + ((j++)) +done exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/path.sh b/usr/src/lib/libshell/common/tests/path.sh index 7083aca713..2e46e2bd48 100644 --- a/usr/src/lib/libshell/common/tests/path.sh +++ b/usr/src/lib/libshell/common/tests/path.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -24,12 +24,49 @@ function err_exit let Errors+=1 } alias err_exit='err_exit $LINENO' - Command=${0##*/} integer Errors=0 + mkdir /tmp/ksh$$ cd /tmp/ksh$$ trap "PATH=$PATH; cd /; rm -rf /tmp/ksh$$" EXIT +type /xxxxxx > out1 2> out2 +[[ -s out1 ]] && err_exit 'type should not write on stdout for not found case' +[[ -s out2 ]] || err_exit 'type should write on stderr for not found case' +mkdir dir1 dir2 +cat > dir1/foobar << '+++' +foobar() { print foobar1;} +function dir1 { print dir1;} ++++ +cat > dir2/foobar << '+++' +foobar() { print foobar2;} +function dir2 { print dir2;} ++++ +chmod +x dir[12]/foobar +p=$PATH +FPATH=$PWD/dir1 +PATH=$FPATH:$p +[[ $( foobar) == foobar1 ]] || err_exit 'foobar should output foobar1' +FPATH=$PWD/dir2 +PATH=$FPATH:$p +[[ $(foobar) == foobar2 ]] || err_exit 'foobar should output foobar2' +FPATH=$PWD/dir1 +PATH=$FPATH:$p +[[ $(foobar) == foobar1 ]] || err_exit 'foobar should output foobar1 again' +FPATH=$PWD/dir2 +PATH=$FPATH:$p +[[ ${ foobar;} == foobar2 ]] || err_exit 'foobar should output foobar2 with ${}' +[[ ${ dir2;} == dir2 ]] || err_exit 'should be dir2' +[[ ${ dir1;} == dir1 ]] 2> /dev/null && err_exit 'should not be be dir1' +FPATH=$PWD/dir1 +PATH=$FPATH:$p +[[ ${ foobar;} == foobar1 ]] || err_exit 'foobar should output foobar1 with ${}' +[[ ${ dir1;} == dir1 ]] || err_exit 'should be dir1' +[[ ${ dir2;} == dir2 ]] 2> /dev/null && err_exit 'should not be be dir2' +FPATH=$PWD/dir2 +PATH=$FPATH:$p +[[ ${ foobar;} == foobar2 ]] || err_exit 'foobar should output foobar2 with ${} again' +PATH=$p (PATH="/bin") [[ $($SHELL -c 'print -r -- "$PATH"') == "$PATH" ]] || err_exit 'export PATH lost in subshell' cat > bug1 <<- \EOF @@ -183,4 +220,45 @@ status=$($SHELL -c $'trap \'print $?\' ERR;/a/b/c/d/e 2> /dev/null') [[ $status == 127 ]] || err_exit "not found command with ERR trap exit status $status -- expected 127" status=$($SHELL -c $'trap \'print $?\' ERR;/dev/null 2> /dev/null') [[ $status == 126 ]] || err_exit "non executable command ERR trap exit status $status -- expected 126" + +# universe via PATH + +builtin getconf +getconf UNIVERSE - att # override sticky default 'UNIVERSE = foo' + +[[ $(PATH=/usr/ucb/bin:/usr/bin echo -n ucb) == 'ucb' ]] || err_exit "ucb universe echo ignores -n option" +[[ $(PATH=/usr/xpg/bin:/usr/bin echo -n att) == '-n att' ]] || err_exit "att universe echo does not ignore -n option" + +PATH=$path + +scr=/tmp/ksh$$/foo +exp=126 + +: > $scr +chmod a=x $scr +{ got=$($scr; print $?); } 2>/dev/null +[[ "$got" == "$exp" ]] || err_exit "unreadable empty script should fail -- expected $exp, got $got" +{ got=$(command $scr; print $?); } 2>/dev/null +[[ "$got" == "$exp" ]] || err_exit "command of unreadable empty script should fail -- expected $exp, got $got" +[[ "$(:; $scr; print $?)" == "$exp" ]] 2>/dev/null || err_exit "unreadable empty script in [[ ... ]] should fail -- expected $exp" +[[ "$(:; command $scr; print $?)" == "$exp" ]] 2>/dev/null || err_exit "command unreadable empty script in [[ ... ]] should fail -- expected $exp" +got=$($SHELL -c "$scr; print \$?" 2>/dev/null) +[[ "$got" == "$exp" ]] || err_exit "\$SHELL -c of unreadable empty script should fail -- expected $exp, got" $got +got=$($SHELL -c "command $scr; print \$?" 2>/dev/null) +[[ "$got" == "$exp" ]] || err_exit "\$SHELL -c of command of unreadable empty script should fail -- expected $exp, got" $got + +rm -f $scr +print : > $scr +chmod a=x $scr +{ got=$($scr; print $?); } 2>/dev/null +[[ "$got" == "$exp" ]] || err_exit "unreadable non-empty script should fail -- expected $exp, got $got" +{ got=$(command $scr; print $?); } 2>/dev/null +[[ "$got" == "$exp" ]] || err_exit "command of unreadable non-empty script should fail -- expected $exp, got $got" +[[ "$(:; $scr; print $?)" == "$exp" ]] 2>/dev/null || err_exit "unreadable non-empty script in [[ ... ]] should fail -- expected $exp" +[[ "$(:; command $scr; print $?)" == "$exp" ]] 2>/dev/null || err_exit "command unreadable non-empty script in [[ ... ]] should fail -- expected $exp" +got=$($SHELL -c "$scr; print \$?" 2>/dev/null) +[[ "$got" == "$exp" ]] || err_exit "\$SHELL -c of unreadable non-empty script should fail -- expected $exp, got" $got +got=$($SHELL -c "command $scr; print \$?" 2>/dev/null) +[[ "$got" == "$exp" ]] || err_exit "\$SHELL -c of command of unreadable non-empty script should fail -- expected $exp, got" $got + exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/pointtype.sh b/usr/src/lib/libshell/common/tests/pointtype.sh new file mode 100644 index 0000000000..25b9f5fbc5 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/pointtype.sh @@ -0,0 +1,145 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 + +typeset -T Pt_t=( + float x=1 + float y=0 + len() + { + print -r $((sqrt(_.x*_.x + _.y*_.y))) + } +) + +for ((i=0; i < 100; i++)) +do +Pt_t p +[[ ${p.x} == 1 ]] || err_exit '${p[x]} is not 1' +(( p.x == 1 )) || err_ext 'p[x] is not 1' +[[ $(p.len) == 1 ]] || err_exit '$(p.len) != 1' +[[ ${p.len} == 1 ]] || err_exit '${p.len} != 1' +(( p.len == 1 )) || err_exit '((p.len != 1))' +Pt_t q=(y=2) +(( q.x == 1 )) || err_exit 'q.x is not 1' +(( (q.len - sqrt(5)) < 10e-10 )) || err_exit 'q.len != sqrt(5)' +q.len() +{ + print -r $((abs(_.x)+abs(_.y) )) +} +(( q.len == 3 )) || err_exit 'q.len is not 3' +p=q +[[ ${p.y} == 2 ]] || err_exit '${p[y]} is not 2' +[[ ${@p} == Pt_t ]] || err_exit 'type of p is not Pt_t' +[[ ${@q} == Pt_t ]] || err_exit 'type of q is not Pt_t' +(( p.len == 3 )) || err_exit 'p.len is not 3' +unset p q +Pt_t pp=( ( x=3 y=4) ( x=5 y=12) (y=2) ) +(( pp[0].len == 5 )) || err_exit 'pp[0].len != 5' +(( pp[1].len == 13 )) || err_exit 'pp[0].len != 12' +(( (pp[2].len - sqrt(5)) < 10e-10 )) || err_exit 'pp[2].len != sqrt(5)' +[[ ${pp[1]} == $'(\n\ttypeset -l -E x=5\n\ttypeset -l -E y=12\n)' ]] || err_exit '${pp[1] is not correct' +[[ ${!pp[@]} == '0 1 2' ]] || err_exit '${pp[@] != "0 1 2"' +pp+=( x=6 y=8) +(( pp[3].len == 10 )) || err_exit 'pp[3].len != 10' +[[ ${!pp[@]} == '0 1 2 3' ]] || err_exit '${pp[@] != "0 1 2 3"' +pp[4]=pp[1] +[[ ${pp[4]} == $'(\n\ttypeset -l -E x=5\n\ttypeset -l -E y=12\n)' ]] || err_exit '${pp[4] is not correct' +unset pp +Pt_t pp=( [one]=( x=3 y=4) [two]=( x=5 y=12) [three]=(y=2) ) +(( pp[one].len == 5 )) || err_exit 'pp[one].len != 5' +(( pp[two].len == 13 )) || err_exit 'pp[two].len != 12' +[[ ${pp[two]} == $'(\n\ttypeset -l -E x=5\n\ttypeset -l -E y=12\n)' ]] || err_exit '${pp[two] is not correct' +[[ ${!pp[@]} == 'one three two' ]] || err_exit '${pp[@] != "one three two"' +[[ ${@pp[1]} == Pt_t ]] || err_exit 'type of pp[1] is not Pt_t' +unset pp +done +# redefinition of point +typeset -T Pt_t=( + Pt_t _=(x=3 y=6) + float z=2 + len() + { + print -r $((sqrt(_.x*_.x + _.y*_.y + _.z*_.z))) + } +) +Pt_t p +[[ ${p.y} == 6 ]] || err_exit '${p.y} != 6' +(( p.len == 7 )) || err_exit '((p.len !=7))' + +z=() +Pt_t -a z.p +z.p[1]=(y=2) +z.p[2]=(y=5) +z.p[3]=(x=6 y=4) +eval y="$z" +[[ $y == "$z" ]] || err_exit 'expansion of indexed array of types is incorrect' +eval "$(typeset -p y)" +[[ $y == "$z" ]] || err_exit 'typeset -p z for indexed array of types is incorrect' +unset z y +z=() +Pt_t -A z.p +z.p[1]=(y=2) +z.p[2]=(y=5) +z.p[3]=(x=6 y=4) +eval y="$z" +[[ $y == "$z" ]] || err_exit 'expansion of associative array of types is incorrect' +eval "$(typeset -p y)" +[[ $y == "$z" ]] || err_exit 'typeset -p z for associative of types is incorrect' +unset z y + +typeset -T A_t=( + Pt_t -a b +) +typeset -T B_t=( + Pt_t -A b +) +A_t r +r.b[1]=(y=2) +r.b[2]=(y=5) +eval s="$r" +[[ $r == "$s" ]] || err_exit 'expansion of type containing index array of types is incorrect' +eval "$(typeset -p s)" +[[ $y == "$z" ]] || err_exit 'typeset -p z for type containing index of types is incorrect' +unset r s +B_t r +r.b[1]=(y=2) +r.b[2]=(y=5) +eval s="$r" +[[ $r == "$s" ]] || err_exit 'expansion of type containing index array of types is incorrect' +eval "$(typeset -p s)" +[[ $y == "$z" ]] || err_exit 'typeset -p z for type containing index of types is incorrect' +unset r s +exit + + + +typeset -C z=(Pt_t -A p=( [1]=(typeset -l -E x=1;typeset -l -E y=2;)[2]=(typeset -l -E x=1;typeset -l -E y=5;)[3]=(typeset -l -E x=6;typeset -l -E y=4;));) + +print z="$z" +exit $Errors diff --git a/usr/src/lib/libshell/common/tests/quoting.sh b/usr/src/lib/libshell/common/tests/quoting.sh index 9a9e23bd55..b15739da08 100644 --- a/usr/src/lib/libshell/common/tests/quoting.sh +++ b/usr/src/lib/libshell/common/tests/quoting.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -328,4 +328,10 @@ x=x x=${x:-`id | sed 's/^[^(]*(\([^)]*\)).*/\1/'`} } 2> /dev/null || err_exit 'skipping over `` failed' [[ $x == x ]] || err_exit 'assignment ${x:=`...`} failed' +[[ $($SHELL -c 'print a[') == 'a[' ]] || err_exit "unbalanced '[' in command arg fails" +$SHELL -c $'false && (( `wc -l /dev/null | nawk \'{print $1}\'` > 2 )) && true;:' 2> /dev/null || err_exit 'syntax error with ` in arithmetic expression' +{ $SHELL -c '(( 1`: "{ }"` ))' ;} 2> /dev/null || err_exit 'problem with ` inside (())' +varname=foobarx +x=`print '"\$'${varname}'"'` +[[ $x == '"$foobarx"' ]] || err_exit $'\\$\' not handled correctly inside ``' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/quoting2.sh b/usr/src/lib/libshell/common/tests/quoting2.sh index 0532f72d7a..334ed40e7c 100644 --- a/usr/src/lib/libshell/common/tests/quoting2.sh +++ b/usr/src/lib/libshell/common/tests/quoting2.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -197,4 +197,11 @@ foo=foo [[ "$" == '$' ]] || err_exit '"$" != $' [[ "${foo}$" == 'foo$' ]] || err_exit 'foo=foo;"${foo}$" != foo$' [[ "${foo}${foo}$" == 'foofoo$' ]] || err_exit 'foo=foo;"${foo}${foo}$" != foofoo$' +foo='$ ' +[[ "$foo" == ~(Elr)(\\\$|#)\ ]] || err_exit $'\'$ \' not matching RE \\\\\\$|#\'' +[[ "$foo" == ~(Elr)('\$'|#)\ ]] || err_exit $'\'$ \' not matching RE \'\\$\'|#\'' +foo='# ' +[[ "$foo" == ~(Elr)(\\\$|#)\ ]] || err_exit $'\'# \' not matching RE \\'\$|#\'' +[[ "$foo" == ~(Elr)('\$'|#)\ ]] || err_exit $'\'# \' not matching RE \'\\$\'|#\'' +[[ '\$' == '\$'* ]] || err_exit $'\'\\$\' not matching \'\\$\'*' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/recttype.sh b/usr/src/lib/libshell/common/tests/recttype.sh new file mode 100644 index 0000000000..8fe1f3d4ff --- /dev/null +++ b/usr/src/lib/libshell/common/tests/recttype.sh @@ -0,0 +1,69 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 + +typeset -T Pt_t=( + float x=1 + float y=0 + len() + { + print -r $((sqrt(_.x*_.x + _.y*_.y))) + } +) + +typeset -T Rect_t=( + Pt_t ll=(x=0 y=0) + Pt_t ur=(x=1 y=1) + area() + { + print -r $(( abs((_.ur.x-_.ll.x)*(_.ur.y-_.ll.y)) )) + } +) + +for ((i=0; i < 100; i++)) +do +Rect_t r +[[ ${r.area} == 1 ]] || err_exit '${r.area} != 1' +Rect_t s=( + Pt_t ur=(x=9 y=9) + Pt_t ll=(x=7 y=7) +) +[[ ${s.ur.x} == 9 ]] || err_exit ' ${s.ur.x} != 9' +(( s.ur.x == 9 ))|| err_exit ' ((s.ur.x)) != 9' +[[ ${s.ll.y} == 7 ]] || err_exit '${s.ll.y} != 7' +(( s.area == 4 )) || err_exit 'area of s should be 4' +[[ ${s.area} == 4 ]] || err_exit '${s.area} != 4' +unset r s +done +Rect_t -A r +r[one]=(ur=(x=4 y=4)) +(( r[one].area == 16 )) || err_exit 'area of r[one] should be 16' +[[ ${r[one].area} == 16 ]] || err_exit '${r[one].area} should be 16' +unset r +exit $Errors diff --git a/usr/src/lib/libshell/common/tests/restricted.sh b/usr/src/lib/libshell/common/tests/restricted.sh new file mode 100644 index 0000000000..9cc28c7303 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/restricted.sh @@ -0,0 +1,77 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + let Errors+=1 +} +alias err_exit='err_exit $LINENO' + +# test restricted shell +Command=${0##*/} +integer Errors=0 +mkdir /tmp/ksh$$ || err_exit "mkdir /tmp/ksh$$ failed" +trap "cd /; rm -rf /tmp/ksh$$" EXIT +pwd=$PWD +case $SHELL in +/*) ;; +*/*) SHELL=$pwd/$SHELL;; +*) SHELL=$(whence "$SHELL");; +esac +function check_restricted +{ + rm -f out + rksh -c "$@" 2> out > /dev/null + grep restricted out > /dev/null 2>&1 +} + +[[ $SHELL != /* ]] && SHELL=$pwd/$SHELL +cd /tmp/ksh$$ || err_exit "cd /tmp/ksh$$ failed" +ln -s $SHELL rksh +PATH=$PWD:$PATH +rksh -c '[[ -o restricted ]]' || err_exit 'restricted option not set' +[[ $(rksh -c 'print hello') == hello ]] || err_exit 'unable to run print' +check_restricted /bin/echo || err_exit '/bin/echo not resticted' +check_restricted ./echo || err_exit './echo not resticted' +check_restricted 'SHELL=ksh' || err_exit 'SHELL asignment not resticted' +check_restricted 'PATH=/bin' || err_exit 'PATH asignment not resticted' +check_restricted 'FPATH=/bin' || err_exit 'FPATH asignment not resticted' +check_restricted 'ENV=/bin' || err_exit 'ENV asignment not resticted' +check_restricted 'print > file' || err_exit '> file not restricted' +> empty +check_restricted 'print <> empty' || err_exit '<> file not restricted' +print 'echo hello' > script +chmod +x ./script +! check_restricted script || err_exit 'script without builtins should run in restricted mode' +check_restricted ./script || err_exit 'script with / in name should not run in restricted mode' +print '/bin/echo hello' > script +! check_restricted script || err_exit 'script with pathnames should run in restricted mode' +print 'echo hello> file' > script +! check_restricted script || err_exit 'script with output redirection should run in restricted mode' +print 'PATH=/bin' > script +! check_restricted script || err_exit 'script with PATH assignment should run in restricted mode' +cat > script <<! +#! $SHELL +print hello +! +! check_restricted 'script;:' || err_exit 'script with #! pathname should run in restricted mode' +! check_restricted 'script' || err_exit 'script with #! pathname should run in restricted mode even if last command in script' +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/return.sh b/usr/src/lib/libshell/common/tests/return.sh index b3ee5b11ff..ac9d2761c0 100644 --- a/usr/src/lib/libshell/common/tests/return.sh +++ b/usr/src/lib/libshell/common/tests/return.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -26,9 +26,11 @@ function err_exit let Errors+=1 } alias err_exit='err_exit $LINENO' - Command=${0##*/} integer Errors=0 + +unset HISTFILE + foo=NOVAL bar=NOVAL file=/tmp/shtest$$ trap "rm -f $file" EXIT INT @@ -144,7 +146,7 @@ x=$( . $file) if [[ $x != $0 ]] then err_exit "\$0 in a dot script is $x. Should be $0" fi -x=$($SHELL -i 2> /dev/null <<\! +x=$($SHELL -i --norc 2> /dev/null <<\! typeset -i x=1/0 print hello ! diff --git a/usr/src/lib/libshell/common/tests/select.sh b/usr/src/lib/libshell/common/tests/select.sh index aec1903fb7..578fca2e11 100644 --- a/usr/src/lib/libshell/common/tests/select.sh +++ b/usr/src/lib/libshell/common/tests/select.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # diff --git a/usr/src/lib/libshell/common/tests/shtests b/usr/src/lib/libshell/common/tests/shtests index a094d8ed20..63b8db0462 100644 --- a/usr/src/lib/libshell/common/tests/shtests +++ b/usr/src/lib/libshell/common/tests/shtests @@ -1,24 +1,46 @@ # This program runs ksh regression tests -# shtests [ name=value ... ] a.sh b.sh ... +# shtests [ name=value ... ] [ --all | --compile ] [ --time ] [ a.sh b.sh ... ] unset DISPLAY ENV FIGNORE LANG=C LC_ALL=C +compile=1 +script=1 time=1 +vmdebug=1 while : do case $1 in + -a|--a*)compile=2 + script=2 + ;; + -c|--c*)compile=2 + script= + ;; + -s|--s*)compile= + script=2 + ;; + -t|--not*)time= + ;; + -v|--nov*)vmdebug= + ;; + -*) echo $0: $1: invalid option >&2 + exit 2 + ;; *=*) n=${1%%=*} v=${1#*=} eval $n=\'$v\' export $n ;; - -t|--t*)time= - ;; *) break ;; esac shift done +if [[ ! $vmdebug ]] +then unset VMDEBUG +elif [[ ! $VMDEBUG ]] +then export VMDEBUG=a +fi export LANG LC_ALL PATH PWD SHELL PWD=`pwd` SHELL=${SHELL-ksh} @@ -37,27 +59,77 @@ if [[ -d /usr/ucb ]] then PATH=$PATH:/usr/ucb fi PATH=$PATH:$d -if [[ $INSTALLROOT && -r $INSTALLROOT/bin/.paths ]] +if [[ $INSTALLROOT && -r $INSTALLROOT/bin/.paths ]] then PATH=$INSTALLROOT/bin:$PATH fi +if [[ $compile ]] +then SHCOMP=${SHCOMP:-shcomp} + if whence $SHCOMP > /dev/null + then tmp=/tmp/ksh-$$ + trap 'rm -rf $tmp' EXIT + mkdir $tmp || exit + elif [[ $compile != 1 ]] + then echo $0: --compile: $SHCOMP not found >&2 + exit 1 + else compile= + fi +fi +typeset -A tests for i in ${*-*.sh} -do echo test $i begins ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} - t=$(grep -c err_exit $i) - if (( $t )) - then (( t = $t - 1 )) +do t=$(grep -c err_exit $i) + if (( $t > 2 )) + then (( t = $t - 2 )) fi + tests[$i]=$t T=test if (( $t != 1 )) then T=${T}s fi E=error - if $SHELL $i - then echo test $i passed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} "[ $t $T 0 ${E}s ]" - else e=$? - E=error - if (( $e != 1 )) - then E=${E}s + if [[ $script ]] + then echo test $i begins ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} + if $SHELL $i + then echo test $i passed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} "[ $t $T 0 ${E}s ]" + else e=$? + if (( e > 256 )) + then e=1 + E=signal + fi + if (( $e != 1 )) + then E=${E}s + fi + echo test $i failed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} with exit code $e "[ $t $T $e $E ]" fi - echo test $i failed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} with exit code $e "[ $t $T $e $E ]" fi done +if [[ $compile ]] +then for i in ${*-*.sh} + do t=${tests[$i]} + T=test + if (( $t != 1 )) + then T=${T}s + fi + o=${i##*/} + o=shcomp-${o%.sh}.ksh + echo test $o begins ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} + E=error + if $SHCOMP $i > $tmp/$o + then if $SHELL $tmp/$o + then echo test $o passed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} "[ $t $T 0 ${E}s ]" + else e=$? + if (( e > 256 )) + then e=1 + E=signal + fi + if (( $e != 1 )) + then E=${E}s + fi + echo test $o failed ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} with exit code $e "[ $t $T $e $E ]" + fi + else e=$? + t=1 + T=test + echo test $o failed to compile ${time:+"at $(date +%Y-%m-%d+%H:%M:%S)"} with exit code $e "[ $t $T 1 $E ]" + fi + done +fi diff --git a/usr/src/lib/libshell/common/tests/sigchld.sh b/usr/src/lib/libshell/common/tests/sigchld.sh new file mode 100644 index 0000000000..018eb9fbc0 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sigchld.sh @@ -0,0 +1,65 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +float DELAY=${1:-0.5} +integer FOREGROUND=10 BACKGROUND=2 Errors=0 + +s=$($SHELL -c ' +integer i foreground=0 background=0 +float delay='$DELAY' d=0 s=0 + +set --errexit + +trap "(( background++ ))" CHLD + +(( d = delay )) +for ((i = 0; i < '$BACKGROUND'; i++)) +do sleep $d & + (( d *= 4 )) + (( s += d )) +done +for ((i = 0; i < '$FOREGROUND'; i++)) +do (( foreground++ )) + sleep $delay + (( s -= delay )) + $SHELL -c : > /dev/null # foreground does not generate SIGCHLD +done +if (( (s += delay) < 1 )) +then (( s = 1 )) +fi +sleep $s +wait +print foreground=$foreground background=$background +') || err_exit "test loop failed" + +eval $s + +(( foreground == FOREGROUND )) || err_exit "expected $FOREGROUND foreground -- got $foreground (DELAY=$DELAY)" +(( background == BACKGROUND )) || err_exit "expected $BACKGROUND background -- got $background (DELAY=$DELAY)" + +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/signal.sh b/usr/src/lib/libshell/common/tests/signal.sh new file mode 100644 index 0000000000..12b06d6fca --- /dev/null +++ b/usr/src/lib/libshell/common/tests/signal.sh @@ -0,0 +1,226 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors++ )) +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 + +mkdir /tmp/ksh$$ || err_exit "mkdir /tmp/ksh$$ failed" +trap 'cd /; rm -rf /tmp/ksh$$' EXIT +cd /tmp/ksh$$ || err_exit "cd /tmp/ksh$$ failed" + +[[ $( trap 'print -n got_child' SIGCHLD + sleep 2 & + for ((i=0; i < 4; i++)) + do sleep .9 + print -n $i + done) == 01got_child23 ]] || err_exit 'SIGCHLD not working' + +# begin standalone SIGINT test generation + +cat > tst <<'!' +# shell trap tests +# +# tst control script that calls tst-1, must be run by ksh +# tst-1 calls tst-2 +# tst-2 calls tst-3 +# tst-3 defaults or handles and discards/propagates SIGINT +# +# initial -v option lists script entry and SIGINT delivery +# +# three test options +# +# d call next script directly, otherwise via $SHELL -c +# t trap, echo, and kill self on SIGINT, otherwise x or SIGINT default if no x +# x trap, echo on SIGINT, and exit 0, otherwise SIGINT default +# +# Usage: tst [-v] [-options] shell-to-test ... + +# "trap + sig" is an unadvertized extension for this test +# if run from nmake SIGINT is set to SIG_IGN +# this call sets it back to SIG_DFL +# semantics w.r.t. function scope must be worked out before +# making it public +trap + INT + +set -o monitor + +function gen +{ + typeset o t x d + for x in - x + do case $x in + [$1]) for t in - t + do case $t in + [$1]) for d in - d + do case $d in + [$1]) o="$o $x$t$d" + esac + done + esac + done + esac + done + echo '' $o +} + +case $1 in +-v) v=v; shift ;; +-*v*) v=v ;; +*) v= ;; +esac +case $1 in +*' '*) o=$1; shift ;; +-*) o=$(gen $1); shift ;; +*) o=$(gen -txd) ;; +esac +case $# in +0) set ksh bash ksh88 pdksh ash zsh ;; +esac +for f in $o +do case $# in + 1) ;; + *) echo ;; + esac + for sh + do if $sh -c 'exit 0' > /dev/null 2>&1 + then case $# in + 1) printf '%3s ' "$f" ;; + *) printf '%16s %3s ' "$sh" "$f" ;; + esac + $sh tst-1 $v$f $sh > tst.out & + wait + echo $(cat tst.out) + fi + done +done +case $# in +1) ;; +*) echo ;; +esac +! +cat > tst-1 <<'!' +exec 2>/dev/null +case $1 in +*v*) echo 1-main ;; +esac +{ + sleep 2 + case $1 in + *v*) echo "SIGINT" ;; + esac + kill -s INT 0 +} & +case $1 in +*t*) trap ' + echo 1-intr + trap - INT + # omitting the self kill exposes shells that deliver + # the SIGINT trap but exit 0 for -xt + # kill -s INT $$ + ' INT + ;; +esac +case $1 in +*d*) tst-2 $1 $2; status=$? ;; +*) $2 -c "tst-2 $1 $2"; status=$? ;; +esac +printf '1-%04d\n' $status +sleep 2 +! +cat > tst-2 <<'!' +case $1 in +*x*) trap ' + echo 2-intr + exit + ' INT + ;; +*t*) trap ' + echo 2-intr + trap - INT + kill -s INT $$ + ' INT + ;; +esac +case $1 in +*v*) echo 2-main ;; +esac +case $1 in +*d*) tst-3 $1 $2; status=$? ;; +*) $2 -c "tst-3 $1 $2"; status=$? ;; +esac +printf '2-%04d\n' $status +! +cat > tst-3 <<'!' +case $1 in +*x*) trap ' + sleep 2 + echo 3-intr + exit 0 + ' INT + ;; +*) trap ' + sleep 2 + echo 3-intr + trap - INT + kill -s INT $$ + ' INT + ;; +esac +case $1 in +*v*) echo 3-main ;; +esac +sleep 5 +printf '3-%04d\n' $? +! +chmod +x tst tst-? + +# end standalone test generation + +export PATH=$PATH: +typeset -A expected +expected[---]="3-intr" +expected[--d]="3-intr" +expected[-t-]="3-intr 2-intr 1-intr 1-0258" +expected[-td]="3-intr 2-intr 1-intr 1-0258" +expected[x--]="3-intr 2-intr 1-0000" +expected[x-d]="3-intr 2-intr 1-0000" +expected[xt-]="3-intr 2-intr 1-intr 1-0000" +expected[xtd]="3-intr 2-intr 1-intr 1-0000" + +tst $SHELL > tst.got + +while read ops out +do [[ $out == ${expected[$ops]} ]] || err_exit "interrupt $ops test failed -- expected '${expected[$ops]}', got '$out'" +done < tst.got + +float s=$SECONDS +[[ $($SHELL -c 'trap "print SIGUSR1 ; exit 0" USR1; (trap "" USR1 ; exec kill -USR1 $$ & sleep 5); print done') == SIGUSR1 ]] || err_exit 'subshell ignoring signal does not send signal to parent' +(( (SECONDS-s) < 4 )) && err_exit 'parent does not wait for child to complete before handling signal' +((s = SECONDS)) +[[ $($SHELL -c 'trap "print SIGUSR1 ; exit 0" USR1; (trap "exit" USR1 ; exec kill -USR1 $$ & sleep 5); print done') == SIGUSR1 ]] || err_exit 'subshell catching signal does not send signal to parent' +(( SECONDS-s < 4 )) && err_exit 'parent completes early' +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/statics.sh b/usr/src/lib/libshell/common/tests/statics.sh new file mode 100644 index 0000000000..d6af9c7c91 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/statics.sh @@ -0,0 +1,106 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit2 +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +function testfunc +{ + integer line_number=$1 + typeset cmd="$2" + typeset expected_output="$3" + typeset output + + output="$($SHELL -c "${cmd}" 2>&1 )" + + [[ "${output}" != "${expected_output}" ]] && err_exit2 ${line_number} "${output} != ${expected_output}" +} +alias testfunc='testfunc $LINENO' +alias err_exit='err_exit2 $LINENO' + +Command=${0##*/} +integer Errors=0 + +# string +testfunc '(function l { typeset -S x ; x+="#" ; $1 && print "$x" ; } ; l false ; l false ; l true)' "###" +testfunc 'function l { typeset -S x=">" ; x+="#" ; $1 && print "$x" ; } ; l false ; l false ; l true' ">###" +testfunc 'function l { typeset -S x=">" ; x+="#" ; $1 && print "$x" ; } ; l false ; (l false) ; l true' ">##" +testfunc 'function l { typeset -S x=">" ; x+="#" ; $1 && print "$x" ; } ; l false; ( ulimit -c 0 ; l false) ; l true' ">##" + +# integer +testfunc '(function l { typeset -S -i x ; x+=1 ; $1 && print "$x" ; } ; l false ; l false ; l true )' "3" +testfunc '(function l { typeset -S -i x ; x+=1 ; $1 && print "$x" ; } ; l false ; (l false) ; l true )' "2" + +# float +testfunc '(function l { float -S x=0.5 ; (( x+=.5 )) ; $1 && print "$x" ; } ; l false ; l false ; l true )' "2" +testfunc '(function l { float -S x=0.5 ; (( x+=.5 )) ; $1 && print "$x" ; } ; l false ; (l false) ; l true )' "1.5" + +# compound variable +[[ "${ + function l + { + typeset -S s=( a=0 b=0 ) + + (( s.a++, s.b++ )) + + $1 && printf 'a=%d, b=%d\n' s.a s.b + } + l false ; l false ; l true +}" != "a=3, b=3" ]] && err_exit "static compound var failed" + + +# array variable +expected="helloan elementan elementan element" +got=$( + function ar + { + typeset -a -S s=( "hello" ) + + s+=( "an element" ) + + $1 && { printf '%s' "${s[@]}" ; printf '\n' ; } + } + ar false ; ar false ; ar true +) +[[ $got != $expected ]] && err_exit "static array var failed -- expected '$expected', got '$got'" + + +# Test visibilty of "global" vs. "static" variables. if we have a "static" variable in a +# function and "unset" it we should see a global variable with the same +# name, right ? +integer hx=5 +function test_hx_scope +{ + integer -S hx=9 + $2 && unset hx + $1 && printf "hx=%d\n" hx +} +test_hx_scope false false +test_hx_scope false false +# first test the "unset" call in a $(...) subshell... +[[ "$( test_hx_scope true true )" != "hx=5" ]] && err_exit "can't see global variable hx after unsetting static variable hx" +# ... end then test whether the value has changed. +[[ "${ test_hx_scope true false }" != "hx=9" ]] && err_exit "hx variable somehow changed" + +exit $((Errors)) + diff --git a/usr/src/lib/libshell/common/tests/subshell.sh b/usr/src/lib/libshell/common/tests/subshell.sh new file mode 100644 index 0000000000..985aba6eaa --- /dev/null +++ b/usr/src/lib/libshell/common/tests/subshell.sh @@ -0,0 +1,223 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u$Error_fd -n "\t" + print -u$Error_fd -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} +alias err_exit='err_exit $LINENO' +Command=${0##*/} +integer Errors=0 Error_fd=2 + +z=() +z.foo=( [one]=hello [two]=(x=3 y=4) [three]=hi) +z.bar[0]=hello +z.bar[2]=world +z.bar[1]=(x=4 y=5) +val='( + typeset -a bar=( + [0]=hello + [2]=world + [1]=( + x=4 + y=5 + ) + ) + typeset -A foo=( + [one]=hello + [three]=hi + [two]=( + x=3 + y=4 + ) + ) +)' +[[ $z == "$val" ]] || err_exit 'compound variable with mixed arrays not working' +z.bar[1]=yesyes +[[ ${z.bar[1]} == yesyes ]] || err_exit 'reassign of index array compound variable fails' +z.bar[1]=(x=12 y=5) +[[ ${z.bar[1]} == $'(\n\tx=12\n\ty=5\n)' ]] || err_exit 'reassign array simple to compound variable fails' +eval val="$z" +( + z.foo[three]=good + [[ ${z.foo[three]} == good ]] || err_exit 'associative array assignment in subshell not working' +) +[[ $z == "$val" ]] || err_exit 'compound variable changes after associative array assignment' +eval val="$z" +( +false + z.foo[two]=ok + [[ ${z.foo[two]} == ok ]] || err_exit 'associative array assignment to compound variable in subshell not working' + z.bar[1]=yes + [[ ${z.bar[1]} == yes ]] || err_exit 'index array assignment to compound variable in subshell not working' +) +[[ $z == "$val" ]] || err_exit 'compound variable changes after associative array assignment' + +x=( + foo=( qqq=abc rrr=def) + bar=( zzz=no rst=fed) +) +eval val="$x" +( + unset x.foo + [[ ${x.foo.qqq} ]] && err_exit 'x.foo.qqq should be unset' + x.foo=good + [[ ${x.foo} == good ]] || err_exit 'x.foo should be good' +) +[[ $x == "$val" ]] || err_exit 'compound variable changes after unset leaves' +unset l +( + l=( a=1 b="BE" ) +) +[[ ${l+foo} != foo ]] || err_exit 'l should be unset' + +Error_fd=9 +eval "exec $Error_fd>&2 2>/dev/null" + +TEST_notfound=notfound +while whence $TEST_notfound >/dev/null 2>&1 +do TEST_notfound=notfound-$RANDOM +done + +tmp=/tmp/kshsubsh$$ +trap "rm -f $tmp" EXIT +integer BS=1024 nb=64 ss=60 bs no +for bs in $BS 1 +do $SHELL -c ' + { + sleep '$ss' + kill -KILL $$ + } & + set -- $(printf %.'$(($BS*$nb))'c x | dd bs='$bs') + print ${#1} + kill $! + ' > $tmp 2>/dev/null + no=$(<$tmp) + (( no == (BS * nb) )) || err_exit "shell hangs on command substitution output size >= $BS*$nb with write size $bs -- expected $((BS*nb)), got ${no:-0}" +done +# this time with redirection on the trailing command +for bs in $BS 1 +do $SHELL -c ' + { + sleep 2 + sleep '$ss' + kill -KILL $$ + } & + set -- $(printf %.'$(($BS*$nb))'c x | dd bs='$bs' 2>/dev/null) + print ${#1} + kill $! + ' > $tmp 2>/dev/null + no=$(<$tmp) + (( no == (BS * nb) )) || err_exit "shell hangs on command substitution output size >= $BS*$nb with write size $bs and trailing redirection -- expected $((BS*nb)), got ${no:-0}" +done + +# exercise command substitutuion trailing newline logic w.r.t. pipe vs. tmp file io + +set -- \ + 'post-line print' \ + '$TEST_unset; ($TEST_fork; print 1); print' \ + 1 \ + 'pre-line print' \ + '$TEST_unset; ($TEST_fork; print); print 1' \ + $'\n1' \ + 'multiple pre-line print' \ + '$TEST_unset; ($TEST_fork; print); print; ($TEST_fork; print 1); print' \ + $'\n\n1' \ + 'multiple post-line print' \ + '$TEST_unset; ($TEST_fork; print 1); print; ($TEST_fork; print); print' \ + 1 \ + 'intermediate print' \ + '$TEST_unset; ($TEST_fork; print 1); print; ($TEST_fork; print 2); print' \ + $'1\n\n2' \ + 'simple variable' \ + '$TEST_unset; ($TEST_fork; l=2; print "$l"); print $l' \ + 2 \ + 'compound variable' \ + '$TEST_unset; ($TEST_fork; l=(a=2 b="BE"); print "$l"); print $l' \ + $'(\n\ta=2\n\tb=BE\n)' \ + +export TEST_fork TEST_unset + +while (( $# >= 3 )) +do txt=$1 + cmd=$2 + exp=$3 + shift 3 + for TEST_unset in '' 'unset var' + do for TEST_fork in '' 'ulimit -c 0' + do for TEST_shell in "eval" "$SHELL -c" + do if ! got=$($TEST_shell "$cmd") + then err_exit "${TEST_shell/*-c/\$SHELL -c} ${TEST_unset:+unset }${TEST_fork:+fork }$txt print failed" + elif [[ "$got" != "$exp" ]] + then EXP=$(printf %q "$exp") + GOT=$(printf %q "$got") + err_exit "${TEST_shell/*-c/\$SHELL -c} ${TEST_unset:+unset }${TEST_fork:+fork }$txt command substitution failed -- expected $EXP, got $GOT" + fi + done + done + done +done + +r=$( ($SHELL -c ' + { + sleep 32 + kill -KILL $$ + } & + for v in $(set | sed "s/=.*//") + do command unset $v + done + typeset -Z5 I + for ((I = 0; I < 1024; I++)) + do eval A$I=1234567890 + done + a=$(set 2>&1) + print ok + kill -KILL $! +') 2>/dev/null) +[[ $r == ok ]] || err_exit "large subshell command substitution hangs" + +for TEST_command in '' $TEST_notfound +do for TEST_exec in '' 'exec' + do for TEST_fork in '' 'ulimit -c 0;' + do for TEST_redirect in '' '>/dev/null' + do for TEST_substitute in '' ': $' + do + + TEST_test="$TEST_substitute($TEST_fork $TEST_exec $TEST_command $TEST_redirect)" + [[ $TEST_test == '('*([[:space:]])')' ]] && continue + r=$($SHELL -c ' + { + sleep 2 + kill -KILL $$ + } & + '"$TEST_test"' + kill $! + print ok + ') + [[ $r == ok ]] || err_exit "shell hangs on $TEST_test" + + done + done + done + done +done + +exit $Errors diff --git a/usr/src/lib/libshell/common/tests/substring.sh b/usr/src/lib/libshell/common/tests/substring.sh index 2859d65671..8d283c6a29 100644 --- a/usr/src/lib/libshell/common/tests/substring.sh +++ b/usr/src/lib/libshell/common/tests/substring.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -166,6 +166,21 @@ x=[123]def if [[ "${x//\[(*)\]/\{\1\}}" != {123}def ]] then err_exit 'closing brace escape not working' fi +xx=%28text%29 +if [[ ${xx//%28/abc\)} != 'abc)text%29' ]] +then err_exit '${xx//%28/abc\)} not working' +fi +xx='a:b' +str='(){}[]*?|&^%$#@l' +for ((i=0 ; i < ${#str}; i++)) +do [[ $(eval print -r -- \"\${xx//:/\\${str:i:1}}\") == "a${str:i:1}b" ]] || err_exit "substitution of \\${str:i:1}} failed" + [[ $(eval print -rn -- \"\${xx//:/\'${str:i:1}\'}\") == "a${str:i:1}b" ]] || err_exit "substitution of '${str:i:1}' failed" + [[ $(eval print -r -- \"\${xx//:/\"${str:i:1}\"}\") == "a${str:i:1}b" ]] || err_exit "substitution of \"${str:i:1}\" failed" +done +[[ ${xx//:/\\n} == 'a\nb' ]] || err_exit "substituion of \\\\n failed" +[[ ${xx//:/'\n'} == 'a\nb' ]] || err_exit "substituion of '\\n' failed" +[[ ${xx//:/"\n"} == 'a\nb' ]] || err_exit "substituion of \"\\n\" failed" +[[ ${xx//:/$'\n'} == $'a\nb' ]] || err_exit "substituion of \$'\\n' failed" unset foo foo=one/two/three if [[ ${foo//'/'/_} != one_two_three ]] @@ -245,13 +260,6 @@ fi if [[ ${var//+(\S)/Q} != 'Q Q' ]] then err_exit '${var//+(\S)/Q} not workding' fi -if [[ "$(LC_ALL=debug $SHELL <<- \+EOF+ - x=a<2bc><3xyz>g - print ${#x} - +EOF+)" != 4 - ]] -then err_exit '${#x} not working with multibyte locales' -fi foo='foo+bar+' [[ $(print -r -- ${foo//+/'|'}) != 'foo|bar|' ]] && err_exit "\${foobar//+/'|'}" [[ $(print -r -- ${foo//+/"|"}) != 'foo|bar|' ]] && err_exit '${foobar//+/"|"}' @@ -270,6 +278,10 @@ unset a b a='\[abc @(*) def\]' b='[abc 123 def]' [[ ${b//$a/\1} == 123 ]] || err_exit "\${var/pattern} not working with \[ in pattern" +unset foo +foo='(win32.i386) ' +[[ ${foo/'('/'(x11-'} == '(x11-win32.i386) ' ]] || err_exit "\${var/pattern} not working with ' in pattern" +$SHELL -c $'v=\'$(hello)\'; [[ ${v//\'$(\'/-I\'$(\'} == -I"$v" ]]' 2> /dev/null || err_exit "\${var/pattern} not working with \$( as pattern" unset X $SHELL -c '[[ ! ${X[@]:0:300} ]]' 2> /dev/null || err_exit '${X[@]:0:300} with X undefined fails' $SHELL -c '[[ ${@:0:300} == "$0" ]]' 2> /dev/null || err_exit '${@:0:300} with no arguments fails' @@ -501,4 +513,56 @@ then LC_ALL=en_US.UTF-8 $SHELL -c b1=$'"\342\202\254\342\202\254\342\202\254\342 fi { $SHELL -c 'unset x;[[ ${SHELL:$x} == $SHELL ]]';} 2> /dev/null || err_exit '${var:$x} fails when x is not set' { $SHELL -c 'x=;[[ ${SHELL:$x} == $SHELL ]]';} 2> /dev/null || err_exit '${var:$x} fails when x is null' + +# subject mode pattern result # +set -- \ + 'a$z' 'E' '[$]|#' 'a($)z' \ + 'a#z' 'E' '[$]|#' 'a(#)z' \ + 'a$z' 'Elr' '[$]|#' 'a$z' \ + 'a#z' 'Elr' '[$]|#' 'a#z' \ + 'a$' 'E' '[$]|#' 'a($)' \ + 'a#' 'E' '[$]|#' 'a(#)' \ + 'a$' 'Elr' '[$]|#' 'a$' \ + 'a#' 'Elr' '[$]|#' 'a#' \ + '$z' 'E' '[$]|#' '($)z' \ + '#z' 'E' '[$]|#' '(#)z' \ + '$z' 'Elr' '[$]|#' '$z' \ + '#z' 'Elr' '[$]|#' '#z' \ + '$' 'E' '[$]|#' '($)' \ + '#' 'E' '[$]|#' '(#)' \ + '$' 'Elr' '[$]|#' '($)' \ + '#' 'Elr' '[$]|#' '(#)' \ + 'a$z' 'E' '\$|#' 'a$z()' \ + 'a$z' 'E' '\\$|#' 'a$z' \ + 'a$z' 'E' '\\\$|#' 'a($)z' \ + 'a#z' 'E' '\\\$|#' 'a(#)z' \ + 'a$z' 'Elr' '\\\$|#' 'a$z' \ + 'a#z' 'Elr' '\\\$|#' 'a#z' \ + 'a$' 'E' '\\\$|#' 'a($)' \ + 'a#' 'E' '\\\$|#' 'a(#)' \ + 'a$' 'Elr' '\\\$|#' 'a$' \ + 'a#' 'Elr' '\\\$|#' 'a#' \ + '$z' 'E' '\\\$|#' '($)z' \ + '#z' 'E' '\\\$|#' '(#)z' \ + '$z' 'Elr' '\\\$|#' '$z' \ + '#z' 'Elr' '\\\$|#' '#z' \ + '$' 'E' '\\\$|#' '($)' \ + '#' 'E' '\\\$|#' '(#)' \ + '$' 'Elr' '\\\$|#' '($)' \ + '#' 'Elr' '\\\$|#' '(#)' \ +# do not delete this line # +unset i o +while (( $# >= 4 )) +do i=$1 + eval o="\${i/~($2)$3/\\(\\0\\)}" + if [[ "$o" != "$4" ]] + then err_exit "i='$1'; \${i/~($2)$3/\\(\\0\\)} failed -- expected '$4', got '$o'" + fi + eval o="\${i/~($2)($3)/\\(\\1\\)}" + if [[ "$o" != "$4" ]] + then err_exit "i='$1'; \${i/~($2)($3)/\\(\\1\\)} failed -- expected '$4', got '$o'" + fi + shift 4 +done + exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_builtin_sum.sh b/usr/src/lib/libshell/common/tests/sun_solaris_builtin_sum.sh new file mode 100644 index 0000000000..d2dc6db883 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_builtin_sum.sh @@ -0,0 +1,108 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Test whether the ksh93/libcmd sum builtin is compatible to +# Solaris/SystemV /usr/bin/sum +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors++ )) +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 + +typeset x + +builtin sum || err_exit "sum builtin not found" + +# Basic tests +x="$(print 'hello' | /usr/bin/sum)" || err_exit "/usr/bin/sum pipe failed." +[[ "$x" == "542 1" ]] || err_exit "print 'hello' | /usr/bin/sum did not return 542 1, got $x" +x="$(print 'hello' | sum)" || err_exit "sum builtin pipe failed." +[[ "$x" == "542 1" ]] || err_exit "print 'hello' | sum builtin did not return 542 1, got $x" +x="$(print 'hello' | sum -x md5)" || err_exit "sum md5 builtin pipe failed." +[[ "$x" == "b1946ac92492d2347c6235b4d2611184" ]] || err_exit "print 'hello' | sum md5 builtin did not return b1946ac92492d2347c6235b4d2611184, got $x" +x="$(print 'hello' | sum -x sha1)" || err_exit "sum sha1 builtin pipe failed." +[[ "$x" == "f572d396fae9206628714fb2ce00f72e94f2258f" ]] || err_exit "print 'hello' | sum sha1 builtin did not return f572d396fae9206628714fb2ce00f72e94f2258f, got $x" +x="$(print 'hello' | sum -x sha256)" || err_exit "sum sha256 builtin pipe failed." +[[ "$x" == "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03" ]] || err_exit "print 'hello' | sum sha256 builtin did not return 5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03, got $x" +x="$(print 'hello' | sum -x sha512)" || err_exit "sum sha512 builtin pipe failed." +[[ "$x" == "e7c22b994c59d9cf2b48e549b1e24666636045930d3da7c1acb299d1c3b7f931f94aae41edda2c2b207a36e10f8bcb8d45223e54878f5b316e7ce3b6bc019629" ]] || err_exit "print 'hello' | sum sha512 builtin did not return e7c22b994c59d9cf2b48e549b1e24666636045930d3da7c1acb299d1c3b7f931f94aae41edda2c2b207a36e10f8bcb8d45223e54878f5b316e7ce3b6bc019629, got $x" +# note that Solaris /usr/bin/cksum outputs $'3015617425\t6' (which may be a bug in Solaris /usr/bin/cksum) +x="$(print 'hello' | sum -x cksum)" || err_exit "sum cksum builtin pipe failed." +[[ "$x" == $'3015617425 6' ]] || err_exit "print 'hello' | sum cksum builtin did not return \$'3015617425 6', got $(printf "%q\n" "$x")" + +[[ "$(print 'hello' | /usr/bin/sum)" == "$(print 'hello' | sum)" ]] || err_exit "sum hello != /usr/bin/sum hello" +[[ "$(print 'fish' | /usr/bin/sum)" == "$(print 'fish' | sum)" ]] || err_exit "sum fish != /usr/bin/sum fish" +[[ "$(print '12345' | /usr/bin/sum)" == "$(print '12345' | sum)" ]] || err_exit "sum 12345 != /usr/bin/sum 12345" +[[ "$(print '\n\r\n \v' | /usr/bin/sum)" == "$(print '\n\r\n \v' | sum)" ]] || err_exit "sum spaces != /usr/bin/sum spaces" + +# Test some binary files... +x="/usr/bin/ls" +[[ "$(cat "$x" | /usr/bin/sum)" == "$(cat "$x" | sum)" ]] || err_exit "pipe: /usr/bin/sum $x != sum $x" +[[ "$(/usr/bin/sum "$x")" == "$(sum "$x")" ]] || err_exit "file: /usr/bin/sum $x != sum $x" + +x="/usr/bin/chmod" +[[ "$(cat "$x" | /usr/bin/sum)" == "$(cat "$x" | sum)" ]] || err_exit "pipe: /usr/bin/sum $x != sum $x" +[[ "$(/usr/bin/sum "$x")" == "$(sum "$x")" ]] || err_exit "file: /usr/bin/sum $x != sum $x" + +x="/usr/bin/tee" +[[ "$(cat "$x" | /usr/bin/sum)" == "$(cat "$x" | sum)" ]] || err_exit "pipe: /usr/bin/sum $x != sum $x" +[[ "$(/usr/bin/sum "$x")" == "$(sum "$x")" ]] || err_exit "file: /usr/bin/sum $x != sum $x" + +x="/usr/bin/grep" +[[ "$(cat "$x" | /usr/bin/sum)" == "$(cat "$x" | sum)" ]] || err_exit "pipe: /usr/bin/sum $x != sum $x" +[[ "$(/usr/bin/sum "$x")" == "$(sum "$x")" ]] || err_exit "file: /usr/bin/sum $x != sum $x" + +x="/usr/bin/egrep" +[[ "$(cat "$x" | /usr/bin/sum)" == "$(cat "$x" | sum)" ]] || err_exit "pipe: /usr/bin/sum $x != sum $x" +[[ "$(/usr/bin/sum "$x")" == "$(sum "$x")" ]] || err_exit "file: /usr/bin/sum $x != sum $x" + +x="/usr/bin/awk" +[[ "$(cat "$x" | /usr/bin/sum)" == "$(cat "$x" | sum)" ]] || err_exit "pipe: /usr/bin/sum $x != sum $x" +[[ "$(/usr/bin/sum "$x")" == "$(sum "$x")" ]] || err_exit "file: /usr/bin/sum $x != sum $x" + +x="/usr/bin/nawk" +[[ "$(cat "$x" | /usr/bin/sum)" == "$(cat "$x" | sum)" ]] || err_exit "pipe: /usr/bin/sum $x != sum $x" +[[ "$(/usr/bin/sum "$x")" == "$(sum "$x")" ]] || err_exit "file: /usr/bin/sum $x != sum $x" + +x="/usr/bin/ksh" +[[ "$(cat "$x" | /usr/bin/sum)" == "$(cat "$x" | sum)" ]] || err_exit "pipe: /usr/bin/sum $x != sum $x" +[[ "$(/usr/bin/sum "$x")" == "$(sum "$x")" ]] || err_exit "file: /usr/bin/sum $x != sum $x" + +x="/usr/bin/sh" +[[ "$(cat "$x" | /usr/bin/sum)" == "$(cat "$x" | sum)" ]] || err_exit "pipe: /usr/bin/sum $x != sum $x" +[[ "$(/usr/bin/sum "$x")" == "$(sum "$x")" ]] || err_exit "file: /usr/bin/sum $x != sum $x" + + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_compoundvario.sh b/usr/src/lib/libshell/common/tests/sun_solaris_compoundvario.sh new file mode 100644 index 0000000000..18aed2aa7a --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_compoundvario.sh @@ -0,0 +1,251 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +typeset -C bracketstat=( + integer bopen=0 + integer bclose=0 +) + +function count_brackets +{ + typeset x="$1" + typeset c + + integer i + (( bracketstat.bopen=0 , bracketstat.bclose=0 )) + + for (( i=0 ; i < ${#x} ; i++ )) ; do + c="${x:i:1}" + [[ "$c" == "(" ]] && (( bracketstat.bopen++ )) + [[ "$c" == ")" ]] && (( bracketstat.bclose++ )) + done + + (( bracketstat.bopen != bracketstat.bclose )) && return 1 + + return 0 +} + +integer Errors=0 + +typeset s + +# Test 1: +# Check whether "read -C" leaves the file pointer at the next line +# (and does not read beyond that point). +# Data layout is: +# -- snip -- +# <compound var> +# hello +# -- snip -- +# (additionally we test some extra stuff like bracket count) +s=${ + typeset -C x=( + a=1 b=2 + typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 ) + typeset -A myarray2=( [a]=1 [b]=2 ["c d"]=3 [e]=4 ["f"]=5 [g]=6 [h]=7 [i]=8 [j]=9 [k]=10 ) + typeset -A myarray3=( + [a]=( + float m1=0.5 + float m2=0.6 + foo="hello" + ) + [b]=( + foo="bar" + ) + ["c d"]=( + integer at=90 + ) + [e]=( + typeset -C nested_cpv=( + typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 ) + typeset str=$'a \'string' + ) + ) + [f]=( + typeset g="f" + ) + ) + ) + + { + printf "%B\n" x + print "hello" + } | { + read -C y + read s + } + print "x${s}x" +} || err_exit "test returned exit code $?" + +[[ "${s}" == "xhellox" ]] || err_exit "Expected 'xhellox', got ${s}" +count_brackets "$y" || err_exit "y: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}" + +# cleanup +unset x y || err_exit "unset failed" +[[ "$x" == "" ]] || err_exit "cleanup failed for x" +[[ "$y" == "" ]] || err_exit "cleanup failed for y" + + +# Test 2: +# Same as test 1 except one more compound var following the "hello" +# line. +# Data layout is: +# -- snip -- +# <compound var> +# hello +# <compound var> +# -- snip -- +s=${ + typeset -C x=( + a=1 b=2 + typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 ) + typeset -A myarray2=( [a]=1 [b]=2 ["c d"]=3 [e]=4 ["f"]=5 [g]=6 [h]=7 [i]=8 [j]=9 [k]=10 ) + typeset -A myarray3=( + [a]=( + float m1=0.5 + float m2=0.6 + foo="hello" + ) + [b]=( + foo="bar" + ) + ["c d"]=( + integer at=90 + ) + [e]=( + typeset -C nested_cpv=( + typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 ) + typeset str=$'a \'string' + ) + ) + [f]=( + typeset g="f" + ) + ) + ) + + { + printf "%B\n" x + print "hello" + printf "%B\n" x + } | { + read -C y1 + read s + read -C y2 + } + + print "x${s}x" +} || err_exit "test returned exit code $?" + +[[ "${s}" == "xhellox" ]] || err_exit "Expected 'xhellox', got ${s}." +[[ "$y1" != "" ]] || err_exit "y1 is empty" +[[ "$y2" != "" ]] || err_exit "y2 is empty" +count_brackets "$y1" || err_exit "y1: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}" +count_brackets "$y2" || err_exit "y2: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}" +[[ "$y1" == "$y2" ]] || err_exit "Expected $(printf "%q\n" "${y1}") == $(printf "%q\n" "${y2}")." +[[ "$x" == "$y1" ]] || err_exit "Expected $(printf "%q\n" "${x}") == $(printf "%q\n" "${y}")." + +# cleanup +unset x y1 y2 || err_exit "unset failed" +[[ "$x" == "" ]] || err_exit "cleanup failed for x" +[[ "$y1" == "" ]] || err_exit "cleanup failed for y1" +[[ "$y2" == "" ]] || err_exit "cleanup failed for y2" + + +# Test 3: Test compound variable copy operator vs. "read -C" +typeset -C x=( + a=1 b=2 + typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 ) + typeset -A myarray2=( [a]=1 [b]=2 ["c d"]=3 [e]=4 ["f"]=5 [g]=6 [h]=7 [i]=8 [j]=9 [k]=10 ) + typeset -A myarray3=( + [a]=( + float m1=0.5 + float m2=0.6 + foo="hello" + ) + [b]=( + foo="bar" + ) + ["c d"]=( + integer at=90 + ) + [e]=( + typeset -C nested_cpv=( + typeset -a myarray=( 1 2 3 4 5 6 7 8 9 10 ) + typeset str=$'a \'string' + ) + ) + [f]=( + typeset g="f" + ) + ) +) + +typeset -C x_copy=x || err_exit "x_copy copy failed" +[[ "${x_copy}" != "" ]] || err_exit "x_copy should not be empty" +count_brackets "${x_copy}" || err_exit "x_copy: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}" + +typeset -C nested_cpv_copy + +nested_cpv_copy=x.myarray3[e].nested_cpv || err_exit "x.myarray3[e].nested_cpv copy failed" + +# unset branch "x.myarray3[e].nested_cpv" of the variable tree "x" ... +unset x.myarray3[e].nested_cpv || err_exit "unset x.myarray3[e].nested_cpv failed" +[[ "${x.myarray3[e].nested_cpv}" == "" ]] || err_exit "x.myarray3[e].nested_cpv still has a value" + +# ... and restore it from the saved copy +printf "%B\n" nested_cpv_copy | read -C x.myarray3[e].nested_cpv || err_exit "read failed" + +# compare copy of the original tree and the modified one +[[ "${x}" == "${x_copy}" ]] || err_exit "x != x_copy" +count_brackets "${x}" || err_exit "x: bracket open ${bracketstat.bopen} != bracket close ${bracketstat.bclose}" + +# cleanup +unset x x_copy nested_cpv_copy || err_exit "unset failed" + + +# Test 4: Test "read -C" failure for missing bracket at the end +typeset s +s=$($SHELL -c 'typeset -C myvar ; print "( unfinished=1" | read -C myvar 2>/dev/null || print "error $?"') || err_exit "shell failed" +[[ "$s" == "error 3" ]] || err_exit "compound_read: expected error 3, got ${s}" + + +# Test 5: Test "read -C" failure for missing bracket at the beginning +typeset s +s=$($SHELL -c 'typeset -C myvar ; print " unfinished=1 )" | read -C myvar 2>/dev/null || print "error $?"') || err_exit "shell failed" +[[ "$s" == "error 3" ]] || err_exit "compound_read: expected error 3, got ${s}" + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_cr_6687139_command_substitution_exec_redirection_allocation_loop.sh b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6687139_command_substitution_exec_redirection_allocation_loop.sh new file mode 100644 index 0000000000..335470aa79 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6687139_command_substitution_exec_redirection_allocation_loop.sh @@ -0,0 +1,212 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This test checks whether the issue described in CR #6687139 +# ("command substitution, exec, and stdout redirection cause +# allocation loop") has been fixed: +# -- snip -- +# The following one-liner (including back ticks) causes ksh93 to spin +# out of control consuming all memory at a *very* rapid pace: +# +# `exec program > file` +# +# If "file" is a real file (as opposed to /dev/null), the file will +# also grow without bound. "program" need not exist, i.e. +# +# `exec > file` +# +# has the same result. Using $() instead of `` also has the same +# effect. +# +# This works fine under all other bourne-compatible shells. +# -- snip -- +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors++ )) +} +alias err_exit='err_exit $LINENO' + +function isvalidpid +{ + kill -0 ${1} 2>/dev/null && return 0 + return 1 +} + +Command=${0##*/} +integer Errors=0 + +integer childpid +typeset testdir +integer childretval + +testdir="/tmp/sun_solaris_cr_6687139_pid$$_${PPID}" +mkdir -p "${testdir}" || err_exit "Cannot create test dirctory" +cd "${testdir}" || { err_exit "Cannot cd to test dirctory" ; exit $Errors ; } + +############################################################################## +## +## test variant 1a: Use command substitution $( ... ) +## + +# Run testcase with "nice" to make sure a "runaway" process can +# still be caught&&terminated by the current shell +nice -n 10 ${SHELL} -c 'touch z ; $(exec nosuchprogram_for_cr_6687139 > z) ; rm z' 2>/dev/null & +childpid=$! + +sleep 5 + +if isvalidpid ${childpid} ; then + # First _stop_, then log error since the child may eat up memory + # VERY VERY quickly + kill -STOP ${childpid} + + err_exit "Child still active after 5 seconds (hang ?)" + + # Get sample stack trace + pstack ${childpid} + kill -KILL ${childpid} +fi + +# collect child's return status +wait ${childpid} +childretval=$? + +(( childretval == 0 )) || err_exit "Child returned non-zero exit code ${childretval}." + +[[ ! -f "z" ]] || { rm "z" ; err_exit "Child did not remove test file" ; } + + +############################################################################## +## +## test variant 1b: Same as test 1a but forces the shell to |fork()| for the +## subshell +## + +# Run testcase with "nice" to make sure a "runaway" process can +# still be caught&&terminated by the current shell +nice -n 10 ${SHELL} -c 'touch z ; $(ulimit -c 0 ; exec nosuchprogram_for_cr_6687139 > z) ; rm z' 2>/dev/null & +childpid=$! + +sleep 5 + +if isvalidpid ${childpid} ; then + # First _stop_, then log error since the child may eat up memory + # VERY VERY quickly + kill -STOP ${childpid} + + err_exit "Child still active after 5 seconds (hang ?)" + + # Get sample stack trace + pstack ${childpid} + kill -KILL ${childpid} +fi + +# collect child's return status +wait ${childpid} +childretval=$? + +(( childretval == 0 )) || err_exit "Child returned non-zero exit code ${childretval}." + +[[ ! -f "z" ]] || { rm "z" ; err_exit "Child did not remove test file" ; } + + +############################################################################## +## +## test variant 2a: Use plain subshell ( ... ) +## + +# Run testcase with "nice" to make sure a "runaway" process can +# still be caught&&terminated by the current shell +nice -n 10 ${SHELL} -c 'touch z ; (exec nosuchprogram_for_cr_6687139 > z) ; rm z' 2>/dev/null & +childpid=$! + +sleep 5 + +if isvalidpid ${childpid} ; then + # First _stop_, then log error since the child may eat up memory + # VERY VERY quickly + kill -STOP ${childpid} + + err_exit "Child still active after 5 seconds (hang ?)" + + # Get sample stack trace + pstack ${childpid} + kill -KILL ${childpid} +fi + +# collect child's return status +wait ${childpid} +childretval=$? + +(( childretval == 0 )) || err_exit "Child returned non-zero exit code ${childretval}." + +[[ ! -f "z" ]] || { rm "z" ; err_exit "Child did not remove test file" ; } + + +############################################################################## +## +## test variant 2b: Same as test 2a but forces the shell to |fork()| for the +## subshell +## + +# Run testcase with "nice" to make sure a "runaway" process can +# still be caught&&terminated by the current shell +nice -n 10 ${SHELL} -c 'touch z ; (ulimit -c 0 ; exec nosuchprogram_for_cr_6687139 > z) ; rm z' 2>/dev/null & +childpid=$! + +sleep 5 + +if isvalidpid ${childpid} ; then + # First _stop_, then log error since the child may eat up memory + # VERY VERY quickly + kill -STOP ${childpid} + + err_exit "Child still active after 5 seconds (hang ?)" + + # Get sample stack trace + pstack ${childpid} + kill -KILL ${childpid} +fi + +# collect child's return status +wait ${childpid} +childretval=$? + +(( childretval == 0 )) || err_exit "Child returned non-zero exit code ${childretval}." + +[[ ! -f "z" ]] || { rm "z" ; err_exit "Child did not remove test file" ; } + +# tests done, remove temporary test subdir +cd /tmp +rmdir "${testdir}" || err_exit "Could not remove temporary test directory ${testdir}" + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_cr_6713682_compound_var_bleeds_through_subshell.sh b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6713682_compound_var_bleeds_through_subshell.sh new file mode 100644 index 0000000000..4f03984951 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6713682_compound_var_bleeds_through_subshell.sh @@ -0,0 +1,108 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Test whether CR #6713682 has been fixed. +# +# Creating a compound variable in s subshell "bleeds through" to the calling subshell in some conditions. +# Example: +# -- snip -- +# $ ksh93 -c 'unset l ; ( l=( a=1 b="BE" ) ; print "$l" ) ; print $l' +# ( +# a=1 +# b=BE +# ) +# ( ) +# -- snip -- +# The first bracket pair is Ok since it's coming from $ print "$l" # , however the 2nd pair comes from the print $l _outside_ the subshell where the variable "l" should no longer exist. +# +# Workaround: +# Force ksh93 to call |fork()| for the matching subshell using $ ulimit -c #, e.g. ... +# -- snip -- +# $ ksh93 -c 'unset l ; ( ulimit -c 0 ; l=( a=1 b="BE" ) ; print "$l" ) ; print $l' +# ( +# a=1 +# b=BE +# ) +# -- snip -- +# ... provides the correct output. +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + +typeset var1 var2 + +# use unset, l=() compound syntax and print +var1="$(${SHELL} -c 'unset l ; ( l=( a=1 b="BE" ) ; print "$l" ) ; print $l')" || err_exit "Non-zero exit code." +var2="$(${SHELL} -c 'unset l ; ( ulimit -c 0 ; l=( a=1 b="BE" ) ; print "$l" ) ; print $l')" || err_exit "Non-zero exit code." +[[ "${var1}" == "${var2}" ]] || err_exit "Non-fork()'ed subshell output differes from fork()'ed subshell output (with unset)." + +# do not use unset, l=() compound syntax and print +var1="$(${SHELL} -c '( l=( a=1 b="BE" ) ; print "$l" ) ; print $l')" || err_exit "Non-zero exit code." +var2="$(${SHELL} -c '( ulimit -c 0 ; l=( a=1 b="BE" ) ; print "$l" ) ; print $l')" || err_exit "Non-zero exit code." +[[ "${var1}" == "${var2}" ]] || err_exit "Non-fork()'ed subshell output differes from fork()'ed subshell output (without unset)." + +# use unset, typeset -C compound syntax and print +var1="$(${SHELL} -c 'unset l ; ( typeset -C l ; l.a=1 ; l.b="BE" ; print "$l" ) ; print $l')" || err_exit "Non-zero exit code." +var2="$(${SHELL} -c 'unset l ; ( ulimit -c 0 ; typeset -C l ; l.a=1 ; l.b="BE" ; print "$l" ) ; print $l')" || err_exit "Non-zero exit code." +[[ "${var1}" == "${var2}" ]] || err_exit "Non-fork()'ed subshell output differes from fork()'ed subshell output (with unset)." + +# do not use unset, typeset -C compound syntax and print +var1="$(${SHELL} -c '( typeset -C l ; l.a=1 ; l.b="BE" ; print "$l" ) ; print $l')" || err_exit "Non-zero exit code." +var2="$(${SHELL} -c '( ulimit -c 0 ; typeset -C l ; l.a=1 ; l.b="BE" ; print "$l" ) ; print $l')" || err_exit "Non-zero exit code." +[[ "${var1}" == "${var2}" ]] || err_exit "Non-fork()'ed subshell output differes from fork()'ed subshell output (with unset)." + +# use unset, l=() compound syntax and printf "%B\n" +var1="$(${SHELL} -c 'unset l ; ( l=( a=1 b="BE" ) ; printf "%B\n" l ) ; printf "%B\n" l')" || err_exit "Non-zero exit code." +var2="$(${SHELL} -c 'unset l ; ( ulimit -c 0 ; l=( a=1 b="BE" ) ; printf "%B\n" l ) ; printf "%B\n" l')" || err_exit "Non-zero exit code." +[[ "${var1}" == "${var2}" ]] || err_exit "Non-fork()'ed subshell output differes from fork()'ed subshell output (with unset)." + +# do not use unset, l=() compound syntax and printf "%B\n" +var1="$(${SHELL} -c '( l=( a=1 b="BE" ) ; printf "%B\n" l) ; printf "%B\n" l')" || err_exit "Non-zero exit code." +var2="$(${SHELL} -c '( ulimit -c 0 ; l=( a=1 b="BE" ) ; printf "%B\n" l) ; printf "%B\n" l')" || err_exit "Non-zero exit code." +[[ "${var1}" == "${var2}" ]] || err_exit "Non-fork()'ed subshell output differes from fork()'ed subshell output (without unset)." + +# use unset, typeset -C compound syntax and printf "%B\n" +var1="$(${SHELL} -c 'unset l ; ( typeset -C l ; l.a=1 ; l.b="BE" ; printf "%B\n" l) ; printf "%B\n" l')" || err_exit "Non-zero exit code." +var2="$(${SHELL} -c 'unset l ; ( ulimit -c 0 ; typeset -C l ; l.a=1 ; l.b="BE" ; printf "%B\n" l) ; printf "%B\n" l')" || err_exit "Non-zero exit code." +[[ "${var1}" == "${var2}" ]] || err_exit "Non-fork()'ed subshell output differes from fork()'ed subshell output (with unset)." + +# do not use unset, typeset -C compound syntax and printf "%B\n" +var1="$(${SHELL} -c '( typeset -C l ; l.a=1 ; l.b="BE" ; printf "%B\n" l) ; printf "%B\n" l')" || err_exit "Non-zero exit code." +var2="$(${SHELL} -c '( ulimit -c 0 ; typeset -C l ; l.a=1 ; l.b="BE" ; printf "%B\n" l) ; printf "%B\n" l')" || err_exit "Non-zero exit code." +[[ "${var1}" == "${var2}" ]] || err_exit "Non-fork()'ed subshell output differes from fork()'ed subshell output (with unset)." + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_cr_6722134_background_CHLD_trap.sh b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6722134_background_CHLD_trap.sh new file mode 100644 index 0000000000..333f5cc1a0 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6722134_background_CHLD_trap.sh @@ -0,0 +1,119 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This test checks whether ksh93 (like ksh88) generates calls a +# CHLD/SIGCHLD trap for background jobs and _not_ for foreground jobs. +# +# This was reported as CR #6722134 ("*ksh93* (20080624_snapshot) +# doesn't execute CHLD trap"): +# -- snip -- +# With "set -o monitor" on and "set -o notify" off, ksh88 executes the CHLD +# trap while waiting for interactive input when a background job completes. +# ksh93 appears not to execute the CHLD trap when a background job terminates. +# Probably related: I noticed that with no CHLD trap set, but -o monitor and +# -o notify set, there should be a similar asynchronous job completion notice. +# It works in ksh88 but not in this ksh93 build. +# -- snip -- +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + +## +## test one: +## +s="$($SHELL -c ' +set -o errexit +integer i + +trap "print got_child" SIGCHLD + +sleep 5 & +sleep 7 & +for ((i=0 ; i < 15 ; i++)) ; do + print $i + sleep 1 + + # external, non-background command for which a SIGCHLD should + # _not_ be fired + /bin/true >/dev/null +done +print "loop finished" +wait +print "done" +' 2>&1 )" || err_exit "test loop failed." + +[[ "$s" == ~(Er)$'14\nloop finished\ndone' ]] || err_exit "Expected '14\nloop finished\ndone' at the end of the output, got ${s}." +[[ "$s" == ~(El)$'0\n1\n2' ]] || err_exit "Expected '0\n1\n2' as at the beginning of the output, got ${s}." + +integer count +(( count=$(fgrep "got_child" <<< "$s" | wc -l) )) || err_exit "counting failed." +(( count == 2 )) || err_exit "Expected count==2, got count==${count}." + + +## +## test two: +## (same as test "one" except that this test has one more "sleep" child) +## +s="$($SHELL -c ' +set -o errexit +integer i + +trap "print got_child" SIGCHLD + +sleep 5 & +sleep 7 & +sleep 9 & +for ((i=0 ; i < 15 ; i++)) ; do + print $i + sleep 1 + + # external, non-background command for which a SIGCHLD should + # _not_ be fired + /bin/true >/dev/null +done +print "loop finished" +wait +print "done" +' 2>&1 )" || err_exit "test loop failed." + +[[ "$s" == ~(Er)$'14\nloop finished\ndone' ]] || err_exit "Expected '14\nloop finished\ndone' at the end of the output, got ${s}." +[[ "$s" == ~(El)$'0\n1\n2' ]] || err_exit "Expected '0\n1\n2' as at the beginning of the output, got ${s}." + +(( count=$(fgrep "got_child" <<< "$s" | wc -l) )) || err_exit "counting failed." +(( count == 3 )) || err_exit "Expected count==3, got count==${count}." + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_cr_6753538_subshell_leaks_umask.sh b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6753538_subshell_leaks_umask.sh new file mode 100644 index 0000000000..c755f86c87 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6753538_subshell_leaks_umask.sh @@ -0,0 +1,97 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Test whether CR #6753538 ("umask modification leaks out of a ksh93 +# subshell") has been fixed. +# +# Quote from CR #6753538: +# -- snip -- +# I discovered that Solaris 11's /bin/sh exhibits the following +# surprising behavior: +# +# $ /bin/sh -c 'umask 22; (umask 0); umask' +# 0000 +# +# All other shells I tried print 22. +# -- snip -- + + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + +# +# test set 1: Simple umask in subshell +# +x=$(${SHELL} -c 'umask 22; (umask 0); umask') +[[ "$x" == "0022" ]] || err_exit "expected umask 0022, got $x" + +x=$(${SHELL} -c 'umask 20; (umask 0); umask') +[[ "$x" == "0020" ]] || err_exit "expected umask 0020, got $x" + +x=$(${SHELL} -c 'umask 0; (umask 22); umask') +[[ "$x" == "0000" ]] || err_exit "expected umask 0000, got $x" + + +# +# test set 2: Simple umask in two subshells +# +x=$(${SHELL} -c 'umask 22; ( (umask 10); umask 0); umask') +[[ "$x" == "0022" ]] || err_exit "expected umask 0022, got $x" + +x=$(${SHELL} -c 'umask 20; ( (umask 10); umask 0); umask') +[[ "$x" == "0020" ]] || err_exit "expected umas k 0020, got $x" + +x=$(${SHELL} -c 'umask 0; ( (umask 10); umask 22); umask') +[[ "$x" == "0000" ]] || err_exit "expected umask 0000, got $x" + + +# +# test set 3: Compare normal subshell vs. subshell in seperate process +# ($ ulimit -c 0 # forced the subshell to |fork()| +# +x=$(${SHELL} -c 'umask 22; ( umask 0); umask') || err_exit "shell failed." +y=$(${SHELL} -c 'umask 22; (ulimit -c 0 ; umask 0); umask') || err_exit "shell failed." +[[ "$x" == "$y" ]] || err_exit "$x != $y" + +x=$(${SHELL} -c 'umask 20; ( umask 0); umask') || err_exit "shell failed." +y=$(${SHELL} -c 'umask 20; (ulimit -c 0 ; umask 0); umask') || err_exit "shell failed." +[[ "$x" == "$y" ]] || err_exit "$x != $y" + +x=$(${SHELL} -c 'umask 0; ( umask 20); umask') || err_exit "shell failed." +y=$(${SHELL} -c 'umask 0; (ulimit -c 0 ; umask 20); umask') || err_exit "shell failed." +[[ "$x" == "$y" ]] || err_exit "$x != $y" + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_cr_6754020_weird_square_bracket_expansion.sh b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6754020_weird_square_bracket_expansion.sh new file mode 100644 index 0000000000..d4bd345833 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6754020_weird_square_bracket_expansion.sh @@ -0,0 +1,78 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Test whether CR #6754020 ("ksh93 does weird '[' expansion") has +# been fixed. +# +# Quote from CR #6754020: +# ---- snip ---- +# The problem is that subprocess uses /bin/sh as the shell when it +# spins off the process. As Brad demonstrated: +# /bin/sh -c 'echo F[[O]' +# F[[O][ +# +# In short, this bug only appears when run through the test suite, +# or by people running /bin/sh who don't understand how their shell +# treats special characters. +# -- snip -- +# +# In this case ksh93 has a bug which causes "F[[O]" to be expanded +# in a wrong way. +# ---- snip ---- + + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + +typeset s + +# test using "echo" +s="$(${SHELL} -c 'echo F[[O]')" +[[ "$s" == 'F[[O]' ]] || err_exit "Expected 'F[[O]', got $s" + +s="$(${SHELL} -c 'echo F[[[O]]')" +[[ "$s" == 'F[[[O]]' ]] || err_exit "Expected 'F[[[O]]', got $s" + + +# test using "print" +s="$(${SHELL} -c 'print F[[O]')" +[[ "$s" == 'F[[O]' ]] || err_exit "Expected 'F[[O]', got $s" + +s="$(${SHELL} -c 'print F[[[O]]')" +[[ "$s" == 'F[[[O]]' ]] || err_exit "Expected 'F[[[O]]', got $s" + + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_cr_6763594_command_failure_execs_twice.sh b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6763594_command_failure_execs_twice.sh new file mode 100644 index 0000000000..f3ffae5e84 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6763594_command_failure_execs_twice.sh @@ -0,0 +1,95 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Test whether CR #6763594 ('ksh93 executes command after "command" +# builtin twice on failure') has been fixed. +# +# Quote from CR #6763594: +# ---- snip ---- +# ksh93 has a bug which causes shell to execute the command after the +# "command" builtin to be executed twice if "command" fails: +# -- snip -- +# $ ksh93 -x -c 'print "true" >myfoo ; chmod a+x,a-r myfoo ; command ./myfoo ; +# print $?' +# + print true +# + 1> myfoo +# + chmod a+x,a-r myfoo +# + command ./myfoo +# ksh93[1]: ./myfoo: ./myfoo: cannot open [Permission denied] +# + print 1 +# 1 +# + print 0 +# 0 +# -- snip -- +# The "print" command at the end is executed twice in this case since +# the shell jumps to the wrong position in the execution sequence. +# +# The correct output should be: +# -- snip -- +# $ ksh93 -x -c 'print "true" >myfoo ; chmod a+x,a-r myfoo ; command ./myfoo ; +# print $?' +# + print true +# + 1> myfoo +# + chmod a+x,a-r myfoo +# + command ./myfoo +# ksh93[1]: ./myfoo: ./myfoo: cannot open [Permission denied] +# + print 1 +# 1 +# -- snip -- +# ---- snip ---- + + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + +typeset testtmpdir=/tmp/ksh93_test_cr_6763594_${PPID}_$$ +mkdir "${testtmpdir}" || { err_exit "Could not create temporary directory ${testtmpdir}." ; exit ${Errors} ; } + +cd "${testtmpdir}" || { err_exit "Cannot cd to temporary directory ${testtmpdir}." ; exit ${Errors} ; } + +typeset s + +${SHELL} -c 'print "true" >myfoo ; chmod a+x,a-r myfoo ; command ./myfoo ; print $?' 1>out_stdout 2>out_stderr +(( $? == 0 )) || err_exit "Return code $?, expected 0" + +s=$( < out_stdout ) ; [[ "$s" == '126' ]] || err_exit "Expected '126', got $(printf "%q\n" "$s")." +s=$( < out_stderr ) ; [[ "$s" == ~(Elr)(.*:\ \./myfoo:\ \./myfoo:\ .*\[.*\]) ]] || err_exit "Output $(printf "%q\n" "$s") does not match pattern '~(Elr)(.*:\ \./myfoo:\ \./myfoo:\ .*\[.*\])'." + +rm "myfoo" "out_stdout" "out_stderr" || err_exit "rm failed." +cd .. +rmdir "${testtmpdir}" || err_exit "Failed to remove temporary directory ${testtmpdir}." + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_cr_6766246_pattern_matching_bug.sh b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6766246_pattern_matching_bug.sh new file mode 100644 index 0000000000..fc83db0e8a --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_cr_6766246_pattern_matching_bug.sh @@ -0,0 +1,172 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Test whether CR #6766246 ("bug in pattern matching") has been fixed. +# +# Quote from CR #6766246: +# ---- snip ---- +# The bootstrap script of pkgsrc contains this code +# checkarg_sane_absolute_path() { +# case "$1" in +# "") ;; # the default value will be used. +# *[!-A-Za-z0-9_./]*) +# die "ERROR: Invalid characters in path $1 (from $2)." ;; +# /*) ;; +# *) die "ERROR: The argument to $2 must be an absolute path." ;; +# esac +# } +# It turns out, the leading "!" in the pattern is not interpreted +# as negation, and the first "-" not as a literal. Instead the +# character range "! to A" is constructed. Paths containing "%" +# or "@" are accepted, but paths containing "-" are rejected. +# Note that this interpretation makes the whole pattern +# syntactically wrong, which isn't noticed either. +# +# Test case: +# -- snip -- +# !/bin/sh +# case "$1" in +# *[!-A-Za-z0-9_./]*) +# echo invalid characters used in $1 +# ;; +# *) +# echo only valid characters used in $1 +# ;; +# esac +# -- snip -- +# Expected Result: +# strings containing a "-" should be accepted, strings containing +# a "@" should be rejected +# Actual Result: +# strings containing a "-" are rejected, strings containing a +# "@" are accepted +# Workaround +# The pattern "*[!A-Za-z0-9_./-]*" (i.e. shifting the dash to +# the end) works as expected. +# ---- snip ---- + + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + + +## test 1 (based on the bug report): + +function do_match +{ + case "$1" in + *[!-A-Za-z0-9_./]*) + print "match" + ;; + *) + print "nomatch" + ;; + esac + return 0 +} + +typeset pat + +pat="foo-bar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched." +pat="foo+bar" ; [[ "$(do_match "${pat}")" == "match" ]] || err_exit "${pat} not matched." +pat="foo/bar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched." +pat="foo_bar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched." +pat="foo@bar" ; [[ "$(do_match "${pat}")" == "match" ]] || err_exit "${pat} not matched." +pat="foobar-" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched." +pat="foobar+" ; [[ "$(do_match "${pat}")" == "match" ]] || err_exit "${pat} not matched." +pat="foobar/" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched." +pat="foobar_" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched." +pat="foobar@" ; [[ "$(do_match "${pat}")" == "match" ]] || err_exit "${pat} not matched." +pat="-foobar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched." +pat="+foobar" ; [[ "$(do_match "${pat}")" == "match" ]] || err_exit "${pat} not matched." +pat="/foobar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched." +pat="_foobar" ; [[ "$(do_match "${pat}")" == "nomatch" ]] || err_exit "${pat} matched." +pat="@foobar" ; [[ "$(do_match "${pat}")" == "match" ]] || err_exit "${pat} not matched." + + +## test 2 (gsf's test chain): + +# Make sure LC_COLLATE has a value +if [[ "${LC_COLLATE}" == "" ]] ; then + if [[ ${LANG} != "" && "${LC_ALL}" == "" ]] ; then + LC_COLLATE="${LANG}" + fi +fi + +if [[ "${LC_ALL}" != "" ]] ; then + LC_COLLATE="${LC_ALL}" +fi + +[[ "${LC_COLLATE}" != "" ]] || err_exit "LC_COLLATE empty." + +set -- \ + 'A' 0 1 1 0 1 1 1 0 0 1 0 0 \ + 'Z' 0 1 1 0 1 1 1 0 0 1 0 0 \ + '/' 0 0 0 0 0 0 1 1 1 1 1 1 \ + '.' 0 0 0 0 0 0 1 1 1 1 1 1 \ + '_' 0 0 0 0 0 0 1 1 1 1 1 1 \ + '-' 1 1 1 1 1 1 0 0 0 0 0 0 \ + '%' 0 0 0 0 0 0 1 1 1 1 1 1 \ + '@' 0 0 0 0 0 0 1 1 1 1 1 1 \ + '!' 0 0 0 0 0 0 1 1 1 1 1 1 \ + '^' 0 0 0 0 0 0 1 1 1 1 1 1 \ + # retain this line # +while (( $# >= 13 )) ; do + c=$1 + shift + for p in \ + '[![.-.]]' \ + '[![.-.][:upper:]]' \ + '[![.-.]A-Z]' \ + '[!-]' \ + '[!-[:upper:]]' \ + '[!-A-Z]' \ + '[[.-.]]' \ + '[[.-.][:upper:]]' \ + '[[.-.]A-Z]' \ + '[-]' \ + '[-[:upper:]]' \ + '[-A-Z]' \ + # retain this line # + do e=$1 + shift + [[ $c == $p ]] + g=$? + [[ $g == $e ]] || err_exit "[[ '$c' == $p ]] for LC_COLLATE=$l failed -- expected $e, got $g" + done +done + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_getconf.sh b/usr/src/lib/libshell/common/tests/sun_solaris_getconf.sh index a8eaeb75f6..2089ad184c 100644 --- a/usr/src/lib/libshell/common/tests/sun_solaris_getconf.sh +++ b/usr/src/lib/libshell/common/tests/sun_solaris_getconf.sh @@ -1,5 +1,3 @@ -#!/bin/ksh93 - # # CDDL HEADER START # @@ -22,11 +20,9 @@ # # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# # # sun_solaris_getconf.sh - test the ksh93 getconf builtin for compatibility @@ -38,7 +34,7 @@ function err_exit { print -u2 -n "\t" print -u2 -r ${Command}[$1]: "${@:2}" - let Errors+=1 + (( Errors++ )) } alias err_exit='err_exit $LINENO' @@ -52,24 +48,29 @@ integer getconf_keys # counts tests (paranoid check to make sure the test loop w export PATH=/usr/bin:/bin # prechecks -[ ! -f "/bin/getconf" ] && err_exit '/bin/getconf not found.' -[ ! -x "/bin/getconf" ] && err_exit '/bin/getconf not executable.' - +[[ ! -f "/bin/getconf" ]] && err_exit '/bin/getconf not found.' +[[ ! -x "/bin/getconf" ]] && err_exit '/bin/getconf not executable.' + +# Define test functions and store them in a string for repeated usagae +# (we can't use "functions" (alias "typeset -f") since this does not +# work in compiled shell scripts) +typeset -r getconf_test_functions="$( +cat <<EOF # compare builtin getconf output with /usr/bin/getconf function compare_normal { mismach=0 getconf_keys=0 /usr/bin/getconf -a | while read i ; do - let getconf_keys++ + (( getconf_keys++ )) t="${i%:*}" a="$(getconf "$t" 2>/dev/null)" b="$(/usr/bin/getconf "$t" 2>/dev/null)" - if [ "$a" != "$b" ] ; then + if [[ "$a" != "$b" ]] ; then print -u2 "getconf/normal built mismatch: |$t|:|$a| != |$b|" - let mismatch++ + (( mismatch++ )) fi done } @@ -80,49 +81,54 @@ function compare_path mismach=0 getconf_keys=0 /usr/bin/getconf -a | while read i ; do - let getconf_keys++ + (( getconf_keys++ )) t="${i%:*}" a="$(getconf "$t" "/tmp" 2>/dev/null)" b="$(/usr/bin/getconf "$t" "/tmp" 2>/dev/null)" - if [ "$a" != "$b" ] ; then + if [[ "$a" != "$b" ]] ; then print -u2 "getconf/path built mismatch: |$t|:|$a| != |$b|" - let mismatch++ + (( mismatch++ )) fi done } +EOF +)" + +print -r -- "$getconf_test_functions" | source /dev/stdin # future versions of this test should test the following ${PATH}s, too: # "/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin" \ #"/usr/xpg4/bin:/bin:/usr/bin" \ for i in \ + "/usr/bin:/bin" \ "/bin:/usr/bin" do export PATH="${i}" ## test whether the getconf builtin is available - if [ "$(builtin | fgrep "/bin/getconf")" = "" ] ; then + if [[ "$(builtin | fgrep "/bin/getconf")" = "" ]] ; then err_exit '/bin/getconf not found in the list of builtins.' fi ## compare "getconf -a" output - if [ "$(getconf -a)" != "$(/usr/bin/getconf -a)" ] ; then + if [[ "$(getconf -a)" != "$(/usr/bin/getconf -a)" ]] ; then err_exit 'getconf -a output mismatch.' fi ## check for a key which is only supported by the AST builtin version of getconf: - if [ "$(getconf LIBPREFIX)" != "lib" ] ; then + if [[ "$(getconf LIBPREFIX)" != "lib" ]] ; then err_exit 'getconf LIBPREFIX did not return "lib".' fi ## run normal test compare_normal - [ ${getconf_keys} -eq 0 ] && err_exit "getconf/normal not working (PATH=${PATH})." - [ ${mismatch} -gt 0 ] && err_exit "getconf/normal test found ${mismatch} differences (PATH=${PATH})." + (( getconf_keys == 0 )) && err_exit "getconf/normal not working (PATH=${PATH})." + (( mismatch > 0 )) && err_exit "getconf/normal test found ${mismatch} differences (PATH=${PATH})." # run the same test in a seperate shell # (we explicitly test this because ast-ksh.2007-01-11 picks up /usr/xpg6/bin/getconf @@ -130,31 +136,30 @@ do # contains /usr/xpg6/bin before ksh93 is started)). ${SHELL} -c "integer mismatch ; \ integer getconf_keys ; \ - $(functions) ; \ + ${getconf_test_functions} ; \ compare_normal ; - [ \${getconf_keys} -eq 0 ] && err_exit \"getconf/normal not working (PATH=\${PATH}).\" ; \ - [ \${mismatch} -gt 0 ] && err_exit \"getconf/normal test found \${mismatch} differences (PATH=\${PATH}).\" ; \ + (( getconf_keys == 0 )) && err_exit \"getconf/normal not working (PATH=\${PATH}).\" ; \ + (( mismatch > 0 )) && err_exit \"getconf/normal test found \${mismatch} differences (PATH=\${PATH}).\" ; \ exit $((Errors))" - let Errors+=$? + (( Errors+=$? )) ## run test with path argument compare_path - [ ${getconf_keys} -eq 0 ] && err_exit "getconf/path not working." - [ ${mismatch} -gt 0 ] && err_exit "getconf/path test found ${mismatch} differences." + (( getconf_keys == 0 )) && err_exit "getconf/path not working." + (( mismatch > 0 )) && err_exit "getconf/path test found ${mismatch} differences." # run the same test in a seperate shell # (see comment above) ${SHELL} -c "integer mismatch ; \ integer getconf_keys ; \ - $(functions) ; \ + ${getconf_test_functions} ; \ compare_path ; - [ \${getconf_keys} -eq 0 ] && err_exit \"getconf/normal not working (PATH=\${PATH}).\" ; \ - [ \${mismatch} -gt 0 ] && err_exit \"getconf/normal test found \${mismatch} differences (PATH=\${PATH}).\" ; \ + (( getconf_keys == 0 )) && err_exit \"getconf/normal not working (PATH=\${PATH}).\" ; \ + (( mismatch > 0 )) && err_exit \"getconf/normal test found \${mismatch} differences (PATH=\${PATH}).\" ; \ exit $((Errors))" - let Errors+=$? + (( Errors+=$? )) done -# test done +# tests done exit $((Errors)) - diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_local_compound_nameref001.sh b/usr/src/lib/libshell/common/tests/sun_solaris_local_compound_nameref001.sh new file mode 100644 index 0000000000..30a2deb314 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_local_compound_nameref001.sh @@ -0,0 +1,64 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# name reference test #001 +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + +function function2 +{ + nameref v=$1 + + v.x=19 + v.y=20 +} + +function function1 +{ + typeset compound_var=() + + function2 compound_var + + printf "x=%d, y=%d\n" compound_var.x compound_var.y +} + +x="$(function1)" + +[[ "$x" != 'x=19, y=20' ]] && err_exit "expected 'x=19, y=20', got '${x}'" + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_staticvariables.sh b/usr/src/lib/libshell/common/tests/sun_solaris_staticvariables.sh new file mode 100644 index 0000000000..9230c9925f --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_staticvariables.sh @@ -0,0 +1,111 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +function err_exit2 +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +function testfunc +{ + integer line_number=$1 + typeset cmd="$2" + typeset expected_output="$3" + typeset output + + output="$($SHELL -c "${cmd}" 2>&1 )" + + [[ "${output}" != "${expected_output}" ]] && err_exit2 ${line_number} "${output} != ${expected_output}" +} +alias testfunc='testfunc $LINENO' +alias err_exit='err_exit2 $LINENO' + +integer Errors=0 + +# string +testfunc '(function l { typeset -S x ; x+="#" ; $1 && print "$x" ; } ; l false ; l false ; l true)' "###" +testfunc 'function l { typeset -S x=">" ; x+="#" ; $1 && print "$x" ; } ; l false ; l false ; l true' ">###" +testfunc 'function l { typeset -S x=">" ; x+="#" ; $1 && print "$x" ; } ; l false ; (l false) ; l true' ">##" +testfunc 'function l { typeset -S x=">" ; x+="#" ; $1 && print "$x" ; } ; l false; ( ulimit -c 0 ; l false) ; l true' ">##" + +# integer +testfunc '(function l { typeset -S -i x ; x+=1 ; $1 && print "$x" ; } ; l false ; l false ; l true )' "3" +testfunc '(function l { typeset -S -i x ; x+=1 ; $1 && print "$x" ; } ; l false ; (l false) ; l true )' "2" + +# float +testfunc '(function l { float -S x=0.5 ; (( x+=.5 )) ; $1 && print "$x" ; } ; l false ; l false ; l true )' "2" +testfunc '(function l { float -S x=0.5 ; (( x+=.5 )) ; $1 && print "$x" ; } ; l false ; (l false) ; l true )' "1.5" + +# compound variable +[[ "${ + function l + { + typeset -S s=( a=0 b=0 ) + + (( s.a++, s.b++ )) + + $1 && printf 'a=%d, b=%d\n' s.a s.b + } + l false ; l false ; l true +}" != "a=3, b=3" ]] && err_exit "static compound var failed" + + +# array variable +[[ "$( + function ar + { + typeset -a -S s=( "hello" ) + + s+=( "an element" ) + + $1 && { printf '%s' "${s[@]}" ; printf '\n' ; } + } + ar false ; ar false ; ar true +)" != "helloan elementan elementan element" ]] && err_exit "static array var failed" + + +# Test visibilty of "global" vs. "static" variables. if we have a "static" variable in a +# function and "unset" it we should see a global variable with the same +# name, right ? +integer hx=5 +function test_hx_scope +{ + integer -S hx=9 + $2 && unset hx + $1 && printf "hx=%d\n" hx +} +test_hx_scope false false +test_hx_scope false false +# first test the "unset" call in a $(...) subshell... +[[ "$( test_hx_scope true true )" != "hx=5" ]] && err_exit "can't see global variable hx after unsetting static variable hx" +# ... end then test whether the value has changed. +[[ "${ test_hx_scope true false }" != "hx=9" ]] && err_exit "hx variable somehow changed" + + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_vartree001.sh b/usr/src/lib/libshell/common/tests/sun_solaris_vartree001.sh new file mode 100644 index 0000000000..74783e6ff7 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_vartree001.sh @@ -0,0 +1,191 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# variable tree test #001 +# Propose of this test is whether ksh93 crashes or not - ast-ksh.2008-05-14 +# crashes like this when running this test: +# +# program terminated by signal ILL (illegal opcode) +# 0xffffffffffffffff: <bad address 0xffffffffffffffff> +# Current function is nv_diropen +# 123 dp->hp = (Namval_t*)dtprev(dp->root,&fake); +# (dbx) where +# [1] 0x100381e80(0x100381e80, 0xffffffff7fffe690, 0x10, 0x61, 0x0, 0x100381ec9), at 0x100381e80 +# =>[2] nv_diropen(np = (nil), name = 0x100381ebc "mysrcdata"), line 123 in "nvtree.c" +# [3] walk_tree(np = 0x1003809e0, dlete = 524289), line 743 in "nvtree.c" +# [4] put_tree(np = 0x1003809e0, val = (nil), flags = 524289, fp = 0x100381db0), line 814 in "nvtree.c" +# [5] nv_putv(np = 0x1003809e0, value = (nil), flags = 524289, nfp = 0x100381db0), line 141 in "nvdisc.c" +# [6] _nv_unset(np = 0x1003809e0, flags = 524289), line 1976 in "name.c" +# [7] table_unset(shp = 0x10033e900, root = 0x100380900, flags = 524289, oroot = 0x100360980), line 1902 in "name.c" +# [8] sh_unscope(shp = 0x10033e900), line 2711 in "name.c" +# [9] sh_funscope(argn = 1, argv = 0x10035e680, fun = (nil), arg = 0xffffffff7ffff118, execflg = 4), line 2470 in "xec.c" +# [10] sh_funct(np = 0x100380860, argn = 1, argv = 0x10035e680, envlist = (nil), execflg = 4), line 2528 in "xec.c" +# [11] sh_exec(t = 0x10035e620, flags = 4), line 1032 in "xec.c" +# [12] exfile(shp = 0x10033e900, iop = 0x100379a20, fno = 10), line 589 in "main.c" +# [13] sh_main(ac = 2, av = 0xffffffff7ffffa08, userinit = (nil)), line 364 in "main.c" +# [14] main(argc = 2, argv = 0xffffffff7ffffa08), line 46 in "pmain.c" +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + + +function build_tree +{ +#set -o errexit -o xtrace + typeset index + typeset s + typeset i + typeset dummy + typeset a b c d e f + + nameref dest_tree="$1" # destination tree + nameref srcdata="$2" # source data + typeset tree_mode="$3" # mode to define the type of leads + + typeset -A dest_tree.l1 + + for index in "${!srcdata.hashnodes[@]}" ; do + nameref node=srcdata.hashnodes["${index}"] + + for i in "${node.xlfd[@]}" ; do + IFS='-' read dummy a b c d e f <<<"$i" + + if [[ "$a" == "" ]] ; then + a="$dummy" + fi + + [[ "$a" == "" ]] && a='-' + [[ "$b" == "" ]] && b='-' + [[ "$c" == "" ]] && c='-' + + if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then + #if ! (unset dest_tree.l1["$a"]) ; then + typeset -A dest_tree.l1["$a"].l2 + fi + + if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then + #if ! (unset dest_tree.l1["$a"].l2["$b"]) ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3 + fi + + if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries + fi + + #dest_tree.l1["$a"].l2["$b"].l3["$c"].entries+=( "$index" ) + typeset new_index + if [[ "${tree_mode}" == "leaf_name" ]] ; then + new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 )) + else + new_index="${node.name}" + + # skip if the leaf node already exists + if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then + continue + fi + fi + + add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}" + done + done + + return 0 +} + +function add_tree_leaf +{ + nameref tree_leafnode="$1" + nameref data_node=srcdata.hashnodes["$2"] + typeset add_mode="$3" + + case "${add_mode}" in + "leaf_name") + tree_leafnode="${data_node.name}" + return 0 + ;; + "leaf_compound") + tree_leafnode=( + typeset name="${data_node.name}" + typeset -a filenames=( "${data_node.filenames[@]}" ) + typeset -a comments=( "${data_node.comments[@]}" ) + typeset -a xlfd=( "${data_node.xlfd[@]}" ) + ) + return 0 + ;; + *) + print -u2 -f "ERROR: Unknown mode %s in add_tree_leaf\n" "${add_mode}" + return 1 + ;; + esac + + # not reached + return 1 +} + +function main +{ + typeset mysrcdata=( + typeset -A hashnodes=( + [abcd]=( + name='abcd' + typeset -a xlfd=( + '-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific' + ) + typeset -a comments=( + 'comment 1' + 'comment 2' + 'comment 3' + ) + typeset -a filenames=( + '/home/foo/abcd_1' + '/home/foo/abcd_2' + '/home/foo/abcd_3' + ) + ) + ) + ) + + mytree=() + build_tree mytree mysrcdata leaf_compound +# (( $(print -r -- "$mytree" | wc -l) > 10 )) || err_exit "Compound tree too small." +} + +main + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_vartree002.sh b/usr/src/lib/libshell/common/tests/sun_solaris_vartree002.sh new file mode 100644 index 0000000000..e233f596e3 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_vartree002.sh @@ -0,0 +1,353 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# variable tree test #002 +# Propose of this test is whether ksh93 handles global variable trees +# and function-local variable trees the same way, including "nameref" +# and "unset" handling. +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + +# "built_tree1" and "built_tree2" are identical except the way how they test +# whether a variable exists: +# - "built_tree1" uses "${varname}" != "", e.g. looking whether the variable +# as non-zero length content +# - "built_tree2" uses "! (unset varname)", e.g. "unset" in a subshell +function build_tree1 +{ +#set -o errexit -o xtrace + typeset index + typeset s + typeset i + typeset dummy + typeset a b c d e f + + nameref dest_tree="$1" # destination tree + nameref srcdata="$2" # source data + typeset tree_mode="$3" # mode to define the type of leads + + typeset -A dest_tree.l1 + + for index in "${!srcdata.hashnodes[@]}" ; do + nameref node=srcdata.hashnodes["${index}"] + + for i in "${node.xlfd[@]}" ; do + IFS='-' read dummy a b c d e f <<<"$i" + + if [[ "$a" == "" ]] ; then + a="$dummy" + fi + + [[ "$a" == "" ]] && a='-' + [[ "$b" == "" ]] && b='-' + [[ "$c" == "" ]] && c='-' + + if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then + #if ! (unset dest_tree.l1["$a"]) ; then + typeset -A dest_tree.l1["$a"].l2 + fi + + if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then + #if ! (unset dest_tree.l1["$a"].l2["$b"]) ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3 + fi + + if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries + fi + + typeset new_index + if [[ "${tree_mode}" == "leaf_name" ]] ; then + new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 )) + else + new_index="${node.name}" + + # skip if the leaf node already exists + if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then + continue + fi + fi + + add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}" + done + done + + return 0 +} + +# "built_tree1" and "built_tree2" are identical except the way how they test +# whether a variable exists: +# - "built_tree1" uses "${varname}" != "", e.g. looking whether the variable +# as non-zero length content +# - "built_tree2" uses "! (unset varname)", e.g. "unset" in a subshell +function build_tree2 +{ +#set -o errexit -o xtrace + typeset index + typeset s + typeset i + typeset dummy + typeset a b c d e f + + nameref dest_tree="$1" # destination tree + nameref srcdata="$2" # source data + typeset tree_mode="$3" # mode to define the type of leads + + typeset -A dest_tree.l1 + + for index in "${!srcdata.hashnodes[@]}" ; do + nameref node=srcdata.hashnodes["${index}"] + + for i in "${node.xlfd[@]}" ; do + IFS='-' read dummy a b c d e f <<<"$i" + + if [[ "$a" == "" ]] ; then + a="$dummy" + fi + + [[ "$a" == "" ]] && a='-' + [[ "$b" == "" ]] && b='-' + [[ "$c" == "" ]] && c='-' + + #if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then + if ! (unset dest_tree.l1["$a"]) ; then + typeset -A dest_tree.l1["$a"].l2 + fi + + #if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then + if ! (unset dest_tree.l1["$a"].l2["$b"]) ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3 + fi + + if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries + fi + + typeset new_index + if [[ "${tree_mode}" == "leaf_name" ]] ; then + new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 )) + else + new_index="${node.name}" + + # skip if the leaf node already exists + if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then + continue + fi + fi + + add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}" + done + done + + return 0 +} + + +function add_tree_leaf +{ + nameref tree_leafnode="$1" + nameref data_node=srcdata.hashnodes["$2"] + typeset add_mode="$3" + + case "${add_mode}" in + "leaf_name") + tree_leafnode="${data_node.name}" + return 0 + ;; + "leaf_compound") + tree_leafnode=( + typeset name="${data_node.name}" + typeset -a filenames=( "${data_node.filenames[@]}" ) + typeset -a comments=( "${data_node.comments[@]}" ) + typeset -a xlfd=( "${data_node.xlfd[@]}" ) + ) + return 0 + ;; + *) + print -u2 -f "ERROR: Unknown mode %s in add_tree_leaf\n" "${add_mode}" + return 1 + ;; + esac + + # not reached + return 1 +} + +# "mysrcdata_local" and "mysrcdata_global" must be identical +typeset mysrcdata_global=( + typeset -A hashnodes=( + [abcd]=( + name='abcd' + typeset -a xlfd=( + '-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific' + ) + typeset -a comments=( + 'comment 1' + 'comment 2' + 'comment 3' + ) + typeset -a filenames=( + '/home/foo/abcd_1' + '/home/foo/abcd_2' + '/home/foo/abcd_3' + ) + ) + ) +) + +mytree_global1=() +mytree_global2=() + +function main +{ + # "mysrcdata_local" and "mysrcdata_global" must be identical + typeset mysrcdata_local=( + typeset -A hashnodes=( + [abcd]=( + name='abcd' + typeset -a xlfd=( + '-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific' + ) + typeset -a comments=( + 'comment 1' + 'comment 2' + 'comment 3' + ) + typeset -a filenames=( + '/home/foo/abcd_1' + '/home/foo/abcd_2' + '/home/foo/abcd_3' + ) + ) + ) + ) + + #### Build tree using global tree variables + build_tree1 mytree_global1 mysrcdata_global leaf_compound || \ + err_exit 'build_tree1 mytree_global1 mysrcdata_global leaf_compound returned an error' + (( $(print -r -- "${mytree_global1}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_global1' too small." + + build_tree2 mytree_global2 mysrcdata_global leaf_compound || \ + err_exit 'build_tree2 mytree_global2 mysrcdata_global leaf_compound returned an error' + (( $(print -r -- "${mytree_global2}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_global2' too small." + + + #### build tree using local tree variables + mytree_local1=() + mytree_local2=() + + build_tree1 mytree_local1 mysrcdata_local leaf_compound || \ + err_exit 'build_tree1 mytree_local1 mysrcdata_local leaf_compound returned an error' + (( $(print -r -- "${mytree_local1}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_local1' too small." + + build_tree2 mytree_local2 mysrcdata_local leaf_compound || \ + err_exit 'build_tree2 mytree_local2 mysrcdata_local leaf_compound returned an error' + (( $(print -r -- "${mytree_local2}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_local2' too small." + + + #### Compare treess + if [[ "${mytree_global1}" != "${mytree_local1}" ]] ; then + err_exit "Compound trees 'mytree_global1' and 'mytree_local1' not identical" + diff -u <( printf "%s\n" "${mytree_global1}" ) <( printf "%s\n" "${mytree_local1}" ) + fi + + if [[ "${mytree_global1}" != "${mytree_global2}" ]] ; then + err_exit "Compound trees 'mytree_global1' and 'mytree_global2' not identical" + diff -u <( printf "%s\n" "${mytree_global1}" ) <( printf "%s\n" "${mytree_global2}" ) + fi + + if [[ "${mytree_local1}" != "${mytree_local2}" ]] ; then + err_exit "Compound trees 'mytree_local1' and 'mytree_local2' not identical" + diff -u <( printf "%s\n" "${mytree_local1}" ) <( printf "%s\n" "${mytree_local2}" ) + fi + + + #### test "unset" in a subshell + ( unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]' ) || \ + err_exit "Try 1: Variable 'mytree_global1.l1[urw].l2[itc zapfdingbats]' not found." + ( unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]' ) || \ + err_exit "Try 2: Variable 'mytree_global1.l1[urw].l2[itc zapfdingbats]' not found." + + # remove parent node (array element) and then check whether the child is gone, too: + ( + set -o errexit + unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]' + ! unset 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' + ) || err_exit "Global: Parent node removed (array element), child still exists" + ( + set -o errexit + unset 'mytree_local1.l1[urw].l2[itc zapfdingbats]' + ! unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' + ) || err_exit "Local: Parent node removed (array element), child still exists" + + # remove parent node (array variable) and then check whether the child is gone, too: + ( + set -o errexit + unset 'mytree_local1.l1[urw].l2' + ! unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' + ) || err_exit "Global: Parent node removed (array variable), child still exists" + ( + set -o errexit + unset 'mytree_local1.l1[urw].l2' + ! unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' + ) || err_exit "Local: Parent node removed (array variable), child still exists" + + + #### test "unset" and compare trees + unset 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' || + err_exit "Variable 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found." + + [[ "${mytree_global1}" != "${mytree_local1}" ]] || err_exit "mytree_global1 and mytree_local1 should differ" + + unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' || + err_exit "Variable 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found." + + # Compare trees (after "unset") + if [[ "${mytree_global1}" != "${mytree_local1}" ]] ; then + err_exit "Compound trees 'mytree_local1' and 'mytree_global1' not identical after unset" + diff -u <( printf "%s\n" "${mytree_global1}" ) <( printf "%s\n" "${mytree_local1}" ) + fi +} + +main + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/sun_solaris_vartree003.sh b/usr/src/lib/libshell/common/tests/sun_solaris_vartree003.sh new file mode 100644 index 0000000000..89bc9e7541 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/sun_solaris_vartree003.sh @@ -0,0 +1,198 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# variable tree test #003 +# Propose of this test is whether ksh93 handles global variable trees +# and function-local variable trees the same way, including "nameref" +# and "unset" handling. +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +integer Errors=0 + +function example_tree +{ +cat <<EOF +( + typeset -A l1=( + [adobe]=( + typeset -A l2=( + [avantgarde]=( + typeset -A l3=( + [demi]=( + typeset -A entries=( + [182c069a485316b1bc7ae001c04c7835]=( + typeset -a comments=( + FONT + -adobe-avantgarde-demi-r-normal--199-120-1200-1200-p-1130-iso8859-1 + COPYRIGHT + 'Copyright Notice not available' + RAW_PIXELSIZE + RAW_POINTSIZE + -- + section + diaeresis + copyright + ordfeminine + guillemotleft + ) + typeset -a filenames=( + X11Rx/R6.4/xc/programs/Xserver/XpConfig/C/print/models/SPSPARC2/fonts/AvantGarde-Demi.pmf + ) + md5sum=182c069a485316b1bc7ae001c04c7835 + typeset -a xlfd=( + -adobe-avantgarde-demi-r-normal--199-120-1200-1200-p-1130-iso8859-1 + ) + ) + [7db15b51965d8fe1f1c55fcb101d7616]=( + typeset -a comments=( + FONT + -adobe-avantgarde-demi-i-normal--199-120-1200-1200-p-1130-iso8859-1 + COPYRIGHT + 'Copyright Notice not available' + RAW_PIXELSIZE + RAW_POINTSIZE + -- + section + diaeresis + copyright + ordfeminine + guillemotleft + ) + typeset -a filenames=( + X11Rx/R6.4/xc/programs/Xserver/XpConfig/C/print/models/SPSPARC2/fonts/AvantGarde-DemiOblique.pmf + ) + md5sum=7db15b51965d8fe1f1c55fcb101d7616 + typeset -a xlfd=( + -adobe-avantgarde-demi-i-normal--199-120-1200-1200-p-1130-iso8859-1 + ) + ) + [a37e4a4a5035abf6f294d830fbd9e775]=( + typeset -a comments=( + FONT + -adobe-avantgarde-demi-r-normal--422-120-2540-2540-p-2395-iso8859-1 + COPYRIGHT + 'Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved.ITC Avant Garde Gothic is a registered trademark of International Typeface Corporation.' + RAW_PIXELSIZE + RAW_POINTSIZE + -- + section + diaeresis + copyright + ordfeminine + guillemotleft + ) + typeset -a filenames=( + fox-gate/XW_NV/open-src/tarballs/xorg-server-1.3.0.0/hw/xprint/config/C/print/models/PSdefault/fonts/AvantGarde-Demi.pmf + ) + md5sum=a37e4a4a5035abf6f294d830fbd9e775 + typeset -a xlfd=( + -adobe-avantgarde-demi-r-normal--422-120-2540-2540-p-2395-iso8859-1 + ) + ) + [da3d6d94fcf759b95c7f829ce5619374]=( + typeset -a comments=( + FONT + -adobe-avantgarde-demi-i-normal--422-120-2540-2540-p-2395-iso8859-1 + COPYRIGHT + 'Copyright (c) 1985, 1987, 1989, 1990, 1991 Adobe Systems Incorporated. All Rights Reserved.ITC Avant Garde Gothic is a registered trademark of International Typeface Corporation.' + RAW_PIXELSIZE + RAW_POINTSIZE + -- + section + diaeresis + copyright + ordfeminine + guillemotleft + ) + typeset -a filenames=( + fox-gate/XW_NV/open-src/tarballs/xorg-server-1.3.0.0/hw/xprint/config/C/print/models/PSdefault/fonts/AvantGarde-DemiOblique.pmf + ) + md5sum=da3d6d94fcf759b95c7f829ce5619374 + typeset -a xlfd=( + -adobe-avantgarde-demi-i-normal--422-120-2540-2540-p-2395-iso8859-1 + ) + ) + ) + ) + ) + ) + ) + ) + ) +) +EOF +} + +function main +{ + set -o errexit + + typeset xlfd_tree=() + typeset -A xlfd_tree.l1 + + eval "xlfd_tree=$( example_tree )" + + typeset i j k l fn + + # filter chain begin + for i in "${!xlfd_tree.l1[@]}" ; do + for j in "${!xlfd_tree.l1["$i"].l2[@]}" ; do + for k in "${!xlfd_tree.l1["$i"].l2["$j"].l3[@]}" ; do + nameref vndnode=xlfd_tree.l1["$i"].l2["$j"].l3["$k"] + + for l in "${!vndnode.entries[@]}" ; do + nameref node=vndnode.entries["$l"] + + for fn in "${node.filenames[@]}" ; do + if [[ "${fn}" != ~(E)x-re_gate_XW_NV_MWS ]] ; then + unset "${!node}" + break + fi + done + done + done + done + done + + # filter chain end + + return 0 +} + +main || ((Errors++)) + +# tests done +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/tilde.sh b/usr/src/lib/libshell/common/tests/tilde.sh index 6ebfd8504c..60a2de0aa8 100644 --- a/usr/src/lib/libshell/common/tests/tilde.sh +++ b/usr/src/lib/libshell/common/tests/tilde.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -83,5 +83,10 @@ then err_exit x=~:~ not $HOME:$HOME fi HOME=/ [[ ~ == / ]] || err_exit '~ should be /' +trap 'rm -rf /tmp/kshtilde$$' EXIT [[ ~/foo == /foo ]] || err_exit '~/foo should be /foo when ~==/' +print $'print ~+\n[[ $1 ]] && $0' > /tmp/kshtilde$$ +chmod +x /tmp/kshtilde$$ +nl=$'\n' +[[ $(/tmp/kshtilde$$ foo) == "$PWD$nl$PWD" ]] 2> /dev/null || err_exit 'tilde fails inside a script run by name' exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/timetype.sh b/usr/src/lib/libshell/common/tests/timetype.sh new file mode 100644 index 0000000000..c2046d5272 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/timetype.sh @@ -0,0 +1,80 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 + +typeset -T Time_t=( + integer .=-1 + _='%F+%H:%M' + get() + { + if (( _ < 0 )) + then .sh.value=${ printf "%(${_._})T" now ;} + else .sh.value=${ printf "%(${_._})T" "#$((_))" ;} + fi + } + set() + { + .sh.value=${ printf "%(%#)T" "${.sh.value}";} + } +) + +d=$(printf "%(%F+%H:%M)T" now) +integer s=$(printf "%(%#)T" "$d") +Time_t t=$d +[[ $t == "$d" ]] || err_exit 'printf %T and Time_t are different' +(( t == s )) || err_exit 'numerical Time_t not correct' +t._='%#' +[[ $t == $s ]] || err_exit 'setting _ to %# not getting correct results' +unset t +Time_t tt=(yesterday today tomorrow) +tt[3]=2pm +[[ ${!tt[@]} == '0 1 2 3' ]] || err_exit 'indexed array subscript names not correct' +[[ ${tt[0]} == *+00:00 ]] || err_exit 'tt[0] is not yesterday' +[[ ${tt[1]} == *+00:00 ]] || err_exit 'tt[1] is not today' +[[ ${tt[2]} == *+00:00 ]] || err_exit 'tt[2] is not tomorrow' +[[ ${tt[3]} == *+14:00 ]] || err_exit 'tt[0] is not 2pm' +unset tt +Time_t tt=('2008-08-11+00:00:00,yesterday' '2008-08-11+00:00:00,today' '2008-08-11+00:00:00,tomorrow') +tt[3]=9am +tt[4]=5pm +(( (tt[1] - tt[0] ) == 24*3600 )) || err_exit 'today-yesterday not one day' +(( (tt[2] - tt[1] ) == 24*3600 )) || err_exit 'tomorrow-today not one day' +(( (tt[4] - tt[3] ) == 8*3600 )) || err_exit '9am .. 5pm is not 8 hours' +unset tt +Time_t tt=([yesterday]='2008-08-11+00:00:00,yesterday' [today]='2008-08-11+00:00:00,today' [tomorrow]='2008-08-11+00:00:00,tomorrow') +tt[2pm]='2008-08-11+00:00:00,2pm' +[[ ${tt[yesterday]} == *+00:00 ]] || err_exit 'tt[yesterday] is not yesterday' +[[ ${tt[today]} == *+00:00 ]] || err_exit 'tt[today] is not today' +[[ ${tt[tomorrow]} == *+00:00 ]] || err_exit 'tt[tomorrow] is not tomorrow' +[[ ${tt[2pm]} == *+14:00 ]] || err_exit 'tt[2pm] is not 2pm' +(( (tt[today] - tt[yesterday] ) == 24*3600 )) || err_exit 'today-yesterday not one day' +(( (tt[tomorrow] - tt[today] ) == 24*3600 )) || err_exit 'tomorrow-today not one day' +(( (tt[2pm] - tt[today] ) == 14*3600 )) || err_exit '2pm is not 14 hours' +unset tt +exit $Errors diff --git a/usr/src/lib/libshell/common/tests/types.sh b/usr/src/lib/libshell/common/tests/types.sh new file mode 100644 index 0000000000..24be9e9863 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/types.sh @@ -0,0 +1,268 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} +alias err_exit='err_exit $LINENO' + +Command=${0##*/} +integer Errors=0 +integer n=2 + +typeset -T Type_t=( + typeset name=foobar + typeset x=(hi=ok bar=yes) + typeset y=(xa=xx xq=89) + typeset -A aa=([one]=abc [two]=def) + typeset -a ia=(abc def) + typeset -i z=5 +) +for ((i=0; i < 10; i++)) +do + Type_t r s + [[ $r == "$s" ]] || err_exit 'r is not equal to s' + typeset -C x=r.x + y=(xa=bb xq=cc) + y2=xyz + z2=xyz + typeset -C z=y + [[ $y == "$z" ]] || err_exit 'y is not equal to z' + typeset -C s.y=z + [[ $y == "${s.y}" ]] || err_exit 'y is not equal to s.y' + .sh.q=$y + typeset -C www=.sh.q + [[ $www == "$z" ]] || err_exit 'www is not equal to z' + typeset -C s.x=r.x + [[ ${s.x} == "${r.x}" ]] || err_exit 's.x is not equal to r.x' + + function foo + { + nameref x=$1 y=$2 + typeset z=$x + y=$x + [[ $x == "$y" ]] || err_exit "x is not equal to y with ${!x}" + } + foo r.y y + [[ $y == "${r.y}" ]] || err_exit 'y is not equal to r.y' + typeset -C y=z + foo y r.y + [[ $y == "${r.y}" ]] || err_exit 'y is not equal to r.y again' + typeset -C y=z + ( + q=${z} + [[ $q == "$z" ]] || err_exit 'q is not equal to z' + z=abc + ) + [[ $z == "$y" ]] || err_exit 'value of z not preserved after subshell' + unset z y r s x z2 y2 www .sh.q +done +typeset -T Frame_t=( typeset file lineno ) +Frame_t frame +[[ $(typeset -p frame) == 'Frame_t frame=(typeset file;typeset lineno;)' ]] || err_exit 'empty fields in type not displayed' +x=( typeset -a arr=([2]=abc [4]=(x=1 y=def));zz=abc) +typeset -C y=x +[[ "$x" == "$y" ]] || print -u2 'y is not equal to x' +Type_t z=(y=(xa=bb xq=cc)) +typeset -A arr=([foo]=one [bar]=2) +typeset -A brr=([foo]=one [bar]=2) +[[ "${arr[@]}" == "${brr[@]}" ]] || err_exit 'arr is not brr' +for ((i=0; i < 1; i++)) +do typeset -m zzz=x + [[ $zzz == "$y" ]] || err_exit 'zzz is not equal to y' + typeset -m x=zzz + [[ $x == "$y" ]] || err_exit 'x is not equal to y' + Type_t t=(y=(xa=bb xq=cc)) + typeset -m r=t + [[ $r == "$z" ]] || err_exit 'r is not equal to z' + typeset -m t=r + [[ $t == "$z" ]] || err_exit 't is not equal to z' + typeset -m crr=arr + [[ "${crr[@]}" == "${brr[@]}" ]] || err_exit 'crr is not brr' + typeset -m arr=crr + [[ "${arr[@]}" == "${brr[@]}" ]] || err_exit 'brr is not arr' +done +typeset -m brr[foo]=brr[bar] +[[ ${brr[foo]} == 2 ]] || err_exit 'move an associative array element fails' +[[ ${brr[bar]} ]] && err_exit 'brr[bar] should be unset after move' +unset x y zzz +x=(a b c) +typeset -m x[1]=x[2] +[[ ${x[1]} == c ]] || err_exit 'move an indexed array element fails' +[[ ${x[2]} ]] && err_exit 'x[2] should be unset after move' +trap 'rm -f /tmp/kshtype$$' EXIT +cat > /tmp/kshtype$$ <<- \+++ + typeset -T Pt_t=(float x=1. y=0.) + Pt_t p=(y=2) + print -r -- ${p.y} ++++ +expected=2 +got=$(. /tmp/kshtype$$) 2>/dev/null +[[ "$got" == "$expected" ]] || err_exit "typedefs in dot script failed -- expected '$expected', got '$got'" +typeset -T X_t=( + typeset x=foo y=bar + typeset s=${_.x} + create() + { + _.y=bam + } +) +X_t x +[[ ${x.x} == foo ]] || err_exit 'x.x should be foo' +[[ ${x.y} == bam ]] || err_exit 'x.y should be bam' +[[ ${x.s} == ${x.x} ]] || err_exit 'x.s should be x.x' +typeset -T Y_t=( X_t r ) +Y_t z +[[ ${z.r.x} == foo ]] || err_exit 'z.r.x should be foo' +[[ ${z.r.y} == bam ]] || err_exit 'z.r.y should be bam' +[[ ${z.r.s} == ${z.r.x} ]] || err_exit 'z.r.s should be z.r.x' + +unset xx yy +typeset -T xx=(typeset yy=zz) +xx=yy +{ typeset -T xx=(typeset yy=zz) ;} 2>/dev/null && err_exit 'type redefinition should fail' +$SHELL 2> /dev/null <<- +++ || err_exit 'typedef with only f(){} fails' + typeset -T X_t=( + f() + { + print ok + } + ) ++++ +$SHELL 2> /dev/null <<- +++ || err_exit 'unable to redefine f discipline function' + typeset -T X_t=( + x=1 + f() + { + print ok + } + ) + X_t z=( + function f + { + print override f + } + ) ++++ +$SHELL 2> /dev/null <<- +++ && err_exit 'invalid discipline name should be an error' + typeset -T X_t=( + x=1 + f() + { + print ok + } + ) + X_t z=( + function g + { + print override f + } + ) ++++ +# compound variables containing type variables +Type_t r +var=( + typeset x=foobar + Type_t r + integer z=5 +) +[[ ${var.r} == "$r" ]] || err_exit 'var.r != r' +(( var.z == 5)) || err_exit 'var.z !=5' +[[ "$var" == *x=foobar* ]] || err_exit '$var does not contain x=foobar' + +typeset -T A_t=( + typeset x=aha + typeset b=${_.x} +) +unset x +A_t x +expected=aha +got=${x.b} +[[ "$got" == "$expected" ]] || err_exit "type '_' reference failed -- expected '$expected', got '$got'" + +typeset -T Tst_t=( + function f + { + A_t a + print ${ _.g ${a.x}; } + } + function g + { + print foo + } +) +Tst_t tst +expected=foo +got=${ tst.f;} +[[ "$got" == "$expected" ]] || err_exit "_.g where g is a function in type discipline method failed -- expected '$expected', got '$got'" + +typeset -T B_t=( + integer -a arr + function f + { + (( _.arr[0] = 0 )) + (( _.arr[1] = 1 )) + print ${_.arr[*]} + } +) +unset x +B_t x +expected='0 1' +got=${ x.f;} +[[ "$got" == "$expected" ]] || err_exit "array assignment of subscripts in type discipline arithmetic failed -- expected '$expected', got '$got'" + +typeset -T Fileinfo_t=( + size=-1 + typeset -a text=() + integer mtime=-1 +) +Fileinfo_t -A _Dbg_filenames +Fileinfo_t finfo +function bar +{ + finfo.text=(line1 line2 line3) + finfo.size=${#finfo.text[@]} + _Dbg_filenames[foo]=finfo +} +bar + +expected='Fileinfo_t -A _Dbg_filenames=([foo]=(size=2;typeset -C -a text=([0]=line1 [1]=line2 [2]=line3);typeset -l -i mtime=-1;))' +got=$(typeset -p _Dbg_filenames) +[[ "$got" == "$expected" ]] || { + got=$(printf %q "$got") + err_exit "copy to associative array of types in function failed -- expected '$expected', got '$got'" +} + +$SHELL > /dev/null <<- '+++++' || err_exit 'passing _ as nameref arg not working' + function f1 + { + typeset -n v=$1 + print -r -- "$v" + } + typeset -T A_t=( + typeset blah=xxx + function f { f1 _ ;} + ) + A_t a + [[ ${ a.f ./t1;} == "$a" ]] ++++++ +exit $Errors diff --git a/usr/src/lib/libshell/common/tests/variables.sh b/usr/src/lib/libshell/common/tests/variables.sh index bc499c890c..1808d2fbcf 100644 --- a/usr/src/lib/libshell/common/tests/variables.sh +++ b/usr/src/lib/libshell/common/tests/variables.sh @@ -1,10 +1,10 @@ ######################################################################## # # # This software is part of the ast package # -# Copyright (c) 1982-2007 AT&T Knowledge Ventures # +# Copyright (c) 1982-2008 AT&T Intellectual Property # # and is licensed under the # # Common Public License, Version 1.0 # -# by AT&T Knowledge Ventures # +# by AT&T Intellectual Property # # # # A copy of the License is available at # # http://www.opensource.org/licenses/cpl1.0.txt # @@ -27,6 +27,12 @@ alias err_exit='err_exit $LINENO' Command=${0##*/} integer Errors=0 + +[[ ${.sh.version} == "$KSH_VERSION" ]] || err_exit '.sh.version != KSH_VERSION' +unset ss +[[ ${@ss} ]] && err_exit '${@ss} should be empty string when ss is unset' +[[ ${!ss} == ss ]] || err_exit '${!ss} should be ss when ss is unset' +[[ ${#ss} == 0 ]] || err_exit '${#ss} should be 0 when ss is unset' # RANDOM if (( RANDOM==RANDOM || $RANDOM==$RANDOM )) then err_exit RANDOM variable not working @@ -97,8 +103,8 @@ fi # check for attributes across subshells typeset -i x=3 y=1/0 -if ( typeset x=y) 2> /dev/null -then print -u2 "attributes not passed to subshells" +if ( typeset x=y ) 2> /dev/null +then err_exit "attributes not passed to subshells" fi unset x function x.set @@ -249,7 +255,7 @@ set -- "${@-}" if (( $# !=1 )) then err_exit '"${@-}" not expanding to null string' fi -for i in : % + / 3b '**' '***' '@@' '{' '[' '}' !! '*a' '@a' '$foo' +for i in : % + / 3b '**' '***' '@@' '{' '[' '}' !! '*a' '$foo' do (eval : \${"$i"} 2> /dev/null) && err_exit "\${$i} not an syntax error" done unset IFS @@ -467,7 +473,7 @@ TIMEFORMAT='this is a test' : ${.sh.version} [[ $(whence rm) == *.sh.* ]] && err_exit '.sh. prefixed to tracked alias name' : ${.sh.version} -[[ $(cd /bin;env | grep PWD) == *.sh.* ]] && err_exit '.sh. prefixed to PWD' +[[ $(cd /bin;env | grep PWD=) == *.sh.* ]] && err_exit '.sh. prefixed to PWD' # unset discipline bug fix dave=dave function dave.unset @@ -476,9 +482,26 @@ function dave.unset } unset dave [[ $(typeset +f) == *dave.* ]] && err_exit 'unset discipline not removed' -print 'print ${VAR}' > /tmp/script$$ -VAR=foo /tmp/script$$ > /tmp/out$$ -[[ $(</tmp/out$$) == foo ]] || err_exit 'environment variables not passed to scripts' + +print 'print ${VAR}' > /tmp/script$$ +unset VAR +VAR=new /tmp/script$$ > /tmp/out$$ +got=$(</tmp/out$$) +[[ $got == new ]] || err_exit "previously unset environment variable not passed to script, expected 'new', got '$got'" +[[ ! $VAR ]] || err_exit "previously unset environment variable set after script, expected '', got '$VAR'" +unset VAR +VAR=old +VAR=new /tmp/script$$ > /tmp/out$$ +got=$(</tmp/out$$) +[[ $got == new ]] || err_exit "environment variable covering local variable not passed to script, expected 'new', got '$got'" +[[ $VAR == old ]] || err_exit "previously set local variable changed after script, expected 'old', got '$VAR'" +unset VAR +export VAR=old +VAR=new /tmp/script$$ > /tmp/out$$ +got=$(</tmp/out$$) +[[ $got == new ]] || err_exit "environment variable covering environment variable not passed to script, expected 'new', got '$got'" +[[ $VAR == old ]] || err_exit "previously set environment variable changed after script, expected 'old', got '$VAR'" + ( unset dave function dave.append @@ -555,4 +578,48 @@ x[0]=0 x[1]=1 x[2]=2 x[3]=3 [[ ${x[@]} == '12 8 5 3' ]] || err_exit 'set discipline for indexed array not working correctly' ((SECONDS=3*4)) (( SECONDS < 12 || SECONDS > 12.1 )) && err_exit "SECONDS is $SECONDS and should be close to 12" +unset a +function a.set +{ + print -r -- "${.sh.name}=${.sh.value}" +} +[[ $(a=1) == a=1 ]] || err_exit 'set discipline not working in subshell assignment' +[[ $(a=1 :) == a=1 ]] || err_exit 'set discipline not working in subshell command' + +[[ ${.sh.subshell} == 0 ]] || err_exit '${.sh.subshell} should be 0' +( + [[ ${.sh.subshell} == 1 ]] || err_exit '${.sh.subshell} should be 1' + ( + [[ ${.sh.subshell} == 2 ]] || err_exit '${.sh.subshell} should be 2' + ) +) + +set -- {1..32768} +(( $# == 32768 )) || err_exit "\$# failed -- expected 32768, got $#" +set -- + +unset r v x +path=$PATH +x=foo +for v in EDITOR VISUAL OPTIND CDPATH FPATH PATH ENV LINENO RANDOM SECONDS _ +do nameref r=$v + unset $v + if ( $SHELL -c "unset $v; : \$$v" ) 2>/dev/null + then [[ $r ]] && err_exit "unset $v failed -- expected '', got '$r'" + r=$x + [[ $r == $x ]] || err_exit "$v=$x failed -- expected '$x', got '$r'" + else err_exit "unset $v; : \$$v failed" + fi +done +for v in LC_ALL LC_CTYPE LC_MESSAGES LC_COLLATE LC_NUMERIC +do nameref r=$v + unset $v + [[ $r ]] && err_exit "unset $v failed -- expected '', got '$r'" + d=$($SHELL -c "$v=$x" 2>&1) + [[ $d ]] || err_exit "$v=$x failed -- expected locale diagnostic" + ( r=$x; [[ ! $r ]] ) 2>/dev/null || err_exit "$v=$x failed -- expected ''" + ( r=C; r=$x; [[ $r == C ]] ) 2>/dev/null || err_exit "$v=C; $v=$x failed -- expected 'C'" +done +PATH=$path + exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/vartree1.sh b/usr/src/lib/libshell/common/tests/vartree1.sh new file mode 100644 index 0000000000..88116a24c3 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/vartree1.sh @@ -0,0 +1,214 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +# +# variable tree test #001 +# Propose of this test is whether ksh93 handles global variable trees +# and function-local variable trees the same way, including "nameref" +# and "unset" handling. +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +function build_tree +{ +#set -o errexit -o xtrace + typeset index + typeset s + typeset i + typeset dummy + typeset a b c d e f + + nameref dest_tree="$1" # destination tree + nameref srcdata="$2" # source data + typeset tree_mode="$3" # mode to define the type of leads + + typeset -A dest_tree.l1 + + for index in "${!srcdata.hashnodes[@]}" ; do + nameref node=srcdata.hashnodes["${index}"] + + for i in "${node.xlfd[@]}" ; do + IFS='-' read dummy a b c d e f <<<"$i" + + if [[ "$a" == "" ]] ; then + a="$dummy" + fi + + [[ "$a" == "" ]] && a='-' + [[ "$b" == "" ]] && b='-' + [[ "$c" == "" ]] && c='-' + + if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then + #if ! (unset dest_tree.l1["$a"]) ; then + typeset -A dest_tree.l1["$a"].l2 + fi + + if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then + #if ! (unset dest_tree.l1["$a"].l2["$b"]) ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3 + fi + + if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries + fi + + #dest_tree.l1["$a"].l2["$b"].l3["$c"].entries+=( "$index" ) + typeset new_index + if [[ "${tree_mode}" == "leaf_name" ]] ; then + new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 )) + else + new_index="${node.name}" + + # skip if the leaf node already exists + if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then + continue + fi + fi + + add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}" + done + done + + return 0 +} + +function add_tree_leaf +{ + nameref tree_leafnode="$1" + nameref data_node=srcdata.hashnodes["$2"] + typeset add_mode="$3" + + case "${add_mode}" in + "leaf_name") + tree_leafnode="${data_node.name}" + return 0 + ;; + "leaf_compound") + tree_leafnode=( + typeset name="${data_node.name}" + typeset -a filenames=( "${data_node.filenames[@]}" ) + typeset -a comments=( "${data_node.comments[@]}" ) + typeset -a xlfd=( "${data_node.xlfd[@]}" ) + ) + return 0 + ;; + *) + print -u2 -f "ERROR: Unknown mode %s in add_tree_leaf\n" "${add_mode}" + return 1 + ;; + esac + + # not reached + return 1 +} + +# "mysrcdata_local" and "mysrcdata_global" must be identical +typeset mysrcdata_global=( + typeset -A hashnodes=( + [abcd]=( + name='abcd' + typeset -a xlfd=( + '-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific' + ) + typeset -a comments=( + 'comment 1' + 'comment 2' + 'comment 3' + ) + typeset -a filenames=( + '/home/foo/abcd_1' + '/home/foo/abcd_2' + '/home/foo/abcd_3' + ) + ) + ) +) + +mytree_global=() + +function main +{ + # "mysrcdata_local" and "mysrcdata_global" must be identical + typeset mysrcdata_local=( + typeset -A hashnodes=( + [abcd]=( + name='abcd' + typeset -a xlfd=( + '-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific' + ) + typeset -a comments=( + 'comment 1' + 'comment 2' + 'comment 3' + ) + typeset -a filenames=( + '/home/foo/abcd_1' + '/home/foo/abcd_2' + '/home/foo/abcd_3' + ) + ) + ) + ) + + # build tree using global tree variables + build_tree mytree_global mysrcdata_global leaf_compound || \ + err_exit 'build_tree mytree_global mysrcdata_global leaf_compound returned an error' + + (( $(print -r -- "${mytree_global}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_global' too small." + + # build tree using local tree variables + mytree_local=() + build_tree mytree_local mysrcdata_local leaf_compound || \ + err_exit 'build_tree mytree_local mysrcdata_local leaf_compound returned an error' + + (( $(print -r -- "${mytree_local}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_local' too small." + + # Compare trees + if [[ "${mytree_global}" != "${mytree_local}" ]] ; then + err_exit "Compound trees 'mytree_local' and 'mytree_global' not identical" + fi + + unset 'mytree_global.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' || + err_exit "Variable 'mytree_global.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found." + + [[ "${mytree_global}" != "${mytree_local}" ]] || err_exit "mytree_global and mytree_local should differ" + + unset 'mytree_local.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' || + err_exit "Variable 'mytree_local.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found." + + # Compare trees (after "unset") + if [[ "${mytree_global}" != "${mytree_local}" ]] ; then + err_exit "Compound trees 'mytree_local' and 'mytree_global' not identical after unset" + fi +} + +main +exit $((Errors)) diff --git a/usr/src/lib/libshell/common/tests/vartree2.sh b/usr/src/lib/libshell/common/tests/vartree2.sh new file mode 100644 index 0000000000..38512d5a42 --- /dev/null +++ b/usr/src/lib/libshell/common/tests/vartree2.sh @@ -0,0 +1,334 @@ +######################################################################## +# # +# This software is part of the ast package # +# Copyright (c) 1982-2008 AT&T Intellectual Property # +# and is licensed under the # +# Common Public License, Version 1.0 # +# by AT&T Intellectual Property # +# # +# A copy of the License is available at # +# http://www.opensource.org/licenses/cpl1.0.txt # +# (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) # +# # +# Information and Software Systems Research # +# AT&T Research # +# Florham Park NJ # +# # +# David Korn <dgk@research.att.com> # +# # +######################################################################## +# +# variable tree test #002 +# Propose of this test is whether ksh93 handles global variable trees +# and function-local variable trees the same way, including "nameref" +# and "unset" handling. +# + +function err_exit +{ + print -u2 -n "\t" + print -u2 -r ${Command}[$1]: "${@:2}" + (( Errors+=1 )) +} + +alias err_exit='err_exit $LINENO' + +# "built_tree1" and "built_tree2" are identical except the way how they test +# whether a variable exists: +# - "built_tree1" uses "${varname}" != "", e.g. looking whether the variable +# as non-zero length content +# - "built_tree2" uses "! (unset varname)", e.g. "unset" in a subshell +function build_tree1 +{ +#set -o errexit -o xtrace + typeset index + typeset s + typeset i + typeset dummy + typeset a b c d e f + + nameref dest_tree="$1" # destination tree + nameref srcdata="$2" # source data + typeset tree_mode="$3" # mode to define the type of leads + + typeset -A dest_tree.l1 + + for index in "${!srcdata.hashnodes[@]}" ; do + nameref node=srcdata.hashnodes["${index}"] + + for i in "${node.xlfd[@]}" ; do + IFS='-' read dummy a b c d e f <<<"$i" + + if [[ "$a" == "" ]] ; then + a="$dummy" + fi + + [[ "$a" == "" ]] && a='-' + [[ "$b" == "" ]] && b='-' + [[ "$c" == "" ]] && c='-' + + if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then + #if ! (unset dest_tree.l1["$a"]) ; then + typeset -A dest_tree.l1["$a"].l2 + fi + + if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then + #if ! (unset dest_tree.l1["$a"].l2["$b"]) ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3 + fi + + if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries + fi + + typeset new_index + if [[ "${tree_mode}" == "leaf_name" ]] ; then + new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 )) + else + new_index="${node.name}" + + # skip if the leaf node already exists + if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then + continue + fi + fi + + add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}" + done + done + + return 0 +} + +# "built_tree1" and "built_tree2" are identical except the way how they test +# whether a variable exists: +# - "built_tree1" uses "${varname}" != "", e.g. looking whether the variable +# as non-zero length content +# - "built_tree2" uses "! (unset varname)", e.g. "unset" in a subshell +function build_tree2 +{ +#set -o errexit -o xtrace + typeset index + typeset s + typeset i + typeset dummy + typeset a b c d e f + + nameref dest_tree="$1" # destination tree + nameref srcdata="$2" # source data + typeset tree_mode="$3" # mode to define the type of leads + + typeset -A dest_tree.l1 + + for index in "${!srcdata.hashnodes[@]}" ; do + nameref node=srcdata.hashnodes["${index}"] + + for i in "${node.xlfd[@]}" ; do + IFS='-' read dummy a b c d e f <<<"$i" + + if [[ "$a" == "" ]] ; then + a="$dummy" + fi + + [[ "$a" == "" ]] && a='-' + [[ "$b" == "" ]] && b='-' + [[ "$c" == "" ]] && c='-' + + #if [[ "${dest_tree.l1["$a"]}" == "" ]] ; then + if ! (unset dest_tree.l1["$a"]) ; then + typeset -A dest_tree.l1["$a"].l2 + fi + + #if [[ "${dest_tree.l1["$a"].l2["$b"]}" == "" ]] ; then + if ! (unset dest_tree.l1["$a"].l2["$b"]) ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3 + fi + + if [[ "${!dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[*]}" == "" ]] ; then + typeset -A dest_tree.l1["$a"].l2["$b"].l3["$c"].entries + fi + + typeset new_index + if [[ "${tree_mode}" == "leaf_name" ]] ; then + new_index=$(( ${#dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[@]}+1 )) + else + new_index="${node.name}" + + # skip if the leaf node already exists + if [[ "${dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}]}" != "" ]] ; then + continue + fi + fi + + add_tree_leaf dest_tree.l1["$a"].l2["$b"].l3["$c"].entries[${new_index}] "${index}" "${tree_mode}" + done + done + + return 0 +} + + +function add_tree_leaf +{ + nameref tree_leafnode="$1" + nameref data_node=srcdata.hashnodes["$2"] + typeset add_mode="$3" + + case "${add_mode}" in + "leaf_name") + tree_leafnode="${data_node.name}" + return 0 + ;; + "leaf_compound") + tree_leafnode=( + typeset name="${data_node.name}" + typeset -a filenames=( "${data_node.filenames[@]}" ) + typeset -a comments=( "${data_node.comments[@]}" ) + typeset -a xlfd=( "${data_node.xlfd[@]}" ) + ) + return 0 + ;; + *) + print -u2 -f "ERROR: Unknown mode %s in add_tree_leaf\n" "${add_mode}" + return 1 + ;; + esac + + # not reached + return 1 +} + +# "mysrcdata_local" and "mysrcdata_global" must be identical +typeset mysrcdata_global=( + typeset -A hashnodes=( + [abcd]=( + name='abcd' + typeset -a xlfd=( + '-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific' + ) + typeset -a comments=( + 'comment 1' + 'comment 2' + 'comment 3' + ) + typeset -a filenames=( + '/home/foo/abcd_1' + '/home/foo/abcd_2' + '/home/foo/abcd_3' + ) + ) + ) +) + +mytree_global1=() +mytree_global2=() + +function main +{ + # "mysrcdata_local" and "mysrcdata_global" must be identical + typeset mysrcdata_local=( + typeset -A hashnodes=( + [abcd]=( + name='abcd' + typeset -a xlfd=( + '-urw-itc zapfchancery-medium-i-normal--0-0-0-0-p-0-iso8859-1' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-adobe-fontspecific' + '-urw-itc zapfdingbats-medium-r-normal--0-0-0-0-p-0-sun-fontspecific' + ) + typeset -a comments=( + 'comment 1' + 'comment 2' + 'comment 3' + ) + typeset -a filenames=( + '/home/foo/abcd_1' + '/home/foo/abcd_2' + '/home/foo/abcd_3' + ) + ) + ) + ) + + #### Build tree using global tree variables + build_tree1 mytree_global1 mysrcdata_global leaf_compound || \ + err_exit 'build_tree1 mytree_global1 mysrcdata_global leaf_compound returned an error' + (( $(print -r -- "${mytree_global1}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_global1' too small." + + build_tree2 mytree_global2 mysrcdata_global leaf_compound || \ + err_exit 'build_tree2 mytree_global2 mysrcdata_global leaf_compound returned an error' + (( $(print -r -- "${mytree_global2}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_global2' too small." + + + #### build tree using local tree variables + mytree_local1=() + mytree_local2=() + + build_tree1 mytree_local1 mysrcdata_local leaf_compound || \ + err_exit 'build_tree1 mytree_local1 mysrcdata_local leaf_compound returned an error' + (( $(print -r -- "${mytree_local1}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_local1' too small." + + build_tree2 mytree_local2 mysrcdata_local leaf_compound || \ + err_exit 'build_tree2 mytree_local2 mysrcdata_local leaf_compound returned an error' + (( $(print -r -- "${mytree_local2}" | wc -l) > 10 )) || err_exit "Compound tree 'mytree_local2' too small." + + + #### Compare treess + if [[ "${mytree_global1}" != "${mytree_local1}" ]] ; then + err_exit "Compound trees 'mytree_global1' and 'mytree_local1' not identical" + fi + + if [[ "${mytree_global1}" != "${mytree_global2}" ]] ; then + err_exit "Compound trees 'mytree_global1' and 'mytree_global2' not identical" + fi + + if [[ "${mytree_local1}" != "${mytree_local2}" ]] ; then + err_exit "Compound trees 'mytree_local1' and 'mytree_local2' not identical" + fi + + + #### test "unset" in a subshell + ( unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]' ) || \ + err_exit "Try 1: Variable 'mytree_global1.l1[urw].l2[itc zapfdingbats]' not found." + ( unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]' ) || \ + err_exit "Try 2: Variable 'mytree_global1.l1[urw].l2[itc zapfdingbats]' not found." + + # remove parent node (array element) and then check whether the child is gone, too: + ( + unset 'mytree_global1.l1[urw].l2[itc zapfdingbats]' + unset 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' + ) && err_exit "Global: Parent node removed (array element), child still exists" + ( + unset 'mytree_local1.l1[urw].l2[itc zapfdingbats]' + unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' + ) && err_exit "Local: Parent node removed (array element), child still exists" + + # remove parent node (array variable) and then check whether the child is gone, too: + ( + unset 'mytree_local1.l1[urw].l2' + unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' + ) && err_exit "Global: Parent node removed (array variable), child still exists" + ( + unset 'mytree_local1.l1[urw].l2' + unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' + ) && err_exit "Local: Parent node removed (array variable), child still exists" + + + #### test "unset" and compare trees + unset 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' || + err_exit "Variable 'mytree_global1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found." + + [[ "${mytree_global1}" != "${mytree_local1}" ]] || err_exit "mytree_global1 and mytree_local1 should differ" + + unset 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' || + err_exit "Variable 'mytree_local1.l1[urw].l2[itc zapfdingbats].l3[medium].entries[abcd].filenames[0]' not found." + + # Compare trees (after "unset") + if [[ "${mytree_global1}" != "${mytree_local1}" ]] ; then + err_exit "Compound trees 'mytree_local1' and 'mytree_global1' not identical after unset" + fi +} + +main +exit $((Errors)) |