Fix two bugs related to command execution:
[dockapps.git] / wmweather+ / metar.c
blobdadfa3a484e568f9e38f7c66e1cf9c0ad307df75
1 #include "config.h"
3 /* Copyright (C) 2002 Brad Jorsch <anomie@users.sourceforge.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <stdio.h>
21 #include <unistd.h>
22 #if TM_IN_SYS_TIME
23 # if TIME_WITH_SYS_TIME
24 # include <sys/time.h>
25 # include <time.h>
26 # else
27 # if HAVE_SYS_TIME_H
28 # include <sys/time.h>
29 # else
30 # include <time.h>
31 # endif
32 # endif
33 #else
34 #include <time.h>
35 #endif
36 #include <string.h>
37 #include <ctype.h>
38 #include <math.h>
39 #include <sys/stat.h>
40 #include <sys/wait.h>
42 #include <pcre.h>
44 #include "wmweather+.h"
45 #include "metar.h"
46 #include "warnings.h"
47 #include "download.h"
48 #include "convert.h"
49 #include "die.h"
50 #include "sunzenith.h"
51 #include "moon.h"
52 #include "subst.h"
54 /* Important variables */
55 static time_t metar_time=0;
57 static char *metar_newfile=NULL;
58 static char *metar_file=NULL;
59 static char *metar_req[2]={ NULL, NULL };
61 struct current_weather current;
63 /* Regular Expressions */
64 static pcre *station_time;
65 static pcre *wind;
66 static pcre *weather;
67 static pcre *vis[4];
68 static pcre *temp;
69 static pcre *pressure;
70 static int ovecsize;
72 /* prototypes */
73 static int parse_metar(char *file);
75 /* functions */
77 static void reset_current(struct current_weather *c){
78 c->last_update=time(NULL);
79 c->month=0;
80 c->date=-1;
81 c->time=-1;
82 c->temp=999;
83 c->rh=-1;
84 c->winddir=-1;
85 c->windspeed=-1;
86 c->pressure=-1;
87 c->heatindex=999;
88 c->windchill=999;
89 c->sky=-1;
90 c->vis=7;
91 c->obs=0;
92 c->frz=0;
93 c->snow=0;
94 c->rain=0;
95 c->tstorm=0;
96 c->moon=NAN;
99 #define compile(var, re) \
100 var=pcre_compile(re, 0, (const char **)&e, &i, NULL); \
101 if(var==NULL) die("init_metar PCRE error: %s at %i", e, i); \
102 pcre_fullinfo(var, NULL, PCRE_INFO_CAPTURECOUNT, &i); \
103 if(i>ovecsize) ovecsize=i;
105 void init_metar(void){
106 int i;
107 char *e;
108 struct subst_val subs[]={
109 { 's', STRING, &metar_station },
110 { 0, 0, 0 }
113 snprintf(bigbuf, BIGBUF_LEN, "%s.metar.txt", metar_station);
114 metar_file=get_pid_filename(bigbuf);
115 snprintf(bigbuf, BIGBUF_LEN, "%s.new-metar.txt", metar_station);
116 metar_newfile=get_pid_filename(bigbuf);
118 if((metar_req[0]=subst(metar_uri, subs))==NULL) die("init_metar");
119 if(metar_post!=NULL && (metar_req[1]=subst(metar_post, subs))==NULL) die("init_metar");
121 metar_time=0;
123 ovecsize=0;
125 strncpy(bigbuf, metar_station, BIGBUF_LEN-25);
126 bigbuf[BIGBUF_LEN-25]='\0';
127 strcat(bigbuf, " ((?:\\d\\d)?)(\\d\\d\\d\\d)Z( .* )");
128 compile(station_time, bigbuf);
129 compile(wind, " (VRB|\\d\\d\\d)(\\d\\d\\d?)(?:G\\d\\d\\d?)?(KT|MPS|KMH)((?: \\d\\d\\dV\\d\\d\\d)?) ");
130 compile(weather, " ((?:-|\\+|VC)?)((?:MI|PR|BC|DR|BL|SH|TS|FZ)?)((?:DZ|RA|SN|SG|IC|PE|PL|GR|GS|UP){0,3})((?:BR|FG|FU|VA|DU|SA|HZ|PY)?)((?:PO|SQ|FC|SS|DS)?)\\b");
131 compile(vis[0], " (\\d+)SM ");
132 compile(vis[1], " (\\d+)/(\\d+)SM ");
133 compile(vis[2], " (\\d+) (\\d+)/(\\d+)SM ");
134 compile(vis[3], " (\\d{4})[NS]?[EW]? ");
135 compile(temp, " (M?\\d\\d\\d?)/((?:M?\\d\\d\\d?)?) ");
136 compile(pressure, " ([AQ])(\\d\\d\\d\\d) ");
138 ovecsize=(ovecsize+1)*3;
140 /* Remove stale file */
141 unlink(metar_file);
142 unlink(metar_newfile);
143 reset_current(&current);
144 current.last_update = 0; // This was not a real "update", just an init
146 #undef compile
148 static void metar_callback(char *filename, void *v){
149 struct stat statbuf;
151 if(stat(metar_newfile, &statbuf)>=0){
152 if(S_ISREG(statbuf.st_mode) && statbuf.st_size!=0
153 && parse_metar(metar_newfile)){
154 rename(metar_newfile, metar_file);
155 } else {
156 unlink(metar_newfile);
157 if(!parse_metar(metar_file)) reset_current(&current);
161 update_warnings(v!=NULL);
164 void metar_cleanup(void){
165 unlink(metar_newfile);
166 unlink(metar_file);
169 void update_metar(int force){
170 time_t t;
172 t=time(NULL)/60;
173 if(!force && metar_time>t) return;
175 metar_time=t+15;
176 download_file(metar_newfile, metar_req[0], metar_req[1], force?DOWNLOAD_KILL_OTHER_REQUESTS:0, metar_callback, force?"":NULL);
180 #define get_substr(n, c) \
181 if(pcre_get_substring(s, ovector, ovalue, n, (const char **)&c)<0){ pcre_free_substring(s); return 0; }
183 static int parse_metar(char *file){
184 FILE *fp;
185 char *s, *c;
186 int ovector[ovecsize];
187 int ovalue;
188 int len;
189 float f;
190 int i, j;
192 reset_current(&current);
193 if((fp=fopen(file, "r"))==NULL) return 0;
194 len=fread(bigbuf, sizeof(char), BIGBUF_LEN-2, fp);
195 fclose(fp);
196 if(len<1) return 0;
197 for(i=0; i<len; i++){
198 if(isspace(bigbuf[i])) bigbuf[i]=' ';
200 c=strstr(bigbuf, " RMK");
201 if(c!=NULL) *(c+1)='\0';
202 c=strstr(bigbuf, " TEMPO");
203 s=strstr(bigbuf, " BECMG");
204 if(c!=NULL) *(c+1)='\0';
205 if(s!=NULL) *(s+1)='\0';
206 /* XXX: parse trend forecast data? */
208 len=strlen(bigbuf);
209 if(bigbuf[len-1]!=' '){
210 bigbuf[len++]=' ';
211 bigbuf[len]='\0';
214 /* Look for something like a METAR coded report */
215 ovalue=pcre_exec(station_time, NULL, bigbuf, len, 0, 0, ovector, ovecsize);
216 if(ovalue<=0) return 0;
217 if(pcre_get_substring(bigbuf, ovector, ovalue, 1, (const char **)&c)<0) return 0;
218 if(c[0]!='\0') current.date=atoi(c);
219 pcre_free_substring(c);
220 if(pcre_get_substring(bigbuf, ovector, ovalue, 2, (const char **)&c)<0) return 0;
221 current.time=atoi(c);
222 pcre_free_substring(c);
224 /* Chop off extraneous stuff */
225 if(pcre_get_substring(bigbuf, ovector, ovalue, 3, (const char **)&s)<0) return 0;
227 /* windspeed, winddir */
228 ovalue=pcre_exec(wind, NULL, s, len, 0, 0, ovector, ovecsize);
229 if(ovalue>0){
230 get_substr(4, c);
231 if(c[0]!='\0'){
232 current.winddir=0;
233 } else {
234 pcre_free_substring(c);
235 get_substr(1, c);
236 if(c[0]=='V') current.winddir=0;
237 else current.winddir=((int)((atoi(c)+11.25)/22.5))%16+1;
239 pcre_free_substring(c);
240 get_substr(2, c);
241 current.windspeed=atoi(c);
242 pcre_free_substring(c);
243 get_substr(3, c);
244 if(c[0]=='M'){ /* MPS */
245 current.windspeed=mps2knots(current.windspeed);
246 } else if(c[0]=='K' && c[1]=='M'){ /* KMH */
247 current.windspeed=kph2knots(current.windspeed);
251 /* vis */
252 f=99;
253 c=strstr(s, " M1/4SM ");
254 if(c!=NULL){
255 f=0;
256 goto wind_done;
258 ovalue=pcre_exec(vis[2], NULL, s, len, 0, 0, ovector, ovecsize);
259 if(ovalue>0){
260 get_substr(2, c);
261 i=atoi(c);
262 pcre_free_substring(c);
263 get_substr(3, c);
264 j=atoi(c);
265 pcre_free_substring(c);
266 get_substr(1, c);
267 f=atoi(c)+(float)i/j;
268 pcre_free_substring(c);
269 goto wind_done;
271 ovalue=pcre_exec(vis[1], NULL, s, len, 0, 0, ovector, ovecsize);
272 if(ovalue>0){
273 get_substr(2, c);
274 i=atoi(c);
275 pcre_free_substring(c);
276 get_substr(1, c);
277 f=(float)atoi(c)/i;
278 pcre_free_substring(c);
279 goto wind_done;
281 ovalue=pcre_exec(vis[0], NULL, s, len, 0, 0, ovector, ovecsize);
282 if(ovalue>0){
283 get_substr(1, c);
284 f=atoi(c);
285 pcre_free_substring(c);
286 goto wind_done;
288 c=strstr(s, " CAVOK ");
289 if(c!=NULL){
290 f=99;
291 current.sky=0;
292 goto wind_done;
294 ovalue=pcre_exec(vis[3], NULL, s, len, 0, 0, ovector, ovecsize);
295 if(ovalue>0){
296 get_substr(1, c);
297 f=m2mi(atoi(c));
298 pcre_free_substring(c);
299 goto wind_done;
301 wind_done:
302 if(f<=6) current.vis=6;
303 if(f<=5) current.vis=5;
304 if(f<3) current.vis=4;
305 if(f<1) current.vis=3;
306 if(f<=.5) current.vis=2;
307 if(f<=.25) current.vis=1;
309 /* temp, rh */
310 ovalue=pcre_exec(temp, NULL, s, len, 0, 0, ovector, ovecsize);
311 if(ovalue>0){
312 get_substr(1, c);
313 if(c[0]=='M') c[0]='-';
314 current.temp=atoi(c);
315 pcre_free_substring(c);
316 get_substr(2, c);
317 if(c[0]!='\0'){
318 if(c[0]=='M') c[0]='-';
319 current.rh=rh_C(current.temp, atoi(c));
321 pcre_free_substring(c);
324 /* pressure */
325 ovalue=pcre_exec(pressure, NULL, s, len, 0, 0, ovector, ovecsize);
326 if(ovalue>0){
327 get_substr(2, c);
328 i=atoi(c);
329 pcre_free_substring(c);
330 get_substr(1, c);
331 if(c[0]=='Q'){
332 current.pressure=hPa2inHg(i);
333 } else {
334 current.pressure=i/100.0;
336 pcre_free_substring(c);
339 /* sky */
340 if(strstr(s, " SKC")!=NULL || strstr(s, " CLR")!=NULL) current.sky=0;
341 if(strstr(s, " FEW")!=NULL) current.sky=1;
342 if(strstr(s, " SCT")!=NULL) current.sky=2;
343 if(strstr(s, " BKN")!=NULL) current.sky=3;
344 if(strstr(s, " OVC")!=NULL || strstr(s, " VV")!=NULL) current.sky=4;
346 /* obs, frz, snow, rain, tstorm */
347 /* There can be multiple weather chunks, so we while loop */
348 j=0;
349 while((ovalue=pcre_exec(weather, NULL, s, len, j, 0, ovector, ovecsize))>0){{
350 char *in, *de, *pp, *ob, *ot;
352 j=ovector[0]+1;
353 get_substr(0, c);
354 i=(c[1]=='\0');
355 pcre_free_substring(c);
356 if(i) continue;
359 get_substr(1, in);
360 get_substr(2, de);
361 get_substr(3, pp);
362 get_substr(4, ob);
363 get_substr(5, ot);
365 #define IN(haystack, needle) ((needle[0]=='\0')?0:strstr(haystack, needle))
366 if(current.obs<1 && strcmp(de, "FZ") && IN("BR|FG", ob))
367 current.obs=1;
368 if(current.obs<2 && IN("FU|VA|DU|SA|HZ|PY", ob))
369 current.obs=2;
370 if(current.obs<3 && IN("PO|SS|DS", ot))
371 current.obs=3;
372 if(current.obs<3 && IN("DR|BL", de)
373 && (strstr(pp, "SN") || IN("DU|SA|PY", ob)))
374 current.obs=3;
375 if(!strcmp(ot, "FC")){
376 current.sky=5;
377 current.obs=99;
378 current.vis=7;
380 #undef IN
382 i=66;
383 if(in[0]=='-' || in[0]=='V') i=33;
384 if(in[0]=='+') i=99;
385 if(!strcmp(de, "SH")) i=33;
386 if(current.frz<i
387 && ((!strcmp(de, "FZ") && (strstr(pp, "DZ") || strstr(pp, "RA")))
388 || strstr(pp, "IC") || strstr(pp, "PE") || strstr(pp, "PL")
389 || strstr(pp, "GR") || strstr(pp, "GS")))
390 current.frz=i;
391 if(current.snow<i && strcmp(de, "BL")
392 && (strstr(pp, "SN") || strstr(pp, "SG")))
393 current.snow=i;
394 if(current.rain<i && (strstr(pp, "UP")
395 || (strcmp(de, "FZ")
396 && (strstr(pp, "DZ") || strstr(pp, "RA")))))
397 current.rain=i;
398 if(current.tstorm<i && !strcmp(de, "TS"))
399 current.tstorm=i;
401 pcre_free_substring(in);
402 pcre_free_substring(de);
403 pcre_free_substring(pp);
404 pcre_free_substring(ob);
405 pcre_free_substring(ot);
407 if(current.obs==99) current.obs=0;
409 pcre_free_substring(s); /* Done parsing! Just a few final calculations... */
411 current.heatindex=heatindex_C(current.temp, current.rh);
412 current.windchill=windchill_C(current.temp, current.windspeed);
414 /* Figure out the proper month... */
416 int mon, day, year, time2; /* holds UTC */
417 int y; /* with current.*, holds local time */
418 time_t t=time(NULL);
419 struct tm *tm=gmtime(&t);
420 current.month=tm->tm_mon+1;
421 if(tm->tm_mday<current.date) current.month--;
422 if(current.month<1){ current.month+=12; tm->tm_year--; }
423 y=year=tm->tm_year;
424 mon=current.month;
425 day=current.date;
426 time2=current.time;
427 current.time=utc2local((int)current.time, &current.month, &current.date, &y, NULL);
429 if(latitude!=999 && calcSolarZenith(latitude, longitude, year, mon, day, hm2min(time2))>90)
430 current.moon=calc_moon(current.month, current.date, y, current.time);
432 return 1;
436 #undef get_substr