FreeRDP
TimeZoneIanaAbbrevMap.c
1 
21 #include "TimeZoneIanaAbbrevMap.h"
22 
23 #include <stdlib.h>
24 #include <dirent.h>
25 #include <time.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 
29 #include <winpr/string.h>
30 
31 typedef struct
32 {
33  char* Iana;
34  char* Abbrev;
35 } TimeZoneInanaAbbrevMapEntry;
36 
37 const static char* zonepath = "/usr/share/zoneinfo";
38 
39 static TimeZoneInanaAbbrevMapEntry* TimeZoneIanaAbbrevMap = NULL;
40 static size_t TimeZoneIanaAbbrevMapSize = 0;
41 
42 static void append(const char* iana, const char* sname)
43 {
44  const size_t size = TimeZoneIanaAbbrevMapSize + 1;
45 
46  TimeZoneInanaAbbrevMapEntry* tmp =
47  realloc(TimeZoneIanaAbbrevMap, size * sizeof(TimeZoneInanaAbbrevMapEntry));
48  if (!tmp)
49  return;
50  TimeZoneIanaAbbrevMap = tmp;
51  TimeZoneIanaAbbrevMapSize = size;
52 
53  TimeZoneInanaAbbrevMapEntry* cur = &TimeZoneIanaAbbrevMap[size - 1];
54  cur->Abbrev = _strdup(sname);
55  cur->Iana = _strdup(iana);
56 }
57 
58 static void append_timezone(const char* dir, const char* name)
59 {
60  char* tz = NULL;
61  if (!dir && !name)
62  return;
63  if (!dir)
64  {
65  size_t len = 0;
66  winpr_asprintf(&tz, &len, "%s", name);
67  }
68  else
69  {
70  size_t len = 0;
71  winpr_asprintf(&tz, &len, "%s/%s", dir, name);
72  }
73  if (!tz)
74  return;
75 
76  const char* otz = getenv("TZ");
77  char* oldtz = NULL;
78  if (otz)
79  oldtz = _strdup(otz);
80  setenv("TZ", tz, 1);
81  tzset();
82  const time_t t = time(NULL);
83  struct tm lt = { 0 };
84  (void)localtime_r(&t, &lt);
85  append(tz, lt.tm_zone);
86  if (oldtz)
87  {
88  setenv("TZ", oldtz, 1);
89  free(oldtz);
90  }
91  else
92  unsetenv("TZ");
93  free(tz);
94 }
95 
96 static void handle_link(const char* base, const char* dir, const char* name);
97 
98 static char* topath(const char* base, const char* bname, const char* name)
99 {
100  size_t plen = 0;
101  char* path = NULL;
102 
103  if (!base && !bname && !name)
104  return NULL;
105 
106  if (!base && !name)
107  return _strdup(bname);
108 
109  if (!bname && !name)
110  return _strdup(base);
111 
112  if (!base && !bname)
113  return _strdup(name);
114 
115  if (!base)
116  winpr_asprintf(&path, &plen, "%s/%s", bname, name);
117  else if (!bname)
118  winpr_asprintf(&path, &plen, "%s/%s", base, name);
119  else if (!name)
120  winpr_asprintf(&path, &plen, "%s/%s", base, bname);
121  else
122  winpr_asprintf(&path, &plen, "%s/%s/%s", base, bname, name);
123  return path;
124 }
125 
126 static void iterate_subdir_recursive(const char* base, const char* bname, const char* name)
127 {
128  char* path = topath(base, bname, name);
129  if (!path)
130  return;
131 
132  DIR* d = opendir(path);
133  if (d)
134  {
135  struct dirent* dp = NULL;
136  while ((dp = readdir(d)) != NULL)
137  {
138  switch (dp->d_type)
139  {
140  case DT_DIR:
141  {
142  if (strcmp(dp->d_name, ".") == 0)
143  continue;
144  if (strcmp(dp->d_name, "..") == 0)
145  continue;
146  iterate_subdir_recursive(path, dp->d_name, NULL);
147  }
148  break;
149  case DT_LNK:
150  handle_link(base, bname, dp->d_name);
151  break;
152  case DT_REG:
153  append_timezone(bname, dp->d_name);
154  break;
155  default:
156  break;
157  }
158  }
159  closedir(d);
160  }
161  free(path);
162 }
163 
164 static char* get_link_target(const char* base, const char* dir, const char* name)
165 {
166  char* apath = NULL;
167  char* path = topath(base, dir, name);
168  if (!path)
169  return NULL;
170 
171  SSIZE_T rc = -1;
172  size_t size = 0;
173  char* target = NULL;
174  do
175  {
176  size += 64;
177  char* tmp = realloc(target, size + 1);
178  if (!tmp)
179  goto fail;
180 
181  target = tmp;
182 
183  memset(target, 0, size + 1);
184  rc = readlink(path, target, size);
185  if (rc < 0)
186  goto fail;
187  } while ((size_t)rc >= size);
188 
189  apath = topath(base, dir, target);
190 fail:
191  free(target);
192  free(path);
193  return apath;
194 }
195 
196 void handle_link(const char* base, const char* dir, const char* name)
197 {
198  int isDir = -1;
199 
200  char* target = get_link_target(base, dir, name);
201  if (target)
202  {
203  struct stat s = { 0 };
204  const int rc3 = stat(target, &s);
205  if (rc3 == 0)
206  isDir = S_ISDIR(s.st_mode);
207 
208  free(target);
209  }
210 
211  switch (isDir)
212  {
213  case 1:
214  iterate_subdir_recursive(base, dir, name);
215  break;
216  case 0:
217  append_timezone(dir, name);
218  break;
219  default:
220  break;
221  }
222 }
223 
224 static void TimeZoneIanaAbbrevCleanup(void)
225 {
226  if (!TimeZoneIanaAbbrevMap)
227  return;
228 
229  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
230  {
231  TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
232  free(entry->Iana);
233  free(entry->Abbrev);
234  }
235  free(TimeZoneIanaAbbrevMap);
236  TimeZoneIanaAbbrevMap = NULL;
237  TimeZoneIanaAbbrevMapSize = 0;
238 }
239 
240 static void TimeZoneIanaAbbrevInitialize(void)
241 {
242  static BOOL initialized = FALSE;
243  if (initialized)
244  return;
245 
246  iterate_subdir_recursive(zonepath, NULL, NULL);
247  (void)atexit(TimeZoneIanaAbbrevCleanup);
248  initialized = TRUE;
249 }
250 
251 size_t TimeZoneIanaAbbrevGet(const char* abbrev, const char** list, size_t listsize)
252 {
253  TimeZoneIanaAbbrevInitialize();
254 
255  size_t rc = 0;
256  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
257  {
258  const TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
259  if (strcmp(abbrev, entry->Abbrev) == 0)
260  {
261  if (listsize > rc)
262  list[rc] = entry->Iana;
263  rc++;
264  }
265  }
266 
267  return rc;
268 }