Skip to content
Merged
4 changes: 2 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ ES_WITH_READLINE
dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS(fcntl.h limits.h sys/ioctl.h sys/time.h unistd.h memory.h stdarg.h sys/cdefs.h)
AC_CHECK_HEADERS(fcntl.h limits.h sys/ioctl.h sys/time.h unistd.h memory.h stdarg.h sys/cdefs.h inttypes.h)


dnl Checks for typedefs, structures, and compiler characteristics.
Expand All @@ -71,7 +71,7 @@ AC_TYPE_GETGROUPS
AC_FUNC_MMAP

AC_CHECK_FUNCS(strerror strtol lstat setrlimit sigrelse sighold sigaction \
sysconf sigsetjmp getrusage mmap mprotect)
sysconf sigsetjmp getrusage gettimeofday mmap mprotect)

AC_CACHE_CHECK(whether getenv can be redefined, es_cv_local_getenv,
[if test "$ac_cv_header_stdlib_h" = no || test "$ac_cv_header_stdc" = no; then
Expand Down
2 changes: 1 addition & 1 deletion doc/es.1
Original file line number Diff line number Diff line change
Expand Up @@ -2756,7 +2756,7 @@ setnoexport setsignals
Some primitives are included in
.I es
conditionally, based on compile-time configuration options.
Those primitives, and the functions to which they are bound, are
Those primitives, and the functions which use them, are
.ta 2i
.Ds
.ft \*(Cf
Expand Down
167 changes: 94 additions & 73 deletions prim-sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,100 +288,121 @@ PRIM(limit) {
}
#endif /* BSD_LIMITS */

/*
* time builtin -- this is nearly as bad as limit
*/

#if BUILTIN_TIME
#if HAVE_GETRUSAGE
/* This function is provided as timersub(3) on some systems, but it's simple enough
* to do ourselves. */
static void timesub(struct timeval *a, struct timeval *b, struct timeval *res) {
res->tv_sec = a->tv_sec - b->tv_sec;
res->tv_usec = a->tv_usec - b->tv_usec;
if (res->tv_usec < 0) {
res->tv_sec -= 1;
res->tv_usec += 1000000;
}
struct times {
intmax_t real_usec;
intmax_t user_usec;
intmax_t sys_usec;
};

static void tmerrchk(int result, char *str) {
if (result == -1)
fail("$&time", "%s: %s", str, esstrerror(errno));
}

static void getrealtime(struct times *ret) {
#if HAVE_GETTIMEOFDAY && MILLISECOND_TIME
#define HAVE_PRECISE_REALTIME 1
struct timeval tv;
tmerrchk(gettimeofday(&tv, NULL), "getrealtime()");
ret->real_usec = (tv.tv_sec * INTMAX_C(1000000)) + tv.tv_usec;
#else /* use time(3p) */
#define HAVE_PRECISE_REALTIME 0
time_t t = time(NULL);
tmerrchk(t, "getrealtime()");
ret->real_usec = t * 1000000;
#endif
}

PRIM(time) {
static void getusagetimes(struct times *ret) {
#if HAVE_GETRUSAGE
struct rusage ru_self, ru_child;
tmerrchk(getrusage(RUSAGE_SELF, &ru_self), "getrusage(RUSAGE_SELF)");
tmerrchk(getrusage(RUSAGE_CHILDREN, &ru_child), "getrusage(RUSAGE_CHILDREN)");
ret->user_usec = (ru_self.ru_utime.tv_sec * 1000000)
+ ru_self.ru_utime.tv_usec
+ (ru_child.ru_utime.tv_sec * 1000000)
+ ru_child.ru_utime.tv_usec;
ret->sys_usec = (ru_self.ru_stime.tv_sec * 1000000)
+ ru_self.ru_stime.tv_usec
+ (ru_child.ru_stime.tv_sec * 1000000)
+ ru_child.ru_stime.tv_usec;
#else
struct tms tms;
static long mul = -1;
if (mul == -1)
mul = 1000000 / sysconf(_SC_CLK_TCK);
tmerrchk(times(&tms), "getusagetimes()");
ret->user_usec = ((intmax_t)tms.tms_utime + tms.tms_cutime) * mul;
ret->sys_usec = ((intmax_t)tms.tms_stime + tms.tms_cstime) * mul;
#endif
}

int pid, status;
time_t t0, t1;
struct rusage ru_prev, ru_new, ru_diff;

Ref(List *, lp, list);

getrusage(RUSAGE_CHILDREN, &ru_prev);
gc(); /* do a garbage collection first to ensure reproducible results */
t0 = time(NULL);
pid = efork(TRUE, FALSE);
if (pid == 0)
esexit(exitstatus(eval(lp, NULL, evalflags | eval_inchild)));
status = ewait(pid, FALSE);
t1 = time(NULL);
SIGCHK();
printstatus(0, status);
static void gettimes(struct times *ret) {
getrealtime(ret);
getusagetimes(ret);
}

getrusage(RUSAGE_CHILDREN, &ru_new);
timesub(&ru_new.ru_utime, &ru_prev.ru_utime, &ru_diff.ru_utime);
timesub(&ru_new.ru_stime, &ru_prev.ru_stime, &ru_diff.ru_stime);
static void subtimes(struct times a, struct times b, struct times *ret) {
ret->real_usec = a.real_usec - b.real_usec;
ret->user_usec = a.user_usec - b.user_usec;
ret->sys_usec = a.sys_usec - b.sys_usec;
}

static void strtimes(struct times time, List *list) {
#if MILLISECOND_TIME
eprint(
"%6ldr %5ld.%ldu %5ld.%lds\t%L\n",
t1 - t0,
ru_diff.ru_utime.tv_sec, (long) (ru_diff.ru_utime.tv_usec / 100000),
ru_diff.ru_stime.tv_sec, (long) (ru_diff.ru_stime.tv_usec / 100000),
lp, " "
#if HAVE_PRECISE_REALTIME
"%6.3jd"
#else
"%6jd"
#endif
"r %7.3jdu %7.3jds\t%L\n",
#if HAVE_PRECISE_REALTIME
time.real_usec / 1000,
#else
time.real_usec / 1000000,
#endif
time.user_usec / 1000,
time.sys_usec / 1000,
list, " "
);
#else
eprint(
"%6jdr %7.1jdu %7.1jds\t%L\n",
time.real_usec / 1000000,
time.user_usec / 100000,
time.sys_usec / 100000,
list, " "
);
#endif
}

RefEnd(lp);
return mklist(mkstr(mkstatus(status)), NULL);

#else /* !HAVE_GETRUSAGE */

PRIM(time) {
int pid, status;
struct times prev, time;

Ref(List *, lp, list);

gc(); /* do a garbage collection first to ensure reproducible results */
gettimes(&prev);
pid = efork(TRUE, FALSE);
if (pid == 0) {
clock_t t0, t1;
struct tms tms;
static clock_t ticks = 0;

if (ticks == 0)
ticks = sysconf(_SC_CLK_TCK);

t0 = times(&tms);
pid = efork(TRUE, FALSE);
if (pid == 0)
esexit(exitstatus(eval(lp, NULL, evalflags | eval_inchild)));

status = ewaitfor(pid);
t1 = times(&tms);
SIGCHK();
printstatus(0, status);

tms.tms_cutime += ticks / 20;
tms.tms_cstime += ticks / 20;

eprint(
"%6ldr %5ld.%ldu %5ld.%lds\t%L\n",
(t1 - t0 + ticks / 2) / ticks,
tms.tms_cutime / ticks, ((tms.tms_cutime * 10) / ticks) % 10,
tms.tms_cstime / ticks, ((tms.tms_cstime * 10) / ticks) % 10,
lp, " "
);
esexit(status);
}
status = ewaitfor(pid);
if (pid == 0)
esexit(exitstatus(eval(lp, NULL, evalflags | eval_inchild)));
status = ewait(pid, FALSE);
gettimes(&time);
SIGCHK();
printstatus(0, status);

subtimes(time, prev, &time);
strtimes(time, lp);

RefEnd(lp);
return mklist(mkstr(mkstatus(status)), NULL);

#endif /* !HAVE_GETRUSAGE */
}
#endif /* BUILTIN_TIME */

Expand Down
41 changes: 25 additions & 16 deletions print.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ static Boolean name(Format *format) { \
Flag(uconv, FMT_unsigned)
Flag(hconv, FMT_short)
Flag(longconv, FMT_long)
Flag(maxconv, FMT_max)
Flag(altconv, FMT_altform)
Flag(leftconv, FMT_leftside)
Flag(dotconv, FMT_f2set)
Expand Down Expand Up @@ -78,16 +79,19 @@ static void intconv(Format *format, unsigned int radix, int upper, char *altform
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
};
char padchar;
size_t len, pre, zeroes, padding, width;
long n, flags;
unsigned long u;
size_t len, pre, padding, width;
long flags;
intmax_t n;
uintmax_t u;
char number[64], prefix[20];

if (radix > 36)
return;

flags = format->flags;
if (flags & FMT_long)
if (flags & FMT_max)
n = va_arg(format->args, intmax_t);
else if (flags & FMT_long)
n = va_arg(format->args, long);
else
n = va_arg(format->args, int);
Expand All @@ -105,30 +109,34 @@ static void intconv(Format *format, unsigned int radix, int upper, char *altform
prefix[pre++] = *altform++;

len = utostr(u, number, radix, table[upper]) - number;
if ((flags & FMT_f2set) && (size_t) format->f2 > len)
zeroes = format->f2 - len;
else
zeroes = 0;
if (flags & FMT_f2set) {
size_t i, figs = format->f2;
if (figs >= len) {
prefix[pre++] = '0';
prefix[pre++] = '.';
for (i = figs - len; i > 0; i--)
prefix[pre++] = '0';
} else {
len++;
for (i = len; i >= len - figs; i--)
number[i] = number[i-1];
number[i] = '.';
}
}

width = pre + zeroes + len;
width = pre + len;
if ((flags & FMT_f1set) && (size_t) format->f1 > width) {
padding = format->f1 - width;
} else
padding = 0;

padchar = ' ';
if (padding > 0 && flags & FMT_zeropad) {
if (flags & FMT_zeropad)
padchar = '0';
if ((flags & FMT_leftside) == 0) {
zeroes += padding;
padding = 0;
}
}

if ((flags & FMT_leftside) == 0)
pad(format, padding, padchar);
fmtappend(format, prefix, pre);
pad(format, zeroes, '0');
fmtappend(format, number, len);
if (flags & FMT_leftside)
pad(format, padding, padchar);
Expand Down Expand Up @@ -188,6 +196,7 @@ static void inittab(void) {
fmttab['u'] = uconv;
fmttab['h'] = hconv;
fmttab['l'] = longconv;
fmttab['j'] = maxconv;
fmttab['#'] = altconv;
fmttab['-'] = leftconv;
fmttab['.'] = dotconv;
Expand Down
3 changes: 2 additions & 1 deletion print.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ enum {
FMT_leftside = 16, /* %- */
FMT_altform = 32, /* %# */
FMT_f1set = 64, /* %<n> */
FMT_f2set = 128 /* %.<n> */
FMT_f2set = 128, /* %.<n> */
FMT_max = 256 /* %j */
};

typedef Boolean (*Conv)(Format *);
Expand Down
14 changes: 14 additions & 0 deletions stdenv.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@
#include <varargs.h>
#endif

/* half-heartedly try to handle a lack of <stdint.h> or <inttypes.h> */
#if HAVE_STDINT_H
#include <stdint.h>
#else
#define intmax_t long
#define uintmax_t unsigned long
#endif

#if HAVE_INTTYPES_H
#include <inttypes.h>
#else
#define strtoimax strtol
#endif

#include <errno.h>
#include <setjmp.h>
#include <signal.h>
Expand Down