mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-02-13 11:42:21 +00:00
POSIX compliance fix: make 'times' a proper builtin
As of this commit, the 'times' command is a POSIX-compliant special builtin command instead of an alias that doesn't produce the required output. It displays the accumulated user and system CPU times, one line with the times used by the shell and another with those used by all of the shell's child processes. https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_27 This was originally written by Kurtis Rader and is now backported and tweaked from the abandoned ksh2020 branch. I chose an earlier and simpler version[*1] that uses times(3), with a precision of hundredths of seconds, so it outputs the same precision as mksh and zsh. Rader later wrote another version[*2] that uses getrusage(2), giving it the same millisecond precision as bash. But that required adding a feature test and a fallback to the old version, which is non-trivial in the old INIT/iffe system. This simpler version is enough to gain POSIX compliance and I think it will do very nicely in this stable bugfix branch. [*1] https://github.com/att/ast/pull/1332 [*2] https://github.com/att/ast/commit/038045f6 src/cmd/ksh93/bltins/misc.c - Add b_times() function for 'times' builtin. - Note we include <times.h>, not <sys/times.h>, so that we use the AST feature-tested version with fallback on systems that need it. src/cmd/ksh93/data/aliases.c: - Remove times='{ { time;} 2>&1;}' builtin alias. src/cmd/ksh93/data/builtins.c, src/cmd/ksh93/include/builtins.h: - Add entry for 'times' special builtin. - Add --help/--man info for same. src/cmd/ksh93/sh.1: - Update manual page. src/cmd/ksh93/tests/builtins.sh: - Add a couple of simple regression tests. (cherry picked from commit ebf71e619eb298ec1cf6b81d1828fa7cdf6e9203)
This commit is contained in:
parent
3a3776f1df
commit
65d363fd34
8 changed files with 96 additions and 5 deletions
8
NEWS
8
NEWS
|
@ -4,6 +4,14 @@ For full details, see the git log at:
|
||||||
|
|
||||||
Any uppercase BUG_* names are modernish shell bug IDs.
|
Any uppercase BUG_* names are modernish shell bug IDs.
|
||||||
|
|
||||||
|
2020-06-06:
|
||||||
|
|
||||||
|
- The 'times' command is now a builtin command that conforms to POSIX
|
||||||
|
instead of an alias for the 'time' command. It displays the accumulated
|
||||||
|
user and system CPU times, one line with the times used by the shell and
|
||||||
|
another with those used by all of the shell's child processes.
|
||||||
|
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_27
|
||||||
|
|
||||||
2020-06-05:
|
2020-06-05:
|
||||||
|
|
||||||
- Fix a bug that caused special variables such as PATH, LANG, LC_ALL,
|
- Fix a bug that caused special variables such as PATH, LANG, LC_ALL,
|
||||||
|
|
|
@ -36,6 +36,9 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <times.h>
|
||||||
|
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "variables.h"
|
#include "variables.h"
|
||||||
#include "shnodes.h"
|
#include "shnodes.h"
|
||||||
|
@ -461,6 +464,61 @@ int b_jobs(register int n,char *argv[],Shbltin_t *context)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* times command
|
||||||
|
*/
|
||||||
|
int b_times(int argc, char *argv[], Shbltin_t *context)
|
||||||
|
{
|
||||||
|
Shell_t *shp = context->shp;
|
||||||
|
const char *cmd = argv[0];
|
||||||
|
struct tms cpu_times;
|
||||||
|
clock_t rv;
|
||||||
|
double utime, stime, utime_min, utime_sec, stime_min, stime_sec;
|
||||||
|
|
||||||
|
while (argc = optget(argv, sh_opttimes)) switch (argc)
|
||||||
|
{
|
||||||
|
case ':':
|
||||||
|
errormsg(SH_DICT, 2, "%s", opt_info.arg);
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
errormsg(SH_DICT, ERROR_usage(0), "%s", opt_info.arg);
|
||||||
|
return(2);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (error_info.errors)
|
||||||
|
errormsg(SH_DICT, ERROR_usage(2), "%s", optusage((char*)0));
|
||||||
|
|
||||||
|
argv += opt_info.index;
|
||||||
|
if (*argv)
|
||||||
|
errormsg(SH_DICT, ERROR_exit(3), e_badsyntax);
|
||||||
|
|
||||||
|
rv = times(&cpu_times);
|
||||||
|
if (rv == (clock_t)-1)
|
||||||
|
errormsg(SH_DICT, ERROR_exit(2), "times(3) failed: errno %d: %s",
|
||||||
|
errno, strerror(errno));
|
||||||
|
|
||||||
|
/* First line: user and system times used by the shell */
|
||||||
|
utime = (double)cpu_times.tms_utime / shp->gd->lim.clk_tck;
|
||||||
|
utime_min = floor(utime / 60);
|
||||||
|
utime_sec = utime - utime_min;
|
||||||
|
stime = (double)cpu_times.tms_stime / shp->gd->lim.clk_tck;
|
||||||
|
stime_min = floor(stime / 60);
|
||||||
|
stime_sec = stime - stime_min;
|
||||||
|
sfprintf(sfstdout, "%dm%.2fs %dm%.2fs\n", (int)utime_min, utime_sec, (int)stime_min, stime_sec);
|
||||||
|
|
||||||
|
/* Second line: same for the shell's child processes */
|
||||||
|
utime = (double)cpu_times.tms_cutime / shp->gd->lim.clk_tck;
|
||||||
|
utime_min = floor(utime / 60);
|
||||||
|
utime_sec = utime - utime_min;
|
||||||
|
stime = (double)cpu_times.tms_cstime / shp->gd->lim.clk_tck;
|
||||||
|
stime_min = floor(stime / 60);
|
||||||
|
stime_sec = stime - stime_min;
|
||||||
|
sfprintf(sfstdout, "%dm%.2fs %dm%.2fs\n", (int)utime_min, utime_sec, (int)stime_min, stime_sec);
|
||||||
|
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef _cmd_universe
|
#ifdef _cmd_universe
|
||||||
/*
|
/*
|
||||||
* There are several universe styles that are masked by the getuniv(),
|
* There are several universe styles that are masked by the getuniv(),
|
||||||
|
|
|
@ -50,7 +50,6 @@ const struct shtable2 shtab_aliases[] =
|
||||||
"stop", NV_NOFREE, "kill -s STOP",
|
"stop", NV_NOFREE, "kill -s STOP",
|
||||||
"suspend", NV_NOFREE, "kill -s STOP $$",
|
"suspend", NV_NOFREE, "kill -s STOP $$",
|
||||||
#endif /*SIGTSTP */
|
#endif /*SIGTSTP */
|
||||||
"times", NV_NOFREE, "{ { time;} 2>&1;}",
|
|
||||||
"type", NV_NOFREE, "whence -v",
|
"type", NV_NOFREE, "whence -v",
|
||||||
"", 0, (char*)0
|
"", 0, (char*)0
|
||||||
};
|
};
|
||||||
|
|
|
@ -115,6 +115,7 @@ const struct shtable3 shtab_builtins[] =
|
||||||
"read", NV_BLTIN|BLT_ENV, bltin(read),
|
"read", NV_BLTIN|BLT_ENV, bltin(read),
|
||||||
"sleep", NV_BLTIN, bltin(sleep),
|
"sleep", NV_BLTIN, bltin(sleep),
|
||||||
"alarm", NV_BLTIN, bltin(alarm),
|
"alarm", NV_BLTIN, bltin(alarm),
|
||||||
|
"times", NV_BLTIN|BLT_SPC, bltin(times),
|
||||||
"ulimit", NV_BLTIN|BLT_ENV, bltin(ulimit),
|
"ulimit", NV_BLTIN|BLT_ENV, bltin(ulimit),
|
||||||
"umask", NV_BLTIN|BLT_ENV, bltin(umask),
|
"umask", NV_BLTIN|BLT_ENV, bltin(umask),
|
||||||
#ifdef _cmd_universe
|
#ifdef _cmd_universe
|
||||||
|
@ -1761,6 +1762,15 @@ USAGE_LICENSE
|
||||||
"[+SEE ALSO?\bulimit\b(2), \bgetrlimit\b(2)]"
|
"[+SEE ALSO?\bulimit\b(2), \bgetrlimit\b(2)]"
|
||||||
;
|
;
|
||||||
|
|
||||||
|
const char sh_opttimes[] =
|
||||||
|
"[-1c?@(#)$Id: times (ksh community) 2020-06-06 $\n]"
|
||||||
|
"[+NAME?times - display CPU usage by the shell and child processes]"
|
||||||
|
"[+DESCRIPTION?\btimes\b displays the accumulated user and system CPU times, "
|
||||||
|
"one line with the times used by the shell and another with those used by "
|
||||||
|
"all of the shell's child processes. No options are supported.]"
|
||||||
|
"[+SEE ALSO?\btime\b(1)]"
|
||||||
|
;
|
||||||
|
|
||||||
const char sh_optumask[] =
|
const char sh_optumask[] =
|
||||||
"[-1c?\n@(#)$Id: umask (AT&T Research) 1999-04-07 $\n]"
|
"[-1c?\n@(#)$Id: umask (AT&T Research) 1999-04-07 $\n]"
|
||||||
USAGE_LICENSE
|
USAGE_LICENSE
|
||||||
|
|
|
@ -105,6 +105,7 @@ extern int b_printf(int, char*[],Shbltin_t*);
|
||||||
extern int b_pwd(int, char*[],Shbltin_t*);
|
extern int b_pwd(int, char*[],Shbltin_t*);
|
||||||
extern int b_sleep(int, char*[],Shbltin_t*);
|
extern int b_sleep(int, char*[],Shbltin_t*);
|
||||||
extern int b_test(int, char*[],Shbltin_t*);
|
extern int b_test(int, char*[],Shbltin_t*);
|
||||||
|
extern int b_times(int, char*[],Shbltin_t*);
|
||||||
#if !SHOPT_ECHOPRINT
|
#if !SHOPT_ECHOPRINT
|
||||||
extern int B_echo(int, char*[],Shbltin_t*);
|
extern int B_echo(int, char*[],Shbltin_t*);
|
||||||
#endif /* SHOPT_ECHOPRINT */
|
#endif /* SHOPT_ECHOPRINT */
|
||||||
|
@ -197,6 +198,7 @@ extern const char sh_optunset[];
|
||||||
#endif /* SHOPT_FS_3D */
|
#endif /* SHOPT_FS_3D */
|
||||||
extern const char sh_optwhence[];
|
extern const char sh_optwhence[];
|
||||||
#endif /* SYSDECLARE */
|
#endif /* SYSDECLARE */
|
||||||
|
extern const char sh_opttimes[];
|
||||||
|
|
||||||
extern const char e_dict[];
|
extern const char e_dict[];
|
||||||
|
|
||||||
|
|
|
@ -17,4 +17,4 @@
|
||||||
* David Korn <dgk@research.att.com> *
|
* David Korn <dgk@research.att.com> *
|
||||||
* *
|
* *
|
||||||
***********************************************************************/
|
***********************************************************************/
|
||||||
#define SH_RELEASE "93u+m 2020-06-05"
|
#define SH_RELEASE "93u+m 2020-06-06"
|
||||||
|
|
|
@ -820,8 +820,6 @@ but can be unset or redefined:
|
||||||
.TP
|
.TP
|
||||||
.B "suspend=\(fmkill \-s \s-1STOP\s+1 $$\(fm"
|
.B "suspend=\(fmkill \-s \s-1STOP\s+1 $$\(fm"
|
||||||
.TP
|
.TP
|
||||||
.B "times=\(fm{ { time;} 2>&1;}\(fm"
|
|
||||||
.TP
|
|
||||||
.B "type=\(fmwhence \-v\(fm"
|
.B "type=\(fmwhence \-v\(fm"
|
||||||
.PD
|
.PD
|
||||||
.RE
|
.RE
|
||||||
|
@ -7112,6 +7110,11 @@ Suspends execution for the number of decimal seconds or fractions of a
|
||||||
second given by
|
second given by
|
||||||
.IR seconds .
|
.IR seconds .
|
||||||
.TP
|
.TP
|
||||||
|
\f3times\fP
|
||||||
|
Displays the accumulated user and system CPU times, one line with the times
|
||||||
|
used by the shell and another with those used by all of the shell's child
|
||||||
|
processes. No options are supporetd.
|
||||||
|
.TP
|
||||||
\(dg \f3trap\fP \*(OK \f3\-p\fP \*(CK \*(OK \f2action\^\fP \*(CK \*(OK \f2sig\^\fP \*(CK .\|.\|.
|
\(dg \f3trap\fP \*(OK \f3\-p\fP \*(CK \*(OK \f2action\^\fP \*(CK \*(OK \f2sig\^\fP \*(CK .\|.\|.
|
||||||
The
|
The
|
||||||
.B \-p
|
.B \-p
|
||||||
|
|
|
@ -665,8 +665,19 @@ actual=$(
|
||||||
expect=$': print: I/O error\n1'
|
expect=$': print: I/O error\n1'
|
||||||
if [[ $actual != *"$expect" ]]
|
if [[ $actual != *"$expect" ]]
|
||||||
then
|
then
|
||||||
err_exit "I/O error not detected (expected '$expect', got '$actual')"
|
err_exit "I/O error not detected: expected $(printf %q "$expect"), got $(printf %q "$actual"))"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ======
|
||||||
|
# 'times' builtin
|
||||||
|
|
||||||
|
expect=$'0m0.0[0-9]s 0m0.0[0-9]s\n0m0.00s 0m0.00s'
|
||||||
|
actual=$("$SHELL" -c times)
|
||||||
|
[[ $actual == $expect ]] || err_exit "times output: expected $(printf %q "$expect"), got $(printf %q "$actual"))"
|
||||||
|
|
||||||
|
expect=$'*: times: incorrect syntax'
|
||||||
|
actual=$(set +x; eval 'times Extra Args' 2>&1)
|
||||||
|
[[ $actual == $expect ]] || err_exit "times with args: expected $(printf %q "$expect"), got $(printf %q "$actual"))"
|
||||||
|
|
||||||
# ======
|
# ======
|
||||||
exit $((Errors<125?Errors:125))
|
exit $((Errors<125?Errors:125))
|
||||||
|
|
Loading…
Reference in a new issue