3 /* Copyright (C) 2002 Brad Jorsch <anomie@users.sourceforge.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
32 # if TIME_WITH_SYS_TIME
33 # include <sys/time.h>
37 # include <sys/time.h>
47 #include <X11/Xatom.h>
50 #include "wmweather+.h"
55 #include "animation.h"
61 char *monthnames
[]={ "", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
62 char *monthnames2
[]={ "", "", "", "", "", "", "JUNE", "JULY", "", "SEPT", "", "", "" };
63 char *wdaynames
[]={ "SUNDAY", "MONDAY", "TUESDAY", "WEDN'SDAY", "THURSDAY", "FRIDAY", "SATURDAY"};
64 char *directions
[]={"VRB", "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"};
67 char *unknown_option
="Unknown option";
69 #define F(a) fprintf(stderr, a "\n");
71 /***************************************************
72 * Configuration parameters
73 ***************************************************/
74 char *email_address
=NULL
;
75 char *metar_station
=NULL
;
77 char *metar_post
=NULL
;
78 char **warning_zones
=NULL
;
79 char *warning_uri
=NULL
;
80 char *warning_post
=NULL
;
81 char *avn_station
=NULL
;
84 char *eta_station
=NULL
;
87 char *mrf_station
=NULL
;
91 char *radar_post
=NULL
;
92 char *radar_crop
=NULL
;
93 char *radar_cross
=NULL
;
99 double latitude
=999, longitude
=999;
100 int start_do_animation
=1;
104 /**********************************
106 **********************************/
107 void usage(int i
) __THROW
__attribute__ ((__noreturn__
));
108 void printversion(void);
109 int readconf(char *file
);
110 int parse_option(char *option
, char *value
);
111 char *get_filename(char *file
);
113 /**********************************
115 **********************************/
118 while(waitpid(-1, NULL
, WNOHANG
)>0);
121 int parse_option(char *option
, char *value
){
126 error
=unknown_option
;
127 if(option
[0]=='-') option
++;
128 if(option
[0]=='-' && option
[1]!='\0' && option
[2]!='\0') option
++;
129 if(option
[0]=='\0') return 0;
130 if(value
!=NULL
&& value
[0]=='\0') value
=NULL
;
134 if(!strncmp(option
, "avn-", 4)){
135 if(!strcmp(option
+4, "station")){
137 error
="avn-station given with no station ID";
140 if(avn_station
!=NULL
) free(avn_station
);
141 avn_station
=strdup(value
);
142 if(avn_station
==NULL
) die("avn-station strdup");
144 } else if(!strcmp(option
+4, "uri")){
146 error
="avn-uri given with no URI";
149 if(avn_uri
!=NULL
) free(avn_uri
);
150 avn_uri
=strdup(value
);
151 if(avn_uri
==NULL
) die("avn-uri strdup");
152 if(avn_post
!=NULL
) free(avn_post
);
155 } else if(!strcmp(option
+4, "post")){
157 error
="avn-post given with no data";
161 error
="avn-post must come after avn-uri";
164 if(avn_post
!=NULL
) free(avn_post
);
165 avn_post
=strdup(value
);
166 if(avn_post
==NULL
) die("avn-post strdup");
170 } else if(!strcmp(option
, "atm")){
173 } else if(!strcmp(option
, "animate")){
174 start_do_animation
=1;
179 if(!strcmp(option
, "beaufort")){
185 if(option
[1]=='\0') return 2; /* -c handled earlier */
186 else if(!strcmp(option
, "cm")){
192 if(!strcmp(option
, "display-mode")){
194 error
="display-mode given with no mode specified";
197 if(!strcasecmp(value
, "cur") || !strcasecmp(value
, "current")){
200 } else if(!strcasecmp(value
, "fcst") || !strcasecmp(value
, "forecast")){
203 } else if(!strcasecmp(value
, "map") || !strcasecmp(value
, "radar")){
207 error
="display-mode given with unrecognized mode";
210 } else if(!strcmp(option
, "display")){
215 if(!strncmp(option
, "eta-", 4)){
216 if(!strcmp(option
+4, "station")){
218 error
="eta-station given with no station ID";
221 if(eta_station
!=NULL
) free(eta_station
);
222 eta_station
=strdup(value
);
223 if(eta_station
==NULL
) die("eta-station strdup");
225 } else if(!strcmp(option
+4, "uri")){
227 error
="eta-uri given with no URI";
230 if(eta_uri
!=NULL
) free(eta_uri
);
231 eta_uri
=strdup(value
);
232 if(eta_uri
==NULL
) die("eta-uri strdup");
233 if(eta_post
!=NULL
) free(eta_post
);
236 } else if(!strcmp(option
+4, "post")){
238 error
="eta-post given with no data";
242 error
="eta-post must come after eta-uri";
245 if(eta_post
!=NULL
) free(eta_post
);
246 eta_post
=strdup(value
);
247 if(eta_post
==NULL
) die("eta-post strdup");
251 } else if(option
[1]=='\0' || !strcmp(option
, "email")){
253 error
="-e/email given with no address";
256 if(email_address
!=NULL
) free(email_address
);
257 email_address
=strdup(value
);
258 if(email_address
==NULL
) die("email strdup");
263 if(!strncmp(option
, "forget-", 7)){
264 if(!strcmp(option
+7, "warning-zones")){
265 if(warning_zones
) free(warning_zones
);
273 if(!strcmp(option
, "hPa")){
279 if(!strcmp(option
, "inHg")){
282 } else if(!strcmp(option
, "in")){
288 if(!strcmp(option
, "kph")){
291 } else if(!strcmp(option
, "knots")){
297 if(!strcmp(option
, "location")){
299 error
="location given with no value";
302 if(!str2dd(value
, &latitude
, &longitude
)){
303 error
="location should be of the form \"dd'mm'ssN dd'mm'ssW\" or \"dd.ddddN dd.dddddW\"\n Note that, if you're using '-location' on the command line, you will need\n to quote the value, e.g. '-location \"dd.ddddN dd.dddddW\"'";
306 if(latitude
>90 || latitude
<-90 || longitude
>180 || longitude
<-180){
307 error
="latitude or longitude out of range";
314 if(option
[1]=='\0' || !strcmp(option
, "metric")){
315 pressure_mode
=windspeed_mode
=temp_mode
=length_mode
=1;
317 } else if(!strncmp(option
, "metar-", 6)){
318 if(!strcmp(option
+6, "station")){
320 error
="metar-station given with no station ID";
323 if(metar_station
!=NULL
) free(metar_station
);
324 metar_station
=strdup(value
);
325 if(metar_station
==NULL
) die("metar-station strdup");
327 } else if(!strcmp(option
+6, "uri")){
329 error
="metar-uri given with no URI";
332 if(metar_uri
!=NULL
) free(metar_uri
);
333 metar_uri
=strdup(value
);
334 if(metar_uri
==NULL
) die("metar-uri strdup");
335 if(metar_post
!=NULL
) free(metar_post
);
338 } else if(!strcmp(option
+6, "post")){
340 error
="metar-post given with no data";
344 error
="metar-post must come after metar-uri";
347 if(metar_post
!=NULL
) free(metar_post
);
348 metar_post
=strdup(value
);
349 if(metar_post
==NULL
) die("metar-post strdup");
353 } else if(!strncmp(option
, "mrf-", 4)){
354 if(!strcmp(option
+4, "station")){
356 error
="mrf-station given with no station ID";
359 if(mrf_station
!=NULL
) free(mrf_station
);
360 mrf_station
=strdup(value
);
361 if(mrf_station
==NULL
) die("mrf-station strdup");
363 } else if(!strcmp(option
+4, "uri")){
365 error
="mrf-uri given with no URI";
368 if(mrf_uri
!=NULL
) free(mrf_uri
);
369 mrf_uri
=strdup(value
);
370 if(mrf_uri
==NULL
) die("mrf-uri strdup");
371 if(mrf_post
!=NULL
) free(mrf_post
);
374 } else if(!strcmp(option
+4, "post")){
376 error
="mrf-post given with no data";
380 error
="mrf-post must come after mrf-uri";
383 if(mrf_post
!=NULL
) free(mrf_post
);
384 mrf_post
=strdup(value
);
385 if(mrf_post
==NULL
) die("mrf-post strdup");
389 } else if(!strcmp(option
, "mmHg")){
392 } else if(!strcmp(option
, "mph")){
395 } else if(!strcmp(option
, "mps")){
398 } else if(!strcmp(option
, "mbar")){
404 if(!strcmp(option
, "noradar")){
405 if(radar_uri
!=NULL
) free(radar_uri
);
408 } else if(!strcmp(option
, "noanimate")){
409 start_do_animation
=0;
414 if(!strcmp(option
, "radar")){
415 warn("'radar' is deprecated, please use 'radar-uri' instead");
416 return parse_option("radar-uri", value
);
417 } else if(!strncmp(option
, "radar-", 6)){
418 if(!strcmp(option
+6, "uri")){
420 error
="radar-uri given with no URI";
423 if(radar_uri
!=NULL
) free(radar_uri
);
424 radar_uri
=strdup(value
);
425 if(radar_uri
==NULL
) die("radar-uri strdup");
426 if(radar_post
!=NULL
) free(radar_post
);
429 } else if(!strcmp(option
+6, "post")){
431 error
="radar-post given with no data";
435 error
="radar-post must come after radar-uri";
438 if(radar_post
!=NULL
) free(radar_post
);
439 radar_post
=strdup(value
);
440 if(radar_post
==NULL
) die("radar-post strdup");
442 } else if(!strcmp(option
+6, "crop")){
444 error
="radar-crop given with no value";
447 if(radar_crop
!=NULL
) free(radar_crop
);
448 radar_crop
=strdup(value
);
449 if(radar_crop
==NULL
) die("radar-crop strdup");
451 } else if(!strcmp(option
+6, "cross")){
453 error
="radar-cross given with no value";
456 if(radar_cross
!=NULL
) free(radar_cross
);
457 radar_cross
=strdup(value
);
458 if(radar_cross
==NULL
) die("radar-cross strdup");
465 if(option
[1]=='\0' || !strcmp(option
, "station")){
467 error
="-s/station given with no value";
470 if(parse_option("metar-station", value
)==2
471 && parse_option("avn-station", value
)==2
472 && parse_option("eta-station", value
)==2
473 && parse_option("mrf-station", value
)==2)
479 if(!strcmp(option
, "tempf")){
482 } else if(!strcmp(option
, "tempc")){
488 if(option
[1]=='\0' || !strcmp(option
, "version")){
491 } else if(!strcmp(option
, "viewer")){
493 error
="viewer given with no value";
496 if(viewer
!=NULL
) free(viewer
);
497 viewer
=strdup(value
);
498 if(viewer
==NULL
) die("viewer strdup");
503 if(!strncmp(option
, "warning-", 8)){
504 if(!strcmp(option
+8, "zone")){
506 error
="warning-zone given with no zone ID";
509 if(warning_zones
!=NULL
){
510 for(i
=0; warning_zones
[i
]!=NULL
; i
++);
514 v
=realloc(warning_zones
, sizeof(*warning_zones
)*(i
+2));
515 if(v
==NULL
) die("warning-zone realloc");
517 warning_zones
[i
+1]=NULL
;
518 warning_zones
[i
]=strdup(value
);
519 if(warning_zones
[i
]==NULL
) die("warning-zone strdup");
521 } else if(!strcmp(option
+8, "uri")){
523 error
="warning-uri given with no URI";
526 if(warning_uri
!=NULL
) free(warning_uri
);
527 warning_uri
=strdup(value
);
528 if(warning_uri
==NULL
) die("warning-uri strdup");
529 if(warning_post
!=NULL
) free(warning_post
);
532 } else if(!strcmp(option
+8, "post")){
534 error
="warning-post given with no data";
537 if(warning_uri
==NULL
){
538 error
="warning-post must come after warning-uri";
541 if(warning_post
!=NULL
) free(warning_post
);
542 warning_post
=strdup(value
);
543 if(warning_post
==NULL
) die("warning-post strdup");
550 if(!strcmp(option
, "zone")){
551 warn("'zone' is deprecated, please use 'warning-zone' instead");
552 return parse_option("warning-zone", value
);
561 int readconf(char *file
){
568 file
=get_filename("conf");
571 if((fp
=fopen(file
, "r"))==NULL
){
580 while(fgets(bigbuf
, BIGBUF_LEN
, fp
)!=NULL
){
582 for(i
=strlen(bigbuf
)-1; i
>=0; i
--){
583 if (!isspace(bigbuf
[i
])) break;
586 c
=bigbuf
+strspn(bigbuf
, " \t");
587 if(*c
=='#' || *c
=='\0') continue;
588 d
=c
+strcspn(c
, " \t");
593 d
+=strspn(d
+1, " \t");
596 if(!parse_option(c
, d
)){
597 warn("%s[%d]: %s", file
, l
, error
);
602 if(!flag
) free(file
);
616 if(mkdir(c
, 0777)<0) die("Couldn't create directory %s", c
);
618 warn("Created directory %s", c
);
621 if(i
<0) die("Couldn't stat %s", c
);
623 if(!S_ISDIR(statbuf
.st_mode
)) die("%s is not a directory", c
);
625 c
=get_filename(".dir-test");
626 if(unlink(c
)<0 && errno
!=ENOENT
) die("Couldn't delete %s", c
);
627 if((i
=stat(c
, &statbuf
))!=-1 || errno
!=ENOENT
){
628 if(i
!=-1) errno
=EEXIST
;
629 die("Couldn't verify nonexistence of %s", c
);
631 if((i
=creat(c
, 0))<0) die("Couldn't create %s", c
);
633 if(stat(c
, &statbuf
)<0) die("Couldn't stat %s", c
);
643 int main(int argc
, char **argv
){
648 if((c
=strrchr(ProgName
, '/'))!=NULL
&& *(c
+1)!='\0'){
652 if((bigbuf
=malloc(BIGBUF_LEN
))==NULL
) die("bigbuf malloc");
655 devnull
=open("/dev/null", O_RDWR
);
656 if(devnull
<0) die("opening /dev/null");
657 /* Parse Command Line */
661 if(!strcmp(argv
[i
], "-c")){
664 F("-c given with no value");
671 if(readconf("/etc/wmweather+.conf")>1) exit(1);
672 if((i
=readconf(c
))==1) warn("Couldn't open %s", c
);
679 j
=parse_option(argv
[i
], (i
+1<argc
)?argv
[i
+1]:NULL
);
681 if(error
==unknown_option
) usage(1);
682 fprintf(stderr
, "%s\n", error
);
690 struct sigaction act
;
691 sigemptyset(&act
.sa_mask
);
692 act
.sa_handler
=sigchld
;
693 act
.sa_flags
=SA_RESTART
;
694 sigaction(SIGCHLD
, &act
, NULL
);
695 act
.sa_handler
=sigint
;
696 sigaction(SIGINT
, &act
, NULL
);
700 if(metar_station
==NULL
){
702 F("Please specify a METAR station.\n See http://www.nws.noaa.gov/tg/siteloc.shtml\n");
708 /* note: if time_t isn't an arithmetic type, mkgmtime is screwed
709 * anyway. So t=0 is as appropriate as anything else. */
711 longitude
=-mkgmtime(localtime(&t
))/240.0;
715 longitude
=-longitude
;
717 fprintf(stderr
, "-location not given. Guessing you're at 0N %d'%d'%d%c\n", (int)longitude
, (int)(longitude
*60)%60, (int)(longitude
*3600)%60, flag
?'E':'W');
718 if(flag
) longitude
=-longitude
;
719 }} else if(latitude
>89.8){
720 F("Latitude greater then 89.9N automatically truncated.\n");
722 } else if(latitude
<-89.8){
723 F("Latitude greater then 89.9S automatically truncated.\n");
727 if(viewer
==NULL
) viewer
="xless";
728 if(metar_uri
==NULL
) metar_uri
="http://weather.noaa.gov/pub/data/observations/metar/stations/%s.TXT";
729 if(avn_uri
==NULL
) avn_uri
="http://www.nws.noaa.gov/cgi-bin/mos/getmav.pl?sta=%s";
730 if(eta_uri
==NULL
) eta_uri
="http://www.nws.noaa.gov/cgi-bin/mos/getmet.pl?sta=%s";
731 if(mrf_uri
==NULL
) mrf_uri
="http://www.nws.noaa.gov/cgi-bin/mos/getmex.pl?sta=%s";
732 if(warning_uri
==NULL
) warning_uri
="http://weather.noaa.gov/pub/data/watches_warnings/%f/%.2z/%z.txt";
734 download_init(email_address
);
735 init_dock(argc
, argv
);
738 download_process(100000);
743 F("Option Value Description");
744 F("------ ----- -----------");
745 F("-c <file> Specify a configuration file");
746 F("-e <address> Alias for -email");
747 F("-email <address> Specify anonymous FTP password");
748 F("-location <lat+lon> Specify latitude and longitude. See manpage.");
749 F("-v Alias for -version");
750 F("-version Display version number");
751 F("-viewer <program> Program to display text from stdin");
752 F("-[no]animate Turn animation on or off");
754 F("-s <ID> Alias for -station");
755 F("-station <ID> Station ID (all stations)");
756 F("-metar-station <ID> Station ID for METAR observations");
757 F("-avn-station <ID> Station ID for AVN forecasts");
758 F("-eta-station <ID> Station ID for ETA forecasts");
759 F("-mrf-station <ID> Station ID for MRF forecasts");
760 F("-warning-zone <zoneID> Zone ID for weather warnings");
761 F("-*-uri <URI> URI for the weather data (see docs for details)");
762 F("-*-post <DATA> Post data for the weather data (see docs)");
763 F(" '*' can be metar, avn, eta, mrf, warning");
764 F("-noradar Do not display a radar image.");
765 F("-radar-uri <URI> URI for radar image");
766 F("-radar-post <DATA> Post data for radar image");
767 F("-radar-crop <string> How to crop the radar image. XxY+W+H format.");
768 F("-radar-cross <string> Where to draw radar crosshairs. XxY format.");
770 F("-m Alias for -metric");
771 F("-metric Same as -cm -hPa -kph -tempc");
773 F("-in Display precipitation amounts in inches");
774 F("-cm Display precipitation amounts in centimeters");
776 F("-inHg Display pressure in inHg");
777 F("-hPa Display pressure in hPa (millibars)");
778 F("-mbar Alias for -hPa");
779 F("-mmHg Display pressure in mmHg");
780 F("-atm Display pressure in atmospheres");
782 F("-mph Display windspeed in miles/hour");
783 F("-kph Display windspeed in kilometers/hour");
784 F("-knots Display windspeed in knots");
785 F("-mps Display windspeed in meters/second");
786 F("-beaufort Display windspeed on the Beaufort scale");
788 F("-tempf Display temperature in degrees Fahrenheit");
789 F("-tempc Display temperature in degrees Celcius");
794 void printversion(void) {
795 fprintf(stderr
, "%s\n", VERSION
);
798 char *get_filename(char *file
){
802 if((f
=malloc(strlen(c
)+strlen(file
)+14))==NULL
) die("get_filename");
804 strcat(f
, "/.wmweather+/");
809 char *get_pid_filename(char *file
){
811 static unsigned short seq
=0;
814 snprintf(buf
, sizeof(buf
), "%08X.%04X-", getpid(), seq
++);
816 if((f
=malloc(strlen(c
)+strlen(file
)+14+14))==NULL
) die("get_pid_filename");
818 strcat(f
, "/.wmweather+/");