Index: bin/sh/parser.c =================================================================== --- bin/sh/parser.c +++ bin/sh/parser.c @@ -45,6 +45,7 @@ #include #include #include +#include #include "shell.h" #include "parser.h" @@ -2088,6 +2089,60 @@ ps[i] = '\\'; break; + /* + * Print time or date as per provided format. + */ + case 'D': { + char tfmt[128], *len; + time_t 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; + } + /* + * Consume the 'D' character and try to copy + * fmt from after '{' until '}'. + * Don't consume '{' from fmt yet. + */ + fmt++; + len = memccpy(tfmt, fmt + 1, '}', sizeof(tfmt)); + if (len == NULL) { + /* + * Format too long or '}' not found, + * so ignore "\D{" altogether. + * The loop will do i++, but nothing + * was written to ps, so do i-- here. + * "fmt++" will consume the '{'. + */ + i--; + break; + } + /* + * Either way, consume fmt until '}'. The loop + * will do fmt++ to finally consume the '}'. + */ + if (len - tfmt == 1) { + /* "\D{}" is documented to mean %X. */ + fmt++; + strcpy(tfmt, "%X"); + } else { + fmt += len - tfmt; + /* Truncate tfmt before '}'. */ + *--len = '\0'; + } + now = time(NULL); + i += strftime(&ps[i], PROMPTLEN - i - 1, + tfmt, localtime(&now)) - 1; + } + break; + /* * CRLF sequence */ Index: bin/sh/sh.1 =================================================================== --- bin/sh/sh.1 +++ bin/sh/sh.1 @@ -1428,6 +1428,18 @@ may include any of the following formatting sequences, which are replaced by the given information: .Bl -tag -width indent +.It Li \eD{format} +The result of calling +.Xr strftime 3 +with the provided +.Ar format . +and the current time. +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