tzset.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*++
  2. Copyright (c) 2017 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. tzset.c
  9. Abstract:
  10. This module implements a program which allows the user to change the
  11. default time zone.
  12. Author:
  13. Evan Green 20-Mar-2017
  14. Environment:
  15. Minoca OS
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include <assert.h>
  21. #include <errno.h>
  22. #include <getopt.h>
  23. #include <sys/stat.h>
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <minoca/lib/minocaos.h>
  27. //
  28. // --------------------------------------------------------------------- Macros
  29. //
  30. //
  31. // ---------------------------------------------------------------- Definitions
  32. //
  33. #define TZSET_DEFAULT_ALMANAC_PATH "/usr/share/tz/tzdata"
  34. #define TZSET_DEFAULT_ZONE_PATH "/etc/tz"
  35. #define TZSET_VERSION_MAJOR 1
  36. #define TZSET_VERSION_MINOR 0
  37. #define TZSET_USAGE \
  38. "usage: tzset [options] zone_name\n" \
  39. " tzset --list\n" \
  40. "The tzset utility allows the user to change the default time zone. \n" \
  41. "Options are:\n" \
  42. " -i, --input=file -- Supply the path to the time zone almanac.\n" \
  43. " The default is " TZSET_DEFAULT_ALMANAC_PATH ".\n" \
  44. " -o, --output=file -- Supply the output path for the filtered data.\n" \
  45. " The defailt is " TZSET_DEFAULT_ZONE_PATH ".\n" \
  46. " -l, --list -- List all time zones in the almanac and exit.\n" \
  47. " --help -- Show this help text and exit.\n" \
  48. " --version -- Print the application version information and exit.\n"
  49. #define TZSET_OPTIONS_STRING "i:o:lhV"
  50. #define TZSET_OPTION_LIST 0x00000001
  51. //
  52. // ------------------------------------------------------ Data Type Definitions
  53. //
  54. //
  55. // ----------------------------------------------- Internal Function Prototypes
  56. //
  57. //
  58. // -------------------------------------------------------------------- Globals
  59. //
  60. struct option TzsetLongOptions[] = {
  61. {"input", required_argument, 0, 'i'},
  62. {"output", required_argument, 0, 'o'},
  63. {"list", no_argument, 0, 'l'},
  64. {"help", no_argument, 0, 'h'},
  65. {"version", no_argument, 0, 'V'},
  66. {NULL, 0, 0, 0},
  67. };
  68. //
  69. // ------------------------------------------------------------------ Functions
  70. //
  71. INT
  72. main (
  73. INT ArgumentCount,
  74. CHAR **Arguments
  75. )
  76. /*++
  77. Routine Description:
  78. This routine implements the tzset program, which allows the user to change
  79. the default time zone.
  80. Arguments:
  81. ArgumentCount - Supplies the number of elements in the arguments array.
  82. Arguments - Supplies an array of strings. The array count is bounded by the
  83. previous parameter, and the strings are null-terminated.
  84. Return Value:
  85. 0 on success.
  86. Non-zero on failure.
  87. --*/
  88. {
  89. PVOID InBuffer;
  90. FILE *InFile;
  91. PCSTR InPath;
  92. KSTATUS KStatus;
  93. INT Option;
  94. ULONG Options;
  95. PSTR OutBuffer;
  96. PSTR OutEnd;
  97. FILE *OutFile;
  98. PCSTR OutPath;
  99. ULONG OutSize;
  100. off_t InSize;
  101. struct stat Stat;
  102. INT Status;
  103. PSTR ZoneName;
  104. InBuffer = NULL;
  105. InFile = NULL;
  106. InPath = TZSET_DEFAULT_ALMANAC_PATH;
  107. OutBuffer = NULL;
  108. OutFile = NULL;
  109. OutPath = TZSET_DEFAULT_ZONE_PATH;
  110. Options = 0;
  111. Status = 1;
  112. //
  113. // Process the control arguments.
  114. //
  115. while (TRUE) {
  116. Option = getopt_long(ArgumentCount,
  117. Arguments,
  118. TZSET_OPTIONS_STRING,
  119. TzsetLongOptions,
  120. NULL);
  121. if (Option == -1) {
  122. break;
  123. }
  124. if ((Option == '?') || (Option == ':')) {
  125. goto MainEnd;
  126. }
  127. switch (Option) {
  128. case 'i':
  129. InPath = optarg;
  130. break;
  131. case 'o':
  132. OutPath = optarg;
  133. break;
  134. case 'l':
  135. Options |= TZSET_OPTION_LIST;
  136. break;
  137. case 'V':
  138. printf("tzset version %d.%d.\n",
  139. TZSET_VERSION_MAJOR,
  140. TZSET_VERSION_MINOR);
  141. return 1;
  142. case 'h':
  143. printf(TZSET_USAGE);
  144. return 1;
  145. default:
  146. assert(FALSE);
  147. goto MainEnd;
  148. }
  149. }
  150. if (stat(InPath, &Stat) != 0) {
  151. fprintf(stderr,
  152. "Error: Failed to stat %s: %s\n",
  153. InPath,
  154. strerror(errno));
  155. goto MainEnd;
  156. }
  157. InSize = Stat.st_size;
  158. InFile = fopen(InPath, "rb");
  159. if (InFile == NULL) {
  160. fprintf(stderr,
  161. "Error: Failed to open %s: %s\n",
  162. InPath,
  163. strerror(errno));
  164. goto MainEnd;
  165. }
  166. InBuffer = malloc(InSize);
  167. OutBuffer = malloc(OutSize);
  168. if ((InBuffer == NULL) || (OutBuffer == NULL)) {
  169. goto MainEnd;
  170. }
  171. if (fread(InBuffer, 1, InSize, InFile) != InSize) {
  172. fprintf(stderr, "Error: Read error: %s\n", strerror(errno));
  173. goto MainEnd;
  174. }
  175. //
  176. // If requested, just list the zones rather than saving a new one.
  177. //
  178. if ((Options & TZSET_OPTION_LIST) != 0) {
  179. KStatus = RtlListTimeZones(InBuffer, InSize, OutBuffer, &OutSize);
  180. if (!KSUCCESS(KStatus)) {
  181. if (KStatus == STATUS_FILE_CORRUPT) {
  182. fprintf(stderr, "Error: Invalid time zone data.\n");
  183. } else {
  184. fprintf(stderr,
  185. "Error: Failed to get zone names: %d\n",
  186. KStatus);
  187. }
  188. goto MainEnd;
  189. }
  190. //
  191. // List all the names, which end with a double null terminator.
  192. //
  193. OutEnd = OutBuffer + OutSize;
  194. ZoneName = OutBuffer;
  195. while ((ZoneName < OutEnd) && (*ZoneName != '\0')) {
  196. printf("%s\n", ZoneName);
  197. ZoneName += strlen(ZoneName) + 1;
  198. }
  199. Status = 0;
  200. goto MainEnd;
  201. }
  202. if (optind + 1 != ArgumentCount) {
  203. fprintf(stderr,
  204. "Error: Expected exactly one argument. "
  205. "See --help for usage.\n");
  206. goto MainEnd;
  207. }
  208. ZoneName = Arguments[optind];
  209. OutSize = InSize;
  210. KStatus = RtlFilterTimeZoneData(InBuffer,
  211. InSize,
  212. ZoneName,
  213. OutBuffer,
  214. &OutSize);
  215. if (!KSUCCESS(KStatus)) {
  216. if (KStatus == STATUS_NOT_FOUND) {
  217. fprintf(stderr, "Error: No such zone '%s'\n", ZoneName);
  218. } else if (KStatus == STATUS_FILE_CORRUPT) {
  219. fprintf(stderr, "Error: Invalid time zone data.\n");
  220. } else {
  221. fprintf(stderr, "Error: Failed to filter zone data: %d\n", KStatus);
  222. }
  223. goto MainEnd;
  224. }
  225. OutFile = fopen(OutPath, "wb");
  226. if (OutFile == NULL) {
  227. fprintf(stderr,
  228. "Error: Failed to open %s: %s.\n",
  229. OutPath,
  230. strerror(errno));
  231. goto MainEnd;
  232. }
  233. if (fwrite(OutBuffer, 1, OutSize, OutFile) != OutSize) {
  234. fprintf(stderr, "Error: Write error: %s\n", strerror(errno));
  235. goto MainEnd;
  236. }
  237. Status = 0;
  238. MainEnd:
  239. if (InBuffer != NULL) {
  240. free(InBuffer);
  241. }
  242. if (OutBuffer != NULL) {
  243. free(OutBuffer);
  244. }
  245. if (InFile != NULL) {
  246. fclose(InFile);
  247. }
  248. if (OutFile != NULL) {
  249. fclose(OutFile);
  250. }
  251. return Status;
  252. }
  253. //
  254. // --------------------------------------------------------- Internal Functions
  255. //