Blame libgweather/weather-metar.c

rpm-build ca2b01
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
rpm-build ca2b01
/* weather-metar.c - Weather server functions (METAR)
rpm-build ca2b01
 *
rpm-build ca2b01
 * This program is free software; you can redistribute it and/or
rpm-build ca2b01
 * modify it under the terms of the GNU General Public License as
rpm-build ca2b01
 * published by the Free Software Foundation; either version 2 of the
rpm-build ca2b01
 * License, or (at your option) any later version.
rpm-build ca2b01
 *
rpm-build ca2b01
 * This program is distributed in the hope that it will be useful, but
rpm-build ca2b01
 * WITHOUT ANY WARRANTY; without even the implied warranty of
rpm-build ca2b01
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
rpm-build ca2b01
 * General Public License for more details.
rpm-build ca2b01
 *
rpm-build ca2b01
 * You should have received a copy of the GNU General Public License
rpm-build ca2b01
 * along with this program; if not, see
rpm-build ca2b01
 * <http://www.gnu.org/licenses/>.
rpm-build ca2b01
 */
rpm-build ca2b01
rpm-build ca2b01
#ifdef HAVE_CONFIG_H
rpm-build ca2b01
#include <config.h>
rpm-build ca2b01
#endif
rpm-build ca2b01
rpm-build ca2b01
#include <stdlib.h>
rpm-build ca2b01
#include <string.h>
rpm-build ca2b01
#include <sys/types.h>
rpm-build ca2b01
rpm-build ca2b01
#include "gweather-private.h"
rpm-build ca2b01
rpm-build ca2b01
enum {
rpm-build ca2b01
    TIME_RE,
rpm-build ca2b01
    WIND_RE,
rpm-build ca2b01
    VIS_RE,
rpm-build ca2b01
    COND_RE,
rpm-build ca2b01
    CLOUD_RE,
rpm-build ca2b01
    TEMP_RE,
rpm-build ca2b01
    PRES_RE,
rpm-build ca2b01
rpm-build ca2b01
    RE_NUM
rpm-build ca2b01
};
rpm-build ca2b01
rpm-build ca2b01
/* Return time of weather report as secs since epoch UTC */
rpm-build ca2b01
static time_t
rpm-build ca2b01
make_time (gint utcDate, gint utcHour, gint utcMin)
rpm-build ca2b01
{
rpm-build ca2b01
    const time_t now = time (NULL);
rpm-build ca2b01
    struct tm tm;
rpm-build ca2b01
rpm-build ca2b01
    localtime_r (&now, &tm;;
rpm-build ca2b01
rpm-build ca2b01
    /* If last reading took place just before midnight UTC on the
rpm-build ca2b01
     * first, adjust the date downward to allow for the month
rpm-build ca2b01
     * change-over.  This ASSUMES that the reading won't be more than
rpm-build ca2b01
     * 24 hrs old! */
rpm-build ca2b01
    if ((utcDate > tm.tm_mday) && (tm.tm_mday == 1)) {
rpm-build ca2b01
        tm.tm_mday = 0; /* mktime knows this is the last day of the previous
rpm-build ca2b01
			 * month. */
rpm-build ca2b01
    } else {
rpm-build ca2b01
        tm.tm_mday = utcDate;
rpm-build ca2b01
    }
rpm-build ca2b01
    tm.tm_hour = utcHour;
rpm-build ca2b01
    tm.tm_min  = utcMin;
rpm-build ca2b01
    tm.tm_sec  = 0;
rpm-build ca2b01
rpm-build ca2b01
    /* mktime() assumes value is local, not UTC.  Use tm_gmtoff to compensate */
rpm-build ca2b01
#ifdef HAVE_TM_TM_GMOFF
rpm-build ca2b01
    return tm.tm_gmtoff + mktime (&tm;;
rpm-build ca2b01
#elif defined HAVE_TIMEZONE
rpm-build ca2b01
    return timezone + mktime (&tm;;
rpm-build ca2b01
#endif
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
static void
rpm-build ca2b01
metar_tok_time (gchar *tokp, GWeatherInfo *info)
rpm-build ca2b01
{
rpm-build ca2b01
    gint day, hr, min;
rpm-build ca2b01
rpm-build ca2b01
    sscanf (tokp, "%2u%2u%2u", &day, &hr, &min);
rpm-build ca2b01
    info->priv->update = make_time (day, hr, min);
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
static void
rpm-build ca2b01
metar_tok_wind (gchar *tokp, GWeatherInfo *info)
rpm-build ca2b01
{
rpm-build ca2b01
    GWeatherInfoPrivate *priv;
rpm-build ca2b01
    gchar sdir[4], sspd[4], sgust[4];
rpm-build ca2b01
    gint dir, spd = -1;
rpm-build ca2b01
    gchar *gustp;
rpm-build ca2b01
    size_t glen;
rpm-build ca2b01
rpm-build ca2b01
    priv = info->priv;
rpm-build ca2b01
rpm-build ca2b01
    strncpy (sdir, tokp, 3);
rpm-build ca2b01
    sdir[3] = 0;
rpm-build ca2b01
    dir = (!strcmp (sdir, "VRB")) ? -1 : atoi (sdir);
rpm-build ca2b01
rpm-build ca2b01
    memset (sspd, 0, sizeof (sspd));
rpm-build ca2b01
    glen = strspn (tokp + 3, CONST_DIGITS);
rpm-build ca2b01
    strncpy (sspd, tokp + 3, glen);
rpm-build ca2b01
    spd = atoi (sspd);
rpm-build ca2b01
    tokp += glen + 3;
rpm-build ca2b01
rpm-build ca2b01
    gustp = strchr (tokp, 'G');
rpm-build ca2b01
    if (gustp) {
rpm-build ca2b01
        memset (sgust, 0, sizeof (sgust));
rpm-build ca2b01
	glen = strspn (gustp + 1, CONST_DIGITS);
rpm-build ca2b01
        strncpy (sgust, gustp + 1, glen);
rpm-build ca2b01
	tokp = gustp + 1 + glen;
rpm-build ca2b01
    }
rpm-build ca2b01
rpm-build ca2b01
    if (!strcmp (tokp, "MPS"))
rpm-build ca2b01
	priv->windspeed = WINDSPEED_MS_TO_KNOTS ((GWeatherWindSpeed)spd);
rpm-build ca2b01
    else
rpm-build ca2b01
	priv->windspeed = (GWeatherWindSpeed)spd;
rpm-build ca2b01
rpm-build ca2b01
    if ((349 <= dir) || (dir <= 11))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_N;
rpm-build ca2b01
    else if ((12 <= dir) && (dir <= 33))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_NNE;
rpm-build ca2b01
    else if ((34 <= dir) && (dir <= 56))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_NE;
rpm-build ca2b01
    else if ((57 <= dir) && (dir <= 78))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_ENE;
rpm-build ca2b01
    else if ((79 <= dir) && (dir <= 101))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_E;
rpm-build ca2b01
    else if ((102 <= dir) && (dir <= 123))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_ESE;
rpm-build ca2b01
    else if ((124 <= dir) && (dir <= 146))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_SE;
rpm-build ca2b01
    else if ((147 <= dir) && (dir <= 168))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_SSE;
rpm-build ca2b01
    else if ((169 <= dir) && (dir <= 191))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_S;
rpm-build ca2b01
    else if ((192 <= dir) && (dir <= 213))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_SSW;
rpm-build ca2b01
    else if ((214 <= dir) && (dir <= 236))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_SW;
rpm-build ca2b01
    else if ((237 <= dir) && (dir <= 258))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_WSW;
rpm-build ca2b01
    else if ((259 <= dir) && (dir <= 281))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_W;
rpm-build ca2b01
    else if ((282 <= dir) && (dir <= 303))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_WNW;
rpm-build ca2b01
    else if ((304 <= dir) && (dir <= 326))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_NW;
rpm-build ca2b01
    else if ((327 <= dir) && (dir <= 348))
rpm-build ca2b01
        priv->wind = GWEATHER_WIND_NNW;
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
static void
rpm-build ca2b01
metar_tok_vis (gchar *tokp, GWeatherInfo *info)
rpm-build ca2b01
{
rpm-build ca2b01
    GWeatherInfoPrivate *priv;
rpm-build ca2b01
    gchar *pfrac, *pend, *psp;
rpm-build ca2b01
    gchar sval[6];
rpm-build ca2b01
    gint num, den, val;
rpm-build ca2b01
rpm-build ca2b01
    priv = info->priv;
rpm-build ca2b01
rpm-build ca2b01
    memset (sval, 0, sizeof (sval));
rpm-build ca2b01
rpm-build ca2b01
    if (!strcmp (tokp,"CAVOK")) {
rpm-build ca2b01
        // "Ceiling And Visibility OK": visibility >= 10 KM
rpm-build ca2b01
        priv->visibility=10000. / VISIBILITY_SM_TO_M (1.);
rpm-build ca2b01
        priv->sky = GWEATHER_SKY_CLEAR;
rpm-build ca2b01
    } else if (0 != (pend = strstr (tokp, "SM"))) {
rpm-build ca2b01
        // US observation: field ends with "SM"
rpm-build ca2b01
        pfrac = strchr (tokp, '/');
rpm-build ca2b01
        if (pfrac) {
rpm-build ca2b01
	    if (*tokp == 'M') {
rpm-build ca2b01
	        priv->visibility = 0.001;
rpm-build ca2b01
	    } else {
rpm-build ca2b01
	        num = (*(pfrac - 1) - '0');
rpm-build ca2b01
		strncpy (sval, pfrac + 1, pend - pfrac - 1);
rpm-build ca2b01
		den = atoi (sval);
rpm-build ca2b01
		priv->visibility =
rpm-build ca2b01
		    ((GWeatherVisibility)num / ((GWeatherVisibility)den));
rpm-build ca2b01
rpm-build ca2b01
		psp = strchr (tokp, ' ');
rpm-build ca2b01
		if (psp) {
rpm-build ca2b01
		    *psp = '\0';
rpm-build ca2b01
		    val = atoi (tokp);
rpm-build ca2b01
		    priv->visibility += (GWeatherVisibility)val;
rpm-build ca2b01
		}
rpm-build ca2b01
	    }
rpm-build ca2b01
	} else {
rpm-build ca2b01
	    strncpy (sval, tokp, pend - tokp);
rpm-build ca2b01
            val = atoi (sval);
rpm-build ca2b01
            priv->visibility = (GWeatherVisibility)val;
rpm-build ca2b01
	}
rpm-build ca2b01
    } else {
rpm-build ca2b01
        // International observation: NNNN(DD NNNNDD)?
rpm-build ca2b01
        // For now: use only the minimum visibility and ignore its direction
rpm-build ca2b01
        strncpy (sval, tokp, strspn (tokp, CONST_DIGITS));
rpm-build ca2b01
	val = atoi (sval);
rpm-build ca2b01
	priv->visibility = (GWeatherVisibility)val / VISIBILITY_SM_TO_M (1.);
rpm-build ca2b01
    }
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
static void
rpm-build ca2b01
metar_tok_cloud (gchar *tokp, GWeatherInfo *info)
rpm-build ca2b01
{
rpm-build ca2b01
    GWeatherInfoPrivate *priv;
rpm-build ca2b01
    gchar stype[4], salt[4];
rpm-build ca2b01
rpm-build ca2b01
    priv = info->priv;
rpm-build ca2b01
rpm-build ca2b01
    strncpy (stype, tokp, 3);
rpm-build ca2b01
    stype[3] = 0;
rpm-build ca2b01
    if (strlen (tokp) == 6) {
rpm-build ca2b01
        strncpy (salt, tokp + 3, 3);
rpm-build ca2b01
        salt[3] = 0;
rpm-build ca2b01
    }
rpm-build ca2b01
rpm-build ca2b01
    if (!strcmp (stype, "CLR")) {
rpm-build ca2b01
        priv->sky = GWEATHER_SKY_CLEAR;
rpm-build ca2b01
    } else if (!strcmp (stype, "SKC")) {
rpm-build ca2b01
        priv->sky = GWEATHER_SKY_CLEAR;
rpm-build ca2b01
    } else if (!strcmp (stype, "NSC")) {
rpm-build ca2b01
        priv->sky = GWEATHER_SKY_CLEAR;
rpm-build ca2b01
    } else if (!strcmp (stype, "BKN")) {
rpm-build ca2b01
        priv->sky = GWEATHER_SKY_BROKEN;
rpm-build ca2b01
    } else if (!strcmp (stype, "SCT")) {
rpm-build ca2b01
        priv->sky = GWEATHER_SKY_SCATTERED;
rpm-build ca2b01
    } else if (!strcmp (stype, "FEW")) {
rpm-build ca2b01
        priv->sky = GWEATHER_SKY_FEW;
rpm-build ca2b01
    } else if (!strcmp (stype, "OVC")) {
rpm-build ca2b01
        priv->sky = GWEATHER_SKY_OVERCAST;
rpm-build ca2b01
    }
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
static void
rpm-build ca2b01
metar_tok_pres (gchar *tokp, GWeatherInfo *info)
rpm-build ca2b01
{
rpm-build ca2b01
    GWeatherInfoPrivate *priv = info->priv;
rpm-build ca2b01
rpm-build ca2b01
    if (*tokp == 'A') {
rpm-build ca2b01
        gchar sintg[3], sfract[3];
rpm-build ca2b01
        gint intg, fract;
rpm-build ca2b01
rpm-build ca2b01
        strncpy (sintg, tokp + 1, 2);
rpm-build ca2b01
        sintg[2] = 0;
rpm-build ca2b01
        intg = atoi (sintg);
rpm-build ca2b01
rpm-build ca2b01
        strncpy (sfract, tokp + 3, 2);
rpm-build ca2b01
        sfract[2] = 0;
rpm-build ca2b01
        fract = atoi (sfract);
rpm-build ca2b01
rpm-build ca2b01
        priv->pressure = (GWeatherPressure)intg + (((GWeatherPressure)fract)/100.0);
rpm-build ca2b01
    } else {  /* *tokp == 'Q' */
rpm-build ca2b01
        gchar spres[5];
rpm-build ca2b01
        gint pres;
rpm-build ca2b01
rpm-build ca2b01
        strncpy (spres, tokp + 1, 4);
rpm-build ca2b01
        spres[4] = 0;
rpm-build ca2b01
        pres = atoi (spres);
rpm-build ca2b01
rpm-build ca2b01
        priv->pressure = PRESSURE_MBAR_TO_INCH ((GWeatherPressure)pres);
rpm-build ca2b01
    }
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
static void
rpm-build ca2b01
metar_tok_temp (gchar *tokp, GWeatherInfo *info)
rpm-build ca2b01
{
rpm-build ca2b01
    GWeatherInfoPrivate *priv;
rpm-build ca2b01
    gchar *ptemp, *pdew, *psep;
rpm-build ca2b01
rpm-build ca2b01
    priv = info->priv;
rpm-build ca2b01
rpm-build ca2b01
    psep = strchr (tokp, '/');
rpm-build ca2b01
    *psep = 0;
rpm-build ca2b01
    ptemp = tokp;
rpm-build ca2b01
    pdew = psep + 1;
rpm-build ca2b01
rpm-build ca2b01
    priv->temp = (*ptemp == 'M') ? TEMP_C_TO_F (-atoi (ptemp + 1))
rpm-build ca2b01
	: TEMP_C_TO_F (atoi (ptemp));
rpm-build ca2b01
    if (*pdew) {
rpm-build ca2b01
	priv->dew = (*pdew == 'M') ? TEMP_C_TO_F (-atoi (pdew + 1))
rpm-build ca2b01
	    : TEMP_C_TO_F (atoi (pdew));
rpm-build ca2b01
    } else {
rpm-build ca2b01
	priv->dew = -1000.0;
rpm-build ca2b01
    }
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
/* How "important" are the conditions to be reported to the user.
rpm-build ca2b01
   Indexed by GWeatherConditionPhenomenon */
rpm-build ca2b01
static const int importance_scale[] = {
rpm-build ca2b01
    0, /* invalid */
rpm-build ca2b01
    0, /* none */
rpm-build ca2b01
    20, /* drizzle */
rpm-build ca2b01
    30, /* rain */
rpm-build ca2b01
    35, /* snow */
rpm-build ca2b01
    35, /* snow grains */
rpm-build ca2b01
    35, /* ice crystals */
rpm-build ca2b01
    35, /* ice pellets */
rpm-build ca2b01
    35, /* hail */
rpm-build ca2b01
    35, /* small hail */
rpm-build ca2b01
    20, /* unknown precipitation */
rpm-build ca2b01
    10, /* mist */
rpm-build ca2b01
    15, /* fog */
rpm-build ca2b01
    15, /* smoke */
rpm-build ca2b01
    18, /* volcanic ash */
rpm-build ca2b01
    18, /* sand */
rpm-build ca2b01
    15, /* haze */
rpm-build ca2b01
    15, /* spray */
rpm-build ca2b01
    15, /* dust */
rpm-build ca2b01
    40, /* squall */
rpm-build ca2b01
    50, /* sandstorm */
rpm-build ca2b01
    50, /* duststorm */
rpm-build ca2b01
    70, /* funnel cloud */
rpm-build ca2b01
    70, /* tornado */
rpm-build ca2b01
    50, /* dust whirls */
rpm-build ca2b01
};
rpm-build ca2b01
rpm-build ca2b01
static gboolean
rpm-build ca2b01
condition_more_important (GWeatherConditions *which,
rpm-build ca2b01
			  GWeatherConditions *than)
rpm-build ca2b01
{
rpm-build ca2b01
    if (!than->significant)
rpm-build ca2b01
	return TRUE;
rpm-build ca2b01
    if (!which->significant)
rpm-build ca2b01
	return FALSE;
rpm-build ca2b01
rpm-build ca2b01
    if (importance_scale[than->phenomenon] <
rpm-build ca2b01
	importance_scale[which->phenomenon])
rpm-build ca2b01
	return TRUE;
rpm-build ca2b01
rpm-build ca2b01
    return FALSE;
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
static void
rpm-build ca2b01
metar_tok_cond (gchar *tokp, GWeatherInfo *info)
rpm-build ca2b01
{
rpm-build ca2b01
    GWeatherInfoPrivate *priv;
rpm-build ca2b01
    GWeatherConditions new_cond;
rpm-build ca2b01
    gchar squal[3], sphen[4];
rpm-build ca2b01
    gchar *pphen;
rpm-build ca2b01
rpm-build ca2b01
    priv = info->priv;
rpm-build ca2b01
rpm-build ca2b01
    if ((strlen (tokp) > 3) && ((*tokp == '+') || (*tokp == '-')))
rpm-build ca2b01
        ++tokp;   /* FIX */
rpm-build ca2b01
rpm-build ca2b01
    if ((*tokp == '+') || (*tokp == '-'))
rpm-build ca2b01
        pphen = tokp + 1;
rpm-build ca2b01
    else if (strlen (tokp) < 4)
rpm-build ca2b01
        pphen = tokp;
rpm-build ca2b01
    else
rpm-build ca2b01
        pphen = tokp + 2;
rpm-build ca2b01
rpm-build ca2b01
    memset (squal, 0, sizeof (squal));
rpm-build ca2b01
    strncpy (squal, tokp, pphen - tokp);
rpm-build ca2b01
    squal[pphen - tokp] = 0;
rpm-build ca2b01
rpm-build ca2b01
    memset (sphen, 0, sizeof (sphen));
rpm-build ca2b01
    strncpy (sphen, pphen, sizeof (sphen));
rpm-build ca2b01
    sphen[sizeof (sphen)-1] = '\0';
rpm-build ca2b01
rpm-build ca2b01
    /* Defaults */
rpm-build ca2b01
    new_cond.qualifier = GWEATHER_QUALIFIER_NONE;
rpm-build ca2b01
    new_cond.phenomenon = GWEATHER_PHENOMENON_NONE;
rpm-build ca2b01
    new_cond.significant = FALSE;
rpm-build ca2b01
rpm-build ca2b01
    if (!strcmp (squal, "")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_MODERATE;
rpm-build ca2b01
    } else if (!strcmp (squal, "-")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_LIGHT;
rpm-build ca2b01
    } else if (!strcmp (squal, "+")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_HEAVY;
rpm-build ca2b01
    } else if (!strcmp (squal, "VC")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_VICINITY;
rpm-build ca2b01
    } else if (!strcmp (squal, "MI")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_SHALLOW;
rpm-build ca2b01
    } else if (!strcmp (squal, "BC")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_PATCHES;
rpm-build ca2b01
    } else if (!strcmp (squal, "PR")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_PARTIAL;
rpm-build ca2b01
    } else if (!strcmp (squal, "TS")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_THUNDERSTORM;
rpm-build ca2b01
    } else if (!strcmp (squal, "BL")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_BLOWING;
rpm-build ca2b01
    } else if (!strcmp (squal, "SH")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_SHOWERS;
rpm-build ca2b01
    } else if (!strcmp (squal, "DR")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_DRIFTING;
rpm-build ca2b01
    } else if (!strcmp (squal, "FZ")) {
rpm-build ca2b01
        new_cond.qualifier = GWEATHER_QUALIFIER_FREEZING;
rpm-build ca2b01
    } else {
rpm-build ca2b01
        return;
rpm-build ca2b01
    }
rpm-build ca2b01
rpm-build ca2b01
    if (!strcmp (sphen, "DZ")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_DRIZZLE;
rpm-build ca2b01
    } else if (!strcmp (sphen, "RA")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_RAIN;
rpm-build ca2b01
    } else if (!strcmp (sphen, "SN")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_SNOW;
rpm-build ca2b01
    } else if (!strcmp (sphen, "SG")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_SNOW_GRAINS;
rpm-build ca2b01
    } else if (!strcmp (sphen, "IC")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_ICE_CRYSTALS;
rpm-build ca2b01
    } else if (!strcmp (sphen, "PL")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_ICE_PELLETS;
rpm-build ca2b01
    } else if (!strcmp (sphen, "GR")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_HAIL;
rpm-build ca2b01
    } else if (!strcmp (sphen, "GS")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_SMALL_HAIL;
rpm-build ca2b01
    } else if (!strcmp (sphen, "UP")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_UNKNOWN_PRECIPITATION;
rpm-build ca2b01
    } else if (!strcmp (sphen, "BR")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_MIST;
rpm-build ca2b01
    } else if (!strcmp (sphen, "FG")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_FOG;
rpm-build ca2b01
    } else if (!strcmp (sphen, "FU")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_SMOKE;
rpm-build ca2b01
    } else if (!strcmp (sphen, "VA")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_VOLCANIC_ASH;
rpm-build ca2b01
    } else if (!strcmp (sphen, "SA")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_SAND;
rpm-build ca2b01
    } else if (!strcmp (sphen, "HZ")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_HAZE;
rpm-build ca2b01
    } else if (!strcmp (sphen, "PY")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_SPRAY;
rpm-build ca2b01
    } else if (!strcmp (sphen, "DU")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_DUST;
rpm-build ca2b01
    } else if (!strcmp (sphen, "SQ")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_SQUALL;
rpm-build ca2b01
    } else if (!strcmp (sphen, "SS")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_SANDSTORM;
rpm-build ca2b01
    } else if (!strcmp (sphen, "DS")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_DUSTSTORM;
rpm-build ca2b01
    } else if (!strcmp (sphen, "PO")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_DUST_WHIRLS;
rpm-build ca2b01
    } else if (!strcmp (sphen, "+FC")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_TORNADO;
rpm-build ca2b01
    } else if (!strcmp (sphen, "FC")) {
rpm-build ca2b01
        new_cond.phenomenon = GWEATHER_PHENOMENON_FUNNEL_CLOUD;
rpm-build ca2b01
    } else {
rpm-build ca2b01
        return;
rpm-build ca2b01
    }
rpm-build ca2b01
rpm-build ca2b01
    if ((new_cond.qualifier != GWEATHER_QUALIFIER_NONE) || (new_cond.phenomenon != GWEATHER_PHENOMENON_NONE))
rpm-build ca2b01
        new_cond.significant = TRUE;
rpm-build ca2b01
rpm-build ca2b01
    if (condition_more_important (&new_cond, &priv->cond))
rpm-build ca2b01
	priv->cond = new_cond;
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
#define TIME_RE_STR  "([0-9]{6})Z"
rpm-build ca2b01
#define WIND_RE_STR  "(([0-9]{3})|VRB)([0-9]?[0-9]{2})(G[0-9]?[0-9]{2})?(KT|MPS)"
rpm-build ca2b01
#define VIS_RE_STR   "((([0-9]?[0-9])|(M?([12] )?([1357]/1?[0-9])))SM)|" \
rpm-build ca2b01
    "([0-9]{4}(N|NE|E|SE|S|SW|W|NW( [0-9]{4}(N|NE|E|SE|S|SW|W|NW))?)?)|" \
rpm-build ca2b01
    "CAVOK"
rpm-build ca2b01
#define COND_RE_STR  "(-|\\+)?(VC|MI|BC|PR|TS|BL|SH|DR|FZ)?(DZ|RA|SN|SG|IC|PE|GR|GS|UP|BR|FG|FU|VA|SA|HZ|PY|DU|SQ|SS|DS|PO|\\+?FC)"
rpm-build ca2b01
#define CLOUD_RE_STR "((CLR|BKN|SCT|FEW|OVC|SKC|NSC)([0-9]{3}|///)?(CB|TCU|///)?)"
rpm-build ca2b01
#define TEMP_RE_STR  "(M?[0-9][0-9])/(M?(//|[0-9][0-9])?)"
rpm-build ca2b01
#define PRES_RE_STR  "(A|Q)([0-9]{4})"
rpm-build ca2b01
rpm-build ca2b01
/* POSIX regular expressions do not allow us to express "match whole words
rpm-build ca2b01
 * only" in a simple way, so we have to wrap them all into
rpm-build ca2b01
 *   (^| )(...regex...)( |$)
rpm-build ca2b01
 */
rpm-build ca2b01
#define RE_PREFIX "(^| )("
rpm-build ca2b01
#define RE_SUFFIX ")( |$)"
rpm-build ca2b01
rpm-build ca2b01
static GRegex *metar_re[RE_NUM];
rpm-build ca2b01
static void (*metar_f[RE_NUM]) (gchar *tokp, GWeatherInfo *info);
rpm-build ca2b01
rpm-build ca2b01
static void
rpm-build ca2b01
metar_init_re (void)
rpm-build ca2b01
{
rpm-build ca2b01
    static gboolean initialized = FALSE;
rpm-build ca2b01
    if (initialized)
rpm-build ca2b01
        return;
rpm-build ca2b01
    initialized = TRUE;
rpm-build ca2b01
rpm-build ca2b01
    metar_re[TIME_RE] = g_regex_new (RE_PREFIX TIME_RE_STR RE_SUFFIX, G_REGEX_OPTIMIZE, 0, NULL);
rpm-build ca2b01
    metar_re[WIND_RE] = g_regex_new (RE_PREFIX WIND_RE_STR RE_SUFFIX, G_REGEX_OPTIMIZE, 0, NULL);
rpm-build ca2b01
    metar_re[VIS_RE] = g_regex_new (RE_PREFIX VIS_RE_STR RE_SUFFIX, G_REGEX_OPTIMIZE, 0, NULL);
rpm-build ca2b01
    metar_re[COND_RE] = g_regex_new (RE_PREFIX COND_RE_STR RE_SUFFIX, G_REGEX_OPTIMIZE, 0, NULL);
rpm-build ca2b01
    metar_re[CLOUD_RE] = g_regex_new (RE_PREFIX CLOUD_RE_STR RE_SUFFIX, G_REGEX_OPTIMIZE, 0, NULL);
rpm-build ca2b01
    metar_re[TEMP_RE] = g_regex_new (RE_PREFIX TEMP_RE_STR RE_SUFFIX, G_REGEX_OPTIMIZE, 0, NULL);
rpm-build ca2b01
    metar_re[PRES_RE] = g_regex_new (RE_PREFIX PRES_RE_STR RE_SUFFIX, G_REGEX_OPTIMIZE, 0, NULL);
rpm-build ca2b01
rpm-build ca2b01
    metar_f[TIME_RE] = metar_tok_time;
rpm-build ca2b01
    metar_f[WIND_RE] = metar_tok_wind;
rpm-build ca2b01
    metar_f[VIS_RE] = metar_tok_vis;
rpm-build ca2b01
    metar_f[COND_RE] = metar_tok_cond;
rpm-build ca2b01
    metar_f[CLOUD_RE] = metar_tok_cloud;
rpm-build ca2b01
    metar_f[TEMP_RE] = metar_tok_temp;
rpm-build ca2b01
    metar_f[PRES_RE] = metar_tok_pres;
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
gboolean
rpm-build ca2b01
metar_parse (gchar *metar, GWeatherInfo *info)
rpm-build ca2b01
{
rpm-build ca2b01
    gchar *p;
rpm-build ca2b01
    //gchar *rmk;
rpm-build ca2b01
    gint i, i2;
rpm-build ca2b01
    gchar *tokp;
rpm-build ca2b01
rpm-build ca2b01
    g_return_val_if_fail (info != NULL, FALSE);
rpm-build ca2b01
    g_return_val_if_fail (metar != NULL, FALSE);
rpm-build ca2b01
rpm-build ca2b01
    metar_init_re ();
rpm-build ca2b01
rpm-build ca2b01
    /*
rpm-build ca2b01
     * Force parsing to end at "RMK" field.  This prevents a subtle
rpm-build ca2b01
     * problem when info within the remark happens to match an earlier state
rpm-build ca2b01
     * and, as a result, throws off all the remaining expression
rpm-build ca2b01
     */
rpm-build ca2b01
    if (0 != (p = strstr (metar, " RMK "))) {
rpm-build ca2b01
        *p = '\0';
rpm-build ca2b01
        //rmk = p + 5;   // uncomment this if RMK data becomes useful
rpm-build ca2b01
    }
rpm-build ca2b01
rpm-build ca2b01
    p = metar;
rpm-build ca2b01
    i = TIME_RE;
rpm-build ca2b01
    while (*p) {
rpm-build ca2b01
        int token_start, token_end;
rpm-build ca2b01
rpm-build ca2b01
        i2 = RE_NUM;
rpm-build ca2b01
        token_start = strlen(p);
rpm-build ca2b01
        token_end = token_start;
rpm-build ca2b01
rpm-build ca2b01
        for (i = 0; i < RE_NUM; i++) {
rpm-build ca2b01
            GMatchInfo *match_info;
rpm-build ca2b01
rpm-build ca2b01
            if (g_regex_match_full (metar_re[i], p, -1, 0, 0, &match_info, NULL))
rpm-build ca2b01
            {
rpm-build ca2b01
                int tmp_token_start, tmp_token_end;
rpm-build ca2b01
                /* Skip leading and trailing space characters, if present.
rpm-build ca2b01
                   (the regular expressions include those characters to
rpm-build ca2b01
                   only get matches limited to whole words). */
rpm-build ca2b01
                g_match_info_fetch_pos (match_info, 0, &tmp_token_start, &tmp_token_end);
rpm-build ca2b01
                if (p[tmp_token_start] == ' ') tmp_token_start++;
rpm-build ca2b01
                if (p[tmp_token_end - 1] == ' ') tmp_token_end--;
rpm-build ca2b01
rpm-build ca2b01
                /* choose the regular expression with the earliest match */
rpm-build ca2b01
                if (tmp_token_start < token_start) {
rpm-build ca2b01
                    i2 = i;
rpm-build ca2b01
                    token_start = tmp_token_start;
rpm-build ca2b01
                    token_end = tmp_token_end;
rpm-build ca2b01
                }
rpm-build ca2b01
            }
rpm-build ca2b01
rpm-build ca2b01
            g_match_info_unref (match_info);
rpm-build ca2b01
        }
rpm-build ca2b01
rpm-build ca2b01
        if (i2 != RE_NUM) {
rpm-build ca2b01
            tokp = g_strndup (p + token_start, token_end - token_start);
rpm-build ca2b01
            metar_f[i2] (tokp, info);
rpm-build ca2b01
            g_free (tokp);
rpm-build ca2b01
        }
rpm-build ca2b01
rpm-build ca2b01
        p += token_end;
rpm-build ca2b01
        p += strspn (p, " ");
rpm-build ca2b01
    }
rpm-build ca2b01
    return TRUE;
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
static void
rpm-build ca2b01
metar_finish (SoupSession *session, SoupMessage *msg, gpointer data)
rpm-build ca2b01
{
rpm-build ca2b01
    GWeatherInfo *info = (GWeatherInfo *)data;
rpm-build ca2b01
    GWeatherInfoPrivate *priv;
rpm-build ca2b01
    WeatherLocation *loc;
rpm-build ca2b01
    const gchar *p, *eoln;
rpm-build ca2b01
    gchar *searchkey, *metar;
rpm-build ca2b01
    gboolean success = FALSE;
rpm-build ca2b01
rpm-build ca2b01
    g_return_if_fail (info != NULL);
rpm-build ca2b01
rpm-build ca2b01
    priv = info->priv;
rpm-build ca2b01
   
rpm-build ca2b01
    if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
rpm-build ca2b01
	if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code))
rpm-build ca2b01
	    priv->network_error = TRUE;
rpm-build ca2b01
	else {
rpm-build ca2b01
	    if (msg->status_code != SOUP_STATUS_CANCELLED)
rpm-build ca2b01
		/* Translators: %d is an error code, and %s the error string */
rpm-build ca2b01
		g_warning (_("Failed to get METAR data: %d %s.\n"),
rpm-build ca2b01
			   msg->status_code, msg->reason_phrase);
rpm-build ca2b01
	}
rpm-build ca2b01
rpm-build ca2b01
	_gweather_info_request_done (info, msg);
rpm-build ca2b01
	return;
rpm-build ca2b01
    }
rpm-build ca2b01
rpm-build ca2b01
    loc = &priv->location;
rpm-build ca2b01
rpm-build ca2b01
    searchkey = g_strdup_printf ("<raw_text>%s", loc->code);
rpm-build ca2b01
    p = strstr (msg->response_body->data, searchkey);
rpm-build ca2b01
    g_free (searchkey);
rpm-build ca2b01
    if (p) {
rpm-build ca2b01
	p += WEATHER_LOCATION_CODE_LEN + 2;
rpm-build ca2b01
	eoln = strchr(p, '\n');
rpm-build ca2b01
	if (eoln)
rpm-build ca2b01
	    metar = g_strndup (p, eoln - p);
rpm-build ca2b01
	else
rpm-build ca2b01
	    metar = g_strdup (p);
rpm-build ca2b01
	success = metar_parse (metar, info);
rpm-build ca2b01
	g_free (metar);
rpm-build ca2b01
    } else if (!strstr (msg->response_body->data, "aviationweather.gov")) {
rpm-build ca2b01
	/* The response doesn't even seem to have come from NOAA...
rpm-build ca2b01
	 * most likely it is a wifi hotspot login page. Call that a
rpm-build ca2b01
	 * network error.
rpm-build ca2b01
	 */
rpm-build ca2b01
	priv->network_error = TRUE;
rpm-build ca2b01
    }
rpm-build ca2b01
rpm-build ca2b01
    priv->valid = success;
rpm-build ca2b01
    _gweather_info_request_done (info, msg);
rpm-build ca2b01
}
rpm-build ca2b01
rpm-build ca2b01
/* Read current conditions and fill in info structure */
rpm-build ca2b01
void
rpm-build ca2b01
metar_start_open (GWeatherInfo *info)
rpm-build ca2b01
{
rpm-build ca2b01
    GWeatherInfoPrivate *priv;
rpm-build ca2b01
    WeatherLocation *loc;
rpm-build ca2b01
    SoupMessage *msg;
rpm-build ca2b01
rpm-build ca2b01
    g_return_if_fail (info != NULL);
rpm-build ca2b01
rpm-build ca2b01
    priv = info->priv;
rpm-build ca2b01
rpm-build ca2b01
    priv->valid = priv->network_error = FALSE;
rpm-build ca2b01
    loc = &priv->location;
rpm-build ca2b01
rpm-build ca2b01
    msg = soup_form_request_new (
rpm-build ca2b01
	"GET", "https://www.aviationweather.gov/adds/dataserver_current/httpparam",
rpm-build ca2b01
	"dataSource", "metars",
rpm-build ca2b01
	"requestType", "retrieve",
rpm-build ca2b01
	"format", "xml",
rpm-build ca2b01
	"hoursBeforeNow", "3",
rpm-build ca2b01
	"mostRecent", "true",
rpm-build ca2b01
	"fields", "raw_text",
rpm-build ca2b01
	"stationString", loc->code,
rpm-build ca2b01
	NULL);
rpm-build ca2b01
    _gweather_info_begin_request (info, msg);
rpm-build ca2b01
    soup_session_queue_message (priv->session, msg, metar_finish, info);
rpm-build ca2b01
}