Bug Summary

File:weather-metar.c
Warning:line 169, column 24
Out of bound memory access (access exceeds upper limit of memory block)

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name weather-metar.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/rootdir/libcafeweather -resource-dir /usr/lib/llvm-16/lib/clang/16 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/ctk-3.0 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/gio-unix-2.0 -I /usr/include/atk-1.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I .. -I . -I /usr/include/libxml2 -I /usr/include/libsoup-3.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/sysprof-6 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D G_LOG_DOMAIN="CafeWeather" -D CAFELOCALEDIR="/usr/share/locale" -D CAFEWEATHER_XML_LOCATION_DIR="/usr/share/libcafeweather" -D PIC -internal-isystem /usr/lib/llvm-16/lib/clang/16/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir=/rootdir/libcafeweather -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2024-02-11-185743-15480-1 -x c weather-metar.c
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2/* weather-metar.c - Weather server functions (METAR)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see
16 * <http://www.gnu.org/licenses/>.
17 */
18
19#ifdef HAVE_CONFIG_H1
20#include <config.h>
21#endif
22
23#include <stdlib.h>
24#include <string.h>
25#include <sys/types.h>
26#include <regex.h>
27
28#define CAFEWEATHER_I_KNOW_THIS_IS_UNSTABLE
29#include "weather.h"
30#include "weather-priv.h"
31
32enum {
33 TIME_RE,
34 WIND_RE,
35 VIS_RE,
36 COND_RE,
37 CLOUD_RE,
38 TEMP_RE,
39 PRES_RE,
40
41 RE_NUM
42};
43
44/* Return time of weather report as secs since epoch UTC */
45static time_t
46make_time (gint utcDate, gint utcHour, gint utcMin)
47{
48 const time_t now = time (NULL((void*)0));
49 struct tm tm;
50
51 localtime_r (&now, &tm);
52
53 /* If last reading took place just before midnight UTC on the
54 * first, adjust the date downward to allow for the month
55 * change-over. This ASSUMES that the reading won't be more than
56 * 24 hrs old! */
57 if ((utcDate > tm.tm_mday) && (tm.tm_mday == 1)) {
58 tm.tm_mday = 0; /* mktime knows this is the last day of the previous
59 * month. */
60 } else {
61 tm.tm_mday = utcDate;
62 }
63 tm.tm_hour = utcHour;
64 tm.tm_min = utcMin;
65 tm.tm_sec = 0;
66
67 /* mktime() assumes value is local, not UTC. Use tm_gmtoff to compensate */
68#ifdef HAVE_TM_TM_GMOFF1
69 return tm.tm_gmtoff + mktime (&tm);
70#elif defined HAVE_TIMEZONE
71 return timezone + mktime (&tm);
72#endif
73}
74
75static void
76metar_tok_time (gchar *tokp, WeatherInfo *info)
77{
78 gint day, hr, min;
79
80 sscanf (tokp, "%2u%2u%2u", &day, &hr, &min);
81 info->update = make_time (day, hr, min);
82}
83
84static void
85metar_tok_wind (gchar *tokp, WeatherInfo *info)
86{
87 gchar sdir[4], sspd[4], sgust[4];
88 gint dir, spd = -1;
89 gchar *gustp;
90 size_t glen;
91
92 strncpy (sdir, tokp, 3);
93 sdir[3] = 0;
94 dir = (!strcmp (sdir, "VRB")) ? -1 : atoi (sdir);
95
96 memset (sspd, 0, sizeof (sspd));
97 glen = strspn (tokp + 3, CONST_DIGITS"0123456789");
98 strncpy (sspd, tokp + 3, glen);
99 spd = atoi (sspd);
100 tokp += glen + 3;
101
102 gustp = strchr (tokp, 'G');
103 if (gustp) {
104 memset (sgust, 0, sizeof (sgust));
105 glen = strspn (gustp + 1, CONST_DIGITS"0123456789");
106 strncpy (sgust, gustp + 1, glen);
107 tokp = gustp + 1 + glen;
108 }
109
110 if (!strcmp (tokp, "MPS"))
111 info->windspeed = WINDSPEED_MS_TO_KNOTS ((WeatherWindSpeed)spd)(((WeatherWindSpeed)spd) / 0.514444);
112 else
113 info->windspeed = (WeatherWindSpeed)spd;
114
115 if ((349 <= dir) || (dir <= 11))
116 info->wind = WIND_N;
117 else if ((12 <= dir) && (dir <= 33))
118 info->wind = WIND_NNE;
119 else if ((34 <= dir) && (dir <= 56))
120 info->wind = WIND_NE;
121 else if ((57 <= dir) && (dir <= 78))
122 info->wind = WIND_ENE;
123 else if ((79 <= dir) && (dir <= 101))
124 info->wind = WIND_E;
125 else if ((102 <= dir) && (dir <= 123))
126 info->wind = WIND_ESE;
127 else if ((124 <= dir) && (dir <= 146))
128 info->wind = WIND_SE;
129 else if ((147 <= dir) && (dir <= 168))
130 info->wind = WIND_SSE;
131 else if ((169 <= dir) && (dir <= 191))
132 info->wind = WIND_S;
133 else if ((192 <= dir) && (dir <= 213))
134 info->wind = WIND_SSW;
135 else if ((214 <= dir) && (dir <= 236))
136 info->wind = WIND_SW;
137 else if ((237 <= dir) && (dir <= 258))
138 info->wind = WIND_WSW;
139 else if ((259 <= dir) && (dir <= 281))
140 info->wind = WIND_W;
141 else if ((282 <= dir) && (dir <= 303))
142 info->wind = WIND_WNW;
143 else if ((304 <= dir) && (dir <= 326))
144 info->wind = WIND_NW;
145 else if ((327 <= dir) && (dir <= 348))
146 info->wind = WIND_NNW;
147}
148
149static void
150metar_tok_vis (gchar *tokp, WeatherInfo *info)
151{
152 gchar *pfrac, *pend, *psp;
153 gchar sval[6];
154 gint num, den, val;
155
156 memset (sval, 0, sizeof (sval));
157
158 if (!strcmp (tokp,"CAVOK")) {
1
Assuming the condition is false
2
Taking false branch
159 // "Ceiling And Visibility OK": visibility >= 10 KM
160 info->visibility=10000. / VISIBILITY_SM_TO_M (1.)(((1.) * 1.609344) * 1000);
161 info->sky = SKY_CLEAR;
162 } else if (0 != (pend = strstr (tokp, "SM"))) {
3
Assuming the condition is true
4
Taking true branch
163 // US observation: field ends with "SM"
164 pfrac = strchr (tokp, '/');
165 if (pfrac) {
5
Assuming 'pfrac' is non-null
6
Taking true branch
166 if (*tokp == 'M') {
7
Assuming the condition is false
8
Taking false branch
167 info->visibility = 0.001;
168 } else {
169 num = (*(pfrac - 1) - '0');
9
Out of bound memory access (access exceeds upper limit of memory block)
170 strncpy (sval, pfrac + 1, pend - pfrac - 1);
171 den = atoi (sval);
172 info->visibility =
173 ((WeatherVisibility)num / ((WeatherVisibility)den));
174
175 psp = strchr (tokp, ' ');
176 if (psp) {
177 *psp = '\0';
178 val = atoi (tokp);
179 info->visibility += (WeatherVisibility)val;
180 }
181 }
182 } else {
183 strncpy (sval, tokp, pend - tokp);
184 val = atoi (sval);
185 info->visibility = (WeatherVisibility)val;
186 }
187 } else {
188 // International observation: NNNN(DD NNNNDD)?
189 // For now: use only the minimum visibility and ignore its direction
190 strncpy (sval, tokp, strspn (tokp, CONST_DIGITS"0123456789"));
191 val = atoi (sval);
192 info->visibility = (WeatherVisibility)val / VISIBILITY_SM_TO_M (1.)(((1.) * 1.609344) * 1000);
193 }
194}
195
196static void
197metar_tok_cloud (gchar *tokp, WeatherInfo *info)
198{
199 gchar stype[4], salt[4];
200
201 strncpy (stype, tokp, 3);
202 stype[3] = 0;
203 if (strlen (tokp) == 6) {
204 strncpy (salt, tokp + 3, 3);
205 salt[3] = 0;
206 }
207
208 if (!strcmp (stype, "CLR")) {
209 info->sky = SKY_CLEAR;
210 } else if (!strcmp (stype, "SKC")) {
211 info->sky = SKY_CLEAR;
212 } else if (!strcmp (stype, "NSC")) {
213 info->sky = SKY_CLEAR;
214 } else if (!strcmp (stype, "BKN")) {
215 info->sky = SKY_BROKEN;
216 } else if (!strcmp (stype, "SCT")) {
217 info->sky = SKY_SCATTERED;
218 } else if (!strcmp (stype, "FEW")) {
219 info->sky = SKY_FEW;
220 } else if (!strcmp (stype, "OVC")) {
221 info->sky = SKY_OVERCAST;
222 }
223}
224
225static void
226metar_tok_pres (gchar *tokp, WeatherInfo *info)
227{
228 if (*tokp == 'A') {
229 gchar sintg[3], sfract[3];
230 gint intg, fract;
231
232 strncpy (sintg, tokp + 1, 2);
233 sintg[2] = 0;
234 intg = atoi (sintg);
235
236 strncpy (sfract, tokp + 3, 2);
237 sfract[2] = 0;
238 fract = atoi (sfract);
239
240 info->pressure = (WeatherPressure)intg + (((WeatherPressure)fract)/100.0);
241 } else { /* *tokp == 'Q' */
242 gchar spres[5];
243 gint pres;
244
245 strncpy (spres, tokp + 1, 4);
246 spres[4] = 0;
247 pres = atoi (spres);
248
249 info->pressure = PRESSURE_MBAR_TO_INCH ((WeatherPressure)pres)(((WeatherPressure)pres) * 0.029533373);
250 }
251}
252
253static void
254metar_tok_temp (gchar *tokp, WeatherInfo *info)
255{
256 gchar *ptemp, *pdew, *psep;
257
258 psep = strchr (tokp, '/');
259 *psep = 0;
260 ptemp = tokp;
261 pdew = psep + 1;
262
263 info->temp = (*ptemp == 'M') ? TEMP_C_TO_F (-atoi (ptemp + 1))(((-atoi (ptemp + 1)) * 1.8) + 32.0)
264 : TEMP_C_TO_F (atoi (ptemp))(((atoi (ptemp)) * 1.8) + 32.0);
265 if (*pdew) {
266 info->dew = (*pdew == 'M') ? TEMP_C_TO_F (-atoi (pdew + 1))(((-atoi (pdew + 1)) * 1.8) + 32.0)
267 : TEMP_C_TO_F (atoi (pdew))(((atoi (pdew)) * 1.8) + 32.0);
268 } else {
269 info->dew = -1000.0;
270 }
271}
272
273static void
274metar_tok_cond (gchar *tokp, WeatherInfo *info)
275{
276 gchar squal[3], sphen[4];
277 gchar *pphen;
278
279 if ((strlen (tokp) > 3) && ((*tokp == '+') || (*tokp == '-')))
280 ++tokp; /* FIX */
281
282 if ((*tokp == '+') || (*tokp == '-'))
283 pphen = tokp + 1;
284 else if (strlen (tokp) < 4)
285 pphen = tokp;
286 else
287 pphen = tokp + 2;
288
289 memset (squal, 0, sizeof (squal));
290 strncpy (squal, tokp, pphen - tokp);
291 squal[pphen - tokp] = 0;
292
293 memset (sphen, 0, sizeof (sphen));
294 strncpy (sphen, pphen, sizeof (sphen));
295 sphen[sizeof (sphen)-1] = '\0';
296
297 /* Defaults */
298 info->cond.qualifier = QUALIFIER_NONE;
299 info->cond.phenomenon = PHENOMENON_NONE;
300 info->cond.significant = FALSE(0);
301
302 if (!strcmp (squal, "")) {
303 info->cond.qualifier = QUALIFIER_MODERATE;
304 } else if (!strcmp (squal, "-")) {
305 info->cond.qualifier = QUALIFIER_LIGHT;
306 } else if (!strcmp (squal, "+")) {
307 info->cond.qualifier = QUALIFIER_HEAVY;
308 } else if (!strcmp (squal, "VC")) {
309 info->cond.qualifier = QUALIFIER_VICINITY;
310 } else if (!strcmp (squal, "MI")) {
311 info->cond.qualifier = QUALIFIER_SHALLOW;
312 } else if (!strcmp (squal, "BC")) {
313 info->cond.qualifier = QUALIFIER_PATCHES;
314 } else if (!strcmp (squal, "PR")) {
315 info->cond.qualifier = QUALIFIER_PARTIAL;
316 } else if (!strcmp (squal, "TS")) {
317 info->cond.qualifier = QUALIFIER_THUNDERSTORM;
318 } else if (!strcmp (squal, "BL")) {
319 info->cond.qualifier = QUALIFIER_BLOWING;
320 } else if (!strcmp (squal, "SH")) {
321 info->cond.qualifier = QUALIFIER_SHOWERS;
322 } else if (!strcmp (squal, "DR")) {
323 info->cond.qualifier = QUALIFIER_DRIFTING;
324 } else if (!strcmp (squal, "FZ")) {
325 info->cond.qualifier = QUALIFIER_FREEZING;
326 } else {
327 return;
328 }
329
330 if (!strcmp (sphen, "DZ")) {
331 info->cond.phenomenon = PHENOMENON_DRIZZLE;
332 } else if (!strcmp (sphen, "RA")) {
333 info->cond.phenomenon = PHENOMENON_RAIN;
334 } else if (!strcmp (sphen, "SN")) {
335 info->cond.phenomenon = PHENOMENON_SNOW;
336 } else if (!strcmp (sphen, "SG")) {
337 info->cond.phenomenon = PHENOMENON_SNOW_GRAINS;
338 } else if (!strcmp (sphen, "IC")) {
339 info->cond.phenomenon = PHENOMENON_ICE_CRYSTALS;
340 } else if (!strcmp (sphen, "PE")) {
341 info->cond.phenomenon = PHENOMENON_ICE_PELLETS;
342 } else if (!strcmp (sphen, "GR")) {
343 info->cond.phenomenon = PHENOMENON_HAIL;
344 } else if (!strcmp (sphen, "GS")) {
345 info->cond.phenomenon = PHENOMENON_SMALL_HAIL;
346 } else if (!strcmp (sphen, "UP")) {
347 info->cond.phenomenon = PHENOMENON_UNKNOWN_PRECIPITATION;
348 } else if (!strcmp (sphen, "BR")) {
349 info->cond.phenomenon = PHENOMENON_MIST;
350 } else if (!strcmp (sphen, "FG")) {
351 info->cond.phenomenon = PHENOMENON_FOG;
352 } else if (!strcmp (sphen, "FU")) {
353 info->cond.phenomenon = PHENOMENON_SMOKE;
354 } else if (!strcmp (sphen, "VA")) {
355 info->cond.phenomenon = PHENOMENON_VOLCANIC_ASH;
356 } else if (!strcmp (sphen, "SA")) {
357 info->cond.phenomenon = PHENOMENON_SAND;
358 } else if (!strcmp (sphen, "HZ")) {
359 info->cond.phenomenon = PHENOMENON_HAZE;
360 } else if (!strcmp (sphen, "PY")) {
361 info->cond.phenomenon = PHENOMENON_SPRAY;
362 } else if (!strcmp (sphen, "DU")) {
363 info->cond.phenomenon = PHENOMENON_DUST;
364 } else if (!strcmp (sphen, "SQ")) {
365 info->cond.phenomenon = PHENOMENON_SQUALL;
366 } else if (!strcmp (sphen, "SS")) {
367 info->cond.phenomenon = PHENOMENON_SANDSTORM;
368 } else if (!strcmp (sphen, "DS")) {
369 info->cond.phenomenon = PHENOMENON_DUSTSTORM;
370 } else if (!strcmp (sphen, "PO")) {
371 info->cond.phenomenon = PHENOMENON_DUST_WHIRLS;
372 } else if (!strcmp (sphen, "+FC")) {
373 info->cond.phenomenon = PHENOMENON_TORNADO;
374 } else if (!strcmp (sphen, "FC")) {
375 info->cond.phenomenon = PHENOMENON_FUNNEL_CLOUD;
376 } else {
377 return;
378 }
379
380 if ((info->cond.qualifier != QUALIFIER_NONE) || (info->cond.phenomenon != PHENOMENON_NONE))
381 info->cond.significant = TRUE(!(0));
382}
383
384#define TIME_RE_STR"([0-9]{6})Z" "([0-9]{6})Z"
385#define WIND_RE_STR"(([0-9]{3})|VRB)([0-9]?[0-9]{2})(G[0-9]?[0-9]{2})?(KT|MPS)" "(([0-9]{3})|VRB)([0-9]?[0-9]{2})(G[0-9]?[0-9]{2})?(KT|MPS)"
386#define VIS_RE_STR"((([0-9]?[0-9])|(M?([12] )?([1357]/1?[0-9])))SM)|" "([0-9]{4}(N|NE|E|SE|S|SW|W|NW( [0-9]{4}(N|NE|E|SE|S|SW|W|NW))?)?)|"
"CAVOK"
"((([0-9]?[0-9])|(M?([12] )?([1357]/1?[0-9])))SM)|" \
387 "([0-9]{4}(N|NE|E|SE|S|SW|W|NW( [0-9]{4}(N|NE|E|SE|S|SW|W|NW))?)?)|" \
388 "CAVOK"
389#define COND_RE_STR"(-|\\+)?(VC|MI|BC|PR|TS|BL|SH|DR|FZ)?(DZ|RA|SN|SG|IC|PE|GR|GS|UP|BR|FG|FU|VA|SA|HZ|PY|DU|SQ|SS|DS|PO|\\+?FC)" "(-|\\+)?(VC|MI|BC|PR|TS|BL|SH|DR|FZ)?(DZ|RA|SN|SG|IC|PE|GR|GS|UP|BR|FG|FU|VA|SA|HZ|PY|DU|SQ|SS|DS|PO|\\+?FC)"
390#define CLOUD_RE_STR"((CLR|BKN|SCT|FEW|OVC|SKC|NSC)([0-9]{3}|///)?(CB|TCU|///)?)" "((CLR|BKN|SCT|FEW|OVC|SKC|NSC)([0-9]{3}|///)?(CB|TCU|///)?)"
391#define TEMP_RE_STR"(M?[0-9][0-9])/(M?(//|[0-9][0-9])?)" "(M?[0-9][0-9])/(M?(//|[0-9][0-9])?)"
392#define PRES_RE_STR"(A|Q)([0-9]{4})" "(A|Q)([0-9]{4})"
393
394/* POSIX regular expressions do not allow us to express "match whole words
395 * only" in a simple way, so we have to wrap them all into
396 * (^| )(...regex...)( |$)
397 */
398#define RE_PREFIX"(^| )(" "(^| )("
399#define RE_SUFFIX")( |$)" ")( |$)"
400
401static regex_t metar_re[RE_NUM];
402static void (*metar_f[RE_NUM]) (gchar *tokp, WeatherInfo *info);
403
404static void
405metar_init_re (void)
406{
407 static gboolean initialized = FALSE(0);
408 if (initialized)
409 return;
410 initialized = TRUE(!(0));
411
412 regcomp (&metar_re[TIME_RE], RE_PREFIX"(^| )(" TIME_RE_STR"([0-9]{6})Z" RE_SUFFIX")( |$)", REG_EXTENDED1);
413 regcomp (&metar_re[WIND_RE], RE_PREFIX"(^| )(" WIND_RE_STR"(([0-9]{3})|VRB)([0-9]?[0-9]{2})(G[0-9]?[0-9]{2})?(KT|MPS)" RE_SUFFIX")( |$)", REG_EXTENDED1);
414 regcomp (&metar_re[VIS_RE], RE_PREFIX"(^| )(" VIS_RE_STR"((([0-9]?[0-9])|(M?([12] )?([1357]/1?[0-9])))SM)|" "([0-9]{4}(N|NE|E|SE|S|SW|W|NW( [0-9]{4}(N|NE|E|SE|S|SW|W|NW))?)?)|"
"CAVOK"
RE_SUFFIX")( |$)", REG_EXTENDED1);
415 regcomp (&metar_re[COND_RE], RE_PREFIX"(^| )(" COND_RE_STR"(-|\\+)?(VC|MI|BC|PR|TS|BL|SH|DR|FZ)?(DZ|RA|SN|SG|IC|PE|GR|GS|UP|BR|FG|FU|VA|SA|HZ|PY|DU|SQ|SS|DS|PO|\\+?FC)" RE_SUFFIX")( |$)", REG_EXTENDED1);
416 regcomp (&metar_re[CLOUD_RE], RE_PREFIX"(^| )(" CLOUD_RE_STR"((CLR|BKN|SCT|FEW|OVC|SKC|NSC)([0-9]{3}|///)?(CB|TCU|///)?)" RE_SUFFIX")( |$)", REG_EXTENDED1);
417 regcomp (&metar_re[TEMP_RE], RE_PREFIX"(^| )(" TEMP_RE_STR"(M?[0-9][0-9])/(M?(//|[0-9][0-9])?)" RE_SUFFIX")( |$)", REG_EXTENDED1);
418 regcomp (&metar_re[PRES_RE], RE_PREFIX"(^| )(" PRES_RE_STR"(A|Q)([0-9]{4})" RE_SUFFIX")( |$)", REG_EXTENDED1);
419
420 metar_f[TIME_RE] = metar_tok_time;
421 metar_f[WIND_RE] = metar_tok_wind;
422 metar_f[VIS_RE] = metar_tok_vis;
423 metar_f[COND_RE] = metar_tok_cond;
424 metar_f[CLOUD_RE] = metar_tok_cloud;
425 metar_f[TEMP_RE] = metar_tok_temp;
426 metar_f[PRES_RE] = metar_tok_pres;
427}
428
429gboolean
430metar_parse (gchar *metar, WeatherInfo *info)
431{
432 gchar *p;
433 //gchar *rmk;
434 gint i, i2;
435 regmatch_t rm, rm2;
436 gchar *tokp;
437
438 g_return_val_if_fail (info != NULL, FALSE)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("CafeWeather", ((const char*) (__func__)), "info != NULL");
return ((0)); } } while (0)
;
439 g_return_val_if_fail (metar != NULL, FALSE)do { if ((metar != ((void*)0))) { } else { g_return_if_fail_warning
("CafeWeather", ((const char*) (__func__)), "metar != NULL")
; return ((0)); } } while (0)
;
440
441 metar_init_re ();
442
443 /*
444 * Force parsing to end at "RMK" field. This prevents a subtle
445 * problem when info within the remark happens to match an earlier state
446 * and, as a result, throws off all the remaining expression
447 */
448 if (0 != (p = strstr (metar, " RMK "))) {
449 *p = '\0';
450 //rmk = p + 5; // uncomment this if RMK data becomes useful
451 }
452
453 p = metar;
454 i = TIME_RE;
455 while (*p) {
456
457 i2 = RE_NUM;
458 rm2.rm_so = strlen (p);
459 rm2.rm_eo = rm2.rm_so;
460
461 for (i = 0; i < RE_NUM && rm2.rm_so > 0; i++) {
462 if (0 == regexec (&metar_re[i], p, 1, &rm, 0)
463 && rm.rm_so < rm2.rm_so)
464 {
465 i2 = i;
466 /* Skip leading and trailing space characters, if present.
467 (the regular expressions include those characters to
468 only get matches limited to whole words). */
469 if (p[rm.rm_so] == ' ') rm.rm_so++;
470 if (p[rm.rm_eo - 1] == ' ') rm.rm_eo--;
471 rm2.rm_so = rm.rm_so;
472 rm2.rm_eo = rm.rm_eo;
473 }
474 }
475
476 if (i2 != RE_NUM) {
477 tokp = g_strndup (p + rm2.rm_so, rm2.rm_eo - rm2.rm_so);
478 metar_f[i2] (tokp, info);
479 g_free (tokp);
480 }
481
482 p += rm2.rm_eo;
483 p += strspn (p, " ");
484 }
485 return TRUE(!(0));
486}
487
488static void
489metar_finish (GObject *object, GAsyncResult *result, gpointer data)
490{
491 SoupSession *session = SOUP_SESSION (object);
492 SoupMessage *msg = soup_session_get_async_result_message (session, result);
493 WeatherInfo *info = (WeatherInfo *)data;
494 WeatherLocation *loc;
495 const gchar *p, *endtag;
496 gchar *searchkey, *metar;
497 gboolean success = FALSE(0);
498 GBytes *response_body = NULL((void*)0);
499 const gchar *msgdata;
500
501 g_return_if_fail (info != NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("CafeWeather", ((const char*) (__func__)), "info != NULL");
return; } } while (0)
;
502
503 response_body = soup_session_send_and_read_finish (session, result, NULL((void*)0));
504
505 if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg))((soup_message_get_status (msg)) >= 200 && (soup_message_get_status
(msg)) < 300)
) {
506 /* Translators: %d is an error code, and %s the error string */
507 g_warning (_("Failed to get METAR data: %d %s.\n")(cafeweather_gettext ("Failed to get METAR data: %d %s.\n")),
508 soup_message_get_status (msg), soup_message_get_reason_phrase (msg));
509
510 request_done (info, FALSE(0));
511 g_bytes_unref (response_body);
512 return;
513 }
514
515 loc = info->location;
516
517 msgdata = g_bytes_get_data (response_body, NULL((void*)0));
518
519 searchkey = g_strdup_printf ("<raw_text>%s", loc->code);
520 p = strstr (msgdata, searchkey);
521 g_free (searchkey);
522
523 if (p) {
524 p += WEATHER_LOCATION_CODE_LEN4 + 11;
525 endtag = strstr (p, "</raw_text>");
526 if (endtag)
527 metar = g_strndup (p, endtag - p);
528 else
529 metar = g_strdup (p)g_strdup_inline (p);
530 success = metar_parse (metar, info);
531 g_free (metar);
532 } else if (!strstr (msgdata, "aviationweather.gov")) {
533 /* The response doesn't even seem to have come from NOAA...
534 * most likely it is a wifi hotspot login page. Call that a
535 * network error.
536 */
537 info->network_error = TRUE(!(0));
538 }
539
540 info->valid = success;
541 request_done (info, TRUE(!(0)));
542 g_bytes_unref (response_body);
543}
544
545/* Read current conditions and fill in info structure */
546void
547metar_start_open (WeatherInfo *info)
548{
549 WeatherLocation *loc;
550 SoupMessage *msg;
551 gchar *query;
552
553 g_return_if_fail (info != NULL)do { if ((info != ((void*)0))) { } else { g_return_if_fail_warning
("CafeWeather", ((const char*) (__func__)), "info != NULL");
return; } } while (0)
;
554 info->valid = info->network_error = FALSE(0);
555 loc = info->location;
556 if (loc == NULL((void*)0)) {
557 g_warning (_("WeatherInfo missing location")(cafeweather_gettext ("WeatherInfo missing location")));
558 return;
559 }
560
561 query = soup_form_encode (
562 "dataSource", "metars",
563 "requestType", "retrieve",
564 "format", "xml",
565 "hoursBeforeNow", "3",
566 "mostRecent", "true",
567 "fields", "raw_text",
568 "stationString", loc->code,
569 NULL((void*)0));
570
571 msg = soup_message_new_from_encoded_form (SOUP_METHOD_GET(((const char *)((__extension__ ({ _Static_assert (sizeof *(&
(_SOUP_METHOD_GET)) == sizeof (gpointer), "Expression evaluates to false"
); __typeof__ (*(&(_SOUP_METHOD_GET))) gapg_temp_newval; __typeof__
((&(_SOUP_METHOD_GET))) gapg_temp_atomic = (&(_SOUP_METHOD_GET
)); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5
); gapg_temp_newval; })) ? (_SOUP_METHOD_GET) : ((__extension__
({ _Static_assert (sizeof *(&(_SOUP_METHOD_GET)) == sizeof
(gpointer), "Expression evaluates to false"); __typeof__ ((&
(_SOUP_METHOD_GET))) gaps_temp_atomic = (&(_SOUP_METHOD_GET
)); __typeof__ (*(&(_SOUP_METHOD_GET))) gaps_temp_newval =
((gpointer)g_intern_static_string ("GET")); (void) (0 ? (gpointer
) * (&(_SOUP_METHOD_GET)) : ((void*)0)); __atomic_store (
gaps_temp_atomic, &gaps_temp_newval, 5); })), (_SOUP_METHOD_GET
)))))
, "https://www.aviationweather.gov/cgi-bin/data/dataserver.php", query);
572
573 soup_session_send_and_read_async (info->session, msg, G_PRIORITY_DEFAULT0, NULL((void*)0), metar_finish, info);
574
575 info->requests_pending++;
576
577 g_free (query);
578}