Imported from archive. 1.4
authorJeremy Stanley <fungi@yuggoth.org>
Sun, 13 Jul 2008 07:49:02 +0000 (07:49 +0000)
committerJeremy Stanley <fungi@yuggoth.org>
Sun, 13 Jul 2008 07:49:02 +0000 (07:49 +0000)
* Release 1.4.

* (all): Updated the copyright years for 2008 on some of the files
in the current release and added a copyright statement to any files
previously lacking one.

* LICENSE: Replaced the previous BSD-like license with the one used
by the OpenBSD project (modeled after the Internet Software
Consortium's, a two-clause BSD license removing language made
unnecessary by the Berne convention); this new license is
functionally identical to the old one, just more terse and openly
recognized.

* weather: Clarified function parameters in calls from the wrapper
script to ease future ABI changes in the underlying module.

* weather, weather.py: Some extra comments were added to the source,
indentation style was updated from tab characters to three-space,
and lines longer than 79 columns were refactored or otherwise split.

* weather.1, weather.5, weather.py: Added an flines option to allow
the maximum number of forecast output lines to be shortened. Added
furl and murl options to allow overriding of the default current
conditions and forecast data retrieval URLs. Added a headers option
to allow overriding the default list of header names for current
conditions data filtering. Added a quiet option to suppress the
preamble lines and indentation for both current conditions and
forecast output.

* weather.py: Replaced the hardcoded fallback default METAR station
ID and forecast city/state abbreviation with error messages to
minimize confusion when necessary values are omitted. Adjusted a
couple of hard-coded error message strings to be consistent with the
output format of the option_parser module. Switched from urllib to
urllib2 for retrieving data, providing a simpler means to detect and
report retrieval errors. Upped the version to 1.4.

FAQ
INSTALL
LICENSE
README
weather
weather.1
weather.py
weatherrc
weatherrc.5

diff --git a/FAQ b/FAQ
index ce7f213..d8cc569 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -1,7 +1,9 @@
 FREQUENTLY ASKED QUESTIONS ABOUT THE WEATHER UTILITY
 
-Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
-Licensed per terms in the LICENSE file distributed with this software.
+Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
+Permission to use, copy, modify, and distribute this software is
+granted under terms provided in the LICENSE file distributed with
+this software.
 
 
 Table of Contents:
@@ -44,8 +46,10 @@ state abbreviation to get to a list of cities in that state).
 4. I live outside the USA--can this be made to work for me
 anyway?
 
-If you have any recommendations for similar forecast data in
-other countries, I will be happy to try and find a way to
+METAR station IDs can be found for cities and airports worldwide,
+but forecast data is harder to come by. If you have any
+recommendations of forecast data for other countries available in a
+format like NOAA's, I will be happy to try and find a way to
 integrate it into the weather utility, but I suspect that some
 serious modification would be necessary given that the data is
 likely to be published in a non-English language, requiring some
@@ -59,10 +63,9 @@ The -i or --id switch (or the id parameter in an alias definition),
 only tells weather(1) what current conditions to retrieve. If you
 specify -f or --forecast on the command line (or forecast=True in
 an alias) without providing a city name and state abbreviation
-(-c/--city and -s/--st, or city and st in an alias), you will
-instead see the forecast for the built-in default location (or the
-city and st defined in the default alias, if you have one). See
-question 3 above for information on figuring out what city name and
-state abbreviation to use, and the manual for weatherrc(5) for
-information on defining aliases.
-
+(-c/--city and -s/--st, or city and st in an alias) and are seeing
+an actual forecast, then you probably have a default city and state
+abbreviation set in your config. See question 3 above for
+information on figuring out what city name and state abbreviation
+to use, and the manual for weatherrc(5) for information on defining
+aliases.
diff --git a/INSTALL b/INSTALL
index c95c989..da831b6 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,7 +1,9 @@
 BASIC UNIX INSTALLATION INSTRUCTIONS FOR THE WEATHER UTILITY
 
-Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
-Licensed per terms in the LICENSE file distributed with this software.
+Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
+Permission to use, copy, modify, and distribute this software is
+granted under terms provided in the LICENSE file distributed with
+this software.
 
 
 PREREQUISITES
@@ -55,4 +57,3 @@ MANUALS
 Optionally, the weather.1 and weatherrc.5 files can be placed in
 sane locations for TROFF/NROFF manual files on your system (for
 example, /usr/local/share/man/ or ~/man/).
-
diff --git a/LICENSE b/LICENSE
index 1f63def..87142ff 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,22 +1,13 @@
-Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-   - Redistributions of source code must retain the above copyright notice,
-     this list of conditions and the following disclaimer.
-   - Redistributions in binary form must reproduce the above copyright notice,
-     this list of conditions and the following disclaimer in the documentation
-     and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
+Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README b/README
index 29baf3d..a564e31 100644 (file)
--- a/README
+++ b/README
@@ -1,7 +1,9 @@
 GENERAL INFORMATION ABOUT THE WEATHER UTILITY
 
-Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
-Licensed per terms in the LICENSE file distributed with this software.
+Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
+Permission to use, copy, modify, and distribute this software is
+granted under terms provided in the LICENSE file distributed with
+this software.
 
 
 WHAT?
@@ -10,24 +12,24 @@ This command-line utility is intended to provide quick access to
 current weather conditions and forecasts. Presently, it is
 capable of returning data for localities throughout the USA by
 retrieving and formatting decoded METARs (Meteorological
-Aerodrome Reports) from NOAA (the National Oceanic and
-Atmospheric Administration) and forecasts from the NWS (National
-Weather Service). The tool is written to function in the same
-spirit as other command-line informational utilities like cal(1),
-calendar(1) and dict(1). It can retrieve arbitrary weather data
-via specific command-line switches (station ID, city, state), or
-aliases can be configured system wide and on a per-user basis. It
-can be freely used and redistributed under the terms of the BSD
-License.
+Aerodrome Reports) from NOAA (the USA National Oceanic and
+Atmospheric Administration) and forecasts from NWS (the USA
+National Weather Service). The tool is written to function in the
+same spirit as other command-line informational utilities like
+cal(1), calendar(1) and dict(1). It can retrieve arbitrary weather
+data via specific command-line switches (station ID, city, state),
+or aliases can be configured system wide and on a per-user basis.
+It can be freely used and redistributed under the terms of a
+BSD-like License.
 
 
 WHY?
 
-My girlfriend has a long commute to/from work and school, and
-often wants to check the weather both for home and her office.
+My girlfriend had a long commute to/from work and school, and
+often wanted to check the weather both for home and her office.
 Unfortunately, starting a Web browser, pulling up a weather site,
 entering multiple ZIP codes and waiting for them to load is
-time-consuming for the marginally-impatient. Since she tends to
+time-consuming for the marginally-impatient. Since she tended to
 stay logged into a shell server most of the time, I figured I'd
 install a quick command-line tool to retrieve weather info for
 her commute, but to my surprise, a quick search turned up little
@@ -47,6 +49,5 @@ be had here:
 
    http://fungi.yuggoth.org/weather/src/
 
-Alternatively, Debian Etch (testing) and Sid (unstable) users can
-install the weather-util package from any mirror.
-
+Alternatively, Debian users can install the weather-util package
+from any mirror.
diff --git a/weather b/weather
index 29ba2c3..5e620b8 100755 (executable)
--- a/weather
+++ b/weather
@@ -1,16 +1,18 @@
 #!/usr/bin/env python
 
-# weather version 1.3, http://fungi.yuggoth.org/weather/
-# Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
-# Licensed per terms in the LICENSE file distributed with this software.
+# weather version 1.4, http://fungi.yuggoth.org/weather/
+# Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
+# Permission to use, copy, modify, and distribute this software is
+# granted under terms provided in the LICENSE file distributed with
+# this software.
 
 """Wrapper utility using the weather.py module."""
 
 # added so distributors can consistently specify a private module location
 private_module_path = None
 if private_module_path:
-       import sys
-       sys.path.insert(1, private_module_path)
+   import sys
+   sys.path.insert(1, private_module_path)
 
 import weather
 
@@ -24,17 +26,22 @@ if get_bool("list"): print weather.list_aliases(selections.config)
 
 # normal operation
 else:
-       for argument in selections.arguments:
-               if get_bool("conditions", argument):
-                       print weather.get_metar(
-                               get("id", argument),
-                               get_bool("verbose", argument)
-                               )
-               if not get_bool("conditions", argument) \
-                       or get_bool("forecast", argument):
-                       print weather.get_forecast(
-                               get("city", argument),
-                               get("st", argument),
-                               get_bool("verbose", argument)
-                               )
-
+   for argument in selections.arguments:
+      if get_bool("conditions", argument):
+         print weather.get_metar(
+            id=get("id", argument),
+            verbose=get_bool("verbose", argument),
+            quiet=get_bool("quiet", argument),
+            headers=get("headers", argument),
+            murl=get("murl", argument)
+            )
+      if not get_bool("conditions", argument) \
+         or get_bool("forecast", argument):
+         print weather.get_forecast(
+            city=get("city", argument),
+            st=get("st", argument),
+            verbose=get_bool("verbose", argument),
+            quiet=get_bool("quiet", argument),
+            flines=get("flines", argument),
+            furl=get("furl", argument)
+            )
index 6e03574..171a5c4 100644 (file)
--- a/weather.1
+++ b/weather.1
@@ -1,6 +1,8 @@
-.TH WEATHER 1 "March 26, 2006" "" \" -*- nroff -*-
-\" Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
-\" Licensed per terms in the LICENSE file distributed with this software.
+.TH WEATHER 1 "July 13, 2008" "" \" -*- nroff -*-
+\" Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
+\" Permission to use, copy, modify, and distribute this software is
+\" granted under terms provided in the LICENSE file distributed with
+\" this software.
 .SH NAME
 weather \- command\-line tool to obtain weather conditions and forecasts
 .SH SYNOPSIS
@@ -30,26 +32,41 @@ show a help message and exit
 .B \-cCITY, \-\-city=CITY
 the city name (ex: "Raleigh Durham")
 .TP
+.B \-\-flines=FLINES
+maximum number of forecast lines to show
+.TP
 .B \-f, \-\-forecast
 include a local forecast
 .TP
+.B \-\-furl=FURL
+forecast URL (including %city% and %st%)
+.TP
+.B \-\-headers=HEADERS
+the conditions headers to display
+.TP
 .B \-iID, \-\-id=ID
 the METAR station ID (ex: KRDU)
 .TP
 .B \-l, \-\-list
 print a list of configured aliases
 .TP
+.B \-\-murl=MURL
+METAR URL (including %id%)
+.TP
 .B \-n, \-\-no\-conditions
 disable output of current conditions (forces \-f)
 .TP
 .B \-o, \-\-omit\-forecast
 omit the local forecast (cancels \-f)
 .TP
+.B \-\-quiet
+skip preambles and don't indent
+.TP
 .B \-sST, \-\-st=ST
 the state abbreviation (ex: NC)
 .TP
 .B \-v, \-\-verbose
-show full decoded feeds
+show full decoded feeds (cancels \-q)
 .SH FILES
 .B weather
 may additionally obtain configuration data from a system\-wide
index 3ea1f54..20c9d5c 100644 (file)
-# weather.py version 1.3, http://fungi.yuggoth.org/weather/
-# Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
-# Licensed per terms in the LICENSE file distributed with this software.
+# weather.py version 1.4, http://fungi.yuggoth.org/weather/
+# Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
+# Permission to use, copy, modify, and distribute this software is
+# granted under terms provided in the LICENSE file distributed with
+# this software.
 
 """Contains various object definitions needed by the weather utility."""
 
-version = "1.3"
+version = "1.4"
 
 class Selections:
-       """An object to contain selection data."""
-       def __init__(self):
-               """Store the config, options and arguments."""
-               self.config = get_config()
-               self.options, self.arguments = get_options(self.config)
-               if self.arguments:
-                       self.arguments = [(x.lower()) for x in self.arguments]
-               else: self.arguments = [ None ]
-       def get(self, option, argument=None):
-               """Retrieve data from the config or options."""
-               if not argument: return self.options.__dict__[option]
-               elif not self.config.has_section(argument):
-                       import sys
-                       sys.stderr.write("ERROR: no alias defined for " \
-                               + argument + "\n")
-                       sys.exit(1)
-               elif self.config.has_option(argument, option):
-                       return self.config.get(argument, option)
-               else: return self.options.__dict__[option]
-       def get_bool(self, option, argument=None):
-               """Get data and coerce to a boolean if necessary."""
-               return bool(self.get(option, argument))
+   """An object to contain selection data."""
+   def __init__(self):
+      """Store the config, options and arguments."""
+      self.config = get_config()
+      self.options, self.arguments = get_options(self.config)
+      if self.arguments:
+         self.arguments = [(x.lower()) for x in self.arguments]
+      else: self.arguments = [ None ]
+   def get(self, option, argument=None):
+      """Retrieve data from the config or options."""
+      if not argument: return self.options.__dict__[option]
+      elif not self.config.has_section(argument):
+         import sys
+         sys.stderr.write("weather: error: no alias defined for " \
+            + argument + "\n")
+         sys.exit(1)
+      elif self.config.has_option(argument, option):
+         return self.config.get(argument, option)
+      else: return self.options.__dict__[option]
+   def get_bool(self, option, argument=None):
+      """Get data and coerce to a boolean if necessary."""
+      return bool(self.get(option, argument))
 
 def bool(data):
-       """Coerce data to a boolean value."""
-       if type(data) is str:
-               if eval(data): return True
-               else: return False
-       else:
-               if data: return True
-               else: return False
+   """Coerce data to a boolean value."""
+   if type(data) is str:
+      if eval(data): return True
+      else: return False
+   else:
+      if data: return True
+      else: return False
 
 def quote(words):
-       """Wrap a string in quotes if it contains spaces."""
-       if words.find(" ") != -1: words = "\"" + words + "\""
-       return words
+   """Wrap a string in quotes if it contains spaces."""
+   if words.find(" ") != -1: words = "\"" + words + "\""
+   return words
 
 def sorted(data):
-       """Return a sorted copy of a list."""
-       new_copy = data[:]
-       new_copy.sort()
-       return new_copy
+   """Return a sorted copy of a list."""
+   new_copy = data[:]
+   new_copy.sort()
+   return new_copy
 
 def get_url(url):
-       """Return a string containing the results of a URL GET."""
-       import urllib
-       return urllib.urlopen(url).read()
-
-def get_metar(id, verbose=False):
-       """Return a summarized METAR for the specified station."""
-       metar = get_url(
-               "http://weather.noaa.gov/pub/data/observations/metar/decoded/" \
-                       + id.upper() + ".TXT")
-       if verbose: return metar
-       else:
-               lines = metar.split("\n")
-               headings = [
-                       "Relative Humidity",
-                       "Precipitation last hour",
-                       "Sky conditions",
-                       "Temperature",
-                       "Weather",
-                       "Wind" 
-                       ]
-               output = []
-               output.append("Current conditions at " \
-                       + lines[0].split(", ")[1] + " (" \
-                       + id.upper() +")")
-               output.append("Last updated " + lines[1])
-               for line in lines:
-                       for heading in headings:
-                               if line.startswith(heading + ":"):
-                                       if line.endswith(":0"):
-                                               line = line[:-2]
-                                       output.append("   " + line)
-               return "\n".join(output)
-
-def get_forecast(city, st, verbose=False):
-       """Return the forecast for a specified city/st combination."""
-       forecast = get_url("http://weather.noaa.gov/pub/data/forecasts/city/" \
-               + st.lower() + "/" + city.lower().replace(" ", "_") \
-               + ".txt")
-       if verbose: return forecast
-       else:
-               lines = forecast.split("\n")
-               output = []
-               output.append(lines[2])
-               output.append(lines[3])
-               for line in lines:
-                       if line.startswith("."):
-                               output.append(line.replace(".", "   ", 1))
-               return "\n".join(output)
+   """Return a string containing the results of a URL GET."""
+   import urllib2
+   try: return urllib2.urlopen(url).read()
+   except urllib2.URLError:
+      import sys, traceback
+      sys.stderr.write("weather: error: failed to retrieve\n   " \
+         + url + "\n   " + \
+         traceback.format_exception_only(sys.exc_type, sys.exc_value)[0])
+      sys.exit(1)
+
+def get_metar(id, verbose=False, quiet=False, headers=None, murl=None):
+   """Return a summarized METAR for the specified station."""
+   if not id:
+      import sys
+      sys.stderr.write("weather: error: id required for conditions\n")
+      sys.exit(1)
+   if not murl:
+      murl = \
+         "http://weather.noaa.gov/pub/data/observations/metar/decoded/%ID%.TXT"
+   murl = murl.replace("%ID%", id.upper())
+   murl = murl.replace("%Id%", id.capitalize())
+   murl = murl.replace("%iD%", id)
+   murl = murl.replace("%id%", id.lower())
+   murl = murl.replace(" ", "_")
+   metar = get_url(murl)
+   if verbose: return metar
+   else:
+      lines = metar.split("\n")
+      if not headers:
+         headers = \
+            "relative_humidity," \
+            + "precipitation_last_hour," \
+            + "sky conditions," \
+            + "temperature," \
+            + "weather," \
+            + "wind"
+      headerlist = headers.lower().replace("_"," ").split(",")
+      output = []
+      if not quiet:
+         output.append("Current conditions at " \
+            + lines[0].split(", ")[1] + " (" \
+            + id.upper() +")")
+         output.append("Last updated " + lines[1])
+      for header in headerlist:
+         for line in lines:
+            if line.lower().startswith(header + ":"):
+               if line.endswith(":0"):
+                  line = line[:-2]
+               if quiet: output.append(line)
+               else: output.append("   " + line)
+      return "\n".join(output)
+
+def get_forecast(city, st, verbose=False, quiet=False, flines="0", furl=None):
+   """Return the forecast for a specified city/st combination."""
+   if not city or not st:
+      import sys
+      sys.stderr.write("weather: error: city and st required for forecast\n")
+      sys.exit(1)
+   if not furl:
+      furl = "http://weather.noaa.gov/pub/data/forecasts/city/%st%/%city%.txt"
+   furl = furl.replace("%CITY%", city.upper())
+   furl = furl.replace("%City%", city.capitalize())
+   furl = furl.replace("%citY%", city)
+   furl = furl.replace("%city%", city.lower())
+   furl = furl.replace("%ST%", st.upper())
+   furl = furl.replace("%St%", st.capitalize())
+   furl = furl.replace("%sT%", st)
+   furl = furl.replace("%st%", st.lower())
+   furl = furl.replace(" ", "_")
+   forecast = get_url(furl)
+   if verbose: return forecast
+   else:
+      lines = forecast.split("\n")
+      output = []
+      if not quiet: output += lines[2:4]
+      flines = int(flines)
+      if not flines: flines = len(lines) - 5
+      for line in lines[5:flines+5]:
+         if line.startswith("."):
+            if quiet: output.append(line.replace(".", "", 1))
+            else: output.append(line.replace(".", "   ", 1))
+      return "\n".join(output)
 
 def get_options(config):
-       """Parse the options passed on the command line."""
-       import optparse
-       usage = "usage: %prog [ options ] [ alias [ alias [...] ] ]"
-       verstring = "%prog " + version
-       option_parser = optparse.OptionParser(usage=usage, version=verstring)
-       if config.has_option("default", "city"):
-               default_city = config.get("default", "city")
-       else: default_city = "Raleigh Durham"
-       option_parser.add_option("-c", "--city",
-               dest="city",
-               default=default_city,
-               help="the city name (ex: \"Raleigh Durham\")")
-       if config.has_option("default", "forecast"):
-               default_forecast = bool(config.get("default", "forecast"))
-       else: default_forecast = False
-       option_parser.add_option("-f", "--forecast",
-               dest="forecast",
-               action="store_true",
-               default=default_forecast,
-               help="include a local forecast")
-       if config.has_option("default", "id"):
-               default_id = config.get("default", "id")
-       else: default_id = "KRDU"
-       option_parser.add_option("-i", "--id",
-               dest="id",
-               default=default_id,
-               help="the METAR station ID (ex: KRDU)")
-       option_parser.add_option("-l", "--list",
-               dest="list",
-               action="store_true",
-               default=False,
-               help="print a list of configured aliases")
-       if config.has_option("default", "conditions"):
-               default_conditions = bool(config.get("default", "conditions"))
-       else: default_conditions = True
-       option_parser.add_option("-n", "--no-conditions",
-               dest="conditions",
-               action="store_false",
-               default=default_conditions,
-               help="disable output of current conditions (forces -f)")
-       option_parser.add_option("-o", "--omit-forecast",
-               dest="forecast",
-               action="store_false",
-               default=default_forecast,
-               help="omit the local forecast (cancels -f)")
-       if config.has_option("default", "st"):
-               default_st = config.get("default", "st")
-       else: default_st = "NC"
-       option_parser.add_option("-s", "--st",
-               dest="st",
-               default=default_st,
-               help="the state abbreviation (ex: NC)")
-       if config.has_option("default", "verbose"):
-               default_verbose = bool(config.get("default", "verbose"))
-       else: default_verbose = False
-       option_parser.add_option("-v", "--verbose",
-               dest="verbose",
-               action="store_true",
-               default=default_verbose,
-               help="show full decoded feeds")
-       options, arguments = option_parser.parse_args()
-       return options, arguments
+   """Parse the options passed on the command line."""
+
+   # for optparse's builtin -h/--help option
+   usage = "usage: %prog [ options ] [ alias [ alias [...] ] ]"
+
+   # for optparse's builtin --version option
+   verstring = "%prog " + version
+
+   # create the parser
+   import optparse
+   option_parser = optparse.OptionParser(usage=usage, version=verstring)
+
+   # the -c/--city option
+   if config.has_option("default", "city"):
+      default_city = config.get("default", "city")
+   else: default_city = ""
+   option_parser.add_option("-c", "--city",
+      dest="city",
+      default=default_city,
+      help="the city name (ex: \"Raleigh Durham\")")
+
+   # the --flines option
+   if config.has_option("default", "flines"):
+      default_flines = config.get("default", "flines")
+   else: default_flines = "0"
+   option_parser.add_option("--flines",
+      dest="flines",
+      default=default_flines,
+      help="maximum number of forecast lines to show")
+
+   # the -f/--forecast option
+   if config.has_option("default", "forecast"):
+      default_forecast = bool(config.get("default", "forecast"))
+   else: default_forecast = False
+   option_parser.add_option("-f", "--forecast",
+      dest="forecast",
+      action="store_true",
+      default=default_forecast,
+      help="include a local forecast")
+
+   # the --furl option
+   if config.has_option("default", "furl"):
+      default_furl = config.get("default", "furl")
+   else:
+      default_furl = \
+         "http://weather.noaa.gov/pub/data/forecasts/city/%st%/%city%.txt"
+   option_parser.add_option("--furl",
+      dest="furl",
+      default=default_furl,
+      help="forecast URL (including %city% and %st%)")
+
+   # the --headers option
+   if config.has_option("default", "headers"):
+      default_headers = config.get("default", "headers")
+   else:
+      default_headers = \
+         "temperature," \
+         + "relative_humidity," \
+         + "wind," \
+         + "weather," \
+         + "sky_conditions," \
+         + "precipitation_last_hour"
+   option_parser.add_option("--headers",
+      dest="headers",
+      default=default_headers,
+      help="the conditions headers to display")
+
+   # the -i/--id option
+   if config.has_option("default", "id"):
+      default_id = config.get("default", "id")
+   else: default_id = ""
+   option_parser.add_option("-i", "--id",
+      dest="id",
+      default=default_id,
+      help="the METAR station ID (ex: KRDU)")
+
+   # the -l/--list option
+   option_parser.add_option("-l", "--list",
+      dest="list",
+      action="store_true",
+      default=False,
+      help="print a list of configured aliases")
+
+   # the --murl option
+   if config.has_option("default", "murl"):
+      default_murl = config.get("default", "murl")
+   else:
+      default_murl = \
+         "http://weather.noaa.gov/pub/data/observations/metar/decoded/%ID%.TXT"
+   option_parser.add_option("--murl",
+      dest="murl",
+      default=default_murl,
+      help="METAR URL (including %id%)")
+
+   # the -n/--no-conditions option
+   if config.has_option("default", "conditions"):
+      default_conditions = bool(config.get("default", "conditions"))
+   else: default_conditions = True
+   option_parser.add_option("-n", "--no-conditions",
+      dest="conditions",
+      action="store_false",
+      default=default_conditions,
+      help="disable output of current conditions (forces -f)")
+
+   # the -o/--omit-forecast option
+   option_parser.add_option("-o", "--omit-forecast",
+      dest="forecast",
+      action="store_false",
+      default=default_forecast,
+      help="omit the local forecast (cancels -f)")
+
+   # the -q/--quiet option
+   if config.has_option("default", "quiet"):
+      default_quiet = bool(config.get("default", "quiet"))
+   else: default_quiet = False
+   option_parser.add_option("-q", "--quiet",
+      dest="quiet",
+      action="store_true",
+      default=default_quiet,
+      help="skip preambles and don't indent")
+
+   # the -s/--st option
+   if config.has_option("default", "st"):
+      default_st = config.get("default", "st")
+   else: default_st = ""
+   option_parser.add_option("-s", "--st",
+      dest="st",
+      default=default_st,
+      help="the state abbreviation (ex: NC)")
+
+   # the -v/--verbose option
+   if config.has_option("default", "verbose"):
+      default_verbose = bool(config.get("default", "verbose"))
+   else: default_verbose = False
+   option_parser.add_option("-v", "--verbose",
+      dest="verbose",
+      action="store_true",
+      default=default_verbose,
+      help="show full decoded feeds (cancels -q)")
+
+   # separate options object from list of arguments and return both
+   options, arguments = option_parser.parse_args()
+   return options, arguments
 
 def get_config():
-       """Parse the aliases and configuration."""
-       import ConfigParser
-       config = ConfigParser.ConfigParser()
-       import os.path
-       rcfiles = [
-               "/etc/weatherrc",
-               os.path.expanduser("~/.weatherrc"),
-               "weatherrc"
-               ]
-       import os
-       for rcfile in rcfiles:
-               if os.access(rcfile, os.R_OK): config.read(rcfile)
-       for section in config.sections():
-               if section != section.lower():
-                       if config.has_section(section.lower()):
-                               config.remove_section(section.lower())
-                       config.add_section(section.lower())
-                       for option,value in config.items(section):
-                               config.set(section.lower(), option, value)
-       return config
+   """Parse the aliases and configuration."""
+   import ConfigParser
+   config = ConfigParser.ConfigParser()
+   import os.path
+   rcfiles = [
+      "/etc/weatherrc",
+      os.path.expanduser("~/.weatherrc"),
+      "weatherrc"
+      ]
+   import os
+   for rcfile in rcfiles:
+      if os.access(rcfile, os.R_OK): config.read(rcfile)
+   for section in config.sections():
+      if section != section.lower():
+         if config.has_section(section.lower()):
+            config.remove_section(section.lower())
+         config.add_section(section.lower())
+         for option,value in config.items(section):
+            config.set(section.lower(), option, value)
+   return config
 
 def list_aliases(config):
-       """Return a formatted list of aliases defined in the config."""
-       sections = []
-       for section in sorted(config.sections()):
-               if section.lower() not in sections and section != "default":
-                       sections.append(section.lower())
-       output = "configured aliases..."
-       for section in sorted(sections):
-               output += "\n   " \
-                       + section \
-                       + ": --id=" \
-                       + quote(config.get(section, "id")) \
-                       + " --city=" \
-                       + quote(config.get(section, "city")) \
-                       + " --st=" \
-                       + quote(config.get(section, "st"))
-       return output
+   """Return a formatted list of aliases defined in the config."""
+   sections = []
+   for section in sorted(config.sections()):
+      if section.lower() not in sections and section != "default":
+         sections.append(section.lower())
+   output = "configured aliases..."
+   for section in sorted(sections):
+      output += "\n   " \
+         + section \
+         + ": --id=" \
+         + quote(config.get(section, "id")) \
+         + " --city=" \
+         + quote(config.get(section, "city")) \
+         + " --st=" \
+         + quote(config.get(section, "st"))
+   return output
 
index 85f0b3c..60e7342 100644 (file)
--- a/weatherrc
+++ b/weatherrc
@@ -1,3 +1,8 @@
+# Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
+# Permission to use, copy, modify, and distribute this software is
+# granted under terms provided in the LICENSE file distributed with
+# this software.
+
 [ABE]
 City = Allentown
 ID = KABE
index 987ec34..e5be871 100644 (file)
@@ -1,6 +1,8 @@
-.TH WEATHERRC 5 "March 26, 2006" "" \" -*- nroff -*-
-\" Copyright (c) 2006 Jeremy Stanley <fungi@yuggoth.org>, all rights reserved.
-\" Licensed per terms in the LICENSE file distributed with this software.
+.TH WEATHERRC 5 "July 13, 2008" "" \" -*- nroff -*-
+\" Copyright (c) 2006-2008 Jeremy Stanley <fungi@yuggoth.org>.
+\" Permission to use, copy, modify, and distribute this software is
+\" granted under terms provided in the LICENSE file distributed with
+\" this software.
 .SH NAME
 weatherrc \- configuration file format for the
 .BR weather (1)
@@ -20,20 +22,47 @@ These parameters are supported...
 .B city
 the city name (ex: Raleigh Durham)
 .TP
+.B conditions
+output current conditions (possible values are False and True or 0 and 1)
+.TP
+.B flines
+maximum number of forecast lines to show (integer value, 0 means unlimited)
+.TP
 .B forecast
 include a local forecast (possible values are False and True or 0 and 1)
 .TP
+.B furl
+forecast URL (ex: http://forecast.org/%st%/%city%.txt)
+.TP
+.B headers
+the conditions headers to display (ex: temperature,wind)
+.TP
 .B id
 the METAR station ID (ex: KRDU)
 .TP
-.B conditions
-output current conditions (possible values are False and True or 0 and 1)
+.B murl
+METAR URL (ex: http://metar.org/%id%.txt)
+.TP
+.B quiet
+skip preambles and don't indent (possible values are False and True or 0 and 1)
 .TP
 .B st
 the state abbreviation (ex: NC)
 .TP
 .B verbose
 show full decoded feeds (possible values are False and True or 0 and 1)
+.SH URL FORMAT
+The placeholders %city% and %st% in the furl URL and %id% in the murl URL
+will be replaced with the city, st and id definitions respectively. If the
+placeholder has all letters lowercased, the replacement will be forced to
+all lowercase. If the placeholder has all letters uppercased, the
+replacement will be forced to all uppercase. If the placeholder has its
+first letter uppercased and the remainder lowercased, then all words in the
+replacement will start with an uppercase letter and the rest will be
+lowercase. If the placeholder has its last letter uppercased and the
+remainder lowercased, then case will be preserved in the replacement. Also,
+after replacement, any spaces in the resulting URL will be converted to
+underscore characters prior to use.
 .SH EXAMPLES
 Following is an example
 .B ~/.weatherrc