Re: [hackers] [sbase][Patch] date: add date/time setting capability

2017-09-03 Thread John Vogel
On Sun, 3 Sep 2017 13:17:57 -0700
Michael Forney  wrote:

> On 2017-03-09, John Vogel  wrote:
> > This tests good here:
> >
> > diff --git a/date.c b/date.c
> > index dcf2528..67f28d0 100644
> > --- a/date.c
> > +++ b/date.c
> > @@ -25,7 +25,7 @@ datefield(const char *s, size_t i)
> >  static void
> >  setdate(const char *s, struct tm *now)
> >  {
> > -   struct tm date;
> > +   struct tm date = { .tm_sec = 0, .tm_isdst = -1 };
> > struct timespec ts;
> >
> > switch (strlen(s)) {  
> 
> Applied finally, with this and a couple more other minor tweaks.
> 

Thanks for all your efforts. Very much appreciated.



Re: [hackers] [sbase][Patch] date: add date/time setting capability

2017-03-09 Thread John Vogel
On Thu, 9 Mar 2017 14:53:11 -0800
Michael Forney  wrote:

> On Thu, Mar 9, 2017 at 2:10 PM, John Vogel  wrote:
> > On Wed, 8 Mar 2017 09:47:15 -0800
> > Michael Forney  wrote:
> >  
> >> I pushed a slightly amended version of this patch to my sbase branch at
> >> https://github.com/michaelforney/sbase/commit/4b13b23a689da5fc01c2a26affe8ad8087c777bf.
> >>  
> >
> > I am running into strange behavior in both this patch, as well as my last 
> > one.
> > I am pretty sure I tested it before, but I am not convinced I did so 
> > thoroughly.
> > When setting the date, sometimes the result is as expected, sometimes the
> > result is wildly off. I have run it through gdb. The broke down time seems 
> > to
> > be set as expected. When inspecting struct tm date in either my patch or 
> > this
> > one, I notice garbage values in parts of the tm struct that are not being
> > set. I have tried two methods for addressing: (1) zeroing the date variable 
> > with
> > memset and (2) declaring struct tm date as static. Both methods do the trick
> > and setting the date is consistent with input. I also considered explicitly
> > zeroing the members that are not touched, but since not all of them seem to 
> > be
> > required by posix, I thought that zeroing the whole struct made more sense. 
> >  
> 
> I think it is safe to just set tm_sec to 0, and tm_isdst to -1 (so
> that mktime will attempt to guess whether or not DST is in effect).
> POSIX says that tm_wday and tm_yday are ignored, and the glibc manual
> (and musl implementation) says that the non-standard fields tm_gmtoff
> and tm_zone are also ignored.
> 

This tests good here:

diff --git a/date.c b/date.c
index dcf2528..67f28d0 100644
--- a/date.c
+++ b/date.c
@@ -25,7 +25,7 @@ datefield(const char *s, size_t i)
 static void
 setdate(const char *s, struct tm *now)
 {
-   struct tm date;
+   struct tm date = { .tm_sec = 0, .tm_isdst = -1 };
struct timespec ts;
 
switch (strlen(s)) {



Re: [hackers] [sbase][Patch] date: add date/time setting capability

2017-03-09 Thread John Vogel
On Wed, 8 Mar 2017 09:47:15 -0800
Michael Forney  wrote:

> I pushed a slightly amended version of this patch to my sbase branch at
> https://github.com/michaelforney/sbase/commit/4b13b23a689da5fc01c2a26affe8ad8087c777bf.
> 

I am running into strange behavior in both this patch, as well as my last one.
I am pretty sure I tested it before, but I am not convinced I did so thoroughly.
When setting the date, sometimes the result is as expected, sometimes the
result is wildly off. I have run it through gdb. The broke down time seems to
be set as expected. When inspecting struct tm date in either my patch or this
one, I notice garbage values in parts of the tm struct that are not being
set. I have tried two methods for addressing: (1) zeroing the date variable with
memset and (2) declaring struct tm date as static. Both methods do the trick
and setting the date is consistent with input. I also considered explicitly
zeroing the members that are not touched, but since not all of them seem to be
required by posix, I thought that zeroing the whole struct made more sense.



Re: [hackers] [sbase][Patch] date: add date/time setting capability

2017-01-01 Thread John Vogel
On Sun, 1 Jan 2017 13:43:59 -0800
Michael Forney  wrote:

> On 12/29/16, John Vogel  wrote:
> > On Thu, 29 Dec 2016 12:11:33 +0100
> > Hiltjo Posthuma  wrote:
> >  
> >> Nitpicks:
> >> - Check if digit in date_field().
> >> - Style: don't declare variables in the inner scope, just use the
> >> function scope for struct tm date; and struct timespec ts;
> >>
> >> Kind regards,
> >> Hiltjo
> >>  
> >
> > Thanks for your input.  
> 
> Hmm, I think your fix for this made the patch a bit awkward. I suggest
> keeping the original structure, but changing date_field to eprintf
> when it hits a non-digit, so you don't have to deal with all that
> error checking in main.
> 

Good point. Thank you.

-- >8 --
 date.1 | 46 +++--
 date.c | 67 +++---
 2 files changed, 104 insertions(+), 9 deletions(-)

diff --git a/date.1 b/date.1
index 29081a5..0171936 100644
--- a/date.1
+++ b/date.1
@@ -3,12 +3,15 @@
 .Os sbase
 .Sh NAME
 .Nm date
-.Nd print date and time
+.Nd print or set date and time
 .Sh SYNOPSIS
 .Nm
 .Op Fl d Ar time
 .Op Fl u
 .Op Cm + Ns Ar format
+.Sm off
+.Op Ar mmddHHMM Oo Oo Ar CC Oc Ar yy Oc
+.Sm on
 .Sh DESCRIPTION
 .Nm
 prints the date and time according to
@@ -16,7 +19,8 @@ prints the date and time according to
 or
 .Ar format
 using
-.Xr strftime 3 .
+.Xr strftime 3
+or sets the date.
 .Sh OPTIONS
 .Bl -tag -width Ds
 .It Fl d Ar time
@@ -27,6 +31,44 @@ Unix epoch 1970-01-01T00:00:00Z.
 .It Fl u
 Print UTC time instead of local time.
 .El
+.Pp
+An operand with a leading plus
+.Pq Cm +
+sign signals a user-defined format string using
+.Xr strftime 3
+conversion specifications.
+.Pp
+An operand without a leading plus sign is
+interpreted a value for setting the systems current date and time.
+The canonical representation for setting the date and time is:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar mm
+The month of the year, from 01 to 12.
+.It Ar dd
+The day of the month, from 01 to 31.
+.It Ar HH
+The hour of the day, from 00 to 23.
+.It Ar MM
+The minute of the hour, from 00 to 59.
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar yy
+The second two digits of the year.
+If
+.Ar yy
+is specified, but
+.Ar CC
+is not, a value for
+.Ar yy
+between 69 and 99 results in a
+.Ar CC
+value of 19. Otherwise, a
+.Ar CC
+value of 20 is used.
+.El
+.Pp
+The century and year are optional. The default is the current year.
 .Sh STANDARDS
 The
 .Nm
diff --git a/date.c b/date.c
index 1671e1f..92ef9f4 100644
--- a/date.c
+++ b/date.c
@@ -1,14 +1,27 @@
 /* See LICENSE file for copyright and license details. */
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 #include "util.h"
 
+
 static void
 usage(void)
 {
-   eprintf("usage: %s [-u] [-d time] [+format]\n", argv0);
+   eprintf("usage: %s [-u] [-d time] [+format] [mmddHHMM[[CC]yy]]\n", 
argv0);
+}
+
+static int
+date_field(const char *s, size_t i)
+{
+   if (!isdigit(s[i]) || !isdigit(s[i+1]))
+   eprintf("invalid date format: %s\n", s);
+
+   return (s[i] - '0') * 10 + (s[i+1] - '0');
 }
 
 int
@@ -16,11 +29,11 @@ main(int argc, char *argv[])
 {
struct tm *now;
struct tm *(*tztime)(const time_t *) = localtime;
+   struct tm date;
+   struct timespec ts;
time_t t;
char buf[BUFSIZ], *fmt = "%c", *tz = "local";
 
-   t = time(NULL);
-
ARGBEGIN {
case 'd':
t = estrtonum(EARGF(usage()), 0, LLONG_MAX);
@@ -33,14 +46,54 @@ main(int argc, char *argv[])
usage();
} ARGEND
 
+   t = time(NULL);
+   if (t == (time_t)-1)
+   eprintf("time failed:");
+
+   if (!(now = tztime(&t)))
+   eprintf("%stime failed\n", tz);
+
if (argc) {
-   if (argc != 1 || argv[0][0] != '+')
+   if (argc != 1) {
usage();
-   else
+   } else if (argv[0][0] == '+') {
fmt = &argv[0][1];
+   } else {
+   switch (strlen(argv[0])) {
+   case 8:
+   date.tm_year = now->tm_year;
+   break;
+   case 10:
+   date.tm_year = date_field(argv[0], 8);
+   if (date.tm_year < 69)
+   date.tm_year += 100;
+   break;
+   case 12:
+   date.tm_year = ((date_field(argv[0], 8) - 19) * 
100) + date_field(argv[0], 10);
+

Re: [hackers] [sbase][Patch] date: add date/time setting capability

2016-12-29 Thread John Vogel
On Thu, 29 Dec 2016 12:11:33 +0100
Hiltjo Posthuma  wrote:

> Nitpicks:
> - Check if digit in date_field().
> - Style: don't declare variables in the inner scope, just use the
> function scope for struct tm date; and struct timespec ts;
> 
> Kind regards,
> Hiltjo
> 

Thanks for your input.

-- >8 --
 date.1 | 46 ++--
 date.c | 75 +++---
 2 files changed, 112 insertions(+), 9 deletions(-)

diff --git a/date.1 b/date.1
index 29081a5..0171936 100644
--- a/date.1
+++ b/date.1
@@ -3,12 +3,15 @@
 .Os sbase
 .Sh NAME
 .Nm date
-.Nd print date and time
+.Nd print or set date and time
 .Sh SYNOPSIS
 .Nm
 .Op Fl d Ar time
 .Op Fl u
 .Op Cm + Ns Ar format
+.Sm off
+.Op Ar mmddHHMM Oo Oo Ar CC Oc Ar yy Oc
+.Sm on
 .Sh DESCRIPTION
 .Nm
 prints the date and time according to
@@ -16,7 +19,8 @@ prints the date and time according to
 or
 .Ar format
 using
-.Xr strftime 3 .
+.Xr strftime 3
+or sets the date.
 .Sh OPTIONS
 .Bl -tag -width Ds
 .It Fl d Ar time
@@ -27,6 +31,44 @@ Unix epoch 1970-01-01T00:00:00Z.
 .It Fl u
 Print UTC time instead of local time.
 .El
+.Pp
+An operand with a leading plus
+.Pq Cm +
+sign signals a user-defined format string using
+.Xr strftime 3
+conversion specifications.
+.Pp
+An operand without a leading plus sign is
+interpreted a value for setting the systems current date and time.
+The canonical representation for setting the date and time is:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar mm
+The month of the year, from 01 to 12.
+.It Ar dd
+The day of the month, from 01 to 31.
+.It Ar HH
+The hour of the day, from 00 to 23.
+.It Ar MM
+The minute of the hour, from 00 to 59.
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar yy
+The second two digits of the year.
+If
+.Ar yy
+is specified, but
+.Ar CC
+is not, a value for
+.Ar yy
+between 69 and 99 results in a
+.Ar CC
+value of 19. Otherwise, a
+.Ar CC
+value of 20 is used.
+.El
+.Pp
+The century and year are optional. The default is the current year.
 .Sh STANDARDS
 The
 .Nm
diff --git a/date.c b/date.c
index 1671e1f..bf377c7 100644
--- a/date.c
+++ b/date.c
@@ -1,6 +1,9 @@
 /* See LICENSE file for copyright and license details. */
 #include 
 #include 
+#include 
+#include 
+#include 
 #include 
 
 #include "util.h"
@@ -8,7 +11,15 @@
 static void
 usage(void)
 {
-   eprintf("usage: %s [-u] [-d time] [+format]\n", argv0);
+   eprintf("usage: %s [-u] [-d time] [+format] [mmddHHMM[[CC]yy]]\n", 
argv0);
+}
+
+static int
+date_field(const char *s, size_t i)
+{
+   if (isdigit(s[i]) && isdigit(s[i+1]))
+   return (s[i] - '0') * 10 + (s[i+1] - '0');
+   return -1;
 }
 
 int
@@ -16,11 +27,12 @@ main(int argc, char *argv[])
 {
struct tm *now;
struct tm *(*tztime)(const time_t *) = localtime;
+   struct tm date;
+   struct timespec ts;
time_t t;
+   int century, year;
char buf[BUFSIZ], *fmt = "%c", *tz = "local";
 
-   t = time(NULL);
-
ARGBEGIN {
case 'd':
t = estrtonum(EARGF(usage()), 0, LLONG_MAX);
@@ -33,14 +45,63 @@ main(int argc, char *argv[])
usage();
} ARGEND
 
+   t = time(NULL);
+   if (t == (time_t)-1)
+   eprintf("time failed:");
+
+   if (!(now = tztime(&t)))
+   eprintf("%stime failed\n", tz);
+
if (argc) {
-   if (argc != 1 || argv[0][0] != '+')
+   if (argc != 1) {
usage();
-   else
+   } else if (argv[0][0] == '+') {
fmt = &argv[0][1];
+   } else {
+   switch (strlen(argv[0])) {
+   case 8:
+   date.tm_year = now->tm_year;
+   break;
+   case 10:
+   date.tm_year = date_field(argv[0], 8);
+   if ((date.tm_year >= 0) && (date.tm_year < 69))
+   date.tm_year += 100;
+   break;
+   case 12:
+   century = date_field(argv[0], 8);
+   year = date_field(argv[0], 10);
+   if ((century >= 0) && (year >= 0)) {
+   date.tm_year = ((century - 19) * 100) + 
year;
+   break;
+   }
+   /* fallthrough */
+   default:
+   date.tm_year = -1;
+   break;
+   }
+   
+   if ((date.tm_year < 0) ||
+   ((date.tm_mon = date_field(argv[0], 0)) < 0) ||
+   ((date.tm_mday = date_field(argv[0], 2)) < 0) ||
+

Re: [hackers] [sbase][Patch] date: add date/time setting capability

2016-12-29 Thread John Vogel
On Wed, 28 Dec 2016 21:38:51 -0800
Michael Forney  wrote:


> > +   eprintf("time failed: %s\n", strerror(errno));  
> 
> eprintf will print the error message and a newline if the format
> string ends in ':'.
> 
> So you can just do `eprintf("time:")`, which seems to be the
> convention for sbase.
Done.

> > +   if (clock_settime(CLOCK_REALTIME, &ts) == -1)
> > +   eprintf("clock_settime failed: %s\n", 
> > strerror(errno));  
> 
> Same here.
Done.

-- >8 --
 date.1 | 46 --
 date.c | 62 +++---
 2 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/date.1 b/date.1
index 29081a5..0171936 100644
--- a/date.1
+++ b/date.1
@@ -3,12 +3,15 @@
 .Os sbase
 .Sh NAME
 .Nm date
-.Nd print date and time
+.Nd print or set date and time
 .Sh SYNOPSIS
 .Nm
 .Op Fl d Ar time
 .Op Fl u
 .Op Cm + Ns Ar format
+.Sm off
+.Op Ar mmddHHMM Oo Oo Ar CC Oc Ar yy Oc
+.Sm on
 .Sh DESCRIPTION
 .Nm
 prints the date and time according to
@@ -16,7 +19,8 @@ prints the date and time according to
 or
 .Ar format
 using
-.Xr strftime 3 .
+.Xr strftime 3
+or sets the date.
 .Sh OPTIONS
 .Bl -tag -width Ds
 .It Fl d Ar time
@@ -27,6 +31,44 @@ Unix epoch 1970-01-01T00:00:00Z.
 .It Fl u
 Print UTC time instead of local time.
 .El
+.Pp
+An operand with a leading plus
+.Pq Cm +
+sign signals a user-defined format string using
+.Xr strftime 3
+conversion specifications.
+.Pp
+An operand without a leading plus sign is
+interpreted a value for setting the systems current date and time.
+The canonical representation for setting the date and time is:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar mm
+The month of the year, from 01 to 12.
+.It Ar dd
+The day of the month, from 01 to 31.
+.It Ar HH
+The hour of the day, from 00 to 23.
+.It Ar MM
+The minute of the hour, from 00 to 59.
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar yy
+The second two digits of the year.
+If
+.Ar yy
+is specified, but
+.Ar CC
+is not, a value for
+.Ar yy
+between 69 and 99 results in a
+.Ar CC
+value of 19. Otherwise, a
+.Ar CC
+value of 20 is used.
+.El
+.Pp
+The century and year are optional. The default is the current year.
 .Sh STANDARDS
 The
 .Nm
diff --git a/date.c b/date.c
index 1671e1f..1885190 100644
--- a/date.c
+++ b/date.c
@@ -1,6 +1,8 @@
 /* See LICENSE file for copyright and license details. */
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "util.h"
@@ -8,7 +10,13 @@
 static void
 usage(void)
 {
-   eprintf("usage: %s [-u] [-d time] [+format]\n", argv0);
+   eprintf("usage: %s [-u] [-d time] [+format] [mmddHHMM[[CC]yy]]\n", 
argv0);
+}
+
+static int
+date_field(const char *s, size_t i)
+{
+   return (s[i] - '0') * 10 + (s[i+1] - '0');
 }
 
 int
@@ -19,8 +27,6 @@ main(int argc, char *argv[])
time_t t;
char buf[BUFSIZ], *fmt = "%c", *tz = "local";
 
-   t = time(NULL);
-
ARGBEGIN {
case 'd':
t = estrtonum(EARGF(usage()), 0, LLONG_MAX);
@@ -33,14 +39,56 @@ main(int argc, char *argv[])
usage();
} ARGEND
 
+   t = time(NULL);
+   if (t == (time_t)-1)
+   eprintf("time failed:");
+
+   if (!(now = tztime(&t)))
+   eprintf("%stime failed\n", tz);
+
if (argc) {
-   if (argc != 1 || argv[0][0] != '+')
+   if (argc != 1) {
usage();
-   else
+   } else if (argv[0][0] == '+') {
fmt = &argv[0][1];
+   } else {
+   struct tm date;
+   struct timespec ts;
+
+   date.tm_mon = date_field(argv[0], 0) - 1;
+   date.tm_mday = date_field(argv[0], 2);
+   date.tm_hour = date_field(argv[0], 4);
+   date.tm_min = date_field(argv[0], 6);
+
+   switch (strlen(argv[0])) {
+   case 8:
+   date.tm_year = now->tm_year;
+   break;
+   case 10:
+   date.tm_year = date_field(argv[0], 8);
+   if (date.tm_year < 69)
+   date.tm_year += 100;
+   break;
+   case 12:
+   date.tm_year = ((date_field(argv[0], 8) - 19) * 
100) + date_field(argv[0], 10);
+   break;
+   default:
+   eprintf("invalid date format: %s\n", argv[0]);
+   }
+   
+   t = mktime(&date);
+   if (t == (time_t)-1)
+   eprintf("mktime failed: bad calender date/time: 
%s\n", argv[0]);
+
+   ts.

Re: [hackers] [sbase][Patch] date: add date/time setting capability

2016-12-28 Thread John Vogel
On Wed, 28 Dec 2016 20:01:38 -0800
Evan Gates  wrote:

> For future reference, could I request use of  scissors for this? Git am[0]
> has a feature to cut off everything above a line containing only 8< (looks
> like scissors) and dashes. e.g.
> 
> 8<8<
> 
> Keeping conversation above this line and patch bow it makes applying the
> patch easier,  no need to manually get rid of the rest of the email.
> 
> Thanks
> 
> [0] https://git-scm.com/docs/git-am

Sounds reasonable. I use that convention in the future.



Re: [hackers] [sbase][Patch] date: add date/time setting capability

2016-12-28 Thread John Vogel
On Wed, 28 Dec 2016 17:42:39 -0800
Michael Forney  wrote:

> Cool. Thanks for the new patch. I just have a few style nits.

> > eprintf("usage: %s [-u] [-d time] [+format]\n", argv0);  
> 
> Let's update this usage message.
Oops, I think I missed bringing that forward from my original patch.

> > +static int date_field(const char *s, size_t i)  
> 
> The suckless style guide says that `static int` should be on its own
> line (sorry, my suggestion had the error too).
No problem.

> > +   t = time(NULL);  
> 
> This is pre-existing, but this call is not checked for errors.
Right. Done.

> > if (argc) {
> > -   if (argc != 1 || argv[0][0] != '+')
> > +   if (argc != 1)
> > usage();
> > -   else  
> 
> This should use braces (since the else branch needs them).
Done.

> 
> > +   else if (argv[0][0] == '+') {
> > fmt = &argv[0][1];
> > +   }
> > +   else {  
> 
> These should be on the same line (`} else {`).
Done.

> > +   date.tm_year = date_field(argv[0], 8);
> > +   if (date.tm_year < 69)
> > +   date.tm_year += now->tm_year < 69 ? 0 : 
> > 100;  
> 
> I think this needs to be just `date.tm_year += 100` (regardless of the
> current year). POSIX says "If century is not specified, then values in
> the range [69,99] shall refer to years 1969 to 1999 inclusive, and
> values in the range [00,68] shall refer to years 2000 to 2068
> inclusive."
Done

> > +   return fshut(stdout, "");  
> 
> This is unnecessary since it never prints anything to stdout. Just `return 0`.
Done.

> Apart from these nits, this looks good to me.
> 

Your help is greatly appreciated, thank you. Revised patch below.

Signed-off-by: John Vogel 
---
 date.1 | 46 --
 date.c | 62 +++---
 2 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/date.1 b/date.1
index 29081a5..0171936 100644
--- a/date.1
+++ b/date.1
@@ -3,12 +3,15 @@
 .Os sbase
 .Sh NAME
 .Nm date
-.Nd print date and time
+.Nd print or set date and time
 .Sh SYNOPSIS
 .Nm
 .Op Fl d Ar time
 .Op Fl u
 .Op Cm + Ns Ar format
+.Sm off
+.Op Ar mmddHHMM Oo Oo Ar CC Oc Ar yy Oc
+.Sm on
 .Sh DESCRIPTION
 .Nm
 prints the date and time according to
@@ -16,7 +19,8 @@ prints the date and time according to
 or
 .Ar format
 using
-.Xr strftime 3 .
+.Xr strftime 3
+or sets the date.
 .Sh OPTIONS
 .Bl -tag -width Ds
 .It Fl d Ar time
@@ -27,6 +31,44 @@ Unix epoch 1970-01-01T00:00:00Z.
 .It Fl u
 Print UTC time instead of local time.
 .El
+.Pp
+An operand with a leading plus
+.Pq Cm +
+sign signals a user-defined format string using
+.Xr strftime 3
+conversion specifications.
+.Pp
+An operand without a leading plus sign is
+interpreted a value for setting the systems current date and time.
+The canonical representation for setting the date and time is:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar mm
+The month of the year, from 01 to 12.
+.It Ar dd
+The day of the month, from 01 to 31.
+.It Ar HH
+The hour of the day, from 00 to 23.
+.It Ar MM
+The minute of the hour, from 00 to 59.
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar yy
+The second two digits of the year.
+If
+.Ar yy
+is specified, but
+.Ar CC
+is not, a value for
+.Ar yy
+between 69 and 99 results in a
+.Ar CC
+value of 19. Otherwise, a
+.Ar CC
+value of 20 is used.
+.El
+.Pp
+The century and year are optional. The default is the current year.
 .Sh STANDARDS
 The
 .Nm
diff --git a/date.c b/date.c
index 1671e1f..4ebd010 100644
--- a/date.c
+++ b/date.c
@@ -1,6 +1,8 @@
 /* See LICENSE file for copyright and license details. */
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "util.h"
@@ -8,7 +10,13 @@
 static void
 usage(void)
 {
-   eprintf("usage: %s [-u] [-d time] [+format]\n", argv0);
+   eprintf("usage: %s [-u] [-d time] [+format] [mmddHHMM[[CC]yy]]\n", 
argv0);
+}
+
+static int
+date_field(const char *s, size_t i)
+{
+   return (s[i] - '0') * 10 + (s[i+1] - '0');
 }
 
 int
@@ -19,8 +27,6 @@ main(int argc, char *argv[])
time_t t;
char buf[BUFSIZ], *fmt = "%c", *tz = "local";
 
-   t = time(NULL);
-
ARGBEGIN {
case 'd':
t = estrtonum(EARGF(usage()), 0, LLONG_MAX);
@@ -33,14 +39,56 @@ main(int argc, char *argv[])
usage();
} ARGEND
 
+   t = time(NULL);
+   if (t == (time_t)-1)
+   eprintf("time failed: %s\n", strerror(errno));
+
+   if (!(now = tztime

Re: [hackers] [sbase][Patch] date: add date/time setting capability

2016-12-28 Thread John Vogel
On Tue, 27 Dec 2016 17:34:02 -0800
Michael Forney  wrote:

> One suggestion I can think of, since the date format is simple and
> uses fixed sizes, it to just parse into struct tm manually rather than
> restructuring the date string into a new buffer with spaces.
> 
> For example (untested):
> static int date_field(char *s, size_t i) {
>   return (s[i] - '0') * 10 + (s[i+1] - '0');
> }
> 
> char *s = argv[0];
> tm->tm_mon = date_field(s, 0) - 1;
> tm->tm_day = date_field(s, 2);
> tm->tm_hour = date_field(s, 4);
> tm->tm_min = date_field(s, 6);
> switch (len) {
> case 8:
>   tm->tm_year = now->tm_year;
>   break;
> case 10:
>   tm->tm_year = date_field(s, 8);
>   if (tm->tm_year < 69)
> tm->tm_year += tm->year < 69 ? 2000 : 1900;
>   break;
> case 12:
>   tm->tm_year = date_field(s, 8) * 100 + date_field(s, 10);
>   break;
> }
> 

Thanks for the help, Michael. I made a couple adjustments to
account for tm_year needing to be 'years since 1900'. Tests
fine here.

--
 date.1 | 46 --
 date.c | 58 --
 2 files changed, 96 insertions(+), 8 deletions(-)

diff --git a/date.1 b/date.1
index 29081a5..0171936 100644
--- a/date.1
+++ b/date.1
@@ -3,12 +3,15 @@
 .Os sbase
 .Sh NAME
 .Nm date
-.Nd print date and time
+.Nd print or set date and time
 .Sh SYNOPSIS
 .Nm
 .Op Fl d Ar time
 .Op Fl u
 .Op Cm + Ns Ar format
+.Sm off
+.Op Ar mmddHHMM Oo Oo Ar CC Oc Ar yy Oc
+.Sm on
 .Sh DESCRIPTION
 .Nm
 prints the date and time according to
@@ -16,7 +19,8 @@ prints the date and time according to
 or
 .Ar format
 using
-.Xr strftime 3 .
+.Xr strftime 3
+or sets the date.
 .Sh OPTIONS
 .Bl -tag -width Ds
 .It Fl d Ar time
@@ -27,6 +31,44 @@ Unix epoch 1970-01-01T00:00:00Z.
 .It Fl u
 Print UTC time instead of local time.
 .El
+.Pp
+An operand with a leading plus
+.Pq Cm +
+sign signals a user-defined format string using
+.Xr strftime 3
+conversion specifications.
+.Pp
+An operand without a leading plus sign is
+interpreted a value for setting the systems current date and time.
+The canonical representation for setting the date and time is:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar mm
+The month of the year, from 01 to 12.
+.It Ar dd
+The day of the month, from 01 to 31.
+.It Ar HH
+The hour of the day, from 00 to 23.
+.It Ar MM
+The minute of the hour, from 00 to 59.
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar yy
+The second two digits of the year.
+If
+.Ar yy
+is specified, but
+.Ar CC
+is not, a value for
+.Ar yy
+between 69 and 99 results in a
+.Ar CC
+value of 19. Otherwise, a
+.Ar CC
+value of 20 is used.
+.El
+.Pp
+The century and year are optional. The default is the current year.
 .Sh STANDARDS
 The
 .Nm
diff --git a/date.c b/date.c
index 1671e1f..4a18bd6 100644
--- a/date.c
+++ b/date.c
@@ -1,6 +1,8 @@
 /* See LICENSE file for copyright and license details. */
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "util.h"
@@ -11,6 +13,11 @@ usage(void)
eprintf("usage: %s [-u] [-d time] [+format]\n", argv0);
 }
 
+static int date_field(const char *s, size_t i)
+{
+   return (s[i] - '0') * 10 + (s[i+1] - '0');
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -19,8 +26,6 @@ main(int argc, char *argv[])
time_t t;
char buf[BUFSIZ], *fmt = "%c", *tz = "local";
 
-   t = time(NULL);
-
ARGBEGIN {
case 'd':
t = estrtonum(EARGF(usage()), 0, LLONG_MAX);
@@ -33,14 +38,55 @@ main(int argc, char *argv[])
usage();
} ARGEND
 
+   t = time(NULL);
+
+   if (!(now = tztime(&t)))
+   eprintf("%stime failed\n", tz);
+
if (argc) {
-   if (argc != 1 || argv[0][0] != '+')
+   if (argc != 1)
usage();
-   else
+   else if (argv[0][0] == '+') {
fmt = &argv[0][1];
+   }
+   else {
+   struct tm date;
+   struct timespec ts;
+
+   date.tm_mon = date_field(argv[0], 0) - 1;
+   date.tm_mday = date_field(argv[0], 2);
+   date.tm_hour = date_field(argv[0], 4);
+   date.tm_min = date_field(argv[0], 6);
+
+   switch (strlen(argv[0])) {
+   case 8:
+   date.tm_year = now->tm_year;
+   break;
+   case 10:
+   date.tm_year = date_field(argv[0], 8);
+   if (date.tm_year < 69)
+   date.tm_year += now->tm_year < 69 ? 0 : 
100;
+   break;
+   case 12:
+   date.tm_year = ((date_field(argv[0], 8) - 19) * 
100) + date_field(argv[0], 10);
+   break;
+   

Re: [hackers] [sbase][Patch] date: add date/time setting capability

2016-12-27 Thread John Vogel
On Tue, 27 Dec 2016 12:20:41 +0100
Laslo Hunhold  wrote:

> I am unsure how to deal with this patch. I personally have an
> NTD-daemon running and it does all my time-manipulations. I can imagine
> that maybe manually setting your time is a good thing for debugging,
> testing, joking, whatever, but is it really worth the added complexity?
> 
> What do the others think?

I need to set date during boot on my raspberrypis. Since posix specifies
setting the date with date, I thought it would be helpful to add it to
sbase. I can always supply my own date setting utility (which I do now)
if the patch is deemed to bloat the code unduly.



[hackers] [sbase][Patch] date: add date/time setting capability

2016-05-05 Thread John Vogel
From 79079cdc560d68257c110482c730ab043c7f4c0f Mon Sep 17 00:00:00 2001
From: John Vogel 
Date: Thu, 5 May 2016 15:01:36 -0400
Subject: [sbase][Patch] date: add date/time setting capability
To: hackers@suckless.org

Signed-off-by: John Vogel 
---
 date.1 | 46 --
 date.c | 65 ++---
 2 files changed, 102 insertions(+), 9 deletions(-)

diff --git a/date.1 b/date.1
index 29081a5..0171936 100644
--- a/date.1
+++ b/date.1
@@ -3,12 +3,15 @@
 .Os sbase
 .Sh NAME
 .Nm date
-.Nd print date and time
+.Nd print or set date and time
 .Sh SYNOPSIS
 .Nm
 .Op Fl d Ar time
 .Op Fl u
 .Op Cm + Ns Ar format
+.Sm off
+.Op Ar mmddHHMM Oo Oo Ar CC Oc Ar yy Oc
+.Sm on
 .Sh DESCRIPTION
 .Nm
 prints the date and time according to
@@ -16,7 +19,8 @@ prints the date and time according to
 or
 .Ar format
 using
-.Xr strftime 3 .
+.Xr strftime 3
+or sets the date.
 .Sh OPTIONS
 .Bl -tag -width Ds
 .It Fl d Ar time
@@ -27,6 +31,44 @@ Unix epoch 1970-01-01T00:00:00Z.
 .It Fl u
 Print UTC time instead of local time.
 .El
+.Pp
+An operand with a leading plus
+.Pq Cm +
+sign signals a user-defined format string using
+.Xr strftime 3
+conversion specifications.
+.Pp
+An operand without a leading plus sign is
+interpreted a value for setting the systems current date and time.
+The canonical representation for setting the date and time is:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar mm
+The month of the year, from 01 to 12.
+.It Ar dd
+The day of the month, from 01 to 31.
+.It Ar HH
+The hour of the day, from 00 to 23.
+.It Ar MM
+The minute of the hour, from 00 to 59.
+.It Ar CC
+The first two digits of the year (the century).
+.It Ar yy
+The second two digits of the year.
+If
+.Ar yy
+is specified, but
+.Ar CC
+is not, a value for
+.Ar yy
+between 69 and 99 results in a
+.Ar CC
+value of 19. Otherwise, a
+.Ar CC
+value of 20 is used.
+.El
+.Pp
+The century and year are optional. The default is the current year.
 .Sh STANDARDS
 The
 .Nm
diff --git a/date.c b/date.c
index 1671e1f..f3db89f 100644
--- a/date.c
+++ b/date.c
@@ -1,6 +1,8 @@
 /* See LICENSE file for copyright and license details. */
 #include 
 #include 
+#include 
+#include 
 #include 
 
 #include "util.h"
@@ -8,19 +10,20 @@
 static void
 usage(void)
 {
-   eprintf("usage: %s [-u] [-d time] [+format]\n", argv0);
+   eprintf("usage: %s [-u] [-d time] [+format] [mmddHHMM[[CC]yy]]\n", 
argv0);
 }
 
 int
 main(int argc, char *argv[])
 {
struct tm *now;
+   struct tm date;
struct tm *(*tztime)(const time_t *) = localtime;
+   struct timespec ts;
time_t t;
+   size_t i, j, len;
char buf[BUFSIZ], *fmt = "%c", *tz = "local";
 
-   t = time(NULL);
-
ARGBEGIN {
case 'd':
t = estrtonum(EARGF(usage()), 0, LLONG_MAX);
@@ -33,14 +36,62 @@ main(int argc, char *argv[])
usage();
} ARGEND
 
+   t = time(NULL);
+
+   if (!(now = tztime(&t)))
+   eprintf("%stime failed\n", tz);
+
if (argc) {
-   if (argc != 1 || argv[0][0] != '+')
+   if (argc != 1) {
usage();
-   else
+   } else if (argv[0][0] == '+') {
fmt = &argv[0][1];
+   } else {
+   len = strlen(argv[0]);
+
+   switch (len) {
+   case 8:
+   fmt = "%m %d %H %M";
+   break;
+   case 10:
+   fmt = "%m %d %H %M %y";
+   break;
+   case 12:
+   fmt = "%m %d %H %M %C %y";
+   break;
+   default:
+   eprintf("invalid date format: %s\n", argv[0]);
+   }
+
+   /* strptime does not handle the mmddHHMM[[CC]yy] format 
with
+* the leading zeroes for each specifier unless they 
are separated.
+*/
+   for (i = 0, j = 0; i < len; i++) {
+   buf[j++] = argv[0][i];
+   if (i && i < len-1 && (i%2))
+   buf[j++] = ' ';
+   }
+   buf[j] = '\0';
+
+   if (strptime(buf, fmt, &date) == NULL)
+   eprintf("strptime failed: invalid input: %s\n", 
argv[0]);
+
+   if (len == 8)
+   date.tm_year = now->tm_year;
+
+   t = mktime(&date);
+