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