File: | weather-metar.c |
Warning: | line 177, column 28 This statement is never executed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | |
32 | enum { |
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 */ |
45 | static time_t |
46 | make_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 | |
75 | static void |
76 | metar_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 | |
84 | static void |
85 | metar_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 | |
149 | static void |
150 | metar_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")) { |
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"))) { |
163 | // US observation: field ends with "SM" |
164 | pfrac = strchr (tokp, '/'); |
165 | if (pfrac) { |
166 | if (*tokp == 'M') { |
167 | info->visibility = 0.001; |
168 | } else { |
169 | num = (*(pfrac - 1) - '0'); |
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'; |
This statement is never executed | |
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 | |
196 | static void |
197 | metar_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 | |
225 | static void |
226 | metar_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 | |
253 | static void |
254 | metar_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 | |
273 | static void |
274 | metar_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 | |
401 | static regex_t metar_re[RE_NUM]; |
402 | static void (*metar_f[RE_NUM]) (gchar *tokp, WeatherInfo *info); |
403 | |
404 | static void |
405 | metar_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 | |
429 | gboolean |
430 | metar_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 | |
488 | static void |
489 | metar_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 */ |
546 | void |
547 | metar_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 | } |