1 #include "config.h"
2 
3 /*  Copyright (C) 2002  Brad Jorsch <anomie@users.sourceforge.net>
4 
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.
9 
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.
14 
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 #include <stdio.h>
21 #include <stdlib.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 <limits.h>
38 #include <math.h>
39 #include <pcre.h>
40 
41 #include "forecast.h"
42 #include "convert.h"
43 #include "getLine.h"
44 #include "die.h"
45 
46 /* Important variables */
47 static struct forecast **forecasts=NULL;
48 static int num_forecasts=0;
49 static pcre *date=NULL;
50 static int ovecsize=1;
51 static int changed=0;
52 
53 /* functions */
54 
find_next_time(char * file,char * pat,int minutes)55 time_t find_next_time(char *file, char *pat, int minutes){
56     FILE *fp;
57     char *s;
58     time_t t, mintime;
59 
60     mintime=time(NULL)/60+15;
61     if((fp=fopen(file, "r"))==NULL) return mintime;
62 
63     s=NULL;
64     while(!feof(fp)){
65         getLine(&s, fp);
66         if(strstr(s, pat)!=NULL) break;
67         free(s);
68         s=NULL;
69     }
70     fclose(fp);
71     if(s==NULL) return mintime;
72     t=parse_time_string(s)/60+minutes;
73     free(s);
74     return (t>mintime)?t:mintime;
75 }
76 
parse_time_string(char * s)77 time_t parse_time_string(char *s){
78     struct tm tm;
79     int ovector[ovecsize];
80     int ovalue;
81     char *e;
82     int i;
83 
84     if(date==NULL){
85         date=pcre_compile("\\b(\\d+)/(\\d+)/(\\d+)\\s+(\\d\\d)(\\d\\d)\\s*UTC\\b", 0, (const char **)&e, &i, NULL);
86         if(date==NULL){
87             warn("find_next PCRE error: %s at %i", e, i);
88             return -1;
89         }
90         pcre_fullinfo(date, NULL, PCRE_INFO_CAPTURECOUNT, &ovecsize);
91         ovecsize=(ovecsize+1)*3;
92         return parse_time_string(s);
93     }
94 
95     ovalue=pcre_exec(date, NULL, s, strlen(s), 0, 0, ovector, ovecsize);
96     if(ovalue<=0) return -1;
97 
98     if(pcre_get_substring(s, ovector, ovalue, 1, (const char **)&e)<0) return 0;
99     tm.tm_mon=atoi(e)-1;
100     pcre_free_substring(e);
101     if(pcre_get_substring(s, ovector, ovalue, 2, (const char **)&e)<0) return 0;
102     tm.tm_mday=atoi(e);
103     pcre_free_substring(e);
104     if(pcre_get_substring(s, ovector, ovalue, 3, (const char **)&e)<0) return 0;
105     tm.tm_year=atoi(e)-1900;
106     pcre_free_substring(e);
107     if(pcre_get_substring(s, ovector, ovalue, 4, (const char **)&e)<0) return 0;
108     tm.tm_hour=atoi(e);
109     pcre_free_substring(e);
110     if(pcre_get_substring(s, ovector, ovalue, 5, (const char **)&e)<0) return 0;
111     tm.tm_min=atoi(e);
112     pcre_free_substring(e);
113     tm.tm_sec=0;
114 
115     return mkgmtime(&tm);
116 }
117 
add_forecast(struct forecast * f,char * ID,char * station)118 void add_forecast(struct forecast *f, char *ID, char *station){
119     if((forecasts=realloc(forecasts, ++num_forecasts*sizeof(*forecasts)))==NULL)
120         die("realloc in add_forecast");
121 
122     if(ID==NULL){
123         memset(f->ID, '\0', 4);
124     } else {
125         strncpy(f->ID, ID, 3);
126         f->ID[3]='\0';
127     }
128     f->station=station;
129     forecasts[num_forecasts-1]=f;
130     changed=1;
131 }
132 
reset_forecast(struct forecast * f)133 void reset_forecast(struct forecast *f){
134     f->last_update=time(NULL);
135     f->month=0;
136     f->day=-1;
137     f->year=SHRT_MIN;
138     f->wday=-1;
139     f->hour=-1;
140     f->low=999;
141     f->high=999;
142     f->temp=999;
143     f->dewpt=999;
144     f->rh=-1;
145     f->winddir=-1;
146     f->windspeed=-1;
147     f->heatindex=999;
148     f->windchill=999;
149     f->precipamt=-1;
150     f->snowamt=-1;
151     f->sky=-1;
152     f->vis=7;
153     f->obs=0;
154     f->pcp_total=0;
155     f->frz=0;
156     f->snow=0;
157     f->rain=0;
158     f->tstorm=0;
159     f->svtstorm=0;
160     f->moon=NAN;
161     f->time=-1;
162     changed=1;
163 }
164 
is_forecast_valid(const struct forecast * a)165 static int is_forecast_valid(const struct forecast *a){
166     return (a->ID[0]!='\0' &&
167             a->month>0 && a->month<=12 &&
168             a->day>0 && a->day<=31 &&
169             a->year!=SHRT_MIN);
170 }
171 
is_forecast_current(struct forecast * f,time_t now)172 static int is_forecast_current(struct forecast *f, time_t now){
173     time_t t;
174 
175     t=forecast_time(f);
176     t+=(f->hour<0)?86399:3599;
177     return t>now;
178 }
179 
compar(const void * aa,const void * bb)180 static int compar(const void *aa, const void *bb){
181     struct forecast *a=*(struct forecast **)aa;
182     struct forecast *b=*(struct forecast **)bb;
183     int i, j;
184 
185     /* First, any undefined forecast is greater than any defined forecast */
186     i=is_forecast_valid(a);
187     j=is_forecast_valid(b);
188     if(!i && !j) return 0; /* all undef forecasts are equal */
189     if(!i) return 1;
190     if(!j) return -1;
191 
192     /* Any whole-day forecast is greater than any partial forecast */
193     if(a->hour<0 && b->hour>=0) return 1;
194     if(a->hour>=0 && b->hour<0) return -1;
195 
196     /* Ok, compare dates now */
197     if(a->year>b->year) return 1;
198     if(a->year<b->year) return -1;
199     if(a->month>b->month) return 1;
200     if(a->month<b->month) return -1;
201     if(a->day>b->day) return 1;
202     if(a->day<b->day) return -1;
203     if(a->hour>b->hour) return 1;
204     if(a->hour<b->hour) return -1;
205 
206     /* Last resort, sort in alphabetical order by ID */
207     return strcasecmp(a->ID, b->ID);
208 }
209 
sort_forecasts(void)210 static void sort_forecasts(void){
211     if(forecasts==NULL) return;
212     qsort(forecasts, num_forecasts, sizeof(struct forecast *), compar);
213     changed=0;
214 }
215 
forecast_time(struct forecast * f)216 time_t forecast_time(struct forecast *f){
217     struct tm tm;
218 
219     if(f->time!=-1) return f->time;
220     tm.tm_year=f->year;
221     tm.tm_mon=f->month-1;
222     tm.tm_mday=f->day;
223     tm.tm_hour=(f->hour<0)?0:f->hour;
224     tm.tm_min=tm.tm_sec=0;
225     return (f->time=mktime(&tm));
226 }
227 
228 static char current_ID[4]={ '\0', '\0', '\0', '\0' };
229 static int current_index=-1;
230 static struct forecast *current=NULL;
231 static time_t current_time=0;
232 static int current_hour=0;
233 
set_current(int i)234 static void set_current(int i){
235     current_index=i;
236     if(i<0 || i>num_forecasts){
237         current=NULL;
238         memset(current_ID, 0, 4);
239         current_time=0;
240         current_hour=0;
241     } else {
242         current=forecasts[i];
243         memcpy(current_ID, current->ID, 4);
244         current_time=forecast_time(current);
245         current_hour=current->hour;
246     }
247 }
248 
locate_current(void)249 static void locate_current(void){
250     int i;
251     time_t now, target;
252     int target_hour;
253     long curdiff=0;
254     long tmpdiff;
255     char target_ID[4];
256 
257     now=time(NULL);
258     if(!changed && current!=NULL && is_forecast_current(current, now)) return;
259 
260     sort_forecasts();
261     target=current_time;
262     target_hour=current_hour;
263     memcpy(target_ID, current_ID, 4);
264     set_current(-1);
265 
266     for(i=0; i<num_forecasts; i++){
267         if(!is_forecast_valid(forecasts[i])) continue;
268         if(!is_forecast_current(forecasts[i], now)) continue;
269         tmpdiff=abs(forecast_time(forecasts[i])-target);
270         if((target_hour<0 && forecasts[i]->hour>=0) ||
271            (target_hour>=0 && forecasts[i]->hour<0))
272             tmpdiff+=31556926;
273         if(memcmp(forecasts[i]->ID, target_ID, 4)) tmpdiff++;
274         if(current==NULL || tmpdiff<curdiff){
275             set_current(i);
276             curdiff=tmpdiff;
277         }
278     }
279 }
280 
current_forecast_get(void)281 struct forecast *current_forecast_get(void){
282     locate_current();
283     return current;
284 }
285 
mod(int i,int n)286 static inline int mod(int i, int n){
287     i=i%n; if(i<0) i+=n;
288     return i;
289 }
290 
current_forecast_next(int dir)291 void current_forecast_next(int dir){
292     int i;
293     time_t now;
294 
295     if(num_forecasts==0) return;
296 
297     locate_current();
298     now=time(NULL);
299 
300     if(current_index<0 || current_index>num_forecasts) current_index=0;
301     for(i=mod(current_index+dir, num_forecasts); ; i=mod(i+dir, num_forecasts)){
302         if(is_forecast_valid(forecasts[i]) && is_forecast_current(forecasts[i], now)){
303             set_current(i);
304             return;
305         }
306         if(i==current_index){
307             set_current(-1);
308             return;
309         }
310     }
311 }
312