Index: uspace/app/date/Makefile
===================================================================
--- uspace/app/date/Makefile	(revision 85f7369aa7a378fe5b780d408f8290b7f07f2afe)
+++ uspace/app/date/Makefile	(revision 85f7369aa7a378fe5b780d408f8290b7f07f2afe)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2012 Maurizio Lombardi
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - 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.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+#
+
+USPACE_PREFIX = ../..
+BINARY = date
+
+SOURCES = \
+	date.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/date/date.c
===================================================================
--- uspace/app/date/date.c	(revision 85f7369aa7a378fe5b780d408f8290b7f07f2afe)
+++ uspace/app/date/date.c	(revision 85f7369aa7a378fe5b780d408f8290b7f07f2afe)
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2012 Maurizio Lombardi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <stdio.h>
+#include <device/clock_dev.h>
+#include <errno.h>
+#include <loc.h>
+#include <time.h>
+#include <malloc.h>
+#include <ipc/clock_ctl.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#define NAME   "date"
+
+static int read_date_from_arg(char *wdate, struct tm *t);
+static int read_time_from_arg(char *wdate, struct tm *t);
+static int read_num_from_str(char *str, size_t len, int *n);
+static int tm_sanity_check(struct tm *t);
+static bool is_leap_year(int year);
+
+static void usage(void);
+
+static int days_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+int
+main(int argc, char **argv)
+{
+	int rc, c;
+	category_id_t cat_id;
+	size_t        svc_cnt;
+	service_id_t  *svc_ids = NULL;
+	service_id_t  svc_id;
+	char          *svc_name = NULL;
+	bool          read_only = true;
+	char          *wdate = NULL;
+	char          *wtime = NULL;
+	sysarg_t      battery_ok;
+	struct tm     t;
+	int           n_args = argc;
+
+	while ((c = getopt(argc, argv, "hd:t:")) != -1) {
+		switch (c) {
+		case 'h':
+			usage();
+			return 0;
+		case 'd':
+			if (wdate) {
+				usage();
+				return 1;
+			}
+			wdate = (char *)optarg;
+			read_only = false;
+			n_args -= 2;
+			break;
+		case 't':
+			if (wtime) {
+				usage();
+				return 1;
+			}
+			wtime = (char *)optarg;
+			read_only = false;
+			n_args -= 2;
+			break;
+		case '?':
+			usage();
+			return 1;
+		}
+	}
+
+	if (n_args != 1) {
+		printf(NAME ": Unrecognized parameter\n");
+		usage();
+		return 1;
+	}
+
+	/* Get the id of the clock category */
+	rc = loc_category_get_id("clock", &cat_id, IPC_FLAG_BLOCKING);
+	if (rc != EOK) {
+		printf(NAME ": Cannot get clock category id\n");
+		goto exit;
+	}
+
+	/* Get the list of available services in the clock category */
+	rc = loc_category_get_svcs(cat_id, &svc_ids, &svc_cnt);
+	if (rc != EOK) {
+		printf(NAME ": Cannot get the list of services in the clock "
+		    "category\n");
+		goto exit;
+	}
+
+	/* Check if there are available services in the clock category */
+	if (svc_cnt == 0) {
+		printf(NAME ": No available service found in "
+		    "the clock category\n");
+		goto exit;
+	}
+
+	/* Get the name of the clock service */
+	rc = loc_service_get_name(svc_ids[0], &svc_name);
+	if (rc != EOK) {
+		printf(NAME ": Cannot get the name of the service\n");
+		goto exit;
+	}
+
+	/* Get the service id for the device */
+	rc = loc_service_get_id(svc_name, &svc_id, 0);
+	if (rc != EOK) {
+		printf(NAME ": Cannot get the service id for device %s",
+		    svc_name);
+		goto exit;
+	}
+
+	/* Connect to the device */
+	async_sess_t *sess = loc_service_connect(EXCHANGE_SERIALIZE,
+	    svc_id, 0);
+	if (!sess) {
+		printf(NAME ": Cannot connect to the device\n");
+		goto exit;
+	}
+
+	/* Check the battery status (if present) */
+	async_exch_t *exch = async_exchange_begin(sess);
+	rc = async_req_0_1(exch, CLOCK_GET_BATTERY_STATUS, &battery_ok);
+	async_exchange_end(exch);
+
+	if (rc == EOK && !battery_ok)
+		printf(NAME ": Warning! RTC battery dead\n");
+
+	/* Read the current date/time */
+	rc = clock_dev_time_get(sess, &t);
+	if (rc != EOK) {
+		printf(NAME ": Cannot read the current time\n");
+		goto exit;
+	}
+
+	if (read_only) {
+		/* Print the current time and exit */
+		printf("%02d/%02d/%d ", t.tm_mday,
+		    t.tm_mon + 1, 1900 + t.tm_year);
+		printf("%02d:%02d:%02d\n", t.tm_hour, t.tm_min, t.tm_sec);
+	} else {
+		if (wdate) {
+			rc = read_date_from_arg(wdate, &t);
+			if (rc != EOK) {
+				printf(NAME ": error, date format not "
+				    "recognized\n");
+				usage();
+				goto exit;
+			}
+		}
+		if (wtime) {
+			rc = read_time_from_arg(wtime, &t);
+			if (rc != EOK) {
+				printf(NAME ": error, time format not "
+				    "recognized\n");
+				usage();
+				goto exit;
+			}
+		}
+
+		rc = tm_sanity_check(&t);
+		if (rc != EOK) {
+			printf(NAME ": error, invalid date/time\n");
+			goto exit;
+		}
+
+		rc = clock_dev_time_set(sess, &t);
+		if (rc != EOK) {
+			printf(NAME ": error, Unable to set date/time\n");
+			goto exit;
+		}
+	}
+
+exit:
+	free(svc_name);
+	free(svc_ids);
+	return rc;
+}
+
+/** Read the day, month and year from a string
+ *  with the following format: DD/MM/YYYY
+ */
+static int
+read_date_from_arg(char *wdate, struct tm *t)
+{
+	int rc;
+
+	if (str_size(wdate) != 10) /* str_size("DD/MM/YYYY") == 10 */
+		return EINVAL;
+
+	if (wdate[2] != '/' ||
+	    wdate[5] != '/') {
+		return EINVAL;
+	}
+
+	rc = read_num_from_str(&wdate[0], 2, &t->tm_mday);
+	if (rc != EOK)
+		return rc;
+
+	rc = read_num_from_str(&wdate[3], 2, &t->tm_mon);
+	if (rc != EOK)
+		return rc;
+	t->tm_mon--;
+
+	rc = read_num_from_str(&wdate[6], 4, &t->tm_year);
+	t->tm_year -= 1900;
+	return rc;
+}
+
+/** Read the hours, minutes and seconds from a string
+ *  with the following format: HH:MM:SS or HH:MM
+ */
+static int
+read_time_from_arg(char *wtime, struct tm *t)
+{
+	int rc;
+	size_t len = str_size(wtime);
+	bool sec_present = len == 8;
+
+	/* str_size("HH:MM") == 5 */
+	/* str_size("HH:MM:SS") == 8 */
+	if (len != 8 && len != 5)
+		return EINVAL;
+
+	if (sec_present && wtime[5] != ':')
+		return EINVAL;
+
+	if (wtime[2] != ':')
+		return EINVAL;
+
+	rc = read_num_from_str(&wtime[0], 2, &t->tm_hour);
+	if (rc != EOK)
+		return rc;
+
+	rc = read_num_from_str(&wtime[3], 2, &t->tm_min);
+	if (rc != EOK)
+		return rc;
+
+	if (sec_present)
+		rc = read_num_from_str(&wtime[6], 2, &t->tm_sec);
+	else
+		t->tm_sec = 0;
+
+	return rc;
+}
+
+static int
+read_num_from_str(char *str, size_t len, int *n)
+{
+	size_t i;
+
+	*n = 0;
+
+	for (i = 0; i < len; ++i, ++str) {
+		if (!isdigit(*str))
+			return EINVAL;
+		*n *= 10;
+		*n += *str - '0';
+	}
+
+	return EOK;
+}
+
+/** Check if the tm structure contains valid values
+ *
+ * @param t     The tm structure to check
+ *
+ * @return      EOK on success or EINVAL
+ */
+static int
+tm_sanity_check(struct tm *t)
+{
+	int ndays;
+
+	if (t->tm_sec < 0 || t->tm_sec > 59)
+		return EINVAL;
+	else if (t->tm_min < 0 || t->tm_min > 59)
+		return EINVAL;
+	else if (t->tm_hour < 0 || t->tm_hour > 23)
+		return EINVAL;
+	else if (t->tm_mday < 1 || t->tm_mday > 31)
+		return EINVAL;
+	else if (t->tm_mon < 0 || t->tm_mon > 11)
+		return EINVAL;
+	else if (t->tm_year < 0 || t->tm_year > 199)
+		return EINVAL;
+
+	if (t->tm_mon == 1/* FEB */ && is_leap_year(t->tm_year))
+		ndays = 29;
+	else
+		ndays = days_month[t->tm_mon];
+
+	if (t->tm_mday > ndays)
+		return EINVAL;
+
+	return EOK;
+}
+
+/** Check if a year is a leap year
+ *
+ * @param year   The year to check
+ *
+ * @return       true if it is a leap year, false otherwise
+ */
+static bool
+is_leap_year(int year)
+{
+	bool r = false;
+
+	if (year % 4 == 0) {
+		if (year % 100 == 0)
+			r = year % 400 == 0;
+		else
+			r = true;
+	}
+
+	return r;
+}
+
+static void
+usage(void)
+{
+	printf("Usage: date [-d DD/MM/YYYY] [-t HH:MM[:SS]]\n");
+	printf("       -d   Change the current date\n");
+	printf("       -t   Change the current time\n");
+	printf("       -h   Display this information\n");
+}
+
