Coverage Report

Created: 2025-08-26 06:51

/src/tinysparql/subprojects/glib-2.80.3/glib/gdatetime-private.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2023 GNOME Foundation Inc.
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18
 *
19
 * Authors:
20
 *  - Philip Withnall <pwithnall@gnome.org>
21
 */
22
23
#include "glib.h"
24
#include "gdatetime-private.h"
25
26
/**
27
 * _g_era_date_compare:
28
 * @date1: first date
29
 * @date2: second date
30
 *
31
 * Compare two #GEraDates for ordering, taking into account negative and
32
 * positive infinity.
33
 *
34
 * Returns: strcmp()-style integer, `<0` indicates `date1 < date2`, `0`
35
 *   indicates `date1 == date2`, `>0` indicates `date1 > date2`
36
 * Since: 2.80
37
 */
38
int
39
_g_era_date_compare (const GEraDate *date1,
40
                     const GEraDate *date2)
41
0
{
42
0
  if (date1->type == G_ERA_DATE_SET &&
43
0
      date2->type == G_ERA_DATE_SET)
44
0
    {
45
0
      if (date1->year != date2->year)
46
0
        return date1->year - date2->year;
47
0
      if (date1->month != date2->month)
48
0
        return date1->month - date2->month;
49
0
      return date1->day - date2->day;
50
0
    }
51
52
0
  if (date1->type == date2->type)
53
0
    return 0;
54
55
0
  if (date1->type == G_ERA_DATE_MINUS_INFINITY || date2->type == G_ERA_DATE_PLUS_INFINITY)
56
0
    return -1;
57
0
  if (date1->type == G_ERA_DATE_PLUS_INFINITY || date2->type == G_ERA_DATE_MINUS_INFINITY)
58
0
    return 1;
59
60
0
  g_assert_not_reached ();
61
0
}
62
63
static gboolean
64
parse_era_date (const char *str,
65
                const char *endptr,
66
                GEraDate   *out_date)
67
0
{
68
0
  const char *str_endptr = NULL;
69
0
  int year_multiplier;
70
0
  guint64 year, month, day;
71
72
0
  year_multiplier = (str[0] == '-') ? -1 : 1;
73
0
  if (str[0] == '-' || str[0] == '+')
74
0
    str++;
75
76
0
  year = g_ascii_strtoull (str, (gchar **) &str_endptr, 10);
77
0
  g_assert (str_endptr <= endptr);
78
0
  if (str_endptr == endptr || *str_endptr != '/' || year > G_MAXINT)
79
0
    return FALSE;
80
0
  str = str_endptr + 1;
81
82
0
  month = g_ascii_strtoull (str, (gchar **) &str_endptr, 10);
83
0
  g_assert (str_endptr <= endptr);
84
0
  if (str_endptr == endptr || *str_endptr != '/' || month < 1 || month > 12)
85
0
    return FALSE;
86
0
  str = str_endptr + 1;
87
88
0
  day = g_ascii_strtoull (str, (gchar **) &str_endptr, 10);
89
0
  g_assert (str_endptr <= endptr);
90
0
  if (str_endptr != endptr || day < 1 || day > 31)
91
0
    return FALSE;
92
93
  /* Success */
94
0
  out_date->type = G_ERA_DATE_SET;
95
0
  out_date->year = year_multiplier * year;
96
0
  out_date->month = month;
97
0
  out_date->day = day;
98
99
0
  return TRUE;
100
0
}
101
102
/**
103
 * _g_era_description_segment_ref:
104
 * @segment: a #GEraDescriptionSegment
105
 *
106
 * Increase the ref count of @segment.
107
 *
108
 * Returns: (transfer full): @segment
109
 * Since: 2.80
110
 */
111
GEraDescriptionSegment *
112
_g_era_description_segment_ref (GEraDescriptionSegment *segment)
113
0
{
114
0
  g_atomic_ref_count_inc (&segment->ref_count);
115
0
  return segment;
116
0
}
117
118
/**
119
 * _g_era_description_segment_unref:
120
 * @segment: (transfer full): a #GEraDescriptionSegment to unref
121
 *
122
 * Decreases the ref count of @segment.
123
 *
124
 * Since: 2.80
125
 */
126
void
127
_g_era_description_segment_unref (GEraDescriptionSegment *segment)
128
0
{
129
0
  if (g_atomic_ref_count_dec (&segment->ref_count))
130
0
    {
131
0
      g_free (segment->era_format);
132
0
      g_free (segment->era_name);
133
0
      g_free (segment);
134
0
    }
135
0
}
136
137
/**
138
 * _g_era_description_parse:
139
 * @desc: an `ERA` description string from `nl_langinfo()`
140
 *
141
 * Parse an ERA description string. See [`nl_langinfo(3)`](man:nl_langinfo(3)).
142
 *
143
 * Example description string for th_TH.UTF-8:
144
 * ```
145
 * +:1:-543/01/01:+*:พ.ศ.:%EC %Ey
146
 * ```
147
 *
148
 * @desc must be in UTF-8, so all conversion from the locale encoding must
149
 * happen before this function is called. The resulting `era_name` and
150
 * `era_format` in the returned segments will be in UTF-8.
151
 *
152
 * Returns: (transfer full) (nullable) (element-type GEraDescriptionSegment):
153
 *   array of one or more parsed era segments, or %NULL if parsing failed
154
 * Since: 2.80
155
 */
156
GPtrArray *
157
_g_era_description_parse (const char *desc)
158
0
{
159
0
  GPtrArray *segments = g_ptr_array_new_with_free_func ((GDestroyNotify) _g_era_description_segment_unref);
160
161
0
  for (const char *p = desc; *p != '\0';)
162
0
    {
163
0
      const char *next_colon, *endptr = NULL;
164
0
      GEraDescriptionSegment *segment = NULL;
165
0
      char direction;
166
0
      guint64 offset;
167
0
      GEraDate start_date, end_date;
168
0
      char *era_name = NULL, *era_format = NULL;
169
170
      /* direction */
171
0
      direction = *p++;
172
0
      if (direction != '+' && direction != '-')
173
0
        goto error;
174
175
0
      if (*p++ != ':')
176
0
        goto error;
177
178
      /* offset */
179
0
      next_colon = strchr (p, ':');
180
0
      if (next_colon == NULL)
181
0
        goto error;
182
183
0
      offset = g_ascii_strtoull (p, (gchar **) &endptr, 10);
184
0
      if (endptr != next_colon)
185
0
        goto error;
186
0
      p = next_colon + 1;
187
188
      /* start_date */
189
0
      next_colon = strchr (p, ':');
190
0
      if (next_colon == NULL)
191
0
        goto error;
192
193
0
      if (!parse_era_date (p, next_colon, &start_date))
194
0
        goto error;
195
0
      p = next_colon + 1;
196
197
      /* end_date */
198
0
      next_colon = strchr (p, ':');
199
0
      if (next_colon == NULL)
200
0
        goto error;
201
202
0
      if (strncmp (p, "-*", 2) == 0)
203
0
        end_date.type = G_ERA_DATE_MINUS_INFINITY;
204
0
      else if (strncmp (p, "+*", 2) == 0)
205
0
        end_date.type = G_ERA_DATE_PLUS_INFINITY;
206
0
      else if (!parse_era_date (p, next_colon, &end_date))
207
0
        goto error;
208
0
      p = next_colon + 1;
209
210
      /* era_name */
211
0
      next_colon = strchr (p, ':');
212
0
      if (next_colon == NULL)
213
0
        goto error;
214
215
0
      if (next_colon - p == 0)
216
0
        goto error;
217
0
      era_name = g_strndup (p, next_colon - p);
218
0
      p = next_colon + 1;
219
220
      /* era_format; either the final field in the segment (followed by a
221
       * semicolon) or the description (followed by nul) */
222
0
      next_colon = strchr (p, ';');
223
0
      if (next_colon == NULL)
224
0
        next_colon = p + strlen (p);
225
226
0
      if (next_colon - p == 0)
227
0
        {
228
0
          g_free (era_name);
229
0
          goto error;
230
0
        }
231
0
      era_format = g_strndup (p, next_colon - p);
232
0
      if (*next_colon == ';')
233
0
        p = next_colon + 1;
234
0
      else
235
0
        p = next_colon;
236
237
      /* Successfully parsed that segment. */
238
0
      segment = g_new0 (GEraDescriptionSegment, 1);
239
0
      g_atomic_ref_count_init (&segment->ref_count);
240
0
      segment->offset = offset;
241
0
      segment->start_date = start_date;
242
0
      segment->end_date = end_date;
243
0
      segment->direction_multiplier =
244
0
          ((_g_era_date_compare (&segment->start_date, &segment->end_date) <= 0) ? 1 : -1) *
245
0
          ((direction == '-') ? -1 : 1);
246
0
      segment->era_name = g_steal_pointer (&era_name);
247
0
      segment->era_format = g_steal_pointer (&era_format);
248
249
0
      g_ptr_array_add (segments, g_steal_pointer (&segment));
250
0
    }
251
252
0
  return g_steal_pointer (&segments);
253
254
0
error:
255
0
  g_ptr_array_unref (segments);
256
0
  return NULL;
257
0
}