Move vi modelines closer to the beginning, so they're more likely to be actually...
[monky] / src / weather.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Please see COPYING for details
7  *
8  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
9  *      (see AUTHORS)
10  * All rights reserved.
11  *
12  * This program is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation, either version 3 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  *
24  */
25
26 #include "conky.h"
27 #include "logging.h"
28 #include "weather.h"
29 #include "temphelper.h"
30 #include "ccurl_thread.h"
31 #include <time.h>
32 #include <ctype.h>
33 #ifdef MATH
34 #include <math.h>
35 #endif /* MATH */
36 #ifdef XOAP
37 #include <libxml/parser.h>
38 #include <libxml/xpath.h>
39
40 /* Xpath expressions for XOAP xml parsing */
41 #define NUM_XPATH_EXPRESSIONS_CC 8
42 const char *xpath_expression_cc[NUM_XPATH_EXPRESSIONS_CC] = {
43         "/weather/cc/lsup", "/weather/cc/tmp", "/weather/cc/t",
44         "/weather/cc/bar/r", "/weather/cc/wind/s", "/weather/cc/wind/d",
45         "/weather/cc/hmid", "/weather/cc/icon"
46 };
47
48 #define NUM_XPATH_EXPRESSIONS_DF 10
49 const char *xpath_expression_df[NUM_XPATH_EXPRESSIONS_DF] = {
50         "/weather/dayf/day[*]/hi", "/weather/dayf/day[*]/low",
51         "/weather/dayf/day[*]/part[1]/icon", "/weather/dayf/day[*]/part[1]/t",
52         "/weather/dayf/day[*]/part[1]/wind/s","/weather/dayf/day[*]/part[1]/wind/d",
53         "/weather/dayf/day[*]/part[1]/ppcp", "/weather/dayf/day[*]/part[1]/hmid",
54         "/weather/dayf/day[*]/@t", "/weather/dayf/day[*]/@dt"
55 };
56 #endif /* XOAP */
57
58 /* Possible sky conditions */
59 #define NUM_CC_CODES 6
60 const char *CC_CODES[NUM_CC_CODES] = {
61         "SKC", "CLR", "FEW", "SCT", "BKN", "OVC"
62 };
63
64 /* Possible weather modifiers */
65 #define NUM_WM_CODES 9
66 const char *WM_CODES[NUM_WM_CODES] = {
67         "VC", "MI", "BC", "PR", "TS", "BL",
68         "SH", "DR", "FZ"
69 };
70
71 /* Possible weather conditions */
72 #define NUM_WC_CODES 17
73 const char *WC_CODES[NUM_WC_CODES] = {
74         "DZ", "RA", "GR", "GS", "SN", "SG",
75         "FG", "HZ", "FU", "BR", "DU", "SA",
76         "FC", "PO", "SQ", "SS", "DS"
77 };
78
79 static ccurl_location_t *locations_head_cc = 0;
80 #ifdef XOAP
81 static ccurl_location_t *locations_head_df = 0;
82 #endif
83
84 void weather_free_info(void)
85 {
86         ccurl_free_locations(&locations_head_cc);
87 #ifdef XOAP
88         ccurl_free_locations(&locations_head_df);
89 #endif
90 }
91
92 int rel_humidity(int dew_point, int air) {
93         const float a = 17.27f;
94         const float b = 237.7f;
95
96         float diff = a*(dew_point/(b+dew_point)-air/(b+air));
97 #ifdef MATH
98         return (int)(100.f*expf(diff));
99 #else
100         return (int)(16.666667163372f*(6.f+diff*(6.f+diff*(3.f+diff))));
101 #endif /* MATH */
102 }
103
104 #ifdef XOAP
105 static void parse_df(PWEATHER_FORECAST *res, xmlXPathContextPtr xpathCtx)
106 {
107         int i, j, k;
108         char *content;
109         xmlXPathObjectPtr xpathObj;
110
111         xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
112         if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
113                         xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
114                 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
115                 NORM_ERR("XOAP error: %s", content);
116                 xmlFree(content);
117                 xmlXPathFreeObject(xpathObj);
118                 return;
119         }
120         xmlXPathFreeObject(xpathObj);
121
122         for (i = 0; i < NUM_XPATH_EXPRESSIONS_DF; i++) {
123                 xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_df[i], xpathCtx);
124                 if (xpathObj != NULL) {
125                         xmlNodeSetPtr nodes = xpathObj->nodesetval;
126                         k = 0;
127                         for (j = 0; j < nodes->nodeNr; ++j) {
128                                 if (nodes->nodeTab[j]->type == XML_ELEMENT_NODE) {
129                                         content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
130                                         switch(i) {
131                                         case 0:
132                                                 res->hi[k] = atoi(content);
133                                                 break;
134                                         case 1:
135                                                 res->low[k] = atoi(content);
136                                                 break;
137                                         case 2:
138                                                 strncpy(res->icon[k], content, 2);
139                                         case 3:
140                                                 strncpy(res->xoap_t[k], content, 31);
141                                                 break;
142                                         case 4:
143                                                 res->wind_s[k] = atoi(content);
144                                                 break;
145                                         case 5:
146                                                 res->wind_d[k] = atoi(content);
147                                                 break;
148                                         case 6:
149                                                 res->ppcp[k] = atoi(content);
150                                                 break;
151                                         case 7:
152                                                 res->hmid[k] = atoi(content);
153                                         }
154                                 } else if (nodes->nodeTab[j]->type == XML_ATTRIBUTE_NODE) {
155                                         content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
156                                         switch(i) {
157                                         case 8:
158                                                 strncpy(res->day[k], content, 8);
159                                                 break;
160                                         case 9:
161                                                 strncpy(res->date[k], content, 6);
162                                         }
163                                 }
164                                 xmlFree(content);
165                                 if (++k == FORECAST_DAYS) break;
166                         }
167                 }
168                 xmlXPathFreeObject(xpathObj);
169         }
170         return;
171 }
172
173 static void parse_weather_forecast_xml(PWEATHER_FORECAST *res, const char *data)
174 {
175         xmlDocPtr doc;
176         xmlXPathContextPtr xpathCtx;
177
178         if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
179                 NORM_ERR("weather_forecast: can't read xml data");
180                 return;
181         }
182
183         xpathCtx = xmlXPathNewContext(doc);
184         if(xpathCtx == NULL) {
185                 NORM_ERR("weather_forecast: unable to create new XPath context");
186                 xmlFreeDoc(doc);
187                 return;
188         }
189
190         parse_df(res, xpathCtx);
191         xmlXPathFreeContext(xpathCtx);
192         xmlFreeDoc(doc);
193         return;
194 }
195
196 static void parse_cc(PWEATHER *res, xmlXPathContextPtr xpathCtx)
197 {
198         int i;
199         char *content;
200         xmlXPathObjectPtr xpathObj;
201
202         xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
203         if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
204                         xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
205                 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
206                 NORM_ERR("XOAP error: %s", content);
207                 xmlFree(content);
208                 xmlXPathFreeObject(xpathObj);
209                 return;
210         }
211         xmlXPathFreeObject(xpathObj);
212
213         for (i = 0; i < NUM_XPATH_EXPRESSIONS_CC; i++) {
214                 xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_cc[i], xpathCtx);
215                 if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr >0 &&
216                                 xpathObj->nodesetval->nodeTab[0]->type ==
217                                 XML_ELEMENT_NODE) {
218                         content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
219                         switch(i) {
220                                 case 0:
221                                         strncpy(res->lastupd, content, 31);
222                                         break;
223                                 case 1:
224                                         res->temp = atoi(content);
225                                         break;
226                                 case 2:
227                                         strncpy(res->xoap_t, content, 31);
228                                         break;
229                                 case 3:
230                                         res->bar = atoi(content);
231                                         break;
232                                 case 4:
233                                         res->wind_s = atoi(content);
234                                         break;
235                                 case 5:
236                                         res->wind_d = atoi(content);
237                                         break;
238                                 case 6:
239                                         res->hmid = atoi(content);
240                                         break;
241                                 case 7:
242                                         strncpy(res->icon, content, 2);
243                         }
244                         xmlFree(content);
245                 }
246                 xmlXPathFreeObject(xpathObj);
247         }
248         return;
249 }
250
251 static void parse_weather_xml(PWEATHER *res, const char *data)
252 {
253         xmlDocPtr doc;
254         xmlXPathContextPtr xpathCtx;
255
256         if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
257                 NORM_ERR("weather: can't read xml data");
258                 return;
259         }
260
261         xpathCtx = xmlXPathNewContext(doc);
262         if(xpathCtx == NULL) {
263                 NORM_ERR("weather: unable to create new XPath context");
264                 xmlFreeDoc(doc);
265                 return;
266         }
267
268         parse_cc(res, xpathCtx);
269         xmlXPathFreeContext(xpathCtx);
270         xmlFreeDoc(doc);
271         return;
272 }
273 #endif /* XOAP */
274
275 /*
276  * Horrible hack to avoid using regexes
277  *
278  */
279
280 static inline void parse_token(PWEATHER *res, char *token) {
281
282         int i;
283         char s_tmp[64];
284
285         switch (strlen(token)) {
286
287                 //Check all tokens 2 chars long
288                 case 2:
289
290                         //Check if token is a weather condition
291                         for (i=0; i<2; i++) {
292                                 if (!isalpha(token[i])) break;
293                         }
294                         if (i==2) {
295                                 for(i=0; i<NUM_WC_CODES; i++) {
296                                         if (!strncmp(token, WC_CODES[i], 2)) {
297                                                 res->wc=i+1;
298                                                 break;
299                                         }
300                                 }
301                                 return;
302                         }
303
304                         //Check for CB
305                         if (!strcmp(token, "CB")) {
306                                 res->cc = 8;
307                                 return;
308                         }
309
310                         break;
311
312                         //Check all tokens 3 chars long
313                 case 3:
314
315                         //Check if token is a modified weather condition
316                         if ((token[0] == '+') || (token[0] == '-')) {
317                                 for (i=1; i<3; i++) {
318                                         if (!isalpha(token[i])) break;
319                                 }
320                                 if (i==3) {
321                                         for(i=0; i<NUM_WC_CODES; i++) {
322                                                 if (!strncmp(&token[1], WC_CODES[i], 2)) {
323                                                         res->wc=i+1;
324                                                         break;
325                                                 }
326                                         }
327                                         return;
328                                 }
329                         }
330
331                         //Check for NCD or NSC
332                         if ((!strcmp(token, "NCD")) || (!strcmp(token, "NSC"))) {
333                                 res->cc = 1;
334                                 return;
335                         }
336
337                         //Check for TCU
338                         if (!strcmp(token, "TCU")) {
339                                 res->cc = 7;
340                                 return;
341                         }
342
343                         break;
344
345                         //Check all tokens 4 chars long
346                 case 4:
347
348                         //Check if token is a modified weather condition
349                         for(i=0; i<NUM_WM_CODES; i++) {
350                                 if (!strncmp(token, WM_CODES[i], 2)) {
351                                         for(i=0; i<NUM_WC_CODES; i++) {
352                                                 if (!strncmp(&token[2], WC_CODES[i], 2)) {
353                                                         res->wc=i+1;
354                                                         return;
355                                                 }
356                                         }
357                                         break;
358                                 }
359                         }
360
361                         break;
362
363                         //Check all tokens 5 chars long
364                 case 5:
365
366                         //Check for CAVOK
367                         if (!strcmp(token, "CAVOK")) {
368                                 res->cc = 1;
369                                 return;
370                         }
371
372                         //Check if token is the temperature
373                         for (i=0; i<2; i++) {
374                                 if (!isdigit(token[i])) break;
375                         }
376                         if ((i==2) && (token[2] == '/')) {
377                                 for (i=3; i<5; i++) {
378                                         if (!isdigit(token[i])) break;
379                                 }
380                                 if (i==5) {
381                                         //First 2 digits gives the air temperature
382                                         res->temp=atoi(token);
383
384                                         //4th and 5th digits gives the dew point temperature
385                                         res->dew=atoi(&token[3]);
386
387                                         //Compute humidity
388                                         res->hmid = rel_humidity(res->dew, res->temp);
389
390                                         return;
391                                 }
392                         }
393
394                         //Check if token is the pressure
395                         if ((token[0] == 'Q') || (token[0] == 'A')) {
396                                 for (i=1; i<5; i++) {
397                                         if (!isdigit(token[i])) break;
398                                 }
399                                 if (i==5) {
400                                         if (token[0] == 'A') {
401                                                 //Convert inches of mercury to mbar
402                                                 res->bar = (int)(atoi(&token[1])*0.338637526f);
403                                                 return;
404                                         }
405
406                                         //Last 4 digits is pressure im mbar
407                                         res->bar = atoi(&token[1]);
408                                         return;
409                                 }
410                         }
411
412                         //Check if token is a modified weather condition
413                         if ((token[0] == '+') || (token[0] == '-')) {
414                                 for(i=0; i<NUM_WM_CODES; i++) {
415                                         if (!strncmp(&token[1], WM_CODES[i], 2)) {
416                                                 for(i=0; i<NUM_WC_CODES; i++) {
417                                                         if (!strncmp(&token[3], WC_CODES[i], 2)) {
418                                                                 res->wc=i+1;
419                                                                 return;
420                                                         }
421                                                 }
422                                                 break;
423                                         }
424                                 }
425                         }
426                         break;
427
428                         //Check all tokens 6 chars long
429                 case 6:
430
431                         //Check if token is the cloud cover
432                         for (i=0; i<3; i++) {
433                                 if (!isalpha(token[i])) break;
434                         }
435                         if (i==3) {
436                                 for (i=3; i<6; i++) {
437                                         if (!isdigit(token[i])) break;
438                                 }
439                                 if (i==6) {
440                                         //Check if first 3 digits gives the cloud cover condition
441                                         for(i=0; i<NUM_CC_CODES; i++) {
442                                                 if (!strncmp(token, CC_CODES[i], 3)) {
443                                                         res->cc=i+1;
444                                                         break;
445                                                 }
446                                         }
447                                         return;
448                                 }
449                         }
450
451                         //Check if token is positive temp and negative dew
452                         for (i=0; i<2; i++) {
453                                 if (!isdigit(token[i])) break;
454                         }
455                         if ((i==2) && (token[2] == '/')  && (token[3] == 'M')) {
456                                 for (i=4; i<6; i++) {
457                                         if (!isdigit(token[i])) break;
458                                 }
459                                 if (i==6) {
460                                         //1st and 2nd digits gives the temperature
461                                         res->temp = atoi(token);
462
463                                         //5th and 6th digits gives the dew point temperature
464                                         res->dew = -atoi(&token[4]);
465
466                                         //Compute humidity
467                                         res->hmid = rel_humidity(res->dew, res->temp);
468
469                                         return;
470                                 }
471                         }
472
473                         break;
474
475                         //Check all tokens 7 chars long
476                 case 7:
477
478                         //Check if token is the observation time
479                         for (i=0; i<6; i++) {
480                                 if (!isdigit(token[i])) break;
481                         }
482                         if ((i==6) && (token[6] == 'Z')) return;
483
484                         //Check if token is the wind speed/direction in knots
485                         for (i=0; i<5; i++) {
486                                 if (!isdigit(token[i])) break;
487                         }
488                         if ((i==5) && (token[5] == 'K') &&  (token[6] == 'T')) {
489
490                                 //First 3 digits are wind direction
491                                 strncpy(s_tmp, token, 3);
492                                 s_tmp[3]='\0';
493                                 res->wind_d=atoi(s_tmp);
494
495                                 //4th and 5th digit are wind speed in knots (convert to km/hr)
496                                 res->wind_s = (int)(atoi(&token[3])*1.852);
497
498                                 return;
499                         }
500
501                         //Check if token is negative temperature
502                         if ((token[0] == 'M') && (token[4] == 'M')) {
503                                 for (i=1; i<3; i++) {
504                                         if (!isdigit(token[i])) break;
505                                 }
506                                 if ((i==3) && (token[3] == '/')) {
507                                         for (i=5; i<7; i++) {
508                                                 if (!isdigit(token[i])) break;
509                                         }
510                                         if (i==7) {
511                                                 //2nd and 3rd digits gives the temperature
512                                                 res->temp = -atoi(&token[1]);
513
514                                                 //6th and 7th digits gives the dew point temperature
515                                                 res->dew = -atoi(&token[5]);
516
517                                                 //Compute humidity
518                                                 res->hmid = rel_humidity(res->dew, res->temp);
519
520                                                 return;
521                                         }
522                                 }
523                         }
524
525                         //Check if token is wind variability
526                         for (i=0; i<3; i++) {
527                                 if (!isdigit(token[i])) break;
528                         }
529                         if ((i==3) && (token[3] == 'V')) {
530                                 for (i=4; i<7; i++) {
531                                         if (!isdigit(token[i])) break;
532                                 }
533                                 if (i==7) return;
534                         }
535
536                         break;
537
538                         //Check all tokens 8 chars long
539                 case 8:
540
541                         //Check if token is the wind speed/direction in m/s
542                         for (i=0; i<5; i++) {
543                                 if (!isdigit(token[i])) break;
544                         }
545                         if ((i==5)&&(token[5] == 'M')&&(token[6] == 'P')&&(token[7] == 'S')) {
546
547                                 //First 3 digits are wind direction
548                                 strncpy(s_tmp, token, 3);
549                                 s_tmp[3]='\0';
550                                 res->wind_d=atoi(s_tmp);
551
552                                 //4th and 5th digit are wind speed in m/s (convert to km/hr)
553                                 res->wind_s = (int)(atoi(&token[3])*3.6);
554
555                                 return;
556                         }
557
558                 default:
559
560                         //printf("token : %s\n", token);
561                         break;
562         }
563 }
564
565 #ifdef XOAP
566 void parse_weather_forecast(void *result, const char *data)
567 {
568         PWEATHER_FORECAST *res = (PWEATHER_FORECAST*)result;
569         /* Reset results */
570         memset(res, 0, sizeof(PWEATHER_FORECAST));
571
572         //Check if it is an xml file
573         if ( strncmp(data, "<?xml ", 6) == 0 ) {
574                 parse_weather_forecast_xml(res, data);
575         }
576 }
577 #endif /* XOAP */
578
579 void parse_weather(void *result, const char *data)
580 {
581         PWEATHER *res = (PWEATHER*)result;
582         /* Reset results */
583         memset(res, 0, sizeof(PWEATHER));
584
585 #ifdef XOAP
586         //Check if it is an xml file
587         if ( strncmp(data, "<?xml ", 6) == 0 ) {
588                 parse_weather_xml(res, data);
589         } else
590 #endif /* XOAP */
591         {
592                 //We assume its a text file
593                 char s_tmp[256];
594                 const char delim[] = " ";
595
596                 //Divide time stamp and metar data
597                 if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
598
599                         //Process all tokens
600                         char *p_tok = NULL;
601                         char *p_save = NULL;
602
603                         if ((strtok_r(s_tmp, delim, &p_save)) != NULL) {
604
605                                 //Jump first token, must be icao
606                                 p_tok = strtok_r(NULL, delim, &p_save);
607
608                                 do {
609
610                                         parse_token(res, p_tok);
611                                         p_tok = strtok_r(NULL, delim, &p_save);
612
613                                 } while (p_tok != NULL);
614                         }
615                         return;
616                 }
617                 else {
618                         return;
619                 }
620         }
621 }
622
623 void wind_deg_to_dir(char *p, int p_max_size, int wind_deg) {
624         if ((wind_deg >= 349) || (wind_deg < 12)) {
625                 strncpy(p, "N", p_max_size);
626         } else if (wind_deg < 33) {
627                 strncpy(p, "NNE", p_max_size);
628         } else if (wind_deg < 57) {
629                 strncpy(p, "NE", p_max_size);
630         } else if (wind_deg < 79) {
631                 strncpy(p, "ENE", p_max_size);
632         } else if (wind_deg < 102) {
633                 strncpy(p, "E", p_max_size);
634         } else if (wind_deg < 124) {
635                 strncpy(p, "ESE", p_max_size);
636         } else if (wind_deg < 147) {
637                 strncpy(p, "SE", p_max_size);
638         } else if (wind_deg < 169) {
639                 strncpy(p, "SSE", p_max_size);
640         } else if (wind_deg < 192) {
641                 strncpy(p, "S", p_max_size);
642         } else if (wind_deg < 214) {
643                 strncpy(p, "SSW", p_max_size);
644         } else if (wind_deg < 237) {
645                 strncpy(p, "SW", p_max_size);
646         } else if (wind_deg < 259) {
647                 strncpy(p, "WSW", p_max_size);
648         } else if (wind_deg < 282) {
649                         strncpy(p, "W", p_max_size);
650         } else if (wind_deg < 304) {
651                 strncpy(p, "WNW", p_max_size);
652         } else if (wind_deg < 327) {
653                 strncpy(p, "NW", p_max_size);
654         } else if (wind_deg < 349) {
655                 strncpy(p, "NNW", p_max_size);
656         };
657 }
658
659 #ifdef XOAP
660 void weather_forecast_process_info(char *p, int p_max_size, char *uri, unsigned int day, char *data_type, int interval)
661 {
662         PWEATHER_FORECAST *data;
663
664         ccurl_location_t *curloc = ccurl_find_location(&locations_head_df, uri);
665         if (!curloc->p_timed_thread) {
666                 curloc->result = malloc(sizeof(PWEATHER_FORECAST));
667                 memset(curloc->result, 0, sizeof(PWEATHER_FORECAST));
668                 curloc->process_function = &parse_weather_forecast;
669                 ccurl_init_thread(curloc, interval);
670                 if (!curloc->p_timed_thread) {
671                         NORM_ERR("error setting up weather_forecast thread");
672                 }
673         }
674
675         timed_thread_lock(curloc->p_timed_thread);
676         data = (PWEATHER_FORECAST*)curloc->result;
677         if (strcmp(data_type, "hi") == EQUAL) {
678                 temp_print(p, p_max_size, data->hi[day], TEMP_CELSIUS);
679         } else if (strcmp(data_type, "low") == EQUAL) {
680                 temp_print(p, p_max_size, data->low[day], TEMP_CELSIUS);
681         } else if (strcmp(data_type, "icon") == EQUAL) {
682                 strncpy(p, data->icon[day], p_max_size);
683         } else if (strcmp(data_type, "forecast") == EQUAL) {
684                 strncpy(p, data->xoap_t[day], p_max_size);
685         } else if (strcmp(data_type, "wind_speed") == EQUAL) {
686                 snprintf(p, p_max_size, "%d", data->wind_s[day]);
687         } else if (strcmp(data_type, "wind_dir") == EQUAL) {
688                 wind_deg_to_dir(p, p_max_size, data->wind_d[day]);
689         } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
690                 snprintf(p, p_max_size, "%d", data->wind_d[day]);
691         } else if (strcmp(data_type, "humidity") == EQUAL) {
692                 snprintf(p, p_max_size, "%d", data->hmid[day]);
693         } else if (strcmp(data_type, "precipitation") == EQUAL) {
694                 snprintf(p, p_max_size, "%d", data->ppcp[day]);
695         } else if (strcmp(data_type, "day") == EQUAL) {
696                 strncpy(p, data->day[day], p_max_size);
697         } else if (strcmp(data_type, "date") == EQUAL) {
698                 strncpy(p, data->date[day], p_max_size);
699         }
700
701         timed_thread_unlock(curloc->p_timed_thread);
702 }
703 #endif /* XOAP */
704
705 void weather_process_info(char *p, int p_max_size, char *uri, char *data_type, int interval)
706 {
707         static const char *wc[] = {
708                 "", "drizzle", "rain", "hail", "soft hail",
709                 "snow", "snow grains", "fog", "haze", "smoke",
710                 "mist", "dust", "sand", "funnel cloud tornado",
711                 "dust/sand", "squall", "sand storm", "dust storm"
712         };
713         PWEATHER *data;
714
715         ccurl_location_t *curloc = ccurl_find_location(&locations_head_cc, uri);
716         if (!curloc->p_timed_thread) {
717                 curloc->result = malloc(sizeof(PWEATHER));
718                 memset(curloc->result, 0, sizeof(PWEATHER));
719                 curloc->process_function = &parse_weather;
720                 ccurl_init_thread(curloc, interval);
721                 if (!curloc->p_timed_thread) {
722                         NORM_ERR("error setting up weather thread");
723                 }
724         }
725
726         timed_thread_lock(curloc->p_timed_thread);
727         data = (PWEATHER*)curloc->result;
728         if (strcmp(data_type, "last_update") == EQUAL) {
729                 strncpy(p, data->lastupd, p_max_size);
730         } else if (strcmp(data_type, "temperature") == EQUAL) {
731                 temp_print(p, p_max_size, data->temp, TEMP_CELSIUS);
732         } else if (strcmp(data_type, "cloud_cover") == EQUAL) {
733 #ifdef XOAP
734                 if (data->xoap_t[0] != '\0') {
735                         char *s = p;
736                         strncpy(p, data->xoap_t, p_max_size);
737                         while (*s) {
738                                 *s = tolower(*s);
739                                 s++;
740                         }
741                 } else
742 #endif /* XOAP */
743                         if (data->cc == 0) {
744                                 strncpy(p, "", p_max_size);
745                         } else if (data->cc < 3) {
746                                 strncpy(p, "clear", p_max_size);
747                         } else if (data->cc < 5) {
748                                 strncpy(p, "partly cloudy", p_max_size);
749                         } else if (data->cc == 5) {
750                                 strncpy(p, "cloudy", p_max_size);
751                         } else if (data->cc == 6) {
752                                 strncpy(p, "overcast", p_max_size);
753                         } else if (data->cc == 7) {
754                                 strncpy(p, "towering cumulus", p_max_size);
755                         } else  {
756                                 strncpy(p, "cumulonimbus", p_max_size);
757                         }
758 #ifdef XOAP
759         } else if (strcmp(data_type, "icon") == EQUAL) {
760                 strncpy(p, data->icon, p_max_size);
761 #endif /* XOAP */
762         } else if (strcmp(data_type, "pressure") == EQUAL) {
763                 snprintf(p, p_max_size, "%d", data->bar);
764         } else if (strcmp(data_type, "wind_speed") == EQUAL) {
765                 snprintf(p, p_max_size, "%d", data->wind_s);
766         } else if (strcmp(data_type, "wind_dir") == EQUAL) {
767                 wind_deg_to_dir(p, p_max_size, data->wind_d);
768         } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
769                 snprintf(p, p_max_size, "%d", data->wind_d);
770
771         } else if (strcmp(data_type, "humidity") == EQUAL) {
772                 snprintf(p, p_max_size, "%d", data->hmid);
773         } else if (strcmp(data_type, "weather") == EQUAL) {
774                 strncpy(p, wc[data->wc], p_max_size);
775         }
776
777         timed_thread_unlock(curloc->p_timed_thread);
778 }
779
780 #ifdef XOAP
781
782 /* xoap suffix for weather from weather.com */
783 static char *xoap_cc = NULL;
784 static char *xoap_df = NULL;
785
786 /*
787  * TODO: make the xoap keys file readable from the config file
788  *       make the keys directly readable from the config file
789  *       make the xoap keys file giveable as a command line option
790  */
791 void load_xoap_keys(void)
792 {
793         FILE *fp;
794         char *par  = (char *) malloc(11 * sizeof(char));
795         char *key  = (char *) malloc(17 * sizeof(char));
796         char *xoap = (char *) malloc(64 * sizeof(char));
797
798         to_real_path(xoap, XOAP_FILE);
799         fp = fopen(xoap, "r");
800         if (fp != NULL) {
801                 if (fscanf(fp, "%10s %16s", par, key) == 2) {
802                         xoap_cc = (char *) malloc(128 * sizeof(char));
803                         xoap_df = (char *) malloc(128 * sizeof(char));
804
805                         strcpy(xoap_cc, "?cc=*&link=xoap&prod=xoap&par=");
806                         strcat(xoap_cc, par);
807                         strcat(xoap_cc, "&key=");
808                         strcat(xoap_cc, key);
809                         strcat(xoap_cc, "&unit=m");
810
811                         /* TODO: Use FORECAST_DAYS instead of 5 */
812                         strcpy(xoap_df, "?dayf=5&link=xoap&prod=xoap&par=");
813                         strcat(xoap_df, par);
814                         strcat(xoap_df, "&key=");
815                         strcat(xoap_df, key);
816                         strcat(xoap_df, "&unit=m");
817                 }
818                 fclose(fp);
819         }
820         free(par);
821         free(key);
822         free(xoap);
823 }
824 #endif /* XOAP */
825
826 int process_weather_uri(char *uri, char *locID, int dayf UNUSED_ATTR)
827 {
828         /* locID MUST BE upper-case */
829         char *tmp_p = locID;
830
831         while (*tmp_p) {
832                 *tmp_p = toupper(*tmp_p);
833                 tmp_p++;
834         }
835
836         /* Construct complete uri */
837 #ifdef XOAP
838         if (strstr(uri, "xoap.weather.com")) {
839                 if ((dayf == 0) && (xoap_cc != NULL)) {
840                         strcat(uri, locID);
841                         strcat(uri, xoap_cc);
842                 } else if ((dayf == 1) && (xoap_df != NULL)) {
843                         strcat(uri, locID);
844                         strcat(uri, xoap_df);
845                 } else {
846                         free(uri);
847                         uri = NULL;
848                 }
849         } else
850 #endif /* XOAP */
851         if (strstr(uri, "weather.noaa.gov")) {
852                 strcat(uri, locID);
853                 strcat(uri, ".TXT");
854         } else  if (!strstr(uri, "localhost") && !strstr(uri, "127.0.0.1")) {
855                 return -1;
856         }
857         return 0;
858 }