summaryrefslogtreecommitdiffstats
path: root/shell_cmds/date
diff options
context:
space:
mode:
Diffstat (limited to 'shell_cmds/date')
-rw-r--r--shell_cmds/date/date.1472
-rw-r--r--shell_cmds/date/date.c365
-rw-r--r--shell_cmds/date/date.plist.part22
-rw-r--r--shell_cmds/date/extern.h35
-rw-r--r--shell_cmds/date/netdate.c181
-rw-r--r--shell_cmds/date/vary.c506
-rw-r--r--shell_cmds/date/vary.h36
7 files changed, 1617 insertions, 0 deletions
diff --git a/shell_cmds/date/date.1 b/shell_cmds/date/date.1
new file mode 100644
index 0000000..0cabb01
--- /dev/null
+++ b/shell_cmds/date/date.1
@@ -0,0 +1,472 @@
+.\"-
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)date.1 8.3 (Berkeley) 4/28/95
+.\" $FreeBSD$
+.\"
+.Dd May 7, 2015
+.Dt DATE 1
+.Os
+.Sh NAME
+.Nm date
+.Nd display or set date and time
+.Sh SYNOPSIS
+.Nm
+.Op Fl jRu
+.Op Fl r Ar seconds | Ar filename
+.Oo
+.Fl v
+.Sm off
+.Op Cm + | -
+.Ar val Op Ar ymwdHMS
+.Sm on
+.Oc
+.Ar ...
+.Op Cm + Ns Ar output_fmt
+.Nm
+.Op Fl jnu
+.Sm off
+.Op Oo Oo Ar mm Oc Ar dd Oc Ar HH
+.Ar MM Oo Oo Ar cc Oc Ar yy Oc Op Ar .ss
+.Sm on
+.Nm
+.Op Fl jnRu
+.Fl f Ar input_fmt new_date
+.Op Cm + Ns Ar output_fmt
+.Nm
+.Op Fl d Ar dst
+.Op Fl t Ar minutes_west
+.Sh DESCRIPTION
+When invoked without arguments, the
+.Nm
+utility displays the current date and time.
+Otherwise, depending on the options specified,
+.Nm
+will set the date and time or print it in a user-defined way.
+.Pp
+The
+.Nm
+utility displays the date and time read from the kernel clock.
+When used to set the date and time,
+both the kernel clock and the hardware clock are updated.
+.Pp
+Only the superuser may set the date,
+and if the system securelevel (see
+.Xr securelevel 7 )
+is greater than 1,
+the time may not be changed by more than 1 second.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d Ar dst
+Set the kernel's value for daylight saving time.
+If
+.Ar dst
+is non-zero, future calls
+to
+.Xr gettimeofday 2
+will return a non-zero for
+.Fa tz_dsttime .
+.It Fl f
+Use
+.Ar input_fmt
+as the format string to parse the
+.Ar new_date
+provided rather than using the default
+.Sm off
+.Oo Oo Oo
+.Ar mm Oc
+.Ar dd Oc
+.Ar HH Oc
+.Ar MM
+.Oo Oo
+.Ar cc Oc
+.Ar yy Oc Oo
+.Ar .ss Oc
+.Sm on
+format.
+Parsing is done using
+.Xr strptime 3 .
+.It Fl j
+Do not try to set the date.
+This allows you to use the
+.Fl f
+flag in addition to the
+.Cm +
+option to convert one date format to another.
+.It Fl n
+By default, if the
+.Xr timed 8
+daemon is running,
+.Nm
+sets the time on all of the machines in the local group.
+The
+.Fl n
+option suppresses this behavior and causes the time to be set only on the
+current machine.
+.It Fl R
+Use RFC 2822 date and time output format. This is equivalent to use
+.Dq Li %a, %d %b %Y \&%T %z
+as
+.Ar output_fmt
+while
+.Ev LC_TIME
+is set to the
+.Dq C
+locale .
+.It Fl r Ar seconds
+Print the date and time represented by
+.Ar seconds ,
+where
+.Ar seconds
+is the number of seconds since the Epoch
+(00:00:00 UTC, January 1, 1970;
+see
+.Xr time 3 ) ,
+and can be specified in decimal, octal, or hex.
+.It Fl r Ar filename
+Print the date and time of the last modification of
+.Ar filename .
+.It Fl t Ar minutes_west
+Set the system's value for minutes west of
+.Tn GMT .
+.Ar minutes_west
+specifies the number of minutes returned in
+.Fa tz_minuteswest
+by future calls to
+.Xr gettimeofday 2 .
+.It Fl u
+Display or set the date in
+.Tn UTC
+(Coordinated Universal) time.
+.It Fl v
+Adjust (i.e., take the current date and display the result of the
+adjustment; not actually set the date) the second, minute, hour, month
+day, week day, month or year according to
+.Ar val .
+If
+.Ar val
+is preceded with a plus or minus sign,
+the date is adjusted forwards or backwards according to the remaining string,
+otherwise the relevant part of the date is set.
+The date can be adjusted as many times as required using these flags.
+Flags are processed in the order given.
+.Pp
+When setting values
+(rather than adjusting them),
+seconds are in the range 0-59, minutes are in the range 0-59, hours are
+in the range 0-23, month days are in the range 1-31, week days are in the
+range 0-6 (Sun-Sat),
+months are in the range 1-12 (Jan-Dec)
+and years are in the range 80-38 or 1980-2038.
+.Pp
+If
+.Ar val
+is numeric, one of either
+.Ar y ,
+.Ar m ,
+.Ar w ,
+.Ar d ,
+.Ar H ,
+.Ar M
+or
+.Ar S
+must be used to specify which part of the date is to be adjusted.
+.Pp
+The week day or month may be specified using a name rather than a
+number.
+If a name is used with the plus
+(or minus)
+sign, the date will be put forwards
+(or backwards)
+to the next
+(previous)
+date that matches the given week day or month.
+This will not adjust the date,
+if the given week day or month is the same as the current one.
+.Pp
+When a date is adjusted to a specific value or in units greater than hours,
+daylight savings time considerations are ignored.
+Adjustments in units of hours or less honor daylight saving time.
+So, assuming the current date is March 26, 0:30 and that the DST adjustment
+means that the clock goes forward at 01:00 to 02:00, using
+.Fl v No +1H
+will adjust the date to March 26, 2:30.
+Likewise, if the date is October 29, 0:30 and the DST adjustment means that
+the clock goes back at 02:00 to 01:00, using
+.Fl v No +3H
+will be necessary to reach October 29, 2:30.
+.Pp
+When the date is adjusted to a specific value that does not actually exist
+(for example March 26, 1:30 BST 2000 in the Europe/London timezone),
+the date will be silently adjusted forwards in units of one hour until it
+reaches a valid time.
+When the date is adjusted to a specific value that occurs twice
+(for example October 29, 1:30 2000),
+the resulting timezone will be set so that the date matches the earlier of
+the two times.
+.Pp
+It is not possible to adjust a date to an invalid absolute day, so using
+the switches
+.Fl v No 31d Fl v No 12m
+will simply fail five months of the year.
+It is therefore usual to set the month before setting the day; using
+.Fl v No 12m Fl v No 31d
+always works.
+.Pp
+Adjusting the date by months is inherently ambiguous because
+a month is a unit of variable length depending on the current date.
+This kind of date adjustment is applied in the most intuitive way.
+First of all,
+.Nm
+tries to preserve the day of the month.
+If it is impossible because the target month is shorter than the present one,
+the last day of the target month will be the result.
+For example, using
+.Fl v No +1m
+on May 31 will adjust the date to June 30, while using the same option
+on January 30 will result in the date adjusted to the last day of February.
+This approach is also believed to make the most sense for shell scripting.
+Nevertheless, be aware that going forth and back by the same number of
+months may take you to a different date.
+.Pp
+Refer to the examples below for further details.
+.El
+.Pp
+An operand with a leading plus
+.Pq Sq +
+sign signals a user-defined format string
+which specifies the format in which to display the date and time.
+The format string may contain any of the conversion specifications
+described in the
+.Xr strftime 3
+manual page, as well as any arbitrary text.
+A newline
+.Pq Ql \en
+character is always output after the characters specified by
+the format string.
+The format string for the default display is
+.Dq +%+ .
+.Pp
+If an operand does not have a leading plus sign, it is interpreted as
+a value for setting the system's notion of the current date and time.
+The canonical representation for setting the date and time is:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It Ar cc
+Century
+(either 19 or 20)
+prepended to the abbreviated year.
+.It Ar yy
+Year in abbreviated form
+(e.g., 89 for 1989, 06 for 2006).
+.It Ar mm
+Numeric month, a number from 1 to 12.
+.It Ar dd
+Day, a number from 1 to 31.
+.It Ar HH
+Hour, a number from 0 to 23.
+.It Ar MM
+Minutes, a number from 0 to 59.
+.It Ar ss
+Seconds, a number from 0 to 61
+(59 plus a maximum of two leap seconds).
+.El
+.Pp
+Everything but the minutes is optional.
+.Pp
+Time changes for Daylight Saving Time, standard time, leap seconds,
+and leap years are handled automatically.
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width Ds
+.It Ev TZ
+The timezone to use when displaying dates.
+The normal format is a pathname relative to
+.Pa /usr/share/zoneinfo .
+For example, the command
+.Dq TZ=America/Los_Angeles date
+displays the current time in California.
+See
+.Xr environ 7
+for more information.
+.El
+.Sh FILES
+.Bl -tag -width /var/log/messages -compact
+.It Pa /var/log/messages
+record of the user setting the time
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 0 on success, 1 if unable to set the date, and 2
+if able to set the local date, but unable to set it globally.
+.Sh EXAMPLES
+The command:
+.Pp
+.Dl "date ""+DATE: %Y-%m-%d%nTIME: %H:%M:%S"""
+.Pp
+will display:
+.Bd -literal -offset indent
+DATE: 1987-11-21
+TIME: 13:36:16
+.Ed
+.Pp
+In the Europe/London timezone, the command:
+.Pp
+.Dl "date -v1m -v+1y"
+.Pp
+will display:
+.Pp
+.Dl "Sun Jan 4 04:15:24 GMT 1998"
+.Pp
+where it is currently
+.Li "Mon Aug 4 04:15:24 BST 1997" .
+.Pp
+The command:
+.Pp
+.Dl "date -v1d -v3m -v0y -v-1d"
+.Pp
+will display the last day of February in the year 2000:
+.Pp
+.Dl "Tue Feb 29 03:18:00 GMT 2000"
+.Pp
+So will the command:
+.Pp
+.Dl "date -v3m -v30d -v0y -v-1m"
+.Pp
+because there is no such date as the 30th of February.
+.Pp
+The command:
+.Pp
+.Dl "date -v1d -v+1m -v-1d -v-fri"
+.Pp
+will display the last Friday of the month:
+.Pp
+.Dl "Fri Aug 29 04:31:11 BST 1997"
+.Pp
+where it is currently
+.Li "Mon Aug 4 04:31:11 BST 1997" .
+.Pp
+The command:
+.Pp
+.Dl "date 0613162785"
+.Pp
+sets the date to
+.Dq Li "June 13, 1985, 4:27 PM" .
+.Pp
+.Dl "date ""+%m%d%H%M%Y.%S"""
+.Pp
+may be used on one machine to print out the date
+suitable for setting on another.
+.Pp
+The command:
+.Pp
+.Dl "date 1432"
+.Pp
+sets the time to
+.Li "2:32 PM" ,
+without modifying the date.
+.Pp
+Finally the command:
+.Pp
+.Dl "date -j -f ""%a %b %d %T %Z %Y"" ""`date`"" ""+%s"""
+.Pp
+can be used to parse the output from
+.Nm
+and express it in Epoch time.
+.Sh DIAGNOSTICS
+Occasionally, when
+.Xr timed 8
+synchronizes the time on many hosts, the setting of a new time value may
+require more than a few seconds.
+On these occasions,
+.Nm
+prints:
+.Ql Network time being set .
+The message
+.Ql Communication error with timed
+occurs when the communication
+between
+.Nm
+and
+.Xr timed 8
+fails.
+.Sh LEGACY SYNOPSIS
+As above, except for the second line, which is:
+.Pp
+.Nm
+.Op Fl jnu
+.Sm off
+.Op Oo Oo Oo Oo Ar cc Oc Ar yy Oc Ar mm Oc Ar dd Oc Ar HH
+.Ar MM Op Ar .ss
+.Sm on
+.Sh LEGACY DIAGNOSTICS
+When invoked in legacy mode, the following exit values are returned:
+.Bl -tag -width X -compact
+.It 0
+The date was written successfully
+.It 1
+Unable to set the date
+.It 2
+Able to set the local date, but unable to set it globally
+.El
+.Pp
+For more information about legacy mode, see
+.Xr compat 5 .
+.Sh SEE ALSO
+.Xr locale 1 ,
+.Xr gettimeofday 2 ,
+.Xr getutxent 3 ,
+.Xr strftime 3 ,
+.Xr strptime 3 ,
+.Xr timed 8
+.Rs
+.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD"
+.%A R. Gusella
+.%A S. Zatti
+.Re
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be compatible with
+.St -p1003.2 .
+The
+.Fl d , f , j , n , r , t ,
+and
+.Fl v
+options are all extensions to the standard.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/shell_cmds/date/date.c b/shell_cmds/date/date.c
new file mode 100644
index 0000000..a45e110
--- /dev/null
+++ b/shell_cmds/date/date.c
@@ -0,0 +1,365 @@
+/*-
+ * Copyright (c) 1985, 1987, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char const copyright[] =
+"@(#) Copyright (c) 1985, 1987, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <utmpx.h>
+
+#ifdef __APPLE__
+#include <get_compat.h>
+#else
+#define COMPAT_MODE(a,b) (1)
+#endif /* __APPLE__ */
+
+#include "extern.h"
+#include "vary.h"
+
+#ifndef TM_YEAR_BASE
+#define TM_YEAR_BASE 1900
+#endif
+
+#ifdef __APPLE__
+#define st_mtim st_mtimespec
+#endif
+
+static time_t tval;
+int retval;
+static int unix2003_std; /* to determine legacy vs std mode */
+
+static void setthetime(const char *, const char *, int, int);
+static void badformat(void);
+static void usage(void);
+
+static const char *rfc2822_format = "%a, %d %b %Y %T %z";
+
+int
+main(int argc, char *argv[])
+{
+ struct timezone tz;
+ int ch, rflag;
+ int jflag, nflag, Rflag;
+ const char *format;
+ char buf[1024];
+ char *endptr, *fmt;
+ char *tmp;
+ int set_timezone;
+ struct vary *v;
+ const struct vary *badv;
+ struct tm lt;
+ struct stat sb;
+
+ unix2003_std = COMPAT_MODE("bin/date", "unix2003"); /* Determine the STD */
+
+ v = NULL;
+ fmt = NULL;
+ (void) setlocale(LC_TIME, "");
+ tz.tz_dsttime = tz.tz_minuteswest = 0;
+ rflag = 0;
+ jflag = nflag = Rflag = 0;
+ set_timezone = 0;
+ while ((ch = getopt(argc, argv, "d:f:jnRr:t:uv:")) != -1)
+ switch((char)ch) {
+ case 'd': /* daylight savings time */
+ tz.tz_dsttime = strtol(optarg, &endptr, 10) ? 1 : 0;
+ if (endptr == optarg || *endptr != '\0')
+ usage();
+ set_timezone = 1;
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'j':
+ jflag = 1; /* don't set time */
+ break;
+ case 'n': /* don't set network */
+ nflag = 1;
+ break;
+ case 'R': /* RFC 2822 datetime format */
+ Rflag = 1;
+ break;
+ case 'r': /* user specified seconds */
+ rflag = 1;
+ tval = strtoq(optarg, &tmp, 0);
+ if (*tmp != 0) {
+ if (stat(optarg, &sb) == 0)
+ tval = sb.st_mtim.tv_sec;
+ else
+ usage();
+ }
+ break;
+ case 't': /* minutes west of UTC */
+ /* error check; don't allow "PST" */
+ tz.tz_minuteswest = strtol(optarg, &endptr, 10);
+ if (endptr == optarg || *endptr != '\0')
+ usage();
+ set_timezone = 1;
+ break;
+ case 'u': /* do everything in UTC */
+ (void)setenv("TZ", "UTC0", 1);
+ break;
+ case 'v':
+ v = vary_append(v, optarg);
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * If -d or -t, set the timezone or daylight savings time; this
+ * doesn't belong here; the kernel should not know about either.
+ */
+ if (set_timezone && settimeofday(NULL, &tz) != 0)
+ err(1, "settimeofday (timezone)");
+
+ if (!rflag && time(&tval) == -1)
+ err(1, "time");
+
+ format = "%+";
+
+ if (Rflag)
+ format = rfc2822_format;
+
+ /* allow the operands in any order */
+ if (*argv && **argv == '+') {
+ format = *argv + 1;
+ ++argv;
+ }
+
+ if (*argv) {
+ setthetime(fmt, *argv, jflag, nflag);
+ ++argv;
+ } else if (fmt != NULL)
+ usage();
+
+ if (*argv && **argv == '+')
+ format = *argv + 1;
+
+#ifdef __APPLE__
+ /* 7999711 */
+ struct tm *ltp = localtime(&tval);
+ if (ltp == NULL) {
+ err(1, "localtime");
+ }
+ lt = *ltp;
+#else
+ lt = *localtime(&tval);
+#endif
+ badv = vary_apply(v, &lt);
+ if (badv) {
+ fprintf(stderr, "%s: Cannot apply date adjustment\n",
+ badv->arg);
+ vary_destroy(v);
+ usage();
+ }
+ vary_destroy(v);
+
+ if (format == rfc2822_format)
+ /*
+ * When using RFC 2822 datetime format, don't honor the
+ * locale.
+ */
+ setlocale(LC_TIME, "C");
+
+ (void)strftime(buf, sizeof(buf), format, &lt);
+ (void)printf("%s\n", buf);
+ if (fflush(stdout))
+ err(1, "stdout");
+ /*
+ * If date/time could not be set/notified in the other hosts as
+ * determined by netsetval(), a return value 2 is set, which is
+ * only propagated back to shell in legacy mode.
+ */
+ if (unix2003_std)
+ exit(0);
+ exit(retval);
+}
+
+#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
+#define ATOI2_OFFSET(s, o) (((s)[o] - '0') * 10 + ((s)[o + 1] - '0'))
+
+static void
+setthetime(const char *fmt, const char *p, int jflag, int nflag)
+{
+ struct utmpx utx;
+ struct tm *lt;
+ struct timeval tv;
+ const char *dot, *t;
+ int century;
+ size_t length;
+
+ lt = localtime(&tval);
+ lt->tm_isdst = -1; /* divine correct DST */
+
+ if (fmt != NULL) {
+ t = strptime(p, fmt, lt);
+ if (t == NULL) {
+ fprintf(stderr, "Failed conversion of ``%s''"
+ " using format ``%s''\n", p, fmt);
+ badformat();
+ } else if (*t != '\0')
+ fprintf(stderr, "Warning: Ignoring %ld extraneous"
+ " characters in date string (%s)\n",
+ (long) strlen(t), t);
+ } else {
+ for (t = p, dot = NULL; *t; ++t) {
+ if (isdigit(*t))
+ continue;
+ if (*t == '.' && dot == NULL) {
+ dot = t;
+ continue;
+ }
+ badformat();
+ }
+
+ if (dot != NULL) { /* .ss */
+ dot++; /* *dot++ = '\0'; */
+ if (strlen(dot) != 2)
+ badformat();
+ lt->tm_sec = ATOI2(dot);
+ if (lt->tm_sec > 61)
+ badformat();
+ } else
+ lt->tm_sec = 0;
+
+ century = 0;
+ /* if p has a ".ss" field then let's pretend it's not there */
+ switch (length = strlen(p) - ((dot != NULL) ? 3 : 0)) {
+ case 12: /* cc */
+ lt->tm_year = (unix2003_std ? ATOI2_OFFSET(p, length - 4) : ATOI2(p)) * 100 - TM_YEAR_BASE;
+ century = 1;
+ /* FALLTHROUGH */
+ case 10: /* yy */
+ if (century)
+ lt->tm_year += (unix2003_std ? ATOI2_OFFSET(p, length - 2) : ATOI2(p));
+ else {
+ lt->tm_year = (unix2003_std ? ATOI2_OFFSET(p, length - 2) : ATOI2(p));
+ if (lt->tm_year < 69) /* hack for 2000 ;-} */
+ lt->tm_year += 2000 - TM_YEAR_BASE;
+ else
+ lt->tm_year += 1900 - TM_YEAR_BASE;
+ }
+ /* FALLTHROUGH */
+ case 8: /* mm */
+ lt->tm_mon = ATOI2(p);
+ if (lt->tm_mon > 12)
+ badformat();
+ --lt->tm_mon; /* time struct is 0 - 11 */
+ /* FALLTHROUGH */
+ case 6: /* dd */
+ lt->tm_mday = ATOI2(p);
+ if (lt->tm_mday > 31)
+ badformat();
+ /* FALLTHROUGH */
+ case 4: /* HH */
+ lt->tm_hour = ATOI2(p);
+ if (lt->tm_hour > 23)
+ badformat();
+ /* FALLTHROUGH */
+ case 2: /* MM */
+ lt->tm_min = ATOI2(p);
+ if (lt->tm_min > 59)
+ badformat();
+ break;
+ default:
+ badformat();
+ }
+ }
+
+ /* convert broken-down time to GMT clock time */
+ if ((tval = mktime(lt)) == -1)
+ errx(1, "nonexistent time");
+
+ if (!jflag) {
+ /* set the time */
+ if (nflag || netsettime(tval)) {
+ utx.ut_type = OLD_TIME;
+ (void)gettimeofday(&utx.ut_tv, NULL);
+ pututxline(&utx);
+ tv.tv_sec = tval;
+ tv.tv_usec = 0;
+ if (settimeofday(&tv, NULL) != 0)
+ err(1, "settimeofday (timeval)");
+ utx.ut_type = NEW_TIME;
+ (void)gettimeofday(&utx.ut_tv, NULL);
+ pututxline(&utx);
+ }
+
+ if ((p = getlogin()) == NULL)
+ p = "???";
+ syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p);
+ }
+}
+
+static void
+badformat(void)
+{
+ warnx("illegal time format");
+ usage();
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: date [-jnRu] [-d dst] [-r seconds] [-t west] "
+ "[-v[+|-]val[ymwdHMS]] ... ",
+ unix2003_std ?
+ " "
+ "[-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]" :
+ " "
+ "[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]");
+ exit(1);
+}
diff --git a/shell_cmds/date/date.plist.part b/shell_cmds/date/date.plist.part
new file mode 100644
index 0000000..da97d84
--- /dev/null
+++ b/shell_cmds/date/date.plist.part
@@ -0,0 +1,22 @@
+ <dict>
+ <key>OpenSourceProject</key>
+ <string>date</string>
+ <key>OpenSourceVersion</key>
+ <string>2015-05-07</string>
+ <key>OpenSourceWebsiteURL</key>
+ <string>http://svnweb.freebsd.org/base/head/bin/date/</string>
+ <key>OpenSourceSCM</key>
+ <string>svn co http://svn.freebsd.org/base/head/bin/date/</string>
+ <key>OpenSourceImportDate</key>
+ <string>2015-11-06</string>
+ <key>OpenSourceModifications</key>
+ <array>
+ <string>date.1: discuss conformance changes</string>
+ <string>date.1: remove bogus utmpx path</string>
+ <string>date.c: conformance changes for date format and exit value</string>
+ <string>date.c: fix crash on invalid dates (7999711)</string>
+ <string>date.c: st_mtim compatibility</string>
+ </array>
+ <key>OpenSourceLicense</key>
+ <string>bsd</string>
+ </dict>
diff --git a/shell_cmds/date/extern.h b/shell_cmds/date/extern.h
new file mode 100644
index 0000000..91aeab2
--- /dev/null
+++ b/shell_cmds/date/extern.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 5/31/93
+ * $FreeBSD$
+ */
+
+extern int retval;
+
+int netsettime(time_t);
diff --git a/shell_cmds/date/netdate.c b/shell_cmds/date/netdate.c
new file mode 100644
index 0000000..e506e6d
--- /dev/null
+++ b/shell_cmds/date/netdate.c
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)netdate.c 8.1 (Berkeley) 5/31/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+#define TSPTYPES
+#include <protocols/timed.h>
+
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#define WAITACK 2 /* seconds */
+#define WAITDATEACK 5 /* seconds */
+
+/*
+ * Set the date in the machines controlled by timedaemons by communicating the
+ * new date to the local timedaemon. If the timedaemon is in the master state,
+ * it performs the correction on all slaves. If it is in the slave state, it
+ * notifies the master that a correction is needed.
+ * Returns 0 on success. Returns > 0 on failure, setting retval to 2;
+ */
+int
+netsettime(time_t tval)
+{
+ struct timeval tout;
+ struct servent *sp;
+ struct tsp msg;
+ struct sockaddr_in lsin, dest, from;
+ fd_set ready;
+ long waittime;
+ int s, port, timed_ack, found, lerr;
+ socklen_t length;
+ char hostname[MAXHOSTNAMELEN];
+
+ if ((sp = getservbyname("timed", "udp")) == NULL) {
+ warnx("timed/udp: unknown service");
+ return (retval = 2);
+ }
+
+ dest.sin_port = sp->s_port;
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY);
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ if (errno != EAFNOSUPPORT)
+ warn("timed");
+ return (retval = 2);
+ }
+
+ memset(&lsin, 0, sizeof(lsin));
+ lsin.sin_family = AF_INET;
+ for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
+ lsin.sin_port = htons((u_short)port);
+ if (bind(s, (struct sockaddr *)&lsin, sizeof(lsin)) >= 0)
+ break;
+ if (errno == EADDRINUSE)
+ continue;
+ if (errno != EADDRNOTAVAIL)
+ warn("bind");
+ goto bad;
+ }
+ if (port == IPPORT_RESERVED / 2) {
+ warnx("all ports in use");
+ goto bad;
+ }
+ memset(&msg, 0, sizeof(msg));
+ msg.tsp_type = TSP_SETDATE;
+ msg.tsp_vers = TSPVERSION;
+ if (gethostname(hostname, sizeof(hostname))) {
+ warn("gethostname");
+ goto bad;
+ }
+ (void)strlcpy(msg.tsp_name, hostname, sizeof(msg.tsp_name));
+ msg.tsp_seq = htons((u_short)0);
+ msg.tsp_time.tv_sec = htonl((u_long)tval);
+ msg.tsp_time.tv_usec = htonl((u_long)0);
+ length = sizeof(struct sockaddr_in);
+ if (connect(s, (struct sockaddr *)&dest, length) < 0) {
+ warn("connect");
+ goto bad;
+ }
+ if (send(s, (char *)&msg, sizeof(struct tsp), 0) < 0) {
+ if (errno != ECONNREFUSED)
+ warn("send");
+ goto bad;
+ }
+
+ timed_ack = -1;
+ waittime = WAITACK;
+loop:
+ tout.tv_sec = waittime;
+ tout.tv_usec = 0;
+
+ FD_ZERO(&ready);
+ FD_SET(s, &ready);
+ found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout);
+
+ length = sizeof(lerr);
+ if (!getsockopt(s,
+ SOL_SOCKET, SO_ERROR, (char *)&lerr, &length) && lerr) {
+ if (lerr != ECONNREFUSED)
+ warnc(lerr, "send (delayed error)");
+ goto bad;
+ }
+
+ if (found > 0 && FD_ISSET(s, &ready)) {
+ length = sizeof(struct sockaddr_in);
+ if (recvfrom(s, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr *)&from, &length) < 0) {
+ if (errno != ECONNREFUSED)
+ warn("recvfrom");
+ goto bad;
+ }
+ msg.tsp_seq = ntohs(msg.tsp_seq);
+ msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec);
+ msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec);
+ switch (msg.tsp_type) {
+ case TSP_ACK:
+ timed_ack = TSP_ACK;
+ waittime = WAITDATEACK;
+ goto loop;
+ case TSP_DATEACK:
+ (void)close(s);
+ return (0);
+ default:
+ warnx("wrong ack received from timed: %s",
+ tsptype[msg.tsp_type]);
+ timed_ack = -1;
+ break;
+ }
+ }
+ if (timed_ack == -1)
+ warnx("can't reach time daemon, time set locally");
+
+bad:
+ (void)close(s);
+ return (retval = 2);
+}
diff --git a/shell_cmds/date/vary.c b/shell_cmds/date/vary.c
new file mode 100644
index 0000000..5f01231
--- /dev/null
+++ b/shell_cmds/date/vary.c
@@ -0,0 +1,506 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include "vary.h"
+
+struct trans {
+ int val;
+ const char *str;
+};
+
+static struct trans trans_mon[] = {
+ { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" },
+ { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" },
+ { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" },
+ { -1, NULL }
+};
+
+static struct trans trans_wday[] = {
+ { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" },
+ { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" },
+ { -1, NULL }
+};
+
+static char digits[] = "0123456789";
+static int adjhour(struct tm *, char, int, int);
+
+static int
+domktime(struct tm *t, char type)
+{
+ time_t ret;
+
+ while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138)
+ /* While mktime() fails, adjust by an hour */
+ adjhour(t, type == '-' ? type : '+', 1, 0);
+
+ return ret;
+}
+
+static int
+trans(const struct trans t[], const char *arg)
+{
+ int f;
+
+ for (f = 0; t[f].val != -1; f++)
+ if (!strncasecmp(t[f].str, arg, 3) ||
+ !strncasecmp(t[f].str, arg, strlen(t[f].str)))
+ return t[f].val;
+
+ return -1;
+}
+
+struct vary *
+vary_append(struct vary *v, char *arg)
+{
+ struct vary *result, **nextp;
+
+ if (v) {
+ result = v;
+ while (v->next)
+ v = v->next;
+ nextp = &v->next;
+ } else
+ nextp = &result;
+
+ if ((*nextp = (struct vary *)malloc(sizeof(struct vary))) == NULL)
+ err(1, "malloc");
+ (*nextp)->arg = arg;
+ (*nextp)->next = NULL;
+ return result;
+}
+
+static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+static int
+daysinmonth(const struct tm *t)
+{
+ int year;
+
+ year = t->tm_year + 1900;
+
+ if (t->tm_mon == 1)
+ if (!(year % 400))
+ return 29;
+ else if (!(year % 100))
+ return 28;
+ else if (!(year % 4))
+ return 29;
+ else
+ return 28;
+ else if (t->tm_mon >= 0 && t->tm_mon < 12)
+ return mdays[t->tm_mon];
+
+ return 0;
+}
+
+
+static int
+adjyear(struct tm *t, char type, int val, int mk)
+{
+ switch (type) {
+ case '+':
+ t->tm_year += val;
+ break;
+ case '-':
+ t->tm_year -= val;
+ break;
+ default:
+ t->tm_year = val;
+ if (t->tm_year < 69)
+ t->tm_year += 100; /* as per date.c */
+ else if (t->tm_year > 1900)
+ t->tm_year -= 1900; /* struct tm holds years since 1900 */
+ break;
+ }
+ return !mk || domktime(t, type) != -1;
+}
+
+static int
+adjmon(struct tm *t, char type, int val, int istext, int mk)
+{
+ int lmdays;
+
+ if (val < 0)
+ return 0;
+
+ switch (type) {
+ case '+':
+ if (istext) {
+ if (val <= t->tm_mon)
+ val += 11 - t->tm_mon; /* early next year */
+ else
+ val -= t->tm_mon + 1; /* later this year */
+ }
+ if (val) {
+ if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0))
+ return 0;
+ val %= 12;
+ t->tm_mon += val;
+ if (t->tm_mon > 11)
+ t->tm_mon -= 12;
+ }
+ break;
+
+ case '-':
+ if (istext) {
+ if (val-1 > t->tm_mon)
+ val = 13 - val + t->tm_mon; /* later last year */
+ else
+ val = t->tm_mon - val + 1; /* early this year */
+ }
+ if (val) {
+ if (!adjyear(t, '-', val / 12, 0))
+ return 0;
+ val %= 12;
+ if (val > t->tm_mon) {
+ if (!adjyear(t, '-', 1, 0))
+ return 0;
+ val -= 12;
+ }
+ t->tm_mon -= val;
+ }
+ break;
+
+ default:
+ if (val > 12 || val < 1)
+ return 0;
+ t->tm_mon = --val;
+ }
+
+ /* e.g., -v-1m on March, 31 is the last day of February in common sense */
+ lmdays = daysinmonth(t);
+ if (t->tm_mday > lmdays)
+ t->tm_mday = lmdays;
+
+ return !mk || domktime(t, type) != -1;
+}
+
+static int
+adjday(struct tm *t, char type, int val, int mk)
+{
+ int lmdays;
+
+ switch (type) {
+ case '+':
+ while (val) {
+ lmdays = daysinmonth(t);
+ if (val > lmdays - t->tm_mday) {
+ val -= lmdays - t->tm_mday + 1;
+ t->tm_mday = 1;
+ if (!adjmon(t, '+', 1, 0, 0))
+ return 0;
+ } else {
+ t->tm_mday += val;
+ val = 0;
+ }
+ }
+ break;
+ case '-':
+ while (val)
+ if (val >= t->tm_mday) {
+ val -= t->tm_mday;
+ t->tm_mday = 1;
+ if (!adjmon(t, '-', 1, 0, 0))
+ return 0;
+ t->tm_mday = daysinmonth(t);
+ } else {
+ t->tm_mday -= val;
+ val = 0;
+ }
+ break;
+ default:
+ if (val > 0 && val <= daysinmonth(t))
+ t->tm_mday = val;
+ else
+ return 0;
+ break;
+ }
+
+ return !mk || domktime(t, type) != -1;
+}
+
+static int
+adjwday(struct tm *t, char type, int val, int istext, int mk)
+{
+ if (val < 0)
+ return 0;
+
+ switch (type) {
+ case '+':
+ if (istext)
+ if (val < t->tm_wday)
+ val = 7 - t->tm_wday + val; /* early next week */
+ else
+ val -= t->tm_wday; /* later this week */
+ else
+ val *= 7; /* "-v+5w" == "5 weeks in the future" */
+ return !val || adjday(t, '+', val, mk);
+ case '-':
+ if (istext) {
+ if (val > t->tm_wday)
+ val = 7 - val + t->tm_wday; /* later last week */
+ else
+ val = t->tm_wday - val; /* early this week */
+ } else
+ val *= 7; /* "-v-5w" == "5 weeks ago" */
+ return !val || adjday(t, '-', val, mk);
+ default:
+ if (val < t->tm_wday)
+ return adjday(t, '-', t->tm_wday - val, mk);
+ else if (val > 6)
+ return 0;
+ else if (val > t->tm_wday)
+ return adjday(t, '+', val - t->tm_wday, mk);
+ }
+ return 1;
+}
+
+static int
+adjhour(struct tm *t, char type, int val, int mk)
+{
+ if (val < 0)
+ return 0;
+
+ switch (type) {
+ case '+':
+ if (val) {
+ int days;
+
+ days = (t->tm_hour + val) / 24;
+ val %= 24;
+ t->tm_hour += val;
+ t->tm_hour %= 24;
+ if (!adjday(t, '+', days, 0))
+ return 0;
+ }
+ break;
+
+ case '-':
+ if (val) {
+ int days;
+
+ days = val / 24;
+ val %= 24;
+ if (val > t->tm_hour) {
+ days++;
+ val -= 24;
+ }
+ t->tm_hour -= val;
+ if (!adjday(t, '-', days, 0))
+ return 0;
+ }
+ break;
+
+ default:
+ if (val > 23)
+ return 0;
+ t->tm_hour = val;
+ }
+
+ return !mk || domktime(t, type) != -1;
+}
+
+static int
+adjmin(struct tm *t, char type, int val, int mk)
+{
+ if (val < 0)
+ return 0;
+
+ switch (type) {
+ case '+':
+ if (val) {
+ if (!adjhour(t, '+', (t->tm_min + val) / 60, 0))
+ return 0;
+ val %= 60;
+ t->tm_min += val;
+ if (t->tm_min > 59)
+ t->tm_min -= 60;
+ }
+ break;
+
+ case '-':
+ if (val) {
+ if (!adjhour(t, '-', val / 60, 0))
+ return 0;
+ val %= 60;
+ if (val > t->tm_min) {
+ if (!adjhour(t, '-', 1, 0))
+ return 0;
+ val -= 60;
+ }
+ t->tm_min -= val;
+ }
+ break;
+
+ default:
+ if (val > 59)
+ return 0;
+ t->tm_min = val;
+ }
+
+ return !mk || domktime(t, type) != -1;
+}
+
+static int
+adjsec(struct tm *t, char type, int val, int mk)
+{
+ if (val < 0)
+ return 0;
+
+ switch (type) {
+ case '+':
+ if (val) {
+ if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0))
+ return 0;
+ val %= 60;
+ t->tm_sec += val;
+ if (t->tm_sec > 59)
+ t->tm_sec -= 60;
+ }
+ break;
+
+ case '-':
+ if (val) {
+ if (!adjmin(t, '-', val / 60, 0))
+ return 0;
+ val %= 60;
+ if (val > t->tm_sec) {
+ if (!adjmin(t, '-', 1, 0))
+ return 0;
+ val -= 60;
+ }
+ t->tm_sec -= val;
+ }
+ break;
+
+ default:
+ if (val > 59)
+ return 0;
+ t->tm_sec = val;
+ }
+
+ return !mk || domktime(t, type) != -1;
+}
+
+const struct vary *
+vary_apply(const struct vary *v, struct tm *t)
+{
+ char type;
+ char which;
+ char *arg;
+ size_t len;
+ int val;
+
+ for (; v; v = v->next) {
+ type = *v->arg;
+ arg = v->arg;
+ if (type == '+' || type == '-')
+ arg++;
+ else
+ type = '\0';
+ len = strlen(arg);
+ if (len < 2)
+ return v;
+
+ if (type == '\0')
+ t->tm_isdst = -1;
+
+ if (strspn(arg, digits) != len-1) {
+ val = trans(trans_wday, arg);
+ if (val != -1) {
+ if (!adjwday(t, type, val, 1, 1))
+ return v;
+ } else {
+ val = trans(trans_mon, arg);
+ if (val != -1) {
+ if (!adjmon(t, type, val, 1, 1))
+ return v;
+ } else
+ return v;
+ }
+ } else {
+ val = atoi(arg);
+ which = arg[len-1];
+
+ switch (which) {
+ case 'S':
+ if (!adjsec(t, type, val, 1))
+ return v;
+ break;
+ case 'M':
+ if (!adjmin(t, type, val, 1))
+ return v;
+ break;
+ case 'H':
+ if (!adjhour(t, type, val, 1))
+ return v;
+ break;
+ case 'd':
+ t->tm_isdst = -1;
+ if (!adjday(t, type, val, 1))
+ return v;
+ break;
+ case 'w':
+ t->tm_isdst = -1;
+ if (!adjwday(t, type, val, 0, 1))
+ return v;
+ break;
+ case 'm':
+ t->tm_isdst = -1;
+ if (!adjmon(t, type, val, 0, 1))
+ return v;
+ break;
+ case 'y':
+ t->tm_isdst = -1;
+ if (!adjyear(t, type, val, 1))
+ return v;
+ break;
+ default:
+ return v;
+ }
+ }
+ }
+ return 0;
+}
+
+void
+vary_destroy(struct vary *v)
+{
+ struct vary *n;
+
+ while (v) {
+ n = v->next;
+ free(v);
+ v = n;
+ }
+}
diff --git a/shell_cmds/date/vary.h b/shell_cmds/date/vary.h
new file mode 100644
index 0000000..b39306a
--- /dev/null
+++ b/shell_cmds/date/vary.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct vary {
+ char *arg;
+ struct vary *next;
+};
+
+extern struct vary *vary_append(struct vary *v, char *arg);
+extern const struct vary *vary_apply(const struct vary *v, struct tm *t);
+extern void vary_destroy(struct vary *v);