diff options
author | Kiyoshi Aman <kiyoshi.aman+adelie@gmail.com> | 2019-03-15 13:08:45 -0500 |
---|---|---|
committer | Kiyoshi Aman <kiyoshi.aman+adelie@gmail.com> | 2019-03-15 13:08:45 -0500 |
commit | 09cff699a514e452eccc6420f1213967b767cb3e (patch) | |
tree | e9888cd55d03c840b20d080af4e472453c910835 /bin | |
parent | ffeabdfb18fd21102159641f5258e8b1c473dec2 (diff) | |
download | userland-09cff699a514e452eccc6420f1213967b767cb3e.tar.gz userland-09cff699a514e452eccc6420f1213967b767cb3e.tar.bz2 userland-09cff699a514e452eccc6420f1213967b767cb3e.tar.xz userland-09cff699a514e452eccc6420f1213967b767cb3e.zip |
rm bin/sh usr.bin/make: utilities provided by other packages
Diffstat (limited to 'bin')
83 files changed, 0 insertions, 32752 deletions
diff --git a/bin/sh/TOUR b/bin/sh/TOUR deleted file mode 100644 index 30cee04..0000000 --- a/bin/sh/TOUR +++ /dev/null @@ -1,357 +0,0 @@ -# $NetBSD: TOUR,v 1.11 2016/10/25 13:01:59 abhinav Exp $ -# @(#)TOUR 8.1 (Berkeley) 5/31/93 - -NOTE -- This is the original TOUR paper distributed with ash and -does not represent the current state of the shell. It is provided anyway -since it provides helpful information for how the shell is structured, -but be warned that things have changed -- the current shell is -still under development. - -================================================================ - - A Tour through Ash - - Copyright 1989 by Kenneth Almquist. - - -DIRECTORIES: The subdirectory bltin contains commands which can -be compiled stand-alone. The rest of the source is in the main -ash directory. - -SOURCE CODE GENERATORS: Files whose names begin with "mk" are -programs that generate source code. A complete list of these -programs is: - - program input files generates - ------- ------------ --------- - mkbuiltins builtins builtins.h builtins.c - mkinit *.c init.c - mknodes nodetypes nodes.h nodes.c - mksignames - signames.h signames.c - mksyntax - syntax.h syntax.c - mktokens - token.h - bltin/mkexpr unary_op binary_op operators.h operators.c - -There are undoubtedly too many of these. Mkinit searches all the -C source files for entries looking like: - - INIT { - x = 1; /* executed during initialization */ - } - - RESET { - x = 2; /* executed when the shell does a longjmp - back to the main command loop */ - } - - SHELLPROC { - x = 3; /* executed when the shell runs a shell procedure */ - } - -It pulls this code out into routines which are when particular -events occur. The intent is to improve modularity by isolating -the information about which modules need to be explicitly -initialized/reset within the modules themselves. - -Mkinit recognizes several constructs for placing declarations in -the init.c file. - INCLUDE "file.h" -includes a file. The storage class MKINIT makes a declaration -available in the init.c file, for example: - MKINIT int funcnest; /* depth of function calls */ -MKINIT alone on a line introduces a structure or union declara- -tion: - MKINIT - struct redirtab { - short renamed[10]; - }; -Preprocessor #define statements are copied to init.c without any -special action to request this. - -INDENTATION: The ash source is indented in multiples of six -spaces. The only study that I have heard of on the subject con- -cluded that the optimal amount to indent is in the range of four -to six spaces. I use six spaces since it is not too big a jump -from the widely used eight spaces. If you really hate six space -indentation, use the adjind (source included) program to change -it to something else. - -EXCEPTIONS: Code for dealing with exceptions appears in -exceptions.c. The C language doesn't include exception handling, -so I implement it using setjmp and longjmp. The global variable -exception contains the type of exception. EXERROR is raised by -calling error. EXINT is an interrupt. EXSHELLPROC is an excep- -tion which is raised when a shell procedure is invoked. The pur- -pose of EXSHELLPROC is to perform the cleanup actions associated -with other exceptions. After these cleanup actions, the shell -can interpret a shell procedure itself without exec'ing a new -copy of the shell. - -INTERRUPTS: In an interactive shell, an interrupt will cause an -EXINT exception to return to the main command loop. (Exception: -EXINT is not raised if the user traps interrupts using the trap -command.) The INTOFF and INTON macros (defined in exception.h) -provide uninterruptible critical sections. Between the execution -of INTOFF and the execution of INTON, interrupt signals will be -held for later delivery. INTOFF and INTON can be nested. - -MEMALLOC.C: Memalloc.c defines versions of malloc and realloc -which call error when there is no memory left. It also defines a -stack oriented memory allocation scheme. Allocating off a stack -is probably more efficient than allocation using malloc, but the -big advantage is that when an exception occurs all we have to do -to free up the memory in use at the time of the exception is to -restore the stack pointer. The stack is implemented using a -linked list of blocks. - -STPUTC: If the stack were contiguous, it would be easy to store -strings on the stack without knowing in advance how long the -string was going to be: - p = stackptr; - *p++ = c; /* repeated as many times as needed */ - stackptr = p; -The following three macros (defined in memalloc.h) perform these -operations, but grow the stack if you run off the end: - STARTSTACKSTR(p); - STPUTC(c, p); /* repeated as many times as needed */ - grabstackstr(p); - -We now start a top-down look at the code: - -MAIN.C: The main routine performs some initialization, executes -the user's profile if necessary, and calls cmdloop. Cmdloop -repeatedly parses and executes commands. - -OPTIONS.C: This file contains the option processing code. It is -called from main to parse the shell arguments when the shell is -invoked, and it also contains the set builtin. The -i and -j op- -tions (the latter turns on job control) require changes in signal -handling. The routines setjobctl (in jobs.c) and setinteractive -(in trap.c) are called to handle changes to these options. - -PARSING: The parser code is all in parser.c. A recursive des- -cent parser is used. Syntax tables (generated by mksyntax) are -used to classify characters during lexical analysis. There are -three tables: one for normal use, one for use when inside single -quotes, and one for use when inside double quotes. The tables -are machine dependent because they are indexed by character vari- -ables and the range of a char varies from machine to machine. - -PARSE OUTPUT: The output of the parser consists of a tree of -nodes. The various types of nodes are defined in the file node- -types. - -Nodes of type NARG are used to represent both words and the con- -tents of here documents. An early version of ash kept the con- -tents of here documents in temporary files, but keeping here do- -cuments in memory typically results in significantly better per- -formance. It would have been nice to make it an option to use -temporary files for here documents, for the benefit of small -machines, but the code to keep track of when to delete the tem- -porary files was complex and I never fixed all the bugs in it. -(AT&T has been maintaining the Bourne shell for more than ten -years, and to the best of my knowledge they still haven't gotten -it to handle temporary files correctly in obscure cases.) - -The text field of a NARG structure points to the text of the -word. The text consists of ordinary characters and a number of -special codes defined in parser.h. The special codes are: - - CTLVAR Variable substitution - CTLENDVAR End of variable substitution - CTLBACKQ Command substitution - CTLBACKQ|CTLQUOTE Command substitution inside double quotes - CTLESC Escape next character - -A variable substitution contains the following elements: - - CTLVAR type name '=' [ alternative-text CTLENDVAR ] - -The type field is a single character specifying the type of sub- -stitution. The possible types are: - - VSNORMAL $var - VSMINUS ${var-text} - VSMINUS|VSNUL ${var:-text} - VSPLUS ${var+text} - VSPLUS|VSNUL ${var:+text} - VSQUESTION ${var?text} - VSQUESTION|VSNUL ${var:?text} - VSASSIGN ${var=text} - VSASSIGN|VSNUL ${var=text} - -In addition, the type field will have the VSQUOTE flag set if the -variable is enclosed in double quotes. The name of the variable -comes next, terminated by an equals sign. If the type is not -VSNORMAL, then the text field in the substitution follows, ter- -minated by a CTLENDVAR byte. - -Commands in back quotes are parsed and stored in a linked list. -The locations of these commands in the string are indicated by -CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether -the back quotes were enclosed in double quotes. - -The character CTLESC escapes the next character, so that in case -any of the CTL characters mentioned above appear in the input, -they can be passed through transparently. CTLESC is also used to -escape '*', '?', '[', and '!' characters which were quoted by the -user and thus should not be used for file name generation. - -CTLESC characters have proved to be particularly tricky to get -right. In the case of here documents which are not subject to -variable and command substitution, the parser doesn't insert any -CTLESC characters to begin with (so the contents of the text -field can be written without any processing). Other here docu- -ments, and words which are not subject to splitting and file name -generation, have the CTLESC characters removed during the vari- -able and command substitution phase. Words which are subject -splitting and file name generation have the CTLESC characters re- -moved as part of the file name phase. - -EXECUTION: Command execution is handled by the following files: - eval.c The top level routines. - redir.c Code to handle redirection of input and output. - jobs.c Code to handle forking, waiting, and job control. - exec.c Code to do path searches and the actual exec sys call. - expand.c Code to evaluate arguments. - var.c Maintains the variable symbol table. Called from expand.c. - -EVAL.C: Evaltree recursively executes a parse tree. The exit -status is returned in the global variable exitstatus. The alter- -native entry evalbackcmd is called to evaluate commands in back -quotes. It saves the result in memory if the command is a buil- -tin; otherwise it forks off a child to execute the command and -connects the standard output of the child to a pipe. - -JOBS.C: To create a process, you call makejob to return a job -structure, and then call forkshell (passing the job structure as -an argument) to create the process. Waitforjob waits for a job -to complete. These routines take care of process groups if job -control is defined. - -REDIR.C: Ash allows file descriptors to be redirected and then -restored without forking off a child process. This is accom- -plished by duplicating the original file descriptors. The redir- -tab structure records where the file descriptors have been dupli- -cated to. - -EXEC.C: The routine find_command locates a command, and enters -the command in the hash table if it is not already there. The -third argument specifies whether it is to print an error message -if the command is not found. (When a pipeline is set up, -find_command is called for all the commands in the pipeline be- -fore any forking is done, so to get the commands into the hash -table of the parent process. But to make command hashing as -transparent as possible, we silently ignore errors at that point -and only print error messages if the command cannot be found -later.) - -The routine shellexec is the interface to the exec system call. - -EXPAND.C: Arguments are processed in three passes. The first -(performed by the routine argstr) performs variable and command -substitution. The second (ifsbreakup) performs word splitting -and the third (expandmeta) performs file name generation. If the -"/u" directory is simulated, then when "/u/username" is replaced -by the user's home directory, the flag "didudir" is set. This -tells the cd command that it should print out the directory name, -just as it would if the "/u" directory were implemented using -symbolic links. - -VAR.C: Variables are stored in a hash table. Probably we should -switch to extensible hashing. The variable name is stored in the -same string as the value (using the format "name=value") so that -no string copying is needed to create the environment of a com- -mand. Variables which the shell references internally are preal- -located so that the shell can reference the values of these vari- -ables without doing a lookup. - -When a program is run, the code in eval.c sticks any environment -variables which precede the command (as in "PATH=xxx command") in -the variable table as the simplest way to strip duplicates, and -then calls "environment" to get the value of the environment. -There are two consequences of this. First, if an assignment to -PATH precedes the command, the value of PATH before the assign- -ment must be remembered and passed to shellexec. Second, if the -program turns out to be a shell procedure, the strings from the -environment variables which preceded the command must be pulled -out of the table and replaced with strings obtained from malloc, -since the former will automatically be freed when the stack (see -the entry on memalloc.c) is emptied. - -BUILTIN COMMANDS: The procedures for handling these are scat- -tered throughout the code, depending on which location appears -most appropriate. They can be recognized because their names al- -ways end in "cmd". The mapping from names to procedures is -specified in the file builtins, which is processed by the mkbuil- -tins command. - -A builtin command is invoked with argc and argv set up like a -normal program. A builtin command is allowed to overwrite its -arguments. Builtin routines can call nextopt to do option pars- -ing. This is kind of like getopt, but you don't pass argc and -argv to it. Builtin routines can also call error. This routine -normally terminates the shell (or returns to the main command -loop if the shell is interactive), but when called from a builtin -command it causes the builtin command to terminate with an exit -status of 2. - -The directory bltins contains commands which can be compiled in- -dependently but can also be built into the shell for efficiency -reasons. The makefile in this directory compiles these programs -in the normal fashion (so that they can be run regardless of -whether the invoker is ash), but also creates a library named -bltinlib.a which can be linked with ash. The header file bltin.h -takes care of most of the differences between the ash and the -stand-alone environment. The user should call the main routine -"main", and #define main to be the name of the routine to use -when the program is linked into ash. This #define should appear -before bltin.h is included; bltin.h will #undef main if the pro- -gram is to be compiled stand-alone. - -CD.C: This file defines the cd and pwd builtins. The pwd com- -mand runs /bin/pwd the first time it is invoked (unless the user -has already done a cd to an absolute pathname), but then -remembers the current directory and updates it when the cd com- -mand is run, so subsequent pwd commands run very fast. The main -complication in the cd command is in the docd command, which -resolves symbolic links into actual names and informs the user -where the user ended up if he crossed a symbolic link. - -SIGNALS: Trap.c implements the trap command. The routine set- -signal figures out what action should be taken when a signal is -received and invokes the signal system call to set the signal ac- -tion appropriately. When a signal that a user has set a trap for -is caught, the routine "onsig" sets a flag. The routine dotrap -is called at appropriate points to actually handle the signal. -When an interrupt is caught and no trap has been set for that -signal, the routine "onint" in error.c is called. - -OUTPUT: Ash uses its own output routines. There are three out- -put structures allocated. "Output" represents the standard out- -put, "errout" the standard error, and "memout" contains output -which is to be stored in memory. This last is used when a buil- -tin command appears in backquotes, to allow its output to be col- -lected without doing any I/O through the UNIX operating system. -The variables out1 and out2 normally point to output and errout, -respectively, but they are set to point to memout when appropri- -ate inside backquotes. - -INPUT: The basic input routine is pgetc, which reads from the -current input file. There is a stack of input files; the current -input file is the top file on this stack. The code allows the -input to come from a string rather than a file. (This is for the --c option and the "." and eval builtin commands.) The global -variable plinno is saved and restored when files are pushed and -popped from the stack. The parser routines store the number of -the current line in this variable. - -DEBUGGING: If DEBUG is defined in shell.h, then the shell will -write debugging information to the file $HOME/trace. Most of -this is done using the TRACE macro, which takes a set of printf -arguments inside two sets of parenthesis. Example: -"TRACE(("n=%d0, n))". The double parenthesis are necessary be- -cause the preprocessor can't handle functions with a variable -number of arguments. Defining DEBUG also causes the shell to -generate a core dump if it is sent a quit signal. The tracing -code is in show.c. diff --git a/bin/sh/USD.doc/Makefile b/bin/sh/USD.doc/Makefile deleted file mode 100644 index 55b7203..0000000 --- a/bin/sh/USD.doc/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# $NetBSD: Makefile,v 1.4 2014/07/05 19:23:00 dholland Exp $ -# @(#)Makefile 8.1 (Berkeley) 8/14/93 - -SECTION=reference/ref1 -ARTICLE=sh -SRCS= referargs t.mac t1 t2 t3 t4 -MACROS=-ms -ROFF_REFER=yes -#REFER_ARGS=-e -p Rv7man -EXTRAHTMLFILES=sh1.png sh2.png sh3.png sh4.png sh5.png - -.include <bsd.doc.mk> diff --git a/bin/sh/USD.doc/Rv7man b/bin/sh/USD.doc/Rv7man deleted file mode 100644 index 628c67f..0000000 --- a/bin/sh/USD.doc/Rv7man +++ /dev/null @@ -1,405 +0,0 @@ -%A L. P. Deutsch -%A B. W. Lampson -%T An online editor -%J Comm. Assoc. Comp. Mach. -%V 10 -%N 12 -%D December 1967 -%P 793-799, 803 -%K qed - -.[ -%r 17 -%K cstr -%R Comp. Sci. Tech. Rep. No. 17 -%I Bell Laboratories -%C Murray Hill, New Jersey -%A B. W. Kernighan -%A L. L. Cherry -%T A System for Typesetting Mathematics -%d May 1974, revised April 1977 -%J Comm. Assoc. Comp. Mach. -%K acm cacm -%V 18 -%P 151-157 -%D March 1975 -.] - -%T U\s-2NIX\s0 Time-Sharing System: Document Preparation -%K unix bstj -%A B. W. Kernighan -%A M. E. Lesk -%A J. F. Ossanna -%J Bell Sys. Tech. J. -%V 57 -%N 6 -%P 2115-2135 -%D 1978 - -%A T. A. Dolotta -%A J. R. Mashey -%T An Introduction to the Programmer's Workbench -%J Proc. 2nd Int. Conf. on Software Engineering -%D October 13-15, 1976 -%P 164-168 - -%T U\s-2NIX\s0 Time-Sharing System: The Programmer's Workbench -%A T. A. Dolotta -%A R. C. Haight -%A J. R. Mashey -%J Bell Sys. Tech. J. -%V 57 -%N 6 -%P 2177-2200 -%D 1978 -%K unix bstj - -%T U\s-2NIX\s0 Time-Sharing System: U\s-2NIX\s0 on a Microprocessor -%K unix bstj -%A H. Lycklama -%J Bell Sys. Tech. J. -%V 57 -%N 6 -%P 2087-2101 -%D 1978 - -%T The C Programming Language -%A B. W. Kernighan -%A D. M. Ritchie -%I Prentice-Hall -%C Englewood Cliffs, New Jersey -%D 1978 - -%T Computer Recreations -%A Aleph-null -%J Software Practice and Experience -%V 1 -%N 2 -%D April-June 1971 -%P 201-204 - -%T U\s-2NIX\s0 Time-Sharing System: The U\s-2NIX\s0 Shell -%A S. R. Bourne -%K unix bstj -%J Bell Sys. Tech. J. -%V 57 -%N 6 -%P 1971-1990 -%D 1978 - -%A L. P. Deutsch -%A B. W. Lampson -%T \*sSDS\*n 930 time-sharing system preliminary reference manual -%R Doc. 30.10.10, Project \*sGENIE\*n -%C Univ. Cal. at Berkeley -%D April 1965 - -%A R. J. Feiertag -%A E. I. Organick -%T The Multics input-output system -%J Proc. Third Symposium on Operating Systems Principles -%D October 18-20, 1971 -%P 35-41 - -%A D. G. Bobrow -%A J. D. Burchfiel -%A D. L. Murphy -%A R. S. Tomlinson -%T \*sTENEX\*n, a Paged Time Sharing System for the \*sPDP\*n-10 -%J Comm. Assoc. Comp. Mach. -%V 15 -%N 3 -%D March 1972 -%K tenex -%P 135-143 - -%A R. E. Griswold -%A D. R. Hanson -%T An Overview of SL5 -%J SIGPLAN Notices -%V 12 -%N 4 -%D April 1977 -%P 40-50 - -%A E. W. Dijkstra -%T Cooperating Sequential Processes -%B Programming Languages -%E F. Genuys -%I Academic Press -%C New York -%D 1968 -%P 43-112 - -%A J. A. Hawley -%A W. B. Meyer -%T M\s-2UNIX\s0, A Multiprocessing Version of U\s-2NIX\s0 -%K munix unix -%R M.S. Thesis -%I Naval Postgraduate School -%C Monterey, Cal. -%D 1975 - -%T The U\s-2NIX\s0 Time-Sharing System -%K unix bstj -%A D. M. Ritchie -%A K. Thompson -%J Bell Sys. Tech. J. -%V 57 -%N 6 -%P 1905-1929 -%D 1978 - -%A E. I. Organick -%T The M\s-2ULTICS\s0 System -%K multics -%I M.I.T. Press -%C Cambridge, Mass. -%D 1972 - -%T UNIX for Beginners -%A B. W. Kernighan -%D 1978 - -%T U\s-2NIX\s0 Programmer's Man\&ual -%A K. Thompson -%A D. M. Ritchie -%K unix -%I Bell Laboratories -%O Seventh Edition. -%D 1978 - -%A K. Thompson -%T The U\s-2NIX\s0 Command Language -%B Structured Programming\(emInfotech State of the Art Report -%I Infotech International Ltd. -%C Nicholson House, Maidenhead, Berkshire, England -%D March 1975 -%P 375-384 -%K unix -%X pwb -Brief description of shell syntax and semantics, without much -detail on implementation. -Much on pipes and convenience of hooking programs together. -Includes SERMONETTE: -"Many familiar computing `concepts' are missing from UNIX. -Files have no records. There are no access methods. -There are no file types. These concepts fill a much-needed gap. -I sincerely hope that when future systems are designed by -manufacturers the value of some of these ingrained notions is re-examined. -Like the politician and his `common man', manufacturers have -their `average user'. - -%A J. R. Mashey -%T PWB/UNIX Shell Tutorial -%D September 30, 1977 - -%A D. F. Hartley (Ed.) -%T The Cambridge Multiple Access System \- Users Reference Manual -%I University Mathematical Laboratory -%C Cambridge, England -%D 1968 - -%A P. A. Crisman (Ed.) -%T The Compatible Time-Sharing System -%I M.I.T. Press -%K whole ctss book -%C Cambridge, Mass. -%D 1965 - -%T LR Parsing -%A A. V. Aho -%A S. C. Johnson -%J Comp. Surveys -%V 6 -%N 2 -%P 99-124 -%D June 1974 - -%T Deterministic Parsing of Ambiguous Grammars -%A A. V. Aho -%A S. C. Johnson -%A J. D. Ullman -%J Comm. Assoc. Comp. Mach. -%K acm cacm -%V 18 -%N 8 -%P 441-452 -%D August 1975 - -%A A. V. Aho -%A J. D. Ullman -%T Principles of Compiler Design -%I Addison-Wesley -%C Reading, Mass. -%D 1977 - -.[ -%r 65 -%R Comp. Sci. Tech. Rep. No. 65 -%K CSTR -%A S. C. Johnson -%T Lint, a C Program Checker -%D December 1977 -%O updated version TM 78-1273-3 -%D 1978 -.] - -%T A Portable Compiler: Theory and Practice -%A S. C. Johnson -%J Proc. 5th ACM Symp. on Principles of Programming Languages -%P 97-104 -%D January 1978 - -.[ -%r 39 -%K CSTR -%R Comp. Sci. Tech. Rep. No. 39 -%I Bell Laboratories -%C Murray Hill, New Jersey -%A M. E. Lesk -%T Lex \(em A Lexical Analyzer Generator -%D October 1975 -.] - -.[ -%r 32 -%K CSTR -%R Comp. Sci. Tech. Rep. No. 32 -%I Bell Laboratories -%C Murray Hill, New Jersey -%A S. C. Johnson -%T Yacc \(em Yet Another Compiler-Compiler -%D July 1975 -.] - -%T U\s-2NIX\s0 Time-Sharing System: Portability of C Programs and the U\s-2NIX\s0 System -%K unix bstj -%A S. C. Johnson -%A D. M. Ritchie -%J Bell Sys. Tech. J. -%V 57 -%N 6 -%P 2021-2048 -%D 1978 - -%T Typing Documents on UNIX and GCOS: The -ms Macros for Troff -%A M. E. Lesk -%D 1977 - -%A K. Thompson -%A D. M. Ritchie -%T U\s-2NIX\s0 Programmer's Manual -%K unix -%I Bell Laboratories -%O Sixth Edition -%D May 1975 - -%T The Network U\s-2NIX\s0 System -%K unix -%A G. L. Chesson -%J Operating Systems Review -%V 9 -%N 5 -%P 60-66 -%D 1975 -%O Also in \f2Proc. 5th Symp. on Operating Systems Principles.\f1 - -%T Spider \(em An Experimental Data Communications System -%Z ctr127 -%A A. G. Fraser -%J Proc. IEEE Conf. on Communications -%P 21F -%O IEEE Cat. No. 74CH0859-9-CSCB. -%D June 1974 - -%T A Virtual Channel Network -%A A. G. Fraser -%J Datamation -%P 51-56 -%D February 1975 - -.[ -%r 41 -%K CSTR -%R Comp. Sci. Tech. Rep. No. 41 -%I Bell Laboratories -%C Murray Hill, New Jersey -%A J. W. Hunt -%A M. D. McIlroy -%T An Algorithm for Differential File Comparison -%D June 1976 -.] - -%A F. P. Brooks, Jr. -%T The Mythical Man-Month -%I Addison-Wesley -%C Reading, Mass. -%D 1975 -%X pwb -Readable, classic reference on software engineering and -problems of large projects, from someone with experience in them. -Required reading for any software engineer, even if conclusions may not -always be agreed with. -%br -"The second is the most dangerous system a man ever designs." p.55. -%br -"Hence plan to throw one away; you will, anyhow." p.116. -%br -"Cosgrove has perceptively pointed out that the programmer delivers -satisfaction of a user need rather than any tangible product. -And both the actual need and the user's perception of that need -will change as programs are built, tested, and used." p.117. -%br -"The total cost of maintaining a widely used program is typically 40 percent -or more of the cost of developing it." p.121. -%br -"As shown above, amalgamating prose and program reduces the total -number of characters to be stored." p.175. - -%T A Portable Compiler for the Language C -%A A. Snyder -%I Master's Thesis, M.I.T. -%C Cambridge, Mass. -%D 1974 - -%T The C Language Calling Sequence -%A M. E. Lesk -%A S. C. Johnson -%A D. M. Ritchie -%D 1977 - -%T Optimal Code Generation for Expression Trees -%A A. V. Aho -%A S. C. Johnson -%D 1975 -%J J. Assoc. Comp. Mach. -%K acm jacm -%V 23 -%N 3 -%P 488-501 -%O Also in \f2Proc. ACM Symp. on Theory of Computing,\f1 pp. 207-217, 1975. - -%A R. Sethi -%A J. D. Ullman -%T The Generation of Optimal Code for Arithmetic Expressions -%J J. Assoc. Comp. Mach. -%K acm jacm -%V 17 -%N 4 -%D October 1970 -%P 715-728 -%O Reprinted as pp. 229-247 in \fICompiler Techniques\fR, ed. B. W. Pollack, Auerbach, Princeton NJ (1972). -%X pwb -Optimal approach for straight-line, fixed -number of regs. - -%T Code Generation for Machines with Multiregister -Operations -%A A. V. Aho -%A S. C. Johnson -%A J. D. Ullman -%J Proc. 4th ACM Symp. on Principles of Programming Languages -%P 21-28 -%D January 1977 - diff --git a/bin/sh/USD.doc/referargs b/bin/sh/USD.doc/referargs deleted file mode 100644 index 3bb6284..0000000 --- a/bin/sh/USD.doc/referargs +++ /dev/null @@ -1,8 +0,0 @@ -.\" $NetBSD: referargs,v 1.1 2014/07/05 19:22:02 dholland Exp $ -.\" -.\" Arguments for refer; these were previously passed on the refer(1) -.\" command line: -e -p Rv7man -.R1 -accumulate -database Rv7man -.R2 diff --git a/bin/sh/USD.doc/t.mac b/bin/sh/USD.doc/t.mac deleted file mode 100644 index 9bf65c8..0000000 --- a/bin/sh/USD.doc/t.mac +++ /dev/null @@ -1,69 +0,0 @@ -.\" $NetBSD: t.mac,v 1.2 2010/08/22 02:19:07 perry Exp $ -.\" -.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions are -.\" met: -.\" -.\" Redistributions of source code and documentation must retain the above -.\" copyright notice, this list of conditions and the following -.\" disclaimer. -.\" -.\" Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" -.\" This product includes software developed or owned by Caldera -.\" International, Inc. Neither the name of Caldera International, Inc. -.\" nor the names of other contributors may be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA -.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE -.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -.\" OR OTHERWISE) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" @(#)t.mac 8.1 (Berkeley) 8/14/93 -.\" -.ds ZZ \fB.\|.\|.\fP -.ds ST \v'.3m'\s+2*\s0\v'-.3m' -.ds DO \h'\w'do 'u' -.ds Ca \h'\w'case 'u' -.ds WH \h'\w'while 'u' -.ds VT \|\fB\(or\fP\| -.ds TH \h'\w'then 'u' -.ds DC \*(DO\*(Ca -.ds AP >\h'-.2m'> -.ds HE <\h'-.2m'< -. \" macros for algol 68c reference manual -.ds DA 1977 November 1 -.ds md \v'.25m' -.ds mu \v'-.25m' -.ds U \*(mu\s-3 -.ds V \s0\*(md -.ds L \*(md\s-3 -.ds M \s0\*(mu -.ds S \s-1 -.ds T \s0 -. \" small 1 -.ds O \*S1\*T -.ds h \| -.ds s \|\| -. \" ellipsis -.ds e .\|.\|. -. \" subscripts -.ds 1 \*(md\s-41\s0\*(mu -.ds 2 \*(md\s-42\s0\*(mu diff --git a/bin/sh/USD.doc/t1 b/bin/sh/USD.doc/t1 deleted file mode 100644 index 075511f..0000000 --- a/bin/sh/USD.doc/t1 +++ /dev/null @@ -1,553 +0,0 @@ -.\" $NetBSD: t1,v 1.3 2010/08/22 02:19:07 perry Exp $ -.\" -.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions are -.\" met: -.\" -.\" Redistributions of source code and documentation must retain the above -.\" copyright notice, this list of conditions and the following -.\" disclaimer. -.\" -.\" Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" All advertising materials mentioning features or use of this software -.\" must display the following acknowledgment: -.\" -.\" This product includes software developed or owned by Caldera -.\" International, Inc. Neither the name of Caldera International, Inc. -.\" nor the names of other contributors may be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA -.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE -.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -.\" OR OTHERWISE) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" @(#)t1 8.1 (Berkeley) 8/14/93 -.\" -.EH 'USD:3-%''An Introduction to the UNIX Shell' -.OH 'An Introduction to the UNIX Shell''USD:3-%' -.\".RP -.TL -An Introduction to the UNIX Shell -.AU -S. R. Bourne -.AI -Murray Hill, NJ -.AU -(Updated for 4.3BSD by Mark Seiden) -.AU -(Further updated by Perry E. Metzger)\(dg -.AB -.FS -\(dg This paper was updated in 2010 to reflect most features of modern -POSIX shells, which all follow the design of S.R. Bourne's original v7 -Unix shell. -Among these are ash, bash, ksh and others. -Typically one of these will be installed as /bin/sh on a modern system. -It does not describe the behavior of the c shell (csh). -If it's the c shell (csh) you're interested in, a good place to -begin is William Joy's paper "An Introduction to the C shell" (USD:4). -.FE -.LP -The -.ul -shell -is a command programming language that provides an interface -to the -.UX -operating system. -Its features include -control-flow primitives, parameter passing, variables and -string substitution. -Constructs such as -.ul -while, if then else, case -and -.ul -for -are available. -Two-way communication is possible between the -.ul -shell -and commands. -String-valued parameters, typically file names or flags, may be -passed to a command. -A return code is set by commands that may be used to determine control-flow, -and the standard output from a command may be used -as shell input. -.LP -The -.ul -shell -can modify the environment -in which commands run. -Input and output can be redirected -to files, and processes that communicate through `pipes' -can be invoked. -Commands are found by -searching directories -in the file system in a -sequence that can be defined by the user. -Commands can be read either from the terminal or from a file, -which allows command procedures to be -stored for later use. -.AE -.ds ST \v'.3m'\s+2*\s0\v'-.3m' -.SH -1.0\ Introduction -.LP -The shell is both a command language -and a programming language -that provides an interface to the UNIX -operating system. -This memorandum describes, with -examples, the UNIX shell. -The first section covers most of the -everyday requirements -of terminal users. -Some familiarity with UNIX -is an advantage when reading this section; -see, for example, -"UNIX for beginners". -.[ -unix beginn kernigh 1978 -.] -Section 2 describes those features -of the shell primarily intended -for use within shell procedures. -These include the control-flow -primitives and string-valued variables -provided by the shell. -A knowledge of a programming language -would be a help when reading this section. -The last section describes the more -advanced features of the shell. -References of the form "see \fIpipe\fP (2)" -are to a section of the UNIX manual. -.[ -seventh 1978 ritchie thompson -.] -.SH -1.1\ Simple\ commands -.LP -Simple commands consist of one or more words -separated by blanks. -The first word is the name of the command -to be executed; any remaining words -are passed as arguments to the command. -For example, -.DS - who -.DE -is a command that prints the names -of users logged in. -The command -.DS - ls \(mil -.DE -prints a list of files in the current -directory. -The argument \fI\(mil\fP tells \fIls\fP -to print status information, size and -the creation date for each file. -.SH -1.2\ Input\ output\ redirection -.LP -Most commands produce output on the standard output -that is initially connected to the terminal. -This output may be sent to a file -by writing, for example, -.DS - ls \(mil >file -.DE -The notation \fI>file\fP -is interpreted by the shell and is not passed -as an argument to \fIls.\fP -If \fIfile\fP does not exist then the -shell creates it; -otherwise the original contents of -\fIfile\fP are replaced with the output -from \fIls.\fP -Output may be appended to a file -using the notation -.DS - ls \(mil \*(APfile -.DE -In this case \fIfile\fP is also created if it does not already -exist. -.LP -The standard input of a command may be taken -from a file instead of the terminal by -writing, for example, -.DS - wc <file -.DE -The command \fIwc\fP reads its standard input -(in this case redirected from \fIfile\fP) -and prints the number of characters, words and -lines found. -If only the number of lines is required -then -.DS - wc \(mil <file -.DE -could be used. -.\" I considered adding the following, but have thought better of it -.\" for now. -.\" -- Perry Metzger -.\" -.\" .LP -.\" Error messages are typically printed by commands on a different -.\" channel, called standard error, which may also be redirected using the -.\" notation 2>\|. -.\" For example -.\" .DS -.\" command some args >out 2>errors -.\" .DE -.\" will redirect standard output to the file `out' but standard error -.\" (and thus all error messages) to `errors'. -.\" The notation 2>&1 sets standard error pointing to the same -.\" place as standard out. -.\" Thus: -.\" .DS -.\" command some args 2>&1 >everything -.\" .DE -.\" will put both standard out and standard error into the file `everything'. -.\" See section 3.7 below for more details. -.SH -1.3\ Pipelines\ and\ filters -.LP -The standard output of one command may be -connected to the standard input of another -by writing -the `pipe' operator, -indicated by \*(VT, -as in, -.DS - ls \(mil \*(VT wc -.DE -Two commands connected in this way constitute -a \fIpipeline\fP and -the overall effect is the same as -.DS - ls \(mil >file; wc <file -.DE -except that no \fIfile\fP is used. -Instead the two \fIprocesses\fP are connected -by a pipe (see \fIpipe\fP(2)) and are -run in parallel. -Pipes are unidirectional and -synchronization is achieved by -halting \fIwc\fP when there is -nothing to read and halting \fIls\fP -when the pipe is full. -.LP -A \fIfilter\fP is a command -that reads its standard input, -transforms it in some way, -and prints the result as output. -One such filter, \fIgrep,\fP -selects from its input those lines -that contain some specified string. -For example, -.DS - ls \*(VT grep old -.DE -prints those lines, if any, of the output -from \fIls\fP that contain -the string \fIold.\fP -Another useful filter is \fIsort\fP. -For example, -.DS - who \*(VT sort -.DE -will print an alphabetically sorted list -of logged in users. -.LP -A pipeline may consist of more than two commands, -for example, -.DS - ls \*(VT grep old \*(VT wc \(mil -.DE -prints the number of file names -in the current directory containing -the string \fIold.\fP -.SH -1.4\ Background\ commands -.LP -To execute a command (or pipeline) the shell normally -creates the new \fIprocesses\fP -and waits for them to finish. -A command may be run without waiting -for it to finish. -For example, -.DS - cc pgm.c & -.DE -calls the C compiler to compile -the file \fIpgm.c\|.\fP -The trailing \fB&\fP is an operator that instructs the shell -not to wait for the command to finish. -To help keep track of such a process -the shell reports its job number (see below) and process -id following its creation. -Such a command is said to be running in the \fIbackground\fP. -By contrast, a command executed without the \fB&\fP is said to be -running in the \fIforeground\fP.\(dg -.FS -\(dg Even after execution, one may move commands from the foreground -to the background, or temporarily suspend their execution (which is -known as \fIstopping\fP a command. -This is described in detail in section 3.10 on \fIJob Control\fB. -.FE -.LP -A list of currently active processes, including ones not associated -with the current shell, may be obtained using the \fIps\fP(1) command. -.SH -1.5\ File\ name\ generation -.LP -Many commands accept arguments -which are file names. -For example, -.DS - ls \(mil main.c -.DE -prints information relating to the file \fImain.c\fP\|. -.LP -The shell provides a mechanism -for generating a list of file names -that match a pattern. -For example, -.DS - ls \(mil \*(ST.c -.DE -generates, as arguments to \fIls,\fP -all file names in the current directory that end in \fI.c\|.\fP -The character \*(ST is a pattern that will match any string -including the null string. -In general \fIpatterns\fP are specified -as follows. -.RS -.IP \fB\*(ST\fR 8 -Matches any string of characters -including the null string. -.IP \fB?\fR 8 -Matches any single character. -.IP \fB[\*(ZZ]\fR 8 -Matches any one of the characters -enclosed. -A pair of characters separated by a minus will -match any character lexically between -the pair. -.RE -.LP -For example, -.DS - [a\(miz]\*(ST -.DE -matches all names in the current directory -beginning with -one of the letters \fIa\fP through \fIz.\fP -.DS - /usr/fred/test/? -.DE -matches all names in the directory -\fB/usr/fred/test\fP that consist of a single character. -If no file name is found that matches -the pattern then the pattern is passed, -unchanged, as an argument. -.LP -This mechanism is useful both to save typing -and to select names according to some pattern. -It may also be used to find files. -For example, -.DS - echo /usr/fred/\*(ST/core -.DE -finds and prints the names of all \fIcore\fP files in sub-directories -of \fB/usr/fred\|.\fP -(\fIecho\fP is a standard UNIX command that prints -its arguments, separated by blanks.) -This last feature can be expensive, -requiring a scan of all -sub-directories of \fB/usr/fred\|.\fP -.LP -There is one exception to the general -rules given for patterns. -The character `\fB.\fP' -at the start of a file name must be explicitly -matched. -.DS - echo \*(ST -.DE -will therefore echo all file names in the current -directory not beginning -with `\fB.\fP'\|. -.DS - echo \fB.\fP\*(ST -.DE -will echo all those file names that begin with `\fB.\fP'\|. -This avoids inadvertent matching -of the names `\fB.\fP' and `\fB..\fP' -which mean `the current directory' -and `the parent directory' -respectively. -(Notice that \fIls\fP suppresses -information for the files `\fB.\fP' and `\fB..\fP'\|.) -.LP -Finally, the tilde character, `\fB\(ap\fP', may be used to indicate the -home directory of a user. -The `\fB\(ap\fP' at the beginning of a path name followed by a -non-alphabetic character expands to the current user's home -directory. -If the `\fB\(ap\fP' is followed by a login name, it expands to the named -user's home directory. -For example: -.DS - ls \(ap - cd \(apegbert/ -.DE -will list the contents of the user's home directory and then change -to the home directory of the user ``egbert''. -.SH -1.6\ Quoting -.LP -Characters that have a special meaning -to the shell, such as \fB< > \*(ST ? \*(VT &\|,\fR -are called metacharacters. -A complete list of metacharacters is given -in appendix B. -Any character preceded by a \fB\\\fR is \fIquoted\fP -and loses its special meaning, if any. -The \fB\\\fP is elided so that -.DS - echo \\? -.DE -will echo a single \fB?\|,\fP -and -.DS - echo \\\\ -.DE -will echo a single \fB\\\|.\fR -To allow long strings to be continued over -more than one line -the sequence \fB\\newline\fP -is ignored. -.LP -\fB\\\fP is convenient for quoting -single characters. -When more than one character needs -quoting the above mechanism is clumsy and -error prone. -A string of characters may be quoted -by enclosing the string between single quotes. -For example, -.DS - echo xx\'\*(ST\*(ST\*(ST\*(ST\'xx -.DE -will echo -.DS - xx\*(ST\*(ST\*(ST\*(STxx -.DE -The quoted string may not contain -a single quote -but may contain newlines, which are preserved. -This quoting mechanism is the most -simple and is recommended -for casual use. -.LP -A third quoting mechanism using double quotes -is also available -that prevents interpretation of some but not all -metacharacters. -Discussion of the -details is deferred -to section 3.5\|. -.SH -1.7\ Prompting -.LP -When the shell is used from a terminal it will -issue a prompt before reading a command. -By default this prompt is `\fB$\ \fR'\|. -It may be changed by saying, -for example, -.DS - \s-1PS1\s0="yesdear$ " -.DE -that sets the prompt to be the string \fIyesdear$\|.\fP -If a newline is typed and further input is needed -then the shell will issue the prompt `\fB>\ \fR'\|. -Sometimes this can be caused by mistyping -a quote mark. -If it is unexpected then entering the interrupt character -(typically \s-1CONTROL-C\s0) -will return the shell to read another command. -This prompt may be changed by saying, for example, -.DS - \s-1PS2\s0=more -.DE -Entering the interrupt character may also be used to terminate most -programs running as the current foreground job. -.LP -(\s-1PS1\s0 and \s-1PS2\s0 are \fIshell variables\fP, which will be -described in section 2.4 below.) -.SH -1.8\ The\ shell\ and\ login -.LP -Following \fIlogin\fP(1) -the shell is called to read and execute -commands typed at the terminal. -If the user's login directory -contains the file \fB.profile\fP -then it is assumed to contain commands -and is read by the shell before reading -any commands from the terminal. -.LP -(Most versions of the shell also specify a file that is read and -executed on start-up whether or not the shell is invoked by login. -The \s-1ENV\s0 shell variable, described in section 2.4 below, can be -used to override the name of this file. -See the shell manual page for further information.) -.SH -1.9\ Summary -.sp -.RS -.IP \(bu -\fBls\fP -.br -Print the names of files in the current directory. -.IP \(bu -\fBls >file\fP -.br -Put the output from \fIls\fP into \fIfile.\fP -.IP \(bu -\fBls \*(VT wc \(mil\fR -.br -Print the number of files in the current directory. -.IP \(bu -\fBls \*(VT grep old\fR -.br -Print those file names containing the string \fIold.\fP -.IP \(bu -\fBls \*(VT grep old \*(VT wc \(mil\fR -.br -Print the number of files whose name contains the string \fIold.\fP -.IP \(bu -\fBcc pgm.c &\fR -.br -Run \fIcc\fP in the background. -.RE diff --git a/bin/sh/USD.doc/t2 b/bin/sh/USD.doc/t2 deleted file mode 100644 index d49747e..0000000 --- a/bin/sh/USD.doc/t2 +++ /dev/null @@ -1,971 +0,0 @@ -.\" $NetBSD: t2,v 1.3 2010/08/22 02:19:07 perry Exp $ -.\" -.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions are -.\" met: -.\" -.\" Redistributions of source code and documentation must retain the above -.\" copyright notice, this list of conditions and the following -.\" disclaimer. -.\" -.\" Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" All advertising materials mentioning features or use of this software -.\" must display the following acknowledgment: -.\" -.\" This product includes software developed or owned by Caldera -.\" International, Inc. Neither the name of Caldera International, Inc. -.\" nor the names of other contributors may be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA -.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE -.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -.\" OR OTHERWISE) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" @(#)t2 8.1 (Berkeley) 6/8/93 -.\" -.SH -2.0\ Shell\ scripts -.LP -The shell may be used to read and execute commands -contained in a file. -For example, -.DS - sh file [ args \*(ZZ ] -.DE -calls the shell to read commands from \fIfile.\fP -Such a file is called a \fIshell script.\fP -Arguments may be supplied with the call -and are referred to in \fIfile\fP -using the positional parameters -\fB$1, $2, \*(ZZ\|.\fR -.LP -For example, if the file \fIwg\fP contains -.DS - who \*(VT grep $1 -.DE -then -.DS - sh wg fred -.DE -is equivalent to -.DS - who \*(VT grep fred -.DE -.LP -UNIX files have three independent attributes, -\fIread,\fP \fIwrite\fP and \fIexecute.\fP -The UNIX command \fIchmod\fP(1) may be used -to make a file executable. -For example, -.DS - chmod +x wg -.DE -will ensure that the file \fIwg\fP has execute status. -Following this, the command -.DS - wg fred -.DE -is equivalent to -.DS - sh wg fred -.DE -This allows shell scripts and other programs -to be used interchangeably. -In either case a new process is created to -run the command. -.LP -The `\fB#\fP' character is used as a comment character by the shell. -All characters following the `#' on a line are ignored. -.LP -A typical modern system has several different shells, some with differing -command syntax, and it is desirable to specify which one should be -invoked when an executable script is invoked. -If the special comment -.DS - #!/\fIpath\fP/\fIto\fP/\fIinterpreter\fP -.DE -appears as the first line in a script, it is used to specify the -absolute pathname of the shell (or other interpreter) that should be -used to execute the file. -(Without such a line, \fB/bin/sh\fP is assumed.) -It is best if a script explicitly states -what shell it is intended for in this manner. -.LP -As well as providing names for the positional -parameters, -the number of positional parameters to a script -is available as \fB$#\|.\fP -The name of the file being executed -is available as \fB$0\|.\fP -.LP -A special shell parameter \fB$\*(ST\fP -is used to substitute for all positional parameters -except \fB$0\|.\fP -A typical use of this is to provide -some default arguments, -as in, -.DS - nroff \(miT450 \(mims $\*(ST -.DE -which simply prepends some arguments -to those already given. -(The variable \fB$@\fP also expands to ``all positional -parameters'', but is subtly different when expanded inside quotes. -See section 3.5, below.) -.SH -2.1\ Control\ flow\ -\ for -.LP -A frequent use of shell scripts is to loop -through the arguments (\fB$1, $2, \*(ZZ\fR) -executing commands once for each argument. -An example of such a script is -\fItel\fP that searches the file -\fB/usr/share/telnos\fR -that contains lines of the form -.DS - \*(ZZ - fred mh0123 - bert mh0789 - \*(ZZ -.DE -The text of \fItel\fP is -.DS - #!/bin/sh - - for i - do - grep $i /usr/share/telnos - done -.DE -The command -.DS - tel fred -.DE -prints those lines in \fB/usr/share/telnos\fR -that contain the string \fIfred\|.\fP -.DS - tel fred bert -.DE -prints those lines containing \fIfred\fP -followed by those for \fIbert.\fP -.LP -The \fBfor\fP loop notation is recognized by the shell -and has the general form -.DS - \fBfor\fR \fIname\fR \fBin\fR \fIw1 w2 \*(ZZ\fR - \fBdo\fR \fIcommand-list\fR - \fBdone\fR -.DE -A \fIcommand-list\fP is a sequence of one or more -simple commands separated or terminated by a newline or semicolon. -Furthermore, reserved words -like \fBdo\fP and \fBdone\fP are only -recognized following a newline or -semicolon. -\fIname\fP is a shell variable that is set -to the words \fIw1 w2 \*(ZZ\fR in turn each time the \fIcommand-list\fP -following \fBdo\fP -is executed. -If \fBin\fR \fIw1 w2 \*(ZZ\fR -is omitted then the loop -is executed once for each positional parameter; -that is, \fBin\fR \fI$\*(ST\fR is assumed. -.LP -Another example of the use of the \fBfor\fP -loop is the \fIcreate\fP command -whose text is -.DS - for i do >$i; done -.DE -The command -.DS - create alpha beta -.DE -ensures that two empty files -\fIalpha\fP and \fIbeta\fP exist -and are empty. -The notation \fI>file\fP may be used on its -own to create or clear the contents of a file. -Notice also that a semicolon (or newline) is required before \fBdone.\fP -.SH -2.2\ Control\ flow\ -\ case -.LP -A multiple way branch is provided for by the -\fBcase\fP notation. -For example, -.DS - case $# in - \*(Ca1) cat \*(AP$1 ;; - \*(Ca2) cat \*(AP$2 <$1 ;; - \*(Ca\*(ST) echo \'usage: append [ from ] to\' ;; - esac -.DE -is an \fIappend\fP command. -When called -with one argument as -.DS - append file -.DE -\fB$#\fP is the string \fI1\fP and -the standard input is copied onto the -end of \fIfile\fP -using the \fIcat\fP command. -.DS - append file1 file2 -.DE -appends the contents of \fIfile1\fP -onto \fIfile2.\fP -If the number of arguments supplied to -\fIappend\fP is other than 1 or 2 -then a message is printed indicating -proper usage. -.LP -The general form of the \fBcase\fP command -is -.DS - \fBcase \fIword \fBin - \*(Ca\fIpattern\|\fB)\ \fIcommand-list\fB\|;; - \*(Ca\*(ZZ - \fBesac\fR -.DE -The shell attempts to match -\fIword\fR with each \fIpattern,\fR -in the order in which the patterns -appear. -If a match is found the -associated \fIcommand-list\fP is -executed and execution -of the \fBcase\fP is complete. -Since \*(ST is the pattern that matches any -string it can be used for the default case. -.LP -A word of caution: -no check is made to ensure that only -one pattern matches -the case argument. -The first match found defines the set of commands -to be executed. -In the example below the commands following -the second \*(ST will never be executed. -.DS - case $# in - \*(Ca\*(ST) \*(ZZ ;; - \*(Ca\*(ST) \*(ZZ ;; - esac -.DE -.LP -Another example of the use of the \fBcase\fP -construction is to distinguish -between different forms -of an argument. -The following example is a fragment of a \fIcc\fP command. -.DS - for i - do case $i in - \*(DC\(mi[ocs]) \*(ZZ ;; - \*(DC\(mi\*(ST) echo "unknown flag $i" ;; - \*(DC\*(ST.c) /lib/c0 $i \*(ZZ ;; - \*(DC\*(ST) echo "unexpected argument $i" ;; - \*(DOesac - done -.DE -.LP -To allow the same commands to be associated -with more than one pattern -the \fBcase\fP command provides -for alternative patterns -separated by a \*(VT\|. -For example, -.DS - case $i in - \*(Ca\(mix\*(VT\(miy) \*(ZZ - esac -.DE -is equivalent to -.DS - case $i in - \*(Ca\(mi[xy]) \*(ZZ - esac -.DE -.LP -The usual quoting conventions apply -so that -.DS - case $i in - \*(Ca\\?) \*(ZZ -.DE -will match the character \fB?\|.\fP -.SH -2.3\ Here\ documents -.LP -The shell script \fItel\fP -in section 2.1 uses the file \fB/usr/share/telnos\fR -to supply the data -for \fIgrep.\fP -An alternative is to include this -data -within the shell script as a \fIhere\fP document, as in, -.DS - for i - do grep $i \*(HE! - \*(DO\*(ZZ - \*(DOfred mh0123 - \*(DObert mh0789 - \*(DO\*(ZZ - ! - done -.DE -In this example -the shell takes the lines between \fB\*(HE!\fR and \fB!\fR -as the standard input for \fIgrep.\fP -The string \fB!\fR is arbitrary, the document -being terminated by a line that consists -of the string following \*(HE\|. -.LP -Parameters are substituted in the document -before it is made available to \fIgrep\fP -as illustrated by the following script -called \fIedg\|.\fP -.DS - ed $3 \*(HE% - g/$1/s//$2/g - w - % -.DE -The call -.DS - edg string1 string2 file -.DE -is then equivalent to the command -.DS - ed file \*(HE% - g/string1/s//string2/g - w - % -.DE -and changes all occurrences of \fIstring1\fP -in \fIfile\fP to \fIstring2\|.\fP -Substitution can be prevented using \\ -to quote the special character \fB$\fP -as in -.DS - ed $3 \*(HE+ - 1,\\$s/$1/$2/g - w - + -.DE -(This version of \fIedg\fP is equivalent to -the first except that \fIed\fP will print -a \fB?\fR if there are no occurrences of -the string \fB$1\|.\fP) -Substitution within a \fIhere\fP document -may be prevented entirely by quoting -the terminating string, -for example, -.DS - grep $i \*(HE'end' - \*(ZZ - end -.DE -The document is presented -without modification to \fIgrep.\fP -If parameter substitution is not required -in a \fIhere\fP document this latter form -is more efficient. -.SH -2.4\ Shell\ variables\(dg -.LP -.FS -Also known as \fIenvironment variables\fB, see \fIenvironment\fB(7). -.FE -The shell -provides string-valued variables. -Variable names begin with a letter -and consist of letters, digits and -underscores. -Variables may be given values by writing, for example, -.DS - user=fred\ box=m000\ acct=mh0000 -.DE -which assigns values to the variables -\fBuser, box\fP and \fBacct.\fP -A variable may be set to the null string -by saying, for example, -.DS - null= -.DE -The value of a variable is substituted -by preceding its name with \fB$\|\fP; -for example, -.DS - echo $user -.DE -will echo \fIfred.\fP -.LP -Variables may be used interactively -to provide abbreviations for frequently -used strings. -For example, -.DS - b=/usr/fred/bin - mv pgm $b -.DE -will move the file \fIpgm\fP -from the current directory to the directory \fB/usr/fred/bin\|.\fR -A more general notation is available for parameter -(or variable) -substitution, as in, -.DS - echo ${user} -.DE -which is equivalent to -.DS - echo $user -.DE -and is used when the parameter name is -followed by a letter or digit. -For example, -.DS - tmp=/tmp/ps - ps a >${tmp}a -.DE -will direct the output of \fIps\fR -to the file \fB/tmp/psa,\fR -whereas, -.DS - ps a >$tmpa -.DE -would cause the value of the variable \fBtmpa\fP -to be substituted. -.LP -Except for \fB$?\fP the following -are set initially by the shell. -\fB$?\fP is set after executing each command. -.RS -.IP \fB$?\fP 8 -The exit status (return code) -of the last command executed -as a decimal string. -Most commands return a zero exit status -if they complete successfully, -otherwise a non-zero exit status is returned. -Testing the value of return codes is dealt with -later under \fBif\fP and \fBwhile\fP commands. -.IP \fB$#\fP 8 -The number of positional parameters -(in decimal). -Used, for example, in the \fIappend\fP command -to check the number of parameters. -.IP \fB$$\fP 8 -The process number of this shell (in decimal). -Since process numbers are unique among -all existing processes, this string is -frequently used to generate -unique -temporary file names. -For example, -.DS - ps a >/tmp/ps$$ - \*(ZZ - rm /tmp/ps$$ -.DE -.IP \fB$\|!\fP 8 -The process number of the last process -run in the background (in decimal). -.IP \fB$\(mi\fP 8 -The current shell flags, such as -\fB\(mix\fR and \fB\(miv\|.\fR -.RE -.LP -Some variables have a special meaning to the -shell and should be avoided for general -use. -.RS -.IP \fB$\s-1MAIL\s0\fP 8 -When used interactively -the shell looks at the file -specified by this variable -before it issues a prompt. -If the specified file has been modified -since it -was last looked at the shell -prints the message -\fIyou have mail\fP before prompting -for the next command. -This variable is typically set -in the file \fB.profile,\fP -in the user's login directory. -For example, -.DS - \s-1MAIL\s0=/usr/spool/mail/fred -.DE -.IP \fB$\s-1HOME\s0\fP 8 -The default argument -for the \fIcd\fP command. -The current directory is used to resolve -file name references that do not begin with -a \fB/\|,\fR -and is changed using the \fIcd\fP command. -For example, -.DS - cd /usr/fred/bin -.DE -makes the current directory \fB/usr/fred/bin\|.\fR -.DS - cat wn -.DE -will print on the terminal the file \fIwn\fP -in this directory. -The command -\fIcd\fP with no argument -is equivalent to -.DS - cd $\s-1HOME\s0 -.DE -This variable is also typically set in the -the user's login profile. -.IP \fB$\s-1PWD\s0\fP 8 -The current working directory. Set by the \fIcd\fB command. -.IP \fB$\s-1PATH\s0\fP 8 -A list of directories that contain commands (the \fIsearch path\fR\|). -Each time a command is executed by the shell -a list of directories is searched -for an executable file. -.ne 5 -If \fB$\s-1PATH\s0\fP is not set -then the current directory, -\fB/bin\fP, and \fB/usr/bin\fP are searched by default. -.ne 5 -Otherwise \fB$\s-1PATH\s0\fP consists of directory -names separated by \fB:\|.\fP -For example, -.DS - \s-1PATH\s0=\fB:\fP/usr/fred/bin\fB:\fP/bin\fB:\fP/usr/bin -.DE -specifies that the current directory -(the null string before the first \fB:\fP\|), -\fB/usr/fred/bin, /bin \fRand\fP /usr/bin\fR -are to be searched in that order. -In this way individual users -can have their own `private' commands -that are accessible independently -of the current directory. -If the command name contains a \fB/\fR then this directory search -is not used; a single attempt -is made to execute the command. -.IP \fB$\s-1PS1\s0\fP 8 -The primary shell prompt string, by default, `\fB$\ \fR'. -.IP \fB$\s-1PS2\s0\fP 8 -The shell prompt when further input is needed, -by default, `\fB>\ \fR'. -.IP \fB$\s-1IFS\s0\fP 8 -The set of characters used by \fIblank -interpretation\fR (see section 3.5). -.IP \fB$\s-1ENV\s0\fP 8 -The shell reads and executes the commands in the file -specified by this variable when it is initially started. -Unlike the \fB.profile\fP file, these commands are executed by all -shells, not just the one started at login. -(Most versions of the shell specify a filename that is used if -\s-1ENV\s0 is not explicitly set. See the manual page for your shell.) -.RE -.SH -2.5\ The\ test\ command -.LP -The \fItest\fP command, although not part of the shell, -is intended for use by shell programs. -For example, -.DS - test \(mif file -.DE -returns zero exit status if \fIfile\fP -exists and non-zero exit status otherwise. -In general \fItest\fP evaluates a predicate -and returns the result as its exit status. -Some of the more frequently used \fItest\fP -arguments are given here, see \fItest\fP(1) -for a complete specification. -.DS - test s true if the argument \fIs\fP is not the null string - test \(mif file true if \fIfile\fP exists - test \(mir file true if \fIfile\fP is readable - test \(miw file true if \fIfile\fP is writable - test \(mid file true if \fIfile\fP is a directory -.DE -The \fItest\fP command is known as `\fB[\fP' and may be invoked as -such. -For aesthetic reasons, the command ignores a close bracket `\fB]\fP' given -at the end of a command so -.DS - [ -f filename ] -.DE -and -.DS - test -f filename -.DE -are completely equivalent. -Typically, the bracket notation is used when \fItest\fP is invoked inside -shell control constructs. -.SH -2.6\ Control\ flow\ -\ while -.LP -The actions of -the \fBfor\fP loop and the \fBcase\fP -branch are determined by data available to the shell. -A \fBwhile\fP or \fBuntil\fP loop -and an \fBif then else\fP branch -are also provided whose -actions are determined by the exit status -returned by commands. -A \fBwhile\fP loop has the general form -.DS - \fBwhile\fP \fIcommand-list\*1\fP - \fBdo\fP \fIcommand-list\*2\fP - \fBdone\fP -.DE -.LP -The value tested by the \fBwhile\fP command -is the exit status of the last simple command -following \fBwhile.\fP -Each time round the loop -\fIcommand-list\*1\fP is executed; -if a zero exit status is returned then -\fIcommand-list\*2\fP -is executed; -otherwise, the loop terminates. -For example, -.DS - while [ $1 ] - do \*(ZZ - \*(DOshift - done -.DE -is equivalent to -.DS - for i - do \*(ZZ - done -.DE -\fIshift\fP is a shell command that -renames the positional parameters -\fB$2, $3, \*(ZZ\fR as \fB$1, $2, \*(ZZ\fR -and loses \fB$1\|.\fP -.LP -Another kind of use for the \fBwhile/until\fP -loop is to wait until some -external event occurs and then run -some commands. -In an \fBuntil\fP loop -the termination condition is reversed. -For example, -.DS - until [ \(mif file ] - do sleep 300; done - \fIcommands\fP -.DE -will loop until \fIfile\fP exists. -Each time round the loop it waits for -5 minutes before trying again. -(Presumably another process -will eventually create the file.) -.LP -The most recent enclosing loop may be exited with the \fBbreak\fP -command, or the rest of the body skipped and the next iteration begun -with the \fBcontinue\fP command. -.LP -The commands \fItrue\fP(1) and \fIfalse\fP(1) return 0 and non-zero -exit statuses respectively. They are sometimes of use in control flow, -e.g.: -.DS - while true - do date; sleep 5 - done -.DE -is an infinite loop that prints the date and time every five seconds. -.SH -2.7\ Control\ flow\ -\ if -.LP -Also available is a -general conditional branch -of the form, -.DS - \fBif\fP \fIcommand-list - \fBthen \fIcommand-list - \fBelse \fIcommand-list - \fBfi\fR -.DE -that tests the value returned by the last simple command -following \fBif.\fP -.LP -The \fBif\fP command may be used -in conjunction with the \fItest\fP command -to test for the existence of a file as in -.DS - if [ \(mif file ] - then \fIprocess file\fP - else \fIdo something else\fP - fi -.DE -.LP -An example of the use of \fBif, case\fP -and \fBfor\fP constructions is given in -section 2.10\|. -.LP -A multiple test \fBif\fP command -of the form -.DS - if \*(ZZ - then \*(ZZ - else if \*(ZZ - then \*(ZZ - else if \*(ZZ - \*(ZZ - fi - fi - fi -.DE -may be written using an extension of the \fBif\fP -notation as, -.DS - if \*(ZZ - then \*(ZZ - elif \*(ZZ - then \*(ZZ - elif \*(ZZ - \*(ZZ - fi -.DE -.LP -The following example is an implementation of the \fItouch\fP command -which changes the `last modified' time for a list -of files. -The command may be used in conjunction -with \fImake\fP(1) to force recompilation of a list -of files. -.DS - #!/bin/sh - - flag= - for i - do case $i in - \*(DC\(mic) flag=N ;; - \*(DC\*(ST) if [ \(mif $i ] - \*(DC then cp $i junk$$; mv junk$$ $i - \*(DC elif [ $flag ] - \*(DC then echo file \\'$i\\' does not exist - \*(DC else >$i - \*(DC fi - \*(DO esac - done -.DE -The \fB\(mic\fP flag is used in this command to -force subsequent files to be created if they do not already exist. -Otherwise, if the file does not exist, an error message is printed. -The shell variable \fIflag\fP -is set to some non-null string if the \fB\(mic\fP -argument is encountered. -The commands -.DS - cp \*(ZZ; mv \*(ZZ -.DE -copy the file and then overwrite it with the copy, -thus causing the last modified date to be updated. -.LP -The sequence -.DS - if command1 - then command2 - fi -.DE -may be written -.DS - command1 && command2 -.DE -Conversely, -.DS - command1 \*(VT\*(VT command2 -.DE -executes \fIcommand2\fP only if \fIcommand1\fP -fails. -In each case the value returned -is that of the last simple command executed. -.LP -Placing a `\fB!\fP' in front of a pipeline inverts its exit -status, almost in the manner of the C operator of the same name. -Thus: -.DS - if ! [ -d $1 ] - then - echo $1 is not a directory - fi -.DE -will print a message only if $1 is not a directory. -.SH -2.8\ Command\ grouping -.LP -Commands may be grouped in two ways, -.DS - \fB{\fI command-list\fB ; }\fR -.DE -and -.DS - \fB(\fI command-list\fB )\fR -.DE -.LP -In the first \fIcommand-list\fP is simply executed. -The second form executes \fIcommand-list\fP -as a separate process. -For example, -.DS - (cd x; rm junk ) -.DE -executes \fIrm junk\fP in the directory -\fBx\fP without changing the current -directory of the invoking shell. -.LP -The commands -.DS - cd x; rm junk -.DE -have the same effect but leave the invoking -shell in the directory \fBx.\fP -.SH -2.9\ Shell\ Functions -.LP -A function may be defined by the syntax -.DS - \fIfuncname\fP() \fB{\fI command-list\fB ; }\fR -.DE -Functions are invoked within a script as though they were separate -commands of the same name. -While they are executed, the -positional parameters \fB$1, $2, \*(ZZ\fR are temporarily set to the -arguments passed to the function. For example: -.DS - count() { - echo $2 : $# - } - - count a b c -.DE -would print `b : 3'. -.SH -2.10\ Debugging\ shell\ scripts -.LP -The shell provides two tracing mechanisms -to help when debugging shell scripts. -The first is invoked within the script -as -.DS - set \(miv -.DE -(\fBv\fP for verbose) and causes lines of the -script to be printed as they are read. -It is useful to help isolate syntax errors. -It may be invoked without modifying the script -by saying -.DS - sh \(miv \fIscript\fP \*(ZZ -.DE -where \fIscript\fP is the name of the shell script. -This flag may be used in conjunction -with the \fB\(min\fP flag which prevents -execution of subsequent commands. -(Note that saying \fIset \(min\fP at a terminal -will render the terminal useless -until an end-of-file is typed.) -.LP -The command -.DS - set \(mix -.DE -will produce an execution -trace. -Following parameter substitution -each command is printed as it is executed. -(Try these at the terminal to see -what effect they have.) -Both flags may be turned off by saying -.DS - set \(mi -.DE -and the current setting of the shell flags is available as \fB$\(mi\|\fR. -.SH -2.11\ The\ man\ command -.LP -The following is a simple implementation of the \fIman\fP command, -which is used to display sections of the UNIX manual on your terminal. -It is called, for example, as -.DS - man sh - man \(mit ed - man 2 fork -.DE -In the first the manual section for \fIsh\fP -is displayed.. -Since no section is specified, section 1 is used. -The second example will typeset (\fB\(mit\fP option) -the manual section for \fIed.\fP -The last prints the \fIfork\fP manual page -from section 2, which covers system calls. -.sp 2 -.DS - #!/bin/sh - - cd /usr/share/man - - # "#" is the comment character - # default is nroff ($N), section 1 ($s) - N=n\ s=1 - - for i - do case $i in -.sp .5 - \*(DC[1\(mi9]\*(ST) s=$i ;; -.sp .5 - \*(DC\(mit) N=t ;; -.sp .5 - \*(DC\(min) N=n ;; -.sp .5 - \*(DC\(mi\*(ST) echo unknown flag \\'$i\\' ;; -.sp .5 - \*(DC\*(ST) if [ \(mif man$s/$i.$s ] - \*(DC then - \*(DC ${N}roff \(miman man$s/$i.$s - \*(DC else # look through all manual sections - \*(DC found=no - \*(DC for j in 1 2 3 4 5 6 7 8 9 - \*(DC do - \*(DC \*(DOif [ \(mif man$j/$i.$j ] - \*(DC \*(DOthen - \*(DC \*(DO\*(THman $j $i - \*(DC \*(DO\*(THfound=yes - \*(DC \*(DO\*(THbreak - \*(DC \*(DOfi - \*(DC done - \*(DC case $found in - \*(DC \*(Cano) echo \\'$i: manual page not found\\' - \*(DC esac - \*(DC fi - \*(DOesac - done -.DE -.ce -.ft B -Figure 1. A version of the man command -.ft R diff --git a/bin/sh/USD.doc/t3 b/bin/sh/USD.doc/t3 deleted file mode 100644 index aab53ee..0000000 --- a/bin/sh/USD.doc/t3 +++ /dev/null @@ -1,976 +0,0 @@ -.\" $NetBSD: t3,v 1.3 2010/08/22 02:19:07 perry Exp $ -.\" -.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions are -.\" met: -.\" -.\" Redistributions of source code and documentation must retain the above -.\" copyright notice, this list of conditions and the following -.\" disclaimer. -.\" -.\" Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" -.\" This product includes software developed or owned by Caldera -.\" International, Inc. Neither the name of Caldera International, Inc. -.\" nor the names of other contributors may be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA -.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE -.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -.\" OR OTHERWISE) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" @(#)t3 8.1 (Berkeley) 6/8/93 -.\" -.SH -3.0\ Keyword\ parameters -.LP -Shell variables may be given values -by assignment -or when a shell script is invoked. -An argument to a command of the form -\fIname=value\fP -that precedes the command name -causes \fIvalue\fP -to be assigned to \fIname\fP -before execution of the command begins. -The value of \fIname\fP in the invoking -shell is not affected. -For example, -.DS - user=fred\ command -.DE -will execute \fIcommand\fP with -\fBuser\fP set to \fIfred\fP. -.\" Removed by Perry Metzger because -k is not in POSIX -.\" -.\" The \fB\(mik\fR flag causes arguments of the form -.\" \fIname=value\fP to be interpreted in this way -.\" anywhere in the argument list. -.\" Such \fInames\fP are sometimes -.\" called keyword parameters. -.\" If any arguments remain they -.\" are available as positional -.\" parameters \fB$1, $2, \*(ZZ\|.\fP -.LP -The \fIset\fP command -may also be used to set positional parameters -from within a script. -For example, -.DS - set\ \(mi\(mi\ \*(ST -.DE -will set \fB$1\fP to the first file name -in the current directory, \fB$2\fP to the next, -and so on. -Note that the first argument, \(mi\(mi, ensures correct treatment -when the first file name begins with a \(mi\|. -.LP -.SH -3.1\ Parameter\ transmission -.LP -When a command is executed both positional parameters -and shell variables may be set on invocation. -Variables are also made available implicitly -to a command -by specifying in advance that such parameters -are to be exported from the invoking shell. -For example, -.DS - export\ user\ box=red -.DE -marks the variables \fBuser\fP and \fBbox\fP -for export (setting \fBbox\fP to ``red'' in the process). -When a command is invoked -copies are made of all exportable variables -(also known as \fIenvironment variables\fP) -for use within the invoked program. -Modification of such variables -within an invoked command does not -affect the values in the invoking shell. -It is generally true of -a shell script or other program -that it -cannot modify the state -of its caller without explicit -actions on the part of the caller. -.\" Removed by Perry Metzger because this is confusing to beginners. -.\" -.\" (Shared file descriptors are an -.\" exception to this rule.) -.LP -Names whose value is intended to remain -constant may be declared \fIreadonly\|.\fP -The form of this command is the same as that of the \fIexport\fP -command, -.DS - readonly name[=value] \*(ZZ -.DE -Subsequent attempts to set readonly variables -are illegal. -.SH -3.2\ Parameter\ substitution -.LP -If a shell parameter is not set -then the null string is substituted for it. -For example, if the variable \fBd\fP -is not set -.DS - echo $d -.DE -or -.DS - echo ${d} -.DE -will echo nothing. -A default string may be given -as in -.DS - echo ${d:\(mi\fB.\fR} -.DE -which will echo -the value of the variable \fBd\fP -if it is set and not null and `\fB.\fP' otherwise. -The default string is evaluated using the usual -quoting conventions so that -.DS - echo ${d:\(mi\'\*(ST\'} -.DE -will echo \fB\*(ST\fP if the variable \fBd\fP -is not set or null. -Similarly -.DS - echo ${d:\(mi$1} -.DE -will echo the value of \fBd\fP if it is set and not null -and the value (if any) of \fB$1\fP otherwise. -.LP -The notation ${d:+\fB.\fR} performs the inverse operation. It -substitutes `\fB.\fP' if \fBd\fP is set or not null, and otherwise -substitutes null. -.LP -A variable may be assigned a default value -using -the notation -.DS - echo ${d:=\fB.\fR} -.DE -which substitutes the same string as -.DS - echo ${d:\(mi\fB.\fR} -.DE -and if \fBd\fP were not previously set or null -then it will be set to the string `\fB.\fP'\|. -.LP -If there is no sensible default then -the notation -.DS - echo ${d:?\fImessage\fP} -.DE -will echo the value of the variable \fBd\fP if it is set and not null, -otherwise \fImessage\fP is printed by the shell and -execution of the shell script is abandoned. -If \fImessage\fP is absent then a standard message -is printed. -A shell script that requires some variables -to be set might start as follows: -.DS - :\ ${user:?}\ ${acct:?}\ ${bin:?} - \*(ZZ -.DE -Colon (\fB:\fP) is a command -that is -built in to the shell and does nothing -once its arguments have been evaluated. -If any of the variables \fBuser, acct\fP -or \fBbin\fP are not set then the shell -will abandon execution of the script. -.SH -3.3\ Command\ substitution -.LP -The standard output from a command can be -substituted in a similar way to parameters. -The command \fIpwd\fP prints on its standard -output the name of the current directory. -For example, if the current directory is -\fB/usr/fred/bin\fR -then the commands -.DS - d=$(pwd) -.DE -(or the older notation d=\`pwd\`) -is equivalent to -.DS - d=/usr/fred/bin -.DE -.LP -The entire string inside $(\*(ZZ)\| (or between grave accents \`\*(ZZ\`) -is taken as the command -to be executed -and is replaced with the output from -the command. -(The difference between the $(\*(ZZ) and \`\*(ZZ\` notations is that -the former may be nested, while the latter cannot be.) -.LP -The command is written using the usual quoting conventions, -except that inside \`\*(ZZ\` -a \fB\`\fR must be escaped using -a \fB\\\|\fR. -For example, -.DS - ls $(echo "$HOME") -.DE -is equivalent to -.DS - ls $HOME -.DE -Command substitution occurs in all contexts -where parameter substitution occurs (including \fIhere\fP documents) and the -treatment of the resulting text is the same -in both cases. -This mechanism allows string -processing commands to be used within -shell scripts. -An example of such a command is \fIbasename\fP -which removes a specified suffix from a string. -For example, -.DS - basename main\fB.\fPc \fB.\fPc -.DE -will print the string \fImain\|.\fP -Its use is illustrated by the following -fragment from a \fIcc\fP command. -.DS - case $A in - \*(Ca\*(ZZ - \*(Ca\*(ST\fB.\fPc) B=$(basename $A \fB.\fPc) - \*(Ca\*(ZZ - esac -.DE -that sets \fBB\fP to the part of \fB$A\fP -with the suffix \fB.c\fP stripped. -.LP -Here are some composite examples. -.RS -.IP \(bu -.ft B -for i in \`ls \(mit\`; do \*(ZZ -.ft R -.br -The variable \fBi\fP is set -to the names of files in time order, -most recent first. -.IP \(bu -.ft B -set \(mi\(mi\| \`date\`; echo $6 $2 $3, $4 -.ft R -.br -will print, e.g., -.ft I -1977 Nov 1, 23:59:59 -.ft R -.RE -.SH -3.4\ Arithmetic\ Expansion -.LP -Within a $((\*(ZZ)) construct, integer arithmetic operations are -evaluated. -(The $ in front of variable names is optional within $((\*(ZZ)). -For example: -.DS - x=5; y=1 - echo $(($x+3*2)) - echo $((y+=x)) - echo $y -.DE -will print `11', then `6', then `6' again. -Most of the constructs permitted in C arithmetic operations are -permitted though some (like `++') are not universally supported \(em -see the shell manual page for details. -.SH -3.5\ Evaluation\ and\ quoting -.LP -The shell is a macro processor that -provides parameter substitution, command substitution and file -name generation for the arguments to commands. -This section discusses the order in which -these evaluations occur and the -effects of the various quoting mechanisms. -.LP -Commands are parsed initially according to the grammar -given in appendix A. -Before a command is executed -the following -substitutions occur. -.RS -.IP \(bu -parameter substitution, e.g. \fB$user\fP -.IP \(bu -command substitution, e.g. \fB$(pwd)\fP or \fB\`pwd\`\fP -.IP \(bu -arithmetic expansion, e.g. \fB$(($count+1))\fP -.RS -.LP -Only one evaluation occurs so that if, for example, the value of the variable -\fBX\fP -is the string \fI$y\fP -then -.DS - echo $X -.DE -will echo \fI$y\|.\fP -.RE -.IP \(bu -blank interpretation -.RS -.LP -Following the above substitutions -the resulting characters -are broken into non-blank words (\fIblank interpretation\fP). -For this purpose `blanks' are the characters of the string -\fB$\s-1IFS\s0\fP. -By default, this string consists of blank, tab and newline. -The null string -is not regarded as a word unless it is quoted. -For example, -.DS - echo \'\' -.DE -will pass on the null string as the first argument to \fIecho\fP, -whereas -.DS - echo $null -.DE -will call \fIecho\fR with no arguments -if the variable \fBnull\fP is not set -or set to the null string. -.RE -.IP \(bu -file name generation -.RS -.LP -Each word -is then scanned for the file pattern characters -\fB\*(ST, ?\fR and \fB[\*(ZZ]\fR -and an alphabetical list of file names -is generated to replace the word. -Each such file name is a separate argument. -.RE -.RE -.LP -The evaluations just described also occur -in the list of words associated with a \fBfor\fP -loop. -Only substitution occurs -in the \fIword\fP used -for a \fBcase\fP branch. -.LP -As well as the quoting mechanisms described -earlier using \fB\\\fR and \fB\'\*(ZZ\'\fR -a third quoting mechanism is provided using double quotes. -Within double quotes parameter and command substitution -occurs but file name generation and the interpretation -of blanks does not. -The following characters -have a special meaning within double quotes -and may be quoted using \fB\\\|.\fP -.DS - \fB$ \fPparameter substitution - \fB$()\fP command substitution - \fB\`\fP command substitution - \fB"\fP ends the quoted string - \fB\e\fP quotes the special characters \fB$ \` " \e\fP -.DE -For example, -.DS - echo "$x" -.DE -will pass the value of the variable \fBx\fP as a -single argument to \fIecho.\fP -Similarly, -.DS - echo "$\*(ST" -.DE -will pass the positional parameters as a single -argument and is equivalent to -.DS - echo "$1 $2 \*(ZZ" -.DE -The notation \fB$@\fP -is the same as \fB$\*(ST\fR -except when it is quoted. -.DS - echo "$@" -.DE -will pass the positional parameters, unevaluated, to \fIecho\fR -and is equivalent to -.DS - echo "$1" "$2" \*(ZZ -.DE -.LP -The following table gives, for each quoting mechanism, -the shell metacharacters that are evaluated. -.DS -.ce -.ft I -metacharacter -.ft -.in 1.5i - \e $ * \` " \' -\' n n n n n t -\` y n n t n n -" y y n y t n - - t terminator - y interpreted - n not interpreted - -.in -.ft B -.ce -Figure 2. Quoting mechanisms -.ft -.DE -.LP -In cases where more than one evaluation of a string -is required the built-in command \fIeval\fP -may be used. -For example, -if the variable \fBX\fP has the value -\fI$y\fP, and if \fBy\fP has the value \fIpqr\fP -then -.DS - eval echo $X -.DE -will echo the string \fIpqr\|.\fP -.LP -In general the \fIeval\fP command -evaluates its arguments (as do all commands) -and treats the result as input to the shell. -The input is read and the resulting command(s) -executed. -For example, -.DS - wg=\'eval who\*(VTgrep\' - $wg fred -.DE -is equivalent to -.DS - who\*(VTgrep fred -.DE -In this example, -\fIeval\fP is required -since there is no interpretation -of metacharacters, such as \fB\*(VT\|\fR, following -substitution. -.SH -3.6\ Error\ handling -.LP -The treatment of errors detected by -the shell depends on the type of error -and on whether the shell is being -used interactively. -An interactive shell is one whose -input and output are connected -to a terminal. -.\" Removed by Perry Metzger, obsolete and excess detail -.\" -.\" (as determined by -.\" \fIgtty\fP (2)). -A shell invoked with the \fB\(mii\fP -flag is also interactive. -.LP -Execution of a command (see also 3.7) may fail -for any of the following reasons. -.IP \(bu -Input output redirection may fail. -For example, if a file does not exist -or cannot be created. -.IP \(bu -The command itself does not exist -or cannot be executed. -.IP \(bu -The command terminates abnormally, -for example, with a "bus error" -or "memory fault". -See Figure 2 below for a complete list -of UNIX signals. -.IP \(bu -The command terminates normally -but returns a non-zero exit status. -.LP -In all of these cases the shell -will go on to execute the next command. -Except for the last case an error -message will be printed by the shell. -All remaining errors cause the shell -to exit from a script. -An interactive shell will return -to read another command from the terminal. -Such errors include the following. -.IP \(bu -Syntax errors. -e.g., if \*(ZZ then \*(ZZ done -.IP \(bu -A signal such as interrupt. -The shell waits for the current -command, if any, to finish execution and -then either exits or returns to the terminal. -.IP \(bu -Failure of any of the built-in commands -such as \fIcd.\fP -.LP -The shell flag \fB\(mie\fP -causes the shell to terminate -if any error is detected. -.DS -1 hangup -2 interrupt -3* quit -4* illegal instruction -5* trace trap -6* IOT instruction -7* EMT instruction -8* floating point exception -9 kill (cannot be caught or ignored) -10* bus error -11* segmentation violation -12* bad argument to system call -13 write on a pipe with no one to read it -14 alarm clock -15 software termination (from \fIkill\fP (1)) - -.DE -.ft B -.ce -Figure 3. UNIX signals\(dg -.ft -.FS -\(dg Additional signals have been added in modern Unix. -See \fIsigvec\fP(2) or \fIsignal\fP(3) for an up-to-date list. -.FE -Those signals marked with an asterisk -produce a core dump -if not caught. -However, -the shell itself ignores quit which is the only -external signal that can cause a dump. -The signals in this list of potential interest -to shell programs are 1, 2, 3, 14 and 15. -.SH -3.7\ Fault\ handling -.LP -shell scripts normally terminate -when an interrupt is received from the -terminal. -The \fItrap\fP command is used -if some cleaning up is required, such -as removing temporary files. -For example, -.DS - trap\ \'rm\ /tmp/ps$$; exit\'\ 2 -.DE -sets a trap for signal 2 (terminal -interrupt), and if this signal is received -will execute the commands -.DS - rm /tmp/ps$$; exit -.DE -\fIexit\fP is -another built-in command -that terminates execution of a shell script. -The \fIexit\fP is required; otherwise, -after the trap has been taken, -the shell will resume executing -the script -at the place where it was interrupted. -.LP -UNIX signals can be handled in one of three ways. -They can be ignored, in which case -the signal is never sent to the process. -They can be caught, in which case the process -must decide what action to take when the -signal is received. -Lastly, they can be left to cause -termination of the process without -it having to take any further action. -If a signal is being ignored -on entry to the shell script, for example, -by invoking it in the background (see 3.7) then \fItrap\fP -commands (and the signal) are ignored. -.LP -The use of \fItrap\fP is illustrated -by this modified version of the \fItouch\fP -command (Figure 4). -The cleanup action is to remove the file \fBjunk$$\fR\|. -.DS - #!/bin/sh - - flag= - trap\ \'rm\ \(mif\ junk$$;\ exit\'\ 1 2 3 15 - for i - do\ case\ $i\ in - \*(DC\(mic) flag=N ;; - \*(DC\*(ST) if\ test\ \(mif\ $i - \*(DC then cp\ $i\ junk$$;\ mv\ junk$$ $i - \*(DC elif\ test\ $flag - \*(DC then echo\ file\ \\'$i\\'\ does\ not\ exist - \*(DC else >$i - \*(DC fi - \*(DOesac - done -.DE -.sp -.ft B -.ce -Figure 4. The touch command -.ft -.sp -The \fItrap\fP command -appears before the creation -of the temporary file; -otherwise it would be -possible for the process -to die without removing -the file. -.LP -Since there is no signal 0 in UNIX -it is used by the shell to indicate the -commands to be executed on exit from the -shell script. -.LP -A script may, itself, elect to -ignore signals by specifying the null -string as the argument to trap. -The following fragment is taken from the -\fInohup\fP command. -.DS - trap \'\' 1 2 3 15 -.DE -which causes \fIhangup, interrupt, quit \fRand\fI kill\fR -to be ignored both by the -script and by invoked commands. -.LP -Traps may be reset by saying -.DS - trap 2 3 -.DE -which resets the traps for signals 2 and 3 to their default values. -A list of the current values of traps may be obtained -by writing -.DS - trap -.DE -.LP -The script \fIscan\fP (Figure 5) is an example -of the use of \fItrap\fP where there is no exit -in the trap command. -\fIscan\fP takes each directory -in the current directory, prompts -with its name, and then executes -commands typed at the terminal -until an end of file or an interrupt is received. -Interrupts are ignored while executing -the requested commands but cause -termination when \fIscan\fP is -waiting for input. -.DS - d=\`pwd\` - for\ i\ in\ \*(ST - do\ if\ test\ \(mid\ $d/$i - \*(DOthen\ cd\ $d/$i - \*(DO\*(THwhile\ echo\ "$i:" - \*(DO\*(TH\*(WHtrap\ exit\ 2 - \*(DO\*(TH\*(WHread\ x - \*(DO\*(THdo\ trap\ :\ 2;\ eval\ $x;\ done - \*(DOfi - done -.DE -.sp -.ft B -.ce -Figure 5. The scan command -.ft -.sp -\fIread x\fR is a built-in command that reads one line from the -standard input -and places the result in the variable \fBx\|.\fP -It returns a non-zero exit status if either -an end-of-file is read or an interrupt -is received. -.SH -3.8\ Command\ execution -.LP -To run a command (other than a built-in) the shell first creates -a new process using the system call \fIfork.\fP -The execution environment for the command -includes input, output and the states of signals, and -is established in the child process -before the command is executed. -The built-in command \fIexec\fP -is used in the rare cases when no fork -is required -and simply replaces the shell with a new command. -For example, a simple version of the \fInohup\fP -command looks like -.DS - trap \\'\\' 1 2 3 15 - exec $\*(ST -.DE -The \fItrap\fP turns off the signals specified -so that they are ignored by subsequently created commands -and \fIexec\fP replaces the shell by the command -specified. -.LP -Most forms of input output redirection have already been -described. -In the following \fIword\fP is only subject -to parameter and command substitution. -No file name generation or blank interpretation -takes place so that, for example, -.DS - echo \*(ZZ >\*(ST.c -.DE -will write its output into a file whose name is \fB\*(ST.c\|.\fP -Input output specifications are evaluated left to right -as they appear in the command. -.IP >\ \fIword\fP 12 -The standard output (file descriptor 1) -is sent to the file \fIword\fP which is -created if it does not already exist. -.IP \*(AP\ \fIword\fP 12 -The standard output is sent to file \fIword.\fP -If the file exists then output is appended -(by seeking to the end); -otherwise the file is created. -.IP <\ \fIword\fP 12 -The standard input (file descriptor 0) -is taken from the file \fIword.\fP -.IP \*(HE\ \fIword\fP 12 -The standard input is taken from the lines -of shell input that follow up to but not -including a line consisting only of \fIword.\fP -If \fIword\fP is quoted then no interpretation -of the document occurs. -If \fIword\fP is not quoted -then parameter and command substitution -occur and \fB\\\fP is used to quote -the characters \fB\\\fP \fB$\fP \fB\`\fP and the first character -of \fIword.\fP -In the latter case \fB\\newline\fP is ignored (c.f. quoted strings). -.IP >&\ \fIdigit\fP 12 -The file descriptor \fIdigit\fP is duplicated using the system -call \fIdup\fP (2) -and the result is used as the standard output. -.IP <&\ \fIdigit\fP 12 -The standard input is duplicated from file descriptor \fIdigit.\fP -.IP <&\(mi 12 -The standard input is closed. -.IP >&\(mi 12 -The standard output is closed. -.LP -Any of the above may be preceded by a digit in which -case the file descriptor created is that specified by the digit -instead of the default 0 or 1. -For example, -.DS - \*(ZZ 2>file -.DE -runs a command with message output (file descriptor 2) -directed to \fIfile.\fP -.DS - \*(ZZ 2>&1 -.DE -runs a command with its standard output and message output -merged. -(Strictly speaking file descriptor 2 is created -by duplicating file descriptor 1 but the effect is usually to -merge the two streams.) -.\" Removed by Perry Metzger, most of this is now obsolete -.\" -.\" .LP -.\" The environment for a command run in the background such as -.\" .DS -.\" list \*(ST.c \*(VT lpr & -.\" .DE -.\" is modified in two ways. -.\" Firstly, the default standard input -.\" for such a command is the empty file \fB/dev/null\|.\fR -.\" This prevents two processes (the shell and the command), -.\" which are running in parallel, from trying to -.\" read the same input. -.\" Chaos would ensue -.\" if this were not the case. -.\" For example, -.\" .DS -.\" ed file & -.\" .DE -.\" would allow both the editor and the shell -.\" to read from the same input at the same time. -.\" .LP -.\" The other modification to the environment of a background -.\" command is to turn off the QUIT and INTERRUPT signals -.\" so that they are ignored by the command. -.\" This allows these signals to be used -.\" at the terminal without causing background -.\" commands to terminate. -.\" For this reason the UNIX convention -.\" for a signal is that if it is set to 1 -.\" (ignored) then it is never changed -.\" even for a short time. -.\" Note that the shell command \fItrap\fP -.\" has no effect for an ignored signal. -.SH -3.9\ Invoking\ the\ shell -.LP -The following flags are interpreted by the shell -when it is invoked. -If the first character of argument zero is a minus, -then commands are read from the file \fB.profile\|.\fP -.IP \fB\(mic\fP\ \fIstring\fP -.br -If the \fB\(mic\fP flag is present then -commands are read from \fIstring\|.\fP -.IP \fB\(mis\fP -If the \fB\(mis\fP flag is present or if no -arguments remain -then commands are read from the standard input. -Shell output is written to -file descriptor 2. -.IP \fB\(mii\fP -If the \fB\(mii\fP flag is present or -if the shell input and output are attached to a terminal (as told by \fIgtty\fP) -then this shell is \fIinteractive.\fP -In this case TERMINATE is ignored (so that \fBkill 0\fP -does not kill an interactive shell) and INTERRUPT is caught and ignored -(so that \fBwait\fP is interruptable). -In all cases QUIT is ignored by the shell. -.SH -3.10\ Job\ Control -.LP -When a command or pipeline (also known as a \fIjob\fP) is running in -the foreground, entering the stop character (typically -\s-1CONTROL-Z\s0 but user settable with the \fIstty\fP(1) command) -will usually cause the job to stop. -.LP -The jobs associated with the current shell may be listed by entering -the \fIjobs\fP command. -Each job has an associated \fIjob number\fP. -Jobs that are stopped may be continued by entering -.DS - bg %\fIjobnumber\fP -.DE -and jobs may be moved to the foreground by entering -.DS - fg %\fIjobnumber\fP -.DE -If there is a sole job with a particular name (say only one instance -of \fIcc\fP running), \fIfg\fP and \fIbg\fP may also use name of the -command in place of the number, as in: -.DS - bg %cc -.DE -If no `\fB%\fP' clause is entered, most recently stopped job -(indicated with a `+' by the \fIjobs\fP command) will be assumed. -See the manual page for the shell for more details. -.SH -3.11\ Aliases -.LP -The \fIalias\fP command creates a so-called shell alias, which is an -abbreviation that macro-expands at run time into some other command. -For example: -.DS - alias ls="ls -F" -.DE -would cause the command sequence \fBls -F\fP to be executed whenever -the user types \fBls\fP into the shell. -Note that if the user types \fBls -a\fP, the shell will in fact -execute \fBls -F -a\fP. -The command \fBalias\fP on its own prints out all current aliases. -The \fIunalias\fP command, as in: -.DS - unalias ls -.DE -will remove an existing alias. -Aliases can shadow pre-existing commands, as in the example above. -They are typically used to override the interactive behavior of -commands in small ways, for example to always invoke some program with -a favorite option, and are almost never found in scripts. -.SH -3.12\ Command\ Line\ Editing\ and\ Recall -.LP -When working interactively with the shell, it is often tedious to -retype previously entered commands, especially if they are complicated. -The shell therefore maintains a so-called \fIhistory\fP, which is -stored in the file specified by the \fB\s-1HISTFILE\s0\fP environment -variable if it is set. -Users may view, edit, and re-enter previous lines of input using -a small subset of the commands of the \fIvi\fP(1) or -\fIemacs\fP(1)\(dg editors. -.FS -Technically, vi command editing is standardized by POSIX while emacs -is not. -However, all modern shells support both styles. -.FE -Emacs style editing may be selected by entering -.DS - set -o emacs -.DE -and vi style editing may be selected with -.DS - set -o vi -.DE -The details of how command line editing works are beyond the scope of -this document. -See the shell manual page for details. -.SH -Acknowledgements -.LP -The design of the shell is -based in part on the original UNIX shell -.[ -unix command language thompson -.] -and the PWB/UNIX shell, -.[ -pwb shell mashey unix -.] -some -features having been taken from both. -Similarities also exist with the -command interpreters -of the Cambridge Multiple Access System -.[ -cambridge multiple access system hartley -.] -and of CTSS. -.[ -ctss -.] -.LP -I would like to thank Dennis Ritchie -and John Mashey for many -discussions during the design of the shell. -I am also grateful to the members of the Computing Science Research Center -and to Joe Maranzano for their -comments on drafts of this document. -.SH -.[ -$LIST$ -.] diff --git a/bin/sh/USD.doc/t4 b/bin/sh/USD.doc/t4 deleted file mode 100644 index 7719d6c..0000000 --- a/bin/sh/USD.doc/t4 +++ /dev/null @@ -1,180 +0,0 @@ -.\" $NetBSD: t4,v 1.3 2010/08/22 02:19:07 perry Exp $ -.\" -.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions are -.\" met: -.\" -.\" Redistributions of source code and documentation must retain the above -.\" copyright notice, this list of conditions and the following -.\" disclaimer. -.\" -.\" Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" All advertising materials mentioning features or use of this software -.\" must display the following acknowledgement: -.\" -.\" This product includes software developed or owned by Caldera -.\" International, Inc. Neither the name of Caldera International, Inc. -.\" nor the names of other contributors may be used to endorse or promote -.\" products derived from this software without specific prior written -.\" permission. -.\" -.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA -.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE -.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -.\" OR OTHERWISE) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" @(#)t4 8.1 (Berkeley) 8/14/93 -.\" -.bp -.SH -Appendix\ A\ -\ Grammar -.LP -Note: This grammar needs updating, it is obsolete. -.LP -.LD -\fIitem: word - input-output - name = value -.sp 0.7 -simple-command: item - simple-command item -.sp 0.7 -command: simple-command - \fB( \fIcommand-list \fB) - \fB{ \fIcommand-list \fB} - \fBfor \fIname \fBdo \fIcommand-list \fBdone - \fBfor \fIname \fBin \fIword \*(ZZ \fBdo \fIcommand-list \fBdone - \fBwhile \fIcommand-list \fBdo \fIcommand-list \fBdone - \fBuntil \fIcommand-list \fBdo \fIcommand-list \fBdone - \fBcase \fIword \fBin \fIcase-part \*(ZZ \fBesac - \fBif \fIcommand-list \fBthen \fIcommand-list \fIelse-part \fBfi -.sp 0.7 -\fIpipeline: command - pipeline \fB\*(VT\fI command -.sp 0.7 -andor: pipeline - andor \fB&&\fI pipeline - andor \fB\*(VT\*(VT\fI pipeline -.sp 0.7 -command-list: andor - command-list \fB;\fI - command-list \fB&\fI - command-list \fB;\fI andor - command-list \fB&\fI andor -.sp 0.7 -input-output: \fB> \fIfile - \fB< \fIfile - \fB\*(AP \fIword - \fB\*(HE \fIword -.sp 0.7 -file: word - \fB&\fI digit - \fB&\fI \(mi -.sp 0.7 -case-part: pattern\fB ) \fIcommand-list\fB ;; -.sp 0.7 -\fIpattern: word - pattern \fB\*(VT\fI word -.sp 0.7 -\fIelse-part: \fBelif \fIcommand-list\fB then\fI command-list else-part\fP - \fBelse \fIcommand-list\fI - empty -.sp 0.7 -empty: -.sp 0.7 -word: \fRa sequence of non-blank characters\fI -.sp 0.7 -name: \fRa sequence of letters, digits or underscores starting with a letter\fI -.sp 0.7 -digit: \fB0 1 2 3 4 5 6 7 8 9\fP -.DE -.LP -.bp -.SH -Appendix\ B\ -\ Meta-characters\ and\ Reserved\ Words -.LP -a) syntactic -.RS -.IP \fB\*(VT\fR 6 -pipe symbol -.IP \fB&&\fR 6 -`andf' symbol -.IP \fB\*(VT\*(VT\fR 6 -`orf' symbol -.IP \fB;\fP 8 -command separator -.IP \fB;;\fP 8 -case delimiter -.IP \fB&\fP 8 -background commands -.IP \fB(\ )\fP 8 -command grouping -.IP \fB<\fP 8 -input redirection -.IP \fB\*(HE\fP 8 -input from a here document -.IP \fB>\fP 8 -output creation -.IP \fB\*(AP\fP 8 -output append -.sp 2 -.RE -.LP -b) patterns -.RS -.IP \fB\*(ST\fP 8 -match any character(s) including none -.IP \fB?\fP 8 -match any single character -.IP \fB[...]\fP 8 -match any of the enclosed characters -.sp 2 -.RE -.LP -c) substitution -.RS -.IP \fB${...}\fP 8 -substitute shell variable -.IP \fB$(...)\fP 8 -substitute command output -.IP \fB\`...\`\fP 8 -substitute command output -.IP \fB$((...))\fP 8 -substitute arithmetic expression -.sp 2 -.RE -.LP -d) quoting -.RS -.IP \fB\e\fP 8 -quote the next character -.IP \fB\'...\'\fP 8 -quote the enclosed characters except for \' -.IP \fB"\&..."\fP 8 -quote the enclosed characters except -for \fB$ \` \e "\fP -.sp 2 -.RE -.LP -e) reserved words -.DS -.ft B -if then else elif fi -case in esac -for while until do done -! { } -.ft -.DE diff --git a/bin/sh/alias.c b/bin/sh/alias.c deleted file mode 100644 index 2848b52..0000000 --- a/bin/sh/alias.c +++ /dev/null @@ -1,314 +0,0 @@ -/* $NetBSD: alias.c,v 1.20 2018/12/03 06:40:26 kre Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)alias.c 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: alias.c,v 1.20 2018/12/03 06:40:26 kre Exp $"); -#endif -#endif /* not lint */ - -#include <stdlib.h> -#include "shell.h" -#include "input.h" -#include "output.h" -#include "error.h" -#include "memalloc.h" -#include "mystring.h" -#include "alias.h" -#include "options.h" /* XXX for argptr (should remove?) */ -#include "builtins.h" -#include "var.h" - -#define ATABSIZE 39 - -struct alias *atab[ATABSIZE]; - -STATIC void setalias(char *, char *); -STATIC int by_name(const void *, const void *); -STATIC void list_aliases(void); -STATIC int unalias(char *); -STATIC struct alias **freealias(struct alias **, int); -STATIC struct alias **hashalias(const char *); -STATIC size_t countaliases(void); - -STATIC -void -setalias(char *name, char *val) -{ - struct alias *ap, **app; - - (void) unalias(name); /* old one (if any) is now gone */ - app = hashalias(name); - - INTOFF; - ap = ckmalloc(sizeof (struct alias)); - ap->name = savestr(name); - ap->flag = 0; - ap->val = savestr(val); - ap->next = *app; - *app = ap; - INTON; -} - -STATIC struct alias ** -freealias(struct alias **app, int force) -{ - struct alias *ap = *app; - - if (ap == NULL) - return app; - - /* - * if the alias is currently in use (i.e. its - * buffer is being used by the input routine) we - * just null out the name instead of discarding it. - * If we encounter it later, when it is idle, - * we will finish freeing it then. - * - * Unless we want to simply free everything (INIT) - */ - if (ap->flag & ALIASINUSE && !force) { - *ap->name = '\0'; - return &ap->next; - } - - INTOFF; - *app = ap->next; - ckfree(ap->name); - ckfree(ap->val); - ckfree(ap); - INTON; - - return app; -} - -STATIC int -unalias(char *name) -{ - struct alias *ap, **app; - - app = hashalias(name); - while ((ap = *app) != NULL) { - if (equal(name, ap->name)) { - (void) freealias(app, 0); - return 0; - } - app = &ap->next; - } - - return 1; -} - -#ifdef mkinit -MKINIT void rmaliases(int); - -SHELLPROC { - rmaliases(1); -} -#endif - -void -rmaliases(int force) -{ - struct alias **app; - int i; - - INTOFF; - for (i = 0; i < ATABSIZE; i++) { - app = &atab[i]; - while (*app) - app = freealias(app, force); - } - INTON; -} - -struct alias * -lookupalias(const char *name, int check) -{ - struct alias *ap = *hashalias(name); - - while (ap != NULL) { - if (equal(name, ap->name)) { - if (check && (ap->flag & ALIASINUSE)) - return NULL; - return ap; - } - ap = ap->next; - } - - return NULL; -} - -const char * -alias_text(void *dummy __unused, const char *name) -{ - struct alias *ap; - - ap = lookupalias(name, 0); - if (ap == NULL) - return NULL; - return ap->val; -} - -STATIC int -by_name(const void *a, const void *b) -{ - - return strcmp( - (*(const struct alias * const *)a)->name, - (*(const struct alias * const *)b)->name); -} - -STATIC void -list_aliases(void) -{ - size_t i, j, n; - const struct alias **aliases; - const struct alias *ap; - - INTOFF; - n = countaliases(); - aliases = ckmalloc(n * sizeof aliases[0]); - - j = 0; - for (i = 0; i < ATABSIZE; i++) - for (ap = atab[i]; ap != NULL; ap = ap->next) - if (ap->name[0] != '\0') - aliases[j++] = ap; - if (j != n) - error("Alias count botch"); - INTON; - - qsort(aliases, n, sizeof aliases[0], by_name); - - for (i = 0; i < n; i++) { - out1fmt("alias %s=", aliases[i]->name); - print_quoted(aliases[i]->val); - out1c('\n'); - } - - ckfree(aliases); -} - -/* - * Count how many aliases are defined (skipping any - * that have been deleted, but don't know it yet). - * Use this opportunity to clean up any of those - * zombies that are no longer needed. - */ -STATIC size_t -countaliases(void) -{ - struct alias *ap, **app; - size_t n; - int i; - - n = 0; - for (i = 0; i < ATABSIZE; i++) - for (app = &atab[i]; (ap = *app) != NULL;) { - if (ap->name[0] != '\0') - n++; - else { - app = freealias(app, 0); - continue; - } - app = &ap->next; - } - - return n; -} - -int -aliascmd(int argc, char **argv) -{ - char *n, *v; - int ret = 0; - struct alias *ap; - - if (argc == 1) { - list_aliases(); - return 0; - } - - while ((n = *++argv) != NULL) { - if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */ - if ((ap = lookupalias(n, 0)) == NULL) { - outfmt(out2, "alias: %s not found\n", n); - ret = 1; - } else { - out1fmt("alias %s=", n); - print_quoted(ap->val); - out1c('\n'); - } - } else { - *v++ = '\0'; - setalias(n, v); - } - } - - return ret; -} - -int -unaliascmd(int argc, char **argv) -{ - int i; - - while ((i = nextopt("a")) != '\0') { - if (i == 'a') { - rmaliases(0); - return 0; - } - } - - (void)countaliases(); /* delete any dead ones */ - for (i = 0; *argptr; argptr++) - i |= unalias(*argptr); - - return i; -} - -STATIC struct alias ** -hashalias(const char *p) -{ - unsigned int hashval; - - hashval = *(const unsigned char *)p << 4; - while (*p) - hashval += *p++; - return &atab[hashval % ATABSIZE]; -} diff --git a/bin/sh/alias.h b/bin/sh/alias.h deleted file mode 100644 index 390df6d..0000000 --- a/bin/sh/alias.h +++ /dev/null @@ -1,48 +0,0 @@ -/* $NetBSD: alias.h,v 1.9 2018/12/03 06:40:26 kre Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)alias.h 8.2 (Berkeley) 5/4/95 - */ - -#define ALIASINUSE 1 - -struct alias { - struct alias *next; - char *name; - char *val; - int flag; -}; - -struct alias *lookupalias(const char *, int); -const char *alias_text(void *, const char *); -void rmaliases(int); diff --git a/bin/sh/arith_token.c b/bin/sh/arith_token.c deleted file mode 100644 index cd91857..0000000 --- a/bin/sh/arith_token.c +++ /dev/null @@ -1,262 +0,0 @@ -/* $NetBSD: arith_token.c,v 1.7 2017/12/17 04:06:03 kre Exp $ */ - -/*- - * Copyright (c) 2002 - * Herbert Xu. - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * From FreeBSD, from dash - */ - -#include <sys/cdefs.h> - -#ifndef lint -__RCSID("$NetBSD: arith_token.c,v 1.7 2017/12/17 04:06:03 kre Exp $"); -#endif /* not lint */ - -#include <inttypes.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> - -#include "shell.h" -#include "arith_tokens.h" -#include "expand.h" -#include "error.h" -#include "memalloc.h" -#include "parser.h" -#include "syntax.h" -#include "show.h" - -#if ARITH_BOR + ARITH_ASS_GAP != ARITH_BORASS || \ - ARITH_ASS + ARITH_ASS_GAP != ARITH_EQ -#error Arithmetic tokens are out of order. -#endif - -/* - * Scan next arithmetic token, return its type, - * leave its value (when applicable) in (global) a_t_val. - * - * Input text is in (global) arith_buf which is updated to - * refer to the next char after the token returned, except - * on error (ARITH_BAD returned) where arith_buf is not altered. - */ -int -arith_token(void) -{ - int token; - const char *buf = arith_buf; - char *end; - const char *p; - - for (;;) { - token = *buf; - - if (isdigit(token)) { - /* - * Numbers all start with a digit, and nothing - * else does, the number ends wherever - * strtoimax() stops... - */ - a_t_val.val = strtoimax(buf, &end, 0); - if (is_in_name(*end)) { - token = *end; - while (is_in_name(*++end)) - continue; - error("arithmetic: unexpected '%c' " - "(out of range) in numeric constant: " - "%.*s", token, (int)(end - buf), buf); - } - arith_buf = end; - VTRACE(DBG_ARITH, ("Arith token ARITH_NUM=%jd\n", - a_t_val.val)); - return ARITH_NUM; - - } else if (is_name(token)) { - /* - * Variable names all start with an alpha (or '_') - * and nothing else does. They continue for the - * longest unbroken sequence of alphanumerics ( + _ ) - */ - arith_var_lno = arith_lno; - p = buf; - while (buf++, is_in_name(*buf)) - ; - a_t_val.name = stalloc(buf - p + 1); - memcpy(a_t_val.name, p, buf - p); - a_t_val.name[buf - p] = '\0'; - arith_buf = buf; - VTRACE(DBG_ARITH, ("Arith token ARITH_VAR=\"%s\"\n", - a_t_val.name)); - return ARITH_VAR; - - } else switch (token) { - /* - * everything else must be some kind of - * operator, white space, or an error. - */ - case '\n': - arith_lno++; - VTRACE(DBG_ARITH, ("Arith: newline\n")); - /* FALLTHROUGH */ - case ' ': - case '\t': - buf++; - continue; - - default: - error("arithmetic: unexpected '%c' (%#x) in expression", - token, token); - /* NOTREACHED */ - - case '=': - token = ARITH_ASS; - checkeq: - buf++; - checkeqcur: - if (*buf != '=') - goto out; - token += ARITH_ASS_GAP; - break; - - case '>': - switch (*++buf) { - case '=': - token = ARITH_GE; - break; - case '>': - token = ARITH_RSHIFT; - goto checkeq; - default: - token = ARITH_GT; - goto out; - } - break; - - case '<': - switch (*++buf) { - case '=': - token = ARITH_LE; - break; - case '<': - token = ARITH_LSHIFT; - goto checkeq; - default: - token = ARITH_LT; - goto out; - } - break; - - case '|': - if (*++buf != '|') { - token = ARITH_BOR; - goto checkeqcur; - } - token = ARITH_OR; - break; - - case '&': - if (*++buf != '&') { - token = ARITH_BAND; - goto checkeqcur; - } - token = ARITH_AND; - break; - - case '!': - if (*++buf != '=') { - token = ARITH_NOT; - goto out; - } - token = ARITH_NE; - break; - - case 0: - goto out; - - case '(': - token = ARITH_LPAREN; - break; - case ')': - token = ARITH_RPAREN; - break; - - case '*': - token = ARITH_MUL; - goto checkeq; - case '/': - token = ARITH_DIV; - goto checkeq; - case '%': - token = ARITH_REM; - goto checkeq; - - case '+': - if (buf[1] == '+') { - buf++; - token = ARITH_INCR; - break; - } - token = ARITH_ADD; - goto checkeq; - case '-': - if (buf[1] == '-') { - buf++; - token = ARITH_DECR; - break; - } - token = ARITH_SUB; - goto checkeq; - case '~': - token = ARITH_BNOT; - break; - case '^': - token = ARITH_BXOR; - goto checkeq; - - case '?': - token = ARITH_QMARK; - break; - case ':': - token = ARITH_COLON; - break; - case ',': - token = ARITH_COMMA; - break; - } - break; - } - buf++; - out: - arith_buf = buf; - VTRACE(DBG_ARITH, ("Arith token: %d\n", token)); - return token; -} diff --git a/bin/sh/arith_tokens.h b/bin/sh/arith_tokens.h deleted file mode 100644 index f655aa9..0000000 --- a/bin/sh/arith_tokens.h +++ /dev/null @@ -1,120 +0,0 @@ -/* $NetBSD: arith_tokens.h,v 1.3 2017/07/24 13:21:14 kre Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 2007 - * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * From FreeBSD who obtained it from dash (modified both times.) - */ - -/* - * Tokens returned from arith_token() - * - * Caution, values are not arbitrary. - */ - -#define ARITH_BAD 0 - -#define ARITH_ASS 1 - -#define ARITH_OR 2 -#define ARITH_AND 3 -#define ARITH_NUM 5 -#define ARITH_VAR 6 -#define ARITH_NOT 7 - -#define ARITH_BINOP_MIN 8 - -#define ARITH_LE 8 -#define ARITH_GE 9 -#define ARITH_LT 10 -#define ARITH_GT 11 -#define ARITH_EQ 12 /* Must be ARITH_ASS + ARITH_ASS_GAP */ - -#define ARITH_ASS_BASE 13 - -#define ARITH_REM 13 -#define ARITH_BAND 14 -#define ARITH_LSHIFT 15 -#define ARITH_RSHIFT 16 -#define ARITH_MUL 17 -#define ARITH_ADD 18 -#define ARITH_BOR 19 -#define ARITH_SUB 20 -#define ARITH_BXOR 21 -#define ARITH_DIV 22 - -#define ARITH_NE 23 - -#define ARITH_BINOP_MAX 24 - -#define ARITH_ASS_MIN ARITH_BINOP_MAX -#define ARITH_ASS_GAP (ARITH_ASS_MIN - ARITH_ASS_BASE) - -#define ARITH_REMASS (ARITH_ASS_GAP + ARITH_REM) -#define ARITH_BANDASS (ARITH_ASS_GAP + ARITH_BAND) -#define ARITH_LSHIFTASS (ARITH_ASS_GAP + ARITH_LSHIFT) -#define ARITH_RSHIFTASS (ARITH_ASS_GAP + ARITH_RSHIFT) -#define ARITH_MULASS (ARITH_ASS_GAP + ARITH_MUL) -#define ARITH_ADDASS (ARITH_ASS_GAP + ARITH_ADD) -#define ARITH_BORASS (ARITH_ASS_GAP + ARITH_BOR) -#define ARITH_SUBASS (ARITH_ASS_GAP + ARITH_SUB) -#define ARITH_BXORASS (ARITH_ASS_GAP + ARITH_BXOR) -#define ARITH_DIVASS (ARITH_ASS_GAP + ARITH_BXOR) - -#define ARITH_ASS_MAX 34 - -#define ARITH_LPAREN 34 -#define ARITH_RPAREN 35 -#define ARITH_BNOT 36 -#define ARITH_QMARK 37 -#define ARITH_COLON 38 -#define ARITH_INCR 39 -#define ARITH_DECR 40 -#define ARITH_COMMA 41 - -/* - * Globals shared between arith parser, and lexer - */ - -extern const char *arith_buf; - -union a_token_val { - intmax_t val; - char *name; -}; - -extern union a_token_val a_t_val; - -extern int arith_lno, arith_var_lno; - -int arith_token(void); diff --git a/bin/sh/arithmetic.c b/bin/sh/arithmetic.c deleted file mode 100644 index f9c91a4..0000000 --- a/bin/sh/arithmetic.c +++ /dev/null @@ -1,502 +0,0 @@ -/* $NetBSD: arithmetic.c,v 1.5 2018/04/21 23:01:29 kre Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 2007 - * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * From FreeBSD, who obtained it from dash, modified both times... - */ - -#include <sys/cdefs.h> - -#ifndef lint -__RCSID("$NetBSD: arithmetic.c,v 1.5 2018/04/21 23:01:29 kre Exp $"); -#endif /* not lint */ - -#include <limits.h> -#include <errno.h> -#include <inttypes.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include "shell.h" -#include "arithmetic.h" -#include "arith_tokens.h" -#include "expand.h" -#include "error.h" -#include "memalloc.h" -#include "output.h" -#include "options.h" -#include "var.h" -#include "show.h" -#include "syntax.h" - -#if ARITH_BOR + ARITH_ASS_GAP != ARITH_BORASS || \ - ARITH_ASS + ARITH_ASS_GAP != ARITH_EQ -#error Arithmetic tokens are out of order. -#endif - -static const char *arith_startbuf; - -const char *arith_buf; -union a_token_val a_t_val; - -static int last_token; - -int arith_lno, arith_var_lno; - -#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec - -static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = { - ARITH_PRECEDENCE(ARITH_MUL, 0), - ARITH_PRECEDENCE(ARITH_DIV, 0), - ARITH_PRECEDENCE(ARITH_REM, 0), - ARITH_PRECEDENCE(ARITH_ADD, 1), - ARITH_PRECEDENCE(ARITH_SUB, 1), - ARITH_PRECEDENCE(ARITH_LSHIFT, 2), - ARITH_PRECEDENCE(ARITH_RSHIFT, 2), - ARITH_PRECEDENCE(ARITH_LT, 3), - ARITH_PRECEDENCE(ARITH_LE, 3), - ARITH_PRECEDENCE(ARITH_GT, 3), - ARITH_PRECEDENCE(ARITH_GE, 3), - ARITH_PRECEDENCE(ARITH_EQ, 4), - ARITH_PRECEDENCE(ARITH_NE, 4), - ARITH_PRECEDENCE(ARITH_BAND, 5), - ARITH_PRECEDENCE(ARITH_BXOR, 6), - ARITH_PRECEDENCE(ARITH_BOR, 7), -}; - -#define ARITH_MAX_PREC 8 - -int expcmd(int, char **); - -static void __dead -arith_err(const char *s) -{ - error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); - /* NOTREACHED */ -} - -static intmax_t -arith_lookupvarint(char *varname) -{ - const char *str; - char *p; - intmax_t result; - const int oln = line_number; - - VTRACE(DBG_ARITH, ("Arith var lookup(\"%s\") with lno=%d\n", varname, - arith_var_lno)); - - line_number = arith_var_lno; - str = lookupvar(varname); - line_number = oln; - - if (uflag && str == NULL) - arith_err("variable not set"); - if (str == NULL || *str == '\0') - str = "0"; - errno = 0; - result = strtoimax(str, &p, 0); - if (errno != 0 || *p != '\0') { - if (errno == 0) { - while (*p != '\0' && is_space(*p)) - p++; - if (*p == '\0') - return result; - } - arith_err("variable contains non-numeric value"); - } - return result; -} - -static inline int -arith_prec(int op) -{ - - return prec[op - ARITH_BINOP_MIN]; -} - -static inline int -higher_prec(int op1, int op2) -{ - - return arith_prec(op1) < arith_prec(op2); -} - -static intmax_t -do_binop(int op, intmax_t a, intmax_t b) -{ - - VTRACE(DBG_ARITH, ("Arith do binop %d (%jd, %jd)\n", op, a, b)); - switch (op) { - default: - arith_err("token error"); - case ARITH_REM: - case ARITH_DIV: - if (b == 0) - arith_err("division by zero"); - if (a == INTMAX_MIN && b == -1) - arith_err("divide error"); - return op == ARITH_REM ? a % b : a / b; - case ARITH_MUL: - return (uintmax_t)a * (uintmax_t)b; - case ARITH_ADD: - return (uintmax_t)a + (uintmax_t)b; - case ARITH_SUB: - return (uintmax_t)a - (uintmax_t)b; - case ARITH_LSHIFT: - return (uintmax_t)a << (b & (sizeof(uintmax_t) * CHAR_BIT - 1)); - case ARITH_RSHIFT: - return a >> (b & (sizeof(uintmax_t) * CHAR_BIT - 1)); - case ARITH_LT: - return a < b; - case ARITH_LE: - return a <= b; - case ARITH_GT: - return a > b; - case ARITH_GE: - return a >= b; - case ARITH_EQ: - return a == b; - case ARITH_NE: - return a != b; - case ARITH_BAND: - return a & b; - case ARITH_BXOR: - return a ^ b; - case ARITH_BOR: - return a | b; - } -} - -static intmax_t assignment(int, int); -static intmax_t comma_list(int, int); - -static intmax_t -primary(int token, union a_token_val *val, int op, int noeval) -{ - intmax_t result; - char sresult[DIGITS(result) + 1]; - - VTRACE(DBG_ARITH, ("Arith primary: token %d op %d%s\n", - token, op, noeval ? " noeval" : "")); - - switch (token) { - case ARITH_LPAREN: - result = comma_list(op, noeval); - if (last_token != ARITH_RPAREN) - arith_err("expecting ')'"); - last_token = arith_token(); - return result; - case ARITH_NUM: - last_token = op; - return val->val; - case ARITH_VAR: - result = noeval ? val->val : arith_lookupvarint(val->name); - if (op == ARITH_INCR || op == ARITH_DECR) { - last_token = arith_token(); - if (noeval) - return val->val; - - snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, - result + (op == ARITH_INCR ? 1 : -1)); - setvar(val->name, sresult, 0); - } else - last_token = op; - return result; - case ARITH_ADD: - *val = a_t_val; - return primary(op, val, arith_token(), noeval); - case ARITH_SUB: - *val = a_t_val; - return -primary(op, val, arith_token(), noeval); - case ARITH_NOT: - *val = a_t_val; - return !primary(op, val, arith_token(), noeval); - case ARITH_BNOT: - *val = a_t_val; - return ~primary(op, val, arith_token(), noeval); - case ARITH_INCR: - case ARITH_DECR: - if (op != ARITH_VAR) - arith_err("incr/decr require var name"); - last_token = arith_token(); - if (noeval) - return val->val; - result = arith_lookupvarint(a_t_val.name); - snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, - result += (token == ARITH_INCR ? 1 : -1)); - setvar(a_t_val.name, sresult, 0); - return result; - default: - arith_err("expecting primary"); - } - return 0; /* never reached */ -} - -static intmax_t -binop2(intmax_t a, int op, int precedence, int noeval) -{ - union a_token_val val; - intmax_t b; - int op2; - int token; - - VTRACE(DBG_ARITH, ("Arith: binop2 %jd op %d (P:%d)%s\n", - a, op, precedence, noeval ? " noeval" : "")); - - for (;;) { - token = arith_token(); - val = a_t_val; - - b = primary(token, &val, arith_token(), noeval); - - op2 = last_token; - if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX && - higher_prec(op2, op)) { - b = binop2(b, op2, arith_prec(op), noeval); - op2 = last_token; - } - - a = noeval ? b : do_binop(op, a, b); - - if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX || - arith_prec(op2) >= precedence) - return a; - - op = op2; - } -} - -static intmax_t -binop(int token, union a_token_val *val, int op, int noeval) -{ - intmax_t a = primary(token, val, op, noeval); - - op = last_token; - if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX) - return a; - - return binop2(a, op, ARITH_MAX_PREC, noeval); -} - -static intmax_t -and(int token, union a_token_val *val, int op, int noeval) -{ - intmax_t a = binop(token, val, op, noeval); - intmax_t b; - - op = last_token; - if (op != ARITH_AND) - return a; - - VTRACE(DBG_ARITH, ("Arith: AND %jd%s\n", a, noeval ? " noeval" : "")); - - token = arith_token(); - *val = a_t_val; - - b = and(token, val, arith_token(), noeval | !a); - - return a && b; -} - -static intmax_t -or(int token, union a_token_val *val, int op, int noeval) -{ - intmax_t a = and(token, val, op, noeval); - intmax_t b; - - op = last_token; - if (op != ARITH_OR) - return a; - - VTRACE(DBG_ARITH, ("Arith: OR %jd%s\n", a, noeval ? " noeval" : "")); - - token = arith_token(); - *val = a_t_val; - - b = or(token, val, arith_token(), noeval | !!a); - - return a || b; -} - -static intmax_t -cond(int token, union a_token_val *val, int op, int noeval) -{ - intmax_t a = or(token, val, op, noeval); - intmax_t b; - intmax_t c; - - if (last_token != ARITH_QMARK) - return a; - - VTRACE(DBG_ARITH, ("Arith: ?: %jd%s\n", a, noeval ? " noeval" : "")); - - b = assignment(arith_token(), noeval | !a); - - if (last_token != ARITH_COLON) - arith_err("expecting ':'"); - - token = arith_token(); - *val = a_t_val; - - c = cond(token, val, arith_token(), noeval | !!a); - - return a ? b : c; -} - -static intmax_t -assignment(int var, int noeval) -{ - union a_token_val val = a_t_val; - int op = arith_token(); - intmax_t result; - char sresult[DIGITS(result) + 1]; - - - if (var != ARITH_VAR) - return cond(var, &val, op, noeval); - - if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX)) - return cond(var, &val, op, noeval); - - VTRACE(DBG_ARITH, ("Arith: %s ASSIGN %d%s\n", val.name, op, - noeval ? " noeval" : "")); - - result = assignment(arith_token(), noeval); - if (noeval) - return result; - - if (op != ARITH_ASS) - result = do_binop(op - ARITH_ASS_GAP, - arith_lookupvarint(val.name), result); - snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result); - setvar(val.name, sresult, 0); - return result; -} - -static intmax_t -comma_list(int token, int noeval) -{ - intmax_t result = assignment(token, noeval); - - while (last_token == ARITH_COMMA) { - VTRACE(DBG_ARITH, ("Arith: comma discarding %jd%s\n", result, - noeval ? " noeval" : "")); - result = assignment(arith_token(), noeval); - } - - return result; -} - -intmax_t -arith(const char *s, int lno) -{ - struct stackmark smark; - intmax_t result; - const char *p; - int nls = 0; - - setstackmark(&smark); - - arith_lno = lno; - - CTRACE(DBG_ARITH, ("Arith(\"%s\", %d) @%d\n", s, lno, arith_lno)); - - /* check if it is possible we might reference LINENO */ - p = s; - while ((p = strchr(p, 'L')) != NULL) { - if (p[1] == 'I' && p[2] == 'N') { - /* if it is possible, we need to correct airth_lno */ - p = s; - while ((p = strchr(p, '\n')) != NULL) - nls++, p++; - VTRACE(DBG_ARITH, ("Arith found %d newlines\n", nls)); - arith_lno -= nls; - break; - } - p++; - } - - arith_buf = arith_startbuf = s; - - result = comma_list(arith_token(), 0); - - if (last_token) - arith_err("expecting end of expression"); - - popstackmark(&smark); - - CTRACE(DBG_ARITH, ("Arith result=%jd\n", result)); - - return result; -} - -/* - * The let(1)/exp(1) builtin. - */ -int -expcmd(int argc, char **argv) -{ - const char *p; - char *concat; - char **ap; - intmax_t i; - - if (argc > 1) { - p = argv[1]; - if (argc > 2) { - /* - * Concatenate arguments. - */ - STARTSTACKSTR(concat); - ap = argv + 2; - for (;;) { - while (*p) - STPUTC(*p++, concat); - if ((p = *ap++) == NULL) - break; - STPUTC(' ', concat); - } - STPUTC('\0', concat); - p = grabstackstr(concat); - } - } else - p = ""; - - i = arith(p, line_number); - - out1fmt(ARITH_FORMAT_STR "\n", i); - return !i; -} diff --git a/bin/sh/arithmetic.h b/bin/sh/arithmetic.h deleted file mode 100644 index 51808d3..0000000 --- a/bin/sh/arithmetic.h +++ /dev/null @@ -1,42 +0,0 @@ -/* $NetBSD: arithmetic.h,v 1.2 2017/06/07 05:08:32 kre Exp $ */ - -/*- - * Copyright (c) 1995 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)arith.h 1.1 (Berkeley) 5/4/95 - * - * From FreeBSD, from dash - */ - -#include "shell.h" - -#define DIGITS(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3) - -#define ARITH_FORMAT_STR "%" PRIdMAX - -intmax_t arith(const char *, int); diff --git a/bin/sh/bltin/bltin.h b/bin/sh/bltin/bltin.h deleted file mode 100644 index b5669f1..0000000 --- a/bin/sh/bltin/bltin.h +++ /dev/null @@ -1,105 +0,0 @@ -/* $NetBSD: bltin.h,v 1.15 2017/06/26 22:09:16 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)bltin.h 8.1 (Berkeley) 5/31/93 - */ - -/* - * This file is included by programs which are optionally built into the - * shell. - * - * We always define SHELL_BUILTIN, to allow other included headers to - * hide some of their symbols if appropriate. - * - * If SHELL is defined, we try to map the standard UNIX library routines - * to ash routines using defines. - */ - -#define SHELL_BUILTIN -#include "../shell.h" -#include "../mystring.h" -#ifdef SHELL -#include "../output.h" -#include "../error.h" -#include "../var.h" -#undef stdout -#undef stderr -#undef putc -#undef putchar -#undef fileno -#undef ferror -#define FILE struct output -#define stdout out1 -#define stderr out2 -#ifdef __GNUC__ -#define _RETURN_INT(x) ({(x); 0;}) /* map from void foo() to int bar() */ -#else -#define _RETURN_INT(x) ((x), 0) /* map from void foo() to int bar() */ -#endif -#define fprintf(...) _RETURN_INT(outfmt(__VA_ARGS__)) -#define printf(...) _RETURN_INT(out1fmt(__VA_ARGS__)) -#define putc(c, file) _RETURN_INT(outc(c, file)) -#define putchar(c) _RETURN_INT(out1c(c)) -#define fputs(...) _RETURN_INT(outstr(__VA_ARGS__)) -#define fflush(f) _RETURN_INT(flushout(f)) -#define fileno(f) ((f)->fd) -#define ferror(f) ((f)->flags & OUTPUT_ERR) -#define INITARGS(argv) -#define err sh_err -#define verr sh_verr -#define errx sh_errx -#define verrx sh_verrx -#define warn sh_warn -#define vwarn sh_vwarn -#define warnx sh_warnx -#define vwarnx sh_vwarnx -#define exit sh_exit -#define setprogname(s) -#define getprogname() commandname -#define setlocate(l,s) 0 - -#define getenv(p) bltinlookup((p),0) - -#else /* ! SHELL */ -#undef NULL -#include <stdio.h> -#undef main -#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else -#endif /* ! SHELL */ - -pointer stalloc(int); - -int echocmd(int, char **); - - -extern const char *commandname; diff --git a/bin/sh/bltin/echo.1 b/bin/sh/bltin/echo.1 deleted file mode 100644 index 13b3c6a..0000000 --- a/bin/sh/bltin/echo.1 +++ /dev/null @@ -1,109 +0,0 @@ -.\" $NetBSD: echo.1,v 1.14 2017/07/03 21:33:24 wiz Exp $ -.\" -.\" Copyright (c) 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" This code is derived from software contributed to Berkeley by -.\" Kenneth Almquist. -.\" Copyright 1989 by Kenneth Almquist -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)echo.1 8.1 (Berkeley) 5/31/93 -.\" -.Dd May 31, 1993 -.Dt ECHO 1 -.Os -.Sh NAME -.Nm echo -.Nd produce message in a shell script -.Sh SYNOPSIS -.Nm -.Op Fl n | Fl e -.Ar args ... -.Sh DESCRIPTION -.Nm -prints its arguments on the standard output, separated by spaces. -Unless the -.Fl n -option is present, a newline is output following the arguments. -The -.Fl e -option causes -.Nm -to treat the escape sequences specially, as described in the following -paragraph. -The -.Fl e -option is the default, and is provided solely for compatibility with -other systems. -Only one of the options -.Fl n -and -.Fl e -may be given. -.Pp -If any of the following sequences of characters is encountered during -output, the sequence is not output. Instead, the specified action is -performed: -.Bl -tag -width indent -.It Li \eb -A backspace character is output. -.It Li \ec -Subsequent output is suppressed. This is normally used at the end of the -last argument to suppress the trailing newline that -.Nm -would otherwise output. -.It Li \ef -Output a form feed. -.It Li \en -Output a newline character. -.It Li \er -Output a carriage return. -.It Li \et -Output a (horizontal) tab character. -.It Li \ev -Output a vertical tab. -.It Li \e0 Ns Ar digits -Output the character whose value is given by zero to three digits. -If there are zero digits, a nul character is output. -.It Li \e\e -Output a backslash. -.El -.Sh HINTS -Remember that backslash is special to the shell and needs to be escaped. -To output a message to standard error, say -.Pp -.D1 echo message >&2 -.Sh BUGS -The octal character escape mechanism -.Pq Li \e0 Ns Ar digits -differs from the -C language mechanism. -.Pp -There is no way to force -.Nm -to treat its arguments literally, rather than interpreting them as -options and escape sequences. diff --git a/bin/sh/bltin/echo.c b/bin/sh/bltin/echo.c deleted file mode 100644 index 440e01b..0000000 --- a/bin/sh/bltin/echo.c +++ /dev/null @@ -1,122 +0,0 @@ -/* $NetBSD: echo.c,v 1.14 2008/10/12 01:40:37 dholland Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)echo.c 8.1 (Berkeley) 5/31/93 - */ - -/* - * Echo command. - * - * echo is steeped in tradition - several of them! - * netbsd has supported 'echo [-n | -e] args' in spite of -e not being - * documented anywhere. - * Posix requires that -n be supported, output from strings containing - * \ is implementation defined - * The Single Unix Spec requires that \ escapes be treated as if -e - * were set, but that -n not be treated as an option. - * (ksh supports 'echo [-eEn] args', but not -- so that it is actually - * impossible to actually output '-n') - * - * It is suggested that 'printf "%b" "string"' be used to get \ sequences - * expanded. printf is now a builtin of netbsd's sh and csh. - */ - -#include <sys/cdefs.h> -__RCSID("$NetBSD: echo.c,v 1.14 2008/10/12 01:40:37 dholland Exp $"); - -#define main echocmd - -#include "bltin.h" - -int -main(int argc, char **argv) -{ - char **ap; - char *p; - char c; - int count; - int nflag = 0; - int eflag = 0; - - ap = argv; - if (argc) - ap++; - - if ((p = *ap) != NULL) { - if (equal(p, "-n")) { - nflag = 1; - ap++; - } else if (equal(p, "-e")) { - eflag = 1; - ap++; - } - } - - while ((p = *ap++) != NULL) { - while ((c = *p++) != '\0') { - if (c == '\\' && eflag) { - switch (*p++) { - case 'a': c = '\a'; break; /* bell */ - case 'b': c = '\b'; break; - case 'c': return 0; /* exit */ - case 'e': c = 033; break; /* escape */ - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - case '\\': break; /* c = '\\' */ - case '0': - c = 0; - count = 3; - while (--count >= 0 && (unsigned)(*p - '0') < 8) - c = (c << 3) + (*p++ - '0'); - break; - default: - /* Output the '/' and char following */ - p--; - break; - } - } - putchar(c); - } - if (*ap) - putchar(' '); - } - if (! nflag) - putchar('\n'); - fflush(stdout); - if (ferror(stdout)) - return 1; - return 0; -} diff --git a/bin/sh/builtins.def b/bin/sh/builtins.def deleted file mode 100644 index d702707..0000000 --- a/bin/sh/builtins.def +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/sh - -# $NetBSD: builtins.def,v 1.25 2017/05/15 20:00:36 kre Exp $ -# -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)builtins.def 8.4 (Berkeley) 5/4/95 - -# -# This file lists all the builtin commands. The first column is the name -# of a C routine. -# The -j flag specifies that this command is to be excluded from systems -# without job control. -# The -h flag specifies that this command is to be excluded from systems -# based on the SMALL compile-time symbol. -# The -s flag specifies that this is a posix 'special builtin' command. -# The -u flag specifies that this is a posix 'standard utility'. -# The rest of the line specifies the command name or names used to run -# the command. - -bltincmd -u command -bgcmd -j -u bg -breakcmd -s break -s continue -cdcmd -u cd chdir -dotcmd -s . -echocmd echo -evalcmd -s eval -execcmd -s exec -exitcmd -s exit -expcmd exp let -exportcmd -s export -s readonly -falsecmd -u false -histcmd -h -u fc -inputrc inputrc -fgcmd -j -u fg -fgcmd_percent -j -u % -getoptscmd -u getopts -hashcmd hash -jobidcmd jobid -jobscmd -u jobs -localcmd local -#ifndef TINY -printfcmd printf -#endif -pwdcmd -u pwd -readcmd -u read -returncmd -s return -setcmd -s set -fdflagscmd fdflags -setvarcmd setvar -shiftcmd -s shift -timescmd -s times -trapcmd -s trap -truecmd -s : -u true -typecmd type -umaskcmd -u umask -unaliascmd -u unalias -unsetcmd -s unset -waitcmd -u wait -aliascmd -u alias -ulimitcmd ulimit -testcmd test [ -killcmd -u kill # mandated by posix for 'kill %job' -wordexpcmd wordexp -#newgrp -u newgrp # optional command in posix - -#ifdef DEBUG -debugcmd debug -#endif - -#exprcmd expr diff --git a/bin/sh/cd.c b/bin/sh/cd.c deleted file mode 100644 index 2b1046d..0000000 --- a/bin/sh/cd.c +++ /dev/null @@ -1,463 +0,0 @@ -/* $NetBSD: cd.c,v 1.50 2017/07/05 20:00:27 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: cd.c,v 1.50 2017/07/05 20:00:27 kre Exp $"); -#endif -#endif /* not lint */ - -#include <sys/types.h> -#include <sys/stat.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -/* - * The cd and pwd commands. - */ - -#include "shell.h" -#include "var.h" -#include "nodes.h" /* for jobs.h */ -#include "jobs.h" -#include "options.h" -#include "builtins.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "exec.h" -#include "redir.h" -#include "mystring.h" -#include "show.h" -#include "cd.h" - -STATIC int docd(const char *, int); -STATIC char *getcomponent(void); -STATIC void updatepwd(const char *); -STATIC void find_curdir(int noerror); - -char *curdir = NULL; /* current working directory */ -char *prevdir; /* previous working directory */ -STATIC char *cdcomppath; - -int -cdcmd(int argc, char **argv) -{ - const char *dest; - const char *path, *cp; - char *p; - char *d; - struct stat statb; - int print = cdprint; /* set -o cdprint to enable */ - - while (nextopt("P") != '\0') - ; - - /* - * Try (quite hard) to have 'curdir' defined, nothing has set - * it on entry to the shell, but we want 'cd fred; cd -' to work. - */ - getpwd(1); - dest = *argptr; - if (dest == NULL) { - dest = bltinlookup("HOME", 1); - if (dest == NULL) - error("HOME not set"); - } else if (argptr[1]) { - /* Do 'ksh' style substitution */ - if (!curdir) - error("PWD not set"); - p = strstr(curdir, dest); - if (!p) - error("bad substitution"); - d = stalloc(strlen(curdir) + strlen(argptr[1]) + 1); - memcpy(d, curdir, p - curdir); - strcpy(d + (p - curdir), argptr[1]); - strcat(d, p + strlen(dest)); - dest = d; - print = 1; - } else if (dest[0] == '-' && dest[1] == '\0') { - dest = prevdir ? prevdir : curdir; - print = 1; - } - if (*dest == '\0') - dest = "."; - - cp = dest; - if (*cp == '.' && *++cp == '.') - cp++; - if (*cp == 0 || *cp == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) - path = nullstr; - while ((p = padvance(&path, dest, 0)) != NULL) { - stunalloc(p); - if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { - int dopr = print; - - if (!print) { - /* - * XXX - rethink - */ - if (p[0] == '.' && p[1] == '/' && p[2] != '\0') - dopr = strcmp(p + 2, dest); - else - dopr = strcmp(p, dest); - } - if (docd(p, dopr) >= 0) - return 0; - - } - } - error("can't cd to %s", dest); - /* NOTREACHED */ -} - - -/* - * Actually do the chdir. In an interactive shell, print the - * directory name if "print" is nonzero. - */ - -STATIC int -docd(const char *dest, int print) -{ -#if 0 /* no "cd -L" (ever) so all this is just a waste of time ... */ - char *p; - char *q; - char *component; - struct stat statb; - int first; - int badstat; - - CTRACE(DBG_CMDS, ("docd(\"%s\", %d) called\n", dest, print)); - - /* - * Check each component of the path. If we find a symlink or - * something we can't stat, clear curdir to force a getcwd() - * next time we get the value of the current directory. - */ - badstat = 0; - cdcomppath = stalloc(strlen(dest) + 1); - scopy(dest, cdcomppath); - STARTSTACKSTR(p); - if (*dest == '/') { - STPUTC('/', p); - cdcomppath++; - } - first = 1; - while ((q = getcomponent()) != NULL) { - if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) - continue; - if (! first) - STPUTC('/', p); - first = 0; - component = q; - while (*q) - STPUTC(*q++, p); - if (equal(component, "..")) - continue; - STACKSTRNUL(p); - if (lstat(stackblock(), &statb) < 0) { - badstat = 1; - break; - } - } -#endif - - INTOFF; - if (chdir(dest) < 0) { - INTON; - return -1; - } - updatepwd(NULL); /* only do cd -P, no "pretend" -L mode */ - INTON; - if (print && iflag == 1 && curdir) - out1fmt("%s\n", curdir); - return 0; -} - - -/* - * Get the next component of the path name pointed to by cdcomppath. - * This routine overwrites the string pointed to by cdcomppath. - */ - -STATIC char * -getcomponent(void) -{ - char *p; - char *start; - - if ((p = cdcomppath) == NULL) - return NULL; - start = cdcomppath; - while (*p != '/' && *p != '\0') - p++; - if (*p == '\0') { - cdcomppath = NULL; - } else { - *p++ = '\0'; - cdcomppath = p; - } - return start; -} - - - -/* - * Update curdir (the name of the current directory) in response to a - * cd command. We also call hashcd to let the routines in exec.c know - * that the current directory has changed. - */ - -STATIC void -updatepwd(const char *dir) -{ - char *new; - char *p; - - hashcd(); /* update command hash table */ - - /* - * If our argument is NULL, we don't know the current directory - * any more because we traversed a symbolic link or something - * we couldn't stat(). - */ - if (dir == NULL || curdir == NULL) { - if (prevdir) - ckfree(prevdir); - INTOFF; - prevdir = curdir; - curdir = NULL; - getpwd(1); - INTON; - if (curdir) { - setvar("OLDPWD", prevdir, VEXPORT); - setvar("PWD", curdir, VEXPORT); - } else - unsetvar("PWD", 0); - return; - } - cdcomppath = stalloc(strlen(dir) + 1); - scopy(dir, cdcomppath); - STARTSTACKSTR(new); - if (*dir != '/') { - p = curdir; - while (*p) - STPUTC(*p++, new); - if (p[-1] == '/') - STUNPUTC(new); - } - while ((p = getcomponent()) != NULL) { - if (equal(p, "..")) { - while (new > stackblock() && (STUNPUTC(new), *new) != '/'); - } else if (*p != '\0' && ! equal(p, ".")) { - STPUTC('/', new); - while (*p) - STPUTC(*p++, new); - } - } - if (new == stackblock()) - STPUTC('/', new); - STACKSTRNUL(new); - INTOFF; - if (prevdir) - ckfree(prevdir); - prevdir = curdir; - curdir = savestr(stackblock()); - setvar("OLDPWD", prevdir, VEXPORT); - setvar("PWD", curdir, VEXPORT); - INTON; -} - -/* - * Posix says the default should be 'pwd -L' (as below), however - * the 'cd' command (above) does something much nearer to the - * posix 'cd -P' (not the posix default of 'cd -L'). - * If 'cd' is changed to support -P/L then the default here - * needs to be revisited if the historic behaviour is to be kept. - */ - -int -pwdcmd(int argc, char **argv) -{ - int i; - char opt = 'L'; - - while ((i = nextopt("LP")) != '\0') - opt = i; - if (*argptr) - error("unexpected argument"); - - if (opt == 'L') - getpwd(0); - else - find_curdir(0); - - setvar("OLDPWD", prevdir, VEXPORT); - setvar("PWD", curdir, VEXPORT); - out1str(curdir); - out1c('\n'); - return 0; -} - - - -void -initpwd(void) -{ - getpwd(1); - if (curdir) - setvar("PWD", curdir, VEXPORT); - else - sh_warnx("Cannot determine current working directory"); -} - -#define MAXPWD 256 - -/* - * Find out what the current directory is. If we already know the current - * directory, this routine returns immediately. - */ -void -getpwd(int noerror) -{ - char *pwd; - struct stat stdot, stpwd; - static int first = 1; - - if (curdir) - return; - - if (first) { - first = 0; - pwd = getenv("PWD"); - if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && - stat(pwd, &stpwd) != -1 && - stdot.st_dev == stpwd.st_dev && - stdot.st_ino == stpwd.st_ino) { - curdir = savestr(pwd); - return; - } - } - - find_curdir(noerror); - - return; -} - -STATIC void -find_curdir(int noerror) -{ - int i; - char *pwd; - - /* - * Things are a bit complicated here; we could have just used - * getcwd, but traditionally getcwd is implemented using popen - * to /bin/pwd. This creates a problem for us, since we cannot - * keep track of the job if it is being ran behind our backs. - * So we re-implement getcwd(), and we suppress interrupts - * throughout the process. This is not completely safe, since - * the user can still break out of it by killing the pwd program. - * We still try to use getcwd for systems that we know have a - * c implementation of getcwd, that does not open a pipe to - * /bin/pwd. - */ -#if defined(__NetBSD__) || defined(__SVR4) - - for (i = MAXPWD;; i *= 2) { - pwd = stalloc(i); - if (getcwd(pwd, i) != NULL) { - curdir = savestr(pwd); - stunalloc(pwd); - return; - } - stunalloc(pwd); - if (errno == ERANGE) - continue; - if (!noerror) - error("getcwd() failed: %s", strerror(errno)); - return; - } -#else - { - char *p; - int status; - struct job *jp; - int pip[2]; - - pwd = stalloc(MAXPWD); - INTOFF; - if (pipe(pip) < 0) - error("Pipe call failed"); - jp = makejob(NULL, 1); - if (forkshell(jp, NULL, FORK_NOJOB) == 0) { - (void) close(pip[0]); - movefd(pip[1], 1); - (void) execl("/bin/pwd", "pwd", (char *)0); - error("Cannot exec /bin/pwd"); - } - (void) close(pip[1]); - pip[1] = -1; - p = pwd; - while ((i = read(pip[0], p, pwd + MAXPWD - p)) > 0 - || (i == -1 && errno == EINTR)) { - if (i > 0) - p += i; - } - (void) close(pip[0]); - pip[0] = -1; - status = waitforjob(jp); - if (status != 0) - error((char *)0); - if (i < 0 || p == pwd || p[-1] != '\n') { - if (noerror) { - INTON; - return; - } - error("pwd command failed"); - } - p[-1] = '\0'; - INTON; - curdir = savestr(pwd); - stunalloc(pwd); - return; - } -#endif -} diff --git a/bin/sh/cd.h b/bin/sh/cd.h deleted file mode 100644 index 7600955..0000000 --- a/bin/sh/cd.h +++ /dev/null @@ -1,34 +0,0 @@ -/* $NetBSD: cd.h,v 1.6 2011/06/18 21:18:46 christos Exp $ */ - -/*- - * Copyright (c) 1995 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -void initpwd(void); -void getpwd(int); diff --git a/bin/sh/error.c b/bin/sh/error.c deleted file mode 100644 index db42926..0000000 --- a/bin/sh/error.c +++ /dev/null @@ -1,378 +0,0 @@ -/* $NetBSD: error.c,v 1.42 2019/01/21 14:29:12 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)error.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: error.c,v 1.42 2019/01/21 14:29:12 kre Exp $"); -#endif -#endif /* not lint */ - -/* - * Errors and exceptions. - */ - -#include <signal.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <stdio.h> -#include <string.h> - -#include "shell.h" -#include "eval.h" /* for commandname */ -#include "main.h" -#include "options.h" -#include "output.h" -#include "error.h" -#include "show.h" - - -/* - * Code to handle exceptions in C. - */ - -struct jmploc *handler; -int exception; -volatile int suppressint; -volatile int intpending; - - -static void exverror(int, const char *, va_list) __dead; - -/* - * Called to raise an exception. Since C doesn't include exceptions, we - * just do a longjmp to the exception handler. The type of exception is - * stored in the global variable "exception". - */ - -void -exraise(int e) -{ - CTRACE(DBG_ERRS, ("exraise(%d)\n", e)); - if (handler == NULL) - abort(); - exception = e; - longjmp(handler->loc, 1); -} - - -/* - * Called from trap.c when a SIGINT is received. (If the user specifies - * that SIGINT is to be trapped or ignored using the trap builtin, then - * this routine is not called.) Suppressint is nonzero when interrupts - * are held using the INTOFF macro. The call to _exit is necessary because - * there is a short period after a fork before the signal handlers are - * set to the appropriate value for the child. (The test for iflag is - * just defensive programming.) - */ - -void -onint(void) -{ - sigset_t nsigset; - - if (suppressint) { - intpending = 1; - return; - } - intpending = 0; - sigemptyset(&nsigset); - sigprocmask(SIG_SETMASK, &nsigset, NULL); - if (rootshell && iflag) - exraise(EXINT); - else { - signal(SIGINT, SIG_DFL); - raise(SIGINT); - } - /* NOTREACHED */ -} - -static __printflike(2, 0) void -exvwarning(int sv_errno, const char *msg, va_list ap) -{ - /* Partially emulate line buffered output so that: - * printf '%d\n' 1 a 2 - * and - * printf '%d %d %d\n' 1 a 2 - * both generate sensible text when stdout and stderr are merged. - */ - if (output.buf != NULL && output.nextc != output.buf && - output.nextc[-1] == '\n') - flushout(&output); - if (commandname) - outfmt(&errout, "%s: ", commandname); - else - outfmt(&errout, "%s: ", getprogname()); - if (msg != NULL) { - doformat(&errout, msg, ap); - if (sv_errno >= 0) - outfmt(&errout, ": "); - } - if (sv_errno >= 0) - outfmt(&errout, "%s", strerror(sv_errno)); - out2c('\n'); - flushout(&errout); -} - -/* - * Exverror is called to raise the error exception. If the second argument - * is not NULL then error prints an error message using printf style - * formatting. It then raises the error exception. - */ -static __printflike(2, 0) void -exverror(int cond, const char *msg, va_list ap) -{ - CLEAR_PENDING_INT; - INTOFF; - -#ifdef DEBUG - if (msg) { - CTRACE(DBG_ERRS, ("exverror(%d, \"", cond)); - CTRACEV(DBG_ERRS, (msg, ap)); - CTRACE(DBG_ERRS, ("\") pid=%d\n", getpid())); - } else - CTRACE(DBG_ERRS, ("exverror(%d, NULL) pid=%d\n", cond, - getpid())); -#endif - if (msg) - exvwarning(-1, msg, ap); - - flushall(); - exraise(cond); - /* NOTREACHED */ -} - - -void -error(const char *msg, ...) -{ - va_list ap; - - /* - * On error, we certainly never want exit(0)... - */ - if (exerrno == 0) - exerrno = 1; - va_start(ap, msg); - exverror(EXERROR, msg, ap); - /* NOTREACHED */ - va_end(ap); -} - - -void -exerror(int cond, const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - exverror(cond, msg, ap); - /* NOTREACHED */ - va_end(ap); -} - -/* - * error/warning routines for external builtins - */ - -void -sh_exit(int rval) -{ - exerrno = rval & 255; - exraise(EXEXEC); -} - -void -sh_err(int status, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - exvwarning(errno, fmt, ap); - va_end(ap); - sh_exit(status); -} - -void -sh_verr(int status, const char *fmt, va_list ap) -{ - exvwarning(errno, fmt, ap); - sh_exit(status); -} - -void -sh_errx(int status, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - exvwarning(-1, fmt, ap); - va_end(ap); - sh_exit(status); -} - -void -sh_verrx(int status, const char *fmt, va_list ap) -{ - exvwarning(-1, fmt, ap); - sh_exit(status); -} - -void -sh_warn(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - exvwarning(errno, fmt, ap); - va_end(ap); -} - -void -sh_vwarn(const char *fmt, va_list ap) -{ - exvwarning(errno, fmt, ap); -} - -void -sh_warnx(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - exvwarning(-1, fmt, ap); - va_end(ap); -} - -void -sh_vwarnx(const char *fmt, va_list ap) -{ - exvwarning(-1, fmt, ap); -} - - -/* - * Table of error messages. - */ - -struct errname { - short errcode; /* error number */ - short action; /* operation which encountered the error */ - const char *msg; /* text describing the error */ -}; - - -#define ALL (E_OPEN|E_CREAT|E_EXEC) - -STATIC const struct errname errormsg[] = { - { EINTR, ALL, "interrupted" }, - { EACCES, ALL, "permission denied" }, - { EIO, ALL, "I/O error" }, - { EEXIST, ALL, "file exists" }, - { ENOENT, E_OPEN, "no such file" }, - { ENOENT, E_CREAT,"directory nonexistent" }, - { ENOENT, E_EXEC, "not found" }, - { ENOTDIR, E_OPEN, "no such file" }, - { ENOTDIR, E_CREAT,"directory nonexistent" }, - { ENOTDIR, E_EXEC, "not found" }, - { EISDIR, ALL, "is a directory" }, -#ifdef EMFILE - { EMFILE, ALL, "too many open files" }, -#endif - { ENFILE, ALL, "file table overflow" }, - { ENOSPC, ALL, "file system full" }, -#ifdef EDQUOT - { EDQUOT, ALL, "disk quota exceeded" }, -#endif -#ifdef ENOSR - { ENOSR, ALL, "no streams resources" }, -#endif - { ENXIO, ALL, "no such device or address" }, - { EROFS, ALL, "read-only file system" }, - { ETXTBSY, ALL, "text busy" }, -#ifdef EAGAIN - { EAGAIN, E_EXEC, "not enough memory" }, -#endif - { ENOMEM, ALL, "not enough memory" }, -#ifdef ENOLINK - { ENOLINK, ALL, "remote access failed" }, -#endif -#ifdef EMULTIHOP - { EMULTIHOP, ALL, "remote access failed" }, -#endif -#ifdef ECOMM - { ECOMM, ALL, "remote access failed" }, -#endif -#ifdef ESTALE - { ESTALE, ALL, "remote access failed" }, -#endif -#ifdef ETIMEDOUT - { ETIMEDOUT, ALL, "remote access failed" }, -#endif -#ifdef ELOOP - { ELOOP, ALL, "symbolic link loop" }, -#endif -#ifdef ENAMETOOLONG - { ENAMETOOLONG, ALL, "file name too long" }, -#endif - { E2BIG, E_EXEC, "argument list too long" }, -#ifdef ELIBACC - { ELIBACC, E_EXEC, "shared library missing" }, -#endif - { 0, 0, NULL }, -}; - - -/* - * Return a string describing an error. The returned string may be a - * pointer to a static buffer that will be overwritten on the next call. - * Action describes the operation that got the error. - */ - -const char * -errmsg(int e, int action) -{ - struct errname const *ep; - static char buf[12]; - - for (ep = errormsg ; ep->errcode ; ep++) { - if (ep->errcode == e && (ep->action & action) != 0) - return ep->msg; - } - fmtstr(buf, sizeof buf, "error %d", e); - return buf; -} diff --git a/bin/sh/error.h b/bin/sh/error.h deleted file mode 100644 index 8dcf737..0000000 --- a/bin/sh/error.h +++ /dev/null @@ -1,120 +0,0 @@ -/* $NetBSD: error.h,v 1.21 2018/08/19 23:50:27 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)error.h 8.2 (Berkeley) 5/4/95 - */ - -#include <stdarg.h> - -/* - * Types of operations (passed to the errmsg routine). - */ - -#define E_OPEN 01 /* opening a file */ -#define E_CREAT 02 /* creating a file */ -#define E_EXEC 04 /* executing a program */ - - -/* - * We enclose jmp_buf in a structure so that we can declare pointers to - * jump locations. The global variable handler contains the location to - * jump to when an exception occurs, and the global variable exception - * contains a code identifying the exeception. To implement nested - * exception handlers, the user should save the value of handler on entry - * to an inner scope, set handler to point to a jmploc structure for the - * inner scope, and restore handler on exit from the scope. - */ - -#include <setjmp.h> - -struct jmploc { - jmp_buf loc; -}; - -extern struct jmploc *handler; -extern int exception; -extern int exerrno; /* error for EXEXEC */ - -/* exceptions */ -#define EXINT 0 /* SIGINT received */ -#define EXERROR 1 /* a generic error */ -#define EXSHELLPROC 2 /* execute a shell procedure */ -#define EXEXEC 3 /* command execution failed */ -#define EXEXIT 4 /* shell wants to exit(exitstatus) */ - - -/* - * These macros allow the user to suspend the handling of interrupt signals - * over a period of time. This is similar to SIGHOLD to or sigblock, but - * much more efficient and portable. (But hacking the kernel is so much - * more fun than worrying about efficiency and portability. :-)) - */ - -extern volatile int suppressint; -extern volatile int intpending; - -#define INTOFF suppressint++ -#define INTON do { if (--suppressint == 0 && intpending) onint(); } while (0) -#define FORCEINTON do { suppressint = 0; if (intpending) onint(); } while (0) -#define CLEAR_PENDING_INT (intpending = 0) -#define int_pending() intpending - -#if ! defined(SHELL_BUILTIN) -void exraise(int) __dead; -void onint(void); -void error(const char *, ...) __dead __printflike(1, 2); -void exerror(int, const char *, ...) __dead __printflike(2, 3); -const char *errmsg(int, int); -#endif /* ! SHELL_BUILTIN */ - -void sh_err(int, const char *, ...) __dead __printflike(2, 3); -void sh_verr(int, const char *, va_list) __dead __printflike(2, 0); -void sh_errx(int, const char *, ...) __dead __printflike(2, 3); -void sh_verrx(int, const char *, va_list) __dead __printflike(2, 0); -void sh_warn(const char *, ...) __printflike(1, 2); -void sh_vwarn(const char *, va_list) __printflike(1, 0); -void sh_warnx(const char *, ...) __printflike(1, 2); -void sh_vwarnx(const char *, va_list) __printflike(1, 0); - -void sh_exit(int) __dead; - - -/* - * BSD setjmp saves the signal mask, which violates ANSI C and takes time, - * so we use _setjmp instead. - */ - -#if defined(BSD) && !defined(__SVR4) -#define setjmp(jmploc) _setjmp(jmploc) -#define longjmp(jmploc, val) _longjmp(jmploc, val) -#endif diff --git a/bin/sh/eval.c b/bin/sh/eval.c deleted file mode 100644 index 2bda702..0000000 --- a/bin/sh/eval.c +++ /dev/null @@ -1,1680 +0,0 @@ -/* $NetBSD: eval.c,v 1.170 2019/01/21 14:18:59 kre Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; -#else -__RCSID("$NetBSD: eval.c,v 1.170 2019/01/21 14:18:59 kre Exp $"); -#endif -#endif /* not lint */ - -#include <stdbool.h> -#include <stdlib.h> -#include <signal.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <limits.h> -#include <unistd.h> -#include <sys/fcntl.h> -#include <sys/stat.h> -#include <sys/times.h> -#include <sys/param.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <sys/sysctl.h> - -/* - * Evaluate a command. - */ - -#include "shell.h" -#include "nodes.h" -#include "syntax.h" -#include "expand.h" -#include "parser.h" -#include "jobs.h" -#include "eval.h" -#include "builtins.h" -#include "options.h" -#include "exec.h" -#include "redir.h" -#include "input.h" -#include "output.h" -#include "trap.h" -#include "var.h" -#include "memalloc.h" -#include "error.h" -#include "show.h" -#include "mystring.h" -#include "main.h" -#ifndef SMALL -#include "nodenames.h" -#include "myhistedit.h" -#endif - - -STATIC struct skipsave s_k_i_p; -#define evalskip (s_k_i_p.state) -#define skipcount (s_k_i_p.count) - -STATIC int loopnest; /* current loop nesting level */ -STATIC int funcnest; /* depth of function calls */ -STATIC int builtin_flags; /* evalcommand flags for builtins */ -/* - * Base function nesting level inside a dot command. Set to 0 initially - * and to (funcnest + 1) before every dot command to enable - * 1) detection of being in a file sourced by a dot command and - * 2) counting of function nesting in that file for the implementation - * of the return command. - * The value is reset to its previous value after the dot command. - */ -STATIC int dot_funcnest; - - -const char *commandname; -struct strlist *cmdenviron; -int exitstatus; /* exit status of last command */ -int back_exitstatus; /* exit status of backquoted command */ - - -STATIC void evalloop(union node *, int); -STATIC void evalfor(union node *, int); -STATIC void evalcase(union node *, int); -STATIC void evalsubshell(union node *, int); -STATIC void expredir(union node *); -STATIC void evalredir(union node *, int); -STATIC void evalpipe(union node *); -STATIC void evalcommand(union node *, int, struct backcmd *); -STATIC void prehash(union node *); - -STATIC char *find_dot_file(char *); - -/* - * Called to reset things after an exception. - */ - -#ifdef mkinit -INCLUDE "eval.h" - -RESET { - reset_eval(); -} - -SHELLPROC { - exitstatus = 0; -} -#endif - -void -reset_eval(void) -{ - evalskip = SKIPNONE; - dot_funcnest = 0; - loopnest = 0; - funcnest = 0; -} - -static int -sh_pipe(int fds[2]) -{ - int nfd; - - if (pipe(fds)) - return -1; - - if (fds[0] < 3) { - nfd = fcntl(fds[0], F_DUPFD, 3); - if (nfd != -1) { - close(fds[0]); - fds[0] = nfd; - } - } - - if (fds[1] < 3) { - nfd = fcntl(fds[1], F_DUPFD, 3); - if (nfd != -1) { - close(fds[1]); - fds[1] = nfd; - } - } - return 0; -} - - -/* - * The eval commmand. - */ - -int -evalcmd(int argc, char **argv) -{ - char *p; - char *concat; - char **ap; - - if (argc > 1) { - p = argv[1]; - if (argc > 2) { - STARTSTACKSTR(concat); - ap = argv + 2; - for (;;) { - while (*p) - STPUTC(*p++, concat); - if ((p = *ap++) == NULL) - break; - STPUTC(' ', concat); - } - STPUTC('\0', concat); - p = grabstackstr(concat); - } - evalstring(p, builtin_flags & EV_TESTED); - } else - exitstatus = 0; - return exitstatus; -} - - -/* - * Execute a command or commands contained in a string. - */ - -void -evalstring(char *s, int flag) -{ - union node *n; - struct stackmark smark; - int last; - int any; - - last = flag & EV_EXIT; - flag &= ~EV_EXIT; - - setstackmark(&smark); - setinputstring(s, 1, line_number); - - any = 0; /* to determine if exitstatus will have been set */ - while ((n = parsecmd(0)) != NEOF) { - XTRACE(DBG_EVAL, ("evalstring: "), showtree(n)); - if (n && nflag == 0) { - if (last && at_eof()) - evaltree(n, flag | EV_EXIT); - else - evaltree(n, flag); - any = 1; - if (evalskip) - break; - } - rststackmark(&smark); - } - popfile(); - popstackmark(&smark); - if (!any) - exitstatus = 0; - if (last) - exraise(EXEXIT); -} - - - -/* - * Evaluate a parse tree. The value is left in the global variable - * exitstatus. - */ - -void -evaltree(union node *n, int flags) -{ - bool do_etest; - int sflags = flags & ~EV_EXIT; - union node *next; - struct stackmark smark; - - do_etest = false; - if (n == NULL || nflag) { - VTRACE(DBG_EVAL, ("evaltree(%s) called\n", - n == NULL ? "NULL" : "-n")); - if (nflag == 0) - exitstatus = 0; - goto out2; - } - - setstackmark(&smark); - do { -#ifndef SMALL - displayhist = 1; /* show history substitutions done with fc */ -#endif - next = NULL; - CTRACE(DBG_EVAL, ("pid %d, evaltree(%p: %s(%d), %#x) called\n", - getpid(), n, NODETYPENAME(n->type), n->type, flags)); - if (n->type != NCMD && traps_invalid) - free_traps(); - switch (n->type) { - case NSEMI: - evaltree(n->nbinary.ch1, sflags); - if (nflag || evalskip) - goto out1; - next = n->nbinary.ch2; - break; - case NAND: - evaltree(n->nbinary.ch1, EV_TESTED); - if (nflag || evalskip || exitstatus != 0) - goto out1; - next = n->nbinary.ch2; - break; - case NOR: - evaltree(n->nbinary.ch1, EV_TESTED); - if (nflag || evalskip || exitstatus == 0) - goto out1; - next = n->nbinary.ch2; - break; - case NREDIR: - evalredir(n, flags); - break; - case NSUBSHELL: - evalsubshell(n, flags); - do_etest = !(flags & EV_TESTED); - break; - case NBACKGND: - evalsubshell(n, flags); - break; - case NIF: { - evaltree(n->nif.test, EV_TESTED); - if (nflag || evalskip) - goto out1; - if (exitstatus == 0) - next = n->nif.ifpart; - else if (n->nif.elsepart) - next = n->nif.elsepart; - else - exitstatus = 0; - break; - } - case NWHILE: - case NUNTIL: - evalloop(n, sflags); - break; - case NFOR: - evalfor(n, sflags); - break; - case NCASE: - evalcase(n, sflags); - break; - case NDEFUN: - CTRACE(DBG_EVAL, ("Defining fn %s @%d%s\n", - n->narg.text, n->narg.lineno, - fnline1 ? " LINENO=1" : "")); - defun(n->narg.text, n->narg.next, n->narg.lineno); - exitstatus = 0; - break; - case NNOT: - evaltree(n->nnot.com, EV_TESTED); - exitstatus = !exitstatus; - break; - case NDNOT: - evaltree(n->nnot.com, EV_TESTED); - if (exitstatus != 0) - exitstatus = 1; - break; - case NPIPE: - evalpipe(n); - do_etest = !(flags & EV_TESTED); - break; - case NCMD: - evalcommand(n, flags, NULL); - do_etest = !(flags & EV_TESTED); - break; - default: -#ifdef NODETYPENAME - out1fmt("Node type = %d(%s)\n", - n->type, NODETYPENAME(n->type)); -#else - out1fmt("Node type = %d\n", n->type); -#endif - flushout(&output); - break; - } - n = next; - rststackmark(&smark); - } while(n != NULL); - out1: - popstackmark(&smark); - out2: - if (pendingsigs) - dotrap(); - if (eflag && exitstatus != 0 && do_etest) - exitshell(exitstatus); - if (flags & EV_EXIT) - exraise(EXEXIT); -} - - -STATIC void -evalloop(union node *n, int flags) -{ - int status; - - loopnest++; - status = 0; - - CTRACE(DBG_EVAL, ("evalloop %s:", NODETYPENAME(n->type))); - VXTRACE(DBG_EVAL, (" "), showtree(n->nbinary.ch1)); - VXTRACE(DBG_EVAL, ("evalloop do: "), showtree(n->nbinary.ch2)); - VTRACE(DBG_EVAL, ("evalloop done\n")); - CTRACE(DBG_EVAL, ("\n")); - - for (;;) { - evaltree(n->nbinary.ch1, EV_TESTED); - if (nflag) - break; - if (evalskip) { - skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { - evalskip = SKIPNONE; - continue; - } - if (evalskip == SKIPBREAK && --skipcount <= 0) - evalskip = SKIPNONE; - break; - } - if (n->type == NWHILE) { - if (exitstatus != 0) - break; - } else { - if (exitstatus == 0) - break; - } - evaltree(n->nbinary.ch2, flags & EV_TESTED); - status = exitstatus; - if (evalskip) - goto skipping; - } - loopnest--; - exitstatus = status; -} - - - -STATIC void -evalfor(union node *n, int flags) -{ - struct arglist arglist; - union node *argp; - struct strlist *sp; - struct stackmark smark; - int status; - - status = nflag ? exitstatus : 0; - - setstackmark(&smark); - arglist.lastp = &arglist.list; - for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { - expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); - if (evalskip) - goto out; - } - *arglist.lastp = NULL; - - loopnest++; - for (sp = arglist.list ; sp ; sp = sp->next) { - if (xflag) { - outxstr(expandstr(ps4val(), line_number)); - outxstr("for "); - outxstr(n->nfor.var); - outxc('='); - outxshstr(sp->text); - outxc('\n'); - flushout(outx); - } - - setvar(n->nfor.var, sp->text, 0); - evaltree(n->nfor.body, flags & EV_TESTED); - status = exitstatus; - if (nflag) - break; - if (evalskip) { - if (evalskip == SKIPCONT && --skipcount <= 0) { - evalskip = SKIPNONE; - continue; - } - if (evalskip == SKIPBREAK && --skipcount <= 0) - evalskip = SKIPNONE; - break; - } - } - loopnest--; - exitstatus = status; - out: - popstackmark(&smark); -} - - - -STATIC void -evalcase(union node *n, int flags) -{ - union node *cp, *ncp; - union node *patp; - struct arglist arglist; - struct stackmark smark; - int status = 0; - - setstackmark(&smark); - arglist.lastp = &arglist.list; - line_number = n->ncase.lineno; - expandarg(n->ncase.expr, &arglist, EXP_TILDE); - for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { - for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { - line_number = patp->narg.lineno; - if (casematch(patp, arglist.list->text)) { - while (cp != NULL && evalskip == 0 && - nflag == 0) { - if (cp->type == NCLISTCONT) - ncp = cp->nclist.next; - else - ncp = NULL; - line_number = cp->nclist.lineno; - evaltree(cp->nclist.body, flags); - status = exitstatus; - cp = ncp; - } - goto out; - } - } - } - out: - exitstatus = status; - popstackmark(&smark); -} - - - -/* - * Kick off a subshell to evaluate a tree. - */ - -STATIC void -evalsubshell(union node *n, int flags) -{ - struct job *jp= NULL; - int backgnd = (n->type == NBACKGND); - - expredir(n->nredir.redirect); - if (xflag && n->nredir.redirect) { - union node *rn; - - outxstr(expandstr(ps4val(), line_number)); - outxstr("using redirections:"); - for (rn = n->nredir.redirect; rn; rn = rn->nfile.next) - (void) outredir(outx, rn, ' '); - outxstr(" do subshell ("/*)*/); - if (backgnd) - outxstr(/*(*/") &"); - outxc('\n'); - flushout(outx); - } - if ((!backgnd && flags & EV_EXIT && !have_traps()) || - forkshell(jp = makejob(n, 1), n, backgnd?FORK_BG:FORK_FG) == 0) { - INTON; - if (backgnd) - flags &=~ EV_TESTED; - redirect(n->nredir.redirect, REDIR_KEEP); - evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ - } else if (!backgnd) { - INTOFF; - exitstatus = waitforjob(jp); - INTON; - } else - exitstatus = 0; - - if (!backgnd && xflag && n->nredir.redirect) { - outxstr(expandstr(ps4val(), line_number)); - outxstr(/*(*/") done subshell\n"); - flushout(outx); - } -} - - - -/* - * Compute the names of the files in a redirection list. - */ - -STATIC void -expredir(union node *n) -{ - union node *redir; - - for (redir = n ; redir ; redir = redir->nfile.next) { - struct arglist fn; - - fn.lastp = &fn.list; - switch (redir->type) { - case NFROMTO: - case NFROM: - case NTO: - case NCLOBBER: - case NAPPEND: - expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); - redir->nfile.expfname = fn.list->text; - break; - case NFROMFD: - case NTOFD: - if (redir->ndup.vname) { - expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); - fixredir(redir, fn.list->text, 1); - } - break; - } - } -} - -/* - * Perform redirections for a compound command, and then do it (and restore) - */ -STATIC void -evalredir(union node *n, int flags) -{ - struct jmploc jmploc; - struct jmploc * const savehandler = handler; - volatile int in_redirect = 1; - const char * volatile PS4 = NULL; - - expredir(n->nredir.redirect); - - if (xflag && n->nredir.redirect) { - union node *rn; - - outxstr(PS4 = expandstr(ps4val(), line_number)); - outxstr("using redirections:"); - for (rn = n->nredir.redirect; rn != NULL; rn = rn->nfile.next) - (void) outredir(outx, rn, ' '); - outxstr(" do {\n"); /* } */ - flushout(outx); - } - - if (setjmp(jmploc.loc)) { - int e; - - handler = savehandler; - e = exception; - popredir(); - if (PS4 != NULL) { - outxstr(PS4); - /* { */ outxstr("} failed\n"); - flushout(outx); - } - if (e == EXERROR || e == EXEXEC) { - if (in_redirect) { - exitstatus = 2; - return; - } - } - longjmp(handler->loc, 1); - } else { - INTOFF; - handler = &jmploc; - redirect(n->nredir.redirect, REDIR_PUSH | REDIR_KEEP); - in_redirect = 0; - INTON; - evaltree(n->nredir.n, flags); - } - INTOFF; - handler = savehandler; - popredir(); - INTON; - - if (PS4 != NULL) { - outxstr(PS4); - /* { */ outxstr("} done\n"); - flushout(outx); - } -} - - -/* - * Evaluate a pipeline. All the processes in the pipeline are children - * of the process creating the pipeline. (This differs from some versions - * of the shell, which make the last process in a pipeline the parent - * of all the rest.) - */ - -STATIC void -evalpipe(union node *n) -{ - struct job *jp; - struct nodelist *lp; - int pipelen; - int prevfd; - int pip[2]; - - CTRACE(DBG_EVAL, ("evalpipe(%p) called\n", n)); - pipelen = 0; - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) - pipelen++; - INTOFF; - jp = makejob(n, pipelen); - prevfd = -1; - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - prehash(lp->n); - pip[1] = -1; - if (lp->next) { - if (sh_pipe(pip) < 0) { - if (prevfd >= 0) - close(prevfd); - error("Pipe call failed: %s", strerror(errno)); - } - } - if (forkshell(jp, lp->n, - n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) { - INTON; - if (prevfd > 0) - movefd(prevfd, 0); - if (pip[1] >= 0) { - close(pip[0]); - movefd(pip[1], 1); - } - evaltree(lp->n, EV_EXIT); - } - if (prevfd >= 0) - close(prevfd); - prevfd = pip[0]; - close(pip[1]); - } - if (n->npipe.backgnd == 0) { - INTOFF; - exitstatus = waitforjob(jp); - CTRACE(DBG_EVAL, ("evalpipe: job done exit status %d\n", - exitstatus)); - INTON; - } else - exitstatus = 0; - INTON; -} - - - -/* - * Execute a command inside back quotes. If it's a builtin command, we - * want to save its output in a block obtained from malloc. Otherwise - * we fork off a subprocess and get the output of the command via a pipe. - * Should be called with interrupts off. - */ - -void -evalbackcmd(union node *n, struct backcmd *result) -{ - int pip[2]; - struct job *jp; - struct stackmark smark; /* unnecessary (because we fork) */ - - result->fd = -1; - result->buf = NULL; - result->nleft = 0; - result->jp = NULL; - - if (nflag || n == NULL) - goto out; - - setstackmark(&smark); - -#ifdef notyet - /* - * For now we disable executing builtins in the same - * context as the shell, because we are not keeping - * enough state to recover from changes that are - * supposed only to affect subshells. eg. echo "`cd /`" - */ - if (n->type == NCMD) { - exitstatus = oexitstatus; /* XXX o... no longer exists */ - evalcommand(n, EV_BACKCMD, result); - } else -#endif - { - INTOFF; - if (sh_pipe(pip) < 0) - error("Pipe call failed"); - jp = makejob(n, 1); - if (forkshell(jp, n, FORK_NOJOB) == 0) { - FORCEINTON; - close(pip[0]); - movefd(pip[1], 1); - eflag = 0; - evaltree(n, EV_EXIT); - /* NOTREACHED */ - } - close(pip[1]); - result->fd = pip[0]; - result->jp = jp; - INTON; - } - popstackmark(&smark); - out: - CTRACE(DBG_EVAL, ("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", - result->fd, result->buf, result->nleft, result->jp)); -} - -const char * -syspath(void) -{ - static char *sys_path = NULL; - static int mib[] = {CTL_USER, USER_CS_PATH}; - static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin"; - size_t len; - - if (sys_path == NULL) { - if (sysctl(mib, 2, 0, &len, 0, 0) != -1 && - (sys_path = ckmalloc(len + 5)) != NULL && - sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) { - memcpy(sys_path, "PATH=", 5); - } else { - ckfree(sys_path); - /* something to keep things happy */ - sys_path = def_path; - } - } - return sys_path; -} - -static int -parse_command_args(int argc, char **argv, int *use_syspath) -{ - int sv_argc = argc; - char *cp, c; - - *use_syspath = 0; - - for (;;) { - argv++; - if (--argc == 0) - break; - cp = *argv; - if (*cp++ != '-') - break; - if (*cp == '-' && cp[1] == 0) { - argv++; - argc--; - break; - } - while ((c = *cp++)) { - switch (c) { - case 'p': - *use_syspath = 1; - break; - default: - /* run 'typecmd' for other options */ - return 0; - } - } - } - return sv_argc - argc; -} - -int vforked = 0; -extern char *trap[]; - -/* - * Execute a simple command. - */ - -STATIC void -evalcommand(union node *cmd, int flgs, struct backcmd *backcmd) -{ - struct stackmark smark; - union node *argp; - struct arglist arglist; - struct arglist varlist; - volatile int flags = flgs; - char ** volatile argv; - volatile int argc; - char **envp; - int varflag; - struct strlist *sp; - volatile int mode; - int pip[2]; - struct cmdentry cmdentry; - struct job * volatile jp; - struct jmploc jmploc; - struct jmploc *volatile savehandler = NULL; - const char *volatile savecmdname; - volatile struct shparam saveparam; - struct localvar *volatile savelocalvars; - struct parsefile *volatile savetopfile; - volatile int e; - char * volatile lastarg; - const char * volatile path = pathval(); - volatile int temp_path; - const int savefuncline = funclinebase; - const int savefuncabs = funclineabs; - volatile int cmd_flags = 0; - - vforked = 0; - /* First expand the arguments. */ - CTRACE(DBG_EVAL, ("evalcommand(%p, %d) called [%s]\n", cmd, flags, - cmd->ncmd.args ? cmd->ncmd.args->narg.text : "")); - setstackmark(&smark); - back_exitstatus = 0; - - line_number = cmd->ncmd.lineno; - - arglist.lastp = &arglist.list; - varflag = 1; - /* Expand arguments, ignoring the initial 'name=value' ones */ - for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { - if (varflag && isassignment(argp->narg.text)) - continue; - varflag = 0; - line_number = argp->narg.lineno; - expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); - } - *arglist.lastp = NULL; - - expredir(cmd->ncmd.redirect); - - /* Now do the initial 'name=value' ones we skipped above */ - varlist.lastp = &varlist.list; - for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { - line_number = argp->narg.lineno; - if (!isassignment(argp->narg.text)) - break; - expandarg(argp, &varlist, EXP_VARTILDE); - } - *varlist.lastp = NULL; - - argc = 0; - for (sp = arglist.list ; sp ; sp = sp->next) - argc++; - argv = stalloc(sizeof (char *) * (argc + 1)); - - for (sp = arglist.list ; sp ; sp = sp->next) { - VTRACE(DBG_EVAL, ("evalcommand arg: %s\n", sp->text)); - *argv++ = sp->text; - } - *argv = NULL; - lastarg = NULL; - if (iflag && funcnest == 0 && argc > 0) - lastarg = argv[-1]; - argv -= argc; - - /* Print the command if xflag is set. */ - if (xflag) { - char sep = 0; - union node *rn; - - outxstr(expandstr(ps4val(), line_number)); - for (sp = varlist.list ; sp ; sp = sp->next) { - char *p; - - if (sep != 0) - outxc(sep); - - /* - * The "var=" part should not be quoted, regardless - * of the value, or it would not represent an - * assignment, but rather a command - */ - p = strchr(sp->text, '='); - if (p != NULL) { - *p = '\0'; /*XXX*/ - outxshstr(sp->text); - outxc('='); - *p++ = '='; /*XXX*/ - } else - p = sp->text; - outxshstr(p); - sep = ' '; - } - for (sp = arglist.list ; sp ; sp = sp->next) { - if (sep != 0) - outxc(sep); - outxshstr(sp->text); - sep = ' '; - } - for (rn = cmd->ncmd.redirect; rn; rn = rn->nfile.next) - if (outredir(outx, rn, sep)) - sep = ' '; - outxc('\n'); - flushout(outx); - } - - /* Now locate the command. */ - if (argc == 0) { - /* - * the empty command begins as a normal builtin, and - * remains that way while redirects are processed, then - * will become special before we get to doing the - * var assigns. - */ - cmdentry.cmdtype = CMDBUILTIN; - cmdentry.u.bltin = bltincmd; - } else { - static const char PATH[] = "PATH="; - - /* - * Modify the command lookup path, if a PATH= assignment - * is present - */ - for (sp = varlist.list; sp; sp = sp->next) - if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) - path = sp->text + sizeof(PATH) - 1; - - do { - int argsused, use_syspath; - - find_command(argv[0], &cmdentry, cmd_flags, path); -#if 0 - /* - * This short circuits all of the processing that - * should be done (including processing the - * redirects), so just don't ... - * - * (eventually this whole #if'd block will vanish) - */ - if (cmdentry.cmdtype == CMDUNKNOWN) { - exitstatus = 127; - flushout(&errout); - goto out; - } -#endif - - /* implement the 'command' builtin here */ - if (cmdentry.cmdtype != CMDBUILTIN || - cmdentry.u.bltin != bltincmd) - break; - cmd_flags |= DO_NOFUNC; - argsused = parse_command_args(argc, argv, &use_syspath); - if (argsused == 0) { - /* use 'type' builtin to display info */ - cmdentry.u.bltin = typecmd; - break; - } - argc -= argsused; - argv += argsused; - if (use_syspath) - path = syspath() + 5; - } while (argc != 0); - if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) - /* posix mandates that 'command <splbltin>' act as if - <splbltin> was a normal builtin */ - cmdentry.cmdtype = CMDBUILTIN; - } - - /* - * When traps are invalid, we permit the following: - * trap - * command trap - * eval trap - * command eval trap - * eval command trap - * without zapping the traps completely, in all other cases we do. - * - * The test here permits eval "anything" but when evalstring() comes - * back here again, the "anything" will be validated. - * This means we can actually do: - * eval eval eval command eval eval command trap - * as long as we end up with just "trap" - * - * We permit "command" by allowing CMDBUILTIN as well as CMDSPLBLTIN - * - * trapcmd() takes care of doing free_traps() if it is needed there. - */ - if (traps_invalid && - ((cmdentry.cmdtype!=CMDSPLBLTIN && cmdentry.cmdtype!=CMDBUILTIN) || - (cmdentry.u.bltin != trapcmd && cmdentry.u.bltin != evalcmd))) - free_traps(); - - /* Fork off a child process if necessary. */ - if (cmd->ncmd.backgnd || (have_traps() && (flags & EV_EXIT) != 0) - || ((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) - && (flags & EV_EXIT) == 0) - || ((flags & EV_BACKCMD) != 0 && - ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN) - || cmdentry.u.bltin == dotcmd - || cmdentry.u.bltin == evalcmd))) { - INTOFF; - jp = makejob(cmd, 1); - mode = cmd->ncmd.backgnd; - if (flags & EV_BACKCMD) { - mode = FORK_NOJOB; - if (sh_pipe(pip) < 0) - error("Pipe call failed"); - } -#ifdef DO_SHAREDVFORK - /* It is essential that if DO_SHAREDVFORK is defined that the - * child's address space is actually shared with the parent as - * we rely on this. - */ - if (usefork == 0 && cmdentry.cmdtype == CMDNORMAL) { - pid_t pid; - int serrno; - - savelocalvars = localvars; - localvars = NULL; - vforked = 1; - VFORK_BLOCK - switch (pid = vfork()) { - case -1: - serrno = errno; - VTRACE(DBG_EVAL, ("vfork() failed, errno=%d\n", - serrno)); - INTON; - error("Cannot vfork (%s)", strerror(serrno)); - break; - case 0: - /* Make sure that exceptions only unwind to - * after the vfork(2) - */ - SHELL_FORKED(); - if (setjmp(jmploc.loc)) { - if (exception == EXSHELLPROC) { - /* - * We can't progress with the - * vfork, so, set vforked = 2 - * so the parent knows, - * and _exit(); - */ - vforked = 2; - _exit(0); - } else { - _exit(exception == EXEXIT ? - exitstatus : exerrno); - } - } - savehandler = handler; - handler = &jmploc; - listmklocal(varlist.list, - VDOEXPORT | VEXPORT | VNOFUNC); - forkchild(jp, cmd, mode, vforked); - break; - default: - VFORK_UNDO(); - /* restore from vfork(2) */ - handler = savehandler; - poplocalvars(); - localvars = savelocalvars; - if (vforked == 2) { - vforked = 0; - - (void)waitpid(pid, NULL, 0); - /* - * We need to progress in a - * normal fork fashion - */ - goto normal_fork; - } - /* - * Here the child has left home, - * getting on with its life, so - * so must we... - */ - vforked = 0; - forkparent(jp, cmd, mode, pid); - goto parent; - } - VFORK_END - } else { - normal_fork: -#endif - if (forkshell(jp, cmd, mode) != 0) - goto parent; /* at end of routine */ - flags |= EV_EXIT; - FORCEINTON; -#ifdef DO_SHAREDVFORK - } -#endif - if (flags & EV_BACKCMD) { - if (!vforked) { - FORCEINTON; - } - close(pip[0]); - movefd(pip[1], 1); - } - flags |= EV_EXIT; - } - - /* This is the child process if a fork occurred. */ - /* Execute the command. */ - switch (cmdentry.cmdtype) { - volatile int saved; - - case CMDFUNCTION: - VXTRACE(DBG_EVAL, ("Shell function%s: ",vforked?" VF":""), - trargs(argv)); - redirect(cmd->ncmd.redirect, saved = - !(flags & EV_EXIT) || have_traps() ? REDIR_PUSH : 0); - saveparam = shellparam; - shellparam.malloc = 0; - shellparam.reset = 1; - shellparam.nparam = argc - 1; - shellparam.p = argv + 1; - shellparam.optnext = NULL; - INTOFF; - savelocalvars = localvars; - localvars = NULL; - reffunc(cmdentry.u.func); - INTON; - if (setjmp(jmploc.loc)) { - if (exception == EXSHELLPROC) { - freeparam((volatile struct shparam *) - &saveparam); - } else { - freeparam(&shellparam); - shellparam = saveparam; - } - if (saved) - popredir();; - unreffunc(cmdentry.u.func); - poplocalvars(); - localvars = savelocalvars; - funclinebase = savefuncline; - funclineabs = savefuncabs; - handler = savehandler; - longjmp(handler->loc, 1); - } - savehandler = handler; - handler = &jmploc; - if (cmdentry.u.func) { - if (cmdentry.lno_frel) - funclinebase = cmdentry.lineno - 1; - else - funclinebase = 0; - funclineabs = cmdentry.lineno; - - VTRACE(DBG_EVAL, - ("function: node: %d '%s' # %d%s; funclinebase=%d\n", - getfuncnode(cmdentry.u.func)->type, - NODETYPENAME(getfuncnode(cmdentry.u.func)->type), - cmdentry.lineno, cmdentry.lno_frel?" (=1)":"", - funclinebase)); - } - listmklocal(varlist.list, VDOEXPORT | VEXPORT); - /* stop shell blowing its stack */ - if (++funcnest > 1000) - error("too many nested function calls"); - evaltree(getfuncnode(cmdentry.u.func), - flags & (EV_TESTED|EV_EXIT)); - funcnest--; - INTOFF; - unreffunc(cmdentry.u.func); - poplocalvars(); - localvars = savelocalvars; - funclinebase = savefuncline; - funclineabs = savefuncabs; - freeparam(&shellparam); - shellparam = saveparam; - handler = savehandler; - if (saved) - popredir(); - INTON; - if (evalskip == SKIPFUNC) { - evalskip = SKIPNONE; - skipcount = 0; - } - if (flags & EV_EXIT) - exitshell(exitstatus); - break; - - case CMDSPLBLTIN: - VTRACE(DBG_EVAL, ("special ")); - case CMDBUILTIN: - VXTRACE(DBG_EVAL, ("builtin command [%d]%s: ", argc, - vforked ? " VF" : ""), trargs(argv)); - mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; - if (flags == EV_BACKCMD) { - memout.nleft = 0; - memout.nextc = memout.buf; - memout.bufsize = 64; - mode |= REDIR_BACKQ; - } - e = -1; - savecmdname = commandname; - savetopfile = getcurrentfile(); - savehandler = handler; - temp_path = 0; - if (!setjmp(jmploc.loc)) { - handler = &jmploc; - - /* - * We need to ensure the command hash table isn't - * corrupted by temporary PATH assignments. - * However we must ensure the 'local' command works! - */ - if (path != pathval() && (cmdentry.u.bltin == hashcmd || - cmdentry.u.bltin == typecmd)) { - savelocalvars = localvars; - localvars = 0; - temp_path = 1; - mklocal(path - 5 /* PATH= */, 0); - } - redirect(cmd->ncmd.redirect, mode); - - /* - * the empty command is regarded as a normal - * builtin for the purposes of redirects, but - * is a special builtin for var assigns. - * (unless we are the "command" command.) - */ - if (argc == 0 && !(cmd_flags & DO_NOFUNC)) - cmdentry.cmdtype = CMDSPLBLTIN; - - /* exec is a special builtin, but needs this list... */ - cmdenviron = varlist.list; - /* we must check 'readonly' flag for all builtins */ - listsetvar(varlist.list, - cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); - commandname = argv[0]; - /* initialize nextopt */ - argptr = argv + 1; - optptr = NULL; - /* and getopt */ - optreset = 1; - optind = 1; - builtin_flags = flags; - exitstatus = cmdentry.u.bltin(argc, argv); - } else { - e = exception; - if (e == EXINT) - exitstatus = SIGINT + 128; - else if (e == EXEXEC) - exitstatus = exerrno; - else if (e != EXEXIT) - exitstatus = 2; - } - handler = savehandler; - flushall(); - out1 = &output; - out2 = &errout; - freestdout(); - if (temp_path) { - poplocalvars(); - localvars = savelocalvars; - } - cmdenviron = NULL; - if (e != EXSHELLPROC) { - commandname = savecmdname; - if (flags & EV_EXIT) - exitshell(exitstatus); - } - if (e != -1) { - if ((e != EXERROR && e != EXEXEC) - || cmdentry.cmdtype == CMDSPLBLTIN) - exraise(e); - popfilesupto(savetopfile); - FORCEINTON; - } - if (cmdentry.u.bltin != execcmd) - popredir(); - if (flags == EV_BACKCMD) { - backcmd->buf = memout.buf; - backcmd->nleft = memout.nextc - memout.buf; - memout.buf = NULL; - } - break; - - default: - VXTRACE(DBG_EVAL, ("normal command%s: ", vforked?" VF":""), - trargs(argv)); - redirect(cmd->ncmd.redirect, - (vforked ? REDIR_VFORK : 0) | REDIR_KEEP); - if (!vforked) - for (sp = varlist.list ; sp ; sp = sp->next) - setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK); - envp = environment(); - shellexec(argv, envp, path, cmdentry.u.index, vforked); - break; - } - goto out; - - parent: /* parent process gets here (if we forked) */ - - exitstatus = 0; /* if not altered just below */ - if (mode == FORK_FG) { /* argument to fork */ - exitstatus = waitforjob(jp); - } else if (mode == FORK_NOJOB) { - backcmd->fd = pip[0]; - close(pip[1]); - backcmd->jp = jp; - } - FORCEINTON; - - out: - if (lastarg) - /* implement $_ for whatever use that really is */ - (void) setvarsafe("_", lastarg, VNOERROR); - popstackmark(&smark); -} - - -/* - * Search for a command. This is called before we fork so that the - * location of the command will be available in the parent as well as - * the child. The check for "goodname" is an overly conservative - * check that the name will not be subject to expansion. - */ - -STATIC void -prehash(union node *n) -{ - struct cmdentry entry; - - if (n && n->type == NCMD && n->ncmd.args) - if (goodname(n->ncmd.args->narg.text)) - find_command(n->ncmd.args->narg.text, &entry, 0, - pathval()); -} - -int -in_function(void) -{ - return funcnest; -} - -enum skipstate -current_skipstate(void) -{ - return evalskip; -} - -void -save_skipstate(struct skipsave *p) -{ - *p = s_k_i_p; -} - -void -restore_skipstate(const struct skipsave *p) -{ - s_k_i_p = *p; -} - -void -stop_skipping(void) -{ - evalskip = SKIPNONE; - skipcount = 0; -} - -/* - * Builtin commands. Builtin commands whose functions are closely - * tied to evaluation are implemented here. - */ - -/* - * No command given. - */ - -int -bltincmd(int argc, char **argv) -{ - /* - * Preserve exitstatus of a previous possible redirection - * as POSIX mandates - */ - return back_exitstatus; -} - - -/* - * Handle break and continue commands. Break, continue, and return are - * all handled by setting the evalskip flag. The evaluation routines - * above all check this flag, and if it is set they start skipping - * commands rather than executing them. The variable skipcount is - * the number of loops to break/continue, or the number of function - * levels to return. (The latter is always 1.) It should probably - * be an error to break out of more loops than exist, but it isn't - * in the standard shell so we don't make it one here. - */ - -int -breakcmd(int argc, char **argv) -{ - int n = argc > 1 ? number(argv[1]) : 1; - - if (n <= 0) - error("invalid count: %d", n); - if (n > loopnest) - n = loopnest; - if (n > 0) { - evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; - skipcount = n; - } - return 0; -} - -int -dotcmd(int argc, char **argv) -{ - exitstatus = 0; - - if (argc >= 2) { /* That's what SVR2 does */ - char *fullname; - /* - * dot_funcnest needs to be 0 when not in a dotcmd, so it - * cannot be restored with (funcnest + 1). - */ - int dot_funcnest_old; - struct stackmark smark; - - setstackmark(&smark); - fullname = find_dot_file(argv[1]); - setinputfile(fullname, 1); - commandname = fullname; - dot_funcnest_old = dot_funcnest; - dot_funcnest = funcnest + 1; - cmdloop(0); - dot_funcnest = dot_funcnest_old; - popfile(); - popstackmark(&smark); - } - return exitstatus; -} - -/* - * Take commands from a file. To be compatible we should do a path - * search for the file, which is necessary to find sub-commands. - */ - -STATIC char * -find_dot_file(char *basename) -{ - char *fullname; - const char *path = pathval(); - struct stat statb; - - /* don't try this for absolute or relative paths */ - if (strchr(basename, '/')) { - if (stat(basename, &statb) == 0) { - if (S_ISDIR(statb.st_mode)) - error("%s: is a directory", basename); - if (S_ISBLK(statb.st_mode)) - error("%s: is a block device", basename); - return basename; - } - } else while ((fullname = padvance(&path, basename, 1)) != NULL) { - if ((stat(fullname, &statb) == 0)) { - /* weird format is to ease future code... */ - if (S_ISDIR(statb.st_mode) || S_ISBLK(statb.st_mode)) - ; -#if notyet - else if (unreadable()) { - /* - * testing this via st_mode is ugly to get - * correct (and would ignore ACLs). - * better way is just to open the file. - * But doing that here would (currently) - * mean opening the file twice, which - * might not be safe. So, defer this - * test until code is restructures so - * we can return a fd. Then we also - * get to fix the mem leak just below... - */ - } -#endif - else { - /* - * Don't bother freeing here, since - * it will be freed by the caller. - * XXX no it won't - a bug for later. - */ - return fullname; - } - } - stunalloc(fullname); - } - - /* not found in the PATH */ - error("%s: not found", basename); - /* NOTREACHED */ -} - - - -/* - * The return command. - * - * Quoth the POSIX standard: - * The return utility shall cause the shell to stop executing the current - * function or dot script. If the shell is not currently executing - * a function or dot script, the results are unspecified. - * - * As for the unspecified part, there seems to be no de-facto standard: bash - * ignores the return with a warning, zsh ignores the return in interactive - * mode but seems to liken it to exit in a script. (checked May 2014) - * - * We choose to silently ignore the return. Older versions of this shell - * set evalskip to SKIPFILE causing the shell to (indirectly) exit. This - * had at least the problem of circumventing the check for stopped jobs, - * which would occur for exit or ^D. - */ - -int -returncmd(int argc, char **argv) -{ - int ret = argc > 1 ? number(argv[1]) : exitstatus; - - if ((dot_funcnest == 0 && funcnest) - || (dot_funcnest > 0 && funcnest - (dot_funcnest - 1) > 0)) { - evalskip = SKIPFUNC; - skipcount = 1; - } else if (dot_funcnest > 0) { - evalskip = SKIPFILE; - skipcount = 1; - } else { - /* XXX: should a warning be issued? */ - ret = 0; - } - - return ret; -} - - -int -falsecmd(int argc, char **argv) -{ - return 1; -} - - -int -truecmd(int argc, char **argv) -{ - return 0; -} - - -int -execcmd(int argc, char **argv) -{ - if (argc > 1) { - struct strlist *sp; - - iflag = 0; /* exit on error */ - mflag = 0; - optschanged(); - for (sp = cmdenviron; sp; sp = sp->next) - setvareq(sp->text, VDOEXPORT|VEXPORT|VSTACK); - shellexec(argv + 1, environment(), pathval(), 0, 0); - } - return 0; -} - -static int -conv_time(clock_t ticks, char *seconds, size_t l) -{ - static clock_t tpm = 0; - clock_t mins; - int i; - - if (!tpm) - tpm = sysconf(_SC_CLK_TCK) * 60; - - mins = ticks / tpm; - snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm ); - - if (seconds[0] == '6' && seconds[1] == '0') { - /* 59.99995 got rounded up... */ - mins++; - strlcpy(seconds, "0.0", l); - return mins; - } - - /* suppress trailing zeros */ - i = strlen(seconds) - 1; - for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--) - seconds[i] = 0; - return mins; -} - -int -timescmd(int argc, char **argv) -{ - struct tms tms; - int u, s, cu, cs; - char us[8], ss[8], cus[8], css[8]; - - nextopt(""); - - times(&tms); - - u = conv_time(tms.tms_utime, us, sizeof(us)); - s = conv_time(tms.tms_stime, ss, sizeof(ss)); - cu = conv_time(tms.tms_cutime, cus, sizeof(cus)); - cs = conv_time(tms.tms_cstime, css, sizeof(css)); - - outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n", - u, us, s, ss, cu, cus, cs, css); - - return 0; -} diff --git a/bin/sh/eval.h b/bin/sh/eval.h deleted file mode 100644 index b18dd42..0000000 --- a/bin/sh/eval.h +++ /dev/null @@ -1,87 +0,0 @@ -/* $NetBSD: eval.h,v 1.22 2018/12/03 06:43:19 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)eval.h 8.2 (Berkeley) 5/4/95 - */ - -extern const char *commandname; /* currently executing command */ -extern int exitstatus; /* exit status of last command */ -extern int back_exitstatus; /* exit status of backquoted command */ -extern struct strlist *cmdenviron; /* environment for builtin command */ - - -struct backcmd { /* result of evalbackcmd */ - int fd; /* file descriptor to read from */ - char *buf; /* buffer */ - int nleft; /* number of chars in buffer */ - struct job *jp; /* job structure for command */ -}; - -void evalstring(char *, int); -union node; /* BLETCH for ansi C */ -void evaltree(union node *, int); -void evalbackcmd(union node *, struct backcmd *); - -const char *syspath(void); - -/* in_function returns nonzero if we are currently evaluating a function */ -int in_function(void); /* return non-zero, if evaluating a function */ - -/* reasons for skipping commands (see comment on breakcmd routine) */ -enum skipstate { - SKIPNONE = 0, /* not skipping */ - SKIPBREAK, /* break */ - SKIPCONT, /* continue */ - SKIPFUNC, /* return in a function */ - SKIPFILE /* return in a dot command */ -}; - -struct skipsave { - enum skipstate state; /* skipping or not */ - int count; /* when break or continue, how many */ -}; - -enum skipstate current_skipstate(void); -void save_skipstate(struct skipsave *); -void restore_skipstate(const struct skipsave *); -void stop_skipping(void); /* reset internal skipping state to SKIPNONE */ - -/* - * Only for use by reset() in init.c! - */ -void reset_eval(void); - -/* flags in argument to evaltree */ -#define EV_EXIT 0x01 /* exit after evaluating tree */ -#define EV_TESTED 0x02 /* exit status is checked; ignore -e flag */ -#define EV_BACKCMD 0x04 /* command executing within back quotes */ diff --git a/bin/sh/exec.c b/bin/sh/exec.c deleted file mode 100644 index 674beb8..0000000 --- a/bin/sh/exec.c +++ /dev/null @@ -1,1183 +0,0 @@ -/* $NetBSD: exec.c,v 1.53 2018/07/25 14:42:50 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; -#else -__RCSID("$NetBSD: exec.c,v 1.53 2018/07/25 14:42:50 kre Exp $"); -#endif -#endif /* not lint */ - -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> - -/* - * When commands are first encountered, they are entered in a hash table. - * This ensures that a full path search will not have to be done for them - * on each invocation. - * - * We should investigate converting to a linear search, even though that - * would make the command name "hash" a misnomer. - */ - -#include "shell.h" -#include "main.h" -#include "nodes.h" -#include "parser.h" -#include "redir.h" -#include "eval.h" -#include "exec.h" -#include "builtins.h" -#include "var.h" -#include "options.h" -#include "input.h" -#include "output.h" -#include "syntax.h" -#include "memalloc.h" -#include "error.h" -#include "init.h" -#include "mystring.h" -#include "show.h" -#include "jobs.h" -#include "alias.h" - - -#define CMDTABLESIZE 31 /* should be prime */ -#define ARB 1 /* actual size determined at run time */ - - - -struct tblentry { - struct tblentry *next; /* next entry in hash chain */ - union param param; /* definition of builtin function */ - short cmdtype; /* index identifying command */ - char rehash; /* if set, cd done since entry created */ - char fn_ln1; /* for functions, LINENO from 1 */ - int lineno; /* for functions abs LINENO of definition */ - char cmdname[ARB]; /* name of command */ -}; - - -STATIC struct tblentry *cmdtable[CMDTABLESIZE]; -STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ -int exerrno = 0; /* Last exec error */ - - -STATIC void tryexec(char *, char **, char **, int); -STATIC void printentry(struct tblentry *, int); -STATIC void addcmdentry(char *, struct cmdentry *); -STATIC void clearcmdentry(int); -STATIC struct tblentry *cmdlookup(const char *, int); -STATIC void delete_cmd_entry(void); - -#ifndef BSD -STATIC void execinterp(char **, char **); -#endif - - -extern const char *const parsekwd[]; - -/* - * Exec a program. Never returns. If you change this routine, you may - * have to change the find_command routine as well. - */ - -void -shellexec(char **argv, char **envp, const char *path, int idx, int vforked) -{ - char *cmdname; - int e; - - if (strchr(argv[0], '/') != NULL) { - tryexec(argv[0], argv, envp, vforked); - e = errno; - } else { - e = ENOENT; - while ((cmdname = padvance(&path, argv[0], 1)) != NULL) { - if (--idx < 0 && pathopt == NULL) { - tryexec(cmdname, argv, envp, vforked); - if (errno != ENOENT && errno != ENOTDIR) - e = errno; - } - stunalloc(cmdname); - } - } - - /* Map to POSIX errors */ - switch (e) { - case EACCES: /* particularly this (unless no search perm) */ - /* - * should perhaps check if this EACCES is an exec() - * EACESS or a namei() EACESS - the latter should be 127 - * - but not today - */ - case EINVAL: /* also explicitly these */ - case ENOEXEC: - default: /* and anything else */ - exerrno = 126; - break; - - case ENOENT: /* these are the "pathname lookup failed" errors */ - case ELOOP: - case ENOTDIR: - case ENAMETOOLONG: - exerrno = 127; - break; - } - CTRACE(DBG_ERRS|DBG_CMDS|DBG_EVAL, - ("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n", - argv[0], e, vforked, suppressint)); - exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); - /* NOTREACHED */ -} - - -STATIC void -tryexec(char *cmd, char **argv, char **envp, int vforked) -{ - int e; -#ifndef BSD - char *p; -#endif - -#ifdef SYSV - do { - execve(cmd, argv, envp); - } while (errno == EINTR); -#else - execve(cmd, argv, envp); -#endif - e = errno; - if (e == ENOEXEC) { - if (vforked) { - /* We are currently vfork(2)ed, so raise an - * exception, and evalcommand will try again - * with a normal fork(2). - */ - exraise(EXSHELLPROC); - } -#ifdef DEBUG - VTRACE(DBG_CMDS, ("execve(cmd=%s) returned ENOEXEC\n", cmd)); -#endif - initshellproc(); - setinputfile(cmd, 0); - commandname = arg0 = savestr(argv[0]); -#ifndef BSD - pgetc(); pungetc(); /* fill up input buffer */ - p = parsenextc; - if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { - argv[0] = cmd; - execinterp(argv, envp); - } -#endif - setparam(argv + 1); - exraise(EXSHELLPROC); - } - errno = e; -} - - -#ifndef BSD -/* - * Execute an interpreter introduced by "#!", for systems where this - * feature has not been built into the kernel. If the interpreter is - * the shell, return (effectively ignoring the "#!"). If the execution - * of the interpreter fails, exit. - * - * This code peeks inside the input buffer in order to avoid actually - * reading any input. It would benefit from a rewrite. - */ - -#define NEWARGS 5 - -STATIC void -execinterp(char **argv, char **envp) -{ - int n; - char *inp; - char *outp; - char c; - char *p; - char **ap; - char *newargs[NEWARGS]; - int i; - char **ap2; - char **new; - - n = parsenleft - 2; - inp = parsenextc + 2; - ap = newargs; - for (;;) { - while (--n >= 0 && (*inp == ' ' || *inp == '\t')) - inp++; - if (n < 0) - goto bad; - if ((c = *inp++) == '\n') - break; - if (ap == &newargs[NEWARGS]) -bad: error("Bad #! line"); - STARTSTACKSTR(outp); - do { - STPUTC(c, outp); - } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); - STPUTC('\0', outp); - n++, inp--; - *ap++ = grabstackstr(outp); - } - if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ - p = newargs[0]; - for (;;) { - if (equal(p, "sh") || equal(p, "ash")) { - return; - } - while (*p != '/') { - if (*p == '\0') - goto break2; - p++; - } - p++; - } -break2:; - } - i = (char *)ap - (char *)newargs; /* size in bytes */ - if (i == 0) - error("Bad #! line"); - for (ap2 = argv ; *ap2++ != NULL ; ); - new = ckmalloc(i + ((char *)ap2 - (char *)argv)); - ap = newargs, ap2 = new; - while ((i -= sizeof (char **)) >= 0) - *ap2++ = *ap++; - ap = argv; - while (*ap2++ = *ap++); - shellexec(new, envp, pathval(), 0); - /* NOTREACHED */ -} -#endif - - - -/* - * Do a path search. The variable path (passed by reference) should be - * set to the start of the path before the first call; padvance will update - * this value as it proceeds. Successive calls to padvance will return - * the possible path expansions in sequence. If an option (indicated by - * a percent sign) appears in the path entry then the global variable - * pathopt will be set to point to it; otherwise pathopt will be set to - * NULL. - */ - -const char *pathopt; - -char * -padvance(const char **path, const char *name, int magic_percent) -{ - const char *p; - char *q; - const char *start; - int len; - - if (*path == NULL) - return NULL; - if (magic_percent) - magic_percent = '%'; - - start = *path; - for (p = start ; *p && *p != ':' && *p != magic_percent ; p++) - ; - len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ - while (stackblocksize() < len) - growstackblock(); - q = stackblock(); - if (p != start) { - memcpy(q, start, p - start); - q += p - start; - if (q[-1] != '/') - *q++ = '/'; - } - strcpy(q, name); - pathopt = NULL; - if (*p == magic_percent) { - pathopt = ++p; - while (*p && *p != ':') - p++; - } - if (*p == ':') - *path = p + 1; - else - *path = NULL; - return grabstackstr(q + strlen(name) + 1); -} - - -/*** Command hashing code ***/ - - -int -hashcmd(int argc, char **argv) -{ - struct tblentry **pp; - struct tblentry *cmdp; - int c; - struct cmdentry entry; - char *name; - int allopt=0, bopt=0, fopt=0, ropt=0, sopt=0, uopt=0, verbose=0; - - while ((c = nextopt("bcfrsuv")) != '\0') - switch (c) { - case 'b': bopt = 1; break; - case 'c': uopt = 1; break; /* c == u */ - case 'f': fopt = 1; break; - case 'r': ropt = 1; break; - case 's': sopt = 1; break; - case 'u': uopt = 1; break; - case 'v': verbose = 1; break; - } - - if (ropt) - clearcmdentry(0); - - if (bopt == 0 && fopt == 0 && sopt == 0 && uopt == 0) - allopt = bopt = fopt = sopt = uopt = 1; - - if (*argptr == NULL) { - for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { - for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { - switch (cmdp->cmdtype) { - case CMDNORMAL: - if (!uopt) - continue; - break; - case CMDBUILTIN: - if (!bopt) - continue; - break; - case CMDSPLBLTIN: - if (!sopt) - continue; - break; - case CMDFUNCTION: - if (!fopt) - continue; - break; - default: /* never happens */ - continue; - } - if (!allopt || verbose || - cmdp->cmdtype == CMDNORMAL) - printentry(cmdp, verbose); - } - } - return 0; - } - - while ((name = *argptr++) != NULL) { - if ((cmdp = cmdlookup(name, 0)) != NULL) { - switch (cmdp->cmdtype) { - case CMDNORMAL: - if (!uopt) - continue; - delete_cmd_entry(); - break; - case CMDBUILTIN: - if (!bopt) - continue; - if (builtinloc >= 0) - delete_cmd_entry(); - break; - case CMDSPLBLTIN: - if (!sopt) - continue; - break; - case CMDFUNCTION: - if (!fopt) - continue; - break; - } - } - find_command(name, &entry, DO_ERR, pathval()); - if (verbose) { - if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ - cmdp = cmdlookup(name, 0); - if (cmdp != NULL) - printentry(cmdp, verbose); - } - flushall(); - } - } - return 0; -} - -STATIC void -printentry(struct tblentry *cmdp, int verbose) -{ - int idx; - const char *path; - char *name; - - switch (cmdp->cmdtype) { - case CMDNORMAL: - idx = cmdp->param.index; - path = pathval(); - do { - name = padvance(&path, cmdp->cmdname, 1); - stunalloc(name); - } while (--idx >= 0); - if (verbose) - out1fmt("Command from PATH[%d]: ", - cmdp->param.index); - out1str(name); - break; - case CMDSPLBLTIN: - if (verbose) - out1str("special "); - /* FALLTHROUGH */ - case CMDBUILTIN: - if (verbose) - out1str("builtin "); - out1fmt("%s", cmdp->cmdname); - break; - case CMDFUNCTION: - if (verbose) - out1str("function "); - out1fmt("%s", cmdp->cmdname); - if (verbose) { - struct procstat ps; - - INTOFF; - commandtext(&ps, getfuncnode(cmdp->param.func)); - INTON; - out1str("() { "); - out1str(ps.cmd); - out1str("; }"); - } - break; - default: - error("internal error: %s cmdtype %d", - cmdp->cmdname, cmdp->cmdtype); - } - if (cmdp->rehash) - out1c('*'); - out1c('\n'); -} - - - -/* - * Resolve a command name. If you change this routine, you may have to - * change the shellexec routine as well. - */ - -void -find_command(char *name, struct cmdentry *entry, int act, const char *path) -{ - struct tblentry *cmdp, loc_cmd; - int idx; - int prev; - char *fullname; - struct stat statb; - int e; - int (*bltin)(int,char **); - - /* If name contains a slash, don't use PATH or hash table */ - if (strchr(name, '/') != NULL) { - if (act & DO_ABS) { - while (stat(name, &statb) < 0) { -#ifdef SYSV - if (errno == EINTR) - continue; -#endif - if (errno != ENOENT && errno != ENOTDIR) - e = errno; - entry->cmdtype = CMDUNKNOWN; - entry->u.index = -1; - return; - } - entry->cmdtype = CMDNORMAL; - entry->u.index = -1; - return; - } - entry->cmdtype = CMDNORMAL; - entry->u.index = 0; - return; - } - - if (path != pathval()) - act |= DO_ALTPATH; - - if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL) - act |= DO_ALTBLTIN; - - /* If name is in the table, check answer will be ok */ - if ((cmdp = cmdlookup(name, 0)) != NULL) { - do { - switch (cmdp->cmdtype) { - case CMDNORMAL: - if (act & DO_ALTPATH) { - cmdp = NULL; - continue; - } - break; - case CMDFUNCTION: - if (act & DO_NOFUNC) { - cmdp = NULL; - continue; - } - break; - case CMDBUILTIN: - if ((act & DO_ALTBLTIN) || builtinloc >= 0) { - cmdp = NULL; - continue; - } - break; - } - /* if not invalidated by cd, we're done */ - if (cmdp->rehash == 0) - goto success; - } while (0); - } - - /* If %builtin not in path, check for builtin next */ - if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) && - (bltin = find_builtin(name)) != 0) - goto builtin_success; - - /* We have to search path. */ - prev = -1; /* where to start */ - if (cmdp) { /* doing a rehash */ - if (cmdp->cmdtype == CMDBUILTIN) - prev = builtinloc; - else - prev = cmdp->param.index; - } - - e = ENOENT; - idx = -1; -loop: - while ((fullname = padvance(&path, name, 1)) != NULL) { - stunalloc(fullname); - idx++; - if (pathopt) { - if (prefix("builtin", pathopt)) { - if ((bltin = find_builtin(name)) == 0) - goto loop; - goto builtin_success; - } else if (prefix("func", pathopt)) { - /* handled below */ - } else { - /* ignore unimplemented options */ - goto loop; - } - } - /* if rehash, don't redo absolute path names */ - if (fullname[0] == '/' && idx <= prev) { - if (idx < prev) - goto loop; - VTRACE(DBG_CMDS, ("searchexec \"%s\": no change\n", - name)); - goto success; - } - while (stat(fullname, &statb) < 0) { -#ifdef SYSV - if (errno == EINTR) - continue; -#endif - if (errno != ENOENT && errno != ENOTDIR) - e = errno; - goto loop; - } - e = EACCES; /* if we fail, this will be the error */ - if (!S_ISREG(statb.st_mode)) - goto loop; - if (pathopt) { /* this is a %func directory */ - char *endname; - - if (act & DO_NOFUNC) - goto loop; - endname = fullname + strlen(fullname) + 1; - grabstackstr(endname); - readcmdfile(fullname); - if ((cmdp = cmdlookup(name, 0)) == NULL || - cmdp->cmdtype != CMDFUNCTION) - error("%s not defined in %s", name, fullname); - ungrabstackstr(fullname, endname); - goto success; - } -#ifdef notdef - /* XXX this code stops root executing stuff, and is buggy - if you need a group from the group list. */ - if (statb.st_uid == geteuid()) { - if ((statb.st_mode & 0100) == 0) - goto loop; - } else if (statb.st_gid == getegid()) { - if ((statb.st_mode & 010) == 0) - goto loop; - } else { - if ((statb.st_mode & 01) == 0) - goto loop; - } -#endif - VTRACE(DBG_CMDS, ("searchexec \"%s\" returns \"%s\"\n", name, - fullname)); - INTOFF; - if (act & DO_ALTPATH) { - /* - * this should be a grabstackstr() but is not needed: - * fullname is no longer needed for anything - stalloc(strlen(fullname) + 1); - */ - cmdp = &loc_cmd; - } else - cmdp = cmdlookup(name, 1); - cmdp->cmdtype = CMDNORMAL; - cmdp->param.index = idx; - INTON; - goto success; - } - - /* We failed. If there was an entry for this command, delete it */ - if (cmdp) - delete_cmd_entry(); - if (act & DO_ERR) - outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); - entry->cmdtype = CMDUNKNOWN; - return; - -builtin_success: - INTOFF; - if (act & DO_ALTPATH) - cmdp = &loc_cmd; - else - cmdp = cmdlookup(name, 1); - if (cmdp->cmdtype == CMDFUNCTION) - /* DO_NOFUNC must have been set */ - cmdp = &loc_cmd; - cmdp->cmdtype = CMDBUILTIN; - cmdp->param.bltin = bltin; - INTON; -success: - if (cmdp) { - cmdp->rehash = 0; - entry->cmdtype = cmdp->cmdtype; - entry->lineno = cmdp->lineno; - entry->lno_frel = cmdp->fn_ln1; - entry->u = cmdp->param; - } else - entry->cmdtype = CMDUNKNOWN; -} - - - -/* - * Search the table of builtin commands. - */ - -int -(*find_builtin(char *name))(int, char **) -{ - const struct builtincmd *bp; - - for (bp = builtincmd ; bp->name ; bp++) { - if (*bp->name == *name - && (*name == '%' || equal(bp->name, name))) - return bp->builtin; - } - return 0; -} - -int -(*find_splbltin(char *name))(int, char **) -{ - const struct builtincmd *bp; - - for (bp = splbltincmd ; bp->name ; bp++) { - if (*bp->name == *name && equal(bp->name, name)) - return bp->builtin; - } - return 0; -} - -/* - * At shell startup put special builtins into hash table. - * ensures they are executed first (see posix). - * We stop functions being added with the same name - * (as they are impossible to call) - */ - -void -hash_special_builtins(void) -{ - const struct builtincmd *bp; - struct tblentry *cmdp; - - for (bp = splbltincmd ; bp->name ; bp++) { - cmdp = cmdlookup(bp->name, 1); - cmdp->cmdtype = CMDSPLBLTIN; - cmdp->param.bltin = bp->builtin; - } -} - - - -/* - * Called when a cd is done. Marks all commands so the next time they - * are executed they will be rehashed. - */ - -void -hashcd(void) -{ - struct tblentry **pp; - struct tblentry *cmdp; - - for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { - for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { - if (cmdp->cmdtype == CMDNORMAL - || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) - cmdp->rehash = 1; - } - } -} - - - -/* - * Fix command hash table when PATH changed. - * Called before PATH is changed. The argument is the new value of PATH; - * pathval() still returns the old value at this point. - * Called with interrupts off. - */ - -void -changepath(const char *newval) -{ - const char *old, *new; - int idx; - int firstchange; - int bltin; - - old = pathval(); - new = newval; - firstchange = 9999; /* assume no change */ - idx = 0; - bltin = -1; - for (;;) { - if (*old != *new) { - firstchange = idx; - if ((*old == '\0' && *new == ':') - || (*old == ':' && *new == '\0')) - firstchange++; - old = new; /* ignore subsequent differences */ - } - if (*new == '\0') - break; - if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) - bltin = idx; - if (*new == ':') { - idx++; - } - new++, old++; - } - if (builtinloc < 0 && bltin >= 0) - builtinloc = bltin; /* zap builtins */ - if (builtinloc >= 0 && bltin < 0) - firstchange = 0; - clearcmdentry(firstchange); - builtinloc = bltin; -} - - -/* - * Clear out command entries. The argument specifies the first entry in - * PATH which has changed. - */ - -STATIC void -clearcmdentry(int firstchange) -{ - struct tblentry **tblp; - struct tblentry **pp; - struct tblentry *cmdp; - - INTOFF; - for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { - pp = tblp; - while ((cmdp = *pp) != NULL) { - if ((cmdp->cmdtype == CMDNORMAL && - cmdp->param.index >= firstchange) - || (cmdp->cmdtype == CMDBUILTIN && - builtinloc >= firstchange)) { - *pp = cmdp->next; - ckfree(cmdp); - } else { - pp = &cmdp->next; - } - } - } - INTON; -} - - -/* - * Delete all functions. - */ - -#ifdef mkinit -MKINIT void deletefuncs(void); -MKINIT void hash_special_builtins(void); - -INIT { - hash_special_builtins(); -} - -SHELLPROC { - deletefuncs(); -} -#endif - -void -deletefuncs(void) -{ - struct tblentry **tblp; - struct tblentry **pp; - struct tblentry *cmdp; - - INTOFF; - for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { - pp = tblp; - while ((cmdp = *pp) != NULL) { - if (cmdp->cmdtype == CMDFUNCTION) { - *pp = cmdp->next; - freefunc(cmdp->param.func); - ckfree(cmdp); - } else { - pp = &cmdp->next; - } - } - } - INTON; -} - - - -/* - * Locate a command in the command hash table. If "add" is nonzero, - * add the command to the table if it is not already present. The - * variable "lastcmdentry" is set to point to the address of the link - * pointing to the entry, so that delete_cmd_entry can delete the - * entry. - */ - -struct tblentry **lastcmdentry; - - -STATIC struct tblentry * -cmdlookup(const char *name, int add) -{ - int hashval; - const char *p; - struct tblentry *cmdp; - struct tblentry **pp; - - p = name; - hashval = *p << 4; - while (*p) - hashval += *p++; - hashval &= 0x7FFF; - pp = &cmdtable[hashval % CMDTABLESIZE]; - for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { - if (equal(cmdp->cmdname, name)) - break; - pp = &cmdp->next; - } - if (add && cmdp == NULL) { - INTOFF; - cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB - + strlen(name) + 1); - cmdp->next = NULL; - cmdp->cmdtype = CMDUNKNOWN; - cmdp->rehash = 0; - strcpy(cmdp->cmdname, name); - INTON; - } - lastcmdentry = pp; - return cmdp; -} - -/* - * Delete the command entry returned on the last lookup. - */ - -STATIC void -delete_cmd_entry(void) -{ - struct tblentry *cmdp; - - INTOFF; - cmdp = *lastcmdentry; - *lastcmdentry = cmdp->next; - ckfree(cmdp); - INTON; -} - - - -#ifdef notdef -void -getcmdentry(char *name, struct cmdentry *entry) -{ - struct tblentry *cmdp = cmdlookup(name, 0); - - if (cmdp) { - entry->u = cmdp->param; - entry->cmdtype = cmdp->cmdtype; - } else { - entry->cmdtype = CMDUNKNOWN; - entry->u.index = 0; - } -} -#endif - - -/* - * Add a new command entry, replacing any existing command entry for - * the same name - except special builtins. - */ - -STATIC void -addcmdentry(char *name, struct cmdentry *entry) -{ - struct tblentry *cmdp; - - INTOFF; - cmdp = cmdlookup(name, 1); - if (cmdp->cmdtype != CMDSPLBLTIN) { - if (cmdp->cmdtype == CMDFUNCTION) - unreffunc(cmdp->param.func); - cmdp->cmdtype = entry->cmdtype; - cmdp->lineno = entry->lineno; - cmdp->fn_ln1 = entry->lno_frel; - cmdp->param = entry->u; - } - INTON; -} - - -/* - * Define a shell function. - */ - -void -defun(char *name, union node *func, int lineno) -{ - struct cmdentry entry; - - INTOFF; - entry.cmdtype = CMDFUNCTION; - entry.lineno = lineno; - entry.lno_frel = fnline1; - entry.u.func = copyfunc(func); - addcmdentry(name, &entry); - INTON; -} - - -/* - * Delete a function if it exists. - */ - -int -unsetfunc(char *name) -{ - struct tblentry *cmdp; - - if ((cmdp = cmdlookup(name, 0)) != NULL && - cmdp->cmdtype == CMDFUNCTION) { - unreffunc(cmdp->param.func); - delete_cmd_entry(); - } - return 0; -} - -/* - * Locate and print what a word is... - * also used for 'command -[v|V]' - */ - -int -typecmd(int argc, char **argv) -{ - struct cmdentry entry; - struct tblentry *cmdp; - const char * const *pp; - struct alias *ap; - int err = 0; - char *arg; - int c; - int V_flag = 0; - int v_flag = 0; - int p_flag = 0; - - while ((c = nextopt("vVp")) != 0) { - switch (c) { - case 'v': v_flag = 1; break; - case 'V': V_flag = 1; break; - case 'p': p_flag = 1; break; - } - } - - if (argv[0][0] != 'c' && v_flag | V_flag | p_flag) - error("usage: %s name...", argv[0]); - - if (v_flag && V_flag) - error("-v and -V cannot both be specified"); - - if (*argptr == NULL) - error("usage: %s%s name ...", argv[0], - argv[0][0] == 'c' ? " [-p] [-v|-V]" : ""); - - while ((arg = *argptr++)) { - if (!v_flag) - out1str(arg); - /* First look at the keywords */ - for (pp = parsekwd; *pp; pp++) - if (**pp == *arg && equal(*pp, arg)) - break; - - if (*pp) { - if (v_flag) - out1fmt("%s\n", arg); - else - out1str(" is a shell keyword\n"); - continue; - } - - /* Then look at the aliases */ - if ((ap = lookupalias(arg, 1)) != NULL) { - int ml = 0; - - if (!v_flag) { - out1str(" is an alias "); - if (strchr(ap->val, '\n')) { - out1str("(multiline)...\n"); - ml = 1; - } else - out1str("for: "); - } - out1fmt("%s\n", ap->val); - if (ml && *argptr != NULL) - out1c('\n'); - continue; - } - - /* Then check if it is a tracked alias */ - if (!p_flag && (cmdp = cmdlookup(arg, 0)) != NULL) { - entry.cmdtype = cmdp->cmdtype; - entry.u = cmdp->param; - } else { - cmdp = NULL; - /* Finally use brute force */ - find_command(arg, &entry, DO_ABS, - p_flag ? syspath() + 5 : pathval()); - } - - switch (entry.cmdtype) { - case CMDNORMAL: { - if (strchr(arg, '/') == NULL) { - const char *path; - char *name; - int j = entry.u.index; - - path = p_flag ? syspath() + 5 : pathval(); - - do { - name = padvance(&path, arg, 1); - stunalloc(name); - } while (--j >= 0); - if (!v_flag) - out1fmt(" is%s ", - cmdp ? " a tracked alias for" : ""); - out1fmt("%s\n", name); - } else { - if (access(arg, X_OK) == 0) { - if (!v_flag) - out1fmt(" is "); - out1fmt("%s\n", arg); - } else { - if (!v_flag) - out1fmt(": %s\n", - strerror(errno)); - else - err = 126; - } - } - break; - } - case CMDFUNCTION: - if (!v_flag) - out1str(" is a shell function\n"); - else - out1fmt("%s\n", arg); - break; - - case CMDBUILTIN: - if (!v_flag) - out1str(" is a shell builtin\n"); - else - out1fmt("%s\n", arg); - break; - - case CMDSPLBLTIN: - if (!v_flag) - out1str(" is a special shell builtin\n"); - else - out1fmt("%s\n", arg); - break; - - default: - if (!v_flag) - out1str(": not found\n"); - err = 127; - break; - } - } - return err; -} diff --git a/bin/sh/exec.h b/bin/sh/exec.h deleted file mode 100644 index 90beba1..0000000 --- a/bin/sh/exec.h +++ /dev/null @@ -1,78 +0,0 @@ -/* $NetBSD: exec.h,v 1.27 2018/06/22 11:04:55 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)exec.h 8.3 (Berkeley) 6/8/95 - */ - -/* values of cmdtype */ -#define CMDUNKNOWN -1 /* no entry in table for command */ -#define CMDNORMAL 0 /* command is an executable program */ -#define CMDFUNCTION 1 /* command is a shell function */ -#define CMDBUILTIN 2 /* command is a shell builtin */ -#define CMDSPLBLTIN 3 /* command is a special shell builtin */ - - -struct cmdentry { - short cmdtype; - short lno_frel; /* for functions: Line numbers count from 1 */ - int lineno; /* for functions: Abs line number of defn */ - union param { - int index; - int (*bltin)(int, char**); - struct funcdef *func; - } u; -}; - - -/* action to find_command() */ -#define DO_ERR 0x01 /* prints errors */ -#define DO_ABS 0x02 /* checks absolute paths */ -#define DO_NOFUNC 0x04 /* don't return shell functions, for command */ -#define DO_ALTPATH 0x08 /* using alternate path */ -#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */ - -extern const char *pathopt; /* set by padvance */ - -void shellexec(char **, char **, const char *, int, int) __dead; -char *padvance(const char **, const char *, int); -void find_command(char *, struct cmdentry *, int, const char *); -int (*find_builtin(char *))(int, char **); -int (*find_splbltin(char *))(int, char **); -void hashcd(void); -void changepath(const char *); -void deletefuncs(void); -void getcmdentry(char *, struct cmdentry *); -union node; -void defun(char *, union node *, int); -int unsetfunc(char *); -void hash_special_builtins(void); diff --git a/bin/sh/expand.c b/bin/sh/expand.c deleted file mode 100644 index 955185b..0000000 --- a/bin/sh/expand.c +++ /dev/null @@ -1,2125 +0,0 @@ -/* $NetBSD: expand.c,v 1.129 2018/12/03 06:41:30 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; -#else -__RCSID("$NetBSD: expand.c,v 1.129 2018/12/03 06:41:30 kre Exp $"); -#endif -#endif /* not lint */ - -#include <sys/types.h> -#include <sys/time.h> -#include <sys/stat.h> -#include <errno.h> -#include <dirent.h> -#include <unistd.h> -#include <pwd.h> -#include <limits.h> -#include <stdlib.h> -#include <stdio.h> -#include <wctype.h> -#include <wchar.h> - -/* - * Routines to expand arguments to commands. We have to deal with - * backquotes, shell variables, and file metacharacters. - */ - -#include "shell.h" -#include "main.h" -#include "nodes.h" -#include "eval.h" -#include "expand.h" -#include "syntax.h" -#include "arithmetic.h" -#include "parser.h" -#include "jobs.h" -#include "options.h" -#include "builtins.h" -#include "var.h" -#include "input.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "mystring.h" -#include "show.h" - -/* - * Structure specifying which parts of the string should be searched - * for IFS characters. - */ - -struct ifsregion { - struct ifsregion *next; /* next region in list */ - int begoff; /* offset of start of region */ - int endoff; /* offset of end of region */ - int inquotes; /* search for nul bytes only */ -}; - - -char *expdest; /* output of current string */ -struct nodelist *argbackq; /* list of back quote expressions */ -struct ifsregion ifsfirst; /* first struct in list of ifs regions */ -struct ifsregion *ifslastp; /* last struct in list */ -struct arglist exparg; /* holds expanded arg list */ - -STATIC const char *argstr(const char *, int); -STATIC const char *exptilde(const char *, int); -STATIC void expbackq(union node *, int, int); -STATIC const char *expari(const char *); -STATIC int subevalvar(const char *, const char *, int, int, int); -STATIC int subevalvar_trim(const char *, int, int, int, int, int); -STATIC const char *evalvar(const char *, int); -STATIC int varisset(const char *, int); -STATIC void varvalue(const char *, int, int, int); -STATIC void recordregion(int, int, int); -STATIC void removerecordregions(int); -STATIC void ifsbreakup(char *, struct arglist *); -STATIC void ifsfree(void); -STATIC void expandmeta(struct strlist *, int); -STATIC void expmeta(char *, char *); -STATIC void addfname(char *); -STATIC struct strlist *expsort(struct strlist *); -STATIC struct strlist *msort(struct strlist *, int); -STATIC int patmatch(const char *, const char *, int); -STATIC char *cvtnum(int, char *); -static int collate_range_cmp(wchar_t, wchar_t); -STATIC void add_args(struct strlist *); -STATIC void rmescapes_nl(char *); - -#ifdef DEBUG -#define NULLTERM_4_TRACE(p) STACKSTRNUL(p) -#else -#define NULLTERM_4_TRACE(p) do { /* nothing */ } while (/*CONSTCOND*/0) -#endif - -#define IS_BORING(_ch) \ - ((_ch) == CTLQUOTEMARK || (_ch) == CTLQUOTEEND || (_ch) == CTLNONL) -#define SKIP_BORING(p) \ - do { \ - char _ch; \ - \ - while ((_ch = *(p)), IS_BORING(_ch)) \ - (p)++; \ - } while (0) - -/* - * Expand shell variables and backquotes inside a here document. - */ - -void -expandhere(union node *arg, int fd) -{ - - herefd = fd; - expandarg(arg, NULL, 0); - xwrite(fd, stackblock(), expdest - stackblock()); -} - - -static int -collate_range_cmp(wchar_t c1, wchar_t c2) -{ - wchar_t s1[2], s2[2]; - - s1[0] = c1; - s1[1] = L'\0'; - s2[0] = c2; - s2[1] = L'\0'; - return (wcscoll(s1, s2)); -} - -/* - * Perform variable substitution and command substitution on an argument, - * placing the resulting list of arguments in arglist. If EXP_FULL is true, - * perform splitting and file name expansion. When arglist is NULL, perform - * here document expansion. - */ - -void -expandarg(union node *arg, struct arglist *arglist, int flag) -{ - struct strlist *sp; - char *p; - - CTRACE(DBG_EXPAND, ("expandarg(fl=%#x)\n", flag)); - if (fflag) /* no filename expandsion */ - flag &= ~EXP_GLOB; - - argbackq = arg->narg.backquote; - STARTSTACKSTR(expdest); - ifsfirst.next = NULL; - ifslastp = NULL; - line_number = arg->narg.lineno; - argstr(arg->narg.text, flag); - if (arglist == NULL) { - STACKSTRNUL(expdest); - CTRACE(DBG_EXPAND, ("expandarg: no arglist, done (%d) \"%s\"\n", - expdest - stackblock(), stackblock())); - return; /* here document expanded */ - } - STPUTC('\0', expdest); - CTRACE(DBG_EXPAND, ("expandarg: arglist got (%d) \"%s\"\n", - expdest - stackblock() - 1, stackblock())); - p = grabstackstr(expdest); - exparg.lastp = &exparg.list; - /* - * TODO - EXP_REDIR - */ - if (flag & EXP_SPLIT) { - ifsbreakup(p, &exparg); - *exparg.lastp = NULL; - exparg.lastp = &exparg.list; - if (flag & EXP_GLOB) - expandmeta(exparg.list, flag); - else - add_args(exparg.list); - } else { - if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ - rmescapes(p); - sp = stalloc(sizeof(*sp)); - sp->text = p; - *exparg.lastp = sp; - exparg.lastp = &sp->next; - } - ifsfree(); - *exparg.lastp = NULL; - if (exparg.list) { - *arglist->lastp = exparg.list; - arglist->lastp = exparg.lastp; - } -} - - - -/* - * Perform variable and command substitution. - * If EXP_GLOB is set, output CTLESC characters to allow for further processing. - * If EXP_SPLIT is set, remember location of result for later, - * Otherwise treat $@ like $* since no splitting will be performed. - */ - -STATIC const char * -argstr(const char *p, int flag) -{ - char c; - const int quotes = flag & EXP_QNEEDED; /* do CTLESC */ - int firsteq = 1; - const char *ifs = NULL; - int ifs_split = EXP_IFS_SPLIT; - - if (flag & EXP_IFS_SPLIT) - ifs = ifsval(); - - CTRACE(DBG_EXPAND, ("argstr(\"%s\", %#x) quotes=%#x\n", p,flag,quotes)); - - if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) - p = exptilde(p, flag); - for (;;) { - switch (c = *p++) { - case '\0': - NULLTERM_4_TRACE(expdest); - VTRACE(DBG_EXPAND, ("argstr returning at \"\" " - "added \"%s\" to expdest\n", stackblock())); - return p - 1; - case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ - case CTLENDARI: /* end of a $(( )) string */ - NULLTERM_4_TRACE(expdest); - VTRACE(DBG_EXPAND, ("argstr returning at \"%.6s\"..." - " after %2.2X; added \"%s\" to expdest\n", - p, (c&0xff), stackblock())); - return p; - case CTLQUOTEMARK: - /* "$@" syntax adherence hack */ - if (p[0] == CTLVAR && p[1] & VSQUOTE && - p[2] == '@' && p[3] == '=') - break; - if ((flag & EXP_SPLIT) != 0) - STPUTC(c, expdest); - ifs_split = 0; - break; - case CTLNONL: - if (flag & EXP_NL) - STPUTC(c, expdest); - line_number++; - break; - case CTLCNL: - STPUTC('\n', expdest); /* no line_number++ */ - break; - case CTLQUOTEEND: - if ((flag & EXP_SPLIT) != 0) - STPUTC(c, expdest); - ifs_split = EXP_IFS_SPLIT; - break; - case CTLESC: - if (quotes) - STPUTC(c, expdest); - c = *p++; - STPUTC(c, expdest); - if (c == '\n') /* should not happen, but ... */ - line_number++; - break; - case CTLVAR: { -#ifdef DEBUG - unsigned int pos = expdest - stackblock(); - NULLTERM_4_TRACE(expdest); -#endif - p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split)); - NULLTERM_4_TRACE(expdest); - VTRACE(DBG_EXPAND, ("argstr evalvar " - "added %zd \"%s\" to expdest\n", - (size_t)(expdest - (stackblock() + pos)), - stackblock() + pos)); - break; - } - case CTLBACKQ: - case CTLBACKQ|CTLQUOTE: { -#ifdef DEBUG - unsigned int pos = expdest - stackblock(); -#endif - expbackq(argbackq->n, c & CTLQUOTE, flag); - argbackq = argbackq->next; - NULLTERM_4_TRACE(expdest); - VTRACE(DBG_EXPAND, ("argstr expbackq added \"%s\" " - "to expdest\n", stackblock() + pos)); - break; - } - case CTLARI: { -#ifdef DEBUG - unsigned int pos = expdest - stackblock(); -#endif - p = expari(p); - NULLTERM_4_TRACE(expdest); - VTRACE(DBG_EXPAND, ("argstr expari " - "+ \"%s\" to expdest p=\"%.5s...\"\n", - stackblock() + pos, p)); - break; - } - case ':': - case '=': - /* - * sort of a hack - expand tildes in variable - * assignments (after the first '=' and after ':'s). - */ - STPUTC(c, expdest); - if (flag & EXP_VARTILDE && *p == '~') { - if (c == '=') { - if (firsteq) - firsteq = 0; - else - break; - } - p = exptilde(p, flag); - } - break; - default: - if (c == '\n') - line_number++; - STPUTC(c, expdest); - if (flag & ifs_split && strchr(ifs, c) != NULL) { - /* We need to get the output split here... */ - recordregion(expdest - stackblock() - 1, - expdest - stackblock(), 0); - } - break; - } - } -} - -STATIC const char * -exptilde(const char *p, int flag) -{ - char c; - const char *startp = p; - struct passwd *pw; - const char *home; - const int quotes = flag & EXP_QNEEDED; - char *user; - struct stackmark smark; -#ifdef DEBUG - unsigned int offs = expdest - stackblock(); -#endif - - setstackmark(&smark); - (void) grabstackstr(expdest); - user = stackblock(); /* we will just borrow top of stack */ - - while ((c = *++p) != '\0') { - switch(c) { - case CTLESC: /* any of these occurring */ - case CTLVAR: /* means ~ expansion */ - case CTLBACKQ: /* does not happen at all */ - case CTLBACKQ | CTLQUOTE: - case CTLARI: /* just leave original unchanged */ - case CTLENDARI: - case CTLQUOTEMARK: - case '\n': - popstackmark(&smark); - return (startp); - case CTLNONL: - continue; - case ':': - if (!posix || flag & EXP_VARTILDE) - goto done; - break; - case CTLENDVAR: - case '/': - goto done; - } - STPUTC(c, user); - } - done: - STACKSTRNUL(user); - user = stackblock(); /* to start of collected username */ - - CTRACE(DBG_EXPAND, ("exptilde, found \"~%s\"", user)); - if (*user == '\0') { - home = lookupvar("HOME"); - /* - * if HOME is unset, results are unspecified... - * we used to just leave the ~ unchanged, but - * (some) other shells do ... and this seems more useful. - */ - if (home == NULL && (pw = getpwuid(getuid())) != NULL) - home = pw->pw_dir; - } else if ((pw = getpwnam(user)) == NULL) { - /* - * If user does not exist, results are undefined. - * so we can abort() here if we want, but let's not! - */ - home = NULL; - } else - home = pw->pw_dir; - - VTRACE(DBG_EXPAND, (" ->\"%s\"", home ? home : "<<NULL>>")); - popstackmark(&smark); /* now expdest is valid again */ - - /* - * Posix XCU 2.6.1: The value of $HOME (for ~) or the initial - * working directory from getpwnam() for ~user - * Nothing there about "except if a null string". So do what it wants. - */ - if (home == NULL /* || *home == '\0' */) { - CTRACE(DBG_EXPAND, (": returning unused \"%s\"\n", startp)); - return startp; - } while ((c = *home++) != '\0') { - if (quotes && NEEDESC(c)) - STPUTC(CTLESC, expdest); - STPUTC(c, expdest); - } - CTRACE(DBG_EXPAND, (": added %d \"%.*s\" returning \"%s\"\n", - expdest - stackblock() - offs, expdest - stackblock() - offs, - stackblock() + offs, p)); - - return (p); -} - - -STATIC void -removerecordregions(int endoff) -{ - - VTRACE(DBG_EXPAND, ("removerecordregions(%d):", endoff)); - if (ifslastp == NULL) { - VTRACE(DBG_EXPAND, (" none\n", endoff)); - return; - } - - if (ifsfirst.endoff > endoff) { - VTRACE(DBG_EXPAND, (" first(%d)", ifsfirst.endoff)); - while (ifsfirst.next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifsfirst.next->next; - ckfree(ifsfirst.next); - ifsfirst.next = ifsp; - INTON; - } - if (ifsfirst.begoff > endoff) - ifslastp = NULL; - else { - VTRACE(DBG_EXPAND,("->(%d,%d)",ifsfirst.begoff,endoff)); - ifslastp = &ifsfirst; - ifsfirst.endoff = endoff; - } - VTRACE(DBG_EXPAND, ("\n")); - return; - } - - ifslastp = &ifsfirst; - while (ifslastp->next && ifslastp->next->begoff < endoff) - ifslastp=ifslastp->next; - VTRACE(DBG_EXPAND, (" found(%d,%d)", ifslastp->begoff,ifslastp->endoff)); - while (ifslastp->next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifslastp->next->next; - ckfree(ifslastp->next); - ifslastp->next = ifsp; - INTON; - } - if (ifslastp->endoff > endoff) - ifslastp->endoff = endoff; - VTRACE(DBG_EXPAND, ("->(%d,%d)", ifslastp->begoff,ifslastp->endoff)); -} - - -/* - * Expand arithmetic expression. - * - * In this incarnation, we start at the beginning (yes, "Let's start at the - * very beginning. A very good place to start.") and collect the expression - * until the end - which means expanding anything contained within. - * - * Fortunately, argstr() just happens to do that for us... - */ -STATIC const char * -expari(const char *p) -{ - char *q, *start; - intmax_t result; - int adjustment; - int begoff; - int quoted; - struct stackmark smark; - - /* ifsfree(); */ - - /* - * SPACE_NEEDED is enough for all possible digits (rounded up) - * plus possible "-", and the terminating '\0', hence, plus 2 - * - * The calculation produces the number of bytes needed to - * represent the biggest possible value, in octal. We only - * generate decimal, which takes (often) less digits (never more) - * so this is safe, if occasionally slightly wasteful. - */ -#define SPACE_NEEDED ((int)((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 2)) - - quoted = *p++ == '"'; - begoff = expdest - stackblock(); - VTRACE(DBG_EXPAND, ("expari%s: \"%s\" begoff %d\n", - quoted ? "(quoted)" : "", p, begoff)); - - p = argstr(p, EXP_NL); /* expand $(( )) string */ - STPUTC('\0', expdest); - start = stackblock() + begoff; - - removerecordregions(begoff); /* nothing there is kept */ - rmescapes_nl(start); /* convert CRTNONL back into \n's */ - - setstackmark(&smark); - q = grabstackstr(expdest); /* keep the expression while eval'ing */ - result = arith(start, line_number); - popstackmark(&smark); /* return the stack to before grab */ - - start = stackblock() + begoff; /* block may have moved */ - adjustment = expdest - start; - STADJUST(-adjustment, expdest); /* remove the argstr() result */ - - CHECKSTRSPACE(SPACE_NEEDED, expdest); /* nb: stack block might move */ - fmtstr(expdest, SPACE_NEEDED, "%"PRIdMAX, result); - - for (q = expdest; *q++ != '\0'; ) /* find end of what we added */ - ; - - if (quoted == 0) /* allow weird splitting */ - recordregion(begoff, begoff + q - 1 - expdest, 0); - adjustment = q - expdest - 1; - STADJUST(adjustment, expdest); /* move expdest to end */ - VTRACE(DBG_EXPAND, ("expari: adding %d \"%s\", returning \"%.5s...\"\n", - adjustment, stackblock() + begoff, p)); - - return p; -} - - -/* - * Expand stuff in backwards quotes (these days, any command substitution). - */ - -STATIC void -expbackq(union node *cmd, int quoted, int flag) -{ - struct backcmd in; - int i; - char buf[128]; - char *p; - char *dest = expdest; /* expdest may be reused by eval, use an alt */ - struct ifsregion saveifs, *savelastp; - struct nodelist *saveargbackq; - char lastc; - int startloc = dest - stackblock(); - int saveherefd; - const int quotes = flag & EXP_QNEEDED; - int nnl; - struct stackmark smark; - - VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x) have %d\n", - quoted, flag, startloc)); - INTOFF; - saveifs = ifsfirst; - savelastp = ifslastp; - saveargbackq = argbackq; - saveherefd = herefd; - herefd = -1; - - setstackmark(&smark); /* preserve the stack */ - p = grabstackstr(dest); /* save what we have there currently */ - evalbackcmd(cmd, &in); /* evaluate the $( ) tree (using stack) */ - popstackmark(&smark); /* and return stack to when we entered */ - - ifsfirst = saveifs; - ifslastp = savelastp; - argbackq = saveargbackq; - herefd = saveherefd; - - p = in.buf; /* now extract the results */ - nnl = 0; /* dropping trailing \n's */ - for (;;) { - if (--in.nleft < 0) { - if (in.fd < 0) - break; - INTON; - while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR) - continue; - INTOFF; - VTRACE(DBG_EXPAND, ("expbackq: read returns %d\n", i)); - if (i <= 0) - break; - p = buf; - in.nleft = i - 1; - } - lastc = *p++; - if (lastc != '\0') { - if (lastc == '\n') /* don't save \n yet */ - nnl++; /* it might be trailing */ - else { - /* - * We have something other than \n - * - * Before saving it, we need to insert - * any \n's that we have just skipped. - */ - - /* XXX - * this hack is just because our - * CHECKSTRSPACE() is lazy, and only - * ever grows the stack once, even - * if that does not allocate the space - * we requested. ie: safe for small - * requests, but not large ones. - * FIXME someday... - */ - if (nnl < 20) { - CHECKSTRSPACE(nnl + 2, dest); - while (nnl > 0) { - nnl--; - USTPUTC('\n', dest); - } - } else { - /* The slower, safer, way */ - while (nnl > 0) { - nnl--; - STPUTC('\n', dest); - } - CHECKSTRSPACE(2, dest); - } - if (quotes && quoted && NEEDESC(lastc)) - USTPUTC(CTLESC, dest); - USTPUTC(lastc, dest); - } - } - } - - if (in.fd >= 0) - close(in.fd); - if (in.buf) - ckfree(in.buf); - if (in.jp) - back_exitstatus = waitforjob(in.jp); - if (quoted == 0) - recordregion(startloc, dest - stackblock(), 0); - CTRACE(DBG_EXPAND, ("evalbackq: size=%d: \"%.*s\"\n", - (int)((dest - stackblock()) - startloc), - (int)((dest - stackblock()) - startloc), - stackblock() + startloc)); - - expdest = dest; /* all done, expdest is all ours again */ - INTON; -} - - -STATIC int -subevalvar(const char *p, const char *str, int subtype, int startloc, - int varflags) -{ - char *startp; - int saveherefd = herefd; - struct nodelist *saveargbackq = argbackq; - int amount; - - herefd = -1; - VTRACE(DBG_EXPAND, ("subevalvar(%d) \"%.20s\" ${%.*s} sloc=%d vf=%x\n", - subtype, p, p-str, str, startloc, varflags)); - argstr(p, subtype == VSASSIGN ? EXP_VARTILDE : EXP_TILDE); - STACKSTRNUL(expdest); - herefd = saveherefd; - argbackq = saveargbackq; - startp = stackblock() + startloc; - - switch (subtype) { - case VSASSIGN: - setvar(str, startp, 0); - amount = startp - expdest; /* remove what argstr added */ - STADJUST(amount, expdest); - varflags &= ~VSNUL; /*XXX Huh? What's that achieve? */ - return 1; /* go back and eval var again */ - - case VSQUESTION: - if (*p != CTLENDVAR) { - outfmt(&errout, "%s\n", startp); - error(NULL); - } - error("%.*s: parameter %snot set", - (int)(p - str - 1), - str, (varflags & VSNUL) ? "null or " - : nullstr); - /* NOTREACHED */ - - default: - abort(); - } -} - -STATIC int -subevalvar_trim(const char *p, int strloc, int subtype, int startloc, - int varflags, int quotes) -{ - char *startp; - char *str; - char *loc = NULL; - char *q; - int c = 0; - int saveherefd = herefd; - struct nodelist *saveargbackq = argbackq; - int amount; - - herefd = -1; - switch (subtype) { - case VSTRIMLEFT: - case VSTRIMLEFTMAX: - case VSTRIMRIGHT: - case VSTRIMRIGHTMAX: - break; - default: - abort(); - break; - } - - VTRACE(DBG_EXPAND, - ("subevalvar_trim(\"%.9s\", STR@%d, SUBT=%d, start@%d, vf=%x, q=%x)\n", - p, strloc, subtype, startloc, varflags, quotes)); - - argstr(p, (varflags & (VSQUOTE|VSPATQ)) == VSQUOTE ? 0 : EXP_CASE); - STACKSTRNUL(expdest); - herefd = saveherefd; - argbackq = saveargbackq; - startp = stackblock() + startloc; - str = stackblock() + strloc; - - switch (subtype) { - - case VSTRIMLEFT: - for (loc = startp; loc < str; loc++) { - c = *loc; - *loc = '\0'; - if (patmatch(str, startp, quotes)) - goto recordleft; - *loc = c; - if (quotes && *loc == CTLESC) - loc++; - } - return 0; - - case VSTRIMLEFTMAX: - for (loc = str - 1; loc >= startp;) { - c = *loc; - *loc = '\0'; - if (patmatch(str, startp, quotes)) - goto recordleft; - *loc = c; - loc--; - if (quotes && loc > startp && - *(loc - 1) == CTLESC) { - for (q = startp; q < loc; q++) - if (*q == CTLESC) - q++; - if (q > loc) - loc--; - } - } - return 0; - - case VSTRIMRIGHT: - for (loc = str - 1; loc >= startp;) { - if (patmatch(str, loc, quotes)) - goto recordright; - loc--; - if (quotes && loc > startp && - *(loc - 1) == CTLESC) { - for (q = startp; q < loc; q++) - if (*q == CTLESC) - q++; - if (q > loc) - loc--; - } - } - return 0; - - case VSTRIMRIGHTMAX: - for (loc = startp; loc < str - 1; loc++) { - if (patmatch(str, loc, quotes)) - goto recordright; - if (quotes && *loc == CTLESC) - loc++; - } - return 0; - - default: - abort(); - } - - recordleft: - *loc = c; - amount = ((str - 1) - (loc - startp)) - expdest; - STADJUST(amount, expdest); - while (loc != str - 1) - *startp++ = *loc++; - return 1; - - recordright: - amount = loc - expdest; - STADJUST(amount, expdest); - STPUTC('\0', expdest); - STADJUST(-1, expdest); - return 1; -} - - -/* - * Expand a variable, and return a pointer to the next character in the - * input string. - */ - -STATIC const char * -evalvar(const char *p, int flag) -{ - int subtype; - int varflags; - const char *var; - char *val; - int patloc; - int c; - int set; - int special; - int startloc; - int varlen; - int apply_ifs; - const int quotes = flag & EXP_QNEEDED; - - varflags = (unsigned char)*p++; - subtype = varflags & VSTYPE; - var = p; - special = !is_name(*p); - p = strchr(p, '=') + 1; - - CTRACE(DBG_EXPAND, - ("evalvar \"%.*s\", flag=%#X quotes=%#X vf=%#X subtype=%X\n", - p - var - 1, var, flag, quotes, varflags, subtype)); - - again: /* jump here after setting a variable with ${var=text} */ - if (varflags & VSLINENO) { - if (line_num.flags & VUNSET) { - set = 0; - val = NULL; - } else { - set = 1; - special = p - var; - val = NULL; - } - } else if (special) { - set = varisset(var, varflags & VSNUL); - val = NULL; - } else { - val = lookupvar(var); - if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { - val = NULL; - set = 0; - } else - set = 1; - } - - varlen = 0; - startloc = expdest - stackblock(); - - if (!set && uflag && *var != '@' && *var != '*') { - switch (subtype) { - case VSNORMAL: - case VSTRIMLEFT: - case VSTRIMLEFTMAX: - case VSTRIMRIGHT: - case VSTRIMRIGHTMAX: - case VSLENGTH: - error("%.*s: parameter not set", - (int)(p - var - 1), var); - /* NOTREACHED */ - } - } - - if (!set && subtype != VSPLUS && special && *var == '@') - if (startloc > 0 && expdest[-1] == CTLQUOTEMARK) - expdest--, startloc--; - - if (set && subtype != VSPLUS) { - /* insert the value of the variable */ - if (special) { - if (varflags & VSLINENO) { - /* - * The LINENO hack (expansion part) - */ - while (--special > 0) { -/* not needed, it is a number... - if (quotes && NEEDESC(*var)) - STPUTC(CTLESC, expdest); -*/ - STPUTC(*var++, expdest); - } - } else - varvalue(var, varflags&VSQUOTE, subtype, flag); - if (subtype == VSLENGTH) { - varlen = expdest - stackblock() - startloc; - STADJUST(-varlen, expdest); - } - } else { - - if (subtype == VSLENGTH) { - for (;*val; val++) - varlen++; - } else if (quotes && varflags & VSQUOTE) { - for (; (c = *val) != '\0'; val++) { - if (NEEDESC(c)) - STPUTC(CTLESC, expdest); - STPUTC(c, expdest); - } - } else { - while (*val) - STPUTC(*val++, expdest); - } - } - } - - - if (varflags & VSQUOTE) { - if (*var == '@' && shellparam.nparam != 1) - apply_ifs = 1; - else { - /* - * Mark so that we don't apply IFS if we recurse through - * here expanding $bar from "${foo-$bar}". - */ - flag |= EXP_IN_QUOTES; - apply_ifs = 0; - } - } else if (flag & EXP_IN_QUOTES) { - apply_ifs = 0; - } else - apply_ifs = 1; - - switch (subtype) { - case VSLENGTH: - expdest = cvtnum(varlen, expdest); - break; - - case VSNORMAL: - break; - - case VSPLUS: - set = !set; - /* FALLTHROUGH */ - case VSMINUS: - if (!set) { - argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0)); - /* - * ${x-a b c} doesn't get split, but removing the - * 'apply_ifs = 0' apparently breaks ${1+"$@"}.. - * ${x-'a b' c} should generate 2 args. - */ - if (*p != CTLENDVAR) - /* We should have marked stuff already */ - apply_ifs = 0; - } - break; - - case VSTRIMLEFT: - case VSTRIMLEFTMAX: - case VSTRIMRIGHT: - case VSTRIMRIGHTMAX: - if (!set) { - set = 1; /* allow argbackq to be advanced if needed */ - break; - } - /* - * Terminate the string and start recording the pattern - * right after it - */ - STPUTC('\0', expdest); - patloc = expdest - stackblock(); - if (subevalvar_trim(p, patloc, subtype, startloc, varflags, - quotes) == 0) { - int amount = (expdest - stackblock() - patloc) + 1; - STADJUST(-amount, expdest); - } - /* Remove any recorded regions beyond start of variable */ - removerecordregions(startloc); - apply_ifs = 1; - break; - - case VSASSIGN: - case VSQUESTION: - if (set) - break; - if (subevalvar(p, var, subtype, startloc, varflags)) { - /* if subevalvar() returns, it always returns 1 */ - - varflags &= ~VSNUL; - /* - * Remove any recorded regions beyond - * start of variable - */ - removerecordregions(startloc); - goto again; - } - apply_ifs = 0; /* never executed */ - break; - - default: - abort(); - } - - if (apply_ifs) - recordregion(startloc, expdest - stackblock(), - varflags & VSQUOTE); - - if (subtype != VSNORMAL) { /* skip to end of alternative */ - int nesting = 1; - for (;;) { - if ((c = *p++) == CTLESC) - p++; - else if (c == CTLNONL) - ; - else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { - if (set) - argbackq = argbackq->next; - } else if (c == CTLVAR) { - if ((*p++ & VSTYPE) != VSNORMAL) - nesting++; - } else if (c == CTLENDVAR) { - if (--nesting == 0) - break; - } - } - } - return p; -} - - - -/* - * Test whether a special parameter is set. - */ - -STATIC int -varisset(const char *name, int nulok) -{ - if (*name == '!') - return backgndpid != -1; - else if (*name == '@' || *name == '*') { - if (*shellparam.p == NULL) - return 0; - - if (nulok) { - char **av; - - for (av = shellparam.p; *av; av++) - if (**av != '\0') - return 1; - return 0; - } - } else if (is_digit(*name)) { - char *ap; - long num; - - /* - * handle overflow sensibly (the *ap tests should never fail) - */ - errno = 0; - num = strtol(name, &ap, 10); - if (errno != 0 || (*ap != '\0' && *ap != '=')) - return 0; - - if (num == 0) - ap = arg0; - else if (num > shellparam.nparam) - return 0; - else - ap = shellparam.p[num - 1]; - - if (nulok && (ap == NULL || *ap == '\0')) - return 0; - } - return 1; -} - - - -/* - * Add the value of a specialized variable to the stack string. - */ - -STATIC void -varvalue(const char *name, int quoted, int subtype, int flag) -{ - int num; - char *p; - int i; - int sep; - char **ap; -#ifdef DEBUG - char *start = expdest; -#endif - - VTRACE(DBG_EXPAND, ("varvalue(%c%s, sub=%d, fl=%#x)", *name, - quoted ? ", quoted" : "", subtype, flag)); - - if (subtype == VSLENGTH) /* no magic required ... */ - flag &= ~EXP_FULL; - -#define STRTODEST(p) \ - do {\ - if ((flag & EXP_QNEEDED) && quoted) { \ - while (*p) { \ - if (NEEDESC(*p)) \ - STPUTC(CTLESC, expdest); \ - STPUTC(*p++, expdest); \ - } \ - } else \ - while (*p) \ - STPUTC(*p++, expdest); \ - } while (0) - - - switch (*name) { - case '$': - num = rootpid; - break; - case '?': - num = exitstatus; - break; - case '#': - num = shellparam.nparam; - break; - case '!': - num = backgndpid; - break; - case '-': - for (i = 0; i < option_flags; i++) { - if (optlist[optorder[i]].val) - STPUTC(optlist[optorder[i]].letter, expdest); - } - VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); - return; - case '@': - if (flag & EXP_SPLIT && quoted) { - VTRACE(DBG_EXPAND, (": $@ split (%d)\n", - shellparam.nparam)); - /* GROSS HACK */ - if (shellparam.nparam == 0 && - expdest[-1] == CTLQUOTEMARK) - expdest--; - /* KCAH SSORG */ - for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { - STRTODEST(p); - if (*ap) - /* A NUL separates args inside "" */ - STPUTC('\0', expdest); - } - return; - } - /* fall through */ - case '*': - sep = ifsval()[0]; - for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { - STRTODEST(p); - if (!*ap) - break; - if (sep) { - if (quoted && (flag & EXP_QNEEDED) && - NEEDESC(sep)) - STPUTC(CTLESC, expdest); - STPUTC(sep, expdest); - } else - if ((flag & (EXP_SPLIT|EXP_IN_QUOTES)) == EXP_SPLIT - && !quoted && **ap != '\0') - STPUTC('\0', expdest); - } - VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); - return; - default: - if (is_digit(*name)) { - long lnum; - - errno = 0; - lnum = strtol(name, &p, 10); - if (errno != 0 || (*p != '\0' && *p != '=')) - return; - - if (lnum == 0) - p = arg0; - else if (lnum > 0 && lnum <= shellparam.nparam) - p = shellparam.p[lnum - 1]; - else - return; - STRTODEST(p); - } - VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); - return; - } - /* - * only the specials with an int value arrive here - */ - VTRACE(DBG_EXPAND, ("(%d)", num)); - expdest = cvtnum(num, expdest); - VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); -} - - - -/* - * Record the fact that we have to scan this region of the - * string for IFS characters. - */ - -STATIC void -recordregion(int start, int end, int inquotes) -{ - struct ifsregion *ifsp; - - VTRACE(DBG_EXPAND, ("recordregion(%d,%d,%d)\n", start, end, inquotes)); - if (ifslastp == NULL) { - ifsp = &ifsfirst; - } else { - if (ifslastp->endoff == start - && ifslastp->inquotes == inquotes) { - /* extend previous area */ - ifslastp->endoff = end; - return; - } - ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); - ifslastp->next = ifsp; - } - ifslastp = ifsp; - ifslastp->next = NULL; - ifslastp->begoff = start; - ifslastp->endoff = end; - ifslastp->inquotes = inquotes; -} - - - -/* - * Break the argument string into pieces based upon IFS and add the - * strings to the argument list. The regions of the string to be - * searched for IFS characters have been stored by recordregion. - */ -STATIC void -ifsbreakup(char *string, struct arglist *arglist) -{ - struct ifsregion *ifsp; - struct strlist *sp; - char *start; - char *p; - char *q; - const char *ifs; - const char *ifsspc; - int had_param_ch = 0; - - start = string; - - VTRACE(DBG_EXPAND, ("ifsbreakup(\"%s\")", string)); /* misses \0's */ - if (ifslastp == NULL) { - /* Return entire argument, IFS doesn't apply to any of it */ - VTRACE(DBG_EXPAND, ("no regions\n", string)); - sp = stalloc(sizeof(*sp)); - sp->text = start; - *arglist->lastp = sp; - arglist->lastp = &sp->next; - return; - } - - ifs = ifsval(); - - for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { - p = string + ifsp->begoff; - VTRACE(DBG_EXPAND, (" !%.*s!(%d)", ifsp->endoff-ifsp->begoff, - p, ifsp->endoff-ifsp->begoff)); - while (p < string + ifsp->endoff) { - had_param_ch = 1; - q = p; - if (IS_BORING(*p)) { - p++; - continue; - } - if (*p == CTLESC) - p++; - if (ifsp->inquotes) { - /* Only NULs (should be from "$@") end args */ - if (*p != 0) { - p++; - continue; - } - ifsspc = NULL; - VTRACE(DBG_EXPAND, (" \\0 nxt:\"%s\" ", p)); - } else { - if (!strchr(ifs, *p)) { - p++; - continue; - } - had_param_ch = 0; - ifsspc = strchr(" \t\n", *p); - - /* Ignore IFS whitespace at start */ - if (q == start && ifsspc != NULL) { - p++; - start = p; - continue; - } - } - - /* Save this argument... */ - *q = '\0'; - VTRACE(DBG_EXPAND, ("<%s>", start)); - sp = stalloc(sizeof(*sp)); - sp->text = start; - *arglist->lastp = sp; - arglist->lastp = &sp->next; - p++; - - if (ifsspc != NULL) { - /* Ignore further trailing IFS whitespace */ - for (; p < string + ifsp->endoff; p++) { - q = p; - if (*p == CTLNONL) - continue; - if (*p == CTLESC) - p++; - if (strchr(ifs, *p) == NULL) { - p = q; - break; - } - if (strchr(" \t\n", *p) == NULL) { - p++; - break; - } - } - } - start = p; - } - } - - while (*start == CTLQUOTEEND) - start++; - - /* - * Save anything left as an argument. - * Traditionally we have treated 'IFS=':'; set -- x$IFS' as - * generating 2 arguments, the second of which is empty. - * Some recent clarification of the Posix spec say that it - * should only generate one.... - */ - if (had_param_ch || *start != 0) { - VTRACE(DBG_EXPAND, (" T<%s>", start)); - sp = stalloc(sizeof(*sp)); - sp->text = start; - *arglist->lastp = sp; - arglist->lastp = &sp->next; - } - VTRACE(DBG_EXPAND, ("\n")); -} - -STATIC void -ifsfree(void) -{ - while (ifsfirst.next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifsfirst.next->next; - ckfree(ifsfirst.next); - ifsfirst.next = ifsp; - INTON; - } - ifslastp = NULL; - ifsfirst.next = NULL; -} - - - -/* - * Expand shell metacharacters. At this point, the only control characters - * should be escapes. The results are stored in the list exparg. - */ - -char *expdir; - - -STATIC void -expandmeta(struct strlist *str, int flag) -{ - char *p; - struct strlist **savelastp; - struct strlist *sp; - char c; - /* TODO - EXP_REDIR */ - - while (str) { - p = str->text; - for (;;) { /* fast check for meta chars */ - if ((c = *p++) == '\0') - goto nometa; - if (c == '*' || c == '?' || c == '[' /* || c == '!' */) - break; - } - savelastp = exparg.lastp; - INTOFF; - if (expdir == NULL) { - int i = strlen(str->text); - expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ - } - - expmeta(expdir, str->text); - ckfree(expdir); - expdir = NULL; - INTON; - if (exparg.lastp == savelastp) { - /* - * no matches - */ - nometa: - *exparg.lastp = str; - rmescapes(str->text); - exparg.lastp = &str->next; - } else { - *exparg.lastp = NULL; - *savelastp = sp = expsort(*savelastp); - while (sp->next != NULL) - sp = sp->next; - exparg.lastp = &sp->next; - } - str = str->next; - } -} - -STATIC void -add_args(struct strlist *str) -{ - while (str) { - *exparg.lastp = str; - rmescapes(str->text); - exparg.lastp = &str->next; - str = str->next; - } -} - - -/* - * Do metacharacter (i.e. *, ?, [...]) expansion. - */ - -STATIC void -expmeta(char *enddir, char *name) -{ - char *p; - const char *cp; - char *q; - char *start; - char *endname; - int metaflag; - struct stat statb; - DIR *dirp; - struct dirent *dp; - int atend; - int matchdot; - - CTRACE(DBG_EXPAND|DBG_MATCH, ("expmeta(\"%s\")\n", name)); - metaflag = 0; - start = name; - for (p = name ; ; p++) { - if (*p == '*' || *p == '?') - metaflag = 1; - else if (*p == '[') { - q = p + 1; - if (*q == '!' || *q == '^') - q++; - for (;;) { - while (IS_BORING(*q)) - q++; - if (*q == ']') { - q++; - metaflag = 1; - break; - } - if (*q == '[' && q[1] == ':') { - /* - * character class, look for :] ending - * also stop on ']' (end bracket expr) - * or '\0' or '/' (end pattern) - */ - while (*++q != '\0' && *q != ']' && - *q != '/') { - if (*q == CTLESC) { - if (*++q == '\0') - break; - if (*q == '/') - break; - } else if (*q == ':' && - q[1] == ']') - break; - } - if (*q == ':') { - /* - * stopped at ':]' - * still in [...] - * skip ":]" and continue; - */ - q += 2; - continue; - } - - /* done at end of pattern, not [...] */ - if (*q == '\0' || *q == '/') - break; - - /* found the ']', we have a [...] */ - metaflag = 1; - q++; /* skip ']' */ - break; - } - if (*q == CTLESC) - q++; - /* end of pattern cannot be escaped */ - if (*q == '/' || *q == '\0') - break; - q++; - } - } else if (*p == '\0') - break; - else if (IS_BORING(*p)) - continue; - else if (*p == CTLESC) - p++; - if (*p == '/') { - if (metaflag) - break; - start = p + 1; - } - } - if (metaflag == 0) { /* we've reached the end of the file name */ - if (enddir != expdir) - metaflag++; - for (p = name ; ; p++) { - if (IS_BORING(*p)) - continue; - if (*p == CTLESC) - p++; - *enddir++ = *p; - if (*p == '\0') - break; - } - if (metaflag == 0 || lstat(expdir, &statb) >= 0) - addfname(expdir); - return; - } - endname = p; - if (start != name) { - p = name; - while (p < start) { - while (IS_BORING(*p)) - p++; - if (*p == CTLESC) - p++; - *enddir++ = *p++; - } - } - if (enddir == expdir) { - cp = "."; - } else if (enddir == expdir + 1 && *expdir == '/') { - cp = "/"; - } else { - cp = expdir; - enddir[-1] = '\0'; - } - if ((dirp = opendir(cp)) == NULL) - return; - if (enddir != expdir) - enddir[-1] = '/'; - if (*endname == 0) { - atend = 1; - } else { - atend = 0; - *endname++ = '\0'; - } - matchdot = 0; - p = start; - while (IS_BORING(*p)) - p++; - if (*p == CTLESC) - p++; - if (*p == '.') - matchdot++; - while (! int_pending() && (dp = readdir(dirp)) != NULL) { - if (dp->d_name[0] == '.' && ! matchdot) - continue; - if (patmatch(start, dp->d_name, 0)) { - if (atend) { - scopy(dp->d_name, enddir); - addfname(expdir); - } else { - for (p = enddir, cp = dp->d_name; - (*p++ = *cp++) != '\0';) - continue; - p[-1] = '/'; - expmeta(p, endname); - } - } - } - closedir(dirp); - if (! atend) - endname[-1] = '/'; -} - - -/* - * Add a file name to the list. - */ - -STATIC void -addfname(char *name) -{ - char *p; - struct strlist *sp; - - p = stalloc(strlen(name) + 1); - scopy(name, p); - sp = stalloc(sizeof(*sp)); - sp->text = p; - *exparg.lastp = sp; - exparg.lastp = &sp->next; -} - - -/* - * Sort the results of file name expansion. It calculates the number of - * strings to sort and then calls msort (short for merge sort) to do the - * work. - */ - -STATIC struct strlist * -expsort(struct strlist *str) -{ - int len; - struct strlist *sp; - - len = 0; - for (sp = str ; sp ; sp = sp->next) - len++; - return msort(str, len); -} - - -STATIC struct strlist * -msort(struct strlist *list, int len) -{ - struct strlist *p, *q = NULL; - struct strlist **lpp; - int half; - int n; - - if (len <= 1) - return list; - half = len >> 1; - p = list; - for (n = half ; --n >= 0 ; ) { - q = p; - p = p->next; - } - q->next = NULL; /* terminate first half of list */ - q = msort(list, half); /* sort first half of list */ - p = msort(p, len - half); /* sort second half */ - lpp = &list; - for (;;) { - if (strcmp(p->text, q->text) < 0) { - *lpp = p; - lpp = &p->next; - if ((p = *lpp) == NULL) { - *lpp = q; - break; - } - } else { - *lpp = q; - lpp = &q->next; - if ((q = *lpp) == NULL) { - *lpp = p; - break; - } - } - } - return list; -} - - -/* - * See if a character matches a character class, starting at the first colon - * of "[:class:]". - * If a valid character class is recognized, a pointer to the next character - * after the final closing bracket is stored into *end, otherwise a null - * pointer is stored into *end. - */ -static int -match_charclass(const char *p, wchar_t chr, const char **end) -{ - char name[20]; - char *nameend; - wctype_t cclass; - - *end = NULL; - p++; - nameend = strstr(p, ":]"); - if (nameend == NULL || nameend == p) /* not a valid class */ - return 0; - - if (!is_alpha(*p) || strspn(p, /* '_' is a local extension */ - "0123456789" "_" - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != (size_t)(nameend - p)) - return 0; - - *end = nameend + 2; /* committed to it being a char class */ - if ((size_t)(nameend - p) >= sizeof(name)) /* but too long */ - return 0; /* so no match */ - memcpy(name, p, nameend - p); - name[nameend - p] = '\0'; - cclass = wctype(name); - /* An unknown class matches nothing but is valid nevertheless. */ - if (cclass == 0) - return 0; - return iswctype(chr, cclass); -} - - -/* - * Returns true if the pattern matches the string. - */ - -STATIC int -patmatch(const char *pattern, const char *string, int squoted) -{ - const char *p, *q, *end; - const char *bt_p, *bt_q; - char c; - wchar_t wc, wc2; - - VTRACE(DBG_MATCH, ("patmatch(P=\"%s\", W=\"%s\"%s): ", - pattern, string, squoted ? ", SQ" : "")); - p = pattern; - q = string; - bt_p = NULL; - bt_q = NULL; - for (;;) { - switch (c = *p++) { - case '\0': - if (squoted && *q == CTLESC) { - if (q[1] == '\0') - q++; - } - if (*q != '\0') - goto backtrack; - VTRACE(DBG_MATCH, ("match\n")); - return 1; - case CTLESC: - if (squoted && *q == CTLESC) - q++; - if (*p == '\0' && *q == '\0') { - VTRACE(DBG_MATCH, ("match-\\\n")); - return 1; - } - if (*q++ != *p++) - goto backtrack; - break; - case '\\': - if (squoted && *q == CTLESC) - q++; - if (*q++ != *p++) - goto backtrack; - break; - case CTLQUOTEMARK: - case CTLQUOTEEND: - case CTLNONL: - continue; - case '?': - if (squoted && *q == CTLESC) - q++; - if (*q++ == '\0') { - VTRACE(DBG_MATCH, ("?fail\n")); - return 0; - } - break; - case '*': - c = *p; - while (c == CTLQUOTEMARK || c == '*') - c = *++p; - if (c != CTLESC && !IS_BORING(c) && - c != '?' && c != '*' && c != '[') { - while (*q != c) { - if (squoted && *q == CTLESC && - q[1] == c) - break; - if (*q == '\0') { - VTRACE(DBG_MATCH, ("*fail\n")); - return 0; - } - if (squoted && *q == CTLESC) - q++; - q++; - } - } - if (c == CTLESC && p[1] == '\0') { - VTRACE(DBG_MATCH, ("match+\\\n")); - return 1; - } - /* - * First try the shortest match for the '*' that - * could work. We can forget any earlier '*' since - * there is no way having it match more characters - * can help us, given that we are already here. - */ - bt_p = p; - bt_q = q; - break; - case '[': { - const char *savep, *saveq, *endp; - int invert, found; - unsigned char chr; - - /* - * First quick check to see if there is a - * possible matching ']' - if not, then this - * is not a char class, and the '[' is just - * a literal '['. - * - * This check will not detect all non classes, but - * that's OK - It just means that we execute the - * harder code sometimes when it it cannot succeed. - */ - endp = p; - if (*endp == '!' || *endp == '^') - endp++; - for (;;) { - while (IS_BORING(*endp)) - endp++; - if (*endp == '\0') - goto dft; /* no matching ] */ - if (*endp++ == ']') - break; - } - /* end shortcut */ - - invert = 0; - savep = p, saveq = q; - invert = 0; - if (*p == '!' || *p == '^') { - invert++; - p++; - } - found = 0; - if (*q == '\0') { - VTRACE(DBG_MATCH, ("[]fail\n")); - return 0; - } - if (squoted && *q == CTLESC) - q++; - chr = (unsigned char)*q++; - c = *p++; - do { - if (IS_BORING(c)) - continue; - if (c == '\0') { - p = savep, q = saveq; - c = '['; - goto dft; - } - if (c == '[' && *p == ':') { - found |= match_charclass(p, chr, &end); - if (end != NULL) { - p = end; - continue; - } - } - if (c == CTLESC || c == '\\') - c = *p++; - wc = (unsigned char)c; - if (*p == '-' && p[1] != ']') { - p++; - if (*p == CTLESC || *p == '\\') - p++; - wc2 = (unsigned char)*p++; - if ( collate_range_cmp(chr, wc) >= 0 - && collate_range_cmp(chr, wc2) <= 0 - ) - found = 1; - } else { - if (chr == wc) - found = 1; - } - } while ((c = *p++) != ']'); - if (found == invert) - goto backtrack; - break; - } - dft: default: - if (squoted && *q == CTLESC) - q++; - if (*q++ == c) - break; - backtrack: - /* - * If we have a mismatch (other than hitting the end - * of the string), go back to the last '*' seen and - * have it match one additional character. - */ - if (bt_p == NULL) { - VTRACE(DBG_MATCH, ("BTP fail\n")); - return 0; - } - if (*bt_q == '\0') { - VTRACE(DBG_MATCH, ("BTQ fail\n")); - return 0; - } - bt_q++; - p = bt_p; - q = bt_q; - break; - } - } -} - - - -/* - * Remove any CTLESC or CTLNONL characters from a string. - */ - -void -rmescapes(char *str) -{ - char *p, *q; - - p = str; - while (*p != CTLESC && !IS_BORING(*p)) { - if (*p++ == '\0') - return; - } - q = p; - while (*p) { - if (IS_BORING(*p)) { - p++; - continue; - } - if (*p == CTLCNL) { - p++; - *q++ = '\n'; - continue; - } - if (*p == CTLESC) - p++; - *q++ = *p++; - } - *q = '\0'; -} - -/* - * and a special version for dealing with expressions to be parsed - * by the arithmetic evaluator. That needs to be able to count \n's - * even ones that were \newline elided \n's, so we have to put the - * latter back into the string - just being careful to put them only - * at a place where white space can reasonably occur in the string - * -- then the \n we insert will just be white space, and ignored - * for all purposes except line counting. - */ - -void -rmescapes_nl(char *str) -{ - char *p, *q; - int nls = 0, holdnl = 0, holdlast; - - p = str; - while (*p != CTLESC && !IS_BORING(*p)) { - if (*p++ == '\0') - return; - } - if (p > str) /* must reprocess char before stopper (if any) */ - --p; /* so we do not place a \n badly */ - q = p; - while (*p) { - if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { - p++; - continue; - } - if (*p == CTLNONL) { - p++; - nls++; - continue; - } - if (*p == CTLCNL) { - p++; - *q++ = '\n'; - continue; - } - if (*p == CTLESC) - p++; - - holdlast = holdnl; - holdnl = is_in_name(*p); /* letters, digits, _ */ - if (q == str || is_space(q[-1]) || (*p != '=' && q[-1] != *p)) { - if (nls > 0 && holdnl != holdlast) { - while (nls > 0) - *q++ = '\n', nls--; - } - } - *q++ = *p++; - } - while (--nls >= 0) - *q++ = '\n'; - *q = '\0'; -} - - - -/* - * See if a pattern matches in a case statement. - */ - -int -casematch(union node *pattern, char *val) -{ - struct stackmark smark; - int result; - char *p; - - CTRACE(DBG_MATCH, ("casematch(P=\"%s\", W=\"%s\")\n", - pattern->narg.text, val)); - setstackmark(&smark); - argbackq = pattern->narg.backquote; - STARTSTACKSTR(expdest); - ifslastp = NULL; - argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); - STPUTC('\0', expdest); - p = grabstackstr(expdest); - result = patmatch(p, val, 0); - popstackmark(&smark); - return result; -} - -/* - * Our own itoa(). Assumes result buffer is on the stack - */ - -STATIC char * -cvtnum(int num, char *buf) -{ - char temp[32]; - int neg = num < 0; - char *p = temp + sizeof temp - 1; - - if (neg) - num = -num; - - *p = '\0'; - do { - *--p = num % 10 + '0'; - } while ((num /= 10) != 0 && p > temp + 1); - - if (neg) - *--p = '-'; - - while (*p) - STPUTC(*p++, buf); - return buf; -} - -/* - * Do most of the work for wordexp(3). - */ - -int -wordexpcmd(int argc, char **argv) -{ - size_t len; - int i; - - out1fmt("%d", argc - 1); - out1c('\0'); - for (i = 1, len = 0; i < argc; i++) - len += strlen(argv[i]); - out1fmt("%zu", len); - out1c('\0'); - for (i = 1; i < argc; i++) { - out1str(argv[i]); - out1c('\0'); - } - return (0); -} diff --git a/bin/sh/expand.h b/bin/sh/expand.h deleted file mode 100644 index d435fd8..0000000 --- a/bin/sh/expand.h +++ /dev/null @@ -1,71 +0,0 @@ -/* $NetBSD: expand.h,v 1.24 2018/11/18 17:23:37 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)expand.h 8.2 (Berkeley) 5/4/95 - */ - -#include <inttypes.h> - -struct strlist { - struct strlist *next; - char *text; -}; - - -struct arglist { - struct strlist *list; - struct strlist **lastp; -}; - -/* - * expandarg() flags - */ -#define EXP_SPLIT 0x1 /* perform word splitting */ -#define EXP_TILDE 0x2 /* do normal tilde expansion */ -#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ -#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */ -#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ -#define EXP_IFS_SPLIT 0x20 /* need to record arguments for ifs breakup */ -#define EXP_IN_QUOTES 0x40 /* don't set EXP_IFS_SPLIT again */ -#define EXP_GLOB 0x80 /* perform filename globbing */ -#define EXP_NL 0x100 /* keep CRTNONL in output */ - -#define EXP_FULL (EXP_SPLIT | EXP_GLOB) -#define EXP_QNEEDED (EXP_GLOB | EXP_CASE | EXP_REDIR) - -union node; - -void expandhere(union node *, int); -void expandarg(union node *, struct arglist *, int); -void rmescapes(char *); -int casematch(union node *, char *); diff --git a/bin/sh/funcs/cmv b/bin/sh/funcs/cmv deleted file mode 100644 index 0e3eef6..0000000 --- a/bin/sh/funcs/cmv +++ /dev/null @@ -1,43 +0,0 @@ -# $NetBSD: cmv,v 1.8 2016/02/29 23:50:59 christos Exp $ -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)cmv 8.2 (Berkeley) 5/4/95 - -# Conditional move--don't replace an existing file. - -cmv() { - if test $# != 2 - then echo "cmv: arg count" - return 2 - fi - if test -f "$2" -o -w "$2" - then echo "$2 exists" - return 2 - fi - /bin/mv "$1" "$2" -} diff --git a/bin/sh/funcs/dirs b/bin/sh/funcs/dirs deleted file mode 100644 index ef2ae0a..0000000 --- a/bin/sh/funcs/dirs +++ /dev/null @@ -1,67 +0,0 @@ -# $NetBSD: dirs,v 1.8 2016/02/29 23:50:59 christos Exp $ -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)dirs 8.2 (Berkeley) 5/4/95 - -# pushd, popd, and dirs --- written by Chris Bertin -# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris -# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW - -pushd () { - SAVE=`pwd` - if [ "$1" = "" ] - then if [ "$DSTACK" = "" ] - then echo "pushd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 || return - shift 1 - DSTACK="$*" - else cd $1 > /dev/null || return - fi - DSTACK="$SAVE $DSTACK" - dirs -} - -popd () { - if [ "$DSTACK" = "" ] - then echo "popd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 - shift - DSTACK=$* - dirs -} - -dirs () { - echo "`pwd` $DSTACK" - return 0 -} diff --git a/bin/sh/funcs/kill b/bin/sh/funcs/kill deleted file mode 100644 index 70f2b27..0000000 --- a/bin/sh/funcs/kill +++ /dev/null @@ -1,43 +0,0 @@ -# $NetBSD: kill,v 1.8 2016/02/29 23:50:59 christos Exp $ -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)kill 8.2 (Berkeley) 5/4/95 - -# Convert job names to process ids and then run /bin/kill. - -kill() { - local args x - args= - for x in "$@" - do case $x in - %*) x=`jobid "$x"` ;; - esac - args="$args $x" - done - /bin/kill $args -} diff --git a/bin/sh/funcs/login b/bin/sh/funcs/login deleted file mode 100644 index a2fe60e..0000000 --- a/bin/sh/funcs/login +++ /dev/null @@ -1,32 +0,0 @@ -# $NetBSD: login,v 1.8 2016/02/29 23:50:59 christos Exp $ -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)login 8.2 (Berkeley) 5/4/95 - -# replaces the login builtin in the BSD shell -login () exec login "$@" diff --git a/bin/sh/funcs/newgrp b/bin/sh/funcs/newgrp deleted file mode 100644 index 1ad46a3..0000000 --- a/bin/sh/funcs/newgrp +++ /dev/null @@ -1,31 +0,0 @@ -# $NetBSD: newgrp,v 1.8 2016/02/29 23:50:59 christos Exp $ -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)newgrp 8.2 (Berkeley) 5/4/95 - -newgrp() exec newgrp "$@" diff --git a/bin/sh/funcs/popd b/bin/sh/funcs/popd deleted file mode 100644 index 836741c..0000000 --- a/bin/sh/funcs/popd +++ /dev/null @@ -1,67 +0,0 @@ -# $NetBSD: popd,v 1.8 2016/02/29 23:50:59 christos Exp $ -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)popd 8.2 (Berkeley) 5/4/95 - -# pushd, popd, and dirs --- written by Chris Bertin -# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris -# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW - -pushd () { - SAVE=`pwd` - if [ "$1" = "" ] - then if [ "$DSTACK" = "" ] - then echo "pushd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 || return - shift 1 - DSTACK="$*" - else cd $1 > /dev/null || return - fi - DSTACK="$SAVE $DSTACK" - dirs -} - -popd () { - if [ "$DSTACK" = "" ] - then echo "popd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 - shift - DSTACK=$* - dirs -} - -dirs () { - echo "`pwd` $DSTACK" - return 0 -} diff --git a/bin/sh/funcs/pushd b/bin/sh/funcs/pushd deleted file mode 100644 index c6ec1af..0000000 --- a/bin/sh/funcs/pushd +++ /dev/null @@ -1,67 +0,0 @@ -# $NetBSD: pushd,v 1.8 2016/02/29 23:50:59 christos Exp $ -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)pushd 8.2 (Berkeley) 5/4/95 - -# pushd, popd, and dirs --- written by Chris Bertin -# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris -# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW - -pushd () { - SAVE=`pwd` - if [ "$1" = "" ] - then if [ "$DSTACK" = "" ] - then echo "pushd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 || return - shift 1 - DSTACK="$*" - else cd $1 > /dev/null || return - fi - DSTACK="$SAVE $DSTACK" - dirs -} - -popd () { - if [ "$DSTACK" = "" ] - then echo "popd: directory stack empty." - return 1 - fi - set $DSTACK - cd $1 - shift - DSTACK=$* - dirs -} - -dirs () { - echo "`pwd` $DSTACK" - return 0 -} diff --git a/bin/sh/funcs/suspend b/bin/sh/funcs/suspend deleted file mode 100644 index 7643f43..0000000 --- a/bin/sh/funcs/suspend +++ /dev/null @@ -1,35 +0,0 @@ -# $NetBSD: suspend,v 1.8 2016/02/29 23:50:59 christos Exp $ -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)suspend 8.2 (Berkeley) 5/4/95 - -suspend() { - local - - set +j - kill -TSTP 0 -} diff --git a/bin/sh/histedit.c b/bin/sh/histedit.c deleted file mode 100644 index 5de3adc..0000000 --- a/bin/sh/histedit.c +++ /dev/null @@ -1,576 +0,0 @@ -/* $NetBSD: histedit.c,v 1.53 2018/07/13 22:43:44 kre Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: histedit.c,v 1.53 2018/07/13 22:43:44 kre Exp $"); -#endif -#endif /* not lint */ - -#include <sys/param.h> -#include <paths.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -/* - * Editline and history functions (and glue). - */ -#include "shell.h" -#include "parser.h" -#include "var.h" -#include "options.h" -#include "builtins.h" -#include "main.h" -#include "output.h" -#include "mystring.h" -#include "myhistedit.h" -#include "error.h" -#include "alias.h" -#ifndef SMALL -#include "eval.h" -#include "memalloc.h" - -#define MAXHISTLOOPS 4 /* max recursions through fc */ -#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ - -History *hist; /* history cookie */ -EditLine *el; /* editline cookie */ -int displayhist; -static FILE *el_in, *el_out; -unsigned char _el_fn_complete(EditLine *, int); - -STATIC const char *fc_replace(const char *, char *, char *); - -#ifdef DEBUG -extern FILE *tracefile; -#endif - -/* - * Set history and editing status. Called whenever the status may - * have changed (figures out what to do). - */ -void -histedit(void) -{ - FILE *el_err; - -#define editing (Eflag || Vflag) - - if (iflag == 1) { - if (!hist) { - /* - * turn history on - */ - INTOFF; - hist = history_init(); - INTON; - - if (hist != NULL) - sethistsize(histsizeval()); - else - out2str("sh: can't initialize history\n"); - } - if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ - /* - * turn editing on - */ - char *term, *shname; - - INTOFF; - if (el_in == NULL) - el_in = fdopen(0, "r"); - if (el_out == NULL) - el_out = fdopen(2, "w"); - if (el_in == NULL || el_out == NULL) - goto bad; - el_err = el_out; -#if DEBUG - if (tracefile) - el_err = tracefile; -#endif - term = lookupvar("TERM"); - if (term) - setenv("TERM", term, 1); - else - unsetenv("TERM"); - shname = arg0; - if (shname[0] == '-') - shname++; - el = el_init(shname, el_in, el_out, el_err); - if (el != NULL) { - if (hist) - el_set(el, EL_HIST, history, hist); - - set_prompt_lit(lookupvar("PSlit")); - el_set(el, EL_SIGNAL, 1); - el_set(el, EL_ALIAS_TEXT, alias_text, NULL); - el_set(el, EL_ADDFN, "rl-complete", - "ReadLine compatible completion function", - _el_fn_complete); - } else { -bad: - out2str("sh: can't initialize editing\n"); - } - INTON; - } else if (!editing && el) { - INTOFF; - el_end(el); - el = NULL; - INTON; - } - if (el) { - if (Vflag) - el_set(el, EL_EDITOR, "vi"); - else if (Eflag) - el_set(el, EL_EDITOR, "emacs"); - el_set(el, EL_BIND, "^I", - tabcomplete ? "rl-complete" : "ed-insert", NULL); - el_source(el, lookupvar("EDITRC")); - } - } else { - INTOFF; - if (el) { /* no editing if not interactive */ - el_end(el); - el = NULL; - } - if (hist) { - history_end(hist); - hist = NULL; - } - INTON; - } -} - -void -set_prompt_lit(const char *lit_ch) -{ - wchar_t wc; - - if (!(iflag && editing && el)) - return; - - if (lit_ch == NULL) { - el_set(el, EL_PROMPT, getprompt); - return; - } - - mbtowc(&wc, NULL, 1); /* state init */ - - if (mbtowc(&wc, lit_ch, strlen(lit_ch)) <= 0) - el_set(el, EL_PROMPT, getprompt); - else - el_set(el, EL_PROMPT_ESC, getprompt, (int)wc); -} - -void -set_editrc(const char *fname) -{ - if (iflag && editing && el) - el_source(el, fname); -} - -void -sethistsize(const char *hs) -{ - int histsize; - HistEvent he; - - if (hist != NULL) { - if (hs == NULL || *hs == '\0' || *hs == '-' || - (histsize = number(hs)) < 0) - histsize = 100; - history(hist, &he, H_SETSIZE, histsize); - history(hist, &he, H_SETUNIQUE, 1); - } -} - -void -setterm(const char *term) -{ - if (el != NULL && term != NULL) - if (el_set(el, EL_TERMINAL, term) != 0) { - outfmt(out2, "sh: Can't set terminal type %s\n", term); - outfmt(out2, "sh: Using dumb terminal settings.\n"); - } -} - -int -inputrc(int argc, char **argv) -{ - if (argc != 2) { - out2str("usage: inputrc file\n"); - return 1; - } - if (el != NULL) { - if (el_source(el, argv[1])) { - out2str("inputrc: failed\n"); - return 1; - } else - return 0; - } else { - out2str("sh: inputrc ignored, not editing\n"); - return 1; - } -} - -/* - * This command is provided since POSIX decided to standardize - * the Korn shell fc command. Oh well... - */ -int -histcmd(volatile int argc, char ** volatile argv) -{ - int ch; - const char * volatile editor = NULL; - HistEvent he; - volatile int lflg = 0, nflg = 0, rflg = 0, sflg = 0; - int i, retval; - const char *firststr, *laststr; - int first, last, direction; - char * volatile pat = NULL, * volatile repl; /* ksh "fc old=new" crap */ - static int active = 0; - struct jmploc jmploc; - struct jmploc *volatile savehandler; - char editfile[MAXPATHLEN + 1]; - FILE * volatile efp; -#ifdef __GNUC__ - repl = NULL; /* XXX gcc4 */ - efp = NULL; /* XXX gcc4 */ -#endif - - if (hist == NULL) - error("history not active"); - - if (argc == 1) - error("missing history argument"); - - optreset = 1; optind = 1; /* initialize getopt */ - while (not_fcnumber(argv[optind]) && - (ch = getopt(argc, argv, ":e:lnrs")) != -1) - switch ((char)ch) { - case 'e': - editor = optionarg; - break; - case 'l': - lflg = 1; - break; - case 'n': - nflg = 1; - break; - case 'r': - rflg = 1; - break; - case 's': - sflg = 1; - break; - case ':': - error("option -%c expects argument", optopt); - /* NOTREACHED */ - case '?': - default: - error("unknown option: -%c", optopt); - /* NOTREACHED */ - } - argc -= optind, argv += optind; - - /* - * If executing... - */ - if (lflg == 0 || editor || sflg) { - lflg = 0; /* ignore */ - editfile[0] = '\0'; - /* - * Catch interrupts to reset active counter and - * cleanup temp files. - */ - savehandler = handler; - if (setjmp(jmploc.loc)) { - active = 0; - if (*editfile) - unlink(editfile); - handler = savehandler; - longjmp(handler->loc, 1); - } - handler = &jmploc; - if (++active > MAXHISTLOOPS) { - active = 0; - displayhist = 0; - error("called recursively too many times"); - } - /* - * Set editor. - */ - if (sflg == 0) { - if (editor == NULL && - (editor = bltinlookup("FCEDIT", 1)) == NULL && - (editor = bltinlookup("EDITOR", 1)) == NULL) - editor = DEFEDITOR; - if (editor[0] == '-' && editor[1] == '\0') { - sflg = 1; /* no edit */ - editor = NULL; - } - } - } - - /* - * If executing, parse [old=new] now - */ - if (lflg == 0 && argc > 0 && - ((repl = strchr(argv[0], '=')) != NULL)) { - pat = argv[0]; - *repl++ = '\0'; - argc--, argv++; - } - - /* - * If -s is specified, accept only one operand - */ - if (sflg && argc >= 2) - error("too many args"); - - /* - * determine [first] and [last] - */ - switch (argc) { - case 0: - firststr = lflg ? "-16" : "-1"; - laststr = "-1"; - break; - case 1: - firststr = argv[0]; - laststr = lflg ? "-1" : argv[0]; - break; - case 2: - firststr = argv[0]; - laststr = argv[1]; - break; - default: - error("too many args"); - /* NOTREACHED */ - } - /* - * Turn into event numbers. - */ - first = str_to_event(firststr, 0); - last = str_to_event(laststr, 1); - - if (rflg) { - i = last; - last = first; - first = i; - } - /* - * XXX - this should not depend on the event numbers - * always increasing. Add sequence numbers or offset - * to the history element in next (diskbased) release. - */ - direction = first < last ? H_PREV : H_NEXT; - - /* - * If editing, grab a temp file. - */ - if (editor) { - int fd; - INTOFF; /* easier */ - snprintf(editfile, sizeof(editfile), "%s_shXXXXXX", _PATH_TMP); - if ((fd = mkstemp(editfile)) < 0) - error("can't create temporary file %s", editfile); - if ((efp = fdopen(fd, "w")) == NULL) { - close(fd); - error("can't allocate stdio buffer for temp"); - } - } - - /* - * Loop through selected history events. If listing or executing, - * do it now. Otherwise, put into temp file and call the editor - * after. - * - * The history interface needs rethinking, as the following - * convolutions will demonstrate. - */ - history(hist, &he, H_FIRST); - retval = history(hist, &he, H_NEXT_EVENT, first); - for (;retval != -1; retval = history(hist, &he, direction)) { - if (lflg) { - if (!nflg) - out1fmt("%5d ", he.num); - out1str(he.str); - } else { - const char *s = pat ? - fc_replace(he.str, pat, repl) : he.str; - - if (sflg) { - if (displayhist) { - out2str(s); - } - - evalstring(strcpy(stalloc(strlen(s) + 1), s), 0); - if (displayhist && hist) { - /* - * XXX what about recursive and - * relative histnums. - */ - history(hist, &he, H_ENTER, s); - } - - break; - } else - fputs(s, efp); - } - /* - * At end? (if we were to lose last, we'd sure be - * messed up). - */ - if (he.num == last) - break; - } - if (editor) { - char *editcmd; - size_t cmdlen; - - fclose(efp); - cmdlen = strlen(editor) + strlen(editfile) + 2; - editcmd = stalloc(cmdlen); - snprintf(editcmd, cmdlen, "%s %s", editor, editfile); - evalstring(editcmd, 0); /* XXX - should use no JC command */ - INTON; - readcmdfile(editfile); /* XXX - should read back - quick tst */ - unlink(editfile); - } - - if (lflg == 0 && active > 0) - --active; - if (displayhist) - displayhist = 0; - return 0; -} - -STATIC const char * -fc_replace(const char *s, char *p, char *r) -{ - char *dest; - int plen = strlen(p); - - STARTSTACKSTR(dest); - while (*s) { - if (*s == *p && strncmp(s, p, plen) == 0) { - while (*r) - STPUTC(*r++, dest); - s += plen; - *p = '\0'; /* so no more matches */ - } else - STPUTC(*s++, dest); - } - STACKSTRNUL(dest); - dest = grabstackstr(dest); - - return (dest); -} - -int -not_fcnumber(char *s) -{ - if (s == NULL) - return 0; - if (*s == '-') - s++; - return (!is_number(s)); -} - -int -str_to_event(const char *str, int last) -{ - HistEvent he; - const char *s = str; - int relative = 0; - int i, retval; - - retval = history(hist, &he, H_FIRST); - switch (*s) { - case '-': - relative = 1; - /*FALLTHROUGH*/ - case '+': - s++; - } - if (is_number(s)) { - i = number(s); - if (relative) { - while (retval != -1 && i--) { - retval = history(hist, &he, H_NEXT); - } - if (retval == -1) - retval = history(hist, &he, H_LAST); - } else { - retval = history(hist, &he, H_NEXT_EVENT, i); - if (retval == -1) { - /* - * the notion of first and last is - * backwards to that of the history package - */ - retval = history(hist, &he, - last ? H_FIRST : H_LAST); - } - } - if (retval == -1) - error("history number %s not found (internal error)", - str); - } else { - /* - * pattern - */ - retval = history(hist, &he, H_PREV_STR, str); - if (retval == -1) - error("history pattern not found: %s", str); - } - return (he.num); -} -#else -int -histcmd(int argc, char **argv) -{ - error("not compiled with history support"); - /* NOTREACHED */ -} -int -inputrc(int argc, char **argv) -{ - error("not compiled with history support"); - /* NOTREACHED */ -} -#endif diff --git a/bin/sh/init.h b/bin/sh/init.h deleted file mode 100644 index 60d924e..0000000 --- a/bin/sh/init.h +++ /dev/null @@ -1,39 +0,0 @@ -/* $NetBSD: init.h,v 1.10 2003/08/07 09:05:32 agc Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)init.h 8.2 (Berkeley) 5/4/95 - */ - -void init(void); -void reset(void); -void initshellproc(void); diff --git a/bin/sh/input.c b/bin/sh/input.c deleted file mode 100644 index dc686f5..0000000 --- a/bin/sh/input.c +++ /dev/null @@ -1,695 +0,0 @@ -/* $NetBSD: input.c,v 1.69 2019/01/16 07:14:17 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; -#else -__RCSID("$NetBSD: input.c,v 1.69 2019/01/16 07:14:17 kre Exp $"); -#endif -#endif /* not lint */ - -#include <stdio.h> /* defines BUFSIZ */ -#include <fcntl.h> -#include <errno.h> -#include <unistd.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> - -/* - * This file implements the input routines used by the parser. - */ - -#include "shell.h" -#include "redir.h" -#include "syntax.h" -#include "input.h" -#include "output.h" -#include "options.h" -#include "memalloc.h" -#include "error.h" -#include "alias.h" -#include "parser.h" -#include "myhistedit.h" -#include "show.h" - -#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ - -MKINIT -struct strpush { - struct strpush *prev; /* preceding string on stack */ - const char *prevstring; - int prevnleft; - int prevlleft; - struct alias *ap; /* if push was associated with an alias */ -}; - -/* - * The parsefile structure pointed to by the global variable parsefile - * contains information about the current file being read. - */ - -MKINIT -struct parsefile { - struct parsefile *prev; /* preceding file on stack */ - int linno; /* current line */ - int fd; /* file descriptor (or -1 if string) */ - int nleft; /* number of chars left in this line */ - int lleft; /* number of chars left in this buffer */ - const char *nextc; /* next char in buffer */ - char *buf; /* input buffer */ - struct strpush *strpush; /* for pushing strings at this level */ - struct strpush basestrpush; /* so pushing one is fast */ -}; - - -int plinno = 1; /* input line number */ -int parsenleft; /* copy of parsefile->nleft */ -MKINIT int parselleft; /* copy of parsefile->lleft */ -const char *parsenextc; /* copy of parsefile->nextc */ -MKINIT struct parsefile basepf; /* top level input file */ -MKINIT char basebuf[BUFSIZ]; /* buffer for top level input file */ -struct parsefile *parsefile = &basepf; /* current input file */ -int init_editline = 0; /* editline library initialized? */ -int whichprompt; /* 1 == PS1, 2 == PS2 */ - -STATIC void pushfile(void); -static int preadfd(void); - -#ifdef mkinit -INCLUDE <stdio.h> -INCLUDE "input.h" -INCLUDE "error.h" - -INIT { - basepf.nextc = basepf.buf = basebuf; -} - -RESET { - if (exception != EXSHELLPROC) - parselleft = parsenleft = 0; /* clear input buffer */ - popallfiles(); -} - -SHELLPROC { - popallfiles(); -} -#endif - - -#if 0 /* this is unused */ -/* - * Read a line from the script. - */ - -char * -pfgets(char *line, int len) -{ - char *p = line; - int nleft = len; - int c; - - while (--nleft > 0) { - c = pgetc_macro(); - if (c == PFAKE) /* consecutive PFAKEs is impossible */ - c = pgetc_macro(); - if (c == PEOF) { - if (p == line) - return NULL; - break; - } - *p++ = c; - if (c == '\n') { - plinno++; - break; - } - } - *p = '\0'; - return line; -} -#endif - - -/* - * Read a character from the script, returning PEOF on end of file. - * Nul characters in the input are silently discarded. - */ - -int -pgetc(void) -{ - int c; - - c = pgetc_macro(); - if (c == PFAKE) - c = pgetc_macro(); - return c; -} - - -static int -preadfd(void) -{ - int nr; - char *buf = parsefile->buf; - parsenextc = buf; - - retry: -#ifndef SMALL - if (parsefile->fd == 0 && el) { - static const char *rl_cp; - static int el_len; - - if (rl_cp == NULL) - rl_cp = el_gets(el, &el_len); - if (rl_cp == NULL) - nr = el_len == 0 ? 0 : -1; - else { - nr = el_len; - if (nr > BUFSIZ - 8) - nr = BUFSIZ - 8; - memcpy(buf, rl_cp, nr); - if (nr != el_len) { - el_len -= nr; - rl_cp += nr; - } else - rl_cp = 0; - } - - } else -#endif - nr = read(parsefile->fd, buf, BUFSIZ - 8); - - - if (nr <= 0) { - if (nr < 0) { - if (errno == EINTR) - goto retry; - if (parsefile->fd == 0 && errno == EWOULDBLOCK) { - int flags = fcntl(0, F_GETFL, 0); - - if (flags >= 0 && flags & O_NONBLOCK) { - flags &=~ O_NONBLOCK; - if (fcntl(0, F_SETFL, flags) >= 0) { - out2str("sh: turning off NDELAY mode\n"); - goto retry; - } - } - } - } - nr = -1; - } - return nr; -} - -/* - * Refill the input buffer and return the next input character: - * - * 1) If a string was pushed back on the input, pop it; - * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading - * from a string so we can't refill the buffer, return EOF. - * 3) If there is more stuff in this buffer, use it else call read to fill it. - * 4) Process input up to the next newline, deleting nul characters. - */ - -int -preadbuffer(void) -{ - char *p, *q; - int more; -#ifndef SMALL - int something; -#endif - char savec; - - while (parsefile->strpush) { - if (parsenleft == -1 && parsefile->strpush->ap != NULL) - return PFAKE; - popstring(); - if (--parsenleft >= 0) - return (*parsenextc++); - } - if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) - return PEOF; - flushout(&output); - flushout(&errout); - - again: - if (parselleft <= 0) { - if ((parselleft = preadfd()) == -1) { - parselleft = parsenleft = EOF_NLEFT; - return PEOF; - } - } - - /* p = (not const char *)parsenextc; */ - p = parsefile->buf + (parsenextc - parsefile->buf); - q = p; - - /* delete nul characters */ -#ifndef SMALL - something = 0; -#endif - for (more = 1; more;) { - switch (*p) { - case '\0': - p++; /* Skip nul */ - goto check; - - case '\t': - case ' ': - break; - - case '\n': - parsenleft = q - parsenextc; - more = 0; /* Stop processing here */ - break; - - default: -#ifndef SMALL - something = 1; -#endif - break; - } - - *q++ = *p++; - check: - if (--parselleft <= 0) { - parsenleft = q - parsenextc - 1; - if (parsenleft < 0) - goto again; - *q = '\0'; - more = 0; - } - } - - savec = *q; - *q = '\0'; - -#ifndef SMALL - if (parsefile->fd == 0 && hist && (something || whichprompt == 2)) { - HistEvent he; - - INTOFF; - history(hist, &he, whichprompt != 2 ? H_ENTER : H_APPEND, - parsenextc); - INTON; - } -#endif - - if (vflag) { - out2str(parsenextc); - flushout(out2); - } - - *q = savec; - - return *parsenextc++; -} - -/* - * Test whether we have reached EOF on input stream. - * Return true only if certain (without attempting a read). - * - * Note the similarity to the opening section of preadbuffer() - */ -int -at_eof(void) -{ - struct strpush *sp = parsefile->strpush; - - if (parsenleft > 0) /* more chars are in the buffer */ - return 0; - - while (sp != NULL) { - /* - * If any pushed string has any remaining data, - * then we are not at EOF (simulating popstring()) - */ - if (sp->prevnleft > 0) - return 0; - sp = sp->prev; - } - - /* - * If we reached real EOF and pushed it back, - * or if we are just processing a string (not reading a file) - * then there is no more. Note that if a file pushes a - * string, the file's ->buf remains present. - */ - if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) - return 1; - - /* - * In other cases, there might be more - */ - return 0; -} - -/* - * Undo the last call to pgetc. Only one character may be pushed back. - * PEOF may be pushed back. - */ - -void -pungetc(void) -{ - parsenleft++; - parsenextc--; -} - -/* - * Push a string back onto the input at this current parsefile level. - * We handle aliases this way. - */ -void -pushstring(const char *s, int len, struct alias *ap) -{ - struct strpush *sp; - - VTRACE(DBG_INPUT, - ("pushstring(\"%.*s\", %d)%s%s%s had: nl=%d ll=%d \"%.*s\"\n", - len, s, len, ap ? " for alias:'" : "", - ap ? ap->name : "", ap ? "'" : "", - parsenleft, parselleft, parsenleft, parsenextc)); - - INTOFF; - if (parsefile->strpush) { - sp = ckmalloc(sizeof (struct strpush)); - sp->prev = parsefile->strpush; - parsefile->strpush = sp; - } else - sp = parsefile->strpush = &(parsefile->basestrpush); - - sp->prevstring = parsenextc; - sp->prevnleft = parsenleft; - sp->prevlleft = parselleft; - sp->ap = ap; - if (ap) - ap->flag |= ALIASINUSE; - parsenextc = s; - parsenleft = len; - INTON; -} - -void -popstring(void) -{ - struct strpush *sp = parsefile->strpush; - - INTOFF; - if (sp->ap) { - int alen; - - if ((alen = strlen(sp->ap->val)) > 0 && - (sp->ap->val[alen - 1] == ' ' || - sp->ap->val[alen - 1] == '\t')) - checkkwd |= CHKALIAS; - sp->ap->flag &= ~ALIASINUSE; - } - parsenextc = sp->prevstring; - parsenleft = sp->prevnleft; - parselleft = sp->prevlleft; - - VTRACE(DBG_INPUT, ("popstring()%s%s%s nl=%d ll=%d \"%.*s\"\n", - sp->ap ? " from alias:'" : "", sp->ap ? sp->ap->name : "", - sp->ap ? "'" : "", parsenleft, parselleft, parsenleft, parsenextc)); - - parsefile->strpush = sp->prev; - if (sp != &(parsefile->basestrpush)) - ckfree(sp); - INTON; -} - -/* - * Set the input to take input from a file. If push is set, push the - * old input onto the stack first. - */ - -void -setinputfile(const char *fname, int push) -{ - unsigned char magic[4]; - int fd; - int fd2; - struct stat sb; - - CTRACE(DBG_INPUT,("setinputfile(\"%s\", %spush)\n",fname,push?"":"no")); - - INTOFF; - if ((fd = open(fname, O_RDONLY)) < 0) - error("Can't open %s", fname); - - /* Since the message "Syntax error: "(" unexpected" is not very - * helpful, we check if the file starts with the ELF magic to - * avoid that message. The first lseek tries to make sure that - * we can later rewind the file. - */ - if (fstat(fd, &sb) == 0 && S_ISREG(sb.st_mode) && - lseek(fd, 0, SEEK_SET) == 0) { - if (read(fd, magic, 4) == 4) { - if (memcmp(magic, "\177ELF", 4) == 0) { - (void)close(fd); - error("Cannot execute ELF binary %s", fname); - } - } - if (lseek(fd, 0, SEEK_SET) != 0) { - (void)close(fd); - error("Cannot rewind the file %s", fname); - } - } - - fd2 = to_upper_fd(fd); /* closes fd, returns higher equiv */ - if (fd2 == fd) { - (void) close(fd); - error("Out of file descriptors"); - } - - setinputfd(fd2, push); - INTON; -} - -/* - * When a shell fd needs to be altered (when the user wants to use - * the same fd - rare, but happens - we need to locate the ref to - * the fd, and update it. This happens via a callback. - * This is the callback func for fd's used for shell input - */ -static void -input_fd_swap(int from, int to) -{ - struct parsefile *pf; - - pf = parsefile; - while (pf != NULL) { /* don't need to stop at basepf */ - if (pf->fd == from) - pf->fd = to; - pf = pf->prev; - } -} - -/* - * Like setinputfile, but takes an open file descriptor. Call this with - * interrupts off. - */ - -void -setinputfd(int fd, int push) -{ - VTRACE(DBG_INPUT, ("setinputfd(%d, %spush)\n", fd, push?"":"no")); - - register_sh_fd(fd, input_fd_swap); - (void) fcntl(fd, F_SETFD, FD_CLOEXEC); - if (push) - pushfile(); - if (parsefile->fd > 0) - sh_close(parsefile->fd); - parsefile->fd = fd; - if (parsefile->buf == NULL) - parsefile->buf = ckmalloc(BUFSIZ); - parselleft = parsenleft = 0; - plinno = 1; - - CTRACE(DBG_INPUT, ("setinputfd(%d, %spush) done; plinno=1\n", fd, - push ? "" : "no")); -} - - -/* - * Like setinputfile, but takes input from a string. - */ - -void -setinputstring(char *string, int push, int line1) -{ - - INTOFF; - if (push) /* XXX: always, as it happens */ - pushfile(); - parsenextc = string; - parselleft = parsenleft = strlen(string); - plinno = line1; - - CTRACE(DBG_INPUT, - ("setinputstring(\"%.20s%s\" (%d), %spush, @ %d)\n", string, - (parsenleft > 20 ? "..." : ""), parsenleft, push?"":"no", line1)); - INTON; -} - - - -/* - * To handle the "." command, a stack of input files is used. Pushfile - * adds a new entry to the stack and popfile restores the previous level. - */ - -STATIC void -pushfile(void) -{ - struct parsefile *pf; - - VTRACE(DBG_INPUT, - ("pushfile(): fd=%d buf=%p nl=%d ll=%d \"%.*s\" plinno=%d\n", - parsefile->fd, parsefile->buf, parsenleft, parselleft, - parsenleft, parsenextc, plinno)); - - parsefile->nleft = parsenleft; - parsefile->lleft = parselleft; - parsefile->nextc = parsenextc; - parsefile->linno = plinno; - pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); - pf->prev = parsefile; - pf->fd = -1; - pf->strpush = NULL; - pf->basestrpush.prev = NULL; - pf->buf = NULL; - parsefile = pf; -} - - -void -popfile(void) -{ - struct parsefile *pf = parsefile; - - INTOFF; - if (pf->fd >= 0) - sh_close(pf->fd); - if (pf->buf) - ckfree(pf->buf); - while (pf->strpush) - popstring(); - parsefile = pf->prev; - ckfree(pf); - parsenleft = parsefile->nleft; - parselleft = parsefile->lleft; - parsenextc = parsefile->nextc; - - VTRACE(DBG_INPUT, - ("popfile(): fd=%d buf=%p nl=%d ll=%d \"%.*s\" plinno:%d->%d\n", - parsefile->fd, parsefile->buf, parsenleft, parselleft, - parsenleft, parsenextc, plinno, parsefile->linno)); - - plinno = parsefile->linno; - INTON; -} - -/* - * Return current file (to go back to it later using popfilesupto()). - */ - -struct parsefile * -getcurrentfile(void) -{ - return parsefile; -} - - -/* - * Pop files until the given file is on top again. Useful for regular - * builtins that read shell commands from files or strings. - * If the given file is not an active file, an error is raised. - */ - -void -popfilesupto(struct parsefile *file) -{ - while (parsefile != file && parsefile != &basepf) - popfile(); - if (parsefile != file) - error("popfilesupto() misused"); -} - - -/* - * Return to top level. - */ - -void -popallfiles(void) -{ - while (parsefile != &basepf) - popfile(); -} - - - -/* - * Close the file(s) that the shell is reading commands from. Called - * after a fork is done. - * - * Takes one arg, vfork, which tells it to not modify its global vars - * as it is still running in the parent. - * - * This code is (probably) unnecessary as the 'close on exec' flag is - * set and should be enough. In the vfork case it is definitely wrong - * to close the fds as another fork() may be done later to feed data - * from a 'here' document into a pipe and we don't want to close the - * pipe! - */ - -void -closescript(int vforked) -{ - if (vforked) - return; - popallfiles(); - if (parsefile->fd > 0) { - sh_close(parsefile->fd); - parsefile->fd = 0; - } -} diff --git a/bin/sh/input.h b/bin/sh/input.h deleted file mode 100644 index c40d4aa..0000000 --- a/bin/sh/input.h +++ /dev/null @@ -1,69 +0,0 @@ -/* $NetBSD: input.h,v 1.21 2018/08/19 23:50:27 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)input.h 8.2 (Berkeley) 5/4/95 - */ - -/* PEOF (the end of file marker) is defined in syntax.h */ - -/* - * The input line number. Input.c just defines this variable, and saves - * and restores it when files are pushed and popped. The user of this - * package must set its value. - */ -extern int plinno; -extern int parsenleft; /* number of characters left in input buffer */ -extern const char *parsenextc; /* next character in input buffer */ -extern int init_editline; /* 0 == not setup, 1 == OK, -1 == failed */ -extern int whichprompt; /* 1 ==> PS1, 2 ==> PS2 */ - -struct alias; -struct parsefile; - -char *pfgets(char *, int); -int pgetc(void); -int preadbuffer(void); -int at_eof(void); -void pungetc(void); -void pushstring(const char *, int, struct alias *); -void popstring(void); -void setinputfile(const char *, int); -void setinputfd(int, int); -void setinputstring(char *, int, int); -void popfile(void); -struct parsefile *getcurrentfile(void); -void popfilesupto(struct parsefile *); -void popallfiles(void); -void closescript(int); - -#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) diff --git a/bin/sh/jobs.c b/bin/sh/jobs.c deleted file mode 100644 index d066bb8..0000000 --- a/bin/sh/jobs.c +++ /dev/null @@ -1,1812 +0,0 @@ -/* $NetBSD: jobs.c,v 1.103 2018/12/03 02:38:30 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: jobs.c,v 1.103 2018/12/03 02:38:30 kre Exp $"); -#endif -#endif /* not lint */ - -#include <stdio.h> -#include <fcntl.h> -#include <signal.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> -#include <paths.h> -#include <sys/types.h> -#include <sys/param.h> -#ifdef BSD -#include <sys/wait.h> -#include <sys/time.h> -#include <sys/resource.h> -#endif -#include <sys/ioctl.h> - -#include "shell.h" -#if JOBS -#if OLD_TTY_DRIVER -#include "sgtty.h" -#else -#include <termios.h> -#endif -#undef CEOF /* syntax.h redefines this */ -#endif -#include "redir.h" -#include "show.h" -#include "main.h" -#include "parser.h" -#include "nodes.h" -#include "jobs.h" -#include "var.h" -#include "options.h" -#include "builtins.h" -#include "trap.h" -#include "syntax.h" -#include "input.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "mystring.h" - - -#ifndef WCONTINUED -#define WCONTINUED 0 /* So we can compile on old systems */ -#endif -#ifndef WIFCONTINUED -#define WIFCONTINUED(x) (0) /* ditto */ -#endif - - -static struct job *jobtab; /* array of jobs */ -static int njobs; /* size of array */ -static int jobs_invalid; /* set in child */ -MKINIT pid_t backgndpid = -1; /* pid of last background process */ -#if JOBS -int initialpgrp; /* pgrp of shell on invocation */ -static int curjob = -1; /* current job */ -#endif -static int ttyfd = -1; - -STATIC void restartjob(struct job *); -STATIC void freejob(struct job *); -STATIC struct job *getjob(const char *, int); -STATIC int dowait(int, struct job *, struct job **); -#define WBLOCK 1 -#define WNOFREE 2 -#define WSILENT 4 -STATIC int jobstatus(const struct job *, int); -STATIC int waitproc(int, struct job *, int *); -STATIC void cmdtxt(union node *); -STATIC void cmdlist(union node *, int); -STATIC void cmdputs(const char *); -inline static void cmdputi(int); - -#ifdef SYSV -STATIC int onsigchild(void); -#endif - -#ifdef OLD_TTY_DRIVER -static pid_t tcgetpgrp(int fd); -static int tcsetpgrp(int fd, pid_t pgrp); - -static pid_t -tcgetpgrp(int fd) -{ - pid_t pgrp; - if (ioctl(fd, TIOCGPGRP, (char *)&pgrp) == -1) - return -1; - else - return pgrp; -} - -static int -tcsetpgrp(int fd, pid_tpgrp) -{ - return ioctl(fd, TIOCSPGRP, (char *)&pgrp); -} -#endif - -static void -ttyfd_change(int from, int to) -{ - if (ttyfd == from) - ttyfd = to; -} - -/* - * Turn job control on and off. - * - * Note: This code assumes that the third arg to ioctl is a character - * pointer, which is true on Berkeley systems but not System V. Since - * System V doesn't have job control yet, this isn't a problem now. - */ - -MKINIT int jobctl; - -void -setjobctl(int on) -{ -#ifdef OLD_TTY_DRIVER - int ldisc; -#endif - - if (on == jobctl || rootshell == 0) - return; - if (on) { -#if defined(FIOCLEX) || defined(FD_CLOEXEC) - int i; - - if (ttyfd != -1) - sh_close(ttyfd); - if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) { - for (i = 0; i < 3; i++) { - if (isatty(i) && (ttyfd = dup(i)) != -1) - break; - } - if (i == 3) - goto out; - } - ttyfd = to_upper_fd(ttyfd); /* Move to a high fd */ - register_sh_fd(ttyfd, ttyfd_change); -#else - out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control"); - goto out; -#endif - do { /* while we are in the background */ - if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) { - out: - out2str("sh: can't access tty; job control turned off\n"); - mflag = 0; - return; - } - if (initialpgrp == -1) - initialpgrp = getpgrp(); - else if (initialpgrp != getpgrp()) { - killpg(0, SIGTTIN); - continue; - } - } while (0); - -#ifdef OLD_TTY_DRIVER - if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0 - || ldisc != NTTYDISC) { - out2str("sh: need new tty driver to run job control; job control turned off\n"); - mflag = 0; - return; - } -#endif - setsignal(SIGTSTP, 0); - setsignal(SIGTTOU, 0); - setsignal(SIGTTIN, 0); - if (getpgrp() != rootpid && setpgid(0, rootpid) == -1) - error("Cannot set process group (%s) at %d", - strerror(errno), __LINE__); - if (tcsetpgrp(ttyfd, rootpid) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - } else { /* turning job control off */ - if (getpgrp() != initialpgrp && setpgid(0, initialpgrp) == -1) - error("Cannot set process group (%s) at %d", - strerror(errno), __LINE__); - if (tcsetpgrp(ttyfd, initialpgrp) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - sh_close(ttyfd); - ttyfd = -1; - setsignal(SIGTSTP, 0); - setsignal(SIGTTOU, 0); - setsignal(SIGTTIN, 0); - } - jobctl = on; -} - - -#ifdef mkinit -INCLUDE <stdlib.h> - -SHELLPROC { - backgndpid = -1; -#if JOBS - jobctl = 0; -#endif -} - -#endif - - - -#if JOBS -static int -do_fgcmd(const char *arg_ptr) -{ - struct job *jp; - int i; - int status; - - if (jobs_invalid) - error("No current jobs"); - jp = getjob(arg_ptr, 0); - if (jp->jobctl == 0) - error("job not created under job control"); - out1fmt("%s", jp->ps[0].cmd); - for (i = 1; i < jp->nprocs; i++) - out1fmt(" | %s", jp->ps[i].cmd ); - out1c('\n'); - flushall(); - - for (i = 0; i < jp->nprocs; i++) - if (tcsetpgrp(ttyfd, jp->ps[i].pid) != -1) - break; - - if (i >= jp->nprocs) { - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - } - restartjob(jp); - INTOFF; - status = waitforjob(jp); - INTON; - return status; -} - -int -fgcmd(int argc, char **argv) -{ - nextopt(""); - return do_fgcmd(*argptr); -} - -int -fgcmd_percent(int argc, char **argv) -{ - nextopt(""); - return do_fgcmd(*argv); -} - -static void -set_curjob(struct job *jp, int mode) -{ - struct job *jp1, *jp2; - int i, ji; - - ji = jp - jobtab; - - /* first remove from list */ - if (ji == curjob) - curjob = jp->prev_job; - else { - for (i = 0; i < njobs; i++) { - if (jobtab[i].prev_job != ji) - continue; - jobtab[i].prev_job = jp->prev_job; - break; - } - } - - /* Then re-insert in correct position */ - switch (mode) { - case 0: /* job being deleted */ - jp->prev_job = -1; - break; - case 1: /* newly created job or backgrounded job, - put after all stopped jobs. */ - if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) { - for (jp1 = jobtab + curjob; ; jp1 = jp2) { - if (jp1->prev_job == -1) - break; - jp2 = jobtab + jp1->prev_job; - if (jp2->state != JOBSTOPPED) - break; - } - jp->prev_job = jp1->prev_job; - jp1->prev_job = ji; - break; - } - /* FALLTHROUGH */ - case 2: /* newly stopped job - becomes curjob */ - jp->prev_job = curjob; - curjob = ji; - break; - } -} - -int -bgcmd(int argc, char **argv) -{ - struct job *jp; - int i; - - nextopt(""); - if (jobs_invalid) - error("No current jobs"); - do { - jp = getjob(*argptr, 0); - if (jp->jobctl == 0) - error("job not created under job control"); - set_curjob(jp, 1); - out1fmt("[%ld] %s", (long)(jp - jobtab + 1), jp->ps[0].cmd); - for (i = 1; i < jp->nprocs; i++) - out1fmt(" | %s", jp->ps[i].cmd ); - out1c('\n'); - flushall(); - restartjob(jp); - } while (*argptr && *++argptr); - return 0; -} - - -STATIC void -restartjob(struct job *jp) -{ - struct procstat *ps; - int i; - - if (jp->state == JOBDONE) - return; - INTOFF; - for (i = 0; i < jp->nprocs; i++) - if (killpg(jp->ps[i].pid, SIGCONT) != -1) - break; - if (i >= jp->nprocs) - error("Cannot continue job (%s)", strerror(errno)); - for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { - if (WIFSTOPPED(ps->status)) { - VTRACE(DBG_JOBS, ( - "restartjob: [%zu] pid %d status change" - " from %#x (stopped) to -1 (running)\n", - (size_t)(jp-jobtab+1), ps->pid, ps->status)); - ps->status = -1; - jp->state = JOBRUNNING; - } - } - INTON; -} -#endif - -inline static void -cmdputi(int n) -{ - char str[20]; - - fmtstr(str, sizeof str, "%d", n); - cmdputs(str); -} - -static void -showjob(struct output *out, struct job *jp, int mode) -{ - int procno; - int st; - struct procstat *ps; - int col; - char s[64]; - -#if JOBS - if (mode & SHOW_PGID) { - /* just output process (group) id of pipeline */ - outfmt(out, "%ld\n", (long)jp->ps->pid); - return; - } -#endif - - procno = jp->nprocs; - if (!procno) - return; - - if (mode & SHOW_PID) - mode |= SHOW_MULTILINE; - - if ((procno > 1 && !(mode & SHOW_MULTILINE)) - || (mode & SHOW_SIGNALLED)) { - /* See if we have more than one status to report */ - ps = jp->ps; - st = ps->status; - do { - int st1 = ps->status; - if (st1 != st) - /* yes - need multi-line output */ - mode |= SHOW_MULTILINE; - if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1)) - continue; - if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f) - && st1 != SIGINT && st1 != SIGPIPE)) - mode |= SHOW_ISSIG; - - } while (ps++, --procno); - procno = jp->nprocs; - } - - if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) { - if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) { - VTRACE(DBG_JOBS, ("showjob: freeing job %d\n", - jp - jobtab + 1)); - freejob(jp); - } - return; - } - - for (ps = jp->ps; --procno >= 0; ps++) { /* for each process */ - if (ps == jp->ps) - fmtstr(s, 16, "[%ld] %c ", - (long)(jp - jobtab + 1), -#if JOBS - jp - jobtab == curjob ? - '+' : - curjob != -1 && - jp - jobtab == jobtab[curjob].prev_job ? - '-' : -#endif - ' '); - else - fmtstr(s, 16, " " ); - col = strlen(s); - if (mode & SHOW_PID) { - fmtstr(s + col, 16, "%ld ", (long)ps->pid); - col += strlen(s + col); - } - if (ps->status == -1) { - scopy("Running", s + col); - } else if (WIFEXITED(ps->status)) { - st = WEXITSTATUS(ps->status); - if (st) - fmtstr(s + col, 16, "Done(%d)", st); - else - fmtstr(s + col, 16, "Done"); - } else { -#if JOBS - if (WIFSTOPPED(ps->status)) - st = WSTOPSIG(ps->status); - else /* WIFSIGNALED(ps->status) */ -#endif - st = WTERMSIG(ps->status); - scopyn(strsignal(st), s + col, 32); - if (WCOREDUMP(ps->status)) { - col += strlen(s + col); - scopyn(" (core dumped)", s + col, 64 - col); - } - } - col += strlen(s + col); - outstr(s, out); - do { - outc(' ', out); - col++; - } while (col < 30); - outstr(ps->cmd, out); - if (mode & SHOW_MULTILINE) { - if (procno > 0) { - outc(' ', out); - outc('|', out); - } - } else { - while (--procno >= 0) - outfmt(out, " | %s", (++ps)->cmd ); - } - outc('\n', out); - } - flushout(out); - jp->flags &= ~JOBCHANGED; - if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) - freejob(jp); -} - -int -jobscmd(int argc, char **argv) -{ - int mode, m; - - mode = 0; - while ((m = nextopt("lp"))) - if (m == 'l') - mode = SHOW_PID; - else - mode = SHOW_PGID; - if (!iflag) - mode |= SHOW_NO_FREE; - if (*argptr) - do - showjob(out1, getjob(*argptr,0), mode); - while (*++argptr); - else - showjobs(out1, mode); - return 0; -} - - -/* - * Print a list of jobs. If "change" is nonzero, only print jobs whose - * statuses have changed since the last call to showjobs. - * - * If the shell is interrupted in the process of creating a job, the - * result may be a job structure containing zero processes. Such structures - * will be freed here. - */ - -void -showjobs(struct output *out, int mode) -{ - int jobno; - struct job *jp; - int silent = 0, gotpid; - - CTRACE(DBG_JOBS, ("showjobs(%x) called\n", mode)); - - /* If not even one one job changed, there is nothing to do */ - gotpid = dowait(WSILENT, NULL, NULL); - while (dowait(WSILENT, NULL, NULL) > 0) - continue; -#ifdef JOBS - /* - * Check if we are not in our foreground group, and if not - * put us in it. - */ - if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) { - if (tcsetpgrp(ttyfd, getpid()) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - VTRACE(DBG_JOBS|DBG_INPUT, ("repaired tty process group\n")); - silent = 1; - } -#endif - - for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { - if (!jp->used) - continue; - if (jp->nprocs == 0) { - if (!jobs_invalid) - freejob(jp); - continue; - } - if ((mode & SHOW_CHANGED) && !(jp->flags & JOBCHANGED)) - continue; - if (silent && (jp->flags & JOBCHANGED)) { - jp->flags &= ~JOBCHANGED; - continue; - } - showjob(out, jp, mode); - } -} - -/* - * Mark a job structure as unused. - */ - -STATIC void -freejob(struct job *jp) -{ - INTOFF; - if (jp->ps != &jp->ps0) { - ckfree(jp->ps); - jp->ps = &jp->ps0; - } - jp->nprocs = 0; - jp->used = 0; -#if JOBS - set_curjob(jp, 0); -#endif - INTON; -} - -/* - * Extract the status of a completed job (for $?) - */ -STATIC int -jobstatus(const struct job *jp, int raw) -{ - int status = 0; - int retval; - - if ((jp->flags & JPIPEFAIL) && jp->nprocs) { - int i; - - for (i = 0; i < jp->nprocs; i++) - if (jp->ps[i].status != 0) - status = jp->ps[i].status; - } else - status = jp->ps[jp->nprocs ? jp->nprocs - 1 : 0].status; - - if (raw) - return status; - - if (WIFEXITED(status)) - retval = WEXITSTATUS(status); -#if JOBS - else if (WIFSTOPPED(status)) - retval = WSTOPSIG(status) + 128; -#endif - else { - /* XXX: limits number of signals */ - retval = WTERMSIG(status) + 128; - } - - return retval; -} - - - -int -waitcmd(int argc, char **argv) -{ - struct job *job, *last; - int retval; - struct job *jp; - int i; - int any = 0; - int found; - char *pid = NULL, *fpid; - char **arg; - char idstring[20]; - - while ((i = nextopt("np:")) != '\0') { - switch (i) { - case 'n': - any = 1; - break; - case 'p': - if (pid) - error("more than one -p unsupported"); - pid = optionarg; - break; - } - } - - if (pid != NULL) { - if (!validname(pid, '\0', NULL)) - error("invalid name: -p '%s'", pid); - if (unsetvar(pid, 0)) - error("%s readonly", pid); - } - - /* - * If we have forked, and not yet created any new jobs, then - * we have no children, whatever jobtab claims, - * so simply return in that case. - * - * The return code is 127 if we had any pid args (none are found) - * or if we had -n (nothing exited), but 0 for plain old "wait". - */ - if (jobs_invalid) { - CTRACE(DBG_WAIT, ("builtin wait%s%s in child, invalid jobtab\n", - any ? " -n" : "", *argptr ? " pid..." : "")); - return (any || *argptr) ? 127 : 0; - } - - /* clear stray flags left from previous waitcmd */ - for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { - jp->flags &= ~JOBWANTED; - jp->ref = NULL; - } - - CTRACE(DBG_WAIT, - ("builtin wait%s%s\n", any ? " -n" : "", *argptr ? " pid..." : "")); - - /* - * First, validate the jobnum args, count how many refer to - * (different) running jobs, and if we had -n, and found that one has - * already finished, we return that one. Otherwise remember - * which ones we are looking for (JOBWANTED). - */ - found = 0; - last = NULL; - for (arg = argptr; *arg; arg++) { - last = jp = getjob(*arg, 1); - if (!jp) - continue; - if (jp->ref == NULL) - jp->ref = *arg; - if (any && jp->state == JOBDONE) { - /* - * We just want any of them, and this one is - * ready for consumption, bon apetit ... - */ - retval = jobstatus(jp, 0); - if (pid) - setvar(pid, *arg, 0); - if (!iflag) - freejob(jp); - CTRACE(DBG_WAIT, ("wait -n found %s already done: %d\n", *arg, retval)); - return retval; - } - if (!(jp->flags & JOBWANTED)) { - /* - * It is possible to list the same job several - * times - the obvious "wait 1 1 1" or - * "wait %% %2 102" where job 2 is current and pid 102 - * However many times it is requested, it is found once. - */ - found++; - jp->flags |= JOBWANTED; - } - job = jp; - } - - VTRACE(DBG_WAIT, ("wait %s%s%sfound %d candidates (last %s)\n", - any ? "-n " : "", *argptr ? *argptr : "", - argptr[0] && argptr[1] ? "... " : " ", found, - job ? (job->ref ? job->ref : "<no-arg>") : "none")); - - /* - * If we were given a list of jobnums: - * and none of those exist, then we're done. - */ - if (*argptr && found == 0) - return 127; - - /* - * Otherwise we need to wait for something to complete - * When it does, we check and see if it is one of the - * jobs we're waiting on, and if so, we clean it up. - * If we had -n, then we're done, otherwise we do it all again - * until all we had listed are done, of if there were no - * jobnum args, all are done. - */ - - retval = any || *argptr ? 127 : 0; - fpid = NULL; - for (;;) { - VTRACE(DBG_WAIT, ("wait waiting (%d remain): ", found)); - for (jp = jobtab, i = njobs; --i >= 0; jp++) { - if (jp->used && jp->flags & JOBWANTED && - jp->state == JOBDONE) - break; - if (jp->used && jp->state == JOBRUNNING) - break; - } - if (i < 0) { - CTRACE(DBG_WAIT, ("nothing running (ret: %d) fpid %s\n", - retval, fpid ? fpid : "unset")); - if (pid && fpid) - setvar(pid, fpid, 0); - return retval; - } - VTRACE(DBG_WAIT, ("found @%d/%d state: %d\n", njobs-i, njobs, - jp->state)); - - /* - * There is at least 1 job running, so we can - * safely wait() for something to exit. - */ - if (jp->state == JOBRUNNING) { - job = NULL; - if ((i = dowait(WBLOCK|WNOFREE, NULL, &job)) == -1) - return 128 + lastsig(); - - /* - * one of the job's processes exited, - * but there are more - */ - if (job->state == JOBRUNNING) - continue; - } else - job = jp; /* we want this, and it is done */ - - if (job->flags & JOBWANTED || (*argptr == 0 && any)) { - int rv; - - job->flags &= ~JOBWANTED; /* got it */ - rv = jobstatus(job, 0); - VTRACE(DBG_WAIT, ( - "wanted %d (%s) done: st=%d", i, - job->ref ? job->ref : "", rv)); - if (any || job == last) { - retval = rv; - fpid = job->ref; - - VTRACE(DBG_WAIT, (" save")); - if (pid) { - /* - * don't need fpid unless we are going - * to return it. - */ - if (fpid == NULL) { - /* - * this only happens with "wait -n" - * (that is, no pid args) - */ - snprintf(idstring, sizeof idstring, - "%d", job->ps[ job->nprocs ? - job->nprocs-1 : - 0 ].pid); - fpid = idstring; - } - VTRACE(DBG_WAIT, (" (for %s)", fpid)); - } - } - - if (job->state == JOBDONE) { - VTRACE(DBG_WAIT, (" free")); - freejob(job); - } - - if (any || (found > 0 && --found == 0)) { - if (pid && fpid) - setvar(pid, fpid, 0); - VTRACE(DBG_WAIT, (" return %d\n", retval)); - return retval; - } - VTRACE(DBG_WAIT, ("\n")); - continue; - } - - /* this is to handle "wait" (no args) */ - if (found == 0 && job->state == JOBDONE) { - VTRACE(DBG_JOBS|DBG_WAIT, ("Cleanup: %d\n", i)); - freejob(job); - } - } -} - - -int -jobidcmd(int argc, char **argv) -{ - struct job *jp; - int i; - int pg = 0, onep = 0, job = 0; - - while ((i = nextopt("gjp"))) { - switch (i) { - case 'g': pg = 1; break; - case 'j': job = 1; break; - case 'p': onep = 1; break; - } - } - CTRACE(DBG_JOBS, ("jobidcmd%s%s%s%s %s\n", pg ? " -g" : "", - onep ? " -p" : "", job ? " -j" : "", jobs_invalid ? " [inv]" : "", - *argptr ? *argptr : "<implicit %%>")); - if (pg + onep + job > 1) - error("-g -j and -p options cannot be combined"); - - if (argptr[0] && argptr[1]) - error("usage: jobid [-g|-p|-r] jobid"); - - jp = getjob(*argptr, 0); - if (job) { - out1fmt("%%%zu\n", (size_t)(jp - jobtab + 1)); - return 0; - } - if (pg) { - if (jp->pgrp != 0) { - out1fmt("%ld\n", (long)jp->pgrp); - return 0; - } - return 1; - } - if (onep) { - i = jp->nprocs - 1; - if (i < 0) - return 1; - out1fmt("%ld\n", (long)jp->ps[i].pid); - return 0; - } - for (i = 0 ; i < jp->nprocs ; ) { - out1fmt("%ld", (long)jp->ps[i].pid); - out1c(++i < jp->nprocs ? ' ' : '\n'); - } - return 0; -} - -int -getjobpgrp(const char *name) -{ - struct job *jp; - - if (jobs_invalid) - error("No such job: %s", name); - jp = getjob(name, 1); - if (jp == 0) - return 0; - return -jp->ps[0].pid; -} - -/* - * Convert a job name to a job structure. - */ - -STATIC struct job * -getjob(const char *name, int noerror) -{ - int jobno = -1; - struct job *jp; - int pid; - int i; - const char *err_msg = "No such job: %s"; - - if (name == NULL) { -#if JOBS - jobno = curjob; -#endif - err_msg = "No current job"; - } else if (name[0] == '%') { - if (is_number(name + 1)) { - jobno = number(name + 1) - 1; - } else if (!name[1] || !name[2]) { - switch (name[1]) { -#if JOBS - case 0: - case '+': - case '%': - jobno = curjob; - err_msg = "No current job"; - break; - case '-': - jobno = curjob; - if (jobno != -1) - jobno = jobtab[jobno].prev_job; - err_msg = "No previous job"; - break; -#endif - default: - goto check_pattern; - } - } else { - struct job *found; - check_pattern: - found = NULL; - for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { - if (!jp->used || jp->nprocs <= 0) - continue; - if ((name[1] == '?' - && strstr(jp->ps[0].cmd, name + 2)) - || prefix(name + 1, jp->ps[0].cmd)) { - if (found) { - err_msg = "%s: ambiguous"; - found = 0; - break; - } - found = jp; - } - } - if (found) - return found; - } - - } else if (is_number(name)) { - pid = number(name); - for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { - if (jp->used && jp->nprocs > 0 - && jp->ps[jp->nprocs - 1].pid == pid) - return jp; - } - } - - if (jobno >= 0 && jobno < njobs) { - jp = jobtab + jobno; - if (jp->used) - return jp; - } - if (!noerror) - error(err_msg, name); - return 0; -} - - - -/* - * Return a new job structure, - */ - -struct job * -makejob(union node *node, int nprocs) -{ - int i; - struct job *jp; - - if (jobs_invalid) { - for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) { - if (jp->used) - freejob(jp); - } - jobs_invalid = 0; - } - - for (i = njobs, jp = jobtab ; ; jp++) { - if (--i < 0) { - INTOFF; - if (njobs == 0) { - jobtab = ckmalloc(4 * sizeof jobtab[0]); - } else { - jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); - memcpy(jp, jobtab, njobs * sizeof jp[0]); - /* Relocate `ps' pointers */ - for (i = 0; i < njobs; i++) - if (jp[i].ps == &jobtab[i].ps0) - jp[i].ps = &jp[i].ps0; - ckfree(jobtab); - jobtab = jp; - } - jp = jobtab + njobs; - for (i = 4 ; --i >= 0 ; njobs++) { - jobtab[njobs].used = 0; - jobtab[njobs].prev_job = -1; - } - INTON; - break; - } - if (jp->used == 0) - break; - } - INTOFF; - jp->state = JOBRUNNING; - jp->used = 1; - jp->flags = pipefail ? JPIPEFAIL : 0; - jp->nprocs = 0; - jp->pgrp = 0; -#if JOBS - jp->jobctl = jobctl; - set_curjob(jp, 1); -#endif - if (nprocs > 1) { - jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); - } else { - jp->ps = &jp->ps0; - } - INTON; - VTRACE(DBG_JOBS, ("makejob(%p, %d)%s returns %%%d\n", (void *)node, - nprocs, (jp->flags&JPIPEFAIL)?" PF":"", jp - jobtab + 1)); - return jp; -} - - -/* - * Fork off a subshell. If we are doing job control, give the subshell its - * own process group. Jp is a job structure that the job is to be added to. - * N is the command that will be evaluated by the child. Both jp and n may - * be NULL. The mode parameter can be one of the following: - * FORK_FG - Fork off a foreground process. - * FORK_BG - Fork off a background process. - * FORK_NOJOB - Like FORK_FG, but don't give the process its own - * process group even if job control is on. - * - * When job control is turned off, background processes have their standard - * input redirected to /dev/null (except for the second and later processes - * in a pipeline). - */ - -int -forkshell(struct job *jp, union node *n, int mode) -{ - pid_t pid; - int serrno; - - CTRACE(DBG_JOBS, ("forkshell(%%%d, %p, %d) called\n", - jp - jobtab, n, mode)); - - switch ((pid = fork())) { - case -1: - serrno = errno; - VTRACE(DBG_JOBS, ("Fork failed, errno=%d\n", serrno)); - INTON; - error("Cannot fork (%s)", strerror(serrno)); - break; - case 0: - SHELL_FORKED(); - forkchild(jp, n, mode, 0); - return 0; - default: - return forkparent(jp, n, mode, pid); - } -} - -int -forkparent(struct job *jp, union node *n, int mode, pid_t pid) -{ - int pgrp; - - if (rootshell && mode != FORK_NOJOB && mflag) { - if (jp == NULL || jp->nprocs == 0) - pgrp = pid; - else - pgrp = jp->ps[0].pid; - jp->pgrp = pgrp; - /* This can fail because we are doing it in the child also */ - (void)setpgid(pid, pgrp); - } - if (mode == FORK_BG) - backgndpid = pid; /* set $! */ - if (jp) { - struct procstat *ps = &jp->ps[jp->nprocs++]; - ps->pid = pid; - ps->status = -1; - ps->cmd[0] = 0; - if (/* iflag && rootshell && */ n) - commandtext(ps, n); - } - CTRACE(DBG_JOBS, ("In parent shell: child = %d (mode %d)\n",pid,mode)); - return pid; -} - -void -forkchild(struct job *jp, union node *n, int mode, int vforked) -{ - int wasroot; - int pgrp; - const char *devnull = _PATH_DEVNULL; - const char *nullerr = "Can't open %s"; - - wasroot = rootshell; - CTRACE(DBG_JOBS, ("Child shell %d %sforked from %d (mode %d)\n", - getpid(), vforked?"v":"", getppid(), mode)); - - if (!vforked) { - rootshell = 0; - handler = &main_handler; - } - - closescript(vforked); - clear_traps(vforked); -#if JOBS - if (!vforked) - jobctl = 0; /* do job control only in root shell */ - if (wasroot && mode != FORK_NOJOB && mflag) { - if (jp == NULL || jp->nprocs == 0) - pgrp = getpid(); - else - pgrp = jp->ps[0].pid; - /* This can fail because we are doing it in the parent also */ - (void)setpgid(0, pgrp); - if (mode == FORK_FG) { - if (tcsetpgrp(ttyfd, pgrp) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - } - setsignal(SIGTSTP, vforked); - setsignal(SIGTTOU, vforked); - } else if (mode == FORK_BG) { - ignoresig(SIGINT, vforked); - ignoresig(SIGQUIT, vforked); - if ((jp == NULL || jp->nprocs == 0) && - ! fd0_redirected_p ()) { - close(0); - if (open(devnull, O_RDONLY) != 0) - error(nullerr, devnull); - } - } -#else - if (mode == FORK_BG) { - ignoresig(SIGINT, vforked); - ignoresig(SIGQUIT, vforked); - if ((jp == NULL || jp->nprocs == 0) && - ! fd0_redirected_p ()) { - close(0); - if (open(devnull, O_RDONLY) != 0) - error(nullerr, devnull); - } - } -#endif - if (wasroot && iflag) { - setsignal(SIGINT, vforked); - setsignal(SIGQUIT, vforked); - setsignal(SIGTERM, vforked); - } - - if (!vforked) - jobs_invalid = 1; -} - -/* - * Wait for job to finish. - * - * Under job control we have the problem that while a child process is - * running interrupts generated by the user are sent to the child but not - * to the shell. This means that an infinite loop started by an inter- - * active user may be hard to kill. With job control turned off, an - * interactive user may place an interactive program inside a loop. If - * the interactive program catches interrupts, the user doesn't want - * these interrupts to also abort the loop. The approach we take here - * is to have the shell ignore interrupt signals while waiting for a - * forground process to terminate, and then send itself an interrupt - * signal if the child process was terminated by an interrupt signal. - * Unfortunately, some programs want to do a bit of cleanup and then - * exit on interrupt; unless these processes terminate themselves by - * sending a signal to themselves (instead of calling exit) they will - * confuse this approach. - */ - -int -waitforjob(struct job *jp) -{ -#if JOBS - int mypgrp = getpgrp(); -#endif - int status; - int st; - - INTOFF; - VTRACE(DBG_JOBS, ("waitforjob(%%%d) called\n", jp - jobtab + 1)); - while (jp->state == JOBRUNNING) { - dowait(WBLOCK, jp, NULL); - } -#if JOBS - if (jp->jobctl) { - if (tcsetpgrp(ttyfd, mypgrp) == -1) - error("Cannot set tty process group (%s) at %d", - strerror(errno), __LINE__); - } - if (jp->state == JOBSTOPPED && curjob != jp - jobtab) - set_curjob(jp, 2); -#endif - status = jobstatus(jp, 1); - - /* convert to 8 bits */ - if (WIFEXITED(status)) - st = WEXITSTATUS(status); -#if JOBS - else if (WIFSTOPPED(status)) - st = WSTOPSIG(status) + 128; -#endif - else - st = WTERMSIG(status) + 128; - - VTRACE(DBG_JOBS, ("waitforjob: job %d, nproc %d, status %d, st %x\n", - jp - jobtab + 1, jp->nprocs, status, st)); -#if JOBS - if (jp->jobctl) { - /* - * This is truly gross. - * If we're doing job control, then we did a TIOCSPGRP which - * caused us (the shell) to no longer be in the controlling - * session -- so we wouldn't have seen any ^C/SIGINT. So, we - * intuit from the subprocess exit status whether a SIGINT - * occurred, and if so interrupt ourselves. Yuck. - mycroft - */ - if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) - raise(SIGINT); - } -#endif - if (! JOBS || jp->state == JOBDONE) - freejob(jp); - INTON; - return st; -} - - - -/* - * Wait for a process to terminate. - */ - -STATIC int -dowait(int flags, struct job *job, struct job **changed) -{ - int pid; - int status; - struct procstat *sp; - struct job *jp; - struct job *thisjob; - int done; - int stopped; - - VTRACE(DBG_JOBS|DBG_PROCS, ("dowait(%x) called\n", flags)); - - if (changed != NULL) - *changed = NULL; - - do { - pid = waitproc(flags & WBLOCK, job, &status); - VTRACE(DBG_JOBS|DBG_PROCS, ("wait returns pid %d, status %#x\n", - pid, status)); - } while (pid == -1 && errno == EINTR && pendingsigs == 0); - if (pid <= 0) - return pid; - INTOFF; - thisjob = NULL; - for (jp = jobtab ; jp < jobtab + njobs ; jp++) { - if (jp->used) { - done = 1; - stopped = 1; - for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { - if (sp->pid == -1) - continue; - if (sp->pid == pid && - (sp->status==-1 || WIFSTOPPED(sp->status))) { - VTRACE(DBG_JOBS | DBG_PROCS, - ("Job %d: changing status of proc %d from %#x to %#x\n", - jp - jobtab + 1, pid, - sp->status, status)); - if (WIFCONTINUED(status)) { - if (sp->status != -1) - jp->flags |= JOBCHANGED; - sp->status = -1; - jp->state = 0; - } else - sp->status = status; - thisjob = jp; - if (changed != NULL) - *changed = jp; - } - if (sp->status == -1) - stopped = 0; - else if (WIFSTOPPED(sp->status)) - done = 0; - } - if (stopped) { /* stopped or done */ - int state = done ? JOBDONE : JOBSTOPPED; - - if (jp->state != state) { - VTRACE(DBG_JOBS, - ("Job %d: changing state from %d to %d\n", - jp - jobtab + 1, jp->state, state)); - jp->state = state; -#if JOBS - if (done) - set_curjob(jp, 0); -#endif - } - } - } - } - - if (thisjob && - (thisjob->state != JOBRUNNING || thisjob->flags & JOBCHANGED)) { - int mode = 0; - - if (!rootshell || !iflag) - mode = SHOW_SIGNALLED; - if ((job == thisjob && (flags & WNOFREE) == 0) || - job != thisjob) - mode = SHOW_SIGNALLED | SHOW_NO_FREE; - if (mode && (flags & WSILENT) == 0) - showjob(out2, thisjob, mode); - else { - VTRACE(DBG_JOBS, - ("Not printing status, rootshell=%d, job=%p\n", - rootshell, job)); - thisjob->flags |= JOBCHANGED; - } - } - - INTON; - return pid; -} - - - -/* - * Do a wait system call. If job control is compiled in, we accept - * stopped processes. If block is zero, we return a value of zero - * rather than blocking. - * - * System V doesn't have a non-blocking wait system call. It does - * have a SIGCLD signal that is sent to a process when one of its - * children dies. The obvious way to use SIGCLD would be to install - * a handler for SIGCLD which simply bumped a counter when a SIGCLD - * was received, and have waitproc bump another counter when it got - * the status of a process. Waitproc would then know that a wait - * system call would not block if the two counters were different. - * This approach doesn't work because if a process has children that - * have not been waited for, System V will send it a SIGCLD when it - * installs a signal handler for SIGCLD. What this means is that when - * a child exits, the shell will be sent SIGCLD signals continuously - * until is runs out of stack space, unless it does a wait call before - * restoring the signal handler. The code below takes advantage of - * this (mis)feature by installing a signal handler for SIGCLD and - * then checking to see whether it was called. If there are any - * children to be waited for, it will be. - * - * If neither SYSV nor BSD is defined, we don't implement nonblocking - * waits at all. In this case, the user will not be informed when - * a background process until the next time she runs a real program - * (as opposed to running a builtin command or just typing return), - * and the jobs command may give out of date information. - */ - -#ifdef SYSV -STATIC int gotsigchild; - -STATIC int onsigchild() { - gotsigchild = 1; -} -#endif - - -STATIC int -waitproc(int block, struct job *jp, int *status) -{ -#ifdef BSD - int flags = 0; - -#if JOBS - if (mflag || (jp != NULL && jp->jobctl)) - flags |= WUNTRACED | WCONTINUED; -#endif - if (block == 0) - flags |= WNOHANG; - VTRACE(DBG_WAIT, ("waitproc: doing waitpid(flags=%#x)\n", flags)); - return waitpid(-1, status, flags); -#else -#ifdef SYSV - int (*save)(); - - if (block == 0) { - gotsigchild = 0; - save = signal(SIGCLD, onsigchild); - signal(SIGCLD, save); - if (gotsigchild == 0) - return 0; - } - return wait(status); -#else - if (block == 0) - return 0; - return wait(status); -#endif -#endif -} - -/* - * return 1 if there are stopped jobs, otherwise 0 - */ -int job_warning = 0; -int -stoppedjobs(void) -{ - int jobno; - struct job *jp; - - if (job_warning || jobs_invalid) - return (0); - for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { - if (jp->used == 0) - continue; - if (jp->state == JOBSTOPPED) { - out2str("You have stopped jobs.\n"); - job_warning = 2; - return (1); - } - } - - return (0); -} - -/* - * Return a string identifying a command (to be printed by the - * jobs command). - */ - -STATIC char *cmdnextc; -STATIC int cmdnleft; - -void -commandtext(struct procstat *ps, union node *n) -{ - int len; - - cmdnextc = ps->cmd; - if (iflag || mflag || sizeof(ps->cmd) <= 60) - len = sizeof(ps->cmd); - else if (sizeof ps->cmd <= 400) - len = 50; - else if (sizeof ps->cmd <= 800) - len = 80; - else - len = sizeof(ps->cmd) / 10; - cmdnleft = len; - cmdtxt(n); - if (cmdnleft <= 0) { - char *p = ps->cmd + len - 4; - p[0] = '.'; - p[1] = '.'; - p[2] = '.'; - p[3] = 0; - } else - *cmdnextc = '\0'; - - VTRACE(DBG_JOBS, - ("commandtext: ps->cmd %p, end %p, left %d\n\t\"%s\"\n", - ps->cmd, cmdnextc, cmdnleft, ps->cmd)); -} - - -STATIC void -cmdtxt(union node *n) -{ - union node *np; - struct nodelist *lp; - const char *p; - int i; - - if (n == NULL || cmdnleft <= 0) - return; - switch (n->type) { - case NSEMI: - cmdtxt(n->nbinary.ch1); - cmdputs("; "); - cmdtxt(n->nbinary.ch2); - break; - case NAND: - cmdtxt(n->nbinary.ch1); - cmdputs(" && "); - cmdtxt(n->nbinary.ch2); - break; - case NOR: - cmdtxt(n->nbinary.ch1); - cmdputs(" || "); - cmdtxt(n->nbinary.ch2); - break; - case NDNOT: - cmdputs("! "); - /* FALLTHROUGH */ - case NNOT: - cmdputs("! "); - cmdtxt(n->nnot.com); - break; - case NPIPE: - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - cmdtxt(lp->n); - if (lp->next) - cmdputs(" | "); - } - if (n->npipe.backgnd) - cmdputs(" &"); - break; - case NSUBSHELL: - cmdputs("("); - cmdtxt(n->nredir.n); - cmdputs(")"); - break; - case NREDIR: - case NBACKGND: - cmdtxt(n->nredir.n); - break; - case NIF: - cmdputs("if "); - cmdtxt(n->nif.test); - cmdputs("; then "); - cmdtxt(n->nif.ifpart); - if (n->nif.elsepart) { - cmdputs("; else "); - cmdtxt(n->nif.elsepart); - } - cmdputs("; fi"); - break; - case NWHILE: - cmdputs("while "); - goto until; - case NUNTIL: - cmdputs("until "); - until: - cmdtxt(n->nbinary.ch1); - cmdputs("; do "); - cmdtxt(n->nbinary.ch2); - cmdputs("; done"); - break; - case NFOR: - cmdputs("for "); - cmdputs(n->nfor.var); - cmdputs(" in "); - cmdlist(n->nfor.args, 1); - cmdputs("; do "); - cmdtxt(n->nfor.body); - cmdputs("; done"); - break; - case NCASE: - cmdputs("case "); - cmdputs(n->ncase.expr->narg.text); - cmdputs(" in "); - for (np = n->ncase.cases; np; np = np->nclist.next) { - cmdtxt(np->nclist.pattern); - cmdputs(") "); - cmdtxt(np->nclist.body); - switch (n->type) { /* switch (not if) for later */ - case NCLISTCONT: - cmdputs(";& "); - break; - default: - cmdputs(";; "); - break; - } - } - cmdputs("esac"); - break; - case NDEFUN: - cmdputs(n->narg.text); - cmdputs("() { ... }"); - break; - case NCMD: - cmdlist(n->ncmd.args, 1); - cmdlist(n->ncmd.redirect, 0); - if (n->ncmd.backgnd) - cmdputs(" &"); - break; - case NARG: - cmdputs(n->narg.text); - break; - case NTO: - p = ">"; i = 1; goto redir; - case NCLOBBER: - p = ">|"; i = 1; goto redir; - case NAPPEND: - p = ">>"; i = 1; goto redir; - case NTOFD: - p = ">&"; i = 1; goto redir; - case NFROM: - p = "<"; i = 0; goto redir; - case NFROMFD: - p = "<&"; i = 0; goto redir; - case NFROMTO: - p = "<>"; i = 0; goto redir; - redir: - if (n->nfile.fd != i) - cmdputi(n->nfile.fd); - cmdputs(p); - if (n->type == NTOFD || n->type == NFROMFD) { - if (n->ndup.dupfd < 0) - cmdputs("-"); - else - cmdputi(n->ndup.dupfd); - } else { - cmdtxt(n->nfile.fname); - } - break; - case NHERE: - case NXHERE: - cmdputs("<<..."); - break; - default: - cmdputs("???"); - break; - } -} - -STATIC void -cmdlist(union node *np, int sep) -{ - for (; np; np = np->narg.next) { - if (!sep) - cmdputs(" "); - cmdtxt(np); - if (sep && np->narg.next) - cmdputs(" "); - } -} - - -STATIC void -cmdputs(const char *s) -{ - const char *p, *str = 0; - char c, cc[2] = " "; - char *nextc; - int nleft; - int subtype = 0; - int quoted = 0; - static char vstype[16][4] = { "", "}", "-", "+", "?", "=", - "#", "##", "%", "%%", "}" }; - - p = s; - nextc = cmdnextc; - nleft = cmdnleft; - while (nleft > 0 && (c = *p++) != 0) { - switch (c) { - case CTLNONL: - c = '\0'; - break; - case CTLESC: - c = *p++; - break; - case CTLVAR: - subtype = *p++; - if (subtype & VSLINENO) { /* undo LINENO hack */ - if ((subtype & VSTYPE) == VSLENGTH) - str = "${#LINENO"; /*}*/ - else - str = "${LINENO"; /*}*/ - while (is_digit(*p)) - p++; - } else if ((subtype & VSTYPE) == VSLENGTH) - str = "${#"; /*}*/ - else - str = "${"; /*}*/ - if (!(subtype & VSQUOTE) != !(quoted & 1)) { - quoted ^= 1; - c = '"'; - } else { - c = *str++; - } - break; - case CTLENDVAR: /*{*/ - c = '}'; - if (quoted & 1) - str = "\""; - quoted >>= 1; - subtype = 0; - break; - case CTLBACKQ: - c = '$'; - str = "(...)"; - break; - case CTLBACKQ+CTLQUOTE: - c = '"'; - str = "$(...)\""; - break; - case CTLARI: - c = '$'; - if (*p == ' ') - p++; - str = "(("; /*))*/ - break; - case CTLENDARI: /*((*/ - c = ')'; - str = ")"; - break; - case CTLQUOTEMARK: - quoted ^= 1; - c = '"'; - break; - case CTLQUOTEEND: - quoted >>= 1; - c = '"'; - break; - case '=': - if (subtype == 0) - break; - str = vstype[subtype & VSTYPE]; - if (subtype & VSNUL) - c = ':'; - else - c = *str++; /*{*/ - if (c != '}') - quoted <<= 1; - else if (*p == CTLENDVAR) - c = *str++; - subtype = 0; - break; - case '\'': - case '\\': - case '"': - case '$': - /* These can only happen inside quotes */ - cc[0] = c; - str = cc; - c = '\\'; - break; - default: - break; - } - if (c != '\0') do { /* c == 0 implies nothing in str */ - *nextc++ = c; - } while (--nleft > 0 && str && (c = *str++)); - str = 0; - } - if ((quoted & 1) && nleft) { - *nextc++ = '"'; - nleft--; - } - cmdnleft = nleft; - cmdnextc = nextc; -} diff --git a/bin/sh/jobs.h b/bin/sh/jobs.h deleted file mode 100644 index a587e9e..0000000 --- a/bin/sh/jobs.h +++ /dev/null @@ -1,105 +0,0 @@ -/* $NetBSD: jobs.h,v 1.23 2018/09/11 03:30:40 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)jobs.h 8.2 (Berkeley) 5/4/95 - */ - -#include "output.h" - -/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ -#define FORK_FG 0 -#define FORK_BG 1 -#define FORK_NOJOB 2 - -/* mode flags for showjob(s) */ -#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ -#define SHOW_MULTILINE 0x02 /* one line per process */ -#define SHOW_PID 0x04 /* include process pid */ -#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ -#define SHOW_SIGNALLED 0x10 /* only if stopped/exited on signal */ -#define SHOW_ISSIG 0x20 /* job was signalled */ -#define SHOW_NO_FREE 0x40 /* do not free job */ - - -/* - * A job structure contains information about a job. A job is either a - * single process or a set of processes contained in a pipeline. In the - * latter case, pidlist will be non-NULL, and will point to a -1 terminated - * array of pids. - */ -#define MAXCMDTEXT 200 - -struct procstat { - pid_t pid; /* process id */ - int status; /* last process status from wait() */ - char cmd[MAXCMDTEXT];/* text of command being run */ -}; - -struct job { - struct procstat ps0; /* status of process */ - struct procstat *ps; /* status or processes when more than one */ - void *ref; /* temporary reference, used variously */ - int nprocs; /* number of processes */ - pid_t pgrp; /* process group of this job */ - char state; -#define JOBRUNNING 0 /* at least one proc running */ -#define JOBSTOPPED 1 /* all procs are stopped */ -#define JOBDONE 2 /* all procs are completed */ - char used; /* true if this entry is in used */ - char flags; -#define JOBCHANGED 1 /* set if status has changed */ -#define JOBWANTED 2 /* set if this is a job being sought */ -#define JPIPEFAIL 4 /* set if -o pipefail when job created */ -#if JOBS - char jobctl; /* job running under job control */ - int prev_job; /* previous job index */ -#endif -}; - -extern pid_t backgndpid; /* pid of last background process */ -extern int job_warning; /* user was warned about stopped jobs */ - -void setjobctl(int); -void showjobs(struct output *, int); -struct job *makejob(union node *, int); -int forkshell(struct job *, union node *, int); -void forkchild(struct job *, union node *, int, int); -int forkparent(struct job *, union node *, int, pid_t); -int waitforjob(struct job *); -int stoppedjobs(void); -void commandtext(struct procstat *, union node *); -int getjobpgrp(const char *); - -#if ! JOBS -#define setjobctl(on) /* do nothing */ -#endif diff --git a/bin/sh/machdep.h b/bin/sh/machdep.h deleted file mode 100644 index 14e803b..0000000 --- a/bin/sh/machdep.h +++ /dev/null @@ -1,47 +0,0 @@ -/* $NetBSD: machdep.h,v 1.11 2003/08/07 09:05:33 agc Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)machdep.h 8.2 (Berkeley) 5/4/95 - */ - -/* - * Most machines require the value returned from malloc to be aligned - * in some way. The following macro will get this right on many machines. - */ - -#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1) -/* - * It appears that grabstackstr() will barf with such alignments - * because stalloc() will return a string allocated in a new stackblock. - */ -#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE) diff --git a/bin/sh/mail.c b/bin/sh/mail.c deleted file mode 100644 index 484b50f..0000000 --- a/bin/sh/mail.c +++ /dev/null @@ -1,144 +0,0 @@ -/* $NetBSD: mail.c,v 1.18 2017/06/04 20:28:13 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)mail.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: mail.c,v 1.18 2017/06/04 20:28:13 kre Exp $"); -#endif -#endif /* not lint */ - -/* - * Routines to check for mail. (Perhaps make part of main.c?) - */ -#include <sys/types.h> -#include <sys/stat.h> -#include <stdlib.h> -#include <string.h> - -#include "shell.h" -#include "exec.h" /* defines padvance() */ -#include "var.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "mail.h" - - -#define MAXMBOXES 10 - - -STATIC int nmboxes; /* number of mailboxes */ -STATIC off_t mailsize[MAXMBOXES]; /* sizes of mailboxes */ - - - -/* - * Print appropriate message(s) if mail has arrived. If the argument is - * nozero, then the value of MAIL has changed, so we just update the - * values. - */ - -void -chkmail(int silent) -{ - int i; - const char *mpath; - char *p; - char *q; - struct stackmark smark; - struct stat statb; - - if (silent) - nmboxes = 10; - if (nmboxes == 0) - return; - setstackmark(&smark); - mpath = mpathset() ? mpathval() : mailval(); - for (i = 0 ; i < nmboxes ; i++) { - p = padvance(&mpath, nullstr, 1); - if (p == NULL) - break; - stunalloc(p); - if (*p == '\0') - continue; - for (q = p ; *q ; q++) - ; - if (q[-1] != '/') - abort(); - q[-1] = '\0'; /* delete trailing '/' */ -#ifdef notdef /* this is what the System V shell claims to do (it lies) */ - if (stat(p, &statb) < 0) - statb.st_mtime = 0; - if (statb.st_mtime > mailtime[i] && ! silent) { - out2str(pathopt ? pathopt : "you have mail"); - out2c('\n'); - } - mailtime[i] = statb.st_mtime; -#else /* this is what it should do */ - if (stat(p, &statb) < 0) - statb.st_size = 0; - if (statb.st_size > mailsize[i] && ! silent) { - const char *pp; - - if ((pp = pathopt) != NULL) { - int len = 0; - - while (*pp && *pp != ':') - len++, pp++; - if (len == 0) { - CHECKSTRSPACE(16, p); - strcat(p, ": file changed"); - } else { - while (stackblocksize() <= len) - growstackblock(); - p = stackblock(); - memcpy(p, pathopt, len); - p[len] = '\0'; - } - pp = p; - } else - pp = "you have mail"; - - out2str(pp); - out2c('\n'); - } - mailsize[i] = statb.st_size; -#endif - } - nmboxes = i; - popstackmark(&smark); -} diff --git a/bin/sh/mail.h b/bin/sh/mail.h deleted file mode 100644 index 9ea7c21..0000000 --- a/bin/sh/mail.h +++ /dev/null @@ -1,37 +0,0 @@ -/* $NetBSD: mail.h,v 1.10 2003/08/07 09:05:34 agc Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)mail.h 8.2 (Berkeley) 5/4/95 - */ - -void chkmail(int); diff --git a/bin/sh/main.c b/bin/sh/main.c deleted file mode 100644 index a023611..0000000 --- a/bin/sh/main.c +++ /dev/null @@ -1,393 +0,0 @@ -/* $NetBSD: main.c,v 1.80 2019/01/19 14:20:22 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -__COPYRIGHT("@(#) Copyright (c) 1991, 1993\ - The Regents of the University of California. All rights reserved."); -#endif /* not lint */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)main.c 8.7 (Berkeley) 7/19/95"; -#else -__RCSID("$NetBSD: main.c,v 1.80 2019/01/19 14:20:22 kre Exp $"); -#endif -#endif /* not lint */ - -#include <errno.h> -#include <stdio.h> -#include <signal.h> -#include <sys/stat.h> -#include <unistd.h> -#include <stdlib.h> -#include <locale.h> -#include <fcntl.h> - - -#include "shell.h" -#include "main.h" -#include "mail.h" -#include "options.h" -#include "builtins.h" -#include "output.h" -#include "parser.h" -#include "nodes.h" -#include "expand.h" -#include "eval.h" -#include "jobs.h" -#include "input.h" -#include "trap.h" -#include "var.h" -#include "show.h" -#include "memalloc.h" -#include "error.h" -#include "init.h" -#include "mystring.h" -#include "exec.h" -#include "cd.h" -#include "redir.h" - -#define PROFILE 0 - -int rootpid; -int rootshell; -struct jmploc main_handler; -int max_user_fd; -#if PROFILE -short profile_buf[16384]; -extern int etext(); -#endif - -STATIC void read_profile(const char *); - -/* - * Main routine. We initialize things, parse the arguments, execute - * profiles if we're a login shell, and then call cmdloop to execute - * commands. The setjmp call sets up the location to jump to when an - * exception occurs. When an exception occurs the variable "state" - * is used to figure out how far we had gotten. - */ - -int -main(int argc, char **argv) -{ - struct stackmark smark; - volatile int state; - char *shinit; - uid_t uid; - gid_t gid; - - uid = getuid(); - gid = getgid(); - - max_user_fd = fcntl(0, F_MAXFD); - if (max_user_fd < 2) - max_user_fd = 2; - - setlocale(LC_ALL, ""); - - posix = getenv("POSIXLY_CORRECT") != NULL; -#if PROFILE - monitor(4, etext, profile_buf, sizeof profile_buf, 50); -#endif - state = 0; - if (setjmp(main_handler.loc)) { - /* - * When a shell procedure is executed, we raise the - * exception EXSHELLPROC to clean up before executing - * the shell procedure. - */ - switch (exception) { - case EXSHELLPROC: - rootpid = getpid(); - rootshell = 1; - minusc = NULL; - state = 3; - break; - - case EXEXEC: - exitstatus = exerrno; - break; - - case EXERROR: - exitstatus = 2; - break; - - default: - break; - } - - if (exception != EXSHELLPROC) { - if (state == 0 || iflag == 0 || ! rootshell || - exception == EXEXIT) - exitshell(exitstatus); - } - reset(); - if (exception == EXINT) { - out2c('\n'); - flushout(&errout); - } - popstackmark(&smark); - FORCEINTON; /* enable interrupts */ - if (state == 1) - goto state1; - else if (state == 2) - goto state2; - else if (state == 3) - goto state3; - else - goto state4; - } - handler = &main_handler; -#ifdef DEBUG -#if DEBUG >= 2 - debug = 1; /* this may be reset by procargs() later */ -#endif - opentrace(); - trputs("Shell args: "); trargs(argv); -#if DEBUG >= 3 - set_debug(((DEBUG)==3 ? "_@" : "++"), 1); -#endif -#endif - rootpid = getpid(); - rootshell = 1; - init(); - initpwd(); - setstackmark(&smark); - procargs(argc, argv); - - /* - * Limit bogus system(3) or popen(3) calls in setuid binaries, - * by requiring the -p flag - */ - if (!pflag && (uid != geteuid() || gid != getegid())) { - setuid(uid); - setgid(gid); - /* PS1 might need to be changed accordingly. */ - choose_ps1(); - } - - if (argv[0] && argv[0][0] == '-') { - state = 1; - read_profile("/etc/profile"); - state1: - state = 2; - read_profile(".profile"); - } - state2: - state = 3; - if ((iflag || !posix) && - getuid() == geteuid() && getgid() == getegid()) { - struct stackmark env_smark; - - setstackmark(&env_smark); - if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { - state = 3; - read_profile(expandenv(shinit)); - } - popstackmark(&env_smark); - } - state3: - state = 4; - line_number = 1; /* undo anything from profile files */ - - if (sflag == 0 || minusc) { - static int sigs[] = { - SIGINT, SIGQUIT, SIGHUP, -#ifdef SIGTSTP - SIGTSTP, -#endif - SIGPIPE - }; -#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) - size_t i; - - for (i = 0; i < SIGSSIZE; i++) - setsignal(sigs[i], 0); - } - - if (minusc) - evalstring(minusc, sflag ? 0 : EV_EXIT); - - if (sflag || minusc == NULL) { - state4: /* XXX ??? - why isn't this before the "if" statement */ - cmdloop(1); - } -#if PROFILE - monitor(0); -#endif - line_number = plinno; - exitshell(exitstatus); - /* NOTREACHED */ -} - - -/* - * Read and execute commands. "Top" is nonzero for the top level command - * loop; it turns on prompting if the shell is interactive. - */ - -void -cmdloop(int top) -{ - union node *n; - struct stackmark smark; - int inter; - int numeof = 0; - enum skipstate skip; - - CTRACE(DBG_ALWAYS, ("cmdloop(%d) called\n", top)); - setstackmark(&smark); - for (;;) { - if (pendingsigs) - dotrap(); - inter = 0; - if (iflag == 1 && top) { - inter = 1; - showjobs(out2, SHOW_CHANGED); - chkmail(0); - flushout(&errout); - nflag = 0; - } - n = parsecmd(inter); - VXTRACE(DBG_PARSE|DBG_EVAL|DBG_CMDS,("cmdloop: "),showtree(n)); - if (n == NEOF) { - if (!top || numeof >= 50) - break; - if (nflag) - break; - if (!stoppedjobs()) { - if (!iflag || !Iflag) - break; - out2str("\nUse \"exit\" to leave shell.\n"); - } - numeof++; - } else if (n != NULL && nflag == 0) { - job_warning = (job_warning == 2) ? 1 : 0; - numeof = 0; - evaltree(n, 0); - } - rststackmark(&smark); - - /* - * Any SKIP* can occur here! SKIP(FUNC|BREAK|CONT) occur when - * a dotcmd is in a loop or a function body and appropriate - * built-ins occurs in file scope in the sourced file. Values - * other than SKIPFILE are reset by the appropriate eval*() - * that contained the dotcmd() call. - */ - skip = current_skipstate(); - if (skip != SKIPNONE) { - if (skip == SKIPFILE) - stop_skipping(); - break; - } - } - popstackmark(&smark); -} - - - -/* - * Read /etc/profile or .profile. Return on error. - */ - -STATIC void -read_profile(const char *name) -{ - int fd; - int xflag_set = 0; - int vflag_set = 0; - - if (*name == '\0') - return; - - INTOFF; - if ((fd = open(name, O_RDONLY)) >= 0) - setinputfd(fd, 1); - INTON; - if (fd < 0) - return; - /* -q turns off -x and -v just when executing init files */ - if (qflag) { - if (xflag) - xflag = 0, xflag_set = 1; - if (vflag) - vflag = 0, vflag_set = 1; - } - cmdloop(0); - if (qflag) { - if (xflag_set) - xflag = 1; - if (vflag_set) - vflag = 1; - } - popfile(); -} - - - -/* - * Read a file containing shell functions. - */ - -void -readcmdfile(char *name) -{ - int fd; - - INTOFF; - if ((fd = open(name, O_RDONLY)) >= 0) - setinputfd(fd, 1); - else - error("Can't open %s", name); - INTON; - cmdloop(0); - popfile(); -} - - - -int -exitcmd(int argc, char **argv) -{ - if (stoppedjobs()) - return 0; - if (argc > 1) - exitshell(number(argv[1])); - else - exitshell_savedstatus(); - /* NOTREACHED */ -} diff --git a/bin/sh/main.h b/bin/sh/main.h deleted file mode 100644 index db1d576..0000000 --- a/bin/sh/main.h +++ /dev/null @@ -1,42 +0,0 @@ -/* $NetBSD: main.h,v 1.12 2018/12/03 02:38:30 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)main.h 8.2 (Berkeley) 5/4/95 - */ - -extern int rootpid; /* pid of main shell */ -extern int rootshell; /* true if we aren't a child of the main shell */ -extern struct jmploc main_handler; /* top level exception handler */ - -void readcmdfile(char *); -void cmdloop(int); diff --git a/bin/sh/memalloc.c b/bin/sh/memalloc.c deleted file mode 100644 index da8ff3c..0000000 --- a/bin/sh/memalloc.c +++ /dev/null @@ -1,334 +0,0 @@ -/* $NetBSD: memalloc.c,v 1.32 2018/08/22 20:08:54 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: memalloc.c,v 1.32 2018/08/22 20:08:54 kre Exp $"); -#endif -#endif /* not lint */ - -#include <stdlib.h> -#include <unistd.h> - -#include "shell.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "machdep.h" -#include "mystring.h" - -/* - * Like malloc, but returns an error when out of space. - */ - -pointer -ckmalloc(size_t nbytes) -{ - pointer p; - - p = malloc(nbytes); - if (p == NULL) - error("Out of space"); - return p; -} - - -/* - * Same for realloc. - */ - -pointer -ckrealloc(pointer p, int nbytes) -{ - p = realloc(p, nbytes); - if (p == NULL) - error("Out of space"); - return p; -} - - -/* - * Make a copy of a string in safe storage. - */ - -char * -savestr(const char *s) -{ - char *p; - - p = ckmalloc(strlen(s) + 1); - scopy(s, p); - return p; -} - - -/* - * Parse trees for commands are allocated in lifo order, so we use a stack - * to make this more efficient, and also to avoid all sorts of exception - * handling code to handle interrupts in the middle of a parse. - * - * The size 504 was chosen because the Ultrix malloc handles that size - * well. - */ - -#define MINSIZE 504 /* minimum size of a block */ - -struct stack_block { - struct stack_block *prev; - char space[MINSIZE]; -}; - -struct stack_block stackbase; -struct stack_block *stackp = &stackbase; -struct stackmark *markp; -char *stacknxt = stackbase.space; -int stacknleft = MINSIZE; -int sstrnleft; -int herefd = -1; - -pointer -stalloc(int nbytes) -{ - char *p; - - nbytes = SHELL_ALIGN(nbytes); - if (nbytes > stacknleft) { - int blocksize; - struct stack_block *sp; - - blocksize = nbytes; - if (blocksize < MINSIZE) - blocksize = MINSIZE; - INTOFF; - sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); - sp->prev = stackp; - stacknxt = sp->space; - stacknleft = blocksize; - stackp = sp; - INTON; - } - p = stacknxt; - stacknxt += nbytes; - stacknleft -= nbytes; - return p; -} - - -void -stunalloc(pointer p) -{ - if (p == NULL) { /*DEBUG */ - write(2, "stunalloc\n", 10); - abort(); - } - stacknleft += stacknxt - (char *)p; - stacknxt = p; -} - - -/* save the current status of the sh stack */ -void -setstackmark(struct stackmark *mark) -{ - mark->stackp = stackp; - mark->stacknxt = stacknxt; - mark->stacknleft = stacknleft; - mark->sstrnleft = sstrnleft; - mark->marknext = markp; - markp = mark; -} - -/* reset the stack mark, and remove it from the list of marks */ -void -popstackmark(struct stackmark *mark) -{ - markp = mark->marknext; /* delete mark from the list */ - rststackmark(mark); /* and reset stack */ -} - -/* reset the shell stack to its state recorded in the stack mark */ -void -rststackmark(struct stackmark *mark) -{ - struct stack_block *sp; - - INTOFF; - while (stackp != mark->stackp) { - /* delete any recently allocated mem blocks */ - sp = stackp; - stackp = sp->prev; - ckfree(sp); - } - stacknxt = mark->stacknxt; - stacknleft = mark->stacknleft; - sstrnleft = mark->sstrnleft; - INTON; -} - - -/* - * When the parser reads in a string, it wants to stick the string on the - * stack and only adjust the stack pointer when it knows how big the - * string is. Stackblock (defined in stack.h) returns a pointer to a block - * of space on top of the stack and stackblocklen returns the length of - * this block. Growstackblock will grow this space by at least one byte, - * possibly moving it (like realloc). Grabstackblock actually allocates the - * part of the block that has been used. - */ - -void -growstackblock(void) -{ - int newlen = SHELL_ALIGN(stacknleft * 2 + 100); - - INTOFF; - if (stacknxt == stackp->space && stackp != &stackbase) { - struct stack_block *oldstackp; - struct stackmark *xmark; - struct stack_block *sp; - - oldstackp = stackp; - sp = stackp; - stackp = sp->prev; - sp = ckrealloc((pointer)sp, - sizeof(struct stack_block) - MINSIZE + newlen); - sp->prev = stackp; - stackp = sp; - stacknxt = sp->space; - sstrnleft += newlen - stacknleft; - stacknleft = newlen; - - /* - * Stack marks pointing to the start of the old block - * must be relocated to point to the new block - */ - xmark = markp; - while (xmark != NULL && xmark->stackp == oldstackp) { - xmark->stackp = stackp; - xmark->stacknxt = stacknxt; - xmark->sstrnleft += stacknleft - xmark->stacknleft; - xmark->stacknleft = stacknleft; - xmark = xmark->marknext; - } - } else { - char *oldspace = stacknxt; - int oldlen = stacknleft; - char *p = stalloc(newlen); - - (void)memcpy(p, oldspace, oldlen); - stacknxt = p; /* free the space */ - stacknleft += newlen; /* we just allocated */ - } - INTON; -} - -void -grabstackblock(int len) -{ - len = SHELL_ALIGN(len); - stacknxt += len; - stacknleft -= len; -} - -/* - * The following routines are somewhat easier to use than the above. - * The user declares a variable of type STACKSTR, which may be declared - * to be a register. The macro STARTSTACKSTR initializes things. Then - * the user uses the macro STPUTC to add characters to the string. In - * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is - * grown as necessary. When the user is done, she can just leave the - * string there and refer to it using stackblock(). Or she can allocate - * the space for it using grabstackstr(). If it is necessary to allow - * someone else to use the stack temporarily and then continue to grow - * the string, the user should use grabstack to allocate the space, and - * then call ungrabstr(p) to return to the previous mode of operation. - * - * USTPUTC is like STPUTC except that it doesn't check for overflow. - * CHECKSTACKSPACE can be called before USTPUTC to ensure that there - * is space for at least one character. - */ - -char * -growstackstr(void) -{ - int len = stackblocksize(); - if (herefd >= 0 && len >= 1024) { - xwrite(herefd, stackblock(), len); - sstrnleft = len - 1; - return stackblock(); - } - growstackblock(); - sstrnleft = stackblocksize() - len - 1; - return stackblock() + len; -} - -/* - * Called from CHECKSTRSPACE. - */ - -char * -makestrspace(void) -{ - int len = stackblocksize() - sstrnleft; - growstackblock(); - sstrnleft = stackblocksize() - len; - return stackblock() + len; -} - -/* - * Note that this only works to release stack space for reuse - * if nothing else has allocated space on the stack since the grabstackstr() - * - * "s" is the start of the area to be released, and "p" represents the end - * of the string we have stored beyond there and are now releasing. - * (ie: "p" should be the same as in the call to grabstackstr()). - * - * stunalloc(s) and ungrabstackstr(s, p) are almost interchangable after - * a grabstackstr(), however the latter also returns string space so we - * can just continue with STPUTC() etc without needing a new STARTSTACKSTR(s) - */ -void -ungrabstackstr(char *s, char *p) -{ -#ifdef DEBUG - if (s < stacknxt || stacknxt + stacknleft < s) - abort(); -#endif - stacknleft += stacknxt - s; - stacknxt = s; - sstrnleft = stacknleft - (p - s); -} diff --git a/bin/sh/memalloc.h b/bin/sh/memalloc.h deleted file mode 100644 index ed6669e..0000000 --- a/bin/sh/memalloc.h +++ /dev/null @@ -1,79 +0,0 @@ -/* $NetBSD: memalloc.h,v 1.18 2018/08/22 20:08:54 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)memalloc.h 8.2 (Berkeley) 5/4/95 - */ - -struct stackmark { - struct stack_block *stackp; - char *stacknxt; - int stacknleft; - int sstrnleft; - struct stackmark *marknext; -}; - - -extern char *stacknxt; -extern int stacknleft; -extern int sstrnleft; -extern int herefd; - -pointer ckmalloc(size_t); -pointer ckrealloc(pointer, int); -char *savestr(const char *); -pointer stalloc(int); -void stunalloc(pointer); -void setstackmark(struct stackmark *); -void popstackmark(struct stackmark *); -void rststackmark(struct stackmark *); -void growstackblock(void); -void grabstackblock(int); -char *growstackstr(void); -char *makestrspace(void); -void ungrabstackstr(char *, char *); - - - -#define stackblock() stacknxt -#define stackblocksize() stacknleft -#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() -#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) -#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(); } -#define USTPUTC(c, p) (--sstrnleft, *p++ = (c)) -#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), sstrnleft++, *p = '\0') : (*p = '\0')) -#define STUNPUTC(p) (++sstrnleft, --p) -#define STTOPC(p) p[-1] -#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount)) -#define grabstackstr(p) stalloc((p) - stackblock()) - -#define ckfree(p) free((pointer)(p)) diff --git a/bin/sh/miscbltin.c b/bin/sh/miscbltin.c deleted file mode 100644 index 3988532..0000000 --- a/bin/sh/miscbltin.c +++ /dev/null @@ -1,458 +0,0 @@ -/* $NetBSD: miscbltin.c,v 1.44 2017/05/13 15:03:34 gson Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: miscbltin.c,v 1.44 2017/05/13 15:03:34 gson Exp $"); -#endif -#endif /* not lint */ - -/* - * Miscelaneous builtins. - */ - -#include <sys/types.h> /* quad_t */ -#include <sys/param.h> /* BSD4_4 */ -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <unistd.h> -#include <stdlib.h> -#include <ctype.h> -#include <errno.h> - -#include "shell.h" -#include "options.h" -#include "var.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "builtins.h" -#include "mystring.h" - -#undef rflag - - - -/* - * The read builtin. - * Backslahes escape the next char unless -r is specified. - * - * This uses unbuffered input, which may be avoidable in some cases. - * - * Note that if IFS=' :' then read x y should work so that: - * 'a b' x='a', y='b' - * ' a b ' x='a', y='b' - * ':b' x='', y='b' - * ':' x='', y='' - * '::' x='', y='' - * ': :' x='', y='' - * ':::' x='', y='::' - * ':b c:' x='', y='b c:' - */ - -int -readcmd(int argc, char **argv) -{ - char **ap; - char c; - int rflag; - char *prompt; - const char *ifs; - char *p; - int startword; - int status; - int i; - int is_ifs; - int saveall = 0; - - rflag = 0; - prompt = NULL; - while ((i = nextopt("p:r")) != '\0') { - if (i == 'p') - prompt = optionarg; - else - rflag = 1; - } - - if (prompt && isatty(0)) { - out2str(prompt); - flushall(); - } - - if (*(ap = argptr) == NULL) - error("arg count"); - - if ((ifs = bltinlookup("IFS", 1)) == NULL) - ifs = " \t\n"; - - status = 0; - startword = 2; - STARTSTACKSTR(p); - for (;;) { - if (read(0, &c, 1) != 1) { - status = 1; - break; - } - if (c == '\0') - continue; - if (c == '\\' && !rflag) { - if (read(0, &c, 1) != 1) { - status = 1; - break; - } - if (c != '\n') - STPUTC(c, p); - continue; - } - if (c == '\n') - break; - if (strchr(ifs, c)) - is_ifs = strchr(" \t\n", c) ? 1 : 2; - else - is_ifs = 0; - - if (startword != 0) { - if (is_ifs == 1) { - /* Ignore leading IFS whitespace */ - if (saveall) - STPUTC(c, p); - continue; - } - if (is_ifs == 2 && startword == 1) { - /* Only one non-whitespace IFS per word */ - startword = 2; - if (saveall) - STPUTC(c, p); - continue; - } - } - - if (is_ifs == 0) { - /* append this character to the current variable */ - startword = 0; - if (saveall) - /* Not just a spare terminator */ - saveall++; - STPUTC(c, p); - continue; - } - - /* end of variable... */ - startword = is_ifs; - - if (ap[1] == NULL) { - /* Last variable needs all IFS chars */ - saveall++; - STPUTC(c, p); - continue; - } - - STACKSTRNUL(p); - setvar(*ap, stackblock(), 0); - ap++; - STARTSTACKSTR(p); - } - STACKSTRNUL(p); - - /* Remove trailing IFS chars */ - for (; stackblock() <= --p; *p = 0) { - if (!strchr(ifs, *p)) - break; - if (strchr(" \t\n", *p)) - /* Always remove whitespace */ - continue; - if (saveall > 1) - /* Don't remove non-whitespace unless it was naked */ - break; - } - setvar(*ap, stackblock(), 0); - - /* Set any remaining args to "" */ - while (*++ap != NULL) - setvar(*ap, nullstr, 0); - return status; -} - - - -int -umaskcmd(int argc, char **argv) -{ - char *ap; - int mask; - int i; - int symbolic_mode = 0; - - while ((i = nextopt("S")) != '\0') { - symbolic_mode = 1; - } - - INTOFF; - mask = umask(0); - umask(mask); - INTON; - - if ((ap = *argptr) == NULL) { - if (symbolic_mode) { - char u[4], g[4], o[4]; - - i = 0; - if ((mask & S_IRUSR) == 0) - u[i++] = 'r'; - if ((mask & S_IWUSR) == 0) - u[i++] = 'w'; - if ((mask & S_IXUSR) == 0) - u[i++] = 'x'; - u[i] = '\0'; - - i = 0; - if ((mask & S_IRGRP) == 0) - g[i++] = 'r'; - if ((mask & S_IWGRP) == 0) - g[i++] = 'w'; - if ((mask & S_IXGRP) == 0) - g[i++] = 'x'; - g[i] = '\0'; - - i = 0; - if ((mask & S_IROTH) == 0) - o[i++] = 'r'; - if ((mask & S_IWOTH) == 0) - o[i++] = 'w'; - if ((mask & S_IXOTH) == 0) - o[i++] = 'x'; - o[i] = '\0'; - - out1fmt("u=%s,g=%s,o=%s\n", u, g, o); - } else { - out1fmt("%.4o\n", mask); - } - } else { - if (isdigit((unsigned char)*ap)) { - mask = 0; - do { - if (*ap >= '8' || *ap < '0') - error("Illegal number: %s", argv[1]); - mask = (mask << 3) + (*ap - '0'); - } while (*++ap != '\0'); - umask(mask); - } else { - void *set; - - INTOFF; - if ((set = setmode(ap)) != 0) { - mask = getmode(set, ~mask & 0777); - ckfree(set); - } - INTON; - if (!set) - error("Cannot set mode `%s' (%s)", ap, - strerror(errno)); - - umask(~mask & 0777); - } - } - return 0; -} - -/* - * ulimit builtin - * - * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and - * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with - * ash by J.T. Conklin. - * - * Public domain. - */ - -struct limits { - const char *name; - const char *unit; - int cmd; - int factor; /* multiply by to get rlim_{cur,max} values */ - char option; -}; - -static const struct limits limits[] = { -#ifdef RLIMIT_CPU - { "time", "seconds", RLIMIT_CPU, 1, 't' }, -#endif -#ifdef RLIMIT_FSIZE - { "file", "blocks", RLIMIT_FSIZE, 512, 'f' }, -#endif -#ifdef RLIMIT_DATA - { "data", "kbytes", RLIMIT_DATA, 1024, 'd' }, -#endif -#ifdef RLIMIT_STACK - { "stack", "kbytes", RLIMIT_STACK, 1024, 's' }, -#endif -#ifdef RLIMIT_CORE - { "coredump", "blocks", RLIMIT_CORE, 512, 'c' }, -#endif -#ifdef RLIMIT_RSS - { "memory", "kbytes", RLIMIT_RSS, 1024, 'm' }, -#endif -#ifdef RLIMIT_MEMLOCK - { "locked memory","kbytes", RLIMIT_MEMLOCK, 1024, 'l' }, -#endif -#ifdef RLIMIT_NTHR - { "thread", "threads", RLIMIT_NTHR, 1, 'r' }, -#endif -#ifdef RLIMIT_NPROC - { "process", "processes", RLIMIT_NPROC, 1, 'p' }, -#endif -#ifdef RLIMIT_NOFILE - { "nofiles", "descriptors", RLIMIT_NOFILE, 1, 'n' }, -#endif -#ifdef RLIMIT_VMEM - { "vmemory", "kbytes", RLIMIT_VMEM, 1024, 'v' }, -#endif -#ifdef RLIMIT_SWAP - { "swap", "kbytes", RLIMIT_SWAP, 1024, 'w' }, -#endif -#ifdef RLIMIT_SBSIZE - { "sbsize", "bytes", RLIMIT_SBSIZE, 1, 'b' }, -#endif - { NULL, NULL, 0, 0, '\0' } -}; - -int -ulimitcmd(int argc, char **argv) -{ - int c; - rlim_t val = 0; - enum { SOFT = 0x1, HARD = 0x2 } - how = SOFT | HARD; - const struct limits *l; - int set, all = 0; - int optc, what; - struct rlimit limit; - - what = 'f'; - while ((optc = nextopt("HSabtfdscmlrpnv")) != '\0') - switch (optc) { - case 'H': - how = HARD; - break; - case 'S': - how = SOFT; - break; - case 'a': - all = 1; - break; - default: - what = optc; - } - - for (l = limits; l->name && l->option != what; l++) - ; - if (!l->name) - error("internal error (%c)", what); - - set = *argptr ? 1 : 0; - if (set) { - char *p = *argptr; - - if (all || argptr[1]) - error("too many arguments"); - if (strcmp(p, "unlimited") == 0) - val = RLIM_INFINITY; - else { - val = (rlim_t) 0; - - while ((c = *p++) >= '0' && c <= '9') - val = (val * 10) + (long)(c - '0'); - if (c) - error("bad number"); - val *= l->factor; - } - } - if (all) { - for (l = limits; l->name; l++) { - getrlimit(l->cmd, &limit); - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; - - out1fmt("%-13s (-%c %-11s) ", l->name, l->option, - l->unit); - if (val == RLIM_INFINITY) - out1fmt("unlimited\n"); - else - { - val /= l->factor; -#ifdef BSD4_4 - out1fmt("%lld\n", (long long) val); -#else - out1fmt("%ld\n", (long) val); -#endif - } - } - return 0; - } - - if (getrlimit(l->cmd, &limit) == -1) - error("error getting limit (%s)", strerror(errno)); - if (set) { - if (how & HARD) - limit.rlim_max = val; - if (how & SOFT) - limit.rlim_cur = val; - if (setrlimit(l->cmd, &limit) < 0) - error("error setting limit (%s)", strerror(errno)); - } else { - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; - - if (val == RLIM_INFINITY) - out1fmt("unlimited\n"); - else - { - val /= l->factor; -#ifdef BSD4_4 - out1fmt("%lld\n", (long long) val); -#else - out1fmt("%ld\n", (long) val); -#endif - } - } - return 0; -} diff --git a/bin/sh/miscbltin.h b/bin/sh/miscbltin.h deleted file mode 100644 index 4c12c82..0000000 --- a/bin/sh/miscbltin.h +++ /dev/null @@ -1,31 +0,0 @@ -/* $NetBSD: miscbltin.h,v 1.3 2003/08/21 17:57:53 christos Exp $ */ - -/* - * Copyright (c) 1997 Christos Zoulas. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -int readcmd(int, char **); -int umaskcmd(int, char **); -int ulimitcmd(int, char **); diff --git a/bin/sh/mkbuiltins b/bin/sh/mkbuiltins deleted file mode 100644 index 2ebf7ac..0000000 --- a/bin/sh/mkbuiltins +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh - -# $NetBSD: mkbuiltins,v 1.22 2009/10/06 19:56:58 apb Exp $ -# -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)mkbuiltins 8.2 (Berkeley) 5/4/95 - -havehist=1 -if [ x"$1" = x"-h" ]; then - havehist=0 - shift -fi - -shell=$1 -builtins=$2 -objdir=$3 - -havejobs=0 -if grep '^#define JOBS[ ]*1' ${shell} > /dev/null -then - havejobs=1 -fi - -exec <$builtins 3> ${objdir}/builtins.c 4> ${objdir}/builtins.h - -echo '/* - * This file was generated by the mkbuiltins program. - */ - -#include "shell.h" -#include "builtins.h" - -const struct builtincmd builtincmd[] = { -' >&3 - -echo '/* - * This file was generated by the mkbuiltins program. - */ - -#include <sys/cdefs.h> - -struct builtincmd { - const char *name; - int (*builtin)(int, char **); -}; - -extern const struct builtincmd builtincmd[]; -extern const struct builtincmd splbltincmd[]; - -' >&4 - -specials= - -while read line -do - set -- $line - [ -z "$1" ] && continue - case "$1" in - \#if*|\#def*|\#end*) - echo $line >&3 - echo $line >&4 - continue - ;; - \#*) - continue - ;; - esac - - func=$1 - shift - [ x"$1" = x'-j' ] && { - [ $havejobs = 0 ] && continue - shift - } - [ x"$1" = x'-h' ] && { - [ $havehist = 0 ] && continue - shift - } - echo 'int '"$func"'(int, char **);' >&4 - while - [ $# != 0 ] && [ x"$1" != x'#' ] - do - [ x"$1" = x'-s' ] && { - specials="$specials $2 $func" - shift 2 - continue; - } - [ x"$1" = x'-u' ] && shift - echo ' { "'$1'", '"$func"' },' >&3 - shift - done -done - -echo ' { 0, 0 },' >&3 -echo '};' >&3 -echo >&3 -echo 'const struct builtincmd splbltincmd[] = {' >&3 - -set -- $specials -while - [ $# != 0 ] -do - echo ' { "'$1'", '"$2"' },' >&3 - shift 2 -done - -echo ' { 0, 0 },' >&3 -echo "};" >&3 diff --git a/bin/sh/mkinit.sh b/bin/sh/mkinit.sh deleted file mode 100755 index da4f46f..0000000 --- a/bin/sh/mkinit.sh +++ /dev/null @@ -1,224 +0,0 @@ -#! /bin/sh -# $NetBSD: mkinit.sh,v 1.10 2018/12/05 09:20:18 kre Exp $ - -# Copyright (c) 2003 The NetBSD Foundation, Inc. -# All rights reserved. -# -# This code is derived from software contributed to The NetBSD Foundation -# by David Laight. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -srcs="$*" - -# use of echo in this script is broken - -# some echo versions will expand \n in the args, which breaks C -# Note: this script is a HOST_PROG ... it must run in the -# build host's environment, with its shell. - -# Fortunately, use of echo here is also trivially simplistic, -# we can easily replace all uses with ... - -echo() -{ - printf '%s\n' "$1" -} - -# CAUTION: for anyone modifying this script.... use printf -# rather than echo to output anything at all... then -# you will avoid being bitten by the simplicity of this function. -# This was done this way rather than wholesale replacement -# to avoid unnecessary code churn. - - -nl=' -' -openparen='(' - -# shells have bugs (including older NetBSD sh) in how \ is -# used in pattern matching. So work out what the shell -# running this script expects. We could also just use a -# literal \ in the pattern, which would need to be quoted -# of course, but then we'd run into a whole host of potential -# other shell bugs (both with the quoting in the pattern, and -# with the matching that follows if that works as inended). -# Far easier, and more reliable, is to just work out what works, -# and then use it, which more or less mandates using a variable... -backslash='\\' -var='abc\' # dummy test case. -if [ "$var" = "${var%$backslash}" ] -then - # buggy sh, try the broken way - backslash='\' - if [ "$var" = "${var%$backslash}" ] - then - printf >&2 "$0: %s\n" 'No pattern match with \ (broken shell)' - exit 1 - fi -fi -# We know we can detect the presence of a trailing \, which is all we need. -# Now to confirm we will not generate false matches. -var='abc' -if [ "$var" != "${var%$backslash}" ] -then - printf >&2 "$0: %s\n" 'Bogus pattern match with \ (broken shell)' - exit 1 -fi -unset var - -includes=' "shell.h" "mystring.h" "init.h" ' -defines= -decles= -event_init= -event_reset= -event_shellproc= - -for src in $srcs; do - exec <$src - decnl="$nl" - while IFS=; read -r line; do - [ "$line" = x ] - case "$line " in - INIT["{ "]* ) event=init;; - RESET["{ "]* ) event=reset;; - SHELLPROC["{ "]* ) event=shellproc;; - INCLUDE[\ \ ]* ) - IFS=' ' - set -- $line - # ignore duplicates - [ "${includes}" != "${includes% $2 *}" ] && continue - includes="$includes$2 " - continue - ;; - MKINIT\ ) - # struct declaration - decles="$decles$nl" - while - read -r line - decles="${decles}${line}${nl}" - [ "$line" != "};" ] - do - : - done - decnl="$nl" - continue - ;; - MKINIT["{ "]* ) - # strip initialiser - def=${line#MKINIT} - comment="${def#*;}" - def="${def%;$comment}" - def="${def%%=*}" - def="${def% }" - decles="${decles}${decnl}extern${def};${comment}${nl}" - decnl= - continue - ;; - \#define[\ \ ]* ) - IFS=' ' - set -- $line - # Ignore those with arguments - [ "$2" = "${2##*$openparen}" ] || continue - # and multiline definitions - [ "$line" = "${line%$backslash}" ] || continue - defines="${defines}#undef $2${nl}${line}${nl}" - continue - ;; - * ) continue;; - esac - # code for events - ev="${nl} /* from $src: */${nl} {${nl}" - # Indent the text by an extra <tab> - while - read -r line - [ "$line" != "}" ] - do - case "$line" in - ('') ;; - ('#'*) ;; - (*) line=" $line";; - esac - ev="${ev}${line}${nl}" - done - ev="${ev} }${nl}" - eval event_$event=\"\$event_$event\$ev\" - done -done - -exec >init.c.tmp - -echo "/*" -echo " * This file was generated by the mkinit program." -echo " */" -echo - -IFS=' ' -for f in $includes; do - echo "#include $f" -done - -echo -echo -echo -echo "$defines" -echo -echo "$decles" -echo -echo -echo "/*" -echo " * Initialization code." -echo " */" -echo -echo "void" -echo "init(void)" -echo "{" -echo "${event_init}" -echo "}" -echo -echo -echo -echo "/*" -echo " * This routine is called when an error or an interrupt occurs in an" -echo " * interactive shell and control is returned to the main command loop." -echo " */" -echo -echo "void" -echo "reset(void)" -echo "{" -echo "${event_reset}" -echo "}" -echo -echo -echo -echo "/*" -echo " * This routine is called to initialize the shell to run a shell procedure." -echo " */" -echo -echo "void" -echo "initshellproc(void)" -echo "{" -echo "${event_shellproc}" -echo "}" - -exec >&- -mv init.c.tmp init.c diff --git a/bin/sh/mknodenames.sh b/bin/sh/mknodenames.sh deleted file mode 100755 index 1d03200..0000000 --- a/bin/sh/mknodenames.sh +++ /dev/null @@ -1,69 +0,0 @@ -#! /bin/sh - -# $NetBSD: mknodenames.sh,v 1.6 2018/08/18 03:09:37 kre Exp $ - -# Use this script however you like, but it would be amazing if -# it has any purpose other than as part of building the shell... - -if [ -z "$1" ]; then - echo "Usage: $0 nodes.h" 1>&2 - exit 1 -fi - -NODES=$1 - -test -t 1 && test -z "$2" && exec > nodenames.h - -echo "\ -/* - * Automatically generated by $0 - * DO NOT EDIT. Do Not 'cvs add'. - */ -" -echo "#ifndef NODENAMES_H_INCLUDED" -echo "#define NODENAMES_H_INCLUDED" -echo -echo "#ifdef DEBUG" - -MAX=$(awk < "$NODES" ' - /#define/ { - if ($3 > MAX) MAX = $3 - } - END { print MAX } -') - -echo -echo '#ifdef DEFINE_NODENAMES' -echo "STATIC const char * const NodeNames[${MAX} + 1] = {" - -grep '^#define' "$NODES" | sort -k3n | while read define name number opt_comment -do - : ${next:=0} - while [ "$number" -gt "$next" ] - do - echo ' "???",' - next=$(( next + 1)) - done - echo ' "'"$name"'",' - next=$(( number + 1 )) -done - -echo "};" -echo '#else' -echo "extern const char * const NodeNames[${MAX} + 1];" -echo '#endif' -echo -echo '#define NODETYPENAME(type) \' -echo ' ((unsigned)(type) <= '"${MAX}"' ? NodeNames[(type)] : "??OOR??")' -echo -echo '#define NODETYPE(type) NODETYPENAME(type), (type)' -echo '#define PRIdsNT "s(%d)"' -echo -echo '#else /* DEBUG */' -echo -echo '#define NODETYPE(type) (type)' -echo '#define PRIdsNT "d"' -echo -echo '#endif /* DEBUG */' -echo -echo '#endif /* !NODENAMES_H_INCLUDED */' diff --git a/bin/sh/mknodes.sh b/bin/sh/mknodes.sh deleted file mode 100755 index 0b1ab80..0000000 --- a/bin/sh/mknodes.sh +++ /dev/null @@ -1,242 +0,0 @@ -#! /bin/sh -# $NetBSD: mknodes.sh,v 1.4 2019/01/19 13:08:50 kre Exp $ - -# Copyright (c) 2003 The NetBSD Foundation, Inc. -# All rights reserved. -# -# This code is derived from software contributed to The NetBSD Foundation -# by David Laight. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS -# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -nodetypes=$1 -nodes_pat=$2 -objdir="$3" - -exec <$nodetypes -exec >$objdir/nodes.h.tmp - -echo "/*" -echo " * This file was generated by mknodes.sh" -echo " */" -echo - -tagno=0 -while IFS=; read -r line; do - line="${line%%#*}" - IFS=' ' - set -- $line - IFS= - [ -z "$2" ] && continue - case "$line" in - [" "]* ) - IFS=' ' - [ $field = 0 ] && struct_list="$struct_list $struct" - eval field_${struct}_$field=\"\$*\" - eval numfld_$struct=\$field - field=$(($field + 1)) - ;; - * ) - define=$1 - struct=$2 - echo "#define $define $tagno" - tagno=$(($tagno + 1)) - eval define_$struct=\"\$define_$struct \$define\" - struct_define="$struct_define $struct" - field=0 - ;; - esac -done - -echo - -IFS=' ' -for struct in $struct_list; do - echo - echo - echo "struct $struct {" - field=0 - while - eval line=\"\$field_${struct}_$field\" - field=$(($field + 1)) - [ -n "$line" ] - do - IFS=' ' - set -- $line - name=$1 - case "$name" in - type) if [ -n "$typetype" ] && [ "$typetype" != "$2" ] - then - echo >&2 "Conflicting type fields: node" \ - "$struct has $2, others $typetype" - exit 1 - fi - if [ $field -ne 1 ] - then - echo >&2 "Node $struct has type as field" \ - "$field (should only be first)" - exit 1 - fi - typetype=$2 - ;; - *) - if [ $field -eq 1 ] - then - echo >&2 "Node $struct does not have" \ - "type as first field" - exit 1 - fi - ;; - esac - case $2 in - nodeptr ) type="union node *";; - nodelist ) type="struct nodelist *";; - string ) type="char *";; - int*_t | uint*_t | int ) type="$2 ";; - * ) name=; shift 2; type="$*";; - esac - echo " $type$name;" - done - echo "};" -done - -echo -echo -echo "union node {" -echo " $typetype type;" -for struct in $struct_list; do - echo " struct $struct $struct;" -done -echo "};" -echo -echo -echo "struct nodelist {" -echo " struct nodelist *next;" -echo " union node *n;" -echo "};" -echo -echo -echo 'struct funcdef;' -echo 'struct funcdef *copyfunc(union node *);' -echo 'union node *getfuncnode(struct funcdef *);' -echo 'void reffunc(struct funcdef *);' -echo 'void unreffunc(struct funcdef *);' -echo 'void freefunc(struct funcdef *);' - -mv $objdir/nodes.h.tmp $objdir/nodes.h || exit 1 - -exec <$nodes_pat -exec >$objdir/nodes.c.tmp - -echo "/*" -echo " * This file was generated by mknodes.sh" -echo " */" -echo - -while IFS=; read -r line; do - IFS=' ' - set -- $line - IFS= - case "$1" in - '%SIZES' ) - echo "static const short nodesize[$tagno] = {" - IFS=' ' - for struct in $struct_define; do - echo " SHELL_ALIGN(sizeof (struct $struct))," - done - echo "};" - ;; - '%CALCSIZE' ) - echo " if (n == NULL)" - echo " return;" - echo " res->bsize += nodesize[n->type];" - echo " switch (n->type) {" - IFS=' ' - for struct in $struct_list; do - eval defines=\"\$define_$struct\" - for define in $defines; do - echo " case $define:" - done - eval field=\$numfld_$struct - while - [ $field != 0 ] - do - eval line=\"\$field_${struct}_$field\" - field=$(($field - 1)) - IFS=' ' - set -- $line - name=$1 - cl=", res)" - case $2 in - nodeptr ) fn=calcsize;; - nodelist ) fn=sizenodelist;; - string ) fn="res->ssize += strlen" - cl=") + 1";; - * ) continue;; - esac - echo " ${fn}(n->$struct.$name${cl};" - done - echo " break;" - done - echo " };" - ;; - '%COPY' ) - echo " if (n == NULL)" - echo " return NULL;" - echo " new = st->block;" - echo " st->block = (char *) st->block + nodesize[n->type];" - echo " switch (n->type) {" - IFS=' ' - for struct in $struct_list; do - eval defines=\"\$define_$struct\" - for define in $defines; do - echo " case $define:" - done - eval field=\$numfld_$struct - while - [ $field != 0 ] - do - eval line=\"\$field_${struct}_$field\" - field=$(($field - 1)) - IFS=' ' - set -- $line - name=$1 - case $2 in - nodeptr ) fn="copynode(";; - nodelist ) fn="copynodelist(";; - string ) fn="nodesavestr(";; - int*_t| uint*_t | int ) fn=;; - * ) continue;; - esac - f="$struct.$name" - echo " new->$f = ${fn}n->$f${fn:+, st)};" - done - echo " break;" - done - echo " };" - echo " new->type = n->type;" - ;; - * ) echo "$line";; - esac -done - -mv $objdir/nodes.c.tmp $objdir/nodes.c || exit 1 diff --git a/bin/sh/mkoptions.sh b/bin/sh/mkoptions.sh deleted file mode 100644 index aecb6df..0000000 --- a/bin/sh/mkoptions.sh +++ /dev/null @@ -1,198 +0,0 @@ -#! /bin/sh - -# $NetBSD: mkoptions.sh,v 1.5 2017/11/15 09:21:19 kre Exp $ - -# -# It would be more sensible to generate 2 .h files, one which -# is for everyone to use, defines the "variables" and (perhaps) generates -# the externs (though they could just be explicit in options.h) -# and one just for options.c which generates the initialisation. -# -# But then I'd have to deal with making the Makefile handle that properly... -# (this is simpler there, and it just means a bit more sh compile time.) - -set -f -IFS=' ' # blank, tab (no newline) - -IF="$1" -OF="${3+$3/}$2" - -E_FILE=$(${MKTEMP:-mktemp} -t MKO.E.$$) -O_FILE=$(${MKTEMP:-mktemp} -t MKO.O.$$) -trap 'rm -f "${E_FILE}" "${O_FILE}"' EXIT - -exec 5> "${E_FILE}" -exec 6> "${O_FILE}" - -{ - printf '/*\n * File automatically generated by %s.\n' "$0" - printf ' * Do not edit, do not add to cvs.\n' - printf ' */\n\n' - - printf '#ifdef DEFINE_OPTIONS\n' - printf 'struct optent optlist[] = {\n' -} >"${OF}" - -FIRST=true - -${SED:-sed} <"${IF}" \ - -e '/^$/d' \ - -e '/^#/d' \ - -e '/^[ ]*\//d' \ - -e '/^[ ]*\*/d' \ - -e '/^[ ]*;/d' | -sort -b -k2,2f -k2,2 < "${IF}" | -while read line -do - # Look for comments in various styles, and ignore them - # (these should generally be already removed by sed above) - - case "${line}" in - '') continue;; - /*) continue;; - \**) continue;; - \;*) continue;; - \#*) continue;; - esac - - case "${line}" in - *'#if'*) - COND="${line#*#}" - COND="#${COND%%#*}" - ;; - *) - COND= - ;; - esac - set -- ${line%%[ ]#*} - - var="$1" name="$2" - - case "${var}" in - ('' | [!A-Za-z_]* | *[!A-Za-z0-9_]*) - printf >&2 "Bad var name: '%s'\\n" "${var}" - # exit 1 - continue # just ignore it for now - esac - - case "${name}" in - ?) set -- ${var} '' $name $3 $4; name= ;; - esac - - chr="$3" set="$4" dflt="$5" - - case "${chr}" in - -) chr= set= dflt="$4";; - ''|?) ;; - *) printf >&2 'flag "%s": Not a character\n' "${chr}"; continue;; - esac - - # options must have some kind of name, or they are useless... - test -z "${name}${chr}" && continue - - case "${set}" in - -) set= ;; - [01] | [Oo][Nn] | [Oo][Ff][Ff]) dflt="${set}"; set= ;; - ''|?) ;; - *) printf >&2 'set "%s": Not a character\n' "${set}"; continue;; - esac - - case "${dflt}" in - '') ;; - [Oo][Nn]) dflt=1;; - [Oo][Ff][Ff]) dflt=0;; - [01]) ;; - *) printf >&2 'default "%s" invalid, use 0 off 1 on\n'; continue;; - esac - - # validation complete, now to generate output - - if [ -n "${COND}" ] - then - printf '%s\n' "${COND}" >&4 - printf '%s\n' "${COND}" >&5 - printf '%s\n' "${COND}" >&6 - fi - - printf '\t_SH_OPT_%s,\n' "${var}" >&5 - - if [ -n "${name}" ] - then - printf ' { "%s", ' "${name}" >&4 - else - printf ' { 0, ' >&4 - fi - - if [ -n "${chr}" ] - then - printf "'%s', " "${chr}" >&4 - else - chr= - printf '0, ' >&4 - fi - - if [ -n "${set}" ] - then - printf "'%s', 0, " "${set}" >&4 - else - printf '0, 0, ' >&4 - fi - - if [ -n "${dflt}" ] - then - printf '%s },\n' "${dflt}" >&4 - else - printf '0 },\n' >&4 - fi - - printf '#define %s\toptlist[_SH_OPT_%s].val\n' "${var}" "${var}" >&6 - - if [ -n "${COND}" ] - then - printf '#endif\n' >&4 - printf '#endif\n' >&5 - printf '#endif\n' >&6 - fi - - test -z "${chr}" && continue - - printf '%s _SH_OPT_%s %s\n' "${chr}" "${var}" "${COND}" - -done 4>>"${OF}" | sort -t' ' -k1,1f -k1,1 | while read chr index COND -do - if $FIRST - then - printf ' { 0, 0, 0, 0, 0 }\n};\n' - printf '#endif\n\n' - - printf 'enum shell_opt_names {\n' - cat "${E_FILE}" - printf '};\n\n' - - printf '#ifdef DEFINE_OPTIONS\n' - printf 'const unsigned char optorder[] = {\n' - FIRST=false - fi - [ -n "${COND}" ] && printf '%s\n' "${COND}" - printf '\t%s,\n' "${index}" - [ -n "${COND}" ] && printf '#endif\n' - -done >>"${OF}" - -{ - printf '};\n\n' - printf '#define NOPTS (sizeof optlist / sizeof optlist[0] - 1)\n' - printf 'int sizeof_optlist = sizeof optlist;\n\n' - printf \ - 'const int option_flags = (sizeof optorder / sizeof optorder[0]);\n' - printf '\n#else\n\n' - printf 'extern struct optent optlist[];\n' - printf 'extern int sizeof_optlist;\n' - printf 'extern const unsigned char optorder[];\n' - printf 'extern const int option_flags;\n' - printf '\n#endif\n\n' - - cat "${O_FILE}" -} >> "${OF}" - -exit 0 diff --git a/bin/sh/mktokens b/bin/sh/mktokens deleted file mode 100644 index a8f81c4..0000000 --- a/bin/sh/mktokens +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/sh - -# $NetBSD: mktokens,v 1.14 2017/07/26 03:46:54 kre Exp $ -# -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)mktokens 8.1 (Berkeley) 5/31/93 - -: ${AWK:=awk} -: ${SED:=sed} - -# The following is a list of tokens. The second column is nonzero if the -# token marks the end of a list. The third column is the name to print in -# error messages. - -# Note that all the keyword tokens come after TWORD, and all the others -# come before it. We rely upon that relationship - keep it! - -cat > /tmp/ka$$ <<\! -TEOF 1 end of file -TNL 0 newline -TSEMI 0 ";" -TBACKGND 0 "&" -TAND 0 "&&" -TOR 0 "||" -TPIPE 0 "|" -TLP 0 "(" -TRP 1 ")" -TENDCASE 1 ";;" -TCASEFALL 1 ";&" -TENDBQUOTE 1 "`" -TREDIR 0 redirection -TWORD 0 word -TIF 0 "if" -TTHEN 1 "then" -TELSE 1 "else" -TELIF 1 "elif" -TFI 1 "fi" -TWHILE 0 "while" -TUNTIL 0 "until" -TFOR 0 "for" -TDO 1 "do" -TDONE 1 "done" -TBEGIN 0 "{" -TEND 1 "}" -TCASE 0 "case" -TESAC 1 "esac" -TNOT 0 "!" -! -nl=`wc -l /tmp/ka$$` -exec > token.h -${AWK} '{print "#define " $1 " " NR-1}' /tmp/ka$$ -echo ' -/* Array indicating which tokens mark the end of a list */ -const char tokendlist[] = {' -${AWK} '{print "\t" $2 ","}' /tmp/ka$$ -echo '}; - -const char *const tokname[] = {' -${SED} -e 's/"/\\"/g' \ - -e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \ - /tmp/ka$$ -echo '}; -' -${SED} 's/"//g' /tmp/ka$$ | ${AWK} ' -/TWORD/{print "#define KWDOFFSET " NR; print ""; - print "const char *const parsekwd[] = {"} -/TIF/,/neverfound/{print " \"" $3 "\","}' -echo ' 0 -};' - -rm /tmp/ka$$ diff --git a/bin/sh/myhistedit.h b/bin/sh/myhistedit.h deleted file mode 100644 index 855c1bc..0000000 --- a/bin/sh/myhistedit.h +++ /dev/null @@ -1,48 +0,0 @@ -/* $NetBSD: myhistedit.h,v 1.13 2017/06/28 13:46:06 kre Exp $ */ - -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)myhistedit.h 8.2 (Berkeley) 5/4/95 - */ - -#include <histedit.h> - -extern History *hist; -extern EditLine *el; -extern int displayhist; - -void histedit(void); -void sethistsize(const char *); -void setterm(const char *); -int inputrc(int, char **); -void set_editrc(const char *); -void set_prompt_lit(const char *); -int not_fcnumber(char *); -int str_to_event(const char *, int); - diff --git a/bin/sh/mystring.c b/bin/sh/mystring.c deleted file mode 100644 index 9b6b40b..0000000 --- a/bin/sh/mystring.c +++ /dev/null @@ -1,140 +0,0 @@ -/* $NetBSD: mystring.c,v 1.18 2018/07/13 22:43:44 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)mystring.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: mystring.c,v 1.18 2018/07/13 22:43:44 kre Exp $"); -#endif -#endif /* not lint */ - -/* - * String functions. - * - * equal(s1, s2) Return true if strings are equal. - * scopy(from, to) Copy a string. - * scopyn(from, to, n) Like scopy, but checks for overflow. - * number(s) Convert a string of digits to an integer. - * is_number(s) Return true if s is a string of digits. - */ - -#include <inttypes.h> -#include <limits.h> -#include <stdlib.h> -#include "shell.h" -#include "syntax.h" -#include "error.h" -#include "mystring.h" - - -const char nullstr[1]; /* zero length string */ - -/* - * equal - #defined in mystring.h - */ - -/* - * scopy - #defined in mystring.h - */ - - -/* - * scopyn - copy a string from "from" to "to", truncating the string - * if necessary. "To" is always nul terminated, even if - * truncation is performed. "Size" is the size of "to". - */ - -void -scopyn(const char *from, char *to, int size) -{ - - while (--size > 0) { - if ((*to++ = *from++) == '\0') - return; - } - *to = '\0'; -} - - -/* - * prefix -- see if pfx is a prefix of string. - */ - -int -prefix(const char *pfx, const char *string) -{ - while (*pfx) { - if (*pfx++ != *string++) - return 0; - } - return 1; -} - - -/* - * Convert a string of digits to an integer, printing an error message on - * failure. - */ - -int -number(const char *s) -{ - char *ep = NULL; - intmax_t n; - - if (!is_digit(*s) || ((n = strtoimax(s, &ep, 10)), - (ep == NULL || ep == s || *ep != '\0'))) - error("Illegal number: '%s'", s); - if (n < INT_MIN || n > INT_MAX) - error("Number out of range: %s", s); - return (int)n; -} - - - -/* - * Check for a valid number. This should be elsewhere. - */ - -int -is_number(const char *p) -{ - do { - if (! is_digit(*p)) - return 0; - } while (*++p != '\0'); - return 1; -} diff --git a/bin/sh/mystring.h b/bin/sh/mystring.h deleted file mode 100644 index 08a73e9..0000000 --- a/bin/sh/mystring.h +++ /dev/null @@ -1,45 +0,0 @@ -/* $NetBSD: mystring.h,v 1.11 2003/08/07 09:05:35 agc Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)mystring.h 8.2 (Berkeley) 5/4/95 - */ - -#include <string.h> - -void scopyn(const char *, char *, int); -int prefix(const char *, const char *); -int number(const char *); -int is_number(const char *); - -#define equal(s1, s2) (strcmp(s1, s2) == 0) -#define scopy(s1, s2) ((void)strcpy(s2, s1)) diff --git a/bin/sh/nodes.c.pat b/bin/sh/nodes.c.pat deleted file mode 100644 index 599597c..0000000 --- a/bin/sh/nodes.c.pat +++ /dev/null @@ -1,210 +0,0 @@ -/* $NetBSD: nodes.c.pat,v 1.14 2018/06/22 11:04:55 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 - */ - -#include <stdlib.h> -#include <stddef.h> - -/* - * Routine for dealing with parsed shell commands. - */ - -#include "shell.h" -#include "nodes.h" -#include "memalloc.h" -#include "machdep.h" -#include "mystring.h" - - -/* used to accumulate sizes of nodes */ -struct nodesize { - int bsize; /* size of structures in function */ - int ssize; /* size of strings in node */ -}; - -/* provides resources for node copies */ -struct nodecopystate { - pointer block; /* block to allocate function from */ - char *string; /* block to allocate strings from */ -}; - - -%SIZES - - - -STATIC void calcsize(union node *, struct nodesize *); -STATIC void sizenodelist(struct nodelist *, struct nodesize *); -STATIC union node *copynode(union node *, struct nodecopystate *); -STATIC struct nodelist *copynodelist(struct nodelist *, struct nodecopystate *); -STATIC char *nodesavestr(char *, struct nodecopystate *); - -struct funcdef { - unsigned int refcount; - union node n; /* must be last */ -}; - - -/* - * Make a copy of a parse tree. - */ - -struct funcdef * -copyfunc(union node *n) -{ - struct nodesize sz; - struct nodecopystate st; - struct funcdef *fn; - - if (n == NULL) - return NULL; - sz.bsize = offsetof(struct funcdef, n); - sz.ssize = 0; - calcsize(n, &sz); - fn = ckmalloc(sz.bsize + sz.ssize); - fn->refcount = 1; - st.block = (char *)fn + offsetof(struct funcdef, n); - st.string = (char *)fn + sz.bsize; - copynode(n, &st); - return fn; -} - -union node * -getfuncnode(struct funcdef *fn) -{ - if (fn == NULL) - return NULL; - return &fn->n; -} - - -STATIC void -calcsize(union node *n, struct nodesize *res) -{ - %CALCSIZE -} - - - -STATIC void -sizenodelist(struct nodelist *lp, struct nodesize *res) -{ - while (lp) { - res->bsize += SHELL_ALIGN(sizeof(struct nodelist)); - calcsize(lp->n, res); - lp = lp->next; - } -} - - - -STATIC union node * -copynode(union node *n, struct nodecopystate *st) -{ - union node *new; - - %COPY - return new; -} - - -STATIC struct nodelist * -copynodelist(struct nodelist *lp, struct nodecopystate *st) -{ - struct nodelist *start; - struct nodelist **lpp; - - lpp = &start; - while (lp) { - *lpp = st->block; - st->block = (char *)st->block + - SHELL_ALIGN(sizeof(struct nodelist)); - (*lpp)->n = copynode(lp->n, st); - lp = lp->next; - lpp = &(*lpp)->next; - } - *lpp = NULL; - return start; -} - - - -STATIC char * -nodesavestr(char *s, struct nodecopystate *st) -{ - register char *p = s; - register char *q = st->string; - char *rtn = st->string; - - while ((*q++ = *p++) != 0) - continue; - st->string = q; - return rtn; -} - - - -/* - * Handle making a reference to a function, and releasing it. - * Free the func code when there are no remaining references. - */ - -void -reffunc(struct funcdef *fn) -{ - if (fn != NULL) - fn->refcount++; -} - -void -unreffunc(struct funcdef *fn) -{ - if (fn != NULL) { - if (--fn->refcount > 0) - return; - ckfree(fn); - } -} - -/* - * this is used when we need to free the func, regardless of refcount - * which only happens when re-initing the shell for a SHELLPROC - */ -void -freefunc(struct funcdef *fn) -{ - if (fn != NULL) - ckfree(fn); -} diff --git a/bin/sh/nodetypes b/bin/sh/nodetypes deleted file mode 100644 index dc5ee4a..0000000 --- a/bin/sh/nodetypes +++ /dev/null @@ -1,149 +0,0 @@ -# $NetBSD: nodetypes,v 1.18 2017/06/08 13:12:17 kre Exp $ -# Copyright (c) 1991, 1993 -# The Regents of the University of California. All rights reserved. -# -# This code is derived from software contributed to Berkeley by -# Kenneth Almquist. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# @(#)nodetypes 8.2 (Berkeley) 5/4/95 - -# This file describes the nodes used in parse trees. Unindented lines -# contain a node type followed by a structure tag. Subsequent indented -# lines specify the fields of the structure. Several node types can share -# the same structure, in which case the fields of the structure should be -# specified only once. -# -# A field of a structure is described by the name of the field followed -# by a type. The currently implemented types are: -# nodeptr - a pointer to a node -# nodelist - a pointer to a list of nodes -# string - a pointer to a nul terminated string -# int - an integer -# other - any type that can be copied by assignment -# temp - a field that doesn't have to be copied when the node is copied -# The last two types should be followed by the text of a C declaration for -# the field. - -NSEMI nbinary # two commands separated by a semicolon - type int - ch1 nodeptr # the first child - ch2 nodeptr # the second child - -NCMD ncmd # a simple command - type int - backgnd int # set to run command in background - args nodeptr # the arguments - redirect nodeptr # list of file redirections - lineno int - -NPIPE npipe # a pipeline - type int - backgnd int # set to run pipeline in background - cmdlist nodelist # the commands in the pipeline - -NREDIR nredir # redirection (of a complex command) - type int - n nodeptr # the command - redirect nodeptr # list of file redirections - -NBACKGND nredir # run command in background -NSUBSHELL nredir # run command in a subshell - -NAND nbinary # the && operator -NOR nbinary # the || operator - -NIF nif # the if statement. Elif clauses are handled - type int # using multiple if nodes. - test nodeptr # if test - ifpart nodeptr # then ifpart - elsepart nodeptr # else elsepart - -NWHILE nbinary # the while statement. First child is the test -NUNTIL nbinary # the until statement - -NFOR nfor # the for statement - type int - args nodeptr # for var in args - body nodeptr # do body; done - var string # the for variable - -NCASE ncase # a case statement - type int - expr nodeptr # the word to switch on - cases nodeptr # the list of cases (NCLIST nodes) - lineno int - -NCLISTCONT nclist # a case terminated by ';&' (fall through) -NCLIST nclist # a case - type int - next nodeptr # the next case in list - pattern nodeptr # list of patterns for this case - body nodeptr # code to execute for this case - lineno int - - -NDEFUN narg # define a function. The "next" field contains - # the body of the function. - -NARG narg # represents a word - type int - next nodeptr # next word in list - text string # the text of the word - backquote nodelist # list of commands in back quotes - lineno int - -NTO nfile # fd> fname -NCLOBBER nfile # fd>| fname -NFROM nfile # fd< fname -NFROMTO nfile # fd<> fname -NAPPEND nfile # fd>> fname - type int - next nodeptr # next redirection in list - fd int # file descriptor being redirected - fname nodeptr # file name, in a NARG node - expfname temp char *expfname # actual file name - -NTOFD ndup # fd<&dupfd -NFROMFD ndup # fd>&dupfd - type int - next nodeptr # next redirection in list - fd int # file descriptor being redirected - dupfd int # file descriptor to duplicate - vname nodeptr # file name if fd>&$var - - -NHERE nhere # fd<<\! -NXHERE nhere # fd<<! - type int - next nodeptr # next redirection in list - fd int # file descriptor being redirected - doc nodeptr # input to command (NARG node) - -NNOT nnot # ! command (actually pipeline) -NDNOT nnot # ! ! pipeline (optimisation) - type int - com nodeptr diff --git a/bin/sh/option.list b/bin/sh/option.list deleted file mode 100644 index 3be683a..0000000 --- a/bin/sh/option.list +++ /dev/null @@ -1,79 +0,0 @@ -/* $NetBSD: option.list,v 1.9 2018/11/23 20:40:06 kre Exp $ */ - -/* - * define the shell's settable options - * - * new options can be defined by adding them here, - * but they do nothing until code to implement them - * is added (using the "var name" field) - */ - -/* - * format is up to 5 columns... (followed by anything) - * end of line comments can be introduced by ' #' (space/tab hash) to eol. - * - * The columns are: - * 1. internal shell "var name" (required) - * 2. option long name - * if a single char, then no long name, and remaining - * columns shift left (this becomes the short name) - * 3. option short name (single character name) - * if '-' or absent then no short name - * if neither long nor short name, line is ignored - * 4. option set short name (name of option equiv class) - * if '-' or absent then no class - * 5. default value of option - * if absent, default is 0 - * only 0 or 1 possible (0==off 1==on) ("on" and "off" can be used) - * - * Data may be followed by any C preprocessor #if expression (incl the #if..) - * (including #ifdef #ifndef) to conditionalise output for that option. - * The #if expression continues until \n or next following '#' - */ - -// the POSIX defined options -aflag allexport a # export all variables -eflag errexit e # exit on command error ($? != 0) -mflag monitor m # enable job control -Cflag noclobber C # do not overwrite files when using > -nflag noexec n # do not execue commands -fflag noglob f # no pathname expansion -uflag nounset u # expanding unset var is an error -vflag verbose v # echo commands as read -xflag xtrace x # trace command execution - -// the long name (ignoreeof) is standard, the I flag is not -Iflag ignoreeof I # do not exit interactive shell on EOF - -// defined but not really implemented by the shell (yet) - they do nothing -bflag notify b # [U] report bg job completion -nolog nolog # [U] no func definitions in history -// 'h' is standard, long name (trackall) is not -hflag trackall h # [U] locate cmds in funcs during defn - -// 's' is standard for command line, not as 'set' option, nor 'stdin' name -sflag stdin s # read from standard input -// minusc c # command line option only. -// -- o # handled differently... - -// non-standard options -- 'i' is just a state, not an option in standard. -iflag interactive i # interactive shell -cdprint cdprint # always print result of a cd -usefork fork F # use fork(2) instead of vfork(2) -pflag nopriv p # preserve privs if set[ug]id -posix posix # be closer to POSIX compat -qflag quietprofile q # disable -v/-x in startup files -fnline1 local_lineno L on # number lines in funcs starting at 1 -promptcmds promptcmds # allow $( ) in PS1 (et al). -pipefail pipefail # pipe exit status -Xflag xlock X #ifndef SMALL # sticky stderr for -x (implies -x) - -// editline/history related options ("vi" is standard, 'V' and others are not) -// only one of vi/emacs can be set, hence the "set" definition, value -// of that can be any char (not used for a different set) -Vflag vi V V # enable vi style editing -Eflag emacs E V # enable emacs style editing -tabcomplete tabcomplete # make <tab> cause filename expansion - -// internal debug option (not usually included in the shell) -debug debug #ifdef DEBUG # enable internal shell debugging diff --git a/bin/sh/options.c b/bin/sh/options.c deleted file mode 100644 index d2c3e68..0000000 --- a/bin/sh/options.c +++ /dev/null @@ -1,631 +0,0 @@ -/* $NetBSD: options.c,v 1.53 2018/07/13 22:43:44 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: options.c,v 1.53 2018/07/13 22:43:44 kre Exp $"); -#endif -#endif /* not lint */ - -#include <signal.h> -#include <unistd.h> -#include <stdlib.h> - -#include "shell.h" -#define DEFINE_OPTIONS -#include "options.h" -#undef DEFINE_OPTIONS -#include "builtins.h" -#include "nodes.h" /* for other header files */ -#include "eval.h" -#include "jobs.h" -#include "input.h" -#include "output.h" -#include "trap.h" -#include "var.h" -#include "memalloc.h" -#include "error.h" -#include "mystring.h" -#include "syntax.h" -#ifndef SMALL -#include "myhistedit.h" -#endif -#include "show.h" - -char *arg0; /* value of $0 */ -struct shparam shellparam; /* current positional parameters */ -char **argptr; /* argument list for builtin commands */ -char *optionarg; /* set by nextopt (like getopt) */ -char *optptr; /* used by nextopt */ - -char *minusc; /* argument to -c option */ - - -STATIC void options(int); -STATIC void minus_o(char *, int); -STATIC void setoption(int, int); -STATIC int getopts(char *, char *, char **, char ***, char **); - - -/* - * Process the shell command line arguments. - */ - -void -procargs(int argc, char **argv) -{ - size_t i; - int psx; - - argptr = argv; - if (argc > 0) - argptr++; - - psx = posix; /* save what we set it to earlier */ - /* - * option values are mostly boolean 0:off 1:on - * we use 2 (just in this routine) to mean "unknown yet" - */ - for (i = 0; i < NOPTS; i++) - optlist[i].val = 2; - posix = psx; /* restore before processing -o ... */ - - options(1); - - if (*argptr == NULL && minusc == NULL) - sflag = 1; - if (iflag == 2 && sflag == 1 && isatty(0) && isatty(2)) - iflag = 1; - if (iflag == 1 && sflag == 2) - iflag = 2; - if (mflag == 2) - mflag = iflag; -#ifndef DO_SHAREDVFORK - if (usefork == 2) - usefork = 1; -#endif -#if DEBUG >= 2 - if (debug == 2) - debug = 1; -#endif - /* - * Any options not dealt with as special cases just above, - * and which were not set on the command line, are set to - * their expected default values (mostly "off") - * - * then as each option is initialised, save its setting now - * as its "default" value for future use ("set -o default"). - */ - for (i = 0; i < NOPTS; i++) { - if (optlist[i].val == 2) - optlist[i].val = optlist[i].dflt; - optlist[i].dflt = optlist[i].val; - } - - arg0 = argv[0]; - if (sflag == 0 && minusc == NULL) { - commandname = argv[0]; - arg0 = *argptr++; - setinputfile(arg0, 0); - commandname = arg0; - } - /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ - if (minusc != NULL) { - if (argptr == NULL || *argptr == NULL) - error("Bad -c option"); - minusc = *argptr++; - if (*argptr != 0) - arg0 = *argptr++; - } - - shellparam.p = argptr; - shellparam.reset = 1; - /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ - while (*argptr) { - shellparam.nparam++; - argptr++; - } - optschanged(); -} - - -void -optschanged(void) -{ - setinteractive(iflag); -#ifndef SMALL - histedit(); -#endif - setjobctl(mflag); -} - -/* - * Process shell options. The global variable argptr contains a pointer - * to the argument list; we advance it past the options. - */ - -STATIC void -options(int cmdline) -{ - static char empty[] = ""; - char *p; - int val; - int c; - - if (cmdline) - minusc = NULL; - while ((p = *argptr) != NULL) { - argptr++; - if ((c = *p++) == '-') { - val = 1; - if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { - if (!cmdline) { - /* "-" means turn off -x and -v */ - if (p[0] == '\0') - xflag = vflag = 0; - /* "--" means reset params */ - else if (*argptr == NULL) - setparam(argptr); - } - break; /* "-" or "--" terminates options */ - } - } else if (c == '+') { - val = 0; - } else { - argptr--; - break; - } - while ((c = *p++) != '\0') { - if (val == 1 && c == 'c' && cmdline) { - /* command is after shell args*/ - minusc = empty; - } else if (c == 'o') { - if (*p != '\0') - minus_o(p, val + (cmdline ? val : 0)); - else if (*argptr) - minus_o(*argptr++, - val + (cmdline ? val : 0)); - else if (!cmdline) - minus_o(NULL, val); - else - error("arg for %co missing", "+-"[val]); - break; -#ifdef DEBUG - } else if (c == 'D') { - if (*p) { - set_debug(p, val); - break; - } else if (*argptr) - set_debug(*argptr++, val); - else - set_debug("*$", val); -#endif - } else { - setoption(c, val); - } - } - } -} - -static void -set_opt_val(size_t i, int val) -{ - size_t j; - int flag; - - if (val && (flag = optlist[i].opt_set)) { - /* some options (eg vi/emacs) are mutually exclusive */ - for (j = 0; j < NOPTS; j++) - if (optlist[j].opt_set == flag) - optlist[j].val = 0; - } -#ifndef SMALL - if (i == _SH_OPT_Xflag) - xtracefdsetup(val); -#endif - optlist[i].val = val; -#ifdef DEBUG - if (&optlist[i].val == &debug) - opentrace(); /* different "trace" than the -x one... */ -#endif -} - -STATIC void -minus_o(char *name, int val) -{ - size_t i; - const char *sep = ": "; - - if (name == NULL) { - if (val) { - out1str("Current option settings"); - for (i = 0; i < NOPTS; i++) { - if (optlist[i].name == NULL) { - out1fmt("%s%c%c", sep, - "+-"[optlist[i].val], - optlist[i].letter); - sep = ", "; - } - } - out1c('\n'); - for (i = 0; i < NOPTS; i++) { - if (optlist[i].name) - out1fmt("%-19s %s\n", optlist[i].name, - optlist[i].val ? "on" : "off"); - } - } else { - out1str("set -o default"); - for (i = 0; i < NOPTS; i++) { - if (optlist[i].val == optlist[i].dflt) - continue; - if (optlist[i].name) - out1fmt(" %co %s", - "+-"[optlist[i].val], optlist[i].name); - else - out1fmt(" %c%c", "+-"[optlist[i].val], - optlist[i].letter); - } - out1c('\n'); - } - } else { - if (val == 1 && equal(name, "default")) { /* special case */ - for (i = 0; i < NOPTS; i++) - set_opt_val(i, optlist[i].dflt); - return; - } - if (val) - val = 1; - for (i = 0; i < NOPTS; i++) - if (optlist[i].name && equal(name, optlist[i].name)) { - set_opt_val(i, val); -#ifndef SMALL - if (i == _SH_OPT_Xflag) - set_opt_val(_SH_OPT_xflag, val); -#endif - return; - } - error("Illegal option %co %s", "+-"[val], name); - } -} - - -STATIC void -setoption(int flag, int val) -{ - size_t i; - - for (i = 0; i < NOPTS; i++) - if (optlist[i].letter == flag) { - set_opt_val(i, val); -#ifndef SMALL - if (i == _SH_OPT_Xflag) - set_opt_val(_SH_OPT_xflag, val); -#endif - return; - } - error("Illegal option %c%c", "+-"[val], flag); - /* NOTREACHED */ -} - - - -#ifdef mkinit -INCLUDE "options.h" - -SHELLPROC { - int i; - - for (i = 0; optlist[i].name; i++) - optlist[i].val = 0; - optschanged(); - -} -#endif - - -/* - * Set the shell parameters. - */ - -void -setparam(char **argv) -{ - char **newparam; - char **ap; - int nparam; - - for (nparam = 0 ; argv[nparam] ; nparam++) - continue; - ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); - while (*argv) { - *ap++ = savestr(*argv++); - } - *ap = NULL; - freeparam(&shellparam); - shellparam.malloc = 1; - shellparam.nparam = nparam; - shellparam.p = newparam; - shellparam.optnext = NULL; -} - - -/* - * Free the list of positional parameters. - */ - -void -freeparam(volatile struct shparam *param) -{ - char **ap; - - if (param->malloc) { - for (ap = param->p ; *ap ; ap++) - ckfree(*ap); - ckfree(param->p); - } -} - - - -/* - * The shift builtin command. - */ - -int -shiftcmd(int argc, char **argv) -{ - int n; - char **ap1, **ap2; - - if (argc > 2) - error("Usage: shift [n]"); - n = 1; - if (argc > 1) - n = number(argv[1]); - if (n > shellparam.nparam) - error("can't shift that many"); - INTOFF; - shellparam.nparam -= n; - for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { - if (shellparam.malloc) - ckfree(*ap1); - } - ap2 = shellparam.p; - while ((*ap2++ = *ap1++) != NULL) - continue; - shellparam.optnext = NULL; - INTON; - return 0; -} - - - -/* - * The set command builtin. - */ - -int -setcmd(int argc, char **argv) -{ - if (argc == 1) - return showvars(0, 0, 1, 0); - INTOFF; - options(0); - optschanged(); - if (*argptr != NULL) { - setparam(argptr); - } - INTON; - return 0; -} - - -void -getoptsreset(const char *value) -{ - /* - * This is just to detect the case where OPTIND=1 - * is executed. Any other string assigned to OPTIND - * is OK, but is not a reset. No errors, so cannot use number() - */ - if (is_digit(*value) && strtol(value, NULL, 10) == 1) { - shellparam.optnext = NULL; - shellparam.reset = 1; - } -} - -/* - * The getopts builtin. Shellparam.optnext points to the next argument - * to be processed. Shellparam.optptr points to the next character to - * be processed in the current argument. If shellparam.optnext is NULL, - * then it's the first time getopts has been called. - */ - -int -getoptscmd(int argc, char **argv) -{ - char **optbase; - - if (argc < 3) - error("usage: getopts optstring var [arg]"); - else if (argc == 3) - optbase = shellparam.p; - else - optbase = &argv[3]; - - if (shellparam.reset == 1) { - shellparam.optnext = optbase; - shellparam.optptr = NULL; - shellparam.reset = 0; - } - - return getopts(argv[1], argv[2], optbase, &shellparam.optnext, - &shellparam.optptr); -} - -STATIC int -getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr) -{ - char *p, *q; - char c = '?'; - int done = 0; - int ind = 0; - int err = 0; - char s[12]; - - if ((p = *optpptr) == NULL || *p == '\0') { - /* Current word is done, advance */ - if (*optnext == NULL) - return 1; - p = **optnext; - if (p == NULL || *p != '-' || *++p == '\0') { -atend: - ind = *optnext - optfirst + 1; - *optnext = NULL; - p = NULL; - done = 1; - goto out; - } - (*optnext)++; - if (p[0] == '-' && p[1] == '\0') /* check for "--" */ - goto atend; - } - - c = *p++; - for (q = optstr; *q != c; ) { - if (*q == '\0') { - if (optstr[0] == ':') { - s[0] = c; - s[1] = '\0'; - err |= setvarsafe("OPTARG", s, 0); - } else { - outfmt(&errout, "Illegal option -%c\n", c); - (void) unsetvar("OPTARG", 0); - } - c = '?'; - goto bad; - } - if (*++q == ':') - q++; - } - - if (*++q == ':') { - if (*p == '\0' && (p = **optnext) == NULL) { - if (optstr[0] == ':') { - s[0] = c; - s[1] = '\0'; - err |= setvarsafe("OPTARG", s, 0); - c = ':'; - } else { - outfmt(&errout, "No arg for -%c option\n", c); - (void) unsetvar("OPTARG", 0); - c = '?'; - } - goto bad; - } - - if (p == **optnext) - (*optnext)++; - err |= setvarsafe("OPTARG", p, 0); - p = NULL; - } else - err |= setvarsafe("OPTARG", "", 0); - ind = *optnext - optfirst + 1; - goto out; - -bad: - ind = 1; - *optnext = NULL; - p = NULL; -out: - *optpptr = p; - fmtstr(s, sizeof(s), "%d", ind); - err |= setvarsafe("OPTIND", s, VNOFUNC); - s[0] = c; - s[1] = '\0'; - err |= setvarsafe(optvar, s, 0); - if (err) { - *optnext = NULL; - *optpptr = NULL; - flushall(); - exraise(EXERROR); - } - return done; -} - -/* - * XXX - should get rid of. have all builtins use getopt(3). the - * library getopt must have the BSD extension static variable "optreset" - * otherwise it can't be used within the shell safely. - * - * Standard option processing (a la getopt) for builtin routines. The - * only argument that is passed to nextopt is the option string; the - * other arguments are unnecessary. It return the character, or '\0' on - * end of input. - */ - -int -nextopt(const char *optstring) -{ - char *p; - const char *q; - char c; - - if ((p = optptr) == NULL || *p == '\0') { - p = *argptr; - if (p == NULL || *p != '-' || *++p == '\0') - return '\0'; - argptr++; - if (p[0] == '-' && p[1] == '\0') /* check for "--" */ - return '\0'; - } - c = *p++; - for (q = optstring ; *q != c ; ) { - if (*q == '\0') - error("Illegal option -%c", c); - if (*++q == ':') - q++; - } - if (*++q == ':') { - if (*p == '\0' && (p = *argptr++) == NULL) - error("No arg for -%c option", c); - optionarg = p; - p = NULL; - } - optptr = p; - return c; -} diff --git a/bin/sh/options.h b/bin/sh/options.h deleted file mode 100644 index 4857d18..0000000 --- a/bin/sh/options.h +++ /dev/null @@ -1,72 +0,0 @@ -/* $NetBSD: options.h,v 1.27 2017/05/28 00:38:01 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)options.h 8.2 (Berkeley) 5/4/95 - */ - -struct shparam { - int nparam; /* # of positional parameters (without $0) */ - unsigned char malloc; /* if parameter list dynamically allocated */ - unsigned char reset; /* if getopts has been reset */ - char **p; /* parameter list */ - char **optnext; /* next parameter to be processed by getopts */ - char *optptr; /* used by getopts */ -}; - -/* - * Note that option default values can be changed at shell startup - * depending upon the environment in which the shell is running. - */ -struct optent { - const char *name; /* for set -o <name> */ - const char letter; /* set [+/-]<letter> and $- */ - const char opt_set; /* mutually exclusive option set */ - unsigned char val; /* value of <letter>flag */ - unsigned char dflt; /* default value of flag */ -}; - -#include "optinit.h" - -extern char *minusc; /* argument to -c option */ -extern char *arg0; /* $0 */ -extern struct shparam shellparam; /* $@ */ -extern char **argptr; /* argument list for builtin commands */ -extern char *optionarg; /* set by nextopt */ -extern char *optptr; /* used by nextopt */ - -void procargs(int, char **); -void optschanged(void); -void setparam(char **); -void freeparam(volatile struct shparam *); -int nextopt(const char *); -void getoptsreset(const char *); diff --git a/bin/sh/output.c b/bin/sh/output.c deleted file mode 100644 index 99bd913..0000000 --- a/bin/sh/output.c +++ /dev/null @@ -1,755 +0,0 @@ -/* $NetBSD: output.c,v 1.40 2017/11/21 03:42:39 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: output.c,v 1.40 2017/11/21 03:42:39 kre Exp $"); -#endif -#endif /* not lint */ - -/* - * Shell output routines. We use our own output routines because: - * When a builtin command is interrupted we have to discard - * any pending output. - * When a builtin command appears in back quotes, we want to - * save the output of the command in a region obtained - * via malloc, rather than doing a fork and reading the - * output of the command via a pipe. - * Our output routines may be smaller than the stdio routines. - */ - -#include <sys/types.h> /* quad_t */ -#include <sys/param.h> /* BSD4_4 */ -#include <sys/ioctl.h> - -#include <stdio.h> /* defines BUFSIZ */ -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> - -#include "shell.h" -#include "syntax.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "redir.h" -#include "options.h" -#include "show.h" - - -#define OUTBUFSIZ BUFSIZ -#define BLOCK_OUT -2 /* output to a fixed block of memory */ -#define MEM_OUT -3 /* output to dynamically allocated memory */ - -#ifdef SMALL -#define CHAIN -#else -#define CHAIN ,NULL -#endif - - - /* nextc nleft bufsize buf fd flags chain */ -struct output output = {NULL, 0, OUTBUFSIZ, NULL, 1, 0 CHAIN }; -struct output errout = {NULL, 0, 100, NULL, 2, 0 CHAIN }; -struct output memout = {NULL, 0, 0, NULL, MEM_OUT, 0 CHAIN }; -struct output *out1 = &output; -struct output *out2 = &errout; -#ifndef SMALL -struct output *outx = &errout; -struct output *outxtop = NULL; -#endif - - -#ifdef mkinit - -INCLUDE "output.h" -INCLUDE "memalloc.h" - -RESET { - out1 = &output; - out2 = &errout; - if (memout.buf != NULL) { - ckfree(memout.buf); - memout.buf = NULL; - } -} - -#endif - - -#ifdef notdef /* no longer used */ -/* - * Set up an output file to write to memory rather than a file. - */ - -void -open_mem(char *block, int length, struct output *file) -{ - file->nextc = block; - file->nleft = --length; - file->fd = BLOCK_OUT; - file->flags = 0; -} -#endif - - -void -out1str(const char *p) -{ - outstr(p, out1); -} - - -void -out2str(const char *p) -{ - outstr(p, out2); -} - -#ifndef SMALL -void -outxstr(const char *p) -{ - outstr(p, outx); -} -#endif - - -void -outstr(const char *p, struct output *file) -{ - char c = 0; - - while (*p) - outc((c = *p++), file); - if (file == out2 || (file == outx && c == '\n')) - flushout(file); -} - - -void -out2shstr(const char *p) -{ - outshstr(p, out2); -} - -#ifndef SMALL -void -outxshstr(const char *p) -{ - outshstr(p, outx); -} -#endif - -/* - * ' is in this list, not because it does not require quoting - * (which applies to all the others) but because '' quoting cannot - * be used to quote it. - */ -static const char norm_chars [] = \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+-=_,.'"; - -static int -inquote(const char *p) -{ - size_t l = strspn(p, norm_chars); - char *s = strchr(p, '\''); - - return s == NULL ? p[l] != '\0' : s - p > (off_t)l; -} - -void -outshstr(const char *p, struct output *file) -{ - int need_q; - int inq; - char c; - - if (strchr(p, '\'') != NULL && p[1] != '\0') { - /* - * string contains ' in it, and is not only the ' - * see if " quoting will work - */ - size_t i = strcspn(p, "\\\"$`"); - - if (p[i] == '\0') { - /* - * string contains no $ ` \ or " chars, perfect... - * - * We know it contains ' so needs quoting, so - * this is easy... - */ - outc('"', file); - outstr(p, file); - outc('"', file); - return; - } - } - - need_q = p[0] == 0 || p[strspn(p, norm_chars)] != 0; - - /* - * Don't emit ' unless something needs quoting before closing ' - */ - if (need_q && (p[0] == 0 || inquote(p) != 0)) { - outc('\'', file); - inq = 1; - } else - inq = 0; - - while ((c = *p++) != '\0') { - if (c != '\'') { - outc(c, file); - continue; - } - - if (inq) - outc('\'', file); /* inq = 0, implicit */ - outc('\\', file); - outc(c, file); - if (need_q && *p != '\0') { - if ((inq = inquote(p)) != 0) - outc('\'', file); - } else - inq = 0; - } - - if (inq) - outc('\'', file); - - if (file == out2) - flushout(file); -} - - -char out_junk[16]; - - -void -emptyoutbuf(struct output *dest) -{ - int offset; - - if (dest->fd == BLOCK_OUT) { - dest->nextc = out_junk; - dest->nleft = sizeof out_junk; - dest->flags |= OUTPUT_ERR; - } else if (dest->buf == NULL) { - INTOFF; - dest->buf = ckmalloc(dest->bufsize); - dest->nextc = dest->buf; - dest->nleft = dest->bufsize; - INTON; - VTRACE(DBG_OUTPUT, ("emptyoutbuf now %d @%p for fd %d\n", - dest->nleft, dest->buf, dest->fd)); - } else if (dest->fd == MEM_OUT) { - offset = dest->bufsize; - INTOFF; - dest->bufsize <<= 1; - dest->buf = ckrealloc(dest->buf, dest->bufsize); - dest->nleft = dest->bufsize - offset; - dest->nextc = dest->buf + offset; - INTON; - } else { - flushout(dest); - } - dest->nleft--; -} - - -void -flushall(void) -{ - flushout(&output); - flushout(&errout); -} - - -void -flushout(struct output *dest) -{ - - if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) - return; - VTRACE(DBG_OUTPUT, ("flushout fd=%d %zd to write\n", dest->fd, - (size_t)(dest->nextc - dest->buf))); - if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) - dest->flags |= OUTPUT_ERR; - dest->nextc = dest->buf; - dest->nleft = dest->bufsize; -} - - -void -freestdout(void) -{ - INTOFF; - if (output.buf) { - ckfree(output.buf); - output.buf = NULL; - output.nleft = 0; - } - INTON; -} - - -void -outfmt(struct output *file, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - doformat(file, fmt, ap); - va_end(ap); -} - - -void -out1fmt(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - doformat(out1, fmt, ap); - va_end(ap); -} - -#ifdef DEBUG -void -debugprintf(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - doformat(out2, fmt, ap); - va_end(ap); - flushout(out2); -} -#endif - -void -fmtstr(char *outbuf, size_t length, const char *fmt, ...) -{ - va_list ap; - struct output strout; - - va_start(ap, fmt); - strout.nextc = outbuf; - strout.nleft = length; - strout.fd = BLOCK_OUT; - strout.flags = 0; - doformat(&strout, fmt, ap); - outc('\0', &strout); - if (strout.flags & OUTPUT_ERR) - outbuf[length - 1] = '\0'; - va_end(ap); -} - -/* - * Formatted output. This routine handles a subset of the printf formats: - * - Formats supported: d, u, o, p, X, s, and c. - * - The x format is also accepted but is treated like X. - * - The l, ll and q modifiers are accepted. - * - The - and # flags are accepted; # only works with the o format. - * - Width and precision may be specified with any format except c. - * - An * may be given for the width or precision. - * - The obsolete practice of preceding the width with a zero to get - * zero padding is not supported; use the precision field. - * - A % may be printed by writing %% in the format string. - */ - -#define TEMPSIZE 24 - -#ifdef BSD4_4 -#define HAVE_VASPRINTF 1 -#endif - -void -doformat(struct output *dest, const char *f, va_list ap) -{ -#if HAVE_VASPRINTF - char *s; - - vasprintf(&s, f, ap); - if (s == NULL) - error("Could not allocate formatted output buffer"); - outstr(s, dest); - free(s); -#else /* !HAVE_VASPRINTF */ - static const char digit[] = "0123456789ABCDEF"; - char c; - char temp[TEMPSIZE]; - int flushleft; - int sharp; - int width; - int prec; - int islong; - int isquad; - char *p; - int sign; -#ifdef BSD4_4 - quad_t l; - u_quad_t num; -#else - long l; - u_long num; -#endif - unsigned base; - int len; - int size; - int pad; - - while ((c = *f++) != '\0') { - if (c != '%') { - outc(c, dest); - continue; - } - flushleft = 0; - sharp = 0; - width = 0; - prec = -1; - islong = 0; - isquad = 0; - for (;;) { - if (*f == '-') - flushleft++; - else if (*f == '#') - sharp++; - else - break; - f++; - } - if (*f == '*') { - width = va_arg(ap, int); - f++; - } else { - while (is_digit(*f)) { - width = 10 * width + digit_val(*f++); - } - } - if (*f == '.') { - if (*++f == '*') { - prec = va_arg(ap, int); - f++; - } else { - prec = 0; - while (is_digit(*f)) { - prec = 10 * prec + digit_val(*f++); - } - } - } - if (*f == 'l') { - f++; - if (*f == 'l') { - isquad++; - f++; - } else - islong++; - } else if (*f == 'q') { - isquad++; - f++; - } - switch (*f) { - case 'd': -#ifdef BSD4_4 - if (isquad) - l = va_arg(ap, quad_t); - else -#endif - if (islong) - l = va_arg(ap, long); - else - l = va_arg(ap, int); - sign = 0; - num = l; - if (l < 0) { - num = -l; - sign = 1; - } - base = 10; - goto number; - case 'u': - base = 10; - goto uns_number; - case 'o': - base = 8; - goto uns_number; - case 'p': - outc('0', dest); - outc('x', dest); - /*FALLTHROUGH*/ - case 'x': - /* we don't implement 'x'; treat like 'X' */ - case 'X': - base = 16; -uns_number: /* an unsigned number */ - sign = 0; -#ifdef BSD4_4 - if (isquad) - num = va_arg(ap, u_quad_t); - else -#endif - if (islong) - num = va_arg(ap, unsigned long); - else - num = va_arg(ap, unsigned int); -number: /* process a number */ - p = temp + TEMPSIZE - 1; - *p = '\0'; - while (num) { - *--p = digit[num % base]; - num /= base; - } - len = (temp + TEMPSIZE - 1) - p; - if (prec < 0) - prec = 1; - if (sharp && *f == 'o' && prec <= len) - prec = len + 1; - pad = 0; - if (width) { - size = len; - if (size < prec) - size = prec; - size += sign; - pad = width - size; - if (flushleft == 0) { - while (--pad >= 0) - outc(' ', dest); - } - } - if (sign) - outc('-', dest); - prec -= len; - while (--prec >= 0) - outc('0', dest); - while (*p) - outc(*p++, dest); - while (--pad >= 0) - outc(' ', dest); - break; - case 's': - p = va_arg(ap, char *); - pad = 0; - if (width) { - len = strlen(p); - if (prec >= 0 && len > prec) - len = prec; - pad = width - len; - if (flushleft == 0) { - while (--pad >= 0) - outc(' ', dest); - } - } - prec++; - while (--prec != 0 && *p) - outc(*p++, dest); - while (--pad >= 0) - outc(' ', dest); - break; - case 'c': - c = va_arg(ap, int); - outc(c, dest); - break; - default: - outc(*f, dest); - break; - } - f++; - } -#endif /* !HAVE_VASPRINTF */ -} - - - -/* - * Version of write which resumes after a signal is caught. - */ - -int -xwrite(int fd, char *buf, int nbytes) -{ - int ntry; - int i; - int n; - - n = nbytes; - ntry = 0; - while (n > 0) { - i = write(fd, buf, n); - if (i > 0) { - if ((n -= i) <= 0) - return nbytes; - buf += i; - ntry = 0; - } else if (i == 0) { - if (++ntry > 10) - return nbytes - n; - } else if (errno != EINTR) { - return -1; - } - } - return nbytes; -} - -#ifndef SMALL -static void -xtrace_fd_swap(int from, int to) -{ - struct output *o = outxtop; - - while (o != NULL) { - if (o->fd == from) - o->fd = to; - o = o->chain; - } -} - -/* - * the -X flag is to be set or reset (not necessarily changed) - * Do what is needed to make tracing go to where it should - * - * Note: Xflag has not yet been altered, "on" indicates what it will become - */ - -void -xtracefdsetup(int on) -{ - if (!on) { - flushout(outx); - - if (Xflag != 1) /* Was already +X */ - return; /* so nothing to do */ - - outx = out2; - CTRACE(DBG_OUTPUT, ("Tracing to stderr\n")); - return; - } - - if (Xflag == 1) { /* was already set */ - /* - * This is a change of output file only - * Just close the current one, and reuse the struct output - */ - if (!(outx->flags & OUTPUT_CLONE)) - sh_close(outx->fd); - } else if (outxtop == NULL) { - /* - * -X is just turning on, for the forst time, - * need a new output struct to become outx - */ - xtrace_clone(1); - } else - outx = outxtop; - - if (outx != out2) { - outx->flags &= ~OUTPUT_CLONE; - outx->fd = to_upper_fd(dup(out2->fd)); - register_sh_fd(outx->fd, xtrace_fd_swap); - } - - CTRACE(DBG_OUTPUT, ("Tracing now to fd %d (from %d)\n", - outx->fd, out2->fd)); -} - -void -xtrace_clone(int new) -{ - struct output *o; - - CTRACE(DBG_OUTPUT, - ("xtrace_clone(%d): %sfd=%d buf=%p nleft=%d flags=%x ", - new, (outx == out2 ? "out2: " : ""), - outx->fd, outx->buf, outx->nleft, outx->flags)); - - flushout(outx); - - if (!new && outxtop == NULL && Xflag == 0) { - /* following stderr, nothing to save */ - CTRACE(DBG_OUTPUT, ("+X\n")); - return; - } - - o = ckmalloc(sizeof(*o)); - o->fd = outx->fd; - o->flags = OUTPUT_CLONE; - o->bufsize = outx->bufsize; - o->nleft = 0; - o->buf = NULL; - o->nextc = NULL; - o->chain = outxtop; - outx = o; - outxtop = o; - - CTRACE(DBG_OUTPUT, ("-> fd=%d flags=%x[CLONE]\n", outx->fd, o->flags)); -} - -void -xtrace_pop(void) -{ - struct output *o; - - CTRACE(DBG_OUTPUT, ("trace_pop: fd=%d buf=%p nleft=%d flags=%x ", - outx->fd, outx->buf, outx->nleft, outx->flags)); - - flushout(outx); - - if (outxtop == NULL) { - /* - * No -X has been used, so nothing much to do - */ - CTRACE(DBG_OUTPUT, ("+X\n")); - return; - } - - o = outxtop; - outx = o->chain; - if (outx == NULL) - outx = &errout; - outxtop = o->chain; - if (o != &errout) { - if (o->fd >= 0 && !(o->flags & OUTPUT_CLONE)) - sh_close(o->fd); - if (o->buf) - ckfree(o->buf); - ckfree(o); - } - - CTRACE(DBG_OUTPUT, ("-> fd=%d buf=%p nleft=%d flags=%x\n", - outx->fd, outx->buf, outx->nleft, outx->flags)); -} -#endif /* SMALL */ diff --git a/bin/sh/output.h b/bin/sh/output.h deleted file mode 100644 index a19df43..0000000 --- a/bin/sh/output.h +++ /dev/null @@ -1,109 +0,0 @@ -/* $NetBSD: output.h,v 1.27 2017/11/21 03:42:39 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)output.h 8.2 (Berkeley) 5/4/95 - */ - -#ifndef OUTPUT_INCL - -#include <stdarg.h> - -struct output { - char *nextc; - int nleft; - int bufsize; - char *buf; - short fd; - short flags; -#ifndef SMALL - struct output *chain; -#endif -}; - -/* flags for ->flags */ -#define OUTPUT_ERR 0x01 /* error occurred on output */ -#define OUTPUT_CLONE 0x02 /* this is a clone of another */ - -extern struct output output; -extern struct output errout; -extern struct output memout; -extern struct output *out1; -extern struct output *out2; -#ifdef SMALL -#define outx out2 -#else -extern struct output *outx; -#endif - -void open_mem(char *, int, struct output *); -void out1str(const char *); -void out2str(const char *); -void outstr(const char *, struct output *); -void out2shstr(const char *); -#ifdef SMALL -#define outxstr out2str -#define outxshstr out2shstr -#else -void outxstr(const char *); -void outxshstr(const char *); -#endif -void outshstr(const char *, struct output *); -void emptyoutbuf(struct output *); -void flushall(void); -void flushout(struct output *); -void freestdout(void); -void outfmt(struct output *, const char *, ...) __printflike(2, 3); -void out1fmt(const char *, ...) __printflike(1, 2); -#ifdef DEBUG -void debugprintf(const char *, ...) __printflike(1, 2); -#endif -void fmtstr(char *, size_t, const char *, ...) __printflike(3, 4); -void doformat(struct output *, const char *, va_list) __printflike(2, 0); -int xwrite(int, char *, int); -#ifdef SMALL -#define xtracefdsetup(x) do { break; } while (0) -#define xtrace_clone(x) do { break; } while (0) -#define xtrace_pop() do { break; } while (0) -#else -void xtracefdsetup(int); -void xtrace_clone(int); -void xtrace_pop(void); -#endif - -#define outc(c, file) (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c))) -#define out1c(c) outc(c, out1) -#define out2c(c) outc(c, out2) -#define outxc(c) outc(c, outx) - -#define OUTPUT_INCL -#endif diff --git a/bin/sh/parser.c b/bin/sh/parser.c deleted file mode 100644 index 6b153aa..0000000 --- a/bin/sh/parser.c +++ /dev/null @@ -1,2756 +0,0 @@ -/* $NetBSD: parser.c,v 1.164 2019/01/22 14:32:17 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; -#else -__RCSID("$NetBSD: parser.c,v 1.164 2019/01/22 14:32:17 kre Exp $"); -#endif -#endif /* not lint */ - -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> - -#include "shell.h" -#include "parser.h" -#include "nodes.h" -#include "expand.h" /* defines rmescapes() */ -#include "eval.h" /* defines commandname */ -#include "syntax.h" -#include "options.h" -#include "input.h" -#include "output.h" -#include "var.h" -#include "error.h" -#include "memalloc.h" -#include "mystring.h" -#include "alias.h" -#include "show.h" -#ifndef SMALL -#include "myhistedit.h" -#endif -#ifdef DEBUG -#include "nodenames.h" -#endif - -/* - * Shell command parser. - */ - -/* values returned by readtoken */ -#include "token.h" - -#define OPENBRACE '{' -#define CLOSEBRACE '}' - -struct HereDoc { - struct HereDoc *next; /* next here document in list */ - union node *here; /* redirection node */ - char *eofmark; /* string indicating end of input */ - int striptabs; /* if set, strip leading tabs */ - int startline; /* line number where << seen */ -}; - -MKINIT struct parse_state parse_state; -union parse_state_p psp = { .c_current_parser = &parse_state }; - -static const struct parse_state init_parse_state = { /* all 0's ... */ - .ps_heredoclist = NULL, - .ps_parsebackquote = 0, - .ps_doprompt = 0, - .ps_needprompt = 0, - .ps_lasttoken = 0, - .ps_tokpushback = 0, - .ps_wordtext = NULL, - .ps_checkkwd = 0, - .ps_redirnode = NULL, - .ps_heredoc = NULL, - .ps_quoteflag = 0, - .ps_startlinno = 0, - .ps_funclinno = 0, - .ps_elided_nl = 0, -}; - -STATIC union node *list(int); -STATIC union node *andor(void); -STATIC union node *pipeline(void); -STATIC union node *command(void); -STATIC union node *simplecmd(union node **, union node *); -STATIC union node *makeword(int); -STATIC void parsefname(void); -STATIC int slurp_heredoc(char *const, const int, const int); -STATIC void readheredocs(void); -STATIC int peektoken(void); -STATIC int readtoken(void); -STATIC int xxreadtoken(void); -STATIC int readtoken1(int, char const *, int); -STATIC int noexpand(char *); -STATIC void linebreak(void); -STATIC void consumetoken(int); -STATIC void synexpect(int, const char *) __dead; -STATIC void synerror(const char *) __dead; -STATIC void setprompt(int); -STATIC int pgetc_linecont(void); - -static const char EOFhere[] = "EOF reading here (<<) document"; - -#ifdef DEBUG -int parsing = 0; -#endif - -/* - * Read and parse a command. Returns NEOF on end of file. (NULL is a - * valid parse tree indicating a blank line.) - */ - -union node * -parsecmd(int interact) -{ - int t; - union node *n; - -#ifdef DEBUG - parsing++; -#endif - tokpushback = 0; - checkkwd = 0; - doprompt = interact; - if (doprompt) - setprompt(1); - else - setprompt(0); - needprompt = 0; - t = readtoken(); -#ifdef DEBUG - parsing--; -#endif - if (t == TEOF) - return NEOF; - if (t == TNL) - return NULL; - -#ifdef DEBUG - parsing++; -#endif - tokpushback++; - n = list(1); -#ifdef DEBUG - parsing--; -#endif - if (heredoclist) - error("%d: Here document (<<%s) expected but not present", - heredoclist->startline, heredoclist->eofmark); - return n; -} - - -STATIC union node * -list(int nlflag) -{ - union node *ntop, *n1, *n2, *n3; - int tok; - - CTRACE(DBG_PARSE, ("list(%d): entered @%d\n",nlflag,plinno)); - - checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (nlflag == 0 && tokendlist[peektoken()]) - return NULL; - ntop = n1 = NULL; - for (;;) { - n2 = andor(); - tok = readtoken(); - if (tok == TBACKGND) { - if (n2->type == NCMD || n2->type == NPIPE) - n2->ncmd.backgnd = 1; - else if (n2->type == NREDIR) - n2->type = NBACKGND; - else { - n3 = stalloc(sizeof(struct nredir)); - n3->type = NBACKGND; - n3->nredir.n = n2; - n3->nredir.redirect = NULL; - n2 = n3; - } - } - - if (ntop == NULL) - ntop = n2; - else if (n1 == NULL) { - n1 = stalloc(sizeof(struct nbinary)); - n1->type = NSEMI; - n1->nbinary.ch1 = ntop; - n1->nbinary.ch2 = n2; - ntop = n1; - } else { - n3 = stalloc(sizeof(struct nbinary)); - n3->type = NSEMI; - n3->nbinary.ch1 = n1->nbinary.ch2; - n3->nbinary.ch2 = n2; - n1->nbinary.ch2 = n3; - n1 = n3; - } - - switch (tok) { - case TBACKGND: - case TSEMI: - tok = readtoken(); - /* FALLTHROUGH */ - case TNL: - if (tok == TNL) { - readheredocs(); - if (nlflag) - return ntop; - } else if (tok == TEOF && nlflag) - return ntop; - else - tokpushback++; - - checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (!nlflag && tokendlist[peektoken()]) - return ntop; - break; - case TEOF: - pungetc(); /* push back EOF on input */ - return ntop; - default: - if (nlflag) - synexpect(-1, 0); - tokpushback++; - return ntop; - } - } -} - -STATIC union node * -andor(void) -{ - union node *n1, *n2, *n3; - int t; - - CTRACE(DBG_PARSE, ("andor: entered @%d\n", plinno)); - - n1 = pipeline(); - for (;;) { - if ((t = readtoken()) == TAND) { - t = NAND; - } else if (t == TOR) { - t = NOR; - } else { - tokpushback++; - return n1; - } - n2 = pipeline(); - n3 = stalloc(sizeof(struct nbinary)); - n3->type = t; - n3->nbinary.ch1 = n1; - n3->nbinary.ch2 = n2; - n1 = n3; - } -} - -STATIC union node * -pipeline(void) -{ - union node *n1, *n2, *pipenode; - struct nodelist *lp, *prev; - int negate; - - CTRACE(DBG_PARSE, ("pipeline: entered @%d\n", plinno)); - - negate = 0; - checkkwd = CHKNL | CHKKWD | CHKALIAS; - while (readtoken() == TNOT) { - CTRACE(DBG_PARSE, ("pipeline: TNOT recognized\n")); -#ifndef BOGUS_NOT_COMMAND - if (posix && negate) - synerror("2nd \"!\" unexpected"); -#endif - negate++; - } - tokpushback++; - n1 = command(); - if (readtoken() == TPIPE) { - pipenode = stalloc(sizeof(struct npipe)); - pipenode->type = NPIPE; - pipenode->npipe.backgnd = 0; - lp = stalloc(sizeof(struct nodelist)); - pipenode->npipe.cmdlist = lp; - lp->n = n1; - do { - prev = lp; - lp = stalloc(sizeof(struct nodelist)); - lp->n = command(); - prev->next = lp; - } while (readtoken() == TPIPE); - lp->next = NULL; - n1 = pipenode; - } - tokpushback++; - if (negate) { - CTRACE(DBG_PARSE, ("%snegate pipeline\n", - (negate&1) ? "" : "double ")); - n2 = stalloc(sizeof(struct nnot)); - n2->type = (negate & 1) ? NNOT : NDNOT; - n2->nnot.com = n1; - return n2; - } else - return n1; -} - - - -STATIC union node * -command(void) -{ - union node *n1, *n2; - union node *ap, **app; - union node *cp, **cpp; - union node *redir, **rpp; - int t; -#ifdef BOGUS_NOT_COMMAND - int negate = 0; -#endif - - CTRACE(DBG_PARSE, ("command: entered @%d\n", plinno)); - - checkkwd = CHKNL | CHKKWD | CHKALIAS; - redir = NULL; - n1 = NULL; - rpp = &redir; - - /* Check for redirection which may precede command */ - while (readtoken() == TREDIR) { - *rpp = n2 = redirnode; - rpp = &n2->nfile.next; - parsefname(); - } - tokpushback++; - -#ifdef BOGUS_NOT_COMMAND /* only in pileline() */ - while (readtoken() == TNOT) { - CTRACE(DBG_PARSE, ("command: TNOT (bogus) recognized\n")); - negate++; - } - tokpushback++; -#endif - - switch (readtoken()) { - case TIF: - n1 = stalloc(sizeof(struct nif)); - n1->type = NIF; - n1->nif.test = list(0); - consumetoken(TTHEN); - n1->nif.ifpart = list(0); - n2 = n1; - while (readtoken() == TELIF) { - n2->nif.elsepart = stalloc(sizeof(struct nif)); - n2 = n2->nif.elsepart; - n2->type = NIF; - n2->nif.test = list(0); - consumetoken(TTHEN); - n2->nif.ifpart = list(0); - } - if (lasttoken == TELSE) - n2->nif.elsepart = list(0); - else { - n2->nif.elsepart = NULL; - tokpushback++; - } - consumetoken(TFI); - checkkwd = CHKKWD | CHKALIAS; - break; - case TWHILE: - case TUNTIL: - n1 = stalloc(sizeof(struct nbinary)); - n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; - n1->nbinary.ch1 = list(0); - consumetoken(TDO); - n1->nbinary.ch2 = list(0); - consumetoken(TDONE); - checkkwd = CHKKWD | CHKALIAS; - break; - case TFOR: - if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) - synerror("Bad for loop variable"); - n1 = stalloc(sizeof(struct nfor)); - n1->type = NFOR; - n1->nfor.var = wordtext; - linebreak(); - if (lasttoken==TWORD && !quoteflag && equal(wordtext,"in")) { - app = ≈ - while (readtoken() == TWORD) { - n2 = makeword(startlinno); - *app = n2; - app = &n2->narg.next; - } - *app = NULL; - n1->nfor.args = ap; - if (lasttoken != TNL && lasttoken != TSEMI) - synexpect(TSEMI, 0); - } else { - static char argvars[5] = { - CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' - }; - - n2 = stalloc(sizeof(struct narg)); - n2->type = NARG; - n2->narg.text = argvars; - n2->narg.backquote = NULL; - n2->narg.next = NULL; - n2->narg.lineno = startlinno; - n1->nfor.args = n2; - /* - * Newline or semicolon here is optional (but note - * that the original Bourne shell only allowed NL). - */ - if (lasttoken != TNL && lasttoken != TSEMI) - tokpushback++; - } - checkkwd = CHKNL | CHKKWD | CHKALIAS; - if ((t = readtoken()) == TDO) - t = TDONE; - else if (t == TBEGIN) - t = TEND; - else - synexpect(TDO, 0); - n1->nfor.body = list(0); - consumetoken(t); - checkkwd = CHKKWD | CHKALIAS; - break; - case TCASE: - n1 = stalloc(sizeof(struct ncase)); - n1->type = NCASE; - n1->ncase.lineno = startlinno - elided_nl; - consumetoken(TWORD); - n1->ncase.expr = makeword(startlinno); - linebreak(); - if (lasttoken != TWORD || !equal(wordtext, "in")) - synexpect(-1, "in"); - cpp = &n1->ncase.cases; - checkkwd = CHKNL | CHKKWD; - readtoken(); - /* - * Both ksh and bash accept 'case x in esac' - * so configure scripts started taking advantage of this. - * The page: http://pubs.opengroup.org/onlinepubs/\ - * 009695399/utilities/xcu_chap02.html contradicts itself, - * as to if this is legal; the "Case Conditional Format" - * paragraph shows one case is required, but the "Grammar" - * section shows a grammar that explicitly allows the no - * case option. - * - * The standard also says (section 2.10): - * This formal syntax shall take precedence over the - * preceding text syntax description. - * ie: the "Grammar" section wins. The text is just - * a rough guide (introduction to the common case.) - */ - while (lasttoken != TESAC) { - *cpp = cp = stalloc(sizeof(struct nclist)); - cp->type = NCLIST; - app = &cp->nclist.pattern; - if (lasttoken == TLP) - readtoken(); - for (;;) { - if (lasttoken < TWORD) - synexpect(TWORD, 0); - *app = ap = makeword(startlinno); - checkkwd = CHKNL | CHKKWD; - if (readtoken() != TPIPE) - break; - app = &ap->narg.next; - readtoken(); - } - if (lasttoken != TRP) - synexpect(TRP, 0); - cp->nclist.lineno = startlinno; - cp->nclist.body = list(0); - - checkkwd = CHKNL | CHKKWD | CHKALIAS; - if ((t = readtoken()) != TESAC) { - if (t != TENDCASE && t != TCASEFALL) { - synexpect(TENDCASE, 0); - } else { - if (t == TCASEFALL) - cp->type = NCLISTCONT; - checkkwd = CHKNL | CHKKWD; - readtoken(); - } - } - cpp = &cp->nclist.next; - } - *cpp = NULL; - checkkwd = CHKKWD | CHKALIAS; - break; - case TLP: - n1 = stalloc(sizeof(struct nredir)); - n1->type = NSUBSHELL; - n1->nredir.n = list(0); - n1->nredir.redirect = NULL; - if (n1->nredir.n == NULL) - synexpect(-1, 0); - consumetoken(TRP); - checkkwd = CHKKWD | CHKALIAS; - break; - case TBEGIN: - n1 = list(0); - if (posix && n1 == NULL) - synexpect(-1, 0); - consumetoken(TEND); - checkkwd = CHKKWD | CHKALIAS; - break; - - case TBACKGND: - case TSEMI: - case TAND: - case TOR: - case TPIPE: - case TNL: - case TEOF: - case TRP: - case TENDCASE: - case TCASEFALL: - /* - * simple commands must have something in them, - * either a word (which at this point includes a=b) - * or a redirection. If we reached the end of the - * command (which one of these tokens indicates) - * when we are just starting, and have not had a - * redirect, then ... - * - * nb: it is still possible to end up with empty - * simple commands, if the "command" is a var - * expansion that produces nothing: - * X= ; $X && $X - * --> && - * That is OK and is handled after word expansions. - */ - if (!redir) - synexpect(-1, 0); - /* - * continue to build a node containing the redirect. - * the tokpushback means that our ending token will be - * read again in simplecmd, causing it to terminate, - * so only the redirect(s) will be contained in the - * returned n1 - */ - /* FALLTHROUGH */ - case TWORD: - tokpushback++; - n1 = simplecmd(rpp, redir); - goto checkneg; - default: - synexpect(-1, 0); - /* NOTREACHED */ - } - - /* Now check for redirection which may follow command */ - while (readtoken() == TREDIR) { - *rpp = n2 = redirnode; - rpp = &n2->nfile.next; - parsefname(); - } - tokpushback++; - *rpp = NULL; - if (redir) { - if (n1 == NULL || n1->type != NSUBSHELL) { - n2 = stalloc(sizeof(struct nredir)); - n2->type = NREDIR; - n2->nredir.n = n1; - n1 = n2; - } - n1->nredir.redirect = redir; - } - - checkneg: -#ifdef BOGUS_NOT_COMMAND - if (negate) { - VTRACE(DBG_PARSE, ("bogus %snegate command\n", - (negate&1) ? "" : "double ")); - n2 = stalloc(sizeof(struct nnot)); - n2->type = (negate & 1) ? NNOT : NDNOT; - n2->nnot.com = n1; - return n2; - } - else -#endif - return n1; -} - - -STATIC union node * -simplecmd(union node **rpp, union node *redir) -{ - union node *args, **app; - union node *n = NULL; - int line = 0; - int savecheckkwd; -#ifdef BOGUS_NOT_COMMAND - union node *n2; - int negate = 0; -#endif - - CTRACE(DBG_PARSE, ("simple command with%s redir already @%d\n", - redir ? "" : "out", plinno)); - - /* If we don't have any redirections already, then we must reset */ - /* rpp to be the address of the local redir variable. */ - if (redir == 0) - rpp = &redir; - - args = NULL; - app = &args; - -#ifdef BOGUS_NOT_COMMAND /* pipelines get negated, commands do not */ - while (readtoken() == TNOT) { - VTRACE(DBG_PARSE, ("simplcmd: bogus TNOT recognized\n")); - negate++; - } - tokpushback++; -#endif - - savecheckkwd = CHKALIAS; - for (;;) { - checkkwd = savecheckkwd; - if (readtoken() == TWORD) { - if (line == 0) - line = startlinno; - n = makeword(startlinno); - *app = n; - app = &n->narg.next; - if (savecheckkwd != 0 && !isassignment(wordtext)) - savecheckkwd = 0; - } else if (lasttoken == TREDIR) { - if (line == 0) - line = startlinno; - *rpp = n = redirnode; - rpp = &n->nfile.next; - parsefname(); /* read name of redirection file */ - } else if (lasttoken == TLP && app == &args->narg.next - && redir == 0) { - /* We have a function */ - consumetoken(TRP); - funclinno = plinno; - rmescapes(n->narg.text); - if (strchr(n->narg.text, '/')) - synerror("Bad function name"); - VTRACE(DBG_PARSE, ("Function '%s' seen @%d\n", - n->narg.text, plinno)); - n->type = NDEFUN; - n->narg.lineno = plinno - elided_nl; - n->narg.next = command(); - funclinno = 0; - goto checkneg; - } else { - tokpushback++; - break; - } - } - - if (args == NULL && redir == NULL) - synexpect(-1, 0); - *app = NULL; - *rpp = NULL; - n = stalloc(sizeof(struct ncmd)); - n->type = NCMD; - n->ncmd.lineno = line - elided_nl; - n->ncmd.backgnd = 0; - n->ncmd.args = args; - n->ncmd.redirect = redir; - n->ncmd.lineno = startlinno; - - checkneg: -#ifdef BOGUS_NOT_COMMAND - if (negate) { - VTRACE(DBG_PARSE, ("bogus %snegate simplecmd\n", - (negate&1) ? "" : "double ")); - n2 = stalloc(sizeof(struct nnot)); - n2->type = (negate & 1) ? NNOT : NDNOT; - n2->nnot.com = n; - return n2; - } - else -#endif - return n; -} - -STATIC union node * -makeword(int lno) -{ - union node *n; - - n = stalloc(sizeof(struct narg)); - n->type = NARG; - n->narg.next = NULL; - n->narg.text = wordtext; - n->narg.backquote = backquotelist; - n->narg.lineno = lno; - return n; -} - -void -fixredir(union node *n, const char *text, int err) -{ - - VTRACE(DBG_PARSE, ("Fix redir %s %d\n", text, err)); - if (!err) - n->ndup.vname = NULL; - - if (is_number(text)) - n->ndup.dupfd = number(text); - else if (text[0] == '-' && text[1] == '\0') - n->ndup.dupfd = -1; - else { - - if (err) - synerror("Bad fd number"); - else - n->ndup.vname = makeword(startlinno - elided_nl); - } -} - - -STATIC void -parsefname(void) -{ - union node *n = redirnode; - - if (readtoken() != TWORD) - synexpect(-1, 0); - if (n->type == NHERE) { - struct HereDoc *here = heredoc; - struct HereDoc *p; - - if (quoteflag == 0) - n->type = NXHERE; - VTRACE(DBG_PARSE, ("Here document %d @%d\n", n->type, plinno)); - if (here->striptabs) { - while (*wordtext == '\t') - wordtext++; - } - - /* - * this test is not really necessary, we are not - * required to expand wordtext, but there's no reason - * it cannot be $$ or something like that - that would - * not mean the pid, but literally two '$' characters. - * There is no need for limits on what the word can be. - * However, it needs to stay literal as entered, not - * have $ converted to CTLVAR or something, which as - * the parser is, at the minute, is impossible to prevent. - * So, leave it like this until the rest of the parser is fixed. - */ - if (!noexpand(wordtext)) - synerror("Illegal eof marker for << redirection"); - - rmescapes(wordtext); - here->eofmark = wordtext; - here->next = NULL; - if (heredoclist == NULL) - heredoclist = here; - else { - for (p = heredoclist ; p->next ; p = p->next) - continue; - p->next = here; - } - } else if (n->type == NTOFD || n->type == NFROMFD) { - fixredir(n, wordtext, 0); - } else { - n->nfile.fname = makeword(startlinno - elided_nl); - } -} - -/* - * Check to see whether we are at the end of the here document. When this - * is called, c is set to the first character of the next input line. If - * we are at the end of the here document, this routine sets the c to PEOF. - * The new value of c is returned. - */ - -static int -checkend(int c, char * const eofmark, const int striptabs) -{ - - if (striptabs) { - while (c == '\t') - c = pgetc(); - } - if (c == PEOF) { - if (*eofmark == '\0') - return (c); - synerror(EOFhere); - } - if (c == *eofmark) { - int c2; - char *q; - - for (q = eofmark + 1; c2 = pgetc(), *q != '\0' && c2 == *q; q++) - if (c2 == '\n') { - plinno++; - needprompt = doprompt; - } - if ((c2 == PEOF || c2 == '\n') && *q == '\0') { - c = PEOF; - if (c2 == '\n') { - plinno++; - needprompt = doprompt; - } - } else { - pungetc(); - pushstring(eofmark + 1, q - (eofmark + 1), NULL); - } - } else if (c == '\n' && *eofmark == '\0') { - c = PEOF; - plinno++; - needprompt = doprompt; - } - return (c); -} - - -/* - * Input any here documents. - */ - -STATIC int -slurp_heredoc(char *const eofmark, const int striptabs, const int sq) -{ - int c; - char *out; - int lines = plinno; - - c = pgetc(); - - /* - * If we hit EOF on the input, and the eofmark is a null string ('') - * we consider this empty line to be the eofmark, and exit without err. - */ - if (c == PEOF && *eofmark != '\0') - synerror(EOFhere); - - STARTSTACKSTR(out); - - while ((c = checkend(c, eofmark, striptabs)) != PEOF) { - do { - if (sq) { - /* - * in single quoted mode (eofmark quoted) - * all we look for is \n so we can check - * for the epfmark - everything saved literally. - */ - STPUTC(c, out); - if (c == '\n') { - plinno++; - break; - } - continue; - } - /* - * In double quoted (non-quoted eofmark) - * we must handle \ followed by \n here - * otherwise we can mismatch the end mark. - * All other uses of \ will be handled later - * when the here doc is expanded. - * - * This also makes sure \\ followed by \n does - * not suppress the newline (the \ quotes itself) - */ - if (c == '\\') { /* A backslash */ - STPUTC(c, out); - c = pgetc(); /* followed by */ - if (c == '\n') { /* a newline? */ - STPUTC(c, out); - plinno++; - continue; /* don't break */ - } - } - STPUTC(c, out); /* keep the char */ - if (c == '\n') { /* at end of line */ - plinno++; - break; /* look for eofmark */ - } - } while ((c = pgetc()) != PEOF); - - /* - * If we have read a line, and reached EOF, without - * finding the eofmark, whether the EOF comes before - * or immediately after the \n, that is an error. - */ - if (c == PEOF || (c = pgetc()) == PEOF) - synerror(EOFhere); - } - STPUTC('\0', out); - - c = out - stackblock(); - out = stackblock(); - grabstackblock(c); - wordtext = out; - - VTRACE(DBG_PARSE, - ("Slurped a %d line %sheredoc (to '%s')%s: len %d, \"%.*s%s\" @%d\n", - plinno - lines, sq ? "quoted " : "", eofmark, - striptabs ? " tab stripped" : "", c, (c > 16 ? 16 : c), - wordtext, (c > 16 ? "..." : ""), plinno)); - - return (plinno - lines); -} - -static char * -insert_elided_nl(char *str) -{ - while (elided_nl > 0) { - STPUTC(CTLNONL, str); - elided_nl--; - } - return str; -} - -STATIC void -readheredocs(void) -{ - struct HereDoc *here; - union node *n; - int line, l; - - line = 0; /*XXX - gcc! obviously unneeded */ - if (heredoclist) - line = heredoclist->startline + 1; - l = 0; - while (heredoclist) { - line += l; - here = heredoclist; - heredoclist = here->next; - if (needprompt) { - setprompt(2); - needprompt = 0; - } - - l = slurp_heredoc(here->eofmark, here->striptabs, - here->here->nhere.type == NHERE); - - here->here->nhere.doc = n = makeword(line); - - if (here->here->nhere.type == NHERE) - continue; - - /* - * Now "parse" here docs that have unquoted eofmarkers. - */ - setinputstring(wordtext, 1, line); - VTRACE(DBG_PARSE, ("Reprocessing %d line here doc from %d\n", - l, line)); - readtoken1(pgetc(), DQSYNTAX, 1); - n->narg.text = wordtext; - n->narg.backquote = backquotelist; - popfile(); - } -} - -STATIC int -peektoken(void) -{ - int t; - - t = readtoken(); - tokpushback++; - return (t); -} - -STATIC int -readtoken(void) -{ - int t; -#ifdef DEBUG - int alreadyseen = tokpushback; - int savecheckkwd = checkkwd; -#endif - struct alias *ap; - - top: - t = xxreadtoken(); - - if (checkkwd & CHKNL) { - while (t == TNL) { - readheredocs(); - t = xxreadtoken(); - } - } - - /* - * check for keywords and aliases - */ - if (t == TWORD && !quoteflag) { - const char *const *pp; - - if (checkkwd & CHKKWD) - for (pp = parsekwd; *pp; pp++) { - if (**pp == *wordtext && equal(*pp, wordtext)) { - lasttoken = t = pp - - parsekwd + KWDOFFSET; - VTRACE(DBG_PARSE, - ("keyword %s recognized @%d\n", - tokname[t], plinno)); - goto out; - } - } - - if (checkkwd & CHKALIAS && - (ap = lookupalias(wordtext, 1)) != NULL) { - VTRACE(DBG_PARSE, - ("alias '%s' recognized -> <:%s:>\n", - wordtext, ap->val)); - pushstring(ap->val, strlen(ap->val), ap); - goto top; - } - } - out: - if (t != TNOT) - checkkwd = 0; - - VTRACE(DBG_PARSE, ("%stoken %s %s @%d (chkkwd %x->%x)\n", - alreadyseen ? "reread " : "", tokname[t], - t == TWORD ? wordtext : "", plinno, savecheckkwd, checkkwd)); - return (t); -} - - -/* - * Read the next input token. - * If the token is a word, we set backquotelist to the list of cmds in - * backquotes. We set quoteflag to true if any part of the word was - * quoted. - * If the token is TREDIR, then we set redirnode to a structure containing - * the redirection. - * In all cases, the variable startlinno is set to the number of the line - * on which the token starts. - * - * [Change comment: here documents and internal procedures] - * [Readtoken shouldn't have any arguments. Perhaps we should make the - * word parsing code into a separate routine. In this case, readtoken - * doesn't need to have any internal procedures, but parseword does. - * We could also make parseoperator in essence the main routine, and - * have parseword (readtoken1?) handle both words and redirection.] - */ - -#define RETURN(token) return lasttoken = (token) - -STATIC int -xxreadtoken(void) -{ - int c; - - if (tokpushback) { - tokpushback = 0; - CTRACE(DBG_LEXER, - ("xxreadtoken() returns %s (%d) again\n", - tokname[lasttoken], lasttoken)); - return lasttoken; - } - if (needprompt) { - setprompt(2); - needprompt = 0; - } - elided_nl = 0; - startlinno = plinno; - for (;;) { /* until token or start of word found */ - c = pgetc_macro(); - CTRACE(DBG_LEXER, ("xxreadtoken() sees '%c' (%#.2x) ", - c&0xFF, c&0x1FF)); - switch (c) { - case ' ': case '\t': case PFAKE: - CTRACE(DBG_LEXER, (" ignored\n")); - continue; - case '#': - while ((c = pgetc()) != '\n' && c != PEOF) - continue; - CTRACE(DBG_LEXER, - ("skipped comment to (not incl) \\n\n")); - pungetc(); - continue; - - case '\n': - plinno++; - CTRACE(DBG_LEXER, ("newline now @%d\n", plinno)); - needprompt = doprompt; - RETURN(TNL); - case PEOF: - CTRACE(DBG_LEXER, ("EOF -> TEOF (return)\n")); - RETURN(TEOF); - - case '&': - if (pgetc_linecont() == '&') { - CTRACE(DBG_LEXER, - ("and another -> TAND (return)\n")); - RETURN(TAND); - } - pungetc(); - CTRACE(DBG_LEXER, (" -> TBACKGND (return)\n")); - RETURN(TBACKGND); - case '|': - if (pgetc_linecont() == '|') { - CTRACE(DBG_LEXER, - ("and another -> TOR (return)\n")); - RETURN(TOR); - } - pungetc(); - CTRACE(DBG_LEXER, (" -> TPIPE (return)\n")); - RETURN(TPIPE); - case ';': - switch (pgetc_linecont()) { - case ';': - CTRACE(DBG_LEXER, - ("and another -> TENDCASE (return)\n")); - RETURN(TENDCASE); - case '&': - CTRACE(DBG_LEXER, - ("and '&' -> TCASEFALL (return)\n")); - RETURN(TCASEFALL); - default: - pungetc(); - CTRACE(DBG_LEXER, (" -> TSEMI (return)\n")); - RETURN(TSEMI); - } - case '(': - CTRACE(DBG_LEXER, (" -> TLP (return)\n")); - RETURN(TLP); - case ')': - CTRACE(DBG_LEXER, (" -> TRP (return)\n")); - RETURN(TRP); - - case '\\': - switch (pgetc()) { - case '\n': - startlinno = ++plinno; - CTRACE(DBG_LEXER, ("\\\n ignored, now @%d\n", - plinno)); - if (doprompt) - setprompt(2); - else - setprompt(0); - continue; - case PEOF: - CTRACE(DBG_LEXER, - ("then EOF -> TEOF (return) '\\' dropped\n")); - RETURN(TEOF); - default: - CTRACE(DBG_LEXER, ("not \\\n or EOF: ")); - pungetc(); - break; - } - /* FALLTHROUGH */ - default: - CTRACE(DBG_LEXER, ("getting a word\n")); - return readtoken1(c, BASESYNTAX, 0); - } - } -#undef RETURN -} - - - -/* - * If eofmark is NULL, read a word or a redirection symbol. If eofmark - * is not NULL, read a here document. In the latter case, eofmark is the - * word which marks the end of the document and striptabs is true if - * leading tabs should be stripped from the document. The argument firstc - * is the first character of the input token or document. - * - * Because C does not have internal subroutines, I have simulated them - * using goto's to implement the subroutine linkage. The following macros - * will run code that appears at the end of readtoken1. - */ - -/* - * We used to remember only the current syntax, variable nesting level, - * double quote state for each var nesting level, and arith nesting - * level (unrelated to var nesting) and one prev syntax when in arith - * syntax. This worked for simple cases, but can't handle arith inside - * var expansion inside arith inside var with some quoted and some not. - * - * Inspired by FreeBSD's implementation (though it was the obvious way) - * though implemented differently, we now have a stack that keeps track - * of what we are doing now, and what we were doing previously. - * Every time something changes, which will eventually end and should - * revert to the previous state, we push this stack, and then pop it - * again later (that is every ${} with an operator (to parse the word - * or pattern that follows) ${x} and $x are too simple to need it) - * $(( )) $( ) and "...". Always. Really, always! - * - * The stack is implemented as one static (on the C stack) base block - * containing LEVELS_PER_BLOCK (8) stack entries, which should be - * enough for the vast majority of cases. For torture tests, we - * malloc more blocks as needed. All accesses through the inline - * functions below. - */ - -/* - * varnest & arinest will typically be 0 or 1 - * (varnest can increment in usages like ${x=${y}} but probably - * does not really need to) - * parenlevel allows balancing parens inside a $(( )), it is reset - * at each new nesting level ( $(( ( x + 3 ${unset-)} )) does not work. - * quoted is special - we need to know 2 things ... are we inside "..." - * (even if inherited from some previous nesting level) and was there - * an opening '"' at this level (so the next will be closing). - * "..." can span nesting levels, but cannot be opened in one and - * closed in a different one. - * To handle this, "quoted" has two fields, the bottom 4 (really 2) - * bits are 0, 1, or 2, for un, single, and double quoted (single quoted - * is really so special that this setting is not very important) - * and 0x10 that indicates that an opening quote has been seen. - * The bottom 4 bits are inherited, the 0x10 bit is not. - */ -struct tokenstate { - const char *ts_syntax; - unsigned short ts_parenlevel; /* counters */ - unsigned short ts_varnest; /* 64000 levels should be enough! */ - unsigned short ts_arinest; - unsigned short ts_quoted; /* 1 -> single, 2 -> double */ - unsigned short ts_magicq; /* heredoc or word expand */ -}; - -#define NQ 0x00 /* Unquoted */ -#define SQ 0x01 /* Single Quotes */ -#define DQ 0x02 /* Double Quotes (or equivalent) */ -#define CQ 0x03 /* C style Single Quotes */ -#define QF 0x0F /* Mask to extract previous values */ -#define QS 0x10 /* Quoting started at this level in stack */ - -#define LEVELS_PER_BLOCK 8 -#define VSS struct statestack - -struct statestack { - VSS *prev; /* previous block in list */ - int cur; /* which of our tokenstates is current */ - struct tokenstate tokenstate[LEVELS_PER_BLOCK]; -}; - -static inline struct tokenstate * -currentstate(VSS *stack) -{ - return &stack->tokenstate[stack->cur]; -} - -#ifdef notdef -static inline struct tokenstate * -prevstate(VSS *stack) -{ - if (stack->cur != 0) - return &stack->tokenstate[stack->cur - 1]; - if (stack->prev == NULL) /* cannot drop below base */ - return &stack->tokenstate[0]; - return &stack->prev->tokenstate[LEVELS_PER_BLOCK - 1]; -} -#endif - -static inline VSS * -bump_state_level(VSS *stack) -{ - struct tokenstate *os, *ts; - - os = currentstate(stack); - - if (++stack->cur >= LEVELS_PER_BLOCK) { - VSS *ss; - - ss = (VSS *)ckmalloc(sizeof (struct statestack)); - ss->cur = 0; - ss->prev = stack; - stack = ss; - } - - ts = currentstate(stack); - - ts->ts_parenlevel = 0; /* parens inside never match outside */ - - ts->ts_quoted = os->ts_quoted & QF; /* these are default settings */ - ts->ts_varnest = os->ts_varnest; - ts->ts_arinest = os->ts_arinest; /* when appropriate */ - ts->ts_syntax = os->ts_syntax; /* they will be altered */ - ts->ts_magicq = os->ts_magicq; - - return stack; -} - -static inline VSS * -drop_state_level(VSS *stack) -{ - if (stack->cur == 0) { - VSS *ss; - - ss = stack; - stack = ss->prev; - if (stack == NULL) - return ss; - ckfree(ss); - } - --stack->cur; - return stack; -} - -static inline void -cleanup_state_stack(VSS *stack) -{ - while (stack->prev != NULL) { - stack->cur = 0; - stack = drop_state_level(stack); - } -} - -#define PARSESUB() {goto parsesub; parsesub_return:;} -#define PARSEARITH() {goto parsearith; parsearith_return:;} - -/* - * The following macros all assume the existance of a local var "stack" - * which contains a pointer to the current struct stackstate - */ - -/* - * These are macros rather than inline funcs to avoid code churn as much - * as possible - they replace macros of the same name used previously. - */ -#define ISDBLQUOTE() (currentstate(stack)->ts_quoted & QS) -#define SETDBLQUOTE() (currentstate(stack)->ts_quoted = QS | DQ) -#ifdef notdef -#define CLRDBLQUOTE() (currentstate(stack)->ts_quoted = \ - stack->cur != 0 || stack->prev ? \ - prevstate(stack)->ts_quoted & QF : 0) -#endif - -/* - * This set are just to avoid excess typing and line lengths... - * The ones that "look like" var names must be implemented to be lvalues - */ -#define syntax (currentstate(stack)->ts_syntax) -#define parenlevel (currentstate(stack)->ts_parenlevel) -#define varnest (currentstate(stack)->ts_varnest) -#define arinest (currentstate(stack)->ts_arinest) -#define quoted (currentstate(stack)->ts_quoted) -#define magicq (currentstate(stack)->ts_magicq) -#define TS_PUSH() (stack = bump_state_level(stack)) -#define TS_POP() (stack = drop_state_level(stack)) - -/* - * Called to parse command substitutions. oldstyle is true if the command - * is enclosed inside `` (otherwise it was enclosed in "$( )") - * - * Internally nlpp is a pointer to the head of the linked - * list of commands (passed by reference), and savelen is the number of - * characters on the top of the stack which must be preserved. - */ -static char * -parsebackq(VSS *const stack, char * const in, - struct nodelist **const pbqlist, const int oldstyle) -{ - struct nodelist **nlpp; - const int savepbq = parsebackquote; - union node *n; - char *out; - char *str = NULL; - char *volatile sstr = str; - struct jmploc jmploc; - struct jmploc *const savehandler = handler; - struct parsefile *const savetopfile = getcurrentfile(); - const int savelen = in - stackblock(); - int saveprompt; - int lno; - - if (setjmp(jmploc.loc)) { - popfilesupto(savetopfile); - if (sstr) - ckfree(__UNVOLATILE(sstr)); - cleanup_state_stack(stack); - parsebackquote = 0; - handler = savehandler; - CTRACE(DBG_LEXER, ("parsebackq() err (%d), unwinding\n", - exception)); - longjmp(handler->loc, 1); - } - INTOFF; - sstr = str = NULL; - if (savelen > 0) { - sstr = str = ckmalloc(savelen); - memcpy(str, stackblock(), savelen); - } - handler = &jmploc; - INTON; - if (oldstyle) { - /* - * We must read until the closing backquote, giving special - * treatment to some slashes, and then push the string and - * reread it as input, interpreting it normally. - */ - int pc; - int psavelen; - char *pstr; - int line1 = plinno; - - VTRACE(DBG_PARSE|DBG_LEXER, - ("parsebackq: repackaging `` as $( )")); - /* - * Because the entire `...` is read here, we don't - * need to bother the state stack. That will be used - * (as appropriate) when the processed string is re-read. - */ - STARTSTACKSTR(out); -#ifdef DEBUG - for (psavelen = 0;;psavelen++) { /* } */ -#else - for (;;) { -#endif - if (needprompt) { - setprompt(2); - needprompt = 0; - } - pc = pgetc(); - VTRACE(DBG_LEXER, - ("parsebackq() got '%c'(%#.2x) in `` %s", pc&0xFF, - pc&0x1FF, pc == '`' ? "terminator\n" : "")); - if (pc == '`') - break; - switch (pc) { - case '\\': - pc = pgetc(); - VTRACE(DBG_LEXER, ("then '%c'(%#.2x) ", - pc&0xFF, pc&0x1FF)); -#ifdef DEBUG - psavelen++; -#endif - if (pc == '\n') { /* keep \ \n for later */ - plinno++; - VTRACE(DBG_LEXER, ("@%d ", plinno)); - needprompt = doprompt; - } - if (pc != '\\' && pc != '`' && pc != '$' - && (!ISDBLQUOTE() || pc != '"')) { - VTRACE(DBG_LEXER, ("keep '\\' ")); - STPUTC('\\', out); - } - break; - - case '\n': - plinno++; - VTRACE(DBG_LEXER, ("@%d ", plinno)); - needprompt = doprompt; - break; - - case PEOF: - startlinno = line1; - VTRACE(DBG_LEXER, ("EOF\n", plinno)); - synerror("EOF in backquote substitution"); - break; - - default: - break; - } - VTRACE(DBG_LEXER, (".\n", plinno)); - STPUTC(pc, out); - } - STPUTC('\0', out); - VTRACE(DBG_LEXER, ("parsebackq() ``:")); - VTRACE(DBG_PARSE|DBG_LEXER, (" read %d", psavelen)); - psavelen = out - stackblock(); - VTRACE(DBG_PARSE|DBG_LEXER, (" produced %d\n", psavelen)); - if (psavelen > 0) { - pstr = grabstackstr(out); - CTRACE(DBG_LEXER, - ("parsebackq() reprocessing as $(%s)\n", pstr)); - setinputstring(pstr, 1, line1); - } - } - nlpp = pbqlist; - while (*nlpp) - nlpp = &(*nlpp)->next; - *nlpp = stalloc(sizeof(struct nodelist)); - (*nlpp)->next = NULL; - parsebackquote = oldstyle; - - if (oldstyle) { - saveprompt = doprompt; - doprompt = 0; - } else - saveprompt = 0; - - lno = -plinno; - CTRACE(DBG_LEXER, ("parsebackq() parsing embedded command list\n")); - n = list(0); - CTRACE(DBG_LEXER, ("parsebackq() parsed $() (%d -> %d)\n", -lno, - lno + plinno)); - lno += plinno; - - if (oldstyle) { - if (peektoken() != TEOF) - synexpect(-1, 0); - doprompt = saveprompt; - } else - consumetoken(TRP); - - (*nlpp)->n = n; - if (oldstyle) { - /* - * Start reading from old file again, ignoring any pushed back - * tokens left from the backquote parsing - */ - CTRACE(DBG_LEXER, ("parsebackq() back to previous input\n")); - popfile(); - tokpushback = 0; - } - - while (stackblocksize() <= savelen) - growstackblock(); - STARTSTACKSTR(out); - if (str) { - memcpy(out, str, savelen); - STADJUST(savelen, out); - INTOFF; - ckfree(str); - sstr = str = NULL; - INTON; - } - parsebackquote = savepbq; - handler = savehandler; - if (arinest || ISDBLQUOTE()) { - STPUTC(CTLBACKQ | CTLQUOTE, out); - while (--lno >= 0) - STPUTC(CTLNONL, out); - } else - STPUTC(CTLBACKQ, out); - - return out; -} - -/* - * Parse a redirection operator. The parameter "out" points to a string - * specifying the fd to be redirected. It is guaranteed to be either "" - * or a numeric string (for now anyway). The parameter "c" contains the - * first character of the redirection operator. - * - * Note the string "out" is on the stack, which we are about to clobber, - * so process it first... - */ - -static void -parseredir(const char *out, int c) -{ - union node *np; - int fd; - - fd = (*out == '\0') ? -1 : number(out); - - np = stalloc(sizeof(struct nfile)); - VTRACE(DBG_LEXER, ("parseredir after '%s%c' ", out, c)); - if (c == '>') { - if (fd < 0) - fd = 1; - c = pgetc_linecont(); - VTRACE(DBG_LEXER, ("is '%c'(%#.2x) ", c&0xFF, c&0x1FF)); - if (c == '>') - np->type = NAPPEND; - else if (c == '|') - np->type = NCLOBBER; - else if (c == '&') - np->type = NTOFD; - else { - np->type = NTO; - VTRACE(DBG_LEXER, ("unwanted ", c)); - pungetc(); - } - } else { /* c == '<' */ - if (fd < 0) - fd = 0; - c = pgetc_linecont(); - VTRACE(DBG_LEXER, ("is '%c'(%#.2x) ", c&0xFF, c&0x1FF)); - switch (c) { - case '<': - /* if sizes differ, just discard the old one */ - if (sizeof (struct nfile) != sizeof (struct nhere)) - np = stalloc(sizeof(struct nhere)); - np->type = NHERE; - np->nhere.fd = 0; - heredoc = stalloc(sizeof(struct HereDoc)); - heredoc->here = np; - heredoc->startline = plinno; - if ((c = pgetc_linecont()) == '-') { - CTRACE(DBG_LEXER, ("and '%c'(%#.2x) ", - c & 0xFF, c & 0x1FF)); - heredoc->striptabs = 1; - } else { - heredoc->striptabs = 0; - pungetc(); - } - break; - - case '&': - np->type = NFROMFD; - break; - - case '>': - np->type = NFROMTO; - break; - - default: - np->type = NFROM; - VTRACE(DBG_LEXER, ("unwanted('%c'0#.2x)", c&0xFF, - c&0x1FF)); - pungetc(); - break; - } - } - np->nfile.fd = fd; - - VTRACE(DBG_LEXER, (" ->%"PRIdsNT" fd=%d\n", NODETYPENAME(np->type),fd)); - - redirnode = np; /* this is the "value" of TRENODE */ -} - -/* - * Called to parse a backslash escape sequence inside $'...'. - * The backslash has already been read. - */ -static char * -readcstyleesc(char *out) -{ - int c, vc, i, n; - unsigned int v; - - c = pgetc(); - VTRACE(DBG_LEXER, ("CSTR(\\%c)(\\%#x)", c&0xFF, c&0x1FF)); - switch (c) { - case '\0': - case PEOF: - synerror("Unterminated quoted string"); - case '\n': - plinno++; - VTRACE(DBG_LEXER, ("@%d ", plinno)); - if (doprompt) - setprompt(2); - else - setprompt(0); - return out; - - case '\\': - case '\'': - case '"': - v = c; - break; - - case 'a': v = '\a'; break; - case 'b': v = '\b'; break; - case 'e': v = '\033'; break; - case 'f': v = '\f'; break; - case 'n': v = '\n'; break; - case 'r': v = '\r'; break; - case 't': v = '\t'; break; - case 'v': v = '\v'; break; - - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - v = c - '0'; - c = pgetc(); - if (c >= '0' && c <= '7') { - v <<= 3; - v += c - '0'; - c = pgetc(); - if (c >= '0' && c <= '7') { - v <<= 3; - v += c - '0'; - } else - pungetc(); - } else - pungetc(); - break; - - case 'c': - c = pgetc(); - if (c < 0x3f || c > 0x7a || c == 0x60) - synerror("Bad \\c escape sequence"); - if (c == '\\' && pgetc() != '\\') - synerror("Bad \\c\\ escape sequence"); - if (c == '?') - v = 127; - else - v = c & 0x1f; - break; - - case 'x': - n = 2; - goto hexval; - case 'u': - n = 4; - goto hexval; - case 'U': - n = 8; - hexval: - v = 0; - for (i = 0; i < n; i++) { - c = pgetc(); - if (c >= '0' && c <= '9') - v = (v << 4) + c - '0'; - else if (c >= 'A' && c <= 'F') - v = (v << 4) + c - 'A' + 10; - else if (c >= 'a' && c <= 'f') - v = (v << 4) + c - 'a' + 10; - else { - pungetc(); - break; - } - } - if (n > 2 && v > 127) { - if (v >= 0xd800 && v <= 0xdfff) - synerror("Invalid \\u escape sequence"); - - /* XXX should we use iconv here. What locale? */ - CHECKSTRSPACE(4, out); - - if (v <= 0x7ff) { - USTPUTC(0xc0 | v >> 6, out); - USTPUTC(0x80 | (v & 0x3f), out); - return out; - } else if (v <= 0xffff) { - USTPUTC(0xe0 | v >> 12, out); - USTPUTC(0x80 | ((v >> 6) & 0x3f), out); - USTPUTC(0x80 | (v & 0x3f), out); - return out; - } else if (v <= 0x10ffff) { - USTPUTC(0xf0 | v >> 18, out); - USTPUTC(0x80 | ((v >> 12) & 0x3f), out); - USTPUTC(0x80 | ((v >> 6) & 0x3f), out); - USTPUTC(0x80 | (v & 0x3f), out); - return out; - } - if (v > 127) - v = '?'; - } - break; - default: - synerror("Unknown $'' escape sequence"); - } - vc = (char)v; - VTRACE(DBG_LEXER, ("->%u(%#x)['%c']", v, v, vc&0xFF)); - - /* - * If we managed to create a \n from a \ sequence (no matter how) - * then we replace it with the magic CRTCNL control char, which - * will turn into a \n again later, but in the meantime, never - * causes LINENO increments. - */ - if (vc == '\n') { - VTRACE(DBG_LEXER, ("CTLCNL.")); - USTPUTC(CTLCNL, out); - return out; - } - - /* - * We can't handle NUL bytes. - * POSIX says we should skip till the closing quote. - */ - if (vc == '\0') { - CTRACE(DBG_LEXER, ("\\0: skip to '", v, v, vc&0xFF)); - while ((c = pgetc()) != '\'') { - if (c == '\\') - c = pgetc(); - if (c == PEOF) - synerror("Unterminated quoted string"); - if (c == '\n') { - plinno++; - if (doprompt) - setprompt(2); - else - setprompt(0); - } - } - pungetc(); - return out; - } - CVTRACE(DBG_LEXER, NEEDESC(vc), ("CTLESC-")); - VTRACE(DBG_LEXER, ("'%c'(%#.2x)", vc&0xFF, vc&0x1FF)); - if (NEEDESC(vc)) - USTPUTC(CTLESC, out); - USTPUTC(vc, out); - return out; -} - -/* - * The lowest level basic tokenizer. - * - * The next input byte (character) is in firstc, syn says which - * syntax tables we are to use (basic, single or double quoted, or arith) - * and magicq (used with sqsyntax and dqsyntax only) indicates that the - * quote character itself is not special (used parsing here docs and similar) - * - * The result is the type of the next token (its value, when there is one, - * is saved in the relevant global var - must fix that someday!) which is - * also saved for re-reading ("lasttoken"). - * - * Overall, this routine does far more parsing than it is supposed to. - * That will also need fixing, someday... - */ -STATIC int -readtoken1(int firstc, char const *syn, int oneword) -{ - int c; - char * out; - int len; - struct nodelist *bqlist; - int quotef; - VSS static_stack; - VSS *stack = &static_stack; - - stack->prev = NULL; - stack->cur = 0; - - syntax = syn; - -#ifdef DEBUG -#define SYNTAX ( syntax == BASESYNTAX ? "BASE" : \ - syntax == DQSYNTAX ? "DQ" : \ - syntax == SQSYNTAX ? "SQ" : \ - syntax == ARISYNTAX ? "ARI" : \ - "???" ) -#endif - - startlinno = plinno; - varnest = 0; - quoted = 0; - if (syntax == DQSYNTAX) - SETDBLQUOTE(); - quotef = 0; - bqlist = NULL; - arinest = 0; - parenlevel = 0; - elided_nl = 0; - magicq = oneword; - - CTRACE(DBG_LEXER, ("readtoken1(%c) syntax=%s %s%s(quoted=%x)\n", - firstc&0xFF, SYNTAX, magicq ? "magic quotes" : "", - ISDBLQUOTE()?" ISDBLQUOTE":"", quoted)); - - STARTSTACKSTR(out); - - for (c = firstc ;; c = pgetc_macro()) { /* until of token */ - if (syntax == ARISYNTAX) - out = insert_elided_nl(out); - CHECKSTRSPACE(6, out); /* permit 6 calls to USTPUTC */ - switch (syntax[c]) { - case CFAKE: - VTRACE(DBG_LEXER, ("CFAKE")); - if (syntax == BASESYNTAX && varnest == 0) - break; - VTRACE(DBG_LEXER, (",")); - continue; - case CNL: /* '\n' */ - VTRACE(DBG_LEXER, ("CNL")); - if (syntax == BASESYNTAX && varnest == 0) - break; /* exit loop */ - USTPUTC(c, out); - plinno++; - VTRACE(DBG_LEXER, ("@%d,", plinno)); - if (doprompt) - setprompt(2); - else - setprompt(0); - continue; - - case CSBACK: /* single quoted backslash */ - if ((quoted & QF) == CQ) { - out = readcstyleesc(out); - continue; - } - VTRACE(DBG_LEXER, ("ESC:")); - USTPUTC(CTLESC, out); - /* FALLTHROUGH */ - case CWORD: - VTRACE(DBG_LEXER, ("'%c'", c)); - USTPUTC(c, out); - continue; - - case CCTL: - CVTRACE(DBG_LEXER, !magicq || ISDBLQUOTE(), - ("%s%sESC:",!magicq?"!m":"",ISDBLQUOTE()?"DQ":"")); - if (!magicq || ISDBLQUOTE()) - USTPUTC(CTLESC, out); - VTRACE(DBG_LEXER, ("'%c'", c)); - USTPUTC(c, out); - continue; - case CBACK: /* backslash */ - c = pgetc(); - VTRACE(DBG_LEXER, ("\\'%c'(%#.2x)", c&0xFF, c&0x1FF)); - if (c == PEOF) { - VTRACE(DBG_LEXER, ("EOF, keep \\ ")); - USTPUTC('\\', out); - pungetc(); - continue; - } - if (c == '\n') { - plinno++; - elided_nl++; - VTRACE(DBG_LEXER, ("eli \\n (%d) @%d ", - elided_nl, plinno)); - if (doprompt) - setprompt(2); - else - setprompt(0); - continue; - } - CVTRACE(DBG_LEXER, quotef==0, (" QF=1 ")); - quotef = 1; /* current token is quoted */ - if (quoted && c != '\\' && c != '`' && - c != '$' && (c != '"' || magicq)) { - /* - * retain the \ (which we *know* needs CTLESC) - * when in "..." and the following char is - * not one of the magic few.) - * Otherwise the \ has done its work, and - * is dropped. - */ - VTRACE(DBG_LEXER, ("ESC:'\\'")); - USTPUTC(CTLESC, out); - USTPUTC('\\', out); - } - CVTRACE(DBG_LEXER, NEEDESC(c) || !magicq, - ("%sESC:", NEEDESC(c) ? "+" : "m")); - VTRACE(DBG_LEXER, ("'%c'(%#.2x)", c&0xFF, c&0x1FF)); - if (NEEDESC(c)) - USTPUTC(CTLESC, out); - else if (!magicq) { - USTPUTC(CTLESC, out); - USTPUTC(c, out); - continue; - } - USTPUTC(c, out); - continue; - case CSQUOTE: - if (syntax != SQSYNTAX) { - CVTRACE(DBG_LEXER, !magicq, (" CQM ")); - if (!magicq) - USTPUTC(CTLQUOTEMARK, out); - CVTRACE(DBG_LEXER, quotef==0, (" QF=1 ")); - quotef = 1; - TS_PUSH(); - syntax = SQSYNTAX; - quoted = SQ; - VTRACE(DBG_LEXER, (" TS_PUSH(SQ)")); - continue; - } - if (magicq && arinest == 0 && varnest == 0) { - /* Ignore inside quoted here document */ - VTRACE(DBG_LEXER, ("<<'>>")); - USTPUTC(c, out); - continue; - } - /* End of single quotes... */ - TS_POP(); - VTRACE(DBG_LEXER, ("SQ TS_POP->%s ", SYNTAX)); - CVTRACE(DBG_LEXER, syntax == BASESYNTAX, (" CQE ")); - if (syntax == BASESYNTAX) - USTPUTC(CTLQUOTEEND, out); - continue; - case CDQUOTE: - if (magicq && arinest == 0 /* && varnest == 0 */) { - VTRACE(DBG_LEXER, ("<<\">>")); - /* Ignore inside here document */ - USTPUTC(c, out); - continue; - } - CVTRACE(DBG_LEXER, quotef==0, (" QF=1 ")); - quotef = 1; - if (arinest) { - if (ISDBLQUOTE()) { - VTRACE(DBG_LEXER, - (" CQE ari(%d", arinest)); - USTPUTC(CTLQUOTEEND, out); - TS_POP(); - VTRACE(DBG_LEXER, ("%d)TS_POP->%s ", - arinest, SYNTAX)); - } else { - VTRACE(DBG_LEXER, - (" ari(%d) %s TS_PUSH->DQ CQM ", - arinest, SYNTAX)); - TS_PUSH(); - syntax = DQSYNTAX; - SETDBLQUOTE(); - USTPUTC(CTLQUOTEMARK, out); - } - continue; - } - CVTRACE(DBG_LEXER, magicq, (" MQignDQ ")); - if (magicq) - continue; - if (ISDBLQUOTE()) { - TS_POP(); - VTRACE(DBG_LEXER, - (" DQ TS_POP->%s CQE ", SYNTAX)); - USTPUTC(CTLQUOTEEND, out); - } else { - VTRACE(DBG_LEXER, - (" %s TS_POP->DQ CQM ", SYNTAX)); - TS_PUSH(); - syntax = DQSYNTAX; - SETDBLQUOTE(); - USTPUTC(CTLQUOTEMARK, out); - } - continue; - case CVAR: /* '$' */ - VTRACE(DBG_LEXER, ("'$'...")); - out = insert_elided_nl(out); - PARSESUB(); /* parse substitution */ - continue; - case CENDVAR: /* CLOSEBRACE */ - if (varnest > 0 && !ISDBLQUOTE()) { - VTRACE(DBG_LEXER, ("vn=%d !DQ", varnest)); - TS_POP(); - VTRACE(DBG_LEXER, (" TS_POP->%s CEV ", SYNTAX)); - USTPUTC(CTLENDVAR, out); - } else { - VTRACE(DBG_LEXER, ("'%c'", c)); - USTPUTC(c, out); - } - out = insert_elided_nl(out); - continue; - case CLP: /* '(' in arithmetic */ - parenlevel++; - VTRACE(DBG_LEXER, ("'('(%d)", parenlevel)); - USTPUTC(c, out); - continue;; - case CRP: /* ')' in arithmetic */ - if (parenlevel > 0) { - USTPUTC(c, out); - --parenlevel; - VTRACE(DBG_LEXER, ("')'(%d)", parenlevel)); - } else { - VTRACE(DBG_LEXER, ("')'(%d)", parenlevel)); - if (pgetc_linecont() == /*(*/ ')') { - out = insert_elided_nl(out); - if (--arinest == 0) { - TS_POP(); - USTPUTC(CTLENDARI, out); - } else - USTPUTC(/*(*/ ')', out); - } else { - break; /* to synerror() just below */ -#if 0 /* the old way, causes weird errors on bad input */ - /* - * unbalanced parens - * (don't 2nd guess - no error) - */ - pungetc(); - USTPUTC(/*(*/ ')', out); -#endif - } - } - continue; - case CBQUOTE: /* '`' */ - VTRACE(DBG_LEXER, ("'`' -> parsebackq()\n")); - out = parsebackq(stack, out, &bqlist, 1); - VTRACE(DBG_LEXER, ("parsebackq() -> readtoken1: ")); - continue; - case CEOF: /* --> c == PEOF */ - VTRACE(DBG_LEXER, ("EOF ")); - break; /* will exit loop */ - default: - VTRACE(DBG_LEXER, ("['%c'(%#.2x)]", c&0xFF, c&0x1FF)); - if (varnest == 0 && !ISDBLQUOTE()) - break; /* exit loop */ - USTPUTC(c, out); - VTRACE(DBG_LEXER, (",")); - continue; - } - VTRACE(DBG_LEXER, (" END TOKEN\n", c&0xFF, c&0x1FF)); - break; /* break from switch -> break from for loop too */ - } - - if (syntax == ARISYNTAX) { - cleanup_state_stack(stack); - synerror(/*((*/ "Missing '))'"); - } - if (syntax != BASESYNTAX && /* ! parsebackquote && */ !magicq) { - cleanup_state_stack(stack); - synerror("Unterminated quoted string"); - } - if (varnest != 0) { - cleanup_state_stack(stack); - startlinno = plinno; - /* { */ - synerror("Missing '}'"); - } - - STPUTC('\0', out); - len = out - stackblock(); - out = stackblock(); - - if (!magicq) { - if ((c == '<' || c == '>') - && quotef == 0 && (*out == '\0' || is_number(out))) { - parseredir(out, c); - cleanup_state_stack(stack); - return lasttoken = TREDIR; - } else { - pungetc(); - } - } - - VTRACE(DBG_PARSE|DBG_LEXER, - ("readtoken1 %sword \"%s\", completed%s (%d) left %d enl\n", - (quotef ? "quoted " : ""), out, (bqlist ? " with cmdsubs" : ""), - len, elided_nl)); - - quoteflag = quotef; - backquotelist = bqlist; - grabstackblock(len); - wordtext = out; - cleanup_state_stack(stack); - return lasttoken = TWORD; -/* end of readtoken routine */ - - -/* - * Parse a substitution. At this point, we have read the dollar sign - * and nothing else. - */ - -parsesub: { - int subtype; - int typeloc; - int flags; - char *p; - static const char types[] = "}-+?="; - - c = pgetc_linecont(); - VTRACE(DBG_LEXER, ("\"$%c\"(%#.2x)", c&0xFF, c&0x1FF)); - if (c == '(' /*)*/) { /* $(command) or $((arith)) */ - if (pgetc_linecont() == '(' /*')'*/ ) { - VTRACE(DBG_LEXER, ("\"$((\" ARITH ")); - out = insert_elided_nl(out); - PARSEARITH(); - } else { - VTRACE(DBG_LEXER, ("\"$(\" CSUB->parsebackq()\n")); - out = insert_elided_nl(out); - pungetc(); - out = parsebackq(stack, out, &bqlist, 0); - VTRACE(DBG_LEXER, ("parseback()->readtoken1(): ")); - } - } else if (c == OPENBRACE || is_name(c) || is_special(c)) { - VTRACE(DBG_LEXER, (" $EXP:CTLVAR ")); - USTPUTC(CTLVAR, out); - typeloc = out - stackblock(); - USTPUTC(VSNORMAL, out); - subtype = VSNORMAL; - flags = 0; - if (c == OPENBRACE) { - c = pgetc_linecont(); - if (c == '#') { - if ((c = pgetc_linecont()) == CLOSEBRACE) - c = '#'; - else if (is_name(c) || isdigit(c)) - subtype = VSLENGTH; - else if (is_special(c)) { - /* - * ${#} is $# - the number of sh params - * ${##} is the length of ${#} - * ${###} is ${#} with as much nothing - * as possible removed from start - * ${##1} is ${#} with leading 1 gone - * ${##\#} is ${#} with leading # gone - * - * this stuff is UGLY! - */ - if (pgetc_linecont() == CLOSEBRACE) { - pungetc(); - subtype = VSLENGTH; - } else { - static char cbuf[2]; - - pungetc(); /* would like 2 */ - cbuf[0] = c; /* so ... */ - cbuf[1] = '\0'; - pushstring(cbuf, 1, NULL); - c = '#'; /* ${#:...} */ - subtype = 0; /* .. or similar */ - } - } else { - pungetc(); - c = '#'; - subtype = 0; - } - } - else - subtype = 0; - VTRACE(DBG_LEXER, ("${ st=%d ", subtype)); - } - if (is_name(c)) { - p = out; - do { - VTRACE(DBG_LEXER, ("%c", c)); - STPUTC(c, out); - c = pgetc_linecont(); - } while (is_in_name(c)); - -#if 0 - if (out - p == 6 && strncmp(p, "LINENO", 6) == 0) { - int i; - int linno; - char buf[10]; - - /* - * The "LINENO hack" - * - * Replace the variable name with the - * current line number. - */ - linno = plinno; - if (funclinno != 0) - linno -= funclinno - 1; - snprintf(buf, sizeof(buf), "%d", linno); - STADJUST(-6, out); - for (i = 0; buf[i] != '\0'; i++) - STPUTC(buf[i], out); - flags |= VSLINENO; - } -#endif - } else if (is_digit(c)) { - do { - VTRACE(DBG_LEXER, ("%c", c)); - STPUTC(c, out); - c = pgetc_linecont(); - } while (subtype != VSNORMAL && is_digit(c)); - } - else if (is_special(c)) { - VTRACE(DBG_LEXER, ("\"$%c", c)); - USTPUTC(c, out); - c = pgetc_linecont(); - } - else { - VTRACE(DBG_LEXER, ("\"$%c(%#.2x)??\n", c&0xFF,c&0x1FF)); - badsub: - cleanup_state_stack(stack); - synerror("Bad substitution"); - } - - STPUTC('=', out); - if (subtype == 0) { - switch (c) { - case ':': - flags |= VSNUL; - c = pgetc_linecont(); - /*FALLTHROUGH*/ - default: - p = strchr(types, c); - if (p == NULL) - goto badsub; - subtype = p - types + VSNORMAL; - break; - case '%': - case '#': - { - int cc = c; - subtype = c == '#' ? VSTRIMLEFT : - VSTRIMRIGHT; - c = pgetc_linecont(); - if (c == cc) - subtype++; - else - pungetc(); - break; - } - } - } else { - if (subtype == VSLENGTH && c != /*{*/ '}') - synerror("no modifiers allowed with ${#var}"); - pungetc(); - } - if (quoted || arinest) - flags |= VSQUOTE; - if (subtype >= VSTRIMLEFT && subtype <= VSTRIMRIGHTMAX) - flags |= VSPATQ; - VTRACE(DBG_LEXER, (" st%d:%x", subtype, flags)); - *(stackblock() + typeloc) = subtype | flags; - if (subtype != VSNORMAL) { - TS_PUSH(); - varnest++; - arinest = 0; - if (subtype > VSASSIGN) { /* # ## % %% */ - syntax = BASESYNTAX; - quoted = 0; - magicq = 0; - } - VTRACE(DBG_LEXER, (" TS_PUSH->%s vn=%d%s ", - SYNTAX, varnest, quoted ? " Q" : "")); - } - } else if (c == '\'' && syntax == BASESYNTAX) { - USTPUTC(CTLQUOTEMARK, out); - VTRACE(DBG_LEXER, (" CSTR \"$'\" CQM ")); - CVTRACE(DBG_LEXER, quotef==0, ("QF=1 ")); - quotef = 1; - TS_PUSH(); - syntax = SQSYNTAX; - quoted = CQ; - VTRACE(DBG_LEXER, ("%s->TS_PUSH()->SQ ", SYNTAX)); - } else { - VTRACE(DBG_LEXER, ("$unk -> '$' (pushback '%c'%#.2x)", - c & 0xFF, c & 0x1FF)); - USTPUTC('$', out); - pungetc(); - } - goto parsesub_return; -} - - -/* - * Parse an arithmetic expansion (indicate start of one and set state) - */ -parsearith: { - -#if 0 - if (syntax == ARISYNTAX) { - /* - * we collapse embedded arithmetic expansion to - * parentheses, which should be equivalent - * - * XXX It isn't, must fix, soonish... - */ - USTPUTC('(' /*)*/, out); - USTPUTC('(' /*)*/, out); - /* - * Need 2 of them because there will (should be) - * two closing ))'s to follow later. - */ - parenlevel += 2; - } else -#endif - { - VTRACE(DBG_LEXER, (" CTLARI%c ", ISDBLQUOTE()?'"':'_')); - USTPUTC(CTLARI, out); - if (ISDBLQUOTE()) - USTPUTC('"',out); - else - USTPUTC(' ',out); - - VTRACE(DBG_LEXER, ("%s->TS_PUSH->ARI(1)", SYNTAX)); - TS_PUSH(); - syntax = ARISYNTAX; - arinest = 1; - varnest = 0; - magicq = 1; - } - goto parsearith_return; -} - -} /* end of readtoken */ - - - - -#ifdef mkinit -INCLUDE "parser.h" - -RESET { - psp.v_current_parser = &parse_state; - - parse_state.ps_tokpushback = 0; - parse_state.ps_checkkwd = 0; - parse_state.ps_heredoclist = NULL; -} -#endif - -/* - * Returns true if the text contains nothing to expand (no dollar signs - * or backquotes). - */ - -STATIC int -noexpand(char *text) -{ - char *p; - char c; - - p = text; - while ((c = *p++) != '\0') { - if (c == CTLQUOTEMARK || c == CTLQUOTEEND) - continue; - if (c == CTLESC) - p++; - else if (BASESYNTAX[(int)c] == CCTL) - return 0; - } - return 1; -} - - -/* - * Return true if the argument is a legal variable name (a letter or - * underscore followed by zero or more letters, underscores, and digits). - */ - -int -goodname(const char *name) -{ - const char *p; - - p = name; - if (! is_name(*p)) - return 0; - while (*++p) { - if (! is_in_name(*p)) - return 0; - } - return 1; -} - -int -isassignment(const char *p) -{ - if (!is_name(*p)) - return 0; - while (*++p != '=') - if (*p == '\0' || !is_in_name(*p)) - return 0; - return 1; -} - -/* - * skip past any \n's, and leave lasttoken set to whatever follows - */ -STATIC void -linebreak(void) -{ - while (readtoken() == TNL) - ; -} - -/* - * The next token must be "token" -- check, then move past it - */ -STATIC void -consumetoken(int token) -{ - if (readtoken() != token) { - VTRACE(DBG_PARSE, ("consumetoken(%d): expecting %s got %s", - token, tokname[token], tokname[lasttoken])); - CVTRACE(DBG_PARSE, (lasttoken==TWORD), (" \"%s\"", wordtext)); - VTRACE(DBG_PARSE, ("\n")); - synexpect(token, NULL); - } -} - -/* - * Called when an unexpected token is read during the parse. The argument - * is the token that is expected, or -1 if more than one type of token can - * occur at this point. - */ - -STATIC void -synexpect(int token, const char *text) -{ - char msg[64]; - char *p; - - if (lasttoken == TWORD) { - size_t len = strlen(wordtext); - - if (len <= 13) - fmtstr(msg, 34, "Word \"%.13s\" unexpected", wordtext); - else - fmtstr(msg, 34, - "Word \"%.10s...\" unexpected", wordtext); - } else - fmtstr(msg, 34, "%s unexpected", tokname[lasttoken]); - - p = strchr(msg, '\0'); - if (text) - fmtstr(p, 30, " (expecting \"%.10s\")", text); - else if (token >= 0) - fmtstr(p, 30, " (expecting %s)", tokname[token]); - - synerror(msg); - /* NOTREACHED */ -} - - -STATIC void -synerror(const char *msg) -{ - error("%d: Syntax error: %s", startlinno, msg); - /* NOTREACHED */ -} - -STATIC void -setprompt(int which) -{ - whichprompt = which; - -#ifndef SMALL - if (!el) -#endif - out2str(getprompt(NULL)); -} - -/* - * handle getting the next character, while ignoring \ \n - * (which is a little tricky as we only have one char of pushback - * and we need that one elsewhere). - */ -STATIC int -pgetc_linecont(void) -{ - int c; - - while ((c = pgetc()) == '\\') { - c = pgetc(); - if (c == '\n') { - plinno++; - elided_nl++; - VTRACE(DBG_LEXER, ("\"\\n\"drop(el=%d@%d)", - elided_nl, plinno)); - if (doprompt) - setprompt(2); - else - setprompt(0); - } else { - pungetc(); - /* Allow the backslash to be pushed back. */ - pushstring("\\", 1, NULL); - return (pgetc()); - } - } - return (c); -} - -/* - * called by editline -- any expansions to the prompt - * should be added here. - */ -const char * -getprompt(void *unused) -{ - char *p; - const char *cp; - int wp; - - if (!doprompt) - return ""; - - VTRACE(DBG_PARSE|DBG_EXPAND, ("getprompt %d\n", whichprompt)); - - switch (wp = whichprompt) { - case 0: - return ""; - case 1: - p = ps1val(); - break; - case 2: - p = ps2val(); - break; - default: - return "<internal prompt error>"; - } - if (p == NULL) - return ""; - - VTRACE(DBG_PARSE|DBG_EXPAND, ("prompt <<%s>>\n", p)); - - cp = expandstr(p, plinno); - whichprompt = wp; /* history depends on it not changing */ - - VTRACE(DBG_PARSE|DBG_EXPAND, ("prompt -> <<%s>>\n", cp)); - - return cp; -} - -/* - * Expand a string ... used for expanding prompts (PS1...) - * - * Never return NULL, always some string (return input string if invalid) - * - * The internal routine does the work, leaving the result on the - * stack (or in a static string, or even the input string) and - * handles parser recursion, and cleanup after an error while parsing. - * - * The visible interface copies the result off the stack (if it is there), - * and handles stack management, leaving the stack in the exact same - * state it was when expandstr() was called (so it can be used part way - * through building a stack data structure - as in when PS2 is being - * expanded half way through reading a "command line") - * - * on error, expandonstack() cleans up the parser state, but then - * simply jumps out through expandstr() withut doing any stack cleanup, - * which is OK, as the error handler must deal with that anyway. - * - * The split into two funcs is to avoid problems with setjmp/longjmp - * and local variables which could otherwise be optimised into bizarre - * behaviour. - */ -static const char * -expandonstack(char *ps, int cmdsub, int lineno) -{ - union node n; - struct jmploc jmploc; - struct jmploc *const savehandler = handler; - struct parsefile *const savetopfile = getcurrentfile(); - const int save_x = xflag; - struct parse_state new_state = init_parse_state; - struct parse_state *const saveparser = psp.v_current_parser; - const char *result = NULL; - - if (!setjmp(jmploc.loc)) { - handler = &jmploc; - - psp.v_current_parser = &new_state; - setinputstring(ps, 1, lineno); - - readtoken1(pgetc(), DQSYNTAX, 1); - if (backquotelist != NULL) { - if (!cmdsub) - result = ps; - else if (!promptcmds) - result = "-o promptcmds not set: "; - } - if (result == NULL) { - n.narg.type = NARG; - n.narg.next = NULL; - n.narg.text = wordtext; - n.narg.lineno = lineno; - n.narg.backquote = backquotelist; - - xflag = 0; /* we might be expanding PS4 ... */ - expandarg(&n, NULL, 0); - result = stackblock(); - } - } else { - psp.v_current_parser = saveparser; - xflag = save_x; - popfilesupto(savetopfile); - handler = savehandler; - - if (exception == EXEXIT) - longjmp(handler->loc, 1); - if (exception == EXINT) - exraise(SIGINT); - return ps; - } - psp.v_current_parser = saveparser; - xflag = save_x; - popfilesupto(savetopfile); - handler = savehandler; - - - if (result == NULL) - result = ps; - - return result; -} - -const char * -expandstr(char *ps, int lineno) -{ - const char *result = NULL; - struct stackmark smark; - static char *buffer = NULL; /* storage for prompt, never freed */ - static size_t bufferlen = 0; - - setstackmark(&smark); - /* - * At this point we anticipate that there may be a string - * growing on the stack, but we have no idea how big it is. - * However we know that it cannot be bigger than the current - * allocated stack block, so simply reserve the whole thing, - * then we can use the stack without barfing all over what - * is there already... (the stack mark undoes this later.) - */ - (void) stalloc(stackblocksize()); - - result = expandonstack(ps, 1, lineno); - - if (__predict_true(result == stackblock())) { - size_t len = strlen(result) + 1; - - /* - * the result (usual case) is on the stack, which we - * are just about to discard (popstackmark()) so we - * need to move it somewhere safe first. - */ - - if (__predict_false(len > bufferlen)) { - char *new; - size_t newlen = bufferlen; - - if (__predict_false(len > (SIZE_MAX >> 4))) { - result = "huge prompt: "; - goto getout; - } - - if (newlen == 0) - newlen = 32; - while (newlen <= len) - newlen <<= 1; - - new = (char *)realloc(buffer, newlen); - - if (__predict_false(new == NULL)) { - /* - * this should rarely (if ever) happen - * but we must do something when it does... - */ - result = "No mem for prompt: "; - goto getout; - } else { - buffer = new; - bufferlen = newlen; - } - } - (void)memcpy(buffer, result, len); - result = buffer; - } - - getout:; - popstackmark(&smark); - - return result; -} - -/* - * and a simpler version, which does no $( ) expansions, for - * use during shell startup when we know we are not parsing, - * and so the stack is not in use - we can do what we like, - * and do not need to clean up (that's handled externally). - * - * Simply return the result, even if it is on the stack - */ -const char * -expandenv(char *arg) -{ - return expandonstack(arg, 0, 0); -} diff --git a/bin/sh/parser.h b/bin/sh/parser.h deleted file mode 100644 index 7545b4f..0000000 --- a/bin/sh/parser.h +++ /dev/null @@ -1,169 +0,0 @@ -/* $NetBSD: parser.h,v 1.27 2018/12/11 13:31:20 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)parser.h 8.3 (Berkeley) 5/4/95 - */ - -/* control characters in argument strings */ -#define CTL_FIRST '\201' /* first 'special' character */ -#define CTLESC '\201' /* escape next character */ -#define CTLVAR '\202' /* variable defn */ -#define CTLENDVAR '\203' -#define CTLBACKQ '\204' -#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ -/* CTLBACKQ | CTLQUOTE == '\205' */ -#define CTLARI '\206' /* arithmetic expression */ -#define CTLENDARI '\207' -#define CTLQUOTEMARK '\210' -#define CTLQUOTEEND '\211' /* only inside ${...} */ -#define CTLNONL '\212' /* The \n in a deleted \ \n sequence */ - /* pure concidence that (CTLNONL & 0x7f) == '\n' */ -#define CTLCNL '\213' /* A $'\n' - newline not counted */ -#define CTL_LAST '\213' /* last 'special' character */ - -/* variable substitution byte (follows CTLVAR) */ -#define VSTYPE 0x0f /* type of variable substitution */ -#define VSNUL 0x10 /* colon--treat the empty string as unset */ -#define VSLINENO 0x20 /* expansion of $LINENO, the line number - follows immediately */ -#define VSPATQ 0x40 /* ensure correct pattern quoting in ${x#pat} */ -#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ - -/* values of VSTYPE field */ -#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ -#define VSMINUS 0x2 /* ${var-text} */ -#define VSPLUS 0x3 /* ${var+text} */ -#define VSQUESTION 0x4 /* ${var?message} */ -#define VSASSIGN 0x5 /* ${var=text} */ -#define VSTRIMLEFT 0x6 /* ${var#pattern} */ -#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */ -#define VSTRIMRIGHT 0x8 /* ${var%pattern} */ -#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */ -#define VSLENGTH 0xa /* ${#var} */ - -union node *parsecmd(int); -void fixredir(union node *, const char *, int); -int goodname(const char *); -int isassignment(const char *); -const char *getprompt(void *); -const char *expandstr(char *, int); -const char *expandenv(char *); - -struct HereDoc; -union node; -struct nodelist; - -struct parse_state { - struct HereDoc *ps_heredoclist; /* list of here documents to read */ - int ps_parsebackquote; /* nonzero inside backquotes */ - int ps_doprompt; /* if set, prompt the user */ - int ps_needprompt; /* true if interactive at line start */ - int ps_lasttoken; /* last token read */ - int ps_tokpushback; /* last token pushed back */ - char *ps_wordtext; /* text of last word returned by readtoken */ - int ps_checkkwd; /* word expansion flags, see below */ - struct nodelist *ps_backquotelist; /* list of cmdsubs to process */ - union node *ps_redirnode; /* node for current redirect */ - struct HereDoc *ps_heredoc; /* current heredoc << beign parsed */ - int ps_quoteflag; /* set if (part) of token was quoted */ - int ps_startlinno; /* line # where last token started */ - int ps_funclinno; /* line # of the current function */ - int ps_elided_nl; /* count of \ \n pairs we have seen */ -}; - -/* - * The parser references the elements of struct parse_state quite - * frequently - they used to be simple globals, so one memory ref - * per access, adding an indirect through global ptr would not be - * nice. The following gross hack allows most of that cost to be - * avoided, by allowing the compiler to understand that the global - * pointer is in fact constant in any function, and so its value can - * be cached, rather than needing to be fetched every time in case - * some other called function has changed it. - * - * The rule to make this work is that any function that wants - * to alter the global must restore it before it returns (and thus - * must have an error trap handler). That means that the struct - * used for the new parser state can be a local in that function's - * stack frame, it never needs to be malloc'd. - */ - -union parse_state_p { - struct parse_state *const c_current_parser; - struct parse_state * v_current_parser; -}; - -extern union parse_state_p psp; - -#define current_parser (psp.c_current_parser) - -/* - * Perhaps one day emulate "static" by moving most of these definitions into - * parser.c ... (only checkkwd & tokpushback are used outside parser.c, - * and only in init.c as a RESET activity) - */ -#define tokpushback (current_parser->ps_tokpushback) -#define checkkwd (current_parser->ps_checkkwd) - -#define noalias (current_parser->ps_noalias) -#define heredoclist (current_parser->ps_heredoclist) -#define parsebackquote (current_parser->ps_parsebackquote) -#define doprompt (current_parser->ps_doprompt) -#define needprompt (current_parser->ps_needprompt) -#define lasttoken (current_parser->ps_lasttoken) -#define wordtext (current_parser->ps_wordtext) -#define backquotelist (current_parser->ps_backquotelist) -#define redirnode (current_parser->ps_redirnode) -#define heredoc (current_parser->ps_heredoc) -#define quoteflag (current_parser->ps_quoteflag) -#define startlinno (current_parser->ps_startlinno) -#define funclinno (current_parser->ps_funclinno) -#define elided_nl (current_parser->ps_elided_nl) - -/* - * Values that can be set in checkkwd - */ -#define CHKKWD 0x01 /* turn word into keyword (if it is) */ -#define CHKNL 0x02 /* ignore leading \n's */ -#define CHKALIAS 0x04 /* lookup words as aliases and ... */ - -/* - * NEOF is returned by parsecmd when it encounters an end of file. It - * must be distinct from NULL, so we use the address of a variable that - * happens to be handy. - */ -#define NEOF ((union node *)&psp) - -#ifdef DEBUG -extern int parsing; -#endif diff --git a/bin/sh/redir.c b/bin/sh/redir.c deleted file mode 100644 index 751cce7..0000000 --- a/bin/sh/redir.c +++ /dev/null @@ -1,982 +0,0 @@ -/* $NetBSD: redir.c,v 1.62 2018/11/26 20:03:39 kamil Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: redir.c,v 1.62 2018/11/26 20:03:39 kamil Exp $"); -#endif -#endif /* not lint */ - -#include <sys/types.h> -#include <sys/param.h> /* PIPE_BUF */ -#include <sys/stat.h> -#include <signal.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> -#include <unistd.h> -#include <stdlib.h> - -/* - * Code for dealing with input/output redirection. - */ - -#include "main.h" -#include "builtins.h" -#include "shell.h" -#include "nodes.h" -#include "jobs.h" -#include "options.h" -#include "expand.h" -#include "redir.h" -#include "output.h" -#include "memalloc.h" -#include "mystring.h" -#include "error.h" -#include "show.h" - - -#define EMPTY -2 /* marks an unused slot in redirtab */ -#define CLOSED -1 /* fd was not open before redir */ -#ifndef PIPE_BUF -# define PIPESIZE 4096 /* amount of buffering in a pipe */ -#else -# define PIPESIZE PIPE_BUF -#endif - - -MKINIT -struct renamelist { - struct renamelist *next; - int orig; - int into; -}; - -MKINIT -struct redirtab { - struct redirtab *next; - struct renamelist *renamed; -}; - - -MKINIT struct redirtab *redirlist; - -/* - * We keep track of whether or not fd0 has been redirected. This is for - * background commands, where we want to redirect fd0 to /dev/null only - * if it hasn't already been redirected. - */ -STATIC int fd0_redirected = 0; - -/* - * And also where to put internal use fds that should be out of the - * way of user defined fds (normally) - */ -STATIC int big_sh_fd = 0; - -STATIC const struct renamelist *is_renamed(const struct renamelist *, int); -STATIC void fd_rename(struct redirtab *, int, int); -STATIC void free_rl(struct redirtab *, int); -STATIC void openredirect(union node *, char[10], int); -STATIC int openhere(const union node *); -STATIC int copyfd(int, int, int); -STATIC void find_big_fd(void); - - -struct shell_fds { /* keep track of internal shell fds */ - struct shell_fds *nxt; - void (*cb)(int, int); - int fd; -}; - -STATIC struct shell_fds *sh_fd_list; - -STATIC void renumber_sh_fd(struct shell_fds *); -STATIC struct shell_fds *sh_fd(int); - -STATIC const struct renamelist * -is_renamed(const struct renamelist *rl, int fd) -{ - while (rl != NULL) { - if (rl->orig == fd) - return rl; - rl = rl->next; - } - return NULL; -} - -STATIC void -free_rl(struct redirtab *rt, int reset) -{ - struct renamelist *rl, *rn = rt->renamed; - - while ((rl = rn) != NULL) { - rn = rl->next; - if (rl->orig == 0) - fd0_redirected--; - VTRACE(DBG_REDIR, ("popredir %d%s: %s", - rl->orig, rl->orig==0 ? " (STDIN)" : "", - reset ? "" : "no reset\n")); - if (reset) { - if (rl->into < 0) { - VTRACE(DBG_REDIR, ("closed\n")); - close(rl->orig); - } else { - VTRACE(DBG_REDIR, ("from %d\n", rl->into)); - movefd(rl->into, rl->orig); - } - } - ckfree(rl); - } - rt->renamed = NULL; -} - -STATIC void -fd_rename(struct redirtab *rt, int from, int to) -{ - /* XXX someday keep a short list (8..10) of freed renamelists XXX */ - struct renamelist *rl = ckmalloc(sizeof(struct renamelist)); - - rl->next = rt->renamed; - rt->renamed = rl; - - rl->orig = from; - rl->into = to; -} - -/* - * Process a list of redirection commands. If the REDIR_PUSH flag is set, - * old file descriptors are stashed away so that the redirection can be - * undone by calling popredir. If the REDIR_BACKQ flag is set, then the - * standard output, and the standard error if it becomes a duplicate of - * stdout, is saved in memory. - */ - -void -redirect(union node *redir, int flags) -{ - union node *n; - struct redirtab *sv = NULL; - int i; - int fd; - char memory[10]; /* file descriptors to write to memory */ - - CTRACE(DBG_REDIR, ("redirect(F=0x%x):%s\n", flags, redir?"":" NONE")); - for (i = 10 ; --i >= 0 ; ) - memory[i] = 0; - memory[1] = flags & REDIR_BACKQ; - if (flags & REDIR_PUSH) { - /* - * We don't have to worry about REDIR_VFORK here, as - * flags & REDIR_PUSH is never true if REDIR_VFORK is set. - */ - sv = ckmalloc(sizeof (struct redirtab)); - sv->renamed = NULL; - sv->next = redirlist; - redirlist = sv; - } - for (n = redir ; n ; n = n->nfile.next) { - fd = n->nfile.fd; - VTRACE(DBG_REDIR, ("redir %d (max=%d) ", fd, max_user_fd)); - if (fd > max_user_fd) - max_user_fd = fd; - renumber_sh_fd(sh_fd(fd)); - if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && - n->ndup.dupfd == fd) { - /* redirect from/to same file descriptor */ - /* make sure it stays open */ - if (fcntl(fd, F_SETFD, 0) < 0) - error("fd %d: %s", fd, strerror(errno)); - VTRACE(DBG_REDIR, ("!cloexec\n")); - continue; - } - - if ((flags & REDIR_PUSH) && !is_renamed(sv->renamed, fd)) { - INTOFF; - if (big_sh_fd < 10) - find_big_fd(); - if ((i = fcntl(fd, F_DUPFD, big_sh_fd)) == -1) { - switch (errno) { - case EBADF: - i = CLOSED; - break; - case EMFILE: - case EINVAL: - find_big_fd(); - i = fcntl(fd, F_DUPFD, big_sh_fd); - if (i >= 0) - break; - /* FALLTHRU */ - default: - i = errno; - INTON; /* XXX not needed here ? */ - error("%d: %s", fd, strerror(i)); - /* NOTREACHED */ - } - } - if (i >= 0) - (void)fcntl(i, F_SETFD, FD_CLOEXEC); - fd_rename(sv, fd, i); - VTRACE(DBG_REDIR, ("saved as %d ", i)); - INTON; - } - VTRACE(DBG_REDIR, ("%s\n", fd == 0 ? "STDIN" : "")); - if (fd == 0) - fd0_redirected++; - openredirect(n, memory, flags); - } - if (memory[1]) - out1 = &memout; - if (memory[2]) - out2 = &memout; -} - - -STATIC void -openredirect(union node *redir, char memory[10], int flags) -{ - struct stat sb; - int fd = redir->nfile.fd; - char *fname; - int f; - int eflags, cloexec; - - /* - * We suppress interrupts so that we won't leave open file - * descriptors around. This may not be such a good idea because - * an open of a device or a fifo can block indefinitely. - */ - INTOFF; - if (fd < 10) - memory[fd] = 0; - switch (redir->nfile.type) { - case NFROM: - fname = redir->nfile.expfname; - if (flags & REDIR_VFORK) - eflags = O_NONBLOCK; - else - eflags = 0; - if ((f = open(fname, O_RDONLY|eflags)) < 0) - goto eopen; - VTRACE(DBG_REDIR, ("openredirect(< '%s') -> %d [%#x]", - fname, f, eflags)); - if (eflags) - (void)fcntl(f, F_SETFL, fcntl(f, F_GETFL, 0) & ~eflags); - break; - case NFROMTO: - fname = redir->nfile.expfname; - if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0) - goto ecreate; - VTRACE(DBG_REDIR, ("openredirect(<> '%s') -> %d", fname, f)); - break; - case NTO: - if (Cflag) { - fname = redir->nfile.expfname; - if ((f = open(fname, O_WRONLY)) == -1) { - if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL, - 0666)) < 0) - goto ecreate; - } else if (fstat(f, &sb) == -1) { - int serrno = errno; - close(f); - errno = serrno; - goto ecreate; - } else if (S_ISREG(sb.st_mode)) { - close(f); - errno = EEXIST; - goto ecreate; - } - VTRACE(DBG_REDIR, ("openredirect(>| '%s') -> %d", - fname, f)); - break; - } - /* FALLTHROUGH */ - case NCLOBBER: - fname = redir->nfile.expfname; - if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) - goto ecreate; - VTRACE(DBG_REDIR, ("openredirect(> '%s') -> %d", fname, f)); - break; - case NAPPEND: - fname = redir->nfile.expfname; - if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) - goto ecreate; - VTRACE(DBG_REDIR, ("openredirect(>> '%s') -> %d", fname, f)); - break; - case NTOFD: - case NFROMFD: - if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ - if (fd < 10 && redir->ndup.dupfd < 10 && - memory[redir->ndup.dupfd]) - memory[fd] = 1; - else if (copyfd(redir->ndup.dupfd, fd, - (flags & REDIR_KEEP) == 0) < 0) - error("Redirect (from %d to %d) failed: %s", - redir->ndup.dupfd, fd, strerror(errno)); - VTRACE(DBG_REDIR, ("openredirect: %d%c&%d\n", fd, - "<>"[redir->nfile.type==NTOFD], redir->ndup.dupfd)); - } else { - (void) close(fd); - VTRACE(DBG_REDIR, ("openredirect: %d%c&-\n", fd, - "<>"[redir->nfile.type==NTOFD])); - } - INTON; - return; - case NHERE: - case NXHERE: - VTRACE(DBG_REDIR, ("openredirect: %d<<...", fd)); - f = openhere(redir); - break; - default: - abort(); - } - - cloexec = fd > 2 && (flags & REDIR_KEEP) == 0 && !posix; - if (f != fd) { - VTRACE(DBG_REDIR, (" -> %d", fd)); - if (copyfd(f, fd, cloexec) < 0) { - int e = errno; - - close(f); - error("redirect reassignment (fd %d) failed: %s", fd, - strerror(e)); - } - close(f); - } else if (cloexec) - (void)fcntl(f, F_SETFD, FD_CLOEXEC); - VTRACE(DBG_REDIR, ("%s\n", cloexec ? " cloexec" : "")); - - INTON; - return; - ecreate: - exerrno = 1; - error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); - eopen: - exerrno = 1; - error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); -} - - -/* - * Handle here documents. Normally we fork off a process to write the - * data to a pipe. If the document is short, we can stuff the data in - * the pipe without forking. - */ - -STATIC int -openhere(const union node *redir) -{ - int pip[2]; - int len = 0; - - if (pipe(pip) < 0) - error("Pipe call failed"); - if (redir->type == NHERE) { - len = strlen(redir->nhere.doc->narg.text); - if (len <= PIPESIZE) { - xwrite(pip[1], redir->nhere.doc->narg.text, len); - goto out; - } - } - VTRACE(DBG_REDIR, (" forking [%d,%d]\n", pip[0], pip[1])); - if (forkshell(NULL, NULL, FORK_NOJOB) == 0) { - close(pip[0]); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - signal(SIGHUP, SIG_IGN); -#ifdef SIGTSTP - signal(SIGTSTP, SIG_IGN); -#endif - signal(SIGPIPE, SIG_DFL); - if (redir->type == NHERE) - xwrite(pip[1], redir->nhere.doc->narg.text, len); - else - expandhere(redir->nhere.doc, pip[1]); - _exit(0); - } - VTRACE(DBG_REDIR, ("openhere (closing %d)", pip[1])); - out: - close(pip[1]); - VTRACE(DBG_REDIR, (" (pipe fd=%d)", pip[0])); - return pip[0]; -} - - - -/* - * Undo the effects of the last redirection. - */ - -void -popredir(void) -{ - struct redirtab *rp = redirlist; - - INTOFF; - free_rl(rp, 1); - redirlist = rp->next; - ckfree(rp); - INTON; -} - -/* - * Undo all redirections. Called on error or interrupt. - */ - -#ifdef mkinit - -INCLUDE "redir.h" - -RESET { - while (redirlist) - popredir(); -} - -SHELLPROC { - clearredir(0); -} - -#endif - -/* Return true if fd 0 has already been redirected at least once. */ -int -fd0_redirected_p(void) -{ - return fd0_redirected != 0; -} - -/* - * Discard all saved file descriptors. - */ - -void -clearredir(int vforked) -{ - struct redirtab *rp; - struct renamelist *rl; - - for (rp = redirlist ; rp ; rp = rp->next) { - if (!vforked) - free_rl(rp, 0); - else for (rl = rp->renamed; rl; rl = rl->next) - if (rl->into >= 0) - close(rl->into); - } -} - - - -/* - * Copy a file descriptor to be == to. - * cloexec indicates if we want close-on-exec or not. - * Returns -1 if any error occurs. - */ - -STATIC int -copyfd(int from, int to, int cloexec) -{ - int newfd; - - if (cloexec && to > 2) - newfd = dup3(from, to, O_CLOEXEC); - else - newfd = dup2(from, to); - - return newfd; -} - -/* - * rename fd from to be fd to (closing from). - * close-on-exec is never set on 'to' (unless - * from==to and it was set on from) - ie: a no-op - * returns to (or errors() if an error occurs). - * - * This is mostly used for rearranging the - * results from pipe(). - */ -int -movefd(int from, int to) -{ - if (from == to) - return to; - - (void) close(to); - if (copyfd(from, to, 0) != to) { - int e = errno; - - (void) close(from); - error("Unable to make fd %d: %s", to, strerror(e)); - } - (void) close(from); - - return to; -} - -STATIC void -find_big_fd(void) -{ - int i, fd; - static int last_start = 3; /* aim to keep sh fd's under 20 */ - - if (last_start < 10) - last_start++; - - for (i = (1 << last_start); i >= 10; i >>= 1) { - if ((fd = fcntl(0, F_DUPFD, i - 1)) >= 0) { - close(fd); - break; - } - } - - fd = (i / 5) * 4; - if (fd < 10) - fd = 10; - - big_sh_fd = fd; -} - -/* - * If possible, move file descriptor fd out of the way - * of expected user fd values. Returns the new fd - * (which may be the input fd if things do not go well.) - * Always set close-on-exec on the result, and close - * the input fd unless it is to be our result. - */ -int -to_upper_fd(int fd) -{ - int i; - - VTRACE(DBG_REDIR|DBG_OUTPUT, ("to_upper_fd(%d)", fd)); - if (big_sh_fd < 10) - find_big_fd(); - do { - i = fcntl(fd, F_DUPFD_CLOEXEC, big_sh_fd); - if (i >= 0) { - if (fd != i) - close(fd); - VTRACE(DBG_REDIR|DBG_OUTPUT, ("-> %d\n", i)); - return i; - } - if (errno != EMFILE && errno != EINVAL) - break; - find_big_fd(); - } while (big_sh_fd > 10); - - /* - * If we wanted to move this fd to some random high number - * we certainly do not intend to pass it through exec, even - * if the reassignment failed. - */ - (void)fcntl(fd, F_SETFD, FD_CLOEXEC); - VTRACE(DBG_REDIR|DBG_OUTPUT, (" fails ->%d\n", fd)); - return fd; -} - -void -register_sh_fd(int fd, void (*cb)(int, int)) -{ - struct shell_fds *fp; - - fp = ckmalloc(sizeof (struct shell_fds)); - if (fp != NULL) { - fp->nxt = sh_fd_list; - sh_fd_list = fp; - - fp->fd = fd; - fp->cb = cb; - } -} - -void -sh_close(int fd) -{ - struct shell_fds **fpp, *fp; - - fpp = &sh_fd_list; - while ((fp = *fpp) != NULL) { - if (fp->fd == fd) { - *fpp = fp->nxt; - ckfree(fp); - break; - } - fpp = &fp->nxt; - } - (void)close(fd); -} - -STATIC struct shell_fds * -sh_fd(int fd) -{ - struct shell_fds *fp; - - for (fp = sh_fd_list; fp != NULL; fp = fp->nxt) - if (fp->fd == fd) - return fp; - return NULL; -} - -STATIC void -renumber_sh_fd(struct shell_fds *fp) -{ - int to; - - if (fp == NULL) - return; - -#ifndef F_DUPFD_CLOEXEC -#define F_DUPFD_CLOEXEC F_DUPFD -#define CLOEXEC(fd) (fcntl((fd), F_SETFD, fcntl((fd),F_GETFD) | FD_CLOEXEC)) -#else -#define CLOEXEC(fd) -#endif - - /* - * if we have had a collision, and the sh fd was a "big" one - * try moving the sh fd base to a higher number (if possible) - * so future sh fds are less likely to be in the user's sights - * (incl this one when moved) - */ - if (fp->fd >= big_sh_fd) - find_big_fd(); - - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd); - if (to == -1) - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, big_sh_fd/2); - if (to == -1) - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, fp->fd + 1); - if (to == -1) - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 10); - if (to == -1) - to = fcntl(fp->fd, F_DUPFD_CLOEXEC, 3); - if (to == -1) - error("insufficient file descriptors available"); - CLOEXEC(to); - - if (fp->fd == to) /* impossible? */ - return; - - (*fp->cb)(fp->fd, to); - (void)close(fp->fd); - fp->fd = to; -} - -static const struct flgnames { - const char *name; - uint16_t minch; - uint32_t value; -} nv[] = { -#ifdef O_APPEND - { "append", 2, O_APPEND }, -#endif -#ifdef O_ASYNC - { "async", 2, O_ASYNC }, -#endif -#ifdef O_SYNC - { "sync", 2, O_SYNC }, -#endif -#ifdef O_NONBLOCK - { "nonblock", 3, O_NONBLOCK }, -#endif -#ifdef O_FSYNC - { "fsync", 2, O_FSYNC }, -#endif -#ifdef O_DSYNC - { "dsync", 2, O_DSYNC }, -#endif -#ifdef O_RSYNC - { "rsync", 2, O_RSYNC }, -#endif -#ifdef O_ALT_IO - { "altio", 2, O_ALT_IO }, -#endif -#ifdef O_DIRECT - { "direct", 2, O_DIRECT }, -#endif -#ifdef O_NOSIGPIPE - { "nosigpipe", 3, O_NOSIGPIPE }, -#endif -#ifdef O_CLOEXEC - { "cloexec", 2, O_CLOEXEC }, -#endif - { 0, 0, 0 } -}; -#define ALLFLAGS (O_APPEND|O_ASYNC|O_SYNC|O_NONBLOCK|O_DSYNC|O_RSYNC|\ - O_ALT_IO|O_DIRECT|O_NOSIGPIPE|O_CLOEXEC) - -static int -getflags(int fd, int p) -{ - int c, f; - - if (sh_fd(fd) != NULL) { - if (!p) - return -1; - error("Can't get status for fd=%d (%s)", fd, - "Bad file descriptor"); /*XXX*/ - } - - if ((c = fcntl(fd, F_GETFD)) == -1) { - if (!p) - return -1; - error("Can't get status for fd=%d (%s)", fd, strerror(errno)); - } - if ((f = fcntl(fd, F_GETFL)) == -1) { - if (!p) - return -1; - error("Can't get flags for fd=%d (%s)", fd, strerror(errno)); - } - if (c & FD_CLOEXEC) - f |= O_CLOEXEC; - return f & ALLFLAGS; -} - -static void -printone(int fd, int p, int verbose, int pfd) -{ - int f = getflags(fd, p); - const struct flgnames *fn; - - if (f == -1) - return; - - if (pfd) - outfmt(out1, "%d: ", fd); - for (fn = nv; fn->name; fn++) { - if (f & fn->value) { - outfmt(out1, "%s%s", verbose ? "+" : "", fn->name); - f &= ~fn->value; - } else if (verbose) - outfmt(out1, "-%s", fn->name); - else - continue; - if (f || (verbose && fn[1].name)) - outfmt(out1, ","); - } - if (verbose && f) /* f should be normally be 0 */ - outfmt(out1, " +%#x", f); - outfmt(out1, "\n"); -} - -static void -parseflags(char *s, int *p, int *n) -{ - int *v, *w; - const struct flgnames *fn; - size_t len; - - *p = 0; - *n = 0; - for (s = strtok(s, ","); s; s = strtok(NULL, ",")) { - switch (*s++) { - case '+': - v = p; - w = n; - break; - case '-': - v = n; - w = p; - break; - default: - error("Missing +/- indicator before flag %s", s-1); - } - - len = strlen(s); - for (fn = nv; fn->name; fn++) - if (len >= fn->minch && strncmp(s,fn->name,len) == 0) { - *v |= fn->value; - *w &=~ fn->value; - break; - } - if (fn->name == 0) - error("Bad flag `%s'", s); - } -} - -static void -setone(int fd, int pos, int neg, int verbose) -{ - int f = getflags(fd, 1); - int n, cloexec; - - if (f == -1) - return; - - cloexec = -1; - if ((pos & O_CLOEXEC) && !(f & O_CLOEXEC)) - cloexec = FD_CLOEXEC; - if ((neg & O_CLOEXEC) && (f & O_CLOEXEC)) - cloexec = 0; - - if (cloexec != -1 && fcntl(fd, F_SETFD, cloexec) == -1) - error("Can't set status for fd=%d (%s)", fd, strerror(errno)); - - pos &= ~O_CLOEXEC; - neg &= ~O_CLOEXEC; - f &= ~O_CLOEXEC; - n = f; - n |= pos; - n &= ~neg; - if (n != f && fcntl(fd, F_SETFL, n) == -1) - error("Can't set flags for fd=%d (%s)", fd, strerror(errno)); - if (verbose) - printone(fd, 1, verbose, 1); -} - -int -fdflagscmd(int argc, char *argv[]) -{ - char *num; - int verbose = 0, ch, pos = 0, neg = 0; - char *setflags = NULL; - - optreset = 1; optind = 1; /* initialize getopt */ - while ((ch = getopt(argc, argv, ":vs:")) != -1) - switch ((char)ch) { - case 'v': - verbose = 1; - break; - case 's': - if (setflags) - goto msg; - setflags = optarg; - break; - case '?': - default: - msg: - error("Usage: fdflags [-v] [-s <flags> fd] [fd...]"); - /* NOTREACHED */ - } - - argc -= optind, argv += optind; - - if (setflags) - parseflags(setflags, &pos, &neg); - - if (argc == 0) { - int i; - - if (setflags) - goto msg; - - for (i = 0; i <= max_user_fd; i++) - printone(i, 0, verbose, 1); - return 0; - } - - while ((num = *argv++) != NULL) { - int fd = number(num); - - while (num[0] == '0' && num[1] != '\0') /* skip 0's */ - num++; - if (strlen(num) > 5) - error("%s too big to be a file descriptor", num); - - if (setflags) - setone(fd, pos, neg, verbose); - else - printone(fd, 1, verbose, argc > 1); - } - return 0; -} - -#undef MAX /* in case we inherited them from somewhere */ -#undef MIN - -#define MIN(a,b) (/*CONSTCOND*/((a)<=(b)) ? (a) : (b)) -#define MAX(a,b) (/*CONSTCOND*/((a)>=(b)) ? (a) : (b)) - - /* now make the compiler work for us... */ -#define MIN_REDIR MIN(MIN(MIN(MIN(NTO,NFROM), MIN(NTOFD,NFROMFD)), \ - MIN(MIN(NCLOBBER,NAPPEND), MIN(NHERE,NXHERE))), NFROMTO) -#define MAX_REDIR MAX(MAX(MAX(MAX(NTO,NFROM), MAX(NTOFD,NFROMFD)), \ - MAX(MAX(NCLOBBER,NAPPEND), MAX(NHERE,NXHERE))), NFROMTO) - -static const char *redir_sym[MAX_REDIR - MIN_REDIR + 1] = { - [NTO - MIN_REDIR]= ">", - [NFROM - MIN_REDIR]= "<", - [NTOFD - MIN_REDIR]= ">&", - [NFROMFD - MIN_REDIR]= "<&", - [NCLOBBER - MIN_REDIR]= ">|", - [NAPPEND - MIN_REDIR]= ">>", - [NHERE - MIN_REDIR]= "<<", - [NXHERE - MIN_REDIR]= "<<", - [NFROMTO - MIN_REDIR]= "<>", -}; - -int -outredir(struct output *out, union node *n, int sep) -{ - if (n == NULL) - return 0; - if (n->type < MIN_REDIR || n->type > MAX_REDIR || - redir_sym[n->type - MIN_REDIR] == NULL) - return 0; - - if (sep) - outc(sep, out); - - /* - * ugly, but all redir node types have "fd" in same slot... - * (and code other places assumes it as well) - */ - if ((redir_sym[n->type - MIN_REDIR][0] == '<' && n->nfile.fd != 0) || - (redir_sym[n->type - MIN_REDIR][0] == '>' && n->nfile.fd != 1)) - outfmt(out, "%d", n->nfile.fd); - - outstr(redir_sym[n->type - MIN_REDIR], out); - - switch (n->type) { - case NHERE: - outstr("'...'", out); - break; - case NXHERE: - outstr("...", out); - break; - case NTOFD: - case NFROMFD: - if (n->ndup.dupfd < 0) - outc('-', out); - else - outfmt(out, "%d", n->ndup.dupfd); - break; - default: - outstr(n->nfile.expfname, out); - break; - } - return 1; -} diff --git a/bin/sh/redir.h b/bin/sh/redir.h deleted file mode 100644 index b186bb4..0000000 --- a/bin/sh/redir.h +++ /dev/null @@ -1,55 +0,0 @@ -/* $NetBSD: redir.h,v 1.24 2017/06/30 23:01:21 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)redir.h 8.2 (Berkeley) 5/4/95 - */ - -/* flags passed to redirect */ -#define REDIR_PUSH 0x01 /* save previous values of file descriptors */ -#define REDIR_BACKQ 0x02 /* save the command output in memory */ -#define REDIR_VFORK 0x04 /* running under vfork(2), be careful */ -#define REDIR_KEEP 0x08 /* don't close-on-exec */ - -union node; -void redirect(union node *, int); -void popredir(void); -int fd0_redirected_p(void); -void clearredir(int); -int movefd(int, int); -int to_upper_fd(int); -void register_sh_fd(int, void (*)(int, int)); -void sh_close(int); -struct output; -int outredir(struct output *, union node *, int); - -int max_user_fd; /* highest fd used by user */ diff --git a/bin/sh/sh.1 b/bin/sh/sh.1 deleted file mode 100644 index 4630689..0000000 --- a/bin/sh/sh.1 +++ /dev/null @@ -1,4549 +0,0 @@ -.\" $NetBSD: sh.1,v 1.217 2019/01/21 14:09:24 kre Exp $ -.\" Copyright (c) 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" This code is derived from software contributed to Berkeley by -.\" Kenneth Almquist. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)sh.1 8.6 (Berkeley) 5/4/95 -.\" -.Dd December 12, 2018 -.Dt SH 1 -.\" everything except c o and s (keep them ordered) -.ds flags abCEeFfhIiLmnpquVvXx -.Os -.Sh NAME -.Nm sh -.Nd command interpreter (shell) -.Sh SYNOPSIS -.Nm -.Bk -words -.Op Fl \*[flags] -.Op Cm +\*[flags] -.Ek -.Bk -words -.Op Fl o Ar option_name -.Op Cm +o Ar option_name -.Ek -.Bk -words -.Op Ar command_file Op Ar argument ... -.Ek -.Nm -.Fl c -.Bk -words -.Op Fl s -.Op Fl \*[flags] -.Op Cm +\*[flags] -.Ek -.Bk -words -.Op Fl o Ar option_name -.Op Cm +o Ar option_name -.Ek -.Bk -words -.Ar command_string -.Op Ar command_name Op Ar argument ... -.Ek -.Nm -.Fl s -.Bk -words -.Op Fl \*[flags] -.Op Cm +\*[flags] -.Ek -.Bk -words -.Op Fl o Ar option_name -.Op Cm +o Ar option_name -.Ek -.Bk -words -.Op Ar argument ... -.Ek -.Sh DESCRIPTION -.Nm -is the standard command interpreter for the system. -The current version of -.Nm -is in the process of being changed to conform more closely to the -POSIX 1003.2 and 1003.2a specifications for the shell. -This version has many -features which make it appear similar in some respects to the Korn shell, -but it is not a Korn shell clone (see -.Xr ksh 1 ) . -This man page is not intended -to be a tutorial or a complete specification of the shell. -.Ss Overview -The shell is a command that reads lines from either a file or the -terminal, interprets them, and generally executes other commands. -A shell is the program that is running when a user logs into the system. -(Users can select which shell is executed for them at login with the -.Xr chsh 1 -command). -The shell implements a language that has flow control -constructs, a macro facility that provides a variety of features in -addition to data storage, along with built in history and line editing -capabilities. -It incorporates many features to aid interactive use and -has the advantage that the interpretative language is common to both -interactive and non-interactive use (shell scripts). -That is, commands -can be typed directly to the running shell or can be put into a file and -the file can be executed directly by the shell. -.Ss Invocation -If no arguments are present and if the standard input, -and standard error output, of the shell -are connected to a terminal (or terminals, or if the -.Fl i -flag is set), -and the -.Fl c -option is not present, the shell is considered an interactive shell. -An interactive shell generally prompts before each command and handles -programming and command errors differently (as described below). -When first starting, -the shell inspects argument 0, and if it begins with a dash -.Sq - , -the shell is also considered -a login shell. -This is normally done automatically by the system -when the user first logs in. -A login shell first reads commands -from the files -.Pa /etc/profile -and -.Pa .profile -in the user's home directory -.Pq \&$HOME , -if they exist. -If the environment variable -.Ev ENV -is set on entry to a shell, -or is set in the -.Pa .profile -of a login shell, -and either the shell is interactive, or the -.Cm posix -option is not set, -the shell then performs parameter and arithmetic -expansion on the value of -.Ev ENV , -(these are described later) -and then reads commands from the file name that results. -If -.Ev ENV -contains a command substitution, or one of the -other expansions fails, or if there are no expansions -to expand, the value of -.Ev ENV -is used as the file name. -.Pp -Therefore, a user should place commands that are to be executed only at -login time in the -.Pa .profile -file, and commands that are executed for every shell inside the -.Ev ENV -file. -To set the -.Ev ENV -variable to some file, place the following line in your -.Pa .profile -of your home directory -.Pp -.Dl ENV=$HOME/.shinit; export ENV -.Pp -substituting for -.Dq .shinit -any filename you wish. -Since the -.Ev ENV -file can be read for every invocation of the shell, including shell scripts -and non-interactive shells, the following paradigm is useful for -restricting commands in the -.Ev ENV -file to interactive invocations. -Place commands within the -.Dq Ic case -and -.Dq Ic esac -below (these commands are described later): -.Bd -literal -offset indent -case $- in *i*) - # commands for interactive use only - ... -esac -.Ed -.Pp -If command line arguments besides the options have been specified, and -neither -.Fl c -nor -.Fl s -was given, then the shell treats the first argument -as the name of a file from which to read commands (a shell script). -This also becomes -.Li $0 -and the remaining arguments are set as the -positional parameters of the shell -.Li ( $1 , $2 , -etc). -Otherwise, if -.Fl c -was given, then the first argument, which must exist, -is taken to be a string of -.Nm -commands to execute. -Then if any additional arguments follow the command string, -those arguments become -.Li $0 , $1 , -\&... -Otherwise, if additional arguments were given -(which implies that -.Fl s -was set) -those arguments become -.Li $1 , $2 , -\&... -If -.Li $0 -has not been set by the preceding processing, it -will be set to -.Va argv\^ Ns [ 0 ] -as passed to the shell, which will -usually be the name of the shell itself. -If -.Fl s -was given, or if neither -.Fl c -nor any additional (non-option) arguments were present, -the shell reads commands from its standard input. -.\" -.\" -.Ss Argument List Processing -.\" -Currently, all of the single letter options that can meaningfully -be set using the -.Ic set -built-in, have a corresponding name -that can be used as an argument to the -.Fl o -option. -The -.Ic set Fl o -name is provided next to the single letter option in -the description below. -Some options have only a long name, they are described after -the flag options, they are used with -.Fl o -or -.Cm +o -only, either on the command line, or with the -.Ic set -built-in command. -Other options described are for the command line only. -Specifying a dash -.Dq Cm \- -turns the option on, while using a plus -.Dq Cm + -disables the option. -The following options can be set from the command line and, -unless otherwise stated, with the -.Ic set -built-in (described later). -.\" -.\" strlen("quietprofile") == strlen("local_lineno"): pick the latter -.\" to give the indent as the _ in local_lineno, and the fi ligature in -.\" quietprofile combine to make "local_lineno' slightly wider when printed -.\" (in italics) in a variable width font. -.Bl -tag -width ".Fl L Em local_lineno" -offset indent -.\" -.It Fl a Em allexport -Automatically export any variable to which a value is assigned -while this flag is set, unless the variable has been marked as -not for export. -.It Fl b Em notify -Enable asynchronous notification of background job completion. -(Not implemented.) -.It Fl C Em noclobber -Don't overwrite existing files with -.Dq > . -.It Fl c -Read commands from the -.Ar command_string -operand instead of, or in addition to, from the standard input. -Special parameter -.Dv 0 \" $0 (comments like this for searching sources only) -will be set from the -.Ar command_name -operand if given, and the positional parameters -.Dv ( 1 , 2 , -etc.) -set from the remaining argument operands, if any. -.Fl c -is only available at invocation, it cannot be -.Ic set , -and there is no form using -.Dq Cm \&+ . -.It Fl E Em emacs -Enable the built-in emacs style -command line editor (disables -.Fl V -if it has been set). -(See the -.Sx Command Line Editing -section below.) -.It Fl e Em errexit -If not interactive, exit immediately if any untested command fails. -If interactive, and an untested command fails, -cease all processing of the current command and return to -prompt for a new command. -The exit status of a command is considered to be -explicitly tested if the command is used to control an -.Ic if , -.Ic elif , -.Ic while , -or -.Ic until , -or if the command is the left hand operand of an -.Dq && -or -.Dq || -operator, -or if it is a pipeline (or simple command) preceded by the -.Dq \&! -operator. -With pipelines, only the status of the entire pipeline -(indicated by the last command it contains) -is tested when -.Fl e -is set to determine if the shell should exit. -.It Fl F Em fork -Cause the shell to always use -.Xr fork 2 -instead of attempting -.Xr vfork 2 -when it needs to create a new process. -This should normally have no visible effect, -but can slow execution. -The -.Nm -can be compiled to always use -.Xr fork 2 -in which case altering the -.Fl F -flag has no effect. -.It Fl f Em noglob -Disable pathname expansion. -.It Fl h Em trackall -Functions defined while this option is set will have paths bound to -commands to be executed by the function at the time of the definition. -When off when a function is defined, -the file system is searched for commands each time the function is invoked. -(Not implemented.) -.It Fl I Em ignoreeof -Ignore EOFs from input when interactive. -(After a large number of consecutive EOFs the shell will exit anyway.) -.It Fl i Em interactive -Force the shell to behave interactively. -.It Fl L Em local_lineno -When set, before a function is defined, -causes the variable -.Dv LINENO -when used within the function, -to refer to the line number defined such that -first line of the function is line 1. -When reset, -.Dv LINENO -in a function refers to the line number within the file -within which the definition of the function occurs. -This option defaults to -.Dq on -in this shell. -For more details see the section -.Sx LINENO -below. -.It Fl m Em monitor -Turn on job control (set automatically when interactive). -.It Fl n Em noexec -Read and parse commands, but do not execute them. -This is useful for checking the syntax of shell scripts. -If -.Fl n -becomes set in an interactive shell, it will automatically be -cleared just before the next time the command line prompt -.Pq Ev PS1 -is written. -.It Fl p Em nopriv -Do not attempt to reset effective UID if it does not match UID. -This is not set by default to help avoid incorrect usage by setuid -root programs via -.Xr system 3 -or -.Xr popen 3 . -.It Fl q Em quietprofile -If the -.Fl v -or -.Fl x -options have been set, do not apply them when reading -initialization files, these being -.Pa /etc/profile , -.Pa .profile , -and the file specified by the -.Ev ENV -environment variable. -.It Fl s Em stdin -Read commands from standard input (set automatically if -neither -.Fl c -nor file arguments are present). -If after processing a command_string with the -.Fl c -option, the shell has not exited, and the -.Fl s -option is set, it will continue reading more commands from standard input. -This option has no effect when set or reset after the shell has -already started reading from the command_file, or from standard input. -Note that the -.Fl s -flag being set does not cause the shell to be interactive. -.It Fl u Em nounset -Write a message to standard error when attempting to obtain a -value from a variable that is not set, -and if the shell is not interactive, exit immediately. -For interactive shells, instead return immediately to the command prompt -and read the next command. -Note that expansions (described later, see -.Sx Word Expansions -below) using the -.Sq \&+ , -.Sq \&\- , -.Sq \&= , -or -.Sq \&? -operators test if the variable is set, before attempting to -obtain its value, and hence are unaffected by -.Fl u . -.It Fl V Em vi -Enable the built-in -.Xr vi 1 -command line editor (disables -.Fl E -if it has been set). -(See the -.Sx Command Line Editing -section below.) -.It Fl v Em verbose -The shell writes its input to standard error as it is read. -Useful for debugging. -.It Fl X Em xlock -Cause output from the -.Ic xtrace -.Pq Fl x -option to be sent to standard error as it exists when the -.Fl X -option is enabled (regardless of its previous state.) -For example: -.Bd -literal -compact - set -X 2>/tmp/trace-file -.Ed -will arrange for tracing output to be sent to the file named, -instead of wherever it was previously being sent, -until the X option is set again, or cleared. -.Pp -Each change (set or clear) to -.Fl X -is also performed upon -.Fl x , -but not the converse. -.It Fl x Em xtrace -Write each command to standard error (preceded by the expanded value of -.Li $PS4 ) -before it is executed. -Unless -.Fl X -is set, -.Dq "standard error" -means that which existed immediately before any redirections to -be applied to the command are performed. -Useful for debugging. -.It "\ \ " Em cdprint -Make an interactive shell always print the new directory name when -changed by the -.Ic cd -command. -In a non-interactive shell this option has no effect. -.It "\ \ " Em nolog -Prevent the entry of function definitions into the command history (see -.Ic fc -in the -.Sx Built-ins -section.) -(Not implemented.) -.It "\ \ " Em pipefail -If set when a pipeline is created, -the way the exit status of the pipeline is determined -is altered. -See -.Sx Pipelines -below for the details. -.It "\ \ " Em posix -Enables closer adherence to the POSIX shell standard. -This option will default set at shell startup if the -environment variable -.Ev POSIXLY_CORRECT -is present. -That can be overridden (set or reset) by the -.Fl o -option on the command line. -Currently this option controls whether (!posix) or not (posix) -the file given by the -.Ev ENV -variable is read at startup by a non-interactive shell. -It also controls whether file descriptors greater than 2 -opened using the -.Ic exec -built-in command are passed on to utilities executed -.Dq ( yes -in posix mode), -whether a colon (:) terminates the user name in tilde (~) expansions -other than in assignment statements -.Dq ( no -in posix mode), -the format of the output of the -.Ic kill Fl l -command, where posix mode causes the names of the signals -be separated by either a single space or newline, and where otherwise -sufficient spaces are inserted to generate nice looking columns, -and whether the shell treats -an empty brace-list compound statement as a syntax error -(expected by POSIX) or permits it. -Such statements -.Dq "{\ }" -can be useful when defining dummy functions. -Lastly, in posix mode, only one -.Dq \&! -is permitted before a pipeline. -.It "\ \ " Em promptcmds -Allows command substitutions (as well as parameter -and arithmetic expansions, which are always performed) -upon the prompt strings -.Ev PS1 , -.Ev PS2 , -and -.Ev PS4 -each time, before they are output. -This option should not be set until after the prompts -have been set (or verified) to avoid accidentally importing -unwanted command substitutions from the environment. -.It "\ \ " Em tabcomplete -Enables filename completion in the command line editor. -Typing a tab character will extend the current input word to match a -filename. -If more than one filename matches it is only extended to be the common prefix. -Typing a second tab character will list all the matching names. -One of the editing modes, either -.Fl E -or -.Fl V , -must be enabled for this to work. -.El -.Ss Lexical Structure -The shell reads input in terms of lines from a file and breaks it up into -words at whitespace (blanks and tabs), and at certain sequences of -characters that are special to the shell called -.Dq operators . -There are two types of operators: control operators and redirection -operators (their meaning is discussed later). -The following is a list of operators: -.Bl -ohang -offset indent -.It "Control operators:" -.Dl & && \&( \&) \&; ;; ;& \&| || <newline> -.It "Redirection operators:" -.Dl < > >| << >> <& >& <<- <> -.El -.Ss Quoting -Quoting is used to remove the special meaning of certain characters or -words to the shell, such as operators, whitespace, or keywords. -There are four types of quoting: -matched single quotes, -matched double quotes, -backslash, -and -dollar preceding matched single quotes (enhanced C style strings.) -.Ss Backslash -An unquoted backslash preserves the literal meaning of the following -character, with the exception of -.Aq newline . -An unquoted backslash preceding a -.Aq newline -is treated as a line continuation, the two characters are simply removed. -.Ss Single Quotes -Enclosing characters in single quotes preserves the literal meaning of all -the characters (except single quotes, making it impossible to put -single quotes in a single-quoted string). -.Ss Double Quotes -Enclosing characters within double quotes preserves the literal -meaning of all characters except dollar sign -.Pq Li \&$ , -backquote -.Pq Li \&` , -and backslash -.Pq Li \e . -The backslash inside double quotes is historically weird, and serves to -quote only the following characters (and these not in all contexts): -.Dl $ ` \*q \e <newline> , -where a backslash newline is a line continuation as above. -Otherwise it remains literal. -.\" -.\" -.Ss Dollar Single Quotes ( Li \&$'...' ) -.\" -.Bd -filled -offset indent -.Bf Em -Note: this form of quoting is still somewhat experimental, -and yet to be included in the POSIX standard. -This implementation is based upon the current proposals for -standardization, and is subject to change should the eventual -adopted text differ. -.Ef -.Ed -.Pp -Enclosing characters in a matched pair of single quotes, with the -first immediately preceded by an unquoted dollar sign -.Pq Li \&$ -provides a quoting mechanism similar to single quotes, except -that within the sequence of characters, any backslash -.Pq Li \e , -is an escape character, which causes the following character to -be treated specially. -Only a subset of the characters that can occur in the string -are defined after a backslash, others are reserved for future -definition, and currently generate a syntax error if used. -The escape sequences are modeled after the similar sequences -in strings in the C programming language, with some extensions. -.Pp -The following characters are treated literally when following -the escape character (backslash): -.Dl \e \&' \(dq -The sequence -.Dq Li \e\e -allows the escape character (backslash) to appear in the string literally. -.Dq Li \e' -allows a single quote character into the string, such an -escaped single quote does not terminate the quoted string. -.Dq Li \e\(dq -is for compatibility with C strings, the double quote has -no special meaning in a shell C-style string, -and does not need to be escaped, but may be. -.Pp -A newline following the escape character is treated as a line continuation, -like the same sequence in a double quoted string, -or when not quoted \(en -the two characters, the backslash escape and the newline, -are removed from the input string. -.Pp -The following characters, when escaped, are converted in a -manner similar to the way they would be in a string in the C language: -.Dl a b e f n r t v -An escaped -.Sq a -generates an alert (or -.Sq BEL ) -character, that is, control-G, or 0x07. -In a similar way, -.Sq b -is backspace (0x08), -.Sq e -(an extension to C) is escape (0x1B), -.Sq f -is form feed (0x0C), -.Sq n -is newline (or line feed, 0x0A), -.Sq r -is return (0x0D), -.Sq t -is horizontal tab (0x09), -and -.Sq v -is vertical tab (0x13). -.Pp -In addition to those there are 5 forms that need additional -data, which is obtained from the subsequent characters. -An escape -.Pq Li \e -followed by one, two or three, octal digits -.Po So 0 Sc Ns \&.. Ns So 7 Sc Ns Pc -is processed to form an 8 bit character value. -If only one or two digits are present, the following -character must be something other than an octal digit. -It is safest to always use all 3 digits, with leading -zeros if needed. -If all three digits are present, the first must be one of -.So 0 Sc Ns \&.. Ns So 3 Sc . -.Pp -An escape followed by -.Sq x -(lower case only) can be followed by one or two -hexadecimal digits -.Po So 0 Sc Ns \&.. Ns So 9 Sc , So A Sc Ns \&.. Ns So F Sc , or So a Sc Ns \&.. Ns So f Sc . Pc -As with octal, if only one hex digit is present, the following -character must be something other than a hex digit, -so always giving 2 hex digits is best. -However, unlike octal, it is unspecified in the standard -how many hex digits can be consumed. -This -.Nm -takes at most two, but other shells will continue consuming -characters as long as they remain valid hex digits. -Consequently, users should ensure that the character -following the hex escape sequence is something other than -a hex digit. -One way to achieve this is to end the -.Li $'...' -string immediately -after the final hex digit, and then, immediately start -another, so -.Dl \&$'\ex33'$'4...' -always gives the character with value 0x33 -.Pq Sq 3 , -followed by the character -.Sq 4 , -whereas -.Dl \&$'\ex334' -in some other shells would be the hex value 0x334 (10, or more, bits). -.Pp -There are two escape sequences beginning with -.Sq Li \eu -or -.Sq Li \eU . -The former is followed by from 1 to 4 hex digits, the latter by -from 1 to 8 hex digits. -Leading zeros can be used to pad the sequences to the maximum -permitted length, to avoid any possible ambiguity problem with -the following character, and because there are some shells that -insist on exactly 4 (or 8) hex digits. -These sequences are evaluated to form the value of a Unicode code -point, which is then encoded into UTF-8 form, and entered into the -string. -(The code point should be converted to the appropriate -code point value for the corresponding character in the character -set given by the current locale, or perhaps the locale in use -when the shell was started, but is not... currently.) -Not all values that are possible to write are valid, values that -specify (known) invalid Unicode code points will be rejected, or -simply produce -.Sq \&? . -.Pp -Lastly, as another addition to what is available in C, the escape -character (backslash), followed by -.Sq c -(lower case only) followed by one additional character, which must -be an alphabetic character (a letter), or one of the following: -.Dl \&@ \&[ \&\e \&] \&^ \&_ \&? -Other than -.Sq Li \ec? -the value obtained is the least significant 5 bits of the -ASCII value of the character following the -.Sq Li \ec -escape sequence. -That is what is commonly known as the -.Dq control -character obtained from the given character. -The escape sequence -.Sq Li \ec? -yields the ASCII DEL character (0x7F). -Note that to obtain the ASCII FS character (0x1C) this way, -.Pq "that is control-\e" -the trailing -.Sq Li \e -must be escaped itself, and so for this one case, the full -escape sequence is -.Dq Li \ec\e\e . -The sequence -.Dq Li \ec\e Ns Ar X\^ -where -.Sq Ar X\^ -is some character other than -.Sq Li \e -is reserved for future use, its meaning is unspecified. -In this -.Nm -an error is generated. -.Pp -If any of the preceding escape sequences generate the value -.Sq \e0 -(a NUL character) that character, and all that follow in the same -.Li $'...' -string, are omitted from the resulting word. -.Pp -After the -.Li $'...' -string has had any included escape sequences -converted, it is treated as if it had been a single quoted string. -.\" -.\" -.Ss Reserved Words -.\" -Reserved words are words that have special meaning to the -shell and are recognized at the beginning of a line and -after a control operator. -The following are reserved words: -.Bl -column while while while while -offset indent -.It Ic \&! Ta Ic \&{ Ta Ic \&} Ta Ic case -.It Ic do Ta Ic done Ta Ic elif Ta Ic else -.It Ic esac Ta Ic fi Ta Ic for Ta Ic if -.It Ic in Ta Ic then Ta Ic until Ta Ic while -.El -.Pp -Their meanings are discussed later. -.\" -.\" -.Ss Aliases -.\" -An alias is a name and corresponding value set using the -.Ic alias -built-in command. -Whenever a reserved word (see above) may occur, -and after checking for reserved words, the shell -checks the word to see if it matches an alias. -If it does, it replaces it in the input stream with its value. -For example, if there is an alias called -.Dq lf -with the value -.Dq "ls -F" , -then the input: -.Pp -.Dl lf foobar Aq return -.Pp -would become -.Pp -.Dl ls -F foobar Aq return -.Pp -Aliases provide a convenient way for naive users to create shorthands for -commands without having to learn how to create functions with arguments. -They can also be used to create lexically obscure code. -This use is strongly discouraged. -.\" -.Ss Commands -.\" -The shell interprets the words it reads according to a language, the -specification of which is outside the scope of this man page (refer to the -BNF in the POSIX 1003.2 document). -Essentially though, a line is read and if the first -word of the line (or after a control operator) is not a reserved word, -then the shell has recognized a simple command. -Otherwise, a complex -command or some other special construct may have been recognized. -.\" -.\" -.Ss Simple Commands -.\" -If a simple command has been recognized, the shell performs -the following actions: -.Bl -enum -offset indent -.It -Leading words of the form -.Dq Ar name Ns Li = Ns Ar value -are stripped off, the value is expanded, as described below, -and the results are assigned to the environment of the simple command. -Redirection operators and their arguments (as described below) are -stripped off and saved for processing in step 3 below. -.It -The remaining words are expanded as described in the -.Sx Word Expansions -section below. -The first remaining word is considered the command name and the -command is located. -Any remaining words are considered the arguments of the command. -If no command name resulted, then the -.Dq Ar name Ns Li = Ns Ar value -variable assignments recognized in item 1 affect the current shell. -.It -Redirections are performed, from first to last, in the order given, -as described in the next section. -.El -.\" -.\" -.Ss Redirections -.\" -Redirections are used to change where a command reads its input or sends -its output. -In general, redirections open, close, or duplicate an -existing reference to a file. -The overall format used for redirection is: -.Pp -.Dl Oo Ar n Oc Ns Va redir-op Ar file -.Pp -where -.Va redir-op -is one of the redirection operators mentioned previously. -The following is a list of the possible redirections. -The -.Op Ar n -is an optional number, as in -.Sq Li 3 -(not -.Li [3] ) , -that refers to a file descriptor. -If present it must occur immediately before the redirection -operator, with no intervening white space, and becomes a -part of that operator. -.Bl -tag -width aaabsfiles -offset indent -.It Oo Ar n Oc Ns Ic > Ar file -Redirect standard output (or -.Ar n ) -to -.Ar file . -.It Oo Ar n Oc Ns Ic >| Ar file -The same, but override the -.Fl C -option. -.It Oo Ar n Oc Ns Ic >> Ar file -Append standard output (or -.Ar n ) -to -.Ar file . -.It Oo Ar n Oc Ns Ic < Ar file -Redirect standard input (or -.Ar n ) -from -.Ar file . -.It Oo Ar n1 Oc Ns Ic <& Ns Ar n2 -Duplicate standard input (or -.Ar n1 ) -from file descriptor -.Ar n2 . -.Ar n2 -is expanded if not a digit string, the result must be a number. -.It Oo Ar n Oc Ns Ic <&- -Close standard input (or -.Ar n ) . -.It Oo Ar n1 Oc Ns Ic >& Ns Ar n2 -Duplicate standard output (or -.Ar n1 ) -to -.Ar n2 . -.It Oo Ar n Oc Ns Ic >&- -Close standard output (or -.Ar n ) . -.It Oo Ar n Oc Ns Ic <> Ar file -Open -.Ar file -for reading and writing on standard input (or -.Ar n ) . -.El -.Pp -The following redirection is often called a -.Dq here-document . -.Bd -unfilled -offset indent -.Oo Ar n Oc Ns Ic << Ar delimiter -.Li \&... here-doc-text ... -.Ar delimiter -.Ed -.Pp -The -.Dq here-doc-text -starts immediately after the next unquoted newline character following -the here-document redirection operator. -If there is more than one here-document redirection on the same -line, then the text for the first (from left to right) is read -first, and subsequent here-doc-text for later here-document redirections -follows immediately after, until all such redirections have been -processed. -.Pp -All the text on successive lines up to the delimiter, -which must appear on a line by itself, with nothing other -than an immediately following newline, is -saved away and made available to the command on standard input, or file -descriptor n if it is specified. -If the delimiter as specified on the initial line is -quoted, then the here-doc-text is treated literally; otherwise, the text is -treated much like a double quoted string, except that -.Sq Li \(dq -characters have no special meaning, and are not escaped by -.Sq Li \&\e , -and is subjected to parameter expansion, command substitution, and arithmetic -expansion as described in the -.Sx Word Expansions -section below. -If the operator is -.Ic <<- -instead of -.Ic << , -then leading tabs in all lines in the here-doc-text, including before the -end delimiter, are stripped. -If the delimiter is not quoted, lines in here-doc-text that end with -an unquoted -.Li \e -are joined to the following line, the -.Li \e -and following -newline are simply removed while reading the here-document, -which thus guarantees -that neither of those lines can be the end delimiter. -.Pp -It is a syntax error for the end of the input file (or string) to be -reached before the delimiter is encountered. -.\" -.\" -.Ss Search and Execution -.\" -There are three types of commands: shell functions, built-in commands, and -normal programs \(em and the command is searched for (by name) in that order. -A command that contains a slash -.Sq \&/ -in its name is always a normal program. -They each are executed in a different way. -.Pp -When a shell function is executed, all of the shell positional parameters -(note: excluding -.Li 0 , \" $0 -which is a special, not positional, parameter, and remains unchanged) -are set to the arguments of the shell function. -The variables which are explicitly placed in the environment of -the command (by placing assignments to them before the function name) are -made local to the function and are set to the values given, -and exported for the benefit of programs executed with the function. -Then the command given in the function definition is executed. -The positional parameters, and local variables, are restored to -their original values when the command completes. -This all occurs within the current shell, and the function -can alter variables, or other settings, of the shell, but -not the positional parameters nor their related special parameters. -.Pp -Shell built-ins are executed internally to the shell, without spawning a -new process. -.Pp -Otherwise, if the command name doesn't match a function or built-in, the -command is searched for as a normal program in the file system (as -described in the next section). -When a normal program is executed, the shell runs the program, -passing the arguments and the environment to the program. -If the program is not a normal executable file, and if it does -not begin with the -.Dq magic number -whose ASCII representation is -.Dq Li "#!" , -so -.Xr execve 2 -returns -.Er ENOEXEC -then) the shell will interpret the program in a sub-shell. -The child shell will reinitialize itself in this case, -so that the effect will be as if a -new shell had been invoked to handle the ad-hoc shell script, except that -the location of hashed commands located in the parent shell will be -remembered by the child. -.Pp -Note that previous versions of this document and the source code itself -misleadingly and sporadically refer to a shell script without a magic -number as a -.Dq shell procedure . -.\" -.\" -.Ss Path Search -.\" -When locating a command, the shell first looks to see if it has a shell -function by that name. -Then it looks for a built-in command by that name. -If a built-in command is not found, one of two things happen: -.Bl -enum -.It -Command names containing a slash are simply executed without performing -any searches. -.It -Otherwise, the shell searches each entry in -.Ev PATH -in turn for the command. -The value of the -.Ev PATH -variable should be a series of entries separated by colons. -Each entry consists of a directory name. -The current directory may be indicated -implicitly by an empty directory name, or explicitly by a single period. -If a directory searched contains an executable file with the same -name as the command given, -the search terminates, and that program is executed. -.El -.Ss Command Exit Status -Each command has an exit status that can influence the behavior -of other shell commands. -The paradigm is that a command exits -with zero in normal cases, or to indicate success, -and non-zero for failure, -error, or a false indication. -The man page for each command -should indicate the various exit codes and what they mean. -Additionally, the built-in commands return exit codes, as does -an executed shell function. -.Pp -If a command consists entirely of variable assignments then the -exit status of the command is that of the last command substitution -if any, otherwise 0. -.Pp -If redirections are present, and any fail to be correctly performed, -any command present is not executed, and an exit status of 2 -is returned. -.Ss Complex Commands -Complex commands are combinations of simple commands with control -operators or reserved words, together creating a larger complex command. -Overall, a shell program is a: -.Bl -tag -width XpipelineX -.It list -Which is a sequence of one or more AND-OR lists. -.It "AND-OR list" -is a sequence of one or more pipelines. -.It pipeline -is a sequence of one or more commands. -.It command -is one of a simple command, a compound command, or a function definition. -.It "simple command" -has been explained above, and is the basic building block. -.It "compound command" -provides mechanisms to group lists to achieve different effects. -.It "function definition" -allows new simple commands to be created as groupings of existing commands. -.El -.Pp -Unless otherwise stated, the exit status of a list -is that of the last simple command executed by the list. -.\" -.\" -.Ss Pipelines -.\" -A pipeline is a sequence of one or more commands separated -by the control operator -.Sq Ic \(ba , -and optionally preceded by the -.Dq Ic \&! -reserved word. -Note that -.Sq Ic \(ba -is an operator, and so is recognized anywhere it appears unquoted, -it does not require surrounding white space or other syntax elements. -On the other hand -.Dq Ic \&! -being a reserved word, must be separated from adjacent words by -white space (or other operators, perhaps redirects) and is only -recognized as the reserved word when it appears in a command word -position (such as at the beginning of a pipeline.) -.Pp -The standard output of all but -the last command in the sequence is connected to the standard input -of the next command. -The standard output of the last -command is inherited from the shell, as usual, -as is the standard input of the first command. -.Pp -The format for a pipeline is: -.Pp -.Dl [!] command1 Op Li \&| command2 No ... -.Pp -The standard output of command1 is connected to the standard input of -command2. -The standard input, standard output, or both of each command is -considered to be assigned by the pipeline before any redirection specified -by redirection operators that are part of the command are performed. -.Pp -If the pipeline is not in the background (discussed later), the shell -waits for all commands to complete. -.Pp -The commands in a pipeline can either be simple commands, -or one of the compound commands described below. -The simplest case of a pipeline is a single simple command. -.Pp -If the -.Ic pipefail -option was set when a pipeline was started, -the pipeline status is the status of -the last (lexically last, i.e.: rightmost) command in the -pipeline to exit with non-zero exit status, or zero, if, -and only if, all commands in the pipeline exited with a status of zero. -If the -.Ic pipefail -option was not set, which is the default state, -the pipeline status is the exit -status of the last (rightmost) command in the pipeline, -and the exit status of any other commands in the pipeline is ignored. -.Pp -If the reserved word -.Dq Ic \&! -precedes the pipeline, the exit status -becomes the logical NOT of the pipeline status as determined above. -That is, if the pipeline status is zero, the exit status is 1; -if the pipeline status is other than zero, the exit status is zero. -If there is no -.Dq Ic \&! -reserved word, the pipeline status becomes the exit status. -.Pp -Because pipeline assignment of standard input or standard output or both -takes place before redirection, it can be modified by redirection. -For example: -.Pp -.Dl $ command1 2>&1 \&| command2 -.Pp -sends both the standard output and standard error of command1 -to the standard input of command2. -.Pp -Note that unlike some other shells, each process in the pipeline is a -child of the invoking shell (unless it is a shell built-in, in which case -it executes in the current shell \(em but any effect it has on the -environment is wiped). -.Pp -A pipeline is a simple case of an AND-OR-list (described below.) -A -.Li \&; -or -.Aq newline -terminator causes the preceding pipeline, or more generally, -the preceding AND-OR-list to be executed sequentially; -that is, the shell executes the commands, and waits for them -to finish before proceeding to following commands. -An -.Li \&& -terminator causes asynchronous (background) execution -of the preceding AND-OR-list (see the next paragraph below). -The exit status of an asynchronous AND-OR-list is zero. -The actual status of the commands, -after they have completed, -can be obtained using the -.Ic wait -built-in command described later. -.\" -.\" -.Ss Background Commands \(em Ic \&& -.\" -If a command, pipeline, or AND-OR-list -is terminated by the control operator ampersand -.Pq Li \&& , -the -shell executes the command asynchronously \(em that is, the shell does not -wait for the command to finish before executing the next command. -.Pp -The format for running a command in background is: -.Pp -.Dl command1 & Op Li command2 & No ... -.Pp -If the shell is not interactive, the standard input of an asynchronous -command is set to -.Pa /dev/null . -The process identifier of the most recent command started in the -background can be obtained from the value of the special parameter -.Dq Dv \&! \" $! -(see -.Sx Special Parameters ) -provided it is accessed before the next asynchronous command is started. -.\" -.\" -.Ss Lists \(em Generally Speaking -.\" -A list is a sequence of one or more commands separated by newlines, -semicolons, or ampersands, and optionally terminated by one of these three -characters. -A shell program, which includes the commands given to an -interactive shell, is a list. -Each command in such a list is executed when it is fully parsed. -Another use of a list is as a complete-command, -which is parsed in its entirety, and then later the commands in -the list are executed only if there were no parsing errors. -.Pp -The commands in a list are executed in the order they are written. -If command is followed by an ampersand, the shell starts the -command and immediately proceeds to the next command; otherwise it waits -for the command to terminate before proceeding to the next one. -A newline is equivalent to a -.Sq Li \&; -when no other operator is present, and the command being input -could syntactically correctly be terminated at the point where -the newline is encountered, otherwise it is just whitespace. -.\" -.\" -.Ss AND-OR Lists (Short-Circuit List Operators) -.\" -.Dq Li \&&& -and -.Dq Li \&|| -are AND-OR list operators. -After executing the commands that precede the -.Dq Li \&&& -the subsequent command is executed -if and only if the exit status of the preceding command(s) is zero. -.Dq Li \&|| -is similar, but executes the subsequent command if and only if the exit status -of the preceding command is nonzero. -If a command is not executed, the exit status remains unchanged -and the following AND-OR list operator (if any) uses that status. -.Dq Li \&&& -and -.Dq Li \&|| -both have the same priority. -Note that these operators are left-associative, so -.Dl true || echo bar && echo baz -writes -.Dq baz -and nothing else. -This is not the way it works in C. -.\" -.\" -.Ss Flow-Control Constructs \(em Ic if , while , until , for , case -.\" -These commands are instances of compound commands. -The syntax of the -.Ic if -command is -.Bd -literal -offset indent -.Ic if Ar list -.Ic then Ar list -.Ic [ elif Ar list -.Ic then Ar list ] No ... -.Ic [ else Ar list ] -.Ic fi -.Ed -.Pp -The first list is executed, and if the exit status of that list is zero, -the list following the -.Ic then -is executed. -Otherwise the list after an -.Ic elif -(if any) is executed and the process repeats. -When no more -.Ic elif -reserved words, and accompanying lists, appear, -the list after the -.Ic else -reserved word, if any, is executed. -.Pp -The syntax of the -.Ic while -command is -.Bd -literal -offset indent -.Ic while Ar list -.Ic do Ar list -.Ic done -.Ed -.Pp -The two lists are executed repeatedly while the exit status of the -first list is zero. -The -.Ic until -command is similar, but has the word -.Ic until -in place of -.Ic while , -which causes it to repeat until the exit status of the first list is zero. -.Pp -The syntax of the -.Ic for -command is -.Bd -literal -offset indent -.Ic for Ar variable Op Ic in Ar word No ... -.Ic do Ar list -.Ic done -.Ed -.Pp -The words are expanded, or -.Li \*q$@\*q -if -.Ic in -(and the following words) is not present, -and then the list is executed repeatedly with the -variable set to each word in turn. -If -.Ic in -appears after the variable, but no words are -present, the list is not executed, and the exit status is zero. -.Ic do -and -.Ic done -may be replaced with -.Sq Ic \&{ -and -.Sq Ic \&} , -but doing so is non-standard and not recommended. -.Pp -The syntax of the -.Ic break -and -.Ic continue -commands is -.Bd -literal -offset indent -.Ic break Op Ar num -.Ic continue Op Ar num -.Ed -.Pp -.Ic break -terminates the -.Ar num -innermost -.Ic for , while , -or -.Ic until -loops. -.Ic continue -breaks execution of the -.Ar num\^ Ns -1 -innermost -.Ic for , while , -or -.Ic until -loops, and then continues with the next iteration of the enclosing loop. -These are implemented as special built-in commands. -The parameter -.Ar num , -if given, must be an unsigned positive integer (greater than zero). -If not given, 1 is used. -.Pp -The syntax of the -.Ic case -command is -.Bd -literal -offset indent -.Ic case Ar word Ic in -.Oo Ic \&( Oc Ar pattern Ns Ic \&) Oo Ar list Oc Ic \&;& -.Oo Ic \&( Oc Ar pattern Ns Ic \&) Oo Ar list Oc Ic \&;; -.No \&... -.Ic esac -.Ed -.Pp -The pattern can actually be one or more patterns (see -.Sx Shell Patterns -described later), separated by -.Dq \(or -characters. -.Pp -.Ar word -is expanded and matched against each -.Ar pattern -in turn, -from first to last, -with each pattern being expanded just before the match is attempted. -When a match is found, pattern comparisons cease, and the associated -.Ar list , -if given, -is evaluated. -If the list is terminated with -.Dq Ic \&;& -execution then falls through to the following list, if any, -without evaluating its pattern, or attempting a match. -When a list terminated with -.Dq Ic \&;; -has been executed, or when -.Ic esac -is reached, execution of the -.Ic case -statement is complete. -The exit status is that of the last command executed -from the last list evaluated, if any, or zero otherwise. -.\" -.\" -.Ss Grouping Commands Together -.\" -Commands may be grouped by writing either -.Dl Ic \&( Ns Ar list Ns Ic \&) -or -.Dl Ic \&{ Ar list Ns Ic \&; \&} -These also form compound commands. -.Pp -Note that while parentheses are operators, and do not require -any extra syntax, braces are reserved words, so the opening brace -must be followed by white space (or some other operator), and the -closing brace must occur in a position where a new command word might -otherwise appear. -.Pp -The first of these executes the commands in a sub-shell. -Built-in commands grouped into a -.Li \&( Ns Ar list Ns \&) -will not affect the current shell. -The second form does not fork another shell so is slightly more efficient, -and allows for commands which do affect the current shell. -Grouping commands together this way allows you to redirect -their output as though they were one program: -.Bd -literal -offset indent -{ echo -n \*qhello \*q ; echo \*qworld\*q ; } > greeting -.Ed -.Pp -Note that -.Dq Ic } -must follow a control operator (here, -.Dq Ic \&; ) -so that it is recognized as a reserved word and not as another command argument. -.\" -.\" -.Ss Functions -.\" -The syntax of a function definition is -.Pp -.Dl Ar name Ns Ic \&() Ar command Op Ar redirect No ... -.Pp -A function definition is an executable statement; when executed it -installs a function named name and returns an exit status of zero. -The command is normally a list enclosed between -.Dq { -and -.Dq } . -The standard syntax also allows the command to be any of the other -compound commands, including a sub-shell, all of which are supported. -As an extension, this shell also allows a simple command -(or even another function definition) to be -used, though users should be aware this is non-standard syntax. -This means that -.Dl l() ls \*q$@\*q -works to make -.Dq l -an alternative name for the -.Ic ls -command. -.Pp -If the optional redirect, (see -.Sx Redirections ) , -which may be of any of the normal forms, -is given, it is applied each time the -function is called. -This means that a simple -.Dq Hello World -function might be written (in the extended syntax) as: -.Bd -literal -offset indent -hello() cat <<EOF -Hello World! -EOF -.Ed -.Pp -To be correctly standards conforming this should be re-written as: -.Bd -literal -offset indent -hello() { cat; } <<EOF -Hello World! -EOF -.Ed -.Pp -Note the distinction between those forms, and -.Bd -literal -offset indent -hello() { cat <<EOF -Hello World! -EOF -\&} -.Ed -.Pp -which reads and processes the here-document -each time the shell executes the function, and which applies -that input only to the cat command, not to any other commands -that might appear in the function. -.Pp -Variables may be declared to be local to a function by using the -.Ic local -command. -This should usually appear as the first statement of a function, -though -.Ic local -is an executable command which can be used anywhere in a function. -See -.Sx Built-ins -below for its definition. -.Pp -The function completes after having executed -.Ar command -with exit status set to the status returned by -.Ar command . -If -.Ar command -is a compound-command -it can use the -.Ic return -command (see -.Sx Built-ins -below) -to finish before completing all of -.Ar command . -.Ss Variables and Parameters -The shell maintains a set of parameters. -A parameter denoted by a name is called a variable. -When starting up, the shell turns all the environment -variables into shell variables, and exports them. -New variables can be set using the form -.Pp -.Dl Ar name Ns Li = Ns Ar value -.Pp -Variables set by the user must have a name consisting solely of -alphabetics, numerics, and underscores \(em the first of which must not be -numeric. -A parameter can also be denoted by a number or a special -character as explained below. -.Ss Positional Parameters -A positional parameter is a parameter denoted by a number (n > 0). -The shell sets these initially to the values of its command line arguments -that follow the name of the shell script. -The -.Ic set -built-in can also be used to set or reset them, and -.Ic shift -can be used to manipulate the list. -.Pp -To refer to the 10th (and later) positional parameters, -the form -.Li \&${ Ns Ar n Ns Li \&} -must be used. -Without the braces, a digit following -.Dq $ -can only refer to one of the first 9 positional parameters, -or the special parameter -.Dv 0 . \" $0 -The word -.Dq Li $10 -is treated identically to -.Dq Li ${1}0 . -.\" -.\" -.Ss Special Parameters -.\" -A special parameter is a parameter denoted by one of the following special -characters. -The value of the parameter is listed next to its character. -.Bl -tag -width thinhyphena -.It Dv * -Expands to the positional parameters, starting from one. -When the -expansion occurs within a double-quoted string it expands to a single -field with the value of each parameter separated by the first character of -the -.Ev IFS -variable, or by a -.Aq space -if -.Ev IFS -is unset. -.It Dv @ \" $@ -Expands to the positional parameters, starting from one. -When the expansion occurs within double quotes, each positional -parameter expands as a separate argument. -If there are no positional parameters, the -expansion of @ generates zero arguments, even when -.Dv $@ -is double-quoted. -What this basically means, for example, is -if -.Li $1 -is -.Dq abc -and -.Li $2 -is -.Dq def\ ghi , -then -.Li \*q$@\*q -expands to -the two arguments: -.Pp -.Sm off -.Dl \*q abc \*q \ \*q def\ ghi \*q -.Sm on -.It Dv # -Expands to the number of positional parameters. -.It Dv \&? -Expands to the exit status of the most recent pipeline. -.It Dv \- No (hyphen, or minus) -Expands to the current option flags (the single-letter -option names concatenated into a string) as specified on -invocation, by the set built-in command, or implicitly -by the shell. -.It Dv $ -Expands to the process ID of the invoked shell. -A sub-shell retains the same value of -.Dv $ -as its parent. -.It Dv \&! -Expands to the process ID of the most recent background -command executed from the current shell. -For a pipeline, the process ID is that of the last command in the pipeline. -If no background commands have yet been started by the shell, then -.Dq Dv \&! -will be unset. -Once set, the value of -.Dq Dv \&! -will be retained until another background command is started. -.It Dv 0 No (zero) \" $0 -Expands to the name of the shell or shell script. -.El -.\" -.\" -.Ss Word Expansions -.\" -This section describes the various expansions that are performed on words. -Not all expansions are performed on every word, as explained later. -.Pp -Tilde expansions, parameter expansions, command substitutions, arithmetic -expansions, and quote removals that occur within a single word expand to a -single field. -It is only field splitting or pathname expansion that can -create multiple fields from a single word. -The single exception to this -rule is the expansion of the special parameter -.Dv @ \" $@ -within double quotes, as was described above. -.Pp -The order of word expansion is: -.Bl -enum -.It -Tilde Expansion, Parameter Expansion, Command Substitution, -Arithmetic Expansion (these all occur at the same time). -.It -Field Splitting is performed on fields -generated by step (1) unless the -.Ev IFS -variable is null. -.It -Pathname Expansion (unless set -.Fl f -is in effect). -.It -Quote Removal. -.El -.Pp -The $ character is used to introduce parameter expansion, command -substitution, or arithmetic evaluation. -.Ss Tilde Expansion (substituting a user's home directory) -A word beginning with an unquoted tilde character (~) is -subjected to tilde expansion. -Provided all of the subsequent characters in the word are unquoted -up to an unquoted slash (/) -or when in an assignment or not in posix mode, an unquoted colon (:), -or if neither of those appear, the end of the word, -they are treated as a user name -and are replaced with the pathname of the named user's home directory. -If the user name is missing (as in -.Pa ~/foobar ) , -the tilde is replaced with the value of the -.Dv HOME -variable (the current user's home directory). -.Pp -In variable assignments, -an unquoted tilde immediately after the assignment operator (=), and -each unquoted tilde immediately after an unquoted colon in the value -to be assigned is also subject to tilde expansion as just stated. -.\" -.\" -.Ss Parameter Expansion -.\" -The format for parameter expansion is as follows: -.Pp -.Dl ${ Ns Ar expression Ns Li } -.Pp -where -.Ar expression -consists of all characters until the matching -.Sq Li } . -Any -.Sq Li } -escaped by a backslash or within a quoted string, and characters in -embedded arithmetic expansions, command substitutions, and variable -expansions, are not examined in determining the matching -.Sq Li } . -.Pp -The simplest form for parameter expansion is: -.Pp -.Dl ${ Ns Ar parameter Ns Li } -.Pp -The value, if any, of -.Ar parameter -is substituted. -.Pp -The parameter name or symbol can be enclosed in braces, -which are optional in this simple case, -except for positional parameters with more than one digit or -when parameter is followed by a character that could be interpreted as -part of the name. -If a parameter expansion occurs inside double quotes: -.Bl -enum -.It -pathname expansion is not performed on the results of the expansion; -.It -field splitting is not performed on the results of the -expansion, with the exception of the special rules for -.Dv @ . \" $@ -.El -.Pp -In addition, a parameter expansion where braces are used, -can be modified by using one of the following formats. -If the -.Sq Ic \&: -is omitted in the following modifiers, then the test in the expansion -applies only to unset parameters, not null ones. -.Bl -tag -width aaparameterwordaaaaa -.It Li ${ Ns Ar parameter Ns Ic :- Ns Ar word Ns Li } -.Sy Use Default Values. -If -.Ar parameter -is unset or null, the expansion of -.Ar word -is substituted; otherwise, the value of -.Ar parameter -is substituted. -.It Li ${ Ns Ar parameter Ns Ic := Ns Ar word Ns Li } -.Sy Assign Default Values. -If -.Ar parameter -is unset or null, the expansion of -.Ar word -is assigned to -.Ar parameter . -In all cases, the final value of -.Ar parameter -is substituted. -Only variables, not positional parameters or special -parameters, can be assigned in this way. -.It Li ${ Ns Ar parameter Ns Ic :? Ns Oo Ar word\^ Oc Ns Li } -.Sy Indicate Error if Null or Unset. -If -.Ar parameter -is unset or null, the expansion of -.Ar word -(or a message indicating it is unset if -.Ar word -is omitted) -is written to standard error and a non-interactive shell exits with -a nonzero exit status. -An interactive shell will not exit, but any associated command(s) will -not be executed. -If the -.Ar parameter -is set, its value is substituted. -.It Li ${ Ns Ar parameter Ns Ic :+ Ns Ar word Ns Li } -.Sy Use Alternative Value. -If -.Ar parameter -is unset or null, null is substituted; -otherwise, the expansion of -.Ar word -is substituted. -The value of -.Ar parameter -.Em is not used -in this expansion. -.It Li ${ Ns Ic # Ns Ar parameter Ns Li } -.Sy String Length. -The length in characters of the value of -.Ar parameter . -.El -.Pp -The following four varieties of parameter expansion provide for substring -processing. -In each case, pattern matching notation (see -.Sx Shell Patterns ) , -rather than regular expression notation, is used to evaluate the patterns. -If parameter is -.Dv * -or -.Dv @ , \" $@ -the result of the expansion is unspecified. -Enclosing the full parameter expansion string in double quotes does not -cause the following four varieties of pattern characters to be quoted, -whereas quoting characters within the braces has this effect. -.Bl -tag -width aaparameterwordaaaaa -.It Li ${ Ns Ar parameter Ns Ic % Ns Ar word Ns Li } -.Sy Remove Smallest Suffix Pattern. -The -.Ar word -is expanded to produce a pattern. -The parameter expansion then results in -.Ar parameter , -with the -smallest portion of the suffix matched by the pattern deleted. -If the -.Ar word -is to start with a -.Sq Li \&% -character, it must be quoted. -.It Li ${ Ns Ar parameter Ns Ic %% Ns Ar word Ns Li } -.Sy Remove Largest Suffix Pattern. -The -.Ar word -is expanded to produce a pattern. -The parameter expansion then results in -.Ar parameter , -with the largest -portion of the suffix matched by the pattern deleted. -The -.Dq Ic %% -pattern operator only produces different results from the -.Dq Ic \&% -operator when the pattern contains at least one unquoted -.Sq Li \&* . -.It Li ${ Ns Ar parameter Ns Ic \&# Ns Ar word Ns Li } -.Sy Remove Smallest Prefix Pattern. -The -.Ar word -is expanded to produce a pattern. -The parameter expansion then results in -.Ar parameter , -with the -smallest portion of the prefix matched by the pattern deleted. -If the -.Ar word -is to start with a -.Sq Li \&# -character, it must be quoted. -.It Li ${ Ns Ar parameter Ns Ic \&## Ns Ar word Ns Li } -.Sy Remove Largest Prefix Pattern. -The -.Ar word -is expanded to produce a pattern. -The parameter expansion then results in -.Ar parameter , -with the largest -portion of the prefix matched by the pattern deleted. -This has the same relationship with the -.Dq Ic \&# -pattern operator as -.Dq Ic %% -has with -.Dq Ic \&% . -.El -.\" -.\" -.Ss Command Substitution -.\" -Command substitution allows the output of a command to be substituted in -place of the command (and surrounding syntax). -Command substitution occurs when a word contains -a command list enclosed as follows: -.Pp -.Dl Ic $( Ns Ar list Ns Ic \&) -.Pp -or the older -.Pq Dq backquoted -version, which is best avoided: -.Pp -.Dl Ic \&` Ns Ar list Ns Ns Ic \&` -.Pp -See the section -.Sx Complex Commands -above for the definition of -.Ic list . -.Pp -The shell expands the command substitution by executing the -.Ar list -in a sub-shell environment and replacing the command substitution with the -standard output of the -.Ar list -after removing any sequence of one or more -.Ao newline Ac Ns s -from the end of the substitution. -(Embedded -.Ao newline Ac Ns s -before -the end of the output are not removed; however, during field splitting, -they may be used to separate fields -.Pq "as spaces usually are" -depending on the value of -.Ev IFS -and any quoting that is in effect.) -.Pp -Note that if a command substitution includes commands -to be run in the background, -the sub-shell running those commands -will only wait for them to complete if an appropriate -.Ic wait -command is included in the command list. -However, the shell in which the result of the command substitution -will be used will wait for both the sub-shell to exit and for the -file descriptor that was initially standard output for the command -substitution sub-shell to be closed. -In some circumstances this might not happen until all processes -started by the command substitution have finished. -.\" While the exit of the sub-shell closes its standard output, -.\" any background process left running may still -.\" have that file descriptor open. -.\" This includes yet another sub-shell which might have been -.\" (almost invisibly) created to wait for some other command to complete, -.\" and even where the background command has had its -.\" standard output redirected or closed, -.\" the waiting sub-shell may still have it open. -.\" Thus there is no guarantee that the result of a command substitution -.\" will be available in the shell which is to use it before all of -.\" the commands started by the command substitution have completed, -.\" though careful coding can often avoid delays beyond the termination -.\" of the command substitution sub-shell. -.\" .Pp -.\" For example, assuming a script were to contain the following -.\" code (which could be done better other ways, attempting -.\" this kind of trickery is not recommended): -.\" .Bd -literal -offset indent -.\" if [ "$( printf "Ready? " >&2; read ans; printf "${ans}"; -.\" { sleep 120 >/dev/null && kill $$ >/dev/null 2>&1; }& )" = go ] -.\" then -.\" printf Working... -.\" # more code -.\" fi -.\" .Ed -.\" .Pp -.\" the -.\" .Dq Working... -.\" output will not be printed, and code that follows will never be executed. -.\" Nor will anything later in the script -.\" .Po -.\" unless -.\" .Dv SIGTERM -.\" is trapped or ignored -.\" .Pc . -.\" .Pp -.\" The intent is to prompt the user, wait for the user to -.\" answer, then if the answer was -.\" .Dq go -.\" do the appropriate work, but set a 2 minute time limit -.\" on completing that work. -.\" If the work is not done by then, kill the shell doing it. -.\" .Pp -.\" It will usually not work as written, as while the 2 minute -.\" .Sq sleep -.\" has its standard output redirected, as does the -.\" .Sq kill -.\" that follows (which also redirects standard error, -.\" so the user would not see an error if the work were -.\" completed and there was no parent process left to kill); -.\" the sub-shell waiting for the -.\" .Sq sleep -.\" to finish successfully, so it can start the -.\" .Sq kill , -.\" does not. -.\" It waits, with standard output still open, -.\" for the 2 minutes until the sleep is done, -.\" even though the kill is not going to need that file descriptor, -.\" and there is nothing else which could. -.\" The command substitution does not complete until after -.\" the kill has executed and the background sub-shell -.\" finishes \(en at which time the shell running it is -.\" presumably dead. -.\" .Pp -.\" Rewriting the background sub-shell part of that as -.\" .Dl "{ sleep 120 && kill $$ 2>&1; } >/dev/null &" -.\" would allow this -.\" .Nm -.\" to perform as expected, but that is not guaranteed, -.\" not all shells would behave as planned. -.\" It is advised to avoid starting background processes -.\" from within a command substitution. -.\" -.\" -.Ss Arithmetic Expansion -.\" -Arithmetic expansion provides a mechanism for evaluating an arithmetic -expression and substituting its value. -The format for arithmetic expansion is as follows: -.Pp -.Dl Ic $(( Ns Ar expression Ns Ic \&)) -.Pp -The expression in an arithmetic expansion is treated as if it were in -double quotes, except that a double quote character inside the expression -is just a normal character (it quotes nothing.) -The shell expands all tokens in the expression for parameter expansion, -command substitution, and quote removal (the only quoting character is -the backslash -.Sq \&\e , -and only when followed by another -.Sq \&\e , -a dollar sign -.Sq \&$ , -a backquote -.Sq \&` -or a newline.) -.Pp -Next, the shell evaluates the expanded result as an arithmetic expression -and substitutes the calculated value of that expression. -.Pp -Arithmetic expressions use a syntax similar to that -of the C language, and are evaluated using the -.Ql intmax_t -data type (this is an extension to POSIX, which requires only -.Ql long -arithmetic.) -Shell variables may be referenced by name inside an arithmetic -expression, without needing a -.Dq \&$ -sign. -Variables that are not set, or which have an empty (null string) value, -used this way evaluate as zero (that is, -.Dq x -in arithmetic, as an R-Value, is evaluated as -.Dq ${x:-0} ) -unless the -.Nm -.Fl u -flag is set, in which case a reference to an unset variable is an error. -Note that unset variables used in the ${var} form expand to a null -string, which might result in syntax errors. -Referencing the value of a variable which is not numeric is an error. -.Pp -All of the C expression operators applicable to integers are supported, -and operate as they would in a C expression. -Use white space, or parentheses, to disambiguate confusing syntax, -otherwise, as in C, the longest sequence of consecutive characters -which make a valid token (operator, variable name, or number) is taken -to be that token, even if the token designated cannot be used -and a different interpretation could produce a successful parse. -This means, as an example, that -.Dq a+++++b -is parsed as the gibberish sequence -.Dq "a ++ ++ + b" , -rather than as the valid alternative -.Dq "a ++ + ++ b" . -Similarly, separate the -.Sq \&, -operator from numbers with white space to avoid the possibility -of confusion with the decimal indicator in some locales (though -fractional, or floating-point, numbers are not supported in this -implementation.) -.Pp -It should not be necessary to state that the C operators which -operate on, or produce, pointer types, are not supported. -Those include unary -.Dq \&* -and -.Dq \&& -and the struct and array referencing binary operators: -.Dq \&. , -.Dq \&-> -and -.Dq \&[ . -.\" -.\" -.Ss White Space Splitting (Field Splitting) -.\" -After parameter expansion, command substitution, and -arithmetic expansion the shell scans the results of -expansions and substitutions that did not occur in double quotes, -and -.Dq Li $@ -even if it did, -for field splitting and multiple fields can result. -.Pp -The shell treats each character of the -.Ev IFS -as a delimiter and uses the delimiters to split the results of parameter -expansion and command substitution into fields. -.Pp -Non-whitespace characters in -.Ev IFS -are treated strictly as parameter separators. -So adjacent non-whitespace -.Ev IFS -characters will produce empty parameters. -On the other hand, any sequence of whitespace -characters that occur in -.Ev IFS -(known as -.Ev IFS -whitespace) -can occur, leading and trailing -.Ev IFS -whitespace, and any -.Ev IFS -whitespace surrounding a non whitespace -.Ev IFS -delimiter, is removed. -Any sequence of -.Ev IFS -whitespace characters without a non-whitespace -.Ev IFS -delimiter acts as a single field separator. -.Pp -If -.Ev IFS -is unset it is assumed to contain space, tab, and newline, -all of which are -.Ev IFS -whitespace characters. -If -.Ev IFS -is set to a null string, there are no delimiters, -and no field splitting occurs. -.Ss Pathname Expansion (File Name Generation) -Unless the -.Fl f -flag is set, file name generation is performed after word splitting is -complete. -Each word is viewed as a series of patterns, separated by slashes. -The process of expansion replaces the word with the names of all -existing files whose names can be formed by replacing each pattern with a -string that matches the specified pattern. -There are two restrictions on -this: first, a pattern cannot match a string containing a slash, and -second, a pattern cannot match a string starting with a period unless the -first character of the pattern is a period. -The next section describes the -patterns used for both Pathname Expansion and the -.Ic case -command. -.Ss Shell Patterns -A pattern consists of normal characters, which match themselves, -and meta-characters. -The meta-characters are -.Dq \&! , -.Dq * , -.Dq \&? , -and -.Dq \&[ . -These characters lose their special meanings if they are quoted. -When command or variable substitution is performed -and the dollar sign or backquotes are not double-quoted, -the value of the variable or the output of -the command is scanned for these characters and they are turned into -meta-characters. -.Pp -An asterisk -.Pq Dq * -matches any string of characters. -A question mark -.Pq Dq \&? -matches any single character. -A left bracket -.Pq Dq \&[ -introduces a character class. -The end of the character class is indicated by a right bracket -.Pq Dq \&] ; -if this -.Dq \&] -is missing then the -.Dq \&[ -matches a -.Dq \&[ -rather than introducing a character class. -A character class matches any of the characters between the square brackets. -A named class of characters (see -.Xr wctype 3 ) -may be specified by surrounding the name with -.Pq Dq [: -and -.Pq Dq :] . -For example, -.Pq Dq [[:alpha:]] -is a shell pattern that matches a single letter. -A range of characters may be specified using a minus sign -.Pq Dq \(mi . -The character class may be complemented -by making an exclamation mark -.Pq Dq \&! -the first character of the character class. -.Pp -To include a -.Dq \&] -in a character class, make it the first character listed (after the -.Dq \&! , -if any). -To include a -.Dq \(mi , -make it the first (after !) or last character listed. -If both -.Dq \&] -and -.Dq \(mi -are to be included, the -.Dq \&] -must be first (after !) -and the -.Dq \(mi -last, in the character class. -.\" -.\" -.Ss Built-ins -.\" -This section lists the built-in commands which are built-in because they -need to perform some operation that can't be performed by a separate -process. -Or just because they traditionally are. -In addition to these, there are several other commands that may -be built in for efficiency (e.g. -.Xr printf 1 , -.Xr echo 1 , -.Xr test 1 , -etc). -.Bl -tag -width 5n -.It Ic \&: Op Ar arg ... -A null command that returns a 0 (true) exit value. -Any arguments or redirects are evaluated, then ignored. -.\" -.It Ic \&. Ar file -The dot command reads and executes the commands from the specified -.Ar file -in the current shell environment. -The file does not need to be executable and is looked up from the directories -listed in the -.Ev PATH -variable if its name does not contain a directory separator -.Pq Sq / . -The -.Ic return -command (see below) -can be used for a premature return from the sourced file. -.Pp -The POSIX standard has been unclear on how loop control keywords (break -and continue) behave across a dot command boundary. -This implementation allows them to control loops surrounding the dot command, -but obviously such behavior should not be relied on. -It is now permitted by the standard, but not required. -.\" -.It Ic alias Op Ar name Ns Op Li = Ns Ar string ... -If -.Ar name Ns Li = Ns Ar string -is specified, the shell defines the alias -.Ar name -with value -.Ar string . -If just -.Ar name -is specified, the value of the alias -.Ar name -is printed. -With no arguments, the -.Ic alias -built-in prints the -names and values of all defined aliases (see -.Ic unalias ) . -.\" -.It Ic bg Op Ar job ... -Continue the specified jobs (or the current job if no -jobs are given) in the background. -.\" -.It Ic command Oo Fl pVv Oc Ar command Op Ar arg ... -Execute the specified command but ignore shell functions when searching -for it. -(This is useful when you -have a shell function with the same name as a command.) -.Bl -tag -width 5n -.It Fl p -search for command using a -.Ev PATH -that guarantees to find all the standard utilities. -.It Fl V -Do not execute the command but -search for the command and print the resolution of the -command search. -This is the same as the -.Ic type -built-in. -.It Fl v -Do not execute the command but -search for the command and print the absolute pathname -of utilities, the name for built-ins or the expansion of aliases. -.El -.\" -.It Ic cd Oo Fl P Oc Op Ar directory Op Ar replace -Switch to the specified directory (default -.Ev $HOME ) . -If -.Ar replace -is specified, then the new directory name is generated by replacing -the first occurrence of the string -.Ar directory -in the current directory name with -.Ar replace . -Otherwise if -.Ar directory -is -.Sq Li - , -then the current working directory is changed to the previous current -working directory as set in -.Ev OLDPWD . -Otherwise if an entry for -.Ev CDPATH -appears in the environment of the -.Ic cd -command or the shell variable -.Ev CDPATH -is set and the directory name does not begin with a slash, -and its first (or only) component isn't dot or dot dot, -then the directories listed in -.Ev CDPATH -will be searched for the specified directory. -The format of -.Ev CDPATH -is the same as that of -.Ev PATH . -.Pp -The -.Fl P -option instructs the shell to update -.Ev PWD -with the specified physical directory path and change to that directory. -This is the default. -.Pp -When the directory changes, the variable -.Ev OLDPWD -is set to the working directory before the change. -.Pp -Some shells also support a -.Fl L -option, which instructs the shell to update -.Ev PWD -with the logical path and to change the current directory -accordingly. -This is not supported. -.Pp -In an interactive shell, the -.Ic cd -command will print out the name of the -directory that it actually switched to if this is different from the name -that the user gave, -or always if the -.Cm cdprint -option is set. -The destination may be different either because the -.Ev CDPATH -mechanism was used -.\" or because a symbolic link was crossed. -or if the -.Ar replace -argument was used. -.\" -.It Ic eval Ar string ... -Concatenate all the arguments with spaces. -Then re-parse and execute the command. -.\" -.It Ic exec Op Ar command Op Ar arg ... -Unless -.Ar command -is omitted, the shell process is replaced with the -specified program (which must be a real program, not a shell built-in or -function). -Any redirections on the -.Ic exec -command are marked as permanent, so that they are not undone when the -.Ic exec -command finishes. -When the -.Cm posix -option is not set, -file descriptors created via such redirections are marked close-on-exec -(see -.Xr open 2 -.Dv O_CLOEXEC -or -.Xr fcntl 2 -.Dv F_SETFD / -.Dv FD_CLOEXEC ) , -unless the descriptors refer to the standard input, -output, or error (file descriptors 0, 1, 2). -Traditionally Bourne-like shells -(except -.Xr ksh 1 ) , -made those file descriptors available to exec'ed processes. -To be assured the close-on-exec setting is off, -redirect the descriptor to (or from) itself, -either when invoking a command for which the descriptor is wanted open, -or by using -.Ic exec -(perhaps the same -.Ic exec -as opened it, after the open) -to leave the descriptor open in the shell -and pass it to all commands invoked subsequently. -Alternatively, see the -.Ic fdflags -command below, which can set, or clear, this, and other, -file descriptor flags. -.\" -.It Ic exit Op Ar exitstatus -Terminate the shell process. -If -.Ar exitstatus -is given it is used as the exit status of the shell; otherwise the -exit status of the preceding command (the current value of $?) is used. -.\" -.It Ic export Oo Fl nx Oc Ar name Ns Oo =value Oc ... -.It Ic export Oo Fl x Oc Oo Fl p Oo Ar name ... Oc Oc -.It Ic export Fl q Oo Fl x Oc Ar name ... -With no options, -but one or more names, -the specified names are exported so that they will appear in the -environment of subsequent commands. -With -.Fl n -the specified names are un-exported. -Variables can also be un-exported using the -.Ic unset -built in command. -With -.Fl x -(exclude) the specified names are marked not to be exported, -and any that had been exported, will be un-exported. -Later attempts to export the variable will be refused. -Note this does not prevent explicitly exporting a variable -to a single command, script or function by preceding that -command invocation by a variable assignment to that variable, -provided the variable is not also read-only. -That is -.Bd -literal -offset indent -export -x FOO # FOO will now not be exported by default -export FOO # this command will fail (non-fatally) -FOO=some_value my_command -.Ed -.Pp -still passes the value -.Pq Li FOO=some_value -to -.Li my_command -through the environment. -.Pp -The shell allows the value of a variable to be set at the -same time it is exported (or unexported, etc) by writing -.Pp -.Dl export [-nx] name=value -.Pp -With no arguments the export command lists the names of all -set exported variables, -or if -.Fl x -was given, all set variables marked not for export. -With the -.Fl p -option specified, the output will be formatted suitably for -non-interactive use, and unset variables are included. -When -.Fl p -is given, variable names, but not values, may also be -given, in which case output is limited to the variables named. -.Pp -With -.Fl q -and a list of variable names, the -.Ic export -command will exit with status 0 if all the named -variables have been marked for export, or 1 if -any are not so marked. -If -.Fl x -is also given, -the test is instead for variables marked not to be exported. -.Pp -Other than with -.Fl q , -the -.Ic export -built-in exits with status 0, -unless an attempt is made to export a variable which has -been marked as unavailable for export, -in which cases it exits with status 1. -In all cases if -an invalid option, or option combination, is given, -or an invalid variable name is present, -.Ic export -will write a message to the standard error output, -and exit with a non-zero status. -A non-interactive shell will terminate. -.Pp -Note that there is no restriction upon exporting, -or un-exporting, read-only variables. -The no-export flag can be reset by unsetting the variable -and creating it again \(en provided the variable is not also read-only. -.\" -.It Ic fc Oo Fl e Ar editor Oc Op Ar first Op Ar last -.It Ic fc Fl l Oo Fl nr Oc Op Ar first Op Ar last -.It Ic fc Fl s Oo Ar old=new Oc Op Ar first -The -.Ic fc -built-in lists, or edits and re-executes, commands previously entered -to an interactive shell. -.Bl -tag -width 5n -.It Fl e Ar editor -Use the editor named by -.Ar editor -to edit the commands. -The -.Ar editor -string is a command name, subject to search via the -.Ev PATH -variable. -The value in the -.Ev FCEDIT -variable is used as a default when -.Fl e -is not specified. -If -.Ev FCEDIT -is null or unset, the value of the -.Ev EDITOR -variable is used. -If -.Ev EDITOR -is null or unset, -.Xr ed 1 -is used as the editor. -.It Fl l No (ell) -List the commands rather than invoking an editor on them. -The commands are written in the sequence indicated by -the first and last operands, as affected by -.Fl r , -with each command preceded by the command number. -.It Fl n -Suppress command numbers when listing with -.Fl l . -.It Fl r -Reverse the order of the commands listed (with -.Fl l ) -or edited (with neither -.Fl l -nor -.Fl s ) . -.It Fl s -Re-execute the command without invoking an editor. -.It Ar first -.It Ar last -Select the commands to list or edit. -The number of previous commands that -can be accessed are determined by the value of the -.Ev HISTSIZE -variable. -The value of -.Ar first -or -.Ar last -or both are one of the following: -.Bl -tag -width 5n -.It Oo Cm + Oc Ns Ar number -A positive number representing a command number; command numbers can be -displayed with the -.Fl l -option. -.It Cm \- Ns Ar number -A negative decimal number representing the command that was executed -number of commands previously. -For example, \-1 is the immediately previous command. -.El -.It Ar string -A string indicating the most recently entered command that begins with -that string. -If the -.Ar old Ns Li = Ns Ar new -operand is not also specified with -.Fl s , -the string form of the first operand cannot contain an embedded equal sign. -.El -.Pp -The following environment variables affect the execution of -.Ic fc : -.Bl -tag -width HISTSIZE -.It Ev FCEDIT -Name of the editor to use. -.It Ev HISTSIZE -The number of previous commands that are accessible. -.El -.\" -.It Ic fg Op Ar job -Move the specified job or the current job to the foreground. -A foreground job can interact with the user via standard input, -and receive signals from the terminal. -.\" -.It Ic fdflags Oo Fl v Oc Op Ar fd ... -.It Ic fdflags Oo Fl v Oc Fl s Ar flags fd Op ... -Get or set file descriptor flags. -The -.Fl v -argument enables verbose printing, printing flags that are also off, and -the flags of the file descriptor being set after setting. -The -.Fl s -flag interprets the -.Ar flags -argument as a comma separated list of file descriptor flags, each preceded -with a -.Dq \(pl -or a -.Dq \(mi -indicating to set or clear the respective flag. -Valid flags are: -.Cm append , -.Cm async , -.Cm sync , -.Cm nonblock , -.Cm fsync , -.Cm dsync , -.Cm rsync , -.Cm direct , -.Cm nosigpipe , -and -.Cm cloexec . -Unique abbreviations of these names, of at least 2 characters, -may be used on input. -See -.Xr fcntl 2 -and -.Xr open 2 -for more information. -.\" -.It Ic getopts Ar optstring var -The POSIX -.Ic getopts -command, not to be confused with the -Bell Labs\[en]derived -.Xr getopt 1 . -.Pp -The first argument should be a series of letters, each of which may be -optionally followed by a colon to indicate that the option requires an -argument. -The variable specified is set to the parsed option. -.Pp -The -.Ic getopts -command deprecates the older -.Xr getopt 1 -utility due to its handling of arguments containing whitespace. -.Pp -The -.Ic getopts -built-in may be used to obtain options and their arguments -from a list of parameters. -When invoked, -.Ic getopts -places the value of the next option from the option string in the list in -the shell variable specified by -.Ar var -and its index in the shell variable -.Ev OPTIND . -When the shell is invoked, -.Ev OPTIND -is initialized to 1. -For each option that requires an argument, the -.Ic getopts -built-in will place it in the shell variable -.Ev OPTARG . -If an option is not allowed for in the -.Ar optstring , -then -.Ev OPTARG -will be unset. -.Pp -.Ar optstring -is a string of recognized option letters (see -.Xr getopt 3 ) . -If a letter is followed by a colon, the option is expected to have an -argument which may or may not be separated from it by whitespace. -If an option character is not found where expected, -.Ic getopts -will set the variable -.Ar var -to a -.Sq Li \&? ; -.Ic getopts -will then unset -.Ev OPTARG -and write output to standard error. -By specifying a colon as the first character of -.Ar optstring -all errors will be ignored. -.Pp -A nonzero value is returned when the last option is reached. -If there are no remaining arguments, -.Ic getopts -will set -.Ar var -to the special option, -.Dq Li \-\- , -otherwise, it will set -.Ar var -to -.Sq Li \&? . -.Pp -The following code fragment shows how one might process the arguments -for a command that can take the options -.Fl a -and -.Fl b , -and the option -.Fl c , -which requires an argument. -.Bd -literal -offset indent -while getopts abc: f -do - case $f in - a | b) flag=$f;; - c) carg=$OPTARG;; - \e?) echo $USAGE; exit 1;; - esac -done -shift $((OPTIND - 1)) -.Ed -.Pp -This code will accept any of the following as equivalent: -.Bd -literal -offset indent -cmd \-acarg file file -cmd \-a \-c arg file file -cmd \-carg -a file file -cmd \-a \-carg \-\- file file -.Ed -.\" -.It Ic hash Oo Fl rv Oc Op Ar command ... -The shell maintains a hash table which remembers the -locations of commands. -With no arguments whatsoever, -the -.Ic hash -command prints out the contents of this table. -Entries which have not been looked at since the last -.Ic cd -command are marked with an asterisk; it is possible for these entries -to be invalid. -.Pp -With arguments, the -.Ic hash -command removes the specified commands from the hash table (unless -they are functions) and then locates them. -With the -.Fl v -option, hash prints the locations of the commands as it finds them. -The -.Fl r -option causes the hash command to delete all the entries in the hash table -except for functions. -.\" -.It Ic inputrc Ar file -Read the -.Ar file -to set key bindings as defined by -.Xr editrc 5 . -.\" -.It Ic jobid Oo Fl g Ns \&| Ns Fl j Ns \&| Ns Fl p Oc Op Ar job -With no flags, print the process identifiers of the processes in the job. -If the -.Ar job -argument is omitted, the current job is used. -Any of the ways to select a job may be used for -.Ar job , -including the -.Sq Li \&% -forms, or the process id of the job leader -.Po -.Dq Li \&$! -if the job was created in the background. -.Pc -.Pp -If one of the flags is given, then instead of the list of -process identifiers, the -.Ic jobid -command prints: -.Bl -tag -width ".Fl g" -.It Fl g -the process group, if one was created for this job, -or nothing otherwise (the job is in the same process -group as the shell.) -.It Fl j -the job identifier (using -.Dq Li \&% Ns Ar n -notation, where -.Ar n -is a number) is printed. -.It Fl p -only the process id of the process group leader is printed. -.El -.Pp -These flags are mutually exclusive. -.Pp -.Ic jobid -exits with status 2 if there is an argument error, -status 1, if with -.Fl g -the job had no separate process group, -or with -.Fl p -there is no process group leader (should not happen), -and otherwise exits with status 0. -.\" -.It Ic jobs Oo Fl l Ns \&| Ns Fl p Oc Op Ar job ... -Without -.Ar job -arguments, -this command lists out all the background processes -which are children of the current shell process. -With -.Ar job -arguments, the listed jobs are shown instead. -Without flags, the output contains the job -identifier (see -.Sx Job Control -below), an indicator character if the job is the current or previous job, -the current status of the job (running, suspended, or terminated successfully, -unsuccessfully, or by a signal) -and a (usually abbreviated) command string. -.Pp -With the -.Fl l -flag the output is in a longer form, with the process identifiers -of each process (run from the top level, as in a pipeline), and the -status of each process, rather than the job status. -.Pp -With the -.Fl p -flag, the output contains only the process identifier of the lead -process. -.Pp -In an interactive shell, each job shown as completed in the output -from the jobs command is implicitly waited for, and is removed from -the jobs table, never to be seen again. -In an interactive shell, when a background job terminates, the -.Ic jobs -command (with that job as an argument) is implicitly run just -before outputting the next PS1 command prompt, after the job -terminated. -This indicates that the job finished, shows its status, -and cleans up the job table entry for that job. -Non-interactive shells need to execute -.Ic wait -commands to clean up terminated background jobs. -.\" -.It Ic local Oo Fl INx Oc Oo Ar variable | \- Oc ... -Define local variables for a function. -Local variables have their attributes, and values, -as they were before the -.Ic local -declaration, restored when the function terminates. -.Pp -With the -.Fl N -flag, variables made local, are unset initially inside -the function. -Unless the -.Fl x -flag is also given, such variables are also unexported. -The -.Fl I -flag, which is the default in this shell, causes -the initial value and exported attribute -of local variables -to be inherited from the variable -with the same name in the surrounding -scope, if there is one. -If there is not, the variable is initially unset, -and not exported. -The -.Fl N -and -.Fl I -flags are mutually exclusive, if both are given, the last specified applies. -The read-only and unexportable attributes are always -inherited, if a variable with the same name already exists. -.Pp -The -.Fl x -flag (lower case) causes the local variable to be exported, -while the function runs, unless it has the unexportable attribute. -This can also be accomplished by using the -.Ic export -command, giving the same -.Ar variable -names, after the -.Ic local -command. -.Pp -Making an existing read-only variable local is possible, -but pointless. -If an attempt is made to assign an initial value to such -a variable, the -.Ic local -command fails, as does any later attempted assignment. -If the -.Ic readonly -command is applied to a variable that has been declared local, -the variable cannot be (further) modified within the function, -or any other functions it calls, however when the function returns, -the previous status (and value) of the variable is returned. -.Pp -Values may be given to local variables on the -.Ic local -command line in a similar fashion as used for -.Ic export -and -.Ic readonly . -These values are assigned immediately after the initialization -described above. -Note that any variable references on the command line will have -been expanded before -.Ic local -is executed, so expressions like -.Pp -.Dl "local -N X=${X}" -.Pp -are well defined, first $X is expanded, and then the command run is -.Pp -.Dl "local -N X=old-value-of-X" -.Pp -After arranging to preserve the old value and attributes, of -.Dv X -.Dq ( old-value-of X ) -.Ic local -unsets -.Dv X , -unexports it, and then assigns the -.Dq old-value-of-X -to -.Ev X . -.Pp -The shell uses dynamic scoping, so that if you make the variable -.Dv x -local to -function -.Dv f , -which then calls function -.Dv g , -references to the variable -.Dv x -made inside -.Dv g -will refer to the variable -.Dv x -declared inside -.Dv f , -not to the global variable named -.Dv x . -.Pp -Another way to view this, is as if the shell just has one flat, global, -namespace, in which all variables exist. -The -.Ic local -command conceptually copies the variable(s) named to unnamed temporary -variables, and when the function ends, copies them back again. -All references to the variables reference the same global variables, -but while the function is active, after the -.Ic local -command has run, the values and attributes of the variables might -be altered, and later, when the function completes, be restored. -.Pp -Note that the positional parameters -.Dv 1 , \" $1 -.Dv 2 , \" $2 -\&... (see -.Sx Positional Parameters ) , -and the special parameters -.Dv \&# , \" $# -.Dv \&* \" $* -and -.Dv \&@ \" $@ -(see -.Sx Special Parameters ) , -are always made local in all functions, and are reset inside the -function to represent the options and arguments passed to the function. -Note that -.Li $0 -however retains the value it had outside the function, -as do all the other special parameters. -.Pp -The only special parameter that can optionally be made local is -.Dq Li \- . -Making -.Dq Li \- -local causes any shell options that are changed via the set command inside the -function to be restored to their original values when the function -returns. -If -.Fl X -option is altered after -.Dq Li \- -has been made local, then when the function returns, the previous -destination for -.Cm xtrace -output (as of the time of the -.Ic local -command) will also be restored. -If any of the shell's magic variables -(those which return a value which may vary without -the variable being explicitly altered, -e.g.: -.Dv SECONDS -or -.Dv HOSTNAME ) -are made local in a function, -they will lose their special properties when set -within the function, including by the -.Ic local -command itself -(if not to be set in the function, there is little point -in making a variable local) -but those properties will be restored when the function returns. -.Pp -It is an error to use -.Ic local -outside the scope of a function definition. -When used inside a function, it exits with status 0, -unless an undefined option is used, or an attempt is made to -assign a value to a read-only variable. -.Pp -Note that either -.Fl I -or -.Fl N -should always be used, or variables made local should always -be given a value, or explicitly unset, as the default behavior -(inheriting the earlier value, or starting unset after -.Ic local ) -differs amongst shell implementations. -Using -.Dq Li local \&\- -is an extension not implemented by most shells. -.Pp -See the section -.Sx LINENO -below for details of the effects of making the variable -.Dv LINENO -local. -.\" -.It Ic pwd Op Fl \&LP -Print the current directory. -If -.Fl L -is specified the cached value (initially set from -.Ev PWD ) -is checked to see if it refers to the current directory; if it does -the value is printed. -Otherwise the current directory name is found using -.Xr getcwd 3 . -The environment variable -.Ev PWD -is set to the printed value. -.Pp -The default is -.Ic pwd -.Fl L , -but note that the built-in -.Ic cd -command doesn't support the -.Fl L -option and will cache (almost) the absolute path. -If -.Ic cd -is changed (as unlikely as that is), -.Ic pwd -may be changed to default to -.Ic pwd -.Fl P . -.Pp -If the current directory is renamed and replaced by a symlink to the -same directory, or the initial -.Ev PWD -value followed a symbolic link, then the cached value may not -be the absolute path. -.Pp -The built-in command may differ from the program of the same name because -the program will use -.Ev PWD -and the built-in uses a separately cached value. -.\" -.It Ic read Oo Fl p Ar prompt Oc Oo Fl r Oc Ar variable Op Ar ... -The -.Ar prompt -is printed if the -.Fl p -option is specified and the standard input is a terminal. -Then a line is read from the standard input. -The trailing newline is deleted from the -line and the line is split as described in the field splitting section of the -.Sx Word Expansions -section above, and the pieces are assigned to the variables in order. -If there are more pieces than variables, the remaining pieces -(along with the characters in -.Ev IFS -that separated them) are assigned to the last variable. -If there are more variables than pieces, -the remaining variables are assigned the null string. -The -.Ic read -built-in will indicate success unless EOF is encountered on input, in -which case failure is returned. -.Pp -By default, unless the -.Fl r -option is specified, the backslash -.Dq \e -acts as an escape character, causing the following character to be treated -literally. -If a backslash is followed by a newline, the backslash and the -newline will be deleted. -.\" -.It Ic readonly Ar name Ns Oo =value Oc ... -.It Ic readonly Oo Fl p Oo Ar name ... Oc Oc -.It Ic readonly Fl q Ar name ... -With no options, -the specified names are marked as read only, so that they cannot be -subsequently modified or unset. -The shell allows the value of a variable -to be set at the same time it is marked read only by writing -.Pp -.Dl readonly name=value -.Pp -With no arguments the -.Ic readonly -command lists the names of all set read only variables. -With the -.Fl p -option specified, -the output will be formatted suitably for non-interactive use, -and unset variables are included. -When the -.Fl p -option is given, -a list of variable names (without values) may also be specified, -in which case output is limited to the named variables. -.Pp -With the -.Fl q -option, the -.Ic readonly -command tests the read-only status of the variables listed -and exits with status 0 if all named variables are read-only, -or with status 1 if any are not read-only. -.Pp -Other than as specified for -.Fl q -the -.Ic readonly -command normally exits with status 0. -In all cases, if an unknown option, or an invalid option combination, -or an invalid variable name, is given; -or a variable which was already read-only is attempted to be set; -the exit status will not be zero, a diagnostic -message will be written to the standard error output, -and a non-interactive shell will terminate. -.\" -.It Ic return Op Ar n -Stop executing the current function or a dot command with return value of -.Ar n -or the value of the last executed command, if not specified. -For portability, -.Ar n -should be in the range from 0 to 255. -.Pp -The POSIX standard says that the results of -.Ic return -outside a function or a dot command are unspecified. -This implementation treats such a return as a no-op with a return value of 0 -(success, true). -Use the -.Ic exit -command instead, if you want to return from a script or exit -your shell. -.\" -.It Ic set Oo { Fl options | Cm +options | Cm \-- } Oc Ar arg ... -The -.Ic set -command performs four different functions. -.Pp -With no arguments, it lists the values of all shell variables. -.Pp -With a single option of either -.Dq Fl o -or -.Dq Cm +o -.Ic set -outputs the current values of the options. -In the -.Fl o -form, all options are listed, with their current values. -In the -.Cm +o -form, the shell outputs a string that can later be used -as a command to reset all options to their current values. -.Pp -If options are given, it sets the specified option -flags, or clears them as described in the -.Sx Argument List Processing -section. -In addition to the options listed there, -when the -.Dq "option name" -given to -.Ic set Fl o -is -.Cm default -all of the options are reset to the values they had immediately -after -.Nm -initialization, before any startup scripts, or other input, had been processed. -While this may be of use to users or scripts, its primary purpose -is for use in the output of -.Dq Ic set Cm +o , -to avoid that command needing to list every available option. -There is no -.Cm +o default . -.Pp -The fourth use of the -.Ic set -command is to set the values of the shell's -positional parameters to the specified arguments. -To change the positional -parameters without changing any options, use -.Dq -\|- -as the first argument to -.Ic set . -If no following arguments are present, the -.Ic set -command -will clear all the positional parameters (equivalent to executing -.Dq Li shift $# . ) -Otherwise the following arguments become -.Li \&$1 , -.Li \&$2 , -\&..., -and -.Li \&$# -is set to the number of arguments present. -.\" -.It Ic setvar Ar variable Ar value -Assigns -.Ar value -to -.Ar variable . -(In general it is better to write -.Li variable=value -rather than using -.Ic setvar . -.Ic setvar -is intended to be used in -functions that assign values to variables whose names are passed as -parameters.) -.\" -.It Ic shift Op Ar n -Shift the positional parameters -.Ar n -times. -If -.Ar n -is omitted, 1 is assumed. -Each -.Ic shift -sets the value of -.Li $1 -to the previous value of -.Li $2 , -the value of -.Li $2 -to the previous value of -.Li $3 , -and so on, decreasing -the value of -.Li $# -by one. -The shift count must be less than or equal to the number of -positional parameters ( -.Dq Li $# ) -before the shift. -.\" -.It Ic times -Prints two lines to standard output. -Each line contains two accumulated time values, expressed -in minutes and seconds (including fractions of a second.) -The first value gives the user time consumed, the second the system time. -.Pp -The first output line gives the CPU and system times consumed by the -shell itself. -The second line gives the accumulated times for children of this -shell (and their descendants) which have exited, and then been -successfully waited for by the relevant parent. -See -.Xr times 3 -for more information. -.Pp -.Ic times -has no parameters, and exits with an exit status of 0 unless -an attempt is made to give it an option. -.\" -.It Ic trap Ar action signal ... -.It Ic trap \- -.It Ic trap Op Fl l -.It Ic trap Oo Fl p Oc Ar signal ... -.It Ic trap Ar N signal ... -.Pp -Cause the shell to parse and execute action when any of the specified -signals are received. -The signals are specified by signal number or as the name of the signal. -If -.Ar signal -is -.Li 0 \" $0 -or its equivalent, -.Li EXIT , -the action is executed when the shell exits. -The -.Ar action -may be a null (empty) string, -which causes the specified signals to be ignored. -With -.Ar action -set to -.Sq Li - -the specified signals are set to their default actions. -If the first -.Ar signal -is specified in its numeric form, then -.Ar action -can be omitted to achieve the same effect. -This archaic, -but still standard, -form should not be relied upon, use the explicit -.Sq Li - -action. -If no signals are specified with an action of -.Sq Li - , -all signals are reset. -.Pp -When the shell forks off a sub-shell, it resets trapped (but not ignored) -signals to the default action. -On non-interactive shells, the -.Ic trap -command has no effect on signals that were -ignored on entry to the shell. -On interactive shells, the -.Ic trap -command will catch or reset signals ignored on entry. -.Pp -Issuing -.Ic trap -with option -.Fl l -will print a list of valid signal names. -.Ic trap -without any arguments causes it to write a list of signals and their -associated non-default actions to the standard output in a format -that is suitable as an input to the shell that achieves the same -trapping results. -With the -.Fl p -flag, trap prints the same information for the signals specified, -or if none are given, for all signals, including those where the -action is the default. -These variants of the trap command may be executed in a sub-shell -.Pq "such as in a command substitution" , -provided they appear as the sole, or first, command in that sub-shell, -in which case the state of traps from the parent of that -sub-shell is reported. -.Pp -Examples: -.Pp -.Dl trap -.Pp -List trapped signals and their corresponding actions. -.Pp -.Dl trap -l -.Pp -Print a list of valid signals. -.Pp -.Dl trap '' INT QUIT tstp 30 -.Pp -Ignore signals INT QUIT TSTP USR1. -.Pp -.Dl trap date INT -.Pp -Run the -.Dq date -command (print the date) upon receiving signal INT. -.Pp -.Dl trap HUP INT -.Pp -Run the -.Dq HUP -command, or function, upon receiving signal INT. -.Pp -.Dl trap 1 2 -.Pp -Reset the actions for signals 1 (HUP) and 2 (INT) to their defaults. -.Bd -literal -offset indent -traps=$(trap -p) - # more commands ... -trap 'action' SIG - # more commands ... -eval "$traps" -.Ed -.Pp -Save the trap status, execute commands, changing some traps, -and then reset all traps to their values at the start of the sequence. -The -.Fl p -option is required in the first command here, -or any signals that were previously -untrapped (in their default states) -and which were altered during the intermediate code, -would not be reset by the final -.Ic eval . -.\" -.It Ic type Op Ar name ... -Interpret each -.Ar name -as a command and print the resolution of the command search. -Possible resolutions are: -shell keyword, alias, shell built-in, -command, tracked alias and not found. -For aliases the alias expansion is -printed; for commands and tracked aliases the complete pathname of the -command is printed. -.\" -.It Ic ulimit Oo Fl H Ns \*(Ba Ns Fl S Oc Op Fl a \*(Ba Fl btfdscmlrpnv Op Ar value -Inquire about or set the hard or soft limits on processes or set new -limits. -The choice between hard limit (which no process is allowed to -violate, and which may not be raised once it has been lowered) and soft -limit (which causes processes to be signaled but not necessarily killed, -and which may be raised) is made with these flags: -.Bl -tag -width Fl -.It Fl H -set or inquire about hard limits -.It Fl S -set or inquire about soft limits. -.El -.Pp -If neither -.Fl H -nor -.Fl S -is specified, the soft limit is displayed or both limits are set. -If both are specified, the last one wins. -.Pp -The limit to be interrogated or set, then, is chosen by specifying -any one of these flags: -.Bl -tag -width Fl -.It Fl a -show all the current limits -.It Fl b -the socket buffer size of a process (bytes) -.It Fl c -the largest core dump size that can be produced -(512-byte blocks) -.It Fl d -the data segment size of a process (kilobytes) -.It Fl f -the largest file that can be created -(512-byte blocks) -.It Fl l -how much memory a process can lock with -.Xr mlock 2 -(kilobytes) -.It Fl m -the total physical memory that can be -in use by a process (kilobytes) -.It Fl n -the number of files a process can have open at once -.It Fl p -the number of processes this user can -have at one time -.It Fl r -the number of threads this user can -have at one time -.It Fl s -the stack size of a process (kilobytes) -.It Fl t -CPU time (seconds) -.It Fl v -how large a process address space can be -.El -.Pp -If none of these is specified, it is the limit on file size that is shown -or set. -If value is specified, the limit is set to that number; otherwise -the current limit is displayed. -.Pp -Limits of an arbitrary process can be displayed or set using the -.Xr sysctl 8 -utility. -.It Ic umask Oo Fl S Oc Op Ar mask -Set the value of umask (see -.Xr umask 2 ) -to the specified octal value. -If the argument is omitted, the umask value is printed. -With -.Fl S -a symbolic form is used instead of an octal number. -.It Ic unalias Oo Fl a Oc Op Ar name -If -.Ar name -is specified, the shell removes that alias. -If -.Fl a -is specified, all aliases are removed. -.It Ic unset Oo Fl efvx Oc Ar name ... -If -.Fl v -is specified, the specified variables are unset and unexported. -Readonly variables cannot be unset. -If -.Fl f -is specified, the specified functions are undefined. -If -.Fl e -is given, the specified variables are unexported, but otherwise unchanged, -alternatively, if -.Fl x -is given, the exported status of the variable will be retained, -even after it is unset. -.Pp -If no flags are provided -.Fl v -is assumed. -If -.Fl f -is given with one of the other flags, -then the named variables will be unset, or unexported, and functions -of the same names will be undefined. -The -.Fl e -and -.Fl x -flags both imply -.Fl v . -If -.Fl e -is given, the -.Fl x -flag is ignored. -.Pp -The exit status is 0, unless an attempt was made to unset -a readonly variable, in which case the exit status is 1. -It is not an error to unset (or undefine) a variable (or function) -that is not currently set (or defined.) -.\" -.It Ic wait Oo Fl n Oc Oo Fl p Ar var Oc Op Ar job ... -Wait for the specified jobs to complete -and return the exit status of the last job in the parameter list, -or 127 if that job is not a current child of the shell. -.Pp -If no -.Ar job -arguments are given, wait for all jobs to -complete and then return an exit status of zero -(including when there were no jobs, and so nothing exited.) -.Pp -With the -.Fl n -option, wait instead for any one of the given -.Ar job Ns s, -or if none are given, any job, to complete, and -return the exit status of that job. -If none of the given -.Ar job -arguments is a current child of the shell, -or if no -.Ar job -arguments are given and the shell has no unwaited for children, -then the exit status will be 127. -.Pp -The -.Fl p Ar var -option allows the process (or job) identifier of the -job for which the exit status is returned to be obtained. -The variable named (which must not be readonly) will be -unset initially, then if a job has exited and its status is -being returned, set to the identifier from the -arg list (if given) of that job, -or the lead process identifier of the job to exit when used with -.Fl n -and no job arguments. -Note that -.Fl p -with neither -.Fl n -nor -.Ar job -arguments is useless, as in that case no job status is -returned, the variable named is simply unset. -.Pp -If the wait is interrupted by a signal, -its exit status will be greater than 128, -and -.Ar var , -if given, will remain unset. -.Pp -Once waited upon, by specific process number or job-id, -or by a -.Ic wait -with no arguments, -knowledge of the child is removed from the system, -and it cannot be waited upon again. -.Pp -Note than when a list of jobs are given, more that -one argument might refer to the same job. -In that case, if the final argument represents a job -that is also given earlier in the list, it is not -defined whether the status returned will be the -exit status of the job, or 127 indicating that -the child no longer existed when the wait command -reached the later argument in the list. -In this -.Nm -the exit status will be that from the job. -.Nm -waits for each job exactly once, regardless of -how many times (or how many different ways) it -is listed in the arguments to -.Ic wait . -That is -.Bd -literal -offset indent -compact -wait 100 100 100 -.Ed -is identical to -.Bd -literal -offset indent -compact -wait 100 -.Ed -.El -.\" -.\" -.Ss Job Control -.\" -Each process (or set of processes) started by -.Nm -is created as a -.Dq job -and added to the jobs table. -When enabled by the -.Fl m -option -.Pq aka Fl o Cm monitor -when the job is created, -.Nm -places each job (if run from the top level shell) -into a process group of its own, which allows control -of the process(es), and its/their descendants, as a unit. -When the -.Fl m -option is off, or when started from a sub-shell environment, -jobs share the same process group as the parent shell. -The -.Fl m -option is enabled by default in interactive shells with -a terminal as standard input and standard error. -.Pp -Jobs with separate process groups may be stopped, and then later -resumed in the foreground (with access to the terminal) -or in the background (where attempting to read from the -terminal will result in the job stopping.) -A list of current jobs can be obtained using the -.Ic jobs -built-in command. -Jobs are identified using either the process identifier -of the lead process of the job (the value available in -the special parameter -.Dq Dv \&! -if the job is started in the background), or using percent -notation. -Each job is given a -.Dq job number -which is a small integer, starting from 1, and can be -referenced as -.Dq Li \&% Ns Ar n -where -.Ar n -is that number. -Note that this applies to jobs both with and without their own process groups. -Job numbers are shown in the output from the -.Ic jobs -command enclosed in brackets -.Po -.Sq Li \&[ -and -.Sq Li \&] -.Pc . -Whenever the job table becomes empty, the numbers begin at one again. -In addition, there is the concept of a current, and a previous job, -identified by -.Dq Li \&%+ -.Po -or -.Dq Li \&%% -or even just -.Dq Li \&% -.Pc , -and a previous job, identified by -.Dq Li \&%\- . -Whenever a background job is started, -or a job is resumed in the background, -it becomes the current job. -The job that was the current job -(prepare for a big surprise here, drum roll..., wait for it...\&) -becomes the previous job. -When the current job terminates, the previous job is -promoted to be the current job. -In addition the form -.Dq Li \&% Ns Ar string\^ -finds the job for which the command starts with -.Ar string -and the form -.Dq Li \&%? Ns Ar string\^ -finds the job which contains the -.Ar string -in its command somewhere. -Both forms require the result to be unambiguous. -For this purpose the -.Dq command -is that shown in the output from the -.Ic jobs -command, not the original command line. -.Pp -The -.Ic bg , -.Ic fg , -.Ic jobid , -.Ic jobs , -.Ic kill , -and -.Ic wait -commands all accept job identifiers as arguments, in addition to -process identifiers (larger integers). -See the -.Sx Built-ins -section above, and -.Xr kill 1 , -for more details of those commands. -In addition, a job identifier -(using one of the -.Dq \&% forms ) -issued as a command, without arguments, is interpreted as -if it had been given as the argument to the -.Ic fg -command. -.Pp -To cause a foreground process to stop, enter the terminal's -.Ic stop -character (usually control-Z). -To cause a background process to stop, send it a -.Dv STOP -signal, using the kill command. -A useful function to define is -.Bd -literal -offset indent -stop() { kill -s STOP "${@:-%%}"; } -.Ed -.Pp -The -.Ic fg -command resumes a stopped job, placing it in the foreground, -and -.Ic bg -resumes a stopped job in the background. -The -.Ic jobid -command provides information about process identifiers, job identifiers, -and the process group identifier, for a job. -.Pp -Whenever a sub-shell is created, the jobs table becomes invalid -(the sub-shell has no children.) -However, to enable uses like -.Bd -literal -offset indent -PID=$(jobid -p %1) -.Ed -.Pp -the table is only actually cleared in a sub-shell when needed to -create the first job there (built-in commands run in the foreground -do not create jobs.) -Note that in this environment, there is no useful current job -.Dq ( Li \&%% -actually refers to the sub-shell itself, but is not accessible) -but the job which is the current job in the parent can be accessed as -.Dq Li \&%\- . -.\" -.\" -.Ss Command Line Editing -.\" -When -.Nm -is being used interactively from a terminal, the current command -and the command history (see -.Ic fc -in the -.Sx Built-ins -section) -can be edited using emacs-mode or vi-mode command-line editing. -The command -.Ql set -o emacs -(or -.Fl E -option) -enables emacs-mode editing. -The command -.Ql set -o vi -(or -.Fl V -option) -enables vi-mode editing and places the current shell process into -vi insert mode. -(See the -.Sx Argument List Processing -section above.) -.Pp -The vi-mode uses commands similar to a subset of those described in the -.Xr vi 1 -man page. -With vi-mode -enabled, -.Nm sh -can be switched between insert mode and command mode. -It's similar to -.Ic vi : -pressing the -.Aq ESC -key will throw you into vi command mode. -Pressing the -.Aq return -key while in command mode will pass the line to the shell. -.Pp -The emacs-mode uses commands similar to a subset available in the -.Ic emacs -editor. -With emacs-mode enabled, special keys can be used to modify the text -in the buffer using the control key. -.Pp -.Nm -uses the -.Xr editline 3 -library. -See -.Xr editline 7 -for a list of the possible command bindings, -and the default settings in emacs and vi modes. -Also see -.Xr editrc 5 -for the commands that can be given to configure -.Xr editline 7 -in the file named by the -.Ev EDITRC -parameter, -or a file used with the -.Ic inputrc -built-in command, -or using -.Xr editline 7 Ap s -configuration command line. -.Pp -When command line editing is enabled, the -.Xr editline 7 -functions control printing of the -.Ev PS1 -and -.Ev PS2 -prompts when required. -As, in this mode, the command line editor needs to -keep track of what characters are in what position on -the command line, care needs to be taken when setting -the prompts. -Normal printing characters are handled automatically, -however mode setting sequences, which do not actually display -on the terminal, need to be identified to -.Xr editline 7 . -This is done, when needed, by choosing a character that -is not needed anywhere in the prompt, including in the mode -setting sequences, any single character is acceptable, -and assigning it to the shell parameter -.Dv PSlit . -Then that character should be used, in pairs, in the -prompt string. -Between each pair of -.Dv PSlit -characters are mode setting sequences, which affect the printing -attributes of the following (normal) characters of the prompt, -but do not themselves appear visibly, nor change the terminal's -cursor position. -.Pp -Each such sequence, that is -.Dv PSlit -character, mode setting character sequence, and another -.Dv PSlit -character, must currently be followed by at least one following -normal prompt character, or it will be ignored. -That is, a -.Dv PSlit -character cannot be the final character of -.Ev PS1 -or -.Ev PS2 , -nor may two -.Dv PSlit -delimited sequences appear adjacent to each other. -Each sequence can contain as many mode altering sequences as are -required however. -Only the first character from -.Dv PSlit -will be used. -When set -.Dv PSlit -should usually be set to a string containing just one -character, then it can simply be embedded in -.Ev PS1 -(or -.Ev PS2 ) -as in -.Pp -.D1 Li PS1=\*q${PSlit} Ns Ar mset\^ Ns Li ${PSlit}XYZ${PSlit} Ns Ar mclr\^ Ns Li ${PSlit}ABC\*q -.Pp -The prompt visible will be -.Dq XYZABC -with the -.Dq XYZ -part shown according as defined by the mode setting characters -.Ar mset , -and then cleared again by -.Ar mclr . -See -.Xr tput 1 -for one method to generate appropriate mode sequences. -Note that both parts, XYZ and ABC, must each contain at least one -character. -.Pp -If -.Dv PSlit -is unset, which is its initial state, or set to a null string, -no literal character will be defined, -and all characters of the prompt strings will be assumed -to be visible characters (which includes spaces etc.) -To allow smooth use of prompts, without needing redefinition, when -.Xr editline 7 -is disabled, the character chosen should be one which will be -ignored by the terminal if received, as when -.Xr editline 7 -is not in use, the prompt strings are simply written to the terminal. -For example, setting: -.\" XXX: PS1 line is too long for -offset indent -.Bd -literal -offset left - PSlit="$(printf\ '\e1')" - PS1="${PSlit}$(tput\ bold\ blink)${PSlit}\e$${PSlit}$(tput\ sgr0)${PSlit}\ " -.Ed -.Pp -will arrange for the primary prompt to be a bold blinking dollar sign, -if supported by the current terminal, followed by an (ordinary) space, -and, as the SOH (control-A) character -.Pq Sq \e1 -will not normally affect -a terminal, this same prompt will usually work with -.Xr editline 7 -enabled or disabled. -.Sh ENVIRONMENT -.Bl -tag -width MAILCHECK -.It Ev CDPATH -The search path used with the -.Ic cd -built-in. -.It Ev EDITRC -Gives the name of the file containing commands for -.Xr editline 7 . -See -.Xr editrc 5 -for possible content and format. -The file is processed, when in interactive mode with -command line editing enabled, whenever -.Ev EDITRC -is set (even with no actual value change,) -and if command line editing changes from disabled to enabled, -or the editor style used is changed. -(See the -.Fl E -and -.Fl V -options of the -.Ic set -built-in command, described in -.Sx Built-ins -above, which are documented further above in -.Sx Argument List Processing . ) -If unset -.Dq $HOME/.editrc -is used. -.It Ev ENV -Names the file sourced at startup by the shell. -Unused by this shell after initialization, -but is usually passed through the environment to -descendant shells. -.It Ev EUSER -Set to the login name of the effective user id running the shell, -as returned by -.Bd -compact -literal -offset indent -getpwuid(geteuid())->pw_name -.Ed -.Po -See -.Xr getpwuid 3 -and -.Xr geteuid 2 -for more details. -.Pc -This is obtained each time -.Ev EUSER -is expanded, so changes to the shell's execution identity -cause updates without further action. -If unset, it returns nothing. -If set it loses its special properties, and is simply a variable. -.It Ev HISTSIZE -The number of lines in the history buffer for the shell. -.It Ev HOME -Set automatically by -.Xr login 1 -from the user's login directory in the password file -.Pq Xr passwd 5 . -This environment variable also functions as the default argument for the -.Ic cd -built-in. -.It Ev HOSTNAME -Set to the current hostname of the system, as returned by -.Xr gethostname 3 . -This is obtained each time -.Ev HOSTNAME -is expanded, so changes to the system's name are reflected -without further action. -If unset, it returns nothing. -If set it loses its special properties, and is simply a variable. -.It Ev IFS -Input Field Separators. -This is normally set to -.Aq space , -.Aq tab , -and -.Aq newline . -See the -.Sx White Space Splitting -section for more details. -.It Ev LANG -The string used to specify localization information that allows users -to work with different culture-specific and language conventions. -See -.Xr nls 7 . -.It Dv LINENO -The current line number in the script or function. -See the section -.Sx LINENO -below for more details. -.It Ev MAIL -The name of a mail file, that will be checked for the arrival of new mail. -Overridden by -.Ev MAILPATH . -The check occurs just before -.Ev PS1 -is written, immediately after reporting jobs which have changed status, -in interactive shells only. -New mail is considered to have arrived if the monitored file -has increased in size since the last check. -.\" .It Ev MAILCHECK -.\" The frequency in seconds that the shell checks for the arrival of mail -.\" in the files specified by the -.\" .Ev MAILPATH -.\" or the -.\" .Ev MAIL -.\" file. -.\" If set to 0, the check will occur at each prompt. -.It Ev MAILPATH -A colon -.Dq \&: -separated list of file names, for the shell to check for incoming mail. -This environment setting overrides the -.Ev MAIL -setting. -There is a maximum of 10 mailboxes that can be monitored at once. -.It Ev PATH -The default search path for executables. -See the -.Sx Path Search -section above. -.It Ev POSIXLY_CORRECT -If set in the environment upon initialization of the shell, -then the shell option -.Ic posix -will be set. -.Po -See the description of the -.Ic set -command in the -.Sx Built-ins -section. -.Pc -After initialization it is unused by the shell, -but is usually passed through the environment to -descendant processes, including other instances of the shell, -which may interpret it in a similar way. -.It Ev PPID -The process identified of the parent process of the -current shell. -This value is set at shell startup, ignoring -any value in the environment, and then made readonly. -.It Ev PS1 -The primary prompt string, which defaults to -.Dq Li "$ " , -unless you are the superuser, in which case it defaults to -.Dq Li "# " . -This string is subject to parameter, arithmetic, and if -enabled by setting the -.Ic promptcmds -option, command substitution before being output. -During execution of commands used by command substitution, -execution tracing, the -.Ic xtrace -.Ic ( set Fl x ) -option is temporarily disabled. -If -.Ic promptcmds -is not set and the prompt string uses command substitution, -the prompt used will be an appropriate error string. -For other expansion errors, a message will be output, -and the unexpanded string will then be used as the prompt. -.It Ev PS2 -The secondary prompt string, which defaults to -.Dq Li "> " . -After expansion (as for -.Ev PS1 ) -it is written whenever more input is required to complete the -current command. -.It Ev PS4 -Output, after expansion like -.Ev PS1 , -before each line when execution trace -.Ic ( set Fl x ) -is enabled. -.Ev PS4 -defaults to -.Dq Li "+ " . -.It Ev PSc -Initialized by the shell, ignoring any value from the environment, -to a single character string, either -.Sq \&# -or -.Sq \&$ , -depending upon whether the current user is the superuser or not. -This is intended for use when building a custom -.Ev PS1 . -.It Ev PSlit -Defines the character which may be embedded in pairs, in -.Ev PS1 -or -.Ev PS2 -to indicate to -.Xr editline 7 -that the characters between each pair of occurrences of the -.Dv PSlit -character will not appear in the visible prompt, and will not -cause the terminal's cursor to change position, but rather set terminal -attributes for the following prompt character(s) at least one of -which must be present. -See -.Sx Command Line Editing -above for more information. -.It Ev RANDOM -Returns a different pseudo-random integer, -in the range [0,32767] each time it is accessed. -.Ev RANDOM -can be assigned an integer value to seed the PRNG. -If the value assigned is a constant, then the -sequence of values produces on subsequent references of -.Ev RANDOM -will repeat after the next time the same constant is assigned. -Note, this is not guaranteed to remain constant from one version -of the shell to another \(en the PRNG algorithm, or seeding -method is subject to change. -If -.Ev RANDOM -is assigned an empty value (null string) then the next time -.Ev RANDOM -is accessed, it will be seeded from a more genuinely random source. -The sequence of pseudo-random numbers generated will not be able to -be generated again (except by luck, whether good or bad, depends!) -This is also how the initial seed is generated, if none has been -assigned before -.Ev RANDOM -is first accessed after shell initialization. -Should the error message -.Dq "RANDOM initialisation failed" -appear on standard error, it indicates that the source -of good random numbers was not available, and -.Ev RANDOM -has instead been seeded with a more predictable value. -The following sequence of random numbers will -not be as unpredictable as they otherwise would be. -.It Ev SECONDS -Returns the number of seconds since the current shell was started. -If unset, it remains unset, and returns nothing, unless set again. -If set, it loses its special properties, and becomes a normal variable. -.It Ev START_TIME -Initialized by the shell to the number of seconds since the Epoch -(see -.Xr localtime 3 ) -when the shell was started. -The value of -.Dl $(( Ns Ev START_TIME + Ev SECONDS Ns )) -represents the current time, if -.Ev START_TIME -has not been modified, and -.Ev SECONDS -has not been set or unset. -.It Ev TERM -The default terminal setting for the shell. -This is inherited by -children of the shell, and is used in the history editing modes. -.\" This is explicitly last, not in sort order - please leave! -.It Ev ToD -When referenced, uses the value of -.Ev ToD_FORMAT -(or -.Dq \&%T -if -.Ev ToD_FORMAT -is unset) as the format argument to -.Xr strftime 3 -to encode the current time of day, in the time zone -defined by -.Ev TZ -if set, or current local time if not, and returns the result. -If unset -.Ev ToD -returns nothing. -If set, it loses its special properties, and becomes a normal variable. -.It Ev ToD_FORMAT -Can be set to the -.Xr strftime 3 -format string to be used when expanding -.Ev ToD . -Initially unset. -.It Ev TZ -If set, gives the time zone -(see -.Xr localtime 3 , -.Xr environ 7 ) -to use when formatting -.Ev ToD -and if exported, other utilities that deal with times. -If unset, the system's local wall clock time zone is used. -.It Ev NETBSD_SHELL -Unlike the variables previously mentioned, -this variable is somewhat strange, -in that it cannot be set, -inherited from the environment, -modified, or exported from the shell. -If set, by the shell, it indicates that the shell is the -.Ic sh -defined by this manual page, and gives its version information. -It can also give information in additional space separated words, -after the version string. -If the shell was built as part of a reproducible build, -the relevant date that was used for that build will be included. -Finally, any non-standard compilation options, -which may affect features available, -that were used when building the shell will be listed. -.Ev NETBSD_SHELL -behaves like any other variable that has the read-only -and un-exportable attributes set. -.El -.Ss Dv LINENO -.Dv LINENO -is in many respects a normal shell variable, containing an -integer value. and can be expanded using any of the forms -mentioned above which can be used for any other variable. -.Pp -.Dv LINENO -can be exported, made readonly, or unset, as with any other -variable, with similar effects. -Note that while being readonly prevents later attempts to -set, or unset, -.Dv LINENO , -it does not prevent its value changing. -References to -.Dv LINENO -.Pq "when not unset" -always obtain the current line number. -However, -.Dv LINENO -should normally not ever be set or unset. -In this shell setting -.Dv LINENO -reverses the effect of an earlier -.Ic unset , -but does not otherwise affect the value obtained. -If unset, -.Dv LINENO -should not normally be set again, doing so is not portable. -If -.Dv LINENO -is set or unset, different shells act differently. -The value of -.Dv LINENO -is never imported from the environment when the shell is -started, though if present there, as with any other variable, -.Dv LINENO -will be exported by this shell. -.Pp -.Dv LINENO -is set automatically by the shell to be the number of the source -line on which it occurs. -When exported, -.Dv LINENO -is exported with its value set to the line number it would have -had had it been referenced on the command line of the command to -which it is exported. -Line numbers are counted from 1, which is the first line the shell -reads from any particular file. -For this shell, standard input, including in an interactive shell, -the user's terminal, is just another file and lines are counted -there as well. -However note that not all shells count interactive -lines this way, it is not wise to rely upon -.Dv LINENO -having a useful value, except in a script, or a function. -.Pp -The role of -.Dv LINENO -in functions is less clear. -In some shells, -.Dv LINENO -continues to refer to the line number in the script which defines -the function, -in others lines count from one within the function, always (and -resume counting normally once the function definition is complete) -and others count in functions from one if the function is defined -interactively, but otherwise just reference the line number in the -script in which the function is defined. -This shell gives the user the option to choose. -If the -.Fl L -flag (the -.Ic local_lineno -option, see -.Sx Argument List Processing ) -is set, when the function is defined, then the function -defaults to counting lines with one being the first line of the -function. -When the -.Fl L -flag is not set, the shell counts lines in a function definition -in the same continuous sequence as the lines that surround the -function definition. -Further, if -.Dv LINENO -is made local -(see -.Sx Built-ins -above) -inside the function, the function can decide which -behavior it prefers. -If -.Dv LINENO -is made local and inherited, and not given a value, as in -.Dl local Fl I Dv LINENO -then from that point in the function, -.Dv LINENO -will give the line number as if lines are counted in sequence -with the lines that surround the function definition (and -any other function definitions in which this is nested.) -If -.Dv LINENO -is made local, and in that same command, given a value, as -.Dl local Oo Fl I Ns | Ns Fl N Oc Dv LINENO Ns = Ns Ar value -then -.Dv LINENO -will give the line number as if lines are counted from one -from the beginning of the function. -The value nominally assigned in this case is irrelevant, and ignored. -For completeness, if lineno is made local and unset, as in -.Dl local Fl N Dv LINENO -then -.Dv LINENO -is simply unset inside the function, and gives no value at all. -.Pp -Now for some technical details. -The line on which -.Dv LINENO -occurs in a parameter expansion, is the line that contains the -.Sq \&$ -that begins the expansion of -.Dv LINENO . -In the case of nested expansions, that -.Sq \&$ -is the one that actually has -.Dv LINENO -as its parameter. -In an arithmetic expansion, where no -.Sq \&$ -is used to evaluate -.Dv LINENO -but -.Dv LINENO -is simply referenced as a variable, then the value is the -line number of the line that contains the -.Sq L -of -.Dv LINENO . -For functions line one of the function definition (when relevant) -is the line that contains the first character of the -function name in the definition. -When exported, the line number of the command is the line number -where the first character of the word which becomes the command name occurs. -.Pp -When the shell opens a new file, for any reason, -it counts lines from one in that file, -and then resumes its original counting once it resumes reading the -previous input stream. -When handling a string passed to -.Ic eval -the line number starts at the line on which the string starts, -and then if the string contains internal newline characters, -those characters increase the line number. -This means that references to -.Dv LINENO -in such a case can produce values larger than would be -produced by a reference on the line after the -.Ic eval . -.Sh FILES -.Bl -item -.It -.Pa $HOME/.profile -.It -.Pa /etc/profile -.El -.Sh EXIT STATUS -Errors that are detected by the shell, such as a syntax error, will cause the -shell to exit with a non-zero exit status. -If the shell is not an -interactive shell, the execution of the shell file will be aborted. -Otherwise -the shell will return the exit status of the last command executed, or -if the exit built-in is used with a numeric argument, it will return the -argument. -.Sh SEE ALSO -.Xr csh 1 , -.Xr echo 1 , -.Xr getopt 1 , -.Xr ksh 1 , -.Xr login 1 , -.Xr printf 1 , -.Xr test 1 , -.Xr editline 3 , -.Xr getopt 3 , -.\" .Xr profile 4 , -.Xr editrc 5 , -.Xr passwd 5 , -.Xr editline 7 , -.Xr environ 7 , -.Xr nls 7 , -.Xr sysctl 8 -.Sh HISTORY -A -.Nm -command appeared in -.At v1 . -It was replaced in -.At v7 -with a version that introduced the basis of the current syntax. -That was, however, unmaintainable so we wrote this one. -.Sh BUGS -Setuid shell scripts should be avoided at all costs, as they are a -significant security risk. -.Pp -The characters generated by filename completion should probably be quoted -to ensure that the filename is still valid after the input line has been -processed. -.Pp -Job control of compound statements (loops, etc) is a complete mess. -.Pp -Many, many, more. -(But less than there were...) diff --git a/bin/sh/shell.h b/bin/sh/shell.h deleted file mode 100644 index 318d56e..0000000 --- a/bin/sh/shell.h +++ /dev/null @@ -1,224 +0,0 @@ -/* $NetBSD: shell.h,v 1.29 2019/01/22 13:48:28 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)shell.h 8.2 (Berkeley) 5/4/95 - */ - -/* - * The follow should be set to reflect the type of system you have: - * JOBS -> 1 if you have Berkeley job control, 0 otherwise. - * define BSD if you are running 4.2 BSD or later. - * define SYSV if you are running under System V. - * define DEBUG=1 to compile in debugging ('set -o debug' to turn on) - * define DEBUG=2 to compile in and enable debugging. - * define DEBUG=3 for DEBUG==2 + enable most standard debug output - * define DEBUG=4 for DEBUG==2 + enable absolutely everything - * define DO_SHAREDVFORK to indicate that vfork(2) shares its address - * with its parent. - * define BOGUS_NOT_COMMAND to allow ! reserved words in weird places - * (traditional ash behaviour.) - * - * When debugging is on, debugging info will be written to ./trace and - * a quit signal will generate a core dump. - */ - -#ifndef SHELL_H -#define SHELL_H -#include <sys/param.h> - -#define JOBS 1 -#ifndef BSD -#define BSD 1 -#endif - -#ifndef DO_SHAREDVFORK -#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 104000000 -#define DO_SHAREDVFORK -#endif -#endif - -typedef void *pointer; -#ifndef NULL -#define NULL (void *)0 -#endif -#ifndef STATIC -#define STATIC /* empty */ -#endif -#define MKINIT /* empty */ - -#include <sys/cdefs.h> - -extern const char nullstr[1]; /* null string */ - -#ifdef SMALL -#undef DEBUG -#endif - -#ifdef DEBUG - -extern uint64_t DFlags; -extern int ShNest; - -/* - * This is selected as there are 26 letters in ascii - not that that - * matters for anything, just makes it easier to assign a different - * command letter to each debug option. We currently use only 18 - * so this could be reduced, but that is of no real benefit. It can also - * be increased, but that both limits the maximum value tha can be - * used with DBG_EXTRAS(), and causes problems with verbose option naming. - */ -#define DBG_VBOSE_SHIFT 27 -#define DBG_EXTRAS(n) ((DBG_VBOSE_SHIFT * 2) + (n)) - -/* - * Macros to enable tracing, so the mainainer can control - * just how much debug output is dumped to the trace file - * - * In the X forms, "xtra" can be any legal C statement(s) without (bare) commas - * executed if the relevant debug flag is enabled, after any tracing output. - */ -#define CTRACE(when, param) do { \ - if ((DFlags & (when)) != 0) \ - trace param; \ - } while (/*CONSTCOND*/ 0) - -#define CCTRACE(when,cond,param) do { \ - if ((cond) && (DFlags & (when)) != 0) \ - trace param; \ - } while (/*CONSTCOND*/ 0) - -#define CTRACEV(when, param) do { \ - if ((DFlags & (when)) != 0) \ - tracev param; \ - } while (/*CONSTCOND*/ 0) - -#define XTRACE(when, param, xtra) do { \ - if ((DFlags & (when)) != 0) { \ - trace param; \ - xtra; \ - } \ - } while (/*CONSTCOND*/ 0) - -#define VTRACE(when, param) do { \ - if ((DFlags & \ - (when)<<DBG_VBOSE_SHIFT) != 0) \ - trace param; \ - } while (/*CONSTCOND*/ 0) - -#define CVTRACE(when,cond,param) do { \ - if ((cond) && (DFlags & \ - (when)<<DBG_VBOSE_SHIFT) != 0) \ - trace param; \ - } while (/*CONSTCOND*/ 0) - -#define VTRACEV(when, param) do { \ - if ((DFlags & \ - (when)<<DBG_VBOSE_SHIFT) != 0) \ - tracev param; \ - } while (/*CONSTCOND*/ 0) - -#define VXTRACE(when, param, xtra) do { \ - if ((DFlags & \ - (when)<<DBG_VBOSE_SHIFT) != 0) {\ - trace param; \ - xtra; \ - } \ - } while (/*CONSTCOND*/ 0) - -#define SHELL_FORKED() ShNest++ -#define VFORK_BLOCK { const int _ShNest = ShNest; -#define VFORK_END } -#define VFORK_UNDO() ShNest = _ShNest - -#define DBG_ALWAYS (1LL << 0) -#define DBG_PARSE (1LL << 1) /* r (read commands) */ -#define DBG_EVAL (1LL << 2) /* e */ -#define DBG_EXPAND (1LL << 3) /* x */ -#define DBG_JOBS (1LL << 4) /* j */ -#define DBG_PROCS (1LL << 5) /* p */ -#define DBG_REDIR (1LL << 6) /* f (fds) */ -#define DBG_CMDS (1LL << 7) /* c */ -#define DBG_ERRS (1LL << 8) /* z (?) */ -#define DBG_WAIT (1LL << 9) /* w */ -#define DBG_TRAP (1LL << 10) /* t */ -#define DBG_VARS (1LL << 11) /* v */ -#define DBG_INPUT (1LL << 12) /* i */ -#define DBG_OUTPUT (1LL << 13) /* o */ -#define DBG_MEM (1LL << 14) /* m */ -#define DBG_ARITH (1LL << 15) /* a */ -#define DBG_HISTORY (1LL << 16) /* h */ -#define DBG_SIG (1LL << 17) /* s */ -#define DBG_MATCH (1LL << 18) /* g (glob) */ -#define DBG_LEXER (1LL << 19) /* l */ - -/* - * reserved extras: b=builtins y=alias - * still free: d k n q u - */ - - /* use VTRACE(DBG_ALWAYS, (...)) to test this one */ -#define DBG_VERBOSE (1LL << DBG_VBOSE_SHIFT) - - /* DBG_EXTRAS 0 .. 9 (max) only - non-alpha options (no VTRACE !!) */ -#define DBG_U0 (1LL << DBG_EXTRAS(0)) /* 0 - ad-hoc extra flags */ -#define DBG_U1 (1LL << DBG_EXTRAS(1)) /* 1 - for short term */ -#define DBG_U2 (1LL << DBG_EXTRAS(2)) /* 2 - extra tracing */ -#define DBG_U3 (1LL << DBG_EXTRAS(3)) /* 3 - when needed */ - /* 4, 5, & 6 currently free */ -#define DBG_LINE (1LL << DBG_EXTRAS(7)) /* @ ($LINENO) */ -#define DBG_PID (1LL << DBG_EXTRAS(8)) /* $ ($$) */ -#define DBG_NEST (1LL << DBG_EXTRAS(9)) /* ^ */ - -/* 26 lower case, 26 upper case, always, verbose, and 10 extras: 64 bits */ - -extern void set_debug(const char *, int); - -#else /* DEBUG */ - -#define CTRACE(when, param) /* conditional normal trace */ -#define CCTRACE(when, cond, param) /* more conditional normal trace */ -#define CTRACEV(when, param) /* conditional varargs trace */ -#define XTRACE(when, param, extra) /* conditional trace, plus more */ -#define VTRACE(when, param) /* conditional verbose trace */ -#define CVTRACE(when, cond, param) /* more conditional verbose trace */ -#define VTRACEV(when, param) /* conditional verbose varargs trace */ -#define VXTRACE(when, param, extra) /* cond verbose trace, plus more */ - -#define SHELL_FORKED() -#define VFORK_BLOCK -#define VFORK_END -#define VFORK_UNDO() - -#endif /* DEBUG */ - -#endif /* SHELL_H */ diff --git a/bin/sh/show.c b/bin/sh/show.c deleted file mode 100644 index fa9f5aa..0000000 --- a/bin/sh/show.c +++ /dev/null @@ -1,1175 +0,0 @@ -/* $NetBSD: show.c,v 1.52 2019/01/22 13:48:28 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: show.c,v 1.52 2019/01/22 13:48:28 kre Exp $"); -#endif -#endif /* not lint */ - -#include <stdio.h> -#include <stdarg.h> -#include <stdlib.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <limits.h> - -#include <sys/types.h> -#include <sys/uio.h> - -#include "shell.h" -#include "parser.h" -#include "nodes.h" -#include "mystring.h" -#include "show.h" -#include "options.h" -#include "redir.h" -#include "error.h" -#include "syntax.h" -#include "input.h" -#include "output.h" -#include "var.h" -#include "builtins.h" - -#define DEFINE_NODENAMES -#include "nodenames.h" /* does almost nothing if !defined(DEBUG) */ - -#define TR_STD_WIDTH 60 /* tend to fold lines wider than this */ -#define TR_IOVECS 10 /* number of lines or trace (max) / write */ - -typedef struct traceinfo { - int tfd; /* file descriptor for open trace file */ - int nxtiov; /* the buffer we should be writing to */ - char lastc; /* the last non-white character output */ - uint8_t supr; /* char classes to suppress after \n */ - pid_t pid; /* process id of process that opened that file */ - size_t llen; /* number of chars in current output line */ - size_t blen; /* chars used in current buffer being filled */ - char * tracefile; /* name of the tracefile */ - struct iovec lines[TR_IOVECS]; /* filled, flling, pending buffers */ -} TFILE; - -/* These are auto turned off when non white space is printed */ -#define SUP_NL 0x01 /* don't print \n */ -#define SUP_SP 0x03 /* suppress spaces */ -#define SUP_WSP 0x04 /* suppress all white space */ - -#ifdef DEBUG /* from here to end of file ... */ - -TFILE tracedata, *tracetfile; -FILE *tracefile; /* just for histedit */ - -uint64_t DFlags; /* currently enabled debug flags */ -int ShNest; /* depth of shell (internal) nesting */ - -static void shtree(union node *, int, int, int, TFILE *); -static void shcmd(union node *, TFILE *); -static void shsubsh(union node *, TFILE *); -static void shredir(union node *, TFILE *, int); -static void sharg(union node *, TFILE *); -static void indent(int, TFILE *); -static void trstring(const char *); -static void trace_putc(char, TFILE *); -static void trace_puts(const char *, TFILE *); -static void trace_flush(TFILE *, int); -static char *trace_id(TFILE *); -static void trace_fd_swap(int, int); - -inline static int trlinelen(TFILE *); - - -/* - * These functions are the externally visible interface - * (but only for a DEBUG shell.) - */ - -void -opentrace(void) -{ - char *s; - int fd; - int i; - pid_t pid; - - if (debug != 1) { - /* leave fd open because libedit might be using it */ - if (tracefile) - fflush(tracefile); - if (tracetfile) - trace_flush(tracetfile, 1); - return; - } -#if DBG_PID == 1 /* using old shell.h, old tracing method */ - DFlags = DBG_PID; /* just force DBG_PID on, and leave it ... */ -#endif - pid = getpid(); - if (asprintf(&s, "trace.%jd", (intmax_t)pid) <= 0) { - debug = 0; - error("Cannot asprintf tracefilename"); - }; - - fd = open(s, O_WRONLY|O_APPEND|O_CREAT, 0666); - if (fd == -1) { - debug = 0; - error("Can't open tracefile: %s (%s)\n", s, strerror(errno)); - } - fd = to_upper_fd(fd); - if (fd <= 2) { - (void) close(fd); - debug = 0; - error("Attempt to use fd %d as tracefile thwarted\n", fd); - } - register_sh_fd(fd, trace_fd_swap); - - /* - * This stuff is just so histedit has a FILE * to use - */ - if (tracefile) - (void) fclose(tracefile); /* also closes tfd */ - tracefile = fdopen(fd, "a"); /* don't care if it is NULL */ - if (tracefile) /* except here... */ - setlinebuf(tracefile); - - /* - * Now the real tracing setup - */ - if (tracedata.tfd > 0 && tracedata.tfd != fd) - (void) close(tracedata.tfd); /* usually done by fclose() */ - - tracedata.tfd = fd; - tracedata.pid = pid; - tracedata.nxtiov = 0; - tracedata.blen = 0; - tracedata.llen = 0; - tracedata.lastc = '\0'; - tracedata.supr = SUP_NL | SUP_WSP; - -#define replace(f, v) do { \ - if (tracedata.f != NULL) \ - free(tracedata.f); \ - tracedata.f = v; \ - } while (/*CONSTCOND*/ 0) - - replace(tracefile, s); - - for (i = 0; i < TR_IOVECS; i++) { - replace(lines[i].iov_base, NULL); - tracedata.lines[i].iov_len = 0; - } - -#undef replace - - tracetfile = &tracedata; - - trace_puts("\nTracing started.\n", tracetfile); -} - -void -trace(const char *fmt, ...) -{ - va_list va; - char *s; - - if (debug != 1 || !tracetfile) - return; - va_start(va, fmt); - (void) vasprintf(&s, fmt, va); - va_end(va); - - trace_puts(s, tracetfile); - free(s); - if (tracetfile->llen == 0) - trace_flush(tracetfile, 0); -} - -void -tracev(const char *fmt, va_list va) -{ - va_list ap; - char *s; - - if (debug != 1 || !tracetfile) - return; - va_copy(ap, va); - (void) vasprintf(&s, fmt, ap); - va_end(ap); - - trace_puts(s, tracetfile); - free(s); - if (tracetfile->llen == 0) - trace_flush(tracetfile, 0); -} - - -void -trputs(const char *s) -{ - if (debug != 1 || !tracetfile) - return; - trace_puts(s, tracetfile); -} - -void -trputc(int c) -{ - if (debug != 1 || !tracetfile) - return; - trace_putc(c, tracetfile); -} - -void -showtree(union node *n) -{ - TFILE *fp; - - if ((fp = tracetfile) == NULL) - return; - - trace_puts("showtree(", fp); - if (n == NULL) - trace_puts("NULL", fp); - else if (n == NEOF) - trace_puts("NEOF", fp); - else - trace("%p", n); - trace_puts(") called\n", fp); - if (n != NULL && n != NEOF) - shtree(n, 1, 1, 1, fp); -} - -void -trargs(char **ap) -{ - if (debug != 1 || !tracetfile) - return; - while (*ap) { - trstring(*ap++); - if (*ap) - trace_putc(' ', tracetfile); - } - trace_putc('\n', tracetfile); -} - -void -trargstr(union node *n) -{ - sharg(n, tracetfile); -} - - -/* - * Beyond here we just have the implementation of all of that - */ - - -inline static int -trlinelen(TFILE * fp) -{ - return fp->llen; -} - -static void -shtree(union node *n, int ind, int ilvl, int nl, TFILE *fp) -{ - struct nodelist *lp; - const char *s; - - if (n == NULL) { - if (nl) - trace_putc('\n', fp); - return; - } - - indent(ind, fp); - switch (n->type) { - case NSEMI: - s = NULL; - goto binop; - case NAND: - s = " && "; - goto binop; - case NOR: - s = " || "; -binop: - shtree(n->nbinary.ch1, 0, ilvl, 0, fp); - if (s != NULL) - trace_puts(s, fp); - if (trlinelen(fp) >= TR_STD_WIDTH) { - trace_putc('\n', fp); - indent(ind < 0 ? 2 : ind + 1, fp); - } else if (s == NULL) { - if (fp->lastc != '&') - trace_puts("; ", fp); - else - trace_putc(' ', fp); - } - shtree(n->nbinary.ch2, 0, ilvl, nl, fp); - break; - case NCMD: - shcmd(n, fp); - if (n->ncmd.backgnd) - trace_puts(" &", fp); - if (nl && trlinelen(fp) > 0) - trace_putc('\n', fp); - break; - case NPIPE: - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - shtree(lp->n, 0, ilvl, 0, fp); - if (lp->next) { - trace_puts(" |", fp); - if (trlinelen(fp) >= TR_STD_WIDTH) { - trace_putc('\n', fp); - indent((ind < 0 ? ilvl : ind) + 1, fp); - } else - trace_putc(' ', fp); - } - } - if (n->npipe.backgnd) - trace_puts(" &", fp); - if (nl || trlinelen(fp) >= TR_STD_WIDTH) - trace_putc('\n', fp); - break; - case NBACKGND: - case NSUBSHELL: - shsubsh(n, fp); - if (n->type == NBACKGND) - trace_puts(" &", fp); - if (nl && trlinelen(fp) > 0) - trace_putc('\n', fp); - break; - case NDEFUN: - trace_puts(n->narg.text, fp); - trace_puts("() {\n", fp); - indent(ind, fp); - shtree(n->narg.next, (ind < 0 ? ilvl : ind) + 1, ilvl+1, 1, fp); - indent(ind, fp); - trace_puts("}\n", fp); - break; - case NDNOT: - trace_puts("! ", fp); - /* FALLTHROUGH */ - case NNOT: - trace_puts("! ", fp); - shtree(n->nnot.com, -1, ilvl, nl, fp); - break; - case NREDIR: - shtree(n->nredir.n, -1, ilvl, 0, fp); - shredir(n->nredir.redirect, fp, n->nredir.n == NULL); - if (nl) - trace_putc('\n', fp); - break; - - case NIF: - itsif: - trace_puts("if ", fp); - shtree(n->nif.test, -1, ilvl, 0, fp); - if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { - if (fp->lastc != '&') - trace_puts(" ;", fp); - } else - indent(ilvl, fp); - trace_puts(" then ", fp); - if (nl || trlinelen(fp) > TR_STD_WIDTH - 24) - indent(ilvl+1, fp); - shtree(n->nif.ifpart, -1, ilvl + 1, 0, fp); - if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { - if (fp->lastc != '&') - trace_puts(" ;", fp); - } else - indent(ilvl, fp); - if (n->nif.elsepart && n->nif.elsepart->type == NIF) { - if (nl || trlinelen(fp) > TR_STD_WIDTH - 24) - indent(ilvl, fp); - n = n->nif.elsepart; - trace_puts(" el", fp); - goto itsif; - } - if (n->nif.elsepart) { - if (nl || trlinelen(fp) > TR_STD_WIDTH - 24) - indent(ilvl+1, fp); - trace_puts(" else ", fp); - shtree(n->nif.elsepart, -1, ilvl + 1, 0, fp); - if (fp->lastc != '&') - trace_puts(" ;", fp); - } - trace_puts(" fi", fp); - if (nl) - trace_putc('\n', fp); - break; - - case NWHILE: - trace_puts("while ", fp); - goto aloop; - case NUNTIL: - trace_puts("until ", fp); - aloop: - shtree(n->nbinary.ch1, -1, ilvl, 0, fp); - if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { - if (fp->lastc != '&') - trace_puts(" ;", fp); - } else - trace_putc('\n', fp); - trace_puts(" do ", fp); - shtree(n->nbinary.ch1, -1, ilvl + 1, 1, fp); - trace_puts(" done ", fp); - if (nl) - trace_putc('\n', fp); - break; - - case NFOR: - trace_puts("for ", fp); - trace_puts(n->nfor.var, fp); - if (n->nfor.args) { - union node *argp; - - trace_puts(" in ", fp); - for (argp = n->nfor.args; argp; argp=argp->narg.next) { - sharg(argp, fp); - trace_putc(' ', fp); - } - if (trlinelen(fp) > 0 && trlinelen(fp) < TR_STD_WIDTH) { - if (fp->lastc != '&') - trace_putc(';', fp); - } else - trace_putc('\n', fp); - } - trace_puts(" do ", fp); - shtree(n->nfor.body, -1, ilvl + 1, 0, fp); - if (fp->lastc != '&') - trace_putc(';', fp); - trace_puts(" done", fp); - if (nl) - trace_putc('\n', fp); - break; - - case NCASE: - trace_puts("case ", fp); - sharg(n->ncase.expr, fp); - trace_puts(" in", fp); - if (nl) - trace_putc('\n', fp); - { - union node *cp; - - for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) { - union node *patp; - - if (nl || trlinelen(fp) > TR_STD_WIDTH - 16) - indent(ilvl, fp); - else - trace_putc(' ', fp); - trace_putc('(', fp); - patp = cp->nclist.pattern; - while (patp != NULL) { - trace_putc(' ', fp); - sharg(patp, fp); - trace_putc(' ', fp); - if ((patp = patp->narg.next) != NULL) - trace_putc('|', fp); - } - trace_putc(')', fp); - if (nl) - indent(ilvl + 1, fp); - else - trace_putc(' ', fp); - shtree(cp->nclist.body, -1, ilvl+2, 0, fp); - if (cp->type == NCLISTCONT) - trace_puts(" ;&", fp); - else - trace_puts(" ;;", fp); - if (nl) - trace_putc('\n', fp); - } - } - if (nl) { - trace_putc('\n', fp); - indent(ind, fp); - } else - trace_putc(' ', fp); - trace_puts("esac", fp); - if (nl) - trace_putc('\n', fp); - break; - - default: { - char *str; - - asprintf(&str, "<node type %d [%s]>", n->type, - NODETYPENAME(n->type)); - trace_puts(str, fp); - free(str); - if (nl) - trace_putc('\n', fp); - } - break; - } -} - - -static void -shcmd(union node *cmd, TFILE *fp) -{ - union node *np; - int first; - - first = 1; - for (np = cmd->ncmd.args ; np ; np = np->narg.next) { - if (! first) - trace_putc(' ', fp); - sharg(np, fp); - first = 0; - } - shredir(cmd->ncmd.redirect, fp, first); -} - -static void -shsubsh(union node *cmd, TFILE *fp) -{ - trace_puts(" ( ", fp); - shtree(cmd->nredir.n, -1, 3, 0, fp); - trace_puts(" ) ", fp); - shredir(cmd->ncmd.redirect, fp, 1); -} - -static void -shredir(union node *np, TFILE *fp, int first) -{ - const char *s; - int dftfd; - char buf[106]; - - for ( ; np ; np = np->nfile.next) { - if (! first) - trace_putc(' ', fp); - switch (np->nfile.type) { - case NTO: s = ">"; dftfd = 1; break; - case NCLOBBER: s = ">|"; dftfd = 1; break; - case NAPPEND: s = ">>"; dftfd = 1; break; - case NTOFD: s = ">&"; dftfd = 1; break; - case NFROM: s = "<"; dftfd = 0; break; - case NFROMFD: s = "<&"; dftfd = 0; break; - case NFROMTO: s = "<>"; dftfd = 0; break; - case NXHERE: /* FALLTHROUGH */ - case NHERE: s = "<<"; dftfd = 0; break; - default: s = "*error*"; dftfd = 0; break; - } - if (np->nfile.fd != dftfd) { - sprintf(buf, "%d", np->nfile.fd); - trace_puts(buf, fp); - } - trace_puts(s, fp); - if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { - if (np->ndup.vname) - sharg(np->ndup.vname, fp); - else { - if (np->ndup.dupfd < 0) - trace_puts("-", fp); - else { - sprintf(buf, "%d", np->ndup.dupfd); - trace_puts(buf, fp); - } - } - } else - if (np->nfile.type == NHERE || np->nfile.type == NXHERE) { - if (np->nfile.type == NHERE) - trace_putc('\\', fp); - trace_puts("!!!\n", fp); - s = np->nhere.doc->narg.text; - if (strlen(s) > 100) { - memmove(buf, s, 100); - buf[100] = '\0'; - strcat(buf, " ...\n"); - s = buf; - } - trace_puts(s, fp); - trace_puts("!!! ", fp); - } else { - sharg(np->nfile.fname, fp); - } - first = 0; - } -} - -static void -sharg(union node *arg, TFILE *fp) -{ - char *p, *s; - struct nodelist *bqlist; - int subtype = 0; - int quoted = 0; - - if (arg->type != NARG) { - asprintf(&s, "<node type %d> ! NARG\n", arg->type); - trace_puts(s, fp); - abort(); /* no need to free s, better not to */ - } - - bqlist = arg->narg.backquote; - for (p = arg->narg.text ; *p ; p++) { - switch (*p) { - case CTLESC: - trace_putc('\\', fp); - trace_putc(*++p, fp); - break; - - case CTLNONL: - trace_putc('\\', fp); - trace_putc('\n', fp); - break; - - case CTLVAR: - subtype = *++p; - if (!quoted != !(subtype & VSQUOTE)) - trace_putc('"', fp); - trace_putc('$', fp); - trace_putc('{', fp); /*}*/ - if ((subtype & VSTYPE) == VSLENGTH) - trace_putc('#', fp); - if (subtype & VSLINENO) - trace_puts("LINENO=", fp); - - while (*++p != '=') - trace_putc(*p, fp); - - if (subtype & VSNUL) - trace_putc(':', fp); - - switch (subtype & VSTYPE) { - case VSNORMAL: - /* { */ - trace_putc('}', fp); - if (!quoted != !(subtype & VSQUOTE)) - trace_putc('"', fp); - break; - case VSMINUS: - trace_putc('-', fp); - break; - case VSPLUS: - trace_putc('+', fp); - break; - case VSQUESTION: - trace_putc('?', fp); - break; - case VSASSIGN: - trace_putc('=', fp); - break; - case VSTRIMLEFTMAX: - trace_putc('#', fp); - /* FALLTHROUGH */ - case VSTRIMLEFT: - trace_putc('#', fp); - break; - case VSTRIMRIGHTMAX: - trace_putc('%', fp); - /* FALLTHROUGH */ - case VSTRIMRIGHT: - trace_putc('%', fp); - break; - case VSLENGTH: - break; - default: { - char str[32]; - - snprintf(str, sizeof str, - "<subtype %d>", subtype); - trace_puts(str, fp); - } - break; - } - break; - case CTLENDVAR: - /* { */ - trace_putc('}', fp); - if (!quoted != !(subtype & VSQUOTE)) - trace_putc('"', fp); - subtype = 0; - break; - - case CTLBACKQ|CTLQUOTE: - if (!quoted) - trace_putc('"', fp); - /* FALLTHRU */ - case CTLBACKQ: - trace_putc('$', fp); - trace_putc('(', fp); - if (bqlist) { - shtree(bqlist->n, -1, 3, 0, fp); - bqlist = bqlist->next; - } else - trace_puts("???", fp); - trace_putc(')', fp); - if (!quoted && *p == (CTLBACKQ|CTLQUOTE)) - trace_putc('"', fp); - break; - - case CTLQUOTEMARK: - if (subtype != 0 || !quoted) { - trace_putc('"', fp); - quoted++; - } - break; - case CTLQUOTEEND: - trace_putc('"', fp); - quoted--; - break; - case CTLARI: - if (*p == ' ') - p++; - trace_puts("$(( ", fp); - break; - case CTLENDARI: - trace_puts(" ))", fp); - break; - - default: - if (*p == '$') - trace_putc('\\', fp); - trace_putc(*p, fp); - break; - } - } - if (quoted) - trace_putc('"', fp); -} - - -static void -indent(int amount, TFILE *fp) -{ - int i; - - if (amount <= 0) - return; - - amount <<= 2; /* indent slots -> chars */ - - i = trlinelen(fp); - fp->supr = SUP_NL; - if (i > amount) { - trace_putc('\n', fp); - i = 0; - } - fp->supr = 0; - for (; i < amount - 7 ; i++) { - trace_putc('\t', fp); - i |= 7; - } - while (i < amount) { - trace_putc(' ', fp); - i++; - } - fp->supr = SUP_WSP; -} - -static void -trace_putc(char c, TFILE *fp) -{ - char *p; - - if (c == '\0') - return; - if (debug == 0 || fp == NULL) - return; - - if (fp->llen == 0) { - if (fp->blen != 0) - abort(); - - if ((fp->supr & SUP_NL) && c == '\n') - return; - if ((fp->supr & (SUP_WSP|SUP_SP)) && c == ' ') - return; - if ((fp->supr & SUP_WSP) && c == '\t') - return; - - if (fp->nxtiov >= TR_IOVECS - 1) /* should be rare */ - trace_flush(fp, 0); - - p = trace_id(fp); - if (p != NULL) { - fp->lines[fp->nxtiov].iov_base = p; - fp->lines[fp->nxtiov].iov_len = strlen(p); - fp->nxtiov++; - } - } else if (fp->blen && fp->blen >= fp->lines[fp->nxtiov].iov_len) { - fp->blen = 0; - if (++fp->nxtiov >= TR_IOVECS) - trace_flush(fp, 0); - } - - if (fp->lines[fp->nxtiov].iov_len == 0) { - p = (char *)malloc(2 * TR_STD_WIDTH); - if (p == NULL) { - trace_flush(fp, 1); - debug = 0; - return; - } - *p = '\0'; - fp->lines[fp->nxtiov].iov_base = p; - fp->lines[fp->nxtiov].iov_len = 2 * TR_STD_WIDTH; - fp->blen = 0; - } - - p = (char *)fp->lines[fp->nxtiov].iov_base + fp->blen++; - *p++ = c; - *p = 0; - - if (c != ' ' && c != '\t' && c != '\n') { - fp->lastc = c; - fp->supr = 0; - } - - if (c == '\n') { - fp->lines[fp->nxtiov++].iov_len = fp->blen; - fp->blen = 0; - fp->llen = 0; - fp->supr |= SUP_NL; - return; - } - - if (c == '\t') - fp->llen |= 7; - fp->llen++; -} - -void -trace_flush(TFILE *fp, int all) -{ - int niov, i; - ssize_t written; - - niov = fp->nxtiov; - if (all && fp->blen > 0) { - fp->lines[niov].iov_len = fp->blen; - fp->blen = 0; - fp->llen = 0; - niov++; - } - if (niov == 0) - return; - if (fp->blen > 0 && --niov == 0) - return; - written = writev(fp->tfd, fp->lines, niov); - for (i = 0; i < niov; i++) { - free(fp->lines[i].iov_base); - fp->lines[i].iov_base = NULL; - fp->lines[i].iov_len = 0; - } - if (written == -1) { - if (fp->blen > 0) { - free(fp->lines[niov].iov_base); - fp->lines[niov].iov_base = NULL; - fp->lines[niov].iov_len = 0; - } - debug = 0; - fp->blen = 0; - fp->llen = 0; - return; - } - if (fp->blen > 0) { - fp->lines[0].iov_base = fp->lines[niov].iov_base; - fp->lines[0].iov_len = fp->lines[niov].iov_len; - fp->lines[niov].iov_base = NULL; - fp->lines[niov].iov_len = 0; - } - fp->nxtiov = 0; -} - -void -trace_puts(const char *s, TFILE *fp) -{ - char c; - - while ((c = *s++) != '\0') - trace_putc(c, fp); -} - -inline static char * -trace_id(TFILE *tf) -{ - int i; - char indent[16]; - char *p; - int lno; - char c; - - if (DFlags & DBG_NEST) { - if ((unsigned)ShNest >= sizeof indent - 1) { - (void) snprintf(indent, sizeof indent, - "### %*d ###", (int)(sizeof indent) - 9, ShNest); - p = strchr(indent, '\0'); - } else { - p = indent; - for (i = 0; i < 6; i++) - *p++ = (i < ShNest) ? '#' : ' '; - while (i++ < ShNest && p < &indent[sizeof indent - 1]) - *p++ = '#'; - *p = '\0'; - } - } else - indent[0] = '\0'; - - /* - * If we are in the parser, then plinno is the current line - * number being processed (parser line no). - * If we are elsewhere, then line_number gives the source - * line of whatever we are currently doing (close enough.) - */ - if (parsing) - lno = plinno; - else - lno = line_number; - - c = ((i = getpid()) == tf->pid) ? ':' : '='; - - if (DFlags & DBG_PID) { - if (DFlags & DBG_LINE) - (void) asprintf(&p, "%5d%c%s\t%4d%c@\t", i, c, - indent, lno, parsing?'-':'+'); - else - (void) asprintf(&p, "%5d%c%s\t", i, c, indent); - return p; - } else if (DFlags & DBG_NEST) { - if (DFlags & DBG_LINE) - (void) asprintf(&p, "%c%s\t%4d%c@\t", c, indent, lno, - parsing?'-':'+'); - else - (void) asprintf(&p, "%c%s\t", c, indent); - return p; - } else if (DFlags & DBG_LINE) { - (void) asprintf(&p, "%c%4d%c@\t", c, lno, parsing?'-':'+'); - return p; - } - return NULL; -} - -/* - * Used only from trargs(), which itself is used only to print - * arg lists (argv[]) either that passed into this shell, or - * the arg list about to be given to some other command (incl - * builtin, and function) as their argv[]. If any of the CTL* - * chars seem to appear, they really should be just treated as data, - * not special... But this is just debug, so, who cares! - */ -static void -trstring(const char *s) -{ - const char *p; - char c; - TFILE *fp; - - if (debug != 1 || !tracetfile) - return; - fp = tracetfile; - trace_putc('"', fp); - for (p = s ; *p ; p++) { - switch (*p) { - case '\n': c = 'n'; goto backslash; - case '\t': c = 't'; goto backslash; - case '\r': c = 'r'; goto backslash; - case '"': c = '"'; goto backslash; - case '\\': c = '\\'; goto backslash; - case CTLESC: c = 'e'; goto backslash; - case CTLVAR: c = 'v'; goto backslash; - case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; - case CTLBACKQ: c = 'q'; goto backslash; - case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; -backslash: trace_putc('\\', fp); - trace_putc(c, fp); - break; - default: - if (*p >= ' ' && *p <= '~') - trace_putc(*p, fp); - else { - trace_putc('\\', fp); - trace_putc(*p >> 6 & 03, fp); - trace_putc(*p >> 3 & 07, fp); - trace_putc(*p & 07, fp); - } - break; - } - } - trace_putc('"', fp); -} - -/* - * deal with the user "accidentally" picking our fd to use. - */ -static void -trace_fd_swap(int from, int to) -{ - if (tracetfile == NULL || from == to) - return; - - tracetfile->tfd = to; - - /* - * This is just so histedit has a stdio FILE* to use. - */ - if (tracefile) - fclose(tracefile); - tracefile = fdopen(to, "a"); - if (tracefile) - setlinebuf(tracefile); -} - - -static struct debug_flag { - char label; - uint64_t flag; -} debug_flags[] = { - { 'a', DBG_ARITH }, /* arithmetic ( $(( )) ) */ - { 'c', DBG_CMDS }, /* command searching, ... */ - { 'e', DBG_EVAL }, /* evaluation of the parse tree */ - { 'f', DBG_REDIR }, /* file descriptors & redirections */ - { 'g', DBG_MATCH }, /* pattern matching (glob) */ - { 'h', DBG_HISTORY }, /* history & cmd line editing */ - { 'i', DBG_INPUT }, /* shell input routines */ - { 'j', DBG_JOBS }, /* job control, structures */ - { 'l', DBG_LEXER }, /* lexical analysis */ - { 'm', DBG_MEM }, /* memory management */ - { 'o', DBG_OUTPUT }, /* output routines */ - { 'p', DBG_PROCS }, /* process management, fork, ... */ - { 'r', DBG_PARSE }, /* parser, lexer, ... tree building */ - { 's', DBG_SIG }, /* signals and everything related */ - { 't', DBG_TRAP }, /* traps & signals */ - { 'v', DBG_VARS }, /* variables and parameters */ - { 'w', DBG_WAIT }, /* waits for processes to finish */ - { 'x', DBG_EXPAND }, /* word expansion ${} $() $(( )) */ - { 'z', DBG_ERRS }, /* error control, jumps, cleanup */ - - { '0', DBG_U0 }, /* ad-hoc temp debug flag #0 */ - { '1', DBG_U1 }, /* ad-hoc temp debug flag #1 */ - { '2', DBG_U2 }, /* ad-hoc temp debug flag #2 */ - { '3', DBG_U3 }, /* ad-hoc temp debug flag #3 */ - - { '@', DBG_LINE }, /* prefix trace lines with line# */ - { '$', DBG_PID }, /* prefix trace lines with sh pid */ - { '^', DBG_NEST }, /* show shell nesting level */ - - /* alpha options only - but not DBG_LEXER */ - { '_', DBG_PARSE | DBG_EVAL | DBG_EXPAND | DBG_JOBS | DBG_SIG | - DBG_PROCS | DBG_REDIR | DBG_CMDS | DBG_ERRS | - DBG_WAIT | DBG_TRAP | DBG_VARS | DBG_MEM | DBG_MATCH | - DBG_INPUT | DBG_OUTPUT | DBG_ARITH | DBG_HISTORY }, - - /* { '*', DBG_ALLVERBOSE }, is handled in the code */ - - { '#', DBG_U0 | DBG_U1 | DBG_U2 | DBG_U3 }, - - { 0, 0 } -}; - -void -set_debug(const char * flags, int on) -{ - char f; - struct debug_flag *df; - int verbose; - - while ((f = *flags++) != '\0') { - verbose = 0; - if (is_upper(f)) { - verbose = 1; - f += 'a' - 'A'; - } - if (f == '*') - f = '_', verbose = 1; - if (f == '+') { - if (*flags == '+') - flags++, verbose=1; - } - - /* - * Note: turning on any debug option also enables DBG_ALWAYS - * turning on any verbose option also enables DBG_VERBOSE - * Once enabled, those flags cannot be disabled. - * (tracing can still be turned off with "set +o debug") - */ - for (df = debug_flags; df->label != '\0'; df++) { - if (f == '+' || df->label == f) { - if (on) { - DFlags |= DBG_ALWAYS | df->flag; - if (verbose) - DFlags |= DBG_VERBOSE | - (df->flag << DBG_VBOSE_SHIFT); - } else { - DFlags &= ~(df->flag<<DBG_VBOSE_SHIFT); - if (!verbose) - DFlags &= ~df->flag; - } - } - } - } -} - - -int -debugcmd(int argc, char **argv) -{ - if (argc == 1) { - struct debug_flag *df; - - out1fmt("Debug: %sabled. Flags: ", debug ? "en" : "dis"); - for (df = debug_flags; df->label != '\0'; df++) { - if (df->flag & (df->flag - 1)) - continue; - if (is_alpha(df->label) && - (df->flag << DBG_VBOSE_SHIFT) & DFlags) - out1c(df->label - ('a' - 'A')); - else if (df->flag & DFlags) - out1c(df->label); - } - out1c('\n'); - return 0; - } - - while (*++argv) { - if (**argv == '-') - set_debug(*argv + 1, 1); - else if (**argv == '+') - set_debug(*argv + 1, 0); - else - return 1; - } - return 0; -} - -#endif /* DEBUG */ diff --git a/bin/sh/show.h b/bin/sh/show.h deleted file mode 100644 index 2c48873..0000000 --- a/bin/sh/show.h +++ /dev/null @@ -1,46 +0,0 @@ -/* $NetBSD: show.h,v 1.11 2017/06/30 23:00:40 kre Exp $ */ - -/*- - * Copyright (c) 1995 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)show.h 1.1 (Berkeley) 5/4/95 - */ - -#include <stdarg.h> - -#ifdef DEBUG -union node; -void showtree(union node *); -void trace(const char *, ...); -void tracev(const char *, va_list); -void trargs(char **); -void trargstr(union node *); -void trputc(int); -void trputs(const char *); -void opentrace(void); -#endif diff --git a/bin/sh/syntax.c b/bin/sh/syntax.c deleted file mode 100644 index 7b460d4..0000000 --- a/bin/sh/syntax.c +++ /dev/null @@ -1,111 +0,0 @@ -/* $NetBSD: syntax.c,v 1.7 2018/12/03 06:40:26 kre Exp $ */ - -#include <sys/cdefs.h> -__RCSID("$NetBSD: syntax.c,v 1.7 2018/12/03 06:40:26 kre Exp $"); - -#include <limits.h> -#include "shell.h" -#include "syntax.h" -#include "parser.h" - -#if CWORD != 0 -#error initialisation assumes 'CWORD' is zero -#endif - -#define ndx(ch) (ch + 2 - CHAR_MIN) -#define set(ch, val) [ndx(ch)] = val, -#define set_range(s, e, val) [ndx(s) ... ndx(e)] = val, - -/* syntax table used when not in quotes */ -const char basesyntax[258] = { CFAKE, CEOF, - set_range(CTL_FIRST, CTL_LAST, CCTL) - set('\n', CNL) - set('\\', CBACK) - set('\'', CSQUOTE) - set('"', CDQUOTE) - set('`', CBQUOTE) - set('$', CVAR) - set('}', CENDVAR) - set('<', CSPCL) - set('>', CSPCL) - set('(', CSPCL) - set(')', CSPCL) - set(';', CSPCL) - set('&', CSPCL) - set('|', CSPCL) - set(' ', CSPCL) - set('\t', CSPCL) -}; - -/* syntax table used when in double quotes */ -const char dqsyntax[258] = { CFAKE, CEOF, - set_range(CTL_FIRST, CTL_LAST, CCTL) - set('\n', CNL) - set('\\', CBACK) - set('"', CDQUOTE) - set('`', CBQUOTE) - set('$', CVAR) - set('}', CENDVAR) - /* ':/' for tilde expansion, '-]' for [a\-x] pattern ranges */ - set('!', CCTL) - set('*', CCTL) - set('?', CCTL) - set('[', CCTL) - set('=', CCTL) - set('~', CCTL) - set(':', CCTL) - set('/', CCTL) - set('-', CCTL) - set(']', CCTL) -}; - -/* syntax table used when in single quotes */ -const char sqsyntax[258] = { CFAKE, CEOF, - set_range(CTL_FIRST, CTL_LAST, CCTL) - set('\n', CNL) - set('\'', CSQUOTE) - set('\\', CSBACK) - /* ':/' for tilde expansion, '-]' for [a\-x] pattern ranges */ - set('!', CCTL) - set('*', CCTL) - set('?', CCTL) - set('[', CCTL) - set('=', CCTL) - set('~', CCTL) - set(':', CCTL) - set('/', CCTL) - set('-', CCTL) - set(']', CCTL) -}; - -/* syntax table used when in arithmetic */ -const char arisyntax[258] = { CFAKE, CEOF, - set_range(CTL_FIRST, CTL_LAST, CCTL) - set('\n', CNL) - set('\\', CBACK) - set('`', CBQUOTE) - set('\'', CSQUOTE) - set('"', CDQUOTE) - set('$', CVAR) - set('}', CENDVAR) - set('(', CLP) - set(')', CRP) -}; - -/* character classification table */ -const char is_type[258] = { 0, 0, - set_range('0', '9', ISDIGIT) - set_range('a', 'z', ISLOWER) - set_range('A', 'Z', ISUPPER) - set('_', ISUNDER) - set('#', ISSPECL) - set('?', ISSPECL) - set('$', ISSPECL) - set('!', ISSPECL) - set('-', ISSPECL) - set('*', ISSPECL) - set('@', ISSPECL) - set(' ', ISSPACE) - set('\t', ISSPACE) - set('\n', ISSPACE) -}; diff --git a/bin/sh/syntax.h b/bin/sh/syntax.h deleted file mode 100644 index ad064b8..0000000 --- a/bin/sh/syntax.h +++ /dev/null @@ -1,98 +0,0 @@ -/* $NetBSD: syntax.h,v 1.11 2018/12/03 06:40:26 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#include <ctype.h> - -/* Syntax classes */ -#define CWORD 0 /* character is nothing special */ -#define CNL 1 /* newline character */ -#define CBACK 2 /* a backslash character */ -#define CSQUOTE 3 /* single quote */ -#define CDQUOTE 4 /* double quote */ -#define CBQUOTE 5 /* backwards single quote */ -#define CVAR 6 /* a dollar sign */ -#define CENDVAR 7 /* a '}' character */ -#define CLP 8 /* a left paren in arithmetic */ -#define CRP 9 /* a right paren in arithmetic */ -#define CEOF 10 /* end of file */ -#define CSPCL 11 /* these terminate a word */ -#define CCTL 12 /* like CWORD, except it must be escaped */ -#define CSBACK 13 /* a backslash in a single quote syntax */ -#define CFAKE 14 /* a delimiter that does not exist */ - /* - * note CSBACK == (CCTL|1) - * the code does not rely upon that, but keeping it allows a - * smart enough compiler to optimise some tests - */ - -/* Syntax classes for is_ functions */ -#define ISDIGIT 01 /* a digit */ -#define ISUPPER 02 /* an upper case letter */ -#define ISLOWER 04 /* a lower case letter */ -#define ISUNDER 010 /* an underscore */ -#define ISSPECL 020 /* the name of a special parameter */ -#define ISSPACE 040 /* a white space character */ - -#define PEOF (CHAR_MIN - 1) -#define PFAKE (CHAR_MIN - 2) -#define SYNBASE (-PFAKE) - - -#define BASESYNTAX (basesyntax + SYNBASE) -#define DQSYNTAX (dqsyntax + SYNBASE) -#define SQSYNTAX (sqsyntax + SYNBASE) -#define ARISYNTAX (arisyntax + SYNBASE) - -/* These defines assume that the digits are contiguous (which is guaranteed) */ -#define is_digit(c) ((unsigned)((c) - '0') <= 9) -#define sh_ctype(c) (is_type+SYNBASE)[(int)(c)] -#define is_upper(c) (sh_ctype(c) & ISUPPER) -#define is_lower(c) (sh_ctype(c) & ISLOWER) -#define is_alpha(c) (sh_ctype(c) & (ISUPPER|ISLOWER)) -#define is_name(c) (sh_ctype(c) & (ISUPPER|ISLOWER|ISUNDER)) -#define is_in_name(c) (sh_ctype(c) & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT)) -#define is_special(c) (sh_ctype(c) & (ISSPECL|ISDIGIT)) -#define is_space(c) (sh_ctype(c) & ISSPACE) -#define digit_val(c) ((c) - '0') - -/* true if the arg char needs CTLESC to protect it */ -#define NEEDESC(c) (SQSYNTAX[(int)(c)] == CCTL || \ - SQSYNTAX[(int)(c)] == CSBACK) - -extern const char basesyntax[]; -extern const char dqsyntax[]; -extern const char sqsyntax[]; -extern const char arisyntax[]; -extern const char is_type[]; diff --git a/bin/sh/trap.c b/bin/sh/trap.c deleted file mode 100644 index cb641fd..0000000 --- a/bin/sh/trap.c +++ /dev/null @@ -1,837 +0,0 @@ -/* $NetBSD: trap.c,v 1.51 2019/01/18 06:28:09 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; -#else -__RCSID("$NetBSD: trap.c,v 1.51 2019/01/18 06:28:09 kre Exp $"); -#endif -#endif /* not lint */ - -#include <signal.h> -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <termios.h> - -#undef CEOF /* from <termios.h> but concflicts with sh use */ - -#include <sys/ioctl.h> -#include <sys/resource.h> - -#include "shell.h" -#include "main.h" -#include "nodes.h" /* for other headers */ -#include "eval.h" -#include "jobs.h" -#include "show.h" -#include "options.h" -#include "builtins.h" -#include "syntax.h" -#include "output.h" -#include "memalloc.h" -#include "error.h" -#include "trap.h" -#include "mystring.h" -#include "var.h" - - -/* - * Sigmode records the current value of the signal handlers for the various - * modes. A value of zero means that the current handler is not known. - * S_HARD_IGN indicates that the signal was ignored on entry to the shell, - */ - -#define S_DFL 1 /* default signal handling (SIG_DFL) */ -#define S_CATCH 2 /* signal is caught */ -#define S_IGN 3 /* signal is ignored (SIG_IGN) */ -#define S_HARD_IGN 4 /* signal is ignored permenantly */ -#define S_RESET 5 /* temporary - to reset a hard ignored sig */ - - -MKINIT char sigmode[NSIG]; /* current value of signal */ -static volatile sig_atomic_t gotsig[NSIG];/* indicates specified signal received */ -volatile sig_atomic_t pendingsigs; /* indicates some signal received */ - -int traps_invalid; /* in a subshell, but trap[] not yet cleared */ -static char * volatile trap[NSIG]; /* trap handler commands */ -static int in_dotrap; -static int last_trapsig; - -static int exiting; /* exitshell() has been done */ -static int exiting_status; /* the status to use for exit() */ - -static int getsigaction(int, sig_t *); -STATIC const char *trap_signame(int); -void printsignals(struct output *, int); - -/* - * return the signal number described by `p' (as a number or a name) - * or -1 if it isn't one - */ - -static int -signame_to_signum(const char *p) -{ - int i; - - if (is_number(p)) - return number(p); - - if (strcasecmp(p, "exit") == 0 ) - return 0; - - i = signalnumber(p); - if (i == 0) - i = -1; - return i; -} - -/* - * return the name of a signal used by the "trap" command - */ -STATIC const char * -trap_signame(int signo) -{ - static char nbuf[12]; - const char *p; - - if (signo == 0) - return "EXIT"; - p = signalname(signo); - if (p != NULL) - return p; - (void)snprintf(nbuf, sizeof nbuf, "%d", signo); - return nbuf; -} - -#ifdef SMALL -/* - * Print a list of valid signal names - */ -void -printsignals(struct output *out, int len) -{ - int n; - - if (len != 0) - outc(' ', out); - for (n = 1; n < NSIG; n++) { - outfmt(out, "%s", trap_signame(n)); - if ((n == NSIG/2) || n == (NSIG - 1)) - outstr("\n", out); - else - outc(' ', out); - } -} -#else /* !SMALL */ -/* - * Print the names of all the signals (neatly) to fp - * "len" gives the number of chars already printed to - * the current output line (in kill.c, always 0) - */ -void -printsignals(struct output *out, int len) -{ - int sig; - int nl, pad; - const char *name; - int termwidth = 80; - - if ((name = bltinlookup("COLUMNS", 1)) != NULL) - termwidth = (int)strtol(name, NULL, 10); - else if (isatty(1)) { - struct winsize win; - - if (ioctl(1, TIOCGWINSZ, &win) == 0 && win.ws_col > 0) - termwidth = win.ws_col; - } - - if (posix) - pad = 1; - else - pad = (len | 7) + 1 - len; - - for (sig = 0; (sig = signalnext(sig)) != 0; ) { - name = signalname(sig); - if (name == NULL) - continue; - - nl = strlen(name); - - if (len > 0 && nl + len + pad >= termwidth) { - outc('\n', out); - len = 0; - pad = 0; - } else if (pad > 0 && len != 0) - outfmt(out, "%*s", pad, ""); - else - pad = 0; - - len += nl + pad; - if (!posix) - pad = (nl | 7) + 1 - nl; - else - pad = 1; - - outstr(name, out); - } - if (len != 0) - outc('\n', out); -} -#endif /* SMALL */ - -/* - * The trap builtin. - */ - -int -trapcmd(int argc, char **argv) -{ - char *action; - char **ap; - int signo; - int errs = 0; - int printonly = 0; - - ap = argv + 1; - - CTRACE(DBG_TRAP, ("trapcmd: ")); - if (argc == 2 && strcmp(*ap, "-l") == 0) { - CTRACE(DBG_TRAP, ("-l\n")); - out1str("EXIT"); - printsignals(out1, 4); - return 0; - } - if (argc == 2 && strcmp(*ap, "-") == 0) { - CTRACE(DBG_TRAP, ("-\n")); - for (signo = 0; signo < NSIG; signo++) { - if (trap[signo] == NULL) - continue; - INTOFF; - ckfree(trap[signo]); - trap[signo] = NULL; - if (signo != 0) - setsignal(signo, 0); - INTON; - } - traps_invalid = 0; - return 0; - } - if (argc >= 2 && strcmp(*ap, "-p") == 0) { - CTRACE(DBG_TRAP, ("-p ")); - printonly = 1; - ap++; - argc--; - } - - if (argc > 1 && strcmp(*ap, "--") == 0) { - argc--; - ap++; - } - - if (argc <= 1) { - int count; - - CTRACE(DBG_TRAP, ("*all*\n")); - if (printonly) { - for (count = 0, signo = 0 ; signo < NSIG ; signo++) - if (trap[signo] == NULL) { - if (count == 0) - out1str("trap -- -"); - out1fmt(" %s", trap_signame(signo)); - /* oh! unlucky 13 */ - if (++count >= 13) { - out1str("\n"); - count = 0; - } - } - if (count) - out1str("\n"); - } - - for (count = 0, signo = 0 ; signo < NSIG ; signo++) - if (trap[signo] != NULL && trap[signo][0] == '\0') { - if (count == 0) - out1str("trap -- ''"); - out1fmt(" %s", trap_signame(signo)); - /* - * the prefix is 10 bytes, with 4 byte - * signal names (common) we have room in - * the 70 bytes left on a normal line for - * 70/(4+1) signals, that's 14, but to - * allow for the occasional longer sig name - * we output one less... - */ - if (++count >= 13) { - out1str("\n"); - count = 0; - } - } - if (count) - out1str("\n"); - - for (signo = 0 ; signo < NSIG ; signo++) - if (trap[signo] != NULL && trap[signo][0] != '\0') { - out1str("trap -- "); - print_quoted(trap[signo]); - out1fmt(" %s\n", trap_signame(signo)); - } - - return 0; - } - CTRACE(DBG_TRAP, ("\n")); - - action = NULL; - - if (!printonly && traps_invalid) - free_traps(); - - if (!printonly && !is_number(*ap)) { - if ((*ap)[0] == '-' && (*ap)[1] == '\0') - ap++; /* reset to default */ - else - action = *ap++; /* can be '' for "ignore" */ - argc--; - } - - if (argc < 2) { /* there must be at least 1 condition */ - out2str("Usage: trap [-l]\n" - " trap -p [condition ...]\n" - " trap action condition ...\n" - " trap N condition ...\n"); - return 2; - } - - - while (*ap) { - signo = signame_to_signum(*ap); - - if (signo < 0 || signo >= NSIG) { - /* This is not a fatal error, so sayeth posix */ - outfmt(out2, "trap: '%s' bad condition\n", *ap); - errs = 1; - ap++; - continue; - } - ap++; - - if (printonly) { - out1str("trap -- "); - if (trap[signo] == NULL) - out1str("-"); - else - print_quoted(trap[signo]); - out1fmt(" %s\n", trap_signame(signo)); - continue; - } - - INTOFF; - if (action) - action = savestr(action); - - VTRACE(DBG_TRAP, ("trap for %d from %s%s%s to %s%s%s\n", signo, - trap[signo] ? "'" : "", trap[signo] ? trap[signo] : "-", - trap[signo] ? "'" : "", action ? "'" : "", - action ? action : "-", action ? "'" : "")); - - if (trap[signo]) - ckfree(trap[signo]); - - trap[signo] = action; - - if (signo != 0) - setsignal(signo, 0); - INTON; - } - return errs; -} - - - -/* - * Clear traps on a fork or vfork. - * Takes one arg vfork, to tell it to not be destructive of - * the parents variables. - */ -void -clear_traps(int vforked) -{ - char * volatile *tp; - - VTRACE(DBG_TRAP, ("clear_traps(%d)\n", vforked)); - if (!vforked) - traps_invalid = 1; - - for (tp = &trap[1] ; tp < &trap[NSIG] ; tp++) { - if (*tp && **tp) { /* trap not NULL or SIG_IGN */ - INTOFF; - setsignal(tp - trap, vforked == 1); - INTON; - } - } - if (vforked == 2) - free_traps(); -} - -void -free_traps(void) -{ - char * volatile *tp; - - VTRACE(DBG_TRAP, ("free_traps%s\n", traps_invalid ? "(invalid)" : "")); - INTOFF; - for (tp = trap ; tp < &trap[NSIG] ; tp++) - if (*tp && **tp) { - ckfree(*tp); - *tp = NULL; - } - traps_invalid = 0; - INTON; -} - -/* - * See if there are any defined traps - */ -int -have_traps(void) -{ - char * volatile *tp; - - if (traps_invalid) - return 0; - - for (tp = trap ; tp < &trap[NSIG] ; tp++) - if (*tp && **tp) /* trap not NULL or SIG_IGN */ - return 1; - return 0; -} - -/* - * Set the signal handler for the specified signal. The routine figures - * out what it should be set to. - */ -void -setsignal(int signo, int vforked) -{ - int action; - sig_t sigact = SIG_DFL, sig; - char *t, tsig; - - if (traps_invalid || (t = trap[signo]) == NULL) - action = S_DFL; - else if (*t != '\0') - action = S_CATCH; - else - action = S_IGN; - - VTRACE(DBG_TRAP, ("setsignal(%d%s) -> %d", signo, - vforked ? ", VF" : "", action)); - if (rootshell && !vforked && action == S_DFL) { - switch (signo) { - case SIGINT: - if (iflag || minusc || sflag == 0) - action = S_CATCH; - break; - case SIGQUIT: -#ifdef DEBUG - if (debug) - break; -#endif - /* FALLTHROUGH */ - case SIGTERM: - if (rootshell && iflag) - action = S_IGN; - break; -#if JOBS - case SIGTSTP: - case SIGTTOU: - if (rootshell && mflag) - action = S_IGN; - break; -#endif - } - } - - /* - * Never let users futz with SIGCHLD - * instead we will give them pseudo SIGCHLD's - * when background jobs complete. - */ - if (signo == SIGCHLD) - action = S_DFL; - - VTRACE(DBG_TRAP, (" -> %d", action)); - - t = &sigmode[signo]; - tsig = *t; - if (tsig == 0) { - /* - * current setting unknown - */ - if (!getsigaction(signo, &sigact)) { - /* - * Pretend it worked; maybe we should give a warning - * here, but other shells don't. We don't alter - * sigmode, so that we retry every time. - */ - VTRACE(DBG_TRAP, (" getsigaction (%d)\n", errno)); - return; - } - VTRACE(DBG_TRAP, (" [%s]%s%s", sigact==SIG_IGN ? "IGN" : - sigact==SIG_DFL ? "DFL" : "caught", - iflag ? "i" : "", mflag ? "m" : "")); - - if (sigact == SIG_IGN) { - /* - * POSIX 3.14.13 states that non-interactive shells - * should ignore trap commands for signals that were - * ignored upon entry, and leaves the behavior - * unspecified for interactive shells. On interactive - * shells, or if job control is on, and we have a job - * control related signal, we allow the trap to work. - * - * This change allows us to be POSIX compliant, and - * at the same time override the default behavior if - * we need to by setting the interactive flag. - */ - if ((mflag && (signo == SIGTSTP || - signo == SIGTTIN || signo == SIGTTOU)) || iflag) { - tsig = S_IGN; - } else - tsig = S_HARD_IGN; - } else { - tsig = S_RESET; /* force to be set */ - } - } - VTRACE(DBG_TRAP, (" tsig=%d\n", tsig)); - - if (tsig == S_HARD_IGN || tsig == action) - return; - - switch (action) { - case S_DFL: sigact = SIG_DFL; break; - case S_CATCH: sigact = onsig; break; - case S_IGN: sigact = SIG_IGN; break; - } - - sig = signal(signo, sigact); - - if (sig != SIG_ERR) { - sigset_t ss; - - if (!vforked) - *t = action; - - if (action == S_CATCH) - (void)siginterrupt(signo, 1); - /* - * If our parent accidentally blocked signals for - * us make sure we unblock them - */ - (void)sigemptyset(&ss); - (void)sigaddset(&ss, signo); - (void)sigprocmask(SIG_UNBLOCK, &ss, NULL); - } - return; -} - -/* - * Return the current setting for sig w/o changing it. - */ -static int -getsigaction(int signo, sig_t *sigact) -{ - struct sigaction sa; - - if (sigaction(signo, (struct sigaction *)0, &sa) == -1) - return 0; - *sigact = (sig_t) sa.sa_handler; - return 1; -} - -/* - * Ignore a signal. - */ - -void -ignoresig(int signo, int vforked) -{ - if (sigmode[signo] == 0) - setsignal(signo, vforked); - - VTRACE(DBG_TRAP, ("ignoresig(%d%s)\n", signo, vforked ? ", VF" : "")); - if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { - signal(signo, SIG_IGN); - if (!vforked) - sigmode[signo] = S_IGN; - } -} - -char * -child_trap(void) -{ - char * p; - - p = trap[SIGCHLD]; - - if (traps_invalid || (p != NULL && *p == '\0')) - p = NULL; - - return p; -} - - -#ifdef mkinit -INCLUDE <signal.h> -INCLUDE "trap.h" -INCLUDE "shell.h" -INCLUDE "show.h" - -SHELLPROC { - char *sm; - - INTOFF; - clear_traps(2); - for (sm = sigmode ; sm < sigmode + NSIG ; sm++) { - if (*sm == S_IGN) { - *sm = S_HARD_IGN; - VTRACE(DBG_TRAP, ("SHELLPROC: %d -> hard_ign\n", - (sm - sigmode))); - } - } - INTON; -} -#endif - - - -/* - * Signal handler. - */ - -void -onsig(int signo) -{ - CTRACE(DBG_SIG, ("Signal %d, had: pending %d, gotsig[%d]=%d\n", - signo, pendingsigs, signo, gotsig[signo])); - - /* This should not be needed. - signal(signo, onsig); - */ - - if (signo == SIGINT && (traps_invalid || trap[SIGINT] == NULL)) { - onint(); - return; - } - - /* - * if the signal will do nothing, no point reporting it - */ - if (!traps_invalid && trap[signo] != NULL && trap[signo][0] != '\0' && - signo != SIGCHLD) { - gotsig[signo] = 1; - pendingsigs++; - } -} - - - -/* - * Called to execute a trap. Perhaps we should avoid entering new trap - * handlers while we are executing a trap handler. - */ - -void -dotrap(void) -{ - int i; - char *tr; - int savestatus; - struct skipsave saveskip; - - in_dotrap++; - - CTRACE(DBG_TRAP, ("dotrap[%d]: %d pending, traps %sinvalid\n", - in_dotrap, pendingsigs, traps_invalid ? "" : "not ")); - for (;;) { - pendingsigs = 0; - for (i = 1 ; ; i++) { - if (i >= NSIG) - return; - if (gotsig[i]) - break; - } - gotsig[i] = 0; - - if (traps_invalid) - continue; - - tr = trap[i]; - - CTRACE(DBG_TRAP|DBG_SIG, ("dotrap %d: %s%s%s\n", i, - tr ? "\"" : "", tr ? tr : "NULL", tr ? "\"" : "")); - - if (tr != NULL) { - last_trapsig = i; - save_skipstate(&saveskip); - savestatus = exitstatus; - - tr = savestr(tr); /* trap code may free trap[i] */ - evalstring(tr, 0); - ckfree(tr); - - if (current_skipstate() == SKIPNONE || - saveskip.state != SKIPNONE) { - restore_skipstate(&saveskip); - exitstatus = savestatus; - } - } - } - - in_dotrap--; -} - -int -lastsig(void) -{ - int i; - - for (i = NSIG; --i > 0; ) - if (gotsig[i]) - return i; - return SIGINT; /* XXX */ -} - -/* - * Controls whether the shell is interactive or not. - */ - - -void -setinteractive(int on) -{ - static int is_interactive; - - if (on == is_interactive) - return; - setsignal(SIGINT, 0); - setsignal(SIGQUIT, 0); - setsignal(SIGTERM, 0); - is_interactive = on; -} - - - -/* - * Called to exit the shell. - */ -void -exitshell(int status) -{ - CTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP, - ("pid %d: exitshell(%d)\n", getpid(), status)); - - exiting = 1; - exiting_status = status; - exitshell_savedstatus(); -} - -void -exitshell_savedstatus(void) -{ - struct jmploc loc; - char *p; - volatile int sig = 0; - int s; - sigset_t sigs; - - CTRACE(DBG_ERRS|DBG_PROCS|DBG_CMDS|DBG_TRAP, - ("pid %d: exitshell_savedstatus()%s $?=%d xs=%d dt=%d ts=%d\n", - getpid(), exiting ? " exiting" : "", exitstatus, - exiting_status, in_dotrap, last_trapsig)); - - if (!exiting) { - if (in_dotrap && last_trapsig) { - sig = last_trapsig; - exiting_status = sig + 128; - } else - exiting_status = exitstatus; - } - exitstatus = exiting_status; - - if (!setjmp(loc.loc)) { - handler = &loc; - - if (!traps_invalid && (p = trap[0]) != NULL && *p != '\0') { - reset_eval(); - trap[0] = NULL; - VTRACE(DBG_TRAP, ("exit trap: \"%s\"\n", p)); - evalstring(p, 0); - } - } - - INTOFF; /* we're done, no more interrupts. */ - - if (!setjmp(loc.loc)) { - handler = &loc; /* probably unnecessary */ - flushall(); -#if JOBS - setjobctl(0); -#endif - } - - if ((s = sig) != 0 && s != SIGSTOP && s != SIGTSTP && s != SIGTTIN && - s != SIGTTOU) { - struct rlimit nocore; - - /* - * if the signal is of the core dump variety, don't... - */ - nocore.rlim_cur = nocore.rlim_max = 0; - (void) setrlimit(RLIMIT_CORE, &nocore); - - signal(s, SIG_DFL); - sigemptyset(&sigs); - sigaddset(&sigs, s); - sigprocmask(SIG_UNBLOCK, &sigs, NULL); - - kill(getpid(), s); - } - _exit(exiting_status); - /* NOTREACHED */ -} diff --git a/bin/sh/trap.h b/bin/sh/trap.h deleted file mode 100644 index 7ea3ef1..0000000 --- a/bin/sh/trap.h +++ /dev/null @@ -1,52 +0,0 @@ -/* $NetBSD: trap.h,v 1.25 2018/12/03 10:53:29 martin Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)trap.h 8.3 (Berkeley) 6/5/95 - */ - -extern volatile sig_atomic_t pendingsigs; - -extern int traps_invalid; - -void clear_traps(int); -void free_traps(void); -int have_traps(void); -void setsignal(int, int); -void ignoresig(int, int); -void onsig(int); -void dotrap(void); -char *child_trap(void); -void setinteractive(int); -void exitshell(int) __dead; -void exitshell_savedstatus(void) __dead; -int lastsig(void); diff --git a/bin/sh/var.c b/bin/sh/var.c deleted file mode 100644 index 378598c..0000000 --- a/bin/sh/var.c +++ /dev/null @@ -1,1587 +0,0 @@ -/* $NetBSD: var.c,v 1.75 2019/01/21 13:27:29 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/cdefs.h> -#ifndef lint -#if 0 -static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; -#else -__RCSID("$NetBSD: var.c,v 1.75 2019/01/21 13:27:29 kre Exp $"); -#endif -#endif /* not lint */ - -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <paths.h> -#include <limits.h> -#include <time.h> -#include <pwd.h> -#include <fcntl.h> -#include <inttypes.h> - -/* - * Shell variables. - */ - -#include "shell.h" -#include "output.h" -#include "expand.h" -#include "nodes.h" /* for other headers */ -#include "eval.h" /* defines cmdenviron */ -#include "exec.h" -#include "syntax.h" -#include "options.h" -#include "builtins.h" -#include "mail.h" -#include "var.h" -#include "memalloc.h" -#include "error.h" -#include "mystring.h" -#include "parser.h" -#include "show.h" -#include "machdep.h" -#ifndef SMALL -#include "myhistedit.h" -#endif - -#ifdef SMALL -#define VTABSIZE 39 -#else -#define VTABSIZE 517 -#endif - - -struct varinit { - struct var *var; - int flags; - const char *text; - union var_func_union v_u; -}; -#define func v_u.set_func -#define rfunc v_u.ref_func - -char *get_lineno(struct var *); - -#ifndef SMALL -char *get_tod(struct var *); -char *get_hostname(struct var *); -char *get_seconds(struct var *); -char *get_euser(struct var *); -char *get_random(struct var *); -#endif - -struct localvar *localvars; - -#ifndef SMALL -struct var vhistsize; -struct var vterm; -struct var editrc; -struct var ps_lit; -#endif -struct var vifs; -struct var vmail; -struct var vmpath; -struct var vpath; -struct var vps1; -struct var vps2; -struct var vps4; -struct var vvers; -struct var voptind; -struct var line_num; -#ifndef SMALL -struct var tod; -struct var host_name; -struct var seconds; -struct var euname; -struct var random_num; - -intmax_t sh_start_time; -#endif - -struct var line_num; -int line_number; -int funclinebase = 0; -int funclineabs = 0; - -char ifs_default[] = " \t\n"; - -const struct varinit varinit[] = { -#ifndef SMALL - { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", - { .set_func= sethistsize } }, -#endif - { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n", - { NULL } }, - { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", - { NULL } }, - { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", - { NULL } }, - { &vvers, VSTRFIXED|VTEXTFIXED|VNOEXPORT, "NETBSD_SHELL=", - { NULL } }, - { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH, - { .set_func= changepath } }, - /* - * vps1 depends on uid - */ - { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", - { NULL } }, - { &vps4, VSTRFIXED|VTEXTFIXED, "PS4=+ ", - { NULL } }, -#ifndef SMALL - { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=", - { .set_func= setterm } }, - { &editrc, VSTRFIXED|VTEXTFIXED|VUNSET, "EDITRC=", - { .set_func= set_editrc } }, - { &ps_lit, VSTRFIXED|VTEXTFIXED|VUNSET, "PSlit=", - { .set_func= set_prompt_lit } }, -#endif - { &voptind, VSTRFIXED|VTEXTFIXED|VNOFUNC, "OPTIND=1", - { .set_func= getoptsreset } }, - { &line_num, VSTRFIXED|VTEXTFIXED|VFUNCREF|VSPECIAL, "LINENO=1", - { .ref_func= get_lineno } }, -#ifndef SMALL - { &tod, VSTRFIXED|VTEXTFIXED|VFUNCREF, "ToD=", - { .ref_func= get_tod } }, - { &host_name, VSTRFIXED|VTEXTFIXED|VFUNCREF, "HOSTNAME=", - { .ref_func= get_hostname } }, - { &seconds, VSTRFIXED|VTEXTFIXED|VFUNCREF, "SECONDS=", - { .ref_func= get_seconds } }, - { &euname, VSTRFIXED|VTEXTFIXED|VFUNCREF, "EUSER=", - { .ref_func= get_euser } }, - { &random_num, VSTRFIXED|VTEXTFIXED|VFUNCREF|VSPECIAL, "RANDOM=", - { .ref_func= get_random } }, -#endif - { NULL, 0, NULL, - { NULL } } -}; - -struct var *vartab[VTABSIZE]; - -STATIC int strequal(const char *, const char *); -STATIC struct var *find_var(const char *, struct var ***, int *); -STATIC void showvar(struct var *, const char *, const char *, int); -static void export_usage(const char *) __dead; - -/* - * Initialize the varable symbol tables and import the environment - */ - -#ifdef mkinit -INCLUDE <stdio.h> -INCLUDE <unistd.h> -INCLUDE <time.h> -INCLUDE "var.h" -INCLUDE "version.h" -MKINIT char **environ; -INIT { - char **envp; - char buf[64]; - -#ifndef SMALL - sh_start_time = (intmax_t)time((time_t *)0); -#endif - /* - * Set up our default variables and their values. - */ - initvar(); - - /* - * Import variables from the environment, which will - * override anything initialised just previously. - */ - for (envp = environ ; *envp ; envp++) { - if (strchr(*envp, '=')) { - setvareq(*envp, VEXPORT|VTEXTFIXED); - } - } - - /* - * Set variables which override anything read from environment. - * - * PPID is readonly - * Always default IFS - * POSIX: "Whenever the shell is invoked, OPTIND shall - * be initialized to 1." - * PSc indicates the root/non-root status of this shell. - * START_TIME belongs only to this shell. - * NETBSD_SHELL is a constant (readonly), and is never exported - * LINENO is simply magic... - */ - snprintf(buf, sizeof(buf), "%d", (int)getppid()); - setvar("PPID", buf, VREADONLY); - setvar("IFS", ifs_default, VTEXTFIXED); - setvar("OPTIND", "1", VTEXTFIXED); - setvar("PSc", (geteuid() == 0 ? "#" : "$"), VTEXTFIXED); - -#ifndef SMALL - snprintf(buf, sizeof(buf), "%jd", sh_start_time); - setvar("START_TIME", buf, VTEXTFIXED); -#endif - - setvar("NETBSD_SHELL", NETBSD_SHELL -#ifdef BUILD_DATE - " BUILD:" BUILD_DATE -#endif -#ifdef DEBUG - " DEBUG" -#endif -#if !defined(JOBS) || JOBS == 0 - " -JOBS" -#endif -#ifndef DO_SHAREDVFORK - " -VFORK" -#endif -#ifdef SMALL - " SMALL" -#endif -#ifdef TINY - " TINY" -#endif -#ifdef OLD_TTY_DRIVER - " OLD_TTY" -#endif -#ifdef SYSV - " SYSV" -#endif -#ifndef BSD - " -BSD" -#endif -#ifdef BOGUS_NOT_COMMAND - " BOGUS_NOT" -#endif - , VTEXTFIXED|VREADONLY|VNOEXPORT); - - setvar("LINENO", "1", VTEXTFIXED); -} -#endif - - -/* - * This routine initializes the builtin variables. It is called when the - * shell is initialized and again when a shell procedure is spawned. - */ - -void -initvar(void) -{ - const struct varinit *ip; - struct var *vp; - struct var **vpp; - - for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { - if (find_var(ip->text, &vpp, &vp->name_len) != NULL) - continue; - vp->next = *vpp; - *vpp = vp; - vp->text = strdup(ip->text); - vp->flags = (ip->flags & ~VTEXTFIXED) | VSTRFIXED; - vp->v_u = ip->v_u; - } - /* - * PS1 depends on uid - */ - if (find_var("PS1", &vpp, &vps1.name_len) == NULL) { - vps1.next = *vpp; - *vpp = &vps1; - vps1.flags = VSTRFIXED; - vps1.text = NULL; - choose_ps1(); - } -} - -void -choose_ps1(void) -{ - uid_t u = geteuid(); - - if ((vps1.flags & (VTEXTFIXED|VSTACK)) == 0) - free(vps1.text); - vps1.text = strdup(u != 0 ? "PS1=$ " : "PS1=# "); - vps1.flags &= ~(VTEXTFIXED|VSTACK); - - /* - * Update PSc whenever we feel the need to update PS1 - */ - setvarsafe("PSc", (u == 0 ? "#" : "$"), 0); -} - -/* - * Validate a string as a valid variable name - * nb: not parameter - special params and such are "invalid" here. - * Name terminated by either \0 or the term param (usually '=' or '\0'). - * - * If not NULL, the length of the (intended) name is returned via len - */ - -int -validname(const char *name, int term, int *len) -{ - const char *p = name; - int ok = 1; - - if (p == NULL || *p == '\0' || *p == term) { - if (len != NULL) - *len = 0; - return 0; - } - - if (!is_name(*p)) - ok = 0; - p++; - for (;;) { - if (*p == '\0' || *p == term) - break; - if (!is_in_name(*p)) - ok = 0; - p++; - } - if (len != NULL) - *len = p - name; - - return ok; -} - -/* - * Safe version of setvar, returns 1 on success 0 on failure. - */ - -int -setvarsafe(const char *name, const char *val, int flags) -{ - struct jmploc jmploc; - struct jmploc * const savehandler = handler; - int volatile err = 0; - - if (setjmp(jmploc.loc)) - err = 1; - else { - handler = &jmploc; - setvar(name, val, flags); - } - handler = savehandler; - return err; -} - -/* - * Set the value of a variable. The flags argument is ored with the - * flags of the variable. If val is NULL, the variable is unset. - * - * This always copies name and val when setting a variable, so - * the source strings can be from anywhere, and are no longer needed - * after this function returns. The VTEXTFIXED and VSTACK flags should - * not be used (but just in case they were, clear them.) - */ - -void -setvar(const char *name, const char *val, int flags) -{ - const char *p; - const char *q; - char *d; - int len; - int namelen; - char *nameeq; - - p = name; - - if (!validname(p, '=', &namelen)) - error("%.*s: bad variable name", namelen, name); - len = namelen + 2; /* 2 is space for '=' and '\0' */ - if (val == NULL) { - flags |= VUNSET; - } else { - len += strlen(val); - } - d = nameeq = ckmalloc(len); - q = name; - while (--namelen >= 0) - *d++ = *q++; - *d++ = '='; - *d = '\0'; - if (val) - scopy(val, d); - setvareq(nameeq, flags & ~(VTEXTFIXED | VSTACK)); -} - - - -/* - * Same as setvar except that the variable and value are passed in - * the first argument as name=value. Since the first argument will - * be actually stored in the table, it should not be a string that - * will go away. The flags (VTEXTFIXED or VSTACK) can be used to - * indicate the source of the string (if neither is set, the string will - * eventually be free()d when a replacement value is assigned.) - */ - -void -setvareq(char *s, int flags) -{ - struct var *vp, **vpp; - int nlen; - - VTRACE(DBG_VARS, ("setvareq([%s],%#x) aflag=%d ", s, flags, aflag)); - if (aflag && !(flags & VNOEXPORT)) - flags |= VEXPORT; - vp = find_var(s, &vpp, &nlen); - if (vp != NULL) { - VTRACE(DBG_VARS, ("was [%s] fl:%#x\n", vp->text, - vp->flags)); - if (vp->flags & VREADONLY) { - if ((flags & (VTEXTFIXED|VSTACK)) == 0) - ckfree(s); - if (flags & VNOERROR) - return; - error("%.*s: is read only", vp->name_len, vp->text); - } - if (flags & VNOSET) { - if ((flags & (VTEXTFIXED|VSTACK)) == 0) - ckfree(s); - return; - } - - INTOFF; - - if (vp->func && !(vp->flags & VFUNCREF) && !(flags & VNOFUNC)) - (*vp->func)(s + vp->name_len + 1); - - if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) - ckfree(vp->text); - - /* - * if we set a magic var, the magic dissipates, - * unless it is very special indeed. - */ - if (vp->rfunc && (vp->flags & (VFUNCREF|VSPECIAL)) == VFUNCREF) - vp->rfunc = NULL; - - vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); - if (flags & VNOEXPORT) - vp->flags &= ~VEXPORT; - if (flags & VDOEXPORT) - vp->flags &= ~VNOEXPORT; - if (vp->flags & VNOEXPORT) - flags &= ~VEXPORT; - vp->flags |= flags & ~(VNOFUNC | VDOEXPORT); - vp->text = s; - - /* - * We could roll this to a function, to handle it as - * a regular variable function callback, but why bother? - */ - if (vp == &vmpath || (vp == &vmail && ! mpathset())) - chkmail(1); - - INTON; - return; - } - /* not found */ - if (flags & VNOSET) { - VTRACE(DBG_VARS, ("new noset\n")); - if ((flags & (VTEXTFIXED|VSTACK)) == 0) - ckfree(s); - return; - } - vp = ckmalloc(sizeof (*vp)); - vp->flags = flags & ~(VNOFUNC|VFUNCREF|VDOEXPORT); - vp->text = s; - vp->name_len = nlen; - vp->func = NULL; - vp->next = *vpp; - *vpp = vp; - - VTRACE(DBG_VARS, ("new [%s] (%d) %#x\n", s, nlen, vp->flags)); -} - - - -/* - * Process a linked list of variable assignments. - */ - -void -listsetvar(struct strlist *list, int flags) -{ - struct strlist *lp; - - INTOFF; - for (lp = list ; lp ; lp = lp->next) { - setvareq(savestr(lp->text), flags); - } - INTON; -} - -void -listmklocal(struct strlist *list, int flags) -{ - struct strlist *lp; - - for (lp = list ; lp ; lp = lp->next) - mklocal(lp->text, flags); -} - - -/* - * Find the value of a variable. Returns NULL if not set. - */ - -char * -lookupvar(const char *name) -{ - struct var *v; - - v = find_var(name, NULL, NULL); - if (v == NULL || v->flags & VUNSET) - return NULL; - if (v->rfunc && (v->flags & VFUNCREF) != 0) - return (*v->rfunc)(v) + v->name_len + 1; - return v->text + v->name_len + 1; -} - - - -/* - * Search the environment of a builtin command. If the second argument - * is nonzero, return the value of a variable even if it hasn't been - * exported. - */ - -char * -bltinlookup(const char *name, int doall) -{ - struct strlist *sp; - struct var *v; - - for (sp = cmdenviron ; sp ; sp = sp->next) { - if (strequal(sp->text, name)) - return strchr(sp->text, '=') + 1; - } - - v = find_var(name, NULL, NULL); - - if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT))) - return NULL; - if (v->rfunc && (v->flags & VFUNCREF) != 0) - return (*v->rfunc)(v) + v->name_len + 1; - return v->text + v->name_len + 1; -} - - - -/* - * Generate a list of exported variables. This routine is used to construct - * the third argument to execve when executing a program. - */ - -char ** -environment(void) -{ - int nenv; - struct var **vpp; - struct var *vp; - char **env; - char **ep; - - nenv = 0; - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) - if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT) - nenv++; - } - CTRACE(DBG_VARS, ("environment: %d vars to export\n", nenv)); - ep = env = stalloc((nenv + 1) * sizeof *env); - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) - if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT) { - if (vp->rfunc && (vp->flags & VFUNCREF)) - *ep++ = (*vp->rfunc)(vp); - else - *ep++ = vp->text; - VTRACE(DBG_VARS, ("environment: %s\n", ep[-1])); - } - } - *ep = NULL; - return env; -} - - -/* - * Called when a shell procedure is invoked to clear out nonexported - * variables. It is also necessary to reallocate variables of with - * VSTACK set since these are currently allocated on the stack. - */ - -#ifdef mkinit -void shprocvar(void); - -SHELLPROC { - shprocvar(); -} -#endif - -void -shprocvar(void) -{ - struct var **vpp; - struct var *vp, **prev; - - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (prev = vpp ; (vp = *prev) != NULL ; ) { - if ((vp->flags & VEXPORT) == 0) { - *prev = vp->next; - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - if ((vp->flags & VSTRFIXED) == 0) - ckfree(vp); - } else { - if (vp->flags & VSTACK) { - vp->text = savestr(vp->text); - vp->flags &=~ VSTACK; - } - prev = &vp->next; - } - } - } - initvar(); -} - - - -/* - * Command to list all variables which are set. Currently this command - * is invoked from the set command when the set command is called without - * any variables. - */ - -void -print_quoted(const char *p) -{ - const char *q; - - if (p[0] == '\0') { - out1fmt("''"); - return; - } - if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) { - out1fmt("%s", p); - return; - } - while (*p) { - if (*p == '\'') { - out1fmt("\\'"); - p++; - continue; - } - q = strchr(p, '\''); - if (!q) { - out1fmt("'%s'", p ); - return; - } - out1fmt("'%.*s'", (int)(q - p), p ); - p = q; - } -} - -static int -sort_var(const void *v_v1, const void *v_v2) -{ - const struct var * const *v1 = v_v1; - const struct var * const *v2 = v_v2; - char *t1 = (*v1)->text, *t2 = (*v2)->text; - - if (*t1 == *t2) { - char *p, *s; - - STARTSTACKSTR(p); - - /* - * note: if lengths are equal, strings must be different - * so we don't care which string we pick for the \0 in - * that case. - */ - if ((strchr(t1, '=') - t1) <= (strchr(t2, '=') - t2)) { - s = t1; - t1 = p; - } else { - s = t2; - t2 = p; - } - - while (*s && *s != '=') { - STPUTC(*s, p); - s++; - } - STPUTC('\0', p); - } - - return strcoll(t1, t2); -} - -/* - * POSIX requires that 'set' (but not export or readonly) output the - * variables in lexicographic order - by the locale's collating order (sigh). - * Maybe we could keep them in an ordered balanced binary tree - * instead of hashed lists. - * For now just roll 'em through qsort for printing... - */ - -STATIC void -showvar(struct var *vp, const char *cmd, const char *xtra, int show_value) -{ - const char *p; - - if (cmd) - out1fmt("%s ", cmd); - if (xtra) - out1fmt("%s ", xtra); - p = vp->text; - if (vp->rfunc && (vp->flags & VFUNCREF) != 0) { - p = (*vp->rfunc)(vp); - if (p == NULL) - p = vp->text; - } - for ( ; *p != '=' ; p++) - out1c(*p); - if (!(vp->flags & VUNSET) && show_value) { - out1fmt("="); - print_quoted(++p); - } - out1c('\n'); -} - -int -showvars(const char *cmd, int flag, int show_value, const char *xtra) -{ - struct var **vpp; - struct var *vp; - - static struct var **list; /* static in case we are interrupted */ - static int list_len; - int count = 0; - - if (!list) { - list_len = 32; - list = ckmalloc(list_len * sizeof *list); - } - - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) { - if (flag && !(vp->flags & flag)) - continue; - if (vp->flags & VUNSET && !(show_value & 2)) - continue; - if (count >= list_len) { - list = ckrealloc(list, - (list_len << 1) * sizeof *list); - list_len <<= 1; - } - list[count++] = vp; - } - } - - qsort(list, count, sizeof *list, sort_var); - - for (vpp = list; count--; vpp++) - showvar(*vpp, cmd, xtra, show_value); - - /* no free(list), will be used again next time ... */ - - return 0; -} - - - -/* - * The export and readonly commands. - */ - -static void __dead -export_usage(const char *cmd) -{ -#ifdef SMALL - if (*cmd == 'r') - error("Usage: %s [ -p | var[=val]... ]", cmd); - else - error("Usage: %s [ -p | [-n] var[=val]... ]", cmd); -#else - if (*cmd == 'r') - error("Usage: %s [-p [var...] | -q var... | var[=val]... ]", cmd); - else - error( - "Usage: %s [ -px [var...] | -q[x] var... | [-n|x] var[=val]... ]", - cmd); -#endif -} - -int -exportcmd(int argc, char **argv) -{ - struct var *vp; - char *name; - const char *p = argv[0]; - int flag = p[0] == 'r'? VREADONLY : VEXPORT; - int pflg = 0; - int nflg = 0; -#ifndef SMALL - int xflg = 0; - int qflg = 0; -#endif - int res; - int c; - int f; - -#ifdef SMALL -#define EXPORT_OPTS "np" -#else -#define EXPORT_OPTS "npqx" -#endif - - while ((c = nextopt(EXPORT_OPTS)) != '\0') { - -#undef EXPORT_OPTS - - switch (c) { - case 'n': - if (pflg || flag == VREADONLY -#ifndef SMALL - || qflg || xflg -#endif - ) - export_usage(p); - nflg = 1; - break; - case 'p': - if (nflg -#ifndef SMALL - || qflg -#endif - ) - export_usage(p); - pflg = 3; - break; -#ifndef SMALL - case 'q': - if (nflg || pflg) - export_usage(p); - qflg = 1; - break; - case 'x': - if (nflg || flag == VREADONLY) - export_usage(p); - flag = VNOEXPORT; - xflg = 1; - break; -#endif - } - } - - if ((nflg -#ifndef SMALL - || qflg -#endif - ) && *argptr == NULL) - export_usage(p); - -#ifndef SMALL - if (pflg && *argptr != NULL) { - while ((name = *argptr++) != NULL) { - int len; - - vp = find_var(name, NULL, &len); - if (name[len] == '=') - export_usage(p); - if (!goodname(name)) - error("%s: bad variable name", name); - - if (vp && vp->flags & flag) - showvar(vp, p, xflg ? "-x" : NULL, 1); - } - return 0; - } -#endif - - if (pflg || *argptr == NULL) - return showvars( pflg ? p : 0, flag, pflg, -#ifndef SMALL - pflg && xflg ? "-x" : -#endif - NULL ); - - res = 0; -#ifndef SMALL - if (qflg) { - while ((name = *argptr++) != NULL) { - int len; - - vp = find_var(name, NULL, &len); - if (name[len] == '=') - export_usage(p); - if (!goodname(name)) - error("%s: bad variable name", name); - - if (vp == NULL || !(vp->flags & flag)) - res = 1; - } - return res; - } -#endif - - while ((name = *argptr++) != NULL) { - int len; - - f = flag; - - vp = find_var(name, NULL, &len); - p = name + len; - if (*p++ != '=') - p = NULL; - - if (vp != NULL) { - if (nflg) - vp->flags &= ~flag; - else if (flag&VEXPORT && vp->flags&VNOEXPORT) { - /* note we go ahead and do any assignment */ - sh_warnx("%.*s: not available for export", - len, name); - res = 1; - } else { - if (flag == VNOEXPORT) - vp->flags &= ~VEXPORT; - - /* if not NULL will be done in setvar below */ - if (p == NULL) - vp->flags |= flag; - } - if (p == NULL) - continue; - } else if (nflg && p == NULL && !goodname(name)) - error("%s: bad variable name", name); - - if (!nflg || p != NULL) - setvar(name, p, f); - } - return res; -} - - -/* - * The "local" command. - */ - -int -localcmd(int argc, char **argv) -{ - char *name; - int c; - int flags = 0; /*XXX perhaps VUNSET from a -o option value */ - - if (! in_function()) - error("Not in a function"); - - /* upper case options, as bash stole all the good ones ... */ - while ((c = nextopt("INx")) != '\0') - switch (c) { - case 'I': flags &= ~VUNSET; break; - case 'N': flags |= VUNSET; break; - case 'x': flags |= VEXPORT; break; - } - - while ((name = *argptr++) != NULL) { - mklocal(name, flags); - } - return 0; -} - - -/* - * Make a variable a local variable. When a variable is made local, its - * value and flags are saved in a localvar structure. The saved values - * will be restored when the shell function returns. We handle the name - * "-" as a special case. - */ - -void -mklocal(const char *name, int flags) -{ - struct localvar *lvp; - struct var **vpp; - struct var *vp; - - INTOFF; - lvp = ckmalloc(sizeof (struct localvar)); - if (name[0] == '-' && name[1] == '\0') { - char *p; - p = ckmalloc(sizeof_optlist); - lvp->text = memcpy(p, optlist, sizeof_optlist); - lvp->rfunc = NULL; - vp = NULL; - xtrace_clone(0); - } else { - vp = find_var(name, &vpp, NULL); - if (vp == NULL) { - flags &= ~VNOEXPORT; - if (strchr(name, '=')) - setvareq(savestr(name), - VSTRFIXED | (flags & ~VUNSET)); - else - setvar(name, NULL, VSTRFIXED|flags); - vp = *vpp; /* the new variable */ - lvp->text = NULL; - lvp->flags = VUNSET; - lvp->rfunc = NULL; - } else { - lvp->text = vp->text; - lvp->flags = vp->flags; - lvp->v_u = vp->v_u; - vp->flags |= VSTRFIXED|VTEXTFIXED; - if (flags & (VDOEXPORT | VUNSET)) - vp->flags &= ~VNOEXPORT; - if (vp->flags & VNOEXPORT && - (flags & (VEXPORT|VDOEXPORT|VUNSET)) == VEXPORT) - flags &= ~VEXPORT; - if (flags & (VNOEXPORT | VUNSET)) - vp->flags &= ~VEXPORT; - flags &= ~VNOEXPORT; - if (name[vp->name_len] == '=') - setvareq(savestr(name), flags & ~VUNSET); - else if (flags & VUNSET) - unsetvar(name, 0); - else - vp->flags |= flags & (VUNSET|VEXPORT); - - if (vp == &line_num) { - if (name[vp->name_len] == '=') - funclinebase = funclineabs -1; - else - funclinebase = 0; - } - } - } - lvp->vp = vp; - lvp->next = localvars; - localvars = lvp; - INTON; -} - - -/* - * Called after a function returns. - */ - -void -poplocalvars(void) -{ - struct localvar *lvp; - struct var *vp; - - while ((lvp = localvars) != NULL) { - localvars = lvp->next; - vp = lvp->vp; - VTRACE(DBG_VARS, ("poplocalvar %s\n", vp ? vp->text : "-")); - if (vp == NULL) { /* $- saved */ - memcpy(optlist, lvp->text, sizeof_optlist); - ckfree(lvp->text); - xtrace_pop(); - optschanged(); - } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { - (void)unsetvar(vp->text, 0); - } else { - if (lvp->func && (lvp->flags & (VNOFUNC|VFUNCREF)) == 0) - (*lvp->func)(lvp->text + vp->name_len + 1); - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - vp->flags = lvp->flags; - vp->text = lvp->text; - vp->v_u = lvp->v_u; - } - ckfree(lvp); - } -} - - -int -setvarcmd(int argc, char **argv) -{ - if (argc <= 2) - return unsetcmd(argc, argv); - else if (argc == 3) - setvar(argv[1], argv[2], 0); - else - error("List assignment not implemented"); - return 0; -} - - -/* - * The unset builtin command. We unset the function before we unset the - * variable to allow a function to be unset when there is a readonly variable - * with the same name. - */ - -int -unsetcmd(int argc, char **argv) -{ - char **ap; - int i; - int flg_func = 0; - int flg_var = 0; - int flg_x = 0; - int ret = 0; - - while ((i = nextopt("efvx")) != '\0') { - switch (i) { - case 'f': - flg_func = 1; - break; - case 'e': - case 'x': - flg_x = (2 >> (i == 'e')); - /* FALLTHROUGH */ - case 'v': - flg_var = 1; - break; - } - } - - if (flg_func == 0 && flg_var == 0) - flg_var = 1; - - for (ap = argptr; *ap ; ap++) { - if (flg_func) - ret |= unsetfunc(*ap); - if (flg_var) - ret |= unsetvar(*ap, flg_x); - } - return ret; -} - - -/* - * Unset the specified variable. - */ - -int -unsetvar(const char *s, int unexport) -{ - struct var **vpp; - struct var *vp; - - vp = find_var(s, &vpp, NULL); - if (vp == NULL) - return 0; - - if (vp->flags & VREADONLY && !(unexport & 1)) - return 1; - - INTOFF; - if (unexport & 1) { - vp->flags &= ~VEXPORT; - } else { - if (vp->text[vp->name_len + 1] != '\0') - setvar(s, nullstr, 0); - if (!(unexport & 2)) - vp->flags &= ~VEXPORT; - vp->flags |= VUNSET; - if ((vp->flags&(VEXPORT|VSTRFIXED|VREADONLY|VNOEXPORT)) == 0) { - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - *vpp = vp->next; - ckfree(vp); - } - } - INTON; - return 0; -} - - -/* - * Returns true if the two strings specify the same varable. The first - * variable name is terminated by '='; the second may be terminated by - * either '=' or '\0'. - */ - -STATIC int -strequal(const char *p, const char *q) -{ - while (*p == *q++) { - if (*p++ == '=') - return 1; - } - if (*p == '=' && *(q - 1) == '\0') - return 1; - return 0; -} - -/* - * Search for a variable. - * 'name' may be terminated by '=' or a NUL. - * vppp is set to the pointer to vp, or the list head if vp isn't found - * lenp is set to the number of characters in 'name' - */ - -STATIC struct var * -find_var(const char *name, struct var ***vppp, int *lenp) -{ - unsigned int hashval; - int len; - struct var *vp, **vpp; - const char *p = name; - - hashval = 0; - while (*p && *p != '=') - hashval = 2 * hashval + (unsigned char)*p++; - - len = p - name; - if (lenp) - *lenp = len; - - vpp = &vartab[hashval % VTABSIZE]; - if (vppp) - *vppp = vpp; - - for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { - if (vp->name_len != len) - continue; - if (memcmp(vp->text, name, len) != 0) - continue; - if (vppp) - *vppp = vpp; - return vp; - } - return NULL; -} - -/* - * The following are the functions that create the values for - * shell variables that are dynamically produced when needed. - * - * The output strings cannot be malloc'd as there is nothing to - * free them - callers assume these are ordinary variables where - * the value returned is vp->text - * - * Each function needs its own storage space, as the results are - * used to create processes' environment, and (if exported) all - * the values will (might) be needed simultaneously. - * - * It is not a problem if a var is updated while nominally in use - * somewhere, all these are intended to be dynamic, the value they - * return is not guaranteed, an updated vaue is just as good. - * - * So, malloc a single buffer for the result of each function, - * grow, and even shrink, it as needed, but once we have one that - * is a suitable size for the actual usage, simply hold it forever. - * - * For a SMALL shell we implement only LINENO, none of the others, - * and give it just a fixed length static buffer for its result. - */ - -#ifndef SMALL - -struct space_reserved { /* record of space allocated for results */ - char *b; - int len; -}; - -/* rough (over-)estimate of the number of bytes needed to hold a number */ -static int -digits_in(intmax_t number) -{ - int res = 0; - - if (number & ~((1LL << 62) - 1)) - res = 64; /* enough for 2^200 and a bit more */ - else if (number & ~((1LL << 32) - 1)) - res = 20; /* enough for 2^64 */ - else if (number & ~((1 << 23) - 1)) - res = 10; /* enough for 2^32 */ - else - res = 8; /* enough for 2^23 or smaller */ - - return res; -} - -static int -make_space(struct space_reserved *m, int bytes) -{ - void *p; - - if (m->len >= bytes && m->len <= (bytes<<2)) - return 1; - - bytes = SHELL_ALIGN(bytes); - /* not ckrealloc() - we want failure, not error() here */ - p = realloc(m->b, bytes); - if (p == NULL) /* what we had should still be there */ - return 0; - - m->b = p; - m->len = bytes; - m->b[bytes - 1] = '\0'; - - return 1; -} -#endif - -char * -get_lineno(struct var *vp) -{ -#ifdef SMALL -#define length (8 + 10) /* 10 digits is enough for a 32 bit line num */ - static char result[length]; -#else - static struct space_reserved buf; -#define result buf.b -#define length buf.len -#endif - int ln = line_number; - - if (vp->flags & VUNSET) - return NULL; - - ln -= funclinebase; - -#ifndef SMALL - if (!make_space(&buf, vp->name_len + 2 + digits_in(ln))) - return vp->text; -#endif - - snprintf(result, length, "%.*s=%d", vp->name_len, vp->text, ln); - return result; -} -#undef result -#undef length - -#ifndef SMALL - -char * -get_hostname(struct var *vp) -{ - static struct space_reserved buf; - - if (vp->flags & VUNSET) - return NULL; - - if (!make_space(&buf, vp->name_len + 2 + 256)) - return vp->text; - - memcpy(buf.b, vp->text, vp->name_len + 1); /* include '=' */ - (void)gethostname(buf.b + vp->name_len + 1, - buf.len - vp->name_len - 3); - return buf.b; -} - -char * -get_tod(struct var *vp) -{ - static struct space_reserved buf; /* space for answers */ - static struct space_reserved tzs; /* remember TZ last used */ - static timezone_t last_zone; /* timezone data for tzs zone */ - const char *fmt; - char *tz; - time_t now; - struct tm tm_now, *tmp; - timezone_t zone = NULL; - static char t_err[] = "time error"; - int len; - - if (vp->flags & VUNSET) - return NULL; - - fmt = lookupvar("ToD_FORMAT"); - if (fmt == NULL) - fmt="%T"; - tz = lookupvar("TZ"); - (void)time(&now); - - if (tz != NULL) { - if (tzs.b == NULL || strcmp(tzs.b, tz) != 0) { - if (make_space(&tzs, strlen(tz) + 1)) { - INTOFF; - strcpy(tzs.b, tz); - if (last_zone) - tzfree(last_zone); - last_zone = zone = tzalloc(tz); - INTON; - } else - zone = tzalloc(tz); - } else - zone = last_zone; - - tmp = localtime_rz(zone, &now, &tm_now); - } else - tmp = localtime_r(&now, &tm_now); - - len = (strlen(fmt) * 4) + vp->name_len + 2; - while (make_space(&buf, len)) { - memcpy(buf.b, vp->text, vp->name_len+1); - if (tmp == NULL) { - if (buf.len >= vp->name_len+2+(int)(sizeof t_err - 1)) { - strcpy(buf.b + vp->name_len + 1, t_err); - if (zone && zone != last_zone) - tzfree(zone); - return buf.b; - } - len = vp->name_len + 4 + sizeof t_err - 1; - continue; - } - if (strftime_z(zone, buf.b + vp->name_len + 1, - buf.len - vp->name_len - 2, fmt, tmp)) { - if (zone && zone != last_zone) - tzfree(zone); - return buf.b; - } - if (len >= 4096) /* Let's be reasonable */ - break; - len <<= 1; - } - if (zone && zone != last_zone) - tzfree(zone); - return vp->text; -} - -char * -get_seconds(struct var *vp) -{ - static struct space_reserved buf; - intmax_t secs; - - if (vp->flags & VUNSET) - return NULL; - - secs = (intmax_t)time((time_t *)0) - sh_start_time; - if (!make_space(&buf, vp->name_len + 2 + digits_in(secs))) - return vp->text; - - snprintf(buf.b, buf.len, "%.*s=%jd", vp->name_len, vp->text, secs); - return buf.b; -} - -char * -get_euser(struct var *vp) -{ - static struct space_reserved buf; - static uid_t lastuid = 0; - uid_t euid; - struct passwd *pw; - - if (vp->flags & VUNSET) - return NULL; - - euid = geteuid(); - if (buf.b != NULL && lastuid == euid) - return buf.b; - - pw = getpwuid(euid); - if (pw == NULL) - return vp->text; - - if (make_space(&buf, vp->name_len + 2 + strlen(pw->pw_name))) { - lastuid = euid; - snprintf(buf.b, buf.len, "%.*s=%s", vp->name_len, vp->text, - pw->pw_name); - return buf.b; - } - - return vp->text; -} - -char * -get_random(struct var *vp) -{ - static struct space_reserved buf; - static intmax_t random_val = 0; - -#ifdef USE_LRAND48 -#define random lrand48 -#define srandom srand48 -#endif - - if (vp->flags & VUNSET) - return NULL; - - if (vp->text != buf.b) { - /* - * Either initialisation, or a new seed has been set - */ - if (vp->text[vp->name_len + 1] == '\0') { - int fd; - - /* - * initialisation (without pre-seeding), - * or explictly requesting a truly random seed. - */ - fd = open("/dev/urandom", 0); - if (fd == -1) { - out2str("RANDOM initialisation failed\n"); - random_val = (getpid()<<3) ^ time((time_t *)0); - } else { - int n; - - do { - n = read(fd,&random_val,sizeof random_val); - } while (n != sizeof random_val); - close(fd); - } - } else - /* good enough for today */ - random_val = strtoimax(vp->text+vp->name_len+1,NULL,0); - - srandom((long)random_val); - } - -#if 0 - random_val = (random_val + 1) & 0x7FFF; /* 15 bit "random" numbers */ -#else - random_val = (random() >> 5) & 0x7FFF; -#endif - - if (!make_space(&buf, vp->name_len + 2 + digits_in(random_val))) - return vp->text; - - snprintf(buf.b, buf.len, "%.*s=%jd", vp->name_len, vp->text, - random_val); - - if (buf.b != vp->text && (vp->flags & (VTEXTFIXED|VSTACK)) == 0) - free(vp->text); - vp->flags |= VTEXTFIXED; - vp->text = buf.b; - - return vp->text; -#undef random -#undef srandom -} - -#endif /* SMALL */ diff --git a/bin/sh/var.h b/bin/sh/var.h deleted file mode 100644 index 43bef4b..0000000 --- a/bin/sh/var.h +++ /dev/null @@ -1,151 +0,0 @@ -/* $NetBSD: var.h,v 1.38 2018/12/04 14:03:30 kre Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)var.h 8.2 (Berkeley) 5/4/95 - */ - -#ifndef VUNSET /* double include protection */ -/* - * Shell variables. - */ - -/* flags */ -#define VUNSET 0x0001 /* the variable is not set */ -#define VEXPORT 0x0002 /* variable is exported */ -#define VREADONLY 0x0004 /* variable cannot be modified */ -#define VNOEXPORT 0x0008 /* variable may not be exported */ - -#define VSTRFIXED 0x0010 /* variable struct is statically allocated */ -#define VTEXTFIXED 0x0020 /* text is statically allocated */ -#define VSTACK 0x0040 /* text is allocated on the stack */ -#define VNOFUNC 0x0100 /* don't call the callback function */ -#define VFUNCREF 0x0200 /* the function is called on ref, not set */ - -#define VSPECIAL 0x1000 /* magic properties not lost when set */ -#define VDOEXPORT 0x2000 /* obey VEXPORT even if VNOEXPORT */ -#define VNOSET 0x4000 /* do not set variable - just readonly test */ -#define VNOERROR 0x8000 /* be quiet if set fails (no error msg) */ - -struct var; - -union var_func_union { /* function to be called when: */ - void (*set_func)(const char *); /* variable gets set/unset */ - char*(*ref_func)(struct var *); /* variable is referenced */ -}; - -struct var { - struct var *next; /* next entry in hash list */ - int flags; /* flags are defined above */ - char *text; /* name=value */ - int name_len; /* length of name */ - union var_func_union v_u; /* function to apply (sometimes) */ -}; - - -struct localvar { - struct localvar *next; /* next local variable in list */ - struct var *vp; /* the variable that was made local */ - int flags; /* saved flags */ - char *text; /* saved text */ - union var_func_union v_u; /* saved function */ -}; - - -extern struct localvar *localvars; - -extern struct var vifs; -extern char ifs_default[]; -extern struct var vmail; -extern struct var vmpath; -extern struct var vpath; -extern struct var vps1; -extern struct var vps2; -extern struct var vps4; -extern struct var line_num; -#ifndef SMALL -extern struct var editrc; -extern struct var vterm; -extern struct var vtermcap; -extern struct var vhistsize; -extern struct var ps_lit; -extern struct var euname; -extern struct var random_num; -extern intmax_t sh_start_time; -#endif - -extern int line_number; -extern int funclinebase; -extern int funclineabs; - -/* - * The following macros access the values of the above variables. - * They have to skip over the name. They return the null string - * for unset variables. - */ - -#define ifsset() ((vifs.flags & VUNSET) == 0) -#define ifsval() (ifsset() ? (vifs.text + 4) : ifs_default) -#define mailval() (vmail.text + 5) -#define mpathval() (vmpath.text + 9) -#define pathval() (vpath.text + 5) -#define ps1val() (vps1.text + 4) -#define ps2val() (vps2.text + 4) -#define ps4val() (vps4.text + 4) -#define optindval() (voptind.text + 7) -#ifndef SMALL -#define histsizeval() (vhistsize.text + 9) -#define termval() (vterm.text + 5) -#endif - -#define mpathset() ((vmpath.flags & VUNSET) == 0) - -void initvar(void); -void setvar(const char *, const char *, int); -void setvareq(char *, int); -struct strlist; -void listsetvar(struct strlist *, int); -char *lookupvar(const char *); -char *bltinlookup(const char *, int); -char **environment(void); -void shprocvar(void); -int showvars(const char *, int, int, const char *); -void mklocal(const char *, int); -void listmklocal(struct strlist *, int); -void poplocalvars(void); -int unsetvar(const char *, int); -void choose_ps1(void); -int setvarsafe(const char *, const char *, int); -void print_quoted(const char *); -int validname(const char *, int, int *); - -#endif diff --git a/bin/sh/version.h b/bin/sh/version.h deleted file mode 100644 index 59069e4..0000000 --- a/bin/sh/version.h +++ /dev/null @@ -1,36 +0,0 @@ -/* $NetBSD: version.h,v 1.3 2018/12/12 12:16:42 kre Exp $ */ - -/*- - * Copyright (c) 2014 The NetBSD Foundation, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This value should be modified rarely - only when significant changes - * to the shell (which does not mean a bug fixed, or some new feature added) - * have been made. The most likely reason to change this value would be - * when a new (shell only) release is to be exported. This should not be - * updated just because a new NetBSD release is to include this code. - */ -#define NETBSD_SHELL "20181212" |