nmeter: add %T (zero-based timestamp) format

function                                             old     new   delta
collect_tv                                             -     132    +132
collect_monotonic                                      -      61     +61
nmeter_main                                          754     778     +24
gmtime                                                 -      21     +21
init_monotonic                                         -      18     +18
init_functions                                        44      48      +4
packed_usage                                       33432   33420     -12
collect_time                                         125      19    -106
------------------------------------------------------------------------------
(add/remove: 5/0 grow/shrink: 2/2 up/down: 260/-118)          Total: 142 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/procps/nmeter.c b/procps/nmeter.c
index ae16d85..856ce02 100644
--- a/procps/nmeter.c
+++ b/procps/nmeter.c
@@ -37,6 +37,7 @@
 //usage:     "\n %[pn]		# of processes"
 //usage:     "\n %b		Block io"
 //usage:     "\n %Nt		Time (with N decimal points)"
+//usage:     "\n %NT		Zero-based timestamp (with N decimal points)"
 //usage:     "\n %r		Print <cr> instead of <lf> at EOL"
 
 //TODO:
@@ -88,6 +89,7 @@
 	int delta;
 	unsigned deltanz;
 	struct timeval tv;
+	struct timeval start;
 #define first_proc_file proc_stat
 	proc_file proc_stat;	// Must match the order of proc_name's!
 	proc_file proc_loadavg;
@@ -101,7 +103,6 @@
 #define is26               (G.is26              )
 #define need_seconds       (G.need_seconds      )
 #define cur_outbuf         (G.cur_outbuf        )
-#define tv                 (G.tv                )
 #define proc_stat          (G.proc_stat         )
 #define proc_loadavg       (G.proc_loadavg      )
 #define proc_net_dev       (G.proc_net_dev      )
@@ -754,25 +755,53 @@
 	unsigned scale;
 S_STAT_END(time_stat)
 
-static void FAST_FUNC collect_time(time_stat *s)
+static void FAST_FUNC collect_tv(time_stat *s, struct timeval *tv, int local)
 {
 	char buf[sizeof("12:34:56.123456")];
 	struct tm* tm;
-	unsigned us = tv.tv_usec + s->scale/2;
-	time_t t = tv.tv_sec;
+	unsigned us = tv->tv_usec + s->scale/2;
+	time_t t = tv->tv_sec;
 
 	if (us >= 1000000) {
 		t++;
 		us -= 1000000;
 	}
-	tm = localtime(&t);
+	if (local)
+		tm = localtime(&t);
+	else
+		tm = gmtime(&t);
 
-	sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
+	sprintf(buf, "%02u:%02u:%02u", tm->tm_hour, tm->tm_min, tm->tm_sec);
 	if (s->prec)
-		sprintf(buf+8, ".%0*d", s->prec, us / s->scale);
+		sprintf(buf+8, ".%0*u", s->prec, us / s->scale);
 	put(buf);
 }
 
+static void FAST_FUNC collect_time(time_stat *s)
+{
+	collect_tv(s, &G.tv, /*local:*/ 1);
+}
+
+static void FAST_FUNC collect_monotonic(time_stat *s)
+{
+	struct timeval tv_mono;
+
+	tv_mono.tv_sec = G.tv.tv_sec - G.start.tv_sec;
+#if 0 /* Do we want this? */
+	if (tv_mono.tv_sec < 0) {
+		/* Time went backwards, reset start time to "now" */
+		tv_mono.tv_sec = 0;
+		G.start = G.tv;
+	}
+#endif
+	tv_mono.tv_usec = G.tv.tv_usec - G.start.tv_usec;
+	if ((int32_t)tv_mono.tv_usec < 0) {
+		tv_mono.tv_usec += 1000000;
+		tv_mono.tv_sec--;
+	}
+	collect_tv(s, &tv_mono, /*local:*/ 0);
+}
+
 static s_stat* init_time(const char *param)
 {
 	int prec;
@@ -789,6 +818,13 @@
 	return (s_stat*)s;
 }
 
+static s_stat* init_monotonic(const char *param)
+{
+	time_stat *s = (void*)init_time(param);
+	s->collect = collect_monotonic;
+	return (s_stat*)s;
+}
+
 static void FAST_FUNC collect_info(s_stat *s)
 {
 	gen ^= 1;
@@ -801,7 +837,7 @@
 
 typedef s_stat* init_func(const char *param);
 
-static const char options[] ALIGN1 = "ncmsfixptbr";
+static const char options[] ALIGN1 = "ncmsfixptTbr";
 static init_func *const init_functions[] = {
 	init_if,
 	init_cpu,
@@ -812,6 +848,7 @@
 	init_ctx,
 	init_fork,
 	init_time,
+	init_monotonic,
 	init_blk,
 	init_cr
 };
@@ -913,13 +950,15 @@
 	// Generate first samples but do not print them, they're bogus
 	collect_info(first);
 	reset_outbuf();
+
 	if (G.delta >= 0) {
-		gettimeofday(&tv, NULL);
-		usleep(G.delta > 1000000 ? 1000000 : G.delta - tv.tv_usec % G.deltanz);
+		gettimeofday(&G.tv, NULL);
+		usleep(G.delta > 1000000 ? 1000000 : G.delta - G.tv.tv_usec % G.deltanz);
 	}
 
+	gettimeofday(&G.start, NULL);
+	G.tv = G.start;
 	while (1) {
-		gettimeofday(&tv, NULL);
 		collect_info(first);
 		put_c(G.final_char);
 		print_outbuf();
@@ -932,11 +971,11 @@
 		if (G.delta >= 0) {
 			int rem;
 			// can be commented out, will sacrifice sleep time precision a bit
-			gettimeofday(&tv, NULL);
+			gettimeofday(&G.tv, NULL);
 			if (need_seconds)
-				rem = G.delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % G.deltanz;
+				rem = G.delta - ((ullong)G.tv.tv_sec*1000000 + G.tv.tv_usec) % G.deltanz;
 			else
-				rem = G.delta - (unsigned)tv.tv_usec % G.deltanz;
+				rem = G.delta - (unsigned)G.tv.tv_usec % G.deltanz;
 			// Sometimes kernel wakes us up just a tiny bit earlier than asked
 			// Do not go to very short sleep in this case
 			if (rem < (unsigned)G.delta / 128) {
@@ -944,6 +983,7 @@
 			}
 			usleep(rem);
 		}
+		gettimeofday(&G.tv, NULL);
 	}
 
 	/*return 0;*/