summaryrefslogtreecommitdiffstats
path: root/posixtz.c
blob: 4a36e10a63736a35d8ebd17b53e85abe4cf368f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/* ripped from uclibc 
*
* Copyright (C) 2010 Denys Vlasenko <vda.linux@googlemail.com>
* Copyright (C) 2011 Natanael Copa <ncopa@alpinelinux.org>
*
* GNU Library General Public License (LGPL) version 2 or later.
*
*/

#include <sys/stat.h>
#include <sys/types.h>

#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "posixtz.h"

#ifndef TZNAME_MAX
#define TZNAME_MAX _POSIX_TZNAME_MAX
#endif

#define TZ_BUFLEN               (2*TZNAME_MAX + 56)

char *posix_tz(const char *filename)
{
	int fd, r;
	static char buf[TZ_BUFLEN];
	char *p = NULL;
	fd = open(filename, O_RDONLY);
	if (fd < 0)
		return NULL;
	
	r = read(fd, buf, TZ_BUFLEN);
	if (r != TZ_BUFLEN
	    || strncmp(buf, "TZif", 4) != 0
	    || (unsigned char)buf[4] < 2
	    || lseek(fd, (off_t) -TZ_BUFLEN, SEEK_END) < 0
	   )
		goto ERROR;

	/* tzfile.h from tzcode database says about TZif2+ files:
	**
	** If tzh_version is '2' or greater, the above is followed by a second instance
	** of tzhead and a second instance of the data in which each coded transition
	** time uses 8 rather than 4 chars,
	** then a POSIX-TZ-environment-variable-style string for use in handling
	** instants after the last transition time stored in the file
	** (with nothing between the newlines if there is no POSIX representation for
	** such instants).
	*/
	r = read(fd, buf, TZ_BUFLEN);
	if (r <= 0 || buf[--r] != '\n')
		goto ERROR;
	buf[r] = 0;
	while (r != 0) {
		if (buf[--r] == '\n') {
			p = buf + r + 1;
			break;
		}
	} /* else ('\n' not found): p remains NULL */
ERROR:
	close(fd);
	return p;
}