commit: eae74fb71a2a4f653b5389f3f75aef3a2b9d8628
parent 591dd4ebc96103e6e9b1d62b66be54ca83062076
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Wed, 1 Nov 2023 05:37:28 +0100
cmd/date: Add support for `-f now_format now`
Diffstat:
7 files changed, 111 insertions(+), 26 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -8,11 +8,11 @@
!/cmd/*.ha
!/cmd/*.1
!/cmd/*.1.in
-/cmd/date.1
-/cmd/touch.1
*.t.err
*.o
+/build/
+
# Binary
/test-lib/strtodur
diff --git a/Makefile b/Makefile
@@ -27,7 +27,7 @@ lint: $(MAN1SO)
.PHONY: clean
clean:
- rm -fr $(EXE) test-lib/strtodur
+ rm -fr $(EXE) $(MAN1SO) test-lib/strtodur
rm -fr ${EXE:=.c.gcov} ${EXE:=.gcda} ${EXE:=.gcno}
install: all
@@ -48,15 +48,15 @@ cmd/date: cmd/date.c lib/iso_parse.c Makefile
rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
$(CC) -std=c99 $(CFLAGS) -o $@ cmd/date.c lib/iso_parse.c $(LDFLAGS)
-cmd/date.1: cmd/date.1.in lib/iso_parse.mdoc Makefile
- $(M4) cmd/date.1.in > cmd/date.1
+build/cmd/date.1: cmd/date.1.in lib/iso_parse.mdoc Makefile
+ $(M4) cmd/date.1.in > $@
cmd/touch: cmd/touch.c lib/iso_parse.c Makefile
rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
$(CC) -std=c99 $(CFLAGS) -o $@ cmd/touch.c lib/iso_parse.c $(LDFLAGS)
-cmd/touch.1: cmd/touch.1.in lib/iso_parse.mdoc Makefile
- $(M4) cmd/touch.1.in > cmd/touch.1
+build/cmd/touch.1: cmd/touch.1.in lib/iso_parse.mdoc Makefile
+ $(M4) cmd/touch.1.in > $@
cmd/sleep: cmd/sleep.c lib/strtodur.c Makefile
rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
diff --git a/cmd/date.1.in b/cmd/date.1.in
@@ -9,26 +9,46 @@
.Nd display date and time
.Sh SYNOPSIS
.Nm
-.Op Fl u
+.Op Fl uR
.Op Fl d Ar datetime
.Op Cm + Ns Ar format
+.Nm
+.Op Fl uR
+.Fl f Ar now_format
+.Ar now
+.Op Cm + Ns Ar format
.Sh DESCRIPTION
When
.Nm
is invoked without arguments it displays the current datetime
Otherwise, depending on the options specified, will print the datetime in a user-defined way.
+.Sh OPTIONS
.Bl -tag -width Ds
.It Fl d Ar datetime
+Use
+.Ar datetime
+instead of current datetime.
include(lib/iso_parse.mdoc)
+.It Fl f Ar now_format
+Use
+.Ar now_format
+as the
+.Xr strptime 3
+format string for
+.Ar now ,
+which will be used instead of the current datetime.
.It Fl u
Use UTC (coordinated universal time) instead of the local time.
-.El
-.Pp
-The plus operand
-.Pq Sq +
-specifies in which format the datetime should be displayed, the format string is specified in the
+.It Fl R
+Set the default value of
+.Ar format
+to match RFC5322 (Internet Message Format).
+.It Cm + Ns Ar format
+Set the displayed datetime in
.Xr strftime 3
-manual page.
+format. Otherwise defaults to
+.Ql %c
+.El
.Sh ENVIRONMENT
Look at the manual page of
.Xr strftime 3
@@ -45,5 +65,15 @@ for the environment variables, typical ones are
is mostly compliant with the
.St -p1003.1-2008
specification.
+.Pp
+The
+.Fl d
+and
+.Fl R
+options are present for compatibility with other modern systems such as
+NetBSD, BusyBox, and GNU coreutils.
+The
+.Fl f
+option is inspired by NetBSD.
.Sh AUTHORS
.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me
diff --git a/cmd/date.c b/cmd/date.c
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
+#define _XOPEN_SOURCE // strptime
#include "../lib/iso_parse.h" /* iso_parse */
@@ -10,13 +11,14 @@
#include <locale.h> /* setlocale() */
#include <stdio.h> /* BUFSIZ, perror(), puts() */
#include <stdlib.h> /* exit(), strtol() */
-#include <time.h> /* time, localtime, tm, strftime */
+#include <time.h> /* time, localtime, tm, strftime, strptime */
#include <unistd.h> /* getopt(), optarg, optind */
void
usage()
{
- fprintf(stderr, "date [-uR][-d datetime] [+format]\n");
+ fprintf(stderr, "date [-uR] [-d datetime] [+format]\n");
+ fprintf(stderr, "date [-uR] -f now_format now [+format]\n");
}
int
@@ -25,8 +27,10 @@ main(int argc, char *argv[])
char outstr[BUFSIZ];
struct tm *tm;
time_t now;
- char *format = "%c";
- int uflag = 0;
+ char *format = "%c";
+ char *input_format = NULL;
+ int uflag = 0;
+ int dflag = 0;
int c;
//setlocale(LC_ALL, "");
@@ -38,19 +42,33 @@ main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
- while((c = getopt(argc, argv, ":d:uR")) != -1)
+ while((c = getopt(argc, argv, ":d:f:uR")) != -1)
{
char *errstr = NULL;
switch(c)
{
case 'd': /* Custom datetime */
- now = iso_parse(optarg, &errstr).tv_sec;
+ if(input_format != NULL)
+ {
+ fprintf(stderr, "date: Cannot both use '-d' and '-f'\n");
+ exit(EXIT_FAILURE);
+ }
+ now = iso_parse(optarg, &errstr).tv_sec;
+ dflag = 1;
if(errstr != NULL)
{
fprintf(stderr, "date: iso_parse(\"%s\", …): %s\n", optarg, errstr);
exit(EXIT_FAILURE);
}
break;
+ case 'f': /* input datetime format */
+ if(dflag == 1)
+ {
+ fprintf(stderr, "date: Cannot both use '-d' and '-f'\n");
+ exit(EXIT_FAILURE);
+ }
+ input_format = optarg;
+ break;
case 'u': /* UTC timezone */
uflag++;
break;
@@ -92,9 +110,30 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
- (void)argc;
+ if(argc > 0 && input_format != NULL)
+ {
+ char *res = strptime(argv[0], input_format, tm);
- if(*argv && **argv == '+') format = *argv + 1;
+ if(res == NULL)
+ {
+ fprintf(stderr,
+ "date: strptime(\"%s\", \"%s\", …) as passed by '-f' option failed\n",
+ argv[0],
+ input_format);
+ exit(EXIT_FAILURE);
+ }
+
+ argv++;
+ argc--;
+ }
+
+ if(argc > 0 && *argv && **argv == '+')
+ {
+ format = *argv + 1;
+
+ argv++;
+ argc--;
+ }
errno = 0;
if(strftime(outstr, sizeof(outstr), format, tm) == 0 && errno != 0)
diff --git a/configure b/configure
@@ -65,7 +65,10 @@ gen_targets() {
echo
printf 'MAN1SO = '
- printf '%s\n ' cmd/*.1.in | grep -v -f target_filter | sed 's;\.in$;;' | tr -d '\n'
+ printf '%s\n' cmd/*.1.in \
+ | grep -v -f target_filter \
+ | sed -e 's;\.in$;;' -e 's;^cmd/;build/cmd/;' \
+ | tr '\n' ' '
echo
printf 'MAN1 = ${MAN1SO} '
diff --git a/lib/iso_parse.mdoc b/lib/iso_parse.mdoc
@@ -7,7 +7,7 @@
.\" .so lib/iso_parse.mdoc
.\"
Should be formatted either with a leading @ (at) symbol followed by
-the Unix timestamp (number of seconds before and after 1970-01-01 00:00:00Z).
+the Unix timestamp (number of seconds before and after 1970-01-01 00:00:00Z),
for example
.Ql @1698791420
corresponds to 2023-10-31 23:30:20 UTC
diff --git a/test-cmd/date b/test-cmd/date
@@ -2,6 +2,11 @@
# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
# SPDX-License-Identifier: MPL-2.0
+usage="\
+date [-uR] [-d datetime] [+format]
+date [-uR] -f now_format now [+format]
+"
+
atf_test_case noargs
noargs_body() {
atf_check -o not-empty ../cmd/date
@@ -9,7 +14,7 @@ noargs_body() {
atf_test_case badarg
badarg_body() {
- atf_check -s 'exit:1' -e "inline:date: Error: Unrecognised option: '-x'\ndate [-uR][-d datetime] [+format]\n" ../cmd/date -x
+ atf_check -s 'exit:1' -e "inline:date: Error: Unrecognised option: '-x'\n${usage}" ../cmd/date -x
}
atf_test_case epoch
@@ -59,7 +64,7 @@ timestamp_body() {
atf_check -o "inline:1970-01-01T00:01:09\n" ../cmd/date -u -d @69 '+%FT%T'
atf_check -o "inline:1969-12-31T23:58:51\n" ../cmd/date -u -d @-69 '+%FT%T'
- atf_check -s 'exit:1' -e "inline:date: Error: Missing operand for option: '-d'\ndate [-uR][-d datetime] [+format]\n" ../cmd/date -u -d
+ atf_check -s 'exit:1' -e "inline:date: Error: Missing operand for option: '-d'\n${usage}" ../cmd/date -u -d
# 36893488147419103232 = 2^65
atf_check -s 'exit:1' -e not-empty ../cmd/date -u -d @36893488147419103232
@@ -74,6 +79,13 @@ isodate_body() {
atf_check -o "inline:-69\n" ../cmd/date -u -d "1969-12-31T23:58:51,00Z" '+%s'
}
+atf_test_case now_format
+now_format_body() {
+ # Note: POSIX doesn't specifies %s for strptime(3)
+ atf_check -o "inline:1155544496\n" ../cmd/date -u -f '%Y-%m-%dT%H:%M:%S' '2006-08-14T08:34:56' '+%s'
+}
+
+
atf_init_test_cases() {
cd "$(atf_get_srcdir)" || exit 1
@@ -92,4 +104,5 @@ atf_init_test_cases() {
atf_add_test_case timestamp
atf_add_test_case isodate
+ atf_add_test_case now_format
}