Importing NetBSD expr
Change-Id: Ifdb1160cbfeb1324f6fcd612e66112fa0940227c
This commit is contained in:
parent
059578953d
commit
5debab0eb0
7 changed files with 735 additions and 249 deletions
|
@ -1,7 +1,7 @@
|
||||||
# $NetBSD: Makefile,v 1.22 2007/12/31 15:31:24 ad Exp $
|
# $NetBSD: Makefile,v 1.22 2007/12/31 15:31:24 ad Exp $
|
||||||
# @(#)Makefile 8.1 (Berkeley) 5/31/93
|
# @(#)Makefile 8.1 (Berkeley) 5/31/93
|
||||||
|
|
||||||
SUBDIR= cat date echo ed \
|
SUBDIR= cat date echo ed expr \
|
||||||
mkdir pax rm rmdir test
|
mkdir pax rm rmdir test
|
||||||
|
|
||||||
.include <bsd.subdir.mk>
|
.include <bsd.subdir.mk>
|
||||||
|
|
6
bin/expr/Makefile
Normal file
6
bin/expr/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# $NetBSD: Makefile,v 1.14 2000/09/19 17:20:00 jdolecek Exp $
|
||||||
|
|
||||||
|
PROG= expr
|
||||||
|
SRCS= expr.y
|
||||||
|
|
||||||
|
.include <bsd.prog.mk>
|
267
bin/expr/expr.1
Normal file
267
bin/expr/expr.1
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
.\" $NetBSD: expr.1,v 1.33 2012/08/12 17:27:04 wiz Exp $
|
||||||
|
.\"
|
||||||
|
.\" Copyright (c) 2000,2003 The NetBSD Foundation, Inc.
|
||||||
|
.\" All rights reserved.
|
||||||
|
.\"
|
||||||
|
.\" This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
.\" by J.T. Conklin <jtc@NetBSD.org> and Jaromir Dolecek <jdolecek@NetBSD.org>.
|
||||||
|
.\"
|
||||||
|
.\" 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.
|
||||||
|
.\"
|
||||||
|
.Dd April 20, 2004
|
||||||
|
.Dt EXPR 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm expr
|
||||||
|
.Nd evaluate expression
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Ar expression
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility evaluates
|
||||||
|
.Ar expression
|
||||||
|
and writes the result on standard output.
|
||||||
|
.Pp
|
||||||
|
All operators are separate arguments to the
|
||||||
|
.Nm
|
||||||
|
utility.
|
||||||
|
Characters special to the command interpreter must be escaped.
|
||||||
|
.Pp
|
||||||
|
Operators are listed below in order of increasing precedence.
|
||||||
|
Operators with equal precedence are grouped within { } symbols.
|
||||||
|
.Bl -tag -width indent
|
||||||
|
.It Ar expr1 Li \&| Ar expr2
|
||||||
|
Returns the evaluation of
|
||||||
|
.Ar expr1
|
||||||
|
if it is neither an empty string nor zero;
|
||||||
|
otherwise, returns the evaluation of
|
||||||
|
.Ar expr2 .
|
||||||
|
.It Ar expr1 Li \*[Am] Ar expr2
|
||||||
|
Returns the evaluation of
|
||||||
|
.Ar expr1
|
||||||
|
if neither expression evaluates to an empty string or zero;
|
||||||
|
otherwise, returns zero.
|
||||||
|
.It Ar expr1 Li "{=, \*[Gt], \*[Ge], \*[Lt], \*[Le], !=}" Ar expr2
|
||||||
|
Returns the results of integer comparison if both arguments are integers;
|
||||||
|
otherwise, returns the results of string comparison using the locale-specific
|
||||||
|
collation sequence.
|
||||||
|
The result of each comparison is 1 if the specified relation is true,
|
||||||
|
or 0 if the relation is false.
|
||||||
|
.It Ar expr1 Li "{+, -}" Ar expr2
|
||||||
|
Returns the results of addition or subtraction of integer-valued arguments.
|
||||||
|
.It Ar expr1 Li "{*, /, %}" Ar expr2
|
||||||
|
Returns the results of multiplication, integer division, or remainder of integer-valued arguments.
|
||||||
|
.It Ar expr1 Li \&: Ar expr2
|
||||||
|
The
|
||||||
|
.Dq \&:
|
||||||
|
operator matches
|
||||||
|
.Ar expr1
|
||||||
|
against
|
||||||
|
.Ar expr2 ,
|
||||||
|
which must be a regular expression.
|
||||||
|
The regular expression is anchored
|
||||||
|
to the beginning of the string with an implicit
|
||||||
|
.Dq ^ .
|
||||||
|
.Pp
|
||||||
|
If the match succeeds and the pattern contains at least one regular
|
||||||
|
expression subexpression
|
||||||
|
.Dq "\e(...\e)" ,
|
||||||
|
the string corresponding to
|
||||||
|
.Dq "\e1"
|
||||||
|
is returned;
|
||||||
|
otherwise the matching operator returns the number of characters matched.
|
||||||
|
If the match fails and the pattern contains a regular expression subexpression
|
||||||
|
the null string is returned;
|
||||||
|
otherwise 0.
|
||||||
|
.It "( " Ar expr No " )"
|
||||||
|
Parentheses are used for grouping in the usual manner.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Additionally, the following keywords are recognized:
|
||||||
|
.Bl -tag -width indent
|
||||||
|
.It length Ar expr
|
||||||
|
Returns the length of the specified string in bytes.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
Operator precedence (from highest to lowest):
|
||||||
|
.Bl -enum -compact -offset indent
|
||||||
|
.It
|
||||||
|
parentheses
|
||||||
|
.It
|
||||||
|
length
|
||||||
|
.It
|
||||||
|
.Dq \&:
|
||||||
|
.It
|
||||||
|
.Dq "*" ,
|
||||||
|
.Dq "/" ,
|
||||||
|
and
|
||||||
|
.Dq "%"
|
||||||
|
.It
|
||||||
|
.Dq "+"
|
||||||
|
and
|
||||||
|
.Dq "-"
|
||||||
|
.It
|
||||||
|
compare operators
|
||||||
|
.It
|
||||||
|
.Dq \*[Am]
|
||||||
|
.It
|
||||||
|
.Dq \&|
|
||||||
|
.El
|
||||||
|
.Sh EXIT STATUS
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility exits with one of the following values:
|
||||||
|
.Bl -tag -width Ds -compact
|
||||||
|
.It 0
|
||||||
|
the expression is neither an empty string nor 0.
|
||||||
|
.It 1
|
||||||
|
the expression is an empty string or 0.
|
||||||
|
.It 2
|
||||||
|
the expression is invalid.
|
||||||
|
.It \*[Gt]2
|
||||||
|
an error occurred (such as memory allocation failure).
|
||||||
|
.El
|
||||||
|
.Sh EXAMPLES
|
||||||
|
.Bl -enum
|
||||||
|
.It
|
||||||
|
The following example adds one to variable
|
||||||
|
.Dq a :
|
||||||
|
.Dl a=`expr $a + 1`
|
||||||
|
.It
|
||||||
|
The following example returns zero, due to subtraction having higher precedence
|
||||||
|
than the
|
||||||
|
.Dq \*[Am]
|
||||||
|
operator:
|
||||||
|
.Dl expr 1 '\*[Am]' 1 - 1
|
||||||
|
.It
|
||||||
|
The following example returns the filename portion of a pathname stored
|
||||||
|
in variable
|
||||||
|
.Dq a :
|
||||||
|
.Dl expr "/$a" Li : '.*/\e(.*\e)'
|
||||||
|
.It
|
||||||
|
The following example returns the number of characters in variable
|
||||||
|
.Dq a :
|
||||||
|
.Dl expr $a Li : '.*'
|
||||||
|
.El
|
||||||
|
.Sh COMPATIBILITY
|
||||||
|
This implementation of
|
||||||
|
.Nm
|
||||||
|
internally uses 64 bit representation of integers and checks for
|
||||||
|
over- and underflows.
|
||||||
|
It also treats
|
||||||
|
.Dq /
|
||||||
|
(the division mark) and option
|
||||||
|
.Dq --
|
||||||
|
correctly depending upon context.
|
||||||
|
.Pp
|
||||||
|
.Nm
|
||||||
|
on other systems (including
|
||||||
|
.Nx
|
||||||
|
up to and including
|
||||||
|
.Nx 1.5 )
|
||||||
|
might not be so graceful.
|
||||||
|
Arithmetic results might be arbitrarily
|
||||||
|
limited on such systems, most commonly to 32 bit quantities.
|
||||||
|
This means such
|
||||||
|
.Nm
|
||||||
|
can only process values between -2147483648 and +2147483647.
|
||||||
|
.Pp
|
||||||
|
On other systems,
|
||||||
|
.Nm
|
||||||
|
might also not work correctly for regular expressions where
|
||||||
|
either side contains
|
||||||
|
.Dq /
|
||||||
|
(a single forward slash), like this:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
expr / : '.*/\e(.*\e)'
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
If this is the case, you might use
|
||||||
|
.Dq //
|
||||||
|
(a double forward slash)
|
||||||
|
to avoid confusion with the division operator:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
expr "//$a" : '.*/\e(.*\e)'
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
According to
|
||||||
|
.St -p1003.2 ,
|
||||||
|
.Nm
|
||||||
|
has to recognize special option
|
||||||
|
.Dq -- ,
|
||||||
|
treat it as a delimiter to mark the end of command
|
||||||
|
line options, and ignore it.
|
||||||
|
Some
|
||||||
|
.Nm
|
||||||
|
implementations don't recognize it at all; others
|
||||||
|
might ignore it even in cases where doing so results in syntax
|
||||||
|
error.
|
||||||
|
There should be same result for both following examples,
|
||||||
|
but it might not always be:
|
||||||
|
.Bl -enum -compact -offset indent
|
||||||
|
.It
|
||||||
|
expr -- : .
|
||||||
|
.It
|
||||||
|
expr -- -- : .
|
||||||
|
.El
|
||||||
|
Although
|
||||||
|
.Nx
|
||||||
|
.Nm
|
||||||
|
handles both cases correctly, you should not depend on this behavior
|
||||||
|
for portability reasons and avoid passing a bare
|
||||||
|
.Dq --
|
||||||
|
as the first
|
||||||
|
argument.
|
||||||
|
.Sh STANDARDS
|
||||||
|
The
|
||||||
|
.Nm
|
||||||
|
utility conforms to
|
||||||
|
.St -p1003.2 .
|
||||||
|
The
|
||||||
|
.Ar length
|
||||||
|
keyword is an extension for compatibility with GNU
|
||||||
|
.Nm .
|
||||||
|
.Sh AUTHORS
|
||||||
|
Original implementation was written by
|
||||||
|
.An J.T. Conklin
|
||||||
|
.Aq jtc@NetBSD.org .
|
||||||
|
It was rewritten for
|
||||||
|
.Nx 1.6
|
||||||
|
by
|
||||||
|
.An Jaromir Dolecek
|
||||||
|
.Aq jdolecek@NetBSD.org .
|
||||||
|
.Sh NOTES
|
||||||
|
The empty string
|
||||||
|
.Do Dc
|
||||||
|
cannot be matched with the intuitive:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
expr '' : '$'
|
||||||
|
.Ed
|
||||||
|
.Pp
|
||||||
|
The reason is that the returned number of matched characters (zero)
|
||||||
|
is indistinguishable from a failed match, so this returns failure.
|
||||||
|
To match the empty string, use something like:
|
||||||
|
.Bd -literal -offset indent
|
||||||
|
expr x'' : 'x$'
|
||||||
|
.Ed
|
459
bin/expr/expr.y
Normal file
459
bin/expr/expr.y
Normal file
|
@ -0,0 +1,459 @@
|
||||||
|
/* $NetBSD: expr.y,v 1.38 2012/03/15 02:02:20 joerg Exp $ */
|
||||||
|
|
||||||
|
/*_
|
||||||
|
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to The NetBSD Foundation
|
||||||
|
* by Jaromir Dolecek <jdolecek@NetBSD.org> and J.T. Conklin <jtc@NetBSD.org>.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
%{
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#ifndef lint
|
||||||
|
__RCSID("$NetBSD: expr.y,v 1.38 2012/03/15 02:02:20 joerg Exp $");
|
||||||
|
#endif /* not lint */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <regex.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static const char * const *av;
|
||||||
|
|
||||||
|
static void yyerror(const char *, ...) __dead;
|
||||||
|
static int yylex(void);
|
||||||
|
static int is_zero_or_null(const char *);
|
||||||
|
static int is_integer(const char *);
|
||||||
|
static int64_t perform_arith_op(const char *, const char *, const char *);
|
||||||
|
|
||||||
|
int main(int, const char * const *);
|
||||||
|
|
||||||
|
#define YYSTYPE const char *
|
||||||
|
|
||||||
|
%}
|
||||||
|
%token STRING
|
||||||
|
%left SPEC_OR
|
||||||
|
%left SPEC_AND
|
||||||
|
%left COMPARE
|
||||||
|
%left ADD_SUB_OPERATOR
|
||||||
|
%left MUL_DIV_MOD_OPERATOR
|
||||||
|
%left SPEC_REG
|
||||||
|
%left LENGTH
|
||||||
|
%left LEFT_PARENT RIGHT_PARENT
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
exp: expr = {
|
||||||
|
(void) printf("%s\n", $1);
|
||||||
|
return (is_zero_or_null($1));
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
expr: item { $$ = $1; }
|
||||||
|
| expr SPEC_OR expr = {
|
||||||
|
/*
|
||||||
|
* Return evaluation of first expression if it is neither
|
||||||
|
* an empty string nor zero; otherwise, returns the evaluation
|
||||||
|
* of second expression.
|
||||||
|
*/
|
||||||
|
if (!is_zero_or_null($1))
|
||||||
|
$$ = $1;
|
||||||
|
else
|
||||||
|
$$ = $3;
|
||||||
|
}
|
||||||
|
| expr SPEC_AND expr = {
|
||||||
|
/*
|
||||||
|
* Returns the evaluation of first expr if neither expression
|
||||||
|
* evaluates to an empty string or zero; otherwise, returns
|
||||||
|
* zero.
|
||||||
|
*/
|
||||||
|
if (!is_zero_or_null($1) && !is_zero_or_null($3))
|
||||||
|
$$ = $1;
|
||||||
|
else
|
||||||
|
$$ = "0";
|
||||||
|
}
|
||||||
|
| expr SPEC_REG expr = {
|
||||||
|
/*
|
||||||
|
* The ``:'' operator matches first expr against the second,
|
||||||
|
* which must be a regular expression.
|
||||||
|
*/
|
||||||
|
regex_t rp;
|
||||||
|
regmatch_t rm[2];
|
||||||
|
int eval;
|
||||||
|
|
||||||
|
/* compile regular expression */
|
||||||
|
if ((eval = regcomp(&rp, $3, REG_BASIC)) != 0) {
|
||||||
|
char errbuf[256];
|
||||||
|
(void)regerror(eval, &rp, errbuf, sizeof(errbuf));
|
||||||
|
yyerror("%s", errbuf);
|
||||||
|
/* NOT REACHED */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compare string against pattern -- remember that patterns
|
||||||
|
are anchored to the beginning of the line */
|
||||||
|
if (regexec(&rp, $1, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
|
||||||
|
char *val;
|
||||||
|
if (rm[1].rm_so >= 0) {
|
||||||
|
(void) asprintf(&val, "%.*s",
|
||||||
|
(int) (rm[1].rm_eo - rm[1].rm_so),
|
||||||
|
$1 + rm[1].rm_so);
|
||||||
|
} else {
|
||||||
|
(void) asprintf(&val, "%d",
|
||||||
|
(int)(rm[0].rm_eo - rm[0].rm_so));
|
||||||
|
}
|
||||||
|
if (val == NULL)
|
||||||
|
err(1, NULL);
|
||||||
|
$$ = val;
|
||||||
|
} else {
|
||||||
|
if (rp.re_nsub == 0) {
|
||||||
|
$$ = "0";
|
||||||
|
} else {
|
||||||
|
$$ = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
| expr ADD_SUB_OPERATOR expr = {
|
||||||
|
/* Returns the results of addition, subtraction */
|
||||||
|
char *val;
|
||||||
|
int64_t res;
|
||||||
|
|
||||||
|
res = perform_arith_op($1, $2, $3);
|
||||||
|
(void) asprintf(&val, "%lld", (long long int) res);
|
||||||
|
if (val == NULL)
|
||||||
|
err(1, NULL);
|
||||||
|
$$ = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
| expr MUL_DIV_MOD_OPERATOR expr = {
|
||||||
|
/*
|
||||||
|
* Returns the results of multiply, divide or remainder of
|
||||||
|
* numeric-valued arguments.
|
||||||
|
*/
|
||||||
|
char *val;
|
||||||
|
int64_t res;
|
||||||
|
|
||||||
|
res = perform_arith_op($1, $2, $3);
|
||||||
|
(void) asprintf(&val, "%lld", (long long int) res);
|
||||||
|
if (val == NULL)
|
||||||
|
err(1, NULL);
|
||||||
|
$$ = val;
|
||||||
|
|
||||||
|
}
|
||||||
|
| expr COMPARE expr = {
|
||||||
|
/*
|
||||||
|
* Returns the results of integer comparison if both arguments
|
||||||
|
* are integers; otherwise, returns the results of string
|
||||||
|
* comparison using the locale-specific collation sequence.
|
||||||
|
* The result of each comparison is 1 if the specified relation
|
||||||
|
* is true, or 0 if the relation is false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int64_t l, r;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Slight hack to avoid differences in the compare code
|
||||||
|
* between string and numeric compare.
|
||||||
|
*/
|
||||||
|
if (is_integer($1) && is_integer($3)) {
|
||||||
|
/* numeric comparison */
|
||||||
|
l = strtoll($1, NULL, 10);
|
||||||
|
r = strtoll($3, NULL, 10);
|
||||||
|
} else {
|
||||||
|
/* string comparison */
|
||||||
|
l = strcoll($1, $3);
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($2[0]) {
|
||||||
|
case '=': /* equal */
|
||||||
|
res = (l == r);
|
||||||
|
break;
|
||||||
|
case '>': /* greater or greater-equal */
|
||||||
|
if ($2[1] == '=')
|
||||||
|
res = (l >= r);
|
||||||
|
else
|
||||||
|
res = (l > r);
|
||||||
|
break;
|
||||||
|
case '<': /* lower or lower-equal */
|
||||||
|
if ($2[1] == '=')
|
||||||
|
res = (l <= r);
|
||||||
|
else
|
||||||
|
res = (l < r);
|
||||||
|
break;
|
||||||
|
case '!': /* not equal */
|
||||||
|
/* the check if this is != was done in yylex() */
|
||||||
|
res = (l != r);
|
||||||
|
}
|
||||||
|
|
||||||
|
$$ = (res) ? "1" : "0";
|
||||||
|
|
||||||
|
}
|
||||||
|
| LEFT_PARENT expr RIGHT_PARENT { $$ = $2; }
|
||||||
|
| LENGTH expr {
|
||||||
|
/*
|
||||||
|
* Return length of 'expr' in bytes.
|
||||||
|
*/
|
||||||
|
char *ln;
|
||||||
|
|
||||||
|
asprintf(&ln, "%ld", (long) strlen($2));
|
||||||
|
if (ln == NULL)
|
||||||
|
err(1, NULL);
|
||||||
|
$$ = ln;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
item: STRING
|
||||||
|
| ADD_SUB_OPERATOR
|
||||||
|
| MUL_DIV_MOD_OPERATOR
|
||||||
|
| COMPARE
|
||||||
|
| SPEC_OR
|
||||||
|
| SPEC_AND
|
||||||
|
| SPEC_REG
|
||||||
|
| LENGTH
|
||||||
|
;
|
||||||
|
%%
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns 1 if the string is empty or contains only numeric zero.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
is_zero_or_null(const char *str)
|
||||||
|
{
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
return str[0] == '\0'
|
||||||
|
|| ( strtoll(str, &endptr, 10) == 0LL
|
||||||
|
&& endptr[0] == '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns 1 if the string is an integer.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
is_integer(const char *str)
|
||||||
|
{
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
(void) strtoll(str, &endptr, 10);
|
||||||
|
/* note we treat empty string as valid number */
|
||||||
|
return (endptr[0] == '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t
|
||||||
|
perform_arith_op(const char *left, const char *op, const char *right)
|
||||||
|
{
|
||||||
|
int64_t res, sign, l, r;
|
||||||
|
u_int64_t temp;
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
|
||||||
|
if (!is_integer(left)) {
|
||||||
|
yyerror("non-integer argument '%s'", left);
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
if (!is_integer(right)) {
|
||||||
|
yyerror("non-integer argument '%s'", right);
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
l = strtoll(left, NULL, 10);
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
yyerror("value '%s' is %s is %lld", left,
|
||||||
|
(l > 0) ? "too big, maximum" : "too small, minimum",
|
||||||
|
(l > 0) ? LLONG_MAX : LLONG_MIN);
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = strtoll(right, NULL, 10);
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
yyerror("value '%s' is %s is %lld", right,
|
||||||
|
(l > 0) ? "too big, maximum" : "too small, minimum",
|
||||||
|
(l > 0) ? LLONG_MAX : LLONG_MIN);
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(op[0]) {
|
||||||
|
case '+':
|
||||||
|
/*
|
||||||
|
* Do the op into an unsigned to avoid overflow and then cast
|
||||||
|
* back to check the resulting signage.
|
||||||
|
*/
|
||||||
|
temp = l + r;
|
||||||
|
res = (int64_t) temp;
|
||||||
|
/* very simplistic check for over-& underflow */
|
||||||
|
if ((res < 0 && l > 0 && r > 0)
|
||||||
|
|| (res > 0 && l < 0 && r < 0))
|
||||||
|
yyerror("integer overflow or underflow occurred for "
|
||||||
|
"operation '%s %s %s'", left, op, right);
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
/*
|
||||||
|
* Do the op into an unsigned to avoid overflow and then cast
|
||||||
|
* back to check the resulting signage.
|
||||||
|
*/
|
||||||
|
temp = l - r;
|
||||||
|
res = (int64_t) temp;
|
||||||
|
/* very simplistic check for over-& underflow */
|
||||||
|
if ((res < 0 && l > 0 && l > r)
|
||||||
|
|| (res > 0 && l < 0 && l < r) )
|
||||||
|
yyerror("integer overflow or underflow occurred for "
|
||||||
|
"operation '%s %s %s'", left, op, right);
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
if (r == 0)
|
||||||
|
yyerror("second argument to '%s' must not be zero", op);
|
||||||
|
res = l / r;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
if (r == 0)
|
||||||
|
yyerror("second argument to '%s' must not be zero", op);
|
||||||
|
res = l % r;
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
/* shortcut */
|
||||||
|
if ((l == 0) || (r == 0)) {
|
||||||
|
res = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sign = 1;
|
||||||
|
if (l < 0)
|
||||||
|
sign *= -1;
|
||||||
|
if (r < 0)
|
||||||
|
sign *= -1;
|
||||||
|
|
||||||
|
res = l * r;
|
||||||
|
/*
|
||||||
|
* XXX: not the most portable but works on anything with 2's
|
||||||
|
* complement arithmetic. If the signs don't match or the
|
||||||
|
* result was 0 on 2's complement this overflowed.
|
||||||
|
*/
|
||||||
|
if ((res < 0 && sign > 0) || (res > 0 && sign < 0) ||
|
||||||
|
(res == 0))
|
||||||
|
yyerror("integer overflow or underflow occurred for "
|
||||||
|
"operation '%s %s %s'", left, op, right);
|
||||||
|
/* NOTREACHED */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *x = "|&=<>+-*/%:()";
|
||||||
|
static const int x_token[] = {
|
||||||
|
SPEC_OR, SPEC_AND, COMPARE, COMPARE, COMPARE, ADD_SUB_OPERATOR,
|
||||||
|
ADD_SUB_OPERATOR, MUL_DIV_MOD_OPERATOR, MUL_DIV_MOD_OPERATOR,
|
||||||
|
MUL_DIV_MOD_OPERATOR, SPEC_REG, LEFT_PARENT, RIGHT_PARENT
|
||||||
|
};
|
||||||
|
|
||||||
|
static int handle_ddash = 1;
|
||||||
|
|
||||||
|
int
|
||||||
|
yylex(void)
|
||||||
|
{
|
||||||
|
const char *p = *av++;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
retval = 0;
|
||||||
|
else if (p[1] == '\0') {
|
||||||
|
const char *w = strchr(x, p[0]);
|
||||||
|
if (w) {
|
||||||
|
retval = x_token[w-x];
|
||||||
|
} else {
|
||||||
|
retval = STRING;
|
||||||
|
}
|
||||||
|
} else if (p[1] == '=' && p[2] == '\0'
|
||||||
|
&& (p[0] == '>' || p[0] == '<' || p[0] == '!'))
|
||||||
|
retval = COMPARE;
|
||||||
|
else if (handle_ddash && p[0] == '-' && p[1] == '-' && p[2] == '\0') {
|
||||||
|
/* ignore "--" if passed as first argument and isn't followed
|
||||||
|
* by another STRING */
|
||||||
|
retval = yylex();
|
||||||
|
if (retval != STRING && retval != LEFT_PARENT
|
||||||
|
&& retval != RIGHT_PARENT) {
|
||||||
|
/* is not followed by string or parenthesis, use as
|
||||||
|
* STRING */
|
||||||
|
retval = STRING;
|
||||||
|
av--; /* was increased in call to yylex() above */
|
||||||
|
p = "--";
|
||||||
|
} else {
|
||||||
|
/* "--" is to be ignored */
|
||||||
|
p = yylval;
|
||||||
|
}
|
||||||
|
} else if (strcmp(p, "length") == 0)
|
||||||
|
retval = LENGTH;
|
||||||
|
else
|
||||||
|
retval = STRING;
|
||||||
|
|
||||||
|
handle_ddash = 0;
|
||||||
|
yylval = p;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print error message and exit with error 2 (syntax error).
|
||||||
|
*/
|
||||||
|
static __printflike(1, 2) void
|
||||||
|
yyerror(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list arg;
|
||||||
|
|
||||||
|
va_start(arg, fmt);
|
||||||
|
verrx(2, fmt, arg);
|
||||||
|
va_end(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, const char * const *argv)
|
||||||
|
{
|
||||||
|
setprogname(argv[0]);
|
||||||
|
(void)setlocale(LC_ALL, "");
|
||||||
|
|
||||||
|
if (argc == 1) {
|
||||||
|
(void)fprintf(stderr, "usage: %s expression\n",
|
||||||
|
getprogname());
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
av = argv + 1;
|
||||||
|
|
||||||
|
exit(yyparse());
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ MAN= ash.1 at.1 banner.1 basename.1 \
|
||||||
cp.1 crc.1 crontab.1 dd.1 \
|
cp.1 crc.1 crontab.1 dd.1 \
|
||||||
df.1 dhrystone.1 dosdir.1 dosread.1 doswrite.1 \
|
df.1 dhrystone.1 dosdir.1 dosread.1 doswrite.1 \
|
||||||
dumpcore.1 eject.1 \
|
dumpcore.1 eject.1 \
|
||||||
env.1 expand.1 expr.1 factor.1 \
|
env.1 expand.1 factor.1 \
|
||||||
finger.1 flexdoc.1 fold.1 format.1 fortune.1 \
|
finger.1 flexdoc.1 fold.1 format.1 fortune.1 \
|
||||||
fsck.mfs.1 head.1 host.1 hostaddr.1 ifdef.1 \
|
fsck.mfs.1 head.1 host.1 hostaddr.1 ifdef.1 \
|
||||||
isodir.1 isoinfo.1 isoread.1 kill.1 \
|
isodir.1 isoinfo.1 isoread.1 kill.1 \
|
||||||
|
@ -54,8 +54,6 @@ MLINKS += compress.1 uncompress.1
|
||||||
MLINKS += cp.1 mv.1
|
MLINKS += cp.1 mv.1
|
||||||
MLINKS += cp.1 ln.1
|
MLINKS += cp.1 ln.1
|
||||||
MLINKS += cp.1 cpdir.1
|
MLINKS += cp.1 cpdir.1
|
||||||
MLINKS += expr.1 test.1
|
|
||||||
MLINKS += expr.1 [.1
|
|
||||||
MLINKS += svc.1 ci.1
|
MLINKS += svc.1 ci.1
|
||||||
MLINKS += svc.1 co.1
|
MLINKS += svc.1 co.1
|
||||||
|
|
||||||
|
|
245
man/man1/expr.1
245
man/man1/expr.1
|
@ -1,245 +0,0 @@
|
||||||
.TH EXPR 1
|
|
||||||
.SH NAME \" Copyright (C) 1989 by Kenneth Almquist.
|
|
||||||
expr, test, [ \- evaluate expressions
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.B expr
|
|
||||||
.I expression
|
|
||||||
.br
|
|
||||||
.B test
|
|
||||||
.I expression
|
|
||||||
.br
|
|
||||||
.B [
|
|
||||||
.I expression
|
|
||||||
.B ]
|
|
||||||
.SH DESCRIPTION
|
|
||||||
.B Expr
|
|
||||||
evaluates the expression and prints the result.
|
|
||||||
.B Test
|
|
||||||
evaluates the expression without printing the result.
|
|
||||||
The ``[''
|
|
||||||
command is a synonym for
|
|
||||||
.BR test ;
|
|
||||||
when invoked under this name
|
|
||||||
the last argument to
|
|
||||||
.B expr
|
|
||||||
must be a ``]'', which is deleted and not considered part of the expression.
|
|
||||||
.PP
|
|
||||||
Three data types may occur in the
|
|
||||||
.IR expression :
|
|
||||||
string, integer, and boolean.
|
|
||||||
The rules for conversion are as follows:
|
|
||||||
.sp
|
|
||||||
.nr i 2
|
|
||||||
.ta \nii
|
|
||||||
.in +\nii
|
|
||||||
.ti -\nii
|
|
||||||
\fIstring\fR\->\fIinteger\fR Done via
|
|
||||||
.BR atoi (3).
|
|
||||||
.ti -\nii
|
|
||||||
\fIinteger\fR\->\fIstring\fR Convert to decimal representation.
|
|
||||||
.ti -\nii
|
|
||||||
\fIstring\fR\->\fIboolean\fR "" \-> false, everything else to true.
|
|
||||||
.ti -\nii
|
|
||||||
\fIboolean\fR\->\fIstring\fR false \-> "", true \-> "true".
|
|
||||||
.ti -\nii
|
|
||||||
\fIinteger\fR\->\fIboolean\fR 0 \-> false, everything else to true.
|
|
||||||
.ti -\nii
|
|
||||||
\fIboolean\fR\->\fIinteger\fR false \-> 0, true \-> 1.
|
|
||||||
.in -\nii
|
|
||||||
.PP
|
|
||||||
Any argument to
|
|
||||||
.B expr
|
|
||||||
which is not a legal operator is treated as a string operand of type
|
|
||||||
.BR string .
|
|
||||||
.PP
|
|
||||||
As a special case, if
|
|
||||||
.I expression
|
|
||||||
is omitted, the result is false.
|
|
||||||
.PP
|
|
||||||
We now list the operators. The syntax
|
|
||||||
.sp
|
|
||||||
.ti +8
|
|
||||||
\fIinteger\fB op \fIinteger\fR \-> \fIboolean\fB (3)\fR
|
|
||||||
.sp
|
|
||||||
means that \fBop\fR is a binary operator which takes operands of type
|
|
||||||
\fIinteger\fR and produces a result of type \fIboolean\fR.
|
|
||||||
The ``(3)'' means that the priority of \fBop\fR is 3.
|
|
||||||
Operands are automatically converted to the appropriate type. The type
|
|
||||||
\fIany\fR is used for operator that take operands of any type.
|
|
||||||
.nr p 1
|
|
||||||
.de b
|
|
||||||
.TP 0.5i
|
|
||||||
\fI\\$1\fB \\$2 \fI\\$3\fR \-> \\fI\\$4\\fR (\\np)
|
|
||||||
..
|
|
||||||
.de u
|
|
||||||
.TP 0.5i
|
|
||||||
\\$1 \fI\\$2\fR \-> \\fI\\$3\\fR (\\np)
|
|
||||||
..
|
|
||||||
.b any \-o any any
|
|
||||||
Returns the value of the left hand operand if the left hand operand
|
|
||||||
would yield
|
|
||||||
.B true
|
|
||||||
if converted to type
|
|
||||||
.BR boolean ,
|
|
||||||
and the value of the right hand operand otherwise.
|
|
||||||
The right hand operand is evaluated only if necessary.
|
|
||||||
``|'' is a synonym for ``\-o''.
|
|
||||||
.nr p \np+1
|
|
||||||
.b any -a any any
|
|
||||||
Returns the value of the left hand operand if the left hand operand
|
|
||||||
would yield
|
|
||||||
.B false
|
|
||||||
if converted to type
|
|
||||||
.BR boolean ,
|
|
||||||
and the value of the right hand operand otherwise.
|
|
||||||
The right hand operand is evaluated only if necessary.
|
|
||||||
``&'' is a synonym for ``\-a''.
|
|
||||||
.nr p \np+1
|
|
||||||
.u ! boolean boolean
|
|
||||||
Returns true if the operand is false, and false if the operand is true.
|
|
||||||
.nr p \np+1
|
|
||||||
.b string = string boolean
|
|
||||||
True if the two strings are equal.
|
|
||||||
.b string != string boolean
|
|
||||||
True if the two strings are not equal.
|
|
||||||
.b integer \-eq integer boolean
|
|
||||||
True if the two operands are equal.
|
|
||||||
.b integer \-ne integer boolean
|
|
||||||
True if the two operands are not equal.
|
|
||||||
.b integer \-gt integer boolean
|
|
||||||
True if the first operand is greater than the second one.
|
|
||||||
.b integer \-lt integer boolean
|
|
||||||
True if the first operand is less than the second one.
|
|
||||||
.b integer \-ge integer boolean
|
|
||||||
True if the first operand is greater than or equal to the second one.
|
|
||||||
.b integer \-le integer boolean
|
|
||||||
True if the first operand is less than or equal to the second one.
|
|
||||||
.nr p \np+1
|
|
||||||
.b integer + integer integer
|
|
||||||
Add two integers.
|
|
||||||
.b integer \- integer integer
|
|
||||||
Subtract two integers.
|
|
||||||
.nr p \np+1
|
|
||||||
.b integer * integer integer
|
|
||||||
Multiply two integers. ``*'' is special to the shell, so you generally
|
|
||||||
have to write this operator as ``\e*''.
|
|
||||||
.b integer / integer integer
|
|
||||||
Divide two integers.
|
|
||||||
.b integer % integer integer
|
|
||||||
Returns the remainder when the first operand is divided by the second one.
|
|
||||||
.nr p \np+1
|
|
||||||
.b string : string "integer or string"
|
|
||||||
The second operand is interpreted as a regular expression (as in the
|
|
||||||
System V
|
|
||||||
.B ed
|
|
||||||
program).
|
|
||||||
This operator attempts to match part (or all) of the first operand
|
|
||||||
with the regular expression. The match must start at the beginning of
|
|
||||||
the first operand.
|
|
||||||
If the regular expression contains \e( \e) pairs, then the result
|
|
||||||
of this operator is the string which is matched by the regular expression
|
|
||||||
between these pairs, or the null string if no match occurred. Otherwise,
|
|
||||||
the result is the number of characters matched by the regular expression,
|
|
||||||
or zero if no no match occurred.
|
|
||||||
.nr p \np+1
|
|
||||||
.u \-n string integer
|
|
||||||
Returns the number of characters in the string.
|
|
||||||
.u \-z string boolean
|
|
||||||
Returns true if the string contains zero characters.
|
|
||||||
.u \-t integer boolean
|
|
||||||
Returns true if the specified file descriptor is associated with a tty.
|
|
||||||
.PP
|
|
||||||
The remaining operators all deal with files. Except as noted, they return
|
|
||||||
false if the
|
|
||||||
specified file does not exist. The ones dealing with permission use
|
|
||||||
the effective user and group ids of the shell.
|
|
||||||
.u \-e string boolean
|
|
||||||
True the file exists.
|
|
||||||
.u \-r string boolean
|
|
||||||
True if you have read permission on the file.
|
|
||||||
.u \-w string boolean
|
|
||||||
True if you have write permission on the file.
|
|
||||||
.u \-x string boolean
|
|
||||||
True if you have execute permission on the file.
|
|
||||||
.u \-f string boolean
|
|
||||||
True if the file is a regular file.
|
|
||||||
.u \-d string boolean
|
|
||||||
True if the file is a directory.
|
|
||||||
.u \-c string boolean
|
|
||||||
True if the file is a character special file.
|
|
||||||
.u \-b string boolean
|
|
||||||
True if the file is a block special file.
|
|
||||||
.u \-p string boolean
|
|
||||||
True if the file is a named pipe (i.e. a fifo).
|
|
||||||
.u \-u string boolean
|
|
||||||
True if the file is setuid.
|
|
||||||
.u \-g string boolean
|
|
||||||
True if the file is setgid.
|
|
||||||
.u \-k string boolean
|
|
||||||
True if the file has the sticky bit set.
|
|
||||||
.u \-s string "integer or boolean"
|
|
||||||
Returns the size of the file, or 0 if the file does not exist.
|
|
||||||
.u \-h string boolean
|
|
||||||
True if the file is a symlink. This is the only file test operator that
|
|
||||||
does not follow symlinks, all others do. So ``\-d'' and ``\-h''
|
|
||||||
are both true on a symlink pointing to a directory.
|
|
||||||
``\-L'' is a synonym for ``\-h''.
|
|
||||||
.SH "EXIT CODE"
|
|
||||||
0 if the result of
|
|
||||||
.I expression
|
|
||||||
would be
|
|
||||||
.B true
|
|
||||||
if the result were converted to
|
|
||||||
.BR boolean .
|
|
||||||
.br
|
|
||||||
1 if the result of
|
|
||||||
.I expression
|
|
||||||
would be
|
|
||||||
.B false
|
|
||||||
if the result were converted to
|
|
||||||
.BR boolean .
|
|
||||||
.br
|
|
||||||
2 if
|
|
||||||
.I expression
|
|
||||||
is syntactically incorrect.
|
|
||||||
.SH EXAMPLES
|
|
||||||
.TP 0.5i
|
|
||||||
filesize=`expr \-s file`
|
|
||||||
Sets the shell variable
|
|
||||||
.I filesize
|
|
||||||
to the size of
|
|
||||||
.IR file .
|
|
||||||
.TP 0.5i
|
|
||||||
if [ \-s file ]; then command; fi
|
|
||||||
Execute
|
|
||||||
.I command
|
|
||||||
if
|
|
||||||
.I file
|
|
||||||
exists and is not empty.
|
|
||||||
.TP 0.5i
|
|
||||||
x=`expr "$x" : '.\\{4\\}\\(.\\{0,3\\}\\)'`
|
|
||||||
Sets
|
|
||||||
.I x
|
|
||||||
to the substring of
|
|
||||||
.I x
|
|
||||||
beginning after the fourth character of
|
|
||||||
.I x
|
|
||||||
and continuing for three characters or until the end of the string,
|
|
||||||
whichever comes first.
|
|
||||||
.TP 0.5i
|
|
||||||
x=`expr X"$x" : X'.\\{4\\}\\(.\\{0,3\\}\\)'`
|
|
||||||
This example is the same as the previous one, but it uses a leading
|
|
||||||
``X'' to make things work when the value of
|
|
||||||
.I x
|
|
||||||
looks like an operator.
|
|
||||||
.SH BUGS
|
|
||||||
The relational operators of the System V
|
|
||||||
.B expr
|
|
||||||
command are not implemented.
|
|
||||||
.PP
|
|
||||||
Certain features of this version of
|
|
||||||
.B expr
|
|
||||||
are not present in System V, so care should be used when writing
|
|
||||||
portable code.
|
|
||||||
.SH COPYRIGHT
|
|
||||||
Kenneth Almquist.
|
|
|
@ -10,6 +10,7 @@
|
||||||
2011/08/27 12:55:09,bin/date
|
2011/08/27 12:55:09,bin/date
|
||||||
2012/10/17 12:00:00,bin/echo
|
2012/10/17 12:00:00,bin/echo
|
||||||
2012/01/16 18:47:57,bin/ed
|
2012/01/16 18:47:57,bin/ed
|
||||||
|
2012/10/17 12:00:00,bin/expr
|
||||||
2012/10/17 12:00:00,bin/Makefile
|
2012/10/17 12:00:00,bin/Makefile
|
||||||
2012/10/17 12:00:00,bin/Makefile.inc
|
2012/10/17 12:00:00,bin/Makefile.inc
|
||||||
2008/07/20 00:52:40,bin/mkdir
|
2008/07/20 00:52:40,bin/mkdir
|
||||||
|
|
Loading…
Reference in a new issue