Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
7 changes: 7 additions & 0 deletions es.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,13 @@ extern void blocksignals(void);
extern void unblocksignals(void);


/* prim-sys.c */

#if BUILTIN_TIME
extern void setbasetime(void);
#endif


/* open.c */

typedef enum { oOpen, oCreate, oAppend, oReadWrite, oReadCreate, oReadAppend } OpenKind;
Expand Down
13 changes: 12 additions & 1 deletion initial.es
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,18 @@ fn-unwind-protect = $&noreturn @ body cleanup {
# and get time from /bin or wherever.

if {~ <=$&primitives limit} {fn-limit = $&limit}
if {~ <=$&primitives time} {fn-time = $&time}
if {~ <=$&primitives time} {
fn time cmd {
$&collect
let ((str times) = <=$&time)
unwind-protect {
$cmd
} {
(str times) = <={$&time $times}
echo >[1=2] $str^\t^$^cmd
}
}
}

# These builtins are mainly useful for internal functions, but
# they're there to be called if you want to use them.
Expand Down
3 changes: 3 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ int main(int argc, char **argv0) {

initconv();
initgc();
#if BUILTIN_TIME
setbasetime();
#endif

if (argc == 0) {
argc = 1;
Expand Down
192 changes: 114 additions & 78 deletions prim-sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,100 +288,136 @@ 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 Boolean timethrows = TRUE;

static void tmerrchk(int result, char *str) {
if (result != -1)
return;
if (timethrows)
fail("$&time", "%s: %s", str, esstrerror(errno));
eprint("%s: %s\n", str, esstrerror(errno));
eprint("Calls to `$&time` or `time` in this shell may produce bad values.\n");
}

static void getrealtime(struct times *ret) {
#if HAVE_GETTIMEOFDAY
#define 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 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);
static void gettimes(struct times *ret) {
getrealtime(ret);
getusagetimes(ret);
}

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 parsetimes(List *list, struct times *ret) {
char *suffix;
if (length(list) != 3)
fail("$&time", "usage: $&time [r u s]");

ret->real_usec = strtoimax(getstr(list->term), &suffix, 10);
if (*suffix != '\0')
fail("$&time", "real-time argument not an int", list->term);
ret->user_usec = strtoimax(getstr(list->next->term), &suffix, 10);
if (*suffix != '\0')
fail("$&time", "user-time argument not an int", list->next->term);
ret->sys_usec = strtoimax(getstr(list->next->next->term), &suffix, 10);
if (*suffix != '\0')
fail("$&time", "sys-time argument not an int", list->next->next->term);
}

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

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, " "
static char *strtimes(struct times time) {
return str(
#if PRECISE_REALTIME
"%6.3jd"
#else
"%6jd"
#endif
"r %7.3jdu %7.3jds",
#if PRECISE_REALTIME
time.real_usec / 1000,
#else
time.real_usec / 1000000,
#endif
time.user_usec / 1000,
time.sys_usec / 1000
);
}

RefEnd(lp);
return mklist(mkstr(mkstatus(status)), NULL);
static struct times base;
extern void setbasetime(void) {
timethrows = FALSE;
gettimes(&base);
timethrows = TRUE;
}

#else /* !HAVE_GETRUSAGE */
PRIM(time) {
struct times prev, time;

int pid, status;
Ref(List *, lp, list);
gettimes(&time);
subtimes(time, base, &time);

gc(); /* do a garbage collection first to ensure reproducible results */
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);
if (list != NULL) {
parsetimes(list, &prev);
subtimes(time, prev, &time);
}
status = ewaitfor(pid);
SIGCHK();
printstatus(0, status);

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

#endif /* !HAVE_GETRUSAGE */
gcdisable();
list = mklist(mkstr(strtimes(time)),
mklist(mkstr(str("%jd", time.real_usec)),
mklist(mkstr(str("%jd", time.user_usec)),
mklist(mkstr(str("%jd", time.sys_usec)), NULL))));
gcenable();
return list;
}
#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
Loading