From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 7815 invoked by alias); 20 Jun 2018 05:12:42 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 43075 Received: (qmail 19039 invoked by uid 1010); 20 Jun 2018 05:12:42 -0000 X-Qmail-Scanner-Diagnostics: from mail-io0-f175.google.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.99.2/21882. spamassassin: 3.4.1. Clear:RC:0(209.85.223.175):SA:0(-1.9/5.0):. Processed in 5.892301 secs); 20 Jun 2018 05:12:42 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.1 (2015-04-28) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_NONE, SPF_PASS,T_DKIMWL_WL_MED,T_DKIM_INVALID autolearn=ham autolearn_force=no version=3.4.1 X-Envelope-From: dana@dana.is X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dana-is.20150623.gappssmtp.com; s=20150623; h=mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=ZhkQCCNLimcOpGozjpNnkNmVjJyRgaAKoMX/X8tsE/8=; b=cn9R+WApTHcc1iBeKM36K1za9m16Ml+4fI6IZJhxQwQN9OjsZQGEyip29OMNh0GtSv LFxPzk8w3WsIJfhnrwY22lte+xLYW88ToX6y3Ct+yq6qDoIalX0Oc0lXPDn5FboeVxAl CIDrXQUIlYDig9PelJEZg4P5BJStzVuPhzIPYcyKAoCCVSU3HSZOtczzTqf4u7XDkVpG ykejEI4JLre5N9bgQOnRcf5uZhlOBECGURBr22uDXzr5ILiPvZSoXvcN2+GZKSSMq5nf 5PV92Lv+CQFkMzOaE106L0aENmgCrk+NljYbQ43hWYYarsY5iiBw4FkD79Xyo+Bdvv67 BUfQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:subject:from:in-reply-to:date:cc :content-transfer-encoding:message-id:references:to; bh=ZhkQCCNLimcOpGozjpNnkNmVjJyRgaAKoMX/X8tsE/8=; b=euQPSYwFKKjmENj8rYt4pDFDPRVKHo2/DZH0r+96CsaB4YitpYPW9ZLHVOA7CdbwsU iGbTzCFa0jN+0iJcLkLRYNrYYi5GBtPNV9O2Z/qwgQAowezP6i5XZv1iVDfj70OaFl23 AAiGtefHrDoMYbFm9ofF/cRxoG8Oe5S4se6ZNnaWY0AuyK0q/hQAZ9UP+eHCcsqtJHCz U2SQ/Zj2Q54n4UGDBRYHa/bbHai2DsyM/yGPYB+8czQmS3Ftclphp5vTaV5+XT1524I4 +LlVVZgGnMjVPRkzLc2wwsE6e8jYrH2FTXTMqX7R4pYvTb7mFnGmoD78aLnRaJHGph6n b+Tw== X-Gm-Message-State: APt69E2FD1vKZmx46DzxK/TltCVeQpnIOo1yutPWGazHkTXsG45hRKL9 0L2/h/J1qNwEx9ua4Js9NvIjvA== X-Google-Smtp-Source: ADUXVKIJlaT2z2ihIznBnNGuADLz5Kzs9GkwnDZxzisIAE3lbDHz6CwsgpGJc7DESpPykKmwgC4c+A== X-Received: by 2002:a6b:d106:: with SMTP id l6-v6mr6566939iob.8.1529471553125; Tue, 19 Jun 2018 22:12:33 -0700 (PDT) Content-Type: text/plain; charset=us-ascii Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\)) Subject: Re: zsh/stat: output atime/mtime/ctime with nanoseconds From: dana In-Reply-To: <16617.1529435823@thecus> Date: Wed, 20 Jun 2018 00:12:31 -0500 Cc: zsh-workers@zsh.org Content-Transfer-Encoding: quoted-printable Message-Id: <103F2833-A38D-49C7-97D1-84EA299D9523@dana.is> References: <20180619141851.GA16101@zira.vinc17.org> <16617.1529435823@thecus> To: Oliver Kiddle X-Mailer: Apple Mail (2.3273) On 19 Jun 2018, at 14:17, Oliver Kiddle wrote: >Checking back, it seems the patch in workers/24059 was never applied. How is this? Building on the earlier patch, it: * Adds a zgettime() function to conveniently get nanosecond-precision = clock time, or at least something approximating it (Peter had independently = added ns precision to zsh/datetime between then and now, so i figured it was = best to centralize that functionality) * Updates ztrftime() to support %9. and %N * Updates prompt expansion to format times with nanoseconds * Updates zsh/datetime to use zgettime() * Updates zsh/stat to format times with nanoseconds * Updates documentation and tests I'm not sure about the error-handling / fall-back stuff in zgettime(). = Is an error message necessary? I put one in because pws had one, but he wasn't = falling back to gettimeofday() like i am. Also the ret++/ret-- stuff may be a = bit strange; i couldn't think of anything cleaner. One remaining (not new) issue is the fact that the strftime built-in = doesn't support sub-second times. That might not be feasible for `strftime -r`, = but the regular one could accept an optional third operand maybe? dana diff --git a/configure.ac b/configure.ac index 7644ebe52..53c30c2cf 100644 --- a/configure.ac +++ b/configure.ac @@ -1113,6 +1113,14 @@ zsh_TYPE_EXISTS([ #endif ], struct timezone) =20 +dnl Check for struct timespec since POSIX only gained it in 2008 +zsh_TYPE_EXISTS([ +#define _GNU_SOURCE 1 +#ifdef HAVE_SYS_TIME_H +# include +#endif +], struct timespec) + dnl Check for utmp structures, for watch zsh_TYPE_EXISTS([ #ifdef HAVE_SYS_TYPES_H diff --git a/Src/zsh_system.h b/Src/zsh_system.h index 5339b496f..8289ee97c 100644 --- a/Src/zsh_system.h +++ b/Src/zsh_system.h @@ -250,6 +250,14 @@ struct timezone { }; #endif =20 +/* Used to provide compatibility with clock_gettime() */ +#if !defined(HAVE_STRUCT_TIMESPEC) && !defined(ZSH_OOT_MODULE) +struct timespec { + time_t tv_sec; + long tv_nsec; +}; +#endif + /* There's more than one non-standard way to get at this data */ #if !defined(HAVE_STRUCT_DIRENT_D_INO) && = defined(HAVE_STRUCT_DIRENT_D_STAT) # define d_ino d_stat.st_ino diff --git a/Src/compat.c b/Src/compat.c index a130d9264..7b5c4411c 100644 --- a/Src/compat.c +++ b/Src/compat.c @@ -94,6 +94,39 @@ gettimeofday(struct timeval *tv, struct timezone *tz) #endif =20 =20 +/* Provide clock time with nanoseconds */ + +/**/ +mod_export int +zgettime(struct timespec *ts) +{ + int ret =3D -1; + +#ifdef HAVE_CLOCK_GETTIME + struct timespec dts; + if (clock_gettime(CLOCK_REALTIME, &dts) < 0) { + zwarn("unable to retrieve time: %e", errno); + ret--; + } else { + ret++; + ts->tv_sec =3D (time_t) dts.tv_sec; + ts->tv_nsec =3D (long) dts.tv_nsec; + } +#endif + + if (ret) { + struct timeval dtv; + struct timezone dtz; + gettimeofday(&dtv, &dtz); + ret++; + ts->tv_sec =3D (time_t) dtv.tv_sec; + ts->tv_nsec =3D (long) dtv.tv_usec * 1000; + } + + return ret; +} + + /* compute the difference between two calendar times */ =20 /**/ diff --git a/Src/utils.c b/Src/utils.c index b41851700..ee2ad207f 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -3224,7 +3224,7 @@ ztrftimebuf(int *bufsizeptr, int decr) =20 /**/ mod_export int -ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec) +ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long nsec) { int hr12; #ifdef HAVE_STRFTIME @@ -3299,15 +3299,15 @@ morefmt: case '.': if (ztrftimebuf(&bufsize, digs)) return -1; - if (digs > 6) - digs =3D 6; - if (digs < 6) { + if (digs > 9) + digs =3D 9; + if (digs < 9) { int trunc; - for (trunc =3D 5 - digs; trunc; trunc--) - usec /=3D 10; - usec =3D (usec + 5) / 10; + for (trunc =3D 8 - digs; trunc; trunc--) + nsec /=3D 10; + nsec =3D (nsec + 8) / 10; } - sprintf(buf, "%0*ld", digs, usec); + sprintf(buf, "%0*ld", digs, nsec); buf +=3D digs; break; case '\0': @@ -3369,6 +3369,12 @@ morefmt: *buf++ =3D '0' + tm->tm_min / 10; *buf++ =3D '0' + tm->tm_min % 10; break; + case 'N': + if (ztrftimebuf(&bufsize, 9)) + return -1; + sprintf(buf, "%09ld", nsec); + buf +=3D 9; + break; case 'S': if (tm->tm_sec > 9 || !strip) *buf++ =3D '0' + tm->tm_sec / 10; diff --git a/Src/prompt.c b/Src/prompt.c index 95da52559..959ed8e3d 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -273,8 +273,7 @@ putpromptchar(int doprint, int endchar, unsigned int = *txtchangep) char *ss, *hostnam; int t0, arg, test, sep, j, numjobs, len; struct tm *tm; - struct timezone dummy_tz; - struct timeval tv; + struct timespec ts; time_t timet; Nameddir nd; =20 @@ -664,8 +663,8 @@ putpromptchar(int doprint, int endchar, unsigned int = *txtchangep) tmfmt =3D "%l:%M%p"; break; } - gettimeofday(&tv, &dummy_tz); - tm =3D localtime(&tv.tv_sec); + zgettime(&ts); + tm =3D localtime(&ts.tv_sec); /* * Hack because strftime won't say how * much space it actually needs. Try to add it @@ -675,7 +674,7 @@ putpromptchar(int doprint, int endchar, unsigned int = *txtchangep) */ for(j =3D 0, t0 =3D strlen(tmfmt)*8; j < 3; j++, = t0*=3D2) { addbufspc(t0); - if ((len =3D ztrftime(bv->bp, t0, tmfmt, tm, = tv.tv_usec)) + if ((len =3D ztrftime(bv->bp, t0, tmfmt, tm, = ts.tv_nsec)) >=3D 0) break; } diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c index 6e9047bc5..be378b347 100644 --- a/Src/Modules/datetime.c +++ b/Src/Modules/datetime.c @@ -180,66 +180,30 @@ getcurrentsecs(UNUSED(Param pm)) } =20 static double -getcurrentrealtime(Param pm) +getcurrentrealtime(UNUSED(Param pm)) { -#ifdef HAVE_CLOCK_GETTIME struct timespec now; - - if (clock_gettime(CLOCK_REALTIME, &now) < 0) { - zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno); - return (double)0.0; - } - + zgettime(&now); return (double)now.tv_sec + (double)now.tv_nsec * 1e-9; -#else - struct timeval now; - struct timezone dummy_tz; - - (void)pm; - gettimeofday(&now, &dummy_tz); - - return (double)now.tv_sec + (double)now.tv_usec * 1e-6; -#endif } =20 static char ** -getcurrenttime(Param pm) +getcurrenttime(UNUSED(Param pm)) { char **arr; char buf[DIGBUFSIZE]; - -#ifdef HAVE_CLOCK_GETTIME struct timespec now; =20 - if (clock_gettime(CLOCK_REALTIME, &now) < 0) { - zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno); - return NULL; - } - - arr =3D (char **)zhalloc(3 * sizeof(*arr)); - sprintf(buf, "%ld", (long)now.tv_sec); - arr[0] =3D dupstring(buf); - sprintf(buf, "%ld", now.tv_nsec); - arr[1] =3D dupstring(buf); - arr[2] =3D NULL; - - return arr; -#else - struct timeval now; - struct timezone dummy_tz; - - (void)pm; - gettimeofday(&now, &dummy_tz); + zgettime(&now); =20 arr =3D (char **)zhalloc(3 * sizeof(*arr)); sprintf(buf, "%ld", (long)now.tv_sec); arr[0] =3D dupstring(buf); - sprintf(buf, "%ld", (long)now.tv_usec * 1000); + sprintf(buf, "%ld", (long)now.tv_nsec); arr[1] =3D dupstring(buf); arr[2] =3D NULL; =20 return arr; -#endif } =20 static struct builtin bintab[] =3D { diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c index 66baa1292..50a6a9bb2 100644 --- a/Src/Modules/stat.c +++ b/Src/Modules/stat.c @@ -188,7 +188,7 @@ static char *timefmt; =20 /**/ static void -stattimeprint(time_t tim, char *outbuf, int flags) +stattimeprint(time_t tim, long nsecs, char *outbuf, int flags) { if (flags & STF_RAW) { sprintf(outbuf, "%ld", (unsigned long)tim); @@ -199,7 +199,7 @@ stattimeprint(time_t tim, char *outbuf, int flags) char *oend =3D outbuf + strlen(outbuf); /* Where the heck does "40" come from? */ int len =3D ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? = gmtime(&tim) : - localtime(&tim), 0L); + localtime(&tim), nsecs); if (len > 0) metafy(oend, len, META_NOALLOC); if (flags & STF_RAW) @@ -291,15 +291,27 @@ statprint(struct stat *sbuf, char *outbuf, char = *fname, int iwhich, int flags) break; =20 case ST_ATIM: - stattimeprint(sbuf->st_atime, optr, flags); +#ifdef GET_ST_ATIME_NSEC + stattimeprint(sbuf->st_atime, GET_ST_ATIME_NSEC(*sbuf), optr, = flags); +#else + stattimeprint(sbuf->st_atime, 0L, optr, flags); +#endif break; =20 case ST_MTIM: - stattimeprint(sbuf->st_mtime, optr, flags); +#ifdef GET_ST_MTIME_NSEC + stattimeprint(sbuf->st_mtime, GET_ST_MTIME_NSEC(*sbuf), optr, = flags); +#else + stattimeprint(sbuf->st_mtime, 0L, optr, flags); +#endif break; =20 case ST_CTIM: - stattimeprint(sbuf->st_ctime, optr, flags); +#ifdef GET_ST_CTIME_NSEC + stattimeprint(sbuf->st_ctime, GET_ST_CTIME_NSEC(*sbuf), optr, = flags); +#else + stattimeprint(sbuf->st_ctime, 0L, optr, flags); +#endif break; =20 case ST_BLKSIZE: diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst index 11f18dc71..56b7c294a 100644 --- a/Test/D01prompt.ztst +++ b/Test/D01prompt.ztst @@ -108,6 +108,14 @@ if (( $date2[7,8] !=3D $date3[1,2] )); then print "Years do not agree in $date2, $date3" fi + # These are somewhat questionable, but... + ns=3D( ${=3D"$(print -P '%D{%9.} %D{%N}')"} ) + if [[ $ns[1] !=3D [0-9](#c9) ]] || [[ $ns[2] !=3D [0-9](#c9) ]]; then + print "Nanosecond lengths/formats are not as expected in $ns[1], = $ns[2]" + fi + if (( ($ns2[2] - $ns[1]) > 5000000 )); then + print "Nanoseconds differ too much in $ns[1], $ns[2]" + fi 0:Dates produced by prompt escapes =20 mkdir foo diff --git a/Doc/Zsh/prompt.yo b/Doc/Zsh/prompt.yo index 3c8f2a094..909012c8e 100644 --- a/Doc/Zsh/prompt.yo +++ b/Doc/Zsh/prompt.yo @@ -198,11 +198,15 @@ endsitem() In addition, if the system supports the POSIX tt(gettimeofday) system call, tt(%.) provides decimal fractions of a second since the epoch = with leading zeroes. By default three decimal places are provided, but a -number of digits up to 6 may be given following the tt(%); hence = tt(%6.) -outputs microseconds. A typical example of this is the format -`tt(%D{%H:%M:%S.%.})'. +number of digits up to 9 may be given following the tt(%); hence = tt(%6.) +outputs microseconds, and tt(%9.) outputs nanoseconds. (The latter +requires a nanosecond-precision tt(clock_gettime); systems lacking this +will return a value multiplied by the appropriate power of 10.) A = typical +example of this is the format `tt(%D{%H:%M:%S.%.})'. =20 -The GNU extension that a `tt(-)' between the tt(%) and the +The GNU extension tt(%N) is handled as a synonym for tt(%9.). + +Additionally, the GNU extension that a `tt(-)' between the tt(%) and = the format character causes a leading zero or space to be stripped is handled directly by the shell for the format characters tt(d), = tt(f), tt(H), tt(k), tt(l), tt(m), tt(M), tt(S) and tt(y); any other format diff --git a/Doc/Zsh/mod_stat.yo b/Doc/Zsh/mod_stat.yo index 96349061e..9caed1e45 100644 --- a/Doc/Zsh/mod_stat.yo +++ b/Doc/Zsh/mod_stat.yo @@ -114,7 +114,11 @@ named files; no list of file names is allowed in = this case. ) item(tt(-F) var(fmt))( Supplies a tt(strftime) (see manref(strftime)(3)) string for the -formatting of the time elements. The tt(-s) option is implied. +formatting of the time elements. The format string supports all of the +zsh extensions described in +ifzman(the section EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc))\ +ifnzman(noderef(Prompt Expansion)). +The tt(-s) option is implied. ) item(tt(-g))( Show the time elements in the GMT time zone. The