git: a675eaec5aef - main - sh: implement PS1 \D to print current time

From: Piotr Pawel Stefaniak <pstef_at_FreeBSD.org>
Date: Sat, 23 Sep 2023 18:28:28 UTC
The branch main has been updated by pstef:

URL: https://cgit.FreeBSD.org/src/commit/?id=a675eaec5aef0089b6838aca8fd432fea0bd883b

commit a675eaec5aef0089b6838aca8fd432fea0bd883b
Author:     Piotr Pawel Stefaniak <pstef@FreeBSD.org>
AuthorDate: 2023-01-01 19:38:35 +0000
Commit:     Piotr Pawel Stefaniak <pstef@FreeBSD.org>
CommitDate: 2023-09-23 18:26:45 +0000

    sh: implement PS1 \D to print current time
    
    \D{format} yields the result of calling strftime(3) with the provided
    format and the current time.
    
    When PS4 can use this, it will enable us to easily generate timestamps
    when tracing script execution.
    
    Differential Revision:  https://reviews.freebsd.org/D35840
---
 bin/sh/parser.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 bin/sh/sh.1     | 10 ++++++++++
 2 files changed, 54 insertions(+)

diff --git a/bin/sh/parser.c b/bin/sh/parser.c
index 121c367c601c..8e959b46596b 100644
--- a/bin/sh/parser.c
+++ b/bin/sh/parser.c
@@ -43,6 +43,7 @@ static char sccsid[] = "@(#)parser.c	8.7 (Berkeley) 5/16/95";
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <time.h>
 
 #include "shell.h"
 #include "parser.h"
@@ -2100,6 +2101,49 @@ getprompt(void *unused __unused)
 			}
 			break;
 
+		/*
+		 * Print the current time as per provided strftime format.
+		 */
+		case 'D': {
+			char tfmt[128] = "%X"; /* \D{} means %X. */
+			struct tm *now;
+
+			if (fmt[1] != '{') {
+				/*
+				 * "\D" but not "\D{", so treat the '\'
+				 * literally and rewind fmt to treat 'D'
+				 * literally next iteration.
+				 */
+				ps[i] = '\\';
+				fmt--;
+				break;
+			}
+			fmt += 2; /* Consume "D{". */
+			if (fmt[0] != '}') {
+				char *end;
+
+				end = memccpy(tfmt, fmt, '}', sizeof(tfmt));
+				if (end == NULL) {
+					/*
+					 * Format too long or no '}', so
+					 * ignore "\D{" altogether.
+					 * The loop will do i++, but nothing
+					 * was written to ps, so do i-- here.
+					 * Rewind fmt for similar reason.
+					 */
+					i--;
+					fmt--;
+					break;
+				}
+				*--end = '\0'; /* Ignore the copy of '}'. */
+				fmt += end - tfmt;
+			}
+			now = localtime(&(time_t){time(NULL)});
+			i += strftime(&ps[i], PROMPTLEN - i - 1, tfmt, now);
+			i--; /* The loop will do i++. */
+			break;
+		}
+
 		/*
 		 * Hostname.
 		 *
diff --git a/bin/sh/sh.1 b/bin/sh/sh.1
index fb3afc7d3d4d..adbc32827046 100644
--- a/bin/sh/sh.1
+++ b/bin/sh/sh.1
@@ -1427,6 +1427,16 @@ unless you are the superuser, in which case it defaults to
 may include any of the following formatting sequences,
 which are replaced by the given information:
 .Bl -tag -width indent
+.It Li \eD{format}
+The current time in
+.Xr strftime 3
+.Ar format .
+The braces are required.
+Empty
+.Ar format
+is equivalent to
+\&%X,
+national representation of the time.
 .It Li \eH
 This system's fully-qualified hostname (FQDN).
 .It Li \eh