123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- date.c
- Abstract:
- This module implements the date utility, which prints or sets the current
- system time.
- Author:
- Evan Green 8-Oct-2013
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <getopt.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include "swlib.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define DATE_VERSION_MAJOR 1
- #define DATE_VERSION_MINOR 0
- #define DATE_USAGE \
- "usage: date [-u] [+format]\n" \
- " date [-u] mmddHHMM[[cc]yy]\n" \
- "The date utility prints the current date and time with no arguments.\n" \
- "Valid options are:\n" \
- " -u -- Perform time operations in UTC (GMT time).\n" \
- " +format -- Print the date according to the specified format. The \n" \
- " format is the same as that given to the strftime function:\n" \
- " %a - Abbreviated weekday.\n" \
- " %A - Full weekday.\n" \
- " %b - Abbreviated month.\n" \
- " %B - Full month.\n" \
- " %c - Locale's appropriate date and time representation.\n" \
- " %C - Century (year divided by 100 and truncated to an integer).\n"\
- " %d - Day of the month [01,31].\n" \
- " %D - Date in the format mm/dd/yy.\n" \
- " %e - Day of the month in a two digit field with a leading space " \
- "fill [1,31].\n" \
- " %h - Same as %b.\n" \
- " %H - Hour (24-hour clock) [00,23].\n" \
- " %I - Hour (12-hour clock) [01,12].\n" \
- " %j - Day of the year [001,366].\n" \
- " %m - Month [01,12].\n" \
- " %M - Minute [00,59].\n" \
- " %n - A newline.\n" \
- " %N - Nanoseconds [000000000,999999999].\n" \
- " %p - Locale's equivalent of AM or PM.\n" \
- " %r - 12-hour clock time [01,12] using the AM/PM notation. \n" \
- " In POSIX, this is \"%I:%M:%S %p\".\n" \
- " %s - Seconds since 1970 GMT.\n" \
- " %S - Seconds [00,60].\n" \
- " %t - A tab.\n" \
- " %T - 24-hour time in the format \"HH:MM:SS\".\n" \
- " %u - Weekday as a number [1,7] (1=Monday).\n" \
- " %U - Week of the year (Sunday as the first day of the week) \n" \
- " [0,53]. All days in a year preceding the first sunday \n" \
- " are in week 0.\n" \
- " %V - Week of the year (Monday as the first day of the week) \n" \
- " [01,53]. If the week containing January 1 has four or \n" \
- " more days, it is week 1. Otherwise it is the last week \n" \
- " of the previous year.\n" \
- " %w - Weekday as a decimal [0,6] (0=Sunday).\n" \
- " %W - Week of the year (Monday as the first day of the week) \n" \
- " [00,53]. All days preceding the first Monday are week 0.\n" \
- " %x - Locale's appropriate date representation.\n" \
- " %X - Locale's appropriate time representation.\n" \
- " %y - Year within century [00,99].\n" \
- " %Y - Year with century.\n" \
- " %Z - Timezone name or nothing if no timezone is available.\n" \
- " %% - A percent sign character.\n\n"
- #define DATE_OPTIONS_STRING "u"
- #define DATE_OPTION_UTC 0x00000001
- //
- // Define a default argument if none is provided.
- //
- #define DATE_DEFAULT_ARGUMENT "+%a %b %d %H:%M:%S %Z %Y"
- //
- // Define the output format for set time.
- //
- #define DATE_SET_TIME_FORMAT "%a %b %d %H:%M:%S %Z %Y"
- //
- // Define the size of the buffer used for the strftime function.
- //
- #define DATE_TIME_FORMAT_SIZE 2048
- //
- // This value defines the two digit year beginning in which the 21st century is
- // assumed.
- //
- #define TWO_DIGIT_YEAR_CUTOFF 70
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- INT
- DateParseComponent (
- PSTR *String
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option DateLongOptions[] = {
- {"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- DateMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the date utility.
- Arguments:
- ArgumentCount - Supplies the number of command line arguments the program
- was invoked with.
- Arguments - Supplies a tokenized array of command line arguments.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- PSTR Argument;
- ULONG ArgumentIndex;
- INT Option;
- ULONG Options;
- struct tm SetTime;
- int Status;
- time_t Time;
- PCHAR TimeBuffer;
- struct tm *TimeComponents;
- struct timeval TimeValue;
- int Year;
- Options = 0;
- TimeBuffer = NULL;
- //
- // Process the control arguments.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- DATE_OPTIONS_STRING,
- DateLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- Status = 1;
- goto MainEnd;
- }
- switch (Option) {
- case 'u':
- Options |= DATE_OPTION_UTC;
- break;
- case 'V':
- SwPrintVersion(DATE_VERSION_MAJOR, DATE_VERSION_MINOR);
- return 1;
- case 'h':
- puts(DATE_USAGE);
- return 1;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- ArgumentIndex = optind;
- if (ArgumentIndex > ArgumentCount) {
- ArgumentIndex = ArgumentCount;
- }
- Argument = DATE_DEFAULT_ARGUMENT;
- if (ArgumentIndex < ArgumentCount) {
- Argument = Arguments[ArgumentIndex];
- }
- //
- // Fail if there are too many arguments.
- //
- if (ArgumentIndex + 1 < ArgumentCount) {
- SwPrintError(0, Arguments[ArgumentIndex + 1], "Too many arguments");
- return 1;
- }
- Time = time(NULL);
- TimeBuffer = malloc(DATE_TIME_FORMAT_SIZE);
- if (TimeBuffer == NULL) {
- Status = ENOMEM;
- goto MainEnd;
- }
- //
- // If the first character of the argument is a +, then print the current
- // date in the specified format.
- //
- if (*Argument == '+') {
- if ((Options & DATE_OPTION_UTC) != 0) {
- TimeComponents = gmtime(&Time);
- } else {
- TimeComponents = localtime(&Time);
- }
- if (TimeComponents == NULL) {
- Status = errno;
- SwPrintError(Status, NULL, "Failed to get current time");
- goto MainEnd;
- }
- strftime(TimeBuffer,
- DATE_TIME_FORMAT_SIZE,
- Argument + 1,
- TimeComponents);
- printf("%s\n", TimeBuffer);
- Status = 0;
- goto MainEnd;
- }
- memset(&SetTime, 0, sizeof(struct tm));
- //
- // Parse the string of the form mmddhhmm[[cc]yy].
- //
- Status = EINVAL;
- SetTime.tm_mon = DateParseComponent(&Argument);
- if (SetTime.tm_mon < 0) {
- SwPrintError(0, Argument, "Failed to parse month");
- goto MainEnd;
- }
- SetTime.tm_mon -= 1;
- if ((SetTime.tm_mon < 0) || (SetTime.tm_mon > 11)) {
- SwPrintError(0, Arguments[ArgumentIndex], "Invalid date");
- goto MainEnd;
- }
- SetTime.tm_mday = DateParseComponent(&Argument);
- if (SetTime.tm_mday < 0) {
- SwPrintError(0, Argument, "Failed to parse day");
- goto MainEnd;
- }
- if ((SetTime.tm_mday < 1) || (SetTime.tm_mday > 31)) {
- SwPrintError(0, Arguments[ArgumentIndex], "Invalid date");
- goto MainEnd;
- }
- SetTime.tm_hour = DateParseComponent(&Argument);
- if (SetTime.tm_hour < 0) {
- SwPrintError(0, Argument, "Failed to parse hour");
- goto MainEnd;
- }
- if ((SetTime.tm_hour < 0) || (SetTime.tm_hour > 23)) {
- SwPrintError(0, Arguments[ArgumentIndex], "Invalid date");
- goto MainEnd;
- }
- SetTime.tm_min = DateParseComponent(&Argument);
- if (SetTime.tm_min < 0) {
- SwPrintError(0, Argument, "Failed to parse minute");
- goto MainEnd;
- }
- if ((SetTime.tm_hour < 0) || (SetTime.tm_hour > 59)) {
- SwPrintError(0, Arguments[ArgumentIndex], "Invalid date");
- goto MainEnd;
- }
- if (*Argument != '\0') {
- SetTime.tm_year = DateParseComponent(&Argument);
- if (SetTime.tm_year < 0) {
- SwPrintError(0, Argument, "Failed to parse year/century");
- goto MainEnd;
- }
- if (*Argument != '\0') {
- Year = DateParseComponent(&Argument);
- if (Year < 0) {
- SwPrintError(0, Argument, "Failed to parse year");
- goto MainEnd;
- }
- SetTime.tm_year *= 100;
- SetTime.tm_year += Year;
- SetTime.tm_year -= 1900;
- //
- // If no century is supplied, then assume the user meant somewhere in
- // the late 1900's or early 2000's with the cutoff at 1970.
- //
- } else {
- if (SetTime.tm_year < TWO_DIGIT_YEAR_CUTOFF) {
- SetTime.tm_year += 100;
- }
- }
- //
- // Use the current year if no year was supplied.
- //
- } else {
- if ((Options & DATE_OPTION_UTC) != 0) {
- TimeComponents = gmtime(&Time);
- } else {
- TimeComponents = localtime(&Time);
- }
- if (TimeComponents == NULL) {
- Status = errno;
- SwPrintError(Status, NULL, "Failed to set time");
- goto MainEnd;
- }
- SetTime.tm_year = TimeComponents->tm_year;
- }
- if (*Argument != '\0') {
- SwPrintError(0, Argument, "Unexpected garbage at end of line");
- goto MainEnd;
- }
- //
- // The daylight savings of the supplied time are unknown.
- //
- SetTime.tm_isdst = -1;
- //
- // Interpret the parsed calendar time depending on the UTC option. If this
- // fails it will return -1 with errno set. Note that -1 without errno set
- // could be the result of a time that is 1 second before the Epoch.
- //
- if ((Options & DATE_OPTION_UTC) != 0) {
- Time = SwConvertGmtTime(&SetTime);
- } else {
- Time = mktime(&SetTime);
- }
- if ((Time == -1) && (errno != 0)) {
- Status = errno;
- SwPrintError(Status, Arguments[ArgumentIndex], "Invalid date");
- goto MainEnd;
- }
- TimeValue.tv_sec = Time;
- TimeValue.tv_usec = 0;
- Status = SwSetTimeOfDay(&TimeValue, NULL);
- if (Status != 0) {
- Status = errno;
- SwPrintError(Status, NULL, "Failed to set time");
- goto MainEnd;
- }
- //
- // Now print the updated time. The mktime and timegm routines modified the
- // parsed calendar time so that it is exactly what needs to be printed.
- //
- strftime(TimeBuffer,
- DATE_TIME_FORMAT_SIZE,
- DATE_SET_TIME_FORMAT,
- &SetTime);
- printf("%s\n", TimeBuffer);
- MainEnd:
- if (TimeBuffer != NULL) {
- free(TimeBuffer);
- }
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- INT
- DateParseComponent (
- PSTR *String
- )
- /*++
- Routine Description:
- This routine pulls the next two digits out of the string and returns their
- decimal numerical representation.
- Arguments:
- String - Supplies a pointer that on input contains a pointer to a string.
- On output this value will be advanced.
- Return Value:
- Returns the integer on success.
- -1 on failure.
- --*/
- {
- PSTR Argument;
- INT Value;
- Argument = *String;
- if ((!isdigit(*Argument)) || (!isdigit(*(Argument + 1)))) {
- return -1;
- }
- Value = ((*Argument - '0') * 10) + (*(Argument + 1) - '0');
- Argument += 2;
- *String = Argument;
- return Value;
- }
|