1
0
Fork 0
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:
Martijn Dekker 2020-06-06 21:25:59 +02:00
parent 3a3776f1df
commit 65d363fd34
8 changed files with 96 additions and 5 deletions

8
NEWS
View file

@ -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,

View file

@ -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(),

View file

@ -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
}; };

View file

@ -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

View file

@ -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[];

View file

@ -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"

View file

@ -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

View file

@ -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))