#include "config.h" /* Copyright (C) 2002 Brad Jorsch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #if TM_IN_SYS_TIME # if TIME_WITH_SYS_TIME # include # include # else # if HAVE_SYS_TIME_H # include # else # include # endif # endif #else #include #endif #include #include #include #include "wmweather+.h" #include "convert.h" #include "download.h" #include "dock.h" #include "die.h" #include "animation.h" char *ProgName; char *bigbuf; int devnull; char *monthnames[]={ "", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; char *monthnames2[]={ "", "", "", "", "", "", "JUNE", "JULY", "", "SEPT", "", "", "" }; char *wdaynames[]={ "SUNDAY", "MONDAY", "TUESDAY", "WEDN'SDAY", "THURSDAY", "FRIDAY", "SATURDAY"}; char *directions[]={"VRB", "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; char *error; char *unknown_option="Unknown option"; #define F(a) fprintf(stderr, a "\n"); /*************************************************** * Configuration parameters ***************************************************/ char *email_address=NULL; char *metar_station=NULL; char *metar_uri=NULL; char *metar_post=NULL; char **warning_zones=NULL; char *warning_uri=NULL; char *warning_post=NULL; char *avn_station=NULL; char *avn_uri=NULL; char *avn_post=NULL; char *eta_station=NULL; char *eta_uri=NULL; char *eta_post=NULL; char *mrf_station=NULL; char *mrf_uri=NULL; char *mrf_post=NULL; char *radar_uri=NULL; char *radar_post=NULL; char *radar_crop=NULL; char *radar_cross=NULL; char *viewer=NULL; int pressure_mode=0; int windspeed_mode=0; int temp_mode=0; int length_mode=0; double latitude=999, longitude=999; int start_do_animation=1; int starting_mode=0; /********************************** * Prototypes **********************************/ void usage(int i) __THROW __attribute__ ((__noreturn__)); void printversion(void); int readconf(char *file); int parse_option(char *option, char *value); char *get_filename(char *file); /********************************** * Functions **********************************/ void sigchld(int i){ while(waitpid(-1, NULL, WNOHANG)>0); } int parse_option(char *option, char *value){ int i; void *v; errno=0; error=unknown_option; if(option[0]=='-') option++; if(option[0]=='-' && option[1]!='\0' && option[2]!='\0') option++; if(option[0]=='\0') return 0; if(value!=NULL && value[0]=='\0') value=NULL; switch (option[0]){ case 'a': if(!strncmp(option, "avn-", 4)){ if(!strcmp(option+4, "station")){ if(value==NULL){ error="avn-station given with no station ID"; return 0; } if(avn_station!=NULL) free(avn_station); avn_station=strdup(value); if(avn_station==NULL) die("avn-station strdup"); return 2; } else if(!strcmp(option+4, "uri")){ if(value==NULL){ error="avn-uri given with no URI"; return 0; } if(avn_uri!=NULL) free(avn_uri); avn_uri=strdup(value); if(avn_uri==NULL) die("avn-uri strdup"); if(avn_post!=NULL) free(avn_post); avn_post=NULL; return 2; } else if(!strcmp(option+4, "post")){ if(value==NULL){ error="avn-post given with no data"; return 0; } if(avn_uri==NULL){ error="avn-post must come after avn-uri"; return 0; } if(avn_post!=NULL) free(avn_post); avn_post=strdup(value); if(avn_post==NULL) die("avn-post strdup"); return 2; } break; } else if(!strcmp(option, "atm")){ pressure_mode=3; return 1; } else if(!strcmp(option, "animate")){ start_do_animation=1; return 1; } break; case 'b': if(!strcmp(option, "beaufort")){ windspeed_mode=4; return 1; } break; case 'c': if(option[1]=='\0') return 2; /* -c handled earlier */ else if(!strcmp(option, "cm")){ length_mode=1; return 1; } break; case 'd': if(!strcmp(option, "display-mode")){ if(value==NULL){ error="display-mode given with no mode specified"; return 0; } if(!strcasecmp(value, "cur") || !strcasecmp(value, "current")){ starting_mode=0; return 2; } else if(!strcasecmp(value, "fcst") || !strcasecmp(value, "forecast")){ starting_mode=1; return 2; } else if(!strcasecmp(value, "map") || !strcasecmp(value, "radar")){ starting_mode=2; return 2; } else { error="display-mode given with unrecognized mode"; return 0; } } else if(!strcmp(option, "display")){ return 1; } break; case 'e': if(!strncmp(option, "eta-", 4)){ if(!strcmp(option+4, "station")){ if(value==NULL){ error="eta-station given with no station ID"; return 0; } if(eta_station!=NULL) free(eta_station); eta_station=strdup(value); if(eta_station==NULL) die("eta-station strdup"); return 2; } else if(!strcmp(option+4, "uri")){ if(value==NULL){ error="eta-uri given with no URI"; return 0; } if(eta_uri!=NULL) free(eta_uri); eta_uri=strdup(value); if(eta_uri==NULL) die("eta-uri strdup"); if(eta_post!=NULL) free(eta_post); eta_post=NULL; return 2; } else if(!strcmp(option+4, "post")){ if(value==NULL){ error="eta-post given with no data"; return 0; } if(eta_uri==NULL){ error="eta-post must come after eta-uri"; return 0; } if(eta_post!=NULL) free(eta_post); eta_post=strdup(value); if(eta_post==NULL) die("eta-post strdup"); return 2; } break; } else if(option[1]=='\0' || !strcmp(option, "email")){ if(value==NULL){ error="-e/email given with no address"; return 0; } if(email_address!=NULL) free(email_address); email_address=strdup(value); if(email_address==NULL) die("email strdup"); return 2; } break; case 'f': if(!strncmp(option, "forget-", 7)){ if(!strcmp(option+7, "warning-zones")){ if(warning_zones) free(warning_zones); warning_zones=NULL; return 1; } break; } break; case 'h': if(!strcmp(option, "hPa")){ pressure_mode=1; return 1; } break; case 'i': if(!strcmp(option, "inHg")){ pressure_mode=0; return 1; } else if(!strcmp(option, "in")){ length_mode=0; return 1; } break; case 'k': if(!strcmp(option, "kph")){ windspeed_mode=1; return 1; } else if(!strcmp(option, "knots")){ windspeed_mode=2; return 1; } break; case 'l': if(!strcmp(option, "location")){ if(value==NULL){ error="location given with no value"; return 0; } if(!str2dd(value, &latitude, &longitude)){ 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\"'"; return 0; } if(latitude>90 || latitude<-90 || longitude>180 || longitude<-180){ error="latitude or longitude out of range"; return 0; } return 2; } break; case 'm': if(option[1]=='\0' || !strcmp(option, "metric")){ pressure_mode=windspeed_mode=temp_mode=length_mode=1; return 1; } else if(!strncmp(option, "metar-", 6)){ if(!strcmp(option+6, "station")){ if(value==NULL){ error="metar-station given with no station ID"; return 0; } if(metar_station!=NULL) free(metar_station); metar_station=strdup(value); if(metar_station==NULL) die("metar-station strdup"); return 2; } else if(!strcmp(option+6, "uri")){ if(value==NULL){ error="metar-uri given with no URI"; return 0; } if(metar_uri!=NULL) free(metar_uri); metar_uri=strdup(value); if(metar_uri==NULL) die("metar-uri strdup"); if(metar_post!=NULL) free(metar_post); metar_post=NULL; return 2; } else if(!strcmp(option+6, "post")){ if(value==NULL){ error="metar-post given with no data"; return 0; } if(metar_uri==NULL){ error="metar-post must come after metar-uri"; return 0; } if(metar_post!=NULL) free(metar_post); metar_post=strdup(value); if(metar_post==NULL) die("metar-post strdup"); return 2; } break; } else if(!strncmp(option, "mrf-", 4)){ if(!strcmp(option+4, "station")){ if(value==NULL){ error="mrf-station given with no station ID"; return 0; } if(mrf_station!=NULL) free(mrf_station); mrf_station=strdup(value); if(mrf_station==NULL) die("mrf-station strdup"); return 2; } else if(!strcmp(option+4, "uri")){ if(value==NULL){ error="mrf-uri given with no URI"; return 0; } if(mrf_uri!=NULL) free(mrf_uri); mrf_uri=strdup(value); if(mrf_uri==NULL) die("mrf-uri strdup"); if(mrf_post!=NULL) free(mrf_post); mrf_post=NULL; return 2; } else if(!strcmp(option+4, "post")){ if(value==NULL){ error="mrf-post given with no data"; return 0; } if(mrf_uri==NULL){ error="mrf-post must come after mrf-uri"; return 0; } if(mrf_post!=NULL) free(mrf_post); mrf_post=strdup(value); if(mrf_post==NULL) die("mrf-post strdup"); return 2; } break; } else if(!strcmp(option, "mmHg")){ pressure_mode=2; return 1; } else if(!strcmp(option, "mph")){ windspeed_mode=0; return 1; } else if(!strcmp(option, "mps")){ windspeed_mode=3; return 1; } else if(!strcmp(option, "mbar")){ pressure_mode=1; return 1; } break; case 'n': if(!strcmp(option, "noradar")){ if(radar_uri!=NULL) free(radar_uri); radar_uri=NULL; return 1; } else if(!strcmp(option, "noanimate")){ start_do_animation=0; return 1; } break; case 'r': if(!strcmp(option, "radar")){ warn("'radar' is deprecated, please use 'radar-uri' instead"); return parse_option("radar-uri", value); } else if(!strncmp(option, "radar-", 6)){ if(!strcmp(option+6, "uri")){ if(value==NULL){ error="radar-uri given with no URI"; return 0; } if(radar_uri!=NULL) free(radar_uri); radar_uri=strdup(value); if(radar_uri==NULL) die("radar-uri strdup"); if(radar_post!=NULL) free(radar_post); radar_post=NULL; return 2; } else if(!strcmp(option+6, "post")){ if(value==NULL){ error="radar-post given with no data"; return 0; } if(radar_uri==NULL){ error="radar-post must come after radar-uri"; return 0; } if(radar_post!=NULL) free(radar_post); radar_post=strdup(value); if(radar_post==NULL) die("radar-post strdup"); return 2; } else if(!strcmp(option+6, "crop")){ if(value==NULL){ error="radar-crop given with no value"; return 0; } if(radar_crop!=NULL) free(radar_crop); radar_crop=strdup(value); if(radar_crop==NULL) die("radar-crop strdup"); return 2; } else if(!strcmp(option+6, "cross")){ if(value==NULL){ error="radar-cross given with no value"; return 0; } if(radar_cross!=NULL) free(radar_cross); radar_cross=strdup(value); if(radar_cross==NULL) die("radar-cross strdup"); return 2; } break; } break; case 's': if(option[1]=='\0' || !strcmp(option, "station")){ if(value==NULL){ error="-s/station given with no value"; return 0; } if(parse_option("metar-station", value)==2 && parse_option("avn-station", value)==2 && parse_option("eta-station", value)==2 && parse_option("mrf-station", value)==2) return 2; return 0; } break; case 't': if(!strcmp(option, "tempf")){ temp_mode=0; return 1; } else if(!strcmp(option, "tempc")){ temp_mode=1; return 1; } break; case 'v': if(option[1]=='\0' || !strcmp(option, "version")){ printversion(); exit(0); } else if(!strcmp(option, "viewer")){ if(value==NULL){ error="viewer given with no value"; return 0; } if(viewer!=NULL) free(viewer); viewer=strdup(value); if(viewer==NULL) die("viewer strdup"); return 2; } break; case 'w': if(!strncmp(option, "warning-", 8)){ if(!strcmp(option+8, "zone")){ if(value==NULL){ error="warning-zone given with no zone ID"; return 0; } if(warning_zones!=NULL){ for(i=0; warning_zones[i]!=NULL; i++); } else { i=0; } v=realloc(warning_zones, sizeof(*warning_zones)*(i+2)); if(v==NULL) die("warning-zone realloc"); warning_zones=v; warning_zones[i+1]=NULL; warning_zones[i]=strdup(value); if(warning_zones[i]==NULL) die("warning-zone strdup"); return 2; } else if(!strcmp(option+8, "uri")){ if(value==NULL){ error="warning-uri given with no URI"; return 0; } if(warning_uri!=NULL) free(warning_uri); warning_uri=strdup(value); if(warning_uri==NULL) die("warning-uri strdup"); if(warning_post!=NULL) free(warning_post); warning_post=NULL; return 2; } else if(!strcmp(option+8, "post")){ if(value==NULL){ error="warning-post given with no data"; return 0; } if(warning_uri==NULL){ error="warning-post must come after warning-uri"; return 0; } if(warning_post!=NULL) free(warning_post); warning_post=strdup(value); if(warning_post==NULL) die("warning-post strdup"); return 2; } break; } break; case 'z': if(!strcmp(option, "zone")){ warn("'zone' is deprecated, please use 'warning-zone' instead"); return parse_option("warning-zone", value); } break; default: break; } return 0; } int readconf(char *file){ char *c, *d; int i, l, flag=1; FILE *fp; if(file==NULL){ flag=0; file=get_filename("conf"); } if((fp=fopen(file, "r"))==NULL){ if(!flag){ free(file); return 0; } return 1; } l=0; while(fgets(bigbuf, BIGBUF_LEN, fp)!=NULL){ l++; for(i=strlen(bigbuf)-1; i>=0; i--){ if (!isspace(bigbuf[i])) break; bigbuf[i]='\0'; } c=bigbuf+strspn(bigbuf, " \t"); if(*c=='#' || *c=='\0') continue; d=c+strcspn(c, " \t"); if(*d=='\0') d=NULL; else { *d='\0'; d++; d+=strspn(d+1, " \t"); if(*d=='\0') d=NULL; } if(!parse_option(c, d)){ warn("%s[%d]: %s", file, l, error); fclose(fp); if(!flag) free(file); return 2; } } fclose(fp); if(!flag) free(file); return 0; } int check_dir(void){ char *c; struct stat statbuf; int i; c=get_filename(""); i=stat(c, &statbuf); if(i<0){ if(errno==ENOENT){ if(mkdir(c, 0777)<0) die("Couldn't create directory %s", c); errno=0; warn("Created directory %s", c); i=stat(c, &statbuf); } if(i<0) die("Couldn't stat %s", c); } if(!S_ISDIR(statbuf.st_mode)) die("%s is not a directory", c); free(c); c=get_filename(".dir-test"); if(unlink(c)<0 && errno!=ENOENT) die("Couldn't delete %s", c); if((i=stat(c, &statbuf))!=-1 || errno!=ENOENT){ if(i!=-1) errno=EEXIST; die("Couldn't verify nonexistence of %s", c); } if((i=creat(c, 0))<0) die("Couldn't create %s", c); close(i); if(stat(c, &statbuf)<0) die("Couldn't stat %s", c); unlink(c); free(c); return 1; } void sigint(int i){ exit(0); } int main(int argc, char **argv){ int i, j; char *c; ProgName = argv[0]; if((c=strrchr(ProgName, '/'))!=NULL && *(c+1)!='\0'){ ProgName=c+1; } if((bigbuf=malloc(BIGBUF_LEN))==NULL) die("bigbuf malloc"); check_dir(); devnull=open("/dev/null", O_RDWR); if(devnull<0) die("opening /dev/null"); /* Parse Command Line */ c=NULL; for(i=1;i1) exit(1); if((i=readconf(c))==1) warn("Couldn't open %s", c); if(i) exit(1); for(i=1;i89.8){ F("Latitude greater then 89.9N automatically truncated.\n"); latitude=89.8; } else if(latitude<-89.8){ F("Latitude greater then 89.9S automatically truncated.\n"); latitude=-89.8; } if(i) exit(1); if(viewer==NULL) viewer="xless"; if(metar_uri==NULL) metar_uri="http://tgftp.nws.noaa.gov/data/observations/metar/stations/%s.TXT"; if(avn_uri==NULL) avn_uri="http://www.nws.noaa.gov/cgi-bin/mos/getmav.pl?sta=%s"; if(eta_uri==NULL) eta_uri="http://www.nws.noaa.gov/cgi-bin/mos/getmet.pl?sta=%s"; if(mrf_uri==NULL) mrf_uri="http://www.nws.noaa.gov/cgi-bin/mos/getmex.pl?sta=%s"; if(warning_uri==NULL) warning_uri="http://tgftp.nws.noaa.gov/data/watches_warnings/%f/%.2z/%z.txt"; download_init(email_address); init_dock(argc, argv); while(1){ update_dock(); download_process(100000); } } void usage(int i) { F("Option Value Description"); F("------ ----- -----------"); F("-c Specify a configuration file"); F("-e
Alias for -email"); F("-email
Specify anonymous FTP password"); F("-location Specify latitude and longitude. See manpage."); F("-v Alias for -version"); F("-version Display version number"); F("-viewer Program to display text from stdin"); F("-[no]animate Turn animation on or off"); F(""); F("-s Alias for -station"); F("-station Station ID (all stations)"); F("-metar-station Station ID for METAR observations"); F("-avn-station Station ID for AVN forecasts"); F("-eta-station Station ID for ETA forecasts"); F("-mrf-station Station ID for MRF forecasts"); F("-warning-zone Zone ID for weather warnings"); F("-*-uri URI for the weather data (see docs for details)"); F("-*-post Post data for the weather data (see docs)"); F(" '*' can be metar, avn, eta, mrf, warning"); F("-noradar Do not display a radar image."); F("-radar-uri URI for radar image"); F("-radar-post Post data for radar image"); F("-radar-crop How to crop the radar image. XxY+W+H format."); F("-radar-cross Where to draw radar crosshairs. XxY format."); F(""); F("-m Alias for -metric"); F("-metric Same as -cm -hPa -kph -tempc"); F(""); F("-in Display precipitation amounts in inches"); F("-cm Display precipitation amounts in centimeters"); F(""); F("-inHg Display pressure in inHg"); F("-hPa Display pressure in hPa (millibars)"); F("-mbar Alias for -hPa"); F("-mmHg Display pressure in mmHg"); F("-atm Display pressure in atmospheres"); F(""); F("-mph Display windspeed in miles/hour"); F("-kph Display windspeed in kilometers/hour"); F("-knots Display windspeed in knots"); F("-mps Display windspeed in meters/second"); F("-beaufort Display windspeed on the Beaufort scale"); F(""); F("-tempf Display temperature in degrees Fahrenheit"); F("-tempc Display temperature in degrees Celcius"); exit(i); } void printversion(void) { fprintf(stderr, "%s\n", VERSION); } char *get_filename(char *file){ char *f, *c; c=getenv("HOME"); if((f=malloc(strlen(c)+strlen(file)+14))==NULL) die("get_filename"); strcpy(f, c); strcat(f, "/.wmweather+/"); strcat(f, file); return f; } char *get_pid_filename(char *file){ char *f, *c; static unsigned short seq=0; char buf[15]; snprintf(buf, sizeof(buf), "%08X.%04X-", getpid(), seq++); c=getenv("HOME"); if((f=malloc(strlen(c)+strlen(file)+14+14))==NULL) die("get_pid_filename"); strcpy(f, c); strcat(f, "/.wmweather+/"); strcat(f, buf); strcat(f, file); return f; }