File: | weather-metar.c |
Warning: | line 169, column 24 Out of bound memory access (access exceeds upper limit of memory block) |
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'; | |||
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 | } |