date.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. date.c
  5. Abstract:
  6. This module implements the date utility, which prints or sets the current
  7. system time.
  8. Author:
  9. Evan Green 8-Oct-2013
  10. Environment:
  11. POSIX
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/lib/types.h>
  17. #include <assert.h>
  18. #include <ctype.h>
  19. #include <errno.h>
  20. #include <getopt.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include "swlib.h"
  25. //
  26. // ---------------------------------------------------------------- Definitions
  27. //
  28. #define DATE_VERSION_MAJOR 1
  29. #define DATE_VERSION_MINOR 0
  30. #define DATE_USAGE \
  31. "usage: date [-u] [+format]\n" \
  32. " date [-u] mmddHHMM[[cc]yy]\n" \
  33. "The date utility prints the current date and time with no arguments.\n" \
  34. "Valid options are:\n" \
  35. " -u -- Perform time operations in UTC (GMT time).\n" \
  36. " +format -- Print the date according to the specified format. The \n" \
  37. " format is the same as that given to the strftime function:\n" \
  38. " %a - Abbreviated weekday.\n" \
  39. " %A - Full weekday.\n" \
  40. " %b - Abbreviated month.\n" \
  41. " %B - Full month.\n" \
  42. " %c - Locale's appropriate date and time representation.\n" \
  43. " %C - Century (year divided by 100 and truncated to an integer).\n"\
  44. " %d - Day of the month [01,31].\n" \
  45. " %D - Date in the format mm/dd/yy.\n" \
  46. " %e - Day of the month in a two digit field with a leading space " \
  47. "fill [1,31].\n" \
  48. " %h - Same as %b.\n" \
  49. " %H - Hour (24-hour clock) [00,23].\n" \
  50. " %I - Hour (12-hour clock) [01,12].\n" \
  51. " %j - Day of the year [001,366].\n" \
  52. " %m - Month [01,12].\n" \
  53. " %M - Minute [00,59].\n" \
  54. " %n - A newline.\n" \
  55. " %N - Nanoseconds [000000000,999999999].\n" \
  56. " %p - Locale's equivalent of AM or PM.\n" \
  57. " %r - 12-hour clock time [01,12] using the AM/PM notation. \n" \
  58. " In POSIX, this is \"%I:%M:%S %p\".\n" \
  59. " %s - Seconds since 1970 GMT.\n" \
  60. " %S - Seconds [00,60].\n" \
  61. " %t - A tab.\n" \
  62. " %T - 24-hour time in the format \"HH:MM:SS\".\n" \
  63. " %u - Weekday as a number [1,7] (1=Monday).\n" \
  64. " %U - Week of the year (Sunday as the first day of the week) \n" \
  65. " [0,53]. All days in a year preceding the first sunday \n" \
  66. " are in week 0.\n" \
  67. " %V - Week of the year (Monday as the first day of the week) \n" \
  68. " [01,53]. If the week containing January 1 has four or \n" \
  69. " more days, it is week 1. Otherwise it is the last week \n" \
  70. " of the previous year.\n" \
  71. " %w - Weekday as a decimal [0,6] (0=Sunday).\n" \
  72. " %W - Week of the year (Monday as the first day of the week) \n" \
  73. " [00,53]. All days preceding the first Monday are week 0.\n" \
  74. " %x - Locale's appropriate date representation.\n" \
  75. " %X - Locale's appropriate time representation.\n" \
  76. " %y - Year within century [00,99].\n" \
  77. " %Y - Year with century.\n" \
  78. " %Z - Timezone name or nothing if no timezone is available.\n" \
  79. " %% - A percent sign character.\n\n"
  80. #define DATE_OPTIONS_STRING "u"
  81. #define DATE_OPTION_UTC 0x00000001
  82. //
  83. // Define a default argument if none is provided.
  84. //
  85. #define DATE_DEFAULT_ARGUMENT "+%a %b %d %H:%M:%S %Z %Y"
  86. //
  87. // Define the output format for set time.
  88. //
  89. #define DATE_SET_TIME_FORMAT "%a %b %d %H:%M:%S %Z %Y"
  90. //
  91. // Define the size of the buffer used for the strftime function.
  92. //
  93. #define DATE_TIME_FORMAT_SIZE 2048
  94. //
  95. // This value defines the two digit year beginning in which the 21st century is
  96. // assumed.
  97. //
  98. #define TWO_DIGIT_YEAR_CUTOFF 70
  99. //
  100. // ------------------------------------------------------ Data Type Definitions
  101. //
  102. //
  103. // ----------------------------------------------- Internal Function Prototypes
  104. //
  105. INT
  106. DateParseComponent (
  107. PSTR *String
  108. );
  109. //
  110. // -------------------------------------------------------------------- Globals
  111. //
  112. struct option DateLongOptions[] = {
  113. {"help", no_argument, 0, 'h'},
  114. {"version", no_argument, 0, 'V'},
  115. {NULL, 0, 0, 0},
  116. };
  117. //
  118. // ------------------------------------------------------------------ Functions
  119. //
  120. INT
  121. DateMain (
  122. INT ArgumentCount,
  123. CHAR **Arguments
  124. )
  125. /*++
  126. Routine Description:
  127. This routine is the main entry point for the date utility.
  128. Arguments:
  129. ArgumentCount - Supplies the number of command line arguments the program
  130. was invoked with.
  131. Arguments - Supplies a tokenized array of command line arguments.
  132. Return Value:
  133. Returns an integer exit code. 0 for success, nonzero otherwise.
  134. --*/
  135. {
  136. PSTR Argument;
  137. ULONG ArgumentIndex;
  138. INT Option;
  139. ULONG Options;
  140. struct tm SetTime;
  141. int Status;
  142. time_t Time;
  143. PCHAR TimeBuffer;
  144. struct tm *TimeComponents;
  145. struct timeval TimeValue;
  146. int Year;
  147. Options = 0;
  148. TimeBuffer = NULL;
  149. //
  150. // Process the control arguments.
  151. //
  152. while (TRUE) {
  153. Option = getopt_long(ArgumentCount,
  154. Arguments,
  155. DATE_OPTIONS_STRING,
  156. DateLongOptions,
  157. NULL);
  158. if (Option == -1) {
  159. break;
  160. }
  161. if ((Option == '?') || (Option == ':')) {
  162. Status = 1;
  163. goto MainEnd;
  164. }
  165. switch (Option) {
  166. case 'u':
  167. Options |= DATE_OPTION_UTC;
  168. break;
  169. case 'V':
  170. SwPrintVersion(DATE_VERSION_MAJOR, DATE_VERSION_MINOR);
  171. return 1;
  172. case 'h':
  173. puts(DATE_USAGE);
  174. return 1;
  175. default:
  176. assert(FALSE);
  177. Status = 1;
  178. goto MainEnd;
  179. }
  180. }
  181. ArgumentIndex = optind;
  182. if (ArgumentIndex > ArgumentCount) {
  183. ArgumentIndex = ArgumentCount;
  184. }
  185. Argument = DATE_DEFAULT_ARGUMENT;
  186. if (ArgumentIndex < ArgumentCount) {
  187. Argument = Arguments[ArgumentIndex];
  188. }
  189. //
  190. // Fail if there are too many arguments.
  191. //
  192. if (ArgumentIndex + 1 < ArgumentCount) {
  193. SwPrintError(0, Arguments[ArgumentIndex + 1], "Too many arguments");
  194. return 1;
  195. }
  196. Time = time(NULL);
  197. TimeBuffer = malloc(DATE_TIME_FORMAT_SIZE);
  198. if (TimeBuffer == NULL) {
  199. Status = ENOMEM;
  200. goto MainEnd;
  201. }
  202. //
  203. // If the first character of the argument is a +, then print the current
  204. // date in the specified format.
  205. //
  206. if (*Argument == '+') {
  207. if ((Options & DATE_OPTION_UTC) != 0) {
  208. TimeComponents = gmtime(&Time);
  209. } else {
  210. TimeComponents = localtime(&Time);
  211. }
  212. if (TimeComponents == NULL) {
  213. Status = errno;
  214. SwPrintError(Status, NULL, "Failed to get current time");
  215. goto MainEnd;
  216. }
  217. strftime(TimeBuffer,
  218. DATE_TIME_FORMAT_SIZE,
  219. Argument + 1,
  220. TimeComponents);
  221. printf("%s\n", TimeBuffer);
  222. Status = 0;
  223. goto MainEnd;
  224. }
  225. memset(&SetTime, 0, sizeof(struct tm));
  226. //
  227. // Parse the string of the form mmddhhmm[[cc]yy].
  228. //
  229. Status = EINVAL;
  230. SetTime.tm_mon = DateParseComponent(&Argument);
  231. if (SetTime.tm_mon < 0) {
  232. SwPrintError(0, Argument, "Failed to parse month");
  233. goto MainEnd;
  234. }
  235. SetTime.tm_mon -= 1;
  236. if ((SetTime.tm_mon < 0) || (SetTime.tm_mon > 11)) {
  237. SwPrintError(0, Arguments[ArgumentIndex], "Invalid date");
  238. goto MainEnd;
  239. }
  240. SetTime.tm_mday = DateParseComponent(&Argument);
  241. if (SetTime.tm_mday < 0) {
  242. SwPrintError(0, Argument, "Failed to parse day");
  243. goto MainEnd;
  244. }
  245. if ((SetTime.tm_mday < 1) || (SetTime.tm_mday > 31)) {
  246. SwPrintError(0, Arguments[ArgumentIndex], "Invalid date");
  247. goto MainEnd;
  248. }
  249. SetTime.tm_hour = DateParseComponent(&Argument);
  250. if (SetTime.tm_hour < 0) {
  251. SwPrintError(0, Argument, "Failed to parse hour");
  252. goto MainEnd;
  253. }
  254. if ((SetTime.tm_hour < 0) || (SetTime.tm_hour > 23)) {
  255. SwPrintError(0, Arguments[ArgumentIndex], "Invalid date");
  256. goto MainEnd;
  257. }
  258. SetTime.tm_min = DateParseComponent(&Argument);
  259. if (SetTime.tm_min < 0) {
  260. SwPrintError(0, Argument, "Failed to parse minute");
  261. goto MainEnd;
  262. }
  263. if ((SetTime.tm_hour < 0) || (SetTime.tm_hour > 59)) {
  264. SwPrintError(0, Arguments[ArgumentIndex], "Invalid date");
  265. goto MainEnd;
  266. }
  267. if (*Argument != '\0') {
  268. SetTime.tm_year = DateParseComponent(&Argument);
  269. if (SetTime.tm_year < 0) {
  270. SwPrintError(0, Argument, "Failed to parse year/century");
  271. goto MainEnd;
  272. }
  273. if (*Argument != '\0') {
  274. Year = DateParseComponent(&Argument);
  275. if (Year < 0) {
  276. SwPrintError(0, Argument, "Failed to parse year");
  277. goto MainEnd;
  278. }
  279. SetTime.tm_year *= 100;
  280. SetTime.tm_year += Year;
  281. SetTime.tm_year -= 1900;
  282. //
  283. // If no century is supplied, then assume the user meant somewhere in
  284. // the late 1900's or early 2000's with the cutoff at 1970.
  285. //
  286. } else {
  287. if (SetTime.tm_year < TWO_DIGIT_YEAR_CUTOFF) {
  288. SetTime.tm_year += 100;
  289. }
  290. }
  291. //
  292. // Use the current year if no year was supplied.
  293. //
  294. } else {
  295. if ((Options & DATE_OPTION_UTC) != 0) {
  296. TimeComponents = gmtime(&Time);
  297. } else {
  298. TimeComponents = localtime(&Time);
  299. }
  300. if (TimeComponents == NULL) {
  301. Status = errno;
  302. SwPrintError(Status, NULL, "Failed to set time");
  303. goto MainEnd;
  304. }
  305. SetTime.tm_year = TimeComponents->tm_year;
  306. }
  307. if (*Argument != '\0') {
  308. SwPrintError(0, Argument, "Unexpected garbage at end of line");
  309. goto MainEnd;
  310. }
  311. //
  312. // The daylight savings of the supplied time are unknown.
  313. //
  314. SetTime.tm_isdst = -1;
  315. //
  316. // Interpret the parsed calendar time depending on the UTC option. If this
  317. // fails it will return -1 with errno set. Note that -1 without errno set
  318. // could be the result of a time that is 1 second before the Epoch.
  319. //
  320. if ((Options & DATE_OPTION_UTC) != 0) {
  321. Time = SwConvertGmtTime(&SetTime);
  322. } else {
  323. Time = mktime(&SetTime);
  324. }
  325. if ((Time == -1) && (errno != 0)) {
  326. Status = errno;
  327. SwPrintError(Status, Arguments[ArgumentIndex], "Invalid date");
  328. goto MainEnd;
  329. }
  330. TimeValue.tv_sec = Time;
  331. TimeValue.tv_usec = 0;
  332. Status = SwSetTimeOfDay(&TimeValue, NULL);
  333. if (Status != 0) {
  334. Status = errno;
  335. SwPrintError(Status, NULL, "Failed to set time");
  336. goto MainEnd;
  337. }
  338. //
  339. // Now print the updated time. The mktime and timegm routines modified the
  340. // parsed calendar time so that it is exactly what needs to be printed.
  341. //
  342. strftime(TimeBuffer,
  343. DATE_TIME_FORMAT_SIZE,
  344. DATE_SET_TIME_FORMAT,
  345. &SetTime);
  346. printf("%s\n", TimeBuffer);
  347. MainEnd:
  348. if (TimeBuffer != NULL) {
  349. free(TimeBuffer);
  350. }
  351. return Status;
  352. }
  353. //
  354. // --------------------------------------------------------- Internal Functions
  355. //
  356. INT
  357. DateParseComponent (
  358. PSTR *String
  359. )
  360. /*++
  361. Routine Description:
  362. This routine pulls the next two digits out of the string and returns their
  363. decimal numerical representation.
  364. Arguments:
  365. String - Supplies a pointer that on input contains a pointer to a string.
  366. On output this value will be advanced.
  367. Return Value:
  368. Returns the integer on success.
  369. -1 on failure.
  370. --*/
  371. {
  372. PSTR Argument;
  373. INT Value;
  374. Argument = *String;
  375. if ((!isdigit(*Argument)) || (!isdigit(*(Argument + 1)))) {
  376. return -1;
  377. }
  378. Value = ((*Argument - '0') * 10) + (*(Argument + 1) - '0');
  379. Argument += 2;
  380. *String = Argument;
  381. return Value;
  382. }