rsalz@uunet.uu.net (Rich Salz) (06/06/90)
Submitted-by: "Arnold D. Robbins" <arnold@unix.cc.emory.edu> Posting-number: Volume 22, Issue 89 Archive-name: gawk2.11/part03 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # Contents: ./gawk.texinfo.04 ./missing.d/strtod.c # Wrapped by rsalz@litchi.bbn.com on Wed Jun 6 12:24:47 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH echo If this archive is complete, you will see the following message: echo ' "shar: End of archive 3 (of 16)."' if test -f './gawk.texinfo.04' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'./gawk.texinfo.04'\" else echo shar: Extracting \"'./gawk.texinfo.04'\" \(49666 characters\) sed "s/^X//" >'./gawk.texinfo.04' <<'END_OF_FILE' Xoften necessary to insure that it happens where you want it to by Xenclosing the items to be concatenated in parentheses. For example, the Xfollowing code fragment does not concatenate @code{file} and @code{name} Xas you might expect: X X@example Xfile = "file" Xname = "name" Xprint "something meaningful" > file name X@end example X X@noindent XIt is necessary to use the following: X X@example Xprint "something meaningful" > (file name) X@end example X XWe recommend you use parentheses around concatenation in all but the Xmost common contexts (such as in the right-hand operand of @samp{=}). X X@ignore X@code{gawk} actually now allows a concatenation on the right hand Xside of a @code{>} redirection, but other @code{awk}s don't. So for Xnow we won't mention that fact. X@end ignore X X@node Comparison Ops, Boolean Ops, Concatenation, Expressions X@section Comparison Expressions X@cindex comparison expressions X@cindex expressions, comparison X@cindex relational operators X@cindex operators, relational X@cindex regexp operators X X@dfn{Comparison expressions} compare strings or numbers for Xrelationships such as equality. They are written using @dfn{relational Xoperators}, which are a superset of those in C. Here is a table of Xthem: X X@table @code X@item @var{x} < @var{y} XTrue if @var{x} is less than @var{y}. X X@item @var{x} <= @var{y} XTrue if @var{x} is less than or equal to @var{y}. X X@item @var{x} > @var{y} XTrue if @var{x} is greater than @var{y}. X X@item @var{x} >= @var{y} XTrue if @var{x} is greater than or equal to @var{y}. X X@item @var{x} == @var{y} XTrue if @var{x} is equal to @var{y}. X X@item @var{x} != @var{y} XTrue if @var{x} is not equal to @var{y}. X X@item @var{x} ~ @var{y} XTrue if the string @var{x} matches the regexp denoted by @var{y}. X X@item @var{x} !~ @var{y} XTrue if the string @var{x} does not match the regexp denoted by @var{y}. X X@item @var{subscript} in @var{array} XTrue if array @var{array} has an element with the subscript @var{subscript}. X@end table X XComparison expressions have the value 1 if true and 0 if false. X XThe operands of a relational operator are compared as numbers if they Xare both numbers. Otherwise they are converted to, and compared as, Xstrings (@pxref{Conversion}). Strings are compared by comparing the Xfirst character of each, then the second character of each, and so on. XThus, @code{"10"} is less than @code{"9"}. X XFor example, X X@example X$1 == "foo" X@end example X X@noindent Xhas the value of 1, or is true, if the first field of the current input Xrecord is precisely @samp{foo}. By contrast, X X@example X$1 ~ /foo/ X@end example X X@noindent Xhas the value 1 if the first field contains @samp{foo}. X XThe right hand operand of the @samp{~} and @samp{!~} operators may be Xeither a constant regexp (@code{/@dots{}/}), or it may be an ordinary Xexpression, in which case the value of the expression as a string is a Xdynamic regexp (@pxref{Regexp Usage}). X X@cindex regexp as expression XIn very recent implementations of @code{awk}, a constant regular Xexpression in slashes by itself is also an expression. The regexp X@code{/@var{regexp}/} is an abbreviation for this comparison expression: X X@example X$0 ~ /@var{regexp}/ X@end example X XIn some contexts it may be necessary to write parentheses around the Xregexp to avoid confusing the @code{gawk} parser. For example, X@code{(/x/ - /y/) > threshold} is not allowed, but @code{((/x/) - (/y/)) X> threshold} parses properly. X XOne special place where @code{/foo/} is @emph{not} an abbreviation for X@code{$0 ~ /foo/} is when it is the right-hand operand of @samp{~} or X@samp{!~}! X X@node Boolean Ops, Assignment Ops, Comparison Ops, Expressions X@section Boolean Expressions X@cindex expressions, boolean X@cindex boolean expressions X@cindex operators, boolean X@cindex boolean operators X@cindex logical operations X@cindex and operator X@cindex or operator X@cindex not operator X XA @dfn{boolean expression} is combination of comparison expressions or Xmatching expressions, using the @dfn{boolean operators} ``or'' X(@samp{||}), ``and'' (@samp{&&}), and ``not'' (@samp{!}), along with Xparentheses to control nesting. The truth of the boolean expression is Xcomputed by combining the truth values of the component expressions. X XBoolean expressions can be used wherever comparison and matching Xexpressions can be used. They can be used in @code{if} and @code{while} Xstatements. They have numeric values (1 if true, 0 if false), which Xcome into place if the result of the boolean expression is stored in a Xvariable, or used in arithmetic. X XIn addition, every boolean expression is also a valid boolean pattern, so Xyou can use it as a pattern to control the execution of rules. X XHere are descriptions of the three boolean operators, with an example of Xeach. It may be instructive to compare these examples with the Xanalogous examples of boolean patterns (@pxref{Boolean Patterns}), which Xuse the same boolean operators in patterns instead of expressions. X X@table @code X@item @var{boolean1} && @var{boolean2} XTrue if both @var{boolean1} and @var{boolean2} are true. For example, Xthe following statement prints the current input record if it contains Xboth @samp{2400} and @samp{foo}.@refill X X@example Xif ($0 ~ /2400/ && $0 ~ /foo/) print X@end example X XThe subexpression @var{boolean2} is evaluated only if @var{boolean1} Xis true. This can make a difference when @var{boolean2} contains Xexpressions that have side effects: in the case of @code{$0 ~ /foo/ && X($2 == bar++)}, the variable @code{bar} is not incremented if there is Xno @samp{foo} in the record. X X@item @var{boolean1} || @var{boolean2} XTrue if at least one of @var{boolean1} and @var{boolean2} is true. XFor example, the following command prints all records in the input Xfile @file{BBS-list} that contain @emph{either} @samp{2400} or X@samp{foo}, or both.@refill X X@example Xawk '@{ if ($0 ~ /2400/ || $0 ~ /foo/) print @}' BBS-list X@end example X XThe subexpression @var{boolean2} is evaluated only if @var{boolean1} Xis false. This can make a difference when @var{boolean2} contains Xexpressions that have side effects. X X@item !@var{boolean} XTrue if @var{boolean} is false. For example, the following program prints Xall records in the input file @file{BBS-list} that do @emph{not} contain the Xstring @samp{foo}. X X@example Xawk '@{ if (! ($0 ~ /foo/)) print @}' BBS-list X@end example X@end table X X@node Assignment Ops, Increment Ops, Boolean Ops, Expressions X@section Assignment Expressions X@cindex assignment operators X@cindex operators, assignment X@cindex expressions, assignment X XAn @dfn{assignment} is an expression that stores a new value into a Xvariable. For example, let's assign the value 1 to the variable X@code{z}:@refill X X@example Xz = 1 X@end example X XAfter this expression is executed, the variable @code{z} has the value 1. XWhatever old value @code{z} had before the assignment is forgotten. X XAssignments can store string values also. For example, this would store Xthe value @code{"this food is good"} in the variable @code{message}: X X@example Xthing = "food" Xpredicate = "good" Xmessage = "this " thing " is " predicate X@end example X X@noindent X(This also illustrates concatenation of strings.) X XThe @samp{=} sign is called an @dfn{assignment operator}. It is the Xsimplest assignment operator because the value of the right-hand Xoperand is stored unchanged. X X@cindex side effect XMost operators (addition, concatenation, and so on) have no effect Xexcept to compute a value. If you ignore the value, you might as well Xnot use the operator. An assignment operator is different; it does Xproduce a value, but even if you ignore the value, the assignment still Xmakes itself felt through the alteration of the variable. We call this Xa @dfn{side effect}. X X@cindex lvalue XThe left-hand operand of an assignment need not be a variable X(@pxref{Variables}); it can also be a field (@pxref{Changing Fields}) or Xan array element (@pxref{Arrays}). These are all called @dfn{lvalues}, Xwhich means they can appear on the left-hand side of an assignment operator. XThe right-hand operand may be any expression; it produces the new value Xwhich the assignment stores in the specified variable, field or array Xelement. X XIt is important to note that variables do @emph{not} have permanent types. XThe type of a variable is simply the type of whatever value it happens Xto hold at the moment. In the following program fragment, the variable X@code{foo} has a numeric value at first, and a string value later on: X X@example Xfoo = 1 Xprint foo Xfoo = "bar" Xprint foo X@end example X X@noindent XWhen the second assignment gives @code{foo} a string value, the fact that Xit previously had a numeric value is forgotten. X XAn assignment is an expression, so it has a value: the same value that Xis assigned. Thus, @code{z = 1} as an expression has the value 1. XOne consequence of this is that you can write multiple assignments together: X X@example Xx = y = z = 0 X@end example X X@noindent Xstores the value 0 in all three variables. It does this because the Xvalue of @code{z = 0}, which is 0, is stored into @code{y}, and then Xthe value of @code{y = z = 0}, which is 0, is stored into @code{x}. X XYou can use an assignment anywhere an expression is called for. For Xexample, it is valid to write @code{x != (y = 1)} to set @code{y} to 1 Xand then test whether @code{x} equals 1. But this style tends to make Xprograms hard to read; except in a one-shot program, you should Xrewrite it to get rid of such nesting of assignments. This is never very Xhard. X XAside from @samp{=}, there are several other assignment operators that Xdo arithmetic with the old value of the variable. For example, the Xoperator @samp{+=} computes a new value by adding the right-hand value Xto the old value of the variable. Thus, the following assignment adds X5 to the value of @code{foo}: X X@example Xfoo += 5 X@end example X X@noindent XThis is precisely equivalent to the following: X X@example Xfoo = foo + 5 X@end example X X@noindent XUse whichever one makes the meaning of your program clearer. X XHere is a table of the arithmetic assignment operators. In each Xcase, the right-hand operand is an expression whose value is converted Xto a number. X X@table @code X@item @var{lvalue} += @var{increment} XAdds @var{increment} to the value of @var{lvalue} to make the new value Xof @var{lvalue}. X X@item @var{lvalue} -= @var{decrement} XSubtracts @var{decrement} from the value of @var{lvalue}. X X@item @var{lvalue} *= @var{coefficient} XMultiplies the value of @var{lvalue} by @var{coefficient}. X X@item @var{lvalue} /= @var{quotient} XDivides the value of @var{lvalue} by @var{quotient}. X X@item @var{lvalue} %= @var{modulus} XSets @var{lvalue} to its remainder by @var{modulus}. X X@item @var{lvalue} ^= @var{power} X@itemx @var{lvalue} **= @var{power} XRaises @var{lvalue} to the power @var{power}. X@end table X X@node Increment Ops, Conversion, Assignment Ops, Expressions X@section Increment Operators X X@cindex increment operators X@cindex operators, increment X@dfn{Increment operators} increase or decrease the value of a variable Xby 1. You could do the same thing with an assignment operator, so Xthe increment operators add no power to the @code{awk} language; but they Xare convenient abbreviations for something very common. X XThe operator to add 1 is written @samp{++}. It can be used to increment Xa variable either before or after taking its value. X XTo pre-increment a variable @var{v}, write @code{++@var{v}}. This adds X1 to the value of @var{v} and that new value is also the value of this Xexpression. The assignment expression @code{@var{v} += 1} is completely Xequivalent. X XWriting the @samp{++} after the variable specifies post-increment. This Xincrements the variable value just the same; the difference is that the Xvalue of the increment expression itself is the variable's @emph{old} Xvalue. Thus, if @code{foo} has value 4, then the expression @code{foo++} Xhas the value 4, but it changes the value of @code{foo} to 5. X XThe post-increment @code{foo++} is nearly equivalent to writing @code{(foo X+= 1) - 1}. It is not perfectly equivalent because all numbers in X@code{awk} are floating point: in floating point, @code{foo + 1 - 1} does Xnot necessarily equal @code{foo}. But the difference is minute as Xlong as you stick to numbers that are fairly small (less than a trillion). X XAny lvalue can be incremented. Fields and array elements are incremented Xjust like variables. X XThe decrement operator @samp{--} works just like @samp{++} except that Xit subtracts 1 instead of adding. Like @samp{++}, it can be used before Xthe lvalue to pre-decrement or after it to post-decrement. X XHere is a summary of increment and decrement expressions. X X@table @code X@item ++@var{lvalue} XThis expression increments @var{lvalue} and the new value becomes the Xvalue of this expression. X X@item @var{lvalue}++ XThis expression causes the contents of @var{lvalue} to be incremented. XThe value of the expression is the @emph{old} value of @var{lvalue}. X X@item --@var{lvalue} XLike @code{++@var{lvalue}}, but instead of adding, it subtracts. It Xdecrements @var{lvalue} and delivers the value that results. X X@item @var{lvalue}-- XLike @code{@var{lvalue}++}, but instead of adding, it subtracts. It Xdecrements @var{lvalue}. The value of the expression is the @emph{old} Xvalue of @var{lvalue}. X@end table X X@node Conversion, Conditional Exp, Increment Ops, Expressions X@section Conversion of Strings and Numbers X X@cindex conversion of strings and numbers XStrings are converted to numbers, and numbers to strings, if the context Xof the @code{awk} program demands it. For example, if the value of Xeither @code{foo} or @code{bar} in the expression @code{foo + bar} Xhappens to be a string, it is converted to a number before the addition Xis performed. If numeric values appear in string concatenation, they Xare converted to strings. Consider this:@refill X X@example Xtwo = 2; three = 3 Xprint (two three) + 4 X@end example X X@noindent XThis eventually prints the (numeric) value 27. The numeric values of Xthe variables @code{two} and @code{three} are converted to strings and Xconcatenated together, and the resulting string is converted back to the Xnumber 23, to which 4 is then added. X XIf, for some reason, you need to force a number to be converted to a Xstring, concatenate the null string with that number. To force a string Xto be converted to a number, add zero to that string. X XStrings are converted to numbers by interpreting them as numerals: X@code{"2.5"} converts to 2.5, and @code{"1e3"} converts to 1000. XStrings that can't be interpreted as valid numbers are converted to Xzero. X X@vindex OFMT XThe exact manner in which numbers are converted into strings is controlled Xby the @code{awk} built-in variable @code{OFMT} (@pxref{Built-in Variables}). XNumbers are converted using a special Xversion of the @code{sprintf} function (@pxref{Built-in}) with @code{OFMT} Xas the format specifier.@refill X X@code{OFMT}'s default value is @code{"%.6g"}, which prints a value with Xat least six significant digits. For some applications you will want to Xchange it to specify more precision. Double precision on most modern Xmachines gives you 16 or 17 decimal digits of precision. X XStrange results can happen if you set @code{OFMT} to a string that doesn't Xtell @code{sprintf} how to format floating point numbers in a useful way. XFor example, if you forget the @samp{%} in the format, all numbers will be Xconverted to the same constant string.@refill X X@node Conditional Exp, Function Calls, Conversion, Expressions X@section Conditional Expressions X@cindex conditional expression X@cindex expression, conditional X XA @dfn{conditional expression} is a special kind of expression with Xthree operands. It allows you to use one expression's value to select Xone of two other expressions. X XThe conditional expression looks the same as in the C language: X X@example X@var{selector} ? @var{if-true-exp} : @var{if-false-exp} X@end example X X@noindent XThere are three subexpressions. The first, @var{selector}, is always Xcomputed first. If it is ``true'' (not zero) then @var{if-true-exp} is Xcomputed next and its value becomes the value of the whole expression. XOtherwise, @var{if-false-exp} is computed next and its value becomes the Xvalue of the whole expression. X XFor example, this expression produces the absolute value of @code{x}: X X@example Xx > 0 ? x : -x X@end example X XEach time the conditional expression is computed, exactly one of X@var{if-true-exp} and @var{if-false-exp} is computed; the other is ignored. XThis is important when the expressions contain side effects. For example, Xthis conditional expression examines element @code{i} of either array X@code{a} or array @code{b}, and increments @code{i}. X X@example Xx == y ? a[i++] : b[i++] X@end example X X@noindent XThis is guaranteed to increment @code{i} exactly once, because each time Xone or the other of the two increment expressions is executed, Xand the other is not. X X@node Function Calls, Precedence, Conditional Exp, Expressions X@section Function Calls X@cindex function call X@cindex calling a function X XA @dfn{function} is a name for a particular calculation. Because it has Xa name, you can ask for it by name at any point in the program. For Xexample, the function @code{sqrt} computes the square root of a number. X XA fixed set of functions are @dfn{built in}, which means they are Xavailable in every @code{awk} program. The @code{sqrt} function is one Xof these. @xref{Built-in}, for a list of built-in functions and their Xdescriptions. In addition, you can define your own functions in the Xprogram for use elsewhere in the same program. @xref{User-defined}, Xfor how to do this. X X@cindex arguments in function call XThe way to use a function is with a @dfn{function call} expression, Xwhich consists of the function name followed by a list of X@dfn{arguments} in parentheses. The arguments are expressions which Xgive the raw materials for the calculation that the function will do. XWhen there is more than one argument, they are separated by commas. If Xthere are no arguments, write just @samp{()} after the function name. XHere are some examples: X X@example Xsqrt(x**2 + y**2) # @r{One argument} Xatan2(y, x) # @r{Two arguments} Xrand() # @r{No arguments} X@end example X X@strong{Do not put any space between the function name and the Xopen-parenthesis!} A user-defined function name looks just like the name of Xa variable, and space would make the expression look like concatenation Xof a variable with an expression inside parentheses. Space before the Xparenthesis is harmless with built-in functions, but it is best not to get Xinto the habit of using space, lest you do likewise for a user-defined Xfunction one day by mistake. X XEach function expects a particular number of arguments. For example, the X@code{sqrt} function must be called with a single argument, the number Xto take the square root of: X X@example Xsqrt(@var{argument}) X@end example X XSome of the built-in functions allow you to omit the final argument. XIf you do so, they use a reasonable default. @xref{Built-in}, Xfor full details. If arguments are omitted in calls to user-defined Xfunctions, then those arguments are treated as local variables, Xinitialized to the null string (@pxref{User-defined}). X XLike every other expression, the function call has a value, which is Xcomputed by the function based on the arguments you give it. In this Xexample, the value of @code{sqrt(@var{argument})} is the square root of the Xargument. A function can also have side effects, such as assigning the Xvalues of certain variables or doing I/O. X XHere is a command to read numbers, one number per line, and print the Xsquare root of each one: X X@example Xawk '@{ print "The square root of", $1, "is", sqrt($1) @}' X@end example X X@node Precedence,, Function Calls, Expressions X@section Operator Precedence: How Operators Nest X@cindex precedence X@cindex operator precedence X X@dfn{Operator precedence} determines how operators are grouped, when Xdifferent operators appear close by in one expression. For example, X@samp{*} has higher precedence than @samp{+}; thus, @code{a + b * c} Xmeans to multiply @code{b} and @code{c}, and then add @code{a} to the Xproduct. X XYou can overrule the precedence of the operators by writing parentheses Xyourself. You can think of the precedence rules as saying where the Xparentheses are assumed if you do not write parentheses yourself. In Xfact, it is wise always to use parentheses whenever you have an unusual Xcombination of operators, because other people who read the program may Xnot remember what the precedence is in this case. You might forget, Xtoo; then you could make a mistake. Explicit parentheses will prevent Xany such mistake. X XWhen operators of equal precedence are used together, the leftmost Xoperator groups first, except for the assignment, conditional and Xand exponentiation operators, which group in the opposite order. XThus, @code{a - b + c} groups as @code{(a - b) + c}; X@code{a = b = c} groups as @code{a = (b = c)}. X XThe precedence of prefix unary operators does not matter as long as only Xunary operators are involved, because there is only one way to parse Xthem---innermost first. Thus, @code{$++i} means @code{$(++i)} and X@code{++$x} means @code{++($x)}. However, when another operator follows Xthe operand, then the precedence of the unary operators can matter. XThus, @code{$x**2} means @code{($x)**2}, but @code{-x**2} means X@code{-(x**2)}, because @samp{-} has lower precedence than @samp{**} Xwhile @samp{$} has higher precedence. X XHere is a table of the operators of @code{awk}, in order of increasing Xprecedence: X X@table @asis X@item assignment X@samp{=}, @samp{+=}, @samp{-=}, @samp{*=}, @samp{/=}, @samp{%=}, X@samp{^=}, @samp{**=}. These operators group right-to-left. X X@item conditional X@samp{?:}. These operators group right-to-left. X X@item logical ``or''. X@samp{||}. X X@item logical ``and''. X@samp{&&}. X X@item array membership X@code{in}. X X@item matching X@samp{~}, @samp{!~}. X X@item relational, and redirection XThe relational operators and the redirections have the same precedence Xlevel. Characters such as @samp{>} serve both as relationals and as Xredirections; the context distinguishes between the two meanings. X XThe relational operators are @samp{<}, @samp{<=}, @samp{==}, @samp{!=}, X@samp{>=} and @samp{>}. X XThe I/O redirection operators are @samp{<}, @samp{>}, @samp{>>} and X@samp{|}. X XNote that I/O redirection operators in @code{print} and @code{printf} Xstatements belong to the statement level, not to expressions. The Xredirection does not produce an expression which could be the operand of Xanother operator. As a result, it does not make sense to use a Xredirection operator near another operator of lower precedence, without Xparentheses. Such combinations, for example @samp{print foo > a ? b : Xc}, result in syntax errors. X X@item concatentation XNo special token is used to indicate concatenation. XThe operands are simply written side by side. X@c This is supposedly being fixed X@ignore XConcatenation has the same precedence as relational and redirection Xoperators. These operators nest left to right. Thus, @code{4 5 > 6} Xconcatenates first, yielding 1, while @code{6 < 4 5} compares first, and Xyields @code{"05"}. X@end ignore X X@item add, subtract X@samp{+}, @samp{-}. X X@item multiply, divide, mod X@samp{*}, @samp{/}, @samp{%}. X X@item unary plus, minus, ``not'' X@samp{+}, @samp{-}, @samp{!}. X X@item exponentiation X@samp{^}, @samp{**}. These operators group right-to-left. X X@item increment, decrement X@samp{++}, @samp{--}. X X@item field X@samp{$}. X@end table X X@node Statements, Arrays, Expressions, Top X@chapter Actions: Control Statements X@cindex control statement X X@dfn{Control statements} such as @code{if}, @code{while}, and so on Xcontrol the flow of execution in @code{awk} programs. Most of the Xcontrol statements in @code{awk} are patterned on similar statements in XC. X XAll the control statements start with special keywords such as @code{if} Xand @code{while}, to distinguish them from simple expressions. X XMany control statements contain other statements; for example, the X@code{if} statement contains another statement which may or may not be Xexecuted. The contained statement is called the @dfn{body}. If you Xwant to include more than one statement in the body, group them into a Xsingle compound statement with curly braces, separating them with Xnewlines or semicolons. X X@menu X* If Statement:: Conditionally execute some @code{awk} statements. X X* While Statement:: Loop until some condition is satisfied. X X* Do Statement:: Do specified action while looping until some X condition is satisfied. X X* For Statement:: Another looping statement, that provides X initialization and increment clauses. X X* Break Statement:: Immediately exit the innermost enclosing loop. X X* Continue Statement:: Skip to the end of the innermost enclosing loop. X X* Next Statement:: Stop processing the current input record. X X* Exit Statement:: Stop execution of @code{awk}. X@end menu X X@node If Statement, While Statement, Statements, Statements X@section The @code{if} Statement X X@cindex @code{if} statement XThe @code{if}-@code{else} statement is @code{awk}'s decision-making Xstatement. It looks like this:@refill X X@example Xif (@var{condition}) @var{then-body} @r{[}else @var{else-body}@r{]} X@end example X X@noindent XHere @var{condition} is an expression that controls what the rest of the Xstatement will do. If @var{condition} is true, @var{then-body} is Xexecuted; otherwise, @var{else-body} is executed (assuming that the X@code{else} clause is present). The @code{else} part of the statement is Xoptional. The condition is considered false if its value is zero or Xthe null string, true otherwise.@refill X XHere is an example: X X@example Xif (x % 2 == 0) X print "x is even" Xelse X print "x is odd" X@end example X XIn this example, if the expression @code{x % 2 == 0} is true (that is, Xthe value of @code{x} is divisible by 2), then the first @code{print} Xstatement is executed, otherwise the second @code{print} statement is Xperformed.@refill X XIf the @code{else} appears on the same line as @var{then-body}, and X@var{then-body} is not a compound statement (i.e., not surrounded by Xcurly braces), then a semicolon must separate @var{then-body} from X@code{else}. To illustrate this, let's rewrite the previous example: X X@group X@example Xawk '@{ if (x % 2 == 0) print "x is even"; else X print "x is odd" @}' X@end example X@end group X X@noindent XIf you forget the @samp{;}, @code{awk} won't be able to parse the Xstatement, and you will get a syntax error. X XWe would not actually write this example this way, because a human Xreader might fail to see the @code{else} if it were not the first thing Xon its line. X X@node While Statement, Do Statement, If Statement, Statements X@section The @code{while} Statement X@cindex @code{while} statement X@cindex loop X@cindex body of a loop X XIn programming, a @dfn{loop} means a part of a program that is (or at least can Xbe) executed two or more times in succession. X XThe @code{while} statement is the simplest looping statement in X@code{awk}. It repeatedly executes a statement as long as a condition is Xtrue. It looks like this: X X@example Xwhile (@var{condition}) X @var{body} X@end example X X@noindent XHere @var{body} is a statement that we call the @dfn{body} of the loop, Xand @var{condition} is an expression that controls how long the loop Xkeeps running. X XThe first thing the @code{while} statement does is test @var{condition}. XIf @var{condition} is true, it executes the statement @var{body}. X(Truth, as usual in @code{awk}, means that the value of @var{condition} Xis not zero and not a null string.) After @var{body} has been executed, X@var{condition} is tested again, and if it is still true, @var{body} is Xexecuted again. This process repeats until @var{condition} is no longer Xtrue. If @var{condition} is initially false, the body of the loop is Xnever executed.@refill X XThis example prints the first three fields of each record, one per line. X X@example Xawk '@{ i = 1 X while (i <= 3) @{ X print $i X i++ X @} X@}' X@end example X X@noindent XHere the body of the loop is a compound statement enclosed in braces, Xcontaining two statements. X XThe loop works like this: first, the value of @code{i} is set to 1. XThen, the @code{while} tests whether @code{i} is less than or equal to Xthree. This is the case when @code{i} equals one, so the @code{i}-th Xfield is printed. Then the @code{i++} increments the value of @code{i} Xand the loop repeats. The loop terminates when @code{i} reaches 4. X XAs you can see, a newline is not required between the condition and the Xbody; but using one makes the program clearer unless the body is a Xcompound statement or is very simple. The newline after the open-brace Xthat begins the compound statement is not required either, but the Xprogram would be hard to read without it. X X@node Do Statement, For Statement, While Statement, Statements X@section The @code{do}-@code{while} Statement X XThe @code{do} loop is a variation of the @code{while} looping statement. XThe @code{do} loop executes the @var{body} once, then repeats @var{body} Xas long as @var{condition} is true. It looks like this: X X@group X@example Xdo X @var{body} Xwhile (@var{condition}) X@end example X@end group X XEven if @var{condition} is false at the start, @var{body} is executed at Xleast once (and only once, unless executing @var{body} makes X@var{condition} true). Contrast this with the corresponding X@code{while} statement: X X@example Xwhile (@var{condition}) X @var{body} X@end example X X@noindent XThis statement does not execute @var{body} even once if @var{condition} Xis false to begin with. X XHere is an example of a @code{do} statement: X X@example Xawk '@{ i = 1 X do @{ X print $0 X i++ X @} while (i <= 10) X@}' X@end example X X@noindent Xprints each input record ten times. It isn't a very realistic example, Xsince in this case an ordinary @code{while} would do just as well. But Xthis reflects actual experience; there is only occasionally a real use Xfor a @code{do} statement.@refill X X@node For Statement, Break Statement, Do Statement, Statements X@section The @code{for} Statement X@cindex @code{for} statement X XThe @code{for} statement makes it more convenient to count iterations of a Xloop. The general form of the @code{for} statement looks like this:@refill X X@example Xfor (@var{initialization}; @var{condition}; @var{increment}) X @var{body} X@end example X X@noindent XThis statement starts by executing @var{initialization}. Then, as long Xas @var{condition} is true, it repeatedly executes @var{body} and then X@var{increment}. Typically @var{initialization} sets a variable to Xeither zero or one, @var{increment} adds 1 to it, and @var{condition} Xcompares it against the desired number of iterations. X XHere is an example of a @code{for} statement: X X@example Xawk '@{ for (i = 1; i <= 3; i++) X print $i X@}' X@end example X X@noindent XThis prints the first three fields of each input record, one field per Xline. X XIn the @code{for} statement, @var{body} stands for any statement, but X@var{initialization}, @var{condition} and @var{increment} are just Xexpressions. You cannot set more than one variable in the X@var{initialization} part unless you use a multiple assignment statement Xsuch as @code{x = y = 0}, which is possible only if all the initial values Xare equal. (But you can initialize additional variables by writing Xtheir assignments as separate statements preceding the @code{for} loop.) X XThe same is true of the @var{increment} part; to increment additional Xvariables, you must write separate statements at the end of the loop. XThe C compound expression, using C's comma operator, would be useful in Xthis context, but it is not supported in @code{awk}. X XMost often, @var{increment} is an increment expression, as in the Xexample above. But this is not required; it can be any expression Xwhatever. For example, this statement prints all the powers of 2 Xbetween 1 and 100: X X@example Xfor (i = 1; i <= 100; i *= 2) X print i X@end example X XAny of the three expressions in the parentheses following @code{for} may Xbe omitted if there is nothing to be done there. Thus, @w{@samp{for (;x X> 0;)}} is equivalent to @w{@samp{while (x > 0)}}. If the X@var{condition} is omitted, it is treated as @var{true}, effectively Xyielding an infinite loop.@refill X XIn most cases, a @code{for} loop is an abbreviation for a @code{while} Xloop, as shown here: X X@example X@var{initialization} Xwhile (@var{condition}) @{ X @var{body} X @var{increment} X@} X@end example X X@noindent XThe only exception is when the @code{continue} statement X(@pxref{Continue Statement}) is used inside the loop; changing a X@code{for} statement to a @code{while} statement in this way can change Xthe effect of the @code{continue} statement inside the loop. X XThere is an alternate version of the @code{for} loop, for iterating over Xall the indices of an array: X X@example Xfor (i in array) X @var{do something with} array[i] X@end example X X@noindent X@xref{Arrays}, for more information on this version of the @code{for} loop. X XThe @code{awk} language has a @code{for} statement in addition to a X@code{while} statement because often a @code{for} loop is both less work to Xtype and more natural to think of. Counting the number of iterations is Xvery common in loops. It can be easier to think of this counting as part Xof looping rather than as something to do inside the loop. X XThe next section has more complicated examples of @code{for} loops. X X@node Break Statement, Continue Statement, For Statement, Statements X@section The @code{break} Statement X@cindex @code{break} statement X@cindex loops, exiting X XThe @code{break} statement jumps out of the innermost @code{for}, X@code{while}, or @code{do}-@code{while} loop that encloses it. The Xfollowing example finds the smallest divisor of any integer, and also Xidentifies prime numbers:@refill X X@example Xawk '# find smallest divisor of num X @{ num = $1 X for (div = 2; div*div <= num; div++) X if (num % div == 0) X break X if (num % div == 0) X printf "Smallest divisor of %d is %d\n", num, div X else X printf "%d is prime\n", num @}' X@end example X XWhen the remainder is zero in the first @code{if} statement, @code{awk} Ximmediately @dfn{breaks out} of the containing @code{for} loop. This means Xthat @code{awk} proceeds immediately to the statement following the loop Xand continues processing. (This is very different from the @code{exit} Xstatement (@pxref{Exit Statement}) which stops the entire @code{awk} Xprogram.)@refill X XHere is another program equivalent to the previous one. It illustrates how Xthe @var{condition} of a @code{for} or @code{while} could just as well be Xreplaced with a @code{break} inside an @code{if}: X X@example Xawk '# find smallest divisor of num X @{ num = $1 X for (div = 2; ; div++) @{ X if (num % div == 0) @{ X printf "Smallest divisor of %d is %d\n", num, div X break X @} X if (div*div > num) @{ X printf "%d is prime\n", num X break X @} X @} X@}' X@end example X X@node Continue Statement, Next Statement, Break Statement, Statements X@section The @code{continue} Statement X X@cindex @code{continue} statement XThe @code{continue} statement, like @code{break}, is used only inside X@code{for}, @code{while}, and @code{do}-@code{while} loops. It skips Xover the rest of the loop body, causing the next cycle around the loop Xto begin immediately. Contrast this with @code{break}, which jumps out Xof the loop altogether. Here is an example:@refill X X@example X# print names that don't contain the string "ignore" X X# first, save the text of each line X@{ names[NR] = $0 @} X X# print what we're interested in XEND @{ X for (x in names) @{ X if (names[x] ~ /ignore/) X continue X print names[x] X @} X@} X@end example X XIf one of the input records contains the string @samp{ignore}, this Xexample skips the print statement for that record, and continues back to Xthe first statement in the loop. X XThis isn't a practical example of @code{continue}, since it would be Xjust as easy to write the loop like this: X X@example Xfor (x in names) X if (names[x] !~ /ignore/) X print names[x] X@end example X XThe @code{continue} statement in a @code{for} loop directs @code{awk} to Xskip the rest of the body of the loop, and resume execution with the Xincrement-expression of the @code{for} statement. The following program Xillustrates this fact:@refill X X@example Xawk 'BEGIN @{ X for (x = 0; x <= 20; x++) @{ X if (x == 5) X continue X printf ("%d ", x) X @} X print "" X@}' X@end example X X@noindent XThis program prints all the numbers from 0 to 20, except for 5, for Xwhich the @code{printf} is skipped. Since the increment @code{x++} Xis not skipped, @code{x} does not remain stuck at 5. Contrast the X@code{for} loop above with the @code{while} loop: X X@example Xawk 'BEGIN @{ X x = 0 X while (x <= 20) @{ X if (x == 5) X continue X printf ("%d ", x) X x++ X @} X print "" X@}' X@end example X X@noindent XThis program loops forever once @code{x} gets to 5. X X@node Next Statement, Exit Statement, Continue Statement, Statements X@section The @code{next} Statement X@cindex @code{next} statement X XThe @code{next} statement forces @code{awk} to immediately stop processing Xthe current record and go on to the next record. This means that no Xfurther rules are executed for the current record. The rest of the Xcurrent rule's action is not executed either. X XContrast this with the effect of the @code{getline} function X(@pxref{Getline}). That too causes @code{awk} to read the next record Ximmediately, but it does not alter the flow of control in any way. So Xthe rest of the current action executes with a new input record. X XAt the grossest level, @code{awk} program execution is a loop that reads Xan input record and then tests each rule's pattern against it. If you Xthink of this loop as a @code{for} statement whose body contains the Xrules, then the @code{next} statement is analogous to a @code{continue} Xstatement: it skips to the end of the body of this implicit loop, and Xexecutes the increment (which reads another record). X XFor example, if your @code{awk} program works only on records with four Xfields, and you don't want it to fail when given bad input, you might Xuse this rule near the beginning of the program: X X@example XNF != 4 @{ X printf("line %d skipped: doesn't have 4 fields", FNR) > "/dev/stderr" X next X@} X@end example X X@noindent Xso that the following rules will not see the bad record. The error Xmessage is redirected to the standard error output stream, as error Xmessages should be. @xref{Special Files}. X XThe @code{next} statement is not allowed in a @code{BEGIN} or @code{END} Xrule. X X@node Exit Statement, , Next Statement, Statements X@section The @code{exit} Statement X X@cindex @code{exit} statement XThe @code{exit} statement causes @code{awk} to immediately stop Xexecuting the current rule and to stop processing input; any remaining input Xis ignored.@refill X XIf an @code{exit} statement is executed from a @code{BEGIN} rule the Xprogram stops processing everything immediately. No input records are Xread. However, if an @code{END} rule is present, it is executed X(@pxref{BEGIN/END}). X XIf @code{exit} is used as part of an @code{END} rule, it causes Xthe program to stop immediately. X XAn @code{exit} statement that is part an ordinary rule (that is, not part Xof a @code{BEGIN} or @code{END} rule) stops the execution of any further Xautomatic rules, but the @code{END} rule is executed if there is one. XIf you don't want the @code{END} rule to do its job in this case, you Xcan set a variable to nonzero before the @code{exit} statement, and check Xthat variable in the @code{END} rule. X XIf an argument is supplied to @code{exit}, its value is used as the exit Xstatus code for the @code{awk} process. If no argument is supplied, X@code{exit} returns status zero (success).@refill X XFor example, let's say you've discovered an error condition you really Xdon't know how to handle. Conventionally, programs report this by Xexiting with a nonzero status. Your @code{awk} program can do this Xusing an @code{exit} statement with a nonzero argument. Here's an Xexample of this:@refill X X@example XBEGIN @{ X if (("date" | getline date_now) < 0) @{ X print "Can't get system date" > "/dev/stderr" X exit 4 X @} X@} X@end example X X@node Arrays, Built-in, Statements, Top X@chapter Arrays in @code{awk} X XAn @dfn{array} is a table of various values, called @dfn{elements}. The Xelements of an array are distinguished by their @dfn{indices}. Indices Xmay be either numbers or strings. Each array has a name, which looks Xlike a variable name, but must not be in use as a variable name in the Xsame @code{awk} program. X X@menu X* Intro: Array Intro. Basic facts about arrays in @code{awk}. X* Reference to Elements:: How to examine one element of an array. X* Assigning Elements:: How to change an element of an array. X* Example: Array Example. Sample program explained. X X* Scanning an Array:: A variation of the @code{for} statement. It loops X through the indices of an array's existing elements. X X* Delete:: The @code{delete} statement removes an element from an array. X X* Multi-dimensional:: Emulating multi-dimensional arrays in @code{awk}. X* Multi-scanning:: Scanning multi-dimensional arrays. X@end menu X X@node Array Intro, Reference to Elements, Arrays, Arrays X@section Introduction to Arrays X X@cindex arrays XThe @code{awk} language has one-dimensional @dfn{arrays} for storing groups Xof related strings or numbers. X XEvery @code{awk} array must have a name. Array names have the same Xsyntax as variable names; any valid variable name would also be a valid Xarray name. But you cannot use one name in both ways (as an array and Xas a variable) in one @code{awk} program. X XArrays in @code{awk} superficially resemble arrays in other programming Xlanguages; but there are fundamental differences. In @code{awk}, you Xdon't need to specify the size of an array before you start to use it. XWhat's more, in @code{awk} any number or even a string may be used as an Xarray index. X XIn most other languages, you have to @dfn{declare} an array and specify Xhow many elements or components it has. In such languages, the Xdeclaration causes a contiguous block of memory to be allocated for that Xmany elements. An index in the array must be a positive integer; for Xexample, the index 0 specifies the first element in the array, which is Xactually stored at the beginning of the block of memory. Index 1 Xspecifies the second element, which is stored in memory right after the Xfirst element, and so on. It is impossible to add more elements to the Xarray, because it has room for only as many elements as you declared. X XA contiguous array of four elements might look like this, conceptually, Xif the element values are 8, @code{"foo"}, @code{""} and 30:@refill X X@example X+---------+---------+--------+---------+ X| 8 | "foo" | "" | 30 | @r{value} X+---------+---------+--------+---------+ X 0 1 2 3 @r{index} X@end example X X@noindent XOnly the values are stored; the indices are implicit from the order of Xthe values. 8 is the value at index 0, because 8 appears in the Xposition with 0 elements before it. X X@cindex arrays, definition of X@cindex associative arrays XArrays in @code{awk} are different: they are @dfn{associative}. This means Xthat each array is a collection of pairs: an index, and its corresponding Xarray element value: X X@example X@r{Element} 4 @r{Value} 30 X@r{Element} 2 @r{Value} "foo" X@r{Element} 1 @r{Value} 8 X@r{Element} 3 @r{Value} "" X@end example X X@noindent XWe have shown the pairs in jumbled order because their order doesn't Xmean anything. X XOne advantage of an associative array is that new pairs can be added Xat any time. For example, suppose we add to that array a tenth element Xwhose value is @w{@code{"number ten"}}. The result is this: X X@example X@r{Element} 10 @r{Value} "number ten" X@r{Element} 4 @r{Value} 30 X@r{Element} 2 @r{Value} "foo" X@r{Element} 1 @r{Value} 8 X@r{Element} 3 @r{Value} "" X@end example X X@noindent XNow the array is @dfn{sparse} (i.e., some indices are missing): it has Xelements 4 and 10, but doesn't have elements 5, 6, 7, 8, or 9.@refill X XAnother consequence of associative arrays is that the indices don't Xhave to be positive integers. Any number, or even a string, can be Xan index. For example, here is an array which translates words from XEnglish into French: X X@example X@r{Element} "dog" @r{Value} "chien" X@r{Element} "cat" @r{Value} "chat" X@r{Element} "one" @r{Value} "un" X@r{Element} 1 @r{Value} "un" X@end example X X@noindent XHere we decided to translate the number 1 in both spelled-out and Xnumeric form---thus illustrating that a single array can have both Xnumbers and strings as indices. X XWhen @code{awk} creates an array for you, e.g., with the @code{split} Xbuilt-in function (@pxref{String Functions}), that array's indices Xare consecutive integers starting at 1. X X@node Reference to Elements, Assigning Elements, Array Intro, Arrays X@section Referring to an Array Element X@cindex array reference X@cindex element of array X@cindex reference to array X XThe principal way of using an array is to refer to one of its elements. XAn array reference is an expression which looks like this: X X@example X@var{array}[@var{index}] X@end example X X@noindent XHere @var{array} is the name of an array. The expression @var{index} is Xthe index of the element of the array that you want. X XThe value of the array reference is the current value of that array Xelement. For example, @code{foo[4.3]} is an expression for the element Xof array @code{foo} at index 4.3. X XIf you refer to an array element that has no recorded value, the value Xof the reference is @code{""}, the null string. This includes elements Xto which you have not assigned any value, and elements that have been Xdeleted (@pxref{Delete}). Such a reference automatically creates that Xarray element, with the null string as its value. (In some cases, Xthis is unfortunate, because it might waste memory inside @code{awk}). X X@cindex arrays, determining presence of elements XYou can find out if an element exists in an array at a certain index with Xthe expression: X X@example X@var{index} in @var{array} X@end example X X@noindent XThis expression tests whether or not the particular index exists, Xwithout the side effect of creating that element if it is not present. XThe expression has the value 1 (true) if @code{@var{array}[@var{index}]} Xexists, and 0 (false) if it does not exist.@refill X XFor example, to test whether the array @code{frequencies} contains the Xindex @code{"2"}, you could write this statement:@refill X X@example Xif ("2" in frequencies) print "Subscript \"2\" is present." X@end example X XNote that this is @emph{not} a test of whether or not the array X@code{frequencies} contains an element whose @emph{value} is @code{"2"}. X(There is no way to do that except to scan all the elements.) Also, this X@emph{does not} create @code{frequencies["2"]}, while the following X(incorrect) alternative would do so:@refill X X@example Xif (frequencies["2"] != "") print "Subscript \"2\" is present." X@end example X X@node Assigning Elements, Array Example, Reference to Elements, Arrays X@section Assigning Array Elements X@cindex array assignment X@cindex element assignment X XArray elements are lvalues: they can be assigned values just like X@code{awk} variables: X X@example X@var{array}[@var{subscript}] = @var{value} X@end example X X@noindent XHere @var{array} is the name of your array. The expression X@var{subscript} is the index of the element of the array that you want Xto assign a value. The expression @var{value} is the value you are Xassigning to that element of the array.@refill X X@node Array Example, Scanning an Array, Assigning Elements, Arrays X@section Basic Example of an Array X XThe following program takes a list of lines, each beginning with a line Xnumber, and prints them out in order of line number. The line numbers are Xnot in order, however, when they are first read: they are scrambled. This Xprogram sorts the lines by making an array using the line numbers as Xsubscripts. It then prints out the lines in sorted order of their numbers. XIt is a very simple program, and gets confused if it encounters repeated Xnumbers, gaps, or lines that don't begin with a number.@refill X X@example X@{ X if ($1 > max) X max = $1 X arr[$1] = $0 X@} X XEND @{ X for (x = 1; x <= max; x++) X print arr[x] X@} X@end example X X@ignore XThe first rule just initializes the variable @code{max}. (This is not Xstrictly necessary, since an uninitialized variable has the null string Xas its value, and the null string is effectively zero when used in Xa context where a number is required.) X@end ignore X XThe first rule keeps track of the largest line number seen so far; Xit also stores each line into the array @code{arr}, at an index that Xis the line's number. X XThe second rule runs after all the input has been read, to print out Xall the lines. X XWhen this program is run with the following input: X X@example X5 I am the Five man X2 Who are you? The new number two! X4 . . . And four on the floor X1 Who is number one? X3 I three you. X@end example X X@noindent Xits output is this: X X@example X1 Who is number one? X2 Who are you? The new number two! X3 I three you. X4 . . . And four on the floor X5 I am the Five man X@end example X END_OF_FILE if test 49666 -ne `wc -c <'./gawk.texinfo.04'`; then echo shar: \"'./gawk.texinfo.04'\" unpacked with wrong size! fi # end of './gawk.texinfo.04' fi if test -f './missing.d/strtod.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'./missing.d/strtod.c'\" else echo shar: Extracting \"'./missing.d/strtod.c'\" \(2023 characters\) sed "s/^X//" >'./missing.d/strtod.c' <<'END_OF_FILE' X/* X * strtod.c X * X * Stupid version of System V strtod(3) library routine. X * Does no overflow/underflow checking. X * X * A real number is defined to be X * optional leading white space X * optional sign X * string of digits with optional decimal point X * optional 'e' or 'E' X * followed by optional sign or space X * followed by an integer X * X * if ptr is not NULL a pointer to the character terminating the X * scan is returned in *ptr. If no number formed, *ptr is set to str X * and 0 is returned. X * X * For speed, we don't do the conversion ourselves. Instead, we find X * the end of the number and then call atof() to do the dirty work. X * This bought us a 10% speedup on a sample program at uunet.uu.net. X */ X X#include <ctype.h> X Xextern double atof(); X Xdouble Xstrtod (s, ptr) Xregister char *s, **ptr; X{ X double ret = 0.0; X char *start = s; X char *begin = NULL; X int success = 0; X X /* optional white space */ X while (isspace(*s)) X s++; X X /* optional sign */ X if (*s == '+' || *s == '-') { X s++; X if (*(s-1) == '-') X begin = s - 1; X else X begin = s; X } X X /* string of digits with optional decimal point */ X if (isdigit(*s) && ! begin) X begin = s; X X while (isdigit(*s)) { X s++; X success++; X } X X if (*s == '.') { X if (! begin) X begin = s; X s++; X while (isdigit(*s)) X s++; X success++; X } X X if (s == start || success == 0) /* nothing there */ X goto out; X X /* X * optional 'e' or 'E' X * followed by optional sign or space X * followed by an integer X */ X X if (*s == 'e' || *s == 'E') { X s++; X X /* XXX - atof probably doesn't allow spaces here */ X while (isspace(*s)) X s++; X X if (*s == '+' || *s == '-') X s++; X X while (isdigit(*s)) X s++; X } X X /* go for it */ X ret = atof(begin); X Xout: X if (! success) X s = start; /* in case all we did was skip whitespace */ X X if (ptr) X *ptr = s; X X return ret; X} X X#ifdef TEST Xmain (argc, argv) Xint argc; Xchar **argv; X{ X double d; X char *p; X X for (argc--, argv++; argc; argc--, argv++) { X d = strtod (*argv, & p); X printf ("%lf [%s]\n", d, p); X } X} X#endif END_OF_FILE if test 2023 -ne `wc -c <'./missing.d/strtod.c'`; then echo shar: \"'./missing.d/strtod.c'\" unpacked with wrong size! fi # end of './missing.d/strtod.c' fi echo shar: End of archive 3 \(of 16\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 16 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still must unpack the following archives: echo " " ${MISSING} fi exit 0 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.