is done via a modified version of Glibc's getdate(3).
lib/t-arcfour
lib/t-charset
lib/t-event
+lib/t-dateparse
Portions extracted from MPG321, http://mpg321.sourceforge.net/
Copyright (C) 2001 Joe Drew
Copyright (C) 2000-2001 Robert Leslie
-Portions Copyright (C) 2000, 2001, 2002, 2003, 2005, 2006 Free Software
-Foundation, Inc.
+Portions Copyright (C) 1997-2006 Free Software Foundation, Inc.
Binaries may derive extra copyright owners through linkage (binary distributors
are expected to do their own legwork)
#include "authorize.h"
#include "vector.h"
#include "version.h"
+#include "dateparse.h"
static disorder_client *client;
static void cf_schedule_play(char **argv) {
if(disorder_schedule_add(getclient(),
- atoll(argv[0]),
+ dateparse(argv[0]),
argv[1],
"play",
argv[2]))
static void cf_schedule_set_global(char **argv) {
if(disorder_schedule_add(getclient(),
- atoll(argv[0]),
+ dateparse(argv[0]),
argv[1],
"set-global",
argv[2],
static void cf_schedule_unset_global(char **argv) {
if(disorder_schedule_add(getclient(),
- atoll(argv[0]),
+ dateparse(argv[0]),
argv[1],
"set-global",
argv[2],
pcre_malloc = xmalloc;
pcre_free = xfree;
if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+ if(!setlocale(LC_TIME, "")) fatal(errno, "error calling setlocale");
while((n = getopt_long(argc, argv, "+hVc:dHlNu:p:", options, 0)) >= 0) {
switch(n) {
case 'h': help();
the user, the action and action-specific data.
.TP
.B schedule-play \fIWHEN PRIORITY TRACK\fI
-Play \fITRACK\fR at time \fIWHEN\fR (which must currently be a raw \fBtime_t\fR
-value).
+Play \fITRACK\fR at time \fIWHEN\fR.
+Various time/date formats are supported depending on locale but the following
+three will always work:
+.RS
+.RS
+.TP
+.B "YYYY-MM-DD HH:MM:SS"
+.TP
+.B "HH:MM:SS"
+.TP
+.B "HH:MM"
+.RE
+.RE
.IP
\fIPRIORITY\fR should be \fBjunk\fR or \fBnormal\fR.
This determines how the event is handled if it becomes due when the server is
#
TESTS=t-addr t-arcfour t-basen t-bits t-cache t-casefold t-charset \
- t-cookies t-event t-filepart t-hash t-heap t-hex t-kvp t-mime \
- t-printf t-regsub t-selection t-signame t-sink t-split \
- t-syscalls t-trackname t-unicode t-url t-utf8 t-vector t-words \
- t-wstat t-macros t-cgi
+ t-cookies t-dateparse t-event t-filepart t-hash t-heap t-hex \
+ t-kvp t-mime t-printf t-regsub t-selection t-signame t-sink \
+ t-split t-syscalls t-trackname t-unicode t-url t-utf8 t-vector \
+ t-words t-wstat t-macros t-cgi
noinst_LIBRARIES=libdisorder.a
include_HEADERS=disorder.h
client-common.c client-common.h \
configuration.c configuration.h \
cookies.c cookies.h \
+ dateparse.c dateparse.h xgetdate.c \
defs.c defs.h \
eclient.c eclient.h \
event.c event.h \
t_cookies_LDADD=libdisorder.a $(LIBPCRE) $(LIBICONV) $(LIBGC)
t_cookies_DEPENDENCIES=libdisorder.a
+t_dateparse_SOURCES=t-dateparse.c test.c test.h
+t_dateparse_LDADD=libdisorder.a $(LIBPCRE) $(LIBICONV) $(LIBGC)
+t_dateparse_DEPENDENCIES=libdisorder.a
+
t_event_SOURCES=t-event.c test.c test.h
t_event_LDADD=libdisorder.a $(LIBPCRE) $(LIBICONV) $(LIBGC)
t_event_DEPENDENCIES=libdisorder.a
--- /dev/null
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2008 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/** @file lib/dateparse.c
+ * @brief Date parsing
+ */
+
+#include <config.h>
+#include "types.h"
+
+#include <time.h>
+
+#include "dateparse.h"
+#include "log.h"
+
+/** @brief Date parsing patterns
+ *
+ * This set of patterns is designed to parse a specific time of a specific day,
+ * since that's what the scheduler needs. Other requirements might need other
+ * pattern lists.
+ */
+static const char *const datemsk[] = {
+ /* ISO format */
+ "%Y-%m-%d %H:%M:%S",
+ /* "%Y-%m-%d %H:%M:%S %Z" - no, not sensibly supported anywhere */
+ /* Locale-specific date + time */
+ "%c",
+ "%Ec",
+ /* Locale-specific time, same day */
+ "%X",
+ "%EX",
+ /* Generic time, same day */
+ "%H:%M",
+ "%H:%M:%S",
+ NULL,
+};
+
+/** @brief Convert string to a @c time_t */
+time_t dateparse(const char *s) {
+ struct tm t;
+ int rc;
+
+ switch(rc = xgetdate_r(s, &t, datemsk)) {
+ case 0:
+ return mktime(&t);
+ case 7:
+ fatal(0, "date string '%s' not in a recognized format", s);
+ case 8:
+ fatal(0, "date string '%s' not representable", s);
+ default:
+ fatal(0, "date string '%s' produced unexpected error %d", s, rc);
+ }
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
--- /dev/null
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2008 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+/** @file lib/dateparse.h
+ * @brief Date parsing
+ */
+
+time_t dateparse(const char *s);
+struct tm *xgetdate(const char *string,
+ const char *const *template);
+int xgetdate_r(const char *string,
+ struct tm *tp,
+ const char *const *template);
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
--- /dev/null
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2008 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+#include "test.h"
+#include <time.h>
+#include "dateparse.h"
+
+static void check_date(time_t when,
+ const char *format,
+ struct tm *(*convert)(const time_t *)) {
+ char buffer[128];
+ time_t parsed;
+
+ strftime(buffer, sizeof buffer, format, convert(&when));
+ parsed = dateparse(buffer);
+ check_integer(parsed, when);
+ if(parsed != when)
+ fprintf(stderr, "format=%s formatted=%s\n", format, buffer);
+}
+
+static void test_dateparse(void) {
+ time_t now = time(0);
+ check_date(now, "%Y-%m-%d %H:%M:%S", localtime);
+#if 0 /* see dateparse.c */
+ check_date(now, "%Y-%m-%d %H:%M:%S %Z", localtime);
+ check_date(now, "%Y-%m-%d %H:%M:%S %Z", gmtime);
+#endif
+ check_date(now, "%c", localtime);
+ check_date(now, "%Ec", localtime);
+ check_date(now, "%X", localtime);
+ check_date(now, "%EX", localtime);
+ check_date(now, "%H:%M:%S", localtime);
+ /* This one needs a bodge: */
+ check_date(now - now % 60, "%H:%M", localtime);
+}
+
+TEST(dateparse);
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
--- /dev/null
+/* Derived from getdate.c in glibc 2.3.6. This is pretty much
+ * standard getdate() except that you supply the template in an
+ * argument, rather than messing around with environment variables and
+ * files. */
+
+/* Convert a string representation of time to a time value.
+ Copyright (C) 1997,1998,1999,2000,2001,2003 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Mark Kettenis <kettenis@phys.uva.nl>, 1997.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "dateparse.h"
+
+#define TM_YEAR_BASE 1900
+
+
+/* Prototypes for local functions. */
+static int first_wday (int year, int mon, int wday);
+static int check_mday (int year, int mon, int mday);
+
+# define isleap(year) \
+ ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
+
+/* Set to one of the following values to indicate an error.
+ 1 the DATEMSK environment variable is null or undefined,
+ 2 the template file cannot be opened for reading,
+ 3 failed to get file status information,
+ 4 the template file is not a regular file,
+ 5 an error is encountered while reading the template file,
+ 6 memory allication failed (not enough memory available),
+ 7 there is no line in the template that matches the input,
+ 8 invalid input specification Example: February 31 or a time is
+ specified that can not be represented in a time_t (representing
+ the time in seconds since 00:00:00 UTC, January 1, 1970) */
+int xgetdate_err;
+
+
+/* Returns the first weekday WDAY of month MON in the year YEAR. */
+static int
+first_wday (int year, int mon, int wday)
+{
+ struct tm tm;
+
+ if (wday == INT_MIN)
+ return 1;
+
+ memset (&tm, 0, sizeof (struct tm));
+ tm.tm_year = year;
+ tm.tm_mon = mon;
+ tm.tm_mday = 1;
+ mktime (&tm);
+
+ return (1 + (wday - tm.tm_wday + 7) % 7);
+}
+
+
+/* Returns 1 if MDAY is a valid day of the month in month MON of year
+ YEAR, and 0 if it is not. */
+static int
+check_mday (int year, int mon, int mday)
+{
+ switch (mon)
+ {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ case 7:
+ case 9:
+ case 11:
+ if (mday >= 1 && mday <= 31)
+ return 1;
+ break;
+ case 3:
+ case 5:
+ case 8:
+ case 10:
+ if (mday >= 1 && mday <= 30)
+ return 1;
+ break;
+ case 1:
+ if (mday >= 1 && mday <= (isleap (year) ? 29 : 28))
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+
+int
+xgetdate_r (const char *string, struct tm *tp,
+ const char *const *template)
+{
+ const char *line;
+ size_t len;
+ char *result = NULL;
+ time_t timer;
+ struct tm tm;
+ int mday_ok = 0;
+
+ line = NULL;
+ len = 0;
+ while((line = *template++))
+ {
+ /* Do the conversion. */
+ tp->tm_year = tp->tm_mon = tp->tm_mday = tp->tm_wday = INT_MIN;
+ tp->tm_hour = tp->tm_sec = tp->tm_min = INT_MIN;
+ tp->tm_isdst = -1;
+ tp->tm_gmtoff = 0;
+ tp->tm_zone = NULL;
+ result = strptime (string, line, tp);
+ if (result && *result == '\0')
+ break;
+ }
+
+ if (result == NULL || *result != '\0')
+ return 7;
+
+ /* Get current time. */
+ time (&timer);
+ localtime_r (&timer, &tm);
+
+ /* If only the weekday is given, today is assumed if the given day
+ is equal to the current day and next week if it is less. */
+ if (tp->tm_wday >= 0 && tp->tm_wday <= 6 && tp->tm_year == INT_MIN
+ && tp->tm_mon == INT_MIN && tp->tm_mday == INT_MIN)
+ {
+ tp->tm_year = tm.tm_year;
+ tp->tm_mon = tm.tm_mon;
+ tp->tm_mday = tm.tm_mday + (tp->tm_wday - tm.tm_wday + 7) % 7;
+ mday_ok = 1;
+ }
+
+ /* If only the month is given, the current month is assumed if the
+ given month is equal to the current month and next year if it is
+ less and no year is given (the first day of month is assumed if
+ no day is given. */
+ if (tp->tm_mon >= 0 && tp->tm_mon <= 11 && tp->tm_mday == INT_MIN)
+ {
+ if (tp->tm_year == INT_MIN)
+ tp->tm_year = tm.tm_year + (((tp->tm_mon - tm.tm_mon) < 0) ? 1 : 0);
+ tp->tm_mday = first_wday (tp->tm_year, tp->tm_mon, tp->tm_wday);
+ mday_ok = 1;
+ }
+
+ /* If no hour, minute and second are given the current hour, minute
+ and second are assumed. */
+ if (tp->tm_hour == INT_MIN && tp->tm_min == INT_MIN && tp->tm_sec == INT_MIN)
+ {
+ tp->tm_hour = tm.tm_hour;
+ tp->tm_min = tm.tm_min;
+ tp->tm_sec = tm.tm_sec;
+ }
+
+ /* If no date is given, today is assumed if the given hour is
+ greater than the current hour and tomorrow is assumed if
+ it is less. */
+ if (tp->tm_hour >= 0 && tp->tm_hour <= 23
+ && tp->tm_year == INT_MIN && tp->tm_mon == INT_MIN
+ && tp->tm_mday == INT_MIN && tp->tm_wday == INT_MIN)
+ {
+ tp->tm_year = tm.tm_year;
+ tp->tm_mon = tm.tm_mon;
+ tp->tm_mday = tm.tm_mday + ((tp->tm_hour - tm.tm_hour) < 0 ? 1 : 0);
+ mday_ok = 1;
+ }
+
+ /* Fill in the gaps. */
+ if (tp->tm_year == INT_MIN)
+ tp->tm_year = tm.tm_year;
+ if (tp->tm_hour == INT_MIN)
+ tp->tm_hour = 0;
+ if (tp->tm_min == INT_MIN)
+ tp->tm_min = 0;
+ if (tp->tm_sec == INT_MIN)
+ tp->tm_sec = 0;
+
+ /* Check if the day of month is within range, and if the time can be
+ represented in a time_t. We make use of the fact that the mktime
+ call normalizes the struct tm. */
+ if ((!mday_ok && !check_mday (TM_YEAR_BASE + tp->tm_year, tp->tm_mon,
+ tp->tm_mday))
+ || mktime (tp) == (time_t) -1)
+ return 8;
+
+ return 0;
+}
+
+
+
+struct tm *
+ xgetdate (const char *string, const char *const *template)
+{
+ /* Buffer returned by getdate. */
+ static struct tm tmbuf;
+ int errval = xgetdate_r (string, &tmbuf, template);
+
+ if (errval != 0)
+ {
+ getdate_err = errval;
+ return NULL;
+ }
+
+ return &tmbuf;
+}
"--config", disorder._configfile,
"--no-per-user-config",
"schedule-play",
- str(now + 4),
+ time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(now + 4)),
"normal",
track])
print " disorder schedule-list output:"
"--config", disorder._configfile,
"--no-per-user-config",
"schedule-set-global",
- str(now + 4),
+ time.strftime("%Y-%m-%d %H:%M:%S",
+ time.localtime(now + 4)),
"normal",
"random-play",
"yes"])