Branch data Line data Source code
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 : 214 : _g_era_date_compare (const GEraDate *date1,
40 : : const GEraDate *date2)
41 : : {
42 [ + + ]: 214 : if (date1->type == G_ERA_DATE_SET &&
43 [ + + ]: 195 : date2->type == G_ERA_DATE_SET)
44 : : {
45 [ + + ]: 172 : if (date1->year != date2->year)
46 : 119 : return date1->year - date2->year;
47 [ + + ]: 53 : if (date1->month != date2->month)
48 : 25 : return date1->month - date2->month;
49 : 28 : return date1->day - date2->day;
50 : : }
51 : :
52 [ - + ]: 42 : if (date1->type == date2->type)
53 : 0 : return 0;
54 : :
55 [ + - + + ]: 42 : if (date1->type == G_ERA_DATE_MINUS_INFINITY || date2->type == G_ERA_DATE_PLUS_INFINITY)
56 : 22 : return -1;
57 [ + + + - ]: 20 : if (date1->type == G_ERA_DATE_PLUS_INFINITY || date2->type == G_ERA_DATE_MINUS_INFINITY)
58 : 20 : return 1;
59 : :
60 : : g_assert_not_reached ();
61 : : }
62 : :
63 : : static gboolean
64 : 21 : parse_era_date (const char *str,
65 : : const char *endptr,
66 : : GEraDate *out_date)
67 : : {
68 : 21 : const char *str_endptr = NULL;
69 : : int year_multiplier;
70 : : guint64 year, month, day;
71 : :
72 [ + + ]: 21 : year_multiplier = (str[0] == '-') ? -1 : 1;
73 [ + + - + ]: 21 : if (str[0] == '-' || str[0] == '+')
74 : 2 : str++;
75 : :
76 : 21 : year = g_ascii_strtoull (str, (gchar **) &str_endptr, 10);
77 : 21 : g_assert (str_endptr <= endptr);
78 [ + - + - : 21 : if (str_endptr == endptr || *str_endptr != '/' || year > G_MAXINT)
- + ]
79 : 0 : return FALSE;
80 : 21 : str = str_endptr + 1;
81 : :
82 : 21 : month = g_ascii_strtoull (str, (gchar **) &str_endptr, 10);
83 : 21 : g_assert (str_endptr <= endptr);
84 [ + - + - : 21 : if (str_endptr == endptr || *str_endptr != '/' || month < 1 || month > 12)
+ - - + ]
85 : 0 : return FALSE;
86 : 21 : str = str_endptr + 1;
87 : :
88 : 21 : day = g_ascii_strtoull (str, (gchar **) &str_endptr, 10);
89 : 21 : g_assert (str_endptr <= endptr);
90 [ + - + - : 21 : if (str_endptr != endptr || day < 1 || day > 31)
- + ]
91 : 0 : return FALSE;
92 : :
93 : : /* Success */
94 : 21 : out_date->type = G_ERA_DATE_SET;
95 : 21 : out_date->year = year_multiplier * year;
96 : 21 : out_date->month = month;
97 : 21 : out_date->day = day;
98 : :
99 : 21 : return TRUE;
100 : : }
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 : 39 : _g_era_description_segment_ref (GEraDescriptionSegment *segment)
113 : : {
114 : 39 : g_atomic_ref_count_inc (&segment->ref_count);
115 : 39 : return segment;
116 : : }
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 : 50 : _g_era_description_segment_unref (GEraDescriptionSegment *segment)
128 : : {
129 [ + + ]: 50 : if (g_atomic_ref_count_dec (&segment->ref_count))
130 : : {
131 : 11 : g_free (segment->era_format);
132 : 11 : g_free (segment->era_name);
133 : 11 : g_free (segment);
134 : : }
135 : 50 : }
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 : 3 : _g_era_description_parse (const char *desc)
158 : : {
159 : 3 : GPtrArray *segments = g_ptr_array_new_with_free_func ((GDestroyNotify) _g_era_description_segment_unref);
160 : :
161 [ + + ]: 15 : for (const char *p = desc; *p != '\0';)
162 : : {
163 : 12 : const char *next_colon, *endptr = NULL;
164 : 12 : GEraDescriptionSegment *segment = NULL;
165 : : char direction;
166 : : guint64 offset;
167 : : GEraDate start_date, end_date;
168 : 12 : char *era_name = NULL, *era_format = NULL;
169 : :
170 : : /* direction */
171 : 12 : direction = *p++;
172 [ - + - - ]: 12 : if (direction != '+' && direction != '-')
173 : 0 : goto error;
174 : :
175 [ - + ]: 12 : if (*p++ != ':')
176 : 0 : goto error;
177 : :
178 : : /* offset */
179 : 12 : next_colon = strchr (p, ':');
180 [ - + ]: 12 : if (next_colon == NULL)
181 : 0 : goto error;
182 : :
183 : 12 : offset = g_ascii_strtoull (p, (gchar **) &endptr, 10);
184 [ - + ]: 12 : if (endptr != next_colon)
185 : 0 : goto error;
186 : 12 : p = next_colon + 1;
187 : :
188 : : /* start_date */
189 : 12 : next_colon = strchr (p, ':');
190 [ - + ]: 12 : if (next_colon == NULL)
191 : 0 : goto error;
192 : :
193 [ - + ]: 12 : if (!parse_era_date (p, next_colon, &start_date))
194 : 0 : goto error;
195 : 12 : p = next_colon + 1;
196 : :
197 : : /* end_date */
198 : 12 : next_colon = strchr (p, ':');
199 [ - + ]: 12 : if (next_colon == NULL)
200 : 0 : goto error;
201 : :
202 [ + + ]: 12 : if (strncmp (p, "-*", 2) == 0)
203 : 1 : end_date.type = G_ERA_DATE_MINUS_INFINITY;
204 [ + + ]: 11 : else if (strncmp (p, "+*", 2) == 0)
205 : 2 : end_date.type = G_ERA_DATE_PLUS_INFINITY;
206 [ - + ]: 9 : else if (!parse_era_date (p, next_colon, &end_date))
207 : 0 : goto error;
208 : 12 : p = next_colon + 1;
209 : :
210 : : /* era_name */
211 : 12 : next_colon = strchr (p, ':');
212 [ - + ]: 12 : if (next_colon == NULL)
213 : 0 : goto error;
214 : :
215 [ - + ]: 12 : if (next_colon - p == 0)
216 : 0 : goto error;
217 : 12 : era_name = g_strndup (p, next_colon - p);
218 : 12 : 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 : 12 : next_colon = strchr (p, ';');
223 [ + + ]: 12 : if (next_colon == NULL)
224 : 2 : next_colon = p + strlen (p);
225 : :
226 [ - + ]: 12 : if (next_colon - p == 0)
227 : : {
228 : 0 : g_free (era_name);
229 : 0 : goto error;
230 : : }
231 : 12 : era_format = g_strndup (p, next_colon - p);
232 [ + + ]: 12 : if (*next_colon == ';')
233 : 10 : p = next_colon + 1;
234 : : else
235 : 2 : p = next_colon;
236 : :
237 : : /* Successfully parsed that segment. */
238 : 12 : segment = g_new0 (GEraDescriptionSegment, 1);
239 : 12 : g_atomic_ref_count_init (&segment->ref_count);
240 : 12 : segment->offset = offset;
241 : 12 : segment->start_date = start_date;
242 : 12 : segment->end_date = end_date;
243 : 12 : segment->direction_multiplier =
244 [ + + ]: 12 : ((_g_era_date_compare (&segment->start_date, &segment->end_date) <= 0) ? 1 : -1) *
245 [ - + ]: 12 : ((direction == '-') ? -1 : 1);
246 : 12 : segment->era_name = g_steal_pointer (&era_name);
247 : 12 : segment->era_format = g_steal_pointer (&era_format);
248 : :
249 : 12 : g_ptr_array_add (segments, g_steal_pointer (&segment));
250 : : }
251 : :
252 : 3 : return g_steal_pointer (&segments);
253 : :
254 : 0 : error:
255 : 0 : g_ptr_array_unref (segments);
256 : 0 : return NULL;
257 : : }
|