Browse Source

time: implement %% and \escapes in -f FMT

function                                             old     new   delta
time_main                                           1217    1316     +99

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Denys Vlasenko 3 months ago
parent
commit
1cac258521
2 changed files with 55 additions and 21 deletions
  1. 18 21
      miscutils/time.c
  2. 37 0
      testsuite/time.tests

+ 18 - 21
miscutils/time.c

@@ -226,28 +226,19 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
 		}
 
 		switch (*fmt) {
-#ifdef NOT_NEEDED
-		/* Handle literal char */
-		/* Usually we optimize for size, but there is a limit
-		 * for everything. With this we do a lot of 1-byte writes */
-		default:
-			bb_putchar(*fmt);
-			break;
-#endif
-
 		case '%':
 			switch (*++fmt) {
-#ifdef NOT_NEEDED_YET
-		/* Our format strings do not have these */
-		/* and we do not take format str from user */
 			default:
-				bb_putchar('%');
+				/* Unknown %<char> is printed as "?<char>" */
+				bb_putchar('?');
+				if (!*fmt) {
+					/* Trailing -f '...%' prints "...?" but NOT newline */
+					goto ret;
+				}
 				/*FALLTHROUGH*/
 			case '%':
-				if (!*fmt) goto ret;
 				bb_putchar(*fmt);
 				break;
-#endif
 			case 'C':	/* The command that got timed.  */
 				printargv(command);
 				break;
@@ -381,14 +372,21 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
 			}
 			break;
 
-#ifdef NOT_NEEDED_YET
-		case '\\':		/* Format escape.  */
+		default: /* *fmt is '\': format escape */
 			switch (*++fmt) {
 			default:
+				/* Unknown \<char> is printed as "?\<char>" */
+				bb_putchar('?');
 				bb_putchar('\\');
+				if (!*fmt) {
+					/* Trailing -f '...\': GNU time 1.9 prints
+					 * "...?\COMMAND" (it's probably a bug).
+					 */
+					puts(command[0]);
+					goto ret;
+				}
 				/*FALLTHROUGH*/
 			case '\\':
-				if (!*fmt) goto ret;
 				bb_putchar(*fmt);
 				break;
 			case 't':
@@ -399,12 +397,11 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
 				break;
 			}
 			break;
-#endif
 		}
 		++fmt;
 	}
- /* ret: */
 	bb_putchar('\n');
+ ret: ;
 }
 
 /* Run command CMD and return statistics on it.
@@ -438,7 +435,7 @@ int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int time_main(int argc UNUSED_PARAM, char **argv)
 {
 	resource_t res;
-	/* $TIME has lowest prio (-v,-p,-f FMT overrride it) */
+	/* $TIME has lowest prio (-v,-p,-f FMT override it) */
 	const char *output_format = getenv("TIME") ? : default_format;
 	char *output_filename;
 	int output_fd;

+ 37 - 0
testsuite/time.tests

@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Copyright 2024 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "description" "arguments" "result" "infile" "stdin"
+
+testing "time -f trailing backslash" \
+	"time -f 'abc\' sleep 0 2>&1" \
+	'abc?\sleep\n' '' ''
+#	^^^^^^^^^^^^^^ this is what GNU time version 1.9 prints
+
+testing "time -f trailing percent" \
+	"time -f 'abc%' sleep 0 2>&1" \
+	'abc?' '' ''
+
+testing "time -f undefined backslash" \
+	"time -f 'abc\^def' sleep 0 2>&1" \
+	'abc?\^def\n' '' ''
+
+testing "time -f undefined percent" \
+	"time -f 'abc%^def' sleep 0 2>&1" \
+	'abc?^def\n' '' ''
+
+testing "time -f backslash tab and newline" \
+	"time -f 'abc\ndef\txyz' sleep 0 2>&1" \
+	'abc
+def	xyz
+' '' ''
+
+testing "time -f percent percent" \
+	"time -f 'abc%%def' sleep 0 2>&1" \
+	'abc%def\n' '' ''
+
+exit $FAILCOUNT