Index: bin/sh/parser.c =================================================================== --- bin/sh/parser.c +++ bin/sh/parser.c @@ -2102,6 +2102,60 @@ } break; + /* + * Print time or date as per provided format. + */ + case 'D': { + char tfmt[128], *cpyend; + 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++; + cpyend = memccpy(tfmt, fmt + 1, '}', sizeof(tfmt)); + if (cpyend == 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 (cpyend - tfmt == 1) { + /* "\D{}" is documented to mean %X. */ + fmt++; + strcpy(tfmt, "%X"); + } else { + fmt += cpyend - tfmt; + /* Truncate tfmt before '}'. */ + *--cpyend = '\0'; + } + now = time(NULL); + i += strftime(&ps[i], PROMPTLEN - i - 1, + tfmt, localtime(&now)) - 1; + break; + } + /* * Hostname. * Index: bin/sh/sh.1 =================================================================== --- bin/sh/sh.1 +++ bin/sh/sh.1 @@ -1428,6 +1428,16 @@ 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