wmpager: EWMH support
[dockapps.git] / wmweather+ / avn.c
blobc8b1d800e17cc595a1f856fd30edccbd85938152
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 <stdlib.h>
22 #include <time.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <sys/stat.h>
28 #include "wmweather+.h"
29 #include "forecast.h"
30 #include "getLine.h"
31 #include "convert.h"
32 #include "download.h"
33 #include "diff.h"
34 #include "die.h"
35 #include "sunzenith.h"
36 #include "moon.h"
37 #include "subst.h"
39 /* Important variables */
40 #define AVN_MAX 21
41 static time_t avn_time=0;
42 static char *avn_file=NULL;
43 static char *avn_newfile=NULL;
44 static char *avn_req[2]={ NULL, NULL };
45 static struct forecast forecasts[AVN_MAX];
48 /********* init functions ************/
49 static int parse_avn(char *file);
51 static void reset_avn(void){
52 int i;
54 for(i=0; i<AVN_MAX; i++) reset_forecast(&forecasts[i]);
57 void init_avn(void){
58 char *e;
59 int i;
60 struct subst_val subs[]={
61 { 's', STRING, &avn_station },
62 { 0, 0, 0 }
65 strncpy(bigbuf, avn_station, BIGBUF_LEN-14);
66 bigbuf[BIGBUF_LEN-14]='\0';
67 for(e=bigbuf; *e!='\0'; e++);
68 strcpy(e, ".avn.txt");
69 avn_file=get_pid_filename(bigbuf);
70 strcpy(e, ".new-avn.txt");
71 avn_newfile=get_pid_filename(bigbuf);
73 if((avn_req[0]=subst(avn_uri, subs))==NULL) die("init_avn");
74 if(avn_post!=NULL && (avn_req[1]=subst(avn_post, subs))==NULL) die("init_avn");
75 avn_time=0;
77 /* Remove stale file */
78 unlink(avn_file);
79 unlink(avn_newfile);
80 reset_avn();
81 for(i=0; i<AVN_MAX; i++) add_forecast(&forecasts[i], "AVN", avn_station);
85 /********* download functions ************/
87 static void avn_callback(char *filename, void *v){
88 struct stat statbuf;
90 if(stat(avn_newfile, &statbuf)>=0){
91 if(S_ISREG(statbuf.st_mode) && statbuf.st_size!=0
92 && diff(avn_newfile, avn_file) && parse_avn(avn_newfile)){
93 avn_time=find_next_time(avn_newfile, "MOS GUIDANCE", 720);
94 rename(avn_newfile, avn_file);
95 } else {
96 unlink(avn_newfile);
97 if(!parse_avn(avn_file)) reset_avn();
102 void avn_cleanup(void){
103 if(avn_file==NULL) return;
104 unlink(avn_newfile);
105 unlink(avn_file);
108 void update_avn(int force){
109 time_t t;
111 if(avn_file==NULL) return;
113 t=time(NULL)/60;
114 if(!force && avn_time>t) return;
116 avn_time=find_next_time(avn_file, "MOS GUIDANCE", 15);
117 download_file(avn_newfile, avn_req[0], avn_req[1], force?DOWNLOAD_KILL_OTHER_REQUESTS:0, avn_callback, NULL);
121 /********* parse functions ************/
123 #define NEXT(s) free(s); \
124 len=getLine(&s, fp); \
125 if(strstr(s, "</PRE>")!=NULL) len=0;
127 #define DIE() return (free(s), fclose(fp), 0)
128 #define SPLIT(s) { \
129 ID[0]=s[0]; \
130 ID[1]=s[1]; \
131 ID[2]=s[2]; \
132 ID[3]='\0'; \
133 for(n=0, c=s+4; c<s+len && n<AVN_MAX; n++, c+=3){ \
134 split[n][0]=c[0]; \
135 split[n][1]=c[1]; \
136 split[n][2]=c[2]; \
137 split[n][3]='\0'; \
140 #define ASSIGN(field) \
141 for(n=0; n<AVN_MAX; n++) forecasts[n].field=atoi(split[n]);
143 #define ASSIGN2(field, inval) \
144 for(n=0; n<AVN_MAX; n++){ \
145 i=atoi(split[n]); \
146 if(i!=inval) forecasts[n].field=i; \
149 static int parse_avn(char *file){
150 FILE *fp;
151 char *s, *c;
152 int len;
153 int mon, day;
154 int h, i=0, j, k, m, n, x, y, z;
155 char ID[4];
156 char split[AVN_MAX][4];
158 reset_avn();
159 if((fp=fopen(file, "r"))==NULL) return 0;
161 /* Look for something like an AVN coded forecast */
162 c=NULL;
163 while(!feof(fp)){
164 len=getLine(&s, fp);
165 if((c=strstr(s, "MOS GUIDANCE"))!=NULL) break;
166 free(s);
168 if(c==NULL) return (fclose(fp), 0);
169 c=strchr(c, '/');
170 if(c==NULL || !isdigit(*(c-1)) || !isdigit(*(c+1))) DIE();
171 m=atoi(c-2);
172 c=strchr(c+1, '/');
173 if(c==NULL || !isdigit(*(c-1)) || !isdigit(*(c+1))) DIE();
174 y=atoi(c+1)-1900;
176 NEXT(s);
177 if(len<10) DIE();
178 if(strncmp(s, "DT ", 3)) DIE();
179 mon=13;
180 c=s;
181 i=4;
182 while(mon>12){
183 c=strchr(c+1, '/');
184 if(c==NULL) DIE();
185 for(mon=1; mon<=12; mon++){
186 if(!strncmp(c+1, monthnames[mon], 3) && isspace(*(c+4))) break;
187 if(!strncmp(c+1, monthnames2[mon], 4) && isspace(*(c+5))){
188 i=5;
189 break;
193 day=atoi(c+i);
194 if(day<1) DIE();
195 if(c>s+4) day--;
196 if(mon<m) y++;
198 NEXT(s);
199 if(len<10) DIE();
200 if(strncmp(s, "HR ", 3)) DIE();
201 x=day;
202 m=mon;
203 SPLIT(s);
204 for(n=0; n<AVN_MAX; n++){
205 i=atoi(split[n]);
206 if(i==0){
207 x++;
208 fix_date(&mon, &x, &y, NULL);
210 m=mon;
211 j=x;
212 z=y;
213 h=utc2local(i*100, &m, &j, &z, &k)/100;
214 forecasts[n].month=m;
215 forecasts[n].day=j;
216 forecasts[n].year=z;
217 forecasts[n].hour=h;
218 forecasts[n].wday=k;
219 if(latitude!=999 && calcSolarZenith(latitude, longitude, y, mon, x, i*60)>90)
220 forecasts[n].moon=calc_moon(m, j, z, h*100);
223 while(1){
224 NEXT(s);
225 if(len<=10) break;
226 SPLIT(s);
228 if(!strcmp(ID, "X/N")) j=1;
229 else if(!strcmp(ID, "N/X")) j=2;
230 else j=0;
231 if(j!=0){
232 for(n=0; n<AVN_MAX; n++){
233 if(!isdigit(split[n][2])) continue;
234 i=atoi(split[n]);
235 k=day+(j>>1);
236 for(m=0; m<AVN_MAX; m++){
237 if((j&1)==1 &&
238 ((forecasts[m].day==k-1 && forecasts[m].hour>=19)
239 || (forecasts[m].day==k && forecasts[m].hour<19)))
240 forecasts[m].high=i;
241 if((j&1)==0 &&
242 ((forecasts[m].day==k-1 && forecasts[m].hour>=8)
243 || (forecasts[m].day==k && forecasts[m].hour<8)))
244 forecasts[m].low=i;
246 j++;
248 continue;
250 if(!strcmp(ID, "TMP")){
251 ASSIGN(temp);
252 continue;
254 if(!strcmp(ID, "DPT")){
255 ASSIGN(dewpt);
256 continue;
258 if(!strcmp(ID, "WDR")){
259 for(n=0; n<AVN_MAX; n++){
260 i=atoi(split[n]);
261 if(i==99) forecasts[n].winddir=0;
262 else forecasts[n].winddir=((int)((i+1.125)/2.25))%16+1;
264 continue;
266 if(!strcmp(ID, "WSP")){
267 ASSIGN2(windspeed, 99);
268 continue;
270 if(!strcmp(ID, "P06")){
271 for(m=0; m<AVN_MAX; m++){
272 if(!isdigit(split[m][2])) continue;
273 i=atoi(split[m]);
274 if(i!=999){
275 forecasts[m].pcp_total=i;
276 /* AVN_MAX-2 because the last 2
277 * are already 6-hour intervals */
278 if(m>0 && m<AVN_MAX-2) forecasts[m-1].pcp_total=i;
281 continue;
283 if(!strcmp(ID, "T06")){
284 for(m=1; m<AVN_MAX; m+=2){
285 if(!isdigit(split[m][2])) continue;
286 i=atoi(split[m]); if(i==999) i=0;
287 j=atoi(split[m+1]+1); if(j==99) j=0;
288 j=i*j/100;
289 forecasts[m].tstorm=forecasts[m+1].tstorm=i;
290 forecasts[m].svtstorm=forecasts[m+1].svtstorm=j;
292 continue;
294 if(!strcmp(ID, "Q06")){
295 for(m=0; m<AVN_MAX; m++){
296 if(!isdigit(split[m][2])) continue;
297 i=atoi(split[m]);
298 if(i!=999){
299 forecasts[m].precipamt=i;
300 /* AVN_MAX-2 because the last 2
301 * are already 6-hour intervals */
302 if(m>0 && m<AVN_MAX-2) forecasts[m-1].precipamt=i;
305 continue;
307 if(!strcmp(ID, "SNW")){
308 for(m=0; m<AVN_MAX; m++){
309 if(!isdigit(split[m][2])) continue;
310 i=atoi(split[m]);
311 if(i!=9){
312 j=forecasts[m].hour;
313 k=forecasts[m].day;
314 for(n=m; n>=0; n--){
315 if(forecasts[n].day<k && forecasts[n].hour<j) break;
316 forecasts[n].snowamt=i;
320 continue;
322 if(!strcmp(ID, "CLD")){
323 for(m=0; m<AVN_MAX; m++){
324 if(split[m][1]=='C') forecasts[m].sky=0;
325 if(split[m][1]=='F') forecasts[m].sky=1;
326 if(split[m][1]=='S') forecasts[m].sky=2;
327 if(split[m][1]=='B') forecasts[m].sky=3;
328 if(split[m][1]=='O') forecasts[m].sky=4;
330 continue;
332 if(!strcmp(ID, "VIS")){
333 ASSIGN2(vis, 9);
334 continue;
336 if(!strcmp(ID, "OBV")){
337 for(m=0; m<AVN_MAX; m++){
338 if(split[m][2]=='N') forecasts[m].obs=0;
339 if(split[m][2]=='R' || split[m][2]=='G') forecasts[m].obs=1;
340 if(split[m][2]=='Z') forecasts[m].obs=2;
341 if(split[m][2]=='L') forecasts[m].obs=3;
343 continue;
345 if(!strcmp(ID, "POZ")){
346 ASSIGN2(frz, 999);
347 continue;
349 if(!strcmp(ID, "POS")){
350 ASSIGN2(snow, 999);
351 continue;
354 free(s);
355 fclose(fp);
357 for(m=0; m<AVN_MAX; m++){
358 forecasts[m].rh=rh_F(forecasts[m].temp, forecasts[m].dewpt);
359 forecasts[m].heatindex=heatindex_F(forecasts[m].temp, forecasts[m].rh);
360 forecasts[m].windchill=windchill_F(forecasts[m].temp, forecasts[m].windspeed);
361 forecasts[m].rain=93-forecasts[m].frz-forecasts[m].snow;
362 forecasts[m].rain=forecasts[m].rain*forecasts[m].pcp_total/93;
363 forecasts[m].snow=forecasts[m].snow*forecasts[m].pcp_total/93;
364 forecasts[m].frz=forecasts[m].frz*forecasts[m].pcp_total/93;
367 return 1;
369 #undef NEXT
370 #undef DIE
371 #undef SPLIT
372 #undef ASSIGN
373 #undef ASSIGN2