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
4 * Conky, a system monitor, based on torsmo
6 * Please see COPYING for details
8 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
10 * All rights reserved.
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.
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/>.
30 #include "temphelper.h"
31 #include "text_object.h"
32 #include "ccurl_thread.h"
39 #include <libxml/parser.h>
40 #include <libxml/xpath.h>
43 typedef struct PWEATHER_ {
60 #define FORECAST_DAYS 5
61 typedef struct PWEATHER_FORECAST_ {
62 int hi[FORECAST_DAYS];
63 int low[FORECAST_DAYS];
64 char icon[FORECAST_DAYS][3];
65 char xoap_t[FORECAST_DAYS][32];
66 char day[FORECAST_DAYS][9];
67 char date[FORECAST_DAYS][7];
68 int wind_s[FORECAST_DAYS];
69 int wind_d[FORECAST_DAYS];
70 int hmid[FORECAST_DAYS];
71 int ppcp[FORECAST_DAYS];
75 /* Xpath expressions for XOAP xml parsing */
76 #define NUM_XPATH_EXPRESSIONS_CC 8
77 const char *xpath_expression_cc[NUM_XPATH_EXPRESSIONS_CC] = {
78 "/weather/cc/lsup", "/weather/cc/tmp", "/weather/cc/t",
79 "/weather/cc/bar/r", "/weather/cc/wind/s", "/weather/cc/wind/d",
80 "/weather/cc/hmid", "/weather/cc/icon"
83 #define NUM_XPATH_EXPRESSIONS_DF 10
84 const char *xpath_expression_df[NUM_XPATH_EXPRESSIONS_DF] = {
85 "/weather/dayf/day[*]/hi", "/weather/dayf/day[*]/low",
86 "/weather/dayf/day[*]/part[1]/icon", "/weather/dayf/day[*]/part[1]/t",
87 "/weather/dayf/day[*]/part[1]/wind/s","/weather/dayf/day[*]/part[1]/wind/d",
88 "/weather/dayf/day[*]/part[1]/ppcp", "/weather/dayf/day[*]/part[1]/hmid",
89 "/weather/dayf/day[*]/@t", "/weather/dayf/day[*]/@dt"
93 /* Possible sky conditions */
94 #define NUM_CC_CODES 6
95 const char *CC_CODES[NUM_CC_CODES] = {
96 "SKC", "CLR", "FEW", "SCT", "BKN", "OVC"
99 /* Possible weather modifiers */
100 #define NUM_WM_CODES 9
101 const char *WM_CODES[NUM_WM_CODES] = {
102 "VC", "MI", "BC", "PR", "TS", "BL",
106 /* Possible weather conditions */
107 #define NUM_WC_CODES 17
108 const char *WC_CODES[NUM_WC_CODES] = {
109 "DZ", "RA", "GR", "GS", "SN", "SG",
110 "FG", "HZ", "FU", "BR", "DU", "SA",
111 "FC", "PO", "SQ", "SS", "DS"
114 static ccurl_location_t *locations_head_cc = 0;
116 static ccurl_location_t *locations_head_df = 0;
119 void weather_free_info(void)
121 ccurl_free_locations(&locations_head_cc);
123 ccurl_free_locations(&locations_head_df);
127 int rel_humidity(int dew_point, int air) {
128 const float a = 17.27f;
129 const float b = 237.7f;
131 float diff = a*(dew_point/(b+dew_point)-air/(b+air));
133 return (int)(100.f*expf(diff));
135 return (int)(16.666667163372f*(6.f+diff*(6.f+diff*(3.f+diff))));
140 static void parse_df(PWEATHER_FORECAST *res, xmlXPathContextPtr xpathCtx)
144 xmlXPathObjectPtr xpathObj;
146 xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
147 if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
148 xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
149 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
150 NORM_ERR("XOAP error: %s", content);
152 xmlXPathFreeObject(xpathObj);
155 xmlXPathFreeObject(xpathObj);
157 for (i = 0; i < NUM_XPATH_EXPRESSIONS_DF; i++) {
158 xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_df[i], xpathCtx);
159 if (xpathObj != NULL) {
160 xmlNodeSetPtr nodes = xpathObj->nodesetval;
162 for (j = 0; j < nodes->nodeNr; ++j) {
163 if (nodes->nodeTab[j]->type == XML_ELEMENT_NODE) {
164 content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
167 res->hi[k] = atoi(content);
170 res->low[k] = atoi(content);
173 strncpy(res->icon[k], content, 2);
175 strncpy(res->xoap_t[k], content, 31);
178 res->wind_s[k] = atoi(content);
181 res->wind_d[k] = atoi(content);
184 res->ppcp[k] = atoi(content);
187 res->hmid[k] = atoi(content);
189 } else if (nodes->nodeTab[j]->type == XML_ATTRIBUTE_NODE) {
190 content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
193 strncpy(res->day[k], content, 8);
196 strncpy(res->date[k], content, 6);
200 if (++k == FORECAST_DAYS) break;
203 xmlXPathFreeObject(xpathObj);
208 static void parse_weather_forecast_xml(PWEATHER_FORECAST *res, const char *data)
211 xmlXPathContextPtr xpathCtx;
213 if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
214 NORM_ERR("weather_forecast: can't read xml data");
218 xpathCtx = xmlXPathNewContext(doc);
219 if(xpathCtx == NULL) {
220 NORM_ERR("weather_forecast: unable to create new XPath context");
225 parse_df(res, xpathCtx);
226 xmlXPathFreeContext(xpathCtx);
231 static void parse_cc(PWEATHER *res, xmlXPathContextPtr xpathCtx)
235 xmlXPathObjectPtr xpathObj;
237 xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
238 if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
239 xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
240 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
241 NORM_ERR("XOAP error: %s", content);
243 xmlXPathFreeObject(xpathObj);
246 xmlXPathFreeObject(xpathObj);
248 for (i = 0; i < NUM_XPATH_EXPRESSIONS_CC; i++) {
249 xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_cc[i], xpathCtx);
250 if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr >0 &&
251 xpathObj->nodesetval->nodeTab[0]->type ==
253 content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
256 strncpy(res->lastupd, content, 31);
259 res->temp = atoi(content);
262 strncpy(res->xoap_t, content, 31);
265 res->bar = atoi(content);
268 res->wind_s = atoi(content);
271 res->wind_d = atoi(content);
274 res->hmid = atoi(content);
277 strncpy(res->icon, content, 2);
281 xmlXPathFreeObject(xpathObj);
286 static void parse_weather_xml(PWEATHER *res, const char *data)
289 xmlXPathContextPtr xpathCtx;
291 if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
292 NORM_ERR("weather: can't read xml data");
296 xpathCtx = xmlXPathNewContext(doc);
297 if(xpathCtx == NULL) {
298 NORM_ERR("weather: unable to create new XPath context");
303 parse_cc(res, xpathCtx);
304 xmlXPathFreeContext(xpathCtx);
311 * Horrible hack to avoid using regexes
315 static inline void parse_token(PWEATHER *res, char *token) {
320 switch (strlen(token)) {
322 //Check all tokens 2 chars long
325 //Check if token is a weather condition
326 for (i=0; i<2; i++) {
327 if (!isalpha(token[i])) break;
330 for(i=0; i<NUM_WC_CODES; i++) {
331 if (!strncmp(token, WC_CODES[i], 2)) {
340 if (!strcmp(token, "CB")) {
347 //Check all tokens 3 chars long
350 //Check if token is a modified weather condition
351 if ((token[0] == '+') || (token[0] == '-')) {
352 for (i=1; i<3; i++) {
353 if (!isalpha(token[i])) break;
356 for(i=0; i<NUM_WC_CODES; i++) {
357 if (!strncmp(&token[1], WC_CODES[i], 2)) {
366 //Check for NCD or NSC
367 if ((!strcmp(token, "NCD")) || (!strcmp(token, "NSC"))) {
373 if (!strcmp(token, "TCU")) {
380 //Check all tokens 4 chars long
383 //Check if token is a modified weather condition
384 for(i=0; i<NUM_WM_CODES; i++) {
385 if (!strncmp(token, WM_CODES[i], 2)) {
386 for(i=0; i<NUM_WC_CODES; i++) {
387 if (!strncmp(&token[2], WC_CODES[i], 2)) {
398 //Check all tokens 5 chars long
402 if (!strcmp(token, "CAVOK")) {
407 //Check if token is the temperature
408 for (i=0; i<2; i++) {
409 if (!isdigit(token[i])) break;
411 if ((i==2) && (token[2] == '/')) {
412 for (i=3; i<5; i++) {
413 if (!isdigit(token[i])) break;
416 //First 2 digits gives the air temperature
417 res->temp=atoi(token);
419 //4th and 5th digits gives the dew point temperature
420 res->dew=atoi(&token[3]);
423 res->hmid = rel_humidity(res->dew, res->temp);
429 //Check if token is the pressure
430 if ((token[0] == 'Q') || (token[0] == 'A')) {
431 for (i=1; i<5; i++) {
432 if (!isdigit(token[i])) break;
435 if (token[0] == 'A') {
436 //Convert inches of mercury to mbar
437 res->bar = (int)(atoi(&token[1])*0.338637526f);
441 //Last 4 digits is pressure im mbar
442 res->bar = atoi(&token[1]);
447 //Check if token is a modified weather condition
448 if ((token[0] == '+') || (token[0] == '-')) {
449 for(i=0; i<NUM_WM_CODES; i++) {
450 if (!strncmp(&token[1], WM_CODES[i], 2)) {
451 for(i=0; i<NUM_WC_CODES; i++) {
452 if (!strncmp(&token[3], WC_CODES[i], 2)) {
463 //Check all tokens 6 chars long
466 //Check if token is the cloud cover
467 for (i=0; i<3; i++) {
468 if (!isalpha(token[i])) break;
471 for (i=3; i<6; i++) {
472 if (!isdigit(token[i])) break;
475 //Check if first 3 digits gives the cloud cover condition
476 for(i=0; i<NUM_CC_CODES; i++) {
477 if (!strncmp(token, CC_CODES[i], 3)) {
486 //Check if token is positive temp and negative dew
487 for (i=0; i<2; i++) {
488 if (!isdigit(token[i])) break;
490 if ((i==2) && (token[2] == '/') && (token[3] == 'M')) {
491 for (i=4; i<6; i++) {
492 if (!isdigit(token[i])) break;
495 //1st and 2nd digits gives the temperature
496 res->temp = atoi(token);
498 //5th and 6th digits gives the dew point temperature
499 res->dew = -atoi(&token[4]);
502 res->hmid = rel_humidity(res->dew, res->temp);
510 //Check all tokens 7 chars long
513 //Check if token is the observation time
514 for (i=0; i<6; i++) {
515 if (!isdigit(token[i])) break;
517 if ((i==6) && (token[6] == 'Z')) return;
519 //Check if token is the wind speed/direction in knots
520 for (i=0; i<5; i++) {
521 if (!isdigit(token[i])) break;
523 if ((i==5) && (token[5] == 'K') && (token[6] == 'T')) {
525 //First 3 digits are wind direction
526 strncpy(s_tmp, token, 3);
528 res->wind_d=atoi(s_tmp);
530 //4th and 5th digit are wind speed in knots (convert to km/hr)
531 res->wind_s = (int)(atoi(&token[3])*1.852);
536 //Check if token is negative temperature
537 if ((token[0] == 'M') && (token[4] == 'M')) {
538 for (i=1; i<3; i++) {
539 if (!isdigit(token[i])) break;
541 if ((i==3) && (token[3] == '/')) {
542 for (i=5; i<7; i++) {
543 if (!isdigit(token[i])) break;
546 //2nd and 3rd digits gives the temperature
547 res->temp = -atoi(&token[1]);
549 //6th and 7th digits gives the dew point temperature
550 res->dew = -atoi(&token[5]);
553 res->hmid = rel_humidity(res->dew, res->temp);
560 //Check if token is wind variability
561 for (i=0; i<3; i++) {
562 if (!isdigit(token[i])) break;
564 if ((i==3) && (token[3] == 'V')) {
565 for (i=4; i<7; i++) {
566 if (!isdigit(token[i])) break;
573 //Check all tokens 8 chars long
576 //Check if token is the wind speed/direction in m/s
577 for (i=0; i<5; i++) {
578 if (!isdigit(token[i])) break;
580 if ((i==5)&&(token[5] == 'M')&&(token[6] == 'P')&&(token[7] == 'S')) {
582 //First 3 digits are wind direction
583 strncpy(s_tmp, token, 3);
585 res->wind_d=atoi(s_tmp);
587 //4th and 5th digit are wind speed in m/s (convert to km/hr)
588 res->wind_s = (int)(atoi(&token[3])*3.6);
595 //printf("token : %s\n", token);
601 void parse_weather_forecast(void *result, const char *data)
603 PWEATHER_FORECAST *res = (PWEATHER_FORECAST*)result;
605 memset(res, 0, sizeof(PWEATHER_FORECAST));
607 //Check if it is an xml file
608 if ( strncmp(data, "<?xml ", 6) == 0 ) {
609 parse_weather_forecast_xml(res, data);
614 void parse_weather(void *result, const char *data)
616 PWEATHER *res = (PWEATHER*)result;
618 memset(res, 0, sizeof(PWEATHER));
621 //Check if it is an xml file
622 if ( strncmp(data, "<?xml ", 6) == 0 ) {
623 parse_weather_xml(res, data);
627 //We assume its a text file
629 const char delim[] = " ";
631 //Divide time stamp and metar data
632 if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
638 if ((strtok_r(s_tmp, delim, &p_save)) != NULL) {
640 //Jump first token, must be icao
641 p_tok = strtok_r(NULL, delim, &p_save);
645 parse_token(res, p_tok);
646 p_tok = strtok_r(NULL, delim, &p_save);
648 } while (p_tok != NULL);
658 void wind_deg_to_dir(char *p, int p_max_size, int wind_deg) {
659 if ((wind_deg >= 349) || (wind_deg < 12)) {
660 strncpy(p, "N", p_max_size);
661 } else if (wind_deg < 33) {
662 strncpy(p, "NNE", p_max_size);
663 } else if (wind_deg < 57) {
664 strncpy(p, "NE", p_max_size);
665 } else if (wind_deg < 79) {
666 strncpy(p, "ENE", p_max_size);
667 } else if (wind_deg < 102) {
668 strncpy(p, "E", p_max_size);
669 } else if (wind_deg < 124) {
670 strncpy(p, "ESE", p_max_size);
671 } else if (wind_deg < 147) {
672 strncpy(p, "SE", p_max_size);
673 } else if (wind_deg < 169) {
674 strncpy(p, "SSE", p_max_size);
675 } else if (wind_deg < 192) {
676 strncpy(p, "S", p_max_size);
677 } else if (wind_deg < 214) {
678 strncpy(p, "SSW", p_max_size);
679 } else if (wind_deg < 237) {
680 strncpy(p, "SW", p_max_size);
681 } else if (wind_deg < 259) {
682 strncpy(p, "WSW", p_max_size);
683 } else if (wind_deg < 282) {
684 strncpy(p, "W", p_max_size);
685 } else if (wind_deg < 304) {
686 strncpy(p, "WNW", p_max_size);
687 } else if (wind_deg < 327) {
688 strncpy(p, "NW", p_max_size);
689 } else if (wind_deg < 349) {
690 strncpy(p, "NNW", p_max_size);
695 static void weather_forecast_process_info(char *p, int p_max_size, char *uri, unsigned int day, char *data_type, int interval)
697 PWEATHER_FORECAST *data;
699 ccurl_location_t *curloc = ccurl_find_location(&locations_head_df, uri);
700 if (!curloc->p_timed_thread) {
701 curloc->result = malloc(sizeof(PWEATHER_FORECAST));
702 memset(curloc->result, 0, sizeof(PWEATHER_FORECAST));
703 curloc->process_function = &parse_weather_forecast;
704 ccurl_init_thread(curloc, interval);
705 if (!curloc->p_timed_thread) {
706 NORM_ERR("error setting up weather_forecast thread");
710 timed_thread_lock(curloc->p_timed_thread);
711 data = (PWEATHER_FORECAST*)curloc->result;
712 if (strcmp(data_type, "hi") == EQUAL) {
713 temp_print(p, p_max_size, data->hi[day], TEMP_CELSIUS);
714 } else if (strcmp(data_type, "low") == EQUAL) {
715 temp_print(p, p_max_size, data->low[day], TEMP_CELSIUS);
716 } else if (strcmp(data_type, "icon") == EQUAL) {
717 strncpy(p, data->icon[day], p_max_size);
718 } else if (strcmp(data_type, "forecast") == EQUAL) {
719 strncpy(p, data->xoap_t[day], p_max_size);
720 } else if (strcmp(data_type, "wind_speed") == EQUAL) {
721 snprintf(p, p_max_size, "%d", data->wind_s[day]);
722 } else if (strcmp(data_type, "wind_dir") == EQUAL) {
723 wind_deg_to_dir(p, p_max_size, data->wind_d[day]);
724 } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
725 snprintf(p, p_max_size, "%d", data->wind_d[day]);
726 } else if (strcmp(data_type, "humidity") == EQUAL) {
727 snprintf(p, p_max_size, "%d", data->hmid[day]);
728 } else if (strcmp(data_type, "precipitation") == EQUAL) {
729 snprintf(p, p_max_size, "%d", data->ppcp[day]);
730 } else if (strcmp(data_type, "day") == EQUAL) {
731 strncpy(p, data->day[day], p_max_size);
732 } else if (strcmp(data_type, "date") == EQUAL) {
733 strncpy(p, data->date[day], p_max_size);
736 timed_thread_unlock(curloc->p_timed_thread);
740 static void weather_process_info(char *p, int p_max_size, char *uri, char *data_type, int interval)
742 static const char *wc[] = {
743 "", "drizzle", "rain", "hail", "soft hail",
744 "snow", "snow grains", "fog", "haze", "smoke",
745 "mist", "dust", "sand", "funnel cloud tornado",
746 "dust/sand", "squall", "sand storm", "dust storm"
750 ccurl_location_t *curloc = ccurl_find_location(&locations_head_cc, uri);
751 if (!curloc->p_timed_thread) {
752 curloc->result = malloc(sizeof(PWEATHER));
753 memset(curloc->result, 0, sizeof(PWEATHER));
754 curloc->process_function = &parse_weather;
755 ccurl_init_thread(curloc, interval);
756 if (!curloc->p_timed_thread) {
757 NORM_ERR("error setting up weather thread");
761 timed_thread_lock(curloc->p_timed_thread);
762 data = (PWEATHER*)curloc->result;
763 if (strcmp(data_type, "last_update") == EQUAL) {
764 strncpy(p, data->lastupd, p_max_size);
765 } else if (strcmp(data_type, "temperature") == EQUAL) {
766 temp_print(p, p_max_size, data->temp, TEMP_CELSIUS);
767 } else if (strcmp(data_type, "cloud_cover") == EQUAL) {
769 if (data->xoap_t[0] != '\0') {
771 strncpy(p, data->xoap_t, p_max_size);
779 strncpy(p, "", p_max_size);
780 } else if (data->cc < 3) {
781 strncpy(p, "clear", p_max_size);
782 } else if (data->cc < 5) {
783 strncpy(p, "partly cloudy", p_max_size);
784 } else if (data->cc == 5) {
785 strncpy(p, "cloudy", p_max_size);
786 } else if (data->cc == 6) {
787 strncpy(p, "overcast", p_max_size);
788 } else if (data->cc == 7) {
789 strncpy(p, "towering cumulus", p_max_size);
791 strncpy(p, "cumulonimbus", p_max_size);
794 } else if (strcmp(data_type, "icon") == EQUAL) {
795 strncpy(p, data->icon, p_max_size);
797 } else if (strcmp(data_type, "pressure") == EQUAL) {
798 snprintf(p, p_max_size, "%d", data->bar);
799 } else if (strcmp(data_type, "wind_speed") == EQUAL) {
800 snprintf(p, p_max_size, "%d", data->wind_s);
801 } else if (strcmp(data_type, "wind_dir") == EQUAL) {
802 wind_deg_to_dir(p, p_max_size, data->wind_d);
803 } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
804 snprintf(p, p_max_size, "%d", data->wind_d);
806 } else if (strcmp(data_type, "humidity") == EQUAL) {
807 snprintf(p, p_max_size, "%d", data->hmid);
808 } else if (strcmp(data_type, "weather") == EQUAL) {
809 strncpy(p, wc[data->wc], p_max_size);
812 timed_thread_unlock(curloc->p_timed_thread);
815 /* xoap suffix for weather from weather.com */
816 static char *xoap_cc = NULL;
817 static char *xoap_df = NULL;
819 static int process_weather_uri(char *uri, char *locID, int dayf UNUSED_ATTR)
821 /* locID MUST BE upper-case */
825 *tmp_p = toupper(*tmp_p);
829 /* Construct complete uri */
831 if (strstr(uri, "xoap.weather.com")) {
832 if ((dayf == 0) && (xoap_cc != NULL)) {
834 strcat(uri, xoap_cc);
835 } else if ((dayf == 1) && (xoap_df != NULL)) {
837 strcat(uri, xoap_df);
844 if (strstr(uri, "weather.noaa.gov")) {
847 } else if (!strstr(uri, "localhost") && !strstr(uri, "127.0.0.1")) {
856 * TODO: make the xoap keys file readable from the config file
857 * make the keys directly readable from the config file
858 * make the xoap keys file giveable as a command line option
860 void load_xoap_keys(void)
863 char *par = (char *) malloc(11 * sizeof(char));
864 char *key = (char *) malloc(17 * sizeof(char));
865 char *xoap = (char *) malloc(64 * sizeof(char));
867 to_real_path(xoap, XOAP_FILE);
868 fp = fopen(xoap, "r");
870 if (fscanf(fp, "%10s %16s", par, key) == 2) {
871 xoap_cc = (char *) malloc(128 * sizeof(char));
872 xoap_df = (char *) malloc(128 * sizeof(char));
874 strcpy(xoap_cc, "?cc=*&link=xoap&prod=xoap&par=");
875 strcat(xoap_cc, par);
876 strcat(xoap_cc, "&key=");
877 strcat(xoap_cc, key);
878 strcat(xoap_cc, "&unit=m");
880 /* TODO: Use FORECAST_DAYS instead of 5 */
881 strcpy(xoap_df, "?dayf=5&link=xoap&prod=xoap&par=");
882 strcat(xoap_df, par);
883 strcat(xoap_df, "&key=");
884 strcat(xoap_df, key);
885 strcat(xoap_df, "&unit=m");
894 void scan_weather_forecast_arg(struct text_object *obj, const char *arg, void *free_at_crash)
899 char *locID = (char *) malloc(9 * sizeof(char));
900 char *uri = (char *) malloc(128 * sizeof(char));
901 char *data_type = (char *) malloc(32 * sizeof(char));
903 argc = sscanf(arg, "%119s %8s %1u %31s %f", uri, locID, &day, data_type, &interval);
909 CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather_forecast");
911 if (process_weather_uri(uri, locID, 1)) {
915 CRIT_ERR(obj, free_at_crash, \
916 "could not recognize the weather forecast uri");
919 obj->data.weather_forecast.uri = uri;
920 obj->data.weather_forecast.data_type = data_type;
922 /* Limit the day between 0 (today) and FORECAST_DAYS */
923 if (day >= FORECAST_DAYS) {
924 day = FORECAST_DAYS-1;
926 obj->data.weather_forecast.day = day;
928 /* Limit the data retrieval interval to 3 hours and an half */
929 if (interval < 210) {
933 /* Convert to seconds */
934 obj->data.weather_forecast.interval = interval * 60;
937 DBGP("weather_forecast: fetching %s for day %d from %s every %d seconds", \
938 data_type, day, uri, obj->data.weather_forecast.interval);
941 void print_weather_forecast(struct text_object *obj, char *p, int p_max_size)
943 if (!obj->data.weather_forecast.uri) {
944 NORM_ERR("error processing weather forecast data, check that you have a valid XOAP key if using XOAP.");
947 weather_forecast_process_info(p, p_max_size, obj->data.weather_forecast.uri, obj->data.weather_forecast.day, obj->data.weather_forecast.data_type, obj->data.weather_forecast.interval);
951 void scan_weather_arg(struct text_object *obj, const char *arg, void *free_at_crash)
955 char *locID = (char *) malloc(9 * sizeof(char));
956 char *uri = (char *) malloc(128 * sizeof(char));
957 char *data_type = (char *) malloc(32 * sizeof(char));
959 argc = sscanf(arg, "%119s %8s %31s %f", uri, locID, data_type, &interval);
965 CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather");
967 if (process_weather_uri(uri, locID, 0)) {
971 CRIT_ERR(obj, free_at_crash, \
972 "could not recognize the weather uri");
975 obj->data.weather.uri = uri;
976 obj->data.weather.data_type = data_type;
978 /* Limit the data retrieval interval to half hour min */
983 /* Convert to seconds */
984 obj->data.weather.interval = interval * 60;
987 DBGP("weather: fetching %s from %s every %d seconds", \
988 data_type, uri, obj->data.weather.interval);
991 void print_weather(struct text_object *obj, char *p, int p_max_size)
993 if (!obj->data.weather.uri) {
994 NORM_ERR("error processing weather data, check that you have a valid XOAP key if using XOAP.");
997 weather_process_info(p, p_max_size, obj->data.weather.uri, obj->data.weather.data_type, obj->data.weather.interval);