~ubuntu-branches/debian/sid/conky/sid

« back to all changes in this revision

Viewing changes to src/weather.c

  • Committer: Bazaar Package Importer
  • Author(s): Cesare Tirabassi
  • Date: 2010-03-28 21:19:51 UTC
  • mfrom: (1.3.1 upstream) (16.1.17 lucid)
  • Revision ID: james.westby@ubuntu.com-20100328211951-ejq536k2r858srli
Tags: 1.8.0-1
* New upstream release:
  - add AF_UNIX socket support
  - fix sigsegv if config file is deleted (LP: #525926)
  - the following debian bugs are closed by this upload:
    + change automake1.10 to automake1.11 (Closes: #550929)
    + hwmon made compatible with kernels >= 2.6.31 (Closes: #556926)
    + text_buffer_size change is well documented (Closes: #519401)
    + fix diskio for not existing devices (Closes: #536557)
    + fix wrong mixer values on some systems (Closes: #540282)
    + fix minor memory leak (Closes: #566524)
    + fix some documentation error re. graphs (Closes: #564518)
    + add -p/--pause startup option (Closes: #513440)
    + fix documentation about mixer values (Closes: #538760)
* This release is based on the Ubuntu package with the following changes
  necessary for Debian (Closes: #536320):
  - change control and rules to build correctly on non-Linux arches
    (Closes: #536326)
  - updated NEWS, descriptions in control and changelog
  - change archive area to contrib
* Change priority of metapackage to extra
* My utmost thanks go to Kapil Hari Paranjape for his packaging work and to
  Luca Falavigna for being so kind to review and sponsor this release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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-2010 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 "config.h"
 
27
#include "conky.h"
 
28
#include "logging.h"
 
29
#include "weather.h"
 
30
#include "temphelper.h"
 
31
#include "text_object.h"
 
32
#include "ccurl_thread.h"
 
33
#include <time.h>
 
34
#include <ctype.h>
 
35
#ifdef MATH
 
36
#include <math.h>
 
37
#endif /* MATH */
 
38
#ifdef XOAP
 
39
#include <libxml/parser.h>
 
40
#include <libxml/xpath.h>
 
41
#endif /* XOAP */
 
42
 
 
43
/* WEATHER data */
 
44
typedef struct PWEATHER_ {
 
45
        char lastupd[32];
 
46
#ifdef XOAP
 
47
        char xoap_t[32];
 
48
        char icon[3];
 
49
#endif /* XOAP */
 
50
        int temp;
 
51
        int dew;
 
52
        int cc;
 
53
        int bar;
 
54
        int wind_s;
 
55
        int wind_d;
 
56
        int hmid;
 
57
        int wc;
 
58
} PWEATHER;
 
59
 
 
60
#ifdef XOAP
 
61
#define FORECAST_DAYS 5
 
62
typedef struct PWEATHER_FORECAST_ {
 
63
        int hi[FORECAST_DAYS];
 
64
        int low[FORECAST_DAYS];
 
65
        char icon[FORECAST_DAYS][3];
 
66
        char xoap_t[FORECAST_DAYS][32];
 
67
        char day[FORECAST_DAYS][9];
 
68
        char date[FORECAST_DAYS][7];
 
69
        int wind_s[FORECAST_DAYS];
 
70
        int wind_d[FORECAST_DAYS];
 
71
        int hmid[FORECAST_DAYS];
 
72
        int ppcp[FORECAST_DAYS];
 
73
} PWEATHER_FORECAST;
 
74
 
 
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"
 
81
};
 
82
 
 
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"
 
90
};
 
91
#endif /* XOAP */
 
92
 
 
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"
 
97
};
 
98
 
 
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",
 
103
        "SH", "DR", "FZ"
 
104
};
 
105
 
 
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"
 
112
};
 
113
 
 
114
static ccurl_location_t *locations_head_cc = 0;
 
115
#ifdef XOAP
 
116
static ccurl_location_t *locations_head_df = 0;
 
117
#endif
 
118
 
 
119
struct weather_data {
 
120
        char uri[128];
 
121
        char data_type[32];
 
122
        int interval;
 
123
};
 
124
 
 
125
#ifdef XOAP
 
126
struct weather_forecast_data {
 
127
        char uri[128];
 
128
        unsigned int day;
 
129
        char data_type[32];
 
130
        int interval;
 
131
};
 
132
#endif
 
133
 
 
134
void weather_free_info(void)
 
135
{
 
136
        ccurl_free_locations(&locations_head_cc);
 
137
#ifdef XOAP
 
138
        ccurl_free_locations(&locations_head_df);
 
139
#endif
 
140
}
 
141
 
 
142
int rel_humidity(int dew_point, int air) {
 
143
        const float a = 17.27f;
 
144
        const float b = 237.7f;
 
145
 
 
146
        float diff = a*(dew_point/(b+dew_point)-air/(b+air));
 
147
#ifdef MATH
 
148
        return (int)(100.f*expf(diff));
 
149
#else
 
150
        return (int)(16.666667163372f*(6.f+diff*(6.f+diff*(3.f+diff))));
 
151
#endif /* MATH */
 
152
}
 
153
 
 
154
#ifdef XOAP
 
155
static void parse_df(PWEATHER_FORECAST *res, xmlXPathContextPtr xpathCtx)
 
156
{
 
157
        int i, j, k;
 
158
        char *content;
 
159
        xmlXPathObjectPtr xpathObj;
 
160
 
 
161
        xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
 
162
        if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
 
163
                        xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
 
164
                content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
 
165
                NORM_ERR("XOAP error: %s", content);
 
166
                xmlFree(content);
 
167
                xmlXPathFreeObject(xpathObj);
 
168
                return;
 
169
        }
 
170
        xmlXPathFreeObject(xpathObj);
 
171
 
 
172
        for (i = 0; i < NUM_XPATH_EXPRESSIONS_DF; i++) {
 
173
                xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_df[i], xpathCtx);
 
174
                if (xpathObj != NULL) {
 
175
                        xmlNodeSetPtr nodes = xpathObj->nodesetval;
 
176
                        k = 0;
 
177
                        for (j = 0; j < nodes->nodeNr; ++j) {
 
178
                                if (nodes->nodeTab[j]->type == XML_ELEMENT_NODE) {
 
179
                                        content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
 
180
                                        switch(i) {
 
181
                                        case 0:
 
182
                                                res->hi[k] = atoi(content);
 
183
                                                break;
 
184
                                        case 1:
 
185
                                                res->low[k] = atoi(content);
 
186
                                                break;
 
187
                                        case 2:
 
188
                                                strncpy(res->icon[k], content, 2);
 
189
                                        case 3:
 
190
                                                strncpy(res->xoap_t[k], content, 31);
 
191
                                                break;
 
192
                                        case 4:
 
193
                                                res->wind_s[k] = atoi(content);
 
194
                                                break;
 
195
                                        case 5:
 
196
                                                res->wind_d[k] = atoi(content);
 
197
                                                break;
 
198
                                        case 6:
 
199
                                                res->ppcp[k] = atoi(content);
 
200
                                                break;
 
201
                                        case 7:
 
202
                                                res->hmid[k] = atoi(content);
 
203
                                        }
 
204
                                } else if (nodes->nodeTab[j]->type == XML_ATTRIBUTE_NODE) {
 
205
                                        content = (char *)xmlNodeGetContent(nodes->nodeTab[k]);
 
206
                                        switch(i) {
 
207
                                        case 8:
 
208
                                                strncpy(res->day[k], content, 8);
 
209
                                                break;
 
210
                                        case 9:
 
211
                                                strncpy(res->date[k], content, 6);
 
212
                                        }
 
213
                                }
 
214
                                xmlFree(content);
 
215
                                if (++k == FORECAST_DAYS) break;
 
216
                        }
 
217
                }
 
218
                xmlXPathFreeObject(xpathObj);
 
219
        }
 
220
        return;
 
221
}
 
222
 
 
223
static void parse_weather_forecast_xml(PWEATHER_FORECAST *res, const char *data)
 
224
{
 
225
        xmlDocPtr doc;
 
226
        xmlXPathContextPtr xpathCtx;
 
227
 
 
228
        if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
 
229
                NORM_ERR("weather_forecast: can't read xml data");
 
230
                return;
 
231
        }
 
232
 
 
233
        xpathCtx = xmlXPathNewContext(doc);
 
234
        if(xpathCtx == NULL) {
 
235
                NORM_ERR("weather_forecast: unable to create new XPath context");
 
236
                xmlFreeDoc(doc);
 
237
                return;
 
238
        }
 
239
 
 
240
        parse_df(res, xpathCtx);
 
241
        xmlXPathFreeContext(xpathCtx);
 
242
        xmlFreeDoc(doc);
 
243
        return;
 
244
}
 
245
 
 
246
static void parse_cc(PWEATHER *res, xmlXPathContextPtr xpathCtx)
 
247
{
 
248
        int i;
 
249
        char *content;
 
250
        xmlXPathObjectPtr xpathObj;
 
251
 
 
252
        xpathObj = xmlXPathEvalExpression((const xmlChar *)"/error/err", xpathCtx);
 
253
        if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr > 0 &&
 
254
                        xpathObj->nodesetval->nodeTab[0]->type == XML_ELEMENT_NODE) {
 
255
                content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
 
256
                NORM_ERR("XOAP error: %s", content);
 
257
                xmlFree(content);
 
258
                xmlXPathFreeObject(xpathObj);
 
259
                return;
 
260
        }
 
261
        xmlXPathFreeObject(xpathObj);
 
262
 
 
263
        for (i = 0; i < NUM_XPATH_EXPRESSIONS_CC; i++) {
 
264
                xpathObj = xmlXPathEvalExpression((const xmlChar *)xpath_expression_cc[i], xpathCtx);
 
265
                if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr >0 &&
 
266
                                xpathObj->nodesetval->nodeTab[0]->type ==
 
267
                                XML_ELEMENT_NODE) {
 
268
                        content = (char *)xmlNodeGetContent(xpathObj->nodesetval->nodeTab[0]);
 
269
                        switch(i) {
 
270
                                case 0:
 
271
                                        strncpy(res->lastupd, content, 31);
 
272
                                        break;
 
273
                                case 1:
 
274
                                        res->temp = atoi(content);
 
275
                                        break;
 
276
                                case 2:
 
277
                                        strncpy(res->xoap_t, content, 31);
 
278
                                        break;
 
279
                                case 3:
 
280
                                        res->bar = atoi(content);
 
281
                                        break;
 
282
                                case 4:
 
283
                                        res->wind_s = atoi(content);
 
284
                                        break;
 
285
                                case 5:
 
286
                                        res->wind_d = atoi(content);
 
287
                                        break;
 
288
                                case 6:
 
289
                                        res->hmid = atoi(content);
 
290
                                        break;
 
291
                                case 7:
 
292
                                        strncpy(res->icon, content, 2);
 
293
                        }
 
294
                        xmlFree(content);
 
295
                }
 
296
                xmlXPathFreeObject(xpathObj);
 
297
        }
 
298
        return;
 
299
}
 
300
 
 
301
static void parse_weather_xml(PWEATHER *res, const char *data)
 
302
{
 
303
        xmlDocPtr doc;
 
304
        xmlXPathContextPtr xpathCtx;
 
305
 
 
306
        if (!(doc = xmlReadMemory(data, strlen(data), "", NULL, 0))) {
 
307
                NORM_ERR("weather: can't read xml data");
 
308
                return;
 
309
        }
 
310
 
 
311
        xpathCtx = xmlXPathNewContext(doc);
 
312
        if(xpathCtx == NULL) {
 
313
                NORM_ERR("weather: unable to create new XPath context");
 
314
                xmlFreeDoc(doc);
 
315
                return;
 
316
        }
 
317
 
 
318
        parse_cc(res, xpathCtx);
 
319
        xmlXPathFreeContext(xpathCtx);
 
320
        xmlFreeDoc(doc);
 
321
        return;
 
322
}
 
323
#endif /* XOAP */
 
324
 
 
325
/*
 
326
 * Horrible hack to avoid using regexes
 
327
 *
 
328
 */
 
329
 
 
330
static inline void parse_token(PWEATHER *res, char *token) {
 
331
 
 
332
        int i;
 
333
        char s_tmp[64];
 
334
 
 
335
        switch (strlen(token)) {
 
336
 
 
337
                //Check all tokens 2 chars long
 
338
                case 2:
 
339
 
 
340
                        //Check if token is a weather condition
 
341
                        for (i=0; i<2; i++) {
 
342
                                if (!isalpha(token[i])) break;
 
343
                        }
 
344
                        if (i==2) {
 
345
                                for(i=0; i<NUM_WC_CODES; i++) {
 
346
                                        if (!strncmp(token, WC_CODES[i], 2)) {
 
347
                                                res->wc=i+1;
 
348
                                                break;
 
349
                                        }
 
350
                                }
 
351
                                return;
 
352
                        }
 
353
 
 
354
                        //Check for CB
 
355
                        if (!strcmp(token, "CB")) {
 
356
                                res->cc = 8;
 
357
                                return;
 
358
                        }
 
359
 
 
360
                        break;
 
361
 
 
362
                        //Check all tokens 3 chars long
 
363
                case 3:
 
364
 
 
365
                        //Check if token is a modified weather condition
 
366
                        if ((token[0] == '+') || (token[0] == '-')) {
 
367
                                for (i=1; i<3; i++) {
 
368
                                        if (!isalpha(token[i])) break;
 
369
                                }
 
370
                                if (i==3) {
 
371
                                        for(i=0; i<NUM_WC_CODES; i++) {
 
372
                                                if (!strncmp(&token[1], WC_CODES[i], 2)) {
 
373
                                                        res->wc=i+1;
 
374
                                                        break;
 
375
                                                }
 
376
                                        }
 
377
                                        return;
 
378
                                }
 
379
                        }
 
380
 
 
381
                        //Check for NCD or NSC
 
382
                        if ((!strcmp(token, "NCD")) || (!strcmp(token, "NSC"))) {
 
383
                                res->cc = 1;
 
384
                                return;
 
385
                        }
 
386
 
 
387
                        //Check for TCU
 
388
                        if (!strcmp(token, "TCU")) {
 
389
                                res->cc = 7;
 
390
                                return;
 
391
                        }
 
392
 
 
393
                        break;
 
394
 
 
395
                        //Check all tokens 4 chars long
 
396
                case 4:
 
397
 
 
398
                        //Check if token is a modified weather condition
 
399
                        for(i=0; i<NUM_WM_CODES; i++) {
 
400
                                if (!strncmp(token, WM_CODES[i], 2)) {
 
401
                                        for(i=0; i<NUM_WC_CODES; i++) {
 
402
                                                if (!strncmp(&token[2], WC_CODES[i], 2)) {
 
403
                                                        res->wc=i+1;
 
404
                                                        return;
 
405
                                                }
 
406
                                        }
 
407
                                        break;
 
408
                                }
 
409
                        }
 
410
 
 
411
                        break;
 
412
 
 
413
                        //Check all tokens 5 chars long
 
414
                case 5:
 
415
 
 
416
                        //Check for CAVOK
 
417
                        if (!strcmp(token, "CAVOK")) {
 
418
                                res->cc = 1;
 
419
                                return;
 
420
                        }
 
421
 
 
422
                        //Check if token is the temperature
 
423
                        for (i=0; i<2; i++) {
 
424
                                if (!isdigit(token[i])) break;
 
425
                        }
 
426
                        if ((i==2) && (token[2] == '/')) {
 
427
                                for (i=3; i<5; i++) {
 
428
                                        if (!isdigit(token[i])) break;
 
429
                                }
 
430
                                if (i==5) {
 
431
                                        //First 2 digits gives the air temperature
 
432
                                        res->temp=atoi(token);
 
433
 
 
434
                                        //4th and 5th digits gives the dew point temperature
 
435
                                        res->dew=atoi(&token[3]);
 
436
 
 
437
                                        //Compute humidity
 
438
                                        res->hmid = rel_humidity(res->dew, res->temp);
 
439
 
 
440
                                        return;
 
441
                                }
 
442
                        }
 
443
 
 
444
                        //Check if token is the pressure
 
445
                        if ((token[0] == 'Q') || (token[0] == 'A')) {
 
446
                                for (i=1; i<5; i++) {
 
447
                                        if (!isdigit(token[i])) break;
 
448
                                }
 
449
                                if (i==5) {
 
450
                                        if (token[0] == 'A') {
 
451
                                                //Convert inches of mercury to mbar
 
452
                                                res->bar = (int)(atoi(&token[1])*0.338637526f);
 
453
                                                return;
 
454
                                        }
 
455
 
 
456
                                        //Last 4 digits is pressure im mbar
 
457
                                        res->bar = atoi(&token[1]);
 
458
                                        return;
 
459
                                }
 
460
                        }
 
461
 
 
462
                        //Check if token is a modified weather condition
 
463
                        if ((token[0] == '+') || (token[0] == '-')) {
 
464
                                for(i=0; i<NUM_WM_CODES; i++) {
 
465
                                        if (!strncmp(&token[1], WM_CODES[i], 2)) {
 
466
                                                for(i=0; i<NUM_WC_CODES; i++) {
 
467
                                                        if (!strncmp(&token[3], WC_CODES[i], 2)) {
 
468
                                                                res->wc=i+1;
 
469
                                                                return;
 
470
                                                        }
 
471
                                                }
 
472
                                                break;
 
473
                                        }
 
474
                                }
 
475
                        }
 
476
                        break;
 
477
 
 
478
                        //Check all tokens 6 chars long
 
479
                case 6:
 
480
 
 
481
                        //Check if token is the cloud cover
 
482
                        for (i=0; i<3; i++) {
 
483
                                if (!isalpha(token[i])) break;
 
484
                        }
 
485
                        if (i==3) {
 
486
                                for (i=3; i<6; i++) {
 
487
                                        if (!isdigit(token[i])) break;
 
488
                                }
 
489
                                if (i==6) {
 
490
                                        //Check if first 3 digits gives the cloud cover condition
 
491
                                        for(i=0; i<NUM_CC_CODES; i++) {
 
492
                                                if (!strncmp(token, CC_CODES[i], 3)) {
 
493
                                                        res->cc=i+1;
 
494
                                                        break;
 
495
                                                }
 
496
                                        }
 
497
                                        return;
 
498
                                }
 
499
                        }
 
500
 
 
501
                        //Check if token is positive temp and negative dew
 
502
                        for (i=0; i<2; i++) {
 
503
                                if (!isdigit(token[i])) break;
 
504
                        }
 
505
                        if ((i==2) && (token[2] == '/')  && (token[3] == 'M')) {
 
506
                                for (i=4; i<6; i++) {
 
507
                                        if (!isdigit(token[i])) break;
 
508
                                }
 
509
                                if (i==6) {
 
510
                                        //1st and 2nd digits gives the temperature
 
511
                                        res->temp = atoi(token);
 
512
 
 
513
                                        //5th and 6th digits gives the dew point temperature
 
514
                                        res->dew = -atoi(&token[4]);
 
515
 
 
516
                                        //Compute humidity
 
517
                                        res->hmid = rel_humidity(res->dew, res->temp);
 
518
 
 
519
                                        return;
 
520
                                }
 
521
                        }
 
522
 
 
523
                        break;
 
524
 
 
525
                        //Check all tokens 7 chars long
 
526
                case 7:
 
527
 
 
528
                        //Check if token is the observation time
 
529
                        for (i=0; i<6; i++) {
 
530
                                if (!isdigit(token[i])) break;
 
531
                        }
 
532
                        if ((i==6) && (token[6] == 'Z')) return;
 
533
 
 
534
                        //Check if token is the wind speed/direction in knots
 
535
                        for (i=0; i<5; i++) {
 
536
                                if (!isdigit(token[i])) break;
 
537
                        }
 
538
                        if ((i==5) && (token[5] == 'K') &&  (token[6] == 'T')) {
 
539
 
 
540
                                //First 3 digits are wind direction
 
541
                                strncpy(s_tmp, token, 3);
 
542
                                s_tmp[3]='\0';
 
543
                                res->wind_d=atoi(s_tmp);
 
544
 
 
545
                                //4th and 5th digit are wind speed in knots (convert to km/hr)
 
546
                                res->wind_s = (int)(atoi(&token[3])*1.852);
 
547
 
 
548
                                return;
 
549
                        }
 
550
 
 
551
                        //Check if token is negative temperature
 
552
                        if ((token[0] == 'M') && (token[4] == 'M')) {
 
553
                                for (i=1; i<3; i++) {
 
554
                                        if (!isdigit(token[i])) break;
 
555
                                }
 
556
                                if ((i==3) && (token[3] == '/')) {
 
557
                                        for (i=5; i<7; i++) {
 
558
                                                if (!isdigit(token[i])) break;
 
559
                                        }
 
560
                                        if (i==7) {
 
561
                                                //2nd and 3rd digits gives the temperature
 
562
                                                res->temp = -atoi(&token[1]);
 
563
 
 
564
                                                //6th and 7th digits gives the dew point temperature
 
565
                                                res->dew = -atoi(&token[5]);
 
566
 
 
567
                                                //Compute humidity
 
568
                                                res->hmid = rel_humidity(res->dew, res->temp);
 
569
 
 
570
                                                return;
 
571
                                        }
 
572
                                }
 
573
                        }
 
574
 
 
575
                        //Check if token is wind variability
 
576
                        for (i=0; i<3; i++) {
 
577
                                if (!isdigit(token[i])) break;
 
578
                        }
 
579
                        if ((i==3) && (token[3] == 'V')) {
 
580
                                for (i=4; i<7; i++) {
 
581
                                        if (!isdigit(token[i])) break;
 
582
                                }
 
583
                                if (i==7) return;
 
584
                        }
 
585
 
 
586
                        break;
 
587
 
 
588
                        //Check all tokens 8 chars long
 
589
                case 8:
 
590
 
 
591
                        //Check if token is the wind speed/direction in m/s
 
592
                        for (i=0; i<5; i++) {
 
593
                                if (!isdigit(token[i])) break;
 
594
                        }
 
595
                        if ((i==5)&&(token[5] == 'M')&&(token[6] == 'P')&&(token[7] == 'S')) {
 
596
 
 
597
                                //First 3 digits are wind direction
 
598
                                strncpy(s_tmp, token, 3);
 
599
                                s_tmp[3]='\0';
 
600
                                res->wind_d=atoi(s_tmp);
 
601
 
 
602
                                //4th and 5th digit are wind speed in m/s (convert to km/hr)
 
603
                                res->wind_s = (int)(atoi(&token[3])*3.6);
 
604
 
 
605
                                return;
 
606
                        }
 
607
 
 
608
                default:
 
609
 
 
610
                        //printf("token : %s\n", token);
 
611
                        break;
 
612
        }
 
613
}
 
614
 
 
615
#ifdef XOAP
 
616
void parse_weather_forecast(void *result, const char *data)
 
617
{
 
618
        PWEATHER_FORECAST *res = (PWEATHER_FORECAST*)result;
 
619
        /* Reset results */
 
620
        memset(res, 0, sizeof(PWEATHER_FORECAST));
 
621
 
 
622
        //Check if it is an xml file
 
623
        if ( strncmp(data, "<?xml ", 6) == 0 ) {
 
624
                parse_weather_forecast_xml(res, data);
 
625
        }
 
626
}
 
627
#endif /* XOAP */
 
628
 
 
629
void parse_weather(void *result, const char *data)
 
630
{
 
631
        PWEATHER *res = (PWEATHER*)result;
 
632
        /* Reset results */
 
633
        memset(res, 0, sizeof(PWEATHER));
 
634
 
 
635
#ifdef XOAP
 
636
        //Check if it is an xml file
 
637
        if ( strncmp(data, "<?xml ", 6) == 0 ) {
 
638
                parse_weather_xml(res, data);
 
639
        } else
 
640
#endif /* XOAP */
 
641
        {
 
642
                //We assume its a text file
 
643
                char s_tmp[256];
 
644
                const char delim[] = " ";
 
645
 
 
646
                //Divide time stamp and metar data
 
647
                if (sscanf(data, "%[^'\n']\n%[^'\n']", res->lastupd, s_tmp) == 2) {
 
648
 
 
649
                        //Process all tokens
 
650
                        char *p_tok = NULL;
 
651
                        char *p_save = NULL;
 
652
 
 
653
                        if ((strtok_r(s_tmp, delim, &p_save)) != NULL) {
 
654
 
 
655
                                //Jump first token, must be icao
 
656
                                p_tok = strtok_r(NULL, delim, &p_save);
 
657
 
 
658
                                do {
 
659
 
 
660
                                        parse_token(res, p_tok);
 
661
                                        p_tok = strtok_r(NULL, delim, &p_save);
 
662
 
 
663
                                } while (p_tok != NULL);
 
664
                        }
 
665
                        return;
 
666
                }
 
667
                else {
 
668
                        return;
 
669
                }
 
670
        }
 
671
}
 
672
 
 
673
void wind_deg_to_dir(char *p, int p_max_size, int wind_deg) {
 
674
        if ((wind_deg >= 349) || (wind_deg < 12)) {
 
675
                strncpy(p, "N", p_max_size);
 
676
        } else if (wind_deg < 33) {
 
677
                strncpy(p, "NNE", p_max_size);
 
678
        } else if (wind_deg < 57) {
 
679
                strncpy(p, "NE", p_max_size);
 
680
        } else if (wind_deg < 79) {
 
681
                strncpy(p, "ENE", p_max_size);
 
682
        } else if (wind_deg < 102) {
 
683
                strncpy(p, "E", p_max_size);
 
684
        } else if (wind_deg < 124) {
 
685
                strncpy(p, "ESE", p_max_size);
 
686
        } else if (wind_deg < 147) {
 
687
                strncpy(p, "SE", p_max_size);
 
688
        } else if (wind_deg < 169) {
 
689
                strncpy(p, "SSE", p_max_size);
 
690
        } else if (wind_deg < 192) {
 
691
                strncpy(p, "S", p_max_size);
 
692
        } else if (wind_deg < 214) {
 
693
                strncpy(p, "SSW", p_max_size);
 
694
        } else if (wind_deg < 237) {
 
695
                strncpy(p, "SW", p_max_size);
 
696
        } else if (wind_deg < 259) {
 
697
                strncpy(p, "WSW", p_max_size);
 
698
        } else if (wind_deg < 282) {
 
699
                        strncpy(p, "W", p_max_size);
 
700
        } else if (wind_deg < 304) {
 
701
                strncpy(p, "WNW", p_max_size);
 
702
        } else if (wind_deg < 327) {
 
703
                strncpy(p, "NW", p_max_size);
 
704
        } else if (wind_deg < 349) {
 
705
                strncpy(p, "NNW", p_max_size);
 
706
        };
 
707
}
 
708
 
 
709
#ifdef XOAP
 
710
static void weather_forecast_process_info(char *p, int p_max_size, char *uri, unsigned int day, char *data_type, int interval)
 
711
{
 
712
        PWEATHER_FORECAST *data;
 
713
 
 
714
        ccurl_location_t *curloc = ccurl_find_location(&locations_head_df, uri);
 
715
        if (!curloc->p_timed_thread) {
 
716
                curloc->result = malloc(sizeof(PWEATHER_FORECAST));
 
717
                memset(curloc->result, 0, sizeof(PWEATHER_FORECAST));
 
718
                curloc->process_function = &parse_weather_forecast;
 
719
                ccurl_init_thread(curloc, interval);
 
720
                if (!curloc->p_timed_thread) {
 
721
                        NORM_ERR("error setting up weather_forecast thread");
 
722
                }
 
723
        }
 
724
 
 
725
        timed_thread_lock(curloc->p_timed_thread);
 
726
        data = (PWEATHER_FORECAST*)curloc->result;
 
727
        if (strcmp(data_type, "hi") == EQUAL) {
 
728
                temp_print(p, p_max_size, data->hi[day], TEMP_CELSIUS);
 
729
        } else if (strcmp(data_type, "low") == EQUAL) {
 
730
                temp_print(p, p_max_size, data->low[day], TEMP_CELSIUS);
 
731
        } else if (strcmp(data_type, "icon") == EQUAL) {
 
732
                strncpy(p, data->icon[day], p_max_size);
 
733
        } else if (strcmp(data_type, "forecast") == EQUAL) {
 
734
                strncpy(p, data->xoap_t[day], p_max_size);
 
735
        } else if (strcmp(data_type, "wind_speed") == EQUAL) {
 
736
                snprintf(p, p_max_size, "%d", data->wind_s[day]);
 
737
        } else if (strcmp(data_type, "wind_dir") == EQUAL) {
 
738
                wind_deg_to_dir(p, p_max_size, data->wind_d[day]);
 
739
        } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
 
740
                snprintf(p, p_max_size, "%d", data->wind_d[day]);
 
741
        } else if (strcmp(data_type, "humidity") == EQUAL) {
 
742
                snprintf(p, p_max_size, "%d", data->hmid[day]);
 
743
        } else if (strcmp(data_type, "precipitation") == EQUAL) {
 
744
                snprintf(p, p_max_size, "%d", data->ppcp[day]);
 
745
        } else if (strcmp(data_type, "day") == EQUAL) {
 
746
                strncpy(p, data->day[day], p_max_size);
 
747
        } else if (strcmp(data_type, "date") == EQUAL) {
 
748
                strncpy(p, data->date[day], p_max_size);
 
749
        }
 
750
 
 
751
        timed_thread_unlock(curloc->p_timed_thread);
 
752
}
 
753
#endif /* XOAP */
 
754
 
 
755
static void weather_process_info(char *p, int p_max_size, char *uri, char *data_type, int interval)
 
756
{
 
757
        static const char *wc[] = {
 
758
                "", "drizzle", "rain", "hail", "soft hail",
 
759
                "snow", "snow grains", "fog", "haze", "smoke",
 
760
                "mist", "dust", "sand", "funnel cloud tornado",
 
761
                "dust/sand", "squall", "sand storm", "dust storm"
 
762
        };
 
763
        PWEATHER *data;
 
764
 
 
765
        ccurl_location_t *curloc = ccurl_find_location(&locations_head_cc, uri);
 
766
        if (!curloc->p_timed_thread) {
 
767
                curloc->result = malloc(sizeof(PWEATHER));
 
768
                memset(curloc->result, 0, sizeof(PWEATHER));
 
769
                curloc->process_function = &parse_weather;
 
770
                ccurl_init_thread(curloc, interval);
 
771
                if (!curloc->p_timed_thread) {
 
772
                        NORM_ERR("error setting up weather thread");
 
773
                }
 
774
        }
 
775
 
 
776
        timed_thread_lock(curloc->p_timed_thread);
 
777
        data = (PWEATHER*)curloc->result;
 
778
        if (strcmp(data_type, "last_update") == EQUAL) {
 
779
                strncpy(p, data->lastupd, p_max_size);
 
780
        } else if (strcmp(data_type, "temperature") == EQUAL) {
 
781
                temp_print(p, p_max_size, data->temp, TEMP_CELSIUS);
 
782
        } else if (strcmp(data_type, "cloud_cover") == EQUAL) {
 
783
#ifdef XOAP
 
784
                if (data->xoap_t[0] != '\0') {
 
785
                        char *s = p;
 
786
                        strncpy(p, data->xoap_t, p_max_size);
 
787
                        while (*s) {
 
788
                                *s = tolower(*s);
 
789
                                s++;
 
790
                        }
 
791
                } else
 
792
#endif /* XOAP */
 
793
                        if (data->cc == 0) {
 
794
                                strncpy(p, "", p_max_size);
 
795
                        } else if (data->cc < 3) {
 
796
                                strncpy(p, "clear", p_max_size);
 
797
                        } else if (data->cc < 5) {
 
798
                                strncpy(p, "partly cloudy", p_max_size);
 
799
                        } else if (data->cc == 5) {
 
800
                                strncpy(p, "cloudy", p_max_size);
 
801
                        } else if (data->cc == 6) {
 
802
                                strncpy(p, "overcast", p_max_size);
 
803
                        } else if (data->cc == 7) {
 
804
                                strncpy(p, "towering cumulus", p_max_size);
 
805
                        } else  {
 
806
                                strncpy(p, "cumulonimbus", p_max_size);
 
807
                        }
 
808
#ifdef XOAP
 
809
        } else if (strcmp(data_type, "icon") == EQUAL) {
 
810
                strncpy(p, data->icon, p_max_size);
 
811
#endif /* XOAP */
 
812
        } else if (strcmp(data_type, "pressure") == EQUAL) {
 
813
                snprintf(p, p_max_size, "%d", data->bar);
 
814
        } else if (strcmp(data_type, "wind_speed") == EQUAL) {
 
815
                snprintf(p, p_max_size, "%d", data->wind_s);
 
816
        } else if (strcmp(data_type, "wind_dir") == EQUAL) {
 
817
                wind_deg_to_dir(p, p_max_size, data->wind_d);
 
818
        } else if (strcmp(data_type, "wind_dir_DEG") == EQUAL) {
 
819
                snprintf(p, p_max_size, "%d", data->wind_d);
 
820
 
 
821
        } else if (strcmp(data_type, "humidity") == EQUAL) {
 
822
                snprintf(p, p_max_size, "%d", data->hmid);
 
823
        } else if (strcmp(data_type, "weather") == EQUAL) {
 
824
                strncpy(p, wc[data->wc], p_max_size);
 
825
        }
 
826
 
 
827
        timed_thread_unlock(curloc->p_timed_thread);
 
828
}
 
829
 
 
830
#ifdef XOAP
 
831
/* xoap suffix for weather from weather.com */
 
832
static char *xoap_cc = NULL;
 
833
static char *xoap_df = NULL;
 
834
#endif /* XOAP */
 
835
 
 
836
static int process_weather_uri(char *uri, char *locID, int dayf UNUSED_ATTR)
 
837
{
 
838
        /* locID MUST BE upper-case */
 
839
        char *tmp_p = locID;
 
840
 
 
841
        while (*tmp_p) {
 
842
                *tmp_p = toupper(*tmp_p);
 
843
                tmp_p++;
 
844
        }
 
845
 
 
846
        /* Construct complete uri */
 
847
#ifdef XOAP
 
848
        if (strstr(uri, "xoap.weather.com")) {
 
849
                if ((dayf == 0) && (xoap_cc != NULL)) {
 
850
                        strcat(uri, locID);
 
851
                        strcat(uri, xoap_cc);
 
852
                } else if ((dayf == 1) && (xoap_df != NULL)) {
 
853
                        strcat(uri, locID);
 
854
                        strcat(uri, xoap_df);
 
855
                } else {
 
856
                        free(uri);
 
857
                        uri = NULL;
 
858
                }
 
859
        } else
 
860
#endif /* XOAP */
 
861
        if (strstr(uri, "weather.noaa.gov")) {
 
862
                strcat(uri, locID);
 
863
                strcat(uri, ".TXT");
 
864
        } else  if (!strstr(uri, "localhost") && !strstr(uri, "127.0.0.1")) {
 
865
                return -1;
 
866
        }
 
867
        return 0;
 
868
}
 
869
 
 
870
#ifdef XOAP
 
871
 
 
872
/*
 
873
 * TODO: make the xoap keys file readable from the config file
 
874
 *       make the keys directly readable from the config file
 
875
 *       make the xoap keys file giveable as a command line option
 
876
 */
 
877
void load_xoap_keys(void)
 
878
{
 
879
        FILE *fp;
 
880
        char *par  = (char *) malloc(11 * sizeof(char));
 
881
        char *key  = (char *) malloc(17 * sizeof(char));
 
882
        char *xoap = (char *) malloc(64 * sizeof(char));
 
883
 
 
884
        to_real_path(xoap, XOAP_FILE);
 
885
        fp = fopen(xoap, "r");
 
886
        if (fp != NULL) {
 
887
                if (fscanf(fp, "%10s %16s", par, key) == 2) {
 
888
                        xoap_cc = (char *) malloc(128 * sizeof(char));
 
889
                        xoap_df = (char *) malloc(128 * sizeof(char));
 
890
 
 
891
                        strcpy(xoap_cc, "?cc=*&link=xoap&prod=xoap&par=");
 
892
                        strcat(xoap_cc, par);
 
893
                        strcat(xoap_cc, "&key=");
 
894
                        strcat(xoap_cc, key);
 
895
                        strcat(xoap_cc, "&unit=m");
 
896
 
 
897
                        /* TODO: Use FORECAST_DAYS instead of 5 */
 
898
                        strcpy(xoap_df, "?dayf=5&link=xoap&prod=xoap&par=");
 
899
                        strcat(xoap_df, par);
 
900
                        strcat(xoap_df, "&key=");
 
901
                        strcat(xoap_df, key);
 
902
                        strcat(xoap_df, "&unit=m");
 
903
                }
 
904
                fclose(fp);
 
905
        }
 
906
        free(par);
 
907
        free(key);
 
908
        free(xoap);
 
909
}
 
910
 
 
911
void scan_weather_forecast_arg(struct text_object *obj, const char *arg, void *free_at_crash)
 
912
{
 
913
        int argc;
 
914
        struct weather_forecast_data *wfd;
 
915
        float interval = 0;
 
916
        char *locID = (char *) malloc(9 * sizeof(char));
 
917
 
 
918
        wfd = malloc(sizeof(struct weather_forecast_data));
 
919
        memset(wfd, 0, sizeof(struct weather_forecast_data));
 
920
 
 
921
        argc = sscanf(arg, "%119s %8s %1u %31s %f", wfd->uri, locID, &wfd->day, wfd->data_type, &interval);
 
922
 
 
923
        if (argc < 4) {
 
924
                free(locID);
 
925
                free(wfd);
 
926
                CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather_forecast");
 
927
        }
 
928
        if (process_weather_uri(wfd->uri, locID, 1)) {
 
929
                free(locID);
 
930
                free(wfd);
 
931
                CRIT_ERR(obj, free_at_crash, \
 
932
                                "could not recognize the weather forecast uri");
 
933
        }
 
934
 
 
935
        /* Limit the day between 0 (today) and FORECAST_DAYS */
 
936
        if (wfd->day >= FORECAST_DAYS) {
 
937
                wfd->day = FORECAST_DAYS-1;
 
938
        }
 
939
 
 
940
        /* Limit the data retrieval interval to 3 hours and an half */
 
941
        if (interval < 210) {
 
942
                interval = 210;
 
943
        }
 
944
 
 
945
        /* Convert to seconds */
 
946
        wfd->interval = interval * 60;
 
947
        free(locID);
 
948
 
 
949
        DBGP("weather_forecast: fetching %s for day %d from %s every %d seconds", \
 
950
                        wfd->data_type, wfd->day, wfd->uri, wfd->interval);
 
951
 
 
952
        obj->data.opaque = wfd;
 
953
}
 
954
 
 
955
void print_weather_forecast(struct text_object *obj, char *p, int p_max_size)
 
956
{
 
957
        struct weather_forecast_data *wfd = obj->data.opaque;
 
958
 
 
959
        if (!wfd || !wfd->uri) {
 
960
                NORM_ERR("error processing weather forecast data, check that you have a valid XOAP key if using XOAP.");
 
961
                return;
 
962
        }
 
963
        weather_forecast_process_info(p, p_max_size, wfd->uri, wfd->day, wfd->data_type, wfd->interval);
 
964
}
 
965
#endif /* XOAP */
 
966
 
 
967
void scan_weather_arg(struct text_object *obj, const char *arg, void *free_at_crash)
 
968
{
 
969
        int argc;
 
970
        struct weather_data *wd;
 
971
        char *locID = (char *) malloc(9 * sizeof(char));
 
972
        float interval = 0;
 
973
 
 
974
        wd = malloc(sizeof(struct weather_data));
 
975
        memset(wd, 0, sizeof(struct weather_data));
 
976
 
 
977
        argc = sscanf(arg, "%119s %8s %31s %f", wd->uri, locID, wd->data_type, &interval);
 
978
 
 
979
        if (argc < 3) {
 
980
                free(locID);
 
981
                free(wd);
 
982
                CRIT_ERR(obj, free_at_crash, "wrong number of arguments for $weather");
 
983
        }
 
984
        if (process_weather_uri(wd->uri, locID, 0)) {
 
985
                free(locID);
 
986
                free(wd);
 
987
                CRIT_ERR(obj, free_at_crash, \
 
988
                                "could not recognize the weather uri");
 
989
        }
 
990
 
 
991
        /* Limit the data retrieval interval to half hour min */
 
992
        if (interval < 30) {
 
993
                interval = 30;
 
994
        }
 
995
 
 
996
        /* Convert to seconds */
 
997
        wd->interval = interval * 60;
 
998
        free(locID);
 
999
 
 
1000
        DBGP("weather: fetching %s from %s every %d seconds", \
 
1001
                        wd->data_type, wd->uri, wd->interval);
 
1002
 
 
1003
        obj->data.opaque = wd;
 
1004
}
 
1005
 
 
1006
void print_weather(struct text_object *obj, char *p, int p_max_size)
 
1007
{
 
1008
        struct weather_data *wd = obj->data.opaque;
 
1009
 
 
1010
        if (!wd || !wd->uri) {
 
1011
                NORM_ERR("error processing weather data, check that you have a valid XOAP key if using XOAP.");
 
1012
                return;
 
1013
        }
 
1014
        weather_process_info(p, p_max_size, wd->uri, wd->data_type, wd->interval);
 
1015
}
 
1016
 
 
1017
void free_weather(struct text_object *obj)
 
1018
{
 
1019
        if (obj->data.opaque) {
 
1020
                free(obj->data.opaque);
 
1021
                obj->data.opaque = NULL;
 
1022
        }
 
1023
}