Coverage Report

Created: 2026-04-12 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/timezone/TimeZoneIanaAbbrevMap.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Time Zone
4
 *
5
 * Copyright 2024 Armin Novak <anovak@thincast.com>
6
 * Copyright 2024 Thincast Technologies GmbH
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
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/atexit.h>
30
#include <winpr/string.h>
31
#include <winpr/synch.h>
32
#include "timezone.h"
33
34
typedef struct
35
{
36
  char* Iana;
37
  char* Abbrev;
38
} TimeZoneInanaAbbrevMapEntry;
39
40
const static char* zonepath = "/usr/share/zoneinfo";
41
42
static TimeZoneInanaAbbrevMapEntry* TimeZoneIanaAbbrevMap = nullptr;
43
static size_t TimeZoneIanaAbbrevMapSize = 0;
44
45
static void append(const char* iana, const char* sname)
46
0
{
47
0
  const size_t size = TimeZoneIanaAbbrevMapSize + 1;
48
49
0
  TimeZoneInanaAbbrevMapEntry* tmp =
50
0
      realloc(TimeZoneIanaAbbrevMap, size * sizeof(TimeZoneInanaAbbrevMapEntry));
51
0
  if (!tmp)
52
0
    return;
53
0
  TimeZoneIanaAbbrevMap = tmp;
54
0
  TimeZoneIanaAbbrevMapSize = size;
55
56
0
  TimeZoneInanaAbbrevMapEntry* cur = &TimeZoneIanaAbbrevMap[size - 1];
57
0
  cur->Abbrev = _strdup(sname);
58
0
  cur->Iana = _strdup(iana);
59
0
}
60
61
static void append_timezone(const char* dir, const char* name)
62
0
{
63
0
  char* tz = nullptr;
64
0
  if (!dir && !name)
65
0
    return;
66
0
  if (!dir)
67
0
  {
68
0
    size_t len = 0;
69
0
    winpr_asprintf(&tz, &len, "%s", name);
70
0
  }
71
0
  else
72
0
  {
73
0
    size_t len = 0;
74
0
    winpr_asprintf(&tz, &len, "%s/%s", dir, name);
75
0
  }
76
0
  if (!tz)
77
0
    return;
78
79
0
  char* oldtz = setNewAndSaveOldTZ(tz);
80
81
0
  const time_t t = time(nullptr);
82
0
  struct tm lt = WINPR_C_ARRAY_INIT;
83
0
  (void)localtime_r(&t, &lt);
84
0
  append(tz, lt.tm_zone);
85
0
  restoreSavedTZ(oldtz);
86
0
  free(tz);
87
0
}
88
89
static void handle_link(const char* base, const char* dir, const char* name);
90
91
static char* topath(const char* base, const char* bname, const char* name)
92
0
{
93
0
  size_t plen = 0;
94
0
  char* path = nullptr;
95
96
0
  if (!base && !bname && !name)
97
0
    return nullptr;
98
99
0
  if (!base && !name)
100
0
    return _strdup(bname);
101
102
0
  if (!bname && !name)
103
0
    return _strdup(base);
104
105
0
  if (!base && !bname)
106
0
    return _strdup(name);
107
108
0
  if (!base)
109
0
    winpr_asprintf(&path, &plen, "%s/%s", bname, name);
110
0
  else if (!bname)
111
0
    winpr_asprintf(&path, &plen, "%s/%s", base, name);
112
0
  else if (!name)
113
0
    winpr_asprintf(&path, &plen, "%s/%s", base, bname);
114
0
  else
115
0
    winpr_asprintf(&path, &plen, "%s/%s/%s", base, bname, name);
116
0
  return path;
117
0
}
118
119
static void iterate_subdir_recursive(const char* base, const char* bname, const char* name)
120
0
{
121
0
  char* path = topath(base, bname, name);
122
0
  if (!path)
123
0
    return;
124
125
0
  DIR* d = opendir(path);
126
0
  if (d)
127
0
  {
128
0
    struct dirent* dp = nullptr;
129
    // NOLINTNEXTLINE(concurrency-mt-unsafe)
130
0
    while ((dp = readdir(d)) != nullptr)
131
0
    {
132
0
      switch (dp->d_type)
133
0
      {
134
0
        case DT_DIR:
135
0
        {
136
0
          if (strcmp(dp->d_name, ".") == 0)
137
0
            continue;
138
0
          if (strcmp(dp->d_name, "..") == 0)
139
0
            continue;
140
0
          iterate_subdir_recursive(path, dp->d_name, nullptr);
141
0
        }
142
0
        break;
143
0
        case DT_LNK:
144
0
          handle_link(base, bname, dp->d_name);
145
0
          break;
146
0
        case DT_REG:
147
0
          append_timezone(bname, dp->d_name);
148
0
          break;
149
0
        default:
150
0
          break;
151
0
      }
152
0
    }
153
0
    closedir(d);
154
0
  }
155
0
  free(path);
156
0
}
157
158
static char* get_link_target(const char* base, const char* dir, const char* name)
159
0
{
160
0
  char* apath = nullptr;
161
0
  char* path = topath(base, dir, name);
162
0
  if (!path)
163
0
    return nullptr;
164
165
0
  SSIZE_T rc = -1;
166
0
  size_t size = 0;
167
0
  char* target = nullptr;
168
0
  do
169
0
  {
170
0
    size += 64;
171
0
    char* tmp = realloc(target, size + 1);
172
0
    if (!tmp)
173
0
      goto fail;
174
175
0
    target = tmp;
176
177
0
    memset(target, 0, size + 1);
178
0
    rc = readlink(path, target, size);
179
0
    if (rc < 0)
180
0
      goto fail;
181
0
  } while ((size_t)rc >= size);
182
183
0
  apath = topath(base, dir, target);
184
0
fail:
185
0
  free(target);
186
0
  free(path);
187
0
  return apath;
188
0
}
189
190
void handle_link(const char* base, const char* dir, const char* name)
191
0
{
192
0
  int isDir = -1;
193
194
0
  char* target = get_link_target(base, dir, name);
195
0
  if (target)
196
0
  {
197
0
    struct stat s = WINPR_C_ARRAY_INIT;
198
0
    const int rc3 = stat(target, &s);
199
0
    if (rc3 == 0)
200
0
      isDir = S_ISDIR(s.st_mode);
201
202
0
    free(target);
203
0
  }
204
205
0
  switch (isDir)
206
0
  {
207
0
    case 1:
208
0
      iterate_subdir_recursive(base, dir, name);
209
0
      break;
210
0
    case 0:
211
0
      append_timezone(dir, name);
212
0
      break;
213
0
    default:
214
0
      break;
215
0
  }
216
0
}
217
218
static void TimeZoneIanaAbbrevCleanup(void)
219
0
{
220
0
  if (!TimeZoneIanaAbbrevMap)
221
0
    return;
222
223
0
  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
224
0
  {
225
0
    TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
226
0
    free(entry->Iana);
227
0
    free(entry->Abbrev);
228
0
  }
229
0
  free(TimeZoneIanaAbbrevMap);
230
0
  TimeZoneIanaAbbrevMap = nullptr;
231
0
  TimeZoneIanaAbbrevMapSize = 0;
232
0
}
233
234
static BOOL CALLBACK TimeZoneIanaAbbrevInitialize(WINPR_ATTR_UNUSED PINIT_ONCE once,
235
                                                  WINPR_ATTR_UNUSED PVOID param,
236
                                                  WINPR_ATTR_UNUSED PVOID* context)
237
0
{
238
0
  iterate_subdir_recursive(zonepath, nullptr, nullptr);
239
0
  (void)winpr_atexit(TimeZoneIanaAbbrevCleanup);
240
241
0
  return TRUE;
242
0
}
243
244
size_t TimeZoneIanaAbbrevGet(const char* abbrev, const char** list, size_t listsize)
245
0
{
246
0
  static INIT_ONCE init_guard = INIT_ONCE_STATIC_INIT;
247
248
0
  if (!InitOnceExecuteOnce(&init_guard, TimeZoneIanaAbbrevInitialize, nullptr, nullptr))
249
0
    return 0;
250
251
0
  size_t rc = 0;
252
0
  for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
253
0
  {
254
0
    const TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
255
0
    if (strcmp(abbrev, entry->Abbrev) == 0)
256
0
    {
257
0
      if (listsize > rc)
258
0
        list[rc] = entry->Iana;
259
0
      rc++;
260
0
    }
261
0
  }
262
263
0
  return rc;
264
0
}